WebGL非等宽高图像旋转失真问题求助
WebGL非正方形纹理旋转失真问题解决思路
嘿,我来帮你拆解这个WebGL里的旋转纹理失真问题~
你遇到的情况其实很典型:当纹理是正方形时旋转一切正常,但换成非正方形(比如256x32)就出现失真,而且看起来只有纹理在转、顶点没动,还没出现裁剪——核心问题大概率出在纹理坐标的旋转没有适配纹理宽高比,同时顶点和纹理的变换不同步上。
为什么会出现这种情况?
- 如果你只在片段着色器里对纹理UV坐标做旋转,却没考虑纹理的宽高比,那非正方形纹理的UV会因为旋转时的比例失衡被拉伸变形;
- 同时顶点还是保持原来的矩形范围,旋转后的纹理会被强制填充到这个矩形里,自然不会出现边角被裁剪的情况,反而会因为拉伸导致失真。
具体解决步骤
1. 在片段着色器里修正UV旋转的宽高比
如果你的需求是保持顶点位置不变,只旋转纹理,那必须在旋转UV时加入宽高比修正,避免拉伸:
precision lowp float; uniform sampler2D uTexture; uniform vec2 uTextureSize; // 传入纹理的实际宽高(比如vec2(256.0, 32.0)) uniform float uRotation; // 旋转角度(弧度) varying vec2 vTexCoord; void main() { // 把UV坐标转换为以中心为原点的坐标系(范围从[0,1]变为[-0.5,0.5]) vec2 uv = vTexCoord - 0.5; // 计算宽高比修正因子,让非正方形纹理的UV在旋转前先“拉正” vec2 aspectFix = vec2(uTextureSize.y / uTextureSize.x, 1.0); uv *= aspectFix; // 应用旋转矩阵 float cosRot = cos(uRotation); float sinRot = sin(uRotation); mat2 rotMatrix = mat2(cosRot, -sinRot, sinRot, cosRot); uv = rotMatrix * uv; // 把UV转换回原范围,同时撤销宽高比修正 uv /= aspectFix; uv += 0.5; // 可选:判断UV是否超出范围,避免重复/拉伸边缘 if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { gl_FragColor = vec4(0.0); // 超出部分设为透明或其他颜色 } else { gl_FragColor = texture2D(uTexture, uv); } }
2. 让顶点和纹理同步旋转(更推荐的常规做法)
如果你的需求是整个图像(顶点+纹理)一起旋转,那应该在顶点着色器里对顶点位置应用旋转矩阵,保持顶点和纹理坐标的对应关系:
// 顶点着色器代码 precision lowp float; attribute vec3 vertPosition; attribute vec2 vertTexCoord; uniform mat4 uModelMatrix; // 包含旋转、平移等变换的模型矩阵 varying vec2 vTexCoord; void main() { gl_Position = uModelMatrix * vec4(vertPosition, 1.0); vTexCoord = vertTexCoord; }
然后在JS代码中创建旋转矩阵,将其传入uModelMatrix uniform变量,这样顶点会跟着旋转,纹理也会同步正确映射,不会出现单独旋转纹理的违和感,也不会有失真问题。
3. 检查纹理的Wrap模式
如果你的纹理设置了gl.REPEAT的wrap模式,旋转后超出UV范围的部分会重复填充,可能让你误以为没有裁剪。可以改成gl.CLAMP_TO_EDGE,或者像上面代码里那样在片段着色器里做边界判断,控制超出部分的显示。
总结
你现在的问题本质是纹理坐标旋转时的比例失衡加上顶点与纹理变换不同步导致的失真。按照上面的方法调整后,非正方形纹理的旋转应该就能正常显示啦~
内容的提问来源于stack exchange,提问作者Elias
相关产品推荐
相关产品推荐

