Skip to content

Commit 49399b2

Browse files
committed
Make writing to gBuffer more robust
Closes #708
1 parent ced0567 commit 49399b2

6 files changed

Lines changed: 152 additions & 74 deletions

File tree

src/materials/shaders/buffer-debug.frag

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <pp_input_buffer_pars_fragment>
88
#include <pp_depth_buffer_pars_fragment>
99
#include <pp_depth_utils_pars_fragment>
10+
#include <pp_normal_codec_pars_fragment>
1011
#include <pp_normal_utils_pars_fragment>
1112
#include <pp_world_utils_pars_fragment>
1213

src/materials/shaders/effect.frag

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <pp_depth_buffer_precision_pars_fragment>
99
#include <pp_depth_utils_pars_fragment>
1010
#include <pp_frame_buffer_precision_pars_fragment>
11+
#include <pp_normal_codec_pars_fragment>
1112
#include <pp_normal_utils_pars_fragment>
1213
#include <pp_resolution_pars_fragment>
1314

src/passes/GeometryPass.ts

Lines changed: 11 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ import {
1616
RGFormat,
1717
SRGBColorSpace,
1818
Scene,
19-
ShaderMaterial,
2019
TextureDataType,
2120
UnsignedByteType,
2221
UnsignedInt248Type,
23-
WebGLProgramParametersWithUniforms,
2422
WebGLRenderTarget,
2523
WebGLRenderer
2624
} from "three";
@@ -32,10 +30,11 @@ import { GBuffer } from "../enums/GBuffer.js";
3230
import { MSAASamples } from "../enums/MSAASamples.js";
3331
import { GBufferConfig } from "../utils/gbuffer/GBufferConfig.js";
3432
import { GBufferTextureConfig } from "../utils/gbuffer/GBufferTextureConfig.js";
35-
import { extractIndices, extractOutputDefinitions } from "../utils/gbuffer/GBufferUtils.js";
33+
import { extractIndices } from "../utils/gbuffer/GBufferUtils.js";
3634
import { ObservableSet } from "../utils/ObservableSet.js";
3735
import { Selection } from "../utils/Selection.js";
3836
import { CopyPass } from "./CopyPass.js";
37+
import { GBufferShaderPlugin } from "../utils/gbuffer/GBufferShaderPlugin.js";
3938

4039
/**
4140
* GeometryPass constructor options.
@@ -110,16 +109,16 @@ export class GeometryPass extends Pass implements GeometryPassOptions, Selective
110109
readonly selection: Selection;
111110

112111
/**
113-
* A collection of materials that have been modified with `onBeforeCompile`.
112+
* A pass that copies the default input buffer to the output color buffer.
114113
*/
115114

116-
private static readonly registeredMaterials = new WeakSet<Material>();
115+
private readonly copyPass: CopyPass;
117116

118117
/**
119-
* A pass that copies the default input buffer to the output color buffer.
118+
* A shader plugin that enables rendering to G-Buffer render targets.
120119
*/
121120

122-
private readonly copyPass: CopyPass;
121+
private readonly gBufferShaderPlugin: GBufferShaderPlugin;
123122

124123
/**
125124
* A resource that wraps the G-Buffer.
@@ -188,6 +187,7 @@ export class GeometryPass extends Pass implements GeometryPassOptions, Selective
188187
const gBufferComponents = new ObservableSet<GBuffer | string>();
189188
gBufferComponents.addEventListener("change", () => this.updateGBuffer());
190189
this.gBufferComponents = gBufferComponents;
190+
this.gBufferShaderPlugin = new GBufferShaderPlugin();
191191
this.gBufferResource = new RenderTargetResource();
192192
this.output.defaultBuffer = this.gBufferResource;
193193

@@ -325,11 +325,9 @@ export class GeometryPass extends Pass implements GeometryPassOptions, Selective
325325
}
326326

327327
/**
328-
* Enables rendering to {@link GBuffer} components for the materials of a given mesh.
328+
* Enables rendering to {@link GBuffer} components for the materials of a given object.
329329
*
330-
* Should be called when a material is added, removed or replaced at runtime.
331-
*
332-
* TODO Remove when `three` supports output layout definitions for MRT.
330+
* Should also be called when a material is added, removed or replaced at runtime.
333331
*
334332
* @param object - The object to update.
335333
*/
@@ -346,61 +344,7 @@ export class GeometryPass extends Pass implements GeometryPassOptions, Selective
346344

347345
for(const material of materials) {
348346

349-
if(GeometryPass.registeredMaterials.has(material)) {
350-
351-
return;
352-
353-
}
354-
355-
GeometryPass.registeredMaterials.add(material);
356-
357-
/* eslint-disable @typescript-eslint/unbound-method */
358-
const onBeforeCompile = material.onBeforeCompile;
359-
const customProgramCacheKey = material.customProgramCacheKey;
360-
361-
material.onBeforeCompile = (shader: WebGLProgramParametersWithUniforms, renderer: WebGLRenderer) => {
362-
363-
// Workaround for troika-three-text, see #660.
364-
if(material.onBeforeCompile !== onBeforeCompile) {
365-
366-
onBeforeCompile.call(material, shader, renderer);
367-
368-
}
369-
370-
if(this.gBuffer === null) {
371-
372-
return;
373-
374-
}
375-
376-
if(material instanceof ShaderMaterial) {
377-
378-
shader.fragmentShader = shader.fragmentShader.replace(
379-
/(^ *void\s+main\(\)\s+{.*)/m,
380-
"$1\n\n#include <pp_default_output_fragment>"
381-
);
382-
383-
}
384-
385-
const outputDefinitions = extractOutputDefinitions(this.gBuffer);
386-
shader.fragmentShader = outputDefinitions + "\n\n" + shader.fragmentShader;
387-
388-
};
389-
390-
material.customProgramCacheKey = () => {
391-
392-
let key = this.gBuffer?.texture?.uuid ?? "";
393-
394-
// Workaround for troika-three-text, see #660.
395-
if(material.customProgramCacheKey !== customProgramCacheKey) {
396-
397-
key += customProgramCacheKey.call(material);
398-
399-
}
400-
401-
return key;
402-
403-
};
347+
this.gBufferShaderPlugin.applyTo(material);
404348

405349
}
406350

@@ -614,6 +558,7 @@ export class GeometryPass extends Pass implements GeometryPassOptions, Selective
614558

615559
}
616560

561+
this.gBufferShaderPlugin.gBuffer = this.output.defaultBuffer?.value ?? null;
617562
this.copyPass.output.defaultBuffer = this.output.defaultBuffer;
618563
this.updateCopyPass();
619564

src/shader-chunks/ShaderChunkExtensions.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import depthBufferParsFragment from "./shaders/depth-buffer-pars.frag";
88
import depthBufferPrecisionParsFragment from "./shaders/depth-buffer-precision-pars.frag";
99
import depthUtilsParsFragment from "./shaders/depth-utils-pars.frag";
1010
import frameBufferPrecisionParsFragment from "./shaders/frame-buffer-precision-pars.frag";
11+
import gbufferDefaultOutputFragment from "./shaders/gbuffer-default-output.frag";
1112
import inputBufferParsFragment from "./shaders/input-buffer-pars.frag";
13+
import normalCodecParsFragment from "./shaders/normal-codec-pars.frag";
1214
import normalUtilsParsFragment from "./shaders/normal-utils-pars.frag";
1315
import resolutionParsFragment from "./shaders/resolution-pars.frag";
1416
import worldUtilsParsFragment from "./shaders/world-utils-pars.frag";
1517

1618
// Extensions for built-in shaders.
17-
import normalCodecParsFragment from "./shaders/normal-codec-pars.frag";
18-
import gbufferDefaultOutputFragment from "./shaders/gbuffer-default-output.frag";
1919
import gbufferNormalFragment from "./shaders/gbuffer-normal.frag";
2020
import gbufferOcclusionFragment from "./shaders/gbuffer-occlusion.frag";
2121
import gbufferRoughnessFragment from "./shaders/gbuffer-roughness.frag";
@@ -61,36 +61,41 @@ export class ShaderChunkExtensions {
6161
"pp_depth_utils_pars_fragment": depthUtilsParsFragment,
6262
"pp_frame_buffer_precision_pars_fragment": frameBufferPrecisionParsFragment,
6363
"pp_input_buffer_pars_fragment": inputBufferParsFragment,
64+
"pp_normal_codec_pars_fragment": normalCodecParsFragment,
6465
"pp_normal_utils_pars_fragment": normalUtilsParsFragment,
6566
"pp_resolution_pars_fragment": resolutionParsFragment,
6667
"pp_world_utils_pars_fragment": worldUtilsParsFragment
6768
});
6869

69-
ShaderChunk.packing += "\n" + normalCodecParsFragment;
7070
ShaderChunk.normal_fragment_maps += "\n" + gbufferNormalFragment;
7171
ShaderChunk.aomap_fragment += "\n" + gbufferOcclusionFragment;
7272
ShaderChunk.roughnessmap_fragment += "\n" + gbufferRoughnessFragment;
7373
ShaderChunk.metalnessmap_fragment += "\n" + gbufferMetalnessFragment;
7474
ShaderChunk.emissivemap_fragment += "\n" + gbufferEmissionFragment;
7575

76-
// Let non-PBR shaders write default values.
76+
// Ensure that all shaders at least write default values.
7777

