OpenGL 3.1 中已弃用 glLineStipple

发布于 2024-11-08 04:47:51 字数 108 浏览 5 评论 0原文

glLineStipple 在最新的 OpenGL API 中已被弃用。 它被替换成什么? 如果不更换,怎样才能达到类似的效果呢? (我当然不想使用兼容性配置文件......)

glLineStipple has been deprecated in the latest OpenGL APIs.
What is it replaced with?
If not replaced, how can I get a similar effect?
(I don't want to use a compatibility profile of course...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

叫嚣ゝ 2024-11-15 04:47:51

抱歉,它还没有被任何东西取代。我想到的第一个模拟它的想法是几何着色器。您向几何着色器提供一条线,计算其屏幕空间长度,并基于此在其起始顶点和结束顶点之间生成可变数量的子线。


编辑:也许您还可以使用带有 alpha(或红色)通道的 1D 纹理,将图案编码为 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;
}

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

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();
}

在几何着色器中,我们取一条线并计算其屏幕空间长度(以像素为单位)。然后,我们将其除以点画图案纹理的大小,当模拟对 glLineStipple(factor, pattern) 的调用时,该大小将为 factor*16。这被视为第二条线端点的一维纹理坐标。

请注意,该纹理坐标必须进行线性插值(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;
}

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

但正如前面所说,如果你想使用glPolygonMode 渲染三角形,这种方式就行不通了。相反,您必须调整几何着色器以接受三角形并为每个三角形生成 3 条线。


编辑: 事实上,OpenGL 3 对着色器中整数运算的直接支持使我们能够完全放弃整个 1D 纹理方法,并直接使用实际的位模式进行工作。因此,几何着色器稍微改变以输出实际的屏幕大小图案坐标,而不进行标准化:

texCoord = 0.5 * length(winPos1-winPos0);

在片段着色器中,我们只将位模式作为无符号整数(尽管与glLineStipple相比是32位) > 的 16 位值)和图案的拉伸因子,只需将纹理坐标(好吧,实际上不再有纹理,但没关系)模 32 来获取它在图案上的位置(那些显式的 uint 很烦人,但我的 GLSL 编译器说 intuint 之间的隐式转换是邪恶的):

uniform uint pattern;
uniform float factor;

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

Sorry, it hasn't been replaced with anything. The first idea coming to my mind for emulating it would be the geometry shader. You feed the geometry shader with a line, compute its screen space length and based on that you generate a variable number of sub lines between its start and end vertex.


EDIT: Perhaps you could also use a 1D texture with the alpha (or red) channel encoding the pattern as 0.0 (no line) or 1.0 (line) and then have the lines texture coordinate go from 0 to 1 and in the fragment chader you make a simple alpha test, discarding fragments with alpha below some threshold. You can facilitate the geometry shader to generate your line texCoords, as otherwise you need different vertices for every line. This way you can also make the texCoord dependent on the screen space length of the line.

The whole thing get's more difficult if you draw triangles (using polygon mode GL_LINE). Then you have to do the triangle-line transformation yourself in the geometry shader, putting in triangles and putting out lines (that could also be a reason for deprecating polygon mode in the future, if it hasn't already).


EDIT: Although I believe this question abandomned, I have made a simple shader triple for the second approach. It's just a minimal solution, feel free to add custom features yourself. I haven't tested it because I lack the neccessary hardware, but you should get the point:

uniform mat4 modelViewProj;

layout(location=0) in vec4 vertex;

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

The vertex shader is a simple pass through.

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();
}

In the geometry shader we take a line and compute its screen space length in pixels. We then devide this by the size of the stipple pattern texture, which would be factor*16 when emulating a call to glLineStipple(factor, pattern). This is taken as 1D texture coordinate of the second line end point.

Note that this texture coordinate has to be interpolated linearly (noperspective interpolation specifier). The usual perpective-correct interpolation would cause the stipple pattern to "squeeze together" on farther away parts of the line, whereas we are explicitly working with screen-space values.

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;
}

The fragment shader now just performs a simple alpha test using the value from the pattern texture, which contains a 1 for line and a 0 for no line. So to emulate the fixed function stipple you would have a 16 pixel 1-component 1D texture instead of a 16bit pattern. Don't forget to set the pattern's wrapping mode to GL_REPEAT, about the filtering mode I'm not that sure, but I suppose GL_NEAREST would be a good idea.

But as said earlier, if you want to render triangles using glPolygonMode, it won't work this way. Instead you have to adapt the geometry shader to accept triangles and generate 3 lines for each triangle.


EDIT: In fact OpenGL 3's direct support for integer operations in shaders allows us to completely drop this whole 1D-texture approach and work straight-forward with an actual bit-pattern. Thus the geometry shader is slightly changed to put out the actual screen-size pattern coordinate, without normalization:

texCoord = 0.5 * length(winPos1-winPos0);

