目标OpenGL 中的坐标变换和颜色的使用。具体任务利用矩阵操作实现视图变换、模型变换和投影变换利用函数指定颜色模式。三种变换模型变换改变物体自身的位置、旋转、缩放比如让立方体向左移动、旋转。视图变换改变观察角度相机位置和朝向不改变物体本身只改变 “从哪里看”。投影变换定义画面的透视效果比如近大远小而视图变换只决定观察方向。代码解释一、主函数// 主函数 int main(int argc, char** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow(OpenGL 矩阵变换示例); init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }对比上节代码区别在于1、glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB |GLUT_DEPTH);开启深度缓存的作用在于每当绘制新像素时OpenGL 会自动比较新像素与已有像素的深度值如果新像素更近深度值更小则更新颜色缓冲区和深度缓冲区显示新像素如果新像素更远则忽略该像素不覆盖已有像素。2、glutReshapeFunc(reshape);当窗口被拉伸、缩放或首次创建时FreeGLUT 会自动调用这个函数。由于我们将在该节中使用透视投影代码如下透视投影与窗口的宽高比密切相关若窗口大小改变而不及时去更改width和height的值可能导致图像投影后失真。gluPerspective(45.0f, (float)width / (float)height, 0.1f, 100.0f);可是问题是为什么窗口会改变呢我们并不会在代码中去更改窗口大小但是用户可以通过拉伸窗口更改其宽高。为了防止图像失真需要主动调用。3、glutTimerFunc(0, timer, 0);如果不注册glutTimerFunc(0, timer, 0);就无法自动更新场景状态和触发重绘动画会静止不动除非通过用户交互手动触发。二、display函数// 显示回调函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲区 // 重置模型视图矩阵 glLoadIdentity(); // -------------------------- 视图变换 -------------------------- // 参数相机位置(x,y,z)、目标点(x,y,z)、上方向向量(x,y,z) gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可以通过按键调整z值 0.0f, 0.0f, 0.0f, // 目标点看向原点 0.0f, 1.0f, 0.0f); // 上方向Y轴为上 // -------------------------- 模型变换 -------------------------- glRotatef(rotationAngle, 1.0f, 1.0f, 0.0f); // 绕(1,1,0)轴旋转 glTranslatef(1.0f, 0.0f, 0.0f); // 沿X轴平移 // 绘制第一个立方体 drawCube(); // 对第二个立方体应用不同的模型变换 glPushMatrix(); // 保存当前矩阵状态 glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glTranslatef(-2.0f, 0.0f, 0.0f); // 沿X轴负方向平移 glScalef(0.5f, 0.5f, 0.5f); // 缩小一半 drawCube(); // 绘制第二个立方体 glPopMatrix(); // 恢复矩阵状态 glutSwapBuffers(); // 交换缓冲区 }这里主要介绍三点1、glLoadIdentity()用于矩阵初始化的核心函数作用是将「当前活跃的矩阵」重置为单位矩阵。为什么需要重置为单位矩阵OpenGL 是一个 “状态机”矩阵运算具有累积性—— 每次调用变换函数如glRotate、glTranslate、glScale都是将当前矩阵与新的变换矩阵相乘并将结果作为新的当前矩阵。如果不重置矩阵新的变换会叠加在之前的变换上导致结果不可控。2、 gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可以通过按键调整z值0.0f, 0.0f, 0.0f, // 目标点看向原点0.0f, 1.0f, 0.0f); // 上方向Y轴为上为什么需要定义上方向想象你手里拿着一个相机镜头对准前方的一棵树目标点当你水平握持相机时镜头朝前相机顶部朝上拍出来的照片是正的地平线水平树干垂直当你倾斜握持相机时镜头仍对准树但相机顶部朝左或朝右拍出来的照片会是歪的地平线倾斜树干歪斜。这里的 “相机顶部朝向” 就是现实中的 “上方向”—— 即使镜头对准同一个目标上方向不同最终画面的 “正立状态” 也会不同。3、glutSwapBuffers()核心作用是交换双缓冲模式下的前后缓冲区让后台缓冲区中绘制完成的完整画面显示到屏幕上从而避免绘制过程中的闪烁。当display函数中的所有绘制操作画点、线、立方体等完成后glutSwapBuffers()会执行一次 “缓冲区交换”原本的后台缓冲区已完成当前帧绘制变成前台缓冲区直接显示到屏幕上原本的前台缓冲区变成新的后台缓冲区为下一帧绘制做准备。这个过程瞬间完成用户看不到中间绘制步骤只会看到完整的画面从而消除闪烁。三、reshape函数// 窗口大小变化回调函数 void reshape(int width, int height) { glViewport(0, 0, width, height); // 设置视口占满整个窗口 // 切换到投影矩阵模式 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 重置投影矩阵 // -------------------------- 投影变换 -------------------------- // 使用透视投影 // 参数视野角度、宽高比、近平面距离、远平面距离 float aspectRatio (float)width / (float)height; gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f); // 切换回模型视图矩阵模式 glMatrixMode(GL_MODELVIEW); }矩阵模式切换投影矩阵GL_PROJECTION专门负责 “投影变换”—— 决定 3D 场景如何被 “投影” 到 2D 屏幕上如透视投影的 “近大远小” 效果。在reshape函数中我们需要设置透视投影参数gluPerspective因此必须先切换到GL_PROJECTION模式否则这些参数会错误地应用到其他矩阵上。模型视图矩阵GL_MODELVIEW专门负责 “模型变换”物体的旋转、平移、缩放和 “视图变换”相机的位置和朝向。绝大多数绘制逻辑如display函数中的旋转、gluLookAt视图变换都需要在这个模式下进行因此设置完投影矩阵后必须切换回GL_MODELVIEW模式确保后续的模型 / 视图变换正确生效。透视投影核心作用是让 3D 场景在 2D 屏幕上呈现出符合人类视觉习惯的真实效果通过近大远小的规律模拟深度感完成 3D 到 2D 的坐标转换限定可见区域优化效率同时保证画面比例正确。三、timer函数// 定时器回调函数用于动画效果 void timer(int value) { rotationAngle 1.0f; // 增加旋转角度 if (rotationAngle 360.0f) { rotationAngle 0.0f; } glutPostRedisplay(); // 标记窗口需要重绘 glutTimerFunc(16, timer, 0); // 约60fps }四、键盘回调函数// 键盘回调函数 void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: // ESC键退出 exit(0); break; case : // 相机靠近 cameraZ - 0.5f; if (cameraZ 1.0f) cameraZ 1.0f; break; case -: // 相机远离 cameraZ 0.5f; break; } glutPostRedisplay(); // 重绘 }代码汇总#include GL/glut.h #include GL/glu.h #include math.h // 全局变量 float rotationAngle 0.0f; // 旋转角度 float cameraZ 5.0f; // 相机Z轴位置 // 初始化函数 void init() { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 背景色深灰色 glEnable(GL_DEPTH_TEST); // 启用深度测试处理遮挡关系 glEnable(GL_COLOR_MATERIAL); // 启用颜色材质使颜色生效 } // 绘制彩色立方体 void drawCube() { glBegin(GL_QUADS); // 使用四边形绘制立方体的6个面 // 前面红色 glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); // 后面绿色 glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); // 右面蓝色 glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); // 左面黄色 glColor3f(1.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); // 上面青色 glColor3f(0.0f, 1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, -0.5f); // 下面紫色 glColor3f(1.0f, 0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glEnd(); } // 显示回调函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲区 // 重置模型视图矩阵 glLoadIdentity(); // -------------------------- 视图变换 -------------------------- // 参数相机位置(x,y,z)、目标点(x,y,z)、上方向向量(x,y,z) gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可以通过按键调整z值 0.0f, 0.0f, 0.0f, // 目标点看向原点 0.0f, 1.0f, 0.0f); // 上方向Y轴为上 // -------------------------- 模型变换 -------------------------- glRotatef(rotationAngle, 1.0f, 1.0f, 0.0f); // 绕(1,1,0)轴旋转 glTranslatef(1.0f, 0.0f, 0.0f); // 沿X轴平移 // 绘制第一个立方体 drawCube(); // 对第二个立方体应用不同的模型变换 glPushMatrix(); // 保存当前矩阵状态 glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glTranslatef(-2.0f, 0.0f, 0.0f); // 沿X轴负方向平移 glScalef(0.5f, 0.5f, 0.5f); // 缩小一半 drawCube(); // 绘制第二个立方体 glPopMatrix(); // 恢复矩阵状态 glutSwapBuffers(); // 交换缓冲区 } // 窗口大小变化回调函数 void reshape(int width, int height) { glViewport(0, 0, width, height); // 设置视口占满整个窗口 // 切换到投影矩阵模式 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 重置投影矩阵 // -------------------------- 投影变换 -------------------------- // 使用透视投影 // 参数视野角度、宽高比、近平面距离、远平面距离 float aspectRatio (float)width / (float)height; gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f); // 切换回模型视图矩阵模式 glMatrixMode(GL_MODELVIEW); } // 定时器回调函数用于动画效果 void timer(int value) { rotationAngle 1.0f; // 增加旋转角度 if (rotationAngle 360.0f) { rotationAngle 0.0f; } glutPostRedisplay(); // 标记窗口需要重绘 glutTimerFunc(16, timer, 0); // 约60fps } // 键盘回调函数 void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: // ESC键退出 exit(0); break; case : // 相机靠近 cameraZ - 0.5f; if (cameraZ 1.0f) cameraZ 1.0f; break; case -: // 相机远离 cameraZ 0.5f; break; } glutPostRedisplay(); // 重绘 } // 主函数 int main(int argc, char** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow(OpenGL 矩阵变换示例); init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }第一个任务完成我们接下来看看第二个任务利用函数指定颜色模式。首先我们先定义三种颜色模式。typedef enum { COLOR_MODE_COLORFUL, // 彩色模式默认 COLOR_MODE_GRAYSCALE, // 灰度模式无彩色仅黑白灰 COLOR_MODE_MONO // 单色模式所有面用同一种颜色 } ColorMode;对具体颜色模式进行处理。ColorMode currentColorMode COLOR_MODE_COLORFUL; // 当前激活的颜色模式 float monoColor[3] { 1.0f, 0.5f, 0.0f }; // 单色模式的预设颜色初始为橙色 void setCurrentColor(float r, float g, float b) { float gray 0.0f; // 提前初始化避免case跳过问题 switch (currentColorMode) { case COLOR_MODE_COLORFUL: // 彩色模式直接使用原始颜色 glColor3f(r, g, b); break; case COLOR_MODE_GRAYSCALE: // 灰度模式计算并使用灰度值 gray 0.299f * r 0.587f * g 0.114f * b; glColor3f(gray, gray, gray); break; case COLOR_MODE_MONO: // 单色模式使用统一预设颜色 glColor3f(monoColor[0], monoColor[1], monoColor[2]); break; } }对键盘函数进行改进加入颜色按键选择。// -------------------------- 新增颜色模式切换 -------------------------- case 1: // 1键切换到彩色模式 currentColorMode COLOR_MODE_COLORFUL; glutSetWindowTitle(OpenGL 矩阵变换示例 - 彩色模式); printf(当前颜色模式彩色模式\n); break; case 2: // 2键切换到灰度模式 currentColorMode COLOR_MODE_GRAYSCALE; glutSetWindowTitle(OpenGL 矩阵变换示例 - 灰度模式); printf(当前颜色模式灰度模式\n); break; case 3: // 3键切换到单色模式 currentColorMode COLOR_MODE_MONO; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式橙色); printf(当前颜色模式单色模式可按4/5/6调整颜色\n); break; // 单色模式下调整颜色4加红5加绿6加蓝 case 4: if (currentColorMode COLOR_MODE_MONO) { monoColor[0] (monoColor[0] 0.1f) 1.0f ? 1.0f : monoColor[0] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式红); printf(单色模式红色分量%.1f\n, monoColor[0]); } break; case 5: if (currentColorMode COLOR_MODE_MONO) { monoColor[1] (monoColor[1] 0.1f) 1.0f ? 1.0f : monoColor[1] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式绿); printf(单色模式绿色分量%.1f\n, monoColor[1]); } break; case 6: if (currentColorMode COLOR_MODE_MONO) { monoColor[2] (monoColor[2] 0.1f) 1.0f ? 1.0f : monoColor[2] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式蓝); printf(单色模式蓝色分量%.1f\n, monoColor[2]); } break; }glutMainLoop()调用后 启动了一个 “无限事件循环”不断触发回调函数如display、timer、keyboard重复执行。代码汇总为#include GL/glut.h #include GL/glu.h #include math.h #include stdio.h // 全局变量 float rotationAngle 0.0f; // 旋转角度 float cameraZ 5.0f; // 相机Z轴位置 // -------------------------- 新增颜色模式相关定义 -------------------------- // 1. 颜色模式枚举定义可支持的颜色模式 typedef enum { COLOR_MODE_COLORFUL, // 彩色模式默认 COLOR_MODE_GRAYSCALE, // 灰度模式无彩色仅黑白灰 COLOR_MODE_MONO // 单色模式所有面用同一种颜色 } ColorMode; ColorMode currentColorMode COLOR_MODE_COLORFUL; // 当前激活的颜色模式 float monoColor[3] { 1.0f, 0.5f, 0.0f }; // 单色模式的预设颜色初始为橙色 // 2. 统一颜色处理函数根据当前颜色模式处理原始颜色并设置 // 参数r/g/b - 物体面的原始颜色返回根据模式处理后的颜色 void setCurrentColor(float r, float g, float b) { float gray 0.0f; // 提前初始化避免case跳过问题 switch (currentColorMode) { case COLOR_MODE_COLORFUL: // 彩色模式直接使用原始颜色 glColor3f(r, g, b); break; case COLOR_MODE_GRAYSCALE: // 灰度模式计算并使用灰度值 gray 0.299f * r 0.587f * g 0.114f * b; glColor3f(gray, gray, gray); break; case COLOR_MODE_MONO: // 单色模式使用统一预设颜色 glColor3f(monoColor[0], monoColor[1], monoColor[2]); break; } } // 初始化函数原有逻辑不变新增颜色模式默认提示 void init() { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 背景色深灰色 glEnable(GL_DEPTH_TEST); // 启用深度测试处理遮挡关系 glEnable(GL_COLOR_MATERIAL); // 启用颜色材质使颜色生效 printf(初始化完成当前颜色模式彩色模式\n); printf(操作提示1彩色 2灰度 3单色 4单色红 5单色绿 6单色蓝 ESC退出\n); } // 绘制立方体修改用setCurrentColor替代直接glColor3f void drawCube() { glBegin(GL_QUADS); // 使用四边形绘制立方体的6个面 // 前面原始红色 setCurrentColor(1.0f, 0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); // 后面原始绿色 setCurrentColor(0.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); // 右面原始蓝色 setCurrentColor(0.0f, 0.0f, 1.0f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); // 左面原始黄色 setCurrentColor(1.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); // 上面原始青色 setCurrentColor(0.0f, 1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, -0.5f); // 下面原始紫色 setCurrentColor(1.0f, 0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glEnd(); } // 显示回调函数原有逻辑不变 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲区 // 重置模型视图矩阵 glLoadIdentity(); // -------------------------- 视图变换 -------------------------- gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可通过/-调整 0.0f, 0.0f, 0.0f, // 目标点看向原点 0.0f, 1.0f, 0.0f); // 上方向Y轴为上 // -------------------------- 模型变换 -------------------------- glRotatef(rotationAngle, 1.0f, 1.0f, 0.0f); // 绕(1,1,0)轴旋转 glTranslatef(1.0f, 0.0f, 0.0f); // 沿X轴平移 // 绘制第一个立方体 drawCube(); // 对第二个立方体应用不同的模型变换 glPushMatrix(); // 保存当前矩阵状态 glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glTranslatef(-2.0f, 0.0f, 0.0f); // 沿X轴负方向平移 glScalef(0.5f, 0.5f, 0.5f); // 缩小一半 drawCube(); // 绘制第二个立方体 glPopMatrix(); // 恢复矩阵状态 glutSwapBuffers(); // 交换缓冲区 } // 窗口大小变化回调函数原有逻辑不变 void reshape(int width, int height) { glViewport(0, 0, width, height); // 设置视口占满整个窗口 // 切换到投影矩阵模式 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 重置投影矩阵 // -------------------------- 投影变换 -------------------------- float aspectRatio (float)width / (float)height; gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f); // 透视投影 // 切换回模型视图矩阵模式 glMatrixMode(GL_MODELVIEW); } // 定时器回调函数原有逻辑不变 void timer(int value) { rotationAngle 1.0f; // 增加旋转角度 if (rotationAngle 360.0f) { rotationAngle 0.0f; } glutPostRedisplay(); // 标记窗口需要重绘 glutTimerFunc(16, timer, 0); // 约60fps } // 键盘回调函数修改新增颜色模式切换逻辑 void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: // ESC键退出程序 exit(0); break; case : // 相机靠近 cameraZ - 0.5f; if (cameraZ 1.0f) cameraZ 1.0f; break; case -: // 相机远离 cameraZ 0.5f; break; // -------------------------- 新增颜色模式切换 -------------------------- case 1: // 1键切换到彩色模式 currentColorMode COLOR_MODE_COLORFUL; glutSetWindowTitle(OpenGL 矩阵变换示例 - 彩色模式); printf(当前颜色模式彩色模式\n); break; case 2: // 2键切换到灰度模式 currentColorMode COLOR_MODE_GRAYSCALE; glutSetWindowTitle(OpenGL 矩阵变换示例 - 灰度模式); printf(当前颜色模式灰度模式\n); break; case 3: // 3键切换到单色模式 currentColorMode COLOR_MODE_MONO; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式橙色); printf(当前颜色模式单色模式可按4/5/6调整颜色\n); break; // 单色模式下调整颜色4加红5加绿6加蓝 case 4: if (currentColorMode COLOR_MODE_MONO) { monoColor[0] (monoColor[0] 0.1f) 1.0f ? 1.0f : monoColor[0] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式红); printf(单色模式红色分量%.1f\n, monoColor[0]); } break; case 5: if (currentColorMode COLOR_MODE_MONO) { monoColor[1] (monoColor[1] 0.1f) 1.0f ? 1.0f : monoColor[1] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式绿); printf(单色模式绿色分量%.1f\n, monoColor[1]); } break; case 6: if (currentColorMode COLOR_MODE_MONO) { monoColor[2] (monoColor[2] 0.1f) 1.0f ? 1.0f : monoColor[2] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式蓝); printf(单色模式蓝色分量%.1f\n, monoColor[2]); } break; } glutPostRedisplay(); // 切换后触发重绘 } // 主函数原有逻辑不变 int main(int argc, char** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow(OpenGL 矩阵变换示例 - 彩色模式); init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }结果为
OpenGL学习(二)
目标OpenGL 中的坐标变换和颜色的使用。具体任务利用矩阵操作实现视图变换、模型变换和投影变换利用函数指定颜色模式。三种变换模型变换改变物体自身的位置、旋转、缩放比如让立方体向左移动、旋转。视图变换改变观察角度相机位置和朝向不改变物体本身只改变 “从哪里看”。投影变换定义画面的透视效果比如近大远小而视图变换只决定观察方向。代码解释一、主函数// 主函数 int main(int argc, char** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow(OpenGL 矩阵变换示例); init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }对比上节代码区别在于1、glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB |GLUT_DEPTH);开启深度缓存的作用在于每当绘制新像素时OpenGL 会自动比较新像素与已有像素的深度值如果新像素更近深度值更小则更新颜色缓冲区和深度缓冲区显示新像素如果新像素更远则忽略该像素不覆盖已有像素。2、glutReshapeFunc(reshape);当窗口被拉伸、缩放或首次创建时FreeGLUT 会自动调用这个函数。由于我们将在该节中使用透视投影代码如下透视投影与窗口的宽高比密切相关若窗口大小改变而不及时去更改width和height的值可能导致图像投影后失真。gluPerspective(45.0f, (float)width / (float)height, 0.1f, 100.0f);可是问题是为什么窗口会改变呢我们并不会在代码中去更改窗口大小但是用户可以通过拉伸窗口更改其宽高。为了防止图像失真需要主动调用。3、glutTimerFunc(0, timer, 0);如果不注册glutTimerFunc(0, timer, 0);就无法自动更新场景状态和触发重绘动画会静止不动除非通过用户交互手动触发。二、display函数// 显示回调函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲区 // 重置模型视图矩阵 glLoadIdentity(); // -------------------------- 视图变换 -------------------------- // 参数相机位置(x,y,z)、目标点(x,y,z)、上方向向量(x,y,z) gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可以通过按键调整z值 0.0f, 0.0f, 0.0f, // 目标点看向原点 0.0f, 1.0f, 0.0f); // 上方向Y轴为上 // -------------------------- 模型变换 -------------------------- glRotatef(rotationAngle, 1.0f, 1.0f, 0.0f); // 绕(1,1,0)轴旋转 glTranslatef(1.0f, 0.0f, 0.0f); // 沿X轴平移 // 绘制第一个立方体 drawCube(); // 对第二个立方体应用不同的模型变换 glPushMatrix(); // 保存当前矩阵状态 glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glTranslatef(-2.0f, 0.0f, 0.0f); // 沿X轴负方向平移 glScalef(0.5f, 0.5f, 0.5f); // 缩小一半 drawCube(); // 绘制第二个立方体 glPopMatrix(); // 恢复矩阵状态 glutSwapBuffers(); // 交换缓冲区 }这里主要介绍三点1、glLoadIdentity()用于矩阵初始化的核心函数作用是将「当前活跃的矩阵」重置为单位矩阵。为什么需要重置为单位矩阵OpenGL 是一个 “状态机”矩阵运算具有累积性—— 每次调用变换函数如glRotate、glTranslate、glScale都是将当前矩阵与新的变换矩阵相乘并将结果作为新的当前矩阵。如果不重置矩阵新的变换会叠加在之前的变换上导致结果不可控。2、 gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可以通过按键调整z值0.0f, 0.0f, 0.0f, // 目标点看向原点0.0f, 1.0f, 0.0f); // 上方向Y轴为上为什么需要定义上方向想象你手里拿着一个相机镜头对准前方的一棵树目标点当你水平握持相机时镜头朝前相机顶部朝上拍出来的照片是正的地平线水平树干垂直当你倾斜握持相机时镜头仍对准树但相机顶部朝左或朝右拍出来的照片会是歪的地平线倾斜树干歪斜。这里的 “相机顶部朝向” 就是现实中的 “上方向”—— 即使镜头对准同一个目标上方向不同最终画面的 “正立状态” 也会不同。3、glutSwapBuffers()核心作用是交换双缓冲模式下的前后缓冲区让后台缓冲区中绘制完成的完整画面显示到屏幕上从而避免绘制过程中的闪烁。当display函数中的所有绘制操作画点、线、立方体等完成后glutSwapBuffers()会执行一次 “缓冲区交换”原本的后台缓冲区已完成当前帧绘制变成前台缓冲区直接显示到屏幕上原本的前台缓冲区变成新的后台缓冲区为下一帧绘制做准备。这个过程瞬间完成用户看不到中间绘制步骤只会看到完整的画面从而消除闪烁。三、reshape函数// 窗口大小变化回调函数 void reshape(int width, int height) { glViewport(0, 0, width, height); // 设置视口占满整个窗口 // 切换到投影矩阵模式 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 重置投影矩阵 // -------------------------- 投影变换 -------------------------- // 使用透视投影 // 参数视野角度、宽高比、近平面距离、远平面距离 float aspectRatio (float)width / (float)height; gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f); // 切换回模型视图矩阵模式 glMatrixMode(GL_MODELVIEW); }矩阵模式切换投影矩阵GL_PROJECTION专门负责 “投影变换”—— 决定 3D 场景如何被 “投影” 到 2D 屏幕上如透视投影的 “近大远小” 效果。在reshape函数中我们需要设置透视投影参数gluPerspective因此必须先切换到GL_PROJECTION模式否则这些参数会错误地应用到其他矩阵上。模型视图矩阵GL_MODELVIEW专门负责 “模型变换”物体的旋转、平移、缩放和 “视图变换”相机的位置和朝向。绝大多数绘制逻辑如display函数中的旋转、gluLookAt视图变换都需要在这个模式下进行因此设置完投影矩阵后必须切换回GL_MODELVIEW模式确保后续的模型 / 视图变换正确生效。透视投影核心作用是让 3D 场景在 2D 屏幕上呈现出符合人类视觉习惯的真实效果通过近大远小的规律模拟深度感完成 3D 到 2D 的坐标转换限定可见区域优化效率同时保证画面比例正确。三、timer函数// 定时器回调函数用于动画效果 void timer(int value) { rotationAngle 1.0f; // 增加旋转角度 if (rotationAngle 360.0f) { rotationAngle 0.0f; } glutPostRedisplay(); // 标记窗口需要重绘 glutTimerFunc(16, timer, 0); // 约60fps }四、键盘回调函数// 键盘回调函数 void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: // ESC键退出 exit(0); break; case : // 相机靠近 cameraZ - 0.5f; if (cameraZ 1.0f) cameraZ 1.0f; break; case -: // 相机远离 cameraZ 0.5f; break; } glutPostRedisplay(); // 重绘 }代码汇总#include GL/glut.h #include GL/glu.h #include math.h // 全局变量 float rotationAngle 0.0f; // 旋转角度 float cameraZ 5.0f; // 相机Z轴位置 // 初始化函数 void init() { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 背景色深灰色 glEnable(GL_DEPTH_TEST); // 启用深度测试处理遮挡关系 glEnable(GL_COLOR_MATERIAL); // 启用颜色材质使颜色生效 } // 绘制彩色立方体 void drawCube() { glBegin(GL_QUADS); // 使用四边形绘制立方体的6个面 // 前面红色 glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); // 后面绿色 glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); // 右面蓝色 glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); // 左面黄色 glColor3f(1.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); // 上面青色 glColor3f(0.0f, 1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, -0.5f); // 下面紫色 glColor3f(1.0f, 0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glEnd(); } // 显示回调函数 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲区 // 重置模型视图矩阵 glLoadIdentity(); // -------------------------- 视图变换 -------------------------- // 参数相机位置(x,y,z)、目标点(x,y,z)、上方向向量(x,y,z) gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可以通过按键调整z值 0.0f, 0.0f, 0.0f, // 目标点看向原点 0.0f, 1.0f, 0.0f); // 上方向Y轴为上 // -------------------------- 模型变换 -------------------------- glRotatef(rotationAngle, 1.0f, 1.0f, 0.0f); // 绕(1,1,0)轴旋转 glTranslatef(1.0f, 0.0f, 0.0f); // 沿X轴平移 // 绘制第一个立方体 drawCube(); // 对第二个立方体应用不同的模型变换 glPushMatrix(); // 保存当前矩阵状态 glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glTranslatef(-2.0f, 0.0f, 0.0f); // 沿X轴负方向平移 glScalef(0.5f, 0.5f, 0.5f); // 缩小一半 drawCube(); // 绘制第二个立方体 glPopMatrix(); // 恢复矩阵状态 glutSwapBuffers(); // 交换缓冲区 } // 窗口大小变化回调函数 void reshape(int width, int height) { glViewport(0, 0, width, height); // 设置视口占满整个窗口 // 切换到投影矩阵模式 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 重置投影矩阵 // -------------------------- 投影变换 -------------------------- // 使用透视投影 // 参数视野角度、宽高比、近平面距离、远平面距离 float aspectRatio (float)width / (float)height; gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f); // 切换回模型视图矩阵模式 glMatrixMode(GL_MODELVIEW); } // 定时器回调函数用于动画效果 void timer(int value) { rotationAngle 1.0f; // 增加旋转角度 if (rotationAngle 360.0f) { rotationAngle 0.0f; } glutPostRedisplay(); // 标记窗口需要重绘 glutTimerFunc(16, timer, 0); // 约60fps } // 键盘回调函数 void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: // ESC键退出 exit(0); break; case : // 相机靠近 cameraZ - 0.5f; if (cameraZ 1.0f) cameraZ 1.0f; break; case -: // 相机远离 cameraZ 0.5f; break; } glutPostRedisplay(); // 重绘 } // 主函数 int main(int argc, char** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow(OpenGL 矩阵变换示例); init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }第一个任务完成我们接下来看看第二个任务利用函数指定颜色模式。首先我们先定义三种颜色模式。typedef enum { COLOR_MODE_COLORFUL, // 彩色模式默认 COLOR_MODE_GRAYSCALE, // 灰度模式无彩色仅黑白灰 COLOR_MODE_MONO // 单色模式所有面用同一种颜色 } ColorMode;对具体颜色模式进行处理。ColorMode currentColorMode COLOR_MODE_COLORFUL; // 当前激活的颜色模式 float monoColor[3] { 1.0f, 0.5f, 0.0f }; // 单色模式的预设颜色初始为橙色 void setCurrentColor(float r, float g, float b) { float gray 0.0f; // 提前初始化避免case跳过问题 switch (currentColorMode) { case COLOR_MODE_COLORFUL: // 彩色模式直接使用原始颜色 glColor3f(r, g, b); break; case COLOR_MODE_GRAYSCALE: // 灰度模式计算并使用灰度值 gray 0.299f * r 0.587f * g 0.114f * b; glColor3f(gray, gray, gray); break; case COLOR_MODE_MONO: // 单色模式使用统一预设颜色 glColor3f(monoColor[0], monoColor[1], monoColor[2]); break; } }对键盘函数进行改进加入颜色按键选择。// -------------------------- 新增颜色模式切换 -------------------------- case 1: // 1键切换到彩色模式 currentColorMode COLOR_MODE_COLORFUL; glutSetWindowTitle(OpenGL 矩阵变换示例 - 彩色模式); printf(当前颜色模式彩色模式\n); break; case 2: // 2键切换到灰度模式 currentColorMode COLOR_MODE_GRAYSCALE; glutSetWindowTitle(OpenGL 矩阵变换示例 - 灰度模式); printf(当前颜色模式灰度模式\n); break; case 3: // 3键切换到单色模式 currentColorMode COLOR_MODE_MONO; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式橙色); printf(当前颜色模式单色模式可按4/5/6调整颜色\n); break; // 单色模式下调整颜色4加红5加绿6加蓝 case 4: if (currentColorMode COLOR_MODE_MONO) { monoColor[0] (monoColor[0] 0.1f) 1.0f ? 1.0f : monoColor[0] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式红); printf(单色模式红色分量%.1f\n, monoColor[0]); } break; case 5: if (currentColorMode COLOR_MODE_MONO) { monoColor[1] (monoColor[1] 0.1f) 1.0f ? 1.0f : monoColor[1] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式绿); printf(单色模式绿色分量%.1f\n, monoColor[1]); } break; case 6: if (currentColorMode COLOR_MODE_MONO) { monoColor[2] (monoColor[2] 0.1f) 1.0f ? 1.0f : monoColor[2] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式蓝); printf(单色模式蓝色分量%.1f\n, monoColor[2]); } break; }glutMainLoop()调用后 启动了一个 “无限事件循环”不断触发回调函数如display、timer、keyboard重复执行。代码汇总为#include GL/glut.h #include GL/glu.h #include math.h #include stdio.h // 全局变量 float rotationAngle 0.0f; // 旋转角度 float cameraZ 5.0f; // 相机Z轴位置 // -------------------------- 新增颜色模式相关定义 -------------------------- // 1. 颜色模式枚举定义可支持的颜色模式 typedef enum { COLOR_MODE_COLORFUL, // 彩色模式默认 COLOR_MODE_GRAYSCALE, // 灰度模式无彩色仅黑白灰 COLOR_MODE_MONO // 单色模式所有面用同一种颜色 } ColorMode; ColorMode currentColorMode COLOR_MODE_COLORFUL; // 当前激活的颜色模式 float monoColor[3] { 1.0f, 0.5f, 0.0f }; // 单色模式的预设颜色初始为橙色 // 2. 统一颜色处理函数根据当前颜色模式处理原始颜色并设置 // 参数r/g/b - 物体面的原始颜色返回根据模式处理后的颜色 void setCurrentColor(float r, float g, float b) { float gray 0.0f; // 提前初始化避免case跳过问题 switch (currentColorMode) { case COLOR_MODE_COLORFUL: // 彩色模式直接使用原始颜色 glColor3f(r, g, b); break; case COLOR_MODE_GRAYSCALE: // 灰度模式计算并使用灰度值 gray 0.299f * r 0.587f * g 0.114f * b; glColor3f(gray, gray, gray); break; case COLOR_MODE_MONO: // 单色模式使用统一预设颜色 glColor3f(monoColor[0], monoColor[1], monoColor[2]); break; } } // 初始化函数原有逻辑不变新增颜色模式默认提示 void init() { glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // 背景色深灰色 glEnable(GL_DEPTH_TEST); // 启用深度测试处理遮挡关系 glEnable(GL_COLOR_MATERIAL); // 启用颜色材质使颜色生效 printf(初始化完成当前颜色模式彩色模式\n); printf(操作提示1彩色 2灰度 3单色 4单色红 5单色绿 6单色蓝 ESC退出\n); } // 绘制立方体修改用setCurrentColor替代直接glColor3f void drawCube() { glBegin(GL_QUADS); // 使用四边形绘制立方体的6个面 // 前面原始红色 setCurrentColor(1.0f, 0.0f, 0.0f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); // 后面原始绿色 setCurrentColor(0.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); // 右面原始蓝色 setCurrentColor(0.0f, 0.0f, 1.0f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, 0.5f, -0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, -0.5f, 0.5f); // 左面原始黄色 setCurrentColor(1.0f, 1.0f, 0.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(-0.5f, 0.5f, -0.5f); // 上面原始青色 setCurrentColor(0.0f, 1.0f, 1.0f); glVertex3f(-0.5f, 0.5f, -0.5f); glVertex3f(-0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, 0.5f); glVertex3f(0.5f, 0.5f, -0.5f); // 下面原始紫色 setCurrentColor(1.0f, 0.0f, 1.0f); glVertex3f(-0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, -0.5f); glVertex3f(0.5f, -0.5f, 0.5f); glVertex3f(-0.5f, -0.5f, 0.5f); glEnd(); } // 显示回调函数原有逻辑不变 void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓冲区 // 重置模型视图矩阵 glLoadIdentity(); // -------------------------- 视图变换 -------------------------- gluLookAt(0.0f, 1.0f, cameraZ, // 相机位置可通过/-调整 0.0f, 0.0f, 0.0f, // 目标点看向原点 0.0f, 1.0f, 0.0f); // 上方向Y轴为上 // -------------------------- 模型变换 -------------------------- glRotatef(rotationAngle, 1.0f, 1.0f, 0.0f); // 绕(1,1,0)轴旋转 glTranslatef(1.0f, 0.0f, 0.0f); // 沿X轴平移 // 绘制第一个立方体 drawCube(); // 对第二个立方体应用不同的模型变换 glPushMatrix(); // 保存当前矩阵状态 glRotatef(rotationAngle, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转 glTranslatef(-2.0f, 0.0f, 0.0f); // 沿X轴负方向平移 glScalef(0.5f, 0.5f, 0.5f); // 缩小一半 drawCube(); // 绘制第二个立方体 glPopMatrix(); // 恢复矩阵状态 glutSwapBuffers(); // 交换缓冲区 } // 窗口大小变化回调函数原有逻辑不变 void reshape(int width, int height) { glViewport(0, 0, width, height); // 设置视口占满整个窗口 // 切换到投影矩阵模式 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 重置投影矩阵 // -------------------------- 投影变换 -------------------------- float aspectRatio (float)width / (float)height; gluPerspective(45.0f, aspectRatio, 0.1f, 100.0f); // 透视投影 // 切换回模型视图矩阵模式 glMatrixMode(GL_MODELVIEW); } // 定时器回调函数原有逻辑不变 void timer(int value) { rotationAngle 1.0f; // 增加旋转角度 if (rotationAngle 360.0f) { rotationAngle 0.0f; } glutPostRedisplay(); // 标记窗口需要重绘 glutTimerFunc(16, timer, 0); // 约60fps } // 键盘回调函数修改新增颜色模式切换逻辑 void keyboard(unsigned char key, int x, int y) { switch (key) { case 27: // ESC键退出程序 exit(0); break; case : // 相机靠近 cameraZ - 0.5f; if (cameraZ 1.0f) cameraZ 1.0f; break; case -: // 相机远离 cameraZ 0.5f; break; // -------------------------- 新增颜色模式切换 -------------------------- case 1: // 1键切换到彩色模式 currentColorMode COLOR_MODE_COLORFUL; glutSetWindowTitle(OpenGL 矩阵变换示例 - 彩色模式); printf(当前颜色模式彩色模式\n); break; case 2: // 2键切换到灰度模式 currentColorMode COLOR_MODE_GRAYSCALE; glutSetWindowTitle(OpenGL 矩阵变换示例 - 灰度模式); printf(当前颜色模式灰度模式\n); break; case 3: // 3键切换到单色模式 currentColorMode COLOR_MODE_MONO; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式橙色); printf(当前颜色模式单色模式可按4/5/6调整颜色\n); break; // 单色模式下调整颜色4加红5加绿6加蓝 case 4: if (currentColorMode COLOR_MODE_MONO) { monoColor[0] (monoColor[0] 0.1f) 1.0f ? 1.0f : monoColor[0] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式红); printf(单色模式红色分量%.1f\n, monoColor[0]); } break; case 5: if (currentColorMode COLOR_MODE_MONO) { monoColor[1] (monoColor[1] 0.1f) 1.0f ? 1.0f : monoColor[1] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式绿); printf(单色模式绿色分量%.1f\n, monoColor[1]); } break; case 6: if (currentColorMode COLOR_MODE_MONO) { monoColor[2] (monoColor[2] 0.1f) 1.0f ? 1.0f : monoColor[2] 0.1f; glutSetWindowTitle(OpenGL 矩阵变换示例 - 单色模式蓝); printf(单色模式蓝色分量%.1f\n, monoColor[2]); } break; } glutPostRedisplay(); // 切换后触发重绘 } // 主函数原有逻辑不变 int main(int argc, char** argv) { glutInit(argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow(OpenGL 矩阵变换示例 - 彩色模式); init(); // 注册回调函数 glutDisplayFunc(display); glutReshapeFunc(reshape); glutTimerFunc(0, timer, 0); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }结果为