JOGL: Lighting the Inside of a Cube

I originally posted this on the JOGL board here, but I haven’t had any replies there, so I’m trying my luck here.

I’m using JOGL, and I am trying to create a simple scene: an area surrounded by a cube, a camera that moves (arrow keys to rotate, p key to go forward), and a light that follows the camera.

I’ve got the cube in place, but I’m having quite a bit of trouble getting the lights to function how I expect them to. I would expect the sides of the cube to get brighter in areas that are closer to the light. Instead, I get strange artifacts on each side of the cube. Here is the camera looking into a corner:

http://forum.jogamp.org/file/n4033471/ss.png

I would expect the corner to be lit up, with the sides getting darker the further away from the light they got.

I thought this might be a result of only using a single polygon for each side of the cube, so I split one of the sides into a bunch of polygons. That side looks like this:

http://forum.jogamp.org/file/n4033471/ss1.png

This is closer to what I’m looking for, but it’s still not quite right. The light seems to be focused in one location instead of spreading out evenly.

I’ve posted the code below. Is there anything obvious I’m doing wrong? Can anybody show me a little example that does what I’m describing?


import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.swing.JFrame;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.FPSAnimator;

public class CameraTest implements GLEventListener {


	private final GLCanvas canvas = new GLCanvas();

	private final int worldX = -400;
	private final int worldY = -400;
	private final int worldWidth = 800;
	private final int worldHeight = 800;
	private final int worldZ = 100;
	private final int worldDepth = 800;

	private GL gl;

	private boolean upPressed = false;
	private boolean downPressed = false;
	private boolean leftPressed = false;
	private boolean rightPressed = false;
	private boolean pPressed = false;

	double speed = 5;

	private double pitchAngle = 0;
	private double rollAngle = 0;

	Vector3D cameraPos = new Vector3D(0, 0, 400);
	Vector3D cameraDirection = new Vector3D(0, 0, -1);

	public CameraTest(){



		canvas.addGLEventListener(this);
		gl = canvas.getGL();



		Animator animator = new FPSAnimator(canvas, 60);
		animator.start();

		canvas.addKeyListener(new KeyAdapter(){

			@Override
			public void keyReleased(KeyEvent e) {

				if(e.getKeyCode() == KeyEvent.VK_UP){
					upPressed = false;
				}
				else if(e.getKeyCode() == KeyEvent.VK_DOWN){
					downPressed = false;
				}
				else if(e.getKeyCode() == KeyEvent.VK_LEFT){
					leftPressed = false;
				}
				else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
					rightPressed = false;
				}
				else if(e.getKeyCode() == KeyEvent.VK_P){
					pPressed = false;
				}
			}

			@Override
			public void keyPressed(KeyEvent e) {


				if(e.getKeyCode() == KeyEvent.VK_UP){
					upPressed = true;
				}
				else if(e.getKeyCode() == KeyEvent.VK_DOWN){
					downPressed = true;
				}
				else if(e.getKeyCode() == KeyEvent.VK_LEFT){
					leftPressed = true;
				}
				else if(e.getKeyCode() == KeyEvent.VK_RIGHT){
					rightPressed = true;
				}
				else if(e.getKeyCode() == KeyEvent.VK_P){
					pPressed = true;
				}

				System.out.println();
				System.out.println("Pos: " + cameraPos.toString());
				System.out.println("Dir: " + cameraDirection.toString());
			}
		});

		JFrame frame = new JFrame("Camera Test");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(canvas, BorderLayout.CENTER);

		frame.setSize(500, 500);
		frame.setLocation(1500, 500);
		frame.setVisible(true);

