Direct3D轮回:游戏特效之全屏泛光(Bloom)

xiaoxiao2025-08-31  25

https://www.cnblogs.com/kenkao/archive/2011/08/25/2153752.html

Bloom,又称“全屏泛光”,是大名鼎鼎的虚幻3游戏引擎中最通用的后期特效技术~

Bloom特效的实现主要依赖于PostProcess框架,即实时绘制当前场景到一后台渲染表面,而后针对其对应贴图进行像素级渲染~

大家还记得我们之前实现的水面效果中的反射和折射贴图吗?此即为PostProcess的典型应用,与Bloom特效有异曲同工之妙。

下面,我们就来揭秘这个Bloom特效的实现流程~

本节我们实现的Bloom特效,在流程上总共分为4步:

1.提取原场景贴图中的亮色;

2.针对提取贴图进行横向模糊;

3.在横向模糊基础上进行纵向模糊;

4.所得贴图与原场景贴图叠加得最终效果图。

所用到的Shader主要有三个,分别对应如上4个步骤中的第1步、第2、3步和第4步。我们来看源代码:

 

BloomExtract.fx

 

GaussianBlur.fx

 

BloomCombine.fx

 

三个Shader均来自于微软WP7开发者俱乐部,如有引用,敬请标明AppHub字样及其站点网址:http://create.msdn.com/en-US/,以示对作者原创版权的尊重!

 

具备相应的Shader之后,下面我们来看如何运用他们来实现Bloom特效的四个关键步骤~

因为最终的效果贴图本质上是一张与屏幕大小相同的纹理,因此,我们使用原来构建的CSpriteBatch进行绘制。而CSpriteBatch提供的接口是针对于CTexture2D的,所以我们首先要增强并完善CTexture2D类的功能~

以下提供两个函数,使得CTexture2D可以直接产生渲染贴图,并允许获取其后台渲染表面:

 

