Graphics How to write a normal facing shader

Bingdom

Googledom
GM Version: GMS1.2 and up
Target Platform: Any
Download: N/A
Links: N/A

Note: For the more experienced; if you found any errors or have better explanations in this tutorial, please let me know.


Summary:
In this tutorial, we will be writing a basic shader which will show the direction our normals are facing. This can be useful as it shows visual information about the direction of the normal.

Recommended knowledge:
  • Understanding on what a normal is, and what role it has in a 3d environment.
  • Basic understanding of shaders.
  • 3D

Tutorial:
First things first, lets set up our 3d environment. Create an object.
Create a room with a size of 1280x720. Put our object in it.
In the create event, put this in it to start 3d drawing
Code:
d3d_start();
Now we will set up our camera and draw event. (I won't go into details for this)
Code:
d3d_set_projection_ext(-dcos(current_time/10)*16, dsin(current_time/10)*16, dcos(current_time/10)*8, 0,0,0,0,0,1, 60, 16/9, 1, 800);

shader_set(shdrNormalFace);
d3d_draw_block(-4,-4,-4,4,4,4,-1,1,1);
shader_reset();
Create a new shader, call it shdrNormalFace.
For our shader to work, we need to pass in normals into the fragment part of the shader. The fragment of the shader will be the bit which will draw the colours.

In the vertex part, uncomment in_Normal. in_Normal carries the current normals for the vertex.

Now we need to create a vec3 to be passed to the fragment. For a variable to be able to be passed between the vertex and fragment, it needs to be varying.
Code:
varying vec3 v_vNormal;
After that, we need to define what v_vNormal is.
Inside void main(), put this:
Code:
v_vNormal = in_Normal;
That's it for the vertex part of the shader. Let's move onto the fragment part.

Since we are passing a new variable to the fragment shader, we need to make sure it's also initialized there too. It's written the same as we did it in the vertex shader.
Code:
varying vec3 v_vNormal;
We need to take care that v_vNormal coords usually vary between -1 to 1. So running a simple rgb colour on it would only display the positive normals. This is where reading normals can get tricky. To get around this, we simply give it a neutral zone. So, inside main(), all we need is this:
Code:
gl_FragColor = vec4(0.5+v_vNormal*0.5,1.0);
The x component of the v_vNormal would be the red channel, the y component would be green and the z component would be blue.

Notes
  • The shader can be optimized by removing in_Colour and in_TextureCoord, since they aren't used in this shader.
  • I'm not entirely sure how attribute normals work in a shader, you may need to use normalize() if using custom models or assigning your own normals for a custom model.
  • See what happens if you use different shapes.
  • If a face is completely grey, that means each normal value could be the same.
 
Last edited:
Top