mirror of
https://github.com/molstar/molstar.git
synced 2026-06-04 21:34:23 +08:00
Compare commits
2 Commits
master
...
ssao-impro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9591ca8a5 | ||
|
|
2c7ecca7fc |
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
} },
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user