[翻译]XNA外文博客文章精选之ten


PS:自己翻译的,转载请著明出处

                                                                         2D光折射
                                 你看见那些用来隔离开空间的玻璃砖块了吗?或许可以看透这个玻璃?在两种情况下你会注意到当你看到另一边的东西时,他们的外观已经扭曲了。这被称为折射,是通过这个物体时光的弯曲并被改变了方向所导致的。在教程中我们将创建一个折射来影响一个2D游戏。
                                 在这个教程中,你需要一些文件,你可以在源代码中提取。在那个.zip文件里你会找一个砖块墙纹理,一个普通的图片的砖块墙,一个冰雪山的图象,它被我们用来作为背景。
                                 实现折射过程是非常类似于执行2D正规映射的方法。所以读者可以读下那个教程就会相当熟悉这里的处理过程。如果你没有读过2D正规影射教程,不要着急,我们在这里会十分彻底的学习它。
                                 2D折射效果的基本是用某部分去绘制屏幕,并且执行一个象post-process效果的折射。我们将使用三个绘制目标对象来达到这个效果。
                                 第一个渲染目标将会是我们的折射目标。我们将绘制任何我们想折射到的任何图象。对于我们的教程,它刚好是我们的冰雪山图象,但在实际的使用中,它可以是任何的图象。
                                 第二个渲染目标将会是我们的标准图对象。这里我们将绘制所有对象的标准图,我们将可能为了折射使用它们。
                                 第三个渲染目标将会是我们的纹理目标对象。这里我们将绘制我们的折射对象的纹理。这将让我们有半透明对象,使背景发生了折射同时仍然保持成一个纹理。
                                 现在,让我们开始吧。在XNA Game Studio创建一个新游戏项目并且添加下面的文件到你的游戏类中:
