Portal cards in Unity

So, I was watching this awesome GDC talk by Matt Nava, where he explained the challenges Giant Squid had to overcome when creating their beautiful game Abzú. What really caught my attention was the use of portal cards in the most variable ways. Here is how I created portal cards for my game in Unity.

You can watch the reference tutorial here:

Here is the code for my portal card shader:

Shader "Gloria/PortalCard" {
Properties
{
_Color ("_Color", Color) = (0.5,0.5,0.5,0.5)
_Depth ("Depth Fade Distance", Range(0.01,100.0)) = 1.0
_FadeLength("Fade Length", Float) = 1.0
}

SubShader
{
Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
LOD 200
Blend SrcAlpha OneMinusSrcAlpha
Lighting Off ZWrite Off

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog;
#pragma target 3.0

#include "UnityCG.cginc"

fixed4 _Color;
float _Depth;
float _FadeLength;
sampler2D _CameraDepthTexture;
float4 _CameraDepthTexture_ST;

struct appdata{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};

struct v2f{
float2 uv : TEXCOORD0;
float3 dist :TEXCOORD3;
float4 projPos : TEXCOORD2;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};

v2f vert (appdata i){
v2f o;
o.vertex = UnityObjectToClipPos(i.vertex);
o.uv = i.texcoord;
UNITY_TRANSFER_FOG(o,o.vertex);
o.dist = UnityObjectToViewPos(i.vertex);

o.projPos = ComputeScreenPos(o.vertex);
UNITY_TRANSFER_DEPTH(o.projPos);

return o;
}

fixed4 frag(v2f i) : SV_TARGET
{
fixed4 col = _Color;

float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
float partZ = i.projPos.z + length(i.dist);
float fade = saturate((sceneZ-partZ) / _Depth);
col.a = saturate((fade * length(i.dist)) / _FadeLength);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}

ENDCG
}
}
FallBack "Diffuse"
}

Remember: In order for the following effect to work on forward rendering path, you need to add the following line of code to any script.

//cam is the Camera class reference.
Camera cam = GetComponent<Camera>();
cam.depthTextureMode = DepthTextureMode.Depth;

Feel free to use it and edit in your project as you please!

3 Likes

Very nice shader. I played around with it and it seems to be working fine in forward rendering mode even without code. Maybe because Unity updated something. One thing you might want to add is ability to work with textures, and maybe a “fade in” distance where it starts out completely invisible until getting to a certain distance where starts to fade in.

bump this should get more attention : )

1 Like

Thanks for sharing this. This will be really useful!

1 Like

I actually designed this shader to work specifically with my game, and funny enough, I ended up needing to add an extra texture field for some situations. haha.

What I posted here is just the backbone of the shader, so you don’t have to reinvent the wheel, and it was a nice practice for someone who is just now learning how to create shaders. Again, you guys can mess around with it as much as you like, no questions asked. :slight_smile:

Can you share the code for the version with the texture field? Thanks.

Sure thing. Here you go:

Shader "Gloria/PortalCard" {
    Properties
    {
        _Color ("Color", Color) = (0.5,0.5,0.5,0.5)
        _MainTex ("Texture (RGBA)", 2D) = "white" {}
        _Depth ("Depth Fade Distance", Range(0.01,100.0)) = 1.0
        _FadeLength("Fade Length", Float) = 10.0
    }

    SubShader
    {
        Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
        LOD 200
        Blend SrcAlpha OneMinusSrcAlpha
        Lighting Off ZWrite Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog;
            #pragma target 3.0

            #include "UnityCG.cginc"

            fixed4 _Color;
            float _Depth;
            float _FadeLength;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _CameraDepthTexture;
            float4 _CameraDepthTexture_ST;

            struct appdata{
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f{
                float2 uv : TEXCOORD0;
                float3 dist :TEXCOORD3;
                float4 projPos : TEXCOORD2;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata i){
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.uv = i.texcoord;
                UNITY_TRANSFER_FOG(o,o.vertex);
                o.dist = UnityObjectToViewPos(i.vertex);

                o.projPos = ComputeScreenPos(o.vertex);
                UNITY_TRANSFER_DEPTH(o.projPos);

                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET
            {
                fixed4 col = _Color * tex2D(_MainTex, i.uv);
                float sceneZ = LinearEyeDepth (SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
                float depthFactor = (sceneZ - length(i.dist)) / _Depth;
                float distFactor = length(i.dist)/_FadeLength;

                col.a *= saturate(distFactor * depthFactor);
                               
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
               
            }

            ENDCG
        }

    }
    FallBack "Diffuse"
}

this is brilliant! Did not want to try to make this myself, would have been a mess.
Thanks!

Edit: just checked if usable in HDRP and it seems not. Can someone point me in the right direction regarding conversion? I have not had much success finding anything specifically.
Thanks

Sorry, D-Coy, I don’t think I can help you there. I’m just scratching the surface of shader programming here, and HDRP is a topic way beyond my skill right now. If I manage to somehow make it work with HDRP, I’ll gladly post the updated shader here.

Thanks for the reply. Yeah, a bit of a ‘can of worms’ question. I think Ill deal with is once HDRP is a little more solid and better documented. Cheers!

1 Like