【Away3D代码解读】(二):渲染核心流程(简介、实体对象收集)

我之前解析过Starling的核心渲染流程,相比Away3D而言Starling真的是足够简单,不过幸运的是两者的渲染流程是大体上相似的;Starling的渲染是每帧调用Starling类中的render方法,类似的Away3D的渲染是每帧调用View3D类中的render方法,那我们要了解Away3D的渲染就需要从这个方法入手了。

View3D的render方法源码:

 1 /**
 2  * Renders the view.
 3  */
 4 public function render():void
 5 {
 6     //if context3D has Disposed by the OS,don‘t render at this frame
 7     if (!stage3DProxy.recoverFromDisposal()) {
 8         _backBufferInvalid = true;
 9         return;
10     }
11
12     // reset or update render settings
13     if (_backBufferInvalid)
14         updateBackBuffer();
15
16     if (_shareContext && _layeredView)
17         stage3DProxy.clearDepthBuffer();
18
19     if (!_parentIsStage) {
20         var globalPos:Point = parent.localToGlobal(_localPos);
21         if (_globalPos.x != globalPos.x || _globalPos.y != globalPos.y) {
22             _globalPos = globalPos;
23             _globalPosDirty = true;
24         }
25     }
26
27     if (_globalPosDirty)
28         updateGlobalPos();
29
30     updateTime();
31
32     updateViewSizeData();
33
34     _entityCollector.clear();
35
36     // collect stuff to render
37     _scene.traversePartitions(_entityCollector);
38
39     // update picking
40     _mouse3DManager.updateCollider(this);
41     _touch3DManager.updateCollider();
42
43     if (_requireDepthRender)
44         renderSceneDepthToTexture(_entityCollector);
45
46     // todo: perform depth prepass after light update and before final render
47     if (_depthPrepass)
48         renderDepthPrepass(_entityCollector);
49
50     _renderer.clearOnRender = !_depthPrepass;
51
52     if (_filter3DRenderer && _stage3DProxy._context3D) {
53         _renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect);
54         _filter3DRenderer.render(_stage3DProxy, camera, _depthRender);
55     } else {
56         _renderer.shareContext = _shareContext;
57         if (_shareContext)
58             _renderer.render(_entityCollector, null, _scissorRect);
59         else
60             _renderer.render(_entityCollector);
61
62     }
63
64     if (!_shareContext) {
65         stage3DProxy.present();
66
67         // fire collected mouse events
68         _mouse3DManager.fireMouseEvents();
69         _touch3DManager.fireTouchEvents();
70     }
71
72     // clean up data for this render
73     _entityCollector.cleanUp();
74
75     // register that a view has been rendered
76     stage3DProxy.bufferClear = false;
77 }

在进入渲染代码的解读之前,我们应该需要大概的解读一下render方法实现的功能;

 1 /**
 2  * 渲染 View3D 对象.
 3  */
 4 public function render():void
 5 {
 6     //判断当前的 context3D 对象是否可以使用, 不能使用则取消本次渲染
 7     if (!stage3DProxy.recoverFromDisposal()) {
 8         _backBufferInvalid = true;
 9         return;
10     }
11
12     //如果 View3D 的尺寸改变则更新后台缓冲区的大小
13     if (_backBufferInvalid)
14         updateBackBuffer();
15
16     //清除深度缓冲
17     if (_shareContext && _layeredView)
18         stage3DProxy.clearDepthBuffer();
19
20     //如果父级不是 stage 对象则需要获取 View3D 对象的舞台坐标
21     if (!_parentIsStage) {
22         var globalPos:Point = parent.localToGlobal(_localPos);
23         if (_globalPos.x != globalPos.x || _globalPos.y != globalPos.y) {
24             _globalPos = globalPos;
25             _globalPosDirty = true;
26         }
27     }
28
29     //更新舞台坐标
30     if (_globalPosDirty)
31         updateGlobalPos();
32
33     //获取当前帧和上一帧之间的间隔时间
34     updateTime();
35
36     //更新视口尺寸数据, 主要是更新当前摄像机的属性
37     updateViewSizeData();
38
39     //清除实体收集器
40     _entityCollector.clear();
41
42     //对当前渲染的场景进行实体收集, 收集到的对象会在后面进行渲染
43     _scene.traversePartitions(_entityCollector);
44
45     //鼠标及触摸事件的处理
46     _mouse3DManager.updateCollider(this);
47     _touch3DManager.updateCollider();
48
49     // ----- 渲染代码 begin -----
50
51     if (_requireDepthRender)
52         renderSceneDepthToTexture(_entityCollector);
53
54     // todo: perform depth prepass after light update and before final render
55     if (_depthPrepass)
56         renderDepthPrepass(_entityCollector);
57
58     _renderer.clearOnRender = !_depthPrepass;
59
60     if (_filter3DRenderer && _stage3DProxy._context3D) {
61         _renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect);
62         _filter3DRenderer.render(_stage3DProxy, camera, _depthRender);
63     } else {
64         _renderer.shareContext = _shareContext;
65         if (_shareContext)
66             _renderer.render(_entityCollector, null, _scissorRect);
67         else
68             _renderer.render(_entityCollector);
69
70     }
71
72     // ----- 渲染代码  end  -----
73
74     //不共享 context3D 对象就直接渲染, 共享需要手动调用 present 方法
75     if (!_shareContext) {
76         //呈现 3D 画面
77         stage3DProxy.present();
78
79         //释放收集的鼠标和触摸事件
80         _mouse3DManager.fireMouseEvents();
81         _touch3DManager.fireTouchEvents();
82     }
83
84     //清除实体收集器的数据
85     _entityCollector.cleanUp();
86
87     //标记已经渲染完毕
88     stage3DProxy.bufferClear = false;
89 }

