Sfoglia il codice sorgente

grid-based smoothing of Overpaint and Transparency

Alexander Rose 3 anni fa
parent
commit
c8018800cc

+ 1 - 0
CHANGELOG.md

@@ -11,6 +11,7 @@ Note that since we don't clearly distinguish between a public and private interf
     - StructureSelectionQuery helpers 'helix' & 'beta' were not ensuring property availability
     - StructureSelectionQuery helpers 'helix' & 'beta' were not ensuring property availability
 - Re-enable VAO with better workaround (bind null elements buffer before deleting)
 - Re-enable VAO with better workaround (bind null elements buffer before deleting)
 - Add ``Representation.geometryVersion`` (increments whenever the geometry of any of its visuals changes)
 - Add ``Representation.geometryVersion`` (increments whenever the geometry of any of its visuals changes)
+- Add support for grid-based smoothing of Overpaint and Transparency visual state for surfaces
 
 
 ## [v2.3.9] - 2021-11-20
 ## [v2.3.9] - 2021-11-20
 
 

+ 68 - 0
src/mol-geo/geometry/mesh/color-smoothing.ts

@@ -288,3 +288,71 @@ export function applyMeshColorSmoothing(values: MeshValues, resolution: number,
         ValueCell.update(values.uColorTexDim, smoothingData.texDim);
         ValueCell.update(values.uColorTexDim, smoothingData.texDim);
     }
     }
 }
 }
+
+function isSupportedOverpaintType(x: string): x is 'groupInstance' {
+    return x === 'groupInstance';
+}
+
+export function applyMeshOverpaintSmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
+    if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
+
+    const smoothingData = calcMeshColorSmoothing({
+        vertexCount: values.uVertexCount.ref.value,
+        instanceCount: values.uInstanceCount.ref.value,
+        groupCount: values.uGroupCount.ref.value,
+        transformBuffer: values.aTransform.ref.value,
+        instanceBuffer: values.aInstance.ref.value,
+        positionBuffer: values.aPosition.ref.value,
+        groupBuffer: values.aGroup.ref.value,
+        colorData: values.tOverpaint.ref.value,
+        colorType: values.dOverpaintType.ref.value,
+        boundingSphere: values.boundingSphere.ref.value,
+        invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
+        itemSize: 4
+    }, resolution, stride, webgl, colorTexture);
+    if (smoothingData.kind === 'volume') {
+        ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
+        ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
+        ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
+        ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
+        ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
+    } else if (smoothingData.kind === 'vertex') {
+        ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
+        ValueCell.update(values.tOverpaint, smoothingData.texture);
+        ValueCell.update(values.uOverpaintTexDim, smoothingData.texDim);
+    }
+}
+
+function isSupportedTransparencyType(x: string): x is 'groupInstance' {
+    return x === 'groupInstance';
+}
+
+export function applyMeshTransparencySmoothing(values: MeshValues, resolution: number, stride: number, webgl?: WebGLContext, colorTexture?: Texture) {
+    if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
+
+    const smoothingData = calcMeshColorSmoothing({
+        vertexCount: values.uVertexCount.ref.value,
+        instanceCount: values.uInstanceCount.ref.value,
+        groupCount: values.uGroupCount.ref.value,
+        transformBuffer: values.aTransform.ref.value,
+        instanceBuffer: values.aInstance.ref.value,
+        positionBuffer: values.aPosition.ref.value,
+        groupBuffer: values.aGroup.ref.value,
+        colorData: values.tTransparency.ref.value,
+        colorType: values.dTransparencyType.ref.value,
+        boundingSphere: values.boundingSphere.ref.value,
+        invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
+        itemSize: 1
+    }, resolution, stride, webgl, colorTexture);
+    if (smoothingData.kind === 'volume') {
+        ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
+        ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
+        ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
+        ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
+        ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
+    } else if (smoothingData.kind === 'vertex') {
+        ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
+        ValueCell.update(values.tTransparency, smoothingData.texture);
+        ValueCell.update(values.uTransparencyTexDim, smoothingData.texDim);
+    }
+}

+ 18 - 2
src/mol-geo/geometry/overpaint-data.ts

@@ -1,18 +1,24 @@
 /**
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
  */
 
 
 import { ValueCell } from '../../mol-util/value-cell';
 import { ValueCell } from '../../mol-util/value-cell';
-import { Vec2 } from '../../mol-math/linear-algebra';
+import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
 import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
 import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
 import { Color } from '../../mol-util/color';
 import { Color } from '../../mol-util/color';
+import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
 
 
 export type OverpaintData = {
 export type OverpaintData = {
     tOverpaint: ValueCell<TextureImage<Uint8Array>>
     tOverpaint: ValueCell<TextureImage<Uint8Array>>
     uOverpaintTexDim: ValueCell<Vec2>
     uOverpaintTexDim: ValueCell<Vec2>
     dOverpaint: ValueCell<boolean>,
     dOverpaint: ValueCell<boolean>,
+
+    tOverpaintGrid: ValueCell<Texture>,
+    uOverpaintGridDim: ValueCell<Vec3>,
+    uOverpaintGridTransform: ValueCell<Vec4>,
+    dOverpaintType: ValueCell<string>,
 }
 }
 
 
 export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color) {
 export function applyOverpaintColor(array: Uint8Array, start: number, end: number, color: Color) {
@@ -40,6 +46,11 @@ export function createOverpaint(count: number, overpaintData?: OverpaintData): O
             tOverpaint: ValueCell.create(overpaint),
             tOverpaint: ValueCell.create(overpaint),
             uOverpaintTexDim: ValueCell.create(Vec2.create(overpaint.width, overpaint.height)),
             uOverpaintTexDim: ValueCell.create(Vec2.create(overpaint.width, overpaint.height)),
             dOverpaint: ValueCell.create(count > 0),
             dOverpaint: ValueCell.create(count > 0),
+
+            tOverpaintGrid: ValueCell.create(createNullTexture()),
+            uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
+            uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
+            dOverpaintType: ValueCell.create('groupInstance'),
         };
         };
     }
     }
 }
 }
