将圆形形状放入透视的实用方法?

时间:2011-02-04 16:41:21

标签: math 3d perspective

不完全确定这是否应该属于Stack Overflow,但是这里有。

我正在使用HTML5 <canvas>对象在3D中创建一个简单的tic-tac-toe版本。第一个玩家使用十字符号,而第二个玩家使用圆圈符号。关键是我不确定如何将圆形形状放入透视中。

目前,我正在使用的方法是创建一个具有尽可能多的角度的正多边形(尽管在某种程度上),通过在这些点之间绘制直线来伪造圆形。我用正弦/余弦计算的这些点(角度)的坐标。

使用6个角度:

enter image description here

使用50个角度(看起来像一个圆圈):

enter image description here

这很好用,但要很好地伪造一个圆圈需要很多点。而且,如果我要创造一个球,我会遇到更多麻烦。例如,维基百科上的图片显示,即使有很多分数,它仍然会有一个相当“块状”的表面:http://en.wikipedia.org/wiki/File:Sphere_wireframe.svg

我想知道是否有任何方法可以更有效地放置圆形透视,也许没有点,以便能够以更实际的方式创建逼真的圆形形状。

提前感谢任何建议。

3 个答案:

答案 0 :(得分:2)

圆形的多边形近似是实用的方法。计算坐标并将透视变换应用于它们非常简单。你应该坚持这个解决方案。

那就是说,你正在考虑的兔子洞非常酷,如果你是数学的东西。事实证明,所有二次曲面 - 包括球体和椭圆 - 都可以用4x4矩阵表示。不仅如此,一旦转换为4x4,您可以应用所有标准的4x4转换矩阵(它不仅仅是乘法)。 IIRC你甚至可以对它们应用透视变换,结果仍然是二次曲面。现在,这对3D世界中的2D形状没有多大帮助。但是,由于圆是圆柱体和平面的交点,并且两者都可以变换,因此应该可以解决问题。

Here is a link describing the representation and transformations of quadrics

如图所示,圆形在地面上的透视投影通常是屏幕空间中的旋转椭圆。我没有变换方法,但我相信一个存在并且比你现在所拥有的更复杂。

答案 1 :(得分:1)

从透视角度看,圆是椭圆形。 Here's an explanation

答案 2 :(得分:1)

我花了几个小时,但我已经完成了所有方程式和SVG代码演示: http://jsfiddle.net/6b8oLhz0/9/ 我已经使用http://mathworld.wolfram.com/Ellipse.html来计算轴的中心,半径和旋转,仅给出它的等式。 代码中最有趣的部分可能就是:

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);
}