在OpenGL中绘制绕Y轴旋转的齿轮

3
如标题所述:我想创建一个有10个齿轮的3D齿轮,它围绕其中心旋转(就像齿轮一样)。该齿轮具有方形齿和简单起见,在齿之间有平面侧面-此齿轮上没有曲线。

齿轮一侧应如何看的可视化。请注意,角度不是100%完美。

enter image description here

根据上面的图像,每个齿轮齿条都必须是一个8边形,而齿间的每条边则必须只是一个4边形。然而,目前的齿形图会执行以下操作:
  1. 本应面向相机的侧齿被旋转到地板上,尽管没有应用初始旋转。
  2. 该齿比其高度两倍宽,尽管缩放和顶点创建使用均匀数值(在x、y、z方向上缩放s,并使用0或0.5作为顶点坐标)。

完全可重现的示例:

import java.awt.Dimension;

import javax.swing.JFrame;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLJPanel;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;

public class SpinCog3D implements GLEventListener {

    JFrame jf;
    GLJPanel gljpanel;
    Dimension dim = new Dimension(800, 600);
    FPSAnimator animator;

    float rotation;
    float speed;

    // set up the OpenGL Panel within a JFrame
    public SpinCog3D() {
        jf = new JFrame();
        gljpanel = new GLJPanel();
        gljpanel.addGLEventListener(this);
        gljpanel.requestFocusInWindow();
        jf.getContentPane().add(gljpanel);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
        jf.setPreferredSize(dim);
        jf.pack();
        animator = new FPSAnimator(gljpanel, 20);
        rotation = 0.0f;
        speed = 0.1f;
        animator.start();
    }

    public static void main(String[] args) {
        new SpinCog3D();
    }

    public void init(GLAutoDrawable dr) {
        GL2 gl2 = dr.getGL().getGL2();
        GLU glu = new GLU();
        gl2.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        gl2.glEnable(GL2.GL_DEPTH_TEST);

        gl2.glMatrixMode(GL2.GL_PROJECTION);
        gl2.glLoadIdentity();
        glu.gluPerspective(60.0, 1.0, 100.0, 800.0);

    }

