Bladeren bron

add ImagePass

Alexander Rose 5 jaren geleden
bovenliggende
commit
33fd105ef7

+ 12 - 6
src/mol-canvas3d/canvas3d.ts

@@ -32,6 +32,7 @@ import { readTexture } from '../mol-gl/compute/util';
 import { DrawPass } from './passes/draw';
 import { PickPass } from './passes/pick';
 import { Task } from '../mol-task';
+import { ImagePass, ImageProps } from './passes/image';
 
 export const Canvas3DParams = {
     cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
@@ -74,6 +75,7 @@ interface Canvas3D {
     downloadScreenshot: () => void
     getPixelData: (variant: GraphicsRenderVariant) => PixelData
     setProps: (props: Partial<Canvas3DProps>) => void
+    getImagePass: () => ImagePass
 
     /** Returns a copy of the current Canvas3D instance props */
     readonly props: Readonly<Canvas3DProps>
@@ -128,12 +130,12 @@ namespace Canvas3D {
         })
 
         const controls = TrackballControls.create(input, camera, p.trackball)
-        const renderer = Renderer.create(webgl, camera, p.renderer)
+        const renderer = Renderer.create(webgl, p.renderer)
         const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
         const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
 
-        const drawPass = new DrawPass(webgl, renderer, scene, debugHelper)
-        const pickPass = new PickPass(webgl, renderer, scene, 0.5)
+        const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper)
+        const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5)
         const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
         const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
 
