动态地形块生成
On-the-fly terrain chunk generation
本文关键字:动态 更新时间:2023-10-16
我正在编写一个可以使用噪音函数生成景观的引擎,并在玩家在地形周围移动时加载新的块。我花了两天的大部分时间来弄清楚如何将这些块放置在正确的位置,这样它们就不会重叠或被放置在现有块的顶部。它在功能上运行得很好,但是当你在离玩家越远的地方生成砖块时,它的性能就会受到很大的影响(例如,如果你在玩家周围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);
}
}
您是否尝试分析它以准确地找出瓶颈在哪里?
你是否需要检查所有这些块,或者你是否可以检查玩家正在看的方向,只生成那些在视野中的?
如果你在显示之前每帧生成一次,那么你为什么要先绘制最靠近玩家的块呢?跳过对它们进行排序的阶段可能会释放一些处理能力。
是否有任何理由不能将前两个循环组合起来,以创建一个需要生成的块向量?
听起来你在渲染线程上做了太多的工作(即构建块)。如果你可以快速完成3块半径的工作,你应该将其限制在每帧。在每种情况下,每帧你要生成多少块?
我将假设生成每个块是独立的,因此,您可能可以将工作转移到另一个线程-然后在准备好时显示块。
相关文章:
- std::向量与传递值的动态数组
- 在c++中用vector填充一个简单的动态数组
- C++中的动态铸造故障
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 内联映射初始化的动态atexit析构函数崩溃
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 控制允许动态运行c++的并发操作数
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 输出没有重复元素的动态数组(收缩数组)C++
- C++为线程工作动态地分割例程
- 正在插入动态数组
- 在c++中使用动态分配的问题
- C++中的动态对象与非动态对象
- 如何在动态数组上使用搜索函数
- 视觉studo 2019中的漫画和静态/动态绑定
- 从C++中的数字输入动态创建矩阵
- 如何从QToolBox中动态创建的QLineEdit中获取文本
- C++ 动态数组每次添加时将大小增加 1 - 错误
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