From c7dad00908b870e2d66bc4680cd03f120bfafc5d Mon Sep 17 00:00:00 2001 From: ludovic autin <autin@scripps.edu> Date: Thu, 17 Nov 2022 09:28:00 -0800 Subject: [PATCH] fake screen space shadow, second pass ssao --- src/apps/docking-viewer/viewport.tsx | 4 +- src/examples/lighting/index.ts | 20 +- src/extensions/cellpack/model.ts | 2 + src/mol-canvas3d/passes/draw.ts | 6 +- src/mol-canvas3d/passes/postprocessing.ts | 121 +++++++- src/mol-gl/renderer.ts | 8 +- src/mol-gl/shader/postprocessing.frag.ts | 12 + src/mol-gl/shader/ssao.frag.ts | 305 ++++++++++++++++++- src/mol-plugin-ui/structure/quick-styles.tsx | 10 +- 9 files changed, 457 insertions(+), 31 deletions(-) diff --git a/src/apps/docking-viewer/viewport.tsx b/src/apps/docking-viewer/viewport.tsx index 67976ac1e..1642a5d0a 100644 --- a/src/apps/docking-viewer/viewport.tsx +++ b/src/apps/docking-viewer/viewport.tsx @@ -48,7 +48,9 @@ function occlusionStyle(plugin: PluginContext) { blurKernelSize: 15, radius: 5, samples: 32, - resolutionScale: 1 + resolutionScale: 1, + shadow: { name: 'off', params: {} }, + closeAO: { name: 'off', params: {} } } }, outline: { name: 'on', params: { scale: 1.0, diff --git a/src/examples/lighting/index.ts b/src/examples/lighting/index.ts index 23b254b5e..657d14909 100644 --- a/src/examples/lighting/index.ts +++ b/src/examples/lighting/index.ts @@ -24,7 +24,15 @@ const Canvas3DPresets = { illustrative: { canvas3d: <Preset>{ postprocessing: { - occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } }, + occlusion: { name: 'on', params: { + bias: 0.8, + blurKernelSize: 15, + radius: 5, + samples: 32, + resolutionScale: 1, + shadow: { name: 'off', params: {} }, + closeAO: { name: 'off', params: {} } + } }, outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } } }, renderer: { @@ -36,7 +44,15 @@ const Canvas3DPresets = { occlusion: { canvas3d: <Preset>{ postprocessing: { - occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } }, + occlusion: { name: 'on', params: { + bias: 0.8, + blurKernelSize: 15, + radius: 5, + samples: 32, + resolutionScale: 1, + shadow: { name: 'off', params: {} }, + closeAO: { name: 'off', params: {} } + } }, outline: { name: 'off', params: {} } }, renderer: { diff --git a/src/extensions/cellpack/model.ts b/src/extensions/cellpack/model.ts index 80fb0b3d9..36ff058b3 100644 --- a/src/extensions/cellpack/model.ts +++ b/src/extensions/cellpack/model.ts @@ -604,6 +604,8 @@ export const LoadCellPackModel = StateAction.build({ bias: 1, blurKernelSize: 15, resolutionScale: 1, + shadow: { name: 'off', params: {} }, + closeAO: { name: 'off', params: {} } } }, outline: { diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index 34282e840..1f6c5549b 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -150,7 +150,7 @@ export class DrawPass { } } - this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps); + this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps, renderer.light); } this.depthTextureOpaque.detachFramebuffer(this.colorTarget.framebuffer, 'depth'); @@ -204,7 +204,7 @@ export class DrawPass { } } - this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps); + this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps, renderer.light); } // render transparent primitives and volumes @@ -268,7 +268,7 @@ export class DrawPass { } } - this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps); + this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps, renderer.light); if (!this.packedDepth) { this.depthTextureOpaque.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth'); diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index 591517976..3a25c0347 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -30,6 +30,8 @@ import { SmaaParams, SmaaPass } from './smaa'; import { isTimingMode } from '../../mol-util/debug'; import { BackgroundParams, BackgroundPass } from './background'; import { AssetManager } from '../../mol-util/assets'; +import { Light } from '../../mol-gl/renderer'; +// import { debug } from 'console'; const OutlinesSchema = { ...QuadSchema, @@ -78,12 +80,31 @@ const SsaoSchema = { uProjection: UniformSpec('m4'), uInvProjection: UniformSpec('m4'), + uView: UniformSpec('m4'), uBounds: UniformSpec('v4'), uTexSize: UniformSpec('v2'), uRadius: UniformSpec('f'), uBias: UniformSpec('f'), + + uCloseAO: UniformSpec('i'), + uCloseBias: UniformSpec('f'), + uCloseDistance: UniformSpec('f'), + + uShadow: UniformSpec('i'), + dSSample: DefineSpec('number'), + uSDistance: UniformSpec('f'), + uSTolerance: UniformSpec('f'), + uSBias: UniformSpec('f'), + + uLightDirection: UniformSpec('v3[]'), + uLightColor: UniformSpec('v3[]'), + dLightCount: DefineSpec('number'), + + dOrthographic: DefineSpec('number'), + uNear: UniformSpec('f'), + uFar: UniformSpec('f'), }; type SsaoRenderable = ComputeRenderable<Values<typeof SsaoSchema>> @@ -98,12 +119,31 @@ function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRender uProjection: ValueCell.create(Mat4.identity()), uInvProjection: ValueCell.create(Mat4.identity()), + uView: ValueCell.create(Mat4.identity()), uBounds: ValueCell.create(Vec4()), uTexSize: ValueCell.create(Vec2.create(ctx.gl.drawingBufferWidth, ctx.gl.drawingBufferHeight)), + uLightDirection: ValueCell.create([]), + uLightColor: ValueCell.create([]), + dLightCount: ValueCell.create(0), + uRadius: ValueCell.create(8.0), uBias: ValueCell.create(0.025), + + uCloseAO: ValueCell.create(1), + uCloseBias: ValueCell.create(1.0), + uCloseDistance: ValueCell.create(0.01), + + uShadow: ValueCell.create(1), + dSSample: ValueCell.create(1), + uSDistance: ValueCell.create(3.0), + uSTolerance: ValueCell.create(0.2), + uSBias: ValueCell.create(1.5), + + dOrthographic: ValueCell.create(0), + uNear: ValueCell.create(0.0), + uFar: ValueCell.create(10000.0), }; const schema = { ...SsaoSchema }; @@ -224,6 +264,10 @@ const PostprocessingSchema = { dOutlineEnable: DefineSpec('boolean'), dOutlineScale: DefineSpec('number'), uOutlineThreshold: UniformSpec('f'), + + dLightCount: DefineSpec('number'), + uLightDirection: UniformSpec('v3[]'), + uLightColor: UniformSpec('v3[]'), }; type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>> @@ -254,6 +298,10 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d dOutlineEnable: ValueCell.create(false), dOutlineScale: ValueCell.create(1), uOutlineThreshold: ValueCell.create(0.33), + + dLightCount: ValueCell.create(0), + uLightDirection: ValueCell.create([]), + uLightColor: ValueCell.create([]), }; const schema = { ...PostprocessingSchema }; @@ -269,8 +317,24 @@ export const PostprocessingParams = { samples: PD.Numeric(32, { min: 1, max: 256, step: 1 }), radius: PD.Numeric(5, { min: 0, max: 10, step: 0.1 }, { description: 'Final occlusion radius is 2^x' }), bias: PD.Numeric(0.8, { min: 0, max: 3, step: 0.1 }), + shadow: PD.MappedStatic('on', { + on: PD.Group({ + sSamples: PD.Numeric(1, { min: 1, max: 20, step: 1 }), + sbias: PD.Numeric(0.8, { min: 0.0, max: 1.0, step: 0.01 }), + sdistance: PD.Numeric(3.0, { min: 0.0, max: 100.0, step: 1.0 }), + stolerance: PD.Numeric(1.0, { min: 0.0, max: 100.0, step: 1.0 }), + }), + off: PD.Group({}) + }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }), + closeAO: PD.MappedStatic('on', { + on: PD.Group({ + cbias: PD.Numeric(1.0, { min: 0.0, max: 2.0, step: 0.01 }), + cdistance: PD.Numeric(0.015, { min: 0.0, max: 0.2, step: 0.001 }), + }), + off: PD.Group({}) + }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }), blurKernelSize: PD.Numeric(15, { min: 1, max: 25, step: 2 }), - resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }), + resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }) }), off: PD.Group({}) }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }), @@ -289,6 +353,7 @@ export const PostprocessingParams = { }, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }), background: PD.Group(BackgroundParams, { isFlat: true }), }; + export type PostprocessingProps = PD.Values<typeof PostprocessingParams> export class PostprocessingPass { @@ -404,7 +469,7 @@ export class PostprocessingPass { } } - private updateState(camera: ICamera, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) { + private updateState(camera: ICamera, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps, light: Light) { let needsUpdateMain = false; let needsUpdateSsao = false; let needsUpdateSsaoBlur = false; @@ -417,8 +482,19 @@ export class PostprocessingPass { Mat4.invert(invProjection, camera.projection); if (props.occlusion.name === 'on') { + const shadowocclusionEnabled = props.occlusion.params.shadow.name === 'on' ? 1 : 0; + const closeocclusionEnabled = props.occlusion.params.closeAO.name === 'on' ? 1 : 0; + + ValueCell.update(this.ssaoRenderable.values.uShadow, shadowocclusionEnabled); + ValueCell.update(this.ssaoRenderable.values.uCloseAO, closeocclusionEnabled); + ValueCell.update(this.ssaoRenderable.values.uProjection, camera.projection); ValueCell.update(this.ssaoRenderable.values.uInvProjection, invProjection); + ValueCell.update(this.ssaoRenderable.values.uView, camera.view); + + ValueCell.updateIfChanged(this.ssaoRenderable.values.uNear, camera.near); + ValueCell.updateIfChanged(this.ssaoRenderable.values.uFar, camera.far); + ValueCell.updateIfChanged(this.ssaoRenderable.values.dOrthographic, orthographic); const [w, h] = this.renderable.values.uTexSize.ref.value; const b = this.ssaoRenderable.values.uBounds; @@ -456,6 +532,36 @@ export class PostprocessingPass { ValueCell.updateIfChanged(this.ssaoRenderable.values.uRadius, Math.pow(2, props.occlusion.params.radius)); ValueCell.updateIfChanged(this.ssaoRenderable.values.uBias, props.occlusion.params.bias); + if (props.occlusion.params.shadow.name === 'on') { + ValueCell.update(this.ssaoRenderable.values.dSSample, props.occlusion.params.shadow.params.sSamples); + ValueCell.update(this.ssaoRenderable.values.uSDistance, props.occlusion.params.shadow.params.sdistance); + ValueCell.update(this.ssaoRenderable.values.uSTolerance, props.occlusion.params.shadow.params.stolerance); + + ValueCell.update(this.ssaoRenderable.values.uSBias, props.occlusion.params.shadow.params.sbias); + if (this.ssaoRenderable.values.dSSample.ref.value !== props.occlusion.params.shadow.params.sSamples || + this.ssaoRenderable.values.uSDistance.ref.value !== props.occlusion.params.shadow.params.sdistance || + this.ssaoRenderable.values.uSBias.ref.value !== props.occlusion.params.shadow.params.sbias) { + needsUpdateSsao = true; + } + } + + if (props.occlusion.params.closeAO.name === 'on') { + ValueCell.update(this.ssaoRenderable.values.uCloseBias, props.occlusion.params.closeAO.params.cbias); + ValueCell.update(this.ssaoRenderable.values.uCloseDistance, props.occlusion.params.closeAO.params.cdistance); + if (this.ssaoRenderable.values.uCloseBias.ref.value !== props.occlusion.params.closeAO.params.cbias || + this.ssaoRenderable.values.uCloseDistance.ref.value !== props.occlusion.params.closeAO.params.cdistance) { + needsUpdateSsao = true; + } + } + + + // console.log(light.direction); + ValueCell.update(this.ssaoRenderable.values.uLightDirection, light.direction); + ValueCell.update(this.ssaoRenderable.values.uLightColor, light.color); + if (this.ssaoRenderable.values.dLightCount.ref.value !== light.count) { + ValueCell.update(this.ssaoRenderable.values.dLightCount, light.count); + needsUpdateSsao = true; + } if (this.blurKernelSize !== props.occlusion.params.blurKernelSize) { needsUpdateSsaoBlur = true; @@ -527,6 +633,13 @@ export class PostprocessingPass { if (this.renderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) { needsUpdateMain = true; } ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, occlusionEnabled); + ValueCell.update(this.renderable.values.uLightDirection, light.direction); + ValueCell.update(this.renderable.values.uLightColor, light.color); + if (this.renderable.values.dLightCount.ref.value !== light.count) { + ValueCell.update(this.renderable.values.dLightCount, light.count); + needsUpdateMain = true; + } + if (needsUpdateSsao) { this.ssaoRenderable.update(); } @@ -564,9 +677,9 @@ export class PostprocessingPass { this.transparentBackground = value; } - render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) { + render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps, light: Light) { if (isTimingMode) this.webgl.timer.mark('PostprocessingPass.render'); - this.updateState(camera, transparentBackground, backgroundColor, props); + this.updateState(camera, transparentBackground, backgroundColor, props, light); if (props.outline.name === 'on') { this.outlinesTarget.bind(); diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 086f6db40..a3124581c 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -54,6 +54,7 @@ export const enum MarkingType { interface Renderer { readonly stats: RendererStats readonly props: Readonly<RendererProps> + readonly light: Readonly<Light> clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void clearDepth: (packed?: boolean) => void @@ -118,7 +119,7 @@ export const RendererParams = { }; export type RendererProps = PD.Values<typeof RendererParams> -type Light = { +export type Light = { count: number direction: number[] color: number[] @@ -126,7 +127,7 @@ type Light = { const tmpDir = Vec3(); const tmpColor = Vec3(); -function getLight(props: RendererProps['light'], light?: Light): Light { +export function getLight(props: RendererProps['light'], light?: Light): Light { const { direction, color } = light || { direction: (new Array(5 * 3)).fill(0), color: (new Array(5 * 3)).fill(0), @@ -827,6 +828,9 @@ namespace Renderer { instancedDrawCount: stats.instancedDrawCount, }; }, + get light(): Light { + return light; + }, dispose: () => { // TODO } diff --git a/src/mol-gl/shader/postprocessing.frag.ts b/src/mol-gl/shader/postprocessing.frag.ts index df1acfad5..0cb8547a2 100644 --- a/src/mol-gl/shader/postprocessing.frag.ts +++ b/src/mol-gl/shader/postprocessing.frag.ts @@ -29,6 +29,18 @@ uniform vec2 uOcclusionOffset; uniform float uMaxPossibleViewZDiff; +#if dLightCount != 0 + uniform vec3 uLightDirection[dLightCount]; + uniform vec3 uLightColor[dLightCount]; +#endif + +// #pragma unroll_loop_start +// for (int i = 0; i < dLightCount; ++i) { +// uLightDirection[i]; +// uLightColor[i]; +// } +// #pragma unroll_loop_end + const vec3 occlusionColor = vec3(0.0); #include common diff --git a/src/mol-gl/shader/ssao.frag.ts b/src/mol-gl/shader/ssao.frag.ts index 41850c5ea..d849a80ef 100644 --- a/src/mol-gl/shader/ssao.frag.ts +++ b/src/mol-gl/shader/ssao.frag.ts @@ -16,14 +16,37 @@ uniform sampler2D tDepth; uniform vec2 uTexSize; uniform vec4 uBounds; +uniform float uNear; +uniform float uFar; + +#if dLightCount != 0 + uniform vec3 uLightDirection[dLightCount]; + uniform vec3 uLightColor[dLightCount]; +#endif + uniform vec3 uSamples[dNSamples]; uniform mat4 uProjection; +uniform mat4 uView; uniform mat4 uInvProjection; uniform float uRadius; uniform float uBias; +uniform float uSDistance; +uniform float uSTolerance; +uniform float uSBias; +uniform int uShadow; +uniform int uCloseAO; +uniform float uCloseBias; +uniform float uCloseDistance; + +#define PI 3.14159265 +#define SAMPLES_HIGH 1 +#define SAMPLES_ULTRA 0 +#define SAMPLE_NOISE 1 + + float smootherstep(float edge0, float edge1, float x) { x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); @@ -72,6 +95,241 @@ vec3 normalFromDepth(const in float depth, const in float depth1, const in float return normalize(normal); } +float compareDepths( in float depth1, in float depth2 ){ + float near = uNear; + float far = uFar; + float aorange = 160.0; //uniform + float depthTolerance = 0.0;//uniform + float aoMultiplier = 100.0;//uniform + float aoCap = 1.0;//uniform + float diff = sqrt(clamp(1.0-(depth1-depth2) / (aorange/(far-near)),0.0,1.0)); + float ao = min(aoCap,max(0.0,depth1-depth2-depthTolerance) * aoMultiplier) * diff; + return ao; +} + +float computeAO(in vec2 scrCoord){ + float depth = getDepth(scrCoord); + vec2 invTexSize = 1.0 / uTexSize; + int do_noise = 0; + + float scale = 1.0; //uniform + float aspect = uTexSize.x/uTexSize.y; + int rings = min(6,int(uRadius)); //uniform + int samples = min(6,int(dNSamples)); //uniform + //vec3 randomVec = normalize(vec3(getNoiseVec2(scrCoord), 0.0)); + vec2 noise = getNoiseVec2(scrCoord);//getRandom(srcCoord);// + float w; + float h; + if (do_noise == 1) { + w = invTexSize.x/clamp(depth,0.05,1.0)+(noise.x*(1.0-noise.x))*scale; + h = invTexSize.y/clamp(depth,0.05,1.0)+(noise.y*(1.0-noise.y))*scale; + } + else { + w = invTexSize.x/clamp(depth,0.05,1.0)+0.001*scale;//+(noise.x*(1.0-noise.x)); + h = invTexSize.y/clamp(depth,0.05,1.0)+0.001*scale;//+(noise.y*(1.0-noise.y)); + } + float pw; + float ph; + + float ao; + float s; + + int ringsamples; + for (int i = 1; i <= rings; i += 1){ + ringsamples = i * samples; + for (int j = 0 ; j < ringsamples ; j += 1) { + float step = PI*2.0 / float(ringsamples); + pw = (cos(float(j)*step)*float(i)); + ph = (sin(float(j)*step)*float(i))*aspect; + float v = getDepth( vec2(scrCoord.s+pw*w,scrCoord.t+ph*h)); + ao += compareDepths(depth, v); + s += 1.0; + } + } + ao /= s; + // ao = 1.0-ao; + return ao; +} + + +float computeOcclusion(in float aradius, in mat3 TBN, in vec3 selfViewPos ){ + float occlusion = 0.0; + for(int i = 0; i < dNSamples; i++){ + vec3 sampleViewPos = TBN * uSamples[i]; + sampleViewPos = selfViewPos + sampleViewPos * aradius; + + vec4 offset = vec4(sampleViewPos, 1.0); + offset = uProjection * offset; + offset.xyz = (offset.xyz / offset.w) * 0.5 + 0.5; + + float sampleViewZ = screenSpaceToViewSpace(vec3(offset.xy, getDepth(offset.xy)), uInvProjection).z; + + occlusion += step(sampleViewPos.z + 0.025, sampleViewZ) * smootherstep(0.0, 1.0, aradius / abs(selfViewPos.z - sampleViewZ)); + } + return occlusion; +} + +float calcAO(in vec2 tcoord, in vec2 uv, in vec3 p, in vec3 cnorm) +{ + float _Bias = 0.129; + float _Intensity = uCloseBias; + float _Distance = 0.001; + vec2 t = tcoord + uv; + float depth = getDepth(t); + vec3 diff = screenSpaceToViewSpace(vec3(t, depth), uInvProjection) - p; + vec3 v = normalize(diff); + float d = length(diff) * _Distance; + // cnorm = normalize(_WorldSpaceCameraPos - p); + return max(0.0, dot(cnorm, v) - _Bias) * (1.0 / (1.0 + d)) * _Intensity; +} + +float invlerp(float from, float to, float value) +{ + return (value - from) / (to - from); +} + +float getViewZ(const in float depth) { + #if dOrthographic == 1 + return orthographicDepthToViewZ(depth, uNear, uFar); + #else + return perspectiveDepthToViewZ(depth, uNear, uFar); + #endif +} + +// Gold Noise function +float PHI = 1.61803398874989484820459 * 00000.1; // Golden Ratio +float PIT = 3.14159265358979323846264 * 00000.1; // PI +float SRT = 1.41421356237309504880169 * 10000.0; // Square Root of Two + +float random_0t1(in vec2 coordinate, in float seed) +{ + return fract(sin(dot(coordinate*seed, vec2(PHI, PIT)))*SRT); +} + +float ssao(in vec2 uv, in vec3 normal) +{ + float _SampleRadius = 5.0; + float _DistanceCutoff = 100.0; + float _CutoffFalloff = 25.0; + + vec2 CROSS[4] = vec2[4]( vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0) ); + float depth = getDepth(uv); + float eyeDepth = getViewZ(depth); + vec3 position = screenSpaceToViewSpace(vec3(uv, depth), uInvProjection); + float radius = uCloseDistance; // original was max(_SampleRadius / eyeDepth, 0.005); + // clip(_DistanceCutoff - eyeDepth); // Skip out of range pixels + if (_DistanceCutoff - eyeDepth < 0.0) discard; + #if defined(SAMPLE_NOISE) + float a = random_0t1(uv,depth); + float b = random_0t1(uv,eyeDepth); + vec2 random = normalize(vec2(a,b)); + // original used a texture for noise + // vec2 random = normalize(tex2D(_NoiseTex, _ScreenParams.xy * uv / _NoiseSize).rg * 2.0 - 1.0); + #endif + float ao = 0.0; + // Sampling + for (int j = 0; j < 4; j++) + { + vec2 coord1; + + #if defined(SAMPLE_NOISE) + coord1 = reflect(CROSS[j], random) * radius; + #else + coord1 = CROSS[j] * radius; + #endif + + // #if !SAMPLES_VERY_LOW + vec2 coord2 = coord1 * 0.707; + coord2 = vec2(coord2.x - coord2.y, coord2.x + coord2.y); + // #endif + + #if defined(SAMPLES_ULTRA) // 20 + ao += calcAO(uv, coord1 * 0.20, position, normal); + ao += calcAO(uv, coord2 * 0.40, position, normal); + ao += calcAO(uv, coord1 * 0.60, position, normal); + ao += calcAO(uv, coord2 * 0.80, position, normal); + ao += calcAO(uv, coord1, position, normal); + #elif defined(SAMPLES_HIGH) // 16 + ao += calcAO(uv, coord1 * 0.25, position, normal); + ao += calcAO(uv, coord2 * 0.50, position, normal); + ao += calcAO(uv, coord1 * 0.75, position, normal); + ao += calcAO(uv, coord2, position, normal); + #elif defined(SAMPLES_MEDIUM) // 12 + ao += calcAO(uv, coord1 * 0.30, position, normal); + ao += calcAO(uv, coord2 * 0.60, position, normal); + ao += calcAO(uv, coord1 * 0.90, position, normal); + #elif defined(SAMPLES_LOW ) // 8 + ao += calcAO(uv, coord1 * 0.30, position, normal); + ao += calcAO(uv, coord2 * 0.80, position, normal); + #else // 4 + ao += calcAO(uv, coord1 * 0.50, position, normal); + #endif + } + + #if SAMPLES_ULTRA + ao /= 20.0; + #elif SAMPLES_HIGH + ao /= 16.0; + #elif SAMPLES_MEDIUM + ao /= 12.0; + #elif SAMPLES_LOW + ao /= 8.0; + #else + ao /= 4.0; + #endif + + // Distance cutoff + ao = mix(1.0 - ao, 1.0, saturate(invlerp(_DistanceCutoff - _CutoffFalloff, _DistanceCutoff, eyeDepth))); + + return ao; +} + +float ScreenSpaceShadows(in vec2 uv, in vec3 position, in vec3 light_direction) +{ + // Settings + int g_sss_steps = dSSample; // Quality/performancedNSamples + float g_sss_ray_max_distance = uSDistance; // Max shadow length + float g_sss_tolerance = uSTolerance; // Error in favor of reducing gaps + float g_sss_step_length = g_sss_ray_max_distance / float(g_sss_steps); + float uvdepth = getDepth(uv); + + float eyeDepth = getViewZ(uvdepth); + // Compute ray position and direction (in view-space) + vec3 ray_pos = position; + vec3 ray_dir = -light_direction; //light direction in View space + vec2 uv_pos = uv; + // Compute ray step + vec3 ray_step = ray_dir * g_sss_step_length; + vec2 uv_step = ray_dir.xy * g_sss_step_length; + // Ray march towards the light + float occlusion = 0.0; + + vec4 ray_uv = vec4(0.0,0.0,0.0,0.0); + for (int i = 0; i < g_sss_steps; i++) + { + // Step the ray + uv_pos += uv_step; + ray_pos += ray_step; + // Compute the difference between the ray's and the camera's depth + ray_uv = (uProjection * vec4(ray_pos,1.0)); + ray_uv.xyz = (ray_uv.xyz / ray_uv.w) * 0.5 + 0.5; + float depth = getDepth(ray_uv.xy); + float depth_z = getViewZ(depth); + float depth_delta = ray_pos.z - depth_z; + if (depth_delta < g_sss_tolerance){ + // original test : if (abs(g_sss_tolerance - depth_delta) < g_sss_tolerance){ + occlusion = 1.0; + vec2 fade = max(12.0 * abs(ray_uv.xy - 0.5) - 5.0, vec2(0.0,0.0)); + occlusion *= saturate(1.0 - dot(fade, fade)); + break; + } + } + // Fade out as we approach the edges of the screen + // occlusion *= screen_fade(ray_uv);return 1.0 - occlusion; + occlusion = 1.0 - (uSBias * occlusion); + return occlusion; +} + // StarCraft II Ambient Occlusion by [Filion and McNaughton 2008] void main(void) { vec2 invTexSize = 1.0 / uTexSize; @@ -100,23 +358,40 @@ void main(void) { vec3 bitangent = cross(selfViewNormal, tangent); mat3 TBN = mat3(tangent, bitangent, selfViewNormal); - float occlusion = 0.0; - for(int i = 0; i < dNSamples; i++){ - vec3 sampleViewPos = TBN * uSamples[i]; - sampleViewPos = selfViewPos + sampleViewPos * uRadius; - - vec4 offset = vec4(sampleViewPos, 1.0); - offset = uProjection * offset; - offset.xyz = (offset.xyz / offset.w) * 0.5 + 0.5; - - float sampleViewZ = screenSpaceToViewSpace(vec3(offset.xy, getDepth(offset.xy)), uInvProjection).z; - - occlusion += step(sampleViewPos.z + 0.025, sampleViewZ) * smootherstep(0.0, 1.0, uRadius / abs(selfViewPos.z - sampleViewZ)); - } + float occlusion = computeOcclusion(uRadius, TBN, selfViewPos); occlusion = 1.0 - (uBias * occlusion / float(dNSamples)); + + /* + // alternative ao algo + float ao = computeAO(selfCoords); + ao = clamp(ao,0.0,1.0); + if ( ao > 1.0 ) {ao = 1.0 ;} + if ( ao < 0.0 ) {ao = 0.0 ;} + if (selfDepth > 1.0 ) {ao = 1.0 ;} + if (selfDepth < 0.0 ) {ao = 0.0 ;} + ao = 1.0 - (uBias * ao); + */ - vec2 packedOcclusion = packUnitIntervalToRG(occlusion); - + float ao = 1.0; + if (uCloseAO == 1){ + ao = saturate(ssao(selfCoords, selfViewNormal)); + } + float o = 9999.9; + if (uShadow == 1) { + #if dLightCount != 0 + float sh[dLightCount]; + #pragma unroll_loop_start + for (int i = 0; i < dLightCount; ++i) { + sh[i] = ScreenSpaceShadows(selfCoords, selfViewPos, uLightDirection[i]); + o = min(o,min(min(sh[i],ao),occlusion)); + } + #pragma unroll_loop_end + #endif + } + else{ + o = min(ao,occlusion); + } + vec2 packedOcclusion = packUnitIntervalToRG(o); gl_FragColor = vec4(packedOcclusion, selfPackedDepth); } `; \ No newline at end of file diff --git a/src/mol-plugin-ui/structure/quick-styles.tsx b/src/mol-plugin-ui/structure/quick-styles.tsx index a0c535acf..8b5a38ebe 100644 --- a/src/mol-plugin-ui/structure/quick-styles.tsx +++ b/src/mol-plugin-ui/structure/quick-styles.tsx @@ -60,8 +60,8 @@ export class QuickStyles extends PurePluginUIComponent { }, occlusion: { name: 'on', - params: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1 } - }, + params: { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1, shadow: { name: 'off', params: {} }, closeAO: { name: 'off', params: {} } } + } } }); } @@ -79,13 +79,15 @@ export class QuickStyles extends PurePluginUIComponent { params: pp.outline.name === 'on' ? pp.outline.params : { scale: 1, color: Color(0x000000), threshold: 0.33 } - }, + } + /* occlusion: { name: 'on', params: pp.occlusion.name === 'on' ? pp.occlusion.params - : { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1 } + : { bias: 0.8, blurKernelSize: 15, radius: 5, samples: 32, resolutionScale: 1, shadow: { name: 'off', params: {} }, closeAO: { name: 'off', params: {}} } }, + */ } }); } -- GitLab