1 Texture2D glacier;
2 Texture2D spriteDiffuse, spriteNormal;
3 Rectangle spriteDest  = new Rectangle(100100300300);
4 RenderTarget2D refractionTarget, normalTarget, diffuseTarget;
5 Effect refractionEffect;
6 SpriteBatch batch;
                                为我们的背景和我们的精灵,我们有少数的纹理。我们有一个矩形为了绘制精灵。我们有我们的三个绘制目标对象就象上面所描述的。我们有它本身的实际效应。我们还有SpriteBatch去处理绘图。
                                让我们进入到LoadGraphicesContent方法中:
 1 protected override void LoadGraphicsContent(bool loadAllContent)
 2 {
 3     if (loadAllContent)
 4     {
 5         batch = new SpriteBatch(graphics.GraphicsDevice);
 6         glacier = content.Load<Texture2D>(@"Content/glacier");
 7         spriteDiffuse = content.Load<Texture2D>(@"Content/rockwall");
 8         spriteNormal = content.Load<Texture2D>(@"Content/rockwall_normal");
 9         //refractionEffect = content.Load<Effect>(@"Content/Refraction");
10         refractionTarget = new RenderTarget2D(
11             graphics.GraphicsDevice,
12             graphics.GraphicsDevice.Viewport.Width,
13             graphics.GraphicsDevice.Viewport.Height,
14             1,
15             SurfaceFormat.Color,
16             graphics.GraphicsDevice.PresentationParameters.MultiSampleType,
17             graphics.GraphicsDevice.PresentationParameters.MultiSampleQuality);
18         normalTarget = new RenderTarget2D(
19             graphics.GraphicsDevice,
20             graphics.GraphicsDevice.Viewport.Width,
21             graphics.GraphicsDevice.Viewport.Height,
22             1,
23             SurfaceFormat.Color,
24             graphics.GraphicsDevice.PresentationParameters.MultiSampleType,
25             graphics.GraphicsDevice.PresentationParameters.MultiSampleQuality);
26         diffuseTarget = new RenderTarget2D(
27             graphics.GraphicsDevice,
28             graphics.GraphicsDevice.Viewport.Width,
29             graphics.GraphicsDevice.Viewport.Height,
30             1,
31             SurfaceFormat.Color,
32             graphics.GraphicsDevice.PresentationParameters.MultiSampleType,
33             graphics.GraphicsDevice.PresentationParameters.MultiSampleQuality);
34     }
35 }
                              你将会看见我们创建了SpriteBatch和加载了我们的纹理。目前我只有注释下我们的折射效果加载的行,因为我们还没有一个折射FX文件。然后我们刚刚创建了我们的三个绘制目标对象去与我们的屏幕大小相配。
                              现在,我们可以进入我们的绘图代码。正如开始所解释的,我们绘制出了三个绘制目标对象,然后用一个着色器结合它们。我们将开始首先绘制我们的折射对象。添加下面的方法到你的游戏类中:
 1 private void RenderRefractionMap()
 2 {
 3     graphics.GraphicsDevice.SetRenderTarget(0, refractionTarget);
 4     graphics.GraphicsDevice.Clear(Color.TransparentBlack);
 5     batch.Begin();
 6     batch.Draw(glacier, new Rectangle(00, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height),Color.White);
 7     batch.End();
 8     graphics.GraphicsDevice.ResolveRenderTarget(0);
 9     graphics.GraphicsDevice.SetRenderTarget(0null);
10 }

                              这是一个相当基础的方法。我们设置并且清除这个绘制目标对象。然后我们全屏绘制冰雪山纹理。然后我们解决这个呈现的目标对象并且不再设置它。如果你想要,你可以投入更多的绘制在这里,我们可以通过我们的精灵得到折射,但是目前我们只会使用冰雪山。
                              接下来,我们必须去绘制出我们的精灵的法线和纹理到这个绘制目标上。继续并且添加下面的方法到你的游戏类中:
 1 private void RenderNormalMap()
 2 {
 3     graphics.GraphicsDevice.SetRenderTarget(0, normalTarget);
 4     graphics.GraphicsDevice.Clear(Color.TransparentBlack);
 5     batch.Begin();
 6     batch.Draw(spriteNormal, spriteDest, Color.White);
 7     batch.End();
 8     graphics.GraphicsDevice.ResolveRenderTarget(0);
 9     graphics.GraphicsDevice.SetRenderTarget(0null);
10 }

 1 private void RenderDiffuseTexture()
 2 {
 3     graphics.GraphicsDevice.SetRenderTarget(0, diffuseTarget);
 4     graphics.GraphicsDevice.Clear(Color.TransparentBlack);
 5     batch.Begin();
 6     batch.Draw(spriteDiffuse, spriteSize, Color.White);
 7     batch.End();
 8     graphics.GraphicsDevice.ResolveRenderTarget(0);
 9     graphics.GraphicsDevice.SetRenderTarget(0null);
10 }

                              这两种方法几乎是相同的除了RenderNormalMap绘制这个精灵使用spriteNormal纹理和RenderDiffuseTexture使用了这个SpriteDiffuse纹理。同样,你可以绘制更多的精灵在这里并且他们所有都可以折射到背景上,但是目前我们将使用一个单一的精灵。
                              现在我们有三种绘制目标对象的方法,让们回到Draw方法中:
1 protected override void Draw(GameTime gameTime)
2 {
3     RenderRefractionMap();
4     RenderNormalMap();
5     RenderDiffuseTexture();
6     graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
7     base.Draw(gameTime);
8 }
                              目前对于所有的C#。我们有一个基本的设置和使我们的三个纹理去渲染目标对象。现在让我们创建我们的FX文件。右击在你的项目上并且添加一个新的文本文件。命名它Refraction.fx。接下来我们添加下面的shell到文件中:
 1 sampler SpriteDiffuse : register(s0);
 2 sampler SpriteNormal : register(s1);
 3 sampler RefractionMap : register(s2);
 4 float normalMapHeight = 0.05;
 5 float textureOpacity = 0.2;
 6 float4 RefractionPixelShader(float2 texCoord : TEXCOORD0) : COLOR0
 7 {
 8     return 0;
 9 }
