Jump to content

double sided mesh


jerome
 Share

Recommended Posts

Hi,

 

Again some problem with normals.

 

I have a list of vector3 to construct some mesh.

I set my vectors3 as positions. Right.

I set my indices to have nice tiny triangles (faces) between my positions. Right. I call this indices array : indicesRecto.

I compute my normals from these positions/indices. No problem. The same : in a normalsRecto array.

 

I inject all that stuff in a vertexData object.

 

I get a mesh reflecting light on one side.

 

 

Now I want it to reflect light the other side.

 

I keep my positions as they are, as I don't need new vertices for this mesh.

I create a new indices array called indicesVerso. I populate it by creating my nice tiny triangles the opposite rotation direction from the one I used for indicesRecto. Trust me, it works.

I compute normals with the original unchanged positions and this indicesVerso array.

I populate so my normalsVerso array.

 

I check : I inject positions, indicesVerso and normalsVerso in the vertexData instead of Recto stuff.

I then get a mesh reflecting the light the other side. Perfect.

 

So, now I concat my indices and normals Recto and Verso arrays.

normals = normalsRecto.concat(normalsVerso);indices = indicesRecto.concat(indicesVerso);

You get it ?

ok, check : my normals and indices arrays are well twice as long as Recto and Verso relatives.

 

Then, I inject these positions, indices and normals in my vertexData object.

 

But I only get the light reflecting on the first face :(

If I swap Recto and Verso in the concat(), then light reflects the other side only.

 

What do I do wrong ?

I just can't get it :(

 

Or is it again some vertex reuse issue (I kept my original positions but created two "opposite-wise" triangles on the same vertices triplet )?

In other terms, do I have to declare twice the same positions for the computeNormals() method ?

I just couldn't find out what could be wrong after reading this method github code :(

Link to comment
Share on other sites

Double sided meshes aren't supported. For meshes that need double sided the solution we use in 3DS Max is to use the "shell modifier" on the mesh parts that need to be double sided. Just set the inner and outer amounts to 0. in blender it's called "solidify"

 

3DS:

http://docs.autodesk.com/3DSMAX/15/ENU/3ds-Max-Help/index.html?url=files/GUID-E38BD284-3283-4311-AC29-3F699B2F04AA.htm,topicNumber=d30e96227

 

Blender:

http://archive.blender.org/development/release-logs/blender-256-beta/solidify-modifier/index.html

Link to comment
Share on other sites

I want to construct them thru code... not from an external soft. 

Because I want be able to have the double-side for maths generated meshes for instance.

 

Here, I just apply two layers of triangles oriented counter-wise on the same geometry.

Link to comment
Share on other sites

15' doing something else, chatting with my son about books, etc, in brief thinking to something else

Anyway, I won't tell my life here (did I ?  ;) )

 

And suddenly : "EUREKA !"

 

I guess it is exactly the same problem as for UVs : there is ONE normal per vertex used the indices array.

I reuse vertices... so can't associate more than ONE normal with a given vertex.

 

Pffff, solving a problem by finding it can't be solved is not that glorious.  :P

 

In brief, as for UVs, vertices are to be declared twice in the positions array if we want 2 normals on each vertex (one for each face) imo.

 

Just wasting my time in optimizing algos to reuse vertices, pfffff  :angry:

Link to comment
Share on other sites

hmmm but somehow it is done here:
http://mrdoob.github.io/three.js/examples/webgl_performance_doublesided.html
maybe it's just emulating the effect?

*edit
the doubleside using that has been depreciated and now its just side (but can still do both sides)

.side

Defines which of the face sides will be rendered - front, back or both.
Default is THREE.FrontSide. Other options are THREE.BackSide and THREE.DoubleSide.

If it can be done maybe there is also a solution for your UV problem?
Link to comment
Share on other sites

yep they did it...

Before hacking their code, I still want to find out a way by myself : self pride, you know ;) and sometimes other simplier ideas than threejs community ones. ex : my tubeMesh doesn't use any Fernet formulas, so far more light in terms of computation and it is an acceptable tube, imho ... the lone I take into account :P

 

mmh.. for now, I'm thinking about a lazy way to achieve it (still not working) :

 

I construct my single sided mesh as usual :

populating the positions array, populating the indices array, then populating with computeNormals() the normals array.

 

Now, before injecting these three array in the vertexData object, I attempt to "duplicate" this representation on itself :

    BABYLON.VertexData.ComputeNormals(positions, indices, normals);    positions = positions.concat(positions);    indices = indices.concat(indices);    var ln = normals.length;    for (var i = 0; i < ln; i++) {      normals.push( -1 * normals[i] );    }

As you can see, here I just replicate each position, each indice in their corresponding arrays. I declare them twice, no reuse at all.

I just set the normals as opposite (negative) from the original computed ones, for the light reflection on the second face.

Then only I inject them in the vertexData object.

Very lazy, isn't it ?

and no normals big recomputation for the second side, just setting as opposite.

 

Still not working... Light reflects on the first side only.

 

And I guess why this time ;)

When replicating each indice, I didn't change what they reference. So added indices still reference original vertices only, not added ones.

Trying to fix it

