由于Unity的渲染器十分复杂,整个渲染循环分支又多,所以本篇只关注渲染主线的数据流水线,即 网格数据流。
Unity的核心渲染组件是 MeshFilter 和 MeshRenderer,前者给后者提供网格数据,后者是单个对象的渲染逻辑。
//在类初始化时自动调用
void MeshFilter::InitializeClass()
{
RegisterAllowNameConversion(TypeOf<MeshFilter>()->GetName(), "m_LodMesh", "m_Mesh");
REGISTER_MESSAGE_VOID(kDidAddComponent, OnDidAddMesh); //注册事件,当有组件添加时触发
}
void MeshFilter::OnDidAddMesh()
{
AssignMeshToRenderer();
}
void MeshFilter::AssignMeshToRenderer()
{
if (GetGameObjectPtr())
{
//当有网格组件添加时,就把网格设置过去
MeshRenderer* renderer = QueryComponent<MeshRenderer>();
if (renderer && renderer->GetSharedMesh() != m_Mesh)
renderer->SetSharedMesh(m_Mesh);
SendMessage(kMeshFilterChanged);
}
}
//所以网格的源数据就存在 MeshRenderer 的 m_Mesh 这个字段中
void MeshRenderer::SetSharedMesh(PPtr<Mesh> mesh)
{
SET_CACHED_SURFACE_AREA_DIRTY();
m_Mesh = mesh; // PPtr<Mesh> m_Mesh;
UpdateCachedMesh();
}
//又做了一次更新,将 m_Mesh 转存在了 m_CachedMesh 中
void MeshRenderer::UpdateCachedMesh()
{
Mesh* mesh = m_Mesh;
if (mesh != m_CachedMesh)
{
...
m_CachedMesh = mesh;
...
UpdateLocalAABB();
}
在 MeshRenderer 中也有一个关键的初始化函数 InitializeClass
void MeshRenderer::InitializeClass()
{
...
RegisterPrepareRenderNodesCallback(kRendererMesh, PrepareMeshRenderNodes<false>, PrepareMeshRenderNodes<true>);
GetRendererUpdateManager().RegisterDispatchUpdate(kRendererMesh, TransformChangeSystemMask(0), DispatchUpdate, PrepareDispatchUpdate, PrepareSingleRendererUpdate, FinalizeUpdate);
}
template<bool kExecuteMultiThreaded>
static void PrepareMeshRenderNodes(RenderNodeQueuePrepareThreadContext& perThreadContext)
这里将 PrepareMeshRenderNodes 函数注册到了一个集中的位置,且类型是 kRendererMesh 这个枚举。这个函数至关重要,后面会讲。
渲染循环的入口点:
LRESULT CALLBACK PlayerMainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ERASEBKGND:
// do not erase background
return 1;
case WM_PAINT:
// redraw screen only when we're paused
if (gInitialized && (kPlayerPaused == GetPlayerPause()))
{
if (!g_ShowingSplashScreen)
{
GfxDevice& device = GetGfxDevice();
device.BeginFrame();
if (device.IsValidState())
{
GetRenderManager().RenderOffscreenCameras();
GetRenderManager().RenderCameras();
#if ENABLE_UNITYGUI
GetGUIManager().Repaint(0);
#endif
Cursors::RenderSoftwareCursor();
}
device.EndFrame();
device.PresentFrame();
}
else
{
DrawSplashScreen(!IsSplashScreenFadeComplete());
}
}
ValidateRect(hWnd, NULL);
return 0;
...
}
...
return DefWindowProcW(hWnd, message, wParam, lParam);
}
在 Windows 系统中渲染循环是在窗口消息 WM_PAINT 触发时执行的。其他的如 Android 或 IOS 各不相同,由各自系统决定。GetRenderManager() 返回的就是总的渲染管理器,它触发了两个渲染操作 RenderOffscreenCameras() 、RenderCameras(),光看名字就能猜到,一个从屏幕之外的相机渲染,一个是真正主屏幕相机。
那么什么事屏幕之外呢?查询 RenderManager 这个类发现两个成员变量 m_Cameras, m_OffScreenCameras。
typedef std::list<PPtr<Camera>> CameraContainer;
CameraContainer m_Cameras, m_OffScreenCameras; //两组相机
//添加相机
void RenderManager::AddCamera(Camera *c)
{
Assert(c != NULL);
PPtr<Camera> cam(c);
...
m_Cameras.remove(cam);
m_OffScreenCameras.remove(cam);
//判断相机是否有 TargetTexture 来选择加入哪一个相机组
CameraContainer &queue = (c->GetTargetTexture() == NULL) ? m_Cameras : m_OffScreenCameras;
...
queue.push_back(c);
}
在Unity中相机组件如果设置的有 TargetTexture 那么它就只渲染到该纹理中,不会渲染到屏幕上,这就是它为啥分开渲染的原因了。
接着回到正题 在函数 RenderManager::RenderCameras(...) 中
void RenderManager::RenderCameras(...)
{
...
RendererScene& scene = GetRendererScene(); //渲染场景
...
CameraStackArray cameraStacks;
FindCameraStacks(m_Cameras, cameraStacks);
while (currentRenderPass < totalRenderPasses)
{
...
// Render each camera stack
for (size_t ist = 0, nst = cameraStacks.size(); ist != nst; ++ist)
{
const CameraStack& stack = cameraStacks[ist];
CameraStackRenderingState stackState;
m_CurrentCameraStackState = &stackState;
stackState.BeginRenderingStack(stack, ist == 0);
// Render each camera in the stack
for (size_t icam = 0, ncam = stack.m_Cameras.size(); icam != ncam; ++icam)
{
Camera* cam = stack.m_Cameras[icam];
if (!ShouldRenderScreenCamera(cam, displayId))
continue;
PROFILER_AUTO_GFX(gCameraRenderManagerProfile, cam);
scene.BeginCameraRender();//渲染场景做准备工作
stackState.SetCurrentCamera(cam);
m_CurrentCamera = cam;
DoRenderScreenCamera(cam, preImageEffectsPerCameraCall, postImageEffectsPerCameraCall); //渲染
scene.EndCameraRender();//进行剔除操作的时候会禁止修改渲染对象列表,将添加和移除操作进行缓存。这里将缓存的操作队列依次处理
}
...
}
...
}
...
}
这里出现了一个重要类 RendererScene,所有拥有渲染组件的对象都会将自己关联到一个统一的地方,进行统一管理。
class MeshRenderer : public Renderer
//当组件设置为激活时触发
void Renderer::SetEnabled(bool newEnabled)
{
if (m_Enabled == newEnabled)
return;
m_Enabled = newEnabled;
SetDirty();
UpdateRenderer();
}
class EXPORT_COREMODULE Renderer : public Unity::Component, public BaseRenderer
void Renderer::UpdateRenderer()
{
bool shouldBeInScene = ShouldBeInScene();
if (IsInScene() == shouldBeInScene)
return;
if (shouldBeInScene)
AddToScene();
else
RemoveFromScene();
}
//将该组件添加进 RendererScene
void Renderer::AddToScene()
{
Assert(!IsInScene());
Assert(!IsVisibleInScene());
RendererScene& scene = GetRendererScene();
m_SceneHandle = scene.AddRenderer(this);
...
}
所以在渲染组件被激活时添加到 RendererScene 中,而失活时就又被移除。添加进场景的组件被处理成 SceneNode
dynamic_array<SceneNode> m_RendererNodes;
struct SceneNode
{
...
BaseRenderer* renderer; //渲染组件用基类存储
UInt32 layer;
...
};
再回到渲染管理器中主线 DoRenderScreenCamera(cam, preImageEffectsPerCameraCall, postImageEffectsPerCameraCall);
static void DoRenderScreenCamera(Camera* cam, RenderManager::PostPerCameraFunctor preImageEffectsPerCameraCall, RenderManager::PostPerCameraFunctor postImageEffectsPerCameraCall)
{
...
{
CullResults cullResults;
if (cam->GetEnabled())
{
cam->Cull(cullResults); //第一步 剔除
}
if (cam->GetEnabled())
{
...
//第二步 渲染
cam->Render(cullResults, GetDefaultPassContext(), Camera::kRenderFlagSetRenderTarget, preImageEffectsPerCameraCall, postImageEffectsPerCameraCall);
}
}
...
}
剔除
在相机类中进行剔除,把结果存在 CullResults 中。剔除操作十分复杂,不在当前文章的讨论范围,这里只找几个关键的点:
void Camera::Cull(CullResults& results, CullingOptions cullingOptions)
{
CameraCullingParameters parameters(*this, kCullFlagNeedsLighting | kCullFlagNeedsReflectionProbes | cullingOptions);
if (GetUseOcclusionCulling())
parameters.cullingOptions |= kCullFlagOcclusionCull;
CustomCull(parameters, results);
}
void Camera::CustomCull(const CameraCullingParameters& parameters, CullResults& results, bool sendOnPreCullMessage)
{
...
m_IsCulling = true;
...
PrepareCullingParameters(parameters, CalculateRenderingPath(), results); //获取剔除参数
...
//获取所有的渲染节点数据,会从 RendererScene 中拿到所有的可渲染的 SceneNode 集合
PrepareCullingParametersRendererArrays(cullingCameraParameters.cameraProperties.coreCameraValues, results);
//开始剔除
CullScene(results);
results.isValid = true;
m_IsCulling = false;
}
CullResults 是整个流程的数据传递核心:
struct CullResults : public NonCopyable
{
...
//剔除后的将要被用于渲染的信息
CullingOutput sceneCullingOutput;
...
//保存要触发 OnWillRenderObject 函数的对象集合
BaseRendererArray needsCullCallback; //全部集中一起保存的
BaseRendererArray rendererCullCallbacks[kRendererTypeCount]; //按类型保存的
//剔除前的保存的所有的参数,内部保存有从 RendererScene 获取到的 SceneNode 集合
SceneCullingParameters sceneCullParameters; //如下
...
SharedRendererScene* sharedRendererScene;
const SharedRendererScene* GetOrCreateSharedRendererScene();
void DestroySharedRendererScene();
...
};
struct SceneCullingParameters : CullingParameters
{
...
//当前渲染场景的所有已经添加进来的节点,并非最后用于渲染的节点。用于剔除的输入参数
RendererCullData *renderers; //如下
int totalRendererListsCount;
...
int renderPath; //渲染路径类型。前向渲染、延迟渲染 等
...
};
//场景渲染节点二次封装
struct RendererCullData
{
const AABB* bounds;
const SceneNode* nodes;
size_t rendererCount;
};
渲染
现在通过渲染的网格数据去寻找整个渲染的一条线
void Camera::Render(CullResults& cullResults, ShaderPassContext& passContext, RenderFlag renderFlags, RenderManager::PostPerCameraFunctor preImageEffectsPerCameraCall, RenderManager::PostPerCameraFunctor postImageEffectsPerCameraCall)
{
//往下走
Render(cullResults, passContext, NULL, renderFlags, preImageEffectsPerCameraCall, postImageEffectsPerCameraCall);
}
void Camera::Render(CullResults& cullResults, ShaderPassContext& passContext, const CameraRenderingParams* params, RenderFlag renderFlags, RenderManager::PostPerCameraFunctor preImageEffectsPerCameraCall, RenderManager::PostPerCameraFunctor postImageEffectsPerCameraCall)
{
//默认的渲染函数处理
DefaultPerformRenderFunction& renderFunctionObj = DefaultPerformRenderFunction::Instance();
//往下走
CustomRender(cullResults, passContext, params, renderFlags, &renderFunctionObj, preImageEffectsPerCameraCall, postImageEffectsPerCameraCall);
}
void Camera::CustomRender(CullResults& cullResults, ShaderPassContext& passContext, const CameraRenderingParams* params, RenderFlag renderFlags, PerformRenderFunction* renderFunctionObj, RenderManager::PostPerCameraFunctor preImageEffectsPerCameraCall, RenderManager::PostPerCameraFunctor postImageEffectsPerCameraCall)
{
...
//根据剔除后的数据,构造一个 SharedRendererScene 对象
const SharedRendererScene* sharedRendererScene = cullResults.GetOrCreateSharedRendererScene();
...
DoRender(cullResults, renderFlags, renderFunctionObj); //渲染所有的几何体
...
CleanupAfterRendering(&cullResults);
}
《1》构造 SharedRendererScene
struct CullResults 内部有字段 SharedRendererScene* sharedRendererScene;
const SharedRendererScene* CullResults::GetOrCreateSharedRendererScene()
{
PROFILER_AUTO(gCreateSharedRendererScene);
__FAKEABLE_METHOD__(CullResults, GetOrCreateSharedRendererScene, ());
if (sharedRendererScene == NULL)
{
CullResults& cullResults = *this;
//new一个新的 SharedRendererScene
sharedRendererScene = UNITY_NEW(SharedRendererScene, kMemTempJobAlloc)(kMemTempJobAlloc);
//提取一个渲染队列用于后续的数据
ExtractSceneRenderNodeQueue(cullResults, kDefaultSceneExtractionFlags, sharedRendererScene->queue);
...
}
return sharedRendererScene;
}
bool ExtractSceneRenderNodeQueue(const CullResults& cullResults, ExtractionFlags flags, RenderNodeQueue& outputQueue)
{
JobBatchDispatcher dispatcher;
const UInt32 customCullNodes = GetCustomCullResultNodeCount(cullResults.sceneCustomCullResults);
RenderNodeQueuePrepareContext* context = BeginRenderQueueExtraction(outputQueue, cullResults.sceneCullingOutput, cullResults.sceneCullParameters.renderers, cullResults.sceneCullParameters.lodDataArrays, customCullNodes, flags, dispatcher);
//往下走
return EndRenderQueueExtraction(context, cullResults.sceneCustomCullResults, dispatcher);
}
bool EndRenderQueueExtraction(RenderNodeQueuePrepareContext* context, const dynamic_array<CustomCullResult*>& customCullResults, JobBatchDispatcher& dispatcher)
{
...
QueuePrepareIntegrateMainThreadObjects(*context); //走
...
return result && (outputQueueFinalSize != outputQueueInitialSize);
}
static void QueuePrepareIntegrateMainThreadObjects(RenderNodeQueuePrepareContext& sharedContext)
{
PrepareRenderNodes* renderNodeCallbacks[kMaxRenderTypeCount];
FillPrepareNodeCallbacks(renderNodeCallbacks, true); //获取各个渲染类型的回调
int contextCount = sharedContext.perThreadContextCount;
int maxObjectCount = sharedContext.outputQueue->GetRenderNodesCount();
for (int i = 0; i < contextCount; i++)
{
RenderNodeQueuePrepareThreadContext& threadContext = sharedContext.perThreadContexts[i];
threadContext.outputIndex = maxObjectCount;
int objCount = threadContext.mainThreadObjects.size();
for (int obj = 0; obj < objCount; obj++)
{
RenderNodeQueuePrepareThreadContext::MainThreadIntegrationInfo& objInfo = threadContext.mainThreadObjects[obj];
...
while (threadContext.inputIndex < threadContext.visibleRenderers.size)
{
BaseRenderer* renderer = GetRendererAtIndex(threadContext, threadContext.inputIndex);
RendererType type = renderer->GetRendererType();
renderNodeCallbacks[type](threadContext); //执行各个渲染数据,获取每个渲染节点的包装数据
}
}
maxObjectCount = threadContext.outputIndex;
}
sharedContext.outputQueue->SetRenderNodesCount(maxObjectCount);
}
//查找各个mesh组件中的回调
static void FillPrepareNodeCallbacks(PrepareRenderNodes** callbacks, bool runOnMainThread)
{
for (UInt32 i = 0; i != kMaxRenderTypeCount; i++)
{
if (gRenderNodeCallbacks[i].prepareRenderNodesMainThread != NULL)
{
if (runOnMainThread)
callbacks[i] = gRenderNodeCallbacks[i].prepareRenderNodesMainThread;
else
callbacks[i] = gRenderNodeCallbacks[i].prepareRenderNodesJob;
}
...
}
}
之前有提到在 MeshRenderer 初始化时注册了一个关键函数 PrepareMeshRenderNodes(...) 用于处理自己类型的网格数据,将 SceneNode 包装为 RenderNode。
RenderNodeQueue 这个类型是用于存放 RenderNode 的集合,所以构造 SharedRendererScene 的最终目的是为了得到 RenderNodeQueue 这个可渲染的数据集。
template<bool kExecuteMultiThreaded>
static void PrepareMeshRenderNodes(RenderNodeQueuePrepareThreadContext& perThreadContext)
{
const IndexList visibleList = perThreadContext.visibleRenderers;
//关键位置,之前获取到的所有的场景节点
const SceneNode* sceneNodes = perThreadContext.rendererCullData.nodes;
...
//遍历场景节点
for (; perThreadContext.inputIndex < visibleList.size; perThreadContext.inputIndex++)
{
UInt32 index = perThreadContext.inputIndex;
int renderIndex = visibleList[index];
const SceneNode& sceneNode = sceneNodes[renderIndex]; //单个场景节点
MeshRenderer* renderer = static_cast<MeshRenderer*>(sceneNode.renderer);//得到渲染网格组件
if (kRendererMesh != renderer->GetRendererType())
break;
if (sceneNode.disable)
continue;
//取到用于渲染的网格数据。
//Mesh* GetMeshUsedForRendering() const { return m_CachedMesh; }
//m_CachedMesh 就是之前处理过的的网格数据字段
Mesh* mesh = renderer->GetMeshUsedForRendering();
if (!mesh)
continue;
...
RenderNode& node = perThreadContext.outputNodes[outputIndex]; //取一个空的渲染节点
renderer->CheckIsTransformInfoUpToDate(!kExecuteMultiThreaded);
//填充一些数据
Renderer::FlattenBasicData(*renderer, CalculateLODFade(lodDataArrays, sceneNode), sceneNode.lodIndexMask, allocator, node);
...
node.smallMeshIndex = mesh->GetInternalMeshID();
//rendererSpecificData 是处理后的网格数据
node.rendererSpecificData = allocator.Allocate<MeshRenderingData>();
MeshRenderingData& renderingData = *(MeshRenderingData*)node.rendererSpecificData;
//填充网格数据 rendererSpecificData
renderingData.Init(mesh, additionalVertexStreams, enlightenVertexStream);//跳转
...
//关键位置,用于最终渲染的工具函数,存在渲染节点 node 中
node.executeCallback = DrawUtil::DrawMeshRawFromNodeQueue;
node.cleanupCallback = DrawUtil::CleanupDrawMeshRawFromNodeQueue;
outputIndex++;
}
perThreadContext.outputIndex = outputIndex;
}
//网格数据填充
inline void MeshRenderingData::Init(Mesh* mesh, Mesh* additionalVertexStreamsMesh, Mesh* enlightenVertexStreamMesh)
{
m_PrimaryMeshData = mesh->AcquireSharedMeshData(); //底层网格数据
m_PrimaryVertexFormat = mesh->GetMeshVertexFormat();
...
}
接回上面的函数位置 FillPrepareNodeCallbacks() QueuePrepareIntegrateMainThreadObjects(...) 执行时就会触发 MeshRenderer 中的 PrepareMeshRenderNodes 函数。
获取两个关键数据 RenderNodeQueue 和 渲染工具函数 executeCallback 。
RenderNodeQueue 会存在 CullResults 下 SharedRendererScene 的成员变量 RenderNodeQueue queue; 中。
《2》DoRender 渲染几何体
void Camera::DoRender(CullResults& cullResults, RenderFlag renderFlags, PerformRenderFunction* renderFunctionObj)
{
...
PreMultiCustomRender(cullResults, renderFlags, true);//再做一个数据中转
MultiCustomRender(cullResults, &renderFunctionObj, 1, true);
PostMultiCustomRender(renderFlags, true);
}
void Camera::PreMultiCustomRender(CullResults& cullResults, RenderFlag renderFlags, bool bSkipMarkers /*= false*/)
{
...
InitializeRenderLoopContext(this, *cullResults.sharedRendererScene, m_RenderLoop);
...
}
void InitializeRenderLoopContext(Camera* camera, const SharedRendererScene& rendererScene, RenderLoop* renderLoop)
{
AssertMsg(renderLoop->m_Context == NULL, "Recursive initialization of the renderloop context. This will leak a lot of memory.");
renderLoop->m_Context = UNITY_NEW(RenderLoopContext, kMemTempJobAlloc)(kMemTempJobAlloc);
const_cast<SharedRendererScene&>(rendererScene).AddRef();
renderLoop->m_Context->m_Camera = camera;
renderLoop->m_Context->m_RenderLoop = renderLoop;
renderLoop->m_Context->m_Scene = &rendererScene;
renderLoop->m_Context->m_Queue = &rendererScene.queue; //关键数据 渲染数据集合
renderLoop->m_Context->m_CommandBufferQueue = &rendererScene.commandBufferQueue;
renderLoop->m_Context->m_TargetType = GetCurrentCameraStackState().GetTarget();
}
不得不承认 Unity 真是能弯弯绕啊,渲染之前还又拿了个 m_RenderLoop、m_Context 做个周转。Camera 在构建时就构造了 RenderLoop 用于渲染处理。
Camera::Camera(MemLabelId label, ObjectCreationMode mode)
{
m_RenderLoop = CreateRenderLoop(*this);
...
}
struct RenderLoop
{
public:
RenderLoop(Camera& camera);
~RenderLoop();
void PrepareFrame(bool renderingShaderReplace);
public:
RenderLoopContext* m_Context;
ImageFilters m_ImageFilters;
};
struct RenderLoopContext : public ThreadSharedObject<RenderLoopContext>
{
RenderLoopContext(MemLabelId memLabel);
~RenderLoopContext();
Camera* m_Camera;
ShadowJobData m_ShadowCullData;
SceneCullingParameters m_ShadowSceneCullParameters;
...
const SharedRendererScene* m_Scene;
const RenderNodeQueue* m_Queue; //渲染节点集合
...
RenderLoop* m_RenderLoop;
};
所以下一步会将渲染逻辑移交到 RenderLoop 中做。
void Camera::MultiCustomRender(CullResults& cullResults, PerformRenderFunction* const* renderFunctionObjects, size_t count, bool bSkipMarkers /*= false*/)
{
...
for (size_t i = 0; i < count; i++)
{
#if ENABLE_ASSERTIONS
if (renderFunctionObjects[i] == &DefaultPerformRenderFunction::Instance())
{
defaultRenderCount++;
}
#endif
(*renderFunctionObjects[i])(this, static_cast<RenderingPath>(cullResults.sceneCullParameters.renderPath), &cullResults);
}
...
}
PerformRenderFunction* const* renderFunctionObjects 这里又根据传入的函数来做处理,查找内部默认的处理函数吧,回到前面
void Camera::Render(CullResults& cullResults, ShaderPassContext& passContext, const CameraRenderingParams* params, RenderFlag renderFlags, RenderManager::PostPerCameraFunctor preImageEffectsPerCameraCall, RenderManager::PostPerCameraFunctor postImageEffectsPerCameraCall)
{
//默认的渲染函数处理
DefaultPerformRenderFunction& renderFunctionObj = DefaultPerformRenderFunction::Instance();
...
}
class DefaultPerformRenderFunction : public PerformRenderFunction
{
public:
virtual void operator()(Camera* camera, RenderingPath renderPath, CullResults* cullResults)
{
if (camera && cullResults)
DoRenderLoop(*camera->m_RenderLoop, renderPath, *cullResults, *camera->m_ShadowCache); //渲染循环
}
...
};
//渲染循环
void DoRenderLoop(
RenderLoop& loop,
RenderingPath renderPath,
CullResults& cullResults,
ShadowMapCache& shadowCache)
{
...
{
PROFILER_AUTO_GFX(gRenderOpaque, &camera);
//根据渲染路径找到对应的渲染逻辑,暂且走 前向渲染路径的处理方式 DoForwardShaderRenderLoop
if (renderPath == kRenderPathPrePass)
{
RenderObjectDataContainer remainingObjects(kMemTempAlloc);
DoPrePassRenderLoop(*loop.m_Context, loop.m_Context->m_OpaqueObjects, remainingObjects, cullResults, rtDepth, rtDepthNormals, &prepassDepthWasCopied);
RenderRemainingObjectsThatCantHandleDeferred(renderPath, *loop.m_Context, cullResults, prepassDepthWasCopied, rtDepth, rtDepthNormals, remainingObjects);
}
else if (renderPath == kRenderPathDeferred)
{
RenderObjectDataContainer remainingObjects(kMemTempJobAlloc);
DoDeferredRenderLoop(*loop.m_Context, loop.m_Context->m_OpaqueObjects, remainingObjects, cullResults, rtDepth, rtDepthNormals, &prepassDepthWasCopied);
RenderRemainingObjectsThatCantHandleDeferred(renderPath, *loop.m_Context, cullResults, prepassDepthWasCopied, rtDepth, rtDepthNormals, remainingObjects);
}
else
{
DoForwardShaderRenderLoop(*loop.m_Context, loop.m_Context->m_OpaqueObjects, cullResults, true, false, true);
}
}
...
}
void DoForwardShaderRenderLoop(
const RenderLoopContext& ctx,
RenderObjectDataContainer& objects,
const CullResults& cullResults,
bool opaque,
bool disableDynamicBatching,
bool clearFrameBuffer,
Camera::RenderFlag cameraRenderFlag)
{
...
//渲染 走
renderLoop->PerformRendering(renderPassDataSortFence, mainDirShadowLight, ctx.m_ShadowCullData, disableDynamicBatching, clearFrameBuffer, opaque, cameraRenderFlag);
}
void ForwardShaderRenderLoop::PerformRendering(JobFence& renderPassDataSortFence, const ActiveLight* mainDirShadowLight, const ShadowJobData& shadowCullData, bool disableDynamicBatching, bool clearFrameBuffer, bool opaque, Camera::RenderFlag cameraRenderFlag)
{
...
//开启渲染任务 走
StartRenderJobs(renderPassDataSortFence, opaque, mixShadowMaskAndRealtimeShadow, defaultPassContext);
...
CleanupAfterRendering();
}
void ForwardShaderRenderLoop::StartRenderJobs(JobFence& renderPassDataSortFence, bool opaque, bool mixShadowmaskAndRealtimeShadow, ShaderPassContext& passContext)
{
...
//走
device.ExecuteAsync(numJobs, ForwardRenderLoopJob, (GfxDeviceAsyncCommand::ArgScratch**)splitHeaders, this, assignProjectorQueueFence);
ClearFenceWithoutSync(assignProjectorQueueFence);
...
}
static void ForwardRenderLoopJob(GfxDeviceAsyncCommand::ArgScratch* scratch, const GfxDeviceAsyncCommand::Arg* arg)
{
...
const RenderNodeQueue& queue = *renderLoop.m_Context->m_Queue;
RenderLoopStats stats;
//这里又把 RenderNodeQueue 传进了 BatchRenderer 这个类中
BatchRenderer batchRenderer(stats, queue, renderLoop.m_EnableDynamicBatching, renderLoop.m_EnableInstancing, false);
...
//循环渲染单个节点
for (size_t renderPassIndex = scratchHeader.startNode; renderPassIndex < scratchHeader.endNode; ++renderPassIndex)
{
...
const RenderNode& node = queue.GetNode(roDataH.nodeIndex);
...
//渲染逻辑再次移交到 BatchRenderer 中
batchRenderer.RenderSingleWithPass(
passContext,
rs.sharedMaterial,
rs.shader,
rs.pass,
rs.flags.subShaderIndex,
passIndex,
instance.nodeID,
instance.subsetIndex,
&renderLoop.m_GrabPasses,
hasBakedLightOcclusions && light.IsOcclusionSeparatelyBaked(),
kBatchBreakCauseMultipleForwardLights);
}
...
}
BatchRenderer 一看就是进行合批处理的逻辑,不过它自然也兼顾普通渲染,合批处理方式又是另一个大内容,暂略过
BatchRenderer::BatchRenderer(RenderLoopStats& stats, const RenderNodeQueue& queue, bool dynamicBatchingEnabled, bool instancingEnabled, bool motionVectorLoop)
: m_BatchInstances(kMemTempAlloc)
...
, m_ActiveNodeQueue(queue) //渲染节点集合又转移给了 m_ActiveNodeQueue 字段
...
{
m_ApplyInstanceProperty.Init(GetGfxDevice());
m_BatchInstances.reserve(128);
}
void BatchRenderer::RenderSingleWithPass(
ShaderPassContext& passContext,
const SharedMaterialData* material,
Shader* shader,
ShaderLab::Pass* pass,
int subshaderIndex,
int passIndex,
UInt32 nodeID, int subsetIndex,
const ShaderLab::GrabPasses* grabPasses,
bool useFwdShadowmaskSelector,
BatchBreakCause cause)
{
...
RenderBatch(&data, 1, vertexInput);
}
void BatchRenderer::RenderBatch(const BatchInstanceData* data, size_t instanceCount, VertexInputMasks vertexInput) const
{
...
const RenderNode& node = m_ActiveNodeQueue.GetNode(data->nodeID);
if (instanceCount == 1 && !m_InstancingBatcher.IsValid())
{
SetupObjectMatrix(node.rendererData.m_TransformInfo.worldMatrix, node.rendererData.m_TransformInfo.transformType);
DebugAssert(node.executeCallback);
//单个渲染节点的 渲染函数调用,还记得之前注册过的工具函数 executeCallback
node.executeCallback(m_ActiveNodeQueue, data->nodeID, vertexInput, data->subsetIndex);
}
// 合批渲染方式 略过
else
{
...
node.executeBatchedCallback(m_ActiveNodeQueue, batchData, vertexInput);
}
}
接下来看看渲染的最后一步,工具函数的处理
void DrawUtil::DrawMeshRawFromNodeQueue(const RenderNodeQueue& queue, UInt32 nodeID, VertexInputMasks vertexInput, int submeshIndex)
{
const RenderNode& node = queue.GetNode(nodeID); //获取当前节点
const MeshRenderingData& data = *static_cast<const MeshRenderingData*>(node.rendererSpecificData); //获取网格数据
GfxDevice& device = GetGfxDevice();
VertexDeclaration* vertexDecl;
DrawBuffersRange drawRange;
MeshBuffers buffers;
//将网格数据填充到 buffers 中
if (!data.PrepareDraw(device, vertexInput, vertexDecl, buffers, drawRange, submeshIndex))
return;
...
//使用当前平台的图形接口进行渲染 走
device.DrawBuffers(buffers.indexBuffer, buffers.vertexBuffers, buffers.vertexBufferCount, &drawRange, 1, vertexDecl);
GPU_TIMESTAMP();
}
//OpenglES 渲染接口
void GfxDeviceGLES::DrawBuffers(GfxBuffer* indexBuf, UInt32 indexStride,
GfxBuffer* const* vertexBufs, const UInt32* vertexStrides, int vertexStreamCount,
const DrawBuffersRange* drawRanges, int drawRangeCount,
VertexDeclaration* vertexDecl)
{
...
BufferGLES* indexBuffer = static_cast<BufferGLES*>(indexBuf); //网格数据,即 顶点数据缓存
indexStride = GetStride(indexBuf, indexStride);
#if GFX_SUPPORTS_SINGLE_PASS_STEREO
m_SinglePassStereoSupport.DrawBuffersStereo(indexBuf, indexStride, vertexBufs, vertexStrides, vertexStreamCount, drawRanges, drawRangeCount, vertexDecl, vertexCount);
#else
//走
DrawBufferRanges(indexBuf, indexStride, vertexBufs, vertexStrides, vertexStreamCount, drawRanges, drawRangeCount, vertexDecl, vertexCount, 1);
#endif
...
}
//渲染
void GfxDeviceGLES::DrawBufferRanges(GfxBuffer* indexBuf, UInt32 indexStride,
GfxBuffer* const* vertexBufs, const UInt32* vertexStrides, int vertexStreamCount,
const DrawBuffersRange* drawRanges, int drawRangeCount,
VertexDeclaration* vertexDecl, size_t vertexCount, int instanceMultiplier)
{
VertexDeclarationGLES* vertexDeclaration = static_cast<VertexDeclarationGLES*>(vertexDecl);
BufferGLES* indexBuffer = static_cast<BufferGLES*>(indexBuf);
...
for (int r = 0; r < drawRangeCount; ++r)
{
...
if (indexBuffer != NULL)
{
//DrawCall 调用图形设备接口渲染。
m_Api.BindElementArrayBuffer(indexBuffer->GetGLName());
//内部就是简单封单封装的 基础图形接口 glDrawElements。 具体细节不展示了
m_Api.DrawElements(range.topology, indexBuffer->GetBindPointer(range.firstIndexByte), range.indexCount, drawBaseVertex, instanceCount, indexStride);
}
else
{
m_Api.DrawArrays(range.topology, range.firstVertex, range.vertexCount, instanceCount);
}
...
}
}
至此按照网格的数据流顺藤摸瓜找到了一个完整的数据流向,这只是一个主线脉络,各种细节还有千千万,至少按照这个主线不至于迷路。
最后,做一个总结:
涉及到的关键类:
RenderManager、RendererScene、SceneNode、Camera、CullResults、RenderNode、RenderNodeQueue、RenderLoop、ForwardShaderRenderLoop、BatchRenderer
MeshFilter、MeshRenderer、Renderer
网格数据流的渲染流水线:
1、MeshRenderer 类初始化注册网格数据预处理函数,预处理函数包含 构造渲染节点逻辑,和平台渲染工具函数。
2、MeshRenderer 被激活时会加入到 RendererScene 场景中,包装成 SceneNode 节点。
3、渲染循环开始时会首先调用 RenderManager 中的相机渲染函数 RenderCameras() ,相机又分为 屏幕外相机(设置了TargetTexture属性的) 和 主相机(屏幕)。
4、 Camera 屏幕相机会调用两个步骤 剔除 和 渲染。
5、剔除会执行各种剔除算法 将可被渲染的 SceneNode 保存在交换数据 CullResults 中。
6、渲染开始的时候会将 CullResults 中的场景节点重新构造成 RenderNode 保存在 RenderNodeQueue 中,RenderNodeQueue 同时也存在 CullResults 中。内部调用的是之前在MeshRenderer中注册的网格预处理函数。(RenderNodeQueue 会往下传递)
7、渲染移交到下一步的 RenderLoop 中,内部会根据渲染路径找到对应的渲染逻辑,比如 前向渲染路径 会走到 ForwardShaderRenderLoop 中。(RenderNodeQueue 会往下传递)
8、再次移交到 BatchRenderer 合批处理中,内部会根据是否合批来执行不同的逻辑。如果是普通逻辑,会走之前MeshRenderer预处函数中注册的工具渲染函数 DrawUtil::DrawMeshRawFromNodeQueue
9、工具函数内部就会调用当前平台的图形设备接口,如 OpenGLES 的 GfxDeviceGLES,内部就会调用DrawCall函数 glDrawElements、glDrawArrays 等等。