Reformat shaders source code

This commit is contained in:
Wojciech Figat
2022-06-28 14:41:29 +02:00
parent df691e62f8
commit 56322005e2
60 changed files with 1969 additions and 1972 deletions

BIN
Content/Editor/Camera/M_Camera.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/DefaultFontMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Gizmo/Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Gizmo/MaterialWire.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Highlight Material.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Icons/IconsMaterial.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

View File

@@ -617,14 +617,14 @@ void PS_Depth(PixelInput input)
#if _PS_QuadOverdraw
#include "./Flax/Editor/QuadOverdraw.hlsl"
//#include "./Flax/Editor/QuadOverdraw.hlsl"
// Pixel Shader function for Quad Overdraw Pass (editor-only)
[earlydepthstencil]
META_PS(USE_EDITOR, FEATURE_LEVEL_SM5)
void PS_QuadOverdraw(float4 svPos : SV_Position, uint primId : SV_PrimitiveID)
{
DoQuadOverdraw(svPos, primId);
//DoQuadOverdraw(svPos, primId);
}
#endif

Binary file not shown.

Binary file not shown.

BIN
Content/Editor/Particles/Smoke.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Particles/Sparks.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/SpriteMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/TexturePreviewMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Editor/Wires Debug Material.flax (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

BIN
Content/Engine/DefaultMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/DefaultTerrainMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/SingleColorMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Engine/SkyboxMaterial.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/GI/DDGI.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax (Stored with Git LFS)

Binary file not shown.

BIN
Content/Shaders/VolumetricFog.flax (Stored with Git LFS)

Binary file not shown.

View File

@@ -37,7 +37,7 @@ const static float3 BetaRayleighScattering = float3(5.8e-3, 1.35e-2, 3.31e-2); /
const static float HeightScaleMie = 1.2f; // 1.2 km, for Mie scattering
const static float3 BetaMieScattering = float3(4e-3f, 4e-3f, 4e-3f); // Equation 4
const static float BetaRatio = 0.9f; // Figure 6, BetaMScattering/BetaMExtinction = 0.9
const static float3 BetaMieExtinction = BetaMieScattering / BetaRatio.rrr;
const static float3 BetaMieExtinction = BetaMieScattering / BetaRatio.rrr;
const static float MieG = 0.8f; // Equation 4
const static float RadiusScale = 1;
@@ -83,16 +83,16 @@ Texture2D AtmosphereIrradianceTexture : register(t4);
Texture3D AtmosphereInscatterTexture : register(t5);
#else
Texture2D AtmosphereTransmittanceTexture : register(t0);
Texture2D AtmosphereIrradianceTexture : register(t1);
Texture3D AtmosphereInscatterTexture : register(t2);
Texture2D AtmosphereIrradianceTexture : register(t1);
Texture3D AtmosphereInscatterTexture : register(t2);
#endif
float2 GetTransmittanceUV(float radius, float Mu)
float2 GetTransmittanceUV(float radius, float Mu)
{
float u, v;
#if TRANSMITTANCE_NON_LINEAR
v = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround));
u = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5;
v = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround));
u = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5;
#else
v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
u = (Mu + 0.15) / (1.0 + 0.15);
@@ -100,7 +100,7 @@ float2 GetTransmittanceUV(float radius, float Mu)
return float2(u, v);
}
void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
{
radius = uv.y;
MuS = uv.x;
@@ -113,14 +113,14 @@ void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
#endif
}
float2 GetIrradianceUV(float radius, float MuS)
float2 GetIrradianceUV(float radius, float MuS)
{
float v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
float u = (MuS + 0.2) / (1.0 + 0.2);
return float2(u, v);
}
void GetIrradianceRMuS(float2 uv, out float radius, out float MuS)
void GetIrradianceRMuS(float2 uv, out float radius, out float MuS)
{
radius = RadiusGround + (uv.y * float(IrradianceTexHeight) - 0.5) / (float(IrradianceTexHeight) - 1.0) * (RadiusAtmosphere - RadiusGround);
MuS = -0.2 + (uv.x * float(IrradianceTexWidth) - 0.5) / (float(IrradianceTexWidth) - 1.0) * (1.0 + 0.2);
@@ -128,47 +128,47 @@ void GetIrradianceRMuS(float2 uv, out float radius, out float MuS)
float4 Texture4DSample(Texture3D tex, float radius, float Mu, float MuS, float Nu)
{
float H = sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround);
float Rho = sqrt(radius * radius - RadiusGround * RadiusGround);
float H = sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround);
float Rho = sqrt(radius * radius - RadiusGround * RadiusGround);
#if INSCATTER_NON_LINEAR
float RMu = radius * Mu;
float Delta = RMu * RMu - radius * radius + RadiusGround * RadiusGround;
float4 TexOffset = RMu < 0.0 && Delta > 0.0 ? float4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(InscatterMuNum)) : float4(-1.0, H * H, H, 0.5 + 0.5 / float(InscatterMuNum));
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
float MuMu = TexOffset.w + (RMu * TexOffset.x + sqrt(Delta + TexOffset.y)) / (Rho + TexOffset.z) * (0.5 - 1.0 / float(InscatterMuNum));
float MuMuS = 0.5 / float(InscatterMuSNum) + (atan(max(MuS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(InscatterMuSNum));
float RMu = radius * Mu;
float Delta = RMu * RMu - radius * radius + RadiusGround * RadiusGround;
float4 TexOffset = RMu < 0.0 && Delta > 0.0 ? float4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(InscatterMuNum)) : float4(-1.0, H * H, H, 0.5 + 0.5 / float(InscatterMuNum));
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
float MuMu = TexOffset.w + (RMu * TexOffset.x + sqrt(Delta + TexOffset.y)) / (Rho + TexOffset.z) * (0.5 - 1.0 / float(InscatterMuNum));
float MuMuS = 0.5 / float(InscatterMuSNum) + (atan(max(MuS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(InscatterMuSNum));
#else
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
float MuMu = 0.5 / float(InscatterMuNum) + (Mu + 1.0) * 0.5f * (1.0 - 1.0 / float(InscatterMuNum));
float MuMuS = 0.5 / float(InscatterMuSNum) + max(MuS + 0.2, 0.0) / 1.2 * (1.0 - 1.0 / float(InscatterMuSNum));
#endif
float LerpValue = (Nu + 1.0) * 0.5f * (float(InscatterNuNum) - 1.0);
float MuNu = floor(LerpValue);
LerpValue = LerpValue - MuNu;
float LerpValue = (Nu + 1.0) * 0.5f * (float(InscatterNuNum) - 1.0);
float MuNu = floor(LerpValue);
LerpValue = LerpValue - MuNu;
return tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS) / float(InscatterNuNum), MuMu, MuR), 0) * (1.0 - LerpValue)
+ tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS + 1.0) / float(InscatterNuNum), MuMu, MuR), 0) * LerpValue;
return tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS) / float(InscatterNuNum), MuMu, MuR), 0) * (1.0 - LerpValue)
+ tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS + 1.0) / float(InscatterNuNum), MuMu, MuR), 0) * LerpValue;
}
float Mod(float x, float y)
{
return x - y * floor(x / y);
return x - y * floor(x / y);
}
void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float MuS, out float Nu)
void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float MuS, out float Nu)
{
float x = uv.x * float(InscatterMuSNum * InscatterNuNum) - 0.5;
float y = uv.y * float(InscatterMuNum) - 0.5;
#if INSCATTER_NON_LINEAR
if (y < float(InscatterMuNum) * 0.5f)
{
if (y < float(InscatterMuNum) * 0.5f)
{
float d = 1.0 - y / (float(InscatterMuNum) * 0.5f - 1.0);
d = min(max(DhdH.z, d * DhdH.w), DhdH.w * 0.999);
Mu = (RadiusGround * RadiusGround - radius * radius - d * d) / (2.0 * radius * d);
Mu = min(Mu, -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) - 0.001);
}
else
{
else
{
float d = (y - float(InscatterMuNum) * 0.5f) / (float(InscatterMuNum) * 0.5f - 1.0);
d = min(max(DhdH.x, d * DhdH.y), DhdH.y * 0.999);
Mu = (RadiusAtmosphere * RadiusAtmosphere - radius * radius - d * d) / (2.0 * radius * d);
@@ -185,15 +185,15 @@ void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float Mu
}
// Nearest intersection of ray r,mu with ground or top atmosphere boundary, mu=cos(ray zenith angle at ray origin)
float Limit(float radius, float Mu)
float Limit(float radius, float Mu)
{
float Dout = -radius * Mu + sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusLimit * RadiusLimit);
float Delta2 = radius * radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround;
if (Delta2 >= 0.0)
{
if (Delta2 >= 0.0)
{
float Din = -radius * Mu - sqrt(Delta2);
if (Din >= 0.0)
{
if (Din >= 0.0)
{
Dout = min(Dout, Din);
}
}
@@ -201,90 +201,90 @@ float Limit(float radius, float Mu)
}
// Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), intersections with ground ignored
float3 Transmittance(float radius, float Mu)
float3 Transmittance(float radius, float Mu)
{
float2 uv = GetTransmittanceUV(radius, Mu);
return AtmosphereTransmittanceTexture.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
float2 uv = GetTransmittanceUV(radius, Mu);
return AtmosphereTransmittanceTexture.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
}
// Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), or zero if ray intersects ground
float3 TransmittanceWithShadow(float radius, float Mu)
float3 TransmittanceWithShadow(float radius, float Mu)
{
return Transmittance(radius, Mu);
return Transmittance(radius, Mu);
}
//Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground. D = Distance between x and x0, mu=cos(zenith angle of [x,x0) ray at x)
float3 TransmittanceWithDistance(float radius, float Mu, float D)
float3 TransmittanceWithDistance(float radius, float Mu, float D)
{
float3 result;
float R1 = sqrt(radius * radius + D * D + 2.0 * radius * Mu * D);
float Mu1 = (radius * Mu + D) / R1;
if (Mu > 0.0)
{
if (Mu > 0.0)
{
result = min(Transmittance(radius, Mu) / Transmittance(R1, Mu1), 1.0);
}
else
{
else
{
result = min(Transmittance(R1, -Mu1) / Transmittance(radius, -Mu), 1.0);
}
return result;
}
// Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground radius=||x||, Mu=cos(zenith angle of [x,x0) ray at x), v=unit direction vector of [x,x0) ray
float3 TransmittanceWithDistance(float radius, float Mu, float3 V, float3 X0)
float3 TransmittanceWithDistance(float radius, float Mu, float3 V, float3 X0)
{
float3 result;
float d1 = length(X0);
float Mu1 = dot(X0, V) / radius;
if (Mu > 0.0)
if (Mu > 0.0)
result = min(Transmittance(radius, Mu) / Transmittance(d1, Mu1), 1.0);
else
else
result = min(Transmittance(d1, -Mu1) / Transmittance(radius, -Mu), 1.0);
return result;
}
// Optical depth for ray (r,mu) of length d, using analytic formula (mu=cos(view zenith angle)), intersections with ground ignored H=height scale of exponential density function
float OpticalDepthWithDistance(float H, float radius, float Mu, float D)
float OpticalDepthWithDistance(float H, float radius, float Mu, float D)
{
float particleDensity = 6.2831; // REK 04, Table 2
float particleDensity = 6.2831; // REK 04, Table 2
float a = sqrt(0.5 / H * radius);
float2 A01 = a * float2(Mu, Mu + D / radius);
float2 A01Sign = sign(A01);
float2 A01Squared = A01*A01;
float2 A01Squared = A01 * A01;
float x = A01Sign.y > A01Sign.x ? exp(A01Squared.x) : 0.0;
float2 y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H*(D / (2.0 * radius) + Mu)));
return sqrt((particleDensity * H)*radius) * exp((RadiusGround - radius) / H) * (x + dot(y, float2(1.0, -1.0)));
float2 y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H * (D / (2.0 * radius) + Mu)));
return sqrt((particleDensity * H) * radius) * exp((RadiusGround - radius) / H) * (x + dot(y, float2(1.0, -1.0)));
}
// Transmittance(=transparency) of atmosphere for ray (r,mu) of length d (mu=cos(view zenith angle)), intersections with ground ignored uses analytic formula instead of transmittance texture, REK 04, Atmospheric Transparency
float3 AnalyticTransmittance(float R, float Mu, float D)
float3 AnalyticTransmittance(float R, float Mu, float D)
{
return exp(- BetaRayleighScattering * OpticalDepthWithDistance(HeightScaleRayleigh, R, Mu, D) - BetaMieExtinction * OpticalDepthWithDistance(HeightScaleMie, R, Mu, D));
}
float3 Irradiance(Texture2D tex, float r, float muS)
float3 Irradiance(Texture2D tex, float r, float muS)
{
float2 uv = GetIrradianceUV(r, muS);
return tex.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
return tex.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
}
// Rayleigh phase function
float PhaseFunctionR(float Mu)
float PhaseFunctionR(float Mu)
{
return (3.0 / (16.0 * PI)) * (1.0 + Mu * Mu);
}
// Mie phase function
float PhaseFunctionM(float Mu)
float PhaseFunctionM(float Mu)
{
return 1.5 * 1.0 / (4.0 * PI) * (1.0 - MieG * MieG) * pow(1.0 + (MieG * MieG) - 2.0 * MieG * Mu, -3.0/2.0) * (1.0 + Mu * Mu) / (2.0 + MieG * MieG);
return 1.5 * 1.0 / (4.0 * PI) * (1.0 - MieG * MieG) * pow(1.0 + (MieG * MieG) - 2.0 * MieG * Mu, -3.0 / 2.0) * (1.0 + Mu * Mu) / (2.0 + MieG * MieG);
}
// Approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision")
float3 GetMie(float4 RayMie)
{
// RayMie.rgb=C*, RayMie.w=Cm,r
return RayMie.rgb * RayMie.w / max(RayMie.r, 1e-4) * (BetaRayleighScattering.rrr / BetaRayleighScattering.rgb);
float3 GetMie(float4 RayMie)
{
// RayMie.rgb=C*, RayMie.w=Cm,r
return RayMie.rgb * RayMie.w / max(RayMie.r, 1e-4) * (BetaRayleighScattering.rrr / BetaRayleighScattering.rgb);
}
#endif

View File

@@ -27,222 +27,221 @@ static const float HeightOffset = 0.01f;
// inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0)
float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry)
{
float3 result = float3(0.0f, 0.0f, 0.0f);
attenuation = float3(1.0f, 1.0f, 1.0f);
float3 result = float3(0.0f, 0.0f, 0.0f);
attenuation = float3(1.0f, 1.0f, 1.0f);
float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
if (d > 0.0f)
{
// if X in space and ray intersects atmosphere
// move X to nearest intersection of ray with top atmosphere boundary
X += d * V;
T -= d;
Mu = (radius * Mu + d) / RadiusAtmosphere;
radius = RadiusAtmosphere;
}
float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
if (d > 0.0f)
{
// if X in space and ray intersects atmosphere
// move X to nearest intersection of ray with top atmosphere boundary
X += d * V;
T -= d;
Mu = (radius * Mu + d) / RadiusAtmosphere;
radius = RadiusAtmosphere;
}
float epsilon = 0.005f;
float epsilon = 0.005f;
if (radius < RadiusGround + HeightOffset + epsilon)
{
float diff = (RadiusGround + HeightOffset + epsilon) - radius;
X -= diff * V;
T -= diff;
radius = RadiusGround + HeightOffset + epsilon;
Mu = dot(X, V) / radius;
}
if (radius < RadiusGround + HeightOffset + epsilon)
{
float diff = (RadiusGround + HeightOffset + epsilon) - radius;
X -= diff * V;
T -= diff;
radius = RadiusGround + HeightOffset + epsilon;
Mu = dot(X, V) / radius;
}
if (radius <= RadiusAtmosphere && fogDepth > 0.0f)
{
float3 X0 = X + T * V;
float R0 = length(X0);
float Nu = dot(V, S);
float MuS = dot(X, S) / radius;
if (radius <= RadiusAtmosphere && fogDepth > 0.0f)
{
float3 X0 = X + T * V;
float R0 = length(X0);
float Nu = dot(V, S);
float MuS = dot(X, S) / radius;
float MuHorizon = -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius));
float MuHorizon = -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius));
if (isSceneGeometry)
{
Mu = max(Mu, MuHorizon + epsilon + 0.15f);
}
else
{
Mu = max(Mu, MuHorizon + epsilon);
}
if (isSceneGeometry)
{
Mu = max(Mu, MuHorizon + epsilon + 0.15f);
}
else
{
Mu = max(Mu, MuHorizon + epsilon);
}
float MuOriginal = Mu;
float blendRatio = 0.0f;
float MuOriginal = Mu;
float blendRatio = 0.0f;
if (isSceneGeometry)
{
blendRatio = saturate(exp(-V.z) - 0.5);
if (blendRatio < 1.0)
{
V.z = max(V.z, 0.15);
V = normalize(V);
float3 x1 = X + T * V;
Mu = dot(x1, V) / length(x1);
}
}
if (isSceneGeometry)
{
blendRatio = saturate(exp(-V.z) - 0.5);
if (blendRatio < 1.0)
{
V.z = max(V.z, 0.15);
V = normalize(V);
float3 x1 = X + T * V;
Mu = dot(x1, V) / length(x1);
}
}
float phaseR = PhaseFunctionR(Nu);
float phaseM = PhaseFunctionM(Nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0);
float phaseR = PhaseFunctionR(Nu);
float phaseM = PhaseFunctionM(Nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0);
if (T > 0.0)
{
attenuation = AnalyticTransmittance(radius, Mu, T);
float Mu0 = dot(X0, V) / R0;
float MuS0 = dot(X0, S) / R0;
if (isSceneGeometry)
{
R0 = max(R0, radius);
}
if (T > 0.0)
{
attenuation = AnalyticTransmittance(radius, Mu, T);
float Mu0 = dot(X0, V) / R0;
float MuS0 = dot(X0, S) / R0;
if (isSceneGeometry)
{
R0 = max(R0, radius);
}
if (R0 > RadiusGround + HeightOffset)
{
if (blendRatio < 1.0)
{
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0);
if (!isSceneGeometry)
{
if (abs(Mu - MuHorizon) < epsilon)
{
Mu = MuHorizon - epsilon;
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
Mu0 = (radius * Mu + T) / R0;
if (R0 > RadiusGround + HeightOffset)
{
if (blendRatio < 1.0)
{
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0);
if (!isSceneGeometry)
{
if (abs(Mu - MuHorizon) < epsilon)
{
Mu = MuHorizon - epsilon;
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
Mu0 = (radius * Mu + T) / R0;
Mu0 = max(MuHorizon + epsilon, Mu0);
float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0);
Mu0 = max(MuHorizon + epsilon, Mu0);
float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0);
Mu = MuHorizon + epsilon;
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
Mu = MuHorizon + epsilon;
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
Mu0 = (radius * Mu + T) / R0;
Mu0 = max(MuHorizon + epsilon, Mu0);
inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0);
Mu0 = (radius * Mu + T) / R0;
Mu0 = max(MuHorizon + epsilon, Mu0);
inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0);
float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon;
inscatter = lerp(inscatterA, inscatterB, alpha);
}
}
else if (blendRatio > 0.0)
{
inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio);
}
}
else
{
inscatter = (1.0 - attenuation.rgbr) * inscatter;
}
}
}
float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon;
inscatter = lerp(inscatterA, inscatterB, alpha);
}
}
else if (blendRatio > 0.0)
{
inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio);
}
}
else
{
inscatter = (1.0 - attenuation.rgbr) * inscatter;
}
}
}
inscatter.w *= smoothstep(0.00, 0.02, MuS);
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
inscatter.w *= smoothstep(0.00, 0.02, MuS);
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
return result;
return result;
}
// Ground radiance at end of ray x+tv, when sun in direction s attenuated between ground and viewer (=R[L0]+R[L*])
float3 GetGroundColor(float4 sceneColor, float3 X, float T, float3 V, float3 S, float radius, float3 attenuation, bool isSceneGeometry)
{
float3 result = float3(0.0f, 0.0f, 0.0f);
if (T > 0.0f)
{
// if ray hits ground surface
// ground Reflectance at end of ray, X0
float3 X0 = X + T * V;
float R0 = length(X0);
float3 N = X0 / R0;
sceneColor.xyz = saturate(sceneColor.xyz + 0.05);
float3 result = float3(0.0f, 0.0f, 0.0f);
if (T > 0.0f)
{
// if ray hits ground surface
// ground Reflectance at end of ray, X0
float3 X0 = X + T * V;
float R0 = length(X0);
float3 N = X0 / R0;
sceneColor.xyz = saturate(sceneColor.xyz + 0.05);
float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0);
float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0);
// direct sun light (radiance) reaching X0
float MuS = dot(N, S);
float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS);
// direct sun light (radiance) reaching X0
float MuS = dot(N, S);
float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS);
// precomputed sky light (irradiance) (=E[L*]) at X0
float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS);
// precomputed sky light (irradiance) (=E[L*]) at X0
float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS);
// light reflected at X0 (=(R[L0]+R[L*])/T(X,X0))
float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI);
// light reflected at X0 (=(R[L0]+R[L*])/T(X,X0))
float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI);
// water specular color due to SunLight
if (!isSceneGeometry && reflectance.w > 0.0)
{
float3 H = normalize(S - V);
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0);
float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0);
groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight;
}
// water specular color due to SunLight
if (!isSceneGeometry && reflectance.w > 0.0)
{
float3 H = normalize(S - V);
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0);
float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0);
groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight;
}
result = attenuation * groundColor; //=R[L0]+R[L*]
}
return result;
result = attenuation * groundColor; //=R[L0]+R[L*]
}
return result;
}
// Direct sun light for ray x+tv, when sun in direction s (=L0)
float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu)
{
if (T > 0.0)
return float3(0.0f, 0.0f, 0.0f);
if (T > 0.0)
return float3(0.0f, 0.0f, 0.0f);
float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo)
float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun
return transmittance * sunIntensity; // Eq (9)
float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo)
float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun
return transmittance * sunIntensity; // Eq (9)
}
float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r, out float mu, out float3 attenuation)
{
float3 result = 0;
r = length(x);
mu = dot(x, v) / r;
float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
float3 result = 0;
r = length(x);
mu = dot(x, v) / r;
float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
// if x in space and ray intersects atmosphere
if (d > 0.0)
{
// move x to nearest intersection of ray with top atmosphere boundary
x += d * v;
t -= d;
mu = (r * mu + d) / RadiusAtmosphere;
r = RadiusAtmosphere;
}
// if x in space and ray intersects atmosphere
if (d > 0.0)
{
// move x to nearest intersection of ray with top atmosphere boundary
x += d * v;
t -= d;
mu = (r * mu + d) / RadiusAtmosphere;
r = RadiusAtmosphere;
}
float epsilon = 0.0045f;
float epsilon = 0.0045f;
// if ray intersects atmosphere
if (r <= RadiusAtmosphere)
{
float nu = dot(v, s);
float muS = dot(x, s) / r;
float phaseR = PhaseFunctionR(nu);
float phaseM = PhaseFunctionM(nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0);
// if ray intersects atmosphere
if (r <= RadiusAtmosphere)
{
float nu = dot(v, s);
float muS = dot(x, s) / r;
float phaseR = PhaseFunctionR(nu);
float phaseM = PhaseFunctionM(nu);
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0);
if (t > 0.0)
{
float3 x0 = x + t * v;
float r0 = length(x0);
float rMu0 = dot(x0, v);
float mu0 = rMu0 / r0;
float muS0 = dot(x0, s) / r0;
if (t > 0.0)
{
float3 x0 = x + t * v;
float r0 = length(x0);
float rMu0 = dot(x0, v);
float mu0 = rMu0 / r0;
float muS0 = dot(x0, s) / r0;
attenuation = AnalyticTransmittance(r, mu, t);
if (r0 > RadiusGround + 0.01)
{
// computes S[L]-T(x,x0)S[L]|x0
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0);
if (r0 > RadiusGround + 0.01)
{
// computes S[L]-T(x,x0)S[L]|x0
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0);
float muHoriz = -sqrt(1.0 - (RadiusGround / r) * (RadiusGround / r));
if (abs(mu - muHoriz) < epsilon)
{
{
mu = muHoriz - epsilon;
r0 = sqrt(r * r + t * t + 2.0 * r * t * mu);
mu0 = (r * mu + t) / r0;
@@ -256,18 +255,18 @@ float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r,
inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu);
inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu);
float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
float alpha = ((mu - muHoriz) + epsilon) / (2.0 * epsilon);
inscatter = lerp(inScatterA, inScatterB, alpha);
}
}
}
}
}
inscatter.w *= smoothstep(0.00, 0.02, muS);
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
}
return result;
return result;
}
static const float EPSILON_ATMOSPHERE = 0.002f;
@@ -280,40 +279,40 @@ static const float EPSILON_INSCATTER = 0.004f;
// output - return value: intersection occurred true/false
bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, out float maxPathLength)
{
offset = 0.0f;
maxPathLength = 0.0f;
offset = 0.0f;
maxPathLength = 0.0f;
// vector from ray origin to center of the sphere
float3 l = -viewPosition;
float l2 = dot(l, l);
float s = dot(l, d);
// vector from ray origin to center of the sphere
float3 l = -viewPosition;
float l2 = dot(l, l);
float s = dot(l, d);
// adjust top atmosphere boundary by small epsilon to prevent artifacts
float r = Rt - EPSILON_ATMOSPHERE;
float r2 = r * r;
if (l2 <= r2)
{
// ray origin inside sphere, hit is ensured
float m2 = l2 - (s * s);
float q = sqrt(r2 - m2);
maxPathLength = s + q;
return true;
}
else if (s >= 0)
{
// ray starts outside in front of sphere, hit is possible
float m2 = l2 - (s * s);
if (m2 <= r2)
{
// ray hits atmosphere definitely
float q = sqrt(r2 - m2);
offset = s - q;
maxPathLength = (s + q) - offset;
return true;
}
}
// adjust top atmosphere boundary by small epsilon to prevent artifacts
float r = Rt - EPSILON_ATMOSPHERE;
float r2 = r * r;
if (l2 <= r2)
{
// ray origin inside sphere, hit is ensured
float m2 = l2 - (s * s);
float q = sqrt(r2 - m2);
maxPathLength = s + q;
return true;
}
if (s >= 0)
{
// ray starts outside in front of sphere, hit is possible
float m2 = l2 - (s * s);
if (m2 <= r2)
{
// ray hits atmosphere definitely
float q = sqrt(r2 - m2);
offset = s - q;
maxPathLength = (s + q) - offset;
return true;
}
}
return false;
return false;
}
// input - surfacePos: reconstructed position of current pixel
@@ -323,105 +322,105 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset,
// output - return value: total in-scattered light
float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosition, in float3 surfacePos, in float3 viewDir, in out float3 attenuation, in out float irradianceFactor)
{
float3 inscatteredLight = float3(0.0f, 0.0f, 0.0f);
float offset;
float maxPathLength;
float3 inscatteredLight = float3(0.0f, 0.0f, 0.0f);
float offset;
float maxPathLength;
if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength))
{
return float3(offset / 10, 0, 0);
if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength))
{
return float3(offset / 10, 0, 0);
float pathLength = distance(viewPosition, surfacePos);
//return pathLength.xxx;
float pathLength = distance(viewPosition, surfacePos);
//return pathLength.xxx;
// check if object occludes atmosphere
if (pathLength > offset)
{
//return float3(1,0,0);
// check if object occludes atmosphere
if (pathLength > offset)
{
//return float3(1,0,0);
// offsetting camera
float3 startPos = viewPosition + offset * viewDir;
float startPosHeight = length(startPos);
pathLength -= offset;
// offsetting camera
float3 startPos = viewPosition + offset * viewDir;
float startPosHeight = length(startPos);
pathLength -= offset;
// starting position of path is now ensured to be inside atmosphere
// was either originally there or has been moved to top boundary
float muStartPos = dot(startPos, viewDir) / startPosHeight;
float nuStartPos = dot(viewDir, atmosphericFog.AtmosphericFogSunDirection);
float musStartPos = dot(startPos, atmosphericFog.AtmosphericFogSunDirection) / startPosHeight;
// starting position of path is now ensured to be inside atmosphere
// was either originally there or has been moved to top boundary
float muStartPos = dot(startPos, viewDir) / startPosHeight;
float nuStartPos = dot(viewDir, atmosphericFog.AtmosphericFogSunDirection);
float musStartPos = dot(startPos, atmosphericFog.AtmosphericFogSunDirection) / startPosHeight;
// in-scattering for infinite ray (light in-scattered when
// no surface hit or object behind atmosphere)
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, startPosHeight, muStartPos, musStartPos, nuStartPos), 0.0f);
float surfacePosHeight = length(surfacePos);
float musEndPos = dot(surfacePos, atmosphericFog.AtmosphericFogSunDirection) / surfacePosHeight;
//return float3(muStartPos, 0, 0);
// in-scattering for infinite ray (light in-scattered when
// no surface hit or object behind atmosphere)
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, startPosHeight, muStartPos, musStartPos, nuStartPos), 0.0f);
float surfacePosHeight = length(surfacePos);
float musEndPos = dot(surfacePos, atmosphericFog.AtmosphericFogSunDirection) / surfacePosHeight;
//return float3(muStartPos, 0, 0);
// check if object hit is inside atmosphere
if (pathLength < maxPathLength)
{
//return float3(1,0,0);
// check if object hit is inside atmosphere
if (pathLength < maxPathLength)
{
//return float3(1,0,0);
// reduce total in-scattered light when surface hit
// within atmosphere
// fíx described in chapter 5.1.1
attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this!! here
float muEndPos = dot(surfacePos, viewDir) / surfacePosHeight;
float4 inscatterSurface = Texture4DSample(AtmosphereInscatterTexture, surfacePosHeight, muEndPos, musEndPos, nuStartPos);
inscatter = max(inscatter - attenuation.rgbr * inscatterSurface, 0.0f);
irradianceFactor = 1.0f;
}
else
{
//return float3(1,0,0);
// reduce total in-scattered light when surface hit
// within atmosphere
// fíx described in chapter 5.1.1
attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this!! here
float muEndPos = dot(surfacePos, viewDir) / surfacePosHeight;
float4 inscatterSurface = Texture4DSample(AtmosphereInscatterTexture, surfacePosHeight, muEndPos, musEndPos, nuStartPos);
inscatter = max(inscatter - attenuation.rgbr * inscatterSurface, 0.0f);
irradianceFactor = 1.0f;
}
else
{
//return float3(1,0,0);
// retrieve extinction factor for inifinte ray
// fíx described in chapter 5.1.1
attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this! and here
}
/*
// avoids imprecision problems near horizon by interpolating between
// two points above and below horizon
// fíx described in chapter 5.1.2
float muHorizon = -sqrt(1.0 - (g_Rg / startPosHeight) * (g_Rg / startPosHeight));
if (abs(muStartPos - muHorizon) < EPSILON_INSCATTER)
{
float mu = muHorizon - EPSILON_INSCATTER;
float samplePosHeight = sqrt(startPosHeight*startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength*mu);
float muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight;
float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos);
float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos);
float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
mu = muHorizon + EPSILON_INSCATTER;
samplePosHeight = sqrt(startPosHeight * startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength * mu);
muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight;
inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos);
inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos);
float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0f);
float t = ((muStartPos - muHorizon) + EPSILON_INSCATTER) / (2.0 * EPSILON_INSCATTER);
inscatter = lerp(inScatterA, inScatterB, t);
}
*/
// retrieve extinction factor for inifinte ray
// fíx described in chapter 5.1.1
attenuation = AnalyticTransmittance(startPosHeight, muStartPos, pathLength); // TODO: optimize this! and here
}
/*
// avoids imprecision problems near horizon by interpolating between
// two points above and below horizon
// fíx described in chapter 5.1.2
float muHorizon = -sqrt(1.0 - (g_Rg / startPosHeight) * (g_Rg / startPosHeight));
if (abs(muStartPos - muHorizon) < EPSILON_INSCATTER)
{
float mu = muHorizon - EPSILON_INSCATTER;
float samplePosHeight = sqrt(startPosHeight*startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength*mu);
float muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight;
float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos);
float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos);
float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
mu = muHorizon + EPSILON_INSCATTER;
samplePosHeight = sqrt(startPosHeight * startPosHeight + pathLength * pathLength + 2.0f * startPosHeight * pathLength * mu);
muSamplePos = (startPosHeight * mu + pathLength) / samplePosHeight;
inScatter0 = Texture4DSample(AtmosphereInscatterTexture, startPosHeight, mu, musStartPos, nuStartPos);
inScatter1 = Texture4DSample(AtmosphereInscatterTexture, samplePosHeight, muSamplePos, musEndPos, nuStartPos);
float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0f);
float t = ((muStartPos - muHorizon) + EPSILON_INSCATTER) / (2.0 * EPSILON_INSCATTER);
inscatter = lerp(inScatterA, inScatterB, t);
}
*/
// avoids imprecision problems in Mie scattering when sun is below
//horizon
// fíx described in chapter 5.1.3
inscatter.w *= smoothstep(0.00, 0.02, musStartPos);
float phaseR = PhaseFunctionR(nuStartPos);
float phaseM = PhaseFunctionM(nuStartPos);
inscatteredLight = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0f);
// avoids imprecision problems in Mie scattering when sun is below
//horizon
// fíx described in chapter 5.1.3
inscatter.w *= smoothstep(0.00, 0.02, musStartPos);
float phaseR = PhaseFunctionR(nuStartPos);
float phaseM = PhaseFunctionM(nuStartPos);
inscatteredLight = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0f);
float sunIntensity = 10;
inscatteredLight *= sunIntensity;
}
}
float sunIntensity = 10;
inscatteredLight *= sunIntensity;
}
}
return inscatteredLight;
return inscatteredLight;
}
float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 viewPosition, float3 viewVector, float sceneDepth, float3 sceneColor)
{
float4 result = float4(1, 0, 0, 1);
float4 result = float4(1, 0, 0, 1);
#if 0
@@ -455,96 +454,96 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float
#if 1
// TODO: scale viewPosition from cm to km
// TODO: scale viewPosition from cm to km
float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale;
viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale;
//viewPosition *= atmosphericFog.AtmosphericFogDistanceScale;
viewPosition *= scale;
//viewPosition.xz *= 0.00001f;
float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale;
viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale;
//viewPosition *= atmosphericFog.AtmosphericFogDistanceScale;
viewPosition *= scale;
//viewPosition.xz *= 0.00001f;
//viewPosition *= scale;
viewPosition.y += RadiusGround + HeightOffset;
//viewPosition *= scale;
viewPosition.y += RadiusGround + HeightOffset;
//viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset;
//worldPosition
float Radius = length(viewPosition);
float3 V = normalize(viewVector);
float Mu = dot(viewPosition, V) / Radius;
float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround);
//viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset;
//worldPosition
float Radius = length(viewPosition);
float3 V = normalize(viewVector);
float Mu = dot(viewPosition, V) / Radius;
float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround);
//-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround)
/*
float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0);
float a = V.x * V.x + V.y * V.y - V.z * V.z;
float b = 2.0 * (g.x * V.x + g.y * V.y - g.z * V.z);
float c = g.x * g.x + g.y * g.y - g.z * g.z;
float d = -(b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
bool cone = d > 0.0 && abs(viewPosition.z + d * V.z - RadiusGround) <= 10.0;
if (T > 0.0)
{
if (cone && d < T)
T = d;
//return float4(1,0,0,1);
}
else if (cone)
{
T = d;
//return float4(0,0,1,1);
}
*/
//return float4(V, 1);
//-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround)
/*
float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0);
float a = V.x * V.x + V.y * V.y - V.z * V.z;
float b = 2.0 * (g.x * V.x + g.y * V.y - g.z * V.z);
float c = g.x * g.x + g.y * g.y - g.z * g.z;
float d = -(b + sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
bool cone = d > 0.0 && abs(viewPosition.z + d * V.z - RadiusGround) <= 10.0;
if (T > 0.0)
{
if (cone && d < T)
T = d;
//return float4(1,0,0,1);
}
else if (cone)
{
T = d;
//return float4(0,0,1,1);
}
*/
//return float4(V, 1);
// TODO: create uniform param for that depth limit
float depthThreshold = min(100.0f * atmosphericFog.AtmosphericFogDistanceScale, (viewFar * 0.997f) * scale); // 100km limit or far plane
sceneDepth *= scale;
// TODO: create uniform param for that depth limit
float depthThreshold = min(100.0f * atmosphericFog.AtmosphericFogDistanceScale, (viewFar * 0.997f) * scale); // 100km limit or far plane
sceneDepth *= scale;
float fogDepth = max(0.0f, sceneDepth - atmosphericFog.AtmosphericFogStartDistance);
float shadowFactor = 1.0f;
float DistanceRatio = min(fogDepth * 0.1f / atmosphericFog.AtmosphericFogStartDistance, 1.0f);
bool isSceneGeometry = (sceneDepth < depthThreshold); // Assume as scene geometry
//isSceneGeometry = false;
if (isSceneGeometry)
{
shadowFactor = DistanceRatio * atmosphericFog.AtmosphericFogPower;
T = max(sceneDepth + atmosphericFog.AtmosphericFogDistanceOffset, 1.0f);
float fogDepth = max(0.0f, sceneDepth - atmosphericFog.AtmosphericFogStartDistance);
float shadowFactor = 1.0f;
float DistanceRatio = min(fogDepth * 0.1f / atmosphericFog.AtmosphericFogStartDistance, 1.0f);
bool isSceneGeometry = (sceneDepth < depthThreshold); // Assume as scene geometry
//isSceneGeometry = false;
if (isSceneGeometry)
{
shadowFactor = DistanceRatio * atmosphericFog.AtmosphericFogPower;
T = max(sceneDepth + atmosphericFog.AtmosphericFogDistanceOffset, 1.0f);
// TODO: fog for scene objects
return 0;
//return float4(0, 1, 0, 1);
}
// TODO: fog for scene objects
return 0;
//return float4(0, 1, 0, 1);
}
float3 attenuation;
float3 inscatterColor = GetInscatterColor(fogDepth, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation, isSceneGeometry); //S[L]-T(viewPosition,xs)S[l]|xs
//float3 inscatterColor = inscatter(viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation); //S[L]-T(viewPosition,xs)S[l]|xs
//float3 groundColor = 0;
float3 groundColor = GetGroundColor(float4(sceneColor.rgb, 1.f), viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, attenuation, isSceneGeometry); //R[L0]+R[L*]
float3 sun = GetSunColor(atmosphericFog, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu); //L0
float3 attenuation;
float3 inscatterColor = GetInscatterColor(fogDepth, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation, isSceneGeometry); //S[L]-T(viewPosition,xs)S[l]|xs
//float3 inscatterColor = inscatter(viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu, attenuation); //S[L]-T(viewPosition,xs)S[l]|xs
//float3 groundColor = 0;
float3 groundColor = GetGroundColor(float4(sceneColor.rgb, 1.f), viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, attenuation, isSceneGeometry); //R[L0]+R[L*]
float3 sun = GetSunColor(atmosphericFog, viewPosition, T, V, atmosphericFog.AtmosphericFogSunDirection, Radius, Mu); //L0
float3 color = (atmosphericFog.AtmosphericFogSunPower) * (sun + groundColor + inscatterColor) * atmosphericFog.AtmosphericFogSunColor.rgb;
//return float4(color, lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)) );
//return lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio));
//color *= lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio));
float3 color = (atmosphericFog.AtmosphericFogSunPower) * (sun + groundColor + inscatterColor) * atmosphericFog.AtmosphericFogSunColor.rgb;
//return float4(color, lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio)) );
//return lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio));
//color *= lerp(saturate(attenuation.r * atmosphericFog.AtmosphericFogDensityScale - atmosphericFog.AtmosphericFogDensityOffset), 1.0f, (1.0f - DistanceRatio));
//return attenuation.bbbb;
//return attenuation.bbbb;
return float4(color, 1);
return float4(color, 1);
//return float4(sun, 1);
//return float4(inscatterColor + sun, 1);
//return float4(sun + groundColor, 1);
//return float4(sun + groundColor + inscatterColor, 1);
//return float4(sun, 1);
//return float4(inscatterColor + sun, 1);
//return float4(sun + groundColor, 1);
//return float4(sun + groundColor + inscatterColor, 1);
#endif
return result;
return result;
}
float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 worldPosition, float3 cameraPosition)
{
float3 viewVector = worldPosition - cameraPosition;
return GetAtmosphericFog(atmosphericFog, viewFar, cameraPosition, viewVector, length(viewVector), float3(0, 0, 0));
float3 viewVector = worldPosition - cameraPosition;
return GetAtmosphericFog(atmosphericFog, viewFar, cameraPosition, viewVector, length(viewVector), float3(0, 0, 0));
}
#endif

View File

@@ -7,55 +7,55 @@
float3 Diffuse_Lambert(float3 diffuseColor)
{
return diffuseColor * (1 / PI);
return diffuseColor * (1 / PI);
}
// GGX / Trowbridge-Reitz
// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"]
float D_GGX(float roughness, float NoH)
{
float a = roughness * roughness;
float a2 = a * a;
float d = (NoH * a2 - NoH) * NoH + 1;
return a2 / (PI * d * d);
float a = roughness * roughness;
float a2 = a * a;
float d = (NoH * a2 - NoH) * NoH + 1;
return a2 / (PI * d * d);
}
// Tuned to match behavior of Vis_Smith
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
float Vis_Schlick(float roughness, float NoV, float NoL)
{
float k = Square(roughness) * 0.5;
float visSchlickV = NoV * (1 - k) + k;
float visSchlickL = NoL * (1 - k) + k;
return 0.25 / (visSchlickV * visSchlickL);
float k = Square(roughness) * 0.5;
float visSchlickV = NoV * (1 - k) + k;
float visSchlickL = NoL * (1 - k) + k;
return 0.25 / (visSchlickV * visSchlickL);
}
// Smith term for GGX
// [Smith 1967, "Geometrical shadowing of a random rough surface"]
float Vis_Smith(float roughness, float NoV, float NoL)
{
float a = Square(roughness);
float a2 = a * a;
float visSmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2);
float visSmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2);
return rcp(visSmithV * visSmithL);
float a = Square(roughness);
float a2 = a * a;
float visSmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2);
float visSmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2);
return rcp(visSmithV * visSmithL);
}
// Appoximation of joint Smith term for GGX
// [Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"]
float Vis_SmithJointApprox(float roughness, float NoV, float NoL)
{
float a = Square(roughness);
float visSmithV = NoL * (NoV * (1 - a) + a);
float visSmithL = NoV * (NoL * (1 - a) + a);
return 0.5 * rcp(visSmithV + visSmithL);
float a = Square(roughness);
float visSmithV = NoL * (NoV * (1 - a) + a);
float visSmithL = NoV * (NoL * (1 - a) + a);
return 0.5 * rcp(visSmithV + visSmithL);
}
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
float3 F_Schlick(float3 specularColor, float VoH)
{
float fc = Pow5(1 - VoH);
return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor;
float fc = Pow5(1 - VoH);
return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor;
}
#define REFLECTION_CAPTURE_NUM_MIPS 7
@@ -64,44 +64,44 @@ float3 F_Schlick(float3 specularColor, float VoH)
half ProbeMipFromRoughness(half roughness)
{
half mip1px = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return REFLECTION_CAPTURE_NUM_MIPS - 1 - mip1px;
half mip1px = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return REFLECTION_CAPTURE_NUM_MIPS - 1 - mip1px;
}
half SSRMipFromRoughness(half roughness)
{
half mip1px = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return max(1, 10 - mip1px);
half mip1px = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
return max(1, 10 - mip1px);
}
float ProbeRoughnessFromMip(float mip)
{
float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip;
return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE);
float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip;
return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE);
}
// [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"]
float3 EnvBRDFApprox(float3 specularColor, float roughness, float NoV)
{
// Approximate version, base for pre integrated version
const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
half4 r = roughness * c0 + c1;
half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
half2 ab = half2(-1.04, 1.04) * a004 + r.zw;
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
// Approximate version, base for pre integrated version
const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
half4 r = roughness * c0 + c1;
half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
half2 ab = half2(-1.04, 1.04) * a004 + r.zw;
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
}
// Importance sampled preintegrated G * F
float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness, float NoV)
{
float2 ab = preIntegratedGF.SampleLevel(SamplerLinearClamp, float2(NoV, roughness), 0).rg;
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
float2 ab = preIntegratedGF.SampleLevel(SamplerLinearClamp, float2(NoV, roughness), 0).rg;
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
}
float RoughnessToSpecularPower(float roughness)
{
return pow(2, 13 * (1 - roughness));
return pow(2, 13 * (1 - roughness));
}
#endif

View File

@@ -5,17 +5,17 @@
bool RayHitSphere(float3 r, float3 sphereCenter, float sphereRadius)
{
float3 closestPointOnRay = max(0, dot(sphereCenter, r)) * r;
float3 centerToRay = closestPointOnRay - sphereCenter;
return dot(centerToRay, centerToRay) <= (sphereRadius * sphereRadius);
float3 closestPointOnRay = max(0, dot(sphereCenter, r)) * r;
float3 centerToRay = closestPointOnRay - sphereCenter;
return dot(centerToRay, centerToRay) <= (sphereRadius * sphereRadius);
}
bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3 rectZ, float rectExtentX, float rectExtentY)
{
float3 pointOnPlane = r * max(0, dot(rectZ, rectCenter) / dot(rectZ, r));
bool inExtentX = abs(dot(rectX, pointOnPlane - rectCenter)) <= rectExtentX;
bool inExtentY = abs(dot(rectY, pointOnPlane - rectCenter)) <= rectExtentY;
return inExtentX && inExtentY;
float3 pointOnPlane = r * max(0, dot(rectZ, rectCenter) / dot(rectZ, r));
bool inExtentX = abs(dot(rectX, pointOnPlane - rectCenter)) <= rectExtentX;
bool inExtentY = abs(dot(rectY, pointOnPlane - rectCenter)) <= rectExtentY;
return inExtentX && inExtentY;
}
// Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd).
@@ -24,21 +24,21 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3
// Hit point is: hitPoint = lineStart + (lineEnd - lineStart) * intersections.x/y.
float2 LineHitBox(float3 lineStart, float3 lineEnd, float3 boxMin, float3 boxMax)
{
float3 invDirection = 1.0f / (lineEnd - lineStart);
float3 enterIntersection = (boxMin - lineStart) * invDirection;
float3 exitIntersection = (boxMax - lineStart) * invDirection;
float3 minIntersections = min(enterIntersection, exitIntersection);
float3 maxIntersections = max(enterIntersection, exitIntersection);
float2 intersections;
intersections.x = max(minIntersections.x, max(minIntersections.y, minIntersections.z));
intersections.y = min(maxIntersections.x, min(maxIntersections.y, maxIntersections.z));
return saturate(intersections);
float3 invDirection = 1.0f / (lineEnd - lineStart);
float3 enterIntersection = (boxMin - lineStart) * invDirection;
float3 exitIntersection = (boxMax - lineStart) * invDirection;
float3 minIntersections = min(enterIntersection, exitIntersection);
float3 maxIntersections = max(enterIntersection, exitIntersection);
float2 intersections;
intersections.x = max(minIntersections.x, max(minIntersections.y, minIntersections.z));
intersections.y = min(maxIntersections.x, min(maxIntersections.y, maxIntersections.z));
return saturate(intersections);
}
// Determines whether there is an intersection between a box and a sphere.
bool BoxIntersectsSphere(float3 boxMin, float3 boxMax, float3 sphereCenter, float sphereRadius)
{
const float3 clampedCenter = clamp(sphereCenter, boxMin, boxMax);
const float3 clampedCenter = clamp(sphereCenter, boxMin, boxMax);
return distance(sphereCenter, clampedCenter) <= sphereRadius;
}

View File

@@ -134,12 +134,12 @@ SamplerComparisonState ShadowSamplerPCF : register(s5);
// Structure that contains information about GBuffer
struct GBufferData
{
float4 ViewInfo; // x-1/Projection[0,0] y-1/Projection[1,1] z-(Far / (Far - Near) w-(-Far * Near) / (Far - Near) / Far)
float4 ScreenSize; // x-Width y-Height z-1/Width w-1/Height
float3 ViewPos; // view position (in world space)
float ViewFar; // view far plane distance (in world space)
float4x4 InvViewMatrix; // inverse view matrix (4 rows by 4 columns)
float4x4 InvProjectionMatrix; // inverse projection matrix (4 rows by 4 columns)
float4 ViewInfo; // x-1/Projection[0,0], y-1/Projection[1,1], z-(Far / (Far - Near), w-(-Far * Near) / (Far - Near) / Far)
float4 ScreenSize; // x-Width, y-Height, z-1/Width, w-1/Height
float3 ViewPos; // view position (in world space)
float ViewFar; // view far plane distance (in world space)
float4x4 InvViewMatrix; // inverse view matrix (4 rows by 4 columns)
float4x4 InvProjectionMatrix; // inverse projection matrix (4 rows by 4 columns)
};
#ifdef PLATFORM_ANDROID
@@ -152,28 +152,28 @@ struct GBufferData
// Structure that contains information about atmosphere fog
struct AtmosphericFogData
{
float AtmosphericFogDensityScale;
float AtmosphericFogSunDiscScale;
float AtmosphericFogDistanceScale;
float AtmosphericFogGroundOffset;
float AtmosphericFogDensityScale;
float AtmosphericFogSunDiscScale;
float AtmosphericFogDistanceScale;
float AtmosphericFogGroundOffset;
float AtmosphericFogAltitudeScale;
float AtmosphericFogStartDistance;
float AtmosphericFogPower;
float AtmosphericFogDistanceOffset;
float AtmosphericFogAltitudeScale;
float AtmosphericFogStartDistance;
float AtmosphericFogPower;
float AtmosphericFogDistanceOffset;
float3 AtmosphericFogSunDirection;
float AtmosphericFogSunPower;
float3 AtmosphericFogSunDirection;
float AtmosphericFogSunPower;
float3 AtmosphericFogSunColor;
float AtmosphericFogDensityOffset;
float3 AtmosphericFogSunColor;
float AtmosphericFogDensityOffset;
};
// Packed env probe data
struct ProbeData
{
float4 Data0; // x - Position.x, y - Position.y, z - Position.z, w - unused
float4 Data1; // x - Radius , y - 1 / Radius, z - Brightness, w - unused
float4 Data0; // x - Position.x, y - Position.y, z - Position.z, w - unused
float4 Data1; // x - Radius , y - 1 / Radius, z - Brightness, w - unused
};
#define ProbePos Data0.xyz
@@ -183,25 +183,25 @@ struct ProbeData
struct Quad_VS2PS
{
float4 Position : SV_Position;
noperspective float2 TexCoord : TEXCOORD0;
float4 Position : SV_Position;
noperspective float2 TexCoord : TEXCOORD0;
};
struct Quad_VS2GS
{
Quad_VS2PS Vertex;
uint LayerIndex : TEXCOORD1;
Quad_VS2PS Vertex;
uint LayerIndex : TEXCOORD1;
};
struct Quad_GS2PS
{
Quad_VS2PS Vertex;
uint LayerIndex : SV_RenderTargetArrayIndex;
Quad_VS2PS Vertex;
uint LayerIndex : SV_RenderTargetArrayIndex;
};
float Luminance(float3 color)
{
return dot(color, float3(0.299f, 0.587f, 0.114f));
return dot(color, float3(0.299f, 0.587f, 0.114f));
}
// Quaternion multiplication (http://mathworld.wolfram.com/Quaternion.html)
@@ -220,13 +220,13 @@ float3 QuatRotateVector(float4 q, float3 v)
// Samples the unwrapped 3D texture (eg. volume texture of size 16x16x16 would be unwrapped to 256x16)
float4 SampleUnwrappedTexture3D(Texture2D tex, SamplerState s, float3 uvw, float size)
{
float intW = floor(uvw.z * size - 0.5);
half fracW = uvw.z * size - 0.5 - intW;
float u = (uvw.x + intW) / size;
float v = uvw.y;
float4 rg0 = tex.Sample(s, float2(u, v));
float4 rg1 = tex.Sample(s, float2(u + 1.0f / size, v));
return lerp(rg0, rg1, fracW);
float intW = floor(uvw.z * size - 0.5);
half fracW = uvw.z * size - 0.5 - intW;
float u = (uvw.x + intW) / size;
float v = uvw.y;
float4 rg0 = tex.Sample(s, float2(u, v));
float4 rg1 = tex.Sample(s, float2(u + 1.0f / size, v));
return lerp(rg0, rg1, fracW);
}
#endif

View File

@@ -3,46 +3,46 @@
#define __QUAD_OVERDRAW__
RWTexture2D<uint> lockUAV : register(u0);
RWTexture2D<uint> overdrawUAV : register(u1);
RWTexture2D<uint> overdrawUAV : register(u1);
RWTexture2D<uint> liveCountUAV : register(u2);
void DoQuadOverdraw(float4 svPos, uint primId)
{
uint2 quad = svPos.xy * 0.5;
uint prevID;
uint unlockedID = 0xffffffff;
bool processed = false;
int lockCount = 0;
int pixelCount = 0;
uint2 quad = svPos.xy * 0.5;
uint prevID;
uint unlockedID = 0xffffffff;
bool processed = false;
int lockCount = 0;
int pixelCount = 0;
for (int i = 0; i < 64; i++)
{
if (!processed)
InterlockedCompareExchange(lockUAV[quad], unlockedID, primId, prevID);
[branch]
if (prevID == unlockedID)
{
if (++lockCount == 4)
{
// Retrieve live pixel count (minus 1) in quad
InterlockedAnd(liveCountUAV[quad], 0, pixelCount);
for (int i = 0; i < 64; i++)
{
if (!processed)
InterlockedCompareExchange(lockUAV[quad], unlockedID, primId, prevID);
[branch]
if (prevID == unlockedID)
{
if (++lockCount == 4)
{
// Retrieve live pixel count (minus 1) in quad
InterlockedAnd(liveCountUAV[quad], 0, pixelCount);
// Unlock for other quads
InterlockedExchange(lockUAV[quad], unlockedID, prevID);
}
processed = true;
}
if (prevID == primId && !processed)
{
InterlockedAdd(liveCountUAV[quad], 1);
processed = true;
}
}
// Unlock for other quads
InterlockedExchange(lockUAV[quad], unlockedID, prevID);
}
processed = true;
}
if (prevID == primId && !processed)
{
InterlockedAdd(liveCountUAV[quad], 1);
processed = true;
}
}
if (lockCount)
{
InterlockedAdd(overdrawUAV[quad], 1);
}
if (lockCount)
{
InterlockedAdd(overdrawUAV[quad], 1);
}
}
#endif

View File

@@ -9,82 +9,82 @@
// Structure that contains information about exponential height fog
struct ExponentialHeightFogData
{
float3 FogInscatteringColor;
float FogMinOpacity;
float3 FogInscatteringColor;
float FogMinOpacity;
float FogDensity;
float FogHeight;
float FogHeightFalloff;
float FogAtViewPosition;
float FogDensity;
float FogHeight;
float FogHeightFalloff;
float FogAtViewPosition;
float3 InscatteringLightDirection;
float ApplyDirectionalInscattering;
float3 InscatteringLightDirection;
float ApplyDirectionalInscattering;
float3 DirectionalInscatteringColor;
float DirectionalInscatteringExponent;
float3 DirectionalInscatteringColor;
float DirectionalInscatteringExponent;
float FogCutoffDistance;
float VolumetricFogMaxDistance;
float DirectionalInscatteringStartDistance;
float StartDistance;
float FogCutoffDistance;
float VolumetricFogMaxDistance;
float DirectionalInscatteringStartDistance;
float StartDistance;
};
float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance)
{
float3 cameraToPos = posWS - camWS;
float cameraToPosSqr = dot(cameraToPos, cameraToPos);
float cameraToPosLenInv = rsqrt(cameraToPosSqr);
float cameraToPosLen = cameraToPosSqr * cameraToPosLenInv;
float3 cameraToReceiverNorm = cameraToPos * cameraToPosLenInv;
float3 cameraToPos = posWS - camWS;
float cameraToPosSqr = dot(cameraToPos, cameraToPos);
float cameraToPosLenInv = rsqrt(cameraToPosSqr);
float cameraToPosLen = cameraToPosSqr * cameraToPosLenInv;
float3 cameraToReceiverNorm = cameraToPos * cameraToPosLenInv;
float rayOriginTerms = exponentialHeightFog.FogAtViewPosition;
float rayLength = cameraToPosLen;
float rayDirectionY = cameraToPos.y;
float rayOriginTerms = exponentialHeightFog.FogAtViewPosition;
float rayLength = cameraToPosLen;
float rayDirectionY = cameraToPos.y;
// Apply start distance offset
skipDistance = max(skipDistance, exponentialHeightFog.StartDistance);
if (skipDistance > 0)
{
float excludeIntersectionTime = skipDistance * cameraToPosLenInv;
float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToPos.y;
float exclusionIntersectionY = camWS.y + cameraToExclusionIntersectionY;
rayLength = (1.0f - excludeIntersectionTime) * cameraToPosLen;
rayDirectionY = cameraToPos.y - cameraToExclusionIntersectionY;
float exponent = exponentialHeightFog.FogHeightFalloff * (exclusionIntersectionY - exponentialHeightFog.FogHeight);
rayOriginTerms = exponentialHeightFog.FogDensity * exp2(-exponent);
}
// Apply start distance offset
skipDistance = max(skipDistance, exponentialHeightFog.StartDistance);
if (skipDistance > 0)
{
float excludeIntersectionTime = skipDistance * cameraToPosLenInv;
float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToPos.y;
float exclusionIntersectionY = camWS.y + cameraToExclusionIntersectionY;
rayLength = (1.0f - excludeIntersectionTime) * cameraToPosLen;
rayDirectionY = cameraToPos.y - cameraToExclusionIntersectionY;
float exponent = exponentialHeightFog.FogHeightFalloff * (exclusionIntersectionY - exponentialHeightFog.FogHeight);
rayOriginTerms = exponentialHeightFog.FogDensity * exp2(-exponent);
}
// Calculate the integral of the ray starting from the view to the object position with the fog density function
float falloff = max(-127.0f, exponentialHeightFog.FogHeightFalloff * rayDirectionY);
float lineIntegral = (1.0f - exp2(-falloff)) / falloff;
float lineIntegralTaylor = log(2.0f) - (0.5f * Pow2(log(2.0f))) * falloff;
float exponentialHeightLineIntegralCalc = rayOriginTerms * (abs(falloff) > 0.01f ? lineIntegral : lineIntegralTaylor);
float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength;
// Calculate the integral of the ray starting from the view to the object position with the fog density function
float falloff = max(-127.0f, exponentialHeightFog.FogHeightFalloff * rayDirectionY);
float lineIntegral = (1.0f - exp2(-falloff)) / falloff;
float lineIntegralTaylor = log(2.0f) - (0.5f * Pow2(log(2.0f))) * falloff;
float exponentialHeightLineIntegralCalc = rayOriginTerms * (abs(falloff) > 0.01f ? lineIntegral : lineIntegralTaylor);
float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength;
// Calculate the light that went through the fog
float expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity);
// Calculate the light that went through the fog
float expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity);
// Calculate the directional light inscattering
float3 inscatteringColor = exponentialHeightFog.FogInscatteringColor;
float3 directionalInscattering = 0;
BRANCH
if (exponentialHeightFog.ApplyDirectionalInscattering > 0)
{
float3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNorm, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent);
float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f);
float directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral));
directionalInscattering = directionalLightInscattering * (1.0f - directionalInscatteringFogFactor);
}
// Calculate the directional light inscattering
float3 inscatteringColor = exponentialHeightFog.FogInscatteringColor;
float3 directionalInscattering = 0;
BRANCH
if (exponentialHeightFog.ApplyDirectionalInscattering > 0)
{
float3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNorm, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent);
float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f);
float directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral));
directionalInscattering = directionalLightInscattering * (1.0f - directionalInscatteringFogFactor);
}
// Disable fog after a certain distance
FLATTEN
if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance)
{
expFogFactor = 1;
directionalInscattering = 0;
}
// Disable fog after a certain distance
FLATTEN
if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance)
{
expFogFactor = 1;
directionalInscattering = 0;
}
return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor);
return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor);
}
#endif

View File

@@ -11,7 +11,7 @@
Texture2D GBuffer0 : register(t0);
Texture2D GBuffer1 : register(t1);
Texture2D GBuffer2 : register(t2);
Texture2D Depth : register(t3);
Texture2D Depth : register(t3);
#if defined(USE_GBUFFER_CUSTOM_DATA)
Texture2D GBuffer3 : register(t4);
#endif
@@ -27,28 +27,28 @@ Texture2D GBuffer3 : register(t4);
// Linearize raw device depth
float LinearizeZ(GBufferData gBuffer, float depth)
{
return gBuffer.ViewInfo.w / (depth - gBuffer.ViewInfo.z);
return gBuffer.ViewInfo.w / (depth - gBuffer.ViewInfo.z);
}
// Convert linear depth to device depth
float LinearZ2DeviceDepth(GBufferData gBuffer, float linearDepth)
{
return (gBuffer.ViewInfo.w / linearDepth) + gBuffer.ViewInfo.z;
return (gBuffer.ViewInfo.w / linearDepth) + gBuffer.ViewInfo.z;
}
// Get view space position at given pixel coordinate with given device depth
float3 GetViewPos(GBufferData gBuffer, float2 uv, float deviceDepth)
{
float4 clipPos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), deviceDepth, 1.0);
float4 viewPos = mul(clipPos, gBuffer.InvProjectionMatrix);
return viewPos.xyz / viewPos.w;
float4 clipPos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), deviceDepth, 1.0);
float4 viewPos = mul(clipPos, gBuffer.InvProjectionMatrix);
return viewPos.xyz / viewPos.w;
}
// Get world space position at given pixel coordinate with given device depth
float3 GetWorldPos(GBufferData gBuffer, float2 uv, float deviceDepth)
{
float3 viewPos = GetViewPos(gBuffer, uv, deviceDepth);
return mul(float4(viewPos, 1), gBuffer.InvViewMatrix).xyz;
float3 viewPos = GetViewPos(gBuffer, uv, deviceDepth);
return mul(float4(viewPos, 1), gBuffer.InvViewMatrix).xyz;
}
#if !defined(NO_GBUFFER_SAMPLING)
@@ -56,92 +56,92 @@ float3 GetWorldPos(GBufferData gBuffer, float2 uv, float deviceDepth)
// Sample raw device depth buffer
float SampleZ(float2 uv)
{
return SAMPLE_RT(Depth, uv).r;
return SAMPLE_RT(Depth, uv).r;
}
// Sample linear depth
float SampleDepth(GBufferData gBuffer, float2 uv)
{
float deviceDepth = SampleZ(uv);
return LinearizeZ(gBuffer, deviceDepth);
float deviceDepth = SampleZ(uv);
return LinearizeZ(gBuffer, deviceDepth);
}
// Get view space position at given pixel coordinate
float3 GetViewPos(GBufferData gBuffer, float2 uv)
{
float deviceDepth = SampleZ(uv);
return GetViewPos(gBuffer, uv, deviceDepth);
float deviceDepth = SampleZ(uv);
return GetViewPos(gBuffer, uv, deviceDepth);
}
// Get world space position at given pixel coordinate
float3 GetWorldPos(GBufferData gBuffer, float2 uv)
{
float deviceDepth = SampleZ(uv);
return GetWorldPos(gBuffer, uv, deviceDepth);
float deviceDepth = SampleZ(uv);
return GetWorldPos(gBuffer, uv, deviceDepth);
}
// Sample normal vector with pixel shading model
float3 SampleNormal(float2 uv, out int shadingModel)
{
// Sample GBuffer
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
// Sample GBuffer
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
// Decode normal and shading model
shadingModel = (int)(gBuffer1.a * 3.999);
return DecodeNormal(gBuffer1.rgb);
// Decode normal and shading model
shadingModel = (int)(gBuffer1.a * 3.999);
return DecodeNormal(gBuffer1.rgb);
}
// Sample GBuffer
GBufferSample SampleGBuffer(GBufferData gBuffer, float2 uv)
{
GBufferSample result;
GBufferSample result;
// Sample GBuffer
float4 gBuffer0 = SAMPLE_RT(GBuffer0, uv);
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
float4 gBuffer2 = SAMPLE_RT(GBuffer2, uv);
// Sample GBuffer
float4 gBuffer0 = SAMPLE_RT(GBuffer0, uv);
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
float4 gBuffer2 = SAMPLE_RT(GBuffer2, uv);
#if defined(USE_GBUFFER_CUSTOM_DATA)
float4 gBuffer3 = SAMPLE_RT(GBuffer3, uv);
#endif
// Decode normal and shading model
result.Normal = DecodeNormal(gBuffer1.rgb);
result.ShadingModel = (int)(gBuffer1.a * 3.999);
// Decode normal and shading model
result.Normal = DecodeNormal(gBuffer1.rgb);
result.ShadingModel = (int)(gBuffer1.a * 3.999);
// Calculate view space and world space positions
result.ViewPos = GetViewPos(gBuffer, uv);
result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz;
// Calculate view space and world space positions
result.ViewPos = GetViewPos(gBuffer, uv);
result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz;
// Decode GBuffer data
result.Color = gBuffer0.rgb;
result.AO = gBuffer0.a;
result.Roughness = gBuffer2.r;
result.Metalness = gBuffer2.g;
result.Specular = gBuffer2.b;
// Decode GBuffer data
result.Color = gBuffer0.rgb;
result.AO = gBuffer0.a;
result.Roughness = gBuffer2.r;
result.Metalness = gBuffer2.g;
result.Specular = gBuffer2.b;
#if defined(USE_GBUFFER_CUSTOM_DATA)
result.CustomData = gBuffer3;
#endif
return result;
return result;
}
// Sample GBuffer (fast - only few parameters are being sampled)
GBufferSample SampleGBufferFast(GBufferData gBuffer, float2 uv)
{
GBufferSample result;
GBufferSample result;
// Sample GBuffer
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
// Sample GBuffer
float4 gBuffer1 = SAMPLE_RT(GBuffer1, uv);
// Decode normal and shading model
result.Normal = DecodeNormal(gBuffer1.rgb);
result.ShadingModel = (int)(gBuffer1.a * 3.999);
// Decode normal and shading model
result.Normal = DecodeNormal(gBuffer1.rgb);
result.ShadingModel = (int)(gBuffer1.a * 3.999);
// Calculate view space position
result.ViewPos = GetViewPos(gBuffer, uv);
result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz;
// Calculate view space position
result.ViewPos = GetViewPos(gBuffer, uv);
result.WorldPos = mul(float4(result.ViewPos, 1), gBuffer.InvViewMatrix).xyz;
return result;
return result;
}
#if defined(USE_GBUFFER_CUSTOM_DATA)

