In Chapter 7, we explored the power of built-in materials like MeshBasicMaterial, MeshLambertMaterial, and MeshPhongMaterial. These provide a solid foundation for most 3D scenes. However, for truly unique visual effects, stunning realism, or creative artistic styles, we need to venture into the realm of custom materials. This is where ShaderMaterial in Three.js shines.
Shaders are small programs that run on your graphics processing unit (GPU). They are responsible for drawing everything you see on your screen, from the color of a pixel to how light interacts with surfaces. Three.js provides ShaderMaterial as a way to write and use your own custom shaders, giving you granular control over the rendering process. This opens up a universe of possibilities for creating dynamic, expressive, and breathtaking visuals.
At its core, a ShaderMaterial requires two essential pieces of code: a vertex shader and a fragment shader.
graph TD; A[Three.js Application] --> B(ShaderMaterial); B --> C(Vertex Shader); B --> D(Fragment Shader); C --> E(GPU); D --> E(GPU); E --> F(Screen Output);
The vertex shader runs once for each vertex of your 3D model. Its primary job is to transform the 3D coordinates of these vertices into 2D screen coordinates, determining where each point of the model will be rendered. It can also pass data, like colors or texture coordinates, to the fragment shader.
The fragment shader (also known as the pixel shader) runs for every pixel that a primitive (like a triangle) covers on the screen. It's responsible for determining the final color of that pixel. This is where you'll implement complex lighting models, procedural textures, and all sorts of visual effects.
Let's look at a basic example of how to create a ShaderMaterial. The ShaderMaterial constructor takes an object with properties like vertexShader and fragmentShader.
const material = new THREE.ShaderMaterial({
vertexShader: `
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red color
}
`
});