C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

本人最近学习OpenGL编程,现尝试运用OpenGL编写贴纹理并可调整转速的‘流浪地球’,由于要显示出地球在流浪,所以除了要使地球自转外并加多绘制小星星来衬托太空环境。另外在屏幕上显示中文操作说明,这期间遇到的困难可要问DeepSeek这位资深大师了。[在此感慨一下DeepSeek的强劲,它真的让编程事半功倍!]

说明:1.本程序所需的SOIL2库文件,也可以通过VS2019本身的NuGet程序包下载安装,这个安装是挺方便的,见下图示;

2.本程序成功编译出的.exe程序还需要依赖本程序编译出来的‘freeglut.dll’这个文件;

3.由于加入显示中文操作(可要将全部的中文字库导入内存中),这导致打开程序运行初始时进出现较长时间的空白屏幕(此时是缓冲数据、像是无反应),这启动时间大约需要20秒;如使用英文则直接可打开程序而不需要缓冲时间;

4.本程序成功在win7/10系统里的VS2019社区版本上运行通过。

NuGet程序包下载安装:

C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

先来看本程序运行结果的GIF动图:

C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

本程序代码如下:

#include <GL/glut.h>

#include <SOIL2/SOIL2.h>

#include <Windows.h> //创建自己图标需要此头件

#include <cmath>

#include <cstdlib>

#include <ctime>

#include <iostream>

#include <string>

constexpr float PI= 3.1415926f;//constexpr定义常量

// 地球旋转角度和速度

float earthRotationAngle = 0.0f;

float rotationSpeed = 0.23f; // 默认地球旋转速度,数值越小,转速越慢

float earthRotationX = 0.0f;

float earthRotationY = 0.0f;

float lastX = 0.0f, lastY = 0.0f;

bool mousePressed = false;

float rotationVelocityX = 0.0f;

float rotationVelocityY = 0.0f;

float damping = 0.95f; // 阻尼系数

// 星星结构体

struct Star {

float x, y, z; // 位置

float r, g, b; // 颜色

float intensity; // 亮度

float speed; // 闪烁速度

};

const int NUM_STARS = 620;

Star stars[NUM_STARS];

// 纹理ID

GLuint earthTexture;

GLuint textureID2;//用于加载说明图片

int imgWidth, imgHeight; // 用于存储说明图片尺寸

// 初始化星星

void initStars() {

/*

srand(time(NULL));

for (int i = 0; i < NUM_STARS; ++i) {

// 随机位置(在单位球面上)

float theta = 2.0f * PI * (rand() / (float)RAND_MAX);//rand()函数提示类型不对出错!

float phi = acos(2.0f * (rand() / (float)RAND_MAX – 1.0f));

float radius = 5.0f + 10.0f * (rand() / (float)RAND_MAX); // 5-15单位距离

stars[i].x = radius * sin(phi) * cos(theta);

stars[i].y = radius * sin(phi) * sin(theta);

stars[i].z = radius * cos(phi);

*/

srand(static_cast<unsigned>(time(NULL))); // 随机位置(在单位球面上)

for (int i = 0; i < NUM_STARS; ++i) {

// 使用浮点常量确保类型正确

float theta = 2.0f * static_cast<float>(PI) * (rand() / static_cast<float>(RAND_MAX));

float phi = acosf(2.0f * (rand() / static_cast<float>(RAND_MAX)) – 1.0f);

float radius = 5.0f + 10.0f * (rand() / static_cast<float>(RAND_MAX));

stars[i].x = radius * sinf(phi) * cosf(theta);

stars[i].y = radius * sinf(phi) * sinf(theta);

stars[i].z = radius * cosf(phi);

// 随机颜色(偏白色)

float color = 0.7f + 0.3f * (rand() / (float)RAND_MAX);

stars[i].r = color;

stars[i].g = color;

stars[i].b = color;

// 随机闪烁参数

stars[i].intensity = 0.3f + 0.7f * (rand() / (float)RAND_MAX);

stars[i].speed = 0.1f + 0.5f * (rand() / (float)RAND_MAX);

}

}

