1522 lines
57 KiB
Plaintext
1522 lines
57 KiB
Plaintext
|
// Upgrade NOTE: commented out 'float3 _WorldSpaceCameraPos', a built-in variable
|
||
|
|
||
|
Shader "Hidden/ssr_shader"
|
||
|
{
|
||
|
Properties
|
||
|
{
|
||
|
_MainTex ("Texture", 2D) = "white" {}
|
||
|
}
|
||
|
SubShader
|
||
|
{
|
||
|
// No culling or depth
|
||
|
Cull Off ZWrite Off ZTest Never
|
||
|
|
||
|
//reflected color and mask only
|
||
|
Pass //0
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
#pragma shader_feature_local TEMPORAL
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
#include "NormalSample.hlsl"
|
||
|
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
};
|
||
|
|
||
|
v2f vert (appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
o.vertex = UnityObjectToClipPos(v.vertex);
|
||
|
o.uv = v.uv;
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
uniform sampler2D _CameraDepthTexture;
|
||
|
|
||
|
float4x4 _InverseProjectionMatrix;
|
||
|
float4x4 _InverseViewMatrix;
|
||
|
float4x4 _ProjectionMatrix;
|
||
|
float4x4 _ViewMatrix;
|
||
|
|
||
|
uniform sampler2D _MainTex;
|
||
|
uniform sampler2D _GBuffer2; //normals and smoothness
|
||
|
|
||
|
float3 _WorldSpaceViewDir;
|
||
|
float _RenderScale;
|
||
|
float stride;
|
||
|
float numSteps;
|
||
|
float minSmoothness;
|
||
|
int iteration;
|
||
|
|
||
|
inline float ScreenEdgeMask(float2 clipPos) {
|
||
|
float yDif = 1 - abs(clipPos.y);
|
||
|
float xDif = 1 - abs(clipPos.x);
|
||
|
[flatten]
|
||
|
if (yDif < 0 || xDif < 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
float t1 = smoothstep(0, .25, yDif);
|
||
|
float t2 = smoothstep(0, .1, xDif);
|
||
|
return saturate(t2 * t1);
|
||
|
}
|
||
|
float4 frag(v2f i) : SV_Target
|
||
|
{
|
||
|
float rawDepth = tex2D(_CameraDepthTexture, i.uv).r;
|
||
|
|
||
|
|
||
|
|
||
|
[branch]
|
||
|
if (rawDepth == 0) {
|
||
|
return float4(0, 0, 0, 0);
|
||
|
}
|
||
|
float4 gbuff = tex2D(_GBuffer2, i.uv);
|
||
|
|
||
|
|
||
|
|
||
|
float smoothness = gbuff.w;
|
||
|
float stepS = smoothstep(minSmoothness, 1, smoothness);
|
||
|
//float3 normal = normalize(gbuff.xyz);
|
||
|
float3 normal = UnpackNormal(gbuff.xyz);
|
||
|
|
||
|
|
||
|
|
||
|
float4 clipSpace = float4(i.uv * 2 - 1, rawDepth, 1);
|
||
|
float4 viewSpacePosition = mul(_InverseProjectionMatrix, clipSpace);
|
||
|
viewSpacePosition /= viewSpacePosition.w;
|
||
|
viewSpacePosition.y *= -1;
|
||
|
float4 worldSpacePosition = mul(_InverseViewMatrix, viewSpacePosition);
|
||
|
float3 viewDir = normalize(float3(worldSpacePosition.xyz) - _WorldSpaceCameraPos);
|
||
|
float3 reflectionRay = reflect(viewDir, normal);
|
||
|
|
||
|
float3 reflectionRay_v = mul(_ViewMatrix, float4(reflectionRay,0));
|
||
|
reflectionRay_v.z *= -1;
|
||
|
viewSpacePosition.z *= -1;
|
||
|
|
||
|
float normalViewDot = dot(normal, -viewDir);
|
||
|
float viewReflectDot = saturate(dot(viewDir, reflectionRay));
|
||
|
float cameraViewReflectDot = saturate(dot(_WorldSpaceViewDir, reflectionRay));
|
||
|
|
||
|
float thickness = stride * 2;
|
||
|
float oneMinusViewReflectDot = sqrt(1 - viewReflectDot);
|
||
|
stride /= oneMinusViewReflectDot;
|
||
|
thickness /= oneMinusViewReflectDot;
|
||
|
|
||
|
//return viewSpacePosition;
|
||
|
|
||
|
int d = 0;
|
||
|
int hit = 0;
|
||
|
float maskOut = 1;
|
||
|
float3 currentPosition = viewSpacePosition.xyz;
|
||
|
float2 currentScreenSpacePosition = i.uv;
|
||
|
|
||
|
bool doRayMarch = smoothness > minSmoothness;
|
||
|
|
||
|
float maxRayLength = numSteps * stride;
|
||
|
float maxDist = lerp(min(viewSpacePosition.z, maxRayLength), maxRayLength, cameraViewReflectDot);
|
||
|
float numSteps_f = maxDist / stride;
|
||
|
numSteps = max(round(numSteps_f),0);
|
||
|
|
||
|
[branch]
|
||
|
if (doRayMarch) {
|
||
|
|
||
|
float3 ray = reflectionRay_v * stride;
|
||
|
float depthDelta = 0;
|
||
|
|
||
|
[loop]
|
||
|
for (int step = 0; step < numSteps; step++)
|
||
|
{
|
||
|
currentPosition += ray;
|
||
|
|
||
|
float currentDepth;
|
||
|
float2 screenSpace;
|
||
|
|
||
|
float4 uv = mul(_ProjectionMatrix, float4(currentPosition.x, currentPosition.y * -1, currentPosition.z * -1, 1));
|
||
|
uv /= uv.w;
|
||
|
uv.x *= 0.5f;
|
||
|
uv.y *= 0.5f;
|
||
|
uv.x += 0.5f;
|
||
|
uv.y += 0.5f;
|
||
|
|
||
|
[branch]
|
||
|
if (uv.x >= 1 || uv.x < 0 || uv.y >= 1 || uv.y < 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//sample depth at current screen space
|
||
|
float sampledDepth = tex2D(_CameraDepthTexture, uv.xy).r;
|
||
|
|
||
|
[branch]
|
||
|
//compare the current depth of the current position to the camera depth at the screen space
|
||
|
if (abs(rawDepth - sampledDepth) > 0 && sampledDepth != 0) {
|
||
|
depthDelta = currentPosition.z - LinearEyeDepth(sampledDepth);
|
||
|
|
||
|
[branch]
|
||
|
if (depthDelta > 0) {
|
||
|
currentScreenSpacePosition = uv.xy;
|
||
|
hit = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (depthDelta > thickness) {
|
||
|
hit = 0;
|
||
|
}
|
||
|
|
||
|
const int stepCount = 16;
|
||
|
int binarySearchSteps = stepCount * hit;
|
||
|
|
||
|
[loop]
|
||
|
for (int i = 0; i < binarySearchSteps; i++)
|
||
|
{
|
||
|
ray *= .5f;
|
||
|
[flatten]
|
||
|
if (depthDelta > 0) {
|
||
|
currentPosition -= ray;
|
||
|
}
|
||
|
else if (depthDelta < 0) {
|
||
|
currentPosition += ray;
|
||
|
}
|
||
|
else {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
float4 uv = mul(_ProjectionMatrix, float4(currentPosition.x, currentPosition.y * -1, currentPosition.z * -1, 1));
|
||
|
uv /= uv.w;
|
||
|
maskOut = ScreenEdgeMask(uv);
|
||
|
uv.x *= 0.5f;
|
||
|
uv.y *= 0.5f;
|
||
|
uv.x += 0.5f;
|
||
|
uv.y += 0.5f;
|
||
|
currentScreenSpacePosition = uv;
|
||
|
|
||
|
float sd = tex2D(_CameraDepthTexture, uv.xy).r;
|
||
|
depthDelta = currentPosition.z - LinearEyeDepth(sd);
|
||
|
float minv = 1 / (oneMinusViewReflectDot * float(i));
|
||
|
if (abs(depthDelta) > minv) {
|
||
|
hit = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//remove backface intersections
|
||
|
//float3 currentNormal = tex2D(_GBuffer2, currentScreenSpacePosition).xyz;
|
||
|
float3 currentNormal = UnpackNormal(tex2D(_GBuffer2, currentScreenSpacePosition).xyz);
|
||
|
float backFaceDot = dot(currentNormal, reflectionRay);
|
||
|
[flatten]
|
||
|
if (backFaceDot > 0) {
|
||
|
hit = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
float3 deltaDir = viewSpacePosition.xyz - currentPosition;
|
||
|
float progress = dot(deltaDir, deltaDir) / (maxDist * maxDist);
|
||
|
progress = smoothstep(0, .5, 1 - progress);
|
||
|
|
||
|
float pf = pow(smoothness, 4);
|
||
|
float fresnal = lerp(pf, 1.0, pow(viewReflectDot, 1 / pf));
|
||
|
maskOut *= stepS * hit * fresnal;
|
||
|
|
||
|
return float4(currentScreenSpacePosition, stepS, maskOut * progress);
|
||
|
}
|
||
|
ENDCG
|
||
|
}
|
||
|
|
||
|
//composite
|
||
|
Pass //1
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
#pragma shader_feature_local TEMPORAL
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
#include "NormalSample.hlsl"
|
||
|
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
};
|
||
|
|
||
|
v2f vert(appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
o.vertex = UnityObjectToClipPos(v.vertex);
|
||
|
o.uv = v.uv;
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
float _RenderScale;
|
||
|
inline float Dither8x8(float2 ScreenPosition, float c0)
|
||
|
{
|
||
|
c0 *= 2;
|
||
|
const float dither[64] =
|
||
|
{
|
||
|
0, 32, 8, 40, 2, 34, 10, 42,
|
||
|
48, 16, 56, 24, 50, 18, 58, 26,
|
||
|
12, 44, 4, 36, 14, 46, 6, 38,
|
||
|
60, 28, 52, 20, 62, 30, 54, 22,
|
||
|
3, 35, 11, 43, 1, 33, 9, 41,
|
||
|
51, 19, 59, 27, 49, 17, 57, 25,
|
||
|
15, 47, 7, 39, 13, 45, 5, 37,
|
||
|
63, 31, 55, 23, 61, 29, 53, 21
|
||
|
};
|
||
|
|
||
|
float2 uv = ScreenPosition.xy * _ScreenParams.xy;
|
||
|
|
||
|
uint index = (uint(uv.x) % 8) * 8 + uint(uv.y) % 8;
|
||
|
|
||
|
float limit = float(dither[index] + 1) / 64.0;
|
||
|
return saturate(c0 - limit);
|
||
|
}
|
||
|
|
||
|
inline float IGN(int pixelX, int pixelY, int frame)
|
||
|
{
|
||
|
frame = frame % 64; // need to periodically reset frame to avoid numerical issues
|
||
|
float x = float(pixelX) + 5.588238f * float(frame);
|
||
|
float y = float(pixelY) + 5.588238f * float(frame);
|
||
|
return fmod(52.9829189f * fmod(0.06711056f * float(x) + 0.00583715f * float(y), 1.0f), 1.0f);
|
||
|
}
|
||
|
|
||
|
|
||
|
uniform sampler2D _GBuffer1; //metalness color
|
||
|
uniform sampler2D _ReflectedColorMap; //contains reflected uv coordinates
|
||
|
uniform sampler2D _MainTex; //main screen color
|
||
|
uniform sampler2D _GBuffer2; //normals and smoothness
|
||
|
float4x4 _InverseProjectionMatrix;
|
||
|
float4x4 _InverseViewMatrix;
|
||
|
float4x4 _ProjectionMatrix;
|
||
|
float4x4 _ViewMatrix;
|
||
|
|
||
|
float4 frag(v2f i) : SV_Target
|
||
|
{
|
||
|
float dither = Dither8x8(i.uv.xy * _RenderScale, .5);
|
||
|
//float dither = IGN(i.uv.x * _ScreenParams.x, i.uv.y * _ScreenParams.y, 0);
|
||
|
|
||
|
float ditherSign0 = ((floor(i.uv.y * _RenderScale * _ScreenParams.y)) % 2) * 2 - 1;
|
||
|
float ditherSign1 = ((floor(i.uv.x * _RenderScale * _ScreenParams.x)) % 2) * 2 - 1;
|
||
|
float ditherSign = ditherSign0 * ditherSign1;
|
||
|
|
||
|
float4 maint = tex2D(_MainTex, i.uv);
|
||
|
float4 reflectedUv = tex2D(_ReflectedColorMap, i.uv);
|
||
|
|
||
|
//return reflectedUv;
|
||
|
|
||
|
float4 normal = tex2D(_GBuffer2, i.uv);
|
||
|
normal.xyz = UnpackNormal(normal.xyz);
|
||
|
normal = mul(_ViewMatrix, float4(normal.xyz,0));
|
||
|
normal = mul(_ProjectionMatrix, float4(normal.xyz,0));
|
||
|
normal.y *= -1;
|
||
|
|
||
|
float stepS = reflectedUv.z;
|
||
|
|
||
|
float maskVal = saturate(reflectedUv.w);
|
||
|
|
||
|
reflectedUv += normal * lerp(dither * 0.05f, 0, stepS) * ditherSign;
|
||
|
|
||
|
float lumin = Luminance(maint);
|
||
|
lumin -= 1;
|
||
|
lumin = saturate(lumin);
|
||
|
|
||
|
float2 gb1 = tex2D(_GBuffer1, i.uv.xy).ra;
|
||
|
|
||
|
float fm = clamp(gb1.x, .3, 1); //smoothness and metalness
|
||
|
fm = clamp(fm, 0, 1 - lumin);
|
||
|
float ff = 1 - fm;
|
||
|
|
||
|
float4 reflectedTexture = tex2Dlod(_MainTex, float4(reflectedUv.xy,0,lerp(0, 5, 1 - stepS)));
|
||
|
|
||
|
float ao = gb1.y;
|
||
|
float refw = maskVal * ao;
|
||
|
float4 res = lerp(maint, maint * ff + reflectedTexture * fm, refw);
|
||
|
|
||
|
return res;
|
||
|
//return float4(ditherSign1 * ditherSign0,0,0,0);
|
||
|
//return float4(dither,0,0,0);
|
||
|
}
|
||
|
ENDCG
|
||
|
}
|
||
|
|
||
|
//reflected color and mask only using hi z tracing
|
||
|
Pass //2
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
#define HIZ_START_LEVEL 0
|
||
|
#define HIZ_MAX_LEVEL 10
|
||
|
#define HIZ_STOP_LEVEL 0
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
#include "NormalSample.hlsl"
|
||
|
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
};
|
||
|
|
||
|
v2f vert(appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
o.vertex = UnityObjectToClipPos(v.vertex);
|
||
|
o.uv = v.uv;
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
float4x4 _InverseProjectionMatrix;
|
||
|
float4x4 _InverseViewMatrix;
|
||
|
float4x4 _ProjectionMatrix;
|
||
|
float4x4 _ViewMatrix;
|
||
|
|
||
|
UNITY_DECLARE_TEX2DARRAY(_DepthPyramid);
|
||
|
uniform Buffer<float2> _DepthPyramidScales;
|
||
|
uniform sampler2D _GBuffer2;
|
||
|
uniform sampler2D _CameraDepthTexture;
|
||
|
uniform sampler2D _MainTex;
|
||
|
float4 _MainTex_TexelSize;
|
||
|
float2 _TargetResolution;
|
||
|
|
||
|
float3 _WorldSpaceViewDir;
|
||
|
float _RenderScale;
|
||
|
float stride;
|
||
|
float numSteps;
|
||
|
float minSmoothness;
|
||
|
int iteration;
|
||
|
int reflectSky;
|
||
|
|
||
|
inline float2 getScreenResolution() {
|
||
|
return _TargetResolution;
|
||
|
}
|
||
|
float2 getResolution(int index) {
|
||
|
float scale = exp2(index);
|
||
|
float2 scaledScreen = getScreenResolution() / scale;
|
||
|
//scaledScreen.xy = max(floor(scaledScreen.xy), float2(1,1));
|
||
|
return scaledScreen.xy;
|
||
|
}
|
||
|
inline float2 scaledUv(float2 uv, int index) {
|
||
|
float2 scaledScreen = getResolution(index);
|
||
|
float2 realScale = scaledScreen.xy / getScreenResolution();
|
||
|
uv *= realScale;
|
||
|
return uv;
|
||
|
}
|
||
|
inline float SampleDepth(float2 uv, int index) {
|
||
|
uv = scaledUv(uv, index);
|
||
|
return UNITY_SAMPLE_TEX2DARRAY(_DepthPyramid, float3(uv, index));
|
||
|
}
|
||
|
inline float2 cross_epsilon() {
|
||
|
//float2 scale = _ScreenParams.xy / getResolution(HIZ_START_LEVEL + 1);
|
||
|
//return float2(_MainTex_TexelSize.xy * scale);
|
||
|
return float2(1 / getScreenResolution() / 128);
|
||
|
}
|
||
|
inline float2 cell(float2 ray, float2 cell_count) {
|
||
|
return floor(ray.xy * cell_count);
|
||
|
}
|
||
|
inline float2 cell_count(float level) {
|
||
|
float2 res = getResolution(level);
|
||
|
return res; //_ScreenParams.xy / exp2(level);
|
||
|
}
|
||
|
inline bool crossed_cell_boundary(float2 cell_id_one, float2 cell_id_two) {
|
||
|
return (int)cell_id_one.x != (int)cell_id_two.x || (int)cell_id_one.y != (int)cell_id_two.y;
|
||
|
}
|
||
|
inline float minimum_depth_plane(float2 ray, float level, float2 cell_count) {
|
||
|
|
||
|
return SampleDepth(ray, level);
|
||
|
}
|
||
|
inline float3 intersectDepthPlane(float3 o, float3 d, float t)
|
||
|
{
|
||
|
return o + d * t;
|
||
|
}
|
||
|
inline float3 intersectCellBoundary(float3 o, float3 d, float2 cellIndex, float2 cellCount, float2 crossStep, float2 crossOffset, float iteration)
|
||
|
{
|
||
|
float2 cell_size = 1.0 / cellCount;
|
||
|
float2 planes = cellIndex / cellCount + cell_size * crossStep;
|
||
|
float2 solutions = (planes - o) / d.xy;
|
||
|
float3 intersection_pos = o + d * min(solutions.x, solutions.y);
|
||
|
|
||
|
//magic scale, it helps with some artifacts
|
||
|
crossOffset.xy *= 16;
|
||
|
|
||
|
intersection_pos.xy += (solutions.x < solutions.y) ? float2(crossOffset.x, 0.0) : float2(0.0, crossOffset.y);
|
||
|
return intersection_pos;
|
||
|
|
||
|
//float2 cell_size = 1.0 / cellCount;
|
||
|
//float2 planes = cellIndex / cellCount + cell_size * crossStep + crossOffset * 50;
|
||
|
//float2 solutions = (planes - o.xy) / d.xy;
|
||
|
//float3 intersection_pos = o + d * min(solutions.x, solutions.y);
|
||
|
//return intersection_pos;
|
||
|
}
|
||
|
|
||
|
|
||
|
float3 hiZTrace(float3 p, float3 v, float MaxIterations, out float hit, out float iterations, out bool isSky)
|
||
|
{
|
||
|
const float rootLevel = HIZ_MAX_LEVEL;
|
||
|
float level = HIZ_START_LEVEL;
|
||
|
|
||
|
iterations = 0;
|
||
|
isSky = false;
|
||
|
hit = 0;
|
||
|
|
||
|
[branch]
|
||
|
if (v.z <= 0) {
|
||
|
return float3(0, 0, 0);
|
||
|
}
|
||
|
|
||
|
// scale vector such that z is 1.0f (maximum depth)
|
||
|
float3 d = v.xyz / v.z;
|
||
|
|
||
|
// get the cell cross direction and a small offset to enter the next cell when doing cell crossing
|
||
|
float2 crossStep = float2(d.x >= 0.0f ? 1.0f : -1.0f, d.y >= 0.0f ? 1.0f : -1.0f);
|
||
|
float2 crossOffset = float2(crossStep.xy * cross_epsilon());
|
||
|
crossStep.xy = saturate(crossStep.xy);
|
||
|
|
||
|
// set current ray to original screen coordinate and depth
|
||
|
float3 ray = p.xyz;
|
||
|
|
||
|
// cross to next cell to avoid immediate self-intersection
|
||
|
float2 rayCell = cell(ray.xy, cell_count(level));
|
||
|
|
||
|
ray = intersectCellBoundary(ray, d, rayCell.xy, cell_count(level), crossStep.xy, crossOffset.xy, 0);
|
||
|
|
||
|
[loop]
|
||
|
while (level >= HIZ_STOP_LEVEL && iterations < MaxIterations)
|
||
|
{
|
||
|
// get the cell number of the current ray
|
||
|
const float2 cellCount = cell_count(level);
|
||
|
const float2 oldCellIdx = cell(ray.xy, cellCount);
|
||
|
|
||
|
// get the minimum depth plane in which the current ray resides
|
||
|
float minZ = minimum_depth_plane(ray.xy, level, rootLevel);
|
||
|
|
||
|
// intersect only if ray depth is below the minimum depth plane
|
||
|
float3 tmpRay = ray;
|
||
|
|
||
|
float min_minus_ray = minZ - ray.z;
|
||
|
|
||
|
tmpRay = min_minus_ray > 0 ? intersectDepthPlane(tmpRay, d, min_minus_ray) : tmpRay;
|
||
|
// get the new cell number as well
|
||
|
const float2 newCellIdx = cell(tmpRay.xy, cellCount);
|
||
|
// if the new cell number is different from the old cell number, a cell was crossed
|
||
|
[branch]
|
||
|
if (crossed_cell_boundary(oldCellIdx, newCellIdx))
|
||
|
{
|
||
|
// intersect the boundary of that cell instead, and go up a level for taking a larger step next iteration
|
||
|
tmpRay = intersectCellBoundary(ray, d, oldCellIdx, cellCount.xy, crossStep.xy, crossOffset.xy, iterations); //// NOTE added .xy to o and d arguments
|
||
|
level = min(HIZ_MAX_LEVEL, level + 2.0f);
|
||
|
}
|
||
|
else if (level == HIZ_START_LEVEL) {
|
||
|
float minZOffset = (minZ + (_ProjectionParams.y * 0.0025) / LinearEyeDepth(1 - p.z));
|
||
|
|
||
|
isSky = minZ == 1 ? true : false;
|
||
|
|
||
|
[branch]
|
||
|
if (tmpRay.z > minZOffset || (reflectSky == 0 && isSky)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// go down a level in the hi-z buffer
|
||
|
--level;
|
||
|
|
||
|
ray.xyz = tmpRay.xyz;
|
||
|
++iterations;
|
||
|
}
|
||
|
hit = level < HIZ_STOP_LEVEL ? 1 : 0;
|
||
|
return ray;
|
||
|
}
|
||
|
|
||
|
inline float ScreenEdgeMask(float2 clipPos) {
|
||
|
float yDif = 1 - abs(clipPos.y);
|
||
|
float xDif = 1 - abs(clipPos.x);
|
||
|
[flatten]
|
||
|
if (yDif < 0 || xDif < 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
float t1 = smoothstep(0, .2, yDif);
|
||
|
float t2 = smoothstep(0, .1, xDif);
|
||
|
return saturate(t2 * t1);
|
||
|
}
|
||
|
|
||
|
float4 frag(v2f i) : SV_Target
|
||
|
{
|
||
|
|
||
|
float rawDepth = tex2D(_CameraDepthTexture, i.uv).r;
|
||
|
|
||
|
[branch]
|
||
|
if (rawDepth == 0) {
|
||
|
return float4(i.uv.xy, 0, 0);
|
||
|
}
|
||
|
float4 gbuff = tex2D(_GBuffer2, i.uv);
|
||
|
float smoothness = gbuff.w;
|
||
|
bool doRayMarch = smoothness > minSmoothness;
|
||
|
[branch]
|
||
|
if (!doRayMarch) {
|
||
|
return float4(i.uv.xy, 0, 0);
|
||
|
}
|
||
|
float stepS = smoothstep(minSmoothness, 1, smoothness);
|
||
|
//float3 normal = normalize(gbuff.xyz);
|
||
|
float3 normal = UnpackNormal(gbuff.xyz);
|
||
|
|
||
|
float4 clipSpace = float4(i.uv * 2 - 1, rawDepth, 1);
|
||
|
clipSpace.y *= -1;
|
||
|
|
||
|
float4 viewSpacePosition = mul(_InverseProjectionMatrix, clipSpace);
|
||
|
viewSpacePosition /= viewSpacePosition.w;
|
||
|
float4 worldSpacePosition = mul(_InverseViewMatrix, viewSpacePosition);
|
||
|
float3 viewDir = normalize(float3(worldSpacePosition.xyz) - _WorldSpaceCameraPos);
|
||
|
float3 reflectionRay_w = reflect(viewDir, normal);
|
||
|
float3 reflectionRay_v = mul(_ViewMatrix, float4(reflectionRay_w,0));
|
||
|
|
||
|
|
||
|
float3 vReflectionEndPosInVS = viewSpacePosition + reflectionRay_v * (viewSpacePosition.z * -1);
|
||
|
float4 vReflectionEndPosInCS = mul(_ProjectionMatrix, float4(vReflectionEndPosInVS.xyz, 1));
|
||
|
vReflectionEndPosInCS /= vReflectionEndPosInCS.w;
|
||
|
|
||
|
|
||
|
vReflectionEndPosInCS.z = 1 - (vReflectionEndPosInCS.z);
|
||
|
clipSpace.z = 1 - (clipSpace.z);
|
||
|
|
||
|
float3 outReflDirInTS = normalize((vReflectionEndPosInCS - clipSpace).xyz);
|
||
|
outReflDirInTS.xy *= float2(0.5f, -0.5f);
|
||
|
float3 outSamplePosInTS = float3(i.uv, clipSpace.z);
|
||
|
|
||
|
float viewNormalDot = dot(-viewDir, normal);
|
||
|
float viewReflectDot = saturate(dot(viewDir, reflectionRay_w));
|
||
|
float ddd = saturate(dot(_WorldSpaceViewDir, reflectionRay_w));
|
||
|
|
||
|
float hit = 0;
|
||
|
float mask = smoothstep(0, 0.1f, ddd);
|
||
|
|
||
|
[branch]
|
||
|
if (mask == 0) {
|
||
|
return float4(i.uv.xy, 0, 0);
|
||
|
}
|
||
|
|
||
|
float iterations;
|
||
|
bool isSky;
|
||
|
float3 intersectPoint = hiZTrace(outSamplePosInTS, outReflDirInTS, numSteps, hit, iterations, isSky);
|
||
|
float edgeMask = ScreenEdgeMask(intersectPoint.xy * 2 - 1);
|
||
|
mask *= hit * edgeMask;
|
||
|
|
||
|
//remove backface intersections
|
||
|
//float3 currentNormal = tex2D(_GBuffer2, intersectPoint.xy).xyz;
|
||
|
float3 currentNormal = UnpackNormal(tex2D(_GBuffer2, intersectPoint.xy).xyz);
|
||
|
float backFaceDot = dot(currentNormal, reflectionRay_w);
|
||
|
mask = backFaceDot > 0 && !isSky ? 0 : mask;
|
||
|
|
||
|
float pf = pow(smoothness, 4);
|
||
|
float fresnal = lerp(pf, 1.0, pow(viewReflectDot, 1 / pf));
|
||
|
|
||
|
//float4 color = tex2D(_MainTex, intersectPoint.xy);
|
||
|
return float4(intersectPoint.xy, stepS, mask * stepS * fresnal);
|
||
|
//return float4(SampleDepth(i.uv, 7),0,0,0);
|
||
|
//return float4(mcolor * (1 - mask) + color * mask);
|
||
|
//return float4(mask,0,0,0);
|
||
|
}
|
||
|
ENDCG
|
||
|
}//END PASS 2
|
||
|
|
||
|
|
||
|
//PASS 3 - 4 GRAPH
|
||
|
//// GRAPH
|
||
|
Pass //3 BLIT
|
||
|
{
|
||
|
Name "ColorBlitPass"
|
||
|
HLSLPROGRAM
|
||
|
// Core.hlsl includes URP basic variables needed for any shader. The Blit.hlsl provides a
|
||
|
//Vert and Fragment function that abstracts platform differences when handling a full screen shader pass.
|
||
|
//It also declares a _BlitTex texture that is bound by the Blitter API.
|
||
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||
|
//#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
|
||
|
// This is a simple read shader so we use the default provided Vert and FragNearest
|
||
|
//functions. If you would like to do a bilinear sample you could use the FragBilinear functions instead.
|
||
|
#include "BlitSSR.hlsl"//v0.2
|
||
|
|
||
|
#pragma vertex Vert
|
||
|
#pragma fragment FragNearest
|
||
|
ENDHLSL
|
||
|
}
|
||
|
Pass //4 BLIT BACKGROUND
|
||
|
{
|
||
|
Name "ColorBlitPasss"
|
||
|
HLSLPROGRAM
|
||
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||
|
//#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
|
||
|
#include "BlitSSR.hlsl"//v0.2
|
||
|
#pragma vertex Vert
|
||
|
#pragma fragment Frag
|
||
|
//
|
||
|
float4 Frag(VaryingsB input) : SV_Target0
|
||
|
{
|
||
|
// this is needed so we account XR platform differences in how they handle texture arrays
|
||
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||
|
// sample the texture using the SAMPLE_TEXTURE2D_X_LOD
|
||
|
float2 uv = input.texcoord.xy;
|
||
|
half4 color = SAMPLE_TEXTURE2D_X_LOD(_BlitTexture, sampler_LinearRepeat, uv, _BlitMipLevel);
|
||
|
// Inverts the sampled color
|
||
|
//return half4(1, 1, 1, 1) - color;
|
||
|
return color;
|
||
|
}
|
||
|
ENDHLSL
|
||
|
}
|
||
|
//// END GRAPH
|
||
|
//EDN PASS 4
|
||
|
|
||
|
|
||
|
//reflected color and mask only
|
||
|
Pass //0 - 5
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
//#include "BlitSSR.hlsl"//v0.2
|
||
|
//#pragma vertex Vert
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
#pragma shader_feature_local TEMPORAL
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
#include "NormalSample.hlsl"
|
||
|
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
};
|
||
|
|
||
|
|
||
|
//GRAPH BLITTER
|
||
|
// Generates a triangle in homogeneous clip space, s.t.
|
||
|
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).
|
||
|
float2 GetFullScreenTriangleTexCoord(uint vertexID)
|
||
|
{
|
||
|
#if UNITY_UV_STARTS_AT_TOP
|
||
|
return float2((vertexID << 1) & 2, 1.0 - (vertexID & 2));
|
||
|
#else
|
||
|
return float2((vertexID << 1) & 2, vertexID & 2);
|
||
|
#endif
|
||
|
}
|
||
|
float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
|
||
|
{
|
||
|
// note: the triangle vertex position coordinates are x2 so the returned UV coordinates are in range -1, 1 on the screen.
|
||
|
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
|
||
|
float4 pos = float4(uv * 2.0 - 1.0, z, 1.0);
|
||
|
//#ifdef UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION
|
||
|
// pos = ApplyPretransformRotation(pos);
|
||
|
//#endif
|
||
|
return pos;
|
||
|
}
|
||
|
//BLITTER
|
||
|
sampler _BlitTexture;
|
||
|
uniform float4 _BlitScaleBias;
|
||
|
#if SHADER_API_GLES
|
||
|
struct AttributesB
|
||
|
{
|
||
|
float4 vertex : POSITION; // positionOS: POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
//UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
|
uint vertexID : SV_VertexID;
|
||
|
};
|
||
|
#else
|
||
|
struct AttributesB
|
||
|
{
|
||
|
uint vertexID : SV_VertexID;
|
||
|
//UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
v2f vert (AttributesB v)//appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
//o.vertex = UnityObjectToClipPos(v.vertex);
|
||
|
//o.uv = v.uv;
|
||
|
|
||
|
//GRAPH BLITTER
|
||
|
#if SHADER_API_GLES
|
||
|
float4 pos = v.vertex;
|
||
|
float2 uv = v.uv;
|
||
|
#else
|
||
|
float4 pos = GetFullScreenTriangleVertexPosition(v.vertexID);
|
||
|
float2 uv = GetFullScreenTriangleTexCoord(v.vertexID);
|
||
|
#endif
|
||
|
|
||
|
o.vertex = pos;
|
||
|
o.uv = uv * _BlitScaleBias.xy + _BlitScaleBias.zw;
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
uniform sampler2D _CameraDepthTexture;
|
||
|
|
||
|
float4x4 _InverseProjectionMatrix;
|
||
|
float4x4 _InverseViewMatrix;
|
||
|
float4x4 _ProjectionMatrix;
|
||
|
float4x4 _ViewMatrix;
|
||
|
|
||
|
uniform sampler2D _MainTex;
|
||
|
uniform sampler2D _GBuffer2; //normals and smoothness
|
||
|
|
||
|
float3 _WorldSpaceViewDir;
|
||
|
float _RenderScale;
|
||
|
float stride;
|
||
|
float numSteps;
|
||
|
float minSmoothness;
|
||
|
int iteration;
|
||
|
|
||
|
float scaleHorizon;
|
||
|
|
||
|
inline float ScreenEdgeMask(float2 clipPos) {
|
||
|
float yDif = 1 - abs(clipPos.y);
|
||
|
float xDif = 1 - abs(clipPos.x);
|
||
|
[flatten]
|
||
|
if (yDif < 0 || xDif < 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
float t1 = smoothstep(0, .25, yDif);
|
||
|
float t2 = smoothstep(0, .1, xDif);
|
||
|
return saturate(t2 * t1);
|
||
|
}
|
||
|
float4 frag(v2f i) : SV_Target
|
||
|
{
|
||
|
float rawDepth = tex2D(_CameraDepthTexture, i.uv).r;
|
||
|
|
||
|
|
||
|
|
||
|
[branch]
|
||
|
if (rawDepth == 0) {
|
||
|
return float4(0, 0, 0, 0);
|
||
|
}
|
||
|
float4 gbuff = tex2D(_GBuffer2, i.uv);
|
||
|
|
||
|
|
||
|
|
||
|
float smoothness = gbuff.w;
|
||
|
float stepS = smoothstep(minSmoothness, 1, smoothness);
|
||
|
//float3 normal = normalize(gbuff.xyz);
|
||
|
float3 normal = UnpackNormal(gbuff.xyz);
|
||
|
|
||
|
|
||
|
|
||
|
float4 clipSpace = float4(i.uv * 2 - 1, rawDepth, 1);
|
||
|
float4 viewSpacePosition = mul(_InverseProjectionMatrix, clipSpace);
|
||
|
viewSpacePosition /= viewSpacePosition.w;
|
||
|
viewSpacePosition.y *= -1 ;
|
||
|
float4 worldSpacePosition = mul(_InverseViewMatrix, viewSpacePosition);
|
||
|
float3 viewDir = normalize(float3(worldSpacePosition.xyz) - _WorldSpaceCameraPos);
|
||
|
float3 reflectionRay = reflect(viewDir, normal);
|
||
|
|
||
|
float3 reflectionRay_v = mul(_ViewMatrix, float4(reflectionRay,0));
|
||
|
reflectionRay_v.z *= -1;
|
||
|
viewSpacePosition.z *= -1;
|
||
|
|
||
|
float normalViewDot = dot(normal, -viewDir);
|
||
|
float viewReflectDot = saturate(dot(viewDir, reflectionRay));
|
||
|
float cameraViewReflectDot = saturate(dot(_WorldSpaceViewDir, reflectionRay));
|
||
|
|
||
|
float thickness = stride * 2;
|
||
|
float oneMinusViewReflectDot = sqrt(1 - viewReflectDot);
|
||
|
stride /= oneMinusViewReflectDot;
|
||
|
thickness /= oneMinusViewReflectDot;
|
||
|
|
||
|
//return viewSpacePosition;
|
||
|
|
||
|
int d = 0;
|
||
|
int hit = 0;
|
||
|
float maskOut = 1;
|
||
|
float3 currentPosition = viewSpacePosition.xyz;
|
||
|
float2 currentScreenSpacePosition = i.uv;
|
||
|
|
||
|
bool doRayMarch = smoothness > minSmoothness;
|
||
|
|
||
|
float maxRayLength = numSteps * stride;
|
||
|
float maxDist = lerp(min(viewSpacePosition.z, maxRayLength), maxRayLength, cameraViewReflectDot);
|
||
|
float numSteps_f = maxDist / stride;
|
||
|
numSteps = max(round(numSteps_f),0);
|
||
|
|
||
|
float backgroundFactor = 0;
|
||
|
|
||
|
[branch]
|
||
|
if (doRayMarch) {
|
||
|
|
||
|
float3 ray = reflectionRay_v * stride;
|
||
|
float depthDelta = 0;
|
||
|
|
||
|
[loop]
|
||
|
for (int step = 0; step < numSteps; step++)
|
||
|
{
|
||
|
currentPosition += ray;
|
||
|
|
||
|
// float currentDepth;
|
||
|
// float2 screenSpace;
|
||
|
|
||
|
float4 uv = mul(_ProjectionMatrix, float4(currentPosition.x, currentPosition.y * -1, currentPosition.z * -1, 1));
|
||
|
uv /= uv.w;
|
||
|
uv.x *= 0.5f;
|
||
|
uv.y *= 0.5f;
|
||
|
uv.x += 0.5f;
|
||
|
uv.y += 0.5f;
|
||
|
|
||
|
[branch]
|
||
|
if (uv.x >= 1 || uv.x < 0 || uv.y >= 1 || uv.y < 0) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//sample depth at current screen space
|
||
|
float sampledDepth = tex2D(_CameraDepthTexture, uv.xy).r;
|
||
|
|
||
|
[branch]
|
||
|
//compare the current depth of the current position to the camera depth at the screen space
|
||
|
if (abs(rawDepth - sampledDepth) > 0 && sampledDepth != 0) {
|
||
|
depthDelta = currentPosition.z - LinearEyeDepth(sampledDepth);
|
||
|
|
||
|
if( Linear01Depth(sampledDepth) > 0.00000000000001){
|
||
|
backgroundFactor = 0.8;
|
||
|
}
|
||
|
|
||
|
[branch]
|
||
|
if (depthDelta > 0) {
|
||
|
currentScreenSpacePosition = uv.xy;
|
||
|
hit = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (depthDelta > thickness) {
|
||
|
hit = 0;
|
||
|
}
|
||
|
|
||
|
const int stepCount = 16;
|
||
|
int binarySearchSteps = stepCount * hit;
|
||
|
|
||
|
[loop]
|
||
|
for (int i = 0; i < binarySearchSteps; i++)
|
||
|
{
|
||
|
ray *= .5f;
|
||
|
[flatten]
|
||
|
if (depthDelta > 0) {
|
||
|
currentPosition -= ray;
|
||
|
}
|
||
|
else if (depthDelta < 0) {
|
||
|
currentPosition += ray;
|
||
|
}
|
||
|
else {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
float4 uv = mul(_ProjectionMatrix, float4(currentPosition.x, currentPosition.y * -1, currentPosition.z * -1, 1));
|
||
|
uv /= uv.w;
|
||
|
maskOut = ScreenEdgeMask(uv);
|
||
|
uv.x *= 0.5f;
|
||
|
uv.y *= 0.5f;
|
||
|
uv.x += 0.5f;
|
||
|
uv.y += 0.5f;
|
||
|
currentScreenSpacePosition = uv;
|
||
|
|
||
|
float sd = tex2D(_CameraDepthTexture, uv.xy).r;
|
||
|
depthDelta = currentPosition.z - LinearEyeDepth(sd);
|
||
|
float minv = 1 / (oneMinusViewReflectDot * float(i));
|
||
|
if (abs(depthDelta) > minv) {
|
||
|
hit = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//remove backface intersections
|
||
|
//float3 currentNormal = tex2D(_GBuffer2, currentScreenSpacePosition).xyz;
|
||
|
float3 currentNormal = UnpackNormal(tex2D(_GBuffer2, currentScreenSpacePosition).xyz);
|
||
|
float backFaceDot = dot(currentNormal, reflectionRay);
|
||
|
[flatten]
|
||
|
if (backFaceDot > 0) {
|
||
|
hit = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
float3 deltaDir = viewSpacePosition.xyz - currentPosition;
|
||
|
float progress = dot(deltaDir, deltaDir) / (maxDist * maxDist);
|
||
|
progress = smoothstep(0, .5, 1 - progress);
|
||
|
|
||
|
float pf = pow(smoothness, 4);
|
||
|
float fresnal = lerp(pf, 1.0, pow(viewReflectDot, 1 / pf));
|
||
|
maskOut *= stepS * hit * fresnal;
|
||
|
|
||
|
|
||
|
//float sampledDepthA = tex2D(_CameraDepthTexture, i.uv.xy).r;
|
||
|
//if( Linear01Depth(sampledDepthA) < 0.02){
|
||
|
// backgroundFactor = 0;
|
||
|
//}
|
||
|
|
||
|
return float4(currentScreenSpacePosition+float2(0,-0.14*scaleHorizon/10), stepS, maskOut * progress + backgroundFactor);
|
||
|
}
|
||
|
ENDCG
|
||
|
}//END PASS 5
|
||
|
|
||
|
//composite
|
||
|
Pass //1 - 6
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
#pragma shader_feature_local TEMPORAL
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
#include "NormalSample.hlsl"
|
||
|
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
};
|
||
|
|
||
|
//GRAPH BLITTER
|
||
|
// Generates a triangle in homogeneous clip space, s.t.
|
||
|
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).
|
||
|
float2 GetFullScreenTriangleTexCoord(uint vertexID)
|
||
|
{
|
||
|
#if UNITY_UV_STARTS_AT_TOP
|
||
|
return float2((vertexID << 1) & 2, 1.0 - (vertexID & 2));
|
||
|
#else
|
||
|
return float2((vertexID << 1) & 2, vertexID & 2);
|
||
|
#endif
|
||
|
}
|
||
|
float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
|
||
|
{
|
||
|
// note: the triangle vertex position coordinates are x2 so the returned UV coordinates are in range -1, 1 on the screen.
|
||
|
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
|
||
|
float4 pos = float4(uv * 2.0 - 1.0, z, 1.0);
|
||
|
//#ifdef UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION
|
||
|
// pos = ApplyPretransformRotation(pos);
|
||
|
//#endif
|
||
|
return pos;
|
||
|
}
|
||
|
//BLITTER
|
||
|
sampler _BlitTexture;
|
||
|
uniform float4 _BlitScaleBias;
|
||
|
#if SHADER_API_GLES
|
||
|
struct AttributesB
|
||
|
{
|
||
|
float4 vertex : POSITION;// positionOS : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
//UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
|
uint vertexID : SV_VertexID;
|
||
|
};
|
||
|
#else
|
||
|
struct AttributesB
|
||
|
{
|
||
|
uint vertexID : SV_VertexID;
|
||
|
//UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
v2f vert (AttributesB v)//appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
//o.vertex = UnityObjectToClipPos(v.vertex);
|
||
|
//o.uv = v.uv;
|
||
|
|
||
|
//GRAPH BLITTER
|
||
|
#if SHADER_API_GLES
|
||
|
float4 pos = v.vertex;
|
||
|
float2 uv = v.uv;
|
||
|
#else
|
||
|
float4 pos = GetFullScreenTriangleVertexPosition(v.vertexID);
|
||
|
float2 uv = GetFullScreenTriangleTexCoord(v.vertexID);
|
||
|
#endif
|
||
|
|
||
|
o.vertex = pos;
|
||
|
o.uv = uv * _BlitScaleBias.xy + _BlitScaleBias.zw;
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
|
||
|
float _RenderScale;
|
||
|
inline float Dither8x8(float2 ScreenPosition, float c0)
|
||
|
{
|
||
|
c0 *= 2;
|
||
|
const float dither[64] =
|
||
|
{
|
||
|
0, 32, 8, 40, 2, 34, 10, 42,
|
||
|
48, 16, 56, 24, 50, 18, 58, 26,
|
||
|
12, 44, 4, 36, 14, 46, 6, 38,
|
||
|
60, 28, 52, 20, 62, 30, 54, 22,
|
||
|
3, 35, 11, 43, 1, 33, 9, 41,
|
||
|
51, 19, 59, 27, 49, 17, 57, 25,
|
||
|
15, 47, 7, 39, 13, 45, 5, 37,
|
||
|
63, 31, 55, 23, 61, 29, 53, 21
|
||
|
};
|
||
|
|
||
|
float2 uv = ScreenPosition.xy * _ScreenParams.xy;
|
||
|
|
||
|
uint index = (uint(uv.x) % 8) * 8 + uint(uv.y) % 8;
|
||
|
|
||
|
float limit = float(dither[index] + 1) / 64.0;
|
||
|
return saturate(c0 - limit);
|
||
|
}
|
||
|
|
||
|
inline float IGN(int pixelX, int pixelY, int frame)
|
||
|
{
|
||
|
frame = frame % 64; // need to periodically reset frame to avoid numerical issues
|
||
|
float x = float(pixelX) + 5.588238f * float(frame);
|
||
|
float y = float(pixelY) + 5.588238f * float(frame);
|
||
|
return fmod(52.9829189f * fmod(0.06711056f * float(x) + 0.00583715f * float(y), 1.0f), 1.0f);
|
||
|
}
|
||
|
|
||
|
|
||
|
uniform sampler2D _GBuffer1; //metalness color
|
||
|
uniform sampler2D _ReflectedColorMap; //contains reflected uv coordinates
|
||
|
uniform sampler2D _MainTex; //main screen color
|
||
|
uniform sampler2D _GBuffer2; //normals and smoothness
|
||
|
float4x4 _InverseProjectionMatrix;
|
||
|
float4x4 _InverseViewMatrix;
|
||
|
float4x4 _ProjectionMatrix;
|
||
|
float4x4 _ViewMatrix;
|
||
|
|
||
|
float scaleHorizon;
|
||
|
|
||
|
float4 frag(v2f i) : SV_Target
|
||
|
{
|
||
|
float dither = Dither8x8(i.uv.xy * _RenderScale, .5);
|
||
|
//float dither = IGN(i.uv.x * _ScreenParams.x, i.uv.y * _ScreenParams.y, 0);
|
||
|
|
||
|
float ditherSign0 = ((floor(i.uv.y * _RenderScale * _ScreenParams.y)) % 2) * 2 - 1;
|
||
|
float ditherSign1 = ((floor(i.uv.x * _RenderScale * _ScreenParams.x)) % 2) * 2 - 1;
|
||
|
float ditherSign = ditherSign0 * ditherSign1;
|
||
|
|
||
|
float4 maint = tex2D(_MainTex, i.uv);
|
||
|
float4 reflectedUv = tex2D(_ReflectedColorMap, i.uv);
|
||
|
|
||
|
//return maint*3;
|
||
|
|
||
|
float4 normal = tex2D(_GBuffer2, i.uv);
|
||
|
normal.xyz = UnpackNormal(normal.xyz);
|
||
|
normal = mul(_ViewMatrix, float4(normal.xyz,0));
|
||
|
normal = mul(_ProjectionMatrix, float4(normal.xyz,0));
|
||
|
normal.y *= -1;
|
||
|
|
||
|
float stepS = reflectedUv.z;
|
||
|
|
||
|
float maskVal = saturate(reflectedUv.w);
|
||
|
|
||
|
reflectedUv += normal * lerp(dither * 0.05f, 0, stepS) * ditherSign;
|
||
|
|
||
|
float lumin = Luminance(maint);
|
||
|
lumin -= 1;
|
||
|
lumin = saturate(lumin);
|
||
|
|
||
|
float2 gb1 = tex2D(_GBuffer1, i.uv.xy).ra;
|
||
|
|
||
|
float fm = clamp(gb1.x, .3, 1); //smoothness and metalness
|
||
|
fm = clamp(fm, 0, 1 - lumin);
|
||
|
float ff = 1 - fm;
|
||
|
|
||
|
/*
|
||
|
if(saturate(reflectedUv.x) == 0){
|
||
|
reflectedUv.x =1;
|
||
|
}
|
||
|
if(saturate(reflectedUv.y) == 0){
|
||
|
reflectedUv.y =1;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
float4 reflectedTexture = tex2Dlod(_MainTex, float4(reflectedUv.xy,0,lerp(0, 5, 1 - stepS)));
|
||
|
|
||
|
//return reflectedUv;
|
||
|
|
||
|
float ao = gb1.y;
|
||
|
float refw = maskVal * ao;
|
||
|
float4 res = lerp(maint, maint * ff + reflectedTexture * fm, refw);
|
||
|
|
||
|
return res;
|
||
|
//return float4(ditherSign1 * ditherSign0,0,0,0);
|
||
|
//return float4(dither,0,0,0);
|
||
|
}
|
||
|
ENDCG
|
||
|
}//END PASS 1 - 6
|
||
|
|
||
|
|
||
|
|
||
|
//reflected color and mask only using hi z tracing
|
||
|
Pass //2 - 7
|
||
|
{
|
||
|
CGPROGRAM
|
||
|
#pragma vertex vert
|
||
|
#pragma fragment frag
|
||
|
#define HIZ_START_LEVEL 0
|
||
|
#define HIZ_MAX_LEVEL 10
|
||
|
#define HIZ_STOP_LEVEL 0
|
||
|
|
||
|
#include "UnityCG.cginc"
|
||
|
#include "NormalSample.hlsl"
|
||
|
|
||
|
struct appdata
|
||
|
{
|
||
|
float4 vertex : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
};
|
||
|
|
||
|
struct v2f
|
||
|
{
|
||
|
float2 uv : TEXCOORD0;
|
||
|
float4 vertex : SV_POSITION;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//GRAPH BLITTER
|
||
|
// Generates a triangle in homogeneous clip space, s.t.
|
||
|
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).
|
||
|
float2 GetFullScreenTriangleTexCoord(uint vertexID)
|
||
|
{
|
||
|
#if UNITY_UV_STARTS_AT_TOP
|
||
|
return float2((vertexID << 1) & 2, 1.0 - (vertexID & 2));
|
||
|
#else
|
||
|
return float2((vertexID << 1) & 2, vertexID & 2);
|
||
|
#endif
|
||
|
}
|
||
|
float4 GetFullScreenTriangleVertexPosition(uint vertexID, float z = UNITY_NEAR_CLIP_VALUE)
|
||
|
{
|
||
|
// note: the triangle vertex position coordinates are x2 so the returned UV coordinates are in range -1, 1 on the screen.
|
||
|
float2 uv = float2((vertexID << 1) & 2, vertexID & 2);
|
||
|
float4 pos = float4(uv * 2.0 - 1.0, z, 1.0);
|
||
|
//#ifdef UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION
|
||
|
// pos = ApplyPretransformRotation(pos);
|
||
|
//#endif
|
||
|
return pos;
|
||
|
}
|
||
|
//BLITTER
|
||
|
sampler _BlitTexture;
|
||
|
uniform float4 _BlitScaleBias;
|
||
|
#if SHADER_API_GLES
|
||
|
struct AttributesB
|
||
|
{
|
||
|
float4 vertex : POSITION;// positionOS : POSITION;
|
||
|
float2 uv : TEXCOORD0;
|
||
|
//UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
|
uint vertexID : SV_VertexID;
|
||
|
};
|
||
|
#else
|
||
|
struct AttributesB
|
||
|
{
|
||
|
uint vertexID : SV_VertexID;
|
||
|
//UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
v2f vert(AttributesB v)//appdata v)
|
||
|
{
|
||
|
v2f o;
|
||
|
// o.vertex = UnityObjectToClipPos(v.vertex);
|
||
|
// o.uv = v.uv;
|
||
|
|
||
|
//GRAPH BLITTER
|
||
|
#if SHADER_API_GLES
|
||
|
float4 pos = v.vertex;
|
||
|
float2 uv = v.uv;
|
||
|
#else
|
||
|
float4 pos = GetFullScreenTriangleVertexPosition(v.vertexID);
|
||
|
float2 uv = GetFullScreenTriangleTexCoord(v.vertexID);
|
||
|
#endif
|
||
|
|
||
|
o.vertex = pos;
|
||
|
o.uv = uv * _BlitScaleBias.xy + _BlitScaleBias.zw;
|
||
|
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
float4x4 _InverseProjectionMatrix;
|
||
|
float4x4 _InverseViewMatrix;
|
||
|
float4x4 _ProjectionMatrix;
|
||
|
float4x4 _ViewMatrix;
|
||
|
|
||
|
UNITY_DECLARE_TEX2DARRAY(_DepthPyramid);
|
||
|
uniform Buffer<float2> _DepthPyramidScales;
|
||
|
uniform sampler2D _GBuffer2;
|
||
|
uniform sampler2D _CameraDepthTexture;
|
||
|
uniform sampler2D _MainTex;
|
||
|
float4 _MainTex_TexelSize;
|
||
|
float2 _TargetResolution;
|
||
|
|
||
|
float3 _WorldSpaceViewDir;
|
||
|
float _RenderScale;
|
||
|
float stride;
|
||
|
float numSteps;
|
||
|
float minSmoothness;
|
||
|
int iteration;
|
||
|
int reflectSky;
|
||
|
|
||
|
inline float2 getScreenResolution() {
|
||
|
return _TargetResolution;
|
||
|
}
|
||
|
float2 getResolution(int index) {
|
||
|
float scale = exp2(index);
|
||
|
float2 scaledScreen = getScreenResolution() / scale;
|
||
|
//scaledScreen.xy = max(floor(scaledScreen.xy), float2(1,1));
|
||
|
return scaledScreen.xy;
|
||
|
}
|
||
|
inline float2 scaledUv(float2 uv, int index) {
|
||
|
float2 scaledScreen = getResolution(index);
|
||
|
float2 realScale = scaledScreen.xy / getScreenResolution();
|
||
|
uv *= realScale;
|
||
|
return uv;
|
||
|
}
|
||
|
inline float SampleDepth(float2 uv, int index) {
|
||
|
uv = scaledUv(uv, index);
|
||
|
return UNITY_SAMPLE_TEX2DARRAY(_DepthPyramid, float3(uv, index));
|
||
|
}
|
||
|
inline float2 cross_epsilon() {
|
||
|
//float2 scale = _ScreenParams.xy / getResolution(HIZ_START_LEVEL + 1);
|
||
|
//return float2(_MainTex_TexelSize.xy * scale);
|
||
|
return float2(1 / getScreenResolution() / 128);
|
||
|
}
|
||
|
inline float2 cell(float2 ray, float2 cell_count) {
|
||
|
return floor(ray.xy * cell_count);
|
||
|
}
|
||
|
inline float2 cell_count(float level) {
|
||
|
float2 res = getResolution(level);
|
||
|
return res; //_ScreenParams.xy / exp2(level);
|
||
|
}
|
||
|
inline bool crossed_cell_boundary(float2 cell_id_one, float2 cell_id_two) {
|
||
|
return (int)cell_id_one.x != (int)cell_id_two.x || (int)cell_id_one.y != (int)cell_id_two.y;
|
||
|
}
|
||
|
inline float minimum_depth_plane(float2 ray, float level, float2 cell_count) {
|
||
|
|
||
|
return SampleDepth(ray, level);
|
||
|
}
|
||
|
inline float3 intersectDepthPlane(float3 o, float3 d, float t)
|
||
|
{
|
||
|
return o + d * t;
|
||
|
}
|
||
|
inline float3 intersectCellBoundary(float3 o, float3 d, float2 cellIndex, float2 cellCount, float2 crossStep, float2 crossOffset, float iteration)
|
||
|
{
|
||
|
float2 cell_size = 1.0 / cellCount;
|
||
|
float2 planes = cellIndex / cellCount + cell_size * crossStep;
|
||
|
float2 solutions = (planes - o) / d.xy;
|
||
|
float3 intersection_pos = o + d * min(solutions.x, solutions.y);
|
||
|
|
||
|
//magic scale, it helps with some artifacts
|
||
|
crossOffset.xy *= 16;
|
||
|
|
||
|
intersection_pos.xy += (solutions.x < solutions.y) ? float2(crossOffset.x, 0.0) : float2(0.0, crossOffset.y);
|
||
|
return intersection_pos;
|
||
|
|
||
|
//float2 cell_size = 1.0 / cellCount;
|
||
|
//float2 planes = cellIndex / cellCount + cell_size * crossStep + crossOffset * 50;
|
||
|
//float2 solutions = (planes - o.xy) / d.xy;
|
||
|
//float3 intersection_pos = o + d * min(solutions.x, solutions.y);
|
||
|
//return intersection_pos;
|
||
|
}
|
||
|
|
||
|
|
||
|
float3 hiZTrace(float3 p, float3 v, float MaxIterations, out float hit, out float iterations, out bool isSky)
|
||
|
{
|
||
|
const float rootLevel = HIZ_MAX_LEVEL;
|
||
|
float level = HIZ_START_LEVEL;
|
||
|
|
||
|
iterations = 0;
|
||
|
isSky = false;
|
||
|
hit = 0;
|
||
|
|
||
|
[branch]
|
||
|
if (v.z <= 0) {
|
||
|
return float3(0, 0, 0);
|
||
|
}
|
||
|
|
||
|
// scale vector such that z is 1.0f (maximum depth)
|
||
|
float3 d = v.xyz / v.z;
|
||
|
|
||
|
// get the cell cross direction and a small offset to enter the next cell when doing cell crossing
|
||
|
float2 crossStep = float2(d.x >= 0.0f ? 1.0f : -1.0f, d.y >= 0.0f ? 1.0f : -1.0f);
|
||
|
float2 crossOffset = float2(crossStep.xy * cross_epsilon());
|
||
|
crossStep.xy = saturate(crossStep.xy);
|
||
|
|
||
|
// set current ray to original screen coordinate and depth
|
||
|
float3 ray = p.xyz;
|
||
|
|
||
|
// cross to next cell to avoid immediate self-intersection
|
||
|
float2 rayCell = cell(ray.xy, cell_count(level));
|
||
|
|
||
|
ray = intersectCellBoundary(ray, d, rayCell.xy, cell_count(level), crossStep.xy, crossOffset.xy, 0);
|
||
|
|
||
|
[loop]
|
||
|
while (level >= HIZ_STOP_LEVEL && iterations < MaxIterations)
|
||
|
{
|
||
|
// get the cell number of the current ray
|
||
|
const float2 cellCount = cell_count(level);
|
||
|
const float2 oldCellIdx = cell(ray.xy, cellCount);
|
||
|
|
||
|
// get the minimum depth plane in which the current ray resides
|
||
|
float minZ = minimum_depth_plane(ray.xy, level, rootLevel);
|
||
|
|
||
|
// intersect only if ray depth is below the minimum depth plane
|
||
|
float3 tmpRay = ray;
|
||
|
|
||
|
float min_minus_ray = minZ - ray.z;
|
||
|
|
||
|
tmpRay = min_minus_ray > 0 ? intersectDepthPlane(tmpRay, d, min_minus_ray) : tmpRay;
|
||
|
// get the new cell number as well
|
||
|
const float2 newCellIdx = cell(tmpRay.xy, cellCount);
|
||
|
// if the new cell number is different from the old cell number, a cell was crossed
|
||
|
[branch]
|
||
|
if (crossed_cell_boundary(oldCellIdx, newCellIdx))
|
||
|
{
|
||
|
// intersect the boundary of that cell instead, and go up a level for taking a larger step next iteration
|
||
|
tmpRay = intersectCellBoundary(ray, d, oldCellIdx, cellCount.xy, crossStep.xy, crossOffset.xy, iterations); //// NOTE added .xy to o and d arguments
|
||
|
level = min(HIZ_MAX_LEVEL, level + 2.0f);
|
||
|
}
|
||
|
else if (level == HIZ_START_LEVEL) {
|
||
|
float minZOffset = (minZ + (_ProjectionParams.y * 0.0025) / LinearEyeDepth(1 - p.z));
|
||
|
|
||
|
isSky = minZ == 1 ? true : false;
|
||
|
|
||
|
[branch]
|
||
|
if (tmpRay.z > minZOffset || (reflectSky == 0 && isSky)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// go down a level in the hi-z buffer
|
||
|
--level;
|
||
|
|
||
|
ray.xyz = tmpRay.xyz;
|
||
|
++iterations;
|
||
|
}
|
||
|
hit = level < HIZ_STOP_LEVEL ? 1 : 0;
|
||
|
return ray;
|
||
|
}
|
||
|
|
||
|
inline float ScreenEdgeMask(float2 clipPos) {
|
||
|
float yDif = 1 - abs(clipPos.y);
|
||
|
float xDif = 1 - abs(clipPos.x);
|
||
|
[flatten]
|
||
|
if (yDif < 0 || xDif < 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
float t1 = smoothstep(0, .2, yDif);
|
||
|
float t2 = smoothstep(0, .1, xDif);
|
||
|
return saturate(t2 * t1);
|
||
|
}
|
||
|
|
||
|
float4 frag(v2f i) : SV_Target
|
||
|
{
|
||
|
|
||
|
float rawDepth = tex2D(_CameraDepthTexture, i.uv).r;
|
||
|
|
||
|
[branch]
|
||
|
if (rawDepth == 0) {
|
||
|
return float4(i.uv.xy, 0, 0);
|
||
|
}
|
||
|
float4 gbuff = tex2D(_GBuffer2, i.uv);
|
||
|
float smoothness = gbuff.w;
|
||
|
bool doRayMarch = smoothness > minSmoothness;
|
||
|
[branch]
|
||
|
if (!doRayMarch) {
|
||
|
return float4(i.uv.xy, 0, 0);
|
||
|
}
|
||
|
float stepS = smoothstep(minSmoothness, 1, smoothness);
|
||
|
//float3 normal = normalize(gbuff.xyz);
|
||
|
float3 normal = UnpackNormal(gbuff.xyz);
|
||
|
|
||
|
float4 clipSpace = float4(i.uv * 2 - 1, rawDepth, 1);
|
||
|
clipSpace.y *= -1;
|
||
|
|
||
|
float4 viewSpacePosition = mul(_InverseProjectionMatrix, clipSpace);
|
||
|
viewSpacePosition /= viewSpacePosition.w;
|
||
|
float4 worldSpacePosition = mul(_InverseViewMatrix, viewSpacePosition);
|
||
|
float3 viewDir = normalize(float3(worldSpacePosition.xyz) - _WorldSpaceCameraPos);
|
||
|
float3 reflectionRay_w = reflect(viewDir, normal);
|
||
|
float3 reflectionRay_v = mul(_ViewMatrix, float4(reflectionRay_w,0));
|
||
|
|
||
|
|
||
|
float3 vReflectionEndPosInVS = viewSpacePosition + reflectionRay_v * (viewSpacePosition.z * -1);
|
||
|
float4 vReflectionEndPosInCS = mul(_ProjectionMatrix, float4(vReflectionEndPosInVS.xyz, 1));
|
||
|
vReflectionEndPosInCS /= vReflectionEndPosInCS.w;
|
||
|
|
||
|
|
||
|
vReflectionEndPosInCS.z = 1 - (vReflectionEndPosInCS.z);
|
||
|
clipSpace.z = 1 - (clipSpace.z);
|
||
|
|
||
|
float3 outReflDirInTS = normalize((vReflectionEndPosInCS - clipSpace).xyz);
|
||
|
outReflDirInTS.xy *= float2(0.5f, -0.5f);
|
||
|
float3 outSamplePosInTS = float3(i.uv, clipSpace.z);
|
||
|
|
||
|
float viewNormalDot = dot(-viewDir, normal);
|
||
|
float viewReflectDot = saturate(dot(viewDir, reflectionRay_w));
|
||
|
float ddd = saturate(dot(_WorldSpaceViewDir, reflectionRay_w));
|
||
|
|
||
|
float hit = 0;
|
||
|
float mask = smoothstep(0, 0.1f, ddd);
|
||
|
|
||
|
[branch]
|
||
|
if (mask == 0) {
|
||
|
return float4(i.uv.xy, 0, 0);
|
||
|
}
|
||
|
|
||
|
float iterations;
|
||
|
bool isSky;
|
||
|
float3 intersectPoint = hiZTrace(outSamplePosInTS, outReflDirInTS, numSteps, hit, iterations, isSky);
|
||
|
float edgeMask = ScreenEdgeMask(intersectPoint.xy * 2 - 1);
|
||
|
mask *= hit * edgeMask;
|
||
|
|
||
|
//remove backface intersections
|
||
|
//float3 currentNormal = tex2D(_GBuffer2, intersectPoint.xy).xyz;
|
||
|
float3 currentNormal = UnpackNormal(tex2D(_GBuffer2, intersectPoint.xy).xyz);
|
||
|
float backFaceDot = dot(currentNormal, reflectionRay_w);
|
||
|
mask = backFaceDot > 0 && !isSky ? 0 : mask;
|
||
|
|
||
|
float pf = pow(smoothness, 4);
|
||
|
float fresnal = lerp(pf, 1.0, pow(viewReflectDot, 1 / pf));
|
||
|
|
||
|
//float4 color = tex2D(_MainTex, intersectPoint.xy);
|
||
|
return float4(intersectPoint.xy, stepS, mask * stepS * fresnal);
|
||
|
//return float4(SampleDepth(i.uv, 7),0,0,0);
|
||
|
//return float4(mcolor * (1 - mask) + color * mask);
|
||
|
//return float4(mask,0,0,0);
|
||
|
}
|
||
|
ENDCG
|
||
|
}//END PASS 2 - 7
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
}
|