Link to comment
Share on other sites

Let's go,

I change references to positions in the added indices :

    BABYLON.VertexData.ComputeNormals(positions, indices, normals);    var lp = positions.length / 3;    positions = positions.concat(positions);    var li = indices.length;    for (var i = 0; i < li; i++) {      indices.push(indices[i]+lp);    }    var ln = normals.length;    for (var i = 0; i < ln; i++) {      normals.push( -1 * normals[i] );    }

aaarggg :o

Now I get the reflection only on the back side :( (good point : the negative normals work fine)

Yet, I have positions declared twice, indices refering no reused vertices (all positions), and normals set for both sides

Still digging ;) ...

Link to comment
Share on other sites

I really feel stuck in this problem

 

Okay, maybe my normals lazy computation is not orthodox enough, so I comment it out and now use the ol'good computeNormals().

Okay, Okay, maybe just replicating the indices is not enough because the tiny replicated triangles are the same orientation as the ones on the first side. So I explicitly recontrusted inverted tiny triangles with the opposite orientation : http://www.babylonjs-playground.com/#K6SNA#5

 

line 68 : computeNormals() instead of negative normals

 

line 61 : inverted tiny triangles

 

I even use two different indice arrays : indices1 for right face, indices2 for back face.

I have now replicated positions, indices in two different arrays and will compute the normals with the usual computeNormals() method.

 

Thus line 66 if you switch indices = indices1; or indices = indices2; and re-run, you will see either  the right face, either the back face reflecting the light.

You can then check the visualized normals are the right enlighted side each time.

 

So if you uncomment line 67 :

//indices = indices.concat(indices2);

you would expect (I would !) both faces to reflect the light, wouldn't you ?

But : http://www.babylonjs-playground.com/#K6SNA#6

:(

(the same not wireframed : http://www.babylonjs-playground.com/#K6SNA#7 )

 

As you can see, normals are right both sides, but only the back face reflects the light.

Just can't get why ...

 

Any help or tip welcome  :)

Link to comment
Share on other sites

It's getting weirder and weirder

 

http://www.babylonjs-playground.com/#K6SNA#8

 

line 58 : instead of concatening positions to themselves to replicate them, here I copy/cut the same for loop (line 39) populating the initial positions array. As you can see, I move the back face of +2 on the z-axis.

What's happening then ? both faces reflect the light !!!

 

Ok, let's stick them back (no +2 on z-axis) : http://www.babylonjs-playground.com/#K6SNA#9  (still line 58)

Only the back face reflects the light ... :wacko:

 

Have a last look at line 70 : indices is the concatenation of indices1 with indices2

 

Let's switch them :

indices = indices2.concat(indices1);

 indices2 first, indices1 after, ok ?

tadaddaaannn : http://www.babylonjs-playground.com/#K6SNA#10

 

Now the first face reflects the light  :blink:

 

And if I unstick back the two faces (line 59 : +2 on z-axis ) : http://www.babylonjs-playground.com/#K6SNA#11

Both faces reflect the light again  :wacko:

 

What do I understand about all of this ..?

Not much.  :)

 

If my algo is not too wrong, when two opposite faces are merged (with opposite normals for each face), the light reflection takes into account only the "last" (*) normals to compute the face brightness.

 

(*) the last = the last declared when iterating the normals/indices arrays.

 

Has this something to do with BJS internal light computation or webGL limitations ?

Definetly no idea  :mellow:

 

I sadly have to admit I can't achieve this double-side feature by myself (tough for self pride  ;) ) and will probably will have to hack, if I succeed, how the threejs guys deal with this.

 

BTW, my lazy algo allow to easily switch yet between right/back reflection (just multiply normals by -1 with no recomputation) ... but no double-side reflection.

Link to comment
Share on other sites

What I understand for now about the doubleside value in threejs is that is not for having the light reflection on back face but rather to disable face visibility on plane meshes.... or something like that.

In brief, if right face is enabled, only the right face is visible, if back face enabled the only visible face is the back, and if doubleside, both faces are visible. Else you can't even see them !

Didn't read anything about back face light reflection ...  :huh:

 

It's quite different in BJS where all faces of a plane are always visible, but light reflection applies on the face carrying the normals.

 

Here is a quite unelegant workaround : http://www.babylonjs-playground.com/#K6SNA#12

two separated faces almost stuck, only distant from 0.001  ;)

 

Totally useless if you want to deal with unpredictable (math computed) shapes  :(

Link to comment
Share on other sites

It's getting weirder and weirder

 

http://www.babylonjs-playground.com/#K6SNA#8

 

line 58 : instead of concatening positions to themselves to replicate them, here I copy/cut the same for loop (line 39) populating the initial positions array. As you can see, I move the back face of +2 on the z-axis.

What's happening then ? both faces reflect the light !!!

 

Ok, let's stick them back (no +2 on z-axis) : http://www.babylonjs-playground.com/#K6SNA#9  (still line 58)

Only the back face reflects the light ... :wacko:

 

Have a last look at line 70 : indices is the concatenation of indices1 with indices2

 

Let's switch them :

indices = indices2.concat(indices1);
 indices2 first, indices1 after, ok ?

tadaddaaannn : http://www.babylonjs-playground.com/#K6SNA#10

 

Now the first face reflects the light  :blink:

 

And if I unstick back the two faces (line 59 : +2 on z-axis ) : http://www.babylonjs-playground.com/#K6SNA#11

Both faces reflect the light again  :wacko:

 

What do I understand about all of this ..?

Not much.  :)

 

If my algo is not too wrong, when two opposite faces are merged (with opposite normals for each face), the light reflection takes into account only the "last" (*) normals to compute the face brightness.

 

(*) the last = the last declared when iterating the normals/indices arrays.

 

Has this something to do with BJS internal light computation or webGL limitations ?

Definetly no idea  :mellow:

 

I sadly have to admit I can't achieve this double-side feature by myself (tough for self pride  ;) ) and will probably will have to hack, if I succeed, how the threejs guys deal with this.

 

BTW, my lazy algo allow to easily switch yet between right/back reflection (just multiply normals by -1 with no recomputation) ... but no double-side reflection.

 

Because you've turned off backface culling and both surfaces are being drawn at the same position, and hence the same depth, what you are seeing is a special case of z-fighting. That is to say that surfaces are not getting "merged", what you are seeing is due to the zbuffer not being able to cull the face you don't want to see. Whether the first or the last mesh drawn to a pixel that "wins" and is seen would depend on the depth test mode used.

I think you just need to not disable backface culling... ie. comment out line 21 or change it to say like:

mat.backFaceCulling = true; //don't disable backface culling, otherwise we will get zfighting from our front and back sufaces

And then make sure you change the order of the vertices on the other side of the mesh so the winding order will handle culling the unseen side eliminating the zfighting issues we saw when we draw front and back geometry together.

Link to comment
Share on other sites

Chg, you make my day !!!!

 

Listen, readers, I get back the first un-working example : http://www.babylonjs-playground.com/#K6SNA#6

 

And now, I just turn on backfaceculling (line 21) and taaddadddannnnnnn : http://www.babylonjs-playground.com/#K6SNA#13

Double-side reflecting !  :)

 

Thank you again Chg !

I will implement this lazy algo as a paramater feature for ribbons.

Link to comment
Share on other sites

line 72 : More lazy.

ComputeNormals() is used once (l 53) only for the first face, then if double-sided, the other face normals are just replicated and made negative for this second face.

Works well : http://www.babylonjs-playground.com/#K6SNA#14

:)

 

I think this kind of feature could be extended to any mesh : rightface, backface, doubleSide (disabling backfaceculling), so users could have their cam inside a big cube or sphere with light reflection computed for instance, etc

Link to comment
Share on other sites

That reminds me: another approach, which would allow you to avoid duplicate geometry, would be to draw the mesh once with the standard material and back face culling, and then switch the mesh to use a modified version of the material with a shader that inverts normals and switch to frontface culling and then draw the original mesh again (to render the other side)...

Link to comment
Share on other sites

dynamically ?

nice idea  ;)

 

but what if the POV changes (cam moving, mesh rotating, pov entering/exiting the mesh volume for a closed mesh) ? if in the same time, the cam sould see each face (back/right) of the same mesh (imagine a curved plane, etc) ?

 

btw, my lazy algo could invert normals on the geometry with no vertex replication, it works, I tested it.

No need for vertex replication, if we just need to switch front/back face for light reflection indeed.

 

I need to replicate vertices just for carrying pairs of normals, for each side, if the mesh must reflect the light on both faces simultaneously.

Link to comment
Share on other sites

The idea of doing the normal inversion in the material's shaders is more for speed/efficiency, as inverting the already calculated normals in the vertex shader should be really cheap (you could invert in the fragment/pixel shader, but that'd be slightly less efficient though likely still much fast that doing it in JS I suspect). You also may (may not) get a boost from reusing the already uploaded vertices without modifying them, as if your lucky the implementation might see you haven't changed the data and avoid uploading it to the GPU / GPU memory on the second pass.

 

But you're right, the technique would require you to implement in a render function that gets called each time the scene is rendered (I presume Babylon.js has this, I haven't actually played with Babylon at all to be honest, so I don't actually know how handles things like mirrors using render targets? You'd have to have a render callback for this to work robustly...)

 

Anyway though, the technique described should handle curved or U shaped meshes just fine, and rotation shouldn't be an issue either presuming you are working in object space not world space, and applying a transform to rotate the object. You only have to recalculate the normals manually when the shape of the mesh changes, eg. the ribbon unfurls

 

 

EDIT: This should really be a Babylon.js I suspect, I actually made the first step of looking at the API, it looks like the 2 sided property would need to be added to SubMesh maybe? Along with a property for a 2nd material for the other side of the surface...

Link to comment
Share on other sites

yep they did it...

Before hacking their code, I still want to find out a way by myself 

but if you know it's possible you have no excuse not to be able to do it :P

I work that way too. It's how I have ended up here. That, and I believe in doing things the utmost best way. I am pretty much a novice here, but did take a massive sacrifice to be here, and I am not the kind of guy to do that unless I see a proper future in it. 

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...