In this chapter the book covers some more advanced shader topics that will allow you to use your programmable graphics hardware for more than simple polygon rendering.

In this post I will focus in the first part of the chapter. Here an entirely new shader stage is introduced: the geometry shader, which can process entire primitives and even generate new primitives on the fly. I’ll present 4 examples of simple geometry shader usage. They may not be very useful, but this is only introductory material ;).

The vertex and fragment shaders for the three first examples are the same, and they are…

#version 330

precision highp float;

// Attributes per vertex: position and normal
in vec4 vertex;
in vec3 normal;

uniform mat4 MVP;
uniform mat4 modelview;
uniform mat3 normalMatrix;
uniform vec3 lightPos;

out VertexData
{
    vec4 color;
    vec3 normal;
} vertexData;

void main(void)
{
    vec3 N = normalize (normalMatrix * normal);

    // Get vertex position in eye coordinates
    vec4 vertexPos = modelview * vertex;
    vec3 vertexEyePos = vertexPos.xyz / vertexPos.w;

    // Get vector to light source
    vec3 L = normalize(lightPos - vertexEyePos);

    // Dot product gives us diffuse intensity
    vertexData.color = vec4 (.3f, .3f, .9f, 1.f) * max (0.f, dot (N, L));

    // Don't forget to transform the geometry!
    gl_Position = MVP * vertex;
    vertexData.normal = normal;
}
#version 330

precision highp float;

smooth in vec4 color;

out vec4 outColor;

void main (void)
{
    outColor = color;
}

And here are the examples:

Culling

This example implements an “imaginary” viewpoint and culls the triangles that face backwards.

#version 330

precision highp float;

layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

in VertexData
{
    vec4 color;
    vec3 normal;
} vertexData[];

uniform vec3 viewpoint;

smooth out vec4 color;

void main(void)
{
    // Calculate two vectors in the plane of the input triangle
    vec3 ab = gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz;
    vec3 ac = gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz;
    vec3 normal = normalize (cross (ac, ab));

    // Calculate the transformed face normal and the view direction vector
    vec3 vt = normalize (viewpoint - gl_in[0].gl_Position.xyz);

    // Take the dot product of the normal with the view direction
    float d = dot (vt, normal);

    // Emit a primitive only if the sign of the dot product is positive
    if (d > 0.f)
    {
        for (int i = 0; i < gl_in.length (); ++i)
        {
            gl_Position = gl_in[i].gl_Position;
            color = vertexData[i].color;
            EmitVertex ();
        }

        EndPrimitive ();
    }
}

Explode

This example computes the triangles’ normals and displaces the triangles along them.

#version 330

precision highp float;

layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;

in VertexData
{
    vec4 color;
    vec3 normal;
} vertexData[];

uniform float explode_factor;

smooth out vec4 color;

void main(void)
{
    vec3 face_normal = normalize (
            cross (gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz,
                gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz));

    for (int i = 0; i < gl_in.length (); ++i)
    {
        color = vertexData[i].color;
        gl_Position = gl_in[i].gl_Position + vec4 (explode_factor * face_normal,
                0.f);
        EmitVertex ();
    }
    EndPrimitive ();
}

Tessellate

This example creates a new vertex on every triangle that forms a cube and places it at the same distance as the rest of the vertices, then creates a triangle strip with the new vertex. We now have 2 triangles for each one that entered the geometry shader!

#version 330

precision highp float;

layout (triangles) in;
layout (triangle_strip, max_vertices = 6) out;

in VertexData
{
    vec4 color;
    vec3 normal;
} vertexData[];

uniform mat4 MVP;

smooth out vec4 color;

void main(void)
{
    vec3 a = normalize(gl_in[0].gl_Position.xyz);
    vec3 b = normalize(gl_in[1].gl_Position.xyz);
    vec3 c = normalize(gl_in[2].gl_Position.xyz);

    vec3 d = normalize(b + c);

    gl_Position = MVP * vec4 (b, 1.f);
    color = vec4 (1.f, 0.f, 0.f, 1.f);
    EmitVertex ();

    gl_Position = MVP * vec4 (d, 1.f);
    color = vec4 (0.f, 1.f, 0.f, 1.f);
    EmitVertex ();

    gl_Position = MVP * vec4 (a, 1.f);
    color = vec4 (0.f, 0.f, 1.f, 1.f);
    EmitVertex ();

    gl_Position = MVP * vec4 (c, 1.f);
    color = vec4 (1.f, 0.f, 1.f, 1.f);
    EmitVertex ();
}

Normals

The last example uses a slightly different vertex shader to create a new effect. It renders the polygon’s normals. The geometry shader transforms the triangles into lines representing vertices and faces normals. To do this, the vertex shader doesn’t have to transform the geometry, that’s why the following vertex shader is just a pass-through for the geometry.

#version 330

precision highp float;

// Attributes per vertex: position and normal
in vec4 position;
in vec3 normal;

out VertexData
{
    vec3 normal;
} vertexData;

void main(void)
{
    // Pass through the data, the Geometry Vertex will transform it
    gl_Position = position;
    vertexData.normal = normal;
}
#version 330

precision highp float;

layout (triangles) in;
layout (line_strip, max_vertices = 8 ) out;

in VertexData
{
    vec3 normal;
} vertexData[];

uniform mat4 MVP;

smooth out vec4 color;

void main(void)
{
    // Normals for the triangle vertices
    for (int i = 0; i < gl_in.length (); ++i)
    {
        color = vec4 (1.f, .3f, .3f, 1.f);
        gl_Position = MVP * gl_in[i].gl_Position;
        EmitVertex ();

        color = vec4 (0.f);
        gl_Position = MVP * (gl_in[i].gl_Position + vec4 (vertexData[i].normal
                    * .05f, 0.f));
        EmitVertex ();

        EndPrimitive ();
    }

    // Triangle face normal (from triangle's centroid)
    vec4 cent = (gl_in[0].gl_Position + gl_in[1].gl_Position +
            gl_in[2].gl_Position) / 3.f;
    vec3 face_normal = normalize (
            cross (gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz,
                gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz));

    gl_Position = MVP * cent;
    color = vec4 (.3f, 1.f, .3f, 1.f);
    EmitVertex ();

    gl_Position = MVP * (cent + vec4 (face_normal * .1f, 0.f));
    color = vec4 (0.f);
    EmitVertex ();

    EndPrimitive ();
}
#version 330

precision highp float;

smooth in vec4 color;

out vec4 outColor;

void main (void)
{
    outColor = color;
}

And finally, here are some cool screenshots!

About these ads