Bladeren bron

basic transparency

Alexander Rose 7 jaren geleden
bovenliggende
commit
8ec1f63ca1

+ 8 - 6
src/apps/render-test/state.ts

@@ -40,8 +40,8 @@ export type ColorTheme = keyof typeof ColorTheme
 
 export default class State {
     viewer: Viewer
-    pdbId = ''
-    emdId = '8689'
+    pdbId = '5ire'
+    emdId = '8116'
     model = new BehaviorSubject<Model | undefined>(undefined)
     volume = new BehaviorSubject<Volume | undefined>(undefined)
     initialized = new BehaviorSubject<boolean>(false)
@@ -115,7 +115,8 @@ export default class State {
         const { viewer } = this
         if (!viewer || !this.model.getValue()) return
 
-        viewer.clear()
+        if (this.pointRepr) this.viewer.remove(this.pointRepr)
+        if (this.spacefillRepr) this.viewer.remove(this.spacefillRepr)
 
         const structure = await this.getStructure()
         if (!structure) return
@@ -150,7 +151,7 @@ export default class State {
         const v = this.volume.getValue()
         if (!viewer || !v) return
 
-        viewer.clear()
+        if (this.surfaceRepr) this.viewer.remove(this.surfaceRepr)
 
         this.surfaceRepr = VolumeRepresentation(Surface)
         await Run(this.surfaceRepr.create(v.volume, { isoValue: VolumeIsoValue.relative(v.volume.dataStats, 1.5) }), log, 500)
@@ -161,7 +162,8 @@ export default class State {
     }
 
     async loadPdbId () {
-        this.viewer.clear()
+        if (this.pointRepr) this.viewer.remove(this.pointRepr)
+        if (this.spacefillRepr) this.viewer.remove(this.spacefillRepr)
         if (this.pdbId.length !== 4) return
         this.loading.next(true)
         this.setModel((await getModelFromPdbId(this.pdbId))[0])
@@ -174,7 +176,7 @@ export default class State {
     }
 
     async loadEmdId () {
-        this.viewer.clear()
+        if (this.surfaceRepr) this.viewer.remove(this.surfaceRepr)
         if (this.emdId.length !== 4) return
         this.loading.next(true)
         this.setVolume(await getVolumeFromEmdId(this.emdId))

+ 4 - 2
src/mol-geo/representation/structure/point.ts

@@ -19,7 +19,8 @@ import { deepEqual } from 'mol-util';
 
 export const DefaultPointProps = {
     colorTheme: { name: 'instance-index' } as ColorTheme,
-    sizeTheme: { name: 'vdw' } as SizeTheme
+    sizeTheme: { name: 'vdw' } as SizeTheme,
+    alpha: 1
 }
 export type PointProps = Partial<typeof DefaultPointProps>
 
@@ -59,7 +60,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
                 _units = units
                 _elementGroup = elementGroup
 
-                const { colorTheme, sizeTheme } = curProps
+                const { colorTheme, sizeTheme, alpha } = curProps
                 const elementCount = OrderedSet.size(elementGroup.elements)
                 const unitCount = units.length
 
@@ -84,6 +85,7 @@ export default function Point(): UnitsRepresentation<PointProps> {
 
                 points = createPointRenderObject({
                     objectId: 0,
+                    alpha,
 
                     position: ValueCell.create(vertices),
                     id: ValueCell.create(fillSerial(new Float32Array(elementCount))),

+ 3 - 1
src/mol-geo/representation/structure/spacefill.ts

@@ -23,6 +23,7 @@ import { icosahedronVertexCount } from '../../primitive/icosahedron';
 export const DefaultSpacefillProps = {
     detail: 0,
     colorTheme: { name: 'instance-index' } as ColorTheme,
+    alpha: 1
 }
 export type SpacefillProps = Partial<typeof DefaultSpacefillProps>
 
@@ -78,7 +79,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
             return Task.create('Spacefill.create', async ctx => {
                 renderObjects.length = 0 // clear
 
-                const { detail, colorTheme } = { ...DefaultSpacefillProps, ...props }
+                const { detail, colorTheme, alpha } = { ...DefaultSpacefillProps, ...props }
 
                 await ctx.update('Computing spacefill mesh');
                 const mesh = await ctx.runChild(createSpacefillMesh(units[0], elementGroup, detail))
@@ -94,6 +95,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
 
                 spheres = createMeshRenderObject({
                     objectId: 0,
+                    alpha,
 
                     position: mesh.vertexBuffer,
                     normal: mesh.normalBuffer as ValueCell<Float32Array>,

+ 4 - 1
src/mol-geo/representation/volume/surface.ts

@@ -34,7 +34,8 @@ export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValu
 }
 
 export const DefaultSurfaceProps = {
-    isoValue: VolumeIsoValue.relative({ min: 0, max: 0, mean: 0, sigma: 0 }, 0)
+    isoValue: VolumeIsoValue.relative({ min: 0, max: 0, mean: 0, sigma: 0 }, 0),
+    alpha: 0.5
 }
 export type SurfaceProps = Partial<typeof DefaultSurfaceProps>
 
@@ -49,11 +50,13 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
             return Task.create('Point.create', async ctx => {
                 renderObjects.length = 0 // clear
                 curProps = { ...DefaultSurfaceProps, ...props }
+                const { alpha } = curProps
 
                 const mesh = await ctx.runChild(computeVolumeSurface(volume, curProps.isoValue))
 
                 surface = createMeshRenderObject({
                     objectId: 0,
+                    alpha,
 
                     position: mesh.vertexBuffer,
                     normal: mesh.normalBuffer,

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

@@ -50,6 +50,7 @@ function createPoints() {
 
     return createPointRenderObject({
         objectId: 0,
+        alpha: 1.0,
 
         position,
         id,

+ 1 - 0
src/mol-gl/renderable/mesh.ts

@@ -18,6 +18,7 @@ type Mesh = 'mesh'
 namespace Mesh {
     export type Props = {
         objectId: number
+        alpha: number
 
         position: ValueCell<Float32Array>
         normal: ValueCell<Float32Array | undefined>

+ 1 - 0
src/mol-gl/renderable/point.ts

@@ -19,6 +19,7 @@ type Point = 'point'
 namespace Point {
     export type Props = {
         objectId: number
+        alpha: number
 
         position: ValueCell<Float32Array>
         id: ValueCell<Float32Array>

+ 4 - 2
src/mol-gl/renderable/util.ts

@@ -65,6 +65,7 @@ interface BaseProps {
     instanceCount: number,
     elementCount: number,
     positionCount: number,
+    alpha: number,
 
     position: ValueCell<Float32Array>
     normal?: ValueCell<Float32Array | undefined>
@@ -87,6 +88,7 @@ export function getBaseUniformDefs(props: BaseProps) {
         // light_position: 'v3',
         light_color: 'v3',
         light_ambient: 'v3',
+        alpha: 'f',
 
         objectId: 'i',
         instanceCount: 'i',
@@ -107,9 +109,9 @@ export function getBaseUniformDefs(props: BaseProps) {
 }
 
 export function getBaseUniformValues(props: BaseProps) {
-    const { objectId, instanceCount, elementCount } = props
+    const { objectId, instanceCount, elementCount, alpha } = props
     const uniformValues: UniformValues = {
-        objectId, instanceCount, elementCount
+        objectId, instanceCount, elementCount, alpha
     }
     const color = props.color
     if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {

+ 54 - 26
src/mol-gl/renderer.ts

@@ -11,6 +11,8 @@ import { Camera } from 'mol-view/camera/base';
 import Scene, { RenderObject } from './scene';
 import { Context } from './webgl/context';
 import { Mat4, Vec3 } from 'mol-math/linear-algebra';
+import { Renderable } from './renderable';
+import { Color } from 'mol-util/color';
 
 export interface RendererStats {
     renderableCount: number
@@ -29,6 +31,7 @@ interface Renderer {
     draw: () => void
 
     setViewport: (viewport: Viewport) => void
+    setClearColor: (color: Color) => void
 
     stats: RendererStats
     dispose: () => void
@@ -38,47 +41,69 @@ function getPixelRatio() {
     return (typeof window !== 'undefined') ? window.devicePixelRatio : 1
 }
 
+export const DefaultRendererProps = {
+    clearColor: 0x000000 as Color,
+    viewport: Viewport.create(0, 0, 0, 0)
+}
+export type RendererProps = Partial<typeof DefaultRendererProps>
+
 namespace Renderer {
-    export function create(ctx: Context, camera: Camera): Renderer {
+    export function create(ctx: Context, camera: Camera, props: RendererProps = {}): Renderer {
         const { gl } = ctx
+        let { clearColor, viewport: _viewport } = { ...DefaultRendererProps, ...props }
         const scene = Scene.create(ctx)
 
         const model = Mat4.identity()
-        const viewport = Viewport.create(0, 0, 0, 0)
+        const viewport = Viewport.clone(_viewport)
         const pixelRatio = getPixelRatio()
 
         // const light_position = Vec3.create(0, 0, -100)
         const light_color = Vec3.create(1.0, 1.0, 1.0)
         const light_ambient = Vec3.create(0.5, 0.5, 0.5)
 
+        function setClearColor(color: Color) {
+            const [ r, g, b ] = Color.toRgbNormalized(color)
+            gl.clearColor(r, g, b, 1.0)
+        }
+        setClearColor(clearColor)
+
+        let currentProgramId = -1
+        const drawObject = (r: Renderable<any>, o: RenderObject) => {
+            if (o.visible) {
+                if (currentProgramId !== r.program.id) {
+                    r.program.use()
+                    r.program.setUniforms({
+                        model,
+                        view: camera.view,
+                        projection: camera.projection,
+
+                        pixelRatio,
+                        viewportHeight: viewport.height,
+
+                        // light_position,
+                        light_color,
+                        light_ambient,
+                    })
+                    currentProgramId = r.program.id
+                }
+                r.draw()
+            }
+        }
+
         const draw = () => {
-            // TODO clear color
+            currentProgramId = -1
+
+            gl.depthMask(true)
             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
+
+            gl.disable(gl.BLEND)
             gl.enable(gl.DEPTH_TEST)
+            scene.eachOpaque(drawObject)
 
-            // TODO painters sort, filter visible, filter picking, visibility culling?
-            let currentProgramId = -1
-            scene.forEach((r, o) => {
-                if (o.visible) {
-                    if (currentProgramId !== r.program.id) {
-                        r.program.use()
-                        r.program.setUniforms({
-                            model,
-                            view: camera.view,
-                            projection: camera.projection,
-
-                            pixelRatio,
-                            viewportHeight: viewport.height,
-
-                            // light_position,
-                            light_color,
-                            light_ambient,
-                        })
-                        currentProgramId = r.program.id
-                    }
-                    r.draw()
-                }
-            })
+            gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
+            gl.enable(gl.BLEND)
+            gl.depthMask(false)
+            scene.eachTransparent(drawObject)
         }
 
         return {
@@ -95,10 +120,13 @@ namespace Renderer {
                 scene.clear()
             },
             draw,
+
+            setClearColor,
             setViewport: (newViewport: Viewport) => {
                 Viewport.copy(viewport, newViewport)
                 gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height)
             },
+
             get stats(): RendererStats {
                 return {
                     renderableCount: scene.count,

+ 15 - 3
src/mol-gl/scene.ts

@@ -16,16 +16,16 @@ function getNextId() {
 
 export type RenderData = { [k: string]: ValueCell<Helpers.TypedArray> }
 
-export interface BaseRenderObject { id: number, type: string, props: {}, visible: boolean }
+export interface BaseRenderObject { id: number, type: string, props: {}, visible: boolean, transparent: boolean }
 export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', props: MeshRenderable.Props }
 export interface PointRenderObject extends BaseRenderObject { type: 'point', props: PointRenderable.Props }
 export type RenderObject = MeshRenderObject | PointRenderObject
 
 export function createMeshRenderObject(props: MeshRenderable.Props): MeshRenderObject {
-    return { id: getNextId(), type: 'mesh', props, visible: true }
+    return { id: getNextId(), type: 'mesh', props, visible: true, transparent: props.alpha < 1 }
 }
 export function createPointRenderObject(props: PointRenderable.Props): PointRenderObject {
-    return { id: getNextId(), type: 'point', props, visible: true }
+    return { id: getNextId(), type: 'point', props, visible: true, transparent: props.alpha < 1 }
 }
 
 export function createRenderable(ctx: Context, o: RenderObject) {
@@ -40,6 +40,8 @@ interface Scene {
     remove: (o: RenderObject) => void
     clear: () => void
     forEach: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => void
+    eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => void
+    eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => void
     count: number
 }
 
@@ -69,6 +71,16 @@ namespace Scene {
             forEach: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
                 renderableMap.forEach(callbackFn)
             },
+            eachOpaque: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
+                renderableMap.forEach((r, o) => {
+                    if (!o.transparent) callbackFn(r, o)
+                })
+            },
+            eachTransparent: (callbackFn: (value: Renderable<any>, key: RenderObject) => void) => {
+                renderableMap.forEach((r, o) => {
+                    if (o.transparent) callbackFn(r, o)
+                })
+            },
             get count() {
                 return renderableMap.size
             }

+ 3 - 2
src/mol-gl/shader/mesh.frag

@@ -14,6 +14,7 @@ precision highp float;
 uniform vec3 light_color;
 uniform vec3 light_ambient;
 uniform mat4 view;
+uniform float alpha;
 
 #ifndef FLAT_SHADED
     varying vec3 vNormal;
@@ -47,7 +48,7 @@ void main() {
     #ifdef FLAT_SHADED
         vec3 fdx = dFdx(vViewPosition);
         vec3 fdy = dFdy(vViewPosition);
-        vec3 N = -normalize(cross(fdx, fdy));
+        vec3 N = normalize(cross(fdx, fdy));
     #else
         vec3 N = -normalize(vNormal);
         #ifdef DOUBLE_SIDED
@@ -66,5 +67,5 @@ void main() {
     // gl_FragColor.rgb = N;
     // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0);
     gl_FragColor.rgb = finalColor;
-    gl_FragColor.a = 1.0;
+    gl_FragColor.a = alpha;
 }

+ 3 - 1
src/mol-gl/shader/point.frag

@@ -6,9 +6,11 @@
 
 precision highp float;
 
+uniform float alpha;
+
 #pragma glslify: import('./chunks/color-frag-params.glsl')
 
 void main(){
     #pragma glslify: import('./chunks/color-assign-material.glsl')
-    gl_FragColor = vec4(material, 1.0);
+    gl_FragColor = vec4(material, alpha);
 }

+ 6 - 1
src/mol-view/viewer.ts

@@ -64,7 +64,12 @@ namespace Viewer {
 
         })
 
-        const gl = getWebGLContext(canvas)
+        const gl = getWebGLContext(canvas, {
+            alpha: true,
+            antialias: true,
+            depth: true,
+            preserveDrawingBuffer: true
+        })
         if (gl === null) {
             throw new Error('Could not create a WebGL rendering context')
         }