[翻译]XNA 3.0 Game Programming Recipes之thirty-five

PS:自己翻译的,转载请著明出处格
                                                 5-13 对凹凸贴图起作用:固定法线
问题
                  一个三角形的主要问题之一是平直的。如果你绘制一个巨大的墙用两个巨大的三角形,覆盖它用很漂亮的纹理,这个结果看起来令人失望的平面。
                 你可以添加真正的详细到一个三角形只在通过分三角形成更小的三角形,你可以定义每个顶点的3D位置 。尽管,消耗你的PC上的巨大资源。
解决方案
                 而不是使用这种蛮力式的方法,你能使用凹凸贴图。凹凸贴图是一种技术,它给观众印象是高度差异在一个大的三角形通过每个象素颜色的改变在三角形中。
                 如果你接受一个平面的图片,红色,塑料板,所有的象素将会几乎完全相同的颜色。换句话说,如果你接受一个粗糙红色表面的图片,如一个红色砖块,所有的象素可能有一个不同的红色着色器,给观众印象这砖块有一个粗糙的表面。
                 这是你想要模拟的效果。在一个粗糙的表面,所有的象素有一个不同的颜色因为它们是光照不同。这是由于你能分一个砖块成成千个小的表面,所有带着一个不同的方向,因此带着法线向量,如图5-26左边所示。每一个不同的法线,你得到一个不同的照明条件(见第6章)
                 而不是绘制你的砖块使用上百个三角形如图5-26左边所示,你正准备去绘制它们作为一个表面只有两个三角形正如图象的右边,在两个三角形的每一个象素,你准备稍微调整默认的法线。这将导致所有的象素,去获得一个不同的光照,从而得到一个不同的颜色。因此,凹凸贴图将添加大量的现实主义(realism),如果砖块的位置或者光照方向改变了。
                 为获取最佳的效果,你不能随机改变法线在象素中,但是你想改变它用一个正常途径,如图5-26右边所指出的法线。注意法线在图5-26右边法线和左边的相同。也就是说,在固定的纹理,你应该知道在每个象素中应该调整多少法线。通常,每个偏离的法线的X,Y,Z坐标被保存成R,G和B通道的一个第二图象,称为纹理的bump map(或法线贴图)
                 所以,不是蛮力方法划分砖块墙成成千上百个三角形,你要绘制两个三角形的一个四方格用两个纹理:一个包含通常砖块的图象包含有用的颜色为每一个象素和另外图象包含多少默认的法线应该被偏离在每个象素中。你将编写一个象素着色器查找通常颜色和偏离法线的一个像素,使用这个法线去计算多少光撞击到象素,用这个值去调整普通颜色的光亮值。
                 作为一个入门凹凸贴图,在这节你碰贴图一个平面表面上。一个平面表面是一个简单化的情况,由于它有一个恒定的法线向量。更一般的曲面表面情况会在下一节叙述。
它是如果工作的
                 首先你需要定义一些三角形描述直平表面。接下来的代码定义两个纹理三角形,足够为一个大的长方形:
 1 private VertexPositionTexture[] InitVertices()
 2 {
 3      VertexPositionTexture[] vertices=new VertexPositionTexture[6];
 4      vertices[0]=new VertexPositionTexture(new Vector3(-5,0,10),new Vector2(0,2));
 5      vertices[1]=new VertexPositionTexture(new Vector3(-5,0,-10),new Vector2(0,0));
 6      vertices[2]=new VertexPositionTexture(new Vector3(5,0,10),new Vector2(1,2));
 7      vertices[3]=new VertexPositionTexture(new Vector3(-5,0,-10),new Vector2(0,0));
 8      vertices[4]=new VertexPositionTexture(new Vector3(5,0,-10),new Vector2(1,0));
 9      vertices[5]=new VertexPositionTexture(new Vector3(5,0,10),new Vector2(1,2));
10      return vertices;
11 }
                 因为你会从一个纹理中取样,几乎是一般3D位置,你同样需要指定纹理坐标(参看5-2节)。由于所有的Y坐标都是0,两个三角形你定义的是躺在平面上的(在XZ平面),所以你知道默认的法线向量指向向上的方向。
注意:这是一个假设,这是真的只有这种情况;参看下一节一个一般的方法。
                 随着你的顶点的定义,你已经准备开始编写你的.fx文件
XNA-to-HLSL Variables
                 正如所有的3D效果,你将会需要传递你的世界,视景和投影矩阵,这样你的顶点着色器能改变你顶点的3D位置到2D屏幕坐标。为了显示凹凸贴图的效果,你应该同样有改变光照方向的能力。
                 此外,正如这章导言所述,你需要传递两个纹理到你的图象卡中:一个从普通颜色取样,第二个检查默认的法线应被偏离多少在每个象素多少中。
 1 float4*4 xWorld;
 2 float4*4 xView;
 3 float4*4 xProjection;
 4 float3 xLightDirection;
 5 Texture xTexture;
 6 sampler TextureSampler=sampler_state{texture=<xTexture>;magfiler=LINEAR;minfilter=LINEAR;mipfilter=LINEAR;AddressU=wrap;AddressV=wrap;};
 7 Texture xBumpMap;
 8 sampler BumMapSampler=sampler_state{texture=<xBumpMap>;magfiler=LINEAR;minfilter=LINEAR;mipfilter=LINEAR;AddressU=wrap;AddressV=wrap;};
 9 struct SBMVertexToPixel
