Problem implementing Blinn–Phong shading model

Posted by Joe Hopfgartner on Stack Overflow See other posts from Stack Overflow or by Joe Hopfgartner
Published on 2011-01-07T20:45:40Z Indexed on 2011/01/08 17:54 UTC
Read the original article Hit count: 350

I did this very simple, perfectly working, implementation of Phong Relflection Model (There is no ambience implemented yet, but that doesn't bother me for now). The functions should be self explaining.

/**
 * Implements the classic Phong illumination Model using a reflected light
 * vector.
 */
public class PhongIllumination implements IlluminationModel {

    @RGBParam(r = 0, g = 0, b = 0)
    public Vec3 ambient;

    @RGBParam(r = 1, g = 1, b = 1)
    public Vec3 diffuse;

    @RGBParam(r = 1, g = 1, b = 1)
    public Vec3 specular;

    @FloatParam(value = 20, min = 1, max = 200.0f)
    public float shininess;

    /*
     * Calculate the intensity of light reflected to the viewer .
     * 
     * @param P = The surface position expressed in world coordinates.
     * 
     * @param V = Normalized viewing vector from surface to eye in world
     * coordinates.
     * 
     * @param N = Normalized normal vector at surface point in world
     * coordinates.
     * 
     * @param surfaceColor = surfaceColor Color of the surface at the current
     * position.
     * 
     * @param lights = The active light sources in the scene.
     * 
     * @return Reflected light intensity I.
     */
    public Vec3 shade(Vec3 P, Vec3 V, Vec3 N, Vec3 surfaceColor, Light lights[]) {
        Vec3 surfaceColordiffused = Vec3.mul(surfaceColor, diffuse);
        Vec3 totalintensity = new Vec3(0, 0, 0);
        for (int i = 0; i < lights.length; i++) {
            Vec3 L = lights[i].calcDirection(P);
            N = N.normalize();
            V = V.normalize();
            Vec3 R = Vec3.reflect(L, N); // reflection vector
            float diffuseLight = Vec3.dot(N, L);
            float specularLight = Vec3.dot(V, R);
            if (diffuseLight > 0) {
                totalintensity = Vec3.add(Vec3.mul(Vec3.mul(
                        surfaceColordiffused, lights[i].calcIntensity(P)),
                        diffuseLight), totalintensity);
                if (specularLight > 0) {

                    Vec3 Il = lights[i].calcIntensity(P);

                    Vec3 Ilincident = Vec3.mul(Il, Math.max(0.0f, Vec3
                            .dot(N, L)));

                    Vec3 intensity = Vec3.mul(Vec3.mul(specular, Ilincident),
                            (float) Math.pow(specularLight, shininess));

                    totalintensity = Vec3.add(totalintensity, intensity);
                }

            }
        }
        return totalintensity;
    }
}

Now i need to adapt it to become a Blinn-Phong illumination model

I used the formulas from hearn and baker, followed pseudocodes and tried to implement it multiple times according to wikipedia articles in several languages but it never worked. I just get no specular reflections or they are so weak and/or are at the wrong place and/or have the wrong color.

From the numerous wrong implementations I post some little code that already seems to be wrong. So I calculate my Half Way vector and my new specular light like so:

Vec3 H = Vec3.mul(Vec3.add(L.normalize(), V), Vec3.add(L.normalize(), V).length());
float specularLight = Vec3.dot(H, N);

With theese little changes it should already work (maby not with correct intensity but basically it should be correct). But the result is wrong.

Here are two images. Left how it should render correctly and right how it renders.

right wrong

If i lower the shininess factor you can see a little specular light at the top right: lower shininess

Altough I understand the concept of Phong illumination and also the simplified more performant adaptaion of blinn phong I am trying around for days and just cant get it to work. Any help is appriciated.

Edit:

I was made aware of an error by this answer, that i am mutiplying by |L+V| instead of dividing by it when calculating H. I changed to deviding doing so:

Vec3 H = Vec3.mul(Vec3.add(L.normalize(), V), 1/Vec3.add(L.normalize(), V).length());

Unfortunately this doesnt change much. The results look like this:

div

and if I rise the specular constant and lower the shininess You can see the effects more clearly in a smilar wrong way:

div2

However this division just the normalisation.

I think I am missing one step. Because the formulas like this just dont make sense to me.

If you look at this picture: http://en.wikipedia.org/wiki/File:Blinn-Phong_vectors.svg

The projection of H to N is far less than V to R. And if you imagine changing the vector V in the picture the angle is the same when the viewing vector is "on the left side". and becomes more and more different when going to the right.

I pesonally would multiply the whole projection by two to become something similiar (and the hole point is to avoid the calculation of R). Altough I didnt read anythinga bout that anywehre i am gonna try this out...

Result: The intension of the specular light is far too much (white areas) and the position is still wrong.

I think I am messing something else up because teh reflection are just at the wrong place. But what?

Edit: Now I read on wikipedia in the notes that the angle of N/H is in fact approximalty half or V/R. To compensate that i should multiply my shineness exponent by 4 rather than my projection. If i do that I end up with this:

shinensss multiply by 4

Far to intense but still one thing. The projection is at the wrong place. Where could i mess up my vectors?

© Stack Overflow or respective owner

Related posts about java

Related posts about graphics