# SH 求谐函数与立体角

Untitled

# 直角坐标系转球面坐标系

Untitled

球坐标转 uv

Untitled

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//uv转vector
float4 UniformSampleSphere( float2 E )
{
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;

float PDF = 1.0 / (4 * PI);

return float4( H, PDF );
}

SH 求谐函数

UE 用的 2 阶

ue 存这 27 个 SH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
FThreeBandSHVector SHBasisFunction3(half3 InputVector)
{
FThreeBandSHVectorResult;
// These are derived from simplifying SHBasisFunction in C++
Result.V0.x = 0.282095f;
Result.V0.y = -0.488603f * InputVector.y;
Result.V0.z = 0.488603f * InputVector.z;
Result.V0.w = -0.488603f * InputVector.x;

half3 VectorSquared = InputVector * InputVector;
Result.V1.x = 1.092548f * InputVector.x * InputVector.y;
Result.V1.y = -1.092548f * InputVector.y * InputVector.z;
Result.V1.z = 0.315392f * (3.0f * VectorSquared.z - 1.0f);
Result.V1.w = -1.092548f * InputVector.x * InputVector.z;
Result.V2 = 0.546274f * (VectorSquared.x - VectorSquared.y);

return Result;
}

FThreeBandSHVectorRGB SampleSHRGB(in float3 SampleDirection, in float weight, in float MipLevel)
{
const float3 SampleColor = SourceCubemapTexture.SampleLevel(SourceCubemapSampler, SampleDirection, MipLevel).rgb;

FThreeBandSHVector Sh3Vector = SHBasisFunction3(SampleDirection);
FThreeBandSHVectorRGB Result;
Result.R = MulSH3(Sh3Vector, SampleColor.r * weight);//weight是 4Π/采样次数
Result.G = MulSH3(Sh3Vector, SampleColor.g * weight);
Result.B = MulSH3(Sh3Vector, SampleColor.b * weight);
return Result;
}

