-
Notifications
You must be signed in to change notification settings - Fork 0
Shaders
This is an introduction to shaders in OpenGL which Zap uses. If you have experience writing shaders in GLSL you can skip this chapter.
Zap uses OpenGL shaders to render graphics. OpenGL shaders are written in GLSL, a programming language similar to C. I will first explain what a shader does and then cover some basic GLSL concepts.
With Zap we communicate with the graphics device (the GPU) via OpenGL. A GPU is specialized for rendering and for massively parallel calculations (for example computing pixel values). Your normal C++ code runs on the CPU, which is optimized for general-purpose tasks but is not designed to perform the same operation many times in parallel like a GPU. The GPU is designed to do many similar computations simultaneously, which provides much more rendering power for graphics work.
We draw objects using triangles. Converting a list of coordinates into a raster image involves multiple pipeline stages. Each stage can be controlled by small programs called shaders. With these shaders you can modify or compute the final result that appears on the screen. I won't go over every stage here — if you want a detailed, step-by-step tutorial, take a look at this LearnOpenGL article: https://learnopengl.com/Getting-started/Hello-Triangle
We are only looking at two shader types for now: the vertex shader and the fragment shader.
- Vertex shader: decides how the input coordinates (vertices) are interpreted and is often used to apply transformations.
- Fragment shader: decides how to color the pixels (fragments) of your shape.
Vertex and fragment shaders are similar in structure. Here is a minimal vertex shader and its fragment shader.
Vertex shader:
#version 330 core
layout(location = 0) in vec3 Pos;
void main()
{
gl_Position = vec4(Pos, 1.0);
}
Fragment shader:
#version 330 core
out vec4 color;
void main()
{
color = vec4(1.0, 0.0, 0.0, 1.0);
}
What we can see are two logical sections: declarations (the part before void main) and the per-invocation code (the part inside void main). Declarations define the shader's inputs, outputs, and helper functions. The code inside main() is executed once for every shader invocation — in a vertex shader that means once per vertex, and in a fragment shader once per fragment produced by rasterization.
On top, in the declarations, we see:
#version 330 core
This declares the GLSL version and the profile (core). Zap uses the core profile.
There is also this line in the vertex shader:
layout(location = 0) in vec3 Pos;
Remember defining the VertexAttributePointer of the Mesh in the chapter before? The vertex attribute pointer (ATP) tells OpenGL how to read groups of numbers from your vertex buffer and which attribute index to feed into the shader. In this line we are receiving the vertex data into the shader. For example:
layout(location = 0) in vec3 Pos;
and on the CPU side (example from the Mesh wrapper):
mesh.SetAttribPointer(0, 3, 3, 0); // attribute index 0, 3 components, stride=..., offset=...
We define the layout location as 0, so the shader receives data from attribute index 0 (the first parameter in the attribute setup). The in qualifier marks this variable as per-vertex input coming from the vertex attributes. vec3 means we receive a 3-component vector (x, y, z). Pos is the variable name used inside the shader.
out vec4 color;
This declares an output variable of type vec4 (four floats). In a fragment shader such an output is used as the color written to the framebuffer or render target. You can choose any valid name for the variable.
gl_Position = vec4(Pos, 1.0);
In the vertex shader we set the built-in gl_Position variable to the vertex position in clip space. In this simple example we pass Pos through unchanged and add a w component of 1.0.
color = vec4(1.0, 0.0, 0.0, 1.0);
The fragment shader sets the output color to red (R=1.0, G=0.0, B=0.0) with alpha = 1.0 (fully opaque). The four components are in RGBA order.
The color for each fragment is composed from the four components of the vec4 (R, G, B, A — red, green, blue, alpha).
This chapter does not cover all important aspects of GLSL. I advise you to experiment and to read more about shaders (lighting, textures, interpolation, uniforms, samplers, additional pipeline stages, etc.) so you can gain a deeper understanding.