在现代 OpenGL 中画一条线
Drawing a line in modern OpenGL
我只想在屏幕上画一条线。我正在使用 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();
}
我的方向是正确的,还是完全不同的方法是当前的最佳实践?
如果是,我该如何修复我的代码?
问题是对 glBufferData
的调用。第二个参数是以字节为单位的缓冲区大小。由于顶点数组由 2 个坐标和 2 个分量组成,因此缓冲区的大小是 4 * sizeof(float)
而不是 2 * sizeof(float)
:
glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float), line, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(float), line, GL_STATIC_DRAW);
但请注意,这仍然不是“现代”OpenGL。如果你想使用核心配置文件OpenGL Context, then you have to use a Shader program and a Vertex Array Object
但是,如果您使用的是 核心 OpenGL context and the forward compatibility bit is set, the width of a line (glLineWidth
),则不能大于 1.0。
参见 OpenGL 4.6 API Core Profile Specification - E.2 Deprecated and Removed Features
Wide lines - LineWidth values greater than 1.0 will generate an INVALID_VALUE
error.
你必须找到不同的方法。
我建议使用 Shader, which generates triangle primitives 沿线带(甚至线环)。
任务是生成粗线带,尽可能减少 CPU 和 GPU 开销。这意味着要避免在 CPU 和几何着色器(或曲面细分着色器)上计算多边形。
直线的每段由一个四边形组成,分别由 2 个三角形基元和 6 个顶点表示。
0 2 5
+-------+ +
| / / |
| / / |
| / / |
+ +-------+
1 3 4
在线段之间必须找到斜接,并且必须将四边形切割成斜接。
+----------------+
| / |
| segment 1 / |
| / |
+--------+ |
| segment 2
| |
| |
+-------+
用线条的角点创建一个数组。第一个点和最后一个点定义线条的起点和终点切线。所以需要在线前加1点,在线后加1点。当然这很容易,通过比较索引为 0 和数组的长度来识别数组的第一个和最后一个元素,但是我们不想在着色器中做任何额外的检查。
如果必须绘制线循环,则必须将最后一个点添加到数组头部,将第一个点添加到数组尾部。
点数组存储到一个Shader Storage Buffer Object. We use the benefit, that the last variable of the SSBO can be an array of variable size. In older versions of OpenGL (or OpenGL ES) a Uniform Buffer Object or even a 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
(请相信我,你不会想使用):
layout(std430, binding = 0) buffer TVertex
{
vec4 vertex[];
};
计算顶点坐标所属的线段的索引和2个三角形中点的索引:
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
)。坐标必须从模型 space 转换为 window space。为此,必须知道视口的分辨率 (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));
在最终的顶点着色器中,我们只需要根据 tri_i
计算 v_miter1
或 v_miter2
。有了斜接,线段的法向量和线的粗细(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);
}
最后 window 坐标必须转换回剪辑 space 坐标。从 window space 转换为标准化设备 space。透视鸿沟必须扭转:
pos.xy = pos.xy / u_resolution * 2.0 - 1.0;
pos.xyz *= pos.w;
使用 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
和 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
创建的多边形:
使用 GLFW API for creating a window, GLEW for loading OpenGL and 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);
}
)";
// main
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 )
throw std::runtime_error( "error initializing glfw" );
GLFWwindow *window = glfwCreateWindow( 800, 600, "GLFW OGL window", nullptr, nullptr );
if (window == nullptr)
{
glfwTerminate();
throw std::runtime_error("error initializing window");
}
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK)
throw std::runtime_error("error initializing glew");
OpenGL::CContext::TDebugLevel debug_level = OpenGL::CContext::TDebugLevel::all;
OpenGL::CContext context;
context.Init( debug_level );
GLuint program = OpenGL::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;
std::vector<glm::vec4> varray;
varray.emplace_back(glm::vec4(0.0f, -1.0f, 0.0f, 1.0f));
varray.emplace_back(glm::vec4(1.0f, -1.0f, 0.0f, 1.0f));
for (int u=0; u <= 90; u += 10)
{
double a = u*M_PI/180.0;
double c = cos(a), s = sin(a);
varray.emplace_back(glm::vec4((float)c, (float)s, 0.0f, 1.0f));
}
varray.emplace_back(glm::vec4(-1.0f, 1.0f, 0.0f, 1.0f));
for (int u = 90; u >= 0; u -= 10)
{
double a = u * M_PI / 180.0;
double c = cos(a), s = sin(a);
varray.emplace_back(glm::vec4((float)c-1.0f, (float)s-1.0f, 0.0f, 1.0f));
}
varray.emplace_back(glm::vec4(1.0f, -1.0f, 0.0f, 1.0f));
varray.emplace_back(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
GLuint ssbo = CreateSSBO(varray);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo);
GLsizei N = (GLsizei)varray.size() - 2;
glClearColor(0.0f, 0.0f, 0.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]);
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;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp1));
glDrawArrays(GL_TRIANGLES, 0, 6*(N-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;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp2));
glDrawArrays(GL_TRIANGLES, 0, 6*(N-1));
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
我只想在屏幕上画一条线。我正在使用 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();
}
我的方向是正确的,还是完全不同的方法是当前的最佳实践?
如果是,我该如何修复我的代码?
问题是对 glBufferData
的调用。第二个参数是以字节为单位的缓冲区大小。由于顶点数组由 2 个坐标和 2 个分量组成,因此缓冲区的大小是 4 * sizeof(float)
而不是 2 * sizeof(float)
:
glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float), line, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(float), line, GL_STATIC_DRAW);
但请注意,这仍然不是“现代”OpenGL。如果你想使用核心配置文件OpenGL Context, then you have to use a Shader program and a Vertex Array Object
但是,如果您使用的是 核心 OpenGL context and the forward compatibility bit is set, the width of a line (glLineWidth
),则不能大于 1.0。
参见 OpenGL 4.6 API Core Profile Specification - E.2 Deprecated and Removed Features
Wide lines - LineWidth values greater than 1.0 will generate an
INVALID_VALUE
error.
你必须找到不同的方法。
我建议使用 Shader, which generates triangle primitives 沿线带(甚至线环)。
任务是生成粗线带,尽可能减少 CPU 和 GPU 开销。这意味着要避免在 CPU 和几何着色器(或曲面细分着色器)上计算多边形。
直线的每段由一个四边形组成,分别由 2 个三角形基元和 6 个顶点表示。
0 2 5
+-------+ +
| / / |
| / / |
| / / |
+ +-------+
1 3 4
在线段之间必须找到斜接,并且必须将四边形切割成斜接。
+----------------+
| / |
| segment 1 / |
| / |
+--------+ |
| segment 2
| |
| |
+-------+
用线条的角点创建一个数组。第一个点和最后一个点定义线条的起点和终点切线。所以需要在线前加1点,在线后加1点。当然这很容易,通过比较索引为 0 和数组的长度来识别数组的第一个和最后一个元素,但是我们不想在着色器中做任何额外的检查。
如果必须绘制线循环,则必须将最后一个点添加到数组头部,将第一个点添加到数组尾部。
点数组存储到一个Shader Storage Buffer Object. We use the benefit, that the last variable of the SSBO can be an array of variable size. In older versions of OpenGL (or OpenGL ES) a Uniform Buffer Object or even a 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
(请相信我,你不会想使用
layout(std430, binding = 0) buffer TVertex
{
vec4 vertex[];
};
计算顶点坐标所属的线段的索引和2个三角形中点的索引:
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
)。坐标必须从模型 space 转换为 window space。为此,必须知道视口的分辨率 (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));
在最终的顶点着色器中,我们只需要根据 tri_i
计算 v_miter1
或 v_miter2
。有了斜接,线段的法向量和线的粗细(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);
}
最后 window 坐标必须转换回剪辑 space 坐标。从 window space 转换为标准化设备 space。透视鸿沟必须扭转:
pos.xy = pos.xy / u_resolution * 2.0 - 1.0;
pos.xyz *= pos.w;
使用 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
和 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
创建的多边形:
使用 GLFW API for creating a window, GLEW for loading OpenGL and 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);
}
)";
// main
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 )
throw std::runtime_error( "error initializing glfw" );
GLFWwindow *window = glfwCreateWindow( 800, 600, "GLFW OGL window", nullptr, nullptr );
if (window == nullptr)
{
glfwTerminate();
throw std::runtime_error("error initializing window");
}
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK)
throw std::runtime_error("error initializing glew");
OpenGL::CContext::TDebugLevel debug_level = OpenGL::CContext::TDebugLevel::all;
OpenGL::CContext context;
context.Init( debug_level );
GLuint program = OpenGL::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;
std::vector<glm::vec4> varray;
varray.emplace_back(glm::vec4(0.0f, -1.0f, 0.0f, 1.0f));
varray.emplace_back(glm::vec4(1.0f, -1.0f, 0.0f, 1.0f));
for (int u=0; u <= 90; u += 10)
{
double a = u*M_PI/180.0;
double c = cos(a), s = sin(a);
varray.emplace_back(glm::vec4((float)c, (float)s, 0.0f, 1.0f));
}
varray.emplace_back(glm::vec4(-1.0f, 1.0f, 0.0f, 1.0f));
for (int u = 90; u >= 0; u -= 10)
{
double a = u * M_PI / 180.0;
double c = cos(a), s = sin(a);
varray.emplace_back(glm::vec4((float)c-1.0f, (float)s-1.0f, 0.0f, 1.0f));
}
varray.emplace_back(glm::vec4(1.0f, -1.0f, 0.0f, 1.0f));
varray.emplace_back(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f));
GLuint ssbo = CreateSSBO(varray);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo);
GLsizei N = (GLsizei)varray.size() - 2;
glClearColor(0.0f, 0.0f, 0.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]);
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;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp1));
glDrawArrays(GL_TRIANGLES, 0, 6*(N-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;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glUniformMatrix4fv(loc_mvp, 1, GL_FALSE, glm::value_ptr(mvp2));
glDrawArrays(GL_TRIANGLES, 0, 6*(N-1));
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}