273 lines
6.8 KiB
HLSL
273 lines
6.8 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================================
|
|
SkyLightMipTreeCommon.ush: Common utilities for MipTree-based SkyLight sampling
|
|
===============================================================================================*/
|
|
|
|
#include "MipTreeCommon.ush"
|
|
|
|
float3 GetTextureCubeVector(float3 TexelCoord, uint2 TextureRes)
|
|
{
|
|
float3 Result;
|
|
|
|
float2 UV = TexelCoord.xy / float2(TextureRes.xy);
|
|
UV = UV * 2.0 - 1.0;
|
|
|
|
uint Cubeface = TexelCoord.z;
|
|
if (Cubeface == 0)
|
|
{
|
|
Result = float3(1, -UV.y, -UV.x);
|
|
}
|
|
else if (Cubeface == 1)
|
|
{
|
|
Result = float3(-1, -UV.y, UV.x);
|
|
}
|
|
else if (Cubeface == 2)
|
|
{
|
|
Result = float3(UV.x, 1, UV.y);
|
|
}
|
|
else if (Cubeface == 3)
|
|
{
|
|
Result = float3(UV.x, -1, -UV.y);
|
|
}
|
|
else if (Cubeface == 4)
|
|
{
|
|
Result = float3(UV.x, -UV.y, 1);
|
|
}
|
|
else if (Cubeface == 5)
|
|
{
|
|
Result = float3(-UV.x, -UV.y, -1);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
uint GetMaximumComponentIndex(float3 Input)
|
|
{
|
|
float AbsX = abs(Input.x);
|
|
float AbsY = abs(Input.y);
|
|
float AbsZ = abs(Input.z);
|
|
if (AbsX > AbsY)
|
|
{
|
|
return AbsX > AbsZ ? 0 : 2;
|
|
}
|
|
else
|
|
{
|
|
return AbsY > AbsZ ? 1 : 2;
|
|
}
|
|
}
|
|
|
|
uint3 GetTextureCubeCoordinate(float3 WorldDirection, uint2 TextureRes)
|
|
{
|
|
// Strongest coordinate determines cube face
|
|
uint MaxComponent = GetMaximumComponentIndex(WorldDirection);
|
|
WorldDirection /= abs(WorldDirection[MaxComponent]);
|
|
float3 TextureCubeCoordinate;
|
|
if (MaxComponent == 0)
|
|
{
|
|
if (WorldDirection[MaxComponent] > 0)
|
|
{
|
|
TextureCubeCoordinate = float3(-WorldDirection.z, -WorldDirection.y, 0);
|
|
}
|
|
else
|
|
{
|
|
TextureCubeCoordinate = float3(WorldDirection.z, -WorldDirection.y, 1);
|
|
}
|
|
}
|
|
else if (MaxComponent == 1)
|
|
{
|
|
if (WorldDirection[MaxComponent] > 0)
|
|
{
|
|
TextureCubeCoordinate = float3(WorldDirection.x, WorldDirection.z, 2);
|
|
}
|
|
else
|
|
{
|
|
TextureCubeCoordinate = float3(WorldDirection.x, -WorldDirection.z, 3);
|
|
}
|
|
}
|
|
else if (MaxComponent == 2)
|
|
{
|
|
if (WorldDirection[MaxComponent] > 0)
|
|
{
|
|
TextureCubeCoordinate = float3(WorldDirection.x, -WorldDirection.y, 4);
|
|
}
|
|
else
|
|
{
|
|
TextureCubeCoordinate = float3(-WorldDirection.x, -WorldDirection.y, 5);
|
|
}
|
|
}
|
|
|
|
TextureCubeCoordinate.xy = (TextureCubeCoordinate.xy + 1.0) / 2.0;
|
|
TextureCubeCoordinate.xy *= TextureRes;
|
|
return TextureCubeCoordinate;
|
|
}
|
|
|
|
/**
|
|
* Cube texel solid angle computation based on formula from Manne Öhrström's
|
|
* thesis - "Spherical Harmonics, Precomputed Radiance Transfer and Realtime Radiosity in Computer Games".
|
|
*/
|
|
float AreaElement(float x, float y)
|
|
{
|
|
// Equation 7.12
|
|
return atan2(x * y, sqrt(x * x + y * y + 1));
|
|
}
|
|
|
|
float TexelCoordSolidAngle(uint2 TextureCoord, uint2 TextureRes)
|
|
{
|
|
float2 InvResolution = 1.0f / (float)TextureRes;
|
|
|
|
// Convert texture coordinate to [-1, 1] range, offset to texel center.
|
|
float U = (2.0f * ((float)TextureCoord.x + 0.5f) * InvResolution.x) - 1.0f;
|
|
float V = (2.0f * ((float)TextureCoord.y + 0.5f) * InvResolution.y) - 1.0f;
|
|
|
|
// Projected area
|
|
float x0 = U - InvResolution.x;
|
|
float y0 = V - InvResolution.y;
|
|
float x1 = U + InvResolution.x;
|
|
float y1 = V + InvResolution.y;
|
|
|
|
// Equation 7.12
|
|
float SolidAngle = AreaElement(x0, y0) - AreaElement(x0, y1) - AreaElement(x1, y0) + AreaElement(x1, y1);
|
|
return SolidAngle;
|
|
}
|
|
|
|
void BuildFaceCdf(uint MipCount, out float FaceCdf[7])
|
|
{
|
|
uint BufferOffset = BufferOffsetAtPixel(uint2(0, 0), MipCount, SkyLight.MipDimensions.xy);
|
|
|
|
FaceCdf[0] = 0.0;
|
|
FaceCdf[1] = SkyLight.MipTreePosX[BufferOffset];
|
|
FaceCdf[2] = FaceCdf[1] + SkyLight.MipTreeNegX[BufferOffset];
|
|
FaceCdf[3] = FaceCdf[2] + SkyLight.MipTreePosY[BufferOffset];
|
|
FaceCdf[4] = FaceCdf[3] + SkyLight.MipTreeNegY[BufferOffset];
|
|
FaceCdf[5] = FaceCdf[4] + SkyLight.MipTreePosZ[BufferOffset];
|
|
FaceCdf[6] = FaceCdf[5] + SkyLight.MipTreeNegZ[BufferOffset];
|
|
float Sum = FaceCdf[6];
|
|
|
|
for (uint Index = 1; Index < 6; ++Index)
|
|
{
|
|
FaceCdf[Index] /= Sum;
|
|
}
|
|
FaceCdf[6] = 1.0;
|
|
}
|
|
|
|
uint SampleFace(float FaceCdf[7], float RandSample, out float FacePdf)
|
|
{
|
|
// Determine CDF entry
|
|
uint FaceIndex = 1;
|
|
while (RandSample > FaceCdf[FaceIndex] && FaceIndex < 7)
|
|
{
|
|
FaceIndex++;
|
|
}
|
|
|
|
// Calculate PDF and return entry
|
|
FacePdf = FaceCdf[FaceIndex] - FaceCdf[FaceIndex - 1];
|
|
|
|
// Cdf is incremented by 1
|
|
return FaceIndex - 1;
|
|
}
|
|
|
|
uint SampleFace(int MipCount, float RandSample, out float FacePdf)
|
|
{
|
|
float FaceCdf[7];
|
|
BuildFaceCdf(MipCount, FaceCdf);
|
|
return SampleFace(FaceCdf, RandSample, FacePdf);
|
|
}
|
|
|
|
float PdfFace(float FaceCdf[7], uint FaceIndex)
|
|
{
|
|
// Cdf is offset by 1
|
|
FaceIndex++;
|
|
float Pdf = FaceCdf[FaceIndex] - FaceCdf[FaceIndex - 1];
|
|
return Pdf;
|
|
}
|
|
|
|
float PdfFace(int MipCount, uint FaceIndex)
|
|
{
|
|
float FaceCdf[7];
|
|
BuildFaceCdf(MipCount, FaceCdf);
|
|
return PdfFace(FaceCdf, FaceIndex);
|
|
}
|
|
|
|
float4 SampleMipTree(uint FaceIndex, uint4 BufferOffset)
|
|
{
|
|
float4 Values;
|
|
if (FaceIndex == 0)
|
|
{
|
|
Values.x = SkyLight.MipTreePosX[BufferOffset.x];
|
|
Values.y = SkyLight.MipTreePosX[BufferOffset.y];
|
|
Values.z = SkyLight.MipTreePosX[BufferOffset.z];
|
|
Values.w = SkyLight.MipTreePosX[BufferOffset.w];
|
|
}
|
|
else if (FaceIndex == 1)
|
|
{
|
|
Values.x = SkyLight.MipTreeNegX[BufferOffset.x];
|
|
Values.y = SkyLight.MipTreeNegX[BufferOffset.y];
|
|
Values.z = SkyLight.MipTreeNegX[BufferOffset.z];
|
|
Values.w = SkyLight.MipTreeNegX[BufferOffset.w];
|
|
}
|
|
else if (FaceIndex == 2)
|
|
{
|
|
Values.x = SkyLight.MipTreePosY[BufferOffset.x];
|
|
Values.y = SkyLight.MipTreePosY[BufferOffset.y];
|
|
Values.z = SkyLight.MipTreePosY[BufferOffset.z];
|
|
Values.w = SkyLight.MipTreePosY[BufferOffset.w];
|
|
}
|
|
else if (FaceIndex == 3)
|
|
{
|
|
Values.x = SkyLight.MipTreeNegY[BufferOffset.x];
|
|
Values.y = SkyLight.MipTreeNegY[BufferOffset.y];
|
|
Values.z = SkyLight.MipTreeNegY[BufferOffset.z];
|
|
Values.w = SkyLight.MipTreeNegY[BufferOffset.w];
|
|
}
|
|
else if (FaceIndex == 4)
|
|
{
|
|
Values.x = SkyLight.MipTreePosZ[BufferOffset.x];
|
|
Values.y = SkyLight.MipTreePosZ[BufferOffset.y];
|
|
Values.z = SkyLight.MipTreePosZ[BufferOffset.z];
|
|
Values.w = SkyLight.MipTreePosZ[BufferOffset.w];
|
|
}
|
|
else
|
|
{
|
|
Values.x = SkyLight.MipTreeNegZ[BufferOffset.x];
|
|
Values.y = SkyLight.MipTreeNegZ[BufferOffset.y];
|
|
Values.z = SkyLight.MipTreeNegZ[BufferOffset.z];
|
|
Values.w = SkyLight.MipTreeNegZ[BufferOffset.w];
|
|
}
|
|
|
|
return Values;
|
|
}
|
|
|
|
float PdfMipTree(uint3 TextureCoord, uint StopLevel)
|
|
{
|
|
float MipPdf = 1.0;
|
|
uint BufferOffset = BufferOffsetAtPixel(TextureCoord.xy, StopLevel, SkyLight.MipDimensions.xy);
|
|
if (TextureCoord.z == 0)
|
|
{
|
|
MipPdf = SkyLight.MipTreePdfPosX[BufferOffset];
|
|
}
|
|
else if (TextureCoord.z == 1)
|
|
{
|
|
MipPdf = SkyLight.MipTreePdfNegX[BufferOffset];
|
|
}
|
|
else if (TextureCoord.z == 2)
|
|
{
|
|
MipPdf = SkyLight.MipTreePdfPosY[BufferOffset];
|
|
}
|
|
else if (TextureCoord.z == 3)
|
|
{
|
|
MipPdf = SkyLight.MipTreePdfNegY[BufferOffset];
|
|
}
|
|
else if (TextureCoord.z == 4)
|
|
{
|
|
MipPdf = SkyLight.MipTreePdfPosZ[BufferOffset];
|
|
}
|
|
else // if (TextureCoord.z == 5)
|
|
{
|
|
MipPdf = SkyLight.MipTreePdfNegZ[BufferOffset];
|
|
}
|
|
return MipPdf;
|
|
}
|
|
|
|
|