关于学习,中国有句古话叫“学以致用”,可见把学到的东西用于实际实践中是多么的重要。现在学习Direct3D/HLSL的人非常多,教程也非常多。但是很多人不知道看完这些教程后该干什么,或者说可以怎么利用学到的知识。本文会对Direct3D/HLSL做一个简单的介绍,讲述如果将HLSL用于数字图像处理,带领大家一起体会HLSL的强大。
简介.
1)Direct3D和HLSL
Direct3D是微软开发的用于编写Windows下高性能图形程序的3D API。通过Direct3D,我们可以访问高速的图形加速卡。它是DirectX众多成员的一部分。
HLSL 全称High Level Shading Language 。是微软推出Direct3D 9时的一个重要更新。所谓的Shading Language还需要从Direct3D的图形管道说起,Direct3D在Direct3D 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。这个低级的语言就是ASM的Shader。它是类似于汇编语言,难以编写和维护。而HLSL则跟我们熟悉的C/C++语言非常类似。大大降低了开发人员学习的成本。HLSL本身就是微软和nVidia联合开发的,nVidia的版本称为Cg,也就是C for Graphics。可想而知,它和C是有同样的血统的。
本文简要介绍了Direct3D和HLSL,如果读者希望深入了解,请访问MSDN等相关网站。同时关于如何在Direct3D应用程序中使用HLSL编写的Vertex Shader和Pixel Shader,请参阅其它的教程和微软的DirectX SDK。
2)RenderMonkey简介
现在的开发人员可能都比较熟悉IDE的工作模式,尤其是使用Visual Studio一类开发工具的Windows程序开发人员。HLSL作为一种新的语言,GPU编程作为一种新事物,目前还没有很好的IDE能完整的支持编写,调试一体化的工作方式。在本文我们将使用ATI的HLSL开发IDE: RenderMonkey。
RenderMonkey是由前ATI开发的,用于编写Shader,并调试的工具。由于RenderMonkey支持插件,所以既可以编写OpenGL的GLSL也可以编写Direct3D的HLSL。它能支持创建RenderTarget,多Pass渲染,可以自由选择用哪个shader model来编译代码。并能加亮显示shader代码。
经典的RenderMonkey界面如下图
左边为工作区,右边为预览区域。下面为信息输出区。在左边的工作区里可以看到。我们可以对Shader的工程进行分组,其中每一个可以独立工作的工程称为一个Effect。在同一时候预览区中只能预览当前激活的Effect。每个Effect由不同的对象组成,其中比较重要的对象如下:
1) Pass . 这个pass就是渲染中常提到的pass.代表一遍的渲染
2) 几何体。就是类红色茶壶表示的,它代表在渲染中使用的几何体。
3) 纹理对象和RenderTarget对象(用一个铅笔表示)
4) Shader中用到的参数,这些参数可以是自定义的,也可以是预定义的(比如当前的观察矩阵,摄像机的位置等参数)。
5) 每个pass中用到的Shader。这些shader可以在RenderMonkey的代码编辑器中进行编辑,并调用命令来编译。
因为文章篇幅的关系,本文将直接使用RenderMonkey来作为演示的平台。关于如何使用RenderMonkey,请参照RenderMonkey的帮助,或者打开RenderMonkey自带的例子,很容易就能掌握这个工具的使用方法。
GPGPU
本文将要介绍的是如何用HLSL来实现PhotoShop的滤镜效果,也就是说需要通过GPU来进行数字图像处理。这是目前非常流行的GPGPU的应用的一种。
GPU是专用于处理3D图形显示的处理器,虽不如CPU的指令集丰富,但GPU的指令集更加有针对性,因此这就决定了GPU在牺牲了的灵活性的前提上有更快的运行速度。GPU特别适合处理那种可以大规模并行的算法,比如某些数字图像处理算法。
因为目前我们的程序只能通过Direct3D的API才能访问到GPU,一般我们采用Pixel Shader来进行GPGPU,所以要使用GPU来处理数据的时候,必须完成以下几件事:
1).将数据提交给GPU
2).调用对应命令让GPU开始处理数据
3).从GPU哪里取回处理完毕的数据。
可以通过两种方法将数据提交到GPU,纹理和shader的参数,纹理中一般保存我们需要进行处理的数据,而shader参数则一般是用于数据处理算法需要用到的一些参数。
当数据已经准备完毕后,调用Direct3D的drawPrimitive函数在屏幕上绘制一个纹理相同大小的矩形,把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的滤镜效果