////////////////////////////////////////////////////// // MK Toon Pipeline // // // // Created by Michael Kremmel // // www.michaelkremmel.de // // Copyright © 2021 All rights reserved. // ////////////////////////////////////////////////////// #ifndef MK_TOON_PIPELINE #define MK_TOON_PIPELINE #include "Config.hlsl" #if defined(MK_URP) #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #elif defined(MK_LWRP) #include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Lighting.hlsl" #else #include "AutoLight.cginc" #include "UnityGlobalIllumination.cginc" #endif ///////////////////////////////////////////////////////////////////////////////////////////// // Helpers ///////////////////////////////////////////////////////////////////////////////////////////// //Somehow on Metal overloaded methods (with different types) cause a redefinition compiler issue //therefore force float only on Metal here #ifndef SHADER_API_METAL half SafeDivide(half v, half d) { return v / (d + HALF_MIN); } half2 SafeDivide(half2 v, half d) { return v / (d + HALF_MIN); } half2 SafeDivide(half2 v, half2 d) { return v / (d + HALF_MIN); } half3 SafeDivide(half3 v, half d) { return v / (d + HALF_MIN); } half3 SafeDivide(half3 v, half3 d) { return v / (d + HALF_MIN); } half4 SafeDivide(half4 v, half d) { return v / (d + HALF_MIN); } half4 SafeDivide(half4 v, half4 d) { return v / (d + HALF_MIN); } #endif float SafeDivide(float v, float d) { return v / (d + HALF_MIN); } float2 SafeDivide(float2 v, float d) { return v / (d + HALF_MIN); } float2 SafeDivide(float2 v, float2 d) { return v / (d + HALF_MIN); } float3 SafeDivide(float3 v, float d) { return v / (d + HALF_MIN); } float3 SafeDivide(float3 v, float3 d) { return v / (d + HALF_MIN); } float4 SafeDivide(float4 v, float d) { return v / (d + HALF_MIN); } float4 SafeDivide(float4 v, float4 d) { return v / (d + HALF_MIN); } half SafeNormalize(half v) { return normalize(v + HALF_MIN); } half2 SafeNormalize(half2 v) { return normalize(v + HALF_MIN); } half3 SafeNormalize(half3 v) { return normalize(v + HALF_MIN); } half4 SafeNormalize(half4 v) { return normalize(v + HALF_MIN); } inline half FastPow2(half v) { return v * v; } inline half FastPow3(half v) { return v * v * v; } inline half FastPow4(half v) { return v * v * v * v; } inline half FastPow5(half v) { return v * v * v * v * v; } //Single Component Reciprocal inline half Rcp(half v) { #if SHADER_TARGET >= 50 return rcp(v); #else //avoid division by 0 return SafeDivide(1.0, v); #endif } //modified clip function for a complete discard when input equal zero oder smaller inline void Clip0(half c) { //if(any(c < 0.0)) discard; clip(c + HALF_MIN); } ///////////////////////////////////////////////////////////////////////////////////////////// // Sampling ///////////////////////////////////////////////////////////////////////////////////////////// #if SHADER_TARGET >= 35 #if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED) #define UNIFORM_TEXTURE_2D_AUTO(textureName) uniform Texture2DArray textureName; #define UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO(textureName) uniform Texture2DArray textureName; uniform SamplerState sampler##textureName; #define DECLARE_TEXTURE_2D_ARGS_AUTO(textureName, samplerName) Texture2DArray textureName, SamplerState samplerName #define UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO_HP(textureName) uniform Texture2DArray textureName; uniform SamplerState sampler##textureName; #define DECLARE_TEXTURE_2D_ARGS_AUTO_HP(textureName, samplerName) Texture2DArray textureName, SamplerState samplerName #else #define UNIFORM_TEXTURE_2D_AUTO(textureName) uniform Texture2D textureName; #define UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO(textureName) uniform Texture2D textureName; uniform SamplerState sampler##textureName; #define DECLARE_TEXTURE_2D_ARGS_AUTO(textureName, samplerName) Texture2D textureName, SamplerState samplerName #define UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO_HP(textureName) uniform Texture2D textureName; uniform SamplerState sampler##textureName; #define DECLARE_TEXTURE_2D_ARGS_AUTO_HP(textureName, samplerName) Texture2D textureName, SamplerState samplerName #endif #define DECLARE_TEXTURE_2D_ARGS(textureName, samplerName) Texture2D textureName, SamplerState samplerName #define UNIFORM_SAMPLER(samplerName) uniform SamplerState sampler##samplerName; #define UNIFORM_TEXTURE_2D(textureName) uniform Texture2D textureName; #define PASS_TEXTURE_2D(textureName, samplerName) textureName, samplerName #else #define UNIFORM_TEXTURE_2D_AUTO(textureName) uniform sampler2D textureName; #define UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO(textureName) uniform sampler2D textureName; #define DECLARE_TEXTURE_2D_ARGS_AUTO(textureName, samplerName) sampler2D textureName #define UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO_HP(textureName) uniform sampler2D textureName; #define DECLARE_TEXTURE_2D_ARGS_AUTO_HP(textureName, samplerName) sampler2D textureName #define DECLARE_TEXTURE_2D_ARGS(textureName, samplerName) sampler2D textureName #define UNIFORM_SAMPLER(samplerName) #define UNIFORM_TEXTURE_2D(textureName) uniform sampler2D textureName; #define PASS_TEXTURE_2D(textureName, samplerName) textureName #endif #ifdef MK_POINT_FILTERING UNIFORM_SAMPLER(_point_repeat_Main) #else UNIFORM_SAMPLER(_linear_repeat_Main) #endif UNIFORM_SAMPLER(_point_clamp_Main) #if SHADER_TARGET >= 35 #ifdef MK_POINT_FILTERING #ifndef SAMPLER_REPEAT_MAIN #define SAMPLER_REPEAT_MAIN sampler_point_repeat_Main #endif #else #ifndef SAMPLER_REPEAT_MAIN #define SAMPLER_REPEAT_MAIN sampler_linear_repeat_Main #endif #endif #ifndef SAMPLER_CLAMPED_MAIN #define SAMPLER_CLAMPED_MAIN sampler_point_clamp_Main #endif #else #ifndef SAMPLER_REPEAT_MAIN #define SAMPLER_REPEAT_MAIN 0 #endif #ifndef SAMPLER_CLAMPED_MAIN #define SAMPLER_CLAMPED_MAIN 0 #endif #endif inline half4 SampleTex2DAuto(DECLARE_TEXTURE_2D_ARGS_AUTO(tex, samplerTex), float2 uv) { #if SHADER_TARGET >= 35 #if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED) return tex.SampleLevel(samplerTex, float3((uv).xy, (float)unity_StereoEyeIndex), 0); #else return tex.SampleLevel(samplerTex, UnityStereoTransformScreenSpaceTex(uv), 0); #endif #else return tex2D(tex, UnityStereoTransformScreenSpaceTex(uv)); #endif } inline float4 SampleTex2DAutoHP(DECLARE_TEXTURE_2D_ARGS_AUTO_HP(tex, samplerTex), float2 uv) { #if SHADER_TARGET >= 35 #if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED) return tex.SampleLevel(samplerTex, float3((uv).xy, (float)unity_StereoEyeIndex), 0); #else return tex.SampleLevel(samplerTex, UnityStereoTransformScreenSpaceTex(uv), 0); #endif #else return tex2D(tex, UnityStereoTransformScreenSpaceTex(uv)); #endif } inline half4 SampleTex2D(DECLARE_TEXTURE_2D_ARGS(tex, samplerTex), float2 uv) { #if SHADER_TARGET >= 35 return tex.Sample(samplerTex, uv); #else return tex2D(tex, uv); #endif } //Clamped ramp samplings /* inline half4 SampleRamp1D(DECLARE_TEXTURE_2D_ARGS(ramp, samplerTex), half value) { #if SHADER_TARGET >= 35 return ramp.Sample(samplerTex, float2(clamp(value, HALF_MIN, ONE_MINUS_HALF_MIN), 0.5)); #else return SampleTex2D(PASS_TEXTURE_2D(ramp, samplerTex), float2(clamp(value, HALF_MIN, ONE_MINUS_HALF_MIN), 0.5)); #endif } inline half4 SampleRamp2D(DECLARE_TEXTURE_2D_ARGS(ramp, samplerTex), half2 value) { #if SHADER_TARGET >= 35 return ramp.Sample(samplerTex, clamp(value, HALF_MIN, ONE_MINUS_HALF_MIN)); #else return SampleTex2D(PASS_TEXTURE_2D(ramp, samplerTex), clamp(value, HALF_MIN, ONE_MINUS_HALF_MIN)); #endif } */ //saturated ramp samplings inline half4 SampleRamp1D(DECLARE_TEXTURE_2D_ARGS(ramp, samplerTex), half value) { #if SHADER_TARGET >= 35 return ramp.Sample(samplerTex, float2(saturate(value), 0.5)); #else return SampleTex2D(PASS_TEXTURE_2D(ramp, samplerTex), float2(saturate(value), 0.5)); #endif } inline half4 SampleRamp2D(DECLARE_TEXTURE_2D_ARGS(ramp, samplerTex), half2 value) { #if SHADER_TARGET >= 35 return ramp.Sample(samplerTex, saturate(value)); #else return SampleTex2D(PASS_TEXTURE_2D(ramp, samplerTex), saturate(value)); #endif } /* //unclamped ramp samplings inline half4 SampleRamp1D(DECLARE_TEXTURE_2D_ARGS(ramp, samplerTex), half value) { #if SHADER_TARGET >= 35 return ramp.Sample(samplerTex, float2(value, 0.5)); #else return SampleTex2D(PASS_TEXTURE_2D(ramp, samplerTex), float2(value, 0.5)); #endif } inline half4 SampleRamp2D(DECLARE_TEXTURE_2D_ARGS(ramp, samplerTex), half2 value) { #if SHADER_TARGET >= 35 return ramp.Sample(samplerTex, value); #else return SampleTex2D(PASS_TEXTURE_2D(ramp, samplerTex), value); #endif } */ #ifdef MK_FLIPBOOK #define SAMPLE_TEX2D_FLIPBOOK(tex, samplerTex, uv, blendUV) SampleTex2DFlipbook(PASS_TEXTURE_2D(tex, samplerTex), uv, blendUV) #else #define SAMPLE_TEX2D_FLIPBOOK(tex, samplerTex, uv, blendUV) SampleTex2D(PASS_TEXTURE_2D(tex, SAMPLER_REPEAT_MAIN), uv) #endif /* #ifdef MK_FLIPBOOK #if SHADER_TARGET >= 35 #define SAMPLE_TEX2D_FLIPBOOK(tex, samplerTex, uv, blendUV) SampleTex2DFlipbook(PASS_TEXTURE_2D(tex, samplerTex), uv, blendUV) #else #define SAMPLE_TEX2D_FLIPBOOK(tex, uv, blendUV) SampleTex2DFlipbook(PASS_TEXTURE_2D(tex, SAMPLER_REPEAT_MAIN), uv, blendUV) #endif #else #if SHADER_TARGET >= 35 #define SAMPLE_TEX2D_FLIPBOOK(tex, samplerTex, uv, blendUV) SampleTex2D(PASS_TEXTURE_2D(tex, samplerTex), uv) #else #define SAMPLE_TEX2D_FLIPBOOK(tex, uv, blendUV) SampleTex2D(PASS_TEXTURE_2D(tex, SAMPLER_REPEAT_MAIN), uv) #endif #endif */ inline half4 SampleTex2DFlipbook(DECLARE_TEXTURE_2D_ARGS(tex, samplerTex), float2 uv, float3 blendUV) { #ifdef MK_FLIPBOOK half4 color0 = SampleTex2D(PASS_TEXTURE_2D(tex, samplerTex), uv); half4 color1 = SampleTex2D(PASS_TEXTURE_2D(tex, samplerTex), blendUV.xy); return lerp(color0, color1, blendUV.z); #else return SampleTex2D(PASS_TEXTURE_2D(tex, samplerTex), uv); #endif } struct TriplanarUV { float2 x, y, z; half3 weights; }; inline TriplanarUV UVTriplanar(float3 position, float scale, half blend, half3 normal) { TriplanarUV uv; float3 uvp = position * scale; uv.x = uvp.zy; uv.y = uvp.xz; uv.z = uvp.xy; uv.x.y += 0.5; uv.z.x += 0.5; uv.weights = pow(abs(normal), blend); uv.weights = SafeDivide(uv.weights, uv.weights.x + uv.weights.y + uv.weights.z); return uv; } inline half4 SampleTriplanar(DECLARE_TEXTURE_2D_ARGS(texX, samplerX), DECLARE_TEXTURE_2D_ARGS(texY, samplerY), DECLARE_TEXTURE_2D_ARGS(texZ, samplerZ), TriplanarUV uv) { half4 colorX = SampleTex2D(PASS_TEXTURE_2D(texX, samplerX), uv.x); half4 colorY = SampleTex2D(PASS_TEXTURE_2D(texY, samplerY), uv.y); half4 colorZ = SampleTex2D(PASS_TEXTURE_2D(texZ, samplerZ), uv.z); return colorX * uv.weights.x + colorY * uv.weights.y + colorZ * uv.weights.z; } ///////////////////////////////////////////////////////////////////////////////////////////// // Transformations ///////////////////////////////////////////////////////////////////////////////////////////// #define CAMERA_POSITION_WORLD _WorldSpaceCameraPos #define MATRIX_VP UNITY_MATRIX_VP #define MATRIX_MVP UNITY_MATRIX_MVP #if defined(MK_URP) || defined(MK_LWRP) #define MATRIX_M UNITY_MATRIX_M #define MATRIX_I_M UNITY_MATRIX_I_M #define VERTEX_INPUT vertexInput #define SV_CLIP_POS svPositionClip #else #define MATRIX_M unity_ObjectToWorld #define MATRIX_I_M unity_WorldToObject #define VERTEX_INPUT v #define SV_CLIP_POS pos #endif #define Z_BUFFER_PARAMS _ZBufferParams inline float4 ComputeObjectToClipSpace(float3 positionObject) { return mul(MATRIX_VP, mul(MATRIX_M, float4(positionObject, 1.0))); //return mul(MATRIX_MVP, float4(positionObject, 1.0)); } inline float3 ComputeObjectToWorldSpace(float3 positionObject) { return mul(MATRIX_M, float4(positionObject, 1.0)).xyz; } inline float4 ComputeWorldToClipSpace(float3 positionWorld) { return mul(MATRIX_VP, float4(positionWorld, 1.0)); } inline half3 ComputeNormalWorld(half3 normalDirectionObject) { #ifdef UNITY_ASSUME_UNIFORM_SCALING return SafeNormalize(mul((float3x3) MATRIX_M, normalDirectionObject)); #else // Normal need to be multiply by inverse transpose return SafeNormalize(mul(normalDirectionObject, (float3x3) MATRIX_I_M)); #endif } inline half3 ComputeNormalObjectToClipSpace(half3 normalDirectionObject) { #ifdef UNITY_ASSUME_UNIFORM_SCALING return SafeNormalize(mul((float3x3) UNITY_MATRIX_VP, mul((float3x3) MATRIX_M, normalDirectionObject))); #else // Normal need to be multiply by inverse transpose return SafeNormalize(mul((float3x3) UNITY_MATRIX_VP, mul(normalDirectionObject, (float3x3) MATRIX_I_M))); #endif } inline half3 ComputeTangentWorld(half3 tangentObject) { return SafeNormalize(mul((float3x3) MATRIX_M, tangentObject)); } inline half3 ComputeBitangentWorld(half3 normalWorld, half3 tangentWorld, half scale) { return SafeNormalize(cross(normalWorld, tangentWorld)) * scale; } inline half3 ComputeViewWorld(float3 positionWorld) { return SafeNormalize(CAMERA_POSITION_WORLD - positionWorld); } inline half3 ComputeViewObject(float3 positionObject) { return SafeNormalize(mul(MATRIX_I_M, float4(CAMERA_POSITION_WORLD, 1)).xyz - positionObject); } inline half3 ComputeViewTangent(half3 view, half3 normal, half3 tangent, half3 bitangent) { return SafeNormalize(mul(half3x3(tangent, bitangent, normal), view)); } inline float ComputeLinearDepth(float depth) { return Rcp(Z_BUFFER_PARAMS.z * depth + Z_BUFFER_PARAMS.w); } inline float4 ComputeNDC(float4 positionClip) { float4 ndc; ndc = positionClip * 0.5; ndc.xy = float2(ndc.x, ndc.y * _ProjectionParams.x) + ndc.w; ndc.zw = positionClip.zw; /* #if defined(UNITY_SINGLE_PASS_STEREO) ndc.xy = TransformStereoScreenSpaceTex(ndc.xy, ndc.w); #endif */ ndc.xyz = SafeDivide(ndc.xyz, ndc.w); return ndc; } inline float2 ComputeNormalizedScreenUV(float4 ndc, float4 nullNdc, float scale) { //Orthographic camera is hard to handle => no ability to get "size" //therefore ortho view differs from perspective view //NDC offset ndc.xy -= nullNdc.xy; //Scale based on rendertarget size #if defined(UNITY_SINGLE_PASS_STEREO) half aspect = SafeDivide(_ScreenParams.x, _ScreenParams.y); #else half aspect = SafeDivide(_ScreenParams.x, _ScreenParams.y); #endif ndc.x *= aspect; ndc.xy *= scale; ndc.xy *= nullNdc.w; return ndc.xy; }; ///////////////////////////////////////////////////////////////////////////////////////////// // Fog ///////////////////////////////////////////////////////////////////////////////////////////// inline float FogFactorVertex(float zClipSpace) { #if defined(MK_URP) || defined(MK_LWRP) return ComputeFogFactor(zClipSpace); #else #if (SHADER_TARGET < 30) || defined(SHADER_API_MOBILE) UNITY_CALC_FOG_FACTOR(zClipSpace); return unityFogFactor; #else return zClipSpace; #endif #endif } inline void ApplyFog(inout half3 color, float fogFactor) { #if defined(MK_URP) || defined(MK_LWRP) color = MixFog(color, fogFactor); #else #if defined(MK_FORWARD_BASE_PASS) || defined(MK_OUTLINE_PASS) UNITY_APPLY_FOG_COLOR(fogFactor, color, unity_FogColor); #elif defined(MK_FORWARD_ADD_PASS) UNITY_APPLY_FOG_COLOR(fogFactor, color, half4(0,0,0,0)); #endif #endif } ///////////////////////////////////////////////////////////////////////////////////////////// // Refraction ///////////////////////////////////////////////////////////////////////////////////////////// #if defined(MK_URP) || defined(MK_LWRP) UNIFORM_TEXTURE_2D_AUTO(_CameraOpaqueTexture) #else UNIFORM_TEXTURE_2D_AUTO(_MKToonRefraction) #endif inline half3 SampleRefraction(float2 uv) { #if defined(MK_URP) || defined(MK_LWRP) return SampleTex2DAuto(PASS_TEXTURE_2D(_CameraOpaqueTexture, SAMPLER_CLAMPED_MAIN), uv).rgb; #else return SampleTex2DAuto(PASS_TEXTURE_2D(_MKToonRefraction, SAMPLER_CLAMPED_MAIN), uv).rgb; #endif } ///////////////////////////////////////////////////////////////////////////////////////////// // Depth ///////////////////////////////////////////////////////////////////////////////////////////// UNIFORM_SAMPLER_AND_TEXTURE_2D_AUTO_HP(_CameraDepthTexture) inline float SampleDepth(float2 uv) { return SampleTex2DAutoHP(PASS_TEXTURE_2D(_CameraDepthTexture, SAMPLER_CLAMPED_MAIN), uv).r; } #endif