c# - 当前的视锥体剔除效果会导致 openTk 中的对象闪烁

标签 c# opengl opentk

我的视锥体剔除方式存在一些问题。目前的方法确实解决了剔除问题,但有一个非常奇怪的效果。当我距离主要父对象太近时,我使用场景图来渲染所有内容,对象开始快速闪烁。当我搬走时,这个就消失了。我已经尝试了很多事情,但没有任何想法了。你们有什么想法吗?非常感谢任何帮助。

我首先创建一个由模型周围的八个点组成的边界框。经过大量测试后,据我所知,这些是正确的。

这是我计算平截头体平面点的方法。 相机位置是世界空间中的位置,方向是它所观察的方向。此外,每次旋转相机时都会计算cameraUp和cameraRight。

    Vector3 pos = Camera.staticPosition;
    Vector3 view = Camera.staticOrientation;
    Vector3 upVector3 = Camera.cameraUp;
    Vector3 rightVector3 = Camera.cameraRight;
    float toRadians = (float)Math.PI / 180.0f;
    float nearDis = .1f;
    float farDistance = 1000f;
    float fov = Game.FOV;
    float aspectRatio = 1.3f;

    //Get with and height of near and far plane
    float tanDiv = 2 * (float) Math.Tan(fov*toRadians / 2);
    float heightNear = tanDiv * nearDis;
    float widthNear = heightNear * aspectRatio;

    float heightFar = tanDiv * farDistance;
    float widthFar = heightFar * aspectRatio;

    // get the centre points of the planes so they can be used to calculate the edge points
    Vector3 centreNear = pos + view * nearDis;
    Vector3 centreFar = pos + view * farDistance;

    // get the halfht values of the width and hegiht to make sure you can get the points
    float hNearHalf = heightNear / 2;
    float wNearHalf = widthNear / 2;
    float hFarHalf = heightFar / 2;
    float wFarHalf = widthFar / 2;

    Vector3 nearTopLeft = centreNear + (upVector3 * hNearHalf) - (rightVector3 * wNearHalf);
    Vector3 nearTopRight = centreNear + (upVector3 * hNearHalf) + (rightVector3 * wNearHalf);
    Vector3 nearBottomLeft = centreNear - (upVector3 * hNearHalf) - (rightVector3 * wNearHalf);
    Vector3 nearBottomRight = centreNear - (upVector3 * hNearHalf) + (rightVector3 * wNearHalf);

    Vector3 farTopLeft = centreFar + (upVector3 * hFarHalf) - (rightVector3 * wFarHalf);
    Vector3 farTopRight = centreFar + (upVector3 * hFarHalf) + (rightVector3 * wFarHalf);
    Vector3 farBotomLeft = centreFar - (upVector3 * hFarHalf) - (rightVector3 * wFarHalf);
    Vector3 farBottomRight = centreFar - (upVector3 * hFarHalf) + (rightVector3 * wFarHalf);

我将截锥体点存储在数组中。首先是近平面点,然后是远平面、顶平面、底平面、左平面,最后是右平面。然后我循环遍历边界框的所有六个平面和 8 个点,如果该点位于平面的右侧,则增加字典中该键的值。

    Vector3[] frustumPoints = new Vector3[18]
    {
        nearTopLeft, nearTopRight, nearBottomLeft, farTopLeft, farTopRight, farBotomLeft, nearTopLeft, farTopLeft,
        nearTopRight, nearBottomLeft, farBotomLeft, nearBottomRight, nearTopLeft, nearBottomLeft, farTopLeft,
        nearTopRight, nearBottomRight, farTopRight
    };
    Dictionary<Vector3, int> count = new Dictionary<Vector3, int>(8);
    for (int value = 0; value < 8; value++)
    {
        count.Add(cubePositions[value], 0);
    }

    for (int x = 0; x < 18; x += 3)
    {
        Vector3 normal = NormalPlane(frustumPoints[x], frustumPoints[x + 1], frustumPoints[x + 2]);

        for (int y = 0; y < 8; y++)
        {
            Vector3 pointPlane = frustumPoints[x] - cubePositions[y];
            float dot = Vector3.Dot(pointPlane, normal);


            if (dot <= 0 && x % 6 == 0)
            {
                count[cubePositions[y]]++;
            }
            else if (dot >= 0 && x % 3 == 0)
            {
                count[cubePositions[y]]++;
            }

        }
    }

这是我获取平面法线的方法

   Vector3 NormalPlane(Vector3 pointOne, Vector3 pointTwo, Vector3 pointThree)
{
    Vector3 normal;
    Vector3 edgeOne = pointTwo - pointOne; // calculate vector from point one to point two
    Vector3 edgeTwo = pointThree - pointOne; // calculate vector from point one to point three

    normal = Vector3.Normalize(Vector3.Cross(edgeOne, edgeTwo)); // calculate the cross product of the two given vectors. Then normalize it so you have normal of plane

    return normal; // return the normal 
}

如果立方体上的这些点之一的计数为六,则这些点位于所有平面内,因此截头体和我绘制该对象。如果没有一个点等于 6,则不会绘制对象。

问题是我不知道我在哪里犯了错误,所以你们有什么想法吗?

提前致谢,

杰罗梅尔

最佳答案

If for one of these points on the cube the count is six, the points is within all planes and thus the frustum and I draw the object. If none of the points is equal to 6 the objects don't get drawn.

你的逻辑似乎是

  • “如果立方体没有顶点位于截锥体内,则立方体不可见。”

但是。这是错误的。即使立方体的所有 8 个顶点都位于平截头体之外,立方体仍然可以与平截头体相交,如以下 2D 草图所示:

                  *----------*
                  |          |
+-----------------+--+       |
 \                | /        |
  \               |/         |
   \              /          |
    \            /|          |
     \          / *----------*
      \        /
       \      /
        +----+

因此,您会剔除可能可见的内容。

视锥体剔除的通常逻辑是,仅当盒子的所有顶点都被同一平面拒绝时才剔除盒子。同一个平面。 (这会导致一些奇怪的情况,即盒子完全在外面并且没有被剔除,但这些情况不太可能发生,通常也不是什么大问题。)

关于c# - 当前的视锥体剔除效果会导致 openTk 中的对象闪烁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44735084/

相关文章:

java - 在 Unity Android 插件中访问和使用图像

c# - 使用 Zip 库创建 Epub 文件

c# - Observable.Range 被重复?

c++ - OpenGL 用鼠标移动画圆

c# - OpenGL - 性能差且标度值高

c# - 排除以特定字符开头的正则表达式匹配

c++ - 颜色和纹理如何协同工作?

c++ - 无法将 id 分配给 OpenGL 中的属性

android - 使用 OpenTK 和 MonoDroid 加载 png

c# - OpenTK 和 GTK# 的 GLWidget 可靠吗?