OpenGL--摄像机漫游

news/2025/2/6 9:01:29/

理论基础

在3D游戏中,我们通常可以通过鼠标或键盘操纵角色英雄在场景中移动,从不同的角度观察物体,这其实就是本章要介绍的摄像机漫游。
关于摄像机漫游其实就是围绕一个函数实现的(通过改变视点以及观察方向来实现),具体的函数为OpenGL中辅助函数库中的gluLookat(),通过设置相应的参数实现场景的漫游效果。函数如下:

void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,
GLdouble centerx,GLdouble centery,GLdouble centerz,
GLdouble upx,GLdouble upy,GLdouble upz);

该函数的9个参数定义了一个视图矩阵,并使用该矩阵与当前矩阵相乘。前三个参数(eyex,eyey,yeyz)定义了视点的位置,即观察者的位置(相当于我们人的眼睛);中间三个参数(centerx,centery,centerz)定义了摄像机瞄准的参考点,它决定了摄像机的朝向;最后三个参数(upx,upy,upz)定义了摄像机的向上向量,一般我们把它定义为(0, 1, 0)。
漫游过程实际上就是一个根据键盘或鼠标命令连续不断改变视点位置或视线方向并渲染场景的过程,这就好比我们通常所说的摄像机一样。关于摄像机的更多理论知识可以参考:
OpenGL–3D世界(视图变换,模型变换,投影变换,视口变换)

示例(vc++控制台项目)

  • 主程序流程代码:
#include "stdafx.h"
#include<gl/glut.h>
#include<gl/glu.h>
#include<gl/gl.h>
#include <gl\GLAUX.h>#include "Camera.h"
#include "CBMPLoader.h"/** 定义光源的属性值 */
GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };    /**< 环境光参数 */
GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };    /**< 漫射光参数 */
GLfloat LightSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f };   /**< 镜面光参数 */
GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f, 1.0f };   /**< 光源位置 */Camera m_Camera;
CBMPLoader m_Texture;/** 加载位图纹理(木箱贴图) */
bool LoadTexture()
{if (!m_Texture.LoadBitmap("image.bmp"))                                         /**< 载入位图文件 */{MessageBox(NULL, (LPCWSTR)"loadTexture error", (LPCWSTR)"error", MB_OK);    /**< 如果载入失败则弹出对话框 */return false;}glGenTextures(1, &m_Texture.ID);                                                /**< 生成一个纹理对象名称 */glBindTexture(GL_TEXTURE_2D, m_Texture.ID);                                     /**< 创建纹理对象 *//** 控制滤波 */glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);/** 创建纹理 */gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, m_Texture.imageWidth,m_Texture.imageHeight, GL_RGB, GL_UNSIGNED_BYTE,m_Texture.image);return true;
}/** 绘制网格地面 */
void DrawGrid()
{/** 获得场景中一些状态  */GLboolean  lp, tp;glGetBooleanv(GL_LIGHTING, &lp);glGetBooleanv(GL_TEXTURE_2D, &tp);/** 关闭纹理和光照 */glDisable(GL_TEXTURE_2D);glDisable(GL_LIGHTING);/** 绘制过程 */glPushAttrib(GL_CURRENT_BIT);   /**< 保存当前属性 */glPushMatrix();                 /**< 压入堆栈 */glTranslatef(0.0f, 0.0f, 0.0f);glColor3f(0.0f, 0.0f, 1.0f);    /**< 设置颜色 *//** 在X,Z平面上绘制网格 */for (float i = -50; i <= 50; i += 1){/** 绘制线 */glBegin(GL_LINES);/** X轴方向 */glVertex3f(-50, 0, i);glVertex3f(50, 0, i);/** Z轴方向 */glVertex3f(i, 0, -50);glVertex3f(i, 0, 50);glEnd();}glPopMatrix();glPopAttrib();/** 恢复场景状态 */if (tp)glEnable(GL_TEXTURE_2D);if (lp)glEnable(GL_LIGHTING);
}/** 绘制球体 */
void DrawSphere()
{/** 设置材质属性 */GLfloat mat_ambient[] = { 0.9f, 0.5f, 0.8f, 1.0f };GLfloat mat_diffuse[] = { 0.9f, 0.5f, 0.8f, 1.0f };GLfloat mat_shininess[] = { 100.0f };glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);/** 获得纹理启用状态 */GLboolean tp;glGetBooleanv(GL_TEXTURE_2D, &tp);glDisable(GL_TEXTURE_2D);                   /**< 关闭纹理 *//** 绘制过程 */glPushMatrix();glTranslatef(-5.0f, 2.0f, -10.0f);GLUquadricObj * sphere = gluNewQuadric();gluQuadricOrientation(sphere, GLU_OUTSIDE);gluQuadricNormals(sphere, GLU_SMOOTH);gluSphere(sphere, 2.0, 50, 50);gluDeleteQuadric(sphere);glPopMatrix();/** 恢复状态 */if (tp)glEnable(GL_TEXTURE_2D);
}/** 绘制木箱 */
void DrawBox()
{/** 设置材质属性 */GLfloat mat_ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f };GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);glPushMatrix();glTranslatef(5.0f, 2.0f, -10.0f);glScalef(2.0f, 2.0f, 2.0f);/** 选择纹理 */glBindTexture(GL_TEXTURE_2D, m_Texture.ID);/** 开始绘制四边形 */glBegin(GL_QUADS);/// 前侧面glNormal3f(0.0f, 0.0f, 1.0f);                               /**< 指定法线指向观察者 */glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);/// 后侧面glNormal3f(0.0f, 0.0f, -1.0f);                              /**< 指定法线背向观察者 */glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f);/// 顶面glNormal3f(0.0f, 1.0f, 0.0f);                               /**< 指定法线向上 */glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f);glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);/// 底面glNormal3f(0.0f, -1.0f, 0.0f);                              /**< 指定法线朝下 */glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f);glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);/// 右侧面glNormal3f(1.0f, 0.0f, 0.0f);                               /**< 指定法线朝右 */glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f);glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);/// 左侧面glNormal3f(-1.0f, 0.0f, 0.0f);                              /**< 指定法线朝左 */glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);glEnd();glPopMatrix();
}void init(void)
{glClearColor(0.0f, 0.0f, 0.0f, 0.5f);glClearDepth(1.0f);glDepthFunc(GL_LEQUAL);glEnable(GL_DEPTH_TEST);glShadeModel(GL_SMOOTH);glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);/** 设置光源的属性值 */glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);     /**< 设置环境光 */glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);     /**< 设置漫射光 */glLightfv(GL_LIGHT1, GL_SPECULAR, LightSpecular);   /**< 设置漫射光 *//** 启用光源和纹理 */glEnable(GL_LIGHTING);glEnable(GL_LIGHT1);glEnable(GL_TEXTURE_2D);/** 载入纹理 */if (!LoadTexture())MessageBox(NULL, (LPCWSTR)"载入纹理失败!", (LPCWSTR)"错误", MB_OK);/** 设置摄像机 */m_Camera.setCamera(0.0f, 1.5f, 6.0f, 0.0f, 1.5f, 0.0f, 0.0f, 1.0f, 0.0f);
}void display(void)
{/** 用户自定义的绘制过程 */glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();/** 放置摄像机 */m_Camera.setLook();glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);   /**< 设置光源位置 *//**< 绘制过程 */DrawGrid();DrawSphere();DrawBox();glFlush();
}void ChangeSize(int width, int height)
{glViewport(0, 0, width, height);                                    /**< 重新设置视口 */glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 1.0f, 100.0f);glMatrixMode(GL_MODELVIEW);glLoadIdentity();
}void motion(int x, int y)
{m_Camera.setViewByMouse();glutPostRedisplay();
}void keyboard(unsigned char key, int x, int y)
{switch (key) {case 27:exit(0);break;case 49:m_Camera.setSpeed(0.2f);break;case 50:m_Camera.setSpeed(0.6f);break;case 119:m_Camera.moveCamera(m_Camera.getSpeed());break;case 115:m_Camera.moveCamera(-m_Camera.getSpeed());break;case 97:m_Camera.yawCamera(-m_Camera.getSpeed());break;case 100:m_Camera.yawCamera(m_Camera.getSpeed());break;}glutPostRedisplay();printf("========%d", key);
}int main(int argc, char** argv)
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB);glutInitWindowSize(800, 600);glutInitWindowPosition((GetSystemMetrics(SM_CXSCREEN) >> 1) - 400, (GetSystemMetrics(SM_CYSCREEN) >> 1) - 300);glutCreateWindow("摄像机漫游");init();glutReshapeFunc(ChangeSize);glutDisplayFunc(display);glutMotionFunc(motion);glutKeyboardFunc(keyboard);glutMainLoop();return 0;
}

  • 辅助类
    1,向量类
