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

时间:2022-01-22 21:59:36

我之前解析过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&period;3&period;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代码解读】其它一些的记录&lpar;持续更新&rpar;

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

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

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

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

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

随机推荐

  1. linux系统的初化始配置(包括网络,主机名,关闭firewalld与selinux)

    每次我们使用Linux都会对系统进行初始化的配置,下面我们一一列出来. 1.服务的开启 systemctl enable firewalld.service //将指定的服务设置为开机启动 syste ...

  2. web项目开发规范整理总结

    一.类.函数.变量名命名: 1.定义类时,全部拼音的首字母必须大写:如Person,ClassDemo:(帕斯卡命名法):也可以用带下斜杆的匈牙利命名法进行命名,如    head_navigatio ...

  3. python 核心编程课后练习&lpar;chapter 6&rpar;

    6-1 #6-1 #help(string) import string str = "helloworld" substr = "h1e" if string ...

  4. JavaScript—之对象参数的引用传递

    变量 1.JavaScript hoisting >>请看例子,我们拿Chrome的console作为JS的运行环境. 上面直接执行console.log(a), 不带一点悬念地抛出了no ...

  5. Fiddler Post Debug

    Content-Type: application/json; charset="UTF-8"

  6. 微分方程&mdash&semi;&mdash&semi;基本概念和常微分方程的发展史

    1.2 基本概念和常微分方程的发展史 自变量.未知函数均为实值的微分方程称为实值微分方程:未知函数取复值或变量及未知函数均取复值时称为复值微分方程.若无特别声明,以下均指实变量的实值微分方程. 1.2 ...

  7. 【读书笔记《Android游戏编程之从零开始》】2&period;Hello,World!

    本人看的是PDF文档,很多都是直接都是复制粘贴的记录,简单的记录下笔记! 2.1 创建一个Android项目 Application Name: 应用名称(安装在手机上显示的名字)Project Na ...

  8. cocos2dx建立项目

    http://www.cocos.com/doc/article/index?type=wiki&url=/doc/cocos-docs-master/manual/framework/nat ...

  9. 通过JS触发TextBox的ontextchanged事件,并获取TextBox所在GridView的那一行

    protected void txtInsNum_TextChanged(object sender, EventArgs e) { TextBox t = (TextBox)sender; Grid ...

  10. visual studio 目录

    可执行文件目录:搜索可执行文件时使用的路径.与环境变量PATH相对应.包含目录            :搜索包含文件时使用的路径.与INCLUDE相对应:引用目录            :搜索通过#U ...