Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Shader "Hidden/ScreenSpaceLocalReflection"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "" {}
}
SubShader
{
Blend Off
ZTest Always
ZWrite Off
Cull Off
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _CameraGBufferTexture2;
sampler2D _CameraDepthTexture;
sampler2D _ReflectionTexture;
sampler2D _PreDepthTexture;
sampler2D _PreAccumulationTexture;
sampler2D _AccumulationTexture;
float4 _ReflectionTexture_TexelSize;
float4x4 _InvViewProj;
float4x4 _ViewProj;
float4x4 _PreViewProj;
float ComputeDepth(float4 clippos)
{
#if defined(SHADER_TARGET_GLSL) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)
return (clippos.z / clippos.w) * 0.5 + 0.5;
#else
return clippos.z / clippos.w;
#endif
}
float noise(float2 seed)
{
return frac(sin(dot(seed.xy, float2(12.9898, 78.233))) * 43758.5453);
}
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float4 screenPos : TEXCOORD0;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.screenPos = ComputeScreenPos(o.vertex);
return o;
}
v2f vert_fullscreen(appdata v)
{
v2f o;
o.vertex = v.vertex;
o.screenPos = ComputeScreenPos(o.vertex);
return o;
}
float4 frag_reflection(v2f i) : SV_Target
{
float2 uv = i.screenPos.xy / i.screenPos.w;
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
if (depth >= 1.0) return float4(0, 0, 0, 0);
float2 spos = 2.0 * uv - 1.0;
float4 pos = mul(_InvViewProj, float4(spos, depth, 1.0));
pos = pos / pos.w;
float3 camDir = normalize(pos - _WorldSpaceCameraPos);
float3 normal = tex2D(_CameraGBufferTexture2, uv).xyz * 2.0 - 1.0;
float3 refDir = normalize(camDir - 2.0 * dot(camDir, normal) * normal);
int maxRayNum = 30;
float maxLength = 2.0;
float3 step = maxLength / maxRayNum * refDir;
float maxThickness = 0.2 / maxRayNum;
float4 col = float4(0, 0, 0, 0);
for (int n = 1; n <= maxRayNum; ++n) {
float3 ray = (n + noise(uv + _Time.x)) * step;
float3 rayPos = pos + ray;
float4 vpPos = mul(_ViewProj, float4(rayPos, 1.0));
float2 rayUv = vpPos.xy / vpPos.w * 0.5 + 0.5;
if (max(abs(rayUv.x - 0.5), abs(rayUv.y - 0.5)) > 0.5) break;
float rayDepth = ComputeDepth(vpPos);
float gbufferDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, rayUv);
if (rayDepth - gbufferDepth > 0 && rayDepth - gbufferDepth < maxThickness) {
float edgeFactor = 1.0 - pow(2.0 * length(rayUv - 0.5), 2);
float a = 0.5 * pow(min(1.0, (maxLength / 2) / length(ray)), 2.0) * edgeFactor;
a *= pow(length(rayUv - 0.5) / 0.5, 0.5);
col = float4(tex2D(_MainTex, rayUv).xyz, a);
break;
}
}
return col;
}
float4 frag_accumulation(v2f i) : SV_Target
{
// 現在のワールド座標を復元
float2 uv = i.screenPos.xy / i.screenPos.w;
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
if (depth >= 1.0) return float4(0, 0, 0, 0);
float2 spos = 2.0 * uv - 1.0;
float4 pos = mul(_InvViewProj, float4(spos, depth, 1.0));
pos = pos / pos.w;
// 前の UV を復元
float4 preVpPos = mul(_PreViewProj, pos);
float2 preUv = preVpPos.xy / preVpPos.w * 0.5 + 0.5;
float4 accumulation = tex2D(_PreAccumulationTexture, preUv);
float4 reflection = tex2D(_ReflectionTexture, uv);
float blend = 0.2;
return lerp(accumulation, reflection, 0.1);
/*
float2 uv = i.screenPos.xy / i.screenPos.w;
float4 accumulation = tex2D(_PreAccumulationTexture, uv);
float4 reflection = tex2D(_ReflectionTexture, uv);
float blend = 0.2;
return lerp(accumulation, reflection, 0.1);
*/
}
float4 frag_composition(v2f i) : SV_Target
{
float2 uv = i.screenPos.xy / i.screenPos.w;
float4 base = tex2D(_MainTex, uv);
float4 reflection = tex2D(_AccumulationTexture, uv);
float a = reflection.a;
return lerp(base, reflection, a);
}
ENDCG
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_reflection
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vert_fullscreen
#pragma fragment frag_accumulation
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag_composition
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment