【Ogre引擎架构】 第八讲 像素卷积的艺术-GaussianBlur高斯模糊(一)

       高斯模糊作为图形引擎后处理部分的典型代表,已经是初级图形人员的基础课程了,通过高斯模糊,我们可以让渲染效果变得有朦胧感,其实shader的编写并不是特别复杂,但是涉及到渲染管线的后处理流程,实现起来还是有相当的难度,由于内容量较多,作者将分数讲进行讲解。

    渲染管线的流程部分需要掌握熟练OpenGL/DirectX,Ogre把渲染API分别封装到了GLRenderSystem和D3DRenderSystem。

    高斯模糊前效果图:

    

    高斯模糊后:

    

    可以看到Sinbad变得朦胧了很多,下面跟踪一下Ogre的设计思路以及实现流程,Ogre工程中对应此部分的是Compositor_Sample那个工程。

     第一步:注册合成器(Compositor)逻辑(Logic)

	CompositorManager::GetSingleton().RegisterCompositorLogic("GaussianBlur",new GaussianBlurLogic);

	RegisterCompositors();
其中所用的GaussianBlurLogic内容如下:

class GaussianBlurLogic:public ListenerFactoryLogic
{
protected:
	virtual Tomo::CompositorInstance::Listener* CreateListener(Tomo::CompositorInstance* newInstance) ;
};
Tomo::CompositorInstance::Listener* GaussianBlurLogic::CreateListener(Tomo::CompositorInstance* newInstance)
{
	GaussianListener* gaussianListener = new GaussianListener;
	Tomo::Viewport* vp = newInstance->GetChain()->GetViewport();
	gaussianListener->NotifyViewportSize(vp->GetActualWidth(),vp->GetActuralHeight());
	return gaussianListener;
}
我们看到GaussianBlurListener创建了一个Listener,GuassianListener负责高斯模糊过程中的流程控制,生成以及传递必要的参数给渲染管线。
class GaussianListener:public Tomo::CompositorInstance::Listener{
protected:
	int m_VpWidth,m_VpHeight;

	float m_BloomTexWeights[15][4];
	float m_BloomTexOffsetsHorz[15][4];
	float m_BloomTexOffsetsVert[15][4];
public:
	GaussianListener();
	virtual ~GaussianListener();

	void NotifyViewportSize(int width,int height);
	virtual void NotifyMaterialSetup(unsigned int passId,Tomo::MaterialPtr& mat);
};
高斯模糊实际是根据高斯分布的数学原理进行的,我们需要混合当前像素前后左右的像素,高斯分布就是负责给相邻像素分配权重的。


void GaussianListener::NotifyViewportSize(int width,int height)
{
	m_VpWidth = width;
	m_VpHeight = height;

	//central sample,no offsets
	m_BloomTexOffsetsHorz[0][0] = 0.f;
	m_BloomTexOffsetsHorz[0][1] = 0.f;
	m_BloomTexOffsetsVert[0][0] = 0.f;
	m_BloomTexOffsetsVert[0][1] = 0.f;
	//
	float scale = 3.f;
	float texelSize = 1.f/(float)((m_VpWidth>m_VpHeight)?m_VpHeight:m_VpWidth);
	m_BloomTexWeights[0][0]=m_BloomTexWeights[0][1]
	= m_BloomTexWeights[0][2] = Tomo::Math::GaussianDistribution(0,0,scale);
	m_BloomTexWeights[0][3] = 1.f;

	for(int i=1;i<8;++i){
		m_BloomTexWeights[i][0]=m_BloomTexWeights[i][1]
		= m_BloomTexWeights[i][2] = Tomo::Math::GaussianDistribution((float)i,0.f,scale);
		m_BloomTexWeights[i][3] = 1.f;
		//
		m_BloomTexOffsetsHorz[i][0] = i*texelSize;
		m_BloomTexOffsetsHorz[i][1] = 0.f;
		m_BloomTexOffsetsVert[i][0] = 0.f;
		m_BloomTexOffsetsVert[i][1] = i*texelSize;
	}

	for(int i=8;i<15;++i){
		m_BloomTexWeights[i][0]=m_BloomTexWeights[i][1]
		= m_BloomTexWeights[i][2] = m_BloomTexWeights[i-7][0];
		m_BloomTexWeights[i][3] = 1.f;
		//
		m_BloomTexOffsetsHorz[i][0] = -m_BloomTexOffsetsHorz[i-7][0];
		m_BloomTexOffsetsHorz[i][1] = 0.f;
		m_BloomTexOffsetsVert[i][0] = 0.f;
		m_BloomTexOffsetsVert[i][1] = -m_BloomTexOffsetsVert[i-7][1];
	}
}
参数的计算根据当前的Viewport的尺寸进行计算,Viewport大小改变,参数也会随之改变,接下来注册合成器(Compositor)。
void CharacterApplication::RegisterCompositors()
{
	const ResourceManager::ResourceMap& compositorMap = CompositorManager::GetSingleton().GetResourceMap();
	for(ResourceManager::ResourceMap::const_iterator it = compositorMap.begin();
		it != compositorMap.end(); ++it){
			//CompositorPtr comp = *it;
			//
			CompositorManager::GetSingleton().AddCompositor(m_pActiveViewport,it->second->GetName());
			if("Gaussian Blur" == it->second->GetName())
				CompositorManager::GetSingleton().SetCompositorEnabled(m_pActiveViewport,it->second->GetName(),true);
	}
}
所有的Compositor将添加到CompositorManager进行统一管理,但是使用时需要CompositorManager::SetCompositorEnabled。
void CompositorManager::SetCompositorEnabled(Viewport* vp,const string& compositor,bool enabled)
	{
		CompositorChain* chain = GetCompositorChain(vp);

		for(size_t pos = 0;pos < chain->GetNumCompositors();++pos){
			CompositorInstance* inst = chain->GetCompositor(pos);
			if(inst->GetCompositor()->GetName() == compositor){
				chain->SetCompositorEnabled(pos,true);
			}
		}
	}
到了这一步,我们已经使GaussianBlur对应的Compositor产生了作用。
    第二步:通过CompositorManager调度Compositor
class _TomoExport CompositorManager:public ResourceManager,public Singleton<CompositorManager>
	{
	public:
		CompositorManager();
		virtual ~CompositorManager();

		CompositorChain* GetCompositorChain(Viewport* vp);
		CompositorInstance* AddCompositor(Viewport* vp,const string& compositor,size_t addPosition = CompositorChain::LAST);
		void SetCompositorEnabled(Viewport* vp,const string& compositor,bool enabled);
		void RegisterCompositorLogic(const string& name,CompositorLogic* logic);
		CompositorLogic* GetCompositorLogic(const string& name);
		Renderable* GetTexturedRectangle2D();
	protected:
		virtual Resource* CreateImpl(const string& name,const string& group,bool isManual = false, ManualResourceLoader* loader = 0,NameValueMap* params = 0);

		typedef map<string,CompositorLogic*> CompositorLogicMap;
		CompositorLogicMap m_mCompositorLogics;

		typedef map<Viewport*,CompositorChain*> CompositorChainMap;
		CompositorChainMap m_mChains;

		Rectangle2D* m_pRectangle2D;
	};
如上面的CompositorManager类,我们可以看到CompositorLogic,CompositorChain的存储,Compositor其实是封装进了CompositorChain里面通过CompositorInstance的形式存储。
class _TomoExport CompositorChain:public RenderTargetListener,public Viewport::Listener
	{
	public:
		CompositorChain(Viewport* vp);
		virtual ~CompositorChain();
protected:
		Viewport* m_pViewport;

		typedef vector<CompositorInstance*> Instances;
		Instances m_vInstances;
};
CompositorInstance的结构如下,
class _TomoExport CompositorInstance{
	public:
		CompositorInstance(CompositionTechnique* technique,CompositorChain* chain);
		virtual ~CompositorInstance();

		protected:
		Compositor* m_pCompositor;
		CompositionTechnique* m_pTechnique;
		CompositorChain* m_pChain;
	};
由于内容量庞大,CompositorManager的具体调度过程将在下一讲进行讲解。

你可能感兴趣的:(【Ogre引擎架构】 第八讲 像素卷积的艺术-GaussianBlur高斯模糊(一))