#ifndef __VECTOR_H__
#define __VECTOR_H__#include <math.h>                            /**< 包含math头文件 *//** 向量类 */
class Vector3
{
public:/** 构造函数 */Vector3()  { x = 0.0; y = 0.0; z = 0.0; }Vector3( float xx, float yy, float zz){x = xx;y = yy;z = zz;}Vector3(const Vector3& vec){x = vec.x;y = vec.y;z = vec.z;}/** 成员函数 */inline float length();                       /**< 计算向量长度 */Vector3 normalize();                         /**< 单位化向量 */float dotProduct(const Vector3& v);          /**< 计算点积 */Vector3 crossProduct(const Vector3& v);      /**< 计算叉积 *//** 重载操作符 */Vector3 operator + (const Vector3& v);Vector3 operator - (const Vector3& v);Vector3 operator * (float scale);Vector3 operator / (float scale);Vector3 operator - ();public:float x,y,z;};#endif //__VECTOR_H__

#include "stdafx.h"
#include "Vector.h"                       /**< 包含头文件 *//** 计算向量的长度 */
inline float Vector3::length()
{return (float)( x * x + y * y + z * z );
}/** 单位化一向量 */
Vector3 Vector3::normalize()
{float len = length();                  /**< 计算向量长度 */if( len == 0 )len = 1;x = x / len;y = y / len;z = z / len;return *this;
}/** 点积 */float Vector3::dotProduct(const Vector3& v)
{return ( x * v.x + y * v.y + z * v.z );
}/** 叉积 */
Vector3 Vector3::crossProduct(const Vector3& v)
{Vector3 vec;vec.x = y * v.z - z * v.y;vec.y = z * v.x - x * v.z;vec.z = x * v.y - y * v.x;return vec;
}/** 操作符 + */Vector3 Vector3::operator +(const Vector3& v)
{Vector3 vec;vec.x = x + v.x;vec.y = y + v.y;vec.z = z + v.z;return vec;
}/** 操作符 - */Vector3 Vector3::operator -(const Vector3& v)
{Vector3 vec;vec.x = x - v.x;vec.y = y - v.y;vec.z = z - v.z;return vec;
}/** 操作符 * */Vector3 Vector3::operator *(float scale)
{x = x * scale;y = y * scale;z = z * scale;return *this;
}/** 操作符 / */Vector3 Vector3::operator /(float scale)
{if(scale != 0.0){   x = x / scale;y = y / scale;z = z / scale;}return *this;
}/** 负号 */Vector3 Vector3::operator -()
{Vector3 vec( - x,- y, - z);return vec;
}

2,摄像机类

#ifndef __CAMERA_H__
#define __CAMERA_H__#include <windows.h>
#include<gl/glu.h>                     /**< 包含gl头文件 */
#include "Vector.h"                 /**< 包含向量类头文件 *//** 摄像机类 */
class Camera
{
public:/** 构造函数和析构函数 */Camera();~Camera();/** 获得摄像机状态方法 */Vector3 getPosition()   {   return m_Position;      }Vector3 getView()       {   return m_View;          }Vector3 getUpVector()   {   return m_UpVector;      }float   getSpeed()      {   return m_Speed;         }/** 设置速度 */void    setSpeed(float speed){ m_Speed  = speed;}/** 设置摄像机的位置, 观察点和向上向量 */void setCamera(float positionX, float positionY, float positionZ,float viewX,     float viewY,     float viewZ,float upVectorX, float upVectorY, float upVectorZ);/** 旋转摄像机方向 */void rotateView(float angle, float X, float Y, float Z);/** 根据鼠标设置摄像机观察方向 */void setViewByMouse(); /** 左右摄像机移动 */void yawCamera(float speed);/** 前后移动摄像机 */void moveCamera(float speed);/** 放置摄像机 */void setLook();private:/** 摄像机属性 */Vector3        m_Position;      /**< 位置 */Vector3        m_View;          /**< 朝向 */Vector3        m_UpVector;      /**< 向上向量 */float          m_Speed;         /**< 速度 */};#endif //__CAMERA_H__

#include "stdafx.h"
#include "Camera.h"                    /**< 包含摄像机头文件 *//** 构造函数 */
Camera::Camera()
{/** 初始化向量值 */Vector3 zero = Vector3(0.0, 0.0, 0.0);      Vector3 view = Vector3(0.0, 1.0, 0.5);      Vector3 up   = Vector3(0.0, 0.0, 1.0);      /** 初始化摄像机 */m_Position  = zero;                 m_View      = view;             m_UpVector  = up;   m_Speed     = 0.2f;}Camera::~Camera()
{
}/** 设置摄像机的位置,朝向和向上向量 */
void Camera::setCamera( float positionX, float positionY, float positionZ,float viewX,     float viewY,     float viewZ,float upVectorX, float upVectorY, float upVectorZ)
{/** 构造向量 */Vector3 Position    = Vector3(positionX, positionY, positionZ);Vector3 View        = Vector3(viewX, viewY, viewZ);Vector3 UpVector    = Vector3(upVectorX, upVectorY, upVectorZ);/** 设置摄像机 */m_Position = Position;  m_View     = View;          m_UpVector = UpVector;  
}/**  旋转摄像机方向  */
void Camera::rotateView(float angle, float x, float y, float z)
{Vector3 newView;/** 计算方向向量 */Vector3 view = m_View - m_Position;     /** 计算 sin 和cos值 */float cosTheta = (float)cos(angle);float sinTheta = (float)sin(angle);/** 计算旋转向量的x值 */newView.x  = (cosTheta + (1 - cosTheta) * x * x)        * view.x;newView.x += ((1 - cosTheta) * x * y - z * sinTheta)    * view.y;newView.x += ((1 - cosTheta) * x * z + y * sinTheta)    * view.z;/** 计算旋转向量的y值 */newView.y  = ((1 - cosTheta) * x * y + z * sinTheta)    * view.x;newView.y += (cosTheta + (1 - cosTheta) * y * y)        * view.y;newView.y += ((1 - cosTheta) * y * z - x * sinTheta)    * view.z;/** 计算旋转向量的z值 */newView.z  = ((1 - cosTheta) * x * z - y * sinTheta)    * view.x;newView.z += ((1 - cosTheta) * y * z + x * sinTheta)    * view.y;newView.z += (cosTheta + (1 - cosTheta) * z * z)        * view.z;/** 更新摄像机的方向 */m_View = m_Position + newView;
}/** 用鼠标旋转摄像机 */
void Camera::setViewByMouse()
{POINT mousePos;                                   /**< 保存当前鼠标位置 */int middleX = GetSystemMetrics(SM_CXSCREEN) >> 1; /**< 得到屏幕宽度的一半 */int middleY = GetSystemMetrics(SM_CYSCREEN) >> 1; /**< 得到屏幕高度的一半 */float angleY = 0.0f;                              /**< 摄像机左右旋转角度 */float angleZ = 0.0f;                              /**< 摄像机上下旋转角度 */                 static float currentRotX = 0.0f;/** 得到当前鼠标位置 */GetCursorPos(&mousePos);                        ShowCursor(TRUE);/** 如果鼠标没有移动,则不用更新 */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;       static float lastRotX = 0.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, 0, 1, 0);
}/** 左右移动摄像机 */
void Camera::yawCamera(float speed)
{Vector3 yaw;Vector3 cross = m_View - m_Position;cross = cross.crossProduct(m_UpVector);// Normalize the strafe vectoryaw = cross.normalize();m_Position.x += yaw.x * speed;m_Position.z += yaw.z * speed;// Add the strafe vector to our viewm_View.x += yaw.x * speed;m_View.z += yaw.z * speed;
}/** 前后移动摄像机 */
void Camera::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_View.x += vector.x * speed;        /**< 根据速度更新方向 */   m_View.z += vector.z * speed;           
}/** 设置视点 */
void Camera::setLook()
{/** 设置视口 */gluLookAt(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);
}

