用OpenGL實現射線揀取物件

2021-05-22 01:17:39 字數 3351 閱讀 9596

本文**: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...