OpenGL3中的虚线? [英] Dashed line in OpenGL3?

查看:389
本文介绍了OpenGL3中的虚线?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在将一个旧的OpenGL 1.1应用程序移植到OpenGL 3.0中,该应用程序使用线框模型.

I'm currently porting an old OpenGL 1.1 application which makes use of wireframe models to OpenGL 3.0.

在1.1中,以下代码用于创建虚线:

In 1.1 following code is used to create a dashed line:

glPushAttrib(GL_ENABLE_BIT); 
glLineStipple(1, 0x0F0F);
glEnable(GL_LINE_STIPPLE);

通常,将参数压入堆栈以影响随后的所有绘图操作.

Here as usual the parameters are pushed to the stack in order to influence all following drawing operations.

我的问题:在不再使用该堆栈的OpenGL3中,这是怎么做的?如何设置虚线(可能是在将坐标移交给glBufferData()之前?)?

My question: how is this done in OpenGL3 where this stack is no longer used? How can I set up my lines to be dashed (probably before handing the coordinates over to glBufferData()?

推荐答案

对于单独的线段,这一点都不复杂.例如,绘制 GL_LINES 原语.
诀窍是要了解片段着色器中线段的起点.通过使用 flat 插值限定符,这非常简单.

For separate line segments, this is not very complicated at all. For example drawing the GL_LINES primitives.
The trick is to know the start of the line segment in the fragment shader. This is quite simple by using the flat interpolation qualifier.

顶点着色器必须将规范化的设备坐标传递给片段着色器.一次使用默认插值,一次不使用(flat)插值.这导致在片段阴影中,第一个输入参数包含行上实际位置的NDC坐标,而后一个行起始处的NDC坐标.

The vertex shader has to pass the normalized device coordinate to the fragment shader. Once with default interpolation and once with no (flat) interpolation. This causes that in the fragment shade, the first input parameter contains the NDC coordinate of the actual position on the line and the later the NDC coordinate of the start of the line.

#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_dashSize包含行的长度,u_gapSize包含像素的间隙的长度.

Additionally to the varying inputs, the fragment shader has uniform variables. u_resolution contains the width and the height of the viewport. u_dashSize contains the length of the line and u_gapSize the length of a gap in pixel.

因此可以计算从开始到实际片段的行长:

So the length of the line from the start to the actual fragment can be calculated:

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

discard 命令.

if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
    discard; 

片段着色器:

#version 330

flat in vec3 startPos;
in vec3 vertPos;

out vec4 fragColor;

uniform vec2  u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;

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

    if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
        discard; 
    fragColor = vec4(1.0);
}

对于以下简单的演示程序,我使用了 GLFW API创建了一个窗口, GLEW 用于加载OpenGL和

For the following simple demo program I've used the GLFW API for creating a window, GLEW for loading OpenGL and GLM -OpenGL Mathematics for the math. I don't provide the code for the function CreateProgram, which just creates a program object, from the vertex shader and fragment shader source code:

#include <GL/glew.h>
#include <GL/gl.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <GLFW/glfw3.h>

#include <vector>

#define _USE_MATH_DEFINES
#include <math.h>

int main(void)
{
    if (glfwInit() == GLFW_FALSE)
        return 0;
    GLFWwindow *window = glfwCreateWindow(400, 300, "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_dash = glGetUniformLocation(program, "u_dashSize");
    GLint loc_gap  = glGetUniformLocation(program, "u_gapSize");

    glUseProgram(program);
    glUniform1f(loc_dash, 10.0f);
    glUniform1f(loc_gap, 10.0f);

    std::vector<float> varray{
        -1, -1, -1,   1, -1, -1,   1, 1, -1,   -1, 1, -1,
        -1, -1,  1,   1, -1,  1,   1, 1,  1,   -1, 1,  1
    };
    std::vector<unsigned int> iarray{
        0, 1, 1, 2, 2, 3, 3, 0, 
        4, 5, 5, 6, 6, 7, 7, 4,
        0, 4, 1, 5, 2, 6, 3, 7
    };

    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); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, iarray.size()*sizeof(*iarray.data()), iarray.data(), GL_STATIC_DRAW);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    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]);
            project = glm::perspective(glm::radians(90.0f), (float)w/(float)h, 0.1f, 10.0f);
            glUniform2f(loc_res, (float)w, (float)h);
        }

        static float angle = 1.0f;
        glm::mat4 modelview( 1.0f );
        modelview = glm::translate(modelview, glm::vec3(0.0f, 0.0f, -3.0f) );
        modelview = glm::rotate(modelview, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
        modelview = glm::rotate(modelview, glm::radians(angle*0.5f), glm::vec3(0.0f, 1.0f, 0.0f));
        angle += 0.5f;
        glm::mat4 mvp = project * modelview;

        glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
        glClear(GL_COLOR_BUFFER_BIT);
        glDrawElements(GL_LINES, (GLsizei)iarray.size(), GL_UNSIGNED_INT, nullptr);

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

    return 0;
}


如果目标是沿多边形绘制虚线,则情况会变得有些复杂.例如,绘制 GL_LINE_STRIP 原语.


Things get a bit more complicated, if the goal is to draw a dashed line along a polygon. For example drawing a GL_LINE_STRIP primitive.

如果不知道该行的所有图元,则无法在着色器程序中计算该行的长度.即使所有原语都是已知的(例如SSBO),也必须在循环中进行计算.
我决定向着色器程序添加一个附加属性,其中包含从线的起点到顶点坐标的距离". 距离"是指投影到视口上的多边形的长度.

The length of the line cannot be calculated in the shader program, without knowing all the primitives of the line. Even if all the primitives would be known (e.g. SSBO), then the calculation would have to be done in a loop.
I decided to add an additional attribute to the shader program, which contains the "distance" from the start of the line to the vertex coordinate. By "distance" is meant the length of the projected polygon on to the viewport.

这将导致顶点着色器和片段着色器更加简单:

This causes that the vertex shader and fragment shader is even simpler:

顶点着色器:

#version 330

layout (location = 0) in vec3 inPos;
layout (location = 1) in float inDist;

out float dist;

uniform mat4 u_mvp;

void main()
{
    dist        = inDist;
    gl_Position = u_mvp * vec4(inPos, 1.0);
}

片段着色器:

#version 330

in float dist;

out vec4 fragColor;

uniform vec2  u_resolution;
uniform float u_dashSize;
uniform float u_gapSize;

void main()
{
    if (fract(dist / (u_dashSize + u_gapSize)) > u_dashSize/(u_dashSize + u_gapSize))
        discard; 
    fragColor = vec4(1.0);
}

在演示程序中,inDist属性是在CPU上计算的.每个顶点坐标都通过模型,视图,投影矩阵进行转换.最后,它从规范化的设备空间转换为窗口空间.计算线带相邻坐标之间的XY距离,并沿着线带求和长度,并将其分配给相应的属性值:

In the demo program the inDist attribute is calculated on the CPU. Each vertex coordinate is transformed by the model, view, projection matrix. Finally it is transformed from normalized device space to window space. The XY distance between adjacent coordinates of the line strip is calculated and the lengths are summed along the line strip and assigned to the corresponding attribute value:

int w = [...], h = [...];               // window widht and height
glm::mat4 mpv = [...];                  // model view projection matrix
std::vector<glm::vec3> varray{ [...] }; // array of vertex 

std::vector<float> darray(varray.size(), 0.0f); // distance attribute - has to be computed

glm::mat4 wndmat = glm::scale(glm::mat4(1.0f), glm::vec3((float)w/2.0f, (float)h/2.0f, 1.0f));
wndmat = glm::translate(wndmat, glm::vec3(1.0f, 1.0f, 0.0f));

glm::vec2 vpPt(0.0f, 0.0f);
float dist = 0.0f;
for (size_t i=0; i < varray.size(); ++i)
{
    darray[i] = dist;
    glm::vec4 clip = mvp * glm::vec4(varray[i], 1.0f);
    glm::vec4 ndc  = clip / clip.w;
    glm::vec4 vpC  = wndmat * ndc;
    float len = i==0 ? 0.0f :  glm::length(vpPt - glm::vec2(vpC));
    vpPt = glm::vec2(vpC);
    dist += len;
}

演示程序:

int main(void)
{
    if (glfwInit() == GLFW_FALSE)
        return 0;
    GLFWwindow *window = glfwCreateWindow(800, 600, "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_dash = glGetUniformLocation(program, "u_dashSize");
    GLint loc_gap  = glGetUniformLocation(program, "u_gapSize");

    glUseProgram(program);
    glUniform1f(loc_dash, 10.0f);
    glUniform1f(loc_gap, 10.0f);

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

    GLuint bo[2], vao;
    glGenBuffers(2, bo);
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0); 
    glEnableVertexAttribArray(1); 
    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); 
    glBindBuffer(GL_ARRAY_BUFFER, bo[1] );
    glBufferData(GL_ARRAY_BUFFER, darray.size()*sizeof(*darray.data()), darray.data(), GL_STATIC_DRAW);
    glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0); 

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 5.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    glm::mat4 project, wndmat;
    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]);
            project = glm::perspective(glm::radians(90.0f), (float)w/(float)h, 0.1f, 10.0f);
            glUniform2f(loc_res, (float)w, (float)h);
            wndmat = glm::scale(glm::mat4(1.0f), glm::vec3((float)w/2.0f, (float)h/2.0f, 1.0f));
            wndmat = glm::translate(wndmat, glm::vec3(1.0f, 1.0f, 0.0f));
        }

        static float angle = 1.0f;
        glm::mat4 modelview( 1.0f );
        modelview = glm::translate(modelview, glm::vec3(0.0f, 0.0f, -2.0f) );
        modelview = glm::rotate(modelview, glm::radians(angle), glm::vec3(1.0f, 0.0f, 0.0f));
        modelview = glm::rotate(modelview, glm::radians(angle*0.5f), glm::vec3(0.0f, 1.0f, 0.0f));
        angle += 0.5f;
        glm::mat4 mvp = project * modelview;

        glm::vec2 vpPt(0.0f, 0.0f);
        float dist = 0.0f;
        for (size_t i=0; i < varray.size(); ++i)
        {
            darray[i] = dist;
            glm::vec4 clip = mvp * glm::vec4(varray[i], 1.0f);
            glm::vec4 ndc  = clip / clip.w;
            glm::vec4 vpC  = wndmat * ndc;
            float len = i==0 ? 0.0f :  glm::length(vpPt - glm::vec2(vpC));
            vpPt = glm::vec2(vpC);
            dist += len;
        }
        glBufferSubData(GL_ARRAY_BUFFER, 0, darray.size()*sizeof(*darray.data()), darray.data());

        glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
        glClear(GL_COLOR_BUFFER_BIT);
        glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)varray.size());

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

    return 0;
}


另请参阅
在OpenGL 3.1中已弃用glLineStipple
OpenGL ES-虚线


See also
glLineStipple deprecated in OpenGL 3.1
OpenGL ES - Dashed Lines

这篇关于OpenGL3中的虚线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