OpenGL 立方体贴图纹理不起作用

OpenGL cube map texture doesn't work

我在让立方体贴图在 OpenGL 中工作时遇到问题。我按照一些教程创建了自己的 class 包装 OpenGL 立方体贴图纹理。 首先,我尝试为立方体贴图的侧面加载六张图像并将它们渲染到盆模型上。我想最终创建一个像镜子一样的效果,但现在我只使用法线作为立方体贴图纹理坐标。问题是片段着色器中的采样器似乎只返回零,所以整个模型都是黑色的。

到目前为止我的发现:

下面是我如何初始化立方体贴图:

texture_cube = new TextureCubeMap(512,TEXEL_TYPE_COLOR);
cout << "cubemap loading: " << texture_cube->load_ppms("rock.ppm","rock.ppm","rock.ppm","rock.ppm","rock.ppm","rock.ppm") << endl;    
texture_cube->update_gpu();

...

glUniform1i(sampler_location,0);         // 'tex' unit
glUniform1i(sampler_cube_location,0);    // 'tex_cube' unit

这是我的渲染循环:

void render()
  {
    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_DEPTH_BUFFER_BIT);
    glUniformMatrix4fv(view_matrix_location,1,GL_TRUE,glm::value_ptr(CameraHandler::camera_transformation.get_matrix()));

    texture_cup->bind(0);
    glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_cup.get_matrix()));
geometry_cup->draw_as_triangles();

    texture_rock->bind(0);
    glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_rock.get_matrix()));
    geometry_rock->draw_as_triangles();

    texture_cow->bind(0);
    glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(transformation_cow.get_matrix()));
    geometry_cow->draw_as_triangles();

    texture_room->bind(0);
    glUniformMatrix4fv(model_matrix_location,1,GL_TRUE,glm::value_ptr(glm::mat4(1.0)));
    geometry_room->draw_as_triangles();

    glUniformMatrix4fv(model_matrix_location,1,GL_TRUE, glm::value_ptr(transformation_mirror.get_matrix()));

    // draw the mirror:

    texture_cube->bind(0);

    glUniform1ui(mirror_location,1);
    geometry_mirror->draw_as_triangles();    
    glUniform1ui(mirror_location,0);

    ErrorWriter::checkGlErrors("rendering loop");

    glutSwapBuffers();
  }

这是我的片段着色器:

#version 330

in vec3 transformed_normal;
in vec2 uv_coords;
uniform vec3 light_direction;
uniform sampler2D tex;
uniform samplerCube tex_cube;
uniform bool mirror;

out vec4 FragColor;
float diffuse_intensity;
float lighting_intensity;

void main()
{
  diffuse_intensity = clamp(dot(normalize(transformed_normal),-1 * light_direction),0.0,1.0);
  lighting_intensity = clamp(0.4 + diffuse_intensity,0.0,1.0);

  if (mirror)
    {
      FragColor = texture(tex_cube, transformed_normal);
    }
  else
    {
      FragColor = 0.3 * vec4(lighting_intensity, lighting_intensity, lighting_intensity, 1.0);
      FragColor += texture(tex, uv_coords);
    }
}

这是我的整个立方体贴图 class(Image2D 是我自己的 class,应该可以正常工作,我已经用 2D 纹理对其进行了测试):

