平面纹理坐标和球面坐标互相转换

平面纹理坐标和球面坐标互相转换

设有一张图片width/height = 2:1。这个比例很重要。因为这个比例的全景图片刚好可以还原成一张球形全景图。比喻展开的世界地图。
世界地图
把图片的宽 当成成 纬度,范围[0-2π]。
把图片的高当成经度,范围[-π/2,π/2]。

在半径为 r 的球面坐标中,设:水平转动角度为θ[0,2π],上下转动角度为φ[-π/2,π/2],所以球面上一点的三维坐标:

x = rcosθcosφ
y = rcosθsinφ
z = r*sinφ

从上面可以看出两者存在着对于的关系。至于什么关系写起来有点麻烦。
想象一下把展开的地图再次包裹在地球仪上。
或者想象一下用刀子把地球仪沿着本初子午线切开。然后展开成一张2:1的图片,至于怎么展开,参考下面的图,在南极北极开个口子,然后扯开,然后切开,展平。纹理的采样和切分的区域数量有关,下图是切割了8块,可以切割更多的小块来提高精度。
在这里插入图片描述

图片的宽高–球体的经纬度–球坐标系–空间坐标系
这四个参数之间有对应关系。
百度球坐标系

在这里插入图片描述参考这图片来理解。

为了方便观察,我制作了一张黑白的图片。经过经纬度转换之后可以变成下面的效果。下图的粒子效果是把图片中黑色的部分像素提出出来换成粒子代替。
在这里插入图片描述
在这里插入图片描述
如果用一张黑白的世界地图来代替的话就会生成一个炫酷的镂空粒子地球。
在这里插入图片描述
核心代码:

function imgTodata(src, r, d, pcolor, name, imgCallBack) {
    var imgData = [];
    var image = new Image;
    image.src = src;

    image.onload = function () {
        var canvas = document.createElement("canvas");
        canvas.width = image.width;
        canvas.height = image.height;
        imgData.width = image.width;
        imgData.height = image.height;
        var context = canvas.getContext("2d");
        context.drawImage(image, 0, 0, image.width, image.height);
        var data = context.getImageData(0, 0, canvas.width, canvas.height);
        var dLength = data.data.length;
        for (var i = 0; i < dLength; i += 4) {
            var x = (i / 4) % canvas.width;
            var y = (i / 4 - x) / canvas.width;
            if (i / 4 % 2 == 1 && y % 2 == 1 && 0 === data.data[i]) {
                var u = (360 / canvas.width) * x - 180;
                var v = (180 / canvas.height) * y - 90;
                var xyz = uvToxyz(u, v, r);
                imgData.push(xyz);
            }
        }
        var globeGeometry = new THREE.BufferGeometry();
        positionsa = [];
        var sprite = new THREE.TextureLoader().load('../img/disc1.png');
        var globeMaterial = new THREE.PointsMaterial({
            size: d + 0.2,
            color: pcolor,
            map: sprite,
            side: THREE.DoubleSide,
            opacity: 0.5,
        });
        imgData.forEach(e => {
            positionsa.push(e[0], e[1], e[2]);
        });
        globeGeometry.addAttribute('position', new THREE.Float32BufferAttribute(positionsa, 3));
        globeGeometry.computeBoundingSphere();
        var globes = new THREE.Points(globeGeometry, globeMaterial);
        globes.name = name;
        imgCallBack.call(this, globes);
    }
}

// 经纬度 转 xyz  uv to xyz
function uvToxyz(u, v, r) {
    var wd = (u + 90) * Math.PI / 180,
        jd = v * Math.PI / 180,
        x = -r * Math.cos(jd) * Math.cos(wd),
        y = -r * Math.sin(jd),
        z = r * Math.cos(jd) * Math.sin(wd);
    return [x, y, z];
}

代码中图片转换成数据,使用的canvas画布的图片处理功能实现的,逐像素读取if (i / 4 % 2 == 1 && y % 2 == 1 && 0 === data.data[i]) ,代码中添加了筛选函数,%2就是每隔一个点取样一次,%5就是每个5个取样一次,通过这个值就可以控制取样点的数量,也就是后期中粒子的数量。如果像素是黑色就读取坐标值保存在数组中,不是就抛弃。

x,y 对应列和行

  • var x = (i / 4) % canvas.width; // 第n列
  • var y = (i / 4 - x) / canvas.width; // 第n行
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X01动力装甲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值