Jump to content

Transparency & alpha issues


acarvallo
 Share

Recommended Posts

Hello Babylon.js people,

I am trying to make a viewer for working with BIM stuff (building and all the stuff inside), so there can be quite a lot of objects and having each one in its own mesh isn't really an option.

So I tried to use two SPS, a single mesh and two meshes, one mesh/sps for opaque objects and another for transparent ones (saw the idea in another post on the forum).

Thing is I need to "highlight", hide and have transparent not pickable objects too.

Here is what I have for now :

image.png.dfb34028d24d1b1c10d20874ec005b85.png

There is a space (big transparent blue-ish), with objects in it (yellow-ish opaque ones in the front) and also transparent grey "boxes".

To be able to switch objects visibility/color quickly I tried to put all objects in both meshes :

  • opaque ones hidden in the transparent mesh
  • transparent ones hidden in the opaque mesh

Then just toggle visibility, but then this kind of thing can happen :

image.png.31578c9168736473606b45f37a0debbf.png

The front box is now transparent with a red color, the other "yellow-ish" box is partly hidden with a red tint (that's correct), but the grey opaque box isn't hidden/tinted (that's not ok) at all and keeps its grey color, looking like it is closer to the camera while it is actually not.

I feel like I tried quite a lot of things and combinations so I may have missed the working one.

 

I also posted an issue trying to have a simple example to demonstrate what felt like a bug : https://github.com/BabylonJS/Babylon.js/issues/2832

As I wrote there, using needDepthPrePass kind of fixes it, but not really since it messes up transparent objects inside another transparent object.

Alpha mode is promising but the ghost/shiny effect is too powerful :

image.png.fbfa0c8ae572309b73d50957dd9aaa9f.png

I would need the behaviour, without the side effect...

Any help/suggestions is welcome, and if you have questions or need more details, feel free to ask :)

 

References :

https://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered

https://doc.babylonjs.com/resources/how_to_use_blend_modes

 

Edited by acarvallo
Update dead links (tutorials > resources)
Link to comment
Share on other sites

Well, I see more or less the idea (the ALPHA_PREMULTIPLIED isn't on the page about blend modes though), and I guess the scale() on the color is to counter the ghost/glow effect, produced by the ALPHA_PREMULTIPLIED :p

Side note, I didn't include in my first post, my objects can have different vertex colors, when I add an object, I create a blank mesh, assign a BABYLON.VertexData with positions, indices, normals, colors and then add that mesh to the SPS, setting computeParticleColor to false.

I multiplied each vertex color by its matching alpha though, and it is kind of working, here is the behaviour (a video is better than trying to explain with words) :

https://sendvid.com/0wsdq652

In short, it works if I start by highlighting one of the grey box behind.

Here is how I "highlight" an object :

    var colors = new Float32Array(vertexCount * 4);
    for (var j = 0; j < vertexCount; j++) {
        colors[j * 4 + 0] = color.r;
        colors[j * 4 + 1] = color.g;
        colors[j * 4 + 2] = color.b;
        colors[j * 4 + 3] = color.a;
    }

    SPST.mesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.ColorKind, colors, startIndex * 4 * 4);

    // If object is opaque
    if (obj.geometry.sps.name == "SPSO") {
        var pIndex = obj.geometry.particleIndex;
        SPST.particles[pIndex].isVisible = true;
        SPST.setParticles(pIndex, pIndex);
        SPSO.particles[pIndex].isVisible = false;
        SPSO.setParticles(pIndex, pIndex);
    }

And the "unhighlight" :

    // Omitted, colors is filled with original vertex colors for the matching object

    if (obj.geometry.sps.name == "SPSO") {
        var pIndex = obj.geometry.particleIndex;
        SPSO.particles[pIndex].isVisible = true;
        SPSO.setParticles(pIndex, pIndex);
        SPST.particles[pIndex].isVisible = false;
        SPST.setParticles(pIndex, pIndex);
    } else {
        obj.geometry.sps.mesh.geometry.updateVerticesDataDirectly(BABYLON.VertexBuffer.ColorKind, colors, startIndex * 4 * 4);
    }

So basically, the scene is always correct at first, it's just wrong when switching alpha/visibility, again thanks for any reply, and if you have a better way of doing what I want to achieve I'll be glad to read your suggestions :)

(Also replied in the issue)

Link to comment
Share on other sites

Well, it would appear that after trying a bunch of things I messed up :mellow:. I was basically doing this when adding meshes as particles in the SPS :

