Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
421 views
in Technique[技术] by (71.8m points)

python - Texture arrays in OpenGL

I am working on a project and I need to use texture arrays to apply textures. I have asked many questions about this, none of which I got an answer I was completely satisfied with (Get access to later versions of GLSL , OpenGL: Access Array Texture in GLSL , and OpenGL: How would I implement texture arrays?) so I'm asking a more broad question to hopefully get a response. Anyways, How would I texture an object in OpenGL (PyOpenGL more specifically, but it's fine if you put your answer in C++). I already have a way to load the texture arrays, just not a way to apply it. This is the desired result:

Example

Image from opengl-tutorial

and this is what I currently have for loading array textures:

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: How would I apply textures to objects in OpenGL using texture arrays?

I will happily provide any other information if necessary.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you want to use a 2D Array Texture for a cube, each of the 6 textures for the 6 side must be the same size. You can lookup the texture by 3 dimensional texture coordinates. The 3rd component of the texture coordinate is the index of the 2d texture in the 2d texture array.
Hence the texture coordinates for the 6 sides are

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)]

Get the 3 dimensional texture coordinate attribute in the vertex shader and pass it to the fragment shader:

in a_uv;
out v_uv;

// [...]

void main()
{
    v_uv = a_uv;

    // [...]
}

Use the 3 dimensional texture coordinate to look up the sampler2DArray in the fragment shader:

out v_uv;
uniform sampler2DArray u_texture;

// [...]

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

    // [...]
}

Create a GL_TEXTURE_2D_ARRAY and use glTexSubImage3D to load 6 2-dimensional images to the 6 planes of the 2D Array Texture. In the following image_planes is a list with the 6 2-dimensional image planes:

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)

See also PyGame and OpenGL 4.


Minimal example:

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()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...