Quick and Dirty Intro to Colors in GLSL
RGB Colors
GLSL uses RGB to define colors and uses vec3
datatypes to represent those colors. This means that the levels of red green and blue can all be represented using a number in the range of [0.0, 1.0]
# Red
Hex: 0xFF0000
RGB: 255, 0, 0
GLSL: vec3(1.0, 0.0, 0.0)
# Green
Hex: 0x00FF00
RGB: 0, 255, 0
GLSL: vec3(0.0, 1.0, 0.0)
# Blue
Hex: 0x0000FF
RGB: 0, 0, 255
GLSL: vec3(0.0, 0.0, 1.0)
Converting colors
hex -> rgb -> GLSL vec3
RGB Value / 255.0 = GLSL Value
e.g. Green -> 0x00FF00 -> FF -> 255 -> 255/255.0 = 1.0 -> vec3(0.0, 1.0, 0.0)
To render a solid color of green in every pixel (using THREE.JS
).
# vertex.glsl
void main() {
vec4 localPosition = vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * localPosition;
}
position
is provided by THREE.JS
and is a 3D point with x,y,z
components.
modelViewMatrix
is provided by THREE.JS
- positions the object relative to the camera. in the transformation pipeline this is when the object moves from local space to camera space.
projectionMatrix
- provided by THREE.JS
projects the object onto screen space. This controls the perspective of the object relative to the camera.
# fragment.glsl
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
Varyings for gradients and beyond
THREE.JS provides a varying
called uvs
which is a vec2
datatype.
uvs
provides UV Coordinates which can be used to map 2d textures onto 3D models. These coordinates range from (0, 0) in the bottom-left corner of the texture to (1, 1) in the top-right corner.
- U represents the horizontal axis (similar to the X-axis of a texture).
- V represents the vertical axis (similar to the Y-axis of a texture).
UV coordinates vary across the mesh they wrap - As the vertex shader interpolates these UVs across the surface of the geometry, each pixel in the fragment shader will receive an appropriate UV coordinate that lies between these values.
In the fragment shaders, you can use the UVs to generate procedural effects like gradients, stripes, or patterns.
# vertex.glsl
varying vec2 uvCords;
void main() {
vec4 localPosition = vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * localPosition;
uvCords = uv;
}
# fragment.glsl
varying vec2 uvCords;
void main() {
// standard gradient
gl_FragColor = vec4(vec3(uvCords.x), 1.0);
}
The x
value in the uvs
is a range from [0.0, 1.0] with black starting from the left. As the vary value is passed to each fragment shader it gradually increases in its value until its white.
Inverting values in GLSL
Reverseing a value to product the opposite effect looks like this:
# fragment.glsl
varying vec2 uvCords;
void main() {
// reverse gradient
gl_FragColor = vec4(vec3(1.0 - uvCords.x), 1.0);
}
Inverting a value by subtracting it from 1.0
is a common and simple method in shader programming, particularly for values that are normalized (ranging between 0.0 and 1.0). This technique is widely used for various tasks such as inverting colors, gradients, and textures.
Final thoughts
gl_FragColor
is always a vec4
- and uvs
is a vec2
- so we can control one or more of the values of gl_FragColor
using either one of the x
and y
co-ordinates of the varying uvs
varying vec2 uvCords;
void main() {
gl_FragColor = vec4(uvCords, 1.0, 1.0);
}
so wtf is this actually doing?…
We’re controlling the red
and green
values of gl_FragColor
by setting those as whatever the varying values of the uvs x
and y
are respectively in the current fragment shader. In this example - blue
is always 1.0
- and we’ve set the alpha channel to 1.0
to render total solid colors. It’s the predicatable and normalized varying values passed to us from THREE.JS
that produce the sort of nicer to look at than the last one; colorful gradient effect.