1. 程式人生 > >ogre 陰影技術分析記錄

ogre 陰影技術分析記錄

—-未完,待續

陰影技術,一般分為 基於 shadow volume 的和基於shadow map的。

shadow volume 原理是 通過模型EdgeData 計算陰影空間
shadow map原理是在燈光下,計算出物體在地面的區域,設定好顏色,動態紋理(陰影紋理)的形式疊加到接收陰影的物體上。
過程如下:
在場景管理器渲染開始時,建立陰影紋理。
mSceneMgr->_renderScene中呼叫

 if (isShadowTechniqueInUse())
    {
        // Prepare shadow materials
        initShadowVolumeMaterials()
; }

跟進initShadowVolumeMaterials();

    /* This should have been set in the SceneManager constructor, but if you
       created the SceneManager BEFORE the Root object, you will need to call
       SceneManager::_setDestinationRenderSystem manually.
     */
    assert( mDestRenderSystem );

    if
(mShadowMaterialInitDone) return; if (!mShadowDebugPass) { MaterialPtr matDebug = MaterialManager::getSingleton().getByName("Ogre/Debug/ShadowVolumes"); if (matDebug.isNull()) { // Create ,建立陰影的動態紋理 matDebug = MaterialManager
::getSingleton().create( "Ogre/Debug/ShadowVolumes", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME); mShadowDebugPass = matDebug->getTechnique(0)->getPass(0); mShadowDebugPass->setSceneBlending(SBT_ADD); //SBT_ADD將紋理值疊加到場景物體上 mShadowDebugPass->setLightingEnabled(false); mShadowDebugPass->setDepthWriteEnabled(false); TextureUnitState* t = mShadowDebugPass->createTextureUnitState(); t->setColourOperationEx(LBX_MODULATE, LBS_MANUAL, LBS_CURRENT, ColourValue(0.7, 0.0, 0.2)); mShadowDebugPass->setCullingMode(CULL_NONE); if (mDestRenderSystem->getCapabilities()->hasCapability( RSC_VERTEX_PROGRAM)) //渲染系統支援頂點程式的話就進行 陰影計算的程式 { /*陰影gpu程式初始化,(ogre中自帶的陰影gpu程式: "Ogre/ShadowExtrudePointLight", "Ogre/ShadowExtrudePointLightDebug", "Ogre/ShadowExtrudeDirLight", "Ogre/ShadowExtrudeDirLightDebug", "Ogre/ShadowExtrudePointLightFinite", "Ogre/ShadowExtrudePointLightFiniteDebug", "Ogre/ShadowExtrudeDirLightFinite", "Ogre/ShadowExtrudeDirLightFiniteDebug")*/ ShadowVolumeExtrudeProgram::initialise(); // Enable the (infinite) point light extruder for now, just to get some params ,點光源的先開啟 mShadowDebugPass->setVertexProgram( ShadowVolumeExtrudeProgram::programNames[ShadowVolumeExtrudeProgram::POINT_LIGHT]); mShadowDebugPass->setFragmentProgram(ShadowVolumeExtrudeProgram::frgProgramName); mInfiniteExtrusionParams = mShadowDebugPass->getVertexProgramParameters(); mInfiniteExtrusionParams->setAutoConstant(0, GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); mInfiniteExtrusionParams->setAutoConstant(4, GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE); // Note ignored extra parameter - for compatibility with finite extrusion vertex program mInfiniteExtrusionParams->setAutoConstant(5, GpuProgramParameters::ACT_SHADOW_EXTRUSION_DISTANCE); } matDebug->compile(); } else { mShadowDebugPass = matDebug->getTechnique(0)->getPass(0); if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM)) { mInfiniteExtrusionParams = mShadowDebugPass->getVertexProgramParameters(); } } } /********模板陰影的設定*******/ if (!mShadowStencilPass) { MaterialPtr matStencil = MaterialManager::getSingleton().getByName( "Ogre/StencilShadowVolumes"); if (matStencil.isNull()) { // Init matStencil = MaterialManager::getSingleton().create( "Ogre/StencilShadowVolumes", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME); mShadowStencilPass = matStencil->getTechnique(0)->getPass(0); if (mDestRenderSystem->getCapabilities()->hasCapability( RSC_VERTEX_PROGRAM)) { // Enable the finite point light extruder for now, just to get some params mShadowStencilPass->setVertexProgram( ShadowVolumeExtrudeProgram::programNames[ShadowVolumeExtrudeProgram::POINT_LIGHT_FINITE]); mShadowStencilPass->setFragmentProgram(ShadowVolumeExtrudeProgram::frgProgramName); mFiniteExtrusionParams = mShadowStencilPass->getVertexProgramParameters(); mFiniteExtrusionParams->setAutoConstant(0, GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); mFiniteExtrusionParams->setAutoConstant(4, GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE); // Note extra parameter mFiniteExtrusionParams->setAutoConstant(5, GpuProgramParameters::ACT_SHADOW_EXTRUSION_DISTANCE); } matStencil->compile(); // Nothing else, we don't use this like a 'real' pass anyway, // it's more of a placeholder } else { mShadowStencilPass = matStencil->getTechnique(0)->getPass(0); if (mDestRenderSystem->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM)) { mFiniteExtrusionParams = mShadowStencilPass->getVertexProgramParameters(); } } } if (!mShadowModulativePass) { MaterialPtr matModStencil = MaterialManager::getSingleton().getByName( "Ogre/StencilShadowModulationPass"); if (matModStencil.isNull()) { // Init matModStencil = MaterialManager::getSingleton().create( "Ogre/StencilShadowModulationPass", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME); mShadowModulativePass = matModStencil->getTechnique(0)->getPass(0); mShadowModulativePass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO); mShadowModulativePass->setLightingEnabled(false); mShadowModulativePass->setDepthWriteEnabled(false); mShadowModulativePass->setDepthCheckEnabled(false); TextureUnitState* t = mShadowModulativePass->createTextureUnitState(); t->setColourOperationEx(LBX_MODULATE, LBS_MANUAL, LBS_CURRENT, mShadowColour); mShadowModulativePass->setCullingMode(CULL_NONE); } else { mShadowModulativePass = matModStencil->getTechnique(0)->getPass(0); } } // Also init full screen quad while we're at it if (!mFullScreenQuad) { mFullScreenQuad = OGRE_NEW Rectangle2D(); mFullScreenQuad->setCorners(-1,1,1,-1); } // Also init shadow caster material for texture shadows /************新增投影材質*******************/ if (!mShadowCasterPlainBlackPass) { MaterialPtr matPlainBlack = MaterialManager::getSingleton().getByName( "Ogre/TextureShadowCaster"); if (matPlainBlack.isNull()) { matPlainBlack = MaterialManager::getSingleton().create( "Ogre/TextureShadowCaster", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME); mShadowCasterPlainBlackPass = matPlainBlack->getTechnique(0)->getPass(0); // Lighting has to be on, because we need shadow coloured objects // Note that because we can't predict vertex programs, we'll have to // bind light values to those, and so we bind White to ambient // reflectance, and we'll set the ambient colour to the shadow colour mShadowCasterPlainBlackPass->setAmbient(ColourValue::White); mShadowCasterPlainBlackPass->setDiffuse(ColourValue::Black); mShadowCasterPlainBlackPass->setSelfIllumination(ColourValue::Black); mShadowCasterPlainBlackPass->setSpecular(ColourValue::Black); // Override fog mShadowCasterPlainBlackPass->setFog(true, FOG_NONE); // no textures or anything else, we will bind vertex programs // every so often though } else { mShadowCasterPlainBlackPass = matPlainBlack->getTechnique(0)->getPass(0); } } /***************接收陰影材質通道設定******************/ if (!mShadowReceiverPass) { MaterialPtr matShadRec = MaterialManager::getSingleton().getByName( "Ogre/TextureShadowReceiver"); if (matShadRec.isNull()) { matShadRec = MaterialManager::getSingleton().create( "Ogre/TextureShadowReceiver", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME); mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0); // Don't set lighting and blending modes here, depends on additive / modulative TextureUnitState* t = mShadowReceiverPass->createTextureUnitState(); t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); } else { mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0); } } // Set up spot shadow fade texture (loaded from code data block) /*************點光源產生的衰減陰影材質***************/ TexturePtr spotShadowFadeTex = TextureManager::getSingleton().getByName("spot_shadow_fade.png"); if (spotShadowFadeTex.isNull()) { // Load the manual buffer into an image (don't destroy memory! DataStreamPtr stream( OGRE_NEW MemoryDataStream(SPOT_SHADOW_FADE_PNG, SPOT_SHADOW_FADE_PNG_SIZE, false)); Image img; img.load(stream, "png"); spotShadowFadeTex = TextureManager::getSingleton().loadImage( "spot_shadow_fade.png", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME, img, TEX_TYPE_2D); } mShadowMaterialInitDone = true;

本步驟之後,陰影材質、頂點及片段程式已經確定建立完成。之後就是計算了
首先燈光中做必要的計算。

mSceneMgr->_renderScene
->findLightsAffectingFrustum(camera);
在其中,有關陰影計算的有:燈光、相機距離。
然後在呼叫
prepareShadowTextures(camera, vp);進行其他準備

ensureShadowTexturesCreated(); //確保陰影材質已經建立,若沒有則建立,建立紋理、紋理對應的相機、材質,並編譯,儲存


    // Set the illumination stage, prevents recursive calls,照明階段設定,用於控制陰影產生。先將物體渲染到紋理,再渲染物體
    IlluminationRenderStage savedStage = mIlluminationStage;
    mIlluminationStage = IRS_RENDER_TO_TEXTURE;

    if (lightList == 0)
        lightList = &mLightsAffectingFrustum;

    try
    {

        // Determine far shadow distance
        Real shadowDist = mDefaultShadowFarDist;
        if (!shadowDist)
        {
            // need a shadow distance, make one up
            shadowDist = cam->getNearClipDistance() * 300;
        }
        Real shadowOffset = shadowDist * mShadowTextureOffset;
        // Precalculate fading info
        Real shadowEnd = shadowDist + shadowOffset;
        Real fadeStart = shadowEnd * mShadowTextureFadeStart;
        Real fadeEnd = shadowEnd * mShadowTextureFadeEnd;
        // Additive lighting should not use fogging, since it will overbrighten; use border clamp
        if (!isShadowTechniqueAdditive())
        {
            // set fogging to hide the shadow edge 
            mShadowReceiverPass->setFog(true, FOG_LINEAR, ColourValue::White, 
                0, fadeStart, fadeEnd);
        }
        else
        {
            // disable fogging explicitly
            mShadowReceiverPass->setFog(true, FOG_NONE);
        }

        // Iterate over the lights we've found, max out at the limit of light textures
        // Note that the light sorting must now place shadow casting lights at the
        // start of the light list, therefore we do not need to deal with potential
        // mismatches in the light<->shadow texture list any more
    //用燈光計算陰影
        LightList::const_iterator i, iend;
        ShadowTextureList::iterator si, siend;
        ShadowTextureCameraList::iterator ci;
        iend = lightList->end();
        siend = mShadowTextures.end();
        ci = mShadowTextureCameras.begin();
        mShadowTextureIndexLightList.clear();
        size_t shadowTextureIndex = 0;
        for (i = lightList->begin(), si = mShadowTextures.begin();
            i != iend && si != siend; ++i)
        {
            Light* light = *i;

            // skip light if shadows are disabled
            if (!light->getCastShadows())
                continue;

            if (mShadowTextureCurrentCasterLightList.empty())
                mShadowTextureCurrentCasterLightList.push_back(light);
            else
                mShadowTextureCurrentCasterLightList[0] = light;


            // texture iteration per light.
            size_t textureCountPerLight = mShadowTextureCountPerType[light->getType()];
            for (size_t j = 0; j < textureCountPerLight && si != siend; ++j)
            {
                TexturePtr &shadowTex = *si;
                RenderTarget *shadowRTT = shadowTex->getBuffer()->getRenderTarget();
                Viewport *shadowView = shadowRTT->getViewport(0);
                Camera *texCam = *ci;
                // rebind camera, incase another SM in use which has switched to its cam
                shadowView->setCamera(texCam);

                // Associate main view camera as LOD camera
                //關聯主相機為視相機的lod相機
                texCam->setLodCamera(cam);
                // set base
                //設定相機的位置和方向(點光源除外)
                if (light->getType() != Light::LT_POINT)
                    texCam->setDirection(light->getDerivedDirection());
                if (light->getType() != Light::LT_DIRECTIONAL)
                    texCam->setPosition(light->getDerivedPosition());

                // Use the material scheme of the main viewport 
                // This is required to pick up the correct shadow_caster_material and similar properties.
                shadowView->setMaterialScheme(vp->getMaterialScheme());

                // update shadow cam - light mapping
                ShadowCamLightMapping::iterator camLightIt = mShadowCamLightMapping.find( texCam );
                assert(camLightIt != mShadowCamLightMapping.end());
                camLightIt->second = light;

                if (light->getCustomShadowCameraSetup().isNull())
                    mDefaultShadowCameraSetup->getShadowCamera(this, cam, vp, light, texCam, j);//獲取,設定陰影相機,位置、旋轉、遠近、FOV、投射方式(方向燈光的相機設定為正交,其他設定為透射)
                else
                    light->getCustomShadowCameraSetup()->getShadowCamera(this, cam, vp, light, texCam, j);

                // Setup background colour
                shadowView->setBackgroundColour(ColourValue::White);

                // Fire shadow caster update, callee can alter camera settings
                fireShadowTexturesPreCaster(light, texCam, j);

                // Update target ,更新陰影材質
                shadowRTT->update(); 

                ++si; // next shadow texture
                ++ci; // next camera
            }

            // set the first shadow texture index for this light.
            mShadowTextureIndexLightList.push_back(shadowTextureIndex);
            shadowTextureIndex += textureCountPerLight;
        }
    }
    catch (Exception& e) 
    {
        // we must reset the illumination stage if an exception occurs
        mIlluminationStage = savedStage;
        throw e;
    }
    // Set the illumination stage, prevents recursive calls
    mIlluminationStage = savedStage;

    fireShadowTexturesUpdated(
        std::min(lightList->size(), mShadowTextures.size()));

    ShadowTextureManager::getSingleton().clearUnused();

下面是建立陰影紋理的過程(ensureShadowTexturesCreated())
,建立紋理、建立紋理對應的渲染相機、建立材質。

if (mShadowTextureConfigDirty)
    {
        destroyShadowTextures();
        //獲取陰影材質們
        ShadowTextureManager::getSingleton().getShadowTextures(
            mShadowTextureConfigList, mShadowTextures);

        // clear shadow cam - light mapping
        mShadowCamLightMapping.clear();

        //Used to get the depth buffer ID setting for each RTT
        size_t __i = 0;

        // Recreate shadow textures
        //重新建立陰影材質,根據相機的屬性等建立新的
        for (ShadowTextureList::iterator i = mShadowTextures.begin(); 
            i != mShadowTextures.end(); ++i, ++__i) 
        {
            const TexturePtr& shadowTex = *i;

            // Camera names are local to SM 
            String camName = shadowTex->getName() + "Cam";
            // Material names are global to SM, make specific
            String matName = shadowTex->getName() + "Mat" + getName();

            RenderTexture *shadowRTT = shadowTex->getBuffer()->getRenderTarget();

            //Set appropriate depth buffer
            shadowRTT->setDepthBufferPool( mShadowTextureConfigList[__i].depthBufferPoolId );

            // Create camera for this texture, but note that we have to rebind
            // in prepareShadowTextures to coexist with multiple SMs
            //渲染到紋理的形式,先建立相機,將相機所見內容渲染到動態的陰影紋理上
            Camera* cam = createCamera(camName);
            cam->setAspectRatio((Real)shadowTex->getWidth() / (Real)shadowTex->getHeight());
            mShadowTextureCameras.push_back(cam);

            // Create a viewport, if not there already
            if (shadowRTT->getNumViewports() == 0)
            {
                // Note camera assignment is transient when multiple SMs
                Viewport *v = shadowRTT->addViewport(cam);
                v->setClearEveryFrame(true);
                // remove overlays
                v->setOverlaysEnabled(false);
            }

            // Don't update automatically - we'll do it when required   ,設定為非自動更新
            shadowRTT->setAutoUpdated(false);

            // Also create corresponding Material used for rendering this shadow,建立材質
            MaterialPtr mat = MaterialManager::getSingleton().getByName(matName);
            if (mat.isNull())
            {
                mat = MaterialManager::getSingleton().create(
                    matName, ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
            }
            Pass* p = mat->getTechnique(0)->getPass(0);
            if (p->getNumTextureUnitStates() != 1 ||
                p->getTextureUnitState(0)->_getTexturePtr(0) != shadowTex)
            {
                mat->getTechnique(0)->getPass(0)->removeAllTextureUnitStates();
                // create texture unit referring to render target texture
                TextureUnitState* texUnit = 
                    p->createTextureUnitState(shadowTex->getName());
                // set projective based on camera,設定基於相機的投射紋理
                texUnit->setProjectiveTexturing(!p->hasVertexProgram(), cam);
                // clamp to border colour
                texUnit->setTextureAddressingMode(TextureUnitState::TAM_BORDER);
                texUnit->setTextureBorderColour(ColourValue::White);
                mat->touch(); //編譯

            }

            // insert dummy camera-light combination
            mShadowCamLightMapping[cam] = 0;

            // Get null shadow texture
            if (mShadowTextureConfigList.empty())
            {
                mNullShadowTexture.setNull();
            }
            else
            {
                mNullShadowTexture = 
                    ShadowTextureManager::getSingleton().getNullShadowTexture(
                        mShadowTextureConfigList[0].format);
            }


        }
        mShadowTextureConfigDirty = false;
    }

至此,需要陰影所需的計算已準備完畢。已準備的有:
1、投射投影的材質、紋理
2、接收投影的材質、紋理
3、動態陰影紋理,包含其相機設定(根據燈光屬性設定的方位、遠近、投影模式、顏色)
,在準備的基礎上,陰影紋理程式已經更新。

下面的步驟是,在每一幀中將開了投影功能的物體投影到“畫布”陰影紋理上。即將計算的出的引數值,設定到vertex、fragment 程式中

_renderQueueGroupObjects->
renderAdditiveStencilShadowedQueueGroupObjects 或
renderModulativeStencilShadowedQueueGroupObjects 或
renderTextureShadowCasterQueueGroupObjects 或
renderAdditiveTextureShadowedQueueGroupObjects 或
renderModulativeTextureShadowedQueueGroupObjects 或
renderBasicQueueGroupObjects
取決於不同的投影策略

以renderAdditiveStencilShadowedQueueGroupObjects 為例,進行跟蹤

->renderShadowVolumesToStencil
->renderShadowVolumeObjects
->renderSingleObject (單個物體進行program中引數設定,rend)