You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenGL中如何用纹理数组为物体贴纹理?(PyOpenGL优先)

Got it, let's break down exactly how to use that texture array you’ve already loaded to apply textures to your objects in OpenGL. Since you mentioned prioritizing PyOpenGL, I’ll lead with that, and toss in a quick C++ snippet if you need it later.


Step 1: Update Your Shaders to Support Texture Arrays

The biggest difference between a single texture and a texture array is how you sample it in GLSL. You’ll need to use a sampler2DArray instead of a regular sampler2D, and sample with a 3D coordinate (texture UVs + layer index).

First, make sure your GLSL version is at least 330 (since sampler2DArray requires this). Here’s what your shaders should look like:

Vertex Shader

This passes vertex positions, texture coordinates, and the texture layer index to the fragment shader:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
layout (location = 2) in float aLayerIndex; // Which texture layer this vertex uses

out vec2 TexCoords;
out float LayerIndex;

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

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoords = aTexCoords;
    LayerIndex = aLayerIndex;
}

Fragment Shader

This samples the texture array using the UVs and layer index from the vertex shader:

#version 330 core
out vec4 FragColor;

in vec2 TexCoords;
in float LayerIndex;

uniform sampler2DArray textureArray; // Matches our loaded texture array

void main()
{
    // Sample with vec3(UV.x, UV.y, layer_index)
    FragColor = texture(textureArray, vec3(TexCoords, LayerIndex));
}

Step 2: Prepare Vertex Data with Layer Information

You’ll need to add a layer index attribute to your vertex data. For example, if you’re texturing a cube with 6 different textures (one per face), each face’s vertices will get a unique layer index (0 to 5, since your texture array has 6 layers).

Here’s a simplified PyOpenGL example of cube vertex data (each entry is x, y, z, u, v, layer_index):

import numpy as np
import ctypes

# Cube vertices: position (3f) + UV (2f) + layer index (1f)
cube_vertices = np.array([
    # Front face (layer 0)
    -0.5, -0.5,  0.5,  0.0, 0.0, 0.0,
     0.5, -0.5,  0.5,  1.0, 0.0, 0.0,
     0.5,  0.5,  0.5,  1.0, 1.0, 0.0,
    -0.5,  0.5,  0.5,  0.0, 1.0, 0.0,
    # Back face (layer 1)
    -0.5, -0.5, -0.5,  0.0, 0.0, 1.0,
     0.5, -0.5, -0.5,  1.0, 0.0, 1.0,
     0.5,  0.5, -0.5,  1.0, 1.0, 1.0,
    -0.5,  0.5, -0.5,  0.0, 1.0, 1.0,
    # Left face (layer 2)
    -0.5,  0.5,  0.5,  0.0, 1.0, 2.0,
    -0.5,  0.5, -0.5,  1.0, 1.0, 2.0,
    -0.5, -0.5, -0.5,  1.0, 0.0, 2.0,
    -0.5, -0.5,  0.5,  0.0, 0.0, 2.0,
    # Right face (layer 3)
     0.5,  0.5,  0.5,  0.0, 1.0, 3.0,
     0.5,  0.5, -0.5,  1.0, 1.0, 3.0,
     0.5, -0.5, -0.5,  1.0, 0.0, 3.0,
     0.5, -0.5,  0.5,  0.0, 0.0, 3.0,
    # Top face (layer 4)
    -0.5,  0.5, -0.5,  0.0, 1.0, 4.0,
     0.5,  0.5, -0.5,  1.0, 1.0, 4.0,
     0.5,  0.5,  0.5,  1.0, 0.0, 4.0,
    -0.5,  0.5,  0.5,  0.0, 0.0, 4.0,
    # Bottom face (layer 5)
    -0.5, -0.5, -0.5,  0.0, 1.0, 5.0,
     0.5, -0.5, -0.5,  1.0, 1.0, 5.0,
     0.5, -0.5,  0.5,  1.0, 0.0, 5.0,
    -0.5, -0.5,  0.5,  0.0, 0.0, 5.0
], dtype=np.float32)

# Set up VAO/VBO to handle the new layer attribute
vao = glGenVertexArrays(1)
vbo = glGenBuffers(1)
glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, cube_vertices.nbytes, cube_vertices, GL_STATIC_DRAW)

