GLSL basics

So i’ve found it very difficult to obtain full detail onto opengl glsl without it being very specific toward a given subject so what im doing is making this tutorial to help people who may struggle , from past experience with glsl and things iv’e picked up along the way that will make the experience more enjoyable and easy.
So lets start with the absolute basics , the vertex shader.
The most basic form of vertex shader is :


#version 330
in vec2 vertex;
void main(){
	gl_Position = vec4(vertex,1,1);
}


So this is the most basic shaders you can possibly create it takes the input coordinates and it outputs them in 2d directly to gl_Position.
so what does each statement do?
#version 330 is the current version of opengl we will be using , it’s very useful to have this statement on your program , the reason for using this is because we want in and out statements because later in the tutorials we will cover the basics of geometry shaders.

in vec2 vertex; You should be familiar with something like this , vec2 is a 2 component vector with variables .x .y and .xy , x returns the x value , y for the y value and .xy returns a vec2 containing x and y.
If you are familiar with vertex shaders then you may be used to using attribute , this functions in exactly the same way as in however attribute is depreciated.

void main(){
}
This is the core function in your class and is called upon vertex call. If you are familiar with c you will notice this is familiar , there are a lot of other things that glsl shares with C but do not be fooled you cannot write a c program in it.
gl_Position is a vec4 , it is the vertex sent to the geometry shader ( we will look into the later in the tutorial ) and created into your triangle or quad. to write to gl_Position is as simple as gl_Position = my_vec4 or vec4(my_vec2,1,1);

Important things to note about gl_Position , its .z value is its 3rd parameter ie vec4(x,y,z,w); ,if you are using 3d you will need to write to this variable aswell otherwise you may find you dont actually move in the z axis. Do not worry about w for the moment.

For an even simpler way to do this we can instead just call ftransform(); this is more useful in 3d because it automatically translates and rotates all coordinates

Brilliant we have our vertex shader and we (hopefully) understand how it works but what about actually colouring it in , we just have a black shape at the moment which is not useful.

Fragment shaders!

What is a fragment ? Well think of a fragment as an interpolated point within your shape, in 2d space think of it like a pixel you are writing to each pixel. Fragment shaders are extremely useful especially for lighting.

The most basic frag_shader is


void main(){
	gl_FragColor = vec4(1,1,1,1);
}

gl_FragColor is the current color of your fragment , if you dont write to it , generally it will be defaulted to 0,0,0,1.
gl_FragColor is a vec4 with variables (r,g,b,a); a is the alpha.

That’s all well and good but what about the texture we bound for our VBO?
Well it’s easier than you think to take a texture and implement it , textures in shaders are stored in uniform variables named samplers , sampler 1d , 2d,3d are all variants that you can use.
But what does uniform mean?
Think of a uniform as a public final TYPE; You cannot modify it within the program but it can be of any type and it can be written to from outside the shader , this is how we communicate with our shader easily.


uniform sampler2D texture;
in vec2 texture_coordinate;
void main(){
gl_FragColor = texture2D(texture,texture_coordinate);
}


Hold on , a wild variable has appeared!
in vec2 texture_coordinate?
Well because we are using it in a fragment shader this variable will automatically interpolate between values when defined in a vertex shader.
So lets implement this into our vertex shader!


#version 330
in vec2 vertex;
in vec2 texcoord;
out vec2 texture_coordinate;
void main(){
	texture_coordinate = texcoord;
	gl_Position = vec4(vertex,1,1);
}


We simply state texture_coordinate as an output variable for that vertex and thats it.
Thats all well and good but what if we want to do more with this what if we want to actually modify the vertex structure as it leave the vertex shader maybe for efficiency to discard all vertices outside of the viewpoint?
Well this is where the geometry shader comes in , these are not very common but when used correctly they are extremely powerful and allow you to make your program more efficient.
Lets explore what we can do with them and why they are just so darn useful.


#version 330


layout (points) in;
layout (triangle_strip) out;
layout (max_vertices = 4) out;
uniform vec2 dimension;
uniform float size;
out vec2 texcoord;
void main(){
	
   vec2 size_adjust = vec2(size/dimension.x,size/dimension.y);
   float eigth = 0.125;
    gl_Position = vec4(gl_in[0].gl_Position.x,gl_in[0].gl_Position.y,1,1);
    texcoord = vec2(gl_in[0].gl_Position.z,gl_in[0].gl_Position.w + eigth);
    EmitVertex();
    gl_Position = vec4(gl_in[0].gl_Position.x,gl_in[0].gl_Position.y + size_adjust.y,1,1);
    texcoord = vec2(gl_in[0].gl_Position.z,gl_in[0].gl_Position.w);
    EmitVertex();
    gl_Position = vec4(gl_in[0].gl_Position.x + size_adjust.x,gl_in[0].gl_Position.y,1,1);
    texcoord = vec2(gl_in[0].gl_Position.z + (1/8),gl_in[0].gl_Position.w + (1/8));
    EmitVertex();
    gl_Position = vec4(gl_in[0].gl_Position.x + size_adjust.x,gl_in[0].gl_Position.y + size_adjust.y,1,1);
    texcoord = vec2(gl_in[0].gl_Position.z + eigth,gl_in[0].gl_Position.w);
    EmitVertex();

    EndPrimitive();
        
        }
}

