饭后温柔

汉堡与老干妈同嚼 有可乐味
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ogre 扩展模型描绘轮廓及实现自阴影明暗

Posted on 2012-05-14 18:03  饭后温柔  阅读(1817)  评论(1编辑  收藏  举报

项目中的轮廓描绘使用了扩展模型的方法,即偏移法向量然后重绘.角色自阴影使用shadow map.

轮廓通过寻边(object空间方法)实现的话,算法复杂,留待以后实现.

.h

#ifndef __CartoonRenderManager_H__
#define __CartoonRenderManager_H__
#include "Loki/Singleton.h"
#include "SeerBaseConfig.h"
/*
    CartoonRenderManager是一个单例类,用于描绘entity的轮廓.
    用法:
    Orz::CartoonRenderManager::Instance().setOutline(entity, color);
    其中entity为欲描绘的实体,color为一个颜色的枚举值.请查看函数注释.
*/

namespace Orz
{
    enum CartoonOutlineType { CT_WIREFRAME, CT_EXTENDMODEL };
    enum CartoonOutLineColor { CT_OL_BLACK, CT_OL_RED, CT_OL_WHITE };

    class _OrzSeerBaseExport CartoonRenderManager
    {
        enum CartoonRenderConstant { SP_OFFSET = 1, SP_COLOR };
    public:
        ~CartoonRenderManager();
    public:
        void init(Ogre::SceneManager* psm);

        /*
            @功能                    描绘entity的轮廓.
            @entity                    欲描绘的实体
            @colour                    轮廓颜色的枚举值:CT_OL_BLACK, CT_OL_RED, CT_OL_WHITE分别为黑,红,白.
            @CartoonOutlineType        描绘轮廓的方法.目前支持扩展描绘法.        
        */
        void setOutline(Ogre::Entity* entity, Ogre::ColourValue colour = Ogre::ColourValue::Black, Ogre::Real offset = 1.0, CartoonOutlineType type = CT_EXTENDMODEL);
        void setOutLineOffset(Ogre::Entity* entity, Ogre::Real offset);
        void setOutLineColour(Ogre::Entity* _entity, Ogre::ColourValue colour);
        void destroyMeshPass(Ogre::Entity* entity);
        void setMipmapsBias(Ogre::Entity* entity, Ogre::Real bias = 0.0);

    private:
        typedef Loki::SingletonHolder<CartoonRenderManager> MySingleton;
    public:
        inline static CartoonRenderManager& Instance()
        {
            return MySingleton::Instance();
        }

    private:
        void setOutlineExtendModel(Ogre::Entity* entity, Ogre::ColourValue colour, Ogre::Real offset);
        
        Ogre::SceneManager* m_psm;
    };
}
#endif

.cpp

#include "SeerBaseStableHeaders.h"
#include "CartoonRenderManager.h"

namespace Orz
{

    CartoonRenderManager::~CartoonRenderManager()
    {
        //m_psm->destroyShadowTextures();
    }

    void CartoonRenderManager::init(Ogre::SceneManager* psm)
    {
        m_psm = psm;

        if (Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_HWRENDER_TO_TEXTURE))
        {
            m_psm->setShadowTextureSettings(1024, 1);
        }
        else
        {
            m_psm->setShadowTextureSettings(512, 1);
        }

        m_psm->setShadowTextureSelfShadow(true);

        m_psm->setShadowTextureCasterMaterial("CelShading/Caster/Float");

        m_psm->setShadowTexturePixelFormat(Ogre::PF_FLOAT32_R);

        m_psm->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_ADDITIVE_INTEGRATED);
        m_psm->setShadowCameraSetup(Ogre::ShadowCameraSetupPtr(new Ogre::FocusedShadowCameraSetup));
        //m_psm->setShadowCameraSetup(ShadowCameraSetupPtr(new Ogre::LiSPSMShadowCameraSetup));


        Ogre::Light* light = m_psm->createLight("shadowLight");
        light->setType(Ogre::Light::LightTypes::LT_DIRECTIONAL);
        light->setCastShadows(true);
        light->setDirection(0, 0, -1);
        m_psm->getRootSceneNode()->attachObject(light);
    }

    void CartoonRenderManager::setOutline(Ogre::Entity* entity, Ogre::ColourValue colour /* = Ogre::ColourValue::Black */, Ogre::Real offset /* = 1.0 */, CartoonOutlineType type /* = CT_EXTENDMODEL */)
    {
        switch (type)
        {
        case CT_WIREFRAME:
            {
                //setOutlineWirefrme(entity, colour);
            }
            break;
        case CT_EXTENDMODEL:
            {
                setOutlineExtendModel(entity, colour, offset);
            }
            break;
        }
    }

    void CartoonRenderManager::setOutlineExtendModel(Ogre::Entity* _entity,    Ogre::ColourValue colour, Ogre::Real offset)
    {
        if(!_entity) return;
        //destroyMeshPass(_entity);
        int subMeshNum = _entity->getNumSubEntities();
        int NumTechniques = _entity->getSubEntity(0)->getMaterial()->getNumTechniques();
        for (int i = 0; i < subMeshNum; i++)
        {

            Ogre::SubEntity* sub =  _entity->getSubEntity(i);
            sub->setCustomParameter(SP_OFFSET, Ogre::Vector4(offset, 0, 0, 0));
            sub->setCustomParameter(SP_COLOR, Ogre::Vector4(colour.r, colour.g, colour.b, colour.a));

            for (int j = 0; j < NumTechniques; j ++)
            {
                Ogre::Pass* pass = NULL;
                pass = sub->getMaterial()->getTechnique(j)->getPass("Edges");

                if(pass == NULL)
                {
                    pass =sub->getMaterial()->getTechnique(j)->createPass();
                    pass->setName("Edges"); 
                }

                pass->setVertexProgram("CelShading/OutlineVP");
                pass->setFragmentProgram("CelShading/OutlineFP");
                pass->setCullingMode(Ogre::CULL_ANTICLOCKWISE);


                //
                pass = sub->getMaterial()->getTechnique(j)->getPass(0);
                if(pass == NULL) return;
                pass->setVertexProgram("CelShading/ReceiverVP");
                pass->setFragmentProgram("CelShading/ReceiverFP");

                Ogre::TextureUnitState* state = NULL;
                state = pass->getTextureUnitState("1");
                if(state == NULL) {
                    state = pass->createTextureUnitState();
                    state->setTextureCoordSet(1);
                    state->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
                    state->setTextureFiltering(Ogre::TFO_NONE);
                    state->setContentType(Ogre::TextureUnitState::CONTENT_SHADOW);
                }

                state = pass->getTextureUnitState("2");
                if(state == NULL) {
                    state = pass->createTextureUnitState();
                    state->setTextureCoordSet(2);
                    state->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);
                    state->setTextureFiltering(Ogre::TFO_BILINEAR);
                    state->setTextureName("Effect/shader_texture/celshading_diffuse.gif", Ogre::TEX_TYPE_1D);
                }
            }
        }
    }


    void CartoonRenderManager::setOutLineOffset(Ogre::Entity* _entity, Ogre::Real offset)
    {
        if(!_entity) return;
        int subMeshNum = _entity->getNumSubEntities();
        for (int i = 0; i < subMeshNum; i++)
        {
            Ogre::SubEntity* sub =  _entity->getSubEntity(i);
            sub->setCustomParameter(SP_OFFSET, Ogre::Vector4(offset, 0, 0, 0));
        }
    }

    void CartoonRenderManager::setOutLineColour(Ogre::Entity* _entity, Ogre::ColourValue colour)
    {
        if(!_entity) return;
        int subMeshNum = _entity->getNumSubEntities();
        for (int i = 0; i < subMeshNum; i++)
        {
            Ogre::SubEntity* sub =  _entity->getSubEntity(i);
            sub->setCustomParameter(SP_COLOR, Ogre::Vector4(colour.r, colour.g, colour.b, colour.a));
        }
    }

    void CartoonRenderManager::setMipmapsBias(Ogre::Entity* _entity, Ogre::Real bias /* = 0.0 */)
    {
        if(!_entity) return;

        //临时固定纹理层次mipmaps
        int subMeshNum = _entity->getNumSubEntities();
        for (int i = 0; i < subMeshNum; i++)
        {
            Ogre::SubEntity* sub =  _entity->getSubEntity(i);
            unsigned short NumTechniques = sub->getMaterial()->getNumTechniques();
            for (unsigned short j = 0; j < NumTechniques; j++)
            {
                Ogre::Technique* tech = sub->getMaterial()->getTechnique(j);
                for (unsigned short k = 0; k < tech->getNumPasses(); k++)
                {
                    Ogre::Pass * tempPass = tech->getPass(k);
                    for (unsigned short l = 0; l < tempPass->getNumTextureUnitStates(); l++)
                        tempPass->getTextureUnitState(l)->setTextureMipmapBias(bias);
                }
            }
        }
    }


    void CartoonRenderManager::destroyMeshPass(Ogre::Entity* _entity)
    {
        int subMeshNum = _entity->getNumSubEntities();

        for (int i = 0; i < subMeshNum; i++)
        {
            int NumTechniques = _entity->getSubEntity(i)->getMaterial()->getNumTechniques();
            for (int j = 0; j < NumTechniques; j++)
            {
                Ogre::Pass* pass = _entity->getSubEntity(i)->getMaterial()->getTechnique(j)->getPass("Edges");
                if(pass)
                    _entity->getSubEntity(i)->getMaterial()->getTechnique(j)->removePass(pass->getIndex());

            }
        }    
    }

}

需要注意阴影pass及轮廓pass因为特殊原因在代码中添加.更好的做法应该在.material文件中添加.

轮廓cg

void outline_vp(float4 position    : POSITION,
             float3 normal        : NORMAL,
             float2 uvIn        : TEXCOORD0,
             // outputs
             out float4 oPosition    : POSITION,
             // parameters
             uniform float4 modelOffset,
             uniform float4x4 worldViewProj)
{
    float3 N = normalize(normal);
    position.xyz += modelOffset.x*N;
    oPosition = mul(worldViewProj, position);
}

void outline_fp(out float4 color : COLOR,
                uniform float4 edgeColor : COLOR)
{
    color = edgeColor;
}

////////////////////////////////////////////////////
void outline_wireframe_vp(  float4 position : POSITION,
                            // outputs
                            out float4 oPosition : POSITION,
                            // parameters
                            uniform float4x4 worldViewProj  )
{
    oPosition = mul(worldViewProj, position);
}

void outline_wireframe_fp(  out float4 color : COLOR,
                            uniform float4 edgeColor : COLOR    )
{
    color = edgeColor;
}

轮廓.program

vertex_program CelShading/OutlineVP cg
{
    source CelShading.cg
    entry_point outline_vp
    profiles vs_1_1 arbvp1

    default_params
    {
        param_named_auto worldViewProj worldviewproj_matrix
        param_named_auto modelOffset custom 1
    }
}