// If original mesh is transparent
if (shape.material.alpha < 1.0) {
    SPSO.particles[index].isVisible = false;
} else {
    SPST.particles[index].isVisible = true;
}

So opaque original meshes added resulted in both opaque & transparent particles being drawn, which leads to strange transparency issues.

Just changing previous code, replacing "SPST.particles[index].isVisible = true;" by "SPST.particles[index].isVisible = false;" fixes my issue, but create another one...

Now I just have a "blink" effect when I highlight opaque particles, like this : https://sendvid.com/ogdv46mn

And *sigh* I finally found the issue by debugging, it was because the bounding box/sphere was empty for the SPST in case (as it was in the video I linked just before) there are only opaque objects, I tried setting

SPST.computeBoundingBox = true;

But if I call the following function (refreshVisibleSize();) each time I switch visibility of a particle, it works !

SPST.refreshVisibleSize();

 

So sorry for my mistakes, thanks for the implication, I really appreciate it.

One more question, shouldn't setting computeBoundingBox to true refresh the bounding box or is it because I call setParticles(id, id) (so updating a single particle and not updating whole SPS's particles) ?

Link to comment
Share on other sites

Let's summarize :

- Adding a shape to the SPS only copies the shape geometry (vertices, indices, normals, uvs, colors). The shape material isn't copied. As the alpha is a property of the material of the shape, it's ignored by the SPS. That said, you can then set your own material to the SPS or play with each particle color/alpha (managing the particle color4 property and setting the SPS mesh property hasVertexAlpha to true)

- As its name says, the particle property .isVisible just makes the particle visible or not whaterver other considerations.

- SPS.computeBoundingBox = true jus enables to global SPS mesh BBox internal computation within the next call to setParticles() instead of having another side computation. This is faster and this is useful for SPS whose sizes evolves with the time (an explosion for instance). This is related to the global SPS mesh visibility/pickability relatively to the camera frustum, this has nothing to do with a given particle blink.