10 technique Refraction
11 {
12     pass Pass0
13     {
14         PixelShader = compile ps_2_0 RefractionPixelShader();
15     }
16 }
                                首先,让我们解释我们的变量。SpriteDiffuse,SpriteNormal,和RefractionMap就象我们听到的那样。这些是我们的取样器为了访问我们绘制的目标对象的纹理。我们使用寄存器来访问这些变量,这样我们可以设置它们通过在XNA Game Studio里的渲染状态。
                                其次,我们有一个normalMapHeight.这个值代表我们假定正常的地图使这个对象有多高。这个效果有多少度的扭曲应用于图象后面的精灵上。最后我们有一个textureOpacity变量。这个决定这个精灵纹理有多透明,当把它与背景的折射图象混合时。
                                下面的变量,我们有一个标准的象素着色器和technique(技巧)。现在我们可以填充我们的象素作色器。首先的事情是我们需要做的是创建一个基础返回颜色并且摄取纹理的颜色从我们的SpriteDiffuse取样器中:
1 float4 color = 0;
2 float4 texColor = tex2D(SpriteDiffuse, texCoord);
                                接下来,我们需要检查如果我们正在绘制折射的精灵区域。我们可以通过检查我们纹理的alpha通道来实现它。因为我们正在做一个全屏的效果,一些区域将不需要应用任何的折射效果。
1 if (texColor.a > 0)
2 {
3 }
4 else
5 {
6     color = tex2D(RefractionMap, texCoord);
7 }
                                你将会看见,如果texColor.a是0(其他情况),我们设置颜色是RefractionMap取样器的直接样本。这可以保证我们不用扭曲屏幕上的区域,因为这个区域没有精灵(译者:玻璃砖块)在它上面。
                                我们很快的改变我们返回的值,这样我们返回颜色的变量:
1 return color;
                                现在你应该有这样的像素着色器:
 1 float4 RefractionPixelShader(float2 texCoord : TEXCOORD0) : COLOR0
 2 {
 3     float4 color = 0;      
 4     float4 texColor = tex2D(SpriteDiffuse, texCoord);
 5     if (texColor.a > 0)
 6     {
 7     }
 8     else
 9     {
10         color = tex2D(RefractionMap, texCoord);
11     }
12     return color;
13 }
                                最后,我们将填充我们的对if语句的真实情况。为了执行这个折射,我们需要得到我们正在从标准图的象素的法线中,然后我们从折射取样器里取样,基于法线偏移纹理的坐标。最后我们把diffuse(弥漫)的纹理和基于我们指定不透明的折射纹理相混合。这是非常简单的代码:
1 float4 normalColor = tex2D(SpriteNormal, texCoord);
2 float2 normalOffset = normalMapHeight * normalColor.rg;
3 float4 refractColor = tex2D(RefractionMap, texCoord + normalOffset); 
4 color = (texColor * textureOpacity) + (refractColor * (1.0f - textureOpacity));
                               这对于我们的FX文件。回到你的游戏类。到LoadGraphicsContent方法并且反注释掉我们加载的的FX文件代码。现在让我们完成我们的Draw方法。
                               添加下面的代码在graphics.GraphicsDevice.Clear行和base.Draw行之间:
1 graphics.GraphicsDevice.Textures[1= normalTarget.GetTexture();
2 graphics.GraphicsDevice.Textures[2= refractionTarget.GetTexture();
3 batch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
4 refractionEffect.Begin();
5 refractionEffect.Passes[0].Begin();
6 batch.Draw(diffuseTarget.GetTexture(),new Rectangle(0,0,graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,graphics.GraphicsDevice.PresentationParameters.BackBufferHeight),Color.White);
7 refractionEffect.Passes[0].End();
8 refractionEffect.End();
9 batch.End();
                              让我们回顾下我们在这里都做了什么。首先我们设置我们的normalTarget的纹理和refractionTarget的纹理到为我们使用的效果的纹理数组中。为了indices我们使用1和2,因为调用SpriteBatch.Draw将设置它的纹理到0索引。
                              接下来我们装备一个简单的SpriteBatch绘制程序,使用一个自定义的效果。我们不能循环我们的效果passes因为我们只知道effect中的一个pass。然后我们只绘制我们的diffuseTarget的纹理到一个全屏的矩形里。
                              就是这样!运行游戏,如果一切正常,你会看见如下图一样的效果:

                              这些是它绘制折射精灵在XNA Game Studio中。
源代码:http://www.ziggyware.com/readarticle.php?article_id=139
(完)
原文地址:https://www.cnblogs.com/315358525/p/1562711.html