Quick and Dirty Intro to Colors in GLSL

RGB Colors

GLSL uses RGB to define colors. GLSL deals with colours 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 colour 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 vUvs;

void main() {
    vec4 localPosition = vec4(position, 1.0);
    gl_Position = projectionMatrix * modelViewMatrix * localPosition;
    vUvs = uv;
}
# fragment.glsl
varying vec2 vUvs;

void main() {
    // standard gradient
    gl_FragColor = vec4(vec3(vUvs.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 vUvs;

void main() {
   //  reverse gradient
    gl_FragColor = vec4(vec3(1.0 - vUvs.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, because many operations in graphics are based on normalized values.

Final thoughts

gl_FragColor is always a vec4 - and uvs is a vec2 - so we can output all available values of the uvs during the render process by refering to them completely…

varying vec2 vUvs;

void main() {
    gl_FragColor = vec4(vUvs, 0.0, 1.0);
}