fragment_program CelShading/OutlineFP cg
{
    source CelShading.cg
    entry_point outline_fp
    profiles ps_1_1 arbfp1 fp20
 
    default_params
    {
        param_named_auto edgeColor custom 2
    }
}


//////////////////////////////////////////////////////////
vertex_program CelShading/OutlineVP_Wireframe cg
{
    source CelShading.cg
    entry_point outline_wireframe_vp
    profiles vs_1_1 arbvp1

    default_params
    {
        param_named_auto worldViewProj worldviewproj_matrix
    }
}

fragment_program CelShading/OutlineFP_Wireframe cg
{
    source CelShading.cg
    entry_point outline_wireframe_fp
    profiles ps_1_1 arbfp1 fp20
 
    default_params
    {
        param_named_auto edgeColor custom 2
    }
}

阴影cg

// Shadow caster vertex program.
void casterVP(
    float4 position            : POSITION,

    out float4 outPosition    : POSITION,
    out float2 outDepth        : TEXCOORD0,

    uniform float4x4 worldViewProj,
    uniform float4 texelOffsets,
    uniform float4 depthRange
    )
{
    outPosition = mul(worldViewProj, position);

    // fix pixel / texel alignment
    outPosition.xy += texelOffsets.zw * outPosition.w;
    // linear depth storage
    // offset / scale range output
#if LINEAR_RANGE
    outDepth.x = (outPosition.z - depthRange.x) * depthRange.w;
#else
    outDepth.x = outPosition.z;
#endif
    outDepth.y = outPosition.w;
}

// Shadow caster fragment program for high-precision single-channel textures    
void casterFP(
    float2 depth            : TEXCOORD0,
    out float4 result        : COLOR
    )
{
#if LINEAR_RANGE
    float finalDepth = depth.x;
#else
    float finalDepth = depth.x / depth.y;
#endif
    // just smear across all components 
    // therefore this one needs high individual channel precision
    result = float4(finalDepth, finalDepth, finalDepth, 1);
}

