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

View3D的render方法源码:

 /**
* Renders the view.
*/
public function render():void
{
//if context3D has Disposed by the OS,don't render at this frame
if (!stage3DProxy.recoverFromDisposal()) {
_backBufferInvalid = true;
return;
} // reset or update render settings
if (_backBufferInvalid)
updateBackBuffer(); if (_shareContext && _layeredView)
stage3DProxy.clearDepthBuffer(); if (!_parentIsStage) {
var globalPos:Point = parent.localToGlobal(_localPos);
if (_globalPos.x != globalPos.x || _globalPos.y != globalPos.y) {
_globalPos = globalPos;
_globalPosDirty = true;
}
} if (_globalPosDirty)
updateGlobalPos(); updateTime(); updateViewSizeData(); _entityCollector.clear(); // collect stuff to render
_scene.traversePartitions(_entityCollector); // update picking
_mouse3DManager.updateCollider(this);
_touch3DManager.updateCollider(); if (_requireDepthRender)
renderSceneDepthToTexture(_entityCollector); // todo: perform depth prepass after light update and before final render
if (_depthPrepass)
renderDepthPrepass(_entityCollector); _renderer.clearOnRender = !_depthPrepass; if (_filter3DRenderer && _stage3DProxy._context3D) {
_renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect);
_filter3DRenderer.render(_stage3DProxy, camera, _depthRender);
} else {
_renderer.shareContext = _shareContext;
if (_shareContext)
_renderer.render(_entityCollector, null, _scissorRect);
else
_renderer.render(_entityCollector); } if (!_shareContext) {
stage3DProxy.present(); // fire collected mouse events
_mouse3DManager.fireMouseEvents();
_touch3DManager.fireTouchEvents();
} // clean up data for this render
_entityCollector.cleanUp(); // register that a view has been rendered
stage3DProxy.bufferClear = false;
}

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

 /**
* 渲染 View3D 对象.
*/
public function render():void
{
//判断当前的 context3D 对象是否可以使用, 不能使用则取消本次渲染
if (!stage3DProxy.recoverFromDisposal()) {
_backBufferInvalid = true;
return;
} //如果 View3D 的尺寸改变则更新后台缓冲区的大小
if (_backBufferInvalid)
updateBackBuffer(); //清除深度缓冲
if (_shareContext && _layeredView)
stage3DProxy.clearDepthBuffer(); //如果父级不是 stage 对象则需要获取 View3D 对象的舞台坐标
if (!_parentIsStage) {
var globalPos:Point = parent.localToGlobal(_localPos);
if (_globalPos.x != globalPos.x || _globalPos.y != globalPos.y) {
_globalPos = globalPos;
_globalPosDirty = true;
}
} //更新舞台坐标
if (_globalPosDirty)
updateGlobalPos(); //获取当前帧和上一帧之间的间隔时间
updateTime(); //更新视口尺寸数据, 主要是更新当前摄像机的属性
updateViewSizeData(); //清除实体收集器
_entityCollector.clear(); //对当前渲染的场景进行实体收集, 收集到的对象会在后面进行渲染
_scene.traversePartitions(_entityCollector); //鼠标及触摸事件的处理
_mouse3DManager.updateCollider(this);
_touch3DManager.updateCollider(); // ----- 渲染代码 begin ----- if (_requireDepthRender)
renderSceneDepthToTexture(_entityCollector); // todo: perform depth prepass after light update and before final render
if (_depthPrepass)
renderDepthPrepass(_entityCollector); _renderer.clearOnRender = !_depthPrepass; if (_filter3DRenderer && _stage3DProxy._context3D) {
_renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect);
_filter3DRenderer.render(_stage3DProxy, camera, _depthRender);
} else {
_renderer.shareContext = _shareContext;
if (_shareContext)
_renderer.render(_entityCollector, null, _scissorRect);
else
_renderer.render(_entityCollector); } // ----- 渲染代码 end ----- //不共享 context3D 对象就直接渲染, 共享需要手动调用 present 方法
if (!_shareContext) {
//呈现 3D 画面
stage3DProxy.present(); //释放收集的鼠标和触摸事件
_mouse3DManager.fireMouseEvents();
_touch3DManager.fireTouchEvents();
} //清除实体收集器的数据
_entityCollector.cleanUp(); //标记已经渲染完毕
stage3DProxy.bufferClear = false;
}

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

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

收集需要渲染的实体:

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

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

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

查看这个方法:

 public function traversePartitions(traverser:PartitionTraverser):void
{
var i:uint;
var len:uint = _partitions.length; traverser.scene = this; while (i < len)
_partitions[i++].traverse(traverser);
}

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

接着查看traverse方法:

 public function traverse(traverser:PartitionTraverser):void
{
if (_updatesMade)
updateEntities(); ++PartitionTraverser._collectionMark; _rootNode.acceptTraverser(traverser);
}

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

 public function acceptTraverser(traverser:PartitionTraverser):void
{
if (_numEntities == 0 && !_debugPrimitive)
return; if (traverser.enterNode(this)) {
var i:uint;
while (i < _numChildNodes)
_childNodes[i++].acceptTraverser(traverser); if (_debugPrimitive)
traverser.applyRenderable(_debugPrimitive);
}
}

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

