Переглянути джерело

patch and background transparency

Aron Kovacs 4 роки тому
батько
коміт
3d21f1ecc6

+ 1 - 1
src/mol-canvas3d/passes/draw.ts

@@ -126,7 +126,7 @@ export class DrawPass {
 
         // console.log('toDrawingBuffer', toDrawingBuffer);
 
-        for (let i = 0; i < this.webgl.gl.getParameter(this.webgl.gl.MAX_TEXTURE_IMAGE_UNITS); i++) {
+        for (let i = 0; i < this.webgl.maxTextureImageUnits; i++) {
             this.webgl.gl.activeTexture(this.webgl.gl.TEXTURE0 + i);
             this.webgl.gl.bindTexture(this.webgl.gl.TEXTURE_2D, null);
         }

+ 12 - 0
src/mol-geo/geometry/transparency-data.ts

@@ -13,6 +13,7 @@ export type TransparencyData = {
     uTransparencyTexDim: ValueCell<Vec2>
     dTransparency: ValueCell<boolean>,
     dTransparencyVariant: ValueCell<string>,
+    transparencyAverage: ValueCell<number>,
 }
 
 export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
@@ -22,6 +23,14 @@ export function applyTransparencyValue(array: Uint8Array, start: number, end: nu
     return true;
 }
 
+export function getTransparencyAverage(array: Uint8Array, count: number): number {
+    let sum = 0;
+    for (let i = 0; i < count; ++i) {
+        sum += array[i];
+    }
+    return sum / (255 * count);
+}
+
 export function clearTransparency(array: Uint8Array, start: number, end: number) {
     array.fill(0, start, end);
 }
@@ -32,6 +41,7 @@ export function createTransparency(count: number, transparencyData?: Transparenc
         ValueCell.update(transparencyData.tTransparency, transparency);
         ValueCell.update(transparencyData.uTransparencyTexDim, Vec2.create(transparency.width, transparency.height));
         ValueCell.updateIfChanged(transparencyData.dTransparency, count > 0);
+        ValueCell.updateIfChanged(transparencyData.transparencyAverage, getTransparencyAverage(transparency.array, count));
         return transparencyData;
     } else {
         return {
@@ -39,6 +49,7 @@ export function createTransparency(count: number, transparencyData?: Transparenc
             uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
             dTransparency: ValueCell.create(count > 0),
             dTransparencyVariant: ValueCell.create('single'),
+            transparencyAverage: ValueCell.create(0),
         };
     }
 }
@@ -55,6 +66,7 @@ export function createEmptyTransparency(transparencyData?: TransparencyData): Tr
             uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
             dTransparency: ValueCell.create(false),
             dTransparencyVariant: ValueCell.create('single'),
+            transparencyAverage: ValueCell.create(0),
         };
     }
 }

+ 1 - 1
src/mol-gl/renderable.ts

