Compare commits

...

2 Commits

Author SHA1 Message Date
Alexander Rose
a9591ca8a5 set blurNormalBias max to 0.95 2024-10-20 17:54:40 -07:00
Alexander Rose
2c7ecca7fc ssao improvements
- add blurStepSize
- add blurNormalBias
- remove devicePixelRatio adjustment
- fix missing viewport/scissor calls
- only render half/quarter when multi-scale is enabled
- calculate normal from position derivative
2024-10-20 13:46:12 -07:00
11 changed files with 185 additions and 101 deletions

View File

@@ -14,7 +14,11 @@ Note that since we don't clearly distinguish between a public and private interf
- Fix `findPredecessorIndex` bug when repeating values
- MolViewSpec: Support for transparency and custom properties
- MolViewSpec: MVP Support for geometrical primitives (mesh, lines, line, label, distance measurement)
- SSAO improvements
- Add `blurStepSize` to reduce blur cost. Works best with multi-sample enabled to mitigate artefacts. Defaults to 2 (since multi-sample is enabled by default). Set to 1 for screenshots.
- Add `blurNormalBias` to improve creases between overlapping spheres. Quite expensive, use with care. Disabled by default (set to zero).
- Remove `devicePixelRatio` adjustment of resolution. This is a bit more expensive but avoids ugly edge artefacts.
- Reduce default `samples` from 32 to 24. Still at 128 for screenshots.
## [v4.7.1] - 2024-09-30

View File

