当前位置: 首页 > 知识库问答 >
问题:

OpenGL/C:将多个纹理传递给一个着色器的问题

洪昊然
2023-03-14

我跟着教程上了课https://learnopengl.com/Lighting/Lighting-maps当使用多个纹理(一个用于镜面反射,一个用于漫反射照明)时,会出现一些奇怪的错误。当只使用一种纹理时,一切正常,但在使用两种纹理后,立方体只渲染黑色或只应用镜面反射纹理。

我在stackoverflow上搜索了所有类似的问题,并阅读了OpenGL文档,但仍然无法找出第二种纹理不起作用的原因。

这是我的主程序的渲染循环部分。创建立方体的cpp,该立方体应使用这两种纹理进行渲染。着色器和纹理都可以很好地加载和编译。

// Load textures
    Texture groundTexture = Texture("GROUND", TEXTURE_DIR+"png/container2.png");
    Texture groundSpecular = Texture("SPECULAR", TEXTURE_DIR+"png/container2_specular.png");

    // Load shaders
    Shader textureShader = Shader("SIMPLE_TEXTURE", SHADER_DIR+"vertex/texture_lighted.vs", SHADER_DIR+"fragment/texture_lighted.fs");
    Shader lampShader = Shader("WHITE", SHADER_DIR+"vertex/basic.vs", SHADER_DIR+"fragment/white.fs");

    // projection matrix
    glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);

    // shader configuration
    // --------------------
    textureShader.use();
    // Get the uniform variables location.
    GLuint diffLocation = glGetUniformLocation(textureShader.ID, "material.diffuse");
    GLuint specLocation  = glGetUniformLocation(textureShader.ID, "material.specular");

另一件奇怪的事情是,如果我将0和1更改为groundTexture。ID和地面反射。ID,至少镜面反射部分渲染,尽管glUniform1i期望的是这里的单位编号,而不是纹理ID。

    // Then bind the uniform samplers to texture units:
    glUseProgram(textureShader.ID);
    glUniform1i(diffLocation, 0);
    glUniform1i(specLocation, 1);


    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // Calc time
        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // be sure to activate shader when setting uniforms/drawing objects
        textureShader.use();
        textureShader.setVec3("light.position", lightPos);
        textureShader.setVec3("viewPos", camera.Position);
        // light properties
        textureShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
        textureShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
        textureShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
        // material properties
        textureShader.setFloat("material.shininess", 64.0f);

        // view/projection transformations
        glm::mat4 view = camera.getViewMatrix();
        textureShader.setMat4("projection", projection);
        textureShader.setMat4("view", view);

        // world transformation
        glm::mat4 model = glm::mat4(1.0f);
        textureShader.setMat4("model", model);

        // bind texture-maps
        glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0
        glBindTexture(GL_TEXTURE_2D, groundTexture.ID);
        glActiveTexture(GL_TEXTURE0 + 1); // Texture unit 1
        glBindTexture(GL_TEXTURE_2D, groundSpecular.ID);
        glActiveTexture(GL_TEXTURE0 + 0); // Texture unit 0

        // render the cube
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 36);

为了完整性起见,这里还有我的着色器类和纹理类:

#include "shader.h"
#include <stdlib.h>
#include <string.h>
#include <glad/glad.h>
#include <iostream>
#include <glm/ext.hpp>
#include <fstream>
#include <sstream>

/*
 * Creates a new Shader.
 * name = name of the shader
 * vShader = vertex shader
 * fShader = fragment shader
 */
Shader::Shader(std::string name, std::string vShaderPath, std::string fShaderPath) {
    this->name = name;
    this->vShaderPath = vShaderPath;
    this->fShaderPath = fShaderPath;
    compileAndLink();
}

Shader::Shader(const Shader& s) {
    this->name = s.name;
    this->vShaderPath = s.vShaderPath;
    this->fShaderPath = s.fShaderPath;
    compileAndLink();
}

void Shader::compileAndLink() {

    std::string vShader, fShader;
    std::ifstream v_inFile, f_inFile;
    std::stringstream v_strStream, f_strStream;

    // Read File for vertexShader
    v_inFile.open(vShaderPath);
    if (!v_inFile.is_open()) {
        std::cout << "ERROR LOADING VERTEX SHADER FILE: " << vShaderPath << std::endl;
    } else {
        v_strStream << v_inFile.rdbuf();
        vShader = v_strStream.str();
    }

    // Read File for fragmentShader
    f_inFile.open(fShaderPath);
    if (!f_inFile.is_open()) {
        std::cout << "ERROR LOADING FRAGMENT SHADER FILE: " << fShaderPath << std::endl;
    } else {
        f_strStream << f_inFile.rdbuf();
        fShader = f_strStream.str();
    }

    // Compile and link
    const char* vertex_shader = vShader.c_str();
    const char* fragment_shader = fShader.c_str();

    // vertex shader
    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertex_shader, NULL);
    glCompileShader(vertexShader);
    // check for shader compile errors
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    // fragment shader
    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragment_shader, NULL);
    glCompileShader(fragmentShader);
    // check for shader compile errors
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    // store reference in ID
    ID = glCreateProgram();

    // link shaders
    glAttachShader(ID, vertexShader);
    glAttachShader(ID, fragmentShader);
    glLinkProgram(ID);

    // check for linking errors
    glGetProgramiv(ID, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(ID, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }

    // clean up
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

}

