OpenGL
用JOGL做简单的3D场景
制作3D场景先确定xyz坐标系,本文用的是右手原则的坐标系,如图
然后确定好自己要画的物体在该坐标系中对应的坐标就可以开始了;本文画的是非常简单的房子。
实现
继承GLEventListener,实现它的4个接口
首先画一个地板,在display(GLAutoDrawable drawable)
中用4个glVertex3f()来构建一个平面,参数分别对应x, y, z
//drawfloor
gl.glBegin(GL2.GL_QUADS);
gl.glColor3f(0.35f, 0.16f, 0.14f);
gl.glVertex3f(-100.0f, -1.0f, -100.0f);
gl.glVertex3f(-100.0f, -1.0f, 100.0f);
gl.glVertex3f(100.0f, -1.0f, 100.0f);
gl.glVertex3f(100.0f, -1.0f, -100.0f);
注意:因为OpenGL是C++的库,所以用JAVA写的时候需要导入的是JOGL,而且需要加上GL2 gl = drawable.getGL().getGL2();
才能使用OpenGL的各个方法
然后用相同的方法画房子主体和屋顶,因为需要改变的只有各个点的坐标,所以就只放实现房顶正面的代码(和画地板是一样的)
画图的方法有很多,网上都能找到,注意:在每次画完一个图形之后,如果需要用glTranslatef()移动或者glBegin()画一个新的图形,需要添加gl.glLoadIdentity();
//roof front
gl.glColor3f(1.0f, 0.0f, 0.0f); //
gl.glVertex3f( 2.0f, 3.7f, 1.25f );
gl.glVertex3f( -2.0f, 3.7f, 1.25f );
gl.glVertex3f( -3.0f, 2.5f, 1.75f );
gl.glVertex3f( 3.0f, 2.5f, 1.75f );
在init( GLAutoDrawable drawable )
中开启深度测试,为的是让位于后面的像素不会阻挡前面的像素
gl.glClearDepth( 1.0f );
gl.glEnable( GL2.GL_DEPTH_TEST ); //用来调整物体遮挡关系,使后面的像素不会挡住前面的像素
gl.glDepthFunc( GL2.GL_LEQUAL );
gl.glHint( GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST );
在reshape(GLAutoDrawable drawable, int x, int y, int w, int h)
中设置投影方式,一种是perspective projection,一种是orthographic projection
float f = (float)w/(float)h;
glu.gluPerspective(45, f, 1, 1000); //prespective projection定义相机内在镜头状态(可视空间),视角,定义在画板上xy方向上的比例;
//gl.glOrthof(-10.0f, 10.0f, -10.0f, 10.0f, 10.0f, -10.0f); //orthographic projection, x, y, z;
perspective中各参数分别代表:可视角度,视景体的宽高比,z轴上相机最近可看到的距离,z轴…最远;
Orthof中各参数:前两个代表x轴上可是范围[-10,10],中间两个是y轴的,最后两个是z轴的;
然后设置相机所在位置和朝向(在3维坐标系中,知道两点的坐标可以确定一个向量)
static float x = 0.0f,y = 4.5f, z = 10.0f; //相机位置
static float vx = 0.0f, vy = -0.3f, vz = -1.0f; //视线方向,初始设置为沿着z轴方向
glu.gluLookAt(x, y, z, x+vx, y+vy, z+vz, 0.0f, 1.0f, 0.0f); //定义摄像机位置,朝向,
本文设置了按键监听,按左右键调整相机角度,上下键实现相机画面的缩放(当然也可以设计成前进后退模拟行走)
注意:如果要设置成缩放,要在display()中加一段代码 gl.glScalef(scale, scale, scale);
int key = ke.getKeyCode();
switch(key) {
case KeyEvent.VK_UP:
scale += 0.1;
break;
case KeyEvent.VK_DOWN:
scale -= 0.1;
break;
case KeyEvent.VK_LEFT:
vx -= 0.05f;
break;
case KeyEvent.VK_RIGHT:
vx += 0.05f;
}
最后就是把画面显示出来,本文设置了3个相机,一个固定,一个使用透视投影,一个使用正交投影,分成3个class,代码基本相同,main函数随便放期中一个就好了
JFrame frame = new JFrame ( "Assignment2 - 3D scene" );
GLCanvas glcanvas = new GLCanvas();
GLCanvas glcanvas1 = new GLCanvas();
GLCanvas glcanvas2 = new GLCanvas();
Perspective persp = new Perspective();
Orthographic ortho = new Orthographic();
glcanvas1 = Perspective.glcanvas;
glcanvas2 = Orthographic.glcanvas;
glcanvas.addGLEventListener( this );
glcanvas.addKeyListener( this );
glcanvas.setPreferredSize( new Dimension(800, 500) );
glcanvas1.addGLEventListener( persp );
glcanvas1.addKeyListener( persp );
glcanvas1.setPreferredSize( new Dimension(400, 400) );
glcanvas2.addGLEventListener( ortho );
glcanvas2.addKeyListener( ortho );
glcanvas2.setPreferredSize( new Dimension(400, 400) );
frame.add( glcanvas, BorderLayout.NORTH );
frame.add( glcanvas1, BorderLayout.WEST );
frame.add( glcanvas2, BorderLayout.EAST );
frame.setSize( frame.getContentPane().getPreferredSize() );
frame.setVisible( true );
anim = new Animator(glcanvas);
anim.start();
anim1 = new Animator(glcanvas1);
anim1.start();
anim2 = new Animator(glcanvas2);
anim2.start();
下面是实现的效果