Advertisement

Face normals for pyramid

Started by November 02, 2016 08:12 PM
6 comments, last by novus22 8 years, 1 month ago

Recently, I learned about face normals, and i'm a bit confused. I need to use them for a directional light, but I've been having trouble calculating them. I would appreciate if someone types the code out for calculating them and explains it.

This is my code for generating a pyramid:


GeneratePyramid(float baseWidth, float baseDepth, float apexHeight, MeshData& meshData) {

	float halfWidth = baseWidth * 0.5f;
	float halfDepth = baseDepth * 0.5f;

	Vertex v[5];

	/*
	        v[2]
	         /\\ 
	        /  \\
	v[4]   /    \\  v[3]
	v[0]  /______\/  v[1]

	*/



	v[0] = Vertex(-halfWidth, 0, -halfDepth);
	v[1] = Vertex(halfWidth, 0, -halfDepth);
	v[2] = Vertex(0, apexHeight, 0); // apex
	v[3] = Vertex(halfWidth, 0, halfDepth);
	v[4] = Vertex(-halfWidth, 0, halfDepth);

	meshData.vertices.assign(&v[0], &v[5]);

	unsigned int i[18] = {
		0, 2, 1,
		1, 2, 3,
		3, 2, 4,
		4, 2, 0,
		0, 1, 4,
		1, 3, 4

	};

	meshData.indices.assign(&i[0], &i[18]);
} 

the struct MeshData is simply defined as:


struct MeshData {
		std::vector<Vertex> vertices;
		std::vector<unsigned int> indices;
}; 

and the Vertex struct is defined as:


struct Vertex {
		Vertex() {};
		Vertex(float x, float y, float z);

		XMFLOAT3 pos;
}; 

I would like GeneratePyramid() to also calculate the normals. Should I add an "XMFLOAT3 normal" member to the Vertex struct? And if so, how would I use that to calculate them in GeneratePyramid()?

Thanks in advance!

Extra:

I read that you don't need vertex normals for pyramids since it has sharp edges, but I would also like to know how you would calculate them.

You could probably find a tutorial for calculating both face and vertex normals with a quick search, luckily it is a fairly easy process. Call each triangle vertex A, B and C for simplicity sake here, you create two vectors edge1 and edge2 by doing edge1 = B-A and edge2 = C-A (a vector that points from A to B and one that points from A to C). Once you have those two you work out the cross product of them, normalize it and that is then your face normal. Do that for each triangle. It really is a simple process. If you find that the normal faces inwards instead of outwards then swap around edge1 and edge2 in the cross product.

Vertex normals are a fair bit more work, first you need to have already calculated face normals, then you need a way of knowing which faces are attached to each vertex. Then for every vertex you work out a weighted average of all the normals of the faces attached to that vertex and normalise it. Something like:

for each vertex

normal = zero;

for each attached face

normal += face.normal * face.surfaceArea // You probably want bigger faces to contribute more

Normalize(normal);

You should probably check the normal isn't zero at the end. There are other methods for averaging the face normals but I find surface area is a good choice (of course you then need to work out the surface area). Whatever you choose you should try to have smaller faces contributing less than larger ones or it ends up looking odd.

Apparently you can workout the area of a triangle by halving the length of the cross product of the edges (which you do while calculating the face normal). source

It's worth noting that for your pyramid to have per vertex normals you would need to duplicated vertices, each triangle face would have it's own 3 vertices and the two for the base can share 4 vertices.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

Advertisement
Out in the real world, normals come from artists and the art tools.

Consider that normals can be whatever the artist wants them to be. They may have created something they want to appear smooth and round, with normals that continuously curve around edges, or they may have intended the same model to have facets and sharp corners at every intersection.

Even with the pyramid described, the lighting could be made to have sharp angles, or appear to bend around a curve at the edges. The program doesn't know the difference, all it sees is the points but not the intent. The modeler knows the intent and can give the normals that match.

Thanks Nanoha, I followed your advice and made the face normal (I ended up making a Face struct that holds an array of 3 Vertex objects. Then, I made a std::vector of Face structs in my MeshData, and looped through the number of Faces and calculated the normal there).

Vertex normals are a fair bit more work, first you need to have already calculated face normals, then you need a way of knowing which faces are attached to each vertex

This may be a dumb question, but how would you determine which faces share which vertices? Is there a way other than looking at the actual vertex data?

----

Out in the real world, normals come from artists and the art tools.


Consider that normals can be whatever the artist wants them to be. They may have created something they want to appear smooth and round, with normals that continuously curve around edges, or they may have intended the same model to have facets and sharp corners at every intersection.

Even with the pyramid described, the lighting could be made to have sharp angles, or appear to bend around a curve at the edges. The program doesn't know the difference, all it sees is the points but not the intent. The modeler knows the intent and can give the normals that match.

Then I can't wait to get out to the real world ;P I'm currently reading an introductory book to DirectX, and so far we're hard-coding all the vertices and indicies of the shapes we're making within the program. The book will eventually be teaching me how to load files from modeling programs, though. Doing this was mostly for the sake of learning how to calculate light, which required the normals. I understand what you're saying though, my wording was a bit off. I meant to say that the pyramid i'm trying to make doesn't need per vertex normals, not pyramids in general.

This may be a dumb question, but how would you determine which faces share which vertices? Is there a way other than looking at the actual vertex data?

That is a very good question and it is exactly as you say, you look at the data. At some point you need to do a bit of work to build up this information, you said you have made a Face struct, hopefully in that you also have details about the indices that make up that face (e.g. face 0 has indices 0, 2, 1). For each vertex, look through all those faces and if they contain the index of the vertex you are checking then that is an attached face. In your above code if I look at vertex 0 I can see that it is used by triangles 0, 3 and 4. Vertex 1 is used by 0, 1, 4 and 5. I'd use those respective face normals when calculating the vertex normal.

With the pyramid as it is though it won't have vertex normals as you expect it to have, it'll be some strange smooth looking thing, to have sharp edges you need to have more vertices. See my earlier point about having to duplicate vertices.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

With the pyramid as it is though it won't have vertex normals as you expect it to have, it'll be some strange smooth looking thing, to have sharp edges you need to have more vertices. See my earlier point about having to duplicate vertices.

Sorry, I'm a bit confused haha. If I were to duplicate my vertices and have each Face struct have 3 distinct vertices, and then calculated vertex normals for all the vertices, wouldn't that have the same effect as simply calculating the face normals? So for smooth shading, you would need vertices that are shared, and if you wish for hard edges for a shape like a pyramid or a cube, you would duplicate the vertices so they're distinct and they don't take the average? Is that why when you want sharp edges, you can just use face normals rather than calculating per vertex normals?

Advertisement

With the pyramid as it is though it won't have vertex normals as you expect it to have, it'll be some strange smooth looking thing, to have sharp edges you need to have more vertices. See my earlier point about having to duplicate vertices.

Sorry, I'm a bit confused haha. If I were to duplicate my vertices and have each Face struct have 3 distinct vertices, and then calculated vertex normals for all the vertices, wouldn't that have the same effect as simply calculating the face normals? So for smooth shading, you would need vertices that are shared, and if you wish for hard edges for a shape like a pyramid or a cube, you would duplicate the vertices so they're distinct and they don't take the average? Is that why when you want sharp edges, you can just use face normals rather than calculating per vertex normals?

Yeah, that's a good summary. The only faces that would share vertices for a pyramid would be the base. Most of the time you will want to have per vertex normals but also have duplicates so you can have both smooth areas and hard edges. Think of something like a half sphere, all the top would be nice and smooth but then the flat side would have a hard edge.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

Finally I understand, thanks!! This made everything much clearer, really appreciate the help! :)

This topic is closed to new replies.

Advertisement