@@ -177,6 +179,7 @@ namespace Canvas3D {
 
             let didRender = false
             controls.update(currentTime)
+            Viewport.set(camera.viewport, 0, 0, width, height)
             const cameraChanged = camera.update()
             multiSample.update(force || cameraChanged, currentTime)
 
@@ -186,9 +189,9 @@ namespace Canvas3D {
                         pickPass.render()
                         break;
                     case 'draw':
-                        renderer.setViewport(0, 0, width, height);
+                        renderer.setViewport(0, 0, width, height)
                         if (multiSample.enabled) {
-                            multiSample.render()
+                            multiSample.render(true)
                         } else {
                             drawPass.render(!postprocessing.enabled)
                             if (postprocessing.enabled) postprocessing.render(true)
@@ -309,7 +312,7 @@ namespace Canvas3D {
             getLoci,
 
             handleResize,
-            resetCamera: (/*dir?: Vec3*/) => {
+            resetCamera: () => {
                 if (scene.isCommiting) {
                     cameraResetRequested = true
                 } else {
@@ -348,6 +351,9 @@ namespace Canvas3D {
                 if (props.debug) debugHelper.setProps(props.debug)
                 requestDraw(true)
             },
+            getImagePass: (props: Partial<ImageProps> = {}) => {
+                return new ImagePass(webgl, renderer, scene, camera, debugHelper, props)
+            },
 
             get props() {
                 return {

+ 10 - 9
src/mol-canvas3d/passes/draw.ts

@@ -10,6 +10,7 @@ import Renderer from '../../mol-gl/renderer';
 import Scene from '../../mol-gl/scene';
 import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
 import { createTexture, Texture } from '../../mol-gl/webgl/texture';
+import { Camera } from '../camera';
 
 export class DrawPass {
     colorTarget: RenderTarget
@@ -18,11 +19,11 @@ export class DrawPass {
 
     private depthTarget: RenderTarget | null
 
-    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private debugHelper: BoundingSphereHelper) {
+    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
         const { gl, extensions } = webgl
         const width = gl.drawingBufferWidth
         const height = gl.drawingBufferHeight
-        this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
+        this.colorTarget = createRenderTarget(webgl, width, height)
         this.packedDepth = !extensions.depthTexture
         this.depthTarget = this.packedDepth ? createRenderTarget(webgl, width, height) : null
         this.depthTexture = this.depthTarget ? this.depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
@@ -42,28 +43,28 @@ export class DrawPass {
     }
 
     render(toDrawingBuffer: boolean) {
-        const { webgl, renderer, scene, debugHelper, colorTarget, depthTarget } = this
-        const { gl } = webgl
+        const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
         if (toDrawingBuffer) {
             webgl.unbindFramebuffer()
         } else {
             colorTarget.bind()
         }
-        renderer.setViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
-        renderer.render(scene, 'color', true)
+
+        renderer.setViewport(0, 0, colorTarget.width, colorTarget.height)
+        renderer.render(scene, camera, 'color', true)
         if (debugHelper.isEnabled) {
             debugHelper.syncVisibility()
-            renderer.render(debugHelper.scene, 'color', false)
+            renderer.render(debugHelper.scene, camera, 'color', false)
         }
 
         // 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, 'depth', true)
+            renderer.render(scene, camera, 'depth', true)
             if (debugHelper.isEnabled) {
                 debugHelper.syncVisibility()
-                renderer.render(debugHelper.scene, 'depth', false)
+                renderer.render(debugHelper.scene, camera, 'depth', false)
             }
         }
     }

+ 91 - 0
src/mol-canvas3d/passes/image.ts

@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { WebGLContext } from '../../mol-gl/webgl/context';
+import { RenderTarget } from '../../mol-gl/webgl/render-target';
+import Renderer from '../../mol-gl/renderer';
+import Scene from '../../mol-gl/scene';
+import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { DrawPass } from './draw'
+import { PostprocessingPass, PostprocessingParams } from './postprocessing'
+import { MultiSamplePass, MultiSampleParams } from './multi-sample'
+import { Camera } from '../camera';
+import { Viewport } from '../camera/util';
+
+export const ImageParams = {
+    multiSample: PD.Group(MultiSampleParams),
+    postprocessing: PD.Group(PostprocessingParams),
+}
+export type ImageProps = PD.Values<typeof ImageParams>
+
+export class ImagePass {
+    private _width = 1024
+    private _height = 768
+    private _camera = new Camera()
+
+    private _colorTarget: RenderTarget
+    get colorTarget() { return this._colorTarget }
+
+    readonly drawPass: DrawPass
+    private readonly postprocessing: PostprocessingPass
+    private readonly multiSample: MultiSamplePass
+
+    get width() { return this._width }
+    get height() { return this._height }
+
+    constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
+        const p = { ...PD.getDefaultValues(ImageParams), ...props }
+
+        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)
+
+        this.setSize(this._width, this._height)
+    }
+
+    setSize(width: number, height: number) {
+        this._width = width
+        this._height = height
+
+        this.drawPass.setSize(width, height)
+        this.postprocessing.setSize(width, height)
+        this.multiSample.setSize(width, height)
+    }
+
+    setProps(props: Partial<ImageProps> = {}) {
+        if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
+        if (props.multiSample) this.multiSample.setProps(props.multiSample)
+    }
+
+    render() {
+        Camera.copySnapshot(this._camera.state, this.camera.state)
+        Viewport.set(this._camera.viewport, 0, 0, this._width, this._height)
+        this._camera.update()
+
+        this.renderer.setViewport(0, 0, this._width, this._height);
+
+        if (this.multiSample.enabled) {
+            this.multiSample.render(false)
+            this._colorTarget = this.multiSample.colorTarget
+        } else {
+            this.drawPass.render(false)
+            if (this.postprocessing.enabled) {
+                this.postprocessing.render(false)
+                this._colorTarget = this.postprocessing.target
+            } else {
+                this._colorTarget = this.drawPass.colorTarget
+            }
+        }
+    }
+
+    getImageData(width: number, height: number) {
+        this.setSize(width, height)
+        this.render()
+        const pd = this.colorTarget.getPixelData()
+        return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height)
+    }
+}

+ 23 - 8
src/mol-canvas3d/passes/multi-sample.ts

@@ -54,6 +54,7 @@ export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
 
 export class MultiSamplePass {
     props: MultiSampleProps
+    colorTarget: RenderTarget
 
     private composeTarget: RenderTarget
     private holdTarget: RenderTarget
@@ -65,6 +66,7 @@ export class MultiSamplePass {
 
     constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
         const { gl } = webgl
+        this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
         this.composeTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
         this.holdTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
         this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
@@ -92,6 +94,7 @@ export class MultiSamplePass {
     }
 
     setSize(width: number, height: number) {
+        this.colorTarget.setSize(width, height)
         this.composeTarget.setSize(width, height)
         this.holdTarget.setSize(width, height)
         ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height))
@@ -102,15 +105,15 @@ export class MultiSamplePass {
         if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
     }
 
-    render() {
+    render(toDrawingBuffer: boolean) {
         if (this.props.mode === 'temporal') {
-            this.renderTemporalMultiSample()
+            this.renderTemporalMultiSample(toDrawingBuffer)
         } else {
-            this.renderMultiSample()
+            this.renderMultiSample(toDrawingBuffer)
         }
     }
 
-    private renderMultiSample() {
+    private renderMultiSample(toDrawingBuffer: boolean) {
         const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
         const { gl, state } = webgl
 
@@ -168,7 +171,11 @@ export class MultiSamplePass {
         ValueCell.update(compose.values.tColor, composeTarget.texture)
         compose.update()
 
-        webgl.unbindFramebuffer()
+        if (toDrawingBuffer) {
+            webgl.unbindFramebuffer()
+        } else {
+            this.colorTarget.bind()
+        }
         gl.viewport(0, 0, width, height)
         state.disable(gl.BLEND)
         compose.render()
@@ -177,7 +184,7 @@ export class MultiSamplePass {
         camera.update()
     }
 
-    private renderTemporalMultiSample() {
+    private renderTemporalMultiSample(toDrawingBuffer: boolean) {
         const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
         const { gl, state } = webgl
 
@@ -252,7 +259,11 @@ export class MultiSamplePass {
             ValueCell.update(compose.values.uWeight, 1.0)
             ValueCell.update(compose.values.tColor, composeTarget.texture)
             compose.update()
-            webgl.unbindFramebuffer()
+            if (toDrawingBuffer) {
+                webgl.unbindFramebuffer()
+            } else {
+                this.colorTarget.bind()
+            }
             gl.viewport(0, 0, width, height)
             state.disable(gl.BLEND)
             compose.render()
@@ -261,7 +272,11 @@ export class MultiSamplePass {
             ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight)
             ValueCell.update(compose.values.tColor, holdTarget.texture)
             compose.update()
-            webgl.unbindFramebuffer()
+            if (toDrawingBuffer) {
+                webgl.unbindFramebuffer()
+            } else {
+                this.colorTarget.bind()
+            }
             gl.viewport(0, 0, width, height)
             if (accumulationWeight === 0) state.disable(gl.BLEND)
             else state.enable(gl.BLEND)

+ 6 - 5
src/mol-canvas3d/passes/pick.ts

@@ -10,6 +10,7 @@ import Renderer from '../../mol-gl/renderer';
 import Scene from '../../mol-gl/scene';
 import { PickingId } from '../../mol-geo/geometry/picking';
 import { decodeFloatRGB } from '../../mol-util/float-packing';
+import { Camera } from '../camera';
 
 export class PickPass {
     pickDirty = true
@@ -26,7 +27,7 @@ export class PickPass {
     private pickWidth: number
     private pickHeight: number
 
-    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private pickBaseScale: number) {
+    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
         const { gl } = webgl
         const width = gl.drawingBufferWidth
         const height = gl.drawingBufferHeight
@@ -64,14 +65,14 @@ export class PickPass {
     }
 
     render() {
-        const { renderer, scene } = this
+        const { renderer, scene, camera } = this
         renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
         this.objectPickTarget.bind();
-        renderer.render(scene, 'pickObject', true);
+        renderer.render(scene, camera, 'pickObject', true);
         this.instancePickTarget.bind();
-        renderer.render(scene, 'pickInstance', true);
+        renderer.render(scene, camera, 'pickInstance', true);
         this.groupPickTarget.bind();
-        renderer.render(scene, 'pickGroup', true);
+        renderer.render(scene, camera, 'pickGroup', true);
 
         this.pickDirty = false
     }

+ 1 - 1
src/mol-gl/_spec/renderer.spec.ts

@@ -30,7 +30,7 @@ function createRenderer(gl: WebGLRenderingContext) {
     const camera = new Camera({
         position: Vec3.create(0, 0, 50)
     })
-    const renderer = Renderer.create(ctx, camera)
+    const renderer = Renderer.create(ctx)
     return { ctx, camera, renderer }
 }
 

+ 21 - 20
src/mol-gl/renderer.ts

@@ -38,7 +38,7 @@ interface Renderer {
     readonly props: Readonly<RendererProps>
 
     clear: () => void
-    render: (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => void
+    render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
     setProps: (props: Partial<RendererProps>) => void
     setViewport: (x: number, y: number, width: number, height: number) => void
     dispose: () => void
@@ -60,39 +60,40 @@ export const RendererParams = {
 export type RendererProps = PD.Values<typeof RendererParams>
 
 namespace Renderer {
-    export function create(ctx: WebGLContext, camera: Camera, props: Partial<RendererProps> = {}): Renderer {
+    export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
         const { gl, state, stats } = ctx
         const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
 
         const viewport = Viewport()
         const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
 
-        const view = Mat4.clone(camera.view)
-        const invView = Mat4.invert(Mat4.identity(), view)
-        const modelView = Mat4.clone(camera.view)
-        const invModelView = Mat4.invert(Mat4.identity(), modelView)
-        const invProjection = Mat4.invert(Mat4.identity(), camera.projection)
-        const modelViewProjection = Mat4.mul(Mat4.identity(), modelView, camera.projection)
-        const invModelViewProjection = Mat4.invert(Mat4.identity(), modelViewProjection)
+        const view = Mat4()
+        const invView = Mat4()
+        const modelView = Mat4()
+        const invModelView = Mat4()
+        const invProjection = Mat4()
+        const modelViewProjection = Mat4()
+        const invModelViewProjection = Mat4()
 
-        const viewOffset = camera.viewOffset.enabled ? Vec2.create(camera.viewOffset.offsetX * 16, camera.viewOffset.offsetY * 16) : Vec2()
+        const viewOffset = Vec2()
 
         const globalUniforms: GlobalUniformValues = {
             uModel: ValueCell.create(Mat4.identity()),
-            uView: ValueCell.create(camera.view),
+            uView: ValueCell.create(view),
             uInvView: ValueCell.create(invView),
             uModelView: ValueCell.create(modelView),
             uInvModelView: ValueCell.create(invModelView),
             uInvProjection: ValueCell.create(invProjection),
-            uProjection: ValueCell.create(Mat4.clone(camera.projection)),
+            uProjection: ValueCell.create(Mat4()),
             uModelViewProjection: ValueCell.create(modelViewProjection),
             uInvModelViewProjection: ValueCell.create(invModelViewProjection),
 
-            uIsOrtho: ValueCell.create(camera.state.mode === 'orthographic' ? 1 : 0),
+            uIsOrtho: ValueCell.create(1),
+            uViewOffset: ValueCell.create(viewOffset),
+
             uPixelRatio: ValueCell.create(ctx.pixelRatio),
             uViewportHeight: ValueCell.create(viewport.height),
             uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
-            uViewOffset: ValueCell.create(viewOffset),
 
             uLightIntensity: ValueCell.create(p.lightIntensity),
             uAmbientIntensity: ValueCell.create(p.ambientIntensity),
@@ -101,11 +102,11 @@ namespace Renderer {
             uRoughness: ValueCell.create(p.roughness),
             uReflectivity: ValueCell.create(p.reflectivity),
 
-            uCameraPosition: ValueCell.create(Vec3.clone(camera.state.position)),
-            uNear: ValueCell.create(camera.near),
-            uFar: ValueCell.create(camera.far),
-            uFogNear: ValueCell.create(camera.fogNear),
-            uFogFar: ValueCell.create(camera.fogFar),
+            uCameraPosition: ValueCell.create(Vec3()),
+            uNear: ValueCell.create(1),
+            uFar: ValueCell.create(10000),
+            uFogNear: ValueCell.create(1),
+            uFogFar: ValueCell.create(10000),
             uFogColor: ValueCell.create(bgColor),
 
             uTransparentBackground: ValueCell.create(p.transparentBackground ? 1 : 0),
@@ -160,7 +161,7 @@ namespace Renderer {
             }
         }
 
-        const render = (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => {
+        const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
             ValueCell.update(globalUniforms.uModel, scene.view)
             ValueCell.update(globalUniforms.uView, camera.view)
             ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))