Learn the OpenGL Shading Language to create vertex and fragment shaders for 3D rendering and visual effects.
getbetterat.work
Topic: Graphics
How to Talk to Your GPU.
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.
Why do we need a new language?
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.
The GPU (Graphics Card)
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.
The Graphics Pipeline (The Factory Line)
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.
1. 3D Data (Points & Math)
YOU WRITE THIS
Vertex Shader
Places the corners of the object on your screen.
YOU WRITE THIS
Fragment Shader
Colors in the pixels between the corners.
4. Your Screen (The final picture)
The Vertex Shader (Moving Shapes)
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.
Its main job: It takes a point from a 3D world (like a sword in a game) and
figures out exactly where that point belongs on your flat 2D monitor.
Output: It MUST spit out a final position. In GLSL, this special output is
called gl_Position.
The Fragment Shader (Painting Pixels)
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.
Its main job: To pick the final color of that exact pixel. It handles
lighting, shadows, and textures (images wrapped around objects).
Output: It MUST spit out a color. In older GLSL, this is called gl_FragColor.
How to write it: Types of Data
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.
float
A Number with a Decimal
You must always use a dot. float speed = 1.5;
vec2
Two Numbers Together
Used for flat 2D positions (X and Y). vec2 point = vec2(10.0, 5.0);
vec3
Three Numbers
Used for 3D positions (X, Y, Z) or basic colors (Red, Green, Blue).
vec4
Four Numbers
Used for Colors with Transparency (Red, Green, Blue, Alpha).
How Data Moves (Inputs and Outputs)
uniform
The Global Remote Control
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.
in /
attribute
The Setup Data
Data that changes for every corner point. This is how the Vertex Shader gets the 3D model
data from the game.
out
/ varying
Passing Notes
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.
The GPU Loves Math
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.
Your First Fragment Shader
Let's write a simple Fragment Shader. This code will color every pixel on the screen solid red.
solid_red.frag
voidmain() {
// 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!
Where is GLSL used?
Video Games
Every modern game uses shaders to draw water, fire, shadows, and metal reflections.
Web Design (WebGL)
Cool, interactive 3D websites run GLSL directly inside your internet browser.
Movie Tools
Animation and 3D modeling software use GLSL so artists can preview materials instantly.
Common Beginner Mistakes
Missing the Semicolon
Like the C language, every single command must end with a semicolon ;. If you forget it, the screen will just go
black.
Mixing Numbers
You cannot easily add an integer (whole number) to a float (decimal). 1.0 + 1 will break. Always write 1.0 + 1.0.
Where Am I? (Screen Coordinates)
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.
The Raw Pixel
If your screen is 1920 pixels wide, gl_FragCoord.x
might be exactly 500.5. But working with huge numbers is messy in math.
The Percentage (UV)
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.
Mixing Colors Like Digital Paint
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)
The Magic of "Swizzling"
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);
SWIZZLED
// 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;
Adding Time & Motion
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.
The Pulse Effect
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!
Images as Wrapping Paper (Textures)
A texture is just a flat image file (like a PNG or JPG). In GLSL, you read this image using a tool
called sampler2D.
How wrapping works:
You give the GPU an image (the sampler2D).
You look at your current UV coordinate percentage (e.g., exactly the middle of the screen: vec2(0.5, 0.5)).
You use the built-in function texture(sampler, uv).
The GPU grabs the exact pixel color from the exact middle of your image and returns it to you!
Why GPUs Hate "IF" Statements
In normal coding, you use if / else all the
time. In GLSL, you should avoid it whenever possible. This is called "Branching".
The Problem
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.
The Solution
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.
Faking Random Nature (Noise)
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.
How to Find Bugs (Debugging)
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!
speed > 1.0
speed < 1.0
The Rule Book (Versions)
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.
version300es
* Note: es stands for Embedded Systems. It is heavily
used in WebGL (web browsers) and mobile phones.
Your Next Steps
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.
Return to Topic List →
More in this series
Master more skills with other tutorials from the Graphics Programming series.