Mathieu, Cory, Venke,
Thank you all for your suggestions, they have helped me increase my understanding of how to handle a volume-of-colors in ParaView.
However, it seems that ParaView actually has some issues with this type of data – and there are still some unknowns about the data formats. I know enough now that I think I can work-around any of ParaView’s issues (though perhaps not easily in the case of the final bullet), but 1) for future readers of this thread; and 2) to hopefully improve ParaView down the road; I’ll convey a bit of the issues I encountered – starting with the data file formats.
Regarding the “COLOR_SCALARS” feature of legacy .vtk files, there doesn’t seem to be any real equivalent in the XML .vti format, other than you can just have some tuples/vectors in your data. And when loading a legacy VTK with “COLOR_SCALARS” it treats it just as a regular vector, for which ParaView assumes the axes are “X”, “Y” and “Z” if there are just 3 components, and numbers “0” through “3” if there are four components. The naming isn’t a big deal, though might confuse some new users. (Maybe there’s a way to assign axis names in the .vtk format?)
Speaking of the .vti format: having a DataArray with 4 components seems to be handled just fine – except the standard “pyevtk” Python package doesn’t like 4-component data. (There’s another Python package for writing VTK files that I came across, but I can’t find my reference to that anymore, so can’t test that.) Also, I presume there’s a way to write the files directly with “vtkpython” or “pvpython”, but it was easier for me (and thus less time consuming) to just alter the “pyevtk” Python package to be able to handle 4-tuples.
All that said, I created a sample datafile of some shapes using Numpy and then wrote them out as both “RGB” and “RGBA”, plus a scalar “intensity” value into the file. Attached is the code to generate that, though the “RGBA” data requires my modified “pyetvk”, so I have a link to the file that is generated.
And regarding the visualization: both the RGB and RGBA data fields work with the “Surface” rendering representation in ParaView. And in the one case where one wants to render the object with the exact alpha values contained in the RGBA data, then that renders fine in “Volume” representation.
However, there are other visualization choices that one may want to make. And so here are the issues that make those choices difficult/impossible:
-
When coloring by the “rgba” value in “Surface” representation, if you turn off “Map Scalars”, nothing appears. If you switch to “rgb” and back to “rgba” then you can see the semi-transparent surfaces – though “rgba” with surface rendering isn’t particularly interesting.
-
If you have the coloring set to “rgb”, and switch to “Volume” rendering representation, then you get the following error from ParaView:
( 291.369s) [paraview ]vtkGPUVolumeRayCastMapp:435 ERR| vtkOpenGLGPUVolumeRayCastMapper (0x153e5770): If IndependentComponents is Off in the volume property, then the data must have either 2 or 4 component scalars. The input data has 3 component(s).
So there is some sense in that, given that the 2-tuple and 4-tuple data fields presumably have an alpha/transparency channel. However, ParaView offers the possibility of using a “Separate Opacity Array”, so probably better than the warning would be to use channel-0 (aka “X”) as the opacity when given a 3-tuple vector field. Because it is actually possible to then enable separate opacity with a 3-tuple field, and set it to another scalar in the dataset (e.g. “intensity”), or one of the vector components. However! (see next bullet)
-
A big problem is that when a 3-tuple/vector datafield (such as RGB) is volume rendered with a separate opacity – it does not seem possible to then color the data based on the RGB values! This seems like it should be doable.
-
Another problem is that when a 4-tuple/vector datafield (such as RGBA) is volume rendered, then attempting to “Use Separate Opacity Array” causes the data to just disappear. Again, I’d like to be able to keep the RGB colors, but affect the opacity based on different data fields.
Here’s the code to generate some data (in .vti format) for testing:
#####
## Generate an example VTK Image data file containing some basic shapes
#
# Based on code from: https://stackoverflow.com/questions/10031580/how-to-write-simple-geometric-shapes-into-numpy-arrays
#
from pyevtk.hl import imageToVTK
import numpy as np
np.random.seed(seed=1)
# All subsequent size values (except splatsize) are based on the "xysize"
# (original value was: xysize = 200)
xysize = 80
zsize = xysize / 4
circle_center = xysize * xysize * 0.16
hwidth = xysize * 4
# xx and yy are xysize X xysize tables containing the x and y coordinates as values
# mgrid is a mesh creation helper
xx, yy = np.mgrid[:xysize, :xysize]
#######
## Generate a cylinder (red)
# circles contains the squared distance to the X-Y midpoint
# we are just using the circle equation learnt at school
circle = (xx - xysize/2) ** 2 + (yy - xysize/2) ** 2
# donuts contains 1's and 0's organized in a donut shape
# you apply 2 thresholds on circle to define the shape
#donut = np.logical_and(circle < (6400 + 60), circle > (6400 - 60))
donut = (circle < (circle_center + hwidth)) & (circle > (circle_center - hwidth))
#print("circle_center = " + str(circle_center))
donut3D = np.repeat(donut[:,:,np.newaxis], zsize, axis=2)
red = donut3D * 0.8
#imageToVTK("donut", pointData={"donut":red})
#######
## Generate a square rod through the center (green)
x2, y2, z2 = np.mgrid[:xysize, :xysize, :zsize]
green = ((x2 > xysize*0.4) & (x2 < xysize*0.6) & (z2 > zsize*0.3) & (z2 < zsize*0.7)) * 0.5
#imageToVTK("csg", pointData={"donut":red, "rod":green})
#######
## Generate some random splats (blue)
splatsize = 4
blue = np.zeros(green.shape, dtype=green.dtype)
for count in range(30):
x3 = np.random.randint(splatsize+1, high=xysize-splatsize-1)
y3 = np.random.randint(splatsize+1, high=xysize-splatsize-1)
z3 = np.random.randint(splatsize+1, high=zsize-splatsize-1)
for i in range(-splatsize, splatsize):
for j in range(-splatsize, splatsize):
for k in range(-splatsize, splatsize):
distish = abs(i) + abs(j) + abs(k)
blue[x3+i, y3+j, z3+k] = ((splatsize*3.0) - distish) / (splatsize*3.0)
intensity = (red + green + blue) / 3.0
shapesRGB = (red, green, blue)
shapesRGBA = (red, green, blue, intensity)
imageToVTK("sampleRGB-" + str(xysize), pointData={"rgb":shapesRGB, "intensity":intensity})
#imageToVTK("sampleRGB-" + str(xysize), pointData={"rgb":shapesRGB, "rgba":shapesRGBA, "intensity":intensity})
And here’s a link to some pre-generated data which includes both the “rgb” and “rgba” fields (plus a scalar “intensity” field): Download: https://freevr.org/Downloads/sampleRGB-80.vti