Forráskód Böngészése

wip, direct volume and webgl2 compatibility

Alexander Rose 6 éve
szülő
commit
0df5c9a13e

+ 103 - 53
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -8,18 +8,17 @@ import { RuntimeContext } from 'mol-task'
 import { ValueCell } from 'mol-util'
 import { Sphere3D } from 'mol-math/geometry'
 import { paramDefaultValues, RangeParam, BooleanParam, SelectParam, TextParam } from 'mol-view/parameter';
-import { DirectVolume2dValues } from 'mol-gl/renderable/direct-volume';
-import { TextureImage } from 'mol-gl/renderable/util';
+import { DirectVolume2dValues, DirectVolumeBaseValues, DirectVolume3dValues } from 'mol-gl/renderable/direct-volume';
+import { TextureImage, TextureVolume } from 'mol-gl/renderable/util';
 import { Vec3, Vec2, Mat4 } from 'mol-math/linear-algebra';
 import { Box } from '../../primitive/box';
 import { getControlPointsFromString, createTransferFunctionTexture } from './transfer-function';
 
-export interface DirectVolume {
-    readonly kind: 'direct-volume',
+const VolumeBox = Box()
+const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
 
+interface DirectVolumeBase {
     readonly gridDimension: ValueCell<Vec3>,
-    readonly gridTexture: ValueCell<TextureImage<any>>,
-    readonly gridTextureDim: ValueCell<Vec2>,
     readonly bboxSize: ValueCell<Vec3>
     readonly bboxMin: ValueCell<Vec3>
     readonly bboxMax: ValueCell<Vec3>
@@ -29,70 +28,121 @@ export interface DirectVolume {
     boundingSphere?: Sphere3D
 }
 
-const VolumeBox = Box()
+const BaseParams = {
+    alpha: RangeParam('Opacity', '', 1, 0, 1, 0.01),
+    visible: BooleanParam('Visible', '', true),
+    depthMask: BooleanParam('Depth Mask', '', true),
+    useFog: BooleanParam('Use Fog', '', false),
+    isoValueAbsolute: RangeParam('Iso Value Absolute', '', 0.22, -1, 1, 0.01),
+    isoValueRelative: RangeParam('Iso Value Relative', '', 2, -10, 10, 0.1),
+    renderMode: SelectParam('Render Mode', '', 'volume', RenderModeOptions),
+    controlPoints: TextParam('Control Points', '', '0.19:0.1, 0.2:0.5, 0.21:0.1, 0.4:0.3'),
+}
+const DefaultBaseProps = paramDefaultValues(BaseParams)
+type BaseProps = typeof DefaultBaseProps
 
-const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
+async function createBaseValues(ctx: RuntimeContext, directVolume: DirectVolumeBase, props: BaseProps): Promise<DirectVolumeBaseValues> {
+    const { bboxSize, bboxMin, bboxMax, gridDimension, transform } = directVolume
 
-export namespace DirectVolume {
-    export function createEmpty(directVolume?: DirectVolume): DirectVolume {
-        // TODO
-        return {
+    const controlPoints = getControlPointsFromString(props.controlPoints)
+    const transferTex = createTransferFunctionTexture(controlPoints)
+
+    const maxSteps = Math.round(Vec3.magnitude(bboxSize.ref.value))
+    console.log('maxSteps', maxSteps)
+
+    return {
+        drawCount: ValueCell.create(VolumeBox.indices.length),
+        instanceCount: ValueCell.create(1),
+
+        aPosition: ValueCell.create(VolumeBox.vertices as Float32Array),
+        elements: ValueCell.create(VolumeBox.indices as Uint32Array),
 
-        } as DirectVolume
+        uAlpha: ValueCell.create(props.alpha),
+        dUseFog: ValueCell.create(props.useFog),
+
+        uIsoValue: ValueCell.create(props.isoValueAbsolute),
+        uBboxMin: bboxMin,
+        uBboxMax: bboxMax,
+        uBboxSize: bboxSize,
+        dMaxSteps: ValueCell.create(maxSteps),
+        uTransform: transform,
+        uGridDim: gridDimension,
+        dRenderMode: ValueCell.create(props.renderMode),
+        tTransferTex: transferTex,
     }
+}
+
+function updateBaseValues(values: DirectVolumeBaseValues, props: BaseProps) {
+    console.log('DirectVolumeBaseValues', props, values)
+    ValueCell.updateIfChanged(values.uIsoValue, props.isoValueAbsolute)
+    ValueCell.updateIfChanged(values.uAlpha, props.alpha)
+    ValueCell.updateIfChanged(values.dUseFog, props.useFog)
+    ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)
+
+    const controlPoints = getControlPointsFromString(props.controlPoints)
+    createTransferFunctionTexture(controlPoints, values.tTransferTex)
+}
+
+// 2d
+
+export interface DirectVolume2d extends DirectVolumeBase {
+    readonly kind: 'direct-volume-2d',
+    readonly gridTexture: ValueCell<TextureImage<any>>,
+    readonly gridTextureDim: ValueCell<Vec2>,
+}
 
-    //
-
-    export const Params = {
-        alpha: RangeParam('Opacity', '', 1, 0, 1, 0.01),
-        visible: BooleanParam('Visible', '', true),
-        depthMask: BooleanParam('Depth Mask', '', true),
-        useFog: BooleanParam('Use Fog', '', false),
-        isoValueAbsolute: RangeParam('Iso Value Absolute', '', 0.22, -1, 1, 0.01),
-        isoValueRelative: RangeParam('Iso Value Relative', '', 2, -10, 10, 0.1),
-        renderMode: SelectParam('Render Mode', '', 'volume', RenderModeOptions),
-        controlPoints: TextParam('Control Points', '', '0.19:0.1, 0.2:0.5, 0.21:0.1, 0.4:0.3'),
+export namespace DirectVolume2d {
+    export function createEmpty(directVolume?: DirectVolume2d): DirectVolume2d {
+        return {} as DirectVolume2d // TODO
     }
+
+    export const Params = BaseParams
     export const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps
 
-    export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume, props: Props): Promise<DirectVolume2dValues> {
-        const { bboxSize, bboxMin, bboxMax, gridDimension, gridTexture, gridTextureDim, transform } = directVolume
-
-        const controlPoints = getControlPointsFromString(props.controlPoints)
-        const transferTex = createTransferFunctionTexture(controlPoints)
+    export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume2d, props: Props): Promise<DirectVolume2dValues> {
+        const { gridTexture, gridTextureDim } = directVolume
 
         return {
-            drawCount: ValueCell.create(VolumeBox.indices.length),
-            instanceCount: ValueCell.create(1),
+            ...await createBaseValues(ctx, directVolume, props),
+            dGridTexType: ValueCell.create('2d'),
+            uGridTexDim: gridTextureDim,
+            tGridTex: gridTexture,
+        }
+    }
 
-            aPosition: ValueCell.create(VolumeBox.vertices as Float32Array),
-            elements: ValueCell.create(VolumeBox.indices as Uint32Array),
+    export function updateValues(values: DirectVolume2dValues, props: Props) {
+        updateBaseValues(values, props)
+    }
+}
 
-            uAlpha: ValueCell.create(props.alpha),
-            dUseFog: ValueCell.create(props.useFog),
+// 3d
 
-            uIsoValue: ValueCell.create(props.isoValueAbsolute),
-            uBboxMin: bboxMin,
-            uBboxMax: bboxMax,
-            uBboxSize: bboxSize,
-            uTransform: transform,
-            uGridDim: gridDimension,
-            uGridTexDim: gridTextureDim,
+export interface DirectVolume3d extends DirectVolumeBase {
+    readonly kind: 'direct-volume-3d',
+    readonly gridTexture: ValueCell<TextureVolume<any>>,
+}
+
+export namespace DirectVolume3d {
+    export function createEmpty(directVolume?: DirectVolume3d): DirectVolume3d {
+        return {} as DirectVolume3d // TODO
+    }
+
+    export const Params = BaseParams
+    export const DefaultProps = paramDefaultValues(Params)
+    export type Props = typeof DefaultProps
+
+    export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume3d, props: Props): Promise<DirectVolume3dValues> {
+        const { gridTexture } = directVolume
+
+        return {
+            ...await createBaseValues(ctx, directVolume, props),
+            dGridTexType: ValueCell.create('3d'),
             tGridTex: gridTexture,
-            dRenderMode: ValueCell.create(props.renderMode),
-            tTransferTex: transferTex,
         }
     }
 
-    export function updateValues(values: DirectVolume2dValues, props: Props) {
-        console.log('DirectVolumeValues', props, values)
-        ValueCell.updateIfChanged(values.uIsoValue, props.isoValueAbsolute)
-        ValueCell.updateIfChanged(values.uAlpha, props.alpha)
-        ValueCell.updateIfChanged(values.dUseFog, props.useFog)
-        ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)
-
-        const controlPoints = getControlPointsFromString(props.controlPoints)
-        createTransferFunctionTexture(controlPoints, values.tTransferTex)
+    export function updateValues(values: DirectVolume3dValues, props: Props) {
+        updateBaseValues(values, props)
     }
 }

+ 2 - 2
src/mol-geo/representation/structure/index.ts

@@ -14,7 +14,7 @@ import { Mesh } from '../../geometry/mesh/mesh';
 import { Points } from '../../geometry/points/points';
 import { Lines } from '../../geometry/lines/lines';
 import { SelectParam, paramDefaultValues } from 'mol-view/parameter';
-import { DirectVolume } from '../../geometry/direct-volume/direct-volume';
+import { DirectVolume2d } from '../../geometry/direct-volume/direct-volume';
 
 export interface StructureRepresentation<P extends RepresentationProps = {}> extends Representation<Structure, P> { }
 
@@ -48,7 +48,7 @@ export const DefaultStructureLinesProps = paramDefaultValues(StructureLinesParam
 export type StructureLinesProps = typeof DefaultStructureLinesProps
 
 export const StructureDirectVolumeParams = {
-    ...DirectVolume.Params,
+    ...DirectVolume2d.Params,
     ...StructureParams,
 }
 export const DefaultStructureDirectVolumeProps = paramDefaultValues(StructureDirectVolumeParams)

+ 8 - 8
src/mol-geo/representation/structure/units-visual.ts

@@ -15,7 +15,7 @@ import { LocationIterator } from '../../util/location-iterator';
 import { Mesh } from '../../geometry/mesh/mesh';
 import { MarkerAction, applyMarkerAction, createMarkers } from '../../geometry/marker-data';
 import { Loci, isEveryLoci, EmptyLoci } from 'mol-model/loci';
-import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolumeRenderObject } from 'mol-gl/render-object';
+import { MeshRenderObject, PointsRenderObject, LinesRenderObject, DirectVolume2dRenderObject } from 'mol-gl/render-object';
 import { createUnitsMeshRenderObject, createUnitsPointsRenderObject, createUnitsTransform, createUnitsLinesRenderObject, createUnitsDirectVolumeRenderObject } from './visual/util/common';
 import { deepEqual, ValueCell, UUID } from 'mol-util';
 import { Interval } from 'mol-data/int';
@@ -25,7 +25,7 @@ import { createColors, ColorProps } from '../../geometry/color-data';
 import { createSizes, SizeProps } from '../../geometry/size-data';
 import { Lines } from '../../geometry/lines/lines';
 import { MultiSelectParam, paramDefaultValues } from 'mol-view/parameter';
-import { DirectVolume } from '../../geometry/direct-volume/direct-volume';
+import { DirectVolume2d } from '../../geometry/direct-volume/direct-volume';
 
 export const UnitKindInfo = {
     'atomic': {},
@@ -540,7 +540,7 @@ export type UnitsDirectVolumeProps = typeof DefaultUnitsDirectVolumeProps
 
 export interface UnitsDirectVolumeVisualBuilder<P extends UnitsDirectVolumeProps> {
     defaultProps: P
-    createDirectVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, directVolume?: DirectVolume): Promise<DirectVolume>
+    createDirectVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: P, directVolume?: DirectVolume2d): Promise<DirectVolume2d>
     createLocationIterator(group: Unit.SymmetryGroup): LocationIterator
     getLoci(pickingId: PickingId, group: Unit.SymmetryGroup, id: number): Loci
     mark(loci: Loci, group: Unit.SymmetryGroup, apply: (interval: Interval) => boolean): boolean
@@ -551,9 +551,9 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde
     const { defaultProps, createDirectVolume, createLocationIterator, getLoci, setUpdateState } = builder
     const updateState = VisualUpdateState.create()
 
-    let renderObject: DirectVolumeRenderObject | undefined
+    let renderObject: DirectVolume2dRenderObject | undefined
     let currentProps: P
-    let directVolume: DirectVolume
+    let directVolume: DirectVolume2d
     let currentGroup: Unit.SymmetryGroup
     let currentStructure: Structure
     let locationIt: LocationIterator
@@ -567,7 +567,7 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde
         currentConformationId = Unit.conformationId(unit)
         directVolume = includesUnitKind(currentProps.unitKinds, unit)
             ? await createDirectVolume(ctx, unit, currentStructure, currentProps, directVolume)
-            : DirectVolume.createEmpty(directVolume)
+            : DirectVolume2d.createEmpty(directVolume)
 
         // TODO create empty location iterator when not in unitKinds
         locationIt = createLocationIterator(group)
@@ -609,7 +609,7 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde
         if (updateState.createGeometry) {
             directVolume = includesUnitKind(newProps.unitKinds, unit)
                 ? await createDirectVolume(ctx, unit, currentStructure, newProps, directVolume)
-                : DirectVolume.createEmpty(directVolume)
+                : DirectVolume2d.createEmpty(directVolume)
             updateState.updateColor = true
         }
 
@@ -618,7 +618,7 @@ export function UnitsDirectVolumeVisual<P extends UnitsDirectVolumeProps>(builde
         // }
 
         // TODO why do I need to cast here?
-        DirectVolume.updateValues(renderObject.values, newProps as UnitsDirectVolumeProps)
+        DirectVolume2d.updateValues(renderObject.values, newProps as UnitsDirectVolumeProps)
         updateRenderableState(renderObject.state, newProps as UnitsDirectVolumeProps)
 
         currentProps = newProps

+ 3 - 3
src/mol-geo/representation/structure/visual/gaussian-density-volume.ts

@@ -11,11 +11,11 @@ import { UnitsDirectVolumeVisual, UnitsDirectVolumeParams } from '../units-visua
 import { StructureElementIterator, getElementLoci, markElement } from './util/element';
 import { GaussianDensityProps, GaussianDensityParams } from 'mol-model/structure/structure/unit/gaussian-density';
 import { paramDefaultValues } from 'mol-view/parameter';
-import { DirectVolume } from '../../../geometry/direct-volume/direct-volume';
+import { DirectVolume2d } from '../../../geometry/direct-volume/direct-volume';
 import { ValueCell } from 'mol-util';
 import { Vec3, Vec2 } from 'mol-math/linear-algebra';
 
-async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume): Promise<DirectVolume> {
+async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, structure: Structure, props: GaussianDensityProps, directVolume?: DirectVolume2d): Promise<DirectVolume2d> {
     const p = { ...props, useGpu: true, ignoreCache: true }
     const { transform, renderTarget, bbox, gridDimension } = await unit.computeGaussianDensity(p, ctx)
     if (!renderTarget || !bbox || !gridDimension) throw new Error('missing renderTarget and/or boundingBox and/or gridDimension')
@@ -30,7 +30,7 @@ async function createGaussianDensityVolume(ctx: RuntimeContext, unit: Unit, stru
         ValueCell.update(directVolume.transform, transform)
     } else {
         directVolume = {
-            kind: 'direct-volume' as 'direct-volume',
+            kind: 'direct-volume-2d' as 'direct-volume-2d',
             gridDimension: ValueCell.create(gridDimension),
             gridTexture: ValueCell.create(renderTarget.image),
             gridTextureDim: ValueCell.create(Vec2.create(renderTarget.width, renderTarget.height)),

+ 6 - 6
src/mol-geo/representation/structure/visual/util/common.ts

@@ -8,14 +8,14 @@ import { Unit, Structure } from 'mol-model/structure';
 import { LocationIterator } from '../../../../util/location-iterator';
 import { Mesh } from '../../../../geometry/mesh/mesh';
 import { StructureProps } from '../..';
-import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object';
+import { createMeshRenderObject, createPointsRenderObject, createLinesRenderObject, createDirectVolume2dRenderObject } from 'mol-gl/render-object';
 import { RuntimeContext } from 'mol-task';
 import { TransformData, createIdentityTransform, createTransform } from '../../../../geometry/transform-data';
 import { Points } from '../../../../geometry/points/points';
 import { createRenderableState } from '../../../../geometry/geometry';
 import { Mat4 } from 'mol-math/linear-algebra';
 import { Lines } from '../../../../geometry/lines/lines';
-import { DirectVolume } from '../../../../geometry/direct-volume/direct-volume';
+import { DirectVolume2d } from '../../../../geometry/direct-volume/direct-volume';
 
 export function createUnitsTransform({ units }: Unit.SymmetryGroup, transformData?: TransformData) {
     const unitCount = units.length
@@ -70,12 +70,12 @@ export async function createUnitsLinesRenderObject(ctx: RuntimeContext, group: U
 
 // direct-volume
 
-type StructureDirectVolumeProps = DirectVolume.Props & StructureProps
+type StructureDirectVolumeProps = DirectVolume2d.Props & StructureProps
 
-export async function createUnitsDirectVolumeRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, directVolume: DirectVolume, locationIt: LocationIterator, props: StructureDirectVolumeProps) {
+export async function createUnitsDirectVolumeRenderObject(ctx: RuntimeContext, group: Unit.SymmetryGroup, directVolume: DirectVolume2d, locationIt: LocationIterator, props: StructureDirectVolumeProps) {
     // TODO transform support
     // const transform = createUnitsTransform(group)
-    const values = await DirectVolume.createValues(ctx, directVolume, props)
+    const values = await DirectVolume2d.createValues(ctx, directVolume, props)
     const state = createRenderableState(props)
-    return createDirectVolumeRenderObject(values, state)
+    return createDirectVolume2dRenderObject(values, state)
 }

+ 92 - 16
src/mol-geo/representation/volume/direct-volume.ts

@@ -7,20 +7,23 @@
 import { VolumeData } from 'mol-model/volume'
 import { RuntimeContext } from 'mol-task'
 import { VolumeVisual, VolumeRepresentation } from '.';
-import { DirectVolumeRenderObject, createDirectVolumeRenderObject } from 'mol-gl/render-object';
+import { DirectVolume2dRenderObject, createDirectVolume2dRenderObject, DirectVolume3dRenderObject, createDirectVolume3dRenderObject } from 'mol-gl/render-object';
 import { PickingId } from '../../geometry/picking';
 import { MarkerAction } from '../../geometry/marker-data';
 import { Loci, EmptyLoci } from 'mol-model/loci';
 import { createRenderableState, updateRenderableState, Geometry } from '../../geometry/geometry';
 import { paramDefaultValues } from 'mol-view/parameter';
 import { ValueCell } from 'mol-util';
-import { DirectVolume } from '../../geometry/direct-volume/direct-volume';
+import { DirectVolume2d, DirectVolume3d } from '../../geometry/direct-volume/direct-volume';
 import { Vec2, Vec3 } from 'mol-math/linear-algebra';
 import { Box3D } from 'mol-math/geometry';
 import { createImageData } from 'mol-gl/webgl/context';
 import { debugTexture } from 'mol-gl/util';
+import { DirectVolume3dValues, DirectVolume2dValues } from 'mol-gl/renderable/direct-volume';
 
-function getFlattedVolumeLayout(dim: Vec3, maxTextureSize = 4096) {
+// 2d volume texture
+
+function getVolumeTexture2dLayout(dim: Vec3, maxTextureSize = 4096) {
     let width = 0
     let height = dim[1]
     let rows = 1
@@ -38,12 +41,12 @@ function getFlattedVolumeLayout(dim: Vec3, maxTextureSize = 4096) {
     return { width, height, columns, rows }
 }
 
-function createFlattendVolumeTexture(volume: VolumeData) {
+function createVolumeTexture2d(volume: VolumeData) {
     const { data: tensor, dataStats: stats } = volume
     const { space, data } = tensor
     const dim = space.dimensions as Vec3
     const { get } = space
-    const { width, height, columns, rows } = getFlattedVolumeLayout(dim)
+    const { width, height, columns, rows } = getVolumeTexture2dLayout(dim)
 
     const array = new Uint8Array(width * height * 4)
     const textureImage = { array, width, height }
@@ -74,10 +77,10 @@ function createFlattendVolumeTexture(volume: VolumeData) {
     return textureImage
 }
 
-export function createDirectVolume(ctx: RuntimeContext, volume: VolumeData, directVolume?: DirectVolume) {
+export function createDirectVolume2d(ctx: RuntimeContext, volume: VolumeData, directVolume?: DirectVolume2d) {
     const gridDimension = volume.data.space.dimensions as Vec3
     // const textureImage = createTextureImage(1, 4)
-    const textureImage = createFlattendVolumeTexture(volume)
+    const textureImage = createVolumeTexture2d(volume)
     const transform = VolumeData.getGridToCartesianTransform(volume)
 
     console.log('textureImage', textureImage)
@@ -99,7 +102,7 @@ export function createDirectVolume(ctx: RuntimeContext, volume: VolumeData, dire
         ValueCell.update(directVolume.transform, transform)
     } else {
         directVolume = {
-            kind: 'direct-volume' as 'direct-volume',
+            kind: 'direct-volume-2d' as 'direct-volume-2d',
             gridDimension: ValueCell.create(dim),
             gridTexture: ValueCell.create(textureImage),
             gridTextureDim: ValueCell.create(Vec2.create(textureImage.width, textureImage.height)),
@@ -118,18 +121,81 @@ export function createDirectVolume(ctx: RuntimeContext, volume: VolumeData, dire
     return directVolume;
 }
 
+// 3d volume texture
+
+function createVolumeTexture3d(volume: VolumeData) {
+    const { data: tensor, dataStats: stats } = volume
+    const { space, data } = tensor
+    const [ width, height, depth ] = space.dimensions as Vec3
+    const { get } = space
+
+    const array = new Uint8Array(width * height * depth * 4)
+    const textureVolume = { array, width, height, depth }
+
+    let i = 0
+    for (let z = 0; z < depth; ++z) {
+        for (let x = 0; x < width; ++x) {
+            for (let y = 0; y < height; ++y) {
+                array[i + 3] = ((get(data, x, y, z) - stats.min) / (stats.max - stats.min)) * 255
+                i += 4
+            }
+        }
+    }
+
+    return textureVolume
+}
+
+export function createDirectVolume3d(ctx: RuntimeContext, volume: VolumeData, directVolume?: DirectVolume3d) {
+    const gridDimension = volume.data.space.dimensions as Vec3
+    const textureVolume = createVolumeTexture3d(volume)
+    const transform = VolumeData.getGridToCartesianTransform(volume)
+
+    console.log('textureVolume', textureVolume)
+
+    const bbox = Box3D.empty()
+    Box3D.add(bbox, gridDimension)
+    Box3D.transform(bbox, bbox, transform)
+
+    if (directVolume) {
+        ValueCell.update(directVolume.gridDimension, gridDimension)
+        ValueCell.update(directVolume.gridTexture, textureVolume)
+        ValueCell.update(directVolume.bboxMin, bbox.min)
+        ValueCell.update(directVolume.bboxMax, bbox.max)
+        ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
+        ValueCell.update(directVolume.transform, transform)
+    } else {
+        directVolume = {
+            kind: 'direct-volume-3d' as 'direct-volume-3d',
+            gridDimension: ValueCell.create(gridDimension),
+            gridTexture: ValueCell.create(textureVolume),
+            bboxMin: ValueCell.create(bbox.min),
+            bboxMax: ValueCell.create(bbox.max),
+            bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
+            transform: ValueCell.create(transform),
+        }
+    }
+
+    return directVolume;
+}
+
+//
+
+function hasWebGL2() {
+    return true
+}
+
 export const DirectVolumeParams = {
     ...Geometry.Params,
-    ...DirectVolume.Params
+    ...DirectVolume2d.Params
 }
 export const DefaultDirectVolumeProps = paramDefaultValues(DirectVolumeParams)
 export type DirectVolumeProps = typeof DefaultDirectVolumeProps
 
 export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
     let currentProps = DefaultDirectVolumeProps
-    let renderObject: DirectVolumeRenderObject
+    let renderObject: DirectVolume2dRenderObject | DirectVolume3dRenderObject
     let currentVolume: VolumeData
-    let directVolume: DirectVolume
+    let directVolume: DirectVolume2d | DirectVolume3d
 
     async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<DirectVolumeProps> = {}) {
         currentProps = { ...DefaultDirectVolumeProps, ...props }
@@ -137,12 +203,18 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
             // currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
         }
 
-        directVolume = await createDirectVolume(ctx, volume, directVolume)
-
-        const values = await DirectVolume.createValues(ctx, directVolume, currentProps)
         const state = createRenderableState(currentProps)
 
-        renderObject = createDirectVolumeRenderObject(values, state)
+        if (hasWebGL2()) {
+            console.log('createing 3d volume')
+            directVolume = await createDirectVolume3d(ctx, volume, directVolume as DirectVolume3d)
+            const values = await DirectVolume3d.createValues(ctx, directVolume as DirectVolume3d, currentProps)
+            renderObject = createDirectVolume3dRenderObject(values, state)
+        } else {
+            directVolume = await createDirectVolume2d(ctx, volume, directVolume as DirectVolume2d)
+            const values = await DirectVolume2d.createValues(ctx, directVolume as DirectVolume2d, currentProps)
+            renderObject = createDirectVolume2dRenderObject(values, state)
+        }
     }
 
     async function update(ctx: RuntimeContext, props: Partial<DirectVolumeProps> = {}) {
@@ -152,7 +224,11 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
             // newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
         }
 
-        DirectVolume.updateValues(renderObject.values, newProps)
+        if (hasWebGL2()) {
+            DirectVolume3d.updateValues(renderObject.values as DirectVolume3dValues, newProps)
+        } else {
+            DirectVolume2d.updateValues(renderObject.values as DirectVolume2dValues, newProps)
+        }
         updateRenderableState(renderObject.state, newProps)
 
         currentProps = newProps

+ 14 - 6
src/mol-gl/render-object.ts

@@ -9,7 +9,7 @@ import { RenderableValues } from './renderable/schema';
 import { idFactory } from 'mol-util/id-factory';
 import { Context } from './webgl/context';
 import { GaussianDensityValues, GaussianDensityRenderable } from './renderable/gaussian-density';
-import { DirectVolume2dValues, DirectVolume2dRenderable } from './renderable/direct-volume';
+import { DirectVolume2dValues, DirectVolume2dRenderable, DirectVolume3dValues, DirectVolume3dRenderable } from './renderable/direct-volume';
 
 const getNextId = idFactory(0, 0x7FFFFFFF)
 
@@ -18,8 +18,12 @@ export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', value
 export interface PointsRenderObject extends BaseRenderObject { type: 'points', values: PointsValues }
 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: DirectVolume2dValues }
-export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject | DirectVolumeRenderObject
+export interface DirectVolume2dRenderObject extends BaseRenderObject { type: 'direct-volume-2d', values: DirectVolume2dValues }
+export interface DirectVolume3dRenderObject extends BaseRenderObject { type: 'direct-volume-3d', values: DirectVolume3dValues }
+
+export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject | DirectVolume2dRenderObject | DirectVolume3dRenderObject
+
+//
 
 export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject {
     return { id: getNextId(), type: 'mesh', values, state }
@@ -33,8 +37,11 @@ export function createLinesRenderObject(values: LinesValues, state: RenderableSt
 export function createGaussianDensityRenderObject(values: GaussianDensityValues, state: RenderableState): GaussianDensityRenderObject {
     return { id: getNextId(), type: 'gaussian-density', values, state }
 }
-export function createDirectVolumeRenderObject(values: DirectVolume2dValues, state: RenderableState): DirectVolumeRenderObject {
-    return { id: getNextId(), type: 'direct-volume', values, state }
+export function createDirectVolume2dRenderObject(values: DirectVolume2dValues, state: RenderableState): DirectVolume2dRenderObject {
+    return { id: getNextId(), type: 'direct-volume-2d', values, state }
+}
+export function createDirectVolume3dRenderObject(values: DirectVolume3dValues, state: RenderableState): DirectVolume3dRenderObject {
+    return { id: getNextId(), type: 'direct-volume-3d', values, state }
 }
 
 export function createRenderable(ctx: Context, o: RenderObject): Renderable<any> {
@@ -43,6 +50,7 @@ export function createRenderable(ctx: Context, o: RenderObject): Renderable<any>
         case 'points': return PointsRenderable(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 DirectVolume2dRenderable(ctx, o.id, o.values, o.state)
+        case 'direct-volume-2d': return DirectVolume2dRenderable(ctx, o.id, o.values, o.state)
+        case 'direct-volume-3d': return DirectVolume3dRenderable(ctx, o.id, o.values, o.state)
     }
 }

+ 34 - 10
src/mol-gl/renderable/direct-volume.ts

@@ -11,7 +11,7 @@ import { AttributeSpec, Values, UniformSpec, GlobalUniformSchema, InternalSchema
 import { DirectVolumeShaderCode } from '../shader-code';
 import { ValueCell } from 'mol-util';
 
-const DirectVolumeBaseSchema = {
+export const DirectVolumeBaseSchema = {
     drawCount: ValueSpec('number'),
     instanceCount: ValueSpec('number'),
 
@@ -25,23 +25,41 @@ const DirectVolumeBaseSchema = {
     uBboxMin: UniformSpec('v3'),
     uBboxMax: UniformSpec('v3'),
     uBboxSize: UniformSpec('v3'),
+    dMaxSteps: DefineSpec('number'),
     uTransform: UniformSpec('m4'),
     uGridDim: UniformSpec('v3'),
     dRenderMode: DefineSpec('string', ['isosurface', 'volume']),
     tTransferTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
 }
+export type DirectVolumeBaseSchema = typeof DirectVolumeBaseSchema
+export type DirectVolumeBaseValues = Values<DirectVolumeBaseSchema>
 
-function getInternalValues(ctx: Context, id: number): InternalValues {
+function getInternalValues(ctx: Context, id: number, version: '100es' | '300es'): InternalValues {
     return {
         dWebGL2: ValueCell.create(ctx.isWebGL2),
+        dGlslVersion: ValueCell.create(version),
         uObjectId: ValueCell.create(id)
     }
 }
 
-//
+function DirectVolumeRenderable<T extends DirectVolumeBaseValues, S extends DirectVolumeBaseSchema>(ctx: Context, id: number, values: T, state: RenderableState, schema: S, version: '100es' | '300es'): Renderable<T> {
+    const fullSchema = Object.assign({}, GlobalUniformSchema, InternalSchema, schema)
+    const internalValues = getInternalValues(ctx, id, version)
+    const fullValues = Object.assign({}, values, internalValues)
+    const shaderCode = DirectVolumeShaderCode
+    const renderItem = createRenderItem(ctx, 'triangles', shaderCode, fullSchema, fullValues)
+    const renderable = createRenderable(renderItem, values, state);
+
+    Object.defineProperty(renderable, 'opaque', { get: () => false });
+
+    return renderable
+}
+
+// via 2d texture
 
 export const DirectVolume2dSchema = {
     ...DirectVolumeBaseSchema,
+    dGridTexType: DefineSpec('string', ['2d']),
     uGridTexDim: UniformSpec('v2'),
     tGridTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
 }
@@ -49,13 +67,19 @@ export type DirectVolume2dSchema = typeof DirectVolume2dSchema
 export type DirectVolume2dValues = Values<DirectVolume2dSchema>
 
 export function DirectVolume2dRenderable(ctx: Context, id: number, values: DirectVolume2dValues, state: RenderableState): Renderable<DirectVolume2dValues> {
-    const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolume2dSchema }
-    const internalValues = getInternalValues(ctx, id)
-    const shaderCode = DirectVolumeShaderCode
-    const renderItem = createRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues })
-    const renderable = createRenderable(renderItem, values, state);
+    return DirectVolumeRenderable(ctx, id, values, state, DirectVolume2dSchema, '100es')
+}
 
-    Object.defineProperty(renderable, 'opaque', { get: () => false });
+// via 3d texture
 
-    return renderable
+export const DirectVolume3dSchema = {
+    ...DirectVolumeBaseSchema,
+    dGridTexType: DefineSpec('string', ['3d']),
+    tGridTex: TextureSpec('volume-uint8', 'rgba', 'ubyte', 'linear'),
+}
+export type DirectVolume3dSchema = typeof DirectVolume3dSchema
+export type DirectVolume3dValues = Values<DirectVolume3dSchema>
+
+export function DirectVolume3dRenderable(ctx: Context, id: number, values: DirectVolume3dValues, state: RenderableState): Renderable<DirectVolume3dValues> {
+    return DirectVolumeRenderable(ctx, id, values, state, DirectVolume3dSchema, '300es')
 }

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

@@ -29,6 +29,7 @@ export function LinesRenderable(ctx: Context, id: number, values: LinesValues, s
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...LinesSchema }
     const internalValues: InternalValues = {
         dWebGL2: ValueCell.create(ctx.isWebGL2),
+        dGlslVersion: ValueCell.create('100es'),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = LinesShaderCode

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

@@ -27,6 +27,7 @@ export function MeshRenderable(ctx: Context, id: number, values: MeshValues, sta
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...MeshSchema }
     const internalValues: InternalValues = {
         dWebGL2: ValueCell.create(ctx.isWebGL2),
+        dGlslVersion: ValueCell.create('100es'),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = MeshShaderCode

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

@@ -26,6 +26,7 @@ export function PointsRenderable(ctx: Context, id: number, values: PointsValues,
     const schema = { ...GlobalUniformSchema, ...InternalSchema, ...PointsSchema }
     const internalValues: InternalValues = {
         dWebGL2: ValueCell.create(ctx.isWebGL2),
+        dGlslVersion: ValueCell.create('100es'),
         uObjectId: ValueCell.create(id)
     }
     const shaderCode = PointsShaderCode

+ 4 - 1
src/mol-gl/renderable/schema.ts

@@ -9,7 +9,7 @@ import { ArrayKind, BufferItemSize, ElementsKind, AttributeValues } from '../web
 import { UniformKind, UniformValues } from '../webgl/uniform';
 import { DefineKind, DefineValues } from '../shader-code';
 import { Vec2, Vec3, Vec4, Mat3, Mat4 } from 'mol-math/linear-algebra';
-import { TextureImage } from './util';
+import { TextureImage, TextureVolume } from './util';
 import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind } from '../webgl/texture';
 
 export type ValueKindType = {
@@ -41,6 +41,8 @@ export type KindValue = {
 
     'image-uint8': TextureImage<Uint8Array>
     'image-float32': TextureImage<Float32Array>
+    'volume-uint8': TextureVolume<Uint8Array>
+    'volume-float32': TextureVolume<Float32Array>
 
     'number': number
     'string': string
@@ -143,6 +145,7 @@ export type GlobalUniformValues = { [k in keyof GlobalUniformSchema]: ValueCell<
 
 export const InternalSchema = {
     dWebGL2: DefineSpec('boolean'),
+    dGlslVersion: DefineSpec('string', ['100es', '300es']),
     uObjectId: UniformSpec('i'),
 }
 export type InternalSchema = typeof InternalSchema

+ 25 - 5
src/mol-gl/shader-code.ts

@@ -1,4 +1,3 @@
-
 /**
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
@@ -8,7 +7,7 @@
 import { ValueCell } from 'mol-util';
 import { idFactory } from 'mol-util/id-factory';
 
-export type DefineKind = 'boolean' | 'string'
+export type DefineKind = 'boolean' | 'string' | 'number'
 export type DefineType = boolean | string
 export type DefineValues = { [k: string]: ValueCell<DefineType> }
 
@@ -62,19 +61,40 @@ function getDefinesCode (defines: ShaderDefines) {
         if (v) {
             if (typeof v === 'string') {
                 lines.push(`#define ${name}_${v}`)
-            } else {
+            } else if (typeof v === 'number') {
+                lines.push(`#define ${name} ${v}`)
+            } else if (typeof v === 'boolean') {
                 lines.push(`#define ${name}`)
+            } else {
+                throw new Error('unknown define type')
             }
         }
     }
     return lines.join('\n') + '\n'
 }
 
+const glsl300VertPrefix = `#version 300 es
+#define attribute in
+#define varying out
+#define texture2D texture
+`
+
+const glsl300FragPrefix = `#version 300 es
+#define varying in
+out highp vec4 out_FragColor;
+#define gl_FragColor out_FragColor
+#define gl_FragDepthEXT gl_FragDepth
+#define texture2D texture
+`
+
 export function addShaderDefines(defines: ShaderDefines, shaders: ShaderCode): ShaderCode {
+    const isGlsl300es = defines.dGlslVersion && defines.dGlslVersion.ref.value === '300es'
     const header = getDefinesCode(defines)
+    const vertPrefix = isGlsl300es ? glsl300VertPrefix : ''
+    const fragPrefix = isGlsl300es ? glsl300FragPrefix : ''
     return {
         id: shaderCodeId(),
-        vert: `${header}${shaders.vert}`,
-        frag: `${header}${shaders.frag}`
+        vert: `${vertPrefix}${header}${shaders.vert}`,
+        frag: `${fragPrefix}${header}${shaders.frag}`
     }
 }

+ 54 - 62
src/mol-gl/shader/direct-volume.frag

@@ -5,6 +5,11 @@
  * @author Michael Krone <michael.krone@uni-tuebingen.de>
  */
 
+#if defined(dGridTexType_2d)
+    precision mediump sampler2D;
+#elif defined(dGridTexType_3d)
+    precision mediump sampler3D;
+#endif
 precision highp float;
 
 varying vec3 unitCoord;
@@ -18,11 +23,16 @@ uniform float uIsoValue;
 uniform vec3 uBboxMin;
 uniform vec3 uBboxMax;
 uniform vec3 uBboxSize;
-uniform sampler2D tGridTex;
 uniform vec3 uGridDim;
-uniform vec2 uGridTexDim;
 uniform sampler2D tTransferTex;
 
+#if defined(dGridTexType_2d)
+    uniform sampler2D tGridTex;
+    uniform vec2 uGridTexDim;
+#elif defined(dGridTexType_3d)
+    uniform sampler3D tGridTex;
+#endif
+
 // float uIsoValue = exp(-1.5);
 // float uIsoValue = 0.7;
 
@@ -65,14 +75,6 @@ vec3 extractCameraPos(const in mat4 modelView) {
     return top / -denom;
 }
 
-// TODO workaround due to some kind of GPU bug
-float myMod(float a, float b) {
-    return a - b * float(int(a) / int(b));
-}
-float myDiv(float a, float b) {
-    return float(int(a) / int(b));
-}
-
 vec3 palette(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
     return a + b * cos(6.28318 * (c * t + d));
 }
@@ -81,66 +83,51 @@ vec3 palette1(in float t) {
     return palette(t, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.10,0.20));
 }
 
-vec4 textureVal(vec3 pos) {
-    float zSlice0 = floor(pos.z * uGridDim.z);
-    float column0 = myMod(zSlice0 * uGridDim.x, uGridTexDim.x) / uGridDim.x;
-    float row0 = floor(myDiv(zSlice0 * uGridDim.x, uGridTexDim.x));
-    vec2 coord0 = (vec2(column0 * uGridDim.x, row0 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
-    vec4 color0 = texture2D(tGridTex, coord0);
-
-    float zSlice1 = zSlice0 + 1.0;
-    float column1 = myMod(zSlice1 * uGridDim.x, uGridTexDim.x) / uGridDim.x;
-    float row1 = floor(myDiv(zSlice1 * uGridDim.x, uGridTexDim.x));
-    vec2 coord1 = (vec2(column1 * uGridDim.x, row1 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
-    vec4 color1 = texture2D(tGridTex, coord1);
-
-    float delta0 = abs((pos.z * uGridDim.z) - zSlice0);
-    return mix(color0, color1, delta0);
-}
+#if defined(dGridTexType_2d)
+    // TODO workaround due to some kind of GPU bug
+    float myMod(float a, float b) {
+        return a - b * float(int(a) / int(b));
+    }
+    float myDiv(float a, float b) {
+        return float(int(a) / int(b));
+    }
+
+    vec4 textureVal(vec3 pos) {
+        float zSlice0 = floor(pos.z * uGridDim.z);
+        float column0 = myMod(zSlice0 * uGridDim.x, uGridTexDim.x) / uGridDim.x;
+        float row0 = floor(myDiv(zSlice0 * uGridDim.x, uGridTexDim.x));
+        vec2 coord0 = (vec2(column0 * uGridDim.x, row0 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
+        vec4 color0 = texture2D(tGridTex, coord0);
+
+        float zSlice1 = zSlice0 + 1.0;
+        float column1 = myMod(zSlice1 * uGridDim.x, uGridTexDim.x) / uGridDim.x;
+        float row1 = floor(myDiv(zSlice1 * uGridDim.x, uGridTexDim.x));
+        vec2 coord1 = (vec2(column1 * uGridDim.x, row1 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
+        vec4 color1 = texture2D(tGridTex, coord1);
+
+        float delta0 = abs((pos.z * uGridDim.z) - zSlice0);
+        return mix(color0, color1, delta0);
+    }
+#elif defined(dGridTexType_3d)
+    vec4 textureVal(vec3 pos) {
+        return texture(tGridTex, pos);
+    }
+#endif
 
 vec4 transferFunction(float value) {
     return texture2D(tTransferTex, vec2(value, 0.0));
 }
 
-// vec4 textureVal(vec3 pos) {
-//     vec4 color0 = textureVal1(pos);
-//     vec4 color1 = textureVal1(vec3(pos.xy, pos.z + 1.1 / uGridDim.z));
-//     float delta0 = abs((pos.z * uGridDim.z) - floor(pos.z * uGridDim.z));
-//     vec3 tmpCol = vec3(0.45, 0.55, 0.8);
-
-//     // coord = pos.xy;
-//     // coord.x += col1;
-//     // coord.x /= uGridTexDim.x;
-//     // coord.y += row1;
-//     // coord.y /= uGridTexDim.y;
-//     // vec4 color1 = texture2D(tGridTex, coord);
-//     // vec4 color1 = texture2D(tGridTex, unitCoordToGridCoord(pos));
-
-//     return vec4(tmpCol.rgb, mix(color0.a, color1.a, delta0));
-//     // return vec4(tmpCol.rgb, color0.a);
-//     // return vec4(mix(color0, color1, delta0).w, tmpCol.x, tmpCol.y, tmpCol.z);
-//     // return vec4(color0.x, tmpCol.x, tmpCol.y, tmpCol.z);
-//     // return vec4(color0.xyz, 1.0);
-//     // return vec4(color0.xyz, 1.0);
-// }
-
-vec3 scaleVol = vec3(1.0) / uGridDim;
 const float gradOffset = 0.5;
-vec3 dx = vec3(gradOffset * scaleVol.x, 0.0, 0.0);
-vec3 dy = vec3(0.0, gradOffset * scaleVol.y, 0.0);
-vec3 dz = vec3(0.0, 0.0, gradOffset * scaleVol.z);
-
-vec3 color = vec3(0.45, 0.55, 0.8);
+const vec3 color = vec3(0.45, 0.55, 0.8);
 
 vec4 raymarch(vec3 cameraPos) {
     vec3 pos = unitCoord;
     float prevValue = -127.0;
     float value = 0.0;
-    float MAX_STEPS_F = max(max(uGridDim.x, uGridDim.y), uGridDim.z);
+    // float MAX_STEPS_F = max(max(uGridDim.x, uGridDim.y), uGridDim.z);
     // int MAX_STEPS = 2 * int(length(vec3(imgresx, imgresy, imgresz)));
-    // TODO define this from outside (recompile shader per data set)
-    const int MAX_STEPS = 300;
-    float stepSize = 1.0 / MAX_STEPS_F;
+    float stepSize = 1.0 / float(dMaxSteps);
     vec4 src = vec4(0.0);
     vec4 dst = vec4(0.0);
 
@@ -152,6 +139,11 @@ vec4 raymarch(vec3 cameraPos) {
     vec3 gradient = vec3(1.0);
     vec3 step = rayDir * (1.0 / uGridDim) * 0.5;
 
+    vec3 scaleVol = vec3(1.0) / uGridDim;
+    vec3 dx = vec3(gradOffset * scaleVol.x, 0.0, 0.0);
+    vec3 dy = vec3(0.0, gradOffset * scaleVol.y, 0.0);
+    vec3 dz = vec3(0.0, 0.0, gradOffset * scaleVol.z);
+
     // dst = vec4(textureVal(vec3(pos.xy, 0.6)).xyz, 0.5);
     // vec2 foo = (vec2(5.0 * uGridDim.x, 5.0 * uGridDim.y) + (pos.xy * uGridDim.xy)) / uGridTexDim;
     // dst = texture2D(tGridTex, foo);
@@ -159,7 +151,7 @@ vec4 raymarch(vec3 cameraPos) {
     // dst.xyz = pos;
     // return mix(dst, vec4(1.0, 0.0, 0.0, 1.0), 0.5);
 
-    for(int i = 0; i < MAX_STEPS; ++i){
+    for(int i = 0; i < dMaxSteps; ++i){
         if( pos.x <= 1.0 && pos.y <= 1.0 && pos.z <= 1.0 && pos.x >= 0.0 && pos.y >= 0.0 && pos.z >= 0.0) {
             value = textureVal(pos).a; // current voxel value
         } else {
@@ -189,7 +181,7 @@ vec4 raymarch(vec3 cameraPos) {
                 gradient.z = textureVal(isoPos - dz).a - textureVal(isoPos + dz).a;
                 gradient = normalize(gradient);
 
-                float d = float(dot(gradient, rayDir) > 0.0);
+                float d = float(dot(gradient, normalize(cameraPos)) > 0.0);
                 gradient = (2.0 * d - 1.0) * gradient;
 
                 src.rgb = color.rgb * abs(dot(gradient, normalize(cameraPos)));
@@ -215,8 +207,8 @@ vec4 raymarch(vec3 cameraPos) {
 }
 
 void main () {
-    // vec3 cameraPos = uInvView[3].xyz / uInvView[3].w;
-    vec3 cameraPos = extractCameraPos(uModelView);
+    vec3 cameraPos = uInvView[3].xyz / uInvView[3].w;
+    // vec3 cameraPos = extractCameraPos(uModelView);
     // vec3 cameraPos = vec3(10.0, 0.0, 0.0);
     gl_FragColor = raymarch(cameraPos);
     if (length(gl_FragColor.rgb) < 0.00001) discard;

+ 1 - 1
src/mol-gl/webgl/render-item.ts

@@ -64,7 +64,7 @@ interface ValueChanges {
 
 /**
  * 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 {

+ 1 - 1
src/mol-gl/webgl/render-target.ts

@@ -37,7 +37,7 @@ export function createRenderTarget (ctx: Context, _width: number, _height: numbe
         height: _height
     }
 
-    const targetTexture = createTexture(ctx, 'rgba', 'ubyte', 'linear')
+    const targetTexture = createTexture(ctx, 'image-uint8', 'rgba', 'ubyte', 'linear')
     targetTexture.load(image)
 
     const framebuffer = createFramebuffer(ctx)

+ 43 - 21
src/mol-gl/webgl/texture.ts

@@ -5,7 +5,7 @@
  */
 
 import { Context } from './context'
-import { TextureImage } from '../renderable/util';
+import { TextureImage, TextureVolume } from '../renderable/util';
 import { ValueCell } from 'mol-util';
 import { RenderableSchema } from '../renderable/schema';
 import { idFactory } from 'mol-util/id-factory';
@@ -16,6 +16,8 @@ const getNextTextureId = idFactory()
 export type TextureKindValue = {
     'image-uint8': TextureImage<Uint8Array>
     'image-float32': TextureImage<Float32Array>
+    'volume-uint8': TextureVolume<Uint8Array>
+    'volume-float32': TextureVolume<Float32Array>
 }
 export type TextureKind = keyof TextureKindValue
 export type TextureType = 'ubyte' | 'float'
@@ -23,7 +25,17 @@ export type TextureFormat = 'alpha' | 'rgb' | 'rgba'
 export type TextureAttachment = 'depth' | 'stencil' | 'color0'
 export type TextureFilter = 'nearest' | 'linear'
 
-export function getFormat(ctx: Context, format: TextureFormat) {
+export function getTarget(ctx: Context, kind: TextureKind): number {
+    const { gl } = ctx
+    switch (kind) {
+        case 'image-uint8': return gl.TEXTURE_2D
+        case 'image-float32': return gl.TEXTURE_2D
+        case 'volume-uint8': return (gl as WebGL2RenderingContext).TEXTURE_3D
+        case 'volume-float32': return (gl as WebGL2RenderingContext).TEXTURE_3D
+    }
+}
+
+export function getFormat(ctx: Context, format: TextureFormat): number {
     const { gl } = ctx
     switch (format) {
         case 'alpha': return gl.ALPHA
@@ -32,7 +44,7 @@ export function getFormat(ctx: Context, format: TextureFormat) {
     }
 }
 
-export function getInternalFormat(ctx: Context, format: TextureFormat, type: TextureType) {
+export function getInternalFormat(ctx: Context, format: TextureFormat, type: TextureType): number {
     const { gl, isWebGL2 } = ctx
     if (isWebGL2) {
         switch (format) {
@@ -56,7 +68,7 @@ export function getInternalFormat(ctx: Context, format: TextureFormat, type: Tex
     return getFormat(ctx, format)
 }
 
-export function getType(ctx: Context, type: TextureType) {
+export function getType(ctx: Context, type: TextureType): number {
     const { gl } = ctx
     switch (type) {
         case 'ubyte': return gl.UNSIGNED_BYTE
@@ -64,7 +76,7 @@ export function getType(ctx: Context, type: TextureType) {
     }
 }
 
-export function getFilter(ctx: Context, type: TextureFilter) {
+export function getFilter(ctx: Context, type: TextureFilter): number {
     const { gl } = ctx
     switch (type) {
         case 'nearest': return gl.NEAREST
@@ -72,7 +84,7 @@ export function getFilter(ctx: Context, type: TextureFilter) {
     }
 }
 
-export function getAttachment(ctx: Context, attachment: TextureAttachment) {
+export function getAttachment(ctx: Context, attachment: TextureAttachment): number {
     const { gl } = ctx
     switch (attachment) {
         case 'depth': return gl.DEPTH_ATTACHMENT
@@ -83,6 +95,7 @@ export function getAttachment(ctx: Context, attachment: TextureAttachment) {
 
 export interface Texture {
     readonly id: number
+    readonly target: number
     readonly format: number
     readonly internalFormat: number
     readonly type: number
@@ -99,7 +112,7 @@ export type TextureId = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 1
 export type TextureValues = { [k: string]: ValueCell<TextureImage<any>> }
 export type Textures = { [k: string]: Texture }
 
-export function createTexture(ctx: Context, _format: TextureFormat, _type: TextureType, _filter: TextureFilter): Texture {
+export function createTexture(ctx: Context, kind: TextureKind, _format: TextureFormat, _type: TextureType, _filter: TextureFilter): Texture {
     const id = getNextTextureId()
     const { gl } = ctx
     const texture = gl.createTexture()
@@ -107,8 +120,7 @@ export function createTexture(ctx: Context, _format: TextureFormat, _type: Textu
         throw new Error('Could not create WebGL texture')
     }
 
-
-
+    const target = getTarget(ctx, kind)
     const filter = getFilter(ctx, _filter)
     const format = getFormat(ctx, _format)
     const internalFormat = getInternalFormat(ctx, _format, _type)
@@ -119,33 +131,43 @@ export function createTexture(ctx: Context, _format: TextureFormat, _type: Textu
 
     return {
         id,
+        target,
         format,
         internalFormat,
         type,
 
-        load: (image: TextureImage<any>) => {
-            const { array, width, height } = image
-            gl.bindTexture(gl.TEXTURE_2D, texture)
+        load: (data: TextureImage<any> | TextureVolume<any>) => {
+            gl.bindTexture(target, texture)
             // unpack alignment of 1 since we use textures only for data
             gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
             gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
-            (gl as WebGLRenderingContext).texImage2D(gl.TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, array) // TODO remove cast when webgl2 types are fixed
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter)
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter)
+            if (target === gl.TEXTURE_2D) {
+                const { array, width, height } = data as TextureImage<any>;
+                // TODO remove cast when webgl2 types are fixed
+                (gl as WebGLRenderingContext).texImage2D(target, 0, internalFormat, width, height, 0, format, type, array)
+            } else if (target === (gl as WebGL2RenderingContext).TEXTURE_3D) {
+                const { array, width, height, depth } = data as TextureVolume<any>;
+                (gl as WebGL2RenderingContext).texImage3D(target, 0, internalFormat, width, height, depth, 0, format, type, array)
+            } else {
+                throw new Error('unknown texture target')
+            }
+            gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filter)
+            gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filter)
             // clamp-to-edge needed for non-power-of-two textures
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
-            gl.bindTexture(gl.TEXTURE_2D, null)
+            gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+            gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+            gl.bindTexture(target, null)
         },
         bind: (id: TextureId) => {
             gl.activeTexture(gl.TEXTURE0 + id)
-            gl.bindTexture(gl.TEXTURE_2D, texture)
+            gl.bindTexture(target, texture)
         },
         unbind: (id: TextureId) => {
             gl.activeTexture(gl.TEXTURE0 + id)
-            gl.bindTexture(gl.TEXTURE_2D, null)
+            gl.bindTexture(target, null)
         },
         attachFramebuffer: (framebuffer: Framebuffer, attachment: TextureAttachment) => {
+            if (target !== gl.TEXTURE_2D) throw new Error('framebuffer texture must be 2d')
             framebuffer.bind()
             gl.framebufferTexture2D(gl.FRAMEBUFFER, getAttachment(ctx, attachment), gl.TEXTURE_2D, texture, 0)
         },
@@ -163,7 +185,7 @@ export function createTextures(ctx: Context, schema: RenderableSchema, values: T
     Object.keys(schema).forEach((k, i) => {
         const spec = schema[k]
         if (spec.type === 'texture') {
-            const texture = createTexture(ctx, spec.format, spec.dataType, spec.filter)
+            const texture = createTexture(ctx, spec.kind, spec.format, spec.dataType, spec.filter)
             texture.load(values[k].ref.value)
             textures[k] = texture
         }