Funciones de modelado

Starter pack

La lógica del shader es especial, es diferente de lo que estamos acostumbrados en JavaScript u otros lenguajes de programación. Aunque la sintaxis es similar, la forma en que implementamos la lógica es distinta. Es un poco como una caja negra, pones algunos datos y obtienes algunos colores para el fragment shader, o algunas posiciones para el vertex shader.

Pero, ¿cómo se controla la salida? ¿Cómo haces que se vea como quieres? Aquí es donde entran las funciones de modelado.

Nos enfocaremos en el fragment shader para esta lección, pero los mismos principios se aplican al vertex shader.

Zona de práctica

Para dominar el arte del modelado, necesitarás practicar y experimentar. Te llevará tiempo acostumbrarte a la forma en que funciona el shader, pero una vez que lo domines, podrás crear efectos increíbles.

Porque quiero ponerte en la mejor posición para tener éxito, he preparado esta escena de Galería de Arte 3D para que visualices tus shaders en tiempo real en un ambiente agradable.

¿Puedes sentir fluir tu creatividad? ¡Todos esos marcos vacíos están esperando tus obras maestras!

El modelo 3D usado es VR Gallery de Maxim Mavrichev y está licenciado bajo Creative Commons Attribution.

Usé <CameraControls /> para crear una cámara en primera persona para navegar dentro de la galería y Squoosh para reducir el tamaño de las texturas.

SimpleShaderMaterial

Todos los marcos de la galería son copias de SimpleShaderMaterial, es un shader personalizado básico que preparé para ti con:

  • uColor: un color
  • uTime: el tiempo transcurrido desde el comienzo de la aplicación
  • vUv: las coordenadas UV del fragment (0 a 1 en ambos ejes)

Todos se extienden en el <App.jsx /> para poder usarlos de forma declarativa en nuestra escena.

Se nombran en función de su posición en la galería, pero siéntete libre de renombrarlos a algo más significativo una vez que hayas creado obras maestras con ellos.

Funciones

Para cada fragmento de nuestro shader, nuestro mismo código se ejecutará con diferentes entradas.

Si queremos dibujar una línea, podríamos usar if statements para verificar si el píxel está dentro de la línea o no. Pero hacer formas más complejas sería muy difícil e ineficiente.

En su lugar, usamos funciones para dar forma a nuestra salida. No te preocupes, no necesitas ser un experto en matemáticas para usarlas. Solo necesitas entender qué funciones están a tu disposición y qué hacen.

Piensa en una función como una máquina que toma una entrada y te da una salida. Por ejemplo, la función f(x) = x * 2 toma un número x y te da x * 2 como resultado.

Para visualizar el efecto de las diferentes funciones, utilizaremos Graphtoy, es una herramienta simple para escribir funciones y ver su salida. Ayuda a validar nuestra comprensión de las funciones cuando experimentamos con ellas en nuestros shaders.

Vamos a visualizar nuestra función f(x) = x * 2 en Graphtoy:

Graphtoy

Graphtoy se convertirá rápidamente en tu mejor amigo cuando experimentes con shaders.

Es hora de experimentar con las diferentes funciones a nuestra disposición.

Step

La función step es una función simple que devuelve 0 si la entrada es menor que un umbral, y 1 si la entrada es mayor que el umbral.

Toma dos parámetros:

  • edge: el umbral
  • x: el valor de entrada

Vamos a probarlo en el fragment shader del frame frontal ArtFront02Material.jsx:

void main() {
  float pct = 1.0;
  pct = step(0.5, vUv.x);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Declaramos una variable pct para determinar el porcentaje del color que queremos mostrar. La configuramos a 1.0 por defecto, y luego usamos la función step para configurarla a 0.0 si el vUv.x es menor que 0.5.

Representación de la función Step

Podemos ver que la mitad izquierda del frame es negra, y la mitad derecha tiene el color establecido en el uniforme uColor.

Vamos a aplicarlo al eje vertical con un umbral diferente:

void main() {
  float pct = 1.0;
  pct = step(0.2, vUv.y);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Representación vertical de la función Step

Podemos ver que el 20% de la parte inferior del frame es negra, y el resto es púrpura.

Las coordenadas UV tienen el origen en la esquina inferior izquierda del frame, por lo que [0, 0] es la esquina inferior izquierda, y [1, 1] es la esquina superior derecha.

Si quieres revertir el efecto, simplemente puedes intercambiar los parámetros de la función step:

void main() {
  float pct = 1.0;
  pct = step(vUv.y, 0.2);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Representación revertida de la función Step

Ahora tenemos el efecto opuesto.

Mix

La función mix es una función simple que devuelve una interpolación lineal entre dos valores.

Toma tres parámetros:

  • x: el primer valor
  • y: el segundo valor
  • a: el valor a interpolar entre x y y

Vamos a probarlo:

void main() {
  float pct = 1.0;
  pct = mix(0.0, 1.0, vUv.x);
  vec3 finalColor = pct * uColor;
  gl_FragColor = vec4(finalColor, 1.0);
}

Mix function representation

Podemos ver un bonito degradado de negro a púrpura.

Como muchas otras funciones, puedes usarla en otros tipos de datos, como los componentes vectoriales. Vamos a usarla para crear un degradado de blanco a púrpura en el eje vertical:

void main() {
  vec3 whiteColor = vec3(1.0);
  vec3 finalColor = mix(whiteColor, uColor, vUv.y);
  gl_FragColor = vec4(finalColor, 1.0);
}

Mix function two colors

Podemos ver un bonito degradado de blanco a púrpura.

Vamos a cambiar el valor de interpolación llamando a la función mix una primera vez para obtener el valor pct, y luego usarlo para interpolar entre el whiteColor y el uColor:

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = mix(0.0, 0.3, vUv.y);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Mix function two colors with pct

El púrpura es muy claro ya que el valor máximo de pct es 0.3.

Utiliza Graphtoy para experimentar con la función mix y entender cómo funciona.

Graphtoy mix function

3 interpolaciones diferentes con la función mix dan resultados completamente diferentes.

Smoothstep

La función smoothstep es una función simple que devuelve una interpolación suave entre dos valores.

Toma tres parámetros:

  • edge0: el borde inferior
  • edge1: el borde superior
  • x: el valor a interpolar entre edge0 y edge1

Devuelve tres resultados diferentes:

  • 0.0 si x es menor que edge0
  • 1.0 si x es mayor que edge1
  • una interpolación suave entre 0.0 y 1.0 si x está entre edge0 y edge1

Vamos a usarlo para cambiar nuestro valor pct:

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = smoothstep(0.4, 0.6, vUv.y);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Smoothstep function representation

La transición ahora es solo entre 0.4 y 0.6. Todo lo que está por debajo es blanco, y todo lo que está por encima es púrpura.

Min & Max

min y max son funciones simples que devuelven el valor mínimo o máximo entre dos entradas. Funcionan exactamente como las funciones Math.min y Math.max en JavaScript.

Vamos a usarlas para ajustar nuestro valor pct. Primero vamos a asegurarnos de que nunca sea inferior a 0.4 (lo que significa que nunca será totalmente blanco):

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = smoothstep(0.4, 0.6, vUv.y);
  pct = max(pct, 0.4);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Y nunca superior a 0.6 (lo que significa que nunca será totalmente púrpura):

void main() {
  vec3 whiteColor = vec3(1.0);
  float pct = smoothstep(0.4, 0.6, vUv.y);
  pct = max(pct, 0.4);
  pct = min(pct, 0.6);
  vec3 finalColor = mix(whiteColor, uColor, pct);
  gl_FragColor = vec4(finalColor, 1.0);
}

Min & Max function representation

Los colores están deslavados y la transición es muy suave.

Vamos a visualizarlo en Graphtoy:

Max graph

Representación de max(x, 0.4)

Min graph

Representación de min(x, 0.6)

Y si los combinamos:

Min & Max graph

Puedes ver que nuestros valores nunca bajan de 0.4 y nunca suben de 0.6.

Nuestra combinación de las funciones min y max puede ser reemplazada por la función clamp, que es una abreviatura de min(max(x, min), max).

Operaciones y patrones

Antes de descubrir muchas otras funciones útiles, veamos cómo podemos usar operaciones para crear patrones.

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.