OpenGL 中的纹理数组

Texture arrays in OpenGL

我正在做一个项目,我需要使用纹理数组来应用纹理。我已经问了很多关于这个的问题,none 我得到了一个我完全满意的答案 (Get access to later versions of GLSL , , and OpenGL: How would I implement texture arrays?) 所以我问了一个更广泛的问题,希望能得到回应。无论如何,我将如何在 OpenGL 中对对象进行纹理处理(更具体地说是 PyOpenGL,但如果您将答案放在 C++ 中就可以了)。我已经有办法加载纹理数组,只是没有办法应用它。这是期望的结果:

图片来自opengl-tutorial

这是我目前用于加载数组纹理的内容:

def load_texture_array(path,width,height):
    teximg = pygame.image.load(path)
    texels = teximg.get_buffer().raw
    texture = GLuint(0)

    layerCount = 6
    mipLevelCount = 1

    glGenTextures(1, texture)
    glBindTexture(GL_TEXTURE_2D_ARRAY, texture)
    glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount)
    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGBA, GL_UNSIGNED_BYTE, texels)

    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)

TLDR:如何使用纹理数组将纹理应用于 OpenGL 中的对象?

如有必要,我很乐意提供任何其他信息。

如果要对立方体使用 2D Array Texture,第 6 个面的 6 个纹理中的每一个都必须具有相同的大小。 您可以通过 3 维纹理坐标查找纹理。纹理坐标的第三个分量是二维纹理在二维纹理数组中的索引。
因此 6 边的纹理坐标是

0:  [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
1:  [(0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]
2:  [(0, 0, 2), (1, 0, 2), (1, 1, 2), (0, 1, 2)]
3:  [(0, 0, 3), (1, 0, 3), (1, 1, 3), (0, 1, 3)]
4:  [(0, 0, 4), (1, 0, 4), (1, 1, 4), (0, 1, 4)]
5:  [(0, 0, 5), (1, 0, 5), (1, 1, 5), (0, 1, 5)]

在顶点着色器中获取3维纹理坐标属性并传递给片段着色器:

in a_uv;
out v_uv;

// [...]

void main()
{
    v_uv = a_uv;

    // [...]
}

使用3维纹理坐标在片段着色器中查找sampler2DArray

out v_uv;
uniform sampler2DArray u_texture;

// [...]

void main()
{
    vec4 texture(u_texture, v_uv.xyz);

    // [...]
}

创建一个GL_TEXTURE_2D_ARRAY并使用glTexSubImage3D加载6个二维图像到2D Array Texture的6个平面。下面image_planes是一个包含6个二维图像平面的列表:

tex_obj = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D_ARRAY, self.tex_obj)
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, sizeX, sizeY, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
for i in range(6):
    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, sizeX, sizeY, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_planes[i])
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

另见 PyGame and OpenGL 4


最小示例:

import os, math, ctypes
import glm
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from OpenGL.arrays import *
import pygame

pygame.init()

image_path = r"images"
image_names = ["banana64.png", "apple64.png", "fish64.png", "rocket64.png", "ice64.png", "boomerang64.png"]

image_planes = [
    (GLubyte * 4)(255, 0, 0, 255), (GLubyte * 4)(0, 255, 0, 255), (GLubyte * 4)(0, 0, 255, 255),
    (GLubyte * 4)(255, 255, 0, 255), (GLubyte * 4)(0, 255, 255, 255), (GLubyte * 4)(255, 0, 255, 255)]
image_size = (1, 1)

for i, filename in enumerate(image_names):
    try:
        image = pygame.image.load(os.path.join(image_path, filename))
        image_size = image.get_size()
        image_planes[i] = pygame.image.tostring(image, 'RGBA')
    except:
        pass