撇开诸如鼠标事件的处理,我们可以知道Away3D的核心渲染是分为两个步骤的:

  1. 收集需要渲染的实体;
  2. 根据收集到的实体开始进行真正的渲染;

收集需要渲染的实体:

我们知道在Starling中是直接采用深度优先遍历的方法来遍历显示列表中的所有显示对象,然后一一进行渲染,并没有分为收集和渲染两个步骤;那么在Away3D中3D显示列表也是树形结构,也可以采用Starling的方法来遍历绘制,特别的是Starling采用画家算法,所以需要得到谁先绘制谁后绘制的正确顺序,而Away3D使用的是ZBuffer算法,无论谁先绘制最终都会呈现一样的结果,那么是不是说Away3D的渲染就更加简单了呢?当然不是,Away3D由于是存在一个3D空间中,所以最终的绘制对象需要结合其摄像机的镜头对准的区域来决定,不在可视区域的对象就不进行绘制,即视锥剔除,可以大大的提高渲染效率;那么Away3D的实体收集其核心就是得到需要渲染的3D对象,去掉不需要渲染的3D对象的过程。

我们看看收集实体对象的代码:

1 // collect stuff to render
2 _scene.traversePartitions(_entityCollector);

查看这个方法:

 1 public function traversePartitions(traverser:PartitionTraverser):void
 2 {
 3     var i:uint;
 4     var len:uint = _partitions.length;
 5
 6     traverser.scene = this;
 7
 8     while (i < len)
 9         _partitions[i++].traverse(traverser);
10 }

我们发现一个陌生的类型Partition3D,而几乎所有的实体收集都是由该类接手处理的,那么这个类究竟是什么呢?每一个Scene3D在初始化的时候都会创建一个_partitions:Vector.<Partition3D>。Partition3D是一个空间分区系统的核心,它用于将三维场景分级成多个互不重叠的子空间,从而形成一个树型数据结构。

接着查看traverse方法:

1 public function traverse(traverser:PartitionTraverser):void
2 {
3     if (_updatesMade)
4         updateEntities();
5
6     ++PartitionTraverser._collectionMark;
7
8     _rootNode.acceptTraverser(traverser);
9 }

我们发现了一个_rootNode对象,该对象是记录Partition3D包含的所有对象的树形结构的root,我们接下来看看acceptTraverser方法:

 1 public function acceptTraverser(traverser:PartitionTraverser):void
 2 {
 3     if (_numEntities == 0 && !_debugPrimitive)
 4         return;
 5
 6     if (traverser.enterNode(this)) {
 7         var i:uint;
 8         while (i < _numChildNodes)
 9             _childNodes[i++].acceptTraverser(traverser);
10
11         if (_debugPrimitive)
12             traverser.applyRenderable(_debugPrimitive);
13     }
14 }

注意参数traverser就是我们的实体收集对象的实例_entityCollector,调用的方法enterNode即视锥剔除,会去掉不需要渲染的对象,而实际上添加需要渲染的对象是NodeBase的子类EntityNode的子类MeshNode,我们分别看看EntityNode和MeshNode的acceptTraverser方法:

EntityNode:

1 override public function acceptTraverser(traverser:PartitionTraverser):void
2 {
3     traverser.applyEntity(_entity);
4 }

MeshNode:

 1 override public function acceptTraverser(traverser:PartitionTraverser):void
 2 {
 3     if (traverser.enterNode(this)) {
 4         super.acceptTraverser(traverser);
 5         var subs:Vector.<SubMesh> = _mesh.subMeshes;
 6         var i:uint;
 7         var len:uint = subs.length;
 8         while (i < len)
 9             traverser.applyRenderable(subs[i++]);
10     }
11 }

最终需要渲染的对象都会被收集,交给下一步的渲染代码进行渲染。

Partition3D将我们的3D空间切割为多个不重合的区域,那么如果一个实体对象移动到另一个Partition3D对象的区域,或改变尺寸跨越多个Partition3D对象时Away3D又是如何处理的呢?

我们看看实体类Entity的notifySceneBoundsInvalid方法,当我们的实体对象位置或尺寸改变时会调用该方法:

1 private function notifySceneBoundsInvalid():void
2 {
3     if (_scene)
4         _scene.invalidateEntityBounds(this);
5 }

这个方法会通知到我们的场景对象调用invalidateEntityBounds方法:

1 arcane function invalidateEntityBounds(entity:Entity):void
2 {
3     entity.implicitPartition.markForUpdate(entity);
4 }

markForUpdate方法会重新将我们的实体对象分配到对应的Partition3D对象中去。

另外有一个大神发现了实体回收的bug,链接贴出来:

Away3D 的实体收集器Bug

时间: 2024-10-27 11:09:49

【Away3D代码解读】(二):渲染核心流程(简介、实体对象收集)的相关文章

【Away3D代码解读】(三):渲染核心流程(渲染)

还是老样子,我们还是需要先简略的看一下View3D中render方法的渲染代码,已添加注释: 1 //如果使用了 Filter3D 的话会判断是否需要渲染深度图, 如果需要的话会在实际渲染之前先渲染深度图 2 if (_requireDepthRender) 3 //深度图会被渲染到 _depthRender 这个贴图对象上 4 renderSceneDepthToTexture(_entityCollector); 5 6 // todo: perform depth prepass afte

【Away3D代码解读】(四):主要模块简介

数据模块: Away3D中最核心的数据类是Mesh类,我们先看看Mesh类的继承关系: NamedAssetBase:为对象提供id和name属性,是Away3D大部分类的基类: Object3D:3D对象基类,提供方便操作3D对象本地转换矩阵的功能,提供坐标.旋转.缩放等属性和较多的实用方法,如lookAt.moveLeft等,注意Object3D对象并不是可渲染对象: ObjectContainer3D:作为可存放3D对象的容器,是构成显示列表树形结构的核心,提供sceneTransform

【Away3D代码解读】(五):动画模块及骨骼动画

动画模块核心存放在away3d.animators包里: Away3D支持下面几种动画格式: VertexAnimator:顶点动画 SkeletonAnimator:骨骼动画 UVAnimator:UV动画 SpriteSheetAnimator:二维切换动画 ParticleAnimator:粒子动画 PathAnimator:路径动画 这几种动画都有各自的特点及应用场景,一般而在3D游戏中应用得最广泛的是骨骼动画,因为骨骼动画是人物动画的核心,我们下半段会专门详解这个动画: 动画简介 核心

Spring(二)核心容器 - 简介 、BeanFactory、ApplicationContext

目录 前言 1.容器简介 2.容器的结构 2.1 BeanFactory 2.2 ApplicationContext 2.2.1 ConfigurableApplicationContext 2.2.2 WebApplicationContext 2.3 差异对比 3.ApplicationContext 准备启动 4.总结 前言 在上篇文章中,和大家一起讨论了 Spring 的整体架构,其大致分为五个模块:核心容器.AOP.Web.Data 数据访问.Test模块.其中核心容器是 Sprin

【Away3D代码解读】其它一些的记录(持续更新)

查看当前正在使用的AGAL代码可以在程序开始时添加下面的代码,AGAL代码会被trace出来: 1 Debug.active = true; 具体的输出是在MaterialPassBase类的updateProgram方法中. ----- 使用stereo包(立体包)渲染的图像可以配合红蓝眼立体镜来查看立体效果. ----- SkyBox是不可到达的对象,会优先被渲染(所以总是处于最后方),一般一个场景只会包含一个SkyBox.

Jsoup代码解读之二-DOM相关对象

Jsoup代码解读之二-DOM相关对象 之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼了很多.这篇文章会说明Jsoup的DOM结构,DOM的遍历方式.在下一篇文章,我会并结合这两个基础,分析一下Jsoup的HTML输出功能. DOM结构相关类 我们先来看看nodes包的类图: 这里可以看到,核心无疑是Node类. Node类是一个抽象类,它代表DOM树中的一个节点,它包含: 父节点parent

Activiti入门——环境搭建和核心API简介

相关文章: <史上最权威的Activiti框架学习> <Activiti入门--轻松解读数据库> 本章内容,主要讲解Activiti框架环境的搭建,能够使用Activiti的API创建23张数据库表,正式开始Activiti之旅. 在前一章,介绍了Activitie核心的数据库23张表的特征[数据库结构简介],在这里我们就要把Activiti集成到我们工程中了. 集成Activiti步骤如下: 1.从官网下载Activti框架包: 2.框架集成第一步都是导包,这些jar包可以在官方

Jsoup代码解读之四-parser

Jsoup代码解读之四-parser 作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性.这部分也是Jsoup最复杂的部分,需要一些数据结构.状态机乃至编译器的知识.好在HTML语法不复杂,解析只是到DOM树为止,所以作为编译器入门倒是挺合适的.这一块不要指望囫囵吞枣,我们还是泡一杯咖啡,细细品味其中的奥妙吧. 基础知识 编译器 将计算机语言转化为另一种计算机语言(通常是更底层的语言,例如机器码.汇编.或者JVM字节码)的过程就叫做编译(compile).编译器(

IOS开发核心动画篇---核心动画简介

iOS开发UI篇—核心动画简介 一.简单介绍 Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍.也就是说,使用少量的代码就可以实现非常强大的功能. Core Animation是跨平台的,可以用在Mac OS X和iOS平台. Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程.不阻塞主线程,可以理解为在执行动画的时候还能点击(按钮). 要注意的是,Core Animation是直接作用