Skip to content

Shaders

This guide will help you get started with shaders in TresJS.

We will build a simple scene with a blob. We will then animate the blob to softly distorted it.

WARNING

Basic knowledge of how shaders work is necessary

Setting up the scene (optional)

We import all the modules that we need. To make it more convenient we will import and use the orbit-controls from cientos, look here to see how.

Now, let's put our camera in the [11,11,11] position.

Lastly just to help us with the location, let's add a simple plane, rotated in the X axis, with [10, 10] units.

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

As you know every instance in ThreeJs is available in TresJs, so is the ShaderMaterial, we just need to add the Tres prefix to use it.

For our blob, we could use a simple SphereGeometry adding some widthSegments and heightSegments to create a smooth effect, and put our blob 4 units in the Y positive axis

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

The ShaderMaterial accepts special properties, like uniforms vertexShader and fragmentShader, so we can create it in our script section and make the bind with our instance.

For this example, our uniforms look like this:

ts
import { Vector2 } from 'three'

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

Our fragment shader looks like this:

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

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

And lastly our 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;
}
`
// ..

Animating the blob

Similar to what we learn in the Basic animations example, we start by referencing our blob, using 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 :vertex-shader="vertexShader" :fragment-shader="fragmentShader" :uniforms="uniforms" />
    </TresMesh>
  </TresCanvas>
</template>

Once we have got that, we could use the onLoop callback to animate our uTime.

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

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

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

And that's it, we have our basic shader running smoothly. 🎉

Using GLSL vite-pluging (optional)

This step is completly optional and is out of the scope of the TresJs team

Defining our shader inline is not always the best idea, but if you're using vite you can put your GLSL files in a different file just by using the vite-plugin-glsl (check out the link for the official documentation).

And you could have a structure similar to this:

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