To draw the multichannel volumes efficiently, I pre-combine the channels into a single object using an operation named "fuse". The fuse takes the max value of r,g,b,a over all channels, for every pixel in the volume.
The Fuse operation was implemented on CPU with web workers, but can be immensely optimized by moving it to gpu.
I implement it using "Max" blending mode, so that all I have to do is draw each channel one by one and apply the RGBA lut to each intensity (in fuseShader.ts). The max blending takes care of the rest.
Because this is no longer asynchronous in a web worker, the redraw logic had to be changed. Basically any time something changes which could require a fuse, we make sure the fuse is requested and then ask for a redraw. In the regular redraw logic, the fuse-on-gpu will be added.
One key resource usage change is that now each Channel has a Texture object that holds the gpu texture containing the channel's data, and a second Texture object to hold the intensity-to-rgba lookup table.
To draw the multichannel volumes efficiently, I pre-combine the channels into a single object using an operation named "fuse". The fuse takes the max value of r,g,b,a over all channels, for every pixel in the volume.
The Fuse operation was implemented on CPU with web workers, but can be immensely optimized by moving it to gpu.
I implement it using "Max" blending mode, so that all I have to do is draw each channel one by one and apply the RGBA lut to each intensity (in fuseShader.ts). The max blending takes care of the rest.
Because this is no longer asynchronous in a web worker, the redraw logic had to be changed. Basically any time something changes which could require a fuse, we make sure the fuse is requested and then ask for a redraw. In the regular redraw logic, the fuse-on-gpu will be added.
One key resource usage change is that now each Channel has a Texture object that holds the gpu texture containing the channel's data, and a second Texture object to hold the intensity-to-rgba lookup table.