EntityNode:

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

MeshNode:

 override public function acceptTraverser(traverser:PartitionTraverser):void
{
if (traverser.enterNode(this)) {
super.acceptTraverser(traverser);
var subs:Vector.<SubMesh> = _mesh.subMeshes;
var i:uint;
var len:uint = subs.length;
while (i < len)
traverser.applyRenderable(subs[i++]);
}
}

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

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

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

 private function notifySceneBoundsInvalid():void
{
if (_scene)
_scene.invalidateEntityBounds(this);
}

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

 arcane function invalidateEntityBounds(entity:Entity):void
{
entity.implicitPartition.markForUpdate(entity);
}

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

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

Away3D 的实体收集器Bug

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

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

    还是老样子,我们还是需要先简略的看一下View3D中render方法的渲染代码,已添加注释: //如果使用了 Filter3D 的话会判断是否需要渲染深度图, 如果需要的话会在实际渲染之前先渲染深度图 ...

  2. 2017.3.31 spring mvc教程(二)核心流程及配置详解

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

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

    数据模块: Away3D中最核心的数据类是Mesh类,我们先看看Mesh类的继承关系: NamedAssetBase:为对象提供id和name属性,是Away3D大部分类的基类: Object3D:3 ...

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

    动画模块核心存放在away3d.animators包里: Away3D支持下面几种动画格式: VertexAnimator:顶点动画 SkeletonAnimator:骨骼动画 UVAnimator: ...

  5. 【Away3D代码解读】(一):主要类及说明

    在深入解读Away3D的代码之前,需要对其有个大概的认识.本节主要列出Away3D中常用的类,并附上说明: View3D: Away3D的入口类,即创建该类就会初始化一个可以使用GPU呈现3D的对象, ...

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

    目录 前言 1.容器简介 2.容器的结构 2.1 BeanFactory 2.2 ApplicationContext 2.2.1 ConfigurableApplicationContext 2.2 ...

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

    查看当前正在使用的AGAL代码可以在程序开始时添加下面的代码,AGAL代码会被trace出来: Debug.active = true; 具体的输出是在MaterialPassBase类的update ...

  8. 从输入一个URL到页面渲染的流程简介

    首先说明以下是我参考网上答案和自己的思考,给出自己的想法,如果有问题,欢迎大家吐槽从用户在浏览器中输入一个URL,到整个页面渲染,这个过程中究竟发生了什么呢?今天先简单写下整个过程,后面再一点点完善. ...

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

    Jsoup代码解读之二-DOM相关对象   之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼了很多 ...

随机推荐

  1. Android:实现无标题的两种方法

    实现无标题的两种方法:配置xml文件和编写代码设置 1.在AndroidManifest.xml文件中进行配置 实现全屏效果: android:theme="@android:style/T ...

  2. Yii cookie操作

    设置cookie: $cookie = new CHttpCookie('mycookie','this is my cookie'); $cookie->expire = time()+60* ...

  3. python学习笔记五--文件

    任何情况下文本文件在Python里均是字符串模式. 一.创建一个文件,并写入: 函数open(文件名,w) 二.打开一个文件,并读取: 函数open(文件名,r),“r”是默认值,可以不用写 三.使用 ...

  4. JavaScript DOM高级程序设计2.1创建可重用的对象--我要坚持到底!

    1.对象中包含什么 在javascript中,从函数到字符串实际上都是对象 继承 //创建一个person对象的实例 var penson={}; person.getName=function(){ ...

  5. 1521. War Games 2(线段树解约瑟夫)

    1521 根据区间和 来确定第k个数在哪 #include <iostream> #include<cstdio> #include<cstring> #inclu ...

  6. Android反编译教程

    本文摘自 http://blog.csdn.net/ithomer/article/details/6727581 本文Android反编译教程,测试环境: Win7 Ultimate x64 Ubu ...

  7. Myeclipse最简单的svn插件安装方法

    首先来这儿下载插件 http://subclipse.tigris.org/servlets/ProjectDocumentList?folderID=2240 找个最新的下载   解压到对应的位置, ...

  8. SafeHandle和Dispose z

    SafeHandle最大的意义是封装一个托管资源且本身会执行.NET中的资源释放模式(所谓的Dispose Pattern),这样,开发者在使用非托管资源时,不可以不需要执行繁琐的资源释放模式,而直接 ...

  9. HDU 3586-Information Disturbing(树形dp)

    题意: n个节点的通信连接树,切断每个边有一定的花费,要你切断边,在总花费不超过m的前提,使所有的其他节点都不能和节点1(根)连通,切边时有花费上限,让你最小化这个上限. 分析:最小化最大值,想到二分 ...

  10. python 连接oracle 数据库

    我们在测试中可能需要对oracle 数据库进行操纵,比如这样一个场景,在往oracle 里面插数据的同时,另一个工具从里面读,如何能保证读出来的数据是有顺序的,即:先插入进去的先读出来,根据这个场景们 ...