即时地形块生成

时间:2011-11-08 23:06:24

标签: c++ optimization

我正在编写一个可以使用噪声函数生成景观的引擎,并在玩家在地形上移动时加载新的块。我花了两天的时间来确定如何将这些块放在正确的位置,这样它们就不会重叠或放在现有块的顶部。它在功能上运行良好,但是从玩家生成块的距离越远,就会产生巨大的性能影响(例如,如果你在玩家周围产生3个半径的半径,它会快速点亮,但如果你将其增加到半径20块很快就慢了。)

我确切地知道为什么会这样,但我想不出有任何其他方法可以做到这一点。在我进一步讨论之前,这里是我正在使用的代码,希望它的评论能够很好地理解:

    // Get the player's position rounded to the nearest chunk on the grid.
    D3DXVECTOR3 roundedPlayerPos(SnapToMultiple(m_Dx->m_Camera->GetPosition().x, CHUNK_X), 0, SnapToMultiple(m_Dx->m_Camera->GetPosition().z, CHUNK_Z));

    // Iterate through every point on an invisible grid. At each point, check if it is
    // inside a circle the size of the grid (so we generate chunks in a circle around
    // the player, not a square). At each point that is inside the circle, add a chunk to
    // the ChunksToAdd vector.
    for (int x = -CHUNK_RANGE-1; x <= CHUNK_RANGE; x++)
    {
        for (int z = -CHUNK_RANGE-1; z <= CHUNK_RANGE; z++)
        {
            if (IsInside(roundedPlayerPos, CHUNK_X*CHUNK_RANGE, D3DXVECTOR3(roundedPlayerPos.x+x*CHUNK_X, 0, roundedPlayerPos.z+z*CHUNK_Z)))
            {
                Chunk chunkToAdd;
                chunkToAdd.chunk = 0;
                chunkToAdd.position = D3DXVECTOR3((roundedPlayerPos.x + x*CHUNK_X), 0, (roundedPlayerPos.z + z*CHUNK_Z));
                chunkToAdd.chunkExists = false;
                m_ChunksToAdd.push_back(chunkToAdd);
            }
        }
    }

    // Iterate through the ChunksToAdd vector. For each chunk in this vector, compare it's
    // position to every chunk in the Chunks vector (which stores each generated chunk).
    // If the statement returns true, then there is already a chunk at that location, and
    // we don't need to generate another.
    for (i = 0; i < m_ChunksToAdd.size(); i++)
    {
        for (int j = 0; j < m_Chunks.size(); j++)
        {
            // Check the chunk in the ChunksToAdd vector with the chunk in the Chunks vector (chunks which are already generated).
            if (m_ChunksToAdd[i].position.x == m_Chunks[j].position.x && m_ChunksToAdd[i].position.z == m_Chunks[j].position.z)
            {
                m_ChunksToAdd[i].chunkExists = true;
            }
        }
    }

    // Determine the closest chunk to the player, so we can generate that first.
    // Iterate through the ChunksToAdd vector, and if the vector doesn't exist (if it
    // does exist, we're not going to generate it so ignore it), compare the current (i)
    // chunk against the current closest chunk. If it is larger, move on, and if it is 
    // smaller, store it's position as the new smallest chunk.
    int closest = 0; 
    for (j = 0; j < m_ChunksToAdd.size(); j++)
    {
        if (!m_ChunksToAdd[j].chunkExists)
        {
            // Get the distance from the player to the chunk for the current closest chunk, and
            // the chunk being tested.
            float x1 = ABS(DistanceFrom(roundedPlayerPos, m_ChunksToAdd[j].position));
            float x2 = ABS(DistanceFrom(roundedPlayerPos, m_ChunksToAdd[closest].position));
            // If the chunk being tested is closer to the player, make it the new closest chunk.
            if (x1 <= x2)
                closest = j;
        }
    }
    // After determining the position of the closest chunk, generate the volume and mesh, and add it
    // to the Chunks vector for rendering.
    if (!m_ChunksToAdd[closest].chunkExists) // Only add it if the chunk doesn't already exist in the Chunks vector.
    {
        Chunk chunk;
        chunk.chunk = new chunkClass;
        chunk.chunk->m_Position = m_ChunksToAdd[closest].position;
        chunk.chunk->GenerateVolume(m_Simplex);
        chunk.chunk->GenerateMesh(m_Dx->GetDevice());
        chunk.position = m_ChunksToAdd[closest].position;
        chunk.chunkExists = true;
        m_Chunks.push_back(chunk);
    }
    // Clear the ChunksToAdd vector ready for another frame.
    m_ChunksToAdd.clear();

