diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 3a9bcfc99deafbe50c2767e94012db7fd1292e89..83bd67d0a4813e6a73592e7100d1f9df02efb4a6 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -30,7 +30,7 @@ import { SetUtils } from 'mol-util/set'; import { Canvas3dInteractionHelper } from './helper/interaction-events'; import { createTexture } from 'mol-gl/webgl/texture'; import { ValueCell } from 'mol-util'; -import { getPostprocessingRenderable, PostprocessingParams, setPostprocessingProps } from './helper/postprocessing'; +import { PostprocessingParams, PostprocessingPass } from './helper/postprocessing'; import { JitterVectors, getComposeRenderable } from './helper/multi-sample'; import { GLRenderingContext } from 'mol-gl/webgl/compat'; import { PixelData } from 'mol-util/image'; @@ -144,8 +144,7 @@ namespace Canvas3D { depthTexture.attachFramebuffer(drawTarget.framebuffer, 'depth') } - const postprocessingTarget = createRenderTarget(webgl, width, height) - const postprocessing = getPostprocessingRenderable(webgl, drawTarget.texture, depthTexture, !!depthTarget, p.postprocessing) + const postprocessing = new PostprocessingPass(webgl, drawTarget.texture, depthTexture, !!depthTarget, p.postprocessing) const composeTarget = createRenderTarget(webgl, width, height) const holdTarget = createRenderTarget(webgl, width, height) @@ -232,7 +231,7 @@ namespace Canvas3D { } } - function renderDraw(postprocessingEnabled: boolean) { + function renderDraw() { renderer.setViewport(0, 0, width, height) renderer.render(scene, 'draw', true) if (debugHelper.isEnabled) { @@ -240,7 +239,7 @@ namespace Canvas3D { renderer.render(debugHelper.scene, 'draw', false) } - if (postprocessingEnabled && depthTarget) { + if (postprocessing.enabled && depthTarget) { depthTarget.bind() renderer.render(scene, 'depth', true) if (debugHelper.isEnabled) { @@ -250,16 +249,7 @@ namespace Canvas3D { } } - function renderPostprocessing() { - gl.viewport(0, 0, width, height) - state.disable(gl.SCISSOR_TEST) - state.disable(gl.BLEND) - state.disable(gl.DEPTH_TEST) - state.depthMask(false) - postprocessing.render() - } - - function renderTemporalMultiSample(postprocessingEnabled: boolean) { + function renderTemporalMultiSample() { // based on the Multisample Anti-Aliasing Render Pass // contributed to three.js by bhouston / http://clara.io/ // @@ -277,13 +267,10 @@ namespace Canvas3D { if (i === 0) { drawTarget.bind() - renderDraw(postprocessingEnabled) - if (postprocessingEnabled) { - postprocessingTarget.bind() - renderPostprocessing() - } + renderDraw() + if (postprocessing.enabled) postprocessing.render(false) ValueCell.update(compose.values.uWeight, 1.0) - ValueCell.update(compose.values.tColor, postprocessingEnabled ? postprocessingTarget.texture : drawTarget.texture) + ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawTarget.texture) compose.update() holdTarget.bind() @@ -294,7 +281,7 @@ namespace Canvas3D { const sampleWeight = 1.0 / offsetList.length camera.viewOffset.enabled = true - ValueCell.update(compose.values.tColor, postprocessingEnabled ? postprocessingTarget.texture : drawTarget.texture) + ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawTarget.texture) ValueCell.update(compose.values.uWeight, sampleWeight) compose.update() @@ -310,11 +297,8 @@ namespace Canvas3D { // render scene and optionally postprocess drawTarget.bind() - renderDraw(postprocessingEnabled) - if (postprocessingEnabled) { - postprocessingTarget.bind() - renderPostprocessing() - } + renderDraw() + if (postprocessing.enabled) postprocessing.render(false) // compose rendered scene with compose target composeTarget.bind() @@ -360,7 +344,7 @@ namespace Canvas3D { if (multiSampleIndex >= offsetList.length) multiSampleIndex = -1 } - function renderMultiSample(postprocessingEnabled: boolean) { + function renderMultiSample() { // based on the Multisample Anti-Aliasing Render Pass // contributed to three.js by bhouston / http://clara.io/ // @@ -372,7 +356,7 @@ namespace Canvas3D { const roundingRange = 1 / 32 camera.viewOffset.enabled = true - ValueCell.update(compose.values.tColor, postprocessingEnabled ? postprocessingTarget.texture : drawTarget.texture) + ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawTarget.texture) compose.update() const { width, height } = drawTarget @@ -393,11 +377,8 @@ namespace Canvas3D { // render scene and optionally postprocess drawTarget.bind() - renderDraw(postprocessingEnabled) - if (postprocessingEnabled) { - postprocessingTarget.bind() - renderPostprocessing() - } + renderDraw() + if (postprocessing.enabled) postprocessing.render(false) // compose rendered scene with compose target composeTarget.bind() @@ -449,7 +430,6 @@ namespace Canvas3D { } else { multiSample = false } - const postprocessingEnabled = p.postprocessing.occlusionEnable || p.postprocessing.outlineEnable if (force || cameraChanged || multiSample) { switch (variant) { @@ -466,18 +446,15 @@ namespace Canvas3D { renderer.setViewport(0, 0, width, height); if (multiSample) { if (p.multiSample === 'temporal') { - renderTemporalMultiSample(postprocessingEnabled) + renderTemporalMultiSample() } else { - renderMultiSample(postprocessingEnabled) + renderMultiSample() } } else { - if (postprocessingEnabled) drawTarget.bind() + if (postprocessing.enabled) drawTarget.bind() else webgl.unbindFramebuffer() - renderDraw(postprocessingEnabled) - if (postprocessingEnabled) { - webgl.unbindFramebuffer() - renderPostprocessing() - } + renderDraw() + if (postprocessing.enabled) postprocessing.render(true) } pickDirty = true break; @@ -662,9 +639,7 @@ namespace Canvas3D { if (props.multiSample !== undefined) p.multiSample = props.multiSample if (props.sampleLevel !== undefined) p.sampleLevel = props.sampleLevel - if (props.postprocessing) { - setPostprocessingProps(props.postprocessing, postprocessing, p.postprocessing, webgl) - } + if (props.postprocessing) postprocessing.setProps(props.postprocessing) if (props.renderer) renderer.setProps(props.renderer) if (props.trackball) controls.setProps(props.trackball) @@ -682,7 +657,7 @@ namespace Canvas3D { multiSample: p.multiSample, sampleLevel: p.sampleLevel, - postprocessing: { ...p.postprocessing }, + postprocessing: { ...postprocessing.props }, renderer: { ...renderer.props }, trackball: { ...controls.props }, debug: { ...debugHelper.props } @@ -717,7 +692,7 @@ namespace Canvas3D { Viewport.set(controls.viewport, 0, 0, width, height) drawTarget.setSize(width, height) - postprocessingTarget.setSize(width, height) + postprocessing.setSize(width, height) composeTarget.setSize(width, height) holdTarget.setSize(width, height) if (depthTarget) { @@ -725,7 +700,6 @@ namespace Canvas3D { } else { depthTexture.define(width, height) } - ValueCell.update(postprocessing.values.uTexSize, Vec2.set(postprocessing.values.uTexSize.ref.value, width, height)) ValueCell.update(compose.values.uTexSize, Vec2.set(compose.values.uTexSize.ref.value, width, height)) pickScale = pickBaseScale / webgl.pixelRatio diff --git a/src/mol-canvas3d/helper/postprocessing.ts b/src/mol-canvas3d/helper/postprocessing.ts index 3f56ebad935764d9b656f3447cff702d0dcc5163..39fc3965988e334f2e17d7716e4303202e14220f 100644 --- a/src/mol-canvas3d/helper/postprocessing.ts +++ b/src/mol-canvas3d/helper/postprocessing.ts @@ -14,6 +14,7 @@ import { createComputeRenderItem } from 'mol-gl/webgl/render-item'; import { createComputeRenderable, ComputeRenderable } from 'mol-gl/renderable'; import { Vec2 } from 'mol-math/linear-algebra'; import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { createRenderTarget, RenderTarget } from 'mol-gl/webgl/render-target'; const PostprocessingSchema = { ...QuadSchema, @@ -77,36 +78,73 @@ export function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Tex return createComputeRenderable(renderItem, values) } -export function setPostprocessingProps(props: Partial<PostprocessingProps>, postprocessing: PostprocessingRenderable, currentProps: PostprocessingProps, webgl: WebGLContext) { - if (props.occlusionEnable !== undefined) { - currentProps.occlusionEnable = props.occlusionEnable - ValueCell.update(postprocessing.values.dOcclusionEnable, props.occlusionEnable) - } - if (props.occlusionKernelSize !== undefined) { - currentProps.occlusionKernelSize = props.occlusionKernelSize - ValueCell.update(postprocessing.values.dOcclusionKernelSize, props.occlusionKernelSize) - } - if (props.occlusionBias !== undefined) { - currentProps.occlusionBias = props.occlusionBias - ValueCell.update(postprocessing.values.uOcclusionBias, props.occlusionBias) - } - if (props.occlusionRadius !== undefined) { - currentProps.occlusionRadius = props.occlusionRadius - ValueCell.update(postprocessing.values.uOcclusionRadius, props.occlusionRadius) +export class PostprocessingPass { + target: RenderTarget + props: PostprocessingProps + renderable: PostprocessingRenderable + + constructor(private webgl: WebGLContext, colorTexture: Texture, depthTexture: Texture, packedDepth: boolean, props: Partial<PostprocessingProps>) { + const { gl } = webgl + this.target = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight) + this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props } + this.renderable = getPostprocessingRenderable(webgl, colorTexture, depthTexture, packedDepth, this.props) } - if (props.outlineEnable !== undefined) { - currentProps.outlineEnable = props.outlineEnable - ValueCell.update(postprocessing.values.dOutlineEnable, props.outlineEnable) + get enabled() { + return this.props.occlusionEnable || this.props.outlineEnable } - if (props.outlineScale !== undefined) { - currentProps.outlineScale = props.outlineScale - ValueCell.update(postprocessing.values.uOutlineScale, props.outlineScale * webgl.pixelRatio) + + setSize(width: number, height: number) { + this.target.setSize(width, height) + ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height)) } - if (props.outlineThreshold !== undefined) { - currentProps.outlineThreshold = props.outlineThreshold - ValueCell.update(postprocessing.values.uOutlineThreshold, props.outlineThreshold) + + setProps(props: Partial<PostprocessingProps>) { + if (props.occlusionEnable !== undefined) { + this.props.occlusionEnable = props.occlusionEnable + ValueCell.update(this.renderable.values.dOcclusionEnable, props.occlusionEnable) + } + if (props.occlusionKernelSize !== undefined) { + this.props.occlusionKernelSize = props.occlusionKernelSize + ValueCell.update(this.renderable.values.dOcclusionKernelSize, props.occlusionKernelSize) + } + if (props.occlusionBias !== undefined) { + this.props.occlusionBias = props.occlusionBias + ValueCell.update(this.renderable.values.uOcclusionBias, props.occlusionBias) + } + if (props.occlusionRadius !== undefined) { + this.props.occlusionRadius = props.occlusionRadius + ValueCell.update(this.renderable.values.uOcclusionRadius, props.occlusionRadius) + } + + if (props.outlineEnable !== undefined) { + this.props.outlineEnable = props.outlineEnable + ValueCell.update(this.renderable.values.dOutlineEnable, props.outlineEnable) + } + if (props.outlineScale !== undefined) { + this.props.outlineScale = props.outlineScale + ValueCell.update(this.renderable.values.uOutlineScale, props.outlineScale * this.webgl.pixelRatio) + } + if (props.outlineThreshold !== undefined) { + this.props.outlineThreshold = props.outlineThreshold + ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold) + } + + this.renderable.update() } - postprocessing.update() + render(toDrawingBuffer: boolean) { + const { gl, state } = this.webgl + if (toDrawingBuffer) { + this.webgl.unbindFramebuffer() + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight) + } else { + this.target.bind() + } + state.disable(gl.SCISSOR_TEST) + state.disable(gl.BLEND) + state.disable(gl.DEPTH_TEST) + state.depthMask(false) + this.renderable.render() + } } \ No newline at end of file