@@ -46,11 +46,13 @@ function occlusionStyle(plugin: PluginContext) {
...plugin.canvas3d!.props.postprocessing,
occlusion: { name: 'on', params: {
blurKernelSize: 15,
blurStepSize: 2,
blurDepthBias: 0.5,
blurNormalBias: 0.0,
multiScale: { name: 'off', params: {} },
radius: 5,
bias: 0.8,
samples: 32,
samples: 24,
resolutionScale: 1,
color: Color(0x000000),
} },

View File

@@ -64,7 +64,7 @@ function adjustPluginProps(ctx: PluginContext) {
occlusion: {
name: 'on',
params: {
samples: 32,
samples: 24,
multiScale: {
name: 'on',
params: {
@@ -81,7 +81,9 @@ function adjustPluginProps(ctx: PluginContext) {
radius: 5,
bias: 1,
blurKernelSize: 11,
blurStepSize: 1,
blurDepthBias: 0.5,
blurNormalBias: 0.0,
resolutionScale: 1,
color: Color(0x000000),
}

View File

@@ -28,12 +28,15 @@ const Canvas3DPresets = {
occlusion: {
name: 'on',
params: {
samples: 32,
samples: 24,
multiScale: { name: 'off', params: {} },
radius: 5,
bias: 0.8,
blurKernelSize: 15,
blurStepStart: 0,
blurStepSize: 2,
blurDepthBias: 0.5,
blurNormalBias: 0.0,
resolutionScale: 1,
color: Color(0x000000),
}

View File

@@ -197,7 +197,7 @@ export class PostprocessingPass {
const occlusionEnabled = SsaoPass.isEnabled(props);
if (occlusionEnabled) {
this.ssao.update(camera, props.occlusion.params as SsaoProps);
this.ssao.update(camera, props.occlusion.params as SsaoProps, this.occlusionOffset);
}
if (shadowsEnabled) {
@@ -279,6 +279,7 @@ export class PostprocessingPass {
// don't render occlusion if offset is given,
// which will reuse the existing occlusion
if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) {
// if (props.occlusion.name === 'on') {
this.ssao.render(camera);
}

View File

@@ -28,11 +28,11 @@ import { isTimingMode } from '../../mol-util/debug';
import { PostprocessingProps } from './postprocessing';
export const SsaoParams = {
samples: PD.Numeric(32, { min: 1, max: 256, step: 1 }),
samples: PD.Numeric(24, { min: 1, max: 256, step: 1 }),
multiScale: PD.MappedStatic('off', {
on: PD.Group({
levels: PD.ObjectList({
radius: PD.Numeric(5, { min: 0, max: 20, step: 0.1 }, { description: 'Final occlusion radius is 2^x' }),
radius: PD.Numeric(5, { min: 0, max: 20, step: 0.1 }, { description: 'Final occlusion radius is 2^x.' }),
bias: PD.Numeric(1, { min: 0, max: 3, step: 0.1 }),
}, o => `${o.radius}, ${o.bias}`, { defaultValue: [
{ radius: 2, bias: 1 },
@@ -45,11 +45,13 @@ export const SsaoParams = {
}),
off: PD.Group({})
}, { cycle: true }),
radius: PD.Numeric(5, { min: 0, max: 20, step: 0.1 }, { description: 'Final occlusion radius is 2^x', hideIf: p => p?.multiScale.name === 'on' }),
radius: PD.Numeric(5, { min: 0, max: 20, step: 0.1 }, { description: 'Final occlusion radius is 2^x.', hideIf: p => p?.multiScale.name === 'on' }),
bias: PD.Numeric(0.8, { min: 0, max: 3, step: 0.1 }),
blurKernelSize: PD.Numeric(15, { min: 1, max: 25, step: 2 }),
blurKernelSize: PD.Numeric(15, { min: 1, max: 35, step: 2 }),
blurStepSize: PD.Numeric(2, { min: 1, max: 3, step: 1 }, { description: 'Step size for the blur. Values greater than one work best with multi-sample enabled to mitigate artefacts.' }),
blurDepthBias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }),
blurNormalBias: PD.Numeric(0.0, { min: 0, max: 0.95, step: 0.01 }, { description: 'Bias for normal comparison in blur. Mainly improves creases between overlapping spheres and the like. Quite expensive, use with care. Disabled when set to zero.' }),
resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation.' }),
color: PD.Color(Color(0x000000)),
};
@@ -104,17 +106,11 @@ export class SsaoPass {
private readonly blurSecondPassRenderable: SsaoBlurRenderable;
private depthTexture: Texture;
private texSize: [number, number];
private nSamples: number;
private blurKernelSize: number;
private texSize: [number, number];
private ssaoScale: number;
private calcSsaoScale(resolutionScale: number) {
// downscale ssao for high pixel-ratios
return Math.min(1, 1 / this.webgl.pixelRatio) * resolutionScale;
}
private levels: { radius: number, bias: number }[];
private getDepthTexture() {
@@ -128,7 +124,7 @@ export class SsaoPass {
this.nSamples = 1;
this.blurKernelSize = 1;
this.ssaoScale = this.calcSsaoScale(1);
this.ssaoScale = 1;
this.texSize = [width, height];
this.levels = [];
@@ -179,7 +175,7 @@ export class SsaoPass {
setSize(width: number, height: number) {
const [w, h] = this.texSize;
const ssaoScale = this.calcSsaoScale(1);
const ssaoScale = 1;
if (width !== w || height !== h || this.ssaoScale !== ssaoScale) {
this.texSize.splice(0, 2, width, height);
@@ -213,7 +209,7 @@ export class SsaoPass {
}
}
update(camera: ICamera, props: SsaoProps) {
update(camera: ICamera, props: SsaoProps, offset: [x: number, y: number]) {
let needsUpdateSsao = false;
let needsUpdateSsaoBlur = false;
let needsUpdateDepthHalf = false;
@@ -253,6 +249,18 @@ export class SsaoPass {
ValueCell.update(this.blurFirstPassRenderable.values.uBlurDepthBias, props.blurDepthBias);
ValueCell.update(this.blurSecondPassRenderable.values.uBlurDepthBias, props.blurDepthBias);
const dBlurNormalBias = props.blurNormalBias !== 0;
if (this.blurFirstPassRenderable.values.dBlurNormalBias.ref.value !== dBlurNormalBias) {
needsUpdateSsaoBlur = true;
ValueCell.update(this.blurFirstPassRenderable.values.dBlurNormalBias, dBlurNormalBias);
ValueCell.update(this.blurSecondPassRenderable.values.dBlurNormalBias, dBlurNormalBias);
}
ValueCell.update(this.blurFirstPassRenderable.values.uBlurNormalBias, props.blurNormalBias);
ValueCell.update(this.blurSecondPassRenderable.values.uBlurNormalBias, props.blurNormalBias);
if (this.blurFirstPassRenderable.values.dOrthographic.ref.value !== orthographic) {
needsUpdateSsaoBlur = true;
ValueCell.update(this.blurFirstPassRenderable.values.dOrthographic, orthographic);
@@ -292,24 +300,30 @@ export class SsaoPass {
}
ValueCell.updateIfChanged(this.renderable.values.uBias, props.bias);
if (this.blurKernelSize !== props.blurKernelSize) {
const blurKernelSize = Math.max(1, Math.floor(props.blurKernelSize / props.blurStepSize));
if (this.blurKernelSize !== blurKernelSize) {
needsUpdateSsaoBlur = true;
this.blurKernelSize = props.blurKernelSize;
const kernel = getBlurKernel(this.blurKernelSize);
this.blurKernelSize = blurKernelSize;
const kernel = getBlurKernel(blurKernelSize);
ValueCell.update(this.blurFirstPassRenderable.values.uKernel, kernel);
ValueCell.update(this.blurSecondPassRenderable.values.uKernel, kernel);
ValueCell.update(this.blurFirstPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
ValueCell.update(this.blurSecondPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
ValueCell.update(this.blurFirstPassRenderable.values.dOcclusionKernelSize, blurKernelSize);
ValueCell.update(this.blurSecondPassRenderable.values.dOcclusionKernelSize, blurKernelSize);
}
const ssaoScale = this.calcSsaoScale(props.resolutionScale);
if (this.ssaoScale !== ssaoScale) {
ValueCell.updateIfChanged(this.blurFirstPassRenderable.values.uBlurStepSize, props.blurStepSize);
ValueCell.updateIfChanged(this.blurSecondPassRenderable.values.uBlurStepSize, props.blurStepSize);
ValueCell.updateIfChanged(this.blurFirstPassRenderable.values.uBlurStepOffset, Vec2.set(this.blurFirstPassRenderable.values.uBlurStepOffset.ref.value, offset[0], offset[1]));
ValueCell.updateIfChanged(this.blurSecondPassRenderable.values.uBlurStepOffset, Vec2.set(this.blurSecondPassRenderable.values.uBlurStepOffset.ref.value, offset[0], offset[1]));
if (this.ssaoScale !== props.resolutionScale) {
needsUpdateSsao = true;
needsUpdateDepthHalf = true;
this.ssaoScale = ssaoScale;
this.ssaoScale = props.resolutionScale;
const sw = Math.floor(w * this.ssaoScale);
const sh = Math.floor(h * this.ssaoScale);
@@ -365,27 +379,44 @@ export class SsaoPass {
const sw = Math.ceil(width * this.ssaoScale);
const sh = Math.ceil(height * this.ssaoScale);
state.viewport(sx, sy, sw, sh);
state.scissor(sx, sy, sw, sh);
if (this.ssaoScale < 1) {
if (isTimingMode) this.webgl.timer.mark('SsaoPass.downsample');
state.viewport(sx, sy, sw, sh);
state.scissor(sx, sy, sw, sh);
this.downsampledDepthTarget.bind();
this.downsampleDepthRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SsaoPass.downsample');
}
if (isTimingMode) this.webgl.timer.mark('SsaoPass.half');
this.depthHalfTarget.bind();
this.depthHalfRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SsaoPass.half');
if (this.renderable.values.dMultiScale.ref.value) {
const hx = Math.floor(sx * 0.5);
const hy = Math.floor(sy * 0.5);
const hw = Math.ceil(sw * 0.5);
const hh = Math.ceil(sh * 0.5);
if (isTimingMode) this.webgl.timer.mark('SsaoPass.quarter');
this.depthQuarterTarget.bind();
this.depthQuarterRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SsaoPass.quarter');
const qx = Math.floor(sx * 0.25);
const qy = Math.floor(sy * 0.25);
const qw = Math.ceil(sw * 0.25);
const qh = Math.ceil(sh * 0.25);
if (isTimingMode) this.webgl.timer.mark('SsaoPass.half');
state.viewport(hx, hy, hw, hh);
state.scissor(hx, hy, hw, hh);
this.depthHalfTarget.bind();
this.depthHalfRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SsaoPass.half');
if (isTimingMode) this.webgl.timer.mark('SsaoPass.quarter');
state.viewport(qx, qy, qw, qh);
state.scissor(sx, qy, sw, qh);
this.depthQuarterTarget.bind();
this.depthQuarterRenderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SsaoPass.quarter');
}
if (isTimingMode) this.webgl.timer.mark('SsaoPass.sample');
state.viewport(sx, sy, sw, sh);
state.scissor(sx, sy, sw, sh);
this.framebuffer.bind();
this.renderable.render();
if (isTimingMode) this.webgl.timer.markEnd('SsaoPass.sample');
@@ -471,6 +502,10 @@ const SsaoBlurSchema = {
uKernel: UniformSpec('f[]'),
dOcclusionKernelSize: DefineSpec('number'),
uBlurDepthBias: UniformSpec('f'),
dBlurNormalBias: DefineSpec('boolean'),
uBlurNormalBias: UniformSpec('f'),
uBlurStepSize: UniformSpec('f'),
uBlurStepOffset: UniformSpec('v2'),
uBlurDirectionX: UniformSpec('f'),
uBlurDirectionY: UniformSpec('f'),
@@ -493,6 +528,10 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
uKernel: ValueCell.create(getBlurKernel(15)),
dOcclusionKernelSize: ValueCell.create(15),
uBlurDepthBias: ValueCell.create(0.5),
dBlurNormalBias: ValueCell.create(false),
uBlurNormalBias: ValueCell.create(0.0),
uBlurStepSize: ValueCell.create(1),
uBlurStepOffset: ValueCell.create(Vec2()),
uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
uBlurDirectionY: ValueCell.create(direction === 'vertical' ? 1 : 0),

View File

@@ -17,6 +17,9 @@ uniform vec4 uBounds;
uniform float uKernel[dOcclusionKernelSize];
uniform float uBlurDepthBias;
uniform float uBlurNormalBias;
uniform float uBlurStepSize;
uniform vec2 uBlurStepOffset;
uniform float uBlurDirectionX;
uniform float uBlurDirectionY;
@@ -36,7 +39,7 @@ float getViewZ(const in float depth) {
}
bool isBackground(const in float depth) {
return depth == 1.0;
return depth > 0.999;
}
bool isNearClip(const in float depth) {
@@ -53,6 +56,64 @@ float getPixelSize(const in vec2 coords, const in float depth) {
return distance(viewPos0, viewPos1);
}
#ifdef dBlurNormalBias
float getDepth(const in vec2 coords) {
return unpackRGToUnitInterval(texture2D(tSsaoDepth, coords).zw);
}
// adapted from https://gist.github.com/bgolus/a07ed65602c009d5e2f753826e8078a0
vec3 viewNormalAtPixelPositionAccurate(const in vec2 vpos) {
// current pixel's depth
float c = getDepth(vpos);
// get current pixel's view space position
vec3 viewSpacePos_c = screenSpaceToViewSpace(vec3(vpos, c), uInvProjection);
// get view space position at 1 pixel offsets in each major direction
vec3 viewSpacePos_l = screenSpaceToViewSpace(vec3(vpos + vec2(-1.0, 0.0) / uTexSize, getDepth(vpos + vec2(-1.0, 0.0) / uTexSize)), uInvProjection);
vec3 viewSpacePos_r = screenSpaceToViewSpace(vec3(vpos + vec2( 1.0, 0.0) / uTexSize, getDepth(vpos + vec2( 1.0, 0.0) / uTexSize)), uInvProjection);
vec3 viewSpacePos_d = screenSpaceToViewSpace(vec3(vpos + vec2( 0.0,-1.0) / uTexSize, getDepth(vpos + vec2( 0.0,-1.0) / uTexSize)), uInvProjection);
vec3 viewSpacePos_u = screenSpaceToViewSpace(vec3(vpos + vec2( 0.0, 1.0) / uTexSize, getDepth(vpos + vec2( 0.0, 1.0) / uTexSize)), uInvProjection);
// get the difference between the current and each offset position
vec3 l = viewSpacePos_c - viewSpacePos_l;
vec3 r = viewSpacePos_r - viewSpacePos_c;
vec3 d = viewSpacePos_c - viewSpacePos_d;
vec3 u = viewSpacePos_u - viewSpacePos_c;
// get depth values at 1 & 2 pixels offsets from current along the horizontal axis
vec4 H = vec4(
getDepth(vpos + vec2(-1.0, 0.0) / uTexSize),
getDepth(vpos + vec2( 1.0, 0.0) / uTexSize),
getDepth(vpos + vec2(-2.0, 0.0) / uTexSize),
getDepth(vpos + vec2( 2.0, 0.0) / uTexSize)
);
// get depth values at 1 & 2 pixels offsets from current along the vertical axis
vec4 V = vec4(
getDepth(vpos + vec2(0.0,-1.0) / uTexSize),
getDepth(vpos + vec2(0.0, 1.0) / uTexSize),
getDepth(vpos + vec2(0.0,-2.0) / uTexSize),
getDepth(vpos + vec2(0.0, 2.0) / uTexSize)
);
// current pixel's depth difference from slope of offset depth samples
// differs from original article because we're using non-linear depth values
// see article's comments
vec2 he = abs((2.0 * H.xy - H.zw) - c);
vec2 ve = abs((2.0 * V.xy - V.zw) - c);
// pick horizontal and vertical diff with the smallest depth difference from slopes
vec3 hDeriv = he.x < he.y ? l : r;
vec3 vDeriv = ve.x < ve.y ? d : u;
// get view space normal from the cross product of the best derivatives
vec3 viewNormal = normalize(cross(hDeriv, vDeriv));
return viewNormal;
}
#endif
void main(void) {
vec2 coords = gl_FragCoord.xy / uTexSize;
@@ -64,8 +125,7 @@ void main(void) {
}
float selfDepth = unpackRGToUnitInterval(packedDepth);
// (if background and if second pass) or if near clip
if ((isBackground(selfDepth) && uBlurDirectionY != 0.0) || isNearClip(selfDepth)) {
if (isBackground(selfDepth) || isNearClip(selfDepth)) {
gl_FragColor = vec4(packUnitIntervalToRG(1.0), packedDepth);
return;
}
@@ -73,7 +133,13 @@ void main(void) {
float selfViewZ = getViewZ(selfDepth);
float pixelSize = getPixelSize(coords, selfDepth);
#ifdef dBlurNormalBias
vec3 selfNormal = viewNormalAtPixelPositionAccurate(coords);
#endif
vec2 offset = vec2(uBlurDirectionX, uBlurDirectionY) / uTexSize;
coords += uBlurStepOffset * vec2(uBlurDirectionX, uBlurDirectionY);
offset *= uBlurStepSize;
float sum = 0.0;
float kernelSum = 0.0;
@@ -93,6 +159,13 @@ void main(void) {
continue;
}
#ifdef dBlurNormalBias
vec3 sampleNormal = viewNormalAtPixelPositionAccurate(sampleCoords);
if (saturate(dot(selfNormal, sampleNormal)) < uBlurNormalBias) {
continue;
}
#endif
float sampleViewZ = getViewZ(sampleDepth);
if (abs(selfViewZ - sampleViewZ) >= uBlurDepthBias) {
continue;

View File

@@ -90,58 +90,6 @@ float getMappedDepth(const in vec2 coords, const in vec2 selfCoords) {
#endif
}
// adapted from https://gist.github.com/bgolus/a07ed65602c009d5e2f753826e8078a0
vec3 viewNormalAtPixelPositionAccurate(vec2 vpos) {
// current pixel's depth
float c = getDepth(vpos);
// get current pixel's view space position
vec3 viewSpacePos_c = screenSpaceToViewSpace(vec3(vpos, c), uInvProjection);
// get view space position at 1 pixel offsets in each major direction
vec3 viewSpacePos_l = screenSpaceToViewSpace(vec3(vpos + vec2(-1.0, 0.0) / uTexSize, getDepth(vpos + vec2(-1.0, 0.0) / uTexSize)), uInvProjection);
vec3 viewSpacePos_r = screenSpaceToViewSpace(vec3(vpos + vec2( 1.0, 0.0) / uTexSize, getDepth(vpos + vec2( 1.0, 0.0) / uTexSize)), uInvProjection);
vec3 viewSpacePos_d = screenSpaceToViewSpace(vec3(vpos + vec2( 0.0,-1.0) / uTexSize, getDepth(vpos + vec2( 0.0,-1.0) / uTexSize)), uInvProjection);
vec3 viewSpacePos_u = screenSpaceToViewSpace(vec3(vpos + vec2( 0.0, 1.0) / uTexSize, getDepth(vpos + vec2( 0.0, 1.0) / uTexSize)), uInvProjection);
// get the difference between the current and each offset position
vec3 l = viewSpacePos_c - viewSpacePos_l;
vec3 r = viewSpacePos_r - viewSpacePos_c;
vec3 d = viewSpacePos_c - viewSpacePos_d;
vec3 u = viewSpacePos_u - viewSpacePos_c;
// get depth values at 1 & 2 pixels offsets from current along the horizontal axis
vec4 H = vec4(
getDepth(vpos + vec2(-1.0, 0.0) / uTexSize),
getDepth(vpos + vec2( 1.0, 0.0) / uTexSize),
getDepth(vpos + vec2(-2.0, 0.0) / uTexSize),
getDepth(vpos + vec2( 2.0, 0.0) / uTexSize)
);
// get depth values at 1 & 2 pixels offsets from current along the vertical axis
vec4 V = vec4(
getDepth(vpos + vec2(0.0,-1.0) / uTexSize),
getDepth(vpos + vec2(0.0, 1.0) / uTexSize),
getDepth(vpos + vec2(0.0,-2.0) / uTexSize),
getDepth(vpos + vec2(0.0, 2.0) / uTexSize)
);
// current pixel's depth difference from slope of offset depth samples
// differs from original article because we're using non-linear depth values
// see article's comments
vec2 he = abs((2.0 * H.xy - H.zw) - c);
vec2 ve = abs((2.0 * V.xy - V.zw) - c);
// pick horizontal and vertical diff with the smallest depth difference from slopes
vec3 hDeriv = he.x < he.y ? l : r;
vec3 vDeriv = ve.x < ve.y ? d : u;
// get view space normal from the cross product of the best derivatives
vec3 viewNormal = normalize(cross(hDeriv, vDeriv));
return viewNormal;
}
float getPixelSize(const in vec2 coords, const in float depth) {
vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);
vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);
@@ -161,8 +109,8 @@ void main(void) {
return;
}
vec3 selfViewNormal = viewNormalAtPixelPositionAccurate(selfCoords);
vec3 selfViewPos = screenSpaceToViewSpace(vec3(selfCoords, selfDepth), uInvProjection);
vec3 selfViewNormal = normalize(cross(dFdx(selfViewPos), dFdy(selfViewPos)));
vec3 randomVec = normalize(vec3(getNoiseVec2(selfCoords) * 2.0 - 1.0, 0.0));
vec3 tangent = normalize(randomVec - selfViewNormal * dot(randomVec, selfViewNormal));

View File

@@ -71,8 +71,10 @@ export class QuickStyles extends PurePluginUIComponent {
radius: 5,
bias: 0.8,
blurKernelSize: 15,
blurStepSize: 2,
blurDepthBias: 0.5,
samples: 32,
blurNormalBias: 0.0,
samples: 24,
resolutionScale: 1,
color: Color(0x000000),
}
@@ -110,8 +112,10 @@ export class QuickStyles extends PurePluginUIComponent {
radius: 5,
bias: 0.8,
blurKernelSize: 15,
blurStepSize: 2,
blurDepthBias: 0.5,
samples: 32,
blurNormalBias: 0.0,
samples: 24,
resolutionScale: 1,
color: Color(0x000000),
}

View File

@@ -220,12 +220,14 @@ export function defaultImagePassParams(): Partial<ImageProps> {
export const STYLIZED_POSTPROCESSING: Partial<PostprocessingProps> = {
occlusion: {
name: 'on' as const, params: {
samples: 32,
samples: 128,
multiScale: { name: 'off', params: {} },
radius: 5,
bias: 0.8,
blurKernelSize: 15,
blurStepSize: 1,
blurDepthBias: 0.5,
blurNormalBias: 0.0,
resolutionScale: 1,
color: ColorNames.black,
}

View File

@@ -121,9 +121,15 @@ class ViewportScreenshotHelper extends PluginComponent {
const aoProps = c.props.postprocessing.occlusion;
return {
...c.props.postprocessing,
occlusion: aoProps.name === 'on'
? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: c.webgl.pixelRatio } }
: aoProps
occlusion: aoProps.name === 'on' ? {
name: 'on',
params: {
...aoProps.params,
samples: 128,
resolutionScale: 1,
blurStepSize: 1,
}
} : aoProps
} as PostprocessingProps;
}