你的位置:首页 > ASP.net教程

[ASP.net教程]SharpGL学习笔记(十九) 摄像机漫游

 

所谓的摄像机漫游,就是可以在场景中来回走动。

现实中,我们通过眼睛观察东西,身体移动带动眼睛移动观察身边的事物,这也是在漫游。

在OpenGL中我们使用函数LookAt()来操作摄像机在三维场景中进行漫游。

 LookAt(double eyex, double eyey, double eyez, double centerx, double centery, double centerz, double upx, double upy, double upz);

 

我们通过改变LookAt的参数实现漫游效果,说明如下:

  • 改变视点eyeX,可以实现在场景中横向移动
  • 改变视点eyeY,可以实现在场景中蹲下,跳起这样的动作
  • 改变视点Z分量eyeZ,能实现在场景中前后的动作
  • 对于摄像机目标点centerx,y,z 的变化,相当于观察者站着不动,但其观察方向在上下左右方向进行变化。

 

 源代码:

 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using SharpGL; 10 using System.Runtime.InteropServices; 11  12 namespace cameraRove 13 { 14   //原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/ 15   public partial class SharpGLForm : Form 16   { 17     SharpGL.SceneGraph.Assets.Texture texture = new SharpGL.SceneGraph.Assets.Texture(); 18     Camera m_Camera = new Camera(); 19  20     //光源位置  21     float lx = 2f; 22     float ly = 1f; 23     float lz = 9f; 24  25     float[] fLightPosition = new float[4];// 光源位置  26     float[] fLightAmbient = new float[4] { 1f, 1f, 1f, 1.0f };// 环境光参数  27     float[] fLightDiffuse = new float[4] { 1f, 1f, 1f, 1f };// 漫射光参数 28  29  30     public SharpGLForm() 31     { 32       InitializeComponent(); 33     } 34  35     private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e) 36     { 37       OpenGL gl = openGLControl.OpenGL; 38       gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 39       gl.LoadIdentity(); 40  41       gl.DrawText(20,this.Height-60, 1f, 1f, 1f, "黑体", 12f, string.Format( 42        "当前位置:X={0} Y={1} Speed ={2} ", m_Camera.getView().x.ToString("0.0"), 43        (-m_Camera.getView().z).ToString("0.0"), m_Camera.getSpeed())); 44  45        46       m_Camera.setLook(gl); 47  48       drawBox(gl, 1f, 1f, 0f); 49  50       m_Camera.setViewByMouse(); 51     } 52  53  54     void drawGrid(OpenGL gl) 55     { 56       //获得场景中一些状态 57       byte[] lp = new byte[] { 0, 0 }; 58       byte[] tp = new byte[] { 0, 0 }; 59       gl.GetBooleanv(OpenGL.GL_LIGHTING, lp); 60       gl.GetBooleanv(OpenGL.GL_TEXTURE_2D, tp); 61  62       //关闭纹理和光照 63       gl.Disable(OpenGL.GL_TEXTURE_2D); 64       gl.Disable(OpenGL.GL_LIGHTING); 65  66       //绘制过程 67       gl.PushAttrib(OpenGL.GL_CURRENT_BIT); //保存当前属性 68       gl.PushMatrix();            //压入堆栈 69       gl.Translate(0f, 0f, 0f); 70       gl.Color(0f, 0f, 1f); 71  72       //在X,Z平面上绘制网格 73       for (float i = -50; i <= 50; i += 1) 74       { 75         //绘制线 76         gl.Begin(OpenGL.GL_LINES); 77         //X轴方向 78         gl.Vertex(-50f, 0f, i); 79         gl.Vertex(50f, 0f, i); 80         //Z轴方向  81         gl.Vertex(i, 0f, -50f); 82         gl.Vertex(i, 0f, 50f); 83         gl.End(); 84       } 85       gl.PopMatrix(); 86       gl.PopAttrib(); 87  88       //恢复场景状态 89       if (tp[0] != 0) 90         gl.Enable(OpenGL.GL_TEXTURE_2D); 91       if (lp[0] != 0) 92         gl.Enable(OpenGL.GL_LIGHTING); 93     } 94  95     void drawSphere(OpenGL gl) 96     { 97       //设置材质属性 98       float[] mat_ambient = { 0.9f, 0.5f, 0.8f, 1.0f }; 99       float[] mat_diffuse = { 0.9f, 0.5f, 0.8f, 1.0f };100       float[] mat_shininess = { 100.0f };101       gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, mat_ambient);102       gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, mat_diffuse);103       gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, mat_shininess);104 105       //获得纹理启用状态106       byte[] tp = { 0, 0 };107       gl.GetBooleanv(OpenGL.GL_TEXTURE_2D, tp);108       gl.Disable(OpenGL.GL_TEXTURE_2D);      //关闭纹理109 110       //绘制过程111       gl.PushMatrix();112       gl.Translate(2f, 1f, 5f);113       var sphere= gl.NewQuadric();114       gl.QuadricNormals(sphere, OpenGL.GLU_OUTSIDE);115       gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH);116       gl.Sphere(sphere, 1f, 50, 50);117       gl.DeleteQuadric(sphere);118       gl.PopMatrix();119 120       if (tp[0] != 0)121         gl.Enable(OpenGL.GL_TEXTURE_2D);122     }123 124     private void drawBox(OpenGL gl, float xPos, float yPos, float zPos)125     {126       gl.PushMatrix();127       texture.Bind(gl);128       gl.Scale(2, 3, 2);129       gl.Translate(xPos, yPos, zPos);130       gl.Begin(OpenGL.GL_QUADS);131       {132         //前133         gl.TexCoord(0, 0); gl.Vertex(0, 0, 0);134         gl.TexCoord(1, 0); gl.Vertex(-1, 0, 0);135         gl.TexCoord(1, 1); gl.Vertex(-1, -1, 0);136         gl.TexCoord(0, 1); gl.Vertex(0, -1, 0);137 138         //底139         gl.TexCoord(0, 0); gl.Vertex(0, 0, 0);140         gl.TexCoord(1, 0); gl.Vertex(0, 0, -1);141         gl.TexCoord(1, 1); gl.Vertex(-1, 0, -1);142         gl.TexCoord(0, 1); gl.Vertex(-1, 0, 0);143 144         //左145         gl.TexCoord(0, 0); gl.Vertex(-1, 0, 0);146         gl.TexCoord(1, 0); gl.Vertex(-1, 0, -1);147         gl.TexCoord(1, 1); gl.Vertex(-1, -1, -1);148         gl.TexCoord(0, 1); gl.Vertex(-1, -1, 0);149 150         //右151         gl.TexCoord(0, 0); gl.Vertex(0, 0, 0);152         gl.TexCoord(1, 0); gl.Vertex(0, 0, -1);153         gl.TexCoord(1, 1); gl.Vertex(0, -1, -1);154         gl.TexCoord(0, 1); gl.Vertex(0, -1, 0);155 156         //后157         gl.TexCoord(0, 0); gl.Vertex(0, 0, -1);158         gl.TexCoord(1, 0); gl.Vertex(-1, 0, -1);159         gl.TexCoord(1, 1); gl.Vertex(-1, -1, -1);160         gl.TexCoord(0, 1); gl.Vertex(0, -1, -1);161 162         //顶163         gl.TexCoord(0, 0); gl.Vertex(0, -1, 0);164         gl.TexCoord(1, 0); gl.Vertex(0, -1, -1);165         gl.TexCoord(1, 1); gl.Vertex(-1, -1, -1);166         gl.TexCoord(0, 1); gl.Vertex(-1, -1, 0);167       }168       gl.End();169 170       gl.PopMatrix();171       drawGrid(gl);172       drawSphere(gl);173     }174 175     private void openGLControl_OpenGLInitialized(object sender, EventArgs e)176     {177       OpenGL gl = openGLControl.OpenGL;178       texture.Create(gl, "image.bmp");179       gl.Enable(OpenGL.GL_TEXTURE_2D);180 181       fLightPosition = new float[4] { lx, ly, lz, 1f };182       gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, fLightAmbient);//环境光源 183       gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, fLightDiffuse);//漫射光源 184       gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, fLightPosition);//光源位置 185       gl.Enable(OpenGL.GL_LIGHTING);//开启光照 186       gl.Enable(OpenGL.GL_LIGHT0);187 188       gl.Enable(OpenGL.GL_NORMALIZE);189 190       gl.ClearColor(0, 0, 0, 0);191 192       m_Camera.setCamera(0.0f, 1.5f, 6.0f, 0.0f, 1.5f, 0.0f, 0.0f, 1.0f, 0.0f);193       m_Camera.setSpeed(1f);194 195     }196 197     private void openGLControl_Resized(object sender, EventArgs e)198     {199       OpenGL gl = openGLControl.OpenGL;200       gl.MatrixMode(OpenGL.GL_PROJECTION);201       gl.LoadIdentity();202       gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);203       gl.LookAt(-6, 1, 0, 0, 0, 0, 0, 1, 0);204       gl.MatrixMode(OpenGL.GL_MODELVIEW);205     }206 207     private void openGLControl_KeyDown(object sender, KeyEventArgs e)208     {209      210       switch (e.KeyCode)211       {212         case Keys.W:  //上213           m_Camera.yawCamera(m_Camera.getSpeed());214           215           break;216         case Keys.S:  //下217           m_Camera.yawCamera(-m_Camera.getSpeed());218          219           break;220         case Keys.A:  //左221           m_Camera.moveCamera(m_Camera.getSpeed());222           break;223         case Keys.D:  //右224           m_Camera.moveCamera(-m_Camera.getSpeed());225          226           break;227         default:228           break;229       }230     }231 232 233   }234 235   236   class Camera237   {238     private Vector3 m_Position;   //位置239     private Vector3 m_View;     //朝向240     private Vector3 m_UpVector;   //向上向量241     private float m_Speed;     //速度242 243     public Camera()244     {245       Vector3 zero =new Vector3(0f, 0f, 0f);246       Vector3 view =new Vector3(0f, 1f, 0.5f);247       Vector3 up =new Vector3(0f, 0f, 1f);248       m_Position = zero;249       m_View = view;250       m_UpVector = up;251       m_Speed = 0.1f;252     }253 254     public void setSpeed(float speed)255     {256       m_Speed = speed;257     }258 259     public float getSpeed()260     {261       return m_Speed;262     }263 264     public Vector3 getPosition()265     {266       return m_Position;267     }268 269     public Vector3 getView()270     {271       return m_View;272     }273 274     public Vector3 getUpVector()275     {276       return m_UpVector;277     }278 279 280     //设置摄像机的位置,朝向和向上向量281     public void setCamera(float positionX,float positionY,282       float positionZ, float viewX,float viewY,283       float viewZ,float upVectorX,float upVectorY,284       float upVectorZ)285     {286       //构造向量287       Vector3 Position =new Vector3(positionX, positionY, positionZ);288       Vector3 View = new Vector3(viewX, viewY, viewZ);289       Vector3 UpVector = new Vector3(upVectorX, upVectorY, upVectorZ);290 291       //设置摄像机292       m_Position = Position;293       m_View = View;294       m_UpVector = UpVector;295     }296 297     //旋转摄像机方向298     void rotateView(float angle, float x, float y, float z)299     {300       Vector3 newView=new Vector3();301       //计算方向向量302       Vector3 view = m_View - m_Position;303       //计算 sin 和cos值 304       float cosTheta =(float) Math.Cos(angle);305       float sinTheta = (float)Math.Sin(angle);306 307       //计算旋转向量的x值308       newView.x = (cosTheta + (1 - cosTheta) * x * x) * view.x;309       newView.x += ((1 - cosTheta) * x * y - z * sinTheta) * view.y;310       newView.x += ((1 - cosTheta) * x * z + y * sinTheta) * view.z;311 312       //计算旋转向量的y值313       newView.y = ((1 - cosTheta) * x * y + z * sinTheta) * view.x;314       newView.y += (cosTheta + (1 - cosTheta) * y * y) * view.y;315       newView.y += ((1 - cosTheta) * y * z - x * sinTheta) * view.z;316 317       //计算旋转向量的y值318       newView.z = ((1 - cosTheta) * x * z - y * sinTheta) * view.x;319       newView.z += ((1 - cosTheta) * y * z + x * sinTheta) * view.y;320       newView.z += (cosTheta + (1 - cosTheta) * z * z) * view.z;321 322       //更新摄像机的方向323       m_View = m_Position + newView;324     }325 326 327     [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]328     public static extern int GetSystemMetrics(int which);329 330     [DllImport("user32.dll", CharSet = CharSet.Auto)]331     public static extern bool GetCursorPos(out Point pt);332 333     [DllImport("user32.dll", EntryPoint = "ShowCursor", CharSet = CharSet.Auto)]334     public extern static void ShowCursor(int status);335 336     [DllImport("user32.dll", EntryPoint = "SetCursorPos")]337     private static extern int SetCursorPos(int x, int y);338 339     float lastRotX = 0.0f;   // 用于保存旋转角度340     float currentRotX = 0.0f;341     public void setViewByMouse()342     {343       const int SM_CXSCREEN = 0;344       const int SM_CYSCREEN = 1;345       Point mousePos;                   /**< 保存当前鼠标位置 */346       int middleX = GetSystemMetrics(SM_CXSCREEN) >> 1; /**< 得到屏幕宽度的一半 */347       int middleY = GetSystemMetrics(SM_CYSCREEN) >> 1; /**< 得到屏幕高度的一半 */348       float angleY = 0.0f;               /**< 摄像机左右旋转角度 */349       float angleZ = 0.0f;               /**< 摄像机上下旋转角度 */350 351 352       // 得到当前鼠标位置353       GetCursorPos(out mousePos);354       ShowCursor(0);355 356       // 如果鼠标没有移动,则不用更新357       if ((mousePos.X == middleX) && (mousePos.Y == middleY))358         return;359 360       // 设置鼠标位置在屏幕中心361       SetCursorPos(middleX, middleY);362 363       // 得到鼠标移动方向364       angleY = (float)((middleX - mousePos.X)) / 1000.0f;365       angleZ = (float)((middleY - mousePos.Y)) / 1000.0f;366 367       lastRotX = currentRotX;368 369       // 跟踪摄像机上下旋转角度 370       currentRotX += angleZ;371 372       // 如果上下旋转弧度大于1.0,我们截取到1.0并旋转373       if (currentRotX > 1.0f)374       {375         currentRotX = 1.0f;376 377         // 根据保存的角度旋转方向378         if (lastRotX != 1.0f)379         {380           // 通过叉积找到与旋转方向垂直的向量381           Vector3 vAxis = m_View - m_Position;382           vAxis = vAxis.crossProduct(m_UpVector);383           vAxis = vAxis.normalize();384 385           ///旋转386           rotateView(1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);387         }388       }389       // 如果旋转弧度小于-1.0,则也截取到-1.0并旋转390       else if (currentRotX < -1.0f)391       {392         currentRotX = -1.0f;393         if (lastRotX != -1.0f)394         {395 396           // 通过叉积找到与旋转方向垂直的向量397           Vector3 vAxis = m_View - m_Position;398           vAxis = vAxis.crossProduct(m_UpVector);399           vAxis = vAxis.normalize();400 401           rotateView(-1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);402         }403       }404       // 否则就旋转angleZ度405       else406       {407         // 找到与旋转方向垂直向量408         Vector3 vAxis = m_View - m_Position;409         vAxis = vAxis.crossProduct(m_UpVector);410         vAxis = vAxis.normalize();411 412         rotateView(angleZ, vAxis.x, vAxis.y, vAxis.z);413       }414 415       // 总是左右旋转摄像机416       rotateView(angleY, 0, 1, 0);417     }418 419 420 421 422 423     public void yawCamera(float speed)424     {425       Vector3 yaw = new Vector3();426       Vector3 cross = m_View - m_Position;427       cross = cross.crossProduct(m_UpVector);428 429       //归一化向量430       yaw = cross.normalize();431 432       m_Position.x += yaw.x * speed;433       m_Position.z += yaw.z * speed;434 435       m_View.x += yaw.x * speed;436       m_View.z += yaw.z * speed;437     }438 439     public void moveCamera(float speed)440     {441       //计算方向向量442       Vector3 vector = m_View - m_Position;443       vector = vector.normalize();  //单位化444 445       //更新摄像机446       m_Position.x += vector.x * speed;  //根据速度更新位置447       m_Position.z += vector.z * speed;448       m_Position.y += vector.y * speed;449 450       m_View.x += vector.x * speed;    //根据速度更新方向451       m_View.y += vector.y * speed;452       m_View.z += vector.z * speed;453     }454 455     //设置视点456     public void setLook(OpenGL gl)457     {458       gl.LookAt(m_Position.x, m_Position.y, m_Position.z,459         m_View.x, m_View.y, m_View.z,460         m_UpVector.x, m_UpVector.y, m_UpVector.z);461     }462   }463 464 465   //向量运算类466   class Vector3467   {468     public float x, y, z;469     public Vector3()470     {471       x = 0; y = 0; z = 0;472     }473 474     public Vector3(float x, float y, float z)475     {476       this.x = x;477       this.y = y;478       this.z = z;479     }480 481     public Vector3(Vector3 vec)482     {483       this.x = vec.x;484       this.y = vec.y;485       this.z = vec.z;486     }487 488     public float length()489     {490       return (float)(x * x + y * y + z * z);491     }492 493     public Vector3 normalize()494     {495       float len = length();496       if (len == 0) len = 1;497       x = x / len;498       y = y / len;499       z = z / len;500       return this;501     }502 503     //点积504     public float dotProduct(Vector3 vec)505     {506       return 0f;507     }508 509     public Vector3 crossProduct(Vector3 vec)510     {511       Vector3 v = new Vector3();512       v.x = y * vec.z - z * vec.y;513       v.y = z * vec.x - x * vec.z;514       v.z = x * vec.y - y * vec.x;515       return v;516     }517 518     public static Vector3 operator +(Vector3 v1,Vector3 v2)519     {520       var res = new Vector3();521       res.x=v1.x+v2.x;522       res.y=v1.y+v2.y;523       res.z=v1.z+v2.z;524       return res;525     }526 527     public static Vector3 operator -(Vector3 v1,Vector3 v2)528     {529       var res = new Vector3();530       res.x=v1.x-v2.x;531       res.y=v1.y-v2.y;532       res.z=v1.z-v2.z;533       return res;534     }535 536     public static Vector3 operator *(Vector3 v1, Vector3 v2)537     {538       var res = new Vector3();539       res.x = v1.x * v2.x;540       res.y = v1.y * v2.y;541       res.z = v1.z * v2.z;542       return res;543     }544 545     public static Vector3 operator /(Vector3 v1, Vector3 v2)546     {547       var res = new Vector3();548       res.x = v1.x / v2.x;549       res.y = v1.y / v2.y;550       res.z = v1.z / v2.z;551       return res;552     }553 554     public static Vector3 operator -(Vector3 vec)555     {556       vec.x=-1*vec.x;557       vec.y=-1*vec.y;558       vec.z=-1*vec.z;559       return vec;560     }561 562   }563 564 }

 

 

 

效果如下图:

移动鼠标可以旋转摄像机,按键盘的WASD四个键可以XY方向移动摄像机。

 

 

本例子改编自徐明亮《OpenGL游戏编程》一书中“摄像机漫游” 一章节。

通过这个例子,发现了一个让笔者不解的问题。为什么我办公电脑那种垃圾配置(双核2G,集成显卡)跑这个例子比较快,但我家里的电脑(四核,显卡是geForce GTX 750Ti) 运行起来却蛮慢?

貌似根本就没有发挥强大显卡的性能嘛!

还有,VC6写的实现同样效果的程序要跑得快些哦!这又是昨回事? 这摆明让人羡慕嫉妒恨嘛!

 

 

本节源代码下载

 

原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/