Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by NosTurtle · Mar 20, 2020 at 03:54 AM · 2dshaders2d spritescameras2.5d

Render entire sprite in 3D but with depth according to position.

Hello there.

This question is hard to describe in a title, so I will show it with pictures. I am making a game that looks like a classic rpg, with sprites for characters but rendered in 3D. I am using an orthographic camera at a 45-degree angle & am making the sprites point to the camera as to not distort them, but as you can see, they clip into the 3D geometry.

I now this is just how a sprite renderer works in 3D, but I know shaders can do some pretty cool things. Is there some technique, shader or even research I can look into to fix this? I am wondering if it is possible to render the entire sprite with the 3D depth from it's position to the camera so that it will appear in front of whatever it clips into, but still behind objects when they are behind?

Thanks in advance. (Pokemon sprites just for prototyping)

alt text alt text

1.png (231.4 kB)
2.png (257.5 kB)
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
1
Best Answer

Answer by Namey5 · Mar 20, 2020 at 10:25 AM

Set your sprite upright and try this shader;

 // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
 
 Shader "Sprites/Isometric"
 {
     Properties
     {
         [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
         _Color ("Tint", Color) = (1,1,1,1)
         _ViewAngle ("Camera View Angle", Float) = 45.0
         [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
         [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
         [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
         [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
         [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
     }
 
     SubShader
     {
         Tags
         {
             "Queue"="Transparent"
             "IgnoreProjector"="True"
             "RenderType"="Transparent"
             "PreviewType"="Plane"
             "CanUseSpriteAtlas"="True"
         }
 
         Cull Off
         Lighting Off
         ZWrite Off
         //ZTest Always
         Blend One OneMinusSrcAlpha
 
         Pass
         {
             CGPROGRAM
             #pragma vertex SpriteVert
             #pragma fragment SpriteFrag
 
             #pragma target 2.0
 
             #pragma multi_compile_instancing
             #pragma multi_compile_local _ PIXELSNAP_ON
             #pragma multi_compile _ ETC1_EXTERNAL_ALPHA
             
             #include "UnityCG.cginc"
 
             #ifdef UNITY_INSTANCING_ENABLED
 
                 UNITY_INSTANCING_BUFFER_START(PerDrawSprite)
                     // SpriteRenderer.Color while Non-Batched/Instanced.
                     UNITY_DEFINE_INSTANCED_PROP(fixed4, unity_SpriteRendererColorArray)
                     // this could be smaller but that's how bit each entry is regardless of type
                     UNITY_DEFINE_INSTANCED_PROP(fixed2, unity_SpriteFlipArray)
                 UNITY_INSTANCING_BUFFER_END(PerDrawSprite)
 
                 #define _RendererColor  UNITY_ACCESS_INSTANCED_PROP(PerDrawSprite, unity_SpriteRendererColorArray)
                 #define _Flip           UNITY_ACCESS_INSTANCED_PROP(PerDrawSprite, unity_SpriteFlipArray)
 
             #endif // instancing
 
             CBUFFER_START(UnityPerDrawSprite)
             #ifndef UNITY_INSTANCING_ENABLED
                 fixed4 _RendererColor;
                 fixed2 _Flip;
             #endif
                 float _EnableExternalAlpha;
             CBUFFER_END
 
             // Material Color.
             fixed4 _Color;
 
             struct appdata_t
             {
                 float4 vertex   : POSITION;
                 float4 color    : COLOR;
                 float2 texcoord : TEXCOORD0;
                 UNITY_VERTEX_INPUT_INSTANCE_ID
             };
 
             struct v2f
             {
                 float4 vertex   : SV_POSITION;
                 fixed4 color    : COLOR;
                 float2 texcoord : TEXCOORD0;
                 UNITY_VERTEX_OUTPUT_STEREO
             };
 
             inline float4 UnityFlipSprite(in float3 pos, in fixed2 flip)
             {
                 return float4(pos.xy * flip, pos.z, 1.0);
             }
 
             half _ViewAngle;
 
             v2f SpriteVert(appdata_t IN)
             {
                 v2f OUT;
 
                 UNITY_SETUP_INSTANCE_ID (IN);
                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
 
                 float3 worldPos = mul (unity_ObjectToWorld, float4 (IN.vertex.xyz, 1)).xyz;
                 float3 origin = mul (unity_ObjectToWorld, float4 (0,0,0,1)).xyz;
                 worldPos.y -= origin.y;
                 const float scale = 1.0 / cos (radians (_ViewAngle));
                 worldPos.y = (worldPos.y * scale) + origin.y;
                 IN.vertex.xyz = mul (unity_WorldToObject, float4 (worldPos, 1)).xyz;
 
                 OUT.vertex = UnityFlipSprite(IN.vertex, _Flip);
                 OUT.vertex = UnityObjectToClipPos(OUT.vertex);
                 OUT.texcoord = IN.texcoord;
                 OUT.color = IN.color * _Color * _RendererColor;
 
                 #ifdef PIXELSNAP_ON
                 OUT.vertex = UnityPixelSnap (OUT.vertex);
                 #endif
 
                 return OUT;
             }
 
             sampler2D _MainTex;
             sampler2D _AlphaTex;
 
             fixed4 SampleSpriteTexture (float2 uv)
             {
                 fixed4 color = tex2D (_MainTex, uv);
 
             #if ETC1_EXTERNAL_ALPHA
                 fixed4 alpha = tex2D (_AlphaTex, uv);
                 color.a = lerp (color.a, alpha.r, _EnableExternalAlpha);
             #endif
 
                 return color;
             }
 
             fixed4 SpriteFrag(v2f IN) : SV_Target
             {
                 fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
                 c.rgb *= c.a;
                 return c;
             }
             ENDCG
         }
     }
 }

Initially, my idea was to warp the mesh straight in the shader and handle occlusion manually with the depth texture, but I soon realised that all that is unnecessary. Instead, you can keep your sprites standing up straight (meaning occlusion works fine), but because this is an orthographic camera with a fixed angle of 45 degrees we can just scale the sprite directly in the shader using some trigonometry. The great thing about this technique is that everything just works, and the camera doesn't have to be at an angle of 45 degrees; so long as you pass the camera's view angle (which I've exposed as a material parameter) into the shader it will work for anything.

Comment
Add comment · Show 8 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image NosTurtle · Mar 22, 2020 at 12:52 AM 0
Share

Thank you so much for replying! Your solution mirrors that of solutions I have later found by changing my searching, but in a much cleaner way. This technique of scaling an upright sprite works well enough for most foreseeable use cases, however, there is an issue of wanting to take advantage of a 3D environment that the taller than normal sprite may interfere with what would otherwise be perfectly fine geometry. For example, going under what would normally be a high enough bridge now clips the character through it, even though he isn't that tall (seen in the first 2 pictures). alt text alt text

I sincerely appreciate your help. Right now I am just prototyping to feel out my limitations, so this may very well what I work with, but an ability to properly display a sprite in 3D without clipping would be ideal. An example of what I'd like to achieve would be like what is found at the bottom of this post link text) :

1.png (247.4 kB)
2.png (246.9 kB)
avatar image Namey5 NosTurtle · Mar 22, 2020 at 01:35 AM 0
Share

Ah, that's an edge case I didn't think about. In that case, my original plan should solve that issue, although to make things easier I'll combine the two approaches so you don't have to do much;

 struct v2f
 {
     float4 vertex   : SV_POSITION;
     fixed4 color    : COLOR;
     float2 texcoord : TEXCOORD0;
     float4 screenPos : TEXCOORD1;
     UNITY_VERTEX_OUTPUT_STEREO
 };
 
 ...
 
 v2f SpriteVert(appdata_t IN)
 {
     v2f OUT;
 
     UNITY_SETUP_INSTANCE_ID (IN);
     UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
 
     OUT.screenPos.z = -mul (UNITY_$$anonymous$$ATRIX_$$anonymous$$V, float4 (IN.vertex.xyz, 1)).z;
 
     float3 worldPos = mul (unity_ObjectToWorld, float4 (IN.vertex.xyz, 1)).xyz;
     float3 origin = mul (unity_ObjectToWorld, float4 (0,0,0,1)).xyz;
     worldPos.y -= origin.y;
     const float scale = 1.0 / cos (radians (_ViewAngle));
     worldPos.y = (worldPos.y * scale) + origin.y;
     IN.vertex.xyz = mul (unity_WorldToObject, float4 (worldPos, 1)).xyz;
                 
     OUT.screenPos.xyw = UnityObjectToClipPos (IN.vertex).xyw;
     OUT.screenPos.xy = OUT.screenPos.xy * 0.5 + OUT.screenPos.w * 0.5;
     #if UNITY_UV_STARTS_AT_TOP
         OUT.screenPos.y = OUT.screenPos.w - OUT.screenPos.y;
     #endif
 
     OUT.vertex = UnityFlipSprite(IN.vertex, _Flip);
     OUT.vertex = UnityObjectToClipPos(OUT.vertex);
     OUT.texcoord = IN.texcoord;
     OUT.color = IN.color * _Color * _RendererColor;
 
     #ifdef PIXELSNAP_ON
     OUT.vertex = UnityPixelSnap (OUT.vertex);
     #endif
 
     return OUT;
 }
 
 sampler2D _$$anonymous$$ainTex;
 sampler2D _AlphaTex;
 sampler2D_float _CameraDepthTexture;
 
 ...
 
 float LinearDepth (float rawDepth)
 {
     if (unity_CameraProjection[3][3] == 0.0)
         return LinearEyeDepth (rawDepth);
                     
     #ifdef UNITY_REVERSED_Z
     rawDepth = 1.0 - rawDepth;
     #endif
 
     return rawDepth * _ProjectionParams.z;
 }
 
 fixed4 SpriteFrag(v2f IN) : SV_Target
 {
     float2 uv = IN.screenPos.xy / IN.screenPos.w;
     float depth = LinearDepth (tex2D (_CameraDepthTexture, uv).r);
     clip (depth - IN.screenPos.z);
 
     fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
     c.rgb *= c.a;
     return c;
 }

Can't post the whole shader due to character limits, but here are the sections that have changed. It is 100% a hack, and you will probably have to move your sprite slightly higher than the ground on ortho cameras so it doesn't clip through the floor, but it should do the job. The problem is that if you scale the mesh, but clip against the original position you end up with false occlusion around contact edges, so you just get weird perspective holes. This is about as close as I could get, and it does work but requires a little bit of fiddling.

avatar image Namey5 Namey5 · Mar 22, 2020 at 01:41 AM 0
Share

Also note that you will probably need to enable the depth texture on your camera (as it most likely isn't in use by default). You can use this little script;

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 [ExecuteInEdit$$anonymous$$ode]
 public class ForceDepthTexture : $$anonymous$$onoBehaviour
 {
     private void OnEnable ()
     {
         Camera cam = GetComponent<Camera>();
         cam.depthTexture$$anonymous$$ode |= DepthTexture$$anonymous$$ode.Depth;
     }
 }
Show more comments
avatar image
0

Answer by KitoCode · Mar 23, 2020 at 03:22 AM

No joke I am working on something extremely similar. Hit me up on discord id love to talk. I believe i have the solution down to pixel perfect with the exception of a small bug.

Pyroh#9393

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image themadmuffin · Nov 23, 2020 at 01:30 PM 0
Share

Did you guys ever solve this? I am getting involved in a project where I would like to do this exact projection method / sprite sorting thing.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

264 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Need help with object behind another one shader 2d 0 Answers

Is there a way I can add drawing mechanic like in "line rider" for a 3D game? 0 Answers

Unity 2D scaling with a diffuse material 0 Answers

How to make a stickman not dismember itself 0 Answers

How to blur 2D background sprites? 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges