思路:用一个个的四边形来近似球面,每一个四边形仅占一小块立体角。将平铺的世界地图与地球仪做如下对应--图片的左上角对应球体的theta = 0, phi = 0位置。向下则theta增加,直到180度;向右则phi增加,直到360度。theta和phi都按照10度步进。步进一次得到一个立体角,立体角的四个顶点分别位于(theta, phi), (theta + 10, phi) (theta, phi + 10) (theta + 10, phi + 10).选取这四个点构成的四边形来近似这个部分球面。这个四边形在世界地图中对应的矩形就是要贴到这个四边形上的纹理。只要把一块块的纹理与四边形对应贴好,就完成了球面绘制。
pro文件:
QT += core gui opengl
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = glEarth3
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
LIBS += -lopengl32 -lGLU32
h文件:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
class MainWindow : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
GLuint m_uiTexID;
GLfloat * m_pVertices;
GLfloat * m_pTextures;
int m_iVerticalPntQuantity;
int m_iHorizontalPntQuantity;
unsigned char * pLoadTex(char * Image, unsigned long & bWidth, unsigned long & bHeight);
void vInitVerticesTextures(int iVerticalAngleStep, int iHorizontalAngleStep, int iR,
GLfloat * & pVertData, GLfloat * & pTexData,
int & iVerticalPntQuantity, int & iHorizontalPntQuantity);
protected:
void initializeGL();
void paintGL();
void resizeGL(int, int);
};
#endif // MAINWINDOW_H
cpp文件:
#include "mainwindow.h"
#include <QDir>
#include <gl/GLU.H>
#if !defined(DEG2RAD)
#define DEG2RAD (3.1415926 / 180)
#endif
MainWindow::MainWindow(QWidget *parent)
: QOpenGLWidget(parent)
{
}
MainWindow::~MainWindow()
{
glDeleteTextures(1, &m_uiTexID);
delete [] m_pTextures;
delete [] m_pVertices;
}
void MainWindow::initializeGL()
{
initializeOpenGLFunctions();
unsigned long bWidth = 0;
unsigned long bHeight = 0;
QString qstrPath = QDir::currentPath();
qstrPath += "/earth.bmp";
unsigned char * pData = pLoadTex(qstrPath.toLatin1().data(), bWidth, bHeight);
glGenTextures(1, &m_uiTexID);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_uiTexID);
//这下面的两句是必要的,否则在绘制区的像素数和纹理的像素数不等时,图案不能正常显示
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, bWidth, bHeight,
GL_BGR_EXT, GL_UNSIGNED_BYTE, pData);
glDisable(GL_TEXTURE_2D);
free(pData);
vInitVerticesTextures(10, 10, 40,
m_pVertices, m_pTextures, m_iVerticalPntQuantity, m_iHorizontalPntQuantity);
glEnable(GL_DEPTH_TEST);
//GL_FRON
}
void MainWindow::paintGL()
{
glBindTexture(GL_TEXTURE_2D, m_uiTexID);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, m_pTextures);
glVertexPointer(3, GL_FLOAT, 0, m_pVertices);
glDrawArrays(GL_TRIANGLES, 0, m_iHorizontalPntQuantity * m_iVerticalPntQuantity * 6);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
void MainWindow::resizeGL(int width, int height)
{
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-50.0, 50.0, -50.0, 50.0, -50.0, 50.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(60,0,0,0,0,0,0,0,1);
}
void MainWindow::vInitVerticesTextures(int iVerticalAngleStep, int iHorizontalAngleStep, int iR,
GLfloat * & pVertData, GLfloat * & pTexData,
int & iVerticalPntQuantity, int & iHorizontalPntQuantity)
{
iVerticalPntQuantity = 180 / iVerticalAngleStep;
iHorizontalPntQuantity = 360 / iHorizontalAngleStep;
pVertData = new GLfloat[iVerticalPntQuantity * iHorizontalPntQuantity * 18];
pTexData = new GLfloat[iVerticalPntQuantity * iHorizontalPntQuantity * 12];
int iVertexNodeCount = 0, iTexNodeCount = 0;
for(int vAngle = 0; vAngle < 180; vAngle = vAngle + iVerticalAngleStep)// 垂直方向angleSpan度一份
{
for(int hAngle = 0; hAngle < 360; hAngle = hAngle + iHorizontalAngleStep)// 水平方向angleSpan度一份
{
// 纵向横向各到一个角度后计算对应的此点在球面上的坐标
float x0 = (float) (iR * sin(vAngle * DEG2RAD) * cos(hAngle * DEG2RAD));
float y0 = (float) (iR * sin(vAngle * DEG2RAD) * sin(hAngle * DEG2RAD));
float z0 = (float) (iR * cos(vAngle * DEG2RAD));
// Log.w("x0 y0 z0","" + x0 + " "+y0+ " " +z0);
float x1 = (float) (iR * sin(vAngle * DEG2RAD) * cos((hAngle + iHorizontalAngleStep) * DEG2RAD));
float y1 = (float) (iR * sin(vAngle * DEG2RAD) * sin((hAngle + iHorizontalAngleStep) * DEG2RAD));
float z1 = (float) (iR * cos(vAngle * DEG2RAD));
float x2 = (float) (iR * sin((vAngle + iVerticalAngleStep) * DEG2RAD) *
cos((hAngle + iHorizontalAngleStep) * DEG2RAD));
float y2 = (float) (iR * sin((vAngle + iVerticalAngleStep) * DEG2RAD) *
sin((hAngle + iHorizontalAngleStep) * DEG2RAD));
float z2 = (float) (iR * cos((vAngle + iVerticalAngleStep) * DEG2RAD));
// Log.w("x2 y2 z2","" + x2 + " "+y2+ " " +z2);
float x3 = (float) (iR * sin((vAngle + iVerticalAngleStep) * DEG2RAD) * cos(hAngle * DEG2RAD));
float y3 = (float) (iR * sin((vAngle + iVerticalAngleStep) * DEG2RAD) * sin(hAngle * DEG2RAD));
float z3 = (float) (iR * cos((vAngle + iVerticalAngleStep) * DEG2RAD));
// Log.w("x3 y3 z3","" + x3 + " "+y3+ " " +z3);
// 将计算出来的XYZ坐标加入存放顶点坐标的ArrayList
pVertData[iVertexNodeCount++] = x1;
//alVertix.add(x1);
pVertData[iVertexNodeCount++] = y1;
//alVertix.add(y1);
pVertData[iVertexNodeCount++] = z1;
//alVertix.add(z1);
pVertData[iVertexNodeCount++] = x3;
//alVertix.add(x3);
pVertData[iVertexNodeCount++] = y3;
//alVertix.add(y3);
pVertData[iVertexNodeCount++] = z3;
//alVertix.add(z3);
pVertData[iVertexNodeCount++] = x0;
//alVertix.add(x0);
pVertData[iVertexNodeCount++] = y0;
//alVertix.add(y0);
pVertData[iVertexNodeCount++] = z0;
//alVertix.add(z0);
// *****************************************************************
float s0 = hAngle / 360.0f;
float s1 = (hAngle + iHorizontalAngleStep)/360.0f ;
float t0 = 1 - vAngle / 180.0f;
float t1 = 1 - (vAngle + iVerticalAngleStep) / 180.0f;
/*t0 = t0 - 0.1;
t1 = t1 - 0.1;
if(t0 < 0)
t0 = t0 + 1;
if(t1 < 0)
t1 = t1 + 1;*/
pTexData[iTexNodeCount++] = s1;
//textureVertix.add(s1);// x1 y1对应纹理坐标
pTexData[iTexNodeCount++] = t0;
//textureVertix.add(t0);
pTexData[iTexNodeCount++] = s0;
//textureVertix.add(s0);// x3 y3对应纹理坐标
pTexData[iTexNodeCount++] = t1;
//textureVertix.add(t1);
pTexData[iTexNodeCount++] = s0;
//textureVertix.add(s0);// x0 y0对应纹理坐标
pTexData[iTexNodeCount++] = t0;
//textureVertix.add(t0);
// *****************************************************************
pVertData[iVertexNodeCount++] = x1;
//alVertix.add(x1);
pVertData[iVertexNodeCount++] = y1;
//alVertix.add(y1);
pVertData[iVertexNodeCount++] = z1;
//alVertix.add(z1);
pVertData[iVertexNodeCount++] = x2;
//alVertix.add(x2);
pVertData[iVertexNodeCount++] = y2;
//alVertix.add(y2);
pVertData[iVertexNodeCount++] = z2;
//alVertix.add(z2);
pVertData[iVertexNodeCount++] = x3;
//alVertix.add(x3);
pVertData[iVertexNodeCount++] = y3;
//alVertix.add(y3);
pVertData[iVertexNodeCount++] = z3;
//alVertix.add(z3);
// *****************************************************************
pTexData[iTexNodeCount++] = s1;
//textureVertix.add(s1);// x1 y1对应纹理坐标
pTexData[iTexNodeCount++] = t0;
//textureVertix.add(t0);
pTexData[iTexNodeCount++] = s1;
//textureVertix.add(s1);// x2 y3对应纹理坐标
pTexData[iTexNodeCount++] = t1;
//textureVertix.add(t1);
pTexData[iTexNodeCount++] = s0;
//textureVertix.add(s0);// x3 y3对应纹理坐标
pTexData[iTexNodeCount++] = t1;
//textureVertix.add(t1);
// *****************************************************************
}
}
//setWindowTitle("123");
}
unsigned char * MainWindow::pLoadTex(char * Image, unsigned long & bWidth, unsigned long & bHeight)
{
FILE* img = NULL;
img = fopen(Image, "rb");
DWORD size = 0;
fseek(img,18,SEEK_SET);
fread(&bWidth,4,1,img);
fread(&bHeight,4,1,img);
fseek(img,0,SEEK_END);
size = ftell(img) - 54;
unsigned char *data = (unsigned char*)malloc(size);
fseek(img,54,SEEK_SET); // image data
fread(data,size,1,img);
fclose(img);
return data;
}
假如程序员想要从另外的角度观察,只要更改gluLookAt函数的观察角度即可。
效果: