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

wip, gpu gaussian density

parent b20b80f9
No related branches found
No related tags found
No related merge requests found
...@@ -4,10 +4,11 @@ ...@@ -4,10 +4,11 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { PointsRenderable, MeshRenderable, RenderableState, MeshValues, PointsValues, LinesValues, LinesRenderable } from './renderable' import { PointsRenderable, MeshRenderable, RenderableState, MeshValues, PointsValues, LinesValues, LinesRenderable, Renderable } from './renderable'
import { RenderableValues } from './renderable/schema'; import { RenderableValues } from './renderable/schema';
import { idFactory } from 'mol-util/id-factory'; import { idFactory } from 'mol-util/id-factory';
import { Context } from './webgl/context'; import { Context } from './webgl/context';
import { GaussianDensityValues, GaussianDensityRenderable } from './renderable/gaussian-density';
const getNextId = idFactory(0, 0x7FFFFFFF) const getNextId = idFactory(0, 0x7FFFFFFF)
...@@ -15,7 +16,8 @@ export interface BaseRenderObject { id: number, type: string, values: Renderable ...@@ -15,7 +16,8 @@ export interface BaseRenderObject { id: number, type: string, values: Renderable
export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', values: MeshValues } export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', values: MeshValues }
export interface PointsRenderObject extends BaseRenderObject { type: 'points', values: PointsValues } export interface PointsRenderObject extends BaseRenderObject { type: 'points', values: PointsValues }
export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues } export interface LinesRenderObject extends BaseRenderObject { type: 'lines', values: LinesValues }
export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject export interface GaussianDensityRenderObject extends BaseRenderObject { type: 'gaussian-density', values: GaussianDensityValues }
export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject
export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject { export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject {
return { id: getNextId(), type: 'mesh', values, state } return { id: getNextId(), type: 'mesh', values, state }
...@@ -26,11 +28,15 @@ export function createPointsRenderObject(values: PointsValues, state: Renderable ...@@ -26,11 +28,15 @@ export function createPointsRenderObject(values: PointsValues, state: Renderable
export function createLinesRenderObject(values: LinesValues, state: RenderableState): LinesRenderObject { export function createLinesRenderObject(values: LinesValues, state: RenderableState): LinesRenderObject {
return { id: getNextId(), type: 'lines', values, state } 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 createRenderable(ctx: Context, o: RenderObject) { export function createRenderable(ctx: Context, o: RenderObject): Renderable<any> {
switch (o.type) { switch (o.type) {
case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state) case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state)
case 'points': return PointsRenderable(ctx, o.id, o.values, o.state) case 'points': return PointsRenderable(ctx, o.id, o.values, o.state)
case 'lines': return LinesRenderable(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)
} }
} }
\ No newline at end of file
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
import { Program } from './webgl/program'; import { Program } from './webgl/program';
import { RenderableValues, Values, RenderableSchema, BaseValues } from './renderable/schema'; import { RenderableValues, Values, RenderableSchema } from './renderable/schema';
import { RenderVariant, RenderItem } from './webgl/render-item'; import { RenderVariant, RenderItem } from './webgl/render-item';
import { Sphere3D } from 'mol-math/geometry'; import { Sphere3D } from 'mol-math/geometry';
// import { calculateBoundingSphereFromValues } from './renderable/util'; // import { calculateBoundingSphereFromValues } from './renderable/util';
...@@ -17,7 +17,7 @@ export type RenderableState = { ...@@ -17,7 +17,7 @@ export type RenderableState = {
depthMask: boolean depthMask: boolean
} }
export interface Renderable<T extends RenderableValues & BaseValues> { export interface Renderable<T extends RenderableValues> {
readonly values: T readonly values: T
readonly state: RenderableState readonly state: RenderableState
readonly boundingSphere: Sphere3D readonly boundingSphere: Sphere3D
...@@ -29,7 +29,7 @@ export interface Renderable<T extends RenderableValues & BaseValues> { ...@@ -29,7 +29,7 @@ export interface Renderable<T extends RenderableValues & BaseValues> {
dispose: () => void dispose: () => void
} }
export function createRenderable<T extends Values<RenderableSchema> & BaseValues>(renderItem: RenderItem, values: T, state: RenderableState): Renderable<T> { export function createRenderable<T extends Values<RenderableSchema>>(renderItem: RenderItem, values: T, state: RenderableState): Renderable<T> {
// TODO // TODO
let boundingSphere: Sphere3D = Sphere3D.create(Vec3.zero(), 50) let boundingSphere: Sphere3D = Sphere3D.create(Vec3.zero(), 50)
...@@ -43,7 +43,7 @@ export function createRenderable<T extends Values<RenderableSchema> & BaseValues ...@@ -43,7 +43,7 @@ export function createRenderable<T extends Values<RenderableSchema> & BaseValues
// boundingSphere = calculateBoundingSphereFromValues(values) // boundingSphere = calculateBoundingSphereFromValues(values)
// return boundingSphere // return boundingSphere
}, },
get opaque () { return values.uAlpha.ref.value === 1 }, get opaque () { return values.uAlpha && values.uAlpha.ref.value === 1 },
render: (variant: RenderVariant) => renderItem.render(variant), render: (variant: RenderVariant) => renderItem.render(variant),
getProgram: (variant: RenderVariant) => renderItem.getProgram(variant), getProgram: (variant: RenderVariant) => renderItem.getProgram(variant),
......
/**
* Copyright (c) 2018 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 { Context } from '../webgl/context';
import { createRenderItem } from '../webgl/render-item';
import { AttributeSpec, Values, UniformSpec, ValueSpec } from './schema';
import { GaussianDensityShaderCode } from '../shader-code';
export const GaussianDensitySchema = {
drawCount: ValueSpec('number'),
instanceCount: ValueSpec('number'),
aRadius: AttributeSpec('float32', 1, 0),
aPosition: AttributeSpec('float32', 3, 0),
uCurrentSlice: UniformSpec('f'),
uCurrentX: UniformSpec('f'),
uCurrentY: UniformSpec('f'),
uBboxMin: UniformSpec('v3'),
uBboxMax: UniformSpec('v3'),
uBboxSize: UniformSpec('v3'),
uGridDim: UniformSpec('v3'),
}
export type GaussianDensitySchema = typeof GaussianDensitySchema
export type GaussianDensityValues = Values<GaussianDensitySchema>
export function GaussianDensityRenderable(ctx: Context, id: number, values: GaussianDensityValues, state: RenderableState): Renderable<GaussianDensityValues> {
const schema = { ...GaussianDensitySchema }
const shaderCode = GaussianDensityShaderCode
const renderItem = createRenderItem(ctx, 'points', shaderCode, schema, { ...values })
return createRenderable(renderItem, values, state);
}
\ No newline at end of file
...@@ -39,6 +39,11 @@ export const MeshShaderCode = ShaderCode( ...@@ -39,6 +39,11 @@ export const MeshShaderCode = ShaderCode(
require('mol-gl/shader/mesh.frag') require('mol-gl/shader/mesh.frag')
) )
export const GaussianDensityShaderCode = ShaderCode(
require('mol-gl/shader/gaussian-density.vert'),
require('mol-gl/shader/gaussian-density.frag')
)
export type ShaderDefines = { export type ShaderDefines = {
[k: string]: ValueCell<DefineType> [k: string]: ValueCell<DefineType>
} }
......
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
*/
precision mediump float;
varying vec3 position;
varying float radius;
uniform vec3 uBboxSize;
uniform vec3 uBboxMin;
uniform vec3 uBboxMax;
uniform vec3 uGridDim;
uniform float uCurrentSlice;
uniform float uCurrentX;
uniform float uCurrentY;
void main() {
vec3 tmpVec = gl_FragCoord.xyz;
tmpVec.x = tmpVec.x - uCurrentX;
tmpVec.y = tmpVec.y - uCurrentY;
vec3 fragPos = vec3(
(tmpVec.x - 0.5) / uGridDim.x,
(tmpVec.y - 0.5) / uGridDim.y,
(uCurrentSlice) / uGridDim.z
);
float dist = length(fragPos * uBboxSize - position * uBboxSize);
float density = 1.0 - smoothstep( 0.0, radius * 2.0, dist);
gl_FragColor = vec4(1, 1, 1, density);
// density = 1.0 - clamp((dist - (radius + 1.4)) + 0.5, 0.0, 1.0);
// gl_FragColor = vec4(vec3(density), 1.0);
}
\ No newline at end of file
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
*/
precision mediump float;
attribute vec3 aPosition;
attribute float aRadius;
varying vec3 position;
varying float radius;
uniform vec3 uBboxSize;
uniform vec3 uBboxMin;
uniform vec3 uBboxMax;
uniform vec3 uGridDim;
uniform float uCurrentSlice;
void main() {
radius = aRadius;
float scale = max(uBboxSize.z, max(uBboxSize.x, uBboxSize.y));
gl_PointSize = (radius / scale) * max(uGridDim.x, uGridDim.y) * 10.0; // * 50.0;
position = (aPosition - uBboxMin) / uBboxSize;
gl_Position = vec4(position * 2.0 - 1.0, 1.0);
}
\ No newline at end of file
...@@ -60,6 +60,13 @@ interface ValueChanges { ...@@ -60,6 +60,13 @@ interface ValueChanges {
uniforms: boolean uniforms: boolean
} }
// TODO make `RenderVariantDefines` a parameter for `createRenderItem`
/**
* Creates a render item
*
* - assumes that `values.drawCount` and `values.instanceCount` exist
*/
export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues): RenderItem { export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues): RenderItem {
const id = getNextRenderItemId() const id = getNextRenderItemId()
const { programCache } = ctx const { programCache } = ctx
......
...@@ -9,6 +9,12 @@ import { Vec3, Mat4, Tensor } from '../linear-algebra'; ...@@ -9,6 +9,12 @@ import { Vec3, Mat4, Tensor } from '../linear-algebra';
import { RuntimeContext, Task } from 'mol-task'; import { RuntimeContext, Task } from 'mol-task';
import { PositionData, DensityData } from './common'; import { PositionData, DensityData } from './common';
import { OrderedSet } from 'mol-data/int'; import { OrderedSet } from 'mol-data/int';
import { createRenderable, createGaussianDensityRenderObject } from 'mol-gl/render-object';
import { createContext } from 'mol-gl/webgl/context';
import { GaussianDensityValues } from 'mol-gl/renderable/gaussian-density';
import { RenderableState } from 'mol-gl/renderable';
import { ValueCell } from 'mol-util';
import { createRenderTarget } from 'mol-gl/webgl/render-target';
export const DefaultGaussianDensityProps = { export const DefaultGaussianDensityProps = {
resolution: 1, resolution: 1,
...@@ -26,10 +32,17 @@ function getDelta(box: Box3D, resolution: number) { ...@@ -26,10 +32,17 @@ function getDelta(box: Box3D, resolution: number) {
} }
export function computeGaussianDensity(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) { export function computeGaussianDensity(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) {
return Task.create('Gaussian Density', async ctx => await GaussianDensity(ctx, position, box, radius, props)); return Task.create('Gaussian Density', async ctx => {
const foo = await GaussianDensityGPU(ctx, position, box, radius, props)
console.log('FOOBAR', foo)
return await GaussianDensity(ctx, position, box, radius, props)
});
} }
export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> { export async function GaussianDensity(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<DensityData> {
const foo = await GaussianDensityGPU(ctx, position, box, radius, props)
console.log('FOOBAR', foo)
const { resolution, radiusOffset, smoothness } = props const { resolution, radiusOffset, smoothness } = props
const { indices, x, y, z } = position const { indices, x, y, z } = position
...@@ -118,3 +131,159 @@ export async function GaussianDensity(ctx: RuntimeContext, position: PositionDat ...@@ -118,3 +131,159 @@ export async function GaussianDensity(ctx: RuntimeContext, position: PositionDat
return { field, idField, transform } return { field, idField, transform }
} }
export async function GaussianDensityGPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) { // }: Promise<DensityData> {
const { resolution, radiusOffset } = props
const { indices, x, y, z } = position
const n = OrderedSet.size(indices)
const positions = new Float32Array(n * 3)
const radii = new Float32Array(n)
const pad = (radiusOffset + 3) * 3 // TODO calculate max radius
const expandedBox = Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad));
const extent = Vec3.sub(Vec3.zero(), expandedBox.max, expandedBox.min)
const delta = getDelta(Box3D.expand(Box3D.empty(), box, Vec3.create(pad, pad, pad)), resolution)
const dim = Vec3.zero()
Vec3.ceil(dim, Vec3.mul(dim, extent, delta))
for (let i = 0; i < n; ++i) {
const j = OrderedSet.getAt(indices, i);
positions[i * 3] = x[j]
positions[i * 3 + 1] = y[j]
positions[i * 3 + 2] = z[j]
radii[i] = radius(j) + radiusOffset
if (i % 10000 === 0 && ctx.shouldUpdate) {
await ctx.update({ message: 'preparing density data', current: i, max: n });
}
}
//
const values: GaussianDensityValues = {
drawCount: ValueCell.create(n),
instanceCount: ValueCell.create(1),
aRadius: ValueCell.create(radii),
aPosition: ValueCell.create(positions),
uCurrentSlice: ValueCell.create(0),
uCurrentX: ValueCell.create(0),
uCurrentY: ValueCell.create(0),
uBboxMin: ValueCell.create(expandedBox.min),
uBboxMax: ValueCell.create(expandedBox.max),
uBboxSize: ValueCell.create(extent),
uGridDim: ValueCell.create(dim),
}
const state: RenderableState = {
visible: true,
depthMask: false
}
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl', {
alpha: false,
antialias: true,
depth: true,
preserveDrawingBuffer: true
})
if (!gl) throw new Error('Could not create a WebGL rendering context')
const webgl = createContext(gl)
const renderObject = createGaussianDensityRenderObject(values, state)
const renderable = createRenderable(webgl, renderObject)
//
// get actual max texture size
const maxTexSize = 4096; // gl. .limits.maxTextureSize;
let fboTexDimX = 0
let fboTexDimY = dim[1]
let fboTexRows = 1
let fboTexCols = dim[0]
if(maxTexSize < dim[0] * dim[2]) {
fboTexCols = Math.floor(maxTexSize / dim[0])
fboTexRows = Math.ceil(dim[2] / fboTexCols)
fboTexDimX = fboTexCols * dim[0]
fboTexDimY *= fboTexRows
} else {
fboTexDimX = dim[0] * dim[2]
}
//
const program = renderable.getProgram('draw')
const renderTarget = createRenderTarget(webgl, fboTexDimX, fboTexDimY)
program.use()
renderTarget.bind()
gl.disable(gl.CULL_FACE)
gl.frontFace(gl.CCW)
gl.cullFace(gl.BACK)
gl.depthMask(true)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.depthMask(false)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.blendEquation(gl.FUNC_ADD)
gl.enable(gl.BLEND)
gl.finish();
let currCol = 0;
let currY = 0;
let currX = 0;
for(let i = 0; i < dim[2]; ++i) {
if (currCol >= fboTexCols) {
currCol -= fboTexCols
currY += dim[1]
currX = 0
}
gl.viewport(currX, currY, dim[0], dim[1])
ValueCell.update(values.uCurrentSlice, i)
ValueCell.update(values.uCurrentX, currX)
ValueCell.update(values.uCurrentY, currY)
renderable.render('draw')
++currCol
currX += dim[0]
}
gl.finish();
const imageData = renderTarget.getImageData()
console.log(imageData)
debugTexture(imageData, 0.4)
//
const transform = Mat4.identity()
Mat4.fromScaling(transform, Vec3.inverse(Vec3.zero(), delta))
Mat4.setTranslation(transform, expandedBox.min)
return { field: imageData, idField: undefined, transform }
}
function debugTexture(imageData: ImageData, scale: number) {
const canvas = document.createElement('canvas')
canvas.width = imageData.width
canvas.height = imageData.height
const ctx = canvas.getContext('2d')
if (!ctx) throw new Error('Could not create canvas 2d context')
ctx.putImageData(imageData, 0, 0)
canvas.toBlob(function(imgBlob){
var objectURL = window.URL.createObjectURL(imgBlob)
var img = document.createElement('img')
img.src = objectURL
img.style.width = imageData.width * scale + 'px'
img.style.height = imageData.height * scale + 'px'
img.style.position = 'absolute'
img.style.top = '0px'
img.style.left = '0px'
document.body.appendChild(img)
}, 'image/png')
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment