threejs 自学 关于摄像机的使用

摄像机运动的理解

观察一个模型的方式

  1. 保持摄像机不变情况下,通过修改被观察模型的旋转角度、位置、大小,来实现;
  2. 旋转摄像机的情况下,通过轨道控制器Orbit controls,实现摄像机的变化
  3. 不借助于轨道控制器,通过脚本来旋转摄像机 - (更加灵活,最重要一点,不需要人为操作)

基本使用

  1. 三维图查看角度
  • x轴方向观察
// 通过UI按钮改变相机观察角度
document.getElementById('x').addEventListener('click', function () {
    camera.position.set(500, 0, 0); //x轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})
  • y轴方向观察
// 通过UI按钮改变相机观察角度
document.getElementById('y').addEventListener('click', function () {
    camera.position.set(0, 500, 0); //y轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})
  • z轴方向观察
// 通过UI按钮改变相机观察角度
document.getElementById('z').addEventListener('click', function () {
    camera.position.set(0, 0, 500); //z轴方向观察
    camera.lookAt(0, 0, 0); //重新计算相机视线方向
})
  1. lookAt()的作用
    改变.position属性后,如果不执行.lookAt()方法,相机的观察方向默认不变
    如果你希望相机圆周运动的同时,改变相机视线方向,保持相机镜头始终指向坐标原点或其它位置,需要每次改变.position属性后,重新执行一遍.lookAt()方法
function render() {
    angle += 0.01;
    camera.position.x = R * Math.cos(angle);
    camera.position.z = R * Math.sin(angle);
    // .position改变,重新执行lookAt(0,0,0)计算相机视线方向
    camera.lookAt(0,0,0);
    requestAnimationFrame(render);
}
render();
  1. 镜头的基本运动
    模拟 贯穿 整个街道的镜头效果
    模拟 望远镜放大缩小( 最好使用目标与摄像机的距离,作为边界显示 )
// 渲染循环
function render() {
    camera.position.z -= 0.3; //-- 相机逐步向canvas画布内移动(如果目标的z值 比摄像机的z值 小的话)
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();
// 渲染循环
function render() {
    camera.position.z += 0.3; //-- 相机逐步向canvas画布外移动(如果目标的z值 比摄像机的z值 大的话)
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

实际使用

  1. 操控摄像机围绕模型旋转
  • 参考案例链接: https://threejs.org/examples/webgl_interactive_cubes.html
let theta = 0;
init();
animate();
function init(){
  let camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
}
function animate(){
  requestAnimationFrame( animate );
  render();
}
function render(){
  theta += 0.1;
  camera.position.x = radius * Math.sin( THREE.MathUtils.degToRad( theta ) );
  camera.position.y = radius * Math.sin( THREE.MathUtils.degToRad( theta ) );
  camera.position.z = radius * Math.cos( THREE.MathUtils.degToRad( theta ) );
  camera.lookAt( scene.position );
  camera.updateMatrixWorld();
}
  1. 将移动镜头封装为一个函数,并且增加缓动效果
  • 参考案例链接: https://dragonir.github.io/3d/#/shadow - 光、模型与摄像机
import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';
...
function animateCamera(position, rotation) {
  //相机 - 位置动画
  new TWEEN.Tween(camera.position)
    .to(position, 1800)
    .easing(TWEEN.Easing.Quadratic.InOut)
    .start()
    .onComplete(function () {
      TWEEN.remove(this)
    })
  //相机 - 旋转动画
  new TWEEN.Tween(camera.rotation)
    .to(rotation, 1800)
    .easing(TWEEN.Easing.Quadratic.InOut)
    .start()
    .onComplete(function () {
      TWEEN.remove(this)
    })
}

// 点击第一Tab菜单
document.getElementById('one').addEventListener('click', () => {
  document.getElementById('one').classList.add('active');
  document.getElementById('two').classList.remove('active')
  document.getElementById('content').innerHTML = 一段文字'
  animateCamera({ x: 3.2, y: 2.8, z: 3.2 }, { y: 1 });
});
  1. 摄像机沿着指定轨迹漫游
    示例地址 - http://www.webgl3d.cn/pages/188907/

  2. 操作摄像机与模型互动,如 开门进入车内动画

  • 操作摄像机移动到指定位置
    示例地址 - https://juejin.cn/post/7028780379649605646
    代码片段
  1. 当小人模型移动到某个指定区域范围时候,修改摄像机的位置以及朝向
//-- 检测修改摄像机位置函数
function checkChangeCameraFn(roleObject){
    //-- 当前摄像机的位置
    var camPos = new THREE.Vector3(
      camera.position.x,
      camera.position.y,
      camera.position.z
    );

    //-- 目标摄像机的位置
    var targetPos;
    
    //-- 角色进入指定范围
    if ( rolePosition.position.x > -3 && rolePosition.position.x < 22 && rolePosition.position.z > 31 && roleObject.position.z < 58 ) {
      targetPos = new THREE.Vector3(
        roleObject.position.x,
        roleObject.position.y + 50,
        roleObject.position.z + 40
      );
    }else{
        //-- 角色不在指定范围内,摄像机位置回到之前与角色关系位置
        targetPos = new THREE.Vector3(
          roleObject.position.x,
          roleObject.position.y + 30,
          roleObject.position.z + 60
        );
    }
    
    //-- 通过Vector3的lerp方法,实现当前位置与目标位置的缓动动画
    camPos.lerp(targetPos, 0.033);
    //-- 修改摄像机的位置以及朝向
    camera.position.copy(camPos);
    camera.lookAt(ballPosition.position);
}
animate();
function animate(){
    requestAnimationFrame( animate );
    ...
    //-- 摄像机位置朝向改变
    checkChangeCameraFn(小人模型对象);
    renderer.render( scene, camera );
}
  1. 模型沿着指定轨迹行驶,渲染时候切换三个摄像机镜头 -- (既有轨迹运动也有镜头切换,推荐!)
  • 添加若干相机,如全局相机,模型相机,被模型盯住模型上的相机,切换相机来显示
  • 沿着指定轨迹行驶
    原贴地址 - https://juejin.cn/post/7061203887536996360
    效果图

OrbitControls轨道控制器 与 摄像机的关系

  • 说明:
    OrbitControls轨道控制器,是一个threejs的非核心库,无需安装,只要显示引入js文件即可

执行构造函数THREE.OrbitControls()浏览器会同时干两件事,
给浏览器定义了一个鼠标、键盘事件,自动检测鼠标键盘的变化,如果变化了就会自动更新相机的数据, 执行该构造函数同时会返回一个对象
给该对象添加一个监听事件,只要鼠标或键盘发生了变化,就会触发渲染函数

作者:3D建模学习
链接:https://juejin.cn/post/7221777883160510525
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 轨道控制器与摄像机的关系
    ※ OrbitControls.target属性对应目标 等同于 camera.lookAt()的观察目标
    ※ OrbitControls.update(); 等同于 相机控件内部会执行 camera.lookAt(controls.target);
    ※ 对于透视投影相机而言,OrbitControls缩放,本质上就是改变camera.position
轨道控制器与摄像机的关系
轨道控制器与摄像机的关系2
  • 轨道控制器常用属性和方法
controls.enablePan = false; //禁止右键拖拽
controls.enableRotate = false; //禁止旋转
controls.enableZoom = false;//禁止缩放
controls.minDistance = 200; //相机位置与观察目标点最小值
controls.maxDistance = 500; //相机位置与观察目标点最大值
controls.getDistance(); //计算出camera.position和controls.target的距离
  • 限制摄像机的旋转范围
    默认上下旋转范围:
// 上下旋转范围
controls.minPolarAngle = 0; //-- 默认值0
controls.maxPolarAngle = Math.PI; //-- 默认值Math.PI

限制不能看到模型底部

controls.maxPolarAngle = Math.PI/2;

限制左右范围 - 如: 前方180度内

controls.minAzimuthAngle = -Math.PI/2;
controls.maxAzimuthAngle = Math.PI/2;
  • 借助轨道控制器查看摄像机的位置变化
orbitControl.addEventListener( 'change', function () {

    //相机位置与目标观察点距离
    // const pos = camera.position;
    // console.log( 'pos', pos );
    // const dis = orbitControl.getDistance();
    // console.log( 'dis', dis );

} );

未完待续.....

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 概念性的知识 threejs是一个让用户通过javascript入手进入搭建WebGL项目的类库; WebGL 使...
    squidbrother阅读 1,085评论 0 2
  • 学习目标 熟悉并掌握Three.js中的基本概念,主要包括场景,摄像机,灯光,渲染器,物体对象,几何体,材质,动画...
    田苗苗_7785阅读 1,770评论 2 8
  • ThreeJS 里元素如下: 1.场景(Scene):是物体、光源等元素的容器, 2.相机(Camera):控制...
    _花阅读 963评论 0 1
  • 本系列所有文章目录[https://www.jianshu.com/p/8fa8eb8e0cdc] 下面是本文例子...
    handyTOOL阅读 2,612评论 0 1
  • 1. 开始 1.1 开使用threejs写一个最简洁的demo 1.2 threejs常用对象或对象属性 1.2...
    littlesunn阅读 1,945评论 2 9