void ComputeSkyEnvMapDiffuseIrradianceCS(uint3 ThreadId : SV_DispatchThreadID)
{
const uint LinearIndex = THREADGROUP_SIZE_X * ThreadId.y + ThreadId.x;

// For a 128x128 cubemap, sampling mip level 2 with only 8x8 samples matches closely the super sampled version.
const float3 SampleDirection = UniformSampleSphere((float2(ThreadId.xy)+0.5f) / float2(THREADGROUP_SIZE_X, THREADGROUP_SIZE_Y)).xyz;
IrradianceSHShared[LinearIndex] = SampleSHRGB(SampleDirection, UniformSampleSolidAngle, MipIndex);

#if THREADGROUP_SIZE != 64
#error That is the only reduction supported today
#endif

// Wait for all group threads to be done
GroupMemoryBarrierWithGroupSync();

if (LinearIndex < 32)
{
IrradianceSHShared[LinearIndex] = AddSH(IrradianceSHShared[LinearIndex], IrradianceSHShared[LinearIndex + 32]);
}
GroupMemoryBarrierWithGroupSync();

if (LinearIndex < 16)
{
IrradianceSHShared[LinearIndex] = AddSH(IrradianceSHShared[LinearIndex], IrradianceSHShared[LinearIndex + 16]);
}
GroupMemoryBarrierWithGroupSync();

// The smallest wave size is 16 on Intel hardware. So now we can do simple math operations without group sync.

if (LinearIndex < 8)
{
IrradianceSHShared[LinearIndex] = AddSH(IrradianceSHShared[LinearIndex], IrradianceSHShared[LinearIndex + 8]);
}
if (LinearIndex < 4)
{
IrradianceSHShared[LinearIndex] = AddSH(IrradianceSHShared[LinearIndex], IrradianceSHShared[LinearIndex + 4]);
}
if (LinearIndex < 2)
{
IrradianceSHShared[LinearIndex] = AddSH(IrradianceSHShared[LinearIndex], IrradianceSHShared[LinearIndex + 2]);
}

if (LinearIndex < 1)
{
FThreeBandSHVectorRGB SkyIrradiance = AddSH(IrradianceSHShared[LinearIndex], IrradianceSHShared[LinearIndex + 1]);

// Pack the SH coefficients in a way that makes applying the lighting use the least shader instructions
// This has the diffuse convolution coefficients baked in. See "Stupid Spherical Harmonics (SH) Tricks".
//https://wolfand11.gitee.io/blogs/graphics/StupidSphericalHarmonics.html
// Also see UpdateSkyIrradianceGpuBuffer.
const float SqrtPI = sqrt(PI);
const float Coefficient0 = 1.0f / (2.0f * SqrtPI);
const float Coefficient1 = sqrt(3.0f) / (3.0f * SqrtPI);
const float Coefficient2 = sqrt(15.0f) / (8.0f * SqrtPI);
const float Coefficient3 = sqrt(5.0f) / (16.0f * SqrtPI);
const float Coefficient4 = 0.5f * Coefficient2;

OutIrradianceEnvMapSH[0].x = -Coefficient1 * SkyIrradiance.R.V0[3];
OutIrradianceEnvMapSH[0].y = -Coefficient1 * SkyIrradiance.R.V0[1];
OutIrradianceEnvMapSH[0].z = Coefficient1 * SkyIrradiance.R.V0[2];
OutIrradianceEnvMapSH[0].w = Coefficient0 * SkyIrradiance.R.V0[0] - Coefficient3 * SkyIrradiance.R.V1[2];//[6];

OutIrradianceEnvMapSH[1].x = -Coefficient1 * SkyIrradiance.G.V0[3];
OutIrradianceEnvMapSH[1].y = -Coefficient1 * SkyIrradiance.G.V0[1];
OutIrradianceEnvMapSH[1].z = Coefficient1 * SkyIrradiance.G.V0[2];
OutIrradianceEnvMapSH[1].w = Coefficient0 * SkyIrradiance.G.V0[0] - Coefficient3 * SkyIrradiance.G.V1[2];//[6];

OutIrradianceEnvMapSH[2].x = -Coefficient1 * SkyIrradiance.B.V0[3];
OutIrradianceEnvMapSH[2].y = -Coefficient1 * SkyIrradiance.B.V0[1];
OutIrradianceEnvMapSH[2].z = Coefficient1 * SkyIrradiance.B.V0[2];
OutIrradianceEnvMapSH[2].w = Coefficient0 * SkyIrradiance.B.V0[0] - Coefficient3 * SkyIrradiance.B.V1[2];//[6];

OutIrradianceEnvMapSH[3].x = Coefficient2 * SkyIrradiance.R.V1[0];//[4];
OutIrradianceEnvMapSH[3].y = -Coefficient2 * SkyIrradiance.R.V1[1];//V[5];
OutIrradianceEnvMapSH[3].z = 3.0f * Coefficient3 * SkyIrradiance.R.V1[2];//[6];
OutIrradianceEnvMapSH[3].w = -Coefficient2 * SkyIrradiance.R.V1[3];//[7];

OutIrradianceEnvMapSH[4].x = Coefficient2 * SkyIrradiance.G.V1[0];//[4];
OutIrradianceEnvMapSH[4].y = -Coefficient2 * SkyIrradiance.G.V1[1];//[5];
OutIrradianceEnvMapSH[4].z = 3.0f * Coefficient3 * SkyIrradiance.G.V1[2];//[6];
OutIrradianceEnvMapSH[4].w = -Coefficient2 * SkyIrradiance.G.V1[3];//[7];

OutIrradianceEnvMapSH[5].x = Coefficient2 * SkyIrradiance.B.V1[0];//[4];
OutIrradianceEnvMapSH[5].y = -Coefficient2 * SkyIrradiance.B.V1[1];//[5];
OutIrradianceEnvMapSH[5].z = 3.0f * Coefficient3 * SkyIrradiance.B.V1[2];//[6];
OutIrradianceEnvMapSH[5].w = -Coefficient2 * SkyIrradiance.B.V1[3];//[7];

OutIrradianceEnvMapSH[6].x = Coefficient4 * SkyIrradiance.R.V2;//[8];
OutIrradianceEnvMapSH[6].y = Coefficient4 * SkyIrradiance.G.V2;//[8];
OutIrradianceEnvMapSH[6].z = Coefficient4 * SkyIrradiance.B.V2;//[8];
OutIrradianceEnvMapSH[6].w = 1.0f;
}
}