@@ -20,7 +20,7 @@ export type RenderableState = {
     pickable: boolean
     colorOnly: boolean
     opaque: boolean
-    writeDepth: boolean,
+    writeDepth: boolean
 }
 
 export interface Renderable<T extends RenderableValues> {

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

@@ -28,6 +28,7 @@ export const DirectVolumeSchema = {
     tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     dTransparency: DefineSpec('boolean'),
     dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
+    transparencyAverage: ValueSpec('number'),
 
     dClipObjectCount: DefineSpec('number'),
     dClipVariant: DefineSpec('string', ['instance', 'pixel']),

+ 1 - 0
src/mol-gl/renderable/schema.ts

@@ -214,6 +214,7 @@ export const TransparencySchema = {
     tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     dTransparency: DefineSpec('boolean'),
     dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
+    transparencyAverage: ValueSpec('number'),
 } as const;
 export type TransparencySchema = typeof TransparencySchema
 export type TransparencyValues = Values<TransparencySchema>

+ 37 - 53
src/mol-gl/renderer.ts

@@ -169,7 +169,7 @@ namespace Renderer {
         const style = getStyle(p.style);
         const clip = getClip(p.clip);
 
-        const { drawBuffers, textureFloat, colorBufferFloat, depthTexture } = ctx.extensions;
+        const { drawBuffers, textureFloat, colorBufferFloat, depthTexture, fragDepth } = ctx.extensions;
 
         const viewport = Viewport();
         const drawingBufferSize = Vec2.create(gl.drawingBufferWidth, gl.drawingBufferHeight);
@@ -177,7 +177,7 @@ namespace Renderer {
 
         const sharedTexturesList: Textures = [];
 
-        const enableWboit = textureFloat !== null && colorBufferFloat !== null && depthTexture !== null;
+        const enableWboit = textureFloat && colorBufferFloat && depthTexture && drawBuffers;
 
         const wboitATexture = enableWboit ? resources.texture('image-float32', 'rgba', 'float', 'nearest') : null;
         wboitATexture?.define(viewport.width, viewport.height);
@@ -186,25 +186,16 @@ namespace Renderer {
 
         const evaluateWboitRenderable = enableWboit ? getEvaluateWboitRenderable(ctx, wboitATexture!, wboitBTexture!) : null;
 
-        const wboitFramebuffers = [resources.framebuffer()];
+        const wboitFramebuffer = resources.framebuffer();
         if (enableWboit) {
-            if (drawBuffers) {
-                wboitFramebuffers.push(resources.framebuffer());
-
-                wboitFramebuffers[0].bind();
-                drawBuffers.drawBuffers([
-                    drawBuffers.COLOR_ATTACHMENT0,
-                    drawBuffers.COLOR_ATTACHMENT1,
-                ]);
-
-                wboitATexture!.attachFramebuffer(wboitFramebuffers[0], 'color0');
-                wboitBTexture!.attachFramebuffer(wboitFramebuffers[0], 'color1');
-            } else {
-                wboitFramebuffers.push(resources.framebuffer(), resources.framebuffer());
-
-                wboitATexture!.attachFramebuffer(wboitFramebuffers[0], 'color0');
-                wboitBTexture!.attachFramebuffer(wboitFramebuffers[1], 'color0');
-            }
+            wboitFramebuffer.bind();
+            drawBuffers!.drawBuffers([
+                drawBuffers!.COLOR_ATTACHMENT0,
+                drawBuffers!.COLOR_ATTACHMENT1,
+            ]);
+
+            wboitATexture!.attachFramebuffer(wboitFramebuffer, 'color0');
+            wboitBTexture!.attachFramebuffer(wboitFramebuffer, 'color1');
         }
 
         const view = Mat4();
@@ -312,6 +303,18 @@ namespace Renderer {
                 // culling done in fragment shader
                 state.disable(gl.CULL_FACE);
                 state.frontFace(gl.CCW);
+
+                if (!enableWboit) {
+                    // depth test done manually in shader against `depthTexture`
+                    // still need to enable when fragDepth can be used to write depth
+                    if (r.values.dRenderMode.ref.value === 'volume' || !fragDepth) {
+                        state.disable(gl.DEPTH_TEST);
+                        state.depthMask(false);
+                    } else {
+                        state.enable(gl.DEPTH_TEST);
+                        state.depthMask(r.state.writeDepth);
+                    }
+                }
             } else {
                 if (r.values.dDoubleSided) {
                     if (r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) {
@@ -337,6 +340,8 @@ namespace Renderer {
                     state.frontFace(gl.CCW);
                     state.cullFace(gl.BACK);
                 }
+
+                if (!enableWboit) state.depthMask(r.state.writeDepth);
             }
 
             r.render(variant, sharedTexturesList);
@@ -347,7 +352,6 @@ namespace Renderer {
             if (depthTexture) {
                 localSharedTexturesList = [...localSharedTexturesList, ['tDepth', depthTexture]];
             }
-            // console.log('depthTexture', depthTexture);
 
             ValueCell.update(globalUniforms.uModel, group.view);
             ValueCell.update(globalUniforms.uView, camera.view);
@@ -404,7 +408,6 @@ namespace Renderer {
             gl.scissor(x, y, width, height);
 
             if (clear) {
-                state.depthMask(true);
                 if (variant === 'color') {
                     state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1);
                 } else {
@@ -420,12 +423,13 @@ namespace Renderer {
                         state.depthMask(true);
 
                         for (let i = 0, il = renderables.length; i < il; ++i) {
-                            // TODO: when available in r.state check if fullyOpaque
                             const r = renderables[i];
-                            renderObject(r, variant, localSharedTexturesList);
+                            if (r.values.uAlpha.ref.value === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values?.dRenderMode?.ref.value !== 'volume') {
+                                renderObject(r, variant, localSharedTexturesList);
+                            }
                         }
                     } else {
-                        wboitFramebuffers[0].bind();
+                        wboitFramebuffer.bind();
 
                         state.clearColor(0, 0, 0, 1);
                         gl.clear(gl.COLOR_BUFFER_BIT);
@@ -439,9 +443,10 @@ namespace Renderer {
                         state.enable(gl.BLEND);
 
                         for (let i = 0, il = renderables.length; i < il; ++i) {
-                            // TODO: when available in r.state check if not fullyOpaque
                             const r = renderables[i];
-                            renderObject(r, variant, localSharedTexturesList);
+                            if (r.values.uAlpha.ref.value < 1 || r.values.transparencyAverage.ref.value !== 0) {
+                                renderObject(r, variant, localSharedTexturesList);
+                            }
                         }
 
                         if (renderTarget) {
@@ -450,7 +455,7 @@ namespace Renderer {
                             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                         }
 
-                        state.blendFuncSeparate(gl.ONE_MINUS_SRC_ALPHA, gl.SRC_ALPHA, gl.ZERO, gl.ONE);
+                        state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
                         state.enable(gl.BLEND);
 
                         // unclear why needed but otherwise there is a
@@ -572,33 +577,12 @@ namespace Renderer {
                     ValueCell.update(globalUniforms.uViewportHeight, height);
                     ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height));
 
+                    // TODO: this gets called every frame... in any case, we will need to move
+                    //       the wboit resources (framebuffer, textures, renderable) eventually
+                    //       to the draw pass to be more efficient when having >100 viewports
+                    //       rendered to a single canvas every frame.
                     wboitATexture?.define(viewport.width, viewport.height);
                     wboitBTexture?.define(viewport.width, viewport.height);
-
-                    // TODO there should not be any need to re-create the framebuffers,
-                    // re-sizing the textures should do it
-                    if (drawBuffers) {
-                        wboitFramebuffers[0].destroy();
-                        wboitFramebuffers.length = 0;
-                        wboitFramebuffers.push(resources.framebuffer());
-
-                        wboitFramebuffers[0].bind();
-                        drawBuffers?.drawBuffers([
-                            drawBuffers.COLOR_ATTACHMENT0,
-                            drawBuffers.COLOR_ATTACHMENT1,
-                        ]);
-
-                        wboitATexture?.attachFramebuffer(wboitFramebuffers[0], 'color0');
-                        wboitBTexture?.attachFramebuffer(wboitFramebuffers[0], 'color1');
-                    } else {
-                        wboitFramebuffers[0].destroy();
-                        wboitFramebuffers[1].destroy();
-                        wboitFramebuffers.length = 0;
-                        wboitFramebuffers.push(resources.framebuffer(), resources.framebuffer());
-
-                        wboitATexture?.attachFramebuffer(wboitFramebuffers[0], 'color0');
-                        wboitBTexture?.attachFramebuffer(wboitFramebuffers[1], 'color0');
-                    }
                 }
             },
 

+ 7 - 7
src/mol-gl/shader-code.ts

@@ -119,31 +119,31 @@ export function ShaderCode(name: string, vert: string, frag: string, extensions:
 
 import points_vert from './shader/points.vert';
 import points_frag from './shader/points.frag';
-export const PointsShaderCode = ShaderCode('points', points_vert, points_frag);
+export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' });
 
 import spheres_vert from './shader/spheres.vert';
 import spheres_frag from './shader/spheres.frag';
-export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required' });
+export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' });
 
 import text_vert from './shader/text.vert';
 import text_frag from './shader/text.frag';
-export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: 'required' });
+export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: 'required', drawBuffers: 'optional' });
 
 import lines_vert from './shader/lines.vert';
 import lines_frag from './shader/lines.frag';
-export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag);
+export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { drawBuffers: 'optional' });
 
 import mesh_vert from './shader/mesh.vert';
 import mesh_frag from './shader/mesh.frag';
-export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: 'optional' });
+export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: 'optional', drawBuffers: 'optional' });
 
 import direct_volume_vert from './shader/direct-volume.vert';
 import direct_volume_frag from './shader/direct-volume.frag';
-export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: 'optional' });
+export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: 'optional', drawBuffers: 'optional' });
 
 import image_vert from './shader/image.vert';
 import image_frag from './shader/image.frag';
