【Away3D代码解读】(二):渲染核心流程(简介、实体对象收集)
我之前解析过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的核心渲染是分为两个步骤的:
- 收集需要渲染的实体;
- 根据收集到的实体开始进行真正的渲染;
收集需要渲染的实体:
我们知道在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代码解读】(二):渲染核心流程(简介、实体对象收集)的更多相关文章
- 【Away3D代码解读】(三):渲染核心流程(渲染)
还是老样子,我们还是需要先简略的看一下View3D中render方法的渲染代码,已添加注释: //如果使用了 Filter3D 的话会判断是否需要渲染深度图, 如果需要的话会在实际渲染之前先渲染深度图 ...
- 2017.3.31 spring mvc教程(二)核心流程及配置详解
学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...
- 【Away3D代码解读】(四):主要模块简介
数据模块: Away3D中最核心的数据类是Mesh类,我们先看看Mesh类的继承关系: NamedAssetBase:为对象提供id和name属性,是Away3D大部分类的基类: Object3D:3 ...
- 【Away3D代码解读】(五):动画模块及骨骼动画
动画模块核心存放在away3d.animators包里: Away3D支持下面几种动画格式: VertexAnimator:顶点动画 SkeletonAnimator:骨骼动画 UVAnimator: ...
- 【Away3D代码解读】(一):主要类及说明
在深入解读Away3D的代码之前,需要对其有个大概的认识.本节主要列出Away3D中常用的类,并附上说明: View3D: Away3D的入口类,即创建该类就会初始化一个可以使用GPU呈现3D的对象, ...
- Spring(二)核心容器 - 简介 、BeanFactory、ApplicationContext
目录 前言 1.容器简介 2.容器的结构 2.1 BeanFactory 2.2 ApplicationContext 2.2.1 ConfigurableApplicationContext 2.2 ...
- 【Away3D代码解读】其它一些的记录(持续更新)
查看当前正在使用的AGAL代码可以在程序开始时添加下面的代码,AGAL代码会被trace出来: Debug.active = true; 具体的输出是在MaterialPassBase类的update ...
- 从输入一个URL到页面渲染的流程简介
首先说明以下是我参考网上答案和自己的思考,给出自己的想法,如果有问题,欢迎大家吐槽从用户在浏览器中输入一个URL,到整个页面渲染,这个过程中究竟发生了什么呢?今天先简单写下整个过程,后面再一点点完善. ...
- Jsoup代码解读之二-DOM相关对象
Jsoup代码解读之二-DOM相关对象 之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼了很多 ...
随机推荐
- Web开发的绝美网站
http://paranimage.com/ http://sixrevisions.com/graphics-design/
- PHP实现浏览历史记录
http://www.3a88.com/service/206.html http://www.1360.cc/ZhanChangJiaoCheng/6831.html http://www.osch ...
- Windows平台下的session0创建进程的问题与解决办法
很多博客都有记载如何在session0下创建进程的办法,也就是使用CreateProcessAsUser.但是这个要求服务的进程有SE_INCREASE_QUOTA_NAME和SE_ASSIGNPRI ...
- 如何提高Web服务端并发效率的异步编程技术
作为一名web工程师都希望自己做的web应用能被越来越多的人使用,如果我们所做的web应用随着用户的增多而宕机了,那么越来越多的人就会变得越来越少了,为了让我们的web应用能有更多人使用,我们就得提升 ...
- DIV内英文或者数字不换行的问题 解决办法
word-wrap:break-word; word-break:break-all;
- Git教程(2)官方命令文档及常用命令表
http://www.cnblogs.com/angeldevil/archive/2013/11/26/3238470.html 1,官方命令文档 http://www.git-scm.com/do ...
- RPi 2B GPIO 测试
/************************************************************************************** * RPi 2B GPI ...
- 一致性hash算法 - consistent hashing
consistent hashing 算法早在 1997 年就在论文 Consistent hashing and random trees 中被提出,目前在 cache 系统中应用越来越广泛: 1 ...
- 分布式存储Memcache替代Session方案
PHP自带的Session实际是在服务器中为每个客户建立独立的文件存放各自的信息. 在不做处理的情况下,很容易被客户端伪造.并且由于采用文件形式,所以存在着IO 读写的瓶颈.一般当用户在线达到1000 ...
- RMAN数据库异机迁移步骤
--RMAN数据库异机迁移步骤----------------------------2013/09/28 测试环境:AIX+ora11g 一. source数据库准备. 1.获取数据文件编号 ...