Open TBody opened 2 years ago
@TBody No worries! This seems to be a common question. I'll reiterate my answer from volumetric rendering for volumetric data #472 .
TLDR: To set the opacity for everything to a constant values, you need to make opacity
an array of 256 floats with the same value. For example, to make the opacity half (on a scale of 0 to 1 255, of course):
opacity = np.ones((256,)) * 0.5
In regards to your second question,
pyvista.add_volume
uses 256 colors on the scale bar by default (controlled by then_colors
argument). Your data has 3 color channels and an alpha channel (R, G, B, and A), but assuming each color channel is 8-bit, this means you can represent(2**8)**3 = 2**24
colors (a big gamut!). Then_colors
is made 256 (i.e.2**8
) so the lookup table (LUT) for opacity can be contained in one byte and thus provide faster lookups. Theopacity
argument is intended to be the same length asn_colors
, but you can specify fewer arguments.In essence, when you specify
[0, 1, 0]
for opacity, you are setting the opacity for the first 3 colors (out of the total 256 colors) to 0, 1, and 0, respectively. The remaining 253 colors preserve their default values. When you specify[0, 1]
, you set only the first two colors, and the remaining 254 retain their value.I believe from your code that you think you are setting the opacity of the color channels, but that is not actually how
opacity
works inpyvista.add_volume
. You can instead create an opacity array of length 256 that has varying degrees of opacity from 0 to 1. For example,opacity = np.linspace(0, 1, 256)
would make a linearly increasing opacity from 0 at the low values of your data to 1 at the high values. Instead,pyvista
already comes with a bunch of pre-made opacity functions (calledopacity transfer functions
in VTK and pyvista), so you can specify a string that maps to a pre-made opacity transfer function.The opacity transfer functions are specified in
pyvista.plotting.tools
in the functionopacity_transfer_function
. You have the following to choose from:
transfer_func = {
'linear': np.linspace(0, 255, n_colors, dtype=np.uint8),
'geom': np.geomspace(1e-6, 255, n_colors, dtype=np.uint8),
'geom_r': np.geomspace(255, 1e-6, n_colors, dtype=np.uint8),
'sigmoid': sigmoid(np.linspace(-10.,10., n_colors)),
'sigmoid_3': sigmoid(np.linspace(-3.,3., n_colors)),
'sigmoid_4': sigmoid(np.linspace(-4.,4., n_colors)),
'sigmoid_5': sigmoid(np.linspace(-5.,5., n_colors)),
'sigmoid_6': sigmoid(np.linspace(-6.,6., n_colors)),
'sigmoid_7': sigmoid(np.linspace(-7.,7., n_colors)),
'sigmoid_8': sigmoid(np.linspace(-8.,8., n_colors)),
'sigmoid_9': sigmoid(np.linspace(-9.,9., n_colors)),
'sigmoid_10': sigmoid(np.linspace(-10.,10., n_colors)),
}
For example,
sigmoid
will give you a nice continuous variation in opacity. In addition, if you would prefer to reverse the color bar so that low values have more opacity, you can add_r
to any of the strings above and it will reverse the colors for you.I would recommend using
sigmoid
as that seems to work nice most of the time. In addition, if you want to "filter out" some values from your data set, I would useclim
because I thinkNone
values won't work, though I could be wrong. For example, you could tryclim=[np.nanmin(scalars_a[scalars_a>0]), np.nanmax(scalars_a)]
.
@MatthewFlamm @akaszynski This would be another great thing to add to our documentation! The volume rendering section could use some TLC.
@TBody If this answers your question, please kindly let me know so we can close the issue. No rush!
@adamgranthendry, you're correct, we need to add that. Right now I've got three PRs in the oven. Would you mind submitting one for this? It's a great example.
@akaszynski You bet! I know I still owe you some more feedback on one of the other ones too.
@adamgranthendry Thanks! Reading more into it, I think what I was actually trying to do was volume rendering: i.e. set the cell volumes as semi-transparent, rather than cell-surface rendering.
If I set, for instance opacity=np.ones((256,))*255/2
I still don't change the volume opacity (see attached).
If I try volume=True
then I get the error "Type <class 'pyvista.core.pointset.UnstructuredGrid'> not supported for volume rendering at this time." -- so I think this is an issue regarding my choice of mesh.
P.S. I found that the opacity range has to be [0, 255] rather than [0, 1] (using pyvista 0.31.3 pyhd8ed1ab_0 conda-forge
)
@TBody Oh yes, you're right, my mistake. The docstring clearly states opacity is on the scale of [0, 255]
. And yes, pyvista
currently only supports UniformGrid
data (i.e. ImageData
in VTK), although VTK has vtkUnstructuredGridVolumeRayCastMapper
.
From what I can see though, your opacity has changed. It has increased for your lower values and decreased for your higher ones. Previously, you were using linear
, so your color bar scale was going from 0% opacity ("translucent") to 100% opacity ("opaque"). Now, with opacity set at 127.5, the intensity is 50% all throughout:
Linear:
50% Opaque:
Do you see how in the first instance, the left end of the color bar is clear and the right end is very intense, but how in the second instance the color is flat all throughout?
Maybe we can figure out how to correctly set the opacity for you so you can see what you're trying to see. What is it exactly that you would like to do?
Setting opacity based on cell-volume data
PyVista newbie, sorry! I'm trying to visualize some 3D data on an unstructured grid (a collection of connected 2D hexahedra, non-overlapping). The dataset is somewhat peculiar in that it has high resolution in 2 dimensions, but low resolution in the other dimension, and so the hexahedra have a small width and breadth, but a very large length (~1000x the other dimensions).
I'd like to set the opacity of the cell based on its value, and so I've tried
I find a similar behavior in Paraview with "Opacity for surfaces", so I believe that this is me not understanding VTK rather than an issue in PyVista. However, I'm not sure how to go about how to get the desired behavior, any pointers greatly appreciated!
Example Data
simple.vtk.zip