In the fragment shader we then just take a bit pattern as unsigned integer (though 32-bit in contrast to glLineStipple's 16-bit value) and the stretch factor of the pattern and just take the texture coordinate (well, no texture anymore actually, but nevermind) modulo 32 to get it's position on the pattern (those explicit uints are annoying, but my GLSL compiler says implicit conversions between int and uint are evil):

uniform uint pattern;
uniform float factor;

...
uint bit = uint(round(linePos/factor)) & 31U;
if((pattern & (1U<<bit)) == 0U)
    discard;
爱本泡沫多脆弱 2024-11-15 04:47:51

要回答这个问题,我们首先要调查一下 glLineStipple 实际上确实如此。

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

< /a>

使用线段时,点画图案从每个线段开始。该模式在每个基元处重新启动
使用线条时,点画图案将无缝应用于整个多边形。超越顶点坐标无缝连续的图案。
请注意,图案的长度在对角线上被拉伸。这可能是实施的关键。

对于单独的线段,这根本不是很复杂,但是对于线带,事情会变得有点复杂。如果不知道线条的所有图元,则无法在着色器程序中计算线条的长度。即使所有原语都是已知的(例如 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;
}

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

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

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; 

片段着色器:

#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);
}

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

< /a>

着色器给出正确的结果线段,但对于线带失败,因为点画图案在每个顶点坐标处重新启动。
这个问题甚至不能通过几何着色器来解决。这部分问题仍然没有解决。

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


另请参阅
OpenGL3 中的虚线?
OpenGL ES - 虚线

To answer this question, we've to investigate first, what glLineStipple actually does.

See the image, where the quad at the left is drawn by 4 separated line segments using the primitive type GL_LINES.
The circle at the right is drawn by a consecutive polygon line, using the primitive type GL_LINE_STRIP.

When using line segments, the stipple pattern started at each segment. The pattern is restarted at each primitive.
When using a line strip, then the stipple pattern is applied seamless to the entire polygon. A pattern seamlessly continuous beyond vertex coordinates.
Be aware that the length of the pattern is stretched at the diagonals. This is possibly the key to the implementation.

For separate line segments, this is not very complicated at all, but for line strips things get a bit more complicated. 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.
See also Dashed lines with OpenGL core profile.

Anyway, it is not necessary to implement a geometry shader. The trick is to know the start of the line segment in the fragment shader. This easy by using a flat interpolation qualifier.

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;
}

Additionally the varying inputs, the fragment shader has uniform variables. u_resolution contains the width and the height of the viewport. u_factor and u_pattern are the multiplier and the 16 bit pattern according to the parameters of glLineStipple.

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);

And fragment on the gap can be discarded, by the discard command.

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

Fragment shader:

#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);
}

This implementation is much easier and shorter, then using geometry shaders. The flat interpolation qualifier is supported since GLSL 1.30 and GLSL ES 3.00. In this version geometry shaders are not supported.
See the line rendering which was generated with the above shader.

The shader gives a proper result line segments, but fails for line strips, since the stipple pattern is restarted at each vertex coordinate.
The issue can't even be solved by a geometry shader. This part of the question remains still unresolved.

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 <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;
}


See also
Dashed line in OpenGL3?
OpenGL ES - Dashed Lines

夏天碎花小短裙 2024-11-15 04:47:51

由于我花了一些功夫(没有双关语)才把它做好,我认为如果我分享我基于 Christian Rau 版本的一组点画着色器的实现,这可能对其他人有用。
为了控制图案密度,片段着色器需要视口每单位长度的图案数 nPatterns - 而不是设置一个因子。还包括可选的剪切平面功能。
剩下的主要是注释和清理。

免费用于所有意图和目的。

顶点着色器:

#version 330

in vec4 vertex;

void main(void)
{
    // just a pass-through
    gl_Position = vertex;
}

几何着色器:

#version 330

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

uniform mat4 pvmMatrix;
uniform mat4 mMatrix;
uniform mat4 vMatrix;


out vec3 vPosition;  // passed to the fragment shader for plane clipping
out float texCoord;  // passed to the fragment shader for stipple pattern

void main(void)
{
    // to achieve uniform pattern density whatever the line orientation
    // the upper texture coordinate is made proportional to the line's length
    vec3 pos0 = gl_in[0].gl_Position.xyz;
    vec3 pos1 = gl_in[1].gl_Position.xyz;
    float max_u_texture = length(pos1 - pos0);

    // Line Start
    gl_Position = pvmMatrix * (gl_in[0].gl_Position);
    texCoord = 0.0;
    // depth position for clip plane
    vec4 vsPos0 = vMatrix * mMatrix * gl_Position;
    vPosition = vsPos0.xyz / vsPos0.w;
    EmitVertex();  // one down, one to go

    // Line End
    gl_Position = pvmMatrix * (gl_in[1].gl_Position);
    texCoord = max_u_texture;
    // depth position for clip plane
    vec4 vsPos1 = vMatrix * mMatrix * gl_Position;
    vPosition = vsPos0.xyz / vsPos0.w;
    EmitVertex();

    // done
    EndPrimitive();
}

片段着色器:

#version 330

