struct ShadowVolumeShader
{
/// Vector3fShader
/// Returns the vertex shader for shadow volume
///
/// shader code as string [1]
public string[] VertexShader()
{
string[] vertexShader = new string[1];
vertexShader[0] = "#version 130\n";
vertexShader[0] += "out vec3 normal;";
vertexShader[0] += "void main(void) {";
vertexShader[0] += "normal = gl_NormalMatrix * gl_Normal;";
vertexShader[0] += "gl_Position = gl_ModelViewMatrix * gl_Vertex;";
vertexShader[0] += "}";
return vertexShader;
}
/// GeometryShader
/// Returns the vertex geometry for normal debugging
///
/// shader code as string [1]
public string[] GeometryShader()
{
string[] geometryShader = new string[1];
geometryShader[0] = "#version 130\n";
geometryShader[0] += "#extension GL_EXT_geometry_shader4 : enable\n";
geometryShader[0] += "layout (triangles) in;"; // 3 vertices in
geometryShader[0] += "layout (triangle_strip, max_vertices = 144) out;"; // 4 per quad * 3 triangle vertices + 6 for near/far caps
geometryShader[0] += "uniform vec4 lightPos[8];";
geometryShader[0] += "uniform mat4 glMV;";
geometryShader[0] += "uniform mat4 glPV;";
geometryShader[0] += "in vec3 normal[];";
geometryShader[0] += "float EPSILON = 0.1;";
#region Emit a quad using a triangle strip
geometryShader[0] += "void EmitQuad(vec4 StartVector3f, vec4 EndVector3f, vec4 lightDirStart, vec4 lightDirEnd) {";
// vertex #1: the starting vertex (just a tiny bit below the original edge)
geometryShader[0] += "gl_Position = glPV * vec4(StartVector3f + (lightDirStart * EPSILON));";
geometryShader[0] += "EmitVertex();";
// vertex #2: the starting vertex projected to infinity
geometryShader[0] += "gl_Position = glPV * lightDirStart;";
geometryShader[0] += "EmitVertex();";
// vertex #3: the ending vertex (just a tiny bit below the original edge)
geometryShader[0] += "gl_Position = glPV * vec4(EndVector3f + (lightDirEnd * EPSILON));";
geometryShader[0] += "EmitVertex();";
// vertex #4: the ending vertex projected to infinity
geometryShader[0] += "gl_Position = glPV * lightDirEnd;";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "EndPrimitive();";
geometryShader[0] += "}";
#endregion
geometryShader[0] += "void main() {";
geometryShader[0] += "for (int lightNo=0;lightNo<8;lightNo++) {"; // loop all lights
geometryShader[0] += "if (lightPos[lightNo].w == 1) {"; // do only if light is enabled
geometryShader[0] += "vec4 lightMVP = glMV * lightPos[lightNo];"; // transform light into WCS
geometryShader[0] += "vec4 LightDir0 = normalize(gl_PositionIn[0] - lightMVP);"; // calculate 1st ray
geometryShader[0] += "vec4 LightDir1 = normalize(gl_PositionIn[1] - lightMVP);"; // calculate 2nd ray
geometryShader[0] += "vec4 LightDir2 = normalize(gl_PositionIn[2] - lightMVP);"; // calculate 3rd ray
// Handle only light facing triangles
geometryShader[0] += "if (dot(normal[0], LightDir0.xyz) < 0 && dot(normal[1], LightDir1.xyz) < 0 && dot(normal[2], LightDir1.xyz) < 0) {"; // check if triangle face is visible against light
geometryShader[0] += "EmitQuad(gl_PositionIn[0], gl_PositionIn[1],LightDir0,LightDir1);"; // make quad out of 1st edge
geometryShader[0] += "EmitQuad(gl_PositionIn[1], gl_PositionIn[2],LightDir1,LightDir2);"; // make quad out of 2nd edge
geometryShader[0] += "EmitQuad(gl_PositionIn[2], gl_PositionIn[0],LightDir2,LightDir0);"; // make quad out of 3rd edge
// render the front cap
geometryShader[0] += "gl_Position = glPV * vec4(gl_PositionIn[0] + (LightDir0 * EPSILON));";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "gl_Position = glPV * vec4(gl_PositionIn[1] + (LightDir1 * EPSILON));";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "gl_Position = glPV * vec4(gl_PositionIn[2] + (LightDir2 * EPSILON));";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "EndPrimitive();";
// render the back cap
geometryShader[0] += "gl_Position = glPV * LightDir0;";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "gl_Position = glPV * LightDir2;";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "gl_Position = glPV * LightDir1;";
geometryShader[0] += "EmitVertex();";
geometryShader[0] += "EndPrimitive();";
geometryShader[0] += "} } } }";
return geometryShader;
}
/// FragmentShader
/// Returns the fragment shader for normal debugging
///
/// shader code as string [1]
public string[] FragmentShader()
{
string[] fragmentShader = new string[1];
fragmentShader[0] = "#version 130\n";
fragmentShader[0] = "void main(void) {";
//fragmentShader[0] += "gl_FragColor = vec4( 1.0f, 0.0f, 0.0f, 0.3f );";
fragmentShader[0] += "}";
return fragmentShader;
}
}