diff --git a/CHANGELOG.md b/CHANGELOG.md index a841a4c09b8dadd843438dff2e2e15a77e6a11c4..23f03b0c3df4af781d7ab72f26d73a1de27afbe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,13 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] - Fix case handling of ``struct_conf`` mmCIF enumeration field (#425) +- Add support for outlines around transparent objects +- Improve per-group transparency when wboit is switched off +- Fix ``allowTransparentBackfaces`` for per-group transparency - Fix ``FormatRegistry.isApplicable`` returning true for unregistered formats - Fix: handle building of ``GridLookup3D`` with zero cell size +- Fix ``ignoreLight`` for direct-volume rendering with webgl1 +- Fix (non-black) outlines when using transparent background ## [v3.7.0] - 2022-04-13 diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index 469393ad983b4bf1b0aacd1532fe37f1b9fda577..3c481dfd5d29e43e2669d6499467b8b180b4e9b0 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -11,48 +11,16 @@ import { Renderer } from '../../mol-gl/renderer'; import { Scene } from '../../mol-gl/scene'; import { Texture } from '../../mol-gl/webgl/texture'; import { Camera, ICamera } from '../camera'; -import { QuadSchema, QuadValues } from '../../mol-gl/compute/util'; -import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema'; -import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable'; -import { ShaderCode } from '../../mol-gl/shader-code'; -import { createComputeRenderItem } from '../../mol-gl/webgl/render-item'; import { ValueCell } from '../../mol-util'; import { Vec2 } from '../../mol-math/linear-algebra'; import { Helper } from '../helper/helper'; -import { quad_vert } from '../../mol-gl/shader/quad.vert'; -import { depthMerge_frag } from '../../mol-gl/shader/depth-merge.frag'; import { StereoCamera } from '../camera/stereo'; import { WboitPass } from './wboit'; import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing'; import { MarkingPass, MarkingProps } from './marking'; import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util'; -const DepthMergeSchema = { - ...QuadSchema, - tDepthPrimitives: TextureSpec('texture', 'depth', 'ushort', 'nearest'), - tDepthVolumes: TextureSpec('texture', 'depth', 'ushort', 'nearest'), - uTexSize: UniformSpec('v2'), - dPackedDepth: DefineSpec('boolean'), -}; -const DepthMergeShaderCode = ShaderCode('depth-merge', quad_vert, depthMerge_frag); -type DepthMergeRenderable = ComputeRenderable<Values<typeof DepthMergeSchema>> - -function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Texture, depthTextureVolumes: Texture, packedDepth: boolean): DepthMergeRenderable { - const values: Values<typeof DepthMergeSchema> = { - ...QuadValues, - tDepthPrimitives: ValueCell.create(depthTexturePrimitives), - tDepthVolumes: ValueCell.create(depthTextureVolumes), - uTexSize: ValueCell.create(Vec2.create(depthTexturePrimitives.getWidth(), depthTexturePrimitives.getHeight())), - dPackedDepth: ValueCell.create(packedDepth), - }; - - const schema = { ...DepthMergeSchema }; - const renderItem = createComputeRenderItem(ctx, 'triangles', DepthMergeShaderCode, schema, values); - - return createComputeRenderable(renderItem, values); -} - type Props = { postprocessing: PostprocessingProps marking: MarkingProps @@ -79,7 +47,6 @@ export class DrawPass { private depthTargetPrimitives: RenderTarget | null; private depthTargetVolumes: RenderTarget | null; private depthTextureVolumes: Texture; - private depthMerge: DepthMergeRenderable; private copyFboTarget: CopyRenderable; private copyFboPostprocessing: CopyRenderable; @@ -113,7 +80,6 @@ export class DrawPass { this.depthTexturePrimitives.define(width, height); this.depthTextureVolumes.define(width, height); } - this.depthMerge = getDepthMergeRenderable(webgl, this.depthTexturePrimitives, this.depthTextureVolumes, this.packedDepth); this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined; this.marking = new MarkingPass(webgl, width, height); @@ -148,8 +114,6 @@ export class DrawPass { this.depthTextureVolumes.define(width, height); } - ValueCell.update(this.depthMerge.values.uTexSize, Vec2.set(this.depthMerge.values.uTexSize.ref.value, width, height)); - ValueCell.update(this.copyFboTarget.values.uTexSize, Vec2.set(this.copyFboTarget.values.uTexSize.ref.value, width, height)); ValueCell.update(this.copyFboPostprocessing.values.uTexSize, Vec2.set(this.copyFboPostprocessing.values.uTexSize.ref.value, width, height)); @@ -163,20 +127,6 @@ export class DrawPass { } } - private _depthMerge() { - const { state, gl } = this.webgl; - - this.depthMerge.update(); - this.depthTarget.bind(); - state.disable(gl.BLEND); - state.disable(gl.DEPTH_TEST); - state.disable(gl.CULL_FACE); - state.depthMask(false); - state.clearColor(1, 1, 1, 1); - gl.clear(gl.COLOR_BUFFER_BIT); - this.depthMerge.render(); - } - private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) { if (!this.wboit?.supported) throw new Error('expected wboit to be supported'); @@ -189,30 +139,27 @@ export class DrawPass { renderer.clearDepth(); renderer.renderWboitOpaque(scene.primitives, camera, null); - // render opaque volumes - this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); - this.colorTarget.bind(); - renderer.clearDepth(); - renderer.renderWboitOpaque(scene.volumes, camera, this.depthTexturePrimitives); - - // merge depth of opaque primitives and volumes - this._depthMerge(); - if (PostprocessingPass.isEnabled(postprocessingProps)) { + if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { + this.depthTarget.bind(); + renderer.clearDepth(true); + if (scene.getOpacityAverage() < 1) { + renderer.renderDepthTransparent(scene.primitives, camera, this.depthTexturePrimitives); + } + } + this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps); } // render transparent primitives and volumes this.wboit.bind(); - renderer.renderWboitTransparent(scene.primitives, camera, this.depthTexture); - renderer.renderWboitTransparent(scene.volumes, camera, this.depthTexture); + renderer.renderWboitTransparent(scene.primitives, camera, this.depthTexturePrimitives); + renderer.renderWboitTransparent(scene.volumes, camera, this.depthTexturePrimitives); // evaluate wboit if (PostprocessingPass.isEnabled(postprocessingProps)) { - this.depthTexturePrimitives.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth'); this.postprocessing.target.bind(); } else { - this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); this.colorTarget.bind(); } this.wboit.render(); @@ -236,42 +183,36 @@ export class DrawPass { // extensions.depthTexture is unsupported (i.e. depthTarget is set) if (this.depthTargetPrimitives) { this.depthTargetPrimitives.bind(); - renderer.clear(false); - // TODO: this should only render opaque - renderer.renderDepth(scene.primitives, camera, null); + renderer.clearDepth(true); + renderer.renderDepthOpaque(scene.primitives, camera, null); this.colorTarget.bind(); } - // do direct-volume rendering - if (!this.packedDepth) { - this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth'); - renderer.clearDepth(); // from previous frame - } - renderer.renderBlendedVolumeOpaque(scene.volumes, camera, this.depthTexturePrimitives); - - // do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set) - if (this.depthTargetVolumes) { - this.depthTargetVolumes.bind(); - renderer.clear(false); - renderer.renderDepth(scene.volumes, camera, this.depthTexturePrimitives); - this.colorTarget.bind(); - } + if (PostprocessingPass.isEnabled(postprocessingProps)) { + if (!this.packedDepth) { + this.depthTexturePrimitives.detachFramebuffer(this.postprocessing.target.framebuffer, 'depth'); + } else { + this.colorTarget.depthRenderbuffer?.detachFramebuffer(this.postprocessing.target.framebuffer); + } - // merge depths from primitive and volume rendering - this._depthMerge(); - this.colorTarget.bind(); + if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) { + this.depthTarget.bind(); + renderer.clearDepth(true); + if (scene.getOpacityAverage() < 1) { + renderer.renderDepthTransparent(scene.primitives, camera, this.depthTexturePrimitives); + } + } - if (PostprocessingPass.isEnabled(postprocessingProps)) { this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps); - } - renderer.renderBlendedVolumeTransparent(scene.volumes, camera, this.depthTexturePrimitives); - const target = PostprocessingPass.isEnabled(postprocessingProps) - ? this.postprocessing.target : this.colorTarget; - if (!this.packedDepth) { - this.depthTexturePrimitives.attachFramebuffer(target.framebuffer, 'depth'); + if (!this.packedDepth) { + this.depthTexturePrimitives.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth'); + } else { + this.colorTarget.depthRenderbuffer?.attachFramebuffer(this.postprocessing.target.framebuffer); + } } - target.bind(); + + renderer.renderBlendedVolume(scene.volumes, camera, this.depthTexturePrimitives); } renderer.renderBlendedTransparent(scene.primitives, camera, null); diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts index 90de38b2e29c93b3ec91f913495980803e06d446..d5777f84f15e0cfecb7eb1b89611830a2f577c3b 100644 --- a/src/mol-canvas3d/passes/pick.ts +++ b/src/mol-canvas3d/passes/pick.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -8,7 +8,6 @@ import { PickingId } from '../../mol-geo/geometry/picking'; import { PickType, Renderer } from '../../mol-gl/renderer'; import { Scene } from '../../mol-gl/scene'; import { WebGLContext } from '../../mol-gl/webgl/context'; -import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item'; import { RenderTarget } from '../../mol-gl/webgl/render-target'; import { Vec3 } from '../../mol-math/linear-algebra'; import { spiral2d } from '../../mol-math/misc'; @@ -64,7 +63,7 @@ export class PickPass { } } - private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant, pickType: number) { + private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) { const depth = this.drawPass.depthTexturePrimitives; renderer.clear(false); diff --git a/src/mol-canvas3d/passes/postprocessing.ts b/src/mol-canvas3d/passes/postprocessing.ts index bf8c53905ee778e46c17fd2e8fa909f3f974e7c2..437c802dbb46f959858d62fdc0da8f1c6d67052b 100644 --- a/src/mol-canvas3d/passes/postprocessing.ts +++ b/src/mol-canvas3d/passes/postprocessing.ts @@ -30,7 +30,8 @@ import { SmaaParams, SmaaPass } from './smaa'; const OutlinesSchema = { ...QuadSchema, - tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), + tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), + tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), uTexSize: UniformSpec('v2'), dOrthographic: DefineSpec('number'), @@ -41,11 +42,15 @@ const OutlinesSchema = { }; type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>> -function getOutlinesRenderable(ctx: WebGLContext, depthTexture: Texture): OutlinesRenderable { +function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, depthTextureTransparent: Texture): OutlinesRenderable { + const width = depthTextureOpaque.getWidth(); + const height = depthTextureOpaque.getHeight(); + const values: Values<typeof OutlinesSchema> = { ...QuadValues, - tDepth: ValueCell.create(depthTexture), - uTexSize: ValueCell.create(Vec2.create(depthTexture.getWidth(), depthTexture.getHeight())), + tDepthOpaque: ValueCell.create(depthTextureOpaque), + tDepthTransparent: ValueCell.create(depthTextureTransparent), + uTexSize: ValueCell.create(Vec2.create(width, height)), dOrthographic: ValueCell.create(0), uNear: ValueCell.create(1), @@ -183,7 +188,8 @@ const PostprocessingSchema = { ...QuadSchema, tSsaoDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), - tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), + tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), + tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), tOutlines: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'), uTexSize: UniformSpec('v2'), @@ -207,12 +213,13 @@ const PostprocessingSchema = { }; type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>> -function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable { +function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable { const values: Values<typeof PostprocessingSchema> = { ...QuadValues, tSsaoDepth: ValueCell.create(ssaoDepthTexture), tColor: ValueCell.create(colorTexture), - tDepth: ValueCell.create(depthTexture), + tDepthOpaque: ValueCell.create(depthTextureOpaque), + tDepthTransparent: ValueCell.create(depthTextureTransparent), tOutlines: ValueCell.create(outlinesTexture), uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())), @@ -274,6 +281,10 @@ export class PostprocessingPass { return props.occlusion.name === 'on' || props.outline.name === 'on'; } + static isOutlineEnabled(props: PostprocessingProps) { + return props.outline.name === 'on'; + } + readonly target: RenderTarget; private readonly outlinesTarget: RenderTarget; @@ -307,7 +318,7 @@ export class PostprocessingPass { } constructor(private webgl: WebGLContext, private drawPass: DrawPass) { - const { colorTarget, depthTexture } = drawPass; + const { colorTarget, depthTexture: depthTextureTransparent, depthTexturePrimitives: depthTextureOpaque } = drawPass; const width = colorTarget.getWidth(); const height = colorTarget.getHeight(); @@ -320,7 +331,7 @@ export class PostprocessingPass { this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear'); this.outlinesTarget = webgl.createRenderTarget(width, height, false); - this.outlinesRenderable = getOutlinesRenderable(webgl, depthTexture); + this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent); this.randomHemisphereVector = []; for (let i = 0; i < 256; i++) { @@ -340,7 +351,7 @@ export class PostprocessingPass { const sh = Math.floor(height * this.ssaoScale); this.downsampledDepthTarget = webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear'); - this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTexture); + this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTextureOpaque); this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear'); this.ssaoDepthTexture.define(sw, sh); @@ -352,10 +363,10 @@ export class PostprocessingPass { this.ssaoDepthTexture.attachFramebuffer(this.ssaoBlurSecondPassFramebuffer, 'color0'); - this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTexture : this.downsampledDepthTarget.texture); + this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture); this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal'); this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical'); - this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, this.outlinesTarget.texture, this.ssaoDepthTexture); + this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture); } setSize(width: number, height: number) { diff --git a/src/mol-geo/geometry/cylinders/cylinders.ts b/src/mol-geo/geometry/cylinders/cylinders.ts index 958331cdd005e68a0862b0c2875ef209a651e61c..341bc72fcec7344096c4bed5d31f318089315557 100644 --- a/src/mol-geo/geometry/cylinders/cylinders.ts +++ b/src/mol-geo/geometry/cylinders/cylinders.ts @@ -242,7 +242,7 @@ export namespace Cylinders { uDoubleSided: ValueCell.create(props.doubleSided), dIgnoreLight: ValueCell.create(props.ignoreLight), dXrayShaded: ValueCell.create(props.xrayShaded), - dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'), + dTransparentBackfaces: ValueCell.create(props.transparentBackfaces), uBumpFrequency: ValueCell.create(props.bumpFrequency), uBumpAmplitude: ValueCell.create(props.bumpAmplitude), }; @@ -260,7 +260,7 @@ export namespace Cylinders { ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided); ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight); ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded); - ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque'); + ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces); ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency); ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude); } diff --git a/src/mol-geo/geometry/mesh/mesh.ts b/src/mol-geo/geometry/mesh/mesh.ts index 04f7be3545df5d67b8aa67c487833d7e55c6351d..20daa221d1d228c6c79cd757de1948fffc6041de 100644 --- a/src/mol-geo/geometry/mesh/mesh.ts +++ b/src/mol-geo/geometry/mesh/mesh.ts @@ -701,7 +701,7 @@ export namespace Mesh { dFlipSided: ValueCell.create(props.flipSided), dIgnoreLight: ValueCell.create(props.ignoreLight), dXrayShaded: ValueCell.create(props.xrayShaded), - dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'), + dTransparentBackfaces: ValueCell.create(props.transparentBackfaces), uBumpFrequency: ValueCell.create(props.bumpFrequency), uBumpAmplitude: ValueCell.create(props.bumpAmplitude), @@ -722,7 +722,7 @@ export namespace Mesh { ValueCell.updateIfChanged(values.dFlipSided, props.flipSided); ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight); ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded); - ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque'); + ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces); ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency); ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude); } diff --git a/src/mol-geo/geometry/spheres/spheres.ts b/src/mol-geo/geometry/spheres/spheres.ts index bd8dfdff2f3fc3147587d4391340e3b8dfd6c3d4..7b442ca910a94e760bf470321fb404d4a65573f0 100644 --- a/src/mol-geo/geometry/spheres/spheres.ts +++ b/src/mol-geo/geometry/spheres/spheres.ts @@ -209,7 +209,7 @@ export namespace Spheres { uDoubleSided: ValueCell.create(props.doubleSided), dIgnoreLight: ValueCell.create(props.ignoreLight), dXrayShaded: ValueCell.create(props.xrayShaded), - dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'), + dTransparentBackfaces: ValueCell.create(props.transparentBackfaces), uBumpFrequency: ValueCell.create(props.bumpFrequency), uBumpAmplitude: ValueCell.create(props.bumpAmplitude), }; @@ -227,7 +227,7 @@ export namespace Spheres { ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided); ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight); ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded); - ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque'); + ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces); ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency); ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude); } diff --git a/src/mol-geo/geometry/texture-mesh/texture-mesh.ts b/src/mol-geo/geometry/texture-mesh/texture-mesh.ts index 19975cdd574dc060b29a7e124bd653517baac668..ff2d989b517b6846e1c0f760d266a8d8c17c545d 100644 --- a/src/mol-geo/geometry/texture-mesh/texture-mesh.ts +++ b/src/mol-geo/geometry/texture-mesh/texture-mesh.ts @@ -174,7 +174,7 @@ export namespace TextureMesh { dFlipSided: ValueCell.create(props.flipSided), dIgnoreLight: ValueCell.create(props.ignoreLight), dXrayShaded: ValueCell.create(props.xrayShaded), - dOpaqueBackfaces: ValueCell.create(props.transparentBackfaces === 'opaque'), + dTransparentBackfaces: ValueCell.create(props.transparentBackfaces), uBumpFrequency: ValueCell.create(props.bumpFrequency), uBumpAmplitude: ValueCell.create(props.bumpAmplitude), @@ -195,7 +195,7 @@ export namespace TextureMesh { ValueCell.updateIfChanged(values.dFlipSided, props.flipSided); ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight); ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded); - ValueCell.updateIfChanged(values.dOpaqueBackfaces, props.transparentBackfaces === 'opaque'); + ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces); ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency); ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude); } diff --git a/src/mol-geo/geometry/transparency-data.ts b/src/mol-geo/geometry/transparency-data.ts index 4c4f94f568b688cc141d04513400eda71dad68ee..fb9df06819e17d041d0a45298d6ba15a0f836822 100644 --- a/src/mol-geo/geometry/transparency-data.ts +++ b/src/mol-geo/geometry/transparency-data.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -29,6 +29,7 @@ export function applyTransparencyValue(array: Uint8Array, start: number, end: nu } export function getTransparencyAverage(array: Uint8Array, count: number): number { + if (count === 0 || array.length < count) return 0; let sum = 0; for (let i = 0; i < count; ++i) { sum += array[i]; diff --git a/src/mol-gl/renderable/cylinders.ts b/src/mol-gl/renderable/cylinders.ts index 8fd4c8222585f538b5e8272287330a67d77330ea..5366aaae108f4f98d631edb538c53b9b8151cd45 100644 --- a/src/mol-gl/renderable/cylinders.ts +++ b/src/mol-gl/renderable/cylinders.ts @@ -26,7 +26,7 @@ export const CylindersSchema = { uDoubleSided: UniformSpec('b'), dIgnoreLight: DefineSpec('boolean'), dXrayShaded: DefineSpec('boolean'), - dOpaqueBackfaces: DefineSpec('boolean'), + dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']), uBumpFrequency: UniformSpec('f'), uBumpAmplitude: UniformSpec('f'), }; diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index e59e1a7e0cbd53de8f0c641cfc30301e38850353..c3da8f71d52720e5123d31f40419a0506946e241 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/src/mol-gl/renderable/mesh.ts @@ -22,7 +22,7 @@ export const MeshSchema = { dFlipSided: DefineSpec('boolean'), dIgnoreLight: DefineSpec('boolean'), dXrayShaded: DefineSpec('boolean'), - dOpaqueBackfaces: DefineSpec('boolean'), + dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']), uBumpFrequency: UniformSpec('f'), uBumpAmplitude: UniformSpec('f'), meta: ValueSpec('unknown') diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index f97c76b721e0e4dd23817c931f0b3633976f4b30..9881dc80730278dcfc5ec501df26c77362135e99 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/src/mol-gl/renderable/schema.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> */ @@ -153,7 +153,7 @@ export const GlobalUniformSchema = { uXrayEdgeFalloff: UniformSpec('f'), - uRenderWboit: UniformSpec('b'), + uRenderMask: UniformSpec('i'), uMarkingDepthTest: UniformSpec('b'), uMarkingType: UniformSpec('i'), uPickType: UniformSpec('i'), diff --git a/src/mol-gl/renderable/spheres.ts b/src/mol-gl/renderable/spheres.ts index 8008f55f5bbe2db4f7430c3d2f8977b9e6ebffeb..60e316d81fefdb9d947c5dd961c451708b616a14 100644 --- a/src/mol-gl/renderable/spheres.ts +++ b/src/mol-gl/renderable/spheres.ts @@ -23,7 +23,7 @@ export const SpheresSchema = { uDoubleSided: UniformSpec('b'), dIgnoreLight: DefineSpec('boolean'), dXrayShaded: DefineSpec('boolean'), - dOpaqueBackfaces: DefineSpec('boolean'), + dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']), uBumpFrequency: UniformSpec('f'), uBumpAmplitude: UniformSpec('f'), }; diff --git a/src/mol-gl/renderable/texture-mesh.ts b/src/mol-gl/renderable/texture-mesh.ts index 285e4aab24415685cb86a5db32ed64cea6aea093..e997a9bb7c5c0e390d77f6e80d77d730c376533a 100644 --- a/src/mol-gl/renderable/texture-mesh.ts +++ b/src/mol-gl/renderable/texture-mesh.ts @@ -23,7 +23,7 @@ export const TextureMeshSchema = { dFlipSided: DefineSpec('boolean'), dIgnoreLight: DefineSpec('boolean'), dXrayShaded: DefineSpec('boolean'), - dOpaqueBackfaces: DefineSpec('boolean'), + dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']), uBumpFrequency: UniformSpec('f'), uBumpAmplitude: UniformSpec('f'), meta: ValueSpec('unknown') diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index a446c2f586fca4236680b15fec212c2942f7de8b..abc4ca7c89a516a73f6dec363d023f2fda370987 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -54,18 +54,19 @@ interface Renderer { readonly props: Readonly<RendererProps> clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void - clearDepth: () => void + clearDepth: (packed?: boolean) => void update: (camera: ICamera) => void - renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => void + renderPick: (group: Scene.Group, camera: ICamera, variant: 'pick' | 'depth', depthTexture: Texture | null, pickType: PickType) => void renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void + renderDepthOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void + renderDepthTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderBlended: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderBlendedOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderBlendedTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void - renderBlendedVolumeOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void - renderBlendedVolumeTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void + renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void @@ -141,6 +142,12 @@ namespace Renderer { BlendedBack = 2 } + const enum Mask { + All = 0, + Opaque = 1, + Transparent = 2, + } + export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer { const { gl, state, stats } = ctx; const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props); @@ -152,7 +159,9 @@ namespace Renderer { let transparentBackground = false; - const emptyDepthTexture = ctx.resources.texture('image-depth', 'depth', 'ushort', 'nearest'); + const emptyDepthTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest'); + emptyDepthTexture.define(1, 1); + emptyDepthTexture.load({ array: new Uint8Array([255, 255, 255, 255]), width: 1, height: 1 }); const sharedTexturesList: Textures = [ ['tDepth', emptyDepthTexture] ]; @@ -197,7 +206,7 @@ namespace Renderer { uFogFar: ValueCell.create(10000), uFogColor: ValueCell.create(bgColor), - uRenderWboit: ValueCell.create(false), + uRenderMask: ValueCell.create(0), uMarkingDepthTest: ValueCell.create(false), uPickType: ValueCell.create(PickType.None), uMarkingType: ValueCell.create(MarkingType.None), @@ -327,7 +336,7 @@ namespace Renderer { ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground); }; - const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderWboit: boolean, markingDepthTest: boolean) => { + const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderMask: Mask, markingDepthTest: boolean) => { arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || emptyDepthTexture); ValueCell.update(globalUniforms.uModel, group.view); @@ -336,7 +345,7 @@ namespace Renderer { ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection)); ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection)); - ValueCell.updateIfChanged(globalUniforms.uRenderWboit, renderWboit); + ValueCell.updateIfChanged(globalUniforms.uRenderMask, renderMask); ValueCell.updateIfChanged(globalUniforms.uMarkingDepthTest, markingDepthTest); state.enable(gl.SCISSOR_TEST); @@ -355,7 +364,7 @@ namespace Renderer { state.enable(gl.DEPTH_TEST); state.depthMask(true); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.All, false); ValueCell.updateIfChanged(globalUniforms.uPickType, pickType); const { renderables } = group; @@ -371,7 +380,7 @@ namespace Renderer { state.enable(gl.DEPTH_TEST); state.depthMask(true); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.All, false); const { renderables } = group; for (let i = 0, il = renderables.length; i < il; ++i) { @@ -379,12 +388,44 @@ namespace Renderer { } }; + const renderDepthOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { + state.disable(gl.BLEND); + state.enable(gl.DEPTH_TEST); + state.depthMask(true); + + updateInternal(group, camera, depthTexture, Mask.Opaque, false); + + const { renderables } = group; + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (r.state.opaque && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) { + renderObject(r, 'depth', Flag.None); + } + } + }; + + const renderDepthTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { + state.disable(gl.BLEND); + state.enable(gl.DEPTH_TEST); + state.depthMask(true); + + updateInternal(group, camera, depthTexture, Mask.Transparent, false); + + const { renderables } = group; + for (let i = 0, il = renderables.length; i < il; ++i) { + const r = renderables[i]; + if (!r.state.opaque || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) { + renderObject(r, 'depth', Flag.None); + } + } + }; + const renderMarkingDepth = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { state.disable(gl.BLEND); state.enable(gl.DEPTH_TEST); state.depthMask(true); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.All, false); ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Depth); const { renderables } = group; @@ -402,7 +443,7 @@ namespace Renderer { state.enable(gl.DEPTH_TEST); state.depthMask(true); - updateInternal(group, camera, depthTexture, false, !!depthTexture); + updateInternal(group, camera, depthTexture, Mask.All, !!depthTexture); ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Mask); const { renderables } = group; @@ -425,14 +466,14 @@ namespace Renderer { state.enable(gl.DEPTH_TEST); state.depthMask(true); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.Opaque, false); const { renderables } = group; for (let i = 0, il = renderables.length; i < il; ++i) { const r = renderables[i]; if (r.state.opaque) { renderObject(r, 'colorBlended', Flag.None); - } else if (r.values.uDoubleSided?.ref.value && r.values.dOpaqueBackfaces?.ref.value) { + } else if (r.values.uDoubleSided?.ref.value && r.values.dTransparentBackfaces?.ref.value === 'opaque') { renderObject(r, 'colorBlended', Flag.BlendedBack); } } @@ -441,7 +482,7 @@ namespace Renderer { const renderBlendedTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { state.enable(gl.DEPTH_TEST); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.Transparent, false); const { renderables } = group; @@ -463,10 +504,10 @@ namespace Renderer { state.depthMask(false); for (let i = 0, il = renderables.length; i < il; ++i) { const r = renderables[i]; - if (!r.state.opaque && !r.state.writeDepth) { + if ((!r.state.opaque && !r.state.writeDepth) || r.values.transparencyAverage.ref.value > 0) { if (r.values.uDoubleSided?.ref.value) { // render frontfaces and backfaces separately to avoid artefacts - if (!r.values.dOpaqueBackfaces?.ref.value) { + if (r.values.dTransparentBackfaces?.ref.value !== 'opaque') { renderObject(r, 'colorBlended', Flag.BlendedBack); } renderObject(r, 'colorBlended', Flag.BlendedFront); @@ -477,39 +518,16 @@ namespace Renderer { } }; - const renderBlendedVolumeOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { + const renderBlendedVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); state.enable(gl.BLEND); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.Transparent, false); const { renderables } = group; for (let i = 0, il = renderables.length; i < il; ++i) { const r = renderables[i]; - - // TODO: simplify, handle in renderable.state??? - // uAlpha is updated in "render" so we need to recompute it here - const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1); - if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) { - renderObject(r, 'colorBlended', Flag.None); - } - } - }; - - const renderBlendedVolumeTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { - state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - state.enable(gl.BLEND); - - updateInternal(group, camera, depthTexture, false, false); - - const { renderables } = group; - for (let i = 0, il = renderables.length; i < il; ++i) { - const r = renderables[i]; - - // TODO: simplify, handle in renderable.state??? - // uAlpha is updated in "render" so we need to recompute it here - const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1); - if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) { + if (r.values.dGeometryType.ref.value === 'directVolume') { renderObject(r, 'colorBlended', Flag.None); } } @@ -520,7 +538,7 @@ namespace Renderer { state.enable(gl.DEPTH_TEST); state.depthMask(true); - updateInternal(group, camera, depthTexture, false, false); + updateInternal(group, camera, depthTexture, Mask.Opaque, false); const { renderables } = group; for (let i = 0, il = renderables.length; i < il; ++i) { @@ -529,14 +547,14 @@ namespace Renderer { // TODO: simplify, handle in renderable.state??? // uAlpha is updated in "render" so we need to recompute it here const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1); - if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dOpaqueBackfaces?.ref.value) { + if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dTransparentBackfaces?.ref.value === 'opaque') { renderObject(r, 'colorWboit', Flag.None); } } }; const renderWboitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => { - updateInternal(group, camera, depthTexture, true, false); + updateInternal(group, camera, depthTexture, Mask.Transparent, false); const { renderables } = group; for (let i = 0, il = renderables.length; i < il; ++i) { @@ -567,23 +585,31 @@ namespace Renderer { } gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); }, - clearDepth: () => { + clearDepth: (packed = false) => { state.enable(gl.SCISSOR_TEST); - state.enable(gl.DEPTH_TEST); - state.depthMask(true); - gl.clear(gl.DEPTH_BUFFER_BIT); + + if (packed) { + state.colorMask(true, true, true, true); + state.clearColor(1, 1, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + } else { + state.enable(gl.DEPTH_TEST); + state.depthMask(true); + gl.clear(gl.DEPTH_BUFFER_BIT); + } }, update, renderPick, renderDepth, + renderDepthOpaque, + renderDepthTransparent, renderMarkingDepth, renderMarkingMask, renderBlended, renderBlendedOpaque, renderBlendedTransparent, - renderBlendedVolumeOpaque, - renderBlendedVolumeTransparent, + renderBlendedVolume, renderWboitOpaque, renderWboitTransparent, diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 7cc2f3c7cd1ac9fc4c2860852370adaa9cf469a0..ba1349f162097e36d1d6c70d6aca24795d9055c6 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -16,6 +16,7 @@ import { BoundaryHelper } from '../mol-math/geometry/boundary-helper'; import { hash1 } from '../mol-data/util'; import { GraphicsRenderable } from './renderable'; import { GraphicsRenderVariants } from './webgl/render-item'; +import { clamp } from '../mol-math/interpolate'; const boundaryHelper = new BoundaryHelper('98'); @@ -80,6 +81,7 @@ interface Scene extends Object3D { clear: () => void forEach: (callbackFn: (value: GraphicsRenderable, key: GraphicsRenderObject) => void) => void getMarkerAverage: () => number + getOpacityAverage: () => number } namespace Scene { @@ -247,14 +249,35 @@ namespace Scene { }, getMarkerAverage() { if (primitives.length === 0 && volumes.length === 0) return 0; + let count = 0; let markerAverage = 0; for (let i = 0, il = primitives.length; i < il; ++i) { + if (!primitives[i].state.visible) continue; markerAverage += primitives[i].values.markerAverage.ref.value; + count += 1; } for (let i = 0, il = volumes.length; i < il; ++i) { + if (!volumes[i].state.visible) continue; markerAverage += volumes[i].values.markerAverage.ref.value; + count += 1; } - return markerAverage / (primitives.length + volumes.length); + return count > 0 ? markerAverage / count : 0; + }, + getOpacityAverage() { + if (primitives.length === 0) return 0; + let count = 0; + let opacityAverage = 0; + for (let i = 0, il = primitives.length; i < il; ++i) { + const p = primitives[i]; + if (!p.state.visible) continue; + + // TODO: simplify, handle in renderable.state??? + // uAlpha is updated in "render" so we need to recompute it here + const alpha = clamp(p.values.alpha.ref.value * p.state.alphaFactor, 0, 1); + opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha; + count += 1; + } + return count > 0 ? opacityAverage / count : 0; }, }; } diff --git a/src/mol-gl/shader/chunks/apply-interior-color.glsl.ts b/src/mol-gl/shader/chunks/apply-interior-color.glsl.ts index e8538506040951ef163176f80ffa3a1e9c294c72..0927e579daa0409a7ae8ad7ed5ca2172e9c41aaf 100644 --- a/src/mol-gl/shader/chunks/apply-interior-color.glsl.ts +++ b/src/mol-gl/shader/chunks/apply-interior-color.glsl.ts @@ -6,7 +6,7 @@ if (interior) { gl_FragColor.rgb *= 1.0 - uInteriorDarkening; } - #ifdef dOpaqueBackfaces + #ifdef dTransparentBackfaces_opaque gl_FragColor.a = 1.0; #endif } 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 f8455431e94650e1d29c43c37cb9a86de216e5f2..f7cab1043a1ed9f39eb956378aaad2c3ce80d7df 100644 --- a/src/mol-gl/shader/chunks/assign-material-color.glsl.ts +++ b/src/mol-gl/shader/chunks/assign-material-color.glsl.ts @@ -31,11 +31,34 @@ export const assign_material_color = ` #elif defined(dRenderVariant_pick) vec4 material = vColor; #elif defined(dRenderVariant_depth) - #ifdef enabledFragDepth - vec4 material = packDepthToRGBA(gl_FragDepthEXT); + if (fragmentDepth > getDepth(gl_FragCoord.xy / uDrawingBufferSize)) { + discard; + } + + #ifndef dXrayShaded + #if defined(dTransparency) + float dta = 1.0 - vTransparency; + if (vTransparency < 0.2) dta = 1.0; // hard cutoff looks better + + if (uRenderMask == MaskTransparent && uAlpha * dta == 1.0) { + discard; + } else if (uRenderMask == MaskOpaque && uAlpha * dta < 1.0) { + discard; + } + #else + if (uRenderMask == MaskTransparent && uAlpha == 1.0) { + discard; + } else if (uRenderMask == MaskOpaque && uAlpha < 1.0) { + discard; + } + #endif #else - vec4 material = packDepthToRGBA(gl_FragCoord.z); + if (uRenderMask == MaskOpaque) { + discard; + } #endif + + vec4 material = packDepthToRGBA(fragmentDepth); #elif defined(dRenderVariant_marking) vec4 material; if(uMarkingType == 1) { @@ -51,7 +74,7 @@ export const assign_material_color = ` discard; float depthTest = 1.0; if (uMarkingDepthTest) { - depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0; + depthTest = (fragmentDepth >= getDepthPacked(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0; } bool isHighlight = intMod(marker, 2.0) > 0.1; float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar); @@ -62,47 +85,41 @@ export const assign_material_color = ` } #endif -// apply screendoor transparency -#if defined(dTransparency) +// apply per-group transparency +#if defined(dTransparency) && (defined(dRenderVariant_pick) || defined(dRenderVariant_color)) float ta = 1.0 - vTransparency; - #if defined(dRenderVariant_colorWboit) - if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better with wboit - #endif + if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better #if defined(dRenderVariant_pick) if (ta < uPickingAlphaThreshold) discard; // ignore so the element below can be picked - #else - #if defined(dRenderVariant_colorBlended) - float at = 0.0; + #elif defined(dRenderVariant_color) + material.a *= ta; - // shift by view-offset during multi-sample rendering to allow for blending - vec2 coord = gl_FragCoord.xy + uViewOffset * 0.25; - - const mat4 thresholdMatrix = mat4( - 1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0, - 13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0, - 4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0, - 16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0 - ); - int ci = int(intMod(coord.x, 4.0)); - int ri = int(intMod(coord.y, 4.0)); - #if __VERSION__ == 100 - vec4 i = vec4(float(ci * 4 + ri)); - vec4 v = thresholdMatrix[0] * vec4(equal(i, vec4(0.0, 1.0, 2.0, 3.0))) + - thresholdMatrix[1] * vec4(equal(i, vec4(4.0, 5.0, 6.0, 7.0))) + - thresholdMatrix[2] * vec4(equal(i, vec4(8.0, 9.0, 10.0, 11.0))) + - thresholdMatrix[3] * vec4(equal(i, vec4(12.0, 13.0, 14.0, 15.0))); - at = v.x + v.y + v.z + v.w; - #else - at = thresholdMatrix[ci][ri]; + #if defined(dRenderVariant_colorBlended) + #if defined(dTransparentBackfaces_off) + if ((uRenderMask == MaskOpaque && material.a < 1.0) || + (uRenderMask == MaskTransparent && material.a == 1.0) || + (interior && material.a < 1.0) + ) { + discard; + } + #elif defined(dTransparentBackfaces_on) + if ((uRenderMask == MaskOpaque && material.a < 1.0) || + (uRenderMask == MaskTransparent && material.a == 1.0) + ) { + discard; + } + #elif defined(dTransparentBackfaces_opaque) + if (interior) { + material.a = 1.0; + } else if ( + (uRenderMask == MaskOpaque && material.a < 1.0) || + (uRenderMask == MaskTransparent && material.a == 1.0) + ) { + discard; + } #endif - - if (ta < 0.99 && (ta < 0.01 || ta < at)) { - discard; - } - #elif defined(dRenderVariant_colorWboit) - material.a *= ta; #endif #endif #endif diff --git a/src/mol-gl/shader/chunks/check-picking-alpha.glsl.ts b/src/mol-gl/shader/chunks/check-picking-alpha.glsl.ts index 5611e9cc3695456f1307177d405de42e715cc751..c2b5e5ae4b7995e11509e590b75de4851cebae40 100644 --- a/src/mol-gl/shader/chunks/check-picking-alpha.glsl.ts +++ b/src/mol-gl/shader/chunks/check-picking-alpha.glsl.ts @@ -4,7 +4,7 @@ float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ)); float alpha = (1.0 - fogFactor) * uAlpha; // if not opaque enough ignore so the element below can be picked if (uAlpha < uPickingAlphaThreshold || alpha < 0.1) { - #ifdef dOpaqueBackfaces + #ifdef dTransparentBackfaces_opaque if (!interior) discard; #else discard; 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 be8b67c4b00548daf211b447027dc62a646ca810..877d8663619268257dc392788c63f6beb539a102 100644 --- a/src/mol-gl/shader/chunks/common-frag-params.glsl.ts +++ b/src/mol-gl/shader/chunks/common-frag-params.glsl.ts @@ -64,17 +64,24 @@ uniform float uXrayEdgeFalloff; uniform mat4 uProjection; -uniform bool uRenderWboit; +uniform int uRenderMask; uniform bool uMarkingDepthTest; uniform sampler2D tDepth; uniform vec2 uDrawingBufferSize; -float getDepth(const in vec2 coords) { - // always packed due to merged depth from primitives and volumes +float getDepthPacked(const in vec2 coords) { return unpackRGBAToDepth(texture2D(tDepth, coords)); } +float getDepth(const in vec2 coords) { + #ifdef depthTextureSupport + return texture2D(tDepth, coords).r; + #else + return unpackRGBAToDepth(texture2D(tDepth, coords)); + #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; diff --git a/src/mol-gl/shader/chunks/common.glsl.ts b/src/mol-gl/shader/chunks/common.glsl.ts index 0f90900065d0c82de7b94f358b72f2b4fae63027..785decd348bc5782b60b354ff47f474d107b1081 100644 --- a/src/mol-gl/shader/chunks/common.glsl.ts +++ b/src/mol-gl/shader/chunks/common.glsl.ts @@ -17,6 +17,10 @@ export const common = ` #define dColorType_varying #endif +#define MaskAll 0 +#define MaskOpaque 1 +#define MaskTransparent 2 + // #define PI 3.14159265 diff --git a/src/mol-gl/shader/chunks/wboit-write.glsl.ts b/src/mol-gl/shader/chunks/wboit-write.glsl.ts index b2d85796e940c4dd83421adf176d69175601172f..9e714aef7646fdb59699f2541718818d4254b9af 100644 --- a/src/mol-gl/shader/chunks/wboit-write.glsl.ts +++ b/src/mol-gl/shader/chunks/wboit-write.glsl.ts @@ -7,13 +7,16 @@ export const wboit_write = ` #if defined(dRenderVariant_colorWboit) - if (!uRenderWboit) { + if (uRenderMask == MaskOpaque) { if (preFogAlpha < 1.0) { discard; } - } else if (uRenderWboit) { + } else if (uRenderMask == MaskTransparent) { // the 'fragmentDepth > 0.99' check is to handle precision issues with packed depth if (preFogAlpha != 1.0 && (fragmentDepth < getDepth(gl_FragCoord.xy / uDrawingBufferSize) || fragmentDepth > 0.99)) { + #ifdef dTransparentBackfaces_off + if (interior) discard; + #endif float alpha = gl_FragColor.a; float wboitWeight = alpha * clamp(pow(1.0 - fragmentDepth, 2.0), 0.01, 1.0); gl_FragColor = vec4(gl_FragColor.rgb * alpha * wboitWeight, alpha); diff --git a/src/mol-gl/shader/direct-volume.frag.ts b/src/mol-gl/shader/direct-volume.frag.ts index 02c5ba5a670f3003add96b6032b1cef58b7305e1..b931de8410da6bc9a656332e0b2a09378b9beae7 100644 --- a/src/mol-gl/shader/direct-volume.frag.ts +++ b/src/mol-gl/shader/direct-volume.frag.ts @@ -72,7 +72,7 @@ uniform float uPickingAlphaThreshold; uniform bool uTransparentBackground; uniform float uXrayEdgeFalloff; -uniform bool uRenderWboit; +uniform int uRenderMask; uniform float uNear; uniform float uFar; @@ -141,12 +141,7 @@ float transferFunction(float value) { float getDepth(const in vec2 coords) { #ifdef depthTextureSupport - if (!uRenderWboit) { - // in case of opaque volumes (and depth texture support) - return texture2D(tDepth, coords).r; - } else { - return unpackRGBAToDepth(texture2D(tDepth, coords)); - } + return texture2D(tDepth, coords).r; #else return unpackRGBAToDepth(texture2D(tDepth, coords)); #endif @@ -289,20 +284,24 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) { material.rgb = mix(material.rgb, overpaint.rgb, overpaint.a); #endif - if (material.a >= 0.01) { - #ifdef dPackedGroup - // compute gradient by central differences - gradient.x = textureVal(unitPos - dx).a - textureVal(unitPos + dx).a; - gradient.y = textureVal(unitPos - dy).a - textureVal(unitPos + dy).a; - gradient.z = textureVal(unitPos - dz).a - textureVal(unitPos + dz).a; - #else - gradient = cell.xyz * 2.0 - 1.0; - #endif - vec3 normal = -normalize(normalMatrix * normalize(gradient)); - #include apply_light_color - } else { + #ifdef dIgnoreLight gl_FragColor.rgb = material.rgb; - } + #else + if (material.a >= 0.01) { + #ifdef dPackedGroup + // compute gradient by central differences + gradient.x = textureVal(unitPos - dx).a - textureVal(unitPos + dx).a; + gradient.y = textureVal(unitPos - dy).a - textureVal(unitPos + dy).a; + gradient.z = textureVal(unitPos - dz).a - textureVal(unitPos + dz).a; + #else + gradient = cell.xyz * 2.0 - 1.0; + #endif + vec3 normal = -normalize(normalMatrix * normalize(gradient)); + #include apply_light_color + } else { + gl_FragColor.rgb = material.rgb; + } + #endif gl_FragColor.a = material.a * uAlpha * uTransferScale; diff --git a/src/mol-gl/shader/image.frag.ts b/src/mol-gl/shader/image.frag.ts index 354eb8f8e6a4ec6813b3e5aa1761240f983ccaa6..31711e8654f9d2615d1446ac66801477370e1d8e 100644 --- a/src/mol-gl/shader/image.frag.ts +++ b/src/mol-gl/shader/image.frag.ts @@ -130,7 +130,7 @@ void main() { discard; float depthTest = 1.0; if (uMarkingDepthTest) { - depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0; + depthTest = (fragmentDepth >= getDepthPacked(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0; } bool isHighlight = intMod(marker, 2.0) > 0.1; gl_FragColor = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0); diff --git a/src/mol-gl/shader/outlines.frag.ts b/src/mol-gl/shader/outlines.frag.ts index df7c96b44d049d7fa50b0c6289ed7ad8c5100595..e2cb6ec226ee07fca037ae216842dd26da0289af 100644 --- a/src/mol-gl/shader/outlines.frag.ts +++ b/src/mol-gl/shader/outlines.frag.ts @@ -1,7 +1,8 @@ /** - * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz> + @author Alexander Rose <alexander.rose@weirdbyte.de> */ export const outlines_frag = ` @@ -9,7 +10,8 @@ precision highp float; precision highp int; precision highp sampler2D; -uniform sampler2D tDepth; +uniform sampler2D tDepthOpaque; +uniform sampler2D tDepthTransparent; uniform vec2 uTexSize; uniform float uNear; @@ -27,8 +29,16 @@ float getViewZ(const in float depth) { #endif } -float getDepth(const in vec2 coords) { - return unpackRGBAToDepth(texture2D(tDepth, coords)); +float getDepthOpaque(const in vec2 coords) { + #ifdef depthTextureSupport + return texture2D(tDepthOpaque, coords).r; + #else + return unpackRGBAToDepth(texture2D(tDepthOpaque, coords)); + #endif +} + +float getDepthTransparent(const in vec2 coords) { + return unpackRGBAToDepth(texture2D(tDepthTransparent, coords)); } bool isBackground(const in float depth) { @@ -41,8 +51,11 @@ void main(void) { vec2 coords = gl_FragCoord.xy / uTexSize; vec2 invTexSize = 1.0 / uTexSize; - float selfDepth = getDepth(coords); - float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(getDepth(coords)); + float selfDepthOpaque = getDepthOpaque(coords); + float selfViewZOpaque = isBackground(selfDepthOpaque) ? backgroundViewZ : getViewZ(selfDepthOpaque); + + float selfDepthTransparent = getDepthTransparent(coords); + float selfViewZTransparent = isBackground(selfDepthTransparent) ? backgroundViewZ : getViewZ(selfDepthTransparent); float outline = 1.0; float bestDepth = 1.0; @@ -50,12 +63,22 @@ void main(void) { for (int y = -1; y <= 1; y++) { for (int x = -1; x <= 1; x++) { vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize; - float sampleDepth = getDepth(sampleCoords); - float sampleViewZ = isBackground(sampleDepth) ? backgroundViewZ : getViewZ(sampleDepth); - if (abs(selfViewZ - sampleViewZ) > uMaxPossibleViewZDiff && selfDepth > sampleDepth && sampleDepth <= bestDepth) { + float sampleDepthOpaque = getDepthOpaque(sampleCoords); + float sampleDepthTransparent = getDepthTransparent(sampleCoords); + + float sampleViewZOpaque = isBackground(sampleDepthOpaque) ? backgroundViewZ : getViewZ(sampleDepthOpaque); + if (abs(selfViewZOpaque - sampleViewZOpaque) > uMaxPossibleViewZDiff && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestDepth) { outline = 0.0; - bestDepth = sampleDepth; + bestDepth = sampleDepthOpaque; + } + + if (sampleDepthTransparent < sampleDepthOpaque) { + float sampleViewZTransparent = isBackground(sampleDepthTransparent) ? backgroundViewZ : getViewZ(sampleDepthTransparent); + if (abs(selfViewZTransparent - sampleViewZTransparent) > uMaxPossibleViewZDiff && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) { + outline = 0.0; + bestDepth = sampleDepthTransparent; + } } } } diff --git a/src/mol-gl/shader/postprocessing.frag.ts b/src/mol-gl/shader/postprocessing.frag.ts index aefe55452a3007003fbb9f0ffbdc5998fd854b49..df1acfad5791cdea0c7aed615751455ebd1cb7c8 100644 --- a/src/mol-gl/shader/postprocessing.frag.ts +++ b/src/mol-gl/shader/postprocessing.frag.ts @@ -12,7 +12,8 @@ precision highp sampler2D; uniform sampler2D tSsaoDepth; uniform sampler2D tColor; -uniform sampler2D tDepth; +uniform sampler2D tDepthOpaque; +uniform sampler2D tDepthTransparent; uniform sampler2D tOutlines; uniform vec2 uTexSize; @@ -40,19 +41,27 @@ float getViewZ(const in float depth) { #endif } -float getDepth(const in vec2 coords) { - return unpackRGBAToDepth(texture2D(tDepth, coords)); +float getDepthOpaque(const in vec2 coords) { + #ifdef depthTextureSupport + return texture2D(tDepthOpaque, coords).r; + #else + return unpackRGBAToDepth(texture2D(tDepthOpaque, coords)); + #endif +} + +float getDepthTransparent(const in vec2 coords) { + return unpackRGBAToDepth(texture2D(tDepthTransparent, coords)); } bool isBackground(const in float depth) { return depth == 1.0; } -float getOutline(const in vec2 coords, out float closestTexel) { +float getOutline(const in vec2 coords, const in float opaqueDepth, out float closestTexel) { float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff; vec2 invTexSize = 1.0 / uTexSize; - float selfDepth = getDepth(coords); + float selfDepth = min(opaqueDepth, getDepthTransparent(coords)); float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(selfDepth); float outline = 1.0; @@ -68,14 +77,15 @@ float getOutline(const in vec2 coords, out float closestTexel) { vec4 sampleOutlineCombined = texture2D(tOutlines, sampleCoords); float sampleOutline = sampleOutlineCombined.r; float sampleOutlineDepth = unpackRGToUnitInterval(sampleOutlineCombined.gb); + float sampleOutlineViewZ = isBackground(sampleOutlineDepth) ? backgroundViewZ : getViewZ(sampleOutlineDepth); - if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineDepth) > uMaxPossibleViewZDiff) { + if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineViewZ) > uMaxPossibleViewZDiff) { outline = 0.0; closestTexel = sampleOutlineDepth; } } } - return outline; + return closestTexel < opaqueDepth ? outline : 1.0; } float getSsao(vec2 coords) { @@ -95,11 +105,11 @@ void main(void) { float viewDist; float fogFactor; + float opaqueDepth = getDepthOpaque(coords); #ifdef dOcclusionEnable - float depth = getDepth(coords); - if (!isBackground(depth)) { - viewDist = abs(getViewZ(depth)); + if (!isBackground(opaqueDepth)) { + viewDist = abs(getViewZ(opaqueDepth)); fogFactor = smoothstep(uFogNear, uFogFar, viewDist); float occlusionFactor = getSsao(coords + uOcclusionOffset); if (!uTransparentBackground) { @@ -113,16 +123,15 @@ void main(void) { // outline needs to be handled after occlusion to keep them clean #ifdef dOutlineEnable float closestTexel; - float outline = getOutline(coords, closestTexel); - + float outline = getOutline(coords, opaqueDepth, closestTexel); if (outline == 0.0) { - color.rgb = mix(uOutlineColor, color.rgb, outline); viewDist = abs(getViewZ(closestTexel)); fogFactor = smoothstep(uFogNear, uFogFar, viewDist); if (!uTransparentBackground) { - color.rgb = mix(color.rgb, uFogColor, fogFactor); + color.rgb = mix(uOutlineColor, uFogColor, fogFactor); } else { color.a = 1.0 - fogFactor; + color.rgb = mix(uOutlineColor, color.rgb, fogFactor); } } #endif diff --git a/src/mol-gl/shader/ssao.frag.ts b/src/mol-gl/shader/ssao.frag.ts index 029661de7af06a9a8c4d5599dfb599b737efdf7a..41850c5ea31f200af46d545731869152bb43baa0 100644 --- a/src/mol-gl/shader/ssao.frag.ts +++ b/src/mol-gl/shader/ssao.frag.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz> @@ -51,7 +51,15 @@ bool outsideBounds(const in vec2 p) { } float getDepth(const in vec2 coords) { - return outsideBounds(coords) ? 1.0 : unpackRGBAToDepth(texture2D(tDepth, coords)); + if (outsideBounds(coords)) { + return 1.0; + } else { + #ifdef depthTextureSupport + return texture2D(tDepth, coords).r; + #else + return unpackRGBAToDepth(texture2D(tDepth, coords)); + #endif + } } vec3 normalFromDepth(const in float depth, const in float depth1, const in float depth2, vec2 offset1, vec2 offset2) { diff --git a/src/mol-gl/webgl/render-target.ts b/src/mol-gl/webgl/render-target.ts index 18d15ef9415d2d8e2c9e89e6dbbd016a8943af09..af9320e738162bbe2386b638eed1bf25c42e24ed 100644 --- a/src/mol-gl/webgl/render-target.ts +++ b/src/mol-gl/webgl/render-target.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> */ @@ -9,6 +9,7 @@ import { createNullTexture, Texture, TextureFilter } from './texture'; import { createNullFramebuffer, Framebuffer } from './framebuffer'; import { WebGLResources } from './resources'; import { GLRenderingContext, isWebGL2 } from './compat'; +import { Renderbuffer } from './renderbuffer'; const getNextRenderTargetId = idFactory(); @@ -16,6 +17,7 @@ export interface RenderTarget { readonly id: number readonly texture: Texture readonly framebuffer: Framebuffer + readonly depthRenderbuffer: Renderbuffer | null getWidth: () => number getHeight: () => number @@ -54,6 +56,7 @@ export function createRenderTarget(gl: GLRenderingContext, resources: WebGLResou id: getNextRenderTargetId(), texture: targetTexture, framebuffer, + depthRenderbuffer, getWidth: () => _width, getHeight: () => _height, @@ -90,6 +93,7 @@ export function createNullRenderTarget(gl: GLRenderingContext): RenderTarget { id: getNextRenderTargetId(), texture: createNullTexture(gl), framebuffer: createNullFramebuffer(), + depthRenderbuffer: null, getWidth: () => 0, getHeight: () => 0, diff --git a/src/mol-gl/webgl/renderbuffer.ts b/src/mol-gl/webgl/renderbuffer.ts index 857422fda22d433af6071cfe616e0ff4c6631f9d..b3d4ff94bbf4c6239b1054dc142eccdede0064a9 100644 --- a/src/mol-gl/webgl/renderbuffer.ts +++ b/src/mol-gl/webgl/renderbuffer.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> */ @@ -40,6 +40,7 @@ export interface Renderbuffer { bind: () => void attachFramebuffer: (framebuffer: Framebuffer) => void + detachFramebuffer: (framebuffer: Framebuffer) => void setSize: (width: number, height: number) => void reset: () => void destroy: () => void @@ -78,6 +79,12 @@ export function createRenderbuffer(gl: GLRenderingContext, format: RenderbufferF gl.framebufferRenderbuffer(gl.FRAMEBUFFER, _attachment, gl.RENDERBUFFER, _renderbuffer); if (isDebugMode) checkFramebufferStatus(gl); }, + detachFramebuffer: (framebuffer: Framebuffer) => { + framebuffer.bind(); + bind(); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, _attachment, gl.RENDERBUFFER, null); + if (isDebugMode) checkFramebufferStatus(gl); + }, setSize: (width: number, height: number) => { _width = width; _height = height; diff --git a/src/mol-gl/webgl/state.ts b/src/mol-gl/webgl/state.ts index ce13740f90f3419370934c20fac45700db892853..d84c91bc8fd48ed129b996d74dfdada51f7b958c 100644 --- a/src/mol-gl/webgl/state.ts +++ b/src/mol-gl/webgl/state.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> */ @@ -44,6 +44,10 @@ export type WebGLState = { cullFace: (mode: number) => void /** sets whether writing into the depth buffer is enabled or disabled */ depthMask: (flag: boolean) => void + /** specifies the depth value used when clearing depth buffer, used when calling `gl.clear` */ + clearDepth: (depth: number) => void + /** sets the depth comparison function */ + depthFunc: (func: number) => void /** sets which color components to enable or to disable */ colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => void /** specifies the color values used when clearing color buffers, used when calling `gl.clear`, clamped to [0, 1] */ @@ -58,6 +62,8 @@ export type WebGLState = { blendEquation: (mode: number) => void /** set the RGB blend equation and alpha blend equation separately, determines how a new pixel is combined with an existing */ blendEquationSeparate: (modeRGB: number, modeAlpha: number) => void + /** specifies the source and destination blending factors, clamped to [0, 1] */ + blendColor: (red: number, green: number, blue: number, alpha: number) => void enableVertexAttrib: (index: number) => void clearVertexAttribsState: () => void @@ -72,6 +78,8 @@ export function createState(gl: GLRenderingContext): WebGLState { let currentFrontFace = gl.getParameter(gl.FRONT_FACE); let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE); let currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK); + let currentClearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE); + let currentDepthFunc = gl.getParameter(gl.DEPTH_FUNC); let currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK); let currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); @@ -79,6 +87,7 @@ export function createState(gl: GLRenderingContext): WebGLState { let currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB); let currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA); let currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA); + let currentBlendColor = gl.getParameter(gl.BLEND_COLOR); let currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB); let currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA); @@ -129,6 +138,18 @@ export function createState(gl: GLRenderingContext): WebGLState { currentDepthMask = flag; } }, + clearDepth: (depth: number) => { + if (depth !== currentClearDepth) { + gl.clearDepth(depth); + currentClearDepth = depth; + } + }, + depthFunc: (func: number) => { + if (func !== currentDepthFunc) { + gl.depthFunc(func); + currentDepthFunc = func; + } + }, colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => { if (red !== currentColorMask[0] || green !== currentColorMask[1] || blue !== currentColorMask[2] || alpha !== currentColorMask[3]) { gl.colorMask(red, green, blue, alpha); @@ -166,7 +187,6 @@ export function createState(gl: GLRenderingContext): WebGLState { currentBlendDstAlpha = dstAlpha; } }, - blendEquation: (mode: number) => { if (mode !== currentBlendEqRGB || mode !== currentBlendEqAlpha) { gl.blendEquation(mode); @@ -181,6 +201,15 @@ export function createState(gl: GLRenderingContext): WebGLState { currentBlendEqAlpha = modeAlpha; } }, + blendColor: (red: number, green: number, blue: number, alpha: number) => { + if (red !== currentBlendColor[0] || green !== currentBlendColor[1] || blue !== currentBlendColor[2] || alpha !== currentBlendColor[3]) { + gl.blendColor(red, green, blue, alpha); + currentBlendColor[0] = red; + currentBlendColor[1] = green; + currentBlendColor[2] = blue; + currentBlendColor[3] = alpha; + } + }, enableVertexAttrib: (index: number) => { gl.enableVertexAttribArray(index); @@ -199,6 +228,8 @@ export function createState(gl: GLRenderingContext): WebGLState { currentFrontFace = gl.getParameter(gl.FRONT_FACE); currentCullFace = gl.getParameter(gl.CULL_FACE_MODE); currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK); + currentClearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE); + currentDepthFunc = gl.getParameter(gl.DEPTH_FUNC); currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK); currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); @@ -206,6 +237,7 @@ export function createState(gl: GLRenderingContext): WebGLState { currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB); currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA); currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA); + currentBlendColor = gl.getParameter(gl.BLEND_COLOR); currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB); currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA); diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts index 7d58ef1da7dddb08823a42aa1e54d663f1186cf3..3966a268d5d2d96cc7abbc94c18013523b86e617 100644 --- a/src/mol-gl/webgl/texture.ts +++ b/src/mol-gl/webgl/texture.ts @@ -238,6 +238,10 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension throw new Error(`texture kind '${kind}' and type '${_type}' are incompatible`); } + if (!extensions.depthTexture && _format === 'depth') { + throw new Error(`extension 'WEBGL_depth_texture' needed for 'depth' texture format`); + } + const target = getTarget(gl, kind); const filter = getFilter(gl, _filter); const format = getFormat(gl, _format, _type);