Эффективная функция рисования текстурированных квадратов OpenGL - PullRequest
0 голосов
/ 28 января 2020

Я следую этому уроку: https://learnopengl.com/Getting-started/Textures, чтобы создать функцию, в которой я могу вызвать ее в своем glfw l oop в основной функции для отображения нескольких квадратов с разными текстурами, вызывая эту функцию в квадрат для создания.

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

Проблема с моей программой что он постоянно перезагружает текстуру и связывает ее, используя для этого много памяти. Я попытался сделать это таким образом, чтобы я мог вызвать функцию квадрата и нарисовать несколько квадратов с различными текстурами одновременно. Я не могу придумать способ уменьшить объем памяти и использовать функцию, чтобы иметь несколько квадратов.

Ответы [ 2 ]

1 голос
/ 28 января 2020

Вы хотите создать шейдер один раз (не каждый раз, когда вы рисуете квадрат!), И вы хотите загрузить текстуру один раз (не каждый раз, когда вы рисуете квадрат!)

Один очень простой способ сделать это - сделать текстуру 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, если хотите. дело за этой программой). dirtTexture, snowTexture, grassTexture. Если вы сделаете это, вы должны взять код, который я сказал вам вставить в main, удалить его из main и создать функцию loadTexture, которая возвращает идентификатор текстуры. Затем вы можете использовать dirtTexture = loadTexture("dirt.jpg"); snowTexture = loadTexture("snow.jpg"); и т. Д.

У вас есть та же ошибка для вашего шейдера, массива вершин и буфера. Что касается шейдера, вы не можете сделать его глобальной переменной, потому что тогда он попытается создать шейдер при запуске программы, до запуска main, до вызова glfwInitialize, а затем вызовет sh, потому что Вы еще не инициализировали OpenGL. Если вы сделаете так, чтобы конструктор не загружал шейдер и добавил функцию типа LoadShader, которая загружает шейдер, , то , вы можете сделать его глобальной переменной.

Для массива вершин тоже самое. Создайте это в основном. Сохраните идентификатор в глобальной переменной. Свяжите один и тот же массив вершин снова и снова.

Для буфера вершин тоже самое. Но учтите, что вам do нужно устанавливать новые данные при каждом вызове Square, потому что квадраты разные. Вы не можете обойти это. Но вам не нужно создавать новый вершинный буфер каждый раз, когда вызывается Square, вам нужно только поместить новые данные в тот же буфер. Это не должно быть очень медленно.

1 голос
/ 28 января 2020

Простым улучшением является разделение вашего draw::Polygon метода на две части:

  • одна универсальная c функция, которая загружает текстуру с диска и возвращает объект Texture, который оборачивает OpenGL ID текстуры
  • переписать Polygon, чтобы принять такой объект текстуры.

См., например, класс sf::Texture из SFML и как он сочетается с sf::Shape класс .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...