diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts
index 8d15a9deee225c852c32ed4d1c47b8c19cde8982..95bd539a88148221f0a2d63320845fda68febe01 100644
--- a/src/mol-canvas3d/canvas3d.ts
+++ b/src/mol-canvas3d/canvas3d.ts
@@ -38,6 +38,7 @@ export const Canvas3DParams = {
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
+ transparentBackground: PD.Boolean(false),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
@@ -191,9 +192,9 @@ namespace Canvas3D {
case 'draw':
renderer.setViewport(0, 0, width, height)
if (multiSample.enabled) {
- multiSample.render(true)
+ multiSample.render(true, p.transparentBackground)
} else {
- drawPass.render(!postprocessing.enabled)
+ drawPass.render(!postprocessing.enabled, p.transparentBackground)
if (postprocessing.enabled) postprocessing.render(true)
}
pickPass.pickDirty = true
@@ -346,6 +347,7 @@ namespace Canvas3D {
camera.setState({ fog: props.cameraFog })
}
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
+ if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
if (props.multiSample) multiSample.setProps(props.multiSample)
@@ -363,6 +365,7 @@ namespace Canvas3D {
cameraMode: camera.state.mode,
cameraFog: camera.state.fog,
cameraResetDurationMs: p.cameraResetDurationMs,
+ transparentBackground: p.transparentBackground,
postprocessing: { ...postprocessing.props },
multiSample: { ...multiSample.props },
diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts
index b2f5a9477707150b45ac0152a8e9aa439d7db36e..5872e3cf074ac86b1c50d510f347d844afc0d542 100644
--- a/src/mol-canvas3d/passes/draw.ts
+++ b/src/mol-canvas3d/passes/draw.ts
@@ -42,7 +42,7 @@ export class DrawPass {
}
}
- render(toDrawingBuffer: boolean) {
+ render(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
@@ -51,20 +51,20 @@ export class DrawPass {
}
renderer.setViewport(0, 0, colorTarget.width, colorTarget.height)
- renderer.render(scene, camera, 'color', true)
+ renderer.render(scene, camera, 'color', true, transparentBackground)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
- renderer.render(debugHelper.scene, camera, 'color', false)
+ renderer.render(debugHelper.scene, camera, 'color', false, transparentBackground)
}
// do a depth pass if not rendering to drawing buffer and
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (!toDrawingBuffer && depthTarget) {
depthTarget.bind()
- renderer.render(scene, camera, 'depth', true)
+ renderer.render(scene, camera, 'depth', true, transparentBackground)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
- renderer.render(debugHelper.scene, camera, 'depth', false)
+ renderer.render(debugHelper.scene, camera, 'depth', false, transparentBackground)
}
}
}
diff --git a/src/mol-canvas3d/passes/image.ts b/src/mol-canvas3d/passes/image.ts
index dde52c99d4d39fc855bbf22ad0d016a16187872d..dc25a9cc36d2a42aa1e50335ea4021cb960afec9 100644
--- a/src/mol-canvas3d/passes/image.ts
+++ b/src/mol-canvas3d/passes/image.ts
@@ -17,6 +17,7 @@ import { Camera } from '../camera';
import { Viewport } from '../camera/util';
export const ImageParams = {
+ transparentBackground: PD.Boolean(false),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
}
@@ -26,6 +27,7 @@ export class ImagePass {
private _width = 1024
private _height = 768
private _camera = new Camera()
+ private _transparentBackground = false
private _colorTarget: RenderTarget
get colorTarget() { return this._colorTarget }
@@ -40,6 +42,8 @@ export class ImagePass {
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
const p = { ...PD.getDefaultValues(ImageParams), ...props }
+ this._transparentBackground = p.transparentBackground
+
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper)
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
@@ -48,6 +52,8 @@ export class ImagePass {
}
setSize(width: number, height: number) {
+ if (width === this._width && height === this._height) return
+
this._width = width
this._height = height
@@ -57,6 +63,7 @@ export class ImagePass {
}
setProps(props: Partial<ImageProps> = {}) {
+ if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
if (props.multiSample) this.multiSample.setProps(props.multiSample)
}
@@ -69,10 +76,10 @@ export class ImagePass {
this.renderer.setViewport(0, 0, this._width, this._height);
if (this.multiSample.enabled) {
- this.multiSample.render(false)
+ this.multiSample.render(false, this._transparentBackground)
this._colorTarget = this.multiSample.colorTarget
} else {
- this.drawPass.render(false)
+ this.drawPass.render(false, this._transparentBackground)
if (this.postprocessing.enabled) {
this.postprocessing.render(false)
this._colorTarget = this.postprocessing.target
diff --git a/src/mol-canvas3d/passes/multi-sample.ts b/src/mol-canvas3d/passes/multi-sample.ts
index 94dfd4cdaa94655dda9ebc665f7437c29582f4da..f22af14afa6d0ee52886bc01c6c92905aa730342 100644
--- a/src/mol-canvas3d/passes/multi-sample.ts
+++ b/src/mol-canvas3d/passes/multi-sample.ts
@@ -105,15 +105,15 @@ export class MultiSamplePass {
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
}
- render(toDrawingBuffer: boolean) {
+ render(toDrawingBuffer: boolean, transparentBackground: boolean) {
if (this.props.mode === 'temporal') {
- this.renderTemporalMultiSample(toDrawingBuffer)
+ this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
} else {
- this.renderMultiSample(toDrawingBuffer)
+ this.renderMultiSample(toDrawingBuffer, transparentBackground)
}
}
- private renderMultiSample(toDrawingBuffer: boolean) {
+ private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
const { gl, state } = webgl
@@ -148,7 +148,7 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, sampleWeight)
// render scene and optionally postprocess
- drawPass.render(false)
+ drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
// compose rendered scene with compose target
@@ -184,7 +184,7 @@ export class MultiSamplePass {
camera.update()
}
- private renderTemporalMultiSample(toDrawingBuffer: boolean) {
+ private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
const { gl, state } = webgl
@@ -204,7 +204,7 @@ export class MultiSamplePass {
const i = this.sampleIndex
if (i === 0) {
- drawPass.render(false)
+ drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
ValueCell.update(compose.values.uWeight, 1.0)
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
@@ -233,7 +233,7 @@ export class MultiSamplePass {
camera.update()
// render scene and optionally postprocess
- drawPass.render(false)
+ drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
// compose rendered scene with compose target
diff --git a/src/mol-canvas3d/passes/pick.ts b/src/mol-canvas3d/passes/pick.ts
index 28867455f11403050358b035d6ad8849653dff09..c4f54a816f55f4bc70e7777f72aa3b1c70462895 100644
--- a/src/mol-canvas3d/passes/pick.ts
+++ b/src/mol-canvas3d/passes/pick.ts
@@ -68,11 +68,11 @@ export class PickPass {
const { renderer, scene, camera } = this
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
this.objectPickTarget.bind();
- renderer.render(scene, camera, 'pickObject', true);
+ renderer.render(scene, camera, 'pickObject', true, false);
this.instancePickTarget.bind();
- renderer.render(scene, camera, 'pickInstance', true);
+ renderer.render(scene, camera, 'pickInstance', true, false);
this.groupPickTarget.bind();
- renderer.render(scene, camera, 'pickGroup', true);
+ renderer.render(scene, camera, 'pickGroup', true, false);
this.pickDirty = false
}
diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts
index f25e539ed05a89ae4716335015786de083fb534b..7bfcad1d691550c9192c02a00eb3089775418453 100644
--- a/src/mol-gl/renderer.ts
+++ b/src/mol-gl/renderer.ts
@@ -37,8 +37,8 @@ interface Renderer {
readonly stats: RendererStats
readonly props: Readonly<RendererProps>
- clear: () => void
- render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
+ clear: (transparentBackground: boolean) => void
+ render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => void
setProps: (props: Partial<RendererProps>) => void
setViewport: (x: number, y: number, width: number, height: number) => void
dispose: () => void
@@ -46,7 +46,6 @@ interface Renderer {
export const RendererParams = {
backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
- transparentBackground: PD.Boolean(false, { description: 'Background opacity of the 3D canvas' }),
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
interiorColorFlag: PD.Boolean(true),
@@ -111,8 +110,8 @@ namespace Renderer {
uFogNear: ValueCell.create(1),
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(bgColor),
+ uTransparentBackground: ValueCell.create(0),
- uTransparentBackground: ValueCell.create(p.transparentBackground ? 1 : 0),
uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
uInteriorDarkening: ValueCell.create(p.interiorDarkening),
uInteriorColorFlag: ValueCell.create(p.interiorColorFlag ? 1 : 0),
@@ -166,7 +165,7 @@ namespace Renderer {
}
}
- const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
+ const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => {
ValueCell.update(globalUniforms.uModel, scene.view)
ValueCell.update(globalUniforms.uView, camera.view)
ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
@@ -186,6 +185,8 @@ namespace Renderer {
ValueCell.update(globalUniforms.uFogFar, camera.fogFar)
ValueCell.update(globalUniforms.uFogNear, camera.fogNear)
+ ValueCell.update(globalUniforms.uTransparentBackground, transparentBackground ? 1 : 0)
+
globalUniformsNeedUpdate = true
state.currentRenderItemId = -1
@@ -199,7 +200,7 @@ namespace Renderer {
if (clear) {
if (variant === 'color') {
- state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
+ state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1)
} else {
state.clearColor(1, 1, 1, 1)
}
@@ -231,10 +232,10 @@ namespace Renderer {
}
return {
- clear: () => {
+ clear: (transparentBackground: boolean) => {
state.depthMask(true)
state.colorMask(true, true, true, true)
- state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
+ state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
},
render,
@@ -262,10 +263,6 @@ namespace Renderer {
Color.toVec3Normalized(bgColor, p.backgroundColor)
ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor))
}
- if (props.transparentBackground !== undefined && props.transparentBackground !== p.transparentBackground) {
- p.transparentBackground = props.transparentBackground
- ValueCell.update(globalUniforms.uTransparentBackground, p.transparentBackground ? 1 : 0)
- }
if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
p.lightIntensity = props.lightIntensity
ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)
diff --git a/src/mol-plugin-ui/skin/base/components/controls.scss b/src/mol-plugin-ui/skin/base/components/controls.scss
index c24043385dcc30bc0b70e1639ea04cf1aefda437..8a0f44a4cd5bf72828f1d4e5ce42f92b582f6c6f 100644
--- a/src/mol-plugin-ui/skin/base/components/controls.scss
+++ b/src/mol-plugin-ui/skin/base/components/controls.scss
@@ -383,6 +383,12 @@
max-height: 180px;
max-width: 100%;
display: 'block';
+
+ background-color: $default-background;
+ background-image: linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%, lightgrey),
+ linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%, lightgrey);
+ background-size: 30px 30px;
+ background-position: 0 0, 15px 15px;
}
> span {
diff --git a/src/mol-plugin-ui/viewport/screenshot.tsx b/src/mol-plugin-ui/viewport/screenshot.tsx
index 430e4eb21854466cffab41853b1b6ac97eaa1c3e..ad22766a3fba63a08ebe43d28c9949a817fce1d3 100644
--- a/src/mol-plugin-ui/viewport/screenshot.tsx
+++ b/src/mol-plugin-ui/viewport/screenshot.tsx
@@ -18,6 +18,7 @@ interface ImageControlsState {
showPreview: boolean
resolution?: ViewportScreenshotHelper.ResolutionSettings,
+ transparent?: boolean,
isDisabled: boolean
}
@@ -25,6 +26,7 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
state: ImageControlsState = {
showPreview: true,
resolution: this.plugin.helpers.viewportScreenshot?.currentResolution,
+ transparent: this.plugin.helpers.viewportScreenshot?.transparent,
isDisabled: false
} as ImageControlsState
@@ -110,6 +112,9 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
this.plugin.helpers.viewportScreenshot!.currentResolution.type = resolution.name;
}
this.setState({ resolution });
+ } else if (p.name === 'transparent') {
+ this.plugin.helpers.viewportScreenshot!.transparent = p.value;
+ this.setState({ transparent: p.value });
}
}
diff --git a/src/mol-plugin-ui/viewport/simple-settings.tsx b/src/mol-plugin-ui/viewport/simple-settings.tsx
index 2062c6f4c9a8c89fedd7615b305961aee62a7a31..8d55f0768f2a83d8e9025f2edbd525954c6bf432 100644
--- a/src/mol-plugin-ui/viewport/simple-settings.tsx
+++ b/src/mol-plugin-ui/viewport/simple-settings.tsx
@@ -40,9 +40,9 @@ export class SimpleSettingsControl extends PluginUIComponent {
const renderer = this.plugin.canvas3d.props.renderer;
const color: typeof SimpleSettingsParams['background']['defaultValue'] = p.value;
if (color.name === 'transparent') {
- PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: ColorNames.white, transparentBackground: true } } });
+ PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: ColorNames.white }, transparentBackground: true } });
} else {
- PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: color.params.color, transparentBackground: false } } });
+ PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: color.params.color }, transparentBackground: false } });
}
} else if (p.name === 'renderStyle') {
if (!this.plugin.canvas3d) return;
@@ -103,7 +103,7 @@ export class SimpleSettingsControl extends PluginUIComponent {
}
}
- if (renderer.backgroundColor === ColorNames.white && renderer.transparentBackground) {
+ if (renderer.backgroundColor === ColorNames.white && this.plugin.canvas3d?.props.transparentBackground) {
background = { name: 'transparent', params: { } }
} else {
background = { name: 'opaque', params: { color: renderer.backgroundColor } }
diff --git a/src/mol-plugin/util/viewport-screenshot.ts b/src/mol-plugin/util/viewport-screenshot.ts
index 87f8818204ee4f0669f8c61ea403584e351a7342..be05711c5863eba48dd1b1abd030b8e1e3e2416e 100644
--- a/src/mol-plugin/util/viewport-screenshot.ts
+++ b/src/mol-plugin/util/viewport-screenshot.ts
@@ -26,6 +26,7 @@ class ViewportScreenshotHelper {
private createParams() {
const max = Math.min(this.plugin.canvas3d ? this.plugin.canvas3d.webgl.maxRenderbufferSize : 4096, 4096)
return {
+ transparent: PD.Boolean(false),
resolution: PD.MappedStatic('full-hd', {
viewport: PD.Group({}),
hd: PD.Group({}),
@@ -54,8 +55,8 @@ class ViewportScreenshotHelper {
get values() {
return this.currentResolution.type === 'custom'
- ? { resolution: { name: 'custom', params: { width: this.currentResolution.width, height: this.currentResolution.height } } }
- : { resolution: { name: this.currentResolution.type, params: { } } };
+ ? { transparent: this.transparent, resolution: { name: 'custom', params: { width: this.currentResolution.width, height: this.currentResolution.height } } }
+ : { transparent: this.transparent, resolution: { name: this.currentResolution.type, params: { } } };
}
private getCanvasSize() {
@@ -65,6 +66,8 @@ class ViewportScreenshotHelper {
};
}
+ transparent = false
+
currentResolution = {
type: 'full-hd' as ViewportScreenshotHelper.ResolutionTypes,
width: 1920,
@@ -88,6 +91,7 @@ class ViewportScreenshotHelper {
this._imagePass = this.plugin.canvas3d!.getImagePass()
this._imagePass.setProps({
+ transparentBackground: this.transparent,
multiSample: { mode: 'on', sampleLevel: 2 },
postprocessing: this.plugin.canvas3d!.props.postprocessing
});
@@ -134,6 +138,10 @@ class ViewportScreenshotHelper {
if (width <= 0 || height <= 0) return;
await ctx.update('Rendering image...')
+ this.imagePass.setProps({
+ transparentBackground: this.transparent,
+ postprocessing: this.plugin.canvas3d!.props.postprocessing // TODO this line should not be required, updating should work by listening to this.plugin.events.canvas3d.settingsUpdated
+ });
const imageData = this.imagePass.getImageData(width, height);
await ctx.update('Encoding image...')