Jump to content

Ab-normals


Bigus
 Share

Recommended Posts

HI All

 

I'm a 3D and Babylon newbie and am struggling with normals. I understand the concept of them (I think) and that in Babylon terms a normal is defined for each vertex, but I don't know what values to use to define those normals or how to calculate them.

 

Here is what I've been working on (a way of extruding an object from a 2d face):

 

http://jsbin.com/noquwehujudi/1/edit

 

The object is meant to be like a rectangular box with a rebate cut out of it and each "face" of that object is composed of 2 polygons but only one of which is "facing" the right way.

 

I basically set all the normals to 1 _ I don't know what that 1 means, it's just a value used in one of the playground demos. I don't understand why some polygons are facing the right way (outwards) and some are facing inwards.

 

Could someone tell me what I should be doing, in not too mathematical terms?

 

Thanks

Bigus

Link to comment
Share on other sites

Thanks for the reply. I did wonder about the order of the faces as there seemed to be no other logical explanation for it. I'll have a look at backFaceCulling too.

 

With normals, what are the actual values expressed in? That is, are they radians or something else? Do they need recalculating when you rotates the object?

Link to comment
Share on other sites

Well, I am having problems with normals myself, trying to re-calculate them on the fly after each frame of deforming / morphing a mesh.  I do know places to look though, for a better understanding. :)

 

First, a normal is a direction vector, values -1 to 1.  In Babylon it is stored as a flatten array of x0,y0,z0,x1,y1,z1 …  

 

See a short section on it in the OpenGL equivalent "Blue/Red" book, "WebGL Programming Guide", pgs 299-301.

 

Also look at source for Mesh.convertToFlatShadedMesh(),  maybe run it on your mesh to see what happens. (Might be in AbstractMesh instead)

 

Finally, I was using code converted to BabylonJS from this page, though since I am having problems, maybe to do not take as perfect.

http://stackoverflow.com/questions/18519586/calculate-normal-per-vertex-opengl

 

Hope that helps.

 

Jeff

Link to comment
Share on other sites

Hi Bigus... welcome to the forum.

 

First, here's some links to some forum posts where i first learned about "plotting".

 

http://www.html5gamedevs.com/topic/2571-the-wingnut-chronicles/page-13#entry31117

http://www.html5gamedevs.com/topic/2571-the-wingnut-chronicles/page-13#entry31179

http://www.html5gamedevs.com/topic/2571-the-wingnut-chronicles/page-13#entry32156

http://www.html5gamedevs.com/topic/2571-the-wingnut-chronicles/page-13#entry32321

http://www.html5gamedevs.com/topic/2571-the-wingnut-chronicles/page-14#entry34152

 

All of those contain SOME kind of demented plotting demo.

 

But for now, lets take a look at this...  http://urbanproductions.com/wingy/babylon/walls/js/wall01.js

 

Find the section near the bottom... BABYLON.Wall.prototype.makeplane = function (width, height) {

 

I remarked out the counter-clockwise plotting of the 4 vertex positions, and went with the clockwise method.  Since my camera was mainly looking in a +z direction, I wanted all my normals FOR those 4 points... to face -z... toward the camera. So...

 

var normals = [
   0, 0, -1,
   0, 0, -1,
   0, 0, -1,
   0, 0, -1
];

 

Those are 'directions'.  You could imagine them as...

 

var normals = [
   new BABYLON.Vector3(0, 0, -1),
   new BABYLON.Vector3(0, 0, -1),
   new BABYLON.Vector3(0, 0, -1),
   new BABYLON.Vector3(0, 0, -1)
];

 

Now IF I would have used counter-clockwise plotting and the same normals as above, the plane would NOT have been visible from the current camera position.  BUT... with counter-clockwise plotting, and camera positioned same +z looking, I could switch the z-direction of all the normals... like this...

 

var normals = [
   0, 0, 1,
   0, 0, 1,
   0, 0, 1,
   0, 0, 1
];

 

(no negative z factors anymore.  All positive z)

 

... and then the plane's face would become visible again.

 

About indices order...

 

In this demo, I kept the indices the same for both clockwise and counterclockwise plotting.

 

But I could have simply done a single plotting of ANY kind... like this...

 

// clockwise plotting - front face is -z
  var positions = [
     hw, -hh, 0,
     hw, hh, 0,
     -hw, hh, 0,
     -hw, -hh, 0
  ];

 

(hw = half width, hh = half-height)

 

And then simply switched the order of THE INDICES of those... like this...

 

  var indices = [];
  indices.push(2);
  indices.push(1);
  indices.push(0);

 

  indices.push(3);
  indices.push(2);
  indices.push(0);

 

(connected the dots of each triangle... in the opposite direction)

 

So to be brief (yeeeah)... the order of plotting the positions is really not important.  It's the order of the indices (the order you connect the dots together with index lines)... that determines clockwise/counter-clockwise.  In other words, I did not need to change the positions data... to attain clockwise and counter-clockwise plotting.  I could have simply reversed the order of "connecting the dots"  (indices)... like shown just above.

 

So never concern yourself with the order of placing your positions (like I did).  Concern yourself with the order of the indices.  And no recalculating necessary.  We let the framework do lighting FOR us (once we have our lighting normals facing in a direction that the light can see them).  :)

 

