【Away3D代码解读】(三):渲染核心流程(渲染)
2014-12-22 14:50 阿诚de窝 阅读(1108) 评论(0) 编辑 收藏 举报还是老样子,我们还是需要先简略的看一下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 }
代码较多,也没有加上注释,大体上是对光源、动画、Method的处理,最后调用了Context3D的drawTriangles方法,对我们当前的Mesh对象进行了最终的绘制。