GLSL中如何缩放纹理图集指定纹理并保持偏移
在GLSL中缩放纹理图集内的特定纹理(不显示其他纹理)
假设你有一个4x4的纹理图集(共16个纹理,每个纹理占图集的1/4区域),已经能通过索引定位到目标纹理,但直接缩放UV会导致纹理位置偏移,甚至采样到图集中的其他纹理。需要实现的是:保持目标纹理的原始偏移位置,单独对其进行放大/缩小,且不显示图集中的其他内容。
你当前的代码能定位到目标纹理,但直接缩放newUv会出现问题:
float scale = 1.0/4.0; vec2 offset = vec2(1.0,2.0); vec2 newUv = (vUv+offset)*scale; // 直接缩放会导致位置偏移、采样到其他纹理 // vec2 scaledUp = newUv*.8; // vec2 scaledDown = newUv*2.0;
核心解决方案
要实现正确的缩放,需要把UV坐标拆分为图集全局坐标和目标纹理的局部坐标:先对局部坐标做缩放变换,再映射回全局坐标,同时限制UV范围避免越界。
代码示例(中心对齐缩放,推荐)
这种方式会以目标纹理的中心为原点进行缩放,保持纹理在原位置中心放大/缩小:
// 纹理图集是4x4,单个纹理格子的尺寸占图集的1/4 float tileSize = 1.0 / 4.0; // 目标纹理在图集中的索引(x:1, y:2,对应你要定位的纹理位置) vec2 tileIndex = vec2(1.0, 2.0); // 缩放系数:>1是放大,<1是缩小 float textureScale = 2.0; // 1. 计算目标纹理在图集中的起始UV坐标(格子的左上角) vec2 tileStartUV = tileIndex * tileSize; // 2. 将原始vUv(0-1范围,对应目标纹理的局部空间)转换为中心原点的坐标,再缩放 vec2 centeredLocalUv = vUv - 0.5; // 把局部UV的原点移到纹理中心 vec2 scaledLocalUv = centeredLocalUv * textureScale; // 对局部UV进行缩放 scaledLocalUv += 0.5; // 把原点移回局部UV的左上角 // 3. 将缩放后的局部UV转换为图集的全局UV坐标 vec2 finalUv = tileStartUV + scaledLocalUv * tileSize; // 4. 可选但推荐:限制UV在目标纹理范围内,防止纹理过滤时采样到其他纹理 finalUv = clamp(finalUv, tileStartUV, tileStartUV + tileSize); // 采样纹理并输出颜色 vec4 texColor = texture2D(u_tex, finalUv); gl_FragColor = vec4(texColor.rgb, 1.0);
简化版(左上角对齐缩放)
如果你的需求是保持纹理左上角对齐缩放(而非中心对齐),可以用更简洁的代码:
float tileSize = 1.0 / 4.0; vec2 tileIndex = vec2(1.0, 2.0); float textureScale = 0.8; vec2 tileStartUV = tileIndex * tileSize; // 直接缩放局部UV,再映射到全局坐标 vec2 scaledLocalUv = vUv * textureScale; vec2 finalUv = tileStartUV + scaledLocalUv * tileSize; // 限制UV范围,避免采样到其他纹理 finalUv = clamp(finalUv, tileStartUV, tileStartUV + tileSize); gl_FragColor = vec4(texture2D(u_tex, finalUv).rgb, 1.0);
关键说明
- 为什么不能直接缩放全局UV?因为
newUv是图集的全局坐标,直接缩放会连偏移量一起放大/缩小,导致目标纹理的位置偏移,甚至跑到其他格子区域,采样到无关纹理。 clamp的作用:如果使用线性过滤或mipmap,纹理边缘的采样可能会超出目标格子的范围,用clamp限制UV后,边缘会重复采样目标纹理的边缘像素,不会显示其他纹理的内容。
内容的提问来源于stack exchange,提问作者Memory1
相关产品推荐
相关产品推荐