// 加载纹理

GLuint loadTexture(const char* filename) {

GLuint textureID = SOIL_load_OGL_texture(

filename,

SOIL_LOAD_AUTO,

SOIL_CREATE_NEW_ID,

SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT

);

if (textureID == 0) {

printf(“SOIL加载错误: ‘%s’
“, SOIL_last_result());

exit(EXIT_FAILURE);

}

glBindTexture(GL_TEXTURE_2D, textureID);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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);

return textureID;

}

// 绘制地球

void drawEarth() {

glPushMatrix();

// 应用鼠标旋转

glRotatef(earthRotationX, 1.0f, 0.0f, 0.0f);

glRotatef(earthRotationY, 0.0f, 1.0f, 0.0f);

// 地球旋转

glRotatef(earthRotationAngle, 0.0f, 1.0f, 0.0f);

// 启用纹理

glEnable(GL_TEXTURE_2D);

glBindTexture(GL_TEXTURE_2D, earthTexture);

// 设置材质属性

GLfloat matAmb[] = { 0.2f, 0.2f, 0.2f, 1.0f };

GLfloat matDiff[] = { 0.8f, 0.8f, 0.8f, 1.0f };

GLfloat matSpec[] = { 0.5f, 0.5f, 0.5f, 1.0f };

GLfloat matShine = 30.0f;

glMaterialfv(GL_FRONT, GL_AMBIENT, matAmb);

glMaterialfv(GL_FRONT, GL_DIFFUSE, matDiff);

glMaterialfv(GL_FRONT, GL_SPECULAR, matSpec);

glMaterialf(GL_FRONT, GL_SHININESS, matShine);

// 绘制球体

GLUquadricObj* quadric = gluNewQuadric();

gluQuadricTexture(quadric, GL_TRUE);

gluSphere(quadric, 1.8, 64, 64);//设置地球大小,原来设置(quadric, 1.0, 64, 64);

gluDeleteQuadric(quadric);

glDisable(GL_TEXTURE_2D);

glPopMatrix();

}

// 绘制星星

void drawStars() {

glDisable(GL_LIGHTING); //设置禁用光照效果,即星星不受光照影响

glBegin(GL_POINTS);

for (int i = 0; i < NUM_STARS; ++i) {

// 更新星星亮度(模拟闪烁)

stars[i].intensity += stars[i].speed * 0.01f;

if (stars[i].intensity > 1.0f || stars[i].intensity < 0.3f) {

stars[i].speed = -stars[i].speed;

}

// 设置颜色和大小

glColor3f(stars[i].r * stars[i].intensity,

stars[i].g * stars[i].intensity,

stars[i].b * stars[i].intensity);

//glEnable(GL_PROGRAM_POINT_SIZE);//需要开启修改点大小的功能,此函数要需要头文件<glad/glad.h> ,或者GLEW的 <GLFW/glfw3.h,但这又与<GL/glut.h>头文件发生冲突

//glPointSize(1.0f + 2.0f * stars[i].intensity); //在着色器程序中,可能需要启用glEnable(GL_PROGRAM_POINT_SIZE)才能使用程序控制点大小,无开启导致不行!

//glPointSize(25.0f); 与 glBegin(GL_POINTS);组合设置也不行!

// 绘制星星

glVertex3f(stars[i].x, stars[i].y, stars[i].z);

}

glEnd();

glEnable(GL_LIGHTING);//设置启用光照效果

}

// 窗口调整大小函数

void reshape(int w, int h) {

glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluPerspective(45.0, (double)w / (double)h, 0.1, 100.0);

}

// 空闲函数(用于动画)

void idle() {

earthRotationAngle += rotationSpeed;

if (earthRotationAngle > 360.0f) {

earthRotationAngle -= 360.0f;

}

if (!mousePressed && (fabs(rotationVelocityX) > 0.1f || fabs(rotationVelocityY) > 0.1f)) {

earthRotationX += rotationVelocityX;

earthRotationY += rotationVelocityY;

rotationVelocityX *= damping;

rotationVelocityY *= damping;

glutPostRedisplay();

}

glutPostRedisplay();

}

