1
\$\begingroup\$

I am currently looking for some guidance on how to correctly calculate my normals on the CPU for my terrain as the results I'm getting seem to be incorrect. Eventually I will attempting to calculate them on the GPU as I'm aiming for terrain deformation in real-time.

These are the results

enter image description here The different colours on the image represent different normal values.

Here is the code I'm using to generate them.

for (int i = 0; i < width; i++) {

    for (int j = 0; j < height; j++) {
        int index1 = (j * width) + i;
        int index2 = (j * width) + (i + 1);
        int index3 = ((j + 1) * width) + i;

        int index4 = ((j + 1) * width + i);
        int index5 = ((j * width + (i + 1)));
        int index6 = ((j + 1) * width + (i + 1));

        //vertices of triangle
        Vertex vertex1 = vertices->at(index1);
        Vertex vertex2 = vertices->at(index2);
        Vertex vertex3 = vertices->at(index3);

        Vertex vertex4 = vertices->at(index4);
        Vertex vertex5 = vertices->at(index5);
        Vertex vertex6 = vertices->at(index6);

        //each vertex as XMVECTOR
        XMVECTOR point1 = XMVectorSet(vertex1.Pos.x, vertex1.Pos.y, vertex1.Pos.z, 0.0f);
        XMVECTOR point2 = XMVectorSet(vertex2.Pos.x, vertex2.Pos.y, vertex2.Pos.z, 0.0f);
        XMVECTOR point3 = XMVectorSet(vertex3.Pos.x, vertex3.Pos.y, vertex3.Pos.z, 0.0f);

        XMVECTOR point4 = XMVectorSet(vertex4.Pos.x, vertex4.Pos.y, vertex4.Pos.z, 0.0f);
        XMVECTOR point5 = XMVectorSet(vertex5.Pos.x, vertex5.Pos.y, vertex5.Pos.z, 0.0f);
        XMVECTOR point6 = XMVectorSet(vertex6.Pos.x, vertex6.Pos.y, vertex6.Pos.z, 0.0f);

        //lines to compute the cross product
        XMVECTOR line1 = XMVectorSubtract(point2, point1);
        XMVECTOR line2 = XMVectorSubtract(point3, point1);

        XMVECTOR line3 = XMVectorSubtract(point5, point4);
        XMVECTOR line4 = XMVectorSubtract(point6, point4);

        //cross product to get our unnormalized normal
        XMVECTOR cross = XMVector3Cross(line1, line2);

        XMVECTOR cross2 = XMVector3Cross(line3, line4);


        //normalize to get our normal value
        cross = XMVector3Normalize(cross);
        cross2 = XMVector3Normalize(cross2);

        vertices->at(index1).Normal = Operations::vectorToXMFLOAT3(cross);
        vertices->at(index2).Normal = Operations::vectorToXMFLOAT3(cross);
        vertices->at(index3).Normal = Operations::vectorToXMFLOAT3(cross);


        vertices->at(index4).Normal = Operations::vectorToXMFLOAT3(cross2);
        vertices->at(index5).Normal = Operations::vectorToXMFLOAT3(cross2);
        vertices->at(index6).Normal = Operations::vectorToXMFLOAT3(cross2);
    }

}

Edit: I have made some changes to the code to account for the second triangle in the triangle pair but still produces similar results. The green (grass) represents a normal value of 0. The pink represents a normal value of 1. The red represents a normal value of -1

Why is the majority of the mesh 0, and where the normals are calculated, they are incorrect?

enter image description here

Edit 2

I have removed the use of the indices array in this loop and now the whole plane is processed, but results are still incorrect.

enter image description here

Edit 3

I did not realise I was referencing the indices array in more than one place. When I corrected this, I am now seeing better results. Although there is a slight problem of the normal that is being calculated is -1 instead of the expected +1

enter image description here

Edit 4:

Normals are calculated per vertex and don't take other touching faces into account, that is the next step. I'm guessing some parts of the texturing/terrain look a little weird due to this.

enter image description here


At the moment my terrain is just a flat plane, so all of the normals should be the same, right? However the values seem to be different when rendering/printing out.

There are some values that are 0, some are -1 and the rest are +1. Does anyone know where I'm going wrong?

\$\endgroup\$
  • \$\begingroup\$ This question/answer on StackOverflow seems to be what you're looking for.. \$\endgroup\$ – Vaillancourt Apr 16 '17 at 0:49
  • 1
    \$\begingroup\$ Hey I took the liberty to download your image from Dropbox and reupload it here. Please avoid linking to resources as volatile as dropbox as the links tend to rot. The images uploaded with the image feature here will never expire, as long as stack exchange is in business so, at least for images, that's the way to go :) \$\endgroup\$ – Vaillancourt Apr 16 '17 at 0:53
  • \$\begingroup\$ @AlexandreVaillancourt Thank you, I will keep this in mind! \$\endgroup\$ – user Apr 16 '17 at 11:02
  • \$\begingroup\$ I do this on the gpu, its much faster and very simple to do. If you want I'm happy to make an answer showing how it works in the shader. \$\endgroup\$ – Justin William Stanley Bryant Apr 17 '17 at 4:25
  • \$\begingroup\$ @JustinWilliamStanleyBryant That is my next task so an example would be great if you could! \$\endgroup\$ – user Apr 17 '17 at 16:19
2
\$\begingroup\$

I see several bugs, if I understand the structure of your mesh correctly. I think it's a grid of triangles, arranged so pairs of triangles form squares.

First, when calculating index1, index2, and index3, you should multiply the j factor with width and not height.

Next, for a smooth mesh (later, when you are deforming it) you'll need to give each vertex the average of all the normals of faces touching that vertex. Right now, only last triangle that you visit which touches a vertex is going to have any influence on its normal.

Last, you're only visiting the "lower left" triangle in each pair. You need to visit all of them to get correct normals. Also, this means that you'll never touch the last vertex (at index width * height - 1) in this loop. The greatest index you'll reach (after fixing the first bug) is width * height - 2.

\$\endgroup\$
  • \$\begingroup\$ Thank you for pointing these out, you are correct. I have made some changes and was wondering if you could review them? I understand to get the real normal values for a smooth surface I have to take other faces into account, but right now I just want to do it per face to ensure that I can at least calculate them correctly. What I seem to have is still giving me incorrect results. I have taken both triangles into account and 'tried' to calculate the normals for each triangle individually but with similar results. What am I doing wrong? \$\endgroup\$ – user Apr 16 '17 at 11:36
  • \$\begingroup\$ Just to verify, when you say the normals a 1, 0, and -1, what do you mean by that? Correct normals for this plan would be vectors, all set to (0, 0, 1). Is that what you're expecting? [Ed: Assuming that z is up; if y is up, then the normals would be (0, 1, 0).] \$\endgroup\$ – Victor T. Apr 16 '17 at 18:43
  • \$\begingroup\$ OK, I just read through your updated code. Since you've changed the loops to i < width instead of i < width - 1 and j < height instead of j < height - 1, you're now checking a maximum vertex index of (height - 1 + 1) * width + (width - 1 + 1) = width * (height + 1). Does this reflect the actual size of the vertex array? \$\endgroup\$ – Victor T. Apr 16 '17 at 18:48
  • \$\begingroup\$ My y value is up, so I would expect every normal to be 0,1,0 as you mentioned, however the colours of the plane don't seem to reflect this. Perhaps I'm not looping through correctly. The grass represents a y normal value of 0, pink represents 1, and red -1. My vertices list size is 263169 and my width/height are both 513. It's as if a big chunk of my plane is not being processed. Thank you for your help. \$\endgroup\$ – user Apr 16 '17 at 19:15
  • 1
    \$\begingroup\$ The cross product of two vectors will face in one of two directions, each the negative of the other, depending on the order you give the parameters. This corresponds indirectly with the orientation of your whole game world. For this case, you could simply swap the order of the parameters to XMVector3Cross. \$\endgroup\$ – Victor T. Apr 17 '17 at 3:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.