Strange specular with shader

I’m working my way through the GLSL tutorial at http://www.lighthouse3d.com/opengl/glsl/ and I’m having some problems with the point light shader.

It’s not supposed to look like this right?



The specular highlight is partly in the dark area outside of what the light can reach…



Could the halfVector for the light be wrong?

Work-in-progress-Code follows. (I plan to eventually post code and shaders for all 3 basic lights when the code and comments are good enough…)



PointLightShaderTest.java

package testjunk;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.NodeHandler;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Teapot;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.RenderState;

public class PointLightShaderTest extends SimpleGame {

    Teapot teapot;

    public static void main(String[] args) {
        PointLightShaderTest app = new PointLightShaderTest();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    /**
     * Set up the scene.
     *
     * @see com.jme.app.AbstractGame#initGame()
     */
    protected void simpleInitGame() {
        // Here the real stuff starts! Lets create a teapot and matching lights
        teapot = createTeapotAndLightStateEtc();
        GLSLShaderObjectsState so = display.getRenderer()
                .createGLSLShaderObjectsState();

        // Check is GLSL is supported on current hardware.
        if (!so.isSupported()) {
            System.out
                    .println("Your graphics card does not support GLSL programs, and thus cannot run this test.");
            quit();
        }

        so.load(PointLightShaderTest.class.getClassLoader().getResource(
                "testjunk/point.vert"), PointLightShaderTest.class
                .getClassLoader().getResource("testjunk/point.frag"));
        so.setEnabled(true);

        teapot.setRenderState(so);
        rootNode.attachChild(teapot);

        // Place camera, set title, add input handlers.. boring stuff..
        doBoringStuff();

        rootNode.updateRenderState();
    }

    protected void simpleUpdate() {
        /**
         * If toggle_shader is a valid command (you pressed SPACE), toggle
         * shader.
         */
        if (KeyBindingManager.getKeyBindingManager().isValidCommand(
                "toggle_shader", false)) {
            boolean enabled = teapot.getRenderState(
                    RenderState.RS_GLSL_SHADER_OBJECTS).isEnabled();
            teapot.getRenderState(RenderState.RS_GLSL_SHADER_OBJECTS)
                    .setEnabled(!enabled);
            teapot.updateRenderState();
        }
    }

    private Teapot createTeapotAndLightStateEtc() {
        // first get rid off all previous lights
        lightState.detachAll();
        lightState.setGlobalAmbient(new ColorRGBA(0.1f, 0.1f, 0.1f, 1));
        PointLight pl = new PointLight();
        pl.setAmbient(new ColorRGBA(0, 0, 0, 1));
        pl.setDiffuse(new ColorRGBA(0.8f, 0.8f, 0.8f, 1));
        pl.setSpecular(new ColorRGBA(0.9f, 0.9f, 0.9f, 1));
        pl.setLocation(new Vector3f(0, 0, 2));
        pl.setAttenuate(true);
        pl.setConstant(0f);
        pl.setLinear(0.1f);
        pl.setQuadratic(0.001f);
        pl.setEnabled(true);
        lightState.attach(pl);
        Box box = new Box("lightMarker", new Vector3f(0, 0, 2), 0.01f, 0.01f,
                0.01f);
        rootNode.attachChild(box);

        Teapot teapot = new Teapot("Mmmm, Tea..");
        teapot.setModelBound(new BoundingBox());
        teapot.updateModelBound();
        // center model
        teapot.getLocalTranslation().subtractLocal(
                teapot.getWorldBound().getCenter());

        MaterialState ms = display.getRenderer().createMaterialState();
        ms.setAmbient(new ColorRGBA(0, 0, 0, 1));
        ms.setDiffuse(new ColorRGBA(0.4f, 0.4f, 0.8f, 1));
        ms.setEmissive(new ColorRGBA(0, 0, 0, 1));
        ms.setSpecular(new ColorRGBA(0.6f, 0.6f, 0.95f, 1));
        ms.setShininess(26);
        ms.setEnabled(true);

        teapot.setRenderState(ms);

        return teapot;
    }

    private void doBoringStuff() {
        display.setTitle("Directional light shader");
        display.getRenderer().setBackgroundColor(
                new ColorRGBA(0.02f, 0.0f, 0.776f, 1.0f));
        // Place camera so we get a nice view of the scene
        cam.setLocation(new Vector3f(0, 0, 7));
        cam.update();
        // NodeHandler will enable us to spin the scene with the mouse
        input = new NodeHandler(teapot, 10, 2);
        // Make it so we can toggle the shader on/off with our mighty space key
        KeyBindingManager.getKeyBindingManager().set("toggle_shader",
                KeyInput.KEY_SPACE);
    }
}



point.vert

