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.
 
 
 
 

624 lines
19 KiB

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Random.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/Sampling.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/PostProcessing/LensFlareDataSRP.cs.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/PostProcessing/LensFlareOcclusionPermutation.cs.hlsl"
struct AttributesLensFlare
{
uint vertexID : SV_VertexID;
#ifndef FLARE_PREVIEW
UNITY_VERTEX_INPUT_INSTANCE_ID
#endif
};
struct VaryingsLensFlare
{
float4 positionCS : SV_POSITION;
float2 texcoord : TEXCOORD0;
float2 screenPos : TEXCOORD1;
#if defined(FLARE_HAS_OCCLUSION) || defined(FLARE_COMPUTE_OCCLUSION)
float occlusion : TEXCOORD2;
#endif
#ifndef FLARE_PREVIEW
UNITY_VERTEX_OUTPUT_STEREO
#endif
};
TEXTURE2D(_FlareTex);
SAMPLER(sampler_FlareTex);
TEXTURE2D(_FlareOcclusionRemapTex);
SAMPLER(sampler_FlareOcclusionRemapTex);
#if defined(FLARE_HAS_OCCLUSION)
TEXTURE2D_ARRAY(_FlareOcclusionTex);
SAMPLER(sampler_FlareOcclusionTex);
#endif
#ifdef HDRP_FLARE
uint _FlareOcclusionPermutation;
TEXTURE2D_X(_FlareSunOcclusionTex);
SAMPLER(sampler_FlareSunOcclusionTex);
#endif
float4 _FlareColorValue;
float4 _FlareData0; // x: localCos0, y: localSin0, zw: PositionOffsetXY
float4 _FlareData1; // x: OcclusionRadius, y: OcclusionSampleCount, z: ScreenPosZ, w: ScreenRatio
// Fragment Shader:
// x: LensFlareType, y: ElementIndex
float4 _FlareData2; // xy: ScreenPos, zw: FlareSize
float4 _FlareData3; // x: Allow Offscreen, y: Edge Offset, z: Falloff, w: invSideCount
// For Ring:
// w: RingThickness
float4 _FlareData4; // x: SDF Roundness, y: Poly Radius, z: PolyParam0, w: PolyParam1
// For Ring:
// x: noiseAmplitude, y: noiseFrequency, z: noiseSparsity, w: noiseSpeed
float4 _FlareData5; // x: ConstantColor, y: Intensity, z: shapeCutOffSpeed, w: cutoffRadius
TEXTURE2D(_FlareRadialTint);
SAMPLER(sampler_FlareRadialTint);
int _ViewId; // Used for XR index, for SinglePass and Multipass
#ifdef FLARE_PREVIEW
float4 _FlarePreviewData;
#define _ScreenSize _FlarePreviewData.xy;
#define _FlareScreenRatio _FlarePreviewData.z;
#endif
float4 _FlareOcclusionIndex;
#define _LocalCos0 _FlareData0.x
#define _LocalSin0 _FlareData0.y
#define _PositionTranslate _FlareData0.zw
// Vertex _FlareData1
#define _OcclusionRadius _FlareData1.x
#define _OcclusionSampleCount _FlareData1.y
#define _ScreenPosZ _FlareData1.z
#ifndef _FlareScreenRatio
#define _FlareScreenRatio _FlareData1.w
#endif
// Fragment _FlareData1
#define _FlareType ((int)_FlareData1.x)
#define _FlareElementIndex ((int)_FlareData1.y)
#define _FlareHoopFactor _FlareData1.z
#define _ScreenPos _FlareData2.xy
#define _FlareSize _FlareData2.zw
#define _OcclusionOffscreen _FlareData3.x
#define _FlareEdgeOffset _FlareData3.y
#define _FlareFalloff _FlareData3.z
#define _FlareShapeInvSide _FlareData3.w
#define _FlareRingThickness _FlareData3.w
#define _FlareSDFRoundness _FlareData4.x
#define _FlareSDFPolyRadius _FlareData4.y
#define _FlareSDFPolyParam0 _FlareData4.z
#define _FlareSDFPolyParam1 _FlareData4.w
// For Ring only
#define _FlareNoiseAmplitude _FlareData4.x
#define _FlareNoiseFrequency _FlareData4.y
#define _FlareNoiseSpeed _FlareData4.z
#define _IsFlareColorRadial (_FlareData5.x == 1.0f)
#define _IsFlareColorAngular (_FlareData5.x == 2.0f)
#define _FlareIntensity _FlareData5.y
#define _FlareCutoffSpeed _FlareData5.z
#define _FlareCutoffRadius _FlareData5.w
void Rotate(out float2 rot, float2 v, float cos0, float sin0)
{
rot = float2(v.x * cos0 - v.y * sin0,
v.x * sin0 + v.y * cos0);
}
// Disables LOAD_TEXTURE2D_ARRAY_LOD-related implicit vector-truncation warnings.
#pragma warning (disable : 3206)
#if defined(FLARE_COMPUTE_OCCLUSION) || defined(FLARE_OPENGL3_OR_OPENGLCORE)
float GetLinearDepthValue(float2 uv)
{
float depth;
#if defined(HDRP_FLARE) || defined(FLARE_PREVIEW)
if (_ViewId >= 0)
{
depth = LOAD_TEXTURE2D_ARRAY_LOD(_CameraDepthTexture, uint2(uv * _ScreenSize.xy), _ViewId, 0).x;
}
else
{
depth = LOAD_TEXTURE2D_X_LOD(_CameraDepthTexture, uint2(uv * _ScreenSize.xy), 0).x;
}
#else
depth = LOAD_TEXTURE2D_X_LOD(_CameraDepthTexture, uint2(uv * GetScaledScreenParams().xy), 0).x;
if (_ViewId >= 0)
{
#if defined(DISABLE_TEXTURE2D_X_ARRAY)
// This should never happen in theory since _ViewId can only be >= 0 IF xr is enabled and so DISABLE_TEXTURE2D_X_ARRAY is disabled.
// We just have to manage the DISABLE_TEXTURE2D_X_ARRAY variant here for avoiding warnings.
// HDRP does not need this because it never uses DISABLE_TEXTURE2D_X_ARRAY.
depth = LOAD_TEXTURE2D_LOD(_CameraDepthTexture, int2(uv * GetScaledScreenParams().xy), 0).x;
#else
depth = LOAD_TEXTURE2D_ARRAY_LOD(_CameraDepthTexture, int2(uv * GetScaledScreenParams().xy), _ViewId, 0).x;
#endif
}
else
depth = LOAD_TEXTURE2D_X_LOD(_CameraDepthTexture, uint2(uv * GetScaledScreenParams().xy), 0).x;
#endif
return LinearEyeDepth(depth, _ZBufferParams);
}
float GetOcclusion(float ratio)
{
if (_OcclusionSampleCount == 0.0f)
return 1.0f;
float contrib = 0.0f;
float sample_Contrib = 1.0f / _OcclusionSampleCount;
float2 ratioScale = float2(1.0f / ratio, 1.0);
for (uint i = 0; i < (uint)_OcclusionSampleCount; i++)
{
float2 dir = _OcclusionRadius * SampleDiskUniform(Hash(2 * i + 0), Hash(2 * i + 1));
float2 pos0 = _ScreenPos.xy + dir;
float2 pos = pos0 * 0.5f + 0.5f;
#ifdef UNITY_UV_STARTS_AT_TOP
pos.y = 1.0f - pos.y;
#endif
if (all(pos >= 0) && all(pos <= 1))
{
float depth0 = GetLinearDepthValue(pos);
#if UNITY_REVERSED_Z
if (depth0 > _ScreenPosZ)
#else
if (depth0 < _ScreenPosZ)
#endif
{
float occlusionValue = 1.0f;
#ifdef HDRP_FLARE
if ((_FlareOcclusionPermutation & LENSFLAREOCCLUSIONPERMUTATION_FOG_OPACITY) != 0)
{
float fogOcclusion;
if (_ViewId >= 0)
fogOcclusion = SAMPLE_TEXTURE2D_ARRAY_LOD(_FlareSunOcclusionTex, sampler_FlareSunOcclusionTex, pos * _RTHandleScale.xy, _ViewId, 0).x;
else
fogOcclusion = SAMPLE_TEXTURE2D_X_LOD(_FlareSunOcclusionTex, sampler_FlareSunOcclusionTex, pos * _RTHandleScale.xy, 0).x;
occlusionValue *= saturate(fogOcclusion);
}
#endif
contrib += sample_Contrib * occlusionValue;
}
}
else if (_OcclusionOffscreen > 0.0f)
{
contrib += sample_Contrib;
}
}
contrib = SAMPLE_TEXTURE2D_LOD(_FlareOcclusionRemapTex, sampler_FlareOcclusionRemapTex, float2(saturate(contrib), 0.0f), 0).x;
contrib = saturate(contrib);
return contrib;
}
#endif
#if defined(FLARE_COMPUTE_OCCLUSION)
VaryingsLensFlare vertOcclusion(AttributesLensFlare input, uint instanceID : SV_InstanceID)
{
VaryingsLensFlare output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
#if defined(HDRP_FLARE) || defined(FLARE_PREVIEW)
float screenRatio = _FlareScreenRatio;
#else
float2 screenParam = GetScaledScreenParams().xy;
float screenRatio = screenParam.y / screenParam.x;
#endif
float2 quadPos = 2.0f * GetQuadVertexPosition(input.vertexID).xy - 1.0f;
float2 uv = GetQuadTexCoord(input.vertexID);
uv.x = 1.0f - uv.x;
output.positionCS.xy = quadPos;
output.texcoord.xy = uv;
output.positionCS.z = 1.0f;
output.positionCS.w = 1.0f;
float occlusion = GetOcclusion(screenRatio);
if (_OcclusionOffscreen < 0.0f && // No lens flare off screen
(any(_ScreenPos.xy < -1) || any(_ScreenPos.xy >= 1)))
occlusion = 0.0f;
output.occlusion = occlusion;
output.screenPos = output.positionCS.xy;
return output;
}
float4 fragOcclusion(VaryingsLensFlare input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
return float4(input.occlusion.xxx, 1.0f);
}
#else
VaryingsLensFlare vert(AttributesLensFlare input, uint instanceID : SV_InstanceID)
{
VaryingsLensFlare output;
#ifndef FLARE_PREVIEW
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
#endif
#if defined(HDRP_FLARE) || defined(FLARE_PREVIEW)
float screenRatio = _FlareScreenRatio;
#else
float2 screenParam = GetScaledScreenParams().xy;
float screenRatio = screenParam.y / screenParam.x;
#endif
float4 posPreScale = float4(2.0f, 2.0f, 1.0f, 1.0f) * GetQuadVertexPosition(input.vertexID) - float4(1.0f, 1.0f, 0.0f, 0.0);
float2 uv = GetQuadTexCoord(input.vertexID);
uv.x = 1.0f - uv.x;
output.texcoord.xy = uv;
posPreScale.xy *= _FlareSize;
float2 local;
Rotate(local, posPreScale.xy, _LocalCos0, _LocalSin0);
local.x *= screenRatio;
output.positionCS.xy = local + _ScreenPos + _PositionTranslate;
output.positionCS.z = 1.0f;
output.positionCS.w = 1.0f;
#if defined(FLARE_HAS_OCCLUSION)
float occlusion;
if (_OcclusionOffscreen < 0.0f && // No lens flare off screen
(any(_ScreenPos.xy < -1.0f) || any(_ScreenPos.xy > 1.0f)))
{
occlusion = 0.0f;
}
else
{
#if defined(FLARE_OPENGL3_OR_OPENGLCORE)
#if defined(HDRP_FLARE) || defined(FLARE_PREVIEW)
float screenRatio = _FlareScreenRatio;
#else
float2 screenParam = GetScaledScreenParams().xy;
float screenRatio = screenParam.y / screenParam.x;
#endif
occlusion = GetOcclusion(screenRatio);
#else // defined(FLARE_OPENGL3_OR_OPENGLCORE)
if (_ViewId >= 0)
occlusion = LOAD_TEXTURE2D_ARRAY_LOD(_FlareOcclusionTex, uint2(_FlareOcclusionIndex.x, 0), _ViewId, 0).x;
else
occlusion = LOAD_TEXTURE2D_ARRAY_LOD(_FlareOcclusionTex, uint2(_FlareOcclusionIndex.x, 0), 0, 0).x;
#endif // defined(FLARE_OPENGL3_OR_OPENGLCORE)
}
output.occlusion = occlusion;
#endif
output.screenPos = output.positionCS.xy;
return output;
}
#endif
// Resets LOAD_TEXTURE2D_ARRAY_LOD-related implicit vector-truncation warnings.
#pragma warning (default : 3206)
// Constrains: x in [0.0f, 1.0f]
float InverseGradient(float x)
{
// Before:
// Do *not* simplify as 1.0f - x
//return x * (1.0f - x) / (x + eps);
// Kind of equivalent of (without the edge smoothness control):
//if (0.0f < x && x < 1.0f)
// return 1.0f - x;
//else
// return 0.0f;
// After:
// Larger eps give smoother boundary-edges
const float eps = 1e-3f;
// cf. https://www.desmos.com/calculator/0hprry9l90
// Rescale to always have the max at 1.0f;
//
// The max of 'x * (1.0f - x) / (x + eps)'
// Is when x == sqrt(eps (1 + eps)) - eps
// which is => 1 + 2*eps - 2*sqrt(eps*(1 + eps))
// We can rescale by scaling with 1/maxValue
// => x*(1 - x)/(x + eps)*(1/(1 + 2*eps - 2*sqrt(eps*(1 + eps))))
// Simplifed as:
// Compile-time variables:
const float eps2 = 2.0f * eps;
const float scale = 2.0f * sqrt(eps * (1.0f + eps));
const float a = scale - eps2 - 1.0f;
const float b = a * eps;
//return x * (x - 1.0f) / (a * x + b);
// to:
return mad(x, x, -x) * rcp(mad(a, x, b));
}
float CircleSDF(float2 center, float2 pos, float r)
{
return length(pos - center) - r;
}
float SDFBlocker(float2 pos, float2 screenPos, float ar, float r)
{
float2 localPos = _ScreenPos - screenPos;
localPos.y = -localPos.y;
float2 offset = localPos * _FlareCutoffSpeed;
float2 rot;
Rotate(rot, offset, _LocalCos0, _LocalSin0);
pos.y *= ar;
return CircleSDF(pos, rot, _FlareCutoffRadius * r);
}
float ComputeCircle(float2 uv, float2 screenPos)
{
float2 v = 2.0f * uv - 1.0f;
float radius = 1.0f;
float sdfBlocker = SDFBlocker(v, screenPos, _FlareSize.y / _FlareSize.x, radius);
float sdfSrc = CircleSDF(v, float2(0.0f, 0.0f), radius);
float sdf = max(sdfSrc, sdfBlocker);
sdf = saturate(sdf / ((_FlareEdgeOffset - radius)));
#if defined(FLARE_INVERSE_SDF)
sdf = saturate(sdf);
sdf = InverseGradient(sdf);
#endif
sdf = pow(saturate(sdf), _FlareFalloff);
return sdf;
}
// Modfied from ref: https://www.shadertoy.com/view/MtKcWW
// https://www.shadertoy.com/view/3tGBDt
float ComputePolygon(float2 uv_, float2 screenPos)
{
float2 v = 2.0f * uv_ - 1.0f;
float r = _FlareSDFPolyRadius;
float an = _FlareSDFPolyParam0;
float he = _FlareSDFPolyParam1;
float bn = an * floor((atan2(v.y, v.x) + 0.5f * an) / an);
float cos0 = cos(bn);
float sin0 = sin(bn);
float2 p = float2( cos0 * v.x + sin0 * v.y,
-sin0 * v.x + cos0 * v.y);
// side of polygon
float sdf = length(p - float2(r, clamp(p.y, -he, he))) * sign(p.x - r) - _FlareSDFRoundness;
float sdfBlocker = SDFBlocker(v, screenPos, _FlareSize.y / _FlareSize.x, r);
sdf = max(sdf, sdfBlocker);
sdf *= _FlareEdgeOffset;
#if defined(FLARE_INVERSE_SDF)
sdf = saturate(-sdf);
sdf = InverseGradient(sdf);
#else
sdf = saturate(-sdf);
#endif
sdf = pow(saturate(sdf), _FlareFalloff);
return sdf;
}
float4 Interpolation_C2_InterpAndDeriv(float2 x)
{
return x.xyxy * x.xyxy * (x.xyxy * (x.xyxy * (x.xyxy * float2(6.0f, 0.0f).xxyy + float2(-15.0f, 30.0f).xxyy) + float2(10.0f, -60.0f).xxyy) + float2(0.0f, 30.0f).xxyy);
}
// Generates a random number for each of the 4 cell corners
float4 NoiseHash2D(float2 gridcell)
{
float2 kOffset = float2(26.0f, 161.0f);
float kDomain = 71.0f;
float kLargeFloat = 1.0f / 951.135664f;
float4 P = float4(gridcell.xy, gridcell.xy + 1.0f);
P = P - floor(P * (1.0f / kDomain)) * kDomain; // truncate the domain
P += kOffset.xyxy; // offset to interesting part of the noise
P *= P; // calculate and return the hash
return frac(P.xzxz * P.yyww * kLargeFloat);
}
float GenerateValueNoise2D(float2 coordinate)
{
float2 i = floor(coordinate);
float2 f = coordinate - i;
float4 hash = NoiseHash2D(i);
float4 blend = Interpolation_C2_InterpAndDeriv(f);
float4 res0 = lerp(hash.xyxz, hash.zwyw, blend.yyxx);
float2 resDelta = res0.yw - res0.xz;
float noise = res0.x + resDelta.x * blend.x;
return noise;
}
float ComputeRing(float2 uv_, float2 screenPos)
{
float2 pos = 2.0f * uv_ - 1.0f;
#ifndef FLARE_PREVIEW
float iTime = _Time.y;
#else
float iTime = 0;
#endif
float r = 1.0f;
float ang = FastAtan2(pos.y, pos.x);
float2 sc;
float noiseAng = ang * _FlareNoiseFrequency;
sincos(noiseAng, sc.x, sc.y);
float noise = abs(abs(GenerateValueNoise2D(_FlareNoiseAmplitude * sc + _FlareElementIndex.xx + _FlareNoiseSpeed * iTime)));
float sdfBlocker = CircleSDF(pos, float2(0.0f, 0.0f), r);
float sdf;
sdf = abs(CircleSDF(pos, 0.0f.xx, (1.0f - _FlareRingThickness))) - _FlareRingThickness;
sdf = max(noise * sdf, sdfBlocker);
sdf = saturate(sdf / ((_FlareEdgeOffset - r)));
#if defined(FLARE_INVERSE_SDF)
sdf = saturate(sdf);
sdf = InverseGradient(sdf);
#endif
sdf = pow(saturate(sdf), _FlareFalloff);
float sdfBlockerCutoff = SDFBlocker(pos, screenPos, _FlareSize.y / _FlareSize.x, 1.0f);
sdf *= saturate(-sdfBlockerCutoff > 0.0f);
return sdf;
}
float4 GetFlareShape(float2 uv, float2 screenPos)
{
float4 flareColor = 1.0f;
float shape;
if (_FlareType == SRPLENSFLARETYPE_CIRCLE)
shape = ComputeCircle(uv, screenPos);
else if (_FlareType == SRPLENSFLARETYPE_POLYGON)
shape = ComputePolygon(uv, screenPos);
else if (_FlareType == SRPLENSFLARETYPE_RING)
shape = ComputeRing(uv, screenPos);
else if (_FlareType == SRPLENSFLARETYPE_IMAGE)
{
shape = 1.0f;
flareColor = SAMPLE_TEXTURE2D(_FlareTex, sampler_FlareTex, uv);
float sdfBlocker = SDFBlocker(2.0f * uv - 1.0f, screenPos, _FlareSize.y / _FlareSize.x, 1.0f);
flareColor *= saturate(-sdfBlocker > 0.0f);
}
else
{
// Not possible, if the code execute this lines, we have a bug on the code
// SRPLENSFLARETYPE_LENS_FLARE_DATA_SRP should never be executed
shape = -1.0f;
}
#if defined(HDRP_FLARE) || defined(FLARE_PREVIEW)
float screenRatio = _FlareScreenRatio;
#else
float2 screenParam = GetScaledScreenParams().xy;
float screenRatio = screenParam.y / screenParam.x;
#endif
float2 pos = 2.0f * uv - 1.0f;
if (_IsFlareColorRadial)
{
float radius = length(pos);
if (_FlareType == SRPLENSFLARETYPE_RING)
{
float offset = 1.0f - _FlareRingThickness;
float a = (1.0f / clamp(2.0f * _FlareRingThickness, _FlareRingThickness, 1.0f));
float b = 1.0f - a;
radius = saturate(a * radius + b);
}
float4 grad = SAMPLE_TEXTURE2D_LOD(_FlareRadialTint, sampler_FlareRadialTint, float2(saturate(radius), 0.0f), 0);
flareColor *= grad;
}
else if (_IsFlareColorAngular)
{
float angle01 = (atan2(pos.y, pos.x) + PI) / TWO_PI;
float4 grad = SAMPLE_TEXTURE2D_LOD(_FlareRadialTint, sampler_FlareRadialTint, float2(saturate(angle01), 0.0f), 0);
flareColor *= grad;
}
flareColor *= _FlareColorValue;
flareColor.rgb *= _FlareIntensity;
#ifdef FLARE_ADDITIVE_BLEND
float4 finalValue = float4(flareColor.rgb * shape, shape * flareColor.a);
#elif defined(FLARE_SCREEN_BLEND)
float4 finalValue = float4(flareColor.rgb * shape, shape * flareColor.a);
#elif defined(FLARE_PREMULTIPLIED_BLEND)
float4 finalValue = float4(flareColor.rgb * shape, shape * flareColor.a);
#elif defined(FLARE_LERP_BLEND)
float4 finalValue = float4(flareColor.rgb, shape * flareColor.a);
#else
float4 finalValue = flareColor * shape;
#endif
return finalValue;
}
float4 frag(VaryingsLensFlare input) : SV_Target
{
#ifndef FLARE_PREVIEW
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
#endif
#if defined(FLARE_HAS_OCCLUSION)
if (input.occlusion > 0.0f)
{
float4 col = GetFlareShape(input.texcoord, input.screenPos.xy);
return col * input.occlusion;
}
else
{
return float4(0.0f, 0.0f, 0.0f, 0.0f);
}
#else
float4 col = GetFlareShape(input.texcoord, input.screenPos.xy);
return col;
#endif
}