Browse Source

renaming, better defaults, ao bias, better outline thresholding, whitespace changes

AronKovacs 4 years ago
parent
commit
94d293a4d3

+ 3 - 3
src/apps/docking-viewer/viewport.tsx

@@ -48,12 +48,12 @@ function occlusionStyle(plugin: PluginContext) {
             occlusion: { name: 'on', params: {
                 samples: 64,
                 radius: 8,
-                bias: 0.025,
-                kernelSize: 13
+                bias: 1.0,
+                blurKernelSize: 13
             } },
             outline: { name: 'on', params: {
                 scale: 1.0,
-                threshold: 0.8
+                threshold: 0.33
             } }
         }
     } });

+ 3 - 3
src/examples/lighting/index.ts

@@ -24,8 +24,8 @@ const Canvas3DPresets = {
             mode: 'temporal' as Canvas3DProps['multiSample']['mode']
         },
         postprocessing: {
-            occlusion: { name: 'on', params: { samples: 64, radius: 8, bias: 0.025, kernelSize: 13 } },
-            outline: { name: 'on', params: { scale: 1, threshold: 0.8 } }
+            occlusion: { name: 'on', params: { samples: 64, radius: 8, bias: 1.0, blurKernelSize: 13 } },
+            outline: { name: 'on', params: { scale: 1, threshold: 0.33 } }
         },
         renderer: {
             ambientIntensity: 1,
@@ -37,7 +37,7 @@ const Canvas3DPresets = {
             mode: 'temporal' as Canvas3DProps['multiSample']['mode']
         },
         postprocessing: {
-            occlusion: { name: 'on', params: { samples: 64, radius: 8, bias: 0.025, kernelSize: 13 } },
+            occlusion: { name: 'on', params: { samples: 64, radius: 8, bias: 1.0, blurKernelSize: 13 } },
             outline: { name: 'off', params: { } }
         },
         renderer: {

+ 38 - 44
src/mol-canvas3d/passes/postprocessing.ts

@@ -78,13 +78,13 @@ const SsaoSchema = {
 
 type SsaoRenderable = ComputeRenderable<Values<typeof SsaoSchema>>
 
-function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture, nSamples: number): SsaoRenderable {
+function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRenderable {
     const values: Values<typeof SsaoSchema> = {
         ...QuadValues,
         tDepth: ValueCell.create(depthTexture),
 
-        uSamples: ValueCell.create(getSamples(nSamples)),
-        dNSamples: ValueCell.create(nSamples),
+        uSamples: ValueCell.create([0.0, 0.0, 1.0]),
+        dNSamples: ValueCell.create(1),
 
         uProjection: ValueCell.create(Mat4.identity()),
         uInvProjection: ValueCell.create(Mat4.identity()),
@@ -122,14 +122,14 @@ const SsaoBlurSchema = {
 
 type SsaoBlurRenderable = ComputeRenderable<Values<typeof SsaoBlurSchema>>
 
-function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, blurKernelSize: number, direction: 'horizontal' | 'vertical'): SsaoBlurRenderable {
+function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, direction: 'horizontal' | 'vertical'): SsaoBlurRenderable {
     const values: Values<typeof SsaoBlurSchema> = {
         ...QuadValues,
         tSsaoDepth: ValueCell.create(ssaoDepthTexture),
         uTexSize: ValueCell.create(Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())),
 
-        uKernel: ValueCell.create(getBlurKernel(blurKernelSize)),
-        dOcclusionKernelSize: ValueCell.create(blurKernelSize),
+        uKernel: ValueCell.create([0.0]),
+        dOcclusionKernelSize: ValueCell.create(1),
 
         uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
         uBlurDirectionY: ValueCell.create(direction === 'vertical' ? 1 : 0),
@@ -160,33 +160,15 @@ function getBlurKernel(kernelSize: number): number[] {
     return kernel;
 }
 
-function getSamples(nSamples: number): number[] {
-    let vectorSamples = [];
+function getSamples(vectorSamples: Vec3[], nSamples: number): number[] {
+    let samples = [];
     for (let i = 0; i < nSamples; i++) {
-        let v = Vec3();
-
-        v[0] = Math.random() * 2.0 - 1.0;
-        v[1] = Math.random() * 2.0 - 1.0;
-        v[2] = Math.random();
-
-        Vec3.normalize(v, v);
-
-        Vec3.scale(v, v, Math.random());
-
-        let scale = (i * i) / (nSamples * nSamples);
+        let scale = (i * i + 2.0 * i + 1) / (nSamples * nSamples);
         scale = 0.1 + scale * (1.0 - 0.1);
 
-        Vec3.scale(v, v, scale);
-
-        vectorSamples.push(v);
-    }
-
-    let samples = [];
-    for (let i = 0; i < nSamples; i++) {
-        let v = vectorSamples[i];
-        samples.push(v[0]);
-        samples.push(v[1]);
-        samples.push(v[2]);
+        samples.push(vectorSamples[i][0] * scale);
+        samples.push(vectorSamples[i][1] * scale);
+        samples.push(vectorSamples[i][2] * scale);
     }
 
     return samples;
@@ -257,16 +239,16 @@ export const PostprocessingParams = {
     occlusion: PD.MappedStatic('off', {
         on: PD.Group({
             samples: PD.Numeric(64, {min: 1, max: 256, step: 1}),
-            radius: PD.Numeric(8.0, { min: 0.1, max: 64, step: 0.1 }),
-            bias: PD.Numeric(0.025, { min: 0, max: 1, step: 0.001 }),
-            kernelSize: PD.Numeric(13, { min: 1, max: 25, step: 2 }),
+            radius: PD.Numeric(8.0, { min: 1, max: 64, step: 1 }),
+            bias: PD.Numeric(1.0, { min: 0, max: 1, step: 0.001 }),
+            blurKernelSize: PD.Numeric(13, { min: 1, max: 25, step: 2 }),
         }),
         off: PD.Group({})
     }, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
     outline: PD.MappedStatic('off', {
         on: PD.Group({
             scale: PD.Numeric(1, { min: 1, max: 5, step: 1 }),
-            threshold: PD.Numeric(0.1, { min: 0.01, max: 1, step: 0.01 }),
+            threshold: PD.Numeric(0.33, { min: 0.01, max: 1, step: 0.01 }),
         }),
         off: PD.Group({})
     }, { cycle: true, description: 'Draw outline around 3D objects' }),
@@ -284,7 +266,7 @@ export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
 
 export class PostprocessingPass {
     static isEnabled(props: PostprocessingProps) {
-        return props.occlusion.name === 'on' || props.outline.name === 'on' || props.antialiasing.name === 'on';
+        return props.occlusion.name === 'on' || props.outline.name === 'on';
     }
 
     readonly target: RenderTarget
@@ -292,6 +274,7 @@ export class PostprocessingPass {
     private readonly outlinesTarget: RenderTarget
     private readonly outlinesRenderable: OutlinesRenderable
 
+    private readonly randomHemisphereVector: Vec3[]
     private readonly ssaoFramebuffer: Framebuffer
     private readonly ssaoBlurFirstPassFramebuffer: Framebuffer
     private readonly ssaoBlurSecondPassFramebuffer: Framebuffer
@@ -313,14 +296,24 @@ export class PostprocessingPass {
         const width = colorTarget.getWidth();
         const height = colorTarget.getHeight();
 
-        this.nSamples = 64;
-        this.blurKernelSize = 13;
+        this.nSamples = 1;
+        this.blurKernelSize = 1;
 
         this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
 
         this.outlinesTarget = webgl.createRenderTarget(width, height, false);
         this.outlinesRenderable = getOutlinesRenderable(webgl, depthTexture);
 
+        this.randomHemisphereVector = [];
+        for (let i = 0; i < 256; i++) {
+            let v = Vec3();
+            v[0] = Math.random() * 2.0 - 1.0;
+            v[1] = Math.random() * 2.0 - 1.0;
+            v[2] = Math.random();
+            Vec3.normalize(v, v);
+            Vec3.scale(v, v, Math.random());
+            this.randomHemisphereVector.push(v);
+        }
         this.ssaoFramebuffer = webgl.resources.framebuffer();
         this.ssaoBlurFirstPassFramebuffer = webgl.resources.framebuffer();
         this.ssaoBlurSecondPassFramebuffer = webgl.resources.framebuffer();
@@ -335,9 +328,9 @@ export class PostprocessingPass {
 
         this.ssaoDepthTexture.attachFramebuffer(this.ssaoBlurSecondPassFramebuffer, 'color0');
 
-        this.ssaoRenderable = getSsaoRenderable(webgl, depthTexture, this.nSamples);
-        this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, this.blurKernelSize, 'horizontal');
-        this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, this.blurKernelSize, 'vertical');
+        this.ssaoRenderable = getSsaoRenderable(webgl, depthTexture);
+        this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
+        this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
         this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture,  depthTexture, packedDepth, this.outlinesTarget.texture, this.ssaoDepthTexture);
     }
 
@@ -387,16 +380,16 @@ export class PostprocessingPass {
                 needsUpdateSsao = true;
 
                 this.nSamples = props.occlusion.params.samples;
-                ValueCell.updateIfChanged(this.ssaoRenderable.values.uSamples, getSamples(this.nSamples));
+                ValueCell.updateIfChanged(this.ssaoRenderable.values.uSamples, getSamples(this.randomHemisphereVector, this.nSamples));
                 ValueCell.updateIfChanged(this.ssaoRenderable.values.dNSamples, this.nSamples);
             }
             ValueCell.updateIfChanged(this.ssaoRenderable.values.uRadius, props.occlusion.params.radius);
             ValueCell.updateIfChanged(this.ssaoRenderable.values.uBias, props.occlusion.params.bias);
 
-            if (this.blurKernelSize !== props.occlusion.params.kernelSize) {
+            if (this.blurKernelSize !== props.occlusion.params.blurKernelSize) {
                 needsUpdateSsaoBlur = true;
 
-                this.blurKernelSize = props.occlusion.params.kernelSize;
+                this.blurKernelSize = props.occlusion.params.blurKernelSize;
                 let kernel = getBlurKernel(this.blurKernelSize);
 
                 ValueCell.updateIfChanged(this.ssaoBlurFirstPassRenderable.values.uKernel, kernel);
@@ -408,7 +401,8 @@ export class PostprocessingPass {
         }
 
         if (props.outline.name === 'on') {
-            let maxPossibleViewZDiff = props.outline.params.threshold * (camera.fogFar - camera.near);
+            let factor = Math.pow(1000, props.outline.params.threshold) / 1000;
+            let maxPossibleViewZDiff = factor * (camera.far - camera.near);
 
             ValueCell.updateIfChanged(this.outlinesRenderable.values.uNear, camera.near);
             ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far);

+ 1 - 1
src/mol-gl/shader/chunks/common.glsl.ts

@@ -52,7 +52,7 @@ float decodeFloatRGB(const in vec3 rgb) {
 vec2 packUnitIntervalToRG(const in float v) {
     vec2 enc;
     enc.xy = vec2(fract(v * 256.0), v);
-	enc.y -= enc.x * (1.0 / 256.0);
+    enc.y -= enc.x * (1.0 / 256.0);
     enc.xy *=  256.0 / 255.0;
 
     return enc;

+ 50 - 50
src/mol-gl/shader/chunks/light-frag-params.glsl.ts

@@ -15,97 +15,97 @@ uniform float uMetalness;
 uniform float uRoughness;
 
 struct PhysicalMaterial {
-	vec3 diffuseColor;
-	float specularRoughness;
-	vec3 specularColor;
+    vec3 diffuseColor;
+    float specularRoughness;
+    vec3 specularColor;
 };
 
 struct IncidentLight {
-	vec3 color;
-	vec3 direction;
+    vec3 color;
+    vec3 direction;
 };
 
 struct ReflectedLight {
-	vec3 directDiffuse;
-	vec3 directSpecular;
-	vec3 indirectDiffuse;
+    vec3 directDiffuse;
+    vec3 directSpecular;
+    vec3 indirectDiffuse;
 };
 
 struct GeometricContext {
-	vec3 position;
-	vec3 normal;
-	vec3 viewDir;
+    vec3 position;
+    vec3 normal;
+    vec3 viewDir;
 };
 
 vec3 F_Schlick(const in vec3 specularColor, const in float dotLH) {
-	// Original approximation by Christophe Schlick '94
-	// float fresnel = pow( 1.0 - dotLH, 5.0 );
-	// Optimized variant (presented by Epic at SIGGRAPH '13)
-	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
-	float fresnel = exp2((-5.55473 * dotLH - 6.98316) * dotLH);
-	return (1.0 - specularColor) * fresnel + specularColor;
+    // Original approximation by Christophe Schlick '94
+    // float fresnel = pow( 1.0 - dotLH, 5.0 );
+    // Optimized variant (presented by Epic at SIGGRAPH '13)
+    // https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
+    float fresnel = exp2((-5.55473 * dotLH - 6.98316) * dotLH);
+    return (1.0 - specularColor) * fresnel + specularColor;
 }
 
 // Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
 // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
 float G_GGX_SmithCorrelated(const in float alpha, const in float dotNL, const in float dotNV) {
-	float a2 = pow2(alpha);
-	// dotNL and dotNV are explicitly swapped. This is not a mistake.
-	float gv = dotNL * sqrt(a2 + (1.0 - a2) * pow2(dotNV));
-	float gl = dotNV * sqrt(a2 + (1.0 - a2) * pow2(dotNL));
-	return 0.5 / max(gv + gl, EPSILON);
+    float a2 = pow2(alpha);
+    // dotNL and dotNV are explicitly swapped. This is not a mistake.
+    float gv = dotNL * sqrt(a2 + (1.0 - a2) * pow2(dotNV));
+    float gl = dotNV * sqrt(a2 + (1.0 - a2) * pow2(dotNL));
+    return 0.5 / max(gv + gl, EPSILON);
 }
 
 // Microfacet Models for Refraction through Rough Surfaces - equation (33)
 // http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
 // alpha is "roughness squared" in Disney’s reparameterization
 float D_GGX(const in float alpha, const in float dotNH) {
-	float a2 = pow2(alpha);
-	float denom = pow2(dotNH) * (a2 - 1.0) + 1.0; // avoid alpha = 0 with dotNH = 1
-	return RECIPROCAL_PI * a2 / pow2(denom);
+    float a2 = pow2(alpha);
+    float denom = pow2(dotNH) * (a2 - 1.0) + 1.0; // avoid alpha = 0 with dotNH = 1
+    return RECIPROCAL_PI * a2 / pow2(denom);
 }
 
 vec3 BRDF_Diffuse_Lambert(const in vec3 diffuseColor) {
-	return RECIPROCAL_PI * diffuseColor;
+    return RECIPROCAL_PI * diffuseColor;
 }
 
 // GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
 vec3 BRDF_Specular_GGX(const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness) {
-	float alpha = pow2(roughness); // UE4's roughness
-	vec3 halfDir = normalize(incidentLight.direction + geometry.viewDir);
-
-	float dotNL = saturate(dot(geometry.normal, incidentLight.direction));
-	float dotNV = saturate(dot(geometry.normal, geometry.viewDir));
-	float dotNH = saturate(dot(geometry.normal, halfDir));
-	float dotLH = saturate(dot(incidentLight.direction, halfDir));
-
-	vec3 F = F_Schlick(specularColor, dotLH);
-	float G = G_GGX_SmithCorrelated(alpha, dotNL, dotNV);
-	float D = D_GGX(alpha, dotNH);
-	return F * (G * D);
+    float alpha = pow2(roughness); // UE4's roughness
+    vec3 halfDir = normalize(incidentLight.direction + geometry.viewDir);
+
+    float dotNL = saturate(dot(geometry.normal, incidentLight.direction));
+    float dotNV = saturate(dot(geometry.normal, geometry.viewDir));
+    float dotNH = saturate(dot(geometry.normal, halfDir));
+    float dotLH = saturate(dot(incidentLight.direction, halfDir));
+
+    vec3 F = F_Schlick(specularColor, dotLH);
+    float G = G_GGX_SmithCorrelated(alpha, dotNL, dotNV);
+    float D = D_GGX(alpha, dotNH);
+    return F * (G * D);
 }
 
 // ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile
 vec3 BRDF_Specular_GGX_Environment(const in GeometricContext geometry, const in vec3 specularColor, const in float roughness) {
-	float dotNV = saturate(dot(geometry.normal, geometry.viewDir));
-	const vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022);
-	const vec4 c1 = vec4(1, 0.0425, 1.04, -0.04);
-	vec4 r = roughness * c0 + c1;
-	float a004 = min(r.x * r.x, exp2(-9.28 * dotNV)) * r.x + r.y;
-	vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
-	return specularColor * AB.x + AB.y;
+    float dotNV = saturate(dot(geometry.normal, geometry.viewDir));
+    const vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022);
+    const vec4 c1 = vec4(1, 0.0425, 1.04, -0.04);
+    vec4 r = roughness * c0 + c1;
+    float a004 = min(r.x * r.x, exp2(-9.28 * dotNV)) * r.x + r.y;
+    vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
+    return specularColor * AB.x + AB.y;
 }
 
 void RE_Direct_Physical(const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
-	float dotNL = saturate(dot(geometry.normal, directLight.direction));
+    float dotNL = saturate(dot(geometry.normal, directLight.direction));
     vec3 irradiance = dotNL * directLight.color;
-	irradiance *= PI; // punctual light
+    irradiance *= PI; // punctual light
 
-	reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX(directLight, geometry, material.specularColor, material.specularRoughness);
-	reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor);
+    reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX(directLight, geometry, material.specularColor, material.specularRoughness);
+    reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor);
 }
 
 void RE_IndirectDiffuse_Physical(const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
-	reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor);
+    reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert(material.diffuseColor);
 }
 `;

+ 26 - 26
src/mol-gl/shader/outlines.frag.ts

@@ -20,23 +20,23 @@ uniform float uMaxPossibleViewZDiff;
 #include common
 
 float perspectiveDepthToViewZ(const in float invClipZ, const in float near, const in float far) {
-	return (near * far) / ((far - near) * invClipZ - far);
+    return (near * far) / ((far - near) * invClipZ - far);
 }
 
 float orthographicDepthToViewZ(const in float linearClipZ, const in float near, const in float far) {
-	return linearClipZ * (near - far) - near;
+    return linearClipZ * (near - far) - near;
 }
 
 float getViewZ(const in float depth) {
-	#if dOrthographic == 1
-		return orthographicDepthToViewZ(depth, uNear, uFar);
-	#else
-		return perspectiveDepthToViewZ(depth, uNear, uFar);
-	#endif
+    #if dOrthographic == 1
+        return orthographicDepthToViewZ(depth, uNear, uFar);
+    #else
+        return perspectiveDepthToViewZ(depth, uNear, uFar);
+    #endif
 }
 
 float getDepth(const in vec2 coords) {
-	return unpackRGBAToDepth(texture2D(tDepth, coords));
+    return unpackRGBAToDepth(texture2D(tDepth, coords));
 }
 
 bool isBackground(const in float depth) {
@@ -45,29 +45,29 @@ bool isBackground(const in float depth) {
 
 void main(void) {
     float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
-	
-	vec2 coords = gl_FragCoord.xy / uTexSize;
+    
+    vec2 coords = gl_FragCoord.xy / uTexSize;
     vec2 invTexSize = 1.0 / uTexSize;
 
-	float selfDepth = getDepth(coords);
-	float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(getDepth(coords));
+    float selfDepth = getDepth(coords);
+    float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(getDepth(coords));
 
-	float outline = 1.0;
-	float bestDepth = 1.0;
+    float outline = 1.0;
+    float bestDepth = 1.0;
 
-	for (int y = -1; y <= 1; y++) {
-		for (int x = -1; x <= 1; x++) {
-			vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize;
-			float sampleDepth = getDepth(sampleCoords);
-			float sampleViewZ = isBackground(sampleDepth) ? backgroundViewZ : getViewZ(sampleDepth);
+    for (int y = -1; y <= 1; y++) {
+        for (int x = -1; x <= 1; x++) {
+            vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize;
+            float sampleDepth = getDepth(sampleCoords);
+            float sampleViewZ = isBackground(sampleDepth) ? backgroundViewZ : getViewZ(sampleDepth);
 
-			if (abs(selfViewZ - sampleViewZ) > uMaxPossibleViewZDiff && selfDepth > sampleDepth && sampleDepth <= bestDepth) {
-				outline = 0.0;
-				bestDepth = sampleDepth;
-			}
-		}
-	}
+            if (abs(selfViewZ - sampleViewZ) > uMaxPossibleViewZDiff && selfDepth > sampleDepth && sampleDepth <= bestDepth) {
+                outline = 0.0;
+                bestDepth = sampleDepth;
+            }
+        }
+    }
 
-	gl_FragColor = vec4(outline, packUnitIntervalToRG(bestDepth), 0.0);
+    gl_FragColor = vec4(outline, packUnitIntervalToRG(bestDepth), 0.0);
 }
 `;

+ 70 - 70
src/mol-gl/shader/postprocessing.frag.ts

@@ -35,23 +35,23 @@ const vec4 occlusionColor = vec4(0.0, 0.0, 0.0, 1.0);
 #include common
 
 float perspectiveDepthToViewZ(const in float invClipZ, const in float near, const in float far) {
-	return (near * far) / ((far - near) * invClipZ - far);
+    return (near * far) / ((far - near) * invClipZ - far);
 }
 
 float orthographicDepthToViewZ(const in float linearClipZ, const in float near, const in float far) {
-	return linearClipZ * (near - far) - near;
+    return linearClipZ * (near - far) - near;
 }
 
 float getViewZ(const in float depth) {
-	#if dOrthographic == 1
-		return orthographicDepthToViewZ(depth, uNear, uFar);
-	#else
-		return perspectiveDepthToViewZ(depth, uNear, uFar);
-	#endif
+    #if dOrthographic == 1
+        return orthographicDepthToViewZ(depth, uNear, uFar);
+    #else
+        return perspectiveDepthToViewZ(depth, uNear, uFar);
+    #endif
 }
 
 float getDepth(const in vec2 coords) {
-	return unpackRGBAToDepth(texture2D(tDepth, coords));
+    return unpackRGBAToDepth(texture2D(tDepth, coords));
 }
 
 bool isBackground(const in float depth) {
@@ -59,73 +59,73 @@ bool isBackground(const in float depth) {
 }
 
 float getOutline(const in vec2 coords, out float closestTexel) {
-	float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
-	vec2 invTexSize = 1.0 / uTexSize;
-
-	float selfDepth = getDepth(coords);
-	float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(getDepth(coords));
-
-	float outline = 1.0;
-	closestTexel = 1.0;
-	for (float y = -uOutlineScale; y <= uOutlineScale; y++) {
-		for (float x = -uOutlineScale; x <= uOutlineScale; x++) {
-			if (x * x + y * y > uOutlineScale * uOutlineScale) {
-				continue;
-			}
-
-			vec2 sampleCoords = coords + vec2(x, y) * invTexSize;
-
-			vec4 sampleOutlineCombined = texture2D(tOutlines, sampleCoords);
-			float sampleOutline = sampleOutlineCombined.r;
-			float sampleOutlineDepth = unpackRGToUnitInterval(sampleOutlineCombined.gb);
-
-			if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineDepth) > uMaxPossibleViewZDiff) {
-				outline = 0.0;
-				closestTexel = sampleOutlineDepth;
-			}
-		}
-	}
-	return outline;
+    float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
+    vec2 invTexSize = 1.0 / uTexSize;
+
+    float selfDepth = getDepth(coords);
+    float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(getDepth(coords));
+
+    float outline = 1.0;
+    closestTexel = 1.0;
+    for (float y = -uOutlineScale; y <= uOutlineScale; y++) {
+        for (float x = -uOutlineScale; x <= uOutlineScale; x++) {
+            if (x * x + y * y > uOutlineScale * uOutlineScale) {
+                continue;
+            }
+
+            vec2 sampleCoords = coords + vec2(x, y) * invTexSize;
+
+            vec4 sampleOutlineCombined = texture2D(tOutlines, sampleCoords);
+            float sampleOutline = sampleOutlineCombined.r;
+            float sampleOutlineDepth = unpackRGToUnitInterval(sampleOutlineCombined.gb);
+
+            if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineDepth) > uMaxPossibleViewZDiff) {
+                outline = 0.0;
+                closestTexel = sampleOutlineDepth;
+            }
+        }
+    }
+    return outline;
 }
 
 float getSsao(vec2 coords) {
-	float rawSsao = unpackRGToUnitInterval(texture(tSsaoDepth, coords).xy);
-	if (rawSsao > 0.999) {
-		return 1.0;
-	} else if (rawSsao > 0.001) {
-		return rawSsao;
-	}
-	return 0.0;
+    float rawSsao = unpackRGToUnitInterval(texture(tSsaoDepth, coords).xy);
+    if (rawSsao > 0.999) {
+        return 1.0;
+    } else if (rawSsao > 0.001) {
+        return rawSsao;
+    }
+    return 0.0;
 }
 
 void main(void) {
-	vec2 coords = gl_FragCoord.xy / uTexSize;
-	vec4 color = texture(tColor, coords);
-
-	#ifdef dOutlineEnable
-		float closestTexel;
-		float outline = getOutline(coords, closestTexel);
-    	
-		if (outline == 0.0) {
-			color.rgb *= outline;
-			float viewDist = abs(getViewZ(closestTexel));
-			float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
-			if (color.a != 1.0) {
-				color.a = 1.0 - fogFactor;
-			}
-			color.rgb = mix(color.rgb, uFogColor, fogFactor);
-		}
-	#endif
-
-	// occlusion needs to be handled after outline to darken them properly
-	#ifdef dOcclusionEnable
-		float depth = getDepth(coords);
-		if (!isBackground(depth)) {
-			float occlusionFactor = getSsao(coords);
-			color = mix(occlusionColor, color, occlusionFactor);
-		}
-	#endif
-
-	gl_FragColor = color;
+    vec2 coords = gl_FragCoord.xy / uTexSize;
+    vec4 color = texture(tColor, coords);
+
+    #ifdef dOutlineEnable
+        float closestTexel;
+        float outline = getOutline(coords, closestTexel);
+
+        if (outline == 0.0) {
+            color.rgb *= outline;
+            float viewDist = abs(getViewZ(closestTexel));
+            float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
+            if (color.a != 1.0) {
+                color.a = 1.0 - fogFactor;
+            }
+            color.rgb = mix(color.rgb, uFogColor, fogFactor);
+        }
+    #endif
+
+    // occlusion needs to be handled after outline to darken them properly
+    #ifdef dOcclusionEnable
+        float depth = getDepth(coords);
+        if (!isBackground(depth)) {
+            float occlusionFactor = getSsao(coords);
+            color = mix(occlusionColor, color, occlusionFactor);
+        }
+    #endif
+
+    gl_FragColor = color;
 }
 `;

+ 9 - 9
src/mol-gl/shader/ssao-blur.frag.ts

@@ -25,19 +25,19 @@ uniform float uFar;
 #include common
 
 float perspectiveDepthToViewZ(const in float invClipZ, const in float near, const in float far) {
-	return (near * far) / ((far - near) * invClipZ - far);
+    return (near * far) / ((far - near) * invClipZ - far);
 }
 
 float orthographicDepthToViewZ(const in float linearClipZ, const in float near, const in float far) {
-	return linearClipZ * (near - far) - near;
+    return linearClipZ * (near - far) - near;
 }
 
 float getViewZ(const in float depth) {
-	#if dOrthographic == 1
-		return orthographicDepthToViewZ(depth, uNear, uFar);
-	#else
-		return perspectiveDepthToViewZ(depth, uNear, uFar);
-	#endif
+    #if dOrthographic == 1
+        return orthographicDepthToViewZ(depth, uNear, uFar);
+    #else
+        return perspectiveDepthToViewZ(depth, uNear, uFar);
+    #endif
 }
 
 bool isBackground(const in float depth) {
@@ -45,13 +45,13 @@ bool isBackground(const in float depth) {
 }
 
 void main(void) {
-	vec2 coords = gl_FragCoord.xy / uTexSize;
+    vec2 coords = gl_FragCoord.xy / uTexSize;
 
     vec2 packedDepth = texture(tSsaoDepth, coords).zw;
 
     float selfDepth = unpackRGToUnitInterval(packedDepth);
     // if background and if second pass
-	if (isBackground(selfDepth) && uBlurDirectionY != 0.0) {
+    if (isBackground(selfDepth) && uBlurDirectionY != 0.0) {
        gl_FragColor = vec4(packUnitIntervalToRG(1.0), packedDepth);
        return;
     }

+ 34 - 35
src/mol-gl/shader/ssao.frag.ts

@@ -25,21 +25,21 @@ uniform float uRadius;
 uniform float uBias;
 
 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);
+    x = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
+    return x * x * x * (x * (x * 6.0 - 15.0) + 10.0);
 }
 
 float noise(const in vec2 coords) {
-	float a = 12.9898;
-	float b = 78.233;
-	float c = 43758.5453;
-	float dt = dot(coords, vec2(a,b));
-	float sn = mod(dt, PI);
-	return abs(fract(sin(sn) * c)); // is abs necessary?
+    float a = 12.9898;
+    float b = 78.233;
+    float c = 43758.5453;
+    float dt = dot(coords, vec2(a,b));
+    float sn = mod(dt, PI);
+    return abs(fract(sin(sn) * c)); // is abs necessary?
 }
 
 vec2 getNoiseVec2(const in vec2 coords) {
-	return vec2(noise(coords), noise(coords + vec2(PI, 2.71828)));
+    return vec2(noise(coords), noise(coords + vec2(PI, 2.71828)));
 }
 
 bool isBackground(const in float depth) {
@@ -47,7 +47,7 @@ bool isBackground(const in float depth) {
 }
 
 float getDepth(const in vec2 coords) {
-	return unpackRGBAToDepth(texture2D(tDepth, coords));
+    return unpackRGBAToDepth(texture2D(tDepth, coords));
 }
 
 vec3 normalFromDepth(const in float depth, const in float depth1, const in float depth2, vec2 offset1, vec2 offset2) {
@@ -62,28 +62,28 @@ vec3 normalFromDepth(const in float depth, const in float depth1, const in float
 
 // StarCraft II Ambient Occlusion by [Filion and McNaughton 2008]
 void main(void) {
-	vec2 invTexSize = 1.0 / uTexSize;
-	vec2 selfCoords = gl_FragCoord.xy * invTexSize;
-
-	float selfDepth = getDepth(selfCoords);
-	vec2 selfPackedDepth = packUnitIntervalToRG(selfDepth);
-
-	if (isBackground(selfDepth)) {
-		gl_FragColor = vec4(packUnitIntervalToRG(0.0), selfPackedDepth);
-		return;
-	}
-	
-	vec2 offset1 = vec2(0.0, invTexSize.y);
+    vec2 invTexSize = 1.0 / uTexSize;
+    vec2 selfCoords = gl_FragCoord.xy * invTexSize;
+
+    float selfDepth = getDepth(selfCoords);
+    vec2 selfPackedDepth = packUnitIntervalToRG(selfDepth);
+
+    if (isBackground(selfDepth)) {
+        gl_FragColor = vec4(packUnitIntervalToRG(0.0), selfPackedDepth);
+        return;
+    }
+    
+    vec2 offset1 = vec2(0.0, invTexSize.y);
     vec2 offset2 = vec2(invTexSize.x, 0.0);
 
-	float selfDepth1 = getDepth(selfCoords + offset1);
-	float selfDepth2 = getDepth(selfCoords + offset2);
+    float selfDepth1 = getDepth(selfCoords + offset1);
+    float selfDepth2 = getDepth(selfCoords + offset2);
 
-	vec3 selfViewNormal = normalFromDepth(selfDepth, selfDepth1, selfDepth2, offset1, offset2);
-	vec3 selfViewPos = screenSpaceToViewSpace(vec3(selfCoords, selfDepth), uInvProjection);
+    vec3 selfViewNormal = normalFromDepth(selfDepth, selfDepth1, selfDepth2, offset1, offset2);
+    vec3 selfViewPos = screenSpaceToViewSpace(vec3(selfCoords, selfDepth), uInvProjection);
 
     vec3 randomVec = normalize(vec3(getNoiseVec2(selfCoords) * 2.0 - 1.0, 0.0));
-	
+
     vec3 tangent = normalize(randomVec - selfViewNormal * dot(randomVec, selfViewNormal));
     vec3 bitangent = cross(selfViewNormal, tangent);
     mat3 TBN = mat3(tangent, bitangent, selfViewNormal);
@@ -92,19 +92,18 @@ void main(void) {
     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.w;
-        offset.xyz = offset.xyz * 0.5 + 0.5;
-        
-		float sampleViewZ = screenSpaceToViewSpace(vec3(offset.xy, getDepth(offset.xy)), uInvProjection).z;
+        offset.xyz = (offset.xyz / offset.w) * 0.5 + 0.5;
+
+        float sampleViewZ = screenSpaceToViewSpace(vec3(offset.xy, getDepth(offset.xy)), uInvProjection).z;
 
-        occlusion += (sampleViewZ >= sampleViewPos.z + uBias ? 1.0 : 0.0) * smootherstep(0.0, 1.0, uRadius / abs(selfViewPos.z - sampleViewZ));           
+        occlusion += step(sampleViewPos.z + 0.025, sampleViewZ) * smootherstep(0.0, 1.0, uRadius / abs(selfViewPos.z - sampleViewZ));         
     }
-    occlusion = 1.0 - (occlusion / float(dNSamples));
+    occlusion = 1.0 - (uBias * occlusion / float(dNSamples));
 
-	vec2 packedOcclusion = packUnitIntervalToRG(occlusion);
+    vec2 packedOcclusion = packUnitIntervalToRG(occlusion);
     
     gl_FragColor = vec4(packedOcclusion, selfPackedDepth);
 }