[翻译]XNA 3.0 Game Programming Recipes之thirty-nine
PS:自己翻译的,转载请著明出处格
6-3 添加高级细节到你的光照:对每一个象素照明
问题
在前面的两节中,大量的阴影被计算在每个顶点中,这些阴影的值是以内插值替换的遍及所有的三角形象素。因此,它被称为pervertex lighting(or Gouraud shading)。
在某些情况下,这不会得到最好的效果。尤其是当使用大的三角形和/或正个边缘时,这会得到不想要的结果。
作为这方面的一个例子,考虑一个立方体的三个边,如图6-5的左边所显示的那样。图象的右边表明如何定义共享
的法线方向。在这个例子中,光照的方向通过四个箭头来表示,在右上部分。
集中在立方体的房顶,相应的直线是在顶点2和4之间在图6-5的右边部分。使用per-vertex光照,这个阴影在顶点2和4被计算。在顶点4中,没有大量个光照被接收因为法线在顶点4几乎垂直光照的方向。也就是说它是百分之20照亮。顶点2接收更多的光照,由于它的法线方向几乎和光照方向相同;也就是说,它是百分之80照亮。在per-vertex阴影中,这个阴影是以内插值替换三角形的象素,在这两个顶点之间接收一个法线,它是一个内插值替换在百分之80和20之间。这种方式,每有一个象素百分之100照亮。
然而,在顶点2和4之间的某处是一个象素,它的法线正好沿着光照的方向!这个法线在图6-5的右边部分被显示出来。明显的,这个象素应该是百分之100照亮,同时使用per-vertex光照象素能接收到一个光照仅仅是在百分之20到80之间。
解决方案
Per-vertex光照计算阴影正好只在顶点中,然后插入阴影编及在顶点之间的象素。
使用per-pixel光照,你插入这法线编及象素中,允许你去计算精确的阴影在每个象素中。
它是如何工作的
使用BasicEffect,它很容易去用per-pixel光照。当设置你的BasicEffect参数时,简单的加入下面的行:
6-3 添加高级细节到你的光照:对每一个象素照明
问题
在前面的两节中,大量的阴影被计算在每个顶点中,这些阴影的值是以内插值替换的遍及所有的三角形象素。因此,它被称为pervertex lighting(or Gouraud shading)。
在某些情况下,这不会得到最好的效果。尤其是当使用大的三角形和/或正个边缘时,这会得到不想要的结果。
作为这方面的一个例子,考虑一个立方体的三个边,如图6-5的左边所显示的那样。图象的右边表明如何定义共享
的法线方向。在这个例子中,光照的方向通过四个箭头来表示,在右上部分。
集中在立方体的房顶,相应的直线是在顶点2和4之间在图6-5的右边部分。使用per-vertex光照,这个阴影在顶点2和4被计算。在顶点4中,没有大量个光照被接收因为法线在顶点4几乎垂直光照的方向。也就是说它是百分之20照亮。顶点2接收更多的光照,由于它的法线方向几乎和光照方向相同;也就是说,它是百分之80照亮。在per-vertex阴影中,这个阴影是以内插值替换三角形的象素,在这两个顶点之间接收一个法线,它是一个内插值替换在百分之80和20之间。这种方式,每有一个象素百分之100照亮。
然而,在顶点2和4之间的某处是一个象素,它的法线正好沿着光照的方向!这个法线在图6-5的右边部分被显示出来。明显的,这个象素应该是百分之100照亮,同时使用per-vertex光照象素能接收到一个光照仅仅是在百分之20到80之间。
解决方案
Per-vertex光照计算阴影正好只在顶点中,然后插入阴影编及在顶点之间的象素。
使用per-pixel光照,你插入这法线编及象素中,允许你去计算精确的阴影在每个象素中。
它是如何工作的
使用BasicEffect,它很容易去用per-pixel光照。当设置你的BasicEffect参数时,简单的加入下面的行:
1 basicEffect.PreferPerPixelLighting=true;
注意:对于per-pixel象素着色器去工作,你需要至少有一个Shader2.0-编译图形卡。你可以很容易检查这一基本要求,评估这一行:
1 (GraphicsDevice.GraphicsDeviceCapabilities.MaxPixelShaderProfile>=ShaderProfile.PS_2_0)
代码
这个代码设置顶点显示在图6-5的左边部分。由于一些法线比统一的长度要长,确保你规格化它们在结束时:
1 private void InitVertices()
2 {
3 vertices=new VertexPositionNormalTexture[8];
4 vertices[0]=new VertexPositionNormalTexture(new Vector3(0,-1,0),new Vector3(-1,0,0),new Vector2(0,1));
5 vertices[1]=new VertexPositionNormalTexture(new Vector3(0,-1,-1),new Vector3(-1,0,0),new Vector2(0,0));
6 vertices[2]=new VertexPositionNormalTexture(new Vector3(0,0,0),new Vector3(-1,1,0),new Vector2(0.33f,1));
7 vertices[3]=new VertexPositionNormalTexture(new Vector3(0,0,-1),new Vector3(-1,1,0),new Vector2(0.33f,0));
8 vertices[4]=new VertexPositionNormalTexture(new Vector3(1,0,0),new Vector3(1,1,0),new Vector2(0.66f,1));
9 vertices[5]=new VertexPositionNormalTexture(new Vector3(1,0,-1),new Vector3(1,1,0),new Vector2(0.66f,0));
10 vertices[6]=new VertexPositionNormalTexture(new Vector3(1,-1,0),new Vector3(1,0,0),new Vector2(1,1));
11 vertices[7]=new VertexPositionNormalTexture(new Vector3(1,-1,-1),new Vector3(1,0,0),new Vector2(1,0));
12 for(int i=0;i<vertices.Length;i++)
13 vertices[i].Normal.Normalize();
14 myVertexDeclaration=new VertexDeclaration(device,VertexPositionNormalTexture.VertexElement);
15 }
2 {
3 vertices=new VertexPositionNormalTexture[8];
4 vertices[0]=new VertexPositionNormalTexture(new Vector3(0,-1,0),new Vector3(-1,0,0),new Vector2(0,1));
5 vertices[1]=new VertexPositionNormalTexture(new Vector3(0,-1,-1),new Vector3(-1,0,0),new Vector2(0,0));
6 vertices[2]=new VertexPositionNormalTexture(new Vector3(0,0,0),new Vector3(-1,1,0),new Vector2(0.33f,1));
7 vertices[3]=new VertexPositionNormalTexture(new Vector3(0,0,-1),new Vector3(-1,1,0),new Vector2(0.33f,0));
8 vertices[4]=new VertexPositionNormalTexture(new Vector3(1,0,0),new Vector3(1,1,0),new Vector2(0.66f,1));
9 vertices[5]=new VertexPositionNormalTexture(new Vector3(1,0,-1),new Vector3(1,1,0),new Vector2(0.66f,0));
10 vertices[6]=new VertexPositionNormalTexture(new Vector3(1,-1,0),new Vector3(1,0,0),new Vector2(1,1));
11 vertices[7]=new VertexPositionNormalTexture(new Vector3(1,-1,-1),new Vector3(1,0,0),new Vector2(1,0));
12 for(int i=0;i<vertices.Length;i++)
13 vertices[i].Normal.Normalize();
14 myVertexDeclaration=new VertexDeclaration(device,VertexPositionNormalTexture.VertexElement);
15 }
读这节"规格化你的法线"在6-1节去明白为什么你需要for循环。
注意:由于XNA不提供一个顶点结构,它接收一个3D位置,一个颜色,和一个法线,这节使用一个实心的蓝色纹理去确保每一个象素的基本颜色是相同的。这个方法,所有颜色的变化是造成你看到只有照明。
在你已经做这个以后,你可以绘制你的三角形使用per-pixel光照:
1 basicEffect.World=Matrix.Identity;
2 basicEffect.View=fpsCam.ViewMatrix;
3 basicEffect.Projection=fpsCam.ProjectionMatrix;
4 basicEffect.Texture=grassTexture;
5 basicEffect.TextureEnabled=true;
6 basicEffect.LightingEnable=true;
7 lingtDirection.Normalize();
8 basicEffect.DirectionalLight0.Direction=lightDirection;
9 basicEffect.DirectionalLight0.DiffuseColor=Color.White.ToVector3();
10 basicEffect.DirectionalLight0.Enable=true;
11 basicEffect.PreferPerPixelLighting=true;
12 basicEffect.Begin();
13 foreach(EffectPass pass in basicEffect.CurrentTechnique.Passes)
14 {
15 pass.Begin();
16 device.VertexDeclaration=myVertexDeclaration;
17 device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleStrip,vertices,0,6);
18 pass.End();
19 }
20 basicEffect.End();
2 basicEffect.View=fpsCam.ViewMatrix;
3 basicEffect.Projection=fpsCam.ProjectionMatrix;
4 basicEffect.Texture=grassTexture;
5 basicEffect.TextureEnabled=true;
6 basicEffect.LightingEnable=true;
7 lingtDirection.Normalize();
8 basicEffect.DirectionalLight0.Direction=lightDirection;
9 basicEffect.DirectionalLight0.DiffuseColor=Color.White.ToVector3();
10 basicEffect.DirectionalLight0.Enable=true;
11 basicEffect.PreferPerPixelLighting=true;
12 basicEffect.Begin();
13 foreach(EffectPass pass in basicEffect.CurrentTechnique.Passes)
14 {
15 pass.Begin();
16 device.VertexDeclaration=myVertexDeclaration;
17 device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleStrip,vertices,0,6);
18 pass.End();
19 }
20 basicEffect.End();