Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

RonHiler

Artifacts using DrawIndexedPrimitive

This topic is 5369 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!