美文网首页
立方体贴图

立方体贴图

作者: 不决书 | 来源:发表于2023-09-18 06:36 被阅读0次

立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面

image.png

方向向量的大小并不重要,只要提供了方向,OpenGL就会获取方向向量(最终)所击中的纹素,并返回对应的采样纹理值。

创建立方体贴图

绑定到 GL_TEXTURE_CUBE_MAP

  unsigned int textureID;
  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

OpenGL给我们提供了6个特殊的纹理目标,专门对应立方体贴图的一个面。

纹理目标 方位
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

设置纹理

  int width, height, nrChannels;
  unsigned char *data;  
  for(unsigned int i = 0; i < textures_faces.size(); i++)
  {
      data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0);
      glTexImage2D(
          GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 
          0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
      );
  }

环绕和过滤方式:

  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  // X方向上
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  // Y方向上
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 // Z 方向上 
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

在片段着色器中 glsl

  in vec3 textureDir; // 代表3D纹理坐标的方向向量
  uniform samplerCube cubemap; // 立方体贴图的纹理采样器

  void main()
  {             
        FragColor = texture(cubemap, textureDir);
  }

天空盒

显示天空盒

  #version 330 core
  layout (location = 0) in vec3 aPos;

  out vec3 TexCoords;

  uniform mat4 projection;
  uniform mat4 view;

  void main()
  {
      TexCoords = aPos;
      gl_Position = projection * view * vec4(aPos, 1.0);
  }
  #version 330 core
  out vec4 FragColor;

  in vec3 TexCoords;

  uniform samplerCube skybox;

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

我们通过取4x4矩阵左上角的3x3矩阵来移除变换矩阵的位移部分。我们可以将观察矩阵转换为3x3矩阵(移除位移),再将其转换回4x4矩阵,来达到类似的效果。

  glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));

优化

因为天空盒将会复写场景中的其它物体。我们需要欺骗深度缓冲,让它认为天空盒有着最大的深度值1.0,只要它前面有一个物体,深度测试就会失败。

坐标系统小节中我们说过,透视除法是在顶点着色器运行之后执行的,将gl_Position的xyz坐标除以w分量。我们又从深度测试小节中知道,相除结果的z分量等于顶点的深度值。使用这些信息,我们可以将输出位置的z分量等于它的w分量,让z分量永远等于1.0,这样子的话,当透视除法执行之后,z分量会变为w / w = 1.0

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

最终的标准化设备坐标将永远会有一个等于1.0的z值:最大的深度值。结果就是天空盒只会在没有可见物体的地方渲染了(只有这样才能通过深度测试,其它所有的东西都在天空盒前面)。

我们还要改变一下深度函数,将它从默认的GL_LESS改为GL_LEQUAL。深度缓冲将会填充上天空盒的1.0值,所以我们需要保证天空盒在值小于或等于深度缓冲而不是小于时通过深度测试。

环境映射

通过使用环境的立方体贴图,我们可以给物体反射和折射的属性。这样使用环境立方体贴图的技术叫做环境映射(Environment Mapping),其中最流行的两个是反射(Reflection)折射(Refraction)

反射

image.png
GLSL内建的reflect函数来计算这个反射向量
  #version 330 core
  out vec4 FragColor;

  in vec3 Normal;
  in vec3 Position;

  uniform vec3 cameraPos;
  uniform samplerCube skybox;

  void main()
  {             
      vec3 I = normalize(Position - cameraPos);
      vec3 R = reflect(I, normalize(Normal));
      FragColor = vec4(texture(skybox, R).rgb, 1.0);
  }

顶点着色器

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

  out vec3 Normal;
  out vec3 Position;

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

  void main()
  {
      Normal = mat3(transpose(inverse(model))) * aNormal;
      Position = vec3(model * vec4(aPos, 1.0));
      gl_Position = projection * view * model * vec4(aPos, 1.0);
  }
image.png

折射

折射是通过斯涅尔定律(Snell’s Law)来描述的,使用环境贴图的话看起来像是这样:

image.png
折射可以使用GLSL的内建refract函数来轻松实现
折射率决定了材质中光线弯曲的程度,每个材质都有自己的折射率。一些最常见的折射率可以在下表中找到:
材质 折射率
1.33
1.309
玻璃 1.52
钻石 2.42

光线/视线从空气进入玻璃(如果我们假设箱子是玻璃制的),所以比值为1.00 \ 1.52 = 0.658

  void main()
{             
    float ratio = 1.00 / 1.52;
    vec3 I = normalize(Position - cameraPos);
    vec3 R = refract(I, normalize(Normal), ratio);
    FragColor = vec4(texture(skybox, R).rgb, 1.0);
}
image.png

动态环境贴图

通过使用帧缓冲,我们能够为物体的6个不同角度创建出场景的纹理,并在每个渲染迭代中将它们储存到一个立方体贴图中。之后我们就可以使用这个(动态生成的)立方体贴图来创建出更真实的,包含其它物体的,反射和折射表面了。这就叫做动态环境映射(Dynamic Environment Mapping),因为我们动态创建了物体周围的立方体贴图,并将其用作环境贴图。

缺点:我们需要为使用环境贴图的物体渲染场景6次,这是对程序是非常大的性能开销。现代的程序通常会尽可能使用天空盒,并在可能的时候使用预编译的立方体贴图,只要它们能产生一点动态环境贴图的效果。虽然动态环境贴图是一个很棒的技术,但是要想在不降低性能的情况下让它工作还是需要非常多的技巧的。

相关文章

  • 18.opengl高级-立方体贴图

    一、原理 立方体贴图在《视觉计算基础》一书中,第14章的环境贴图中有讲到,常见的环境贴图有立方体环境贴图和球体环境...

  • 计算机图形学(OPENGL):天空盒

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io 立方体贴图   立方体贴图是...

  • OpenGL学习20——立方体贴图

    立方体贴图(Cubemaps) 立方体贴图(cubemap)是一种纹理,包含来自立方体每个平面的6个独立的2D纹理...

  • OpenGL ES入门3-立方体纹理贴图

    概述 给立方体6个面进行纹理贴图,首先要能绘制立方体,然后对立方体每对表面进行纹理贴图。这里使用glDrawArr...

  • NDK OpenGL ES 3.0 开发(十五):立方体贴图(天

    该原创文章首发于微信公众号:字节流动 OpenGL ES 立方体贴图 OpenGL ES 立方体贴图本质上还是纹理...

  • 2022-10-14 天空盒

    一、原理 1.创建一个立方体贴图,代码见3.1 2.关闭深度写入,先绘制一个盒子,纹理为立方体贴图 shader:...

  • three.js给一个立方体贴图片

    three.js给一个立方体贴图片

  • opengl学习-立方体贴图

    立方体贴图通过方向向量来进行索引/采样。 创建立方体贴图 优化天空盒 将观察矩阵转换为3x3矩阵再将其转换回4x4...

  • 天空盒

    天空盒Get知识点: 1、立方体贴图是和其它纹理一样的,所以如果想创建一个立方体贴图的话,我们需要生成一个纹理,并...

  • 立方体贴图

    将多个纹理组合起来映射到一个单一纹理,立方体贴图含有6个2D纹理对应每个面。 特点:立方体贴图可使用方向向量对其索...

网友评论

      本文标题:立方体贴图

      本文链接:https://www.haomeiwen.com/subject/kkbwvdtx.html