Browse Source

depth render pass, clear related fixes

Alexander Rose 5 years ago
parent
commit
2c5be409cc

+ 41 - 16
src/mol-canvas3d/canvas3d.ts

@@ -34,6 +34,7 @@ import { getPostprocessingRenderable, PostprocessingParams, setPostprocessingPro
 import { JitterVectors, getComposeRenderable } from './helper/multi-sample';
 import { GLRenderingContext } from 'mol-gl/webgl/compat';
 import { PixelData } from 'mol-util/image';
+import { readTexture } from 'mol-gl/compute/util';
 
 export const Canvas3DParams = {
     // TODO: FPS cap?
@@ -90,6 +91,8 @@ interface Canvas3D {
     dispose: () => void
 }
 
+const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
+
 namespace Canvas3D {
     export interface HighlightEvent { current: Representation.Loci, prev: Representation.Loci, modifiers?: ModifiersKeys }
     export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
@@ -134,12 +137,15 @@ namespace Canvas3D {
         const renderer = Renderer.create(webgl, camera, p.renderer)
 
         const drawTarget = createRenderTarget(webgl, width, height)
-        const depthTexture = createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
-        depthTexture.define(width, height)
-        depthTexture.attachFramebuffer(drawTarget.framebuffer, 'depth')
+        const depthTarget = webgl.extensions.depthTexture ? null : createRenderTarget(webgl, width, height)
+        const depthTexture = depthTarget ? depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
+        if (!depthTarget) {
+            depthTexture.define(width, height)
+            depthTexture.attachFramebuffer(drawTarget.framebuffer, 'depth')
+        }
 
         const postprocessingTarget = createRenderTarget(webgl, width, height)
-        const postprocessing = getPostprocessingRenderable(webgl, drawTarget.texture, depthTexture, p.postprocessing)
+        const postprocessing = getPostprocessingRenderable(webgl, drawTarget.texture, depthTexture, !!depthTarget, p.postprocessing)
 
         const composeTarget = createRenderTarget(webgl, width, height)
         const holdTarget = createRenderTarget(webgl, width, height)
@@ -226,12 +232,21 @@ namespace Canvas3D {
             }
         }
 
-        function renderDraw() {
+        function renderDraw(postprocessingEnabled: boolean) {
             renderer.setViewport(0, 0, width, height)
-            renderer.render(scene, 'draw')
+            renderer.render(scene, 'draw', true)
             if (debugHelper.isEnabled) {
                 debugHelper.syncVisibility()
-                renderer.render(debugHelper.scene, 'draw')
+                renderer.render(debugHelper.scene, 'draw', false)
+            }
+
+            if (postprocessingEnabled && depthTarget) {
+                depthTarget.bind()
+                renderer.render(scene, 'depth', true)
+                if (debugHelper.isEnabled) {
+                    debugHelper.syncVisibility()
+                    renderer.render(debugHelper.scene, 'depth', false)
+                }
             }
         }
 
@@ -262,7 +277,7 @@ namespace Canvas3D {
 
             if (i === 0) {
                 drawTarget.bind()
-                renderDraw()
+                renderDraw(postprocessingEnabled)
                 if (postprocessingEnabled) {
                     postprocessingTarget.bind()
                     renderPostprocessing()
@@ -295,7 +310,7 @@ namespace Canvas3D {
 
                 // render scene and optionally postprocess
                 drawTarget.bind()
-                renderDraw()
+                renderDraw(postprocessingEnabled)
                 if (postprocessingEnabled) {
                     postprocessingTarget.bind()
                     renderPostprocessing()
@@ -378,7 +393,7 @@ namespace Canvas3D {
 
                 // render scene and optionally postprocess
                 drawTarget.bind()
-                renderDraw()
+                renderDraw(postprocessingEnabled)
                 if (postprocessingEnabled) {
                     postprocessingTarget.bind()
                     renderPostprocessing()
@@ -441,11 +456,11 @@ namespace Canvas3D {
                     case 'pick':
                         renderer.setViewport(0, 0, pickWidth, pickHeight);
                         objectPickTarget.bind();
-                        renderer.render(scene, 'pickObject');
+                        renderer.render(scene, 'pickObject', true);
                         instancePickTarget.bind();
-                        renderer.render(scene, 'pickInstance');
+                        renderer.render(scene, 'pickInstance', true);
                         groupPickTarget.bind();
-                        renderer.render(scene, 'pickGroup');
+                        renderer.render(scene, 'pickGroup', true);
                         break;
                     case 'draw':
                         renderer.setViewport(0, 0, width, height);
@@ -458,7 +473,7 @@ namespace Canvas3D {
                         } else {
                             if (postprocessingEnabled) drawTarget.bind()
                             else webgl.unbindFramebuffer()
-                            renderDraw()
+                            renderDraw(postprocessingEnabled)
                             if (postprocessingEnabled) {
                                 webgl.unbindFramebuffer()
                                 renderPostprocessing()
@@ -496,7 +511,7 @@ namespace Canvas3D {
             camera.transition.tick(currentTime);
             draw(false);
             if (!camera.transition.inTransition) interactionHelper.tick(currentTime);
-            window.requestAnimationFrame(animate)
+            requestAnimationFrame(animate)
         }
 
         function pick() {
@@ -627,6 +642,12 @@ namespace Canvas3D {
                     case 'pickObject': return objectPickTarget.getPixelData()
                     case 'pickInstance': return instancePickTarget.getPixelData()
                     case 'pickGroup': return groupPickTarget.getPixelData()
+                    case 'depth':
+                        if (depthTarget) {
+                            return depthTarget.getPixelData()
+                        } else {
+                            return readTexture(webgl, depthTexture) as PixelData
+                        }
                 }
             },
             didDraw,
@@ -699,7 +720,11 @@ namespace Canvas3D {
             postprocessingTarget.setSize(width, height)
             composeTarget.setSize(width, height)
             holdTarget.setSize(width, height)
-            depthTexture.define(width, height)
+            if (depthTarget) {
+                depthTarget.setSize(width, height)
+            } else {
+                depthTexture.define(width, height)
+            }
             ValueCell.update(postprocessing.values.uTexSize, Vec2.set(postprocessing.values.uTexSize.ref.value, width, height))
             ValueCell.update(compose.values.uTexSize, Vec2.set(compose.values.uTexSize.ref.value, width, height))
 

+ 6 - 2
src/mol-canvas3d/helper/postprocessing.ts

@@ -29,6 +29,8 @@ const PostprocessingSchema = {
     dOutlineEnable: DefineSpec('boolean'),
     uOutlineScale: UniformSpec('f'),
     uOutlineThreshold: UniformSpec('f'),
+
+    dPackedDepth: DefineSpec('boolean'),
 }
 
 export const PostprocessingParams = {
@@ -45,8 +47,8 @@ export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
 
 type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
 
-export function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, props: Partial<PostprocessingProps>): PostprocessingRenderable {
-    const p = { ...PD.getDefaultValues(PostprocessingParams), props }
+export function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, packedDepth: boolean, props: Partial<PostprocessingProps>): PostprocessingRenderable {
+    const p = { ...PD.getDefaultValues(PostprocessingParams), ...props }
     const values: Values<typeof PostprocessingSchema> = {
         ...QuadValues,
         tColor: ValueCell.create(colorTexture),
@@ -61,6 +63,8 @@ export function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Tex
         dOutlineEnable: ValueCell.create(p.outlineEnable),
         uOutlineScale: ValueCell.create(p.outlineScale * ctx.pixelRatio),
         uOutlineThreshold: ValueCell.create(p.outlineThreshold),
+
+        dPackedDepth: ValueCell.create(packedDepth),
     }
 
     const schema = { ...PostprocessingSchema }

+ 1 - 1
src/mol-gl/compute/util.ts

@@ -33,7 +33,7 @@ export const QuadValues: Values<typeof QuadSchema> = {
 
 //
 
-function getArrayForTexture(gl:GLRenderingContext, texture: Texture, size: number) {
+function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: number) {
     switch (texture.type) {
         case gl.UNSIGNED_BYTE: return new Uint8Array(size)
         case gl.FLOAT: return new Float32Array(size)

+ 13 - 6
src/mol-gl/renderer.ts

@@ -38,7 +38,7 @@ interface Renderer {
     readonly props: Readonly<RendererProps>
 
     clear: () => void
-    render: (scene: Scene, variant: GraphicsRenderVariant) => void
+    render: (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => void
     setProps: (props: Partial<RendererProps>) => void
     setViewport: (x: number, y: number, width: number, height: number) => void
     dispose: () => void
@@ -153,7 +153,7 @@ namespace Renderer {
             }
         }
 
-        const render = (scene: Scene, variant: GraphicsRenderVariant) => {
+        const render = (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => {
             ValueCell.update(globalUniforms.uModel, scene.view)
             ValueCell.update(globalUniforms.uView, camera.view)
             ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
@@ -182,8 +182,15 @@ namespace Renderer {
             state.depthMask(true)
             state.colorMask(true, true, true, true)
             state.enable(gl.DEPTH_TEST)
-            state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
-            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
+
+            if (clear) {
+                if (variant === 'draw') {
+                    state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
+                } else {
+                    state.clearColor(1, 1, 1, 1)
+                }
+                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
+            }
 
             if (variant === 'draw') {
                 for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -198,8 +205,7 @@ namespace Renderer {
                     state.depthMask(r.values.uAlpha.ref.value === 1.0)
                     if (!r.state.opaque) renderObject(r, variant)
                 }
-            } else {
-                // picking
+            } else { // picking & depth
                 for (let i = 0, il = renderables.length; i < il; ++i) {
                     renderObject(renderables[i], variant)
                 }
@@ -211,6 +217,7 @@ namespace Renderer {
         return {
             clear: () => {
                 state.depthMask(true)
+                state.colorMask(true, true, true, true)
                 state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
                 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
             },

+ 6 - 0
src/mol-gl/shader/chunks/assign-material-color.glsl.ts

@@ -5,6 +5,12 @@ export default `
     vec4 material = vec4(vColor.rgb, uAlpha);
 #elif defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
     vec4 material = uPickable == 1 ? vColor : vec4(0.0, 0.0, 0.0, 1.0); // set to empty picking id
+#elif defined(dColorType_depth)
+    #ifdef enabledFragDepth
+        vec4 material = packDepthToRGBA(gl_FragDepthEXT);
+    #else
+        vec4 material = packDepthToRGBA(gl_FragCoord.z);
+    #endif
 #endif
 
 // mix material with overpaint

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

@@ -29,6 +29,21 @@ float decodeFloatRGB(const in vec3 rgb) {
     return (rgb.r * 256.0 * 256.0 * 255.0 + rgb.g * 256.0 * 255.0 + rgb.b * 255.0) - 1.0;
 }
 
+const float PackUpscale = 256.0 / 255.0; // fraction -> 0..1 (including 1)
+const float UnpackDownscale = 255.0 / 256.0; // 0..1 -> fraction (excluding 1)
+const vec3 PackFactors = vec3(256.0 * 256.0 * 256.0, 256.0 * 256.0,  256.0);
+const vec4 UnpackFactors = UnpackDownscale / vec4(PackFactors, 1.0);
+const float ShiftRight8 = 1.0 / 256.0;
+
+vec4 packDepthToRGBA(const in float v) {
+	vec4 r = vec4(fract(v * PackFactors), v);
+	r.yzw -= r.xyz * ShiftRight8; // tidy overflow
+	return r * PackUpscale;
+}
+float unpackRGBAToDepth(const in vec4 v) {
+	return dot(v, UnpackFactors);
+}
+
 #if __VERSION__ != 300
     // transpose
 

+ 2 - 0
src/mol-gl/shader/direct-volume.frag.ts

@@ -128,6 +128,8 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 viewDir) {
                 #elif defined(dColorType_groupPicking)
                     float group = floor(decodeFloatRGB(textureGroup(isoPos).rgb) + 0.5);
                     return vec4(encodeFloatRGB(group), 1.0);
+                #elif defined(dColorType_depth)
+                    return packDepthToRGBA(gl_FragCoord.z); // TODO calculate isosurface depth
                 #else
                     // compute gradient by central differences
                     gradient.x = textureVal(isoPos - dx).a - textureVal(isoPos + dx).a;

+ 2 - 0
src/mol-gl/shader/lines.frag.ts

@@ -19,6 +19,8 @@ void main(){
         if (uAlpha < uPickingAlphaThreshold)
             discard; // ignore so the element below can be picked
         gl_FragColor = material;
+    #elif defined(dColorType_depth)
+        gl_FragColor = material;
     #else
         gl_FragColor = material;
 

+ 2 - 0
src/mol-gl/shader/mesh.frag.ts

@@ -21,6 +21,8 @@ void main() {
         if (uAlpha < uPickingAlphaThreshold)
             discard; // ignore so the element below can be picked
         gl_FragColor = material;
+    #elif defined(dColorType_depth)
+        gl_FragColor = material;
     #else
         #include assign_normal
         #include apply_light_color

+ 2 - 0
src/mol-gl/shader/points.frag.ts

@@ -26,6 +26,8 @@ void main(){
         if (uAlpha < uPickingAlphaThreshold)
             discard; // ignore so the element below can be picked
         gl_FragColor = material;
+    #elif defined(dColorType_depth)
+        gl_FragColor = material;
     #else
         gl_FragColor = material;
 

+ 16 - 6
src/mol-gl/shader/postprocessing.frag.ts

@@ -15,6 +15,8 @@ uniform float uOutlineThreshold;
 
 const float noiseAmount = 0.0002;
 
+#include common
+
 float noise(vec2 coords) {
 	float a = 12.9898;
 	float b = 78.233;
@@ -25,6 +27,14 @@ float noise(vec2 coords) {
 	return fract(sin(sn) * c);
 }
 
+float getDepth(in vec2 coords) {
+	#ifdef dPackedDepth
+		return unpackRGBAToDepth(texture2D(tDepth, coords));
+	#else
+		return texture2D(tDepth, coords).r;
+	#endif
+}
+
 float calcSSAO(in vec2 coords, in float depth) {
 	float occlusionFactor = 0.0;
 
@@ -33,7 +43,7 @@ float calcSSAO(in vec2 coords, in float depth) {
 			vec2 coordsDelta = coords + uOcclusionRadius / float(dOcclusionKernelSize) * vec2(float(i) / uTexSize.x, float(j) / uTexSize.y);
             coordsDelta += noiseAmount * (noise(coordsDelta) - 0.5) / uTexSize;
             coordsDelta = clamp(coordsDelta, 0.5 / uTexSize, 1.0 - 1.0 / uTexSize);
-			if (texture2D(tDepth, coordsDelta).r < depth) occlusionFactor += 1.0;
+			if (getDepth(coordsDelta) < depth) occlusionFactor += 1.0;
 		}
 	}
 
@@ -50,10 +60,10 @@ float calcEdgeDepth(in vec2 coords) {
     vec2 bottomRightUV = coords + vec2(invTexSize.x * halfScaleCeil, -invTexSize.y * halfScaleFloor);
     vec2 topLeftUV = coords + vec2(-invTexSize.x * halfScaleFloor, invTexSize.y * halfScaleCeil);
 
-    float depth0 = texture2D(tDepth, bottomLeftUV).r;
-    float depth1 = texture2D(tDepth, topRightUV).r;
-    float depth2 = texture2D(tDepth, bottomRightUV).r;
-    float depth3 = texture2D(tDepth, topLeftUV).r;
+    float depth0 = getDepth(bottomLeftUV);
+    float depth1 = getDepth(topRightUV);
+    float depth2 = getDepth(bottomRightUV);
+    float depth3 = getDepth(topLeftUV);
 
     float depthFiniteDifference0 = depth1 - depth0;
     float depthFiniteDifference1 = depth3 - depth2;
@@ -66,7 +76,7 @@ void main(void) {
 	vec4 color = texture2D(tColor, coords);
 
 	#ifdef dOcclusionEnable
-		float depth = texture2D(tDepth, coords).r;
+		float depth = getDepth(coords);
 		if (depth != 1.0) {
 			float occlusionFactor = calcSSAO(coords, depth);
 			color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), uOcclusionBias * occlusionFactor);

+ 2 - 0
src/mol-gl/shader/spheres.frag.ts

@@ -125,6 +125,8 @@ void main(void){
         if (uAlpha < uPickingAlphaThreshold)
             discard; // ignore so the element below can be picked
         gl_FragColor = material;
+    #elif defined(dColorType_depth)
+        gl_FragColor = material;
     #else
         vec3 normal = cameraNormal;
         vec3 vViewPosition = -cameraPos;

+ 2 - 0
src/mol-gl/shader/text.frag.ts

@@ -59,6 +59,8 @@ void main(){
     #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
         if (uAlpha < uPickingAlphaThreshold)
             discard; // ignore so the element below can be picked
+    #elif defined(dColorType_depth)
+        gl_FragColor = material;
     #else
         #include apply_marker_color
         #include apply_fog

+ 23 - 19
src/mol-gl/webgl/extensions.ts

@@ -8,13 +8,13 @@ import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivative
 
 export type WebGLExtensions = {
     instancedArrays: COMPAT_instanced_arrays
-    standardDerivatives: COMPAT_standard_derivatives
-    blendMinMax: COMPAT_blend_minmax
     textureFloat: COMPAT_texture_float
-    textureFloatLinear: COMPAT_texture_float_linear
     elementIndexUint: COMPAT_element_index_uint
-    depthTexture: COMPAT_depth_texture
 
+    standardDerivatives: COMPAT_standard_derivatives | null
+    textureFloatLinear: COMPAT_texture_float_linear | null
+    depthTexture: COMPAT_depth_texture | null
+    blendMinMax: COMPAT_blend_minmax | null
     vertexArrayObject: COMPAT_vertex_array_object | null
     fragDepth: COMPAT_frag_depth | null
     colorBufferFloat: COMPAT_color_buffer_float | null
@@ -27,31 +27,35 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
     if (instancedArrays === null) {
         throw new Error('Could not find support for "instanced_arrays"')
     }
-    const standardDerivatives = getStandardDerivatives(gl)
-    if (standardDerivatives === null) {
-        throw new Error('Could not find support for "standard_derivatives"')
-    }
-    const blendMinMax = getBlendMinMax(gl)
-    if (blendMinMax === null) {
-        throw new Error('Could not find support for "blend_minmax"')
-    }
     const textureFloat = getTextureFloat(gl)
     if (textureFloat === null) {
         throw new Error('Could not find support for "texture_float"')
     }
-    const textureFloatLinear = getTextureFloatLinear(gl)
-    if (textureFloatLinear === null) {
-        throw new Error('Could not find support for "texture_float_linear"')
-    }
     const elementIndexUint = getElementIndexUint(gl)
     if (elementIndexUint === null) {
         throw new Error('Could not find support for "element_index_uint"')
     }
+
+    const standardDerivatives = getStandardDerivatives(gl)
+    if (standardDerivatives === null) {
+        // TODO handle non-support downstream (e.g. no flat shading)
+        // throw new Error('Could not find support for "standard_derivatives"')
+        console.log('Could not find support for "standard_derivatives"')
+    }
+    const textureFloatLinear = getTextureFloatLinear(gl)
+    if (textureFloatLinear === null) {
+        // TODO handle non-support downstream (no gpu gaussian calc, no gpu mc???)
+        // throw new Error('Could not find support for "texture_float_linear"')
+        console.log('Could not find support for "texture_float_linear"')
+    }
     const depthTexture = getDepthTexture(gl)
     if (depthTexture === null) {
-        throw new Error('Could not find support for "depth_texture"')
+        console.log('Could not find support for "depth_texture"')
+    }
+    const blendMinMax = getBlendMinMax(gl)
+    if (blendMinMax === null) {
+        console.log('Could not find support for "blend_minmax"')
     }
-
     const vertexArrayObject = getVertexArrayObject(gl)
     if (vertexArrayObject === null) {
         console.log('Could not find support for "vertex_array_object"')
@@ -76,12 +80,12 @@ export function createExtensions(gl: GLRenderingContext): WebGLExtensions {
     return {
         instancedArrays,
         standardDerivatives,
-        blendMinMax,
         textureFloat,
         textureFloatLinear,
         elementIndexUint,
         depthTexture,
 
+        blendMinMax,
         vertexArrayObject,
         fragDepth,
         colorBufferFloat,

+ 2 - 1
src/mol-gl/webgl/render-item.ts

@@ -51,7 +51,8 @@ const GraphicsRenderVariantDefines = {
     'draw': {},
     'pickObject': { dColorType: ValueCell.create('objectPicking') },
     'pickInstance': { dColorType: ValueCell.create('instancePicking') },
-    'pickGroup': { dColorType: ValueCell.create('groupPicking') }
+    'pickGroup': { dColorType: ValueCell.create('groupPicking') },
+    'depth': { dColorType: ValueCell.create('depth') }
 }
 export type GraphicsRenderVariant = keyof typeof GraphicsRenderVariantDefines
 

+ 3 - 0
src/mol-math/geometry/gaussian-density/gpu.ts

@@ -307,6 +307,9 @@ function setupMinDistanceRendering(webgl: WebGLContext, renderable: ComputeRende
     state.colorMask(false, false, false, true)
     state.blendFunc(gl.ONE, gl.ONE)
     // the shader writes 1 - dist so we set blending to MAX
+    if (!webgl.extensions.blendMinMax) {
+        throw new Error('GPU gaussian surface calculation requires EXT_blend_minmax')
+    }
     state.blendEquation(webgl.extensions.blendMinMax.MAX)
 }