UNPKG

@thewtex/vtk.js-esm

Version:

Visualization Toolkit for the Web

227 lines (202 loc) 9.13 kB
import macro from '../../macro.js'; import vtkWebGPUTexture from './Texture.js'; import vtkWebGPURenderEncoder from './RenderEncoder.js'; import vtkWebGPUShaderCache from './ShaderCache.js'; import vtkRenderPass from '../SceneGraph/RenderPass.js'; import vtkWebGPUFullScreenQuad from './FullScreenQuad.js'; var oitpFragTemplate = "\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var tcoord: vec2<i32> = vec2<i32>(i32(input.fragPos.x), i32(input.fragPos.y));\n var reveal: f32 = textureLoad(oitpAccumTexture, tcoord, 0).r;\n if (reveal == 1.0) { discard; }\n var tcolor: vec4<f32> = textureLoad(oitpColorTexture, tcoord, 0);\n var total: f32 = max(tcolor.a, 0.01);\n var computedColor: vec4<f32> = vec4<f32>(tcolor.r/total, tcolor.g/total, tcolor.b/total, 1.0 - reveal);\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n"; function vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model) { // Set our className model.classHierarchy.push('vtkWebGPUOrderIndependentTranslucentPass'); // this pass implements a forward rendering pipeline // if both volumes and opaque geometry are present // it will mix the two together by capturing a zbuffer // first publicAPI.traverse = function (renNode, viewNode) { if (model.deleted) { return; } // we just render our delegates in order model.currentParent = viewNode; var device = viewNode.getDevice(); if (!model.translucentRenderEncoder) { publicAPI.createRenderEncoder(); publicAPI.createFinalEncoder(); model.translucentColorTexture = vtkWebGPUTexture.newInstance(); model.translucentColorTexture.create(device, { width: viewNode.getCanvas().width, height: viewNode.getCanvas().height, format: 'rgba16float', /* eslint-disable no-undef */ /* eslint-disable no-bitwise */ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.SAMPLED }); var v1 = model.translucentColorTexture.createView(); v1.setName('oitpColorTexture'); model.translucentRenderEncoder.setColorTextureView(0, v1); model.translucentAccumulateTexture = vtkWebGPUTexture.newInstance(); model.translucentAccumulateTexture.create(device, { width: viewNode.getCanvas().width, height: viewNode.getCanvas().height, format: 'r16float', /* eslint-disable no-undef */ /* eslint-disable no-bitwise */ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.SAMPLED }); var v2 = model.translucentAccumulateTexture.createView(); v2.setName('oitpAccumTexture'); model.translucentRenderEncoder.setColorTextureView(1, v2); model.fullScreenQuad = vtkWebGPUFullScreenQuad.newInstance(); model.fullScreenQuad.setDevice(viewNode.getDevice()); model.fullScreenQuad.setPipelineHash('oitpfsq'); model.fullScreenQuad.setTextureViews(model.translucentRenderEncoder.getColorTextureViews()); model.fullScreenQuad.setFragmentShaderTemplate(oitpFragTemplate); } else { model.translucentColorTexture.resizeToMatch(model.colorTextureView.getTexture()); model.translucentAccumulateTexture.resizeToMatch(model.colorTextureView.getTexture()); } model.translucentRenderEncoder.setDepthTextureView(model.depthTextureView); model.translucentRenderEncoder.attachTextureViews(); publicAPI.setCurrentOperation('translucentPass'); renNode.setRenderEncoder(model.translucentRenderEncoder); renNode.traverse(publicAPI); publicAPI.finalPass(viewNode, renNode); }; publicAPI.finalPass = function (viewNode, renNode) { model.translucentFinalEncoder.setColorTextureView(0, model.colorTextureView); model.translucentFinalEncoder.attachTextureViews(); renNode.setRenderEncoder(model.translucentFinalEncoder); model.translucentFinalEncoder.begin(viewNode.getCommandEncoder()); // set viewport renNode.scissorAndViewport(model.translucentFinalEncoder); model.fullScreenQuad.render(model.translucentFinalEncoder, viewNode.getDevice()); model.translucentFinalEncoder.end(); }; publicAPI.getTextures = function () { return [model.translucentColorTexture, model.translucentAccumulateTexture]; }; publicAPI.createRenderEncoder = function () { model.translucentRenderEncoder = vtkWebGPURenderEncoder.newInstance(); var rDesc = model.translucentRenderEncoder.getDescription(); rDesc.colorAttachments = [{ view: undefined, loadValue: [0.0, 0.0, 0.0, 0.0], storeOp: 'store' }, { view: undefined, loadValue: [1.0, 0.0, 0.0, 0.0], storeOp: 'store' }]; rDesc.depthStencilAttachment = { view: undefined, depthLoadValue: 'load', depthStoreOp: 'store', stencilLoadValue: 'load', stencilStoreOp: 'store' }; model.translucentRenderEncoder.setReplaceShaderCodeFunction(function (pipeline) { var fDesc = pipeline.getShaderDescription('fragment'); fDesc.addOutput('vec4<f32>', 'outColor'); fDesc.addOutput('f32', 'outAccum'); fDesc.addBuiltinInput('vec4<f32>', '[[builtin(position)]] fragPos'); var code = fDesc.getCode(); code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', [// very simple depth weighting in w 'var w: f32 = 1.0 - input.fragPos.z * 0.9;', 'output.outColor = vec4<f32>(computedColor.rgb*computedColor.a, computedColor.a) * w;', 'output.outAccum = computedColor.a;']).result; fDesc.setCode(code); }); model.translucentRenderEncoder.setPipelineHash('oitpr'); model.translucentRenderEncoder.setPipelineSettings({ primitive: { cullMode: 'none' }, depthStencil: { depthWriteEnabled: false, depthCompare: 'less', format: 'depth32float' }, fragment: { targets: [{ format: 'rgba16float', blend: { color: { srcFactor: 'one', dstFactor: 'one' }, alpha: { srcfactor: 'one', dstFactor: 'one' } } }, { format: 'r16float', blend: { color: { srcFactor: 'zero', dstFactor: 'one-minus-src' }, alpha: { srcfactor: 'one', dstFactor: 'one-minus-src-alpha' } } }] } }); }; publicAPI.createFinalEncoder = function () { model.translucentFinalEncoder = vtkWebGPURenderEncoder.newInstance(); model.translucentFinalEncoder.setDescription({ colorAttachments: [{ view: null, loadValue: 'load', storeOp: 'store' }] }); model.translucentFinalEncoder.setReplaceShaderCodeFunction(function (pipeline) { var fDesc = pipeline.getShaderDescription('fragment'); fDesc.addOutput('vec4<f32>', 'outColor'); fDesc.addBuiltinInput('vec4<f32>', '[[builtin(position)]] fragPos'); var code = fDesc.getCode(); code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb*computedColor.a, computedColor.a);']).result; fDesc.setCode(code); }); model.translucentFinalEncoder.setPipelineHash('oitpf'); model.translucentFinalEncoder.setPipelineSettings({ primitive: { cullMode: 'none' }, fragment: { targets: [{ format: 'bgra8unorm', blend: { color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha' }, alpha: { srcfactor: 'one', dstFactor: 'one-minus-src-alpha' } } }] } }); }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- var DEFAULT_VALUES = { colorTextureView: null, depthTextureView: null }; // ---------------------------------------------------------------------------- function extend(publicAPI, model) { var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API vtkRenderPass.extend(publicAPI, model, initialValues); macro.setGet(publicAPI, model, ['colorTextureView', 'depthTextureView']); // Object methods vtkWebGPUOrderIndependentTranslucentPass(publicAPI, model); } // ---------------------------------------------------------------------------- var newInstance = macro.newInstance(extend, 'vtkWebGPUOrderIndependentTranslucentPass'); // ---------------------------------------------------------------------------- var vtkWebGPUOrderIndepenentTranslucentPass = { newInstance: newInstance, extend: extend }; export default vtkWebGPUOrderIndepenentTranslucentPass; export { extend, newInstance };