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

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

在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 的变化,相当于观察者站着不动,但其观察方向在上下左右方向进行变化。

效果缩略图:

源代码:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpGL;
using System.Runtime.InteropServices; namespace cameraRove
{
//原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/
public partial class SharpGLForm : Form
{
SharpGL.SceneGraph.Assets.Texture texture = new SharpGL.SceneGraph.Assets.Texture();
Camera m_Camera = new Camera(); //光源位置
float lx = 2f;
float ly = 1f;
float lz = 9f; float[] fLightPosition = new float[];// 光源位置
float[] fLightAmbient = new float[] { 1f, 1f, 1f, 1.0f };// 环境光参数
float[] fLightDiffuse = new float[] { 1f, 1f, 1f, 1f };// 漫射光参数 public SharpGLForm()
{
InitializeComponent();
} private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{
OpenGL gl = openGLControl.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.LoadIdentity(); gl.DrawText(,this.Height-, 1f, 1f, 1f, "黑体", 12f, string.Format(
"当前位置:X={0} Y={1} Speed ={2} ", m_Camera.getView().x.ToString("0.0"),
(-m_Camera.getView().z).ToString("0.0"), m_Camera.getSpeed())); m_Camera.setLook(gl); drawBox(gl, 1f, 1f, 0f); m_Camera.setViewByMouse();
} void drawGrid(OpenGL gl)
{
//获得场景中一些状态
byte[] lp = new byte[] { , };
byte[] tp = new byte[] { , };
gl.GetBooleanv(OpenGL.GL_LIGHTING, lp);
gl.GetBooleanv(OpenGL.GL_TEXTURE_2D, tp); //关闭纹理和光照
gl.Disable(OpenGL.GL_TEXTURE_2D);
gl.Disable(OpenGL.GL_LIGHTING); //绘制过程
gl.PushAttrib(OpenGL.GL_CURRENT_BIT); //保存当前属性
gl.PushMatrix(); //压入堆栈
gl.Translate(0f, 0f, 0f);
gl.Color(0f, 0f, 1f); //在X,Z平面上绘制网格
for (float i = -; i <= ; i += )
{
//绘制线
gl.Begin(OpenGL.GL_LINES);
//X轴方向
gl.Vertex(-50f, 0f, i);
gl.Vertex(50f, 0f, i);
//Z轴方向
gl.Vertex(i, 0f, -50f);
gl.Vertex(i, 0f, 50f);
gl.End();
}
gl.PopMatrix();
gl.PopAttrib(); //恢复场景状态
if (tp[] != )
gl.Enable(OpenGL.GL_TEXTURE_2D);
if (lp[] != )
gl.Enable(OpenGL.GL_LIGHTING);
} void drawSphere(OpenGL gl)
{
//设置材质属性
float[] mat_ambient = { 0.9f, 0.5f, 0.8f, 1.0f };
float[] mat_diffuse = { 0.9f, 0.5f, 0.8f, 1.0f };
float[] mat_shininess = { 100.0f };
gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, mat_ambient);
gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, mat_diffuse);
gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, mat_shininess); //获得纹理启用状态
byte[] tp = { , };
gl.GetBooleanv(OpenGL.GL_TEXTURE_2D, tp);
gl.Disable(OpenGL.GL_TEXTURE_2D); //关闭纹理 //绘制过程
gl.PushMatrix();
gl.Translate(2f, 1f, 5f);
var sphere= gl.NewQuadric();
gl.QuadricNormals(sphere, OpenGL.GLU_OUTSIDE);
gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH);
gl.Sphere(sphere, 1f, , );
gl.DeleteQuadric(sphere);
gl.PopMatrix(); if (tp[] != )
gl.Enable(OpenGL.GL_TEXTURE_2D);
} private void drawBox(OpenGL gl, float xPos, float yPos, float zPos)
{
gl.PushMatrix();
texture.Bind(gl);
gl.Scale(, , );
gl.Translate(xPos, yPos, zPos);
gl.Begin(OpenGL.GL_QUADS);
{
//前
gl.TexCoord(, ); gl.Vertex(, , );
gl.TexCoord(, ); gl.Vertex(-, , );
gl.TexCoord(, ); gl.Vertex(-, -, );
gl.TexCoord(, ); gl.Vertex(, -, ); //底
gl.TexCoord(, ); gl.Vertex(, , );
gl.TexCoord(, ); gl.Vertex(, , -);
gl.TexCoord(, ); gl.Vertex(-, , -);
gl.TexCoord(, ); gl.Vertex(-, , ); //左
gl.TexCoord(, ); gl.Vertex(-, , );
gl.TexCoord(, ); gl.Vertex(-, , -);
gl.TexCoord(, ); gl.Vertex(-, -, -);
gl.TexCoord(, ); gl.Vertex(-, -, ); //右
gl.TexCoord(, ); gl.Vertex(, , );
gl.TexCoord(, ); gl.Vertex(, , -);
gl.TexCoord(, ); gl.Vertex(, -, -);
gl.TexCoord(, ); gl.Vertex(, -, ); //后
gl.TexCoord(, ); gl.Vertex(, , -);
gl.TexCoord(, ); gl.Vertex(-, , -);
gl.TexCoord(, ); gl.Vertex(-, -, -);
gl.TexCoord(, ); gl.Vertex(, -, -); //顶
gl.TexCoord(, ); gl.Vertex(, -, );
gl.TexCoord(, ); gl.Vertex(, -, -);
gl.TexCoord(, ); gl.Vertex(-, -, -);
gl.TexCoord(, ); gl.Vertex(-, -, );
}
gl.End(); gl.PopMatrix();
drawGrid(gl);
drawSphere(gl);
} private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
{
OpenGL gl = openGLControl.OpenGL;
texture.Create(gl, "image.bmp");
gl.Enable(OpenGL.GL_TEXTURE_2D); fLightPosition = new float[] { lx, ly, lz, 1f };
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, fLightAmbient);//环境光源
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, fLightDiffuse);//漫射光源
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, fLightPosition);//光源位置
gl.Enable(OpenGL.GL_LIGHTING);//开启光照
gl.Enable(OpenGL.GL_LIGHT0); gl.Enable(OpenGL.GL_NORMALIZE); gl.ClearColor(, , , ); m_Camera.setCamera(0.0f, 1.5f, 6.0f, 0.0f, 1.5f, 0.0f, 0.0f, 1.0f, 0.0f);
m_Camera.setSpeed(1f); } private void openGLControl_Resized(object sender, EventArgs e)
{
OpenGL gl = openGLControl.OpenGL;
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0);
gl.LookAt(-, , , , , , , , );
gl.MatrixMode(OpenGL.GL_MODELVIEW);
} private void openGLControl_KeyDown(object sender, KeyEventArgs e)
{ switch (e.KeyCode)
{
case Keys.W: //上
m_Camera.yawCamera(m_Camera.getSpeed()); break;
case Keys.S: //下
m_Camera.yawCamera(-m_Camera.getSpeed()); break;
case Keys.A: //左
m_Camera.moveCamera(m_Camera.getSpeed());
break;
case Keys.D: //右
m_Camera.moveCamera(-m_Camera.getSpeed()); break;
default:
break;
}
} } class Camera
{
private Vector3 m_Position; //位置
private Vector3 m_View; //朝向
private Vector3 m_UpVector; //向上向量
private float m_Speed; //速度 public Camera()
{
Vector3 zero =new Vector3(0f, 0f, 0f);
Vector3 view =new Vector3(0f, 1f, 0.5f);
Vector3 up =new Vector3(0f, 0f, 1f);
m_Position = zero;
m_View = view;
m_UpVector = up;
m_Speed = 0.1f;
} public void setSpeed(float speed)
{
m_Speed = speed;
} public float getSpeed()
{
return m_Speed;
} public Vector3 getPosition()
{
return m_Position;
} public Vector3 getView()
{
return m_View;
} public Vector3 getUpVector()
{
return m_UpVector;
} //设置摄像机的位置,朝向和向上向量
public void setCamera(float positionX,float positionY,
float positionZ, float viewX,float viewY,
float viewZ,float upVectorX,float upVectorY,
float upVectorZ)
{
//构造向量
Vector3 Position =new Vector3(positionX, positionY, positionZ);
Vector3 View = new Vector3(viewX, viewY, viewZ);
Vector3 UpVector = new Vector3(upVectorX, upVectorY, upVectorZ); //设置摄像机
m_Position = Position;
m_View = View;
m_UpVector = UpVector;
} //旋转摄像机方向
void rotateView(float angle, float x, float y, float z)
{
Vector3 newView=new Vector3();
//计算方向向量
Vector3 view = m_View - m_Position;
//计算 sin 和cos值
float cosTheta =(float) Math.Cos(angle);
float sinTheta = (float)Math.Sin(angle); //计算旋转向量的x值
newView.x = (cosTheta + ( - cosTheta) * x * x) * view.x;
newView.x += (( - cosTheta) * x * y - z * sinTheta) * view.y;
newView.x += (( - cosTheta) * x * z + y * sinTheta) * view.z; //计算旋转向量的y值
newView.y = (( - cosTheta) * x * y + z * sinTheta) * view.x;
newView.y += (cosTheta + ( - cosTheta) * y * y) * view.y;
newView.y += (( - cosTheta) * y * z - x * sinTheta) * view.z; //计算旋转向量的y值
newView.z = (( - cosTheta) * x * z - y * sinTheta) * view.x;
newView.z += (( - cosTheta) * y * z + x * sinTheta) * view.y;
newView.z += (cosTheta + ( - cosTheta) * z * z) * view.z; //更新摄像机的方向
m_View = m_Position + newView;
} [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which); [DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetCursorPos(out Point pt); [DllImport("user32.dll", EntryPoint = "ShowCursor", CharSet = CharSet.Auto)]
public extern static void ShowCursor(int status); [DllImport("user32.dll", EntryPoint = "SetCursorPos")]
private static extern int SetCursorPos(int x, int y); float lastRotX = 0.0f; // 用于保存旋转角度
float currentRotX = 0.0f;
public void setViewByMouse()
{
const int SM_CXSCREEN = ;
const int SM_CYSCREEN = ;
Point mousePos; /**< 保存当前鼠标位置 */
int middleX = GetSystemMetrics(SM_CXSCREEN) >> ; /**< 得到屏幕宽度的一半 */
int middleY = GetSystemMetrics(SM_CYSCREEN) >> ; /**< 得到屏幕高度的一半 */
float angleY = 0.0f; /**< 摄像机左右旋转角度 */
float angleZ = 0.0f; /**< 摄像机上下旋转角度 */ // 得到当前鼠标位置
GetCursorPos(out mousePos);
ShowCursor(); // 如果鼠标没有移动,则不用更新
if ((mousePos.X == middleX) && (mousePos.Y == middleY))
return; // 设置鼠标位置在屏幕中心
SetCursorPos(middleX, middleY); // 得到鼠标移动方向
angleY = (float)((middleX - mousePos.X)) / 1000.0f;
angleZ = (float)((middleY - mousePos.Y)) / 1000.0f; lastRotX = currentRotX; // 跟踪摄像机上下旋转角度
currentRotX += angleZ; // 如果上下旋转弧度大于1.0,我们截取到1.0并旋转
if (currentRotX > 1.0f)
{
currentRotX = 1.0f; // 根据保存的角度旋转方向
if (lastRotX != 1.0f)
{
// 通过叉积找到与旋转方向垂直的向量
Vector3 vAxis = m_View - m_Position;
vAxis = vAxis.crossProduct(m_UpVector);
vAxis = vAxis.normalize(); ///旋转
rotateView(1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);
}
}
// 如果旋转弧度小于-1.0,则也截取到-1.0并旋转
else if (currentRotX < -1.0f)
{
currentRotX = -1.0f;
if (lastRotX != -1.0f)
{ // 通过叉积找到与旋转方向垂直的向量
Vector3 vAxis = m_View - m_Position;
vAxis = vAxis.crossProduct(m_UpVector);
vAxis = vAxis.normalize(); rotateView(-1.0f - lastRotX, vAxis.x, vAxis.y, vAxis.z);
}
}
// 否则就旋转angleZ度
else
{
// 找到与旋转方向垂直向量
Vector3 vAxis = m_View - m_Position;
vAxis = vAxis.crossProduct(m_UpVector);
vAxis = vAxis.normalize(); rotateView(angleZ, vAxis.x, vAxis.y, vAxis.z);
} // 总是左右旋转摄像机
rotateView(angleY, , , );
} public void yawCamera(float speed)
{
Vector3 yaw = new Vector3();
Vector3 cross = m_View - m_Position;
cross = cross.crossProduct(m_UpVector); //归一化向量
yaw = cross.normalize(); m_Position.x += yaw.x * speed;
m_Position.z += yaw.z * speed; m_View.x += yaw.x * speed;
m_View.z += yaw.z * speed;
} public void moveCamera(float speed)
{
//计算方向向量
Vector3 vector = m_View - m_Position;
vector = vector.normalize(); //单位化 //更新摄像机
m_Position.x += vector.x * speed; //根据速度更新位置
m_Position.z += vector.z * speed;
m_Position.y += vector.y * speed; m_View.x += vector.x * speed; //根据速度更新方向
m_View.y += vector.y * speed;
m_View.z += vector.z * speed;
} //设置视点
public void setLook(OpenGL gl)
{
gl.LookAt(m_Position.x, m_Position.y, m_Position.z,
m_View.x, m_View.y, m_View.z,
m_UpVector.x, m_UpVector.y, m_UpVector.z);
}
} //向量运算类
class Vector3
{
public float x, y, z;
public Vector3()
{
x = ; y = ; z = ;
} public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
} public Vector3(Vector3 vec)
{
this.x = vec.x;
this.y = vec.y;
this.z = vec.z;
} public float length()
{
return (float)(x * x + y * y + z * z);
} public Vector3 normalize()
{
float len = length();
if (len == ) len = ;
x = x / len;
y = y / len;
z = z / len;
return this;
} //点积
public float dotProduct(Vector3 vec)
{
return 0f;
} public Vector3 crossProduct(Vector3 vec)
{
Vector3 v = new Vector3();
v.x = y * vec.z - z * vec.y;
v.y = z * vec.x - x * vec.z;
v.z = x * vec.y - y * vec.x;
return v;
} public static Vector3 operator +(Vector3 v1,Vector3 v2)
{
var res = new Vector3();
res.x=v1.x+v2.x;
res.y=v1.y+v2.y;
res.z=v1.z+v2.z;
return res;
} public static Vector3 operator -(Vector3 v1,Vector3 v2)
{
var res = new Vector3();
res.x=v1.x-v2.x;
res.y=v1.y-v2.y;
res.z=v1.z-v2.z;
return res;
} public static Vector3 operator *(Vector3 v1, Vector3 v2)
{
var res = new Vector3();
res.x = v1.x * v2.x;
res.y = v1.y * v2.y;
res.z = v1.z * v2.z;
return res;
} public static Vector3 operator /(Vector3 v1, Vector3 v2)
{
var res = new Vector3();
res.x = v1.x / v2.x;
res.y = v1.y / v2.y;
res.z = v1.z / v2.z;
return res;
} public static Vector3 operator -(Vector3 vec)
{
vec.x=-*vec.x;
vec.y=-*vec.y;
vec.z=-*vec.z;
return vec;
} } }

效果如下图:

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

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

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

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

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

补充一点:这个程序请按Alt+F4退出.

本节源代码下载

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

SharpGL学习笔记(十九) 摄像机漫游的更多相关文章

  1. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  2. (C/C++学习笔记) 十九. 模板

    十九. 模板 ● 模板的基本概念 模板(template) 函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计. 语法: template <<模 ...

  3. SharpGL学习笔记(十八) 解析3ds模型并显示

    笔者设想的3D仿真中的元件,是不可能都是“画”出来的.这样就玩复杂了,应该把任务分包出去,让善于制作模型的软件来制作三维模型,我们只需要解析并且显示它即可. 3dsmax制作三维模型的方便,快捷,专业 ...

  4. SharpGL学习笔记(十六) 多重纹理映射

    多重纹理就把多张贴图隔和在一起.比如下面示例中,一个表现砖墙的纹理,配合一个表现聚光灯效果的灰度图,就形成了砖墙被一个聚光灯照亮的效果,这便是所谓的光照贴图技术. 多重纹理只在OpenGL扩展库中才提 ...

  5. SharpGL学习笔记(十五) 纹理映射

    纹理映射非常实用,在游戏场景中已经无所不在了. 一个较少的多边形构成的模形,配合好的纹理贴图进行映射,可以得到逼真的效果.游戏中的天空,地面,墙面,和植物都是纹理贴图进行映射的. 例如最终幻想8的男女 ...

  6. SharpGL学习笔记(十四) 材质:十二个材质球

    材质颜色 OpenGL用材料对光的红.绿.蓝三原色的反射率来近似定义材料的颜色.象光源一样,材料颜色也分成环境.漫反射和镜面反射成分,它们决定了材料对环境光.漫反射光和镜面反射光的反射程度.在进行光照 ...

  7. SharpGL学习笔记(十二) 光源例子:解决光源场景中的常见问题

    笔者学到光源这一节,遇到的问题就比较多了,收集了一些如下所述: (1) 导入的3ds模型,如果没有材质光照效果很奇怪.如下图 (2) 导入的3ds模型,有材质,灯光效果发暗,材质偏色,效果也很奇怪. ...

  8. Java基础学习笔记十九 IO

    File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...

  9. Java基础学习笔记十九 File

    IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...