3,bmp纹理加载类


#ifndef __CBMPLOADER_H__
#define __CBMPLOADER_H__#include<windows.h>        /**< 包含windows.h文件 */#define BITMAP_ID 0x4D42    /**< 位图文件的标志 *//** 位图载入类 */
class CBMPLoader
{public:CBMPLoader();~CBMPLoader();bool LoadBitmap(char *filename); /**< 装载一个bmp文件 */void FreeImage();                /**< 释放图像数据 */unsigned int ID;                 /**< 生成纹理的ID号 */int imageWidth;                  /**< 图像宽度 */int imageHeight;                 /**< 图像高度 */unsigned char *image;            /**< 指向图像数据的指针 */
};#endif //__CBMPLOADER_H__

#include "stdafx.h"
#include"CBMPLoader.h"              /**< 包含头文件 *//** 构造函数 */
CBMPLoader::CBMPLoader()
{/** 初始化成员值为0 */image = 0;imageWidth = 0;imageHeight = 0;
}/** 析构函数 */
CBMPLoader::~CBMPLoader()
{FreeImage(); /**< 释放图像数据占据的内存 */
}/** 装载一个位图文件 */
bool CBMPLoader::LoadBitmap(char *file)
{FILE *pFile = 0; /**< 文件指针 *//** 创建位图文件信息和位图文件头结构 */BITMAPINFOHEADER bitmapInfoHeader;BITMAPFILEHEADER header;unsigned char textureColors = 0;/**< 用于将图像颜色从BGR变换到RGB *//** 打开文件,并检查错误 */pFile = fopen(file, "rb");if(pFile == 0) return false;/** 读入位图文件头信息 */ fread(&header, sizeof(BITMAPFILEHEADER), 1, pFile);/** 检查该文件是否为位图文件 */if(header.bfType != BITMAP_ID){fclose(pFile);             /**< 若不是位图文件,则关闭文件并返回 */return false;}/** 读入位图文件信息 */fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, pFile);/** 保存图像的宽度和高度 */imageWidth = bitmapInfoHeader.biWidth;imageHeight = bitmapInfoHeader.biHeight;/** 确保读取数据的大小 */if(bitmapInfoHeader.biSizeImage == 0)bitmapInfoHeader.biSizeImage = bitmapInfoHeader.biWidth *bitmapInfoHeader.biHeight * 3;/** 将指针移到数据开始位置 */fseek(pFile, header.bfOffBits, SEEK_SET);/** 分配内存 */image = new unsigned char[bitmapInfoHeader.biSizeImage];/** 检查内存分配是否成功 */if(!image)                        /**< 若分配内存失败则返回 */{delete[] image;fclose(pFile);return false;}/** 读取图像数据 */fread(image, 1, bitmapInfoHeader.biSizeImage, pFile);/** 将图像颜色数据格式进行交换,由BGR转换为RGB */for(int index = 0; index < (int)bitmapInfoHeader.biSizeImage; index+=3){textureColors = image[index];image[index] = image[index + 2];image[index + 2] = textureColors;}fclose(pFile);       /**< 关闭文件 */return true;         /**< 成功返回 */
}/** 释放内存 */
void CBMPLoader::FreeImage()
{/** 释放分配的内存 */if(image){delete[] image;image = 0;}
}

