3d 调整相机以匹配光线和屏幕点

3d 调整相机以匹配光线和屏幕点,3d,camera,three.js,3d,Camera,Three.js,我正在使用three.js制作360度全景的免费查看工具。我希望当用户拖动屏幕上的点时,相机旋转,使该点正好位于鼠标指针下方 几何体是围绕世界原点的简单长方体几何体,摄影机是位于原点的透视摄影机: this.mesh = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2, 0, 0, 0), new THREE.MeshFaceMaterial(_array_of_THREE.MeshBasicMaterial_)); this.camera =

我正在使用three.js制作360度全景的免费查看工具。我希望当用户拖动屏幕上的点时,相机旋转,使该点正好位于鼠标指针下方


几何体是围绕世界原点的简单长方体几何体,摄影机是位于原点的透视摄影机:

this.mesh = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2, 0, 0, 0),
    new THREE.MeshFaceMaterial(_array_of_THREE.MeshBasicMaterial_));
this.camera = new THREE.PerspectiveCamera(90, width / height, 0.1, 2);
我有一个不准确的解决方案,它基于以下步骤:

  • 当用户开始拖动时,我记得世界坐标中的光线 指向拖动开始的屏幕点
  • 每当我想调整相机时(仅限当前拖动端),我都会计算指向鼠标指针当前位置的世界坐标中的光线
  • 然后,我计算将第一条光线带到目标所需的轴和角度 第二个是旋转
  • 我把摄像机方向的矢量绕轴旋转这个角度
  • 最后,我将相机设置为观察新方向 以下是步骤2-5的代码:

    adjustCamera: function(cameraDirection, worldRay, screenPoint){
            var angle;
            this.camera.lookAt(cameraDirection);
            this.raycaster.setFromCamera(screenPoint, this.camera);
            this.axis.copy(this.raycaster.ray.direction);
            angle = this.axis.angleTo(worldRay);
            this.axis.cross(worldRay).normalize();
            this.ray.copy(cameraDirection);
            this.ray.applyAxisAngle(this.axis, angle).normalize();
            this.camera.lookAt(this.ray);
        }
    
    我明白了为什么这个模式不起作用。以这种方式更改的相机方向会产生一些滚动(当旋转轴的坐标不为零时),这一点通过查看消除-它会消除滚动,只留下俯仰和偏航。这会导致一些不精确性,当初始光线和最终光线距离较远时,以及当初始相机位置具有较高的俯仰时,误差会增大。我现在被困在这里,不知道如何在没有滚动的情况下计算摄像机的位置


    因此,问题是:如何准确地旋转相机以将特定光线带到特定的屏幕点?(更适合我使用的模式)


    编辑: 实际上,可以有多个(如果光线不指向最低点或天顶,似乎不超过两个)正确(无滚动)的相机位置将世界光线投影到屏幕上的特定点

    想象一下下面的例子:接近天顶的光线应该和屏幕上半部分的点相匹配。第一个摄像头选项很明显,在这种情况下,第二个摄像头绕垂直轴旋转180度,并且具有更高的俯仰角。在第一个选项中,天顶投影在锁定点上方的屏幕上,在第二个选项中,天顶显示在下面


    在这种模棱两可的情况下,应选择最接近初始相机方向的选项。

    无论何时处理旋转,最好使用该选项

    我想说这个答案并不完整,因为它不会阻止相机滚动,但它会让你的起点在光标下,无论旋转如何。希望它能帮助我们找到一个完整的解决方案!代码可能也更有效,这是一个相当长的黑客会话

    注意:您可以忽略着色器材质,这只是在测试时给我一个参考点

    var canvas=document.getElementById('canvas');
    var scene=new THREE.scene();
    var renderer=new THREE.WebGLRenderer({canvas:canvas,antialas:true});
    var摄像机=新的三视角摄像机(70,canvas.clientWidth/canvas.clientWidth,11000);
    var geometry=新的3.BoxGeometry(10,10,10);
    //var几何=新的三种。球墨法(500,50,50);
    var material=新的3.ShaderMaterial({
    vertexShader:document.getElementById('vertex-shader').textContent,
    fragmentShader:document.getElementById('fragment-shader').textContent,
    侧面:三。背面
    });
    var mesh=新的三个网格(几何体、材质);
    场景。添加(网格);
    render();
    函数render(){
    请求动画帧(渲染);
    if(canvas.width!==canvas.clientWidth | | canvas.height!==canvas.clientHeight){
    renderer.setSize(canvas.clientWidth、canvas.clientHeight、false);
    camera.aspect=canvas.clientWidth/canvas.clientHeight;
    camera.updateProjectMatrix();
    }
    渲染器。渲染(场景、摄影机);
    }
    //控制
    var mousedown=false;
    var raycaster=new THREE.raycaster();
    var mouse=new THREE.Vector2();
    var lastQuat=新的三个四元数();
    var lastMouse=new THREE.Vector3();
    var v=新的3.Vector3();
    var-cam=camera.clone();
    功能鼠标指针(v、x、y){
    v、 设置(x/canvas.clientWidth*2-1,-y/canvas.clientHeight*2+1);
    }
    canvas.addEventListener('mousedown',函数(事件){
    mousedown=true;
    mouseToNDC(鼠标,event.layerX,event.layerY);
    raycaster.setFromCamera(鼠标、相机);
    复制(raycaster.ray.direction);
    lastQuat.copy(相机四元数);
    摄像机复制(摄像机);
    });
    window.addEventListener('mouseup',function(){
    mousedown=false;
    });
    canvas.addEventListener('mousemove',函数(事件){
    如果(鼠标向下){
    mouseToNDC(鼠标,event.layerX,event.layerY);
    raycaster.setFromCamera(鼠标、摄像头);
    camera.quaternion.setFromUnitVectors(raycaster.ray.direction,lastMouse).multiply(lastQuat);
    }
    });
    
    html,主体,#画布{
    保证金:0;
    填充:0;
    宽度:100%;
    身高:100%;
    溢出:隐藏;
    }
    
    可变vec2 vUv;
    void main(){
    vUv=紫外线;
    gl_位置=projectionMatrix*modelViewMatrix*vec4(位置,1.0);
    }
    #定义毛头6.2831853071795864769252867665590
    可变vec2 vUv;
    void main(){
    浮球x=地板(sin(5.0*M_头*vUv.x)/2.0+1.0);
    浮动y=地板(sin(5.0*M_头*vUv.y)/2.0+1.0);
    浮动c=最小值(x,y);
    gl_FragColor=vec4(vec3(c),1.0);
    }
    
    为类似的全景图目的解决这个问题花费了相当长的时间

    这里的复杂之处在于如何从摄像机注视
    方向
    和固定滚动方向的
    向上
    矢量构建旋转矩阵。矩阵最终投影常规X、Y和Z轴,以便:

    • Z匹配注视
      方向
    • X垂直于Z和
      向上
      矢量
    • Y垂直于X和Z以使轴正交
    像这样:

    axisZ = direction;
    axisX = normalize(cross(up, direction));
    axisY = cross(direction, axisX);
    
    由于
    向上
    方向
    不垂直,我们需要对