3D Volume Plots in Python

How to make 3D Volume Plots in Python with Plotly.


Plotly Studio: Transform any dataset into an interactive data application in minutes with AI. Try Plotly Studio now.

A volume plot with go.Volume shows several partially transparent isosurfaces for volume rendering. The API of go.Volume is close to the one of go.Isosurface. However, whereas isosurface plots show all surfaces with the same opacity, tweaking the opacityscale parameter of go.Volume results in a depth effect and better volume rendering.

Simple volume plot with go.Volume

In the three examples below, note that the default colormap is different whether isomin and isomax have the same sign or not.

In [1]:
import plotly.graph_objects as go
import numpy as np
X, Y, Z = np.mgrid[-8:8:40j, -8:8:40j, -8:8:40j]
values = np.sin(X*Y*Z) / (X*Y*Z)

fig = go.Figure(data=go.Volume(
    x=X.flatten(),
    y=Y.flatten(),
    z=Z.flatten(),
    value=values.flatten(),
    isomin=0.1,
    isomax=0.8,
    opacity=0.1, # needs to be small to see through all surfaces
    surface_count=17, # needs to be a large number for good volume rendering
    ))
fig.show()
In [2]:
import plotly.graph_objects as go
import numpy as np
X, Y, Z = np.mgrid[-1:1:30j, -1:1:30j, -1:1:30j]
values =    np.sin(np.pi*X) * np.cos(np.pi*Z) * np.sin(np.pi*Y)

fig = go.Figure(data=go.Volume(
    x=X.flatten(),
    y=Y.flatten(),
    z=Z.flatten(),
    value=values.flatten(),
    isomin=-0.1,
    isomax=0.8,
    opacity=0.1, # needs to be small to see through all surfaces
    surface_count=21, # needs to be a large number for good volume rendering
    ))
fig.show()
In [3]:
import numpy as np
import plotly.graph_objects as go

# Generate nicely looking random 3D-field
np.random.seed(0)
l = 30
X, Y, Z = np.mgrid[:l, :l, :l]
vol = np.zeros((l, l, l))
pts = (l * np.random.rand(3, 15)).astype(int)
vol[tuple(indices for indices in pts)] = 1
from scipy import ndimage
vol = ndimage.gaussian_filter(vol, 4)
vol /= vol.max()

fig = go.Figure(data=go.Volume(
    x=X.flatten(), y=Y.flatten(), z=Z.flatten(),
    value=vol.flatten(),
    isomin=0.2,
    isomax=0.7,
    opacity=0.1,
    surface_count=25,
    ))
fig.update_layout(scene_xaxis_showticklabels=False,
                  scene_yaxis_showticklabels=False,
                  scene_zaxis_showticklabels=False)
fig.show()

Defining the opacity scale of volume plots

In order to see through the volume, the different isosurfaces need to be partially transparent. This transparency is controlled by a global parameter, opacity, as well as an opacity scale mapping scalar values to opacity levels. The figure below shows that changing the opacity scale changes a lot the visualization, so that opacityscale should be chosen carefully (uniform corresponds to a uniform opacity, min/max maps the minimum/maximum value to a maximal opacity, and extremes maps both the minimum and maximum values to maximal opacity, with a dip in between).

In [4]:
import plotly.graph_objects as go
from plotly.subplots import make_subplots
fig = make_subplots(
    rows=2, cols=2,
    specs=[[{'type': 'volume'}, {'type': 'volume'}],
           [{'type': 'volume'}, {'type': 'volume'}]])

import numpy as np

X, Y, Z = np.mgrid[-8:8:30j, -8:8:30j, -8:8:30j]
values =    np.sin(X*Y*Z) / (X*Y*Z)


fig.add_trace(go.Volume(
    opacityscale="uniform",
    ), row=1, col=1)
fig.add_trace(go.Volume(
    opacityscale="extremes",
    ), row=1, col=2)
fig.add_trace(go.Volume(
    opacityscale="min",
    ), row=2, col=1)
fig.add_trace(go.Volume(
    opacityscale="max",
    ), row=2, col=2)
fig.update_traces(x=X.flatten(), y=Y.flatten(), z=Z.flatten(), value=values.flatten(),
    isomin=0.15, isomax=0.9, opacity=0.1, surface_count=15)
fig.show()