Created
March 31, 2022 19:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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