参考
Unity3D Camera设置结合OpenGL详细解读
Unity 对Camera 属性Clear Flags 的SkyBox/Solid Color/Depth Only深度解析
一、Unity3D Camera属性
1.Clear Flags
每帧绘制完后,都需要清空各种缓冲区,最主要的是color和depth,一个负责画面最终呈现的颜色,一个负责景深。重要的是,在当前Camera范围内color和depth会有被覆盖的可能性。在范围外的color和depth有被其它Camera使用的可能性。这里的清除很多时候是为了处理,在当前Camera范围外的区域。
- Skybox, 可以理解为Camera范围外的区域,使用SkyBox来填充。
- Solid color,可以理解为Camera范围外的区域,使用一个纯色来填充。
- Depth only,可以理解为Camera范围外的区域,只清空depth,而不清除color。这就保留了其它Camera范围绘制的画面,但丢失了所有景深信息。这就是为什么,当一个Camera选择Depth only的时候,其它Camera的渲染画面可以保留。没有了景深,多个Camera之间的渲染层级就依赖Camera的Depth来设置。
- Don’t clear, 就是不清除color和depth,每次绘制都会叠加在一起。
2.Background:
就是用来填充,当前Camera范围内没有被绘制的区域。
3.Culling Mask:
没被选中的Layer会被当前Camera给剔除,不会渲染。过滤非选中层的方法有很多,可能都不会调用渲染函数,没必要使用OpenGL的剪裁功能。
4.Projection:
正交ortho和透视perspective,以及剪裁平面,就是生成一个矩阵数据。
参考正交相机与透视相机的区别
正交相机,三只羊一样大。
透视相机,三只羊大小不一样,缩小视距可以减轻这种影响。
5.Viewport Rect:
直接对应了,OpenGL的 glViewport 调用。
6.Depth:
当有多个Camera的时候,每个Camera有自己的绘制空间区域和屏幕视口。所包含的物体会通过Camera的范围渲染出来。多个Camera的层级,决定了所包含物体的渲染顺序。这个数字从小到大,越来越靠近屏幕。
7.Rendering Path:
通用的几个shader选择,主要处理光照的。
8.Target Texture:
可以把Camera渲染的画面,绘制到一个纹理上,而不是设备的显示缓冲区里。
二、Unity3D Clear Flags详解
1.内部环境裁剪(Clipped inside the environment)
这个说的是Camera的另一个属性Clipping Planes,这个属性包含两个值Near/Far。举个简单的例子,比如人眼睛,距离人眼睛太近的物体看不清、距离人眼睛太远的物体看不到,这就是Near、Far了。看下图
你看,这个观赏树的近端可以看完整,远景也OK。这个时候Clipping Planes Near == 0.3 Far == 1000
再看,近端丢了一块,远景也丢了。这个时候Near == 3 Far == 6
也就是说距Camera小于Near距离的物体被裁剪掉、距Camera大于Far距离的物体被裁剪掉。
那么,怎么能避免物体被裁剪掉,而且还能离摄像头Near>=3呢?那就是,把这个景观树单独用另一个摄像头来显示。具体,看下一个知识点。
2.Depth
这个深度,顾名思义,就是摄像机的深度了。当只有一个摄像机时,这个属性没有意义。当有大于两个摄像机时,就牵扯到多个摄像机的前后层叠问题了。
Depth值越大,越靠上,也就是越靠外,也就是可以遮挡值较小的摄像机的画面。比如MainCamera就默认为-1,然后新建摄像机就是0,在上边。
那么回到刚才景观树这个问题,如果想用一个摄像机专门展示景观树,而且还在主摄像机中可以看到,那么Depth必然要大一些,在主摄像机的上面喽。接下来,我们就来讨论Clear Flags 的Depth Only啦:
我们先试着把显示景观树的摄像机的Clear Flags设置为SkyBox或者Solid Color,我们会发现主摄像机镜头被天空盒或者纯色挡住了。然后将ClearFlags改为Depth only,我们会发现,景观树成功的嵌入了主摄像机的其他景色中,浑然天成。根据Depth only字面意思来讨论,也就是说,这个时候摄像机的未渲染部分的显示内容取决于深度,未渲染部分显示什么由深度小于本摄像机的内容来决定。
3.Depth only应用实例
现在回到枪的话题。我做了一个小例子,如图就是场景,其中有三个Enemy,一个Player(蓝色),一个Gun(绿色):
然后先是只有一个Camera,我们看看效果:
看,由于枪离人物太近,导致从人物的视角观察时,最近的一部分被裁剪掉了。那怎么办?我总不能把枪再朝外吧?再朝外就不自然了呢。
那么我把枪的Layer设置成GunLayer,然后在MainCamera中CullingMask中取消对GunLayer的显示。然后新建Camera,把深度设置为0(因为MainCamera的深度为-1),Clear Flags设置成Depth only,把CullingMask中只勾选GunLayer,那么这个新的Camera就只显示Gun了。调整Camera的位置到适合位置。看效果:
人物举枪的姿势,很自然吧?
接下来,我将专门解析这段话的含义,觉得上面的翻译有些不好,我自己尝试翻译了下
This will keep the graphical display of the environment on the screen, but discard all information about where each object exists in 3-D space. When the gun is drawn, the opaque parts will completely cover anything drawn, regardless of how close the gun is to the wall.(设置深度的这个操作)将会保持环境中的图形显示的完整,但是,会丢弃所有其他物体在3D空间中存放位置的信息。当枪被绘制时,它的透明部分会完整的覆盖其他任何绘制,不管这把枪到底离墙有多近。
那么我理解的意思就是设置成Depth only的Camera中的物体,将会凌驾在其他任何物体上,当然,只能凌驾在Depth比他小的物体上。不受3D位置的前后影响。能够完整的呈现在观察者面前。
我做实验如下:
我先将一个Enemy顶进了枪里:
再来观察效果:
枪还在外面,没有顶进去。很明显,其他物体的3D位置信息没有体现出来,不然就不是这个效果了。
至此,这个Depth Only,的含义已经很传神了。
三、cocos3.0 camera
参考
cocos3.0官方文档 camera
cocos3.0官方文档 渲染排序说明
1.相机分组渲染
Visibility 属性用于设置哪些层级(Layer)的节点应该被相机观察到,可同时选择多个 Layer。注意:从 Cocos Creator 3.0 开始,2D 元素(例如 Sprite)的渲染也遵从 Layer 与 Visibility 的判断,开发者可以根据需要自行调整 Layer 与 Visibility。
当开发者在 Visibility 属性中勾选了多个 Layer 时,Visibility 属性值便是通过将多个 Layer 的属性值执行 | 操作计算得出。
2.Clear Flags
- DONT_CLEAR:不清空;
- DEPTH_ONLY:只清空深度;
- SOLID_COLOR:清空颜色、深度与模板缓冲;
- SKYBOX:启用天空盒,只清空深度
排序是一个很简单的功能,但是最终的呈现却是根据不同平台提供的渲染能力来的。因此,在这里说明一下,如果遇到了 UI 渲染出错,花屏,闪屏等现象,首先要检查的就是场景里所有相机(Camera 和 Canvas)的 ClearFlag,确保场景里必须有一个相机要执行 Solid_Color 清屏操作。
具体如何设置 ClearFlag,可参考以下几种情况:
如果场景中只有一个 UI Canvas 或者 3D Camera,那么 ClearFlag 属性设置为 Solid_Color。
如果场景中包含 UI 背景层、3D 场景层、 UI 操作层,则:
- 2D 背景层:ClearFlag 属性设置为 Solid_Color。
- 3D 场景层:ClearFlag 属性设置为 Depth_Only。
- 2D UI 层:若有模型,ClearFlag 属性设置为 Depth_Only 以避免出现模型闪屏或者穿透的情况。若没有模型,ClearFlag 属性可设置为 Dont_Clear 或 Depth_Only。
3.canvas自带的摄像机属性
如图,设计分辨率1080*2000时,默认canvas的camera属性。其中orthoHeight是高度2000的一半。如果设计分辨率是1080*1920,则orthoHeight就是960了。
上图摄像机盒子的厚度是由far来控制的,默认摄像机位置Z是1000,默认Far是2000。如果将far改为1000,则刚好看到canvas的内容。
默认的正交摄像机,改fov没影响。在透视摄像机中,改Fov和摄像机的坐标以及旋转角度,就能得到合适的画面。
3.测试实例
新建一个Scene,以下操作全部使用默认配置:
- 添加一个胶囊,默认LAYER是DEFAULT
- scene中默认的相机ClearFlag使用SOLID_COLOR,visibility设置为IGNORE_RAYCAST|UI_3D|DEFAULT,注意为透视投影
- 添加一个Button,会自动生成CANVAS父节点和相应的CAMERA,CANVAS和BUTTON的LAYER全是UI_2D
- CANVAS下的CAMERA,其visibility设置为UI_3D|UI_2D,ClearFlag使用DEPTH_ONLY,注意为正交投影
- Priority 相机的渲染优先级,值越小越优先渲染。所以这里CANVAS下的CAMERA的Priority值非常大(1073741824),而SCENE中的CAMERA的Priority为0(简单记忆为,值大的盖在上面)
分析:
BUTTON以及CANVAS的LAYER全是UI_2D,其相应的CAMERA,设置的VISIBILITY确保只看到这些,然后使用的DEPTH_ONLY会叠加到场景中的主摄像机上。
场景里所有相机(Camera 和 Canvas)的 ClearFlag,确保 场景里必须有一个相机要执行 Solid_Color 清屏操作。
根据官方文档所说,场景中的Camera 设置为Solid_Color即可。
4.【Cocos Creator3.0教程】小地图的实现方式
- TargetTexture 指定相机的渲染输出目标贴图,默认为空,直接渲染到屏幕,本例将副摄像机看到的内容动态输出到一个sprite中
- ClearColor 指定清空颜色,本例中将颜色值和透明度全部清0
//MiniMap.ts:
import { _decorator, Component, Node, Camera, Sprite, RenderTexture, UITransform, SpriteFrame } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('Minimap')
export class Minimap extends Component {
@property(Camera)
private minimapCamera: Camera = null!;
@property(Sprite)
private contentSprite: Sprite = null!;
start() {
//新建一个和当前contentSprite大小一样的rendertexture
const size = this.contentSprite.getComponent(UITransform)!.contentSize;
const renderTexture = new RenderTexture();
renderTexture.reset(size);
//把摄像机看到的内容输出到这个rendertexture
this.minimapCamera.targetTexture = renderTexture;
//把这个rendertexture包装到SpriteFrame中,然后呈现给当前contentSprite
const spriteFrame = new SpriteFrame();
spriteFrame.texture = renderTexture;
this.contentSprite.spriteFrame = spriteFrame;
}
}
5.near
如上图,当把模型的scale调大后,会出现面部贴图糊掉。此时改一下camera的near属性即可。
6.固定角度的3D场景适配
参考
各位大佬,3D模型怎么做适配呀
急!creator3d的适配问题
Creator 3D Camera按宽度适配的解决方案
import { _decorator, Component, Node, CameraComponent, view, SystemEventType, systemEvent } from "cc";
const { ccclass, property, requireComponent } = _decorator;
@ccclass
@requireComponent(CameraComponent)
export class FitWidthCamerats extends Component {
private _camera!: CameraComponent;
private _defaultTanHalfFov!: number;
onLoad() {
this._camera = this.getComponent(CameraComponent)!;
this._defaultTanHalfFov = Math.tan(this._camera.fov * 0.5 / 180 * Math.PI);
this.updateFov();
window.addEventListener('resize', this.updateFov);
}
updateFov = () => {
console.log(view.getVisibleSize().height, view.getDesignResolutionSize().height)
let tanHalfFov2 = view.getVisibleSize().height / view.getDesignResolutionSize().height * this._defaultTanHalfFov;
this._camera.fov = Math.atan(tanHalfFov2) / Math.PI * 180 * 2;
}
onDestroy() {
window.removeEventListener('resize', this.updateFov);
}
}
这里要注意,增加fov会导致屏幕宽高的可视区域都会加大。在特别瘦长的手机屏幕上,为了保证屏幕宽度范围内看到所有模型,就会导致额外看到屏幕顶部和底部更多内容。