/*
 * Tell OpenGL to use this shader-program.
 */
void Shader::use() {
    glUseProgram(ID);
}

#include <glad/glad.h>
#include <iostream>
#define STB_IMAGE_IMPLEMENTATION
#include "../image-loader/stb_image.h"
#include "texture.h"

Texture::Texture(std::string name, std::string path) {
    this->name = name;
    this->path = path;
    createTexture();
}

Texture::Texture(const Texture& t) {
    this->name = t.name;
    this->path = t.path;
    createTexture();
}

void Texture::createTexture() {
    unsigned int texture;
    glGenTextures(1, &texture); // generate one texture

    // Load file
    int width, height, nrChannels;
    unsigned char *data = stbi_load(this->path.c_str(), &width, &height, &nrChannels, 0);

    if (data) {
        GLenum format;
        if (nrChannels == 1)
            format = GL_RED;
        else if (nrChannels == 3)
            format = GL_RGB;
        else if (nrChannels == 4)
            format = GL_RGBA;

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

        // Set Texture Parameters
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // x
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // y
        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); // clean up

    } else {
        std::cout << "Failed to load texture" << std::endl;
    }

    this->ID = texture;
}

void Texture::use() {
    glBindTexture(GL_TEXTURE_2D, this->ID);
}

我正在使用OpenGL 4.4,初始化如下,很高兴加载OpenGL函数指针。

glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

提前谢谢。

编辑:以下是两个着色器(显然很重要):纹理。vs(顶点着色器)

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

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    TexCoords = aTexCoords;
    // transpose normal vectors with normal matrix of model matrix
    Normal = mat3(transpose(inverse(model))) * aNormal;
}

你被点亮了。fs(片段着色器)

#version 330 core
out vec4 FragColor;

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float shininess;
};

struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;

uniform vec3 viewPos;
uniform Material material;
uniform Light light;

void main()
{
    // ambient
    vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;

    // diffuse
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;

    // specular
    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;

    vec3 result = ambient + diffuse + specular;
    FragColor = vec4(result, 1.0);
} 

共有1个答案

卜鹏
2023-03-14

glGenTexture生成纹理的名称(值)后,命名纹理必须由glBindTexture绑定到纹理目标,然后纹理图像由glTexImage2D指定。
glTexImage2D定义纹理的纹理图像,当前绑定到指定的目标:

glGenTextures(1, &texture); 

// [...]

glBindTexture(GL_TEXTURE_2D, texture); 
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
 类似资料:
  • 目前,我有问题与传递多个纹理到一个glsl着色器在iOS。 在渲染中,想要的纹理被绑定到制服上 我在这里一直使用GL_TEXTURE0和GL_TEXTURE1,因为当纹理(实际上是来自iPhone摄像头的luma和Chroma纹理,最终用于计算相应的rgb值)被创建时,这些textureLots被使用。 我使用的fragmentshader非常简单,它只是用给定的纹理对一个简单的屏幕填充四方进行纹

  • 他们没有提到的是如何首先将两个纹理传递给着色器。 以前我 所以现在我 如何将大小不同的纹理数组传递给GLSL? 将多个纹理从OpenGL传递到GLSL着色器 GLSL中的多个纹理-只有一个有效 (这是家庭作业,所以请用最少的代码片段回答(如果有的话)。谢谢!) 如果做不到这一点,有人有茶具吗?

  • 我正在写我的学生项目,有一些问题。 我的任务是加载两个大小相同的纹理(图像和灰度掩码),然后应用基于灰度掩码的卷积核模糊滤波器。 提前谢谢你。

  • 我试图在OpenGL中制作一个场景,从太空模拟地球。我现在有两个球体,一个用于地球,另一个稍大一些用于云层。地球和云球体对象都有自己的着色器程序来保持简单。地球着色器程序采用4种纹理(白天、夜晚、specmap和normalmap),而云着色器程序采用2种纹理(cloudmap和normalmap)。我有一个对象类,它有一个渲染函数,在该函数中我使用以下逻辑: 它从第0个纹理单元开始,从GL_TE

  • 我正在QT-QML-3D中用C开发一个应用程序。我需要将数十个浮点值(或浮点数向量)传递给片段着色器。这些值是灯光的位置和颜色,所以我需要更多的值,范围从0.0到1.0。但是,不支持将浮点或整数数组传递给QML中的着色器。我的想法是将浮动保存到纹理,将纹理传递到着色器,并从中获取值。 我试过这样的方法: 并将此QImage作为sampler2D传递给片段着色器。但是有没有像memcpy这样的方法可

  • 我是OpenGL的新手,在整理如何将纹理和着色器绑定到VBOs时遇到了困难。 我正在使用Cinder的纹理和着色器类。以下是我绘制方法的一部分: 在上面的代码中,如果我注释掉对mShader的调用。bind(),我的球体VBO将显示纹理(myImage)。我的着色器适用于普通(无纹理)形状,但当我在绘制任何带有包裹纹理的形状之前绑定着色器时,它会阻止纹理显示。 这是我使用的着色器的问题,还是我不理