草庐IT

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

coder 2023-05-17 原文

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

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 环境。

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



    其中参数包括:
  • 顶部 :可见空间上边缘的 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/

    有关c# - 基本渲染 3D 透视投影到带摄像头的 2D 屏幕(不带 opengl)的更多相关文章

    1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    2. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    3. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

      无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

    4. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

      ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

    5. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

      本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

    6. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

      Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

    7. [Vuforia]二.3D物体识别 - 2

      之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

    8. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    9. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

      项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

    10. ruby-on-rails - 使用 HTTParty 的非常基本的 Rails 4.1 API 调用 - 2

      Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"

    随机推荐