如何正确设置 OpenGL 场景以可视化单个对象

发布于 2024-07-14 18:19:24 字数 2694 浏览 10 评论 0原文

我需要为我的网格工具包编写一个简单的可视化工具。 我正在使用的对象始终位于 [-1,1]^3 框(包含)内,因此我需要确保该对象对用户完全可见。 我还希望能够围绕物体旋转相机,就像用户围绕物体“飞行”一样。

我就是这样做的:

static void Reshape(int w, int h)
{
    glViewport(0,0,(GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float maxDistance = sqrt(2);
    if (w <= h)
    {
        glOrtho(-maxDistance, maxDistance, -maxDistance * (GLfloat)h / (GLfloat)w,
            maxDistance * (GLfloat)h / (GLfloat)w, -6.0, 6.0);
    }
    else
    {
        glOrtho(-maxDistance * (GLfloat)w / (GLfloat)h, maxDistance * (GLfloat)w / (GLfloat)h,
            -maxDistance, maxDistance, -6.0, 6.0);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void PolarView(GLdouble distance, GLdouble twist, GLdouble elevation)
{
    double centerx, centery, centerz;
    double eyex, eyey, eyez;

    eyex = distance * cos(-twist) * cos(elevation);
    eyey = distance * sin(-twist) * cos(elevation);
    eyez = distance * sin(elevation);
    centerx = centery = centerz = 0;

    gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, 0, 0, 1);
}

在初始设置期间调用 Reshape 函数,并且在每次调整可视化工具控件大小后,在每次重绘时调用 PolarView 函数,其中角度和距离大于 3 的平方根(这真的很重要吗? )。 该代码对于像立方体或球体这样的凸面物体工作得很好,但是对于环面物体有一些问题(一些面是通过其他面看到的),所以我相信这是关于深度测试的。 我的设置有什么问题吗? 截图:
坏环面

glEnable(GL_DEPTH_TEST);
glClearDepth(1);
glPolygonMode(GL_FRONT, GL_LINE); // Changing of GL_LINE to GL_FILL doesn't fixing my problem, it just changing the appearance of the model.
glClearColor(BackColor.R / 255.0f, BackColor.G / 255.0f, BackColor.B / 255.0f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
PolarView(sqrt(3), _phi, _theta);
// .. only draws 

我的 PIXELFORMATDESCRIPTOR:

        PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        24,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        32, // Depth buffer size
        0,
        0,
        PFD_MAIN_PLANE,
        0,
        0,
        0,
        0
    };

我找到了一些解决此问题的方法:

  • 交换近平面和远平面的值
  • ,将 glDepthFunc 设置为 GL_GREATERglClearDepth0
    好的,如果有偶数个错误,我的代码将正常工作。 但是第一个错误在哪里呢?

I need to write a simple visualizer for my mesh toolkit. The objects I'm working with is always located inside [-1,1]^3 box (inclusive), so I need to ensure that object will be entirely visible by user. I also want to have a possiblity to rotate a camera around object like user is "flying" around object.

That's how I'm doing this:

static void Reshape(int w, int h)
{
    glViewport(0,0,(GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float maxDistance = sqrt(2);
    if (w <= h)
    {
        glOrtho(-maxDistance, maxDistance, -maxDistance * (GLfloat)h / (GLfloat)w,
            maxDistance * (GLfloat)h / (GLfloat)w, -6.0, 6.0);
    }
    else
    {
        glOrtho(-maxDistance * (GLfloat)w / (GLfloat)h, maxDistance * (GLfloat)w / (GLfloat)h,
            -maxDistance, maxDistance, -6.0, 6.0);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void PolarView(GLdouble distance, GLdouble twist, GLdouble elevation)
{
    double centerx, centery, centerz;
    double eyex, eyey, eyez;

    eyex = distance * cos(-twist) * cos(elevation);
    eyey = distance * sin(-twist) * cos(elevation);
    eyez = distance * sin(elevation);
    centerx = centery = centerz = 0;

    gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, 0, 0, 1);
}

The Reshape function is called during initial setup and after each resize of the visualizer control, the PolarView function is called on each redraw with some angles and distance greater than square root of 3 (is it really matters?). That code works fine with convex objects like cube or sphere, but it have some problems with torus object (Some of the faces are seen thru others), so I believe it's something about depth testing. What's wrong with my setup? Screenshot:
Bad torus

Bad torus filled

I've made some search on the internet and found that such problem can happen in case when there is something wrong with my near and far plane parameters. What is the correct values for these in my case?
My drawing procedure looks like:

glEnable(GL_DEPTH_TEST);
glClearDepth(1);
glPolygonMode(GL_FRONT, GL_LINE); // Changing of GL_LINE to GL_FILL doesn't fixing my problem, it just changing the appearance of the model.
glClearColor(BackColor.R / 255.0f, BackColor.G / 255.0f, BackColor.B / 255.0f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
PolarView(sqrt(3), _phi, _theta);
// .. only draws 

My PIXELFORMATDESCRIPTOR:

        PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        24,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        32, // Depth buffer size
        0,
        0,
        PFD_MAIN_PLANE,
        0,
        0,
        0,
        0
    };

I've found some ways to workaround this:

  • Swap values for my near and far planes
  • set glDepthFunc to GL_GREATER and glClearDepth to 0
    OK, my code will be working fine if there is even number of errors. But where is first error?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小姐丶请自重 2024-07-21 18:19:24

您读过 OpenGL 常见问题解答 12 吗?

您的代码看起来可疑的一件事是负近平面。 负 Z 值位于相机后面,这在渲染 3D 场景时通常是一个错误。 然而,这本身不会导致您所看到的问题,并且 Z 的范围 [-6,6] 应该为此类场景提供足够的精度。

您是否正在调用glEnable(GL_DEPTH_TEST)? 您是否将 GL_DEPTH_BUFFER_BIT 每帧传递给 glClear

更新:您正在调用 glPolygonMode(GL_FRONT, GL_LINE)。 这意味着正面三角形仅绘制轮廓,这意味着如果正面三角形 A 与另一个正面三角形 B 重叠,您可以透过 A 看到 B 的边缘。对于凸体,这种情况不会发生,所以你不会注意到这个问题。

如果您希望三角形遮挡其后面的三角形,则需要使用模式 GL_FILL 填充它们。 要获得线框图形,您需要使用白色填充绘制模型,然后再次使用黑色轮廓绘制模型,如下所示:

glDepthFunc(GL_LEQUAL);
glPolygonMode(GL_FRONT, GL_FILL);
/* draw the model in white */
glDepthFunc(GL_EQUAL);
glPolygonMode(GL_FRONT, GL_LINE);
/* draw the model again in black */

这个想法是,在模型的第二遍上,我们只想绘制不存在的轮廓被第一遍中绘制的一些更靠近相机(即 Z 值较低)的三角形遮挡。

另一个想法:我认为你的相机可能指向错误的方向。 这可以解释为什么使用 glOrtho 透视图绘制的场景是错误的,而不是使用 glFrustum 透视图绘制的。 在 glOrtho 情况下,整个场景都在相机后面绘制; 这就是为什么它是用错误的 Z 顺序绘制的。 当您将近平面设置为正数时,整个场景都会被剔除。

Have you read OpenGL FAQ 12?

The one thing that looks suspicious about your code is the negative near plane. Negative Z values are behind the camera, which is usually a mistake when rendering a 3D scene. However, that in itself shouldn't cause the problem you're seeing, and the range [-6,6] for Z should provide plenty of precision for this kind of scene.

Are you calling glEnable(GL_DEPTH_TEST)? Are you passing GL_DEPTH_BUFFER_BIT to glClear each frame?

Update: you're calling glPolygonMode(GL_FRONT, GL_LINE). This means that front-facing triangles are drawn in outline only, which means that if front-facing triangle A overlaps another front-facing triangle B, you can see through A to the edges of B. With a convex body this can't happen, so you don't notice the problem.

If you want triangles to occlude the triangles behind them, then you are going to need to fill them in using mode GL_FILL. To get a wireframe figure, you need to draw the model with white fill, then draw the model again with black outline, like this:

glDepthFunc(GL_LEQUAL);
glPolygonMode(GL_FRONT, GL_FILL);
/* draw the model in white */
glDepthFunc(GL_EQUAL);
glPolygonMode(GL_FRONT, GL_LINE);
/* draw the model again in black */

The idea is that on the second pass over the model, we only want to draw the outlines that are not obscured by some triangle drawn in the first pass that is closer to the camera (that is, with lower Z).

Another idea: I think your camera may be pointing the wrong way. This would explain why the scene is being drawn wrongly with the glOrtho perspective, and not at all with the glFrustum perspective. In the glOrtho case, the entire scene is being drawn behind the camera; that's why it's being drawn with the Z order the wrong way round. When you set the near plane to a positive number, the entire scene is culled.

“8.070 如何自动计算显示整个模型的视图?(我知道边界球体和向上向量。)” 条目位于 OpenGL FAQ 8 回答了我的问题。 然而,我的设置略有不同,这是我重写的 PolarView 和 Reshape 函数:

static void Reshape(int w, int h)
{
    float diameter = sqrt(3);
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLdouble zNear = 1;
    GLdouble zFar = zNear + diameter * 2;
    GLdouble left = -diameter;
    GLdouble right = diameter;
    GLdouble top = -diameter;
    GLdouble bottom = diameter;
    double aspect = (double)w / (double)h;
    if (aspect < 1)
    {
        bottom /= aspect;
        top /= aspect;
    }
    else
    {
        left *= aspect;
        right *= aspect;
    }
    glOrtho(left, right, bottom, top, zNear, zFar);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void PolarView(GLdouble twist, GLdouble elevation)
{
    float diameter = sqrt(3);
    double distance = diameter * 2;
    double centerx, centery, centerz;
    double eyex, eyey, eyez;

    eyex = distance * cos(twist) * cos(elevation);
    eyey = distance * sin(twist) * cos(elevation);
    eyez = distance * sin(elevation);
    centerx = centery = centerz = 0;
    gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, 0, 0, 1);
}  

"8.070 How can I automatically calculate a view that displays my entire model? (I know the bounding sphere and up vector.)" entry at OpenGL FAQ 8 answers my question. However, my setup is slightly different, here is my rewritten PolarView and Reshape functions:

static void Reshape(int w, int h)
{
    float diameter = sqrt(3);
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLdouble zNear = 1;
    GLdouble zFar = zNear + diameter * 2;
    GLdouble left = -diameter;
    GLdouble right = diameter;
    GLdouble top = -diameter;
    GLdouble bottom = diameter;
    double aspect = (double)w / (double)h;
    if (aspect < 1)
    {
        bottom /= aspect;
        top /= aspect;
    }
    else
    {
        left *= aspect;
        right *= aspect;
    }
    glOrtho(left, right, bottom, top, zNear, zFar);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void PolarView(GLdouble twist, GLdouble elevation)
{
    float diameter = sqrt(3);
    double distance = diameter * 2;
    double centerx, centery, centerz;
    double eyex, eyey, eyez;

    eyex = distance * cos(twist) * cos(elevation);
    eyey = distance * sin(twist) * cos(elevation);
    eyez = distance * sin(elevation);
    centerx = centery = centerz = 0;
    gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, 0, 0, 1);
}  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文