# Position attribute (location 0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)

# UV attribute (location 1)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(3 * 4))
glEnableVertexAttribArray(1)

# Layer index attribute (location 2)
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(5 * 4))
glEnableVertexAttribArray(2)

Step 3: Fix Your Texture Array Loading (Potential Bug)

Looking at your existing load_texture_array function, there’s a common pitfall: if your source image is a horizontal/vertical strip of 6 textures, you can’t upload the entire image at once. You need to split it into individual tiles and upload each to a separate layer. Here’s the corrected version:

def load_texture_array(path, tile_width, tile_height):
    teximg = pygame.image.load(path)
    img_width, img_height = teximg.get_size()
    
    # Verify the image matches our tile dimensions (6 tiles wide, 1 tall)
    assert img_width == tile_width * 6 and img_height == tile_height, "Image size doesn't match tile layout"
    
    texture = GLuint(0)
    layer_count = 6
    mip_levels = 1
    glGenTextures(1, texture)
    glBindTexture(GL_TEXTURE_2D_ARRAY, texture)
    glTexStorage3D(GL_TEXTURE_2D_ARRAY, mip_levels, GL_RGBA8, tile_width, tile_height, layer_count)
    
    # Upload each texture tile to its own layer
    for layer in range(layer_count):
        # Extract the tile from the source image
        start_x = layer * tile_width
        tile_surface = pygame.Surface((tile_width, tile_height))
        tile_surface.blit(teximg, (0, 0), (start_x, 0, tile_width, tile_height))
        texels = tile_surface.get_buffer().raw
        
        # Upload to the current layer
        glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, tile_width, tile_height, 1, GL_RGBA, GL_UNSIGNED_BYTE, texels)
    
    # Set texture parameters
    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)
    
    return texture.value

Step 4: Render with the Texture Array

In your main render loop, you just need to bind the texture array, link it to your shader’s uniform, and draw your object:

# Assume you have a compiled shader program stored in shader_program
# And texture_array_id is the ID returned from load_texture_array

while running:
    # Clear screen
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    
    # Use shader program
    glUseProgram(shader_program)
    
    # Set up model/view/projection matrices (fill in your own values)
    model = glm.mat4(1.0)
    view = glm.lookAt(glm.vec3(3.0, 3.0, 3.0), glm.vec3(0.0, 0.0, 0.0), glm.vec3(0.0, 1.0, 0.0))
    projection = glm.perspective(glm.radians(45.0), width/height, 0.1, 100.0)
    
    glUniformMatrix4fv(glGetUniformLocation(shader_program, "model"), 1, GL_FALSE, glm.value_ptr(model))
    glUniformMatrix4fv(glGetUniformLocation(shader_program, "view"), 1, GL_FALSE, glm.value_ptr(view))
    glUniformMatrix4fv(glGetUniformLocation(shader_program, "projection"), 1, GL_FALSE, glm.value_ptr(projection))
    
    # Bind texture array to texture unit 0
    glActiveTexture(GL_TEXTURE0)
    glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array_id)
    # Tell the shader which texture unit to use
    glUniform1i(glGetUniformLocation(shader_program, "textureArray"), 0)
    
    # Draw the cube
    glBindVertexArray(vao)
    glDrawArrays(GL_TRIANGLES, 0, 36)
    
    # Swap buffers
    pygame.display.flip()

Quick C++ Snippet (If Needed)

The logic is identical in C++—here’s the core rendering code:

// Bind texture array to unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayID);
// Link to shader uniform
shader.setInt("textureArray", 0); // Replace with your shader's uniform set method

// Draw
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);

Troubleshooting Tips
  • GLSL Version Check: Make sure your OpenGL context is set to 3.3 or higher. In PyOpenGL with Pygame, add this before creating the window:
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 3)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 3)
    pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, pygame.GL_CONTEXT_PROFILE_CORE)
    
  • Layer Index Range: Ensure your layer indices are between 0 and 5 (since your array has 6 layers). Using values outside this range will cause sampling errors.
  • Texture Format Match: Confirm your image’s RGBA format matches the GL_RGBA8 and GL_RGBA/GL_UNSIGNED_BYTE parameters in your loading code.

内容的提问来源于stack exchange,提问作者User-92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 09:07:32