diff --git a/src/apps/docking-viewer/viewport.tsx b/src/apps/docking-viewer/viewport.tsx index 73b7d99d627a5f535f9d0f56659a8c153ef06ff8..5ead151ff5ff4f28ee12d2e0d4033706704d7877 100644 --- a/src/apps/docking-viewer/viewport.tsx +++ b/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 } } } } }); diff --git a/src/examples/lighting/index.ts b/src/examples/lighting/index.ts index 6e5b7b4bd87193e3902dd9bd688882ed7d1dd55e..95757a6215baf0d3737948d2cdf3065bb7f6a1f6 100644 --- a/src/examples/lighting/index.ts +++ b/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: { diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index 5e6f0af0c8d6338c13e98951f257af3da49806ac..bca5b842257c0036faefed54c1a39d7aec78d5e4 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/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); diff --git a/src/mol-gl/shader/chunks/common.glsl.ts b/src/mol-gl/shader/chunks/common.glsl.ts index 338ad8b0809d5c236f3a044cdeb74e65781393e7..077baa036bd8bf81b3b565c9a187a5b4f09e7580 100644 --- a/src/mol-gl/shader/chunks/common.glsl.ts +++ b/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; diff --git a/src/mol-gl/shader/chunks/light-frag-params.glsl.ts b/src/mol-gl/shader/chunks/light-frag-params.glsl.ts index 7c0a731c744a75d766e7d717b1533b19febeb3fb..7f6f25493f26153be4515feba58d6cc22efab6b3 100644 --- a/src/mol-gl/shader/chunks/light-frag-params.glsl.ts +++ b/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); } `; \ No newline at end of file diff --git a/src/mol-gl/shader/outlines.frag.ts b/src/mol-gl/shader/outlines.frag.ts index 8968eea2da9823b23c29060458b289be8a50db7b..19f91fc381dedd8c9da325303f81f43b1bc2d048 100644 --- a/src/mol-gl/shader/outlines.frag.ts +++ b/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); } `; \ No newline at end of file diff --git a/src/mol-gl/shader/postprocessing.frag.ts b/src/mol-gl/shader/postprocessing.frag.ts index dd3cf167e6e20a74a67ba333840310307e4deb7a..a3a30836adb0735c1980075e753902eaf4d39be3 100644 --- a/src/mol-gl/shader/postprocessing.frag.ts +++ b/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; } `; \ No newline at end of file diff --git a/src/mol-gl/shader/ssao-blur.frag.ts b/src/mol-gl/shader/ssao-blur.frag.ts index 7ec1b7447311891f5b26d2610b364b2424ff37ae..ace3593d5f54e0036c3b8735ed6fa63edbae18b7 100644 --- a/src/mol-gl/shader/ssao-blur.frag.ts +++ b/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; } diff --git a/src/mol-gl/shader/ssao.frag.ts b/src/mol-gl/shader/ssao.frag.ts index 12568e1e716c6759ed4bfae28afb1e9b6f2ab8da..ef74f9d5d7c8b87fbc10a739a9eaaed488de4ac5 100644 --- a/src/mol-gl/shader/ssao.frag.ts +++ b/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); }