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.
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'
</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
<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:
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:
// ...
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:
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
<script setup lang="ts">
import { shallowRef } from 'vue'
import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'
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
.
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