Skip to content

Commit 08f8f80

Browse files
mvaligurskyMartin Valigursky
andauthored
Per frame randomized SSAO sampling & debug mode in composite pass (playcanvas#7139)
* Per frame randomized SSAO sampling & debug mode in composite pass * lint * examples lint * debug option is null and not ‘’ --------- Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
1 parent d4df9b6 commit 08f8f80

8 files changed

Lines changed: 171 additions & 10 deletions

File tree

examples/src/examples/graphics/ambient-occlusion.controls.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
3333
link: { observer, path: 'data.ssao.blurEnabled' }
3434
})
3535
),
36+
jsx(
37+
LabelGroup,
38+
{ text: 'randomize' },
39+
jsx(BooleanInput, {
40+
type: 'toggle',
41+
binding: new BindingTwoWay(),
42+
link: { observer, path: 'data.ssao.randomize' }
43+
})
44+
),
3645
jsx(
3746
LabelGroup,
3847
{ text: 'radius' },
@@ -92,6 +101,24 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
92101
binding: new BindingTwoWay(),
93102
link: { observer, path: 'data.ssao.scale' }
94103
})
104+
),
105+
jsx(
106+
LabelGroup,
107+
{ text: 'TAA' },
108+
jsx(BooleanInput, {
109+
type: 'toggle',
110+
binding: new BindingTwoWay(),
111+
link: { observer, path: 'data.ssao.taa' }
112+
})
113+
),
114+
jsx(
115+
LabelGroup,
116+
{ text: 'debug' },
117+
jsx(BooleanInput, {
118+
type: 'toggle',
119+
binding: new BindingTwoWay(),
120+
link: { observer, path: 'data.ssao.debug' }
121+
})
95122
)
96123
)
97124
);

examples/src/examples/graphics/ambient-occlusion.example.mjs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,11 @@ assetListLoader.load(() => {
168168
// ------ Custom render passes set up ------
169169

170170
const cameraFrame = new pc.CameraFrame(app, cameraEntity.camera);
171-
cameraFrame.rendering.samples = 4;
172171
cameraFrame.rendering.toneMapping = pc.TONEMAP_NEUTRAL;
173172

173+
// use 16but render target for better precision, improves quality with TAA and randomized SSAO
174+
cameraFrame.rendering.renderFormats = [pc.PIXELFORMAT_RGBA16F];
175+
174176
const applySettings = () => {
175177

176178
cameraFrame.ssao.type = data.get('data.ssao.type');
@@ -181,6 +183,14 @@ assetListLoader.load(() => {
181183
cameraFrame.ssao.samples = data.get('data.ssao.samples');
182184
cameraFrame.ssao.minAngle = data.get('data.ssao.minAngle');
183185
cameraFrame.ssao.scale = data.get('data.ssao.scale');
186+
cameraFrame.ssao.randomize = data.get('data.ssao.randomize');
187+
cameraFrame.debug = data.get('data.ssao.debug') ? 'ssao' : null;
188+
189+
// TAA or MSAA
190+
const taa = data.get('data.ssao.taa');
191+
cameraFrame.taa.enabled = taa;
192+
cameraFrame.rendering.samples = taa ? 1 : 4; // disable MSAA when TAA is enabled
193+
cameraFrame.rendering.sharpness = taa ? 1 : 0; // sharpen the image when TAA is enabled
184194

185195
cameraFrame.update();
186196
};
@@ -213,7 +223,10 @@ assetListLoader.load(() => {
213223
intensity: 0.4,
214224
power: 6,
215225
minAngle: 10,
216-
scale: 1
226+
scale: 1,
227+
taa: false,
228+
randomize: false,
229+
debug: false
217230
}
218231
});
219232
});

examples/src/examples/graphics/post-processing.controls.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,21 @@ export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
5959
{ v: pc.TONEMAP_NEUTRAL, t: 'NEUTRAL' }
6060
]
6161
})
62+
),
63+
jsx(
64+
LabelGroup,
65+
{ text: 'Debug' },
66+
jsx(SelectInput, {
67+
binding: new BindingTwoWay(),
68+
link: { observer, path: 'data.scene.debug' },
69+
type: 'number',
70+
options: [
71+
{ v: 0, t: 'NONE' },
72+
{ v: 1, t: 'BLOOM' },
73+
{ v: 2, t: 'VIGNETTE' },
74+
{ v: 3, t: 'SCENE' }
75+
]
76+
})
6277
)
6378
),
6479
jsx(

examples/src/examples/graphics/post-processing.example.mjs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ assetListLoader.load(() => {
264264
// fringing
265265
cameraFrame.fringing.intensity = data.get('data.fringing.enabled') ? data.get('data.fringing.intensity') : 0;
266266

267+
// debug
268+
switch (data.get('data.scene.debug')) {
269+
case 0: cameraFrame.debug = null; break;
270+
case 1: cameraFrame.debug = 'bloom'; break;
271+
case 2: cameraFrame.debug = 'vignette'; break;
272+
case 3: cameraFrame.debug = 'scene'; break;
273+
}
274+
267275
// apply all settings
268276
cameraFrame.update();
269277
};
@@ -279,7 +287,8 @@ assetListLoader.load(() => {
279287
scale: 1.8,
280288
background: 6,
281289
emissive: 200,
282-
tonemapping: pc.TONEMAP_ACES
290+
tonemapping: pc.TONEMAP_ACES,
291+
debug: 0
283292
},
284293
bloom: {
285294
enabled: true,

scripts/utils/camera-frame.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ const RenderFormat = {
2626
RGBA32: 14 // PIXELFORMAT_RGBA32F
2727
};
2828

29+
/** @enum {string} */
30+
const DebugType = {
31+
NONE: '',
32+
SCENE: 'scene',
33+
SSAO: 'ssao',
34+
BLOOM: 'bloom',
35+
VIGNETTE: 'vignette'
36+
};
37+
2938
/** @interface */
3039
class Rendering {
3140
/**
@@ -80,6 +89,12 @@ class Rendering {
8089
* @step 0.001
8190
*/
8291
sharpness = 0.0;
92+
93+
/**
94+
* @attribute
95+
* @type {DebugType}
96+
*/
97+
debug = DebugType.NONE;
8398
}
8499

85100
/** @interface */
@@ -370,6 +385,9 @@ class CameraFrame extends Script {
370385
const dstFringing = cf.fringing;
371386
dstFringing.intensity = fringing.enabled ? fringing.intensity : 0;
372387

388+
// debugging
389+
cf.debug = rendering.debug;
390+
373391
cf.update();
374392
}
375393
}

src/extras/render-passes/camera-frame.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ import { CameraFrameOptions, RenderPassCameraFrame } from './render-pass-camera-
6565
* - {@link SSAOTYPE_COMBINE}
6666
*
6767
* @property {boolean} blurEnabled - Whether the SSAO effect is blurred. Defaults to true.
68+
* @property {boolean} randomize - Whether the SSAO sampling is randomized. Useful when used instead
69+
* of blur effect together with TAA. Defaults to false.
6870
* @property {number} intensity - The intensity of the SSAO effect, 0-1 range. Defaults to 0.5.
6971
* @property {number} radius - The radius of the SSAO effect, 0-100 range. Defaults to 30.
7072
* @property {number} samples - The number of samples of the SSAO effect, 1-64 range. Defaults to 12.
@@ -174,6 +176,7 @@ class CameraFrame {
174176
ssao = {
175177
type: SSAOTYPE_NONE,
176178
blurEnabled: true,
179+
randomize: false,
177180
intensity: 0.5,
178181
radius: 30,
179182
samples: 12,
@@ -236,6 +239,13 @@ class CameraFrame {
236239
intensity: 0
237240
};
238241

242+
/**
243+
* Debug rendering. Set to null to disable.
244+
*
245+
* @type {null|'scene'|'ssao'|'bloom'|'vignette'}
246+
*/
247+
debug = null;
248+
239249
options = new CameraFrameOptions();
240250

241251
/**
@@ -357,6 +367,7 @@ class CameraFrame {
357367
ssaoPass.sampleCount = ssao.samples;
358368
ssaoPass.minAngle = ssao.minAngle;
359369
ssaoPass.scale = ssao.scale;
370+
ssaoPass.randomize = ssao.randomize;
360371
}
361372

362373
composePass.gradingEnabled = grading.enabled;
@@ -382,6 +393,11 @@ class CameraFrame {
382393

383394
// enable camera jitter if taa is enabled
384395
cameraComponent.jitter = taa.enabled ? taa.jitter : 0;
396+
397+
// debug rendering
398+
composePass.debug = this.debug;
399+
if (composePass.debug === 'ssao' && options.ssaoType === SSAOTYPE_NONE) composePass.debug = null;
400+
if (composePass.debug === 'vignette' && !composePass.vignetteEnabled) composePass.debug = null;
385401
}
386402
}
387403

src/extras/render-passes/render-pass-compose.js

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ const fragmentShader = /* glsl */ `
2323
#endif
2424
2525
#ifdef SSAO
26+
#define SSAO_TEXTURE
27+
#endif
28+
29+
#if DEBUG_COMPOSE == ssao
30+
#define SSAO_TEXTURE
31+
#endif
32+
33+
#ifdef SSAO_TEXTURE
2634
uniform sampler2D ssaoTexture;
2735
#endif
2836
@@ -146,8 +154,12 @@ const fragmentShader = /* glsl */ `
146154
result = cas(result, uv, sharpness);
147155
#endif
148156
157+
#ifdef SSAO_TEXTURE
158+
mediump float ssao = texture2DLodEXT(ssaoTexture, uv0, 0.0).r;
159+
#endif
160+
149161
#ifdef SSAO
150-
result *= texture2DLodEXT(ssaoTexture, uv0, 0.0).r;
162+
result *= ssao;
151163
#endif
152164
153165
#ifdef FRINGING
@@ -167,7 +179,31 @@ const fragmentShader = /* glsl */ `
167179
result = toneMap(result);
168180
169181
#ifdef VIGNETTE
170-
result *= vignette(uv);
182+
mediump float vig = vignette(uv);
183+
result *= vig;
184+
#endif
185+
186+
// debug output
187+
#ifdef DEBUG_COMPOSE
188+
189+
#ifdef BLOOM
190+
#if DEBUG_COMPOSE == bloom
191+
result = bloom * bloomIntensity;
192+
#endif
193+
#endif
194+
195+
#if DEBUG_COMPOSE == ssao
196+
result = vec3(ssao);
197+
#endif
198+
199+
#if DEBUG_COMPOSE == vignette
200+
result = vec3(vig);
201+
#endif
202+
203+
#if DEBUG_COMPOSE == scene
204+
result = scene.rgb;
205+
#endif
206+
171207
#endif
172208
173209
#ifdef GAMMA_CORRECT_OUTPUT
@@ -229,6 +265,8 @@ class RenderPassCompose extends RenderPassShaderQuad {
229265

230266
_key = '';
231267

268+
_debug = null;
269+
232270
constructor(graphicsDevice) {
233271
super(graphicsDevice);
234272

@@ -246,6 +284,17 @@ class RenderPassCompose extends RenderPassShaderQuad {
246284
this.sharpnessId = scope.resolve('sharpness');
247285
}
248286

287+
set debug(value) {
288+
if (this._debug !== value) {
289+
this._debug = value;
290+
this._shaderDirty = true;
291+
}
292+
}
293+
294+
get debug() {
295+
return this._debug;
296+
}
297+
249298
set bloomTexture(value) {
250299
if (this._bloomTexture !== value) {
251300
this._bloomTexture = value;
@@ -367,7 +416,8 @@ class RenderPassCompose extends RenderPassShaderQuad {
367416
`-${this.fringingEnabled ? 'fringing' : 'nofringing'}` +
368417
`-${this.taaEnabled ? 'taa' : 'notaa'}` +
369418
`-${this.isSharpnessEnabled ? 'cas' : 'nocas'}` +
370-
`-${this._srgb ? 'srgb' : 'linear'}`;
419+
`-${this._srgb ? 'srgb' : 'linear'}` +
420+
`-${this._debug ?? ''}`;
371421

372422
if (this._key !== key) {
373423
this._key = key;
@@ -380,7 +430,8 @@ class RenderPassCompose extends RenderPassShaderQuad {
380430
(this.fringingEnabled ? '#define FRINGING\n' : '') +
381431
(this.taaEnabled ? '#define TAA\n' : '') +
382432
(this.isSharpnessEnabled ? '#define CAS\n' : '') +
383-
(this._srgb ? '' : '#define GAMMA_CORRECT_OUTPUT\n');
433+
(this._srgb ? '' : '#define GAMMA_CORRECT_OUTPUT\n') +
434+
(this._debug ? `#define DEBUG_COMPOSE ${this._debug}\n` : '');
384435

385436
const fsChunks =
386437
shaderChunks.decodePS +

src/extras/render-passes/render-pass-ssao.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { BlueNoise } from '../../core/math/blue-noise.js';
12
import { Color } from '../../core/math/color.js';
23
import { ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_R8 } from '../../platform/graphics/constants.js';
34
import { RenderTarget } from '../../platform/graphics/render-target.js';
@@ -150,9 +151,10 @@ const fs = /* glsl */`
150151
151152
uniform float uProjectionScaleRadius;
152153
uniform float uIntensity;
154+
uniform float uRandomize;
153155
154156
float scalableAmbientObscurance(highp vec2 uv, highp vec3 origin, vec3 normal) {
155-
float noise = random(getFragCoord());
157+
float noise = random(getFragCoord()) + uRandomize;
156158
highp vec2 tapPosition = startPosition(noise);
157159
highp mat2 angleStep = tapAngleStep();
158160
@@ -234,6 +236,12 @@ class RenderPassSsao extends RenderPassShaderQuad {
234236
*/
235237
minAngle = 5;
236238

239+
/**
240+
* Enable randomization of the sample pattern. Useful when TAA is used to remove the noise,
241+
* instead of blurring.
242+
*/
243+
randomize = false;
244+
237245
/**
238246
* The texture containing the occlusion information in the red channel.
239247
*
@@ -245,6 +253,8 @@ class RenderPassSsao extends RenderPassShaderQuad {
245253
/** @type {number} */
246254
_scale = 1;
247255

256+
_blueNoise = new BlueNoise(19);
257+
248258
constructor(device, sourceTexture, cameraComponent, blurEnabled) {
249259
super(device);
250260
this.sourceTexture = sourceTexture;
@@ -356,11 +366,12 @@ class RenderPassSsao extends RenderPassShaderQuad {
356366

357367
const spiralTurns = 10.0;
358368
const step = (1.0 / (sampleCount - 0.5)) * spiralTurns * 2.0 * 3.141;
359-
const radius = this.radius * scale;
369+
const radius = this.radius / scale;
370+
360371
const bias = 0.001;
361372
const peak = 0.1 * radius;
362373
const intensity = 2 * (peak * 2.0 * 3.141) * this.intensity / sampleCount;
363-
const projectionScale = 0.5 * sourceTexture.height * scale;
374+
const projectionScale = 0.5 * sourceTexture.height;
364375
scope.resolve('uSpiralTurns').setValue(spiralTurns);
365376
scope.resolve('uAngleIncCosSin').setValue([Math.cos(step), Math.sin(step)]);
366377
scope.resolve('uMaxLevel').setValue(0.0);
@@ -370,6 +381,7 @@ class RenderPassSsao extends RenderPassShaderQuad {
370381
scope.resolve('uIntensity').setValue(intensity);
371382
scope.resolve('uPower').setValue(this.power);
372383
scope.resolve('uProjectionScaleRadius').setValue(projectionScale * radius);
384+
scope.resolve('uRandomize').setValue(this.randomize ? this._blueNoise.value() : 0);
373385

374386
super.execute();
375387
}

0 commit comments

Comments
 (0)