zoukankan      html  css  js  c++  java
  • 【Away3D代码解读】(三):渲染核心流程(渲染)

    还是老样子,我们还是需要先简略的看一下View3D中render方法的渲染代码,已添加注释:

     1 //如果使用了 Filter3D 的话会判断是否需要渲染深度图, 如果需要的话会在实际渲染之前先渲染深度图
     2 if (_requireDepthRender)
     3     //深度图会被渲染到 _depthRender 这个贴图对象上
     4     renderSceneDepthToTexture(_entityCollector);
     5 
     6 // todo: perform depth prepass after light update and before final render
     7 if (_depthPrepass)
     8     renderDepthPrepass(_entityCollector);
     9 
    10 _renderer.clearOnRender = !_depthPrepass;
    11 
    12 //判断是否使用了滤镜
    13 if (_filter3DRenderer && _stage3DProxy._context3D) {
    14     //使用滤镜会进行特殊的渲染
    15     _renderer.render(_entityCollector, _filter3DRenderer.getMainInputTexture(_stage3DProxy), _rttBufferManager.renderToTextureRect);
    16     //滤镜渲染对象再渲染一次
    17     _filter3DRenderer.render(_stage3DProxy, camera, _depthRender);
    18 } else {
    19     //如果没有使用滤镜则直接使用默认的渲染对象进行渲染
    20     _renderer.shareContext = _shareContext;
    21     if (_shareContext)
    22         _renderer.render(_entityCollector, null, _scissorRect);
    23     else
    24         _renderer.render(_entityCollector);
    25     
    26 }

    添加filters包中的滤镜可以通过设置View3D的filters3d属性,设置方法和原生显示对象的filters属性一致,不过需要注意的是该属性是针对整个3D场景渲染的。

    本笔记的渲染流程解读就暂时抛开滤镜的渲染,专门解读核心的渲染流程。

    说到Away3D的渲染,那么away3d.core.render这个包就一定要看一下,我们打开这个包后会发现多个以Renderer结尾的类,这些类都是核心的渲染类,我们主要查看DefaultRenderer类即可,Away3D默认的渲染类就是该类。

    渲染流程:

    我们先看看DefaultRenderer类的render方法,位于其父类RendererBase中:

     1 arcane function render(entityCollector:EntityCollector, target:TextureBase = null, scissorRect:Rectangle = null, surfaceSelector:int = 0):void
     2 {
     3     if (!_stage3DProxy || !_context)
     4         return;
     5     
     6     //更新摄像机的投影矩阵
     7     _rttViewProjectionMatrix.copyFrom(entityCollector.camera.viewProjection);
     8     _rttViewProjectionMatrix.appendScale(_textureRatioX, _textureRatioY, 1);
     9     
    10     //执行渲染的方法
    11     executeRender(entityCollector, target, scissorRect, surfaceSelector);
    12     
    13     // clear buffers
    14     for (var i:uint = 0; i < 8; ++i) {
    15         _context.setVertexBufferAt(i, null);
    16         _context.setTextureAt(i, null);
    17     }
    18 }

    我们发现实际是执行executeRender这段代码,先看DefaultRenderer的部分:

     1 protected override function executeRender(entityCollector:EntityCollector, target:TextureBase = null, scissorRect:Rectangle = null, surfaceSelector:int = 0):void
     2 {
     3     //更新光照数据
     4     updateLights(entityCollector);
     5 
     6     //供 filters3d 使用, 我们可以不看
     7     // otherwise RTT will interfere with other RTTs
     8     if (target) {
     9         drawRenderables(entityCollector.opaqueRenderableHead, entityCollector, RTT_PASSES);
    10         drawRenderables(entityCollector.blendedRenderableHead, entityCollector, RTT_PASSES);
    11     }
    12 
    13     //父级方法
    14     super.executeRender(entityCollector, target, scissorRect, surfaceSelector);
    15 }

    再看RendererBase的部分:

     1 protected function executeRender(entityCollector:EntityCollector, target:TextureBase = null, scissorRect:Rectangle = null, surfaceSelector:int = 0):void
     2 {
     3     _renderTarget = target;
     4     _renderTargetSurface = surfaceSelector;
     5     
     6     //对收集到的实体对象进行排序
     7     if (_renderableSorter)
     8         _renderableSorter.sort(entityCollector);
     9     
    10     //渲染到一个纹理, DefaultRenderer 中始终为 false, 略过即可
    11     if (_renderToTexture)
    12         executeRenderToTexturePass(entityCollector);
    13     
    14     //如果 target 存在则渲染到 target 对象, 否则渲染到后台缓冲区
    15     _stage3DProxy.setRenderTarget(target, true, surfaceSelector);
    16     
    17     //清除 3D 场景
    18     if ((target || !_shareContext) && _clearOnRender)
    19         _context.clear(_backgroundR, _backgroundG, _backgroundB, _backgroundAlpha, 1, 0);
    20     //设置深度测试
    21     _context.setDepthTest(false, Context3DCompareMode.ALWAYS);
    22     //设置裁剪区域
    23     _stage3DProxy.scissorRect = scissorRect;
    24     //背景渲染, 这个可以在底部绘制一个图像, 具体可以参考 BackgroundImageRenderer 类
    25     if (_backgroundImageRenderer)
    26         _backgroundImageRenderer.render();
    27     
    28     //调用绘制方法
    29     draw(entityCollector, target);
    30     
    31     //和 Starling 集成时需要还原深度测试
    32     //line required for correct rendering when using away3d with starling. DO NOT REMOVE UNLESS STARLING INTEGRATION IS RETESTED!
    33     _context.setDepthTest(false, Context3DCompareMode.LESS_EQUAL);
    34     
    35     //是否需要截屏到 _snapshotBitmapData 对象中
    36     if (!_shareContext) {
    37         if (_snapshotRequired && _snapshotBitmapData) {
    38             _context.drawToBitmapData(_snapshotBitmapData);
    39             _snapshotRequired = false;
    40         }
    41     }
    42     //清空裁剪区域
    43     _stage3DProxy.scissorRect = null;
    44 }

    我们先看看对实体对象进行排序的类,位于away3d.core.sort包中(包含接口IEntitySorter和类RenderableMergeSort),我们大概了解一下排序类的功能:通过排序获得更好的渲染效率。实体对象会先根据其使用的纹理和与摄像机之间的距离进行排序,不透明的物体会按从前到后的顺序排序,有混合模式的物体也会进行排序以确保得到正确的效果。

    我们发现主要的渲染是通过调用自身的draw方法进行的,我们看下DefaultRenderer的draw方法:

     1 override protected function draw(entityCollector:EntityCollector, target:TextureBase):void
     2 {
     3     //设置混合因子
     4     _context.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ZERO);
     5 
     6     //如果存在天空盒就先绘制天空盒
     7     if (entityCollector.skyBox) {
     8         if (_activeMaterial)
     9             _activeMaterial.deactivate(_stage3DProxy);
    10         _activeMaterial = null;
    11 
    12         _context.setDepthTest(false, Context3DCompareMode.ALWAYS);
    13         drawSkyBox(entityCollector);
    14     }
    15 
    16     //设置深度测试
    17     _context.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);
    18 
    19     var which:int = target? SCREEN_PASSES : ALL_PASSES;
    20     //渲染不透明的实体对象
    21     drawRenderables(entityCollector.opaqueRenderableHead, entityCollector, which);
    22     //渲染带有混合模式的实体对象
    23     drawRenderables(entityCollector.blendedRenderableHead, entityCollector, which);
    24 
    25     //设置深度测试
    26     _context.setDepthTest(false, Context3DCompareMode.LESS_EQUAL);
    27 
    28     //销毁 _activeMaterial 对象
    29     if (_activeMaterial)
    30         _activeMaterial.deactivate(_stage3DProxy);
    31 
    32     _activeMaterial = null;
    33 }

    我们来看看drawRenderables方法实现了什么功能:

     1 private function drawRenderables(item:RenderableListItem, entityCollector:EntityCollector, which:int):void
     2 {
     3     var numPasses:uint;
     4     var j:uint;
     5     var camera:Camera3D = entityCollector.camera;
     6     var item2:RenderableListItem;
     7 
     8     //item 是类似于链表的结构, 通过 item 的 next 属性遍历所有需要渲染的实体对象
     9     while (item) {
    10         //获取纹理对象
    11         _activeMaterial = item.renderable.material;
    12         _activeMaterial.updateMaterial(_context);
    13 
    14         //获取纹理的 Pass 数量, 每个纹理可以附带多个 Pass 对象, Pass 对象进行最终的绘制操作
    15         numPasses = _activeMaterial.numPasses;
    16         j = 0;
    17 
    18         //下面是遍历纹理中的所有 Pass 对象
    19         do {
    20             item2 = item;
    21 
    22             var rttMask:int = _activeMaterial.passRendersToTexture(j)? 1 : 2;
    23 
    24             //这个判断还是判断是否需要渲染到纹理
    25             if ((rttMask & which) != 0) {
    26                 //激活 Pass 对象
    27                 _activeMaterial.activatePass(j, _stage3DProxy, camera);
    28                 //渲染纹理相同的后续实体对象
    29                 do {
    30                     //使用 Pass 对象进行渲染
    31                     _activeMaterial.renderPass(j, item2.renderable, _stage3DProxy, entityCollector, _rttViewProjectionMatrix);
    32                     item2 = item2.next;
    33                 } while (item2 && item2.renderable.material == _activeMaterial);
    34                 //取消 Pass 对象的激活
    35                 _activeMaterial.deactivatePass(j, _stage3DProxy);
    36             } else {
    37                 //跳过纹理相同的后续实体对象
    38                 do
    39                     item2 = item2.next;
    40                 while (item2 && item2.renderable.material == _activeMaterial);
    41             }
    42 
    43         } while (++j < numPasses);
    44 
    45         item = item2;
    46     }
    47 }

    其实到这里我们可以看到,Away3D中的渲染实际是遍历所有的SubMesh对象,通过调用SubMesh所指定的Material的renderPass方法来进行实际的渲染,而renderPass方法的第一个参数表示使用第几个Pass进行渲染,通过改变该参数到达遍历所有Pass对象进行渲染的目的。

    接下来我们的解读暂停一下,看看新出现的Pass和Method对象:

    • 每一类纹理对应一个或多个Pass,其提供主要的渲染控制,每种纹理对应不同的渲染方式,具体点说就是主要提供AGAL的代码。
    • 每一个Pass可以添加多个Method,每一个Method对应一段追加的渲染脚本,本意就是在现有的渲染流程中提供一定的变数,比如颜色转换,纹理模糊、阴影等。

    可以理解为Pass提供核心的渲染代码,而Method可以提供类似原生显示列表中的滤镜的功能,每当给一个纹理添加一个Method之后,对应的Method会对该Pass的片段着色器添加多行实现代码,同时也会提供部分可变参数作为常量提交到GPU。

    具体的把Method的AGAL代码合并到Pass中是在ShaderCompiler的compileMethods方法中实现的。

    下面我们继续解读渲染代码,我们看看MaterialBase类的renderPass方法:

     1 arcane function renderPass(index:uint, renderable:IRenderable, stage3DProxy:Stage3DProxy, entityCollector:EntityCollector, viewProjection:Matrix3D):void
     2 {
     3     //如果存在光源则处理光源
     4     if (_lightPicker)
     5         _lightPicker.collectLights(renderable, entityCollector);
     6     
     7     //获取对应的 pass 对象
     8     var pass:MaterialPassBase = _passes[index];
     9     
    10     //存在动画则更新动画状态
    11     if (renderable.animator)
    12         pass.updateAnimationState(renderable, stage3DProxy, entityCollector.camera);
    13     
    14     //使用 pass 进行渲染
    15     pass.render(renderable, stage3DProxy, entityCollector.camera, viewProjection);
    16 }

    我跟着Basic_View示例进行调试时进入了CompiledPass类的render方法,当前随着不同的Pass对象,实际进行渲染的代码会不一样:

     1 arcane override function render(renderable:IRenderable, stage3DProxy:Stage3DProxy, camera:Camera3D, viewProjection:Matrix3D):void
     2 {
     3     var i:uint;
     4     var context:Context3D = stage3DProxy._context3D;
     5     if (_uvBufferIndex >= 0)
     6         renderable.activateUVBuffer(_uvBufferIndex, stage3DProxy);
     7     if (_secondaryUVBufferIndex >= 0)
     8         renderable.activateSecondaryUVBuffer(_secondaryUVBufferIndex, stage3DProxy);
     9     if (_normalBufferIndex >= 0)
    10         renderable.activateVertexNormalBuffer(_normalBufferIndex, stage3DProxy);
    11     if (_tangentBufferIndex >= 0)
    12         renderable.activateVertexTangentBuffer(_tangentBufferIndex, stage3DProxy);
    13     
    14     if (_animateUVs) {
    15         var uvTransform:Matrix = renderable.uvTransform;
    16         if (uvTransform) {
    17             _vertexConstantData[_uvTransformIndex] = uvTransform.a;
    18             _vertexConstantData[_uvTransformIndex + 1] = uvTransform.b;
    19             _vertexConstantData[_uvTransformIndex + 3] = uvTransform.tx;
    20             _vertexConstantData[_uvTransformIndex + 4] = uvTransform.c;
    21             _vertexConstantData[_uvTransformIndex + 5] = uvTransform.d;
    22             _vertexConstantData[_uvTransformIndex + 7] = uvTransform.ty;
    23         } else {
    24             _vertexConstantData[_uvTransformIndex] = 1;
    25             _vertexConstantData[_uvTransformIndex + 1] = 0;
    26             _vertexConstantData[_uvTransformIndex + 3] = 0;
    27             _vertexConstantData[_uvTransformIndex + 4] = 0;
    28             _vertexConstantData[_uvTransformIndex + 5] = 1;
    29             _vertexConstantData[_uvTransformIndex + 7] = 0;
    30         }
    31     }
    32     
    33     _ambientLightR = _ambientLightG = _ambientLightB = 0;
    34 
    35     if (usesLights())
    36         updateLightConstants();
    37 
    38     if (usesProbes())
    39         updateProbes(stage3DProxy);
    40     
    41     if (_sceneMatrixIndex >= 0) {
    42         renderable.getRenderSceneTransform(camera).copyRawDataTo(_vertexConstantData, _sceneMatrixIndex, true);
    43         viewProjection.copyRawDataTo(_vertexConstantData, 0, true);
    44     } else {
    45         var matrix3D:Matrix3D = Matrix3DUtils.CALCULATION_MATRIX;
    46         matrix3D.copyFrom(renderable.getRenderSceneTransform(camera));
    47         matrix3D.append(viewProjection);
    48         matrix3D.copyRawDataTo(_vertexConstantData, 0, true);
    49     }
    50     
    51     if (_sceneNormalMatrixIndex >= 0)
    52         renderable.inverseSceneTransform.copyRawDataTo(_vertexConstantData, _sceneNormalMatrixIndex, false);
    53     
    54     if (_usesNormals)
    55         _methodSetup._normalMethod.setRenderState(_methodSetup._normalMethodVO, renderable, stage3DProxy, camera);
    56     
    57     var ambientMethod:BasicAmbientMethod = _methodSetup._ambientMethod;
    58     ambientMethod._lightAmbientR = _ambientLightR;
    59     ambientMethod._lightAmbientG = _ambientLightG;
    60     ambientMethod._lightAmbientB = _ambientLightB;
    61     ambientMethod.setRenderState(_methodSetup._ambientMethodVO, renderable, stage3DProxy, camera);
    62     
    63     if (_methodSetup._shadowMethod)
    64         _methodSetup._shadowMethod.setRenderState(_methodSetup._shadowMethodVO, renderable, stage3DProxy, camera);
    65     _methodSetup._diffuseMethod.setRenderState(_methodSetup._diffuseMethodVO, renderable, stage3DProxy, camera);
    66     if (_usingSpecularMethod)
    67         _methodSetup._specularMethod.setRenderState(_methodSetup._specularMethodVO, renderable, stage3DProxy, camera);
    68     if (_methodSetup._colorTransformMethod)
    69         _methodSetup._colorTransformMethod.setRenderState(_methodSetup._colorTransformMethodVO, renderable, stage3DProxy, camera);
    70     
    71     var methods:Vector.<MethodVOSet> = _methodSetup._methods;
    72     var len:uint = methods.length;
    73     for (i = 0; i < len; ++i) {
    74         var set:MethodVOSet = methods[i];
    75         set.method.setRenderState(set.data, renderable, stage3DProxy, camera);
    76     }
    77     
    78     context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 0, _vertexConstantData, _numUsedVertexConstants);
    79     context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, _fragmentConstantData, _numUsedFragmentConstants);
    80     
    81     renderable.activateVertexBuffer(0, stage3DProxy);
    82     context.drawTriangles(renderable.getIndexBuffer(stage3DProxy), 0, renderable.numTriangles);
    83 }
    View Code

    代码较多,也没有加上注释,大体上是对光源、动画、Method的处理,最后调用了Context3D的drawTriangles方法,对我们当前的Mesh对象进行了最终的绘制。

  • 相关阅读:
    C#经典书籍推荐 [转]
    ubuntu的ADSL拨号上网(主要是无线网情况下) [转]
    2007元旦粤北山区:我的“多背1公斤”
    Javascript中给动态生成的表格添加样式,JavaScript里setAttribute的问题
    Asp.net2.0的AjaxPro中不能使用Server.HtmlEncode()函数?
    去了深圳出差,到了珠海泡温泉
    2007元旦粤北山区:翻山越岭到乳源,半途而废云门寺,风雨兼程向瑶山
    很好很强大的FLEX控件
    PHP生成PDF文档的FPDF类
    Flex,事件,绑定,机制
  • 原文地址:https://www.cnblogs.com/hammerc/p/4167932.html
Copyright © 2011-2022 走看看