HTML5 Canvas - 2d 线框球体

Age*_*oon 1 html canvas html5-canvas

我正在寻找在 2D Canvas 中绘制 3D 线框球体。无论如何,我不是数学忍者,所以我想知道是否有人知道一种简单的方法来使用线到弧连接在 Canvas 中绘制一个并使用 :math: 进行绘制:

我将不胜感激任何帮助。

像这样的东西:http://en.wikipedia.org/wiki/File: Sphere_wireframe_10deg_6r.svg

我希望这是一个简单的方程式,但如果您知道它不是(即绘制需要大量代码),我会很高兴知道这一点,并且我可能需要重新考虑我想做什么。

flu*_*upe 5

这是一个旧线程,但我有同样的问题,但找不到任何现有的令人满意的答案。也就是说,“使用 WebGL”或“使用 Three.js”以外的答案。你瞧,我带来了好消息:实际上可以仅使用 Canvas2D 的ellipse函数来渲染这样的球体,这给了我们:

  • 一个简单的实现(大约 130 行,包括所有内容)
  • 无需计算顶点和边
  • 性感光滑边缘

在此输入图像描述

您可以在 JSBin 上找到一个演示,供后代使用,其中有很多选项。

关键是要注意,我们尝试绘制的“线框”仅由圆圈组成,并且在 3d 空间中旋转的每个圆圈都会以椭圆形投影到相机上。那么问题来了:如何求旋转圆的投影对应的椭圆呢?

由于我们只对球体表面上的圆感兴趣,因此我们可以通过平面和(单位)球体的(交)截面来表征它们中的每一个。因此,每个圆都可以用一个法向量和一个偏移量来描述-1 < o < 1

那么计算并绘制圆投影得到的椭圆就不是太困难了:

function draw_section(n, o = 0) {
  let {x, y, z} = project(_p, n) // project normal on camera
  let a  = atan2(y, x)           // angle of projected normal -> angle of ellipse
  let ry = sqrt(1 - o * o)       // radius of section -> y-radius of ellipse
  let rx = ry * abs(z)           // x-radius of ellipse
  let W  = sqrt(x * x + y * y)
  let sa = acos(clamp(-1, 1, o * (1 / W - W) / rx || 0)) // ellipse start angle
  let sb = z > 0 ? 2 * PI - sa : - sa                    // ellipse end angle

  ctx.beginPath()
  ctx.ellipse(x * o * RADIUS, y * o * RADIUS, rx * RADIUS, ry * RADIUS, a, sa, sb, z <= 0)
  ctx.stroke()
}
Run Code Online (Sandbox Code Playgroud)

示例映像中的磁盘可以通过以下方式获取:

  • 绕 z 轴旋转平面
  • 沿 z 轴移动平面

function draw_arcs() {
  for (let i = 10; i--;) {
    let a = i / 10 * Math.PI
    draw_section(vec.set(_n, cos(a), sin(a), 0))
  }

  for (let i = 9; i--;) {
    let a = (i + 1) / 10 * Math.PI
    draw_section(Z, cos(a))
  }
}
Run Code Online (Sandbox Code Playgroud)

这种方法的一个很好的好处是,您可以对所有轴执行“沿 Z 轴移动平面”,从而产生一个可爱的线框,如果手动计算顶点和边,则很难重现:

在此输入图像描述

唯一的变化如下:

function draw_arcs() {
  for (let i = 9; i--;) {
    let a = (i + 1) / 10 * Math.PI
    draw_section(Z, cos(a))
    draw_section(X, cos(a))
    draw_section(Y, cos(a))
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的函数draw_section经过精心设计,因此它只绘制给定部分的面向相机的弧线,这意味着我们可以免费进行遮挡剔除

在此输入图像描述

(我用不同颜色渲染球体背面的肮脏技巧是draw_arcs翻转画布后再次运行)

也可以使用 2 个径向渐变来产生一些假深度阴影,如图像中所示:

在此输入图像描述

遗憾的是,浏览器在绘制渐变路径时似乎遇到了很多困难。


我现在看到的缺点有两个:

  • 性能可能因浏览器而异。可能可以进行一些优化,例如将连续的调用合并.stroke()为一个。ellipse坦白说,有时使用速度似乎很慢,这让我感到非常惊讶。
  • 这是一种平行投影而不是透视投影。如果我们添加透视,投影圆仍然会显示为椭圆,但椭圆的计算会稍微复杂一些。我还没有做到,我希望它是可能的,如果我成功的话可能会更新我的答案。