@kitware/vtk.js
Version:
Visualization Toolkit for the Web
334 lines (299 loc) • 12.6 kB
JavaScript
import { m as macro } from '../../macros2.js';
import vtkOpenGLTexture from './Texture.js';
import vtkOpenGLFramebuffer from './Framebuffer.js';
import vtkRenderPass from '../SceneGraph/RenderPass.js';
import vtkDataArray from '../../Common/Core/DataArray.js';
import vtkHelper from './Helper.js';
import vtkProperty from '../Core/Property.js';
import vtkShaderProgram from './ShaderProgram.js';
import vtkVertexArrayObject from './VertexArrayObject.js';
const {
Representation
} = vtkProperty;
const {
vtkErrorMacro
} = macro;
// ----------------------------------------------------------------------------
function translucentShaderReplacement(shaders) {
const substituteRes = vtkShaderProgram.substitute(shaders.Fragment, '//VTK::RenderPassFragmentShader::Impl', `
float weight = gl_FragData[0].a * pow(max(1.1 - gl_FragCoord.z, 0.0), 2.0);
gl_FragData[0] = vec4(gl_FragData[0].rgb*weight, gl_FragData[0].a);
gl_FragData[1].r = weight;
`, false);
shaders.Fragment = substituteRes.result;
}
const oitpFragTemplate = `//VTK::System::Dec
in vec2 tcoord;
uniform sampler2D translucentRTexture;
uniform sampler2D translucentRGBATexture;
// the output of this shader
//VTK::Output::Dec
void main()
{
vec4 t1Color = texture(translucentRGBATexture, tcoord);
float t2Color = texture(translucentRTexture, tcoord).r;
gl_FragData[0] = vec4(t1Color.rgb/max(t2Color,0.01), 1.0 - t1Color.a);
}
`;
function vtkOpenGLOrderIndependentTranslucentPass(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkOpenGLOrderIndependentTranslucentPass');
// build vertices etc
publicAPI.createVertexBuffer = () => {
// 4 corner points in clipping space in order (x, y, z) where z is always set to -1
// prettier-ignore
const ptsArray = new Float32Array([-1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1]);
// 4 corresponding corner points in texture space in order (x, y)
const tcoordArray = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);
// a square defined as cell relation ship in order (cell_size, v1, v2, v3, v4)
const cellArray = new Uint16Array([4, 0, 1, 3, 2]);
const points = vtkDataArray.newInstance({
numberOfComponents: 3,
values: ptsArray
});
points.setName('points');
const tcoords = vtkDataArray.newInstance({
numberOfComponents: 2,
values: tcoordArray
});
tcoords.setName('tcoords');
const cells = vtkDataArray.newInstance({
numberOfComponents: 1,
values: cellArray
});
model.tris.getCABO().createVBO(cells, 'polys', Representation.SURFACE, {
points,
tcoords,
cellOffset: 0
});
model.VBOBuildTime.modified();
};
publicAPI.createFramebuffer = viewNode => {
const size = viewNode.getSize();
const gl = viewNode.getContext();
model.framebuffer = vtkOpenGLFramebuffer.newInstance();
model.framebuffer.setOpenGLRenderWindow(viewNode);
model.framebuffer.create(...size);
model.framebuffer.saveCurrentBindingsAndBuffers();
model.framebuffer.bind();
model.translucentRGBATexture = vtkOpenGLTexture.newInstance();
model.translucentRGBATexture.setInternalFormat(gl.RGBA16F);
model.translucentRGBATexture.setFormat(gl.RGBA);
model.translucentRGBATexture.setOpenGLDataType(gl.HALF_FLOAT);
model.translucentRGBATexture.setOpenGLRenderWindow(viewNode);
model.translucentRGBATexture.create2DFromRaw({
width: size[0],
height: size[1],
numComps: 4,
dataType: 'Float32Array',
data: null
});
model.translucentRTexture = vtkOpenGLTexture.newInstance();
model.translucentRTexture.setInternalFormat(gl.R16F);
model.translucentRTexture.setFormat(gl.RED);
model.translucentRTexture.setOpenGLDataType(gl.HALF_FLOAT);
model.translucentRTexture.setOpenGLRenderWindow(viewNode);
model.translucentRTexture.create2DFromRaw({
width: size[0],
height: size[1],
numComps: 1,
dataType: 'Float32Array',
data: null
});
model.translucentZTexture = vtkOpenGLTexture.newInstance();
model.translucentZTexture.setOpenGLRenderWindow(viewNode);
model.translucentZTexture.createDepthFromRaw({
width: size[0],
height: size[1],
dataType: 'Float32Array',
data: null
});
model.framebuffer.setColorBuffer(model.translucentRGBATexture, 0);
model.framebuffer.setColorBuffer(model.translucentRTexture, 1);
model.framebuffer.setDepthBuffer(model.translucentZTexture);
};
publicAPI.createCopyShader = viewNode => {
model.copyShader = viewNode.getShaderCache().readyShaderProgramArray(['//VTK::System::Dec', 'attribute vec4 vertexDC;', 'attribute vec2 tcoordTC;', 'varying vec2 tcoord;', 'void main() { tcoord = tcoordTC; gl_Position = vertexDC; }'].join('\n'), oitpFragTemplate, '');
};
publicAPI.createVBO = viewNode => {
const gl = viewNode.getContext();
model.tris.setOpenGLRenderWindow(viewNode);
publicAPI.createVertexBuffer();
const program = model.copyShader;
// prepare the vertex and triangle data for the image plane to render to
model.tris.getCABO().bind();
if (!model.copyVAO.addAttributeArray(program, model.tris.getCABO(), 'vertexDC', model.tris.getCABO().getVertexOffset(), model.tris.getCABO().getStride(), gl.FLOAT, 3, gl.FALSE)) {
vtkErrorMacro('Error setting vertexDC in copy shader VAO.');
}
if (!model.copyVAO.addAttributeArray(program, model.tris.getCABO(), 'tcoordTC', model.tris.getCABO().getTCoordOffset(), model.tris.getCABO().getStride(), gl.FLOAT, 2, gl.FALSE)) {
vtkErrorMacro('Error setting vertexDC in copy shader VAO.');
}
};
publicAPI.traverse = (viewNode, renNode, forwardPass) => {
if (model.deleted) {
return;
}
const size = viewNode.getSize();
const gl = viewNode.getContext();
// if we lack the webgl2 and half floatsupport just do
// basic alpha blending
model._supported = false;
if (renNode.getSelector() || !gl || !viewNode.getWebgl2() || !gl.getExtension('EXT_color_buffer_half_float') && !gl.getExtension('EXT_color_buffer_float')) {
publicAPI.setCurrentOperation('translucentPass');
renNode.traverse(publicAPI);
return;
}
model._supported = true;
// prepare framebuffer // allocate framebuffer if needed and bind it
if (model.framebuffer === null) {
publicAPI.createFramebuffer(viewNode);
} else {
const fbSize = model.framebuffer.getSize();
if (fbSize === null || fbSize[0] !== size[0] || fbSize[1] !== size[1]) {
model.framebuffer.releaseGraphicsResources();
model.translucentRGBATexture.releaseGraphicsResources(viewNode);
model.translucentRTexture.releaseGraphicsResources(viewNode);
model.translucentZTexture.releaseGraphicsResources(viewNode);
publicAPI.createFramebuffer(viewNode);
} else {
// store framebuffer bindings to restore them later
model.framebuffer.saveCurrentBindingsAndBuffers();
model.framebuffer.bind();
}
}
gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
gl.clearBufferfv(gl.COLOR, 0, [0.0, 0.0, 0.0, 0.0]);
gl.clearBufferfv(gl.DEPTH, 0, [1.0]);
gl.colorMask(false, false, false, false);
// rerender the opaque pass to set the depth buffer
// TODO remove when webgl1 is deprecated and instead
// have the forward pass use a texture backed zbuffer
if (forwardPass.getOpaqueActorCount() > 0) {
// Don't use zBufferPass as it will also render the depth of translucent actors
forwardPass.setCurrentOperation('opaqueZBufferPass');
renNode.traverse(forwardPass);
}
gl.colorMask(true, true, true, true);
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
// make sure to clear the entire framebuffer as we will
// be blitting the entire thing all of it needs good initial values
gl.viewport(0, 0, size[0], size[1]);
gl.scissor(0, 0, size[0], size[1]);
gl.clearBufferfv(gl.COLOR, 0, [0.0, 0.0, 0.0, 1.0]);
gl.clearBufferfv(gl.COLOR, 1, [0.0, 0.0, 0.0, 0.0]);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
// basic gist is we accumulate color into RGB We compute final opacity
// into A We store accumulated opacity into R of the R texture.
gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ZERO, gl.ONE_MINUS_SRC_ALPHA);
// now do the translucent rendering
publicAPI.setCurrentOperation('translucentPass');
renNode.traverse(publicAPI);
gl.drawBuffers([gl.NONE]);
model.framebuffer.restorePreviousBindingsAndBuffers();
// gl.drawBuffers([gl.BACK]);
// make sure the copy shader is ready
if (model.copyShader === null) {
publicAPI.createCopyShader(viewNode);
} else {
viewNode.getShaderCache().readyShaderProgram(model.copyShader);
}
// make sure we have a VAO
if (!model.copyVAO) {
model.copyVAO = vtkVertexArrayObject.newInstance();
model.copyVAO.setOpenGLRenderWindow(viewNode);
}
model.copyVAO.bind();
// make sure the VBO is up to date
if (model.VBOBuildTime.getMTime() < publicAPI.getMTime()) {
publicAPI.createVBO(viewNode);
}
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.depthMask(false);
gl.depthFunc(gl.ALWAYS);
gl.viewport(0, 0, size[0], size[1]);
gl.scissor(0, 0, size[0], size[1]);
// activate texture
model.translucentRGBATexture.activate();
model.copyShader.setUniformi('translucentRGBATexture', model.translucentRGBATexture.getTextureUnit());
model.translucentRTexture.activate();
model.copyShader.setUniformi('translucentRTexture', model.translucentRTexture.getTextureUnit());
// render quad
gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount());
gl.depthMask(true);
gl.depthFunc(gl.LEQUAL);
model.translucentRGBATexture.deactivate();
model.translucentRTexture.deactivate();
// restore scissor + viewport from renderer
const ts = renNode.getTiledSizeAndOrigin();
gl.scissor(ts.lowerLeftU, ts.lowerLeftV, ts.usize, ts.vsize);
gl.viewport(ts.lowerLeftU, ts.lowerLeftV, ts.usize, ts.vsize);
};
publicAPI.getShaderReplacement = () => {
if (model._supported) {
return translucentShaderReplacement;
}
return null;
};
publicAPI.releaseGraphicsResources = viewNode => {
if (model.framebuffer) {
model.framebuffer.releaseGraphicsResources(viewNode);
model.framebuffer = null;
}
if (model.translucentRGBATexture) {
model.translucentRGBATexture.releaseGraphicsResources(viewNode);
model.translucentRGBATexture = null;
}
if (model.translucentRTexture) {
model.translucentRTexture.releaseGraphicsResources(viewNode);
model.translucentRTexture = null;
}
if (model.translucentZTexture) {
model.translucentZTexture.releaseGraphicsResources(viewNode);
model.translucentZTexture = null;
}
if (model.copyVAO) {
model.copyVAO.releaseGraphicsResources(viewNode);
model.copyVAO = null;
}
if (model.copyShader) {
model.copyShader.releaseGraphicsResources(viewNode);
model.copyShader = null;
}
if (model.tris) {
model.tris.releaseGraphicsResources(viewNode);
model.tris = null;
}
publicAPI.modified();
};
}
// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------
const DEFAULT_VALUES = {
framebuffer: null,
copyShader: null,
tris: null
};
// ----------------------------------------------------------------------------
function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);
// Build VTK API
vtkRenderPass.extend(publicAPI, model, initialValues);
model.VBOBuildTime = {};
macro.obj(model.VBOBuildTime, {
mtime: 0
});
model.tris = vtkHelper.newInstance();
macro.get(publicAPI, model, ['framebuffer']);
// Object methods
vtkOpenGLOrderIndependentTranslucentPass(publicAPI, model);
}
// ----------------------------------------------------------------------------
const newInstance = macro.newInstance(extend, 'vtkOpenGLOrderIndependentTranslucentPass');
// ----------------------------------------------------------------------------
var vtkOpenGLOrderIndependentTranslucentPass$1 = {
newInstance,
extend
};
export { vtkOpenGLOrderIndependentTranslucentPass$1 as default, extend, newInstance };