Browse Source

fake screen space shadow, second pass ssao

ludovic autin 2 years ago
parent
commit
c7dad00908

+ 3 - 1
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,

+ 18 - 2
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: {

+ 2 - 0
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: {

+ 3 - 3
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');

+ 117 - 4
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();

+ 6 - 2
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
             }

+ 12 - 0
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

+ 290 - 15
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);
 }
 `;

+ 6 - 4
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: {}} }
                     },
+                    */
                 }
             });
         }