three-gpu-pathtracer 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +887 -815
- package/build/index.module.js +5796 -5451
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +5795 -5448
- package/build/index.umd.cjs.map +1 -1
- package/package.json +69 -68
- package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
- package/src/core/MaterialReducer.js +256 -256
- package/src/core/PathTracingRenderer.js +270 -270
- package/src/core/PathTracingSceneGenerator.js +1 -1
- package/src/index.js +39 -35
- package/src/materials/AlphaDisplayMaterial.js +48 -48
- package/src/materials/AmbientOcclusionMaterial.js +197 -197
- package/src/materials/BlendMaterial.js +67 -67
- package/src/materials/DenoiseMaterial.js +142 -142
- package/src/materials/GraphMaterial.js +243 -243
- package/src/materials/LambertPathTracingMaterial.js +285 -285
- package/src/materials/MaterialBase.js +56 -56
- package/src/materials/PhysicalPathTracingMaterial.js +973 -970
- package/src/objects/EquirectCamera.js +13 -13
- package/src/objects/PhysicalCamera.js +28 -28
- package/src/objects/PhysicalSpotLight.js +14 -14
- package/src/objects/ShapedAreaLight.js +12 -12
- package/src/shader/shaderEnvMapSampling.js +59 -59
- package/src/shader/shaderGGXFunctions.js +100 -100
- package/src/shader/shaderIridescenceFunctions.js +130 -130
- package/src/shader/shaderLayerTexelFetchFunctions.js +25 -0
- package/src/shader/shaderLightSampling.js +231 -231
- package/src/shader/shaderMaterialSampling.js +504 -542
- package/src/shader/shaderSheenFunctions.js +98 -98
- package/src/shader/shaderStructs.js +321 -321
- package/src/shader/shaderUtils.js +403 -364
- package/src/textures/GradientEquirectTexture.js +35 -0
- package/src/textures/ProceduralEquirectTexture.js +75 -0
- package/src/uniforms/AttributesTextureArray.js +35 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +259 -259
- package/src/uniforms/FloatAttributeTextureArray.js +169 -0
- package/src/uniforms/IESProfilesTexture.js +100 -100
- package/src/uniforms/LightsInfoUniformStruct.js +162 -162
- package/src/uniforms/MaterialsTexture.js +420 -426
- package/src/uniforms/PhysicalCameraUniform.js +36 -36
- package/src/uniforms/RenderTarget2DArray.js +97 -93
- package/src/uniforms/utils.js +21 -0
- package/src/utils/BlurredEnvMapGenerator.js +116 -113
- package/src/utils/IESLoader.js +325 -325
- package/src/utils/UVUnwrapper.js +101 -101
- package/src/workers/PathTracingSceneWorker.js +42 -42
|
@@ -1,231 +1,231 @@
|
|
|
1
|
-
export const shaderLightSampling = /* glsl */`
|
|
2
|
-
|
|
3
|
-
float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {
|
|
4
|
-
|
|
5
|
-
return smoothstep( coneCosine, penumbraCosine, angleCosine );
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
|
|
10
|
-
|
|
11
|
-
// based upon Frostbite 3 Moving to Physically-based Rendering
|
|
12
|
-
// page 32, equation 26: E[window1]
|
|
13
|
-
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
|
|
14
|
-
float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), EPSILON );
|
|
15
|
-
|
|
16
|
-
if ( cutoffDistance > 0.0 ) {
|
|
17
|
-
|
|
18
|
-
distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return distanceFalloff;
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
float getPhotometricAttenuation( sampler2DArray iesProfiles, int iesProfile, vec3 posToLight, vec3 lightDir, vec3 u, vec3 v ) {
|
|
27
|
-
|
|
28
|
-
float cosTheta = dot( posToLight, lightDir );
|
|
29
|
-
float angle = acos( cosTheta ) * ( 1.0 / PI );
|
|
30
|
-
|
|
31
|
-
return texture2D( iesProfiles, vec3( 0.0, angle, iesProfile ) ).r;
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
struct LightSampleRec {
|
|
36
|
-
|
|
37
|
-
bool hit;
|
|
38
|
-
float dist;
|
|
39
|
-
vec3 direction;
|
|
40
|
-
float pdf;
|
|
41
|
-
vec3 emission;
|
|
42
|
-
int type;
|
|
43
|
-
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection ) {
|
|
47
|
-
|
|
48
|
-
LightSampleRec lightSampleRec;
|
|
49
|
-
lightSampleRec.hit = false;
|
|
50
|
-
|
|
51
|
-
uint l;
|
|
52
|
-
for ( l = 0u; l < lightCount; l ++ ) {
|
|
53
|
-
|
|
54
|
-
Light light = readLightInfo( lights, l );
|
|
55
|
-
|
|
56
|
-
vec3 u = light.u;
|
|
57
|
-
vec3 v = light.v;
|
|
58
|
-
|
|
59
|
-
// check for backface
|
|
60
|
-
vec3 normal = normalize( cross( u, v ) );
|
|
61
|
-
if ( dot( normal, rayDirection ) < 0.0 ) {
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
u *= 1.0 / dot( u, u );
|
|
66
|
-
v *= 1.0 / dot( v, v );
|
|
67
|
-
|
|
68
|
-
float dist;
|
|
69
|
-
|
|
70
|
-
if(
|
|
71
|
-
( light.type == RECT_AREA_LIGHT_TYPE && intersectsRectangle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) ) ||
|
|
72
|
-
( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
|
|
73
|
-
) {
|
|
74
|
-
|
|
75
|
-
if ( dist < lightSampleRec.dist || ! lightSampleRec.hit ) {
|
|
76
|
-
|
|
77
|
-
float cosTheta = dot( rayDirection, normal );
|
|
78
|
-
|
|
79
|
-
lightSampleRec.hit = true;
|
|
80
|
-
lightSampleRec.dist = dist;
|
|
81
|
-
lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
82
|
-
lightSampleRec.emission = light.color * light.intensity;
|
|
83
|
-
lightSampleRec.direction = rayDirection;
|
|
84
|
-
lightSampleRec.type = light.type;
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
} else if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
89
|
-
|
|
90
|
-
// TODO: forward path tracing sampling needs to be made consistent with direct light sampling logic
|
|
91
|
-
// float radius = light.radius;
|
|
92
|
-
// vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
93
|
-
// float angle = acos( light.coneCos );
|
|
94
|
-
// float angleTan = tan( angle );
|
|
95
|
-
// float startDistance = radius / max( angleTan, EPSILON );
|
|
96
|
-
|
|
97
|
-
// u = light.u / radius;
|
|
98
|
-
// v = light.v / radius;
|
|
99
|
-
|
|
100
|
-
// if (
|
|
101
|
-
// intersectsCircle( light.position - normal * startDistance, normal, u, v, rayOrigin, rayDirection, dist ) &&
|
|
102
|
-
// ( dist < lightSampleRec.dist || ! lightSampleRec.hit )
|
|
103
|
-
// ) {
|
|
104
|
-
|
|
105
|
-
// float cosTheta = dot( rayDirection, normal );
|
|
106
|
-
// float spotAttenuation = light.iesProfile != - 1 ?
|
|
107
|
-
// getPhotometricAttenuation( iesProfiles, light.iesProfile, rayDirection, normal, u, v )
|
|
108
|
-
// : getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
109
|
-
|
|
110
|
-
// float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
111
|
-
|
|
112
|
-
// lightSampleRec.hit = true;
|
|
113
|
-
// lightSampleRec.dist = dist;
|
|
114
|
-
// lightSampleRec.direction = rayDirection;
|
|
115
|
-
// lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
116
|
-
// lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
117
|
-
|
|
118
|
-
// }
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return lightSampleRec;
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin ) {
|
|
129
|
-
|
|
130
|
-
LightSampleRec lightSampleRec;
|
|
131
|
-
lightSampleRec.hit = true;
|
|
132
|
-
lightSampleRec.type = light.type;
|
|
133
|
-
|
|
134
|
-
lightSampleRec.emission = light.color * light.intensity;
|
|
135
|
-
|
|
136
|
-
vec3 randomPos;
|
|
137
|
-
if( light.type == RECT_AREA_LIGHT_TYPE ) {
|
|
138
|
-
|
|
139
|
-
// rectangular area light
|
|
140
|
-
randomPos = light.position + light.u * ( rand() - 0.5 ) + light.v * ( rand() - 0.5 );
|
|
141
|
-
|
|
142
|
-
} else if( light.type == 1 ) {
|
|
143
|
-
|
|
144
|
-
// circular area light
|
|
145
|
-
float r = 0.5 * sqrt( rand() );
|
|
146
|
-
float theta = rand() * 2.0 * PI;
|
|
147
|
-
float x = r * cos( theta );
|
|
148
|
-
float y = r * sin( theta );
|
|
149
|
-
|
|
150
|
-
randomPos = light.position + light.u * x + light.v * y;
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
vec3 toLight = randomPos - rayOrigin;
|
|
155
|
-
float lightDistSq = dot( toLight, toLight );
|
|
156
|
-
lightSampleRec.dist = sqrt( lightDistSq );
|
|
157
|
-
|
|
158
|
-
vec3 direction = toLight / lightSampleRec.dist;
|
|
159
|
-
lightSampleRec.direction = direction;
|
|
160
|
-
|
|
161
|
-
vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
162
|
-
lightSampleRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
|
|
163
|
-
|
|
164
|
-
return lightSampleRec;
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, vec3 rayOrigin ) {
|
|
169
|
-
|
|
170
|
-
float radius = light.radius * sqrt( rand() );
|
|
171
|
-
float theta = rand() * 2.0 * PI;
|
|
172
|
-
float x = radius * cos( theta );
|
|
173
|
-
float y = radius * sin( theta );
|
|
174
|
-
|
|
175
|
-
vec3 u = light.u;
|
|
176
|
-
vec3 v = light.v;
|
|
177
|
-
vec3 normal = normalize( cross( u, v ) );
|
|
178
|
-
|
|
179
|
-
float angle = acos( light.coneCos );
|
|
180
|
-
float angleTan = tan( angle );
|
|
181
|
-
float startDistance = light.radius / max( angleTan, EPSILON );
|
|
182
|
-
|
|
183
|
-
vec3 randomPos = light.position - normal * startDistance + u * x + v * y;
|
|
184
|
-
vec3 toLight = randomPos - rayOrigin;
|
|
185
|
-
float lightDistSq = dot( toLight, toLight );
|
|
186
|
-
float dist = sqrt( lightDistSq );
|
|
187
|
-
|
|
188
|
-
vec3 direction = toLight / max( dist, EPSILON );
|
|
189
|
-
float cosTheta = dot( direction, normal );
|
|
190
|
-
|
|
191
|
-
float spotAttenuation = light.iesProfile != - 1 ?
|
|
192
|
-
getPhotometricAttenuation( iesProfiles, light.iesProfile, direction, normal, u, v )
|
|
193
|
-
: getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
194
|
-
|
|
195
|
-
float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
196
|
-
LightSampleRec lightSampleRec;
|
|
197
|
-
lightSampleRec.hit = true;
|
|
198
|
-
lightSampleRec.type = light.type;
|
|
199
|
-
lightSampleRec.dist = dist;
|
|
200
|
-
lightSampleRec.direction = direction;
|
|
201
|
-
lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
202
|
-
|
|
203
|
-
// TODO: this makes the result consistent between MIS and non MIS paths but at radius 0 the pdf is infinite
|
|
204
|
-
// and the intensity of the light is not correct
|
|
205
|
-
lightSampleRec.pdf = 1.0;
|
|
206
|
-
// lightSampleRec.pdf = lightDistSq / ( light.area * cosTheta );
|
|
207
|
-
|
|
208
|
-
return lightSampleRec;
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin ) {
|
|
213
|
-
|
|
214
|
-
// pick a random light
|
|
215
|
-
uint l = uint( rand() * float( lightCount ) );
|
|
216
|
-
Light light = readLightInfo( lights, l );
|
|
217
|
-
|
|
218
|
-
if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
219
|
-
|
|
220
|
-
return randomSpotLightSample( light, iesProfiles, rayOrigin );
|
|
221
|
-
|
|
222
|
-
} else {
|
|
223
|
-
|
|
224
|
-
// sample the light
|
|
225
|
-
return randomAreaLightSample( light, rayOrigin );
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
`;
|
|
1
|
+
export const shaderLightSampling = /* glsl */`
|
|
2
|
+
|
|
3
|
+
float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {
|
|
4
|
+
|
|
5
|
+
return smoothstep( coneCosine, penumbraCosine, angleCosine );
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
|
|
10
|
+
|
|
11
|
+
// based upon Frostbite 3 Moving to Physically-based Rendering
|
|
12
|
+
// page 32, equation 26: E[window1]
|
|
13
|
+
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
|
|
14
|
+
float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), EPSILON );
|
|
15
|
+
|
|
16
|
+
if ( cutoffDistance > 0.0 ) {
|
|
17
|
+
|
|
18
|
+
distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return distanceFalloff;
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
float getPhotometricAttenuation( sampler2DArray iesProfiles, int iesProfile, vec3 posToLight, vec3 lightDir, vec3 u, vec3 v ) {
|
|
27
|
+
|
|
28
|
+
float cosTheta = dot( posToLight, lightDir );
|
|
29
|
+
float angle = acos( cosTheta ) * ( 1.0 / PI );
|
|
30
|
+
|
|
31
|
+
return texture2D( iesProfiles, vec3( 0.0, angle, iesProfile ) ).r;
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
struct LightSampleRec {
|
|
36
|
+
|
|
37
|
+
bool hit;
|
|
38
|
+
float dist;
|
|
39
|
+
vec3 direction;
|
|
40
|
+
float pdf;
|
|
41
|
+
vec3 emission;
|
|
42
|
+
int type;
|
|
43
|
+
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection ) {
|
|
47
|
+
|
|
48
|
+
LightSampleRec lightSampleRec;
|
|
49
|
+
lightSampleRec.hit = false;
|
|
50
|
+
|
|
51
|
+
uint l;
|
|
52
|
+
for ( l = 0u; l < lightCount; l ++ ) {
|
|
53
|
+
|
|
54
|
+
Light light = readLightInfo( lights, l );
|
|
55
|
+
|
|
56
|
+
vec3 u = light.u;
|
|
57
|
+
vec3 v = light.v;
|
|
58
|
+
|
|
59
|
+
// check for backface
|
|
60
|
+
vec3 normal = normalize( cross( u, v ) );
|
|
61
|
+
if ( dot( normal, rayDirection ) < 0.0 ) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
u *= 1.0 / dot( u, u );
|
|
66
|
+
v *= 1.0 / dot( v, v );
|
|
67
|
+
|
|
68
|
+
float dist;
|
|
69
|
+
|
|
70
|
+
if(
|
|
71
|
+
( light.type == RECT_AREA_LIGHT_TYPE && intersectsRectangle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) ) ||
|
|
72
|
+
( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
|
|
73
|
+
) {
|
|
74
|
+
|
|
75
|
+
if ( dist < lightSampleRec.dist || ! lightSampleRec.hit ) {
|
|
76
|
+
|
|
77
|
+
float cosTheta = dot( rayDirection, normal );
|
|
78
|
+
|
|
79
|
+
lightSampleRec.hit = true;
|
|
80
|
+
lightSampleRec.dist = dist;
|
|
81
|
+
lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
82
|
+
lightSampleRec.emission = light.color * light.intensity;
|
|
83
|
+
lightSampleRec.direction = rayDirection;
|
|
84
|
+
lightSampleRec.type = light.type;
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
} else if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
89
|
+
|
|
90
|
+
// TODO: forward path tracing sampling needs to be made consistent with direct light sampling logic
|
|
91
|
+
// float radius = light.radius;
|
|
92
|
+
// vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
93
|
+
// float angle = acos( light.coneCos );
|
|
94
|
+
// float angleTan = tan( angle );
|
|
95
|
+
// float startDistance = radius / max( angleTan, EPSILON );
|
|
96
|
+
|
|
97
|
+
// u = light.u / radius;
|
|
98
|
+
// v = light.v / radius;
|
|
99
|
+
|
|
100
|
+
// if (
|
|
101
|
+
// intersectsCircle( light.position - normal * startDistance, normal, u, v, rayOrigin, rayDirection, dist ) &&
|
|
102
|
+
// ( dist < lightSampleRec.dist || ! lightSampleRec.hit )
|
|
103
|
+
// ) {
|
|
104
|
+
|
|
105
|
+
// float cosTheta = dot( rayDirection, normal );
|
|
106
|
+
// float spotAttenuation = light.iesProfile != - 1 ?
|
|
107
|
+
// getPhotometricAttenuation( iesProfiles, light.iesProfile, rayDirection, normal, u, v )
|
|
108
|
+
// : getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
109
|
+
|
|
110
|
+
// float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
111
|
+
|
|
112
|
+
// lightSampleRec.hit = true;
|
|
113
|
+
// lightSampleRec.dist = dist;
|
|
114
|
+
// lightSampleRec.direction = rayDirection;
|
|
115
|
+
// lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
116
|
+
// lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
117
|
+
|
|
118
|
+
// }
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return lightSampleRec;
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin ) {
|
|
129
|
+
|
|
130
|
+
LightSampleRec lightSampleRec;
|
|
131
|
+
lightSampleRec.hit = true;
|
|
132
|
+
lightSampleRec.type = light.type;
|
|
133
|
+
|
|
134
|
+
lightSampleRec.emission = light.color * light.intensity;
|
|
135
|
+
|
|
136
|
+
vec3 randomPos;
|
|
137
|
+
if( light.type == RECT_AREA_LIGHT_TYPE ) {
|
|
138
|
+
|
|
139
|
+
// rectangular area light
|
|
140
|
+
randomPos = light.position + light.u * ( rand() - 0.5 ) + light.v * ( rand() - 0.5 );
|
|
141
|
+
|
|
142
|
+
} else if( light.type == 1 ) {
|
|
143
|
+
|
|
144
|
+
// circular area light
|
|
145
|
+
float r = 0.5 * sqrt( rand() );
|
|
146
|
+
float theta = rand() * 2.0 * PI;
|
|
147
|
+
float x = r * cos( theta );
|
|
148
|
+
float y = r * sin( theta );
|
|
149
|
+
|
|
150
|
+
randomPos = light.position + light.u * x + light.v * y;
|
|
151
|
+
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
vec3 toLight = randomPos - rayOrigin;
|
|
155
|
+
float lightDistSq = dot( toLight, toLight );
|
|
156
|
+
lightSampleRec.dist = sqrt( lightDistSq );
|
|
157
|
+
|
|
158
|
+
vec3 direction = toLight / lightSampleRec.dist;
|
|
159
|
+
lightSampleRec.direction = direction;
|
|
160
|
+
|
|
161
|
+
vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
162
|
+
lightSampleRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
|
|
163
|
+
|
|
164
|
+
return lightSampleRec;
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, vec3 rayOrigin ) {
|
|
169
|
+
|
|
170
|
+
float radius = light.radius * sqrt( rand() );
|
|
171
|
+
float theta = rand() * 2.0 * PI;
|
|
172
|
+
float x = radius * cos( theta );
|
|
173
|
+
float y = radius * sin( theta );
|
|
174
|
+
|
|
175
|
+
vec3 u = light.u;
|
|
176
|
+
vec3 v = light.v;
|
|
177
|
+
vec3 normal = normalize( cross( u, v ) );
|
|
178
|
+
|
|
179
|
+
float angle = acos( light.coneCos );
|
|
180
|
+
float angleTan = tan( angle );
|
|
181
|
+
float startDistance = light.radius / max( angleTan, EPSILON );
|
|
182
|
+
|
|
183
|
+
vec3 randomPos = light.position - normal * startDistance + u * x + v * y;
|
|
184
|
+
vec3 toLight = randomPos - rayOrigin;
|
|
185
|
+
float lightDistSq = dot( toLight, toLight );
|
|
186
|
+
float dist = sqrt( lightDistSq );
|
|
187
|
+
|
|
188
|
+
vec3 direction = toLight / max( dist, EPSILON );
|
|
189
|
+
float cosTheta = dot( direction, normal );
|
|
190
|
+
|
|
191
|
+
float spotAttenuation = light.iesProfile != - 1 ?
|
|
192
|
+
getPhotometricAttenuation( iesProfiles, light.iesProfile, direction, normal, u, v )
|
|
193
|
+
: getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
194
|
+
|
|
195
|
+
float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
196
|
+
LightSampleRec lightSampleRec;
|
|
197
|
+
lightSampleRec.hit = true;
|
|
198
|
+
lightSampleRec.type = light.type;
|
|
199
|
+
lightSampleRec.dist = dist;
|
|
200
|
+
lightSampleRec.direction = direction;
|
|
201
|
+
lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
202
|
+
|
|
203
|
+
// TODO: this makes the result consistent between MIS and non MIS paths but at radius 0 the pdf is infinite
|
|
204
|
+
// and the intensity of the light is not correct
|
|
205
|
+
lightSampleRec.pdf = 1.0;
|
|
206
|
+
// lightSampleRec.pdf = lightDistSq / ( light.area * cosTheta );
|
|
207
|
+
|
|
208
|
+
return lightSampleRec;
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin ) {
|
|
213
|
+
|
|
214
|
+
// pick a random light
|
|
215
|
+
uint l = uint( rand() * float( lightCount ) );
|
|
216
|
+
Light light = readLightInfo( lights, l );
|
|
217
|
+
|
|
218
|
+
if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
219
|
+
|
|
220
|
+
return randomSpotLightSample( light, iesProfiles, rayOrigin );
|
|
221
|
+
|
|
222
|
+
} else {
|
|
223
|
+
|
|
224
|
+
// sample the light
|
|
225
|
+
return randomAreaLightSample( light, rayOrigin );
|
|
226
|
+
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
`;
|