天天看點

手把手制作太陽系運作模型,紋理貼圖、模型變換、動畫、計算機圖形學(OpenGL、C++實作、超級詳細)

這裡寫目錄标題

    • 一、基本原理
    • 二、代碼(完整版本)
      • 2.1 頭檔案
      • 2.2 mian函數
      • 2.3 OnReshape防止變形函數
      • 2.4 Init初始化函數
      • 2.5 OnTimer函數
      • 2.6 DrawCircle畫軌道函數
      • 2.7 gltDrawSphere繪制星球函數
      • 2.8 OnDisplay函數
    • 三、注意事項
手把手制作太陽系運作模型,紋理貼圖、模型變換、動畫、計算機圖形學(OpenGL、C++實作、超級詳細)

一、基本原理

  • 使用封裝的gltDrawSphere繪制星球,DrawCircle繪制軌道
  • 在封裝的Init函數中開啟燈光,OnDisplay函數中繪制時候寫好光照參數
  • 在封裝的Init函數中使用auxDIBImageLoad函數讀取bmp檔案的寬、高與data
  • 在封裝的OnDisplay函數中使用glPushMatrix與glPopMatrix依次繪制每一個星球
  • 使用glutTimerFun函數實作動畫

二、代碼(完整版本)

2.1 頭檔案

ifndef _HERDER_H_
#define _HERDER_H_
#include <GL/glut.h>
#include <GL/GLAux.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
AUX_RGBImageRec* Images[10];   
GLuint ImagesIDs[11];  //索引
char* szFiles[10] = {
	("F:\\OGL\\sun\\Sun.bmp"),
	("F:\\OGL\\sun\\Mercury.bmp"),
	("F:\\OGL\\sun\\Venus.bmp"),
	("F:\\OGL\\sun\\Earth.bmp"),
	("F:\\OGL\\sun\\Mars.bmp"),
	("F:\\OGL\\sun\\Jupiter.bmp"),
	("F:\\OGL\\sun\\Saturn.bmp"),
	("F:\\OGL\\sun\\Uranus.bmp"),
	("F:\\OGL\\sun\\Neptune.bmp"),
	("F:\\OGL\\sun\\Moon.bmp"),};
GLubyte* pImg;
GLint iWidth, iHeight;

#define PI 3.1415926
static float  year = 0, month = 0, day = 0, angle = 30;

GLint W, H, width, height;
float pox = 2, poy = 3, poz = 8;  //照相機的位置
GLint fovy = 60;

void Init();
void OnDisplay();
void OnReshape(int, int);
void OnTimer(int);
void DrawCircle(GLdouble);
void gltDrawSphere(GLfloat, GLint, GLint);

#endif
           

2.2 mian函數

int main(int argc, char* argv[]){
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(400, 400);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("太陽系");
	Init();
	glutReshapeFunc(OnReshape);
	glutDisplayFunc(&OnDisplay);
	glutTimerFunc(100, OnTimer, 1);
	glutMainLoop();
	return 0;
}

           

2.3 OnReshape防止變形函數

void OnReshape(int w, int h){
	W = w; H = h;
	width = W; height = H;
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1, 20);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();}

           

2.4 Init初始化函數

void Init(){
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	for (int i = 0; i < 10; i++){
		glGenTextures(1, &ImagesIDs[i]);  //生成紋理
		glBindTexture(GL_TEXTURE_2D, ImagesIDs[i]);   //綁定紋理

		//vs2019下必須進行轉化
		WCHAR wfilename[256];
		memset(wfilename, 0, sizeof(wfilename));
		//該函數映射一個字元串到一個寬字元(unicode)的字元串
		MultiByteToWideChar(CP_ACP, 0, szFiles[i], strlen(szFiles[i]) + 1, wfilename, sizeof(wfilename) / sizeof(wfilename[0]));
	
		Images[i] = auxDIBImageLoad(wfilename);  //加載圖檔

		iWidth = Images[i]->sizeX;
		iHeight = Images[i]->sizeY;
		pImg = Images[i]->data;
//裝載紋理
		glTexImage2D(GL_TEXTURE_2D, 0, 3, iWidth, iHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pImg);

		//紋理過濾、紋理裁剪
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

		//紋理環境
		glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);

		//啟動紋理
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_DEPTH_TEST);
	}
	glEnable(GL_LIGHTING);                          // 開啟光照效果
	glEnable(GL_LIGHT0);
}
           

2.5 OnTimer函數

