Skip to content

Instantly share code, notes, and snippets.

@herohiralal
Created June 29, 2021 02:20
Show Gist options
  • Save herohiralal/f64afe56f602d11a74cc7dd225140250 to your computer and use it in GitHub Desktop.
Save herohiralal/f64afe56f602d11a74cc7dd225140250 to your computer and use it in GitHub Desktop.
URP Sobel Edge Detection
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
}
}
}
#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