Thread: Weird Z-buffer issues

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Weird Z-buffer issues

    I've finally gotten around to re-coding my terrain system.. This version does not use a quad-tree as I had to remove all of the code because I forgot how it all worked. Seriously.

    Anyways this screenshot is showing major z buffer issues with a 32-bit zbuffer. Is this a driver error or something I'm doing wrong? I did not encounter this on my GeForce3, but I also did not get 490 FPS on a 256x256 brute force grid either.

    My quad tree code gave 90 to 100 FPS on my GeForce 3, and I cannot imagine what it will do for this GeForce 7800.

    Any ideas on how to fix this zbuffer issue?

    EDIT:

    I should note that the far clip plane in this scene is at 50000.0f. Perhaps the ZBuffer values are so close at that distance that float is not accurate enough to correctly render in the right order. I ramped up the distance because the FPS on my GeForce 7800 are just amazing and I cannot for the life of me get the frames to drop significantly.
    Last edited by VirtualAce; 03-12-2011 at 11:42 AM.

  2. #2
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    What is your near clip plane set too? Having a near clip too small is even worse than having a far clip too far for z-buffer innacuracies.

  3. #3
    vae victus! skorman00's Avatar
    Join Date
    Nov 2003
    Posts
    594
    Lowering the far clip plane will fix your problem. As Perspective said, the near clip plane is usally the culprit, but an extremely large far clip plane like 50000 will also do the trick.

  4. #4

    Join Date
    May 2005
    Posts
    1,042
    I should note that the far clip plane in this scene is at 50000.0f. Perhaps the ZBuffer values are so close at that distance that float is not accurate enough to correctly render in the right order
    Well, this is the nature of 'z fighting' problems I have seen in OpenGL. Have you tried setting different values for what it means for a depth test to succeed? e.g. in OpenGL GL_LESSTHAN and GL_LESSTHANEQUAL don't necessarily produde the same results.


    My near/far clip plane values, with no artifacts / z fighting (ATI driver, OpenGL):

    near = 10
    far = 20000

    I am quite sure that I have set the far to substantially further out than even 100,000 without seeing artifacts. When I render the world, I use GL_LESS as my depth test. I only use GL_LESSTHANEQUAL for rendering sky (GL_LEQUAL).
    I'm not immature, I'm refined in the opposite direction.

  5. #5
    Registered User Osaou's Avatar
    Join Date
    Nov 2004
    Location
    Stockholm, Sweden
    Posts
    69
    My hunch is Bubba use DirectX... ^^
    And yes, it certainly looks like it could be the near plane that's pulling some prank.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I will try setting the near clip plane to 10.0f as Bob suggested. None of my sources say much about the near clip plane and I was not aware this might cause issues. Perhaps this is why my previous culling algos culled too much.

    Thanks for the help. I'll keep you posted on the outcome.

    EDIT: Totally fixed by setting near clip plane to 10.0f. Wow. FPS increased too. So is this why my previous culling algorithm was 'too perfect'? Was the near clip plane the problem? If so, I'm gonna feel real stupid.

    Screenshot:
    ---------------
    Last edited by VirtualAce; 03-12-2011 at 11:42 AM.

  7. #7

    Join Date
    May 2005
    Posts
    1,042
    I was trying to find the msdn article that explains why having particular near/far values lead to silly issues. But, ultimately, what you stated is that the inaccuracy/uncertainty outweighs the precision of floating point number under certain conditions the further out you get, and having a smaller near clip plane value perturbs this problem
    I'm not immature, I'm refined in the opposite direction.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    So do you think my quadtree culling system will now work correctly? I scrapped the code due to major problems. Rather than attempt to fix a broken system, a complete redesign is what I was after. Sometimes starting over is a good thing.

    I have a possible method to produce neverending terrain and I'd like to see someone try it in OpenGL as well. My method is as follows.

    The problem:
    * A terrain is created from a simple greyscale bitmap which has a finite width and depth.
    * This leads to a large mesh being created, but eventually the camera runs off the edge of the mesh.

    Solution:
    My idea is to setup 4 zones in world coordinates. These 4 zones are the same for every terrain mesh. Each zone informs the terrain system it needs to
    - Translate a new patch of terrain to it's correct world position
    - Render the new patch

    Here is what I have so far with image attached
    -----------------------------------------------------------

    This algo re-uses the one terrain patch 8 different times. When the player enters a specified zone, specified outer zones are translated and rotated (oriented) to make the terrain appear to be endless.

    Here are the zones and their associated load zones:
    Zone 1 - DAB
    Zone 2 - BCE
    Zone 3 - DFG
    Zone 4 - GHE

    I will probably also create a 'neutral zone' where nothing is loaded and only the terrain patch the camera is inside of will be rendered.

    If I employ the quad-tree culling algo (and provided it works correctly), the terrain engine should run at very high frame rates. The zones are the same for all areas and can be computed with simple math. It is essentially a large scale tiling system in which new tiles at the edges are loaded in and rendered. The 'load' process is not actually a load. The terrain class will simply be re-used 8 times. The only thing that will differentiate the terrain patches is their central location. By translating the entire mesh to a position, I can effectively move the entire terrain section anywhere in the world I wish to.

    To produce never-ending terrain that is random or different would simply require a simple cache in and render scheme.

    I'll see if the idea works very soon.
    Last edited by VirtualAce; 03-12-2011 at 11:42 AM.

  9. #9

    Join Date
    May 2005
    Posts
    1,042
    So do you think my quadtree culling system will now work correctly?
    I remember the issue, but I suppose I might not have ever entirely understood the exact nature of the problem...what I'm saying is that it should have worked 'back then,' I don't entirely believe that a difference in the near plane value could have been the cause of that problem, but I also can't rule it out. You should try to re-implement it, I'm curious to find out.

    >>Sometimes starting over is a good thing.

    Yes, I do agree. I often find that if I understand something, but cannot seem to get it to work properly, I simply

    need to scrap it and restart from the basics. Actually, this is how I fix most of my bugs (although, this doesn't

    always mean completely redesigning a complex system but you get the idea).

    >>which has a finite width and depth.

    I don't know what depth means in this context

    Seeing as how I'm a complete babe in the woods when it comes to rendering terrain, I don't quite understand what you mean by 'orienting' the outer zones. I also don't understand what happens when you go from the inner zone 4 to 3 and then hit the other boundary of 3. Umm, yeah, like I said, complete babe in the woods

    It's interesting that when one of us knows how to do something, the other hasn't really got any ........ing clue what's going on. If we produced a child (because you were hitting on me in that quaternion thread, I KNOW it!) then we'd make like, one great god damned programmer.
    I'm not immature, I'm refined in the opposite direction.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Terrain is represented as one mesh object in my system. It's no different than a spaceship, character, rock, or anything else. It's just that the spaceship and rock don't take up (256*256*gridsize) world units. But it is rendered just the same. So let's say we want to move spaceship A to 100,100,100. We just translate the sucker and render.
    Same with the terrain. If a terrain is 256x256x32, then we know the middle is 128,128. That corresponds to 128*32,128*32 in world units. So since the terrain is created around 0,0,0 (just like other 3D objects), if I translate a portion of terrain to 0,0,128*32 world units the terrain section will be just north (with north as 0 degrees) of the current section. 128*32,0,0 would be directly west (looking north) or D in the diagram. Now let's say you move into zone 1 and we begin rendering DAB. To do this we simply flag those sections to be rendered. Now let's say we are in Section A, zone 1. Now we take the old D, translate it to the new world center for D based on camera position and render. I need more than one terrain zone because I cannot just take the current section and translate it - it would look like the terrain section was simply jumping in front of the camera. If you looked behind you, you would see empty space. I'm trying to create the 'illusion' of a never-ending world and to accomplish this, I need several sections of terrain.

    Let me also clarify that the zones are only for the terrain 'section' the camera is inside of. So the square containing zones 1,2,3 and 4 is always being rendered. The sections must change orientation because the lower right corner of A, does not line up with the upper left corner of the current section.

    I'll show you the terrain vertex generation code and index generation code. For the most part this has not been altered too much from the book Beginning Game Programming with DirectX 9.

    Code:
    void CTerrain::CreateVertices(void)
    {
      //Setup vars 
      int iStartX=-m_iWorldWidth2;
      int iStartZ=m_iWorldDepth2;
      int iEndX=m_iWorldWidth2;
      int iEndZ=-m_iWorldDepth2;
      
        
      //Lock vertex buffer
      TerrainVertex *pVerts;
      
      m_pVB->Lock(0,0,(void **)&pVerts,0);
      
      int iNumVerts=0;
      
      //Compute tex coord increments
      float fUInc=1.0f/(float)m_iNumCellsPerRow;
      float fVInc=1.0f/(float)m_iNumCellsPerCol;
      
      //Row, column and offset counters
      int r=0,c=0;
      int iOffset=0;
      
      for (int z=iStartZ;z>iEndZ;z-=m_iCellSize)
      {
        c=0;
        for (int x=iStartX;x<iEndX;x+=m_iCellSize)
        {
          //Create the vertex
          pVerts[iNumVerts]=TerrainVertex((float)x,m_puHeightMap[iOffset]*32,(float)z,(float)c*(fUInc*16.0f),
                                          (float)r*(fVInc*16.0f));
          //Set detail texture coords and multiply by 128 (detail texture is tiled across 1 quad)
          //Giving a very high level of detail
          pVerts[iNumVerts].du=(float)r*(fVInc*128.0f);
          pVerts[iNumVerts].dv=(float)c*(fUInc*128.0f);      
          
          //Set diffuse and specular color - only for fixed function pipeline lighting
          pVerts[iNumVerts].Diffuse=D3DXCOLOR(1.0f,1.0f,1.0f,1.0f);
          pVerts[iNumVerts].Specular=D3DXCOLOR(0.3f,0.3f,0.3f,0.4f);      
          iNumVerts++;
          c++;
          iOffset++;
        }
        r++;
      }
      
      //Unlock vertex buffer so D3D can push vertices to card
      m_pVB->Unlock(); 
             
    }
    
    void CTerrain::CreateIndices(void)
    {
    
      //Pointer to index buffer
      WORD *pIndices=0;
      
      //Lock index buffer and access it via pIndices 
      m_pIB->Lock(0,0,(void **)&pIndices,0);
      
      int iOffset=0;
      
      int iVert=0;
      
      for (int i=0;i<m_iNumCellsPerCol;i++)
      {
        for (int j=0;j<m_iNumCellsPerRow;j++)
        {
               
          //First triangle
          pIndices[iVert]=i*m_iNumVertsPerRow+j;
          pIndices[iVert+1]=i*m_iNumVertsPerRow+j+1;
          pIndices[iVert+2]=(i+1)*m_iNumVertsPerRow+j;
        
          //Second triangle
          pIndices[iVert+3]=(i+1)*m_iNumVertsPerRow+j;
          pIndices[iVert+4]=i*m_iNumVertsPerRow+j+1;
          pIndices[iVert+5]=(i+1)*m_iNumVertsPerRow+j+1;
    
          //Each quad is 2 triangles or 6 vertices
          iVert+=6;
        }
      }
      
      m_pIB->Unlock();
    }
    It's just a simple regular grid with the Y coordinate extracted from the heightmap and multiplied by a scale factor. Width (x) and Depth (z) are incremented by a constant gridsize.

    LH coordinate system:
    +X is right
    +Y is up
    +Z is into the screen

    Since the terrain grid is one object in this setup it does not allow me test at the face level or vertex level for culling. This would be extremely expensive so the quad tree is designed to fix this. The quad tree breaks the terrain section into many smaller sections. These smaller sections are then given bounding volumes and these volumes are tested against the frustum. The cool thing about the quad tree is this. If you divide once, you get 4 terrain sections, similar to the zones map I provided. If the bounding volume of the section is not partially or completely inside of the frustum, it is rejected and no more tests are performed. If the bounding volume is completely inside the frustum, the entire branch of the tree is rendered w/o further testing. If the bounding volume is completely inside, we know that all of it's children's volumes are also inside since they are contained within the parent volume.

    So:

    Outside - reject
    Partially inside - accept and test children
    Completely inside - accept and render children w/o testing

    As you can see this system can determine what needs to be rendered in about 2 to 3 recursions. It is very fast. Once more if I add things like rocks and trees they will become a part of the quad tree as well. If the bounding volume for the section where the trees and rocks are is completely out - I don't render or test any of them, etc, etc.

    The only thing the quad-tree does not work well with is a dynamic object. When an object is moving across the sections you must continually update which portion of the quad-tree it needs to be in. However, my solution is that dynamic objects are not in the terrain quad tree. They are in a quad tree of sorts, but it is an artificial one in that I perform a quad tree test without having a quad-tree. The first test is the most expensive because it must test every dynamic object in the render list. Each recursion significantly reduces the number of objects.

    And besides, how many dynamic objects are going to be on a terrain and in or just beyond visible range at any one time? Probably not too many.

    Now if you want to stick buildings on the terrain that you can enter then you cannot just use the quad tree. A fairly simple way is to first render the building using a BSP tree. Then you render the terrain using the quadtree. Since the building will fill the z buffer with correct values when you render the terrain, it will not overwrite the building. Windows in buildings can be accomplished either using stencil buffer or designing the geometry in such a way as to have 'holes' where the windows are. If they are 'holes' or rectangular cutouts then when the building is rendered, these 'windows' will not write to the zbuffer in that area. When you render the terrain, the terrain will pass the z test inside the window area and then it looks as if you can see through the windows. Later on a second pass you can add transparent textures to simulate glass effects. Since the two renderers are separate you can still achieve excellent outside lighting and excellent interior lighting because the shaders are relative to the renderer used, not the objects. Note that also since you render the building first and then the terrain, sun can still create a lens flare through the window. Shadowing can be used inside of the building to simulate light coming in the window, but I've not explored that as of yet.
    Last edited by VirtualAce; 07-31-2006 at 02:59 AM.

  11. #11
    vae victus! skorman00's Avatar
    Join Date
    Nov 2003
    Posts
    594
    You guys will enjoy this read:

    http://www.sjbaker.org/steve/omniv/l..._z_buffer.html

    This is the best explanation I've found about this problem. I think I've also posted it before...but hey what's the harm of looking at it again?

    I don't know if you guys dig Gamasutra, but if you do, here are some interesting articles about terrain:
    http://www.gamasutra.com/features/20.../ulrich_01.htm

    this one I particularly enjoy, because it discussing doing everything procedurally with chaos/noise algorithms.
    http://www.gamasutra.com/features/20010302/oneil_01.htm

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Yes I've read that article on terrains as well as many from www.vterrain.org

    The article on zbuffer was very interesting and makes sense. Just from a numerical perspective I should have known what was going on. Thanks a million.

  13. #13
    vae victus! skorman00's Avatar
    Join Date
    Nov 2003
    Posts
    594
    Now you know, and knowing is half the battle.

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Thanks GI Joe.


  15. #15
    Registered User linuxdude's Avatar
    Join Date
    Mar 2003
    Location
    Louisiana
    Posts
    926
    about the near clipping plane issue here is an article that helped me. Setitng near farther is better though.
    http://www.sjbaker.org/steve/omniv/l..._z_buffer.html
    Hope this helps bubba

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 10-29-2006, 05:04 AM
  2. Another weird error
    By rwmarsh in forum Game Programming
    Replies: 4
    Last Post: 09-24-2006, 10:00 PM
  3. Print out a buffer
    By SwarfEye in forum C Programming
    Replies: 4
    Last Post: 09-08-2006, 09:32 AM
  4. writing a pack-style function, any advices?
    By isaac_s in forum C Programming
    Replies: 10
    Last Post: 07-08-2006, 08:09 PM
  5. DirectSound - multiple sounds
    By Magos in forum Game Programming
    Replies: 9
    Last Post: 03-03-2004, 04:33 PM