-export const ImageShaderCode = ShaderCode('image', image_vert, image_frag);
+export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' });
 
 //
 

+ 2 - 1
src/mol-gl/shader/evaluate-wboit.frag.ts

@@ -8,7 +8,8 @@ void main() {
     ivec2 coords = ivec2(gl_FragCoord.xy);
     
     vec4 accum = texelFetch(tWboitA, coords, 0);
-    float r = accum.a;
+    float r = 1.0 - accum.a;
+
     accum.a = texelFetch(tWboitB, coords, 0).r;
     gl_FragColor = vec4(accum.rgb / clamp(accum.a, 0.0001, 50000.0), r);
 }

+ 3 - 0
src/mol-gl/webgl/context.ts

@@ -186,6 +186,7 @@ export interface WebGLContext {
     readonly maxTextureSize: number
     readonly maxRenderbufferSize: number
     readonly maxDrawBuffers: number
+    readonly maxTextureImageUnits: number
 
     readonly isContextLost: boolean
     readonly contextRestored: BehaviorSubject<now.Timestamp>
@@ -220,6 +221,7 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
         maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,
         maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) as number,
         maxDrawBuffers: isWebGL2(gl) ? gl.getParameter(gl.MAX_DRAW_BUFFERS) as number : 0,
+        maxTextureImageUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) as number,
         maxVertexTextureImageUnits: gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) as number,
     };
 
