欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

bloom(转)

发布时间:2025/5/22 编程问答 44 豆豆
生活随笔 收集整理的这篇文章主要介绍了 bloom(转) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

转载:http://www.cnblogs.com/kenkao/archive/2011/08/25/2153752.html

感谢原创者。

 

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

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

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

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

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

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

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

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

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

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

 

BloomExtract.fx /*-------------------------------------

代码清单:BloomExtract.fx
来自:http://create.msdn.com/en-US/(AppHub)

-------------------------------------*/

//提取原始场景贴图中明亮的部分
//这是应用全屏泛光效果的第一步

sampler TextureSampler : register(s0);

floatBloomThreshold;

float4 ThePixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
    //提取原有像素颜色
float4 c =tex2D(TextureSampler, texCoord);

    //在BloomThreshold参数基础上筛选较明亮的像素
returnsaturate((c -BloomThreshold) /(1-BloomThreshold));
    
}

technique BloomExtract
{
    pass Pass1
    {
        PixelShader =compile ps_2_0 ThePixelShader();
    }
}

 

GaussianBlur.fx /*-------------------------------------

代码清单:GaussianBlur.fx
来自:http://create.msdn.com/en-US/(AppHub)

-------------------------------------*/

//高斯模糊过滤
//这个特效要应用两次,一次为横向模糊,另一次为横向模糊基础上的纵向模糊,以减少算法上的时间复杂度
//这是应用Bloom特效的中间步骤

sampler TextureSampler : register(s0);

#defineSAMPLE_COUNT 15

//偏移数组
float2 SampleOffsets[SAMPLE_COUNT];
//权重数组
floatSampleWeights[SAMPLE_COUNT];

float4 ThePixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 c =0;
    
    //按偏移及权重数组叠加周围颜色值到该像素
    //相对原理,即可理解为该像素颜色按特定权重发散到周围偏移像素
for(inti =0; i <SAMPLE_COUNT; i++)
    {
        c +=tex2D(TextureSampler, texCoord +SampleOffsets[i]) *SampleWeights[i];
    }
    
    returnc;
}

technique GaussianBlur
{
    pass Pass1
    {
        PixelShader =compile ps_2_0 ThePixelShader();
    }
}

 

BloomCombine.fx /*-------------------------------------

代码清单:BloomCombine.fx
来自:http://create.msdn.com/en-US/(AppHub)

-------------------------------------*/

//按照特定比例混合原始场景贴图及高斯模糊贴图,产生泛光效果
//这是全屏泛光特效的最后一步

//模糊场景纹理采样器
sampler BloomSampler : register(s0);

//原始场景纹理及采样器定义
texture BaseTexture;
sampler BaseSampler =sampler_state
{
    Texture   =(BaseTexture);
    MinFilter =Linear;
    MagFilter =Linear;
    MipFilter =Point;
    AddressU  =Clamp;
    AddressV  =Clamp;
};

floatBloomIntensity;
floatBaseIntensity;

floatBloomSaturation;
floatBaseSaturation;

//减缓颜色的饱和度
float4 AdjustSaturation(float4 color, floatsaturation)
{
    //人眼更喜欢绿光,因此选取0.3, 0.59, 0.11三个值
floatgrey =dot(color, float3(0.3, 0.59, 0.11));
    returnlerp(grey, color, saturation);
}

float4 ThePixelShader(float2 texCoord : TEXCOORD0) : COLOR0
{
    //提取原始场景贴图及模糊场景贴图的像素颜色
float4 bloom =tex2D(BloomSampler, texCoord);
    float4 base=tex2D(BaseSampler, texCoord);
    
    //柔化原有像素颜色
bloom =AdjustSaturation(bloom, BloomSaturation) *BloomIntensity;
    base=AdjustSaturation(base, BaseSaturation) *BaseIntensity;
    
    //结合模糊像素值微调原始像素值
base*=(1-saturate(bloom));
    
    //叠加原始场景贴图及模糊场景贴图,即在原有像素基础上叠加模糊后的像素,实现发光效果
returnbase+bloom;
}

technique BloomCombine
{
    pass Pass1
    {
        PixelShader =compile ps_2_0 ThePixelShader();
    }
}

 

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

 

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

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

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

 

boolCTexture2D::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))
        returnfalse;
    m_Width  =SizeX;
    m_Height =SizeY;
    m_SurRect.top    =0;
    m_SurRect.left   =0;
    m_SurRect.right  =m_Width;
    m_SurRect.bottom =m_Height;
    returntrue;
}

boolCTexture2D::GetRenderSurface(IDirect3DSurface9**pOutSurface)
{
    //获得贴图的渲染表面
HRESULT hr;
    hr =m_pTexture->GetSurfaceLevel(0, pOutSurface);
    if(FAILED(hr))
        returnfalse;
    else
        returntrue;
}

 

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

 

/*-------------------------------------

代码清单:BloomEffect.h
来自:http://www.cnblogs.com/kenkao

-------------------------------------*/

#include "D3DEffect.h"
#include "Texture2D.h"

#pragmaonce

//Bloom参数体
typedef struct_BloomParam
{
    char*_Name;              //Bloom特效名称
float_BloomThreshold;    //饱和度
float_BlurAmount;        //模糊程度
float_BloomIntensity;    //模糊剧烈度
float_BaseIntensity;     //原始剧烈度
float_BloomSaturation;   //模糊饱和度
float_BaseSaturation;    //原始饱和度
_BloomParam(){}
    _BloomParam(char*name, floatbloomThreshold, floatblurAmount,
        floatbloomIntensity, floatbaseIntensity,
        floatbloomSaturation, floatbaseSaturation)
    {
        _Name =name;  _BloomThreshold =bloomThreshold; _BlurAmount =blurAmount;
        _BloomIntensity =bloomIntensity;   _BaseIntensity =baseIntensity;
        _BloomSaturation =bloomSaturation; _BaseSaturation =baseSaturation;
    }

}BloomParam;

classCBloomEffect
{
public:
    CBloomEffect(void);
    ~CBloomEffect(void);
public:
    voidCreate();       //创建Bloom特效
voidRelease();      //释放Bloom特效
voidDrawScene();    //场景绘制
voidBegin();        //开启Bloom
voidEnd();          //关闭Bloom
public:
    voidSetParam(BloomParam*pParam){m_pParam =pParam;}  //参数体设置
BloomParam*GetParam()                  {returnm_pParam;}    //参数体获取
private:
    voidPostScene();                   //Bloom场景投递
voidPostSurface(                   //应用特效投递贴图到特定缓存表面,本质就是将一个贴图应用Shader之后的效果写入另一个贴图
CTexture2D*pTexture,          //后台纹理
IDirect3DSurface9*pSurface,   //渲染表面
CD3DEffect*pEffect);          //目标特效
boolLoadContent();                 //加载内容
boolGetSurfaces();                 //获取渲染表面
private:
    floatComputeGaussian(floatn);                 //计算高斯模糊参数
voidSetBlurEffectParam(floatdx, floatdy);   //计算偏移数组及权重数组
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.cnblogs.com/zengqh/archive/2012/10/09/2716447.html

总结

以上是生活随笔为你收集整理的bloom(转)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。