How can I optimize the volume rendering code based on the webGL: max_texture_size of the web-browser so that it can render image in any web browser?

Hello everyone,
I am trying to render a volumetric (volume dimensions: [5861, 3429, 27]) image into desktop Google Chrome using the volume rendering method (vtkVolumeMapper), but I am encountering the following webGL error:

WebGL: INVALID_VALUE: texImage3D: width, height or depth out of range
e.create3DFromRaw @ vtk.js:2
e.create3DFilterableFromRaw @ vtk.js:2
e.buildBufferObjects @ vtk.js:2
e.updateBufferObjects @ vtk.js:2
e.renderPieceStart @ vtk.js:2
e.renderPiece @ vtk.js:2
e.volumePass @ vtk.js:2
e.apply @ vtk.js:2
e.traverse @ vtk.js:2
GK.e.traverseVolumePass @ vtk.js:2
e.traverse @ vtk.js:2
e.traverse @ vtk.js:2
KL.e.traverse @ vtk.js:2
e.traverseAllPasses @ vtk.js:2
c @ vtk.js:2
Vm.e.render @ vtk.js:2
Vm.e.initialize @ vtk.js:2
volume_rendering @ index_render_options.js:495
(anonymous) @ index_render_options.js:1572
Promise.then (async)
vol_processFile @ index_render_options.js:1564
(anonymous) @ (index):1158
dispatch @ jquery-3.6.0.min.js:2
v.handle @ jquery-3.6.0.min.js:2

I looked into the problem to understand more, and I found that the texture I’m trying to render is larger than the GPU can handle, which is why I’m getting this error.
Therefore, I checked the webGL properties of the desktop chrome and found the below info:

Textures:
Max Texture Size:	16384
Max Cube Map Texture Size:	16384
Max Combined Texture Image Units:	64
Max Anisotropy:	16
Max 3D Texture Size:	2048
Max Array Texture Layers:	2048
Max Texture LOD Bias:	15

When I try to render same image in my laptop with exactly same code on chrome which has below max texture support it render without a problem:

Textures:
Max Texture Size:	32768
Max Cube Map Texture Size:	32768
Max Combined Texture Image Units:	64
Max Anisotropy:	16
Max 3D Texture Size:	16384
Max Array Texture Layers:	2048
Max Texture LOD Bias:	15

I noticed that the texture size restriction was the cause of the error I was experiencing in desktop Chrome. I now want to take some action to optimize the volume rendering code and render it on both devices without running into texture limitation issues. How can I achieve it?

Any suggestion would be appreciated.

I am getting this error when I run volume rendering code, but I don´t get these errors for isosurface plot.
Thank you!

You can consider downsampling your volume. You would get a more manageable data size at the cost of dataset resolution. I’m not sure what you can do to try to optimize the volume rendering code, so downsampling would be my first approach.

Hi,
Thanks for the reply.
I tried to downsample the volume by applying vtkImageReslice but it does not seems to work:

           down_sample = vtk.Imaging.Core.vtkImageReslice.newInstance();
            Interpolator = vtk.Imaging.Core.vtkImageInterpolator.newInstance();
            down_sample.setInputData(volume_imageData[i]);
            const ss = 0.5;
            down_sample.setOutputSpacing(ss, ss, ss);
            const imageResampled = down_sample.getOutputData();
            console.log('imageResampled', imageResampled)

I am getting error in imageResampled as: Uncaught (in promise) TypeError: Cannot read properties of null (reading ‘0’)

Can you provide me an example of performing down-sample? And also can you explain why I am getting GPU max_3D_texture_size error in volume rendering but not in surface rendering?
Thank you.

Can you provide the full stacktrace of your error?

The entire volume gets loaded into a texture, whereas surface rendering is geometry. Hence the memory differences.

Hi,
Here you go.

