Brain Dump

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);

Links to this note