ogre 阴影技术分析记录

来源:互联网 发布:vscode移除文件夹 编辑:程序博客网 时间:2024/06/04 19:20

—-未完,待续

阴影技术,一般分为 基于 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)