Creación de sombreadores con Babylon.js

 

 

 


Índice
  1. ¿Como funciona?
    1. Canalización de gráficos
    2. GLSL
    3. Estructura del sombreador de vértices
    4. El vudú detrás de las matrices
    5. Volver a los sombreadores
    6. Estructura de sombreador de píxeles (o fragmentos)
  2. ¿Demasiado duro? BABYLON.ShaderMaterial al rescate
  3. Crea tu propio sombreador (CYOS)
    1. Sombreador básico
    2. Sombreador en blanco y negro
    3. Sombreador de sombreado de celdas
    4. Phong Sombreador
    5. Descartar sombreador
    6. Sombreador de ondas
    7. Mapeo del entorno esférico
    8. Sombreador Fresnel
    9. ¿Tu sombreador?
    10. Otras lecturas

En este artículo, David Catuhe te ayudará a comprender cómo funcionan los sombreadores e incluso a experimentar con su poder interior de una manera sencilla, gracias a Babylon.js. Antes de experimentar, David te mostrará cómo funcionan las cosas internamente. Cuando se trata de 3D acelerado por hardware, tendrás que lidiar con dos CPU: la CPU principal y la GPU. La GPU es una especie de CPU extremadamente especializada. Una vez establecidos todos los estados, la CPU puede definir qué renderizar: la geometría. El último paso para la CPU es definir cómo renderizar la geometría. Los sombreadores son fragmentos de código que la GPU ejecutará para cada uno de los vértices y píxeles que debe renderizar.

 

Los sombreadores son un concepto clave si desea liberar la potencia bruta de su GPU. Te ayudaré a comprender cómo funcionan e incluso a experimentar con su poder interior de una manera sencilla, gracias a Babylon.js .

¿Como funciona?

Antes de experimentar, debemos ver cómo funcionan las cosas internamente.

Cuando se trata de 3D acelerado por hardware, tendrás que lidiar con dos CPU: la CPU principal y la GPU. La GPU es una especie de CPU extremadamente especializada.

La GPU es una máquina de estado que se configura mediante la CPU. Por ejemplo, la CPU configurará la GPU para representar líneas en lugar de triángulos; definirá si la transparencia está activada; etcétera.

Una vez establecidos todos los estados, la CPU puede definir qué renderizar: la geometría.

La geometría se compone de:

 

  • una lista de puntos que se llaman vértices y se almacenan en una matriz llamada búfer de vértices,
  • una lista de índices que definen las caras (o triángulos) almacenadas en una matriz denominada index buffer.

El último paso para la CPU es definir cómo renderizar la geometría; Para esta tarea, la CPU definirá sombreadores en la GPU. Los sombreadores son fragmentos de código que la GPU ejecutará para cada uno de los vértices y píxeles que debe renderizar. (Un vértice -o vértices cuando hay varios- es un “punto” en 3D).

Hay dos tipos de sombreadores: sombreadores de vértices y sombreadores de píxeles (o fragmentos).

Canalización de gráficos

Antes de profundizar en los sombreadores, retrocedamos. Para renderizar píxeles, la GPU tomará la geometría definida por la CPU y hará lo siguiente:

  • Utilizando el búfer de índice, se reúnen tres vértices para definir un triángulo.
  • El búfer de índice contiene una lista de índices de vértices. Esto significa que cada entrada en el búfer de índice es el número de un vértice en el búfer de vértices.
  • Esto es realmente útil para evitar duplicar vértices.

Por ejemplo, el siguiente búfer de índice es una lista de dos caras: [1 2 3 1 3 4]. La primera cara contiene el vértice 1, el vértice 2 y el vértice 3. La segunda cara contiene el vértice 1, el vértice 3 y el vértice 4. Entonces, hay cuatro vértices en esta geometría:

El sombreador de vértices se aplica a cada vértice del triángulo. El objetivo principal del sombreador de vértices es producir un píxel para cada vértice (la proyección en la pantalla 2D del vértice 3D):

Usando estos tres píxeles (que definen un triángulo 2D en la pantalla), la GPU interpolará todos los valores adjuntos al píxel (al menos sus posiciones) y el sombreador de píxeles se aplicará a cada píxel incluido en el triángulo 2D para generar un color para cada píxel:

