如何用 threejs 渲染 360 x 235 度 FOV 图像?

How to render 360 x 235 degree FOV image with threejs?

我有一个可以拍摄圆形图像的相机,声称可以代表 235 度的视野,比如这个:

我有一些使用 threejs 的经验,并使用它正确渲染存储为等距柱状投影的 360x360 图像 - 网络上有大量示例可以做到这一点。

但我想使用 threejs 渲染我的圆形图像,可能作为球体内部的纹理。我看过一些示例(例如 https://threejs.org/examples/#webgl_panorama_dualfisheye and Mapping image onto a sphere in Three.js),它们似乎为我指明了正确的方向,但我认为我对 threejs 如何将各个部分组合在一起的了解还不够。

第二个(眼球)似乎让我很接近,但遵循眼球代码恰好意味着我的图像出现了两次(我想这是有道理的,因为我的图像是方形的,纹理必须覆盖整个球体。

所以我尝试使用 SphereGeometry 的构造函数的长版本来制作一个部分球体 (360 * 235),但是改变 phi 给了我一个看起来像橙色缺失切片的东西。改变 theta 给了我正确的形状,但纹理没有正确放置。

所以我的总体问题是如何进行这项工作,但我认为难倒我的主要问题是:

  1. 什么控制纹理如何绘制到网格上? (这样我就可以只绘制一次纹理,并且只绘制到我需要的球体部分)

  2. 我看到设置 UV 是必要的 (geometry.faceVertexUvs?) 我读了 https://en.wikipedia.org/wiki/UV_mapping 所以我有点明白那是什么意思,但我不知道在哪里从为我的图像找出正确的 UV 开始(和数字 1 一样,如何阻止它显示在球体的 "back" 上)

好的,所以我去阅读了更多有关 UV 的内容并做了很多实验。这是工作代码(只是相关位),我大部分理解:

var geometry = new THREE.SphereGeometry( 30, 256, 128, 0, Math.PI * 2, 0, Math.PI );
geometry.scale( - 1, 1, 1 );//inside out

var imgWidth = 0.8;//I thought this should be 235/360, but used trial and error for less fisheye distortion

var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
for ( i = 0; i < faceVertexUvs.length; i ++ ) {

    var uvs = faceVertexUvs[ i ];
    var face = geometry.faces[ i ];
    //y is top to bottom
    //z is in-out?
    //x is side-side? positive x is front, negative is back

    for ( var j = 0; j < 3; j ++ ) {
        var fvNj = face.vertexNormals[j];
        var yaw = Math.atan2(fvNj.z, fvNj.x)/(Math.PI); //around, -1 to 1
        var pitch = Math.asin(fvNj.y)/Math.PI; //height, -0.5 to 0.5
        pitch = pitch * -1 + 0.5;//flip and make zero to one
        pitch *= imgWidth;

        if(pitch < 0.5){
            //how to do fisheye correction??
            //var correction = (fvNj.x == 0 && fvNj.z == 0) ? 1 : (Math.acos(fvNj.y) / Math.sqrt(fvNj.x * fvNj.x + fvNj.z * fvNj.z)) * (2 / Math.PI);
            var x = Math.cos(yaw * Math.PI);// -1 to 1
            var y = Math.sin(yaw * Math.PI);// -1 to 1
            var u = 0.5 + x*pitch;
            var v = 0.5 + y*pitch;  

            uvs[ j ].x = u;
            uvs[ j ].y = v;
        }else{
            uvs[j].x = 0;
            uvs[j].y = 0;
        }
    }
}

mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

如您所知,您的源图像不完整,因为它覆盖了 360x235。您必须“扩展”它以覆盖整个 360x360 球体。

我有一个相机也可以创建相同的图像;截至目前,我无法找到完整的 javascript 源来获取这些原始图像,转换为正确的格式并在球体上变形,所以我不得不找到另一个解决方案:提前转换图像。

我为此使用 ffmpeg。

使用此命令行:

ffmpeg -i qNfrX.jpg -vf v360=fisheye:e:ih_fov=235:iv_fov=235 test.jpg 

你得到这张图片:

改用此命令行:

ffmpeg -i qNfrX.jpg -vf v360=fisheye:e:ih_fov=235:iv_fov=235:pitch=-90 test2.jpg

使用不同的俯仰角,你得到这个图像:

然后你可以用一个这些equirectangular images来包裹一个球体,但在这方面我不能直接帮助;可能这个资源可以提供帮助,但我仍在研究它:

https://github.com/legokichi/three-fisheye