【问题标题】:OpenGL matrix setup for tiled rendering用于平铺渲染的 OpenGL 矩阵设置
【发布时间】:2015-01-26 17:48:58
【问题描述】:

阅读datenwolf's 2011 answer concerning tile-based render setup in OpenGL 后,我尝试实施他的解决方案。源图像如下所示(800 x 600)

生成的图像包含 2x2 块,每个块的大小为 800 x 600,如下所示。

正如您所见,它们并不完全匹配,尽管我可以看到发生了一些隐约有趣的事情。我确定我在某处犯了一个基本错误,但我看不出来。

我正在做 4 次传球,其中:

w, h are 2,2 (2x2 tiles)    
x, y are (0,0) (1,0) (0,1) and (1,1) in each of the 4 passes    
MyFov is 1.30899692 (75 degrees)
MyWindowWidth, MyWindowHeight are 800, 600
MyNearPlane, MyFarPlane are 0.1, 200.0

计算每个图块的截锥体的算法是:

auto aspect = static_cast<float>(MyWindowWidth) / static_cast<float>(MyWindowHeight);
auto right = -0.5f * Math::Tan(MyFov) * MyShaderData.Camera_NearPlane;
auto left = -right;
auto top = aspect * right;
auto bottom = -top;
auto shift_X = (right - left) / static_cast<float>(w);
auto shift_Y = (top - bottom) / static_cast<float>(h);
auto frustum = Math::Frustum(left   + shift_X * static_cast<float>(x),
                             left   + shift_X * static_cast<float>(x + 1), 
                             bottom + shift_Y * static_cast<float>(y),
                             bottom + shift_Y * static_cast<float>(y + 1),
                             MyShaderData.Camera_NearPlane,
                             MyShaderData.Camera_FarPlane);

,其中 Math::Frustum 是:

template<class T>
Matrix4x4<T> Frustum(T left, T right, T bottom, T top, T nearPlane, T farPlane)
{
    Matrix4x4<T> r(InitialiseAs::InitialiseZero);

    r.m11 = (static_cast<T>(2) * nearPlane) / (right - left);
    r.m22 = (static_cast<T>(2) * nearPlane) / (top - bottom);
    r.m31 = (right + left) / (right - left);
    r.m32 = (top + bottom) / (top - bottom);
    r.m33 = -(farPlane + nearPlane) / (farPlane - nearPlane);
    r.m34 =   static_cast<T>(-1);
    r.m43 = -(static_cast<T>(2) * farPlane * nearPlane) / (farPlane - nearPlane);

    return r;
}

为了完整起见,我的 Matrx4x4 布局是:

struct
{
    T m11, m12, m13, m14;
    T m21, m22, m23, m24;
    T m31, m32, m33, m34;
    T m41, m42, m43, m44;
};

谁能发现我的错误?

编辑

因此,derhass 向我解释了它 - 一种更简单的做事方式是简单地缩放和转换投影矩阵。为了测试,我修改了我的翻译矩阵,放大了 2 倍,如下(更改每个图块的翻译):

auto scale = Math::Scale(2.f, 2.f, 1.f);
auto translate = Math::Translate(0.5f, 0.5f, 0.f);
auto projection = Math::Perspective(MyFov,
                                    static_cast<float>(MyWindowWidth) / static_cast<float>(MyWindowHeight),
                                    MyShaderData.Camera_NearPlane,
                                    MyShaderData.Camera_FarPlane);          

MyShaderData.Camera_Projection = scale * translate * projection;

生成的图像如下(将 4 个拼接在一起) - 图像中的不连续性是由我认为的后期处理引起的,所以这是我可能不得不在某个时候处理的另一个问题。

