如何使用GL_LINES在视口周围绘制边界



让着色器在纹理的外围运行时遇到问题。我跟随https://learnopengl.com/并使用索引顶点来绘制线基元。问题是只画了右边和底线,缺少了上边和左边。

我写了一个小程序来演示这一点,试图在黑色视口周围绘制一个红色边框。它主要是来自learnopengl.com的相同骨架代码。

这是着色器:

#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1, 0, 0, 1);
}

和C++代码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <learnopengl/shader_s.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 800;
struct vaoinfo
{
unsigned int VBO, VAO, EBO;
vaoinfo() : VAO(0), VBO(0), EBO(0)
{}
};
void create_vao(vaoinfo& info)
{
glGenVertexArrays(1, &info.VAO);
glGenBuffers(1, &info.VBO);
glGenBuffers(1, &info.EBO);
}
void init_vao(vaoinfo& info, float* vertices, int num_vertices, int* indices, int num_indices)
{
glGenVertexArrays( 1, &info.VAO);
glGenBuffers( 1, &info.VBO);
glGenBuffers( 1, &info.EBO);
glBindVertexArray(info.VAO);
glBindBuffer( GL_ARRAY_BUFFER, info.VBO);
glBufferData( GL_ARRAY_BUFFER, sizeof(float) * num_vertices, vertices, GL_STATIC_DRAW);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, info.EBO);
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * num_indices, indices, GL_STATIC_DRAW);
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray( 0);
glBindVertexArray( 0);
}
int main()
{
// glfw: initialize and configure
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// glfw window creation
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Shader borderProgram("default.vert.glsl", "border.frag.glsl"); // you can name your shader files however you like
float vertices[] = {
1.f,  1.f, 0.0f,  // top right
1.f, -1.f, 0.0f,  // bottom right
-1.f, -1.f, 0.0f,  // bottom left
-1.f,  1.f, 0.0f,   // top left 
};
vaoinfo T, B, L, R;
int top[] = { 3,0 };
create_vao(T);
init_vao(T, vertices, 12, top, 2);
int bottom[] = { 1,2 };
create_vao(B);
init_vao(B, vertices, 12, bottom, 2);
int left[] = { 2,3 };
create_vao(L);
init_vao(L, vertices, 12, left, 2);

int right[] = { 0,1 };
create_vao(R);
init_vao(R, vertices, 12, right, 2);

glBindFramebuffer(GL_FRAMEBUFFER, 0);
while (!glfwWindowShouldClose(window))
{
// input
processInput(window);
glClearColor(0.f, 0.f, 0.f, 0.f);
borderProgram.use();
glBindVertexArray(T.VAO);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(B.VAO);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(L.VAO);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(R.VAO);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, nullptr);

glfwSwapBuffers(window);
glfwPollEvents();
Sleep(16);
}
glfwTerminate();
return 0;
}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}

编辑:我解决了问题。。。有点我的印象是,在处理顶点时(我认为创建vao时称为"局部空间"(,x和y的坐标范围为[-1,1]。但显然它实际上是[-m.x,1]和[-1,m.y],其中m=(1-1/w,1-1/h(。这个假设正确吗?

裁剪空间中的X和Y坐标-1和+1(即您绘制的坐标(映射到视口的边缘。问题是,当你在一个像素的边缘画一条线时,OpenGL需要决定该边缘共享的两个像素中的哪一个将产生碎片。就OpenGL规范而言,实际的OpenGL实现可以自由选择它想要的任何行为,只要满足某些条件,例如,两个相邻的像素都应该而不是产生片段,只有其中一个应该。事实上,您没有看到视口的所有四条边的像素为您的线生成片段,这是因为OpenGL实现也需要与平局打破规则保持一致。当视口向左和向上只大一个像素时(当前看不到该行的任何像素(,您会在那里看到该行的像素。

如果要确保在视口的所有四条边上看到所有四条线,则必须调整这些线的坐标,使它们与像素的中心相交(与两个相邻像素之间的边相反(。

可以通过将顶点坐标有效地偏移半个像素来实现这一点。由于剪裁空间沿每个X轴和Y轴的范围为2个单位,因此需要将负顶点坐标分别偏移1.0f/windowWidth1.0f/windowHeight,将正坐标分别偏移-1.0f/windowWidth-1.0f/windowHeight

如果你在纸上画一个小网格,把最左边/最下边标记为-1,把最右边/最上边标记为+1,然后用简单的数学计算出边缘上像素中心的位置,这实际上会有所帮助。这将是您需要绘制的顶点的坐标。

最新更新