molstar
Version:
A comprehensive macromolecular library.
552 lines (551 loc) • 29.7 kB
JavaScript
/**
* Copyright (c) 2024-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { QuadSchema, QuadValues, createCopyRenderable } from '../../mol-gl/compute/util';
import { DefineSpec, TextureSpec, UniformSpec } from '../../mol-gl/renderable/schema';
import { ValueCell } from '../../mol-util';
import { isDebugMode, isTimingMode } from '../../mol-util/debug';
import { Camera } from '../camera';
import { ShaderCode } from '../../mol-gl/shader-code';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { createComputeRenderable } from '../../mol-gl/renderable';
import { compose_frag } from '../../mol-gl/shader/illumination/compose.frag';
import { Vec2 } from '../../mol-math/linear-algebra/3d/vec2';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Color } from '../../mol-util/color/color';
import { AntialiasingPass, PostprocessingPass } from './postprocessing';
import { MarkingPass } from './marking';
import { DofPass } from './dof';
import { TracingParams, TracingPass } from './tracing';
import { JitterVectors } from './multi-sample';
import { compose_frag as multiSample_compose_frag } from '../../mol-gl/shader/compose.frag';
import { clamp, lerp } from '../../mol-math/interpolate';
export const IlluminationParams = {
enabled: PD.Boolean(false),
maxIterations: PD.Numeric(5, { min: 0, max: 16, step: 1 }, { description: 'Maximum number of tracing iterations. Final iteration count is 2^x.' }),
denoise: PD.Boolean(true),
denoiseThreshold: PD.Interval([0.15, 1], { min: 0, max: 4, step: 0.01 }, { description: 'Threshold for denoising. Automatically adjusted within given interval based on current iteration.' }),
ignoreOutline: PD.Boolean(true, { description: 'Ignore outline in illumination pass where it is generally not needed for visual clarity. Useful when illumination is often toggled on/off.' }),
...TracingParams,
};
export class IlluminationPass {
get iteration() { return this._iteration; }
get colorTarget() { return this._colorTarget; }
get supported() {
return this._supported;
}
getMaxIterations(props) {
return Math.pow(2, props.illumination.maxIterations);
}
static isSupported(webgl) {
const { drawBuffers, textureFloat, colorBufferFloat, depthTexture } = webgl.extensions;
if (!textureFloat || !colorBufferFloat || !depthTexture || !drawBuffers) {
if (isDebugMode) {
const missing = [];
if (!textureFloat)
missing.push('textureFloat');
if (!colorBufferFloat)
missing.push('colorBufferFloat');
if (!depthTexture)
missing.push('depthTexture');
if (!drawBuffers)
missing.push('drawBuffers');
console.log(`Missing "${missing.join('", "')}" extensions required for "illumination"`);
}
return false;
}
else {
return true;
}
}
constructor(webgl, drawPass) {
this.webgl = webgl;
this.drawPass = drawPass;
this._iteration = 0;
this._supported = false;
this.prevSampleIndex = -1;
if (!IlluminationPass.isSupported(webgl))
return;
const { colorTarget } = drawPass;
const width = colorTarget.getWidth();
const height = colorTarget.getHeight();
this.tracing = new TracingPass(webgl, this.drawPass);
this.transparentTarget = webgl.createRenderTarget(width, height, false, 'uint8', 'nearest');
this.outputTarget = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
this.copyRenderable = createCopyRenderable(webgl, this.transparentTarget.texture);
this.composeRenderable = getComposeRenderable(webgl, this.tracing.accumulateTarget.texture, this.tracing.normalTextureOpaque, this.tracing.colorTextureOpaque, this.drawPass.depthTextureOpaque, this.drawPass.depthTargetTransparent.texture, this.drawPass.postprocessing.outline.target.texture, this.transparentTarget.texture, this.drawPass.postprocessing.ssao.ssaoDepthTexture, this.drawPass.postprocessing.ssao.ssaoDepthTransparentTexture, false);
this.multiSampleComposeTarget = webgl.createRenderTarget(width, height, false, 'float32');
this.multiSampleHoldTarget = webgl.createRenderTarget(width, height, false);
this.multiSampleAccumulateTarget = webgl.createRenderTarget(width, height, false, 'float32');
this.multiSampleCompose = getMultiSampleComposeRenderable(webgl, this.outputTarget.texture);
this._supported = true;
}
renderInput(renderer, camera, scene, props) {
if (isTimingMode)
this.webgl.timer.mark('IlluminationPass.renderInput');
const { gl, state } = this.webgl;
const markingEnabled = MarkingPass.isEnabled(props.marking);
const hasTransparent = scene.opacityAverage < 1;
const hasMarking = markingEnabled && scene.markerAverage > 0;
this.transparentTarget.bind();
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
const outlineEnabled = PostprocessingPass.isTransparentOutlineEnabled(props.postprocessing) && !props.illumination.ignoreOutline;
const dofEnabled = DofPass.isEnabled(props.postprocessing);
const ssaoEnabled = PostprocessingPass.isTransparentSsaoEnabled(scene, props.postprocessing);
if (outlineEnabled || dofEnabled || ssaoEnabled) {
this.drawPass.depthTargetTransparent.bind();
renderer.clearDepth(true);
}
if (hasTransparent) {
if (this.drawPass.transparency === 'wboit') {
this.drawPass.wboit.bind();
renderer.renderWboitTransparent(scene.primitives, camera, this.drawPass.depthTextureOpaque);
if (scene.volumes.renderables.length > 0) {
renderer.renderWboitTransparent(scene.volumes, camera, this.drawPass.depthTextureOpaque);
}
this.transparentTarget.bind();
this.drawPass.wboit.render();
}
else if (this.drawPass.transparency === 'dpoit') {
const dpoitTextures = this.drawPass.dpoit.bind();
renderer.renderDpoitTransparent(scene.primitives, camera, this.drawPass.depthTextureOpaque, dpoitTextures);
for (let i = 0, il = props.dpoitIterations; i < il; i++) {
if (isTimingMode)
this.webgl.timer.mark('DpoitPass.layer');
const dpoitTextures = this.drawPass.dpoit.bindDualDepthPeeling();
renderer.renderDpoitTransparent(scene.primitives, camera, this.drawPass.depthTextureOpaque, dpoitTextures);
this.transparentTarget.bind();
this.drawPass.dpoit.renderBlendBack();
if (isTimingMode)
this.webgl.timer.markEnd('DpoitPass.layer');
}
// evaluate dpoit
this.transparentTarget.bind();
this.drawPass.dpoit.render();
if (scene.volumes.renderables.length > 0) {
renderer.renderVolume(scene.volumes, camera, this.drawPass.depthTextureOpaque);
}
}
else {
this.transparentTarget.bind();
this.drawPass.depthTextureOpaque.attachFramebuffer(this.transparentTarget.framebuffer, 'depth');
renderer.renderBlendedTransparent(scene.primitives, camera);
this.drawPass.depthTextureOpaque.detachFramebuffer(this.transparentTarget.framebuffer, 'depth');
if (scene.volumes.renderables.length > 0) {
renderer.renderVolume(scene.volumes, camera, this.drawPass.depthTextureOpaque);
}
}
if (outlineEnabled || dofEnabled || ssaoEnabled) {
this.drawPass.depthTargetTransparent.bind();
if (scene.opacityAverage < 1) {
renderer.renderDepthTransparent(scene.primitives, camera, this.drawPass.depthTextureOpaque);
}
}
if (ssaoEnabled) {
this.drawPass.postprocessing.ssao.update(camera, scene, props.postprocessing.occlusion.params, true);
this.drawPass.postprocessing.ssao.render(camera);
}
}
//
if (hasMarking) {
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest && scene.markerAverage !== 1) {
this.drawPass.marking.depthTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingDepth(scene.primitives, camera);
}
this.drawPass.marking.maskTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.drawPass.marking.depthTarget.texture : null);
this.drawPass.marking.update(props.marking);
this.drawPass.marking.render(camera.viewport, this.transparentTarget);
}
//
this.tracing.composeTarget.bind();
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
if (isTimingMode)
this.webgl.timer.markEnd('IlluminationPass.renderInput');
}
shouldRender(props) {
return this._supported && props.illumination.enabled && this._iteration < this.getMaxIterations(props);
}
setSize(width, height) {
if (!this._supported)
return;
const w = this.outputTarget.getWidth();
const h = this.outputTarget.getHeight();
if (width !== w || height !== h) {
this.tracing.setSize(width, height);
this.transparentTarget.setSize(width, height);
this.outputTarget.setSize(width, height);
ValueCell.update(this.copyRenderable.values.uTexSize, Vec2.set(this.copyRenderable.values.uTexSize.ref.value, width, height));
ValueCell.update(this.composeRenderable.values.uTexSize, Vec2.set(this.composeRenderable.values.uTexSize.ref.value, width, height));
this.multiSampleComposeTarget.setSize(width, height);
this.multiSampleHoldTarget.setSize(width, height);
this.multiSampleAccumulateTarget.setSize(width, height);
ValueCell.update(this.multiSampleCompose.values.uTexSize, Vec2.set(this.multiSampleCompose.values.uTexSize.ref.value, width, height));
}
this.drawPass.setSize(width, height);
}
reset() {
if (!this._supported)
return;
this.tracing.reset();
this._iteration = 0;
this.prevSampleIndex = -1;
}
restart(clearAdjustedProps = false) {
if (!this._supported)
return;
this.tracing.restart(clearAdjustedProps);
this._iteration = 0;
this.prevSampleIndex = -1;
}
renderInternal(ctx, props, toDrawingBuffer, forceRenderInput) {
if (!this.shouldRender(props))
return;
if (isTimingMode) {
this.webgl.timer.mark('IlluminationPass.render', {
note: `iteration ${this._iteration + 1} of ${this.getMaxIterations(props)}`
});
}
this.tracing.render(ctx, props.transparentBackground, props.illumination, this._iteration, forceRenderInput);
const { renderer, camera, scene, helper } = ctx;
const { gl, state } = this.webgl;
const { x, y, width, height } = camera.viewport;
if (this._iteration === 0 || forceRenderInput) {
// render color & depth
renderer.setTransparentBackground(props.transparentBackground);
renderer.setDrawingBufferSize(this.tracing.composeTarget.getWidth(), this.tracing.composeTarget.getHeight());
renderer.setPixelRatio(this.webgl.pixelRatio);
renderer.setViewport(x, y, width, height);
renderer.update(camera, scene);
this.renderInput(renderer, camera, scene, props);
}
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.CULL_FACE);
state.depthMask(false);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
const orthographic = camera.state.mode === 'orthographic' ? 1 : 0;
const outlinesEnabled = props.postprocessing.outline.name === 'on' && !props.illumination.ignoreOutline;
const occlusionEnabled = PostprocessingPass.isTransparentSsaoEnabled(scene, props.postprocessing);
const markingEnabled = MarkingPass.isEnabled(props.marking);
const hasTransparent = scene.opacityAverage < 1;
const hasMarking = markingEnabled && scene.markerAverage > 0;
let needsUpdateCompose = false;
if (this.composeRenderable.values.dOutlineEnable.ref.value !== outlinesEnabled) {
needsUpdateCompose = true;
ValueCell.update(this.composeRenderable.values.dOutlineEnable, outlinesEnabled);
}
if (props.postprocessing.outline.name === 'on') {
const { transparentOutline, outlineScale } = this.drawPass.postprocessing.outline.update(camera, props.postprocessing.outline.params, this.drawPass.depthTargetTransparent.texture, this.drawPass.depthTextureOpaque);
this.drawPass.postprocessing.outline.render();
ValueCell.update(this.composeRenderable.values.uOutlineColor, Color.toVec3Normalized(this.composeRenderable.values.uOutlineColor.ref.value, props.postprocessing.outline.params.color));
if (this.composeRenderable.values.dOutlineScale.ref.value !== outlineScale) {
needsUpdateCompose = true;
ValueCell.update(this.composeRenderable.values.dOutlineScale, outlineScale);
}
if (this.composeRenderable.values.dTransparentOutline.ref.value !== transparentOutline) {
needsUpdateCompose = true;
ValueCell.update(this.composeRenderable.values.dTransparentOutline, transparentOutline);
}
}
if (this.composeRenderable.values.dOcclusionEnable.ref.value !== occlusionEnabled) {
needsUpdateCompose = true;
ValueCell.update(this.composeRenderable.values.dOcclusionEnable, occlusionEnabled);
}
if (props.postprocessing.occlusion.name === 'on') {
ValueCell.update(this.composeRenderable.values.uOcclusionColor, Color.toVec3Normalized(this.composeRenderable.values.uOcclusionColor.ref.value, props.postprocessing.occlusion.params.color));
}
const blendTransparency = hasTransparent || hasMarking;
if (this.composeRenderable.values.dBlendTransparency.ref.value !== blendTransparency) {
needsUpdateCompose = true;
ValueCell.update(this.composeRenderable.values.dBlendTransparency, blendTransparency);
}
ValueCell.updateIfChanged(this.composeRenderable.values.uNear, camera.near);
ValueCell.updateIfChanged(this.composeRenderable.values.uFar, camera.far);
ValueCell.updateIfChanged(this.composeRenderable.values.uFogFar, camera.fogFar);
ValueCell.updateIfChanged(this.composeRenderable.values.uFogNear, camera.fogNear);
ValueCell.update(this.composeRenderable.values.uFogColor, Color.toVec3Normalized(this.composeRenderable.values.uFogColor.ref.value, renderer.props.backgroundColor));
if (this.composeRenderable.values.dOrthographic.ref.value !== orthographic) {
ValueCell.update(this.composeRenderable.values.dOrthographic, orthographic);
needsUpdateCompose = true;
}
// background
const _toDrawingBuffer = toDrawingBuffer && !AntialiasingPass.isEnabled(props.postprocessing) && props.postprocessing.dof.name === 'off';
if (_toDrawingBuffer) {
this.webgl.unbindFramebuffer();
}
else {
this.tracing.composeTarget.bind();
}
this._colorTarget = this.tracing.composeTarget;
this.drawPass.postprocessing.background.update(camera, props.postprocessing.background);
this.drawPass.postprocessing.background.clear(props.postprocessing.background, props.transparentBackground, renderer.props.backgroundColor);
this.drawPass.postprocessing.background.render(props.postprocessing.background);
// compose
ValueCell.updateIfChanged(this.composeRenderable.values.uTransparentBackground, props.transparentBackground || this.drawPass.postprocessing.background.isEnabled(props.postprocessing.background));
if (this.composeRenderable.values.dDenoise.ref.value !== props.illumination.denoise) {
ValueCell.update(this.composeRenderable.values.dDenoise, props.illumination.denoise);
needsUpdateCompose = true;
}
const denoiseThreshold = props.multiSample.mode === 'on'
? props.illumination.denoiseThreshold[0]
: lerp(props.illumination.denoiseThreshold[1], props.illumination.denoiseThreshold[0], clamp(this.iteration / (this.getMaxIterations(props) / 2), 0, 1));
ValueCell.updateIfChanged(this.composeRenderable.values.uDenoiseThreshold, denoiseThreshold);
if (needsUpdateCompose)
this.composeRenderable.update();
this.composeRenderable.render();
//
renderer.setDrawingBufferSize(this.tracing.composeTarget.getWidth(), this.tracing.composeTarget.getHeight());
renderer.setPixelRatio(this.webgl.pixelRatio);
renderer.setViewport(x, y, width, height);
renderer.update(camera, scene);
if (helper.debug.isEnabled) {
helper.debug.syncVisibility();
renderer.renderBlended(helper.debug.scene, camera);
}
if (helper.handle.isEnabled) {
renderer.renderBlended(helper.handle.scene, camera);
}
if (helper.camera.isEnabled) {
helper.camera.update(camera);
renderer.update(helper.camera.camera, helper.camera.scene);
renderer.renderBlended(helper.camera.scene, helper.camera.camera);
}
//
let targetIsDrawingbuffer = false;
let swapTarget = this.outputTarget;
if (AntialiasingPass.isEnabled(props.postprocessing)) {
const _toDrawingBuffer = toDrawingBuffer && props.postprocessing.dof.name === 'off';
this.drawPass.antialiasing.render(camera, this.tracing.composeTarget.texture, _toDrawingBuffer ? true : this.outputTarget, props.postprocessing);
if (_toDrawingBuffer) {
targetIsDrawingbuffer = true;
}
else {
this._colorTarget = this.outputTarget;
swapTarget = this.tracing.composeTarget;
}
}
if (props.postprocessing.bloom.name === 'on') {
const _toDrawingBuffer = (toDrawingBuffer && props.postprocessing.dof.name === 'off') || targetIsDrawingbuffer;
this.drawPass.bloom.update(this.tracing.colorTextureOpaque, this.tracing.normalTextureOpaque, this.drawPass.depthTextureOpaque, props.postprocessing.bloom.params);
this.drawPass.bloom.render(camera.viewport, _toDrawingBuffer ? undefined : this._colorTarget);
}
if (props.postprocessing.dof.name === 'on') {
const _toDrawingBuffer = toDrawingBuffer || targetIsDrawingbuffer;
this.drawPass.dof.update(camera, this._colorTarget.texture, this.drawPass.depthTextureOpaque, this.drawPass.depthTargetTransparent.texture, props.postprocessing.dof.params, scene.boundingSphereVisible);
this.drawPass.dof.render(camera.viewport, _toDrawingBuffer ? undefined : swapTarget);
if (!_toDrawingBuffer) {
this._colorTarget = swapTarget;
}
}
this._iteration += 1;
if (isTimingMode)
this.webgl.timer.markEnd('IlluminationPass.render');
this.webgl.gl.flush();
}
renderMultiSample(ctx, props, toDrawingBuffer) {
const { camera } = ctx;
const { multiSampleCompose, multiSampleComposeTarget, multiSampleHoldTarget, webgl } = this;
const { gl, state } = webgl;
// based on the Multisample Anti-Aliasing Render Pass
// contributed to three.js by bhouston / http://clara.io/
//
// This manual approach to MSAA re-renders the scene once for
// each sample with camera jitter and accumulates the results.
const offsetList = JitterVectors[Math.max(0, Math.min(props.multiSample.sampleLevel, 5))];
const maxIterations = this.getMaxIterations(props);
const iteration = Math.min(this._iteration, maxIterations);
const sampleIndex = Math.floor((iteration / maxIterations) * offsetList.length);
if (isTimingMode) {
webgl.timer.mark('IlluminationPass.renderMultiSample', {
note: `sampleIndex ${sampleIndex + 1} of ${offsetList.length}`
});
}
const { x, y, width, height } = camera.viewport;
const sampleWeight = 1.0 / maxIterations;
if (iteration === 0) {
this.renderInternal(ctx, props, false, true);
ValueCell.update(multiSampleCompose.values.uWeight, 1.0);
ValueCell.update(multiSampleCompose.values.tColor, this._colorTarget.texture);
multiSampleCompose.update();
multiSampleHoldTarget.bind();
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
multiSampleCompose.render();
}
else {
camera.viewOffset.enabled = true;
ValueCell.update(multiSampleCompose.values.tColor, this._colorTarget.texture);
ValueCell.update(multiSampleCompose.values.uWeight, sampleWeight);
multiSampleCompose.update();
// render the scene multiple times, each slightly jitter offset
// from the last and accumulate the results.
const offset = offsetList[sampleIndex];
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height);
camera.update();
// render scene
this.renderInternal(ctx, props, false, this.prevSampleIndex !== sampleIndex);
// compose rendered scene with compose target
multiSampleComposeTarget.bind();
state.enable(gl.BLEND);
state.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD);
state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
if (iteration === 1) {
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
multiSampleCompose.render();
}
this.prevSampleIndex = sampleIndex;
if (toDrawingBuffer) {
this.webgl.unbindFramebuffer();
}
else {
this.multiSampleAccumulateTarget.bind();
}
state.viewport(x, y, width, height);
state.scissor(x, y, width, height);
const accumulationWeight = iteration * sampleWeight;
if (accumulationWeight > 0) {
ValueCell.update(multiSampleCompose.values.uWeight, 1.0);
ValueCell.update(multiSampleCompose.values.tColor, multiSampleComposeTarget.texture);
multiSampleCompose.update();
state.disable(gl.BLEND);
multiSampleCompose.render();
}
if (accumulationWeight < 1.0) {
ValueCell.update(multiSampleCompose.values.uWeight, 1.0 - accumulationWeight);
ValueCell.update(multiSampleCompose.values.tColor, multiSampleHoldTarget.texture);
multiSampleCompose.update();
if (accumulationWeight === 0)
state.disable(gl.BLEND);
else
state.enable(gl.BLEND);
multiSampleCompose.render();
}
if (!toDrawingBuffer) {
state.disable(gl.BLEND);
this.colorTarget.bind();
if (this.copyRenderable.values.tColor.ref.value !== this.multiSampleAccumulateTarget.texture) {
ValueCell.update(this.copyRenderable.values.tColor, this.multiSampleAccumulateTarget.texture);
this.copyRenderable.update();
}
this.copyRenderable.render();
}
camera.viewOffset.enabled = false;
camera.update();
if (isTimingMode)
webgl.timer.markEnd('IlluminationPass.renderMultiSample');
}
render(ctx, props, toDrawingBuffer) {
if (!this._supported)
return;
if (props.multiSample.mode === 'on') {
this.renderMultiSample(ctx, props, toDrawingBuffer);
}
else {
this.renderInternal(ctx, props, toDrawingBuffer, false);
}
}
}
//
const ComposeSchema = {
...QuadSchema,
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tNormal: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tShaded: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tTransparentColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
dBlendTransparency: DefineSpec('boolean'),
tSsaoDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tSsaoDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tOutlines: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
dDenoise: DefineSpec('boolean'),
uDenoiseThreshold: UniformSpec('f'),
dOrthographic: DefineSpec('number'),
uNear: UniformSpec('f'),
uFar: UniformSpec('f'),
uFogNear: UniformSpec('f'),
uFogFar: UniformSpec('f'),
uFogColor: UniformSpec('v3'),
uOutlineColor: UniformSpec('v3'),
uOcclusionColor: UniformSpec('v3'),
uTransparentBackground: UniformSpec('b'),
dOcclusionEnable: DefineSpec('boolean'),
dOutlineEnable: DefineSpec('boolean'),
dOutlineScale: DefineSpec('number'),
dTransparentOutline: DefineSpec('boolean'),
};
const ComposeShaderCode = ShaderCode('compose', quad_vert, compose_frag);
function getComposeRenderable(ctx, colorTexture, normalTexture, shadedTexture, depthTextureOpaque, depthTextureTransparent, outlinesTexture, transparentColorTexture, ssaoDepthOpaqueTexture, ssaoDepthTransparentTexture, transparentOutline) {
const values = {
...QuadValues,
tColor: ValueCell.create(colorTexture),
tNormal: ValueCell.create(normalTexture),
tShaded: ValueCell.create(shadedTexture),
tTransparentColor: ValueCell.create(transparentColorTexture),
dBlendTransparency: ValueCell.create(true),
tSsaoDepth: ValueCell.create(ssaoDepthOpaqueTexture),
tSsaoDepthTransparent: ValueCell.create(ssaoDepthTransparentTexture),
tDepthOpaque: ValueCell.create(depthTextureOpaque),
tDepthTransparent: ValueCell.create(depthTextureTransparent),
tOutlines: ValueCell.create(outlinesTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
dDenoise: ValueCell.create(true),
uDenoiseThreshold: ValueCell.create(0.1),
dOrthographic: ValueCell.create(0),
uNear: ValueCell.create(1),
uFar: ValueCell.create(10000),
uFogNear: ValueCell.create(10000),
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
uOutlineColor: ValueCell.create(Vec3.create(0, 0, 0)),
uOcclusionColor: ValueCell.create(Vec3.create(0, 0, 0)),
uTransparentBackground: ValueCell.create(false),
dOcclusionEnable: ValueCell.create(false),
dOutlineEnable: ValueCell.create(false),
dOutlineScale: ValueCell.create(1),
dTransparentOutline: ValueCell.create(transparentOutline),
};
const schema = { ...ComposeSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', ComposeShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
//
const MultiSampleComposeSchema = {
...QuadSchema,
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
uWeight: UniformSpec('f'),
};
const MultiSampleComposeShaderCode = ShaderCode('compose', quad_vert, multiSample_compose_frag);
function getMultiSampleComposeRenderable(ctx, colorTexture) {
const values = {
...QuadValues,
tColor: ValueCell.create(colorTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
uWeight: ValueCell.create(1.0),
};
const schema = { ...MultiSampleComposeSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', MultiSampleComposeShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}