   varying vec4 diffuse,ambientGlobal,ambient;
   varying vec3 normal,lightDir,halfVector;
   varying float dist;
   
   void main()
   {   
      vec4 ecPos;
      vec3 aux;
      
      normal = normalize(gl_NormalMatrix * gl_Normal);
      
      /* these are the new lines of code to compute the light's direction */
      ecPos = gl_ModelViewMatrix * gl_Vertex;
      aux = vec3(gl_LightSource[0].position-ecPos);
      lightDir = normalize(aux);
      dist = length(aux);
   
      halfVector = normalize(gl_LightSource[0].halfVector.xyz);
      
      /* Compute the diffuse, ambient and globalAmbient terms */
      diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
      
      /* The ambient terms have been separated since one of them */
      /* suffers attenuation */
      ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
      ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
      
         
      gl_Position = ftransform();
   }



point.frag

   varying vec4 diffuse,ambientGlobal, ambient;
   varying vec3 normal,lightDir,halfVector;
   varying float dist;
   
   
   void main()
   {
      vec3 n,halfV,viewV,ldir;
      float NdotL,NdotHV;
      vec4 color = ambientGlobal;
      float att;
      
      /* a fragment shader can't write a varying variable, hence we need
      a new variable to store the normalized interpolated normal */
      n = normalize(normal);
      
      /* compute the dot product between normal and normalized lightdir */
      NdotL = max(dot(n,normalize(lightDir)),0.0);
   
      if (NdotL > 0.0) {
      
         att = 1.0 / (gl_LightSource[0].constantAttenuation +
               gl_LightSource[0].linearAttenuation * dist +
               gl_LightSource[0].quadraticAttenuation * dist * dist);
         color += att * (diffuse * NdotL + ambient);
      
         
         halfV = normalize(halfVector);
         NdotHV = max(dot(n,halfV),0.0);
         color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular *
                     pow(NdotHV,gl_FrontMaterial.shininess);
      }
   
      gl_FragColor = color;
   }

Try increasing the location of your PointLight along the Z axis to 20.

It looks like your light is inside the Teapot…



On another note - why Gouraud lighting? Eugh!

You can fly around with the teapot by w,a,s,d and moving the mouse. I only put it this close to the light to better illustrate how diffuse light and specular light seem to come from different directions. If I move the teapot to the right then diffuse light will come from the left but specular light still seem to come from directly ahead. When I toggle the shader off (space-key) then the specular highlight jumps to the correct location again.



I just want to learn shaders and get away from vertex based lighting. :smiley: What lighting model would you recommend I go for after this one, so that my next screenshot may be more pleasing to your eyes. :stuck_out_tongue:

I neglected to use the WSAD keys to experiment.



Well, I

Ok, I'm somewhat confused now… The shader code I use is the one from http://www.lighthouse3d.com/opengl/glsl/index.php?pointlight and isn't that blinn-phong? :? And gouraud is what you get without shaders with glShadeModel(GL_SMOOTH)?

Anyway, I've worked around the problem by switching to another shader that uses reflect() instead of the halfVector, but I'm still curious as to why it didn't work initially. Is there a bug concerning the halfVector in jme/lwjgl or was the shader wrong somehow?



The new shader is the one from oZone3D.Net Tutorials - Phong Lighting with GLSL - OpenGL Shading Language - Point Light - Spot Light - Attenuation , thanks for the tip adamgp. :slight_smile:

Yes my books tend to use reflect() also.

Sorry, I simply ran the code, rotated the model and assumed it was Gouraud shading judging by the primitive lighting effect. Saying that when I compare what i saw when running your code from memory against your screen shot they look nothing alike. Thats all in the past now. Glad the new shader is working  :wink:

i don't think the built in halfvector attribute is defined for pointlights, only directional(and perhaps spot)…