    public void display(GLAutoDrawable dr) {

        GL2 gl2 = dr.getGL().getGL2();
        GLU glu = new GLU();

        gl2.glMatrixMode(GL2.GL_MODELVIEW);
        gl2.glLoadIdentity();
        glu.gluLookAt(0.0, 200.0, 500.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

        gl2.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        // Draw 1 Cog Tooth + 1 Side
        drawTooth(gl2, 100.0, 1.0f, 0.0f, 0.0f, 0.0, 0.0, 1.0, 0.0, 0);
        drawSide(gl2, 100.0, 0.0f, 1.0f, 0.0f, 0.0, 0.0, 1.0, 0.0, 10);

        // Draw Floor
        sideRotatedColorScaledFloor(gl2, 300.0, 0.0f, 0.0f, 0.0f, 90.0, 1.0, 0.0, 0.0, 0.0);

        gl2.glFlush();
        rotation += speed;
        if (rotation > 360.9f)
            rotation = 0.0f;
    }

    // draw a single side with a set color and orientation
    private void drawTooth(GL2 gl2, double s, float r, float g, float b, double a, double ax, double ay,
                                        double az, double zoffset) {
        gl2.glPushMatrix();
        gl2.glRotated(a, ax, ay, az);
        gl2.glColor3f(r, g, b);
        gl2.glTranslated(0.0, 0.0, zoffset);
        gl2.glScaled(s, s, s);

        tooth(gl2);
        gl2.glPopMatrix();
    }

    private void drawSide(GL2 gl2, double s, float r, float g, float b, double a, double ax, double ay,
                           double az, double zoffset) {
        gl2.glPushMatrix();
        gl2.glRotated(a, ax, ay, az);
        gl2.glColor3f(r, g, b);
        gl2.glTranslated(0.0, 0.0, zoffset);
        gl2.glScaled(s, s, s);

        side(gl2);
        gl2.glPopMatrix();
    }

    private void sideRotatedColorScaledFloor(GL2 gl2, double s, float r, float g, float b, double a, double ax, double ay,
                                        double az, double zoffset) {
        gl2.glPushMatrix();
        gl2.glRotated(a, ax, ay, az);
        gl2.glColor3f(r, g, b);
        gl2.glTranslated(0.0, 0.0, zoffset);
        gl2.glScaled(s, s, s);

        side(gl2);
        gl2.glPopMatrix();
    }

    private void tooth(GL2 gl2) {
        gl2.glBegin(GL2.GL_POLYGON);
        gl2.glVertex3d(-0.5, -0.5, 0.0);
        gl2.glVertex3d(-0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, -0.5, 0.0);
        gl2.glVertex3d(-0.5, -0.5, 0.5);
        gl2.glVertex3d(-0.5, 0.5, 0.5);
        gl2.glVertex3d(0.5, 0.5, 0.5);
        gl2.glVertex3d(0.5, -0.5, 0.5);
        gl2.glEnd();
    }

    private void side(GL2 gl2) {
        gl2.glBegin(GL2.GL_POLYGON);
        gl2.glVertex3d(-0.5, -0.5, 0.0);
        gl2.glVertex3d(-0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, 0.5, 0.0);
        gl2.glVertex3d(0.5, -0.5, 0.0);
        gl2.glEnd();
    }
}

我知道glBegin()glEnd()早已被弃用,但这不是重点。如何使我的图形正确绘制,并与边缘对齐?


你能否添加“奇怪的事情”的描述或最好能够添加图片吗?我认为这可能会使您的问题更好,这样我们就不必自己运行代码来查看问题所在。 - zero298
我添加了答案,而没有使用矩阵...你的错误很可能就在这里... - Spektre
1个回答

3

齿轮绘制做了各种奇怪的事情...

这不是一个好的问题描述。我猜你的网格有很多缝隙和重叠,因为你使用硬编码的形状而没有对齿轮的实际大小进行任何修正。比如说,要适合n个齿轮以精确覆盖整个圆形,齿轮必须有多大......

如何利用参数圆方程呢?

这将消除矩阵混乱,并且我认为会更简单。

所以我只需将齿轮划分为三角形切片,每个切片都有几个边缘点。然后用QUAD连接它们,并将其放入for循环中执行所有切片。

我不会用JAVA编程,但是这里有一个小的C++示例,使用旧的GL api和没有花哨的C ++内容,所以你应该能够轻松地移植它:

overview

void glCog(float r0,float r1,float r2,float w,int n)    // shaft/inner/outer radiuses, width, tooths
    {
    int i;
    float a,da,x,y,c,s;
    float p[6][3],q[6][3];  // slice points
    // set z for slice points
    a=-0.5*w; for (i=0;i<3;i++){ p[i][2]=a; q[i][2]=a; }
    a=+0.5*w; for (i=3;i<6;i++){ p[i][2]=a; q[i][2]=a; }
    // init first slice
    q[0][0]= r0; q[5][0]= r0;
    q[0][1]=0.0; q[5][1]=0.0;
    q[1][0]= r1; q[4][0]= r1;
    q[1][1]=0.0; q[4][1]=0.0;
    q[2][0]= r2; q[3][0]= r2;
    q[2][1]=0.0; q[3][1]=0.0;
    // divide circle to 2*n slices
    da=2.0*M_PI/float(4*n);
    glBegin(GL_QUADS);
    for (a=0.0,i=0;i<=n;i++)
        {
        // points on circles at angle a
        c=cos(a); s=sin(a); a+=da;
        x=r0*c; y=r0*s; p[0][0]=x; p[5][0]=x;
                        p[0][1]=y; p[5][1]=y;
        x=r1*c; y=r1*s; p[1][0]=x; p[4][0]=x;
                        p[1][1]=y; p[4][1]=y;
        x=r2*c; y=r2*s; p[2][0]=x; p[3][0]=x;
                        p[2][1]=y; p[3][1]=y;
        // render tooth
        c=cos(a); s=sin(a); a+=da;
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(p[0]);
        glVertex3fv(p[2]);
        glVertex3fv(q[2]);
        glVertex3fv(q[0]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(p[3]);
        glVertex3fv(p[5]);
        glVertex3fv(q[5]);
        glVertex3fv(q[3]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(p[5]);
        glVertex3fv(p[0]);
        glVertex3fv(q[0]);
        glVertex3fv(q[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(p[2]);
        glVertex3fv(p[3]);
        glVertex3fv(q[3]);
        glVertex3fv(q[2]);
        glNormal3f(-s,c,0.0);
        glVertex3fv(p[4]);
        glVertex3fv(p[3]);
        glVertex3fv(p[2]);
        glVertex3fv(p[1]);
        glNormal3f(s,-c,0.0);
        glVertex3fv(q[1]);
        glVertex3fv(q[2]);
        glVertex3fv(q[3]);
        glVertex3fv(q[4]);

        // points on circles at angle a
        c=cos(a); s=sin(a); a+=da;
        x=r0*c; y=r0*s; q[0][0]=x; q[5][0]=x;
                        q[0][1]=y; q[5][1]=y;
        x=r1*c; y=r1*s; q[1][0]=x; q[4][0]=x;
                        q[1][1]=y; q[4][1]=y;
        x=r2*c; y=r2*s; q[2][0]=x; q[3][0]=x;
                        q[2][1]=y; q[3][1]=y;
        // render gap
        c=cos(a); s=sin(a); a+=da;
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(q[0]);
        glVertex3fv(q[1]);
        glVertex3fv(p[1]);
        glVertex3fv(p[0]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(q[4]);
        glVertex3fv(q[5]);
        glVertex3fv(p[5]);
        glVertex3fv(p[4]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(q[5]);
        glVertex3fv(q[0]);
        glVertex3fv(p[0]);
        glVertex3fv(p[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(q[1]);
        glVertex3fv(q[4]);
        glVertex3fv(p[4]);
        glVertex3fv(p[1]);
        }
    glEnd();
    }

这里是 glCog(0.1,0.5,0.6,0.1,10); 的预览:

preview1

这里是 glCog(0.2,0.5,0.52,0.2,50); 的预览:

preview2

然而请注意,牙齿并不完全是矩形。牙齿数量越少,误差就越大。如果您想要牙齿的形状完全是矩形,您需要平移最外边的点而不是旋转(或更正它们计算角度的方式)

使用平移来解决这个问题:

void glCog(float r0,float r1,float r2,float w,int n)    // shaft/inner/outer radiuses, width, tooths
    {
    int i,j;
    float a,da,dr,x,y,c,s;
    float p[6][3],q[6][3];  // slice points
    // divide circle to 2*n slices
    da=2.0*M_PI/float(4*n);
    dr=r2-r1;
    // set z for slice points
    a=-0.5*w; for (i=0;i<3;i++){ p[i][2]=a; q[i][2]=a; }
    a=+0.5*w; for (i=3;i<6;i++){ p[i][2]=a; q[i][2]=a; }
    // init first slice
    q[0][0]= r0; q[5][0]= r0;
    q[0][1]=0.0; q[5][1]=0.0;
    q[1][0]= r1; q[4][0]= r1;
    q[1][1]=0.0; q[4][1]=0.0;
    x=r1+dr*cos(-da); y=dr*sin(-da);
    q[2][0]=  x; q[3][0]=  x;
    q[2][1]=  y; q[3][1]=  y;
    glBegin(GL_QUADS);
    for (a=0.0,i=0;i<=n;i++)
        {
        // points on circles at angle a
        c=cos(a); s=sin(a);
        x=r0*c; y=r0*s; p[0][0]=x; p[5][0]=x;
                        p[0][1]=y; p[5][1]=y;
        x=r1*c; y=r1*s; p[1][0]=x; p[4][0]=x;
                        p[1][1]=y; p[4][1]=y;
        c=cos(a-da); s=sin(a-da); a+=da;
        x+=dr*c;y+=dr*s;p[2][0]=x; p[3][0]=x;
                        p[2][1]=y; p[3][1]=y;
        c=cos(a); s=sin(a); a+=da;
        // render tooth
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(p[0]);
        glVertex3fv(p[1]);
        glVertex3fv(q[1]);
        glVertex3fv(q[0]);
        glVertex3fv(p[1]);
        glVertex3fv(p[2]);
        glVertex3fv(q[2]);
        glVertex3fv(q[1]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(p[3]);
        glVertex3fv(p[4]);
        glVertex3fv(q[4]);
        glVertex3fv(q[3]);
        glVertex3fv(p[4]);
        glVertex3fv(p[5]);
        glVertex3fv(q[5]);
        glVertex3fv(q[4]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(p[5]);
        glVertex3fv(p[0]);
        glVertex3fv(q[0]);
        glVertex3fv(q[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(p[2]);
        glVertex3fv(p[3]);
        glVertex3fv(q[3]);
        glVertex3fv(q[2]);
        glNormal3f(-s,c,0.0);
        glVertex3fv(p[4]);
        glVertex3fv(p[3]);
        glVertex3fv(p[2]);
        glVertex3fv(p[1]);
        glNormal3f(s,-c,0.0);
        glVertex3fv(q[1]);
        glVertex3fv(q[2]);
        glVertex3fv(q[3]);
        glVertex3fv(q[4]);

        // points on circles at angle a
        c=cos(a); s=sin(a);;
        x=r0*c; y=r0*s; q[0][0]=x; q[5][0]=x;
                        q[0][1]=y; q[5][1]=y;
        x=r1*c; y=r1*s; q[1][0]=x; q[4][0]=x;
                        q[1][1]=y; q[4][1]=y;
        c=cos(a+da); s=sin(a+da); a+=da;
        x+=dr*c;y+=dr*s;q[2][0]=x; q[3][0]=x;
                        q[2][1]=y; q[3][1]=y;
        c=cos(a); s=sin(a); a+=da;
        // render gap
        glNormal3f(0.0,0.0,-1.0);   // -Z base
        glVertex3fv(q[0]);
        glVertex3fv(q[1]);
        glVertex3fv(p[1]);
        glVertex3fv(p[0]);
        glNormal3f(0.0,0.0,+1.0);   // +Z base
        glVertex3fv(q[4]);
        glVertex3fv(q[5]);
        glVertex3fv(p[5]);
        glVertex3fv(p[4]);
        glNormal3f(-c,-s,0.0);      // shaft circumference side
        glVertex3fv(q[5]);
        glVertex3fv(q[0]);
        glVertex3fv(p[0]);
        glVertex3fv(p[5]);
        glNormal3f(c,s,0.0);        // outter circumference side
        glVertex3fv(q[1]);
        glVertex3fv(q[4]);
        glVertex3fv(p[4]);
        glVertex3fv(p[1]);
        }
    glEnd();
    }

所以只需稍微调整点 p[2],q[2],p[3],q[3] ,并且底边必须使用更多的QUADS来补偿...

这是 glCog(0.2,0.5,0.6,0.2,10); 的预览:

final preview


很好的说明(+1) - Rabbid76
@Rabbid76 谢谢...这是由我的自己的GIF捕获和编码器捕获的...然而有时在捕获GL渲染时,最后会出现白色帧,不确定为什么(在我开发时没有这样做,可能是多年来图形驱动程序的某些更改)。 - Spektre
如果可以的话,我会给你+2分。非常感谢您。 - R. Rengold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接