Search Unity

Question Problem with clipping 2d semi-transparent meshes when ZWrite is on

Discussion in 'Shaders' started by hojjat-reyhane, Jul 7, 2021.

  1. hojjat-reyhane

    hojjat-reyhane

    Joined:
    Feb 4, 2014
    Posts:
    49
    Hi everyone,
    I a 2d game with semi-transparent units and showing them with quad meshes.
    Also I have to combine meshes to keep SetPass calls and number of batches as low as possible because I have thousands of units.
    My combined meshes are something like this, they are at different Z positions.

    Screenshot 2021-07-07 154654.png

    The problem appears when units with different materials are overlapping each other

    Screenshot 2021-07-07 155013.png

    When ZWrite is off: one mesh (which is lower) will always render in front.

    Screenshot 2021-07-07 155045.png

    But when ZWrite is on: some objects left behind and not rendered properly.

    Screenshot 2021-07-07 155333.png

    So I added this line to my shader fragment
    clip(col.a - 1.0 / 255.0);

    and here is the result:

    Screenshot 2021-07-07 155548.png

    As you can see, some objects behind transparent parts aren't rendered correctly.
    Using the same material is not an option because each unit has a big animated sprite sheet and I have lots of units.

    Can someone please help me fix this problem?
    I'm struggling with this about 2 months.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,336
    You need to sort the order of the quads before combining them. The z depth is irrelevant for transparent meshes when rendering as a single mesh. When they're separate meshes, Unity is sorting them for you before drawing, but if you combine them you have to sort them yourself manually.

    https://en.wikipedia.org/wiki/Painter's_algorithm

    The other option would be to abuse a particle system, or use sprites instead of quads. Both systems are able to batch (and sort) significantly more quads than Unity's dynamic batching system is capable of. And I'm guessing you resorted to manual sorting after you found the dynamic batching was only combining about 75 quads at a time. Both particle systems and sprites will happily batch up to around 4000 quads without blinking an eye. I think particle systems might even go higher these days, though the performance cost of batching that many quads together on the CPU and uploading the combined mesh to the GPU starts to outweighs the benefits and GPU instancing ... which particle systems can also help a bit with, can be a lot cheaper.


    Alpha blending an ZWrite are inherently incompatible outside of very limited situations, which is why you're having problems with that approach. The fix for that is to ... sort the quads before hand ... at which point you don't need ZWrite anymore.
     
  3. hojjat-reyhane

    hojjat-reyhane

    Joined:
    Feb 4, 2014
    Posts:
    49
    Thanks bgolus,
    The meshes of blue objects and red objects are not combined as they are using different materials.
    So I tested sorting meshes based on their z positions and then combine them, but it didn't help.

    Using a particle system or sprite is not the answer, because I have different materials.
    If I use separate meshes or sprites the SetPass calls and number of batches will increase when objects overlap each other.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,336
    This is because to get transparent surface to sort properly when rendering it requires they be pre-sorted and drawn in that order. It is not possible* to sort two transparent meshes that interweave. It's not even possible to sort a single mesh that interweaves without pre-sorting the triangles. And intersections aren't possible no matter what.

    See that link to The Painter's Algorithm again. That's how transparency sorting is accomplished for real time rendering. If you want transparent objects to sort, you have to render them in exactly the correct order and there really isn't a way around that.

    You'll need to not do that to fix this problem. They must use the same material, or you need to draw each quad separately. Those are your two options.

    If they're separate materials because you're setting a color value on the material, you'll need to use the mesh's vertex color, or use GPU instancing and make the color be an instanced property you set using a material property block. If you're using different textures, use a texture atlas or texture array and modify the mesh UVs or have the array index be an instanced property.


    * There do exist a number of techniques referred to as Order-Independent Transparency (OIT). Unity doesn't not support any of these out of the box. True accurate sorting is only possible using ray tracing, per-pixel linked list buffers, or an A-buffer. All of these options are considered too slow for real world use, though hardware accelerated ray tracing may make that an option at some point in the near-ish future. A-buffer and per-pixel linked lists require an unbounded amount of memory that increases exponentially with the number of overlapping surfaces. Then there's Depth peeling and Weighted Blended OIT. Depth peeling is 100% accurate, but only supports a fixed number of "layers", usually no more than 8, and beyond that becomes too expensive or falls back to what you currently see. Weighted Blended OIT is an approximate OIT method that works well for mostly transparent objects that have an unclear sorting order that fails with anything that approaches or reaches full opacity. Then there are stochastic / dithered opacity options where you use opaque rendering with a noisy alpha test to approximate transparency. All of these options are dead ends for what you're trying to do.
     
    hojjat-reyhane likes this.
  5. hojjat-reyhane

    hojjat-reyhane

    Joined:
    Feb 4, 2014
    Posts:
    49
    Thanks again.
    These blue and red objects are not my real assets.

    I have about 30 sprite sheets (1024×1024).
    I'm combining the meshes to decrease the number of batches and SetPass calls.
    So I think there is no solution for this.
    I have to choose between high performance or good looking game.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,336
    There is another option. Switch to the URP.

    The SRP Batcher gets near batching level performance without needing to combine meshes. Individual draw calls are just way, way faster.
     
    hojjat-reyhane likes this.
  7. hojjat-reyhane

    hojjat-reyhane

    Joined:
    Feb 4, 2014
    Posts:
    49
    Yes maybe it work! I have to test it.
    I will post the result if I tested it.