前言

          使用openGL模拟烟雾特效,基于Bulr算法,通常,图像处理软件会提供"模糊"(blur)滤镜,使图片产生模糊的效果,"模糊"的算法有很多种,其中有一种叫做​​"高斯模糊"​​​(Gaussian Blur)。它将​​正态分布​​(又名"高斯分布")用于图像处理。

       

openGL模拟烟雾特效_#include

"模糊"的算法有很多种,其中有一种叫做​​"高斯模糊"​​​(Gaussian Blur)。它将​​正态分布​​(又名"高斯分布")用于图像处理。

​讲解高斯分布文章​​,大家可以参考下

原理

一下内容参考网上大牛:

作者: ​​阮一峰​

日期: ​​2012年11月14日​

photoShop上面有 Blur选项

openGL模拟烟雾特效_openGL烟雾特效_02

本文介绍"高斯模糊"的算法,你会看到这是一个非常简单易懂的算法。本质上,它是一种​​数据平滑技术​​(data smoothing),适用于多个场合,图像处理恰好提供了一个直观的应用实例。

一、高斯模糊的原理

所谓"模糊",可以理解成每一个像素都取周边像素的平均值。

openGL模拟烟雾特效_Blur模糊算法_03

上图中,2是中间点,周边点都是1。

openGL模拟烟雾特效_Blur模糊算法_04

"中间点"取"周围点"的平均值,就会变成1。在数值上,这是一种"平滑化"。在图形上,就相当于产生"模糊"效果,"中间点"失去细节。

openGL模拟烟雾特效_#include_05

显然,计算平均值时,取值范围越大,"模糊效果"越强烈。

openGL模拟烟雾特效_openGL烟雾特效_06

上面分别是原图、模糊半径3像素、模糊半径10像素的效果。模糊半径越大,图像就越模糊。从数值角度看,就是数值越平滑。

接下来的问题就是,既然每个点都要取周边像素的平均值,那么应该如何分配权重呢?

如果使用简单平均,显然不是很合理,因为图像都是连续的,越靠近的点关系越密切,越远离的点关系越疏远。因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。

二、正态分布的权重

正态分布显然是一种可取的权重分配模式。

openGL模拟烟雾特效_i++_07

在图形上,正态分布是一种钟形曲线,越接近中心,取值越大,越远离中心,取值越小。

计算平均值的时候,我们只需要将"中心点"作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值。

三、高斯函数

上面的正态分布是一维的,图像都是二维的,所以我们需要二维的正态分布。

openGL模拟烟雾特效_#include_08

正态分布的密度函数叫做​​"高斯函数"​​(Gaussian function)。它的一维形式是:

其中,μ是x的均值,σ是x的方差。因为计算平均值的时候,中心点就是原点,所以μ等于0。

根据一维高斯函数,可以推导得到二维高斯函数:

有了这个函数 ,就可以计算每个点的权重了。

四、权重矩阵

假定中心点的坐标是(0,0),那么距离它最近的8个点的坐标如下:

openGL模拟烟雾特效_openGL烟雾特效_09

更远的点以此类推。

为了计算权重矩阵,需要设定σ的值。假定σ=1.5,则模糊半径为1的权重矩阵如下:

openGL模拟烟雾特效_i++_10

这9个点的权重总和等于0.4787147,如果只计算这9个点的加权平均,还必须让它们的权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。

openGL模拟烟雾特效_Blur模糊算法_11

五、计算高斯模糊

有了权重矩阵,就可以计算高斯模糊的值了。

假设现有9个像素点,灰度值(0-255)如下:

openGL模拟烟雾特效_openGL烟雾特效_12

每个点乘以自己的权重值:

openGL模拟烟雾特效_Blur模糊算法_13

得到

openGL模拟烟雾特效_Blur模糊算法_14

将这9个值加起来,就是中心点的高斯模糊的值。

对所有点重复这个过程,就得到了高斯模糊后的图像。如果原图是彩色图片,可以对RGB三个通道分别做高斯模糊。

六、边界点的处理

如果一个点处于边界,周边没有足够的点,怎么办?

一个变通方法,就是把已有的点拷贝到另一面的对应位置,模拟出完整的矩阵。

七、参考文献

* ​​How to program a Gaussian Blur without using 3rd party libraries​

实践

          好了,上面介绍完了Blur理论知识,现在开始从代码方面讨论在openGL中实现基于Blur的烟雾特效。

先看下运行效果:烟雾是动态的哦

openGL模拟烟雾特效_i++_15

tools.h   这个头文件是个工具类,主要实现openGL基本的操作,矩阵操作、点乘、叉乘、坐标变换

#include <cmath>
#include "gl/glut.h"

using namespace std;

struct GPoint3d {
double mX, mY, mZ;
double x() { return mX; }
double y() { return mY; }
double z() { return mZ; }
void setX(double x) { mX = x; }
void setY(double y) { mY = y; }
void setZ(double z) { mZ = z; }
void set(double x, double y, double z) { mX = x; mY = y; mZ = z; }
};

class TrackBall
{
int OldX;
int OldY;
double mMatrix[16];
public:
TrackBall() {}
// 向量的点积
double dotMult(GPoint3d v1, GPoint3d v2);
// 向量的叉积
GPoint3d crossMult(GPoint3d v1, GPoint3d v2);
// 将鼠标二维点映射为球面向量(用于鼠标追踪球)
GPoint3d gMousePtToSphereVec(int x, int y, int w, int h);
void makeRolate();

void MouseMove(int x, int y);
void resize()
{
glGetDoublev(GL_MODELVIEW_MATRIX, mMatrix); // 返回当前模型矩阵
}
void setXY(int x, int y) { OldX = x; OldY = y; }
void setP(double *v)//{x1,y1,z1, x2,y2,z2, x3,y3,z3}
{
GPoint3d v1, v2, v3;
v1.setX(v[3] - v[0]);
v1.setY(v[4] - v[1]);
v1.setZ(v[5] - v[2]);

v2.setX(v[6] - v[0]);
v2.setY(v[7] - v[1]);
v2.setZ(v[8] - v[2]);

v3 = crossMult(v1, v2);

glNormal3f(v3.x(), v3.y(), v3.z());
}
};

 tools.cpp

#include "tools.h"


double TrackBall::dotMult(GPoint3d v1, GPoint3d v2)
{
double angle;
angle = v1.x()*v2.x() + v1.y()*v2.y() + v1.z()*v2.z();
return angle;
}
GPoint3d TrackBall::crossMult(GPoint3d v1, GPoint3d v2)
{
GPoint3d v;
v.setX(v1.y()*v2.z() - v1.z()*v2.y());
v.setY(v1.z()*v2.x() - v1.x()*v2.z());
v.setZ(v1.x()*v2.y() - v1.y()*v2.x());
return v;
}

// 将鼠标二维点映射为球面向量(用于鼠标追踪球)
GPoint3d TrackBall::gMousePtToSphereVec(int x, int y, int w, int h)
{
double x1, y1, z1, r, len;
GPoint3d vec;
x1 = (2.0*x - w) / w;
y1 = (h - 2.0*y) / h;
r = x1*x1 + y1*y1;
if (r > 1) r = 1;
z1 = sqrt(1 - r);
len = sqrt(x1*x1 + y1*y1 + z1*z1);
vec.setX(x1 / len);
vec.setY(y1 / len);
vec.setZ(z1 / len);
return vec;
}
void TrackBall::makeRolate()
{
glMultMatrixd(mMatrix);
}
void TrackBall::MouseMove(int x, int y)
{
if (x != OldX || y != OldY)
{
int wWidth, wHeight;
wWidth = glutGet(GLUT_WINDOW_WIDTH);
wHeight = glutGet(GLUT_WINDOW_HEIGHT);
GPoint3d lastVec = gMousePtToSphereVec(OldX, OldY, wWidth, wHeight);
GPoint3d currentVec = gMousePtToSphereVec(x, y, wWidth, wHeight);
OldX = x; OldY = y;
// 求旋转角度
double rotAngle = acos(dotMult(lastVec, currentVec))*57.29577958;
// 求旋转向量轴
GPoint3d axis = crossMult(lastVec, currentVec);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glRotated(rotAngle, axis.x(), axis.y(), axis.z()); // 旋转

glMultMatrixd(mMatrix);
glGetDoublev(GL_MODELVIEW_MATRIX, mMatrix); // 返回当前模型矩阵

glPopMatrix();
}
}

Grids.h  绘制网格用的,可以暂时不管他

#include <iostream>

#include <cmath>

using namespace std;

class Grids
{
int row;
int col;
double **mat;
public:
Grids();
Grids(int r, int c);
Grids(int r, int c, double **m);
~Grids();
void Mul_Mat(Grids* m1, Grids* m2, Grids* m3);
void Inv_Mat(Grids* m1, Grids* m2);
double** Surface_Fitting(Grids* m1, Grids* m2);
};

Grids.cpp

#include "Grids.h"

Grids::Grids()
{
row = 0;
col = 0;
mat = 0;
}

Grids::Grids(int r, int c)
{
row = r;
col = c;
mat = 0;
}

Grids::Grids(int r, int c, double **m)
{
row = r;
col = c;
mat = m;
}

Grids::~Grids()
{
mat = 0;
row = 0;
col = 0;
}

void Grids::Mul_Mat(Grids* m1, Grids* m2, Grids* m3)
{
int i = 0, j = 0, p = 0;
double sum = 0;
if (m1->col != m2->row) {
cout << "\n行、列数不匹配!";
exit(0);
}
m3->row = m1->row;
m3->col = m2->col;
m3->mat = new double*[m1->row];
if (NULL == m3->mat) {
cout << "ERROR!\n";
exit(0);
}
for (i = 0; i < m3->row; i++) {
m3->mat[i] = new double[m3->col];
for (j = 0; j < m3->col; j++) {
for (m3->mat[i][j] = 0, p = 0; p < m1->col; p++) {
m3->mat[i][j] += m1->mat[i][p] * m2->mat[p][j];
}
}
}
}

void Grids::Inv_Mat(Grids* m1, Grids* m2)
{
int i, j, n, *is, *js, k;
double d, p;
if (m1->row != m1->col) {
cout << "ERROR! 必须是方阵才能求逆!\n";
exit(1);
}
m2->mat = new double*[m1->row];//申请行指针数组
if (NULL == m2->mat) {
cout << "ERROR! 申请内存出错!\n";
exit(1);
}
for (i = 0; i < m1->row; i++) {
m2->mat[i] = new double[m1->col];//申请行
for (j = 0; j < m1->col; j++)
m2->mat[i][j] = m1->mat[i][j];
}
n = m1->row;
m2->row = m1->row;
m2->col = m1->col;
is = new int[n];
js = new int[n];
if (NULL == is || NULL == js) {
cout << "ERROR! 申请内存出错!\n";
exit(1);
}
for (k = 0; k <= n - 1; k++) { //全选主元
d = 0.000;
for (i = k; i <= n - 1; i++) {
for (j = k; j <= n - 1; j++) {
p = fabs(m2->mat[i][j]);
if (p > d) {
d = p;
is[k] = i;
js[k] = j;
}
}
}
if (1.0 == d + 1.0) {
delete[]is;
delete[]js;
cout << "ERROR ! 矩阵求逆出错!\n";
exit(1);
}
if (is[k] != k) { /*行交换*/
for (j = 0; j <= n - 1; j++) {
p = m2->mat[k][j];
m2->mat[k][j] = m2->mat[is[k]][j];
m2->mat[is[k]][j] = p;
}
}
if (js[k] != k) { /*列交换*/
for (i = 0; i <= n - 1; i++) {
p = m2->mat[i][k];
m2->mat[i][k] = m2->mat[i][js[k]];
m2->mat[i][js[k]] = p;
}
}
m2->mat[k][k] = 1 / m2->mat[k][k];
for (j = 0; j <= n - 1; j++) {
if (j != k) {
m2->mat[k][j] = m2->mat[k][j] * m2->mat[k][k];
}
}
for (i = 0; i <= n - 1; i++) {
if (i != k) {
for (j = 0; j <= n - 1; j++) {
if (j != k) {
m2->mat[i][j] = m2->mat[i][j] - m2->mat[i][k] * m2->mat[k][j];
}
}
}
}
for (i = 0; i <= n - 1; i++) {
if (i != k) {
m2->mat[i][k] = -m2->mat[i][k] * m2->mat[k][k];
}
}
}
for (k = n - 1; k >= 0; k--) {
if (js[k] != k) {
for (j = 0; j <= n - 1; j++) {
p = m2->mat[k][j];
m2->mat[k][j] = m2->mat[js[k]][j];
m2->mat[js[k]][j] = p;
}
}
if (is[k] != k) {
for (i = 0; i <= n - 1; i++) {
p = m2->mat[i][k];
m2->mat[i][k] = m2->mat[i][is[k]];
m2->mat[i][is[k]] = p;
}
}
}
delete[]is;
delete[]js;
}

