OpenGL 蓝书学习(第四版)第五章
OpenGL 蓝书学习(第四版)第五章
总共22章等着我学习🥹
在OpenGL中使用颜色。
画一个黑色到白色渐变线
// mac 特有的头文件
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <cmath>
#include <iostream>
#include <numbers>
// 可视坐标大小
static constexpr GLfloat COORDINATE_SIZE = 120.0F;
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
// 4x4 矩阵乘法: result = a * b(列主序,兼容 OpenGL)
void multiplyMatrix4(const GLfloat a[16], const GLfloat b[16],
GLfloat result[16]) {
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
result[col * 4 + row] = a[0 * 4 + row] * b[col * 4 + 0] +
a[1 * 4 + row] * b[col * 4 + 1] +
a[2 * 4 + row] * b[col * 4 + 2] +
a[3 * 4 + row] * b[col * 4 + 3];
}
}
}
void renderScene() {
std::cout << std::format("renderScene: xRot={}, yRot={}, zRot={}", xRot, yRot, zRot) << "\n";
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// --- 手动构建变换矩阵,替代 glTranslatef / glRotatef ---
const GLfloat radX = xRot * PI / 180.0F;
const GLfloat radY = yRot * PI / 180.0F;
const GLfloat radZ = zRot * PI / 180.0F;
const GLfloat cx = cosf(radX), sx = sinf(radX);
const GLfloat cy = cosf(radY), sy = sinf(radY);
const GLfloat cz = cosf(radZ), sz = sinf(radZ);
// 平移矩阵 T(列主序)
const GLfloat trans[16] = {
1.0F, 0.0F, 0.0F, 0.0F, // col 0
0.0F, 1.0F, 0.0F, 0.0F, // col 1
0.0F, 0.0F, 1.0F, 0.0F, // col 2
0.0F, 0.0F, -400.0F, 1.0F // col 3 (tx, ty, tz, 1)
};
// 绕 X 轴旋转矩阵(列主序)
const GLfloat rotX[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, cx, sx, 0.0F,
0.0F, -sx, cx, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Y 轴旋转矩阵(列主序)
const GLfloat rotY[16] = {cy, 0.0F, -sy, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
sy, 0.0F, cy, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Z 轴旋转矩阵(列主序)
const GLfloat rotZ[16] = {cz, sz, 0.0F, 0.0F, -sz, cz, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 组合: T * Rx * Ry * Rz
GLfloat temp1[16], temp2[16], finalMatrix[16];
multiplyMatrix4(rotY, rotZ, temp1); // temp1 = Ry * Rz
multiplyMatrix4(rotX, temp1, temp2); // temp2 = Rx * Ry * Rz
multiplyMatrix4(trans, temp2, finalMatrix); // final = T * Rx * Ry * Rz
glMultMatrixf(finalMatrix);
// 绘制立方体
glPointSize(50.0F);
glBegin(GL_POINTS);
glColor3ub(255, 255, 255);
glVertex3f(0.0F, 0.0F, 0.0F);
glEnd();
glLineWidth(10.0F);
glBegin(GL_LINES);
glColor3ub(0, 0, 0);
glVertex3f(0.0F, 0.0F, 0.0F);
glColor3ub(255, 255, 255);
glVertex3f(255.0F, 255.F, 255.0F);
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void setupRc() {
// 设置清除颜色为黑色
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
glEnable(GL_DEPTH_TEST); // 启用深度测试
glShadeModel(GL_SMOOTH);
}
void changeSize(const GLsizei w, const GLsizei h) {
std::cout << std::format("changeSize: w={}, h={}", w, h) << "\n";
// 防止除以 0
if (h == 0) {
throw std::runtime_error("h == 0");
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // 投影
glLoadIdentity();
const GLfloat aspectRatio =
static_cast<GLfloat>(w) / static_cast<GLfloat>(h);
gluPerspective(60.0F, aspectRatio, 1.0F, 500.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y) {
std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y)
<< "\n";
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
break;
}
// Refresh the Window
glutPostRedisplay();
}
auto main(const int argc, const char **const argv) -> int {
std::cout << "hello world\n";
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << "\n";
}
// 初始化 glut
glutInit(const_cast<int *>(&argc), const_cast<char **>(argv));
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// 设置窗口大小
glutInitWindowSize(800, 600);
// 创建窗口
const int result = glutCreateWindow("Hello World");
std::cout << "result: " << result << "\n";
if (result == 0) {
throw std::runtime_error("glutCreateWindow failed");
}
// 设置显示回调函数
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// 注册 键盘事件 回调
glutSpecialFunc(specialKeys); // 特殊键事件
glutKeyboardFunc(keyboard); // 普通键事件
setupRc();
glutMainLoop();
return 0;
}
画一个三角形
// mac 特有的头文件
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <cmath>
#include <iostream>
#include <numbers>
// 可视坐标大小
static constexpr GLfloat COORDINATE_SIZE = 120.0F;
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
// 4x4 矩阵乘法: result = a * b(列主序,兼容 OpenGL)
void multiplyMatrix4(const GLfloat a[16], const GLfloat b[16],
GLfloat result[16]) {
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
result[col * 4 + row] = a[0 * 4 + row] * b[col * 4 + 0] +
a[1 * 4 + row] * b[col * 4 + 1] +
a[2 * 4 + row] * b[col * 4 + 2] +
a[3 * 4 + row] * b[col * 4 + 3];
}
}
}
void renderScene() {
std::cout << std::format("renderScene: xRot={}, yRot={}, zRot={}", xRot, yRot, zRot) << "\n";
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// --- 手动构建变换矩阵,替代 glTranslatef / glRotatef ---
const GLfloat radX = xRot * PI / 180.0F;
const GLfloat radY = yRot * PI / 180.0F;
const GLfloat radZ = zRot * PI / 180.0F;
const GLfloat cx = cosf(radX), sx = sinf(radX);
const GLfloat cy = cosf(radY), sy = sinf(radY);
const GLfloat cz = cosf(radZ), sz = sinf(radZ);
// 平移矩阵 T(列主序)
const GLfloat trans[16] = {
1.0F, 0.0F, 0.0F, 0.0F, // col 0
0.0F, 1.0F, 0.0F, 0.0F, // col 1
0.0F, 0.0F, 1.0F, 0.0F, // col 2
0.0F, 0.0F, -300.0F, 1.0F // col 3 (tx, ty, tz, 1)
};
// 绕 X 轴旋转矩阵(列主序)
const GLfloat rotX[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, cx, sx, 0.0F,
0.0F, -sx, cx, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Y 轴旋转矩阵(列主序)
const GLfloat rotY[16] = {cy, 0.0F, -sy, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
sy, 0.0F, cy, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Z 轴旋转矩阵(列主序)
const GLfloat rotZ[16] = {cz, sz, 0.0F, 0.0F, -sz, cz, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 组合: T * Rx * Ry * Rz
GLfloat temp1[16], temp2[16], finalMatrix[16];
multiplyMatrix4(rotY, rotZ, temp1); // temp1 = Ry * Rz
multiplyMatrix4(rotX, temp1, temp2); // temp2 = Rx * Ry * Rz
multiplyMatrix4(trans, temp2, finalMatrix); // final = T * Rx * Ry * Rz
glMultMatrixf(finalMatrix);
// 绘制普通三角形
glBegin(GL_TRIANGLES);
glColor3ub(255, 0, 0);
glVertex3f(0.0F, 100.F, 0.0F);
glColor3ub(0, 255, 0);
glVertex3f(-200.0F * tanf(30.0F * PI / 180.0F), -100.0F, 0.0F);
glColor3ub(0, 0, 255);
glVertex3f(200.0F * tanf(30.0F * PI / 180.0F), -100.0F, 0.0F);
glEnd();
// 绘制立方体 颜色立方体
glBegin(GL_TRIANGLES);
glColor3ub(255, 0, 0);
glVertex3f(255.0F, 0.F, 0.0F);
glColor3ub(0, 255, 0);
glVertex3f(0.0F, 255.0F, 0.0F);
glColor3ub(0, 0, 255);
glVertex3f(0.0F, 0.0F, 255.0F);
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void setupRc() {
// 设置清除颜色为黑色
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
glEnable(GL_DEPTH_TEST); // 启用深度测试
glShadeModel(GL_SMOOTH);
}
void changeSize(const GLsizei w, const GLsizei h) {
std::cout << std::format("changeSize: w={}, h={}", w, h) << "\n";
// 防止除以 0
if (h == 0) {
throw std::runtime_error("h == 0");
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // 投影
glLoadIdentity();
const GLfloat aspectRatio =
static_cast<GLfloat>(w) / static_cast<GLfloat>(h);
gluPerspective(60.0F, aspectRatio, 1.0F, 500.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y) {
std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y)
<< "\n";
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
break;
}
// Refresh the Window
glutPostRedisplay();
}
auto main(const int argc, const char **const argv) -> int {
std::cout << "hello world\n";
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << "\n";
}
// 初始化 glut
glutInit(const_cast<int *>(&argc), const_cast<char **>(argv));
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// 设置窗口大小
glutInitWindowSize(800, 600);
// 创建窗口
const int result = glutCreateWindow("Hello World");
std::cout << "result: " << result << "\n";
if (result == 0) {
throw std::runtime_error("glutCreateWindow failed");
}
// 设置显示回调函数
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// 注册 键盘事件 回调
glutSpecialFunc(specialKeys); // 特殊键事件
glutKeyboardFunc(keyboard); // 普通键事件
setupRc();
glutMainLoop();
return 0;
}
画一个颜色立方体
// mac 特有的头文件
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <cmath>
#include <iostream>
#include <numbers>
// 可视坐标大小
static constexpr GLfloat COORDINATE_SIZE = 120.0F;
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
// 4x4 矩阵乘法: result = a * b(列主序,兼容 OpenGL)
void multiplyMatrix4(const GLfloat a[16], const GLfloat b[16],
GLfloat result[16]) {
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
result[col * 4 + row] = a[0 * 4 + row] * b[col * 4 + 0] +
a[1 * 4 + row] * b[col * 4 + 1] +
a[2 * 4 + row] * b[col * 4 + 2] +
a[3 * 4 + row] * b[col * 4 + 3];
}
}
}
void renderScene() {
std::cout << std::format("renderScene: xRot={}, yRot={}, zRot={}", xRot, yRot, zRot) << "\n";
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// --- 手动构建变换矩阵,替代 glTranslatef / glRotatef ---
const GLfloat radX = xRot * PI / 180.0F;
const GLfloat radY = yRot * PI / 180.0F;
const GLfloat radZ = zRot * PI / 180.0F;
const GLfloat cx = cosf(radX), sx = sinf(radX);
const GLfloat cy = cosf(radY), sy = sinf(radY);
const GLfloat cz = cosf(radZ), sz = sinf(radZ);
// 平移矩阵 T(列主序)
const GLfloat trans[16] = {
1.0F, 0.0F, 0.0F, 0.0F, // col 0
0.0F, 1.0F, 0.0F, 0.0F, // col 1
0.0F, 0.0F, 1.0F, 0.0F, // col 2
0.0F, 0.0F, -400.0F, 1.0F // col 3 (tx, ty, tz, 1)
};
// 绕 X 轴旋转矩阵(列主序)
const GLfloat rotX[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, cx, sx, 0.0F,
0.0F, -sx, cx, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Y 轴旋转矩阵(列主序)
const GLfloat rotY[16] = {cy, 0.0F, -sy, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
sy, 0.0F, cy, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Z 轴旋转矩阵(列主序)
const GLfloat rotZ[16] = {cz, sz, 0.0F, 0.0F, -sz, cz, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 组合: T * Rx * Ry * Rz
GLfloat temp1[16], temp2[16], finalMatrix[16];
multiplyMatrix4(rotY, rotZ, temp1); // temp1 = Ry * Rz
multiplyMatrix4(rotX, temp1, temp2); // temp2 = Rx * Ry * Rz
multiplyMatrix4(trans, temp2, finalMatrix); // final = T * Rx * Ry * Rz
glMultMatrixf(finalMatrix);
// 绘制立方体
glBegin(GL_QUADS);
// 前
glColor3ub(255, 255, 255);
glVertex3f(100.0F, 100.0F, 100.0F);
glColor3ub(0, 255, 255);
glVertex3f(-100.0F, 100.0F, 100.0F);
glColor3ub(0, 0, 255);
glVertex3f(-100.0F, -100.0F, 100.0F);
glColor3ub(255, 0, 255);
glVertex3f(100.0F, -100.0F, 100.0F);
// 后
glColor3ub(255, 255, 0);
glVertex3f(100.0F, 100.0F, -100.0F);
glColor3ub(0, 255, 0);
glVertex3f(-100.0F, 100.0F, -100.0F);
glColor3ub(0, 0, 0);
glVertex3f(-100.0F, -100.0F, -100.0F);
glColor3ub(255, 0, 0);
glVertex3f(100.0F, -100.0F, -100.0F);
// 顶面 (y = +100)
glColor3ub(255, 255, 255);
glVertex3f(100.0F, 100.0F, 100.0F);
glColor3ub(255, 255, 0);
glVertex3f(100.0F, 100.0F, -100.0F);
glColor3ub(0, 255, 0);
glVertex3f(-100.0F, 100.0F, -100.0F);
glColor3ub(0, 255, 255);
glVertex3f(-100.0F, 100.0F, 100.0F);
// 底面 (y = -100)
glColor3ub(255, 0, 255);
glVertex3f(100.0F, -100.0F, 100.0F);
glColor3ub(0, 0, 255);
glVertex3f(-100.0F, -100.0F, 100.0F);
glColor3ub(0, 0, 0);
glVertex3f(-100.0F, -100.0F, -100.0F);
glColor3ub(255, 0, 0);
glVertex3f(100.0F, -100.0F, -100.0F);
// 右面 (x = +100)
glColor3ub(255, 255, 255);
glVertex3f(100.0F, 100.0F, 100.0F);
glColor3ub(255, 0, 255);
glVertex3f(100.0F, -100.0F, 100.0F);
glColor3ub(255, 0, 0);
glVertex3f(100.0F, -100.0F, -100.0F);
glColor3ub(255, 255, 0);
glVertex3f(100.0F, 100.0F, -100.0F);
// 左面 (x = -100)
glColor3ub(0, 255, 255);
glVertex3f(-100.0F, 100.0F, 100.0F);
glColor3ub(0, 255, 0);
glVertex3f(-100.0F, 100.0F, -100.0F);
glColor3ub(0, 0, 0);
glVertex3f(-100.0F, -100.0F, -100.0F);
glColor3ub(0, 0, 255);
glVertex3f(-100.0F, -100.0F, 100.0F);
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void setupRc() {
// 设置清除颜色为黑色
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
glEnable(GL_DEPTH_TEST); // 启用深度测试
glShadeModel(GL_SMOOTH);
}
void changeSize(const GLsizei w, const GLsizei h) {
std::cout << std::format("changeSize: w={}, h={}", w, h) << "\n";
// 防止除以 0
if (h == 0) {
throw std::runtime_error("h == 0");
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // 投影
glLoadIdentity();
const GLfloat aspectRatio =
static_cast<GLfloat>(w) / static_cast<GLfloat>(h);
gluPerspective(60.0F, aspectRatio, 1.0F, 500.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y) {
std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y)
<< "\n";
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
break;
}
// Refresh the Window
glutPostRedisplay();
}
auto main(const int argc, const char **const argv) -> int {
std::cout << "hello world\n";
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << "\n";
}
// 初始化 glut
glutInit(const_cast<int *>(&argc), const_cast<char **>(argv));
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// 设置窗口大小
glutInitWindowSize(800, 600);
// 创建窗口
const int result = glutCreateWindow("Hello World");
std::cout << "result: " << result << "\n";
if (result == 0) {
throw std::runtime_error("glutCreateWindow failed");
}
// 设置显示回调函数
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// 注册 键盘事件 回调
glutSpecialFunc(specialKeys); // 特殊键事件
glutKeyboardFunc(keyboard); // 普通键事件
setupRc();
glutMainLoop();
return 0;
}
画一个飞机
// mac 特有的头文件
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <cmath>
#include <iostream>
#include <numbers>
// 可视坐标大小
static constexpr GLfloat COORDINATE_SIZE = 120.0F;
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
// 4x4 矩阵乘法: result = a * b(列主序,兼容 OpenGL)
void multiplyMatrix4(const GLfloat a[16], const GLfloat b[16],
GLfloat result[16]) {
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
result[col * 4 + row] = a[0 * 4 + row] * b[col * 4 + 0] +
a[1 * 4 + row] * b[col * 4 + 1] +
a[2 * 4 + row] * b[col * 4 + 2] +
a[3 * 4 + row] * b[col * 4 + 3];
}
}
}
void renderScene() {
std::cout << std::format("renderScene: xRot={}, yRot={}, zRot={}", xRot, yRot, zRot) << "\n";
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// --- 手动构建变换矩阵,替代 glTranslatef / glRotatef ---
const GLfloat radX = xRot * PI / 180.0F;
const GLfloat radY = yRot * PI / 180.0F;
const GLfloat radZ = zRot * PI / 180.0F;
const GLfloat cx = cosf(radX), sx = sinf(radX);
const GLfloat cy = cosf(radY), sy = sinf(radY);
const GLfloat cz = cosf(radZ), sz = sinf(radZ);
// 平移矩阵 T(列主序)
const GLfloat trans[16] = {
1.0F, 0.0F, 0.0F, 0.0F, // col 0
0.0F, 1.0F, 0.0F, 0.0F, // col 1
0.0F, 0.0F, 1.0F, 0.0F, // col 2
0.0F, 0.0F, -100.0F, 1.0F // col 3 (tx, ty, tz, 1)
};
// 绕 X 轴旋转矩阵(列主序)
const GLfloat rotX[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, cx, sx, 0.0F,
0.0F, -sx, cx, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Y 轴旋转矩阵(列主序)
const GLfloat rotY[16] = {cy, 0.0F, -sy, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
sy, 0.0F, cy, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Z 轴旋转矩阵(列主序)
const GLfloat rotZ[16] = {cz, sz, 0.0F, 0.0F, -sz, cz, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 组合: T * Rx * Ry * Rz
GLfloat temp1[16], temp2[16], finalMatrix[16];
multiplyMatrix4(rotY, rotZ, temp1); // temp1 = Ry * Rz
multiplyMatrix4(rotX, temp1, temp2); // temp2 = Rx * Ry * Rz
multiplyMatrix4(trans, temp2, finalMatrix); // final = T * Rx * Ry * Rz
glMultMatrixf(finalMatrix);
// 绘制 喷气式飞机
glBegin(GL_TRIANGLES);
// 飞机头
glColor3ub(255, 255, 255);
glVertex3f(0.0F, 0.0F, 60.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glColor3ub(0, 0, 0);
glVertex3f(15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
glColor3ub(255, 0, 0);
glVertex3f(0.0F, 0.0F, 60.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
// 飞机体
glColor3ub(0, 255, 0);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
glColor3ub(255, 255, 0);
glVertex3f(0.0F, 0.0F, -56.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glColor3ub(0, 255, 255);
glVertex3f(15.0F, 0.0F, 30.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
// 机翼
glColor3ub(128, 128, 128);
glVertex3f(0.0F, 2.0F, 27.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
glColor3ub(64, 64, 64);
glVertex3f(60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
glColor3ub(192, 192, 192);
glVertex3f(60.0F, 2.0F, -8.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glColor3ub(64, 64, 64);
glVertex3f(0.0F, 2.0F, 27.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
// tail
glColor3ub(255, 128, 255);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
glColor3ub(255, 128, 0);
glVertex3f(0.0F, -0.5F, -40.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
glColor3ub(255, 128, 0);
glVertex3f(0.0F, 4.0F, -57.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
glColor3ub(255, 255, 255);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
glColor3ub(255, 0, 0);
glVertex3f(0.0F, 0.5F, -40.0F);
glVertex3f(3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glColor3ub(255, 0, 0);
glVertex3f(0.0F, 25.0F, -65.0F);
glVertex3f(-3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 0.5F, -40.0F);
glColor3ub(128, 128, 128);
glVertex3f(3.0F, 0.5F, -57.0F);
glVertex3f(-3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void setupRc() {
// 设置清除颜色为黑色
glClearColor(0.23F, 0.23F, 0.34F, 1.0F);
glEnable(GL_DEPTH_TEST); // 启用深度测试
glShadeModel(GL_SMOOTH);
}
void changeSize(const GLsizei w, const GLsizei h) {
std::cout << std::format("changeSize: w={}, h={}", w, h) << "\n";
// 防止除以 0
if (h == 0) {
throw std::runtime_error("h == 0");
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // 投影
glLoadIdentity();
const GLfloat aspectRatio =
static_cast<GLfloat>(w) / static_cast<GLfloat>(h);
gluPerspective(60.0F, aspectRatio, 1.0F, 500.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y) {
std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y)
<< "\n";
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
break;
}
// Refresh the Window
glutPostRedisplay();
}
auto main(const int argc, const char **const argv) -> int {
std::cout << "hello world\n";
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << "\n";
}
// 初始化 glut
glutInit(const_cast<int *>(&argc), const_cast<char **>(argv));
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// 设置窗口大小
glutInitWindowSize(800, 600);
// 创建窗口
const int result = glutCreateWindow("Hello World");
std::cout << "result: " << result << "\n";
if (result == 0) {
throw std::runtime_error("glutCreateWindow failed");
}
// 设置显示回调函数
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// 注册 键盘事件 回调
glutSpecialFunc(specialKeys); // 特殊键事件
glutKeyboardFunc(keyboard); // 普通键事件
setupRc();
glutMainLoop();
return 0;
}
画一个有亮度的飞机
// mac 特有的头文件
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <cmath>
#include <iostream>
#include <numbers>
// 可视坐标大小
static constexpr GLfloat COORDINATE_SIZE = 120.0F;
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
// 4x4 矩阵乘法: result = a * b(列主序,兼容 OpenGL)
void multiplyMatrix4(const GLfloat a[16], const GLfloat b[16],
GLfloat result[16]) {
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
result[col * 4 + row] = a[0 * 4 + row] * b[col * 4 + 0] +
a[1 * 4 + row] * b[col * 4 + 1] +
a[2 * 4 + row] * b[col * 4 + 2] +
a[3 * 4 + row] * b[col * 4 + 3];
}
}
}
// 用三角形三个顶点求面法线(右手定则叉乘 + 归一化),并设为当前法线。
// 顶点顺序需与 glVertex3f 一致:CCW 正面时法线指向观察者。
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
void renderScene() {
std::cout << std::format("renderScene: xRot={}, yRot={}, zRot={}", xRot, yRot, zRot) << "\n";
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// --- 手动构建变换矩阵,替代 glTranslatef / glRotatef ---
const GLfloat radX = xRot * PI / 180.0F;
const GLfloat radY = yRot * PI / 180.0F;
const GLfloat radZ = zRot * PI / 180.0F;
const GLfloat cx = cosf(radX), sx = sinf(radX);
const GLfloat cy = cosf(radY), sy = sinf(radY);
const GLfloat cz = cosf(radZ), sz = sinf(radZ);
// 平移矩阵 T(列主序)
const GLfloat trans[16] = {
1.0F, 0.0F, 0.0F, 0.0F, // col 0
0.0F, 1.0F, 0.0F, 0.0F, // col 1
0.0F, 0.0F, 1.0F, 0.0F, // col 2
0.0F, 0.0F, -100.0F, 1.0F // col 3 (tx, ty, tz, 1)
};
// 绕 X 轴旋转矩阵(列主序)
const GLfloat rotX[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, cx, sx, 0.0F,
0.0F, -sx, cx, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Y 轴旋转矩阵(列主序)
const GLfloat rotY[16] = {cy, 0.0F, -sy, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
sy, 0.0F, cy, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Z 轴旋转矩阵(列主序)
const GLfloat rotZ[16] = {cz, sz, 0.0F, 0.0F, -sz, cz, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 组合: T * Rx * Ry * Rz
GLfloat temp1[16], temp2[16], finalMatrix[16];
multiplyMatrix4(rotY, rotZ, temp1); // temp1 = Ry * Rz
multiplyMatrix4(rotX, temp1, temp2); // temp2 = Rx * Ry * Rz
multiplyMatrix4(trans, temp2, finalMatrix); // final = T * Rx * Ry * Rz
glMultMatrixf(finalMatrix);
// 绘制 喷气式飞机
glBegin(GL_TRIANGLES);
// 飞机头
glColor3ub(255, 255, 255);
setFaceNormal(0.0F, 0.0F, 60.0F,
-15.0F, 0.0F, 30.0F,
15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glColor3ub(0, 0, 0);
setFaceNormal(15.0F, 0.0F, 30.0F,
0.0F, 15.0F, 30.0F,
0.0F, 0.0F, 60.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
glColor3ub(255, 0, 0);
setFaceNormal(0.0F, 0.0F, 60.0F,
0.0F, 15.0F, 30.0F,
-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
// 飞机体
glColor3ub(0, 255, 0);
setFaceNormal(-15.0F, 0.0F, 30.0F,
0.0F, 15.0F, 30.0F,
0.0F, 0.0F, -56.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
glColor3ub(255, 255, 0);
setFaceNormal(0.0F, 0.0F, -56.0F,
0.0F, 15.0F, 30.0F,
15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glColor3ub(0, 255, 255);
setFaceNormal(15.0F, 0.0F, 30.0F,
-15.0F, 0.0F, 30.0F,
0.0F, 0.0F, -56.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
// 机翼
glColor3ub(128, 128, 128);
setFaceNormal(0.0F, 2.0F, 27.0F,
-60.0F, 2.0F, -8.0F,
60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
glColor3ub(64, 64, 64);
setFaceNormal(60.0F, 2.0F, -8.0F,
0.0F, 7.0F, -8.0F,
0.0F, 2.0F, 27.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
glColor3ub(192, 192, 192);
setFaceNormal(60.0F, 2.0F, -8.0F,
-60.0F, 2.0F, -8.0F,
0.0F, 7.0F, -8.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glColor3ub(64, 64, 64);
setFaceNormal(0.0F, 2.0F, 27.0F,
0.0F, 7.0F, -8.0F,
-60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
// tail
glColor3ub(255, 128, 255);
setFaceNormal(-30.0F, -0.5F, -57.0F,
30.0F, -0.5F, -57.0F,
0.0F, -0.5F, -40.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
glColor3ub(255, 128, 0);
setFaceNormal(0.0F, -0.5F, -40.0F,
30.0F, -0.5F, -57.0F,
0.0F, 4.0F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
glColor3ub(255, 128, 0);
setFaceNormal(0.0F, 4.0F, -57.0F,
-30.0F, -0.5F, -57.0F,
0.0F, -0.5F, -40.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
glColor3ub(255, 255, 255);
setFaceNormal(30.0F, -0.5F, -57.0F,
-30.0F, -0.5F, -57.0F,
0.0F, 4.0F, -57.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
glColor3ub(255, 0, 0);
setFaceNormal(0.0F, 0.5F, -40.0F,
3.0F, 0.5F, -57.0F,
0.0F, 25.0F, -65.0F);
glVertex3f(0.0F, 0.5F, -40.0F);
glVertex3f(3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glColor3ub(255, 0, 0);
setFaceNormal(0.0F, 25.0F, -65.0F,
-3.0F, 0.5F, -57.0F,
0.0F, 0.5F, -40.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glVertex3f(-3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 0.5F, -40.0F);
glColor3ub(128, 128, 128);
setFaceNormal(3.0F, 0.5F, -57.0F,
-3.0F, 0.5F, -57.0F,
0.0F, 25.0F, -65.0F);
glVertex3f(3.0F, 0.5F, -57.0F);
glVertex3f(-3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glEnd();
glPopMatrix();
glutSwapBuffers();
}
void setupRc() {
// 设置清除颜色为黑色
glClearColor(0.23F, 0.23F, 0.34F, 1.0F);
glEnable(GL_DEPTH_TEST); // 启用深度测试
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
constexpr GLfloat ambientLight[] = {0.3F, 0.3F, 0.3F, 1.0F};
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
constexpr GLfloat diffuseLight[] = {0.7F, 0.7F, 0.7F, 1.0F};
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
constexpr GLfloat lightPos[] = {0.0F, 50.0F, -100.0F, 1.0F};
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
constexpr GLfloat specularLight[] = {1.0F, 1.0F, 1.0F, 1.0F};
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
constexpr GLfloat specref[] = {1.0F, 1.0F, 1.0F, 1.0F};
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
}
void changeSize(const GLsizei w, const GLsizei h) {
std::cout << std::format("changeSize: w={}, h={}", w, h) << "\n";
// 防止除以 0
if (h == 0) {
throw std::runtime_error("h == 0");
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // 投影
glLoadIdentity();
const GLfloat aspectRatio =
static_cast<GLfloat>(w) / static_cast<GLfloat>(h);
gluPerspective(60.0F, aspectRatio, 1.0F, 500.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y) {
std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y)
<< "\n";
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
break;
}
// Refresh the Window
glutPostRedisplay();
}
auto main(const int argc, const char **const argv) -> int {
std::cout << "hello world\n";
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << "\n";
}
// 初始化 glut
glutInit(const_cast<int *>(&argc), const_cast<char **>(argv));
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// 设置窗口大小
glutInitWindowSize(800, 600);
// 创建窗口
const int result = glutCreateWindow("Hello World");
std::cout << "result: " << result << "\n";
if (result == 0) {
throw std::runtime_error("glutCreateWindow failed");
}
// 设置显示回调函数
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// 注册 键盘事件 回调
glutSpecialFunc(specialKeys); // 特殊键事件
glutKeyboardFunc(keyboard); // 普通键事件
setupRc();
glutMainLoop();
return 0;
}
灯绕球旋转
// mac 特有的头文件
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <cmath>
#include <iostream>
#include <numbers>
// 可视坐标大小
static constexpr GLfloat COORDINATE_SIZE = 120.0F;
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
static int mainWindowId = 0; // 全局
static GLboolean isShade = GL_TRUE; // 是否平滑着色 (GL_TRUE = 平滑, GL_FALSE = 平面)
static GLubyte iTess = 0;
static GLfloat lightPos[] = {0.0F, 0.0F, 75.0F, 1.0F};
// 4x4 矩阵乘法: result = a * b(列主序,兼容 OpenGL)
void multiplyMatrix4(const GLfloat a[16], const GLfloat b[16],
GLfloat result[16]) {
for (int col = 0; col < 4; ++col) {
for (int row = 0; row < 4; ++row) {
result[col * 4 + row] = a[0 * 4 + row] * b[col * 4 + 0] +
a[1 * 4 + row] * b[col * 4 + 1] +
a[2 * 4 + row] * b[col * 4 + 2] +
a[3 * 4 + row] * b[col * 4 + 3];
}
}
}
// 用三角形三个顶点求面法线(右手定则叉乘 + 归一化),并设为当前法线。
// 顶点顺序需与 glVertex3f 一致:CCW 正面时法线指向观察者。
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
void renderScene() {
std::cout << std::format("renderScene: xRot={}, yRot={}, zRot={}", xRot, yRot, zRot) << "\n";
if (isShade) {
glShadeModel(GL_SMOOTH);
} else {
glShadeModel(GL_FLAT);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// --- 手动构建变换矩阵,替代 glTranslatef / glRotatef ---
const GLfloat radX = xRot * PI / 180.0F;
const GLfloat radY = yRot * PI / 180.0F;
const GLfloat radZ = zRot * PI / 180.0F;
const GLfloat cx = cosf(radX), sx = sinf(radX);
const GLfloat cy = cosf(radY), sy = sinf(radY);
const GLfloat cz = cosf(radZ), sz = sinf(radZ);
// 平移矩阵 T(列主序)
const GLfloat trans[16] = {
1.0F, 0.0F, 0.0F, 0.0F, // col 0
0.0F, 1.0F, 0.0F, 0.0F, // col 1
0.0F, 0.0F, 1.0F, 0.0F, // col 2
0.0F, 0.0F, 1.0F, 1.0F // col 3 (tx, ty, tz, 1)
};
// 绕 X 轴旋转矩阵(列主序)
const GLfloat rotX[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, cx, sx, 0.0F,
0.0F, -sx, cx, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Y 轴旋转矩阵(列主序)
const GLfloat rotY[16] = {cy, 0.0F, -sy, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
sy, 0.0F, cy, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 绕 Z 轴旋转矩阵(列主序)
const GLfloat rotZ[16] = {cz, sz, 0.0F, 0.0F, -sz, cz, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};
// 组合: T * Rx * Ry * Rz
GLfloat temp1[16], temp2[16], finalMatrix[16];
multiplyMatrix4(rotY, rotZ, temp1); // temp1 = Ry * Rz
multiplyMatrix4(rotX, temp1, temp2); // temp2 = Rx * Ry * Rz
multiplyMatrix4(trans, temp2, finalMatrix); // final = T * Rx * Ry * Rz
glMultMatrixf(finalMatrix);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
constexpr GLfloat spotDir[] = { 0.0F, 0.0F, -1.0F }; // 聚光灯方向-z
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);
glPushAttrib(GL_LIGHTING_BIT);
glDisable(GL_LIGHTING);
glColor3ub(255, 0, 0); // 红色
glTranslatef(lightPos[0], lightPos[1], lightPos[2]);
glutSolidCone(4.0F, 6.0F, 15, 15); // 绘制光源位置的小球
glColor3ub(255, 255, 0); // 黄色
glutSolidSphere(3.0F, 15, 15); // 绘制光源位置的小球
glPopAttrib();
glPopMatrix();
// 设置大球材质颜色为蓝色(否则会沿用灯泡的黄色)
glColor3ub(0, 0, 255);
switch (iTess) {
case 0:
// 绘制默认模型
glutSolidSphere(30.0F, 7, 7);
break;
case 1:
// 绘制细分模型 1
glutSolidSphere(30.0F, 15, 15);
break;
case 2:
// 绘制细分模型 2
glutSolidSphere(30.0F, 50, 50);
break;
default:
break;
}
glutSwapBuffers();
}
void setupRc() {
// 设置清除颜色为黑色
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
glEnable(GL_DEPTH_TEST); // 启用深度测试
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
constexpr GLfloat ambientLight[] = {0.5F, 0.5F, 0.5F, 1.0F};
// 全局环境光(原版用 GL_LIGHT_MODEL_AMBIENT):聚光锥外的暗部也能看见
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
constexpr GLfloat lightPos[] = {0.0F, 0.0F, 75.0F, 1.0F};
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
constexpr GLfloat specularLight[] = {1.0F, 1.0F, 1.0F, 1.0F};
// 镜面反射光
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
constexpr GLfloat spotDir[] = { 0.0F, 0.0F, -1.0F }; // 聚光灯方向-z
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotDir);
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 60.0F);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
constexpr GLfloat specref[] = {1.0F, 1.0F, 1.0F, 1.0F};
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
}
void changeSize(const GLsizei w, const GLsizei h) {
std::cout << std::format("changeSize: w={}, h={}", w, h) << "\n";
// 防止除以 0
if (h == 0) {
throw std::runtime_error("h == 0");
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // 投影
glLoadIdentity();
const GLfloat aspectRatio =
static_cast<GLfloat>(w) / static_cast<GLfloat>(h);
gluPerspective(35.0F, aspectRatio, 1.0F, 500.0F); // 原版 Spot.c 的视角
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -250.0f);
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y) {
std::cout << std::format("specialKeys: key={}, x={}, y={}", key, x, y)
<< "\n";
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
break;
}
// Refresh the Window
glutPostRedisplay();
}
void processMenu(const int value) {
std::cout << std::format("processMenu: value={}", value) << "\n";
switch (value) {
case 1:
isShade = GL_FALSE; // 平面着色
break;
case 2:
isShade = GL_TRUE; // 平滑着色
break;
case 3:
iTess = 0;
break;
case 4:
iTess = 1;
break;
case 5:
iTess = 2;
break;
default:
break;
}
glutSetWindow(mainWindowId);
glutPostRedisplay();
}
auto main(const int argc, const char **const argv) -> int {
std::cout << "hello world\n";
for (int i = 0; i < argc; ++i) {
std::cout << "argv[" << i << "]: " << argv[i] << "\n";
}
// 初始化 glut
glutInit(const_cast<int *>(&argc), const_cast<char **>(argv));
// 设置显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
// 设置窗口大小
glutInitWindowSize(800, 600);
// 创建窗口
mainWindowId = glutCreateWindow("Hello World");
std::cout << "mainWindowId: " << mainWindowId << "\n";
if (mainWindowId == 0) {
throw std::runtime_error("glutCreateWindow failed");
}
// 设置显示回调函数
glutDisplayFunc(renderScene);
glutReshapeFunc(changeSize);
// 注册 键盘事件 回调
glutSpecialFunc(specialKeys); // 特殊键事件
glutKeyboardFunc(keyboard); // 普通键事件
const int menuId = glutCreateMenu(processMenu);
std::cout << "menuId: " << menuId << "\n";
glutSetMenu(menuId);
glutAddMenuEntry("Flat Shading", 1);
glutAddMenuEntry("Smooth Shading", 2);
glutAddMenuEntry("VL Tess", 3);
glutAddMenuEntry("MD Tess", 4);
glutAddMenuEntry("VH Tess", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
setupRc();
glutMainLoop();
return 0;
}
飞机阴影
#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <iostream>
// 常量
static constexpr GLfloat ambientLight[] = { 0.3F, 0.3F, 0.3F, 1.0F };
static constexpr GLfloat diffuseLight[] = { 0.7F, 0.7F, 0.7F, 1.0F };
static constexpr GLfloat specular[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat lightPos[] = { -75.0F, 150.0F, -50.0F, 0.0F };
static constexpr GLfloat specref[] = { 1.0F, 1.0F, 1.0F, 1.0F };
// 地面平面方程:绿色地面 y = -100 → 0·x + 1·y + 0·z + 100 = 0(注意 d 是 +100)
static constexpr GLfloat groundPlaneEq[] = { 0.0F, 1.0F, 0.0F, 100.0F };
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
/**
*
*/
void changeSize(const int width, const int height){
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat aspectRatio = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
gluPerspective(60.0F, aspectRatio, 200.0F, 500.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
xRot -= 5.0F;
break;
case GLUT_KEY_DOWN:
xRot += 5.0F;
break;
case GLUT_KEY_LEFT:
yRot -= 5.0F;
break;
case GLUT_KEY_RIGHT:
yRot += 5.0F;
break;
default:
return; // 不处理其他按键
}
glutPostRedisplay();
}
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
void drawJet(const GLboolean isShadow = GL_FALSE) {
if (isShadow) {
glColor3ub(0, 0, 0);
} else {
glColor3ub(128, 128, 128);
}
glBegin(GL_TRIANGLES);
// 飞机头
setFaceNormal(0.0F, 0.0F, 60.0F,
-15.0F, 0.0F, 30.0F,
15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
setFaceNormal(15.0F, 0.0F, 30.0F,
0.0F, 15.0F, 30.0F,
0.0F, 0.0F, 60.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
setFaceNormal(0.0F, 0.0F, 60.0F,
0.0F, 15.0F, 30.0F,
-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, 60.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
// 飞机体
setFaceNormal(-15.0F, 0.0F, 30.0F,
0.0F, 15.0F, 30.0F,
0.0F, 0.0F, -56.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
setFaceNormal(0.0F, 0.0F, -56.0F,
0.0F, 15.0F, 30.0F,
15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
glVertex3f(0.0F, 15.0F, 30.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
setFaceNormal(15.0F, 0.0F, 30.0F,
-15.0F, 0.0F, 30.0F,
0.0F, 0.0F, -56.0F);
glVertex3f(15.0F, 0.0F, 30.0F);
glVertex3f(-15.0F, 0.0F, 30.0F);
glVertex3f(0.0F, 0.0F, -56.0F);
// 机翼
setFaceNormal(0.0F, 2.0F, 27.0F,
-60.0F, 2.0F, -8.0F,
60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
setFaceNormal(60.0F, 2.0F, -8.0F,
0.0F, 7.0F, -8.0F,
0.0F, 2.0F, 27.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
setFaceNormal(60.0F, 2.0F, -8.0F,
-60.0F, 2.0F, -8.0F,
0.0F, 7.0F, -8.0F);
glVertex3f(60.0F, 2.0F, -8.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
setFaceNormal(0.0F, 2.0F, 27.0F,
0.0F, 7.0F, -8.0F,
-60.0F, 2.0F, -8.0F);
glVertex3f(0.0F, 2.0F, 27.0F);
glVertex3f(0.0F, 7.0F, -8.0F);
glVertex3f(-60.0F, 2.0F, -8.0F);
// tail
setFaceNormal(-30.0F, -0.5F, -57.0F,
30.0F, -0.5F, -57.0F,
0.0F, -0.5F, -40.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
setFaceNormal(0.0F, -0.5F, -40.0F,
30.0F, -0.5F, -57.0F,
0.0F, 4.0F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
setFaceNormal(0.0F, 4.0F, -57.0F,
-30.0F, -0.5F, -57.0F,
0.0F, -0.5F, -40.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, -0.5F, -40.0F);
setFaceNormal(30.0F, -0.5F, -57.0F,
-30.0F, -0.5F, -57.0F,
0.0F, 4.0F, -57.0F);
glVertex3f(30.0F, -0.5F, -57.0F);
glVertex3f(-30.0F, -0.5F, -57.0F);
glVertex3f(0.0F, 4.0F, -57.0F);
setFaceNormal(0.0F, 0.5F, -40.0F,
3.0F, 0.5F, -57.0F,
0.0F, 25.0F, -65.0F);
glVertex3f(0.0F, 0.5F, -40.0F);
glVertex3f(3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
setFaceNormal(0.0F, 25.0F, -65.0F,
-3.0F, 0.5F, -57.0F,
0.0F, 0.5F, -40.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glVertex3f(-3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 0.5F, -40.0F);
setFaceNormal(3.0F, 0.5F, -57.0F,
-3.0F, 0.5F, -57.0F,
0.0F, 25.0F, -65.0F);
glVertex3f(3.0F, 0.5F, -57.0F);
glVertex3f(-3.0F, 0.5F, -57.0F);
glVertex3f(0.0F, 25.0F, -65.0F);
glEnd();
}
/**
* 计算平面投影阴影矩阵:把顶点沿光线方向投影到平面 planeEq 上
*
* planeEq : 平面方程 a·x + b·y + c·z + d = 0
* lightPos: 与 glLightfv(GL_POSITION) 相同的齐次坐标
* w = 1 点光源(阴影有透视近大远小)
* w = 0 平行光/方向光(阳光,阴影是平行投影)
* m : 输出 16 元素列主序矩阵,可直接传给 glMultMatrixf
*
* 原理:顶点 p 沿光线方向的影子 s = p + t·L 满足 n·s + d = 0,
* 解得 t = -(n·p + d)/(n·L),代回可得齐次形式
* s' = dot·p - (n·p)·L,其中 dot = n·L + d·Lw。
* 这是 p 的线性(射影)变换,故可写成 4×4 矩阵:
* M[i][j] = dot·δ(i,j) - L[i]·n[j],按列主序展开。
*/
void makeShadowMatrix(GLfloat m[16], const GLfloat planeEq[4], const GLfloat lightPos[4]) {
const GLfloat dot = planeEq[0] * lightPos[0] + planeEq[1] * lightPos[1] +
planeEq[2] * lightPos[2] + planeEq[3] * lightPos[3];
m[0] = dot - lightPos[0] * planeEq[0]; // 第 1 列(列主序)
m[1] = - lightPos[1] * planeEq[0];
m[2] = - lightPos[2] * planeEq[0];
m[3] = - lightPos[3] * planeEq[0];
m[4] = - lightPos[0] * planeEq[1]; // 第 2 列
m[5] = dot - lightPos[1] * planeEq[1];
m[6] = - lightPos[2] * planeEq[1];
m[7] = - lightPos[3] * planeEq[1];
m[8] = - lightPos[0] * planeEq[2]; // 第 3 列
m[9] = - lightPos[1] * planeEq[2];
m[10] = dot - lightPos[2] * planeEq[2];
m[11] = - lightPos[3] * planeEq[2];
m[12] = - lightPos[0] * planeEq[3]; // 第 4 列
m[13] = - lightPos[1] * planeEq[3];
m[14] = - lightPos[2] * planeEq[3];
m[15] = dot - lightPos[3] * planeEq[3];
}
void renderScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3ub(0, 32, 0);
glVertex3f(2000.0F, -100.0F, -500.0F); // 远边(暗)
glVertex3f(-2000.0F, -100.0F, -500.0F);
glColor3ub(0, 255, 0);
glVertex3f(-2000.0F, -100.0F, -200.0F); // 近边(亮)
glVertex3f(2000.0F, -100.0F, -200.0F);
glEnd();
glPushMatrix();
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glTranslatef(0.0F, 0.0F, -350.0F);
glRotatef(xRot, 1.0F, 0.0F, 0.0F);
glRotatef(yRot, 0.0F, 1.0F, 0.0F);
glRotatef(zRot, 0.0F, 0.0F, 1.0F);
drawJet();
glPopMatrix();
// 绘制阴影
glDisable(GL_DEPTH_TEST); // 不关闭的话阴影显示不全
glDisable(GL_LIGHTING);
glPushMatrix();
// 根据 绿色地面 和 光源位置 计算阴影矩阵
GLfloat shadowMat[16];
makeShadowMatrix(shadowMat, groundPlaneEq, lightPos);
glMultMatrixf(shadowMat);
glTranslatef(0.0F, 0.0F, -350.0F);
glRotatef(xRot, 1.0F, 0.0F, 0.0F);
glRotatef(yRot, 0.0F, 1.0F, 0.0F);
glRotatef(zRot, 0.0F, 0.0F, 1.0F);
drawJet(GL_TRUE);
glPopMatrix();
// 光源
glPushMatrix();
glTranslatef(lightPos[0], lightPos[1], -350.0F);
glColor3ub(255, 255, 0);
glutSolidSphere(5.0F, 15, 15);
glPopMatrix();
glutSwapBuffers();
}
void setupRc(){
glClearColor(0.0F, 0.0F, 1.0F, 1.0F);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 600);
const int mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
setupRc();
glutMainLoop();
return 0;
}
视锥如下:

视点可以根据gluLookAt调整。
涉及线性代数数学知识,已知,点\(A(x_1, y_1,z_1),B(x_2,y_2,z_2),C(x_3,y_3,z_3)\)三点不共线,求平面方程
方法1:用叉积求法向量
方法2: 行列式
这个矩阵就是把你刚才的投影公式,用齐次坐标和矩阵乘法表达出来的紧凑形式。我来一步步推导,你就能看清它是怎么来的。
1. 回顾我们之前的公式
平面方程:\(ax + by + cz + d = 0\),写成向量点积:
其中 \(\vec{n} = (a,b,c)\),\(\vec{x} = (x,y,z)\)。
用齐次坐标表示平面:
点\(\vec{x} = (x,y,z,w)\) 在平面上满足:
(对于欧氏点,\(w=1\))
2. 投影公式的齐次形式
光源:\(L = (l_x, l_y, l_z, l_w)\)(欧氏点通常 \(l_w=1\))
物体点:\(Q = (x, y, z, w)\)(欧氏点 \(w=1\))
射线参数方程:
要求它在平面上:
代入:
注意:因为 \(Q\) 在平面上时 \(\vec{p} \cdot Q = 0\),所以 \(t=1\),点不变(合理)。
3. 关键技巧:令 \(t = -\frac{\vec{p} \cdot L}{\vec{p} \cdot Q - \vec{p} \cdot L}\)
阴影点:
代入 $$t$$:
通分:
化简分子:
关键发现:分母的\(\vec{p} \cdot Q - \vec{p} \cdot L\) 也可以写成 \(\vec{p} \cdot (Q - L)\),但为了矩阵形式,我们保持原样。
4. 齐次坐标下的线性变换
上面的表达式是分式线性的,但在齐次坐标下可以写成线性变换:
定义变换 \(M\),使得:
写成矩阵形式:
但这里分母是 \(\vec{p} \cdot Q - \vec{p} \cdot L\),不是\(\vec{p} \cdot Q\)。
关键:我们要找一个矩阵 \(M\),使得:
而这个结果自动满足:
也就是说,$$M \cdot Q$$ 已经是在平面上的点了(齐次坐标意义下)。
5. 矩阵\(M\) 的推导
目标是:
把右边的两个项展开成矩阵形式:
第一项:((\vec{p} \cdot Q)L)
这是把标量 \((\vec{p} \cdot Q)\) 乘以向量 \(L\)。
在矩阵形式中,相当于:
作用于 (Q):
第二项:\((\vec{p} \cdot L)Q\)
这是把标量 \((\vec{p} \cdot L)\) 乘以向量 \(Q\)。
在矩阵形式中,相当于:
作用于 (Q):
所以:
6. 展开成 4×4 矩阵
设:
\(L \cdot \vec{p}^T\) 是一个外积(4×4 矩阵):
减去 \((\vec{p} \cdot L) I\):
7. 对比代码中的矩阵(列主序)
代码中的矩阵是列主序(OpenGL 风格),所以存储顺序是:
m[0] m[4] m[8] m[12]
m[1] m[5] m[9] m[13]
m[2] m[6] m[10] m[14]
m[3] m[7] m[11] m[15]
对照我们的矩阵:
第 1 列(对应 (x) 分量):
m[0] = l_x*a - dotm[1] = l_y*am[2] = l_z*am[3] = l_w*a
第 2 列(对应 (y) 分量):
m[4] = l_x*bm[5] = l_y*b - dotm[6] = l_z*bm[7] = l_w*b
第 3 列(对应 (z) 分量):
m[8] = l_x*cm[9] = l_y*cm[10] = l_z*c - dotm[11] = l_w*c
第 4 列(对应 (w) 分量):
m[12] = l_x*dm[13] = l_y*dm[14] = l_z*dm[15] = l_w*d - dot
8. 和代码对比
代码中(注意符号和我推导的相反,但效果一样,因为整体乘以 -1 不改变投影):
m[0] = dot - lightPos[0] * planeEq[0]; // dot - l_x*a
m[4] = - lightPos[0] * planeEq[1]; // -l_x*b
m[8] = - lightPos[0] * planeEq[2]; // -l_x*c
m[12] = - lightPos[0] * planeEq[3]; // -l_x*d
这个矩阵是负的我们的 \(M\):
总结
| 推导步骤 | 公式 |
|---|---|
| 投影公式 | \(S = L + t(Q-L)\), \(t = -\frac{\vec{p}\cdot L}{\vec{p}\cdot Q - \vec{p}\cdot L}\) |
| 化简 | \(S = \frac{(\vec{p}\cdot Q)L - (\vec{p}\cdot L)Q}{\vec{p}\cdot Q - \vec{p}\cdot L}\) |
| 矩阵形式 | \(M = L \cdot \vec{p}^T - (\vec{p}\cdot L) I\) |
| 代码中的矩阵 | \(M_{\text{code}} = (\vec{p}\cdot L) I - L \cdot \vec{p}^T\)(取负,不影响投影) |
之所以能写成矩阵,是因为在齐次坐标下,投影变换是一个线性变换,而之前我们用的 \((x,y,z)\) 坐标是非线性的(有除法)。这就是为什么图形学中普遍使用齐次坐标和 4×4 矩阵的原因。
球体世界阴影

#include <OpenGL/gl.h>
#include <OpenGL/gltypes.h>
#include <OpenGL/glu.h>
#include <numbers>
#ifdef __APPLE__
#include <GLUT/glut.h>
#include <GLKit/GLKMath.h>
#else
extern "C" {
#include <GL/freeglut.h>
}
#endif
#include <cmath>
#include <iostream>
struct GLFrame {
GLfloat vLocation[3]; // 位置 -> move x, y, z
GLfloat vUp[3]; // +y
GLfloat vForward[3]; // -z
// GLfloat right[3]; // +x-> 通过 yz叉积计算 -> vUp, vForward均为单位向量
};
// 常量
static constexpr GLfloat PI = std::numbers::pi_v<GLfloat>;
static constexpr GLint NUM_SPHERES = 30;
static constexpr GLfloat ambientLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
static constexpr GLfloat diffuseLight[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat specular[] = { 1.0F, 1.0F, 1.0F, 1.0F };
static constexpr GLfloat lightPos[] = { -100.0F, 100.0F, 50.0F, 1.0F };
static constexpr GLfloat specref[] = { 1.0F, 1.0F, 1.0F, 1.0F };
// 变量
static GLfloat xRot = 0.0F; // x旋转角度
static GLfloat yRot = 0.0F; // y旋转角度
static GLfloat zRot = 0.0F; // z旋转角度
GLFrame frameCamera; // 摄像机帧
GLFrame spheres[NUM_SPHERES]; // 球体帧
GLfloat shadowMat[16];
void gltInitFrame(GLFrame *frame) {
frame->vLocation[0] = 0.0F;
frame->vLocation[1] = 0.0F;
frame->vLocation[2] = 0.0F;
frame->vUp[0] = 0.0F;
frame->vUp[1] = 1.0F;
frame->vUp[2] = 0.0F;
frame->vForward[0] = 0.0F;
frame->vForward[1] = 0.0F;
frame->vForward[2] = -1.0F; // 相机朝向为-z
}
void changeSize(const int width, const int height){
std::cout << std::format("changeSize: width={}, height={}", width, height) << "\n";
if(height == 0) {
throw std::runtime_error("Height cannot be zero.");
}
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const GLfloat aspectRatio = static_cast<GLfloat>(width) / static_cast<GLfloat>(height);
gluPerspective(35.0F, aspectRatio, 1.0F, 50.0F);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// 键盘回调:按 ESC 或 'q' 退出
void keyboard(const unsigned char key, const int x, const int y) {
std::cout << std::format("keyboard: key={}, x={}, y={}", key, x, y) << "\n";
switch (key) {
case 27: // ESC 键
case 'q':
case 'Q':
std::cout << "keyboard close..." << "\n";
exit(0);
break;
case 'a':
case 'A':
zRot -= 5.0F; // 绕 Z 轴逆时针旋转
glutPostRedisplay();
break;
case 'd':
case 'D':
zRot += 5.0F; // 绕 Z 轴顺时针旋转
glutPostRedisplay();
break;
default:
break;
}
}
// 绕 vUp 轴左右转向(yaw)。正角度 = 向左转(右手系绕 +Y 逆时针)。
// Rodrigues 公式:v' = v·cosθ + (vUp × v)·sinθ(vUp ⟂ vForward,u·(u·v)(1-cosθ) 项为 0)
void rotateCamera(const GLfloat degrees) {
constexpr GLfloat radPerDeg = 3.14159265F / 180.0F;
const GLfloat theta = degrees * radPerDeg;
const GLfloat c = std::cos(theta);
const GLfloat s = std::sin(theta);
const GLfloat fx = frameCamera.vForward[0];
const GLfloat fy = frameCamera.vForward[1];
const GLfloat fz = frameCamera.vForward[2];
frameCamera.vForward[0] = fx * c + (frameCamera.vUp[1] * fz - frameCamera.vUp[2] * fy) * s;
frameCamera.vForward[1] = fy * c + (frameCamera.vUp[2] * fx - frameCamera.vUp[0] * fz) * s;
frameCamera.vForward[2] = fz * c + (frameCamera.vUp[0] * fy - frameCamera.vUp[1] * fx) * s;
// 浮点累加会漂移,重新归一化保持单位长度(前进步长 0.1 才精确)
const GLfloat len = std::sqrt(frameCamera.vForward[0] * frameCamera.vForward[0] +
frameCamera.vForward[1] * frameCamera.vForward[1] +
frameCamera.vForward[2] * frameCamera.vForward[2]);
if (len > 0.0F) {
frameCamera.vForward[0] /= len;
frameCamera.vForward[1] /= len;
frameCamera.vForward[2] /= len;
}
}
void specialKeys(const int key, const int x, const int y){
switch (key) {
case GLUT_KEY_UP:
frameCamera.vLocation[0] += frameCamera.vForward[0] * 0.1F; // 0
frameCamera.vLocation[1] += frameCamera.vForward[1] * 0.1F; // 0
frameCamera.vLocation[2] += frameCamera.vForward[2] * 0.1F; // 0.1
break;
case GLUT_KEY_DOWN:
frameCamera.vLocation[0] -= frameCamera.vForward[0] * 0.1F; // 0
frameCamera.vLocation[1] -= frameCamera.vForward[1] * 0.1F; //
frameCamera.vLocation[2] -= frameCamera.vForward[2] * 0.1F; // -0.1
break;
case GLUT_KEY_LEFT:
rotateCamera(5.0F); // 向左转(正角度 = 绕 vUp 逆时针)
break;
case GLUT_KEY_RIGHT:
rotateCamera(-5.0F); // 向右转
break;
default:
return; // 不处理其他按键
}
glutPostRedisplay();
}
void setFaceNormal(const GLfloat ax, const GLfloat ay, const GLfloat az,
const GLfloat bx, const GLfloat by, const GLfloat bz,
const GLfloat cx, const GLfloat cy, const GLfloat cz) {
const GLfloat ux = bx - ax, uy = by - ay, uz = bz - az;
const GLfloat vx = cx - ax, vy = cy - ay, vz = cz - az;
GLfloat nx = uy * vz - uz * vy;
GLfloat ny = uz * vx - ux * vz;
GLfloat nz = ux * vy - uy * vx;
const GLfloat len = std::sqrt(nx * nx + ny * ny + nz * nz);
if (len > 0.0F) {
nx /= len;
ny /= len;
nz /= len;
}
glNormal3f(nx, ny, nz);
}
/**
* 计算平面投影阴影矩阵:把顶点沿光线方向投影到平面 planeEq 上
*
* planeEq : 平面方程 a·x + b·y + c·z + d = 0
* lightPos: 与 glLightfv(GL_POSITION) 相同的齐次坐标
* w = 1 点光源(阴影有透视近大远小)
* w = 0 平行光/方向光(阳光,阴影是平行投影)
* m : 输出 16 元素列主序矩阵,可直接传给 glMultMatrixf
*
* 原理:顶点 p 沿光线方向的影子 s = p + t·L 满足 n·s + d = 0,
* 解得 t = -(n·p + d)/(n·L),代回可得齐次形式
* s' = dot·p - (n·p)·L,其中 dot = n·L + d·Lw。
* 这是 p 的线性(射影)变换,故可写成 4×4 矩阵:
* M[i][j] = dot·δ(i,j) - L[i]·n[j],按列主序展开。
*/
void makeShadowMatrix(GLfloat m[16], const GLfloat planeEq[4], const GLfloat lightPos[4]) {
const GLfloat dot = planeEq[0] * lightPos[0] + planeEq[1] * lightPos[1] +
planeEq[2] * lightPos[2] + planeEq[3] * lightPos[3];
m[0] = dot - lightPos[0] * planeEq[0]; // 第 1 列(列主序)
m[1] = - lightPos[1] * planeEq[0];
m[2] = - lightPos[2] * planeEq[0];
m[3] = - lightPos[3] * planeEq[0];
m[4] = - lightPos[0] * planeEq[1]; // 第 2 列
m[5] = dot - lightPos[1] * planeEq[1];
m[6] = - lightPos[2] * planeEq[1];
m[7] = - lightPos[3] * planeEq[1];
m[8] = - lightPos[0] * planeEq[2]; // 第 3 列
m[9] = - lightPos[1] * planeEq[2];
m[10] = dot - lightPos[2] * planeEq[2];
m[11] = - lightPos[3] * planeEq[2];
m[12] = - lightPos[0] * planeEq[3]; // 第 4 列
m[13] = - lightPos[1] * planeEq[3];
m[14] = - lightPos[2] * planeEq[3];
m[15] = dot - lightPos[3] * planeEq[3];
}
void drawGround(){
glColor3f(1.0F, 1.0F, 1.0F); // 绿色
constexpr GLfloat y = -0.4F;
constexpr GLfloat edge = 20.0F;
constexpr GLfloat step = 1.0F;
for (GLfloat i = -edge; i <= edge; i += step) {
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0.0F, 1.0F, 0.0F); // All Point up
for (GLfloat j = edge; j >= -edge; j -= step) {
glVertex3f(i, y, j);
glVertex3f(i + step, y, j);
}
glEnd();
}
}
/**
* 物体→世界
* 叉积有方向前面为右手法则,右向量 = Forward × Up
* 右手法则:四指从前向上卷,拇指指向右手的右边,叉积的方向就是拇指的方向。
*/
void gltGetMatrixFromFrame(const GLFrame *pFrame, GLfloat m[16]) {
GLfloat r[3]; // 右向量 X = Forward × Up
r[0] = pFrame->vForward[1] * pFrame->vUp[2] - pFrame->vForward[2] * pFrame->vUp[1];
r[1] = pFrame->vForward[2] * pFrame->vUp[0] - pFrame->vForward[0] * pFrame->vUp[2];
r[2] = pFrame->vForward[0] * pFrame->vUp[1] - pFrame->vForward[1] * pFrame->vUp[0];
// 列主序
// 第3列取 -vForward:OpenGL 的 +Z 指向观察者背后
m[0]=r[0]; m[4]=pFrame->vUp[0]; m[8] =-pFrame->vForward[0]; m[12]=pFrame->vLocation[0];
m[1]=r[1]; m[5]=pFrame->vUp[1]; m[9] =-pFrame->vForward[1]; m[13]=pFrame->vLocation[1];
m[2]=r[2]; m[6]=pFrame->vUp[2]; m[10]=-pFrame->vForward[2]; m[14]=pFrame->vLocation[2];
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
}
/**
* 视图矩阵 = 上面的逆:旋转转置 + 平移取负
* 把世界挪到相机眼前
*/
void gltApplyCameraTransform(const GLFrame *pCamera) {
GLfloat r[3];
r[0] = pCamera->vForward[1] * pCamera->vUp[2] - pCamera->vForward[2] * pCamera->vUp[1];
r[1] = pCamera->vForward[2] * pCamera->vUp[0] - pCamera->vForward[0] * pCamera->vUp[2];
r[2] = pCamera->vForward[0] * pCamera->vUp[1] - pCamera->vForward[1] * pCamera->vUp[0];
GLfloat m[16]; // 同样的轴,改填「行」,即转置
m[0]=r[0]; m[4]=r[1]; m[8] =r[2]; m[12]=0.0F;
m[1]=pCamera->vUp[0]; m[5]=pCamera->vUp[1]; m[9] =pCamera->vUp[2]; m[13]=0.0F;
m[2]=-pCamera->vForward[0]; m[6]=-pCamera->vForward[1]; m[10]=-pCamera->vForward[2]; m[14]=0.0F;
m[3]=0.0F; m[7]=0.0F; m[11]=0.0F; m[15]=1.0F;
glMultMatrixf(m);
glTranslatef(-pCamera->vLocation[0], -pCamera->vLocation[1], -pCamera->vLocation[2]);
}
void drawSpheres() {
for (int i = 0; i < NUM_SPHERES; ++i) {
glPushMatrix();
GLfloat m[16];
gltGetMatrixFromFrame(&spheres[i], m);
glMultMatrixf(m);
glutSolidSphere(0.3F, 17, 9);
glPopMatrix();
}
}
void drawTorus(const GLfloat majorRadius, const GLfloat minorRadius, const GLint numMajor, const GLint numMinor) {
glPushMatrix();
glTranslatef(0.0F, 0.1F, -2.5F);
for (GLint i = 0; i < numMajor; ++i) {
const GLfloat u0 = (GLfloat)i / numMajor * 2.0F * PI;
const GLfloat u1 = (GLfloat)(i + 1) / numMajor * 2.0F * PI;
glBegin(GL_TRIANGLE_STRIP);
for (GLint j = 0; j <= numMinor; ++j) {
GLfloat v = (GLfloat)j / numMinor * 2.0F * PI;
GLfloat cosV = cosf(v), sinV = sinf(v);
// 点1 (u0 经线)
GLfloat cu0 = cosf(u0), su0 = sinf(u0);
glNormal3f(cu0 * cosV, su0 * cosV, sinV);
glVertex3f(cu0 * (majorRadius + minorRadius * cosV),
su0 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
// 点2 (u1 经线)
GLfloat cu1 = cosf(u1), su1 = sinf(u1);
glNormal3f(cu1 * cosV, su1 * cosV, sinV);
glVertex3f(cu1 * (majorRadius + minorRadius * cosV),
su1 * (majorRadius + minorRadius * cosV),
minorRadius * sinV);
}
glEnd();
}
glPopMatrix();
}
void drawBall(){
glPushMatrix();
glTranslatef(1.0F, 0.1F, -2.5F);
glutSolidSphere(0.1F, 17, 9);
glPopMatrix();
}
void renderScene(){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
gltApplyCameraTransform(&frameCamera);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
// 绘制地面
glColor3f(0.60F, 0.40F, 0.10F);
drawGround();
// 阴影
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glMultMatrixf(shadowMat);
glColor3f(0.0F, 0.0F, 0.0F);
drawSpheres();
drawTorus(0.35, 0.15, 61, 37);
drawBall();
glPopMatrix();
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
// 球体、圆环和小球
glColor3f(0.0F, 1.0F, 0.0F);
drawSpheres();
glColor3f(1.0F, 0.0F, 0.0F);
drawTorus(0.35, 0.15, 61, 37);
glColor3f(0.0F, 0.0F, 1.0F);
drawBall();
glPopMatrix();
glutSwapBuffers();
}
void setupRc(){
constexpr GLfloat fNoLight[] = { 0.0F, 0.0F, 0.0F, 0.0F };
constexpr GLfloat fLowLight[] = { 0.25F, 0.25F, 0.25F, 1.0F };
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glClearColor(0.25F, 0.25F, 0.25F, 1.0F);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, specref);
glMateriali(GL_FRONT, GL_SHININESS, 128);
// 阴影
constexpr GLfloat planeEq[4] = { 0.0F, 1.0F, 0.0F, 0.4F };
makeShadowMatrix(shadowMat, planeEq, lightPos);
// 初始化摄像机和球体帧
gltInitFrame(&frameCamera);
for (int i = 0; i < NUM_SPHERES; ++i) {
gltInitFrame(&spheres[i]);
// -20.0 到 19.9
spheres[i].vLocation[0] = static_cast<GLfloat>((rand() % 400 - 200) * 0.1F);
spheres[i].vLocation[1] = 0.0F;
spheres[i].vLocation[2] = static_cast<GLfloat>((rand() % 400 - 200) * 0.1F);
}
}
auto main(const int argc, const char** argv) -> int {
glutInit(const_cast<int*>(&argc), const_cast<char**>(argv));
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 600);
const int mainWindowId = glutCreateWindow("OpenGL Window");
std::cout << "Main window ID: " << mainWindowId << std::endl;
glutReshapeFunc(changeSize);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutDisplayFunc(renderScene);
setupRc();
glutMainLoop();
return 0;
}
实现这个是真的很难0.0

浙公网安备 33010602011771号