
Shaders 101
Planted:
Tended:
Status: sprout
Hits: 3994
Intended Audience: Creative coders, front-end developers with basic knowledge of three.js
Mesh
In three.js, an object like a cube or sphere is called a mesh. This mesh is a plane. A flat, rectangular surface. Every mesh is made of a geometry and a material.
/index.js


Geometry
The geometry defines the initial shape. The plane is made up of segments. A segment consists of two triangles. A triangle is a primitive, a basic building block.
/index.js


Material (shader)
The material defines how the geometry surface appears. Material is a shader.
/index.js


Pipeline
The pipeline that transforms three.js code to pixels looks like this:
- 1. three.js JavaScript code is executed on the CPU
- 2. Shader code is executed on the GPU
- 3. Pixels are rendered on the screen
A shader has two parts, a vertex and fragment shader. They work like this:
- 1. Each vertex runs through the vertex shader to get its viewport space position.
- 2. Primitives are converted into fragments. Fragments are data corresponding to a potential pixel.
- 3. Each fragment runs through the fragment shader to get its color.
Vertex shader
three.js provides some shaders but to open up creativity possibilities, I need to understand how to make my own. The starting point is using THREE.ShaderMaterial
, a material that renders custom shader code. Below is boilerplate code.
/index.js


The vertex shader's main
function is called for every vertex.
- ▪ The purpose of the vertex shader is to set the value for
gl_Position
to the current vertex's clip space coordinates. - ▪
gl_Position
is a reserved global variable - ▪
position
,projectMatrix
andmodelViewMatrix
are being passed in to the shader by three.js - ▪
position
is the current vertex local space coordinates - ▪ multiplying
position
byprojectMatrix
andmodelViewMatrix
converts it to clip space coordinates
GLSL
Shaders are written in a C-like language called GLSL (OpenGL Shading Language). A data type commonly used is vectors. Similar to arrays in JavaScript.
- ▪
vec2 myVec = vec2(1.0, 0.5)
is likeconst myVec = [1.0, 0.5]
,vec3 myVec = vec3(1.0, 0.5, 1.0)
is likeconst myVec = [1.0, 0.5, 1.0], ...
- ▪ A value can be accessed using
myVec.x
ormyVec.y
which is likemyVec[0]
ormyVec[1]
- ▪ A shorthand for creating a vector with the same values is
vec2 myVec = vec2(0.0)
, which is the same asvec2 myVec = vec2(0.0, 0.0)
UVs
UV is a 2D coordinate system used for the surface of the geometry. Similar to the XY coordinate system except all values are normalized (between 0 and 1). In the vertex shader, the UV coords of the current vertex are passed in by three.js.
/index.js


Fragment shader
The fragment shader's main
function is called for every fragment.The purpose of the fragment shader is to set the value for gl_FragColor
to the current fragment's color. It accepts a normalized rgba color value.
/index.js


Note:
- ▪ three.js only sends
uv
coords to the vertex shader. To access the current fragment's UV coords, they have to be sent from the vertex shader. See the sandbox below on how to do this. They are labeledvUv
by convention. - ▪
transparent: true
needs to be set to use an alpha value less than1.0
.
Strength / weaknesses
Besides low-level control, shaders are also performant. JavaScript runs on a CPU thread using sequential processing. Capable of only doing one task at a time, like rendering one pixel at a time. Shader code runs on the GPU using parallel processing. Capable of doing many tasks at once, like rendering all pixels at once. The GPU also has hardware accelerated angle, trigonometric and exponential functions. Functions used to create shader effects.
Their weakness is writing in native WebGL.
Things aren't abstracted and simplified like in three.js.
Making code complex.
Also, the only output is color.
There is no console.log
for debugging.
Sandbox
Feedback
Have any feedback about this note or just want to comment on the state of the economy?
Where to next?
