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;
}