@@ -55,6 +66,11 @@ export function createEmptyOverpaint(overpaintData?: OverpaintData): OverpaintDa
             tOverpaint: ValueCell.create(emptyOverpaintTexture),
             tOverpaint: ValueCell.create(emptyOverpaintTexture),
             uOverpaintTexDim: ValueCell.create(Vec2.create(1, 1)),
             uOverpaintTexDim: ValueCell.create(Vec2.create(1, 1)),
             dOverpaint: ValueCell.create(false),
             dOverpaint: ValueCell.create(false),
+
+            tOverpaintGrid: ValueCell.create(createNullTexture()),
+            uOverpaintGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
+            uOverpaintGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
+            dOverpaintType: ValueCell.create('groupInstance'),
         };
         };
     }
     }
 }
 }

+ 74 - 0
src/mol-geo/geometry/texture-mesh/color-smoothing.ts

@@ -390,6 +390,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
 //
 //
 
 
 const ColorSmoothingRgbName = 'color-smoothing-rgb';
 const ColorSmoothingRgbName = 'color-smoothing-rgb';
+const ColorSmoothingRgbaName = 'color-smoothing-rgba';
+const ColorSmoothingAlphaName = 'color-smoothing-alpha';
 
 
 function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
 function isSupportedColorType(x: string): x is 'group' | 'groupInstance' {
     return x === 'group' || x === 'groupInstance';
     return x === 'group' || x === 'groupInstance';
@@ -426,3 +428,75 @@ export function applyTextureMeshColorSmoothing(values: TextureMeshValues, resolu
     ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
     ValueCell.update(values.uColorGridDim, smoothingData.gridDim);
     ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
     ValueCell.update(values.uColorGridTransform, smoothingData.gridTransform);
 }
 }
+
+function isSupportedOverpaintType(x: string): x is 'groupInstance' {
+    return x === 'groupInstance';
+}
+
+export function applyTextureMeshOverpaintSmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
+    if (!isSupportedOverpaintType(values.dOverpaintType.ref.value)) return;
+
+    stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
+
+    if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
+        webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
+    }
+    const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
+    colorData.load(values.tOverpaint.ref.value);
+
+    const smoothingData = calcTextureMeshColorSmoothing({
+        vertexCount: values.uVertexCount.ref.value,
+        instanceCount: values.uInstanceCount.ref.value,
+        groupCount: values.uGroupCount.ref.value,
+        transformBuffer: values.aTransform.ref.value,
+        instanceBuffer: values.aInstance.ref.value,
+        positionTexture: values.tPosition.ref.value,
+        groupTexture: values.tGroup.ref.value,
+        colorData,
+        colorType: values.dOverpaintType.ref.value,
+        boundingSphere: values.boundingSphere.ref.value,
+        invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
+    }, resolution, stride, webgl, colorTexture);
+
+    ValueCell.updateIfChanged(values.dOverpaintType, smoothingData.type);
+    ValueCell.update(values.tOverpaintGrid, smoothingData.texture);
+    ValueCell.update(values.uOverpaintTexDim, smoothingData.gridTexDim);
+    ValueCell.update(values.uOverpaintGridDim, smoothingData.gridDim);
+    ValueCell.update(values.uOverpaintGridTransform, smoothingData.gridTransform);
+}
+
+function isSupportedTransparencyType(x: string): x is 'groupInstance' {
+    return x === 'groupInstance';
+}
+
+export function applyTextureMeshTransparencySmoothing(values: TextureMeshValues, resolution: number, stride: number, webgl: WebGLContext, colorTexture?: Texture) {
+    if (!isSupportedTransparencyType(values.dTransparencyType.ref.value)) return;
+
+    stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
+
+    if (!webgl.namedTextures[ColorSmoothingAlphaName]) {
+        webgl.namedTextures[ColorSmoothingAlphaName] = webgl.resources.texture('image-uint8', 'alpha', 'ubyte', 'nearest');
+    }
+    const colorData = webgl.namedTextures[ColorSmoothingAlphaName];
+    colorData.load(values.tTransparency.ref.value);
+
+    const smoothingData = calcTextureMeshColorSmoothing({
+        vertexCount: values.uVertexCount.ref.value,
+        instanceCount: values.uInstanceCount.ref.value,
+        groupCount: values.uGroupCount.ref.value,
+        transformBuffer: values.aTransform.ref.value,
+        instanceBuffer: values.aInstance.ref.value,
+        positionTexture: values.tPosition.ref.value,
+        groupTexture: values.tGroup.ref.value,
+        colorData,
+        colorType: values.dTransparencyType.ref.value,
+        boundingSphere: values.boundingSphere.ref.value,
+        invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
+    }, resolution, stride, webgl, colorTexture);
+
+    ValueCell.updateIfChanged(values.dTransparencyType, smoothingData.type);
+    ValueCell.update(values.tTransparencyGrid, smoothingData.texture);
+    ValueCell.update(values.uTransparencyTexDim, smoothingData.gridTexDim);
+    ValueCell.update(values.uTransparencyGridDim, smoothingData.gridDim);
+    ValueCell.update(values.uTransparencyGridTransform, smoothingData.gridTransform);
+}