for (i = 0; i < volume_imageData.length; i++){

            volume_vtk = vtk.Rendering.Core.vtkVolume.newInstance();
            volumeMapper = vtk.Rendering.Core.vtkVolumeMapper.newInstance();
            volumeProperty = vtk.Rendering.Core.vtkVolumeProperty.newInstance();
            ofun = vtk.Common.DataModel.vtkPiecewiseFunction.newInstance();
            ctfun = vtk.Rendering.Core.vtkColorTransferFunction.newInstance();

            // Load the Input
            dataRange = volume_imageData[i].getPointData().getScalars().getRange();
            dimensions = volume_imageData[i].getDimensions()
            console.log('volume dimensions', dimensions)
            const spacing = volume_imageData[i].getSpacing()
            console.log('volume spacing', spacing)

            ///////////
            down_sample = vtk.Imaging.Core.vtkImageReslice.newInstance();
            //Interpolator = vtk.Imaging.Core.vtkImageInterpolator.newInstance();
            down_sample.setInputData(volume_imageData[i]);

            const ss = 0.5;
            down_sample.setOutputSpacing(ss, ss, ss);
            // down_sample.setInterpolationModeToCubic();
           imageResampled = down_sample.getOutputData();
           console.log('resampp', imageResampled)
            //console.log(down_sample.getOutputExtent())
            // console.log('down_sample', down_sample)
            /////////////////

            // Calculating Pixel range
            ii_0 = parseInt(dataRange[0])
            ii_mid = parseInt(dataRange[1] / 2)
            ii_1 = parseInt(dataRange[1])

            // Define PieceWise transfer function
            ofun.removeAllPoints();
            ofun.addPoint(ii_0, 0.0);
            // ofun.addPoint((ii_0[0] + ii_1[1]) * 0.5, 0.5);
            console.log(ofun);
            ofun.addPoint(ii_1, 1.0);

            // #####################################################################
            min = ii_0;
            console.log(min);
            max = ii_1;
            console.log(max);
            temp = 0

            for (t = min; t <= max; t++){
                    newVal = parseInt(((t-min)/(max-min)) * 255);
                    if (newVal != temp) {
                        // console.log(t);
                        rgb = searchvalue(newVal, mycolor);
                         //print(i, r, g, b)
                        // console.log(rgb[0]);
                        ctfun.addRGBPoint(t, rgb[0], rgb[1], rgb[2])
                        // console.log(temp);
                        temp = temp + 1
                    }
            }

            // Define Volume Property
            volumeProperty.setRGBTransferFunction(0, ctfun);
            volumeProperty.setScalarOpacity(0, ofun);
            volumeProperty.setShade(true); // Increase the lights
            volumeProperty.setInterpolationTypeToLinear();
            volumeProperty.setAmbient(0.7);
            volumeProperty.setDiffuse(0.7);
            volumeProperty.setSpecular(0.3);
            volumeProperty.setSpecularPower(30.0); // setSpecularPower(1.0, 1.0, 1.0);
            volumeProperty.setOpacityMode(0, 20);
            volumeProperty.setIndependentComponents(true);
            volumeProperty.setUseGradientOpacity(0, true); // Helps to make image translucent
            volumeProperty.setGradientOpacityMinimumValue(0, 3.5);
            volumeProperty.setGradientOpacityMinimumOpacity(0, 0.0);
            volumeProperty.setGradientOpacityMaximumValue(0, 20);
            volumeProperty.setGradientOpacityMaximumOpacity(0, 1.0);

            volumeMapper.setInputData(volume_imageData[i]);
            // volumeMapper.setSampleDistance(0.4);
            volumeMapper.setMaximumSamplesPerRay(4000)

            // Define vtkVolume
            volume_vtk.setMapper(volumeMapper);
            volume_vtk.setProperty(volumeProperty);

            renderer.addActor(volume_vtk);

            volume_opacity_val[i] = volumeProperty
            volume_sample_Distance[i] = volumeMapper
            volume_visibility_control[i] = volume_vtk
            console.log(volume_visibility_control[i])
      }

Error:
It gives me below error on imageResampled = down_sample.getOutputData() :
Uncaught (in promise) TypeError: Cannot read properties of null (reading ‘0’)

I also would like to ask you one more question regarding a addRGBPoint transfer function. Currently, I am passing the color points from the matplotlib library to addRGBPoint and it works but it does have all color which i want and the color are also not so dynamic. So do you know any other library which i can use to pass color points into it as i know volume render plot depends on the pixel intensity not a solid like in isosurface. Before I tried to pass VTK color but i didn’t get succeed and I also didn’t find any example.