ソースを参照

Merge pull request #447 from molstar/pick-drawbuffers

use drawbuffers for picking
Alexander Rose 2 年 前
コミット
c51cb67519

+ 1 - 0
CHANGELOG.md

@@ -6,6 +6,7 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Improve picking by using drawbuffers (when available) to reduce number of drawcalls
 - GPU timing support
     - Add ``timing-mode`` Viewer GET param
     - Add support for webgl timer queries

+ 143 - 29
src/mol-canvas3d/passes/pick.ts

@@ -7,8 +7,12 @@
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { PickType, Renderer } from '../../mol-gl/renderer';
 import { Scene } from '../../mol-gl/scene';
+import { isWebGL2 } from '../../mol-gl/webgl/compat';
 import { WebGLContext } from '../../mol-gl/webgl/context';
+import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
 import { RenderTarget } from '../../mol-gl/webgl/render-target';
+import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
+import { Texture } from '../../mol-gl/webgl/texture';
 import { Vec3 } from '../../mol-math/linear-algebra';
 import { spiral2d } from '../../mol-math/misc';
 import { isTimingMode } from '../../mol-util/debug';
@@ -25,10 +29,24 @@ const NullId = Math.pow(2, 24) - 2;
 export type PickData = { id: PickingId, position: Vec3 }
 
 export class PickPass {
-    readonly objectPickTarget: RenderTarget;
-    readonly instancePickTarget: RenderTarget;
-    readonly groupPickTarget: RenderTarget;
-    readonly depthPickTarget: RenderTarget;
+    private readonly objectPickTarget: RenderTarget;
+    private readonly instancePickTarget: RenderTarget;
+    private readonly groupPickTarget: RenderTarget;
+    private readonly depthPickTarget: RenderTarget;
+
+    private readonly framebuffer: Framebuffer;
+
+    private readonly objectPickTexture: Texture;
+    private readonly instancePickTexture: Texture;
+    private readonly groupPickTexture: Texture;
+    private readonly depthPickTexture: Texture;
+
+    private readonly objectPickFramebuffer: Framebuffer;
+    private readonly instancePickFramebuffer: Framebuffer;
+    private readonly groupPickFramebuffer: Framebuffer;
+    private readonly depthPickFramebuffer: Framebuffer;
+
+    private readonly depthRenderbuffer: Renderbuffer;
 
     private pickWidth: number;
     private pickHeight: number;
@@ -38,10 +56,89 @@ export class PickPass {
         this.pickWidth = Math.ceil(drawPass.colorTarget.getWidth() * pickScale);
         this.pickHeight = Math.ceil(drawPass.colorTarget.getHeight() * pickScale);
 
-        this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
-        this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
-        this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
-        this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
+        const { resources, extensions: { drawBuffers }, gl } = webgl;
+
+        if (drawBuffers) {
+            this.objectPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
+            this.objectPickTexture.define(this.pickWidth, this.pickHeight);
+
+            this.instancePickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
+            this.instancePickTexture.define(this.pickWidth, this.pickHeight);
+
+            this.groupPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
+            this.groupPickTexture.define(this.pickWidth, this.pickHeight);
+
+            this.depthPickTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
+            this.depthPickTexture.define(this.pickWidth, this.pickHeight);
+
+            this.framebuffer = resources.framebuffer();
+
+            this.objectPickFramebuffer = resources.framebuffer();
+            this.instancePickFramebuffer = resources.framebuffer();
+            this.groupPickFramebuffer = resources.framebuffer();
+            this.depthPickFramebuffer = resources.framebuffer();
+
+            this.framebuffer.bind();
+            drawBuffers!.drawBuffers([
+                drawBuffers!.COLOR_ATTACHMENT0,
+                drawBuffers!.COLOR_ATTACHMENT1,
+                drawBuffers!.COLOR_ATTACHMENT2,
+                drawBuffers!.COLOR_ATTACHMENT3,
+            ]);
+
+            this.objectPickTexture.attachFramebuffer(this.framebuffer, 'color0');
+            this.instancePickTexture.attachFramebuffer(this.framebuffer, 'color1');
+            this.groupPickTexture.attachFramebuffer(this.framebuffer, 'color2');
+            this.depthPickTexture.attachFramebuffer(this.framebuffer, 'color3');
+
+            this.depthRenderbuffer = isWebGL2(gl)
+                ? resources.renderbuffer('depth32f', 'depth', this.pickWidth, this.pickHeight)
+                : resources.renderbuffer('depth16', 'depth', this.pickWidth, this.pickHeight);
+
+            this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
+
+            this.objectPickTexture.attachFramebuffer(this.objectPickFramebuffer, 'color0');
+            this.instancePickTexture.attachFramebuffer(this.instancePickFramebuffer, 'color0');
+            this.groupPickTexture.attachFramebuffer(this.groupPickFramebuffer, 'color0');
+            this.depthPickTexture.attachFramebuffer(this.depthPickFramebuffer, 'color0');
+        } else {
+            this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
+            this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
+            this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
+            this.depthPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight);
+        }
+    }
+
+    bindObject() {
+        if (this.webgl.extensions.drawBuffers) {
+            this.objectPickFramebuffer.bind();
+        } else {
+            this.objectPickTarget.bind();
+        }
+    }
+
+    bindInstance() {
+        if (this.webgl.extensions.drawBuffers) {
+            this.instancePickFramebuffer.bind();
+        } else {
+            this.instancePickTarget.bind();
+        }
+    }
+
+    bindGroup() {
+        if (this.webgl.extensions.drawBuffers) {
+            this.groupPickFramebuffer.bind();
+        } else {
+            this.groupPickTarget.bind();
+        }
+    }
+
+    bindDepth() {
+        if (this.webgl.extensions.drawBuffers) {
+            this.depthPickFramebuffer.bind();
+        } else {
+            this.depthPickTarget.bind();
+        }
     }
 
     get drawingBufferHeight() {
@@ -57,19 +154,30 @@ export class PickPass {
             this.pickWidth = pickWidth;
             this.pickHeight = pickHeight;
 
-            this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
-            this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
-            this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
-            this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
+            if (this.webgl.extensions.drawBuffers) {
+                this.objectPickTexture.define(this.pickWidth, this.pickHeight);
+                this.instancePickTexture.define(this.pickWidth, this.pickHeight);
+                this.groupPickTexture.define(this.pickWidth, this.pickHeight);
+                this.depthPickTexture.define(this.pickWidth, this.pickHeight);
+
+                this.depthRenderbuffer.setSize(this.pickWidth, this.pickHeight);
+            } else {
+                this.objectPickTarget.setSize(this.pickWidth, this.pickHeight);
+                this.instancePickTarget.setSize(this.pickWidth, this.pickHeight);
+                this.groupPickTarget.setSize(this.pickWidth, this.pickHeight);
+                this.depthPickTarget.setSize(this.pickWidth, this.pickHeight);
+            }
         }
     }
 
     private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
         renderer.clear(false);