+ 18 - 2
src/mol-geo/geometry/transparency-data.ts

@@ -1,18 +1,24 @@
 /**
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
  */
 
 
 import { ValueCell } from '../../mol-util/value-cell';
 import { ValueCell } from '../../mol-util/value-cell';
-import { Vec2 } from '../../mol-math/linear-algebra';
+import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
 import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
 import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
+import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
 
 
 export type TransparencyData = {
 export type TransparencyData = {
     tTransparency: ValueCell<TextureImage<Uint8Array>>
     tTransparency: ValueCell<TextureImage<Uint8Array>>
     uTransparencyTexDim: ValueCell<Vec2>
     uTransparencyTexDim: ValueCell<Vec2>
     dTransparency: ValueCell<boolean>,
     dTransparency: ValueCell<boolean>,
     transparencyAverage: ValueCell<number>,
     transparencyAverage: ValueCell<number>,
+
+    tTransparencyGrid: ValueCell<Texture>,
+    uTransparencyGridDim: ValueCell<Vec3>,
+    uTransparencyGridTransform: ValueCell<Vec4>,
+    dTransparencyType: ValueCell<string>,
 }
 }
 
 
 export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
 export function applyTransparencyValue(array: Uint8Array, start: number, end: number, value: number) {
@@ -48,6 +54,11 @@ export function createTransparency(count: number, transparencyData?: Transparenc
             uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
             uTransparencyTexDim: ValueCell.create(Vec2.create(transparency.width, transparency.height)),
             dTransparency: ValueCell.create(count > 0),
             dTransparency: ValueCell.create(count > 0),
             transparencyAverage: ValueCell.create(0),
             transparencyAverage: ValueCell.create(0),
+
+            tTransparencyGrid: ValueCell.create(createNullTexture()),
+            uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
+            uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
+            dTransparencyType: ValueCell.create('groupInstance'),
         };
         };
     }
     }
 }
 }
@@ -64,6 +75,11 @@ export function createEmptyTransparency(transparencyData?: TransparencyData): Tr
             uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
             uTransparencyTexDim: ValueCell.create(Vec2.create(1, 1)),
             dTransparency: ValueCell.create(false),
             dTransparency: ValueCell.create(false),
             transparencyAverage: ValueCell.create(0),
             transparencyAverage: ValueCell.create(0),
+
+            tTransparencyGrid: ValueCell.create(createNullTexture()),
+            uTransparencyGridDim: ValueCell.create(Vec3.create(1, 1, 1)),
+            uTransparencyGridTransform: ValueCell.create(Vec4.create(0, 0, 0, 1)),
+            dTransparencyType: ValueCell.create('groupInstance'),
         };
         };
     }
     }
 }
 }

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

@@ -224,6 +224,11 @@ export const OverpaintSchema = {
     uOverpaintTexDim: UniformSpec('v2'),
     uOverpaintTexDim: UniformSpec('v2'),
     tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
     tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
     dOverpaint: DefineSpec('boolean'),
     dOverpaint: DefineSpec('boolean'),
+
+    uOverpaintGridDim: UniformSpec('v3'),
+    uOverpaintGridTransform: UniformSpec('v4'),
+    tOverpaintGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
+    dOverpaintType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
 } as const;
 } as const;
 export type OverpaintSchema = typeof OverpaintSchema
 export type OverpaintSchema = typeof OverpaintSchema
 export type OverpaintValues = Values<OverpaintSchema>
 export type OverpaintValues = Values<OverpaintSchema>
@@ -233,6 +238,11 @@ export const TransparencySchema = {
     tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     tTransparency: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
     dTransparency: DefineSpec('boolean'),
     dTransparency: DefineSpec('boolean'),
     transparencyAverage: ValueSpec('number'),
     transparencyAverage: ValueSpec('number'),
+
+    uTransparencyGridDim: UniformSpec('v3'),
+    uTransparencyGridTransform: UniformSpec('v4'),
+    tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
+    dTransparencyType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
 } as const;
 } as const;
 export type TransparencySchema = typeof TransparencySchema
 export type TransparencySchema = typeof TransparencySchema
 export type TransparencyValues = Values<TransparencySchema>
 export type TransparencyValues = Values<TransparencySchema>

+ 28 - 6
src/mol-gl/shader/chunks/assign-color-varying.glsl.ts

@@ -13,11 +13,11 @@ export const assign_color_varying = `
     #elif defined(dColorType_vertexInstance)
     #elif defined(dColorType_vertexInstance)
         vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb;
         vColor.rgb = readFromTexture(tColor, int(aInstance) * uVertexCount + VertexID, uColorTexDim).rgb;
     #elif defined(dColorType_volume)
     #elif defined(dColorType_volume)
-        vec3 gridPos = (uColorGridTransform.w * (position - uColorGridTransform.xyz)) / uColorGridDim;
-        vColor.rgb = texture3dFrom2dLinear(tColorGrid, gridPos, uColorGridDim, uColorTexDim).rgb;
+        vec3 cgridPos = (uColorGridTransform.w * (position - uColorGridTransform.xyz)) / uColorGridDim;
+        vColor.rgb = texture3dFrom2dLinear(tColorGrid, cgridPos, uColorGridDim, uColorTexDim).rgb;
     #elif defined(dColorType_volumeInstance)
     #elif defined(dColorType_volumeInstance)
-        vec3 gridPos = (uColorGridTransform.w * (vModelPosition - uColorGridTransform.xyz)) / uColorGridDim;
-        vColor.rgb = texture3dFrom2dLinear(tColorGrid, gridPos, uColorGridDim, uColorTexDim).rgb;
+        vec3 cgridPos = (uColorGridTransform.w * (vModelPosition - uColorGridTransform.xyz)) / uColorGridDim;
+        vColor.rgb = texture3dFrom2dLinear(tColorGrid, cgridPos, uColorGridDim, uColorTexDim).rgb;
     #endif
     #endif
 
 
     #ifdef dUsePalette
     #ifdef dUsePalette
@@ -25,7 +25,21 @@ export const assign_color_varying = `
     #endif
     #endif
 
 
     #ifdef dOverpaint
     #ifdef dOverpaint
-        vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
+        #if defined(dOverpaintType_groupInstance)
+            vOverpaint = readFromTexture(tOverpaint, aInstance * float(uGroupCount) + group, uOverpaintTexDim);
+        #elif defined(dOverpaintType_vertexInstance)
+            vOverpaint = readFromTexture(tOverpaint, int(aInstance) * uVertexCount + VertexID, uOverpaintTexDim);
+        #elif defined(dOverpaintType_volumeInstance)
+            vec3 ogridPos = (uOverpaintGridTransform.w * (vModelPosition - uOverpaintGridTransform.xyz)) / uOverpaintGridDim;
+            vOverpaint = texture3dFrom2dLinear(tOverpaintGrid, ogridPos, uOverpaintGridDim, uOverpaintTexDim);
+        #endif
+
+        // pre-mix to avoid darkening due to empty overpaint
+        #ifdef dColorType_uniform
+            vOverpaint.rgb = mix(uColor.rgb, vOverpaint.rgb, vOverpaint.a);
+        #else
+            vOverpaint.rgb = mix(vColor.rgb, vOverpaint.rgb, vOverpaint.a);
+        #endif
     #endif
     #endif
 #elif defined(dRenderVariant_pick)
 #elif defined(dRenderVariant_pick)
     #if defined(dRenderVariant_pickObject)
     #if defined(dRenderVariant_pickObject)
@@ -39,6 +53,14 @@ export const assign_color_varying = `
 
 
 #ifdef dTransparency
 #ifdef dTransparency
     vGroup = group;
     vGroup = group;
-    vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
+
+    #if defined(dTransparencyType_groupInstance)
+        vTransparency = readFromTexture(tTransparency, aInstance * float(uGroupCount) + group, uTransparencyTexDim).a;
+    #elif defined(dTransparencyType_vertexInstance)
+        vTransparency = readFromTexture(tTransparency, int(aInstance) * uVertexCount + VertexID, uTransparencyTexDim).a;
+    #elif defined(dTransparencyType_volumeInstance)
+        vec3 tgridPos = (uTransparencyGridTransform.w * (vModelPosition - uTransparencyGridTransform.xyz)) / uTransparencyGridDim;
+        vTransparency = texture3dFrom2dLinear(tTransparencyGrid, tgridPos, uTransparencyGridDim, uTransparencyTexDim).a;
+    #endif
 #endif
 #endif
 `;
 `;

+ 3 - 0
src/mol-gl/shader/chunks/assign-material-color.glsl.ts

@@ -54,6 +54,9 @@ export const assign_material_color = `
 // apply screendoor transparency
 // apply screendoor transparency
 #if defined(dTransparency)
 #if defined(dTransparency)
     float ta = 1.0 - vTransparency;
     float ta = 1.0 - vTransparency;
+    #if defined(dRenderVariant_colorWboit)
+        if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better with wboit
+    #endif
 
 
     #if defined(dRenderVariant_pick)
     #if defined(dRenderVariant_pick)
         if (ta < uPickingAlphaThreshold)
         if (ta < uPickingAlphaThreshold)

+ 22 - 6
src/mol-gl/shader/chunks/color-vert-params.glsl.ts

@@ -18,9 +18,17 @@ export const color_vert_params = `
     #endif
     #endif
 
 
     #ifdef dOverpaint
     #ifdef dOverpaint
-        varying vec4 vOverpaint;
-        uniform vec2 uOverpaintTexDim;
-        uniform sampler2D tOverpaint;
+        #if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
+            varying vec4 vOverpaint;
+            uniform vec2 uOverpaintTexDim;
+            uniform sampler2D tOverpaint;
+        #elif defined(dOverpaintType_volumeInstance)
+            varying vec4 vOverpaint;
+            uniform vec2 uOverpaintTexDim;
+            uniform vec3 uOverpaintGridDim;
+            uniform vec4 uOverpaintGridTransform;
+            uniform sampler2D tOverpaintGrid;
+        #endif
     #endif
     #endif
 #elif defined(dRenderVariant_pick)
 #elif defined(dRenderVariant_pick)
     #if __VERSION__ == 100
     #if __VERSION__ == 100
@@ -32,9 +40,17 @@ export const color_vert_params = `
 
 
 #ifdef dTransparency
 #ifdef dTransparency
     varying float vGroup;
     varying float vGroup;
-    varying float vTransparency;
-    uniform vec2 uTransparencyTexDim;
-    uniform sampler2D tTransparency;
+    #if defined(dTransparencyType_groupInstance) || defined(dTransparencyType_vertexInstance)
+        varying float vTransparency;
+        uniform vec2 uTransparencyTexDim;
+        uniform sampler2D tTransparency;
+    #elif defined(dTransparencyType_volumeInstance)
+        varying float vTransparency;
+        uniform vec2 uTransparencyTexDim;
+        uniform vec3 uTransparencyGridDim;
+        uniform vec4 uTransparencyGridTransform;
+        uniform sampler2D tTransparencyGrid;
+    #endif
 #endif
 #endif
 
 
 #ifdef dUsePalette
 #ifdef dUsePalette

