diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index bb22845aa08610aba040d98747a1638d647c93af..f25b0cd1fd489d2d1b3552b5c4d79456bfd059c6 100644 --- a/src/apps/render-test/state.ts +++ b/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)) diff --git a/src/mol-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index 8a9d28fa61298d8e752ad4997772254052d4e42a..5b567b3690edc44332e491ef1cc7fe16942146fd 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/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))), diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index 1362927bf4597208785b9337dcea83df5d1cb419..a4b2c485182b94c3697c1ea67d06efaaa969f457 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/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>, diff --git a/src/mol-geo/representation/volume/surface.ts b/src/mol-geo/representation/volume/surface.ts index 2298ed467b8e21682453973db92b5b8fda68eaff..fd22678fe086cd1068ab90e03f9dd0657a0bd77c 100644 --- a/src/mol-geo/representation/volume/surface.ts +++ b/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, diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index 7465af899e56b57f7f1f8dfe29a1a58feb7886f0..ff80f1218f50efd3be9b7b0de4c3f63c9e87152a 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -50,6 +50,7 @@ function createPoints() { return createPointRenderObject({ objectId: 0, + alpha: 1.0, position, id, diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index 38d202788c73b83452392fe1bbef57b71f39bacc..7aa2a1eb67715ed4735c5b4a3ac7bc53c6a95c2d 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/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> diff --git a/src/mol-gl/renderable/point.ts b/src/mol-gl/renderable/point.ts index 7ff653779138a7afd7275f48bc2e0df0143c255a..f0d4b78e0c605432bef6083095c2a251c4d4052f 100644 --- a/src/mol-gl/renderable/point.ts +++ b/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> diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 59caeafaa2676dc4dd6403b4c0686e23255ad905..87504a7db07493a68c15c6503588c0c8fea63493 100644 --- a/src/mol-gl/renderable/util.ts +++ b/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') { diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 8fe4c2707afacaaf4762aa3cd6a2c227a1c51b0b..aea1cc8386a8f994b9f69f2beec6e3c8c6fd4b5b 100644 --- a/src/mol-gl/renderer.ts +++ b/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, diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 92faf5e27e14031434515bb3706acdad921d3d5d..28789540a70daf65c75a0b7e14bbc44561637c07 100644 --- a/src/mol-gl/scene.ts +++ b/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 } diff --git a/src/mol-gl/shader/mesh.frag b/src/mol-gl/shader/mesh.frag index 89191d411a5b4691d4eea3e058d25aa9c5534150..088812ec63079cf1aa1f9ef504fa090bc705f0a3 100644 --- a/src/mol-gl/shader/mesh.frag +++ b/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; } \ No newline at end of file diff --git a/src/mol-gl/shader/point.frag b/src/mol-gl/shader/point.frag index 7afba8f5e1baf326d903e6f898495ca201ef52fd..a39cf8d9866968633113ac50252b91cc2cfcc67e 100644 --- a/src/mol-gl/shader/point.frag +++ b/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); } \ No newline at end of file diff --git a/src/mol-view/viewer.ts b/src/mol-view/viewer.ts index 22169186d2e350fae3ae7d181d53b95e189f329e..48ff8e3d4bcbdc838f932feed804449c6cd22b66 100644 --- a/src/mol-view/viewer.ts +++ b/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') }