Should I 'normalize' my normals?

Blitz3D Forums/Blitz3D Programming/Should I 'normalize' my normals?

John Pickford(Posted 2003) [#1]
I'm building a landscape engine and calculating the vertex normals myself.

Do I actually NEED to 'normalize' the normals (set them to a length of one)?

It seems like the correct thing to do but as far as I can tell it makes no difference visually. If I can safely leave out this step I can lose a SQR and 3 divides.

Will this store up problems or fail on some cards?


Floyd(Posted 2003) [#2]
I think it doesn't matter for graphics.

Does Blitz3D use normals for anything other than lighting?

UpdateNormals originally did not normalize. I once reported an apparently
unrelated bug in UpdateNormals. Mark fixed this, and also changed UpdateNormals
so it does normalize normals.

So maybe it is important.


sswift(Posted 2003) [#3]
Well here's a test for you. Multiply each component of the normals by 100. If you don't see anty visual difference then you don't need to normalize them. Presumably the 3d card or Blitz is doing it. But I suspect that they do not automatically normalize them for you. And as far as I know, surface normals need to have a length of 1 to properly calculate lighting via the dot product.


John Pickford(Posted 2003) [#4]
Tried multiplying by 100. Can't see a difference.

I think I'll keep the normalization in there for now, just in case I run into a problem. No point in optimising it out unless I hit a speed problem.


Rob(Posted 2003) [#5]
Forgive me for being thick, but whats the formula for getting the normal of a vert pointing straight up? I need this to calculate the normals for my water meshes. Which verts only ever move up or down...


Floyd(Posted 2003) [#6]
This should probably be done by computing the normal to the mathematical surface being used.

I imagine you have y = SomeFunction(x,z), involving Sin and/or Cos.
The relevant concept here is the Gradient. You need a little bit of calculus for this, but not much.

One 'gotcha' is that Blitz Sin and Cos use degrees rather than radians.
A calculus book will tell you that the derivative of Sin is Cos.
But in Blitz the derivative of Sin is (Pi/180)*Cos.

If you don't know enough math for this then just post the details of SomeFunction().
I'll tell you the Gradient.


Rob(Posted 2003) [#7]
All I have is an array of verts that can be any height at any one time, depending on the cos and sin I use to create the wave pattern of the water... I ignore x and z positions totally, although these would be easy enough to store in a lookup table, since x and z never change in realtime.


Floyd(Posted 2003) [#8]
I know x,z are in there somewhere, although they may not appear explicitly.

Without knowing the details I can't be more specific.


simonh(Posted 2003) [#9]
You need to calculate the cross product. There's some information about it here: http://www.geocities.com/SiliconValley/2151/math3d.html

Or you might want to look at my code archive entry, which calculates a normal for a triangle:
http://www.blitzbasic.com/codearcs/codearcs.php?code=466


Floyd(Posted 2003) [#10]
That is appropriate for meshes in general.

But if you know the underlying mathematical surface you can calculate
normals much more accurately and more quickly.

It's also simpler, once you get past the hurdle of writing down an equation.


sswift(Posted 2003) [#11]
The normal for a vertex pointing straight up is 0,1,0.


Rob(Posted 2003) [#12]
	s=GetSurface(water,1)
	For i=0 To CountVertices(s)-1
		Vertex(i)\y#=Sin(freq+Vertex(i)\x#*500+Vertex(i)\z#*300)*1.2
		VertexCoords s,i,Vertex(i)\x#,-Vertex(i)\y#,Vertex(i)\z#
	Next


This is my water preturb routine.... you can see the underlying coords. A vert normal of 1 ruins the effect. Updatenormals looks perfect though.


sswift(Posted 2003) [#13]
Can't look perfect because it's mathematically impossible to properly represent the light falling on a surface with vertex normals. :-)

Imagine if you will a triangular-wave shaped terrain. Each vertex is at a point where the light should have a sharp transition from dark to light. So do you set it's normal to point away from the light, or towards it? Mathematically, updatenormals would do neither. It would set them all to point straight up, which obviously would look bad.


Floyd(Posted 2003) [#14]
Rob,

Here's the mathematical version of normals. Your water lies on this surface:

y = -1.2 * Sin( freq + x*500 + z*300 )

The first step is to reformulate this as

y + 1.2 * Sin( freq + x*500 + z*300 ) = 0

We now have the form F(x,y,z)=0.

The gradient vector, which is perpendicular to this surface, is

( dF/dx, dF/dy, dF/dz )

where e.g. dF/dx denotes the partial derivative of F with respect to x.

NOTE: d is not the correct symbol for partial derivative.
The proper one looks like a flipped over 6, but we don't have that on our keyboards.

Finally, we need partial derivatives, e.g.

dF/dx = 1.2 * (Pi/180) * 500 * Cos( freq + 500 * Vertex(i)\x# + 300 * Vertex(i)\z# )


Here is your vertex code, updated to calculate normals:
s=GetSurface(water,1)

A# = 1.2 * (Pi/180) * 500
B# = 1.2 * (Pi/180) * 300

For i=0 To CountVertices(s)-1

	u# = freq + 500 * Vertex(i)\x# + 300 * Vertex(i)\z#

	Vertex(i)\y# = 1.2 * Sin(u)
	VertexCoords s,i, Vertex(i)\x#, -Vertex(i)\y#, Vertex(i)\z#
	
	; Now the gradient at point (x,y,z)
	
	C# = Cos(u)
	
	nx# = A * C
	ny# = 1
	nz# = B * C
	
	; (nx,ny,nz) is the gradient, which is perpendicular to the surface.
	; It has length greater than 1, but that is probably harmless.
	; If there is any problem you can scale to length 1 before doing:
	
	VertexNormal s, i, nx, ny, nz
	
Next


This hasn't been tested, but it should work.

You can see how much simpler the gradient calculation is than doing vector cross products.
The only hard part is that you need to know some elementary calculus.


Michael Reitzenstein(Posted 2003) [#15]
The proper one looks like a flipped over 6, but we don't have that on our keyboards.

I think that sign (delta or whatever it is?) is only for 'change' rather than derivative itself. All the textbooks I have used use it for change in (eg for the approximation formula [delta]y = dy/dx * [delta]x.) and not in the actual derivitive. Or maybe my textbooks suck (which is actually highly likely!).


Rob(Posted 2003) [#16]
Thankyou Floyd, it's beginning to make sense- much appreciated!

Although your fixed code makes the cubic mapped water go haywire :)


Floyd(Posted 2003) [#17]
Rob,

Sorry about the haywireness. Offhand I don't see where the problem might be.

You might try scaling nx,ny,nz to have length 1.
I don't know if that matters here. It definitely would matter if you were creating a bump map.
The method used for encoding normals into the texture assumes length 1.


Michael,

That 'rounded d' symbol is shown here in The Partial Derivatives section.

Notice that equations are actually rendered as images.
This is because ordinary fonts don't have the necessary symbols.