摄影机

我们主要从以下方面为大家介绍摄像机以及摄像机的使用:

摄像机基本概念

摄像机,如同大家拍照时使用的相机,用来确定观察 3D 场景的视角。

摄像机包含两个重要的位置参数:镜头位置 position 和被拍摄物体的位置 target (又叫目标点)。

在 ThingJS 中,可以通过 app.camera 获取摄像机对象。

通过获取 camera 的 position 和 target 属性可以得到 3D 场景中摄像机的位置信息,也可以将其打印在控制台中。

var pos = app.camera.position; //获取摄像机镜头位置
var target = app.camera.target; //获取摄像机目标点位置
app.camera.log(); //在控制台中打印摄像机位置信息
//打印结果中第一个数组为镜头位置,第二个数组为目标点位置,如下
//[0, 20, 20], [-30, 10, 0]

设置摄影机位置

通过摄像机的 lookAt() 方法,可以让摄像机一直“盯着”某个位置或物体看。

//摄像机一直“盯着”[0,0,0]点看
app.camera.lookAt([0, 0, 0]); //
//摄像机一直“盯着”某物体看
var obj = app.query("car01")[0];
app.camera.lookAt(obj);
//取消摄影机一直盯着物体看
app.camera.lookAt(null);

通过摄像机的 fit() 方法,也可以设置摄像机位置。

app.camera.fit({
    position: [100, 100, 100],
    target: [0, 0, 0]
});

如果 fit() 方法中传的是物体对象,那么镜头将“聚焦”到该物体,此时 ThingJS 会计算出该对象的“最佳看点”,从而“自适应”该对象来设置摄像机位置。

//设置摄像机到物体的“最佳看点”
app.camera.fit(obj);

//当不传参数时,设置摄像机到当前整个场景下的“最佳看点”
app.camera.fit();

关于“最佳看点”

当摄像机看向目标物体时,一般将物体中心点作为 “看点” 的 target 位置。

我们可以通过相对于目标物体的坐标系下 x 轴旋转角度、 y 轴旋转角度以及距目标物体“中心”的距离来确定一个位置,作为“看点”的 position 位置。

比如,当( x 轴旋转角度, y 轴旋转角度,距离)=(0,0,1)时,是在正朝向物体 Z 轴方向上 1 倍包围球半径的位置看向物体。如图

但是上图并不是物体的“最佳看点”, ThingJS 中会默认计算一个物体的“最佳看点”。

我们将物体 包围盒 的中心点作为“最佳看点”的 target 位置。

以中心点的 X 轴旋转 45 度(xAngle:45),Y 轴旋转 -45 度方向(yAngle:-45),2倍包围球半径距离(radiusFactor:2)的位置为“最佳看点”的 position 位置。

如果用户需自定义 fit() 物体的摄像机位置,可控制以下参数来进行设置:

app.camera.fit({
    'object': obj,
    'xAngle': 60,  //绕物体自身X轴旋转角度
    'yAngle': 30,  //绕物体自身Y轴旋转角度
    'radiusFactor':3,  //物体包围球半径的倍数
});

设置摄像机飞行

通过 camera 的 flyTo() 方法,可以让摄像机从当前位置,飞行到将要设置的位置。

大部分参数和 fit 相同,该方法增加了 time(飞行时间)和 lerpType (插值方式)参数来控制飞行过程的速度,还可通过 complete 设置飞行结束后的回调函数。

 //从当前摄像机位置飞到指定位置,飞行时间2s,飞行结束后调用回调函数
app.camera.flyTo({
    position: [0, 20, 20],
    target: [-30, 10, 0],
    time: 2 * 1000,
    complete: function() {
    console.log("飞行结束");
    }
});
//以Quartic.In的插值方式 让飞行速度渐增
app.camera.flyTo({
    position: [0, 20, 20],
    target: [-30, 10, 0],
    time: 3 * 1000,
    lerpType: THING.LerpType.Quartic.In
});

//飞到物体的最佳视角,默认飞行时间3s
app.camera.flyTo(obj);

//2s飞到物体的最佳视角
app.camera.flyTo({
    object: obj,
    time: 2 * 1000,
    complete: function() {
    console.log("飞行结束");
    }
});

