diff --git a/src/mol-canvas3d/passes/draw.ts b/src/mol-canvas3d/passes/draw.ts index 85ce63a30cd0b4c880d3e4a4479697b819cdad5a..bd04e66ce9835c6450734f62e360415d5fbb4e8b 100644 --- a/src/mol-canvas3d/passes/draw.ts +++ b/src/mol-canvas3d/passes/draw.ts @@ -154,6 +154,7 @@ export class DrawPass { // this.webgl.state.disable(this.webgl.gl.SCISSOR_TEST); this.webgl.state.disable(this.webgl.gl.BLEND); this.webgl.state.disable(this.webgl.gl.DEPTH_TEST); + this.webgl.state.disable(this.webgl.gl.CULL_FACE); this.webgl.state.depthMask(false); this.webgl.state.clearColor(1, 1, 1, 1); this.webgl.gl.viewport(x, y, width, height); diff --git a/src/mol-gl/renderer.ts b/src/mol-gl/renderer.ts index 357b59fdf98c6384abc9234550daa1fcff8089c0..5821c1479ae1ad055976fe66655272513e2671c6 100644 --- a/src/mol-gl/renderer.ts +++ b/src/mol-gl/renderer.ts @@ -158,7 +158,7 @@ function getClip(props: RendererProps['clip'], clip?: Clip): Clip { namespace Renderer { export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer { - const { gl, state, stats } = ctx; + const { gl, state, stats, extensions: { fragDepth } } = ctx; const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props); const style = getStyle(p.style); const clip = getClip(p.clip); @@ -267,34 +267,49 @@ namespace Renderer { if (depthTexture) program.bindTextures([['tDepth', depthTexture]]); - if (r.values.dDoubleSided) { - if ((r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) && - !r.values.uStepFactor // indicates direct-volume, always cull - ) { - state.disable(gl.CULL_FACE); + if (r.values.uStepFactor) { // indicates direct-volume + // always cull front + state.enable(gl.CULL_FACE); + state.frontFace(gl.CW); + state.cullFace(gl.BACK); + + // depth test done manually in shader against `depthTexture` + // still need to enable when fragDepth can be used to write depth + // (unclear why depthMask is insufficient) + if (r.values.dRenderMode.ref.value === 'volume' || !fragDepth) { + state.disable(gl.DEPTH_TEST); + state.depthMask(false); } else { - state.enable(gl.CULL_FACE); + state.enable(gl.DEPTH_TEST); + state.depthMask(r.state.writeDepth); } } else { - // webgl default - state.disable(gl.CULL_FACE); - } + state.enable(gl.DEPTH_TEST); + if (r.values.dDoubleSided) { + if (r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) { + state.disable(gl.CULL_FACE); + } else { + state.enable(gl.CULL_FACE); + } + } else { + // webgl default + state.disable(gl.CULL_FACE); + } - if (r.values.dFlipSided) { - if (r.values.dFlipSided.ref.value) { - state.frontFace(gl.CW); - state.cullFace(gl.FRONT); + if (r.values.dFlipSided) { + if (r.values.dFlipSided.ref.value) { + state.frontFace(gl.CW); + state.cullFace(gl.FRONT); + } else { + state.frontFace(gl.CCW); + state.cullFace(gl.BACK); + } } else { + // webgl default state.frontFace(gl.CCW); state.cullFace(gl.BACK); } - } else { - // webgl default - state.frontFace(gl.CCW); - state.cullFace(gl.BACK); - } - if (variant === 'color') { state.depthMask(r.state.writeDepth); } diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts index 211cd41ea709cd369a830b659ff08bcc4f48037f..3b85e1800ca82654970e38f8574769d84f19a0bb 100644 --- a/src/mol-gl/shader-code.ts +++ b/src/mol-gl/shader-code.ts @@ -15,11 +15,12 @@ export type DefineValues = { [k: string]: ValueCell<DefineType> } const shaderCodeId = idFactory(); +type ShaderExtensionsValue = 'required' | 'optional' export interface ShaderExtensions { - readonly standardDerivatives?: boolean - readonly fragDepth?: boolean - readonly drawBuffers?: boolean - readonly shaderTextureLod?: boolean + readonly standardDerivatives?: ShaderExtensionsValue + readonly fragDepth?: ShaderExtensionsValue + readonly drawBuffers?: ShaderExtensionsValue + readonly shaderTextureLod?: ShaderExtensionsValue } export interface ShaderCode { @@ -118,11 +119,11 @@ export const PointsShaderCode = ShaderCode('points', points_vert, points_frag); import spheres_vert from './shader/spheres.vert'; import spheres_frag from './shader/spheres.frag'; -export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: true }); +export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required' }); import text_vert from './shader/text.vert'; import text_frag from './shader/text.frag'; -export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: true }); +export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: 'required' }); import lines_vert from './shader/lines.vert'; import lines_frag from './shader/lines.frag'; @@ -130,11 +131,11 @@ export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag); import mesh_vert from './shader/mesh.vert'; import mesh_frag from './shader/mesh.frag'; -export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: true }); +export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: 'optional' }); import direct_volume_vert from './shader/direct-volume.vert'; import direct_volume_frag from './shader/direct-volume.frag'; -export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: true }); +export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: 'optional' }); import image_vert from './shader/image.vert'; import image_frag from './shader/image.frag'; @@ -177,24 +178,24 @@ function getGlsl100FragPrefix(extensions: WebGLExtensions, shaderExtensions: Sha if (extensions.fragDepth) { prefix.push('#extension GL_EXT_frag_depth : enable'); prefix.push('#define enabledFragDepth'); - } else { - throw new Error(`requested 'GL_EXT_frag_depth' extension is unavailable`); + } else if (shaderExtensions.fragDepth === 'required') { + throw new Error(`required 'GL_EXT_frag_depth' extension not available`); } } if (shaderExtensions.drawBuffers) { if (extensions.drawBuffers) { prefix.push('#extension GL_EXT_draw_buffers : require'); prefix.push('#define requiredDrawBuffers'); - } else { - throw new Error(`requested 'GL_EXT_draw_buffers' extension is unavailable`); + } else if (shaderExtensions.drawBuffers === 'required') { + throw new Error(`required 'GL_EXT_draw_buffers' extension not available`); } } if (shaderExtensions.shaderTextureLod) { if (extensions.shaderTextureLod) { prefix.push('#extension GL_EXT_shader_texture_lod : enable'); prefix.push('#define enabledShaderTextureLod'); - } else { - throw new Error(`requested 'GL_EXT_shader_texture_lod' extension is unavailable`); + } else if (shaderExtensions.shaderTextureLod === 'required') { + throw new Error(`required 'GL_EXT_shader_texture_lod' extension not available`); } } return prefix.join('\n') + '\n'; diff --git a/src/mol-gl/shader/direct-volume.frag.ts b/src/mol-gl/shader/direct-volume.frag.ts index 4da7349b10045413091b3e79a4eebf140820041b..0f0819f99a8aaf1998e5e03fab4e8d0b562a1f69 100644 --- a/src/mol-gl/shader/direct-volume.frag.ts +++ b/src/mol-gl/shader/direct-volume.frag.ts @@ -12,12 +12,20 @@ precision highp int; #include common #include light_frag_params +#if dClipObjectCount != 0 + uniform int uClipObjectType[dClipObjectCount]; + uniform vec3 uClipObjectPosition[dClipObjectCount]; + uniform vec4 uClipObjectRotation[dClipObjectCount]; + uniform vec3 uClipObjectScale[dClipObjectCount]; +#endif +#include common_clip + #include read_from_texture #include texture3d_from_1d_trilinear #include texture3d_from_2d_nearest #include texture3d_from_2d_linear -uniform mat4 uProjection, uTransform, uModelView, uView; +uniform mat4 uProjection, uTransform, uModelView, uModel, uView; uniform vec3 uCameraDir; uniform sampler2D tDepth; @@ -25,13 +33,14 @@ uniform vec2 uDrawingBufferSize; uniform float uNear; uniform float uFar; -varying vec3 unitCoord; -varying vec3 origPos; -varying float instance; +varying vec3 vOrigPos; +varying float vInstance; +varying vec4 vBoundingSphere; uniform mat4 uInvView; uniform vec2 uIsoValue; uniform vec3 uGridDim; +uniform vec3 uBboxSize; uniform sampler2D tTransferTex; uniform float uStepFactor; @@ -178,6 +187,17 @@ vec4 raymarch(vec3 startLoc, vec3 step) { isoPos = toUnit(mix(pos - step, pos, ((prevValue - uIsoValue.x) / ((prevValue - uIsoValue.x) - (value - uIsoValue.x))))); vec4 mvPosition = uModelView * uTransform * vec4(isoPos * uGridDim, 1.0); + + #if defined(dClipVariant_pixel) && dClipObjectCount != 0 + vec3 vModelPosition = (uModel * uTransform * vec4(isoPos * uGridDim, 1.0)).xyz; + if (clipTest(vec4(vModelPosition, 0.0), 0)) { + prevValue = value; + prevCell = cell; + pos += step; + continue; + } + #endif + float depth = calcDepth(mvPosition.xyz); if (depth > getDepth(gl_FragCoord.xy / uDrawingBufferSize)) break; @@ -192,7 +212,7 @@ vec4 raymarch(vec3 startLoc, vec3 step) { #if defined(dRenderVariant_pickObject) return vec4(encodeFloatRGB(float(uObjectId)), 1.0); #elif defined(dRenderVariant_pickInstance) - return vec4(encodeFloatRGB(instance), 1.0); + return vec4(encodeFloatRGB(vInstance), 1.0); #elif defined(dRenderVariant_pickGroup) #ifdef dPackedGroup return vec4(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb, 1.0); @@ -217,15 +237,15 @@ vec4 raymarch(vec3 startLoc, vec3 step) { #if defined(dColorType_uniform) color = uColor; #elif defined(dColorType_instance) - color = readFromTexture(tColor, instance, uColorTexDim).rgb; + color = readFromTexture(tColor, vInstance, uColorTexDim).rgb; #elif defined(dColorType_group) color = readFromTexture(tColor, group, uColorTexDim).rgb; #elif defined(dColorType_groupInstance) - color = readFromTexture(tColor, instance * float(uGroupCount) + group, uColorTexDim).rgb; + color = readFromTexture(tColor, vInstance * float(uGroupCount) + group, uColorTexDim).rgb; #elif defined(dColorType_vertex) color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, 0.0).rgb; #elif defined(dColorType_vertexInstance) - color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, instance * float(uVertexCount)).rgb; + color = texture3dFrom1dTrilinear(tColor, isoPos, uGridDim, uColorTexDim, vInstance * float(uVertexCount)).rgb; #endif // handle flipping and negative isosurfaces @@ -268,7 +288,7 @@ vec4 raymarch(vec3 startLoc, vec3 step) { #include apply_light_color #endif - float vMarker = readFromTexture(tMarker, instance * float(uGroupCount) + group, uMarkerTexDim).a; + float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a; #include apply_interior_color #include apply_marker_color #include apply_fog @@ -290,6 +310,16 @@ vec4 raymarch(vec3 startLoc, vec3 step) { if (calcDepth(mvPosition.xyz) > getDepth(gl_FragCoord.xy / uDrawingBufferSize)) break; + #if defined(dClipVariant_pixel) && dClipObjectCount != 0 + vec3 vModelPosition = (uModel * uTransform * vec4(isoPos * uGridDim, 1.0)).xyz; + if (clipTest(vec4(vModelPosition, 0.0), 0)) { + prevValue = value; + prevCell = cell; + pos += step; + continue; + } + #endif + vec3 vViewPosition = mvPosition.xyz; vec4 material = transferFunction(value); @@ -318,7 +348,7 @@ vec4 raymarch(vec3 startLoc, vec3 step) { float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y; #endif - float vMarker = readFromTexture(tMarker, instance * float(uGroupCount) + group, uMarkerTexDim).a; + float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a; #include apply_marker_color #include apply_fog @@ -344,15 +374,15 @@ vec4 raymarch(vec3 startLoc, vec3 step) { return dst; } -// TODO calculate normalMatrix on CPU -// TODO fix near/far clipping -// TODO support clip objects -// TODO support float texture for higher precision values??? +// TODO: calculate normalMatrix on CPU +// TODO: support float texture for higher precision values??? +// TODO: support clipping exclusion texture support +// TODO: support instance transforms void main () { - // TODO handle on CPU in renderloop? - #if defined(dRenderVariant_pick) + #if defined(dRenderVariant_pick) || defined(dRenderVariant_depth) #if defined(dRenderMode_volume) + // always ignore pick & depth for volume discard; #elif defined(dRenderMode_isosurface) if (uAlpha < uPickingAlphaThreshold) @@ -360,13 +390,20 @@ void main () { #endif #endif - vec3 rayDir = mix(normalize(origPos - uCameraPosition), uCameraDir, uIsOrtho);; + vec3 rayDir = mix(normalize(vOrigPos - uCameraPosition), uCameraDir, uIsOrtho);; // TODO: set the scale as uniform? float stepScale = min(uCellDim.x, min(uCellDim.y, uCellDim.z)) * uStepFactor; vec3 step = rayDir * stepScale; - float d = uNear - distance(origPos, uCameraPosition); - gl_FragColor = raymarch(origPos + (d * rayDir), step); + float boundingSphereNear = distance(vBoundingSphere.xyz, uCameraPosition) - vBoundingSphere.w; + float d = max(uNear, boundingSphereNear) - distance(vOrigPos, uCameraPosition); + gl_FragColor = raymarch(vOrigPos + (d * rayDir), step); + + #if defined(dRenderVariant_pick) || defined(dRenderVariant_depth) + // discard when nothing was hit + if (gl_FragColor == vec4(0.0)) + discard; + #endif } `; \ No newline at end of file diff --git a/src/mol-gl/shader/direct-volume.vert.ts b/src/mol-gl/shader/direct-volume.vert.ts index 370e2cb053f873a165a39af9202b5ef2c04a45a0..f2e6e791efa01df90014724da8b048312b22a721 100644 --- a/src/mol-gl/shader/direct-volume.vert.ts +++ b/src/mol-gl/shader/direct-volume.vert.ts @@ -12,9 +12,13 @@ attribute vec3 aPosition; attribute mat4 aTransform; attribute float aInstance; -varying vec3 unitCoord; -varying vec3 origPos; -varying float instance; +uniform mat4 uModelView; +uniform mat4 uProjection; +uniform vec4 uInvariantBoundingSphere; + +varying vec3 vOrigPos; +varying float vInstance; +varying vec4 vBoundingSphere; uniform vec3 uBboxSize; uniform vec3 uBboxMin; @@ -25,21 +29,17 @@ uniform mat4 uTransform; uniform mat4 uUnitToCartn; uniform mat4 uCartnToUnit; -uniform mat4 uModelView; -uniform mat4 uProjection; - void main() { - unitCoord = aPosition + vec3(0.5); + vec3 unitCoord = aPosition + vec3(0.5); vec4 mvPosition = uModelView * uUnitToCartn * vec4(unitCoord, 1.0); - origPos = (uUnitToCartn * vec4(unitCoord, 1.0)).xyz; - instance = aInstance; + + vOrigPos = (uUnitToCartn * vec4(unitCoord, 1.0)).xyz; + vInstance = aInstance; + vBoundingSphere = uInvariantBoundingSphere; + gl_Position = uProjection * mvPosition; - // clamp z position to clip space - if(gl_Position.z > gl_Position.w) { - gl_Position.z = gl_Position.w - 0.0001; - } else if(gl_Position.z < -gl_Position.w) { - gl_Position.z = -gl_Position.w + 0.0001; - } + // move z position to near clip plane + gl_Position.z = gl_Position.w - 0.0001; } `; \ No newline at end of file diff --git a/src/mol-math/geometry/gaussian-density/gpu.ts b/src/mol-math/geometry/gaussian-density/gpu.ts index c91bca94147082cc4bd85835af0ea311498e535f..b5d5352e1f6450f121a1462c9d62d87b7a1e30b0 100644 --- a/src/mol-math/geometry/gaussian-density/gpu.ts +++ b/src/mol-math/geometry/gaussian-density/gpu.ts @@ -49,9 +49,8 @@ export const GaussianDensitySchema = { type GaussianDensityValues = Values<typeof GaussianDensitySchema> type GaussianDensityRenderable = ComputeRenderable<GaussianDensityValues> -export const GaussianDensityShaderCode = ShaderCode( - 'gaussian-density', gaussian_density_vert, gaussian_density_frag, - { standardDerivatives: false, fragDepth: false } +const GaussianDensityShaderCode = ShaderCode( + 'gaussian-density', gaussian_density_vert, gaussian_density_frag ); let _tmpTexture: Texture | undefined = undefined; @@ -299,22 +298,22 @@ function getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, po ValueCell.updateIfChanged(v.drawCount, drawCount); ValueCell.updateIfChanged(v.instanceCount, 1); - ValueCell.updateIfChanged(v.aRadius, radii); - ValueCell.updateIfChanged(v.aPosition, positions); - ValueCell.updateIfChanged(v.aGroup, groups); + ValueCell.update(v.aRadius, radii); + ValueCell.update(v.aPosition, positions); + ValueCell.update(v.aGroup, groups); ValueCell.updateIfChanged(v.uCurrentSlice, 0); ValueCell.updateIfChanged(v.uCurrentX, 0); ValueCell.updateIfChanged(v.uCurrentY, 0); - ValueCell.updateIfChanged(v.uBboxMin, box.min); - ValueCell.updateIfChanged(v.uBboxSize, extent); - ValueCell.updateIfChanged(v.uGridDim, gridDim); - ValueCell.updateIfChanged(v.uGridTexDim, gridTexDim); - ValueCell.updateIfChanged(v.uGridTexScale, gridTexScale); + ValueCell.update(v.uBboxMin, box.min); + ValueCell.update(v.uBboxSize, extent); + ValueCell.update(v.uGridDim, gridDim); + ValueCell.update(v.uGridTexDim, gridTexDim); + ValueCell.update(v.uGridTexScale, gridTexScale); ValueCell.updateIfChanged(v.uAlpha, smoothness); ValueCell.updateIfChanged(v.uResolution, resolution); ValueCell.updateIfChanged(v.uRadiusFactor, radiusFactor); - ValueCell.updateIfChanged(v.tMinDistanceTex, minDistanceTexture); + ValueCell.update(v.tMinDistanceTex, minDistanceTexture); ValueCell.updateIfChanged(v.dGridTexType, minDistanceTexture.getDepth() > 0 ? '3d' : '2d'); ValueCell.updateIfChanged(v.dCalcType, 'density'); @@ -355,8 +354,7 @@ function _getGaussianDensityRenderable(webgl: WebGLContext, drawCount: number, p }; const schema = { ...GaussianDensitySchema }; - const shaderCode = GaussianDensityShaderCode; - const renderItem = createComputeRenderItem(webgl, 'points', shaderCode, schema, values); + const renderItem = createComputeRenderItem(webgl, 'points', GaussianDensityShaderCode, schema, values); return createComputeRenderable(renderItem, values); } diff --git a/src/mol-model/volume/volume.ts b/src/mol-model/volume/volume.ts index 2833f95aa436b03b0aa68e121b058b2227b1c7d1..db901d7fb6e8ab41f3994249ae20316647ddc251 100644 --- a/src/mol-model/volume/volume.ts +++ b/src/mol-model/volume/volume.ts @@ -36,6 +36,16 @@ export interface Volume { } export namespace Volume { + export function is(x: any): x is Volume { + // TODO: improve + return ( + x?.grid?.cells?.space?.dimensions?.length && + x?.sourceData && + x?.customProperties && + x?._propertyData + ); + } + export type CellIndex = { readonly '@type': 'cell-index' } & number export type IsoValue = IsoValue.Absolute | IsoValue.Relative diff --git a/src/mol-repr/util.ts b/src/mol-repr/util.ts index 13740209b037846dacd6202e2863eafa19e684c5..06620f4bd3f4c2a20a49feebf5d54d58cab4e124 100644 --- a/src/mol-repr/util.ts +++ b/src/mol-repr/util.ts @@ -9,6 +9,7 @@ import { Structure } from '../mol-model/structure'; import { VisualQuality } from '../mol-geo/geometry/base'; import { Box3D, SpacegroupCell } from '../mol-math/geometry'; import { ModelSymmetry } from '../mol-model-formats/structure/property/symmetry'; +import { Volume } from '../mol-model/volume'; export interface VisualUpdateState { updateTransform: boolean @@ -111,9 +112,15 @@ export function getQualityProps(props: Partial<QualityProps>, data?: any) { let doubleSided = defaults(props.doubleSided, true); let volume = 0; - if (quality === 'auto' && data instanceof Structure) { - quality = getStructureQuality(data.root); - volume = getRootVolume(data); + if (quality === 'auto') { + if (data instanceof Structure) { + quality = getStructureQuality(data.root); + volume = getRootVolume(data); + } else if (Volume.is(data)) { + const [x, y, z] = data.grid.cells.space.dimensions; + volume = x * y * z; + quality = volume < 10_000_000 ? 'medium' : 'low'; + } } switch (quality) { diff --git a/src/mol-repr/volume/representation.ts b/src/mol-repr/volume/representation.ts index 48544c3b3a5788b9991687324651ae0c9402a32d..38dcff89f0bedd1abae4603fbc58e54e9f817ae1 100644 --- a/src/mol-repr/volume/representation.ts +++ b/src/mol-repr/volume/representation.ts @@ -15,7 +15,7 @@ import { createRenderObject, getNextMaterialId, GraphicsRenderObject } from '../ import { PickingId } from '../../mol-geo/geometry/picking'; import { Loci, isEveryLoci, EmptyLoci } from '../../mol-model/loci'; import { Interval } from '../../mol-data/int'; -import { VisualUpdateState } from '../util'; +import { getQualityProps, VisualUpdateState } from '../util'; import { ColorTheme } from '../../mol-theme/color'; import { ValueCell } from '../../mol-util'; import { createSizes } from '../../mol-geo/geometry/size-data'; @@ -243,7 +243,8 @@ export function VolumeRepresentation<P extends VolumeParams>(label: string, ctx: _volume = volume; if (!_props) _props = PD.getDefaultValues(_params); } - _props = Object.assign({}, _props, props); + const qualityProps = getQualityProps(Object.assign({}, _props, props), _volume); + Object.assign(_props, props, qualityProps); return Task.create('Creating or updating VolumeRepresentation', async runtime => { if (!visual) visual = visualCtor(materialId);