Calculating Normals Speed Tips
BlitzMax Forums/BlitzMax Programming/Calculating Normals Speed Tips
| ||
Hi there Playing around a little with OpenGL and 3D maths I've encountered a problem. It appears when calculating normals for a mesh. I don't know exactly whether my code is bad (the results are OK though), it is a beta problem or calculating normals just takes a looong time. Maybe you can give me some hints on how to optimize my code. The problem is calculating smoothed normals in particular. But now the code extract from the UpdateNormals method that causes me headache (won't run alone): 'Step 2: look whether a normal is affected by a neighbor If Smooth Then Local Vert:eeVertex 'vertex type Local OtherVertices:Int 'number of vertices that have the same X/Y/Z coords like the current vertex +1 For Local T:Int = 0 To Self.TriangleCount -1 'mesh trianglecount For Local V:Int = 0 To 2 'vertices 'check every single vertex Vert = Self.TriangleData[T].V[V] 'get current vertex OtherVertices = 1 'reset 'for every all other triangle's vertices For Local T2:Int = 0 To Self.TriangleCount -1 Local Triangle:eeTriangle = Self.TriangleData[T2] If Triangle <> Self.TriangleData[T] Then 'if the triangles are not the same 'if the coords are the same with any of the triangles vertices If (Abs(Vert.X-Triangle.V[0].X) + Abs(Vert.Y-Triangle.V[0].Y) + Abs(Vert.Z-Triangle.V[0].Z) <= .0) Or .. (Abs(Vert.X-Triangle.V[1].X) + Abs(Vert.Y-Triangle.V[1].Y) + Abs(Vert.Z-Triangle.V[1].Z) <= .0) Or .. (Abs(Vert.X-Triangle.V[2].X) + Abs(Vert.Y-Triangle.V[2].Y) + Abs(Vert.Z-Triangle.V[2].Z) <= .0) Then OtherVertices :+ 1 'mark that a normal has been added Vert.NX = Vert.NX + Triangle.NX Vert.NY = Vert.NY + Triangle.NY Vert.NZ = Vert.NZ + Triangle.NZ EndIf EndIf Next 'divide the normal parts through the count of normals that have affected the data Vert.NX :/ Float(OtherVertices) ; Vert.NY :/ Float(OtherVertices) ; Vert.NZ :/ Float(OtherVertices) Next Next EndIf I've tested it with a single 2048 poly mesh and this part of the method takes >3.3 seconds (on a 2.8 ghz p4!). On the other hand it is quite costly because you have to iterate all vertices and then iterate all vertices for every single vertex again, which makes a total of 2048*3*(2048-1)*3 = 37,730,304 vertices that have to be checked. I thought 'OK, let's turn off the debug mode' but things got worse. It suddenly took >4.5 seconds (!!). So my questions are: Is there a way to optimize the code? How did Blitz3D do it? How do other engines do it? Will that debug thingy get fixed? By the way - please ignore all grammar errors ;) |
| ||
It looks like you are over complicating it. The following pseudo code, shows a simpler way:For Every Vertex NormalX = 0.0 NormalY = 0.0 NormalZ = 0.0 Next For Every Triangle CalculateNormal For Every Vertex Used By Triangle NormalX :+ TriangleNormalX NormalY :+ TriangleNormalY NormalZ :+ TriangleNormalZ Next Next For Every Vertex NormalLength = SquareRoot (NormalX^2 + NormalY^2 + NormalZ^2) NormalX :/ NormalLength NormalY :/ NormalLength NormalZ :/ NormalLength Next |
| ||
That's just for common normals, not for smoothed ones :) |
| ||
What? Surely you don't want to smooth across non-shared vertices? And if you for some reason do, it's a simple matter of making a cross-reference list before calculating the normals. |
| ||
If all else fails, try this. Use it myself on a p2-350 without any slow down on bigger meshes. Rename the maths funcs to whichever you're using, all standard ops. |
| ||
Aah now I get it. The problem is that I used 3 independant vertices for each triangle. I think that this can be fixed easily. So I suppose you should merge vertices with the same coordinates after loading the mesh (depends on how that mesh is saved of course). Is there a way to do smoothing across non-shared vertices faster? Just being curious. What stays is that debug issue. |
| ||
Yes, build an array:Type SharedVert field x:float,y:float,z:float field nx:float,ny:float,nz:float EndType local sharedverts:TList = New TList local VertReference:SharedVert[VertexCount] For Every Vertex local found = false for sv:SharedVert = EachIn SharedVerts if vertex position = sv position found = true exit endif next if found = false sv:SharedVert = new SharedVert sv position = vertex position sharedverts.addlast sv endif VertReference[vertexnumber] = sv Next ClearList SharedVertsAnd then use the VertReference[] instead of searching through all the vertices for each triangle. |
| ||
It might be a good idea to print some debug info from the code as well - just to make sure that loops are being executed the correct number of times, and to identify where slowdown is occuring. |
| ||
The number of executed loop iterations is correct (checked it like you suggested with print). The slowdown seems to come from the vast amount of iterations. It irritated me because I didn't know there are faster ways to do it. What keeps irritating me is that debug thing. What's BRL's position on that? |
| ||
eizdealer are you even reading the posts? |
| ||
I hope so... I'm sorry if I missed something though. I've got a fast working method now - thank you. Edit: Ah got it, I'm really sorry :( This day was just too long. I thought you refer to 'merging' with that 'yes' |