View File

@@ -8,15 +8,15 @@
// GBuffer sample data structure
struct GBufferSample
{
float3 Normal;
float Roughness;
float3 WorldPos;
float Metalness;
float3 Color;
float Specular;
float3 ViewPos;
float AO;
int ShadingModel;
float3 Normal;
float Roughness;
float3 WorldPos;
float Metalness;
float3 Color;
float Specular;
float3 ViewPos;
float AO;
int ShadingModel;
#if defined(USE_GBUFFER_CUSTOM_DATA)
float4 CustomData;
#endif
@@ -24,45 +24,45 @@ struct GBufferSample
bool IsSubsurfaceMode(int shadingModel)
{
return shadingModel == SHADING_MODEL_SUBSURFACE || shadingModel == SHADING_MODEL_FOLIAGE;
return shadingModel == SHADING_MODEL_SUBSURFACE || shadingModel == SHADING_MODEL_FOLIAGE;
}
float3 GetDiffuseColor(in float3 color, in float metalness)
{
return color - color * metalness;
return color - color * metalness;
}
float3 GetSpecularColor(in float3 color, in float specular, in float metalness)
{
return lerp(0.08 * specular.xxx, color.rgb, metalness.xxx);
return lerp(0.08 * specular.xxx, color.rgb, metalness.xxx);
}
// Calculate material diffuse color
float3 GetDiffuseColor(in GBufferSample gBuffer)
{
return gBuffer.Color - gBuffer.Color * gBuffer.Metalness;
return gBuffer.Color - gBuffer.Color * gBuffer.Metalness;
}
// Calculate material specular color
float3 GetSpecularColor(in GBufferSample gBuffer)
{
return lerp(0.08 * gBuffer.Specular.xxx, gBuffer.Color.rgb, gBuffer.Metalness.xxx);
return lerp(0.08 * gBuffer.Specular.xxx, gBuffer.Color.rgb, gBuffer.Metalness.xxx);
}
// Compact Normal Storage for Small G-Buffers
// [Aras Pranckevièius 2010, http://aras-p.info/texts/CompactNormalStorage.html]
float3 EncodeNormal(float3 n)
{
// Pack to [0;1] range
return n * 0.5 + 0.5;
// Pack to [0;1] range
return n * 0.5 + 0.5;
}
// Compact Normal Storage for Small G-Buffers
// [Aras Pranckevièius 2010, http://aras-p.info/texts/CompactNormalStorage.html]
float3 DecodeNormal(float3 enc)
{
// Unpack from [0;1] range
return normalize(enc * 2 - 1);
// Unpack from [0;1] range
return normalize(enc * 2 - 1);
}
#endif

View File

@@ -158,7 +158,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesState,
}
if (cascadeIndex == data.CascadesCount)
return data.FallbackIrradiance;
float probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w;
float3 probesOrigin = data.ProbesScrollOffsets[cascadeIndex].xyz * probesSpacing + data.ProbesOriginAndSpacing[cascadeIndex].xyz;
float3 probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f);
@@ -196,7 +196,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D<snorm float4> probesState,
// Smooth backface test
float weight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f);
// Sample distance texture
float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe);
float2 uv = GetDDGIProbeUV(data, cascadeIndex, probeIndex, octahedralCoords, DDGI_PROBE_RESOLUTION_DISTANCE);

View File

@@ -14,114 +14,114 @@
struct GlobalSurfaceTile
{
float4 AtlasRectUV;
float4x4 WorldToLocal;
float3 ViewBoundsSize;
float4 AtlasRectUV;
float4x4 WorldToLocal;
float3 ViewBoundsSize;
};
struct GlobalSurfaceObject
{
float3 BoundsPosition;
float BoundsRadius;
float4x4 WorldToLocal;
float3 Extent;
float3 BoundsPosition;
float BoundsRadius;
float4x4 WorldToLocal;
float3 Extent;
bool UseVisibility;
uint TileOffsets[6];
uint DataSize; // count of float4s for object+tiles
uint TileOffsets[6];
uint DataSize; // count of float4s for object+tiles
};
float4 LoadGlobalSurfaceAtlasObjectBounds(Buffer<float4> objects, uint objectAddress)
{
// This must match C++
return objects.Load(objectAddress + 0);
// This must match C++
return objects.Load(objectAddress + 0);
}
uint LoadGlobalSurfaceAtlasObjectDataSize(Buffer<float4> objects, uint objectAddress)
{
// This must match C++
return asuint(objects.Load(objectAddress + 1).w);
// This must match C++
return asuint(objects.Load(objectAddress + 1).w);
}
GlobalSurfaceObject LoadGlobalSurfaceAtlasObject(Buffer<float4> objects, uint objectAddress)
{
// This must match C++
float4 vector0 = objects.Load(objectAddress + 0);
float4 vector1 = objects.Load(objectAddress + 1);
float4 vector2 = objects.Load(objectAddress + 2);
float4 vector3 = objects.Load(objectAddress + 3);
float4 vector4 = objects.Load(objectAddress + 4);
float4 vector5 = objects.Load(objectAddress + 5);
GlobalSurfaceObject object = (GlobalSurfaceObject)0;
object.BoundsPosition = vector0.xyz;
object.BoundsRadius = vector0.w;
object.WorldToLocal[0] = float4(vector2.xyz, 0.0f);
object.WorldToLocal[1] = float4(vector3.xyz, 0.0f);
object.WorldToLocal[2] = float4(vector4.xyz, 0.0f);
object.WorldToLocal[3] = float4(vector2.w, vector3.w, vector4.w, 1.0f);
object.Extent = vector5.xyz;
object.UseVisibility = vector5.w > 0.5f;
uint vector1x = asuint(vector1.x);
uint vector1y = asuint(vector1.y);
uint vector1z = asuint(vector1.z);
object.DataSize = asuint(vector1.w);
object.TileOffsets[0] = vector1x & 0xffff;
object.TileOffsets[1] = vector1x >> 16;
object.TileOffsets[2] = vector1y & 0xffff;
object.TileOffsets[3] = vector1y >> 16;
object.TileOffsets[4] = vector1z & 0xffff;
object.TileOffsets[5] = vector1z >> 16;
return object;
// This must match C++
float4 vector0 = objects.Load(objectAddress + 0);
float4 vector1 = objects.Load(objectAddress + 1);
float4 vector2 = objects.Load(objectAddress + 2);
float4 vector3 = objects.Load(objectAddress + 3);
float4 vector4 = objects.Load(objectAddress + 4);
float4 vector5 = objects.Load(objectAddress + 5);
GlobalSurfaceObject object = (GlobalSurfaceObject)0;
object.BoundsPosition = vector0.xyz;
object.BoundsRadius = vector0.w;
object.WorldToLocal[0] = float4(vector2.xyz, 0.0f);
object.WorldToLocal[1] = float4(vector3.xyz, 0.0f);
object.WorldToLocal[2] = float4(vector4.xyz, 0.0f);
object.WorldToLocal[3] = float4(vector2.w, vector3.w, vector4.w, 1.0f);
object.Extent = vector5.xyz;
object.UseVisibility = vector5.w > 0.5f;
uint vector1x = asuint(vector1.x);
uint vector1y = asuint(vector1.y);
uint vector1z = asuint(vector1.z);
object.DataSize = asuint(vector1.w);
object.TileOffsets[0] = vector1x & 0xffff;
object.TileOffsets[1] = vector1x >> 16;
object.TileOffsets[2] = vector1y & 0xffff;
object.TileOffsets[3] = vector1y >> 16;
object.TileOffsets[4] = vector1z & 0xffff;
object.TileOffsets[5] = vector1z >> 16;
return object;
}
GlobalSurfaceTile LoadGlobalSurfaceAtlasTile(Buffer<float4> objects, uint tileAddress)
{
// This must match C++
float4 vector0 = objects.Load(tileAddress + 0);
float4 vector1 = objects.Load(tileAddress + 1);
float4 vector2 = objects.Load(tileAddress + 2);
float4 vector3 = objects.Load(tileAddress + 3);
float4 vector4 = objects.Load(tileAddress + 4); // w unused
GlobalSurfaceTile tile = (GlobalSurfaceTile)0;
tile.AtlasRectUV = vector0.xyzw;
tile.WorldToLocal[0] = float4(vector1.xyz, 0.0f);
tile.WorldToLocal[1] = float4(vector2.xyz, 0.0f);
tile.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
tile.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
tile.ViewBoundsSize = vector4.xyz;
return tile;
// This must match C++
float4 vector0 = objects.Load(tileAddress + 0);
float4 vector1 = objects.Load(tileAddress + 1);
float4 vector2 = objects.Load(tileAddress + 2);
float4 vector3 = objects.Load(tileAddress + 3);
float4 vector4 = objects.Load(tileAddress + 4); // w unused
GlobalSurfaceTile tile = (GlobalSurfaceTile)0;
tile.AtlasRectUV = vector0.xyzw;
tile.WorldToLocal[0] = float4(vector1.xyz, 0.0f);
tile.WorldToLocal[1] = float4(vector2.xyz, 0.0f);
tile.WorldToLocal[2] = float4(vector3.xyz, 0.0f);
tile.WorldToLocal[3] = float4(vector1.w, vector2.w, vector3.w, 1.0f);
tile.ViewBoundsSize = vector4.xyz;
return tile;
}
// Global Surface Atlas data for a constant buffer
struct GlobalSurfaceAtlasData
{
float3 ViewPos;
float Padding0;
float Padding1;
float Resolution;
float ChunkSize;
uint ObjectsCount;
float3 ViewPos;
float Padding0;
float Padding1;
float Resolution;
float ChunkSize;
uint ObjectsCount;
};
float3 SampleGlobalSurfaceAtlasTex(Texture2D atlas, float2 atlasUV, float4 bilinearWeights)
{
float4 sampleX = atlas.GatherRed(SamplerLinearClamp, atlasUV);
float4 sampleY = atlas.GatherGreen(SamplerLinearClamp, atlasUV);
float4 sampleZ = atlas.GatherBlue(SamplerLinearClamp, atlasUV);
return float3(dot(sampleX, bilinearWeights), dot(sampleY, bilinearWeights), dot(sampleZ, bilinearWeights));
float4 sampleX = atlas.GatherRed(SamplerLinearClamp, atlasUV);
float4 sampleY = atlas.GatherGreen(SamplerLinearClamp, atlasUV);
float4 sampleZ = atlas.GatherBlue(SamplerLinearClamp, atlasUV);
return float3(dot(sampleX, bilinearWeights), dot(sampleY, bilinearWeights), dot(sampleZ, bilinearWeights));
}
float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSurfaceObject object, GlobalSurfaceTile tile, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold)
{
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED
// Tile normal weight based on the sampling angle
float3 tileNormal = normalize(mul(worldNormal, (float3x3)tile.WorldToLocal));
float normalWeight = saturate(dot(float3(0, 0, -1), tileNormal));
normalWeight = (normalWeight - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD) / (1.0f - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD);
if (normalWeight <= 0.0f && object.UseVisibility)
return 0;
// Tile normal weight based on the sampling angle
float3 tileNormal = normalize(mul(worldNormal, (float3x3)tile.WorldToLocal));
float normalWeight = saturate(dot(float3(0, 0, -1), tileNormal));
normalWeight = (normalWeight - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD) / (1.0f - GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD);
if (normalWeight <= 0.0f && object.UseVisibility)
return 0;
#endif
// Get tile UV and depth at the world position
// Get tile UV and depth at the world position
float3 tilePosition = mul(float4(worldPosition, 1), tile.WorldToLocal).xyz;
float tileDepth = tilePosition.z / tile.ViewBoundsSize.z;
float2 tileUV = saturate((tilePosition.xy / tile.ViewBoundsSize.xy) + 0.5f);
@@ -129,15 +129,15 @@ float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSur
tileUV = min(tileUV, 0.999999f);
float2 atlasUV = tileUV * tile.AtlasRectUV.zw + tile.AtlasRectUV.xy;
// Calculate bilinear weights
float2 bilinearWeightsUV = frac(atlasUV * data.Resolution + 0.5f);
float4 bilinearWeights;
bilinearWeights.x = (1.0 - bilinearWeightsUV.x) * (bilinearWeightsUV.y);
bilinearWeights.y = (bilinearWeightsUV.x) * (bilinearWeightsUV.y);
bilinearWeights.z = (bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y);
bilinearWeights.w = (1 - bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y);
// Calculate bilinear weights
float2 bilinearWeightsUV = frac(atlasUV * data.Resolution + 0.5f);
float4 bilinearWeights;
bilinearWeights.x = (1.0 - bilinearWeightsUV.x) * (bilinearWeightsUV.y);
bilinearWeights.y = (bilinearWeightsUV.x) * (bilinearWeightsUV.y);
bilinearWeights.z = (bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y);
bilinearWeights.w = (1 - bilinearWeightsUV.x) * (1 - bilinearWeightsUV.y);
// Tile depth weight based on sample position occlusion
// Tile depth weight based on sample position occlusion
float4 tileZ = depth.Gather(SamplerLinearClamp, atlasUV, 0.0f);
float depthThreshold = 2.0f * surfaceThreshold / tile.ViewBoundsSize.z;
float4 depthVisibility = 1.0f;
@@ -150,74 +150,74 @@ float4 SampleGlobalSurfaceAtlasTile(const GlobalSurfaceAtlasData data, GlobalSur
}
float sampleWeight = dot(depthVisibility, bilinearWeights);
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_WEIGHT_ENABLED
if (object.UseVisibility)
if (object.UseVisibility)
sampleWeight *= normalWeight;
#endif
if (sampleWeight <= 0.0f)
return 0;
bilinearWeights *= depthVisibility;
//bilinearWeights = normalize(bilinearWeights);
bilinearWeights *= depthVisibility;
//bilinearWeights = normalize(bilinearWeights);
// Sample atlas texture
float3 sampleColor = SampleGlobalSurfaceAtlasTex(atlas, atlasUV, bilinearWeights);
// Sample atlas texture
float3 sampleColor = SampleGlobalSurfaceAtlasTex(atlas, atlasUV, bilinearWeights);
//return float4(sampleWeight.xxx, sampleWeight);
return float4(sampleColor.rgb * sampleWeight, sampleWeight);
//return float4(normalWeight.xxx, sampleWeight);
//return float4(sampleWeight.xxx, sampleWeight);
return float4(sampleColor.rgb * sampleWeight, sampleWeight);
//return float4(normalWeight.xxx, sampleWeight);
}
// Samples the Global Surface Atlas and returns the lighting (with opacity) at the given world location (and direction).
// surfaceThreshold - Additional threshold (in world-units) between object or tile size compared with input data (error due to SDF or LOD incorrect appearance)
float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, ByteAddressBuffer chunks, ByteAddressBuffer culledObjects, Buffer<float4> objects, Texture2D depth, Texture2D atlas, float3 worldPosition, float3 worldNormal, float surfaceThreshold = 20.0f)
{
float4 result = float4(0, 0, 0, 0);
float4 result = float4(0, 0, 0, 0);
// Snap to the closest chunk to get culled objects
uint3 chunkCoord = (uint3)clamp(floor((worldPosition - data.ViewPos) / data.ChunkSize + (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)), 0, GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION - 1);
uint chunkAddress = (chunkCoord.z * (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION) + chunkCoord.y * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION + chunkCoord.x) * 4;
uint objectsStart = chunks.Load(chunkAddress);
if (objectsStart == 0)
{
// Empty chunk
return result;
}
// Snap to the closest chunk to get culled objects
uint3 chunkCoord = (uint3)clamp(floor((worldPosition - data.ViewPos) / data.ChunkSize + (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * 0.5f)), 0, GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION - 1);
uint chunkAddress = (chunkCoord.z * (GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION) + chunkCoord.y * GLOBAL_SURFACE_ATLAS_CHUNKS_RESOLUTION + chunkCoord.x) * 4;
uint objectsStart = chunks.Load(chunkAddress);
if (objectsStart == 0)
{
// Empty chunk
return result;
}
// Read objects counter
uint objectsCount = culledObjects.Load(objectsStart * 4);
if (objectsCount > data.ObjectsCount) // Prevents crashing - don't know why the data is invalid here (rare issue when moving fast though scene with terrain)
return result;
objectsStart++;
// Read objects counter
uint objectsCount = culledObjects.Load(objectsStart * 4);
if (objectsCount > data.ObjectsCount) // Prevents crashing - don't know why the data is invalid here (rare issue when moving fast though scene with terrain)
return result;
objectsStart++;
// Loop over culled objects inside the chunk
LOOP
for (uint objectIndex = 0; objectIndex < objectsCount; objectIndex++)
{
// Cull point vs sphere
uint objectAddress = culledObjects.Load(objectsStart * 4);
objectsStart++;
float4 objectBounds = LoadGlobalSurfaceAtlasObjectBounds(objects, objectAddress);
if (distance(objectBounds.xyz, worldPosition) > objectBounds.w)
continue;
GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(objects, objectAddress);
float3 localPosition = mul(float4(worldPosition, 1), object.WorldToLocal).xyz;
float3 localExtent = object.Extent + surfaceThreshold;
if (any(localPosition > localExtent) || any(localPosition < -localExtent))
continue;
// Loop over culled objects inside the chunk
LOOP
for (uint objectIndex = 0; objectIndex < objectsCount; objectIndex++)
{
// Cull point vs sphere
uint objectAddress = culledObjects.Load(objectsStart * 4);
objectsStart++;
float4 objectBounds = LoadGlobalSurfaceAtlasObjectBounds(objects, objectAddress);
if (distance(objectBounds.xyz, worldPosition) > objectBounds.w)
continue;
GlobalSurfaceObject object = LoadGlobalSurfaceAtlasObject(objects, objectAddress);
float3 localPosition = mul(float4(worldPosition, 1), object.WorldToLocal).xyz;
float3 localExtent = object.Extent + surfaceThreshold;
if (any(localPosition > localExtent) || any(localPosition < -localExtent))
continue;
// Remove the scale vector from the transformation matrix
float3x3 worldToLocal = (float3x3)object.WorldToLocal;
float scaleX = length(worldToLocal[0]);
float scaleY = length(worldToLocal[1]);
float scaleZ = length(worldToLocal[2]);
float3 invScale = float3(
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
worldToLocal[0] *= invScale.x;
worldToLocal[1] *= invScale.y;
worldToLocal[2] *= invScale.z;
// Remove the scale vector from the transformation matrix
float3x3 worldToLocal = (float3x3)object.WorldToLocal;
float scaleX = length(worldToLocal[0]);
float scaleY = length(worldToLocal[1]);
float scaleZ = length(worldToLocal[2]);
float3 invScale = float3(
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
worldToLocal[0] *= invScale.x;
worldToLocal[1] *= invScale.y;
worldToLocal[2] *= invScale.z;
// Sample tiles based on the directionality
// Sample tiles based on the directionality
#if GLOBAL_SURFACE_ATLAS_TILE_NORMAL_THRESHOLD_ENABLED
float3 localNormal = normalize(mul(worldNormal, worldToLocal));
float3 localNormalSq = localNormal * localNormal;
@@ -240,47 +240,47 @@ float4 SampleGlobalSurfaceAtlas(const GlobalSurfaceAtlasData data, ByteAddressBu
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
#else
uint tileOffset = object.TileOffsets[0];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[1];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[2];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[3];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[4];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[5];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
uint tileOffset = object.TileOffsets[0];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[1];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[2];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[3];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[4];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
tileOffset = object.TileOffsets[5];
if (tileOffset != 0)
{
GlobalSurfaceTile tile = LoadGlobalSurfaceAtlasTile(objects, objectAddress + tileOffset);
result += SampleGlobalSurfaceAtlasTile(data, object, tile, depth, atlas, worldPosition, worldNormal, surfaceThreshold);
}
#endif
}
}
// Normalize result
result.rgb /= max(result.a, 0.0001f);
// Normalize result
result.rgb /= max(result.a, 0.0001f);
return result;
return result;
}

View File

@@ -9,53 +9,53 @@
struct Render2DVertex
{
float2 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Color : COLOR0;
float4 CustomDataAndClipOrigin : TEXCOORD1; // x-per-geometry type, y-features mask, zw-clip origin
float4 ClipExtents : TEXCOORD2;
float2 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Color : COLOR0;
float4 CustomDataAndClipOrigin : TEXCOORD1; // x-per-geometry type, y-features mask, zw-clip origin
float4 ClipExtents : TEXCOORD2;
};
struct VS2PS
{
float4 Position : SV_Position;
float4 Color : COLOR0;
float2 TexCoord : TEXCOORD0;
float2 CustomData : TEXCOORD1;
float4 ClipExtents : TEXCOORD2;
float4 ClipOriginAndPos : TEXCOORD3;
float4 Position : SV_Position;
float4 Color : COLOR0;
float2 TexCoord : TEXCOORD0;
float2 CustomData : TEXCOORD1;
float4 ClipExtents : TEXCOORD2;
float4 ClipOriginAndPos : TEXCOORD3;
};
float cross2(float2 a, float2 b)
{
return a.x * b.y - a.y * b.x;
return a.x * b.y - a.y * b.x;
}
// Given a point p and a parallelogram defined by point a and vectors b and c, determines in p is inside the parallelogram. Returns a 4-vector that can be used with the clip instruction.
float4 PointInParallelogram(float2 p, float2 a, float4 bc)
{
float2 o = p - a;
float invD = 1 / cross2(bc.xy, bc.zw);
float2 t = (o.x * bc.yw - o.y * bc.xz) * float2(-invD, invD);
return float4(t, float2(1, 1) - t);
float2 o = p - a;
float invD = 1 / cross2(bc.xy, bc.zw);
float2 t = (o.x * bc.yw - o.y * bc.xz) * float2(-invD, invD);
return float4(t, float2(1, 1) - t);
}
void PerformClipping(float2 clipOrigin, float2 windowPos, float4 clipExtents)
{
#if CLIPPING_ENABLE
// Clip pixels which are outside of the clipping rect
float4 clipTest = PointInParallelogram(windowPos, clipOrigin, clipExtents);
// Clip pixels which are outside of the clipping rect
float4 clipTest = PointInParallelogram(windowPos, clipOrigin, clipExtents);
// Clip pixels which are outside of the clipping rect
clip(clipTest);
// Clip pixels which are outside of the clipping rect
clip(clipTest);
#endif
}
void PerformClipping(VS2PS input)
{
PerformClipping(input.ClipOriginAndPos.xy, input.ClipOriginAndPos.zw, input.ClipExtents);
PerformClipping(input.ClipOriginAndPos.xy, input.ClipOriginAndPos.zw, input.ClipExtents);
}
#endif

View File

@@ -9,69 +9,69 @@
// http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
float3 FastTonemap(float3 c)
{
return c * rcp(max(max(c.r, c.g), c.b) + 1.0);
return c * rcp(max(max(c.r, c.g), c.b) + 1.0);
}
float4 FastTonemap(float4 c)
{
return float4(FastTonemap(c.rgb), c.a);
return float4(FastTonemap(c.rgb), c.a);
}
float3 FastTonemap(float3 c, float w)
{
return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0));
return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0));
}
float4 FastTonemap(float4 c, float w)
{
return float4(FastTonemap(c.rgb, w), c.a);
return float4(FastTonemap(c.rgb, w), c.a);
}
float3 FastTonemapInvert(float3 c)
{
return c * rcp(1.0 - max(max(c.r, c.g), c.b));
return c * rcp(1.0 - max(max(c.r, c.g), c.b));
}
float4 FastTonemapInvert(float4 c)
{
return float4(FastTonemapInvert(c.rgb), c.a);
return float4(FastTonemapInvert(c.rgb), c.a);
}
float LinearToSrgbChannel(float linearColor)
{
if (linearColor < 0.00313067)
return linearColor * 12.92;
return pow(linearColor, (1.0 / 2.4)) * 1.055 - 0.055;
if (linearColor < 0.00313067)
return linearColor * 12.92;
return pow(linearColor, (1.0 / 2.4)) * 1.055 - 0.055;
}
float3 LinearToSrgb(float3 linearColor)
{
return float3(
LinearToSrgbChannel(linearColor.r),
LinearToSrgbChannel(linearColor.g),
LinearToSrgbChannel(linearColor.b));
return float3(
LinearToSrgbChannel(linearColor.r),
LinearToSrgbChannel(linearColor.g),
LinearToSrgbChannel(linearColor.b));
}
float3 sRGBToLinear(float3 color)
{
color = max(6.10352e-5, color);
return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92);
color = max(6.10352e-5, color);
return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92);
}
float3 LogToLinear(float3 logColor)
{
const float linearRange = 14.0f;
const float linearGrey = 0.18f;
const float exposureGrey = 444.0f;
return exp2((logColor - exposureGrey / 1023.0) * linearRange) * linearGrey;
const float linearRange = 14.0f;
const float linearGrey = 0.18f;
const float exposureGrey = 444.0f;
return exp2((logColor - exposureGrey / 1023.0) * linearRange) * linearGrey;
}
float3 LinearToLog(float3 linearColor)
{
const float linearRange = 14.0f;
const float linearGrey = 0.18f;
const float exposureGrey = 444.0f;
return saturate(log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0f);
const float linearRange = 14.0f;
const float linearGrey = 0.18f;
const float exposureGrey = 444.0f;
return saturate(log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0f);
}
#endif

View File

@@ -10,11 +10,11 @@ float4 TextureGatherRed(Texture2D tex, SamplerState sam, float2 uv)
#if CAN_USE_GATHER
return tex.GatherRed(sam, uv);
#else
float x = tex.Sample(sam, uv, int2(0, 1)).x;
float y = tex.Sample(sam, uv, int2(1, 1)).x;
float z = tex.Sample(sam, uv, int2(1, 0)).x;
float w = tex.Sample(sam, uv, int2(0, 0)).x;
return float4(x, y, z, w);
float x = tex.Sample(sam, uv, int2(0, 1)).x;
float y = tex.Sample(sam, uv, int2(1, 1)).x;
float z = tex.Sample(sam, uv, int2(1, 0)).x;
float w = tex.Sample(sam, uv, int2(0, 0)).x;
return float4(x, y, z, w);
#endif
}
@@ -23,11 +23,11 @@ float4 TextureGatherRed(Texture2DArray tex, SamplerState sam, float3 uv)
#if CAN_USE_GATHER
return tex.GatherRed(sam, uv);
#else
float x = tex.Sample(sam, uv, int2(0, 1)).x;
float y = tex.Sample(sam, uv, int2(1, 1)).x;
float z = tex.Sample(sam, uv, int2(1, 0)).x;
float w = tex.Sample(sam, uv, int2(0, 0)).x;
return float4(x, y, z, w);
float x = tex.Sample(sam, uv, int2(0, 1)).x;
float y = tex.Sample(sam, uv, int2(1, 1)).x;
float z = tex.Sample(sam, uv, int2(1, 0)).x;
float w = tex.Sample(sam, uv, int2(0, 0)).x;
return float4(x, y, z, w);
#endif
}
@@ -36,11 +36,11 @@ float4 TextureGatherRed(Texture2D tex, SamplerState sam, float2 uv, int2 offset)
#if CAN_USE_GATHER
return tex.GatherRed(sam, uv, offset);
#else
float x = tex.Sample(sam, uv, offset + int2(0, 1)).x;
float y = tex.Sample(sam, uv, offset + int2(1, 1)).x;
float z = tex.Sample(sam, uv, offset + int2(1, 0)).x;
float w = tex.Sample(sam, uv, offset + int2(0, 0)).x;
return float4(x, y, z, w);
float x = tex.Sample(sam, uv, offset + int2(0, 1)).x;
float y = tex.Sample(sam, uv, offset + int2(1, 1)).x;
float z = tex.Sample(sam, uv, offset + int2(1, 0)).x;
float w = tex.Sample(sam, uv, offset + int2(0, 0)).x;
return float4(x, y, z, w);
#endif
}
@@ -49,11 +49,11 @@ float4 TextureGatherRed(Texture2D<float> tex, SamplerState sam, float2 uv)
#if CAN_USE_GATHER
return tex.GatherRed(sam, uv);
#else
float x = tex.Sample(sam, uv, int2(0, 1)).x;
float y = tex.Sample(sam, uv, int2(1, 1)).x;
float z = tex.Sample(sam, uv, int2(1, 0)).x;
float w = tex.Sample(sam, uv, int2(0, 0)).x;
return float4(x, y, z, w);
float x = tex.Sample(sam, uv, int2(0, 1)).x;
float y = tex.Sample(sam, uv, int2(1, 1)).x;
float z = tex.Sample(sam, uv, int2(1, 0)).x;
float w = tex.Sample(sam, uv, int2(0, 0)).x;
return float4(x, y, z, w);
#endif
}
@@ -62,11 +62,11 @@ float4 TextureGatherRed(Texture2D<float> tex, SamplerState sam, float2 uv, int2
#if CAN_USE_GATHER
return tex.GatherRed(sam, uv, offset);
#else
float x = tex.Sample(sam, uv, offset + int2(0, 1)).x;
float y = tex.Sample(sam, uv, offset + int2(1, 1)).x;
float z = tex.Sample(sam, uv, offset + int2(1, 0)).x;
float w = tex.Sample(sam, uv, offset + int2(0, 0)).x;
return float4(x, y, z, w);
float x = tex.Sample(sam, uv, offset + int2(0, 1)).x;
float y = tex.Sample(sam, uv, offset + int2(1, 1)).x;
float z = tex.Sample(sam, uv, offset + int2(1, 0)).x;
float w = tex.Sample(sam, uv, offset + int2(0, 0)).x;
return float4(x, y, z, w);
#endif
}

View File

@@ -11,52 +11,52 @@
// Global SDF data for a constant buffer
struct GlobalSDFData
{
float4 CascadePosDistance[4];
float4 CascadeVoxelSize;
float2 Padding;
float4 CascadePosDistance[4];
float4 CascadeVoxelSize;
float2 Padding;
uint CascadesCount;
float Resolution;
float Resolution;
};
// Global SDF ray trace settings.
struct GlobalSDFTrace
{
float3 WorldPosition;
float MinDistance;
float3 WorldDirection;
float MaxDistance;
float StepScale;
bool NeedsHitNormal;
float3 WorldPosition;
float MinDistance;
float3 WorldDirection;
float MaxDistance;
float StepScale;
bool NeedsHitNormal;
void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f)
{
WorldPosition = worldPosition;
WorldDirection = worldDirection;
MinDistance = minDistance;
MaxDistance = maxDistance;
StepScale = stepScale;
NeedsHitNormal = false;
}
void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f)
{
WorldPosition = worldPosition;
WorldDirection = worldDirection;
MinDistance = minDistance;
MaxDistance = maxDistance;
StepScale = stepScale;
NeedsHitNormal = false;
}
};
// Global SDF ray trace hit information.
struct GlobalSDFHit
{
float3 HitNormal;
float HitTime;
uint HitCascade;
uint StepsCount;
float HitSDF;
float3 HitNormal;
float HitTime;
uint HitCascade;
uint StepsCount;
float HitSDF;
bool IsHit()
{
return HitTime >= 0.0f;
}
bool IsHit()
{
return HitTime >= 0.0f;
}
float3 GetHitPosition(const GlobalSDFTrace trace)
{
return trace.WorldPosition + trace.WorldDirection * HitTime;
}
float3 GetHitPosition(const GlobalSDFTrace trace)
{
return trace.WorldPosition + trace.WorldDirection * HitTime;
}
};
void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float cascadeMaxDistance, out float3 cascadeUV, out float3 textureUV)
@@ -71,182 +71,182 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP
// Samples the Global SDF and returns the distance to the closest surface (in world units) at the given world location.
float SampleGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition)
{
float distance = data.CascadePosDistance[3].w * 2.0f;
if (distance <= 0.0f)
return GLOBAL_SDF_WORLD_SIZE;
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 1.0f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
distance = cascadeDistance * cascadeMaxDistance;
break;
}
}
return distance;
float distance = data.CascadePosDistance[3].w * 2.0f;
if (distance <= 0.0f)
return GLOBAL_SDF_WORLD_SIZE;
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 1.0f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
distance = cascadeDistance * cascadeMaxDistance;
break;
}
}
return distance;
}
// Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector.
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, float3 worldPosition, out float distance)
{
float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE;
if (data.CascadePosDistance[3].w <= 0.0f)
return gradient;
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
distance = cascadeDistance * cascadeMaxDistance;
break;
}
}
return gradient;
float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE;
if (data.CascadePosDistance[3].w <= 0.0f)
return gradient;
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < 0.9f && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
distance = cascadeDistance * cascadeMaxDistance;
break;
}
}
return gradient;
}
// Samples the Global SDF and returns the gradient vector (derivative) at the given world location. Normalize it to get normal vector.
float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, float3 worldPosition, out float distance)
{
float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE;
if (data.CascadePosDistance[3].w <= 0.0f)
return gradient;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < chunkSizeDistance && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistanceTex < chunkMarginDistance * 2)
{
cascadeDistance = cascadeDistanceTex;
}
float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
distance = cascadeDistance * cascadeMaxDistance;
break;
}
}
return gradient;
float3 gradient = float3(0, 0.00001f, 0);
distance = GLOBAL_SDF_WORLD_SIZE;
if (data.CascadePosDistance[3].w <= 0.0f)
return gradient;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
for (uint cascade = 0; cascade < data.CascadesCount; cascade++)
{
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeMaxDistance, cascadeUV, textureUV);
float cascadeDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistance < chunkSizeDistance && !any(cascadeUV < 0) && !any(cascadeUV > 1))
{
float cascadeDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (cascadeDistanceTex < chunkMarginDistance * 2)
{
cascadeDistance = cascadeDistanceTex;
}
float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
gradient = float3(xp - xn, yp - yn, zp - zn) * cascadeMaxDistance;
distance = cascadeDistance * cascadeMaxDistance;
break;
}
}
return gradient;
}
// Ray traces the Global SDF.
// cascadeTraceStartBias - scales the trace start position offset (along the trace direction) by cascade voxel size (reduces artifacts on far cascades). Use it for shadow rays to prevent self-occlusion when tracing from object surface that looses quality in far cascades.
GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D<float> tex, Texture3D<float> mip, const GlobalSDFTrace trace, float cascadeTraceStartBias = 0.0f)
{
GlobalSDFHit hit = (GlobalSDFHit)0;
hit.HitTime = -1.0f;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
float nextIntersectionStart = 0.0f;
float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2);
float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance;
for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
float voxelSize = data.CascadeVoxelSize[cascade];
float voxelExtent = voxelSize * 0.5f;
float cascadeMinStep = voxelSize;
float3 worldPosition = trace.WorldPosition + trace.WorldDirection * (voxelSize * cascadeTraceStartBias);
GlobalSDFHit hit = (GlobalSDFHit)0;
hit.HitTime = -1.0f;
float chunkSizeDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_SIZE / data.Resolution; // Size of the chunk in SDF distance (0-1)
float chunkMarginDistance = (float)GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN / data.Resolution; // Size of the chunk margin in SDF distance (0-1)
float nextIntersectionStart = 0.0f;
float traceMaxDistance = min(trace.MaxDistance, data.CascadePosDistance[3].w * 2);
float3 traceEndPosition = trace.WorldPosition + trace.WorldDirection * traceMaxDistance;
for (uint cascade = 0; cascade < data.CascadesCount && hit.HitTime < 0.0f; cascade++)
{
float4 cascadePosDistance = data.CascadePosDistance[cascade];
float voxelSize = data.CascadeVoxelSize[cascade];
float voxelExtent = voxelSize * 0.5f;
float cascadeMinStep = voxelSize;
float3 worldPosition = trace.WorldPosition + trace.WorldDirection * (voxelSize * cascadeTraceStartBias);
// Hit the cascade bounds to find the intersection points
float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www);
intersections.xy *= traceMaxDistance;
intersections.x = max(intersections.x, nextIntersectionStart);
float stepTime = intersections.x;
if (intersections.x >= intersections.y)
{
// Skip the current cascade if the ray starts outside it
stepTime = intersections.y;
}
else
{
// Skip the current cascade tracing on the next cascade
nextIntersectionStart = intersections.y;
}
// Hit the cascade bounds to find the intersection points
float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www);
intersections.xy *= traceMaxDistance;
intersections.x = max(intersections.x, nextIntersectionStart);
float stepTime = intersections.x;
if (intersections.x >= intersections.y)
{
// Skip the current cascade if the ray starts outside it
stepTime = intersections.y;
}
else
{
// Skip the current cascade tracing on the next cascade
nextIntersectionStart = intersections.y;
}
// Walk over the cascade SDF
uint step = 0;
LOOP
for (; step < 250 && stepTime < intersections.y; step++)
{
float3 stepPosition = worldPosition + trace.WorldDirection * stepTime;
// Walk over the cascade SDF
uint step = 0;
LOOP
for (; step < 250 && stepTime < intersections.y; step++)
{
float3 stepPosition = worldPosition + trace.WorldDirection * stepTime;
// Sample SDF
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV);
float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (stepDistance < chunkSizeDistance)
{
float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (stepDistanceTex < chunkMarginDistance * 2)
{
stepDistance = stepDistanceTex;
}
}
else
{
// Assume no SDF nearby so perform a jump
stepDistance = chunkSizeDistance;
}
stepDistance *= cascadeMaxDistance;
// Sample SDF
float cascadeMaxDistance;
float3 cascadeUV, textureUV;
GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeMaxDistance, cascadeUV, textureUV);
float stepDistance = mip.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (stepDistance < chunkSizeDistance)
{
float stepDistanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0);
if (stepDistanceTex < chunkMarginDistance * 2)
{
stepDistance = stepDistanceTex;
}
}
else
{
// Assume no SDF nearby so perform a jump
stepDistance = chunkSizeDistance;
}
stepDistance *= cascadeMaxDistance;
// Detect surface hit
float minSurfaceThickness = voxelExtent * saturate(stepTime / (voxelExtent * 2.0f));
if (stepDistance < minSurfaceThickness)
{
// Surface hit
hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f);
hit.HitCascade = cascade;
hit.HitSDF = stepDistance;
if (trace.NeedsHitNormal)
{
// Calculate hit normal from SDF gradient
float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn));
}
break;
}
// Detect surface hit
float minSurfaceThickness = voxelExtent * saturate(stepTime / (voxelExtent * 2.0f));
if (stepDistance < minSurfaceThickness)
{
// Surface hit
hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f);
hit.HitCascade = cascade;
hit.HitSDF = stepDistance;
if (trace.NeedsHitNormal)
{
// Calculate hit normal from SDF gradient
float texelOffset = 1.0f / data.Resolution;
float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x;
float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x;
float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x;
float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x;
float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x;
float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x;
hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn));
}
break;
}
// Move forward
stepTime += max(stepDistance * trace.StepScale, cascadeMinStep);
}
hit.StepsCount += step;
}
return hit;
// Move forward
stepTime += max(stepDistance * trace.StepScale, cascadeMinStep);
}
hit.StepsCount += step;
}
return hit;
}
// Calculates the surface threshold for Global Surface Atlas sampling which matches the Global SDF trace to reduce artifacts

View File

@@ -6,10 +6,10 @@
// Calculate IES light profile from 1D texture
float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection)
{
float3 l = normalize(worldPosition - lightPosition);
float d = dot(lightPosition, lightDirection);
float angle = asin(d) / PI + 0.5f;
return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r;
float3 l = normalize(worldPosition - lightPosition);
float d = dot(lightPosition, lightDirection);
float angle = asin(d) / PI + 0.5f;
return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r;
}
#endif

View File

