• 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/

  • 相关阅读:
    Maven pom.xml中添加指定的中央仓库
    命令行远程链接MySQL
    A required class was missing while executing org.apache.maven.plugins:maven-war-plugin:2.1.1:war
    mvn deploy命令上传包
    保存好你的密码
    PuTTY免输密码自动登录Linux
    ActiveMQ无法启动
    linux控制台批量杀进程
    dubbo入门之微服务客户端服务端配置
    dubbo入门之helloWorld
  • 原文地址:https://www.cnblogs.com/hackpig/p/5847307.html
Copyright © 2020-2023  润新知