OpenGL - 如何创建和绑定立方体贴图数组?
OpenGL - How to create and bind cubemap arrays?
在超过纹理单元限制后,我决定使用立方体贴图数组纹理。
出于测试目的,我将立方体贴图的内容渲染为天空盒。问题是,当我绑定一个立方体贴图数组时,OpenGL 会忽略它,而是使用之前绑定的立方体贴图来渲染天空盒。
GL.BindTexture(TextureTarget.TextureCubeMap, shadowMaps[5].cubeMapTexture);
GL.BindTexture(TextureTarget.TextureCubeMapArray, shadowMapArray.cubeMapHandle); // Ignored
我假设这是错误创建的立方体贴图数组的默认行为。据我所知,我所做的一切都是正确的:Framebuffer complete
.
那么,创建和绑定立方体贴图数组的正确方法是什么?
public class CubeMapArray
{
public static int size = 512;
public static int layers = 8; // number of cube maps in array
public int FBO_handle;
public int cubeMapHandle;
public int cubeMapDepthHandle;
// Constructor //
public CubeMapArray()
{
// Create the FBO
GL.GenFramebuffers(1, out FBO_handle);
// Create and bind the CubeMap array
GL.GenTextures(1, out cubeMapHandle);
GL.BindTexture(TextureTarget.TextureCubeMapArray, cubeMapHandle);
// Allocate storage space
GL.TexImage3D(TextureTarget.TextureCubeMapArray, 0, PixelInternalFormat.Rg16, size, size, layers * 6, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
// Set the suitable texture parameters
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMaxLevel, 0);
// Create and bind the CubeMap depth array
GL.GenTextures(1, out cubeMapDepthHandle);
GL.BindTexture(TextureTarget.TextureCubeMapArray, cubeMapDepthHandle);
// Allocate storage space
GL.TexImage3D(TextureTarget.TextureCubeMapArray, 0, PixelInternalFormat.DepthComponent, size, size, layers * 6, 0, PixelFormat.DepthComponent, PixelType.UnsignedByte, IntPtr.Zero);
// Set the suitable texture parameters
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMaxLevel, 0);
// Attach cubemap texture as the FBO's color buffer
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBO_handle);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, cubeMapHandle, 0);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, cubeMapDepthHandle, 0);
// Error check
var errorcheck = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
Console.WriteLine("CUBEMAP ARRAY: " + errorcheck);
// Bind default framebuffer
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
}
}
}
着色器非常简单,令人惊讶的是,可以绑定常规立方体贴图。
顶点
#version 330
in vec3 texCoord;
out vec4 fragColor;
uniform samplerCube cubeMapArray[16];
void main (void)
{
fragColor = texture(cubeMapArray[0], texCoord);
}
片段
#version 330
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
in vec3 in_position;
out vec3 texCoord;
void main()
{
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_position, 1.0);
texCoord = in_position;
}
您需要在着色器代码中使用相应的采样器类型。立方体贴图数组在 OpenGL 4.0 及更高版本中可用,因此您还需要将 GLSL 版本提高到 400
或更高版本。
着色器代码将看起来像这样:
#version 400
...
uniform samplerCubeArray cubeMapArray;
...
fragColor = texture(cubeMapArray, vec4(texCoord, 0));
注意采样的数组层被指定为纹理坐标的第4个分量
uniform samplerCube cubeMapArray[16];
OK,有一个array of samplers and an array texture的区别。您在这里拥有的是一组采样器。您在 OpenGL 中创建的是数组纹理。
阵列纹理是 separate texture types 来自它们的非阵列对应物。立方体贴图数组纹理不是立方体贴图纹理;它是一个立方体贴图数组纹理。就像二维数组纹理不是二维纹理一样。
所以如果你想要一个数组纹理,你必须使用合适的采样器类型。由于它是立方体贴图数组纹理,因此您使用 samplerCubeArray
。而且你必须去掉数组下标。
从立方体贴图数组纹理中采样时,第四个分量是您要从中采样的数组层:
fragColor = texture(cubeMapArray, vec4(texCoord, 0));
此外,如果您打算使用立方体贴图阵列纹理,请完全不要绑定非阵列立方体贴图纹理。这不违法;它只是令人困惑,最好避免。
在超过纹理单元限制后,我决定使用立方体贴图数组纹理。
出于测试目的,我将立方体贴图的内容渲染为天空盒。问题是,当我绑定一个立方体贴图数组时,OpenGL 会忽略它,而是使用之前绑定的立方体贴图来渲染天空盒。
GL.BindTexture(TextureTarget.TextureCubeMap, shadowMaps[5].cubeMapTexture);
GL.BindTexture(TextureTarget.TextureCubeMapArray, shadowMapArray.cubeMapHandle); // Ignored
我假设这是错误创建的立方体贴图数组的默认行为。据我所知,我所做的一切都是正确的:Framebuffer complete
.
那么,创建和绑定立方体贴图数组的正确方法是什么?
public class CubeMapArray
{
public static int size = 512;
public static int layers = 8; // number of cube maps in array
public int FBO_handle;
public int cubeMapHandle;
public int cubeMapDepthHandle;
// Constructor //
public CubeMapArray()
{
// Create the FBO
GL.GenFramebuffers(1, out FBO_handle);
// Create and bind the CubeMap array
GL.GenTextures(1, out cubeMapHandle);
GL.BindTexture(TextureTarget.TextureCubeMapArray, cubeMapHandle);
// Allocate storage space
GL.TexImage3D(TextureTarget.TextureCubeMapArray, 0, PixelInternalFormat.Rg16, size, size, layers * 6, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
// Set the suitable texture parameters
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMaxLevel, 0);
// Create and bind the CubeMap depth array
GL.GenTextures(1, out cubeMapDepthHandle);
GL.BindTexture(TextureTarget.TextureCubeMapArray, cubeMapDepthHandle);
// Allocate storage space
GL.TexImage3D(TextureTarget.TextureCubeMapArray, 0, PixelInternalFormat.DepthComponent, size, size, layers * 6, 0, PixelFormat.DepthComponent, PixelType.UnsignedByte, IntPtr.Zero);
// Set the suitable texture parameters
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMapArray, TextureParameterName.TextureMaxLevel, 0);
// Attach cubemap texture as the FBO's color buffer
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBO_handle);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, cubeMapHandle, 0);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, cubeMapDepthHandle, 0);
// Error check
var errorcheck = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
Console.WriteLine("CUBEMAP ARRAY: " + errorcheck);
// Bind default framebuffer
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
}
}
}
着色器非常简单,令人惊讶的是,可以绑定常规立方体贴图。
顶点
#version 330
in vec3 texCoord;
out vec4 fragColor;
uniform samplerCube cubeMapArray[16];
void main (void)
{
fragColor = texture(cubeMapArray[0], texCoord);
}
片段
#version 330
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
in vec3 in_position;
out vec3 texCoord;
void main()
{
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_position, 1.0);
texCoord = in_position;
}
您需要在着色器代码中使用相应的采样器类型。立方体贴图数组在 OpenGL 4.0 及更高版本中可用,因此您还需要将 GLSL 版本提高到 400
或更高版本。
着色器代码将看起来像这样:
#version 400
...
uniform samplerCubeArray cubeMapArray;
...
fragColor = texture(cubeMapArray, vec4(texCoord, 0));
注意采样的数组层被指定为纹理坐标的第4个分量
uniform samplerCube cubeMapArray[16];
OK,有一个array of samplers and an array texture的区别。您在这里拥有的是一组采样器。您在 OpenGL 中创建的是数组纹理。
阵列纹理是 separate texture types 来自它们的非阵列对应物。立方体贴图数组纹理不是立方体贴图纹理;它是一个立方体贴图数组纹理。就像二维数组纹理不是二维纹理一样。
所以如果你想要一个数组纹理,你必须使用合适的采样器类型。由于它是立方体贴图数组纹理,因此您使用 samplerCubeArray
。而且你必须去掉数组下标。
从立方体贴图数组纹理中采样时,第四个分量是您要从中采样的数组层:
fragColor = texture(cubeMapArray, vec4(texCoord, 0));
此外,如果您打算使用立方体贴图阵列纹理,请完全不要绑定非阵列立方体贴图纹理。这不违法;它只是令人困惑,最好避免。