自己是這方面的小白,希望大神可以對有問題以及可以優化的地方提出來。也歡迎指出不足和吐槽。希望幫到小白。
opengl 太陽、地球、月亮 酷炫實例(一):
https://blog.csdn.net/qq_40515692/article/details/100778870
自己參考得比較多的網站是這個:
http://www.cppblog.com/doing5552/archive/2009/01/08/71532.html
以及:https://blog.csdn.net/xie_zi/article/details/1911406
所有代碼都可以去github免費下載:
https://github.com/Iamttp/OpenGLTest
好先上這一節的效果圖。這一節是上一節的繼續,在上一節的代碼基礎上添加了場景漫遊,可以用鍵盤控制“相機”了。
首先是添加場景漫遊相關的基礎函數,我們先假設初始化相機位於(0,0,3)的位置,相機鏡頭對着(0,0,-1)方向,那麼就是相當於你的眼睛是相機,顯示屏在原點,你和顯示屏的距離爲3(好吧,並不直觀 / w \)。
gluLookAt函數提供了一個簡單直觀的方法來設置照相機的位置和方向。它有三組參數,每一組由三個浮點型數組成。前三個參數表明照相機的位置,第二組參數定義照相機觀察的方向,最後一組表明向上的向量,這個通常設爲(0.0,1.0,0.0)。也就是說照相機並沒有傾斜。如你想看到所有的物體都是倒置的則可以設置爲(0.0,-1.0,0.0)。
那麼我們定義orientMe表示根據傳入的角度對lx,lz進行更改,即給定和y軸的角度,求出方向向量。
定義moveMeFlat表示根據給定的距離對當前x,z進行更改,即給定距離,然後和當前方向相乘,然後更新x,z。
static float angle = 0.0, ratio; // angle繞y軸的旋轉角,ratio窗口高寬比
static float x = 0.0f, y = 0.0f, z = 3.0f; //相機位置
static float lx = 0.0f, ly = 0.0f, lz = -1.0f; //視線方向,初始設爲沿着Z軸負方向
void orientMe(float ang) {
lx = sin(ang);
lz = -cos(ang);
glLoadIdentity();
gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}
void moveMeFlat(int direction) {
x = x + direction * (lx) * 0.1;
z = z + direction * (lz) * 0.1;
glLoadIdentity();
gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}
然後是按鍵控制,這個比較簡單,在主函數裏面加入。
glutSpecialFunc(&processSpecialKeys); // 按鍵
然後定義按鍵函數即可,左右方向鍵,將照相機繞y軸旋轉,上下方向鍵,將前後方向移動照相機。調用上面提到的函數。
void processSpecialKeys(int key, int x, int y) {
switch (key) {
case GLUT_KEY_LEFT:
angle -= 0.01f;
orientMe(angle);
break;
case GLUT_KEY_RIGHT:
angle += 0.01f;
orientMe(angle);
break;
case GLUT_KEY_UP:
moveMeFlat(1);
break;
case GLUT_KEY_DOWN:
moveMeFlat(-1);
break;
}
}
最後一步我們添加 glutReshapeFunc,設置觀察方式。具體的代碼我就直接放在最終代碼那裏了。最終代碼爲:
#include <gl/glut.h>
#include <stdio.h>
#include <time.h>
#include <cmath>
static float angle = 0.0, ratio; // angle繞y軸的旋轉角,ratio窗口高寬比
static float x = 0.0f, y = 0.0f, z = 3.0f; //相機位置
static float lx = 0.0f, ly = 0.0f, lz = -1.0f; //視線方向,初始設爲沿着Z軸負方向
static int my_angle = 0; // 表示旋轉的角度
/**
* 定義觀察方式
*/
void changeSize(int w, int h) {
//除以0的情況
if (h == 0) h = 1;
ratio = 1.0f * w / h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//設置視口爲整個窗口大小
glViewport(0, 0, w, h);
//設置可視空間
gluPerspective(45, ratio, 1, 1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}
/**
* 視野漫遊函數
*/
void orientMe(float ang) {
lx = sin(ang);
lz = -cos(ang);
glLoadIdentity();
gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}
void moveMeFlat(int direction) {
x = x + direction * (lx) * 0.1;
z = z + direction * (lz) * 0.1;
glLoadIdentity();
gluLookAt(x, y, z, x + lx, y + ly, z + lz, 0.0f, 1.0f, 0.0f);
}
/**
* 加入按鍵控制
*/
void processSpecialKeys(int key, int x, int y) {
switch (key) {
case GLUT_KEY_LEFT:
angle -= 0.01f;
orientMe(angle);
break;
case GLUT_KEY_RIGHT:
angle += 0.01f;
orientMe(angle);
break;
case GLUT_KEY_UP:
moveMeFlat(1);
break;
case GLUT_KEY_DOWN:
moveMeFlat(-1);
break;
}
}
void myDisplay(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 太陽
glPushMatrix();
glColor3f(1.0, 1.0, 0.0);
glutSolidSphere(0.15, 200, 200);
glPopMatrix();
// 地球
glPushMatrix();
glColor3f(0.0, 0.0, 1.0);
glRotated(my_angle, 1.0, 1.0, 1.0); //公轉
glTranslatef(0.5, 0.5, -0.5); //平移
glutSolidSphere(0.1, 200, 200);
glPopMatrix();
// 月亮
glPushMatrix();
glColor3f(1.0, 1.0, 1.0);
glRotated(my_angle, 1.0, 1.0, 1.0); //然後移動到地球旁邊旋轉
glTranslatef(0.5, 0.5, -0.5); //平移
glRotated(my_angle, 1.0, 1.0, 1.0); //先假設原點爲地球旋轉
glTranslatef(-0.15, -0.15, 0.15); //平移
glutSolidSphere(0.05, 200, 200); //繪製月亮
glPopMatrix();
glutSwapBuffers();
}
/**
* 計時增加角度
*/
void myIdle(void) {
static int mm = 0;
mm++;
if (mm % 300000 == 0) {
++my_angle;
if (my_angle >= 360) my_angle = 0;
myDisplay();
}
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(1000, 1000);
glutCreateWindow("太陽,地球和月亮"); // 改了窗口標題
glutDisplayFunc(&myDisplay);
glutIdleFunc(&myIdle); // 表示在CPU空閒的時間調用某一函數
glutSpecialFunc(&processSpecialKeys); // 按鍵
glutReshapeFunc(&changeSize);
// 在OpenGL中,默認是沒有開啓深度檢測的,後繪製的物體覆蓋先繪製的物體。
// GL_DEPTH_TEST 用來開啓更新深度緩衝區的功能
glEnable(GL_DEPTH_TEST);
glutMainLoop();
return 0;
}