Have I explained this to the point of confusing you?  Probably.  :)  Hope this helps.

Link to comment
Share on other sites

THanks guys for the responses.

 

Wingnut, that's a great explanation. Between that, and reading those pages Jeff suggested in the WebGL book, the relationship between points, normals and indices makes sense now.

 

Initially, I was also wondering what was meant by [counter-]clockwise plotting, i.e: clockwise in relation to what? But I see now, in the case of my object (and the cube example in that OpenGL book) the plotting (or order of points as specified by the indices) is done clockwise round each face. Applying that, I now have whole faces looking OK (not sorted normals yet):

 

http://jsbin.com/noquwehujudi/2/

 

I'm not sure I understand why it's necessary to plot the vertices in a particular order though. They just create a triangular polygon between 3 vertices in 3D space, so why does the order matter?

 

Jeff, on that stackoverflow page, did you manage to translate that response from Shabbi into Javascript/Babylon?

 

Thanks, Bigus

Link to comment
Share on other sites

I'm not sure I understand why it's necessary to plot the vertices in a particular order though. They just create a triangular polygon between 3 vertices in 3D space, so why does the order matter?

 

Jeff, on that stackoverflow page, did you manage to translate that response from Shabbi into Javascript/Babylon?

 

Thanks, Bigus

 

 

The order dependence is a old trick to avoid drawing triangles that won't be seen. You don't have to use it (you can turn it off) but but gfx hardware does it for you for free and it saves you from wasting rending time drawing pixels that will never be seen!

 

The idea is is that if you have a closed mesh, the triangles facing away from the camera at any given instant won't be seen as they will be 100% covered by those that are facing the camera.

 

It turns out that checking the order of the points, in screen space(!), is an efficient way to track which way a triangle (or quad, or convex polygon) is facing, as the ordering is preserved by translation, rotation and (positive) scaling so long as that triangle is still ends up with the side you care about facing the camera, and the order is flipped if the triangle doesn't!

 

This trick works so well in fact that if you just have the odd mesh that's not closed (ie. has tris that need to show either way) it can be more efficient just to duplicate those faces with the reverse winding that to turn face culling off even temporarily.

Link to comment
Share on other sites

Yes,

I have also overridden Mesh.setIndices() to determine / store all the faces a given vertex is a member of for performance reasons.  It is in an unreleased BABYLON.Automation sub-class.  Some of the stuff I am doing applies equally to the parent class BABYLON.Mesh, but I am developing in a vacuum, since it was easier.

 

That said, it still needs debugging, and I plan on moving at least the caller of the calculation of down to a class called BABYLON.ShapeKeyGroup.  There I store all the final positions of each shape key in the group (A mesh can have multiple groups, MOUTH & LEGS).  The animation interpolates the correct position of each vertex in the group for a given frame.  Interpolating the normals the same way would be much faster that calc every frame.  If one computes the end point normals when either loading a key from Blender or computes a derived key, higher frame rates can sustained.

 

FYI, everything is in native arrays, e.g. Float32Array & Int16Array.  I also bypass that BABYLON,VertexData class and go directly to BABYLON.Engine, except for the initial load.

Link to comment
Share on other sites

I have encounter many problems, which I think I have worked through.  Have come up against an issue that I believe is related to how the Blender exporter processes vertices.  I end up with some faces where 2 of the points are identical.  To make sure I was not screwed up, I wrote a separate function to count them, without doing anything.

function findZeroAreaFaces(meshID) {    var mesh = scene.getMeshByID(meshID);    var indices = mesh.getIndices();    var positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);    var nFaces = indices.length / 3;    var nZeroAreaFaces = 0;    var faceOffset;    var p1 = BABYLON.Vector3.Zero();    var p2 = BABYLON.Vector3.Zero();    var p3 = BABYLON.Vector3.Zero();    for (var f = 0; f < nFaces; f++) {        faceOffset = f * 3;        BABYLON.Vector3.FromArrayToRef(positions, indices[faceOffset    ], p1);        BABYLON.Vector3.FromArrayToRef(positions, indices[faceOffset + 1], p2);        BABYLON.Vector3.FromArrayToRef(positions, indices[faceOffset + 2], p3);        if (p1.equals(p2) || p1.equals(p3) || p2.equals(p3)) {            nZeroAreaFaces++;        }    }	console.log("# of zero area faces:  " + nZeroAreaFaces);}

