Artifacts using DrawIndexedPrimitive

Started by
4 comments, last by RonHiler 20 years, 3 months ago
Hi guys, I have one of those bugs that is driving me up a wall. I'm taking out all the error handling in the code I'm about to display for brevity, but I do check the return codes, and no errors are returned. The problem I'm getting is with my terrain. When I draw more than about 1800 terrain tris, everything is fine. However, when I draw less than 1800 tris, I get artifacts. Here are links to two screens with the terrain in wireframe to better show the problem. This is a normal shot Normal And this is one that shows the artifacts Artifacts During initialization, this is how I create the VB and IB for the terrain:

    r = Direct3D.D3DDevice->CreateVertexBuffer(40960*sizeof(GENERIC3DVERTEX),D3DUSAGE_WRITEONLY, D3DFVF_GENERIC3DVERTEX, D3DPOOL_DEFAULT, &TerrainVertexBuffer);
    r = Direct3D.D3DDevice->CreateIndexBuffer(40960*sizeof(WORD), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &TerrainVertexIndexBuffer);
Late during initialization, the terrain VB gets loaded. This happens only once, and then the VB never gets touched again (there is, ATM, only one landblock for the player to walk around in)
   
    GENERIC3DVERTEX *Vertices = NULL;

    LandBlock.NumVertices = 0;

    //set up the vertex buffer

    r = TerrainVertexBuffer->Lock(0, 0, (BYTE**)&Vertices, D3DLOCK_DISCARD);

    for (unsigned int i=0; i<LandBlock.Vertices.size(); i++)
        {
        *Vertices++ = Init3DVertex(LandBlock.Vertices[i].VertexPoint, LandBlock.Vertices[i].VertexNormal,LandBlock.Vertices[i].tU, LandBlock.Vertices[i].tV);
        LandBlock.NumVertices++;
        }

    r = TerrainVertexBuffer->Unlock();
Okay, then I set up the index list. This happens whenever the camera moves. This is a bit complex, but just before this routine I first determine the terrain bounding box visiblities (using a BSP sort of system). Here I am just culling out the non-visible terrain tris and adding the rest of their indeces to the index list. The whole thing with the MAXINT is just a leaf node detector, ignore it, it's not part of the problem:

    WORD *Indeces = NULL;

    LandBlock.NumChildTris = 0;
    LandBlock.NumCulledTris = 0;

    //set up the index buffer

    r = TerrainVertexIndexBuffer->Lock(0, 0, (BYTE**)&Indeces, D3DLOCK_DISCARD);

    for (unsigned int i=0; i<LandBlock.Triangles.size(); i++)
        LandBlock.Triangles[i].IsVisible = true;

    for (unsigned int i=0; i<LandBlock.BoundingBoxes.size(); i++)
        {
        if (LandBlock.BoundingBoxes[i].IsVisible == false)
            {
            for (unsigned int j=0; j<LandBlock.BoundingBoxes[i].BoundTriangleIndeces.size(); j++)
                {
                LandBlock.Triangles[LandBlock.BoundingBoxes[i].BoundTriangleIndeces[j]].IsVisible = false;
                }
            }
        }

    for (unsigned int i=0; i<LandBlock.Triangles.size(); i++)
        {
        if (LandBlock.Triangles[i].LeftChild == MAXINT && LandBlock.Triangles[i].RightChild == MAXINT)
            {
            if (LandBlock.Triangles[i].IsVisible == true)
                {
                *Indeces++ = LandBlock.Triangles[i].Vertex[0];
                *Indeces++ = LandBlock.Triangles[i].Vertex[1];
                *Indeces++ = LandBlock.Triangles[i].Vertex[2];
                LandBlock.NumChildTris++;
                }
            else
                {
                LandBlock.NumCulledTris++;
                }
            }
        }

    r = TerrainVertexIndexBuffer->Unlock();
    if (FAILED(r))
        {
        D3DXGetErrorString (r, string, 250);
        sprintf(OutputString, "Failed to Unlock Terrain Vertex Index Buffer: %s", string);
        MessageBox(NULL, OutputString, "D3DClass::SetTriangleList()", MB_OK);
        return;
        }
And finally, every frame we actually draw the terrain. Toward the beginning in the "if camera moved" block, I actually call the routine I just showed to set the terrain indeces:
 
    float Aspect = Direct3D.WorldViewport.Width / Direct3D.WorldViewport.Height;
    D3DXMatrixPerspectiveFovLH(&ProjectionMatrix, D3DX_PI/4, Aspect, 1.0f, 200.0f);
    Direct3D.GetD3DDevice()->SetTransform(D3DTS_PROJECTION, &ProjectionMatrix);

    D3DXMatrixLookAtLH(&ViewMatrix, &CameraEyePoint, &CameraLookAt, &CameraUp);
    Direct3D.GetD3DDevice()->SetTransform(D3DTS_VIEW, &ViewMatrix);

    D3DXMatrixIdentity(&WorldMatrix);
    Direct3D.GetD3DDevice()->SetTransform(D3DTS_WORLD, &WorldMatrix);

    if (CameraMoved == true)
        {
        ExtractFrustumPlanes(&ProjectionMatrix, &ViewMatrix);
        DetermineBoxVisibilities();
        Direct3D.SetTriangleIndexList();
        CameraMoved = false;
        }

    //Set up render state

    r = Direct3D.GetD3DDevice()->CaptureStateBlock(SavedStateBlock);

    r = Direct3D.GetD3DDevice()->ApplyStateBlock(LandBlockStateBlock);

    r = Direct3D.GetD3DDevice()->SetMaterial( &WorldMaterial );

    r = Direct3D.GetD3DDevice()->SetVertexShader(D3DFVF_GENERIC3DVERTEX);

    r = Direct3D.GetD3DDevice()->SetTexture(0, LandScapeTexture);

    Direct3D.GetD3DDevice()->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(AmbientLightLevelR,AmbientLightLevelG,AmbientLightLevelB));

    r = Direct3D.GetD3DDevice()->SetStreamSource(0, Direct3D.TerrainVertexBuffer, sizeof(GENERIC3DVERTEX));

    r = Direct3D.GetD3DDevice()->SetIndices(Direct3D.TerrainVertexIndexBuffer, 0);

    r = Direct3D.GetD3DDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 3*NumChildTris, 0, NumChildTris);

    r = Direct3D.GetD3DDevice()->ApplyStateBlock(SavedStateBlock);

    return S_OK;
  
Any suggestions as to why the artifacts? I can't seem to figure it out. The bounding boxes (visible vs not visible) are working just fine, I've run a bunch of tests on them. It really does seem to be the case that if the terrain tris are bigger than about 1800 triangles, everything draws just fine, but otherwise I get these artifacts where it seems like one vertex of every triangle goes somewhere it's not supposed to. Advice is much appreciated. Ron [edited by - RonHiler on February 7, 2004 8:40:44 AM] [edited by - RonHiler on February 7, 2004 8:43:14 AM]
Creation is an act of sheer will
Advertisement
One thing that seems strange to me about the code you posted is that the number of elements in your vertex buffer is the same as the number of elements in your index buffer. Usually, the index buffer is much larger than the vertex buffer.

I think that the artifacts could due to the index buffer refering to vertices that don't make up valid triangles. For example, say you have a 4x4 hightmap that you make your terrain out of:

* * * *
* * * *
* * * *
* * * *

This hightmap can be triangulated like this (forgive my ascii art):
*-*-*-*|\|\|\|*-*-*-*|\|\|\|*-*-*-*|\|\|\|*-*-*-* 

This is a total of 18 triangles. In general, the number of triangles needed to form terrain from a heightmap is (w-1)*(h-2)*2, where w is the width of the heightmap and h is the height of the heightmap.

Your vertex buffer would contain only 16 vertices. But your index buffer would be much larger. Let's say you're using a triangle list. Then your index buffer would need three indices for each triangle. Since there are 18 triangles in this sample terrain, your index buffer would contain 18*3 = 54 indices.

So basically, I think you may want to revisit how you generate your index buffer and make sure there aren't any mistakes in there.

neneboricua

[edited by - neneboricua19 on February 7, 2004 4:59:25 PM]
I understand what you are saying, and you''re right in certain respects.

The screenshot values you are referring to are somewhat misleading, at least in the manner you are reading it The vertices number is the number of UNIQUE vertices, not the total number used for drawing.

The tesselation routine I developed will always give me exactly two more vertices than the number of leaf node triangles in the terrain. You see, I start out with a quad (two tris, four verts). The quad is checked against the height map and if it is too far off, one of the tris is split down the middle (one new vert), resulting in a total of three leaf node tris and five verts. This is done recursively until the error of the tesselated terrain is some tolerance level below where the height map says it ought to be.

You are right in the respect that the number of verts fed to the renderer should be 3 times the number of tris to render for a triangle list, and this is indeed the case. It''s just that the verts are shared between terrain tris.

I''ll go back and check the values again, but I''ve run a lot of tests on them, and I think the tesselation is working fine

But thanks for the offering, it made me go back and restudy my tesellation routines again. I think I can eliminate them as a cultprit though, especially since tesselation only happens once at initialization and the terrain seem to work fine most of the time (it''s only when there are only a relatively few tris being drawn that I''m getting these artifacts).
Creation is an act of sheer will
I figured it out.

It turns out, Nen, that you were on the right track, and you nudged me in the right direction

The problem was with this line:

r = Direct3D.GetD3DDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 3*NumChildTris, 0, NumChildTris);

I took the third parameter (NumVerts) to mean the number of vertices needed to draw the triangles, which would always be 3* the number of triangles drawn.

What it actually wants is the number of verts in the VB, which in my case is a constant value (5865) because the VB never changes after initialization.

So what happened was that DIP got the incorrect number of verts in the VB. When the number of tris to draw was larger than 5865/3, this wasn''t causing any problem (because, although technically it could have overflowed, none of my IB values reference anything above 5865, so everything was okay). However, when the number of tris to draw was below this value, I got references in my IB to what was now undefined values in the VB, and I got garbage!

The correct line is:

r = Direct3D.GetD3DDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, NumVerts, 0, NumChildTris);

where NumVerts = the number of vertices in the VB (or 5865).

Whew, I''m glad that one is fixed

Ron
Creation is an act of sheer will
Glad to hear it Doesn''t it always seem that it''s the smallest bugs that cause the biggest headaches?
Yeah, you''re not kidding! I spent an entire day banging my head against the screen because of this one bug
Creation is an act of sheer will

This topic is closed to new replies.

Advertisement