@@ -11,150 +11,150 @@
ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
{
ShadowData shadow;
shadow.SurfaceShadow = gBuffer.AO * shadowMask.r;
shadow.TransmissionShadow = shadowMask.g;
return shadow;
ShadowData shadow;
shadow.SurfaceShadow = gBuffer.AO * shadowMask.r;
shadow.TransmissionShadow = shadowMask.g;
return shadow;
}
LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
{
float3 diffuseColor = GetDiffuseColor(gBuffer);
float3 H = normalize(V + L);
float NoL = saturate(dot(N, L));
float NoV = max(dot(N, V), 1e-5);
float NoH = saturate(dot(N, H));
float VoH = saturate(dot(V, H));
float3 diffuseColor = GetDiffuseColor(gBuffer);
float3 H = normalize(V + L);
float NoL = saturate(dot(N, L));
float NoV = max(dot(N, V), 1e-5);
float NoH = saturate(dot(N, H));
float VoH = saturate(dot(V, H));
LightingData lighting;
lighting.Diffuse = Diffuse_Lambert(diffuseColor);
LightingData lighting;
lighting.Diffuse = Diffuse_Lambert(diffuseColor);
#if NO_SPECULAR
lighting.Specular = 0;
lighting.Specular = 0;
#else
float3 specularColor = GetSpecularColor(gBuffer);
float3 F = F_Schlick(specularColor, VoH);
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
lighting.Specular = (D * Vis) * F;
float3 specularColor = GetSpecularColor(gBuffer);
float3 F = F_Schlick(specularColor, VoH);
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
lighting.Specular = (D * Vis) * F;
#endif
lighting.Transmission = 0;
return lighting;
lighting.Transmission = 0;
return lighting;
}
LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
{
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
#if defined(USE_GBUFFER_CUSTOM_DATA)
// Fake effect of the light going through the material
float3 subsurfaceColor = gBuffer.CustomData.rgb;
float opacity = gBuffer.CustomData.a;
float3 H = normalize(V + L);
float inscatter = pow(saturate(dot(L, -V)), 12.1f) * lerp(3, 0.1f, opacity);
float normalContribution = saturate(dot(N, H) * opacity + 1.0f - opacity);
float backScatter = gBuffer.AO * normalContribution / (PI * 2.0f);
lighting.Transmission = lerp(backScatter, 1, inscatter) * subsurfaceColor;
// Fake effect of the light going through the material
float3 subsurfaceColor = gBuffer.CustomData.rgb;
float opacity = gBuffer.CustomData.a;
float3 H = normalize(V + L);
float inscatter = pow(saturate(dot(L, -V)), 12.1f) * lerp(3, 0.1f, opacity);
float normalContribution = saturate(dot(N, H) * opacity + 1.0f - opacity);
float backScatter = gBuffer.AO * normalContribution / (PI * 2.0f);
lighting.Transmission = lerp(backScatter, 1, inscatter) * subsurfaceColor;
#endif
return lighting;
return lighting;
}
LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
{
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
#if defined(USE_GBUFFER_CUSTOM_DATA)
// Fake effect of the light going through the thin foliage
float3 subsurfaceColor = gBuffer.CustomData.rgb;
float wrapNoL = saturate((-dot(N, L) + 0.5f) / 2.25);
float VoL = dot(V, L);
float scatter = D_GGX(0.36, saturate(-VoL));
lighting.Transmission = subsurfaceColor * (wrapNoL * scatter);
// Fake effect of the light going through the thin foliage
float3 subsurfaceColor = gBuffer.CustomData.rgb;
float wrapNoL = saturate((-dot(N, L) + 0.5f) / 2.25);
float VoL = dot(V, L);
float scatter = D_GGX(0.36, saturate(-VoL));
lighting.Transmission = subsurfaceColor * (wrapNoL * scatter);
#endif
return lighting;
return lighting;
}
LightingData SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
{
switch (gBuffer.ShadingModel)
{
case SHADING_MODEL_UNLIT:
case SHADING_MODEL_LIT:
return StandardShading(gBuffer, energy, L, V, N);
case SHADING_MODEL_SUBSURFACE:
return SubsurfaceShading(gBuffer, energy, L, V, N);
case SHADING_MODEL_FOLIAGE:
return FoliageShading(gBuffer, energy, L, V, N);
default:
return (LightingData)0;
}
switch (gBuffer.ShadingModel)
{
case SHADING_MODEL_UNLIT:
case SHADING_MODEL_LIT:
return StandardShading(gBuffer, energy, L, V, N);
case SHADING_MODEL_SUBSURFACE:
return SubsurfaceShading(gBuffer, energy, L, V, N);
case SHADING_MODEL_FOLIAGE:
return FoliageShading(gBuffer, energy, L, V, N);
default:
return (LightingData)0;
}
}
float4 GetSkyLightLighting(LightData lightData, GBufferSample gBuffer, TextureCube ibl)
{
// Get material diffuse color
float3 diffuseColor = GetDiffuseColor(gBuffer);
// Get material diffuse color
float3 diffuseColor = GetDiffuseColor(gBuffer);
// Compute the preconvolved incoming lighting with the normal direction (apply ambient color)
// Some data is packed, see C++ RendererSkyLightData::SetupLightData
float mip = lightData.SourceLength;
float3 diffuseLookup = ibl.SampleLevel(SamplerLinearClamp, gBuffer.Normal, mip).rgb * lightData.Color.rgb;
diffuseLookup += float3(lightData.SpotAngles.rg, lightData.SourceRadius);
// Compute the preconvolved incoming lighting with the normal direction (apply ambient color)
// Some data is packed, see C++ RendererSkyLightData::SetupLightData
float mip = lightData.SourceLength;
float3 diffuseLookup = ibl.SampleLevel(SamplerLinearClamp, gBuffer.Normal, mip).rgb * lightData.Color.rgb;
diffuseLookup += float3(lightData.SpotAngles.rg, lightData.SourceRadius);
// Fade out based on distance to capture
float3 captureVector = gBuffer.WorldPos - lightData.Position;
float captureVectorLength = length(captureVector);
float normalizedDistanceToCapture = saturate(captureVectorLength / lightData.Radius);
float distanceAlpha = 1.0 - smoothstep(0.6, 1, normalizedDistanceToCapture);
// Fade out based on distance to capture
float3 captureVector = gBuffer.WorldPos - lightData.Position;
float captureVectorLength = length(captureVector);
float normalizedDistanceToCapture = saturate(captureVectorLength / lightData.Radius);
float distanceAlpha = 1.0 - smoothstep(0.6, 1, normalizedDistanceToCapture);
// Calculate final light
float3 color = diffuseLookup * diffuseColor;
float luminance = Luminance(diffuseLookup);
return float4(color, luminance) * (distanceAlpha * gBuffer.AO);
// Calculate final light
float3 color = diffuseLookup * diffuseColor;
float luminance = Luminance(diffuseLookup);
return float4(color, luminance) * (distanceAlpha * gBuffer.AO);
}
float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, float4 shadowMask, bool isRadial, bool isSpotLight)
{
float4 result = 0;
float3 V = normalize(viewPos - gBuffer.WorldPos);
float3 N = gBuffer.Normal;
float3 L = lightData.Direction; // no need to normalize
float NoL = saturate(dot(N, L));
float distanceAttenuation = 1;
float lightRadiusMask = 1;
float spotAttenuation = 1;
float3 toLight = lightData.Direction;
float4 result = 0;
float3 V = normalize(viewPos - gBuffer.WorldPos);
float3 N = gBuffer.Normal;
float3 L = lightData.Direction; // no need to normalize
float NoL = saturate(dot(N, L));
float distanceAttenuation = 1;
float lightRadiusMask = 1;
float spotAttenuation = 1;
float3 toLight = lightData.Direction;
// Calculate attenuation
if (isRadial)
{
toLight = lightData.Position - gBuffer.WorldPos;
float distanceSqr = dot(toLight, toLight);
L = toLight * rsqrt(distanceSqr);
GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation);
}
float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation;
// Calculate attenuation
if (isRadial)
{
toLight = lightData.Position - gBuffer.WorldPos;
float distanceSqr = dot(toLight, toLight);
L = toLight * rsqrt(distanceSqr);
GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation);
}
float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation;
// Calculate shadow
ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask);
// Calculate shadow
ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask);
// Reduce shadow mapping artifacts
shadow.SurfaceShadow *= saturate(NoL * 6.0f - 0.2f);
// Reduce shadow mapping artifacts
shadow.SurfaceShadow *= saturate(NoL * 6.0f - 0.2f);
BRANCH
if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0)
{
gBuffer.Roughness = max(gBuffer.Roughness, lightData.MinRoughness);
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, toLight, L, V, N);
BRANCH
if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0)
{
gBuffer.Roughness = max(gBuffer.Roughness, lightData.MinRoughness);
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, toLight, L, V, N);
// Calculate direct lighting
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
// Calculate direct lighting
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
// Calculate final light color
float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * (NoL * attenuation * shadow.SurfaceShadow);
float3 subsurfaceLight = lighting.Transmission * (attenuation * shadow.TransmissionShadow);
result.rgb = lightData.Color * (surfaceLight + subsurfaceLight);
result.a = 1;
}
// Calculate final light color
float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * (NoL * attenuation * shadow.SurfaceShadow);
float3 subsurfaceLight = lighting.Transmission * (attenuation * shadow.TransmissionShadow);
result.rgb = lightData.Color * (surfaceLight + subsurfaceLight);
result.a = 1;
}
return result;
return result;
}
#endif

View File

@@ -9,126 +9,126 @@
// Structure that contains information about light
struct LightData
{
float2 SpotAngles;
float SourceRadius;
float SourceLength;
float2 SpotAngles;
float SourceRadius;
float SourceLength;
float3 Color;
float MinRoughness;
float3 Color;
float MinRoughness;
float3 Position;
float CastShadows;
float3 Position;
float CastShadows;
float3 Direction;
float Radius;
float3 Direction;
float Radius;
float FalloffExponent;
float InverseSquared;
float Dummy0;
float RadiusInv;
float FalloffExponent;
float InverseSquared;
float Dummy0;
float RadiusInv;
};
// Structure that contains information about shadow
struct ShadowData
{
float SurfaceShadow;
float TransmissionShadow;
float SurfaceShadow;
float TransmissionShadow;
};
// Structure that contains information about direct lighting calculations result
struct LightingData
{
float3 Diffuse;
float3 Specular;
float3 Transmission;
float3 Diffuse;
float3 Specular;
float3 Transmission;
};
// Calculates radial light (point or spot) attenuation factors (distance, spot and radius mask)
void GetRadialLightAttenuation(
LightData lightData,
bool isSpotLight,
float3 N,
float distanceSqr,
float distanceBiasSqr,
float3 toLight,
float3 L,
inout float NoL,
inout float distanceAttenuation,
inout float lightRadiusMask,
inout float spotAttenuation)
LightData lightData,
bool isSpotLight,
float3 N,
float distanceSqr,
float distanceBiasSqr,
float3 toLight,
float3 L,
inout float NoL,
inout float distanceAttenuation,
inout float lightRadiusMask,
inout float spotAttenuation)
{
if (lightData.InverseSquared)
{
BRANCH
if (lightData.SourceLength > 0)
{
float3 l01 = lightData.Direction * lightData.SourceLength;
float3 l0 = toLight - 0.5 * l01;
float3 l1 = toLight + 0.5 * l01;
float lengthL0 = length(l0);
float lengthL1 = length(l1);
distanceAttenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr);
NoL = saturate(0.5 * (dot(N, l0) / lengthL0 + dot(N, l1) / lengthL1));
}
else
{
distanceAttenuation = rcp(distanceSqr + distanceBiasSqr);
NoL = saturate(dot(N, L));
}
lightRadiusMask = Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv))));
}
else
{
distanceAttenuation = 1;
NoL = saturate(dot(N, L));
float3 worldLightVector = toLight * lightData.RadiusInv;
float t = dot(worldLightVector, worldLightVector);
lightRadiusMask = pow(1.0f - saturate(t), lightData.FalloffExponent);
}
if (lightData.InverseSquared)
{
BRANCH
if (lightData.SourceLength > 0)
{
float3 l01 = lightData.Direction * lightData.SourceLength;
float3 l0 = toLight - 0.5 * l01;
float3 l1 = toLight + 0.5 * l01;
float lengthL0 = length(l0);
float lengthL1 = length(l1);
distanceAttenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr);
NoL = saturate(0.5 * (dot(N, l0) / lengthL0 + dot(N, l1) / lengthL1));
}
else
{
distanceAttenuation = rcp(distanceSqr + distanceBiasSqr);
NoL = saturate(dot(N, L));
}
lightRadiusMask = Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv))));
}
else
{
distanceAttenuation = 1;
NoL = saturate(dot(N, L));
float3 worldLightVector = toLight * lightData.RadiusInv;
float t = dot(worldLightVector, worldLightVector);
lightRadiusMask = pow(1.0f - saturate(t), lightData.FalloffExponent);
}
if (isSpotLight)
{
// SpotAngles.x is CosOuterCone, SpotAngles.y is InvCosConeDifference
spotAttenuation = Square(saturate((dot(normalize(-L), lightData.Direction) - lightData.SpotAngles.x) * lightData.SpotAngles.y));
}
if (isSpotLight)
{
// SpotAngles.x is CosOuterCone, SpotAngles.y is InvCosConeDifference
spotAttenuation = Square(saturate((dot(normalize(-L), lightData.Direction) - lightData.SpotAngles.x) * lightData.SpotAngles.y));
}
}
// Find representative incoming light direction and energy modification
float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLight, inout float3 L, float3 V, half3 N)
{
float energy = 1;
float energy = 1;
float m = roughness * roughness;
float3 r = reflect(-V, N);
float invDistToLight = rsqrt(dot(toLight, toLight));
float m = roughness * roughness;
float3 r = reflect(-V, N);
float invDistToLight = rsqrt(dot(toLight, toLight));
BRANCH
if (lightData.SourceLength > 0)
{
float lineAngle = saturate(lightData.SourceLength * invDistToLight);
energy *= m / saturate(m + 0.5 * lineAngle);
float3 l01 = lightData.Direction * lightData.SourceLength;
float3 l0 = toLight - 0.5 * l01;
float a = Square(lightData.SourceLength);
float b = dot(r, l01);
float t = saturate(dot(l0, b * r - l01) / (a - b * b));
toLight = l0 + t * l01;
}
BRANCH
if (lightData.SourceLength > 0)
{
float lineAngle = saturate(lightData.SourceLength * invDistToLight);
energy *= m / saturate(m + 0.5 * lineAngle);
float3 l01 = lightData.Direction * lightData.SourceLength;
float3 l0 = toLight - 0.5 * l01;
float a = Square(lightData.SourceLength);
float b = dot(r, l01);
float t = saturate(dot(l0, b * r - l01) / (a - b * b));
toLight = l0 + t * l01;
}
BRANCH
if (lightData.SourceRadius > 0)
{
float sphereAngle = saturate(lightData.SourceRadius * invDistToLight);
energy *= Square(m / saturate(m + 0.5 * sphereAngle));
float3 closestPointOnRay = dot(toLight, r) * r;
float3 centerToRay = closestPointOnRay - toLight;
float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.SourceRadius * rsqrt(dot(centerToRay, centerToRay)));
toLight = closestPointOnSphere;
}
BRANCH
if (lightData.SourceRadius > 0)
{
float sphereAngle = saturate(lightData.SourceRadius * invDistToLight);
energy *= Square(m / saturate(m + 0.5 * sphereAngle));
float3 closestPointOnRay = dot(toLight, r) * r;
float3 centerToRay = closestPointOnRay - toLight;
float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.SourceRadius * rsqrt(dot(centerToRay, centerToRay)));
toLight = closestPointOnSphere;
}
L = normalize(toLight);
L = normalize(toLight);
return energy;
return energy;
}
#endif

View File

@@ -20,69 +20,69 @@
// Validate inputs
#ifndef MATERIAL
#define MATERIAL 0
#define MATERIAL 0
#endif
#ifndef MATERIAL_DOMAIN
#define MATERIAL_DOMAIN MATERIAL_DOMAIN_SURFACE
#define MATERIAL_DOMAIN MATERIAL_DOMAIN_SURFACE
#endif
#ifndef MATERIAL_BLEND
#define MATERIAL_BLEND MATERIAL_BLEND_OPAQUE
#define MATERIAL_BLEND MATERIAL_BLEND_OPAQUE
#endif
#ifndef MATERIAL_SHADING_MODEL
#define MATERIAL_SHADING_MODEL SHADING_MODEL_LIT
#define MATERIAL_SHADING_MODEL SHADING_MODEL_LIT
#endif
#ifndef USE_INSTANCING
#define USE_INSTANCING 0
#define USE_INSTANCING 0
#endif
#ifndef USE_SKINNING
#define USE_SKINNING 0
#define USE_SKINNING 0
#endif
#ifndef USE_LIGHTMAP
#define USE_LIGHTMAP 0
#define USE_LIGHTMAP 0
#endif
#ifndef USE_POSITION_OFFSET
#define USE_POSITION_OFFSET 0
#define USE_POSITION_OFFSET 0
#endif
#ifndef USE_VERTEX_COLOR
#define USE_VERTEX_COLOR 0
#define USE_VERTEX_COLOR 0
#endif
#ifndef USE_DISPLACEMENT
#define USE_DISPLACEMENT 0
#define USE_DISPLACEMENT 0
#endif
#ifndef USE_TESSELLATION
#define USE_TESSELLATION 0
#define USE_TESSELLATION 0
#endif
#ifndef USE_DITHERED_LOD_TRANSITION
#define USE_DITHERED_LOD_TRANSITION 0
#define USE_DITHERED_LOD_TRANSITION 0
#endif
#ifndef MATERIAL_TESSELLATION
#define MATERIAL_TESSELLATION MATERIAL_TESSELLATION_NONE
#define MATERIAL_TESSELLATION MATERIAL_TESSELLATION_NONE
#endif
#ifndef MAX_TESSELLATION_FACTOR
#define MAX_TESSELLATION_FACTOR 15
#define MAX_TESSELLATION_FACTOR 15
#endif
#ifndef PER_BONE_MOTION_BLUR
#define PER_BONE_MOTION_BLUR 0
#define PER_BONE_MOTION_BLUR 0
#endif
// Material properties
struct Material
{
float3 Emissive;
float Roughness;
float3 Color;
float AO;
float3 WorldNormal;
float Metalness;
float3 TangentNormal;
float Specular;
float3 PositionOffset;
float Opacity;
float3 SubsurfaceColor;
float Refraction;
float Mask;
float TessellationMultiplier;
float3 WorldDisplacement;
float3 Emissive;
float Roughness;
float3 Color;
float AO;
float3 WorldNormal;
float Metalness;
float3 TangentNormal;
float Specular;
float3 PositionOffset;
float Opacity;
float3 SubsurfaceColor;
float Refraction;
float Mask;
float TessellationMultiplier;
float3 WorldDisplacement;
#if USE_CUSTOM_VERTEX_INTERPOLATORS
float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT];
#endif
@@ -90,11 +90,11 @@ struct Material
struct ModelInput
{
float3 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float4 Normal : NORMAL;
float4 Tangent : TANGENT;
float2 LightmapUV : TEXCOORD1;
float3 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float4 Normal : NORMAL;
float4 Tangent : TANGENT;
float2 LightmapUV : TEXCOORD1;
#if USE_VERTEX_COLOR
half4 Color : COLOR;
#endif
@@ -109,7 +109,7 @@ struct ModelInput
struct ModelInput_PosOnly
{
float3 Position : POSITION;
float3 Position : POSITION;
#if USE_INSTANCING
float4 InstanceOrigin : ATTRIBUTE0; // .w contains PerInstanceRandom
float4 InstanceTransform1 : ATTRIBUTE1; // .w contains LODDitherFactor
@@ -121,12 +121,12 @@ struct ModelInput_PosOnly
struct ModelInput_Skinned
{
float3 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float4 Normal : NORMAL;
float4 Tangent : TANGENT;
uint4 BlendIndices : BLENDINDICES;
float4 BlendWeights : BLENDWEIGHT;
float3 Position : POSITION;
float2 TexCoord : TEXCOORD0;
float4 Normal : NORMAL;
float4 Tangent : TANGENT;
uint4 BlendIndices : BLENDINDICES;
float4 BlendWeights : BLENDWEIGHT;
#if USE_INSTANCING
float4 InstanceOrigin : ATTRIBUTE0; // .w contains PerInstanceRandom
float4 InstanceTransform1 : ATTRIBUTE1; // .w contains LODDitherFactor
@@ -138,55 +138,55 @@ struct ModelInput_Skinned
struct Model_VS2PS
{
float4 Position : SV_Position;
float4 ScreenPos : TEXCOORD0;
float4 Position : SV_Position;
float4 ScreenPos : TEXCOORD0;
};
struct GBufferOutput
{
float4 Light : SV_Target0;
float4 RT0 : SV_Target1;
float4 RT1 : SV_Target2;
float4 RT2 : SV_Target3;
float4 RT3 : SV_Target4;
float4 Light : SV_Target0;
float4 RT0 : SV_Target1;
float4 RT1 : SV_Target2;
float4 RT2 : SV_Target3;
float4 RT3 : SV_Target4;
};
float3x3 CalcTangentBasis(float3 normal, float3 pos, float2 uv)
{
// References:
// http://www.thetenthplanet.de/archives/1180
// https://zhangdoa.com/posts/normal-and-normal-mapping
float3 dp1 = ddx(pos);
float3 dp2 = ddy(pos);
float2 duv1 = ddx(uv);
float2 duv2 = ddy(uv);
float3 dp2perp = cross(dp2, normal);
float3 dp1perp = cross(normal, dp1);
float3 tangent = normalize(dp2perp * duv1.x + dp1perp * duv2.x);
float3 bitangent = normalize(dp2perp * duv1.y + dp1perp * duv2.y);
return float3x3(tangent, bitangent, normal);
// References:
// http://www.thetenthplanet.de/archives/1180
// https://zhangdoa.com/posts/normal-and-normal-mapping
float3 dp1 = ddx(pos);
float3 dp2 = ddy(pos);
float2 duv1 = ddx(uv);
float2 duv2 = ddy(uv);
float3 dp2perp = cross(dp2, normal);
float3 dp1perp = cross(normal, dp1);
float3 tangent = normalize(dp2perp * duv1.x + dp1perp * duv2.x);
float3 bitangent = normalize(dp2perp * duv1.y + dp1perp * duv2.y);
return float3x3(tangent, bitangent, normal);
}
float3x3 CalcTangentBasisFromWorldNormal(float3 normal)
{
float3 tangent = cross(normal, float3(1, 0, 0));
float3 bitangent = cross(normal, tangent);
return float3x3(tangent, bitangent, normal);
float3 tangent = cross(normal, float3(1, 0, 0));
float3 bitangent = cross(normal, tangent);
return float3x3(tangent, bitangent, normal);
}
float3x3 CalcTangentBasis(float3 normal, float4 tangent)
{
float3 bitangent = cross(normal, tangent.xyz) * tangent.w;
return float3x3(tangent.xyz, bitangent, normal);
float3 bitangent = cross(normal, tangent.xyz) * tangent.w;
return float3x3(tangent.xyz, bitangent, normal);
}
// [Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion"]
float3 AOMultiBounce(float visibility, float3 albedo)
{
float3 a = 2.0404 * albedo - 0.3324;
float3 b = -4.7951 * albedo + 0.6417;
float3 c = 2.7552 * albedo + 0.6903;
return max(visibility, ((visibility * a + b) * visibility + c) * visibility);
float3 a = 2.0404 * albedo - 0.3324;
float3 b = -4.7951 * albedo + 0.6417;
float3 c = 2.7552 * albedo + 0.6903;
return max(visibility, ((visibility * a + b) * visibility + c) * visibility);
}
#endif

View File

@@ -5,254 +5,254 @@
uint NextPow2(uint value)
{
uint mask = (1 << firstbithigh(value)) - 1;
return (value + mask) & ~mask;
uint mask = (1 << firstbithigh(value)) - 1;
return (value + mask) & ~mask;
}
float3 SafeNormalize(float3 v)
{
return v / sqrt(max(dot(v, v), 0.01));
return v / sqrt(max(dot(v, v), 0.01));
}
float3 ExtractLargestComponent(float3 v)
{
float3 a = abs(v);
if (a.x > a.y)
{
if (a.x > a.z)
{
return float3(v.x > 0 ? 1 : -1, 0, 0);
}
}
else
{
if (a.y > a.z)
{
return float3(0, v.y > 0 ? 1 : -1, 0);
}
}
return float3(0, 0, v.z > 0 ? 1 : -1);
float3 a = abs(v);
if (a.x > a.y)
{
if (a.x > a.z)
{
return float3(v.x > 0 ? 1 : -1, 0, 0);
}
}
else
{
if (a.y > a.z)
{
return float3(0, v.y > 0 ? 1 : -1, 0);
}
}
return float3(0, 0, v.z > 0 ? 1 : -1);
}
float Square(float x)
{
return x * x;
return x * x;
}
float2 Square(float2 x)
{
return x * x;
return x * x;
}
float3 Square(float3 x)
{
return x * x;
return x * x;
}
float4 Square(float4 x)
{
return x * x;
return x * x;
}
float Min2(float2 x)
{
return min(x.x, x.y);
return min(x.x, x.y);
}
float Min3(float3 x)
{
return min(x.x, min(x.y, x.z));
return min(x.x, min(x.y, x.z));
}
float Min4(float4 x)
{
return min(x.x, min(x.y, min(x.z, x.w)));
return min(x.x, min(x.y, min(x.z, x.w)));
}
float Max2(float2 x)
{
return max(x.x, x.y);
return max(x.x, x.y);
}
float Max3(float3 x)
{
return max(x.x, max(x.y, x.z));
return max(x.x, max(x.y, x.z));
}
float Max4(float4 x)
{
return max(x.x, max(x.y, max(x.z, x.w)));
return max(x.x, max(x.y, max(x.z, x.w)));
}
float Pow2(float x)
{
return x * x;
return x * x;
}
float2 Pow2(float2 x)
{
return x * x;
return x * x;
}
float3 Pow2(float3 x)
{
return x * x;
return x * x;
}
float4 Pow2(float4 x)
{
return x * x;
return x * x;
}
float Pow3(float x)
{
return x * x * x;
return x * x * x;
}
float2 Pow3(float2 x)
{
return x * x * x;
return x * x * x;
}
float3 Pow3(float3 x)
{
return x * x * x;
return x * x * x;
}
float4 Pow3(float4 x)
{
return x * x * x;
return x * x * x;
}
float Pow4(float x)
{
float xx = x * x;
return xx * xx;
float xx = x * x;
return xx * xx;
}
float2 Pow4(float2 x)
{
float2 xx = x * x;
return xx * xx;
float2 xx = x * x;
return xx * xx;
}
float3 Pow4(float3 x)
{
float3 xx = x * x;
return xx * xx;
float3 xx = x * x;
return xx * xx;
}
float4 Pow4(float4 x)
{
float4 xx = x * x;
return xx * xx;
float4 xx = x * x;
return xx * xx;
}
float Pow5(float x)
{
float xx = x * x;
return xx * xx * x;
float xx = x * x;
return xx * xx * x;
}
float2 Pow5(float2 x)
{
float2 xx = x * x;
return xx * xx * x;
float2 xx = x * x;
return xx * xx * x;
}
float3 Pow5(float3 x)
{
float3 xx = x * x;
return xx * xx * x;
float3 xx = x * x;
return xx * xx * x;
}
float4 Pow5(float4 x)
{
float4 xx = x * x;
return xx * xx * x;
float4 xx = x * x;
return xx * xx * x;
}
float Pow6(float x)
{
float xx = x * x;
return xx * xx * xx;
float xx = x * x;
return xx * xx * xx;
}
float2 Pow6(float2 x)
{
float2 xx = x * x;
return xx * xx * xx;
float2 xx = x * x;
return xx * xx * xx;
}
float3 Pow6(float3 x)
{
float3 xx = x * x;
return xx * xx * xx;
float3 xx = x * x;
return xx * xx * xx;
}
float4 Pow6(float4 x)
{
float4 xx = x * x;
return xx * xx * xx;
float4 xx = x * x;
return xx * xx * xx;
}
float ClampedPow(float x, float y)
{
return pow(max(abs(x), 0.000001f), y);
return pow(max(abs(x), 0.000001f), y);
}
float2 ClampedPow(float2 x, float2 y)
{
return pow(max(abs(x), float2(0.000001f, 0.000001f)), y);
return pow(max(abs(x), float2(0.000001f, 0.000001f)), y);
}
float3 ClampedPow(float3 x, float3 y)
{
return pow(max(abs(x), float3(0.000001f, 0.000001f, 0.000001f)), y);
return pow(max(abs(x), float3(0.000001f, 0.000001f, 0.000001f)), y);
}
float4 ClampedPow(float4 x, float4 y)
{
return pow(max(abs(x), float4(0.000001f, 0.000001f, 0.000001f, 0.000001f)), y);
return pow(max(abs(x), float4(0.000001f, 0.000001f, 0.000001f, 0.000001f)), y);
}
float4 FindQuatBetween(float3 from, float3 to)
{
float normAB = 1.0f;
float w = normAB + dot(from, to);
float4 result;
float normAB = 1.0f;
float w = normAB + dot(from, to);
float4 result;
if (w >= 1e-6f * normAB)
{
result = float4
(
from.y * to.z - from.z * to.y,
from.z * to.x - from.x * to.z,
from.x * to.y - from.y * to.x,
w
);
}
else
{
w = 0.f;
result = abs(from.x) > abs(from.y)
? float4(-from.z, 0.f, from.x, w)
: float4(0.f, -from.z, from.y, w);
}
if (w >= 1e-6f * normAB)
{
result = float4
(
from.y * to.z - from.z * to.y,
from.z * to.x - from.x * to.z,
from.x * to.y - from.y * to.x,
w
);
}
else
{
w = 0.f;
result = abs(from.x) > abs(from.y)
? float4(-from.z, 0.f, from.x, w)
: float4(0.f, -from.z, from.y, w);
}
return normalize(result);
return normalize(result);
}
// Rotates Position about the given axis by the given angle, in radians, and returns the offset to position
float3 RotateAboutAxis(float4 normalizedRotationAxisAndAngle, float3 positionOnAxis, float3 position)
{
float3 pointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis);
float3 axisU = position - pointOnAxis;
float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU);
float cosAngle, sinAngle;
sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle);
float3 rotation = axisU * cosAngle + axisV * sinAngle;
return pointOnAxis + rotation - position;
float3 pointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis);
float3 axisU = position - pointOnAxis;
float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU);
float cosAngle, sinAngle;
sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle);
float3 rotation = axisU * cosAngle + axisV * sinAngle;
return pointOnAxis + rotation - position;
}
#endif

