绘制大量三角形的有效方法(OpenGL)
Posted
技术标签:
【中文标题】绘制大量三角形的有效方法(OpenGL)【英文标题】:Efficient Way to Draw Lots of Triangles (OpenGL) 【发布时间】:2020-08-19 18:02:08 【问题描述】:我对 OpenGL 有点陌生。我正在尝试使用 Java OpenGL 和 WorldWind Java 为飞机绘制 3D 动态轨迹,我可以使用 glDrawArrays 绘制它。由于飞机的轨迹在每一帧(25fps)中都会增加,因此我将新的顶点值放入 verticeBuffer。我还使用 rightFloatBuffer 和 leftFloatBuffer 将 GL_LINE_STRIP 绘制到路径的两侧,正如您在附加的第一张图片中看到的那样。由于随着飞机飞行轨迹变得越来越长,我认为我需要为三角形(verticeBuffer)创建一个大的 FloatBuffer,为左右线创建 2 个大的 FloatBuffer。
我的第一个问题:绘制多个三角形最有效的方法是什么?根据我的代码,我认为在飞行 5 小时后,FloatBuffers 将已满。如果我尝试在每帧中使用 for 循环更新值,并且如果我同时有 50-75 架飞机,这将降低性能。因此,我在每一帧更新一个三角形。
第二个问题:我想像第二张图片那样画一条小路。如您所见,随着距离飞机越来越近,轨迹变得更加透明。当飞机变颜色时,轨迹的底部似乎有所不同。我该怎么做?
第三个问题:我使用gl.DepthMask(false) 和draw line_strip 和gl.DepthMask(true) 来绘制平滑的线条,线条之间没有间隙。但这一次首先添加到场景中的飞机轨迹总是出现在顶部,无论它是否在另一条轨迹下方。我能做些什么来克服这个问题?或者考虑到顶点的数量,我该怎么做才能绘制没有间隙的平滑线?
我绘制轨迹的代码如下:
private final FloatBuffer verticeBuffer = GLBuffers.newDirectFloatBuffer(3000000);
private final FloatBuffer rightFloatBuffer = GLBuffers.newDirectFloatBuffer(1500000);
private final FloatBuffer leftFloatBuffer = GLBuffers.newDirectFloatBuffer(1500000);
protected void drawTrail()
gl.glPushAttrib(GL2.GL_CURRENT_BIT | GL2.GL_COLOR_BUFFER_BIT | GL2.GL_LINE_BIT | GL2.GL_ENABLE_BIT
| GL2.GL_DEPTH_BUFFER_BIT);
try
gl.glEnable(GL.GL_BLEND);
gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
doDrawTrail(dc);
gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
gl.glDisable(GL.GL_BLEND);
finally
gl.glPopAttrib();
protected void doDrawTrail()
updateTrailVertices();
float[] colors = new float[]trailColor.getRed() / 255.f, trailColor.getGreen() / 255.f, trailColor.getBlue() / 255.f;
gl.glColor4f(colors[0], colors[1], colors[2], 0.6f);
gl.glEnable(GL2.GL_LINE_SMOOTH);
gl.glHint(GL2.GL_LINE_SMOOTH_HINT, GL2.GL_NICEST);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, verticeBuffer.rewind());
gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, verticeBuffer.limit() / 3);
gl.glColor3f(colors[0], colors[1], colors[2]);
gl.glLineWidth(3f);
//To draw smooth lines
gl.glDepthMask(false);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, rightFloatBuffer.rewind());
gl.glDrawArrays(GL.GL_LINE_STRIP, 0, rightFloatBuffer.limit() / 3);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, leftFloatBuffer.rewind());
gl.glDrawArrays(GL.GL_LINE_STRIP, 0, leftFloatBuffer.limit() / 3);
gl.glDepthMask(true);
protected void updateTrailVertices()
// In each frame when the aircraft position changes this function updates the last vertices
if (positionChange)
positionChange = false;
//I need to set the position and the limit of the buffers to draw only updated parts
verticeBuffer.position(lastIndex * 2);
rightFloatBuffer.position(lastIndex);
leftFloatBuffer.position(lastIndex);
verticeBuffer.limit((lastIndex * 2) + 6);
rightFloatBuffer.limit(lastIndex + 3);
leftFloatBuffer.limit(lastIndex + 3);
List<Vec4> pointEdges = computeVec4(this.currentPosition, this.currentHeading, this.currentRoll, this.span);
verticeBuffer.put((float) pointEdges.get(0).x).put((float) pointEdges.get(0).y).put((float) pointEdges.get(0).z);
verticeBuffer.put((float) pointEdges.get(1).x).put((float) pointEdges.get(1).y).put((float) pointEdges.get(1).z);
rightFloatBuffer.put((float) pointEdges.get(0).x).put((float) pointEdges.get(0).y).put((float) pointEdges.get(0).z);
leftFloatBuffer.put((float) pointEdges.get(1).x).put((float) pointEdges.get(1).y).put((float) pointEdges.get(1).z);
lastIndex = rightFloatBuffer.position();
【问题讨论】:
请将您的帖子编辑为仅一个问题。您可以在多个帖子中提出多个问题。它使其他人更容易回答您的问题或找到问题。 为什么要禁用平滑线的深度缓冲区?我认为您想使用 MSAA。 【参考方案1】:如果您可以使用几何着色器,显示飞行轨迹的最有效方法是拥有一个顶点缓冲区并将其渲染为线条。顶点缓冲区包含较早的位置和法线向量(平面向上方向)。使用这两个值,您可以将几何着色器转换为四边形。这些四边形应该包含纹理坐标,可以在片段着色器中用于显示边界。
您只需要一个绘图调用,并将存储在 gpu 上的数据减少到绝对最小值。
飞行轨迹的褪色可以通过使用带有平面坐标的制服来完成。您的一种阴影可以计算到平面的距离,并使用该像素的 alpha 值。
【讨论】:
感谢您的回答。但是如果我画线条,那么我就不能将飞机的滚动显示为一条带子。你的意思是我可以用四边形来做丝带外观吗?我只能将轨迹显示为一条线。我不知道如何在动态几何体上使用几何着色器和纹理?你有什么例子或参考资料让我看看。 几何着色器可以将您的几何图形从一条线转换为一条四边形。这是他们的一项主要应用。这是一个资源:learnopengl.com/Advanced-OpenGL/Geometry-Shader以上是关于绘制大量三角形的有效方法(OpenGL)的主要内容,如果未能解决你的问题,请参考以下文章