//自定义飞到物体的摄像机位置参数(同fit)
app.camera.flyTo({
    object: obj,
    xAngle: 30, //绕物体自身X轴旋转角度
    yAngle: 60, //绕物体自身Y轴旋转角度
    radiusFactor: 3, //物体包围盒半径的倍数
    time: 5 * 1000,
    complete: function() {
    console.log("飞行结束");
    }
});

// 也可以通过设置距离物体包围盒中心的距离(radius)确定摄像机位置
app.camera.flyTo({
    object: obj,
    xAngle: 30, //绕物体自身X轴旋转角度
    yAngle: 60, //绕物体自身Y轴旋转角度
    radius: 3, //距离物体包围盒中心的距离 单位米
    time: 5 * 1000,
    complete: function() {
    console.log("飞行结束");
    }
});

可以通过 app.camera.flying 属性,判断摄像机是否在飞行。

可以通过 app.camera.stopFlying() 来停止摄像机飞行。

ThingJS 还封装了一些和物体相关的摄像机方法,便于开发者使用。

  • rotateAround 让摄像机环绕某坐标点(世界坐标系下)或某物体旋转飞行

    //环绕[0,0,0]点旋转 180 度,5s 转完
    app.camera.rotateAround({
    target: [0,0,0],//环绕的坐标点
    time: 5*1000,//环绕飞行的时间
    yRotateAngle : 180,//环绕y轴飞行的旋转角度
    complete:function(){
        console.log('结束环绕飞行');
    }
    });
     
    //绕某物体飞行 180 度,5s 转完后反向转回,以此循环往复
    app.camera.rotateAround({
    object: obj,//环绕的物体
    time: 5*1000,//环绕飞行的时间
    yRotateAngle : 180,//环绕y轴飞行的旋转角度
    loopType:THING.LoopType.PingPong //循环往复环绕
    });
    • 通过 target 设置环绕的坐标点;
    • 通过 object 设置所需环绕的物体(object 与 target 的设置互斥);
    • 通过 time 设置环绕飞行的时间;
    • 通过 yRotateAngle 设置环绕y轴飞行的旋转角度;
    • 通过 loopType 设置循环类型 默认是 THING.LoopType.No;
    • 通过 complete 设置飞行结束后的回调函数(仅当 loopType 为 no 时生效);<

    可以通过 app.camera.stopRotateAround() 来停止摄像机飞行。

  • followObject 让摄像机一直跟随某物体

    app.camera.followObject(obj);

    相应的可调用 app.camera.stopFollowingObject() 停止摄像机跟随物体。

摄像机交互控制

ThingJS 摄像机提供了默认的平移、旋转、缩放的交互操作。

  • 对于鼠标输入而言:
    • 按住鼠标左键拖动,旋转场景;
    • 按住鼠标右键拖动,平移场景;
    • 滑动滚轮或按住滚轮拖动进行缩放;
  • 对于键盘输入而言:
    • 上下左右键,控制场景移动;
  • 对于触屏而言:
    • 单个手指滑动,旋转场景;
    • 两个手指平行滑动,平移场景;
    • 两个手指缩放,缩放场景;

可以通过设置 app.camera.inputEnabled=false 关闭所有默认的交互操作(旋转、平移、缩放);

以通过设置 app.camera.enablePan=false 关闭默认的平移操作;

可以通过设置 app.camera.enableRotate=false 关闭默认的旋转操作;

可以通过设置 app.camera.enableZoom=false 关闭默认的缩放操作;

对默认的交互操作,开发者可以控制操作过程中摄像机移动的速度,限制水平旋转角度、俯仰旋转角度。

//设置鼠标输入时移动摄像机的速度
app.camera.mousePanSpeed = 10;
//设置键盘输入时移动摄像机的速度
app.camera.keyPanSpeed  = 10;

//设置摄像机水平角度范围[最小值, 最大值]
app.camera.xAngleLimited = [30, 60];
//设置摄像机俯仰角度范围[最小值, 最大值]
app.camera.yAngleLimited = [30, 60];

当然开发者也可以通过 camera 的 move(),zoom(),rotateY(),rotateX()来控制摄像机的移动、缩放、旋转。

