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

如何结合GL_TEXTURE_2D_ARRAY与stb_image实现正确的精灵动画?

纹理数组实现精灵动画的问题排查与正确实现

问题定位

你的问题核心出在CPU端的纹理裁剪逻辑,着色器部分无致命错误但可优化。具体错误点:

  1. 精灵图Tile的内存偏移计算错误:原代码假设所有Tile在内存中是连续块排列,但实际精灵图是行优先的网格排列(比如6x6的图,第6个Tile是第二行第一列,而非连续内存的第6块),直接按连续块计算偏移会导致纹理采样错位。
  2. 错误设置GL_UNPACK_IMAGE_HEIGHT:该参数用于3D纹理的像素解包,你的源图是2D精灵图,设置它会干扰正常的像素解包逻辑。

修正方案

1. 修复纹理裁剪逻辑

重新计算每个Tile的内存偏移,按网格行列推导起始地址,同时移除不必要的解包参数:

int width, height, depth;
stbi_set_flip_vertically_on_load(true);
// 注意修正路径中的转义错误,示例改为正确的相对路径
byte_t* buffer = stbi_load(R"(./fire.jpg)", &width, &height, &depth, STBI_rgb_alpha);
if (buffer == nullptr) {
    std::cerr << "Could not read texture" << std::endl;
    return EXIT_FAILURE;
}
GLuint texture_id;
const GLenum target = GL_TEXTURE_2D_ARRAY;
SAFE_CALL(glGenTextures(1, &texture_id));
SAFE_CALL(glBindTexture(target, texture_id));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 1));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
SAFE_CALL(glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));

const GLsizei tile_w_count = 6, tile_h_count = 6;
const GLsizei total_tiles = tile_w_count * tile_h_count;
const GLsizei tile_w = width / tile_w_count,
              tile_h = height / tile_h_count;
std::cout << "Texture WxH: " << width << "x" << height;
std::cout << ", Tile WxH: " << tile_w << "x" << tile_h << std::endl;
SAFE_CALL(glTexStorage3D(target, 1, GL_RGBA8, tile_w, tile_h, total_tiles));

// 仅设置2D纹理解包所需的行长度,移除不必要的UNPACK_IMAGE_HEIGHT
SAFE_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, width));

for (GLsizei i = 0; i < total_tiles; ++i) {
    // 计算当前Tile在网格中的列、行索引
    GLsizei col = i % tile_w_count;
    GLsizei row = i / tile_w_count;
    
    // 推导Tile在源buffer中的起始字节偏移:行偏移*每行字节数 + 列偏移*单像素字节数
    size_t offset = (row * tile_h * width + col * tile_w) * depth;
    
    SAFE_CALL(glTexSubImage3D(
        GL_TEXTURE_2D_ARRAY,
        0,
        0, 0, i,
        tile_w, tile_h, 1,
        GL_RGBA,
        GL_UNSIGNED_BYTE,
        buffer + offset
    ));
}

// 记得释放stbi加载的内存
stbi_image_free(buffer);

2. 优化片元着色器

原着色器的四舍五入操作多余(整数+0.5后floor仍为原整数),简化边界钳制逻辑:

#version 460 core

in vec2 tex_coords;
out vec4 frag_color;

uniform sampler2DArray texture_0;

uniform int current_frame;
uniform int total_frames;

float actual_layer() {
    // 直接将整数帧索引转为float,同时钳制在有效范围内
    float frame = float(current_frame);
    return clamp(frame, 0.0, float(total_frames - 1));
}

void main() {
    frag_color = texture(texture_0, vec3(tex_coords, actual_layer()));
}

3. 额外注意事项

  • 确保精灵图的宽高能被tile_w_count和tile_h_count整除,否则tile_w/tile_h的计算误差会导致纹理错位。
  • 若启用mipmap,需调用glGenerateMipmap(GL_TEXTURE_2D_ARRAY),但你的代码仅分配了1级mipmap,无需额外操作。
  • 检查纹理文件路径是否正确,避免因路径错误导致纹理加载失败。

内容的提问来源于stack exchange,提问作者oiushder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:24:40