diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index d6b9264ae9f7b1254b1288cb452127b46d60ff1e..51cec91370b4dfaea01c6524493df0e9dd200bb6 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -288,7 +288,7 @@ namespace Canvas3D { if (MultiSamplePass.isEnabled(p.multiSample)) { passes.multiSample.render(renderer, cam, scene, helper, true, p.transparentBackground, p); } else { - const toDrawingBuffer = !PostprocessingPass.isEnabled(p.postprocessing) && scene.volumes.renderables.length === 0; + const toDrawingBuffer = false; //!PostprocessingPass.isEnabled(p.postprocessing) && scene.volumes.renderables.length === 0; passes.draw.render(renderer, cam, scene, helper, toDrawingBuffer, p.transparentBackground); if (!toDrawingBuffer) passes.postprocessing.render(cam, true, p.postprocessing); } diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index bd04e66ce9835c6450734f62e360415d5fbb4e8b..3962d57c08a715de20f7669eaeb604d2b34cb5df 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -52,10 +52,12 @@ export class DrawPass { readonly colorTarget: RenderTarget readonly depthTexture: Texture readonly depthTexturePrimitives: Texture + readonly depthTexturePrimitivesTransparent: Texture private readonly packedDepth: boolean private depthTarget: RenderTarget private depthTargetPrimitives: RenderTarget | null + private depthTargetPrimitivesTransparent: RenderTarget | null private depthTargetVolumes: RenderTarget | null private depthTextureVolumes: Texture private depthMerge: DepthMergeRenderable @@ -70,9 +72,11 @@ export class DrawPass { this.depthTexture = this.depthTarget.texture; this.depthTargetPrimitives = this.packedDepth ? webgl.createRenderTarget(width, height) : null; + this.depthTargetPrimitivesTransparent = this.packedDepth ? webgl.createRenderTarget(width, height) : null; this.depthTargetVolumes = this.packedDepth ? webgl.createRenderTarget(width, height) : null; this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest'); + this.depthTexturePrimitivesTransparent = this.depthTargetPrimitivesTransparent ? this.depthTargetPrimitivesTransparent.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest'); this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest'); if (!this.packedDepth) { this.depthTexturePrimitives.define(width, height); @@ -94,6 +98,11 @@ export class DrawPass { } else { this.depthTexturePrimitives.define(width, height); } + if (this.depthTargetPrimitivesTransparent) { + this.depthTargetPrimitivesTransparent.setSize(width, height); + } else { + this.depthTexturePrimitivesTransparent.define(width, height); + } if (this.depthTargetVolumes) { this.depthTargetVolumes.setSize(width, height); @@ -109,23 +118,58 @@ export class DrawPass { const { x, y, width, height } = camera.viewport; renderer.setViewport(x, y, width, height); + let renderTarget; if (toDrawingBuffer) { - this.webgl.unbindFramebuffer(); + renderTarget = null; } else { - this.colorTarget.bind(); if (!this.packedDepth) { this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); } + renderTarget = this.colorTarget; } - renderer.render(scene.primitives, camera, 'color', true, transparentBackground, 1, null); - // do a depth pass if not rendering to drawing buffer and // extensions.depthTexture is unsupported (i.e. depthTarget is set) if (!toDrawingBuffer && this.depthTargetPrimitives) { - this.depthTargetPrimitives.bind(); - renderer.render(scene.primitives, camera, 'depth', true, transparentBackground, 1, null); - this.colorTarget.bind(); + renderer.render(this.depthTargetPrimitives, scene.primitives, camera, 'depth', true, transparentBackground, 1, null, false); + } + + this.depthTexturePrimitives.attachFramebuffer(renderTarget!.framebuffer, 'depth'); + renderer.setViewport(0, 0, this.colorTarget.getWidth(), this.colorTarget.getHeight()); + renderer.render(renderTarget, scene.primitives, camera, 'color', true, transparentBackground, 1, null, false); + + if (helper.debug.isEnabled) { + helper.debug.syncVisibility(); + renderer.render(renderTarget, helper.debug.scene, camera, 'color', false, transparentBackground, 1, null, false); + } + if (helper.handle.isEnabled) { + renderer.render(renderTarget, helper.handle.scene, camera, 'color', false, transparentBackground, 1, null, false); + } + if (helper.camera.isEnabled) { + helper.camera.update(camera); + renderer.render(renderTarget, helper.camera.scene, helper.camera.camera, 'color', false, transparentBackground, 1, null, false); + } + + if (!toDrawingBuffer && this.depthTargetPrimitivesTransparent) { + renderer.render(this.depthTargetPrimitivesTransparent, scene.primitives, camera, 'depth', true, transparentBackground, 1, null, true); + } + + if (renderTarget !== null) { + // this.depthTexturePrimitivesTransparent.attachFramebuffer(renderTarget.framebuffer, 'depth'); + renderer.setViewport(0, 0, this.colorTarget.getWidth(), this.colorTarget.getHeight()); + renderer.render(renderTarget, scene.primitives, camera, 'color', false, transparentBackground, 1, this.depthTexturePrimitives, true); + } + + if (helper.debug.isEnabled) { + helper.debug.syncVisibility(); + renderer.render(renderTarget, helper.debug.scene, camera, 'color', false, transparentBackground, 1, null, true); + } + if (helper.handle.isEnabled) { + renderer.render(renderTarget, helper.handle.scene, camera, 'color', false, transparentBackground, 1, null, true); + } + if (helper.camera.isEnabled) { + helper.camera.update(camera); + renderer.render(renderTarget, helper.camera.scene, helper.camera.camera, 'color', false, transparentBackground, 1, null, true); } // do direct-volume rendering @@ -137,13 +181,11 @@ export class DrawPass { this.webgl.gl.scissor(x, y, width, height); this.webgl.gl.clear(this.webgl.gl.DEPTH_BUFFER_BIT); } - renderer.render(scene.volumes, camera, 'color', false, transparentBackground, 1, this.depthTexturePrimitives); + renderer.render(renderTarget, scene.volumes, camera, 'color', false, transparentBackground, 1, this.depthTexturePrimitives, true); // do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set) if (this.depthTargetVolumes) { - this.depthTargetVolumes.bind(); - renderer.render(scene.volumes, camera, 'depth', true, transparentBackground, 1, this.depthTexturePrimitives); - this.colorTarget.bind(); + renderer.render(this.depthTargetVolumes, scene.volumes, camera, 'depth', true, transparentBackground, 1, this.depthTexturePrimitives, false); } } @@ -163,18 +205,6 @@ export class DrawPass { this.depthMerge.render(); this.colorTarget.bind(); } - - if (helper.debug.isEnabled) { - helper.debug.syncVisibility(); - renderer.render(helper.debug.scene, camera, 'color', false, transparentBackground, 1, null); - } - if (helper.handle.isEnabled) { - renderer.render(helper.handle.scene, camera, 'color', false, transparentBackground, 1, null); - } - if (helper.camera.isEnabled) { - helper.camera.update(camera); - renderer.render(helper.camera.scene, helper.camera.camera, 'color', false, transparentBackground, 1, null); - } } render(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean) { diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts index 348180eae64322f32fd96755a4e9bee70fa347d4..210243b399aa76a0870e41b299e754a4fe9466b5 100644 --- a/src/mol-canvas3d/passes/pick.ts +++ b/src/mol-canvas3d/passes/pick.ts @@ -63,26 +63,26 @@ export class PickPass { } } - private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant) { + private renderVariant(renderTarget: RenderTarget, renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant) { const pickScale = this.pickBaseScale / this.webgl.pixelRatio; const depth = this.drawPass.depthTexturePrimitives; - renderer.render(scene.primitives, camera, variant, true, false, pickScale, null); - renderer.render(scene.volumes, camera, variant, false, false, pickScale, depth); - renderer.render(helper.handle.scene, camera, variant, false, false, pickScale, null); + renderer.render(renderTarget, scene.primitives, camera, variant, true, false, pickScale, null, false); + renderer.render(renderTarget, scene.volumes, camera, variant, false, false, pickScale, depth, false); + renderer.render(renderTarget, helper.handle.scene, camera, variant, false, false, pickScale, null, false); } render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) { this.objectPickTarget.bind(); - this.renderVariant(renderer, camera, scene, helper, 'pickObject'); + this.renderVariant(this.objectPickTarget, renderer, camera, scene, helper, 'pickObject'); this.instancePickTarget.bind(); - this.renderVariant(renderer, camera, scene, helper, 'pickInstance'); + this.renderVariant(this.instancePickTarget, renderer, camera, scene, helper, 'pickInstance'); this.groupPickTarget.bind(); - this.renderVariant(renderer, camera, scene, helper, 'pickGroup'); + this.renderVariant(this.groupPickTarget, renderer, camera, scene, helper, 'pickGroup'); this.depthPickTarget.bind(); - this.renderVariant(renderer, camera, scene, helper, 'depth'); + this.renderVariant(this.depthPickTarget, renderer, camera, scene, helper, 'depth'); } } diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index b1df70de41a8d31d2c308c1d7b4d609890671957..9c90f8d06745f230c65f6adc257dd39e04771c36 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/src/mol-geo/geometry/direct-volume/direct-volume.ts @@ -286,6 +286,8 @@ export namespace DirectVolume { dFlatShaded: ValueCell.create(props.flatShaded), dFlipSided: ValueCell.create(props.flipSided), dIgnoreLight: ValueCell.create(props.ignoreLight), + + uRenderWboit: ValueCell.create(1), }; } diff --git a/src/mol-gl/renderable.ts b/src/mol-gl/renderable.ts index 51f087b79b309950499064488082646990a4a909..ad28d9262aeba9c0106a374e95baef422def45d1 100644 --- a/src/mol-gl/renderable.ts +++ b/src/mol-gl/renderable.ts @@ -10,6 +10,7 @@ import { GraphicsRenderItem, ComputeRenderItem, GraphicsRenderVariant } from './ import { ValueCell } from '../mol-util'; import { idFactory } from '../mol-util/id-factory'; import { clamp } from '../mol-math/interpolate'; +import { Textures } from './webgl/texture'; const getNextRenderableId = idFactory(); @@ -28,7 +29,7 @@ export interface Renderable<T extends RenderableValues> { readonly values: T readonly state: RenderableState - render: (variant: GraphicsRenderVariant) => void + render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => void getProgram: (variant: GraphicsRenderVariant) => Program update: () => void dispose: () => void @@ -41,11 +42,11 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem: values, state, - render: (variant: GraphicsRenderVariant) => { + render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => { if (values.uAlpha && values.alpha) { ValueCell.updateIfChanged(values.uAlpha, clamp(values.alpha.ref.value * state.alphaFactor, 0, 1)); } - renderItem.render(variant); + renderItem.render(variant, sharedTexturesList); }, getProgram: (variant: GraphicsRenderVariant) => renderItem.getProgram(variant), update: () => renderItem.update(), diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts index 79e10b821f56a5d3a767e836b521f8d0d06a681b..0b4dcb3b2a369ff441e44e5f6abc37ff0c65f8e3 100644 --- a/src/mol-gl/renderable/direct-volume.ts +++ b/src/mol-gl/renderable/direct-volume.ts @@ -61,6 +61,8 @@ export const DirectVolumeSchema = { uAlpha: UniformSpec('f'), + uRenderWboit: UniformSpec('i'), + uIsoValue: UniformSpec('v2'), uBboxMin: UniformSpec('v3'), uBboxMax: UniformSpec('v3'), diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index 5c56c062cd7373b15388715d36da78e8f2491b3c..50de083a80a73d3e2086e5ad441a944a0a2f0c76 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -7,7 +7,7 @@ import { Renderable, RenderableState, createRenderable } from '../renderable'; import { WebGLContext } from '../webgl/context'; import { createGraphicsRenderItem } from '../webgl/render-item'; -import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues } from './schema'; +import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema } from './schema'; import { MeshShaderCode } from '../shader-code'; import { ValueCell } from '../../mol-util'; @@ -25,7 +25,7 @@ export type MeshSchema = typeof MeshSchema export type MeshValues = Values<MeshSchema> export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues, state: RenderableState, materialId: number): Renderable<MeshValues> { - const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema }; + const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...MeshSchema }; const internalValues: InternalValues = { uObjectId: ValueCell.create(id), }; diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index 06f65477c0393f337969b1c1cc124fe58ccc1554..fa0927a85f81bbb42356ef5c0e8849c54cd22d34 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.ts @@ -155,6 +155,8 @@ export const GlobalUniformSchema = { uHighlightColor: UniformSpec('v3'), uSelectColor: UniformSpec('v3'), + + uRenderWboit: UniformSpec('i'), } as const; export type GlobalUniformSchema = typeof GlobalUniformSchema export type GlobalUniformValues = Values<GlobalUniformSchema> diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 5821c1479ae1ad055976fe66655272513e2671c6..1a9b7474bbc9d54e5ad567a5bfcf93771c9876a7 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -9,17 +9,23 @@ import { ICamera } from '../mol-canvas3d/camera'; import Scene from './scene'; import { WebGLContext } from './webgl/context'; import { Mat4, Vec3, Vec4, Vec2, Quat } from '../mol-math/linear-algebra'; -import { Renderable } from './renderable'; +import { ComputeRenderable, createComputeRenderable, Renderable } from './renderable'; import { Color } from '../mol-util/color'; import { ValueCell, deepEqual } from '../mol-util'; -import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema'; -import { GraphicsRenderVariant } from './webgl/render-item'; +import { RenderableValues, GlobalUniformValues, BaseValues, TextureSpec, Values } from './renderable/schema'; +import { createComputeRenderItem, GraphicsRenderVariant } from './webgl/render-item'; import { ParamDefinition as PD } from '../mol-util/param-definition'; import { Clipping } from '../mol-theme/clipping'; import { stringToWords } from '../mol-util/string'; import { Transparency } from '../mol-theme/transparency'; import { degToRad } from '../mol-math/misc'; -import { Texture } from './webgl/texture'; +import { Texture, Textures } from './webgl/texture'; +import { RenderTarget } from './webgl/render-target'; +import { QuadSchema, QuadValues } from './compute/util'; + +import quad_vert from '../mol-gl/shader/quad.vert'; +import evaluate_wboit_frag from '../mol-gl/shader/evaluate-wboit.frag'; +import { ShaderCode } from './shader-code'; export interface RendererStats { programCount: number @@ -42,7 +48,7 @@ interface Renderer { readonly props: Readonly<RendererProps> clear: (transparentBackground: boolean) => void - render: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, drawingBufferScale: number, depthTexture: Texture | null) => void + render: (renderTarget: RenderTarget | null, group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, drawingBufferScale: number, depthTexture: Texture | null, renderTransparent: boolean) => void setProps: (props: Partial<RendererProps>) => void setViewport: (x: number, y: number, width: number, height: number) => void dispose: () => void @@ -158,14 +164,46 @@ function getClip(props: RendererProps['clip'], clip?: Clip): Clip { namespace Renderer { export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer { - const { gl, state, stats, extensions: { fragDepth } } = ctx; + const { gl, state, resources, stats, extensions: { fragDepth } } = ctx; const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props); const style = getStyle(p.style); const clip = getClip(p.clip); + const { drawBuffers, textureFloat, colorBufferFloat, depthTexture } = ctx.extensions; + const viewport = Viewport(); const drawingBufferSize = Vec2.create(gl.drawingBufferWidth, gl.drawingBufferHeight); const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor); + + const sharedTexturesList: Textures = []; + + let enableWboit = textureFloat !== null && colorBufferFloat !== null && depthTexture !== null; + + let wboitATexture = enableWboit ? resources.texture('image-float32', 'rgba', 'float', 'nearest') : null; + wboitATexture?.define(viewport.width, viewport.height); + let wboitBTexture = enableWboit ? resources.texture('image-float32', 'rgba', 'float', 'nearest') : null; + wboitBTexture?.define(viewport.width, viewport.height); + + let evaluateWboitRenderable = enableWboit ? getEvaluateWboitRenderable(ctx, wboitATexture!, wboitBTexture!) : null; + + let wboitFramebuffers = [resources.framebuffer()]; + 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'); + } const view = Mat4(); const invView = Mat4(); @@ -204,6 +242,9 @@ namespace Renderer { uFogNear: ValueCell.create(1), uFogFar: ValueCell.create(10000), uFogColor: ValueCell.create(bgColor), + + uRenderWboit: ValueCell.create(0), + uTransparentBackground: ValueCell.create(false), uClipObjectType: ValueCell.create(clip.objects.type), @@ -232,7 +273,7 @@ namespace Renderer { let globalUniformsNeedUpdate = true; - const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant, depthTexture: Texture | null) => { + const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => { if (!r.state.visible || (!r.state.pickable && variant[0] === 'p')) { return; } @@ -265,8 +306,6 @@ namespace Renderer { globalUniformsNeedUpdate = false; } - if (depthTexture) program.bindTextures([['tDepth', depthTexture]]); - if (r.values.uStepFactor) { // indicates direct-volume // always cull front state.enable(gl.CULL_FACE); @@ -313,10 +352,15 @@ namespace Renderer { state.depthMask(r.state.writeDepth); } - r.render(variant); + r.render(variant, sharedTexturesList); }; - const render = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, drawingBufferScale: number, depthTexture: Texture | null) => { + const render = (renderTarget: RenderTarget | null, group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean, drawingBufferScale: number, depthTexture: Texture | null, renderTransparent: boolean) => { + let localSharedTexturesList = sharedTexturesList; + if (depthTexture) { + localSharedTexturesList = [...localSharedTexturesList, ['tDepth', depthTexture]]; + } + ValueCell.update(globalUniforms.uModel, group.view); ValueCell.update(globalUniforms.uView, camera.view); ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view)); @@ -339,6 +383,8 @@ namespace Renderer { ValueCell.updateIfChanged(globalUniforms.uFogNear, camera.fogNear); ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground); + ValueCell.update(globalUniforms.uRenderWboit, 0); + if (gl.drawingBufferWidth * drawingBufferScale !== drawingBufferSize[0] || gl.drawingBufferHeight * drawingBufferScale !== drawingBufferSize[1] ) { @@ -358,10 +404,16 @@ namespace Renderer { state.colorMask(true, true, true, true); state.enable(gl.DEPTH_TEST); + if (renderTarget) { + renderTarget.bind(); + } else { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } + const { x, y, width, height } = viewport; gl.viewport(x, y, width, height); gl.scissor(x, y, width, height); - + if (clear) { state.depthMask(true); if (variant === 'color') { @@ -373,31 +425,99 @@ namespace Renderer { } if (variant === 'color') { - for (let i = 0, il = renderables.length; i < il; ++i) { - const r = renderables[i]; - if (r.state.opaque) { - renderObject(r, variant, depthTexture); + if (enableWboit) { + if (!renderTransparent) { + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (r.state.opaque) { + renderObject(r, variant, localSharedTexturesList); + } + } + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque && r.state.writeDepth) { + renderObject(r, variant, localSharedTexturesList); + } + } + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque && !r.state.writeDepth) { + renderObject(r, variant, localSharedTexturesList); + } + } + } else { + wboitFramebuffers[0].bind(); + + state.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + ValueCell.update(globalUniforms.uRenderWboit, 1); + globalUniformsNeedUpdate = true; + + state.disable(gl.DEPTH_TEST); + state.enable(gl.BLEND); + state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ZERO, gl.ONE_MINUS_SRC_ALPHA); + + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (r.state.opaque) { + renderObject(r, variant, localSharedTexturesList); + } + } + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque && r.state.writeDepth) { + renderObject(r, variant, localSharedTexturesList); + } + } + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque && !r.state.writeDepth) { + renderObject(r, variant, localSharedTexturesList); + } + } + if (renderTarget) { + renderTarget.bind(); + } else { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } + + state.blendFuncSeparate(gl.ONE_MINUS_SRC_ALPHA, gl.SRC_ALPHA, gl.ZERO, gl.ONE); + state.enable(gl.BLEND); + state.disable(gl.DEPTH_TEST); + + evaluateWboitRenderable?.update(); + evaluateWboitRenderable?.render(); } - } - - state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); - state.enable(gl.BLEND); - for (let i = 0, il = renderables.length; i < il; ++i) { - const r = renderables[i]; - if (!r.state.opaque && r.state.writeDepth) { - renderObject(r, variant, depthTexture); + } else { + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (r.state.opaque) { + renderObject(r, variant, sharedTexturesList); + } } - } - for (let i = 0, il = renderables.length; i < il; ++i) { - const r = renderables[i]; - if (!r.state.opaque && !r.state.writeDepth) { - renderObject(r, variant, depthTexture); + + state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE); + state.enable(gl.BLEND); + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque && r.state.writeDepth) { + renderObject(r, variant, sharedTexturesList); + } + } + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque && !r.state.writeDepth) { + renderObject(r, variant, sharedTexturesList); + } } } } else { // picking & depth - for (let i = 0, il = renderables.length; i < il; ++i) { - if (!renderables[i].state.colorOnly) { - renderObject(renderables[i], variant, depthTexture); + if (!renderTransparent) { + for (let i = 0, il = renderables.length; i < il; ++i) { + if (!renderables[i].state.colorOnly) { + renderObject(renderables[i], variant, sharedTexturesList); + } } } } @@ -479,6 +599,32 @@ namespace Renderer { Viewport.set(viewport, x, y, width, height); ValueCell.update(globalUniforms.uViewportHeight, height); ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height)); + + wboitATexture?.define(viewport.width, viewport.height); + wboitBTexture?.define(viewport.width, viewport.height); + + if (drawBuffers) { + wboitFramebuffers[0].destroy(); + wboitFramebuffers = []; + 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 = []; + wboitFramebuffers.push(resources.framebuffer(), resources.framebuffer()); + + wboitATexture?.attachFramebuffer(wboitFramebuffers[0], 'color0') + wboitBTexture?.attachFramebuffer(wboitFramebuffers[1], 'color0'); + } } }, @@ -509,4 +655,26 @@ namespace Renderer { } } +const EvaluateWboitSchema = { + ...QuadSchema, + tWboitA: TextureSpec('texture', 'rgba', 'float', 'nearest'), + tWboitB: TextureSpec('texture', 'rgba', 'float', 'nearest'), +}; + +type EvaluateWboitRenderable = ComputeRenderable<Values<typeof EvaluateWboitSchema>> + +function getEvaluateWboitRenderable(ctx: WebGLContext, wboitATexture: Texture, wboitBTexture: Texture): EvaluateWboitRenderable { + const values: Values<typeof EvaluateWboitSchema> = { + ...QuadValues, + tWboitA: ValueCell.create(wboitATexture), + tWboitB: ValueCell.create(wboitBTexture), + }; + + const schema = { ...EvaluateWboitSchema }; + const shaderCode = ShaderCode('ssao', quad_vert, evaluate_wboit_frag); + const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values); + + return createComputeRenderable(renderItem, values); +} + export default Renderer; \ No newline at end of file diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 3b85e1800ca82654970e38f8574769d84f19a0bb..1322623f012da30624f79d0291df0f8de617c5cc 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -59,6 +59,8 @@ import size_vert_params from './shader/chunks/size-vert-params.glsl'; import texture3d_from_1d_trilinear from './shader/chunks/texture3d-from-1d-trilinear.glsl'; import texture3d_from_2d_linear from './shader/chunks/texture3d-from-2d-linear.glsl'; import texture3d_from_2d_nearest from './shader/chunks/texture3d-from-2d-nearest.glsl'; +import wboit_params from './shader/chunks/wboit-params.glsl'; +import wboit_write from './shader/chunks/wboit-write.glsl'; const ShaderChunks: { [k: string]: string } = { apply_fog, @@ -88,7 +90,9 @@ const ShaderChunks: { [k: string]: string } = { size_vert_params, texture3d_from_1d_trilinear, texture3d_from_2d_linear, - texture3d_from_2d_nearest + texture3d_from_2d_nearest, + wboit_params, + wboit_write }; const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gmi; diff --git a/src/mol-gl/shader/chunks/assign-material-color.glsl.ts b/src/mol-gl/shader/chunks/assign-material-color.glsl.ts index 4ad0f6a17c3f5960e9a4269f390aa0e6eaf0aad1..e9d73c05e6d907d9817d5d81718b7c3a04525fe0 100644 --- a/src/mol-gl/shader/chunks/assign-material-color.glsl.ts +++ b/src/mol-gl/shader/chunks/assign-material-color.glsl.ts @@ -45,8 +45,10 @@ export default ` at = fract(dot(vec3(coord, vGroup + 0.5), vec3(2.0, 7.0, 23.0) / 17.0f)); #endif - if (ta < 0.99 && (ta < 0.01 || ta < at)) - discard; + if (ta < 0.99 && (ta < 0.01 || ta < at)) { + //discard; + } + material.a = ta; #endif #endif `; \ No newline at end of file diff --git a/src/mol-gl/shader/chunks/common-frag-params.glsl.ts b/src/mol-gl/shader/chunks/common-frag-params.glsl.ts index e5e44da56106b0c335a4cf7d007e72a2847ab90a..6c784e73b062809b0f6d047331e8b5f3eca14c9a 100644 --- a/src/mol-gl/shader/chunks/common-frag-params.glsl.ts +++ b/src/mol-gl/shader/chunks/common-frag-params.glsl.ts @@ -43,4 +43,6 @@ uniform float uInteriorDarkening; uniform bool uInteriorColorFlag; uniform vec3 uInteriorColor; bool interior; + +uniform mat4 uProjection; `; \ No newline at end of file diff --git a/src/mol-gl/shader/chunks/wboit-params.glsl.ts b/src/mol-gl/shader/chunks/wboit-params.glsl.ts new file mode 100644 index 0000000000000000000000000000000000000000..adbab0996eb1b4363beffc797e7d4c930e26dd00 --- /dev/null +++ b/src/mol-gl/shader/chunks/wboit-params.glsl.ts @@ -0,0 +1,22 @@ +export default ` +#if defined(dRenderVariant_color) +#if !defined(dRenderMode_volume) + uniform sampler2D tDepth; + + float getDepth(const in vec2 coords) { + #ifdef dPackedDepth + return unpackRGBAToDepth(texture2D(tDepth, coords)); + #else + return texture2D(tDepth, coords).r; + #endif + } +#endif + uniform int uRenderWboit; + uniform vec4 uViewport; +#endif + +float calcDepth(const in vec3 pos) { + vec2 clipZW = pos.z * uProjection[2].zw + uProjection[3].zw; + return 0.5 + 0.5 * clipZW.x / clipZW.y; +} +`; \ No newline at end of file diff --git a/src/mol-gl/shader/chunks/wboit-write.glsl.ts b/src/mol-gl/shader/chunks/wboit-write.glsl.ts new file mode 100644 index 0000000000000000000000000000000000000000..c75eb3a683938b0bbd856fdcc8511b82c1a5bb1a --- /dev/null +++ b/src/mol-gl/shader/chunks/wboit-write.glsl.ts @@ -0,0 +1,16 @@ +export default ` +if (uRenderWboit == 0) { + if (gl_FragColor.a != 1.0) { + discard; + } +} else if (uRenderWboit == 1) { + if (gl_FragColor.a != 1.0 && abs(calcDepth(vViewPosition) + 0.01) < getDepth(gl_FragCoord.xy / uViewport.zw)) { + float alpha = gl_FragColor.a; + float wboitWeight = alpha * clamp(pow(1.0 - absFragDepth, 2.0), 0.01, 1.0); + gl_FragColor = vec4(gl_FragColor.rgb * alpha * wboitWeight, alpha); + out_FragData1 = vec4(alpha * wboitWeight); + } else { + discard; + } +} +`; \ No newline at end of file diff --git a/src/mol-gl/shader/direct-volume.frag.ts b/src/mol-gl/shader/direct-volume.frag.ts index 0f0819f99a8aaf1998e5e03fab4e8d0b562a1f69..87e67e6df96f774e0b9320257022e316c40d9f28 100644 --- a/src/mol-gl/shader/direct-volume.frag.ts +++ b/src/mol-gl/shader/direct-volume.frag.ts @@ -119,16 +119,12 @@ uniform mat4 uUnitToCartn; } #endif +#include wboit_params + vec4 transferFunction(float value) { return texture2D(tTransferTex, vec2(value, 0.0)); } -// Calculate depth based on the given camera position. -float calcDepth(const in vec3 cameraPos){ - vec2 clipZW = cameraPos.z * uProjection[2].zw + uProjection[3].zw; - return 0.5 + 0.5 * clipZW.x / clipZW.y; -} - float getDepth(const in vec2 coords) { #ifdef dPackedDepth return unpackRGBAToDepth(texture2D(tDepth, coords)); @@ -405,5 +401,11 @@ void main () { if (gl_FragColor == vec4(0.0)) discard; #endif + + #if defined(dRenderVariant_color) + vec3 vViewPosition = vOrigPos; + float absFragDepth = abs(calcDepth(vViewPosition)); + #include wboit_write + #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/evaluate-wboit.frag.ts b/src/mol-gl/shader/evaluate-wboit.frag.ts new file mode 100644 index 0000000000000000000000000000000000000000..e09ce3407b69e7253603447206cac64d73a8d89d --- /dev/null +++ b/src/mol-gl/shader/evaluate-wboit.frag.ts @@ -0,0 +1,15 @@ +export default ` +precision highp float; + +uniform sampler2D tWboitA; +uniform sampler2D tWboitB; + +void main() { + ivec2 coords = ivec2(gl_FragCoord.xy); + + vec4 accum = texelFetch(tWboitA, coords, 0); + float r = accum.a; + accum.a = texelFetch(tWboitB, coords, 0).r; + gl_FragColor = vec4(accum.rgb / clamp(accum.a, 0.0001, 50000.0), r); +} +`; \ No newline at end of file diff --git a/src/mol-gl/shader/lines.frag.ts b/src/mol-gl/shader/lines.frag.ts index 81c91489b990406d39ded0af995917f11098f6d0..7cc764e647cc1d64efd9fa331c12a1210f030ad5 100644 --- a/src/mol-gl/shader/lines.frag.ts +++ b/src/mol-gl/shader/lines.frag.ts @@ -12,6 +12,7 @@ precision highp int; #include common_frag_params #include color_frag_params #include common_clip +#include wboit_params void main(){ #include clip_pixel @@ -27,6 +28,9 @@ void main(){ #include apply_marker_color #include apply_fog + + float absFragDepth = abs(gl_FragCoord.z); + #include wboit_write #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/mesh.frag.ts b/src/mol-gl/shader/mesh.frag.ts index 0ed9f7ebe318da42c6fa91bea2d1d1199fa575c4..a30f4df13c732cb566e307f5fa80ceb72f2cadcd 100644 --- a/src/mol-gl/shader/mesh.frag.ts +++ b/src/mol-gl/shader/mesh.frag.ts @@ -14,6 +14,7 @@ precision highp int; #include light_frag_params #include normal_frag_params #include common_clip +#include wboit_params void main() { #include clip_pixel @@ -59,6 +60,9 @@ void main() { #include apply_interior_color #include apply_marker_color #include apply_fog + + float absFragDepth = abs(gl_FragCoord.z); + #include wboit_write #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/points.frag.ts b/src/mol-gl/shader/points.frag.ts index e3e8fafb350afdfe991fafd83d8a651f05067d03..3082ca04d0728b96ad1ae289dbc9d2e7481b843c 100644 --- a/src/mol-gl/shader/points.frag.ts +++ b/src/mol-gl/shader/points.frag.ts @@ -12,6 +12,7 @@ precision highp int; #include common_frag_params #include color_frag_params #include common_clip +#include wboit_params #ifdef dPointFilledCircle uniform float uPointEdgeBleach; @@ -41,6 +42,9 @@ void main(){ #include apply_marker_color #include apply_fog + + float absFragDepth = abs(gl_FragCoord.z); + #include wboit_write #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/spheres.frag.ts b/src/mol-gl/shader/spheres.frag.ts index de5731f3deaa07513cd6c1bf2666a7ede3bbe0de..1ef482aadb033027a585c75af0c920cb24b4af47 100644 --- a/src/mol-gl/shader/spheres.frag.ts +++ b/src/mol-gl/shader/spheres.frag.ts @@ -13,8 +13,7 @@ precision highp int; #include color_frag_params #include light_frag_params #include common_clip - -uniform mat4 uProjection; +#include wboit_params uniform float uClipNear; uniform float uIsOrtho; @@ -27,12 +26,6 @@ varying vec3 vPointViewPosition; vec3 cameraPos; vec3 cameraNormal; -// Calculate depth based on the given camera position. -float calcDepth(const in vec3 cameraPos){ - vec2 clipZW = cameraPos.z * uProjection[2].zw + uProjection[3].zw; - return 0.5 + 0.5 * clipZW.x / clipZW.y; -} - float calcClip(const in vec3 cameraPos) { return dot(vec4(cameraPos, 1.0), vec4(0.0, 0.0, 1.0, uClipNear - 0.5)); } @@ -114,6 +107,9 @@ void main(void){ #include apply_interior_color #include apply_marker_color #include apply_fog + + float absFragDepth = abs(gl_FragDepthEXT); + #include wboit_write #endif } `; \ No newline at end of file diff --git a/src/mol-gl/webgl/program.ts b/src/mol-gl/webgl/program.ts index 065fed789c6e70607e8eef1cb5ae61bca8ccc638..8d3443d42a430ef8892d5d5b0e17915ea6ff6975 100644 --- a/src/mol-gl/webgl/program.ts +++ b/src/mol-gl/webgl/program.ts @@ -24,7 +24,7 @@ export interface Program { use: () => void setUniforms: (uniformValues: UniformsList) => void bindAttributes: (attribueBuffers: AttributeBuffers) => void - bindTextures: (textures: Textures) => void + bindTextures: (textures: Textures, startingTargetUnit?: number) => void reset: () => void destroy: () => void @@ -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(); @@ -198,21 +198,17 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi if (l !== -1) buffer.bind(l); } }, - bindTextures: (textures: Textures) => { + bindTextures: (textures: Textures, startingTargetUnit?: number) => { + startingTargetUnit = startingTargetUnit ?? 0; + for (let i = 0, il = textures.length; i < il; ++i) { const [k, texture] = textures[i]; const l = locations[k]; if (l !== null && l !== undefined) { - if (k === 'tDepth') { - // TODO find more explicit way? - texture.bind(15 as TextureId); - uniformSetters[k](gl, l, 15 as TextureId); - } else { - // TODO if the order and count of textures in a material can be made invariant - // bind needs to be called only when the material changes - texture.bind(i as TextureId); - uniformSetters[k](gl, l, i as TextureId); - } + // TODO if the order and count of textures in a material can be made invariant + // bind needs to be called only when the material changes + texture.bind((i + startingTargetUnit) as TextureId); + uniformSetters[k](gl, l, (i + startingTargetUnit) as TextureId); } } }, diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 88b9058212e518b9245354db4be0135eb128d7fb..3d5355e0b364bf864ee425888b8ea9a0869b0fa9 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/src/mol-gl/webgl/render-item.ts @@ -5,7 +5,7 @@ */ import { createAttributeBuffers, ElementsBuffer, AttributeKind } from './buffer'; -import { createTextures, Texture } from './texture'; +import { createTextures, Texture, Textures } from './texture'; import { WebGLContext, checkError } from './context'; import { ShaderCode, DefineValues } from '../shader-code'; import { Program } from './program'; @@ -40,7 +40,7 @@ export interface RenderItem<T extends string> { readonly materialId: number getProgram: (variant: T) => Program - render: (variant: T) => void + render: (variant: T, sharedTexturesList?: Textures) => void update: () => Readonly<ValueChanges> destroy: () => void } @@ -164,12 +164,17 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: materialId, getProgram: (variant: T) => programs[variant], - render: (variant: T) => { + render: (variant: T, sharedTexturesList?: Textures) => { if (drawCount === 0 || instanceCount === 0 || ctx.isContextLost) return; const program = programs[variant]; if (program.id === currentProgramId && state.currentRenderItemId === id) { program.setUniforms(uniformValueEntries); - program.bindTextures(textures); + if (sharedTexturesList && sharedTexturesList.length > 0) { + program.bindTextures(sharedTexturesList, 0); + program.bindTextures(textures, sharedTexturesList.length); + } else { + program.bindTextures(textures); + } } else { const vertexArray = vertexArrays[variant]; if (program.id !== state.currentProgramId || program.id !== currentProgramId || @@ -182,7 +187,12 @@ export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: currentProgramId = program.id; } program.setUniforms(uniformValueEntries); - program.bindTextures(textures); + if (sharedTexturesList && sharedTexturesList.length > 0) { + program.bindTextures(sharedTexturesList, 0); + program.bindTextures(textures, sharedTexturesList.length); + } else { + program.bindTextures(textures); + } if (vertexArray) { vertexArray.bind(); // need to bind elements buffer explicitly since it is not always recorded in the VAO