Created
June 29, 2021 02:20
-
-
Save herohiralal/f64afe56f602d11a74cc7dd225140250 to your computer and use it in GitHub Desktop.
URP Sobel Edge Detection
This file contains hidden or 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
Shader "Universal Render Pipeline/Post-Processing/EdgeDetection" | |
{ | |
Properties | |
{ | |
[Toggle(ENABLED)] __enabled("Enabled", Float) = 1 | |
[MainTex] [HideInInspector] _MainTex ("Base Map", 2D) = "white" { } | |
[MainColor] _Color ("Color", Color) = (1, 1, 1, 1) | |
_Thickness ("Thickness", Float) = 0 | |
_Mask ("Mask", Range(0, 1)) = 0 | |
_DepthTightening ("Depth Tightening", Float) = 0 | |
_DepthStrength ("Depth Strength", Float) = 0 | |
_AcuteAngleStartDot("Acute Angle Start Dot", Float) = 0 | |
_AcuteDepthThreshold ("Acute Depth Threshold", Float) = 0 | |
_DepthThreshold ("Depth Threshold", Float) = 0 | |
_NormalTightening ("Normal Tightening", Float) = 0 | |
_NormalStrength ("Normal Strength", Float) = 0 | |
_NormalAdjustNearDepth ("Normal Adjust Near Depth", Float) = 0 | |
_NormalAdjustFarDepth ("Normal Adjust Far Depth", Float) = 0 | |
_NormalFarThreshold ("Normal Far Threshold", Float) = 0 | |
_NormalThreshold ("Normal Threshold", Float) = 0 | |
} | |
SubShader | |
{ | |
Tags | |
{ | |
"RenderType"="Opaque" | |
"RenderPipeline" = "Universal" | |
} | |
Pass | |
{ | |
HLSLPROGRAM | |
#pragma vertex Vert | |
#pragma fragment Frag | |
#pragma multi_compile _ ENABLED | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" | |
#include "SobelEdgeDetection.hlsl" | |
struct Attributes | |
{ | |
float3 PositionOS : POSITION; | |
float2 UV : TEXCOORD0; | |
}; | |
struct VertexToFragment | |
{ | |
float4 PositionHCS : SV_POSITION; | |
float2 UV : TEXCOORD0; | |
}; | |
TEXTURE2D(_MainTex); | |
SAMPLER(sampler_MainTex); | |
CBUFFER_START(UnityPerMaterial) | |
float4 _MainTex_ST; | |
float4 _Color; | |
float _Thickness; | |
float _Mask; | |
float _DepthTightening; | |
float _DepthStrength; | |
float _AcuteAngleStartDot; | |
float _AcuteDepthThreshold; | |
float _DepthThreshold; | |
float _NormalTightening; | |
float _NormalStrength; | |
float _NormalAdjustNearDepth; | |
float _NormalAdjustFarDepth; | |
float _NormalFarThreshold; | |
float _NormalThreshold; | |
CBUFFER_END | |
float GetProcessedDepthThreshold(in const float2 UV) | |
{ | |
const float ViewDotNormal = dot(ScreenUVToViewDirection(UV), SampleDepthNormalMap(UV).Normal); | |
const float T = smoothstep(_AcuteAngleStartDot, 1, 1 - ViewDotNormal); | |
const float AcuteModifiedDepthThreshold = lerp(_DepthThreshold, _AcuteDepthThreshold, T); | |
return AcuteModifiedDepthThreshold * SampleSceneDepth(UV); | |
} | |
float GetProcessedNormalThreshold(in const float2 UV) | |
{ | |
const float SceneDepthEye = LinearEyeDepth(SampleSceneDepth(UV), _ZBufferParams); | |
const float T = smoothstep(_NormalAdjustNearDepth, _NormalAdjustFarDepth, SceneDepthEye); | |
return lerp(_NormalThreshold, _NormalFarThreshold, T); | |
} | |
VertexToFragment Vert(const Attributes Input) | |
{ | |
VertexToFragment Output; | |
Output.PositionHCS = TransformObjectToHClip(Input.PositionOS); | |
Output.UV = Input.UV; | |
return Output; | |
} | |
float4 Frag(const VertexToFragment Input) : SV_Target | |
{ | |
const float4 MainTex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, Input.UV); | |
#ifndef ENABLED | |
return MainTex; | |
#else | |
const DepthAndNormalSobel SobelValues = CalculateDepthAndNormalSobel(Input.UV, _Thickness); | |
const float DepthEdgeMask = ConvertSobelToEdgeMask( | |
SobelValues.DepthSobel, | |
GetProcessedDepthThreshold(Input.UV), | |
_DepthTightening, | |
_DepthStrength); | |
const float NormalEdgeMask = ConvertSobelToEdgeMask( | |
SobelValues.NormalSobel, | |
GetProcessedNormalThreshold(Input.UV), | |
_NormalTightening, | |
_NormalStrength); | |
const float TotalSobel = pow(max(DepthEdgeMask, NormalEdgeMask), 3); | |
const float4 BorderedColored = lerp(MainTex, _Color, TotalSobel); | |
const float DistanceFromScreenCenter = length(Input.UV - float2(0.5, 0.5)); | |
return DistanceFromScreenCenter < _Mask ? TotalSobel : MainTex; | |
#endif | |
} | |
ENDHLSL | |
} | |
} | |
} |
This file contains hidden or 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
#ifndef SOBEL_EDGE_DETECTION_INCLUDED | |
#define SOBEL_EDGE_DETECTION_INCLUDED | |
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl" | |
TEXTURE2D(_DepthNormalsTexture); | |
SAMPLER(sampler_DepthNormalsTexture); | |
struct DepthNormalMap | |
{ | |
float3 Normal; | |
float Depth; | |
}; | |
DepthNormalMap SampleDepthNormalMap(in const float2 UV) | |
{ | |
const float4 CodedDepthAndNormals = SAMPLE_TEXTURE2D(_DepthNormalsTexture, sampler_DepthNormalsTexture, UV); | |
DepthNormalMap Output; | |
Output.Depth = dot(CodedDepthAndNormals.zw, float2(1.0, 1 / 255.0)); | |
const float Scale = 1.7777; | |
const float3 Nn = CodedDepthAndNormals.xyz * float3(2 * Scale, 2 * Scale, 0) + float3(-Scale, -Scale, 1); | |
const float G = 2.0 / dot(Nn.xyz, Nn.xyz); | |
const float3 Normal01 = float3(G * Nn.xy, G - 1); | |
Output.Normal = Normal01 * 2 - 1; | |
return Output; | |
} | |
static const float2 GSobelSamplePoints[9] = | |
{ | |
float2(-1, +1), float2(+0, +1), float2(+1, +1), | |
float2(-1, +0), float2(+0, +0), float2(+1, +0), | |
float2(-1, -1), float2(+0, -1), float2(+1, -1), | |
}; | |
static const float GSobelXMatrix[9] = | |
{ | |
+1, +0, -1, | |
+2, +0, -2, | |
+1, +0, -1, | |
}; | |
static const float GSobelYMatrix[9] = | |
{ | |
+1, +2, +1, | |
+0, +0, +0, | |
-1, -2, -1, | |
}; | |
float3 ScreenUVToViewDirection(in const float2 Input) | |
{ | |
const float2 POneOneTwoTwo = float2(unity_CameraProjection._11, unity_CameraProjection._22); | |
return -normalize(float3((Input * 2 - 1) / POneOneTwoTwo, -1)); | |
} | |
float ConvertSobelToEdgeMask(in const float Sobel, in const float Threshold, in const float Tightening, in const float Strength) | |
{ | |
return pow(smoothstep(0, Threshold, Sobel), Tightening) * Strength; | |
} | |
struct DepthAndNormalSobel | |
{ | |
float NormalSobel; | |
float DepthSobel; | |
}; | |
DepthAndNormalSobel CalculateDepthAndNormalSobel(in const float2 UV, in const float Thickness) | |
{ | |
float2 SobelD = 0, SobelX = 0, SobelY = 0, SobelZ = 0; | |
[unroll] for (int Iterator = 0; Iterator < 9; ++Iterator) | |
{ | |
// scene data | |
const float Depth = SampleSceneDepth(UV + GSobelSamplePoints[Iterator] * Thickness); | |
const float3 Normal = SampleDepthNormalMap(UV + GSobelSamplePoints[Iterator] * Thickness).Normal; | |
// calculation data | |
const float2 Kernel = float2(GSobelXMatrix[Iterator], GSobelYMatrix[Iterator]); | |
SobelD += Depth * Kernel; | |
SobelX += Normal.x * Kernel; | |
SobelY += Normal.y * Kernel; | |
SobelZ += Normal.z * Kernel; | |
} | |
DepthAndNormalSobel Output; | |
Output.DepthSobel = length(SobelD); | |
Output.NormalSobel = max(max(length(SobelX), length(SobelY)), length(SobelZ)); | |
return Output; | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment