C#如何用线程来控制三维点云图的动态绘制?

使用SharpGL三位点云图的绘制时,一般是将所有的数据处理好然后通过OpenGLDraw整体绘制出来。

我现在想要用线程来控制点云图的绘制,同时要是动态的。我试过用OpenGLDraw来动态绘制,将z轴数据固定加1,第一次画一个点,然后擦除,第二次两个点,擦除,这样循环直到绘制完成。

这个方法是可以的,然后我又试了用线程代替OpenGLDraw,将每次绘制擦除的代码放到一个while循环内,但是结果有问题,OpenGLControl控件上没有显示点。

请问能否给我提供一个思路,或者在我的思路上帮我解答一下问题。

以下是我线程上挂的方法代码。

private void DynamicShow()
        {
            for (int i = 0; i < xds; i++)//xds为总点数
            {
                dynamicZ.Add(maxz[i] + i);//准备Z轴数据
            }

            while (openFileReadyFlag)
            {
                gl.LoadIdentity();//重置当前指定的矩阵为单位矩阵
                gl.Translate(_ZBx, _ZBy, _ZBz);//平移函数,参数为XYZ方向上的平移大小;最后一个参数为距离屏幕距离;

                gl.Rotate(_x, 1.0f, 0.0f, 0.0f);//绕x轴旋转_x度,_x为正表示逆时针
                gl.Rotate(_y, 0.0f, 1.0f, 0.0f);
                gl.Rotate(_z, 0.0f, 0.0f, 1.0f);

                gl.Begin(_model);
                gl.Color(1.0f, 1.0f, 0.0f);

                gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
                for (int i = 0; i < max; i++)//用max来控制使每次绘制比上一次多一个点
                {
                    gl.Vertex((maxx[i] - _ZBxT) / _ZBv, (maxy[i] - _ZByT) / _ZBv, maxz[i] / _ZBzT);//数组为点云坐标数组,其他为坐标换算参数
                }
                max++;
                Thread.Sleep(2);

                gl.End();
                if (max == xds)
                {
                    break;
                }
            }
        }

 

你的方法是通过循环不断地在OpenGL中添加和删除点来动态绘制点云图的。然而,这种方式可能会在每次迭代中花费较长的时间,因为你需要在绘图上下文中进行许多操作,包括重置矩阵、平移、旋转、清除颜色缓冲区和绘制点。

在这种情况下,使用线程可以提高绘制速度,但是你的线程函数中的操作似乎也是一个死循环,只会导致OpenGL控件一直处于绘图状态。此外,你的线程函数似乎没有使用OpenGL的双缓冲技术,这可能会导致绘图不连贯或闪烁。

下面是一种更有效的方法来实现动态绘制点云图:

  • 在主线程中创建一个点云对象,例如使用OpenGL的顶点缓冲区(VBO)或者顶点数组(VAO)。
  • 在后台线程中更新点云对象。你可以使用C#的Task或者Thread类来实现后台线程,这里我们使用Task作为示例。
  • 在主线程中调用OpenGL绘图函数,例如glDrawArrays或glDrawElements,以使用更新后的点云对象渲染场景。

以下是使用Task来实现动态绘制点云图的代码示例:

private void InitScene()
{
    // 初始化点云对象,例如使用VBO或者VAO
    // 这里使用VBO作为示例
    float[] points = new float[xds * 3]; // 每个点包含x、y、z三个分量
    // 将x、y、z坐标按顺序存入points数组
    // ...
    int vbo;
    gl.GenBuffers(1, out vbo);
    gl.BindBuffer(OpenGL.GL_ARRAY_BUFFER, vbo);
    gl.BufferData(OpenGL.GL_ARRAY_BUFFER, points.Length * sizeof(float), points, OpenGL.GL_STATIC_DRAW);
    gl.VertexPointer(3, OpenGL.GL_FLOAT, 0, IntPtr.Zero);
    gl.EnableClientState(OpenGL.GL_VERTEX_ARRAY);
}

private void StartDrawing()
{
    Task.Run(() =>
    {
        int max = 0;
        while (openFileReadyFlag)
        {
            float[] dynamicZ = new float[xds];
            for (int i = 0; i < xds; i++)
            {
                dynamicZ[i] = maxz[i] + i;
            }
            // 将dynamicZ数组的值更新到点云对象中的z坐标上
            // ...

            max++; // 增加每次绘制的点数

            Thread.Sleep(2);
            if (max >= xds) break;
        }
    });
}

private void DrawScene()
{
    gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

    gl.DrawArrays(_model, 0, max); // 渲染点云对象中的前max个点

    gl.Flush();
}


注意,在使用OpenGL绘图函数之前,需要确保已经初始化了点

  • OpenGL 帧缓冲区的清除
    在每次绘图时,需要清除帧缓冲区,以避免之前的绘制影响到当前的绘制。您在代码中使用了 gl.Clear() 函数来清除帧缓冲区,但是该函数的位置不太合适,应该将其放在循环外面,只执行一次,如下所示:

gl.LoadIdentity();//重置当前指定的矩阵为单位矩阵
gl.Translate(_ZBx, _ZBy, _ZBz);//平移函数,参数为XYZ方向上的平移大小;最后一个参数为距离屏幕距离;
gl.Rotate(_x, 1.0f, 0.0f, 0.0f);//绕x轴旋转_x度,_x为正表示逆时针
gl.Rotate(_y, 0.0f, 1.0f, 0.0f);
gl.Rotate(_z, 0.0f, 0.0f, 1.0f);
gl.Begin(_model);
gl.Color(1.0f, 1.0f, 0.0f);

gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); // 在循环外清除帧缓冲区
while (openFileReadyFlag)
{
    for (int i = 0; i < max; i++)//用max来控制使每次绘制比上一次多一个点
    {
        gl.Vertex((maxx[i] - _ZBxT) / _ZBv, (maxy[i] - _ZByT) / _ZBv, maxz[i] / _ZBzT);//数组为点云坐标数组,其他为坐标换算参数
    }
    max++;
    Thread.Sleep(2);

    gl.End();
    if (max == xds)
    {
        break;
    }
}

  • 顶点坐标的范围
    您在绘制时使用了 gl.Vertex() 函数来指定顶点坐标,但是坐标值可能超出了 OpenGL 视口的范围,导致无法显示。您可以尝试调整坐标值的范围,或者调整 OpenGL 视口的大小来解决该问题。