void receiverVP(
    float4 position        : POSITION,
    float4 normal        : NORMAL,
    float2 uv            : TEXCOORD0,

    out float4 outPosition    : POSITION,
    out float2 outUV        : TEXCOORD0,
    out float4 outShadowUV    : TEXCOORD1,
    out float outDiffuse    : TEXCOORD2,
#if SPECULAR
    out float outSpecular    : TEXCOORD3,

    uniform float4 eyePosition,
    uniform float shininess,
#endif
    uniform float4x4 world,
    uniform float4x4 worldViewProj,
    uniform float4x4 texViewProj,
    uniform float4 lightPosition,
    uniform float4 shadowDepthRange,
    uniform float4 lightPositionObjectSpace
    )
{
    outPosition = mul(worldViewProj, position);

    outUV = uv;

    // calculate shadow map coords
    float4 worldPos = mul(world, position);
    outShadowUV = mul(texViewProj, worldPos);
#if LINEAR_RANGE
    // adjust by fixed depth bias, rescale into range
    outShadowUV.z = (outShadowUV.z - shadowDepthRange.x) * shadowDepthRange.w;
#endif

    // calculate light vector
    float3 N = normalize(normal.xyz);

#if DIRECTIONAL
    // used in directional light source
    float3 L = normalize(lightPositionObjectSpace.xyz);
#else
    // use this line if used in point light source
    float3 L = normalize(lightPositionObjectSpace.xyz - position.xyz);
#endif
    
    // Calculate diffuse component
    outDiffuse = max(dot(N, L) , 0);
#if SPECULAR
    // Calculate specular component
    float3 E = normalize(eyePosition.xyz - position.xyz);
    float3 H = normalize(L + E);
    outSpecular = pow(max(dot(N, H), 0), shininess);
#endif
}

void receiverFP(
    float4 position            : POSITION,
    float2 uv                : TEXCOORD0,
    float4 shadowUV            : TEXCOORD1,
    float diffuse            : TEXCOORD2,
#if SPECULAR
    float specular            : TEXCOORD3,
#endif

    uniform sampler2D myTexture        : register(s0),
    uniform sampler2D shadowMap        : register(s1),
    uniform sampler1D diffuseRamp    : register(s2),
#if SPECULAR
    uniform sampler1D specularRamp    : register(s3),
#endif
    uniform float inverseShadowmapSize,
    uniform float fixedDepthBias,
    uniform float gradientClamp,
    uniform float gradientScaleBias,
    //uniform float shadowFuzzyWidth,
    uniform float darken,
#if SPECULAR
    uniform float lighten,
#endif

    out float4 result        : COLOR
    )
{
    // point on shadowmap
#if LINEAR_RANGE
    shadowUV.xy = shadowUV.xy / shadowUV.w;
#else
    shadowUV = shadowUV / shadowUV.w;
#endif

    float centerdepth = tex2D(shadowMap, shadowUV.xy).x;
    
    // gradient calculation
      float pixeloffset = inverseShadowmapSize;
    float4 depths = float4(
        tex2D(shadowMap, shadowUV.xy + float2(-pixeloffset, 0)).x,
        tex2D(shadowMap, shadowUV.xy + float2(+pixeloffset, 0)).x,
        tex2D(shadowMap, shadowUV.xy + float2(0, -pixeloffset)).x,
        tex2D(shadowMap, shadowUV.xy + float2(0, +pixeloffset)).x);

    float2 differences = abs( depths.yw - depths.xz );
    float gradient = min(gradientClamp, max(differences.x, differences.y));
    float gradientFactor = gradient * gradientScaleBias;

    // visibility function
    float depthAdjust = gradientFactor + (fixedDepthBias * centerdepth);
    float finalCenterDepth = centerdepth + depthAdjust;

    // shadowUV.z contains lightspace position of current object

    float shadow;
#if FUZZY_TEST
    // fuzzy test - introduces some ghosting in result and doesn't appear to be needed?
    //shadow = saturate(1 + delta_z / (gradient * shadowFuzzyWidth));
    shadow = saturate(1 + (finalCenterDepth - shadowUV.z) * shadowFuzzyWidth * shadowUV.w);
#else
    // hard test
#if PCF
    // use depths from prev, calculate diff
    depths += depthAdjust.xxxx;
    shadow = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f;
    shadow += (depths.x > shadowUV.z) ? 1.0f : 0.0f;
    shadow += (depths.y > shadowUV.z) ? 1.0f : 0.0f;
    shadow += (depths.z > shadowUV.z) ? 1.0f : 0.0f;
    shadow += (depths.w > shadowUV.z) ? 1.0f : 0.0f;
    
    shadow *= 0.2f;
#else
    shadow = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f;
#endif

#endif
    float4 vertexColour = tex2D(myTexture, uv);
    //result = float4(vertexColour.xyz * shadow, 1);

    diffuse = tex1D(diffuseRamp, diffuse).x;
    float darkness = (shadow > diffuse) ? diffuse : shadow;

    float final = (darkness - 1) * darken;
#if SPECULAR
    // if there is already a shadow, no specular will be casted
    final += (shadow > 0.0f) ? tex1D(specularRamp, specular).x * lighten : 0;
#endif

    // then darken the original texture.
    result = float4(vertexColour.xyz + float3(final), 1);
    //result = float4(float3(final),1);
}
// Generic Shadow caster material (floating point shadowmap)
material CelShading/Caster/Float
{
    technique
    {
        pass 
        {
            vertex_program_ref CelShading/CasterVP
            {
            }
            fragment_program_ref CelShading/CasterFP
            {
            }
        }
    }
}