NOTE (maybe here's you answer) : when calling setParticle(id, id) the particle loop doesn't recompute everything but only the passed particle range (here only one : id), so the whole Bbox can't be rightly updated and would have an inconsistent value (maybe I could auto-disabled it here, not sure). A forced global computation will be needed with refreshVisibleSize(). You could also disable before the property computeBoundingBox so its last computed value is kept or set a fixed value if the SPS size doesn't change, instead of calling refreshVisibleSize().

- SPS.refreshVisibleSize() forces the immediate the SPS global mesh Bbox recomputation. This is an expensive operation, not needed when using computeBoundingBox. This is useful to compute once the Bbox of a SPS that reached its maximum size and won't evolve then, after some particle init for instance (and thus to disable computeBoundingBox). As formerly, this concerns only the global vsibility relatively to the camera frustum and not a given particle.

Not sure to help.

A PG would be helpful.

In your video, the bug doesn't seem to be related to some SPS visibility (so BBox) issue, because only one particle blinks at once. I would say it has something to do with the call to setParticles(i, i) .... just guessing. Do you have the same effect by calling simply setParticles() ?

.

Link to comment
Share on other sites

  • 2 weeks later...

Hello (sorry for the late reply, been a bit busy),

First, finally was able to put part of my viewer in a PG: https://playground.babylonjs.com/#MNZ2VS

Found what was going wrong again, before starting the render loop I was copying the SPS (without their meshes being built), and building the mesh of the copy (but the copy was dropping get/set prototypes...), so setting computeBoundingBox did nothing (not setting the actual _computeBoundingBox attribute).

So all is good on Babylon's side regarding bounding box stuff, and what is depending on it (ray/intersect).

It is now working well with my 2 SPS (but is not optimal for performance).

Then I wanted to try with only a single one (having both opaque & transparent objects in it), using forceDepthWrite=true on the material used for the SPS seems to do wonderful !

Only thing is that for some cases (one opaque object in front of another), setting the front one as transparent won't make the back one visible at all (see the PG for that).

Also, as of now I feel like the separateCullingPass property of a material does nothing, I can't really spot a change when setting its value to false in the PG of that comment : https://github.com/BabylonJS/Babylon.js/issues/2832#issuecomment-331647665

Link to comment
Share on other sites

Hi,

Yes the one from the issue, when you specifically mentioned that it could be used to maybe solve my problem : https://www.babylonjs-playground.com/#JB6H3P#5

As I also said, forceDepthWrite really helps but is not perfect for my real usage example, and setting separateCullingPass doesn't seem to change it as well : https://playground.babylonjs.com/#MNZ2VS#1

Link to comment
Share on other sites

Yeah unfortunately I see no easy way to deal with a SPS here

With separated meshes, this will not be a problem at all but inside the same SPS it could be complex.

So why not consider that the SPS is not transparent and when you want to highlight a piece of it, you just need to hide it and instead display a transparent mesh. So the SPS is not transparent and the transparent mesh will be correctly displayed

 

(Does it make sense?)

Link to comment
Share on other sites

It totally makes sense, unfortunately the PG is just a part of my geometry, I can have a lot of objects both transparent and opaque for their initial state.

I used a SPS because it makes picking easier thanks to particles/pickedParticles, but the thing I'm really looking for is performance, may it be using a SPS, a regular mesh or something else.

Maybe you have another idea or magic property (like forceDepthWrite) in your bag of knowledge that could help ?

 

Again, thanks for the answers !

Link to comment
Share on other sites

The problem we are trying to face here is clearly the need for face sorting. It is a really old problem in realtime 3d. and there is no good solution so far.

The sps is only one mesh so you cannot have face sorting at all.

Perhaps you could consider having 2 sps: one solid and one transparent?. The transparent one could be set to use forceDepthWrite and separateCullingPass

Link to comment
Share on other sites

Maybe this will help for the SPS : https://github.com/BabylonJS/Babylon.js/pull/2961

// create a particle depth sort enabled SPS
var sps = new BABYLON.SolidParticleSystem("sps", scene, {enableDepthSort: true});
// then later only ...
sps.setParticles();   // and the particle are depth sorted each call
// We can skip the sorting at any time (or reactive it) : sps and camera not moving anymore
sps.depthSortParticles = false;  // true by default when enableDepthSort is set to true

 

Documentation and PG to come

Link to comment
Share on other sites

Let's start with a simple PG, a SPS populated with transparent solid particles : http://playground.babylonjs.com/#EPBTB7#3

As you can notice when rotating the camera, because the SPS is a standard mesh and because transparent standard meshes don't sort their faces according to the camera position for performance reasons, the transparent particles are weirdly rendered from some points of view. This is unfortunately the expected behaviour when dealing with "auto-transparency" (transparency applied to itself).

Now the SPS provides a new feature called the particle depth sort : http://playground.babylonjs.com/#EPBTB7#2

The internal SPS geometry is recomputed each frame so the most distant particles are drawn before the closest ones.

Note this process is CPU intensive so it's done only at the particle level and not at the facet level.

Note also that, for now, the distance between the camera global position and the particle position is computed only in the SPS local space. This means that if the SPS mesh is scaled, translated or rotated, this distance is no longer pertinent. So, for now, it's better to set up your SPS internal particle positions, rotations, etc, like these were World ones and to let the SPS unrotated, unscaled at the origin (it's designed to do this very well actually).

I need to think about the opportunity to add the SPS global transformation (translation, rotation, scaling) to the current particle location to get a septh sort working in every case. It would be much more computation, so not sure it's worh it.

[EDIT] I just did a PR fixing this at a decent performance. Now the SPS can be rotated or translated and the particle will keep sorted from the camera as expected.

Link to comment
Share on other sites

  • 5 months later...

Hm, I should have activated notifications, could have answered earlier :|

So,  thanks @jerome for the SPS particle depth sort, but it does nothing in my case due to all my particles's position being Zero (only using position buffer from the original mesh for each particle).

And you're absolutely right about having 2 SPS @Deltakosh, it's indeed what I have in my actual project, but I can't seem to have a perfect (or decent) looking render.

Here is a playground to see what's going on : https://playground.babylonjs.com/#BQLAVV#2

And 2 screenshots, camera looking from one side, then the opposite one : 

image.thumb.png.f62146c91ebd38e3b74c2964537c92a9.png

I would basically like to have the render on the right be of the same type as the one on the left.

Tried to play a bit with depth/culling options and alphaMode (as you can see starting from line 48), but I couldn't find a really good combination.

 

Link to comment
Share on other sites

Hi, thanks for the link, I wasn't aware of this feature !

Updated PG : https://playground.babylonjs.com/#BQLAVV#3

It's for sure better, but there still seems to be some artifacts :

image.png.e18b6da7636ceb8abb3027c25ac95319.png

It's strange because they should all be similar elements,  so I would think they will render the same. I guess it's because the faces touching each other have the same positions.

Not sure if there is much more doable with facets data, another possible option could be to depth sort particles, but use, for example, the center of the bounding box of each particle instead of its position.

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