(if it wasn't already obvious, this is run every frame.)

The problem area is to do with the CHUNK_RANGE variable. The larger this value, the more the first two loops are iterated through each frame, slowing the whole thing down tremendously. I need some advice or suggestions on how to do this more efficiently, thanks.

编辑:这是一些改进的代码:

// Get the player's position rounded to the nearest chunk on the grid.
D3DXVECTOR3 roundedPlayerPos(SnapToMultiple(m_Dx->m_Camera->GetPosition().x, CHUNK_X), 0, SnapToMultiple(m_Dx->m_Camera->GetPosition().z, CHUNK_Z));

// Find if the player has changed into another chunk, if they have, we will scan 
// to see if more chunks need to be generated.
static D3DXVECTOR3 roundedPlayerPosOld = roundedPlayerPos;
static bool playerPosChanged = true;
if (roundedPlayerPosOld != roundedPlayerPos)
{
    roundedPlayerPosOld = roundedPlayerPos;
    playerPosChanged = true;
}

// Iterate through every point on an invisible grid. At each point, check if it is
// inside a circle the size of the grid (so we generate chunks in a circle around
// the player, not a square). At each point that is inside the circle, add a chunk to
// the ChunksToAdd vector.
if (playerPosChanged)
{
    m_ChunksToAdd.clear();
    for (int x = -CHUNK_CREATE_RANGE-1; x <= CHUNK_CREATE_RANGE; x++)
    {
        for (int z = -CHUNK_CREATE_RANGE-1; z <= CHUNK_CREATE_RANGE; z++)
        {
            if (IsInside(roundedPlayerPos, CHUNK_X*CHUNK_CREATE_RANGE, D3DXVECTOR3(roundedPlayerPos.x+x*CHUNK_X, 0, roundedPlayerPos.z+z*CHUNK_Z)))
            {
                bool chunkExists = false;
                for (int j = 0; j < m_Chunks.size(); j++)
                {
                    // Check the chunk in the ChunksToAdd vector with the chunk in the Chunks vector (chunks which are already generated).
                    if ((roundedPlayerPos.x + x*CHUNK_X) == m_Chunks[j].position.x && (roundedPlayerPos.z + z*CHUNK_Z) == m_Chunks[j].position.z)
                    {
                        chunkExists = true;
                        break;
                    }
                }
                if (!chunkExists)
                {
                    Chunk chunkToAdd;
                    chunkToAdd.chunk = 0;
                    chunkToAdd.position = D3DXVECTOR3((roundedPlayerPos.x + x*CHUNK_X), 0, (roundedPlayerPos.z + z*CHUNK_Z));
                    m_ChunksToAdd.push_back(chunkToAdd);
                }
            }
        }
    }
}
playerPosChanged = false;

// If there are chunks to render.
if (m_ChunksToAdd.size() > 0)
{
    // Determine the closest chunk to the player, so we can generate that first.
    // Iterate through the ChunksToAdd vector, and if the vector doesn't exist (if it
    // does exist, we're not going to generate it so ignore it), compare the current (i)
    // chunk against the current closest chunk. If it is larger, move on, and if it is 
    // smaller, store it's position as the new smallest chunk.
    int closest = 0; 
    for (j = 0; j < m_ChunksToAdd.size(); j++)
    {
        // Get the distance from the player to the chunk for the current closest chunk, and
        // the chunk being tested.
        float x1 = ABS(DistanceFrom(roundedPlayerPos, m_ChunksToAdd[j].position));
        float x2 = ABS(DistanceFrom(roundedPlayerPos, m_ChunksToAdd[closest].position));
        // If the chunk being tested is closer to the player, make it the new closest chunk.
        if (x1 <= x2)
            closest = j;
    }

    // After determining the position of the closest chunk, generate the volume and mesh, and add it
    // to the Chunks vector for rendering.
    Chunk chunk;
    chunk.chunk = new chunkClass;
    chunk.chunk->m_Position = m_ChunksToAdd[closest].position;
    chunk.chunk->GenerateVolume(m_Simplex);
    chunk.chunk->GenerateMesh(m_Dx->GetDevice());
    chunk.position = m_ChunksToAdd[closest].position;
    m_Chunks.push_back(chunk);
    m_ChunksToAdd.erase(m_ChunksToAdd.begin()+closest);
}

// Remove chunks that are far away from the player.
for (i = 0; i < m_Chunks.size(); i++)
{
    if (DistanceFrom(roundedPlayerPos, m_Chunks[i].position) > (CHUNK_REMOVE_RANGE*CHUNK_X)*(CHUNK_REMOVE_RANGE*CHUNK_X))
    {
        m_Chunks[i].chunk->Shutdown();
        delete m_Chunks[i].chunk;
        m_Chunks[i].chunk = 0;
        m_Chunks.erase(m_Chunks.begin()+i);
    }
}

2 个答案:

答案 0 :(得分:2)

您是否尝试过对其进行分析以确定哪里瓶颈?

您是否需要检查所有这些块或者您是否可以通过检查玩家正在查看的方向并仅生成视图中的方向?

如果你在显示之前每帧生成一次,那么你有没有理由先绘制最接近玩家的块?跳过对它们进行排序的阶段可能会释放一些处理能力。

有没有理由你不能将前两个循环组合起来创建一个需要生成的块的向量?

答案 1 :(得分:1)

听起来你正试图在渲染线程上做太多的工作(即构建块)。如果你可以非常快地完成三块半径的工作,你应该将其限制为每帧。你想在每种情况下每帧产生多少块?

我将假设生成每个块是独立的,因此,您可以将工作移动到另一个线程 - 然后在准备好时显示块。

相关问题