@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/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var DEFAULT_HEIGHT = 720;
|
|
|
4
4
|
var DEFAULT_MAX_DEPTH = 6;
|
|
5
5
|
var DEFAULT_TILE_SIZE = 128;
|
|
6
6
|
var DEFAULT_SAMPLES_PER_PIXEL = 1;
|
|
7
|
+
var DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION = 256;
|
|
7
8
|
var DEFAULT_SCENE_OBJECT_CAPACITY = 128;
|
|
8
9
|
var DEFAULT_ENVIRONMENT_PORTAL_CAPACITY = 32;
|
|
9
10
|
var WORKGROUP_SIZE = 64;
|
|
@@ -1034,6 +1035,15 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1034
1035
|
1,
|
|
1035
1036
|
64
|
|
1036
1037
|
);
|
|
1038
|
+
const maxFramePassesPerSubmission = clamp(
|
|
1039
|
+
readPositiveInteger(
|
|
1040
|
+
"maxFramePassesPerSubmission",
|
|
1041
|
+
options.maxFramePassesPerSubmission,
|
|
1042
|
+
DEFAULT_MAX_FRAME_PASSES_PER_SUBMISSION
|
|
1043
|
+
),
|
|
1044
|
+
1,
|
|
1045
|
+
4096
|
|
1046
|
+
);
|
|
1037
1047
|
const tilePixelCapacity = readPositiveInteger(
|
|
1038
1048
|
"tilePixelCapacity",
|
|
1039
1049
|
options.tilePixelCapacity,
|
|
@@ -1101,6 +1111,7 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
1101
1111
|
maxDepth,
|
|
1102
1112
|
tileSize,
|
|
1103
1113
|
samplesPerPixel,
|
|
1114
|
+
maxFramePassesPerSubmission,
|
|
1104
1115
|
tilePixelCapacity,
|
|
1105
1116
|
sceneObjects,
|
|
1106
1117
|
sceneObjectCount: sceneObjects.length,
|
|
@@ -1967,6 +1978,80 @@ fn terminal_surface_environment_contribution(ray: RayRecord, hit: HitRecord) ->
|
|
|
1967
1978
|
return clamp_sample_radiance(ray.throughput.xyz * surfaceColor * environmentFloor * materialFloor);
|
|
1968
1979
|
}
|
|
1969
1980
|
|
|
1981
|
+
fn direct_environment_portal_irradiance(origin: vec3<f32>, normal: vec3<f32>) -> vec3<f32> {
|
|
1982
|
+
if (config.environmentPortalCount == 0u || config.environmentPortalMode == 0u) {
|
|
1983
|
+
return vec3<f32>(0.0);
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
var irradiance = vec3<f32>(0.0);
|
|
1987
|
+
for (var portalIndex = 0u; portalIndex < config.environmentPortalCount; portalIndex = portalIndex + 1u) {
|
|
1988
|
+
let portal = environmentPortals[portalIndex];
|
|
1989
|
+
if (portal.kind != 1u) {
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
let toPortal = portal.position.xyz - origin;
|
|
1994
|
+
let distanceSquared = max(dot(toPortal, toPortal), 0.01);
|
|
1995
|
+
let direction = safe_normalize(toPortal, normal);
|
|
1996
|
+
let surfaceFacing = saturate(dot(normal, direction));
|
|
1997
|
+
if (surfaceFacing <= 0.0001) {
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
let portalNormal = safe_normalize(portal.normal.xyz, vec3<f32>(0.0, 0.0, 1.0));
|
|
2002
|
+
let twoSided = (portal.flags & 1u) != 0u;
|
|
2003
|
+
let portalFacing = select(
|
|
2004
|
+
saturate(dot(-direction, portalNormal)),
|
|
2005
|
+
max(abs(dot(direction, portalNormal)), 0.15),
|
|
2006
|
+
twoSided
|
|
2007
|
+
);
|
|
2008
|
+
let area = max(portal.position.w, 0.0001);
|
|
2009
|
+
let distanceFalloff = clamp(area / max(distanceSquared, area * 0.25), 0.0, 2.5);
|
|
2010
|
+
irradiance = irradiance +
|
|
2011
|
+
portal.color.rgb *
|
|
2012
|
+
portal.normal.w *
|
|
2013
|
+
portal.color.a *
|
|
2014
|
+
surfaceFacing *
|
|
2015
|
+
portalFacing *
|
|
2016
|
+
distanceFalloff;
|
|
2017
|
+
}
|
|
2018
|
+
return irradiance;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
fn surface_direct_environment_contribution(ray: RayRecord, hit: HitRecord) -> vec3<f32> {
|
|
2022
|
+
let normal = safe_normalize(hit.shadingNormal.xyz, vec3<f32>(0.0, 1.0, 0.0));
|
|
2023
|
+
let origin = hit.position.xyz + normal * 0.003;
|
|
2024
|
+
let viewDirection = safe_normalize(-ray.direction.xyz, normal);
|
|
2025
|
+
let surfaceColor = clamp(max(hit.color.xyz, config.ambientColor.xyz * 0.35), vec3<f32>(0.0), vec3<f32>(1.0));
|
|
2026
|
+
let roughness = clamp(hit.material.x, 0.0, 1.0);
|
|
2027
|
+
let metallic = clamp(hit.material.y, 0.0, 1.0);
|
|
2028
|
+
|
|
2029
|
+
let normalEnvironment = gated_environment_radiance(origin, normal);
|
|
2030
|
+
let skyVisibility = 0.35 + saturate(normal.y * 0.5 + 0.5) * 0.45;
|
|
2031
|
+
let skyIrradiance = max(config.ambientColor.xyz * 0.72, normalEnvironment * skyVisibility * 0.16);
|
|
2032
|
+
|
|
2033
|
+
let sunDirection = safe_normalize(
|
|
2034
|
+
config.environmentSunDirectionIntensity.xyz,
|
|
2035
|
+
vec3<f32>(0.0, 1.0, 0.0)
|
|
2036
|
+
);
|
|
2037
|
+
let sunFacing = saturate(dot(normal, sunDirection));
|
|
2038
|
+
let sunRadiance = gated_environment_radiance(origin, sunDirection);
|
|
2039
|
+
let sunIrradiance = sunRadiance * sunFacing * 0.2;
|
|
2040
|
+
let portalIrradiance = direct_environment_portal_irradiance(origin, normal);
|
|
2041
|
+
|
|
2042
|
+
let diffuseWeight = select(1.0 - metallic * 0.65, 0.22, hit.materialKind == 1u);
|
|
2043
|
+
let diffuse = surfaceColor * (skyIrradiance + sunIrradiance + portalIrradiance) * diffuseWeight;
|
|
2044
|
+
|
|
2045
|
+
let halfVector = safe_normalize(sunDirection + viewDirection, normal);
|
|
2046
|
+
let specularPower = 8.0 + (1.0 - roughness) * 96.0;
|
|
2047
|
+
let specularFacing = pow(saturate(dot(normal, halfVector)), specularPower) * sunFacing;
|
|
2048
|
+
let specularTint = mix(vec3<f32>(0.04), surfaceColor, metallic);
|
|
2049
|
+
let specular = specularTint * sunRadiance * specularFacing * select(0.16, 0.48, hit.materialKind == 1u || hit.materialKind == 2u);
|
|
2050
|
+
|
|
2051
|
+
let bounceWeight = select(1.0, 0.38, ray.bounce > 0u);
|
|
2052
|
+
return clamp_sample_radiance(ray.throughput.xyz * (diffuse + specular) * bounceWeight);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
1970
2055
|
fn default_mesh_range() -> MeshRange {
|
|
1971
2056
|
return MeshRange(
|
|
1972
2057
|
0u,
|
|
@@ -2836,6 +2921,14 @@ fn resolveSurfaceRecords(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
|
2836
2921
|
return;
|
|
2837
2922
|
}
|
|
2838
2923
|
|
|
2924
|
+
let shouldEstimateDirectEnvironment =
|
|
2925
|
+
(hit.materialKind == 0u || hit.materialKind == 1u) && hit.material.z >= 0.95;
|
|
2926
|
+
if (shouldEstimateDirectEnvironment) {
|
|
2927
|
+
let directEnvironment = surface_direct_environment_contribution(ray, hit);
|
|
2928
|
+
accumulation[ray.rayId] =
|
|
2929
|
+
accumulation[ray.rayId] + vec4<f32>(directEnvironment * sample_weight(), 0.0);
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2839
2932
|
if (ray.bounce + 1u >= config.maxDepth) {
|
|
2840
2933
|
let terminalEnvironment = terminal_surface_environment_contribution(ray, hit);
|
|
2841
2934
|
accumulation[ray.rayId] =
|
|
@@ -3511,6 +3604,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3511
3604
|
let frame = 0;
|
|
3512
3605
|
let accelerationBuilt = !config.gpuAccelerationBuildRequired;
|
|
3513
3606
|
let accelerationBuildCount = 0;
|
|
3607
|
+
let activeCameraOptions = options.camera ?? DEFAULT_CAMERA;
|
|
3514
3608
|
function createFrameConfigWriter(frameIndex) {
|
|
3515
3609
|
let slot = 0;
|
|
3516
3610
|
return (tile, buildRange = {}) => {
|
|
@@ -3670,33 +3764,53 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3670
3764
|
}
|
|
3671
3765
|
function dispatchFrame(frameIndex) {
|
|
3672
3766
|
const writeFrameConfig = createFrameConfigWriter(frameIndex);
|
|
3673
|
-
|
|
3674
|
-
|
|
3767
|
+
let submissionCount = 0;
|
|
3768
|
+
let encodedFramePasses = 0;
|
|
3769
|
+
let encoder = device.createCommandEncoder({
|
|
3770
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`
|
|
3675
3771
|
});
|
|
3772
|
+
function submitCurrentEncoder() {
|
|
3773
|
+
if (encodedFramePasses <= 0) {
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
device.queue.submit([encoder.finish()]);
|
|
3777
|
+
submissionCount += 1;
|
|
3778
|
+
encodedFramePasses = 0;
|
|
3779
|
+
encoder = device.createCommandEncoder({
|
|
3780
|
+
label: `plasius.wavefront.frame.${frameIndex}.batched.${submissionCount + 1}`
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
function reserveEncoder(passCount = 1) {
|
|
3784
|
+
if (encodedFramePasses > 0 && encodedFramePasses + passCount > config.maxFramePassesPerSubmission) {
|
|
3785
|
+
submitCurrentEncoder();
|
|
3786
|
+
}
|
|
3787
|
+
encodedFramePasses += passCount;
|
|
3788
|
+
return encoder;
|
|
3789
|
+
}
|
|
3676
3790
|
for (const tile of tiles) {
|
|
3677
3791
|
for (let sampleIndex = 0; sampleIndex < config.samplesPerPixel; sampleIndex += 1) {
|
|
3678
3792
|
const configOffset = writeFrameConfig(tile, {
|
|
3679
3793
|
sampleIndex,
|
|
3680
3794
|
sampleWeight: 1 / config.samplesPerPixel
|
|
3681
3795
|
});
|
|
3682
|
-
encodeTileSample(
|
|
3796
|
+
encodeTileSample(reserveEncoder(), tile, configOffset);
|
|
3683
3797
|
}
|
|
3684
3798
|
const outputConfigOffset = writeFrameConfig(tile, {
|
|
3685
3799
|
sampleIndex: 0,
|
|
3686
3800
|
sampleWeight: 1 / config.samplesPerPixel
|
|
3687
3801
|
});
|
|
3688
|
-
encodeTileOutput(
|
|
3802
|
+
encodeTileOutput(reserveEncoder(), tile, outputConfigOffset);
|
|
3689
3803
|
}
|
|
3690
3804
|
if (config.denoise) {
|
|
3691
3805
|
const denoiseConfigOffset = writeFrameConfig(
|
|
3692
3806
|
{ x: 0, y: 0, width: config.width, height: config.height },
|
|
3693
3807
|
{ sampleIndex: 0, sampleWeight: 1 / config.samplesPerPixel }
|
|
3694
3808
|
);
|
|
3695
|
-
encodeDenoise(
|
|
3809
|
+
encodeDenoise(reserveEncoder(), denoiseConfigOffset);
|
|
3696
3810
|
}
|
|
3697
|
-
encodePresent(
|
|
3698
|
-
|
|
3699
|
-
return
|
|
3811
|
+
encodePresent(reserveEncoder());
|
|
3812
|
+
submitCurrentEncoder();
|
|
3813
|
+
return submissionCount;
|
|
3700
3814
|
}
|
|
3701
3815
|
function renderOnce() {
|
|
3702
3816
|
frame += 1;
|
|
@@ -3711,6 +3825,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3711
3825
|
tiles: tiles.length,
|
|
3712
3826
|
tileSize: config.tileSize,
|
|
3713
3827
|
samplesPerPixel: config.samplesPerPixel,
|
|
3828
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
3714
3829
|
screenRays: config.width * config.height,
|
|
3715
3830
|
primaryRays: config.width * config.height * config.samplesPerPixel,
|
|
3716
3831
|
sceneObjectCount: config.sceneObjectCount,
|
|
@@ -3797,11 +3912,29 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3797
3912
|
samplesPerPixel: config.samplesPerPixel,
|
|
3798
3913
|
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
3799
3914
|
sceneObjects: packedScene.objects,
|
|
3915
|
+
camera: activeCameraOptions,
|
|
3800
3916
|
frameIndex: config.frameIndex
|
|
3801
3917
|
});
|
|
3802
3918
|
device.queue.writeBuffer(sceneObjectBuffer, 0, packedScene.buffer);
|
|
3803
3919
|
return config;
|
|
3804
3920
|
}
|
|
3921
|
+
function updateCamera(cameraOptions = {}) {
|
|
3922
|
+
activeCameraOptions = cameraOptions;
|
|
3923
|
+
config = createWavefrontPathTracingComputeConfig({
|
|
3924
|
+
...options,
|
|
3925
|
+
canvas,
|
|
3926
|
+
width: config.width,
|
|
3927
|
+
height: config.height,
|
|
3928
|
+
maxDepth: config.maxDepth,
|
|
3929
|
+
tileSize: config.tileSize,
|
|
3930
|
+
samplesPerPixel: config.samplesPerPixel,
|
|
3931
|
+
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
3932
|
+
sceneObjects: packedScene.objects,
|
|
3933
|
+
camera: activeCameraOptions,
|
|
3934
|
+
frameIndex: config.frameIndex
|
|
3935
|
+
});
|
|
3936
|
+
return config;
|
|
3937
|
+
}
|
|
3805
3938
|
function getSnapshot() {
|
|
3806
3939
|
return Object.freeze({
|
|
3807
3940
|
frame,
|
|
@@ -3811,6 +3944,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3811
3944
|
tiles: tiles.length,
|
|
3812
3945
|
tileSize: config.tileSize,
|
|
3813
3946
|
samplesPerPixel: config.samplesPerPixel,
|
|
3947
|
+
maxFramePassesPerSubmission: config.maxFramePassesPerSubmission,
|
|
3814
3948
|
sceneObjectCount: config.sceneObjectCount,
|
|
3815
3949
|
triangleCount: config.triangleCount,
|
|
3816
3950
|
emissiveTriangleCount: config.emissiveTriangleCount,
|
|
@@ -3858,6 +3992,7 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
|
|
|
3858
3992
|
renderFrame,
|
|
3859
3993
|
readOutputProbe,
|
|
3860
3994
|
updateSceneObjects,
|
|
3995
|
+
updateCamera,
|
|
3861
3996
|
getSnapshot,
|
|
3862
3997
|
destroy
|
|
3863
3998
|
});
|