My vertex normals appear to be messed up.

Hello! I’ve been learning the Java binding of OpenGL (Jogl) and have been having trouble with lighting.

I’ve been writing an .OBJ file loader for the past week and have been getting pretty far. I decided to calculate the surface normals rather than rely on the file including them. The surface normals appear to be working accurately, but my vertex normals look quite off. Here are some screenshots of the smooth shading off and on.
I can’t write a link yet, so please copy and paste this to the url bar to see the pictures: imgur.com/a/hzS9i

I’m not sure what exactly is wrong with my code to produce issues like that, but here are the parts that I think might be contributing.

private void averageNormals() {



		long time = System.currentTimeMillis();


		sNormals_x = new Float[vertices_x.size()];
		sNormals_y = new Float[vertices_y.size()];
		sNormals_z = new Float[vertices_z.size()];

		for(int i = 0; i < faces.size(); i++){//each face of the polygon
			vertex:
				for(int j = 0; j < faces.get(i).size(); j++){//each vertex call of each face


					float vertNorm_x = 0,vertNorm_y = 0,vertNorm_z = 0;

					if(sNormals_x[faces.get(i).get(j)] != null)
						continue vertex;


					for(int x = i; x < faces.size(); x++){
						for(int y = 0; y < faces.get(x).size(); y++){

							if(vertices_x.get(faces.get(x).get(y)) == vertices_x.get(faces.get(i).get(j)) 
									&& vertices_y.get(faces.get(x).get(y)) == vertices_y.get(faces.get(i).get(j)) 
									&& vertices_z.get(faces.get(x).get(y)) == vertices_z.get(faces.get(i).get(j)) 
									){


								//if(faces.get(i).get(j) == faces.get(x).get(y)){

								vertNorm_x += faceNormals.get(x)[0];
								vertNorm_y += faceNormals.get(x)[1];
								vertNorm_z += faceNormals.get(x)[2];





							}

						}
					}

					float total = (float) Math.pow(	Math.pow(vertNorm_x,2) + 
							Math.pow(vertNorm_y,2) +
							Math.pow(vertNorm_z,2) 
							,.5);

					sNormals_x[faces.get(i).get(j)] = (vertNorm_x / total);
					sNormals_y[faces.get(i).get(j)] = (vertNorm_y / total);
					sNormals_z[faces.get(i).get(j)] = (vertNorm_z / total);






				}}
		System.out.println("It took " + (System.currentTimeMillis() - time) + " milliseconds.");


	}

sNormals_* are array lists of vertex normals. It loops through everything vertex of the object, then loops through every vertex again to look for matches (adjacent faces). when it does, it adds the face normal to the temporary “vertNorm*.” After doing this it normalizes vertNorm* and adds it to the smooth normals at the index that faces.get(i).get(j) returns (the same index that a call to the list of vertices would require. it just pairs them up)

	float SHINE_ALL_DIRECTIONS = 1;
		float[] lightPos = {20 + offset_x,20 + offset_y,20 + offset_z, SHINE_ALL_DIRECTIONS};
		//float[] lightColorAmbient = {0.4f, 0.4f, 0.4f, 1f};
		float[] lightColorSpecular = {1f, 1f, 1f, 1f};

		// Set light parameters.
		gl.glLightfv(GL_LIGHT1, GL_POSITION, lightPos, 0);
	//	gl.glLightfv(GL_LIGHT1, GL_AMBIENT, lightColorAmbient, 0);
		gl.glLightfv(GL_LIGHT1, GL_SPECULAR, lightColorSpecular, 0);

		// Enable lighting in GL.
		gl.glEnable(GL_LIGHT1);
		gl.glEnable(GL_LIGHTING);

		// Set material properties.
		float[] rgba = {1, 1, 1};
		gl.glMaterialfv(GL_FRONT, GL_AMBIENT, rgba, 0);
		gl.glMaterialfv(GL_FRONT, GL_SPECULAR, rgba, 0);
		gl.glMaterialf(GL_FRONT, GL_SHININESS, 0.5f);

This part was taken from a jogl tutorial.

If that wasn’t enough, I can get some more of my code.

But should the lighting look like that? I don’t think so, but I’m fairly new to this. I’m fairly certain that Link is incorrect, but I’m not sure why it is.

Also I apologize if this has been asked, I searched the forums for “light” but couldn’t come up with a better search term for it.

Just from the pictures, I’d guess that you aren’t normalising the normals to unit length, and aren’t using glEnable(GL_NORMALIZE) either. That would result in the lighting intensity values typically being far outside the -1…1 range, so that any positive intensity is clamped to 1.0 and any negative intensity to 0.0. IOW, all faces are either fully lit or unlit.

Thanks for the help!

I believe I’ve normalized it with

	float total = (float) Math.pow(	Math.pow(vertNorm_x,2) + 
							Math.pow(vertNorm_y,2) +
							Math.pow(vertNorm_z,2) 
							,.5);
 
					sNormals_x[faces.get(i).get(j)] = (vertNorm_x / total);
					sNormals_y[faces.get(i).get(j)] = (vertNorm_y / total);
					sNormals_z[faces.get(i).get(j)] = (vertNorm_z / total);

and I printed the normals for everything, and I believe they mostly look correct.


---------
x Normal:0.0652547
y Normal:0.6629226
z Normal:0.74583876
---------
x Normal:0.4228353
y Normal:0.6056943
z Normal:0.674051
---------
x Normal:0.029017318
y Normal:0.9948907
z Normal:0.09669842
---------
x Normal:-0.5769266
y Normal:0.8094493
z Normal:0.109304525
---------
x Normal:-0.36956686
y Normal:0.51802975
z Normal:0.77140486
---------
x Normal:0.0652547
y Normal:0.6629226
z Normal:0.74583876
---------
x Normal:-0.5769266
y Normal:0.8094493
z Normal:0.109304525
---------
x Normal:-0.84950495
y Normal:0.49310163
z Normal:0.18759586
---------
x Normal:-0.6336471
y Normal:0.27636924
z Normal:0.7225728
---------
x Normal:-0.36956686
y Normal:0.51802975
z Normal:0.77140486
---------
x Normal:-0.84950495
y Normal:0.49310163
z Normal:0.18759586
---------
x Normal:-0.903545
y Normal:0.32187897
z Normal:0.28284326
---------
x Normal:-0.6913337
y Normal:0.18393901
z Normal:0.6987304
---------
x Normal:-0.6336471
y Normal:0.27636924
z Normal:0.7225728
---------
x Normal:-0.903545
y Normal:0.32187897
z Normal:0.28284326
---------
x Normal:-0.89845586
y Normal:0.27665398
z Normal:0.34093925
---------
x Normal:-0.66959554
y Normal:0.2797173
z Normal:0.68804073
---------
x Normal:-0.6913337
y Normal:0.18393901
z Normal:0.6987304
---------
x Normal:0.8056257
y Normal:-0.28696525
z Normal:0.51828384
---------
x Normal:0.7915004
y Normal:-0.14076306
z Normal:0.5947377
---------
x Normal:0.41655508
y Normal:-0.014255802
z Normal:0.9089987
---------
x Normal:0.41371974
y Normal:-0.020337181
z Normal:0.9101771
---------
x Normal:0.7915004
y Normal:-0.14076306
z Normal:0.5947377
---------
x Normal:0.80526567
y Normal:0.083248034
z Normal:0.58704084
---------
x Normal:0.4599735
y Normal:0.10497045
z Normal:0.8817061
---------
x Normal:0.41655508
y Normal:-0.014255802
z Normal:0.9089987
---------
x Normal:0.80526567
y Normal:0.083248034
z Normal:0.58704084
---------
x Normal:0.7734537
y Normal:0.2717568
z Normal:0.57264096
---------
x Normal:0.49563178
y Normal:0.22927073
z Normal:0.8377256
---------
x Normal:0.4599735
y Normal:0.10497045
z Normal:0.8817061
---------
x Normal:0.7734537
y Normal:0.2717568
z Normal:0.57264096
---------
x Normal:0.66004086
y Normal:0.44558245
z Normal:0.60481596
---------
x Normal:0.4522171
y Normal:0.33794355
z Normal:0.82540524
---------
x Normal:0.49563178
y Normal:0.22927073
z Normal:0.8377256
---------
x Normal:0.66004086
y Normal:0.44558245
z Normal:0.60481596
---------
x Normal:0.4228353
y Normal:0.6056943
z Normal:0.674051
---------
x Normal:0.3127996
y Normal:0.40948018
z Normal:0.8570194
---------
x Normal:0.4522171
y Normal:0.33794355
z Normal:0.82540524
---------
x Normal:0.4228353
y Normal:0.6056943
z Normal:0.674051
---------
x Normal:0.0652547
y Normal:0.6629226
z Normal:0.74583876
---------
x Normal:0.11311255
y Normal:0.3696725
z Normal:0.92225146
---------
x Normal:0.3127996
y Normal:0.40948018
z Normal:0.8570194
---------
x Normal:0.0652547
y Normal:0.6629226
z Normal:0.74583876
---------
x Normal:-0.36956686
y Normal:0.51802975
z Normal:0.77140486
---------
x Normal:-0.14152305
y Normal:0.24042831
z Normal:0.9602944
---------
x Normal:0.11311255
y Normal:0.3696725
z Normal:0.92225146
---------
x Normal:-0.36956686
y Normal:0.51802975
z Normal:0.77140486
---------
x Normal:-0.6336471
y Normal:0.27636924
z Normal:0.7225728
---------
x Normal:-0.34416345
y Normal:0.12383199
z Normal:0.9307079
---------
x Normal:-0.14152305
y Normal:0.24042831
z Normal:0.9602944
---------
x Normal:-0.6336471
y Normal:0.27636924
z Normal:0.7225728
---------
x Normal:-0.6913337
y Normal:0.18393901
z Normal:0.6987304
---------
x Normal:-0.42294383
y Normal:0.13286038
z Normal:0.89636296
---------
x Normal:-0.34416345
y Normal:0.12383199
z Normal:0.9307079
---------
x Normal:-0.6913337
y Normal:0.18393901
z Normal:0.6987304
---------
x Normal:-0.66959554
y Normal:0.2797173
z Normal:0.68804073
---------
x Normal:-0.4009233
y Normal:0.24534404
z Normal:0.88264763
---------
x Normal:-0.42294383
y Normal:0.13286038
z Normal:0.89636296
---------
x Normal:0.41371974
y Normal:-0.020337181
z Normal:0.9101771
---------
x Normal:0.41655508
y Normal:-0.014255802
z Normal:0.9089987
---------
x Normal:0.09368722
y Normal:0.053059284
z Normal:0.9941868
---------
x Normal:0.06595066
y Normal:0.08613506
z Normal:0.9940982
---------
x Normal:0.41655508
y Normal:-0.014255802
z Normal:0.9089987
---------
x Normal:0.4599735
y Normal:0.10497045
z Normal:0.8817061
---------
x Normal:0.18968892
y Normal:0.12181822
z Normal:0.9742579
---------
x Normal:0.09368722
y Normal:0.053059284
z Normal:0.9941868
---------
x Normal:0.4599735
y Normal:0.10497045
z Normal:0.8817061
---------
x Normal:0.49563178
y Normal:0.22927073
z Normal:0.8377256
---------
x Normal:0.29667988
y Normal:0.21751207
z Normal:0.9298761
---------
x Normal:0.18968892
y Normal:0.12181822
z Normal:0.9742579
---------
x Normal:0.49563178
y Normal:0.22927073
z Normal:0.8377256
---------
x Normal:0.4522171
y Normal:0.33794355
z Normal:0.82540524
---------
x Normal:0.35260683
y Normal:0.29905915
z Normal:0.8866973
---------
x Normal:0.29667988
y Normal:0.21751207
z Normal:0.9298761
---------
x Normal:0.4522171
y Normal:0.33794355
z Normal:0.82540524
---------
x Normal:0.3127996
y Normal:0.40948018
z Normal:0.8570194
---------
x Normal:0.27517816
y Normal:0.3385862
z Normal:0.8997979
---------
x Normal:0.35260683
y Normal:0.29905915
z Normal:0.8866973
---------
x Normal:0.3127996
y Normal:0.40948018
z Normal:0.8570194
---------
x Normal:0.11311255
y Normal:0.3696725
z Normal:0.92225146
---------
x Normal:0.15692547
y Normal:0.26293245
z Normal:0.9519669
---------
x Normal:0.27517816
y Normal:0.3385862
z Normal:0.8997979
---------
x Normal:0.11311255
y Normal:0.3696725
z Normal:0.92225146
---------
x Normal:-0.14152305
y Normal:0.24042831
z Normal:0.9602944
---------
x Normal:-0.003721362
y Normal:0.15562807
z Normal:0.98780876
---------
x Normal:0.15692547
y Normal:0.26293245
z Normal:0.9519669
---------
x Normal:-0.14152305
y Normal:0.24042831
z Normal:0.9602944
---------
x Normal:-0.34416345
y Normal:0.12383199
z Normal:0.9307079
---------
x Normal:-0.12946573
y Normal:0.084074035
z Normal:0.98801327
---------
x Normal:-0.003721362
y Normal:0.15562807
z Normal:0.98780876
---------
x Normal:-0.34416345
y Normal:0.12383199
z Normal:0.9307079
---------
x Normal:-0.42294383
y Normal:0.13286038
z Normal:0.89636296
---------
x Normal:-0.16307753
y Normal:0.115943775
z Normal:0.9797769
---------
x Normal:-0.12946573
y Normal:0.084074035
z Normal:0.98801327
---------
x Normal:-0.42294383
y Normal:0.13286038
z Normal:0.89636296
---------
x Normal:-0.4009233
y Normal:0.24534404
z Normal:0.88264763
---------
x Normal:-0.14297333
y Normal:0.19504027
z Normal:0.97031844
---------
x Normal:-0.16307753
y Normal:0.115943775
z Normal:0.9797769
---------
x Normal:0.27517816
y Normal:0.3385862
z Normal:0.8997979
---------
x Normal:0.15692547
y Normal:0.26293245
z Normal:0.9519669

