浏览代码

wip, direct volume

Alexander Rose 6 年之前
父节点
当前提交
7e88eec1b1

+ 1 - 1
src/apps/structure-info/volume.ts

@@ -38,7 +38,7 @@ function print(data: Volume) {
 }
 
 async function doMesh(data: Volume, filename: string) {
-    const mesh = await Task.create('', ctx => createVolumeSurface(ctx, data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5))).run();
+    const mesh = await Task.create('', ctx => createVolumeSurface(ctx, data.volume, VolumeIsoValue.calcAbsolute(data.volume.dataStats, 1.5))).run();
     console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
 
     // Export the mesh in OBJ format.

+ 2 - 2
src/mol-geo/geometry/color-data.ts

@@ -20,7 +20,7 @@ export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 export type ColorData = {
     uColor: ValueCell<Vec3>,
     aColor: ValueCell<Float32Array>,
-    tColor: ValueCell<TextureImage>,
+    tColor: ValueCell<TextureImage<Uint8Array>>,
     uColorTexDim: ValueCell<Vec2>,
     dColorType: ValueCell<string>,
 }
@@ -77,7 +77,7 @@ export async function createUniformColor(ctx: RuntimeContext, locationIt: Locati
     return createValueColor(color(NullLocation, false), colorData)
 }
 
