UNPKG

@kitware/vtk.js

Version:

Visualization Toolkit for the Web

391 lines (370 loc) 16.2 kB
import { m as macro } from '../../../macros2.js'; import { v as vtkLineIntegralConvolution2D_LIC0 } from './glsl/vtkLineIntegralConvolution2D_LIC0.glsl.js'; import { v as vtkLineIntegralConvolution2D_LICI } from './glsl/vtkLineIntegralConvolution2D_LICI.glsl.js'; import { v as vtkLineIntegralConvolution2D_LICN } from './glsl/vtkLineIntegralConvolution2D_LICN.glsl.js'; import { v as vtkLineIntegralConvolution2D_CE } from './glsl/vtkLineIntegralConvolution2D_CE.glsl.js'; import { v as vtkLineIntegralConvolution2D_EE } from './glsl/vtkLineIntegralConvolution2D_EE.glsl.js'; import { v as vtkLineIntegralConvolution2D_AAH } from './glsl/vtkLineIntegralConvolution2D_AAH.glsl.js'; import { v as vtkLineIntegralConvolution2D_VT } from './glsl/vtkLineIntegralConvolution2D_VT.glsl.js'; import { v as vtkLineIntegralConvolution2D_AAV } from './glsl/vtkLineIntegralConvolution2D_AAV.glsl.js'; import { v as vtkLineIntegralConvolution2D_quadVS } from './glsl/vtkLineIntegralConvolution2D_quadVS.glsl.js'; import vtkShaderProgram from '../ShaderProgram.js'; import vtkOpenGLFramebuffer from '../Framebuffer.js'; import vtkLICPingPongBufferManager from './LineIntegralConvolution2D/pingpong.js'; import { ContrastEnhanceMode } from '../../Core/SurfaceLICInterface/Constants.js'; function getVectorLookupProgram(normalize = true) { // lookup the vector and normalize const getNormVecSrc = ` vec2 getVector( vec2 vectc )\n {\n vec2 V = texture2D( texVectors, vectc ).xy;\n // normalize if |V| not 0\n float lenV = length( V );\n if ( lenV > 1.0e-8 )\n {\n return V/lenV;\n }\n else\n {\n return vec2( 0.0, 0.0 );\n }\n }\n `; // lookup the vector const getVecSrc = ` vec2 getVector( vec2 vectc )\n {\n return texture2D( texVectors, vectc ).xy;\n }\n `; if (normalize) { return getNormVecSrc; } return getVecSrc; } function vtkLineIntegralConvolution2D(publicAPI, model) { model.classHierarchy.push('vtkLineIntegralConvolution2D'); publicAPI.buildAShader = fSource => model._openGLRenderWindow.getShaderCache().readyShaderProgramArray(vtkLineIntegralConvolution2D_quadVS, fSource, ''); publicAPI.dumpTextureValues = (texture, [width, height], context = model.context, openGLRenderWindow = model._openGLRenderWindow, nComp = 4) => { // To get texture values in es 2.0, we need to attach the texture to a fbo, // then use glReadPixels const fb = vtkOpenGLFramebuffer.newInstance(); const gl = context; let pixels = null; fb.setOpenGLRenderWindow(openGLRenderWindow); fb.saveCurrentBindingsAndBuffers(); fb.create(width, height); fb.populateFramebuffer(); fb.setColorBuffer(texture); pixels = new Float32Array(width * height * nComp); gl.readPixels(0, 0, width, height, nComp === 4 ? gl.RGBA : gl.RGB, gl.FLOAT, pixels); fb.restorePreviousBindingsAndBuffers(); return pixels; }; publicAPI.getTextureMinMax = (texture, size, context = model.context, openGLRenderWindow = model._openGLRenderWindow) => { const values = publicAPI.dumpTextureValues(texture, size, context, openGLRenderWindow, 4); let min = Number.MAX_VALUE; let max = Number.MIN_VALUE; for (let i = 0; i < values.length; i += 4) { // Make sure the current pixel is inside the rendered geometry (g === 0) if (values[i + 1] === 0.0) { const val = values[i]; if (val < min) { min = val; } if (val > max) { max = val; } } } return { min, max }; }; publicAPI.getComponentSelectionProgram = ids => { const compNames = 'xyzw'; return `.${compNames[ids[0]]}${compNames[ids[1]]}`; }; publicAPI.buildShaders = () => { model.LIC0ShaderProgram = publicAPI.buildAShader(vtkLineIntegralConvolution2D_LIC0); const VTFSource = vtkShaderProgram.substitute(vtkLineIntegralConvolution2D_VT, '//VTK::LICComponentSelection::Impl', `vec2 V = texture2D(texVectors, tcoordVC.st)${publicAPI.getComponentSelectionProgram(model.componentIds)};`).result; model.VTProgram = publicAPI.buildAShader(VTFSource); const LICISource = vtkShaderProgram.substitute(vtkLineIntegralConvolution2D_LICI, '//VTK::LICVectorLookup::Impl', getVectorLookupProgram(model.normalizeVectors), true).result; model.LICIShaderProgram = publicAPI.buildAShader(LICISource); model.LICNShaderProgram = publicAPI.buildAShader(vtkLineIntegralConvolution2D_LICN); model.CEProgram = publicAPI.buildAShader(vtkLineIntegralConvolution2D_CE); model.EEProgram = publicAPI.buildAShader(vtkLineIntegralConvolution2D_EE); model.AAHProgram = publicAPI.buildAShader(vtkLineIntegralConvolution2D_AAH); model.AAVProgram = publicAPI.buildAShader(vtkLineIntegralConvolution2D_AAV); }; // factorized out frequent patterns function setLICUniforms(program, bufs) { program.setUniformi('texLIC', bufs.getLICTextureUnit()); program.setUniformi('texSeedPts', bufs.getSeedTextureUnit()); } function renderPingPong(bufs, size, program) { bufs.attachLICBuffers(); bufs.renderQuad(size, program); bufs.detachLICBuffers(); bufs.swap(); } publicAPI.executeLIC = (size, vectorTexture, maskVectorTexture, noiseTexture, openGLRenderWindow, options) => { model._openGLRenderWindow = openGLRenderWindow; model.context = openGLRenderWindow.getContext(); Object.assign(model, options); if (size[0] <= 0.0 || size[1] <= 0.0) { return null; } const tcScale = [1.0 / size[0], 1.0 / size[1]]; let stepSize = model.stepSize * Math.sqrt(tcScale[0] * tcScale[0] + tcScale[1] * tcScale[1]); if (stepSize <= 0) { stepSize = 1.0e-10; } const gl = model.context; let fb = model.framebuffer; const fbSize = fb?.getSize(); if (!fb || !fbSize || size[0] !== fbSize || size[1] !== fbSize) { fb = vtkOpenGLFramebuffer.newInstance(); fb.setOpenGLRenderWindow(model._openGLRenderWindow); fb.saveCurrentBindingsAndBuffers(); fb.create(...size); fb.populateFramebuffer(); fb.restorePreviousBindingsAndBuffers(); model.framebuffer = fb; } fb.saveCurrentBindingsAndBuffers(); fb.bind(); gl.viewport(0, 0, ...size); gl.scissor(0, 0, ...size); if (model.shadersNeedBuild) { publicAPI.buildShaders(); model.shadersNeedBuild = false; } if (!model.bufs) { model.bufs = vtkLICPingPongBufferManager.newInstance({ openGLRenderWindow, doEEPass: model.enhancedLIC, doVTPass: model.transformVectors, vectorTexture, maskVectorTexture, noiseTexture, framebuffer: fb, size }); } else { model.bufs.setVectorTexture(vectorTexture); model.bufs.setMaskVectorTexture(maskVectorTexture); model.bufs.setNoiseTexture(noiseTexture); } const noiseBoundsPt1 = [(noiseTexture.getWidth() + 1) / size[0], (noiseTexture.getHeight() + 1) / size[1]]; const dx = 1.0 / size[0]; const dy = 1.0 / size[1]; const shaderCache = model._openGLRenderWindow.getShaderCache(); if (model.transformVectors) { const VTShaderProgram = model.VTProgram; shaderCache.readyShaderProgram(VTShaderProgram); model.bufs.attachImageVectorBuffer(); VTShaderProgram.setUniform2f('uTexSize', ...size); VTShaderProgram.setUniformi('texVectors', model.bufs.getVectorTextureUnit()); gl.clearColor(0.0, 0.0, 0.0, 0.0); gl.clear(gl.COLOR_BUFFER_BIT); model.bufs.renderQuad(size, VTShaderProgram); model.bufs.detachImageVectorBuffer(); } // first pass // initialize convolution and seeds model.bufs.clearBuffers(model.enhancedLIC); model.bufs.activateVectorTextures(); model.bufs.activateNoiseTexture(0); const { LIC0ShaderProgram } = model; shaderCache.readyShaderProgram(LIC0ShaderProgram); LIC0ShaderProgram.setUniformi('uStepNo', 0); LIC0ShaderProgram.setUniformi('uPassNo', 0); LIC0ShaderProgram.setUniformf('uMaskThreshold', model.maskThreshold); LIC0ShaderProgram.setUniform2f('uNoiseBoundsPt1', ...noiseBoundsPt1); LIC0ShaderProgram.setUniformi('texMaskVectors', model.bufs.getMaskVectorTextureUnit()); LIC0ShaderProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); LIC0ShaderProgram.setUniformi('texNoise', model.bufs.getNoiseTextureUnit(0)); renderPingPong(model.bufs, size, LIC0ShaderProgram); // backward lic const { LICIShaderProgram } = model; shaderCache.readyShaderProgram(LICIShaderProgram); LICIShaderProgram.setUniformi('uPassNo', 0); LICIShaderProgram.setUniformf('uStepSize', -stepSize); LICIShaderProgram.setUniform2f('uNoiseBoundsPt1', ...noiseBoundsPt1); LICIShaderProgram.setUniformi('texVectors', model.bufs.getImageVectorTextureUnit()); LICIShaderProgram.setUniformi('texNoise', model.bufs.getNoiseTextureUnit(0)); for (let stepIdx = 0; stepIdx < model.numberOfSteps; ++stepIdx) { setLICUniforms(LICIShaderProgram, model.bufs); renderPingPong(model.bufs, size, LICIShaderProgram); } // initialize seeds shaderCache.readyShaderProgram(LIC0ShaderProgram); LIC0ShaderProgram.setUniformi('uStepNo', 1); setLICUniforms(LIC0ShaderProgram, model.bufs); renderPingPong(model.bufs, size, LIC0ShaderProgram); // forward LIC shaderCache.readyShaderProgram(LICIShaderProgram); LICIShaderProgram.setUniformf('uStepSize', stepSize); for (let stepIdx = 0; stepIdx < model.numberOfSteps; ++stepIdx) { setLICUniforms(LICIShaderProgram, model.bufs); renderPingPong(model.bufs, size, LICIShaderProgram); } model.bufs.deactivateNoiseTexture(0); model.bufs.deactivateVectorTextures(); // finalize LIC const { LICNShaderProgram } = model; shaderCache.readyShaderProgram(LICNShaderProgram); LICNShaderProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); renderPingPong(model.bufs, size, LICNShaderProgram); // end of first-pass lic if (model.enhancedLIC) { if (model.enhanceContrast === ContrastEnhanceMode.LIC || model.enhanceContrast === ContrastEnhanceMode.BOTH) { publicAPI.contrastEnhance(false, size); } // EE stage model.bufs.attachEEBuffer(); const { EEProgram } = model; shaderCache.readyShaderProgram(EEProgram); EEProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); EEProgram.setUniformf('uDx', dx); EEProgram.setUniformf('uDy', dy); model.bufs.renderQuad(size, EEProgram); model.bufs.detachEEBuffer(); // begin second pass LIC // clear buffers model.bufs.detachBuffers(); model.bufs.clearBuffers(false); model.bufs.activateVectorTextures(); model.bufs.activateNoiseTexture(1); // initialize convolution and seeds shaderCache.readyShaderProgram(LIC0ShaderProgram); LIC0ShaderProgram.setUniformi('uStepNo', 0); LIC0ShaderProgram.setUniformi('uPassNo', 1); setLICUniforms(LIC0ShaderProgram, model.bufs); LIC0ShaderProgram.setUniformi('texNoise', model.bufs.getNoiseTextureUnit(1)); renderPingPong(model.bufs, size, LIC0ShaderProgram); // backward LIC shaderCache.readyShaderProgram(LICIShaderProgram); LICIShaderProgram.setUniformi('uPassNo', 1); LICIShaderProgram.setUniformf('uStepSize', -stepSize); LICIShaderProgram.setUniformi('texNoise', model.bufs.getNoiseTextureUnit(1)); const nSteps = model.numberOfSteps / 2; for (let stepIdx = 0; stepIdx < nSteps; ++stepIdx) { setLICUniforms(LICIShaderProgram, model.bufs); renderPingPong(model.bufs, size, LICIShaderProgram); } // initialize seeds shaderCache.readyShaderProgram(LIC0ShaderProgram); LIC0ShaderProgram.setUniformi('uStepNo', 1); setLICUniforms(LIC0ShaderProgram, model.bufs); renderPingPong(model.bufs, size, LIC0ShaderProgram); // forward LIC shaderCache.readyShaderProgram(LICIShaderProgram); LICIShaderProgram.setUniformf('uStepSize', stepSize); for (let stepIdx = 0; stepIdx < nSteps; ++stepIdx) { setLICUniforms(LICIShaderProgram, model.bufs); renderPingPong(model.bufs, size, LICIShaderProgram); } model.bufs.deactivateNoiseTexture(1); model.bufs.deactivateVectorTextures(); // finalize LIC shaderCache.readyShaderProgram(LICNShaderProgram); LICNShaderProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); LICNShaderProgram.setUniformi('texSeedPts', model.bufs.getSeedTextureUnit()); renderPingPong(model.bufs, size, LICNShaderProgram); } if (model.antiAlias) { const AAHShaderProgram = model.AAHProgram; shaderCache.readyShaderProgram(AAHShaderProgram); AAHShaderProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); AAHShaderProgram.setUniformf('uDx', dx); const AAVShaderProgram = model.AAVProgram; shaderCache.readyShaderProgram(AAVShaderProgram); AAVShaderProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); AAVShaderProgram.setUniformf('uDy', dy); for (let i = 0; i < model.antiAlias; ++i) { // Vertical pass shaderCache.readyShaderProgram(AAHShaderProgram); setLICUniforms(AAHShaderProgram, model.bufs); renderPingPong(model.bufs, size, AAHShaderProgram); // Horizontal pass shaderCache.readyShaderProgram(AAVShaderProgram); setLICUniforms(AAVShaderProgram, model.bufs); renderPingPong(model.bufs, size, AAVShaderProgram); } } if (model.enhanceContrast === ContrastEnhanceMode.LIC || model.enhanceContrast === ContrastEnhanceMode.BOTH) { publicAPI.contrastEnhance(true, size); } model.bufs.detachBuffers(); fb.restorePreviousBindingsAndBuffers(); return model.bufs.getLastLICBuffer(); }; publicAPI.contrastEnhance = (isSecondStage, size) => { const shaderCache = model._openGLRenderWindow.getShaderCache(); let { min, max } = publicAPI.getTextureMinMax(model.bufs.getLastLICBuffer(), size, model.context, model._openGLRenderWindow); if (max <= min || max > 1.0 || min < 0) { console.error('Invalid color range: ', min, max); min = 0.0; max = 1.0; } let diff = max - min; if (isSecondStage) { min += diff * model.lowLICContrastEnhancementFactor; max -= diff * model.highLICContrastEnhancementFactor; diff = max - min; } const { CEProgram } = model; shaderCache.readyShaderProgram(CEProgram); CEProgram.setUniformi('texLIC', model.bufs.getLICTextureUnit()); CEProgram.setUniformf('uMin', min); CEProgram.setUniformf('uMaxMinDiff', diff); renderPingPong(model.bufs, size, CEProgram); }; } const DEFAULT_VALUES = { shadersNeedBuild: true, stepSize: 1, numberOfSteps: 10, enhancedLIC: true, enhanceContrast: false, lowContrastEnhancementFactor: 0, highContrastEnhancementFactor: 0, antiAlias: 0, componentIds: [0, 1], normalizeVectors: true, maskThreshold: 0.0, transformVectors: true, bufs: null, isComposite: true }; function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); macro.obj(publicAPI, model); macro.setGet(publicAPI, model, ['context', '_openGLRenderWindow', 'nuberOfSteps', 'stepSize', 'normalizeVectors', 'maskThreshold', 'enhancedLIC', 'enhanceContrast', 'lowLICContrastEnhancementFactor', 'highLICContrastEnhancementFactor', 'antiAlias', 'componentIds', 'isComposite']); macro.moveToProtected(publicAPI, model, ['openGLRenderWindow']); // Object methods vtkLineIntegralConvolution2D(publicAPI, model); } // ---------------------------------------------------------------------------- const newInstance = macro.newInstance(extend, 'vtkLineIntegralConvolution2D'); var vtkLineIntegralConvolution2D$1 = { newInstance, extend }; export { vtkLineIntegralConvolution2D$1 as default, extend, newInstance };