2
votes

I'd like to write a GLSL shader program for a per-face shading. My first attempt uses the flat interpolation qualifier with provoking vertices. I use the flat interpolation for both normal and position vertex attributes which gives me the desired old-school effect of solid-painted surfaces.

Although the rendering looks correct, the shader program doesn't actually do the right job:

  1. The light calculation is still performed on a per-fragment basis (in the fragment shader),
  2. The position vector is taken from the provoking vertex, not the triangle's centroid (right?).

Is it possible to apply the illumination equation once, to the triangle's centroid, and then use the calculated color value for the whole primitive? How to do that?

2

2 Answers

3
votes

Use a geometry shader whose input is a triangle and whose output is a triangle. Pass normals and positions to it from the vertex shader, calculate the centroid yourself (by averaging the positions), and do the lighting, passing the output color as an output variable to the fragment shader, which just reads it in and writes it out.

1
votes

Another simple approach is to compute the (screenspace) face normal in the fragment shader using the derivative of the screenspace position. It is very simple to implement and even performs well.

I have written an example of it here (requires a WebGL capable browser):

Vertex:

attribute vec3 vertex;

uniform mat4 _mvProj;
uniform mat4 _mv;

varying vec3 fragVertexEc;

void main(void) {
    gl_Position = _mvProj * vec4(vertex, 1.0);
    fragVertexEc = (_mv * vec4(vertex, 1.0)).xyz;
}

Fragment:

#ifdef GL_ES
precision highp float;
#endif

#extension GL_OES_standard_derivatives : enable

varying vec3 fragVertexEc;

const vec3 lightPosEc = vec3(0,0,10);
const vec3 lightColor = vec3(1.0,1.0,1.0);

void main()
{
    vec3 X = dFdx(fragVertexEc);
    vec3 Y = dFdy(fragVertexEc);
    vec3 normal=normalize(cross(X,Y));

    vec3 lightDirection = normalize(lightPosEc - fragVertexEc);

    float light = max(0.0, dot(lightDirection, normal));

    gl_FragColor = vec4(normal, 1.0);
    gl_FragColor = vec4(lightColor * light, 1.0);
}