double** Grids::Surface_Fitting(Grids* m1, Grids* m2)
{ // m1为已知数组R,m2为自变量数组
int i, j;
double ipso = 0.05;
Grids *R = new Grids(m1->col + 3, m1->col + 3);
R->mat = new double*[R->row];
for (i = 0; i < R->row; i++)
R->mat[i] = new double[R->col];
for (i = 0; i < R->row; i++) {
for (j = 0; j < R->col; j++) {
if (i < m1->col&&j < m1->col) {
if (i == j)
R->mat[i][j] = ipso;
else
R->mat[i][j] = sqrt(pow(m1->mat[0][i] - m1->mat[0][j], 2) + pow(m1->mat[1][i] - m1->mat[1][j], 2));
}
if (i >= m1->col&&j < m1->col) {
if (i == m1->col)
R->mat[i][j] = 1;
else
R->mat[i][j] = m1->mat[i - m1->col - 1][j];
}
if (i < m1->col&&j >= m1->col) {
if (j == m1->col)
R->mat[i][j] = 1;
else
R->mat[i][j] = m1->mat[j - (m1->col) - 1][i];
}
if (i >= m1->col&&j >= m1->col) {
R->mat[i][j] = 0;
}
}
}

Grids *z1 = new Grids(m1->col + 3, 1);
Grids *invm1 = new Grids();
Grids *F = new Grids();
z1->mat = new double*[z1->row];
for (i = 0; i < z1->row; i++) {
z1->mat[i] = new double[z1->col];
for (j = 0; j < z1->col; j++) {
if (i < m1->col)
z1->mat[i][j] = m1->mat[2][i];
else
z1->mat[i][j] = 0;
}
}
Inv_Mat(R, invm1);

Mul_Mat(invm1, z1, F);

Grids *z = new Grids();
Grids *r = new Grids(m2->col, F->row);
r->mat = new double*[r->row];
for (i = 0; i < r->row; i++)
r->mat[i] = new double[r->col];
for (i = 0; i < r->row; i++) {
for (j = 0; j < m1->col + 3; j++) {
if (j < m1->col) {
double temp = sqrt(pow(m2->mat[0][i] - m1->mat[0][j], 2) + pow(m2->mat[1][i] - m1->mat[1][j], 2));
r->mat[i][j] = temp*log(temp + ipso);
}
else {
if (j == m1->col)
r->mat[i][j] = 1;
else
r->mat[i][j] = m2->mat[j - m1->col - 1][i];
}
}
}
/* FF->mat=new double*[FF->row];
for(i=0;i<FF->row;i++)
FF->mat[i]=new double[FF->col];
for(i=0;i<FF->row;i++){
for(j=0;j<FF->col;j++){
if(i<m2->col)
FF->mat[i][j]=F->mat[i/4][j/4];
else
FF->mat[i][j]=F->mat[i-m2->col+m1->col][j];
}
}*/
Mul_Mat(r, F, z);

Grids *xyz = new Grids(m2->row + 1, m2->col);
xyz->mat = new double*[xyz->row];
for (i = 0; i < xyz->row; i++)
xyz->mat[i] = new double[xyz->col];
for (i = 0; i < xyz->row; i++) {
for (j = 0; j < xyz->col; j++) {
if (i < m2->row)
xyz->mat[i][j] = m2->mat[i][j];
else
xyz->mat[i][j] = z->mat[j][0];
}
}
for (i = 0; i < R->row; i++)
delete[] R->mat[i];
delete[] R->mat;
for (i = 0; i < z1->row; i++)
delete[] z1->mat[i];
delete[] z1->mat;
for (i = 0; i < invm1->row; i++)
delete[] invm1->mat[i];
delete[] invm1->mat;
for (i = 0; i < F->row; i++)
delete[] F->mat[i];
delete[] F->mat;
for (i = 0; i < z->row; i++)
delete[] z->mat[i];
delete[] z->mat;
for (i = 0; i < r->row; i++)
delete[] r->mat[i];
delete[] r->mat;
return xyz->mat;
}

3dmap.h  主要是模拟点的,与烟雾无关,也可以不用管它

class _3dMap
{
double **data;
int M, N;
int dip;
double scale;
bool showBaseLine;
double max, min;
bool LineMode;
public:
_3dMap();
~_3dMap();

void initMap();
void drawBaseLine();
void showColorStrip();
void drawMap();
//void _3dMap::setLineOrFill();
void setLineOrFill();
double getAver(double * arr);
void setColor(double z1, double z2, double z3);
};

3dmap.cpp

#include "3dmap.h"
#include "Grids.h"
#include "GL/glut.h"

_3dMap::_3dMap()
{
M = 320;
N = 220;
showBaseLine = true;
LineMode = false;
scale = 20.0;
dip = 1;
max = -1000;
min = 10000;
}

_3dMap::~_3dMap()
{

}



void _3dMap::setLineOrFill()
{
LineMode = !LineMode;
}

double _3dMap::getAver(double * arr)
{
double num = 0;
int n = 3;//(int)(sizeof(arr)/sizeof(double))/3;
for (int i = 0; i < n; i++)
num += arr[3 * i + 2];
return num / n;
}

void _3dMap::setColor(double z1, double z2, double z3)
{
float r, g, b;
double temp = (max + min) / 2;
double aver = (z1 + z2 + z3) / 3;
/*printf("%lf ",aver);*/
if (aver > temp)
{
r = (aver - temp) / (temp - min);
b = 0;
}
else {
r = 0;
b = (temp - aver) / (temp - min);
}
g = 1 - ((abs(temp - aver)) / (temp - min));
glColor3f(r, g, b);
}


void _3dMap::initMap()
{

int temp[] = {
229,219,199,216,235,255,266,285,272,241,246,281,284,275,261,273,
221,214,195,216,234,258,273,289,281,249,259,278,287,272,275,277,
213,203,196,206,221,232,259,293,294,277,258,285,287,283,288,286,
204,195,200,201,209,218,231,259,288,306,286,291,301,311,319,298,
196,207,201,211,239,234,241,259,294,315,317,321,325,322,325,341,
208,218,204,214,235,260,239,268,298,291,331,313,281,280,280,280,
216,231,218,196,220,255,271,253,264,303,322,312,276,243,238,239,
236,242,218,198,200,215,224,238,261,294,324,312,280,255,220,200,
255,241,219,211,206,225,252,275,284,285,305,316,271,237,208,191,
245,218,207,198,214,241,261,256,273,276,291,298,281,238,197,175,
225,215,205,195,208,221,235,252,262,271,301,275,245,212,181,171
};
int len = 11 * 16;
int i, j;
double** src = new double*[3];
for (i = 0; i < 3; i++)
src[i] = new double[len];
for (i = 0; i < 11; i++)
{//y
for (j = 0; j < 16; j++)
{//x
src[0][i * 16 + j] = j * 10;
src[1][i * 16 + j] = i * 10;
src[2][i * 16 + j] = temp[i * 16 + j];
}
}
Grids g1(3, len, src);
double** des = new double*[2];
for (i = 0; i < 2; i++)
des[i] = new double[N*M];
for (i = 0; i < N; i++)
{//y
for (j = 0; j < M; j++)
{//x
des[0][i*M + j] = j / 2;
des[1][i*M + j] = i / 2;
}
}
Grids g2(2, N*M, des);
data = g1.Surface_Fitting(&g1, &g2);

for (i = 0; i < N; i++)
{//height
for (int j = 0; j < M; j++)
{//width
data[0][i*M + j] *= 2;
data[1][i*M + j] *= 2;
data[2][i*M + j] /= 5;

if (max < data[2][i*M + j])
max = data[2][i*M + j];
if (min > data[2][i*M + j])
min = data[2][i*M + j];
}
}
printf("max=%lf,min=%lf\n", max, min);
}
void _3dMap::drawBaseLine()
{
glColor3f(0.0f, 0.0f, 1.0f);//指定线的颜色,蓝色

glLineWidth(4);
glBegin(GL_LINES);
{
// x-axis

glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(410.0f, 0.0f, 0.0f);

// x-axis arrow

glVertex3f(410.0f, 0.0f, 0.0f);
glVertex3f(400.0f, 6.0f, 0.0f);
glVertex3f(410.0f, 0.0f, 0.0f);
glVertex3f(400.0f, -6.0f, 0.0f);
}
glEnd();

glColor3f(0.0f, 1.0f, 0.0f);//指定线的颜色,绿色

glBegin(GL_LINES);
{
// y-axis

glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 410.0f, 0.0f);

glVertex3f(0.0f, 410.0f, 0.0f);
glVertex3f(6.0f, 400.0f, 0.0f);
glVertex3f(0.0f, 410.0f, 0.0f);
glVertex3f(-6.0f, 400.0f, 0.0f);
}
glEnd();


glColor3f(1.0f, 0.0f, 0.0f);//指定线的颜色,红色

glBegin(GL_LINES);
{
// z-axis

glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 410.0f);
glVertex3f(0.0f, 0.0f, 410.0f);
glVertex3f(0.0f, 6.0f, 400.0f);
glVertex3f(0.0f, 0.0f, 410.0f);
glVertex3f(0.0f, -6.0f, 400.0f);
}
glEnd();


//绘制y-z平面表格
glColor3f((GLfloat)216.0f / 255.0f, (GLfloat)191.0f / 255.0f, (GLfloat)216.0f / 255.0f);//指定线的颜色,蓝色
//glColor3f(0.0f, 0.0f, 0.13f);
glLineWidth(2);
glBegin(GL_LINES);
{
for (int i = 1; i <= 400; i += 40)
{
glVertex3f(0.0f, 370.0f, i*1.0f); //纵向
glVertex3f(0.0f, 0.0f, i*1.0f);

glVertex3f(0.0f, i*1.0f, 370.0f); //横向
glVertex3f(0.0f, i*1.0f, 0.0f);
}
}
glEnd();

//x-y平面表格
glBegin(GL_LINES);
{
for (int i = 1; i <= 400; i += 40)
{
glVertex3f(i*1.0f, 370.0f, 0.0f); //纵向
glVertex3f(i*1.0f, 0.0f, 0.0f);

glVertex3f(0.0f, i*1.0f, 0.0f); //横向
glVertex3f(370.0f, i*1.0f, 0.0f);
}
}
glEnd();


//x-z平面表格
glBegin(GL_LINES);
{
for (int i = 1; i <= 400; i += 40)
{
glVertex3f(370.0f, 0.0f, i*1.0f);
glVertex3f(0.0f, 0.0f, i*1.0f);

glVertex3f(i*1.0f, 0.0f, 0.0f); //横向
glVertex3f(i*1.0f, 0.0f, 370.0f);
}
}
glEnd();
}

void _3dMap::showColorStrip()
{
GLint width = 200;
GLint height = 10;

GLsizei dw;
int n, i;
dw = 2;
n = (width - 1) / dw;
//cout << "n=" << n << endl;
//glClear(GL_COLOR_BUFFER_BIT);
GLsizei r, g, b;
for (i = 0; i < n; i++)
{
//绘制应力云图的关键就是下面的这五种不同的颜色对应关系
//根据自己需要可以设置不同的等级,四种也是可以的
if (i >= 0 && i <= n / 5)
{
r = 255;
g = i * 255 / (n / 5);
b = 0;
}
else if (i > n / 5 && i <= 2 * n / 5)
{
r = 255 - (i - n / 5) * 255 / (n / 5);
g = 255;
b = 0;
}
else if (i > 2 * n / 5 && i <= 3 * n / 5)
{
r = 0;
g = 255;
b = (i - 2 * n / 5) * 255 / (n / 5);
}
else if (i > 3 * n / 5 && i <= 4 * n / 5)
{
r = 0;
g = 255 - (i - 3 * n / 5) * 255 / (n / 5);
b = 255;
}
else
{
r = (i - 4 * n / 5) * 255 / (n / 5);
g = 0;
b = 255;
}
glColor3f(r / 255.0f, g / 255.0f, b / 255.0f);
glRectf(1.0*i*dw, 0.0, 1.0*(i + 1)*dw, height);
}


glFlush();
}
void _3dMap::drawMap()
{

if (showBaseLine)
drawBaseLine();

if (LineMode)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

glLineWidth(0.4f);
for (int i = 0; i < N - 1; i++)
{//height22

for (int j = 0; j < M - 1; j++)
{//width32
glBegin(GL_TRIANGLE_FAN);
int t = i*M + j;
setColor(data[2][t], data[2][t + 1], data[2][t + 1 + M]);

glVertex3f(data[0][t], data[1][t], data[2][t]);
glVertex3f(data[0][t + 1], data[1][t + 1], data[2][t + 1]);
glVertex3f(data[0][t + 1 + M], data[1][t + 1 + M], data[2][t + 1 + M]);
setColor(data[2][t + M], data[2][t + 1], data[2][t + 1 + M]);
glVertex3f(data[0][t + M], data[1][t + M], data[2][t + M]);
glEnd();
}
}
}

xFreeTypeLib.h   这时渲染三维字体用的,也可以不用管它

#ifndef _X_FREE_TYPE_LIB_H_
#define _X_FREE_TYPE_LIB_H_
#endif //_X_FREE_TYPE_LIB_H_


#include <Windows.h>
#include <iostream>
#include "Gl\GL.h"
#include "Gl\glut.h"
#include "ft2build.h"
#include "freetype/freetype.h"
#include "freetype/ftglyph.h"
#include "freetype/ftoutln.h"
#include "freetype/fttrigon.h"

#define MAX_NO_TEXTURES 1

#define CUBE_TEXTURE 0

//GLuint texture_id[MAX_NO_TEXTURES];

typedef struct xCharTexture
{
GLuint m_texID;
wchar_t m_chaID;
int m_Width;
int m_Height;

int m_adv_x;
int m_adv_y;
int m_delta_x;
int m_delta_y;
public:
xCharTexture()
{
m_texID = 0;
m_chaID = 0;
m_Width = 0;
m_Height = 0;
}
}stuxCharTexture;
/*g_TexID[65536];*/

class xFreeTypeLib
{
FT_Library m_FT2Lib;
FT_Face m_FT_Face;

public:
int m_w;
int m_h;
void load(const char* font_file, int _w, int _h);
GLuint loadChar(wchar_t ch);
};

xFreeTypeLib.cpp

#include "xFreeTypeLib.h"

stuxCharTexture g_TexID[65536];

void xFreeTypeLib::load(const char* font_file, int _w, int _h)
{
FT_Library library;
if (FT_Init_FreeType(&library))
exit(0);
//加载一个字体,取默认的Face,一般为Regualer
if (FT_New_Face(library, font_file, 0, &m_FT_Face))
exit(0);
//选择字符表
FT_Select_Charmap(m_FT_Face, FT_ENCODING_UNICODE);
m_w = _w; m_h = _h;
m_FT_Face->num_fixed_sizes;
//大小要乘64.这是规定。照做就可以了。
//FT_Set_Char_Size( m_FT_Face , 0 , m_w << 6, 96, 96);
//用来存放指定字符宽度和高度的特定数据
FT_Set_Pixel_Sizes(m_FT_Face, m_w, m_h);
}

GLuint xFreeTypeLib::loadChar(wchar_t ch)
{
if (g_TexID[ch].m_texID)
return g_TexID[ch].m_texID;
/* 装载字形图像到字形槽(将会抹掉先前的字形图像) */
if (FT_Load_Char(m_FT_Face, ch, /*FT_LOAD_RENDER|*/FT_LOAD_FORCE_AUTOHINT |
(TRUE ? FT_LOAD_TARGET_NORMAL : FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO)))
{
return 0;
}

/*if(FT_Load_Glyph( m_FT_Face, FT_Get_Char_Index( m_FT_Face, ch ), FT_LOAD_FORCE_AUTOHINT ))
throw std::runtime_error("FT_Load_Glyph failed");*/

xCharTexture& charTex = g_TexID[ch];

//得到字模
FT_Glyph glyph;
//把字形图像从字形槽复制到新的FT_Glyph对象glyph中。这个函数返回一个错误码并且设置glyph。
if (FT_Get_Glyph(m_FT_Face->glyph, &glyph))
return 0;

//转化成位图
FT_Render_Glyph(m_FT_Face->glyph, FT_RENDER_MODE_LCD);//FT_RENDER_MODE_NORMAL );
FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1);
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

//取道位图数据
FT_Bitmap& bitmap = bitmap_glyph->bitmap;

//把位图数据拷贝自己定义的数据区里.这样旧可以画到需要的东西上面了。
int width = bitmap.width;
int height = bitmap.rows;

m_FT_Face->size->metrics.y_ppem; //伸缩距离到设备空间
m_FT_Face->glyph->metrics.horiAdvance; //水平文本排列


charTex.m_Width = width;
charTex.m_Height = height;
charTex.m_adv_x = m_FT_Face->glyph->advance.x / 64.0f; //步进宽度
charTex.m_adv_y = m_FT_Face->size->metrics.y_ppem; //m_FT_Face->glyph->metrics.horiBearingY / 64.0f;
charTex.m_delta_x = (float)bitmap_glyph->left; //left:字形原点(0,0)到字形位图最左边象素的水平距离.它以整数象素的形式表示。
charTex.m_delta_y = (float)bitmap_glyph->top - height; //Top: 类似于字形槽的bitmap_top字段。
glGenTextures(1, &charTex.m_texID);
glBindTexture(GL_TEXTURE_2D, charTex.m_texID);
char* pBuf = new char[width * height * 4];
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
unsigned char _vl = (i >= bitmap.width || j >= bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j];
pBuf[(4 * i + (height - j - 1) * width * 4)] = 0xFF;
pBuf[(4 * i + (height - j - 1) * width * 4) + 1] = 0xFF;
pBuf[(4 * i + (height - j - 1) * width * 4) + 2] = 0xFF;
pBuf[(4 * i + (height - j - 1) * width * 4) + 3] = _vl;
}
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBuf); //指定一个二维的纹理图片
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); //glTexParameteri():纹理过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE); //纹理进行混合

/*gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pBuf);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexEnvi(GL_TEXTURE_2D,GL_TEXTURE_ENV_MODE,GL_REPLACE);*/
delete[] pBuf;
return charTex.m_chaID;
}

main.cpp

// SmokeSimulate.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
// 包含有关OpenGL函数的头文件
//#include "GL/GL.H"
//#include "GL/GLU.h"
//#include "GL/GLAUX.H"
//#include "GL/glut.h"
//#include <iostream>

//#include <iostream>
//#include <Windows.h>
#include "tools.h"
#include "Grids.h"
#include "3dmap.h"
#include "xFreeTypeLib.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

#define SMOKEX 32
#define SMOKEY 32

typedef struct {
float r;
float g;
float b;
} COL;

/
bool RenderScene();
int InitGL(GLvoid);
void Fuoco(void);
void ShowSmoke(float x, float y, float z, float dim);
COL Colore(float k);

bool freeze;
int frame;
GLuint Texture[1];
unsigned char Bsmoke[SMOKEX][SMOKEY];

int xFar = 0.0f, yFar = 0.0f, zFar = 0.0f;
int wWidth = 1366, wHeight = 768;
int oldX, oldY;
bool gIsStartTrackBall = false;
bool gIsMoveMap = false;
TrackBall trackball;
_3dMap map;

xFreeTypeLib g_FreeTypeLib;
float ratio;
//wchar_t g_UnicodeString[]=L"aaabb/x4E2D/x6587/x0031/x0032/x0033";
const char g_UnicodeString[] = "0 1 2 3 4 5 6 时间(天)\" ";
const char g_UnicodeStringScript[] = "-10 0 10 20 30 40 50 \" ";
const char g_UnicodeStringScriptHz[] = "0 1000 2000 3000 4000 5000 频率(MHz)\" ";
const char g_UnicodeStringScriptLevel[] = "0 1000 2000 3000 4000 5000 能量电平(Db)\" ";


extern stuxCharTexture g_TexID[65536];


LPWSTR AnsiToUnicode(LPCSTR lpcstr);
void drawText(wchar_t* _strText, int x, int y, int maxW, int h);

void displayEvent()
{
//glClearColor(0.0f, 0.0f, 0.1f, 0.5f);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//glMatrixMode(GL_MODELVIEW);
//glLoadIdentity();

//glPushMatrix();
//glTranslatef(-200.0f, 50.0f, -400.0f);
//glRotatef(-45.0f, 0.0f, 1.0f, 0.0f);
//glTranslatef(xFar, yFar, zFar);
//trackball.makeRolate();
//map.drawMap();

时间
//glTranslatef(0.0f, 0.0f, 390.0f);
//glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
//wchar_t *wstr = AnsiToUnicode(g_UnicodeString);
//drawText(wstr, 0, -10, 900, 25); //什么变成透明模式

频率
//glTranslatef(320.0f, 0.0f, 0.0f);
//glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
//wchar_t *wstrScriptHz = AnsiToUnicode(g_UnicodeStringScriptHz);
//drawText(wstrScriptHz, 10, 90, 900, 25); //什么变成透明模式

能量电平
频率
//glTranslatef(0.0f, -420.0f, 30.0f);
//glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
glRotatef(90.0f, 0.0f, 1.0f, 1.0f);
//wchar_t *wstrScriptLevel = AnsiToUnicode(g_UnicodeStringScriptLevel);
//drawText(wstrScriptLevel, 10, 90, 900, 25); //什么变成透明模式

//glPopMatrix();
//glEnable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_2D); //关闭字形纹理

//map.showColorStrip();

//glPushMatrix();
//glTranslatef(0.0f, 80.0f, 0.0f);
//glRotatef(-150.0f, 1.0f, 0.0f, 0.0f);
//wchar_t *wstrScript = AnsiToUnicode(g_UnicodeStringScript);
//drawText(wstrScript, 10, 90, 900, 25); //什么变成透明模式



//glPopMatrix();


//烟雾相关
RenderScene();


//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glLoadIdentity();
//frame++;
//frame = frame % 2;
//if (frame == 0)
//{
// if (freeze == false)
// Fuoco(); // 生成烟雾
//}
//glTranslatef(0.1f, 0.0f, -2.7f);
//ShowSmoke(0, 0, 0, (float)0.2); // 显示烟雾


glFlush();
glutSwapBuffers(); // 交换缓存
}

