原文:http://www.jianshu.com/p/96fd1fbe8409

投影空间和屏幕空间

先不说UI,如果是一个普通渲染流程,一个Cube普通地被渲染。

800X600的屏幕中,右边的Cube和中间的Image大小相仿。(可能由于网页显示问题导致本图显示尺寸变小。请自行脑补成其真实800x600大小,起码比下图248X186大好多)

248X186的屏幕中,发现正方体已变小,但Image却没变

如果屏幕在现实中变小了,这个Cube在现实中也会变小。
具体地说,屏幕变小时,Cube在投影空间中是恒定大小的。但透视空间转换到屏幕空间时,一般都是使用默认的GPU流程:直接把投影空间投影面的四个角直接映射到屏幕空间的四个角。因为屏幕变小了,屏幕空间就变小了,所以这个Cube也就变小了。

总之,屏幕变小不影响投影空间,但会让屏幕空间变小。

Canvas

注,文章以下部分如不特殊注明,都认为Canvas.RenderMode是ScreenSpace,而并非WorldSpace。

Canvas Scaler

Canvas Scaler的各种模式最终都是设置了Canvas.scaleFactor(还有Canvas.referencePixelsPerUnit,但并非本文重点)。
Canvas.scaleFactor会影响Canvas的RectTransform的ScaleXYZ。

Canvas.RenderMode:ScreenSpace - Overlay

当是ScreenOverlay的情况,因为根本没有经过投影空间,而是直接在屏幕空间绘制,所以可以更加简单地讨论问题。

因为在屏幕空间的坐标直接绘制像素,所以Canvas的RectTransform的width*scaleX必须等于屏幕的宽、RectTransform的height*scaleY必须等于屏幕的高。
其中,scaleXY事实上是可以通过Canvas.scaleFactor给脚本设置的,也就是说:

Canvas的核心功能是,自动根据屏幕设备的分辨率、当前的scaleXY,算出正确的width和height,确保和屏幕匹配。

Canvas的核心功能是自动根据屏幕设备的分辨率、当前的scaleXY,算出正确的width和height,确保和屏幕匹配。

注,Overlay情况下,Scene View是世界空间的、Canvas是屏幕空间的,两者处于不同空间,它们的大小比较也就没有可比性了。但也总得把Canvas在SceneView显示出来对吧,所以Unity这里的Editor实现是,屏幕空间1像素等于世界空间1米地把Canvas在SceneView显示出来。

CanvasScaler.ScaleMode: ConstantPixelSize

以图中Scale Factor=1为例,ConstantPixelSize的作用是直接设置scaleXYZ为1。所以,Canvas的width和height总是等于屏幕大小(244x183)。
所以当设备分辨率变小时,scaleXYZ总是1,UI元素的大小并不改变,但Canvas的width和height变小了,所以UI元素占比Canvas的比例也随之变大。结果:屏幕分辨率变小,UI元素在设备的实际像素数不变,也就大小不变。

CanvasScaler.ScaleMode: ScaleWithScreenSize

ScreenMatchMode.MatchWidthOrHeight

以图中Canvas的Reference Resolution等于800X600比例为4:3、屏幕恰好是同比例4:3的244X183为例,
scaleXYZ总是等于屏幕分辨率/参考分辨率=0.305,所以Canvas的width和height总是800X600。
所以当屏幕分辨率变小于800X600时,Canvas的width/height不变。Canvas的scaleXYZ变小,Canvas本身和UI元素的全局scaleXYZ都变小,但UI元素占比Canvas的比例不变。结果:屏幕分辨率变小,UI元素在设备的比例不变,所以UI的实际像素数变小。
事实上,CanvasScaler是ScaleWithScreenSize的作用就是:

根据参考分辨率和当前设备分辨率,得出一个“合适”的scaleFactor。至于设备分辨率和参考分辨率比例不同,这个问题交由Canvas自动设置width/height来解决。

当设备分辨率比参考分辨率大时,scaleFactor需要“合适”的大,当设备分辨率比参考分辨率小时,scaleFactor需要“合适”地小。
这个“合适”,在内部实现的代码是:

