Reformat shaders source code
This commit is contained in:
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
@@ -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
|
||||
|
||||
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Smoke.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Sparks.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Sparks.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GI/DDGI.flax
(Stored with Git LFS)
BIN
Content/Shaders/GI/DDGI.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
BIN
Content/Shaders/GI/GlobalSurfaceAtlas.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user