1

这些天来,我正在阅读 Jason L. McKesson 的Learning Modern 3D Graphics Programming一书。基本上这是一本关于 OpenGL 3.3 的书,我现在在第 4 章,即关于正交视图和透视图。

在本章的最后,在“进一步研究”部分,他建议尝试一些事情,例如实现可变视点(他在相机空间的开头使用 (0, 0, 0) 以实现 semplicity)和任意透视平面地点。他说我需要将顶点的 X、Y 相机空间位置分别偏移 E_x 和 E_y。

我无法理解这段话,我应该如何使用仅修改 X、Y 偏移的可变眼点?

编辑:可能是这样的吗?

#version 330

layout(location = 0) in vec4 position;
layout(location = 1) in vec4 color;

smooth out vec4 theColor;

uniform vec2 offset;
uniform vec2 E;
uniform float zNear;
uniform float zFar;
uniform float frustumScale;


void main()
{
    vec4 cameraPos = position + vec4(offset.x, offset.y, 0.0, 0.0);
    vec4 clipPos;

    clipPos.xy = cameraPos.xy * frustumScale + vec4(E.x, E.y, 0.0, 0.0);

    clipPos.z = cameraPos.z * (zNear + zFar) / (zNear - zFar);
    clipPos.z += 2 * zNear * zFar / (zNear - zFar);

    clipPos.w = cameraPos.z / (-E.z);

    gl_Position = clipPos;
    theColor = color;
}

Edit2:谢谢鲍里斯,你的照片帮了很多忙:) 特别是因为:

  • 它清楚地说明了您之前所说的将 E 视为投影位置而不是眼点位置
  • 它强调了项目平面的大小必须始终为 [-1, 1],这是我在书上阅读的段落,但没有完全理解它的含义

只是好奇,你为什么在减法之后提到乘法?是不是和书上说的一样,就是纵横比?因为从逻辑上讲,一切都迫使我做相反的事情,即首先翻译(-2)然后乘法(/5)。或者也许用“缩放”一词,这本书指的是重塑功能?

4

1 回答 1

5

在这里,我们感兴趣的是计算从相机坐标 (CC) 到标准化设备坐标 (NDC) 的转换。

E其视为Camera Coordinates中投影平面的位置,而不是根据投影平面的眼点位置。在相机坐标中,眼点根据定义位于原点,至少在我对“相机坐标”含义的解释中:以您观看场景的位置为中心的坐标系。(您可以在数学上定义以任何位置为中心的透视变换,但这意味着您的输入空间不是相机空间,恕我直言。这就是 World->Camera 变换的用途,正如您将在第 6 章中看到的那样)

概括:

  • 您在相机空间中,因此您的视点位于 (0,0,0)
  • 您正在寻找负 Z 轴
  • 您的投影平面平行于 xOy 平面,两个方向的大小为 [-1,1]

这是这里的图片(每个刻度是 0.5 个单位):

在此处输入图像描述

在这张图片中,可以看到投影平面(灰色梯形的底边)以 (0,0,-1) 为中心,X 和 Y 方向的大小均为 [-1,1]。

现在,所要求的不是选择 (0,0,-1) 作为该平面的中心,而是选择任意 (Ex, Ey, Ez) 位置(假设 Ez 为负数)。但平面仍需与 xOy 轴平行且尺寸相同。

您可以看到维度 E.xy 与 Ez 扮演的角色非常不同,这就是为什么 E.xy 将参与减法,而 Ez 将参与除法。通过一个示例很容易看出这一点:

  1. 假设 zNear = -Ez (不一定是这种情况,但实际上您总是可以更改 frustumScale 以获得满足此要求的等效透视图)
  2. 考虑点 E(投影平面的中心)。

它在 NDC 空间中的坐标是什么?根据定义,它是 (0,0,-1)。您所做的是减去 E.xy,但除以 -E_z。

您的代码有这个想法,但仍有一些问题:

  1. 首先,您定义uniform vec2 E;而不是uniform vec3 E;(只是一个错字,没什么大不了的)
  2. 这条线clipPos.xy = ... ;是关于vec2算术的。因此,您只能乘以标量值(即浮点数)或加/减值vec2。因此,vec4(E.x, E.y, 0.0, 0.0)类型不正确,您应该改用E.xy它,它具有正确的类型vec2
  3. 实际上,您应该减去E.xy而不是添加它。这在我上面的例子中很容易看出。
  4. 最后,事情变得更加微妙;-)

我做了一张图片来说明修改:

在此处输入图像描述

在这张图片中,每个刻度是 1 个单位。左上角是您的相机坐标空间,显示了 zNear、zFar 和两个可能的投影平面。蓝色是这里的解释和着色器中使用的那个,红色的是你现在要使用的那个。彩色区域对应于最终屏幕中应显示的内容,例如 NDC 空间中的立方体 [-1,1]^3 中应显示的内容。因此,如果你使用蓝色投影平面,你想获得右上角的空间,如果你使用红色投影平面,你想选择底部的空间。为此,您可以观察到您需要在 NDC 空间中执行缩放和平移,例如在透视分割之后!(我认为书中写的内容要么不正确,要么对问题的解释不同)。

因此,您想要在欧几里得坐标中(即非齐次坐标,例如没有 W 坐标):

clipPosEuclideanRed.xy = clipPosEuclideanBlue.xy * (-E.z) - E.xy;
clipPosEuclideanRed.z = clipPosEuclideanBlue.z;

但是,由于您处于齐次坐标中,因此该值实际上是:

clipPosEuclidean.xyz = clipPos.xyz / clipPos.w; // with clipPos.w = -cameraPos.z;

因此,您必须通过编写来组合:

clipPosRed.xy = clipPosBlue.xy * (-E.z) - E.xy * (-cameraPos.z);
clipPosRed.z = clipPosBlue.z;

所以我对这个问题的解决方案是只添加一行:

void main()
{
    vec4 cameraPos = position + vec4(offset.x, offset.y, 0.0, 0.0);
    vec4 clipPos;

    clipPos.xy = cameraPos.xy * frustumScale;

    // only add this line
    clipPos.xy = - clipPos.xy * E.z + E.xy * cameraPos.z;

    clipPos.z = cameraPos.z * (zNear + zFar) / (zNear - zFar);
    clipPos.z += 2 * zNear * zFar / (zNear - zFar);

    clipPos.w = -cameraPos.z;

    gl_Position = clipPos;
    theColor = color;
}
于 2013-06-18T10:33:00.783 回答