OpenGL线宽

时间:2010-08-14 16:54:32

标签: c++ opengl line

在我的OpenGL应用程序中,它不会让我绘制一个大于十个像素宽的线。有没有办法让它绘制超过十个像素?

void OGL_Renderer::drawLine(int x, int y, int x2, int y2, int r, int g, int b, int a, int line_width)
{   
    glColor4ub(r, g, b, a);

    glLineWidth((GLfloat)line_width);
    glBegin(GL_LINES);
    glVertex2i(x, y);
    glVertex2i(x2, y2);
    glEnd();
    glLineWidth(1.0f);
}

4 个答案:

答案 0 :(得分:7)

您可以尝试绘制四边形。将它设置为您希望线条较长的宽度,并将其设置为您需要的线宽,然后旋转并将其放置在线条所在的位置。

答案 1 :(得分:6)

我建议使用Shader,它会沿线带(甚至是线环)生成triangle primitives
任务是生成粗线带,并尽可能减少CPU和GPU开销。这意味着要避免在CPU和几何体着色器(或曲面细分着色器)上计算多边形。

该线的每个线段都由一个四边形组成,该四边形由2个三角形图元分别代表6个顶点组成。

0        2   5
 +-------+  +
 |     /  / |
 |   /  /   |
 | /  /     |
 +  +-------+
1   3        4

必须在其中找到斜接的线段之间,并且必须将四边形切成斜接。

+----------------+
|              / |
| segment 1  /   |
|          /     |
+--------+       |
         | segment 2
         |       |
         |       |
         +-------+

创建一个带有线带角点的数组。数组必须包含第一个和最后一个点两次。通过比较索引为0和数组的长度来确定数组的第一个和最后一个元素当然很容易,但是我们不想在着色器中进行任何额外的检查。
如果必须绘制线循环,则必须将最后一个点添加到数组的头部,将第一个点添加到数组的尾部。

点数组存储到Shader Storage Buffer Object中。我们利用了好处,即SSBO的最后一个变量可以是大小可变的数组。在旧版的OpenGL(或OpenGL ES)中,可以使用Uniform Buffer Object甚至是Texture

着色器不需要任何顶点坐标或属性。我们所需要知道的是线段的索引。坐标存储在缓冲区中。要找到索引,我们利用当前正在处理的顶点的索引(gl_VertexID)。
要绘制具有N个线段的线带,需要处理6*(N-1)个顶点。

我们必须创建一个“空” Vertex Array Object(没有任何顶点属性说明):

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

并绘制2*(N-1)三角形(6*(N-1)顶点):

glDrawArrays(GL_TRIANGLES, 0, 6*(N-1));

对于SSBO中的坐标数组,使用数据类型vec4(请相信我,您不想使用vec3):

layout(std430, binding = 0) buffer TVertex
{
   vec4 vertex[];
};

计算也属于顶点坐标的线段的索引以及两个三角形中点的索引:

int line_i = gl_VertexID / 6;
int tri_i  = gl_VertexID % 6;

由于我们正在绘制N-1线段,但是数组中的元素数为N+2,因此可以为每个顶点访问从vertex[line_t]vertex[line_t+3]的元素在顶点着色器中进行处理。
vertex[line_t+1]vertex[line_t+2]是线段的起点和终点坐标。要计算斜接,需要vertex[line_t]vertex[line_t+3]

线条的粗细应以像素单位(uniform float u_thickness)设置。坐标必须从模型空间转换为窗口空间。为此,必须知道视口的分辨率(uniform vec2 u_resolution)。不要忘记perspective divide。线条的绘制甚至可以在透视投影中使用。

vec4 va[4];
for (int i=0; i<4; ++i)
{
    va[i] = u_mvp * vertex[line_i+i];
    va[i].xyz /= va[i].w;
    va[i].xy = (va[i].xy + 1.0) * 0.5 * u_resolution;
}

如果前置点或后继点等于线段的起点或终点,则斜接计算甚至可以进行。在这种情况下,将线的末端垂直于其切线切割:

vec2 v_line   = normalize(va[2].xy - va[1].xy);
vec2 nv_line  = vec2(-v_line.y, v_line.x);
vec2 v_pred   = normalize(va[1].xy - va[0].xy);
vec2 v_succ   = normalize(va[3].xy - va[2].xy);
vec2 v_miter1 = normalize(nv_line + vec2(-v_pred.y, v_pred.x));
vec2 v_miter2 = normalize(nv_line + vec2(-v_succ.y, v_succ.x));

在最终的顶点着色器中,我们只需要根据v_miter1计算v_miter2tri_i。使用斜接,线段的法线向量和线粗度(u_thickness),可以计算顶点坐标:

vec4 pos;
if (tri_i == 0 || tri_i == 1 || tri_i == 3)
{
    vec2 v_pred  = normalize(va[1].xy - va[0].xy);
    vec2 v_miter = normalize(nv_line + vec2(-v_pred.y, v_pred.x));

    pos = va[1];
    pos.xy += v_miter * u_thickness * (tri_i == 1 ? -0.5 : 0.5) / dot(v_miter, nv_line);
}
else
{
    vec2 v_succ  = normalize(va[3].xy - va[2].xy);
    vec2 v_miter = normalize(nv_line + vec2(-v_succ.y, v_succ.x));

    pos = va[2];
    pos.xy += v_miter * u_thickness * (tri_i == 5 ? 0.5 : -0.5) / dot(v_miter, nv_line);
}

