﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-xiaoguozi's Blog-随笔分类-游戏开发</title><link>http://www.cppblog.com/guojingjia2006/category/8725.html</link><description>Pay it forword - 我并不觉的自豪，我所尝试的事情都失败了······习惯原本生活的人不容易改变，就算现状很糟，他们也很难改变，在过程中，他们还是放弃了······他们一放弃，大家就都是输家······让爱传出去，很困难，也无法预料，人们需要更细心的观察别人，要随时注意才能保护别人，因为他们未必知道自己要什么·····</description><language>zh-cn</language><lastBuildDate>Fri, 07 Dec 2012 13:06:42 GMT</lastBuildDate><pubDate>Fri, 07 Dec 2012 13:06:42 GMT</pubDate><ttl>60</ttl><item><title>OpenGL图形管线和坐标变换  (转)</title><link>http://www.cppblog.com/guojingjia2006/archive/2012/12/07/196089.html</link><dc:creator>小果子</dc:creator><author>小果子</author><pubDate>Fri, 07 Dec 2012 12:40:00 GMT</pubDate><guid>http://www.cppblog.com/guojingjia2006/archive/2012/12/07/196089.html</guid><wfw:comment>http://www.cppblog.com/guojingjia2006/comments/196089.html</wfw:comment><comments>http://www.cppblog.com/guojingjia2006/archive/2012/12/07/196089.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guojingjia2006/comments/commentRss/196089.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guojingjia2006/services/trackbacks/196089.html</trackback:ping><description><![CDATA[<div><div id="article_content"> <p><strong>1. </strong><strong>OpenGL </strong><strong>渲染管线</strong></p> <p>OpenGL渲染管线分为两大部分，<strong>模型观测变换(ModelView Transformation)</strong>和<strong>投影变换(Projection Transformation)</strong>。做个比喻，计算机图形开发就像我们照相一样，目的就是把真实的场景在一张照相纸上表现出来。那么<strong>观测变换的过程就像是我们摆设相机的位置，选择好要照的物体，摆好物体的造型</strong>。而<strong>投影变换就像相机把真实的三维场景显示在相纸</strong>上一样。下面就分别详细的讲一下这两个过程。</p> <p>1.1<strong>模型观测变换</strong></p> <p>让我们先来弄清楚OpenGL中的<strong>渲染管线</strong>。管线是一个抽象的概念，之所以称之为<strong>管线是因为显卡在处理数据的时候是按照一个固定的顺序来的，而且严格按照这个顺序</strong>。就像水从一根管子的一端流到另一端，这个顺序是不能打破的。先来看看下面的图1：</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966861v2w3.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="1" src="http://hi.csdn.net/attachment/201009/20/0_1284966862Hv0f.gif" alt="1" border="0" height="323" width="665" /></a><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图1 OPENGL渲染管线</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p>图中显示了OpenGL图形管线的主要部分，也是我们在进行图形编程的时候常常要用到的部分。一个顶点数据从图的<strong>左上角(MC)进入管线</strong>，最后从图的<strong>右下角(DC)输出</strong>。<strong>MC是Model Coordinate的简写，表示模型坐标</strong>。<strong>DC</strong>是<strong>Device Coordinate</strong>的简写，表示<strong>设备坐标</strong>。 当然DC有很多了，什么显示器，打印机等等。这里DC我们就理解成常说的屏幕坐标好了。MC当然就是3D坐标了(注意：我说的3D坐标，而不是世界坐 标)，这个3D坐标就是模型坐标，也说成本地坐标(相对于世界坐标)。MC要经过模型变换(Modeling  Transformation)才变换到世界坐标，图2：</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966862f4ca.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="2" src="http://hi.csdn.net/attachment/201009/20/0_1284966863yh36.gif" alt="2" border="0" height="314" width="346" /></a><strong>图2 世界坐标系和模型坐标系</strong> </p> <p>变换到世界坐标WC(World Coordinate)说简单点就是如何用世界坐标系来表示本地坐标系中的坐标。为了讲得更清楚一些，这里举个2D的例子。如图3：</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966863xbI1.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="3" src="http://hi.csdn.net/attachment/201009/20/0_1284966864ufuJ.gif" alt="3" border="0" height="338" width="367" /></a> <strong>图3 世界坐标系和模型坐标系的计算</strong> </p> <p>图中红色坐标系是世界<strong>坐标系WC</strong>，绿色的是<strong>模型坐标系MC</strong>。现在有一个顶点，在模型坐标系中的坐标为(1,1)，现在要把这个模型坐标转换到世界坐标中来表示。从图中可以看出，点(1,1)在世界坐标系中的坐标为(3,4)，现在我们来通过计算得到我们希望的结果。首先我们要把模型坐标系MC在世界坐标系中表示出来，使用<strong>齐次坐标(Homogeneous Coordinate )</strong>可以表示为矩阵(注意，本教程中使用的矩阵都是以列向量组成)：<a href="http://hi.csdn.net/attachment/201009/20/0_1284966864h4bB.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex " src="http://hi.csdn.net/attachment/201009/20/0_128496686527RR.gif" alt="gif.latex " border="0" height="85" width="184" /></a> 其中，矩阵的<strong>第一列为MC中x轴在WC中的向量表示</strong>，<strong>第二列为MC中y轴WC中的向量表示</strong>，<strong>第三列为MC中的原点在WC中的坐标</strong>。对齐次坐标系不了解的同学，请先学习游戏数学方面的知识。有了这个模型变换矩阵后，用这个矩阵乘以在MC中表示的坐标就可以得到该坐标在世界坐标系中的坐标。所以该矩阵和MC中的坐标(1,1)相乘有:</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966865Y3Ci.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex2" src="http://hi.csdn.net/attachment/201009/20/0_1284966865ELLU.gif" alt="gif.latex2" border="0" height="74" width="195" /></a> 这也正是我们需要的结果。现在让我们把相机坐标也加进去，相机坐标也称为观测坐标(View Coordinate)，如图4和图5。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966866KK6f.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="4" src="http://hi.csdn.net/attachment/201009/20/0_1284966866Rr3b.gif" alt="4" border="0" height="257" width="277" /></a> <strong>图4 ModelView变换的三个坐标系</strong></p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_12849668678s9z.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="5" src="http://hi.csdn.net/attachment/201009/20/0_12849668676nS5.gif" alt="5" border="0" height="255" width="273" /></a> <strong>图5 ModelView变换计算</strong></p> <p>来看看MC坐标中的点(1,1)如何在相机坐标中表示。从图5中可以直接看出MC中的点(1,1)在相机坐标系VC中为(-2,-2)。和上面同样的道理，我们可以写出相机坐标系VC在世界标系WC中可以表示为：</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966867ncr7.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex3" src="http://hi.csdn.net/attachment/201009/20/0_12849668684R16.gif" alt="gif.latex3" border="0" height="90" width="188" /></a> 那么世界坐标系中的点转换为相机坐标系中的点我们就需求VC的逆矩阵：</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_12849668685gu3.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex4" src="http://hi.csdn.net/attachment/201009/20/0_1284966871cd22.gif" alt="gif.latex4" border="0" height="79" width="204" /></a>那么世界坐标系WC中的点(3,4)在相机坐标系VC中坐标为： </p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966872nSmh.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex5" src="http://hi.csdn.net/attachment/201009/20/0_1284966872WBhv.gif" alt="gif.latex5" border="0" height="73" width="221" /></a> 上面的变换过程，就是可以把<strong>模型坐标变换为相机坐标</strong>。在OpenGL中，当我们申明顶点的时候，有时候说的是世界坐标，这是因为初始化的时候世界坐标系、模型坐标系和相机坐标系是一样的，重合在一起的。所以OpenGL中提供了模型观测变换，它是把模型坐标系直接转换为相机坐标系，如图4。现在我们已经计算得到了VC<sup>-1</sup>和MC，如果把VC<sup>-1</sup>和 MC相乘，就可以得到模型坐标在相机坐标中的表示。为了得到模型坐标系中的坐标在相机坐标系中的表示，这就是OpenGL中的ModelView变换矩 阵。这也是ModelView变换的名字的由来，它是通过了上面两个步骤得到的。那么这里，ModelView变换矩阵M为：</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966872Z9cZ.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex6" src="http://hi.csdn.net/attachment/201009/20/0_1284966873Lic4.gif" alt="gif.latex6" border="0" height="63" width="472" /></a> 现在只要用上面的<strong>模型观测矩阵M乘以模型坐标系MC中的坐标就可以得到相机坐标系中的坐标</strong>了。模型观测变换的关键就是要得到相机坐标系中的坐标，因为光照等计算都是在这个这个坐标系中完成的。下面我们实际OpenGL程序中检查一下。在程序中，为了计算方便，我们使用图6中的模型。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966873cecL.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="6" src="http://hi.csdn.net/attachment/201009/20/0_12849668744eV2.gif" alt="6" border="0" height="333" width="494" /></a> <strong>图6 ModelView变换计算模型</strong></p> <p>根据图中的数据，我们分别可以写出对应<strong>MC和VC<sup>-1</sup></strong>，从而求得<strong>观测变换矩阵M</strong>。</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966874lNIl.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex7" src="http://hi.csdn.net/attachment/201009/20/0_12849668743Lhl.gif" alt="gif.latex7" border="0" height="160" width="334" /></a> 现在程序中用<strong>glGetFloatv()</strong>这个函数来获得当前矩阵数据来检查一下。</p> <pre><span style="color: #0000ff;"><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>float&nbsp;m[16]&nbsp;=&nbsp;{0};&nbsp;//用来保存当前矩阵数据&nbsp;&nbsp;</span></li><li>glMatrixMode(GL_MODELVIEW);&nbsp;&nbsp;</li><li>glLoadIdentity();&nbsp;&nbsp;</li><li>glGetFloatv(GL_MODELVIEW_MATRIX,&nbsp;m);&nbsp;&nbsp;&nbsp;</li><li><span>//相机设置，View&nbsp;变换&nbsp;&nbsp;</span></li><li>gluLookAt(0.0,&nbsp;0.0,&nbsp;5.0,&nbsp;&nbsp;</li><li>0.0,&nbsp;0.0,&nbsp;0.0,&nbsp;&nbsp;</li><li>0.0,&nbsp;1.0,&nbsp;0.0);&nbsp;&nbsp;</li><li>glGetFloatv(GL_MODELVIEW_MATRIX,&nbsp;m);&nbsp;&nbsp;&nbsp;</li><li><span>//投影设置&nbsp;&nbsp;</span></li><li>glMatrixMode(GL_PROJECTION);&nbsp;&nbsp;</li><li>glLoadIdentity();&nbsp;&nbsp;</li><li>glOrtho(-10,10,-10,10,-10,10);&nbsp;&nbsp;</li><li>glMatrixMode(GL_MODELVIEW);&nbsp;&nbsp;&nbsp;</li><li><span>//Modeling变换&nbsp;&nbsp;</span></li><li>glTranslatef(0,&nbsp;0,&nbsp;-3);&nbsp;&nbsp;</li><li>glGetFloatv(GL_MODELVIEW_MATRIX,&nbsp;m);&nbsp;&nbsp;</li><li>glBegin(GL_POINTS);&nbsp;&nbsp;</li><li>glVertex3f(1,1,0);&nbsp;&nbsp;</li><li>glEnd();&nbsp;&nbsp;</li></ol></div> </span></pre> <p>如果在上面程序段中最后一个<strong>glGetFloatv(GL_MODELVIEW_MATRIX, m)</strong>处设定断点的话，就可以看到图7所显示的数据。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966875bbzW.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="7" src="http://hi.csdn.net/attachment/201009/20/0_1284966876K0pM.gif" alt="7" border="0" height="286" width="484" /></a> <strong>图7 ModelView变换矩阵数据</strong></p> <p>到这里，整个<strong>ModelView变换</strong>就完成了。通过ModelView变换后得到是相机坐标系内的坐标。在这个坐标系内典型的计算就是法线了。现在再来看看后面一个阶段。</p> <p>1.2<strong>投影变换</strong></p> <p>先还是复习一下OpenGL的渲染管线。图1中可以看到，在<strong>投影变换(Projection Transformation)</strong>中也分为两个部分，第一个部分是将上个阶段得到的<strong>坐标转换为平面坐标</strong>，第二个部分是将转换后的<strong>平面坐标在进行归一化并进行剪裁</strong>。一般地，将三维坐标转换为平面坐标有两种投影方式：<strong>正交投影(Orthogonal Projection)和透视投影(Perspective Projection)</strong>。</p> <p><strong>1.2.1 </strong><strong>正交投影</strong></p> <p>正交投影很简单，如图8，对于三维空间中的坐标点和一个二维平面，要在对应的平面上投影，只需将非该平面上的点的坐标分量改为该平面上的坐标值，其余坐标不变。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966877SokW.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="8" src="http://hi.csdn.net/attachment/201009/20/0_128496687755NK.gif" alt="8" border="0" height="396" width="542" /></a> <strong>图8 正交投影</strong></p> <p>比如将点(1,1,5)正交投影到z=0的平面上，那么投影后的坐标为(1,1,0)。在openGL中，设置正交投影可以使用函数:</p> <div> <pre><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>glOrtho&nbsp;(GLdouble&nbsp;left,&nbsp;GLdouble&nbsp;right,&nbsp;GLdouble&nbsp;bottom,&nbsp;GLdouble&nbsp;top,&nbsp;GLdouble&nbsp;zNear,&nbsp;GLdouble&nbsp;zFar)&nbsp;&nbsp;</span></li></ol></div> </pre> </div> <p> 该函数可以设置正交投影的投影空间，在该空间以外的坐标点就不会被投影到投影平面上。函数中的六个参数分是投影空间六个平面，如图9: </p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_128496687868hM.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="9" src="http://hi.csdn.net/attachment/201009/20/0_128496687885HO.gif" alt="9" border="0" height="314" width="543" /></a> </p> <p align="center"><strong>图9 OpenGL正交投影空间和投影变换</strong></p> <p>在图9中，大的投影空间是根据这六个参数设置的投影空间，OpenGL会自动将该空间归一化，也就是将该空间或立方体转化为变长为1的正六面体投影 空间，并且该证六面体的中心在相机坐标系的原点。一旦设置使用glortho函数设置投影空间，OpenGL会生成投影矩阵。这个矩阵的作用就是将坐标进 行正交投影并且将投影后的坐标正规化(转换到-1到1之间)。要注意的是，生成该矩阵的时候，OpenGL会把右手坐标系转换为左手坐标系。原因很简单， 右手坐标系的Z轴向平面外的，这样不符合我们的习惯。该矩阵的矩阵推导这里就不详细说明了，不了解的同学可以参考游戏数学方面资料，这里只给出正交投影矩 阵。</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_128496687983b3.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="15" src="http://hi.csdn.net/attachment/201009/20/0_1284966879K1oU.gif" alt="15" border="0" height="110" width="428" /></a> 这个矩阵看来很复杂，其实计算很简单。举个例子，现在设置了这样的正交投影空间glOrtho(-10,10,-10,10,-10,10)，这是个正六面体空间，变长为10。把这些参数带入上面的矩阵可以得到</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966879vOHo.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="gif.latex8" src="http://hi.csdn.net/attachment/201009/20/0_1284966880i7bb.gif" alt="gif.latex8" border="0" height="99" width="261" /></a> 现在还是在OpenGL程序中来检查一下。在OpenGL程序中添加下面代码段:</p> <div> <pre><span style="color: #008000;"><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>//投影设置&nbsp;&nbsp;</span></li><li>glMatrixMode(GL_PROJECTION);&nbsp;&nbsp;</li><li>glLoadIdentity();&nbsp;&nbsp;</li><li>glOrtho(-10,10,-10,10,-10,10);&nbsp;&nbsp;</li><li>glMatrixMode(GL_MODELVIEW);&nbsp;&nbsp;</li><li>glGetFloatv(GL_PROJECTION_MATRIX,m)&nbsp;&nbsp;</li></ol></div> </span></pre> <pre>在<strong>glGetFloatv(GL_PROJECTION_MATRIX,m)</strong>处设定断点就可以看到图10中所显示的信息。</pre> </div> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966881OcqQ.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="10" src="http://hi.csdn.net/attachment/201009/20/0_1284966881H1ch.gif" alt="10" border="0" height="313" width="438" /></a> <strong>图10 正交变换矩阵数据     <br /></strong></p> <p><strong>1.2.2</strong><strong>透视投影</strong><strong></strong></p> <p>透视投影和正交投影最大的区别就是透视投影具有远近感。</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966882yD8p.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="11" src="http://hi.csdn.net/attachment/201009/20/0_1284966883ptta.gif" alt="11" border="0" height="256" width="441" /></a> </p> <p align="center"><strong>图11 透视投影</strong></p> <p>透视投影采用了图11中的模型，这样的模型就是保证远的物体看起来小，近的物体看起来大。 在OpenGL中设置透视投影可以使用函数:</p> <div> <pre><span style="color: #0000ff;"><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>void&nbsp;APIENTRY&nbsp;gluPerspective&nbsp;(GLdouble&nbsp;fovy,&nbsp;GLdouble&nbsp;aspect,&nbsp;GLdouble&nbsp;zNear,&nbsp;GLdouble&nbsp;zFar);&nbsp;&nbsp;</span></li></ol></div> </span></pre> </div> <p>该函数也会根据给定的参数生成一个投影空间。如图11中，该投影空间是一个截头体。同样地，OpenGL会自动生成透视投影矩阵，该矩阵也会让3D坐标投影在投影平面上，并且将投影后的坐标也进行正规化。下面也直接给出OpenGL中使用的透视投影矩阵。</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_12849668832ayn.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="16" src="http://hi.csdn.net/attachment/201009/20/0_1284966884q0qS.gif" alt="16" border="0" height="113" width="405" /></a> 下面在OpenGL中添加下面代码段:</p> <div> <pre><span style="color: #008000;"><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>//投影设置&nbsp;&nbsp;</span></li><li>glMatrixMode(GL_PROJECTION);&nbsp;&nbsp;</li><li>glLoadIdentity();&nbsp;&nbsp;</li><li>gluPerspective(45,&nbsp;1.0,&nbsp;1.0,&nbsp;100);&nbsp;&nbsp;</li><li>glMatrixMode(GL_MODELVIEW);&nbsp;&nbsp;</li><li>glGetFloatv(GL_PROJECTION_MATRIX,m)&nbsp;&nbsp;</li></ol></div> </span></pre> </div> <p>设置断点后，我们可以看到图12中显示的数据。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966885ItQD.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="12" src="http://hi.csdn.net/attachment/201009/20/0_1284966885ll4m.gif" alt="12" border="0" height="338" width="472" /></a><strong>图12 透视变换矩阵数据</strong></p> <p>到此为止，整个投影变换就完成了。透过投影变换后得到的是正规化的投影平面坐标。这为下一个阶段的<strong>视口变换(View port Transformation)</strong>做好了准备。   </p> <p>1.3<strong>视口变换</strong></p> <p>现在到了最后一个阶段了。这个阶段叫做视口变换，它把上个阶段得到的正规化的投影坐标转化为windows 窗口坐标。视口变换会将投影平面上的画面映射到窗口上。在OpenGL中可以使用函数</p> <div> <pre><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>GLAPI&nbsp;void&nbsp;GLAPIENTRY&nbsp;glViewport&nbsp;(GLint&nbsp;x,&nbsp;GLint&nbsp;y,&nbsp;GLsizei&nbsp;width,&nbsp;GLsizei&nbsp;height);&nbsp;&nbsp;</span></li></ol></div> </pre> </div> <p>来进行对窗口的映射，如图13。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966886lVI5.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="13" src="http://hi.csdn.net/attachment/201009/20/0_12849668864nu3.gif" alt="13" border="0" height="394" width="429" /></a>    <br /><strong>图13 视口变换glViewport(width/2, 0, width/2, height/2)</strong></p> <p>举个例子说明，比如上个阶段中得到了一个顶点的坐标为(0,0,0.5,1)，根据这个坐标，该顶点位于投影平面的正中间。如果将该点映射到大小为 50*50的窗口上时，那么它应该位于屏幕的中间，坐标为(25,25,  0.5,1)。当然这里深度值0.5是不会改变的。有的同学肯定有疑问了，既然投影到了窗口上，那么还要深度值0.5干什么？这里要注意的是，虽然在窗口 上显示时只需要x，y坐标就够了，但是要在2D窗口上显示3D图形时深度值是不可少的。这里的深度值不是用于显示，而是用于在光栅化的时候进行深度测试。</p> <p>OpenGL也会根据glViewport函数提供的参数值生成一个视口变换矩阵</p> <p><a href="http://hi.csdn.net/attachment/201009/20/0_1284966887GuwW.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="17" src="http://hi.csdn.net/attachment/201009/20/0_12849668871BLa.gif" alt="17" border="0" height="108" width="401" /></a>   该矩阵把上个阶段得到的正规化坐标映射到窗口上，并且将正规化坐标中的深度值在转换到0到1之间。所以在深度缓冲中最大值为1，最小值为0。视口变换结束 后，OpenGL中主要的图形管线阶段就算完成了，后面就是光栅化等等操作。再来回顾一下图1，现在相信大家对这个渲染管线有了一定的认识了，也明白了每 一个阶段对应的变换矩阵以及如何进行坐标之间的转换的。</p> <p><strong>2. </strong><strong>屏幕坐标转换为世界坐标</strong></p> <p>通过前面的教程，以及现在大家对OpenGL整个渲染管线理解后，现在要将屏幕上一点坐标转换为世界坐标就比较容易了。从图形管线的开始到结束，一 个模型坐标系中的坐标被转化为了屏幕坐标，那么现在把整个过程倒过来的话，屏幕上一点坐标也可以转为为世界坐标。只要在对应的阶段求得对应变换矩阵的逆矩 阵，就可以得到前一个阶段的坐标。这整个过程可以用图14表示。</p> <p align="center"><a href="http://hi.csdn.net/attachment/201009/20/0_1284966888gzv4.gif"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="14" src="http://hi.csdn.net/attachment/201009/20/0_1284966888Gujq.gif" alt="14" border="0" height="232" width="467" /></a> <strong>图14屏幕坐标转换为世界坐标</strong></p> <p>图中显示的过程完全就是OpenGL渲染管线的逆过程，通过这个过程，屏幕上的点就可以转化为世界坐标系中的点了。可能又有的同学要问，当鼠标点击屏幕上一点的时候并没有深度信息，转换的时候要怎么办呢？这个时候可以使用OpenGL函数</p> <div> <pre><span style="color: #0000ff;"><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>void&nbsp;glReadPixels&nbsp;(GLint&nbsp;x,&nbsp;GLint&nbsp;y,&nbsp;GLsizei&nbsp;width,&nbsp;GLsizei&nbsp;height,&nbsp;GLenum&nbsp;format,&nbsp;GLenum&nbsp;type,&nbsp;GLvoid&nbsp;*pixels);&nbsp;&nbsp;</span></li></ol></div> </span></pre> </div> <p>该函数能够获得屏幕上一点对应像素的深度信息。有了这个深度信息，就可以利用上面过程把屏幕上一点转换为世界坐标了。在OpenGL中，上面的过程其实已经有现成的函数可以使用，那就是</p> <div> <pre><span style="color: #0000ff;"><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>int&nbsp;APIENTRY&nbsp;gluUnProject&nbsp;(&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;&nbsp;winx,&nbsp;GLdouble&nbsp;&nbsp;winy,&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;&nbsp;winz,&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;<span>const&nbsp;GLdouble&nbsp;modelMatrix[16],&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;<span>const&nbsp;GLdouble&nbsp;projMatrix[16],&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;<span>const&nbsp;GLint&nbsp;&nbsp;&nbsp;&nbsp;viewport[4],&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;&nbsp;*objx,&nbsp;&nbsp;GLdouble&nbsp;&nbsp;*objy,&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*objz);&nbsp;&nbsp;</li></ol></div> </span></pre> </div> <p>该函数直接将屏幕上一点转换对应的世界坐标，该函数的内部实现其实还是上面的那么逆过程。下面给出利用该函数获取世界坐标的代码段。</p> <div> <pre><div bg_cpp"=""><div><div><strong>[cpp]</strong> <a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="view plain">view plain</a><a href="http://blog.csdn.net/zhulinpptor/article/details/5897102#" title="copy">copy</a></div></div><ol start="1"><li><span>GVector&nbsp;screen2world(int&nbsp;x,&nbsp;int&nbsp;y)&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLint&nbsp;viewport[4];&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;modelview[16];&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;projection[16];&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLfloat&nbsp;winX,&nbsp;winY,&nbsp;winZ;&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLdouble&nbsp;posX,&nbsp;posY,&nbsp;posZ;&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glGetDoublev(GL_MODELVIEW_MATRIX,&nbsp;modelview);&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glGetDoublev(GL_PROJECTION_MATRIX,&nbsp;projection);&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glGetIntegerv(GL_VIEWPORT,&nbsp;viewport);&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winX&nbsp;=&nbsp;(<span>float)x;&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;winY&nbsp;=&nbsp;(<span>float)viewport[3]&nbsp;-&nbsp;(float)y;&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glReadPixels(x,&nbsp;<span>int(winY),&nbsp;1,&nbsp;1,&nbsp;GL_DEPTH_COMPONENT,&nbsp;GL_FLOAT,&nbsp;&amp;winZ);&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gluUnProject(winX,&nbsp;winY,&nbsp;winZ,&nbsp;modelview,&nbsp;projection,&nbsp;viewport,&nbsp;&amp;posX,&nbsp;&amp;posY,&nbsp;&amp;posZ);&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GVector&nbsp;v(4,&nbsp;posX,&nbsp;posY,&nbsp;posZ,&nbsp;1.0);&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span>return&nbsp;v;&nbsp;&nbsp;</span></li><li>}&nbsp;&nbsp;</li></ol></div> </pre> <pre>代码中函数返回类型GVector是用户定义的向量类，返回的是齐次坐标。</pre> </div> </div></div><img src ="http://www.cppblog.com/guojingjia2006/aggbug/196089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guojingjia2006/" target="_blank">小果子</a> 2012-12-07 20:40 <a href="http://www.cppblog.com/guojingjia2006/archive/2012/12/07/196089.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>武林英雄网页版辅助小程序</title><link>http://www.cppblog.com/guojingjia2006/archive/2009/09/02/95091.html</link><dc:creator>小果子</dc:creator><author>小果子</author><pubDate>Wed, 02 Sep 2009 05:40:00 GMT</pubDate><guid>http://www.cppblog.com/guojingjia2006/archive/2009/09/02/95091.html</guid><wfw:comment>http://www.cppblog.com/guojingjia2006/comments/95091.html</wfw:comment><comments>http://www.cppblog.com/guojingjia2006/archive/2009/09/02/95091.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/guojingjia2006/comments/commentRss/95091.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guojingjia2006/services/trackbacks/95091.html</trackback:ping><description><![CDATA[<p>以前自己玩游戏时写的一个网页游戏小辅助，有兴趣的email<br><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/guojingjia2006/b_5348k017015.png" width=324 height=441><img border=0 alt="" src="http://www.cppblog.com/images/cppblog_com/guojingjia2006/b_5294i017016.png" width=347 height=447></p>
<img src ="http://www.cppblog.com/guojingjia2006/aggbug/95091.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guojingjia2006/" target="_blank">小果子</a> 2009-09-02 13:40 <a href="http://www.cppblog.com/guojingjia2006/archive/2009/09/02/95091.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>win32双扣游戏</title><link>http://www.cppblog.com/guojingjia2006/archive/2008/11/12/66750.html</link><dc:creator>小果子</dc:creator><author>小果子</author><pubDate>Wed, 12 Nov 2008 12:33:00 GMT</pubDate><guid>http://www.cppblog.com/guojingjia2006/archive/2008/11/12/66750.html</guid><wfw:comment>http://www.cppblog.com/guojingjia2006/comments/66750.html</wfw:comment><comments>http://www.cppblog.com/guojingjia2006/archive/2008/11/12/66750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/guojingjia2006/comments/commentRss/66750.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/guojingjia2006/services/trackbacks/66750.html</trackback:ping><description><![CDATA[<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">测试版学校内网双扣，只是完成最基本的一些东西，不过可以玩的起来了，不过客户端代码写的比较垃圾，违反了许多c</span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">不该犯的东西，不过没办法，我起先写的时候无法估计写这的复杂度，随着时间的推移，才慢慢觉得越来难以管理和扩展。不过还是应着头皮还是写完了整个模块.不过还有很多东西待改正，如时间的显示，一大堆，还有很多功能模块没扩展，如聊天的功能和记分牌，还有安全问题一个没写。。就是服务器这端验证没写，估计肯定会补上，不然不安全的，不过现在的首要的是将我自己写的重新整理下，不然真的难以维护，呵呵，现在体会到了软件工程老师说的一句了</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">写程序如果是堆积起来的，就像积木一样，不稳，越往上，坍塌的几率也越高，不可能在往上了.</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,现在状况就这样，要好好整理下代码，这样往后写才容易维护和扩展，主要开始写的时候没估计到写这的复杂性。。以后是个警戒了..<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>如果有时间的话：重新组织代码，扩展聊天功能，安全性加强，界面写的漂亮点。目标就是希望比qq那双扣写的更好.<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #000000">^</span><span style="COLOR: #000000">_</span><span style="COLOR: #000000">^</span><span style="COLOR: #000000">..<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
已完成重构...扩展也比以前强...已经经过测试，出牌逻辑咱未发现bug.<br>有兴趣学win32游戏编程，因为整个项目压缩上传量超限制了，所以不能上传，对代码如果感兴趣的话，发邮件<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#103;&#117;&#111;&#106;&#105;&#110;&#103;&#106;&#105;&#97;&#64;&#115;&#116;&#117;&#46;&#104;&#100;&#117;&#46;&#101;&#100;&#117;&#46;&#99;&#110;">guojingjia@stu.hdu.edu.cn</a>给我，这也是我刚学win32的时候编的一个游戏，也是代码量最多的了...2500-3000..我也是菜鸟...大家一起学习...<br>
<img src ="http://www.cppblog.com/guojingjia2006/aggbug/66750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/guojingjia2006/" target="_blank">小果子</a> 2008-11-12 20:33 <a href="http://www.cppblog.com/guojingjia2006/archive/2008/11/12/66750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>