无法将基础坐标从左下角更改为左上角

问题描述 投票:0回答:1

我正在使用 OpenGL 和 GLFW 编写一个文本编辑器,传统上 GUI 应用程序从左上角(而不是从左下角)开始缩放,所以这对我来说很重要。

我想做到这一点,以便当我调整窗口大小时,渲染的内容位于左上角而不是左下角。

这里有一些截图来展示我的意思:

初始窗口:

enter image description here

调整窗口大小(转到左下角):

enter image description here

我想要调整大小的窗口的外观(左上):

enter image description here

最小可重现示例:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

class Shader
{
public:
    unsigned int ID;
    // constructor generates the shader on the fly
    // ------------------------------------------------------------------------
    Shader(const char* vertexPath, const char* fragmentPath)
    {
        // 1. retrieve the vertex/fragment source code from filePath
        std::string vertexCode;
        std::string fragmentCode;
        std::ifstream vShaderFile;
        std::ifstream fShaderFile;
        // ensure ifstream objects can throw exceptions:
        vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
        fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
        try 
        {
            // open files
            vShaderFile.open(vertexPath);
            fShaderFile.open(fragmentPath);
            std::stringstream vShaderStream, fShaderStream;
            // read file's buffer contents into streams
            vShaderStream << vShaderFile.rdbuf();
            fShaderStream << fShaderFile.rdbuf();
            // close file handlers
            vShaderFile.close();
            fShaderFile.close();
            // convert stream into string
            vertexCode   = vShaderStream.str();
            fragmentCode = fShaderStream.str();
        }
        catch (std::ifstream::failure& e)
        {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;
        }
        const char* vShaderCode = vertexCode.c_str();
        const char * fShaderCode = fragmentCode.c_str();
        // 2. compile shaders
        unsigned int vertex, fragment;
        // vertex shader
        vertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertex, 1, &vShaderCode, NULL);
        glCompileShader(vertex);
        checkCompileErrors(vertex, "VERTEX");
        // fragment Shader
        fragment = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragment, 1, &fShaderCode, NULL);
        glCompileShader(fragment);
        checkCompileErrors(fragment, "FRAGMENT");
        // shader Program
        ID = glCreateProgram();
        glAttachShader(ID, vertex);
        glAttachShader(ID, fragment);
        glLinkProgram(ID);
        checkCompileErrors(ID, "PROGRAM");
        // delete the shaders as they're linked into our program now and no longer necessary
        glDeleteShader(vertex);
        glDeleteShader(fragment);
    }
    // activate the shader
    // ------------------------------------------------------------------------
    void use() 
    { 
        glUseProgram(ID); 
    }
    // utility uniform functions
    // ------------------------------------------------------------------------
    void setBool(const std::string &name, bool value) const
    {         
        glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); 
    }
    // ------------------------------------------------------------------------
    void setInt(const std::string &name, int value) const
    { 
        glUniform1i(glGetUniformLocation(ID, name.c_str()), value); 
    }
    // ------------------------------------------------------------------------
    void setFloat(const std::string &name, float value) const
    { 
        glUniform1f(glGetUniformLocation(ID, name.c_str()), value); 
    }

private:
    // utility function for checking shader compilation/linking errors.
    // ------------------------------------------------------------------------
    void checkCompileErrors(unsigned int shader, std::string type)
    {
        int success;
        char infoLog[1024];
        if (type != "PROGRAM")
        {
            glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
            if (!success)
            {
                glGetShaderInfoLog(shader, 1024, NULL, infoLog);
                std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
        else
        {
            glGetProgramiv(shader, GL_LINK_STATUS, &success);
            if (!success)
            {
                glGetProgramInfoLog(shader, 1024, NULL, infoLog);
                std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
    }
};

void processInput(GLFWwindow *window);

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

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

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    Shader ourShader("3.3.shader.vs", "3.3.shader.fs"); // you can name your shader files however you like

    float vertices[] = {
         0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // bottom right
        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // bottom left
         0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // top
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    while (!glfwWindowShouldClose(window))
    {
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        ourShader.use();
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    glfwTerminate();
    return 0;
}

void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

3.3.shader.fs:

#version 330 core
out vec4 FragColor;

in vec3 ourColor;

void main()
{
    FragColor = vec4(ourColor, 1.0f);
}

3.3.着色器.vs:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
}
c++ opengl glsl glfw coordinate-systems
1个回答
0
投票

首先,从问题和评论来看,我怀疑这是一个XY问题。无论如何,让我们先解决实际的问题和代码。

问题中的 mre 奇怪地缺少的是 viewport 的设置。通常(例如在学习 OpenGL 教程GLFW文档中)视口是使用回调函数设置的。当窗口的帧缓冲区大小改变时,视口也会相应改变:

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

这是典型的 OpenGL 用法:图元的位置在顶点着色器中设置为

clip space
中的 gl_Position(范围 [-1, 1]),然后转换为 标准化设备坐标 (NDC)并使用视口参数,将它们转换为窗口坐标。调整窗口大小时,三角形保持在中间并被拉伸/压缩,因为到边缘的相对距离保持不变。

在给定的mre中,这种情况不会发生。视口设置为窗口的(建议)大小 (600x800),因为这种情况在默认情况下会发生,并且即使窗口/帧缓冲区的大小被调整,也会使用此大小。因此,三角形将始终以固定大小和到窗口坐标中的原点(左下角)的固定距离进行渲染。 据我所知,将这个原点更改为左上角是“不可能”的,但我们可以看看一些接近的选项。对于剪辑空间可以这样做:

glClipControl(GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE); // Requires OpenGL >=4.5 但是由于这是针对剪辑空间而不是窗口空间,因此三角形只会出现颠倒,并且仍然位于左下角。 您在评论中的建议也不起作用:

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, height - SCR_HEIGHT, SCR_WIDTH, SCR_HEIGHT);
}

height > SCR_HEIGHT

时,第二个参数变为负数,这是不允许的(

GL_INVALID_VALUE
)。请注意,为第三个和第四个参数提供负宽度和高度也是不可能的;这些参数是无符号整数。

现在看看你的第二个建议:


void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    src_height = height;
}

并在[]

RenderText

函数中[编辑此]:
float ypos = y - (ch.Size.y - ch.Bearing.y) * scale;
对此:
float ypos = src_height - (y + (ch.Size.y - ch.Bearing.y) * scale);


这指的是 mre 之外的代码,所以我不能确定,但看起来这相当于使字符的 y 位置取决于窗口高度。这可以提供您正在寻找的效果,但确切的结果会因机器而异(请记住,传递给 
glfwCreateWindow

的尺寸只是提示!)并且这仍然是一种非常不寻常的方式使用OpenGL。 OpenGL 通常用于通过投影和其他变换来渲染在某些世界空间中定义的场景 - 与实际窗口分辨率无关。

让我们回到您的实际目标:编写一个将文本放置在左上角的文本编辑器。通常的 OpenGL 方法是这样的:

使用帧缓冲区回调函数将视口设置为实际帧缓冲区大小,该大小可能与glfwCreateWindow(..)

建议的不同。或者,使用回调函数来

存储

帧缓冲区大小并在渲染之前设置视口。
  1. 定义世界空间中字符或文本的位置(可能与剪辑空间相同)。如果文本应从左上角开始,那么这将接近 [-1, 1]。
    在顶点着色器中应用正交投影,以防止场景中的对象随着窗口纵横比的变化而被拉伸/压缩。您还可以在此处应用缩放和平移变换。
  2. 渲染您的场景。
  3. 结果应该是场景中的对象(字符/文本)始终出现在相同的相对位置,与分辨率无关。
© www.soinside.com 2019 - 2024. All rights reserved.