Files
2026-07-13 13:13:17 +08:00

372 lines
8.9 KiB
HLSL

float4x4 objectToWorldMatrix;
float4x4 worldToCameraMatrix;
float4x4 cameraToObjectMatrix; // modelview inverse
float4x4 projMatrix;
float4x4 textureMatrix;
TextureCube CubeMap : register(t0);;
//Texture2D txDiffuse;
//Texture2D Image : register(t0);
sampler texSampler : register(s0);
float g_CubeSize = 256;
float g_CubeLod = 0;
float g_CubeLodCount = 1;
// float Roughness;
// int BaseMip;
// int NumSamples;
SamplerState samLinear
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
cbuffer Params : register(b0)
{
float Roughness;
int BaseMip;
int NumSamples;
float Exposure;
}
float mod(float a, float b)
{
return a - b*floor(a/b);
}
float3 mod(float3 a, float b)
{
return a - b*floor(a/b);
}
static const float PI = 3.14159265358979;
float radicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
float2 hammersley2d(uint i, uint N)
{
return float2(float(i)/float(N), radicalInverse_VdC(i));
}
/*
float3 hemisphereSample_uniform(float u, float v)
{
float phi = v * 2.0 * PI;
float cosTheta = 1.0 - u;
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
return float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
}
float3 hemisphereSample_cos(float u, float v)
{
float phi = v * 2.0 * PI;
float cosTheta = sqrt(1.0 - u);
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
return float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
}
float3 importanceSampleGGX(float2 xi, float3 N)
{
float alpha = Roughness * Roughness;
float alpha2 = alpha*alpha;
float phi = 2 * PI * xi.x;
float cosTheta = sqrt((1 - xi.y) / (1 + (alpha2 - 1) * xi.y));
float sinTheta = sqrt(1 - cosTheta*cosTheta);
float3 H;
H.x = sinTheta * cos(phi);
H.y = sinTheta * sin(phi);
H.z = cosTheta;
float3 up = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 tangentX = normalize(cross(up, N));
float3 tangentY = cross(N, tangentX);
return tangentX*H.x + tangentY*H.y + N*H.z;
}
*/
struct vsOutput
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
void vsMain(out vsOutput o, uint id : SV_VERTEXID)
{
o.uv = float2((id << 1) & 2, id & 2);
o.pos = float4(o.uv * float2(2,-2) + float2(-1,1), 0, 1);
//o.uv = (o.pos.xy * float2(0.5,-0.5) + 0.5) * 4;
//o.uv.y = 1 - o.uv.y;
}
struct gsOutput
{
float4 pos : SV_POSITION;
float3 nrm : TEXCOORD0;
float4 col : COLOR0;
uint face : SV_RENDERTARGETARRAYINDEX;
};
float4 colorOfBox(uint face)
{
float4 c = float4(0,0,0,1);
if (face == 0) // posx (red)
{
c = float4(1,0,0,1);
}
else if (face == 1) // negx (cyan)
{
c = float4(1,1,0,1);
}
else if (face == 2) // posy (green)
{
c = float4(0,1,0,1);
}
else if (face == 3) // negy (magenta)
{
c = float4(0,1,1,1);
}
else if (face == 4) // posz (blue)
{
c = float4(0,0,1,1);
}
else // if (i.face == 5) // negz (yellow)
{
c = float4(1,0,1,1);
}
return c;
}
float3 UvAndIndexToBoxCoord(float2 uv, uint face)
{
float3 n = float3(0,0,0);
float3 t = float3(0,0,0);
if (face == 0) // posx (red)
{
n = float3(1,0,0);
t = float3(0,1,0);
}
else if (face == 1) // negx (cyan)
{
n = float3(-1,0,0);
t = float3(0,1,0);
}
else if (face == 2) // posy (green)
{
n = float3(0,-1,0);
t = float3(0,0,-1);
}
else if (face == 3) // negy (magenta)
{
n = float3(0,1,0);
t = float3(0,0,1);
}
else if (face == 4) // posz (blue)
{
n = float3(0,0,-1);
t = float3(0,1,0);
}
else // if (i.face == 5) // negz (yellow)
{
n = float3(0,0,1);
t = float3(0,1,0);
}
float3 x = cross(n, t);
uv = uv * 2 - 1;
n = n + t*uv.y + x*uv.x;
n.y *= -1;
n.z *= -1;
return n;
}
[maxvertexcount(18)]
void gsMain(triangle vsOutput input[3], inout TriangleStream<gsOutput> output)
{
for( int f = 0; f < 6; ++f )
{
for( int v = 0; v < 3; ++v )
{
gsOutput o;
o.pos = input[v].pos;
o.nrm = UvAndIndexToBoxCoord(input[v].uv, f);
o.col = colorOfBox(f);
o.face = f;
output.Append(o);
}
output.RestartStrip();
}
}
// static const SamplerState g_samCube
// {
// Filter = MIN_MAG_MIP_LINEAR;
// AddressU = Clamp;
// AddressV = Clamp;
// };
/*
cbuffer mip : register(b0)
{
float g_CubeSize;
float g_CubeLod;
float g_CubeLodCount;
};
*/
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
/*
float radicalInverse_VdC(uint bits) {
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
*/
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
float2 Hammersley(uint i, uint N)
{
return float2(float(i)/float(N), radicalInverse_VdC(i));
}
float G_schlick_IBL(float NoV, float NoL, float roughness)
{
float k = roughness*roughness/2.0f;
float one_minus_k = 1.0f - k;
return (NoL / (NoL * one_minus_k + k)) * (NoV / (NoV * one_minus_k + k) );
}
// Image-Based Lighting
// http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
float3 ImportanceSampleGGX( float2 Xi, float roughness, float3 N )
{
float a = roughness * roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
float3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
float3 UpVector = abs(N.z) < 0.999 ? float3(0,0,1) : float3(1,0,0);
float3 TangentX = normalize( cross( UpVector, N ) );
float3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
// Ignacio Castano via http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
float3 fix_cube_lookup_for_lod(float3 v, float cube_size, float lod)
{
float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
float scale = 1 - exp2(lod) / cube_size;
if (abs(v.x) != M) v.x *= scale;
if (abs(v.y) != M) v.y *= scale;
if (abs(v.z) != M) v.z *= scale;
return v;
}
float D_GGX(float NoH, float roughness)
{
// towbridge-reitz / GGX distribution
float alpha = roughness*roughness;
float alpha2 = alpha*alpha;
float NoH2 = NoH*NoH;
float f = NoH2 * (alpha2 - 1.0) + 1;
return alpha2 / (3.1415 * f*f);
}
//#define REFERENCE_ON
float4 psMain(in gsOutput i) : SV_TARGET0
{
float3 N = normalize(i.nrm);
// return colorOfBox(i.face);
float4 totalRadiance = float4(0,0,0,0);
float roughness = max(Roughness, 0.01);
#ifdef REFERENCE_ON
uint NUM_SAMPLES = 25000;
#else
uint NUM_SAMPLES = NumSamples;
#endif
[fastopt]
for (uint j = 0; j < NUM_SAMPLES; ++j)
{
float2 Xi = Hammersley(j, NUM_SAMPLES);
float3 H = ImportanceSampleGGX(Xi, roughness, N);
float3 L = 2*dot(N, H)*H - N;
float NdotL = saturate(dot(N, L));
if (NdotL > 0)
{
#ifdef REFERENCE_ON
float mipmapLevel = 0;
#else
float NdotH = saturate(dot(N,H));
float pdf_H = D_GGX(NdotH, roughness)*NdotH;
float pdf = pdf_H/(4*NdotH); // transform from half to incoming
float area = 2*3.1415f; // hemispehere area
float solidangleSample = area/(NUM_SAMPLES*pdf); // solid angle for sample
float solidangleTexel = area/(3.0*128*128); // solid angle per cubemap texel
float mipmapLevel = clamp(0.5 * log2(solidangleSample/solidangleTexel), BaseMip, 9);
#endif
totalRadiance.rgb += CubeMap.SampleLevel(texSampler, L, mipmapLevel).rgb*NdotL;
totalRadiance.w += NdotL;
}
}
//return float4(Roughness, Roughness, Roughness, 1);
return float4(totalRadiance.rgb / totalRadiance.w * Exposure, 1);
}
// technique10 Render
// {
// pass P0
// {
// SetVertexShader( CompileShader( vs_5_0, vsMain() ) );
// SetGeometryShader( CompileShader( gs_5_0, gsMain() ) );
// SetPixelShader( CompileShader( ps_5_0, psMain() ) );
// }
// }