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 Camera
237     {
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             else
406             {
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 Vector3
467     {
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寫的實現同樣效果的程序要跑得快些哦!這又是昨回事? 這擺明讓人羡慕嫉妒恨嘛!

 

補充一點:這個程序請按Alt+F4退出.

 

本節源代碼下載

 

原創文章,出自"博客園, 豬悟能'S博客" : http://www.cnblogs.com/hackpig/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM