Search Unity

Mask dependent sprite rendering

Discussion in '2D' started by emilianop, Sep 12, 2014.

  1. emilianop

    emilianop

    Joined:
    Aug 16, 2014
    Posts:
    28
    Hi all,

    this might be probably quite trivial, but I'm not sure what'ss the best way of doing it so I'll ask here.
    I have a iso-orthographic setup, where a set of sprites move on top of a fixed background (it's all 2D).
    Background and sprites are on different layers, but this can be changed in case it's not optimal.
    Sprites can move "behind" other sprites with the usual y-coord-to-depth trick, where collision detection happens only at feet level of each character while they get z-sorted based on their Y coord.
    It's basically like any old setup from the 90ies=> http://www.gamefabrique.com/storage/screenshots/snes/king-of-dragons-02.png

    Now the questions:

    1- I want to have environment elements that are part of the background texture, but considered by the engine to be much further in front. The optimal way, workflow-wise, would be to paint a secondary texture, aligned with the background one, representing per-pixel depth, and use that one to drive sprite sorting and/or visibility. (basically imagine that in that screenshot the sprites might be able to "pass behind the mast, while being in front of a skeleton that's also behind the mast"

    2- as a consequence of #1, I'd need to be sure that the depth sorted by using this texture should be factored into the default sprite to sprite depth-sorting, so they all fall in place consistently

    All this is to avoid for the extra work of extracting foreground elements from backgrounds and making them into sprites on a fore layer.

    I'm not sure it needs to be true zdepth sorting or just masking or culling, I just need for that part for the sprite "to disappear" in favour of the section of background considered to be in front of it.
    I'm assuming this needs a dedicated shader in any case.

    Hope it makes sense, cheers,
    e
     
  2. Freezy

    Freezy

    Joined:
    Jul 15, 2012
    Posts:
    234
    http://docs.unity3d.com/Manual/SL-Blend.html
    The documentation beats around the bush a bit, just keep in mind the following:
    Blend SrcFactor DstFactor
    Blend RenderingImage DestinationOnScreen

    Blend in the end decided the two halves of a pixel then adds them together.

    If you render everything else first, then you can later add on the background.
    Or you could render the background first and the rest later on with a seperate shader.

    Use the debug alpha scene view, (in the top of the scene view, find textured and the RGB, change RGB to alpha). You can check out the in between results.

    Set the alpha to either 0 or 1 and you got yourself a nice mask.
    You could even do gradients, but it would do actual blending and might cause clipping issues with sprites.

    If you make it so that 0 alpha is background is in front, then using OneMinusDstAlpha as the DstFactor would decide how much the background would add to it's half of the final pixel.

    Blend SrcAlpha OneMinusDstAlpha
    Would give you an alpha blended mess, as the two pixels would merge to form the final result.

    What you would then need is a two pass approach, where you collect the bits of the background where the sprite is not allowed to draw.

    As this would result in a double drawcall and additional fancy shading and shader code.
    The easier and more perfomance friendly solution would be to just set the background up twice.
    The same texture is used with one diffuse (background) and the other transparant (foreground).
    The sprites render in between.
    Set up the background with transparancy 0 (black) for background, 1 (white) for front.
    The transparant shader would still draw all those 0 alpha pixels, but the result would be identical to what was there before.

    I hope this helps.
     
  3. emilianop

    emilianop

    Joined:
    Aug 16, 2014
    Posts:
    28
    That's great, thanks Freezy.

    Some more question: I thought that blending was basically the very last part of the shading pipe, meaning everything would get computed first, then discarded when you'd blend with alpha=0. Would that degrade performance?

    Also, in my case it's not just a matter of "either in front or behind". If I place a column in the middle of the screen, say with depth 0.5, then whenever my character has depth 1 should go behind, when it's less than 0.5 in front (or the other way around, you get the idea). So i need to somehow pull pixels value from the background's depth mask and compare it with the same depth of that pixel on the character after I had re-set it to the normal depth.

    I'll follow your instruction and see what I can come up with, thanks again!
     
  4. Freezy

    Freezy

    Joined:
    Jul 15, 2012
    Posts:
    234
    Blending pixels with alpha 0 basically disregards the color information of that side of the blend function.

    I think I understand what you are looking for now. A sprite that is able to move 'between' different layers of the background, without having to manually slice up the background into actual layers.
    Gradients would be hard to do render correctly here, but still possible with a bit of math.

    Unfortunately there is no way to actually 'depth' test a pixel, as it is using the depth of it's 3D object (like a simple quad or plane). So reading the alpha into a separate layer then using that layer to test the other alpha against is the only viable way I can think of (not a really big shader guru here, i understand about half of the stuff I'm trying to explain here ;-).

    Using the stencil buffer might also be a possible solution here as you could easily render the alpha into the stencil, then check each pixel if it should render. Not sure if it would be good for mobile performance, either way worth doing some tests with.
    http://docs.unity3d.com/Manual/SL-Stencil.html

    Premature optimization might not be worth it, depending on the needs of the application (are you going to use this method for 20 or 20.000 of something).
    My recommendation is to just try stuff out. If you need more FPS, then you could try to find alternative methods.
     
  5. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    maybe this helpful?
    youtu.be/HM17mAmLd7k?t=33m25s
     
  6. Freezy

    Freezy

    Joined:
    Jul 15, 2012
    Posts:
    234
    this would require slicing the background into individual sprites. then controlling their 'depth' by setting sorting order value.