It is quite tricky to make an efficient function for plotting dynamic 3D surfaces, which change with time using a high-level code such as Wolfram Language.
However, graphics library provides a low-level access to a GPU, that operates with vertices and triangles. The last one is our way to go
Download original notebookTLDR
Download this notebook to see the result
How to draw fast
There is a special structure for that
GraphicsComplex[vertices_, primitives_, "VertexColors"->colors_]
where vertices
will be send directly to the GPU and can be efficiently updates as well. The only drawback is that the number of points has to be the same
Therfore we need to define the working mesh and triangulate it
Random point distribution
Putting random numbers on 2D plane is not an optimal way, we need something more sophisticated
proc = HardcorePointProcess[50, 0.9, 2]; reg = Rectangle[{-10, -10}, {10, 10}]; samples = RandomPointConfiguration[proc, reg]["Points"]; ListPlot[samples]
(*VB[*)(FrontEndRef["694ad5f2-7a1b-47b9-9e11-ce34dd596715"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm1maJKaYphnpmicaJumamCdZ6lqmGhrqJqcam6SkmFqamRuaAgCFPRWB"*)(*]VB*)
here is models a set of hard-core particles, which has a certain potential that keeps them apart.
Triangulation
We will use Delaunay method for that with some post-processing
Needs["ComputationalGeometry`"]; triangles2[points_] := Module[{tr, triples}, tr = DelaunayTriangulation[points]; triples = Flatten[Function[{v, list}, Switch[Length[list], (* account for nodes with connectivity 2 or less *) 1, {}, 2, {Flatten[{v, list}]}, _, {v, ##} & @@@ Partition[list, 2, 1, {1, 1}] ] ] @@@ tr, 1]; Cases[GatherBy[triples, Sort], a_ /; Length[a] == 3 :> a[[1]]]]
now apply it to our data
triangles = triangles2[samples]; ListPlot[samples, Prolog->{EdgeForm[Black], Yellow, Opacity[0.5], GraphicsComplex[samples, Polygon[triangles]]}, PlotStyle->{Red, PointSize[0.03]}]
(*VB[*)(FrontEndRef["4def7292-250f-4955-9beb-482692c0d671"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKm6SkppkbWRrpGpkapOmaWJqa6lompSbpmlgYmVkaJRukmJkbAgB+uRUk"*)(*]VB*)
For the demonstration purpose we kept the number of point very low. However, it can be increased drastically.
Sampling our function in 3D
Let us have an example function - a Gaussian distribution. We can sample it using our mesh and then plot it using polygons
(* sample function *) f[p_, {x_,y_,z_}] := z Exp[-(*FB[*)(((*SpB[*)Power[Norm[p - {x,y}](*|*),(*|*)2](*]SpB*))(*,*)/(*,*)(2.))(*]FB*)] probe = {#[[1]], #[[2]], f[#, {10, 0, 0}]} &/@ samples // Chop; colors = With[{mm = MinMax[probe[[All,3]]]}, (Blend[{{mm[[1]], Blue}, {mm[[2]], Red}}, #[[3]]] )&/@ probe /. {RGBColor -> List} // Chop];
Make a static plot
Graphics3D[{ GraphicsComplex[probe, {Polygon[triangles]}, "VertexColors"->colors] }]
(*VB[*)(FrontEndRef["18250678-2582-4554-b34a-6986be55867e"])(*,*)(*"1:eJxTTMoPSmNkYGAoZgESHvk5KRCeEJBwK8rPK3HNS3GtSE0uLUlMykkNVgEKG1oYmRqYmVvoGplaGOmamJqa6CYZmyTqmllamCWlmppamJmnAgBlMhRt"*)(*]VB*)
Dynamic plot
Now we need only to add Offload
wrappers in order to make our data alive as well as some elements of control to manipulate the data
evaluate initialization cells, before running this one
Graphics3D[{ GraphicsComplex[probe // Offload, {Polygon[triangles]}, "VertexColors"->Offload[colors, "Static"->True]], EventHandler[Sphere[{0,0,0}, 0.1], {"transform"->Function[data, With[{pos = data["position"]}, probe = {#[[1]], #[[2]], f[#, pos]} &/@ samples // Chop; colors = With[{mm = MinMax[probe[[All,3]]]}, (Blend[{{mm[[1]], Blue}, {mm[[2]], Red}}, #[[3]]] )&/@ probe /. {RGBColor -> List} // Chop]; ]]}] }]
Or variation with a light-source
light = {0,0,0}; Graphics3D[{ GraphicsComplex[probe // Offload, {Polygon[triangles]}], PointLight[Red, light // Offload], EventHandler[Sphere[{0,0,0}, 0.1], {"transform"->Function[data, With[{pos = data["position"]}, probe = {#[[1]], #[[2]], f[#, pos]} &/@ samples // Chop; light = pos; ]]}] }]