根据与相机的距离调整点精灵的大小

时间:2011-12-22 19:33:20

标签: c++ opengl glsl opengl-3 point-sprites

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

据我所知,以前版本的OGL实际上会为你做这件事,但是这个功能已被删除,而我所有尝试重新实现它都会导致完全失败。

我目前的实施方式是可以在距离上通过,在中间范围内不会太破旧,而在近距离范围内会很眩晕。

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

Expected Result Actual Result

任何人都可以指引我到我错的地方,并解释原因吗?

代码:
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 个答案:

答案 0 :(得分:13)

首先,我真的不明白你使用pos.x + 1的原因。

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

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上或在顶点着色器中完成。

答案 1 :(得分:5)

基于Christian Rau's answer(上次编辑),我实现了一个在ViewSpace中构建广告牌的几何着色器,它似乎解决了我所有的问题:

Expected Result Actual Result

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

顶点着色器

#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;
}

答案 2 :(得分:1)

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

以这种方式思考 - 在投影空间中,当物体靠近你时,它在水平和垂直方向上的距离变得夸张。当你接近它们时,你可以看到灯从中心向屏幕顶部移动的方式。这些尺寸的夸大将使得距离越来越近,这就是为什么你看到物体缩小的原因。

答案 3 :(得分:0)

至少在OpenGL ES 2.0中,OpenGL实现强加的gl_PointSize存在最大大小限制。您可以使用ALIASED_POINT_SIZE_RANGE查询大小。