我只是想在屏幕上画一条线。我使用的是OpenGl 4.6。我找到的所有教程都使用了glVertexPointer
,据我所知,它已经过时了。
我知道如何使用缓冲区来绘制三角形,所以我尝试用一条线来绘制三角形。它不起作用,只是显示一个黑屏。(我使用GLFW和GLEW,我使用的是顶点+片段着色器,我已经在三角形上测试过了)
// Make line
float line[] = {
0.0, 0.0,
1.0, 1.0
};
unsigned int buffer; // The ID, kind of a pointer for VRAM
glGenBuffers(1, &buffer); // Allocate memory for the triangle
glBindBuffer(GL_ARRAY_BUFFER, buffer); // Set the buffer as the active array
glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float), line, GL_STATIC_DRAW); // Fill the buffer with data
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0); // Specify how the buffer is converted to vertices
glEnableVertexAttribArray(0); // Enable the vertex array
// Loop until the user closes the window
while (!glfwWindowShouldClose(window))
{
// Clear previous
glClear(GL_COLOR_BUFFER_BIT);
// Draw the line
glDrawArrays(GL_LINES, 0, 2);
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
我的方向是正确的吗?还是一种完全不同的方法是当前的最佳实践?
如果是,我如何修复代码?
1条答案
按热度按时间ztigrdn81#
问题在于
glBufferData
的调用。第二个参数是缓冲区的大小(以字节为单位)。由于顶点数组由2个坐标和2个组件组成,因此缓冲区的大小是4 * sizeof(float)
而不是2 * sizeof(float)
:glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float), line, GL_STATIC_DRAW);
但请注意,这仍然不是“现代”OpenGL。如果您想使用核心配置文件OpenGL Context,那么您必须使用Shader程序和Vertex Array Object
但是,如果您使用coreOpenGL context,并且向前兼容位已设置,则行的宽度(
glLineWidth
)不能大于1.0。请参阅OpenGL 4.6 API核心配置文件规范- E.2已弃用和删除的功能
宽线- * 线宽 * 值大于 1.0 将生成
INVALID_VALUE
错误。你必须找到一个不同的方法。
我建议使用Shader,它可以沿着线条(甚至是线条循环)生成三角形图元。
任务是生成粗线条,尽可能减少CPU和GPU开销,这意味着避免CPU和几何着色器(或镶嵌着色器)上的多边形计算。
线的每一段由分别由2个三角形基元和6个顶点表示的四边形组成。
在线段之间,必须找到斜接,并且必须将四边形切割为斜接。
创建一个包含线条带的角点的数组。第一个点和最后一个点定义线条带的起点和终点切线。因此,您需要在线条前添加一个点,在线条后添加一个点。当然,通过比较索引为0和数组的长度,识别数组的第一个元素和最后一个元素是很容易的。但我们不想在着色器中做任何额外的检查。
如果必须绘制一个线循环,则必须将最后一个点添加到数组头部,并将第一个点添加到数组尾部。
点的数组被存储到Shader Storage Buffer Object。我们利用了SSBO的最后一个变量可以是可变大小数组的好处。在较早版本的OpenGL(或OpenGL ES)中,可以使用Uniform Buffer Object甚至Texture。
着色器不需要任何顶点坐标或属性。我们只需要知道线段的索引。坐标存储在缓冲区中。为了找到索引,我们使用当前正在处理的顶点的索引(
gl_VertexID
)。要绘制具有
N
段的线条带,必须处理6*(N-1)
顶点。我们必须创建一个“空的”顶点数组对象(没有任何顶点属性规范):
并绘制
2*(N-1)
三角形(6*(N-1)
顶点):对于SSBO中的坐标数组,使用数据类型
vec4
(请相信我,您不会希望使用vec3
):计算顶点坐标所属的线段的索引以及2个三角形中的点的索引:
由于我们正在绘制
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
)。不要忘记透视分割。线条的绘制甚至可以在透视投影下进行。如果前一个或后一个点分别等于线段的起点和终点,斜接计算也会起作用。在这种情况下,直线的终点将垂直于其切线进行切割:
在最后的顶点着色器中,我们只需要根据
tri_i
计算v_miter1
或v_miter2
。使用斜接、线段的法向量和线宽(u_thickness
),可以计算顶点坐标:最后,窗口坐标必须转换回剪辑空间坐标。从窗口空间转换到规范化设备空间。透视分割必须反向:
使用
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
和glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
创建的多边形:演示程序使用GLFW API创建一个窗口,GLEW加载OpenGL和GLM -OpenGL Mathematics的数学。(我不提供代码的函数
CreateProgram
,它只是创建一个程序对象,从顶点着色器和片段着色器源代码):