最后,必须将窗口坐标转换回剪辑空间坐标。从窗口空间转换为规范化的设备空间。视角鸿沟必须逆转:

pos.xy = pos.xy / u_resolution * 2.0 - 1.0;
pos.xyz *= pos.w;

着色器可以生成以下多边形(用glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)渲染)

(使用默认模式-glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

对于以下简单的演示程序,我使用了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 460

layout(std430, binding = 0) buffer TVertex
{
   vec4 vertex[]; 
};

uniform mat4  u_mvp;
uniform vec2  u_resolution;
uniform float u_thickness;

void main()
{
    int line_i = gl_VertexID / 6;
    int tri_i  = gl_VertexID % 6;

    vec4 va[4];
    for (int i=0; i<4; ++i)
    {
        va[i] = u_mvp * vertex[line_i+i];
        va[i].xyz /= va[i].w;
        va[i].xy = (va[i].xy + 1.0) * 0.5 * u_resolution;
    }

    vec2 v_line  = normalize(va[2].xy - va[1].xy);
    vec2 nv_line = vec2(-v_line.y, v_line.x);

    vec4 pos;
    if (tri_i == 0 || tri_i == 1 || tri_i == 3)
    {
        vec2 v_pred  = normalize(va[1].xy - va[0].xy);
        vec2 v_miter = normalize(nv_line + vec2(-v_pred.y, v_pred.x));

        pos = va[1];
        pos.xy += v_miter * u_thickness * (tri_i == 1 ? -0.5 : 0.5) / dot(v_miter, nv_line);
    }
    else
    {
        vec2 v_succ  = normalize(va[3].xy - va[2].xy);
        vec2 v_miter = normalize(nv_line + vec2(-v_succ.y, v_succ.x));

        pos = va[2];
        pos.xy += v_miter * u_thickness * (tri_i == 5 ? 0.5 : -0.5) / dot(v_miter, nv_line);
    }

    pos.xy = pos.xy / u_resolution * 2.0 - 1.0;
    pos.xyz *= pos.w;
    gl_Position = pos;
}
)";

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

out vec4 fragColor;

void main()
{
    fragColor = vec4(1.0);
}
)";

GLuint CreateSSBO(std::vector<glm::vec4> &varray)
{
    GLuint ssbo;
    glGenBuffers(1, &ssbo);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo );
    glBufferData(GL_SHADER_STORAGE_BUFFER, varray.size()*sizeof(*varray.data()), varray.data(), GL_STATIC_DRAW); 
    return ssbo;
}

int main(void)
{
    if ( glfwInit() == 0 )
        return 0;
    GLFWwindow *window = glfwCreateWindow( 800, 600, "GLFW OGL window", nullptr, nullptr );
    if ( window == nullptr )
    {
        glfwTerminate();
        retturn 0;
    }
    glfwMakeContextCurrent(window);
    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_thi  = glGetUniformLocation(program, "u_thickness");

    glUseProgram(program);
    glUniform1f(loc_thi, 20.0);

    GLushort pattern = 0x18ff;
    GLfloat  factor  = 2.0f;

    glm::vec4 p0(-1.0f, -1.0f, 0.0f, 1.0f);
    glm::vec4 p1(1.0f, -1.0f, 0.0f, 1.0f);
    glm::vec4 p2(1.0f, 1.0f, 0.0f, 1.0f);
    glm::vec4 p3(-1.0f, 1.0f, 0.0f, 1.0f);
    std::vector<glm::vec4> varray1{ p3, p0, p1, p2, p3, p0, p1 };
    GLuint ssbo1 = CreateSSBO(varray1);

    std::vector<glm::vec4> varray2;
    for (int u=-8; u <= 368; u += 8)
    {
        double a = u*M_PI/180.0;
        double c = cos(a), s = sin(a);
        varray2.emplace_back(glm::vec4((float)c, (float)s, 0.0f, 1.0f));
    }
    GLuint ssbo2 = CreateSSBO(varray2);

    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    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));
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo1);
        GLsizei N1 = (GLsizei)varray1.size()-2;
        glDrawArrays(GL_TRIANGLES, 0, 6*(N1-1));

        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));
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo2);
        GLsizei N2 = (GLsizei)varray2.size()-2;
        glDrawArrays(GL_TRIANGLES, 0, 6*(N2-1));

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

    return 0;
}

答案 2 :(得分:3)

啊,现在我理解你的意思了:

  1. 逐个画一个方格。
  2. 计算线的长度和方向
  3. 将其拉伸到x
  4. 的长度
  5. 翻译为startpos并旋转至line_orientation
  6. 或:

    1. 得到行的向量:v:(x2 - x1,y2 - y1)
    2. 规范化v:n 3-得到载体的正交(正常):o(在2d中容易)
    3. 从行的结尾添加和减去o,并从起点获得4个角点
    4. 用这些点绘制四边形。

答案 3 :(得分:3)

你不能这样做是有意义的。从glLineWidth参考:

通过使用参数GL_LINE_WIDTH_RANGE和GL_LINE_WIDTH_GRANULARITY调用glGet,可以查询支持的宽度范围和范围内支持的宽度之间的大小差异。