Skip to content
Snippets Groups Projects
Commit 93cc8221 authored by Alexander Rose's avatar Alexander Rose
Browse files

wip, spheres renderable

parent 3a68f977
No related branches found
No related tags found
No related merge requests found
......@@ -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
}
......
/**
* 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
/**
* 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
/**
* 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
/**
* 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
......@@ -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
......@@ -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'),
......
/**
* 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
/**
* 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
......@@ -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 ===)
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment