Cocos 3.x 2D和3D混合的UI UIMeshRenderer

本文参照官方文档,涉及camera可以参考Cocos 3.0 camera

一、2D 对象概述

区别于 3D 模型对象,我们将不涉及模型的图片渲染体统称为 2D 渲染对象。2D 渲染对象的处理在底层的数据提交上与 3D 模型存在差异,其遵循自己的规则做出了一些针对性的调整以实现更好的效率表现和使用体验。

1.RenderRoot2D

2D 渲染对象的收集采用树状结构,RenderRoot 节点(带有 RenderRoot2D 组件的节点)为 2D 对象数据收集的入口节点,所有的 2D渲染对象需在 RenderRoot 节点下才可以被渲染

由于 Canvas 组件本身继承 RenderRoot2D 组件,所以 Canvas 组件也可以作为数据收集的入口。2D 渲染节点必须带有 UITransform 组件作为渲染顶点数据、点击或者对齐策略等功能生效的必要条件。

2D 渲染也可以支持对模型进行渲染,唯一的条件是带有模型组件(例如 MeshRenderer/SkinnedMeshRenderer)的节点必须添加 UI/UIMeshRenderer 组件才可以和 UI 在相同的管线上进行渲染。

引擎中所有不拥有的 model 的渲染对象都为 2D 渲染对象。与 3D 对象不同,2D 对象本身不拥有 model 信息,其顶点信息是由 UITransform 组件的 Rect 信息持有并由引擎创建的,且本身没有厚度。由于引擎的设计要求,2D渲染对象需要为 RenderRoot 节点(带有 RenderRoot2D 组件的节点)的子节点才能完成数据的收集操作。

所以 2D 渲染对象的渲染要求有两点:

  • 需要有 UITransform 组件
  • 需要为 RenderRoot 节点的子节点
2.2D 渲染对象可见性说明

由于 2D 渲染对象在 Camera 的可见性判断上和 3D 渲染节点并无区别,所以用户需要自己控制节点的 layer 属性并设置 Camera 的 Visibility 来配合进行分组渲染,如果场景中出现多个相机的情况,错误的 layer 设置导致节点重复渲染或不渲染。

这里请 3D 1.2 版本升级的用户注意,我们纠正了之前的 Canvas 只会渲染其子节点的行为,目前需要用户自己管理节点的 layer 和相机的 Visibility,之前使用了多 Canvas 渲染的用户可能会需要对项目做出调整以达到更合理的场景结构。

二、渲染排序说明

https://docs.cocos.com/creator/3.0/manual/zh/ui-system/components/engine/priority.html

2D 渲染节点可分为在 Canvas 下的节点和不在 Canvas 下的节点两种,在 Canvas 下的节点可参考下文的 UI 节点排序。 对于不在 Canvas 下的节点,用户可选择通过自定义材质来开启深度检测实现和 3D 物体的遮挡显示,开启后则会按照物体的 Z 轴坐标进行遮挡渲染(可参考范例 2d-rendering-in-3dGitHub | Gitee)。没有开启深度检测的话,则数据提交依旧会按照节点树顺序提交,也就意味着节点树靠下的节点会后渲染。

image.png

OpenGL学习笔记十二(深度测试)
OpenGL ES 学习教程(十二) DEPTH_TEST(深度缓冲测试)

当我们渲染多个物体时,这多个物体之间存在互相遮挡的关系,被遮挡的物体的部分将不可见,也就是它离相机更远,为了告诉计算机被遮挡的物体不需要渲染,我们就需要对物体上的点做深度测试,检测它是否需要渲染。
为了实现上述的检测,就需要深度缓冲,简单而言就是存储物体上点深度值的数组,这个数组一开始值为0,当目前渲染的物体的深度值大于缓冲区中存储的深度值时,就将这个值写入缓冲区,同时通过深度测试,如果与此相反,深度值小于缓冲区中的深度值,就是未通过深度测试,且不写入缓冲区,淘汰掉这个点(不做渲染)。

透明物体的深度写入问题
【关于cocos creator的渲染的提议】

1.UI 节点排序

UI 节点特指在 Canvas 节点下的 UI 节点,这些节点并未开启深度测试,所以节点的混合是严格按照节点树进行排序的。UI 的渲染排序采用的是一个深度优先的排序方式,每一个 UITransform 组件身上都有一个 priority 属性,根据 priority 的值来调整节点顺序。排序从根节点下的子节点开始,根据子节点的优先级来确定整体的渲染结构,也就是根节点下的子节点的排序已经决定了最终的渲染顺序。每一个节点下的所有子节点的 priority 则用来确定在当前节点下的渲染顺序。直接修改了 priority 也会直接改变节点树顺序。

举个例子:


image.png

因此,上图中整体的渲染顺序则是:B -> b1 -> C -> A -> a1 -> a2,在屏幕上的呈现状态为:a2 -> a1 -> A -> C -> b1 -> B。

注意事项

排序是一个很简单的功能,但是最终的呈现却是根据不同平台提供的渲染能力来的。因此,在这里说明一下,如果遇到了 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。
image.png

image.png
2.论坛中的案例

原贴Cocos Creator3.0 在3D游戏场景下添加2D精灵(利用RenderRoot2D),2D精灵的会默认渲染在3D元素上面,类似于UI的渲染层级。

image.png

image.png

注:使用双相机时,注意调节3D物体的SCALE,太小了可能看不到

