【问题标题】:OpenGL cube map texture doesn't workOpenGL立方体贴图纹理不起作用
【发布时间】:2015-10-12 05:42:38
【问题描述】:

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

到目前为止我的发现:

  • 纹理数据应该上传到 GPU - 我通过上传 (glTexImage2D) 和检索数据 (glGetTexImage2D) 进行了检查。
  • 我检查了 glGetError() 并且它没有返回错误,即使我设置了glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 对于立方体贴图纹理,当我尝试渲染镜像(锅)时,在渲染循环中出现无效操作错误 - 奇怪。
  • 锅的法线应该没问题,我已经通过渲染检查了它们(见下图)。
  • 我正在加载的 ppm 图像是 512 x 512,适用于 2D 纹理,所以应该没有问题。

这是我初始化立方体贴图的方法:

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

这是我的整个立方体贴图类(Image2D 是我自己的类,应该可以正常工作,我已经用 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);
        }
  };

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

【问题讨论】:

  • 看起来像是 mipmapping 问题。当 MIN_FILTER 未更改且未生成 mipmap 时,采样器始终返回 0。将其设置为 GL_LINEAR,则一切正常。顺便说一句:您的 glTexParameteri 代码会引发错误,因为 MAG_FILTER 仅允许 GL_LINEAR 或 GL_NEAREST。
  • 非常感谢,我会尽力让你知道它做了什么:)
  • 好的,我试过了,但它似乎不起作用,也许我做错了什么。我还添加了glEnable(GL_TEXTURE_CUBE_MAP_EXT); 并尝试了glAreTexturesResident(...),它在上传纹理后返回 1。将每一面的纹理上传到 GPU 后,是否使用 glGenerateMipmap(GL_TEXTURE_CUBE_MAP); 生成 mipmap?
  • 我还发现了这个:“如果纹理不一致,OpenGL 需要像禁用不一致的纹理单元一样。”。我想知道如何检查这个。
  • 现在我发现如果我在渲染循环中删除除了立方体贴图之外的所有纹理绑定,立方体贴图就会开始工作。

标签: opengl textures


【解决方案1】:

您自己在 cmets 中找到了解决方案,但要求提供更多背景信息来说明它为何如此行事。

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

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

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 采样器”部分:

程序对象内不允许有不同采样器类型的变量指向同一个纹理图像单元。这种情况只能在下一个渲染命令发出时检测到,然后会产生一个 INVALID_OPERATION 错误。

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

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

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

【讨论】:

    猜你喜欢
    • 2014-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多