Pre-computed normals not working

This is a discussion on Pre-computed normals not working within the Game Programming forums, part of the General Programming Boards category; For some reason this simple normal code does not produce the desired results. Code: void CTerrain::ComputeNormals(void) { TerrainVertex *Vertices; m_pVB->Lock(0,0,(void ...

  1. #1
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,590

    Pre-computed normals not working

    For some reason this simple normal code does not produce the desired results.

    Code:
    void CTerrain::ComputeNormals(void)
    {
      TerrainVertex *Vertices;
      m_pVB->Lock(0,0,(void **)&Vertices,0);
    
      
      for (int i=0;i<m_iNumCellsPerCol;i++)
      {
        for (int j=0;j<m_iNumCellsPerRow;j++)
        {
          int index0=i      * m_iNumVertsPerRow + j;
          int index1=i      * m_iNumVertsPerRow + j + 1;
          int index2=(i+1)  * m_iNumVertsPerRow + j;
          
          int index3=(i+1)  * m_iNumVertsPerRow + j;
          int index4=i      * m_iNumVertsPerRow + j + 1;
          int index5=(i+1)  * m_iNumVertsPerRow + j + 1;
    
          
    
    
          D3DXVECTOR3 v1=Vertices[index1].Position-Vertices[index0].Position;
          D3DXVECTOR3 v2=Vertices[index2].Position-Vertices[index0].Position;
    
          D3DXVECTOR3 v3=Vertices[index3].Position-Vertices[index4].Position;
          D3DXVECTOR3 v4=Vertices[index5].Position-Vertices[index4].Position;
    
          D3DXVECTOR3 Cross1,Cross2;
          D3DXVec3Cross(&Cross1,&v1,&v2);
          D3DXVec3Cross(&Cross2,&v3,&v4);
    
          D3DXVec3Normalize(&Cross1,&Cross1);
          D3DXVec3Normalize(&Cross2,&Cross2);
    
          Vertices[index0].Normal=Cross1;
          Vertices[index1].Normal=Cross1;
          Vertices[index2].Normal=Cross1;
    
          Vertices[index3].Normal=Cross2;
          Vertices[index4].Normal=Cross2;
          Vertices[index5].Normal=Cross2;
                
        }
      }
    
      m_pVB->Unlock();
    
    }
    
    void CTerrain::LightTerrain(D3DXVECTOR3 LightSource)
    {
      TerrainVertex *Vertices;
      m_pVB->Lock(0,0,(void **)&Vertices,0);
    
      
      for (int i=0;i<m_iNumCellsPerCol;i++)
      {
        for (int j=0;j<m_iNumCellsPerRow;j++)
        {
          int index=i      * m_iNumVertsPerRow + j;
    
          D3DXVECTOR3 ToLight=LightSource-Vertices[index].Position;
          D3DXVec3Normalize(&ToLight,&ToLight);
    
          float dot=D3DXVec3Dot(&Vertices[index].Normal,&ToLight);
    
          if (dot>=0.0f)
          {
    
            Vertices[index].r*=dot;
            Vertices[index].g*=dot;
            Vertices[index].b*=dot;
    
            Vertices[index].Diffuse=D3DXCOLOR(Vertices[index].r,
                                              Vertices[index].g,
                                              Vertices[index].b,
                                              1.0f);
          }
        }
      }
      m_pVB->Unlock();
    
    }
    Perhaps they are backwards or something. I'm using pre-computed light because not much is gained with dynamic lighting.

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    I'm not sure exactly what's going on here, because I haven't done anything in the way of graphics work, so you may be right here. However, I'll point it out anyway:
    Code:
    D3DXVECTOR3 v1=Vertices[index1].Position-Vertices[index0].Position;
          D3DXVECTOR3 v2=Vertices[index2].Position-Vertices[index0].Position;
    
          D3DXVECTOR3 v3=Vertices[index3].Position-Vertices[index4].Position;
          D3DXVECTOR3 v4=Vertices[index5].Position-Vertices[index4].Position;
    Did you mean to skip 'index4'?


    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    what happens when i and j equal the number of rows/cols - 1 ? you'll be accessing out of bounds with i + 1 and j + 1 unless you've set your member vars to account for that.

  4. #4
    Registered User
    Join Date
    Apr 2002
    Posts
    1,571
    A few things. Make sure you're taking the cross in the correct direction depending on your coordinate system. Don't want it pointing down under your terrain. Also you are using shared vertices I imagine. So a given vertex could actually belong to two or more faces. Well which normal do you assign it? You overwrite the previous normal value with the current which is incorrect. You need to sum up all the normal values for that vertex using component-wise addition then divide by the number of faces it belongs to. This will provide you with the correct normal for that vertex.

    Also I assume you start all colors as (0,0,0,1) RGBA so that is why you skip the case where dot < 0. Otherwise you would need max(0, n DOT l).
    "...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

  5. #5
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,590
    NumCellsPerCol and NumCellsPerRow are

    int m_iNumCellsPerCol=m_iNumVertsPerCol-1;
    int m_iNumCellsPerRow=m_iNumVertsPerRow-1;

    So it doesn't go out of bounds. Mr. Wizard is correct in that normal information is being overwritten. I could fix this by creating 6 vertices per quad, but this is why I didn't do that:

    1. It's wasteful.
    2. Accessing the terrain information later becomes extremely tedious. Here's why:

    Let's say we have a grid...or an array to be more exact. I'll use Direct3D code here to illustrate. This assumes that m_pVB is a valid IDirect3DVertexBuffer9 interface pointer with size (width*height)*sizeof(VertexType).

    Code:
    VertexType *Vertices;
    
    m_pVB->Lock(0,0,(void **)&Vertices,0);
    
    int startx=-((width*cellsize)>>1);
    int startz=-((depth*cellsize))>>1);
    
    int vx=startx;
    int vz=startz;
    
    for (int i=0;i<depth;i++)
    {
      for (int j=0;j<width;j++)
      {
        int index=i * m_iNumVertsPerRow + j;
        Vertices[index]=VertexType((float)vx,0.0f,(float)vz);
        vx+=cellsize;
      }
      vx=startx;
      vz+=cellsize;
    }
    
    m_pVB->Unlock();
    Now with this code I have a simple 2D array or grid of vertices. I can access any grid point using a simple VertexBuffer[row * width + column].

    This also makes it very easy to do algos that require indexing into the array to change y values based on distance from some point, etc, etc.

    So the vertex buffer in memory is easy to use.

    Now take the following code.

    Code:
    VertexType *Vertices;
    
    m_pVB->Lock(0,0,(void **)&Vertices,0);
    
    int startx=-((width*cellsize)>>1);
    int startz=-((depth*cellsize))>>1);
    
    int vx=startx;
    int vz=startz;
    
    for (int i=0;i<depth-1;i++)
    {
      for (int j=0;j<width-1;j++)
      {
        int index1=i * m_iNumVertsPerRow + j;
        int index2=i*m_iNumVertsPerRow+j+1;
        int index3=(i+1)*m_iNumVertsPerRow+j;
    
        int index4=index3;
        int index5=index2;
        int index6=(i+1)*m_iNumVertsPerRow+j+1;
    
        Vertices[index1]=VertexType((float)vx,0.0f,(float)vz);
        Vertices[index2]=VertexType((float)vx+cellsize,0.0f,vz);
        Vertices[index3]=VertexType((float)vx,0.0f,vz+cellsize);
    
        Vertices[index4]=Vertices[index3];
        Vertices[index5]=Vertices[index2];
        Vertices[index6]=VertexType((float)vx+cellsize,0.0f,vz+cellsize);
      }
    }
     
    m_pVB->Unlock();
    Now you see that the array is not easy to use. Every 6 vertices or 6 elements represents one quad. It's not impossible to index into it for terrain algos, but it is cumbersome and quite confusing. The corners of the quad are really (starting at Vertex 0):

    Vertex 0......Vertex 1/4
    ................................
    Vertex 2/3......Vertex 5

    0,1,2 - First triangle
    3,4,5 - Second triangle

    Duplicated Vertices:

    Vertex 2 (duplicate is Vertex 3)
    Vertex 1(duplicate is Vertex 4)


    The duplicate vertices method allows me much more flexibility since each triangle of the quad can have different normals.....which fits with the design of the terrain. Perhaps I'll need to do this in order to get it to work. But accessing the terrain data is not going to be as easy as my first method. I must remember that if the algo is changing the height value of the upper right corner or the lower left corner...I must change this for both triangles. This would be Vertex 1,4 and Vertex 2,3 since they are overlapping vertices.
    Last edited by VirtualAce; 06-21-2005 at 04:09 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. x on upper right corner not working
    By caduardo21 in forum Windows Programming
    Replies: 1
    Last Post: 02-20-2005, 07:35 PM
  2. working out...
    By TechWins in forum A Brief History of Cprogramming.com
    Replies: 11
    Last Post: 04-10-2003, 10:20 AM
  3. cygwin -> unix , my code not working properly ;(
    By CyC|OpS in forum C Programming
    Replies: 4
    Last Post: 05-18-2002, 04:08 AM
  4. getting current working filename
    By Unregistered in forum C++ Programming
    Replies: 1
    Last Post: 04-17-2002, 03:42 PM
  5. What ive been working one, GL related
    By Eber Kain in forum Game Programming
    Replies: 3
    Last Post: 10-15-2001, 09:20 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21