OpenGL射线拾取

用鼠标选择网格上的三角形。
主要步骤:
1. 每次渲染设置摄像机后,用gluUnProject得到拾取射线,然后和每个三角形所在平面求交。
2. 若拾取起点到交点的向量 和 拾取射线向量的数量积大于0(即三角形在观察者前方),则判断是点是否在三角形内。
3. 若在,则 这个三角形被选择。

由于要不断求交,实际用的时候应该考虑粗略判断,以便减少求交次数。
代码如下(c#):
None.gif namespace  RayPick 
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
InBlock.gif    
public partial class Form1 : Form 
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif
InBlock.gif        Vec2i mpos;  
//鼠标在窗体内的位置 
InBlock.gif
        CDevice decice;   //初始化OpenGL 
InBlock.gif
        Vec3 rayStart = new Vec3(), rayEnd = new Vec3();   //拾取射线的起点和终点 
InBlock.gif

InBlock.gif        CTestMesh[] obj 
= new CTestMesh[4];  //物体 
InBlock.gif
        float yrot = 0;   
InBlock.gif
InBlock.gif        
double[] mv = new double[16], prj = new double[16];  //模型变换矩阵和投影矩阵 
InBlock.gif
         
InBlock.gif        ……………………………………………… 
InBlock.gif
InBlock.gif        
//--------------------------------------核心代码------------------------------------- 
InBlock.gif
//渲染循环 
InBlock.gif
        private void timer1_Tick(object sender, EventArgs e) 
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif
InBlock.gif            GL.glLoadIdentity(); 
InBlock.gif
InBlock.gif            GL.glClear(GL.GL_COLOR_BUFFER_BIT 
| GL.GL_DEPTH_BUFFER_BIT); 
InBlock.gif            GL.glRotated(
15100); 
InBlock.gif
InBlock.gif            GL.glRotated(yrot, 
010); 
InBlock.gif            GL.glTranslated(
0-50); 
InBlock.gif            yrot 
+= 0.3f
InBlock.gif
InBlock.gif            PickRay(); 
//得到拾取射线的起点和终点 
InBlock.gif

ExpandedSubBlockStart.gifContractedSubBlock.gif            
for (int i = 0; i <= 3; i++dot.gif{ obj[i].Render(); } 
InBlock.gif
InBlock.gif            decice.EndRendering(); 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif
InBlock.gif        
private void PickRay() 
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif
InBlock.gif            
double o1x, o1y, o1z; 
InBlock.gif            
double o2x, o2y, o2z; 
InBlock.gif
InBlock.gif            WGL.GetCursorPos(
ref mpos); 
InBlock.gif            Point p 
= this.PointToClient(new Point(mpos.x, mpos.y)); 
InBlock.gif            mpos.x 
= p.X; mpos.y = p.Y; 
InBlock.gif
InBlock.gif            
//更新矩阵 
InBlock.gif
            GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX , mv); 
InBlock.gif            GL.glGetDoublev(GL.GL_PROJECTION_MATRIX, prj); 
InBlock.gif
InBlock.gif            
int[] viewPort = new int[4]; 
InBlock.gif            GL.glGetIntegerv(GL.GL_VIEWPORT, viewPort); 
InBlock.gif
InBlock.gif            GLU.gluUnProject(mpos.x, (viewPort[
3- mpos.y - 1), 0.0, mv, prj, viewPort, out o1x, out o1y, out o1z); 
InBlock.gif            GLU.gluUnProject(mpos.x, (viewPort[
3- mpos.y - 1), 1.0, mv, prj, viewPort, out o2x, out o2y, out o2z); 
InBlock.gif
InBlock.gif            rayStart 
= new Vec3(o1x, o1y, o1z); 
InBlock.gif            rayEnd 
= new Vec3(o2x, o2y, o2z); 
InBlock.gif
ExpandedSubBlockEnd.gif        }
 
InBlock.gif
InBlock.gif        
//计算平面法向量,用来确定平面点法式方程 
InBlock.gif
        private static Vec3 CalcPlaneNormal(Vec3 P1, Vec3 P2, Vec3 P3) 
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif
InBlock.gif            Vec3 V1, V2; 
InBlock.gif            Vec3 result; 
InBlock.gif
InBlock.gif            V1 
= P2 - P1; 
InBlock.gif            V2 
= P3 - P1; 
InBlock.gif
InBlock.gif            result 
= V1 % V2; 
InBlock.gif
InBlock.gif            result.Normalise(); 
InBlock.gif
InBlock.gif            
return result; 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif
InBlock.gif        
//判断点是否在三角形内 
InBlock.gif
        public static bool IsInTrg(Vec3 vtxA, Vec3 vtxB, Vec3 vtxC, TriEqaResult pHit) 
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif
InBlock.gif            
//把点代入三边的方程,符号一样则在三角形内 
InBlock.gif
            LineEqv l1, l2, l3;    //LineEqv 空间直线方程 
InBlock.gif

InBlock.gif            l1 
= new LineEqv(vtxA, vtxB - vtxA); 
InBlock.gif            l2 
= new LineEqv(vtxC, vtxA - vtxC); 
InBlock.gif            l3 
= new LineEqv(vtxB, vtxC - vtxB); 
InBlock.gif
InBlock.gif        ………………………… 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif         
InBlock.gif        
public bool Intersect(Vec3 vtxA, Vec3 vtxB, Vec3 vtxC, ref Vec3 pos) 
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif
InBlock.gif            
//PlaneEqv 平面方程 
InBlock.gif
            PlaneEqv cu***ce = new PlaneEqv(vtxA, CalcPlaneNormal(vtxA, vtxB, vtxC)); 
InBlock.gif            Vec3 ori 
= rayEnd - rayStart; 
InBlock.gif            LineEqv curSpd 
= new LineEqv(rayStart, ori); 
InBlock.gif
InBlock.gif            
//三元一次方程组的解,用来求平面和直线的交点 
InBlock.gif
            TriEqaResult pHit = new TriEqaResult(curSpd, cu***ce); 
InBlock.gif            
//判断有无解 
ExpandedSubBlockStart.gifContractedSubBlock.gif
            if (!pHit.hasSolution) dot.gifreturn false; } 
InBlock.gif
InBlock.gif            pos 
= new Vec3(pHit.x, pHit.y, pHit.z); 
InBlock.gif
InBlock.gif            
//判断拾取起点到交点的向量 和 拾取射线向量的数量积 
InBlock.gif
            if ((pos - rayStart) * ori > 0
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif
InBlock.gif                
return IsInTrg(vtxA, vtxB, vtxC, pHit); 
ExpandedSubBlockEnd.gif            }
 
ExpandedSubBlockStart.gifContractedSubBlock.gif            
else dot.gifreturn false; } 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif
InBlock.gif        
private void Form1_Click(object sender, EventArgs e) 
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif
InBlock.gif            
int bestIdx = -1;   //拾取到的最近物体的索引 
InBlock.gif
            double bestDist = double.MaxValue;        //拾取到的最近物体的距离 
InBlock.gif

InBlock.gif            
for (int i = 0; i < 4; i++
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif
InBlock.gif                Vec3 intersect 
= new Vec3();  //交点 
InBlock.gif
                if (Intersect(obj[i].vtx[obj[i].faces.idxA], obj[i].vtx[obj[i].faces.idxB], obj[i].vtx[obj[i].faces.idxC], ref intersect)) 
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif
InBlock.gif                    
double dist = intersect & rayStart; 
InBlock.gif                    
//拾取起点到焦点的距离 和一个最近距离值比较,小于则更新距离值和物体索引 
InBlock.gif
                    if (dist < bestDist) 
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif
InBlock.gif                        bestDist 
= dist; bestIdx = i; 
ExpandedSubBlockEnd.gif                    }
 
ExpandedSubBlockEnd.gif                }
 
ExpandedSubBlockEnd.gif            }
 
ExpandedSubBlockStart.gifContractedSubBlock.gif            
if (bestIdx != -1dot.gif{ System.Windows.Forms.MessageBox.Show(bestIdx.ToString()); } 
ExpandedSubBlockEnd.gif        }
 
InBlock.gif         
InBlock.gif………………………… 
源代码: http://files.cnblogs.com/Yuri/RayPick.rar

转载于:https://www.cnblogs.com/Yuri/articles/786843.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值