@zephyr3d/scene 0.9.17 → 0.9.19
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/collision.js +8 -6
- package/dist/animation/joint_dynamics/collision.js.map +1 -1
- package/dist/animation/morphtargetgrouptrack.js +11 -3
- package/dist/animation/morphtargetgrouptrack.js.map +1 -1
- package/dist/app/scriptregistry.js +124 -6
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/index.d.ts +270 -7
- package/dist/index.js +2 -1
- package/dist/index.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/mtoon.js +20 -2
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/shader/helper.js +21 -8
- 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 +10 -10
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssr.js +247 -16
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/taa.js +121 -19
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/posteffect/tonemap.js +6 -6
- package/dist/posteffect/tonemap.js.map +1 -1
- package/dist/render/abuffer_oit.js +2 -1
- package/dist/render/abuffer_oit.js.map +1 -1
- 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 +7 -15
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/renderer.js +4 -3
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +78 -0
- package/dist/render/rendergraph/blackboard.js.map +1 -0
- 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 +495 -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/scene/light.js +146 -146
- package/dist/scene/light.js.map +1 -1
- package/dist/shaders/ssr.js +52 -21
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/shadow/shadowmapper.js +95 -95
- package/dist/shadow/shadowmapper.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 +2 -1
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +36 -2
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/package.json +2 -2
|
@@ -10,12 +10,13 @@ import '@zephyr3d/device';
|
|
|
10
10
|
import { CopyBlitter } from '../../blitter/copy.js';
|
|
11
11
|
import { fetchSampler } from '../../utility/misc.js';
|
|
12
12
|
import { MaterialVaryingFlags } from '../../values.js';
|
|
13
|
-
import {
|
|
13
|
+
import { PostEffectLayer, AbstractPostEffect } from '../../posteffect/posteffect.js';
|
|
14
14
|
import { RenderGraph } from './rendergraph.js';
|
|
15
15
|
import { RenderGraphExecutor } from './executor.js';
|
|
16
16
|
import { DevicePoolAllocator } from './device_pool_allocator.js';
|
|
17
17
|
import { HistoryResourceManager } from './history_resource_manager.js';
|
|
18
18
|
import { RGHistoryResources } from './history_resources.js';
|
|
19
|
+
import { FrameResources, RGBlackboard } from './blackboard.js';
|
|
19
20
|
import { renderObjectColors } from '../gpu_picking.js';
|
|
20
21
|
import { BoxShape } from '../../shapes/box.js';
|
|
21
22
|
import '../../shapes/capsule.js';
|
|
@@ -173,9 +174,11 @@ function getSSSLightingTextureFormat(ctx, attachmentCount, includeSSRSurfaceMRT)
|
|
|
173
174
|
return colorFormat;
|
|
174
175
|
}
|
|
175
176
|
const caps = ctx.device.getDeviceCaps();
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
// The SSR roughness/normal MRT textures use the glossy surface format; they
|
|
178
|
+
// are graph textures now, so derive the format directly instead of reading
|
|
179
|
+
// the (not yet resolved) DrawContext fields.
|
|
180
|
+
const surfaceFormat = getSurfaceTextureFormat(ctx);
|
|
181
|
+
const colorBytes = getTextureFormatBytes(ctx, colorFormat) + getTextureFormatBytes(ctx, surfaceFormat) + getTextureFormatBytes(ctx, surfaceFormat);
|
|
179
182
|
const fullPrecisionBytes = colorBytes + getTextureFormatBytes(ctx, colorFormat) * attachmentCount;
|
|
180
183
|
if (fullPrecisionBytes <= caps.framebufferCaps.maxColorAttachmentBytesPerSample) {
|
|
181
184
|
return colorFormat;
|
|
@@ -256,6 +259,8 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
256
259
|
const backbuffer = graph.importTexture('backbuffer');
|
|
257
260
|
ctx.SSS = !!options.sss;
|
|
258
261
|
ctx.SkinSSSTexture = null;
|
|
262
|
+
// Named registry of shared frame resources (consumed by post effect setup)
|
|
263
|
+
const blackboard = new RGBlackboard();
|
|
259
264
|
// Shared mutable frame state
|
|
260
265
|
const frame = {
|
|
261
266
|
ctx,
|
|
@@ -263,7 +268,6 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
263
268
|
depthFramebuffer: null,
|
|
264
269
|
sunLightColor: null,
|
|
265
270
|
options,
|
|
266
|
-
intermediateDeviceStatePushed: false,
|
|
267
271
|
renderQueueDisposed: false,
|
|
268
272
|
clusteredLightReleased: false,
|
|
269
273
|
sunLightRestored: false
|
|
@@ -372,21 +376,48 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
372
376
|
depthFramebufferHandle
|
|
373
377
|
};
|
|
374
378
|
});
|
|
375
|
-
|
|
379
|
+
let depthHandle = depthPassResult.depthHandle;
|
|
376
380
|
const motionVectorHandle = depthPassResult.motionVectorHandle;
|
|
381
|
+
blackboard.set(FrameResources.LinearDepth, depthHandle);
|
|
382
|
+
if (motionVectorHandle) {
|
|
383
|
+
blackboard.set(FrameResources.MotionVector, motionVectorHandle);
|
|
384
|
+
}
|
|
385
|
+
if (depthPassResult.graphDepthAttachmentHandle) {
|
|
386
|
+
blackboard.set(FrameResources.SceneDepthAttachment, depthPassResult.graphDepthAttachmentHandle);
|
|
387
|
+
}
|
|
377
388
|
const renderDepthAttachment = depthPassResult.graphDepthAttachmentHandle ?? depthPassResult.externalDepthAttachment ?? null;
|
|
378
|
-
|
|
389
|
+
// Rendering the scene directly into the final framebuffer is only possible
|
|
390
|
+
// when no opaque-layer effect is enabled: those effects must sample the
|
|
391
|
+
// opaque scene color as a texture and may require surface MRT attachments
|
|
392
|
+
// (SSR roughness / SSS / SkinSSS), which the single-color final framebuffer
|
|
393
|
+
// cannot carry. The legacy compositor.begin() used to redirect rendering to
|
|
394
|
+
// a temporal MRT framebuffer in that case; the graph path uses the regular
|
|
395
|
+
// scene color texture pipeline instead.
|
|
396
|
+
const opaqueLayerHasEffects = !!ctx.compositor?.layerHasEnabledEffect(PostEffectLayer.opaque);
|
|
397
|
+
const useFinalFramebufferAsIntermediate = !!depthPassResult.externalDepthAttachment && depthPassResult.externalDepthAttachment === ctx.finalFramebuffer?.getDepthAttachment() && !opaqueLayerHasEffects;
|
|
379
398
|
let preLightTransmissionDepthToken;
|
|
380
399
|
if (options.needsTransmissionDepthForSSR) {
|
|
381
|
-
|
|
400
|
+
const transmissionDepthResult = graph.addPass('TransmissionDepthForSSR', (builder)=>{
|
|
401
|
+
builder.read(depthHandle);
|
|
382
402
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
403
|
+
// This pass renders transmission geometry into the prepass linear-depth
|
|
404
|
+
// texture: model the mutation as a write so later readers order against
|
|
405
|
+
// it through data flow instead of relying on the token alone.
|
|
406
|
+
const depthOut = builder.write(depthHandle);
|
|
383
407
|
const done = builder.createToken('TransmissionDepthForSSRDone');
|
|
384
408
|
builder.sideEffect();
|
|
385
409
|
builder.setExecute((rgCtx)=>{
|
|
386
410
|
renderTransmissionDepthPass(frame, rgCtx);
|
|
387
411
|
});
|
|
388
|
-
return
|
|
412
|
+
return {
|
|
413
|
+
done,
|
|
414
|
+
depthOut
|
|
415
|
+
};
|
|
389
416
|
});
|
|
417
|
+
preLightTransmissionDepthToken = transmissionDepthResult.done;
|
|
418
|
+
depthHandle = transmissionDepthResult.depthOut;
|
|
419
|
+
// Re-register so blackboard consumers read the post-transmission version.
|
|
420
|
+
blackboard.set(FrameResources.LinearDepth, depthHandle);
|
|
390
421
|
}
|
|
391
422
|
// ── 6. Hi-Z (optional) ───────────────────────────────────────────
|
|
392
423
|
let hiZHandle;
|
|
@@ -398,7 +429,7 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
398
429
|
builder.read(preLightTransmissionDepthToken);
|
|
399
430
|
}
|
|
400
431
|
hiZHandle = builder.createTexture({
|
|
401
|
-
format: '
|
|
432
|
+
format: 'rg32f',
|
|
402
433
|
label: 'hiZ',
|
|
403
434
|
mipLevels: getFullMipLevelCount(ctx.renderWidth, ctx.renderHeight)
|
|
404
435
|
});
|
|
@@ -420,11 +451,13 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
420
451
|
}
|
|
421
452
|
});
|
|
422
453
|
});
|
|
454
|
+
if (hiZHandle) {
|
|
455
|
+
blackboard.set(FrameResources.HiZ, hiZHandle);
|
|
456
|
+
}
|
|
423
457
|
}
|
|
424
458
|
// ── 7. Main Light Pass ────────────────────────────────────────────
|
|
425
459
|
const historyManager = ctx.camera?.getHistoryResourceManager?.() ?? null;
|
|
426
460
|
const lightHistoryReadBindings = [];
|
|
427
|
-
const compositeHistoryReadBindings = [];
|
|
428
461
|
const historySize = {
|
|
429
462
|
width: ctx.renderWidth,
|
|
430
463
|
height: ctx.renderHeight
|
|
@@ -452,29 +485,7 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
452
485
|
});
|
|
453
486
|
}
|
|
454
487
|
}
|
|
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
|
-
}
|
|
488
|
+
// Note: TAA history import/commit is handled by TAA.setup() (self-describing).
|
|
478
489
|
let sssProfileResult;
|
|
479
490
|
if (options.sss) {
|
|
480
491
|
sssProfileResult = graph.addPass('SSSProfile', (builder)=>{
|
|
@@ -524,6 +535,41 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
524
535
|
};
|
|
525
536
|
});
|
|
526
537
|
}
|
|
538
|
+
// 7b. Scene color grab: renders the full scene (no transmission) into a copy
|
|
539
|
+
// texture that transmission/refraction materials sample as background.
|
|
540
|
+
// Extracted from the former monolithic light pass.
|
|
541
|
+
let grabResult;
|
|
542
|
+
if (options.needSceneColor) {
|
|
543
|
+
grabResult = graph.addPass('SceneColorGrab', (builder)=>{
|
|
544
|
+
builder.read(depthHandle);
|
|
545
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
546
|
+
if (preLightTransmissionDepthToken) {
|
|
547
|
+
builder.read(preLightTransmissionDepthToken);
|
|
548
|
+
}
|
|
549
|
+
const copyHandle = builder.createTexture({
|
|
550
|
+
format: ctx.colorFormat,
|
|
551
|
+
label: 'sceneColorCopy'
|
|
552
|
+
});
|
|
553
|
+
// SSR may pre-insert transmission depth before LightPass for Hi-Z. In that
|
|
554
|
+
// case the refraction scene-color copy needs an isolated depth buffer so
|
|
555
|
+
// transmission surfaces do not occlude the background they sample.
|
|
556
|
+
const copyFramebufferHandle = !options.needsTransmissionDepthForSSR ? builder.createFramebuffer({
|
|
557
|
+
label: 'SceneColorCopyFramebuffer',
|
|
558
|
+
width: ctx.renderWidth,
|
|
559
|
+
height: ctx.renderHeight,
|
|
560
|
+
colorAttachments: copyHandle,
|
|
561
|
+
depthAttachment: renderDepthAttachment,
|
|
562
|
+
ignoreDepthStencil: false
|
|
563
|
+
}) : undefined;
|
|
564
|
+
builder.setExecute((rgCtx)=>{
|
|
565
|
+
renderSceneColorGrab(frame, rgCtx, copyHandle, copyFramebufferHandle);
|
|
566
|
+
});
|
|
567
|
+
return {
|
|
568
|
+
copyHandle,
|
|
569
|
+
copyFramebufferHandle
|
|
570
|
+
};
|
|
571
|
+
});
|
|
572
|
+
}
|
|
527
573
|
const lightPassResult = graph.addPass('LightPass', (builder)=>{
|
|
528
574
|
builder.read(depthHandle);
|
|
529
575
|
builder.read(depthPassResult.depthFramebufferHandle);
|
|
@@ -536,18 +582,18 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
536
582
|
for (const binding of lightHistoryReadBindings){
|
|
537
583
|
builder.read(binding.handle);
|
|
538
584
|
}
|
|
539
|
-
//
|
|
540
|
-
|
|
585
|
+
// Scene color: in final-framebuffer-as-intermediate mode the scene is
|
|
586
|
+
// physically rendered into the final framebuffer, so declare the
|
|
587
|
+
// backbuffer write — the graph sees the real data flow and no keep-alive
|
|
588
|
+
// reads are needed downstream. Otherwise render into a graph texture.
|
|
589
|
+
const sceneColorHandle = useFinalFramebufferAsIntermediate ? builder.write(backbuffer) : builder.createTexture({
|
|
541
590
|
format: ctx.colorFormat,
|
|
542
591
|
label: 'sceneColor'
|
|
543
592
|
});
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
format: ctx.colorFormat,
|
|
549
|
-
label: 'sceneColorCopy'
|
|
550
|
-
});
|
|
593
|
+
// Transmission/refraction background produced by the SceneColorGrab pass
|
|
594
|
+
const sceneColorCopyHandle = grabResult?.copyHandle;
|
|
595
|
+
if (sceneColorCopyHandle) {
|
|
596
|
+
builder.read(sceneColorCopyHandle);
|
|
551
597
|
}
|
|
552
598
|
if (sssProfileResult) {
|
|
553
599
|
builder.read(sssProfileResult.profileHandle);
|
|
@@ -557,6 +603,17 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
557
603
|
}
|
|
558
604
|
}
|
|
559
605
|
const includeSSRSurfaceMRT = !!options.ssr;
|
|
606
|
+
// SSR glossy-surface MRT outputs (roughness + world normal) are graph
|
|
607
|
+
// textures owned by this pass; effects reach them through the blackboard
|
|
608
|
+
// handles (or the ctx fields resolved below during execution).
|
|
609
|
+
const ssrRoughnessHandle = includeSSRSurfaceMRT ? builder.createTexture({
|
|
610
|
+
format: getSurfaceTextureFormat(ctx),
|
|
611
|
+
label: 'ssrRoughness'
|
|
612
|
+
}) : undefined;
|
|
613
|
+
const ssrNormalHandle = includeSSRSurfaceMRT ? builder.createTexture({
|
|
614
|
+
format: getSurfaceTextureFormat(ctx),
|
|
615
|
+
label: 'ssrNormal'
|
|
616
|
+
}) : undefined;
|
|
560
617
|
const writeSSSDiffuse = options.sss && shouldStoreSSSDiffuse(ctx);
|
|
561
618
|
let writeSSSTransmission = options.sss && shouldStoreSSSTransmission(ctx);
|
|
562
619
|
if (writeSSSDiffuse && writeSSSTransmission && includeSSRSurfaceMRT && getSSSLightingTextureFormat(ctx, 2, includeSSRSurfaceMRT) !== ctx.colorFormat) {
|
|
@@ -584,20 +641,13 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
584
641
|
colorAttachments: sceneColorHandle,
|
|
585
642
|
depthAttachment: renderDepthAttachment
|
|
586
643
|
});
|
|
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
644
|
builder.setExecute((rgCtx)=>{
|
|
599
645
|
const sceneColorTex = rgCtx.getTexture(sceneColorHandle);
|
|
600
646
|
const sceneColorCopyTex = sceneColorCopyHandle ? rgCtx.getTexture(sceneColorCopyHandle) : null;
|
|
647
|
+
// Resolve MRT products into the DrawContext bridge fields that scene
|
|
648
|
+
// rendering and apply()-based effects still read.
|
|
649
|
+
ctx.SSRRoughnessTexture = ssrRoughnessHandle ? rgCtx.getTexture(ssrRoughnessHandle) : null;
|
|
650
|
+
ctx.SSRNormalTexture = ssrNormalHandle ? rgCtx.getTexture(ssrNormalHandle) : null;
|
|
601
651
|
if (sssProfileResult) {
|
|
602
652
|
ctx.SSSProfileTexture = rgCtx.getTexture(sssProfileResult.profileHandle);
|
|
603
653
|
ctx.SSSParamTexture = rgCtx.getTexture(sssProfileResult.paramHandle);
|
|
@@ -608,7 +658,7 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
608
658
|
ctx.SSSDiffuseTexture = sssDiffuseHandle ? rgCtx.getTexture(sssDiffuseHandle) : null;
|
|
609
659
|
ctx.SSSTransmissionTexture = sssTransmissionHandle ? rgCtx.getTexture(sssTransmissionHandle) : null;
|
|
610
660
|
ctx.SkinSSSTexture = skinSSSHandle ? rgCtx.getTexture(skinSSSHandle) : null;
|
|
611
|
-
const renderLightPass = ()=>
|
|
661
|
+
const renderLightPass = ()=>renderOpaqueScenePass(frame, sceneColorTex, sceneColorCopyTex, rgCtx, sceneColorFramebufferHandle);
|
|
612
662
|
if (historyManager && lightHistoryReadBindings.length > 0) {
|
|
613
663
|
historyManager.beginReadScope(lightHistoryReadBindings.map((binding)=>({
|
|
614
664
|
name: binding.name,
|
|
@@ -626,66 +676,247 @@ function buildForwardPlusGraphInternal(graph, ctx, renderQueue, options) {
|
|
|
626
676
|
return {
|
|
627
677
|
sceneColorHandle,
|
|
628
678
|
sceneColorCopyHandle,
|
|
629
|
-
sceneColorFramebufferHandle
|
|
679
|
+
sceneColorFramebufferHandle,
|
|
680
|
+
ssrRoughnessHandle,
|
|
681
|
+
ssrNormalHandle,
|
|
682
|
+
sssDiffuseHandle,
|
|
683
|
+
sssTransmissionHandle,
|
|
684
|
+
skinSSSHandle
|
|
630
685
|
};
|
|
631
686
|
});
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
687
|
+
// Register the LightPass MRT products so effects can look them up by name.
|
|
688
|
+
if (lightPassResult.ssrRoughnessHandle) {
|
|
689
|
+
blackboard.set(FrameResources.SSRRoughness, lightPassResult.ssrRoughnessHandle);
|
|
690
|
+
}
|
|
691
|
+
if (lightPassResult.ssrNormalHandle) {
|
|
692
|
+
blackboard.set(FrameResources.SSRNormal, lightPassResult.ssrNormalHandle);
|
|
693
|
+
} else if (sssProfileResult?.normalHandle) {
|
|
694
|
+
blackboard.set(FrameResources.SSRNormal, sssProfileResult.normalHandle);
|
|
695
|
+
}
|
|
696
|
+
if (lightPassResult.sssDiffuseHandle) {
|
|
697
|
+
blackboard.set(FrameResources.SSSDiffuse, lightPassResult.sssDiffuseHandle);
|
|
698
|
+
}
|
|
699
|
+
if (lightPassResult.sssTransmissionHandle) {
|
|
700
|
+
blackboard.set(FrameResources.SSSTransmission, lightPassResult.sssTransmissionHandle);
|
|
701
|
+
}
|
|
702
|
+
if (lightPassResult.skinSSSHandle) {
|
|
703
|
+
blackboard.set(FrameResources.SkinSSS, lightPassResult.skinSSSHandle);
|
|
704
|
+
}
|
|
705
|
+
// 7d. Opaque-layer post effects (SAO/SSR/SSS/SkinSSS). They read the opaque
|
|
706
|
+
// scene color and must complete before transparent geometry renders on top
|
|
707
|
+
// of their output. Never direct-write: the chain output becomes the
|
|
708
|
+
// transparent pass's render target. In final-framebuffer-as-intermediate
|
|
709
|
+
// mode the scene color handle is the LightPass's backbuffer write version,
|
|
710
|
+
// so the data flow is real either way — no keep-alive reads needed.
|
|
711
|
+
const opaqueChainInput = lightPassResult.sceneColorHandle;
|
|
712
|
+
// Data dependencies for every effect pass: frame textures the effects sample
|
|
713
|
+
// through DrawContext fields (linear depth, HiZ, scene color copy, SSS MRT
|
|
714
|
+
// outputs) rather than through declared require* hooks.
|
|
715
|
+
const opaqueChainDeps = [
|
|
716
|
+
depthHandle
|
|
717
|
+
];
|
|
718
|
+
if (hiZHandle) {
|
|
719
|
+
opaqueChainDeps.push(hiZHandle);
|
|
720
|
+
}
|
|
721
|
+
if (lightPassResult.sceneColorFramebufferHandle) {
|
|
722
|
+
opaqueChainDeps.push(lightPassResult.sceneColorFramebufferHandle);
|
|
723
|
+
}
|
|
724
|
+
if (grabResult) {
|
|
725
|
+
opaqueChainDeps.push(grabResult.copyHandle);
|
|
646
726
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
727
|
+
if (sssProfileResult) {
|
|
728
|
+
opaqueChainDeps.push(sssProfileResult.profileHandle, sssProfileResult.paramHandle);
|
|
729
|
+
if (sssProfileResult.normalHandle) {
|
|
730
|
+
opaqueChainDeps.push(sssProfileResult.normalHandle);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
for (const handle of [
|
|
734
|
+
lightPassResult.ssrRoughnessHandle,
|
|
735
|
+
lightPassResult.ssrNormalHandle,
|
|
736
|
+
lightPassResult.sssDiffuseHandle,
|
|
737
|
+
lightPassResult.sssTransmissionHandle,
|
|
738
|
+
lightPassResult.skinSSSHandle
|
|
739
|
+
]){
|
|
740
|
+
if (handle) {
|
|
741
|
+
opaqueChainDeps.push(handle);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const opaqueChainResult = ctx.compositor ? ctx.compositor.buildLayer({
|
|
745
|
+
graph,
|
|
746
|
+
ctx,
|
|
747
|
+
layer: PostEffectLayer.opaque,
|
|
748
|
+
blackboard,
|
|
749
|
+
input: opaqueChainInput,
|
|
750
|
+
finalOutput: null,
|
|
751
|
+
sceneDepthAttachment: renderDepthAttachment,
|
|
752
|
+
dependencies: opaqueChainDeps,
|
|
753
|
+
historyReads: lightHistoryReadBindings,
|
|
754
|
+
history: historyManager
|
|
755
|
+
}) : {
|
|
756
|
+
color: opaqueChainInput,
|
|
757
|
+
wroteFinal: false
|
|
758
|
+
};
|
|
759
|
+
const opaqueChainRan = opaqueChainResult.color !== opaqueChainInput;
|
|
760
|
+
// 7e. Transparent scene geometry (transmission/transparent lists + OIT).
|
|
761
|
+
// Renders on top of the opaque-chain output; graph-wise an in-place write
|
|
762
|
+
// producing a new version of the current scene color.
|
|
763
|
+
const sceneColorHandle = graph.addPass('TransparentPass', (builder)=>{
|
|
650
764
|
builder.read(depthHandle);
|
|
765
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
651
766
|
if (hiZHandle) {
|
|
767
|
+
// Transparent-phase materials may ray-march HiZ (e.g. water SSR)
|
|
652
768
|
builder.read(hiZHandle);
|
|
653
769
|
}
|
|
654
|
-
if (motionVectorHandle) {
|
|
655
|
-
builder.read(motionVectorHandle);
|
|
656
|
-
}
|
|
657
770
|
if (lightPassResult.sceneColorFramebufferHandle) {
|
|
658
771
|
builder.read(lightPassResult.sceneColorFramebufferHandle);
|
|
659
772
|
}
|
|
660
|
-
if (
|
|
661
|
-
builder.read(
|
|
662
|
-
}
|
|
663
|
-
for (const binding of compositeHistoryReadBindings){
|
|
664
|
-
builder.read(binding.handle);
|
|
773
|
+
if (lightPassResult.sceneColorCopyHandle) {
|
|
774
|
+
builder.read(lightPassResult.sceneColorCopyHandle);
|
|
665
775
|
}
|
|
666
|
-
|
|
667
|
-
const
|
|
776
|
+
builder.read(opaqueChainResult.color);
|
|
777
|
+
const out = builder.write(opaqueChainResult.color);
|
|
668
778
|
builder.setExecute((rgCtx)=>{
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
779
|
+
renderTransparentScenePass(frame, rgCtx, opaqueChainRan ? opaqueChainResult.color : null, lightPassResult.sceneColorFramebufferHandle);
|
|
780
|
+
});
|
|
781
|
+
return out;
|
|
782
|
+
});
|
|
783
|
+
blackboard.set(FrameResources.SceneColor, sceneColorHandle);
|
|
784
|
+
if (lightPassResult.sceneColorCopyHandle) {
|
|
785
|
+
blackboard.set(FrameResources.SceneColorCopy, lightPassResult.sceneColorCopyHandle);
|
|
786
|
+
}
|
|
787
|
+
if (sssProfileResult) {
|
|
788
|
+
blackboard.set(FrameResources.SSSProfile, sssProfileResult.profileHandle);
|
|
789
|
+
blackboard.set(FrameResources.SSSParam, sssProfileResult.paramHandle);
|
|
790
|
+
}
|
|
791
|
+
// 8. Post effect chains + transmission depth.
|
|
792
|
+
//
|
|
793
|
+
// Chain input: the TransparentPass output version — the authoritative scene
|
|
794
|
+
// color regardless of whether it physically lives in a texture or in the
|
|
795
|
+
// backbuffer (final framebuffer used as intermediate, no opaque effects).
|
|
796
|
+
const chainInput = sceneColorHandle;
|
|
797
|
+
// When the scene color still physically resides in the final framebuffer,
|
|
798
|
+
// the Present blit must be skipped if no effect moved it to a texture.
|
|
799
|
+
// (Opaque-layer effects disable final-as-intermediate mode, so the scene
|
|
800
|
+
// color is backbuffer-resident whenever that mode is active.)
|
|
801
|
+
const backbufferResidentHandle = useFinalFramebufferAsIntermediate ? sceneColorHandle : null;
|
|
802
|
+
// No extra chain dependencies: the effect chains link to the scene color
|
|
803
|
+
// through their inputs, and per-effect texture needs are declared via the
|
|
804
|
+
// require* hooks in AbstractPostEffect.setup().
|
|
805
|
+
const chainDependencies = [];
|
|
806
|
+
const finalOutput = {
|
|
807
|
+
handle: backbuffer,
|
|
808
|
+
isScreen: !ctx.finalFramebuffer
|
|
809
|
+
};
|
|
810
|
+
const endLayerHasEffects = !!ctx.compositor?.layerHasEnabledEffect(PostEffectLayer.end);
|
|
811
|
+
// 8a. Transparent-layer effects (bloom, tonemap, FXAA, ...). They run right
|
|
812
|
+
// after the light pass and must sample the pre-transmission linear depth, so
|
|
813
|
+
// they carry no transmissionDepthToken dependency; TransmissionDepth is
|
|
814
|
+
// instead ordered after this chain (see 8b).
|
|
815
|
+
const transparentChainResult = ctx.compositor ? ctx.compositor.buildLayer({
|
|
816
|
+
graph,
|
|
817
|
+
ctx,
|
|
818
|
+
layer: PostEffectLayer.transparent,
|
|
819
|
+
blackboard,
|
|
820
|
+
input: chainInput,
|
|
821
|
+
finalOutput: endLayerHasEffects ? null : finalOutput,
|
|
822
|
+
inputResidesInFinalTarget: !!backbufferResidentHandle,
|
|
823
|
+
sceneDepthAttachment: renderDepthAttachment,
|
|
824
|
+
dependencies: chainDependencies,
|
|
825
|
+
history: historyManager
|
|
826
|
+
}) : {
|
|
827
|
+
color: chainInput,
|
|
828
|
+
wroteFinal: false
|
|
829
|
+
};
|
|
830
|
+
// 8b. Transmission depth pass (optional). Mutates the linear depth texture;
|
|
831
|
+
// the mutation is modeled as a graph write: the WAR hazard orders this pass
|
|
832
|
+
// after every pre-transmission depth reader (the transparent-layer chain),
|
|
833
|
+
// and re-registering the post-write version in the blackboard gives
|
|
834
|
+
// end-layer effects (TAA) a real data dependency on the transmission depth.
|
|
835
|
+
let transmissionDepthToken;
|
|
836
|
+
if (options.needSceneColor && !options.needsTransmissionDepthForSSR) {
|
|
837
|
+
const transmissionDepthResult = graph.addPass('TransmissionDepth', (builder)=>{
|
|
838
|
+
builder.read(sceneColorHandle);
|
|
839
|
+
if (transparentChainResult.color !== sceneColorHandle) {
|
|
840
|
+
builder.read(transparentChainResult.color);
|
|
841
|
+
}
|
|
842
|
+
builder.read(depthHandle);
|
|
843
|
+
builder.read(depthPassResult.depthFramebufferHandle);
|
|
844
|
+
const depthOut = builder.write(depthHandle);
|
|
845
|
+
const done = builder.createToken('TransmissionDepthDone');
|
|
846
|
+
builder.sideEffect();
|
|
847
|
+
builder.setExecute((rgCtx)=>{
|
|
848
|
+
renderTransmissionDepthPass(frame, rgCtx);
|
|
849
|
+
});
|
|
850
|
+
return {
|
|
851
|
+
done,
|
|
852
|
+
depthOut
|
|
672
853
|
};
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
854
|
+
});
|
|
855
|
+
transmissionDepthToken = transmissionDepthResult.done;
|
|
856
|
+
depthHandle = transmissionDepthResult.depthOut;
|
|
857
|
+
// The transparent-layer chain above read the pre-transmission version;
|
|
858
|
+
// everything built from here on (end-layer chain) reads this one.
|
|
859
|
+
blackboard.set(FrameResources.LinearDepth, depthHandle);
|
|
860
|
+
}
|
|
861
|
+
// 9. End-layer effects (TAA). Ordered after TransmissionDepth.
|
|
862
|
+
const endChainDependencies = transmissionDepthToken ? [
|
|
863
|
+
...chainDependencies,
|
|
864
|
+
transmissionDepthToken
|
|
865
|
+
] : chainDependencies;
|
|
866
|
+
const chainResult = ctx.compositor ? ctx.compositor.buildLayer({
|
|
867
|
+
graph,
|
|
868
|
+
ctx,
|
|
869
|
+
layer: PostEffectLayer.end,
|
|
870
|
+
blackboard,
|
|
871
|
+
input: transparentChainResult.color,
|
|
872
|
+
finalOutput,
|
|
873
|
+
// Still backbuffer-resident if the transparent-layer chain ran no effect
|
|
874
|
+
inputResidesInFinalTarget: !!backbufferResidentHandle && transparentChainResult.color === chainInput,
|
|
875
|
+
sceneDepthAttachment: renderDepthAttachment,
|
|
876
|
+
dependencies: endChainDependencies,
|
|
877
|
+
history: historyManager
|
|
878
|
+
}) : {
|
|
879
|
+
color: transparentChainResult.color,
|
|
880
|
+
wroteFinal: false
|
|
881
|
+
};
|
|
882
|
+
const finalWroteFinal = chainResult.wroteFinal || transparentChainResult.wroteFinal;
|
|
883
|
+
// 10. Present + frame cleanup.
|
|
884
|
+
let presentedBackbuffer;
|
|
885
|
+
if (finalWroteFinal) {
|
|
886
|
+
// The last effect wrote the final target directly; only cleanup remains.
|
|
887
|
+
presentedBackbuffer = chainResult.color;
|
|
888
|
+
graph.addPass('FrameCleanup', (builder)=>{
|
|
889
|
+
builder.read(presentedBackbuffer);
|
|
890
|
+
for (const dep of endChainDependencies){
|
|
891
|
+
builder.read(dep);
|
|
685
892
|
}
|
|
893
|
+
builder.sideEffect();
|
|
894
|
+
builder.setExecute(()=>{
|
|
895
|
+
finishFrame(frame);
|
|
896
|
+
});
|
|
686
897
|
});
|
|
687
|
-
|
|
688
|
-
|
|
898
|
+
} else {
|
|
899
|
+
presentedBackbuffer = graph.addPass('Present', (builder)=>{
|
|
900
|
+
builder.read(chainResult.color);
|
|
901
|
+
for (const dep of endChainDependencies){
|
|
902
|
+
builder.read(dep);
|
|
903
|
+
}
|
|
904
|
+
const outputBackbuffer = builder.write(backbuffer);
|
|
905
|
+
// Skip the blit when the chain output already lives in the final target
|
|
906
|
+
// (final framebuffer used as intermediate and no end-layer effect ran).
|
|
907
|
+
const needsBlit = chainResult.color !== backbufferResidentHandle;
|
|
908
|
+
builder.setExecute((rgCtx)=>{
|
|
909
|
+
const sourceTex = needsBlit ? rgCtx.getTexture(chainResult.color) : null;
|
|
910
|
+
if (sourceTex) {
|
|
911
|
+
const blitter = new CopyBlitter();
|
|
912
|
+
blitter.srgbOut = !ctx.finalFramebuffer;
|
|
913
|
+
blitter.blit(sourceTex, ctx.finalFramebuffer ?? null, fetchSampler('clamp_nearest_nomip'));
|
|
914
|
+
}
|
|
915
|
+
finishFrame(frame);
|
|
916
|
+
});
|
|
917
|
+
return outputBackbuffer;
|
|
918
|
+
});
|
|
919
|
+
}
|
|
689
920
|
return {
|
|
690
921
|
backbuffer: presentedBackbuffer,
|
|
691
922
|
frame
|
|
@@ -766,12 +997,9 @@ function renderForwardSSSProfile(frame, profileFramebuffer, profileTexture, para
|
|
|
766
997
|
}
|
|
767
998
|
}
|
|
768
999
|
function releaseIntermediateFramebuffer(frame) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
frame.intermediateDeviceStatePushed = false;
|
|
773
|
-
}
|
|
774
|
-
ctx.intermediateFramebuffer = null;
|
|
1000
|
+
// Device state is contained within each pass now (LightPass/TransparentPass
|
|
1001
|
+
// push/pop their own framebuffer bindings); only the context field remains.
|
|
1002
|
+
frame.ctx.intermediateFramebuffer = null;
|
|
775
1003
|
}
|
|
776
1004
|
function releaseDepthFramebuffer(frame) {
|
|
777
1005
|
frame.depthFramebuffer = null;
|
|
@@ -965,9 +1193,58 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
965
1193
|
const destination = framebuffer?.getColorAttachment(0) ?? null;
|
|
966
1194
|
new CopyBlitter().blit(source, destination, fetchSampler('clamp_nearest_nomip'));
|
|
967
1195
|
}
|
|
968
|
-
/**
|
|
1196
|
+
/**
|
|
1197
|
+
* Renders the full scene (no transmission) into the scene-color copy texture
|
|
1198
|
+
* used as refraction background. Runs as its own graph pass before LightPass.
|
|
1199
|
+
* @internal
|
|
1200
|
+
*/ function renderSceneColorGrab(frame, rgCtx, copyHandle, copyFramebufferHandle) {
|
|
969
1201
|
const { ctx, renderQueue } = frame;
|
|
970
1202
|
const device = ctx.device;
|
|
1203
|
+
const depthTex = frame.depthFramebuffer?.getDepthAttachment();
|
|
1204
|
+
const copyTex = rgCtx.getTexture(copyHandle);
|
|
1205
|
+
const compositor = ctx.compositor;
|
|
1206
|
+
ctx.compositor = null;
|
|
1207
|
+
const isolateSceneColorDepth = frame.options.needsTransmissionDepthForSSR;
|
|
1208
|
+
const savedDepthPrepassAttachment = ctx.depthPrepassAttachment;
|
|
1209
|
+
const savedMaterialFlags = ctx.materialFlags;
|
|
1210
|
+
// MRT store flags never apply to the background copy
|
|
1211
|
+
const sceneColorMaterialFlags = ctx.materialFlags & ~SURFACE_MRT_FLAGS;
|
|
1212
|
+
const sceneColorFramebuffer = copyFramebufferHandle ? rgCtx.getFramebuffer(copyFramebufferHandle) : rgCtx.createFramebuffer({
|
|
1213
|
+
width: copyTex.width,
|
|
1214
|
+
height: copyTex.height,
|
|
1215
|
+
colorAttachments: copyTex,
|
|
1216
|
+
depthAttachment: isolateSceneColorDepth ? ctx.depthFormat : depthTex,
|
|
1217
|
+
ignoreDepthStencil: false
|
|
1218
|
+
});
|
|
1219
|
+
let sceneColorStatePushed = false;
|
|
1220
|
+
try {
|
|
1221
|
+
device.pushDeviceStates();
|
|
1222
|
+
sceneColorStatePushed = true;
|
|
1223
|
+
device.setFramebuffer(sceneColorFramebuffer);
|
|
1224
|
+
_scenePass.transmission = false;
|
|
1225
|
+
if (isolateSceneColorDepth) {
|
|
1226
|
+
ctx.depthPrepassAttachment = undefined;
|
|
1227
|
+
_scenePass.clearDepth = 1;
|
|
1228
|
+
_scenePass.clearStencil = 0;
|
|
1229
|
+
} else {
|
|
1230
|
+
_scenePass.clearDepth = depthTex ? null : 1;
|
|
1231
|
+
_scenePass.clearStencil = depthTex ? null : 0;
|
|
1232
|
+
}
|
|
1233
|
+
ctx.materialFlags = sceneColorMaterialFlags;
|
|
1234
|
+
_scenePass.render(ctx, null, null, renderQueue);
|
|
1235
|
+
} finally{
|
|
1236
|
+
ctx.materialFlags = savedMaterialFlags;
|
|
1237
|
+
if (isolateSceneColorDepth) {
|
|
1238
|
+
ctx.depthPrepassAttachment = savedDepthPrepassAttachment;
|
|
1239
|
+
}
|
|
1240
|
+
if (sceneColorStatePushed) {
|
|
1241
|
+
device.popDeviceStates();
|
|
1242
|
+
}
|
|
1243
|
+
ctx.compositor = compositor;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
/** @internal */ function renderOpaqueScenePass(frame, sceneColorTex, sceneColorCopyTex, rgCtx, sceneColorFramebufferHandle) {
|
|
1247
|
+
const { ctx, renderQueue } = frame;
|
|
971
1248
|
// Use RenderGraph-allocated scene color texture
|
|
972
1249
|
const depthTex = frame.depthFramebuffer?.getDepthAttachment();
|
|
973
1250
|
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 +1262,15 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
985
1262
|
if (ctx.SkinSSSTexture) {
|
|
986
1263
|
ctx.materialFlags |= MaterialVaryingFlags.SKIN_SSS_STORE;
|
|
987
1264
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1265
|
+
// The graph scene color framebuffer takes priority: it is absent only when
|
|
1266
|
+
// the graph was built in final-framebuffer-as-intermediate mode (external
|
|
1267
|
+
// depth shared with the final framebuffer AND no opaque-layer effects).
|
|
1268
|
+
// Checking the shared depth first would wrongly route the MRT/opaque-effect
|
|
1269
|
+
// case into the single-color final framebuffer.
|
|
1270
|
+
if (sceneColorFramebufferHandle && !hasSurfaceMRT(ctx)) {
|
|
991
1271
|
ctx.intermediateFramebuffer = rgCtx.getFramebuffer(sceneColorFramebufferHandle);
|
|
1272
|
+
} else if (!sceneColorFramebufferHandle && depthTex === ctx.finalFramebuffer?.getDepthAttachment()) {
|
|
1273
|
+
ctx.intermediateFramebuffer = ctx.finalFramebuffer;
|
|
992
1274
|
} else {
|
|
993
1275
|
ctx.intermediateFramebuffer = rgCtx.createFramebuffer({
|
|
994
1276
|
width: sceneColorTex.width,
|
|
@@ -997,104 +1279,122 @@ function blitToCurrentColorAttachment(ctx, source) {
|
|
|
997
1279
|
depthAttachment: depthTex
|
|
998
1280
|
});
|
|
999
1281
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1282
|
+
// The scene target is bound explicitly and the device state is restored at
|
|
1283
|
+
// the end of this pass: graph passes never communicate through leftover
|
|
1284
|
+
// device state. TransparentPass re-binds the same target (or the opaque
|
|
1285
|
+
// chain output) explicitly.
|
|
1286
|
+
ctx.device.pushDeviceStates();
|
|
1287
|
+
try {
|
|
1288
|
+
// setFramebuffer() no-ops when the target is unchanged, so reset the
|
|
1289
|
+
// viewport/scissor explicitly to cover that case.
|
|
1290
|
+
ctx.device.setFramebuffer(ctx.intermediateFramebuffer);
|
|
1291
|
+
ctx.device.setViewport(null);
|
|
1292
|
+
ctx.device.setScissor(null);
|
|
1293
|
+
_scenePass.transmission = false;
|
|
1294
|
+
_scenePass.clearDepth = depthTex ? null : 1;
|
|
1295
|
+
_scenePass.clearStencil = depthTex ? null : 0;
|
|
1296
|
+
if (renderQueue.needSceneColor() && sceneColorCopyTex) {
|
|
1297
|
+
// Background copy was produced by the SceneColorGrab pass; seed the main
|
|
1298
|
+
// color attachment with it and render only transmission/transparent on top.
|
|
1299
|
+
ctx.sceneColorTexture = sceneColorCopyTex;
|
|
1300
|
+
blitToCurrentColorAttachment(ctx, ctx.sceneColorTexture);
|
|
1301
|
+
if (hasSurfaceMRT(ctx)) {
|
|
1302
|
+
// The background copy carries no surface MRT attachments, so opaque
|
|
1303
|
+
// geometry exists only there without roughness/normal (and SSS
|
|
1304
|
+
// lighting) data. Re-render the opaque lists into the MRT scene
|
|
1305
|
+
// target: early-z against the prepass depth keeps this cheap, and the
|
|
1306
|
+
// color output matches the blitted copy on opaque pixels while the
|
|
1307
|
+
// MRT attachments receive the surface data SSR/SSS require.
|
|
1308
|
+
_scenePass.clearColor = null;
|
|
1309
|
+
_scenePass.clearDepth = null;
|
|
1310
|
+
_scenePass.clearStencil = null;
|
|
1311
|
+
_scenePass.renderOpaque = true;
|
|
1312
|
+
_scenePass.renderTransparent = false;
|
|
1313
|
+
_scenePass.render(ctx, null, null, renderQueue);
|
|
1314
|
+
}
|
|
1315
|
+
_scenePass.transmission = true;
|
|
1316
|
+
_scenePass.clearColor = null;
|
|
1317
|
+
_scenePass.clearDepth = null;
|
|
1318
|
+
_scenePass.clearStencil = null;
|
|
1319
|
+
}
|
|
1320
|
+
_scenePass.renderOpaque = true;
|
|
1321
|
+
_scenePass.renderTransparent = false;
|
|
1322
|
+
_scenePass.render(ctx, null, null, renderQueue);
|
|
1323
|
+
_scenePass.renderTransparent = true;
|
|
1324
|
+
} finally{
|
|
1325
|
+
ctx.device.popDeviceStates();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Renders the transmission/transparent geometry lists (including OIT) on top
|
|
1330
|
+
* of the opaque result. The target framebuffer is always bound explicitly:
|
|
1331
|
+
* the opaque chain output when opaque-layer effects ran, otherwise the same
|
|
1332
|
+
* scene target the light pass rendered into. OIT implementations composite
|
|
1333
|
+
* into the currently bound framebuffer, which this pass guarantees.
|
|
1334
|
+
* @internal
|
|
1335
|
+
*/ function renderTransparentScenePass(frame, rgCtx, opaqueChainOutput, sceneColorFramebufferHandle) {
|
|
1336
|
+
const { ctx, renderQueue } = frame;
|
|
1337
|
+
const device = ctx.device;
|
|
1338
|
+
let framebuffer;
|
|
1339
|
+
if (opaqueChainOutput) {
|
|
1340
|
+
// Opaque-layer effects redirected the scene color into their chain output;
|
|
1341
|
+
// transparent geometry renders on top of it with the scene depth attached.
|
|
1342
|
+
const chainTex = rgCtx.getTexture(opaqueChainOutput);
|
|
1343
|
+
const depthTex = frame.depthFramebuffer?.getDepthAttachment();
|
|
1344
|
+
framebuffer = rgCtx.createFramebuffer({
|
|
1345
|
+
width: chainTex.width,
|
|
1346
|
+
height: chainTex.height,
|
|
1347
|
+
colorAttachments: chainTex,
|
|
1348
|
+
depthAttachment: depthTex
|
|
1349
|
+
});
|
|
1350
|
+
// The chain output is single-color: surface MRT stores no longer apply.
|
|
1351
|
+
ctx.materialFlags &= ~SURFACE_MRT_FLAGS;
|
|
1352
|
+
} else if (sceneColorFramebufferHandle) {
|
|
1353
|
+
// No opaque-layer effect ran (thus no surface MRT either): continue in the
|
|
1354
|
+
// graph scene color framebuffer the light pass rendered into.
|
|
1355
|
+
framebuffer = rgCtx.getFramebuffer(sceneColorFramebufferHandle);
|
|
1004
1356
|
} else {
|
|
1357
|
+
// Final framebuffer used as scene intermediate.
|
|
1358
|
+
framebuffer = ctx.finalFramebuffer;
|
|
1359
|
+
}
|
|
1360
|
+
device.pushDeviceStates();
|
|
1361
|
+
try {
|
|
1362
|
+
// setFramebuffer() no-ops when the target is unchanged, so reset the
|
|
1363
|
+
// viewport/scissor explicitly to cover that case.
|
|
1364
|
+
device.setFramebuffer(framebuffer);
|
|
1005
1365
|
device.setViewport(null);
|
|
1006
1366
|
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;
|
|
1367
|
+
// _scenePass.transmission carries over from the opaque phase (true when the
|
|
1368
|
+
// scene color copy seeded the background, false otherwise). Never clear:
|
|
1369
|
+
// the opaque result is already in the target.
|
|
1370
|
+
_scenePass.clearColor = null;
|
|
1371
|
+
_scenePass.clearDepth = null;
|
|
1372
|
+
_scenePass.clearStencil = null;
|
|
1373
|
+
_scenePass.renderOpaque = false;
|
|
1374
|
+
_scenePass.renderTransparent = true;
|
|
1030
1375
|
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
1376
|
_scenePass.render(ctx, null, null, renderQueue);
|
|
1042
1377
|
} 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;
|
|
1378
|
+
_scenePass.renderOpaque = true;
|
|
1053
1379
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
_scenePass.transmission = true;
|
|
1057
|
-
_scenePass.clearColor = null;
|
|
1058
|
-
_scenePass.clearDepth = null;
|
|
1059
|
-
_scenePass.clearStencil = null;
|
|
1380
|
+
} finally{
|
|
1381
|
+
device.popDeviceStates();
|
|
1060
1382
|
}
|
|
1061
|
-
_scenePass.render(ctx, null, null, renderQueue);
|
|
1062
1383
|
}
|
|
1063
1384
|
/** @internal */ function renderTransmissionDepthPass(frame, rgCtx) {
|
|
1064
1385
|
renderSceneDepth(frame, frame.depthFramebuffer, rgCtx);
|
|
1065
1386
|
}
|
|
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) {
|
|
1387
|
+
/**
|
|
1388
|
+
* Frame-tail housekeeping shared by the Present and FrameCleanup passes.
|
|
1389
|
+
*
|
|
1390
|
+
* The end-layer post effect chain and the final blit are graph passes now
|
|
1391
|
+
* (see buildForwardPlusGraphInternal step 9/10); this only releases per-frame
|
|
1392
|
+
* state. The compositor's scene ping-pong is flushed back to the intermediate
|
|
1393
|
+
* framebuffer by compositor.end() at the tail of the light pass.
|
|
1394
|
+
*
|
|
1395
|
+
* @internal
|
|
1396
|
+
*/ function finishFrame(frame) {
|
|
1095
1397
|
const { ctx } = frame;
|
|
1096
|
-
ctx.compositor?.drawPostEffects(ctx, PostEffectLayer.end, ctx.linearDepthTexture);
|
|
1097
|
-
ctx.compositor?.end(ctx);
|
|
1098
1398
|
disposeRenderQueue(frame);
|
|
1099
1399
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSR_STORE_ROUGHNESS;
|
|
1100
1400
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSS_STORE_PROFILE;
|
|
@@ -1102,12 +1402,6 @@ function queueTAAHistoryCommit(frame, rgCtx, historyManager, colorHandle, motion
|
|
|
1102
1402
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSS_STORE_NORMAL;
|
|
1103
1403
|
ctx.materialFlags &= ~MaterialVaryingFlags.SSS_STORE_TRANSMISSION;
|
|
1104
1404
|
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
1405
|
releaseIntermediateFramebuffer(frame);
|
|
1112
1406
|
releaseDepthFramebuffer(frame);
|
|
1113
1407
|
releaseClusteredLight(frame);
|