View File

@@ -49,26 +49,26 @@ float3x3 EulerMatrix(float3 angles)
{
float3 s, c;
sincos(angles, s, c);
return float3x3(c.y * c.z + s.x * s.y * s.z, c.z * s.x * s.y - c.y * s.z, c.x * s.y,
c.x * s.z, c.x * c.z, -s.x,
-c.z * s.y + c.y * s.x * s.z, c.y * c.z * s.x + s.y * s.z, c.x * c.y);
return float3x3(c.y * c.z + s.x * s.y * s.z, c.z * s.x * s.y - c.y * s.z, c.x * s.y, c.x * s.z, c.x * c.z, -s.x, -c.z * s.y + c.y * s.x * s.z, c.y * c.z * s.x + s.y * s.z, c.x * c.y);
}
float4x4 QuaternionToMatrix(float4 q)
{
float x2 = q.x + q.x; float y2 = q.y + q.y; float z2 = q.z + q.z;
float xx = q.x * x2; float xy = q.x * y2; float xz = q.x * z2;
float yy = q.y * y2; float yz = q.y * z2; float zz = q.z * z2;
float wx = q.w * x2; float wy = q.w * y2; float wz = q.w * z2;
float4x4 result =
{
1.0f - (yy + zz), xy - wz, xz + wy, 0.0f,
xy + wz, 1.0f - (xx + zz), yz - wx, 0.0f,
xz - wy, yz + wx, 1.0f - (xx + yy), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
return result;
// @formatter:off
float x2 = q.x + q.x; float y2 = q.y + q.y; float z2 = q.z + q.z;
float xx = q.x * x2; float xy = q.x * y2; float xz = q.x * z2;
float yy = q.y * y2; float yz = q.y * z2; float zz = q.z * z2;
float wx = q.w * x2; float wy = q.w * y2; float wz = q.w * z2;
float4x4 result =
{
1.0f - (yy + zz), xy - wz, xz + wy, 0.0f,
xy + wz, 1.0f - (xx + zz), yz - wx, 0.0f,
xz - wy, yz + wx, 1.0f - (xx + yy), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
return result;
// @formatter:on
}
#endif

View File

@@ -5,16 +5,16 @@
float3 TangentToWorld(float3 vec, float3 tangentZ)
{
float3 upVector = abs(tangentZ.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 tangentX = normalize(cross(upVector, tangentZ));
float3 tangentY = cross(tangentZ, tangentX);
return tangentX * vec.x + tangentY * vec.y + tangentZ * vec.z;
float3 upVector = abs(tangentZ.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
float3 tangentX = normalize(cross(upVector, tangentZ));
float3 tangentY = cross(tangentZ, tangentX);
return tangentX * vec.x + tangentY * vec.y + tangentZ * vec.z;
}
uint ReverseBits32(uint bits)
{
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
return reversebits(bits);
return reversebits(bits);
#else
bits = ( bits << 16) | ( bits >> 16);
bits = ( (bits & 0x00ff00ff) << 8 ) | ( (bits & 0xff00ff00) >> 8 );
@@ -27,117 +27,117 @@ uint ReverseBits32(uint bits)
float2 Hammersley(uint index, uint numSamples, uint2 random)
{
float e1 = frac((float)index / numSamples + float(random.x & 0xffff) / (1 << 16));
float e2 = float(ReverseBits32(index) ^ random.y) * 2.3283064365386963e-10;
return float2(e1, e2);
float e1 = frac((float)index / numSamples + float(random.x & 0xffff) / (1 << 16));
float e2 = float(ReverseBits32(index) ^ random.y) * 2.3283064365386963e-10;
return float2(e1, e2);
}
float4 UniformSampleSphere(float2 e)
{
float phi = 2 * PI * e.x;
float cosTheta = 1 - 2 * e.y;
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float phi = 2 * PI * e.x;
float cosTheta = 1 - 2 * e.y;
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float pdf = 1.0 / (4 * PI);
return float4(h, pdf);
float pdf = 1.0 / (4 * PI);
return float4(h, pdf);
}
float4 UniformSampleHemisphere(float2 e)
{
float phi = 2 * PI * e.x;
float cosTheta = e.y;
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float phi = 2 * PI * e.x;
float cosTheta = e.y;
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float pdf = 1.0 / (2 * PI);
return float4(h, pdf);
float pdf = 1.0 / (2 * PI);
return float4(h, pdf);
}
float4 CosineSampleHemisphere(float2 e)
{
float phi = 2 * PI * e.x;
float cosTheta = sqrt(e.y);
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float phi = 2 * PI * e.x;
float cosTheta = sqrt(e.y);
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float pdf = cosTheta / PI;
return float4(h, pdf);
float pdf = cosTheta / PI;
return float4(h, pdf);
}
float4 UniformSampleCone(float2 e, float cosThetaMax)
{
float phi = 2 * PI * e.x;
float cosTheta = lerp(cosThetaMax, 1, e.y);
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float phi = 2 * PI * e.x;
float cosTheta = lerp(cosThetaMax, 1, e.y);
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float3 l;
l.x = sinTheta * cos(phi);
l.y = sinTheta * sin(phi);
l.z = cosTheta;
float3 l;
l.x = sinTheta * cos(phi);
l.y = sinTheta * sin(phi);
l.z = cosTheta;
float pdf = 1.0 / (2 * PI * (1 - cosThetaMax));
return float4(l, pdf);
float pdf = 1.0 / (2 * PI * (1 - cosThetaMax));
return float4(l, pdf);
}
float4 ImportanceSampleBlinn(float2 e, float roughness)
{
float m = roughness * roughness;
float n = 2 / (m * m) - 2;
float m = roughness * roughness;
float n = 2 / (m * m) - 2;
float phi = 2 * PI * e.x;
float cosTheta = ClampedPow(e.y, 1 / (n + 1));
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float phi = 2 * PI * e.x;
float cosTheta = ClampedPow(e.y, 1 / (n + 1));
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float d = (n + 2)/ (2 * PI) * ClampedPow(cosTheta, n);
float pdf = d * cosTheta;
return float4(h, pdf);
float d = (n + 2) / (2 * PI) * ClampedPow(cosTheta, n);
float pdf = d * cosTheta;
return float4(h, pdf);
}
float4 ImportanceSampleGGX(float2 e, float roughness)
{
float m = roughness * roughness;
float m2 = m * m;
float m = roughness * roughness;
float m2 = m * m;
float phi = 2 * PI * e.x;
float cosTheta = sqrt((1 - e.y) / (1 + (m2 - 1) * e.y));
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float phi = 2 * PI * e.x;
float cosTheta = sqrt((1 - e.y) / (1 + (m2 - 1) * e.y));
float sinTheta = sqrt(1 - cosTheta * cosTheta);
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float r = (cosTheta * m2 - cosTheta) * cosTheta + 1;
float d = m2 / (PI * r * r);
float pdf = d * cosTheta;
return float4(h, pdf);
float3 h;
h.x = sinTheta * cos(phi);
h.y = sinTheta * sin(phi);
h.z = cosTheta;
float r = (cosTheta * m2 - cosTheta) * cosTheta + 1;
float d = m2 / (PI * r * r);
float pdf = d * cosTheta;
return float4(h, pdf);
}
// Multiple importance sampling power heuristic of two functions with a power of two.
// [Veach 1997, "Robust Monte Carlo Methods for Light Transport Simulation"]
float MISWeight(uint number, float PDF, uint otherNumber, float otherPDF)
{
float weight = number * PDF;
float otherWeight = otherNumber * otherPDF;
return weight * weight / (weight * weight + otherWeight * otherWeight);
float weight = number * PDF;
float otherWeight = otherNumber * otherPDF;
return weight * weight / (weight * weight + otherWeight * otherWeight);
}
#endif

View File

@@ -5,14 +5,14 @@
float4 QuaternionMultiply(float4 q1, float4 q2)
{
return float4(q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), q1.w * q2.w - dot(q1.xyz, q2.xyz));
return float4(q2.xyz * q1.w + q1.xyz * q2.w + cross(q1.xyz, q2.xyz), q1.w * q2.w - dot(q1.xyz, q2.xyz));
}
float3 QuaternionRotate(float4 q, float3 v)
{
float3 b = q.xyz;
float b2 = dot(b, b);
return (v * (q.w * q.w - b2) + b * (dot(v, b) * 2.f) + cross(b, v) * (q.w * 2.f));
float3 b = q.xyz;
float b2 = dot(b, b);
return (v * (q.w * q.w - b2) + b * (dot(v, b) * 2.f) + cross(b, v) * (q.w * 2.f));
}
#endif

View File

@@ -5,8 +5,8 @@
float PseudoRandom(float2 xy)
{
float2 p = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f);
return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
float2 p = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f);
return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
}
// Generic noise (1-component)
@@ -23,33 +23,33 @@ float2 RandN2(float2 n)
void FindBestAxisVectors(float3 input, out float3 axis1, out float3 axis2)
{
const float3 a = abs(input);
if (a.z > a.x && a.z > a.y)
axis1 = float3(1, 0, 0);
else
axis1 = float3(0, 0, 1);
axis1 = normalize(axis1 - input * dot(axis1, input));
axis2 = cross(axis1, input);
const float3 a = abs(input);
if (a.z > a.x && a.z > a.y)
axis1 = float3(1, 0, 0);
else
axis1 = float3(0, 0, 1);
axis1 = normalize(axis1 - input * dot(axis1, input));
axis2 = cross(axis1, input);
}
float PerlinRamp(in float t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
return t * t * t * (t * (t * 6 - 15) + 10);
}
float2 PerlinRamp(in float2 t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
return t * t * t * (t * (t * 6 - 15) + 10);
}
float3 PerlinRamp(in float3 t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
return t * t * t * (t * (t * 6 - 15) + 10);
}
float4 PerlinRamp(in float4 t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
return t * t * t * (t * (t * 6 - 15) + 10);
}
#endif

View File

@@ -7,48 +7,48 @@
float GetSpecularOcclusion(float NoV, float roughnessSq, float ao)
{
return saturate(pow(NoV + ao, roughnessSq) - 1 + ao);
return saturate(pow(NoV + ao, roughnessSq) - 1 + ao);
}
float4 SampleReflectionProbe(float3 viewPos, TextureCube probe, ProbeData data, float3 positionWS, float3 normal, float roughness)
{
// Calculate distance from probe to the pixel
float3 captureVector = positionWS - data.ProbePos;
float captureVectorLength = length(captureVector);
// Check if cannot light pixel
// TODO: maybe remove this check?? - test it out with dozens of probes
BRANCH
if (captureVectorLength >= data.ProbeRadius)
{
// End
return 0;
}
// Calculate distance from probe to the pixel
float3 captureVector = positionWS - data.ProbePos;
float captureVectorLength = length(captureVector);
// Fade out based on distance to capture
float normalizedDistanceToCapture = saturate(captureVectorLength * data.ProbeInvRadius);
float distanceAlpha = 1.0 - smoothstep(0.7, 1, normalizedDistanceToCapture);
float fade = distanceAlpha * data.ProbeBrightness;
// Check if cannot light pixel
// TODO: maybe remove this check?? - test it out with dozens of probes
BRANCH
if (captureVectorLength >= data.ProbeRadius)
{
// End
return 0;
}
// Calculate reflection vector
float3 V = normalize(positionWS - viewPos);
float3 R = reflect(V, normal);
float3 D = data.ProbeInvRadius * captureVector + R;
// Fade out based on distance to capture
float normalizedDistanceToCapture = saturate(captureVectorLength * data.ProbeInvRadius);
float distanceAlpha = 1.0 - smoothstep(0.7, 1, normalizedDistanceToCapture);
float fade = distanceAlpha * data.ProbeBrightness;
// Sample probe at valid mip level based on surface roughness value
half mip = ProbeMipFromRoughness(roughness);
float4 probeSample = probe.SampleLevel(SamplerLinearClamp, D, mip);
// Calculate reflection vector
float3 V = normalize(positionWS - viewPos);
float3 R = reflect(V, normal);
float3 D = data.ProbeInvRadius * captureVector + R;
return probeSample * fade;
// Sample probe at valid mip level based on surface roughness value
half mip = ProbeMipFromRoughness(roughness);
float4 probeSample = probe.SampleLevel(SamplerLinearClamp, D, mip);
return probeSample * fade;
}
// Calculates the reflective environment lighting to multiply the raw reflection color for the specular light (eg. from Env Probe or SSR).
float3 GetReflectionSpecularLighting(float3 viewPos, GBufferSample gBuffer)
{
float3 specularColor = GetSpecularColor(gBuffer);
float3 V = normalize(viewPos - gBuffer.WorldPos);
float NoV = saturate(dot(gBuffer.Normal, V));
return EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV);
float3 specularColor = GetSpecularColor(gBuffer);
float3 V = normalize(viewPos - gBuffer.WorldPos);
float NoV = saturate(dot(gBuffer.Normal, V));
return EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV);
}
#endif

View File

