代码基于1.7.3版本。整个scene的渲染堆栈如下
Root::startRendering() Root::renderOneFrame() Root::_updateAllRenderTargets() RenderSystem::_updateAllRenderTargets() RenderTarget::update() RenderTarget::updateImpl() RenderTarget::_updateAutoUpdated() D3D9RenderWindow::_updateViewport() RenderTarget::_updateViewport() Viewport::update() Camera::_renderScene() --这里在调用下面的两边,分别是呼叫listener的cameraPreRenderScene()和cameraPostRenderScene() SceneManager::_renderScene()
1、查看阴影是否开启,如果开启,则做一些初始化
if (isShadowTechniqueInUse()) { // Prepare shadow materials initShadowVolumeMaterials(); }
2、测试可以影响到视景体的灯
// Locate any lights which could be affecting the frustum findLightsAffectingFrustum(camera); // Are we using any shadows at all? if (isShadowTechniqueInUse() && vp->getShadowsEnabled()) { // Prepare shadow textures if texture shadow based shadowing // technique in use if (isShadowTechniqueTextureBased()) { OgreProfileGroup("prepareShadowTextures", OGREPROF_GENERAL); // ******* // WARNING // ******* // This call will result in re-entrant calls to this method // therefore anything which comes before this is NOT // guaranteed persistent. Make sure that anything which // MUST be specific to this camera / target is done // AFTER THIS POINT prepareShadowTextures(camera, vp); --此处生成shadow texture?No,因为世界坐标还没有更新到? // reset the cameras & viewport because of the re-entrant call mCameraInProgress = camera; mCurrentViewport = vp; } }
_updateSceneGraph(camera); //octtree的_updateSceneGraph直接调用下面的 void SceneManager::_updateSceneGraph(Camera* cam) { // Process queued needUpdate calls Node::processQueuedUpdates(); // Cascade down the graph updating transforms & world bounds // In this implementation, just update from the root // Smarter SceneManager subclasses may choose to update only // certain scene graph branches getRootSceneNode()->_update(true, false); }
4、预先准备render Queue,PVS生成
// Prepare render queue for receiving new objects { OgreProfileGroup("prepareRenderQueue", OGREPROF_GENERAL); prepareRenderQueue(); } if (mFindVisibleObjects) { OgreProfileGroup("_findVisibleObjects", OGREPROF_CULLING); // Assemble an AAB on the fly which contains the scene elements visible // by the camera. CamVisibleObjectsMap::iterator camVisObjIt = mCamVisibleObjectsMap.find( camera ); assert (camVisObjIt != mCamVisibleObjectsMap.end() && "Should never fail to find a visible object bound for a camera, " "did you override SceneManager::createCamera or something?"); // reset the bounds camVisObjIt->second.reset(); // Parse the scene and tag visibles firePreFindVisibleObjects(vp); _findVisibleObjects(camera, &(camVisObjIt->second), mIlluminationStage == IRS_RENDER_TO_TEXTURE? true : false); firePostFindVisibleObjects(vp); mAutoParamDataSource->setMainCamBoundsInfo(&(camVisObjIt->second)); }
// Begin the frame mDestRenderSystem->_beginFrame(); // Set rasterisation mode mDestRenderSystem->_setPolygonMode(camera->getPolygonMode()); --各种可能的polygon 模式设置 // Set initial camera state mDestRenderSystem->_setProjectionMatrix(mCameraInProgress->getProjectionMatrixRS()); // --设置project matrix,每个物体的世界坐标设置在渲染每个物体的时候会用相关的D3D设置好。 // --这里内部用到 SetTransform( D3DTS_PROJECTION, &mDxProjMat ) mCachedViewMatrix = mCameraInProgress->getViewMatrix(true); if (mCameraRelativeRendering) { mCachedViewMatrix.setTrans(Vector3::ZERO); mCameraRelativePosition = mCameraInProgress->getDerivedPosition(); } mDestRenderSystem->_setTextureProjectionRelativeTo(mCameraRelativeRendering, camera->getDerivedPosition()); setViewMatrix(mCachedViewMatrix); // --设置view matrix // --这里内部用到 SetTransform( D3DTS_VIEW, &mDxViewMat ) // Render scene content { OgreProfileGroup("_renderVisibleObjects", OGREPROF_RENDERING); _renderVisibleObjects(); --从PVS中得到并渲染各个节点的物体 } // End frame mDestRenderSystem->_endFrame();
如果场景不需要shadow的,经由来渲染每个节点
void SceneManager::renderBasicQueueGroupObjects(RenderQueueGroup* pGroup, QueuedRenderableCollection::OrganisationMode om) { // Basic render loop // Iterate through priorities RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator(); while (groupIt.hasMoreElements()) { RenderPriorityGroup* pPriorityGrp = groupIt.getNext(); // Sort the queue first --先按geometry性质排序 pPriorityGrp->sort(mCameraInProgress); // Do solids --先没有透明、半透明的渲染 renderObjects(pPriorityGrp->getSolidsBasic(), om, true, true); // Do unsorted transparents --透明,没有排序的 renderObjects(pPriorityGrp->getTransparentsUnsorted(), om, true, true); // Do transparents (always descending) --透明的 renderObjects(pPriorityGrp->getTransparents(), QueuedRenderableCollection::OM_SORT_DESCENDING, true, true); }// for each priority }
渲染单个物体:
void SceneManager::renderSingleObject(Renderable* rend, const Pass* pass, bool lightScissoringClipping, bool doLightIteration, const LightList* manualLightList) { unsigned short numMatrices; RenderOperation ro; // Set up rendering operation // I know, I know, const_cast is nasty but otherwise it requires all internal // state of the Renderable assigned to the rop to be mutable const_cast<Renderable*>(rend)->getRenderOperation(ro); ro.srcRenderable = rend; GpuProgram* vprog = pass->hasVertexProgram() ? pass->getVertexProgram().get() : 0; bool passTransformState = true; if (vprog) { passTransformState = vprog->getPassTransformStates(); } // Set world transformation numMatrices = rend->getNumWorldTransforms(); if (numMatrices > 0) { rend->getWorldTransforms(mTempXform); if (mCameraRelativeRendering && !rend->getUseIdentityView()) { for (unsigned short i = 0; i < numMatrices; ++i) { mTempXform[i].setTrans(mTempXform[i].getTrans() - mCameraRelativePosition); } } if (passTransformState) { // -- 这里会用 SetTransform( D3DTS_WORLD, &mDxWorldMat ) 设置物体的世界矩阵 if (numMatrices > 1) { mDestRenderSystem->_setWorldMatrices(mTempXform, numMatrices); } else { mDestRenderSystem->_setWorldMatrix(*mTempXform); } } } // Issue view / projection changes if any useRenderableViewProjMode(rend, passTransformState); // mark per-object params as dirty mGpuParamsDirty |= (uint16)GPV_PER_OBJECT; if (!mSuppressRenderStateChanges) { bool passSurfaceAndLightParams = true; if (pass->isProgrammable()) { // Tell auto params object about the renderable change mAutoParamDataSource->setCurrentRenderable(rend); // Tell auto params object about the world matrices, eliminated query from renderable again mAutoParamDataSource->setWorldMatrices(mTempXform, numMatrices); if (vprog) { passSurfaceAndLightParams = vprog->getPassSurfaceAndLightStates(); } } // Reissue any texture gen settings which are dependent on view matrix Pass::ConstTextureUnitStateIterator texIter = pass->getTextureUnitStateIterator(); size_t unit = 0; while(texIter.hasMoreElements()) { TextureUnitState* pTex = texIter.getNext(); if (pTex->hasViewRelativeTextureCoordinateGeneration()) { mDestRenderSystem->_setTextureUnitSettings(unit, *pTex); } ++unit; } // Sort out normalisation // Assume first world matrix representative - shaders that use multiple // matrices should control renormalisation themselves if ((pass->getNormaliseNormals() || mNormaliseNormalsOnScale) && mTempXform[0].hasScale()) mDestRenderSystem->setNormaliseNormals(true); else mDestRenderSystem->setNormaliseNormals(false); // Sort out negative scaling // Assume first world matrix representative if (mFlipCullingOnNegativeScale) { CullingMode cullMode = mPassCullingMode; if (mTempXform[0].hasNegativeScale()) { switch(mPassCullingMode) { case CULL_CLOCKWISE: cullMode = CULL_ANTICLOCKWISE; break; case CULL_ANTICLOCKWISE: cullMode = CULL_CLOCKWISE; break; case CULL_NONE: break; }; } // this also copes with returning from negative scale in previous render op // for same pass if (cullMode != mDestRenderSystem->_getCullingMode()) mDestRenderSystem->_setCullingMode(cullMode); } // Set up the solid / wireframe override // Precedence is Camera, Object, Material // Camera might not override object if not overrideable PolygonMode reqMode = pass->getPolygonMode(); if (pass->getPolygonModeOverrideable() && rend->getPolygonModeOverrideable()) { PolygonMode camPolyMode = mCameraInProgress->getPolygonMode(); // check camera detial only when render detail is overridable if (reqMode > camPolyMode) { // only downgrade detail; if cam says wireframe we don't go up to solid reqMode = camPolyMode; } } mDestRenderSystem->_setPolygonMode(reqMode); if (doLightIteration) { // Create local light list for faster light iteration setup static LightList localLightList; // Here's where we issue the rendering operation to the render system // Note that we may do this once per light, therefore it's in a loop // and the light parameters are updated once per traversal through the // loop const LightList& rendLightList = rend->getLights(); bool iteratePerLight = pass->getIteratePerLight(); // deliberately unsigned in case start light exceeds number of lights // in which case this pass would be skipped int lightsLeft = 1; if (iteratePerLight) { lightsLeft = static_cast<int>(rendLightList.size()) - pass->getStartLight(); // Don't allow total light count for all iterations to exceed max per pass if (lightsLeft > static_cast<int>(pass->getMaxSimultaneousLights())) { lightsLeft = static_cast<int>(pass->getMaxSimultaneousLights()); } } const LightList* pLightListToUse; // Start counting from the start light size_t lightIndex = pass->getStartLight(); size_t depthInc = 0; while (lightsLeft > 0) { // Determine light list to use if (iteratePerLight) { // Starting shadow texture index. size_t shadowTexIndex = mShadowTextures.size(); if (mShadowTextureIndexLightList.size() > lightIndex) shadowTexIndex = mShadowTextureIndexLightList[lightIndex]; localLightList.resize(pass->getLightCountPerIteration()); LightList::iterator destit = localLightList.begin(); unsigned short numShadowTextureLights = 0; for (; destit != localLightList.end() && lightIndex < rendLightList.size(); ++lightIndex, --lightsLeft) { Light* currLight = rendLightList[lightIndex]; // Check whether we need to filter this one out if (pass->getRunOnlyForOneLightType() && pass->getOnlyLightType() != currLight->getType()) { // Skip // Also skip shadow texture(s) if (isShadowTechniqueTextureBased()) { shadowTexIndex += mShadowTextureCountPerType[currLight->getType()]; } continue; } *destit++ = currLight; // potentially need to update content_type shadow texunit // corresponding to this light if (isShadowTechniqueTextureBased()) { size_t textureCountPerLight = mShadowTextureCountPerType[currLight->getType()]; for (size_t j = 0; j < textureCountPerLight && shadowTexIndex < mShadowTextures.size(); ++j) { // link the numShadowTextureLights'th shadow texture unit unsigned short tuindex = pass->_getTextureUnitWithContentTypeIndex( TextureUnitState::CONTENT_SHADOW, numShadowTextureLights); if (tuindex > pass->getNumTextureUnitStates()) break; // I know, nasty const_cast TextureUnitState* tu = const_cast<TextureUnitState*>( pass->getTextureUnitState(tuindex)); const TexturePtr& shadowTex = mShadowTextures[shadowTexIndex]; tu->_setTexturePtr(shadowTex); Camera *cam = shadowTex->getBuffer()->getRenderTarget()->getViewport(0)->getCamera(); tu->setProjectiveTexturing(!pass->hasVertexProgram(), cam); mAutoParamDataSource->setTextureProjector(cam, numShadowTextureLights); ++numShadowTextureLights; ++shadowTexIndex; // Have to set TU on rendersystem right now, although // autoparams will be set later mDestRenderSystem->_setTextureUnitSettings(tuindex, *tu); } } } // Did we run out of lights before slots? e.g. 5 lights, 2 per iteration if (destit != localLightList.end()) { localLightList.erase(destit, localLightList.end()); lightsLeft = 0; } pLightListToUse = &localLightList; // deal with the case where we found no lights // since this is light iteration, we shouldn't render at all if (pLightListToUse->empty()) return; } else // !iterate per light { // Use complete light list potentially adjusted by start light if (pass->getStartLight() || pass->getMaxSimultaneousLights() != OGRE_MAX_SIMULTANEOUS_LIGHTS) { // out of lights? // skip manual 2nd lighting passes onwards if we run out of lights, but never the first one if (pass->getStartLight() > 0 && pass->getStartLight() >= rendLightList.size()) { lightsLeft = 0; break; } else { localLightList.clear(); LightList::const_iterator copyStart = rendLightList.begin(); std::advance(copyStart, pass->getStartLight()); LightList::const_iterator copyEnd = copyStart; // Clamp lights to copy to avoid overrunning the end of the list size_t lightsToCopy = std::min( static_cast<size_t>(pass->getMaxSimultaneousLights()), rendLightList.size() - pass->getStartLight()); std::advance(copyEnd, lightsToCopy); localLightList.insert(localLightList.begin(), copyStart, copyEnd); pLightListToUse = &localLightList; } } else { pLightListToUse = &rendLightList; } lightsLeft = 0; } fireRenderSingleObject(rend, pass, mAutoParamDataSource, pLightListToUse, mSuppressRenderStateChanges); // Do we need to update GPU program parameters? if (pass->isProgrammable()) { useLightsGpuProgram(pass, pLightListToUse); } // Do we need to update light states? // Only do this if fixed-function vertex lighting applies if (pass->getLightingEnabled() && passSurfaceAndLightParams) { useLights(*pLightListToUse, pass->getMaxSimultaneousLights()); } // optional light scissoring & clipping ClipResult scissored = CLIPPED_NONE; ClipResult clipped = CLIPPED_NONE; if (lightScissoringClipping && (pass->getLightScissoringEnabled() || pass->getLightClipPlanesEnabled())) { // if there's no lights hitting the scene, then we might as // well stop since clipping cannot include anything if (pLightListToUse->empty()) continue; if (pass->getLightScissoringEnabled()) scissored = buildAndSetScissor(*pLightListToUse, mCameraInProgress); if (pass->getLightClipPlanesEnabled()) clipped = buildAndSetLightClip(*pLightListToUse); if (scissored == CLIPPED_ALL || clipped == CLIPPED_ALL) continue; } // issue the render op // nfz: check for gpu_multipass mDestRenderSystem->setCurrentPassIterationCount(pass->getPassIterationCount()); // We might need to update the depth bias each iteration if (pass->getIterationDepthBias() != 0.0f) { float depthBiasBase = pass->getDepthBiasConstant() + pass->getIterationDepthBias() * depthInc; // depthInc deals with light iteration // Note that we have to set the depth bias here even if the depthInc // is zero (in which case you would think there is no change from // what was set in _setPass(). The reason is that if there are // multiple Renderables with this Pass, we won't go through _setPass // again at the start of the iteration for the next Renderable // because of Pass state grouping. So set it always // Set modified depth bias right away mDestRenderSystem->_setDepthBias(depthBiasBase, pass->getDepthBiasSlopeScale()); // Set to increment internally too if rendersystem iterates mDestRenderSystem->setDeriveDepthBias(true, depthBiasBase, pass->getIterationDepthBias(), pass->getDepthBiasSlopeScale()); } else { mDestRenderSystem->setDeriveDepthBias(false); } depthInc += pass->getPassIterationCount(); // Finalise GPU parameter bindings updateGpuProgramParameters(pass); if (rend->preRender(this, mDestRenderSystem)) mDestRenderSystem->_render(ro); rend->postRender(this, mDestRenderSystem); if (scissored == CLIPPED_SOME) resetScissor(); if (clipped == CLIPPED_SOME) resetLightClip(); } // possibly iterate per light } else // no automatic light processing { // Even if manually driving lights, check light type passes bool skipBecauseOfLightType = false; if (pass->getRunOnlyForOneLightType()) { if (!manualLightList || (manualLightList->size() == 1 && manualLightList->at(0)->getType() != pass->getOnlyLightType())) { skipBecauseOfLightType = true; } } if (!skipBecauseOfLightType) { fireRenderSingleObject(rend, pass, mAutoParamDataSource, manualLightList, mSuppressRenderStateChanges); // Do we need to update GPU program parameters? if (pass->isProgrammable()) { // Do we have a manual light list? if (manualLightList) { useLightsGpuProgram(pass, manualLightList); } } // Use manual lights if present, and not using vertex programs that don't use fixed pipeline if (manualLightList && pass->getLightingEnabled() && passSurfaceAndLightParams) { useLights(*manualLightList, pass->getMaxSimultaneousLights()); } // optional light scissoring ClipResult scissored = CLIPPED_NONE; ClipResult clipped = CLIPPED_NONE; if (lightScissoringClipping && manualLightList && pass->getLightScissoringEnabled()) { scissored = buildAndSetScissor(*manualLightList, mCameraInProgress); } if (lightScissoringClipping && manualLightList && pass->getLightClipPlanesEnabled()) { clipped = buildAndSetLightClip(*manualLightList); } // don't bother rendering if clipped / scissored entirely if (scissored != CLIPPED_ALL && clipped != CLIPPED_ALL) { // issue the render op // nfz: set up multipass rendering mDestRenderSystem->setCurrentPassIterationCount(pass->getPassIterationCount()); // Finalise GPU parameter bindings updateGpuProgramParameters(pass); if (rend->preRender(this, mDestRenderSystem)) mDestRenderSystem->_render(ro); rend->postRender(this, mDestRenderSystem); } if (scissored == CLIPPED_SOME) resetScissor(); if (clipped == CLIPPED_SOME) resetLightClip(); } // !skipBecauseOfLightType } } else // mSuppressRenderStateChanges { fireRenderSingleObject(rend, pass, mAutoParamDataSource, NULL, mSuppressRenderStateChanges); // Just render mDestRenderSystem->setCurrentPassIterationCount(1); if (rend->preRender(this, mDestRenderSystem)) mDestRenderSystem->_render(ro); rend->postRender(this, mDestRenderSystem); } // Reset view / projection changes if any resetViewProjMode(passTransformState); }
对于上述单个物体来讲,重点步骤如下:
1、设置world matrix
2、设置culling mode
3、设置polygon mode
4、设置light --SetLight() , EnableLight()
5、用DrawPrimitive等绘制图形