首页 技术 正文
技术 2022年11月17日
0 收藏 898 点赞 4,959 浏览 10442 个字

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

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

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

效果缩略图:

SharpGL学习笔记(十九) 摄像机漫游

源代码:

 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方向移动摄像机。

SharpGL学习笔记(十九) 摄像机漫游

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

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

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

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

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

本节源代码下载

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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,086
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,561
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,410
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,183
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,820
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,903