Project_WL/Assets/ThirdParty/Toon Shaders Pro/Shaders/Outline/Resources/Outline.shader

408 lines
14 KiB
GLSL

Shader "Hidden/ToonShadersPro/URP/Outlines"
{
SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
}
HLSLINCLUDE
// Credit to https://alexanderameye.github.io/outlineshader.html:
float3 DecodeNormal(float4 enc)
{
float kScale = 1.7777;
float3 nn = enc.xyz*float3(2 * kScale, 2 * kScale, 0) + float3(-kScale, -kScale, 1);
float g = 2.0 / dot(nn.xyz, nn.xyz);
float3 n;
n.xy = g * nn.xy;
n.z = g - 1;
return n;
}
// Code 'liberated' from Shader Graph's Simple Noise node.
inline float randomValue(float2 uv)
{
return frac(sin(dot(uv, float2(12.9898, 78.233)))*43758.5453);
}
inline float perlinLerp(float a, float b, float t)
{
return (1.0f - t) * a + (t * b);
}
inline float valueNoise(float2 uv)
{
float2 i = floor(uv);
float2 f = frac(uv);
f = f * f * (3.0 - 2.0 * f);
uv = abs(frac(uv) - 0.5);
float2 c0 = i + float2(0.0, 0.0);
float2 c1 = i + float2(1.0, 0.0);
float2 c2 = i + float2(0.0, 1.0);
float2 c3 = i + float2(1.0, 1.0);
float r0 = randomValue(c0);
float r1 = randomValue(c1);
float r2 = randomValue(c2);
float r3 = randomValue(c3);
float bottomOfGrid = perlinLerp(r0, r1, f.x);
float topOfGrid = perlinLerp(r2, r3, f.x);
float t = perlinLerp(bottomOfGrid, topOfGrid, f.y);
return t;
}
float perlinNoise(float2 uv, float offset, float2 scale)
{
float t = 0.0;
float2 scaledUV = uv * scale + offset;
float freq = pow(2.0, float(0));
float amp = pow(0.5, float(3 - 0));
t += valueNoise(float2(scaledUV.x / freq, scaledUV.y / freq))*amp;
freq = pow(2.0, float(1));
amp = pow(0.5, float(3 - 1));
t += valueNoise(float2(scaledUV.x / freq, scaledUV.y / freq))*amp;
freq = pow(2.0, float(2));
amp = pow(0.5, float(3 - 2));
t += valueNoise(float2(scaledUV.x / freq, scaledUV.y / freq))*amp;
return t;
}
float2 perlinNoiseRG(float2 uv, float2 screenSize, float offset, float scale)
{
float2 aspectScale = float2(screenSize.x / screenSize.y * scale, scale);
return float2(perlinNoise(uv, offset, aspectScale), perlinNoise(uv + float2(321.24167f, -47.124f), offset, aspectScale)) * 2.0f - 1.0f;
}
ENDHLSL
Pass
{
Name "DepthNormalOutlines"
ZTest Always
Cull Off
ZWrite Off
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag
#pragma shader_feature_local_fragment _USE_NOISE_OFFSETS
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareNormalsTexture.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#include "OutlineUtils.hlsl"
float4 _OutlineColor;
float _ColorSensitivity;
float _ColorStrength;
float _DepthSensitivity;
float _DepthStrength;
float _NormalsSensitivity;
float _NormalsStrength;
float _DepthThreshold;
#ifdef _USE_NOISE_OFFSETS
float _NoiseScale;
float _NoiseOffset;
float _NoiseStrength;
#endif
float4 frag (Varyings i) : SV_TARGET
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
float2 uv = i.texcoord;
float4 col = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv);
#ifdef _USE_NOISE_OFFSETS
uv += perlinNoiseRG(uv, float2(_ScreenParams.x, _ScreenParams.y), _NoiseOffset, _NoiseScale) * _NoiseStrength;
#endif
// Get UV coords of pixel to the left, right, below, and above the center pixel.
float2 leftUV = uv + float2(1.0f / -_ScreenParams.x, 0.0f);
float2 rightUV = uv + float2(1.0f / _ScreenParams.x, 0.0f);
float2 bottomUV = uv + float2(0.0f, 1.0f / -_ScreenParams.y);
float2 topUV = uv + float2(0.0f, 1.0f / _ScreenParams.y);
// Find differences between nearby pixel colors.
float3 col0 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, leftUV).rgb;
float3 col1 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, rightUV).rgb;
float3 col2 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, bottomUV).rgb;
float3 col3 = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, topUV).rgb;
float3 c0 = col1 - col0;
float3 c1 = col3 - col2;
// Detect color-based edges using gradients.
float edgeCol = sqrt(dot(c0, c0) + dot(c1, c1));
edgeCol = edgeCol > _ColorSensitivity ? _ColorStrength : 0;
// Find differences between nearby pixel depth values.
float depth0 = Linear01Depth(GetSceneDepth(leftUV), _ZBufferParams);
float depth1 = Linear01Depth(GetSceneDepth(rightUV), _ZBufferParams);
float depth2 = Linear01Depth(GetSceneDepth(bottomUV), _ZBufferParams);
float depth3 = Linear01Depth(GetSceneDepth(topUV), _ZBufferParams);
float d0 = depth1 - depth0;
float d1 = depth3 - depth2;
// Detect depth-based edges using gradients.
float edgeDepth = sqrt(d0 * d0 + d1 * d1);
edgeDepth = edgeDepth > _DepthSensitivity ? _DepthStrength : 0;
// Find differences between nearby pixel normals.
float3 normal0 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, leftUV));
float3 normal1 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, rightUV));
float3 normal2 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, bottomUV));
float3 normal3 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, topUV));
float3 n0 = normal1 - normal0;
float3 n1 = normal3 - normal2;
// Detect normal-based edges using gradients.
float edgeNormal = sqrt(dot(n0, n0) + dot(n1, n1));
edgeNormal = edgeNormal > _NormalsSensitivity ? _NormalsStrength : 0;
// Detect edges with the highest of the three metrics.
float edge = max(max(edgeCol, edgeDepth), edgeNormal);
float depth = GetSceneDepth(uv);
// Discard edge-detected pixels which are super far away (usually at/near the skybox).
depth = Linear01Depth(depth, _ZBufferParams);
edge = depth > _DepthThreshold ? 0.0f : edge;
return lerp(col, _OutlineColor, edge);
}
ENDHLSL
}
Pass
{
Name "MaskedObjectOutlines"
ZTest Always
Cull Off
ZWrite Off
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag
#pragma shader_feature_local_fragment _USE_DEPTH_NORMALS
#pragma shader_feature_local_fragment _USE_NOISE_OFFSETS
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
#ifdef _USE_DEPTH_NORMALS
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareNormalsTexture.hlsl"
#endif
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#include "OutlineUtils.hlsl"
TEXTURE2D(_MaskedObjects);
float4 _OutlineColor;
float _OutlineWidth;
float _Spread;
float _OutlineFadeStart;
float _OutlineFadeEnd;
float2 _DrawSides;
#ifdef _USE_DEPTH_NORMALS
float _NormalsSensitivity;
float _NormalsStrength;
#endif
#ifdef _USE_NOISE_OFFSETS
float _NoiseScale;
float _NoiseOffset;
float _NoiseStrength;
#endif
inline float distanceFade(float x, float y)
{
return (abs(x) + abs(y)) * _Spread;
}
float4 frag (Varyings i) : SV_TARGET
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
float2 uv = i.texcoord;
float4 col = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv);
#ifdef _USE_NOISE_OFFSETS
uv += perlinNoiseRG(uv, float2(_ScreenParams.x, _ScreenParams.y), _NoiseOffset, _NoiseScale) * _NoiseStrength;
#endif
// Get mask and depth values for center pixel.
float maskMiddle = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, uv).r;
float depthMiddle = GetSceneDepth(uv);
// Will need eye depth later for outline fade-out.
float eyeDepth = LinearEyeDepth(depthMiddle, _ZBufferParams);
// Is the center pixel masked?
float isMaskedPixel = step(0.0001f, maskMiddle.r);
float totalDiff = 0.0f;
// Iterate over a kernel of width (2 * _OutlineWidth + 1).
for(int x = -_OutlineWidth; x <= _OutlineWidth; ++x)
{
for(int y = -_OutlineWidth; y <= _OutlineWidth; ++y)
{
// Get the mask and depth values of kernel pixel.
float2 maskUV = uv + float2(x / _ScreenParams.x, y / _ScreenParams.y);
float maskAdjacent = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, maskUV).r;
float depthAdjacent = GetSceneDepth(maskUV);
// Set eye depth to the closest pixel.
eyeDepth = min(eyeDepth, LinearEyeDepth(depthAdjacent, _ZBufferParams));
// Find out how the center and kernel pixel are different/same.
float adjacentIsMask = step(0.0001f, maskAdjacent.r);
float maskIsDifferent = step(0.000001f, distance(maskAdjacent.r, maskMiddle.r));
float adjacentIsCloser = (1.0f - step(depthAdjacent, depthMiddle));
// For masked center pixels, detect masked kernel pixels, or any unmasked pixel that is further away.
float pixelDiff = isMaskedPixel * adjacentIsMask * maskIsDifferent * lerp(_DrawSides.x, _DrawSides.y, adjacentIsCloser);
pixelDiff += isMaskedPixel * _DrawSides.x * maskIsDifferent * (1.0f - adjacentIsMask) * (1.0f - adjacentIsCloser);
// For unmasked center pixels, detect masked pixels that are closer.
pixelDiff += (1.0f - isMaskedPixel) * adjacentIsMask * adjacentIsCloser * _DrawSides.y;
// Smooth out edge detect contributions using distance-based weighting.
totalDiff += distanceFade(x, y) * saturate(pixelDiff);
}
}
// Fade out outlines over a distance range.
float outlineStrength = saturate(totalDiff) * _OutlineColor.a;
outlineStrength *= smoothstep(_OutlineFadeEnd, _OutlineFadeStart, eyeDepth);
#ifdef _USE_DEPTH_NORMALS
// Get UV coords of pixel to the left, right, below, and above the center pixel.
float2 leftUV = uv + float2(1.0f / -_ScreenParams.x, 0.0f);
float2 rightUV = uv + float2(1.0f / _ScreenParams.x, 0.0f);
float2 bottomUV = uv + float2(0.0f, 1.0f / -_ScreenParams.y);
float2 topUV = uv + float2(0.0f, 1.0f / _ScreenParams.y);
// Find differences between nearby pixel normals.
float3 normal0 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, leftUV));
float3 normal1 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, rightUV));
float3 normal2 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, bottomUV));
float3 normal3 = DecodeNormal(SAMPLE_TEXTURE2D_X(_CameraNormalsTexture, sampler_LinearClamp, topUV));
float3 n0 = normal1 - normal0;
float3 n1 = normal3 - normal2;
// Detect normal-based edges using gradients.
float edgeNormal = sqrt(dot(n0, n0) + dot(n1, n1));
edgeNormal = edgeNormal > _NormalsSensitivity ? _NormalsStrength : 0;
outlineStrength = max(outlineStrength, edgeNormal);
#endif
col.rgb = lerp(col.rgb, _OutlineColor.rgb, outlineStrength);
return col;
}
ENDHLSL
}
Pass
{
Name "ThinMaskedObjectOutlines"
ZTest Always
Cull Off
ZWrite Off
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment frag
#pragma shader_feature_local_fragment _USE_NOISE_OFFSETS
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#include "OutlineUtils.hlsl"
TEXTURE2D(_MaskedObjects);
float4 _OutlineColor;
#ifdef _USE_NOISE_OFFSETS
float _NoiseScale;
float _NoiseOffset;
float _NoiseStrength;
#endif
float4 frag (Varyings i) : SV_TARGET
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
float2 uv = i.texcoord;
float4 col = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, uv);
#ifdef _USE_NOISE_OFFSETS
uv += perlinNoiseRG(uv, float2(_ScreenParams.x, _ScreenParams.y), _NoiseOffset, _NoiseScale) * _NoiseStrength;
#endif
// Get UVs for nearby four pixels.
float2 leftUV = uv + float2(1.0f / -_ScreenParams.x, 0.0f);
float2 rightUV = uv + float2(1.0f / _ScreenParams.x, 0.0f);
float2 bottomUV = uv + float2(0.0f, 1.0f / -_ScreenParams.y);
float2 topUV = uv + float2(0.0f, 1.0f / _ScreenParams.y);
// Get mask pixels for center and four adjacent pixels.
float middleMask = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, uv).r;
float mask0 = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, leftUV).r;
float mask1 = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, rightUV).r;
float mask2 = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, bottomUV).r;
float mask3 = SAMPLE_TEXTURE2D(_MaskedObjects, sampler_LinearClamp, topUV).r;
// Get four adjacent depth pixels.
float middleDepth = GetSceneDepth(uv);
float depth0 = GetSceneDepth(leftUV);
float depth1 = GetSceneDepth(rightUV);
float depth2 = GetSceneDepth(bottomUV);
float depth3 = GetSceneDepth(topUV);
// Only use mask values for further away pixels.
mask0 = step(middleDepth, depth0) < 0.5f ? mask0 : middleMask;
mask1 = step(middleDepth, depth1) < 0.5f ? mask1 : middleMask;
mask2 = step(middleDepth, depth2) < 0.5f ? mask2 : middleMask;
mask3 = step(middleDepth, depth3) < 0.5f ? mask3 : middleMask;
// Find differences across adjacent pixels to find edges.
float c0 = mask1 - mask0;
float c1 = mask3 - mask2;
float edgeDiff = sqrt(c0 * c0 + c1 * c1);
float edgeDetect = step(0.0001f, edgeDiff);
// Do not detect edges on pixels that are not masked.
edgeDetect *= step(0.001f, middleMask);
col.rgb = lerp(col.rgb, _OutlineColor.rgb, edgeDetect * _OutlineColor.a);
return col;
}
ENDHLSL
}
}
}