-export function createTextureColor(colors: TextureImage, type: ColorType, colorData?: ColorData): ColorData {
+export function createTextureColor(colors: TextureImage<Uint8Array>, type: ColorType, colorData?: ColorData): ColorData {
     if (colorData) {
         ValueCell.update(colorData.tColor, colors)
         ValueCell.update(colorData.uColorTexDim, Vec2.create(colors.width, colors.height))

+ 8 - 7
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -8,7 +8,7 @@ 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 { DirectVolumeValues } from 'mol-gl/renderable/direct-volume';
+import { DirectVolume2dValues } from 'mol-gl/renderable/direct-volume';
 import { TextureImage } from 'mol-gl/renderable/util';
 import { Vec3, Vec2, Mat4 } from 'mol-math/linear-algebra';
 import { Box } from '../../primitive/box';
@@ -18,7 +18,7 @@ export interface DirectVolume {
     readonly kind: 'direct-volume',
 
     readonly gridDimension: ValueCell<Vec3>,
-    readonly gridTexture: ValueCell<TextureImage>,
+    readonly gridTexture: ValueCell<TextureImage<any>>,
     readonly gridTextureDim: ValueCell<Vec2>,
     readonly bboxSize: ValueCell<Vec3>
     readonly bboxMin: ValueCell<Vec3>
@@ -48,14 +48,15 @@ export namespace DirectVolume {
         visible: BooleanParam('Visible', '', true),
         depthMask: BooleanParam('Depth Mask', '', true),
         useFog: BooleanParam('Use Fog', '', false),
-        isoValue: RangeParam('Iso Value', '', 0.22, 0, 1, 0.01),
+        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 const DefaultProps = paramDefaultValues(Params)
     export type Props = typeof DefaultProps
 
-    export async function createValues(ctx: RuntimeContext, directVolume: DirectVolume, props: Props): Promise<DirectVolumeValues> {
+    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)
@@ -71,7 +72,7 @@ export namespace DirectVolume {
             uAlpha: ValueCell.create(props.alpha),
             dUseFog: ValueCell.create(props.useFog),
 
-            uIsoValue: ValueCell.create(props.isoValue),
+            uIsoValue: ValueCell.create(props.isoValueAbsolute),
             uBboxMin: bboxMin,
             uBboxMax: bboxMax,
             uBboxSize: bboxSize,
@@ -84,9 +85,9 @@ export namespace DirectVolume {
         }
     }
 
-    export function updateValues(values: DirectVolumeValues, props: Props) {
+    export function updateValues(values: DirectVolume2dValues, props: Props) {
         console.log('DirectVolumeValues', props, values)
-        ValueCell.updateIfChanged(values.uIsoValue, props.isoValue)
+        ValueCell.updateIfChanged(values.uIsoValue, props.isoValueAbsolute)
         ValueCell.updateIfChanged(values.uAlpha, props.alpha)
         ValueCell.updateIfChanged(values.dUseFog, props.useFog)
         ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)

+ 1 - 1
src/mol-geo/geometry/direct-volume/transfer-function.ts

@@ -19,7 +19,7 @@ export function getControlPointsFromString(s: string): ControlPoint[] {
     })
 }
 // TODO move core function to mol-view/color
-export function createTransferFunctionTexture(controlPoints: ControlPoint[], texture?: ValueCell<TextureImage>): ValueCell<TextureImage> {
+export function createTransferFunctionTexture(controlPoints: ControlPoint[], texture?: ValueCell<TextureImage<Uint8Array>>): ValueCell<TextureImage<Uint8Array>> {
     const cp = [
         { x: 0, alpha: 0 },
         { x: 0, alpha: 0 },

+ 1 - 1
src/mol-geo/geometry/marker-data.ts

@@ -9,7 +9,7 @@ import { Vec2 } from 'mol-math/linear-algebra'
 import { TextureImage, createTextureImage } from 'mol-gl/renderable/util';
 
 export type MarkerData = {
-    tMarker: ValueCell<TextureImage>
+    tMarker: ValueCell<TextureImage<Uint8Array>>
     uMarkerTexDim: ValueCell<Vec2>
 }
 

+ 2 - 2
src/mol-geo/geometry/size-data.ts

@@ -19,7 +19,7 @@ export type SizeType = 'uniform' | 'instance' | 'group' | 'groupInstance'
 export type SizeData = {
     uSize: ValueCell<number>,
     aSize: ValueCell<Float32Array>,
-    tSize: ValueCell<TextureImage>,
+    tSize: ValueCell<TextureImage<Uint8Array>>,
     uSizeTexDim: ValueCell<Vec2>,
     dSizeType: ValueCell<string>,
 }
@@ -82,7 +82,7 @@ export async function createUniformSize(ctx: RuntimeContext, locationIt: Locatio
     return createValueSize(sizeFn(NullLocation), sizeData)
 }
 
-export function createTextureSize(sizes: TextureImage, type: SizeType, sizeData?: SizeData): SizeData {
+export function createTextureSize(sizes: TextureImage<Uint8Array>, type: SizeType, sizeData?: SizeData): SizeData {
     if (sizeData) {
         ValueCell.update(sizeData.tSize, sizes)
         ValueCell.update(sizeData.uSizeTexDim, Vec2.create(sizes.width, sizes.height))

+ 15 - 18
src/mol-geo/representation/volume/direct-volume.ts

@@ -12,10 +12,10 @@ 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, RangeParam } from 'mol-view/parameter';
+import { paramDefaultValues } from 'mol-view/parameter';
 import { ValueCell } from 'mol-util';
 import { DirectVolume } from '../../geometry/direct-volume/direct-volume';
-import { Vec2, Vec3, Tensor } from 'mol-math/linear-algebra';
+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';
@@ -38,15 +38,14 @@ function getFlattedVolumeLayout(dim: Vec3, maxTextureSize = 4096) {
     return { width, height, columns, rows }
 }
 
-// let foo = 0
-
-function createFlattendVolumeTexture(tensor: Tensor, itemSize = 4) {
+function createFlattendVolumeTexture(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 array = new Uint8Array(width * height * itemSize)
+    const array = new Uint8Array(width * height * 4)
     const textureImage = { array, width, height }
 
     const [ xl, yl, zl ] = dim
@@ -57,15 +56,8 @@ function createFlattendVolumeTexture(tensor: Tensor, itemSize = 4) {
         const column = Math.floor(((z * xlp) % width) / xlp)
         const row = Math.floor((z * xlp) / width)
         const px = column * xlp + x
-        // const py = row * ylp + y
-        const index = itemSize * ((row * ylp * width) + (y * width) + px);
-        array[index] = value * 255;
-        array[index + 1] = value * 255;
-        array[index + 3] = value;
-        // if (foo % 1000 === 0) {
-        //     console.log(value * 255, x, y, z, index, '|', column, row);
-        // }
-        // ++foo;
+        const index = 4 * ((row * ylp * width) + (y * width) + px)
+        array[index + 3] = ((value - stats.min) / (stats.max - stats.min)) * 255
     }
 
     console.log('dim', dim)
@@ -85,7 +77,7 @@ function createFlattendVolumeTexture(tensor: Tensor, itemSize = 4) {
 export function createDirectVolume(ctx: RuntimeContext, volume: VolumeData, directVolume?: DirectVolume) {
     const gridDimension = volume.data.space.dimensions as Vec3
     // const textureImage = createTextureImage(1, 4)
-    const textureImage = createFlattendVolumeTexture(volume.data)
+    const textureImage = createFlattendVolumeTexture(volume)
     const transform = VolumeData.getGridToCartesianTransform(volume)
 
     console.log('textureImage', textureImage)
@@ -128,8 +120,7 @@ export function createDirectVolume(ctx: RuntimeContext, volume: VolumeData, dire
 
 export const DirectVolumeParams = {
     ...Geometry.Params,
-    ...DirectVolume.Params,
-    isoValue: RangeParam('Iso Value', '', 2, -15, 15, 0.01),
+    ...DirectVolume.Params
 }
 export const DefaultDirectVolumeProps = paramDefaultValues(DirectVolumeParams)
 export type DirectVolumeProps = typeof DefaultDirectVolumeProps
@@ -142,6 +133,9 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
 
     async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<DirectVolumeProps> = {}) {
         currentProps = { ...DefaultDirectVolumeProps, ...props }
+        if (props.isoValueRelative) {
+            // currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
+        }
 
         directVolume = await createDirectVolume(ctx, volume, directVolume)
 
@@ -154,6 +148,9 @@ export function DirectVolumeVisual(): VolumeVisual<DirectVolumeProps> {
     async function update(ctx: RuntimeContext, props: Partial<DirectVolumeProps> = {}) {
         console.log('props', props)
         const newProps = { ...currentProps, ...props }
+        if (props.isoValueRelative) {
+            // newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
+        }
 
         DirectVolume.updateValues(renderObject.values, newProps)
         updateRenderableState(renderObject.state, newProps)

+ 16 - 7
src/mol-geo/representation/volume/isosurface-mesh.ts

@@ -18,14 +18,14 @@ import { LocationIterator } from '../../util/location-iterator';
 import { NullLocation } from 'mol-model/location';
 import { createIdentityTransform } from '../../geometry/transform-data';
 import { createRenderableState, updateRenderableState } from '../../geometry/geometry';
-import { paramDefaultValues, NumberParam } from 'mol-view/parameter';
+import { paramDefaultValues, RangeParam } from 'mol-view/parameter';
 import { ValueCell } from 'mol-util';
 
-export async function createVolumeSurface(ctx: RuntimeContext, volume: VolumeData, isoValue: VolumeIsoValue, mesh?: Mesh) {
+export async function createVolumeSurface(ctx: RuntimeContext, volume: VolumeData, isoValueAbsolute: number, mesh?: Mesh) {
     ctx.update({ message: 'Marching cubes...' });
 
     const surface = await computeMarchingCubesMesh({
-        isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
+        isoLevel: isoValueAbsolute,
         scalarField: volume.data
     }, mesh).runAsChild(ctx);
 
@@ -39,7 +39,8 @@ export async function createVolumeSurface(ctx: RuntimeContext, volume: VolumeDat
 
 export const IsosurfaceParams = {
     ...Mesh.Params,
-    isoValue: NumberParam('Iso Value', '', 2, -15, 15, 0.01),
+    isoValueAbsolute: RangeParam('Iso Value Absolute', '', 0.22, -1, 1, 0.01),
+    isoValueRelative: RangeParam('Iso Value Relative', '', 2, -10, 10, 0.1),
 }
 export const DefaultIsosurfaceProps = paramDefaultValues(IsosurfaceParams)
 export type IsosurfaceProps = typeof DefaultIsosurfaceProps
@@ -52,8 +53,12 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
 
     async function create(ctx: RuntimeContext, volume: VolumeData, props: Partial<IsosurfaceProps> = {}) {
         currentProps = { ...DefaultIsosurfaceProps, ...props }
+        if (props.isoValueRelative) {
+            currentProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
+            console.log('create props.isoValueRelative', props.isoValueRelative, currentProps.isoValueAbsolute, currentVolume.dataStats)
+        }
 
-        mesh = await createVolumeSurface(ctx, volume,  VolumeIsoValue.relative(volume.dataStats, currentProps.isoValue))
+        mesh = await createVolumeSurface(ctx, volume, currentProps.isoValueAbsolute)
 
         const locationIt = LocationIterator(1, 1, () => NullLocation)
         const transform = createIdentityTransform()
@@ -66,13 +71,17 @@ export function IsosurfaceVisual(): VolumeVisual<IsosurfaceProps> {
 
     async function update(ctx: RuntimeContext, props: Partial<IsosurfaceProps> = {}) {
         const newProps = { ...currentProps, ...props }
+        if (props.isoValueRelative) {
+            newProps.isoValueAbsolute = VolumeIsoValue.calcAbsolute(currentVolume.dataStats, props.isoValueRelative)
+            console.log('update props.isoValueRelative', props.isoValueRelative, newProps.isoValueAbsolute, currentVolume.dataStats)
+        }
 
         let createMesh = false
 
-        if (newProps.isoValue !== currentProps.isoValue) createMesh = true
+        if (newProps.isoValueAbsolute !== currentProps.isoValueAbsolute) createMesh = true
 
         if (createMesh) {
-            mesh = await createVolumeSurface(ctx, currentVolume,  VolumeIsoValue.relative(currentVolume.dataStats, currentProps.isoValue), mesh)
+            mesh = await createVolumeSurface(ctx, currentVolume, newProps.isoValueAbsolute, mesh)
             ValueCell.update(renderObject.values.drawCount, mesh.triangleCount * 3)
         }
 

+ 4 - 4
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 { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume';
+import { DirectVolume2dValues, DirectVolume2dRenderable } from './renderable/direct-volume';
 
 const getNextId = idFactory(0, 0x7FFFFFFF)
 
@@ -18,7 +18,7 @@ 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: DirectVolumeValues }
+export interface DirectVolumeRenderObject extends BaseRenderObject { type: 'direct-volume', values: DirectVolume2dValues }
 export type RenderObject = MeshRenderObject | PointsRenderObject | LinesRenderObject | GaussianDensityRenderObject | DirectVolumeRenderObject
 
 export function createMeshRenderObject(values: MeshValues, state: RenderableState): MeshRenderObject {
@@ -33,7 +33,7 @@ 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: DirectVolumeValues, state: RenderableState): DirectVolumeRenderObject {
+export function createDirectVolumeRenderObject(values: DirectVolume2dValues, state: RenderableState): DirectVolumeRenderObject {
     return { id: getNextId(), type: 'direct-volume', values, state }
 }
 
@@ -43,6 +43,6 @@ 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 DirectVolumeRenderable(ctx, o.id, o.values, o.state)
+        case 'direct-volume': return DirectVolume2dRenderable(ctx, o.id, o.values, o.state)
     }
 }

+ 19 - 9
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';
 
-export const DirectVolumeSchema = {
+const DirectVolumeBaseSchema = {
     drawCount: ValueSpec('number'),
     instanceCount: ValueSpec('number'),
 
@@ -27,20 +27,30 @@ export const DirectVolumeSchema = {
     uBboxSize: UniformSpec('v3'),
     uTransform: UniformSpec('m4'),
     uGridDim: UniformSpec('v3'),
-    uGridTexDim: UniformSpec('v2'),
-    tGridTex: TextureSpec('rgba', 'ubyte', 'linear'),
     dRenderMode: DefineSpec('string', ['isosurface', 'volume']),
-    tTransferTex: TextureSpec('rgba', 'ubyte', 'linear'),
+    tTransferTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
 }
-export type DirectVolumeSchema = typeof DirectVolumeSchema
-export type DirectVolumeValues = Values<DirectVolumeSchema>
 
-export function DirectVolumeRenderable(ctx: Context, id: number, values: DirectVolumeValues, state: RenderableState): Renderable<DirectVolumeValues> {
-    const schema = { ...GlobalUniformSchema, ...InternalSchema, ...DirectVolumeSchema }
-    const internalValues: InternalValues = {
+function getInternalValues(ctx: Context, id: number): InternalValues {
+    return {
         dWebGL2: ValueCell.create(ctx.isWebGL2),
         uObjectId: ValueCell.create(id)
     }
+}
+
+//
+
+export const DirectVolume2dSchema = {
+    ...DirectVolumeBaseSchema,
+    uGridTexDim: UniformSpec('v2'),
+    tGridTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
+}
+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);

+ 10 - 9
src/mol-gl/renderable/schema.ts

@@ -10,7 +10,7 @@ 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 { TextureValues, TextureType, TextureFormat, TextureFilter } from '../webgl/texture';
+import { TextureValues, TextureType, TextureFormat, TextureFilter, TextureKind } from '../webgl/texture';
 
 export type ValueKindType = {
     'number': number
@@ -39,7 +39,8 @@ export type KindValue = {
     'int32': Int32Array
     'float32': Float32Array
 
-    'image': TextureImage
+    'image-uint8': TextureImage<Uint8Array>
+    'image-float32': TextureImage<Float32Array>
 
     'number': number
     'string': string
@@ -84,9 +85,9 @@ export function UniformSpec<K extends UniformKind>(kind: K): UniformSpec<K> {
     return { type: 'uniform', kind }
 }
 
-export type TextureSpec = { type: 'texture', kind: 'image', format: TextureFormat, dataType: TextureType, filter: TextureFilter }
-export function TextureSpec(format: TextureFormat, dataType: TextureType, filter: TextureFilter): TextureSpec {
-    return { type: 'texture', kind: 'image', format, dataType, filter }
+export type TextureSpec<K extends TextureKind> = { type: 'texture', kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter }
+export function TextureSpec<K extends TextureKind>(kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter): TextureSpec<K> {
+    return { type: 'texture', kind, format, dataType, filter }
 }
 
 export type ElementsSpec<K extends ElementsKind> = { type: 'elements', kind: K }
@@ -108,7 +109,7 @@ export function ValueSpec<K extends ValueKind>(kind: K): ValueSpec<K> {
 
 export type RenderableSchema = {
     [k: string]: (
-        AttributeSpec<ArrayKind> | UniformSpec<UniformKind> | TextureSpec |
+        AttributeSpec<ArrayKind> | UniformSpec<UniformKind> | TextureSpec<TextureKind> |
         ValueSpec<ValueKind> | DefineSpec<DefineKind> | ElementsSpec<ElementsKind>
     )
 }
@@ -151,7 +152,7 @@ export const ColorSchema = {
     aColor: AttributeSpec('float32', 3, 0),
     uColor: UniformSpec('v3'),
     uColorTexDim: UniformSpec('v2'),
-    tColor: TextureSpec('rgb', 'ubyte', 'nearest'),
+    tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
     dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
 }
 export type ColorSchema = typeof ColorSchema
@@ -161,7 +162,7 @@ export const SizeSchema = {
     aSize: AttributeSpec('float32', 1, 0),
     uSize: UniformSpec('f'),
     uSizeTexDim: UniformSpec('v2'),
-    tSize: TextureSpec('alpha', 'ubyte', 'nearest'),
+    tSize: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
 }
 export type SizeSchema = typeof SizeSchema
@@ -179,7 +180,7 @@ export const BaseSchema = {
     uGroupCount: UniformSpec('i'),
     uMarkerTexDim: UniformSpec('v2'),
 
-    tMarker: TextureSpec('alpha', 'ubyte', 'nearest'),
+    tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
 
     drawCount: ValueSpec('number'),
     instanceCount: ValueSpec('number'),

+ 10 - 3
src/mol-gl/renderable/util.ts

@@ -16,13 +16,20 @@ export function calculateTextureInfo (n: number, itemSize: number) {
     return { width, height, length: width * height * itemSize }
 }
 
-export interface TextureImage {
-    readonly array: Uint8Array
+export interface TextureImage<T extends Uint8Array | Float32Array> {
+    readonly array: T
     readonly width: number
     readonly height: number
 }
 
-export function createTextureImage (n: number, itemSize: number): TextureImage {
+export interface TextureVolume<T extends Uint8Array | Float32Array> {
+    readonly array: T
+    readonly width: number
+    readonly height: number
+    readonly depth: number
+}
+
+export function createTextureImage(n: number, itemSize: number): TextureImage<Uint8Array> {
     const { length, width, height } = calculateTextureInfo(n, itemSize)
     return { array: new Uint8Array(length), width, height }
 }

+ 3 - 2
src/mol-gl/shader/direct-volume.frag

@@ -134,7 +134,7 @@ vec3 color = vec3(0.45, 0.55, 0.8);
 
 vec4 raymarch(vec3 cameraPos) {
     vec3 pos = unitCoord;
-    float prevValue = -1.0;
+    float prevValue = -127.0;
     float value = 0.0;
     float MAX_STEPS_F = max(max(uGridDim.x, uGridDim.y), uGridDim.z);
     // int MAX_STEPS = 2 * int(length(vec3(imgresx, imgresy, imgresz)));
@@ -196,8 +196,9 @@ vec4 raymarch(vec3 cameraPos) {
                 src.a = uAlpha;
 
                 // draw interior darker
-                if( (prevValue - uIsoValue) < 0.0 )
+                if( (prevValue - uIsoValue) > 0.0 ) {
                     src.rgb *= 0.5;
+                }
 
                 src.rgb *= src.a;
                 dst = (1.0 - dst.a) * src + dst; // standard blending

+ 15 - 1
src/mol-gl/webgl/compat.ts

@@ -61,7 +61,7 @@ export interface COMPAT_element_index_uint {
 }
 
 export function getElementIndexUint(gl: GLRenderingContext): COMPAT_element_index_uint | null {
-    return isWebGL2(gl) ? {} : gl.getExtension('OES_standard_derivatives')
+    return isWebGL2(gl) ? {} : gl.getExtension('OES_element_index_uint')
 }
 
 export interface COMPAT_vertex_array_object {
@@ -92,4 +92,18 @@ export function getVertexArrayObject(gl: GLRenderingContext): COMPAT_vertex_arra
             isVertexArray: ext.isVertexArrayOES.bind(ext)
         }
     }
+}
+
+export interface COMPAT_texture_float {
+}
+
+export function getTextureFloat(gl: GLRenderingContext): COMPAT_texture_float | null {
+    return isWebGL2(gl) ? {} : gl.getExtension('OES_texture_float')
+}
+
+export interface COMPAT_texture_float_linear {
+}
+
+export function getTextureFloatLinear(gl: GLRenderingContext): COMPAT_texture_float_linear | null {
+    return gl.getExtension('OES_texture_float_linear')
 }

+ 20 - 3
src/mol-gl/webgl/context.ts

@@ -6,7 +6,7 @@
 
 import { createProgramCache, ProgramCache } from './program'
 import { createShaderCache, ShaderCache } from './shader'
-import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, isWebGL2, COMPAT_element_index_uint, getElementIndexUint } from './compat';
+import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, isWebGL2, COMPAT_element_index_uint, getElementIndexUint, COMPAT_texture_float, getTextureFloat, COMPAT_texture_float_linear, getTextureFloatLinear } from './compat';
 
 export function getGLContext(canvas: HTMLCanvasElement, contextAttributes?: WebGLContextAttributes): GLRenderingContext | null {
     function getContext(contextId: 'webgl' | 'experimental-webgl' | 'webgl2') {
@@ -51,7 +51,7 @@ function unbindFramebuffer(gl: GLRenderingContext) {
     gl.bindFramebuffer(gl.FRAMEBUFFER, null)
 }
 
-export function createImageData(buffer: Uint8Array, width: number, height: number) {
+export function createImageData(buffer: ArrayLike<number>, width: number, height: number) {
     const w = width * 4
     const h = height
     const data = new Uint8ClampedArray(width * height * 4)
@@ -69,6 +69,8 @@ export function createImageData(buffer: Uint8Array, width: number, height: numbe
 type Extensions = {
     instancedArrays: COMPAT_instanced_arrays
     standardDerivatives: COMPAT_standard_derivatives
+    textureFloat: COMPAT_texture_float,
+    textureFloatLinear: COMPAT_texture_float_linear,
     elementIndexUint: COMPAT_element_index_uint | null
     vertexArrayObject: COMPAT_vertex_array_object | null
 }
@@ -111,6 +113,14 @@ export function createContext(gl: GLRenderingContext): Context {
     if (standardDerivatives === null) {
         throw new Error('Could not find support for "standard_derivatives"')
     }
+    const textureFloat = getTextureFloat(gl)
+    if (textureFloat === null) {
+        throw new Error('Could not find support for "texture_float"')
+    }
+    const textureFloatLinear = getTextureFloatLinear(gl)
+    if (textureFloatLinear === null) {
+        throw new Error('Could not find support for "texture_float_linear"')
+    }
     const elementIndexUint = getElementIndexUint(gl)
     if (elementIndexUint === null) {
         console.warn('Could not find support for "element_index_uint"')
@@ -130,7 +140,14 @@ export function createContext(gl: GLRenderingContext): Context {
     return {
         gl,
         isWebGL2: isWebGL2(gl),
-        extensions: { instancedArrays, standardDerivatives, elementIndexUint, vertexArrayObject },
+        extensions: {
+            instancedArrays,
+            standardDerivatives,
+            textureFloat,
+            textureFloatLinear,
+            elementIndexUint,
+            vertexArrayObject
+        },
         pixelRatio: getPixelRatio(),
 
         shaderCache,

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

@@ -17,7 +17,7 @@ export interface RenderTarget {
     readonly id: number
     readonly width: number
     readonly height: number
-    readonly image: TextureImage
+    readonly image: TextureImage<any>
     readonly texture: Texture
 
     bind: () => void
@@ -31,7 +31,7 @@ export interface RenderTarget {
 export function createRenderTarget (ctx: Context, _width: number, _height: number): RenderTarget {
     const { gl } = ctx
 
-    const image: Helpers.Mutable<TextureImage> = {
+    const image: Helpers.Mutable<TextureImage<Uint8Array>> = {
         array: new Uint8Array(_width * _height * 4),
         width: _width,
         height: _height

+ 41 - 18
src/mol-gl/webgl/texture.ts

@@ -13,8 +13,13 @@ import { Framebuffer } from './framebuffer';
 
 const getNextTextureId = idFactory()
 
+export type TextureKindValue = {
+    'image-uint8': TextureImage<Uint8Array>
+    'image-float32': TextureImage<Float32Array>
+}
+export type TextureKind = keyof TextureKindValue
+export type TextureType = 'ubyte' | 'float'
 export type TextureFormat = 'alpha' | 'rgb' | 'rgba'
-export type TextureType = 'ubyte' | 'uint'
 export type TextureAttachment = 'depth' | 'stencil' | 'color0'
 export type TextureFilter = 'nearest' | 'linear'
 
@@ -27,11 +32,35 @@ export function getFormat(ctx: Context, format: TextureFormat) {
     }
 }
 
+export function getInternalFormat(ctx: Context, format: TextureFormat, type: TextureType) {
+    const { gl, isWebGL2 } = ctx
+    if (isWebGL2) {
+        switch (format) {
+            case 'alpha':
+                switch (type) {
+                    case 'ubyte': return gl.ALPHA
+                    case 'float': throw new Error('invalid format/type combination alpha/float')
+                }
+            case 'rgb':
+                switch (type) {
+                    case 'ubyte': return gl.RGB
+                    case 'float': return (gl as WebGL2RenderingContext).RGB32F
+                }
+            case 'rgba':
+                switch (type) {
+                    case 'ubyte': return gl.RGBA
+                    case 'float': return (gl as WebGL2RenderingContext).RGBA32F
+                }
+        }
+    }
+    return getFormat(ctx, format)
+}
+
 export function getType(ctx: Context, type: TextureType) {
     const { gl } = ctx
     switch (type) {
         case 'ubyte': return gl.UNSIGNED_BYTE
-        case 'uint': return gl.UNSIGNED_INT
+        case 'float': return gl.FLOAT
     }
 }
 
@@ -55,12 +84,10 @@ export function getAttachment(ctx: Context, attachment: TextureAttachment) {
 export interface Texture {
     readonly id: number
     readonly format: number
+    readonly internalFormat: number
     readonly type: number
 
-    readonly width: number
-    readonly height: number
-
-    load: (image: TextureImage) => void
+    load: (image: TextureImage<any>) => void
     bind: (id: TextureId) => void
     unbind: (id: TextureId) => void
     attachFramebuffer: (framebuffer: Framebuffer, attachment: TextureAttachment) => void
@@ -69,7 +96,7 @@ export interface Texture {
 
 export type TextureId = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15
 
-export type TextureValues = { [k: string]: ValueCell<TextureImage> }
+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 {
@@ -80,33 +107,29 @@ export function createTexture(ctx: Context, _format: TextureFormat, _type: Textu
         throw new Error('Could not create WebGL texture')
     }
 
+
+
     const filter = getFilter(ctx, _filter)
     const format = getFormat(ctx, _format)
+    const internalFormat = getInternalFormat(ctx, _format, _type)
     const type = getType(ctx, _type)
 
-    let _width = 0
-    let _height = 0
-
     let destroyed = false
     ctx.textureCount += 1
 
     return {
         id,
         format,
+        internalFormat,
         type,
 
-        get width () { return _width },
-        get height () { return _height },
-
-        load: (image: TextureImage) => {
+        load: (image: TextureImage<any>) => {
             const { array, width, height } = image
             gl.bindTexture(gl.TEXTURE_2D, texture)
             // unpack alignment of 1 since we use textures only for data
             gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
-            // gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
-            (gl as WebGLRenderingContext).texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, type, array) // TODO remove cast when webgl2 types are fixed
-            _width = width
-            _height = height
+            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)
             // clamp-to-edge needed for non-power-of-two textures

+ 1 - 2
src/mol-gl/webgl/uniform.ts

@@ -6,7 +6,6 @@
 
 import { Mat3, Mat4, Vec2, Vec3, Vec4 } from 'mol-math/linear-algebra'
 import { Context } from './context';
-import { TextureImage } from '../renderable/util';
 import { ValueCell } from 'mol-util';
 import { RenderableSchema } from '../renderable/schema';
 
@@ -21,7 +20,7 @@ export type UniformKindValue = {
     't2': number
 }
 export type UniformKind = keyof UniformKindValue
-export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4 | TextureImage
+export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4
 export interface UniformUpdater {
     set: (value: UniformType) => void,
     clear: () => void

+ 10 - 6
src/mol-model/volume/data.ts

@@ -39,25 +39,29 @@ namespace VolumeIsoValue {
     export function absolute(stats: VolumeData['dataStats'], value: number): Absolute { return { kind: 'absolute', stats, absoluteValue: value }; }
     export function relative(stats: VolumeData['dataStats'], value: number): Relative { return { kind: 'relative', stats, relativeValue: value }; }
 
+    export function calcAbsolute(stats: VolumeData['dataStats'], relativeValue: number): number {
+        return relativeValue * stats.sigma + stats.mean
+    }
+
+    export function calcRelative(stats: VolumeData['dataStats'], absoluteValue: number): number {
+        return (stats.mean - absoluteValue) / stats.sigma
+    }
+
     export function toAbsolute(value: VolumeIsoValue): Absolute {
         if (value.kind === 'absolute') return value;
-
-        const { mean, sigma } = value.stats
         return {
             kind: 'absolute',
             stats: value.stats,
-            absoluteValue: value.relativeValue * sigma + mean
+            absoluteValue: calcAbsolute(value.stats, value.relativeValue)
         }
     }
 
     export function toRelative(value: VolumeIsoValue): Relative {
         if (value.kind === 'relative') return value;
-
-        const { mean, sigma } = value.stats
         return {
             kind: 'relative',
             stats: value.stats,
-            relativeValue: (mean - value.absoluteValue) / sigma
+            relativeValue: calcRelative(value.stats, value.absoluteValue)
         }
     }
 }