Skip to content

Shaders

Esta guía te ayudará a comenzar con los shaders en TresJS.

Construiremos una escena simple con un blob. Luego alo animaremos para distorsionarlo suavemente.

WARNING

Es necesario tener conocimientos básicos sobre cómo funcionan los shaders

Configurando la escena (opcional)

Importamos todos los módulos que necesitamos. Para mayor comodidad, podemos usar los orbit-controls de cientos. Consulta aquí para ver cómo.

Ahora, coloquemos nuestra cámara en la posición [11,11,11].

Por último, para ayudarnos con la ubicación, agreguemos un plano simple, rotado en el eje X, con una medida de [10, 10] unidades.

vue
<script setup lang="ts">
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
</script>

<template>
  <TresCanvas
    clear-color="#111"
    window-size
  >
    <OrbitControls />
    <TresPerspectiveCamera :position="[11, 11, 11]" />

    <TresMesh :rotation="[-Math.PI / 2, 0, 0]">
      <TresPlaneGeometry :args="[10, 10]" />
      <TresMeshBasicMaterial color="#444" />
    </TresMesh>
  </TresCanvas>
</template>

ShaderMaterial

Como sabes, cada instancia en ThreeJs está disponible en TresJs, por lo que también podemos usar el ShaderMaterial, solo necesitamos agregar el prefijo Tres para utilizarlo.

Para nuestro blob, podríamos usar una simple SphereGeometry agregando algunos widthSegments y heightSegments para crear un efecto suave, y colocar nuestro blob 4 unidades en el eje Y positivo.

vue
<TresMesh :position="[0, 4, 0]">
  <TresSphereGeometry :args="[2, 32, 32]" />
  <TresShaderMaterial />
</TresMesh>

El ShaderMaterial acepta propiedades especiales, como uniforms, vertexShader y fragmentShader, por lo que podemos crearlo en nuestra sección de script y hacer la conexión con nuestra instancia.

Para este ejemplo, nuestros uniforms se ven así:

ts
import { Vector2 } from 'three'

// ...
const uniforms = {
  uTime: { value: 0 },
  uAmplitude: { value: new Vector2(0.1, 0.1) },
  uFrequency: { value: new Vector2(20, 5) },
}
// ..

Nuestro fragment shader se ve así:

ts
// ...
const fragmentShader = `
precision mediump float;
varying vec2 vUv;

void main() {
    gl_FragColor = vec4(1.0, vUv.y, 0.5, 1.0);
}
`
// ..

Y finalmente nuestro vertexShader:

ts
const vertexShader = `
uniform vec2 uAmplitude;
uniform vec2 uFrequency;
uniform float uTime;

varying vec2 vUv;

void main() {
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    modelPosition.y += sin(modelPosition.x * uFrequency.x - uTime) * uAmplitude.x;
    modelPosition.x += cos(modelPosition.y * uFrequency.y - uTime) * uAmplitude.y;

    vec4 viewPosition = viewMatrix * modelPosition;
    gl_Position = projectionMatrix * viewPosition;
    vUv = uv;
}
`
// ..

Animando el blob

Similar a lo que aprendimos en el ejemplo de Animaciones básicas, comenzamos haciendo referencia a nuestro blob utilizando Template Ref

vue
<script setup lang="ts">
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
import { shallowRef } from 'vue'

const blobRef = shallowRef(null)
// ...
</script>

<template>
  <TresCanvas
    clear-color="#111"
    window-size
  >
    <OrbitControls />
    <TresPerspectiveCamera :position="[11, 11, 11]" />
    <TresMesh
      ref="blobRef"
      :position="[0, 4, 0]"
    >
      <TresSphereGeometry :args="[2, 32, 32]" />
      <TresShaderMaterial />
    </TresMesh>
  </TresCanvas>
</template>

Una vez que hayamos hecho eso, podemos usar el callback onLoop para animar nuestro uTime.

ts
import { TresCanvas, useRenderLoop } from '@tresjs/core'

// ...
const { onLoop } = useRenderLoop()

onLoop(({ elapsed }) => {
  if (blobRef.value) {
    blobRef.value.material.uniforms.uTime.value = elapsed
  }
})
// ...

Y eso es todo, tenemos nuestro shader básico funcionando sin problemas.

Usando GLSL vite-plugin (opcional)

Este paso es completamente opcional y está fuera del alcance del equipo de TresJs

Definir nuestro shader en línea no siempre es la mejor idea, pero si estás utilizando vite, puedes colocar tus archivos GLSL en un archivo diferente utilizando el vite-plugin-glsl (consulta el enlace para obtener la documentación oficial).

Y podrías tener una estructura similar a esta:

├── src/
│   ├── myTresJsComponent.vue
│   ├── shaders/
│       ├── vertexShader.glsl
│       ├── fragmentShader.glsl