@plasius/gpu-renderer 0.2.2 → 0.2.3
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/CHANGELOG.md +12 -5
- package/README.md +7 -3
- package/dist/index.cjs +143 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +143 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.d.ts +5 -0
- package/src/wavefront-compute.js +150 -8
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -315,6 +315,7 @@ export interface WavefrontPathTracingComputeConfig {
|
|
|
315
315
|
readonly maxDepth: number;
|
|
316
316
|
readonly tileSize: number;
|
|
317
317
|
readonly samplesPerPixel: number;
|
|
318
|
+
readonly maxFramePassesPerSubmission: number;
|
|
318
319
|
readonly tilePixelCapacity: number;
|
|
319
320
|
readonly sceneObjects: readonly WavefrontSceneObject[];
|
|
320
321
|
readonly sceneObjectCount: number;
|
|
@@ -413,6 +414,7 @@ export interface CreateWavefrontPathTracingComputeRendererOptions {
|
|
|
413
414
|
readonly maxDepth?: number;
|
|
414
415
|
readonly tileSize?: number;
|
|
415
416
|
readonly samplesPerPixel?: number;
|
|
417
|
+
readonly maxFramePassesPerSubmission?: number;
|
|
416
418
|
readonly tilePixelCapacity?: number;
|
|
417
419
|
readonly sceneObjectCapacity?: number;
|
|
418
420
|
readonly sceneObjects?: readonly WavefrontSceneObjectInput[];
|
|
@@ -458,6 +460,7 @@ export interface WavefrontPathTracingComputeRenderer {
|
|
|
458
460
|
}>
|
|
459
461
|
>;
|
|
460
462
|
updateSceneObjects(sceneObjects: readonly WavefrontSceneObjectInput[]): WavefrontPathTracingComputeConfig;
|
|
463
|
+
updateCamera(camera: WavefrontCameraOptions): WavefrontPathTracingComputeConfig;
|
|
461
464
|
getSnapshot(): Readonly<{
|
|
462
465
|
frame: number;
|
|
463
466
|
width: number;
|
|
@@ -466,6 +469,7 @@ export interface WavefrontPathTracingComputeRenderer {
|
|
|
466
469
|
tiles: number;
|
|
467
470
|
tileSize: number;
|
|
468
471
|
samplesPerPixel: number;
|
|
472
|
+
maxFramePassesPerSubmission: number;
|
|
469
473
|
sceneObjectCount: number;
|
|
470
474
|
triangleCount: number;
|
|
471
475
|
emissiveTriangleCount: number;
|
|
@@ -491,6 +495,7 @@ export interface WavefrontPathTracingComputeFrameStats {
|
|
|
491
495
|
readonly tiles: number;
|
|
492
496
|
readonly tileSize: number;
|
|
493
497
|
readonly samplesPerPixel: number;
|
|
498
|
+
readonly maxFramePassesPerSubmission: number;
|
|
494
499
|
readonly screenRays: number;
|
|
495
500
|
readonly primaryRays: number;
|
|
496
501
|
readonly sceneObjectCount: number;
|
package/src/wavefront-compute.js
CHANGED
|
@@ -3,6 +3,7 @@ const DEFAULT_HEIGHT = 720;
|
|
|
3
3
|
const DEFAULT_MAX_DEPTH = 6;
|
|
4
4
|
const DEFAULT_TILE_SIZE = 128;
|
|
5
5
|
const DEFAULT_SAMPLES_PER_PIXEL = 1;
|
|
6
|
+
const DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION = 256;
|
|
6
7
|
const DEFAULT_SCENE_OBJECT_CAPACITY = 128;
|
|
7
8
|
const DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
|
|
8
9
|
const WORKGROUP_SIZE = 64;
|
|
@@ -1193,6 +1194,15 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1193
1194
|
1,
|
|
1194
1195
|
64
|
|
1195
1196
|
);
|
|
1197
|
+
const maxFramePassesPerSubmission = clamp(
|
|
1198
|
+
readPositiveInteger(
|
|
1199
|
+
"maxFramePassesPerSubmission",
|
|
1200
|
+
options.maxFramePassesPerSubmission,
|
|
1201
|
+
DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION
|
|
1202
|
+
),
|
|
1203
|
+
1,
|
|
1204
|
+
4096
|
|
1205
|
+
);
|
|
1196
1206
|
const tilePixelCapacity = readPositiveInteger(
|
|
1197
1207
|
"tilePixelCapacity",
|
|
1198
1208
|
options.tilePixelCapacity,
|
|
@@ -1284,6 +1294,7 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1284
1294
|
maxDepth,
|
|
1285
1295
|
tileSize,
|
|
1286
1296
|
samplesPerPixel,
|
|
1297
|
+
maxFramePassesPerSubmission,
|
|
1287
1298
|
tilePixelCapacity,
|
|
1288
1299
|
sceneObjects,
|
|
1289
1300
|
sceneObjectCount: sceneObjects.length,
|
|
@@ -2209,6 +2220,80 @@ fn terminal_surface_environment_contribution(ray: RayRecord, hit: HitRecord) ->
|
|
|
2209
2220
|
return clamp_sample_radiance(ray.throughput.xyz * surfaceColor * environmentFloor * materialFloor);
|
|
2210
2221
|
}
|
|
2211
2222
|
|
|
2223
|
+
fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
|
|
2224
|
+
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
2225
|
+
return vec3<f32>(0.0);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
var irradiance = vec3<f32>(0.0);
|
|
2229
|
+
for (var portalIndex = 0u; portalIndex < config.environmentPortalCount; portalIndex = portalIndex + 1u) {
|
|
2230
|
+
let portal = environmentPortals[portalIndex];
|
|
2231
|
+
if (portal.kind != 1u) {
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
let toPortal = portal.position.xyz - origin;
|
|
2236
|
+
let distanceSquared = max(dot(toPortal, toPortal), 0.01);
|
|
2237
|
+
let direction = safe_normalize(toPortal, normal);
|
|
2238
|
+
let surfaceFacing = saturate(dot(normal, direction));
|
|
2239
|
+
if (surfaceFacing <= 0.0001) {
|
|
2240
|
+
continue;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
let portalNormal = safe_normalize(portal.normal.xyz, vec3<f32>(0.0, 0.0, 1.0));
|
|
2244
|
+
let twoSided = (portal.flags & 1u) != 0u;
|
|
2245
|
+
let portalFacing = select(
|
|
2246
|
+
saturate(dot(-direction, portalNormal)),
|
|
2247
|
+
max(abs(dot(direction, portalNormal)), 0.15),
|
|
2248
|
+
twoSided
|
|
2249
|
+
);
|
|
2250
|
+
let area = max(portal.position.w, 0.0001);
|
|
2251
|
+
let distanceFalloff = clamp(area / max(distanceSquared, area * 0.25), 0.0, 2.5);
|
|
2252
|
+
irradiance = irradiance +
|
|
2253
|
+
portal.color.rgb *
|
|
2254
|
+
portal.normal.w *
|
|
2255
|
+
portal.color.a *
|
|
2256
|
+
surfaceFacing *
|
|
2257
|
+
portalFacing *
|
|
2258
|
+
distanceFalloff;
|
|
2259
|
+
}
|
|
2260
|
+
return irradiance;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
|
|
2264
|
+
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
2265
|
+
let origin = hit.position.xyz + normal * 0.003;
|
|
2266
|
+
let viewDirection = safe_normalize(-ray.direction.xyz, normal);
|
|
2267
|
+
let surfaceColor = clamp(max(hit.color.xyz, config.ambientColor.xyz * 0.35), vec3<f32>(0.0), vec3<f32>(1.0));
|
|
2268
|
+
let roughness = clamp(hit.material.x, 0.0, 1.0);
|
|
2269
|
+
let metallic = clamp(hit.material.y, 0.0, 1.0);
|
|
2270
|
+
|
|
2271
|
+
let normalEnvironment = gated_environment_radiance(origin, normal);
|
|
2272
|
+
let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
|
|
2273
|
+
let skyIrradiance = max(config.ambientColor.xyz * 0.72, normalEnvironment * skyVisibility * 0.16);
|
|
2274
|
+
|
|
2275
|
+
let sunDirection = safe_normalize(
|
|
2276
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2277
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2278
|
+
);
|
|
2279
|
+
let sunFacing = saturate(dot(normal, sunDirection));
|
|
2280
|
+
let sunRadiance = gated_environment_radiance(origin, sunDirection);
|
|
2281
|
+
let sunIrradiance = sunRadiance * sunFacing * 0.2;
|
|
2282
|
+
let portalIrradiance = direct_environment_portal_irradiance(origin, normal);
|
|
2283
|
+
|
|
2284
|
+
let diffuseWeight = select(1.0 - metallic * 0.65, 0.22, hit.materialKind == 1u);
|
|
2285
|
+
let diffuse = surfaceColor * (skyIrradiance + sunIrradiance + portalIrradiance) * diffuseWeight;
|
|
2286
|
+
|
|
2287
|
+
let halfVector = safe_normalize(sunDirection + viewDirection, normal);
|
|
2288
|
+
let specularPower = 8.0 + (1.0 - roughness) * 96.0;
|
|
2289
|
+
let specularFacing = pow(saturate(dot(normal, halfVector)), specularPower) * sunFacing;
|
|
2290
|
+
let specularTint = mix(vec3<f32>(0.04), surfaceColor, metallic);
|
|
2291
|
+
let specular = specularTint * sunRadiance * specularFacing * select(0.16, 0.48, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2292
|
+
|
|
2293
|
+
let bounceWeight = select(1.0, 0.38, ray.bounce > 0u);
|
|
2294
|
+
return clamp_sample_radiance(ray.throughput.xyz * (diffuse + specular) * bounceWeight);
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2212
2297
|
fn default_mesh_range() -> MeshRange {
|
|
2213
2298
|
return MeshRange(
|
|
2214
2299
|
0u,
|
|
@@ -3078,6 +3163,14 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
3078
3163
|
return;
|
|
3079
3164
|
}
|
|
3080
3165
|
|
|
3166
|
+
let shouldEstimateDirectEnvironment =
|
|
3167
|
+
(hit.materialKind == 0u || hit.materialKind == 1u) && hit.material.z >= 0.95;
|
|
3168
|
+
if (shouldEstimateDirectEnvironment) {
|
|
3169
|
+
let directEnvironment = surface_direct_environment_contribution(ray, hit);
|
|
3170
|
+
accumulation[ray.rayId] =
|
|
3171
|
+
accumulation[ray.rayId] + vec4<f32>(directEnvironment * sample_weight(), 0.0);
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3081
3174
|
if (ray.bounce + 1u >= config.maxDepth) {
|
|
3082
3175
|
let terminalEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
3083
3176
|
accumulation[ray.rayId] =
|
|
@@ -3787,6 +3880,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3787
3880
|
let frame = 0;
|
|
3788
3881
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3789
3882
|
let accelerationBuildCount = 0;
|
|
3883
|
+
let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
|
|
3790
3884
|
|
|
3791
3885
|
function createFrameConfigWriter(frameIndex) {
|
|
3792
3886
|
let slot = 0;
|
|
@@ -3957,33 +4051,59 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3957
4051
|
|
|
3958
4052
|
function dispatchFrame(frameIndex) {
|
|
3959
4053
|
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
3960
|
-
|
|
3961
|
-
|
|
4054
|
+
let submissionCount = 0;
|
|
4055
|
+
let encodedFramePasses = 0;
|
|
4056
|
+
let encoder = device.createCommandEncoder({
|
|
4057
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`,
|
|
3962
4058
|
});
|
|
4059
|
+
|
|
4060
|
+
function submitCurrentEncoder() {
|
|
4061
|
+
if (encodedFramePasses <= 0) {
|
|
4062
|
+
return;
|
|
4063
|
+
}
|
|
4064
|
+
device.queue.submit([encoder.finish()]);
|
|
4065
|
+
submissionCount += 1;
|
|
4066
|
+
encodedFramePasses = 0;
|
|
4067
|
+
encoder = device.createCommandEncoder({
|
|
4068
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`,
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
function reserveEncoder(passCount = 1) {
|
|
4073
|
+
if (
|
|
4074
|
+
encodedFramePasses > 0 &&
|
|
4075
|
+
encodedFramePasses + passCount > config.maxFramePassesPerSubmission
|
|
4076
|
+
) {
|
|
4077
|
+
submitCurrentEncoder();
|
|
4078
|
+
}
|
|
4079
|
+
encodedFramePasses += passCount;
|
|
4080
|
+
return encoder;
|
|
4081
|
+
}
|
|
4082
|
+
|
|
3963
4083
|
for (const tile of tiles) {
|
|
3964
4084
|
for (let sampleIndex = 0; sampleIndex < config.samplesPerPixel; sampleIndex += 1) {
|
|
3965
4085
|
const configOffset = writeFrameConfig(tile, {
|
|
3966
4086
|
sampleIndex,
|
|
3967
4087
|
sampleWeight: 1 / config.samplesPerPixel,
|
|
3968
4088
|
});
|
|
3969
|
-
encodeTileSample(
|
|
4089
|
+
encodeTileSample(reserveEncoder(), tile, configOffset);
|
|
3970
4090
|
}
|
|
3971
4091
|
const outputConfigOffset = writeFrameConfig(tile, {
|
|
3972
4092
|
sampleIndex: 0,
|
|
3973
4093
|
sampleWeight: 1 / config.samplesPerPixel,
|
|
3974
4094
|
});
|
|
3975
|
-
encodeTileOutput(
|
|
4095
|
+
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset);
|
|
3976
4096
|
}
|
|
3977
4097
|
if (config.denoise) {
|
|
3978
4098
|
const denoiseConfigOffset = writeFrameConfig(
|
|
3979
4099
|
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3980
4100
|
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3981
4101
|
);
|
|
3982
|
-
encodeDenoise(
|
|
4102
|
+
encodeDenoise(reserveEncoder(), denoiseConfigOffset);
|
|
3983
4103
|
}
|
|
3984
|
-
encodePresent(
|
|
3985
|
-
|
|
3986
|
-
return
|
|
4104
|
+
encodePresent(reserveEncoder());
|
|
4105
|
+
submitCurrentEncoder();
|
|
4106
|
+
return submissionCount;
|
|
3987
4107
|
}
|
|
3988
4108
|
|
|
3989
4109
|
function renderOnce() {
|
|
@@ -3999,6 +4119,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3999
4119
|
tiles: tiles.length,
|
|
4000
4120
|
tileSize: config.tileSize,
|
|
4001
4121
|
samplesPerPixel: config.samplesPerPixel,
|
|
4122
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
4002
4123
|
screenRays: config.width * config.height,
|
|
4003
4124
|
primaryRays: config.width * config.height * config.samplesPerPixel,
|
|
4004
4125
|
sceneObjectCount: config.sceneObjectCount,
|
|
@@ -4091,12 +4212,31 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4091
4212
|
samplesPerPixel: config.samplesPerPixel,
|
|
4092
4213
|
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
4093
4214
|
sceneObjects: packedScene.objects,
|
|
4215
|
+
camera: activeCameraOptions,
|
|
4094
4216
|
frameIndex: config.frameIndex,
|
|
4095
4217
|
});
|
|
4096
4218
|
device.queue.writeBuffer(sceneObjectBuffer, 0, packedScene.buffer);
|
|
4097
4219
|
return config;
|
|
4098
4220
|
}
|
|
4099
4221
|
|
|
4222
|
+
function updateCamera(cameraOptions = {}) {
|
|
4223
|
+
activeCameraOptions = cameraOptions;
|
|
4224
|
+
config = createWavefrontPathTracingComputeConfig({
|
|
4225
|
+
...options,
|
|
4226
|
+
canvas,
|
|
4227
|
+
width: config.width,
|
|
4228
|
+
height: config.height,
|
|
4229
|
+
maxDepth: config.maxDepth,
|
|
4230
|
+
tileSize: config.tileSize,
|
|
4231
|
+
samplesPerPixel: config.samplesPerPixel,
|
|
4232
|
+
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
4233
|
+
sceneObjects: packedScene.objects,
|
|
4234
|
+
camera: activeCameraOptions,
|
|
4235
|
+
frameIndex: config.frameIndex,
|
|
4236
|
+
});
|
|
4237
|
+
return config;
|
|
4238
|
+
}
|
|
4239
|
+
|
|
4100
4240
|
function getSnapshot() {
|
|
4101
4241
|
return Object.freeze({
|
|
4102
4242
|
frame,
|
|
@@ -4106,6 +4246,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4106
4246
|
tiles: tiles.length,
|
|
4107
4247
|
tileSize: config.tileSize,
|
|
4108
4248
|
samplesPerPixel: config.samplesPerPixel,
|
|
4249
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
4109
4250
|
sceneObjectCount: config.sceneObjectCount,
|
|
4110
4251
|
triangleCount: config.triangleCount,
|
|
4111
4252
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
@@ -4155,6 +4296,7 @@ export async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
4155
4296
|
renderFrame,
|
|
4156
4297
|
readOutputProbe,
|
|
4157
4298
|
updateSceneObjects,
|
|
4299
|
+
updateCamera,
|
|
4158
4300
|
getSnapshot,
|
|
4159
4301
|
destroy,
|
|
4160
4302
|
});
|