关于freeglut:opengl:如何显示彗星及其追踪的路径?

opengl: How to display a comet and path traced by it?

我正在尝试使用 freeglut 进行一些 opengl 编程。假设一个在 3D 空间中移动的对象(彗星/飞机),我想将其追踪的路径渲染为点云(表示飞机留下的白色轨迹)。

我的问题是,每次调用 glutDispFunc 时,我都需要使用 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) 清除屏幕,以便让飞机移动(我正在使用键盘功能来改变飞机的位置)。但是,我还需要显示大量点,这些点会随着飞机移动而不断累积。我尝试过使用

清除屏幕

1
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

随着点数的增加会导致性能严重下降,但可以显示移动平面以及点云。有谁知道如何处理这种情况?


是的,您每次都清除屏幕并重新绘制所有内容。这就是游戏引擎的工作量。

However I also need to display a huge number of points which go on accumulating as the plane moves.I have tried with clear screen using

你可能试图绘制太多的粒子。 100 个粒子(或几百个,但不到一千个)应该足以绘制彗星轨迹。只需 200 (BIG) 个粒子,您就可以制作出令人信服的云。

另外,绘制彗星轨迹不需要积分。使用 alpha 混合,您可以使用三角形轻松绘制半透明的光剑状线条。

类光剑、等离子、类火焰应使用任何带有少量白色的颜色(例如 rgb 255、10、10),并且传统上使用 glBlendFunc(GL_SRC_ALPHA, GL_ONE)

绘制

这里有几个例子。示例显示您应该使用的三角形网格。数字表示阿尔法。 0 - 完全透明,1 - 完全不透明。必要时,斜线表示三角测量。"|"和"-"表示面边缘:

发光线

1
2
3
4
5
6
7
8
9
10
11
12
13
 0--0--0--0
 | \\|  | /|
 0--1--1--0
 | /|  | \\|
 0--0--0--0

 repeat
 0--0
 |  |
 1--1
 |  |
 0--0
 to add more segments

发光的粗线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 0--0--0--0
 | \\|  | /|
 0--1--1--0
 |  |  |  |
 0--1--1--0
 | /|  | \\|
 0--0--0--0
repeat
 0--0
 |  |
 1--1
 |  |
 1--1
 |  |
 0--0
to add more segments.

您可以将类似的东西用于彗星轨迹。并添加一些小颗粒以产生火花或其他东西。
也不要忘记纹理。使用纹理可以轻松添加更多细节,而不是尝试使用无纹理几何图形绘制所有内容。

--编辑--

aeroplane's white trail

实现任何类型的烟雾...

  • 您需要使用具有"烟熏"/"多云"纹理的非常大的纹理粒子。如果你在谷歌图片上搜索"烟雾粒子纹理",你就会明白我在说什么。
  • 那些"大"粒子通常不能使用点sprite绘制(因为它可以在近距离比屏幕大)并且需要"广告牌"。广告牌是一个始终面向摄像机的正方形。
  • 理想情况下,烟雾纹理的颜色通道应该是完全白色的(因此您可以使用顶点颜色为单个粒子着色),并且"烟雾"应该绘制在 alpha 上。
  • 绘制烟雾粒子时应按深度排序,并应从最远到最近(从相机)渲染。插入排序算法适用于"部分排序"的数据。
  • 应该在禁用深度写入的情况下渲染烟雾粒子(glDepthMask(0),如果我没记错的话)
  • 烟雾粒子应使用 glBlendFunc(GL_SRC_ALPHA, GL_INV_SRC_ALPHA) 混合。
  • 但是,如果烟雾中有火焰,火焰应该使用glBlendFunc(GL_SRC_ALPHA, GL_ONE)。火焰粒子应该混合在一起(即与它们一起按深度排序),或者在烟雾之后绘制(如果它使用单独的粒子系统)。
  • 为了使烟雾逼真,为所有粒子指定小的初始速度,随着时间的推移慢慢增加它们的大小,并使它们变得更加透明。当粒子变得完全透明时,将它们从粒子系统中移除。
  • 绘制半透明 alpha 混合曲面时,启用 alpha-test 并用 alpha==0 截断像素。这将加快渲染速度。 (重要,因为即使在体面的硬件上,你也可以用太多的烟雾粒子杀死帧率)
  • 使用许多广告牌烟雾粒子可能会很昂贵,特别是如果它们非常接近,所以如果你想用这种烟雾填充大面积并且粒子会很大并且漂浮在眼睛水平,用半透明表面覆盖整个屏幕区域多个有时,您可能想研究其他一些体积雾技术。
  • 不要使用矩阵移动单个粒子。不要每次调用绘制一个粒子。一口气把它们全部画出来,这样会快得多。

  • 是的,大多数游戏都是通过擦除屏幕并在每一帧重新绘制它来工作的。但是,也可以减少每帧的工作量。屏幕上除了轨迹还有别的吗?这条路应该有多长?它是否必须随着时间的推移保持恒定的长度(而不是像鼠标轨迹那样随着时间的推移逐渐消失)?

    如果您有能力淡出您的踪迹,您可以使用缓冲反馈。此技术在相关屏幕(或对象轨迹)上呈现半透明四边形,其效果是将尾部的最远部分淡化为背景颜色。然后在新位置绘制对象,再次创建尾部最亮的部分。这是一个相当常见的演示/可视化效果,可以创建非常漂亮的尾巴,每帧绘制开销很小。

    这可能不是你所需要的(我在你澄清你的需要之前写了这个作为对@SigTerm 答案的评论),但我将把它留在这里,因为其他人可能需要这种"线索"。


    如果您还没有使用它们,您应该使用 glDrawArrays 进行绘制,例如,它采用一组点,并且只需一个函数调用即可绘制所有内容。它比直接绘图 API(glBegin 和 glEnd)快得多。你可以这样设置:

    1
    2
    3
    4
    float points[100][3];
    int pointsCount = 0;
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), points);

    然后,每次对象"产生"一个点(例如,每一帧),您可以将其位置添加到数组中:

    1
    2
    3
    4
    points[pointsCount][0] = object.x;
    points[pointsCount][1] = object.y;
    points[pointsCount][2] = object.z;
    pointsCount++;

    然后,像这样画它们:

    1
    glDrawArrays(GL_POINTS, 0, pointsCount);

    当然,当数组填满时你必须做一些事情,但你明白了。

    还有 VBO,可以进一步提高速度,但是是更高级的功能。

    编辑:是的,您应该使用此方法重绘所有内容。我会说它仍然不会比 glBegin/End 影响更多的点数。根据此处的答案,您应该能够使用 glBufferData 调整缓冲区的大小,但可能必须将当前数据存储在数组中,并在调整大小后将其传回。