three-gpu-pathtracer 0.0.10 → 0.0.12
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 +886 -886
- package/build/index.module.js +6479 -6472
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +6478 -6470
- package/build/index.umd.cjs.map +1 -1
- package/package.json +72 -69
- package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
- package/src/core/MaterialReducer.js +256 -256
- package/src/core/PathTracingRenderer.js +275 -275
- package/src/core/PathTracingSceneGenerator.js +69 -69
- package/src/index.js +39 -39
- package/src/materials/AlphaDisplayMaterial.js +48 -48
- package/src/materials/AmbientOcclusionMaterial.js +199 -199
- 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 +982 -982
- 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/shaderBvhAnyHit.js +76 -0
- package/src/shader/shaderEnvMapSampling.js +58 -58
- package/src/shader/shaderGGXFunctions.js +100 -100
- package/src/shader/shaderIridescenceFunctions.js +130 -130
- package/src/shader/shaderLayerTexelFetchFunctions.js +25 -25
- package/src/shader/shaderLightSampling.js +229 -229
- package/src/shader/shaderMaterialSampling.js +506 -498
- package/src/shader/shaderRandFunctions.js +57 -57
- package/src/shader/shaderSheenFunctions.js +98 -98
- package/src/shader/shaderSobolSampling.js +256 -256
- package/src/shader/shaderStructs.js +325 -325
- package/src/shader/shaderUtils.js +361 -361
- package/src/textures/GradientEquirectTexture.js +35 -35
- package/src/textures/ProceduralEquirectTexture.js +75 -75
- package/src/uniforms/AttributesTextureArray.js +35 -35
- package/src/uniforms/EquirectHdrInfoUniform.js +259 -259
- package/src/uniforms/FloatAttributeTextureArray.js +169 -169
- package/src/uniforms/IESProfilesTexture.js +100 -100
- package/src/uniforms/LightsInfoUniformStruct.js +207 -207
- package/src/uniforms/MaterialsTexture.js +426 -426
- package/src/uniforms/PhysicalCameraUniform.js +36 -36
- package/src/uniforms/RenderTarget2DArray.js +97 -97
- package/src/uniforms/utils.js +30 -30
- package/src/utils/BlurredEnvMapGenerator.js +116 -116
- package/src/utils/GeometryPreparationUtils.js +214 -214
- package/src/utils/IESLoader.js +325 -325
- package/src/utils/SobolNumberMapGenerator.js +80 -80
- package/src/utils/UVUnwrapper.js +101 -101
- package/src/workers/PathTracingSceneWorker.js +42 -42
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Camera } from 'three';
|
|
2
|
-
|
|
3
|
-
export class EquirectCamera extends Camera {
|
|
4
|
-
|
|
5
|
-
constructor() {
|
|
6
|
-
|
|
7
|
-
super();
|
|
8
|
-
|
|
9
|
-
this.isEquirectCamera = true;
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
}
|
|
1
|
+
import { Camera } from 'three';
|
|
2
|
+
|
|
3
|
+
export class EquirectCamera extends Camera {
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
|
|
7
|
+
super();
|
|
8
|
+
|
|
9
|
+
this.isEquirectCamera = true;
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { PerspectiveCamera } from 'three';
|
|
2
|
-
|
|
3
|
-
export class PhysicalCamera extends PerspectiveCamera {
|
|
4
|
-
|
|
5
|
-
set bokehSize( size ) {
|
|
6
|
-
|
|
7
|
-
this.fStop = this.getFocalLength() / size;
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
get bokehSize() {
|
|
12
|
-
|
|
13
|
-
return this.getFocalLength() / this.fStop;
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
constructor( ...args ) {
|
|
18
|
-
|
|
19
|
-
super( ...args );
|
|
20
|
-
this.fStop = 1.4;
|
|
21
|
-
this.apertureBlades = 0;
|
|
22
|
-
this.apertureRotation = 0;
|
|
23
|
-
this.focusDistance = 25;
|
|
24
|
-
this.anamorphicRatio = 1;
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
}
|
|
1
|
+
import { PerspectiveCamera } from 'three';
|
|
2
|
+
|
|
3
|
+
export class PhysicalCamera extends PerspectiveCamera {
|
|
4
|
+
|
|
5
|
+
set bokehSize( size ) {
|
|
6
|
+
|
|
7
|
+
this.fStop = this.getFocalLength() / size;
|
|
8
|
+
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get bokehSize() {
|
|
12
|
+
|
|
13
|
+
return this.getFocalLength() / this.fStop;
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
constructor( ...args ) {
|
|
18
|
+
|
|
19
|
+
super( ...args );
|
|
20
|
+
this.fStop = 1.4;
|
|
21
|
+
this.apertureBlades = 0;
|
|
22
|
+
this.apertureRotation = 0;
|
|
23
|
+
this.focusDistance = 25;
|
|
24
|
+
this.anamorphicRatio = 1;
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { SpotLight } from 'three';
|
|
2
|
-
|
|
3
|
-
export class PhysicalSpotLight extends SpotLight {
|
|
4
|
-
|
|
5
|
-
constructor( ...args ) {
|
|
6
|
-
|
|
7
|
-
super( ...args );
|
|
8
|
-
|
|
9
|
-
this.iesTexture = null;
|
|
10
|
-
this.radius = 0;
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
}
|
|
1
|
+
import { SpotLight } from 'three';
|
|
2
|
+
|
|
3
|
+
export class PhysicalSpotLight extends SpotLight {
|
|
4
|
+
|
|
5
|
+
constructor( ...args ) {
|
|
6
|
+
|
|
7
|
+
super( ...args );
|
|
8
|
+
|
|
9
|
+
this.iesTexture = null;
|
|
10
|
+
this.radius = 0;
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { RectAreaLight } from 'three';
|
|
2
|
-
|
|
3
|
-
export class ShapedAreaLight extends RectAreaLight {
|
|
4
|
-
|
|
5
|
-
constructor( ...args ) {
|
|
6
|
-
|
|
7
|
-
super( ...args );
|
|
8
|
-
this.isCircular = false;
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
}
|
|
1
|
+
import { RectAreaLight } from 'three';
|
|
2
|
+
|
|
3
|
+
export class ShapedAreaLight extends RectAreaLight {
|
|
4
|
+
|
|
5
|
+
constructor( ...args ) {
|
|
6
|
+
|
|
7
|
+
super( ...args );
|
|
8
|
+
this.isCircular = false;
|
|
9
|
+
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const shaderBvhAnyHit = /* glsl */`
|
|
2
|
+
|
|
3
|
+
bool bvhIntersectAnyHit(
|
|
4
|
+
BVH bvh, vec3 rayOrigin, vec3 rayDirection,
|
|
5
|
+
|
|
6
|
+
// output variables
|
|
7
|
+
out float side, out float dist
|
|
8
|
+
) {
|
|
9
|
+
|
|
10
|
+
uvec4 faceIndices;
|
|
11
|
+
vec3 faceNormal;
|
|
12
|
+
vec3 barycoord;
|
|
13
|
+
|
|
14
|
+
// stack needs to be twice as long as the deepest tree we expect because
|
|
15
|
+
// we push both the left and right child onto the stack every traversal
|
|
16
|
+
int ptr = 0;
|
|
17
|
+
uint stack[ 60 ];
|
|
18
|
+
stack[ 0 ] = 0u;
|
|
19
|
+
|
|
20
|
+
float triangleDistance = 1e20;
|
|
21
|
+
while ( ptr > - 1 && ptr < 60 ) {
|
|
22
|
+
|
|
23
|
+
uint currNodeIndex = stack[ ptr ];
|
|
24
|
+
ptr --;
|
|
25
|
+
|
|
26
|
+
// check if we intersect the current bounds
|
|
27
|
+
float boundsHitDistance = intersectsBVHNodeBounds( rayOrigin, rayDirection, bvh, currNodeIndex );
|
|
28
|
+
if ( boundsHitDistance == INFINITY ) {
|
|
29
|
+
|
|
30
|
+
continue;
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
uvec2 boundsInfo = uTexelFetch1D( bvh.bvhContents, currNodeIndex ).xy;
|
|
35
|
+
bool isLeaf = bool( boundsInfo.x & 0xffff0000u );
|
|
36
|
+
|
|
37
|
+
if ( isLeaf ) {
|
|
38
|
+
|
|
39
|
+
uint count = boundsInfo.x & 0x0000ffffu;
|
|
40
|
+
uint offset = boundsInfo.y;
|
|
41
|
+
|
|
42
|
+
bool found = intersectTriangles(
|
|
43
|
+
bvh, rayOrigin, rayDirection, offset, count, triangleDistance,
|
|
44
|
+
faceIndices, faceNormal, barycoord, side, dist
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if ( found ) {
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
} else {
|
|
54
|
+
|
|
55
|
+
uint leftIndex = currNodeIndex + 1u;
|
|
56
|
+
uint splitAxis = boundsInfo.x & 0x0000ffffu;
|
|
57
|
+
uint rightIndex = boundsInfo.y;
|
|
58
|
+
|
|
59
|
+
// set c2 in the stack so we traverse it later. We need to keep track of a pointer in
|
|
60
|
+
// the stack while we traverse. The second pointer added is the one that will be
|
|
61
|
+
// traversed first
|
|
62
|
+
ptr ++;
|
|
63
|
+
stack[ ptr ] = leftIndex;
|
|
64
|
+
|
|
65
|
+
ptr ++;
|
|
66
|
+
stack[ ptr ] = rightIndex;
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false;
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
`;
|
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
export const shaderEnvMapSampling = /* glsl */`
|
|
2
|
-
|
|
3
|
-
vec3 sampleEquirectEnvMapColor( vec3 direction, sampler2D map ) {
|
|
4
|
-
|
|
5
|
-
return texture2D( map, equirectDirectionToUv( direction ) ).rgb;
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
float envMapDirectionPdf( vec3 direction ) {
|
|
10
|
-
|
|
11
|
-
vec2 uv = equirectDirectionToUv( direction );
|
|
12
|
-
float theta = uv.y * PI;
|
|
13
|
-
float sinTheta = sin( theta );
|
|
14
|
-
if ( sinTheta == 0.0 ) {
|
|
15
|
-
|
|
16
|
-
return 0.0;
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return 1.0 / ( 2.0 * PI * PI * sinTheta );
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
float sampleEnvMap( EquirectHdrInfo info, vec3 direction, out vec3 color ) {
|
|
25
|
-
|
|
26
|
-
vec2 uv = equirectDirectionToUv( direction );
|
|
27
|
-
color = texture2D( info.map, uv ).rgb;
|
|
28
|
-
|
|
29
|
-
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
30
|
-
float lum = luminance( color );
|
|
31
|
-
ivec2 resolution = textureSize( info.map, 0 );
|
|
32
|
-
float pdf = lum / totalSum;
|
|
33
|
-
|
|
34
|
-
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
float sampleEnvMapProbability( EquirectHdrInfo info, vec2 r, out vec3 color, out vec3 direction ) {
|
|
39
|
-
|
|
40
|
-
// sample env map cdf
|
|
41
|
-
float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
42
|
-
float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
|
|
43
|
-
vec2 uv = vec2( u, v );
|
|
44
|
-
|
|
45
|
-
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
46
|
-
direction = derivedDirection;
|
|
47
|
-
color = texture2D( info.map, uv ).rgb;
|
|
48
|
-
|
|
49
|
-
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
50
|
-
float lum = luminance( color );
|
|
51
|
-
ivec2 resolution = textureSize( info.map, 0 );
|
|
52
|
-
float pdf = lum / totalSum;
|
|
53
|
-
|
|
54
|
-
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
`;
|
|
1
|
+
export const shaderEnvMapSampling = /* glsl */`
|
|
2
|
+
|
|
3
|
+
vec3 sampleEquirectEnvMapColor( vec3 direction, sampler2D map ) {
|
|
4
|
+
|
|
5
|
+
return texture2D( map, equirectDirectionToUv( direction ) ).rgb;
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
float envMapDirectionPdf( vec3 direction ) {
|
|
10
|
+
|
|
11
|
+
vec2 uv = equirectDirectionToUv( direction );
|
|
12
|
+
float theta = uv.y * PI;
|
|
13
|
+
float sinTheta = sin( theta );
|
|
14
|
+
if ( sinTheta == 0.0 ) {
|
|
15
|
+
|
|
16
|
+
return 0.0;
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return 1.0 / ( 2.0 * PI * PI * sinTheta );
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
float sampleEnvMap( EquirectHdrInfo info, vec3 direction, out vec3 color ) {
|
|
25
|
+
|
|
26
|
+
vec2 uv = equirectDirectionToUv( direction );
|
|
27
|
+
color = texture2D( info.map, uv ).rgb;
|
|
28
|
+
|
|
29
|
+
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
30
|
+
float lum = luminance( color );
|
|
31
|
+
ivec2 resolution = textureSize( info.map, 0 );
|
|
32
|
+
float pdf = lum / totalSum;
|
|
33
|
+
|
|
34
|
+
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
float sampleEnvMapProbability( EquirectHdrInfo info, vec2 r, out vec3 color, out vec3 direction ) {
|
|
39
|
+
|
|
40
|
+
// sample env map cdf
|
|
41
|
+
float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
42
|
+
float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
|
|
43
|
+
vec2 uv = vec2( u, v );
|
|
44
|
+
|
|
45
|
+
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
46
|
+
direction = derivedDirection;
|
|
47
|
+
color = texture2D( info.map, uv ).rgb;
|
|
48
|
+
|
|
49
|
+
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
50
|
+
float lum = luminance( color );
|
|
51
|
+
ivec2 resolution = textureSize( info.map, 0 );
|
|
52
|
+
float pdf = lum / totalSum;
|
|
53
|
+
|
|
54
|
+
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
`;
|
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
export const shaderGGXFunctions = /* glsl */`
|
|
2
|
-
// The GGX functions provide sampling and distribution information for normals as output so
|
|
3
|
-
// in order to get probability of scatter direction the half vector must be computed and provided.
|
|
4
|
-
// [0] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
|
|
5
|
-
// [1] https://hal.archives-ouvertes.fr/hal-01509746/document
|
|
6
|
-
// [2] http://jcgt.org/published/0007/04/01/
|
|
7
|
-
// [4] http://jcgt.org/published/0003/02/03/
|
|
8
|
-
|
|
9
|
-
// trowbridge-reitz === GGX === GTR
|
|
10
|
-
|
|
11
|
-
vec3 ggxDirection( vec3 incidentDir, vec2 roughness, vec2 uv ) {
|
|
12
|
-
|
|
13
|
-
// TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
|
|
14
|
-
// function below, as well
|
|
15
|
-
|
|
16
|
-
// Implementation from reference [1]
|
|
17
|
-
// stretch view
|
|
18
|
-
vec3 V = normalize( vec3( roughness * incidentDir.xy, incidentDir.z ) );
|
|
19
|
-
|
|
20
|
-
// orthonormal basis
|
|
21
|
-
vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
|
|
22
|
-
vec3 T2 = cross( T1, V );
|
|
23
|
-
|
|
24
|
-
// sample point with polar coordinates (r, phi)
|
|
25
|
-
float a = 1.0 / ( 1.0 + V.z );
|
|
26
|
-
float r = sqrt( uv.x );
|
|
27
|
-
float phi = ( uv.y < a ) ? uv.y / a * PI : PI + ( uv.y - a ) / ( 1.0 - a ) * PI;
|
|
28
|
-
float P1 = r * cos( phi );
|
|
29
|
-
float P2 = r * sin( phi ) * ( ( uv.y < a ) ? 1.0 : V.z );
|
|
30
|
-
|
|
31
|
-
// compute normal
|
|
32
|
-
vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
|
|
33
|
-
|
|
34
|
-
// unstretch
|
|
35
|
-
N = normalize( vec3( roughness * N.xy, max( 0.0, N.z ) ) );
|
|
36
|
-
|
|
37
|
-
return N;
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Below are PDF and related functions for use in a Monte Carlo path tracer
|
|
42
|
-
// as specified in Appendix B of the following paper
|
|
43
|
-
// See equation (34) from reference [0]
|
|
44
|
-
float ggxLamda( float theta, float roughness ) {
|
|
45
|
-
|
|
46
|
-
float tanTheta = tan( theta );
|
|
47
|
-
float tanTheta2 = tanTheta * tanTheta;
|
|
48
|
-
float alpha2 = roughness * roughness;
|
|
49
|
-
|
|
50
|
-
float numerator = - 1.0 + sqrt( 1.0 + alpha2 * tanTheta2 );
|
|
51
|
-
return numerator / 2.0;
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// See equation (34) from reference [0]
|
|
56
|
-
float ggxShadowMaskG1( float theta, float roughness ) {
|
|
57
|
-
|
|
58
|
-
return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// See equation (125) from reference [4]
|
|
63
|
-
float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
|
|
64
|
-
|
|
65
|
-
float incidentTheta = acos( wi.z );
|
|
66
|
-
float scatterTheta = acos( wo.z );
|
|
67
|
-
return 1.0 / ( 1.0 + ggxLamda( incidentTheta, roughness ) + ggxLamda( scatterTheta, roughness ) );
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// See equation (33) from reference [0]
|
|
72
|
-
float ggxDistribution( vec3 halfVector, float roughness ) {
|
|
73
|
-
|
|
74
|
-
float a2 = roughness * roughness;
|
|
75
|
-
a2 = max( EPSILON, a2 );
|
|
76
|
-
float cosTheta = halfVector.z;
|
|
77
|
-
float cosTheta4 = pow( cosTheta, 4.0 );
|
|
78
|
-
|
|
79
|
-
if ( cosTheta == 0.0 ) return 0.0;
|
|
80
|
-
|
|
81
|
-
float theta = acosSafe( halfVector.z );
|
|
82
|
-
float tanTheta = tan( theta );
|
|
83
|
-
float tanTheta2 = pow( tanTheta, 2.0 );
|
|
84
|
-
|
|
85
|
-
float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
|
|
86
|
-
return ( a2 / denom );
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// See equation (3) from reference [2]
|
|
91
|
-
float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
|
|
92
|
-
|
|
93
|
-
float incidentTheta = acos( wi.z );
|
|
94
|
-
float D = ggxDistribution( halfVector, roughness );
|
|
95
|
-
float G1 = ggxShadowMaskG1( incidentTheta, roughness );
|
|
96
|
-
|
|
97
|
-
return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
`;
|
|
1
|
+
export const shaderGGXFunctions = /* glsl */`
|
|
2
|
+
// The GGX functions provide sampling and distribution information for normals as output so
|
|
3
|
+
// in order to get probability of scatter direction the half vector must be computed and provided.
|
|
4
|
+
// [0] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
|
|
5
|
+
// [1] https://hal.archives-ouvertes.fr/hal-01509746/document
|
|
6
|
+
// [2] http://jcgt.org/published/0007/04/01/
|
|
7
|
+
// [4] http://jcgt.org/published/0003/02/03/
|
|
8
|
+
|
|
9
|
+
// trowbridge-reitz === GGX === GTR
|
|
10
|
+
|
|
11
|
+
vec3 ggxDirection( vec3 incidentDir, vec2 roughness, vec2 uv ) {
|
|
12
|
+
|
|
13
|
+
// TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
|
|
14
|
+
// function below, as well
|
|
15
|
+
|
|
16
|
+
// Implementation from reference [1]
|
|
17
|
+
// stretch view
|
|
18
|
+
vec3 V = normalize( vec3( roughness * incidentDir.xy, incidentDir.z ) );
|
|
19
|
+
|
|
20
|
+
// orthonormal basis
|
|
21
|
+
vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
|
|
22
|
+
vec3 T2 = cross( T1, V );
|
|
23
|
+
|
|
24
|
+
// sample point with polar coordinates (r, phi)
|
|
25
|
+
float a = 1.0 / ( 1.0 + V.z );
|
|
26
|
+
float r = sqrt( uv.x );
|
|
27
|
+
float phi = ( uv.y < a ) ? uv.y / a * PI : PI + ( uv.y - a ) / ( 1.0 - a ) * PI;
|
|
28
|
+
float P1 = r * cos( phi );
|
|
29
|
+
float P2 = r * sin( phi ) * ( ( uv.y < a ) ? 1.0 : V.z );
|
|
30
|
+
|
|
31
|
+
// compute normal
|
|
32
|
+
vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
|
|
33
|
+
|
|
34
|
+
// unstretch
|
|
35
|
+
N = normalize( vec3( roughness * N.xy, max( 0.0, N.z ) ) );
|
|
36
|
+
|
|
37
|
+
return N;
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Below are PDF and related functions for use in a Monte Carlo path tracer
|
|
42
|
+
// as specified in Appendix B of the following paper
|
|
43
|
+
// See equation (34) from reference [0]
|
|
44
|
+
float ggxLamda( float theta, float roughness ) {
|
|
45
|
+
|
|
46
|
+
float tanTheta = tan( theta );
|
|
47
|
+
float tanTheta2 = tanTheta * tanTheta;
|
|
48
|
+
float alpha2 = roughness * roughness;
|
|
49
|
+
|
|
50
|
+
float numerator = - 1.0 + sqrt( 1.0 + alpha2 * tanTheta2 );
|
|
51
|
+
return numerator / 2.0;
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// See equation (34) from reference [0]
|
|
56
|
+
float ggxShadowMaskG1( float theta, float roughness ) {
|
|
57
|
+
|
|
58
|
+
return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// See equation (125) from reference [4]
|
|
63
|
+
float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
|
|
64
|
+
|
|
65
|
+
float incidentTheta = acos( wi.z );
|
|
66
|
+
float scatterTheta = acos( wo.z );
|
|
67
|
+
return 1.0 / ( 1.0 + ggxLamda( incidentTheta, roughness ) + ggxLamda( scatterTheta, roughness ) );
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// See equation (33) from reference [0]
|
|
72
|
+
float ggxDistribution( vec3 halfVector, float roughness ) {
|
|
73
|
+
|
|
74
|
+
float a2 = roughness * roughness;
|
|
75
|
+
a2 = max( EPSILON, a2 );
|
|
76
|
+
float cosTheta = halfVector.z;
|
|
77
|
+
float cosTheta4 = pow( cosTheta, 4.0 );
|
|
78
|
+
|
|
79
|
+
if ( cosTheta == 0.0 ) return 0.0;
|
|
80
|
+
|
|
81
|
+
float theta = acosSafe( halfVector.z );
|
|
82
|
+
float tanTheta = tan( theta );
|
|
83
|
+
float tanTheta2 = pow( tanTheta, 2.0 );
|
|
84
|
+
|
|
85
|
+
float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
|
|
86
|
+
return ( a2 / denom );
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// See equation (3) from reference [2]
|
|
91
|
+
float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
|
|
92
|
+
|
|
93
|
+
float incidentTheta = acos( wi.z );
|
|
94
|
+
float D = ggxDistribution( halfVector, roughness );
|
|
95
|
+
float G1 = ggxShadowMaskG1( incidentTheta, roughness );
|
|
96
|
+
|
|
97
|
+
return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
`;
|