7878
const shaders = [
7979
ShaderLib.background,
80+
ShaderLib.backgroundCube,
8081
ShaderLib.basic,
82+
ShaderLib.cube,
83+
ShaderLib.dashed,
8184
ShaderLib.lambert,
82-
ShaderLib.phong,
8385
ShaderLib.matcap,
86+
ShaderLib.phong,
87+
ShaderLib.physical,
8488
ShaderLib.points,
85-
ShaderLib.dashed,
86-
ShaderLib.sprite
89+
ShaderLib.sprite,
90+
ShaderLib.standard,
91+
ShaderLib.toon
8792
];
8893

8994
for(const shader of shaders) {
9095

9196
shader.fragmentShader = shader.fragmentShader.replace(
9297
/(^ *void\s+main\(\)\s+{.*)/m,
93-
"$1\n\n#include <pp_default_output_fragment>"
98+
"#include <pp_normal_codec_pars_fragment>\n\n$1\n\n#include <pp_default_output_fragment>"
9499
);
95100

96101
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import {
2+
Material,
3+
ShaderMaterial,
4+
WebGLProgramParametersWithUniforms,
5+
WebGLRenderer,
6+
WebGLRenderTarget
7+
} from "three";
8+
9+
import { extractOutputDefinitions } from "./GBufferUtils.js";
10+
11+
/**
12+
* A shader plugin that enables rendering to G-Buffer render targets.
13+
*
14+
* @category Utils
15+
* @internal
16+
*/
17+
18+
export class GBufferShaderPlugin {
19+
20+
/**
21+
* A collection of materials that have been modified with `onBeforeCompile`.
22+
*/
23+
24+
private static readonly registeredMaterials = new WeakSet<Material>();
25+
26+
/**
27+
* A collection of materials that have been modified with `onBeforeCompile`.
28+
*/
29+
30+
private _gBuffer: WebGLRenderTarget | null;
31+
32+
/**
33+
* Constructs a new G-Buffer shader plugin.
34+
*/
35+
36+
constructor() {
37+
38+
this._gBuffer = null;
39+
40+
}
41+
42+
/**
43+
* Returns the G-Buffer render target, or null if there is none.
44+
*/
45+
46+
get gBuffer(): WebGLRenderTarget | null {
47+
48+
return this._gBuffer;
49+
50+
}
51+
52+
set gBuffer(value: WebGLRenderTarget | null) {
53+
54+
this._gBuffer = value;
55+
56+
}
57+
58+
/**
59+
* Applies this plugin to the given material.
60+
*
61+
* @param material - The material.
62+
*/
63+
64+
applyTo(material: Material) {
65+
66+
if(GBufferShaderPlugin.registeredMaterials.has(material)) {
67+
68+
return;
69+
70+
}
71+
72+
GBufferShaderPlugin.registeredMaterials.add(material);
73+
74+
/* eslint-disable @typescript-eslint/unbound-method */
75+
const onBeforeCompile = material.onBeforeCompile;
76+
const customProgramCacheKey = material.customProgramCacheKey;
77+
78+
material.onBeforeCompile = (shader: WebGLProgramParametersWithUniforms, renderer: WebGLRenderer) => {
79+
80+
// Workaround for troika-three-text, see #660.
81+
if(material.onBeforeCompile !== onBeforeCompile) {
82+
83+
onBeforeCompile.call(material, shader, renderer);
84+
85+
}
86+
87+
if(this.gBuffer === null) {
88+
89+
return;
90+
91+
}
92+
93+
// Built-in materials have already been modified via ShaderLib.
94+
if(material instanceof ShaderMaterial) {
95+
96+
shader.fragmentShader = shader.fragmentShader.replace(
97+
/(^ *void\s+main\(\)\s+{.*)/m,
98+
"#include <pp_normal_codec_pars_fragment>\n\n$1\n\n#include <pp_default_output_fragment>"
99+
);
100+
101+
}
102+
103+
const outputDefinitions = extractOutputDefinitions(this.gBuffer);
104+
shader.fragmentShader = outputDefinitions + "\n\n" + shader.fragmentShader;
105+
106+
};
107+
108+
material.customProgramCacheKey = () => {
109+
110+
let key = this.gBuffer?.texture?.uuid ?? "";
111+
112+
// Workaround for troika-three-text, see #660.
113+
if(material.customProgramCacheKey !== customProgramCacheKey) {
114+
115+
key += customProgramCacheKey.call(material);
116+
117+
}
118+
119+
return key;
120+
121+
};
122+
123+
}
124+
125+
}

src/utils/gbuffer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./GBufferConfig.js";
2+
export * from "./GBufferShaderPlugin.js";
23
export * from "./GBufferTextureConfig.js";
34
export * from "./GBufferUtils.js";

0 commit comments

Comments
 (0)