PDA

View Full Version : Normals Producing Funny Lighting



JMoore
11-01-2010, 08:05 AM
I'm rendering a very simple house to teach myself openGL and I'm up to learning normal calculation. I'm fairly certain that I have the correct function to calculate the normal vectors, but my lighting looks a bit funny. Below is a screenshot.
http://img261.imageshack.us/img261/2408/screenshot20101028at112.png

Here's the portion of code that renders the model. I'm using coordinates from an array, so I assemble ever three floating-point numbers as a vertex, and then use these for calculating the normals. Originally I was using a Point and Triangle class, but it was being weird, so I just switched to using 2D arrays.

I'm also seeing random polygons all over the place, like the model explodes out. I cannot for the life of me figure out why :( Any help would be very much appreciated!


package com.android.aliaga;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

public class AliagaRenderer implements GLSurfaceView.Renderer {
private FloatBuffer _vertexBuffer, _normalBuffer;

private int _nrOfVertices = 0;
float[][] point;
float[][] out;
float[] normalCoords;
public int detail = GL10.GL_TRIANGLES;

private float _xAngle;
private float _yAngle;

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_CULL_FACE);
gl.glCullFace(GL10.GL_BACK);
gl.glFrontFace(GL10.GL_CCW);

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

initShape();
}

public void onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0, 0, w, h);
}

public void setXAngle(float angle) {
_xAngle = angle;
}

public float getXAngle() {
return _xAngle;
}

public void setYAngle(float angle) {
_yAngle = angle;
}

public float getYAngle() {
return _yAngle;
}

public void onDrawFrame(GL10 gl) {
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();

if(_xAngle > 0 &amp;&amp; _xAngle <= 90) {
gl.glRotatef(_xAngle, 1f, 0f, 0f);
}
if(_xAngle > 90) {
gl.glRotatef(90, 1f, 0f, 0f);
}
gl.glRotatef(_yAngle, 0f, 1f, 0f);

gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(-2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f);

gl.glClearColor(0.0f, 0.1f, 1.0f, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, _vertexBuffer);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

gl.glNormalPointer(GL10.GL_FLOAT, 0, _normalBuffer);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);

gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_LIGHTING);

FloatBuffer light0 = FloatBuffer.allocate(4);
light0.put(2.0f); light0.put(2.0f); light0.put(0.0f); light0.put(1.0f);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, light0);

FloatBuffer light1 = FloatBuffer.allocate(4);
light1.put(-2.0f); light1.put(2.0f); light1.put(0.0f); light1.put(0.5f);
gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, light1);

gl.glEnable(GL10.GL_LIGHT0);
gl.glEnable(GL10.GL_LIGHT1);

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, _nrOfVertices-1);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}

public void detail(int d) {
detail = d;
}

private void initShape() {
float[] coords = {
-1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f,

1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 0.0f, 1.0f,

1.0f, 0.0f, 1.0f,
1.0f, 0.0f, -1.0f,
1.0f, 1.0f, -1.0f,

1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f,

1.0f, 0.0f, -1.0f,
-1.0f, 0.0f, -1.0f,
-1.0f, 1.0f, -1.0f,

1.0f, 0.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,

-1.0f, 0.0f, -1.0f,
-1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 1.0f,

-1.0f, 0.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,

1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.5f, 0.0f,

-1.0f, 1.0f, 1.0f,
-1.0f, 1.5f, 0.0f,
-1.0f, 1.0f, -1.0f,

-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.5f, 0.0f,

-1.0f, 1.0f, 1.0f,
1.0f, 1.5f, 0.0f,
-1.0f, 1.5f, 0.0f,

1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.5f, 0.0f,

1.0f, 1.0f, -1.0f,
-1.0f, 1.5f, 0.0f,
1.0f, 1.5f, 0.0f
};

_nrOfVertices = coords.length;

ByteBuffer vbb = ByteBuffer.allocateDirect(coords.length * 4);
vbb.order(ByteOrder.nativeOrder());
_vertexBuffer = vbb.asFloatBuffer();
_vertexBuffer.put(coords);
_vertexBuffer.position(0);

// Fills vertex array with coordinates
point = new float[_nrOfVertices/3][3];
int i = 0;
int j = 0;
while(i<_nrOfVertices) {
point[j][0] = coords[i++]; // x
point[j][1] = coords[i++]; // y
point[j++][2] = coords[i++]; // z
}

// Normal function called
normalCoords = new float[(_nrOfVertices/3)];
normal();

ByteBuffer nb = ByteBuffer.allocateDirect(normalCoords.length*4);
nb.order(ByteOrder.nativeOrder());
_normalBuffer = nb.asFloatBuffer();
_normalBuffer.put(normalCoords);
_normalBuffer.position(0);
}

void normal() {
float[] v1 = new float[3];
float[] v2 = new float[3];

int i = 0;
int j = 0;
while(i<_nrOfVertices) {
v1[0] = point[i+1][0] - point[i][0];
v1[1] = point[i+1][1] - point[i][1];
v1[2] = point[i+1][2] - point[i][2];

v2[0] = point[i+2][0] - point[i][0];
v2[1] = point[i+2][1] - point[i][1];
v2[2] = point[i+2][2] - point[i][2];

normalCoords[j++] = (v1[1] * v2[2]) - (v1[2] * v2[1]);
normalCoords[j++] = (v1[2] * v2[0]) - (v1[0] * v2[2]);
normalCoords[j++] = (v1[0] * v2[1]) - (v1[1] * v2[0]);

i += 3;
}
}
}

bcthund
11-01-2010, 03:16 PM
I'm fairly new to the mathematics involved but from the routines I use, a normal calculation for a triangle is as follows:



struct Point {
float x, y, z;
};

ab.x = b.x - a.x;
ab.y = b.y - a.y;
ab.z = b.z - a.z;

ac.x = c.x - a.x;
ac.y = c.y - a.y;
ac.z = c.z - a.z;

//Compute Normal
Point n;
n.x = (u.y * v.z) - (u.z * v.y);
n.y = -((u.x * v.z) - (u.z * v.x));
n.z = (u.x * v.y) - (u.y * v.x);


My Cross Product looks slightly different from yours, specifically the "n.y" line.

I use this routine to get the normal for a triangle, not individual vertex normals but the math should all be the same. But like I said, this is all very new to me as well. Hope this helps.

MaxH
11-01-2010, 05:06 PM
Are you normalizing your normals somewhere? They must have length = 1.0.

JMoore
11-01-2010, 05:45 PM
bcthund, I'll try that out and see what it produces.

Max, I had the normalization added but didn't see any changes. Perhaps I did it incorrectly.

Can you think of any reason why I'm seeing random polygons scattered about all over when I touch and rotate? Could it possibly be a graphics card issue? I'm using an '08 Macbook so I can't imagine it would be...

bcthund
11-01-2010, 05:51 PM
If you get graphical artifacts in other opengl applications then it's possibly a card or driver issue but that is unlikely. I have had a laptop in the past that produced bad geometry in half-life causing triangles to spike up occasionally.

But chances are there is a glitch in your translation code. I notice you have glLoadIdentity() called twice and I have known this to cause issues on occasion. I call glLoadIdentity one time per frame and use glPushMatrix() and glPopMatrix() to control my translations.

JMoore
11-01-2010, 06:57 PM
I'm pretty new to this, so I'll have to read up on glPush and PopMatrix. Not too sure what those are.

I was reading on another forum that flickering can be remedied by double buffering? Not sure how to implement this.

Also, is there an easy way to draw the normal lines so that I can see where my function is going wrong? I have a feeling that I'm not computing them correctly for each face.