UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

334 lines (299 loc) 12.6 kB
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 };