这篇文章只讨论 PerspectiveCamera 的适配方法

做过手机 H5 的同学可能会觉得屏幕适配挺麻烦。原因是设计师提供的设计稿尺寸比固定,但是前端开发者却要适配不同大小、长宽比的目标设备。适配的终极目标无非是最大程度把主体内容优雅地呈现给用户。开发和设计如果没有协调好的话可能会妥协比较丑陋的方案,例如由于设计比例问题,为了照顾主体内容不被裁剪,只好设备两边,或者上下留黑边这种。

不过在 3D 的世界里,我们不用担心会有黑边的问题,因为 3D 场景是无限延伸的,总能填满任何比例的屏幕。

先看看 PerspectiveCamera 官方 API 说明如下:

PerspectiveCamera( fov, aspect, near, far )fov — Camera frustum vertical field of view.
aspect — Camera frustum aspect ratio.
near — Camera frustum near plane. far — Camera frustum far plane.

上面四个参数都会影响成像结果,fov 和 aspect 设置 XY 平面的范围,也就是广度。 near 和 far 影响的是纵深 Z 轴的范围,也就是深度。纵深只要保证物体离相机距离在这个范围就可以了,这是为了性能而设置的参数,由用户设置,只渲染必要的东西。实际上真实的相机这两个值对应的是 0 到 无限远。

这些参数设置好之后,成像就相应确定了。最后 three.js 把相机拍摄到的矩形区域对应好四个顶点渲染到屏幕上。同样比例的屏幕看到的图像是一致的,与屏幕大小无关</span>。

下面我用一个简单的场景来看一下这些参数对成像的影响。

场景元素

  • 相机 (PerspectiveCamera)

  • 一个边长为 100 的平面(主体内容范围),放在世界坐标中心。

var camera = new THREE.PerspectiveCamera(53, 500 / 500, 0.1, 1000); var planeGemo = new THREE.PlaneGeometry( 100, 100, 10, 10 ) var meshMaterial = new THREE.MeshLambertMaterial(); meshMaterial.color = new THREE.Color(0x2dcaf1); meshMaterial.side = THREE.DoubleSide; var wireFrameMat = new THREE.MeshBasicMaterial(); wireFrameMat.color = new THREE.Color(0xdddddd); wireFrameMat.wireframe = true; var plane = THREE.SceneUtils.createMultiMaterialObject(planeGemo, [meshMaterial, wireFrameMat]); scene.add(plane);

目标

在任何屏幕下,都能最大程度地显示完整的立方体。最大程度,就是最少多余空间的意思。下面是要达到效果

设置 fov 参数

可以直接想到的一种适配方法是——改变 camera 到目标物体的距离以控制成像的内容,但是这样做计算成本比较高,而且还有可能影响其他一些数值,然后需要相应一起计算修改。
我想到改变视角也可以达到控制成像内容多少的目的,于是我想可不可以只通过改变 fov 一个数值,达到我要的效果。

fov 官网的定义翻译过来是垂直方向的视角大小。我们先规定好相机到平面的距离为 100,然后试试看能不能通过计算设置 fov 值,刚好让平面填满一个宽高比为 1:1 的屏幕。

plane.position.set(0,0,0);
camera.position.set(0,0,100); camera.lookAt(new THREE.Vector3);

观察上面的图,可以很容易求出 fov 的值, fov = arctan((100/2)/100) * 2; fov 为 0.9272952180016122,约等于 53 度。

camera.fov = Math.atan((100/2)/100) * 2 * (180 / Math.PI); camera.updateProjectionMatrix();

设置完刚刚求出的 fov 值,将场景渲染到 宽高比为 1:1 的画布上。

渲染结果和预想的一样,平面刚好填满了 1:1 的画布。

fov 和宽高比例的关系

下面在固定的 fov 下,使用 dat.gui 工具调整宽高比,观察渲染区域的变化。

因为fov设置的是垂直方向的视角范围,可以看到无论我们怎么改变宽高比例,垂直方向的渲染范围,都是一致的。水平方向则是以裁剪的方式显示。也就是说当我们设置好视角让垂直方向范围刚好等于主体内容的范围,只要宽高比大于1,我们得到的渲染结果,已经是最佳的了。问题就只剩下当宽高比小于1的情况了。

宽高比小于1的时候,垂直方向显示的高度刚好是等于主体内容的高度。为了能让水平方向完整显示主体内容,我们只有将垂直方向范围增大,也就是将 fov 设置一个更大的值,此时水平方向的范围也会随之增大。当将 fov调整到 水平方向刚好能显示主体内容时,垂直方向此时显示的范围是超过主体内容垂直方向的范围的。其中的关系,其实可以用很简单的函数求出来。

已知 照相机到主题内容的距离为 d
正方形主体内容的边长为 w

设宽高比为 r,求照相机垂直方向的视角 f

当 r >= 1 时,照相机拍摄到的垂直方向范围等于 w

当 r > 1
d * tan(f/2) * 2 = w

当 r < 1 时,照相机拍摄到的水平方向范围等于 w,垂直方向范围应该是 w/r
d * tan(f/2) * 2 = w/r

这样,任意宽高比例的屏幕应该对应多大的垂直视角就确定了。

最终代码与效果

var controls = new function () { camera.position.z = CAMERA_TO_MAIN_DIS; this.width = 500; this.height = 500; this.planeRY = 0; /** * 计算相机 fov 的函数 * @param d : 在相机前方 d 距离 * @param w : 想要看到最大正方形区域边长为 w * @param r : 屏幕宽高比 */ function calcFov(d, w, r) { var f; var vertical = w; if (r < 1) { vertical = vertical/r; } f = Math.atan(vertical/d/2)*2 * (180 / Math.PI); return f; } this.redraw = ()=>{ webGLRenderer.setSize(this.width, this.height); plane.rotation.y = this.planeRY; camera.fov = calcFov(CAMERA_TO_MAIN_DIS, MAIN_CONTENT_WIDTH, this.width / this.height); camera.aspect = this.width / this.height; camera.updateProjectionMatrix(); } }

效果:

demo 的完整代码:http://codepen.io/JasonTurbo/pen/ZLwJMo点击预览
原文链接:https://segmentfault.com/a/1190000008796468

转载于:https://www.cnblogs.com/ningxiaofang/p/8554785.html

简单一招搞定 three.js 屏幕适配相关推荐

  1. 简单一招搞定公司牛人 转自 潘文富

    简单一招搞定公司牛人 潘文富 所谓公司牛人,就是在老板之下,众员工之上的人物.公司牛人,有的是凭借资历,有的是身居高位的职业经理人,有的是凭借自己在某方面的专业水平和经验,总之,有牛的资本. 这些牛人 ...

  2. iphone5刷android,iphone5s怎么刷机 简单几招搞定iOS【图文教程】

    刷机一词意思就是将手机的操作系统完全重装或更新一次.一般安卓用户对于这个操作是非常熟悉的.但是对于iPhone来说,我们就很少听说刷机了.其实对于iPhone手机来说,刷机的含义也同样是指更新一词系统 ...

  3. 苹果手机小圆点怎么设置?一招搞定!

    案例:苹果手机上的悬浮球怎么设置? [求助!苹果手机小圆点怎么设置出来啊?弄了好久都没搞懂.] 苹果手机小圆点是一个非常实用的功能,它可以帮助您在打字时更快地移动光标,以便更好地编辑文本.但是,很多人 ...

  4. Android-软键盘一招搞定(实践篇)

    前言 软键盘系列文章: Android-软键盘一招搞定(实践篇) Android-软键盘一招搞定(原理篇) 软键盘是Android进行用户交互的重要途径之一,Android应用开发基本无法避免不使用它 ...

  5. android 软件盘未弹出如何获取高度,Android 软键盘的那些坑,一招搞定!

    3 软键盘高度获取 对于上面的问题1,既然想要EditText单独顶上去,那么就需要知道当前键盘弹出的高度,再设置EditText坐标即可. 问题的关键转变为如何获取键盘的高度. Activity窗口 ...

  6. 十招搞定SQL2K安全

    十招搞定SQL2K安全 本文详述提高SQL Server 2K安装的安全性实施的十个注意事项:  1.安装最新的服务包 为了提高服务器安全性,最有效的一个方法就是升级到 SQL Server 2000 ...

  7. 【开源】简单4步搞定QQ登录,无需什么代码功底【无语言界限】

    说17号发超简单的教程就17号,qq核审通过后就封装了这个,现在放出来~~ 这个是我封装的一个开源项目:https://github.com/dunitian/LoTQQLogin --------- ...

  8. 怎么样把计算机桌面的图标改小,怎样将电脑桌面图标变小_三招搞定桌面图标太小问题-系统城...

    电脑安装win10系统后发现桌面的图标太大,想要把这些图标变小,这要怎么操作?由于对操作界面都不熟悉,所以不懂怎么设置,别担忧,小编今天就来分享一种将电脑桌面图标变小的方法,感兴趣的快来试试. 具体方 ...

  9. R语言ineq算基尼系数_科学网—一招搞定泰尔指数及其分解 - 王庆喜的博文

    一招搞定泰尔指数及其分解 上期讲了区位基尼系数,这期讲讲泰尔指数. 泰尔指数的原理同基尼系数,都是在测度一组数据分布的差异性.但是其有着良好的可分解性质,因此当需要考察产业空间集中度在不同区域层面的差 ...

最新文章

  1. 常用的Firefox浏览器插件、Chrome浏览器插件收藏
  2. 机床使用教学_2020沧州cnc培训20年教学经验颁发职业
  3. 使用find命令查找文件
  4. 7649:我家的门牌号
  5. 深度学习-数学-第一篇-标量,向量,矩阵,张量
  6. c++ hsv 红色范围_维生素C的定量测定—2,6二氯酚靛酚法
  7. 解决 ‘Response‘ object has no attribute ‘body‘
  8. LabVIEW的VISA函数串口数据采集例子——温度采集系统
  9. mac Error: EACCES: permission denied, mkdir './cache'
  10. unity 接入谷歌广告
  11. 人工智能能写剧本了 还被拍成了9分钟的短片
  12. 解决out.print()爆红问题
  13. 无法访问 您可能没有权限使用网络资源
  14. Android开发框架大全
  15. Java编程:Integer的取值范围(-2^31~2^31-1)分析
  16. 合肥工业大学宣城校区2018年-2019年第一学期(大三上学期)物联网工程专业资料汇总(含课件、个人实验报告、实验代码、课设报告等)
  17. MySQL查看表结构及查看建表语句
  18. Pico VR 一体机初测
  19. #7.白盒测试:控制流测试
  20. 李一男离开华为时的忠告

热门文章

  1. CTFshow php特性 web94
  2. 制作简易的LED闪烁测试工具
  3. 解决sublime3的标签页乱码(小方块)
  4. 第7周实践项目2.2 求解报数问题
  5. matlab 暂停命令(pause和input)
  6. collections python_python: collections
  7. 推论统计学基础一:Estimation
  8. 【大脑】改善记忆力的食物有哪些
  9. 单元测试工具JUnit
  10. 网络编程 socket模块 tcp协议 udp协议