ShaderMaterial gives full control over GLSL vertex and
fragment shaders while keeping Three.js's scene graph, matrices, and
geometry handling. This tutorial covers the complete workflow: uniforms,
varyings, texture sampling, procedural patterns, and animated effects.
1Minimal ShaderMaterial skeleton
import * as THREE from
'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';
const mat = new THREE.ShaderMaterial({ vertexShader: /* glsl */ ` void
main() { // modelViewMatrix, projectionMatrix, position are injected
by Three.js gl_Position = projectionMatrix * modelViewMatrix *
vec4(position, 1.0); } `, fragmentShader: /* glsl */ ` void main() {
gl_FragColor = vec4(0.13, 0.77, 0.37, 1.0); // solid green } `, });
const mesh = new THREE.Mesh(new THREE.SphereGeometry(1, 64, 32), mat);
scene.add(mesh);
Three.js automatically prepends projectionMatrix,
modelViewMatrix, normalMatrix,
position, normal, uv, and
uv2 declarations to ShaderMaterial shaders.
RawShaderMaterial does not (you declare everything
yourself).
// Include a GLSL hash + value noise helper; no external texture
needed const noiseFrag = /* glsl */ ` precision highp float; varying
vec2 vUv; uniform float u_time; float hash(vec2 p) { p = fract(p *
vec2(234.34, 435.345)); p += dot(p, p + 34.23); return fract(p.x *
p.y); } float noise(vec2 p) { vec2 i = floor(p), f = fract(p); vec2 u
= f * f * (3.0 - 2.0 * f); return mix(mix(hash(i + vec2(0,0)), hash(i
+ vec2(1,0)), u.x), mix(hash(i + vec2(0,1)), hash(i + vec2(1,1)),
u.x), u.y); } float fbm(vec2 p) { float v = 0.0, a = 0.5; for (int i =
0; i < 5; i++) { v += noise(p) * a; p *= 2.1; a *= 0.5; } return v;
} void main() { float n = fbm(vUv * 4.0 + u_time * 0.3); vec3 col =
mix(vec3(0.05, 0.1, 0.2), vec3(0.1, 0.8, 0.5), n); gl_FragColor =
vec4(col, 1.0); } `;
6ShaderMaterial vs RawShaderMaterial
// ShaderMaterial automatically injects: // - Built-in uniforms:
modelMatrix, viewMatrix, projectionMatrix, // modelViewMatrix,
normalMatrix, cameraPosition // - Built-in attributes: position,
normal, uv, tangent, color // - #define directives for lights if
lights:true const mat = new THREE.ShaderMaterial({ lights: true, //
inject THREE_MAX_LIGHTS defines and light uniforms fog: true, //
inject fog uniforms u_fogColor, u_fogNear, u_fogFar depthWrite: true,
transparent: false, // ShaderMaterial can mix Three.js chunks via
#include <common> etc. }); // RawShaderMaterial: NO automatic
injections // You must declare precision, all uniforms, and all
attributes yourself. // Use when you want the smallest possible shader
with no overhead. const rawMat = new THREE.RawShaderMaterial({
vertexShader: ` precision highp float; attribute vec3 position;
uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; void
main() { gl_Position = projectionMatrix * modelViewMatrix *
vec4(position, 1.0); } `, fragmentShader: ` precision highp float;
void main() { gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0); } `, });
When debugging a shader, add
console.log(mat.vertexShader) — Three.js shows the final
shader source including all injected code, which makes it easier to
understand what's being compiled.
Frequently Asked Questions
What will I learn in this tutorial?
Write a fully custom GLSL vertex and fragment shader in Three.js using ShaderMaterial — uniforms, varyings, texture sampling, and noise-driven effects.
What topics are covered in this tutorial?
This tutorial covers: Minimal ShaderMaterial skeleton, Uniforms and updating them, Varyings: pass data vertex → fragment, Texture sampling, Procedural noise pattern, Extending ShaderMaterial vs RawShaderMaterial.
What tools and technologies does this tutorial use?
This tutorial uses Three.js, GLSL, ShaderMaterial, Uniforms.
How long does this tutorial take?
This tutorial takes approximately 50 minutes to complete.
What prerequisites do I need before starting?
This is a Intermediate-level tutorial — no special preparation beyond basic JavaScript is assumed.