示例图:
这里写图片描述


http://www.ppmy.cn/news/162912.html

相关文章

摄像机标定学习笔记

摄像机标定学习笔记&#xff0c;后面部分参考链接&#xff1a;http://www.sigvc.org/why/book/3dp/chap6.3.1.htm&#xff0c;如有侵权&#xff0c;立删。同时强烈推荐阅读该链接文章http://www.cnblogs.com/gemstone/articles/2293806.html 利用单目视觉测量模型解算出目标距离…

Unity摄像机详解

摄像机包含五个组件&#xff1a; Transform 变换组件Camera 摄像机&#xff1a;向玩家捕获并展示世界Audio Listener 音频监听器&#xff1a;接受场景输入的音频源并通过计算机的扬声器播放声音。Flare Layer&#xff1a;激活可显示光源耀斑GUI Layer&#xff1a;激活可渲染二…

网络摄像机-ISP基础一

&#xfeff;&#xfeff; 什么是ISP&#xff0c;他的工作原理是怎样的&#xff1f; ISP是Image Signal Processor的缩写&#xff0c;全称是影像处理器。在相机成像的整个环节中&#xff0c;它负责接收感光元件&#xff08;Sensor&#xff09;的原始信号数据&#xff0c;可以理…

URP——摄像机

Unity中的相机就像现实世界中的相机一样工作:它捕捉三维空间中的物体&#xff0c;然后将其展平&#xff0c;显示在二维平面上。 通用渲染管线(URP)中的摄像头基于Unity的标准摄像头功能&#xff0c;但有一些显著的区别。URP相机与标准Unity相机最显著的区别是: 通用附加相机数…

UE4摄像机系统解析

一&#xff0e;摄像机工作原理 在游戏中&#xff0c;摄像机是玩家的眼睛&#xff0c;他控制了玩家的视点&#xff08;POV即PointOfView&#xff0c;后面简称POV&#xff09;位置以及玩家的视野大小&#xff08;FOV即FieldOfView&#xff0c;后面简称FOV&#xff09;。一句话&a…

unity摄影机depth模式_摄像机的深度图

Camera可以创建深度图、深度+法向纹理、运动向量图。这算是一种简化版的G-buffer,这些纹理图可以用在post-processing中,从而实现自定义的光照模型。我们也可以使用Shader Replacement创建这类纹理图。 Camera虽然内置了创建深度图的功能,但是通常该功能是关闭的,需要在脚本…

ping 丢包 网络摄像头_网络监控摄像机丢包的几大原因分析!

不少朋友在做安防监控工程中,使用网络和监控摄像系统的时候都有遇到过数据丢包的情况,数据丢包的原因是多种多样的,以下就为大家介绍一下网络数据丢包的原因及摄像机丢包的原因。 摄像机丢包的原因1:路由错误 网络路径错误也会导致数据包不能到达目的主机,如主机的默认路由…

视频-摄像机-推流

本文主要介绍使用 WVPZLMediaKitMediaServerUI 实现通过 GB28181 进行海康、大华、宇视等品牌的 IPC、NVR 、DVR 接入&#xff0c;完成摄像头监控播放&#xff0c;控制&#xff0c;录制。 一、概念 IPC是&#xff08;IP Camera 即网络摄像机&#xff09;缩写&#xff0c;由网…