C++ 动态地形块生成

C++ 动态地形块生成,c++,optimization,C++,Optimization,我正在编写一个引擎,可以使用噪波函数生成风景,并在玩家在地形上移动时加载新的块。我花了两天的大部分时间研究如何将这些块放在正确的位置,这样它们就不会重叠或放在现有块的上面。它在功能上工作得很好,但是从播放器生成块的距离越远,性能就会受到很大的影响(例如,如果在播放器周围以3个块的半径生成块,它的发光速度就越快,但是如果将其增加到20个块的半径,速度就会非常快) 我知道这是为什么,但我想不出任何其他方法来做到这一点。在我继续之前,这里是我目前使用的代码,希望它的注释足够好,可以理解: //

我正在编写一个引擎,可以使用噪波函数生成风景,并在玩家在地形上移动时加载新的块。我花了两天的大部分时间研究如何将这些块放在正确的位置,这样它们就不会重叠或放在现有块的上面。它在功能上工作得很好,但是从播放器生成块的距离越远,性能就会受到很大的影响(例如,如果在播放器周围以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.
//将玩家的位置四舍五入到网格上最近的区块。
D3DXVECTOR3圆形播放程序(SnaptomMultiple(m_Dx->m_Camera->GetPosition().x,CHUNK_x),0,SnaptomMultiple(m_Dx->m_Camera->GetPosition().z,CHUNK_z));
//迭代通过不可见网格上的每个点。在每个点上,检查它是否正确
//在一个圆圈内,网格的大小(因此我们在一个圆圈内生成块)
//球员,而不是正方形)。在圆内的每个点上,添加一个块到
//块将添加向量。
for(int x=-CHUNK_RANGE-1;x GenerateVolume(m_Simplex);
chunk.chunk->GenerateMesh(m_Dx->GetDevice());
chunk.position=m_ChunksToAdd[closest].position;
chunk.chunkExists=true;
m_chunk.向后推(chunk);
}
//清除chunkstoaddvector以准备另一帧。
m_ChunksToAdd.clear();
(如果还不明显,则每帧运行一次。)
问题在于CHUNK_RANGE变量。该值越大,前两个循环在每一帧中迭代的次数就越多,整个过程的速度就会大大降低。我需要一些关于如何更有效地执行此操作的建议,谢谢。
编辑:以下是一些改进的代码:

// 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);
    }
}
//将玩家的位置四舍五入到网格上最近的区块。
D3DXVECTOR3圆形播放程序(SnaptomMultiple(m_Dx->m_Camera->GetPosition().x,CHUNK_x),0,SnaptomMultiple(m_Dx->m_Camera->GetPosition().z,CHUNK_z));
//查找玩家是否已更改为其他区块,如果已更改,我们将扫描
//查看是否需要生成更多的块。
静态D3DXVECTOR3 roundedPlayerPosOld=roundedPlayerPos;
静态布尔playerPosChanged=true;
if(roundedPlayerPosOld!=roundedPlayerPos)
{
roundedPlayerPosOld=roundedPlayerPos;
playerPosChanged=true;
}
//遍历不可见网格上的每个点。在每个点上,检查它是否为
//在一个圆圈内,网格的大小(因此我们在一个圆圈内生成块)
//玩家,而不是正方形)。在圆圈内的每个点上,添加一个块到
//块将添加向量。
如果(播放者更改)
{
m_ChunksToAdd.clear();

对于(int x=-CHUNK\u CREATE\u RANGE-1;x,听起来您似乎试图在渲染线程上做太多的工作(即构建块)。如果您可以非常快地完成三个块半径的工作,则应将其限制为每帧。在每种情况下,每帧要生成多少块


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

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


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

您是否尝试对其进行分析以确定瓶颈的确切位置

您是否需要检查所有这些块,或者您是否可以检查玩家正在查看的方向而只生成视图中的块

如果在显示前每帧生成一次块,那么为什么要先绘制离播放器最近的块呢?跳过对块进行排序的阶段可能会释放一些处理能力


有什么原因不能将前两个循环组合起来,只创建一个需要生成的块向量吗?

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

您是否需要检查所有这些块,或者您是否可以检查玩家正在查看的方向而只生成视图中的块

如果在显示前每帧生成一次块,那么为什么要先绘制离播放器最近的块呢?跳过对块进行排序的阶段可能会释放一些处理能力


有什么原因不能将前两个循环组合起来,只创建一个需要生成的区块向量吗?

区块有多大?区块大小是否总是相同?区块比网格大?网格是无限的,没有定义。roundedPlayerPos只是玩家的位置捕捉到网格上最近的交点。Each块是16x16(有一个高度,但这并不重要),因此它会将位置捕捉到16x16Z或32x64Z等。一个区块有多大?总是一样大吗?一个区块比你的网格大吗?网格是无限的,没有定义。roundedPlayerPos只是玩家的位置捕捉到网格上最近的交点。每个区块都是16x16(有高度,但这并不重要),所以它会将位置捕捉到16x 16z或32x 64z等。每个帧只生成一个块,是的,该部分可以删除,它只是让它在生成地形时飞到地形上方时看起来更漂亮。无论如何,我用一些改进的代码更新了OP。每个帧只生成一个块,是的,该部分可以删除