void OnTimer(int value){
	day += angle;
	glutPostRedisplay();
	glutTimerFunc(100, OnTimer, 1);
}
           

2.6 DrawCircle畫軌道函數

//軌道函數
void DrawCircle(GLdouble R){
	glPushMatrix();
	glRotatef(90.0, 1.0, 0.0, 0.0);
	glColor3f(1.0, 1.0, 1.0);
	glBegin(GL_LINE_LOOP);
	for (int i = 0; i < 1000; i++){
		GLdouble angle1 = i * 2 * PI / 1000;
		glVertex2d(R * cos(angle1), R * sin(angle1));
	}
	glEnd();
	glPopMatrix();
}
           

2.7 gltDrawSphere繪制星球函數

//繪制星球
void gltDrawSphere(GLfloat fRadius, GLint iSlices, GLint iStacks){
	GLfloat drho = (GLfloat)(3.141592653589) / (GLfloat)iStacks;
	GLfloat dtheta = 2.0f * (GLfloat)(3.141592653589) / (GLfloat)iSlices;
	GLfloat ds = 1.0f / (GLfloat)iSlices;
	GLfloat dt = 1.0f / (GLfloat)iStacks;
	GLfloat t = 1.0f;
	GLfloat s = 0.0f;
	GLint i, j;

	for (i = 0; i < iStacks; i++){
		GLfloat rho = (GLfloat)i * drho;
		GLfloat srho = (GLfloat)(sin(rho));
		GLfloat crho = (GLfloat)(cos(rho));
		GLfloat srhodrho = (GLfloat)(sin(rho + drho));
		GLfloat crhodrho = (GLfloat)(cos(rho + drho));

		glBegin(GL_TRIANGLE_STRIP);
		s = 0.0f;
	}
for (j = 0; j <= iSlices; j++){
			GLfloat theta = (j == iSlices) ? 0.0f : j * dtheta;
			GLfloat stheta = (GLfloat)(-sin(theta));
			GLfloat ctheta = (GLfloat)(cos(theta));

			GLfloat x = stheta * srho;
			GLfloat y = ctheta * srho;
			GLfloat z = crho;

			glTexCoord2f(s, t);
			glNormal3f(x, y, z);
			glVertex3f(x * fRadius, y * fRadius, z * fRadius);

			x = stheta * srhodrho;
			y = ctheta * srhodrho;
			z = crhodrho;
			glTexCoord2f(s, t - dt);
			s += ds;
			glNormal3f(x, y, z);
			glVertex3f(x * fRadius, y * fRadius, z * fRadius);
		}
		glEnd();

		t -= dt;
	}
           

2.8 OnDisplay函數

