簡體   English   中英

根據與相機的距離調整點精靈的大小

[英]Resizing point sprites based on distance from the camera

我正在使用僅用於大學的核心OpenGL 3.3編寫Wolfenstein 3D的克隆,我遇到了精靈的一些問題,即讓它們根據距離正確縮放。

據我所知,以前版本的OGL實際上會為你做這件事,但是這個功能已被刪除,而我重新實現它的所有嘗試都導致完全失敗。

我目前的實施方式是可以在距離上通過,在中距離不太破舊,近距離發呆。

主要問題(我認為)是我對我正在使用的數學沒有理解。
精靈的目標大小略大於視口,因此當你接近它時它應該“走出畫面”,但事實並非如此。 它變小了,這讓我很困惑。
我錄制了一個小視頻,以防單詞不夠。 (我在右邊)

Expected ResultActual 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;
}

首先,我真的不明白為什么你使用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的紋理四邊形時的spriteWidth

編輯:當然你也應該記住點精靈的局限性。 點精靈根據其中心位置進行裁剪。 這意味着當它的中心移出屏幕時,整個精靈會消失。 對於大精靈(就像你的情況,我認為),這可能真的是一個問題。

所以我寧願建議你使用簡單的紋理四邊形。 這樣就可以避免這個整個衰減問題,因為四邊形像其他每個3d對象一樣被轉換。 您只需要向查看器實現旋轉,這可以在CPU上或在頂點着色器中完成。

基於Christian Rau的回答 (最后編輯),我實現了一個幾何着色器,它在ViewSpace中構建了一個廣告牌,它似乎解決了我所有的問題:

Expected ResultActual 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;
}

我認為您不希望將頂點着色器中的距離計算基於投影位置。 而只是計算相對於視圖的位置,即使用模型視圖矩陣而不是模型視圖投影矩陣。

以這種方式思考 - 在投影空間中,當物體靠近你時,它在水平和垂直方向上的距離變得誇張。 當你接近它們時,你可以看到燈從中心向屏幕頂部移動的方式。 這些尺寸的誇大將使得距離越來越近,這就是為什么你看到物體縮小的原因。

至少在OpenGL ES 2.0中,由OpenGL實現強加的gl_PointSize存在最大大小限制。 您可以使用ALIASED_POINT_SIZE_RANGE查詢大小。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM