Calculating normals goes somehow wrong

Started by TheBoneJarmer, November 30, 2014, 21:17:55

Previous topic - Next topic

TheBoneJarmer

Hey guys

So here is my problem. I draw a pyramid. Calculate normals for it. Pyramid goes black. Can't put it another way. I searched up some tutorials for normal calculation and I think I got quite far. Lighting mostly does what it needs to but I assume I made some mistakes over there as well. I'll just post my code and see if you guys can see what is going wrong. These are face by face calculations bytheway. Did not went beyond that.

Code: Contents of model file
v -1.0 0.0 -1.0
v 0.0 2.0 0.0
v 1.0 0.0 -1.0
v -1.0 0.0 1.0
v 0.0 2.0 0.0
v 1.0 0.0 1.0
v -1.0 0.0 -1.0
v 0.0 2.0 0.0
v -1.0 0.0 1.0
v 1.0 0.0 -1.0
v 0.0 2.0 0.0
v 1.0 0.0 1.0
v -1.0 0.0 1.0
v -1.0 0.0 -1.0
v 1.0 0.0 -1.0
v -1.0 0.0 1.0
v 1.0 0.0 1.0
v 1.0 0.0 -1.0
n 0.0 0.4472136 -0.8944272
n 0.0 0.4472136 -0.8944272
n 0.0 0.4472136 -0.8944272
n 0.0 -0.4472136 -0.8944272
n 0.0 -0.4472136 -0.8944272
n 0.0 -0.4472136 -0.8944272
n 0.8944272 -0.4472136 0.0
n 0.8944272 -0.4472136 0.0
n 0.8944272 -0.4472136 0.0
n 0.8944272 0.4472136 -0.0
n 0.8944272 0.4472136 -0.0
n 0.8944272 0.4472136 -0.0
n 0.0 -1.0 0.0
n 0.0 -1.0 0.0
n 0.0 -1.0 0.0
n -0.0 1.0 0.0
n -0.0 1.0 0.0
n -0.0 1.0 0.0


These are the results of the calculation shown in the next piece of code.

public void calculateNormals() {
		listNormals = new ArrayList<Vector3>();
		
		for (int i=0; i < listVertices.size(); i+=3) {		
			Vector3 p1 = listVertices.get(i);
			Vector3 p2 = listVertices.get(i+1);
			Vector3 p3 = listVertices.get(i+2);
			
			//Get the UV data
			Vector3 u = new Vector3(p2.x - p1.x,p2.y - p1.y,p2.z - p1.z);
			Vector3 v = new Vector3(p3.x - p1.x,p3.y - p1.y,p3.z - p1.z);
			
			//Calculate normals
			float nx = (u.y * v.z) - (u.z * v.y);
			float ny = (u.z * v.x) - (u.x * v.z);
			float nz = (u.x * v.y) - (u.y * v.x);
			
			//Normalise them
			float nf = (float) Math.sqrt((nx * nx) + (ny * ny) + (nz * nz)); //Normalisation factor
			nx = nx / nf;
			ny = ny / nf;
			nz = nz / nf;
			
			listNormals.add(new Vector3(nx,ny,nz));
			listNormals.add(new Vector3(nx,ny,nz));
			listNormals.add(new Vector3(nx,ny,nz));
		}
	}


My Vector3 class
public class Vector3 {
	//Variables
	public float x;
	public float y;
	public float z;
	
