- Home /
2D sprites in a 3D environment - undistorted without clipping
So I'm trying to create an environment with a 2D sprite in a 3D, perspective environment.
The issue I've come across is that 2D sprites drawn as flat textured quads in the scene become distorted. To solve this, I have a simple line of code that rotates the sprite quad to face the camera plane:
sprite.LookAt(Vector3.ProjectOnPlane(transform.position + Camera.main.transform.position, Camera.main.transform.forward) - Camera.main.transform.position);
This works, but produces an issue with the depth buffer. The sprite appears to be flat on the ground to the viewer, but is in fact leaning backwards at a fairly steep angle. This causes the sprite to clip through the geometry, as seen in this image:
The obvious solution to this issue is to stretch the sprite rather than rotate it, in order to counteract the perspective projection, but this also has problems, in that the stretched sprite would inevitably be extremely tall, and the environment may contain overlapping layers, which could easily clip through the stretched sprite.
My attempts so far have been to disable the depth buffer and force transparent rendering, but neither of these have made any difference whatsoever. The ideal solutions for me would be to either:
Force the sprite to be sorted at the object level rather than per pixel, including shadow casting.
Use a custom shader that produces custom calculations for the depth buffer, including shadow casting.
Directly draw the sprite programatically at a specified depth.
Any of these solutions would produce what I'm looking for, but there may be others I haven't considered. Can anyone help with this?
I suppose you could rewrite the default Sprite shader to be aligned with the view plane. Replace the commented line with the new code. As explained in this billboard shader article.
//OUT.vertex = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, IN.vertex);
OUT.vertex = mul(UNITY_$$anonymous$$ATRIX_P,
mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$V, float4(0.0, 0.0, 0.0, 1.0))
+ float4(IN.vertex.x, IN.vertex.y, 0.0, 0.0));
Actually, shadows won't work on Sprites, so you'll need take that into account. $$anonymous$$aybe try that code snippet in a Standard shader ins$$anonymous$$d.
Changing the vertices in the shader won't help the problem. If I move the vertices, all I end up with is exactly the same problem I have now. The issue is that in order to maintain the sprite proportions, I need to rotate or scale the sprite - i.e. move the vertices - in 3D space. This ends up with the sprite clipping through the geometry. Just moving that logic into the shader won't help that.
So I've been doing this in a shader and trying to write to the depth buffer. So far I've managed to write to the depth buffer using the float dep:DEPTH;
notation, but I can't seem to get the object depth working. When I do this:
o.dep=i.pos.z;
I get exactly what you see above (where i.pos
is the translated vertex location). However when I use:
o.dep=i.objpos.z;
Where objpos
is defined as:
float4 objpos : POSITION1;
objpos = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, float4(0, 0, 0, 1));
in the vertex shader, my sprite renders behind everything else in the scene.
$$anonymous$$ore info. I have deter$$anonymous$$ed that these two lines of code, absolutely adjacent to each other, produce different results:
struct v2f {
float4 pos : POSITION0;
float4 objpos : POSITION1;
};
o.pos = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, v.vertex);
o.objpos = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, v.vertex);
$$anonymous$$ore stuff! I've got the depth buffer sort of behaving by adding a constant offset to the depth of my sprite. This works well enough for my needs.
I'm now working on shadow casting and... well this is the best I can get:
http://i.imgur.com/$$anonymous$$iuRRY$$anonymous$$.png
As you can see, shadows are interacting with the cut out sections of the sprite, which is obviously undesirable. This effect is achieved by using a Fallback to an existing shader, which adds the shadow passes from that shader onto the custom passes. Unfortunately it seems I'm missing something vital in order for this to work properly.
Can you show how you managed to solve the clipping by "adding a constant offset to the depth". I'm dealing with the same issue.
Your answer
Follow this Question
Related Questions
clipping issue with sprites in fixed camera scene 0 Answers
Sprite clips into 3D environment; any way to render the sprite? 1 Answer
Inverse depth masking 0 Answers
Sprites rendered in the wrong order 1 Answer
Game view goes crazy (clear flags)? 1 Answer