26

glLineStipple最新の OpenGL API では廃止されました。それは何に置き換えられますか?交換しない場合、どうすれば同様の効果を得ることができますか? (もちろん、互換性プロファイルは使いたくない...)

4

3 に答える 3

28

申し訳ありませんが、何も置き換えられていません。それをエミュレートするために頭に浮かんだ最初のアイデアは、ジオメトリ シェーダーです。ジオメトリ シェーダーにラインを供給し、そのスクリーン スペースの長さを計算し、それに基づいて開始頂点と終了頂点の間に可変数のサブラインを生成します。


編集:おそらく、パターンを 0.0 (線なし) または 1.0 (線) としてエンコードするアルファ (または赤) チャネルで 1D テクスチャを使用してから、線のテクスチャ座標を 0 から 1 に変更し、フラグメント チャダーで簡単なアルファテストを行い、アルファがしきい値を下回るフラグメントを破棄します。ジオメトリ シェーダーがライン texCoords を生成するのを容易にすることができます。そうしないと、ラインごとに異なる頂点が必要になります。このようにして、texCoord を行のスクリーン スペースの長さに依存させることもできます。

(ポリゴン モードを使用して) 三角形を描くと、全体がより難しくなりますGL_LINE。次に、ジオメトリ シェーダーで自分で三角形を線に変換し、三角形を配置して線を配置する必要があります (これは、ポリゴン モードが廃止されていない場合は、将来廃止される理由になる可能性もあります)。


編集:この質問は放棄されたと思いますが、2 番目のアプローチのために単純なシェーダー トリプルを作成しました。これは最小限のソリューションです。カスタム機能を自分で自由に追加してください。必要なハードウェアが不足しているため、テストしていませんが、要点を理解する必要があります。

uniform mat4 modelViewProj;

layout(location=0) in vec4 vertex;

void main()
{
    gl_Position = modelViewProj * vertex;
}

頂点シェーダーは単純なパススルーです。

layout(lines) in;
layout(line_strip, max_vertices=2) out;

uniform vec2 screenSize;
uniform float patternSize;

noperspective out float texCoord;

void main()
{
    vec2 winPos0 = screenSize.xy * gl_in[0].gl_Position.xy / gl_in[0].gl_Position.w;
    vec2 winPos1 = screenSize.xy * gl_in[1].gl_Position.xy / gl_in[1].gl_Position.w;
    gl_Position = gl_in[0].gl_Position;
    texCoord = 0.0;
    EmitVertex();
    gl_Position = gl_in[1].gl_Position;
    texCoord = 0.5 * length(winPos1-winPos0) / patternSize;
    EmitVertex();
}

ジオメトリ シェーダーでは、ラインを取得し、そのスクリーン スペースの長さをピクセル単位で計算します。次に、これを点描パターン テクスチャのサイズで割ります。これはfactor*16、 への呼び出しをエミュレートするときのものglLineStipple(factor, pattern)です。これは、2 番目のラインの終点の 1D テクスチャ座標として取得されます。

このテクスチャ座標は線形に補間する必要があることに注意してください (noperspective補間指定子)。通常の遠近法による正確な補間では、スクリーン スペースの値を明示的に処理しているのに対し、ラインの遠く離れた部分で点描パターンが「一緒に圧縮」されます。

uniform sampler1D pattern;
uniform vec4 lineColor;

noperspective in float texCoord;

layout(location=0) out vec4 color;

void main()
{
    if(texture(pattern, texCoord).r < 0.5)
        discard;
    color = lineColor;
}

フラグメント シェーダーは、パターン テクスチャの値を使用して単純なアルファ テストを実行するだけになりました。この値には、線の場合は 1、線がない場合は 0 が含まれます。したがって、固定機能のスティプルをエミュレートするには、16 ビット パターンの代わりに 16 ピクセルの 1 コンポーネント 1D テクスチャを使用します。パターンのラッピング モードを に設定することを忘れないでくださいGL_REPEAT。フィルタリング モードについてはよくわかりませんGL_NEARESTが、良いアイデアだと思います。

しかし、前に述べたように、 を使用して三角形をレンダリングしたい場合、glPolygonModeこの方法では機能しません。代わりに、三角形を受け入れて三角形ごとに 3 つの線を生成するようにジオメトリ シェーダーを調整する必要があります。


編集:実際、シェーダーでの整数操作に対する OpenGL 3 の直接サポートにより、この 1D テクスチャ アプローチ全体を完全に削除し、実際のビットパターンで直接作業することができます。したがって、ジオメトリ シェーダーは、正規化せずに実際の画面サイズのパターン座標を出力するようにわずかに変更されます。

texCoord = 0.5 * length(winPos1-winPos0);

フラグメント シェーダーでは、ビット パターンを符号なし整数 (ただし、16 ビット値とは対照的に 32 ビットglLineStipple) として取得し、パターンのストレッチ ファクターを取得して、テクスチャ座標を取得します (まあ、実際にはテクスチャはもうありません。しかし気にしないでください)パターン上の位置を取得するためのモジュロ32(これらの明示的なuints は面倒ですが、私のGLSLコンパイラは と の間の暗黙の変換は悪であると言います) int:uint

uniform uint pattern;
uniform float factor;

...
uint bit = uint(round(linePos/factor)) & 31U;
if((pattern & (1U<<bit)) == 0U)
    discard;
于 2011-05-16T12:17:19.730 に答える
12

この質問に答えるには、まずglLineStipple実際に何が行われるかを調査する必要があります。

画像を参照してください。左側の四角形は、プリミティブ タイプ を使用して 4 つの分離した線分によって描かれていGL_LINESます。
右側の円は、プリミティブ タイプ を使用して、連続するポリゴン ラインによって描画されGL_LINE_STRIPます。

線分を使用する場合、点画パターンは各線分で開始されました。パターンは各プリミティブで再開されます。
ライン ストリップを使用すると、点描パターンがポリゴン全体にシームレスに適用されます。頂点座標を超えてシームレスに連続するパターン。
パターンの長さが対角線で伸びていることに注意してください。これはおそらく実装の鍵です。

別々のライン セグメントの場合、これはまったく複雑ではありませんが、ライン ストリップの場合はもう少し複雑になります。線の長さは、線のすべてのプリミティブを知らずに、シェーダー プログラムで計算することはできません。すべてのプリミティブがわかっている場合でも (SSBO など)、計算はループで実行する必要があります。OpenGL コア プロファイルの破線
も参照してください。

とにかく、ジオメトリ シェーダーを実装する必要はありません。トリックは、フラグメント シェーダーで線分の開始を知ることです。flatこれは、補間修飾子を使用して簡単に行うことができます。

頂点シェーダーは、正規化されたデバイス座標をフラグメント シェーダーに渡す必要があります。1 回はデフォルトの補間で、もう 1 回は補間なし (フラット) です。これにより、フラグメント シェーディングでは、最初の入力パラメーターにライン上の実際の位置の NDC 座標が含まれ、その後にラインの開始点の NDC 座標が含まれます。

#version 330

layout (location = 0) in vec3 inPos;

flat out vec3 startPos;
out vec3 vertPos;

uniform mat4 u_mvp;

void main()
{
    vec4 pos    = u_mvp * vec4(inPos, 1.0);
    gl_Position = pos;
    vertPos     = pos.xyz / pos.w;
    startPos    = vertPos;
}

さまざまな入力に加えて、フラグメント シェーダーには均一な変数があります。u_resolutionビューポートの幅と高さを含みます。u_factoru_patternは、 のパラメータに基づく乗数と 16 ビット パターンですglLineStipple

したがって、開始から実際のフラグメントまでの行の長さは次のように計算できます。

vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);

また、ギャップ上のフラグメントは、コマンドによって破棄できますdiscard

uint bit = uint(round(dist / u_factor)) & 15U;
if ((u_pattern & (1U<<bit)) == 0U)
    discard; 

フラグメント シェーダー:

#version 330

flat in vec3 startPos;
in vec3 vertPos;

out vec4 fragColor;

uniform vec2  u_resolution;
uniform uint  u_pattern;
uniform float u_factor;

void main()
{
    vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
    float dist = length(dir);

    uint bit = uint(round(dist / u_factor)) & 15U;
    if ((u_pattern & (1U<<bit)) == 0U)
        discard; 
    fragColor = vec4(1.0);
}

この実装は、ジオメトリ シェーダーを使用するよりもはるかに簡単で短時間です。flat補間修飾子は、GLSL 1.30 および GLSL ES 3.00 以降でサポートされます。このバージョンでは、ジオメトリ シェーダーはサポートされていません。
上記のシェーダーで生成されたライン レンダリングを参照してください。

シェーダーは適切な結果のライン セグメントを提供しますが、点画パターンが各頂点座標で再開されるため、ライン ストリップでは失敗します。
この問題は、ジオメトリ シェーダーでも解決できません。問題のこの部分はまだ未解決のままです。

次の簡単なデモ プログラムでは、ウィンドウの作成にGLFW API、OpenGL の読み込みにGLEW 、数学にGLM -OpenGL Mathematicsを使用しました。CreateProgram頂点シェーダーとフラグメント シェーダーのソース コードからプログラム オブジェクトを作成する関数 のコードは提供しません。

#include <vector>
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <gl/gl_glew.h>
#include <GLFW/glfw3.h>

