使用OpenGL实现三维坐标的鼠标拣选
OpenGL鼠标拾取机制的应用
图 1是一个 简单  ̄ 9 , - 维正 方形模型和~ 幅纹 理贴图 。
在 绘 制 该 正 方 形 之 前 ,如 果 为 顶 点 V1 、V2 、V3 、V4
指定纹 理坐标 ,使每 个顶 点的坐 标和 它的纹理坐标 一一对 应 , 此 时开 启纹 理贴 图功能 ,就可使 Op e n GL根 据顶 点的 贴图 坐标渲 染出指定 的四边形 。如图 2所示 。
名 字标 识
Op e n GL有一种拾取记录数据 ,保存在一个 自定 义的缓
i
¨ ¨
O b j e c t 【 8 ] . 1 i s t c n a i a b j — I i s t t t  ̄ x t u r P — w a p s ,f … 一 i n d i c i e s ,
设定 当前线程 B g , i 宣染环境 。
字体初始化 。 像 素 格 式 设 置
鼠标拾取
拾 取机 制
在创 建 RC 函数 被调 用之 前 ,需对 设 备描 述 表设 置 好
适 当 的 像 素 格 式 。 其 原 理 为 : 函 数 Ch o o s e Pi x e l F o r ma t
该函数原型为 : g I S e l e c t Bu f e r ( GI s i z e i s i z e, Gl u i n t b u f e r ): 参数 s i z e为 该缓存 区的 具体 大小 ,指 针 b u f e r 为 自定 义缓存 区数组指针 ,其定义大小 即为 s i z e的大小 。 在 进入选择 模式之前 ,必须调 用函数 g l Se l e c t Bu f e r() 来指定缓存 区。
在 使 用 Op e n GL函 数 进 行 鼠标 拾 取 时 ,有 选 择和 反 馈 两 种 方 式 ,以实 现 对 屏 幕 上 的 某 个 物 体 的 信 息 提 供 , 达 到 交 互 的 目 的 。选 择 ( GL SEL EC T)、反 馈 ( GL F EE DB ACK)和绘 制 ( GL RE NDE R)是操 作 中 互相 独
关于鼠标屏幕坐标转3D空间坐标的问题
小弟我最近遇到一个问题,用opengl和MFC画图,搭建好MFC下的opengl画图环境后,首先在3D空间画了一个平面:glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);glLoadIdentity();glTranslated(0.0,-1.0,-30.0);glRotatef(x_angle, 1.0, 0.0, 0.0); //x_angle可以设置为任何值;一开始不妨设为0;后面还要用到此变量。
glColor3f(1.0, 0.0, 0.0);glBegin(GL_POLYGON);glVertex3f(-10, -5, 15);glVertex3f(10, -5, 15);glVertex3f(10, -5, -15);glVertex3f(0, -5, -25);glVertex3f(-10, -5, -15);glEnd();以上代码是画平面,在ONDRAW()里调用,编译后即可看到红色的平铺的一个平面;然后在菜单中增加一项:"用鼠标来画线"的菜单选项,响应函数如下:void CMYView::Onstraight(){// TODO: 在此添加命令处理程序代码m_straight=true; //这个BOOL变量用来判断是不是点了这个菜单选项;}接着在鼠标左键响应函数里添加:void CtestView::OnLButtonDown(UINT nFlags, CPoint point){// TODO: 在此添加消息处理程序代码和/或调用默认值if(m_straight){m_startpt=point;glPushMatrix();glTranslated(0.0,-1.0,-30.0);glGetIntegerv(GL_VIEWPORT, viewport); // 得到的是最后一个设置视口的参数,viewport在头文件中定//义,得到视区的X,Y,LEFT,BOTTOM;glGetDoublev(GL_MODELVIEW_MATRIX, modelview); //得到投影矩阵glGetDoublev(GL_PROJECTION_MATRIX, projection);//得到透视矩阵glPopMatrix();winX = point.x; //得到视区坐标winY = viewport[3] - point.y;//视区坐标//进行坐标转换,把wniX,winY,0.0(代表近裁减面)转化为3D空间坐标:posX,posY,posZ; gluUnProject(winX, winY, 0.0, modelview, projection, viewport, &posX, &posY, &posZ);GLdouble nearpt[3],farpt[3];nearpt[0]=posX;nearpt[1]=posY;nearpt[2]=posZ;//转化为远裁减面3D坐标gluUnProject(winX, winY, 1.0, modelview, projection, viewport, &posX, &posY, &posZ);farpt[0]=posX;farpt[1]=posY;farpt[2]=posZ;//得到两个裁减面之间的距离(不知道这么说对不对),然后得到3个坐标resultpt[0,1,2]; //这3个坐标表示的是当鼠标移动时,得到的鼠标屏幕坐标在红色平面上的投影坐标,简单说就是鼠标发射的射线和红色平面相交的交点的3d坐标;GLdouble tmppt[3],resultpt[3];tmppt[0]=farpt[0]-nearpt[0];tmppt[1]=farpt[1]-nearpt[1];tmppt[2]=farpt[2]-nearpt[2];resultpt[0]=tmppt[0]*(-5-nearpt[1])/tmppt[1]+nearpt[0];resultpt[1]=-5;resultpt[2]=tmppt[2]*(-5-nearpt[1])/tmppt[1]+nearpt[2];//----------------------------}CScrollView::OnLButtonDown(nFlags, point);}然后就可以在onmosemove函数里用类似的方法得到即时的在红色平面的投影坐标值,并把这个坐标点画在红色平面上(具体方法略);现在我已经把这个投影点画出来,并且能让它随着鼠标移动而在红色平面上移动,但是,问题在于:假如我现在把x_angle设为90度,即让这个平铺的平面绕X轴转90度竖直的对着我们,鼠标移动到当前窗口的最上面时,对应的投影坐标点只能移动到红色平面的中间部分,不能再向上移动了,也就是说这个角度下,我不能将鼠标的投影坐标点移动到红色平面的上半部分,请问这是为什么?我需要怎么改进才能在这种角度下让投影坐标能移动到红色平面的上半部分?。
深入理解OpenGL拾取模式(OpenGL Picking)
深入理解OpenGL拾取模式(OpenGL Picking)在用OpenGL进行图形编程的时候,通常要用鼠标进行交互操作,比如用鼠标点选择画面中的物体,我们称之为拾取(Picking),在网上看了很多OpenGL拾取的文章,但大多是只是介绍在OpenGL中如何拾取,如何利用OpenGL提供的一系列函数来完成拾取,最多再简单介绍下OpenGL的名字栈(Name stack),拾取矩阵(Picking Matrix)等等,但是拾取真正的原理确没有提到。
所以,我在这里为大家详细介绍下OpenGL中拾取是怎样实现的,以及其背后的真正原理。
OpenGL中的拾取是对OpenGL图形管线的一个应用。
所以OpenGL中的拾取并不是像D3D一样采用射线交叉测试来判断是否选中一个目标,而是在图形管线的投影变换(Projection Transformation)阶段利用拾取矩阵来实现的。
为了理解这个过程,先来复习一下OpenGL的图形管线。
总的来说,OpenGL图形管线大体分为上面的五个阶段。
在编程的时候使用glMatrixMode(GL_MODELVIEW),或者glMatrixMode(GL_PROJECTION)就是告诉OpenGL 我们是要在那个阶段进行操作。
先来看看投影变换,因为理解投影变换是理解OpenGL拾取的前提条件。
为了简单起见,这里以正交投影(Orthogonal Projection)为例。
在OpenGL中,使用正交投影可以调用glOrtho (left, right, bottom, top, zNear, zFar),其中的六个参数分别对应正交投影视体的六个平面到观察坐标系原点的距离。
一旦在程序中调用了这个函数,OpenGL会马上创建根据给定的六个参数创建一个视体,并且把视体的大小归一化到-1到1之间,也就是说,OpenGL会自动把你给的参数所对应的x,y,z值转换为-1到1之间的值,并且这个视体的中心就是观察坐标系的原点。
OpenGL下三维模型的显示和自由旋转
20 世纪 70 年代后期 ,计算机图形工作站的出现推动了基于矢量的 3D 计算机图形学 ( 3D Computer Grap hics) 的迅速发展 . 目前 3D 计算机图形学已经广泛地应用于各个行业和领域 , 如 :3D Max ,Poser 等三维设计软件可以生成复杂的三维模型 . 考虑到软件的通用性 , 本文选 取了图形数据工业标准 — — — DXF 格式的三维模型数据文件作为源文件 , 不仅可以对任意软件 生成的 DXF 格式的三维模型文件进行处理 ,同时还可以处理三维模型和二维模型 . 在三维软件的开发方面 ,目前主要存在 2 种 API : 一个是微软的 Direct 3D ,一个是开放标 准 Open GL . Direct 3D 大量应用于民用产品 ,如游戏领域 ,而 Open GL 作为应用最为广泛的高 性能工业图形标准 ,是开发高质量的二维或三维交互式图形应用程序的首选环境 [ 1 ] . Open GL 提供的应用程序 API 包含了许多现代图形学中的基本功能 , 如 : 图元的绘制 、 颜色指定 、 纹理 映射 、 光照处理 、 双缓存动画和特效等 ; 另外 , 其跨平台的特性也备受用户推崇 ; Open GL 简化 了图形软件的开发过程 ,缩短了产品进入市场的周期 : 因此 ,我们选用 Open GL 作为开发工具 . 在对三维模型的研究中 ,需要对其进行自由旋转 ,以方便从任意角度细致地进行观察 . 本 文提供了一种通过操纵虚拟球实现对模型自由旋转的方法 ,使用户仅通过鼠标的拖动就能从 任何角度观看模型 .
v= b +c , cosβ= c/ v , sinβ= b/ v ;
2 2
1
Rx =
0 0 0
0 β cos sinβ - sinβ cosβ 0 0
OpenGL中的三维处理
OpenGL通过相机模拟、可以实现计算机图形学中最基本的三维变换,即几何变换、投影变换、裁剪变换、视口变换等,同时,OpenGL还实现了矩阵堆栈等。
理解掌握了有关坐标变换的内容,就算真正走进了精彩地三维世界。
一、OpenGL中的三维物体的显示(一)坐标系统在现实世界中,所有的物体都具有三维特征,但计算机本身只能处理数字,显示二维的图形,将三维物体及二维数据联系在一起的唯一纽带就是坐标。
为了使被显示的三维物体数字化,要在被显示的物体所在的空间中定义一个坐标系。
这个坐标系的长度单位和坐标轴的方向要适合对被显示物体的描述,这个坐标系称为世界坐标系。
世界坐标系是始终固定不变的。
OpenGL还定义了局部坐标系的概念,所谓局部坐标系,也就是坐标系以物体的中心为坐标原点,物体的旋转或平移等操作都是围绕局部坐标系进行的,这时,当物体模型进行旋转或平移等操作时,局部坐标系也执行相应的旋转或平移操作。
需要注意的是,如果对物体模型进行缩放操作,则局部坐标系也要进行相应的缩放,如果缩放比例在案各坐标轴上不同,那么再经过旋转操作后,局部坐标轴之间可能不再相互垂直。
无论是在世界坐标系中进行转换还是在局部坐标系中进行转换,程序代码是相同的,只是不同的坐标系考虑的转换方式不同罢了。
计算机对数字化的显示物体作了加工处理后,要在图形显示器上显示,这就要在图形显示器屏幕上定义一个二维直角坐标系,这个坐标系称为屏幕坐标系。
这个坐标系坐标轴的方向通常取成平行于屏幕的边缘,坐标原点取在左下角,长度单位常取成一个象素。
(二)三维物体的相机模拟为了说明在三维物体到二维图象之间,需要经过什么样的变换,我们引入了相机(Camera)模拟的方式,假定用相机来拍摄这个世界,那么在相机的取景器中,就存在人眼和现实世界之间的一个变换过程。
图一、相机模拟OpenGL中的各种坐标变换从三维物体到二维图象,就如同用相机拍照一样,通常都要经历以下几个步骤:1、将相机置于三角架上,让它对准三维景物,它相当于OpenGL中调整视点的位置,即视点变换(Viewing Transformation)。
基于OpenGL三维拾取技术研究
基于OpenGL三维拾取技术研究刘彬孙永高明马克李晓诗(解放军信息工程大学测绘学院河南郑州陇海中路66号450052 )【摘要】在OpenGL环境中,三维场景是通过二维平面表现的,本文通过对这种表现原理的研究,介绍了“射线拾取法”和“包围盒法”两种算法,分析了其实现的原理,并介绍了相关的核心算法。
在此基础之上,又提出了一种适用于基于DEM的三维系统的改进算法,把三维空间中的拾取问题转化到了二维平面上,大大简化了拾取的抽象性和复杂性,提高了拾取的效率,更易于理解和实现。
【关键字】DEM OpenGL 拾取光标投影模型1. 引言在三维图形系统中,现实世界中的物体要依次通过模型视点变换,投影变换和视口变换才影射到屏幕窗口上的,而对物体的拾取操作,其实就是在二维屏幕上对三维空间中的物体的拾取技术。
2. 射线拾取技术射线拾取算法是判断由视点发出经屏幕光标的射线是否与目标物体相交。
其具体的实现实现方法如下所示:(1)确定射线的位置及方向,可以通过取得射线与远近两个裁减面的交点来确定。
(2)判断射线与拾取目标是否有交点,因为在射线上,任意一点可以表示为单位向量(L)与模(len)的乘积,所以交点可以表示为:X= P’+L*len ;又因为三角形内的任意一点都可以用变量u、v和其三个顶点坐标来确定,其中0<u<1 0<v<1、,0<u+v<1。
设三个顶点为T1,T2,T3则:X= T1 + u*( T2-T1) + v*( T3-T1) ;由此可以得出P’-T1 =-( L*len) + u*( T2-T1) + v*( T3-T1) ,即方程组:(-L.x)*len +( T2.x- T1.x)*u + (T3.x –T1.x )*v = P’.x -T1.x(-L.y)*len +( T2.y- T1.y)*u + (T3.y –T1.y )*v = P’.y -T1.y(-L.z)*len +( T2.z- T1.z)*u + (T3.z –T1.z )*v = P’.z -T1.z这是一个线性方程组,根据克拉姆法则,当满足条件:0<v<1,0<u<1, len>0, ,0<u+v<1 和【-L,T2-T1,T3-T1】不为零则射线和三角形相交。
实验二结合MFC与OpenGL实现三维应用程序的显示
实验二结合MFC与OpenGL实现三维应用程序的显示1.实验目的●熟悉OpenGL的编程环境设置●熟悉MFC的基本编程框架●MFC与OpenGL混合编程的设置●OpenGL中基本图元的绘制2.实验内容●创建MFC+OpenGL的编程环境●利用三角形和四边形等基本图元绘制底面圆圆心在坐标原点,半径为r,高为h,方向沿z轴方向的圆柱3.实验指导3.1MFC应用程序框架在Visual C++ 2.0以后的版本中,Microsoft公司推出了MFC(Microsoft Foundation Class)类库。
MFC类库是用来编写Windows程序的C++类集。
使用MFC类库,可以简化应用程序的开发,从而缩短开发周期,而且代码的可靠性和可重用性也大大提高。
3.1.1MFC应用程序的文档/视图结构概述MFC提供了一个典型且实用的基于文档与视图的应用程序框架模板,按照其应用程序生成向导的导引步骤(MFC AppWizard)就可以创建一个基于文档/视图结构的MFC应用程序框架。
在此框架的基础上,设计和插入相关的对象,就可以实现交互式的用户界面、几何模型的管理和操作、图形图像的显示,以及其他各种专业功能。
在MFC的文档/视图结构的应用程序框架中,文档类和视图类是成对出现的。
文档用于管理应用程序的数据;而视图用于显示文档中的数据,并处理与用户的交互信息。
MFC通过文档类和视图类的划分,使数据的存储和显示既相对独立又相互关联。
在MFC所提供的框架结构中,文档与视图的关系可以由图0.1简要表示。
MFC中的视图和文档是由视图类(CView Class)和文档类(CDocument Class)分别表示的。
视图类可以调用其本身的成员函数GetDocument(),获得一个指向文档类的指针,从而能够访问文档类中的数据。
例如:在视图类中的OnDraw()函数中,视图类通过调用GetDocument()函数获得一个指向文档累的指针,然后通过这个指针获取文档类中的数据,并使用CDC类(负责处理应用程序显示设备接口的MFC类)中的函数将这些数据绘制在视图窗口中。
轻轻松松做OpenGL拾取
轻轻松松做OpenGL拾取概述在科学和工程的3维可视化应用当中,用户在屏幕上点击就可以让应用程序知道用户点击的是什么对象。
我们将这一过程,称之为拾取。
想象一下,这一过程在软件中由自己编程来实现,将如何进行的。
你得为此做各种变换,并找出各个对象最终在屏幕上的位置,然后判断究竟哪一下与鼠标最近。
当然这样做是可行的,但没有人会真正去做这个事,因为过程实在是太繁琐了。
幸运的是,OpenGL让硬件提供了对拾取的支持。
基本思路如下:1.让硬件处于拾取(或“选择”)模式;2.让硬件在拾取模式下,重新绘制场景,只不过不加入颜色;3.绘制场景时,为需要做拾取的对象命名;4.根据鼠标位置加上一定的误差范围,硬件返回选择的结果;5.让硬件重新回到绘图(“渲染”)模式。
设置首先是进行设置,从一组#define和变量声明开始。
PICK_TOL来定义拾取误差(按像素计),PICK_BUFFER_SIZE是拾取对象命名数组PickBuffer的大小。
RenderMode记录绘图的模式。
/* picking tolerance in pixels: */#define PICK_TOL 10./* how big to make the pick buffer: */#define PICK_BUFFER_SIZE 256unsigned int PickBuffer[PICK_BUFFER_SIZE]; /* picking buffer */int RenderMode; /* GL_RENDER or GL_SELECT */InitGraphics()在这个函数中,告诉硬件使用哪个数组作拾取名字数组,这个数组多大。
它必须在创建窗口后完成。
/* open the window and set its title: */glutInitWindowSize( INIT_WINDOW_SIZE, INIT_WINDOW_SIZE ); glutInitWindowPosition( WIN_LEFT, WIN_BOTTOM );GrWindow = glutCreateWindow( WINDOWTITLE );glutSetWindowTitle( WINDOWTITLE );. . ./* setup the picking buffer: */glSelectBuffer( PICK_BUFFER_SIZE, PickBuffer );拾取对象命名为需要做拾取的对象进行命名,名字实际上是32位无符号整数。
《2024年五自由度三维鼠标的设计与实现》范文
《五自由度三维鼠标的设计与实现》篇一一、引言随着科技的进步和计算机技术的飞速发展,人机交互技术已经成为人们日常生活和工作中不可或缺的一部分。
五自由度三维鼠标作为一种先进的交互设备,其设计实现了在三维空间中的高精度、高效率操作。
本文将详细介绍五自由度三维鼠标的设计思路、实现方法以及其在现实应用中的价值。
二、五自由度三维鼠标的设计1. 设计理念五自由度三维鼠标的设计理念在于提供更自然、更高效的人机交互方式。
其核心思想是通过多方向的自由移动,实现用户在三维空间中的精确操作。
这种设计理念旨在提高用户的工作效率和操作体验。
2. 结构设计五自由度三维鼠标的结构设计主要包括基座、旋转机构、移动机构等部分。
基座用于支撑整个设备,旋转机构和移动机构则分别负责实现鼠标在三个方向上的旋转和移动。
此外,为了实现五自由度操作,还需要配备相应的传感器和控制系统。
3. 自由度设计五自由度三维鼠标的五个自由度包括:X轴、Y轴、Z轴的平移,以及绕X轴、Y轴的旋转。
这些自由度的设计使得用户可以轻松地在三维空间中完成各种复杂的操作任务。
三、五自由度三维鼠标的实现1. 硬件实现五自由度三维鼠标的硬件实现主要包括传感器、控制系统和机械结构等部分。
传感器用于检测鼠标的移动和旋转信息,控制系统则负责处理这些信息并输出到计算机中。
机械结构则负责支撑整个设备并实现鼠标的移动和旋转。
2. 软件实现在软件方面,需要编写相应的驱动程序和控制软件。
驱动程序负责与硬件设备进行通信,获取鼠标的移动和旋转信息。
控制软件则负责将这些信息转换为计算机可以识别的指令,并实现与计算机的交互。
四、五自由度三维鼠标的应用价值五自由度三维鼠标的应用价值主要体现在以下几个方面:1. 提高工作效率:五自由度三维鼠标的高精度、高效率操作方式可以大大提高用户的工作效率,减少操作时间。
2. 提升操作体验:五自由度三维鼠标的设计更加符合人体工程学原理,使用户在操作过程中更加自然、舒适。
delphiopengl三维坐标鼠标拾取[资料]
delphi opengl 三维坐标鼠标拾取unit MainUnit;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs,gl,glu, ExtCtrls, StdCtrls,Math, ComCtrls;typeTMainForm = class(TForm)StatusBar1: TStatusBar;procedure FormCreate(Sender: TObject);procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);procedure Panel1Resize(Sender: TObject);procedure FormResize(Sender: TObject);procedure FormMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,procedure FormMouseUp(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);//procedure glDrawString(x,y,z:Extended;const ss: string);private{ Private declarations }public{ Public declarations }end;constBUFSIZE = 32; // Size of selection buffer.typegl1f = glfloat;gl3f = array [0..2] of glfloat;gl4f = array [0..3] of glfloat;varMainForm: TMainForm;WinW:integer;WinH:integer;keys : Array[0..255] of Boolean; // Holds keystrokesviewP :TviewPortArray;modeM,projM :T16DArray;//viewP:Array [0..3] of GLint;//modeM,projM:Array [0..15] of GLdouble;// User vaiablesLdown,Rdown :boolean;zPos :Tpoint;x0, y0 :Integer; // mouse movementrx, ry :single;// Object positionobj :array[1..236410] of gl3f; //对象的空间坐标Pos :array[1..236410] of gl3f; //对象的屏幕坐标{***********************}p,pi:single;m_Pattern:Integer;mat:array[0..3] of GLDouble;m_bhide,m_bColor:boolean;dnum:integer;bs,zl:double;implementation{$R *.dfm}procedure glDrawCamber;varx1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4,x0,y0,z0,QL,m,n:Extende d;i,j,z,k,tt:integer;Arr1:Array [1..720,1..3] of Extended;Arr2:Array [1..720,1..3] of Extended;beginglColor3f(1.0,1.0,0.0);// glbegin(GL_LINE_LOOP);//初始化for i:=1 to 720 dobeginfor j:=1 to 3 dobeginArr1[i,j]:=100;Arr2[i,j]:=100;end;end;for i:=0 to 80 dobeginfor j:=1 to 720 dobegin//tt:=tt+1;z:=((j) mod 16)+1;ql:=p;case z of1:beginql:=p*0.95;//glColor3f(1.0,0.0,0.0); end;2:beginql:=p*0.96;// glColor3f(0.0,1.0,0.0); end;3:beginql:=p*0.97;//glColor3f(0.0,0.0,1.0);end;4:beginql:=p*0.99;//glColor3f(1.0,1.0,0.0);end;5:beginql:=p*1.00;//glColor3f(1.0,0.0,1.0);end;6:ql:=p*1.02;7:ql:=p*1.03;8:ql:=p*1.04;9:ql:=p*1.05;10:ql:=p*1.04;11:ql:=p*1.03;12:ql:=p*1.02;13:ql:=p*1.00;14:ql:=p*0.99;15:ql:=p*0.98;16:ql:=p*0.97;end;k:=i-40;m:=k/2;n:=j/2;x0:=ql*cos(m*pi/180)*cos(n*pi/180); z0:=ql*sin(m*pi/180);y0:=ql*cos(m*pi/180)*sin(n*pi/180);Arr1[j,1]:=x0;Arr1[j,2]:=y0;Arr1[j,3]:=z0;if (i>0) and (j>1) thenbeginx1:=Arr2[j-1,1];y1:=Arr2[j-1,2];z1:=Arr2[j-1,3];x2:=Arr1[j-1,1];y2:=Arr1[j-1,2];z2:=Arr1[j-1,3];x3:=Arr2[j,1];y3:=Arr2[j,2];z3:=Arr2[j,3];x4:=Arr1[j,1];y4:=Arr1[j,2];z4:=Arr1[j,3];//GlBegin(GL_POINTS);//GlBegin(GL_lines);//glColor3f(1.0,1.0,0.0); //GlBegin(GL_POLYGON);GlBegin(GL_LINE_LOOP);glVertex3f(x1,y1,z1);glVertex3f(x3,Y3,z3);glVertex3f(x4,y4,z4);glVertex3f(x2,y2,z2);glend();end;if j=720 thenbeginfor tt:=1 to 720 dobeginArr2[tt,1]:=Arr1[tt,1];Arr2[tt,2]:=Arr1[tt,2];Arr2[tt,3]:=Arr1[tt,3];end;end;end;end;//glend();end;procedure glDrawColor( Mode :integer);varx1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4:GL1f;i,j,k,pnum:integer;beginpnum:=3;for i:=0 to 40 dobeginfor j:=1 to 360 dobeginpnum:=pnum+1;if mode = GL_SELECT then glLoadName(pnum);k:=i-20;x1:=P*cos(k*pi/180)*cos(j*pi/180);z1:=p*sin(k*pi/180);y1:=p*cos(k*pi/180)*sin(j*pi/180);x2:=P*cos((k+1)*pi/180)*cos(j*pi/180);z2:=p*sin((k+1)*pi/180);y2:=p*cos((k+1)*pi/180)*sin(j*pi/180);x3:=P*cos(k*pi/180)*cos((j+1)*pi/180);z3:=p*sin(k*pi/180);y3:=p*cos(k*pi/180)*sin((j+1)*pi/180);x4:=P*cos((k+1)*pi/180)*cos((j+1)*pi/180);z4:=p*sin((k+1)*pi/180);y4:=p*cos((k+1)*pi/180)*sin((j+1)*pi/180);if (k<=5) and (k>=-5) thenbeginif (j>=1) and (j<=90) thenglColor3f(1.0,0.0,0.0)else if (j>90) and (j<=180) thenglColor3f(0.0,1.0,1.0)else if (j>180) and (j<=270) thenglColor3f(1.0,1.0,1.0)elseglColor3f(1.0,1.0,0.0);endelse if ((k<=10) and (K>=5)) or ((k>=-10) and (k<=-5)) thenglColor3f(0.0,1.0,0.0)else if ((k<=15) and (K>=10)) or ((k>=-15) and (k<=-10)) thenglColor3f(0.0,0.0,1.0)elseglColor3f(0.0,1.0,1.0);glbegin(GL_POLYGON );if hit=pnum thenglColor3f(1, 0, 0);// obj[pnum]:=Vert(x1,y1,z1);//obj[pnum+1]:=Vert(x2,y2,z2);//obj[pnum+2]:=Vert(x3,y3,z3);//obj[pnum+3]:=Vert(x4,y4,z4);//glVertex3fv(@obj[pnum]);//glVertex3fv(@obj[pnum+2]);//glVertex3fv(@obj[pnum+3]);//glVertex3fv(@obj[pnum+1]);glVertex3f(x1,y1,z1);glVertex3f(x3,Y3,z3);glVertex3f(x4,y4,z4);glVertex3f(x2,y2,z2);glend();//end;end;end;end;procedure glDrawString(x,y,z:Extended;const ss: string);const MAX_char:integer=128;{$J+}const isFirstCall:integer = 1;const lists:GLuint=0;{$J-}vari,j:integer;beginglRasterPos3f(x,y,z);if isFirstCall=1 thenbegin// 如果是第一次调用,执行初始化// 为每一个ASCII字符产生一个显示列表isFirstCall:=0;// 申请MAX_CHAR个连续的显示列表编号lists:=glGenLists(MAX_CHAR);// 把每个字符的绘制命令都装到对应的显示列表中wglUseFontBitmaps(wglGetCurrentDC(),0,MAX_CHAR,lists);end;// 调用每个字符对应的显示列表,绘制每个字符for i:=1 to length(ss) dobeginj:=Ord(ss[i]);glCallList(Lists+j);end;end;{------------------------------------------------------------------}{ Function to convert int tostring. }{------------------------------------------------------------------}function IntToStr(Num : Integer) : String;beginStr(Num, result);end;function Vert( x,y,z :gl1f) :gl3f;beginresult[0]:=x; result[1]:=y; result[2]:=z;end;function Point(X, Y: Integer): TPoint;beginResult.X:= X; Result.Y:= Y;end;procedure setAxisList;beginglNewList(1,GL_COMPILE); //坐标线glColor3f(1,0.5,0.5); glLineWidth (1);//绘图直线时笔的宽度glBegin(GL_LINE_STRIP);glColor3f(1,0,1); glVertex3f( 0,0,0);glVertex3f( 2.5,0,0);glColor3f(0,1,0); glVertex3f( 0,0,0);glVertex3f( 0,2.5,0);glColor3f(0,0,1); glVertex3f( 0,0,0);glVertex3f( 0,0,2.5);glEnd;glEndList;end;procedure setupPixelFormat(DC:HDC);constpfd:TPIXELFORMATDESCRIPTOR =(nSize:sizeof(TPIXELFORMATDESCRIPTOR); // sizenVersion:1; // versiondwFlags:PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER; // support double-bufferingiPixelType:PFD_TYPE_RGBA; // color typecColorBits:24; // preferred color depthcRedBits:0; cRedShift:0; // color bits (ignored)cGreenBits:0; cGreenShift:0;cBlueBits:0; cBlueShift:0;cAlphaBits:0; cAlphaShift:0; // no alpha buffercAccumBits: 0;cAccumRedBits: 0; // no accumulation buffer,cAccumGreenBits: 0; // accum bits (ignored)cAccumBlueBits: 0;cAccumAlphaBits: 0;cDepthBits:16; // depth buffercStencilBits:0; // no stencil buffercAuxBuffers:0; // no auxiliary buffersiLayerType:PFD_MAIN_PLANE; // main layerbReserved: 0;dwLayerMask: 0;dwVisibleMask: 0;dwDamageMask: 0; // no layer, visible, damage masks);var pixelFormat:integer;beginpixelFormat := ChoosePixelFormat(DC, @pfd);if (pixelFormat = 0) thenexit;if (SetPixelFormat(DC, pixelFormat, @pfd) <> TRUE) thenexit;end;{------------------------------------------------------------------}{ InitialiseOpenGL }{------------------------------------------------------------------}procedure GLInit;begin// set viewing projectionglMatrixMode(GL_PROJECTION);glFrustum(-0.1, 0.1, -0.1, 0.1, 0.3, 25.0);// position viewer */glMatrixMode(GL_MODELVIEW);glEnable(GL_DEPTH_TEST);glClearColor(0.0, 0.0, 0.0, 0.0); // Black Background// glShadeModel(GL_SMOOTH); // Enables Smooth Color ShadingglClearDepth(1.0); // Depth Buffer SetupglDepthFunc(GL_LESS); // The Type Of Depth Test To DoglEnable(GL_DEPTH_TEST); // Enable Depth BufferglHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//Realy Nice perspective calculationsglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //控制混合方式glEnable(GL_BLEND); //混合(透明)使能glEnable(GL_line_smooth);glEnable(GL_POINT_SMOOTH);end;procedure gldrawball;beginend;//############ 获得对象的屏幕坐标 ################function GetWinPos( h:integer) :gl3f;var wx,wy,wz :glDouble; // 屏幕坐标begingluProject( obj[h,0],obj[h,1],obj[h,2], modeM,projM,viewP, @wx,@wy,@wz);result:= Vert( wx,wy,wz);end;//############ 将屏幕坐标转换为空间坐标 ################function GetObjPos( x,y,z:gl1f) :gl3f;var px,py,pz :glDouble; // 对象坐标a,b:Double;begingluUnProject( x,y,z, modeM,projM,viewP, @px,@py,@pz);result:= Vert( px,py,pz);MainForm.StatusBar1.Panels[0].Text:='X='+FloattoStr(px); MainForm.StatusBar1.Panels[1].Text:='Y='+FloattoStr(py); MainForm.StatusBar1.Panels[2].Text:='Z='+FloattoStr(pz);if Hit>3 thenbegina:=ArcSin(pz/p)*180/pi; //纬度与xy平面的反正弦b:=ArcCos(Sqrt(px*px+pz*pz)/p)*180/pi; //经度与xz平面的反余弦if (px<0) and (py>=0) thenb:=180-b;if (px<0) and (py<0) thenb:=b-180;MainForm.StatusBar1.Panels[3].Text:='LON='+FloattoStr(a); MainForm.StatusBar1.Panels[4].Text:='LAT='+FloattoStr(b);end;end;--------}{ Handle windowresize }{------------------------------------------------------------------}procedure glResizeWnd(Width, Height : Integer);beginwinW:= width; winH:= Height;if (Height = 0) then Height := 1; // prevent divide by zero exceptionglViewport(0, 0, Width, Height); // Set the viewport for the OpenGL windowglMatrixMode(GL_PROJECTION); // Change Matrix Mode to ProjectionglLoadIdentity(); // Reset ViewgluPerspective(45, Width/Height, 1, 100); // Do the perspective calculations. Last value = max clipping depthglMatrixMode(GL_MODELVIEW); // Return to the modelview matrixglLoadIdentity(); // Reset Viewend;{------------------------------------------------------------------}{ Function to draw the actualscene }--------}procedure glDraw( Mode :integer);var h :integer;i,j,latw,lonw:integer;x,y,z,x0,y0,z0,m,n:Extended;x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4,lx,ly,lz:GL1f;k,pnum:integer;pp:Array of Array of GL3f;beginglClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth BufferglLoadIdentity(); // Reset The ViewglTranslatef(0,0,-eye);glRotatef( ry, 1,0,0);glRotatef( rx, 0,0,1);glColor3f(0, 0.1, 0.6);glCallList(1);{*******************************}glmatrixmode(gl_modelview);glclipplane(gl_clip_plane1,@mat[0]);if m_bhide thenglenable(gl_clip_plane1)elsegldisable(gl_clip_plane1);//glLineWidth(1);{===================================}glPointSize(6);glLineWidth(2);{ if mode = GL_SELECT then glLoadName(1);glBegin(GL_POINTS);glColor3f(1, 1, 0);if hit=1 then glColor3f(1, 0, 0) else glColor3f(1, 1, 0); glVertex3fv(@obj[1]); //画第一点glEnd;if mode = GL_SELECT then glLoadName(2);glBegin(GL_POINTS);glColor3f(1, 1, 0);if hit=2 then glColor3f(1, 0, 0) else glColor3f(1, 1, 0); glVertex3fv( @obj[2]); //画第二点glEnd;if mode = GL_SELECT then glLoadName(3);glBegin( GL_LINES);if hit=3 then glColor3f(1, 0, 0) else glColor3f(1, 0.5, 0.5);glVertex3fv( @obj[1]); glVertex3fv( @obj[2]); //连接线glEnd;}//if mode = GL_SELECT then glLoadName(4);{**********************************}{红色: (1.0,0.0,0.0)(1.0,0.125,0.0)红橙色: (1.0,0.25,0.0);(1.0,0.45,0.0)橙色: (1.0,0.65,0.0)(1.0,0.7,0.0)黄橙色: (1.0,0.8,0.0)(1.0,0.9,0.0)黄色: (1.0,1.0,0.0)(0.75,1.0,0.0)黄绿色: (0.5,1.0,0.0)(0.25,1.0,0.0)绿色: (0.0,1.0,0.0)(0.0,0.75,0.25)蓝绿色: (0.0,0.5,0.5)(0.0,0.25,0.75)蓝色 (0.0,0.0,1.0)(0.25,0.0,1.0)蓝紫色: (0.5,0.0,1.0)(0.5,0.0,0.75)紫色: (0.5,0.0,0.5)(0.75,0.0,0.75)红紫色: (1.0,0.0,1.0)(1.0,0.0,0.5)}pnum:=0;glPointSize(1);setlength(pp,1440,161);for i:=0 to 1439 dobeginfor j:=0 to 159 dobegin//pnum:=pnum+1;//if mode=GL_Select then GLLoadName(pnum);n:=i*0.25;m:=j*0.25-20;pp[i,j,0]:=P*cos(m*pi/180)*cos(n*pi/180);pp[i,j,2]:=p*sin(m*pi/180);pp[i,j,1]:=p*cos(m*pi/180)*sin(n*pi/180);endend;for i:=0 to 1438 dobeginfor j:=0 to 159 dobegin//颜色选取pnum:=pnum+1;if mode=GL_Select then GLLoadName(pnum);//k:=(pnum div 23000)+1;case pnum of1..9600: glColor3f(1.0,0.0,0.0); //红色9601..19200:glColor3f(1.0,0.125,0.0);19201..28800: glColor3f(1.0,0.25,0.0);28801..38400: glColor3f(1.0,0.45,0.0);38401..48000:glColor3f(1.0,0.65,0.0);48001..57600: glColor3f(1.0,0.7,0.0);57601..67200: glColor3f(1.0,0.8,0.0);67201..76800: glColor3f(1.0,0.9,0.0);76801..86400: glColor3f(1.0,1.0,0.0);86401..96000: glColor3f(0.75,1.0,0.0);96001..105600: glColor3f(0.5,1.0,0.0);105601..115200: glColor3f(0.25,1.0,0.0);115201..124800:glColor3f(0.0,1.0,0.0);124801..134400:glColor3f(0.0,0.75,0.25);134401..144000: glColor3f(0.0,0.5,0.5); 144001..153600:glColor3f(0.0,0.25,0.75);153601..163200: glColor3f(0.0,0.0,1.0); 163201..172800: glColor3f(0.25,0.0,1.0);172801..182400: glColor3f(0.5,0.0,1.0); 182401..192000: glColor3f(0.5,0.0,0.75);192001..201600: glColor3f(0.5,0.0,0.5); 201601..211200: glColor3f(0.75,0.0,0.75);211201..220800: glColor3f(1.0,0.0,1.0); 220801..239000: glColor3f(1.0,0.0,0.5); end;if j<>159 thenbeginGLbegin(GL_POLYGON);if hit=pnum thenbeginGLColor3f(1.0,1.0,1.0);MainForm.StatusBar1.Panels[5].Text:='光强='+InttoStr(hit);end;obj[pnum]:=Vert(pp[i,j,0],pp[i,j,1],pp[i,j,2]); obj[pnum+1]:=Vert(pp[i+1,j,0],pp[i+1,j,1],pp[i+1,j,2]);obj[pnum+2]:=Vert(pp[i+1,j+1,0],pp[i+1,j+1,1],pp[i+1,j+1,2] );obj[pnum+3]:=Vert(pp[i,j+1,0],pp[i,j+1,1],pp[i,j+1,2]);glVertex3fv(@obj[pnum]);glVertex3fv(@obj[pnum+1]);glVertex3fv(@obj[pnum+2]);glVertex3fv(@obj[pnum+3]);GLend();end;end;end;{pnum:=0;for i:=0 to 40 dobeginfor j:=1 to 360 dopnum:=pnum+1;if mode = GL_SELECT then glLoadName(pnum);k:=i-20;x1:=P*cos(k*pi/180)*cos(j*pi/180);z1:=p*sin(k*pi/180);y1:=p*cos(k*pi/180)*sin(j*pi/180);x2:=P*cos((k+1)*pi/180)*cos(j*pi/180);z2:=p*sin((k+1)*pi/180);y2:=p*cos((k+1)*pi/180)*sin(j*pi/180);x3:=P*cos(k*pi/180)*cos((j+1)*pi/180);z3:=p*sin(k*pi/180);y3:=p*cos(k*pi/180)*sin((j+1)*pi/180);x4:=P*cos((k+1)*pi/180)*cos((j+1)*pi/180);z4:=p*sin((k+1)*pi/180);y4:=p*cos((k+1)*pi/180)*sin((j+1)*pi/180);if (k<=5) and (k>=-5) thenbeginif (j>=1) and (j<=90) thenglColor3f(1.0,0.0,0.0)else if (j>90) and (j<=180) thenglColor3f(0.0,1.0,1.0)else if (j>180) and (j<=270) thenglColor3f(1.0,1.0,1.0)elseglColor3f(1.0,1.0,0.0);else if ((k<=10) and (K>=5)) or ((k>=-10) and (k<=-5)) thenglColor3f(0.0,1.0,0.0)else if ((k<=15) and (K>=10)) or ((k>=-15) and (k<=-10)) thenglColor3f(0.0,0.0,1.0)elseglColor3f(0.0,1.0,1.0);glbegin(GL_POLYGON );if hit=pnum thenbeginglColor3f(1, 0, 0);MainForm.Edit6.Text:=InttoStr(hit);end;obj[pnum]:=Vert(x1,y1,z1);obj[pnum+1]:=Vert(x2,y2,z2);obj[pnum+2]:=Vert(x3,y3,z3);obj[pnum+3]:=Vert(x4,y4,z4);glVertex3fv(@obj[pnum]);glVertex3fv(@obj[pnum+2]);glVertex3fv(@obj[pnum+3]);glVertex3fv(@obj[pnum+1]);glend();//end;end;end;}//显示0、x,y,zglDrawString(0,0,0,'0');glDrawString(2.5,0,0,'X');glDrawString(0,2.5,0,'Y');glDrawString(0,0,2.5,'Z');//颜色绘图// if MainForm.CheckBox3.Checked then// glDrawCamber; //画曲面{=================================}if mode = GL_SELECT then glLoadName(0);glCallList(1); //画坐标轴线{ if Ldown and(hit>0) then //画坐标指示线beginglColor3f( 0.5,0.5,0.5);glEnable( GL_LINE_STIPPLE);glLineStipple(1,$4444);if hit<3 then h:=hit else h:=1;glBegin( GL_LINE_STRIP);glVertex3fv( @obj[h]);glVertex3f( obj[h,0],obj[h,1],0);glVertex3f( obj[h,0],0,0); glVertex3f( 0,0,0);glVertex3f( 0,obj[h,1],0);glVertex3f( obj[h,0],obj[h,1],0);glEnd;glDisable( GL_LINE_STIPPLE);end;}SwapBuffers(wglGetCurrentDC);end;{------------------------------------------------------------------}{ Processes all mouseClicks }{------------------------------------------------------------------}{function doSelect(X, Y : Integer) :integer;var selectBuff : Array[1..8] of glUint;begin// result:= -1;glGetIntegerv(GL_VIEWPORT, @viewP); // Viewport = [0, 0, width, height]glSelectBuffer(8, @selectBuff);// glSelectBuffer(BUFSIZE, @selectBuff);glRenderMode(GL_SELECT);glInitNames;glPushName(0);glMatrixMode(GL_PROJECTION);glPushMatrix(); //--glLoadIdentity();gluPickMatrix(x, winH-y, 0.1, 0.1, viewP); // Set-up pick matrixgluPerspective(45, winW/winH, 1, 100); // Do the perspective calculations. Last value = max clipping depthglMatrixMode(GL_MODELVIEW);glDraw( GL_SELECT);glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);if (glRenderMode(GL_RENDER)>0 )then result:= selectBuff[4] else result :=-1;end; }function doSelect(X, Y : Integer) :integer;var selectBuff : Array[0..31] of glUint;beginglGetIntegerv(GL_VIEWPORT, @viewP); // Viewport = [0, 0, width, height]glSelectBuffer(BUFSIZE, @selectBuff);glRenderMode(GL_SELECT);glInitNames;glPushName(32);glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();gluPickMatrix(x, winH-y-20, 4, 4, viewP); // Set-up pick matrixgluPerspective(45, winW/winH, 1, 100); // Do the perspective calculations. Last value = max clipping depthglMatrixMode(GL_MODELVIEW);glDraw(GL_SELECT);glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);if glRenderMode(GL_RENDER)>0then result:= selectBuff[3]else result:= -1;end;//============ 按下鼠标 =============procedure MouseDw( hit, x,y : Integer);var i :integer;beginLdown:= TRUE;if hit<=0 then exit;zPos:= point(x,y); //很奇怪,这个变量无任何用处,但在这里却不能删除,否则就会黑屏!glGetDoublev( GL_Modelview_Matrix, @modeM);glGetDoublev(GL_Projection_Matrix, @projM);for i:= 1 to 236160 do Pos[i]:= GetWinPos(i);//构件的屏幕坐标glDraw(GL_RENDER);end;//============= 移动鼠标 ==============procedure MouseMv( hit, x,y, x0,y0 : Integer);var dx,dy, i :integer;begindx:= x-x0; dy:= y-y0;if hit<0 then begin rx:= rx+ (x-x0)/5; ry:= ry+ (y-y0)/5; end; //改变视角if hit<3 thenobj[hit]:= GetObjPos( x, winH-y, GetWinPos( hit)[2]); //直接将构件移动到新的位置if hit=3 thenfor i:= 1 to 2 dobeginobj[i]:=GetObjPos( Pos[i][0]+dx,(Pos[i][1]-dy),Pos[i][2]);//将构件移动到新的位置Pos[i]:= GetWinPos(i);//构件的屏幕坐标end;if hit>3 thenbeginobj[hit]:=GetObjPos( Pos[hit][0],(Pos[hit][1]),Pos[hit][2]);//将构件移动到新的位置Pos[hit]:= GetWinPos(hit);//构件的屏幕坐标end;glDraw(GL_RENDER);end;procedure TMainForm.FormCreate(Sender: TObject);var DC:HDC;RC:HGLRC;beginDC:=GetDC(Handle); //Actually, you can use any windowed control hereSetupPixelFormat(DC);RC:=wglCreateContext(DC); //makes OpenGL window out of DC wglMakeCurrent(DC, RC); //makes OpenGL window activeGLInit; //initialize OpenGLglResizeWnd(800,600);glDraw(GL_RENDER);p:=2.0;mat[0]:=0;mat[1]:=0;mat[2]:=1;mat[3]:=0;m_bHide:=False;m_bColor:=False;m_Pattern:=2;dnum:=24;pi:=3.1415926;setAxisList;rx:= -125; ry:= -65; //视角eye:= 10; //景深obj[1]:=Vert(-1, 2, 0.8);obj[2]:=Vert( 2,-1,-0.5);bs:=8*pi*p*p*sin(pi/9);bs:=bs/(1440*160);zl:=Sqrt(bs);end;procedure TMainForm.Panel1MouseDown(Sender: TObject; Button:TMouseButton;Shift: TShiftState; X, Y: Integer);beginif Button=mbLeft thenbeginx0:=x;y0:=y;MouseDw( hit, x0,y0 );MouseMv( hit, x,y, x0,y0);end;if Button=mbRight thenbeginx0:= x;y0:= y;Rdown:= true;end;// glDraw(GL_RENDER);glDraw(GL_RENDER); // Draw the sceneend;procedure TMainForm.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);beginif not(Ldown or Rdown) thenbeginhit:= doSelect(x, y); //自由移动// MouseMv( hit, x,y, x0,y0);end;if Ldown then MouseMv( hit, x,y, x0,y0); //拖动左键if Rdown then begin //拖动右键eye:= eye- (x-x0); //改变景深if eye>80 theneye:=80;if eye<5 theneye:=5;end;x0:= x; y0:= y;glDraw(GL_RENDER); // Draw the sceneend;procedure TMainForm.Panel1MouseUp(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);beginif Button=mbLeft thenbeginLdown :=FALSE;end;if Button=mbRight thenbeginRdown :=FALSE;end;// glDraw(GL_RENDER);glDraw(GL_RENDER); // Draw the sceneend;procedure TMainForm.Panel1Resize(Sender: TObject);beginglResizeWnd(MainForm.Width,MainForm.Height);end;procedure TMainForm.FormResize(Sender: TObject);beginglResizeWnd(MainForm.Width,MainForm.Height);end;procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);beginif Button=mbLeft thenbeginx0:=x;y0:=y;MouseDw( hit, x0,y0 );MouseMv( hit, x,y, x0,y0);end;if Button=mbRight thenbeginx0:= x;y0:= y;Rdown:= true;end;// glDraw(GL_RENDER);glDraw(GL_RENDER); // Draw the sceneend;procedure TMainForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);beginif not(Ldown or Rdown) thenbeginhit:= doSelect(x, y); //自由移动// MouseMv( hit, x,y, x0,y0);end;if Ldown then MouseMv( hit, x,y, x0,y0); //拖动左键if Rdown then begin //拖动右键eye:= eye- (x-x0); //改变景深if eye>80 theneye:=80;if eye<5 theneye:=5;end;x0:= x; y0:= y;glDraw(GL_RENDER); // Draw the sceneend;procedure TMainForm.FormMouseUp(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);beginif Button=mbLeft thenbeginLdown :=FALSE;end;if Button=mbRight thenbeginRdown :=FALSE;end;// glDraw(GL_RENDER);glDraw(GL_RENDER); // Draw the sceneend;end.。
基于 OpenGL 的三维模型点坐标拾取方法
基于 OpenGL 的三维模型点坐标拾取方法施珂奕;邓春健;邹昆【摘要】获得三维模型指定点的空间坐标是针对模型文件操作的基础。
针对传统算法的不足提出一种获取所选点原始坐标的方法。
在选点的过程中,该算法允许模型进行任意角度的旋转,以选取模型侧面及背面的点,并通过旋转矩阵对该点还原。
算法完成从屏幕的二维像素坐标到模型的三维坐标,再到模型文件原始坐标的转换。
实验表明,该方法可以准确获取模型的所有可视表面的点坐标,提高了拾取的灵活性。
%To pick-up a specified coordinate of a point is a basic operation for a 3D model file.Aimed at the shortages of traditional methods about 3D picking,a new method,which points at getting the o-riginal coordinate of specified point,is proposed.During picking,rotation at any angle is allowed.So points either on the side or on the back can be selected.And rotation matrix is used to restore points to their original state.The method transform the two-dimensional pixel coordinate to 3D model coordi-nate then to original model coordinate.All the points on visible surface can be picked precisely by this method and thus the flexibility is improved.【期刊名称】《液晶与显示》【年(卷),期】2016(031)007【总页数】6页(P708-713)【关键词】OpenGL;三维模型;拾取;坐标【作者】施珂奕;邓春健;邹昆【作者单位】电子科技大学计算机科学与工程学院,四川成都 611731; 电子科技大学中山学院,广东中山 528402;电子科技大学计算机科学与工程学院,四川成都 611731; 电子科技大学中山学院,广东中山 528402;电子科技大学计算机科学与工程学院,四川成都 611731; 电子科技大学中山学院,广东中山528402【正文语种】中文【中图分类】TP79三维模型的交互控制是人机交互的重要功能,为了操作三维模型,常常需要获取图形特定部位的空间坐标。
《2024年五自由度三维鼠标的设计与实现》范文
《五自由度三维鼠标的设计与实现》篇一一、引言随着科技的飞速发展,计算机与人类交互的方式也在不断更新。
其中,三维鼠标作为新一代的输入设备,以其独特的操作方式在虚拟现实、游戏娱乐、机械控制等多个领域发挥着越来越重要的作用。
本文将深入探讨五自由度三维鼠标的设计与实现,为读者提供详细的解读与解析。
二、五自由度三维鼠标设计原理五自由度三维鼠标,指的是具备在三维空间中五个方向的自由移动能力,包括X轴、Y轴的平面移动,Z轴的垂直移动以及旋转和倾斜的自由度。
这种设计使得用户可以更自然、更直观地与计算机进行交互。
设计上,五自由度三维鼠标主要依赖于精密的机械结构和先进的传感器技术。
其中,机械结构部分主要包括底座、滑轨、滚球等部分,传感器部分则负责捕捉鼠标的移动轨迹和方向。
三、五自由度三维鼠标的结构设计在结构设计上,五自由度三维鼠标通常采用模块化设计,包括底座模块、滑轨模块、滚球模块等。
底座模块负责支撑整个设备并连接电脑;滑轨模块负责实现X轴和Y轴的平面移动;滚球模块则通过滚球和传感器实现Z轴的垂直移动以及旋转和倾斜的自由度。
四、五自由度三维鼠标的实现技术在实现技术上,五自由度三维鼠标主要依赖于传感器技术。
其中,光学传感器负责捕捉滑轨模块的移动轨迹,并通过算法转化为电脑可以识别的信号;倾角传感器则负责捕捉滚球模块的倾斜角度和旋转方向。
此外,为了确保操作的准确性和稳定性,还需要对信号进行滤波、校正等处理。
五、五自由度三维鼠标的软件实现在软件实现方面,五自由度三维鼠标需要配合相应的驱动程序和应用程序。
驱动程序负责将传感器捕捉到的信号转化为电脑可以识别的数据格式;应用程序则负责将这些数据转化为具体的操作指令,如平移、旋转等。
此外,为了提供更好的用户体验,还需要对软件进行优化和调试,确保操作的流畅性和准确性。
六、五自由度三维鼠标的应用领域五自由度三维鼠标因其独特的操作方式和精确的控制能力,在多个领域都有广泛的应用。
在虚拟现实领域,它可以实现更自然、更直观的操作方式;在游戏娱乐领域,它可以为玩家带来更加刺激、更加真实的游戏体验;在机械控制领域,它可以实现精确的定位和操作,提高工作效率。
OpenGl读取导入3D模型并且添加鼠标移动旋转显示
OpenGl读取导⼊3D模型并且添加⿏标移动旋转显⽰原⽂作者:aircraft最近实习要⽤到opengl库就是跟opencv 有点像的那个,然后下了⼀个3D模型的读取显⽰来研究现在分享给⼤家吧注释基本我都打好了,所以也懒得再写很多解析了,⾃⼰看注释吧!本⽂⽤到obj ⽂件的百度云下载是:控制⼀个3d模型不难,那么控制多个呢?看下⾯这篇博客:下载之后,复制代码到⾃⼰的项⽬运⾏改⼀个那个我定义的路径,然后还要配置⼀下opengl和openmesh来读取obj⽂件解析,,配置过程肯定会出现很多的问题的没事习惯就好加油⾃⼰百度去不要来问我路径就是这个:改⼀下file变成你们⾃⼰的就⾏了,,最后说⼀下配置有问题⾃⼰百度OK 百度⽆敌⼀、操作⿏标控制物体旋转移动,滚轮缩放,上下左右键可以控制模型的移动F1,F2,F3,F4,F5,F6,F7,F8可以更换显⽰⽂件Insert键更换显⽰模式 (wire,flat,flatlines)⼆、实验演⽰按F1 读⼊⼀个 cow的 obj⽂件切换为只显⽰线的模式:切换为可以显⽰线和⾯⼀起的模式:可以实现旋转:旋转之后:可以实现缩放:缩⼩:可以实现平移:按完相应键盘的按键之后,就会进⾏⼊读obj⽂件,使⽤命令⾏显⽰当前状态#include <iostream>#include<stdlib.h>#include<OpenMesh/Core/IO/MeshIO.hh>#include<OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>#include<GL/glut.h>#include <math.h>#include <Windows.h>#include <string>#define GLUT_WHEEL_UP 3 //定义滚轮操作#define GLUT_WHEEL_DOWN 4using namespace std;typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh;//⿏标交互有关的int mousetate = 0; //⿏标当前的状态GLfloat Oldx = 0.0; // 点击之前的位置GLfloat Oldy = 0.0;GLuint texture;//与实现⾓度⼤⼩相关的参数,只需要两个就可以完成float xRotate = 0.0f; //旋转float yRotate = 0.0f;float ty = 0.0f;float tx = 0.0f;float scale = 0.004;//⽂件读取有关的MyMesh mesh; //mesh把⽂件读取了,封装在mesh对象中//"dinosaur.obj";const string file = "D:\\参考项⽬代码\\objdata\\objdata\\";const string file_1 = file + "cow.obj";const string file_2 = file + "bunny.obj";const string file_3 = file + "dinosaur.obj";const string file_4 = file + "mba1.obj";const string file_5 = file + "monkey.obj";const string file_6 = file + "porsche.obj";//const string file_7 = "teddy.obj";const string file_7 = file + "huangfeng.obj";const string file_8 = file + "file.obj";const string file_9 = file + "face.sur";int currentfile = 1;GLuint showFaceList, showWireList;int showstate = 1;bool showFace = true;bool showWire = false;bool showFlatlines = false;void setLightRes() {//GLfloat lightPosition[] = { 0.0f, 0.0f, 1.0f, 0.0f };GLfloat lightPosition[] = { 0.0f, 1.0f, 0.0f, 0.0f }; // 平⾏光源, GL_POSITION属性的最后⼀个参数为0glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);glEnable(GL_LIGHTING); //启⽤光源glEnable(GL_LIGHT0); //使⽤指定灯光}void SetupRC(){//当你想剔除背⾯的时候,你只需要调⽤glEnable(GL_CULL_FACE)就可以了,OPENGL状态机会⾃动按照默认值进⾏CULL_FACE,//默认是glFrontFace(GL_CCW) GL_CCW逆时针为正,GL_CW顺时针glEnable(GL_DEPTH_TEST);glFrontFace(GL_CCW);glEnable(GL_CULL_FACE);// 启⽤光照计算glEnable(GL_LIGHTING);// 指定环境光强度(RGBA)此时可以控制模型的显⽰颜⾊GLfloat ambientLight[] = { 1.0f, 0.0f, 0.0f, 0.0f };// 设置光照模型,将ambientLight所指定的RGBA强度值应⽤到环境光glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);// 启⽤颜⾊追踪//GL_COLOR_MATERIAL使我们可以⽤颜⾊来贴物体。
深度理解Opengl拾取机制
总的来说,OpenGL图形管线大体分为上面的五个阶段。
在编程的时候使用glMatrixMode(GL_MODELVIEW>,或者glMatrixMode(GL_PROJECTION>就是告诉OpenGL我们是要在那个阶段进行操作。
先来看看投影变换,因为理解投影变换是理解OpenGL 拾取的前提条件。
为了简单起见,这里以正交投影(Orthogonal Projection>为例。
在OpenGL中,使用正交投影可以调用glOrtho (left, right, bottom, top, zNear, zFar>,其中的六个参数分别对应正交投影视体的六个平面到观察坐标系原点的距离。
一旦在程序中调用了这个函数,OpenGL会马上创建根据给定的六个参数创建一个视体,并且把视体的大小归一化到-1到1之间,也就是说,OpenGL会自动把你给的参数所对应的x,y,z值转换为-1到1之间的值,并且这个视体的中心就是观察坐标系的原点。
要注意的是,当视体归一化后,z轴的方向要反向,也就是说,这里OpenGL的右手坐标系要换成左手坐标系。
原因很简单,z轴朝向显示器里的方向更符合我们的常识,越向里就离我们越远,z的值也就越大。
p1EanqFDPw上面的矩阵虽然看起来很复杂,其实很简单。
它就是进行移动,缩放,反号三个操作而已。
现在我们在OpenGL中检查一下是不是进行了这样的操作。
添加下面的代码。
RTCrpUDGiTglMatrixMode(GL_PROJECTION>。
glLoadIdentity(>。
glOrtho(-10, 10, -10, 10, -10, 10>。
GLfloat m[16]。
glGetFloatv(GL_PROJECTION_MATRIX, m>。
可以看到,得到的数据和我们计算的一样。
说明OpenGL的确是创建了这样的矩阵来进行计算。
弄清楚了OpenGL中的投影变换,现在就开看看大家关心的拾取操作。
基于OpenGL的网格数据三维地形鼠标取值方法
基于OpenGL的网格数据三维地形鼠标取值方法
范益进;龚声蓉
【期刊名称】《计算机工程与应用》
【年(卷),期】2005(041)032
【摘要】在OpenGL的三维场景模拟中,通过鼠标取点得到该点的三维坐标一直是一个难题,而在三维地形模拟中则进一步要求通过鼠标取点得到该点在地形数据中的实际值,即用鼠标取值,因此OpenGL三维地形的鼠标取值一直是这类应用程序中的难点.文章在解决实际问题的基础上实现了一种解决基于OpenGL的网格数据的三维地形的鼠标取值方法.
【总页数】3页(P100-102)
【作者】范益进;龚声蓉
【作者单位】苏州大学计算机科学与技术学院,苏州,215006;苏州大学计算机科学与技术学院,苏州,215006
【正文语种】中文
【中图分类】TP301
【相关文献】
1.基于OpenGL的三维地形图显示方法和应用 [J], 郭立波;赵海燕;王新海
2.基于三角网格的三维地形局部贴图方法的研究 [J], 黄有群;吴冰冰
3.基于OpenGL的三维地形可视化方法研究 [J], 韩样
4.基于OpenGL的三维地形场景绘制及并行方法探讨 [J], 杨菲;张小龙
5.基于图像纹理特征采用网格细分方法生成三维地形 [J], 朴雪;吴昌明
因版权原因,仅展示原文概要,查看原文内容请购买。
OpenGL轨迹球实现的鼠标旋转
OpenGL:轨迹球实现的鼠标旋转轨迹球控制By Terence J. Grant 如果只用鼠标来控制你的模型是不是很酷?轨迹球可以帮你做到这一点,我将告诉你我的实现,你可以把它应用在你的工程里。
我的实现是基于Bretton Wade’s,它是基于Ken Shoemake’s 实现的,最初的版本,你可以从游戏编程指南这本图上找到。
但我还是修正了一些错误,并优化了它。
轨迹球实现的内容就是把二维的鼠标点映射到三维的轨迹球,并基于它完成旋转变化。
为了完成这个设想,首先我们把鼠标坐标映射到[-1,1]之间,它很简单:MousePt.X = ((MousePt.X / ((Width -1) / 2)) -1);MousePt.Y = -((MousePt.Y / ((Height -1) / 2))-1);这只是为了数学上的简化,下面我们计算这个长度,如果它大于轨迹球的边界,我们将简单的把z轴设为0,否则我们把z轴设置为这个二维点映射到球面上对应的z值。
一旦我们有了两个点,就可以计算它的法向量了和旋转角了。
下面我们从构造函数开始,完整的讲解这个类:ArcBall_t::ArcBall_t(GLfloat NewWidth, GLfloat NewHeight)当点击鼠标时,记录点击的位置void ArcBall_t::click(const Point2fT* NewPt)当拖动鼠标时,记录当前鼠标的位置,并计算出旋转的量。
void ArcBall_t::drag(const Point2fT* NewPt, Quat4fT* NewRot)如果窗口大小改变,设置鼠标移动的范围void ArcBall_t::setBounds(GLfloat NewWidth, GLfloat NewHeight)下面是完成计算所要用到的数据结果,都是一些矩阵和向量Matrix4fT Transform ={ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };Matrix3fT LastRot = { 1.0f, 0.0f, 0.0f,0.0f, 1.0f, 0.0f,0.0f, 0.0f, 1.0f };Matrix3fT ThisRot = { 1.0f, 0.0f, 0.0f,0.0f, 1.0f, 0.0f,0.0f, 0.0f, 1.0f };ArcBallT ArcBall(640.0f, 480.0f);Point2fT MousePt;bool isClicked = false; // 是否点击鼠标bool isRClicked = false; // 是否右击鼠标bool isDragging = false; // 是否拖动在上面定义的变量中,transform是我们获得的最终的变换矩阵,lastRot是上一次鼠标拖动得到的旋转矩阵,thisRot为这次鼠标拖动得到的旋转矩阵。
C++_OpenGL选择模式理解
在用OpenGL进行图形编程的时候,通常要用鼠标进行交互操作,比如用鼠标点选择画面中的物体,我们称之为拾取(Picking),在网上看了很多OpenGL拾取的文,但大多是只是介绍在OpenGL中如何拾取,如何利用OpenGL提供的一系列函数来完成拾取,最多再简单介绍下OpenGL的*名字栈(Name stack),拾取矩阵(PickingMatrix)*等等,但是拾取真正的原理确没有提到。
所以,我在这里为大家详细介绍下OpenGL中拾取是怎样实现的,以及其背后的真正原理。
OpenGL中的拾取是对OpenGL图形管线的一个应用。
所以OpenGL中的拾取并不是像D3D一样采用射线交叉测试来判断是否选中一个目标,而是在*图形管线*的*投影变换(Projection Transformation)*阶段利用拾取矩阵来实现的。
为了理解这个过程,先来复习一下OpenGL的图形管线。
总的来说,OpenGL图形管线大体分为上面的五个阶段。
在编程的时候使用*glMatrixMode(GL_MODELVIEW),或者glMatrixMode(GL_PROJECTION)*就是告诉OpenGL我们是要在那个阶段进行操作。
先来看看投影变换,因为理解投影变换是理解OpenGL拾取的前提条件。
为了简单起见,这里以*正交投影(OrthogonalProjection)*为例。
在OpenGL中,使用正交投影可以调用*glOrtho (left, right,bottom, top, zNear, zFar),*其中的六个参数分别对应正交投影视体的六个平面到观察坐标系原点的距离。
一旦在程序中调用了这个函数,OpenGL会马上创建根据给定的六个参数创建一个视体,并且把视体的大小归一化到-1到1之间,也就是说,OpenGL会自动把你给的参数所对应的x,y,z值转换为-1到1之间的值,并且这个视体的中心就是观察坐标系的原点。
要注意的是,当视体归一化后,z轴的方向要反向,也就是说,这里OpenGL的右手坐标系要换成左手坐标系。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
OpenGL红宝书中提出了一种方法,使用后缓存来处理选择,个人觉得其完全可以替代标准选择机制。
使用方法与标准选择机制比较类似,不同的是其使用颜色来标记物体,每一组对象使用不同的颜色值,当发生选择时将场景以标记颜色在后缓存渲染一遍,然后读取帧缓存中所点选的点的颜色值(获取到颜色后不应交换前后缓存,而应直接清空后缓存以免该单色场景被显示出来),由该颜色值即可以得出被选中的是哪个对象。
和标准选择机制相比,这种方法同样需要重复渲染一次,但是由于其使用的是颜色标记,可以利用顶点缓存来显著地提高渲染速度,这是标准选择机制所无法做到的。
另外,因为读取的是像素数据而不是图元数据,拾取到的像素已经经过了Alpha测试和深度测试,所以也不会出现选取到没有光栅化的完全透明的物体或被遮挡物体的现象。
而在现在的标准24位颜色深度设备情况下,该方法能够标识256*256*256=16777216个不同的对象,这作为一般应用已经足够了。
使用OpenGL实现三维坐标的鼠标拣选Implementation of RIP(Ray-Intersection-Penetration)3D Coordinates Mouse Selection Using OpenGL顾露(武汉理工大学计算机系中科院智能设计与智能制造研究所湖北武汉 430070)摘要(Abstract):本文提出并实现一种用于三维坐标拣选的RIP(Ray-Intersection-Penetration)方法。
介绍了如何在已经渲染至窗口的三维场景中,使用鼠标或者相关设备拣选特定三维对象的方法。
此方法对于正交投影或透视投影均有效,相对于OpenGL自带的选择与反馈机制,本方法无论是拣选精度还是算法实现效率均高出许多,是一种比较通用的解决方案。
关键词(Keywords)正交投影(Ortho-Projection)、透视投影(Perspective-Projection)世界坐标系、屏幕坐标系、三维拣选、OpenGL一、简介(Introduction)OpenGL是一种比较“纯粹”的3D图形API,一般仅用于三维图形的渲染,对于特定领域的开发者(如游戏开发者)而言,如果选择使用OpenGL进行开发,类似碰撞检测的机制就都需要自行编写了。
但是由于鼠标在图形程序中的应用非常非常之广泛(例如现在已经很少有PC游戏能完全地脱离鼠标),OpenGL在图形库的基础上添加了选择与反馈机制(Select & Feedback)来满足用户使用鼠标实时操作三维图形的需要。
但由于种种原因,我们需要更为特殊的选择机制以满足特定需求,在这里我们提出了一种简单迅速的RIP (Ray-Intersection-Penetration)方法,可以满足绝大多数典型应用的需要。
二、相关研究(Related Work)用过OpenGL选择与反馈机制的开发者,或多或少可能都会觉得它难以令人满意。
大致表现在下面几个方面:一、编写程序比较繁琐。
想要使用选择反馈机制就需要切换渲染模式,操作命名堆栈,计算拣选矩阵,检查选中记录,这些繁琐的步骤很容易出错,而且非常不便于调试,只会降低工作效率和热情。
二、只能做基于图元的选定。
如下图(1 - a),使用GL_TRIANGLES绘制了一个三角形,三个顶点分别为P1、P2和P3。
若使用该机制,你将只能判断是否在三维场景中选中了这个三角形(用户点击处是否在P1、P2和P3的范围内),而无法判断用户是点击了这个三角形哪一部分(是左边的m区域内还是右边的n区域内),因为所绘制的P1、P2和P3本身构成的三角形就是一个基本图元,对于拣选机制而言是不可分的。
当然,把这个三角形拆成两个三角形再分别进行测试也是一个可行的方案,可是看看图(1 - b),这可怎么拆呢?还有图(1 – c)呢?另外,如果n和m两个平面不共面呢?对于使用者而言,OpenGL提供的拣选机制功能的确有限。
三、降低了渲染效率。
OpenGL中的选择和反馈是与普通渲染方式不同的一种特殊的渲染方式。
我们使用时一般是先在帧缓存中渲染普通场景,然后进入选择模式重绘场景,此时帧缓存的内容并无变化。
也就是说,为了选择某些物体,我们需要在一帧中使用不同的渲染方式将其渲染两遍。
我们知道对对象进行渲染是比较耗时的操作,当场景中需要选择的对象多而杂的时候,采用这个机制是非常影响速度的。
另外在OpenGL红宝书中介绍了一种简便易行的办法:在后缓冲中使用不同的颜色重绘所有对象,每个对象用一个单色来标示其颜色,这样画好之后我们读取鼠标所在点的颜色,就能够确定我们拣选了哪个物体。
这种方法有一个缺陷,当场景中需要选择的对象的数目超出一定限度时,可能会出现标识数的溢出。
对于这个问题,红宝书给出的解决办法就是多次扫描。
实践证明这种方法的确简便易行,但仍有不少局限性,而且做起来并不比第一种机制方便多少。
限于篇幅,不再赘述。
三、具体描述(Related Work)看过了上面两种方法,我们会发现这两种方法都不是十分的方便,而且使用者不能对其进行完全的控制,不能精确地判定鼠标定位与实际的世界空间中三维坐标的关系。
那么有什么更好的办法能够更简单更精确地对其加以控制呢?实际上此处给出的解决方案十分简单,就是一个很普通也很有用的GLU 函数gluUnProject()。
此函数的具体用途是将一个OpenGL视区内的二维点转换为与其对应的场景中的三维坐标。
转换过程如下图所示(由点P在窗口中的XY坐标得到其在三维空间中的世界坐标):这个函数在glu.h中的原型定义如下:int APIENTRY gluUnProject (GLdouble winx,GLdouble winy,GLdouble winz,const GLdouble modelMatrix[16],const GLdouble projMatrix[16],const GLint viewport[4],GLdouble *objx,GLdouble *objy,GLdouble *objz);其中前三个值表示窗口坐标,中间三个分别为模型视图矩阵(Model/View Matrix),投影矩阵(Projection Matrix)和视区(ViewPort),最后三个为输出的世界坐标值。
可能你会问:窗口坐标不是只有X轴和Y轴两个值么,怎么这里还有Z值?这就要从二维空间与三维空间的关系说起了。
众所周知,我们通过一个放置在三维世界中的摄像机,来观察当前场景中的对象。
通过使用诸如gluPerspective() 这样的OpenGL函数,我们可以设置这个摄像机所能看到的视野的大小范围。
这个视野的边界所围成的几何体是一个标准的平截头体(Frustum),可以看做是金字塔状的几何体削去金字塔的上半部分后形成的一个台状物,如果还原成金字塔状,就得到了通常我们所说的视锥(View Frustum)这个视锥的锥顶就是视点(V iew Point)也就是摄像机所在的位置。
平截头体,视锥以及视点之间的关系,如下图所示:在上面的图中,远裁剪面ABCD和近裁剪面A’B’C’D’构成了平截头体,加上虚线部分就是视锥,顶点O就是摄像机所在的视点。
我们在窗口中所能看到的东东,全部都在此平截头体内。
这跟前面的窗口坐标Z值有什么关系呢?看下图:如此图所示,点P和点P’分别在远裁剪面ABCD和近裁剪面A’B’C’D’上。
我们点击屏幕上的点P,反映到视锥中,就是选中了所有的从点P到点P’的点。
举个形象的例子,这就像是我们挽弓放箭,如果射出去的箭近乎笔直地飞出(假设力量非常之大近乎无穷),从挽弓的地点直至击中目标,在这条直线的轨迹上任何物体都将被一穿而过。
对应这里的情况,用户单击鼠标获得屏幕上的某一点,即是指定了从视点指向屏幕深处的某一方向,也就确定了屏幕上某条从O点出发的射线(在图中即为OP)。
在这里,我们称呼其为拣选射线。
因此,从窗口的XY坐标,我们仅仅只能获得一条出发自O点的拣选射线,并不能得到用户想要的点在这条射线上的确切位置。
这时候窗口坐标的Z值就能派上用场了。
我们通过Z值,来指定我们想要的点在射线上的位置。
假如用户点击了屏幕上的点(100,100)得到了这条射线OP,那么我们传入值1.0f 就表示近裁剪面上的P点,而值0.0f则对应远裁剪面上的P’点。
这样,我们通过引入一个窗口坐标的Z值,就能指定视锥内任意点的三维坐标。
与此同时,我们还解决了前面红宝书给出的方法中存在的缺陷——同一位置上重叠物体的选择问题。
解决办法是:从屏幕坐标得到射线之后,分别让重叠的物体与该射线求交,得到的交点,然后根据这些与视点的远近确定选择的对象。
如此我们就不必受“仅仅只能选取屏幕中离观察者最近的物体”的限制了。
这样一来,如果需要的话,我们甚至可以用代码来作一定的限定,通过判断交点与视点的距离,使得与该拣选射线相交的物体中,离视点远的对象才能被选取,这样就能够对那些暂时被其他对象遮住的物体进行选取。
至于如何求拣选射线与对象的交点,在各种图形学的书中的数学部分均有讲述,在此不再赘述。
四、例程(Sample Code Fragment)前面讲述了RIP方法,现在我们来看如何编写代码以实现之,以及一些需要注意的问题。
由于拣选射线以线段形式存储更加便于后面的计算,况且我们可以直接得到纵跨整个平截头体的线段(即前面图中的线段PP’),故我们直接计算出这条连接远近裁剪面的线段。
我们将拣选射线的线段形式称之为拣选线段。
在下面的代码前方声明有两个类Point3f和LineSegment这分别表示由三个浮点数构成的三维空间中的点,以及由两个点构成的空间中的一条线段。
应注意代码中用到了类Point3f的一个需要三个浮点参数的构造函数,以及类LineSegment的一个需要两个点参数的构造函数。
获取拣选射线的例程如下所示(使用C++语言编写):class Point3f;class LineSegment;LineSegment GetSelectionRay(int mouse_x,int mouse_y){// 获取 Model-View、Projection 矩阵 & 获取Viewport视区GLdouble modelview[16];GLdouble projection[16];GLint viewport[4];glGetDoublev (GL_MODELVIEW_MATRIX, modelview);glGetDoublev (GL_PROJECTION_MATRIX, projection);glGetIntegerv (GL_VIEWPORT, viewport);GLdouble world_x, world_y, world_z;// 获取近裁剪面上的交点gluUnProject((GLdouble) mouse_x,(GLdouble) mouse_y,0.0,modelview, projection, viewport,&world_x,&world_y,&world_z);Point3f near_point (world_x , world_y , world_z );// 获取远裁剪面上的交点gluUnProject ( (GLdouble ) mouse_x , (GLdouble ) mouse_y , 1.0,modelview , projection , viewport ,&world_x , &world_y , &world_z );Point3f far_point (world_x , world_y , world_z );return LineSegment (near_point , far_point );}如果你是使用Win32平台进行开发,那么应当注意传入正确的参数。