Question

Not entirely sure if this should belong on Stack Overflow, but here goes.

I'm creating a simple version of tic-tac-toe in 3D using the HTML5 <canvas> object. The first player uses the cross symbol, whereas the second player uses the circle symbol. The point is that I'm not sure how to put circular shapes into perspective.

Currently, the method I'm using is creating a regular polygon with as many angles as possible (to a certain extent, though) to fake a circle by drawing straight lines between those points. The coordinates of these points (angles) I have calculated using sine / cosine.

Using 6 angles:

enter image description here

Using 50 angles (looks like a circle well enough):

enter image description here

This works well, but it requires quite a lot of points to fake a circle nicely. Moreover, if I were to create a ball, I'd be in more trouble. The picture at Wikipedia, for example, shows that even with a great deal of points, it would still have a rather 'blocky' surface: http://en.wikipedia.org/wiki/File:Sphere_wireframe.svg

I was wondering if there is any way to put a circle in perspective more effectively, perhaps without points, so as to be able to create a realistic looking circular shapes in a more practical way.

Thanks in advance for any suggestions.

Was it helpful?

Solution

Your polygonal approximation of a circle is the practical way. It's very simple to compute coordinates and apply the perspective transformation to them. You should probably stick to that solution.

That said, the rabbit hole you're considering is VERY cool if you're into mathematical things. It turns out that all quadric surfaces - including spheres and ellipses - can be represented with a 4x4 matrix. Not only that, but once converted to a 4x4 you can apply all the standard 4x4 transformation matrices (it's not just a multiply). IIRC you can even apply the perspective transform to them and the result is still a quadric surface. Now that doesn't quite help you with 2D shapes in a 3d world. However, since a circle is the intersection of a cylinder and a plane and both can be transformed, there should be a solution to your problem.

Here is a link describing the representation and transformations of quadrics

As you have shown, the perspective projection of a circle on the ground is often a rotated ellipse in screen space. I do not have a transform method, but I do believe one exists and is more complex that what you've got now.

OTHER TIPS

A circle, when viewed in perspective, is an ellipse. Here's an explanation.

It took me a few hours, but I've finnally developed all the equations and SVG code demo: http://jsfiddle.net/6b8oLhz0/9/ I've used http://mathworld.wolfram.com/Ellipse.html to compute center, radii, and rotation of axis given it's equation only. The most interesting part of the code is perhaps this:

function ellipseBy3DCircle(circle){
  var r=circle.radius;
  var n=circle.normal;
  var c=circle.center;
  //Let (u,v) be a point of the Ellipse.
  //Which point of the circle it represents?
  //This 3-D point must have a form of (u*z,v*z,z) for some z,
  //bacause it lays on a ray from observer (0,0,0) through (u,v,1) on the screen.
  //A circle is an intersection of a plane with a sphere.
  //So we have two conditions for our point :
  //1) it has to belong to the plane given by the center and normal of the circle:
  //(u*z-c.x)*n.x+  (v*z-c.y)*n.y + (z-c.z)*n.z = 0
  //2) it has to belong to the sphere given by the center and radius
  //(u*z-c.x)^2  +  (v*z-c.y)^2   + (z-c.z)^2   = 0
  //The first equation alows us to express z in terms of u,v and constants:
  //z =   (c.x*n.x+c.y*n.y+c.z*n.z) / (u*n.x+v*n.y+n.z) 
  //      ^^^^^^^^^^^^ s ^^^^^^^^^    ^^^^  t(u,v) ^^^^
  var s=c.x*n.x+c.y*n.y+c.z*n.z;
  //t(u,v)=u*n.x+v*n.y+n.z
  //The second equation gives us:
  //zz(uu+vv+1)-2z(u*c.x+v*c.y+z*c.z)+c.x^2+c.y^2+c.z^2-r^2 = 0
  //                                  ^^^^^^^^ H  ^^^^^^^^^
  var H=c.x*c.x+c.y*c.y+c.z*c.z-r*r;
  //Recall however, that z has u and v in denominator which makes it hard to solve/simplify.
  //But z=s/t(u,v), so let us multiply both sides by t(u,v)^2 :
  //ss*(uu+vv+1)-2*s*t(u,v)*(u*c.x+v*c.y+c.z)+t(u,v)^2*H=0
  //ss*uu+ss*vv+ss-2*s*(u*n.x+v*n.y+n.z)*(u*c.x+v*c.y+c.z)+(u*n.x+v*n.y+n.z)*(u*n.x+v*n.y+n.z)*H=0 
  //By regrouping terms so as to match the ax^2+2bxy+cy^2+2dx+2fy+g = 0 formula, we get:
  var A=s*s+H*n.x*n.x-2*s*n.x*c.x;
  var B=H*n.x*n.y-s*n.x*c.y-s*n.y*c.x;
  var C=s*s+H*n.y*n.y-2*s*n.y*c.y;
  var D=H*n.x*n.z-s*n.x*c.z-s*n.z*c.x;
  var F=H*n.y*n.z-s*n.y*c.z-s*n.z*c.y;
  var G=s*s+H*n.z*n.z-2*s*n.z*c.z;

  return ellipseByEquation(A,B,C,D,F,G);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top