bool CTexture2D::CreateRenderTarget(UINT SizeX, UINT SizeY) {     // 创建渲染贴图     HRESULT hr;     hr = D3DXCreateTexture(g_pD3DDevice, SizeX, SizeY, 1,         D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pTexture);     if(FAILED(hr))         return false;     m_Width  = SizeX;     m_Height = SizeY;     m_SurRect.top    = 0;     m_SurRect.left   = 0;     m_SurRect.right  = m_Width;     m_SurRect.bottom = m_Height;     return true; } bool CTexture2D::GetRenderSurface(IDirect3DSurface9** pOutSurface) {     // 获得贴图的渲染表面     HRESULT hr;     hr = m_pTexture->GetSurfaceLevel(0, pOutSurface);     if(FAILED(hr))         return false;     else         return true; }

 

之后,就可以着手构建我们的CBloomEffect效果类了:

 

/*------------------------------------- 代码清单:BloomEffect.h 来自:http://www.cnblogs.com/kenkao -------------------------------------*/ #include "D3DEffect.h" #include "Texture2D.h" #pragma once // Bloom参数体 typedef struct _BloomParam {     char*  _Name;              // Bloom特效名称     float  _BloomThreshold;    // 饱和度     float  _BlurAmount;        // 模糊程度     float  _BloomIntensity;    // 模糊剧烈度     float  _BaseIntensity;     // 原始剧烈度     float  _BloomSaturation;   // 模糊饱和度     float  _BaseSaturation;    // 原始饱和度     _BloomParam(){}     _BloomParam(char* name, float bloomThreshold, float blurAmount,         float bloomIntensity, float baseIntensity,         float bloomSaturation, float baseSaturation)     {         _Name = name;  _BloomThreshold = bloomThreshold; _BlurAmount = blurAmount;         _BloomIntensity = bloomIntensity;   _BaseIntensity = baseIntensity;         _BloomSaturation = bloomSaturation; _BaseSaturation = baseSaturation;     } }BloomParam; class CBloomEffect { public:     CBloomEffect(void);     ~CBloomEffect(void); public:     void Create();       // 创建Bloom特效     void Release();      // 释放Bloom特效     void DrawScene();    // 场景绘制     void Begin();        // 开启Bloom     void End();          // 关闭Bloom public:     void        SetParam(BloomParam* pParam){m_pParam = pParam;}  // 参数体设置     BloomParam* GetParam()                  {return m_pParam;}    // 参数体获取 private:     void PostScene();                   // Bloom场景投递     void PostSurface(                   // 应用特效投递贴图到特定缓存表面,本质就是将一个贴图应用Shader之后的效果写入另一个贴图          CTexture2D* pTexture,          // 后台纹理          IDirect3DSurface9* pSurface,   // 渲染表面          CD3DEffect* pEffect);          // 目标特效     bool LoadContent();                 // 加载内容     bool GetSurfaces();                 // 获取渲染表面 private:     float ComputeGaussian(float n);                 // 计算高斯模糊参数     void  SetBlurEffectParam(float dx, float dy);   // 计算偏移数组及权重数组 private:     CD3DEffect* m_pBloomExtractEffect;  // Bloom依次用到的三个特效     CD3DEffect* m_pGaussianBlurEffect;     CD3DEffect* m_pBloomCombineEffect; private:     CTexture2D*        m_pResolveTarget;   // 原始贴图     CTexture2D*        m_pTexture1;        // 模糊贴图     CTexture2D*        m_pTexture2;        // 临时模糊贴图     IDirect3DSurface9* m_pResolveSurface;  // 原始贴图渲染表面     IDirect3DSurface9* m_pSurface1;        // 模糊贴图渲染表面     IDirect3DSurface9* m_pSurface2;        // 临时贴图渲染表面     IDirect3DSurface9* m_pOriSurface;      // 初始渲染表面 private:     BloomParam* m_pParam;                  // Bloom参数体 };

 

BloomEffect.cpp

 

该类共产生了3个渲染表面,并在渲染过程中发生了相互叠加,嗯…有点混乱…

我们不妨来看一看每个步骤所得到的渲染效果,而后再针对4个步骤进行分析。如下是我在渲染过程中提取的效果图:

有了这个流程图,大家的思路是不是清晰了一些呢?下面,大家结合这个流程图来分析各个步骤:

第一步:

应用BloomExtract特效提取原始场景贴图m_pResolveTarget中较明亮的颜色绘制到m_pTexture1贴图中(m_pResolveTarget--->m_pTexture1)

第二步:

应用GaussianBlur特效,在m_pTexture1贴图基础上进行横向高斯模糊到m_pTexture2贴图(m_pTexture1--->m_pTexture2)

第三步:

再次应用GaussianBlur特效,在横向模糊之后的m_pTexture2贴图基础上进行纵向高斯模糊,得到最终的模糊贴图m_pTexture1(m_pTexture2--->m_pTexture1)

注意:此时,m_pTexture1贴图即为最终的模糊效果贴图

如果大家不明白高斯模糊的内部原理,可以参看老师为大家翻译的这篇文章:http://shiba.hpe.sh.cn/jiaoyanzu/WULI/showArticle.aspx?articleId=518&classId=4

第四步:

应用BloomCombine特效,叠加原始场景贴图m_pResolveTarget及两次模糊之后的场景贴图m_pTexture1,从而实现发光效果(m_pResolveTarget+m_pTexture1)

怎么样?大家明白了吗?呵呵~

 

我们来看主体代码:

D3DGame.cpp

 

对Bloom参数体各个成员变量数值进行交叉组合,则我们可以得到一系列不同的Bloom效果:

 

 

 

 

还在羡慕那些专业游戏中美轮美奂的后期特效吗?现在我们也有能力实现了~ 大家赶快动手尝试一下吧 ^ ^

以上,谢谢~

转载请注明原文地址: https://www.6miu.com/read-5035533.html

最新回复(0)