Tu primer shader 2D

Introducción

Los shaders son programas especiales que se ejecutan en la GPU y se utilizan para renderizar gráficos. Todo el renderizado moderno se realiza mediante shaders. Para obtener una descripción más detallada de qué son los shaders, consulta Qué son los shaders.

Este tutorial se centrará en los aspectos prácticos de la escritura de programas shaders, guiándole a través del proceso de escritura de un shader con funciones tanto de vértice como de fragmento. Este tutorial está dirigido a principiantes absolutos de shaders.

Nota

Si tienes experiencia escribiendo shaders y sólo buscas una visión general de cómo funcionan los shaders en Godot, ver la Shading Reference.

Organización

CanvasItem los shaders se utilizan para dibujar todo en 2D

En Godot, los objetos 2D se dibujan utilizando los shaders CanvasItem, mientras que los shaders Spatial se utilizan para dibujar todos los objetos 3D.

Para usar un shader debe estar unido dentro de un Material que debe estar unido a un objeto. Los materiales son un tipo de Resource. Para dibujar varios objetos con el mismo material, el material debe estar unido a cada objeto.

Todos los objetos derivados de un CanvasItem tienen una propiedad material. Esto incluye todos los elementos GUI, Sprites, TileMaps, MeshInstance2Ds etc. También tienen la opción de heredar el material de sus padres. Esto puede ser útil si tienes un gran número de nodos que quieren usar el mismo material.

Para empezar, crea un nodo Sprite. Puedes usar cualquier CanvasItem, pero para este tutorial usaremos un Sprite.

En el Inspector, haga clic al lado de "Texture" donde dice "[empty]" y seleccione "Cargar", luego seleccione "Icon.png". Para nuevos proyectos, este es el icono de Godot. Ahora deberías ver el icono en la vista.

A continuación, mira en el Inspector, en la sección CanvasItem, haz clic al lado de "Material" y selecciona "New ShaderMaterial". Esto crea un nuevo recurso material. Haz clic en la esfera que aparece. Actualmente, Godot no sabe si está escribiendo un CanvasItem Shader o un Spatial Shader y previsualiza la salida de los shaders espaciales. Así que lo que está viendo es la salida del Spatial Shader por defecto.

Haz clic al lado de "Shader" y selecciona "New Shader". Finalmente, haz clic en el nuevo recurso de shaders y se abrirá el editor de shaders. Ya estás listo para empezar a escribir tu primer shader.

Tu primer shader CanvasItem

En Godot, todos los shaders deben especificar el tipo en la primera línea, en el siguiente formato:

shader_type canvas_item;

Porque estamos escribiendo un shader de CanvasItem, especificamos canvas_item en la primera línea. Todo nuestro código irá debajo de esta declaración.

Esta línea le dice al motor cuáles son las variables incorporadas y la funcionalidad que debe suministrarles.

En Godot puedes sobreescribir tres funciones para controlar el funcionamiento del shader: vertex, fragment y light. Este tutorial te guiará a través de la escritura de shader con ambas funciones de vértice y fragmento. Las funciones de la luz son significativamente más complejas que las funciones de vértice y fragmento, por lo que no serán tratadas aquí.

Tu primera función fragment

La función de fragmento se ejecuta para cada píxel de un Sprite y determina de qué color debe ser ese píxel.

Están restringidos a los píxeles cubiertos por el Sprite, eso significa que no puedes usar uno para, por ejemplo, crear un contorno alrededor de un Sprite.

La función de fragmento más básica no hace nada excepto asignar un solo color a cada píxel.

Lo hacemos escribiendo un vec4 en la variable incorporada COLOR. Vec4 es la abreviatura para construir un vector con 4 números. Para más información sobre los vectores, ver el tutorial de matemáticas de vectores: ref:Vector math tutorial <doc_vector_math> COLOR es tanto una variable de entrada a la función de fragmentos como la salida final de la misma.

void fragment(){
  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
}
../../../_images/blue-box.png

