討論區快速選單
知識庫快速選單
討論區最近新進100則主題 掌握Salesforce雲端管理秘訣
[ 回上頁 ] [ 討論區發言規則 ]
glut 教學 - 滑鼠旋轉介面 (Axis-Angle)
更改我的閱讀文章字型大小
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 01:37:59
配合 滑鼠 和 Axis-Angle 來作 旋轉介面 是最有效的方案, 但是 每一次當我向網友們推薦它時, 網友們總是因為它的概念比較複雜而對它 敬而遠之. 或許, Axis-Angle 的概念真的是比較複雜, 可是, 程式編寫起來倒是蠻簡潔的, 承接著上一個教學的 按鍵旋轉介面, 這一個教學, 就讓我介紹一下 如何利用 Axis-Angle 來編寫
滑鼠旋轉介面.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 02:06:18
/////////////////////////
// glutTest13.cpp
//
// Created by Gary Ho, ma_hty@hotmail.com, 2007
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <GL/glut.h>

#define PI 3.14159265358979323846f

float v0[3], v1[3];
float mo[16] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 };

float clamp( float x, float a, float b );
float dot( const float *a, const float *b );
float norm( const float *a );
void vassign( float *a, float x, float y, float z );
void vassign( float *a, const float *b );
void cross( float *a, const float *b, const float *c );
void normalize( float *a );

void display();
void mousebutton(int button, int state, int x, int y );
void mousemove(int x, int y);


void main( int argc, char **argv )
{
  glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
  glutInitWindowSize( 512, 512 );

  glutCreateWindow( "test09" );
  glutDisplayFunc( display );

  glutMouseFunc( mousebutton );
  glutMotionFunc( mousemove );

  glutMainLoop();
}

void display()
{
  GLint viewport[4];
    glGetIntegerv( GL_VIEWPORT, viewport );

  glEnable( GL_DEPTH_TEST );
  glEnable( GL_LIGHTING );
  glEnable( GL_LIGHT0 );

  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 10 );
  
  glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 0,0,3, 0,0,0, 0,1,0 );
    glMultMatrixf( mo );

    glutSolidTeapot(1);

  glutSwapBuffers();
}

void mousebutton(int button, int state, int x, int y )
{
  vassign( v0, 2.0*x/512-1, -2.0*y/512+1, 1 );
  normalize(v0);
}

void mousemove(int x, int y)
{
  float axis[3], angle;

  vassign( v1, 2.0*x/512-1, -2.0*y/512+1, 1 );
  normalize(v1);
  if( dot(v0,v1)>.999 )
    return;
  cross(axis,v0,v1);
  normalize(axis);
  angle = acosf( clamp(dot(v0,v1),-1,1) );
  vassign( v0, v1 );

  glPushMatrix();
    glLoadIdentity();
    glRotatef( angle*180/PI, axis[0], axis[1], axis[2] );
    glMultMatrixf( mo );
    glGetFloatv( GL_MODELVIEW_MATRIX, mo );
  glPopMatrix();
  glutPostRedisplay();
}


float clamp( float x, float a, float b ){ return x<a ? a : (x<b?x:b); }
float dot( const float *a, const float *b ){ return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; }
float norm( const float *a ){ return sqrtf(dot(a,a)); }
void vassign( float *a, float x, float y, float z ){ a[0]=x; a[1]=y; a[2]=z; }
void vassign( float *a, const float *b ){ a[0]=b[0]; a[1]=b[1]; a[2]=b[2]; }

void cross( float *a, const float *b, const float *c )
{
  a[0] = b[1]*c[2] - c[1]*b[2];
  a[1] = -b[0]*c[2] + c[0]*b[2];
  a[2] = b[0]*c[1] - c[0]*b[1];
}

void normalize( float *a )
{
  float l = norm(a);
  a[0]/=l; a[1]/=l; a[2]/=l;
}

作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 02:20:44
長長的程式碼, 實在重要的部份, 就只有 mousebutton() 和 mousemove(), 其餘的就只是以往教學介紹過的 OpenGL 基本設定 和 一些簡單的向量運算.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 02:25:04
現在說說 配合 axis-angle 和 滑鼠 作旋轉介面的步驟, 首先, 你要把 開始拖曳滑鼠座標 (x0,y0) 和 當下的滑鼠座標 (x1,y1) 轉換成兩支 3維的單位向量, 然後以 axis-angle 計算需要的旋轉, 最後配合 glRotatef() 使用就可以了, 即是...

視窗的大少 = (w,h)

v0 = normalize( 2*x0/w-1, -2*y0/h+1, 1 );
v1 = normalize( 2*x1/w-1, -2*y1/h+1, 1 );

axis = cross( v0, v1 );
angle = acos( dot(v0,v1) );

glRotatef( angle * 180 / PI, axis.x, axis.y, axis.z );
 
 
 
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 02:35:59
mousebutton() 這個函數, 當使用者在視窗上按下滑鼠鍵時, 就會被呼叫, 亦相當於我們的滑鼠拖曳開始, 這裡, 我們要把滑鼠座標轉換成單位向量.

void mousebutton(int button, int state, int x, int y )
{
  // 把 滑鼠座標 (x,y) 投射到在 z=1 平面上, 並把 x,y 座標
  // 按比例縮小至 [-1,1] 之間, 然後把修正後的數值, 都填寫到
  // v0 之內
  vassign( v0, 2.0*x/512-1, -2.0*y/512+1, 1 );

  // 使 v0 成為單位向量
  normalize(v0);
}

作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 02:53:16
當使用者按著滑鼠鍵, 並在視窗上移動, 以下的函數就會被呼叫, 同樣地, 我們要把 當下的滑鼠座標轉換成單位向量 (方法同上), cross(v0,v1) 然後再 normalize 之後, 就會形成自轉軸 axis, v0 和 v1 的夾角, 就是自轉角 angle, 沿著 axis 自旋 angle 度之後, 就會剛好把 v0 旋轉至 v1.

void mousemove(int x, int y)
{
  float axis[3], angle;

  // 轉換鼠座標轉換成單位向量
  vassign( v1, 2.0*x/512-1, -2.0*y/512+1, 1 );
  normalize(v1);

  // 避免使用全等的 v0 和 v1 去計算 axis angle.
  // 如果 v0 等於 v1 的話, 就是不用旋轉的意思.
  if( dot(v0,v1)>.999 )
    return;

  // 計算自轉軸 使之成為 單位向量.
  cross(axis,v0,v1);
  normalize(axis);

  // 計算自轉角.
  angle = acosf( clamp(dot(v0,v1),-1,1) );

  // 把 v0 重設成 v1, 等待下一之滑鼠移動事件的旋轉運算
  vassign( v0, v1 );

  // 最後, 我們把 是次旋轉, 合併到總旋轉 mo 之內.
  // 如果不了解它的意思, 請參考上一個教學.
  glPushMatrix();
    glLoadIdentity();
    glRotatef( angle*180/PI, axis[0], axis[1], axis[2] );
    glMultMatrixf( mo );
    glGetFloatv( GL_MODELVIEW_MATRIX, mo );
  glPopMatrix();
  glutPostRedisplay();
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 03:01:16
以下的只是基本向量運算, 我們聰明的網友們應該沒困難去解讀它們的, 不過, 也讓我簡述一下.

// 限制 x 的數值在 [a,b] 之間, 少於 a 當 a, 大於 b 當 b.
float clamp( float x, float a, float b ){ return x<a ? a : (x<b?x:b); }

// 向量內積
float dot( const float *a, const float *b ){ return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]; }

// 向量長度
float norm( const float *a ){ return sqrtf(dot(a,a)); }

// 設定 a 的 向量數值
void vassign( float *a, float x, float y, float z ){ a[0]=x; a[1]=y; a[2]=z; }

// 複製 b 的值 到 a
void vassign( float *a, const float *b ){ a[0]=b[0]; a[1]=b[1]; a[2]=b[2]; }

// 向量外積
void cross( float *a, const float *b, const float *c )
{
  a[0] = b[1]*c[2] - c[1]*b[2];
  a[1] = -b[0]*c[2] + c[0]*b[2];
  a[2] = b[0]*c[1] - c[0]*b[1];
}

// 使 a 成為單位向量
void normalize( float *a )
{
  float l = norm(a);
  a[0]/=l; a[1]/=l; a[2]/=l;
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 03:15:57
做功課是不能缺的吧, 是次教學, 跟上一個教學都是以同一的手法把 小旋轉 合併到 總合旋轉 之內, 因此, 兩個教學應該可以輕鬆的合併, 成為 按鍵+滑鼠 旋轉介面 的. 請你合併它們吧. 完成了的話, 請你簽個名吧.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 03:17:19
噢... 真的忙中有錯了... 範例中, 在處理滑鼠座標轉換的部份, 我把 視窗大少 硬設成 (512,512), 這樣, 視窗縮放時就會影響運算, 比較好的做法, 應該使用
glutGet( GLUT_WINDOW_WIDTH ) 和
glutGet( GLUT_WINDOW_HEIGHT )
去取得 視窗大少.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/23 上午 03:39:31
有一個小修正...

void mousemove(int x, int y)
{
  //...

  if( dot(v0,v1)>.999999) //<< 這裡的數值原來設定得太小, 把部份合適 v1 的事件也移除了

  //...
}
作者 : jojozerox(jojozerox)
[ 貼文 45 | 人氣 6735 | 評價 10 | 評價/貼文 0.22 | 送出評價 4 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/27 上午 10:33:27
感謝大大的教導
我終於試出來,只剩下一個功能沒研究出來
就是我第二次在使用滑鼠旋轉的時候
他不是從先前的位置開始旋轉而是從0的原本位置開始旋轉
是不是MO的問題...O_Oa
就算我加入了問題依然沒有解決Orz

例外就是內積的求角度方法
大大是不是dot後還要除於|v0|*|v1|才可以acos求出角度???
因為您的寫法沒有除於|v0|*|v1|讓我覺得這個步驟怪怪@@a~
 angle = acosf( clamp(dot(v0,v1),-1,1) );
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2189 | 人氣 89850 | 評價 10120 | 評價/貼文 4.62 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2007/11/27 下午 11:42:46
你要記錄著總合旋轉, 然後, 新的旋轉要合併到總合旋轉, 合併的方法上文範例已經有介紹了.



>大大是不是dot後還要除於|v0|*|v1|才可以acos求出角度???

v0 和 v1 是單位向量, 長度是 1, 因此可以省卻關於向量長度的計算.
作者 : sgmp2555029(雨嵐)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/5/17 下午 05:13:03
vassign( v1, 2.0*x/512-1, -2.0*y/512+1, 1 );

我想問一下 上面的2.0*x/512-1, -2.0*y/512+1,是什麼意思?

作者 : lab123(Lab123)
[ 貼文 11 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/4 下午 01:46:07
那個翻轉的圖形是利用載入的還是使用程式碼寫的??
這兩段程式碼能否套用載入OBJ檔圖形中??因為我們將OBJ圖形套入時 這兩段程式碼發現不能使用??!!那如果要修改成能導入OBJ檔的滑鼠翻轉之程式碼要如何修改??
作者 : lab123(Lab123)
[ 貼文 11 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/5/4 下午 01:46:14
那個翻轉的圖形是利用載入的還是使用程式碼寫的??
這兩段程式碼能否套用載入OBJ檔圖形中??因為我們將OBJ圖形套入時 這兩段程式碼發現不能使用??!!那如果要修改成能導入OBJ檔的滑鼠翻轉之程式碼要如何修改??
作者 : waiho(jaom)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2011/10/10 上午 01:09:03
大大我想請問一下下面的為何不可用x/get_window_w, 和y/get_window_height?

vassign( v1, 2.0*x/512-1, -2.0*y/512+1, 1 );

我想問一下 上面的2.0*x/512-1, -2.0*y/512+1,是什麼意思?
 板主 : 白老鼠(Gary)
 > OpenGL - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - OpenGL - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
OpenGL
1 白老鼠(Gary) 2720 
2 CROMAYEN2000 1530 
3 aming 500 
4 東昇 380 
5 PLAYER 120 
6 富伯 110 
7 qq 100 
8 NDark 80 
9 ozzy 60 
10 simula 60 
OpenGL
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
9.277344E-02