Skip to content
This repository was archived by the owner on May 19, 2024. It is now read-only.

8. Basic shading

Lubos Lenco edited this page Nov 3, 2018 · 5 revisions

(Reference)

If you have reached this point, you are getting a pretty solid ground for programming more advanced 3D stuff. This is the last essential tutorial. We will implement a shading technique into shader programs. Let's use suzanne model for this, along with new texture. As usual, save them in 'Assets' folder.

suzanne.obj

Store additional constant locations for shaders.

    viewMatrixID = pipeline.getConstantLocation("V");
    modelMatrixID = pipeline.getConstantLocation("M");
    lightID = pipeline.getConstantLocation("lightPos");

Load suzanne model instead.

    var obj = new ObjLoader(Assets.blobs.suzanne_obj);

Set shader uniforms.

    g.setMatrix(modelMatrixID, model);
    g.setMatrix(viewMatrixID, view);

    // Set light position to (4, 4, 4)
    g.setFloat3(lightID, 4, 4, 4);

Now that everything is prepared, we can extend the shaders. Vertex shader follows.

#version 450

// Input vertex data, different for all executions of this shader
in vec3 pos;
in vec2 uv;
in vec3 nor;

// Output data: will be interpolated for each fragment
out vec2 vUV;
out vec3 positionWorldspace;
out vec3 normalCameraspace;
out vec3 eyeDirectionCameraspace;
out vec3 lightDirectionCameraspace;

// Values that stay constant for the whole mesh
uniform mat4 MVP;
uniform mat4 V;
uniform mat4 M;
uniform vec3 lightPos;

void main() {
	// Output position of the vertex, in clip space: MVP * position
	gl_Position = MVP * vec4(pos, 1.0);
	
	// Position of the vertex, in worldspace : M * position
	positionWorldspace = (M * vec4(pos, 1.0)).xyz;
	
	// Vector that goes from the vertex to the camera, in camera space.
	// In camera space, the camera is at the origin (0,0,0).
	vec3 vertexPositionCameraspace = (V * M * vec4(pos, 1.0)).xyz;
	eyeDirectionCameraspace = vec3(0.0, 0.0, 0.0) - vertexPositionCameraspace;

	// Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.
	vec3 lightPositionCameraspace = (V * vec4(lightPos, 1.0)).xyz;
	lightDirectionCameraspace = lightPositionCameraspace + eyeDirectionCameraspace;
	
	// Normal of the the vertex, in camera space
	normalCameraspace = (V * M * vec4(nor, 0.0)).xyz; // Only correct if modelMatrix does not scale the model! Use its inverse transpose if not.
	
	// UV of the vertex. No special space for this one.
	vUV = uv;
}

Fragment shader.

 #version 450

// Interpolated values from the vertex shaders
in vec2 vUV;
in vec3 positionWorldspace;
in vec3 normalCameraspace;
in vec3 eyeDirectionCameraspace;
in vec3 lightDirectionCameraspace;

// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;
uniform vec3 lightPos;

out vec4 fragColor;

void main() {

	// Light emission properties
	// You probably want to put them as uniforms
	vec3 lightColor = vec3(1.0,1.0,1.0);
	float lightPower = 50.0;
	
	// Material properties
	vec3 materialDiffuseColor = texture(myTextureSampler, vUV).rgb;
	vec3 materialAmbientColor = vec3(0.1, 0.1, 0.1) * materialDiffuseColor;
	vec3 materialSpecularColor = vec3(0.3, 0.3, 0.3);

	// Distance to the light
	float distance = length(lightPos - positionWorldspace);

	// Normal of the computed fragment, in camera space
	vec3 n = normalize(normalCameraspace);
	// Direction of the light (from the fragment to the light)
	vec3 l = normalize(lightDirectionCameraspace);
	// Cosine of the angle between the normal and the light direction, 
	// clamped above 0
	//  - light is at the vertical of the triangle -> 1
	//  - light is perpendicular to the triangle -> 0
	//  - light is behind the triangle -> 0
	float cosTheta = clamp(dot(n, l), 0.0, 1.0);
	
	// Eye vector (towards the camera)
	vec3 E = normalize(eyeDirectionCameraspace);
	// Direction in which the triangle reflects the light
	vec3 R = (-l) - 2.0 * dot(n, (-l)) * n;
	//vec3 R = reflect(-l,n); // TODO: waiting for krafix fix

	// Cosine of the angle between the Eye vector and the Reflect vector,
	// clamped to 0
	//  - Looking into the reflection -> 1
	//  - Looking elsewhere -> < 1
	float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
	
	fragColor = vec4(vec3(
		// Ambient: simulates indirect lighting
		materialAmbientColor +
		// Diffuse: "color" of the object
		materialDiffuseColor * lightColor * lightPower * cosTheta / (distance * distance) +
		// Specular: reflective highlight, like a mirror
		materialSpecularColor * lightColor * lightPower * pow(cosAlpha, 5.0) / (distance * distance)
	), 1.0);
}

You can access complete sources here.

The result looks fabulous! Congratulations - you now posses a knownledge to create something really cool. And if you do, make sure to show us at forums or #kha irc channel at irc.ktxsoftware.com.

Clone this wiki locally