Okay, those do seem to be normalised.

The fact remains that the lighting appears to be saturated, i.e. most faces are either fully lit or unlit, with very little in between.

If GL_LIGHT0 is getting enabled, that will be a problem, as it starts out with a diffuse colour of (1,1,1,1).

The material shininess of 0.5 will result in specular highlights covering much of the object. Typical shininess values are greater than 1.0.

There may be other issues with parts of the code not shown. Initially, I’d suggest commenting out all references to materials and lighting. Enable GL_LIGHTING and GL_LIGHT0, leaving all light and material parameters at their defaults, except for the light position.

ah! I didn’t even think to change my lighting settings!

I played around with it and now the specular on the material has been removed and the shininess has been changed to 2f. I’ve added the resulting models to the imgur album.

I think link is broken because of a problem with the way I load it in, but if you see any issue, I’d love the help.

and Thank you very much

Edit: It looks like the high amounts of light only makes the problem more visible. I think there’s still something wrong with my code to calculate vertex normals. Any help would be very appreciated.

Those all look reasonable.

I can’t figure out why you have 4 levels of loops rather than just 2. A typical algorithm looks something like:


//types
struct Vertex {
    Point3 position;
    Point3 normal;
};
struct Face {
    vector<int> vertices;
    Point3 normal;
};
// global variables
vector<Vertex> vertices;
vector<Face> faces;

// function
void calculate_vertex_normals()
{
    for (int v = 0; v < vertices.size(); v++)
        vertices[v].normal = Point3(0, 0, 0);

    for (int f = 0; f < faces.size(); f++)
        for (int i = 0; i < faces[f].vertices.size(); i++)
            vertices[faces[f].vertices[i]].normal += faces[f].normal;

    for (int v = 0; v < vertices.size(); v++)
        vertices[v].normal /= vertices[v].normal.magnitude();
}

Oh, I thought the shadows looked slightly triangle-y

And because some model files export weirdly and duplicate vertices, making each vertex index different. “faces[f].vertices[i]” would give different references than it should. Instead, I have to check if they’re equal instead of using their index value.
I do need to make it better, however.

If you need to weld coincident vertices, that should probably be a distinct step, performed before you calculate normals.

But that isn’t always desirable. If you ignore the normals in the file and generate your own, and you always weld coincident vertices, it’s impossible for a model to have corners.

Now that I think about it, that would probably make quite a few things easier. For most models I have however, this will suffice until I get around to implementing that.

I don’t fully understand what you’re saying, but I believe the problem you are describing can be solved using the smoothing groups in the .obj file. If I were to follow them correctly, it would prevent smoothing between faces that share a hard edge.

Edit: My light works now! I didn’t think about glScale at all, and didn’t realize it would screw up my normals. I read the docs and took their advice to use GL_NORMALIZE

[ATTACH=CONFIG]470[/ATTACH]