DepthTest可以参考
cocosCreator3.2.0 Skeleton的深度写入问题

三、UIMeshRenderer

参考
https://docs.cocos.com/creator/3.3/manual/zh/ui-system/components/editor/ui-model.html
Creator3D :太厉害了!3D模型原来可以这样显示在2DUI上
Creator 3.0怎么在2D场景里面跑3D人物?

UIMeshRenderer 是一个将 3D 模型从 3D 渲染管线转换到 2D 渲染管线的带有转换功能的渲染组件。该组件支持 3D 模型和粒子在 UI 上的显示,没有这个组件,即使模型和粒子节点在 UI 里也不会被渲染。

该组件的添加方式是在 层级管理器 中选中带有或继承自 MeshRenderer 组件的节点,然后点击 属性检查器 下方的 添加组件 按钮,选择 UI-> UIMeshRenderer 即可。而粒子则是添加到粒子节点上。通常结构如下所示:


image.png

注意,如果结点是多层结构,则每一层带有或继承自 MeshRenderer 组件的节点,都要添加UIMeshRenderer。如果运行后看不到,需要检查canvas中Camera的visibility设置。

四、3D模型显示在2D示例
1.UIMeshRenderer 方式

https://gitee.com/yeshao2069/CocosCreatorDemos/tree/v3.0.0/2DDemo/UIMeshRendererDemo
绑定了MeshRenderer组件的3d节点必须放在Canvas下,也就是2DUI层

image.png

3d节点的大小,3d节点添加了MeshRenderer组件,并且放置到Canvas下,那么他的大小将不会按照3d节点在摄像机下的大小显示,而是按照3d节点相对Canvas的大小来显示,通常模型的大小需要缩放到在UI层下的实际预览大小


image.png

材质,当大小和层级调整好以后你会发现模型是显示出来了,但是是黑的,,大家的默认effect 应该是builtin-standard吧,只需要将其改为builtin-unlit就可以了

但是,如果是两个material就会有问题,第2个material会失效


image.png

表情糊了

这个时候,如果是在UI上显示3D模型,不使用UIMeshRenderer只改scale,在摄像机可见性和3D模型层级匹配的情况下,其实本来就是能看到的。唯一的问题就是UI节点上的图片会挡住3D模型,此时将这些图片重新一个自定义的mtl即可,注意勾上USE TEXTURE和DepthTest
image.png

背景图片

如果后面有两张背景图片时,发现有一张看不到了,要检查一下是不是把Depth Write也勾上了。

2.RenderTexture方式

https://gitee.com/yeshao2069/CocosCreatorDemos/tree/v3.0.0/2DDemo/RenderTextureDemo

  • 原理就是 把 3D 相机照射的内容绘制到 UI 的精灵帧上
  • 那么主要就是3d摄像机,需要创建一个摄像机,然后将你所需要显示在ui上的3d节点移动到摄像机的视觉范围内
  • 创建Sprite用来摄像机渲染出来的显示
  • 在实现过程中可能会出现,本来只想显示指定的模型,但是会将摄像机的缓冲颜色也显示出来,这里菜鸟是将摄像机的ClearColor的透明度直接调为0,
@ccclass('ReaderTexture')
export class ReaderTexture extends Component {
    @type(Sprite)
    modelSprite!: Sprite;
    @type(Camera)
    camera!: Camera ;
    @type(Node)
    player!: Node;

    private isRotate: boolean = false;


    start() {
        this.isRotate = false;
        this.player.active = false;
        this.refreshRenderTexture();
    }
    btnShowPlayerEvent(): void {
        this.player.active = true;
    }
    btnHidePlayerEvent(): void {
        this.isRotate = false;
        this.player.active = false;
    }
    btnRotatePlayerEvent(): void {
        this.isRotate = true;
    }
    update(deltaTime: number) {
        if (this.isRotate) {
            let eulerAngles: Vec3 = this.player.eulerAngles;
            eulerAngles.y++;
            this.player.eulerAngles = eulerAngles;
        }
    }

    refreshRenderTexture(): void {
        this.isRotate = false;
        this.player.active = false;

        const _colorAttachment = new GFXColorAttachment();
        const _depthStencilAttachment = new GFXDepthStencilAttachment();

        let renderTex = new RenderTexture();
        renderTex.reset({
            width: 350,
            height: 610,
            passInfo: {
                colorAttachments: [_colorAttachment],
                depthStencilAttachment: _depthStencilAttachment,
                subPasses : []
            }
        });

        let spriteframe: SpriteFrame = this.modelSprite.spriteFrame!;
        let sp: SpriteFrame = new SpriteFrame();
        sp.reset({
            originalSize: spriteframe.originalSize,
            rect: spriteframe.rect,
            offset: spriteframe.offset,
            isRotate: spriteframe.rotated,
            borderTop: spriteframe.insetTop,
            borderLeft: spriteframe.insetLeft,
            borderBottom: spriteframe.insetBottom,
            borderRight: spriteframe.insetRight,
        });

        this.camera.targetTexture = renderTex;
        sp.texture = renderTex;
        this.modelSprite.spriteFrame = sp;
    }
}
3.骨骼动画-RenderTexture方式

https://gitee.com/yeshao2069/CocosCreatorDemos/tree/v3.0.0/2DDemo/RenderTextureDemoForSkeletalAnimation

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

推荐阅读更多精彩内容