void mouseMoveEvent(int x, int y)
{
if (gIsStartTrackBall)
{
trackball.MouseMove(x, y);
//glutPostRedisplay();
}
if (gIsMoveMap)
{
xFar -= oldX - x;
yFar += oldY - y;
oldX = x;
oldY = y;
//glutPostRedisplay();
}
}
// 鼠标事件函数
void mouseEvent(int button, int state, int x, int y)
{
if (state == GLUT_UP && button == GLUT_WHEEL_UP) //鼠标滚轮
{
zFar -= 80;
glutPostRedisplay();
}
if (state == GLUT_UP && button == GLUT_WHEEL_DOWN)
{
zFar += 80;
glutPostRedisplay();
}


if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
oldX = x;
oldY = y;
trackball.setXY(x, y);
gIsStartTrackBall = true;
}
else if (state == GLUT_UP)
{
oldX = x;
oldY = y;
gIsStartTrackBall = false;
}
glutPostRedisplay();
}
else if (button == GLUT_RIGHT_BUTTON)
{
if (state == GLUT_DOWN)
{
oldX = x;
oldY = y;
gIsMoveMap = true;
}
else if (state == GLUT_UP)
{
oldX = x;
oldY = y;
gIsMoveMap = false;
}
}
}
// 窗体尺寸变化事件
void resizeEvent(int w, int h)
{
//wWidth = w;
//wHeight = h;
//zFar = 0.0f;
//xFar = 0.0f;
//yFar = 0.0f;
//glViewport(0, 0, w, h);
//glMatrixMode(GL_PROJECTION);
//glLoadIdentity();
//h = h > 0 ? h : 1;
//float aspect = (float)w / (float)h;
//gluPerspective(45, aspect, 0.001, 15000.0);
//glTranslatef(200, -190, -550.0f);
glRotatef(-90.0f, 0.0f, 0.0f, 1.0f);
//trackball.resize();



/
//添加窗口缩放时的图形变换函数
glViewport(0, 0, w, h);
/
glMatrixMode(GL_PROJECTION); // 选择投影矩阵
glLoadIdentity(); // 设置投影矩阵
// 根据窗口的比例设置变换
gluPerspective(45.0f, (GLfloat)w / (GLfloat)h, 0.1f, 100.0f);
glMatrixMode(GL_MODELVIEW); // 选择模型矩阵
glLoadIdentity(); // 设置模型矩阵

glutPostRedisplay();

}
void processSpecialKeys(int key, int x, int y) {
if (key == 101)
{
zFar += 4;
glutPostRedisplay();
}
if (key == 103)
{//
zFar -= 4;
glutPostRedisplay();
}
printf("key:%d\n", key);
}

void MenuFunc(int data)
{
switch (data)
{
case 1:
map.setLineOrFill(); break;
default:break;
}
glutPostRedisplay();
}
void glInit()
{
glShadeModel(GL_FLAT);//SMOOTH//GL_FLAT
glClearColor(1.0f, 1.0f, 1.0f, 0.5f);
glClearDepth(1.0f);

glEnable(GL_NORMALIZE);

glEnable(GL_DEPTH_TEST);
glAlphaFunc(GL_GREATER, 0);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

/*glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);*/

glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POLYGON_SMOOTH);

glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Make round points, not square points
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Antialias the lines
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);


//glClearColor(1.0f,1.0f,1.0f,0.5f); //窗口背景设置为白色
glMatrixMode(GL_MODELVIEW); //设置投影参数

glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

//g_FreeTypeLib.load("simhei.ttf",14,14);
// g_FreeTypeLib.load("c://windows//fonts//simhei.ttf",14,14);
g_FreeTypeLib.load("c://windows//fonts//simhei.ttf", 12, 12);

//烟雾
InitGL();

//glDisable(GL_CULL_FACE);

//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

}

xCharTexture* getTextChar(wchar_t ch)
{
g_FreeTypeLib.loadChar(ch);
return &g_TexID[ch];
}

LPWSTR AnsiToUnicode(LPCSTR lpcstr) //参数lpcstr类型也可是char*
{
LPWSTR Pwstr;
int i;
i = MultiByteToWideChar(CP_ACP, 0, lpcstr, -1, NULL, 0);
Pwstr = new WCHAR[i];
MultiByteToWideChar(CP_ACP, 0, lpcstr, -1, Pwstr, i);

return (Pwstr);
}