material CelShading/Base
{
    lod_distances 300.0 800.0
    // ultra-high scheme technique uses cel-shading with texture shadow support
    technique
    {
        scheme ultrahigh
        lod_index 0
        pass
        {
            lighting off

            // Vertex program reference
            vertex_program_ref CelShading/ReceiverVP
            {
            }

            // Fragment program
            fragment_program_ref CelShading/ReceiverFP
            {
            }

            texture_unit
            {
                texture_alias textureUnit_0
                tex_address_mode clamp
                filtering bilinear
                tex_coord_set 0
            }
            texture_unit
            {
                content_type shadow
                tex_address_mode clamp
                filtering none
                tex_coord_set 1
            }
            texture_unit
            {
                texture Effect/shader_texture/celshading_diffuse.gif 1d 
                tex_address_mode clamp
                filtering bilinear
                tex_coord_set 2
            }
        }
    }
}

// Basic material with specular which support shadows as a seperate scheme
// material Lolo/CelShadingV4/Specular/Base
// {
    // lod_distances 300.0 800.0
    // // ultra-high scheme technique uses cel-shading with texture shadow support
    // technique
    // {
        // scheme ultrahigh
        // lod_index 0
        // pass ShadeWithShadow
        // {
            // lighting off

            // // Vertex program reference
            // vertex_program_ref CelShading/Specular/ReceiverVP
            // {
            // }

            // // Fragment program
            // fragment_program_ref CelShading/Specular/ReceiverFP
            // {
            // }

            // texture_unit
            // {
                // texture_alias MainTexture
                // tex_address_mode clamp
                // filtering bilinear
                // tex_coord_set 0
            // }
            // texture_unit
            // {
                // content_type shadow
                // tex_address_mode clamp
                // filtering none
                // tex_coord_set 1
            // }
            // texture_unit
            // {
                // texture celshading_diffuse.gif 1d
                // tex_address_mode clamp
                // filtering bilinear
                // tex_coord_set 2
            // }
            // texture_unit
            // {
                // texture celshading_specular.gif 1d
                // tex_address_mode clamp
                // filtering bilinear
                // tex_coord_set 3
            // }
        // }
    // }
// }    

 

阴影.program

vertex_program CelShading/CasterVP cg
{
    source CelShadingShadow.cg
    entry_point casterVP
    profiles arbvp1 vs_2_0
    
    compile_arguments -DLINEAR_RANGE=0

    default_params
    {
        param_named_auto worldViewProj worldviewproj_matrix
        param_named_auto texelOffsets texel_offsets
        param_named_auto depthRange scene_depth_range
    }
}

fragment_program CelShading/CasterFP cg
{
    source CelShadingShadow.cg
    entry_point casterFP
    profiles arbfp1 ps_2_0 fp20

    compile_arguments -DLINEAR_RANGE=0
    
    default_params
    {
    }
}

vertex_program CelShading/ReceiverVP cg
{
    source CelShadingShadow.cg
    entry_point receiverVP
    profiles arbvp1 vs_2_0
    
    // set DDIRECTIONAL=1 if used with directional light
    compile_arguments -DDIRECTIONAL=1 -DLINEAR_RANGE=0 -DSPECULAR=0

    default_params
    {
        param_named_auto world world_matrix
        param_named_auto worldViewProj worldviewproj_matrix
        param_named_auto texViewProj texture_viewproj_matrix
        //param_named_auto lightPosition light_position 0
        param_named_auto shadowDepthRange shadow_scene_depth_range 0
        param_named_auto lightPositionObjectSpace light_position_object_space 0
    }
}

fragment_program CelShading/ReceiverFP cg
{
    source CelShadingShadow.cg
    entry_point receiverFP
    profiles arbfp1 ps_2_0 fp20
    
    compile_arguments -DLINEAR_RANGE=0 -DSPECULAR=0 -DFUZZY_TEST=0 -DPCF=1
    
    default_params
    {
        param_named inverseShadowmapSize float 0.0009765625
        param_named fixedDepthBias float 0.01
        param_named gradientClamp float 0.0098
        param_named gradientScaleBias float 1
        //param_named shadowFuzzyWidth float 0.3
        param_named darken float 0.15
    }
}

阴影部分的cg需要注意平行光源与聚光灯光源的区别,设定cg的编译常量来决定.