优化针孔相机渲染系统

发布于 2024-08-14 01:17:28 字数 2175 浏览 2 评论 0 原文

我正在为学校制作一个软件光栅化器,并且使用一种不寻常的渲染方法而不是传统的矩阵计算。它基于针孔相机。我在 3D 空间中有几个点,我通过获取它们与相机之间的距离并将其标准化,将它们转换为 2D 屏幕坐标,

Vec3 ray_to_camera = (a_Point - plane_pos).Normalize();

这给了我一个朝向相机的方向向量。然后,我通过将射线的原点放置在相机上并与相机稍后方的平面执行射线平面相交,将该方向转换为射线。

Vec3 plane_pos = m_Position + (m_Direction * m_ScreenDistance);

float dot = ray_to_camera.GetDotProduct(m_Direction);
if (dot < 0)
{
   float time = (-m_ScreenDistance - plane_pos.GetDotProduct(m_Direction)) / dot;

   // if time is smaller than 0 the ray is either parallel to the plane or misses it
   if (time >= 0)
   {
      // retrieving the actual intersection point
      a_Point -= (m_Direction * ((a_Point - plane_pos).GetDotProduct(m_Direction)));

      // subtracting the plane origin from the intersection point 
      // puts the point at world origin (0, 0, 0)
      Vec3 sub = a_Point - plane_pos;

      // the axes are calculated by saying the directional vector of the camera
      // is the new z axis
      projected.x = sub.GetDotProduct(m_Axis[0]);
      projected.y = sub.GetDotProduct(m_Axis[1]);
   }
}

这很有效,但我想知道:算法可以做得更快吗?现在,对于场景中的每个三角形,我必须计算三个法线。

float length = 1 / sqrtf(GetSquaredLength());
x *= length;
y *= length;
z *= length;

即使使用快速倒数平方根近似 (1 / sqrt(x)),这也将是非常苛刻的。

我的问题是:
有没有一个好的方法来近似三个法线?
这种渲染技术叫什么?
可以使用质心的法线来近似三个顶点吗? ((v0 + v1 + v2) / 3)

提前致谢。

PS“在接下来的七周内,您将在该领域专家的帮助下构建一个功能齐全的软件光栅器。开始吧。”我喜欢我的教育。 :)

编辑:

Vec2 projected;

// the plane is behind the camera
Vec3 plane_pos = m_Position + (m_Direction * m_ScreenDistance);

float scale = m_ScreenDistance / (m_Position - plane_pos).GetSquaredLength();

// times -100 because of the squared length instead of the length
// (which would involve a squared root)
projected.x = a_Point.GetDotProduct(m_Axis[0]).x * scale * -100;
projected.y = a_Point.GetDotProduct(m_Axis[1]).y * scale * -100;

return projected;

这会返回正确的结果,但是模型现在独立于相机位置。 :(

不过它更短更快!

I'm making a software rasterizer for school, and I'm using an unusual rendering method instead of traditional matrix calculations. It's based on a pinhole camera. I have a few points in 3D space, and I convert them to 2D screen coordinates by taking the distance between it and the camera and normalizing it

Vec3 ray_to_camera = (a_Point - plane_pos).Normalize();

This gives me a directional vector towards the camera. I then turn that direction into a ray by placing the ray's origin on the camera and performing a ray-plane intersection with a plane slightly behind the camera.

Vec3 plane_pos = m_Position + (m_Direction * m_ScreenDistance);

float dot = ray_to_camera.GetDotProduct(m_Direction);
if (dot < 0)
{
   float time = (-m_ScreenDistance - plane_pos.GetDotProduct(m_Direction)) / dot;

   // if time is smaller than 0 the ray is either parallel to the plane or misses it
   if (time >= 0)
   {
      // retrieving the actual intersection point
      a_Point -= (m_Direction * ((a_Point - plane_pos).GetDotProduct(m_Direction)));

      // subtracting the plane origin from the intersection point 
      // puts the point at world origin (0, 0, 0)
      Vec3 sub = a_Point - plane_pos;

      // the axes are calculated by saying the directional vector of the camera
      // is the new z axis
      projected.x = sub.GetDotProduct(m_Axis[0]);
      projected.y = sub.GetDotProduct(m_Axis[1]);
   }
}

This works wonderful, but I'm wondering: can the algorithm be made any faster? Right now, for every triangle in the scene, I have to calculate three normals.

float length = 1 / sqrtf(GetSquaredLength());
x *= length;
y *= length;
z *= length;

Even with a fast reciprocal square root approximation (1 / sqrt(x)) that's going to be very demanding.

My questions are thus:
Is there a good way to approximate the three normals?
What is this rendering technique called?
Can the three vertex points be approximated using the normal of the centroid? ((v0 + v1 + v2) / 3)

Thanks in advance.

P.S. "You will build a fully functional software rasterizer in the next seven weeks with the help of an expert in this field. Begin." I ADORE my education. :)

EDIT:

Vec2 projected;

// the plane is behind the camera
Vec3 plane_pos = m_Position + (m_Direction * m_ScreenDistance);

float scale = m_ScreenDistance / (m_Position - plane_pos).GetSquaredLength();

