PS:自己翻译的,转载请著明出处
在XNA中对Sprites使用HLSL
Aaron T Foley
目的
这篇教程深入研究如何对XNA中的精灵使用HLSL。
导言
因为游戏变的越来越复杂,老游戏有时对新的玩家失去它们的吸引力,正因为它们缺乏影响。所以通过执行HLSL在我们的2D游戏里,我们可以创建一些惊人的视觉效果的2D游戏。
第一步:创建一个新的工程并且包含艺术品
为了开始我们需要去创建一个新的工程。
为了创建一个新的工程
1.打开XNA游戏的Studio Express
2.点击File菜单,然后点击New Project去创建一个新的项目
3.从出现的模板表单中,选择Windows Game 或者Xbox360 Game
4.为你的游戏在Name区域输入一个名字,选择一个路径你想要这个游戏文件保存在你指定的地方
5.点击OK
为了添加艺术品到你的项目中
1.确保你可以看见你项目的Solution Explorer在窗口的右部。如果你不能看见它,点击View菜单,并且稍后点击Solution Explorer.当它出现时,你会看见文件在你工程的树状结构结合在一起了。
2.右-击Content文件夹在Solution Explorer,点击Add,然后点击Existing Item.使用出现的对话框,浏览到你艺术品所在位置的路径。选择你的两个纹理。如果你不能看见这个纹理,确保你改变了文件的选择框盒子的Files of type,去读取Content Pipeline Files,点击OK.
一旦你完成你应该做的就象这里。
第二步:初始化
现在好了,我们有我们的工程设置,让我们得到我们主要的游戏类的设置。首先让我们通过声明我们的变量。为了这个例子我们装备使用一个Texture2D,它代表我们之前加载的内容。为了实现这个,到我们的Game1类的声明区域,添加下面的代码在GraphicsDeviceManager和SpriteBatch的后面。
2 float Intensity = 0.5f;
3 Effect myEffect;
现在,我们有我们的基本声明,我们需要去初始化我们的图象并且加载某些东西到它里面。最好在我们的Game1类的LoadContent()方法中去做这个。添加下面的代码行。
2 myEffect = Content.Load<Effect>("hlsl");
3 myEffect.Parameters["Intensity"].SetValue(Intensity);
好吧,我们设置我们图象和effect的这两个声明,在我们的Content目录中使用任何我们有的文件。你提供给这里的资源名字是你给你的文件名的名字一样,它是你之前添加到Content文件夹中的文件。
注意
默认的当我们添加在content它的名字成为没有扩展名的文件。
正如你看见的我们使用一个涉及的资源名称,它还不存在呢。在目前这是正常的;我们马上就会得到。目前我们将使用它去设置所有我们的基础代码,这样我们可以把精力集中在后面的HLSL文件上。
同样我们可以使用myEffect去设置一个没有我的HLSL文件的参数。任何参数我们想传递到HLSL文件中,我们可以用以后使用这个方法来实现它。简单的输入相同的参数名,在HLSL文件中,并且设置我们需要的值。
现在我们拥有一切初始化,让我们为我们的更新添加代码。
第三步:Update方法
现在我们将继续到我们的更新方法中。这里我们将添加一对键盘和手柄命令去调整我们的动态效果,在程序运行的时候。首先我喜欢添加一些多的代码,这样Windows用户可以退出这个程序使用键盘的Escape键。然后我添加到键里去调整Intensity(译者:强度)的大小。这里是完整的Update()方法的样子。
2 {
3 // Allows the game to exit
4 if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
5 {
6 Exit();
7 }
8 if (Keyboard.GetState().IsKeyDown(Keys.Q) || GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
9 {
10 Intensity = Intensity - 0.01f;
11 myEffect.Parameters["Intensity"].SetValue(Intensity);
12 }
13 if (Keyboard.GetState().IsKeyDown(Keys.E) || GamePad.GetState(PlayerIndex.One).Buttons.B == ButtonState.Pressed)
14 {
15 Intensity = Intensity + 0.01f;
16 myEffect.Parameters["Intensity"].SetValue(Intensity);
17 }
18 base.Update(gameTime);
19 }
正如你看见的,依赖这个键压下我们调整Intensity上或下通过0.01,然后使用这个与前面相同的方法 去设置Intensity在我们的HLSL文件中。
第四步:绘制方法
现在我们有我们的Update()和LoadContent()方法,让我们跳入我们的最终的Game1类的代码中。
2 {
3 graphics.GraphicsDevice.Clear(Color.Black);
4 spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate,SaveStateMode.None);
5 myEffect.Begin();
6 myEffect.CurrentTechnique.Passes[0].Begin();
7 spriteBatch.Draw(myImage, new Vector2(0.0f, 0.0f),Color.White);
8 myEffect.CurrentTechnique.Passes[0].End();
9 myEffect.End();
10 spriteBatch.End();
11 }
正如你看见,让我们从上部开始。首先我们设置每一祯我们的鲜明的色彩并且清除背景。这里我使用Black去代替CornflowerBlue,因为我想我将使用它有非常动态的效果。接下来,我们设置我们的SpriteBatch开始收集我们要绘制的信息。为了HLSL去工作在SpriteBatch中,我们需要确保我们使用SpriteSortMode.Immediate在我们的Begin()参数,否则我们的效果不会显示出来。这将导致batch应用新的图形设备设置,并且在每次Draw()调用上绘制所有的精灵。接下来我们调用我们的Begin()方法为我们的Effect。任何我们的Effect在Begin()和End()方法之间将会被HLSL文件影响。接下来我们设置我们想要从我们的HLSL文件中使用它的pass。然后我们绘制我们的精灵到屏幕上。这里我刚才做了一些基础的事情。我把这个精灵放在屏幕的(0,0)并且使用白色作为它的颜色。最后我们End()我们的pass,然后End()我们的Effect,End()我们的spriteBatch.现在我们有一个完成的Draw()方法,让我继续我们的HLSL文件。
第五步:HLSL 文件(.FX)
接下来我们需要去创建我们将要使用的基础HLSL文件,这个创建类似于我们之前在content内添加。To add an Effect file(.FX)to your project(为了添加一个Effect文件到你的项目中)
1.确保你可以看见窗口的右边的你项目的Solution Explorer。如果你不能看见它,点击View菜单,然后点击Solution Explorer.当它出现,你将会可那件文件结合在你的树型结构中。
2.右-击在Solution Explorer的Content文件夹,点击Add,然后点击New Item.使用这个出现的对话框,选择模板的Effect File.命名这个Efect File不管你叫它什么作为你前面资源的名字在你的项目中,当你初始这个myEffect变量。点击OK。
现在我们应该有我们的基础Effect File。这里应该给你有大量的代码,所以只有全部选择并且删除它。我们将从头开始学,去了解做更多的事情。
就象所有的C程序语言,我们需要一个起始点。所以让我们看起始点象这样。
2 {
3 pass p0
4 {
5 PixelShader = compile ps_2_0 Brightness();
6 }
7 }
接下来我们声明这个pass将会被用一个PixelShader和它将会被编译,它的Shader Model版本我们将会使用(在这种情况下是2.0),象素着色器的功能名字将会被使用(在Brightness()的情况下)。
接下来我们需要去声明我们将会使用的变量,我们的象素着色器的Brightness()功能。所以我们返回文件的上部并且添加下面的代码。
2 sampler2D clrSampler;
3 float4 Brightness(float2 Tex : TEXCOORD0) : COLOR0
4 {
5 float4 Color;
6 Color = tex2D(clrSampler, Tex.xy) * Intensity;
7 return Color;
8 }
接下来,我们有我们的Brightness()函数。正如看见的,它有一个返回值是float4,意思是它将传回四个floats和所有方式的结尾,我们看到冒号后面的是叫做句法。这个句法基本上是一个这个函数的模板。所以在这种情况下我们使用这个颜色数据在寄存器0的模板功能。我们使用寄存器0,因为此时我们不能使用多个纹理,所以默认的所有信息传入到我们的HLSL文件里,是在寄存器0中。现在在我们的参数里,我们看见一些相似的地方。这里我们有一个float2,用TEXCOORD0的句法命名的Tex。所以意思我们通过使用在寄存器0中(X,Y)坐标收集纹理信息。
现在,我们有我们的函数声明,让我们进入函数中。首先让我们声明一个变量去保存我们的新颜色信息。然后我们通过使用tex2D()功能去设置这个信息。这个函数使用我们前面定义的取样器,去得到从我们的纹理得到的纹理信息。这个信息是通过使用(点)扩展提取出来。从我们的坐标的xy.然后通过我们的Intensity乘以它。
因此,在本质上是这样做的...
假设我们在纹理的(0,0)位置,我们用一个颜色(100,100,100,255)象素.它接收这个信息并且然后转换它成一个从0到1的值。所以它接收Red值并且找到它的值是100/255或者0.39。它与我们其他的值相同。然后它接收我们的Intensity变量并且乘以它自己的每一部分。所以我们说Intensity是0.5。意思我们的Red值是(100/255)*0.5=0.39*0.5=0.195。它在0到255范围内可能是49.725。
这是所有的代码。编译它,试一试看它做了些什么。这仅仅是HLSL的冰山一角,还有许多不同的效果。你可以通过改变a couple of things去实现它。
下面是一组有趣的效果。
Brightness
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy) * Intensity;
5 return Color;
6 }
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy) * Tex.y;
5 return Color;
6 }
Left fade
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy) * Tex.x;
5 return Color;
6 }
Top/Left fade
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy) * Tex.y * Tex.x;
5 return Color;
6 }
Color hues
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy);
5 Color.g *= Intensity;
6 return Color;
7 }
Color glow
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy);
5 Color.g /= Intensity;
6 return Color;
7 }
Multi Color glow
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy);
5 Color.rg /= Intensity;
6 return Color;
7 }
Scaling
2 {
3 float4 Color;
4 Tex.xy *= Intensity;
5 Color = tex2D(clrSampler, Tex.xy);
6 return Color;
7 }
Panning
2 {
3 float4 Color;
4 Tex.xy += Intensity;
5 Color = tex2D(clrSampler, Tex.xy);
6 return Color;
7 }
Fishbowl
2 {
3 float4 Color;
4 Tex.xy = Tex.xy + (sin(Tex.xy * Intensity)* 0.1);
5 Color = tex2D(clrSampler, Tex.xy);
6 return Color;
7 }
Note
这很难说一个单独的屏幕截图。尝试下,并且来回调整强度。
Blur
2 {
3 float4 Color;
4 Color = tex2D(clrSampler, Tex.xy);
5 Color += tex2D(clrSampler, Tex.xy + (0.01));
6 Color += tex2D(clrSampler, Tex.xy + (0.02));
7 Color += tex2D(clrSampler, Tex.xy + (0.03));
8 Color = Color / 4;
9
10 return Color;
11 }
Grayscale
2 {
3 float4 Color;
4 Color.a = 1.0f;
5 Color = tex2D( clrSampler, Tex.xy);
6 Color.rgb = (Color.r + Color.g + Color.b) / Intensity;
7 return Color;
8 }
Negative
2 {
3 float4 Color;
4 Color = 1 - tex2D(clrSampler, Tex.xy);
5 Color.a = 1.0f;
6 return Color;
7 }
结论
正如你看见的这里使用精灵的HLSL有无限的可能。只要想下所有可与2D游戏也做的事情。我希望本教程可以帮助你。如果任何人有任何疑问,枪毙我的电子邮件在slyprid@sbcglobal.net
源代码:http://www.ziggyware.com/readarticle.php?article_id=147
(完)