10 {
11    float4 Position:POSITION;
12    float2 TexCoord:TEXCOORD0;
13 };
14 struct SBMPixelToFrame
15 {
16     float4 Color:COLOR0;
17 };
                 顶点着色器将输出二维屏幕上的每个顶点的位置(一如既往)一起与纹理坐标。已在支持Pixel Shader计算最终颜色。
顶点着色器
                 顶点着色器是非常容易,因为所有它必须做的事是把三维坐标到二维屏幕空间,并通过纹理坐标到像素着色器:
1 SBMVertexToPixel SBMVertexShader(float4 inPos:POSITION0,float2 inTexCoord:TEXCOORD0)
2 {
3     SBMVertexToPixel Output=(SBMVertexToPixel)0;
4     float4*4 preViewProjection=mul(xView,xProjection);
5     float4*4 preWorldViewProjection=mul(xWorld,preViewProjection);
6     Output.Position=mul(inPos,preWorldViewProjection);
7     Output.TexCoord=inTexCoord;
8     return Output;
9 }
                 作为要求,3D位置被世界,视景和投影矩阵联合矩阵被转换来获得2D屏幕坐标。纹理坐标只是发送到象素着色器。
象素着色器
                 每一个象素在每一个三角形上,象素着色器将查询普通的颜色从颜色纹理中。这次,象素着色器将同样从凹凸贴图取样,去查看哪个方向默认的法线应该偏离,为具体的象素。
                 一个法线向量是一个3D向量,被三个坐标定义。更多的信息关于法线,参看5-7节。为了了解一个法线会影响光照,参看6-1节。
                 凹凸贴图的每一个象素,这三个扭曲法线的坐标被保存在三个颜色部分。虽然,这三个坐标应该范围从-1到1(一个方向能沿着正或负x-,y-,z-坐标轴),同时一个颜色组成部分能被指定仅在[0,1]范围(1表示高亮度,0表示没有亮度)。所以你需要贴图这个值从[0,1]范围到[-1,1]范围,哪个能,例如,减去0.5从这个值(贴图这值在[-0.5,0.5]的范围内),用2乘以这个结果(贴图它在[-1,1]内的范围)。这个在你象素着色器的第二和第三行完成:
 1 SBMPixelToFrame SBMPixelShader(SBMVertexToPixel PSIn):COLOR0
 2 {
 3       SBMPixelToFrame Output=(SBMPixelToFrame)0;
 4       float3 bumpMapColor=tex2D(BumpMapSampler,PSIn.TexCoord).rbg;
 5       float3 normalFromBumpMap=(bumpMapColor-0.5f)*2.0f;
 6       float lightFactor=dot(-normalize(normalFromBumpMap),normalize(xLightDirection));
 7       float4 texColor=tex2D(TextureSampler,PSIn.TexCoord);
 8       Output.Color=lightFactor*texColor;
 9       return Output;
10 }
                在凹凸贴图,三个颜色通道显示哪个方向默认法线在象素着色器应该被偏离。像素的默认正常情况下不应该偏离是蓝色(R=0.5,G=0.5,B=1)。因此,在这两个三角形的具体的例子定义在XZ平面中,一个像素应该得到一个法线向量指着向上,在(0,1,0)的方向。
                象素在凹凸贴图有一个颜色,它与(R=0.5,G=0.5,B=1)不同,表明这法线应该偏离(0,1,0)方向。在这种情况下,你想要法线偏离到X或Z方向里为这个象素。参看下一节一般方法。
                这是为什么象素着色器在这个特定例子用蓝色颜色组成部分作为Y坐标为结果的法线,红色和绿色组成部分作为X和Z坐标为这法线。作为一个例子,如果颜色从bump map中取样相当于(0.5,0.5,1),normalFromBumpMap将会是(0,1,0),意思是法线没有偏离默认的法线位置(哪个Up为两个三角形)。虽然,如果bumpMapColor=(0.145,0.5,0.855),normalFromBumpMap会是(-0.71,0.71,0),意思是默认法线已经偏离左边45度。
                一旦你知道每个象素中的法线,你规格化这个法线(使它的长度等于1.0f)和光照方向在你接收它们的数量积之前。在6-5节有说明,这个数量积表明当前的象素的亮度,所以你乘以它用正常的颜色。
Technique Definition
                最后,添加这个技术定义到你的.fx文件中,达到它的功能:
1 technique SimpleBumpMapping
2 {
3     pass Pass0
4     { 
5          VertexShader=compile vs_2_0 SBMVertexShader();
6          PixelShader=compile ps_2_0 SBMPixelShader();
7     }
8 }
代码
        详细代码参看上面的代码。

posted on 2009-08-09 15:08  一盘散沙  阅读(271)  评论(0编辑  收藏  举报

导航