// times -100 because of the squared length instead of the length
// (which would involve a squared root)
projected.x = a_Point.GetDotProduct(m_Axis[0]).x * scale * -100;
projected.y = a_Point.GetDotProduct(m_Axis[1]).y * scale * -100;

return projected;

This returns the correct results, however the model is now independent of the camera position. :(

It's a lot shorter and faster though!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

木槿暧夏七纪年 2024-08-21 01:17:28

这称为光线追踪器——这是第一门计算机图形课程的相当典型的作业*——您可以在经典的 Foley/Van Damm 教科书 (计算机图形原理与实践)。我强烈建议您购买/借用这本教科书并仔细阅读。

*只需等到您开始反射和折射...现在乐趣就开始了!

This is called a ray-tracer - a rather typical assignment for a first computer graphics course* - and you can find a lot of interesting implementation details on the classic Foley/Van Damm textbook (Computer Graphics Principes and Practice). I strongly suggest you buy/borrow this textbook and read it carefully.

*Just wait until you get started on reflections and refraction... Now the fun begins!

白芷 2024-08-21 01:17:28

很难准确理解您的代码在做什么,因为它似乎执行了很多冗余操作!但是,如果我理解你所说的你想要做的事情,你就是:

  • 找到从针孔到
  • 归一化
  • 沿着归一化向量向后投影到“图像平面”(当然在针孔后面!)
  • 点的向量, 从图像平面上的中心点到该点的向量
  • 与“轴”向量对结果进行点积以找到 x 和 y 屏幕坐标

如果上述描述代表了您的意图,那么归一化应该是多余的 - 您应该根本不必这样做!如果删除标准化会给您带来不好的结果,那么您可能正在做一些与您规定的计划略有不同的事情......换句话说,您似乎和我一样混淆了自己,并且标准化步骤是将其“修复”为它在您的测试用例中看起来足够好,即使它可能仍然没有达到您想要的效果。

我认为,总体问题在于您的代码被严重过度设计:您正在将所有高级向量代数编写为要在内部循环中执行的代码。优化这一点的方法是在纸上计算出所有矢量代数,为内部循环找到最简单的表达式,并在相机设置时预先计算所有必要的常量。针孔相机规格仅是相机设置例程的输入。

不幸的是,除非我错过了我的猜测,否则这应该将你的针孔相机减少到传统的、无聊的旧矩阵计算。 (光线追踪确实使制作很酷的非标准相机的东西变得很容易 - 但你所描述的最终应该是完全标准的......)

It is difficult to understand exactly what your code doing, because it seems to be performing a lot of redundant operations! However, if I understand what you say you're trying to do, you are:

  • finding the vector from the pinhole to the point
  • normalizing it
  • projecting backwards along the normalized vector to an "image plane" (behind the pinhole, natch!)
  • finding the vector to this point from a central point on the image plane
  • doing dot products on the result with "axis" vectors to find the x and y screen coordinates

If the above description represents your intentions, then the normalization should be redundant -- you shouldn't have to do it at all! If removing the normalization gives you bad results, you are probably doing something slightly different from your stated plan... in other words, it seems likely that you have confused yourself along with me, and that the normalization step is "fixing" it to the extent that it looks good enough in your test cases, even though it probably still isn't doing quite what you want it to.

The overall problem, I think, is that your code is massively overengineered: you are writing all your high-level vector algebra as code to be executed in the inner loop. The way to optimize this is to work out all your vector algebra on paper, find the simplest expression possible for your inner loop, and precompute all the necessary constants for this at camera setup time. The pinhole camera specs would only be the inputs to the camera setup routine.

Unfortunately, unless I miss my guess, this should reduce your pinhole camera to the traditional, boring old matrix calculations. (ray tracing does make it easy to do cool nonstandard camera stuff -- but what you describe should end up perfectly standard...)

屋顶上的小猫咪 2024-08-21 01:17:28

你的代码对我来说有点不清楚(plane_pos?),但看起来你确实可以删除一些不必要的计算。

与其标准化光线(将其缩放到长度 1),为什么不缩放它以使 z 分量等于从相机到平面的距离 - 事实上,按此因子缩放 x 和 y,您不需要需要z。

float scale = distance_to_plane/z;
x *= scale;
y *= scale;

这将给出平面上的 x 和 y 坐标,没有 sqrt(),没有点积。

Your code is a little unclear to me (plane_pos?), but it does seem that you could cut out some unnecessary calculation.

Instead of normalizing the ray (scaling it to length 1), why not scale it so that the z component is equal to the distance from the camera to the plane-- in fact, scale x and y by this factor, you don't need z.

float scale = distance_to_plane/z;
x *= scale;
y *= scale;

This will give the x and y coordinates on the plane, no sqrt(), no dot products.

无畏 2024-08-21 01:17:28

好吧,当程序启动时,您可以立即计算每个三角形的法线。然后当你真正跑步时,你只需要访问法线即可。这种为了节省后期成本而进行的启动计算在图形中经常发生。这就是为什么我们在很多视频游戏中都有大的加载屏幕!

Well, off the bat, you can calculate normals for every triangle when your program starts up. Then when you're actually running, you just have to access the normals. This sort of startup calculation to save costs later tends to happen a lot in graphics. This is why we have large loading screens in a lot of our video games!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文