The above generates a square and passes the texture onto the fragment shader.
Geometry shaders lay between the vertex shader , just after the vertices are assembled but before a fragment shader is applied. This means that the geometry shader recieves entire shapes that you can directly modify.
Currently we are only going to go through the absolute necessity’s for the variables.
points , this variable is the type of data we receive , generally this should be either gl_points or gl_triangle_strip.
triangle_strip , this is the output variable we are going to send out , so after the geometry shader the shape is created this variable decides how it will be created.

max_verticies , this variable is not a necessity but it is much more efficient when defined. When defined the GPU can automatically assume operations to perform afterward increasing your performance.

So what can we do with this?
EmitVertex();
and
EndPrimitive();

These are the two most important functions because emit_vertex outputs a specific coordinate. End primitive ends the current grouping of vertices as a structure.
But there is more , if we define an output variable as I have done we can modify that and send it to the fragment stage with the vertex with the value being unique for each vertex.
To do this we simple perform


out vec2 texture_coordinate;
gl_Position = Defined_position;
texture_coordinate = Defined_texture;
EmitVertex();

This will output the texture_coordinate variable to the fragment shader , it will still be interpolated.

What other useful features do geometry shaders incorperate?

Well you can send structures to them which will then be generated in groupings dependant on structure.


in Vert_data{
	float a;
	float b;
	float c;
};Vert[1];

It is defined in exactly the same way that we would if we were creating a struct array in C. The advantage to this is that you can pass a large group of variables directly to geometry shader which is useful if you are using it to generate objects in a simulation. If you were doing it creatively you may even be able to implement lighting effects with this or even ray casting.

To define this is our vertex shader we simply do the same. HOWEVER: in the vertex shader you do not create an array you create a single structure object! This is because you are only outputting 1 vertex not multiple. If for example instead of gl Points we were using gl_triangle_strip then in vert_Data would have to have an array created of length 3 because 3 vertices are being sent to the geometry shader.

Things to note about shaders:
The default type is float not int , this means it is more efficient to use floats because the gpu will be able to calculate it much more easily.
Attempting to set the value of the Uniform in a shader will prevent gl20.glGetUniformLocation from finding the variable , so if your function is returning -1 as the location check you are not doing this.
If you put 1/2 , it will be treated as integer to ensure that it is not either perform float a = 1 float b = 2, a/b or 1.0/2.0.
GLSL is NOT C , you cannot read or right to memory , however like C structures must end in a semi colon.
Variable initialisation is perform through constructors for instance float a = 0; but vec2 b = vec2(0,0);
As with C all functions used within main MUST be defined before the main function as the generation and calling of functions is sequential.

That mystical w variable , what does it do?
W is defined through the shader as the normal , if this is equal to 1 then nothing happens if its defined as 2 then all of your vertices will scale away from you because they are all divided by w , this is normalising them , this allows them to be compared to each other much more easily. But don’t worry too much about it.
I hope this tutorial was helpful and if you think there is anything I should add then just ask.
Thanks.

I believe that #330 is not version of OpenGL, but rather the version of GLSL.

Actually, starting with OpenGL version 3.3 the GLSL version numbers match the OpenGL version.

330 is the version of both I believe , if not I can correct it. I chose that version as its the most recent and has features required for this tutorial.


gl_Position = vec4(vertex,1,1);

Why you chose z = 1?
Why not use decimal notation which is needed in gles and good manner anyway.(implicit int to float cast)
What you chosed name vertex instead more common a_position. a_ is prefix that is short from attribute. Tutorials are all about common standards so use them.

Vertex is clear , a_ doesnt really make sense to the person who is doing this for the first time , plus attribute is now depreciated hence it would be confusing to do this.

Good start, lcass, keep at it!
Could you please elaborate more on the various shader stages?
In your article they seem to be popping up too quickly with little “transitioning” between them and further introduction.

Also try to elaborate more on the interface between the host program and the shader.

All in all it’s a good tutorial.

Just as a side note, for you and anyone else writing tutorials or articles, I also highly recommend reading the excellent book On Writing Well.
Even if English is not your native language, I found it applies to all other languages as well and greatly helps in improving writing style.