Browse Source

move direct-volume coloring into theme

- add 'direct' color type
- remove color from transfer-function (now only alpha)
- add direct-volume color theme support
- add volume-value color theme
Alexander Rose 3 years ago
parent
commit
47d7dd4d22

+ 24 - 1
src/mol-geo/geometry/color-data.ts

@@ -15,7 +15,7 @@ import { LocationColor, ColorTheme } from '../../mol-theme/color';
 import { Geometry } from './geometry';
 import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
 
-export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance'
+export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance' | 'direct'
 
 export type ColorData = {
     uColor: ValueCell<Vec3>,
@@ -50,6 +50,7 @@ function _createColors(locationIt: LocationIterator, positionIt: LocationIterato
         case 'vertexInstance': return createVertexInstanceColor(positionIt, colorTheme.color, colorData);
         case 'volume': return createGridColor((colorTheme as any).grid, 'volume', colorData);
         case 'volumeInstance': return createGridColor((colorTheme as any).grid, 'volumeInstance', colorData);
+        case 'direct': return createDirectColor(colorData);
     }
 }
 
@@ -237,3 +238,25 @@ export function createGridColor(grid: ColorVolume, type: ColorType, colorData?:
         };
     }
 }
+
+//
+
+/** Creates direct color */
+function createDirectColor(colorData?: ColorData): ColorData {
+    if (colorData) {
+        ValueCell.updateIfChanged(colorData.dColorType, 'direct');
+        return colorData;
+    } else {
+        return {
+            uColor: ValueCell.create(Vec3()),
+            tColor: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
+            tColorGrid: ValueCell.create(createNullTexture()),
+            tPalette: ValueCell.create({ array: new Uint8Array(3), width: 1, height: 1 }),
+            uColorTexDim: ValueCell.create(Vec2.create(1, 1)),
+            uColorGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
+            uColorGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
+            dColorType: ValueCell.create('direct'),
+            dUsePalette: ValueCell.create(false),
+        };
+    }
+}

+ 10 - 32
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -27,7 +27,6 @@ import { createEmptyTransparency } from '../transparency-data';
 import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
 import { createEmptyClipping } from '../clipping-data';
 import { Grid } from '../../../mol-model/volume';
-import { ColorNames } from '../../../mol-util/color/names';
 import { createEmptySubstance } from '../substance-data';
 
 const VolumeBox = Box();
@@ -141,36 +140,15 @@ export namespace DirectVolume {
         return create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, directVolume);
     }
 
-    export function createRenderModeParam() {
-        return PD.MappedStatic('volume', {
-            volume: PD.Group({
-                controlPoints: PD.LineGraph([
-                    Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
-                    Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
-                ]),
-                list: PD.ColorList({
-                    kind: 'interpolate',
-                    colors: [
-                        [ColorNames.white, 0],
-                        [ColorNames.red, 0.25],
-                        [ColorNames.white, 0.5],
-                        [ColorNames.blue, 0.75],
-                        [ColorNames.white, 1]
-                    ]
-                }, { offsets: true }),
-            }, { isFlat: true })
-        }, { isEssential: true });
-    }
-
     export const Params = {
         ...BaseGeometry.Params,
-        doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
-        flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
-        flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
         ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
         xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
-        renderMode: createRenderModeParam(),
-        stepsPerCell: PD.Numeric(5, { min: 1, max: 20, step: 1 }),
+        controlPoints: PD.LineGraph([
+            Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
+            Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
+        ], { isEssential: true }),
+        stepsPerCell: PD.Numeric(3, { min: 1, max: 10, step: 1 }),
         jumpLength: PD.Numeric(0, { min: 0, max: 20, step: 0.1 }),
     };
     export type Params = typeof Params
