@zephyr3d/scene 0.9.18 → 0.9.20
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/dist/animation/joint_dynamics/convex_collider.js +320 -0
- package/dist/animation/joint_dynamics/convex_collider.js.map +1 -0
- package/dist/app/scriptregistry.js +124 -6
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/assetmanager.js +1 -0
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/camera/camera.js +240 -201
- package/dist/camera/camera.js.map +1 -1
- package/dist/index.d.ts +458 -9
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/material/meshmaterial.js +2 -2
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +11 -5
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lit.js +10 -0
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/shader/helper.js +138 -10
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/skin.js +4 -6
- package/dist/material/skin.js.map +1 -1
- package/dist/material/water.js +1 -1
- package/dist/material/water.js.map +1 -1
- package/dist/posteffect/bloom.js +177 -29
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/compositor.js +111 -204
- package/dist/posteffect/compositor.js.map +1 -1
- package/dist/posteffect/motionblur.js +3 -0
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/posteffect.js +79 -0
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssr.js +238 -7
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +16 -15
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js +121 -19
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js +1 -1
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +7 -1
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/cluster_light.js +52 -8
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +331 -0
- package/dist/render/dualdepthpeeling_oit.js.map +1 -0
- package/dist/render/envlight.js +6 -3
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/hzb.js +11 -4
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/lightpass.js +21 -12
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/renderer.js +7 -3
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +79 -0
- package/dist/render/rendergraph/blackboard.js.map +1 -0
- package/dist/render/rendergraph/device_pool_allocator.js +10 -1
- package/dist/render/rendergraph/device_pool_allocator.js.map +1 -1
- package/dist/render/rendergraph/executor.js +41 -11
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +551 -201
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/rendergraph.js +29 -0
- package/dist/render/rendergraph/rendergraph.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/renderpass.js +12 -2
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/shadow_mask_pass.js +297 -0
- package/dist/render/shadow_mask_pass.js.map +1 -0
- package/dist/render/weightedblended_oit.js +5 -0
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +1 -0
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +1 -0
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +1 -0
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/msdftext.js +1 -0
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +1 -0
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +1 -0
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/scene.js +1 -0
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/terrain-cm/grass.js +1 -0
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +1 -0
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/water.js +1 -0
- package/dist/scene/water.js.map +1 -1
- package/dist/shaders/ssr.js +52 -21
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/utility/misc.js +10 -1
- package/dist/utility/misc.js.map +1 -1
- package/dist/utility/serialization/manager.js +1 -0
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +1 -0
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +1 -0
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +35 -2
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +1 -0
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +1 -0
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +1 -0
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/particle.js +1 -0
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/primitive.js +1 -0
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +1 -0
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +1 -0
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +1 -0
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +1 -0
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/values.js +11 -1
- package/dist/values.js.map +1 -1
- package/package.json +2 -2
|
@@ -4,18 +4,20 @@ import { LightPass } from '../lightpass.js';
|
|
|
4
4
|
import { ShadowMapPass } from '../shadowmap_pass.js';
|
|
5
5
|
import { DepthPass } from '../depthpass.js';
|
|
6
6
|
import { ClusteredLight } from '../cluster_light.js';
|
|
7
|
+
import { ShadowMaskRenderer } from '../shadow_mask_pass.js';
|
|
7
8
|
import { buildHiZ } from '../hzb.js';
|
|
8
9
|
import '../primitive.js';
|
|
9
10
|
import '@zephyr3d/device';
|
|
10
11
|
import { CopyBlitter } from '../../blitter/copy.js';
|
|
11
12
|
import { fetchSampler } from '../../utility/misc.js';
|
|
12
13
|
import { MaterialVaryingFlags } from '../../values.js';
|
|
13
|
-
import {
|
|
14
|
+
import { PostEffectLayer, AbstractPostEffect } from '../../posteffect/posteffect.js';
|
|
14
15
|
import { RenderGraph } from './rendergraph.js';
|
|
15
16
|
import { RenderGraphExecutor } from './executor.js';
|
|
16
17
|
import { DevicePoolAllocator } from './device_pool_allocator.js';
|
|
17
18
|
import { HistoryResourceManager } from './history_resource_manager.js';
|
|
18
19
|
import { RGHistoryResources } from './history_resources.js';
|
|
20
|
+
import { FrameResources, RGBlackboard } from './blackboard.js';
|
|
19
21
|
import { renderObjectColors } from '../gpu_picking.js';
|
|
20
22
|
import { BoxShape } from '../../shapes/box.js';
|
|
21
23
|
import '../../shapes/capsule.js';
|
|
@@ -30,6 +32,7 @@ const _scenePass = new LightPass();
|
|
|
30
32
|
const _depthPass = new DepthPass();
|
|
31
33
|
const _shadowMapPass = new ShadowMapPass();
|
|
32
34
|
const _clusters = [];
|
|
35
|
+
const _shadowMaskRenderer = new ShadowMaskRenderer();
|
|
33
36
|
const _devicePoolAllocator = new DevicePoolAllocator();
|
|
34
37
|
let _backDepthColorState = null;
|
|
35
38
|
let _frontDepthColorState = null;
|
|
@@ -173,9 +176,11 @@ function getSSSLightingTextureFormat(ctx, attachmentCount, includeSSRSurfaceMRT)
|
|
|
173
176
|
return colorFormat;
|
|
174
177
|
}
|
|
175
178
|
const caps = ctx.device.getDeviceCaps();
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
// The SSR roughness/normal MRT textures use the glossy surface format; they
|
|
180
|
+
// are graph textures now, so derive the format directly instead of reading
|
|
181
|
+
// the (not yet resolved) DrawContext fields.
|
|
182
|
+
const surfaceFormat = getSurfaceTextureFormat(ctx);
|
|
183
|
+
const colorBytes = getTextureFormatBytes(ctx, colorFormat) + getTextureFormatBytes(ctx, surfaceFormat) + getTextureFormatBytes(ctx, surfaceFormat);
|
|
179
184
|
const fullPrecisionBytes = colorBytes + getTextureFormatBytes(ctx, colorFormat) * attachmentCount;
|
|
180
185
|
if (fullPrecisionBytes <= caps.framebufferCaps.maxColorAttachmentBytesPerSample) {
|
|
181
186
|
return colorFormat;
|
|
@@ -256,6 +261,8 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
256
261
|
const backbuffer = graph.importTexture('backbuffer');
|
|
257
262
|
ctx.SSS = !!options.sss;
|
|
258
263
|
ctx.SkinSSSTexture = null;
|
|
264
|
+
// Named registry of shared frame resources (consumed by post effect setup)
|
|
265
|
+
const blackboard = new RGBlackboard();
|
|
259
266
|
// Shared mutable frame state
|
|
260
267
|
const frame = {
|
|
261
268
|
ctx,
|
|
@@ -263,7 +270,6 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
263
270
|
depthFramebuffer: null,
|
|
264
271
|
sunLightColor: null,
|
|
265
272
|
options,
|
|
266
|
-
intermediateDeviceStatePushed: false,
|
|
267
273
|
renderQueueDisposed: false,
|
|
268
274
|
clusteredLightReleased: false,
|
|
269
275
|
sunLightRestored: false
|
|
@@ -372,21 +378,99 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
372
378
|
depthFramebufferHandle
|
|
373
379
|
};
|
|
374
380
|
});
|
|
375
|
-
|
|
381
|
+
let depthHandle = depthPassResult.depthHandle;
|
|
376
382
|
const motionVectorHandle = depthPassResult.motionVectorHandle;
|
|
383
|
+
blackboard.set(FrameResources.LinearDepth, depthHandle);
|
|
384
|
+
if (motionVectorHandle) {
|
|
385
|
+
blackboard.set(FrameResources.MotionVector, motionVectorHandle);
|
|
386
|
+
}
|
|
387
|
+
if (depthPassResult.graphDepthAttachmentHandle) {
|
|
388
|
+
blackboard.set(FrameResources.SceneDepthAttachment, depthPassResult.graphDepthAttachmentHandle);
|
|
389
|
+
}
|
|
390
|
+
// ── Screen-space shadow mask ────────────────────────────────────────────
|
|
391
|
+
// When active, render each shadow-casting light's visibility into an RGBA8
|
|
392
|
+
// texture array (4 lights per layer). The clustered LightPass samples this
|
|
393
|
+
// mask instead of each shadowed light running its own additive pass. The
|
|
394
|
+
// layer/channel order is locked to ClusteredLight.getVisibleLights: shadow
|
|
395
|
+
// lights fill clustered buffer indices 1..N in renderQueue.shadowedLights
|
|
396
|
+
// order, and ordinal s = index-1 maps to layer s>>2, channel s&3.
|
|
397
|
+
let shadowMaskHandle = null;
|
|
398
|
+
// Reset any mask carried over from a previous frame; the pass below re-sets it
|
|
399
|
+
// during execution only when the mask is actually produced.
|
|
400
|
+
ctx.shadowMaskTexture = null;
|
|
401
|
+
// Gate on build-time state only: renderQueue.shadowedLights is available now,
|
|
402
|
+
// whereas ctx.shadowMapInfo is populated later by the ShadowMaps pass execute
|
|
403
|
+
// (which runs before this pass thanks to the orderToken chain), so it must not
|
|
404
|
+
// be part of the pass-creation condition.
|
|
405
|
+
const useShadowMask = ctx.screenSpaceShadowMask && renderQueue.shadowedLights.length > 0;
|
|
406
|
+
if (useShadowMask) {
|
|
407
|
+
const numShadowLights = renderQueue.shadowedLights.length;
|
|
408
|
+
const numLayers = ShadowMaskRenderer.getLayerCount(numShadowLights);
|
|
409
|
+
const maskPassResult = graph.addPass('ShadowMaskPass', (builder)=>{
|
|
410
|
+
builder.read(depthHandle);
|
|
411
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
412
|
+
// createTexture already registers this pass as the resource producer, so
|
|
413
|
+
// downstream passes read this handle directly (same pattern as HiZ). The
|
|
414
|
+
// per-layer framebuffers are created inside execute (rgCtx.createFramebuffer),
|
|
415
|
+
// which the graph cannot see; the createTexture producer edge is what keeps
|
|
416
|
+
// the resource alive and ordered before the LightPass reader.
|
|
417
|
+
const maskHandle = builder.createTexture({
|
|
418
|
+
format: 'rgba8unorm',
|
|
419
|
+
label: 'shadowMask',
|
|
420
|
+
arrayLayers: numLayers
|
|
421
|
+
});
|
|
422
|
+
builder.setExecute((rgCtx)=>{
|
|
423
|
+
const depthTex = rgCtx.getTexture(depthHandle);
|
|
424
|
+
const maskTex = rgCtx.getTexture(maskHandle);
|
|
425
|
+
_shadowMaskRenderer.render(ctx, depthTex, renderQueue.shadowedLights, (layer)=>rgCtx.createFramebuffer({
|
|
426
|
+
width: maskTex.width,
|
|
427
|
+
height: maskTex.height,
|
|
428
|
+
colorAttachments: maskTex,
|
|
429
|
+
depthAttachment: null,
|
|
430
|
+
attachmentLayer: layer
|
|
431
|
+
}));
|
|
432
|
+
ctx.shadowMaskTexture = maskTex;
|
|
433
|
+
});
|
|
434
|
+
return {
|
|
435
|
+
maskHandle
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
shadowMaskHandle = maskPassResult.maskHandle;
|
|
439
|
+
blackboard.set(FrameResources.ShadowMask, shadowMaskHandle);
|
|
440
|
+
}
|
|
377
441
|
const renderDepthAttachment = depthPassResult.graphDepthAttachmentHandle ?? depthPassResult.externalDepthAttachment ?? null;
|
|
378
|
-
|
|
442
|
+
// Rendering the scene directly into the final framebuffer is only possible
|
|
443
|
+
// when no opaque-layer effect is enabled: those effects must sample the
|
|
444
|
+
// opaque scene color as a texture and may require surface MRT attachments
|
|
445
|
+
// (SSR roughness / SSS / SkinSSS), which the single-color final framebuffer
|
|
446
|
+
// cannot carry. The legacy compositor.begin() used to redirect rendering to
|
|
447
|
+
// a temporal MRT framebuffer in that case; the graph path uses the regular
|
|
448
|
+
// scene color texture pipeline instead.
|
|
449
|
+
const opaqueLayerHasEffects = !!ctx.compositor?.layerHasEnabledEffect(PostEffectLayer.opaque);
|
|
450
|
+
const useFinalFramebufferAsIntermediate = !!depthPassResult.externalDepthAttachment && depthPassResult.externalDepthAttachment === ctx.finalFramebuffer?.getDepthAttachment() && !opaqueLayerHasEffects;
|
|
379
451
|
let preLightTransmissionDepthToken;
|
|
380
452
|
if (options.needsTransmissionDepthForSSR) {
|
|
381
|
-
|
|
453
|
+
const transmissionDepthResult = graph.addPass('TransmissionDepthForSSR', (builder)=>{
|
|
454
|
+
builder.read(depthHandle);
|
|
382
455
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
456
|
+
// This pass renders transmission geometry into the prepass linear-depth
|
|
457
|
+
// texture: model the mutation as a write so later readers order against
|
|
458
|
+
// it through data flow instead of relying on the token alone.
|
|
459
|
+
const depthOut = builder.write(depthHandle);
|
|
383
460
|
const done = builder.createToken('TransmissionDepthForSSRDone');
|
|
384
461
|
builder.sideEffect();
|
|
385
462
|
builder.setExecute((rgCtx)=>{
|
|
386
463
|
renderTransmissionDepthPass(frame, rgCtx);
|
|
387
464
|
});
|
|
388
|
-
return
|
|
465
|
+
return {
|
|
466
|
+
done,
|
|
467
|
+
depthOut
|
|
468
|
+
};
|
|
389
469
|
});
|
|
470
|
+
preLightTransmissionDepthToken = transmissionDepthResult.done;
|
|
471
|
+
depthHandle = transmissionDepthResult.depthOut;
|
|
472
|
+
// Re-register so blackboard consumers read the post-transmission version.
|
|
473
|
+
blackboard.set(FrameResources.LinearDepth, depthHandle);
|
|
390
474
|
}
|
|
391
475
|
// ── 6. Hi-Z (optional) ───────────────────────────────────────────
|
|
392
476
|
let hiZHandle;
|
|
@@ -398,7 +482,7 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
398
482
|
builder.read(preLightTransmissionDepthToken);
|
|
399
483
|
}
|
|
400
484
|
hiZHandle = builder.createTexture({
|
|
401
|
-
format: '
|
|
485
|
+
format: 'rg32f',
|
|
402
486
|
label: 'hiZ',
|
|
403
487
|
mipLevels: getFullMipLevelCount(ctx.renderWidth, ctx.renderHeight)
|
|
404
488
|
});
|
|
@@ -420,11 +504,13 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
420
504
|
}
|
|
421
505
|
});
|
|
422
506
|
});
|
|
507
|
+
if (hiZHandle) {
|
|
508
|
+
blackboard.set(FrameResources.HiZ, hiZHandle);
|
|
509
|
+
}
|
|
423
510
|
}
|
|
424
511
|
// ── 7. Main Light Pass ────────────────────────────────────────────
|
|
425
512
|
const historyManager = ctx.camera?.getHistoryResourceManager?.() ?? null;
|
|
426
513
|
const lightHistoryReadBindings = [];
|
|
427
|
-
const compositeHistoryReadBindings = [];
|
|
428
514
|
const historySize = {
|
|
429
515
|
width: ctx.renderWidth,
|
|
430
516
|
height: ctx.renderHeight
|
|
@@ -452,29 +538,7 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
452
538
|
});
|
|
453
539
|
}
|
|
454
540
|
}
|
|
455
|
-
|
|
456
|
-
const colorHistoryHandle = historyManager.importPreviousIfCompatible(graph, RGHistoryResources.TAA_COLOR, {
|
|
457
|
-
format: ctx.colorFormat,
|
|
458
|
-
sizeMode: 'absolute',
|
|
459
|
-
width: ctx.renderWidth,
|
|
460
|
-
height: ctx.renderHeight
|
|
461
|
-
}, historySize);
|
|
462
|
-
const motionVectorHistoryHandle = historyManager.importPreviousIfCompatible(graph, RGHistoryResources.TAA_MOTION_VECTOR, {
|
|
463
|
-
format: 'rgba16f',
|
|
464
|
-
sizeMode: 'absolute',
|
|
465
|
-
width: ctx.renderWidth,
|
|
466
|
-
height: ctx.renderHeight
|
|
467
|
-
}, historySize);
|
|
468
|
-
if (colorHistoryHandle && motionVectorHistoryHandle) {
|
|
469
|
-
compositeHistoryReadBindings.push({
|
|
470
|
-
name: RGHistoryResources.TAA_COLOR,
|
|
471
|
-
handle: colorHistoryHandle
|
|
472
|
-
}, {
|
|
473
|
-
name: RGHistoryResources.TAA_MOTION_VECTOR,
|
|
474
|
-
handle: motionVectorHistoryHandle
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
541
|
+
// Note: TAA history import/commit is handled by TAA.setup() (self-describing).
|
|
478
542
|
let sssProfileResult;
|
|
479
543
|
if (options.sss) {
|
|
480
544
|
sssProfileResult = graph.addPass('SSSProfile', (builder)=>{
|
|
@@ -524,9 +588,47 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
524
588
|
};
|
|
525
589
|
});
|
|
526
590
|
}
|
|
591
|
+
// 7b. Scene color grab: renders the full scene (no transmission) into a copy
|
|
592
|
+
// texture that transmission/refraction materials sample as background.
|
|
593
|
+
// Extracted from the former monolithic light pass.
|
|
594
|
+
let grabResult;
|
|
595
|
+
if (options.needSceneColor) {
|
|
596
|
+
grabResult = graph.addPass('SceneColorGrab', (builder)=>{
|
|
597
|
+
builder.read(depthHandle);
|
|
598
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
599
|
+
if (preLightTransmissionDepthToken) {
|
|
600
|
+
builder.read(preLightTransmissionDepthToken);
|
|
601
|
+
}
|
|
602
|
+
const copyHandle = builder.createTexture({
|
|
603
|
+
format: ctx.colorFormat,
|
|
604
|
+
label: 'sceneColorCopy'
|
|
605
|
+
});
|
|
606
|
+
// SSR may pre-insert transmission depth before LightPass for Hi-Z. In that
|
|
607
|
+
// case the refraction scene-color copy needs an isolated depth buffer so
|
|
608
|
+
// transmission surfaces do not occlude the background they sample.
|
|
609
|
+
const copyFramebufferHandle = !options.needsTransmissionDepthForSSR ? builder.createFramebuffer({
|
|
610
|
+
label: 'SceneColorCopyFramebuffer',
|
|
611
|
+
width: ctx.renderWidth,
|
|
612
|
+
height: ctx.renderHeight,
|
|
613
|
+
colorAttachments: copyHandle,
|
|
614
|
+
depthAttachment: renderDepthAttachment,
|
|
615
|
+
ignoreDepthStencil: false
|
|
616
|
+
}) : undefined;
|
|
617
|
+
builder.setExecute((rgCtx)=>{
|
|
618
|
+
renderSceneColorGrab(frame, rgCtx, copyHandle, copyFramebufferHandle);
|
|
619
|
+
});
|
|
620
|
+
return {
|
|
621
|
+
copyHandle,
|
|
622
|
+
copyFramebufferHandle
|
|
623
|
+
};
|
|
624
|
+
});
|
|
625
|
+
}
|
|
527
626
|
const lightPassResult = graph.addPass('LightPass', (builder)=>{
|
|
528
627
|
builder.read(depthHandle);
|
|
529
628
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
629
|
+
if (shadowMaskHandle) {
|
|
630
|
+
builder.read(shadowMaskHandle);
|
|
631
|
+
}
|
|
530
632
|
if (preLightTransmissionDepthToken) {
|
|
531
633
|
builder.read(preLightTransmissionDepthToken);
|
|
532
634
|
}
|
|
@@ -536,18 +638,18 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
536
638
|
for (const binding of lightHistoryReadBindings){
|
|
537
639
|
builder.read(binding.handle);
|
|
538
640
|
}
|
|
539
|
-
//
|
|
540
|
-
|
|
641
|
+
// Scene color: in final-framebuffer-as-intermediate mode the scene is
|
|
642
|
+
// physically rendered into the final framebuffer, so declare the
|
|
643
|
+
// backbuffer write — the graph sees the real data flow and no keep-alive
|
|
644
|
+
// reads are needed downstream. Otherwise render into a graph texture.
|
|
645
|
+
const sceneColorHandle = useFinalFramebufferAsIntermediate ? builder.write(backbuffer) : builder.createTexture({
|
|
541
646
|
format: ctx.colorFormat,
|
|
542
647
|
label: 'sceneColor'
|
|
543
648
|
});
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
format: ctx.colorFormat,
|
|
549
|
-
label: 'sceneColorCopy'
|
|
550
|
-
});
|
|
649
|
+
// Transmission/refraction background produced by the SceneColorGrab pass
|
|
650
|
+
const sceneColorCopyHandle = grabResult?.copyHandle;
|
|
651
|
+
if (sceneColorCopyHandle) {
|
|
652
|
+
builder.read(sceneColorCopyHandle);
|
|
551
653
|
}
|
|
552
654
|
if (sssProfileResult) {
|
|
553
655
|
builder.read(sssProfileResult.profileHandle);
|
|
@@ -557,6 +659,17 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
557
659
|
}
|
|
558
660
|
}
|
|
559
661
|
const includeSSRSurfaceMRT = !!options.ssr;
|
|
662
|
+
// SSR glossy-surface MRT outputs (roughness + world normal) are graph
|
|
663
|
+
// textures owned by this pass; effects reach them through the blackboard
|
|
664
|
+
// handles (or the ctx fields resolved below during execution).
|
|
665
|
+
const ssrRoughnessHandle = includeSSRSurfaceMRT ? builder.createTexture({
|
|
666
|
+
format: getSurfaceTextureFormat(ctx),
|
|
667
|
+
label: 'ssrRoughness'
|
|
668
|
+
}) : undefined;
|
|
669
|
+
const ssrNormalHandle = includeSSRSurfaceMRT ? builder.createTexture({
|
|
670
|
+
format: getSurfaceTextureFormat(ctx),
|
|
671
|
+
label: 'ssrNormal'
|
|
672
|
+
}) : undefined;
|
|
560
673
|
const writeSSSDiffuse = options.sss && shouldStoreSSSDiffuse(ctx);
|
|
561
674
|
let writeSSSTransmission = options.sss && shouldStoreSSSTransmission(ctx);
|
|
562
675
|
if (writeSSSDiffuse && writeSSSTransmission && includeSSRSurfaceMRT && getSSSLightingTextureFormat(ctx, 2, includeSSRSurfaceMRT) !== ctx.colorFormat) {
|
|
@@ -584,20 +697,13 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
584
697
|
colorAttachments: sceneColorHandle,
|
|
585
698
|
depthAttachment: renderDepthAttachment
|
|
586
699
|
});
|
|
587
|
-
// SSR may pre-insert transmission depth before LightPass for Hi-Z. In that case the
|
|
588
|
-
// refraction scene-color copy needs an isolated depth buffer so transmission surfaces
|
|
589
|
-
// do not occlude the background color they are about to sample.
|
|
590
|
-
const sceneColorCopyFramebufferHandle = sceneColorCopyHandle && !options.needsTransmissionDepthForSSR ? builder.createFramebuffer({
|
|
591
|
-
label: 'SceneColorCopyFramebuffer',
|
|
592
|
-
width: ctx.renderWidth,
|
|
593
|
-
height: ctx.renderHeight,
|
|
594
|
-
colorAttachments: sceneColorCopyHandle,
|
|
595
|
-
depthAttachment: renderDepthAttachment,
|
|
596
|
-
ignoreDepthStencil: false
|
|
597
|
-
}) : undefined;
|
|
598
700
|
builder.setExecute((rgCtx)=>{
|
|
599
701
|
const sceneColorTex = rgCtx.getTexture(sceneColorHandle);
|
|
600
702
|
const sceneColorCopyTex = sceneColorCopyHandle ? rgCtx.getTexture(sceneColorCopyHandle) : null;
|
|
703
|
+
// Resolve MRT products into the DrawContext bridge fields that scene
|
|
704
|
+
// rendering and apply()-based effects still read.
|
|
705
|
+
ctx.SSRRoughnessTexture = ssrRoughnessHandle ? rgCtx.getTexture(ssrRoughnessHandle) : null;
|
|
706
|
+
ctx.SSRNormalTexture = ssrNormalHandle ? rgCtx.getTexture(ssrNormalHandle) : null;
|
|
601
707
|
if (sssProfileResult) {
|
|
602
708
|
ctx.SSSProfileTexture = rgCtx.getTexture(sssProfileResult.profileHandle);
|
|
603
709
|
ctx.SSSParamTexture = rgCtx.getTexture(sssProfileResult.paramHandle);
|
|
@@ -608,7 +714,7 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
608
714
|
ctx.SSSDiffuseTexture = sssDiffuseHandle ? rgCtx.getTexture(sssDiffuseHandle) : null;
|
|
609
715
|
ctx.SSSTransmissionTexture = sssTransmissionHandle ? rgCtx.getTexture(sssTransmissionHandle) : null;
|
|
610
716
|
ctx.SkinSSSTexture = skinSSSHandle ? rgCtx.getTexture(skinSSSHandle) : null;
|
|
611
|
-
const renderLightPass = ()=>
|
|
717
|
+
const renderLightPass = ()=>renderOpaqueScenePass(frame, sceneColorTex, sceneColorCopyTex, rgCtx, sceneColorFramebufferHandle);
|
|
612
718
|
if (historyManager && lightHistoryReadBindings.length > 0) {
|
|
613
719
|
historyManager.beginReadScope(lightHistoryReadBindings.map((binding)=>({
|
|
614
720
|
name: binding.name,
|
|
@@ -626,66 +732,247 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
626
732
|
return {
|
|
627
733
|
sceneColorHandle,
|
|
628
734
|
sceneColorCopyHandle,
|
|
629
|
-
sceneColorFramebufferHandle
|
|
735
|
+
sceneColorFramebufferHandle,
|
|
736
|
+
ssrRoughnessHandle,
|
|
737
|
+
ssrNormalHandle,
|
|
738
|
+
sssDiffuseHandle,
|
|
739
|
+
sssTransmissionHandle,
|
|
740
|
+
skinSSSHandle
|
|
630
741
|
};
|
|
631
742
|
});
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
743
|
+
// Register the LightPass MRT products so effects can look them up by name.
|
|
744
|
+
if (lightPassResult.ssrRoughnessHandle) {
|
|
745
|
+
blackboard.set(FrameResources.SSRRoughness, lightPassResult.ssrRoughnessHandle);
|
|
746
|
+
}
|
|
747
|
+
if (lightPassResult.ssrNormalHandle) {
|
|
748
|
+
blackboard.set(FrameResources.SSRNormal, lightPassResult.ssrNormalHandle);
|
|
749
|
+
} else if (sssProfileResult?.normalHandle) {
|
|
750
|
+
blackboard.set(FrameResources.SSRNormal, sssProfileResult.normalHandle);
|
|
751
|
+
}
|
|
752
|
+
if (lightPassResult.sssDiffuseHandle) {
|
|
753
|
+
blackboard.set(FrameResources.SSSDiffuse, lightPassResult.sssDiffuseHandle);
|
|
754
|
+
}
|
|
755
|
+
if (lightPassResult.sssTransmissionHandle) {
|
|
756
|
+
blackboard.set(FrameResources.SSSTransmission, lightPassResult.sssTransmissionHandle);
|
|
757
|
+
}
|
|
758
|
+
if (lightPassResult.skinSSSHandle) {
|
|
759
|
+
blackboard.set(FrameResources.SkinSSS, lightPassResult.skinSSSHandle);
|
|
646
760
|
}
|
|
647
|
-
//
|
|
648
|
-
|
|
649
|
-
|
|
761
|
+
// 7d. Opaque-layer post effects (SAO/SSR/SSS/SkinSSS). They read the opaque
|
|
762
|
+
// scene color and must complete before transparent geometry renders on top
|
|
763
|
+
// of their output. Never direct-write: the chain output becomes the
|
|
764
|
+
// transparent pass's render target. In final-framebuffer-as-intermediate
|
|
765
|
+
// mode the scene color handle is the LightPass's backbuffer write version,
|
|
766
|
+
// so the data flow is real either way — no keep-alive reads needed.
|
|
767
|
+
const opaqueChainInput = lightPassResult.sceneColorHandle;
|
|
768
|
+
// Data dependencies for every effect pass: frame textures the effects sample
|
|
769
|
+
// through DrawContext fields (linear depth, HiZ, scene color copy, SSS MRT
|
|
770
|
+
// outputs) rather than through declared require* hooks.
|
|
771
|
+
const opaqueChainDeps = [
|
|
772
|
+
depthHandle
|
|
773
|
+
];
|
|
774
|
+
if (hiZHandle) {
|
|
775
|
+
opaqueChainDeps.push(hiZHandle);
|
|
776
|
+
}
|
|
777
|
+
if (lightPassResult.sceneColorFramebufferHandle) {
|
|
778
|
+
opaqueChainDeps.push(lightPassResult.sceneColorFramebufferHandle);
|
|
779
|
+
}
|
|
780
|
+
if (grabResult) {
|
|
781
|
+
opaqueChainDeps.push(grabResult.copyHandle);
|
|
782
|
+
}
|
|
783
|
+
if (sssProfileResult) {
|
|
784
|
+
opaqueChainDeps.push(sssProfileResult.profileHandle, sssProfileResult.paramHandle);
|
|
785
|
+
if (sssProfileResult.normalHandle) {
|
|
786
|
+
opaqueChainDeps.push(sssProfileResult.normalHandle);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
for (const handle of [
|
|
790
|
+
lightPassResult.ssrRoughnessHandle,
|
|
791
|
+
lightPassResult.ssrNormalHandle,
|
|
792
|
+
lightPassResult.sssDiffuseHandle,
|
|
793
|
+
lightPassResult.sssTransmissionHandle,
|
|
794
|
+
lightPassResult.skinSSSHandle
|
|
795
|
+
]){
|
|
796
|
+
if (handle) {
|
|
797
|
+
opaqueChainDeps.push(handle);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const opaqueChainResult = ctx.compositor ? ctx.compositor.buildLayer({
|
|
801
|
+
graph,
|
|
802
|
+
ctx,
|
|
803
|
+
layer: PostEffectLayer.opaque,
|
|
804
|
+
blackboard,
|
|
805
|
+
input: opaqueChainInput,
|
|
806
|
+
finalOutput: null,
|
|
807
|
+
sceneDepthAttachment: renderDepthAttachment,
|
|
808
|
+
dependencies: opaqueChainDeps,
|
|
809
|
+
historyReads: lightHistoryReadBindings,
|
|
810
|
+
history: historyManager
|
|
811
|
+
}) : {
|
|
812
|
+
color: opaqueChainInput,
|
|
813
|
+
wroteFinal: false
|
|
814
|
+
};
|
|
815
|
+
const opaqueChainRan = opaqueChainResult.color !== opaqueChainInput;
|
|
816
|
+
// 7e. Transparent scene geometry (transmission/transparent lists + OIT).
|
|
817
|
+
// Renders on top of the opaque-chain output; graph-wise an in-place write
|
|
818
|
+
// producing a new version of the current scene color.
|
|
819
|
+
const sceneColorHandle = graph.addPass('TransparentPass', (builder)=>{
|
|
650
820
|
builder.read(depthHandle);
|
|
821
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
651
822
|
if (hiZHandle) {
|
|
823
|
+
// Transparent-phase materials may ray-march HiZ (e.g. water SSR)
|
|
652
824
|
builder.read(hiZHandle);
|
|
653
825
|
}
|
|
654
|
-
if (motionVectorHandle) {
|
|
655
|
-
builder.read(motionVectorHandle);
|
|
656
|
-
}
|
|
657
826
|
if (lightPassResult.sceneColorFramebufferHandle) {
|
|
658
827
|
builder.read(lightPassResult.sceneColorFramebufferHandle);
|
|
659
828
|
}
|
|
660
|
-
if (
|
|
661
|
-
builder.read(
|
|
829
|
+
if (lightPassResult.sceneColorCopyHandle) {
|
|
830
|
+
builder.read(lightPassResult.sceneColorCopyHandle);
|
|
662
831
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
}
|
|
666
|
-
const outputBackbuffer = builder.write(backbuffer);
|
|
667
|
-
const taaHistoryColorHandle = useFinalFramebufferAsIntermediate ? outputBackbuffer : sceneColorHandle;
|
|
832
|
+
builder.read(opaqueChainResult.color);
|
|
833
|
+
const out = builder.write(opaqueChainResult.color);
|
|
668
834
|
builder.setExecute((rgCtx)=>{
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
835
|
+
renderTransparentScenePass(frame, rgCtx, opaqueChainRan ? opaqueChainResult.color : null, lightPassResult.sceneColorFramebufferHandle);
|
|
836
|
+
});
|
|
837
|
+
return out;
|
|
838
|
+
});
|
|
839
|
+
blackboard.set(FrameResources.SceneColor, sceneColorHandle);
|
|
840
|
+
if (lightPassResult.sceneColorCopyHandle) {
|
|
841
|
+
blackboard.set(FrameResources.SceneColorCopy, lightPassResult.sceneColorCopyHandle);
|
|
842
|
+
}
|
|
843
|
+
if (sssProfileResult) {
|
|
844
|
+
blackboard.set(FrameResources.SSSProfile, sssProfileResult.profileHandle);
|
|
845
|
+
blackboard.set(FrameResources.SSSParam, sssProfileResult.paramHandle);
|
|
846
|
+
}
|
|
847
|
+
// 8. Post effect chains + transmission depth.
|
|
848
|
+
//
|
|
849
|
+
// Chain input: the TransparentPass output version — the authoritative scene
|
|
850
|
+
// color regardless of whether it physically lives in a texture or in the
|
|
851
|
+
// backbuffer (final framebuffer used as intermediate, no opaque effects).
|
|
852
|
+
const chainInput = sceneColorHandle;
|
|
853
|
+
// When the scene color still physically resides in the final framebuffer,
|
|
854
|
+
// the Present blit must be skipped if no effect moved it to a texture.
|
|
855
|
+
// (Opaque-layer effects disable final-as-intermediate mode, so the scene
|
|
856
|
+
// color is backbuffer-resident whenever that mode is active.)
|
|
857
|
+
const backbufferResidentHandle = useFinalFramebufferAsIntermediate ? sceneColorHandle : null;
|
|
858
|
+
// No extra chain dependencies: the effect chains link to the scene color
|
|
859
|
+
// through their inputs, and per-effect texture needs are declared via the
|
|
860
|
+
// require* hooks in AbstractPostEffect.setup().
|
|
861
|
+
const chainDependencies = [];
|
|
862
|
+
const finalOutput = {
|
|
863
|
+
handle: backbuffer,
|
|
864
|
+
isScreen: !ctx.finalFramebuffer
|
|
865
|
+
};
|
|
866
|
+
const endLayerHasEffects = !!ctx.compositor?.layerHasEnabledEffect(PostEffectLayer.end);
|
|
867
|
+
// 8a. Transparent-layer effects (bloom, tonemap, FXAA, ...). They run right
|
|
868
|
+
// after the light pass and must sample the pre-transmission linear depth, so
|
|
869
|
+
// they carry no transmissionDepthToken dependency; TransmissionDepth is
|
|
870
|
+
// instead ordered after this chain (see 8b).
|
|
871
|
+
const transparentChainResult = ctx.compositor ? ctx.compositor.buildLayer({
|
|
872
|
+
graph,
|
|
873
|
+
ctx,
|
|
874
|
+
layer: PostEffectLayer.transparent,
|
|
875
|
+
blackboard,
|
|
876
|
+
input: chainInput,
|
|
877
|
+
finalOutput: endLayerHasEffects ? null : finalOutput,
|
|
878
|
+
inputResidesInFinalTarget: !!backbufferResidentHandle,
|
|
879
|
+
sceneDepthAttachment: renderDepthAttachment,
|
|
880
|
+
dependencies: chainDependencies,
|
|
881
|
+
history: historyManager
|
|
882
|
+
}) : {
|
|
883
|
+
color: chainInput,
|
|
884
|
+
wroteFinal: false
|
|
885
|
+
};
|
|
886
|
+
// 8b. Transmission depth pass (optional). Mutates the linear depth texture;
|
|
887
|
+
// the mutation is modeled as a graph write: the WAR hazard orders this pass
|
|
888
|
+
// after every pre-transmission depth reader (the transparent-layer chain),
|
|
889
|
+
// and re-registering the post-write version in the blackboard gives
|
|
890
|
+
// end-layer effects (TAA) a real data dependency on the transmission depth.
|
|
891
|
+
let transmissionDepthToken;
|
|
892
|
+
if (options.needSceneColor && !options.needsTransmissionDepthForSSR) {
|
|
893
|
+
const transmissionDepthResult = graph.addPass('TransmissionDepth', (builder)=>{
|
|
894
|
+
builder.read(sceneColorHandle);
|
|
895
|
+
if (transparentChainResult.color !== sceneColorHandle) {
|
|
896
|
+
builder.read(transparentChainResult.color);
|
|
897
|
+
}
|
|
898
|
+
builder.read(depthHandle);
|
|
899
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
900
|
+
const depthOut = builder.write(depthHandle);
|
|
901
|
+
const done = builder.createToken('TransmissionDepthDone');
|
|
902
|
+
builder.sideEffect();
|
|
903
|
+
builder.setExecute((rgCtx)=>{
|
|
904
|
+
renderTransmissionDepthPass(frame, rgCtx);
|
|
905
|
+
});
|
|
906
|
+
return {
|
|
907
|
+
done,
|
|
908
|
+
depthOut
|
|
672
909
|
};
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
910
|
+
});
|
|
911
|
+
transmissionDepthToken = transmissionDepthResult.done;
|
|
912
|
+
depthHandle = transmissionDepthResult.depthOut;
|
|
913
|
+
// The transparent-layer chain above read the pre-transmission version;
|
|
914
|
+
// everything built from here on (end-layer chain) reads this one.
|
|
915
|
+
blackboard.set(FrameResources.LinearDepth, depthHandle);
|
|
916
|
+
}
|
|
917
|
+
// 9. End-layer effects (TAA). Ordered after TransmissionDepth.
|
|
918
|
+
const endChainDependencies = transmissionDepthToken ? [
|
|
919
|
+
...chainDependencies,
|
|
920
|
+
transmissionDepthToken
|
|
921
|
+
] : chainDependencies;
|
|
922
|
+
const chainResult = ctx.compositor ? ctx.compositor.buildLayer({
|
|
923
|
+
graph,
|
|
924
|
+
ctx,
|
|
925
|
+
layer: PostEffectLayer.end,
|
|
926
|
+
blackboard,
|
|
927
|
+
input: transparentChainResult.color,
|
|
928
|
+
finalOutput,
|
|
929
|
+
// Still backbuffer-resident if the transparent-layer chain ran no effect
|
|
930
|
+
inputResidesInFinalTarget: !!backbufferResidentHandle && transparentChainResult.color === chainInput,
|
|
931
|
+
sceneDepthAttachment: renderDepthAttachment,
|
|
932
|
+
dependencies: endChainDependencies,
|
|
933
|
+
history: historyManager
|
|
934
|
+
}) : {
|
|
935
|
+
color: transparentChainResult.color,
|
|
936
|
+
wroteFinal: false
|
|
937
|
+
};
|
|
938
|
+
const finalWroteFinal = chainResult.wroteFinal || transparentChainResult.wroteFinal;
|
|
939
|
+
// 10. Present + frame cleanup.
|
|
940
|
+
let presentedBackbuffer;
|
|
941
|
+
if (finalWroteFinal) {
|
|
942
|
+
// The last effect wrote the final target directly; only cleanup remains.
|
|
943
|
+
presentedBackbuffer = chainResult.color;
|
|
944
|
+
graph.addPass('FrameCleanup', (builder)=>{
|
|
945
|
+
builder.read(presentedBackbuffer);
|
|
946
|
+
for (const dep of endChainDependencies){
|
|
947
|
+
builder.read(dep);
|
|
685
948
|
}
|
|
949
|
+
builder.sideEffect();
|
|
950
|
+
builder.setExecute(()=>{
|
|
951
|
+
finishFrame(frame);
|
|
952
|
+
});
|
|
686
953
|
});
|
|
687
|
-
|
|
688
|
-
|
|
954
|
+
} else {
|
|
955
|
+
presentedBackbuffer = graph.addPass('Present', (builder)=>{
|
|
956
|
+
builder.read(chainResult.color);
|
|
957
|
+
for (const dep of endChainDependencies){
|
|
958
|
+
builder.read(dep);
|
|
959
|
+
}
|
|
960
|
+
const outputBackbuffer = builder.write(backbuffer);
|
|
961
|
+
// Skip the blit when the chain output already lives in the final target
|
|
962
|
+
// (final framebuffer used as intermediate and no end-layer effect ran).
|
|
963
|
+
const needsBlit = chainResult.color !== backbufferResidentHandle;
|
|
964
|
+
builder.setExecute((rgCtx)=>{
|
|
965
|
+
const sourceTex = needsBlit ? rgCtx.getTexture(chainResult.color) : null;
|
|
966
|
+
if (sourceTex) {
|
|
967
|
+
const blitter = new CopyBlitter();
|
|
968
|
+
blitter.srgbOut = !ctx.finalFramebuffer;
|
|
969
|
+
blitter.blit(sourceTex, ctx.finalFramebuffer ?? null, fetchSampler('clamp_nearest_nomip'));
|
|
970
|
+
}
|
|
971
|
+
finishFrame(frame);
|
|
972
|
+
});
|
|
973
|
+
return outputBackbuffer;
|
|
974
|
+
});
|
|
975
|
+
}
|
|
689
976
|
return {
|
|
690
977
|
backbuffer: presentedBackbuffer,
|
|
691
978
|
frame
|
|
@@ -766,12 +1053,9 @@ function renderForwardSSSProfile(frame, profileFramebuffer, profileTexture, para
|
|
|
766
1053
|
}
|
|
767
1054
|
}
|
|
768
1055
|
function releaseIntermediateFramebuffer(frame) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
frame.intermediateDeviceStatePushed = false;
|
|
773
|
-
}
|
|
774
|
-
ctx.intermediateFramebuffer = null;
|
|
1056
|
+
// Device state is contained within each pass now (LightPass/TransparentPass
|
|
1057
|
+
// push/pop their own framebuffer bindings); only the context field remains.
|
|
1058
|
+
frame.ctx.intermediateFramebuffer = null;
|
|
775
1059
|
}
|
|
776
1060
|
function releaseDepthFramebuffer(frame) {
|
|
777
1061
|
frame.depthFramebuffer = null;
|
|
@@ -965,9 +1249,58 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
965
1249
|
const destination = framebuffer?.getColorAttachment(0) ?? null;
|
|
966
1250
|
new CopyBlitter().blit(source, destination, fetchSampler('clamp_nearest_nomip'));
|
|
967
1251
|
}
|
|
968
|
-
/**
|
|
1252
|
+
/**
|
|
1253
|
+
* Renders the full scene (no transmission) into the scene-color copy texture
|
|
1254
|
+
* used as refraction background. Runs as its own graph pass before LightPass.
|
|
1255
|
+
* @internal
|
|
1256
|
+
*/ function renderSceneColorGrab(frame, rgCtx, copyHandle, copyFramebufferHandle) {
|
|
969
1257
|
const { ctx, renderQueue } = frame;
|
|
970
1258
|
const device = ctx.device;
|
|
1259
|
+
const depthTex = frame.depthFramebuffer?.getDepthAttachment();
|
|
1260
|
+
const copyTex = rgCtx.getTexture(copyHandle);
|
|
1261
|
+
const compositor = ctx.compositor;
|
|
1262
|
+
ctx.compositor = null;
|
|
1263
|
+
const isolateSceneColorDepth = frame.options.needsTransmissionDepthForSSR;
|
|
1264
|
+
const savedDepthPrepassAttachment = ctx.depthPrepassAttachment;
|
|
1265
|
+
const savedMaterialFlags = ctx.materialFlags;
|
|
1266
|
+
// MRT store flags never apply to the background copy
|
|
1267
|
+
const sceneColorMaterialFlags = ctx.materialFlags & ~SURFACE_MRT_FLAGS;
|
|
1268
|
+
const sceneColorFramebuffer = copyFramebufferHandle ? rgCtx.getFramebuffer(copyFramebufferHandle) : rgCtx.createFramebuffer({
|
|
1269
|
+
width: copyTex.width,
|
|
1270
|
+
height: copyTex.height,
|
|
1271
|
+
colorAttachments: copyTex,
|
|
1272
|
+
depthAttachment: isolateSceneColorDepth ? ctx.depthFormat : depthTex,
|
|
1273
|
+
ignoreDepthStencil: false
|
|
1274
|
+
});
|
|
1275
|
+
let sceneColorStatePushed = false;
|
|
1276
|
+
try {
|
|
1277
|
+
device.pushDeviceStates();
|
|
1278
|
+
sceneColorStatePushed = true;
|
|
1279
|
+
device.setFramebuffer(sceneColorFramebuffer);
|
|
1280
|
+
_scenePass.transmission = false;
|
|
1281
|
+
if (isolateSceneColorDepth) {
|
|
1282
|
+
ctx.depthPrepassAttachment = undefined;
|
|
1283
|
+
_scenePass.clearDepth = 1;
|
|
1284
|
+
_scenePass.clearStencil = 0;
|
|
1285
|
+
} else {
|
|
1286
|
+
_scenePass.clearDepth = depthTex ? null : 1;
|
|
1287
|
+
_scenePass.clearStencil = depthTex ? null : 0;
|
|
1288
|
+
}
|
|
1289
|
+
ctx.materialFlags = sceneColorMaterialFlags;
|
|
1290
|
+
_scenePass.render(ctx, null, null, renderQueue);
|
|
1291
|
+
} finally{
|
|
1292
|
+
ctx.materialFlags = savedMaterialFlags;
|
|
1293
|
+
if (isolateSceneColorDepth) {
|
|
1294
|
+
ctx.depthPrepassAttachment = savedDepthPrepassAttachment;
|
|
1295
|
+
}
|
|
1296
|
+
if (sceneColorStatePushed) {
|
|
1297
|
+
device.popDeviceStates();
|
|
1298
|
+
}
|
|
1299
|
+
ctx.compositor = compositor;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
/** @internal */ function renderOpaqueScenePass(frame, sceneColorTex, sceneColorCopyTex, rgCtx, sceneColorFramebufferHandle) {
|
|
1303
|
+
const { ctx, renderQueue } = frame;
|
|
971
1304
|
// Use RenderGraph-allocated scene color texture
|
|
972
1305
|
const depthTex = frame.depthFramebuffer?.getDepthAttachment();
|
|
973
1306
|
ctx.materialFlags &= ~(MaterialVaryingFlags.SSR_STORE_ROUGHNESS | MaterialVaryingFlags.SSS_STORE_PROFILE | MaterialVaryingFlags.SSS_STORE_DIFFUSE | MaterialVaryingFlags.SSS_STORE_NORMAL | MaterialVaryingFlags.SSS_STORE_TRANSMISSION | MaterialVaryingFlags.SKIN_SSS_STORE);
|
|
@@ -985,10 +1318,15 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
985
1318
|
if (ctx.SkinSSSTexture) {
|
|
986
1319
|
ctx.materialFlags |= MaterialVaryingFlags.SKIN_SSS_STORE;
|
|
987
1320
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1321
|
+
// The graph scene color framebuffer takes priority: it is absent only when
|
|
1322
|
+
// the graph was built in final-framebuffer-as-intermediate mode (external
|
|
1323
|
+
// depth shared with the final framebuffer AND no opaque-layer effects).
|
|
1324
|
+
// Checking the shared depth first would wrongly route the MRT/opaque-effect
|
|
1325
|
+
// case into the single-color final framebuffer.
|
|
1326
|
+
if (sceneColorFramebufferHandle && !hasSurfaceMRT(ctx)) {
|
|
991
1327
|
ctx.intermediateFramebuffer = rgCtx.getFramebuffer(sceneColorFramebufferHandle);
|
|
1328
|
+
} else if (!sceneColorFramebufferHandle && depthTex === ctx.finalFramebuffer?.getDepthAttachment()) {
|
|
1329
|
+
ctx.intermediateFramebuffer = ctx.finalFramebuffer;
|
|
992
1330
|
} else {
|
|
993
1331
|
ctx.intermediateFramebuffer = rgCtx.createFramebuffer({
|
|
994
1332
|
width: sceneColorTex.width,
|
|
@@ -997,104 +1335,122 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
997
1335
|
depthAttachment: depthTex
|
|
998
1336
|
});
|
|
999
1337
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1338
|
+
// The scene target is bound explicitly and the device state is restored at
|
|
1339
|
+
// the end of this pass: graph passes never communicate through leftover
|
|
1340
|
+
// device state. TransparentPass re-binds the same target (or the opaque
|
|
1341
|
+
// chain output) explicitly.
|
|
1342
|
+
ctx.device.pushDeviceStates();
|
|
1343
|
+
try {
|
|
1344
|
+
// setFramebuffer() no-ops when the target is unchanged, so reset the
|
|
1345
|
+
// viewport/scissor explicitly to cover that case.
|
|
1346
|
+
ctx.device.setFramebuffer(ctx.intermediateFramebuffer);
|
|
1347
|
+
ctx.device.setViewport(null);
|
|
1348
|
+
ctx.device.setScissor(null);
|
|
1349
|
+
_scenePass.transmission = false;
|
|
1350
|
+
_scenePass.clearDepth = depthTex ? null : 1;
|
|
1351
|
+
_scenePass.clearStencil = depthTex ? null : 0;
|
|
1352
|
+
if (renderQueue.needSceneColor() && sceneColorCopyTex) {
|
|
1353
|
+
// Background copy was produced by the SceneColorGrab pass; seed the main
|
|
1354
|
+
// color attachment with it and render only transmission/transparent on top.
|
|
1355
|
+
ctx.sceneColorTexture = sceneColorCopyTex;
|
|
1356
|
+
blitToCurrentColorAttachment(ctx, ctx.sceneColorTexture);
|
|
1357
|
+
if (hasSurfaceMRT(ctx)) {
|
|
1358
|
+
// The background copy carries no surface MRT attachments, so opaque
|
|
1359
|
+
// geometry exists only there without roughness/normal (and SSS
|
|
1360
|
+
// lighting) data. Re-render the opaque lists into the MRT scene
|
|
1361
|
+
// target: early-z against the prepass depth keeps this cheap, and the
|
|
1362
|
+
// color output matches the blitted copy on opaque pixels while the
|
|
1363
|
+
// MRT attachments receive the surface data SSR/SSS require.
|
|
1364
|
+
_scenePass.clearColor = null;
|
|
1365
|
+
_scenePass.clearDepth = null;
|
|
1366
|
+
_scenePass.clearStencil = null;
|
|
1367
|
+
_scenePass.renderOpaque = true;
|
|
1368
|
+
_scenePass.renderTransparent = false;
|
|
1369
|
+
_scenePass.render(ctx, null, null, renderQueue);
|
|
1370
|
+
}
|
|
1371
|
+
_scenePass.transmission = true;
|
|
1372
|
+
_scenePass.clearColor = null;
|
|
1373
|
+
_scenePass.clearDepth = null;
|
|
1374
|
+
_scenePass.clearStencil = null;
|
|
1375
|
+
}
|
|
1376
|
+
_scenePass.renderOpaque = true;
|
|
1377
|
+
_scenePass.renderTransparent = false;
|
|
1378
|
+
_scenePass.render(ctx, null, null, renderQueue);
|
|
1379
|
+
_scenePass.renderTransparent = true;
|
|
1380
|
+
} finally{
|
|
1381
|
+
ctx.device.popDeviceStates();
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Renders the transmission/transparent geometry lists (including OIT) on top
|
|
1386
|
+
* of the opaque result. The target framebuffer is always bound explicitly:
|
|
1387
|
+
* the opaque chain output when opaque-layer effects ran, otherwise the same
|
|
1388
|
+
* scene target the light pass rendered into. OIT implementations composite
|
|
1389
|
+
* into the currently bound framebuffer, which this pass guarantees.
|
|
1390
|
+
* @internal
|
|
1391
|
+
*/ function renderTransparentScenePass(frame, rgCtx, opaqueChainOutput, sceneColorFramebufferHandle) {
|
|
1392
|
+
const { ctx, renderQueue } = frame;
|
|
1393
|
+
const device = ctx.device;
|
|
1394
|
+
let framebuffer;
|
|
1395
|
+
if (opaqueChainOutput) {
|
|
1396
|
+
// Opaque-layer effects redirected the scene color into their chain output;
|
|
1397
|
+
// transparent geometry renders on top of it with the scene depth attached.
|
|
1398
|
+
const chainTex = rgCtx.getTexture(opaqueChainOutput);
|
|
1399
|
+
const depthTex = frame.depthFramebuffer?.getDepthAttachment();
|
|
1400
|
+
framebuffer = rgCtx.createFramebuffer({
|
|
1401
|
+
width: chainTex.width,
|
|
1402
|
+
height: chainTex.height,
|
|
1403
|
+
colorAttachments: chainTex,
|
|
1404
|
+
depthAttachment: depthTex
|
|
1405
|
+
});
|
|
1406
|
+
// The chain output is single-color: surface MRT stores no longer apply.
|
|
1407
|
+
ctx.materialFlags &= ~SURFACE_MRT_FLAGS;
|
|
1408
|
+
} else if (sceneColorFramebufferHandle) {
|
|
1409
|
+
// No opaque-layer effect ran (thus no surface MRT either): continue in the
|
|
1410
|
+
// graph scene color framebuffer the light pass rendered into.
|
|
1411
|
+
framebuffer = rgCtx.getFramebuffer(sceneColorFramebufferHandle);
|
|
1004
1412
|
} else {
|
|
1413
|
+
// Final framebuffer used as scene intermediate.
|
|
1414
|
+
framebuffer = ctx.finalFramebuffer;
|
|
1415
|
+
}
|
|
1416
|
+
device.pushDeviceStates();
|
|
1417
|
+
try {
|
|
1418
|
+
// setFramebuffer() no-ops when the target is unchanged, so reset the
|
|
1419
|
+
// viewport/scissor explicitly to cover that case.
|
|
1420
|
+
device.setFramebuffer(framebuffer);
|
|
1005
1421
|
device.setViewport(null);
|
|
1006
1422
|
device.setScissor(null);
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const isolateSceneColorDepth = frame.options.needsTransmissionDepthForSSR;
|
|
1016
|
-
const savedDepthPrepassAttachment = ctx.depthPrepassAttachment;
|
|
1017
|
-
const savedClearDepth = _scenePass.clearDepth;
|
|
1018
|
-
const savedClearStencil = _scenePass.clearStencil;
|
|
1019
|
-
const savedMaterialFlags = ctx.materialFlags;
|
|
1020
|
-
// Use RenderGraph-allocated sceneColorCopy texture
|
|
1021
|
-
const sceneColorMaterialFlags = ctx.materialFlags & ~SURFACE_MRT_FLAGS;
|
|
1022
|
-
const sceneColorFramebuffer = sceneColorCopyFramebufferHandle ? rgCtx.getFramebuffer(sceneColorCopyFramebufferHandle) : rgCtx.createFramebuffer({
|
|
1023
|
-
width: sceneColorCopyTex.width,
|
|
1024
|
-
height: sceneColorCopyTex.height,
|
|
1025
|
-
colorAttachments: sceneColorCopyTex,
|
|
1026
|
-
depthAttachment: isolateSceneColorDepth ? ctx.depthFormat : depthTex,
|
|
1027
|
-
ignoreDepthStencil: false
|
|
1028
|
-
});
|
|
1029
|
-
let sceneColorStatePushed = false;
|
|
1423
|
+
// _scenePass.transmission carries over from the opaque phase (true when the
|
|
1424
|
+
// scene color copy seeded the background, false otherwise). Never clear:
|
|
1425
|
+
// the opaque result is already in the target.
|
|
1426
|
+
_scenePass.clearColor = null;
|
|
1427
|
+
_scenePass.clearDepth = null;
|
|
1428
|
+
_scenePass.clearStencil = null;
|
|
1429
|
+
_scenePass.renderOpaque = false;
|
|
1430
|
+
_scenePass.renderTransparent = true;
|
|
1030
1431
|
try {
|
|
1031
|
-
device.pushDeviceStates();
|
|
1032
|
-
sceneColorStatePushed = true;
|
|
1033
|
-
device.setFramebuffer(sceneColorFramebuffer);
|
|
1034
|
-
_scenePass.transmission = false;
|
|
1035
|
-
if (isolateSceneColorDepth) {
|
|
1036
|
-
ctx.depthPrepassAttachment = undefined;
|
|
1037
|
-
_scenePass.clearDepth = 1;
|
|
1038
|
-
_scenePass.clearStencil = 0;
|
|
1039
|
-
}
|
|
1040
|
-
ctx.materialFlags = sceneColorMaterialFlags;
|
|
1041
1432
|
_scenePass.render(ctx, null, null, renderQueue);
|
|
1042
1433
|
} finally{
|
|
1043
|
-
|
|
1044
|
-
if (isolateSceneColorDepth) {
|
|
1045
|
-
ctx.depthPrepassAttachment = savedDepthPrepassAttachment;
|
|
1046
|
-
_scenePass.clearDepth = savedClearDepth;
|
|
1047
|
-
_scenePass.clearStencil = savedClearStencil;
|
|
1048
|
-
}
|
|
1049
|
-
if (sceneColorStatePushed) {
|
|
1050
|
-
device.popDeviceStates();
|
|
1051
|
-
}
|
|
1052
|
-
ctx.compositor = compositor;
|
|
1434
|
+
_scenePass.renderOpaque = true;
|
|
1053
1435
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
_scenePass.transmission = true;
|
|
1057
|
-
_scenePass.clearColor = null;
|
|
1058
|
-
_scenePass.clearDepth = null;
|
|
1059
|
-
_scenePass.clearStencil = null;
|
|
1436
|
+
} finally{
|
|
1437
|
+
device.popDeviceStates();
|
|
1060
1438
|
}
|
|
1061
|
-
_scenePass.render(ctx, null, null, renderQueue);
|
|
1062
1439
|
}
|
|
1063
1440
|
/** @internal */ function renderTransmissionDepthPass(frame, rgCtx) {
|
|
1064
1441
|
renderSceneDepth(frame, frame.depthFramebuffer, rgCtx);
|
|
1065
1442
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
historyManager.queueRetainedCommit(RGHistoryResources.TAA_COLOR, {
|
|
1077
|
-
format: colorTexture.format,
|
|
1078
|
-
sizeMode: 'absolute',
|
|
1079
|
-
width: colorTexture.width,
|
|
1080
|
-
height: colorTexture.height
|
|
1081
|
-
}, colorSize, colorTexture);
|
|
1082
|
-
const motionVectorTexture = rgCtx.getTexture(motionVectorHandle);
|
|
1083
|
-
const motionVectorSize = {
|
|
1084
|
-
width: motionVectorTexture.width,
|
|
1085
|
-
height: motionVectorTexture.height
|
|
1086
|
-
};
|
|
1087
|
-
historyManager.queueRetainedCommit(RGHistoryResources.TAA_MOTION_VECTOR, {
|
|
1088
|
-
format: motionVectorTexture.format,
|
|
1089
|
-
sizeMode: 'absolute',
|
|
1090
|
-
width: motionVectorTexture.width,
|
|
1091
|
-
height: motionVectorTexture.height
|
|
1092
|
-
}, motionVectorSize, motionVectorTexture);
|
|
1093
|
-
}
|
|
1094
|
-
/** @internal */ function renderComposite(frame) {
|
|
1443
|
+
/**
|
|
1444
|
+
* Frame-tail housekeeping shared by the Present and FrameCleanup passes.
|
|
1445
|
+
*
|
|
1446
|
+
* The end-layer post effect chain and the final blit are graph passes now
|
|
1447
|
+
* (see buildForwardPlusGraphInternal step 9/10); this only releases per-frame
|
|
1448
|
+
* state. The compositor's scene ping-pong is flushed back to the intermediate
|
|
1449
|
+
* framebuffer by compositor.end() at the tail of the light pass.
|
|
1450
|
+
*
|
|
1451
|
+
* @internal
|
|
1452
|
+
*/ function finishFrame(frame) {
|
|
1095
1453
|
const { ctx } = frame;
|
|
1096
|
-
ctx.compositor?.drawPostEffects(ctx, PostEffectLayer.end, ctx.linearDepthTexture);
|
|
1097
|
-
ctx.compositor?.end(ctx);
|
|
1098
1454
|
disposeRenderQueue(frame);
|
|
1099
1455
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSR_STORE_ROUGHNESS;
|
|
1100
1456
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSS_STORE_PROFILE;
|
|
@@ -1102,12 +1458,6 @@ function queueTAAHistoryCommit(frame, rgCtx, historyManager, colorHandle, motion
|
|
|
1102
1458
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSS_STORE_NORMAL;
|
|
1103
1459
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSS_STORE_TRANSMISSION;
|
|
1104
1460
|
ctx.materialFlags &= ~MaterialVaryingFlags.SKIN_SSS_STORE;
|
|
1105
|
-
if (ctx.intermediateFramebuffer && ctx.intermediateFramebuffer !== ctx.finalFramebuffer) {
|
|
1106
|
-
const blitter = new CopyBlitter();
|
|
1107
|
-
blitter.srgbOut = !ctx.finalFramebuffer;
|
|
1108
|
-
const srcTex = ctx.intermediateFramebuffer.getColorAttachments()[0];
|
|
1109
|
-
blitter.blit(srcTex, ctx.finalFramebuffer ?? null, fetchSampler('clamp_nearest_nomip'));
|
|
1110
|
-
}
|
|
1111
1461
|
releaseIntermediateFramebuffer(frame);
|
|
1112
1462
|
releaseDepthFramebuffer(frame);
|
|
1113
1463
|
releaseClusteredLight(frame);
|