Skip to content
Snippets Groups Projects
Commit a828113d authored by Alexander Rose's avatar Alexander Rose
Browse files

use drawbuffers for picking

parent ab4d509e
No related branches found
No related tags found
No related merge requests found
Showing
with 302 additions and 72 deletions
......@@ -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
- Fix ``Scene.clear`` not clearing primitives & volumes arrays (@JonStargaryen)
- Fix rendering volumes when wboit is switched off and postprocessing is enabled
......
......@@ -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 { unpackRGBToInt, unpackRGBAToDepth } from '../../mol-util/number-packing';
......@@ -24,10 +28,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;
......@@ -37,11 +55,90 @@ export class PickPass {
this.pickWidth = Math.ceil(drawPass.colorTarget.getWidth() * pickScale);
this.pickHeight = Math.ceil(drawPass.colorTarget.getHeight() * pickScale);
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() {
return this.drawPass.colorTarget.getHeight();
......@@ -56,19 +153,30 @@ export class PickPass {
this.pickWidth = pickWidth;
this.pickHeight = 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);
if (helper.handle.isEnabled) {
renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
}
if (helper.camera.isEnabled) {
helper.camera.update(camera);
......@@ -78,6 +186,10 @@ export class PickPass {
}
render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
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);
......@@ -92,6 +204,7 @@ export class PickPass {
this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
}
}
}
export class PickHelper {
dirty = true;
......@@ -146,16 +259,16 @@ export class PickHelper {
private 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);
}
......@@ -179,7 +292,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)) {
......@@ -237,6 +350,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);
......@@ -251,7 +365,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 };
}
......
/**
* 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,25 +300,43 @@ 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) {
if (extensions.fragDepth) {
prefix.push('#define enabledFragDepth');
}
}
if (shaderExtensions.drawBuffers) {
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) {
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);
......
......@@ -56,6 +56,11 @@ export const assign_color_varying = `
vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
#endif
#elif defined(dRenderVariant_pick)
#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) {
......@@ -64,6 +69,7 @@ export const assign_color_varying = `
vColor = vec4(packIntToRGB(group), 1.0);
}
#endif
#endif
#ifdef dTransparency
vGroup = group;
......
......@@ -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;
......
......@@ -28,11 +28,23 @@ uniform float uBumpiness;
#endif
#elif defined(dRenderVariant_pick)
#if __VERSION__ == 100
#ifdef requiredDrawBuffers
varying vec4 vObject;
varying vec4 vInstance;
varying vec4 vGroup;
#else
varying vec4 vColor;
#endif
#else
#ifdef requiredDrawBuffers
flat in vec4 vObject;
flat in vec4 vInstance;
flat in vec4 vGroup;
#else
flat in vec4 vColor;
#endif
#endif
#endif
#ifdef dTransparency
varying float vGroup;
......
......@@ -56,11 +56,23 @@ uniform float uBumpiness;
#endif
#elif defined(dRenderVariant_pick)
#if __VERSION__ == 100
#ifdef requiredDrawBuffers
varying vec4 vObject;
varying vec4 vInstance;
varying vec4 vGroup;
#else
varying vec4 vColor;
#endif
#else
#ifdef requiredDrawBuffers
flat out vec4 vObject;
flat out vec4 vInstance;
flat out vec4 vGroup;
#else
flat out vec4 vColor;
#endif
#endif
#endif
#ifdef dTransparency
varying float vGroup;
......
......@@ -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)
......
......@@ -68,7 +68,6 @@ uniform float uFogFar;
uniform vec3 uFogColor;
uniform float uAlpha;
uniform float uPickingAlphaThreshold;
uniform bool uTransparentBackground;
uniform float uXrayEdgeFalloff;
......
......@@ -103,6 +103,13 @@ void main() {
#if defined(dRenderVariant_pick)
if (imageData.a < 0.3)
discard;
#ifdef requiredDrawBuffers
gl_FragColor = vec4(packIntToRGB(float(uObjectId)), 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) {
......@@ -110,6 +117,7 @@ void main() {
} else {
gl_FragColor = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
}
#endif
#elif defined(dRenderVariant_depth)
if (imageData.a < 0.05)
discard;
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -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,6 +51,8 @@ void main(){
a = pow(a, 1.0 / gamma);
if (a < 0.5) discard;
#if defined(dRenderVariant_color)
material.a *= a;
// add border
......@@ -56,17 +60,26 @@ void main(){
if (uBorderWidth > 0.0 && sdf < t) {
material.xyz = mix(uBorderColor, material.xyz, smoothstep(t - w, t, sdf));
}
gl_FragColor = material;
#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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment