three-gpu-pathtracer 0.0.12 → 0.0.14
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/README.md +102 -7
- package/build/index.module.js +2891 -2065
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +2886 -2062
- package/build/index.umd.cjs.map +1 -1
- package/package.json +2 -1
- package/src/core/PathTracingRenderer.js +87 -16
- package/src/core/PathTracingSceneGenerator.js +1 -1
- package/src/core/QuiltPathTracingRenderer.js +223 -0
- package/src/index.js +5 -8
- package/src/materials/{GraphMaterial.js → debug/GraphMaterial.js} +1 -1
- package/src/materials/{AlphaDisplayMaterial.js → fullscreen/AlphaDisplayMaterial.js} +1 -1
- package/src/materials/{BlendMaterial.js → fullscreen/BlendMaterial.js} +1 -1
- package/src/materials/{DenoiseMaterial.js → fullscreen/DenoiseMaterial.js} +1 -1
- package/src/materials/{LambertPathTracingMaterial.js → pathtracing/LambertPathTracingMaterial.js} +18 -7
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +635 -0
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +179 -0
- package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +81 -0
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +317 -0
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +54 -0
- package/src/materials/{AmbientOcclusionMaterial.js → surface/AmbientOcclusionMaterial.js} +16 -8
- package/src/materials/surface/FogVolumeMaterial.js +23 -0
- package/src/shader/bsdf/bsdfSampling.glsl.js +490 -0
- package/src/shader/bsdf/fog.glsl.js +23 -0
- package/src/shader/bsdf/ggx.glsl.js +102 -0
- package/src/shader/bsdf/iridescence.glsl.js +135 -0
- package/src/shader/bsdf/sheen.glsl.js +98 -0
- package/src/shader/{shaderLayerTexelFetchFunctions.js → common/arraySamplerTexelFetch.glsl.js} +1 -1
- package/src/shader/common/bvhAnyHit.glsl.js +76 -0
- package/src/shader/common/fresnel.glsl.js +98 -0
- package/src/shader/common/intersectShapes.glsl.js +62 -0
- package/src/shader/common/math.glsl.js +81 -0
- package/src/shader/common/utils.glsl.js +116 -0
- package/src/shader/{shaderRandFunctions.js → rand/pcg.glsl.js} +1 -1
- package/src/shader/{shaderSobolSampling.js → rand/sobol.glsl.js} +3 -3
- package/src/shader/sampling/equirectSampling.glsl.js +62 -0
- package/src/shader/sampling/lightSampling.glsl.js +223 -0
- package/src/shader/sampling/shapeSampling.glsl.js +86 -0
- package/src/shader/structs/cameraStruct.glsl.js +13 -0
- package/src/shader/structs/equirectStruct.glsl.js +14 -0
- package/src/shader/structs/fogMaterialBvh.glsl.js +62 -0
- package/src/shader/structs/lightsStruct.glsl.js +78 -0
- package/src/shader/{shaderStructs.js → structs/materialStruct.glsl.js} +5 -123
- package/src/uniforms/EquirectHdrInfoUniform.js +29 -11
- package/src/uniforms/LightsInfoUniformStruct.js +9 -4
- package/src/uniforms/MaterialsTexture.js +80 -3
- package/src/utils/BlurredEnvMapGenerator.js +2 -2
- package/src/utils/SobolNumberMapGenerator.js +3 -3
- package/src/utils/macroify.js +9 -0
- package/src/materials/PhysicalPathTracingMaterial.js +0 -982
- package/src/shader/shaderBvhAnyHit.js +0 -76
- package/src/shader/shaderEnvMapSampling.js +0 -58
- package/src/shader/shaderGGXFunctions.js +0 -100
- package/src/shader/shaderIridescenceFunctions.js +0 -130
- package/src/shader/shaderLightSampling.js +0 -229
- package/src/shader/shaderMaterialSampling.js +0 -506
- package/src/shader/shaderSheenFunctions.js +0 -98
- package/src/shader/shaderUtils.js +0 -361
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export const iridescenceGLSL = /* glsl */`
|
|
2
|
+
|
|
3
|
+
// XYZ to sRGB color space
|
|
4
|
+
const mat3 XYZ_TO_REC709 = mat3(
|
|
5
|
+
3.2404542, -0.9692660, 0.0556434,
|
|
6
|
+
-1.5371385, 1.8760108, -0.2040259,
|
|
7
|
+
-0.4985314, 0.0415560, 1.0572252
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
vec3 fresnel0ToIor( vec3 fresnel0 ) {
|
|
11
|
+
|
|
12
|
+
vec3 sqrtF0 = sqrt( fresnel0 );
|
|
13
|
+
return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Conversion FO/IOR
|
|
18
|
+
vec3 iorToFresnel0( vec3 transmittedIor, float incidentIor ) {
|
|
19
|
+
|
|
20
|
+
return square( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ior is a value between 1.0 and 3.0. 1.0 is air interface
|
|
25
|
+
float iorToFresnel0( float transmittedIor, float incidentIor ) {
|
|
26
|
+
|
|
27
|
+
return square( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ) );
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Fresnel equations for dielectric/dielectric interfaces. See https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html
|
|
32
|
+
vec3 evalSensitivity( float OPD, vec3 shift ) {
|
|
33
|
+
|
|
34
|
+
float phase = 2.0 * PI * OPD * 1.0e-9;
|
|
35
|
+
|
|
36
|
+
vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );
|
|
37
|
+
vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );
|
|
38
|
+
vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );
|
|
39
|
+
|
|
40
|
+
vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - square( phase ) * var );
|
|
41
|
+
xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * square( phase ) );
|
|
42
|
+
xyz /= 1.0685e-7;
|
|
43
|
+
|
|
44
|
+
vec3 srgb = XYZ_TO_REC709 * xyz;
|
|
45
|
+
return srgb;
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// See Section 4. Analytic Spectral Integration, A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence, https://hal.archives-ouvertes.fr/hal-01518344/document
|
|
50
|
+
vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
|
|
51
|
+
|
|
52
|
+
vec3 I;
|
|
53
|
+
|
|
54
|
+
// Force iridescenceIor -> outsideIOR when thinFilmThickness -> 0.0
|
|
55
|
+
float iridescenceIor = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
|
|
56
|
+
|
|
57
|
+
// Evaluate the cosTheta on the base layer (Snell law)
|
|
58
|
+
float sinTheta2Sq = square( outsideIOR / iridescenceIor ) * ( 1.0 - square( cosTheta1 ) );
|
|
59
|
+
|
|
60
|
+
// Handle TIR:
|
|
61
|
+
float cosTheta2Sq = 1.0 - sinTheta2Sq;
|
|
62
|
+
if ( cosTheta2Sq < 0.0 ) {
|
|
63
|
+
|
|
64
|
+
return vec3( 1.0 );
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
float cosTheta2 = sqrt( cosTheta2Sq );
|
|
69
|
+
|
|
70
|
+
// First interface
|
|
71
|
+
float R0 = iorToFresnel0( iridescenceIor, outsideIOR );
|
|
72
|
+
float R12 = schlickFresnel( cosTheta1, R0 );
|
|
73
|
+
float R21 = R12;
|
|
74
|
+
float T121 = 1.0 - R12;
|
|
75
|
+
float phi12 = 0.0;
|
|
76
|
+
if ( iridescenceIor < outsideIOR ) {
|
|
77
|
+
|
|
78
|
+
phi12 = PI;
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
float phi21 = PI - phi12;
|
|
83
|
+
|
|
84
|
+
// Second interface
|
|
85
|
+
vec3 baseIOR = fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
|
|
86
|
+
vec3 R1 = iorToFresnel0( baseIOR, iridescenceIor );
|
|
87
|
+
vec3 R23 = schlickFresnel( cosTheta2, R1 );
|
|
88
|
+
vec3 phi23 = vec3( 0.0 );
|
|
89
|
+
if ( baseIOR[0] < iridescenceIor ) {
|
|
90
|
+
|
|
91
|
+
phi23[ 0 ] = PI;
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if ( baseIOR[1] < iridescenceIor ) {
|
|
96
|
+
|
|
97
|
+
phi23[ 1 ] = PI;
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if ( baseIOR[2] < iridescenceIor ) {
|
|
102
|
+
|
|
103
|
+
phi23[ 2 ] = PI;
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Phase shift
|
|
108
|
+
float OPD = 2.0 * iridescenceIor * thinFilmThickness * cosTheta2;
|
|
109
|
+
vec3 phi = vec3( phi21 ) + phi23;
|
|
110
|
+
|
|
111
|
+
// Compound terms
|
|
112
|
+
vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );
|
|
113
|
+
vec3 r123 = sqrt( R123 );
|
|
114
|
+
vec3 Rs = square( T121 ) * R23 / ( vec3( 1.0 ) - R123 );
|
|
115
|
+
|
|
116
|
+
// Reflectance term for m = 0 (DC term amplitude)
|
|
117
|
+
vec3 C0 = R12 + Rs;
|
|
118
|
+
I = C0;
|
|
119
|
+
|
|
120
|
+
// Reflectance term for m > 0 (pairs of diracs)
|
|
121
|
+
vec3 Cm = Rs - T121;
|
|
122
|
+
for ( int m = 1; m <= 2; ++ m ) {
|
|
123
|
+
|
|
124
|
+
Cm *= r123;
|
|
125
|
+
vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );
|
|
126
|
+
I += Cm * Sm;
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Since out of gamut colors might be produced, negative color values are clamped to 0.
|
|
131
|
+
return max( I, vec3( 0.0 ) );
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
`;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const sheenGLSL = /* glsl */`
|
|
2
|
+
|
|
3
|
+
// See equation (2) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
4
|
+
float velvetD( float cosThetaH, float roughness ) {
|
|
5
|
+
|
|
6
|
+
float alpha = max( roughness, 0.07 );
|
|
7
|
+
alpha = alpha * alpha;
|
|
8
|
+
|
|
9
|
+
float invAlpha = 1.0 / alpha;
|
|
10
|
+
|
|
11
|
+
float sqrCosThetaH = cosThetaH * cosThetaH;
|
|
12
|
+
float sinThetaH = max( 1.0 - sqrCosThetaH, 0.001 );
|
|
13
|
+
|
|
14
|
+
return ( 2.0 + invAlpha ) * pow( sinThetaH, 0.5 * invAlpha ) / ( 2.0 * PI );
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
float velvetParamsInterpolate( int i, float oneMinusAlphaSquared ) {
|
|
19
|
+
|
|
20
|
+
const float p0[5] = float[5]( 25.3245, 3.32435, 0.16801, -1.27393, -4.85967 );
|
|
21
|
+
const float p1[5] = float[5]( 21.5473, 3.82987, 0.19823, -1.97760, -4.32054 );
|
|
22
|
+
|
|
23
|
+
return mix( p1[i], p0[i], oneMinusAlphaSquared );
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
float velvetL( float x, float alpha ) {
|
|
28
|
+
|
|
29
|
+
float oneMinusAlpha = 1.0 - alpha;
|
|
30
|
+
float oneMinusAlphaSquared = oneMinusAlpha * oneMinusAlpha;
|
|
31
|
+
|
|
32
|
+
float a = velvetParamsInterpolate( 0, oneMinusAlphaSquared );
|
|
33
|
+
float b = velvetParamsInterpolate( 1, oneMinusAlphaSquared );
|
|
34
|
+
float c = velvetParamsInterpolate( 2, oneMinusAlphaSquared );
|
|
35
|
+
float d = velvetParamsInterpolate( 3, oneMinusAlphaSquared );
|
|
36
|
+
float e = velvetParamsInterpolate( 4, oneMinusAlphaSquared );
|
|
37
|
+
|
|
38
|
+
return a / ( 1.0 + b * pow( abs( x ), c ) ) + d * x + e;
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// See equation (3) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
43
|
+
float velvetLambda( float cosTheta, float alpha ) {
|
|
44
|
+
|
|
45
|
+
return abs( cosTheta ) < 0.5 ? exp( velvetL( cosTheta, alpha ) ) : exp( 2.0 * velvetL( 0.5, alpha ) - velvetL( 1.0 - cosTheta, alpha ) );
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// See Section 3, Shadowing Term, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
50
|
+
float velvetG( float cosThetaO, float cosThetaI, float roughness ) {
|
|
51
|
+
|
|
52
|
+
float alpha = max( roughness, 0.07 );
|
|
53
|
+
alpha = alpha * alpha;
|
|
54
|
+
|
|
55
|
+
return 1.0 / ( 1.0 + velvetLambda( cosThetaO, alpha ) + velvetLambda( cosThetaI, alpha ) );
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
float directionalAlbedoSheen( float cosTheta, float alpha ) {
|
|
60
|
+
|
|
61
|
+
cosTheta = saturate( cosTheta );
|
|
62
|
+
|
|
63
|
+
float c = 1.0 - cosTheta;
|
|
64
|
+
float c3 = c * c * c;
|
|
65
|
+
|
|
66
|
+
return 0.65584461 * c3 + 1.0 / ( 4.16526551 + exp( -7.97291361 * sqrt( alpha ) + 6.33516894 ) );
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
float sheenAlbedoScaling( vec3 wo, vec3 wi, SurfaceRecord surf ) {
|
|
71
|
+
|
|
72
|
+
float alpha = max( surf.sheenRoughness, 0.07 );
|
|
73
|
+
alpha = alpha * alpha;
|
|
74
|
+
|
|
75
|
+
float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
|
|
76
|
+
|
|
77
|
+
float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
|
|
78
|
+
float eWi = directionalAlbedoSheen( saturateCos( wi.z ), alpha );
|
|
79
|
+
|
|
80
|
+
return min( 1.0 - maxSheenColor * eWo, 1.0 - maxSheenColor * eWi );
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// See Section 5, Layering, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
85
|
+
float sheenAlbedoScaling( vec3 wo, SurfaceRecord surf ) {
|
|
86
|
+
|
|
87
|
+
float alpha = max( surf.sheenRoughness, 0.07 );
|
|
88
|
+
alpha = alpha * alpha;
|
|
89
|
+
|
|
90
|
+
float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
|
|
91
|
+
|
|
92
|
+
float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
|
|
93
|
+
|
|
94
|
+
return 1.0 - maxSheenColor * eWo;
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
`;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const bvhAnyHitGLSL = /* 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
|
+
`;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export const fresnelGLSL = /* glsl */`
|
|
2
|
+
|
|
3
|
+
bool totalInternalReflection( float cosTheta, float eta ) {
|
|
4
|
+
|
|
5
|
+
float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
|
|
6
|
+
return eta * sinTheta > 1.0;
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// https://google.github.io/filament/Filament.md.html#materialsystem/diffusebrdf
|
|
11
|
+
float schlickFresnel( float cosine, float f0 ) {
|
|
12
|
+
|
|
13
|
+
return f0 + ( 1.0 - f0 ) * pow( 1.0 - cosine, 5.0 );
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
vec3 schlickFresnel( float cosine, vec3 f0 ) {
|
|
18
|
+
|
|
19
|
+
return f0 + ( 1.0 - f0 ) * pow( 1.0 - cosine, 5.0 );
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
vec3 schlickFresnel( float cosine, vec3 f0, vec3 f90 ) {
|
|
24
|
+
|
|
25
|
+
return f0 + ( f90 - f0 ) * pow( 1.0 - cosine, 5.0 );
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
float dielectricFresnel( float cosThetaI, float eta ) {
|
|
30
|
+
|
|
31
|
+
// https://schuttejoe.github.io/post/disneybsdf/
|
|
32
|
+
float ni = eta;
|
|
33
|
+
float nt = 1.0;
|
|
34
|
+
|
|
35
|
+
// Check for total internal reflection
|
|
36
|
+
float sinThetaISq = 1.0f - cosThetaI * cosThetaI;
|
|
37
|
+
float sinThetaTSq = eta * eta * sinThetaISq;
|
|
38
|
+
if( sinThetaTSq >= 1.0 ) {
|
|
39
|
+
|
|
40
|
+
return 1.0;
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
float sinThetaT = sqrt( sinThetaTSq );
|
|
45
|
+
|
|
46
|
+
float cosThetaT = sqrt( max( 0.0, 1.0f - sinThetaT * sinThetaT ) );
|
|
47
|
+
float rParallel = ( ( nt * cosThetaI ) - ( ni * cosThetaT ) ) / ( ( nt * cosThetaI ) + ( ni * cosThetaT ) );
|
|
48
|
+
float rPerpendicular = ( ( ni * cosThetaI ) - ( nt * cosThetaT ) ) / ( ( ni * cosThetaI ) + ( nt * cosThetaT ) );
|
|
49
|
+
return ( rParallel * rParallel + rPerpendicular * rPerpendicular ) / 2.0;
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// https://raytracing.github.io/books/RayTracingInOneWeekend.html#dielectrics/schlickapproximation
|
|
54
|
+
float iorRatioToF0( float eta ) {
|
|
55
|
+
|
|
56
|
+
return pow( ( 1.0 - eta ) / ( 1.0 + eta ), 2.0 );
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
vec3 evaluateFresnel( float cosTheta, float eta, vec3 f0, vec3 f90 ) {
|
|
61
|
+
|
|
62
|
+
if ( totalInternalReflection( cosTheta, eta ) ) {
|
|
63
|
+
|
|
64
|
+
return f90;
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return schlickFresnel( cosTheta, f0, f90 );
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
float evaluateFresnelWeight( float cosTheta, float eta, float f0 ) {
|
|
73
|
+
|
|
74
|
+
if ( totalInternalReflection( cosTheta, eta ) ) {
|
|
75
|
+
|
|
76
|
+
return 1.0;
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return schlickFresnel( cosTheta, f0 );
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
// https://schuttejoe.github.io/post/disneybsdf/
|
|
86
|
+
float disneyFresnel( vec3 wo, vec3 wi, vec3 wh, float f0, float eta, float metalness ) {
|
|
87
|
+
|
|
88
|
+
float dotHV = dot( wo, wh );
|
|
89
|
+
float dotHL = dot( wi, wh );
|
|
90
|
+
|
|
91
|
+
float dielectricFresnel = dielectricFresnel( abs( dotHV ), eta );
|
|
92
|
+
float metallicFresnel = schlickFresnel( dotHL, f0 );
|
|
93
|
+
|
|
94
|
+
return mix( dielectricFresnel, metallicFresnel, metalness );
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
*/
|
|
98
|
+
`;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const intersectShapesGLSL = /* glsl */`
|
|
2
|
+
|
|
3
|
+
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
4
|
+
// falls in the bounds of the rectangle on that same plane.
|
|
5
|
+
// Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
|
|
6
|
+
bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
|
|
7
|
+
|
|
8
|
+
float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
9
|
+
|
|
10
|
+
if ( t > EPSILON ) {
|
|
11
|
+
|
|
12
|
+
vec3 p = rayOrigin + rayDirection * t;
|
|
13
|
+
vec3 vi = p - center;
|
|
14
|
+
|
|
15
|
+
// check if p falls inside the rectangle
|
|
16
|
+
float a1 = dot( u, vi );
|
|
17
|
+
if ( abs( a1 ) <= 0.5 ) {
|
|
18
|
+
|
|
19
|
+
float a2 = dot( v, vi );
|
|
20
|
+
if ( abs( a2 ) <= 0.5 ) {
|
|
21
|
+
|
|
22
|
+
dist = t;
|
|
23
|
+
return true;
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return false;
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
36
|
+
// falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
|
|
37
|
+
bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
|
|
38
|
+
|
|
39
|
+
float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
40
|
+
|
|
41
|
+
if ( t > EPSILON ) {
|
|
42
|
+
|
|
43
|
+
vec3 hit = rayOrigin + rayDirection * t;
|
|
44
|
+
vec3 vi = hit - position;
|
|
45
|
+
|
|
46
|
+
float a1 = dot( u, vi );
|
|
47
|
+
float a2 = dot( v, vi );
|
|
48
|
+
|
|
49
|
+
if( length( vec2( a1, a2 ) ) <= 0.5 ) {
|
|
50
|
+
|
|
51
|
+
dist = t;
|
|
52
|
+
return true;
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false;
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
`;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const mathGLSL = /* glsl */`
|
|
2
|
+
|
|
3
|
+
// Fast arccos approximation used to remove banding artifacts caused by numerical errors in acos.
|
|
4
|
+
// This is a cubic Lagrange interpolating polynomial for x = [-1, -1/2, 0, 1/2, 1].
|
|
5
|
+
// For more information see: https://github.com/gkjohnson/three-gpu-pathtracer/pull/171#issuecomment-1152275248
|
|
6
|
+
float acosApprox( float x ) {
|
|
7
|
+
|
|
8
|
+
x = clamp( x, -1.0, 1.0 );
|
|
9
|
+
return ( - 0.69813170079773212 * x * x - 0.87266462599716477 ) * x + 1.5707963267948966;
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// An acos with input values bound to the range [-1, 1].
|
|
14
|
+
float acosSafe( float x ) {
|
|
15
|
+
|
|
16
|
+
return acos( clamp( x, -1.0, 1.0 ) );
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
float saturateCos( float val ) {
|
|
21
|
+
|
|
22
|
+
return clamp( val, 0.001, 1.0 );
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
float square( float t ) {
|
|
27
|
+
|
|
28
|
+
return t * t;
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
vec2 square( vec2 t ) {
|
|
33
|
+
|
|
34
|
+
return t * t;
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
vec3 square( vec3 t ) {
|
|
39
|
+
|
|
40
|
+
return t * t;
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
vec4 square( vec4 t ) {
|
|
45
|
+
|
|
46
|
+
return t * t;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
vec2 rotateVector( vec2 v, float t ) {
|
|
51
|
+
|
|
52
|
+
float ac = cos( t );
|
|
53
|
+
float as = sin( t );
|
|
54
|
+
return vec2(
|
|
55
|
+
v.x * ac - v.y * as,
|
|
56
|
+
v.x * as + v.y * ac
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// forms a basis with the normal vector as Z
|
|
62
|
+
mat3 getBasisFromNormal( vec3 normal ) {
|
|
63
|
+
|
|
64
|
+
vec3 other;
|
|
65
|
+
if ( abs( normal.x ) > 0.5 ) {
|
|
66
|
+
|
|
67
|
+
other = vec3( 0.0, 1.0, 0.0 );
|
|
68
|
+
|
|
69
|
+
} else {
|
|
70
|
+
|
|
71
|
+
other = vec3( 1.0, 0.0, 0.0 );
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
vec3 ortho = normalize( cross( normal, other ) );
|
|
76
|
+
vec3 ortho2 = normalize( cross( normal, ortho ) );
|
|
77
|
+
return mat3( ortho2, ortho, normal );
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
`;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export const utilsGLSL = /* glsl */`
|
|
2
|
+
|
|
3
|
+
// TODO: possibly this should be renamed something related to material or path tracing logic
|
|
4
|
+
|
|
5
|
+
#ifndef RAY_OFFSET
|
|
6
|
+
#define RAY_OFFSET 1e-4
|
|
7
|
+
#endif
|
|
8
|
+
|
|
9
|
+
// adjust the hit point by the surface normal by a factor of some offset and the
|
|
10
|
+
// maximum component-wise value of the current point to accommodate floating point
|
|
11
|
+
// error as values increase.
|
|
12
|
+
vec3 stepRayOrigin( vec3 rayOrigin, vec3 rayDirection, vec3 offset, float dist ) {
|
|
13
|
+
|
|
14
|
+
vec3 point = rayOrigin + rayDirection * dist;
|
|
15
|
+
vec3 absPoint = abs( point );
|
|
16
|
+
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
17
|
+
return point + offset * ( maxPoint + 1.0 ) * RAY_OFFSET;
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md#attenuation
|
|
22
|
+
vec3 transmissionAttenuation( float dist, vec3 attColor, float attDist ) {
|
|
23
|
+
|
|
24
|
+
vec3 ot = - log( attColor ) / attDist;
|
|
25
|
+
return exp( - ot * dist );
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
vec3 getHalfVector( vec3 wi, vec3 wo, float eta ) {
|
|
30
|
+
|
|
31
|
+
// get the half vector - assuming if the light incident vector is on the other side
|
|
32
|
+
// of the that it's transmissive.
|
|
33
|
+
vec3 h;
|
|
34
|
+
if ( wi.z > 0.0 ) {
|
|
35
|
+
|
|
36
|
+
h = normalize( wi + wo );
|
|
37
|
+
|
|
38
|
+
} else {
|
|
39
|
+
|
|
40
|
+
// Scale by the ior ratio to retrieve the appropriate half vector
|
|
41
|
+
// From Section 2.2 on computing the transmission half vector:
|
|
42
|
+
// https://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf
|
|
43
|
+
h = normalize( wi + wo * eta );
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
h *= sign( h.z );
|
|
48
|
+
return h;
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
vec3 getHalfVector( vec3 a, vec3 b ) {
|
|
53
|
+
|
|
54
|
+
return normalize( a + b );
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The discrepancy between interpolated surface normal and geometry normal can cause issues when a ray
|
|
59
|
+
// is cast that is on the top side of the geometry normal plane but below the surface normal plane. If
|
|
60
|
+
// we find a ray like that we ignore it to avoid artifacts.
|
|
61
|
+
// This function returns if the direction is on the same side of both planes.
|
|
62
|
+
bool isDirectionValid( vec3 direction, vec3 surfaceNormal, vec3 geometryNormal ) {
|
|
63
|
+
|
|
64
|
+
bool aboveSurfaceNormal = dot( direction, surfaceNormal ) > 0.0;
|
|
65
|
+
bool aboveGeometryNormal = dot( direction, geometryNormal ) > 0.0;
|
|
66
|
+
return aboveSurfaceNormal == aboveGeometryNormal;
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ray sampling x and z are swapped to align with expected background view
|
|
71
|
+
vec2 equirectDirectionToUv( vec3 direction ) {
|
|
72
|
+
|
|
73
|
+
// from Spherical.setFromCartesianCoords
|
|
74
|
+
vec2 uv = vec2( atan( direction.z, direction.x ), acos( direction.y ) );
|
|
75
|
+
uv /= vec2( 2.0 * PI, PI );
|
|
76
|
+
|
|
77
|
+
// apply adjustments to get values in range [0, 1] and y right side up
|
|
78
|
+
uv.x += 0.5;
|
|
79
|
+
uv.y = 1.0 - uv.y;
|
|
80
|
+
return uv;
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
vec3 equirectUvToDirection( vec2 uv ) {
|
|
85
|
+
|
|
86
|
+
// undo above adjustments
|
|
87
|
+
uv.x -= 0.5;
|
|
88
|
+
uv.y = 1.0 - uv.y;
|
|
89
|
+
|
|
90
|
+
// from Vector3.setFromSphericalCoords
|
|
91
|
+
float theta = uv.x * 2.0 * PI;
|
|
92
|
+
float phi = uv.y * PI;
|
|
93
|
+
|
|
94
|
+
float sinPhi = sin( phi );
|
|
95
|
+
|
|
96
|
+
return vec3( sinPhi * cos( theta ), cos( phi ), sinPhi * sin( theta ) );
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// power heuristic for multiple importance sampling
|
|
101
|
+
float misHeuristic( float a, float b ) {
|
|
102
|
+
|
|
103
|
+
float aa = a * a;
|
|
104
|
+
float bb = b * b;
|
|
105
|
+
return aa / ( aa + bb );
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
|
|
110
|
+
// erichlof/THREE.js-PathTracing-Renderer/
|
|
111
|
+
float tentFilter( float x ) {
|
|
112
|
+
|
|
113
|
+
return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
@@ -105,7 +105,7 @@ function generateSobolSampleFunctions( dim = 1 ) {
|
|
|
105
105
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
export const
|
|
108
|
+
export const sobolCommonGLSL = /* glsl */`
|
|
109
109
|
|
|
110
110
|
// Utils
|
|
111
111
|
const float SOBOL_FACTOR = 1.0 / 16777216.0;
|
|
@@ -130,7 +130,7 @@ export const shaderSobolCommon = /* glsl */`
|
|
|
130
130
|
|
|
131
131
|
`;
|
|
132
132
|
|
|
133
|
-
export const
|
|
133
|
+
export const sobolGenerationGLSL = /* glsl */`
|
|
134
134
|
|
|
135
135
|
const uint SOBOL_DIRECTIONS_1[ 32 ] = uint[ 32 ](
|
|
136
136
|
0x80000000u, 0xc0000000u, 0xa0000000u, 0xf0000000u,
|
|
@@ -210,7 +210,7 @@ export const shaderSobolGeneration = /* glsl */`
|
|
|
210
210
|
|
|
211
211
|
`;
|
|
212
212
|
|
|
213
|
-
export const
|
|
213
|
+
export const sobolSamplingGLSL = /* glsl */`
|
|
214
214
|
|
|
215
215
|
// Seeds
|
|
216
216
|
uniform sampler2D sobolTexture;
|