Skip to content

Instantly share code, notes, and snippets.

@tombuben
Created March 31, 2022 19:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tombuben/bc08f1b07c63c8298e2aa70af3d6f14c to your computer and use it in GitHub Desktop.
Save tombuben/bc08f1b07c63c8298e2aa70af3d6f14c to your computer and use it in GitHub Desktop.
Simple extension of the built in Unity Standard shader to properly support motion vectors with cutout transparency
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
Shader "New/Internal-MotionVectors"
{
//BEGIN(Tomas.Bubenicek): Properties taken directly from Standard.shader
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo", 2D) = "white" {}
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
_Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
[Enum(Metallic Alpha,0,Albedo Alpha,1)] _SmoothnessTextureChannel ("Smoothness texture channel", Float) = 0
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
_MetallicGlossMap("Metallic", 2D) = "white" {}
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
[ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0
_BumpScale("Scale", Float) = 1.0
[Normal] _BumpMap("Normal Map", 2D) = "bump" {}
_Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
_ParallaxMap ("Height Map", 2D) = "black" {}
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}
_EmissionColor("Color", Color) = (0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
_DetailMask("Detail Mask", 2D) = "white" {}
_DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
_DetailNormalMapScale("Scale", Float) = 1.0
[Normal] _DetailNormalMap("Normal Map", 2D) = "bump" {}
[Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
// Blending state
[HideInInspector] _Mode ("__mode", Float) = 0.0
[HideInInspector] _SrcBlend ("__src", Float) = 1.0
[HideInInspector] _DstBlend ("__dst", Float) = 0.0
[HideInInspector] _ZWrite ("__zw", Float) = 1.0
}
CGINCLUDE
#define UNITY_SETUP_BRDF_INPUT MetallicSetup
ENDCG
//END(Tomas.Bubenicek)
SubShader
{
//BEGIN(Tomas.Bubenicek)
Tags { "RenderType"="TransparentCutout" }
//END(Tomas.Bubenicek)
CGINCLUDE
#include "UnityCG.cginc"
// Object rendering things
#if defined(USING_STEREO_MATRICES)
float4x4 _StereoNonJitteredVP[2];
float4x4 _StereoPreviousVP[2];
#else
float4x4 _NonJitteredVP;
float4x4 _PreviousVP;
#endif
float4x4 _PreviousM;
bool _HasLastPositionData;
bool _ForceNoMotion;
float _MotionVectorDepthBias;
// BEGIN(Tomas.Bubenicek)
// To add support for transparent cutout materials
uniform fixed4 _Color;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform fixed _Cutoff;
//END(Tomas.Bubenicek)
struct MotionVectorData
{
float4 transferPos : TEXCOORD0;
float4 transferPosOld : TEXCOORD1;
float4 pos : SV_POSITION;
//BEGIN(Tomas.Bubenicek)
float2 uv: TEXCOORD2;
//END(Tomas.Bubenicek)
UNITY_VERTEX_OUTPUT_STEREO
};
struct MotionVertexInput
{
float4 vertex : POSITION;
float3 oldPos : TEXCOORD4;
//BEGIN(Tomas.Bubenicek)
float2 texcoord : TEXCOORD0;
//END(Tomas.Bubenicek)
UNITY_VERTEX_INPUT_INSTANCE_ID
};
MotionVectorData VertMotionVectors(MotionVertexInput v)
{
MotionVectorData o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
// this works around an issue with dynamic batching
// potentially remove in 5.4 when we use instancing
#if defined(UNITY_REVERSED_Z)
o.pos.z -= _MotionVectorDepthBias * o.pos.w;
#else
o.pos.z += _MotionVectorDepthBias * o.pos.w;
#endif
#if defined(USING_STEREO_MATRICES)
o.transferPos = mul(_StereoNonJitteredVP[unity_StereoEyeIndex], mul(unity_ObjectToWorld, v.vertex));
o.transferPosOld = mul(_StereoPreviousVP[unity_StereoEyeIndex], mul(_PreviousM, _HasLastPositionData ? float4(v.oldPos, 1) : v.vertex));
#else
o.transferPos = mul(_NonJitteredVP, mul(unity_ObjectToWorld, v.vertex));
o.transferPosOld = mul(_PreviousVP, mul(_PreviousM, _HasLastPositionData ? float4(v.oldPos, 1) : v.vertex));
#endif
//BEGIN(Tomas.Bubenicek)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
//END(Tomas.Bubenicek)
return o;
}
half4 FragMotionVectors(MotionVectorData i) : SV_Target
{
//BEGIN(Tomas.Bubenicek)
fixed4 texcol = tex2D( _MainTex, i.uv );
clip( texcol.a*_Color.a - _Cutoff );
//END(Tomas.Bubenicek)
float3 hPos = (i.transferPos.xyz / i.transferPos.w);
float3 hPosOld = (i.transferPosOld.xyz / i.transferPosOld.w);
// V is the viewport position at this pixel in the range 0 to 1.
float2 vPos = (hPos.xy + 1.0f) / 2.0f;
float2 vPosOld = (hPosOld.xy + 1.0f) / 2.0f;
#if UNITY_UV_STARTS_AT_TOP
vPos.y = 1.0 - vPos.y;
vPosOld.y = 1.0 - vPosOld.y;
#endif
half2 uvDiff = vPos - vPosOld;
return lerp(half4(uvDiff, 0, 1), 0, (half)_ForceNoMotion);
}
//Camera rendering things
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
struct CamMotionVectors
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 ray : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
struct CamMotionVectorsInput
{
float4 vertex : POSITION;
float3 normal : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
CamMotionVectors VertMotionVectorsCamera(CamMotionVectorsInput v)
{
CamMotionVectors o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
o.pos = UnityObjectToClipPos(v.vertex);
#ifdef UNITY_HALF_TEXEL_OFFSET
o.pos.xy += (_ScreenParams.zw - 1.0) * float2(-1, 1) * o.pos.w;
#endif
o.uv = ComputeScreenPos(o.pos);
// we know we are rendering a quad,
// and the normal passed from C++ is the raw ray.
o.ray = v.normal;
return o;
}
inline half2 CalculateMotion(float rawDepth, float2 inUV, float3 inRay)
{
float depth = Linear01Depth(rawDepth);
float3 ray = inRay * (_ProjectionParams.z / inRay.z);
float3 vPos = ray * depth;
float4 worldPos = mul(unity_CameraToWorld, float4(vPos, 1.0));
#if defined(USING_STEREO_MATRICES)
float4 prevClipPos = mul(_StereoPreviousVP[unity_StereoEyeIndex], worldPos);
float4 curClipPos = mul(_StereoNonJitteredVP[unity_StereoEyeIndex], worldPos);
#else
float4 prevClipPos = mul(_PreviousVP, worldPos);
float4 curClipPos = mul(_NonJitteredVP, worldPos);
#endif
float2 prevHPos = prevClipPos.xy / prevClipPos.w;
float2 curHPos = curClipPos.xy / curClipPos.w;
// V is the viewport position at this pixel in the range 0 to 1.
float2 vPosPrev = (prevHPos.xy + 1.0f) / 2.0f;
float2 vPosCur = (curHPos.xy + 1.0f) / 2.0f;
#if UNITY_UV_STARTS_AT_TOP
vPosPrev.y = 1.0 - vPosPrev.y;
vPosCur.y = 1.0 - vPosCur.y;
#endif
return vPosCur - vPosPrev;
}
half4 FragMotionVectorsCamera(CamMotionVectors i) : SV_Target
{
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
return half4(CalculateMotion(depth, i.uv, i.ray), 0, 1);
}
half4 FragMotionVectorsCameraWithDepth(CamMotionVectors i, out float outDepth : SV_Depth) : SV_Target
{
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
outDepth = depth;
return half4(CalculateMotion(depth, i.uv, i.ray), 0, 1);
}
ENDCG
// 0 - Motion vectors
Pass
{
Tags{ "LightMode" = "MotionVectors" }
ZTest LEqual
Cull Back
ZWrite Off
CGPROGRAM
#pragma vertex VertMotionVectors
#pragma fragment FragMotionVectors
ENDCG
}
//BEGIN(Tomas.Bubenicek): Here's just an include of the entire standard shader SubShader, but sadly, material keywords from the original standard shader aren't applied :/
// Replace this part with any other
UsePass "Standard/FORWARD"
UsePass "Standard/FORWARD_DELTA"
UsePass "Standard/ShadowCaster"
UsePass "Standard/DEFERRED"
UsePass "Standard/META"
//END(Tomas.Bubenicek)
}
FallBack "VertexLit"
//BEGIN(Tomas.Bubenicek): Allow to use as a basic material
CustomEditor "StandardShaderGUI"
//END(Tomas.Bubenicek)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment