zoukankan      html  css  js  c++  java
  • bloom特效

          由于之前在各种场合看到别人贴出的bloom特效做的图片,一开始还以为是用的HDR技术,后来一研究才发现绝大部分都仅仅是一个bloom特效而已,遂打算学习一番。其实bloom是一个非常简单的后期图像处理过程,之所以称其为图像处理过程,是因为它是一种可以在图片生成完毕后再使用的后处理过程。那么它到底是什么样的一种过程呢?简单地说就是:

    Step1. 先对图片每一像素点进行一个亮度值检测,若大于某一个阈值就保留其原始颜色值,否则置为黑色;

    Step2. 对上一步结果做高斯模糊;

    Step3. 将模糊后的图片和原图片做一个加权和(权值视具体情况而定);

    这里面涉及到的几个关键知识点有必要简单地说一下:

    1.所谓一个像素的亮度值是什么?

    亮度值并不是(R+G+B)/3,而是另外一种颜色格式(YUV颜色格式)中的Y值,YUV格式是欧洲电视系统所采用的一种颜色编码方式,其中"Y"表示明亮度,"U"和"V"表示色彩和饱和度。YUV格式在图像处理中运用广泛,比如大家耳熟能详的灰度图其实就是存放的每个像素对应的"Y"值。YUV和RGB之间有一个线性的转换关系:

    Y=0.299R+0.587G+0.114B;

    U=-0.147R-0.289G+0.436B;

    V=0.615R-0.515G-0.100B;

    可见G的值对亮度的贡献最大,而B贡献最小,这也符合人类视觉系统对于亮度的敏感程度值,因此Step1中的亮度值检测实际就是判断(0.299R+0.587G+0.114B)是否大于一个既定阈值。

    2.高斯模糊是什么?

    图像模糊的原理很简单,就是一张图片中每个像素都取周边一定范围内像素的加权和,最简单的权值设置方法就是均匀求权,即每个像素是这个范围内所有像素的平均值,如果范围是3*3,那么权值矩阵为:,而高斯模糊则是利用二维正态分布来生成这个权值矩阵,比如设二维正态分布函数为G(x,y),我们可以让矩阵正中心那个点值为G(0,0),中心偏左的点为G(-1,0),以此类推,3*3的权值矩阵可以是:。当然,这里只是随便举个例子,实际运用中肯定会比这个复杂得多,因为还要涉及到方差σ的选取等问题。实际上高斯模糊假设了在图像空间中像素的变化是缓慢的,因此相邻像素之间的变化不会太明显,但是随机的两个像素间就可能形成很大的像素差,这样也使高斯模糊能在保留信号的条件下减小噪声。但是这种方法在接近物体边缘的时候就无效了,因为图像中物体边缘两个点的颜色差值往往会比较大,所以高斯模糊会把边缘平滑掉。而另一种双边滤波方式则不会把边缘磨平,但是在bloom中,恰恰相反,我们希望得到一种看起来"颜色外溢"的效果,所以高斯模糊是个理想的选择。

          只要对以上两点了解后,编写一个bloom特效程序简直就是分分钟的事了,下面是我用OPENCV写的一个对图片进行bloom处理的简单程序:

    #include <cv.h>
    #include <highgui.h>

    #define THRESHOLD_COLOR 0x38

    #define A_VALUE 1.0f

    #define B_VALUE 1.0f

    IplImage *img;

    int main(int argc,char** argv) {

     IplImage *img = cvLoadImage("chinesedragon.jpg"); 

     cvNamedWindow("Image-in",CV_WINDOW_AUTOSIZE); 

     //先显示原jpg图 

     cvShowImage("Image-in",img);     // 灰度化

     IplImage *imggrey=cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1); 

     cvCvtColor(img,imggrey,CV_BGR2GRAY);

     cvShowImage("Image-grey",imggrey); 

     IplImage *tmp=cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3); 

     cvZero(tmp);

     for (int h=0;h<tmp->height;h++)  { 

      uchar *p=(uchar*)(imggrey->imageData+h*imggrey->widthStep); 

      uchar *ptr_s = cvPtr2D(img, h, 0, NULL);

      uchar *ptr_d = cvPtr2D(tmp, h, 0, NULL);

      for (int w=0;w<tmp->width;w++)   { 

        if (p[w] >= THRESHOLD_COLOR)    {

        ptr_d[w*3] = ptr_s[w*3];

        ptr_d[w*3+1] = ptr_s[w*3+1];

        ptr_d[w*3+2] = ptr_s[w*3+2];

        }        

      }     

    }

     cvShowImage("Image-threshold",tmp); 

     //分配空间存储处理后的图像 

     IplImage *blur=cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3); 

     cvSmooth(tmp,blur,CV_GAUSSIAN,7,7); 

     cvShowImage("Image-gaussianblur",blur); 

     // 图像相加

     for (int h=0;h<tmp->height;h++)  { 

      uchar *ptr_s = cvPtr2D(img, h, 0, NULL);

      uchar *ptr_d = cvPtr2D(blur, h, 0, NULL);

      for (int w=0;w<tmp->width;w++)   {

        unsigned int r1 = A_VALUE * ptr_s[w*3] + B_VALUE * ptr_d[w*3];// * ptr_d[w*3] / 0xff;

        if (r1 > 0xff) r1 = 0xff;

        ptr_d[w*3] = r1;

        unsigned int r2 = A_VALUE * ptr_s[w*3+1] + B_VALUE * ptr_d[w*3+1];// * ptr_d[w*3+1] / 0xff;

        if (r2 > 0xff) r2 = 0xff;

        ptr_d[w*3+1] = r2;

        unsigned int r3 = A_VALUE * ptr_s[w*3+2] + B_VALUE * ptr_d[w*3+2];// * ptr_d[w*3+2] / 0xff;

        if (r3 > 0xff) r3 = 0xff;

        ptr_d[w*3+2] = r3;

      }  

    }

     cvShowImage("Image-bloom",blur); 

     //清除垃圾 

     cvReleaseImage(&imggrey); 

     cvReleaseImage(&img); 

     cvReleaseImage(&tmp);  

     cvWaitKey(); 

     //销毁窗口

     cvDestroyWindow("Image-in");  

     cvDestroyWindow("Image-grey"); 

     cvDestroyWindow("Image-threshold"); 

     cvDestroyWindow("Image-gaussianblur");

     cvDestroyWindow("Image-bloom");

     return 0;

    }

           最后是我用这段程序对我以前渲染的中国龙进行bloom后的效果(下面两张图分别是bloom效果图和原始图片):

    后记:我发现在很多游戏中阈值似乎设置的是0(也可能是它们用了更为复杂的方法),这样可以避免刚好处于阈值两端的像素点间颜色不连续的问题。

    在网上有一篇讲bloom和hdr区别的帖子中(可参考http://game.ali213.net/thread-2075708-1-1.html)用了一幅毁灭战士3中的图片,下面是效果比较

    (1:游戏中未开启bloom的效果,2:游戏中开启bloom的效果,3:图1经过我的程序跑出来的bloom效果(阈值设为0,A,B两个value都设为1.0))

     

     

    可以看出游戏中的泛光现象更加明显,可能是它用了更宽范围的高斯模糊(或类似算法)。

  • 相关阅读:
    IE6IE9兼容性问题列表及解决办法总结
    正则表达式笔记:提取C#代码中的中文信息,双引号,尖括号间的信息
    IE6IE9兼容性问题列表及解决办法_补遗漏之一:button的type默认值改变为submit了。
    IE6IE9兼容性问题列表及解决办法_补充之三:Table的Col不再支持style及align,需要到th, td中去设置
    再谈IE的浏览器模式和文档模式
    WebRequest, WebResponse , HttpWebResponse, HttpWebResponse , WebClient笔记
    IE6IE9兼容性问题列表及解决办法_补遗漏之二:CSS区分大小写,Style中height需要加px
    VS2010的智能提示没有了的可能原因
    IE6IE9兼容性问题列表及解决办法_补充之四:HTC (Html Components) 功能逐渐被IE抛弃
    LINQ to XML 笔记,确实比DOM好用,方便。
  • 原文地址:https://www.cnblogs.com/starfallen/p/3006481.html
Copyright © 2011-2022 走看看