7

我正在为大学使用仅核心 OpenGL 3.3 编写 Wolfenstein 3D 的克隆,我遇到了精灵的一些问题,即让它们根据距离正确缩放。

据我所知,以前版本的 OGL 实际上会为您执行此操作,但该功能已被删除,并且我所有重新实现它的尝试都导致完全失败。

我目前的实现在远处是可以通过的,在中距离不是太破旧,在近距离是奇怪的。

主要问题(我认为)是我不了解我正在使用的数学。
精灵的目标尺寸略大于视口,所以当你接近它时它应该“走出图片”,但事实并非如此。它变小了,这让我很困惑。
我录制了一个小视频,以防文字不够。(我的在右边)

预期结果 实际结果

谁能指导我到哪里出错,并解释原因?

代码:
C++

// setup
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
glEnable(GL_PROGRAM_POINT_SIZE);

// Drawing
glUseProgram(StaticsProg);
glBindVertexArray(statixVAO);
glUniformMatrix4fv(uStatixMVP, 1, GL_FALSE, glm::value_ptr(MVP));
glDrawArrays(GL_POINTS, 0, iNumSprites);

顶点着色器

#version 330 core

layout(location = 0) in vec2 pos;
layout(location = 1) in int spriteNum_;

flat out int spriteNum;

uniform mat4 MVP;

const float constAtten  = 0.9;
const float linearAtten = 0.6;
const float quadAtten   = 0.001;

void main() {
    spriteNum = spriteNum_;
    gl_Position = MVP * vec4(pos.x + 1, pos.y, 0.5, 1); // Note: I have fiddled the MVP so that z is height rather than depth, since this is how I learned my vectors.
    float dist = distance(gl_Position, vec4(0,0,0,1));
    float attn = constAtten / ((1 + linearAtten * dist) * (1 + quadAtten * dist * dist));
    gl_PointSize = 768.0 * attn;
}

片段着色器

#version 330 core

flat in int spriteNum;

out vec4 color;

uniform sampler2DArray Sprites;

void main() {
    color = texture(Sprites, vec3(gl_PointCoord.s, gl_PointCoord.t, spriteNum));
    if (color.a < 0.2)
        discard;
}
4

4 回答 4

13

首先,我真的不明白你为什么使用pos.x + 1.

接下来,就像 Nathan 所说,您不应该使用剪辑空间点,而是使用眼睛空间点。这意味着您只使用模型视图转换的点(没有投影)来计算距离。

uniform mat4 MV;       //modelview matrix

vec3 eyePos = MV * vec4(pos.x, pos.y, 0.5, 1); 

此外,我不完全了解您的衰减计算。目前,较高的constAtten值意味着较少的衰减。为什么不直接使用 OpenGL 弃用的点参数使用的模型:

float dist = length(eyePos);   //since the distance to (0,0,0) is just the length
float attn = inversesqrt(constAtten + linearAtten*dist + quadAtten*dist*dist);

编辑:但总的来说,我认为这种衰减模型不是一个好方法,因为通常您只希望精灵保持其对象空间大小,而您必须摆弄衰减因子才能实现我认为的效果。

gl_PointSize更好的方法是输入其对象空间大小,然后使用当前视图和投影设置基于像素计算屏幕空间大小(实际上是这样):

uniform mat4 MV;                //modelview matrix
uniform mat4 P;                 //projection matrix
uniform float spriteWidth;      //object space width of sprite (maybe an per-vertex in)
uniform float screenWidth;      //screen width in pixels

vec4 eyePos = MV * vec4(pos.x, pos.y, 0.5, 1); 
vec4 projCorner = P * vec4(0.5*spriteWidth, 0.5*spriteWidth, eyePos.z, eyePos.w);
gl_PointSize = screenWidth * projCorner.x / projCorner.w;
gl_Position = P * eyePos;

这样,精灵总是得到它在渲染为宽度为spriteWidth.

编辑:当然你也应该记住点精灵的局限性。点精灵根据其中心位置进行裁剪。这意味着当它的中心移出屏幕时,整个精灵就会消失。对于大型精灵(就像你的情况,我认为)这可能真的是一个问题。

因此,我宁愿建议您使用简单的纹理四边形。通过这种方式,您可以避免整个衰减问题,因为四边形只是像其他所有 3d 对象一样进行变换。您只需要实现朝向查看器的旋转,这可以在 CPU 或顶点着色器中完成。

于 2011-12-22T20:09:42.910 回答
5

根据Christian Rau 的回答(最后一次编辑),我实现了一个几何着色器,它在 ViewSpace 中构建了一个广告牌,这似乎解决了我所有的问题:

预期结果 实际结果

这是着色器:(请注意,我已经修复了需要原始着色器将 1 添加到 x 的对齐问题)

顶点着色器

#version 330 core

layout (location = 0) in vec4 gridPos;
layout (location = 1) in int  spriteNum_in;

flat out int spriteNum;

// simple pass-thru to the geometry generator
void main() {
    gl_Position = gridPos;
    spriteNum = spriteNum_in;
}

几何着色器

#version 330 core

layout (points) in;
layout (triangle_strip, max_vertices = 4) out;

flat in int spriteNum[];

smooth out vec3 stp;

uniform mat4 Projection;
uniform mat4 View;

void main() {
    // Put us into screen space. 
    vec4 pos = View * gl_in[0].gl_Position;

    int snum = spriteNum[0];

    // Bottom left corner
    gl_Position = pos;
    gl_Position.x += 0.5;
    gl_Position = Projection * gl_Position;
    stp = vec3(0, 0, snum);
    EmitVertex();

    // Top left corner
    gl_Position = pos;
    gl_Position.x += 0.5;
    gl_Position.y += 1;
    gl_Position = Projection * gl_Position;
    stp = vec3(0, 1, snum);
    EmitVertex();

    // Bottom right corner
    gl_Position = pos;
    gl_Position.x -= 0.5;
    gl_Position = Projection * gl_Position;
    stp = vec3(1, 0, snum);
    EmitVertex();

    // Top right corner
    gl_Position = pos;
    gl_Position.x -= 0.5;
    gl_Position.y += 1;
    gl_Position = Projection * gl_Position;
    stp = vec3(1, 1, snum);
    EmitVertex();

    EndPrimitive();
}

片段着色器

#version 330 core

smooth in vec3 stp;

out vec4 colour;

uniform sampler2DArray Sprites;

void main() {
    colour = texture(Sprites, stp);
    if (colour.a < 0.2)
        discard;
}
于 2011-12-24T00:01:02.683 回答
1

我认为您不想将顶点着色器中的距离计算基于投影位置。相反,只需计算相对于您的视图的位置,即使用模型视图矩阵而不是模型视图投影矩阵。

这样想——在投影空间中,随着一个物体离你越来越近,它在水平和垂直方向上的距离变得夸张了。当您接近它们时,您可以从灯从中心向屏幕顶部移动的方式看到这一点。当你真正靠近时,这些尺寸的夸张会使距离变大,这就是你看到物体缩小的原因。

于 2011-12-22T19:52:44.190 回答
0

至少在 OpenGL ES 2.0 中,OpenGL 实现对 gl_PointSize 有最大尺寸限制。您可以使用 ALIASED_POINT_SIZE_RANGE 查询大小。

于 2012-12-14T19:02:51.953 回答