std::string vertShader = R"(
#version 330

layout (location = 0) in vec3 inPos;

flat out vec3 startPos;
out vec3 vertPos;

uniform mat4 u_mvp;

void main()
{
    vec4 pos    = u_mvp * vec4(inPos, 1.0);
    gl_Position = pos;
    vertPos     = pos.xyz / pos.w;
    startPos    = vertPos;
}
)";

std::string fragShader = R"(
#version 330

flat in vec3 startPos;
in vec3 vertPos;

out vec4 fragColor;

uniform vec2  u_resolution;
uniform uint  u_pattern;
uniform float u_factor;

void main()
{
    vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
    float dist = length(dir);

    uint bit = uint(round(dist / u_factor)) & 15U;
    if ((u_pattern & (1U<<bit)) == 0U)
        discard; 
    fragColor = vec4(1.0);
}
)";

GLuint CreateVAO(std::vector<glm::vec3> &varray)
{
    GLuint bo[2], vao;
    glGenBuffers(2, bo);
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0); 
    glBindBuffer(GL_ARRAY_BUFFER, bo[0] );
    glBufferData(GL_ARRAY_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 

    return vao;
}

int main(void)
{
    if ( glfwInit() == 0 )
        return 0;
    GLFWwindow *window = glfwCreateWindow( 800, 600, "GLFW OGL window", nullptr, nullptr );
    if ( window == nullptr )
        return 0;
    glfwMakeContextCurrent(window);

    glewExperimental = true;
    if ( glewInit() != GLEW_OK )
        return 0;

    GLuint program    = CreateProgram(vertShader, fragShader);
    GLint loc_mvp     = glGetUniformLocation(program, "u_mvp");
    GLint loc_res     = glGetUniformLocation(program, "u_resolution");
    GLint loc_pattern = glGetUniformLocation(program, "u_pattern");
    GLint loc_factor  = glGetUniformLocation(program, "u_factor");

    glUseProgram(program);

    GLushort pattern = 0x18ff;
    GLfloat  factor  = 2.0f;
    glUniform1ui(loc_pattern, pattern);
    glUniform1f(loc_factor, factor);
    //glLineStipple(2.0, pattern);
    //glEnable(GL_LINE_STIPPLE);

    glm::vec3 p0(-1.0f, -1.0f, 0.0f);
    glm::vec3 p1(1.0f, -1.0f, 0.0f);
    glm::vec3 p2(1.0f, 1.0f, 0.0f);
    glm::vec3 p3(-1.0f, 1.0f, 0.0f);
    std::vector<glm::vec3> varray1{ p0, p1, p1, p2, p2, p3, p3, p0 };
    GLuint vao1 = CreateVAO(varray1);

    std::vector<glm::vec3> varray2;
    for (size_t u=0; u <= 360; u += 8)
    {
        double a = u*M_PI/180.0;
        double c = cos(a), s = sin(a);
        varray2.emplace_back(glm::vec3((float)c, (float)s, 0.0f));
    }
    GLuint vao2 = CreateVAO(varray2);

    glm::mat4(project);
    int vpSize[2]{0, 0};
    while (!glfwWindowShouldClose(window))
    {
        int w, h;
        glfwGetFramebufferSize(window, &w, &h);
        if (w != vpSize[0] ||  h != vpSize[1])
        {
            vpSize[0] = w; vpSize[1] = h;
            glViewport(0, 0, vpSize[0], vpSize[1]);
            float aspect = (float)w/(float)h;
            project = glm::ortho(-aspect, aspect, -1.0f, 1.0f, -10.0f, 10.0f);
            glUniform2f(loc_res, (float)w, (float)h);
        }

        glClear(GL_COLOR_BUFFER_BIT);

        glm::mat4 modelview1( 1.0f );
        modelview1 = glm::translate(modelview1, glm::vec3(-0.6f, 0.0f, 0.0f) );
        modelview1 = glm::scale(modelview1, glm::vec3(0.5f, 0.5f, 1.0f) );
        glm::mat4 mvp1 = project * modelview1;

        glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp1));
        glBindVertexArray(vao1);
        glDrawArrays(GL_LINES, 0, (GLsizei)varray1.size());

        glm::mat4 modelview2( 1.0f );
        modelview2 = glm::translate(modelview2, glm::vec3(0.6f, 0.0f, 0.0f) );
        modelview2 = glm::scale(modelview2, glm::vec3(0.5f, 0.5f, 1.0f) );
        glm::mat4 mvp2 = project * modelview2;

        glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp2));
        glBindVertexArray(vao2);
        glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)varray2.size());

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();

    return 0;
}


OpenGL3 の破線 も参照してください。
OpenGL ES - 破線

于 2019-03-10T14:28:04.100 に答える