+ 1 - 4
src/mol-gl/shader/mesh.vert.ts

@@ -14,10 +14,7 @@ precision highp sampler2D;
 #include common_vert_params
 #include common_vert_params
 #include color_vert_params
 #include color_vert_params
 #include common_clip
 #include common_clip
-
-#if defined(dColorType_grid)
-    #include texture3d_from_2d_linear
-#endif
+#include texture3d_from_2d_linear
 
 
 #ifdef dGeoTexture
 #ifdef dGeoTexture
     uniform vec2 uGeoTexDim;
     uniform vec2 uGeoTexDim;

+ 41 - 15
src/mol-plugin-state/transforms/representation.ts

@@ -24,7 +24,7 @@ import { unwindStructureAssembly, explodeStructure, spinStructure, SpinStructure
 import { Color } from '../../mol-util/color';
 import { Color } from '../../mol-util/color';
 import { Overpaint } from '../../mol-theme/overpaint';
 import { Overpaint } from '../../mol-theme/overpaint';
 import { Transparency } from '../../mol-theme/transparency';
 import { Transparency } from '../../mol-theme/transparency';
-import { BaseGeometry } from '../../mol-geo/geometry/base';
+import { BaseGeometry, hasColorSmoothingProp } from '../../mol-geo/geometry/base';
 import { Script } from '../../mol-script/script';
 import { Script } from '../../mol-script/script';
 import { UnitcellParams, UnitcellRepresentation, getUnitcellData } from '../../mol-repr/shape/model/unitcell';
 import { UnitcellParams, UnitcellRepresentation, getUnitcellData } from '../../mol-repr/shape/model/unitcell';
 import { DistanceParams, DistanceRepresentation } from '../../mol-repr/shape/loci/distance';
 import { DistanceParams, DistanceRepresentation } from '../../mol-repr/shape/loci/distance';
@@ -328,25 +328,31 @@ const OverpaintStructureRepresentation3DFromScript = PluginStateTransform.BuiltI
     },
     },
     apply({ a, params }) {
     apply({ a, params }) {
         const structure = a.data.sourceData;
         const structure = a.data.sourceData;
+        const geometryVersion = a.data.repr.geometryVersion;
         const overpaint = Overpaint.ofScript(params.layers, structure);
         const overpaint = Overpaint.ofScript(params.layers, structure);
 
 
         return new SO.Molecule.Structure.Representation3DState({
         return new SO.Molecule.Structure.Representation3DState({
             state: { overpaint },
             state: { overpaint },
             initialState: { overpaint: Overpaint.Empty },
             initialState: { overpaint: Overpaint.Empty },
-            info: structure,
+            info: { structure, geometryVersion },
             repr: a.data.repr
             repr: a.data.repr
         }, { label: `Overpaint (${overpaint.layers.length} Layers)` });
         }, { label: `Overpaint (${overpaint.layers.length} Layers)` });
     },
     },
     update({ a, b, newParams, oldParams }) {
     update({ a, b, newParams, oldParams }) {
-        const oldStructure = b.data.info as Structure;
+        const info = b.data.info as { structure: Structure, geometryVersion: number };
         const newStructure = a.data.sourceData;
         const newStructure = a.data.sourceData;
-        if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
+        if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
 
 
+        const newGeometryVersion = a.data.repr.geometryVersion;
+        // smoothing needs to be re-calculated when geometry changes
+        if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
+
         const oldOverpaint = b.data.state.overpaint!;
         const oldOverpaint = b.data.state.overpaint!;
         const newOverpaint = Overpaint.ofScript(newParams.layers, newStructure);
         const newOverpaint = Overpaint.ofScript(newParams.layers, newStructure);
         if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
         if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
 
 
+        info.geometryVersion = newGeometryVersion;
         b.data.state.overpaint = newOverpaint;
         b.data.state.overpaint = newOverpaint;
         b.data.repr = a.data.repr;
         b.data.repr = a.data.repr;
         b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
         b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
@@ -380,25 +386,31 @@ const OverpaintStructureRepresentation3DFromBundle = PluginStateTransform.BuiltI
     },
     },
     apply({ a, params }) {
     apply({ a, params }) {
         const structure = a.data.sourceData;
         const structure = a.data.sourceData;
+        const geometryVersion = a.data.repr.geometryVersion;
         const overpaint = Overpaint.ofBundle(params.layers, structure);
         const overpaint = Overpaint.ofBundle(params.layers, structure);
 
 
         return new SO.Molecule.Structure.Representation3DState({
         return new SO.Molecule.Structure.Representation3DState({
             state: { overpaint },
             state: { overpaint },
             initialState: { overpaint: Overpaint.Empty },
             initialState: { overpaint: Overpaint.Empty },
-            info: structure,
+            info: { structure, geometryVersion },
             repr: a.data.repr
             repr: a.data.repr
         }, { label: `Overpaint (${overpaint.layers.length} Layers)` });
         }, { label: `Overpaint (${overpaint.layers.length} Layers)` });
     },
     },
     update({ a, b, newParams, oldParams }) {
     update({ a, b, newParams, oldParams }) {
-        const oldStructure = b.data.info as Structure;
+        const info = b.data.info as { structure: Structure, geometryVersion: number };
         const newStructure = a.data.sourceData;
         const newStructure = a.data.sourceData;
-        if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate;
+        if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
 
 
+        const newGeometryVersion = a.data.repr.geometryVersion;
+        // smoothing needs to be re-calculated when geometry changes
+        if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
+
         const oldOverpaint = b.data.state.overpaint!;
         const oldOverpaint = b.data.state.overpaint!;
         const newOverpaint = Overpaint.ofBundle(newParams.layers, newStructure);
         const newOverpaint = Overpaint.ofBundle(newParams.layers, newStructure);
         if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
         if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
 
 
+        info.geometryVersion = newGeometryVersion;
         b.data.state.overpaint = newOverpaint;
         b.data.state.overpaint = newOverpaint;
         b.data.repr = a.data.repr;
         b.data.repr = a.data.repr;
         b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
         b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
@@ -429,24 +441,31 @@ const TransparencyStructureRepresentation3DFromScript = PluginStateTransform.Bui
     },
     },
     apply({ a, params }) {
     apply({ a, params }) {
         const structure = a.data.sourceData;
         const structure = a.data.sourceData;
+        const geometryVersion = a.data.repr.geometryVersion;
         const transparency = Transparency.ofScript(params.layers, structure);
         const transparency = Transparency.ofScript(params.layers, structure);
 
 
         return new SO.Molecule.Structure.Representation3DState({
         return new SO.Molecule.Structure.Representation3DState({
             state: { transparency },
             state: { transparency },
             initialState: { transparency: Transparency.Empty },
             initialState: { transparency: Transparency.Empty },
-            info: structure,
+            info: { structure, geometryVersion },
             repr: a.data.repr
             repr: a.data.repr
         }, { label: `Transparency (${transparency.layers.length} Layers)` });
         }, { label: `Transparency (${transparency.layers.length} Layers)` });
     },
     },
     update({ a, b, newParams, oldParams }) {
     update({ a, b, newParams, oldParams }) {
-        const structure = b.data.info as Structure;
-        if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
+        const info = b.data.info as { structure: Structure, geometryVersion: number };
+        const newStructure = a.data.sourceData;
+        if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
 
 
+        const newGeometryVersion = a.data.repr.geometryVersion;
+        // smoothing needs to be re-calculated when geometry changes
+        if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
+
         const oldTransparency = b.data.state.transparency!;
         const oldTransparency = b.data.state.transparency!;
-        const newTransparency = Transparency.ofScript(newParams.layers, structure);
+        const newTransparency = Transparency.ofScript(newParams.layers, newStructure);
         if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
         if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
 
 
+        info.geometryVersion = newGeometryVersion;
         b.data.state.transparency = newTransparency;
         b.data.state.transparency = newTransparency;
         b.data.repr = a.data.repr;
         b.data.repr = a.data.repr;
         b.label = `Transparency (${newTransparency.layers.length} Layers)`;
         b.label = `Transparency (${newTransparency.layers.length} Layers)`;
@@ -478,24 +497,31 @@ const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.Bui
     },
     },
     apply({ a, params }) {
     apply({ a, params }) {
         const structure = a.data.sourceData;
         const structure = a.data.sourceData;
+        const geometryVersion = a.data.repr.geometryVersion;
         const transparency = Transparency.ofBundle(params.layers, structure);
         const transparency = Transparency.ofBundle(params.layers, structure);
 
 
         return new SO.Molecule.Structure.Representation3DState({
         return new SO.Molecule.Structure.Representation3DState({
             state: { transparency },
             state: { transparency },
             initialState: { transparency: Transparency.Empty },
             initialState: { transparency: Transparency.Empty },
-            info: structure,
+            info: { structure, geometryVersion },
             repr: a.data.repr
             repr: a.data.repr
         }, { label: `Transparency (${transparency.layers.length} Layers)` });
         }, { label: `Transparency (${transparency.layers.length} Layers)` });
     },
     },
     update({ a, b, newParams, oldParams }) {
     update({ a, b, newParams, oldParams }) {
-        const structure = b.data.info as Structure;
-        if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
+        const info = b.data.info as { structure: Structure, geometryVersion: number };
+        const newStructure = a.data.sourceData;
+        if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
         if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
 
 
+        const newGeometryVersion = a.data.repr.geometryVersion;
+        // smoothing needs to be re-calculated when geometry changes
+        if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
+
         const oldTransparency = b.data.state.transparency!;
         const oldTransparency = b.data.state.transparency!;
-        const newTransparency = Transparency.ofBundle(newParams.layers, structure);
+        const newTransparency = Transparency.ofBundle(newParams.layers, newStructure);
         if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
         if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
 
 
+        info.geometryVersion = newGeometryVersion;
         b.data.state.transparency = newTransparency;
         b.data.state.transparency = newTransparency;
         b.data.repr = a.data.repr;
         b.data.repr = a.data.repr;
         b.label = `Transparency (${newTransparency.layers.length} Layers)`;
         b.label = `Transparency (${newTransparency.layers.length} Layers)`;

+ 2 - 2
src/mol-repr/structure/complex-representation.ts

@@ -106,12 +106,12 @@ export function ComplexRepresentation<P extends StructureParams>(label: string,
         if (state.overpaint !== undefined && visual) {
         if (state.overpaint !== undefined && visual) {
             // Remap loci from equivalent structure to the current structure
             // Remap loci from equivalent structure to the current structure
             const remappedOverpaint = Overpaint.remap(state.overpaint, _structure);
             const remappedOverpaint = Overpaint.remap(state.overpaint, _structure);
-            visual.setOverpaint(remappedOverpaint);
+            visual.setOverpaint(remappedOverpaint, webgl);
         }
         }
         if (state.transparency !== undefined && visual) {
         if (state.transparency !== undefined && visual) {
             // Remap loci from equivalent structure to the current structure
             // Remap loci from equivalent structure to the current structure
             const remappedTransparency = Transparency.remap(state.transparency, _structure);
             const remappedTransparency = Transparency.remap(state.transparency, _structure);
-            visual.setTransparency(remappedTransparency);
+            visual.setTransparency(remappedTransparency, webgl);
         }
         }
         if (state.clipping !== undefined && visual) {
         if (state.clipping !== undefined && visual) {
             // Remap loci from equivalent structure to the current structure
             // Remap loci from equivalent structure to the current structure

+ 6 - 4
src/mol-repr/structure/complex-visual.ts

@@ -258,11 +258,13 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
         setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
         setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
             Visual.setTransform(renderObject, matrix, instanceMatrices);
             Visual.setTransform(renderObject, matrix, instanceMatrices);
         },
         },
-        setOverpaint(overpaint: Overpaint) {
-            Visual.setOverpaint(renderObject, overpaint, lociApply, true);
+        setOverpaint(overpaint: Overpaint, webgl?: WebGLContext) {
+            const smoothing = { geometry, props: currentProps, webgl };
+            Visual.setOverpaint(renderObject, overpaint, lociApply, true, smoothing);
         },
         },
-        setTransparency(transparency: Transparency) {
-            Visual.setTransparency(renderObject, transparency, lociApply, true);
+        setTransparency(transparency: Transparency, webgl?: WebGLContext) {
+            const smoothing = { geometry, props: currentProps, webgl };
+            Visual.setTransparency(renderObject, transparency, lociApply, true, smoothing);
         },
         },
         setClipping(clipping: Clipping) {
         setClipping(clipping: Clipping) {
             Visual.setClipping(renderObject, clipping, lociApply, true);
             Visual.setClipping(renderObject, clipping, lociApply, true);

+ 2 - 2
src/mol-repr/structure/units-representation.ts

@@ -223,8 +223,8 @@ export function UnitsRepresentation<P extends StructureParams>(label: string, ct
         if (visible !== undefined) visual.setVisibility(visible);
         if (visible !== undefined) visual.setVisibility(visible);
         if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor);
         if (alphaFactor !== undefined) visual.setAlphaFactor(alphaFactor);
         if (pickable !== undefined) visual.setPickable(pickable);
         if (pickable !== undefined) visual.setPickable(pickable);
-        if (overpaint !== undefined) visual.setOverpaint(overpaint);
-        if (transparency !== undefined) visual.setTransparency(transparency);
+        if (overpaint !== undefined) visual.setOverpaint(overpaint, webgl);
+        if (transparency !== undefined) visual.setTransparency(transparency, webgl);
         if (clipping !== undefined) visual.setClipping(clipping);
         if (clipping !== undefined) visual.setClipping(clipping);
         if (transform !== undefined) visual.setTransform(transform);
         if (transform !== undefined) visual.setTransform(transform);
         if (unitTransforms !== undefined) {
         if (unitTransforms !== undefined) {

+ 6 - 4
src/mol-repr/structure/units-visual.ts

@@ -323,11 +323,13 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
         setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
         setTransform(matrix?: Mat4, instanceMatrices?: Float32Array | null) {
             Visual.setTransform(renderObject, matrix, instanceMatrices);
             Visual.setTransform(renderObject, matrix, instanceMatrices);
         },
         },
-        setOverpaint(overpaint: Overpaint) {
-            Visual.setOverpaint(renderObject, overpaint, lociApply, true);
+        setOverpaint(overpaint: Overpaint, webgl?: WebGLContext) {
+            const smoothing = { geometry, props: currentProps, webgl };
+            Visual.setOverpaint(renderObject, overpaint, lociApply, true, smoothing);
         },
         },
-        setTransparency(transparency: Transparency) {
-            Visual.setTransparency(renderObject, transparency, lociApply, true);
+        setTransparency(transparency: Transparency, webgl?: WebGLContext) {
+            const smoothing = { geometry, props: currentProps, webgl };
+            Visual.setTransparency(renderObject, transparency, lociApply, true, smoothing);
         },
         },
         setClipping(clipping: Clipping) {
         setClipping(clipping: Clipping) {
             Visual.setClipping(renderObject, clipping, lociApply, true);
             Visual.setClipping(renderObject, clipping, lociApply, true);

+ 67 - 6
src/mol-repr/visual.ts

@@ -24,6 +24,11 @@ import { createTransparency, clearTransparency, applyTransparencyValue, getTrans
 import { Clipping } from '../mol-theme/clipping';
 import { Clipping } from '../mol-theme/clipping';
 import { createClipping, applyClippingGroups, clearClipping } from '../mol-geo/geometry/clipping-data';
 import { createClipping, applyClippingGroups, clearClipping } from '../mol-geo/geometry/clipping-data';
 import { getMarkersAverage } from '../mol-geo/geometry/marker-data';
 import { getMarkersAverage } from '../mol-geo/geometry/marker-data';
+import { Texture } from '../mol-gl/webgl/texture';
+import { Geometry } from '../mol-geo/geometry/geometry';
+import { getColorSmoothingProps, hasColorSmoothingProp } from '../mol-geo/geometry/base';
+import { applyMeshOverpaintSmoothing, applyMeshTransparencySmoothing } from '../mol-geo/geometry/mesh/color-smoothing';
+import { applyTextureMeshOverpaintSmoothing, applyTextureMeshTransparencySmoothing } from '../mol-geo/geometry/texture-mesh/color-smoothing';
 
 
 export interface VisualContext {
 export interface VisualContext {
     readonly runtime: RuntimeContext
     readonly runtime: RuntimeContext
@@ -44,8 +49,8 @@ interface Visual<D, P extends PD.Params> {
     setPickable: (pickable: boolean) => void
     setPickable: (pickable: boolean) => void
     setColorOnly: (colorOnly: boolean) => void
     setColorOnly: (colorOnly: boolean) => void
     setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void
     setTransform: (matrix?: Mat4, instanceMatrices?: Float32Array | null) => void
-    setOverpaint: (overpaint: Overpaint) => void
-    setTransparency: (transparency: Transparency) => void
+    setOverpaint: (overpaint: Overpaint, webgl?: WebGLContext) => void
+    setTransparency: (transparency: Transparency, webgl?: WebGLContext) => void
     setClipping: (clipping: Clipping) => void
     setClipping: (clipping: Clipping) => void
     destroy: () => void
     destroy: () => void
     mustRecreate?: (data: D, props: PD.Values<P>, webgl?: WebGLContext) => boolean
     mustRecreate?: (data: D, props: PD.Values<P>, webgl?: WebGLContext) => boolean
@@ -134,10 +139,22 @@ namespace Visual {
         return changed;
         return changed;
     }
     }
 
 
-    export function setOverpaint(renderObject: GraphicsRenderObject | undefined, overpaint: Overpaint, lociApply: LociApply, clear: boolean) {
+    type SurfaceMeta = {
+        resolution?: number
+        overpaintTexture?: Texture
+        transparencyTexture?: Texture
+    }
+
+    type SmoothingContext = {
+        geometry: Geometry,
+        props: PD.Values<any>,
+        webgl?: WebGLContext
+    }
+
+    export function setOverpaint(renderObject: GraphicsRenderObject | undefined, overpaint: Overpaint, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
         if (!renderObject) return;
         if (!renderObject) return;
 
 
-        const { tOverpaint, uGroupCount, instanceCount } = renderObject.values;
+        const { tOverpaint, dOverpaintType, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
 
         // ensure texture has right size
         // ensure texture has right size
@@ -159,12 +176,34 @@ namespace Visual {
             lociApply(loci, apply, false);
             lociApply(loci, apply, false);
         }
         }
         ValueCell.update(tOverpaint, tOverpaint.ref.value);
         ValueCell.update(tOverpaint, tOverpaint.ref.value);
+        ValueCell.updateIfChanged(dOverpaintType, 'groupInstance');
+
+        if (overpaint.layers.length === 0) return;
+
+        if (smoothing && hasColorSmoothingProp(smoothing.props)) {
+            const { geometry, props, webgl } = smoothing;
+            if (geometry.kind === 'mesh') {
+                const { resolution, overpaintTexture } = geometry.meta as SurfaceMeta;
+                const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
+                if (csp) {
+                    applyMeshOverpaintSmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, overpaintTexture);
+                    (geometry.meta as SurfaceMeta).overpaintTexture = renderObject.values.tOverpaintGrid.ref.value;
+                }
+            } else if (webgl && geometry.kind === 'texture-mesh') {
+                const { resolution, overpaintTexture } = geometry.meta as SurfaceMeta;
+                const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
+                if (csp) {
+                    applyTextureMeshOverpaintSmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, overpaintTexture);
+                    (geometry.meta as SurfaceMeta).overpaintTexture = renderObject.values.tOverpaintGrid.ref.value;
+                }
+            }
+        }
     }
     }
 
 
-    export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean) {
+    export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
         if (!renderObject) return;
         if (!renderObject) return;
 
 
-        const { tTransparency, transparencyAverage, uGroupCount, instanceCount } = renderObject.values;
+        const { tTransparency, dTransparencyType, transparencyAverage, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
 
         // ensure texture has right size and variant
         // ensure texture has right size and variant
@@ -185,6 +224,28 @@ namespace Visual {
         }
         }
         ValueCell.update(tTransparency, tTransparency.ref.value);
         ValueCell.update(tTransparency, tTransparency.ref.value);
         ValueCell.updateIfChanged(transparencyAverage, getTransparencyAverage(array, count));
         ValueCell.updateIfChanged(transparencyAverage, getTransparencyAverage(array, count));
+        ValueCell.updateIfChanged(dTransparencyType, 'groupInstance');
+
+        if (transparency.layers.length === 0) return;
+
+        if (smoothing && hasColorSmoothingProp(smoothing.props)) {
+            const { geometry, props, webgl } = smoothing;
+            if (geometry.kind === 'mesh') {
+                const { resolution, transparencyTexture } = geometry.meta as SurfaceMeta;
+                const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
+                if (csp) {
+                    applyMeshTransparencySmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, transparencyTexture);
+                    (geometry.meta as SurfaceMeta).transparencyTexture = renderObject.values.tTransparencyGrid.ref.value;
+                }
+            } else if (webgl && geometry.kind === 'texture-mesh') {
+                const { resolution, transparencyTexture } = geometry.meta as SurfaceMeta;
+                const csp = getColorSmoothingProps(props.smoothColors, true, resolution);
+                if (csp) {
+                    applyTextureMeshTransparencySmoothing(renderObject.values as any, csp.resolution, csp.stride, webgl, transparencyTexture);
+                    (geometry.meta as SurfaceMeta).transparencyTexture = renderObject.values.tTransparencyGrid.ref.value;
+                }
+            }
+        }
     }
     }
 
 
     export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {
     export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {