// ------------------------------------------------------------------------ // This program is complementary material for the book: // // Frank Nielsen // // Visual Computing: Geometry, Graphics, and Vision // // ISBN: 1-58450-427-7 // // Charles River Media, Inc. // // // All programs are available at http://www.charlesriver.com/visualcomputing/ // // You may use this program for ACADEMIC and PERSONAL purposes ONLY. // // // The use of this program in a commercial product requires EXPLICITLY // written permission from the author. The author is NOT responsible or // liable for damage or loss that may be caused by the use of this program. // // Copyright (c) 2005. Frank Nielsen. All rights reserved. // ------------------------------------------------------------------------ // ------------------------------------------------------------------------ // File: slerp.cpp // // Description: Spherical linear interpolation demo program in OpenGL(R) // ------------------------------------------------------------------------ #include "stdafx.h" #include #include #include #include using namespace std; #define W 800 #define H 800 inline double drand() {return rand()/(double)RAND_MAX;} #define M_PI 3.14159265 #define toRad(x) ((x)*(M_PI/180.0)) class Point3D { public: double x,y,z; }; class Quaternion{ public: double w; Point3D u; inline void Multiply(const Quaternion q) { Quaternion tmp; tmp.u.x = ((w * q.u.x) + (u.x * q.w) + (u.y * q.u.z) - (u.z * q.u.y)); tmp.u.y = ((w * q.u.y) - (u.x * q.u.z) + (u.y * q.w) + (u.z * q.u.x)); tmp.u.z = ((w * q.u.z) + (u.x * q.u.y) - (u.y * q.u.x) + (u.z * q.w)); tmp.w = ((w * q.w) - (u.x * q.u.x) - (u.y * q.u.y) - (u.z * q.u.z)); *this = tmp; } inline double Norm() {return sqrt(u.x*u.x+u.y*u.y+u.z*u.z+w*w);} inline void Normalize() { double norm=Norm(); u.x/=norm;u.y/=norm;u.z/=norm; } inline void Conjugate() { u.x=-u.x; u.y=-u.y; u.z=-u.z; } inline void Inverse() { double norm=Norm(); Conjugate(); u.x/=norm; u.y/=norm; u.z/=norm; w/=norm; } void ExportToMatrix(float matrix[16]) { float wx, wy, wz, xx, yy, yz, xy, xz, zz; // adapted from Shoemake xx = u.x * u.x; xy = u.x * u.y; xz = u.x * u.z; yy = u.y * u.y; zz = u.z * u.z; yz = u.y * u.z; wx = w * u.x; wy = w * u.y; wz = w * u.z; matrix[0] = 1.0f - 2.0f*(yy + zz); matrix[4] = 2.0f*(xy - wz); matrix[8] = 2.0f*(xz + wy); matrix[12] = 0.0; matrix[1] = 2.0f*(xy + wz); matrix[5] = 1.0f - 2.0f*(xx + zz); matrix[9] = 2.0f*(yz - wx); matrix[13] = 0.0; matrix[2] = 2.0f*(xz - wy); matrix[6] = 2.0f*(yz + wx); matrix[10] = 1.0f - 2.0f*(xx + yy); matrix[14] = 0.0; matrix[3] = 0; matrix[7] = 0; matrix[11] = 0; matrix[15] = 1; } }; Quaternion RotateAboutAxis(Point3D pt, double angle, Point3D axis) { Quaternion q,p, qinv; q.w=cos(0.5*angle); q.u.x=sin(0.5*angle)*axis.x; q.u.y=sin(0.5*angle)*axis.y; q.u.z=sin(0.5*angle)*axis.z; p.w=0; p.u=pt; qinv=q; qinv.Inverse(); q.Multiply(p); q.Multiply(qinv); return q; } void Slerp(Quaternion q1, Quaternion q2, Quaternion &qr , double lambda) { float dotproduct = q1.u.x * q2.u.x + q1.u.y * q2.u.y + q1.u.z * q2.u.z + q1.w * q2.w; float theta, st, sut, sout, coeff1, coeff2; // algorithm adapted from Shoemake's paper lambda=lambda/2.0; theta = (float) acos(dotproduct); if (theta<0.0) theta=-theta; st = (float) sin(theta); sut = (float) sin(lambda*theta); sout = (float) sin((1-lambda)*theta); coeff1 = sout/st; coeff2 = sut/st; qr.u.x = coeff1*q1.u.x + coeff2*q2.u.x; qr.u.y = coeff1*q1.u.y + coeff2*q2.u.y; qr.u.z = coeff1*q1.u.z + coeff2*q2.u.z; qr.w = coeff1*q1.w + coeff2*q2.w; qr.Normalize(); } Point3D p,q,Rp; double lambdaanim=0.0; void MultiplyPointMatrix(float m[16], Point3D p, Point3D& rotp) { rotp.x=m[0]*p.x+m[1]*p.y+m[2]*p.z; rotp.y=m[4]*p.x+m[5]*p.y+m[6]*p.z; rotp.z=m[8]*p.x+m[9]*p.y+m[10]*p.z; } void display(void) { GLfloat m[16]; char buffer[256]; double s=1.0; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f(0.8,0.8,0.8); glutSolidSphere(0.99,32,32); glColor3f(1,1,0); glBegin(GL_LINES); glVertex3f(0,0,0); glVertex3f(0,2,0); glVertex3f(0,0,0); glVertex3f(2,0,0); glVertex3f(0,0,0); glVertex3f(0,0,2); glEnd(); glColor3f(0,0,1); glPointSize(10); glBegin(GL_POINTS); glVertex3f(p.x,p.y,p.z); glEnd(); glColor3f(1,0,1); glBegin(GL_POINTS); glVertex3f(q.x,q.y,q.z); glEnd(); Quaternion quatp,quatq, quatr; quatp.u=p;quatp.w=0; quatq.u=q;quatq.w=0; glColor3f(0,1,0); glPointSize(1.0); glBegin(GL_LINE_STRIP); for(double lambda=0;lambda<=1.01;lambda+=0.01) { Slerp(quatp, quatq, quatr,lambda); quatr.ExportToMatrix(m); MultiplyPointMatrix(m,p,Rp); glVertex3f(s*Rp.x,s*Rp.y,s*Rp.z); } glEnd(); lambdaanim+=0.01; if (lambdaanim>1.0) lambdaanim=0.0; Slerp(quatp, quatq, quatr,lambdaanim); quatr.ExportToMatrix(m); MultiplyPointMatrix(m,p,Rp); glPointSize(5); glColor3f(1,0,0); glBegin(GL_POINTS); glVertex3f(s*Rp.x,s*Rp.y,s*Rp.z); glEnd(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho (0.0, W, 0.0, H, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glColor3f (0, 0, 0); glRasterPos2f(50,30); sprintf(buffer,"Spherical Linear Interpolation of Quaternions (L=%.3f)",lambdaanim); for(int i=0;buffer[i]!=0;i++) glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[i]); glRasterPos2f(50,H-30); sprintf(buffer,"Press any key to initialize another pair of points."); for(int i=0;buffer[i]!=0;i++) glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, buffer[i]); glMatrixMode(GL_PROJECTION); glPopMatrix(); glFlush(); glutSwapBuffers(); } void reshape(int w, int h) { GLfloat aspect = (GLfloat) w / (GLfloat) h; glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho(-1.25, 1.25, -1.25 * aspect, 1.25 * aspect, -2.0, 2.0); else glOrtho(-1.25 * aspect, 1.25 * aspect, -1.25, 1.25, -2.0, 2.0); glutPostRedisplay(); } inline void Spherical2Cartesian(double t,double p,double &X, double &Y, double &Z) { X=cos(p)*sin(t); Y=sin(p); Z=cos(p)*cos(t); } void keyboard(unsigned char key, int x, int y) { if (key=='q') exit(0); // theta phi of point p Spherical2Cartesian(M_PI*drand()*2.0,-M_PI/2.0+M_PI*drand(),p.x,p.y,p.z); // theta, phi of point q Spherical2Cartesian(M_PI*drand()*2.0,-M_PI/2.0+M_PI*drand(),q.x,q.y,q.z); glutPostRedisplay(); } int _tmain(int argc, _TCHAR* argv[]) { srand(1234); cout<<"Visual Computing: Geometry, Graphics, and Vision (ISBN:1-58450-427-7)"<