How to create a 3D graphing calculator in Unity?

This isn’t a question that is particularly related to Unity, but a discussion on how to create a 3D graphing calculator. I have been trying to tackle this problem for a few days, and it seems the algorithms are beyond me.

I managed to simplify and evaluate algebraic equations from strings using other peoples libraries.
My method takes about 2 minutes to get all the data points for the mesh. Not all points are calculated by the library, and some points shouldn’t exist. I rounded the data points to the interval they were parsed through, and added these to a dictionary so that next when I used marching cubes algorithm, I could quickly check if a z value existed at the corner. (umm IDK if I lost values because of floating point precision, I did round the keys down, but I also drew out gizmos which visually matched the mesh.)

I managed to get the shape, with holes in it, and I am not satisfied with this one bit. The shape is larger on the negative side than on the positive side. Nonetheless, this got a fairly close shape to what I wanted and wasn’t complete gibberish.

So, can someone help me break down the problem so that I can quickly generate a seamless mesh, similar to how commercial 3D graphing calculators do it?

I like to post videos of stuff on my youtube channel with WARNING random music :slight_smile: , anyway here is what I managed …and it is not pretty.

1 Like

Thanks for that. Fortunately I had my headphones off or I would have been deafened.

This is the kind of problem that if you have to ask, you’d better off finding some library to do it for you. It is just a lot of work.

Broadly:

You can start by learning how to generate a plane mesh, given a domain and resolution, i.e. z=0 . There’s plenty of tutorials on this.

Then generating a surface for a function is just the same thing, but with z = f(x,y)

Sometime there are multiple solutions for z, you’ll have to generate a separate surface for each.

Then you’ll probably want to adaptively sample the mesh instead of just sampling on an uniform grid. This is a massive can of worm. The simplest is probably to split each quads into 4 equal quads until the error is acceptable.

If your function are analytically differentiable, you can also calculate the derivative and thus surface normal to get a smoother looking mesh.


This is just the broad stroke, you’ll have to solve many problems that you don’t sound like you’re ready to solve: dealing with discontinuites, singularities, aliasing, numerical precision and stability. That’s just what I can immediately think of.

2 Likes

I went ahead and made it with plane meshes, thanks for suggesting this idea. I initially did not think it will work, but couldn’t think of any other way. Here is what it looks like https://www.youtube.com/watch?v=Orfc-HsCI_s
I am still wondering about better and quicker ways. The code to generate a single side plane mesh is here Front Plane (only) Mesh Generation for 3D Graphing calculator in Unity · GitHub

You seems to be generating 6 surfaces. You should just generates 1 for each branch of the solution. For example, -x^2 +y^2 +z^2=0 have 2 solutions for z, so only 2 planes.

You’re probably doing it because this particular equation have no discontinuities when sampled on the yz plane instead of xy. As I said before, handling discontinuities is one of the many challenges you’ll have to overcome. Changing the sampling plane only hide the problem.

The artifact in the middle is due to the singularity there, which is also 1 of the challenges I’ve mentioned.

Sampling is the general method, which have its drawbacks. Commercial graphing calculators most certainly detect common shapes, such as the cone in this example, and have dedicated code path for each of them. But that is infinitely more works.

This is not the kind of problem you can have solved by asking on general forum. You’ll have to dig through code of existing opensource solutions and read a lot of research papers on the topic.

So, I didn’t quite understand what you meant by

You should just generates 1 for each branch of the solution. For example,
−x2+y2+z2=0
have 2 solutions for z, so only 2 planes.

So instead of generating 6 planes, I should at most just generate 2, and “squish” the planes like a sandwich (I hope you get the mental picture) instead of having 6 planes, like a cube and “squishing” them?

Oh… I think I understand… so you mean that if there are multiple imaginary numbers in the solution, suppose 3 imaginary zs for example, then there are 2^3 == 8 planes/branches? Then I need to set the normal for each vertex on that plane.
…hmm, no idea, I think I could try that after a while once I have an equation solving algorithm, seems quite challenging.

Blockquote

So I tried to think of ways to handle discontinuities. The first way I thought of was to raycast cast into a 3D grid, and mark all the “cubic” sections where it is within the threshold, then generate a square shaped mesh that is perpendicular to the normal (which is the gradient vector composed of partial derivatives), and scale it inside the “cube”.
If the resolution is high enough, I suppose these scaly aligned square shapes should look seamless, and this method could likely handle more edge-case equations that the plane mesh wont.

Another way I thought of was to use a 2D way of handling the discontinuities. If I understand it correctly, when the angle between the derivative and the actual 2D direction is >= 90 degrees then that could mean there is a discontinuity. Therefore, when calculating Y for the “top” over XZ grid mesh, I took the normal (3D gradient vector) of the equation, then I converted it to Vector2(x,y), Vector2(y,z), then derived new perpendicular vectors from these, where either x is always positive, or z is always positive, respectively to the points. Then for each 3 point mesh triangle sequence, I sorted it by x first, and I made sure not to compare the points if for the xy grid the points had the same x (or different zs, but I actually encountered a problem which is why I didn’t include the different zs check) or for the yz, I sorted by zs, and made sure to not compare the same z, then I compared the angle between the perpendicular 2D vector and the actual 2D direction. (To avoid any confusion I sorted first before getting the gradient and perpendicular.)

For my example I used the equation

1/(x^3) + y + z = 0

Then handling over XY with my way


And ZY (but not shown combined with XY)

I could then combine both of the ways after that to cut out any triangles the other missed.

There are cases where the normal can be (0,0,0), which I haven’t handled yet, but I feel like I could used (1x,0y) for xy perpendicular, and (0y,1z) for yz perpendicular, … hold on, at (0,0,0) I should remove the vertex, or at least not draw the triangle. Or when the perpendicular x,z are 0, which I also didn’t know how to handle, but I compared the direction with the perpendicular of the other point on the triangle.

I think this will fail as I add the other plane meshes or test against different types of equations.