c# - 基本渲染 3D 透视投影到带摄像头的 2D 屏幕(不带 opengl)

标签 c# java math 3d projection

假设我有一个如下所示的数据结构:

Camera {
   double x, y, z

   /** ideally the camera angle is positioned to aim at the 0,0,0 point */
   double angleX, angleY, angleZ;
}

SomePointIn3DSpace {
   double x, y, z
}

ScreenData {
   /** Convert from some point 3d space to 2d space, end up with x, y */
   int x_screenPositionOfPt, y_screenPositionOfPt

   double zFar = 100;

   int width=640, height=480
}

...

如果没有屏幕剪辑或其他任何东西,我将如何计算给定空间中某个 3d 点的某个点的屏幕 x,y 位置。我想将该 3d 点投影到 2d 屏幕上。
Camera.x = 0
Camera.y = 10;
Camera.z = -10;


/** ideally, I want the camera to point at the ground at 3d space 0,0,0 */
Camera.angleX = ???;
Camera.angleY = ????
Camera.angleZ = ????;

SomePointIn3DSpace.x = 5;
SomePointIn3DSpace.y = 5;
SomePointIn3DSpace.z = 5;

ScreenData.x 和 y 是空间中 3d 点的屏幕 x 位置。我如何计算这些值?

我可以使用这里找到的方程,但我不明白屏幕宽度/高度是如何起作用的。另外,我在 wiki 条目中不明白观看者的位置与相机位置的关系。

http://en.wikipedia.org/wiki/3D_projection

最佳答案

“完成的方式”是使用同构变换和坐标。你在空间中取一个点,然后:

  • 使用模型矩阵将其相对于相机定位。
  • 使用投影矩阵以正交或透视方式投影它。
  • 应用视口(viewport)变形将其放置在屏幕上。

  • 这变得非常模糊,但我会尝试涵盖重要的部分,并将其中的一些留给您。我假设您了解矩阵数学的基础知识:)。

    齐次 vector 、点、变换

    在 3D 中,齐次点将是 [x, y, z, 1] 形式的列矩阵。最后一个组件是“w”,一个缩放因子,对于 vector 来说,它是 0:这会导致你无法转换 vector ,这在数学上是正确的。我们不会去那里,我们在讨论要点。

    齐次变换是 4x4 矩阵,使用它们是因为它们允许将转换表示为矩阵乘法,而不是加法,这对您的视频卡来说既好又快。也很方便,因为我们可以通过将它们相乘来表示连续的变换。我们通过执行transformation * point 对点进行变换。

    有 3 个主要的齐次变换:
  • Translation,
  • Rotation,
  • Scaling.

  • 还有其他值得探索的,特别是“观察”转换。但是,我只想提供一个简短的列表和一些链接。应用于点的移动、缩放和旋转的连续应用统称为模型变换矩阵,并将它们放置在场景中,相对于相机。重要的是要意识到我们所做的类似于在相机周围移动物体,而不是相反。

    正交和透视

    要将世界坐标转换为屏幕坐标,您首先要使用投影矩阵,通常有两种形式:
  • 正交,常用于 2D 和 CAD。
  • 视角,适用于游戏和 3D 环境。

  • 正交投影矩阵的构造如下:

    An orthographic projection matrix, courtesy of Wikipedia.

    其中参数包括:
  • 顶部 :可见空间上边缘的 Y 坐标。
  • 底部 :可见空间下边缘的 Y 坐标。
  • :可见空间左边缘的X坐标。
  • :可见空间右边缘的 X 坐标。

  • 我认为这很简单。您建立的是一个将出现在屏幕上的空间区域,您可以对其进行剪辑。这里很简单,因为可见空间区域是一个矩形。透视剪裁更复杂,因为出现在屏幕或观看体积上的区域是 frustrum .

    如果您对透视投影的维基百科有困难,这是构建合适矩阵的代码,courtesy of geeks3D
    void BuildPerspProjMat(float *m, float fov, float aspect,
    float znear, float zfar)
    {
      float xymax = znear * tan(fov * PI_OVER_360);
      float ymin = -xymax;
      float xmin = -xymax;
    
      float width = xymax - xmin;
      float height = xymax - ymin;
    
      float depth = zfar - znear;
      float q = -(zfar + znear) / depth;
      float qn = -2 * (zfar * znear) / depth;
    
      float w = 2 * znear / width;
      w = w / aspect;
      float h = 2 * znear / height;
    
      m[0]  = w;
      m[1]  = 0;
      m[2]  = 0;
      m[3]  = 0;
    
      m[4]  = 0;
      m[5]  = h;
      m[6]  = 0;
      m[7]  = 0;
    
      m[8]  = 0;
      m[9]  = 0;
      m[10] = q;
      m[11] = -1;
    
      m[12] = 0;
      m[13] = 0;
      m[14] = qn;
      m[15] = 0;
    }
    

    变量是:
  • fov : 视野,pi/4 弧度是一个很好的值。
  • 方面 : 高宽比。
  • znear, zfar : 用于剪辑,我会忽略这些。

  • 生成的矩阵是列主矩阵,在上面的代码中索引如下:
    0   4   8  12
    1   5   9  13
    2   6  10  14
    3   7  11  15
    

    视口(viewport)变换,屏幕坐标

    这两种变换都需要另一个矩阵矩阵来将事物置于屏幕坐标中,称为视口(viewport)变换。 That's described here, I won't cover it (it's dead simple) .

    因此,对于点 p,我们将:
  • 执行模型变换矩阵 * p,得到 pm。
  • 执行投影矩阵 * pm,得到 pp.
  • 根据观看量剪辑 pp。
  • 执行视口(viewport)变换矩阵 * pp,结果是 ps: 屏幕上的点。

  • 摘要

    我希望能涵盖大部分内容。上面有漏洞,有些地方含糊不清,有什么问题可以在下面留言。这个主题通常值得一整章教科书,我已经尽我所能提炼这个过程,希望对你有利!

    我在上面链接到了这个,但我强烈建议你阅读这个,并下载二进制文件。这是一个很好的工具,可以进一步了解这些转换以及它如何在屏幕上获得点数:

    http://www.songho.ca/opengl/gl_transform.html

    就实际工作而言,您需要为齐次变换实现一个 4x4 矩阵类以及一个齐次点类,您可以将其与它相乘以应用变换(请记住,[x, y, z, 1])。您需要按照上述和链接中的说明生成转换。一旦你理解了程序,这并不是那么困难。祝你好运:)。

    关于c# - 基本渲染 3D 透视投影到带摄像头的 2D 屏幕(不带 opengl),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8633034/

    相关文章:

    java - 为什么我的二维数组将 'x' 显示为 120?

    java - 在 CRaSH shell(Spring-boot 远程 shell)中过滤 Spring beans

    java - 我如何在 Tapestry5 中创建自定义文本字段以将一些 Javascript 呈现到页面上?

    java - 在原始图像中显示使用 Rosetta 代码霍夫变换找到的线条

    sql - 如何将 UPDATE + 数学函数执行到空列中?

    c# - WCF - 发送请求的能力,其中字段可以按任何顺序排列

    c# - 命令行参数分隔符以及如何在 C# 中检查它

    c# - 如何在 c# 中禁用磁盘缓存使用 FILE_FLAG_NO_BUFFERING 调用 win32 CreateFile api

    c# - 在 EF4 Code-First 中按基本类型查询

    math - 没有溢出标志的处理器如何执行带符号算术?