Shader
- Tags
- opengl
A shader is a GLSL (C
-like) script used to configure part of the graphics pipeline.
Each shader is compiled and linked in the GPU and then for each set of vertices passed into the graphics pipeline, a new shader process is started concurrently and used to determine how to render those vertices.
Structure
Here's an example vertex shader:
// specify the OpenGL version (3.3) and run in core-profile
#version version_number core
// specify any [[id:16ea0666-cb68-41b8-85cb-bd5bc7904484][vertex-attributes]] we're using.
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
// main entry point for the Shader program.
void main() {
// process input(s) and do some weird graphics stuff
...
// output processed stuff to output variable
out_variable_name = weird_stuff_we_processed;
}
Compiling
Shaders have to be compiled before they can be used. We store references to shaders as ids and have to specify the kind of shader we're creating.
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
Next we need to connect the shader id to the shader source code.
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
And compile it.
glCompileShader(vertexShader);
Note: you can use the glGetShaderiv and glGetShaderInfoLog
methods to check
whether shader compilation failed.
Using a Shader
Shaders cannot be used by themselves, they must be attached to a shader program and then enabled in bulk. After a shader has been attached to a shaderProgram you can delete the original shaders from the GPU.
glDeleteShader(vertexShader);
Types
GLSL has the following builtin types:
- int
- float
- double
- uint
- bool
- sampler to reference bitmap textures. Comes in 1D, 2D and 3D.
- vec (with suffixes for any of the above types indicating types, eg. bvec3 is a vec3 of booleans).
- matrices
Note: vectors have a maximum size of 4.
You can access the attributes of a vector using vec.{x,y,z,w}
, vec.{r,g,b,a}
or vec.{s,t,p,q}
.
You can also use swizzling to get away with weird assignments:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
In and Out
You can mark vertex-attributes with the specifiers in and out
.
A variable marked with in
can be passed in from a previous vertex shader where
that same variable was marked out
. The exception to this rule is the vertex
shader which specifies input attributes directly from the vertex data.
For example, you can set the colors in the fragment shader from the vertex shader.
// vertex shader
#version 330 core
layout (location = 0) in vec3 aPos; // the position variable has attribute position 0
out vec4 vertexColor; // specify a color output to the fragment shader
void main() {
gl_Position = vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructor
vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // set the output variable to a dark-red color
}
// fragment shader
#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // the input variable from the vertex shader (same name and same type)
void main() {
FragColor = vertexColor;
}
Uniform
Uniform is another specifier you can attach to a variable, and that variable will be globally available throughout the same shader program. You can assign the value of a uniform by first fetching the uniforms memory location.
For example, we can continually change the value of one field in the color value until as time elapses using a global. This is a more flexible approach than with a regular vertex-attribute.
#include <math.h>
// calculate green value
float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
// get location in shader program
int vertexColorLocation = glGetUniformLocation(shaderProgram, "vertexColor");
glUseProgram(shaderProgram);
// assign uniform value
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
Location
Structs
Shaders also support grouping variables into structs.
#version 330 core
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
You can get the location of these structs in memory and assign them through dot notation.
glUniform3f(glGetUniformLocation(objectShader.ID, "material.ambient"), 1.0f, 0.5f, 0.31f);
Arrays
Shaders support arrays of types just like C. You have to know the length of an array before declaring it.
#define FOO_COUNT 4
float foos[FOO_COUNT];
Lets assign them.
glUniform3f(glGetUniformLocation(objectShader.ID, "foos[0]"), 1.0f, 0.5f, 0.31f);