@@ -241,8 +219,8 @@ export namespace DirectVolume {
         const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
         const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
 
-        const controlPoints = getControlPointsFromVec2Array(props.renderMode.params.controlPoints);
-        const transferTex = createTransferFunctionTexture(controlPoints, props.renderMode.params.list.colors);
+        const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
+        const transferTex = createTransferFunctionTexture(controlPoints);
 
         return {
             ...color,
@@ -268,7 +246,7 @@ export namespace DirectVolume {
             uJumpLength: ValueCell.create(props.jumpLength),
             uTransform: gridTransform,
             uGridDim: gridDimension,
-            dRenderMode: ValueCell.create(props.renderMode.name),
+            dRenderMode: ValueCell.create('volume'),
             tTransferTex: transferTex,
             uTransferScale: ValueCell.create(getTransferScale(props.stepsPerCell)),
 
@@ -298,8 +276,8 @@ export namespace DirectVolume {
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
         ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
 
-        const controlPoints = getControlPointsFromVec2Array(props.renderMode.params.controlPoints);
-        createTransferFunctionTexture(controlPoints, props.renderMode.params.list.colors, values.tTransferTex);
+        const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
+        createTransferFunctionTexture(controlPoints, values.tTransferTex);
 
         ValueCell.updateIfChanged(values.uMaxSteps, getMaxSteps(values.uGridDim.ref.value, props.stepsPerCell));
         ValueCell.updateIfChanged(values.uStepScale, getStepScale(values.uCellDim.ref.value, props.stepsPerCell));

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

@@ -1,16 +1,13 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { TextureImage } from '../../../mol-gl/renderable/util';
 import { spline } from '../../../mol-math/interpolate';
-import { ColorScale } from '../../../mol-util/color';
 import { ValueCell } from '../../../mol-util';
 import { Vec2 } from '../../../mol-math/linear-algebra';
-import { ColorListName } from '../../../mol-util/color/lists';
-import { ColorListEntry } from '../../../mol-util/color/color';
 
 export interface ControlPoint { x: number, alpha: number }
 
@@ -25,7 +22,7 @@ export function getControlPointsFromVec2Array(array: Vec2[]): ControlPoint[] {
     return array.map(v => ({ x: v[0], alpha: v[1] }));
 }
 
-export function createTransferFunctionTexture(controlPoints: ControlPoint[], listOrName: ColorListEntry[] | ColorListName, texture?: ValueCell<TextureImage<Uint8Array>>): ValueCell<TextureImage<Uint8Array>> {
+export function createTransferFunctionTexture(controlPoints: ControlPoint[], texture?: ValueCell<TextureImage<Uint8Array>>): ValueCell<TextureImage<Uint8Array>> {
     const cp = [
         { x: 0, alpha: 0 },
         { x: 0, alpha: 0 },
@@ -33,10 +30,9 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], lis
         { x: 1, alpha: 0 },
         { x: 1, alpha: 0 },
     ];
-    const scale = ColorScale.create({ domain: [0, 1], listOrName });
 
     const n = 256;
-    const array = texture ? texture.ref.value.array : new Uint8Array(n * 4);
+    const array = texture ? texture.ref.value.array : new Uint8Array(n);
 
     let k = 0;
     let x1: number, x2: number;
@@ -55,8 +51,7 @@ export function createTransferFunctionTexture(controlPoints: ControlPoint[], lis
         const jl = Math.round((x2 - x1) * n);
         for (let j = 0; j < jl; ++j) {
             const t = j / jl;
-            array[k * 4 + 3] = Math.max(0, spline(a0, a1, a2, a3, t, 0.5) * 255);
-            scale.colorToArray(k / 255, array, k * 4);
+            array[k] = Math.max(0, spline(a0, a1, a2, a3, t, 0.5) * 255);
             ++k;
         }
     }

+ 1 - 1
src/mol-gl/renderable/direct-volume.ts

@@ -26,7 +26,7 @@ export const DirectVolumeSchema = {
     uTransform: UniformSpec('m4'),
     uGridDim: UniformSpec('v3'),
     dRenderMode: DefineSpec('string', ['volume']),
-    tTransferTex: TextureSpec('image-uint8', 'rgba', 'ubyte', 'linear'),
+    tTransferTex: TextureSpec('image-uint8', 'alpha', 'ubyte', 'linear'),
     uTransferScale: UniformSpec('f'),
 
     dGridTexType: DefineSpec('string', ['2d', '3d']),

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

@@ -182,7 +182,7 @@ export const ColorSchema = {
     tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
     tPalette: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
     tColorGrid: TextureSpec('texture', 'rgb', 'ubyte', 'linear'),
-    dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance', 'volume', 'volumeInstance']),
+    dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance', 'vertex', 'vertexInstance', 'volume', 'volumeInstance', 'direct']),
     dUsePalette: DefineSpec('boolean'),
 } as const;
 export type ColorSchema = typeof ColorSchema

+ 58 - 9
src/mol-gl/shader/direct-volume.frag.ts

@@ -101,6 +101,26 @@ uniform mat4 uCartnToUnit;
     uniform sampler3D tGridTex;
 #endif
 
+#if defined(dRenderVariant_color)
+    #if defined(dColorType_uniform)
+        uniform vec3 uColor;
+    #elif defined(dColorType_texture)
+        uniform vec2 uColorTexDim;
+        uniform sampler2D tColor;
+    #endif
+
+    #ifdef dOverpaint
+        #if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
+            uniform vec2 uOverpaintTexDim;
+            uniform sampler2D tOverpaint;
+        #endif
+    #endif
+
+    #ifdef dUsePalette
+        uniform sampler2D tPalette;
+    #endif
+#endif
+
 #if defined(dGridTexType_2d)
     vec4 textureVal(vec3 pos) {
         return texture3dFrom2dLinear(tGridTex, pos + (vec3(0.5, 0.5, 0.0) / uGridDim), uGridDim, uGridTexDim.xy);
@@ -122,8 +142,8 @@ float calcDepth(const in vec3 pos) {
     return 0.5 + 0.5 * clipZW.x / clipZW.y;
 }
 
-vec4 transferFunction(float value) {
-    return texture2D(tTransferTex, vec2(value, 0.0));
+float transferFunction(float value) {
+    return texture2D(tTransferTex, vec2(value, 0.0)).a;
 }
 
 float getDepth(const in vec2 coords) {
@@ -174,6 +194,8 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
     vec3 nextPos;
     float nextValue;
 
+    vec4 material;
+    vec4 overpaint;
     float metalness = uMetalness;
     float roughness = uRoughness;
 
@@ -229,7 +251,40 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
 
         #if defined(dRenderVariant_color)
             vec3 vViewPosition = mvPosition.xyz;
-            vec4 material = transferFunction(value);
+            material.a = transferFunction(value);
+
+            #ifdef dPackedGroup
+                float group = decodeFloatRGB(textureGroup(floor(unitPos * uGridDim + 0.5) / uGridDim).rgb);
+            #else
+                vec3 g = floor(unitPos * uGridDim + 0.5);
+                float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
+            #endif
+
+            #if defined(dColorType_direct) && defined(dUsePalette)
+                material.rgb = texture2D(tPalette, vec2(value, 0.0)).rgb;
+            #elif defined(dColorType_uniform)
+                material.rgb = uColor;
+            #elif defined(dColorType_instance)
+                material.rgb = readFromTexture(tColor, vInstance, uColorTexDim).rgb;
+            #elif defined(dColorType_group)
+                material.rgb = readFromTexture(tColor, group, uColorTexDim).rgb;
+            #elif defined(dColorType_groupInstance)
+                material.rgb = readFromTexture(tColor, vInstance * float(uGroupCount) + group, uColorTexDim).rgb;
+            #elif defined(dColorType_vertex)
+                material.rgb = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, 0.0).rgb;
+            #elif defined(dColorType_vertexInstance)
+                material.rgb = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, vInstance * float(uVertexCount)).rgb;
+            #endif
+
+            #ifdef dOverpaint
+                #if defined(dOverpaintType_groupInstance)
+                    overpaint = readFromTexture(tOverpaint, vInstance * float(uGroupCount) + group, uOverpaintTexDim);
+                #elif defined(dOverpaintType_vertexInstance)
+                    overpaint = texture3dFrom1dTrilinear(tOverpaint, isoPos, uGridDim, uOverpaintTexDim, vInstance * float(uVertexCount));
+                #endif
+
+                material.rgb = mix(material.rgb, overpaint.rgb, overpaint.a);
+            #endif
 
             #ifdef dIgnoreLight
                 gl_FragColor.rgb = material.rgb;
@@ -254,12 +309,6 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
 
             float marker = uMarker;
             if (uMarker == -1.0) {
-                #ifdef dPackedGroup
-                    float group = decodeFloatRGB(textureGroup(floor(unitPos * uGridDim + 0.5) / uGridDim).rgb);
-                #else
-                    vec3 g = floor(unitPos * uGridDim + 0.5);
-                    float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
-                #endif
                 marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
                 marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
             }

+ 1 - 1
src/mol-repr/volume/direct-volume.ts

@@ -158,7 +158,7 @@ export const DirectVolumeRepresentationProvider = VolumeRepresentationProvider({
     factory: DirectVolumeRepresentation,
     getParams: getDirectVolumeParams,
     defaultValues: PD.getDefaultValues(DirectVolumeParams),
-    defaultColorTheme: { name: 'uniform' },
+    defaultColorTheme: { name: 'volume-value' },
     defaultSizeTheme: { name: 'uniform' },
     isApplicable: (volume: Volume) => !Volume.isEmpty(volume)
 });

+ 2 - 0
src/mol-theme/color.ts

@@ -36,6 +36,7 @@ import { PartialChargeColorThemeProvider } from './color/partial-charge';
 import { AtomIdColorThemeProvider } from './color/atom-id';
 import { EntityIdColorThemeProvider } from './color/entity-id';
 import { TextureFilter } from '../mol-gl/webgl/texture';
+import { VolumeValueColorThemeProvider } from './color/volume-value';
 
 export type LocationColor = (location: Location, isSecondary: boolean) => Color
 
@@ -120,6 +121,7 @@ namespace ColorTheme {
         'uncertainty': UncertaintyColorThemeProvider,
         'unit-index': UnitIndexColorThemeProvider,
         'uniform': UniformColorThemeProvider,
+        'volume-value': VolumeValueColorThemeProvider,
     };
     type _BuiltIn = typeof BuiltIn
     export type BuiltIn = keyof _BuiltIn

+ 62 - 0
src/mol-theme/color/volume-value.ts

@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ColorTheme } from '../color';
+import { Color, ColorScale } from '../../mol-util/color';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { ThemeDataContext } from '../theme';
+import { ColorNames } from '../../mol-util/color/names';
+
+const DefaultColor = Color(0xCCCCCC);
+const Description = 'Assign color based on the given value of a volume cell.';
+
+export const VolumeValueColorThemeParams = {
+    colorList: PD.ColorList({
+        kind: 'interpolate',
+        colors: [
+            [ColorNames.white, 0],
+            [ColorNames.red, 0.25],
+            [ColorNames.white, 0.5],
+            [ColorNames.blue, 0.75],
+            [ColorNames.white, 1]
+        ]
+    }, { offsets: true, isEssential: true }),
+};
+export type VolumeValueColorThemeParams = typeof VolumeValueColorThemeParams
+export function getVolumeValueColorThemeParams(ctx: ThemeDataContext) {
+    return VolumeValueColorThemeParams; // TODO return copy
+}
+
+export function VolumeValueColorTheme(ctx: ThemeDataContext, props: PD.Values<VolumeValueColorThemeParams>): ColorTheme<VolumeValueColorThemeParams> {
+    const scale = ColorScale.create({ domain: [0, 1], listOrName: props.colorList.colors });
+
+    const colors: Color[] = [];
+    for (let i = 0; i < 256; ++i) {
+        colors[i] = scale.color(i / 255);
+    }
+
+    const palette: ColorTheme.Palette = { colors, filter: 'linear' };
+
+    return {
+        factory: VolumeValueColorTheme,
+        granularity: 'direct',
+        color: () => DefaultColor,
+        props: props,
+        description: Description,
+        legend: scale.legend,
+        palette,
+    };
+}
+
+export const VolumeValueColorThemeProvider: ColorTheme.Provider<VolumeValueColorThemeParams, 'volume-value'> = {
+    name: 'volume-value',
+    label: 'Volume Value',
+    category: ColorTheme.Category.Misc,
+    factory: VolumeValueColorTheme,
+    getParams: getVolumeValueColorThemeParams,
+    defaultValues: PD.getDefaultValues(VolumeValueColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.volume,
+};