Skip to content

Commit a16019b

Browse files
committed
Merge branch 'webgpu-pathtracer' into webgpu/mobile-2
2 parents 8a26119 + d915f7c commit a16019b

6 files changed

Lines changed: 167 additions & 31 deletions

File tree

src/webgpu/RenderTarget2DArray.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {
33
RenderTarget,
44
RGBAFormat,
55
UnsignedByteType,
6-
RepeatWrapping,
6+
ClampToEdgeWrapping,
77
LinearFilter,
88
NoToneMapping,
99
QuadMesh,
1010
NoBlending,
11+
MeshBasicMaterial,
1112
} from 'three/webgpu';
1213

1314
function getTextureHash( texture ) {
@@ -43,8 +44,8 @@ export class RenderTarget2DArray {
4344
type: UnsignedByteType,
4445
minFilter: LinearFilter,
4546
magFilter: LinearFilter,
46-
wrapS: RepeatWrapping,
47-
wrapT: RepeatWrapping,
47+
wrapS: ClampToEdgeWrapping,
48+
wrapT: ClampToEdgeWrapping,
4849
generateMipmaps: false,
4950
...options,
5051
};

src/webgpu/WebGPUPathTracer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export class WebGPUPathTracer {
136136
// Build TLAS and compute functions
137137
const bvhData = new PathtracerBVHComputeData( scene );
138138
bvhData.update();
139-
bvhData.useTransparencyRaycastFn();
139+
bvhData.useTransparencyRaycastFn( this.textureArray.texture );
140140

141141
this.textureArray.setTextures( this._renderer, bvhData.textures );
142142
this._pathTracer.setTextures( this.textureArray.texture );

src/webgpu/nodes/PathtracerBVHComputeData.js

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import { BackSide, FrontSide, DoubleSide, BufferAttribute, BufferGeometry, StorageBufferAttribute, StructTypeNode, Vector4, SkinnedMesh, StructNode } from 'three/webgpu';
1+
import { BackSide, FrontSide, DoubleSide, BufferAttribute, BufferGeometry, StorageBufferAttribute, StructTypeNode, Vector4, SkinnedMesh, StructNode, RepeatWrapping, ClampToEdgeWrapping, MirroredRepeatWrapping, NearestFilter } from 'three/webgpu';
22
import { BVHComputeData, intersectionResultStruct, intersectsTriangle } from '../lib/BVHComputeData.js';
3-
import { storage, float } from 'three/tsl';
3+
import { storage, float, sampler, texture } from 'three/tsl';
44
import { SkinnedMeshBVH, MeshBVH, SAH } from 'three-mesh-bvh';
55
import { materialStruct } from './structs.wgsl.js';
66
import { getTextureHash } from '../../core/utils/sceneUpdateUtils.js';
77
import { bvhNodeBoundsStruct, bvhNodeStruct, rayStruct } from '../lib/wgsl/structs.wgsl.js';
88
import { wgslTagFn } from '../lib/nodes/WGSLTagFnNode.js';
99
import { pcgRand } from './random.wgsl.js';
10+
import { sampleTexelFunc } from './utils.wgsl.js';
1011

1112
const _colorVec = new Vector4();
1213
const transformStruct = new StructTypeNode( {
@@ -44,7 +45,10 @@ export class PathtracerBVHComputeData extends BVHComputeData {
4445

4546
}
4647

47-
useTransparencyRaycastFn() {
48+
useTransparencyRaycastFn( textures ) {
49+
50+
const texturesNode = texture( textures );
51+
const samplerNode = sampler( textures );
4852

4953
const { prefix, storage, structs, fns } = this;
5054

@@ -125,11 +129,53 @@ export class PathtracerBVHComputeData extends BVHComputeData {
125129
if ( triResult.didHit && ( ! result.didHit || triResult.dist < result.dist ) ) {
126130
127131
let material = ${ currentMaterial };
128-
if ( material.transparent != 0 ) {
129132
130-
let opacity = ${ baseOpacityScalar };
131-
// TODO: sample albedo + alphaMap alpha
132-
if ( opacity < ${ pcgRand }() ) {
133+
// TODO: if material is a transmissive volume we may need to assume double-sidedness
134+
if ( material.side != 0 && triResult.side != material.side ) {
135+
136+
continue;
137+
138+
}
139+
140+
if ( material.transparent != 0 || material.alphaTest > 0.0 ) {
141+
142+
var opacity = ${ baseOpacityScalar };
143+
144+
// account for alpha component of albedo map
145+
if ( material.map != - 1 ) {
146+
147+
let barycoord = triResult.barycoord;
148+
let a = ${ storage.attributes }[ i0 ].uv.xy;
149+
let b = ${ storage.attributes }[ i1 ].uv.xy;
150+
let c = ${ storage.attributes }[ i2 ].uv.xy;
151+
let uv = barycoord.x * a + barycoord.y * b + barycoord.z * c;
152+
let uvPrime = material.mapTransform * vec3f( uv, 1 );
153+
154+
opacity *= ${ sampleTexelFunc }( ${ texturesNode }, ${ samplerNode }, uvPrime.xy, material.map, 0 ).a;
155+
156+
}
157+
158+
// account for green component of alpha map
159+
if ( material.alphaMap != - 1 ) {
160+
161+
let barycoord = triResult.barycoord;
162+
let a = ${ storage.attributes }[ i0 ].uv.xy;
163+
let b = ${ storage.attributes }[ i1 ].uv.xy;
164+
let c = ${ storage.attributes }[ i2 ].uv.xy;
165+
let uv = barycoord.x * a + barycoord.y * b + barycoord.z * c;
166+
let uvPrime = material.alphaMapTransform * vec3f( uv, 1 );
167+
168+
opacity *= ${ sampleTexelFunc }( ${ texturesNode }, ${ samplerNode }, uvPrime.xy, material.alphaMap, 0 ).g;
169+
170+
}
171+
172+
if ( material.transparent != 0 && opacity < ${ pcgRand }() ) {
173+
174+
continue;
175+
176+
}
177+
178+
if ( opacity < material.alphaTest ) {
133179
134180
continue;
135181
@@ -214,24 +260,46 @@ export class PathtracerBVHComputeData extends BVHComputeData {
214260

215261
writeMaterialsBuffer( materials ) {
216262

217-
function getTexture( material, key, def = - 1 ) {
263+
function encodeTextureWrap( wrap ) {
264+
265+
switch ( wrap ) {
266+
267+
case RepeatWrapping:
268+
return 0;
269+
case ClampToEdgeWrapping:
270+
return 1;
271+
case MirroredRepeatWrapping:
272+
return 2;
273+
default:
274+
return 0;
275+
276+
}
277+
278+
}
279+
280+
function getTexture( material, key ) {
218281

219282
if ( key in material && material[ key ] ) {
220283

221-
const hash = getTextureHash( material[ key ] );
284+
const texture = material[ key ];
285+
const hash = getTextureHash( texture );
222286

223287
if ( ! textureLookUp.has( hash ) ) {
224288

225289
textureLookUp.set( hash, textureLookUp.size );
226-
textures.push( material[ key ] );
290+
textures.push( texture );
227291

228292
}
229293

230-
return textureLookUp.get( hash );
294+
const idx = textureLookUp.get( hash );
295+
const wrapS = encodeTextureWrap( texture.wrapS );
296+
const wrapT = encodeTextureWrap( texture.wrapT );
297+
const nearest = texture.magFilter === NearestFilter ? 1 : 0;
298+
return ( nearest << 28 ) | ( wrapT << 26 ) | ( wrapS << 24 ) | idx;
231299

232300
} else {
233301

234-
return def;
302+
return - 1;
235303

236304
}
237305

src/webgpu/nodes/material.wgsl.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
iorToF0Func,
66
schlickFresnelFunc,
77
schlickFresnelVecFunc,
8+
sampleTexelFunc,
89
} from './utils.wgsl';
910
import {
1011
ggxSmithVisibilityFunc,
@@ -48,7 +49,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
4849
let vTBN = mat3x3f( tangent, bitangent, faceNormal );
4950
5051
let uvPrime = material.normalMapTransform * vec3( uv, 1.0 );
51-
var texNormal = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.normalMap ), 0 ).xyz;
52+
var texNormal = sampleTexel( textures, textureSampler, uvPrime.xy, material.normalMap, 0 ).xyz;
5253
texNormal = texNormal * 2.0 - 1.0;
5354
texNormal = texNormal * vec3f( material.normalScale, 1.0 );
5455
normal = normalize( vTBN * texNormal );
@@ -71,7 +72,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
7172
if ( material.map != -1 ) {
7273
7374
let uvPrime = material.mapTransform * vec3f( uv, 1 );
74-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.map ), 0 );
75+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.map, 0 );
7576
albedo *= vec4f( texColor.rgb, 1.0 );
7677
7778
}
@@ -80,7 +81,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
8081
if ( material.roughnessMap != -1 ) {
8182
8283
let uvPrime = material.roughnessMapTransform * vec3f( uv, 1 );
83-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.roughnessMap ), 0 );
84+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.roughnessMap, 0 );
8485
roughness *= texColor.g;
8586
8687
}
@@ -89,7 +90,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
8990
if ( material.metalnessMap != -1 ) {
9091
9192
let uvPrime = material.metalnessMapTransform * vec3f( uv, 1 );
92-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.metalnessMap ), 0 );
93+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.metalnessMap, 0 );
9394
metalness *= texColor.b;
9495
9596
}
@@ -98,7 +99,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
9899
if ( material.emissiveMap != -1 ) {
99100
100101
let uvPrime = material.emissiveMapTransform * vec3f( uv, 1 );
101-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.emissiveMap ), 0 );
102+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.emissiveMap, 0 );
102103
emission *= texColor.rgb;
103104
104105
}
@@ -107,7 +108,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
107108
if ( material.transmissionMap != -1 ) {
108109
109110
let uvPrime = material.transmissionMapTransform * vec3f( uv, 1 );
110-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.transmissionMap ), 0 );
111+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.transmissionMap, 0 );
111112
transmission *= texColor.r;
112113
113114
}
@@ -116,7 +117,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
116117
if ( material.clearcoatMap != -1 ) {
117118
118119
let uvPrime = material.clearcoatMapTransform * vec3f( uv, 1 );
119-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.clearcoatMap ), 0 );
120+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.clearcoatMap, 0 );
120121
clearcoat *= texColor.r;
121122
122123
}
@@ -125,7 +126,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
125126
if ( material.clearcoatRoughnessMap != -1 ) {
126127
127128
let uvPrime = material.clearcoatRoughnessMapTransform * vec3f( uv, 1 );
128-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.clearcoatRoughnessMap ), 0 );
129+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.clearcoatRoughnessMap, 0 );
129130
clearcoatRoughness *= texColor.r;
130131
131132
}
@@ -142,7 +143,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
142143
let vTBN = mat3x3f( tangent, bitangent, faceNormal );
143144
144145
let uvPrime = material.clearcoatNormalMapTransform * vec3( uv, 1.0 );
145-
var texNormal = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.clearcoatNormalMap ), 0 ).xyz;
146+
var texNormal = sampleTexel( textures, textureSampler, uvPrime.xy, material.clearcoatNormalMap, 0 ).xyz;
146147
texNormal = texNormal * 2.0 - 1.0;
147148
texNormal = texNormal * vec3f( material.clearcoatNormalScale, 1.0 );
148149
clearcoatNormal = normalize( vTBN * texNormal );
@@ -156,7 +157,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
156157
if ( material.sheenColorMap != -1 ) {
157158
158159
let uvPrime = material.sheenColorMapTransform * vec3f( uv, 1 );
159-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.sheenColorMap ), 0 );
160+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.sheenColorMap, 0 );
160161
sheenColor *= texColor.rgb;
161162
162163
}
@@ -165,7 +166,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
165166
if ( material.sheenRoughnessMap != -1 ) {
166167
167168
let uvPrime = material.sheenRoughnessMapTransform * vec3f( uv, 1 );
168-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.sheenRoughnessMap ), 0 );
169+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.sheenRoughnessMap, 0 );
169170
sheenRoughness *= texColor.r;
170171
171172
}
@@ -174,7 +175,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
174175
if ( material.iridescenceMap != -1 ) {
175176
176177
let uvPrime = material.iridescenceMapTransform * vec3f( uv, 1 );
177-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.iridescenceMap ), 0 );
178+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.iridescenceMap, 0 );
178179
iridescence *= texColor.r;
179180
180181
}
@@ -183,7 +184,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
183184
if ( material.iridescenceThicknessMap != -1 ) {
184185
185186
let uvPrime = material.iridescenceThicknessMapTransform * vec3f( uv, 1 );
186-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.iridescenceThicknessMap ), 0 );
187+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.iridescenceThicknessMap, 0 );
187188
188189
iridescenceThickness = mix(
189190
material.iridescenceThicknessMinimum,
@@ -197,7 +198,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
197198
if ( material.specularColorMap != -1 ) {
198199
199200
let uvPrime = material.specularColorMapTransform * vec3f( uv, 1 );
200-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.specularColorMap ), 0 );
201+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.specularColorMap, 0 );
201202
specularColor *= texColor.rgb;
202203
203204
}
@@ -206,7 +207,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
206207
if ( material.specularIntensityMap != -1 ) {
207208
208209
let uvPrime = material.specularIntensityMapTransform * vec3f( uv, 1 );
209-
let texColor = textureSampleLevel( textures, textureSampler, uvPrime.xy, i32( material.specularIntensityMap ), 0 );
210+
let texColor = sampleTexel( textures, textureSampler, uvPrime.xy, material.specularIntensityMap, 0 );
210211
specularIntensity *= texColor.r;
211212
212213
}
@@ -265,6 +266,7 @@ export const getSurfaceRecordFunc = wgslFn( /* wgsl */ `
265266
inverseMat3x3Func,
266267
iorToF0Func,
267268
getBasisFromNormalFunc,
269+
sampleTexelFunc,
268270
surfaceRecordStruct,
269271
] );
270272

src/webgpu/nodes/structs.wgsl.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ export const scatterRecordStruct = new StructTypeNode( {
1515
pdf: 'float',
1616
}, 'ScatterRecord' );
1717

18+
// NOTE: all "*Map" fields are bit-packed integers of texture index & settings:
19+
// bits 0-23: texture index
20+
// bits 24-25: wrapS setting
21+
// bits 26-27: wrapT setting
22+
// bit 28: filter setting
23+
// bits 29-30: unused
24+
// bit 31: negative bit used for indicating unused texture
1825
export const materialStruct = new StructTypeNode( {
1926
// offset 0
2027
color: 'vec3',

src/webgpu/nodes/utils.wgsl.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,61 @@ export const evaluateFresnelFunc = wgslFn( /* wgsl */ `
100100
101101
`, [ totalInternalReflectionFunc ] );
102102

103+
export const applyWrapFunc = wgslFn( /* wgsl */ `
104+
105+
fn applyWrap( v: f32, wrapMode: i32 ) -> f32 {
106+
107+
var res = v;
108+
if ( wrapMode == 1 ) {
109+
110+
// ClampToEdge
111+
res = clamp( res, 0.0, 1.0 );
112+
113+
} else if ( wrapMode == 2 ) {
114+
115+
// MirroredRepeat
116+
res = 1.0 - abs( 2.0 * fract( res * 0.5 ) - 1.0 );
117+
118+
} else {
119+
120+
// Repeat
121+
res = fract( res );
122+
123+
}
124+
125+
return res;
126+
127+
}
128+
129+
` );
130+
131+
export const sampleTexelFunc = wgslFn( /* wgsl */ `
132+
133+
fn sampleTexel( textures: texture_2d_array<f32>, textureSampler: sampler, uv: vec2f, packed: i32, lod: f32 ) -> vec4f {
134+
135+
let wrapS = ( packed >> 24 ) & 0x3;
136+
let wrapT = ( packed >> 26 ) & 0x3;
137+
let nearest = ( packed >> 28 ) & 0x1;
138+
let texIndex = packed & 0xFFFFFF;
139+
140+
let wrappedUv = vec2f(
141+
applyWrap( uv.x, wrapS ),
142+
applyWrap( uv.y, wrapT ),
143+
);
144+
145+
if ( nearest == 1 ) {
146+
147+
let dims = vec2f( textureDimensions( textures, 0 ).xy );
148+
let texel = vec2i( wrappedUv * dims );
149+
return textureLoad( textures, texel, texIndex, 0 );
150+
151+
} else {
152+
153+
return textureSampleLevel( textures, textureSampler, wrappedUv, texIndex, lod );
154+
155+
}
156+
157+
}
158+
159+
`, [ applyWrapFunc ] );
160+

0 commit comments

Comments
 (0)