class MyWindow:

    __glsl_vert = """
        #version 130

        in vec3 a_pos;
        in vec3 a_nv;
        in vec3 a_uv;

        out vec3 v_pos;
        out vec3 v_nv;
        out vec3 v_uv;

        uniform mat4 u_proj;
        uniform mat4 u_view;
        uniform mat4 u_model;

        void main()
        {
            mat4 model_view = u_view * u_model;
            mat3 normal     = mat3(model_view);

            vec4 view_pos   = model_view * vec4(a_pos.xyz, 1.0);

            v_pos       = view_pos.xyz;
            v_nv        = normal * a_nv;  
            v_uv        = a_uv;  
            gl_Position = u_proj * view_pos;
        }
    """

    __glsl_frag = """
        #version 130

        out vec4 frag_color;
        in  vec3 v_pos;
        in  vec3 v_nv;
        in  vec3 v_uv;

        uniform sampler2DArray u_texture;

        void main()
        {
            vec3  N     = normalize(v_nv);
            vec3  V     = -normalize(v_pos);
            float ka    = 0.1;
            float kd    = max(0.0, dot(N, V)) * 0.9;
            vec4  color = texture(u_texture, v_uv.xyz);
            frag_color  = vec4(color.rgb * (ka + kd), color.a);
        }
    """

    def __init__(self, w, h):
        self.__caption = 'OpenGL Window'
        self.__vp_size = [w, h]

        pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24)  
        self.__screen = pygame.display.set_mode(self.__vp_size, pygame.DOUBLEBUF| pygame.OPENGL)
        
        self.__program = compileProgram( 
            compileShader( self.__glsl_vert, GL_VERTEX_SHADER ),
            compileShader( self.__glsl_frag, GL_FRAGMENT_SHADER ),
        )
        self.___attrib = { a : glGetAttribLocation (self.__program, a) for a in ['a_pos', 'a_nv', 'a_uv'] }
        print(self.___attrib)
        self.___uniform = { u : glGetUniformLocation (self.__program, u) for u in ['u_model', 'u_view', 'u_proj'] }
        print(self.___uniform)

        v = [[-1,-1,1], [1,-1,1], [1,1,1], [-1,1,1], [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1]]
        n = [[0,0,1], [1,0,0], [0,0,-1], [-1,0,0], [0,1,0], [0,-1,0]]
        e = [[0,1,2,3], [1,5,6,2], [5,4,7,6], [4,0,3,7], [3,2,6,7], [1,0,4,5]]
        t = [[0, 0], [1, 0], [1, 1], [0, 1]]
        index_array = [si*4+[0, 1, 2, 0, 2, 3][vi] for si in range(6) for vi in range(6)]
        attr_array = []
        for si in range(len(e)):
            for i, vi in enumerate(e[si]):
                attr_array += [*v[vi], *n[si], *t[i], si]

        self.__no_vert = len(attr_array) // 10
        self.__no_indices = len(index_array)
        vertex_attributes = (ctypes.c_float * len(attr_array))(*attr_array)
        indices = (ctypes.c_uint32 * self.__no_indices)(*index_array)

        self.__vao = glGenVertexArrays(1)
        self.__vbo, self.__ibo = glGenBuffers(2)

        glBindVertexArray(self.__vao)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.__ibo)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
        glBindBuffer(GL_ARRAY_BUFFER, self.__vbo)
        glBufferData(GL_ARRAY_BUFFER, vertex_attributes, GL_STATIC_DRAW)

        float_size = ctypes.sizeof(ctypes.c_float)   
        glVertexAttribPointer(self.___attrib['a_pos'], 3, GL_FLOAT, False, 9*float_size, None)
        glVertexAttribPointer(self.___attrib['a_nv'], 3, GL_FLOAT, False, 9*float_size, ctypes.c_void_p(3*float_size))
        glVertexAttribPointer(self.___attrib['a_uv'], 3, GL_FLOAT, False, 9*float_size, ctypes.c_void_p(6*float_size))
        glEnableVertexAttribArray(self.___attrib['a_pos'])
        glEnableVertexAttribArray(self.___attrib['a_nv'])
        glEnableVertexAttribArray(self.___attrib['a_uv'])

        glEnable(GL_DEPTH_TEST)
        glUseProgram(self.__program)

        glActiveTexture(GL_TEXTURE0)
        sizeX, sizeY = image_size
        self.tex_obj = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D_ARRAY, self.tex_obj)
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, sizeX, sizeY, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
        for i in range(6):
            glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, sizeX, sizeY, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_planes[i])
        glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

    def run(self):
        self.__starttime = 0
        self.__starttime = self.elapsed_ms()
        
        run = True
        while run:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    run = False
            self.__mainloop()
            pygame.display.flip()

        pygame.quit()

    def elapsed_ms(self):
      return pygame.time.get_ticks() - self.__starttime

    def __mainloop(self):

        proj, view, model  = glm.mat4(1), glm.mat4(1), glm.mat4(1)
        aspect = self.__vp_size[0]/self.__vp_size[1]
        proj = glm.perspective(glm.radians(90.0), aspect, 0.1, 10.0)
        view = glm.lookAt(glm.vec3(0,-3,0), glm.vec3(0, 0, 0), glm.vec3(0,0,1))
        angle1 = self.elapsed_ms() * math.pi * 2 / 5000.0
        angle2 = self.elapsed_ms() * math.pi * 2 / 7333.0
        model = glm.rotate(model, angle1, glm.vec3(1, 0, 0))
        model = glm.rotate(model, angle2, glm.vec3(0, 1, 0))

        glUniformMatrix4fv(self.___uniform['u_proj'], 1, GL_FALSE, glm.value_ptr(proj) )
        glUniformMatrix4fv(self.___uniform['u_view'], 1, GL_FALSE, glm.value_ptr(view) )
        glUniformMatrix4fv(self.___uniform['u_model'], 1, GL_FALSE, glm.value_ptr(model) )

        glClearColor(0.2, 0.3, 0.3, 1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        glDrawElements(GL_TRIANGLES, self.__no_indices, GL_UNSIGNED_INT, None)

window = MyWindow(800, 600)
window.run()