@@ -6,34 +6,34 @@
// The SH coefficients for the projection of a function that maps directions to scalar values
struct ThreeBandSHVector
{
half4 V0;
half4 V1;
half V2;
half4 V0;
half4 V1;
half V2;
};
ThreeBandSHVector SHBasisFunction3(half3 v)
{
ThreeBandSHVector result;
ThreeBandSHVector result;
result.V0.x = 0.282095f;
result.V0.y = -0.488603f * v.y;
result.V0.z = 0.488603f * v.z;
result.V0.w = -0.488603f * v.x;
result.V0.x = 0.282095f;
result.V0.y = -0.488603f * v.y;
result.V0.z = 0.488603f * v.z;
result.V0.w = -0.488603f * v.x;
result.V1.x = 1.092548f * v.x * v.y;
result.V1.y = -1.092548f * v.y * v.z;
result.V1.z = 0.315392f * (3.0f * v.z - 1.0f);
result.V1.w = -1.092548f * v.x * v.z;
result.V2 = 0.546274f * (v.x - v.y);
result.V1.x = 1.092548f * v.x * v.y;
result.V1.y = -1.092548f * v.y * v.z;
result.V1.z = 0.315392f * (3.0f * v.z - 1.0f);
result.V1.w = -1.092548f * v.x * v.z;
result.V2 = 0.546274f * (v.x - v.y);
return result;
return result;
}
// Projects a direction onto SH and convolves with a cosine kernel to compute irradiance
void ProjectOntoSH3(in float3 n, in float3 color, out float3 sh[9])
{
// Cosine kernel
const float A0 = 3.141593f;
// Cosine kernel
const float A0 = 3.141593f;
const float A1 = 2.095395f;
const float A2 = 0.785398f;
@@ -57,27 +57,28 @@ void ProjectOntoSH3(in float3 n, in float3 color, out float3 sh[9])
// [Ralf Habel and Michael Wimmer, "Efficient Irradiance Normal Mapping"]
void ConvertSH3ToHBasis(in float3 sh[9], out float3 hBasis[4])
{
const float rt2 = sqrt(2.0f);
const float rt32 = sqrt(3.0f / 2.0f);
const float rt52 = sqrt(5.0f / 2.0f);
const float rt152 = sqrt(15.0f / 2.0f);
const float convMatrix[4*9] =
{
1.0f / rt2, 0, 0.5f * rt32, 0, 0, 0, 0, 0, 0,
0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0, 0, 0,
0, 0, 1.0f / (2.0f * rt2), 0, 0, 0, 0.25f * rt152, 0, 0,
0, 0, 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0
};
const float rt2 = sqrt(2.0f);
const float rt32 = sqrt(3.0f / 2.0f);
const float rt52 = sqrt(5.0f / 2.0f);
const float rt152 = sqrt(15.0f / 2.0f);
const float convMatrix[4 * 9] =
{
// @formatter:off
1.0f / rt2, 0, 0.5f * rt32, 0, 0, 0, 0, 0, 0,
0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0, 0, 0,
0, 0, 1.0f / (2.0f * rt2), 0, 0, 0, 0.25f * rt152, 0, 0,
0, 0, 0, 1.0f / rt2, 0, 0, 0, (3.0f / 8.0f) * rt52, 0
// @formatter:on
};
UNROLL
for (uint row = 0; row < 4; row++)
{
hBasis[row] = 0.0f;
UNROLL
for (uint col = 0; col < 9; col++)
hBasis[row] += convMatrix[row * 9 + col] * sh[col];
}
UNROLL
for (uint row = 0; row < 4; row++)
{
hBasis[row] = 0.0f;
UNROLL
for (uint col = 0; col < 9; col++)
hBasis[row] += convMatrix[row * 9 + col] * sh[col];
}
}
#endif

View File

@@ -9,137 +9,134 @@
// 1:-1 to 0:1
float2 ClipToUv(float2 clipPos)
{
return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5);
return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5);
}
// go into clip space (-1:1 from bottom/left to up/right)
float3 ProjectWorldToClip(float3 wsPos, float4x4 viewProjectionMatrix)
{
float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix);
return clipPos.xyz / clipPos.w;
float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix);
return clipPos.xyz / clipPos.w;
}
// go into UV space. (0:1 from top/left to bottom/right)
float3 ProjectWorldToUv(float3 wsPos, float4x4 viewProjectionMatrix)
{
float3 clipPos = ProjectWorldToClip(wsPos, viewProjectionMatrix);
return float3(ClipToUv(clipPos.xy), clipPos.z);
float3 clipPos = ProjectWorldToClip(wsPos, viewProjectionMatrix);
return float3(ClipToUv(clipPos.xy), clipPos.z);
}
float3 TangentToWorld(float3 N, float4 H)
{
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
float3 T = normalize(cross(upVector, N));
float3 B = cross(N, T);
return float3((T * H.x) + (B * H.y) + (N * H.z));
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
float3 T = normalize(cross(upVector, N));
float3 B = cross(N, T);
return float3((T * H.x) + (B * H.y) + (N * H.z));
}
float RayAttenBorder(float2 pos, float value)
{
float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y));
return saturate(borderDist > value ? 1.0 : borderDist / value);
float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y));
return saturate(borderDist > value ? 1.0 : borderDist / value);
}
// Screen Space Reflection ray tracing utility.
// Returns: xy: hitUV, z: hitMask, where hitUV is the result UV of hit pixel, hitMask is the normalized sample weight (0 if no hit).
float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D depthBuffer, float3 viewPos, float4x4 viewMatrix, float4x4 viewProjectionMatrix, float stepSize, float maxSamples = 20, bool temporal = false, float temporalTime = 0.0f, float worldAntiSelfOcclusionBias = 0.1f, float brdfBias = 0.82f, float drawDistance = 5000.0f, float roughnessThreshold = 0.4f, float edgeFade = 0.1f)
{
// Reject invalid pixels
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance)
return 0;
// Reject invalid pixels
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance)
return 0;
// Calculate view space normal vector
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
// Calculate view space normal vector
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
// Randomize it a little
float2 jitter = RandN2(uv + temporalTime);
float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, brdfBias);
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
// Randomize it a little
float2 jitter = RandN2(uv + temporalTime);
float2 Xi = jitter;
Xi.y = lerp(Xi.y, 0.0, brdfBias);
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
// Calculate normalized view space reflection vector
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
return 0;
// Calculate normalized view space reflection vector
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
return 0;
float3 viewWS = normalize(gBuffer.WorldPos - viewPos);
float3 reflectWS = reflect(viewWS, H.xyz);
float3 viewWS = normalize(gBuffer.WorldPos - viewPos);
float3 reflectWS = reflect(viewWS, H.xyz);
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias;
float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix);
float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix);
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias;
float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix);
float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix);
float3 rayUV = endUV - startUV;
float3 rayUV = endUV - startUV;
float2 rayUVAbs = abs(rayUV.xy);
rayUV *= stepSize / max(rayUVAbs.x, rayUVAbs.y);
float3 startUv = startUV + rayUV * 2;
rayUV *= stepSize / max(rayUVAbs.x, rayUVAbs.y);
float3 startUv = startUV + rayUV * 2;
float3 currOffset = startUv;
float3 rayStep = rayUV * 2;
float3 currOffset = startUv;
float3 rayStep = rayUV * 2;
// Calculate number of samples
float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz;
samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f;
float numSamples = min(maxSamples, samplesToEdge.x);
rayStep *= samplesToEdge.x / numSamples;
// Calculate number of samples
float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz;
samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f;
float numSamples = min(maxSamples, samplesToEdge.x);
rayStep *= samplesToEdge.x / numSamples;
// Calculate depth difference error
float depthDiffError = 1.3f * abs(rayStep.z);
// Calculate depth difference error
float depthDiffError = 1.3f * abs(rayStep.z);
// Ray trace
float currSampleIndex = 0;
float currSample, depthDiff;
LOOP
while (currSampleIndex < numSamples)
{
// Sample depth buffer and calculate depth difference
currSample = SAMPLE_RT(depthBuffer, currOffset.xy).r;
depthDiff = currOffset.z - currSample;
// Ray trace
float currSampleIndex = 0;
float currSample, depthDiff;
LOOP
while (currSampleIndex < numSamples)
{
// Sample depth buffer and calculate depth difference
currSample = SAMPLE_RT(depthBuffer, currOffset.xy).r;
depthDiff = currOffset.z - currSample;
// Check intersection
if (depthDiff >= 0)
{
if (depthDiff < depthDiffError)
{
break;
}
else
{
currOffset -= rayStep;
rayStep *= 0.5;
}
}
// Check intersection
if (depthDiff >= 0)
{
if (depthDiff < depthDiffError)
{
break;
}
currOffset -= rayStep;
rayStep *= 0.5;
}
// Move forward
currOffset += rayStep;
currSampleIndex++;
}
// Move forward
currOffset += rayStep;
currSampleIndex++;
}
// Check if has valid result after ray tracing
if (currSampleIndex >= numSamples)
{
// All samples done but no result
return 0;
}
// Check if has valid result after ray tracing
if (currSampleIndex >= numSamples)
{
// All samples done but no result
return 0;
}
float2 hitUV = currOffset.xy;
float2 hitUV = currOffset.xy;
// Fade rays close to screen edge
const float fadeStart = 0.9f;
const float fadeEnd = 1.0f;
const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart);
float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f;
float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp);
fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp);
fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder);
fadeOnBorder *= RayAttenBorder(hitUV, edgeFade);
// Fade rays close to screen edge
const float fadeStart = 0.9f;
const float fadeEnd = 1.0f;
const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart);
float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f;
float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp);
fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp);
fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder);
fadeOnBorder *= RayAttenBorder(hitUV, edgeFade);
// Fade rays on high roughness
float roughnessFade = saturate((roughnessThreshold - gBuffer.Roughness) * 20);
// Fade rays on high roughness
float roughnessFade = saturate((roughnessThreshold - gBuffer.Roughness) * 20);
// Fade on distance
float distanceFade = saturate((drawDistance - gBuffer.ViewPos.z) / drawDistance);
// Fade on distance
float distanceFade = saturate((drawDistance - gBuffer.ViewPos.z) / drawDistance);
// Output: xy: hitUV, z: hitMask
return float3(hitUV, fadeOnBorder * roughnessFade * distanceFade);
// Output: xy: hitUV, z: hitMask
return float3(hitUV, fadeOnBorder * roughnessFade * distanceFade);
}

View File

@@ -10,26 +10,26 @@
// Set default macros if not provided
#ifndef SHADOWS_QUALITY
#define SHADOWS_QUALITY 0
#define SHADOWS_QUALITY 0
#endif
#ifndef CSM_BLENDING
#define CSM_BLENDING 0
#define CSM_BLENDING 0
#endif
// Structure that contains information about light
struct LightShadowData
{
float2 ShadowMapSize;
float Sharpness;
float Fade;
float2 ShadowMapSize;
float Sharpness;
float Fade;
float NormalOffsetScale;
float Bias;
float FadeDistance;
uint NumCascades;
float NormalOffsetScale;
float Bias;
float FadeDistance;
uint NumCascades;
float4 CascadeSplits;
float4x4 ShadowVP[6];
float4 CascadeSplits;
float4x4 ShadowVP[6];
};
#ifdef PLATFORM_ANDROID
@@ -41,15 +41,15 @@ struct LightShadowData
float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
{
float normalOffsetScale = saturate(1.0f - NoL);
return normal * (offsetScale * normalOffsetScale);
float normalOffsetScale = saturate(1.0f - NoL);
return normal * (offsetScale * normalOffsetScale);
}
float CalculateSubsurfaceOcclusion(float opacity, float sceneDepth, float shadowMapDepth)
{
float thickness = max(sceneDepth - shadowMapDepth, 0);
float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity);
return shadowMapDepth > 0.99f ? 1 : occlusion;
float thickness = max(sceneDepth - shadowMapDepth, 0);
float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity);
return shadowMapDepth > 0.99f ? 1 : occlusion;
}
#endif

View File

@@ -14,10 +14,10 @@
// Spot: 2, 5, 12, 29
#if SHADOWS_QUALITY == 0
#define FilterSizeCSM 2
#define FilterSizeCube 2
#define FilterSizeSpot 2
#define FilterSizeCSM 2
#define FilterSizeCube 2
#define FilterSizeSpot 2
#elif SHADOWS_QUALITY == 1
#define FilterSizeCSM 3
@@ -46,16 +46,16 @@
// Where: direction = normalize(worldPosition - lightPosition)
int GetCubeFaceIndex(float3 direction)
{
int cubeFaceIndex;
float3 absDirection = abs(direction);
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
if (maxDirection == absDirection.x)
cubeFaceIndex = absDirection.x == direction.x ? 0 : 1;
else if (maxDirection == absDirection.y)
cubeFaceIndex = absDirection.y == direction.y ? 2 : 3;
else
cubeFaceIndex = absDirection.z == direction.z ? 4 : 5;
return cubeFaceIndex;
int cubeFaceIndex;
float3 absDirection = abs(direction);
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
if (maxDirection == absDirection.x)
cubeFaceIndex = absDirection.x == direction.x ? 0 : 1;
else if (maxDirection == absDirection.y)
cubeFaceIndex = absDirection.y == direction.y ? 2 : 3;
else
cubeFaceIndex = absDirection.z == direction.z ? 4 : 5;
return cubeFaceIndex;
}
// Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmpRed.
@@ -64,15 +64,15 @@ float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize
{
#if FilterSizeCSM == 2
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
#else
#else
return sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, float3(shadowPos.xy, cascadeIndex), 0).r;
#endif
#endif
#else
@@ -120,11 +120,11 @@ float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize
if (value != 0.0f)
{
// Gather returns xyzw which is counter clockwise order starting with the sample to the lower left of the queried location
#if CAN_USE_GATHER
#if CAN_USE_GATHER
v1[(col + FS_2) / 2] = shadowMap.GatherCmp(ShadowSampler, baseUV, sceneDepth, int2(col, row));
#else
#else
float4 gather;
@@ -135,7 +135,7 @@ float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize
v1[(col + FS_2) / 2] = gather;
#endif
#endif
}
else
v1[(col + FS_2) / 2] = 0.0f;
@@ -242,7 +242,7 @@ float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize
#if FilterSizeCSM == 2
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
#elif FilterSizeCSM == 3
@@ -265,7 +265,7 @@ float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize
return sum * 1.0f / 16;
#elif FilterSizeCSM == 5
#elif FilterSizeCSM == 5
float uw0 = (4 - 3 * s);
float uw1 = 7;
@@ -347,41 +347,41 @@ float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize
// Samples the shadow from the shadow map cascade
float SampleShadowCascade(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPosition, uint cascadeIndex)
{
float shadow = SampleShadowMapFixedSizePCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
//float shadow = SampleShadowMapOptimizedPCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
return shadow;
float shadow = SampleShadowMapFixedSizePCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
//float shadow = SampleShadowMapOptimizedPCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
return shadow;
}
// Samples the shadow for the given directional light (cascaded shadow map sampling)
float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, float3 worldPosition, float viewDepth)
{
// Create a blend factor which is one before and at the fade plane
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
BRANCH
if (fade >= 1.0)
{
return 1;
}
// Create a blend factor which is one before and at the fade plane
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
BRANCH
if (fade >= 1.0)
{
return 1;
}
// Figure out which cascade to sample from
uint cascadeIndex = 0;
for (uint i = 0; i < shadow.NumCascades - 1; i++)
{
if (viewDepth > shadow.CascadeSplits[i])
cascadeIndex = i + 1;
}
// Figure out which cascade to sample from
uint cascadeIndex = 0;
for (uint i = 0; i < shadow.NumCascades - 1; i++)
{
if (viewDepth > shadow.CascadeSplits[i])
cascadeIndex = i + 1;
}
// Project into shadow space
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex]);
shadowPosition.xy /= shadowPosition.w;
shadowPosition.z -= shadow.Bias;
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex]);
shadowPosition.xy /= shadowPosition.w;
shadowPosition.z -= shadow.Bias;
// Sample shadow
float result = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex);
// Sample shadow
float result = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex);
// Increase the sharpness for higher cascades to match the filter radius
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
float sharpness = shadow.Sharpness * SharpnessScale[cascadeIndex];
// Increase the sharpness for higher cascades to match the filter radius
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
float sharpness = shadow.Sharpness * SharpnessScale[cascadeIndex];
#if CSM_BLENDING
// Sample the next cascade, and blend between the two results to smooth the transition
@@ -408,33 +408,33 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shado
}
#endif
// Apply shadow fade and sharpness
result = saturate((result - 0.5) * sharpness + 0.5);
result = lerp(1.0f, result, (1 - fade) * shadow.Fade);
return result;
// Apply shadow fade and sharpness
result = saturate((result - 0.5) * sharpness + 0.5);
result = lerp(1.0f, result, (1 - fade) * shadow.Fade);
return result;
}
// Samples the shadow for the given directional light (cascaded shadow map sampling) for the material surface (supports subsurface shadowing)
float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
{
subsurfaceShadow = 1;
subsurfaceShadow = 1;
// Create a blend factor which is one before and at the fade plane
float viewDepth = gBuffer.ViewPos.z;
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
BRANCH
if (fade >= 1.0)
{
return 1;
}
// Create a blend factor which is one before and at the fade plane
float viewDepth = gBuffer.ViewPos.z;
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
BRANCH
if (fade >= 1.0)
{
return 1;
}
// Figure out which cascade to sample from
uint cascadeIndex = 0;
for (uint i = 0; i < shadow.NumCascades - 1; i++)
{
if (viewDepth > shadow.CascadeSplits[i])
cascadeIndex = i + 1;
}
// Figure out which cascade to sample from
uint cascadeIndex = 0;
for (uint i = 0; i < shadow.NumCascades - 1; i++)
{
if (viewDepth > shadow.CascadeSplits[i])
cascadeIndex = i + 1;
}
#if defined(USE_GBUFFER_CUSTOM_DATA)
// Subsurface shadowing
@@ -458,51 +458,51 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shado
}
#endif
// Skip if surface is in a full shadow
float NoL = dot(gBuffer.Normal, light.Direction);
BRANCH
if (NoL <= 0)
{
return 0;
}
// Skip if surface is in a full shadow
float NoL = dot(gBuffer.Normal, light.Direction);
BRANCH
if (NoL <= 0)
{
return 0;
}
// Apply normal offset bias
float3 samplePosWS = gBuffer.WorldPos;
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
// Apply normal offset bias
float3 samplePosWS = gBuffer.WorldPos;
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
// Sample shadow
return SampleShadow(light, shadow, shadowMap, samplePosWS, viewDepth);
// Sample shadow
return SampleShadow(light, shadow, shadowMap, samplePosWS, viewDepth);
}
// Samples the shadow for the given spot light (PCF shadow map sampling)
float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, float3 worldPosition)
{
float3 toLight = light.Position - worldPosition;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
float dirCheck = dot(-light.Direction, L);
float3 toLight = light.Position - worldPosition;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
float dirCheck = dot(-light.Direction, L);
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius || dirCheck < 0)
{
return 1;
}
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius || dirCheck < 0)
{
return 1;
}
// Negate direction and use normalized value
toLight = -L;
// Negate direction and use normalized value
toLight = -L;
// Project into shadow space
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[0]);
shadowPosition.z -= shadow.Bias;
shadowPosition.xyz /= shadowPosition.w;
// Project into shadow space
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[0]);
shadowPosition.z -= shadow.Bias;
shadowPosition.xyz /= shadowPosition.w;
float2 shadowMapUVs = shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
float2 shadowMapUVs = shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
#if FilterSizeSpot == 2
#if FilterSizeSpot == 2
// Use single hardware sample with filtering
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, shadowMapUVs, shadowPosition.z);
// Use single hardware sample with filtering
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, shadowMapUVs, shadowPosition.z);
#else
@@ -525,28 +525,28 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap,
#endif
// Apply shadow fade and sharpness
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
result = lerp(1.0f, result, shadow.Fade);
// Apply shadow fade and sharpness
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
result = lerp(1.0f, result, shadow.Fade);
return result;
return result;
}
// Samples the shadow for the given spot light (PCF shadow map sampling) for the material surface (supports subsurface shadowing)
float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
{
subsurfaceShadow = 1;
float3 toLight = light.Position - gBuffer.WorldPos;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
float dirCheck = dot(-light.Direction, L);
subsurfaceShadow = 1;
float3 toLight = light.Position - gBuffer.WorldPos;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
float dirCheck = dot(-light.Direction, L);
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius || dirCheck < 0)
{
return 1;
}
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius || dirCheck < 0)
{
return 1;
}
#if defined(USE_GBUFFER_CUSTOM_DATA)
// Subsurface shadowing
@@ -570,51 +570,51 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap,
}
#endif
// Skip if surface is in a full shadow
float NoL = dot(gBuffer.Normal, L);
BRANCH
if (NoL <= 0)
{
return 0;
}
// Skip if surface is in a full shadow
float NoL = dot(gBuffer.Normal, L);
BRANCH
if (NoL <= 0)
{
return 0;
}
// Apply normal offset bias
float3 samplePosWS = gBuffer.WorldPos;
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
// Apply normal offset bias
float3 samplePosWS = gBuffer.WorldPos;
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
// Sample shadow
return SampleShadow(light, shadow, shadowMap, samplePosWS);
// Sample shadow
return SampleShadow(light, shadow, shadowMap, samplePosWS);
}
// Samples the shadow for the given point light (PCF shadow map sampling)
float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> shadowMap, float3 worldPosition)
{
float3 toLight = light.Position - worldPosition;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
float3 toLight = light.Position - worldPosition;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius)
{
return 1;
}
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius)
{
return 1;
}
// Negate direction and use normalized value
toLight = -L;
// Negate direction and use normalized value
toLight = -L;
// Figure out which cube face we're sampling from
int cubeFaceIndex = GetCubeFaceIndex(toLight);
// Figure out which cube face we're sampling from
int cubeFaceIndex = GetCubeFaceIndex(toLight);
// Project into shadow space
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cubeFaceIndex]);
shadowPosition.z -= shadow.Bias;
shadowPosition.xyz /= shadowPosition.w;
// Project into shadow space
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cubeFaceIndex]);
shadowPosition.z -= shadow.Bias;
shadowPosition.xyz /= shadowPosition.w;
#if FilterSizeCube == 2
#if FilterSizeCube == 2
// Use single hardware sample with filtering
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, toLight, shadowPosition.z);
// Use single hardware sample with filtering
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, toLight, shadowPosition.z);
#else
@@ -637,33 +637,33 @@ float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> s
#endif
// Apply shadow fade and sharpness
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
result = lerp(1.0f, result, shadow.Fade);
// Apply shadow fade and sharpness
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
result = lerp(1.0f, result, shadow.Fade);
return result;
return result;
}
// Samples the shadow for the given point light (PCF shadow map sampling) for the material surface (supports subsurface shadowing)
float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
{
subsurfaceShadow = 1;
float3 toLight = light.Position - gBuffer.WorldPos;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
subsurfaceShadow = 1;
float3 toLight = light.Position - gBuffer.WorldPos;
float toLightLength = length(toLight);
float3 L = toLight / toLightLength;
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius)
{
return 1;
}
// Skip pixels outside of the light influence
BRANCH
if (toLightLength > light.Radius)
{
return 1;
}
// Negate direction and use normalized value
toLight = -L;
// Negate direction and use normalized value
toLight = -L;
// Figure out which cube face we're sampling from
int cubeFaceIndex = GetCubeFaceIndex(toLight);
// Figure out which cube face we're sampling from
int cubeFaceIndex = GetCubeFaceIndex(toLight);
#if defined(USE_GBUFFER_CUSTOM_DATA)
// Subsurface shadowing
@@ -688,19 +688,19 @@ float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> s
#endif
// Skip if surface is in a full shadow
float NoL = dot(gBuffer.Normal, L);
BRANCH
if (NoL <= 0)
{
return 0;
}
float NoL = dot(gBuffer.Normal, L);
BRANCH
if (NoL <= 0)
{
return 0;
}
// Apply normal offset bias
float3 samplePosWS = gBuffer.WorldPos;
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
// Apply normal offset bias
float3 samplePosWS = gBuffer.WorldPos;
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
// Sample shadow
return SampleShadow(light, shadow, shadowMap, samplePosWS);
// Sample shadow
return SampleShadow(light, shadow, shadowMap, samplePosWS);
}
#endif