(click anywhere to close)
OPEN MENU

[Three.js] Beginners GLSL Tutorial

category: Games | course: Threejs | difficulty:

GLSL stands for (Open)GL Shading Language. It's the programming language used for OpenGL/WebGL shading, and it's incorporated into the Three.js library. Of course, this language is also applicable anywhere else you intend to use WebGL, but I'm focusing on Three.js here. It'skinda like C, but specialized in shaders (so a lot of functions for vectors).Basically, what you can do with GLSL is determine what the shader does when it is run on a certain geometry. And what a shader does, can be nearly anything.

The Shading Pipeline

When we're shading a 3D scene, there are multiple different stages in the pipeline. We're able to completely modify two of these stages, thevertex shader and thefragment shader. This image presents a basic overview of the pipeline:

ShaderPipeline-01

As you see, first the basic code is loaded on the CPU's part. Then, it passes on the geometries for all objects (and camera/light info) to the GPU.

This GPU receives them, and send each vertex through the vertex shader. Here you can modify them in any way you like.

Then the modified vertices are send back, and some operations are performed to determine which object is in front, clipping occurs outside of the camera's view, etc. What's left then, is send to the fragment shader, which basically gets a lot of triangles, and has to determine the colors for each pixel in them. These are sent back and properly displayed on the monitor.

Uniform, varying and attribute

What's also shown in the diagram, are weird types of variables. These are three different types of variables (like local, global, static, etc. - but different) used in GLSL.

  • Uniform, is a variable that is the same for both shaders. For example, the light's position and color. Therefore, it is also read-only.
  • Varying, is  passed in to the vertex shader, then modified, and then the new value is passed to the fragment shader. For example, if you change the positions of vertices, you also have to change their normals (which are given to and used by the fragment shader) so shading works correctly.
  • Attribute, is only available in the vertex shader, and is read-only: they are standard variables for a vertex' position, normal, etc.

A Basic Shader

The most basic shader you can get, simply leaves the vertex positions as they are and gives the whole object one overall color.

Vertex Shader (gl_Position):

//Here's the space for variables

//The built-in main function
void main() {
  //Every vertex shader must eventually set 'gl_Position'
  //And in this case, we multiply the vertex position with the camera view and screen matrix to get the final output.
  gl_Position = projectionMatrix *
                modelViewMatrix *
                vec4(position,1.0);
}

Fragment Shader (gl_FragColor):

//Again, space for variables

void main() {
  //Just as vertex shader, fragment shader must in the end set this variable (gl_FragColor)
  //We set it to a pink color.
  gl_FragColor = vec4(1.0,  // R
                      0.0,  // G
                      1.0,  // B
                      1.0); // A
}

This shader simply displays the object with a color, nothing more, nothing less. The vertex shader leaves the position as it is, and gives it to the fragment shader. This one gives the fragments their colors and we're done!

But, where do we put shader code?

Good question! There are multiple possibilities, but the one that's used the most is by putting the shader code into '<script>' tags, and using jQuery to get the text out of it and use it for the material.

//Vertex Shader Code
<script id="cubeVertexShader" type="x-shader/x-vertex">
    //codehere...
</script>

//Fragment Shader Code
<script id="cubeFragmentShader" type="x-shader/x-fragment">
    //codehere...
</script>

<script>
//Getting the text out of it
var vShader = $('#cubeVertexShader').text();
var fShader = $('#cubeFragmentShader').text();

//Or, for non-jQuery kind of people
var vShader = document.getElementById('cubeVertexShader').innerHTML;
var fShader = document.getElementById('cubeFragmentShader').innerHTML;
</script>

And the final bit: add it to the scene

Last but certainly not least: we must add the shaders together into a material Three.js can use.

//Create a new Shader Material with our code powering it
var itemMaterial = new THREE.ShaderMaterial({
    //Optional, here you can supply uniforms and attributes
    vertexShader: vShader,
    fragmentShader: fShader,
});

//Create a new item with our material
var item = new THREE.Mesh(new THREE.CubeGeometry(100, 10, 10), itemMaterial);

That's...the basics.

You haven't really accomplished much. I'm not saying this to make you feel bad about yourself, I mean: we've just recreated what can also be done with a Three.js standard material. Now, it's time to start adding lots of more fun, and we do exactly that in the next part.

CONTINUE WITH THIS COURSE
Do you like my tutorials?
To keep this site running, donate some motivational food!
Crisps
(€2.00)
Chocolate Milk
(€3.50)
Pizza
(€5.00)