void drawText(wchar_t* _strText, int x, int y, int maxW, int h)
{
int sx = x;
int sy = y;
int maxH = h;
size_t nLen = wcslen(_strText);

for (int i = 0; i < nLen; i++)
{
if (_strText[i] == '/n')
{
sx = x; sy += maxH + 12;
continue;
}
xCharTexture* pCharTex = getTextChar(_strText[i]);
glBindTexture(GL_TEXTURE_2D, pCharTex->m_texID); //绑定到目标纹理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnable(GL_BLEND);
glDepthMask(GL_TRUE);//打开或关闭OpenGL的特殊功能
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //特殊的像素算法
//glBlendFunc(GL_ONE, GL_ZERO);
glEnable(GL_TEXTURE_2D); //去透明
int w = pCharTex->m_Width;
int h = pCharTex->m_Height;

int ch_x = sx + pCharTex->m_delta_x;
int ch_y = sy - h - pCharTex->m_delta_y;

if (maxH < h) maxH = h;
glBegin(GL_QUADS); // 定义一个或一组原始的顶点
{
glTexCoord2f(0.0f, 1.0f); glVertex3f(ch_x, ch_y, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(ch_x + w, ch_y, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(ch_x + w, ch_y + h, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(ch_x, ch_y + h, 1.0f);
}
glEnd();
sx += pCharTex->m_adv_x;
if (sx > x + maxW)
{
sx = x; sy += maxH + 12;
}
}
}


//
// 场景绘制与渲染
//
bool RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
frame++;
frame = frame % 2;
if (frame == 0)
{
if (freeze == false)
Fuoco(); // 生成烟雾
}
glTranslatef(0.1f, 0.0f, -2.7f);
ShowSmoke(0, 0, 0, (float)0.2); // 显示烟雾

//::SwapBuffers(m_pDC->GetSafeHdc()); // 交互缓冲区
//glFlush();
//glutSwapBuffers(); // 交换缓存

glutPostRedisplay();
return true;
}


COL Colore(float k)
{
COL c;
// if( k<64 )
// {
// c.r=k/64;
// c.g=k/64;
// c.b=k/64;
// }
// else
// {
if (k < 128)
{
c.r = k / 128;
c.g = k / 128;
c.b = k / 128;
}
else
{
if (k < 192)
{
c.r = k / 256;
c.g = k / 256;
c.b = k / 256;
}
else
{
c.r = 1;
c.g = 1;
c.b = 1;
}
}
// }
return(c);
}

void ShowSmoke(float x, float y, float z, float dim)
{
float xi, yi;
float ka, kb, kc, kd;
COL col;
int xd, yd;

yi = y + dim * SMOKEY / 2;

for (yd = 0; yd < SMOKEY - 1; yd++)
{
xi = x - dim * SMOKEX / 2;
for (xd = 1; xd < SMOKEX - 1; xd++)
{
ka = (float)Bsmoke[xd][yd];
kb = (float)Bsmoke[xd + 1][yd];
kc = (float)Bsmoke[xd + 1][yd + 1];
kd = (float)Bsmoke[xd][yd + 1];

glBegin(GL_QUADS); // 绘制四边形
col = Colore(kd);
glColor3f(col.r, col.g, col.b);
glVertex3f(xi, yi, z);
col = Colore(kc);
glColor3f(col.r, col.g, col.b);
glVertex3f(xi + dim, yi, z);
col = Colore(kb);
glColor3f(col.r, col.g, col.b);
glVertex3f(xi + dim, yi + dim, z);
col = Colore(ka);
glColor3f(col.r, col.g, col.b);
glVertex3f(xi, yi + dim, z);
glEnd();
xi += dim;
}
yi -= dim;
}
}

void Fuoco(void)
{
int x, y;
int k;

for (x = 8; x < SMOKEX - 8; x++)
Bsmoke[x][SMOKEY - 1] = rand() % 192;

for (x = 0; x < 5; x++)
Bsmoke[rand() % SMOKEX][SMOKEY - 1] = 0;

for (y = 0; y < SMOKEY - 1; y++)
{
for (x = 1; x < SMOKEX - 1; x++)
{
k = Bsmoke[x][y] + Bsmoke[x - 1][y + 1] + Bsmoke[x + 1][y + 1] + Bsmoke[x][y + 1];
k = k / 4 - 2;
if (k < 0)
k = 0;
Bsmoke[x][y] = (unsigned char)k;
}
}
}

int InitGL(GLvoid)
{
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

for (int y = 0; y < SMOKEY; y++)
{
for (int x = 0; x < SMOKEX; x++)
Bsmoke[x][y] = 0;
}

freeze = false;
frame = 0;
return TRUE;
}


int main(int argc, char* argv[])
{
//ANSI字符串,内容长度7字节
char sz[20] = "中文123";

//UNICODE字符串,内容长度5个wchar_t(10字节)
wchar_t wsz[100] = L"/x4E2D/x6587/x0031/x0032/x0033";
//运行时设定当前ANSI编码,VC格式
setlocale(LC_ALL, ".936");

//GCC中格式
setlocale(LC_ALL, "zh_CN.GBK");

//VisualC++中使用小写%s,按照setlocale指定编码输出到文件
//GCC中使用大写%S


//把UNICODE字符串按照setlocale指定的编码转换成字节
wcstombs(sz, wsz, 20);
//把字节串按照setlocale指定的编码转换成UNICODE字符串
mbstowcs(wsz, sz, 20);

map.initMap();

glutInit(&argc, argv); //初始化GLUT
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_MULTISAMPLE); //设置显示模式
glutInitWindowPosition(0, 0); //设置显示窗口的左上角位置
glutInitWindowSize(wWidth, wHeight); //设置窗口的长和高
glutCreateWindow("3DMap"); //创造显示窗口

glInit(); //开始初始化过程

int main_version, sub_version, release_version;
const char* version = (const char*)glGetString(GL_VERSION);
sscanf(version, "%d.%d.%d", &main_version, &sub_version, &release_version);
printf("OpenGL 版本:%s\n", version);
printf("主版本号:%d\n", main_version);
printf("次版本号:%d\n", sub_version);
printf("发行版本号:%d\n", release_version);


glutReshapeFunc(resizeEvent);
glutDisplayFunc(displayEvent);
glutMouseFunc(mouseEvent);
glutSpecialFunc(processSpecialKeys);
glutMotionFunc(mouseMoveEvent);
glutCreateMenu(MenuFunc);
glutAddMenuEntry("填充/网格", 1);
glutAttachMenu(GLUT_MIDDLE_BUTTON);

glutMainLoop(); //显示所有并等候

getchar();
return 0;
}

​工程源码下载地址​