float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);

ScreenMatchMode.Expand

scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

scaleFactor尽可能小,可知,结果是尽量把UI在设备屏幕给放大

ScreenMatchMode.Shrink

scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

scaleFactor尽可能大,可知,结果是尽量把UI在设备屏幕给缩小

CanvasScaler.ScaleMode: ConstantPhysicalSize

根据屏幕的PPI信息和ConstantPhysicalSize本身的配置信息,得出一个“合适”的scaleFactor,以达到UI在不同PPI设备上的最终大小都是一致的。

Canvas.RenderMode:ScreenSpace - Camera

由于使用了Camera,所以此时的Canvas和Scene View里的任意GameObject一样,都是处于世界空间了。
所以对于ScreenSpace - Camera的Canvas来说,关键是如何把处于世界空间中的Canvas能够占满投影空间的投影平面。

然而,由于在世界坐标系中任意GameObject的默认scaleXYZ都是1,Canvas也不例外。所以,需要根据Camera的设置、Canvas的plane distance设置,算出scaleFactorCamera出来。

正交(Orthographic)Camera

正交Camera的可调参数是Size。Size的定义为视体的投影平面高的1/2。 比如Camera的Size是300,即投影平面高为600。投影平面的宽根据平面的高和屏幕比例得出。
所以Canvas必须和这个投影平面一样大小(且位置重合)。此时,有两种方法和投影平面大小一样:

  1. 调整Canvas的width/height
  2. 调整Canvas的ScaleXY

由于第1种方法会导致切换Overlay和Camera的方式时,UI的表现不一致,所以Unity的实现是采取了第2种方法。即:

当Canvas是Screen Space - Overlay时,
Canvas.RectTransform.scaleXYZ = Canvas.scaleFactor
当Canvas是Screen Space - Camera时,
Canvas.RectTransform.scaleXYZ = Canvas.scaleFactor * scaleFactorCamera

所以,在这种情况下,

  • Canvas.height==Screen.height
  • Canvas.height * scaleFactor * scaleFactorCamera = Camera.size * 2,
  • 所以scaleFactorCamera = Camera.size * 2 / Canvas.height / scaleFactor = Camera.size * 2 / Screen.height / scaleFactor

透视(Perspective)Camera

透视Camera的可调参数为fov。同时Canvas的plane distance也认为是参数之一。

  • Canvas.height == Screen.height
  • Canvas.height * scaleFactor * scaleFactorCamera = 2 * Canvas.planeDistance * tan(Camera.fov/2)
  • 所以scaleFactorCamera = 2 * Canvas.planeDistance * tan(Camera.fov/2) / Screen.height / scaleFactor

CanvasScaler

至此,已求出不同Camera模式的scaleFactorCamera。

在ScreenSpace - Camera的情况下,不管CanvasScaler的模式再怎么变,都是在上面Overlay的情况下的CanvasScaler的讨论基础上,再乘多一个scaleFactorCamera即可。

WorldSpace

Canvas的width和height、scaleXYZ都可以任意设置。Canvas不再自动根据屏幕设备的分辨率、当前的scaleXY,算出正确的width和height。
此时的CanvasScaler在此模式下几乎形同虚设。只是拿来设置Canvas.referencePixelsPerUnit

UGUI中的Canvas、Camera和Canvas Scaler相关推荐

  1. Unity快速入门之一 3D基础概念、Camera、Canvas RenderMode的几种方式对比

    最近要给公司的小伙伴做Unity入门,针对几个常用的知识进行快速入门介绍. Unity快速入门之一 3D基础概念.Camera.Canvas RenderMode的几种方式对比_翕翕堂 Unity快速 ...

  2. canvas笔记-在canvas中使用其他HTML元素

    这里使用的原理是使用css把div中关于html的元素通过布局位置,以及透明相关的设置,将其设置到canvas上,这样看起来就是在canvas绘制的按钮.然后使用JavaScript获取按钮的数据或者 ...

  3. openlayers中使用rBush(R树)来存放要素等信息,本文修改了一点其中的rbush源码中的demo,使用canvas画出了insert和delete操作(建立树和删除树中数据)

    openlayers中使用rBush(R树)来存放要素等信息,本文修改了一点其中的rbush源码中的demo,使用canvas画出了insert和delete操作(建立树和删除树中数据) 修改后的源代 ...

  4. html5中移动旋转缩放,HTML5 Canvas进阶(一):渐变,透明,移动,旋转,缩放

    前一篇博文介绍了在标签中绘制图形的一些基本方法,本文将进一步介绍如何在标签中对图形图像进行操纵. 渐变 Context对象可以通过createLinearGradient()和createRadial ...

  5. android 自定义paint,Android自定义View中Paint、Rect、Canvas介绍(一)

    自定义View对于新手而言貌似是一个很复杂的东西.格式,各函数的意义.对于大神经常忘记各函数及一些参数的具体写法及意义,刚好在做一个风车效果,把过程及遇到的问题都写下来 1.如何自定义一个View p ...

  6. canvas.width和canvas.style.width区别以及应用

    今天讲的内容是canvas.width和canvas.style.width的区别,在没有做canvas项目之前,其实我是并没有深入了解过这两个属性的,最近在研究canvas项目的自适应问题,尤其是在 ...

  7. Linux环境安装canvas,npm install canvas简明指南

    安装环境:Mac OSX Mavericks 要使用Node.js的canvas库,直接通过npm命令安装,通常,会得到一个编译错误: $ npm install canvas ... > no ...

  8. android canvas bitmap matrix,Canvas API详解(Part 3)Matrix和drawBitmapMash

    本节引言:在Canvas的API文档中,我们看到这样一个方法:drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) 这个Matrix可是有大文章的 ...

  9. 初识canvas,使用canvas做一个百分比加载进度的动画

    canvas作为H5中重要的新增特性,使开发者可以用它来创作各种令人惊叹的作品.先来看一下浏览器对canvas的支持情况. <canvas> 标签定义图形,比如图表和其他图像,我们使用脚本 ...

最新文章

  1. 三公子论「财务自由」
  2. python设置画布背景图_如何使画布中的背景图像随窗口展开?
  3. 用神经网络做分子模型:乙烯和乙炔的实验数据
  4. 学习笔记Hadoop(一)—— Hadoop介绍(1)——认识大数据
  5. mongo的php查询,如何在php中查询mongo?
  6. OpenCV学习(二十二) :反向投影:calcBackProject(),mixChannels()
  7. Asp.net MVC4 Knockoutjs BootStrap Ace NinJect Jqgrid sqlserver2008
  8. 滴滴等8家网约车平台将增设“一键叫车”功能 便利老年人打车
  9. 82c55单片机c语言,AT89S51单片机与82C55的接口设计编程
  10. 1.3 更多边缘检测内容
  11. hdu 1561(树形DP+背包)
  12. 阅读之大量数据访问机器的架构优化
  13. win下MySQL 8.0.11 修改密码、开启远程访问
  14. 小米8刷官方欧版rom并从国内版rom提取安装MiPay、门卡模拟
  15. 几款经典的免费小软件 -- 白领的左右手
  16. 整车电子电气架构EEA
  17. OpenWrt ADSL单线多拨,负载均衡(仅供参考)
  18. hdu4556(欧拉函数)
  19. 计算机efs加密,我的电脑文件efs加密了,现在从做系统打不开了怎么处理啊
  20. 试着在unity实现阴阳师抽卡效果

热门文章

  1. Python多线程编程(详细:适合小白入门)
  2. vue实现自定义搜索功能
  3. java软件安装教程详细_java编程软件安装教程
  4. 《随机漫步的傻瓜》---寻找你的投资原则,查理芒格说:当成功概率很高的时刻,下最大的赌注,其余时间按兵不动...
  5. 知名商业智能BI厂商盘点
  6. 在 Raspberry Pi (树莓派) 上安装 CMake
  7. Redis 入门看这篇就够了
  8. Linux 开发环境搭建与使用——Linux 常用编辑器之vim
  9. 头歌实践教学平台:Java入门 - Java初体验
  10. 关于Find My iPhone的三个故事