【发布时间】:2011-08-26 09:46:45
【问题描述】:
glLineStipple 在最新的 OpenGL API 中已被弃用。
它用什么代替?
如果不更换,如何获得类似的效果?
(我当然不想使用兼容性配置文件......)
【问题讨论】:
-
并没有真正放弃,学习和玩opengl是一种爱好,我实际上很少有时间花在它上面。但我很乐意为你的答案投票!
标签: opengl glsl shader line opengl-4
glLineStipple 在最新的 OpenGL API 中已被弃用。
它用什么代替?
如果不更换,如何获得类似的效果?
(我当然不想使用兼容性配置文件......)
【问题讨论】:
标签: opengl glsl shader line opengl-4
对不起,它没有被任何东西取代。我想到的第一个模拟它的想法是几何着色器。您向几何着色器提供一条线,计算其屏幕空间长度,并在此基础上在其开始和结束顶点之间生成可变数量的子线。
编辑:也许您还可以使用 alpha(或红色)通道将图案编码为 0.0(无线)或 1.0(线)的 1D 纹理,然后让线条纹理坐标变为从 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);
然后,在片段着色器中,我们只需将位模式作为无符号整数(尽管是 32 位,而 glLineStipple 的 16 位值)和模式的拉伸因子,然后仅获取纹理坐标(嗯, 实际上不再有纹理,但没关系)模 32 得到它在模式上的位置(那些显式的 uints 很烦人,但我的 GLSL 编译器说 int 和 uint 之间的隐式转换是邪恶的):
uniform uint pattern;
uniform float factor;
...
uint bit = uint(round(linePos/factor)) & 31U;
if((pattern & (1U<<bit)) == 0U)
discard;
【讨论】:
glLineStipple。
要回答这个问题,我们首先要调查glLineStipple 的实际作用。
看图,左边的四边形是用基本类型GL_LINES的4个分开的线段绘制的。
右边的圆圈是由一条连续的多边形线绘制的,使用基本类型GL_LINE_STRIP。
使用线段时,点画图案从每个线段开始。模式在每个基元处重新启动。
使用线条时,点画图案将无缝应用于整个多边形。超出顶点坐标的无缝连续模式。
请注意,图案的长度在对角线处被拉伸。这可能是实现的关键。
对于单独的线段,这根本不是很复杂,但是对于线条来说,事情变得有点复杂。在不知道线的所有图元的情况下,无法在着色器程序中计算线的长度。即使已知所有原语(例如 SSBO),也必须在循环中完成计算。
另见Dashed lines with OpenGL core profile。
无论如何,没有必要实现几何着色器。诀窍是知道片段着色器中线段的起点。这很容易通过使用 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_factor 和u_pattern 是乘数和根据glLineStipple 的参数的16 位模式。
所以可以计算出从开始到实际片段的线的长度:
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);
}
与使用几何着色器相比,此实现更简单、更短。从GLSL 1.30 和GLSL ES 3.00 开始支持flat 插值限定符。此版本不支持几何着色器。
查看使用上述着色器生成的线条渲染。
着色器给出了正确的线段结果,但线段失败,因为点画图案在每个顶点坐标处重新开始。
这个问题甚至不能通过几何着色器来解决。这部分问题仍未解决。
对于以下简单的演示程序,我使用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;
}
【讨论】:
由于我为弄好它而费了一番功夫(不是双关语),我认为如果我分享我的一组基于 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;
}
【讨论】: