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

次に、ネイサンが言ったように、クリップ スペース ポイントではなく、アイ スペース ポイントを使用する必要があります。これは、モデルビュー変換されたポイント (投影なし) のみを使用して距離を計算することを意味します。

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 にビルボードを作成するジオメトリ シェーダーを実装しました。これにより、すべての問題が解決されるようです。

期待される結果 実結果

シェーダーは次のとおりです: (元のシェーダーで 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;
}
于 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 に答える