【问题讨论】:

  • 我没有检查你的实现有什么问题,但我认为 datenwolf 的解决方案有不必要的复杂性。如果您使用某些投影矩阵(无论是正交还是透视)渲染某些场景,则生成的平截头体将最终以 [-1,1]^3 NDC 空间的同质表示形式结束。因此,如果您想要渲染全屏的某个图块,只需在 x 和 y 中 预乘 一些缩放和平移来选择您喜欢的任何 2D 子矩形(这将具有与在平截头体中改变 FOV 和不对称效果相同)。
  • 我认为 NDC 发生在齐次除法之后,即乘以投影矩阵之后,所以我不知道它会如何工作。无论如何,我尝试了 WxH 比例预乘和各种不同的翻译,但我无法让它工作。数学是错误的。
  • 我的建议适用于剪辑空间。我只为截头锥的 [-1,1] 范围引入了 NDC。关键是在应用投影矩阵后应用简单的平移和缩放(这意味着 pre 按照默认的 GL 约定乘以它)来缩放和移动 [-1,1] 的某些子矩形到完整的 [-1,1] 范围将完全让您在完整的视口中获得图像的那部分。
  • 例如,如果你想要通过应用矩阵P 获得的图像的左上角,你可以简单地使用P' = S(2,2,1) * T(0.5, -0.5, 0) * PS 是一个比例矩阵,@ 987654333@一个翻译)作为P的替代品(不管P是什么投影,在任何情况下都会起作用)。所有这些都假设您使用 GL 的常用矩阵约定,因此顶点v 的变换为v'=M * v。如果你使用v' = M *v,后乘修改是正确的方法。
  • 啊,我只花了 2 天时间就“搞定”了。请将此添加为答案,以便我可以给您一些积分。我将编辑我的问题以显示结果。

标签: opengl matrix tile


【解决方案1】:

这不是问题的真正答案,但它可能是您在此处尝试解决的有用替代方法。在我看来,datenwolf 在他对您所指的stackoverflow question 的回答中的解决方案比它需要的更复杂。所以我在这里提出我的替代方案。

前言:我假设标准的 OpenGL 矩阵约定,因此矩阵M 的顶点变换与v'= M *v 一样完成(就像固定功能管道所做的那样)。

当使用某个投影矩阵P 渲染场景时,您可以在应用投影矩阵之后通过应用缩放和变换操作来提取所述场景的任何轴对齐子矩形。

关键是查看体积定义为NDC空间中的[-1,1]^3立方体。剪辑空间(P 将数据转换为)只是该卷的同质表示。由于典型的4x4 变换矩阵都在同构空间中工作,我们根本不需要关心w,只需像在 NDC 空间中一样定义变换即可。

由于您只需要一些 2D 平铺,z 应该保持原样,并且只需要在 xy 中进行一些缩放和平移。当将转换AB 组合成单个矩阵C 作为C=A*B 时,按照上述约定,这将导致首先应用B,最后应用A(因为C*v == A*B*v == A*(B*v))。因此,要修改 投影后的结果,我们必须 pre-将一些转换乘以 P,然后我们就完成了:

P'=S(sx,sy,1) * T(tx,ty,0) * P

P' 的构造将适用于任何有效的投影矩阵P,无论它是透视变换还是正交变换。在正交情况下,它的作用是非常清楚的。在透视的情况下,这实际上修改了视场并将平截头体转变为不对称的。

当您想要将图像平铺成m 乘以n 段的网格时。很明显sx=msy=n。正如我确实使用了S * T 顺序(通过选择),T 在比例之前应用,所以对于每个图块,(tx,ty) 只是将图块中心移动到新中心的向量(这将是起源)。由于 NDC 空间是 2 个单位宽和高,对于一个 tile x,y,转换是

tx= - (-1 + 2/(2*m) + (2/m) * x)
ty= - (-1 + 2/(2*n) + (2/n) * y)
//     ^     ^         ^    
//     |     |         |
//     |     |         +- size of of each tile in NDC space
//     |     |
//     |     +- half the size (as the center offset)
//     |
//     +- left/bottom border of NDC space

【讨论】:

  • 感谢您对本次会议的所有帮助。我很感激。
  • 顺便说一句,如果您使用额外的剪切平面,此解决方案将无法正常工作。
  • @Thomas:这取决于您如何应用额外的剪辑平面。通常,它们是在世界或眼睛空间中定义的,这种方法根本不会修改。
猜你喜欢
  • 2013-05-14
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-29
  • 1970-01-01
  • 2019-05-22
相关资源
最近更新 更多