I point it at most of the Blender meshes I have access to, and it reports > 0.  This results in a NaN for the sin_alpha calc, since there will be a division by 0.  Somebody have a clue?  My detection is correct, right?

Link to comment
Share on other sites

Are you sure the Blender meshes you tried weren't just like that? Presuming you have Blender yourself, have you tried cleaning the meshes to remove degenerate geometry?

eg. as per http://wiki.blender.org/index.php/Doc:2.6/Manual/Modeling/Meshes/Cleanup

 

Or maybe confirming comparing the triangle count Blender gives for the mesh to the raw count (including degenerates) you see from the exporter?

Link to comment
Share on other sites

I have full access to the .blends and exporter.  Since posting have implemented similar test code inside the python export.  Get 0 there for a .blend that get 54 in JS. Am sure python test works as I already had a same_vertex() & also tested with same_vertex(pos1, pos1) and got a numZeroFaces which equalled total faces.

pos0 = mesh.vertices[face.vertices[0]].copos1 = mesh.vertices[face.vertices[1]].copos2 = mesh.vertices[face.vertices[2]].coif same_vertex(pos0, pos1) or same_vertex(pos0, pos2) or same_vertex(pos1, pos2): numZeroAreaFaces += 1

Have written my own exporter, Tower of Babel, but it is a re-write of original, especially in this area.  I also tested .babylons out of the original exporter in JS, same result.  Total triangle count exactly matches the number in the banner for Blender for the .blend with 54 issues.

 

This python code is complicated in two ways:

-  It processes faces in material order for the benefit of the fragment shader

-  Allows for large Blender meshes to be broken into multiple BabylonJS meshes

 

Neither come into play for my test blend.

Link to comment
Share on other sites

I moved the down test down after the values are stored in the python class members, close to what is going to be written out.  Not getting any zero areas:

 # just finished a face pos0 = self.positions[self.indices[indicesCount - 3]] pos1 = self.positions[self.indices[indicesCount - 2]] pos2 = self.positions[self.indices[indicesCount - 1]] if same_vertex(pos0, pos1) or same_vertex(pos0, pos2) or same_vertex(pos1, pos2): numZeroAreaFaces += 1

Am going do 1 more test, where I actually run through the entire self.positions & self.indices in a separate method.  Exactly the same as the JS above.  Not sure why, but sometimes testing pointless things prove you wrong in important ways.

Link to comment
Share on other sites

Ran the python method before, no zero area faces.  That is as close to the output as you can get.  Must be on JS side.

    def find_zero_area_faces(self):        nFaces = int(len(self.indices) / 3)        nZeroAreaFaces = 0        for f in range(0, nFaces):            faceOffset = f * 3            p1 = self.positions[self.indices[faceOffset    ]]            p2 = self.positions[self.indices[faceOffset + 1]]            p3 = self.positions[self.indices[faceOffset + 2]]                        if same_vertex(p1, p2) or same_vertex(p1, p3) or same_vertex(p2, p3): nZeroAreaFaces += 1                   TowerOfBabel.warn('WARNING: # of 0 area faces found:  ' + str(nZeroAreaFaces), 2)        
Link to comment
Share on other sites

I love it when an error crops up a very short distance from when it was correct.  Get one instance of when it was wrong in JS, find what it should be in python, then mentally "Interpolate".  In this case, need also multiple value out of indices by 3.  Now can start work forward.  Final JS test code:

function findZeroAreaFaces(meshID) {    var mesh = scene.getMeshByID(meshID);    var indices = mesh.getIndices();    var positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);    var nFaces = indices.length / 3;    var nZeroAreaFaces = 0;    var faceOffset;    var p1 = BABYLON.Vector3.Zero();    var p2 = BABYLON.Vector3.Zero();    var p3 = BABYLON.Vector3.Zero();    for (var f = 0; f < nFaces; f++) {        faceOffset = f * 3;        BABYLON.Vector3.FromArrayToRef(positions, 3 * indices[faceOffset    ], p1);        BABYLON.Vector3.FromArrayToRef(positions, 3 * indices[faceOffset + 1], p2);        BABYLON.Vector3.FromArrayToRef(positions, 3 * indices[faceOffset + 2], p3);        if (p1.equals(p2) || p1.equals(p3) || p2.equals(p3)) {            nZeroAreaFaces++;        }    }	console.log("# of zero area faces:  " + nZeroAreaFaces);}

It's always obvious once you know why.  Write that down.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...