Advanced topics in modeling with Java OpenGL - JOGL 2 Spatial orientation: Euler angles, gimbal lock, a practical solution for practical introduction to JOGL 2.0 Advanced Animation an
Trang 1Advanced topics
in modeling
with Java OpenGL - JOGL 2
Spatial orientation: Euler angles, gimbal lock, a practical solution
(for practical introduction to JOGL 2.0)
Advanced Animation and Rendering Techniques – Theory and Practice
Alan Watt, Mark Watt; Addison-Wesley, ACM press
http://www.mactech.com/articles/develop/issue_25/schneider.html (for NURBS)
http://www.oera.net/How2/TextureMaps2.htm (for beautiful free planet textures)
http://planetpixelemporium.com/planets.html (for beautiful free planet textures)
Trang 2Spatial orientation: Euler angles, gimbal lock,
a practical solution
Euler angles:
any spatial orientation of an object can be described by 3 angles = 3 degrees of freedom
how to get from the absolute coordinate system
to the local coordinate system of the object ?
global rotation = succession of 3 rotations around coordinate system axes
several possibilities: in which order do we rotate around axes x , y , z ?
one possible solution:
absolute
local (x'',y'',z'')
Trang 3gimbal lock:
decoupled degrees of freedom:
axes of rotation remain more or less orthogonal
angles remain decoupled = they have independent actions
big problem: loss of a degree of freedom:
in certain configurations, 2 of the 3 axes of rotation become aligned!
their 2 associated angles are equivalent = they produce the same rotation in space
only 2 effective degrees of freedom left
example: b = - / 2 a and c both rotate around axis z
once b is fixed, only possible to rotate around z axis with a and c
annoying problem: degrees of freedom partially coupled:
b tends toward / 2 a and c become increasingly coupled
varying a has an increasing effect on the rotation that should be controled only by c
Trang 4a practical solution:
instead of Euler angles,
define directly the local coordinate system of the object
with 3 orthogonal unit vectors u , v , w
assume u , v , w given initially how to adjust the space orientation of u , v , w ?
incremental approach:
start with an inital u , v , w
then rotate gradually u , v , w in space
new u , v , w after each incremental rotation δ
at each time step, apply small rotations around u , v or w
= applied relatively to the object
intuitive for operator
Trang 53D rotation around a vector:
rotation of angle δ around unit vector n : v v'
Trang 6application to attitude control of virtual camera:
initial orientation of camera:
w aiming direction
uup up vector = up direction of screen u orthogonalized relative to w : u = uup - (u.w) w
real-time control of evolution of camera orientation:
we adjust camera orientation in real time
= we are in a spacecraft and we control its orientation (attitude control system,
v = w × u
u = uup - (uup.w) w
w
uup
Trang 7controlling orientation with some keyboard action keys:
Insert key pressed while other keys used fast rotation by 10 δ per time step
Delete
Insert
Page Down
rot(δ,v) over u,w
roll incline right:
rot(δ,w) over u,v
roll incline left:
rot(-δ,w) over u,v
fast rotation:
rotation 10 δ
Trang 8extra: position control of virtual camera:
how to move the camera?
incremental approach:
initial position p
small translation over p at each time step
translation expressed relatively to camera orientation: intuitive for operator
move backward - forward: translation along w
move left - right: translation along v
move down - up: translation along u
Delete
Insert
Page Down
Trang 9class V3d: operations on 3D vectors
class V3d
{
double x , y , z;
V3d() {}
V3d(double x , double y , double z) { this.x = x; this.y = y; this.z = z; }
void V3d_move(V3d n , double d) // this < this + d * n
double c = Math.cos(theta * Math.PI / 180.0) , s = Math.sin(theta * Math.PI / 180.0);
double k = (1 - c) * (this.x * n.x + this.y * n.y + this.z * n.z);
double x = c * this.x + k * n.x + s * (n.y * this.z - n.z * this.y)
, y = c * this.y + k * n.y + s * (n.z * this.x - n.x * this.z)
, z = c * this.z + k * n.z + s * (n.x * this.y - n.y * this.x);
this.x = x; this.y = y; this.z = z;
}
void V3d_orthogonalize(V3d b) // this < this - (this b) b
{ double d = this.x * b.x + this.y * b.y + this.z * b.z;
this.x -= d * b.x; this.y -= d * b.y; this.z -= d * b.z; }
void V3d_normalize() // this < unit vector (this)
{ double d = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
if (d < 1.0e-10) { System.out.println("in V3d_normalize: d < 1.0e-10"); System.exit(0); }
this.x /= d; this.y /= d; this.z /= d; }
void V3d_vector_product(V3d a , V3d b) // this < a x b
{ this.x = a.y * b.z - a.z * b.y; this.y = a.z * b.x - a.x * b.z; this.z = a.x * b.y - a.y * b.x; }
Trang 10class Craft: virtual craft (orientation and position controlled in real time with keyboard)
class Craft
{
V3d p , u , v , w;
double dmove , drot; boolean move , fast;
Craft( double posx , double posy , double posz
, double aimx , double aimy , double aimz
, double upx , double upy , double upz
, final JFrame f
, double dm , double dr)
{
p = new V3d(posx , posy , posz);
w = new V3d(aimx , aimy , aimz); w.V3d_normalize();
u = new V3d(upx , upy , upz); u.V3d_orthogonalize(w); u.V3d_normalize();
v = new V3d(); v.V3d_vector_product(w , u);
this.dmove = dm; this.drot = dr; move = false; fast = false;
f.addKeyListener(new KeyAdapter()
{ public void keyPressed(KeyEvent e)
{ int key = e.getKeyCode();
double d; if (move) d = (fast ? 10 * dmove : dmove); else d = (fast ? 10 * drot : drot);
{ public void keyReleased(KeyEvent e)
{ int key = e.getKeyCode();
switch (key)
{ case KeyEvent.VK_INSERT: fast = false; break;
Trang 11class Gui: create a virtual craft and use its orientation and position to input gluLookAt
class Gui
{
JFrame f; DrawingPanel p;
Craft craft;
class DrawingPanel extends GLJPanel
{
DrawingPanel() {
super(new GLCapabilities(GLProfile.getDefault())); this.addGLEventListener(new GLEventListener() {
public void display(GLAutoDrawable drawable) //**** DISPLAY {
GL2 gl = drawable.getGL().getGL2();
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); gl.glLoadIdentity(); glu.gluLookAt( (float)craft.p.x , (float)craft.p.y , (float)craft.p.z , (float)(craft.p.x + craft.w.x) , (float)(craft.p.y + craft.w.y) , (float)(craft.p.z + craft.w.z) , (float)craft.u.x , (float)craft.u.y , (float)craft.u.z );
}
} );
}
}
Gui() {
f = new JFrame(); f.setFocusable(true); f.setVisible(true); p = new DrawingPanel(); f.getContentPane().add(p , BorderLayout.CENTER); craft = new Craft(100.0 , 0.0 , 0.0 , -1.0 , 0.0 , 0.0 , 0.0 , 0.0 , 1.0 , f , 0.5 , 0.5); f.setSize(new Dimension(900 + 16 , 600 + 38)); }
}
Trang 12Textures
create a texture object from a jpeg file
map the texture over a surface:
one or several polygons precise at each vertex the texture coordinates
GLU quadric (sphere, cylinder, disk, partial disk) automatic mapping
assign white color to the surface to preserve the colors of the texture
(else: red surface texture becomes reddish when applied to surface)
creating a texture object:
import texture package:
import com.jogamp.opengl.util.texture.*;
declare texture object:
Texture texture;
create texture object from image file:
try { texture = TextureIO.newTexture(new File("texture.jpg") , true); }
catch(Exception e) {}
texture == null error reading the image file
true if antialiasing (mipmap) false if aliasing tolerated
Trang 13texture coordinate system:
ut parameter: left to right
vt parameter: top to down flip image vertically and save image file
example: earth map (Earth_flipV.jpg)
do not forget to flip vertically your jpeg image, else confusion over coordinates
ut
vt
Trang 14mapping the texture over polygons:
enable texturing: texture.enable();
bind texture to GL context: texture.bind();
define polygon with texture coordinates at each vertex:
between glBegin and glEnd , for each vertex:
gl.glNormal3f(nx , ny , nz);
gl.glTexCoord2f(ut , vt);
gl.glVertex3f(x , y, z);
disable texturing with texture: texture.disable();
texture will not be applied over following surface
normal at vertex
texture coordinates at vertex
position of vertex
Trang 15template for textured polygon(s):
Trang 16example: Earth_flipV.jpg mapped over a flat quadrangle
Texture tEv;
try { tEv = TextureIO.newTexture(new File("Earth_flipV.jpg") , true); } catch(Exception e) {}
if (tEv == null) { System.out.println("tEv is null"); System.exit(0); }
//// - QUADRANGLE
gl.glMaterialfv(GL.GL_FRONT_AND_BACK , GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , new float[] { 1.0f , 1.0f , 1.0f , 1.0f } , 0);
tEv.enable(); tEv.bind();
gl.glBegin(GL2.GL_QUADS);
gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(0.0f , 0.0f); gl.glVertex3f(0.0f , 0.0f , 0.0f); gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(1.0f , 0.0f); gl.glVertex3f(0.0f , 100.0f , 0.0f); gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(1.0f , 1.0f); gl.glVertex3f(0.0f , 100.0f , 50.0f); gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(0.0f , 1.0f); gl.glVertex3f(0.0f , 0.0f , 50.0f); gl.glEnd();
Trang 17example: Earth_flipV.jpg mapped over a single triangle
Texture tEv;
try { tEv = TextureIO.newTexture(new File("Earth_flipV.jpg") , true); } catch(Exception e) {}
if (tEv == null) { System.out.println("tEv is null"); System.exit(0); }
//// - TRIANGLE
gl.glMaterialfv(GL.GL_FRONT_AND_BACK , GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , new float[] { 1.0f , 1.0f , 1.0f , 1.0f } , 0);
tEv.enable(); tEv.bind();
gl.glBegin(GL2.GL_TRIANGLES);
gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(0.0f , 0.0f); gl.glVertex3f(0.0f , 0.0f , 0.0f); gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(1.0f , 0.0f); gl.glVertex3f(0.0f , 100.0f , 0.0f); gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(0.5f , 1.0f); gl.glVertex3f(0.0f , 50.0f , 50.0f); gl.glEnd();
Trang 18example: Earth_flipV.jpg mapped over a triangle strip with 4 triangles
.
Texture tEv;
try { tEv = TextureIO.newTexture(new File("Earth_flipV.jpg") , true); } catch(Exception e) {}
if (tEv == null) { System.out.println("tEv is null"); System.exit(0); }
//// - TRIANGLE STRIP
gl.glMaterialfv(GL.GL_FRONT_AND_BACK , GLLightingFunc.GL_AMBIENT_AND_DIFFUSE , new float[] { 1.0f , 1.0f , 1.0f , 1.0f } , 0);
tEv.enable(); tEv.bind();
gl.glBegin(GL2.GL_TRIANGLES);
gl.glNormal3f(0.0f , 1.0f , 0.0f); gl.glTexCoord2f(0.0f , 0.0f); gl.glVertex3f( 0.0f , 0.0f , 0.0f);
gl.glNormal3f(0.0f , 1.0f , 0.0f); gl.glTexCoord2f(0.5f , 1.0f); gl.glVertex3f( 0.0f , 50.0f , 50.0f);
gl.glNormal3f(0.0f , 1.0f , 0.0f); gl.glTexCoord2f(0.0f , 1.0f); gl.glVertex3f(43.0f , 25.0f , 50.0f);
gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(0.0f , 0.0f); gl.glVertex3f(0.0f , 0.0f , 0.0f);
gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(1.0f , 0.0f); gl.glVertex3f(0.0f , 100.0f , 0.0f);
gl.glNormal3f(1.0f , 0.0f , 0.0f); gl.glTexCoord2f(0.5f , 1.0f); gl.glVertex3f(0.0f , 50.0f , 50.0f);
gl.glNormal3f(0.0f , 1.0f , 0.0f); gl.glTexCoord2f(1.0f , 0.0f); gl.glVertex3f( 0.0f , 100.0f , 0.0f); gl.glNormal3f(0.0f , 1.0f , 0.0f); gl.glTexCoord2f(1.0f , 1.0f); gl.glVertex3f(-50.0f , 50.0f , 50.0f); gl.glNormal3f(0.0f , 1.0f , 0.0f); gl.glTexCoord2f(0.5f , 1.0f); gl.glVertex3f( 0.0f , 50.0f , 50.0f); gl.glEnd();
tEv.disable();
1st triangle
3rd triangle
Trang 19mapping the texture over GLU quadrics:
enable texturing: texture.enable();
bind texture to GL context: texture.bind();
enable texturing for quadric object quad: glu.gluQuadricTexture(quad , true);
define quadric: glu.gluSphere(quad , );
glu.gluCylinder(quad , );
glu.gluDisk(quad , );
glu.gluPartialDisk(quad , );
disable texturing with texture: texture.disable();
texture will not be applied over following surface
automatic mapping over sphere:
automatic mapping according to rules below
ut
z
Trang 20automatic mapping over cylinder:
flip vertically and horizontally your jpeg image
automatic mapping over disk / partial disk:
Trang 21template for textured quadric(s):
import com.jogamp.opengl.util.texture.*;
class Gui {
class DrawingPanel extends GLJPanel {
Texture texture; DrawingPanel() {
this.addGLEventListener(new GLEventListener() {
public void init(GLAutoDrawable drawable) {
try { texture = TextureIO.newTexture(new File("texture.jpg") , true); } catch(Exception e) {} }
public void display(GLAutoDrawable drawable) {
texture.enable(); texture.bind(); glu.gluQuadricTexture(quad , true); glu.gluSphere(quad , );
glu.gluCylinder(quad , );
glu.gluDisk(quad , );
glu.gluPartialDisk(quad , );
texture.disable(); }
} );
}
}
}
Trang 22example: Earth_flipV.jpg mapped over a sphere, a disk, a partial disk
Earth_flipVH.jpg mapped over a cylinder
try { tEv = TextureIO.newTexture(new File("Earth_flipV.jpg") , true); } catch(Exception e) {} try { tEvh = TextureIO.newTexture(new File("Earth_flipVH.jpg") , true); } catch(Exception e) {}
tEv.enable(); tEv.bind(); glu.gluQuadricTexture(quad , true);
Trang 23example: Earth_flipV.jpg , Moon_flipV.jpg , Sun_flipV.jpg , Heaven_flipV.jpg
mapped on spheres ; virtual spacecraft
try { tEv = TextureIO.newTexture(new File("Earth_flipV.jpg") , true); } catch(Exception e) {} try { tMv = TextureIO.newTexture(new File("Moon_flipV.jpg") , true); } catch(Exception e) {} try { tSv = TextureIO.newTexture(new File("Sun_flipV.jpg") , true); } catch(Exception e) {} try { tHv = TextureIO.newTexture(new File("Heaven_flipV.jpg") , true); } catch(Exception e) {}
tEv.enable(); tEv.bind(); glu.gluQuadricTexture(quad , true);
Trang 25NURBS surfaces
grid of control points along surface parameters u and v :
"Non Uniform":
knots = help us define intervals for parameters u , v between control points
define non uniform intervals (stretched, squeezed intervals)
smoothen, sharpen locally the surface