【问题标题】:Efficient function to draw textured squares OpenGL绘制纹理正方形OpenGL的高效函数
【发布时间】:2020-01-28 23:40:15
【问题描述】:

我正在关注本教程:https://learnopengl.com/Getting-started/Textures 创建一个函数,我可以在主函数的 glfw 循环中调用它,通过在每个正方形中调用此函数来显示多个具有不同纹理的正方形。

Draw.h

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stb_image.h>
#include <shader_s.h>
#include <iostream>


int width = 860;
int height = 640;




class Draw {
public:

    int drawErr = 0;


    void Square(int x, int y, int z, int w, int h, const char* file, bool usingTexture, bool flipImage){
        Shader ourShader("vertex.vs", "fragment.fs");
        if (!usingTexture) {
            //draw polygon with no texture
        }
        else {
            //shouldnt be in loop to draw
            float vertices[] = {
                //position            //color             //texCoords

                x+w,y,z, 0.0f, 0.0f, 0.0f,  1.0f, 1.0f, //top right
                x+w,y+h,z, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,//bottom right
                x,y+h,z, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, //bottom left
                x,y,z, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f//top left
            };
            unsigned int indices[] = {
                0, 1, 3,
                1, 2, 3
            };
            unsigned int VAO, VBO, EBO;
            glGenVertexArrays(1, &VAO);
            glGenBuffers(1, &VBO);
            glGenBuffers(1, &EBO);
            glBindVertexArray(VAO);
            glBindBuffer(GL_ARRAY_BUFFER, VBO);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
            //position
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
            glEnableVertexAttribArray(0);
            //color
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
            glEnableVertexAttribArray(1);
            //texture
            glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
            glEnableVertexAttribArray(2);
            //load Texture
            stbi_set_flip_vertically_on_load(flipImage);
            unsigned int texture;
            glGenTextures(1, &texture);
            glBindTexture(GL_TEXTURE_2D, texture);
            int width, height, nrChannels;
            unsigned char* data = stbi_load(file, &width, &height, &nrChannels, 0);
            if (data) {
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
                glGenerateMipmap(GL_TEXTURE_2D);
            }
            else {
                std::cout << "Failed to load texture!" << std::endl;
                glfwTerminate();
                drawErr = -1;
            }
            stbi_image_free(data);

            //should be in loop to draw
            ourShader.use();
            glBindVertexArray(VAO);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);


        }
    }

//other drawing functions here
};

Shader.h

#ifndef SHADER_H
#define SHADER_H

#include <gl/glew.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

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_SUCCESFULLY_READ" << 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;
            }
        }
    }
};
#endif

主要功能:

int main() {

    Draw draw;

    if (!glfwInit()) {
        std::cout << "failed to load GLFW" << std::endl;
        return -1;
    }
    GLFWwindow* window = glfwCreateWindow(width, height, "Electrocraft", NULL, NULL);
    glfwMakeContextCurrent(window);
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
    }
    std::cerr << "Status: Using GLEW " << glewGetString(GLEW_VERSION) << std::endl;




    while (!glfwWindowShouldClose(window)) {
        glClearColor(68.0f / 255.0f, 85.0f / 255.0f, 255.0f / 255.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        draw.Polygon(-1.0f, 1.0f, 0.0f, 2.0f, 2.0f, "Library\\Textures\\blocks\\dirt.bmp", true, false);

        glfwSwapBuffers(window);
        glfwPollEvents();

    }
}

Vertex.vs

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

out vec3 ourColor;
out vec2 TexCoord;

void main(){
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
    }

Fragment.vs

#version 330 core
out vec4 fragColor;

in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;

void main(){
    fragColor = texture(ourTexture, TexCoord);
    }

我的程序的问题是它不断地重新加载纹理并使用大量内存重新绑定它。我试过这样做,所以我可以调用 square 函数并同时绘制多个具有不同纹理的正方形。我想不出一种方法来减少内存并使用该函数来拥有多个正方形。

【问题讨论】:

  • 当然我会编辑以显示一个最小的可重现示例并明确我的问题
  • 我想你的问题将在本教程的后面得到解答,即关于实例化的章节可能是你想要的。-

标签: c++ opengl


【解决方案1】:

一个简单的改进是将您的draw::Polygon 方法分成两部分:

  • 一个从磁盘加载纹理并返回包装 OpenGL 纹理 ID 的 Texture 对象的通用函数
  • 重写 Polygon 以接受这样的 Texture 对象。

例如查看sf::Texture class from SFML 以及它如何与sf::Shape class 结合。

【讨论】:

  • 着色器也是如此。
【解决方案2】:

您希望创建着色器一次(不是每次绘制正方形!)并且您希望加载纹理一次(不是每次绘制正方形!)

一个非常简单的方法是让 OpenGL 纹理成为一个全局变量:

// put this outside of any function, and delete it from Square

unsigned int texture;



// put this in main and delete it from Square

stbi_set_flip_vertically_on_load(flipImage);
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels;
unsigned char* data = stbi_load(file, &width, &height, &nrChannels, 0);
if (data) {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
}
else {
    std::cout << "Failed to load texture!" << std::endl;
    glfwTerminate();
    drawErr = -1;
}
stbi_image_free(data);



// put this in Square

glBindTexture(GL_TEXTURE_2D, texture);

这是一次加载纹理的最简单方式。

但是你怎么能有多个纹理呢?

嗯,你可以有多个全局变量(如果你愿意,也可以将它们放在 Draw 类中。这对这个程序来说并不重要)。 dirtTexturesnowTexturegrassTexture。如果你这样做,你应该把我告诉你的代码放入main,并从main中删除它,并创建一个返回纹理ID的loadTexture函数。然后你可以使用dirtTexture = loadTexture("dirt.jpg");snowTexture = loadTexture("snow.jpg");等等。

你的着色器、顶点数组和缓冲区也有同样的错误。对于着色器,您不能将其设为全局变量,因为它会在程序启动时尝试创建着色器,在 main 运行之前,在调用 glfwInitialize 之前,然后它会因为您没有而崩溃t 初始化 OpenGL。如果你让构造函数加载着色器,并添加一个类似LoadShader的函数来加载着色器,那么你可以把它变成一个全局变量。

对于顶点数组,同样的事情。在 main 中创建它。将 ID 存储在全局变量中。一遍又一遍地绑定同一个顶点数组。

对于顶点缓冲区,同样的事情。但请注意,您确实需要在每次调用 Square 时设置新数据,因为方块不同。你无法真正解决这个问题。但是您不需要每次调用Square 时都创建一个新的顶点缓冲区,您只需要将新数据放入同一个缓冲区中。应该不会很慢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多