【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里解脱出来,使得代码精炼了很多 ...
随机推荐
- VC++的文件格式详解
.APS:存放二进制资源的中间文件,VC把当前资源文件转换成二进制格式,并存放在APS文件中,以加快资源装载速度.资源辅助文件. .BMP:位图资源文件. .BSC:浏览信息文件,由浏览信息维护工具( ...
- Memcache+Cookie解决分布式系统共享登录状态------------------------------Why Memcached?
每个用户请求向IIS发送一个请求,但IIS服务器的请求数有限,cpu支持的线程数有限,如果一秒钟向这台服务器发送10000次,那么则一般就会有问题,考虑集群, 请求数据分流,几台服务器共同对应一个公共 ...
- CHtmlView类的中文介绍
http://zhidao.baidu.com/link?url=h8FaKA6FMNXzYJu_XO-_buBxuGdM0jozKUSVv6pgEPsvhTB2-xLltH-jVLDDJKMBAkn ...
- JMS消息传输机制
JMS消息传送模型: 消息传送机制, 是基于拉取(pull)或者轮询(polling)的方式. JMS具备两种"消息传送模型": P2P和Pub/sub. (1) P2P:点对点 ...
- puppet&mcollective客户端安装
一.环境: 1.客户端: fedora 19 2.DnsServer: 192.168.0.160 3.server1.xxx.com(10.8.1.201):运行以下服 ...
- .NET中各种不同的Timer之间区别
System.Timer.Timer 根据命名空间看这个类貌似才是标准的Timer,它提供Interval属性和Elapsed事件.可以每隔一个时间周期触发一次Elapsed事件.在ThreadPoo ...
- cf A. Inna and Pink Pony(思维题)
题目:http://codeforces.com/contest/374/problem/A 题意:求到达边界的最小步数.. 刚开始以为是 bfs,不过数据10^6太大了,肯定不是... 一个思维题, ...
- [原]Unity3D深入浅出 - 认识开发环境中的GameObject菜单栏
Create Empty:创建空对象 Create Other:创建其他对象 Particle System:创建粒子系统 Camera:创建相机 GUI Text:GUI文本 GUI Texture ...
- jquery在线引用的地址
1. 很多网站都是使用这种方式引入,客户的浏览器可能已经缓存过了 jquery.可以直接调用本地的,速度更快… 2. Google code 使用了 cdn 技术在很多地方有节点服务器,加载 jque ...
- HDU 2289 Cup
Cup Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...