@@ -285,6 +287,7 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
         get maxTextureSize () { return parameters.maxTextureSize; },
         get maxRenderbufferSize () { return parameters.maxRenderbufferSize; },
         get maxDrawBuffers () { return parameters.maxDrawBuffers; },
+        get maxTextureImageUnits () { return parameters.maxTextureImageUnits; },
 
         namedComputeRenderables: Object.create(null),
         namedFramebuffers: Object.create(null),

+ 2 - 2
src/mol-gl/webgl/program.ts

@@ -166,8 +166,8 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi
         uniformSetters = getUniformSetters(schema);
 
         if (isDebugMode) {
-            // checkActiveAttributes(gl, program, schema);
-            // checkActiveUniforms(gl, program, schema);
+            checkActiveAttributes(gl, program, schema);
+            checkActiveUniforms(gl, program, schema);
         }
     }
     init();

+ 3 - 2
src/mol-repr/visual.ts

@@ -20,7 +20,7 @@ import { Overpaint } from '../mol-theme/overpaint';
 import { createOverpaint, clearOverpaint, applyOverpaintColor } from '../mol-geo/geometry/overpaint-data';
 import { Interval } from '../mol-data/int';
 import { Transparency } from '../mol-theme/transparency';
-import { createTransparency, clearTransparency, applyTransparencyValue } from '../mol-geo/geometry/transparency-data';
+import { createTransparency, clearTransparency, applyTransparencyValue, getTransparencyAverage } from '../mol-geo/geometry/transparency-data';
 import { Clipping } from '../mol-theme/clipping';
 import { createClipping, applyClippingGroups, clearClipping } from '../mol-geo/geometry/clipping-data';
 
@@ -114,7 +114,7 @@ namespace Visual {
     export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean) {
         if (!renderObject) return;
 
-        const { tTransparency, uGroupCount, instanceCount } = renderObject.values;
+        const { tTransparency, transparencyAverage, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
         // ensure texture has right size and variant
@@ -134,6 +134,7 @@ namespace Visual {
             lociApply(loci, apply, false);
         }
         ValueCell.update(tTransparency, tTransparency.ref.value);
+        ValueCell.updateIfChanged(transparencyAverage, getTransparencyAverage(array, count));
     }
 
     export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {