glLineStipple在OpenGL 3.1中已弃用

Vin*_*ent 24 opengl glsl line opengl-3 opengl-4

glLineStipple已在最新的OpenGL API中弃用.什么被取代?如果没有更换,我怎么能得到类似的效果?(我当然不想使用兼容性配置文件...)

Chr*_*ica 25

对不起,它没有被任何东西取代.我想到的第一个想法就是几何着色器.您使用直线为几何着色器提供数据,计算其屏幕空间长度,并根据该生成器在其起始和结束顶点之间生成可变数量的子行.


编辑:也许你也可以使用1D纹理与alpha(或红色)通道编码模式为0.0(无线)或1.0(线)然后让线纹理坐标从0到1并在片段chader你进行简单的alpha测试,丢弃alpha低于某个阈值的片段.您可以方便几何着色器生成行texCoords,否则每行需要不同的顶点.这样,您还可以使texCoord依赖于线的屏幕空间长度.

如果绘制三角形(使用多边形模式GL_LINE),整个过程会变得更加困难.然后你必须在几何着色器中自己进行三角形线变换,放入三角形并放出线条(这也可能是将来弃用多边形模式的原因,如果还没有的话).


编辑:虽然我认为这个问题已经放弃,但我已经为第二种方法制作了一个简单的着色器三元组.它只是一个最小的解决方案,您可以自己添加自定义功能.我没有测试它,因为我缺少必要的硬件,但你应该明白:

uniform mat4 modelViewProj;

layout(location=0) in vec4 vertex;

void main()
{
    gl_Position = modelViewProj * vertex;
}
Run Code Online (Sandbox Code Playgroud)

顶点着色器是一个简单的传递.

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();
}
Run Code Online (Sandbox Code Playgroud)

在几何着色器中,我们采用一条线并以像素为单位计算其屏幕空间长度.然后我们通过点画模式纹理的大小来划分它,这将是factor*16模拟调用时glLineStipple(factor, pattern).这被视为第二行终点的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;
}
Run Code Online (Sandbox Code Playgroud)

碎片着色器现在只使用模式纹理中的值执行简单的alpha测试,其中包含1表示行,0表示不行.因此,要模拟固定功能点画,您将拥有16像素1分量1D纹理而不是16位图案.不要忘记将模式的包装模式设置为GL_REPEAT,关于过滤模式,我不确定,但我认为GL_NEAREST这是一个好主意.

但正如前面所说,如果你想使用渲染三角形glPolygonMode,它将无法以这种方式工作.相反,您必须调整几何着色器以接受三角形并为每个三角形生成3条线.


编辑:事实上,OpenGL 3直接支持着色器中的整数运算,这使我们可以完全放弃整个1D纹理方法,并使用实际的位模式直接工作.因此,稍微更改几何着色器以显示实际的屏幕尺寸图案坐标,而不进行标准化:

texCoord = 0.5 * length(winPos1-winPos0);
Run Code Online (Sandbox Code Playgroud)

在片段着色器中,我们只需要将一个位模式作为无符号整数(虽然与glLineStipple16位值相比为32位)和模式的拉伸因子,然后只取纹理坐标(好吧,实际上没有纹理,但没关系)模32来得到它的上的图案位置(那些明确uints为恼人的,但我的GLSL的编译器说之间的隐式转换intuint是邪恶的):

uniform uint pattern;
uniform float factor;

...
uint bit = uint(round(linePos/factor)) & 31U;
if((pattern & (1U<<bit)) == 0U)
    discard;
Run Code Online (Sandbox Code Playgroud)

  • 真棒.但是如果没有OpenGLES-2中的几何着色器,它可以完成吗? (2认同)

Rab*_*d76 5

要回答这个问题,我们首先要研究glLineStipple实际的作用。

参见图像,其中使用基本类型通过4个单独的线段绘制左侧的四边形GL_LINES
右边的圆由图元类型的连续多边形线绘制GL_LINE_STRIP

使用线段时,在每个线段处重新启动点画模式。该模式在每个图元上均已重列。
当使用线带时,点画图案将无缝应用于整个多边形。图案在顶点坐标之外无缝连续。
请注意,图案的长度在对角线处拉伸。这可能是实现的关键。

对于单独的线段,这根本不是很复杂,但是对于线带来说,情况会变得有些复杂。如果不知道该行的所有图元,则无法在着色器程序中计算该行的长度。即使所有原语都是已知的(例如SSBO),也必须在循环中进行计算。
另请参见带有OpenGL核心配置文件的虚线

无论如何,没有必要实现几何着色器。诀窍是要了解片段着色器中线段的起点。使用flat插值限定符很容易。

顶点着色器必须将规范化的设备坐标传递给片段着色器。一次使用默认插值,一次不使用(平面)插值。这导致在片段阴影中,第一个输入参数包含行上实际位置的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;
}
Run Code Online (Sandbox Code Playgroud)

除了变化的输入外,片段着色器还具有统一变量。u_resolution包含视口的宽度和高度。u_factoru_pattern是根据的参数的乘数和16位模式glLineStipple

这样就可以计算出从开始到实际片段的行长:

vec2  dir  = (vertPos.xy-startPos.xy) * u_resolution/2.0;
float dist = length(dir);
Run Code Online (Sandbox Code Playgroud)

通过该discard命令可以丢弃间隙上的碎片。

uint bit = uint(round(dist / u_factor)) & 15U;
if ((u_pattern & (1U<<bit)) == 0U)
    discard; 
Run Code Online (Sandbox Code Playgroud)

片段着色器:

#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);
}
Run Code Online (Sandbox Code Playgroud)

与使用几何着色器相比,此实现更容易,更短。该flat自插限定符支持GLSL 1.30GLSL ES 3.00。在此版本中,不支持几何着色器。
请参见上面的着色器生成的线条渲染。

着色器给出了正确的结果线段,但对于线条却不起作用,因为点画图案在每个顶点坐标处重新开始。
几何着色器甚至无法解决该问题。问题的这一部分仍未解决。

对于以下简单的演示程序,我使用GLFW API创建窗口,使用GLEW加载OpenGL和使用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;
}
Run Code Online (Sandbox Code Playgroud)