Problem
When you draw a custom structure, you will find that the light is incorrect.
This is because you have not specified the correct normal vector. The video card requires that each vertex have a normal information so that it can determine how much light each triangle gets. For details, see chapter 6.
For each vertexAlgorithmLine vectors look complicated because most vertices are shared by multiple triangles.
Solution
If each vertex is used by only one triangle, you only need to find the normal vector of the triangle (in other words, this vector is perpendicular to the triangle) and use this vector as the normal vector of the three vertices.
However, in a structure, all vertices are shared by several triangles. To get a smooth result, each vertex needs to store the average values of all the normal values of the surrounding triangles.
Working Principle
Use the following pseudoCode, You can find the correct normal of each vertex:
- For each vertex in the structure, find the Triangle using the vertex.
- Calculate the normal vectors of these triangles.
- Returns the average value of all normal vectors.
- Store the average line in the vertex.
The process of averaging is required, because you always need to normalize the normal vector stored in the vertex (in other words, change the length to 1 ).
Note:Because we need to use the normal vector to calculate the light factor in vertex and pixel shader, we need to change the length of the normal vector to 1. When the light factor is determined only by the angle between the incident light and the normal, a large normal vector will cause the light factor to increase. For details, see chapter 6.
You can change the above steps to specific code, but if you switch steps 1 and 2, it will become easier:
- For each triangle in the structure, calculate the normal vector.
- Add this vector to the normal of the three vertices in the triangle. After performing this operation on all triangles, perform the following operations:
- Normalize the normal vector of each vertex in the structure.
Calculate the normal of a triangle: the definition of cross Multiplication
Before calculation, you need to know what a normal is. In short, the normal is perpendicular to the direction of the triangle, which means that any point of the normal on the triangle is the same. Because the normal is perpendicular to the triangle plane, it is also perpendicular to any vertex of the triangle.
So how to calculate the normal vector of a triangle? You can use cross-multiplication because the cross-multiplication of two vectors returns the plane vector perpendicular to the two vectors.
You can take the two sides of a triangle and obtain the vector of the vertical Triangle through the cross multiplication, as shown in Figure 5-13. The length of this normal is based on the length and angle of the two sides, so we need to normalize the normal vector.
Figure 5-13 get the normal vector of the triangle
Note:Vector3. cross (vec1, vec2) and vector3. cross (vec2, vec1) are calculated differently. The two results have the same length but the opposite direction. This is because a plane has two vertical directions: one pointing to the outside of the paper and the other pointing to the inside of the paper.
Generatenormalsfortrianglelist Method
When defining a large object, you usually want to use an index to define the structure, because this is the only way for vertices to be shared by multiple triangles, this allows you to smooth the light (see tutorial 6-2 ). Therefore, this tutorial is based on the vertex array and index array in the structure:
Private vertexpositionnormaltexture [] generatenormalsfortrianglelist (vertexpositionnormaltexture [] vertices, int [] indices ){}
This method accepts an array of vertices that do not contain the normal data, stores the correct normal information in each vertex, and returns this array. Based on the index information, this method can determine which vertices constitute a triangle. However, the content of the Index Array varies depending on whether trianglelist or trianglestrip is used. Therefore, the Code varies depending on the two conditions.
Calculate the Normal Line Based on trianglelist
If the vertex already contains the normal data, first change them to 0:
For (INT I = 0; I <vertices. length; I ++) vertices [I]. Normal = new vector3 (0, 0, 0 );
Then, as shown in the preceding pseudocode, You need to traverse all triangles and calculate their normal. In trianglelist, each triangle is defined by three consecutive indexes. This means that the number of triangles is length/3. The following is the circular code for Traversing each triangle defined in the index array:
For (INT I = 0; I <indices. length/3; I ++) {vector3 firstvec = vertices [indices [I * 3 + 1]. position-vertices [indices [I * 3]. position; vector3 secondvec = vertices [indices [I * 3 + 2]. position-vertices [indices [I * 3]. position; vector3 normal = vector3.cross (secondvec, firstvec); normal. normalize (); vertices [indices [I * 3]. normal + = normal; vertices [indices [I * 3 + 1]. normal + = normal; vertices [indices [I * 3 + 2]. normal + = normal ;}
For each triangle, you define two vectors 5-13. The vector between point P0 and Point P1 can be obtained by subtracting P1 from point P0, which is the first line of code. The second line of code calculates the vector from p0 to P2.
Then, get the vector perpendicular to the two vectors by using the cross-Multiplication. Don't forget to normalize the result to 1.
Note:According to the way you define the index, you need to use vector3. cross (secondvec, firstvec); instead. As mentioned above, this will obtain a vector in the opposite direction. If the vertex is defined clockwise (see tutorial 5-6), the code works normally.
Knowing the triangle's normal, you just need to simply add it to each vertex. After the for loop is complete, each vertex stores the sum of all the normal triangles. Then, we need to normalize the length of these large vectors to 1.
For (INT I = 0; I <vertices. length; I ++) vertices [I]. Normal. normalize (); Return vertices;
After storing all the normal lines to the vertex array, return the array to the calling code.
Calculate the Normal Line Based on trianglestrip
The situation for trianglestrip is a bit different, because each index in the index array is created based on it and the first two indexes to create a triangle:
For (INT I = 2; I <indices. length; I ++) {vector3 firstvec = vertices [indices [I-1]. position-vertices [indices [I]. position; vector3 secondvec = vertices [indices [I-2]. position-vertices [indices [I]. position; vector3 normal = vector3.cross (firstvec, secondvec); normal. normalize ();}
Starting from the third index, each index you encounter creates a triangle Based on index I, I-1, and I-2. The preceding Code traverses all triangles defined by the index array and creates two vectors on both sides of the corresponding triangle.
However, when you define an index in trianglestrip mode, the rotation sequence is automatically reversed after each triangle (see note 5-1 in the tutorial ). The result is that firstvec and secondvec change their positions. Changing the positions of firstvec and secondvec in the cross method achieves the same effect. After each triangle, the normal direction is reversed.
You cannot change this reversal, but it can solve this problem. You only need to create a Boolean variable, and the value is reversed after each triangle. If this value is true, you can change the direction of the normal:
Bool swappedwinding = false; For (INT I = 2; I <indices. length; I ++) {vector3 firstvec = vertices [indices [I-1]. position-vertices [indices [I]. position; vector3 secondvec = vertices [indices [I-2]. position-vertices [indices [I]. position; vector3 normal = vector3.cross (firstvec, secondvec); normal. normalize (); If (swappedwinding) normal * =-1; vertices [indices [I]. normal + = normal; vertices [in Dices [I-1]. Normal + = normal; vertices [indices [I-2]. Normal + = normal; swappedwinding =! Swappedwinding ;}
The rest of the code is similar to the previous one. Do not forget the code at the beginning of the Code that resets the initial normal to 0 and the last normalized normal.
Fail-safe
If the firstvec and secondvec vectors have the same direction, the vector3. Cross method will encounter an error. In this case, the triangle will become a line, called a ghost triangle (see the example 5-8 in the tutorial ).
In this case, vector3. Cross returns vector3 containing three Nan values. If this happens, do not add this vector to the vertex; otherwise, an error is returned:
If (! Float. isnan (normal. x) {vertices [indices [I]. normal + = normal; vertices [indices [I-1]. normal + = normal; vertices [indices [I-2]. normal + = normal ;}
Starting from vertex buffering and index Buffering
The previous Code starts with two arrays containing vertices and indexes. If you have stored them in the buffer (see tutorial 5-4) and saved a local copy, you need to retrieve the data. The method is as follows:
Int numberofvertices = myvertexbuffer. sizeinbytes/vertexpositionnormaltexture. sizeinbytes; vertexpositionnormaltexture [] vertices = new vertexpositionnormaltexture [numberofvertices]; myvertexbuffer. getdata (vertices); int numberofindices = myindexbuffer. sizeinbytes/4; int [] indices = new int [numberofindices]; myindexbuffer. getdata (indices );
You can obtain the number of vertices in the buffer by checking the number of bytes occupied by the vertex buffer. Because you know that a vertex occupies the majority of bytes, you can know how many vertices are included in the vertex buffer.
In the same way, you can find the number of indexes in the index buffer, because you know that an index contains four bytes.
Note:As explained in tutorial 5-4, getdata is not recommended for vertex buffering or index buffering. If you use the bufferusage. writeonly flag to create a buffer, the compiler reports an error to the getdata method.
Code
The following code stores the normal data in a vertex array, and the index is used to draw a triangle in trianglestrip mode:
Private vertexpositionnormaltexture [] generatenormalsfortrianglestrip (vertexpositionnormaltexture [] vertices, int [] indices) {for (INT I = 0; I <vertices. length; I ++) vertices [I]. normal = new vector3 (0, 0, 0); bool swappedwinding = false; For (INT I = 2; I <indices. length; I ++) {vector3 firstvec = vertices [indices [I-1]. position-vertices [indices [I]. position; vector3 secondvec = vertices [ind ICES [I-2]. position-vertices [indices [I]. position; vector3 normal = vector3.cross (firstvec, secondvec); normal. normalize (); If (swappedwinding) normal * =-1; if (! Float. isnan (normal. x) {vertices [indices [I]. normal + = normal; vertices [indices [I-1]. normal + = normal; vertices [indices [I-2]. normal + = normal;} swappedwinding =! Swappedwinding;} For (INT I = 0; I <vertices. length; I ++) vertices [I]. Normal. normalize (); Return vertices ;}