uniform int pattern;   // an integer between 0 and 0xFFFF representing the bitwise pattern
uniform int nPatterns; // the number of patterns/unit length of the viewport, typically 200-300 for good pattern density
uniform vec4 color;
uniform vec4 clipPlane0; // defined in view-space

in float texCoord;

in vec3 vPosition;

layout(location=0) out vec4 fragColor;

void main(void)
{
    // test vertex postion vs. clip plane position (optional)
    if (vPosition.z > clipPlane0.w) {
        discard;
        return;
    }

    // use 4 bytes for the masking pattern
    // map the texture coordinate to the interval [0,2*8[
    uint bitpos = uint(round(texCoord * nPatterns)) % 16U;
    // move a unit bit 1U to position bitpos so that
    // bit is an integer between 1 and 1000 0000 0000 0000 = 0x8000
    uint bit = (1U << bitpos);

    // test the bit against the masking pattern
    //  Line::SOLID:       pattern = 0xFFFF;  // = 1111 1111 1111 1111 = solid pattern
    //  Line::DASH:        pattern = 0x3F3F;  // = 0011 1111 0011 1111
    //  Line::DOT:         pattern = 0x6666;  // = 0110 0110 0110 0110
    //  Line::DASHDOT:     pattern = 0xFF18;  // = 1111 1111 0001 1000
    //  Line::DASHDOTDOT:  pattern = 0x7E66;  // = 0111 1110 0110 0110
    uint up = uint(pattern);

    // discard the bit if it doesn't match the masking pattern
    if ((up & bit) == 0U) discard;

    fragColor = color;
}

Since I struggled a bit (no pun intended) to get it right, I thought it could be useful to others if I shared my implementation of a set of stippling shaders based on Christian Rau's version.
To control pattern density, the fragment shader requires the number of patterns nPatterns per unit length of the viewport - instead of setting a factor. Also included is an optional clipping plane feature.
The rest is mainly commenting and cleaning.

Free to use to all intents and purposes.

The vertex shader:

#version 330

in vec4 vertex;

void main(void)
{
    // just a pass-through
    gl_Position = vertex;
}

The geometry shader:

#version 330

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

uniform mat4 pvmMatrix;
uniform mat4 mMatrix;
uniform mat4 vMatrix;


out vec3 vPosition;  // passed to the fragment shader for plane clipping
out float texCoord;  // passed to the fragment shader for stipple pattern

void main(void)
{
    // to achieve uniform pattern density whatever the line orientation
    // the upper texture coordinate is made proportional to the line's length
    vec3 pos0 = gl_in[0].gl_Position.xyz;
    vec3 pos1 = gl_in[1].gl_Position.xyz;
    float max_u_texture = length(pos1 - pos0);

    // Line Start
    gl_Position = pvmMatrix * (gl_in[0].gl_Position);
    texCoord = 0.0;
    // depth position for clip plane
    vec4 vsPos0 = vMatrix * mMatrix * gl_Position;
    vPosition = vsPos0.xyz / vsPos0.w;
    EmitVertex();  // one down, one to go

    // Line End
    gl_Position = pvmMatrix * (gl_in[1].gl_Position);
    texCoord = max_u_texture;
    // depth position for clip plane
    vec4 vsPos1 = vMatrix * mMatrix * gl_Position;
    vPosition = vsPos0.xyz / vsPos0.w;
    EmitVertex();

    // done
    EndPrimitive();
}

The fragment shader:

#version 330

uniform int pattern;   // an integer between 0 and 0xFFFF representing the bitwise pattern
uniform int nPatterns; // the number of patterns/unit length of the viewport, typically 200-300 for good pattern density
uniform vec4 color;
uniform vec4 clipPlane0; // defined in view-space

in float texCoord;

in vec3 vPosition;

layout(location=0) out vec4 fragColor;

void main(void)
{
    // test vertex postion vs. clip plane position (optional)
    if (vPosition.z > clipPlane0.w) {
        discard;
        return;
    }

    // use 4 bytes for the masking pattern
    // map the texture coordinate to the interval [0,2*8[
    uint bitpos = uint(round(texCoord * nPatterns)) % 16U;
    // move a unit bit 1U to position bitpos so that
    // bit is an integer between 1 and 1000 0000 0000 0000 = 0x8000
    uint bit = (1U << bitpos);

    // test the bit against the masking pattern
    //  Line::SOLID:       pattern = 0xFFFF;  // = 1111 1111 1111 1111 = solid pattern
    //  Line::DASH:        pattern = 0x3F3F;  // = 0011 1111 0011 1111
    //  Line::DOT:         pattern = 0x6666;  // = 0110 0110 0110 0110
    //  Line::DASHDOT:     pattern = 0xFF18;  // = 1111 1111 0001 1000
    //  Line::DASHDOTDOT:  pattern = 0x7E66;  // = 0111 1110 0110 0110
    uint up = uint(pattern);

    // discard the bit if it doesn't match the masking pattern
    if ((up & bit) == 0U) discard;

    fragColor = color;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文