In plotting discrete 3D arrays using Makie, how can one modify the lighting and smoothness properties?
Here is an example of a thick, extruded cross geometry constructed as a binary 3D array. (Figure on left) The lighting on the horizontal slab, the vertical slab, and along the slab thickness is very different. How can this be made more consistent?
(Figure on right) There is also some surface roughness, although this is only obvious when zooming in and less of an issue.
I am plotting these binary 3D arrays with the
volume call and
isovalue = 1.0 property.
I notice that when plotting analytical
3D data, the lighting and surface roughness issues are gone. Below is an example of a Schwarz “G” surface, also plotted with the
calls, similar to the Makie gallery example here
I am open to using Plots or other packages, but I’ve not found a better solution than Makie.
The roughness is almost unavoidable - or you can just trade it in with other artifacts.
You can also try to visualize it as a mesh, which should have sharp edges:
To make the lighting smoother, you may want to change the light position (by the keyword
light = Vec3f0(1, 2, 3)) or disable shading entirely (
shading = false).
shading changes nothing in my volume plot, perhaps because it is not a mesh.
light keyword part of the
volume call or something else? I input several different values for the light vector and it produces no change.
I’ve added an example in the latest docs for direct function meshing. https://juliageometry.github.io/Meshing.jl/dev/examples/#Functions-1
If you want smooth normals you may want to use
MarchingTetrahedra() rather than
MarchingCubes(). I haven’t tested Meshing on Boolean arrays, so I am not sure what the expected behavior should be, but it should still work.
The issue with visualizing a mesh is missing ends or “isocaps”.
This results from meshing a binary array (0’s and 1’s) and using
iso = 0.5 in the
Here is a hackish example which gives Isocaps:
gyroid(v) = cos(v)*sin(v)+cos(v)*sin(v)+cos(v)*sin(v)
gyroid_shell(v) = max(gyroid(v)-0.4,-gyroid(v)-0.4)
xr,yr,zr = ntuple(_->LinRange(0,pi*4,50),3)
A = [gyroid_shell((x,y,z)) for x in xr, y in yr, z in zr]
A[1,:,:] .= 1e10
A[:,1,:] .= 1e10
A[:,:,1] .= 1e10
A[end,:,:] .= 1e10
A[:,end,:] .= 1e10
A[:,:,end] .= 1e10
gy_mesh = GLNormalMesh(A, MarchingCubes())
# view with Makie
Makie.mesh(gy_mesh, color=[norm(v) for v in gy_mesh.vertices])
Last time I tried this kind of thing I found that interpolating the underlying isosurface to generate the normals using
Interpolations.gradient worked better than relying on the normals generated by
GLNormalMesh (which are based on the intermediate mesh).
For code, see the example I posted here:
(code available at Makie.jl based visualization of 3D 1/f noise · GitHub)