You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

139 lines
6.9 KiB

Shader "Custom/TiltShift"
{
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
ZTest Always ZWrite Off Cull Off Blend Off
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
// DeclareDepthTexture provides TEXTURE2D_X_FLOAT(_CameraDepthTexture) and SampleSceneDepth().
// Include unconditionally; it is only sampled in DEPTH_MODE branches.
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
// ── Mode selection ──────────────────────────────────────────────────────────
// DEPTH_MODE: Scheimpflug-based (uses depth buffer). Default: screen-space.
#pragma multi_compile_local _ DEPTH_MODE
// ── Shared parameters ───────────────────────────────────────────────────────
float _TiltShift_MaxBlurRadius; // max blur in pixels
int _TiltShift_SampleCount; // samples per side per pass
// ── Screen-space mode ───────────────────────────────────────────────────────
float _TiltShift_CenterOffset; // vertical offset of sharp band (UV units, +up)
float _TiltShift_FocusWidth; // full height of sharp band as fraction of screen
float _TiltShift_FalloffRange; // blur fade-in distance in UV units
// ── Depth mode (Scheimpflug / tilt plane) ───────────────────────────────────
// _CameraDepthTexture and _ZBufferParams are provided by URP / DeclareDepthTexture.hlsl.
float _TiltShift_FocusDistance; // world-space depth of the focal plane centre
float _TiltShift_TiltFactor; // world-space depth shift per UV unit of screen height
// positive → top focuses farther (typical bird's-eye)
float _TiltShift_FocusBand; // half-width of the sharp zone in world units
float _TiltShift_DepthFalloff; // blur ramp width in world units
// ── Blur radius computation ─────────────────────────────────────────────────
float ComputeBlurRadius(float2 uv)
{
#if defined(DEPTH_MODE)
// ----------------------------------------------------------------
// Scheimpflug tilt-plane CoC
// SampleSceneDepth uses TEXTURE2D_X_FLOAT + point-clamp (from DeclareDepthTexture.hlsl).
// ----------------------------------------------------------------
float rawDepth = SampleSceneDepth(uv);
float eyeDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
// Depth of the focus plane at this screen row:
// uv.y = 0 → bottom, uv.y = 1 → top (OpenGL-style; URP handles flip internally)
float focusAtY = _TiltShift_FocusDistance + _TiltShift_TiltFactor * (uv.y - 0.5);
float distToPlane = abs(eyeDepth - focusAtY);
float band = max(_TiltShift_FocusBand, 0.001);
float falloff = max(_TiltShift_DepthFalloff, 0.001);
return smoothstep(band, band + falloff, distToPlane) * _TiltShift_MaxBlurRadius;
#else
// ----------------------------------------------------------------
// Screen-space approximation (no depth buffer needed)
// ----------------------------------------------------------------
float dist = abs(uv.y - 0.5 + _TiltShift_CenterOffset);
float halfFocus = _TiltShift_FocusWidth * 0.5;
float range = max(_TiltShift_FalloffRange, 0.001);
return smoothstep(halfFocus, halfFocus + range, dist) * _TiltShift_MaxBlurRadius;
#endif
}
// ── Separable Gaussian blur ─────────────────────────────────────────────────
// stepUV = one sample offset in UV space (direction × pixelSize)
float4 GaussianBlur(float2 uv, float2 stepUV)
{
int n = max(1, _TiltShift_SampleCount);
float4 color = 0;
float totalWeight = 0;
[loop]
for (int i = -n; i <= n; i++)
{
// t ∈ [−1, 1]; gaussian shaped so weight → ~0 at the extremes
float t = float(i) / float(n);
float weight = exp(-4.5 * t * t);
color += SAMPLE_TEXTURE2D_X_LOD(_BlitTexture, sampler_LinearClamp, uv + stepUV * float(i), 0) * weight;
totalWeight += weight;
}
return color / totalWeight;
}
// ── Fragment shaders ────────────────────────────────────────────────────────
float4 HorizontalBlur(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
float2 uv = input.texcoord;
float radius = ComputeBlurRadius(uv);
if (radius < 0.5)
return SAMPLE_TEXTURE2D_X_LOD(_BlitTexture, sampler_LinearClamp, uv, 0);
int n = max(1, _TiltShift_SampleCount);
float2 stepUV = float2((radius / float(n)) * _BlitTexture_TexelSize.x, 0.0);
return GaussianBlur(uv, stepUV);
}
float4 VerticalBlur(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
float2 uv = input.texcoord;
float radius = ComputeBlurRadius(uv);
if (radius < 0.5)
return SAMPLE_TEXTURE2D_X_LOD(_BlitTexture, sampler_LinearClamp, uv, 0);
int n = max(1, _TiltShift_SampleCount);
float2 stepUV = float2(0.0, (radius / float(n)) * _BlitTexture_TexelSize.y);
return GaussianBlur(uv, stepUV);
}
ENDHLSL
// Pass 0 — Horizontal blur
Pass
{
Name "TiltShift_H"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment HorizontalBlur
ENDHLSL
}
// Pass 1 — Vertical blur
Pass
{
Name "TiltShift_V"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment VerticalBlur
ENDHLSL
}
}
}