void OnDisplay(){
	
	glColor3f(1.0, 0.0, 0.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	year = day / 365;
	month = day / 30;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(fovy, (GLfloat)W / (GLfloat)H, 2, 60.0);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(pox, poy, poz, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 

	//太陽光
	GLfloat sun_mat_ambient[4] = { 1.0,1.0,1.0,0.0 };
	GLfloat sun_mat_diffuse[4] = { 1.0,1.0,0.5,1.0 };
	GLfloat sun_mat_specular[4 = { 1.0,1.0,1.0,1.0 };
	GLfloat sun_mat_shininess[] = { 10.0 };
	GLfloat sun_mat_emission[4]= { 0.1,0.1,0.1,1.0 };
	GLfloat mat_ambient[4] = { 0.2,0.2,0.2,1.0 };
	GLfloat mat_diffuse[4] = { 1.0,1.0,1.0,1.0 };
	GLfloat mat_specular[4] = { 0.5,0.5,0.5,1.0 };
	GLfloat mat_shininess[] = { 5.0 };
	GLfloat light_position[]={ -10.0,10.0,0.0, 1.0};//光源位置
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);     // 建立光源
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, sun_mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, sun_mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sun_mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, sun_mat_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, sun_mat_emission);

	//繪制太陽
	glPushMatrix();
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);            // 建立光源
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, sun_mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, sun_mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sun_mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, sun_mat_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, sun_mat_emission);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[0]);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0);  //太陽自轉
	glRotatef(90.0, -1.0, 0.0, 0.0);
	gltDrawSphere(1.0, 50, 50);
	glPopMatrix();

	//繪制水星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(2.0);//繪制水星的軌道
	glRotatef((GLfloat)month / 2, 0.0, 1.0, 0.0);  //水星圍繞太陽轉
	glTranslatef(2, 0.0, 0.0);  //向x軸右移,讓其在軌道上
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[1]);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0);
	glRotatef(90.0, -1.0, 0.0, 0.0);//自轉
	gltDrawSphere(0.2, 10, 10);
	glPopMatrix();

	//繪制金星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(3.0);
	glRotatef((GLfloat)month / 3, 0.0, 1.0, 0.0);  //金星圍繞太陽轉
	glTranslatef(3, 0.0, 0.0);  //向x軸右移
	glRotatef(30.0, -1.0, 0.0, 0.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[2]);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0);
	glRotatef(90.0, -1.0, 0.0, 0.0);
	gltDrawSphere(0.25, 10, 10);
	glPopMatrix();

	//繪制地月系統
	glPushMatrix();
	//繪制地球
	DrawCircle(4.5);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	glRotatef((GLfloat)year, 0.0, 1.0, 0.0);  //地球公轉
	glTranslatef(4.5, 0.0, 0.0);  //向x軸右移
	glRotatef(30.0, -1.0, 0.0, 0.0);//讓地球傾斜
	glPushMatrix();
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[3]);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0);
	glRotatef(90.0, -1.0, 0.0, 0.0);
	gltDrawSphere(0.4, 20, 20);
	glPopMatrix();
	
	//繪制月亮
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(1.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[9]);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0); //月球繞地球轉
	glTranslatef(1.0, 0.0, 0.0);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0);
	glRotatef(90.0, -1.0, 0.0, 0.0);
	gltDrawSphere(0.15, 10, 10);
	glPopMatrix();
	glPopMatrix();

	//繪制火星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(6.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[4]);
	glRotatef((GLfloat)month / 1.5, 0.0, 1.0, 0.0);
	glTranslatef(6.0, 0.0, 0.0);
	glRotatef(30, -1, 0, 0);
	glRotatef((GLfloat)month, 0.0, 1.0, 0.0);
	glRotatef(90, -1, 0, 0);
	gltDrawSphere(0.3, 10, 10);
	glPopMatrix();

	//繪制木星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(7.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[5]);
	glRotatef((GLfloat)month / 4, 0.0, 1.0, 0.0);
	glTranslatef(7.0, 0.0, 0.0);
	glRotatef((GLfloat)month / 2, 0.0, 1.0, 0.0);
	glRotatef(90, -1, 0, 0);
	gltDrawSphere(0.42, 10, 10);
	glPopMatrix();

	//繪制土星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(8.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[6]);
	glRotatef((GLfloat)month / 3.5, 0.0, 1.0, 0.0);
	glTranslatef(8.0, 0.0, 0.0);
	glRotatef((GLfloat)month / 2, 0.0, 1.0, 0.0);
	glRotatef(90, -1, 0, 0);
	gltDrawSphere(0.35, 10, 10);
	glPopMatrix();

	//繪制天王星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(9.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[7]);
	glRotatef((GLfloat)month / 3, 0.0, 1.0, 0.0);
	glTranslatef(9.0, 0.0, 0.0);
	glRotatef(30, -1, 0, 0);
	glRotatef((GLfloat)month / 2, 0.0, 1.0, 0.0);
	glRotatef(90, -1, 0, 0);
	gltDrawSphere(0.3, 10, 10);
	glPopMatrix();

	//繪制海王星
	glPushMatrix();
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);   // 材質設定
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	DrawCircle(10.0);
	glBindTexture(GL_TEXTURE_2D, ImagesIDs[8]);
	glRotatef((GLfloat)month / 2, 0.0, 1.0, 0.0);
	glTranslatef(10.0, 0.0, 0.0);
	glRotatef(30, -1, 0, 0);
	glRotatef((GLfloat)month / 2, 0.0, 1.0, 0.0);
	glRotatef(90, -1, 0, 0);
	gltDrawSphere(0.25, 10, 10);
	glPopMatrix();

	glutSwapBuffers();
}

           

三、注意事項

  • 除了頭檔案,其他都是依次放到cpp檔案中就可以
  • 代碼使用vs2019,其他版本的可能會報錯,直接百度錯誤就可以很快的解決
  • .glutXXXX的模型都是很簡陋的,沒有紋理UV坐标的模型,使用glutSolidSphere函數是無法進行貼圖。是以寫了一個gltDrawSphere函數繪制星球
  • 我使用的是vs2019,在使用加載的紋理圖檔之前,要把路徑字元串轉化為寬字元字元串,這一點可以檢視上面glutSolidSphere函數代碼。其他版本可能不需要轉化,去掉就可以。
  • 求個三連!不明白的可以評論或者私信我~