diff --git a/src/mol-geo/geometry/direct-volume/direct-volume.ts b/src/mol-geo/geometry/direct-volume/direct-volume.ts index d89d13979bdd2484e624521b589414510564953f..4e420d977cd65e00f7971dcee29d5dc8af96733b 100644 --- a/src/mol-geo/geometry/direct-volume/direct-volume.ts +++ b/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) } } \ No newline at end of file diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index b5c094fc2efb84ec161a72296e1d7015d7076a16..5c1cae6f6840b9ee845365deeb5f5ade64586b51 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/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) diff --git a/src/mol-geo/representation/structure/units-visual.ts b/src/mol-geo/representation/structure/units-visual.ts index 0763753ed9fbf0e2f393a9664a716e1bf0d7bdbd..08f3eedd1a0b119fc3be8ba907f237f860b9d67d 100644 --- a/src/mol-geo/representation/structure/units-visual.ts +++ b/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 diff --git a/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts b/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts index ff77b61640e2b0b325b8b4bad9d37e08f63f893f..d8a42b9b7c70c70bf96c69f466b4d2551d21fd03 100644 --- a/src/mol-geo/representation/structure/visual/gaussian-density-volume.ts +++ b/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)), diff --git a/src/mol-geo/representation/structure/visual/util/common.ts b/src/mol-geo/representation/structure/visual/util/common.ts index 98257c06dedba9fe748e39365c515d6169aeedd3..7c95a0300faaa2f271f576bc8cc37bda52178c4a 100644 --- a/src/mol-geo/representation/structure/visual/util/common.ts +++ b/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) } \ No newline at end of file diff --git a/src/mol-geo/representation/volume/direct-volume.ts b/src/mol-geo/representation/volume/direct-volume.ts index 4f13ee8cc63e3af484abd77a8195844f29e07a01..db1cc6544c5dd503704fd326e213cd2cda2e5cd9 100644 --- a/src/mol-geo/representation/volume/direct-volume.ts +++ b/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 diff --git a/src/mol-gl/render-object.ts b/src/mol-gl/render-object.ts index 51c8fb902ce1c7f8a357cb9a4156ba6d44517818..f600d65a55be3f7acba20bfe1782bb821abc3df3 100644 --- a/src/mol-gl/render-object.ts +++ b/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) } } \ No newline at end of file diff --git a/src/mol-gl/renderable/direct-volume.ts b/src/mol-gl/renderable/direct-volume.ts index 5464f41959c1e2ed025dac80c680d7409ef44eca..55d3143f07fa1780a59c7ec0a22eb6ff63ef6653 100644 --- a/src/mol-gl/renderable/direct-volume.ts +++ b/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') } \ No newline at end of file diff --git a/src/mol-gl/renderable/lines.ts b/src/mol-gl/renderable/lines.ts index 9f35feb42f45ae049f724971dc2a63b6981ec77b..186772e2ea67115abd1339b24d79dd7db754cdb2 100644 --- a/src/mol-gl/renderable/lines.ts +++ b/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 diff --git a/src/mol-gl/renderable/mesh.ts b/src/mol-gl/renderable/mesh.ts index b40670a74d9987017bba3453034d846e0d0ab6b6..fd16901cc765c7ff23b52c21e5e2b044346a9f53 100644 --- a/src/mol-gl/renderable/mesh.ts +++ b/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 diff --git a/src/mol-gl/renderable/points.ts b/src/mol-gl/renderable/points.ts index 6270b9a58cde33b0c048c7f71499f4a18fc10995..f92bc00a5956f8a4e0627fb083b99fb895230624 100644 --- a/src/mol-gl/renderable/points.ts +++ b/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 diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index 01ff196338b5576637e0725e0766044279573f8e..55dcdec498fed1fe9c9ff131a39bdde9f53d1cfb 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/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 diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 9499cb7ab508bcd2566aa409bd07551a45e35d21..93bfcab696c6e7e7e1a010aac1db3d1f36b93d89 100644 --- a/src/mol-gl/shader-code.ts +++ b/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}` } } \ No newline at end of file diff --git a/src/mol-gl/shader/direct-volume.frag b/src/mol-gl/shader/direct-volume.frag index ac72c81a0fd05ff3cb1b160c34e1fa8a80f8f160..54b80b2fb79b5fc60d76c8b3cf36717b273a6bfc 100644 --- a/src/mol-gl/shader/direct-volume.frag +++ b/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; diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 6f3d3e6c8e1206af37bb731526ff9bf7768c1ab0..7c37fb60991798c495cadda0707299055d60f00b 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/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 { diff --git a/src/mol-gl/webgl/render-target.ts b/src/mol-gl/webgl/render-target.ts index 81ddd9fb87c8a7addd817b600ca3b341144722b8..92c840867abf0c7c0b5d4f41c5e82f78a4dd3792 100644 --- a/src/mol-gl/webgl/render-target.ts +++ b/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) diff --git a/src/mol-gl/webgl/texture.ts b/src/mol-gl/webgl/texture.ts index c090c1aca7c2c70e7db686000371b56c20c2d2f7..26966378896c86d6ffc3cd018381387775d237ec 100644 --- a/src/mol-gl/webgl/texture.ts +++ b/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 }