Este proceso se realiza para cada cara definida por el búfer de índice.

Obviamente, debido a su naturaleza paralela, la GPU es capaz de procesar este paso para muchas caras simultáneamente y lograr un rendimiento realmente bueno.

GLSL

Acabamos de ver que para renderizar triángulos, la GPU necesita dos sombreadores: el sombreador de vértices y el sombreador de píxeles. Estos sombreadores están escritos en un lenguaje llamado Graphics Library Shader Language (GLSL). Parece c.

A continuación se muestra un ejemplo de un sombreador de vértices común:

precision highp float;// Attributesattribute vec3 position;attribute vec2 uv;// Uniformsuniform mat4 worldViewProjection;// Varyingvarying vec2 vUV;void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv;}

Estructura del sombreador de vértices

Un sombreador de vértices contiene lo siguiente:

  • Atributos . Un atributo define una parte de un vértice. Por defecto, un vértice debe contener al menos una posición (a vector3:x, y, z). Sin embargo, como desarrollador, puedes decidir agregar más información. Por ejemplo, en el sombreador anterior, hay un vector2nombre uv(es decir, coordenadas de textura que le permiten aplicar una textura 2D a un objeto 3D).
  • Uniformes . Un uniforme es una variable utilizada por el sombreador y definida por la CPU. El único uniforme que tenemos aquí es una matriz utilizada para proyectar la posición del vértice (x, y, z) en la pantalla (x, y).
  • Variar . Las variables variables son valores creados por el sombreador de vértices y transmitidos al sombreador de píxeles. Aquí, el sombreador de vértices transmitirá un valor vUV(una copia simple de uv) al sombreador de píxeles. Esto significa que aquí se define un píxel con una posición y coordenadas de textura. Estos valores serán interpolados por la GPU y utilizados por el sombreador de píxeles.
  • Principal . La función nombrada maines el código ejecutado por la GPU para cada vértice y debe al menos producir un valor para gl_position(la posición del vértice actual en la pantalla).

Podemos ver en nuestro ejemplo que el sombreador de vértices es bastante simple. Genera una variable de sistema (que comienza con gl_) llamada gl_positionpara definir la posición del píxel asociado y establece una variable variable llamada vUV.

 

El vudú detrás de las matrices

Lo que pasa con nuestro sombreador es que tenemos una matriz llamada worldViewProjectiony usamos esta matriz para proyectar la posición del vértice a la gl_positionvariable. Eso está bien, pero ¿cómo obtenemos el valor de esta matriz? Es uniforme, por lo que tenemos que definirlo en el lado de la CPU (usando JavaScript).

Esta es una de las partes complejas de hacer 3D. Debes entender matemáticas complejas (o tendrás que usar un motor 3D como Babylon.js, que veremos más adelante).

La worldViewProjectionmatriz es la combinación de tres matrices diferentes:

El uso de la matriz resultante nos permite transformar vértices 3D en píxeles 2D, teniendo en cuenta el punto de vista y todo lo relacionado con la posición, escala y rotación del objeto actual.

Esta es tu responsabilidad como desarrollador 3D: crear y mantener actualizada esta matriz.

Volver a los sombreadores

Una vez que el sombreador de vértices se ejecuta en cada vértice (tres veces, entonces), tendremos tres píxeles con el valor correcto gl_positiony un . vUVLa GPU interpolará estos valores en cada píxel contenido en el triángulo producido con estos píxeles.

Luego, para cada píxel, ejecutará el sombreador de píxeles:

precision highp float;varying vec2 vUV;uniform sampler2D textureSampler;void main(void) { gl_FragColor = texture2D(textureSampler, vUV);}

Estructura de sombreador de píxeles (o fragmentos)

La estructura de un sombreador de píxeles es similar a la de un sombreador de vértices:

  • Variar . Las variables variables son valores creados por el sombreador de vértices y transmitidos al sombreador de píxeles. Aquí, el sombreador de píxeles recibirá un vUVvalor del sombreador de vértices.
  • Uniformes . Un uniforme es una variable utilizada por el sombreador y definida por la CPU. El único uniforme que tenemos aquí es un sampler, que es una herramienta que se utiliza para leer los colores de las texturas.
  • Principal . La función nombrada maines el código ejecutado por la GPU para cada píxel y que debe producir al menos un valor para gl_FragColor(es decir, el color del píxel actual).

Este sombreador de píxeles es bastante simple: lee el color de la textura usando las coordenadas de textura del sombreador de vértices (que, a su vez, lo obtiene del vértice).

 

El problema es que cuando se desarrollan sombreadores, sólo estás a mitad del camino, porque luego tienes que lidiar con una gran cantidad de código WebGL. De hecho, WebGL es realmente poderoso pero también de muy bajo nivel, y usted tiene que hacerlo todo usted mismo, desde crear los buffers hasta definir las estructuras de los vértices. También tienes que hacer todos los cálculos, configurar todos los estados, manejar la carga de texturas, etc.

¿Demasiado duro? BABYLON.ShaderMaterial al rescate

Sé lo que estás pensando: "Los sombreadores son realmente geniales, pero no quiero molestarme con las tuberías internas de WebGL ni siquiera con las matemáticas".

¡Y tienes razón! Esta es una pregunta perfectamente legítima, ¡y es exactamente por eso que creé Babylon.js!

Para utilizar Babylon.js, primero necesita una página web sencilla:

!DOCTYPE htmlhtmlhead titleBabylon.js/title script src="Babylon.js"/script script type="application/vertexShader" precision highp float; // Attributes attribute vec3 position; attribute vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Normal varying vec2 vUV; void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv; } /script script type="application/fragmentShader" precision highp float; varying vec2 vUV; uniform sampler2D textureSampler; void main(void) { gl_FragColor = texture2D(textureSampler, vUV); } /script script src="index.js"/script style html, body { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden; margin: 0px; overflow: hidden; } #renderCanvas { width: 100%; height: 100%; touch-action: none; -ms-touch-action: none; } /style/headbody canvas/canvas/body/html

Notarás que los sombreadores están definidos por scriptetiquetas. Con Babylon.js, también puedes definirlos en archivos separados ( .fxarchivos).

  • Fuente Babylon.js
  • repositorio de GitHub

Finalmente, el código JavaScript principal es este:

