如何为球体正确计算UV坐标?解决纹理映射拉伸变形问题
问题分析与解决方案
核心问题:投影不匹配
你遇到的赤道区域缩小、极地区域放大的问题,本质是OSM瓦片的Web墨卡托投影(EPSG:3857)和你当前使用的线性/球面UV映射逻辑不兼容。
OSM的瓦片采用Web墨卡托投影,它对纬度的映射是非线性的:高纬度区域在纹理中会占据更多像素(墨卡托投影天然会拉伸高纬度区域),而你用的v=(lat+90)/180是线性映射,直接把球面纬度线性对应到纹理Y轴,这就导致了错位——球面赤道的大区域对应纹理的窄条像素(看起来缩小),球面极地的小区域对应纹理的宽条像素(看起来放大)。
正确的UV计算方式
针对Web墨卡托纹理,你需要用墨卡托投影的纬度转换公式来计算v值,u值的两种原有计算方式是可行的(二者逻辑等价):
完整UV代码示例
// 若输入纬度为角度,先转为弧度 const latRad = lat * Math.PI / 180; // 计算Web墨卡托投影的y分量 const yMercator = Math.log(Math.tan(Math.PI / 4 + latRad / 2)); // 将墨卡托y值归一化为[0,1]的纹理v坐标(匹配OSM瓦片的有效纬度范围±85.0511°) const v = (yMercator + Math.PI) / (2 * Math.PI); // u值二选一即可,结果一致 // 方式1:基于经纬度计算 const u = (lon + 180) / 360; // 方式2:基于顶点笛卡尔坐标计算(x对应经度0方向,z对应经度90°方向) const u = Math.atan2(z, x) / (2 * Math.PI) + 0.5;
纹理尺寸适配说明
对于你制作的1024×1083纹理:
- 中间1024×1024区域对应Web墨卡托的有效纬度范围(±85.0511°),用上述UV公式映射;
- 顶部和底部的纯色区域对应纬度±85.0511°到±90°,保持你现有片元着色器的纯色填充逻辑即可。
验证逻辑
- 当lat=0°(赤道)时,
yMercator=0,v=0.5,和线性映射结果一致; - 当lat=85.0511°时,
yMercator=π,v=1,刚好对应OSM瓦片的顶部边界; - 当lat=-85.0511°时,
yMercator=-π,v=0,对应OSM瓦片的底部边界;
这种映射完全匹配OSM纹理的像素分布,可解决拉伸/压缩问题。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