随机推荐

  1. UNIX环境高级编程笔记之进程环境

    本章讲的都是一些非常基础的知识,目的是为了下一章讲进程控制做铺垫,所以,本章就不做过多的总结了,直接看图吧.

  2. C# 开发者代码审查清单【转】

    这是为C#开发者准备的通用性代码审查清单,可以当做开发过程中的参考.这是为了确保在编码过程中,大部分通用编码指导原则都能注意到.对于新手和缺乏经验(0到3年工作经验)的开发者,参考这份清单编码会很帮助 ...

  3. CSS知识点-- Padding

    The CSS padding properties define the space between the element border and the element content. Padd ...

  4. python 集合set

    python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和 ...

  5. 图文详解远程部署ASP.NET MVC 5项目 [转载]

    话外篇: 由于感觉自己的机器比较慢,配置不好,所以最近想把之前的项目部署到实验室的服务器上,但是由于常不在实验室,所以在想能不能远程部署.因此今天专门研究了一下具体的过程,下面和大家分享一下.本人新手 ...

  6. 有了Hadoop MapReduce, 为什么还要Spark?

    a. 由于MapReduce的shuffle过程需写磁盘,比较影响性能:而Spark利用RDD技术,计算在内存中进行. b. MapReduce计算框架(API)比较局限, 而Spark则是具备灵活性 ...

  7. MyBatis知多少(23)MyBatis结果映射

    resultMap的元素是在MyBatis的最重要和最强大的元素.您可以通过使用MyBatis的结果映射减少高达90%的JDBC编码,在某些情况下,可以让你做JDBC不支持的事情. ResultMap ...

  8. outlook无法创建保存附件解决

    用outlook2003收发邮件时,当打开或另存附件时提示“无法创建和保存文件” 解决 开始-运行,输入“regedit”打开注册表,打开以下键值“HKEY_CURRENT_USER\software ...

  9. [Python] Symbol Review

    From:http://learnpythonthehardway.org/book/ex37.html 1. with X as Y: pass 1.1 yield 2. exec 2.1 name ...

  10. (翻译)为你的MVC应用程序创建自定义视图引擎

    Creating your own MVC View Engine For MVC Application 原文链接:http://www.codeproject.com/Articles/29429 ...