如何在 JOGL 中正确使用光照?

How do I use lighting correctly in JOGL?

下面是我在实际项目中遇到的一个问题的小demo

我的显示方法是这样的:

@Override
public void display(GLAutoDrawable drawable) {
    gl2.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

    gl2.glEnable(GL2.GL_DEPTH_TEST);
    gl2.glEnable(GL2.GL_LIGHTING);

    gl2.glMatrixMode(GL2.GL_MODELVIEW);
    gl2.glLoadIdentity();

    gl2.glMatrixMode(GL2.GL_PROJECTION);
    gl2.glLoadIdentity();

    gl2.glMatrixMode(GL2.GL_MODELVIEW);
    glu.gluLookAt(10, 10, 11, 10, 10, 9, 0, 1, 0);
    gl2.glMatrixMode(GL2.GL_PROJECTION);
    glu.gluPerspective(90, 1, 0.1, 1000.1);
    gl2.glMatrixMode(GL2.GL_MODELVIEW);

    gl2.glRotated(angle++, 0, 1, 0);

    light();

    wall1();
    wall2();
    wall3();

    gl2.glFlush();
}

灯光放在景物中间:

private void light() {
    float[] position = { 10, 10, 20, 1 };
    gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, position, 0);

    gl2.glEnable(GL2.GL_LIGHT0);
}

风景由三面墙组成:

private void wall1() {
    float[] colors = { 0, 1, 0, 1 };
    gl2.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE, colors, 0);

    gl2.glBegin(GL2.GL_QUADS);

    for (int i = 0; i < 20; i++) {
    for (int j = 0; j < 20; j++) {
        gl2.glNormal3d(0, 0, 1);
        gl2.glVertex3d(i, j, 0);

        gl2.glNormal3d(0, 0, 1);
        gl2.glVertex3d(i + 1, j, 0);

        gl2.glNormal3d(0, 0, 1);
        gl2.glVertex3d(i + 1, j + 1, 0);

        gl2.glNormal3d(0, 0, 1);
        gl2.glVertex3d(i, j + 1, 0);
    }
    }

    gl2.glEnd();
}

private void wall2() {
    float[] colors = { 0, 1, 0, 1 };
    gl2.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE, colors, 0);

    gl2.glBegin(GL2.GL_QUADS);

    for (int i = 0; i < 20; i++) {
    for (int j = 0; j < 40; j++) {
        gl2.glNormal3d(-1, 0, 0);
        gl2.glVertex3d(20, i, j);

        gl2.glNormal3d(-1, 0, 0);
        gl2.glVertex3d(20, i + 1, j);

        gl2.glNormal3d(-1, 0, 0);
        gl2.glVertex3d(20, i + 1, j + 1);

        gl2.glNormal3d(-1, 0, 0);
        gl2.glVertex3d(20, i, j + 1);
    }
    }

    gl2.glEnd();
}

private void wall3() {
    float[] colors = { 0, 1, 0, 1 };
    gl2.glMaterialfv(GL2.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE, colors, 0);

    gl2.glBegin(GL2.GL_QUADS);

    for (int i = 0; i < 20; i++) {
    for (int j = 0; j < 20; j++) {
        gl2.glNormal3d(0, 0, -1);
        gl2.glVertex3d(20 - i, j, 40);

        gl2.glNormal3d(0, 0, -1);
        gl2.glVertex3d(20 - i - 1, j, 40);

        gl2.glNormal3d(0, 0, -1);
        gl2.glVertex3d(20 - i - 1, j + 1, 40);

        gl2.glNormal3d(0, 0, -1);
        gl2.glVertex3d(20 - i, j + 1, 40);
    }
    }

    gl2.glEnd();
}

最后,结果是这样的:

可以看到中间的大墙(wall2)如期亮起,而左边(wall1)和右边([=15)的两个小墙=]) 似乎获得了最大的光线,这对我来说似乎不太现实。

我该如何解决这个问题?

提前致谢!

OpenGL 中弃用的标准光源模型非常简陋。它将使用距离和角度来减少表面从光源接收到的光量。在这种情况下,较大表面的远端接收到的光比较小表面少,因为光的角度更锐利。

另一个问题是 OpenGL 将计算每个顶点的光照值,然后为每个像素插值,根据面及其顶点的大小和布局为您提供不同的结果。

是的,这看起来不切实际,这是您不应使用过时和弃用功能的众多原因之一。如果您坚持使用已弃用的 OpenGL 功能,那么您就会遇到如此糟糕的照明。

您可以尝试使用 GL_CONSTANT_ATTENUATIONGL_LINEAR_ATTENUATIONGL_QUADRATIC_ATTENUATION 以获得特定渲染场景的最佳结果,或者您可以尝试使用聚光灯而不是位置灯,但您可能永远不会得到完美的解决方案。见 docs for glLight.

您也可以尝试更改 faces/vertices 的大小和布局,使插值结果更接近您的期望。

但是,正确的解决方案是使用着色器实现您自己的基于像素的照明或使用提供适当照明的第三方库。