Как я могу иметь 2 скайбокса в OpenGL?

#c #opengl #textures #glm-math #skybox

#c #opengl #Текстуры #glm-математика #скайбокс

Вопрос:

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

 //SkyBox
    GLuint skyboxVBO, skyboxVAO;
    glGenVertexArrays(1, amp;skyboxVAO);
    glGenBuffers(1, amp;skyboxVBO);
    glBindVertexArray(skyboxVAO);
    glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), amp;skyboxVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0);

    // Load textures
    vector<const GLchar*> facesN;
    facesN.push_back("SkyBox/night/right.tga");
    facesN.push_back("SkyBox/night/left.tga");
    facesN.push_back("SkyBox/night/top.tga");
    facesN.push_back("SkyBox/night/bottom.tga");
    facesN.push_back("SkyBox/night/back.tga");
    facesN.push_back("SkyBox/night/front.tga");

    GLuint cubemapTextureN = TextureLoading::LoadCubemap(facesN);

    vector<const GLchar*> faces;
    faces.push_back("SkyBox/day/right.tga");
    faces.push_back("SkyBox/day/left.tga");
    faces.push_back("SkyBox/day/top.tga");
    faces.push_back("SkyBox/day/bottom.tga");
    faces.push_back("SkyBox/day/back.tga");
    faces.push_back("SkyBox/day/front.tga");

    GLuint cubemapTexture = TextureLoading::LoadCubemap(faces);
 

Затем в цикле программы я рисую скайбокс следующим образом:

 // Draw skybox as last
        glDepthFunc(GL_LEQUAL);  // Change depth function so depth test passes when values are equal to depth buffer's content
        SkyBoxshader.Use();
        view = glm::mat4(glm::mat3(camera.GetViewMatrix()));    // Remove any translation component of the view matrix
        glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
        glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

        // skybox cube
        glBindVertexArray(skyboxVAO);
        glActiveTexture(GL_TEXTURE1);
        if (daytime == 1.0f) {
            glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
        }
        else {
            glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTextureN);
        }
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glBindVertexArray(0);
        glDepthFunc(GL_LESS); // Set depth function back to default
 

Теоретически, всякий раз, когда я нажимаю кнопку, которая изменяет значение daytime , скайбокс должен менять свои текстуры на cubemapTextureN , а при другом нажатии кнопки — обратно на cubemapTexture . Вместо этого он просто рисует текстуру, которая была загружена последней, cubemapTexture в данном случае.
Я уже тестировал daytime , и это действительно меняется, так что дело не в этом.

Кроме того, я попробовал загрузить cubemapTextureN после cubemapTexture и, сделав это, вместо этого cubemapTextureN отрисовывается, но по-прежнему без изменений. Я предполагаю, что каким-то образом при загрузке вторых текстур первые обновляются, чего не должно происходить, поскольку у них разные имена, если я не понимаю это неправильно.

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

Редактировать: Это шейдеры для скайбокса. Шейдер фрагментов:

 #version 330 core
in vec3 TexCoords;
out vec4 color;

uniform samplerCube skybox;

void main()
{
    color = texture(skybox, TexCoords);
}
 

Вершинный шейдер:

 #version 330 core
layout (location = 0) in vec3 position;
out vec3 TexCoords;

uniform mat4 projection;
uniform mat4 view;


void main()
{
    vec4 pos = projection * view * vec4(position, 1.0);
    gl_Position = pos.xyww;
    TexCoords = position;
}
 

И их использование:
Shader SkyBoxshader("Shaders/SkyBox.vs", "Shaders/SkyBox.frag");

Кроме того, если это полезно, это заголовок с определением TextureLoading , который содержит LoadCubemap

 #pragma once
// GLEW
#include <GL/glew.h>

// Other Libs
#include "stb_image.h"

// Other includes
#include "Model.h"
#include <vector>


class TextureLoading
{
public:
    static GLuint LoadTexture(GLchar *path)
    {
        unsigned int textureID;
        glGenTextures(1, amp;textureID);

        int width, height, nrComponents;
        unsigned char *data = stbi_load(path, amp;width, amp;height, amp;nrComponents, 0);
        if (data)
        {
            GLenum format;
            if (nrComponents == 1)
                format = GL_RED;
            else if (nrComponents == 3)
                format = GL_RGB;
            else if (nrComponents == 4)
                format = GL_RGBA;

            glBindTexture(GL_TEXTURE_2D, textureID);
            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
            glGenerateMipmap(GL_TEXTURE_2D);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

            stbi_image_free(data);

        }
        else
        {
            std::cout << "Failed to load texture" << path << std::endl;
            stbi_image_free(data);
        }

        return textureID;
    }


    static GLuint LoadCubemap(vector<const GLchar * > faces)
    {
        GLuint textureID;
        glGenTextures(1, amp;textureID);

        int width, height, nrChannels;
        for (unsigned int i = 0; i < faces.size(); i  )
        {
            unsigned char *data = stbi_load(faces[i], amp;width, amp;height, amp;nrChannels, 0);
            if (data)
            {
                glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X   i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
                stbi_image_free(data);
            }
            else
            {
                std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
                stbi_image_free(data);
            }
        }
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
        glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

        return textureID;
    }

};
 

Комментарии:

1. Почему GL_TEXTURE1, а не GL_TEXTURE0? И, пожалуйста, покажите свои привязки shader и glUniform *.

2. Скорее всего, ваш шейдер использует текстурный модуль 0 ( GL_TEXTURE0 ), который остается привязанным к последней загруженной вами текстуре (потому что вы, должно быть glBindTexture , где-то вызываете LoadCubemap ). Пожалуйста, покажите минимально воспроизводимый пример.

3. Спасибо. Я отредактировал сообщение , добавив шейдер и Texture.h который содержит LoadCubemap . Честно говоря, я не уверен, почему он использует GL_Texture1 вместо GL_Texture0 , но я попробовал использовать GL_Texture0 , и текстуры просто исчезли.

4. @LollStarr: Вы нигде не устанавливаете skybox форму. Если вы привязываете свою текстуру к GL_TEXTURE1 , вам нужно установить для uniform целочисленное значение 1 . Прочитайте о том, как привязывать текстуры к сэмплерам .