diff --git a/CHANGELOG.md b/CHANGELOG.md index ff01d03563e5d617f2923fe75fc5e141f5ef7f6f..392ab304cb54dfa7d57759179f5265f6cdc37460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Note that since we don't clearly distinguish between a public and private interf ## [Unreleased] +- Add mipmap-based blur for skybox backgrounds + ## [v3.19.0] - 2022-10-01 - Fix "empty textures" error on empty canvas diff --git a/src/extensions/backgrounds/index.ts b/src/extensions/backgrounds/index.ts index c87c8899f367a38bf377f6d266586faf0fe32ae0..c5bbdc99e1f013f3658ffa660cc0df4889db5e44 100644 --- a/src/extensions/backgrounds/index.ts +++ b/src/extensions/backgrounds/index.ts @@ -72,6 +72,7 @@ export const Backgrounds = PluginBehavior.create<{ }>({ lightness: 0, saturation: 0, opacity: 1, + blur: 0.3, } } }, 'Purple Nebula Skybox'], diff --git a/src/mol-canvas3d/passes/background.ts b/src/mol-canvas3d/passes/background.ts index d4bfb3e59aa3fb02cfdc4e958b61618dbc500235..14d194b6baa418f1e381cf47b182735590df2be2 100644 --- a/src/mol-canvas3d/passes/background.ts +++ b/src/mol-canvas3d/passes/background.ts @@ -49,6 +49,7 @@ const SkyboxParams = { pz: PD.File({ label: 'Positive Z / Front', accept: 'image/*' }), }, { isExpanded: true, label: 'Files' }), }), + blur: PD.Numeric(0, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'Note, this only works in WebGL2 or when "EXT_shader_texture_lod" is available.' }), ...SharedParams, }; type SkyboxProps = PD.Values<typeof SkyboxParams> @@ -170,6 +171,7 @@ export class BackgroundPass { Mat4.invert(m, m); ValueCell.update(this.renderable.values.uViewDirectionProjectionInverse, m); + ValueCell.updateIfChanged(this.renderable.values.uBlur, props.blur); ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity); ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation); ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness); @@ -367,7 +369,7 @@ function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: const cubeAssets = getCubeAssets(assetManager, faces); const cubeFaces = getCubeFaces(assetManager, cubeAssets); const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz]; - const texture = ctx.resources.cubeTexture(cubeFaces, false, onload); + const texture = ctx.resources.cubeTexture(cubeFaces, true, onload); return { texture, assets }; } @@ -424,12 +426,15 @@ const BackgroundSchema = { uGradientColorA: UniformSpec('v3'), uGradientColorB: UniformSpec('v3'), uGradientRatio: UniformSpec('f'), + uBlur: UniformSpec('f'), uOpacity: UniformSpec('f'), uSaturation: UniformSpec('f'), uLightness: UniformSpec('f'), dVariant: DefineSpec('string', ['skybox', 'image', 'verticalGradient', 'horizontalGradient', 'radialGradient']), }; -const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag); +const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag, { + shaderTextureLod: 'optional' +}); type BackgroundRenderable = ComputeRenderable<Values<typeof BackgroundSchema>> function getBackgroundRenderable(ctx: WebGLContext, width: number, height: number): BackgroundRenderable { @@ -448,6 +453,7 @@ function getBackgroundRenderable(ctx: WebGLContext, width: number, height: numbe uGradientColorA: ValueCell.create(Vec3()), uGradientColorB: ValueCell.create(Vec3()), uGradientRatio: ValueCell.create(0.5), + uBlur: ValueCell.create(0), uOpacity: ValueCell.create(1), uSaturation: ValueCell.create(0), uLightness: ValueCell.create(0), diff --git a/src/mol-gl/shader/background.frag.ts b/src/mol-gl/shader/background.frag.ts index a764a9ad8237ec635bb53ad45c7bb7e08f297c4f..835314ff237890841d3d5c57ffccd73eed72911b 100644 --- a/src/mol-gl/shader/background.frag.ts +++ b/src/mol-gl/shader/background.frag.ts @@ -6,6 +6,7 @@ precision mediump sampler2D; #if defined(dVariant_skybox) uniform samplerCube tSkybox; uniform mat4 uViewDirectionProjectionInverse; + uniform float uBlur; uniform float uOpacity; uniform float uSaturation; uniform float uLightness; @@ -49,7 +50,11 @@ vec3 lightenColor(vec3 c, float amount) { void main() { #if defined(dVariant_skybox) vec4 t = uViewDirectionProjectionInverse * vPosition; - gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w)); + #ifdef enabledShaderTextureLod + gl_FragColor = textureCubeLodEXT(tSkybox, normalize(t.xyz / t.w), uBlur * 8.0); + #else + gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w)); + #endif gl_FragColor.a = uOpacity; gl_FragColor.rgb = lightenColor(saturateColor(gl_FragColor.rgb, uSaturation), uLightness); #elif defined(dVariant_image)