Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

With infinite light source and only ambient and diffuse lights — no specular light, it seems the rendering results of vertex lighting and fragment lighting should be the same, because all the lighting calculations are linear and it means calculation-then-interpolation and interpolation-then-calculation should be the same. However, the actual results are still not exactly the same, with fragment lighting still smoother. What did I misunderstand? Or is it just because of loss of arithmetic precision during calculation-then-interpolation?

Code of fragment lighting:

// Vertex shader

attribute vec4 Position;
attribute vec3 Normal;

uniform mat4 Projection;
uniform mat4 Modelview;
uniform mat3 NormalMatrix;

varying vec3 EyespaceNormal;

void main(void)
{
    EyespaceNormal = NormalMatrix * Normal;
    gl_Position = Projection * Modelview * Position;
}



// Fragment shader   

varying mediump vec3 EyespaceNormal;

uniform highp vec3 LightPosition;
uniform highp vec3 DiffuseMaterial;
uniform highp vec3 AmbientMaterial;

void main(void)
{
    highp vec3 N = normalize(EyespaceNormal);
    highp vec3 L = normalize(LightPosition);    
    highp float df = max(0.0, dot(N, L));

    lowp vec3 color = AmbientMaterial + df * DiffuseMaterial;

    gl_FragColor = vec4(color, 1);
}

Code of vertex lighting:

// Vertex shader

attribute vec4 Position;
attribute vec3 Normal;

uniform mat4 Projection;
uniform mat4 Modelview;
uniform mat3 NormalMatrix;

uniform highp vec3 LightPosition;
uniform highp vec3 DiffuseMaterial;
uniform highp vec3 AmbientMaterial;

varying lowp vec3 Color;

void main(void)
{
    highp vec3 EyespaceNormal = NormalMatrix * Normal;

    highp vec3 N = normalize(EyespaceNormal);
    highp vec3 L = normalize(LightPosition);
    highp float df = max(0.0, dot(N, L));

    Color = AmbientMaterial + df * DiffuseMaterial;

    gl_Position = Projection * Modelview * Position;
}



// Fragment shader

varying lowp vec3 Color;

void main(void)
{
    gl_FragColor = vec4(Color, 1);
}
share|improve this question
1  
Essentially the same question at math.SE: Interpolation of surface normals on the face of a triangle and Goroud shading –  Nathan Reed Mar 22 '13 at 20:30
    
@NathanReed Very valuable and relevant! Thanks :) –  an0 Mar 22 '13 at 21:55
add comment

1 Answer

up vote 5 down vote accepted

Vector normalization is not linear, and this is sufficient to make the difference.

Imagine normals as points on the unit sphere; then linear interpolation cuts across the interior, rather than moving along the surface, and this means that an interpolated normal will not correspond to any possible surface orientation (it will have a magnitude less than 1). Normalizing the normals after interpolation restores a reasonable result.

Nathan Reed got the link there first but this question at Math.SE is what I had in mind for more formal information on the topic.

share|improve this answer
    
This, basically. Normalization involves a sqrt, and sqrt is a curve. –  Jimmy Shelter Mar 22 '13 at 20:26
    
I see. I missed the normalization part of normals :( Thanks. –  an0 Mar 22 '13 at 21:54
1  
Can you accept this answer please? –  Jimmy Shelter Mar 22 '13 at 23:24
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.