• [翻译]XNA 3.0 Game Programming Recipes之twentynine


    PS:自己翻译的,转载请著明出处格
                                                 5-7 用一个VertexBuffer计算所有顶点的法线
    问题
                  当渲染你自定义的结构到3D世界中,你注意到它不能正确的发亮。
                  这是因为你不能指定正常的法向矢量为结构的每一个顶点。一个法线要求用每个顶点,所以你的图形卡能测定多少光线撞击到三角形。参看第6章有更多的信息关于法线和为什么需要它们。
                  计算法向量为每个顶点看起来有点复杂,因为在所有结构大多数顶点被多个三角形共享。因此,你需要一个方法,自动计算出法线为对象的所有顶点。
    解决方案
                  如果每一个顶点被一个三角形使用,所有您需要做的是找出三角形的法线向量(换句话说,这个向量垂直于三角形)分配这个向量到这个顶点。
                  但是,对于一个结构,将所有顶点之间共享几个三角形。以至获得平稳的影响,每个顶点需要存储法线,这是所有围绕三角形的法线的平均值。
    它是如何工作的
                  使用此位的伪代码,您可以找到每个顶点的正确的法线:
                  1。每一个顶点在你的结构中,用这个顶点找到所有三角形。
                  2。计算法线向量为这些三角形。
                  3。这些法线向量的平均值。
                  4。保存这个平均的法线向量在顶点中。
                  这些步骤是需要的,因为你总是希望法线向量保存在你的顶点被规格化(换句话说,有一个长度正好是1).
    注意:你希望你的法线向量的长度正好为1,因为这个向量将会被用来用顶点和像素着色器计算照明因素。一个大的法线向量将导致一个大光照因子比小的向量,同时你希望光照因子依靠这个角度在射入光线和法线之间的,如第6章所解释的。
                  你可以继续转变这些成为真实的代码,但是如果你交换1和2的顺序,你可以使它容易些:
                 1。在你结构的每一个三角形,计算法线向量。
                 2。添加这个向量到三角形的三个顶点的法线中。之后,为所有的三角形,做到以下几点:
                 3。在你结构里的每一个顶点,规格化它的法线向量。
    Calculating the Normal of a Triangle: Definition of the Cross Product
                 在继续之前,你应该知道正常的发现是什么样的。很简单,它是垂直于三角形的方向。这也就是说用三角形的任何顶点,法线是相同的。因为法线是垂直于三角形平面,它同样是垂直于任何在三角形内的顶点。
                 所以,你如何计算一个单独三角形的法线向量?你使用一个十字坐标系做这个,由于两个顶点的十字坐标返回向量垂直于两个顶点的平面。
                 在你的三角形的情况下,你会采取三角形的两个边作为向量。使用它们的十字坐标,你将获得向量垂直于三角形,在图5-13虚线所表示。然而,这个法线的长度基于在两条边之间夹住的这个角度,最后,你需要规格化你的顶点到统一的长度。

                 注意Vector3.Cross(Vec1,Vec2)不是和Vector3.Cross(Vec2,Vec1)一样。两个结果顶点将会在同一条直线上,但是它们是相反的方向。这是因为一个平面,因此先前的三角形有两个垂直的方向:一个指向这个页面和别的也指向这个页面。
    The GenerateNormalsForTriangleList Method
                 当定义一个大的对象时,你主要是想确定你的结构使用这些指数,因为这是唯一的方法一个顶点能被多个三角形使用,它允许光滑的光线在你的对象上(6-2节),因此,这节将根据计算两个数组包含的顶点和结构指数:
    1 private VertexPositionNormalTexture[] GenerateNormalsForTriangleList(VertexPositionNormalTexture[] vertices,int[] indices)
    2 {
    3 }
                 这个方法接收顶点数组,这还不包含任何法线的数据。它将存储正确的法线的信息,每个顶点将返回数组。基于指数,它可以判断哪些顶点用于创建三角形。不过,根据你是否绘制三角形作为一个TrianleList或者TrianleStrip,indices数组的内容将会是不同的,所以用这个方法的相同的直线的两种情况下是不同的。
    Calculating Normals for a Structure Rendered As a TriangleList
                 如果顶点已经包含了任何关于它们法线的数据,重置它们为0:
    1 for(int i=0;i<vertices.Length;i++)
    2       vertices[i].Normal=new Vector3(0,0,0);
                 接下来,正如前伪代码,你会翻阅所有的三角形的结构和计算其法线。在一个TriangleList,每一个三角形是定义用三个连续的指数。也就是说三角形的数量在你的结构中等于indices.Length/3。
                 这是for循环通过每一个三角形定义在indices数组中:
     1 for(int i=0;i<indices.Length./3;i++)
     2 {
     3      Vector3 firstVec=vertices[indices[i*3+1]].Position-vertices[indices[i*3]].Position;
     4      Vector3 secondVec=vertices[indices[i*3+2]].Position-vertices[indices[i*3]].Position;
     5      Vector3 normal=Vector3.Cross(secondVec,firstVec);
     6      normal.Normalize();
     7      vertices[indices[i*3]].Normal+=normal;
     8      vertices[indices[i*3+1]].Normal+=normal;
     9      vertices[indices[i*3]+2].Normal+=normal;
    10 }
                 每一个三角形,你定义两个顶点如图5-13所示。至于任何在点p0和p1之间的顶点,你可以用P1减去P0得到。这个实现在代码第一行,第二行计算从P0到P2之间的向量。
                 下一步,你找到垂直于这些顶点的向量,用十字坐标。不要忘记规格化这个结果,这样它的长度变成了1了。
    注意:根据你定义你的指数,你需要使用Vector3.Cross(secondVec,firstVec)代替;如前所述,这个将导致顶点反方向。如果你定义你的顶点用顺时针顺序(5-6节),这个代码就完成了。
                  现在你知道三角形的法线了,你很容易的添加它到三角形的每个顶点的法线。当第一for循环完成,每一个顶点将会保存三角形的所有法线的总和。
                  这也就是说你必须把这些大的向量的长度规格化到1:
    1 for(int i=0;i<vertices.Length;i++)
    2        vertices[i].Normal.Normalize();
    3 return vertices;
                  随着所有法线保存在顶点数组中,你返回这些数组到调用代码。
    Calculating Normals for a Structure Rendered As a TriangleStrip
                  一个TriangleStrip,事情稍有不同,因为每个指数在indices数组中,创建一个新的三角形,基于指数和先前的两个指数:
    1 for(int i=2;i<indices.Length;i++)
    2 {
    3     Vector3 firstVec=vertices[indices[i-1]].Position-vertices[indices[i]].Position;
    4     Vector3 secondVec=vertices[indices[i-2]].Position-vertices[indices[i]].Position;
    5     Vector3 norma=Vector3.Cross(firstVec,secondVec);
    6     normal.Normalize();
    7 }
                  从第3个指数,每一个指数你阻止建造一个三角形基于指数i,i-1和i-2.先前的代码查阅所有的三角形被indices数组定义,创建两个向量响应三角形的两边。
                  虽然,当你定义指数为你的TriangleStrip,你自动交换缠绕顺序在每个三角形后(参看5-1节的注意)。作为一个结果,你调用firstVec和secondVec在每个三角形后改变它的边。这将会是同样的效果作为改变firstVec和secondVec在Cross方法为每下一个三角形,它将会每一次交换结果法线的方向。
                  你不能做任何关于它的事,但是你可以很容易的补偿它。只要掌握一个布尔型,在每个三角形之后选择它的值。如果它的值是true,你再一次交换法线的方向:
     1 bool swappedWinding=false;
     2 for(int i=2;i<indices.Length;i++)
     3 {
     4     Vector3 firstVec=vertices[indices[i-1]].Position-vertices[indices[i]].Position;
     5     Vector3 secondVec=vertices[indices[i-2]].Position-vertices[indices[i]].Position;
     6     Vector3 normal=Vector3.Cross(firstVec,secondVec);
     7     normal.Normalize();
     8     if(swappedWinding)
     9         normal*=-1;
    10     vertices[indices[i]].Normal+=normal;
    11     vertices[indices[i-1]].Normal+=normal;
    12     vertices[indices[i-2]].Normal+=normal;
    13     swappedWinding=!swappedWinding;
    14 }
                   其余的代码是于上一节的方法相同的,所以不要忘记关于重置代码在方法的开始的时候,和在代码的最后规格化。
    Making the Method Fail-Safe
                   Vector3.Cross方法早些时候使用时可能会得到一个错误,如果firstVec和secondVec是相同的方向时。这种情况下的三角形可能会是一条直线,而不是一个三角形,被称为ghost triangle(参看5-8节的例子)
                   在这种情况下,Vector3.Cross方法将会返回一个Vector3包含三个Not-a-Number(NaN)值。如果是这种情况,不要添加向量到任何顶点,否则它将会成为被恶化的:
    1 if(!float.IsNaN(normal.X))
    2 {
    3     vertices[indices[i]].Normal+=normal;
    4     vertices[indices[i-1]].Normal+=normal;
    5     vertices[indices[i-2]].Normal+=normal;
    6 }
    Starting from a VertexBuffer and an IndexBuffer
                   先前的代码开始于两个数组包含顶点和索引。如果你已经保存了它们在Buffers在录象RAM(5-4节),你不能保存一个本地的副本,你只有得到数据的返回。这是它如何做到的:
    1 int numberOfVertices=myVertexBuffer.SizeInByte/VertexPositionNormalTexture.SizeInBytes;
    2 VertexPositionNormalTexture[] vertices=new VertexPositionNormalTexture[numberOfVertices];
    3 myVertexBuffer.GetData(vertices);
    4 int numberOfIndices=myIndexBuffer.SizeInBytes/4;
    5 int[] indices=new int[numberOfIndices];
    6 myIndexBuffer.GetData(indices);
                   你找到你顶点的数量在你的VertexBuffer,通过考虑它占据了多少字节。直到你知道一个顶点占据多少字节,你能找到多少个顶点适合这VertexBuffer.
                   你按同样的方法可以找到一定数量的indices在IndexBuffer内,因为你知道一个整形占据4个字节。
    注意:5-4节作为解释说明,使用GetData在一个VertexBuffer或者一个IndexBuffer肯定不被推荐。甚至,如果你已经使用了BufferUsage.WriteOnly
    标志去创建一个buffer,编译器将会抛出一个错误在GetData方法中。
    代码
                   下面的方法增加了法线数据到顶点保存在顶点数组中,如果indices绘制三角形作为一个TriangleStrip:
     1 private VertexPositionNormalTexture[] GenerateNormalsForTrianleStrip(VertexPositionNormalTexture[] vertices,int[] indices)
     2 {
     3       for(int i=0;i<vertices.Length;i++)
     4              vertices[i].Normal=new Vector3(0,0,0)
     5       bool swappedWinding=false;
     6       for(int i=2;i<indices.Length;i++)
     7       {
     8           Vector3 firstVec=vertices[inices[i-1]].Position-vertices[indices[i]].Position;
     9           Vector3 secondVec=vertices[inices[i-2]].Position-vertices[indices[i]].Position;
    10           Vector3 normal=Vector3.Cross(firsVec,secondVec);
    11           normal.Normalize();
    12           if(swappedWinding)
    13               normal*=-1;
    14           if(!float.IsNaN(normal.X))
    15           {
    16               vertices[indices[i]].Normal+=normal; 
    17               vertices[indices[i-1]].Normal+=normal;
    18               vertices[indices[i-2]].Normal+=normal;
    19           }
    20           swappedWinding=!swappedWinding;
    21       }
    22        for(int i=0;i<vertices.Length;i++)
    23               vertices[i].Normal.Normalize();
    24        return vertices;
    25 }
    26 
  • 相关阅读:
    For each···in / For···in / For···of
    JavaScript object
    specific word count (index of )
    history of program
    js的回调函数
    promise
    js的事件流事件机制
    js的closures(闭包)
    baidu-map
    基于封装优点的类设计习惯
  • 原文地址:https://www.cnblogs.com/315358525/p/1538928.html
Copyright © 2020-2023  润新知