From 93cc8221d9174cb66c7377844d191e6e1bf5ec87 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Thu, 10 Jan 2019 17:11:04 -0800 Subject: [PATCH] wip, spheres renderable --- src/mol-geo/geometry/geometry.ts | 3 + .../geometry/spheres/spheres-builder.ts | 64 ++++++ src/mol-geo/geometry/spheres/spheres.ts | 148 ++++++++++++++ src/mol-gl/render-object.ts | 29 ++- src/mol-gl/renderable/spheres.ts | 38 ++++ src/mol-gl/renderable/util.ts | 4 +- src/mol-gl/shader-code.ts | 6 + src/mol-gl/shader/spheres.frag | 187 ++++++++++++++++++ src/mol-gl/shader/spheres.vert | 104 ++++++++++ src/mol-math/geometry/primitives/sphere3d.ts | 6 + 10 files changed, 581 insertions(+), 8 deletions(-) create mode 100644 src/mol-geo/geometry/spheres/spheres-builder.ts create mode 100644 src/mol-geo/geometry/spheres/spheres.ts create mode 100644 src/mol-gl/renderable/spheres.ts create mode 100644 src/mol-gl/shader/spheres.frag create mode 100644 src/mol-gl/shader/spheres.vert diff --git a/src/mol-geo/geometry/geometry.ts b/src/mol-geo/geometry/geometry.ts index bff13f79d..8b1a6b7d0 100644 --- a/src/mol-geo/geometry/geometry.ts +++ b/src/mol-geo/geometry/geometry.ts @@ -17,6 +17,7 @@ import { ParamDefinition as PD } from 'mol-util/param-definition' import { DirectVolume } from './direct-volume/direct-volume'; import { Color } from 'mol-util/color'; import { Vec3 } from 'mol-math/linear-algebra'; +import { Spheres } from './spheres/spheres'; // @@ -40,6 +41,7 @@ export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [Visua export type GeometryKindType = { 'mesh': Mesh, 'points': Points, + 'spheres': Spheres, 'lines': Lines, 'direct-volume': DirectVolume, } @@ -51,6 +53,7 @@ export namespace Geometry { switch (geometry.kind) { case 'mesh': return geometry.triangleCount * 3 case 'points': return geometry.pointCount + case 'spheres': return geometry.sphereCount * 2 * 3 case 'lines': return geometry.lineCount * 2 * 3 case 'direct-volume': return 12 * 3 } diff --git a/src/mol-geo/geometry/spheres/spheres-builder.ts b/src/mol-geo/geometry/spheres/spheres-builder.ts new file mode 100644 index 000000000..39db630f8 --- /dev/null +++ b/src/mol-geo/geometry/spheres/spheres-builder.ts @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ValueCell } from 'mol-util/value-cell' +import { ChunkedArray } from 'mol-data/util'; +import { Spheres } from './spheres'; + +const quadMapping = new Float32Array([ + -1.0, 1.0, + -1.0, -1.0, + 1.0, 1.0, + 1.0, -1.0 +]) + +const quadMappingIndices = new Uint16Array([ + 0, 1, 2, + 1, 3, 2 +]) + +export interface SpheresBuilder { + add(x: number, y: number, z: number, group: number): void + getSpheres(): Spheres +} + +export namespace SpheresBuilder { + export function create(initialCount = 2048, chunkSize = 1024, spheres?: Spheres): SpheresBuilder { + initialCount *= 4 + chunkSize *= 4 + const centers = ChunkedArray.create(Float32Array, 3, chunkSize, spheres ? spheres.centerBuffer.ref.value : initialCount); + const mappings = ChunkedArray.create(Float32Array, 2, chunkSize, spheres ? spheres.mappingBuffer.ref.value : initialCount); + const indices = ChunkedArray.create(Uint32Array, 3, chunkSize / 2, spheres ? spheres.indexBuffer.ref.value : initialCount / 2); + const groups = ChunkedArray.create(Float32Array, 1, chunkSize, spheres ? spheres.groupBuffer.ref.value : initialCount); + + return { + add: (x: number, y: number, z: number, group: number) => { + const offset = centers.elementCount + for (let i = 0; i < 4; ++i) { + ChunkedArray.add3(centers, x, y, z) + ChunkedArray.add2(mappings, quadMapping[i * 2], quadMapping[i * 2 + 1]) + ChunkedArray.add(groups, group) + } + ChunkedArray.add3(indices, offset + quadMappingIndices[0], offset + quadMappingIndices[1], offset + quadMappingIndices[2]) + ChunkedArray.add3(indices, offset + quadMappingIndices[3], offset + quadMappingIndices[4], offset + quadMappingIndices[5]) + }, + getSpheres: () => { + const cb = ChunkedArray.compact(centers, true) as Float32Array + const mb = ChunkedArray.compact(mappings, true) as Float32Array + const ib = ChunkedArray.compact(indices, true) as Uint32Array + const gb = ChunkedArray.compact(groups, true) as Float32Array + return { + kind: 'spheres', + sphereCount: centers.elementCount / 4, + centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb), + mappingBuffer: spheres ? ValueCell.update(spheres.centerBuffer, mb) : ValueCell.create(mb), + indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib), + groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb), + } + } + } + } +} \ No newline at end of file diff --git a/src/mol-geo/geometry/spheres/spheres.ts b/src/mol-geo/geometry/spheres/spheres.ts new file mode 100644 index 000000000..270409f22 --- /dev/null +++ b/src/mol-geo/geometry/spheres/spheres.ts @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { ValueCell } from 'mol-util'; +import { Geometry } from '../geometry'; +import { ParamDefinition as PD } from 'mol-util/param-definition'; +import { TransformData, createIdentityTransform } from '../transform-data'; +import { LocationIterator } from 'mol-geo/util/location-iterator'; +import { Theme } from 'mol-theme/theme'; +import { SpheresValues } from 'mol-gl/renderable/spheres'; +import { createColors, createValueColor } from '../color-data'; +import { createMarkers } from '../marker-data'; +import { calculateBoundingSphere } from 'mol-gl/renderable/util'; +import { ColorNames } from 'mol-util/color/tables'; +import { Sphere3D } from 'mol-math/geometry'; +import { createSizes, createValueSize } from '../size-data'; + +/** Spheres */ +export interface Spheres { + readonly kind: 'spheres', + + /** Number of spheres */ + sphereCount: number, + + /** Center buffer as array of xyz values wrapped in a value cell */ + readonly centerBuffer: ValueCell<Float32Array>, + /** Mapping buffer as array of xy values wrapped in a value cell */ + readonly mappingBuffer: ValueCell<Float32Array>, + /** Index buffer as array of center index triplets wrapped in a value cell */ + readonly indexBuffer: ValueCell<Uint32Array>, + /** Group buffer as array of group ids for each vertex wrapped in a value cell */ + readonly groupBuffer: ValueCell<Float32Array>, +} + +export namespace Spheres { + export function createEmpty(spheres?: Spheres): Spheres { + const cb = spheres ? spheres.centerBuffer.ref.value : new Float32Array(0) + const mb = spheres ? spheres.mappingBuffer.ref.value : new Float32Array(0) + const ib = spheres ? spheres.indexBuffer.ref.value : new Uint32Array(0) + const gb = spheres ? spheres.groupBuffer.ref.value : new Float32Array(0) + return { + kind: 'spheres', + sphereCount: 0, + centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb), + mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb), + indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib), + groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb) + } + } + + export const Params = { + ...Geometry.Params, + doubleSided: PD.Boolean(false), + } + export type Params = typeof Params + + export function createValues(spheres: Spheres, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): SpheresValues { + const { instanceCount, groupCount } = locationIt + if (instanceCount !== transform.instanceCount.ref.value) { + throw new Error('instanceCount values in TransformData and LocationIterator differ') + } + + const color = createColors(locationIt, theme.color) + const size = createSizes(locationIt, theme.size) + const marker = createMarkers(instanceCount * groupCount) + + const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount } + + const padding = 5 // TODO get max sphere size + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( + spheres.centerBuffer.ref.value, spheres.sphereCount * 4, + transform.aTransform.ref.value, instanceCount, padding + ) + + return { + aPosition: spheres.centerBuffer, + aMapping: spheres.mappingBuffer, + aGroup: spheres.groupBuffer, + elements: spheres.indexBuffer, + boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), + ...color, + ...size, + ...marker, + ...transform, + + padding: ValueCell.create(padding), + + ...Geometry.createValues(props, counts), + dDoubleSided: ValueCell.create(props.doubleSided), + } + } + + export function createValuesSimple(spheres: Spheres, props: Partial<PD.Values<Params>>, colorValue = ColorNames.grey, sizeValue = 1, transform?: TransformData): SpheresValues { + const p = { ...PD.getDefaultValues(Params), ...props } + if (!transform) transform = createIdentityTransform() + const instanceCount = transform.instanceCount.ref.value + const groupCount = 1 + const color = createValueColor(colorValue) + const size = createValueSize(sizeValue) + const marker = createMarkers(instanceCount * groupCount) + + const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount } + + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( + spheres.centerBuffer.ref.value, spheres.sphereCount * 4, + transform.aTransform.ref.value, instanceCount, sizeValue + ) + + return { + aPosition: spheres.centerBuffer, + aMapping: spheres.mappingBuffer, + aGroup: spheres.groupBuffer, + elements: spheres.indexBuffer, + boundingSphere: ValueCell.create(boundingSphere), + invariantBoundingSphere: ValueCell.create(invariantBoundingSphere), + ...color, + ...size, + ...marker, + ...transform, + + padding: ValueCell.create(sizeValue), + + ...Geometry.createValues(p, counts), + dDoubleSided: ValueCell.create(p.doubleSided), + } + } + + export function updateValues(values: SpheresValues, props: PD.Values<Params>) { + Geometry.updateValues(values, props) + } + + export function updateBoundingSphere(values: SpheresValues, spheres: Spheres) { + const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere( + values.aPosition.ref.value, spheres.sphereCount * 4, + values.aTransform.ref.value, values.instanceCount.ref.value, values.padding.ref.value + ) + if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) { + ValueCell.update(values.boundingSphere, boundingSphere) + } + if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) { + ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere) + } + } +} \ No newline at end of file diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index f4761dea4..871d7f07f 100644 --- a/src/mol-gl/render-object.ts +++ b/src/mol-gl/render-object.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -13,17 +13,26 @@ import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct- import { MeshValues, MeshRenderable } from './renderable/mesh'; import { PointsValues, PointsRenderable } from './renderable/points'; import { LinesValues, LinesRenderable } from './renderable/lines'; +import { SpheresValues, SpheresRenderable } from './renderable/spheres'; const getNextId = idFactory(0, 0x7FFFFFFF) export interface BaseRenderObject { id: number, type: string, values: RenderableValues, state: RenderableState } export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', values: MeshValues } export interface PointsRenderObject extends BaseRenderObject { type: 'points', values: PointsValues } +export interface SpheresRenderObject extends BaseRenderObject { type: 'spheres', values: SpheresValues } export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues } -export interface GaussianDensityRenderObject extends BaseRenderObject { type: 'gaussian-density', values: GaussianDensityValues } export interface DirectVolumeRenderObject extends BaseRenderObject { type: 'direct-volume', values: DirectVolumeValues } -export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject | DirectVolumeRenderObject +export interface GaussianDensityRenderObject extends BaseRenderObject { type: 'gaussian-density', values: GaussianDensityValues } + +// + +export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | LinesRenderObject | DirectVolumeRenderObject + +export type ComputeRenderObject = GaussianDensityRenderObject + +export type RenderObject = GraphicsRenderObject | ComputeRenderObject // @@ -33,22 +42,28 @@ export function createMeshRenderObject(values: MeshValues, state: RenderableStat export function createPointsRenderObject(values: PointsValues, state: RenderableState): PointsRenderObject { return { id: getNextId(), type: 'points', values, state } } +export function createSpheresRenderObject(values: SpheresValues, state: RenderableState): SpheresRenderObject { + return { id: getNextId(), type: 'spheres', values, state } +} export function createLinesRenderObject(values: LinesValues, state: RenderableState): LinesRenderObject { return { id: getNextId(), type: 'lines', values, state } } -export function createGaussianDensityRenderObject(values: GaussianDensityValues, state: RenderableState): GaussianDensityRenderObject { - return { id: getNextId(), type: 'gaussian-density', values, state } -} export function createDirectVolumeRenderObject(values: DirectVolumeValues, state: RenderableState): DirectVolumeRenderObject { return { id: getNextId(), type: 'direct-volume', values, state } } +export function createGaussianDensityRenderObject(values: GaussianDensityValues, state: RenderableState): GaussianDensityRenderObject { + return { id: getNextId(), type: 'gaussian-density', values, state } +} + export function createRenderable(ctx: WebGLContext, o: RenderObject): Renderable<any> { switch (o.type) { case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state) case 'points': return PointsRenderable(ctx, o.id, o.values, o.state) + case 'spheres': return SpheresRenderable(ctx, o.id, o.values, o.state) case 'lines': return LinesRenderable(ctx, o.id, o.values, o.state) - case 'gaussian-density': return GaussianDensityRenderable(ctx, o.id, o.values, o.state) case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values, o.state) + + case 'gaussian-density': return GaussianDensityRenderable(ctx, o.id, o.values, o.state) } } \ No newline at end of file diff --git a/src/mol-gl/renderable/spheres.ts b/src/mol-gl/renderable/spheres.ts new file mode 100644 index 000000000..4cb132301 --- /dev/null +++ b/src/mol-gl/renderable/spheres.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Renderable, RenderableState, createRenderable } from '../renderable' +import { WebGLContext } from '../webgl/context'; +import { createRenderItem } from '../webgl/render-item'; +import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec } from './schema'; +import { SpheresShaderCode } from '../shader-code'; +import { ValueCell } from 'mol-util'; + +export const SpheresSchema = { + ...BaseSchema, + ...SizeSchema, + aPosition: AttributeSpec('float32', 3, 0), + aMapping: AttributeSpec('float32', 2, 0), + elements: ElementsSpec('uint32'), + + padding: ValueSpec('number'), + dDoubleSided: DefineSpec('boolean'), +} +export type SpheresSchema = typeof SpheresSchema +export type SpheresValues = Values<SpheresSchema> + +export function SpheresRenderable(ctx: WebGLContext, id: number, values: SpheresValues, state: RenderableState): Renderable<SpheresValues> { + const schema = { ...GlobalUniformSchema, ...InternalSchema, ...SpheresSchema } + const internalValues: InternalValues = { + uObjectId: ValueCell.create(id), + uPickable: ValueCell.create(state.pickable ? 1 : 0) + } + const shaderCode = SpheresShaderCode + const renderItem = createRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }) + const renderable = createRenderable(renderItem, values, state); + + return renderable +} \ No newline at end of file diff --git a/src/mol-gl/renderable/util.ts b/src/mol-gl/renderable/util.ts index 1bfdc6ee5..73a60a38a 100644 --- a/src/mol-gl/renderable/util.ts +++ b/src/mol-gl/renderable/util.ts @@ -89,8 +89,10 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere return boundaryHelper.getSphere() } -export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } { +export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } { const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount) const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount) + Sphere3D.expand(boundingSphere, boundingSphere, padding) + Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding) return { boundingSphere, invariantBoundingSphere } } \ No newline at end of file diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 49467749f..539bf98c9 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -36,6 +36,12 @@ export const PointsShaderCode = ShaderCode( { standardDerivatives: false, fragDepth: false } ) +export const SpheresShaderCode = ShaderCode( + require('mol-gl/shader/spheres.vert'), + require('mol-gl/shader/spheres.frag'), + { standardDerivatives: false, fragDepth: true } +) + export const LinesShaderCode = ShaderCode( require('mol-gl/shader/lines.vert'), require('mol-gl/shader/lines.frag'), diff --git a/src/mol-gl/shader/spheres.frag b/src/mol-gl/shader/spheres.frag new file mode 100644 index 000000000..fc215b5ad --- /dev/null +++ b/src/mol-gl/shader/spheres.frag @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +precision highp float; +precision highp int; + +#pragma glslify: import('./chunks/common-frag-params.glsl') +#pragma glslify: import('./chunks/color-frag-params.glsl') + +// uniform vec3 uLightPosition; +uniform vec3 uLightColor; +uniform vec3 uLightAmbient; +uniform mat4 uView; + +uniform mat4 uProjection; +// uniform vec3 interiorColor; +// uniform float interiorDarkening; +vec3 interiorColor = vec3(1.0, 0.5, 0.5); +float interiorDarkening = 0.0; + +uniform float clipNear; +// uniform float ortho; +float ortho = 0.0; + +varying float vRadius; +varying float vRadiusSq; +varying vec3 vPoint; +varying vec3 vPointViewPosition; + +#pragma glslify: attenuation = require(./utils/attenuation.glsl) +#pragma glslify: calculateSpecular = require(./utils/phong-specular.glsl) +#pragma glslify: calculateDiffuse = require(./utils/oren-nayar-diffuse.glsl) + +const float specularScale = 0.15; +const float shininess = 200.0; +const float roughness = 100.0; +const float albedo = 0.95; + +bool flag2 = false; +bool interior = false; +vec3 cameraPos; +vec3 cameraNormal; + +// Calculate depth based on the given camera position. +float calcDepth(in vec3 cameraPos){ + vec2 clipZW = cameraPos.z * uProjection[2].zw + uProjection[3].zw; + return 0.5 + 0.5 * clipZW.x / clipZW.y; +} + +float calcClip(in vec3 cameraPos) { + return dot(vec4(cameraPos, 1.0), vec4(0.0, 0.0, 1.0, clipNear - 0.5)); +} + +bool Impostor(out vec3 cameraPos, out vec3 cameraNormal){ + vec3 cameraSpherePos = -vPointViewPosition; + cameraSpherePos.z += vRadius; + + vec3 rayOrigin = mix(vec3(0.0, 0.0, 0.0), vPoint, ortho); + vec3 rayDirection = mix(normalize(vPoint), vec3(0.0, 0.0, 1.0), ortho); + vec3 cameraSphereDir = mix(cameraSpherePos, rayOrigin - cameraSpherePos, ortho); + + float B = dot(rayDirection, cameraSphereDir); + float det = B * B + vRadiusSq - dot(cameraSphereDir, cameraSphereDir); + + if(det < 0.0){ + discard; + return false; + } + + float sqrtDet = sqrt(det); + float posT = mix(B + sqrtDet, B + sqrtDet, ortho); + float negT = mix(B - sqrtDet, sqrtDet - B, ortho); + + cameraPos = rayDirection * negT + rayOrigin; + + #ifdef NEAR_CLIP + if(calcDepth(cameraPos) <= 0.0){ + cameraPos = rayDirection * posT + rayOrigin; + interior = true; + }else if(calcClip(cameraPos) > 0.0){ + cameraPos = rayDirection * posT + rayOrigin; + interior = true; + flag2 = true; + } + #else + if(calcDepth(cameraPos) <= 0.0){ + cameraPos = rayDirection * posT + rayOrigin; + interior = true; + } + #endif + + cameraNormal = normalize(cameraPos - cameraSpherePos); + cameraNormal *= float(!interior) * 2.0 - 1.0; + + return !interior; +} + +void main2(void){ + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} + +void main(void){ + bool flag = Impostor(cameraPos, cameraNormal); + + #ifdef NEAR_CLIP + if(calcClip(cameraPos) > 0.0) + discard; + #endif + + // FIXME not compatible with custom clipping plane + // Set the depth based on the new cameraPos. + gl_FragDepthEXT = calcDepth(cameraPos); + if(!flag){ + // clamp to near clipping plane and add a tiny value to + // make spheres with a greater radius occlude smaller ones + #ifdef NEAR_CLIP + if( flag2 ){ + gl_FragDepthEXT = max(0.0, calcDepth(vec3(-(clipNear - 0.5))) + (0.0000001 / vRadius)); + }else if(gl_FragDepthEXT >= 0.0){ + gl_FragDepthEXT = 0.0 + (0.0000001 / vRadius); + } + #else + if(gl_FragDepthEXT >= 0.0){ + gl_FragDepthEXT = 0.0 + (0.0000001 / vRadius); + } + #endif + } + + // bugfix (mac only?) + if (gl_FragDepthEXT < 0.0) + discard; + if (gl_FragDepthEXT > 1.0) + discard; + + // material color + #pragma glslify: import('./chunks/assign-material-color.glsl') + + #if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking) + if (uAlpha < uPickingAlphaThreshold) + discard; // ignore so the element below can be picked + gl_FragColor = material; + #else + + vec3 vNormal = cameraNormal; + vec3 vViewPosition = -cameraPos; + + // determine surface to light direction + // vec4 viewLightPosition = view * vec4(lightPosition, 1.0); + // vec3 lightVector = viewLightPosition.xyz - vViewPosition; + vec3 lightVector = vViewPosition; + + vec3 L = normalize(lightVector); // light direction + vec3 V = normalize(vViewPosition); // eye direction + + vec3 N = normalize(vNormal); + #ifdef dDoubleSided + N = N * (float(gl_FrontFacing) * 2.0 - 1.0); + #endif + + // compute our diffuse & specular terms + float specular = calculateSpecular(L, V, N, shininess) * specularScale; + vec3 diffuse = uLightColor * calculateDiffuse(L, V, N, roughness, albedo); + vec3 ambient = uLightAmbient; + + // add the lighting + vec3 finalColor = material.rgb * (diffuse + ambient) + specular; + + // gl_FragColor.rgb = N; + // gl_FragColor.a = 1.0; + // gl_FragColor.rgb = vec3(1.0, 0.0, 0.0); + gl_FragColor.rgb = finalColor; + gl_FragColor.a = material.a; + + if(interior){ + #ifdef USE_INTERIOR_COLOR + gl_FragColor.rgb = interiorColor; + #endif + gl_FragColor.rgb *= 1.0 - interiorDarkening; + } + + #pragma glslify: import('./chunks/apply-marker-color.glsl') + #pragma glslify: import('./chunks/apply-fog.glsl') + #endif +} \ No newline at end of file diff --git a/src/mol-gl/shader/spheres.vert b/src/mol-gl/shader/spheres.vert new file mode 100644 index 000000000..4c0740262 --- /dev/null +++ b/src/mol-gl/shader/spheres.vert @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +precision highp float; +precision highp int; + +#pragma glslify: import('./chunks/common-vert-params.glsl') +#pragma glslify: import('./chunks/color-vert-params.glsl') +#pragma glslify: import('./chunks/size-vert-params.glsl') + +uniform mat4 uModelView; +uniform mat4 uInvProjection; + +attribute vec3 aPosition; +attribute vec2 aMapping; +attribute mat4 aTransform; +attribute float aInstance; +attribute float aGroup; + +varying float vRadius; +varying float vRadiusSq; +varying vec3 vPoint; +varying vec3 vPointViewPosition; + +#pragma glslify: matrixScale = require(./utils/matrix-scale.glsl) + +const mat4 D = mat4( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, -1.0 +); + +mat4 transpose2(in mat4 inMatrix) { + vec4 i0 = inMatrix[0]; + vec4 i1 = inMatrix[1]; + vec4 i2 = inMatrix[2]; + vec4 i3 = inMatrix[3]; + + mat4 outMatrix = mat4( + vec4(i0.x, i1.x, i2.x, i3.x), + vec4(i0.y, i1.y, i2.y, i3.y), + vec4(i0.z, i1.z, i2.z, i3.z), + vec4(i0.w, i1.w, i2.w, i3.w) + ); + return outMatrix; +} + +/** + * Compute point size and center using the technique described in: + * "GPU-Based Ray-Casting of Quadratic Surfaces" http://dl.acm.org/citation.cfm?id=2386396 + * by Christian Sigg, Tim Weyrich, Mario Botsch, Markus Gross. + */ +void quadraticProjection(const in float radius, const in vec3 position){ + vec2 xbc, ybc; + + mat4 T = mat4( + radius, 0.0, 0.0, 0.0, + 0.0, radius, 0.0, 0.0, + 0.0, 0.0, radius, 0.0, + position.x, position.y, position.z, 1.0 + ); + + mat4 R = transpose2(uProjection * uModelView * aTransform * T); + float A = dot(R[3], D * R[3]); + float B = -2.0 * dot(R[0], D * R[3]); + float C = dot(R[0], D * R[0]); + xbc[0] = (-B - sqrt(B * B - 4.0 * A * C)) / (2.0 * A); + xbc[1] = (-B + sqrt(B * B - 4.0 * A * C)) / (2.0 * A); + float sx = abs(xbc[0] - xbc[1]) * 0.5; + + A = dot(R[3], D * R[3]); + B = -2.0 * dot(R[1], D * R[3]); + C = dot(R[1], D * R[1]); + ybc[0] = (-B - sqrt(B * B - 4.0 * A * C)) / (2.0 * A); + ybc[1] = (-B + sqrt(B * B - 4.0 * A * C)) / (2.0 * A); + float sy = abs(ybc[0] - ybc[1]) * 0.5; + + gl_Position.xy = vec2(0.5 * (xbc.x + xbc.y), 0.5 * (ybc.x + ybc.y)); + gl_Position.xy -= aMapping * vec2(sx, sy); + gl_Position.xy *= gl_Position.w; +} + + +void main(void){ + #pragma glslify: import('./chunks/assign-color-varying.glsl') + #pragma glslify: import('./chunks/assign-size.glsl') + + vRadius = size * matrixScale(uModelView); + + vec4 mvPosition = uModelView * aTransform * vec4(aPosition, 1.0); + mvPosition.z -= vRadius; // avoid clipping, added again in fragment shader + + gl_Position = uProjection * vec4(mvPosition.xyz, 1.0); + quadraticProjection(size, aPosition); + + vRadiusSq = vRadius * vRadius; + vec4 vPoint4 = uInvProjection * gl_Position; + vPoint = vPoint4.xyz / vPoint4.w; + vPointViewPosition = -mvPosition.xyz / mvPosition.w; +} \ No newline at end of file diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts index ef6c3c685..5dbbf1315 100644 --- a/src/mol-math/geometry/primitives/sphere3d.ts +++ b/src/mol-math/geometry/primitives/sphere3d.ts @@ -80,6 +80,12 @@ namespace Sphere3D { return out } + /** Expand sphere by delta */ + export function expand(out: Sphere3D, sphere: Sphere3D, delta: number): Sphere3D { + out.radius = sphere.radius + delta + return out + } + /** * Returns whether or not the spheres have exactly the same center and radius (when compared with ===) */ -- GitLab