The CPU (Main Brain)
Your CPU is very smart. It handles game logic, physics, and input. But it does tasks one by one. If you ask it to paint 2 million pixels, it paints them one at a time. This is too slow for video games.
GLSL stands for OpenGL Shading Language. It is the code you write to tell your graphics card how to draw shapes and colors on your screen.
Most code runs on your computer's main brain (the CPU). But drawing millions of pixels very fast is hard. So, we send that job to the graphics card (the GPU). GLSL is the language the GPU understands.
Your CPU is very smart. It handles game logic, physics, and input. But it does tasks one by one. If you ask it to paint 2 million pixels, it paints them one at a time. This is too slow for video games.
Your GPU is not as smart, but it has thousands of tiny workers. It can do the exact same simple math on millions of pixels all at once. GLSL gives the instructions to these thousands of workers.
Drawing a 3D object on a flat screen takes steps. The GPU moves data through a factory line called the pipeline. You write GLSL code to control two major stops on this line.
Places the corners of the object on your screen.
Colors in the pixels between the corners.
A "vertex" is just a fancy word for a corner point. A triangle has 3 vertices. A square has 4. A 3D game character might have 50,000 vertices. The Vertex Shader runs one time for every single corner point.
gl_Position.
After the Vertex Shader places the corners, the GPU figures out which screen pixels fall inside those corners. A "fragment" is basically a pixel. The Fragment Shader runs one time for every single pixel inside the shape.
gl_FragColor.
GLSL looks a lot like the "C" programming language. It is very strict about math. You cannot mix whole numbers and decimals easily. Here are the most common blocks of data you will use.
A Number with a Decimal
You must always use a dot. float speed = 1.5;
Two Numbers Together
Used for flat 2D positions (X and Y). vec2 point = vec2(10.0, 5.0);
Three Numbers
Used for 3D positions (X, Y, Z) or basic colors (Red, Green, Blue).
Four Numbers
Used for Colors with Transparency (Red, Green, Blue, Alpha).
A uniform is a piece of data sent from your main game code (CPU) to the GPU. It stays the exact same for every single pixel. Examples: Game Time, Mouse Position, Screen Size.
Data that changes for every corner point. This is how the Vertex Shader gets the 3D model data from the game.
This is how the Vertex Shader passes a message down the factory line to the Fragment Shader. Example: Passing the color from a corner down to the pixels.
You do not need to write complex math from scratch. GLSL comes with built-in math tools that run incredibly fast. Here are some of the most popular tools:
sin() & cos()
Great for making things wave back and forth over time. Used for water effects or breathing animations.
mix(a, b, % )
Blends two values together based on a percentage. Great for mixing two colors.
step(edge, x)
Returns 0.0 or 1.0. It makes sharp, hard edges. Good for blocky, retro, or cartoon shadows.
length()
Finds the distance between two points. Very useful for making circular shapes or glowing lights.
Let's write a simple Fragment Shader. This code will color every pixel on the screen solid red.
void main() { // We create a vec4 for the color. (Red, Green, Blue, Alpha) vec4 myColor = vec4(1.0, 0.0, 0.0, 1.0); // We tell the GPU to use this color for the pixel gl_FragColor = myColor; }
Notice: We use 1.0 instead of 1. In GLSL, colors go from 0.0 (none) to 1.0 (full). Because they are floats, you must write the
decimal!
Every modern game uses shaders to draw water, fire, shadows, and metal reflections.
Cool, interactive 3D websites run GLSL directly inside your internet browser.
Animation and 3D modeling software use GLSL so artists can preview materials instantly.
Like the C language, every single command must end with a semicolon ;. If you forget it, the screen will just go
black.
You cannot easily add an integer (whole number) to a float (decimal). 1.0 + 1 will break. Always write 1.0 + 1.0.
When the Fragment Shader runs, it needs to know which pixel it is currently painting.
It gets this from a built-in variable called gl_FragCoord.
If your screen is 1920 pixels wide, gl_FragCoord.x
might be exactly 500.5. But working with huge numbers is messy in math.
We prefer numbers from 0.0 to 1.0. We divide the current pixel by the total screen width. Now, the left side is 0.0, the middle is 0.5, and the right is 1.0. This is called a UV Coordinate.
GLSL uses light to mix colors (Red, Green, Blue). Remember, 0.0 is dark, and 1.0 is fully bright. Mixing them gives you every
color on your screen.
vec3(1.0, 0.0, 0.0)
Pure Red
vec3(0.0, 1.0, 0.0)
Pure Green
vec3(1.0, 1.0, 0.0)
Yellow (Red + Green)
GLSL has a superpower called swizzling. It lets you shuffle, flip, and copy the parts of a vector (like a color or position) just by typing the letters in a different order.
// Setup a normal color
vec4 color = vec4(1.0, 0.5, 0.2, 1.0);
// Extract only the Red, Green, Blue
vec3 rgb_only = color.rgb;
// Flip the color backwards!
vec3 flipped = color.bgr;
// Copy Red three times (makes greyscale)
vec3 all_red = color.rrr;
Shaders are usually static pictures. To make them move, your main CPU code must send a
uniform variable that tracks the clock. We usually call this u_time.
If you pass u_time
into the built-in math tool sin(u_time), the result will smoothly
float up and down between -1.0 and 1.0 forever. This is how you make glowing lights or breathing
animations!
A texture is just a flat image file (like a PNG or JPG). In GLSL, you read this image using a tool
called sampler2D.
sampler2D).vec2(0.5, 0.5)).texture(sampler, uv).
In normal coding, you use if / else all the
time. In GLSL, you should avoid it whenever possible. This is called "Branching".
The thousands of tiny workers in your GPU are tied together in groups. If one worker takes the "if" path and another takes the "else" path, they both have to wait for the other to finish. It slows everything down.
Instead of true/false logic, GLSL developers use math to blend things. They use tools like step() and mix() to smoothly transition between two
choices without making the workers split up.
GPUs are strict math machines. They do not have an easy Math.random() command. If you want to draw a
natural-looking cloud, dirt, or fire, you cannot rely on true randomness.
Instead, developers use complex mathematical equations called "Noise Functions". These math equations spit out values that look random and chaotic, but are actually completely predictable. If you put the exact same position into the math, you will always get the exact same "random" dot out.
You cannot use console.log() or print text to the
screen from a shader. If your math breaks, your screen just turns completely black.
Visual Debugging:
The only way to test a variable is to turn it into a color. If you want to know if your speed variable is working, you tell the
shader:
"If speed is greater than 1.0, paint the screen pure red. Otherwise, paint it
green."
Then you just look at your monitor to see what color it is!
Before writing any code, the very first line of a GLSL file must always declare which rule book (version) it is using. If you forget this, the GPU might not understand your newer commands.
* Note: es stands for Embedded Systems. It is heavily
used in WebGL (web browsers) and mobile phones.
You now know the vocabulary. The next step is simply practicing how to combine these tools.
Start by drawing a circle using the length() tool. Then, make
that circle move left and right using sin(u_time). Finally, try
mixing its color from Red to Blue based on its screen coordinates.