-
         renderer.update(camera);
         renderer.renderPick(scene.primitives, camera, variant, null, pickType);
-        renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
+
+        if (helper.handle.isEnabled) {
+            renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
+        }
 
         if (helper.camera.isEnabled) {
             helper.camera.update(camera);
@@ -79,18 +187,23 @@ export class PickPass {
     }
 
     render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
-        this.objectPickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
+        if (this.webgl.extensions.drawBuffers) {
+            this.framebuffer.bind();
+            this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.None);
+        } else {
+            this.objectPickTarget.bind();
+            this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
 
-        this.instancePickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
+            this.instancePickTarget.bind();
+            this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
 
-        this.groupPickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
-        // printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
+            this.groupPickTarget.bind();
+            this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
+            // printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
 
-        this.depthPickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
+            this.depthPickTarget.bind();
+            this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
+        }
     }
 }
 
@@ -148,16 +261,16 @@ export class PickHelper {
         if (isTimingMode) this.webgl.timer.mark('PickHelper.syncBuffers');
         const { pickX, pickY, pickWidth, pickHeight } = this;
 
-        this.pickPass.objectPickTarget.bind();
+        this.pickPass.bindObject();
         this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.objectBuffer);
 
-        this.pickPass.instancePickTarget.bind();
+        this.pickPass.bindInstance();
         this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.instanceBuffer);
 
-        this.pickPass.groupPickTarget.bind();
+        this.pickPass.bindGroup();
         this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.groupBuffer);
 
-        this.pickPass.depthPickTarget.bind();
+        this.pickPass.bindDepth();
         this.webgl.readPixels(pickX, pickY, pickWidth, pickHeight, this.depthBuffer);
         if (isTimingMode) this.webgl.timer.markEnd('PickHelper.syncBuffers');
     }
@@ -183,7 +296,7 @@ export class PickHelper {
         const { renderer, scene, helper } = this;
 
         renderer.setTransparentBackground(false);
-        renderer.setDrawingBufferSize(this.pickPass.objectPickTarget.getWidth(), this.pickPass.objectPickTarget.getHeight());
+        renderer.setDrawingBufferSize(pickWidth, pickHeight);
         renderer.setPixelRatio(this.pickScale);
 
         if (StereoCamera.is(camera)) {
@@ -244,6 +357,7 @@ export class PickHelper {
         if (groupId === -1 || groupId === NullId) return;
 
         const z = this.getDepth(xp, yp);
+        // console.log('z', z);
         const position = Vec3.create(x, viewport.height - y, z);
         if (StereoCamera.is(camera)) {
             const halfWidth = Math.floor(viewport.width / 2);
@@ -258,7 +372,7 @@ export class PickHelper {
             cameraUnproject(position, position, viewport, camera.inverseProjectionView);
         }
 
-        // console.log({ { objectId, instanceId, groupId }, position} );
+        // console.log({ id: { objectId, instanceId, groupId }, position });
         return { id: { objectId, instanceId, groupId }, position };
     }
 

+ 42 - 10
src/mol-gl/shader-code.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -236,6 +236,18 @@ function getDefinesCode(defines: ShaderDefines, ignore?: IgnoreDefine) {
     return lines.join('\n') + '\n';
 }
 
+function getGlsl100VertPrefix(extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
+    const prefix: string[] = [];
+    if (shaderExtensions.drawBuffers) {
+        if (extensions.drawBuffers) {
+            prefix.push('#define requiredDrawBuffers');
+        } else if (shaderExtensions.drawBuffers === 'required') {
+            throw new Error(`required 'GL_EXT_draw_buffers' extension not available`);
+        }
+    }
+    return prefix.join('\n') + '\n';
+}
+
 function getGlsl100FragPrefix(extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
     const prefix: string[] = [
         '#extension GL_OES_standard_derivatives : enable'
@@ -271,7 +283,7 @@ function getGlsl100FragPrefix(extensions: WebGLExtensions, shaderExtensions: Sha
     return prefix.join('\n') + '\n';
 }
 
-const glsl300VertPrefix = `#version 300 es
+const glsl300VertPrefixCommon = `
 #define attribute in
 #define varying out
 #define texture2D texture
@@ -288,24 +300,42 @@ const glsl300FragPrefixCommon = `
 #define depthTextureSupport
 `;
 
+function getGlsl300VertPrefix(extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
+    const prefix = [
+        '#version 300 es',
+    ];
+    if (shaderExtensions.drawBuffers) {
+        if (extensions.drawBuffers) {
+            prefix.push('#define requiredDrawBuffers');
+        }
+    }
+    prefix.push(glsl300VertPrefixCommon);
+    return prefix.join('\n') + '\n';
+}
+
 function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions, outTypes: FragOutTypes) {
     const prefix = [
         '#version 300 es',
         `layout(location = 0) out highp ${outTypes[0] || 'vec4'} out_FragData0;`
     ];
-
     if (shaderExtensions.fragDepth) {
-        prefix.push('#define enabledFragDepth');
+        if (extensions.fragDepth) {
+            prefix.push('#define enabledFragDepth');
+        }
     }
     if (shaderExtensions.drawBuffers) {
-        prefix.push('#define requiredDrawBuffers');
-        const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number;
-        for (let i = 1, il = maxDrawBuffers; i < il; ++i) {
-            prefix.push(`layout(location = ${i}) out highp ${outTypes[i] || 'vec4'} out_FragData${i};`);
+        if (extensions.drawBuffers) {
+            prefix.push('#define requiredDrawBuffers');
+            const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number;
+            for (let i = 1, il = maxDrawBuffers; i < il; ++i) {
+                prefix.push(`layout(location = ${i}) out highp ${outTypes[i] || 'vec4'} out_FragData${i};`);
+            }
         }
     }
     if (shaderExtensions.shaderTextureLod) {
-        prefix.push('#define enabledShaderTextureLod');
+        if (extensions.shaderTextureLod) {
+            prefix.push('#define enabledShaderTextureLod');
+        }
     }
     prefix.push(glsl300FragPrefixCommon);
     return prefix.join('\n') + '\n';
@@ -318,7 +348,9 @@ function transformGlsl300Frag(frag: string) {
 export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtensions, defines: ShaderDefines, shaders: ShaderCode): ShaderCode {
     const vertHeader = getDefinesCode(defines, shaders.ignoreDefine);
     const fragHeader = getDefinesCode(defines, shaders.ignoreDefine);
-    const vertPrefix = isWebGL2(gl) ? glsl300VertPrefix : '';
+    const vertPrefix = isWebGL2(gl)
+        ? getGlsl300VertPrefix(extensions, shaders.extensions)
+        : getGlsl100VertPrefix(extensions, shaders.extensions);
     const fragPrefix = isWebGL2(gl)
         ? getGlsl300FragPrefix(gl, extensions, shaders.extensions, shaders.outTypes)
         : getGlsl100FragPrefix(extensions, shaders.extensions);

+ 13 - 7
src/mol-gl/shader/chunks/assign-color-varying.glsl.ts

@@ -56,13 +56,19 @@ export const assign_color_varying = `
         vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
     #endif
 #elif defined(dRenderVariant_pick)
-    if (uPickType == 1) {
-        vColor = vec4(packIntToRGB(float(uObjectId)), 1.0);
-    } else if (uPickType == 2) {
-        vColor = vec4(packIntToRGB(aInstance), 1.0);
-    } else {
-        vColor = vec4(packIntToRGB(group), 1.0);
-    }
+    #ifdef requiredDrawBuffers
+        vObject = vec4(packIntToRGB(float(uObjectId)), 1.0);
+        vInstance = vec4(packIntToRGB(aInstance), 1.0);
+        vGroup = vec4(packIntToRGB(group), 1.0);
+    #else
+        if (uPickType == 1) {
+            vColor = vec4(packIntToRGB(float(uObjectId)), 1.0);
+        } else if (uPickType == 2) {
+            vColor = vec4(packIntToRGB(aInstance), 1.0);
+        } else {
+            vColor = vec4(packIntToRGB(group), 1.0);
+        }
+    #endif
 #endif
 
 #ifdef dTransparency

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

@@ -28,8 +28,6 @@ export const assign_material_color = `
         roughness = mix(roughness, vSubstance.g, vSubstance.a);
         bumpiness = mix(bumpiness, vSubstance.b, vSubstance.a);
     #endif
-#elif defined(dRenderVariant_pick)
-    vec4 material = vColor;
 #elif defined(dRenderVariant_depth)
     if (fragmentDepth > getDepth(gl_FragCoord.xy / uDrawingBufferSize)) {
         discard;

+ 14 - 2
src/mol-gl/shader/chunks/color-frag-params.glsl.ts

@@ -28,9 +28,21 @@ uniform float uBumpiness;
     #endif
 #elif defined(dRenderVariant_pick)
     #if __VERSION__ == 100
-        varying vec4 vColor;
+        #ifdef requiredDrawBuffers
+            varying vec4 vObject;
+            varying vec4 vInstance;
+            varying vec4 vGroup;
+        #else
+            varying vec4 vColor;
+        #endif
     #else
-        flat in vec4 vColor;
+        #ifdef requiredDrawBuffers
+            flat in vec4 vObject;
+            flat in vec4 vInstance;
+            flat in vec4 vGroup;
+        #else
+            flat in vec4 vColor;
+        #endif
     #endif
 #endif
 

+ 14 - 2
src/mol-gl/shader/chunks/color-vert-params.glsl.ts

@@ -56,9 +56,21 @@ uniform float uBumpiness;
     #endif
 #elif defined(dRenderVariant_pick)
     #if __VERSION__ == 100
-        varying vec4 vColor;
+        #ifdef requiredDrawBuffers
+            varying vec4 vObject;
+            varying vec4 vInstance;
+            varying vec4 vGroup;
+        #else
+            varying vec4 vColor;
+        #endif
     #else
-        flat out vec4 vColor;
+        #ifdef requiredDrawBuffers
+            flat out vec4 vObject;
+            flat out vec4 vInstance;
+            flat out vec4 vGroup;
+        #else
+            flat out vec4 vColor;
+        #endif
     #endif
 #endif
 

+ 8 - 1
src/mol-gl/shader/cylinders.frag.ts

@@ -121,7 +121,14 @@ void main() {
 
     #if defined(dRenderVariant_pick)
         #include check_picking_alpha
-        gl_FragColor = material;
+        #ifdef requiredDrawBuffers
+            gl_FragColor = vObject;
+            gl_FragData[1] = vInstance;
+            gl_FragData[2] = vGroup;
+            gl_FragData[3] = packDepthToRGBA(fragmentDepth);
+        #else
+            gl_FragColor = vColor;
+        #endif
     #elif defined(dRenderVariant_depth)
         gl_FragColor = material;
     #elif defined(dRenderVariant_marking)

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

@@ -68,7 +68,6 @@ uniform float uFogFar;
 uniform vec3 uFogColor;
 
 uniform float uAlpha;
-uniform float uPickingAlphaThreshold;
 uniform bool uTransparentBackground;
 uniform float uXrayEdgeFalloff;
 

+ 14 - 6
src/mol-gl/shader/image.frag.ts

@@ -103,13 +103,21 @@ void main() {
     #if defined(dRenderVariant_pick)
         if (imageData.a < 0.3)
             discard;
-        if (uPickType == 1) {
+        #ifdef requiredDrawBuffers
             gl_FragColor = vec4(packIntToRGB(float(uObjectId)), 1.0);
-        } else if (uPickType == 2) {
-            gl_FragColor = vec4(packIntToRGB(vInstance), 1.0);
-        } else {
-            gl_FragColor = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
-        }
+            gl_FragData[1] = vec4(packIntToRGB(vInstance), 1.0);
+            gl_FragData[2] = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
+            gl_FragData[3] = packDepthToRGBA(gl_FragCoord.z);
+        #else
+            gl_FragColor = vColor;
+            if (uPickType == 1) {
+                gl_FragColor = vec4(packIntToRGB(float(uObjectId)), 1.0);
+            } else if (uPickType == 2) {
+                gl_FragColor = vec4(packIntToRGB(vInstance), 1.0);
+            } else {
+                gl_FragColor = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
+            }
+        #endif
     #elif defined(dRenderVariant_depth)
         if (imageData.a < 0.05)
             discard;

+ 8 - 1
src/mol-gl/shader/lines.frag.ts

@@ -21,7 +21,14 @@ void main(){
 
     #if defined(dRenderVariant_pick)
         #include check_picking_alpha
-        gl_FragColor = material;
+        #ifdef requiredDrawBuffers
+            gl_FragColor = vObject;
+            gl_FragData[1] = vInstance;
+            gl_FragData[2] = vGroup;
+            gl_FragData[3] = packDepthToRGBA(fragmentDepth);
+        #else
+            gl_FragColor = vColor;
+        #endif
     #elif defined(dRenderVariant_depth)
         gl_FragColor = material;
     #elif defined(dRenderVariant_marking)

+ 8 - 1
src/mol-gl/shader/mesh.frag.ts

@@ -37,7 +37,14 @@ void main() {
 
     #if defined(dRenderVariant_pick)
         #include check_picking_alpha
-        gl_FragColor = material;
+        #ifdef requiredDrawBuffers
+            gl_FragColor = vObject;
+            gl_FragData[1] = vInstance;
+            gl_FragData[2] = vGroup;
+            gl_FragData[3] = packDepthToRGBA(fragmentDepth);
+        #else
+            gl_FragColor = vColor;
+        #endif
     #elif defined(dRenderVariant_depth)
         gl_FragColor = material;
     #elif defined(dRenderVariant_marking)

+ 8 - 1
src/mol-gl/shader/points.frag.ts

@@ -33,7 +33,14 @@ void main(){
 
     #if defined(dRenderVariant_pick)
         #include check_picking_alpha
-        gl_FragColor = material;
+        #ifdef requiredDrawBuffers
+            gl_FragColor = vObject;
+            gl_FragData[1] = vInstance;
+            gl_FragData[2] = vGroup;
+            gl_FragData[3] = packDepthToRGBA(fragmentDepth);
+        #else
+            gl_FragColor = vColor;
+        #endif
     #elif defined(dRenderVariant_depth)
         gl_FragColor = material;
     #elif defined(dRenderVariant_marking)

+ 8 - 1
src/mol-gl/shader/spheres.frag.ts

@@ -86,7 +86,14 @@ void main(void){
 
     #if defined(dRenderVariant_pick)
         #include check_picking_alpha
-        gl_FragColor = material;
+        #ifdef requiredDrawBuffers
+            gl_FragColor = vObject;
+            gl_FragData[1] = vInstance;
+            gl_FragData[2] = vGroup;
+            gl_FragData[3] = packDepthToRGBA(fragmentDepth);
+        #else
+            gl_FragColor = vColor;
+        #endif
     #elif defined(dRenderVariant_depth)
         gl_FragColor = material;
     #elif defined(dRenderVariant_marking)

+ 21 - 8
src/mol-gl/shader/text.frag.ts

@@ -36,7 +36,9 @@ void main(){
     #include assign_material_color
 
     if (vTexCoord.x > 1.0) {
-        gl_FragColor = vec4(uBackgroundColor, uBackgroundOpacity * material.a);
+        #if defined(dRenderVariant_color)
+            material = vec4(uBackgroundColor, uBackgroundOpacity * material.a);
+        #endif
     } else {
         // retrieve signed distance
         float sdf = texture2D(tFont, vTexCoord).a + uBorderWidth;
@@ -49,24 +51,35 @@ void main(){
         a = pow(a, 1.0 / gamma);
 
         if (a < 0.5) discard;
-        material.a *= a;
 
-        // add border
-        float t = 0.5 + uBorderWidth;
-        if (uBorderWidth > 0.0 && sdf < t) {
-            material.xyz = mix(uBorderColor, material.xyz, smoothstep(t - w, t, sdf));
-        }
+        #if defined(dRenderVariant_color)
+            material.a *= a;
 
-        gl_FragColor = material;
+            // add border
+            float t = 0.5 + uBorderWidth;
+            if (uBorderWidth > 0.0 && sdf < t) {
+                material.xyz = mix(uBorderColor, material.xyz, smoothstep(t - w, t, sdf));
+            }
+        #endif
     }
 
     #if defined(dRenderVariant_pick)
         #include check_picking_alpha
+        #ifdef requiredDrawBuffers
+            gl_FragColor = vObject;
+            gl_FragData[1] = vInstance;
+            gl_FragData[2] = vGroup;
+            gl_FragData[3] = packDepthToRGBA(fragmentDepth);
+        #else
+            gl_FragColor = vColor;
+        #endif
     #elif defined(dRenderVariant_depth)
         gl_FragColor = material;
     #elif defined(dRenderVariant_marking)
         gl_FragColor = material;
     #elif defined(dRenderVariant_color)
+        gl_FragColor = material;
+
         #include apply_marker_color
         #include apply_fog
         #include wboit_write