现在的位置: 首页 > 综合 > 正文

HLSL编程实现PhotoShop滤镜效果

2013年03月05日 ⁄ 综合 ⁄ 共 4974字 ⁄ 字号 评论关闭

文章为it168稿件.地址为
http://tech.it168.com/n/2007-03-29/200703291522292.shtml

Direct3D提高篇之:HLSL编程实现PhotoShop滤镜效果

潘李亮 2007-3-16

xheartblue@163.com

关于学习,中国有句古话叫“学以致用”,可见把学到的东西用于实际实践中是多么的重要,现在学习Direct3D/HLSL的人非常多,教程也非常多。但是很多人不知道看完这些教程后该干什么,或者说可以怎么利用学到的知识,本文针对已经学习过Direct3D/HLSL的初学者,讲述如果将HLSL用于数字图像处理,带领大家一起体会HLSL的强大。

本文会对Direct3D/HLSL做一个简单的介绍,但是假设读者已经了解和掌握了Direct3D/HLSL的基本知识。

简介.

1)Direct3DHLSL

众所周知,Direct3D是微软开发的用于编写Windows下高性能图形程序的3D API。通过Direct3D,我们可以访问高速的图形加速卡。它是DirectX众多成员的一部分。

HLSL 全称High Level Shading Language . MS推出Direct3D 9时的一个重要更新。所谓的Shading Language还需要从Direct3D的图形管道说起,Direct3DDirect3D 8以前只能工作在固定管道(Fixed Function Pipe-line)的模式下,在固定管道模式下,图元从提交到被转化成可以显示的像素是按照实现定义好的流程和算法来完成,可以认为是固化在硬件中的死功能。

Direct3D 8开始,微软在Direct3D中引入了可编程管道(Programable Function Pipeline)的概念,在可编程管道中,开发人员可以自己编写用于处理顶点和像素的程序,这些程序是运行在GPU上而不是CPU上的。在Direct3D里面,用于处理顶点的程序叫Vertex Shader,用于处理像素的叫Pixel Shader。(目前最新的Direct3D10中又引入了Geometry Shader的概念)。因为硬件的水平在进步,所以可编程管道的处理能力也在不断的提高,根据不同的硬件能力,Shader的版本也已经有对应的不同版本。从Direct3D发布的最早的Shader Model 1.0到现在主流的Shader Model 3.0,可编程管道已经能提供一点范围的通用编程能力了,这就是所谓的GPGPU

从名字上可知,HLSL是一种高级语言(High Level),那么必然有与之对应的Low Level Shading Language,这个低级的语言就是ASMShader。它是类似于汇编语言,难以编写和维护,而HLSL则跟我们熟悉的C/C++语言非常类似。大大降低了开发人员学习的成本。HLSL本身就是微软和nVidia联合开发的,nVidia的版本称为Cg,也就是C for Graphics。可想而知,它和C是有同样的血统的。

本文不是Direct3DHLSL的教程,如果读者觉得以上的概念还比较陌生,请先学习Direct3D的基础知识。同时关于如何在Direct3D应用程序中使用HLSL编写的Vertex ShaderPixel Shader,请参阅其它的教程和微软的DirectX SDK

 

2RenderMonkey简介

现在的开发人员可能都比较熟悉IDE的工作模式,尤其是使用Visual Studio一类开发工具的Windows程序开发人员。在一个统一的开发环境中,可以编写和调试程序。HLSL作为一种新的语言,GPU编程作为一种新事物,目前还没有很好的IDE能完整的支持编写,调试一体化的工作方式。在本文我们将使用ATI的一个相对比较好用的开发HLSLIDE: RenderMonkey

RenderMonkey是由前ATI开发的,用于编写Shader,并调试Shader的一个工具。由于RenderMonkey支持插件,所以RenderMonkey既可以编写OpenGLGLSL也可以编写Direct3DHLSL。它能支持创建RenderTarget,多Pass渲染,可以自由选择用哪个shader model来编译代码。并能加亮显示shader代码。

经典的RenderMonkey界面如下图


左边为工作区,右边为预览区域。下面为信息输出区。在左边的工作区里可以看到。我们可以对Shader的工程进行分组,其中每一个可以独立工作的工程称为一个Effect。在同一时候预览区中只能预览当前激活的Effect。每个Effect由不同的对象组成,其中比较重要的对象如下:

1)       Pass . 这个pass就是渲染中常提到的pass.代表一遍的渲染

2)       几何体。就是类红色茶壶表示的,它代表在渲染中使用的几何体。

3)       纹理对象和RenderTarget对象(用一个铅笔表示)

4)       Shader中用到的参数,这些参数可以是自定义的,也可以是预定义的(比如当前的观察矩阵,摄像机的位置等参数)。

5)       每个pass中用到的Shader。这些shader可以在RenderMonkey的代码编辑器中进行编辑,并调用命令来编译。

因为文章篇幅的关系,也不采用编写Direct3D程序加载HLSL的方式来做演示程序,而是直接使用RenderMonkey来作为演示的平台。关于如何使用RenderMonkey,请参照RenderMonkey的帮助,或者打开RenderMonkey自带的例子,很容易就能掌握这个工具的使用方法。

 

GPGPU

         本文将要介绍的是如何用HLSL来实现PhotoShop的滤镜效果,也就是说需要通过GPU来进行数字图像处理。这是目前很流行的GPGPU的应用的一种。

我们知道,GPUCPU的工作方式和用途都是不同的,CPU是通用的处理器,而GPU是专用于处理3D图形显示的,因此CPU的指令集更加丰富,而GPU的指令集更加有针对性,因此这就决定了GPU在牺牲了CPU的灵活性的前提上有更快的运行速度。GPU特别适合处理那种可以大规模并行的算法,比如某些数字图像处理算法。