¡Felicidades! Ya has terminado. Has escrito con éxito tu primer shader en Godot.

Ahora hagamos las cosas más complejas.

Hay muchas entradas para la función de fragmentos que puedes usar para calcular el COLOR. UV es una de ellas. Las coordenadas UV se especifican en tu Sprite (¡sin que lo sepas!) y le dicen al shader dónde leer de las texturas para cada parte de la malla.

En la función de fragmentos sólo puedes leer desde UV, pero puedes usarla en otras funciones o para asignar valores a COLOR directamente.

"UV" varía entre 0-1 de izquierda a derecha y de arriba a abajo.

../../../_images/iconuv.png
void fragment() {
  COLOR = vec4(UV, 0.5, 1.0);
}
../../../_images/UV.png

Usando la TEXTURE incorporada

Cuando quieres ajustar un color en un Sprite no puedes simplemente ajustar el color de la textura manualmente como en el código de abajo.

void fragment(){
  //this shader will result in an all white rectangle
  COLOR.b = 1.0;
}

La función de fragmento por defecto lee de una textura y la muestra. Cuando se sobreescribe la función de fragmento por defecto, se pierde esa función, por lo que hay que implementarla uno mismo. Lees de las texturas usando la función texture. Ciertos nodos, como los Sprites, tienen una variable de textura dedicada a la que se puede acceder en el sombreador usando TEXTURE. Úsala junto con "UV" y "textura" para dibujar el Sprite.

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = 1.0; //set blue channel to 1.0
}
../../../_images/blue-tex.png

Entrada uniform

La entrada uniforme se utiliza para pasar los datos a un shader que será el mismo en todo el shader.

Puedes usar los uniformes definiéndolos en la parte superior de tu shader así:

uniform float size;

Para más información sobre el uso, lee Shading Language doc.

Añade un uniforme para cambiar la cantidad de azul en nuestro Sprite.

uniform float blue = 1.0; // you can assign a default value to uniforms

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = blue;
}

Ahora puedes cambiar la cantidad de azul en el Sprite desde el editor. Mira al Inspector debajo de donde creaste tu shader. Deberías ver una sección llamada "Shader Param". Despliega esa sección y verás el uniforme que acabas de declarar. Si cambias el valor en el editor, éste sobrescribirá el valor por defecto que proporcionaste en el shader.

Interactuando con shaders desde código

Puedes cambiar los uniformes del código usando la función set_shader_param() que se llama en el recurso material del nodo. Con un nodo Sprite, el siguiente código puede ser usado para establecer el uniforme azul.

var blue_value = 1.0
material.set_shader_param("blue", blue_value)

Observe que el nombre del uniforme es una string. La string debe coincidir exactamente con la forma en que está escrita en el shader, incluyendo la ortografía y las mayúsculas.

Tu primera función vertex

Ahora que tenemos una función de fragmento, escribamos una función de vértice.

Utiliza la función de vértice para calcular en qué parte de la pantalla debe terminar cada vértice.

La variable más importante en la función de vértice es VERTEX. Inicialmente, especifica las coordenadas de los vértices en tu modelo, pero también le escribes para determinar dónde dibujar esos vértices. VERTEX es un vec2 que se presenta inicialmente en el espacio local (es decir, no en relación con la cámara, el puerto de visualización, o los nodos principales).

Puedes desplazar los vértices añadiendo directamente a VERTEX.

void vertex() {
  VERTEX += vec2(10.0, 0.0);
}

Combinado con la variable incorporada TIME, esto puede ser usado para una simple animación.

void vertex() {
  // Animate Sprite moving in big circle around its location
  VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
}

Conclusión

En su núcleo, los shaders hacen lo que has visto hasta ahora, calculan el VERTEX y el COLOR. Depende de ti soñar con estrategias matemáticas más complejas para asignar valores a esas variables.

Para inspirarse, eche un vistazo a algunos de los tutoriales de shaders más avanzados, y mira a otros sitios como Shadertoy y The Book of Shaders.