"use strict";document.addEventListener("DOMContentLoaded", startGame, false);function startGame() { if (BABYLON.Engine.isSupported()) { var canvas = document.getElementById("renderCanvas"); var engine = new BABYLON.Engine(canvas, false); var scene = new BABYLON.Scene(engine); var camera = new BABYLON.ArcRotateCamera("Camera", 0, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas); // Creating sphere var sphere = BABYLON.Mesh.CreateSphere("Sphere", 16, 5, scene); var amigaMaterial = new BABYLON.ShaderMaterial("amiga", scene, { vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode", }, { attributes: ["position", "uv"], uniforms: ["worldViewProjection"] }); amigaMaterial.setTexture("textureSampler", new BABYLON.Texture("amiga.jpg", scene)); sphere.material = amigaMaterial; engine.runRenderLoop(function () { sphere.rotation.y += 0.05; scene.render(); }); }};

Puedes ver que lo uso BABYLON.ShaderMaterialpara deshacerme de la carga de compilar, vincular y manejar sombreadores.

 

Cuando crea BABYLON.ShaderMaterial, debe especificar el elemento DOM utilizado para almacenar los sombreadores o el nombre base de los archivos donde están los sombreadores. Si elige utilizar archivos, debe crear un archivo para cada sombreador y utilizar el siguiente patrón: basename.vertex.fxy basename.fragment.fx. Luego, tendrás que crear el material así:

var cloudMaterial = new BABYLON.ShaderMaterial("cloud", scene, "./myShader", { attributes: ["position", "uv"], uniforms: ["worldViewProjection"] });

También debes especificar los nombres de los atributos y uniformes que utilizas.

Luego, puede establecer directamente los valores de sus uniformes y muestreadores usando las funciones setTexture, setFloat, setFloats, setColor3, , , .setColor4setVector2setVector3setVector4setMatrix

Bastante simple, ¿verdad?

¿Y recuerdas la worldViewProjectionmatriz anterior, usando Babylon.js y BABYLON.ShaderMaterial. ¡Simplemente no tienes que preocuparte por eso! BABYLON.ShaderMateriallo calculará automáticamente porque lo declararás en la lista de uniformes.

BABYLON.ShaderMaterialTambién puede manejar las siguientes matrices por usted:

  • world,
  • view,
  • projection,
  • worldView,
  • worldViewProjection.

Ya no hay necesidad de matemáticas. Por ejemplo, cada vez que ejecute sphere.rotation.y += 0.05, la worldmatriz de la esfera se generará y se transmitirá a la GPU.

Crea tu propio sombreador (CYOS)

Ahora, vayamos más allá y creemos una página donde puedas crear dinámicamente tus propios sombreadores y ver el resultado inmediatamente. Esta página utilizará el mismo código que comentamos anteriormente y utilizará el BABYLON.ShaderMaterialobjeto para compilar y ejecutar los sombreadores que usted creará.

Utilicé el editor de código ACE para Create Your Own Shader (CYOS). Es un editor de código increíble, con resaltado de sintaxis. No dudes en echarle un vistazo .

Usando el primer cuadro combinado, podrá seleccionar sombreadores predefinidos. Veremos a cada uno de ellos inmediatamente después.

También puedes cambiar la malla (es decir, el objeto 3D) utilizada para obtener una vista previa de tus sombreadores usando el segundo cuadro combinado.

 

El botón de compilación se utiliza para crear uno nuevo BABYLON.ShaderMateriala partir de sus sombreadores. El código utilizado por este botón es el siguiente:

// CompileshaderMaterial = new BABYLON.ShaderMaterial("shader", scene, { vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode",}, { attributes: ["position", "normal", "uv"], uniforms: ["world", "worldView", "worldViewProjection"] });var refTexture = new BABYLON.Texture("ref.jpg", scene);refTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;refTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;var amigaTexture = new BABYLON.Texture("amiga.jpg", scene);shaderMaterial.setTexture("textureSampler", amigaTexture);shaderMaterial.setTexture("refSampler", refTexture);shaderMaterial.setFloat("time", 0);shaderMaterial.setVector3("cameraPosition", BABYLON.Vector3.Zero());shaderMaterial.backFaceCulling = false;mesh.material = shaderMaterial;

Increíblemente simple, ¿verdad? El material está listo para enviarle tres matrices precalculadas ( world, worldViewy worldViewProjection). Los vértices vendrán con coordenadas de posición, normales y de textura. También hay dos texturas cargadas para ti:

Finalmente, renderLoopes donde actualizo dos uniformes convenientes:

  • Uno es llamado timey recibe algunas animaciones divertidas.
  • El otro se llama cameraPosition, que obtiene la posición de la cámara en los sombreadores (útil para ecuaciones de iluminación).
engine.runRenderLoop(function () { mesh.rotation.y += 0.001; if (shaderMaterial) { shaderMaterial.setFloat("time", time); time += 0.02; shaderMaterial.setVector3("cameraPosition", camera.position); } scene.render();});

Sombreador básico

Comencemos con el primer sombreador definido en CYOS: el sombreador básico.

Ya conocemos este sombreador. Calcula gl_positiony utiliza coordenadas de textura para obtener un color para cada píxel.

Para calcular la posición del píxel, sólo necesitamos la worldViewProjectionmatriz y la posición del vértice:

precision highp float;// Attributesattribute vec3 position;attribute vec2 uv;// Uniformsuniform mat4 worldViewProjection;// Varyingvarying vec2 vUV;void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv;}

Las coordenadas de textura ( uv) se transmiten sin modificaciones al sombreador de píxeles.

Tenga en cuenta que debemos agregar precision mediump floatla primera línea para los sombreadores de vértices y píxeles porque Chrome lo requiere. Especifica que, para un mejor rendimiento, no utilizamos valores flotantes de precisión total.

El sombreador de píxeles es aún más simple, porque solo necesitamos usar coordenadas de textura y buscar un color de textura:

precision highp float;varying vec2 vUV;uniform sampler2D textureSampler;void main(void) { gl_FragColor = texture2D(textureSampler, vUV);}

Anteriormente vimos que el textureSampleruniforme se rellena con la amigatextura. Entonces, el resultado es el siguiente:

 

Sombreador en blanco y negro

Continuamos con un nuevo sombreador: el sombreador de blanco y negro. El objetivo de este sombreador es utilizar el anterior pero con un modo de renderizado solo en blanco y negro.

Para hacerlo, podemos mantener el mismo sombreador de vértices. El sombreador de píxeles se modificará ligeramente.

La primera opción que tenemos es coger solo un componente, como por ejemplo el verde:

precision highp float;varying vec2 vUV;uniform sampler2D textureSampler;void main(void) { gl_FragColor = vec4(texture2D(textureSampler, vUV).ggg, 1.0);}

Como puede ver, en lugar de usar .rgb(esta operación se llama swizzle), hemos usado .ggg.

Pero si queremos un efecto de blanco y negro realmente preciso, entonces sería mejor calcular la luminancia (que tiene en cuenta todos los componentes):

precision highp float;varying vec2 vUV;uniform sampler2D textureSampler;void main(void) { float luminance = dot(texture2D(textureSampler, vUV).rgb, vec3(0.3, 0.59, 0.11)); gl_FragColor = vec4(luminance, luminance, luminance, 1.0);}

La dotoperación (o dotproducto) se calcula así: result = v0.x * v1.x + v0.y * v1.y + v0.z * v1.z.

Entonces, en nuestro caso, luminance = r * 0.3 + g * 0.59 + b * 0.11. (Estos valores se basan en el hecho de que el ojo humano es más sensible al verde).

Suena genial, ¿no?

Sombreador de sombreado de celdas

Pasemos a un sombreador más complejo: el sombreador de celdas.

Éste requerirá que obtengamos la normalidad del vértice y la posición del vértice en el sombreador de píxeles. Entonces, el sombreador de vértices se verá así:

precision highp float;// Attributesattribute vec3 position;attribute vec3 normal;attribute vec2 uv;// Uniformsuniform mat4 world;uniform mat4 worldViewProjection;// Varyingvarying vec3 vPositionW;varying vec3 vNormalW;varying vec2 vUV;void main(void) { vec4 outPosition = worldViewProjection * vec4(position, 1.0); gl_Position = outPosition; vPositionW = vec3(world * vec4(position, 1.0)); vNormalW = normalize(vec3(world * vec4(normal, 0.0))); vUV = uv;}

Tenga en cuenta que también utilizamos la matriz mundial porque la posición y la normal se almacenan sin ninguna transformación, y debemos aplicar la matriz mundial para tener en cuenta la rotación del objeto.

El sombreador de píxeles es el siguiente:

precision highp float;// Lightsvarying vec3 vPositionW;varying vec3 vNormalW;varying vec2 vUV;// Refsuniform sampler2D textureSampler;void main(void) { float ToonThresholds[4]; ToonThresholds[0] = 0.95; ToonThresholds[1] = 0.5; ToonThresholds[2] = 0.2; ToonThresholds[3] = 0.03; float ToonBrightnessLevels[5]; ToonBrightnessLevels[0] = 1.0; ToonBrightnessLevels[1] = 0.8; ToonBrightnessLevels[2] = 0.6; ToonBrightnessLevels[3] = 0.35; ToonBrightnessLevels[4] = 0.2; vec3 vLightPosition = vec3(0, 20, 10); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); // diffuse float ndl = max(0., dot(vNormalW, lightVectorW)); vec3 color = texture2D(textureSampler, vUV).rgb; if (ndl ToonThresholds[0]) { color *= ToonBrightnessLevels[0]; } else if (ndl ToonThresholds[1]) { color *= ToonBrightnessLevels[1]; } else if (ndl ToonThresholds[2]) { color *= ToonBrightnessLevels[2]; } else if (ndl ToonThresholds[3]) { color *= ToonBrightnessLevels[3]; } else { color *= ToonBrightnessLevels[4]; } gl_FragColor = vec4(color, 1.);}

El objetivo de este sombreador es simular la luz y, en lugar de calcular un sombreado suave, aplicaremos la luz de acuerdo con umbrales de brillo específicos. Por ejemplo, si la intensidad de la luz está entre 1 (máximo) y 0,95, el color del objeto (obtenido de la textura) se aplicaría directamente. Si la intensidad está entre 0,95 y 0,5, el color se atenuaría en un factor de 0,8. Etcétera.

 

Hay principalmente cuatro pasos en este sombreador.

Primero, declaramos umbrales y niveles constantes.

Luego, calculamos la iluminación usando la ecuación de Phong (consideraremos que la luz no se mueve):

vec3 vLightPosition = vec3(0, 20, 10);// Lightvec3 lightVectorW = normalize(vLightPosition - vPositionW);// diffusefloat ndl = max(0., dot(vNormalW, lightVectorW));

La intensidad de la luz por píxel depende del ángulo entre la dirección normal y la de la luz.

Luego, obtenemos el color de textura del píxel.

Finalmente, comprobamos el umbral y aplicamos el nivel al color.

El resultado parece un objeto de dibujos animados:

Phong Sombreador

Usamos una parte de la ecuación de Phong en el sombreador anterior. Usémoslo completamente ahora.

El sombreador de vértices es claramente simple aquí porque todo se hará en el sombreador de píxeles:

precision highp float;// Attributesattribute vec3 position;attribute vec3 normal;attribute vec2 uv;// Uniformsuniform mat4 worldViewProjection;// Varyingvarying vec3 vPosition;varying vec3 vNormal;varying vec2 vUV;void main(void) { vec4 outPosition = worldViewProjection * vec4(position, 1.0); gl_Position = outPosition; vUV = uv; vPosition = position; vNormal = normal;}

Según la ecuación, debemos calcular las partes “difusa” y “especular” usando la dirección de la luz y la normal del vértice:

precision highp float;// Varyingvarying vec3 vPosition;varying vec3 vNormal;varying vec2 vUV;// Uniformsuniform mat4 world;// Refsuniform vec3 cameraPosition;uniform sampler2D textureSampler;void main(void) { vec3 vLightPosition = vec3(0, 20, 10); // World values vec3 vPositionW = vec3(world * vec4(vPosition, 1.0)); vec3 vNormalW = normalize(vec3(world * vec4(vNormal, 0.0))); vec3 viewDirectionW = normalize(cameraPosition - vPositionW); // Light vec3 lightVectorW = normalize(vLightPosition - vPositionW); vec3 color = texture2D(textureSampler, vUV).rgb; // diffuse float ndl = max(0., dot(vNormalW, lightVectorW)); // Specular vec3 angleW = normalize(viewDirectionW + lightVectorW); float specComp = max(0., dot(vNormalW, angleW)); specComp = pow(specComp, max(1., 64.)) * 2.; gl_FragColor = vec4(color * ndl + vec3(specComp), 1.);}

Ya usamos la parte difusa en el sombreador anterior, así que aquí solo necesitamos agregar la parte especular. Puede encontrar más información sobre el sombreado Phong en Wikipedia .

 

El resultado de nuestra esfera:

Descartar sombreador

Para el sombreador de descarte, me gustaría introducir un nuevo concepto: la discardpalabra clave.

Este sombreador descarta todos los píxeles que no sean rojos y crea la ilusión de un objeto excavado.

El sombreador de vértices es el mismo que utiliza el sombreador básico:

precision highp float;// Attributesattribute vec3 position;attribute vec3 normal;attribute vec2 uv;// Uniformsuniform mat4 worldViewProjection;// Varyingvarying vec2 vUV;void main(void) { gl_Position = worldViewProjection * vec4(position, 1.0); vUV = uv;}

El sombreador de píxeles de su lado tendrá que probar el color y descartar cuando, por ejemplo, el componente verde sea demasiado alto:

precision highp float;varying vec2 vUV;// Refsuniform sampler2D textureSampler;void main(void) { vec3 color = texture2D(textureSampler, vUV).rgb; if (color.g 0.5) { discard; } gl_FragColor = vec4(color, 1.);}

El resultado es un poco divertido:

Sombreador de ondas

Hemos jugado mucho con el sombreador de píxeles, pero también quiero que sepan que podemos hacer muchas cosas con los sombreadores de vértices.

Para el sombreador de ondas, reutilizaremos el sombreador de píxeles Phong.

El sombreador de vértices utilizará el uniforme nombrado timepara obtener algunos valores animados. Usando este uniforme, el sombreador generará una onda con las posiciones de los vértices:

precision highp float;// Attributesattribute vec3 position;attribute vec3 normal;attribute vec2 uv;// Uniformsuniform mat4 worldViewProjection;uniform float time;// Varyingvarying vec3 vPosition;varying vec3 vNormal;varying vec2 vUV;void main(void) { vec3 v = position; v.x += sin(2.0 * position.y + (time)) * 0.5; gl_Position = worldViewProjection * vec4(v, 1.0); vPosition = position; vNormal = normal; vUV = uv;}

Se aplica un seno position.yy el resultado es el siguiente:

Mapeo del entorno esférico

Este se inspiró en gran medida en el artículo " Creación de un sombreador de mapeo de entorno/reflexión esférica ". Te dejaré leer ese excelente artículo y jugar con el sombreador asociado.

Sombreador Fresnel

Me gustaría concluir este artículo con mi favorito: el sombreador Fresnel.

Este sombreador se utiliza para aplicar una intensidad diferente según el ángulo entre la dirección de la vista y la normal del vértice.

El sombreador de vértices es el mismo que usa el sombreador de celdas, y podemos calcular fácilmente el término de Fresnel en nuestro sombreador de píxeles (porque tenemos la normal y la posición de la cámara, que se pueden usar para evaluar la dirección de la vista):

precision highp float;// Lightsvarying vec3 vPositionW;varying vec3 vNormalW;// Refsuniform vec3 cameraPosition;uniform sampler2D textureSampler;void main(void) { vec3 color = vec3(1., 1., 1.); vec3 viewDirectionW = normalize(cameraPosition - vPositionW); // Fresnel float fresnelTerm = dot(viewDirectionW, vNormalW); fresnelTerm = clamp(1.0 - fresnelTerm, 0., 1.); gl_FragColor = vec4(color * fresnelTerm, 1.);}

¿Tu sombreador?

Ahora estás más preparado para crear tu propio sombreador. ¡No dudes en publicar en el foro Babylon.js para compartir tus experimentos!

Si quieres ir más allá, aquí tienes algunos enlaces útiles:

  • Babylon.js , sitio web oficial
  • Babylon.js , repositorio de GitHub
  • Foro Babylon.js , Desarrolladores de juegos HTML5
  • Crea tu propio sombreador (CYOS) , Babylon.js
  • Lenguaje de sombreado OpenGL ”, Wikipedia
  • Lenguaje de sombreado OpenGL , documentación

Otras lecturas

  • Creación de un juego WebGL multiplataforma con Babylon.js
  • Uso de la API de Gamepad en juegos web
  • Introducción al modelado poligonal y Three.js
  • Cómo crear una caja de ritmos responsiva de 8 bits

(rb, al, mrn)Explora más en

  • Codificación
  • Herramientas
  • javascript
  • Técnicas





Tal vez te puede interesar:

  1. Creación de su propia biblioteca de validación de React: la experiencia del desarrollador (Parte 3)
  2. Introducción a Quasar Framework: creación de aplicaciones multiplataforma
  3. Creación de un componente web retro que se puede arrastrar con iluminación
  4. Creación y acoplamiento de una aplicación Node.js con arquitectura sin estado con la ayuda de Kinsta

Creación de sombreadores con Babylon.js

Creación de sombreadores con Babylon.js

Canalización de gráficosGLSLEstructura del sombreador de vérticesEl vudú detrás de las matricesVolver a los sombreadoresEstructura de sombreador de píxel

programar

es

https://pseint.es/static/images/programar-creacion-de-sombreadores-con-babylon-904-0.jpg

2024-05-20

 

Creación de sombreadores con Babylon.js
Creación de sombreadores con Babylon.js

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Update cookies preferences