在OpenGL中绘制斯坦纳的罗马曲面

Drawing Steiner's Roman Surface in OpenGL

本文关键字:罗马 曲面 OpenGL 绘制      更新时间:2023-10-16

我正试图在OpenGL中绘制施泰纳的罗马表面,并且我遇到了一些问题,以获得正确的法线,以便表面正确点亮。我使用了维基百科上的参数方程:http://en.wikipedia.org/wiki/Roman_surface。对于法线,我对做偏微分,然后是,然后交叉偏微分得到法线。

由于罗马曲面是一个不可定向的曲面,这使得表面不能正常发光。因此,我想知道是否有一种方法可以得到正确的法线,以便表面可以正确点亮。我试过对整个曲面和部分曲面的法线取负(对n的第一个和最后一个四分之一取负),但似乎不起作用。

我现在的代码如下:

double getRad(double deg, double n){
    return deg * M_PI / n;
}
int n = 24;
for(int i = 0; i < n; i++){
    for(int j = 0; j < 2*n; j++){
            glBegin(GL_POLYGON);
                double x = -pow(r,4) * cos(2*getRad(i+0.5,n)) * pow(cos(getRad(j+0.5,n)),2) * cos(2*getRad(j+0.5,n)) * sin(getRad(i+0.5,n)) - 2 * pow(r,4) * pow(cos(getRad(i+0.5,n)),2) * pow(cos(getRad(j+0.5,n)),2) * sin(getRad(i+0.5,n)) * pow(sin(getRad(j+0.5,n)),2);
                double y = pow(r,4) * cos(getRad(i+0.5,n)) * cos(2*getRad(i+0.5,n)) * pow(cos(getRad(j+0.5,n)),2) * cos(2*getRad(j+0.5,n)) - 2 * pow(r,4) * cos(getRad(i+0.5,n)) * pow(cos(getRad(j+0.5,n)),2) * pow(sin(getRad(i+0.5,n)),2) * pow(sin(getRad(j+0.5,n)),2);
                double z = -pow(r,4) * pow(cos(getRad(i+0.5,n)),2) * cos(getRad(j+0.5,n)) * cos(2*getRad(j+0.5,n)) * sin(getRad(j+0.5,n)) - pow(r,4) * cos(getRad(j+0.5,n)) * cos(2*getRad(j+0.5,n)) * pow(sin(getRad(i+0.5,n)),2) * sin(getRad(j+0.5,n));

                glNormal3d(x, y, z);                
                glVertex3d(r*r*cos(getRad(i,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*sin(getRad(i,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*cos(getRad(i,n))*sin(getRad(i,n))*cos(getRad(j,n))*cos(getRad(j,n)));
                glVertex3d(r*r*cos(getRad(i+1,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*sin(getRad(i+1,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*cos(getRad(i+1,n))*sin(getRad(i+1,n))*cos(getRad(j,n))*cos(getRad(j,n)));
                glVertex3d(r*r*cos(getRad(i+1,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*sin(getRad(i+1,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*cos(getRad(i+1,n))*sin(getRad(i+1,n))*cos(getRad(j+1,n))*cos(getRad(j+1,n)));
                glVertex3d(r*r*cos(getRad(i,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*sin(getRad(i,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*cos(getRad(i,n))*sin(getRad(i,n))*cos(getRad(j+1,n))*cos(getRad(j+1,n)));
            glEnd();
            glFlush();
    }
}

如果你正在处理不可定向的表面(如斯坦纳的罗马人,或著名的Möbius条),你必须:启用双面照明

glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

或者你启用面剔除,用两个通道渲染表面(正面和背面)-你必须取消背面通道的法线。

glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); // backside faces are NOT rendered
draw_with_positive_normals();
glCullFace(GL_FRONT);
draw_with_negative_normals();

将多边形分成两个三角形可能会得到更好的结果-然后每个三角形都保证是平面的。此外,您可以从每个三角形生成法线,或者在相邻三角形之间平滑它们。

另一个技巧是将你的点预生成到一个数组中,然后在glVertex调用中引用该数组。这样你就有更多关于如何生成法线的选择。

同样,你可以用glBegin(GL_LINES)来渲染法线本身…glEnd()序列。

对于您生成的每个三角形,创建一个具有相同坐标/法线,但缠绕/翻转方向相反的三角形