// 键盘控制函数

void keyboard(unsigned char key, int x, int y) {

switch (key) {

case ‘+’: // 加快旋转速度

rotationSpeed += 0.1f;

break;

case ‘-‘: // 减慢旋转速度

rotationSpeed -= 0.1f;

if (rotationSpeed < 0.0f) rotationSpeed = 0.0f;

break;

case ‘0’: // 按‘0’停止旋转

rotationSpeed = 0.0f;

break;

case 27: // ESC键退出

exit(0);

break;

}

}

void mouseButton(int button, int state, int x, int y) {

if (button == GLUT_LEFT_BUTTON) {

if (state == GLUT_DOWN) {

mousePressed = true;

lastX = x;

lastY = y;

}

else {

mousePressed = false;

}

}

}

// 修改mouseMove函数

void mouseMove(int x, int y) {

if (mousePressed) {

// 计算鼠标移动距离

float dx = x – lastX;

float dy = y – lastY;

// 更新旋转角度

earthRotationY += dx * 0.5f; // 绕Y轴旋转

earthRotationX += dy * 0.5f; // 绕X轴旋转

// 限制X轴旋转角度在-90到90度之间

if (earthRotationX > 90.0f) earthRotationX = 90.0f;

if (earthRotationX < -90.0f) earthRotationX = -90.0f;

lastX = x;

lastY = y;

glutPostRedisplay(); // 请求重绘

}

}

//—–以下为显示中文字部分—–

// 初始化中文字体

GLuint fontBase = 0;// 存储显示字体列表基址

void initFont() {

HDC hdc = wglGetCurrentDC();

LOGFONTW lf;

memset(&lf, 0, sizeof(LOGFONTW));

lf.lfHeight = -20; //使用字符高度,即定义字体大小(带符号值表明准确匹配)

lf.lfCharSet = GB2312_CHARSET;

wcscpy_s(lf.lfFaceName, LF_FACESIZE, L”宋体”);//使用微软雅黑,并可直接改变字体,如改为:微软雅黑、楷体等

HFONT hFont = CreateFontIndirectW(&lf);

HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);

// 生成ASCII及常用汉字对应的显示列表(示例范围)

fontBase = glGenLists(65535); // 覆盖所有Unicode基本多语言平面字符

wglUseFontBitmapsW(hdc, 0, 65535, fontBase);

SelectObject(hdc, hOldFont);

DeleteObject(hFont);

}

// 绘制宽字符字符串

void drawWString(const wchar_t* str) {

glPushMatrix();

glListBase(fontBase); // 设置显示列表基址

glCallLists(wcslen(str), GL_UNSIGNED_SHORT, str);

glPopMatrix();

}

// 多字节转宽字符

std::wstring multiByteToWide(const std::string& str) {

int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);

std::wstring wstr(len – 1, 0);

MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wstr[0], len);

return wstr;

}

//创建在屏幕上显示文字的函数

void renderText(float x, float y, const std::string& text) {

//glDisable(GL_LIGHTING); // 设置禁用光照效果,即文字不受光照影响

glMatrixMode(GL_PROJECTION);//设置当前矩阵模式:投影矩阵模式。

glPushMatrix();//用于将当前矩阵复制并压入矩阵堆栈中。这意味着在调用glPushMatrix之后,所有的矩阵操作都会基于新的复制矩阵进行,而不会影响之前的矩阵状态。

glLoadIdentity();//将当前矩阵重置为单位矩阵,这样可以确保后续的变换操作是在一个统一的状态下进行,避免不同帧之间的相互影响‌

gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT));//用来指定屏幕区域对应的模型坐标范围,自己所绘制的图形的坐标必须在这个范围内,不然看不到绘制的图形。

glMatrixMode(GL_MODELVIEW);//设置当前矩阵模式:模型视景矩阵模式。

glLoadIdentity();

glColor3f(1.0, 1.0, 1.0); //设置文字颜色为白色;设置为其它颜色如(1.0, 0.0, 1.0)品红色是不起作用的