		canvas.requestFocusInWindow();
	}


	public void init(GLAutoDrawable drawable) {

	}

	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glEnable(GL.GL_BLEND);
		gl.glEnable(GL.GL_NORMALIZE);

		gl.glEnable(GL.GL_LIGHTING);
		gl.glEnable(GL.GL_LIGHT0);

		gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, new float[]{1f, 1f, 1f, 1},0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_EMISSION, new float[]{.1f, .1f, .1f, 1},0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, new float[]{0f, 0f, 0f, 1},0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, new float[]{1f, 1f, 1f, 1},0);

		gl.glEnable(GL.GL_LIGHT1);
		gl.glLightf(GL.GL_LIGHT1, GL.GL_SPOT_CUTOFF, 45f);
		gl.glLightf(GL.GL_LIGHT1, GL.GL_SPOT_EXPONENT, 2f);
	    gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT,  new float[]{1f, 1f, 1f, 1},0);
	    gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE,  new float[]{1f, 1f, 1f, 1},0);
	    gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR,  new float[]{1f, 1f, 1f, 1},0);
	    gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, new float[]{1f, 1f, 1f, 1},0);

		gl.glBlendFunc(GL.GL_SRC_ALPHA,  GL.GL_ONE_MINUS_SRC_ALPHA);

		gl.glEnable(GL.GL_CULL_FACE);
		gl.glCullFace(GL.GL_BACK);

		gl.glLoadIdentity();
		gl.glFrustum(-1, 1, -1, 1, 1, worldZ + worldDepth + 10);
		gl.glViewport(0, 0, width, height);

		gl.glClearColor(.25f, .25f, .25f, 1f);
	}


	public void display(GLAutoDrawable drawable){
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		step();
		displayFromCamera(drawable);
	}

	private void step(){

		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, new float[]{(float)cameraPos.x, (float)cameraPos.y, (float)cameraPos.z}, 0);

		
		float dirX = (float) (Math.sin(Math.toRadians(rollAngle)) * Math.cos(Math.toRadians(pitchAngle )));
		float dirY = (float) (-Math.sin(Math.toRadians(pitchAngle) ));
		float dirZ = (float) (Math.cos(Math.toRadians(rollAngle) ) * Math.cos(Math.toRadians(pitchAngle)));
		
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPOT_DIRECTION, new float[]{dirX, dirY, dirZ},0);
	    gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, new float[]{(float)cameraPos.x, (float)cameraPos.y, (float)cameraPos.z},0);


		if(upPressed){
			pitchAngle+=1;
		}
		if(downPressed){
			pitchAngle-=1;
		}

		while(pitchAngle < 0){
			pitchAngle += 360;
		}
		while(pitchAngle > 360){
			pitchAngle -= 360;
		}

		if(leftPressed){
			if(pitchAngle < 270 && pitchAngle > 90){
				rollAngle+=1;
			}
			else{
				rollAngle-=1;
			}

		}
		if(rightPressed){
			if(pitchAngle < 270 && pitchAngle > 90){
				rollAngle-=1;
			}
			else{
				rollAngle+=1;
			}
		}

		if(pPressed){
			double newPosX = speed *  Math.sin(Math.toRadians(rollAngle)) * Math.cos(Math.toRadians(pitchAngle ));
			double newPosY = speed * -Math.sin(Math.toRadians(pitchAngle) );
			double newPosZ = speed *  Math.cos(Math.toRadians(rollAngle) ) * Math.cos(Math.toRadians(pitchAngle));

			cameraPos = cameraPos.add(new Vector3D(newPosX, newPosY, newPosZ));
		}
	}

	public void displayFromCamera(GLAutoDrawable drawable){
		gl.glPushMatrix();


		gl.glViewport(0, 0, canvas.getWidth(), canvas.getHeight());

		gl.glLoadIdentity();

		gl.glFrustum(-1, 1, -1, 1, 1, 100000);

		gl.glRotated(pitchAngle, 1, 0, 0);
		gl.glRotated(rollAngle, 0, 1, 0);

		gl.glTranslated(-cameraPos.x, -cameraPos.y, cameraPos.z);

		displayWholeScene(drawable, false);

		gl.glPopMatrix();
	}


	public void material(float r, float g, float b){

		float[] m = {r, g, b, 1};

			gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, m,0);
		//	gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 100.0f);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, m, 0);
		//gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, m, 0);
		//gl.glMaterialfv(GL.GL_FRONT, GL.GL_EMISSION, m, 0);
	}

	public void displayWholeScene(GLAutoDrawable drawable, boolean drawSnake){


		gl.glLineWidth(5f);

		gl.glPolygonMode(GL.GL_BACK, GL.GL_LINES);
		gl.glPolygonMode(GL.GL_FRONT, GL.GL_FILL);

		//front

		gl.glColor3f(1, 0, 0);
		material(.1f, 0, 0);
		gl.glBegin(GL.GL_POLYGON);
		gl.glNormal3d(0, 0, -1);
		gl.glVertex3d(worldX, worldY+worldHeight, -worldZ);
		gl.glVertex3d(worldX+worldWidth, worldY+worldHeight, -worldZ);
		gl.glVertex3d(worldX+worldWidth, worldY, -worldZ);
		gl.glVertex3d(worldX, worldY, -worldZ);
		gl.glEnd();

		//back
		//		gl.glBegin(GL.GL_POLYGON);
		//		gl.glColor3f(0, 1, 0);
		//		material(0, 0, 1);
		//		gl.glNormal3d(0, 0, 1);
		//		gl.glVertex3d(worldX, worldY, -(worldZ+worldDepth));
		//		gl.glVertex3d(worldX+worldWidth, worldY, -(worldZ+worldDepth));
		//		gl.glVertex3d(worldX+worldWidth, worldY+worldHeight, -(worldZ+worldDepth));
		//		gl.glVertex3d(worldX, worldY+worldHeight, -(worldZ+worldDepth));
		//		gl.glEnd();

		double w = (worldWidth)/25;
		double h = (worldHeight)/25;

		for(double x = worldX; x < worldX+worldWidth; x+=w){
			for(double y = worldY; y < worldY+worldHeight; y+=h){

				gl.glBegin(GL.GL_POLYGON);

				gl.glColor3f(0, 1, 0);
				material(0, .5f, 0);
				gl.glNormal3d(0, 0, 1);

				gl.glVertex3d(x, y, -(worldZ+worldDepth));
				gl.glVertex3d(x+w, y, -(worldZ+worldDepth));
				gl.glVertex3d(x+w, y+h, -(worldZ+worldDepth));
				gl.glVertex3d(x, y+h, -(worldZ+worldDepth));
				gl.glEnd();
			}
		}



		//left side

		gl.glBegin(GL.GL_POLYGON);
		gl.glColor3f(0, 0, 1);
		material(0, 0, 1);
		gl.glNormal3d(1, 0, 0);
		gl.glVertex3d(worldX, worldY+worldHeight, -(worldZ+worldDepth));
		gl.glVertex3d(worldX, worldY+worldHeight, -(worldZ));
		gl.glVertex3d(worldX, worldY, -(worldZ));
		gl.glVertex3d(worldX, worldY, -(worldZ+worldDepth));
		gl.glEnd();


		//right side
		gl.glBegin(GL.GL_POLYGON);
		gl.glColor3f(1, 1, 0);
		material(1, 1, 0);
		gl.glNormal3d(0, -1, 0);
		gl.glVertex3d(worldX+worldWidth, worldY, -(worldZ+worldDepth));
		gl.glVertex3d(worldX+worldWidth, worldY, -(worldZ));
		gl.glVertex3d(worldX+worldWidth, worldY+worldHeight, -(worldZ));
		gl.glVertex3d(worldX+worldWidth, worldY+worldHeight, -(worldZ+worldDepth));
		gl.glEnd();

		//bottom
		gl.glBegin(GL.GL_POLYGON);
		gl.glColor3f(0, 1, 1);
		material(0, 1, 1);
		gl.glNormal3d(0, 1, 0);
		gl.glVertex3d(worldX+worldHeight, worldY, -worldZ);
		gl.glVertex3d(worldX+worldWidth, worldY, -(worldZ+worldDepth));
		gl.glVertex3d(worldX, worldY, -(worldZ+worldDepth));
		gl.glVertex3d(worldX, worldY, -worldZ);
		gl.glEnd();

		//top
		gl.glBegin(GL.GL_POLYGON);
		gl.glNormal3d(0, -1, 0);
		gl.glColor3f(1, 0, 1);
		material(1, 0, 1);
		gl.glVertex3d(worldX, worldY+worldHeight, -worldZ);
		gl.glVertex3d(worldX, worldY+worldHeight, -(worldZ+worldDepth));
		gl.glVertex3d(worldX+worldWidth, worldY+worldHeight, -(worldZ+worldDepth));
		gl.glVertex3d(worldX+worldHeight, worldY+worldHeight, -worldZ);
		gl.glEnd();
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
	}


	private static class Vector3D{
		private final double x;
		private final double y;
		private final double z;

		public Vector3D(double x, double y, double z){
			this.x = x;
			this.y = y;
			this.z = z;
		}

		public Vector3D add(Vector3D delta) {
			return new Vector3D(x+delta.x, y+delta.y, z+delta.z);
		}

		public String toString(){
			return x + ", " + y + ", " + z;
		}

	}

	public static void main(String[] args) {
		new CameraTest();
	}
}