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

时间:2015-07-22 11:17:54

标签: opengl textures

我在让立方体贴图在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);
        }
  };

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

bug image rendered normals

1 个答案:

答案 0 :(得分:1)

您自己在评论中找到了解决方案,但要求更多背景知道它的行为方式。

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

对于两个纹理使用相同目标的情况,很明显这必须是真的。否则你的纹理绑定代码看起来像这样:

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页上,在&#34; 2.11.5采样器&#34;部分中:

  

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

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

要做到这一点,你将不得不使用两个不同的纹理单位,就像你使用相同目标的两个纹理的情​​况一样:

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