glRasterPos2f(x, y);//设置文字输出位置,这’y’位置是从屏幕底部起计算的

drawWString(multiByteToWide(text).c_str());

/*

for (const char& c : text) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, c);//设置文字大小,- 18点Helvetica字体

}

*/

//===下面四行代码是设置:恢复之前保存的投影矩阵和模型视图矩阵状态。这种代码常见于临时修改矩阵状态后需要还原的场景===

glPopMatrix();

glMatrixMode(GL_PROJECTION);

glPopMatrix();

glMatrixMode(GL_MODELVIEW);//是这样设置后才能显示文字,但经这样设置后,文字的颜色只能是默认的白色了(由于其它矩阵默认颜色是白色)

//glEnable(GL_LIGHTING);//设置启用光照效果

}

//—–以上为编写显示中文文字部分—

// 显示函数

void display() {

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

// 设置相机位置

gluLookAt(0.0, 0.0, 5.0, // 相机位置

0.0, 0.0, 0.0, // 观察点

0.0, 1.0, 0.0); // 上向量

// 绘制场景

drawStars();

drawEarth();

// 显示文字

glColor3f(1.0, 1.0, 1.0); //设置文字颜色为白色;设置为其它颜色如(1.0, 0.0, 1.0)品红色是不起作用的

// renderText(10, 80, “Tips3: You can Press the ‘Esc’ key to Exit.”);

// renderText(10, 60, “Tips2: You can use the ‘+/-‘ keys on the keyboard to adjust the rotation speed /”);

// renderText(10, 40, ” / and Press the 0 key to stop rotating.”);

// renderText(10, 20, “Tips1: You can control the rotation angle with the mouse: drag the cube freely to change the rotation angle.”);

renderText(10, 25, “3.可用鼠标控制旋转角度和方向:任意拖动球体改变旋转角度和方向- -3个轴向方向.”);

renderText(10, 52, “2.旋转速度调整:用键盘的’ + / -‘键调整速度、按’0’键停止旋转.”);

renderText(10, 80, “1.按‘Esc’键可退出.”);

renderText(10, 105, “提示:”);

glDisable(GL_TEXTURE_2D);

glutSwapBuffers();// 对于双缓冲模式很重大

}

// 初始化函数

void init() {

glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // 黑色背景

glEnable(GL_DEPTH_TEST);

// 启用光照(用于地球)

glEnable(GL_LIGHTING);

glEnable(GL_LIGHT0);

// 设置光源

GLfloat lightPos[] = { 4.0f, 5.0f, 3.0f, 1.5f };//{ 5.0f, 5.0f, 5.0f, 1.0f };

GLfloat lightAmb[] = { 0.2f, 0.2f, 0.2f, 1.0f };

GLfloat lightDiff[] = { 0.8f, 0.8f, 0.8f, 1.0f };

GLfloat lightSpec[] = { 1.0f, 1.0f, 1.0f, 1.0f };

glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);

glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiff);

glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpec);

// 加载地球纹理

earthTexture = loadTexture(“Earth.jpg”);

// 初始化星星

initStars();

initFont(); // 初始化字体

}

// 主函数

int main(int argc, char** argv) {

// 隐藏控制台窗口(仅适用于 Windows)

HWND hwnd = GetConsoleWindow();

ShowWindow(hwnd, SW_HIDE); // SW_HIDE 隐藏,SW_SHOW 显示

glutInit(&argc, argv);

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);// 双缓冲模式以支持平滑动画。

glutInitWindowSize(1300, 820);//创建自己的窗口

glutCreateWindow(“流浪地球模拟”);

//printf(“OpenGL Version: %s
“, glGetString(GL_VERSION));//获取 OpenGL 版本,自己版本为3.1版本

init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMouseFunc(mouseButton);

glutMotionFunc(mouseMove);

glutIdleFunc(idle);

glutKeyboardFunc(keyboard);

glutMainLoop();

return 0;

}

运行结果图:

C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

附贴图图片:

C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

C++ OpenGL编写贴纹理并可调整转速的‘流浪地球’

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容