【问题标题】:Resizing point sprites based on distance from the camera根据与相机的距离调整点精灵的大小
【发布时间】:2012-01-26 09:18:32
【问题描述】:

我正在为大学编写仅使用核心 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;
}

【问题讨论】:

  • 我认为是因为他将精灵中心位置设置为左下方,但他确实希望锚点在顶部中心。
  • 我希望连接点位于底部边缘的中心,但是由于一堆对齐问题我没有时间整理,我不得不移动很多周围的事情有些奇怪。

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


【解决方案1】:

首先,我不太明白你为什么使用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 或顶点着色器中完成。

【讨论】:

  • 您的第二段代码导致this,我不完全确定它在做什么。你能进一步解释一下吗?我也会研究四边形。
  • 是的,有 projCorner 的那个。
  • @Lexi 第二个dit基本上首先将精灵位置转换为眼睛/视图空间。然后它构造一个点,即精灵的左上角,与输入精灵的大小相同,位于屏幕中心,距离输入精灵的距离。然后这个角由投影矩阵变换(并除以 w)。而这个点到(0,0)的水平距离,所以它的x值,其实就是它在[0,1]中的屏幕尺寸,只需要乘以屏幕宽度,就可以得到像素尺寸。我不知道为什么它不起作用。
  • @LexiR 换句话说,它需要一个精灵,即在屏幕中心和输入精灵的相机距离处,并且与输入精灵的大小相同。将其转换为屏幕空间,并将其屏幕空间宽度(以像素为单位)作为点大小。
  • 在描述位置时 w 不总是 1 吗?
【解决方案2】:

基于Christian Rau's answer(上次编辑),我实现了一个几何着色器,可以在 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;
}

【讨论】:

  • 所以 Christian Rau 的答案是什么让你走到了现在,但你接受了你自己的答案?哇,在他付出努力之后,这真是令人难以置信。将人们视为理所当然的方式。
  • @NickWiggill 老实说,我并没有真正想过这样的事情。我做出并接受了我的答案,因为我认为这对解决我的问题的其他人会更有帮助。
  • +1 您的答案,以换取您考虑重新分配已接受的答案。无论如何,你的最终可能会获得更多选票。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多