//摄像机水平移动 10m
app.camera.move(10, 0);
//摄像机垂直移动 10m
app.camera.move(0, 10);
//摄像机向前推进 10m
app.camera.zoom(10);
//设置摄像机target为圆心转在水平方向上旋转的夹角增量
app.camera.rotateY(20);
// 设置摄像机target为圆心转在竖直方向上旋转的夹角增量
app.camera.rotateX(20);

摄像机观察模式切换

//设置摄像机俯仰角度范围[最小值, 最大值]
app.camera.yAngleLimited = [30, 60];

事件

摄像机位置变动开始触发 CameraChangeStart ;

摄像机位置变动结束触发 CameraChangeEnd ;

摄像机位置变动中触发 CameraChange ;

摄像机位置缩放后触发 CameraZoom ;

改变摄像机观察模式后触发 CameraViewChange ;

//设置摄像机俯仰角度范围[最小值, 最大值]
app.camera.yAngleLimited = [30, 60];
app.on(THING.EventType.CameraChangeStart, function (ev) {
    console.log('CameraChangeStart ');
    console.log('pos:' + ev.position + ' target:' + ev.target);
});
app.on(THING.EventType.CameraChangeEnd, function (ev) {
    console.log('CameraChangeEnd ');
    console.log('pos:' + ev.position + ' target:' + ev.target);
})
app.on(THING.EventType.CameraChange, function (ev) {
    console.log(' CameraChange ');
    console.log('pos:' + ev.position + ' target:' + ev.target);
})
app.on(THING.EventType.CameraZoom,function(ev){
    console.log('CameraZoom ');
    console.log(ev.delta);
})
app.on(THING.EventType.CameraViewChange, function (ev) {
    console.log('CameraViewChange ');
    console.log(ev.view);
})

屏幕坐标与3D世界坐标转换

可通过 worldToScreen()screenToWorld() 进行 3D 世界坐标和 2D 屏幕坐标的相互转换。

help
注意事项

2D 屏幕坐标系以左上角为原点,向右为 X 正向,向下为 Y 正向

//世界坐标系下3D坐标转换成2D屏幕坐标
app.camera.worldToScreen(obj.position);
//返回结果是一个数组,如
//[445,421,0.9970992025530352]
//分别代表屏幕X坐标、Y坐标、Z深度

//屏幕坐标转3D世界坐标
//传入屏幕坐标
app.camera.screenToWorld([200,200]);
//返回三维坐标
//该坐标为经过摄像机position,target两点的直线 与 世界坐标系下过target点的垂直平面的交点
                                                            

屏幕坐标转 3D 世界坐标返回的为经过摄像机 position , target 两点的直线与三维世界坐标系下平面的交点,见下图:

摄像机高级设置

摄像机的 up 参数

可以通过 app.camera.up 获取或设置定义摄像机向上方向,默认值是[0,1,0],此时可理解为一个人正常拿着一个照相机照相如果设置成 [0,-1,0] 可理解为一个人倒着拿摄像机照相,效果如图:

而投影的方式有透视投影和正射投影两种:

  • 透视投影

    即离视点近的物体大,离视点远的物体小。

  • 正射投影

    又叫平行投影。它的特点是无论物体距离相机多远,投影后的物体大小尺寸不变。

在 ThingJS 中可通过 app.camera.porjectionType 设置摄像机的投影类型。

//透视投影
app.camera.projectionType= THING.CameraProjectionType.Perspective;
//正射投影
app.camera.projectionType= THING.CameraProjectionType.Orthographic;

视锥体

视锥体为可以观看投影物体的可视区域,由近平面,远平面,左,上,右,下各面组成。 只有在可视区域内才可见,区域外会被裁剪。

在 ThingJS 中可以设置 camera 的远剪裁面 far 、近剪裁面 nearfov 来设置可视区域范围。

FOV(视场角)即Field of View,类似于摄影中的焦距,值越大,画面显示的内容就越多,但也越扭曲。

//设置近裁剪面的距离,比这个距离近的物体将不会被渲染
app.camera.near = 10;//默认值0.1
//设置远裁剪面的距离,比这个距离远的物体将不会被渲染
app.camera.far= 100; //默认值1000
 //设置摄像机FOV
app.camera.fov = 30;//默认值60

可通过 isInView() 判断场景中某个物体是否在可视范围内。

app.camera.isInView(obj);
在线咨询
QQ交流群
微信公众号