Blog>
Snippets

Custom Shaders with Three.js

Write custom vertex and fragment shaders for a unique visual effect on a Three.js mesh using the ShaderMaterial.
// Vertex shader code
const vertexShader = `
  varying vec3 vPosition;

  void main() {
    vPosition = position;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;
This vertex shader code simply takes the position of each vertex and sets it up for the fragment shader, passing the position through to the varying variable vPosition. It calculates the final position of the vertex as seen on the screen.
// Fragment shader code
const fragmentShader = `
  varying vec3 vPosition;
  uniform float uTime;

  void main() {
    vec3 color = vec3(0.5 + 0.5 * cos(uTime + vPosition.x),
                     0.5 + 0.5 * sin(uTime + vPosition.y),
                     sin(uTime + vPosition.z));

    gl_FragColor = vec4(color, 1.0);
  }
`;
The fragment shader uses the vPosition varying from the vertex shader, along with a time uniform to create a dynamic color change effect. Colors are calculated using sine and cosine functions based on time and position, which gives the mesh a unique and moving wave of colors.
// Shader material setup
const shaderMaterial = new THREE.ShaderMaterial({
  vertexShader,
  fragmentShader,
  uniforms: {
    uTime: { value: 0.0 }
  }
});
This creates a new ShaderMaterial object, providing the vertex and fragment shaders compiled earlier in the code. It also initializes a uniform named uTime, which will be used to animate the shader over time.
// Mesh creation with custom shader
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, shaderMaterial);
scene.add(mesh);
This snippet creates a new BoxGeometry that can be rendered on the scene, applies our custom shader material to it, and then adds the resulting mesh to the scene.
// Animation loop with time update
function animate() {
  requestAnimationFrame(animate);

  // Update the uniform 'uTime' with the elapsed time
  shaderMaterial.uniforms.uTime.value = performance.now() / 1000;

  // Render the scene
  renderer.render(scene, camera);
}

animate();
This function is the animation loop. It updates the uTime uniform with the current time to make the shader animation run. Each frame it requests another animation frame, updates the time, and renders the scene with the new shader uniform value.