【问题标题】:Terrain Collision issues地形碰撞问题
【发布时间】:2016-11-27 10:30:12
【问题描述】:

我正在尝试为我的高度图地形实现地形碰撞,我正在关注this。本教程是针对java的,但我使用的是C++,虽然原理是一样的,所以应该不是问题。

首先,我们需要一个函数来根据相机的位置获取地形的高度。 WorldX 和 WorldZ 是相机的位置 (x, z),heights 是一个包含所有顶点高度的二维数组。

float HeightMap::getHeightOfTerrain(float worldX, float worldZ, float heights[][256])
{   
    //Image is (256 x 256)
    float gridLength = 256;
    float terrainLength = 256;

    float terrainX = worldX;
    float terrainZ = worldZ;
    float gridSquareLength = terrainLength / ((float)gridLength - 1);
    int gridX = (int)std::floor(terrainX / gridSquareLength);
    int gridZ = (int)std::floor(terrainZ / gridSquareLength);

    //Check if position is on the terrain
    if (gridX >= gridLength - 1 || gridZ >= gridLength - 1 || gridX < 0 || gridZ < 0)
    {
        return 0;
    }

    //Find out where the player is on the grid square
    float xCoord = std::fmod(terrainX, gridSquareLength) / gridSquareLength;
    float zCoord = std::fmod(terrainZ, gridSquareLength) / gridSquareLength;
    float answer = 0.0;

    //Top triangle of a square else the bottom
    if (xCoord <= (1 - zCoord))
    {
        answer = barryCentric(glm::vec3(0, heights[gridX][gridZ], 0),
        glm::vec3(1, heights[gridX + 1][gridZ], 0), glm::vec3(0, heights[gridX][gridZ + 1], 1),
        glm::vec2(xCoord, zCoord));
    }

    else 
    {
        answer = barryCentric(glm::vec3(1, heights[gridX + 1][gridZ], 0),
        glm::vec3(1, heights[gridX + 1][gridZ + 1], 1), glm::vec3(0, heights[gridX][gridZ + 1], 1),
        glm::vec2(xCoord, zCoord));
    }

    return answer;
} 

要找到相机当前所在三角形的高度,我们使用 baryCentric 插值函数。

float HeightMap::barryCentric(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, glm::vec2 pos)
{
    float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
    float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
    float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
    float l3 = 1.0f - l1 - l2;
    return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}

然后我们只需要使用我们计算的高度来检查 比赛中的碰撞

float terrainHeight = heightMap.getHeightOfTerrain(camera.Position.x, camera.Position.z, heights);
    if (camera.Position.y < terrainHeight)
    {
        camera.Position.y = terrainHeight;
    };

现在根据教程,这应该可以正常工作,但是高度相当偏离,在某些地方甚至无法正常工作。我认为这可能与地形的平移和缩放部分有关

    glm::mat4 model;
    model = glm::translate(model, glm::vec3(0.0f, -0.3f, -15.0f));
    model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));

我应该将 heights 数组的值乘以 0.1,因为缩放会在 GPU 端对地形进行那部分,但这并没有起到作用。

注意

在教程中,getHeightOfTerrain 函数的第一行说

float terrainX = worldX - x;
float terrainZ = worldZ - z;

其中 x 和 z 是地形的世界位置。这样做是为了获取玩家相对于地形位置的位置。我尝试使用翻译部分的值,但它也不起作用。我更改了这些行,因为它似乎没有必要。

【问题讨论】:

    标签: c++ math collision-detection terrain


    【解决方案1】:
    float terrainX = worldX - x;
    float terrainZ = worldZ - z;
    

    事实上,这些线是非常必要的,除非你的地形总是在原点。

    您的代码资源(教程)假定您没有以任何方式缩放或旋转地形。 xz 变量是地形的 XZ 位置,用于处理地形平移的情况。

    理想情况下,您应该将世界位置向量从世界空间转换到物体空间(使用用于地形的 model 矩阵的逆矩阵),类似于

    vec3 localPosition = inverse(model) * vec4(worldPosition, 1)
    

    然后使用localPosition.xlocalPosition.z 代替terrainXterrainZ

    【讨论】:

    • 我明白了,现在我不知道这听起来是不是很傻,但是我怎么能找到 Worldposition 向量,因为我只有一个偏移本地空间位置的平移向量,但我没有知道局部空间位置,因为它对于每个顶点都是不同的,并且 GPU 使用这个平移向量单独偏移每个顶点?如果你明白,我的意思是什么? @Evil Tak
    • 只需要地形左下角的世界空间位置(假设X、Y、Z轴分别指向右、上、前)。
    • 这可以通过检查向量的第一个索引和所有顶点然后翻译它来找到:(0.0, 0.89, 0.0) + (0.0, -0.3, -15.0) = (0.0, 0.59,-15.0)? (如果我不做任何缩放)@Evil Tak
    • 是的。不要翻译它,而是用model 矩阵变换(相乘)它。您还应该将地形存储为一个实体(就像您/本教程对相机所做的那样),具有位置、旋转和缩放等公共属性。渲染地形时,请确保在该位置渲染左下角。左下角应该是地形的支点。你也可以让它保持世界空间位置为地形的中心,但这需要你稍微改变你的范围检查代码。
    • 有效!感谢所有的帮助。我会把你的帖子标记为答案。 @Evil Tak
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多