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

如何将图像序列加载到着色器中以实现独立采样?

实现帧序列的任意帧采样方案(GLSL/HLSL)

你提到的sampler2DArray/Texture2DArray和sampler3D/Texture3D都能实现需求,其中2D纹理数组是更适合视频帧序列的选择(因为所有帧分辨率一致,硬件优化更好),3D纹理则更偏向体积数据场景,但也能用来存储帧序列。以下是具体实现步骤:


GLSL(OpenGL环境)

方案1:sampler2DArray(推荐)

1. 从磁盘加载帧数据

  • 遍历磁盘上的帧文件(如frame_000.png到frame_127.png),用stb_image或FreeImage等库读取每帧像素数据,确保所有帧的分辨率、像素格式(如RGBA8)完全一致。
  • 将所有帧的像素数据按顺序拼接成连续内存块:总数据大小 = 单帧宽度 × 单帧高度 × 通道数 × 总帧数。

2. 创建并配置纹理数组

// 假设已加载好像素数据到data指针,单帧宽w、高h,总帧数128,格式RGBA8
GLuint texArray;
glGenTextures(1, &texArray);
glBindTexture(GL_TEXTURE_2D_ARRAY, texArray);

// 设置采样参数
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);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// 上传纹理数据
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, w, h, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

3. 片段着色器中采样

#version 450 core

uniform sampler2DArray video_frames;

in vec2 frag_uv;
out vec4 out_color;

// 生成0-127的随机帧索引示例
int get_random_frame() {
    return int(mod(frag_uv.x * frag_uv.y * gl_FragCoord.x * 128.0, 128.0));
}

void main() {
    int frame_idx = get_random_frame();
    // 用vec3(uv.x, uv.y, 浮点型帧索引)采样
    out_color = texture(video_frames, vec3(frag_uv, float(frame_idx)));
}

方案2:sampler3D

1. 加载帧数据

与sampler2DArray的加载方式一致,将所有帧按顺序拼接成连续内存块,3D纹理的"深度"维度对应总帧数。

2. 创建3D纹理

GLuint tex3D;
glGenTextures(1, &tex3D);
glBindTexture(GL_TEXTURE_3D, tex3D);

glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// 上传数据,深度参数设为总帧数128
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, w, h, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

3. 片段着色器中采样

#version 450 core

uniform sampler3D video_volume;

in vec2 frag_uv;
out vec4 out_color;

int get_random_frame() {
    return int(mod(frag_uv.x * frag_uv.y * gl_FragCoord.x * 128.0, 128.0));
}

void main() {
    int frame_idx = get_random_frame();
    // 3D采样的z坐标需归一化到0-1范围
    float z_coord = float(frame_idx) / 127.0;
    out_color = texture(video_volume, vec3(frag_uv, z_coord));
}

HLSL(DirectX环境)

方案1:Texture2DArray(推荐)

1. 加载帧数据

同GLSL步骤,读取所有帧并拼接成连续内存块,确保分辨率和格式一致。

2. 创建纹理数组及着色器资源视图(SRV)

// 假设使用DirectX 11,已初始化device和context
D3D11_TEXTURE2D_DESC texArrayDesc = {};
texArrayDesc.Width = w;
texArrayDesc.Height = h;
texArrayDesc.MipLevels = 1;
texArrayDesc.ArraySize = 128; // 总帧数
texArrayDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texArrayDesc.SampleDesc.Count = 1;
texArrayDesc.Usage = D3D11_USAGE_DEFAULT;
texArrayDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

// 准备每帧的子资源数据
std::vector<D3D11_SUBRESOURCE_DATA> subresources(128);
for (int i = 0; i < 128; ++i) {
    subresources[i].pSysMem = data + i * w * h * 4; // RGBA每像素4字节
    subresources[i].SysMemPitch = w * 4;
    subresources[i].SysMemSlicePitch = 0;
}

ID3D11Texture2D* texArray = nullptr;
device->CreateTexture2D(&texArrayDesc, subresources.data(), &texArray);

// 创建SRV并绑定到像素着色器槽位0
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = texArrayDesc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.MipLevels = 1;
srvDesc.Texture2DArray.FirstArraySlice = 0;
srvDesc.Texture2DArray.ArraySize = 128;

ID3D11ShaderResourceView* srv = nullptr;
device->CreateShaderResourceView(texArray, &srvDesc, &srv);
context->PSSetShaderResources(0, 1, &srv);

3. 像素着色器中采样

Texture2DArray<float4> video_frames : register(t0);
SamplerState sampler_linear : register(s0);

float4 PSShader(float2 frag_uv : TEXCOORD0) : SV_TARGET {
    int frame_idx = rand(128); // 自定义随机帧索引逻辑
    // HLSL支持直接用整数索引帧
    return video_frames.Sample(sampler_linear, float3(frag_uv, frame_idx));
}

方案2:Texture3D

1. 加载帧数据

将所有帧按顺序拼接成连续内存块,总大小为w × h × 128 × 4(RGBA8格式)。

2. 创建3D纹理及SRV

D3D11_TEXTURE3D_DESC tex3DDesc = {};
tex3DDesc.Width = w;
tex3DDesc.Height = h;
tex3DDesc.Depth = 128; // 深度对应总帧数
tex3DDesc.MipLevels = 1;
tex3DDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
tex3DDesc.Usage = D3D11_USAGE_DEFAULT;
tex3DDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

D3D11_SUBRESOURCE_DATA subresource = {};
subresource.pSysMem = data;
subresource.SysMemPitch = w * 4;
subresource.SysMemSlicePitch = w * h * 4; // 单帧字节数

ID3D11Texture3D* tex3D = nullptr;
device->CreateTexture3D(&tex3DDesc, &subresource, &tex3D);

// 创建SRV并绑定
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = tex3DDesc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
srvDesc.Texture3D.MipLevels = 1;
srvDesc.Texture3D.FirstMipLevel = 0;
srvDesc.Texture3D.DepthSliceCount = 128;

ID3D11ShaderResourceView* srv = nullptr;
device->CreateShaderResourceView(tex3D, &srvDesc, &srv);
context->PSSetShaderResources(0, 1, &srv);

3. 像素着色器中采样

Texture3D<float4> video_volume : register(t0);
SamplerState sampler_linear : register(s0);

float4 PSShader(float2 frag_uv : TEXCOORD0) : SV_TARGET {
    int frame_idx = rand(128);
    // z坐标归一化到0-1范围
    float z_coord = float(frame_idx) / 127.0f;
    return video_volume.Sample(sampler_linear, float3(frag_uv, z_coord));
}

关键注意事项

  • 格式匹配:加载帧的像素格式必须与创建纹理时的格式一致,否则会出现采样错误。
  • 性能优化:2D纹理数组的采样性能优于3D纹理,更适合视频帧序列场景。
  • 显存限制:高分辨率、多帧的序列会占用大量显存,需提前计算显存占用(如4K 128帧RGBA8约占4.1GB),确保显卡能承载。
  • 索引处理:GLSL中2D数组采样需将帧索引转为浮点型;3D纹理在两个环境中都需要将索引归一化到0-1范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 17:05:57