class TextureCubeMap: public Texture
  {
    protected:
      unsigned int size;

    public:
      Image2D *image_front;
      Image2D *image_back;
      Image2D *image_left;
      Image2D *image_right;
      Image2D *image_top;
      Image2D *image_bottom;

      /**
      * Initialises a new cube map object.
      * 
      * @size width and height resolution in pixels (cube map must
      *   have square size)
      */

      TextureCubeMap(unsigned int size, unsigned int texel_type = TEXEL_TYPE_COLOR)
        {
          this->size = size;
          glGenTextures(1,&(this->to));

          glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);

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

          this->image_front = new Image2D(size,size,texel_type);
          this->image_back = new Image2D(size,size,texel_type);
          this->image_left = new Image2D(size,size,texel_type);
          this->image_right = new Image2D(size,size,texel_type);
          this->image_top = new Image2D(size,size,texel_type);
          this->image_bottom = new Image2D(size,size,texel_type);
        }

      virtual ~TextureCubeMap()
        {
          delete this->image_front;
          delete this->image_back;
          delete this->image_left;
          delete this->image_right;
          delete this->image_top;
          delete this->image_bottom;
        }

      virtual void update_gpu()
        {
          int i;
          Image2D *images[] =
            {this->image_front,
            this->image_back,
            this->image_left,
            this->image_right,
            this->image_bottom,
            this->image_top};

          GLuint targets[] =
            {GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
            GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
            GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
            GL_TEXTURE_CUBE_MAP_POSITIVE_X,
            GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
            GL_TEXTURE_CUBE_MAP_POSITIVE_Y};

          glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);

          for (i = 0; i < 6; i++)
            {
              glTexImage2D(targets[i],0,GL_RGBA,this->size,this->size,0,GL_RGBA,GL_FLOAT,images[i]->get_data_pointer());

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

      virtual void load_from_gpu()
        {
          int i;

          glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);

          Image2D *images[] =
            {this->image_front,
            this->image_back,
            this->image_left,
            this->image_right,
            this->image_bottom,
            this->image_top};

          GLuint targets[] =
            {GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
            GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
            GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
            GL_TEXTURE_CUBE_MAP_POSITIVE_X,
            GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
            GL_TEXTURE_CUBE_MAP_POSITIVE_Y};

          for (i = 0; i < 6; i++)
            {
              images[i]->set_size(this->size,this->size);
              glGetTexImage(targets[i],0,GL_RGBA,GL_FLOAT,images[i]->get_data_pointer());
            }
        }

      bool load_ppms(string front, string back, string left, string right, string bottom, string top)
        {
          bool result = true;

          result = result && this->image_front->load_ppm(front);
          result = result && this->image_back->load_ppm(back);
          result = result && this->image_left->load_ppm(left);
          result = result && this->image_right->load_ppm(right);
          result = result && this->image_bottom->load_ppm(bottom);
          result = result && this->image_top->load_ppm(top);

          auto lambda_size_ok = [](Image2D *image, int size)
            {
              return (image->get_width() == size) && (image->get_height() == size);
            };

          if (!lambda_size_ok(this->image_front,this->size) ||
              !lambda_size_ok(this->image_back,this->size) ||
              !lambda_size_ok(this->image_left,this->size) ||
              !lambda_size_ok(this->image_right,this->size) ||
              !lambda_size_ok(this->image_top,this->size) ||
              !lambda_size_ok(this->image_bottom,this->size))
            ErrorWriter::write_error("Loaded cubemap images don't have the same size.");

          return result;
        }

      virtual void print()
        {
          cout << "front:" << endl;
          this->image_front->print();
          cout << "back:" << endl;
          this->image_back->print();
          cout << "left:" << endl;
          this->image_left->print();
          cout << "right:" << endl;
          this->image_right->print();
          cout << "bottom:" << endl;
          this->image_bottom->print();
          cout << "top:" << endl;
          this->image_top->print();
        }

      virtual void bind(unsigned int unit)
        {
          switch (unit)
            {
              case 0: glActiveTexture(GL_TEXTURE0); break;
              case 1: glActiveTexture(GL_TEXTURE1); break;
              case 2: glActiveTexture(GL_TEXTURE2); break;
              case 3: glActiveTexture(GL_TEXTURE3); break;
              case 4: glActiveTexture(GL_TEXTURE4); break;
              case 5: glActiveTexture(GL_TEXTURE5); break;
              case 6: glActiveTexture(GL_TEXTURE6); break;
              case 7: glActiveTexture(GL_TEXTURE7); break;
              case 8: glActiveTexture(GL_TEXTURE8); break;
              case 9: glActiveTexture(GL_TEXTURE9); break;

              default:
                break;
            }

          glBindTexture(GL_TEXTURE_CUBE_MAP,this->to);
        }
  };

以及错误的图像(第一个是我得到的,第二个是法线渲染的):

您在评论中自己找到了解决方案,但要求提供更多背景信息以了解其行为方式的原因。

是的,如果你想在同一个片段着色器中使用两个不同的纹理,你必须将两个纹理绑定到不同的纹理单元,并将采样器统一变量的值设置为匹配的纹理单元索引。

对于两个纹理使用相同目标的情况,很明显这一定是真的。否则您的纹理绑定代码将如下所示:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glBindTexture(GL_TEXTURE_2D, tex2);

和第二个 glBindTexture() 调用当然会覆盖第一个。因此,您将使用不同的纹理单元:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex2);

在您使用两个不同纹理目标的情况下,事情就不那么明显了。在这个调用序列之后:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex2);

您确实有两个 纹理绑定到纹理单元 0。在 OpenGL 状态下,每个纹理单元包含每个目标的绑定。

现在,如果您想在同一个着色器中使用 2D 和立方体贴图纹理,那么您可以如上所示绑定两个纹理,并将两个采样器统一设置为值 0 似乎是一个完全合理的期望.但其实这个是不支持的,因为...就是这么定义的

这是 OpenGL 3.3 规范第 74 页的“2.11.5 采样器”部分:

It is not allowed to have variables of different sampler types pointing to the same texture image unit within a program object. This situation can only be detected at the next rendering command issued, and an INVALID_OPERATION error will then be generated.

虽然这基于 GPU 通常如何从着色器代码访问采样器是有意义的,但不幸的是,您可以通过着色器代码无法使用的方式为纹理绑定设置状态。部分原因可能是基于 OpenGL 的悠久历史。

要完成这项工作,您将必须使用两个不同的纹理单元,就像您在两个纹理使用相同目标的情况下所做的那样:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex2);