WebGL开发中如何基于纹理图集实现纹理重复平铺效果
WebGL多纹理重复平铺高性能实现方案
问题背景
正在基于WebGL开发游戏引擎,核心需求如下:
- 支持多纹理、多尺寸纹理加载渲染
- 支持单个图元上的纹理多次重复平铺效果
逐次绘制单独绑定新纹理的实现逻辑非常简单,但反复切换纹理绑定状态会带来严重的性能开销,无法支撑批量渲染需求。
常规单纹理的重复平铺实现门槛极低,只需将UV坐标范围从默认的0~1调整为目标重复范围即可,例如实现纹理重复2次的纹理坐标缓冲定义如下:
const texCoordBufferData = new Float32Array([ 0,2, 0,0, 2,2, 2,0, ]);
单纹理重复两次的渲染效果如下:
纹理图集是解决多纹理绑定性能问题的通用方案,目前已掌握纹理图集的制作与基础渲染逻辑:无重复需求时,只需根据子纹理在图集中的位置偏移、尺寸换算对应UV坐标即可正常渲染,基础渲染效果如下:
但现有技术资料仅提到使用纹理图集时需要手动在着色器中实现纹理重复/钳位逻辑,没有给出可落地的具体实现方法,需要明确:
- 基于纹理图集实现自定义纹理重复平铺的具体落地方案
- 其他可高性能支持多尺寸、多纹理重复平铺的替代方案
落地方案
方案1:纹理图集自定义重复逻辑(全WebGL版本兼容)
核心思路是将UV计算拆成局部重复计算和图集区域映射两个独立步骤,绝对不能颠倒顺序,否则会出现跨子纹理采样的错误。
具体实现步骤:
- 顶点数据仅传入对应图元的局部UV,范围固定为0~1,对应单张子纹理的完整覆盖区域,不要提前把重复次数、图集偏移固化到顶点UV里
- 按渲染批次/图元传入当前使用子纹理的参数:子纹理在图集中的U/V偏移值、U/V方向归一化尺寸、U/V方向的重复次数,需要其他包裹模式可额外传包裹类型参数
- 片元着色器中先在局部UV空间完成重复/包裹逻辑计算,再映射到图集的全局UV坐标,最后采样即可
最简化的片元着色器实现代码:
precision mediump float; uniform sampler2D u_atlas_tex; varying vec2 v_local_uv; // 顶点传入的0~1范围局部UV // 按当前渲染的子纹理传入对应参数 uniform float u_sub_u_offset; uniform float u_sub_v_offset; uniform float u_sub_u_scale; uniform float u_sub_v_scale; uniform float u_repeat_u; uniform float u_repeat_v; void main() { // 第一步:在局部UV空间计算重复坐标 vec2 repeat_local_uv = fract(v_local_uv * vec2(u_repeat_u, u_repeat_v)); // 第二步:将局部重复UV映射到图集对应子纹理的全局区域 vec2 atlas_global_uv = vec2(u_sub_u_offset, u_sub_v_offset) + repeat_local_uv * vec2(u_sub_u_scale, u_sub_v_scale); gl_FragColor = texture2D(u_atlas_tex, atlas_global_uv); }
其他包裹模式只需替换第一步的重复计算逻辑即可:
- 钳位模式(CLAMP_TO_EDGE):用
clamp(v_local_uv * vec2(u_repeat_u, u_repeat_v), 0.0, 1.0)替换fract计算 - 镜像重复模式(MIRRORED_REPEAT):对乘完重复次数的UV值做奇偶判断,偶数区间取fract值,奇数区间取
1.0 - fract值
注意事项:
- 必须给图集中的每个子纹理预留12像素的内边距,采样时将repeat_local_uv的范围从01缩放到
padding/子纹理像素尺寸 ~ 1 - padding/子纹理像素尺寸区间,避免采样到相邻子纹理的像素,出现边缘串色问题 - 禁止先将局部UV映射到图集全局空间再做fract取余,这是最常见的踩坑点,会直接导致重复逻辑跨子纹理边界失效
方案2:纹理数组(WebGL 2.0优先推荐)
如果不需要兼容极老旧的WebGL 1.0环境(目前WebGL 2.0全球覆盖率超过98%),纹理数组是比纹理图集维护成本更低、适配更简单的高性能方案,性能和纹理图集完全一致:
- 纹理数组将多张纹理存储在同一个纹理对象中,通过第三维的z轴索引切换不同的子纹理,渲染时不需要反复绑定纹理,可支撑和纹理图集同级别的批量渲染性能
- 原生支持所有纹理包裹模式(REPEAT/MIRRORED_REPEAT/CLAMP_TO_EDGE),不需要手动在着色器里实现重复逻辑,和单纹理渲染的UV逻辑完全一致,也不存在纹理图集的边缘串色问题
- 多尺寸纹理适配:按纹理尺寸分桶存储即可,例如将256x256分辨率的纹理归入一个纹理数组,512x512分辨率的归入另一个,渲染时按材质选择对应纹理数组、传入层索引即可,额外开销极低
内容的提问来源于stack exchange,提问作者Rob Louie
相关产品推荐
相关产品推荐

