The 3D story is the same —
GraphicsComplex
maps directly to THREE.js
BufferAttribute
objects written into GPU memory. The only difference is that you now also have
VertexNormals
for lighting.
Find Part 1 of this post here: https://community.wolfram.com/groups/-/m/t/3720274​
Original article: https://wljs.io/blog/graphicscomplex2
Let's start with a dodecahedron:
In[]:=
v=PolyhedronData["Dodecahedron","Vertices"]//N;​​i=PolyhedronData["Dodecahedron","FaceIndices"];
Wireframe:
In[]:=
GraphicsComplex[v,{Red,Line[i]}]//Graphics3D
Out[]=
Solid:
In[]:=
GraphicsComplex[v,Polygon[i]]//Graphics3D
Out[]=

Vertex colors in 3D

Same idea as in 2D — per-vertex colors get linearly interpolated by the GPU rasterizer:
In[]:=
v={{1,0,0},{1,1,1},{0,0,1}};​​Graphics3D[{Yellow,GraphicsComplex[v,Polygon[{1,2,3}],VertexColors->{Red,Blue,Green}]},ViewPoint->{1,1,1}]
Out[]=
Again, we recommend avoiding color symbols inside
VertexColors
; use triples of real numbers instead.

Normals and lighting

“You may think of normals as a sort of workaround to avoid having an infinite number of polygon faces while modeling smooth surfaces. We will touch on this more closely after this next “formal” example.”
Without explicit normals, the renderer computes flat normals from polygon faces, resulting in flat shading. Kinda like a 90s demo effect:
In[]:=
v={{1,0,0},{1,1,1},{0,0,1}};​​Graphics3D[{Yellow,GraphicsComplex[v,Polygon[{1,2,3}]]},ViewPoint->{1,1,1}]
Out[]=
Supply your own
VertexNormals
, and you control exactly how light bounces off the surface:
In[]:=
v={{1,0,0},{1,1,1},{0,0,1}};​​n={1,-1,1};​​n2={-1,0,-1};​​Graphics3D[{Yellow,GraphicsComplex[v,Polygon[{1,2,3}],VertexNormals->{n2,n,n}]},ViewPoint->{1,1,1}]
Out[]=
The idea of having normals is not quite there if you have a single flat triangle. The change of color at the bottom is due to Lambert’s law.
“Lambert’s Cosine Law states that the radiant intensity or luminous intensity observed from an ideal diffusely reflecting surface is proportional to the cosine of the angle between the observer’s line of sight and the surface normal. It is named after Johann Heinrich Lambert, from his Photometria, published in 1760.”
I(θ)∝cos(θ)
Here is an example:
This is widely used in old and modern computer graphics to model realistic diffuse surfaces, as well as so-called normal map textures used in deferred lighting.
Let's generate a sphere mesh from randomly scattered points.
In[]:=
mesh=DelaunayMesh[SpherePoints[50]];​​v=MeshCoordinates[mesh];​​i=MeshCells[mesh,2][[All,1]];
One could sample more points, but it would be too expensive. Instead, let's define normals for each vertex based on the fact that the object has to represent a sphere:
Oops! We have a problem here. Unfortunately, the indices of a triangulated mesh are often not sorted (in the best case, they should follow the CW or CCW rule), i.e. the 3D engine cannot tell whether a given polygon faces in or out. We can fix this by presorting them manually based on our normals:
Let's plot it:
Nice and smooth, with just 50 extra normals provided.

Texture mapping in 3D

UV coordinates work identically to the 2D case:
The mapping can also be done automatically:
How about animation and manipulation?

Animating 3D meshes

Fixed 10,000 vertices

Akin to the 2D case, we are free to mutate vertex data with ease. Normals can be provided as well; otherwise, the 3D engine will automatically estimate them based on the direction the polygon is facing — flat shading.
Here's a 100×100 grid — that's 10,000 vertices and ~20,000 triangles — animated in real time:
The triangle indices are computed once and stay fixed. Only the vertex positions change each frame — which means only the position buffer gets updated on the GPU:

Adding dynamic color

Same grid, but now we also update a color buffer each frame. Two GPU buffers updated per frame — positions and colors — still smooth:

Animated surface with AnimationFrameListener

Dynamic topology in 3D

Two orbiting holes punch through the mesh in real time:

Hijacking plot functions as geometry generators

Define a function that interpolates between a torus and a seashell:
Try it!

CITE THIS NOTEBOOK

Real-time GPU rendering via local JavaScript engine. Part 2​
by Kirill Vasin​
Wolfram Community, STAFF PICKS, May 22, 2026
​https://community.wolfram.com/groups/-/m/t/3720773