Tutorial 05.2: Gouraud Shading

The Gouraud Shader calculates the color at each vertex of the face, but instead of using the average color for the whole face, it interpolates the colo values with in the face.

Furthermore it uses NOT the geometric normal of the surface, but the "smooth" normals that are calculated during model loading:

    • if a vertex is shared between multiple surfaces, the smooth normal is the average normal of all faces that use this vertex.

Complete Source Code:

Use the same code as in the flat shading tutorial.

Shader Source Code:

Geometry Shader: "shader.gs"

#version 450 core

layout (triangles) in;

layout (triangle_strip, max_vertices = 3) out;

#define MAX_MATERIALS 1000

#define MAX_POINTLIGHTS 10

#define MAX_DIRECTIONALLIGHTS 10

#define MAX_SPOTLIGHTS 10

in VS_GS_INTERFACE {

mat4 model;

vec3 position;

vec2 texcoord;

vec3 normal;

flat int materialindex;

flat int objectID;

} vs_out[];

// here the definition has vec4s instead of vec3

// thats because some drivers get the std140 layout for vec3s wrong

struct Material {

vec4 Ka, Kd, Ks;

float Ns;

float d;

};

// has constant intensity

struct DirectionalLight {

vec4 Direction;

vec4 Intensity;

};

// has distance-dependent intensity

struct PointLight {

vec4 Position;

vec4 Intensity;

float AttenuationLinear;

};

// has angle-dependent intensity

struct SpotLight {

vec4 Position;

vec4 Direction;

vec4 Intensity;

int Exponent;

};

layout (std140, binding = 1) uniform MaterialBlock

{

Material Materials[MAX_MATERIALS];

};

layout (std140, binding = 2) uniform DirectionalLightBlock

{

DirectionalLight DirectionalLights[MAX_DIRECTIONALLIGHTS];

};

layout (std140, binding = 3) uniform PointLightBlock

{

PointLight PointLights[MAX_POINTLIGHTS];

};

layout (std140, binding = 4) uniform SpotLightBlock

{

SpotLight SpotLights[MAX_SPOTLIGHTS];

};

uniform vec3 CameraPosition = vec3(0, 0, 0);

uniform vec3 Ambient = vec3(0, 0, 0);

uniform int DirectionalLightCount = 0;

uniform int PointLightCount = 0;

uniform int SpotLightCount = 0;

uniform mat4 View = mat4(1);

uniform mat4 Projection = mat4(1);

out GS_FS_INTERFACE {

vec4 color;

flat int objectID;

} gs_out;

void ProcessDirectionalLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera);

void ProcessPointLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera);

void ProcessSpotLights( inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera);

void main(void)

{

mat4 MVP = Projection * View * vs_out[0].model;

int objectID = vs_out[0].objectID;

vec3 Ka = Materials[vs_out[0].materialindex].Ka.rgb;

vec3 Kd = Materials[vs_out[0].materialindex].Kd.rgb;

vec3 Ks = Materials[vs_out[0].materialindex].Ks.rgb;

float d = Materials[vs_out[0].materialindex].d;

for (int i = 0; i < 3; i++)

{

vec3 TotalDiffuse = vec3(0, 0, 0);

vec3 TotalSpecular = vec3(0, 0, 0);

vec3 VertexPosition = (vs_out[i].model * vec4(vs_out[i].position, 1)).xyz;

vec3 SurfaceToCamera = normalize(CameraPosition - VertexPosition);

vec3 SurfaceNormal = normalize((vs_out[i].model * vec4(vs_out[i].normal, 0)).xyz);

ProcessDirectionalLights( TotalDiffuse, TotalSpecular, VertexPosition, SurfaceNormal, SurfaceToCamera);

ProcessPointLights( TotalDiffuse, TotalSpecular, VertexPosition, SurfaceNormal, SurfaceToCamera);

ProcessSpotLights( TotalDiffuse, TotalSpecular, VertexPosition, SurfaceNormal, SurfaceToCamera);

gl_Position = MVP * vec4(vs_out[i].position, 1);

gs_out.color = vec4(Ambient * Ka + TotalDiffuse * Kd + TotalSpecular * Ks, d);

gs_out.objectID = objectID;

EmitVertex();

}

EndPrimitive();

}

void ProcessDirectionalLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera)

{

float Ns = Materials[vs_out[0].materialindex].Ns;

for (int i = 0; i < DirectionalLightCount; i++)

{

// for specular:

vec3 ReflectedLight = normalize(reflect(DirectionalLights[i].Direction.xyz, SurfaceNormal));

Diffuse += DirectionalLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, -normalize(DirectionalLights[i].Direction.xyz)));

Specular += DirectionalLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns)));

}

}

void ProcessPointLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera)

{

float Ns = Materials[vs_out[0].materialindex].Ns;

for (int i = 0; i < PointLightCount; i++)

{

// for diffuse:

vec3 SurfaceToLight = PointLights[i].Position.xyz - SurfacePosition;

float DistanceLightSurface = length(SurfaceToLight);

SurfaceToLight /= DistanceLightSurface;

float Attenuation = 1.0f + PointLights[i].AttenuationLinear * DistanceLightSurface;

// for specular:

vec3 ReflectedLight = normalize(reflect(-SurfaceToLight, SurfaceNormal));

Diffuse += PointLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, SurfaceToLight)) / Attenuation;

Specular += PointLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns))) / Attenuation;

}

}

void ProcessSpotLights(inout vec3 Diffuse, inout vec3 Specular, vec3 SurfacePosition, vec3 SurfaceNormal, vec3 SurfaceToCamera)

{

float Ns = Materials[vs_out[0].materialindex].Ns;

for (int i = 0; i < SpotLightCount; i++)

{

// for diffuse:

vec3 SurfaceToLight = normalize(SpotLights[i].Position.xyz - SurfacePosition);

float CosAngle = dot(-SurfaceToLight, normalize(SpotLights[i].Direction.xyz));

float Multiplier = pow(max(0, CosAngle), max(1, SpotLights[i].Exponent));

// for specular:

vec3 ReflectedLight = normalize(reflect(-SurfaceToLight, SurfaceNormal));

Diffuse += SpotLights[i].Intensity.rgb * max(0, dot(SurfaceNormal, SurfaceToLight)) * Multiplier;

Specular += SpotLights[i].Intensity.rgb * max(0, pow(dot(ReflectedLight, SurfaceToCamera), max(1, Ns))) * Multiplier;

}

}

Fragment Shader: "shader.fs"

#version 450 core

in GS_FS_INTERFACE {

vec4 color;

flat int objectID;

} gs_out;

layout (location = 0) out vec4 Fragment0;

layout (location = 1) out int Fragment1;

void main(void)

{

Fragment0 = vec4(gs_out.color);

Fragment1 = gs_out.objectID;

}