本文**:http://bbs.gameres.com/showthread.asp?postid=100771
關於用射線原理來揀取物件網上已經有完整的理論,另外directx也提供了乙個pick例子來演示,在這裡我將這些資料和理論來稍微的總結,並給出opengl下的完整實現。
相關的理論大體來自一篇英文資料和一篇總結性的中文資料,分別是:
前一篇完整講述用directx實現射線揀取物體的原理和實現。後一篇講述的是二維螢幕空間到三維世界空間的轉換原理。前一篇的名字是「direct3d中實現圖元的滑鼠拾取」,它的講述很好也很透徹。後一篇講述射線形成的原理,並且有原始碼例子。
下面就opengl進行實現。
第一步:
實現螢幕座標到三維世界空間座標的轉化,在這一步opengl要比directx簡單的多,利用函式 gluunproject直接可以得到螢幕座標相應的三維空間座標,示例如下:
gluunproject((gldouble)xpos,(gldouble)ypos,1.0,mvmatrix,projmatrix,viewport,&wx,&wy,&wz); xpos 和ypos 是以螢幕左下角為原點的螢幕座標,1.0代表返回zbuffer為1.0處(遠剪下面交點)的世界座標,mvmatrix 為視矩陣,通過getdoublev(gl_modelview_matrix,mvmatrix)得到,projmatrix為投影矩陣,通過glgetdoublev(gl_projection_matrix,projmatrix)得到,viewport為視口,通過glgetintegerv(gl_viewport,viewport)得到,剩下的wx、wy、wz 就是我們要得到的世界座標,得到這樣兩個世界座標,射線就確定了,或者也可以用原點(視點)來代替其中乙個點,因為這條射線是從視點出發的。
第二步:
用射線和要檢測的三角形求交點,用到的原理和公式如下。
原理一:三角形內的任意一點都可以用變數u、v和其三個頂點座標來確定,其中0(-dir.x)*len +(v2.x-v1.x)*u + (v3.x – v1.x )*v = originpoint.x -v1.x
(-dir.y)*len +(v2.y-v1.y)*u + (v3.y – v1.y )*v = originpoint.y -v1.y
(-dir.z)*len +(v2.z-v1.z)*u + (v3.z – v1.z )*v = originpoint.z -v1.z
或:len
【-dir,v2-v1,v3-v1】 = originpoint – v1
v這是乙個線性方程組,根據克拉姆法則,【-dir,v2-v1,v3-v1】不為零。
所以滿足條件:00, ,0偽碼實現(原理在directx pick例子中有原始碼實現):
// 三角形兩個邊的向量
vector3 edge1 = v1 - v0;
vctor3 edge2 = v2 - v0;
vctor3 pvec;
vec3cross( &pvec, &dir, &edge2 );// 差積
float det = vec3dot( &edge1, &pvec );// 點積
// det其含義為【-dir,v2-v1,v3-v1】矩陣展開
vector3 tvec;
if( det > 0 )//
else
if( det < 0.0001f )// 接近零視為0
return false;
// 求u的值,求線性方程組的解展開後等同於求點積展開
*u = vec3dot( &tvec, &pvec );
if( *u < 0.0f || *u > det )
return false;
// 求v的值
vector3 qvec;
vec3cross( &qvec, &tvec, &edge1 );
*v = vec3dot( &dir, &qvec );
if( *v < 0.0f || *u + *v > det )
return false;
// 計算t,並把t,u,v放縮為合法值
*t = d3dxvec3dot( &edge2, &qvec );
// 前面的t,v,u在計算時多乘了乙個係數det
float finvdet = 1.0f / det;
*t *= finvdet;
*u *= finvdet;
*v *= finvdet;
// 這裡這個演算法是微軟給出的,從幾何角度分析其含義十分難懂,真正的方法是根據線性方程租求解,巧的是文中的方法恰好和線性方程組整理出來的東西相符合,這大概就是幾何和代數相通的原理。
原始碼(vc6.0 + opengl + windows2000,除錯通過):
bool intersect********()
else
if( det < 0.0001f ) return false;
glfloat u ;
u = tvec[0]*pvec[0]+ tvec[1]*pvec[1]+ tvec[2]*pvec[2];
if( u < 0.0f || u > det ) return false;
glfloat qvec[3];
qvec[0]= tvec[1]*edge1[2] - tvec[2]*edge1[1];
qvec[1]= tvec[2]*edge1[0] - tvec[0]*edge1[2];
qvec[2]= tvec[0]*edge1[1] - tvec[1]*edge1[0];
glfloat v;
v = dir[0]*qvec[0]+dir[1]*qvec[1]+dir[2]*qvec[2];
if( v < 0.0f || u + v > det ) return false;
glfloat t = edge2[0]*qvec[0]+edge2[1]*qvec[1]+edge2[2]*qvec[2];
glfloat finvdet = 1.0f / det;
t *= finvdet;
u *= finvdet;
v *= finvdet;
return true;
void pick(glfloat xpos,glfloat ypos)
glfloat v0[3]=;
glfloat v1[3]=;
glfloat v2[3]=;
void drawglscene(glvoid)
opengl實現X射線渲染
x射線也就是輪郭線 實現原理 物體表面的法線與入射光線的夾角為90度時,剛好能看到物體的輪郭線 實現效果,不同的計算方式會得到不同的效果 頂點shader attribute vec3 pos attribute vec2 texcoord attribute vec3 normal uniform...
拾取操作的實現 OpenGL
opengl 中採用一種比較複雜的方式實現了拾取操作,即選擇模式。選擇模式是一種繪製模式,它基本思想是在一次拾取操作時,系統根據拾取操作的引數 如滑鼠位置 生成乙個特定視景體,然後由系統重新繪製場景中的所有圖元,但這些圖元並不會繪製到顏色快取中,系統跟蹤有哪些圖元繪製到了這個特定的視景體中,並將這些...
用OpenGL實現跳躍的立體小球
掌握opengl中顯示列表物件的使用方法。github位址 include stdafx.h include include include include include 色彩全域性常量 glfloat white 白色glfloat red 紅色glfloat green 綠色glfloat m...