因为目前我们的程序只能通过Direct3DAPI才能访问到GPU,一般我们采用Pixel Shader来进行GPGPU,所以我们要使用GPU来处理数据的时候,必须完成以下几件事:

1).将数据提交给GPU

2).调用对应命令让GPU开始处理数据

3).GPU哪里取回处理完毕的数据。

我们可以通过两种方法将数据提交到GPU,纹理和shader的参数,纹理中一般保存我们需要进行处理的数据,而shader参数则一般是用于数据处理算法需要用到的一些参数。当然这也不是绝对的。

当数据已经准备完毕后,我们调用Direct3DdrawPrimitive函数在屏幕上绘制一个纹理相同大小的矩形,把GPGPU的算法写到用于绘制这个矩形的Pixel Shader中。当Direct3D开始绘制这个矩形以后,会为每一个象素调用一次整个Pixel Shader,然后把Pixel Shader的输出写入到RenderTarget中,因为我们绘制的矩形的大小和纹理的大小是一致的,所以输出象素和纹理的象素可以做到一一对应的关系,也就是说纹理中的每一个象素在经过Pixel Shader的运算后被输出掉RenderTarget里,等于对这个数据调用了一次我们需要的算法。我们知道现代的GPU中往往有大量的Pixel Shader处理单元,而这些处理都是可以并行运行的,可想而知,这个处理是非常快速的。

经过前面的步骤,处理完的数据已经到了RenderTarget里了,我们可以事先自己创建一个RenderTarget(通常和输入纹理等大)来接受步骤2中的数据,然后Lock这个RenderTarget取回数据。也可以在步骤二中直接把图象绘制到屏幕上,通过Capture屏幕来得到输出(对于图象处理也够了,就是速度慢,而且显得非常傻)。

GPGPU简单介绍到这里。详细的GPGPU资料请参考www.gpgpu.org 同时nvidia的网站和发布的SDK上也有很多关于GPGPU的例子。

接下来我们使用RenderMonkey来搭建一个用于数字图像处理的架子,以实现类似PhotoShop的滤镜效果

 

RenderMonkey图像处理的架子-图像黑白化

下面我们通过一个简单的例子,先来完成一个最简单的图像处理-把图像黑白化。来说明RenderMonkey如何来处理数字图像。

Render MonkeyVC类似,内置了一些工程代码。在这里我们在RenderMonkey的工作区菜单里选择Add Effect -> DirectX->Screen-AlignedQuad. 在生成的工程中,我们看到RenderMonkey为我们显示了一个默认的图片,首先我们就是要修改这个图片,我们双击那个base图片对应的节点,选择一个我们要演示的图片。如下图。

接下来,我们要开始进行我们关键的一步,编写处理图像的算法,我们双击刚才建立的项目中的single  pass -> pixel shader . 开始编辑Pixel Shader的代码。

我们知道,一个RGB颜色的亮度和各个分量之间的关系的公式为:

       GrayValue = 0.3 * R + 0.59*G  + 0.11 *B

根据这个公式,我们的代码如下:


sampler2D Texture0;

float4  main( float2 texCoord  : TEXCOORD0 ) : COLOR

{

   float4 _inColor = tex2D( Texture0, texCoord );

   float h = 0.3 * _inColor.x + 0.59 * _inColor.y + 0.11* _inColor.z;

   float4 _outColor = float4(h,h,h,1.0);

   return _outColor;

}

 

 

 

 

 

 

 

 

 


我来详细的解释一下这个Pixel Shader,首先我们定义的的sampler2D Texture0Texture0就是代表我们输入的图像。这个图像在RenderMonkey的工作区中用两部分表示,首先需要在工作区中创建一个图像对象bas,然后需要在用到这个纹理图像的pass中创建一个纹理对象Texture0,然后让这个Texture0指向我们刚才创建的纹理图像bas,读者应该注意到了纹理对象的名字就是我们Shader里的sampler2D变量的名字,不错,RenderMonkey就是以这种方法把shader代码中的变量名字和工作区中的对象关联起来.不光纹理如此,其它的float4/float3/float2/float变量都如此.

接下来的main函数中,我们通过纹理采样的方式得到当前需要绘制的像素,float4 _inColor = tex2D( Texture0, texCoord ); 也就是输入的颜色。得到输入颜色后,我们可以通过上面给出的公式来计算出这个颜色的灰度值,并用这个值构造一个灰度颜色返回给Direct3D。系统就会把这个颜色作为最终的色彩显示在窗口中,也就是得到一个黑白的图像。最终结果如下图:

图:图像去色效果

(注:这个例子是最简单的HLSL用于图像处理的例子,如果读者觉得到目前为止还很有难度,建议重新温习一遍Direct3DHLSL的知识)。

通过这个例子,我们已经基本了解了RenderMonkey处理图像的步骤和流程,下面我们通过分析一些更加复杂一点的例子来体会HLSL的强大能力

 

l   入门效果之浮雕

  "浮雕"图象效果是指图像的前景前向凸出背景。常见于一些纪念碑的雕刻上,要实现浮雕其实非常简单。我们把图象的一个象素和左上方的象素进行求差运算,并加上一个灰度。这个灰度就是表示背景颜色。这里我们设置这个插值为128 (图象RGB的值是0-255)。同时,我们还应该把这两个颜色的差值转换为亮度信息.否则浮雕图像会出现彩色J

在使用HLSL处理浮雕效果的时候,两个问题我们需要注意一下。

其中一个图象边界,写过C++实现浮雕效果的朋友都知道,在处理

抱歉!评论已关闭.