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

时间:2022-08-28 12:36:47

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

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

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

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

  1. python3&period;4学习笔记&lpar;十九&rpar; 同一台机器同时安装 python2&period;7 和 python3&period;4的解决方法

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

  2. &lpar;C&sol;C&plus;&plus;学习笔记&rpar; 十九&period; 模板

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

  3. SharpGL学习笔记&lpar;十八&rpar; 解析3ds模型并显示

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

  4. SharpGL学习笔记&lpar;十六&rpar; 多重纹理映射

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

  5. SharpGL学习笔记&lpar;十五&rpar; 纹理映射

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

  6. SharpGL学习笔记&lpar;十四&rpar; 材质:十二个材质球

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

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

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

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

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

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

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

随机推荐

  1. PHP获取上个月最后一天的一个容易忽略的问题

    正常来说,PHP是有一个很方便的函数可以获取上个月时间的 strtotime (PHP 4, PHP 5, PHP 7) strtotime - 将任何英文文本的日期时间描述解析为 Unix 时间戳 ...

  2. 操作系统学习笔记(五)--CPU调度

    由于第四章线程的介绍没有上传视频,故之后看书来补. 最近开始学习操作系统原理这门课程,特将学习笔记整理成技术博客的形式发表,希望能给大家的操作系统学习带来帮助.同时盼望大家能对文章评论,大家一起多多交 ...

  3. c&plus;&plus; 构造函数以及explicit 关键字的使用

    关于构造函数中的隐式转换: 在一个类所定义的构造函数中,存在如下的用法: #pragma once #ifndef __EXERCISE__ #define __EXERCISE__ #include ...

  4. 微信小程序实现淡入淡出效果(页面跳转)

    //目前小程序没有fadeIn(),fadeOut()方法所以还是本方法手写  <!--wxml--><!--蒙版(渐出淡去效果)--><view class=&quot ...

  5. DIY 空气质量检测表

    DIY 空气质量检测表 前几天逛淘宝看到有空气颗粒物浓度测量的传感器,直接是 3.3V TTL 电压串口输出的,也不贵,也就 100 多一点.觉得挺好就买了个,这两天自己捣鼓了个小程序,搞了个软件界面 ...

  6. &lbrack;Android&rsqb;使用Spring for Android改善数据交互流程

    如果开发一个和服务端有数据交互android应用,流程通常是这样的:界面收集用户数据之后,将它转换成JSON或者XML格式的字符串,以HTTP的方式提交给服务端,获得返回的文本数据,再将数据解析为ja ...

  7. MySql使用笔记

    mysql版本信息:mysqld --version Ver 5.6.24 在服务里面看不到mysql 安装服务: mysqld.exe -install 启动服务: net start mysql ...

  8. jQuery-修改元素属性

    1.attr方法 获取匹配的元素集合中的第一个元素的属性的值 或 设置匹配元素指定的属性 使用说明: 1)只传一个参数的情况: 1>字符串(属性名称) 只传一个字符串属性名称 表示获取匹配的元素 ...

  9. SaltStack实现Haproxy Nginx&plus;php MySQL主从

    构建图如下 参考文档:https://github.com/unixhot/saltbook-code/tree/master

  10. struts2 和 js 标签取值

    struts标签是在服务器上替换成html代码的,js是在用户浏览器执行的,这个顺序如果没搞清楚你是搞不好web开发的