	//Conscturctors
	public Vector3() {
	}
	public Vector3(float x,float y,float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	//Conversions
	public final String toString() {
		return "[VECTOR3] " + x + " " + y + " " + z;
	}
	public final float[] toFloat() {
		float[] toReturn = {x,y,z};
		return toReturn;
	}
	public final int[] toInt() {
		int[] toReturn = {(int) x,(int) y,(int) z};
		return toReturn;
	}
}


Do you want me to post my classes I use for lighting as well? Someone in a topic somewhere mentioned it. If I place normals manually for a cube which is not that difficult lighting works as expected. Which is why I assume the source of the problem is not there.

These are the sources I used:
https://www.opengl.org/wiki/Calculating_a_Surface_Normal
http://fullonsoftware.co.uk/snippets/content/Math_-_Calculating_Face_Normals.pdf

I also looked for topics with the same problem, but nothing came close (or at least I assumed they weren't)
Thanks in advance guys!

Kai

It looks about right, except that your 'u' and 'v' vectors must be swapped when computing the cross product, assuming your rendering uses counter-clockwise (default in OpenGL) front-facing and the triangles of your model are also defined in counter-clockwise order.
Provided, this is the case, your normals currently point in exactly the other direction (inwards, i.e. towards the back face of the triange).
So either swap u and v before calculating the cross product or negate the result at the end.


EDIT: At second thought, the cross product actually turns out right. Then, the issue could be that your model is not given in counter-clockwise order but is still rendered because GL_CULL_FACE is disable (by default). Try to enable it with glEnable(GL_CULL_FACE) and see whether you see your model rendered.

TheBoneJarmer

Thanks for the reply, unfortunately, it did not work. Pyramid is still black and some of it's faces are missing. What you mean by counter-clockwise front-facing bytheway? I currently use negative z values to translate the vertex closer to the camera. The pyramid's code I posted is displaying just fine without culling enabled. I've tested your suggestion to swap U and V. Of course it did not work but my front and back triangles suddenly turned white.

Kai

If some triangles were missing after you turned on back-face-culling, it means, the triangles in your model even have differing winding orders.

Inform yourself about "OpenGL winding order" first. Have a look at https://www.opengl.org/wiki/Face_Culling for example.

Your computations of the normal is however correct. The only thing you must consider, is whether all of your triangles in your model have the same winding order and which order that is.
After you figured that out, you set the order accordingly with glFrontFace.

EDIT: Looking at your model definition code, was that derived from a Wavefront OBJ file? It could be the case that you are loading it somehow wrong then, especially, since you did not include the face 'f' elements in your listing (iff that was derived from a Wavefront obj file) and that some of your triangles are missing once you turn on face culling.
Have a look at the topic http://forum.lwjgl.org/index.php?topic=5569.0, which explains how that is done.

Also: If your model is in fact given in clock-wise order, you actually need to negate the computed normal, since the cross-product assumes the two vectors 'u' and 'v' to be derived (as you do it) from a counter-clockwise vertex ordering.

TheBoneJarmer

It works! Thanks! I checked out my wind orders and I changed my vertices to this:
v -1 0 -1
v 0 2 0
v 1 0 -1

v 1 0 1
v 0 2 0
v -1 0 1

v -1 0 1
v 0 2 0
v -1 0 -1

v 1 0 -1
v 0 2 0
v 1 0 1


I also decided to go with CCW, which is default.

EDIT

I did not see your edit in time. I don't load from OBJ. I'm writing my own parser for now (although I have made a obj loader in the past). The contents of the code are actually the contents of my model file.

TheBoneJarmer

Hey

I want to add something that I am stuck with for a while. Almost 50% of my normals are not between 1 and -1. I never changed my calculation but every page I found stuff about normals it always is supposed to be a number between 1 and -1 and my calculation isn't always doing that. How come?

TheBoneJarmer

Sorry for my doublepost, but I have solved it yesterday. The answer is quite simply really: it was never wrong in the first place. Yes, not all normals are between -1 and 1. But I've checked things out with Blender and other modelling software and it turned out their normals weren't between -1 and 1 either. As stated earlier, the formula is fine. For me this matter is solved!

Kai

Saying "normals are between -1 and +1" is a bit misleading, because that sentence mixes two mathematical concepts and is therefore ambiguous in what is actually meant.

To explain further:

A "normal" is a mathematical three-dimensional "vector", whereas the numbers -1 or +1 or any other single number are "scalars".

The normal (i.e. vector) is composed of three such scalars, namely for all three dimension (i.e. x, y and z).

Because all those three components are scalars, they themselves can be a number between -1 and +1, respectively.

And in fact, for normals those numbers always are between -1 and +1.
If they weren't, the length of that normal/vector would exceed 1. If that was the case, we say that the normal/vector is not "normalized".
And because all normals are normalized (hence the name), every normal's length is exactly 1.

Of course, a normal's components neet not be exactly -1 or exactly +1. The only restriction upon a normal vector is that it's length (computed via sqrt(xÃ,² + yÃ,² + zÃ,²)) is exactly 1.

EDIT:

After writing this, I actually did a look-up on "normal vectors" on Wolfram http://mathworld.wolfram.com/NormalVector.html and in the above text I was partly wrong. The term "normal" seems not to stem from being "normalized", but instead is a term related to a vector being perpendicular to a surface.
It just so happens that in OpenGL a normal vector should also be normalized (have a length of 1) because of the various operations performed on it, which only give reasonable results when dealing with normalized vectors, as is the case for the dot/inner-product and the cross-product.

TheBoneJarmer

Hey Kai, thanks for the reply! I understand it better now, but still. My formula is correct isn't it? Do you have any idea why the results aren't between -1 and 1 than?

Kai

To repeat myself:
Saying that "a normal is between -1 and +1" is nonsensical.
Therefore, I simply do not know what you mean by "... why the results aren't between -1 and 1 than?".
But yes, your formula is correct and produces correct normals, provided you have a counter-clockwise winding order.

TheBoneJarmer

Ah okay. Sorry, misunderstood that. Thanks!