目 录
1 实验目的和内容
1.1实验目的
1.2实验内容
2 基本原理
3 主要仪器与设备
4 实验步骤/数据处理与结果
Step1:开发环境配置与项目创建
Step2:头文件参数定义
Step3:纹理映射底层实现
Step4:灯光与材质实现
Step5:3D房屋场景实现
Step6:旋转地球、旋转立方功能实现
Step7:交互功能实现
Step8:调试优化
Step9:编译运行与效果展示
1 实验目的和内容
1.1实验目的
(1)理解纹理映射就是把具有某种表面纹理的图像粘贴到物体表面,从而获得具有真实感效果的物体模型;(2)掌握纹理映射的步骤:①指定纹理;②指定纹理应用的方式;③激活纹理映射;④指定纹理坐标和几何坐标,绘制场景。
1.2实验内容
(1)设计一个有实际意义的三维几何形体场景;(2)为中心三维几何形体贴上不同的纹理,使场景表现具有真实感;(3)为场景设置灯光,添加光照效果;(4)在中心三维几何形体纹理贴图上设置材质属性,进一步增强物体真实质感。
2 基本原理
纹理映射:纹理映射(Texture Mapping)是计算机图形学中用于增强三维模型表面细节和真实感的关键技术,其核心是将二维纹理图像映射到三维几何表面。
3 主要仪器与设备
Visual Studio 2010、OpenGL函数库、BMP文件
4 实验步骤/数据处理与结果
Step1:开发环境配置与项目创建
Step2:头文件参数定义
定义场景枚举、全局参数(纹理ID、模型尺寸、灯光、材质、交互参数等),声明所有自定义函数,为main.cpp的实现提供支撑。
Step3:纹理映射底层实现
纹理映射的底层核心是“加载纹理→配置纹理→激活纹理→绑定坐标”,设计两个核心函数,实现从纹理加载到应用的完整流程。
// 加载单张纹理,实现纹理映射第一步(指定纹理)、第二步(配置纹理应用方式) GLuint LoadSingleTex(TCHAR* path){ GLuint texId = 0; // 调用GLAux库函数加载bmp纹理,判断图片有效性 AUX_RGBImageRec* img = auxDIBImageLoad(path); if(!img || !img->data){ if(img) free(img); return texId; // 加载失败返回空ID,避免底层异常 } // 生成纹理,绑定纹理ID glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); // 纹理过滤、环绕模式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // 将纹理数据传入,完成纹理指定 glTexImage2D(GL_TEXTURE_2D, 0, 3, img->sizeX, img->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, img->data); // 释放图片资源,避免内存泄漏 free(img->data); free(img); return texId; } // 批量加载所有纹理,调用单纹理加载函数,简化底层代码 void LoadAllTex(){ texDeskTop = LoadSingleTex(szDeskTop); texDeskLeg = LoadSingleTex(szDeskLeg); texFootball = LoadSingleTex(_T("football.bmp")); texFrame = LoadSingleTex(_T("frame.bmp")); texPainting = LoadSingleTex(_T("painting.bmp")); texLampBase = LoadSingleTex(_T("lamp_base.bmp")); texLampShade = LoadSingleTex(_T("lamp_shade.bmp")); texEarth = LoadSingleTex(szEarth); for(int i=0; i<6; i++){ texCube[i] = LoadSingleTex(szCube[i]); } }Step4:灯光与材质实现
灯光与材质的底层逻辑是“配置参数→开启功能→绑定到模型”,通过OpenGL函数配置光照和材质参数,让模型呈现明暗对比和真实质感。
// 设置太阳光照,配置光照参数,开启光照功能 void SetSunLight(){ GLfloat lightDiffuse[] = {0.8f, 0.8f, 0.8f, 1.0f}; // 漫反射光(模拟太阳光) GLfloat lightSpecular[] = {1.0f, 1.0f, 1.0f, 1.0f}; // 镜面反射光(配合材质) GLfloat lightPosition[] = {5.0f, 8.0f, 5.0f, 1.0f}; // 光源位置(斜上方照射) // 配置光照参数,绑定到0号光源 glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular); glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); // 开启光照功能,让配置生效 glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } // 设置书桌(中心形体)材质,将材质参数绑定到模型,增强真实质感 void SetDeskMaterial(){ // 配置材质的环境光、漫反射、镜面反射参数,绑定到模型正面 glMaterialfv(GL_FRONT, GL_AMBIENT, deskMatAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, deskMatDiffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, deskMatSpecular); glMaterialfv(GL_FRONT, GL_SHININESS, deskMatShininess); }Step5:3D房屋场景实现
按场景标识,调用对应绘制函数,实现纹理、灯光、材质的整合应用,绘制中心形体及辅助形体,整合纹理、灯光、材质,实现室内书桌场景。
// 绘制书桌(中心形体),实现纹理映射第三步(激活纹理)、第四步(绑定坐标) void DrawDesk(){ glEnable(GL_TEXTURE_2D); // 激活纹理映射(第三步) SetDeskMaterial(); // 绑定书桌材质,与灯光交互 // 绘制书桌桌面(木质纹理) glBindTexture(GL_TEXTURE_2D, texDeskTop); glBegin(GL_QUADS); glNormal3f(0.0f, 1.0f, 0.0f); // 绑定纹理坐标与几何坐标(第四步),一一对应,确保纹理贴合 glTexCoord2f(0.0f, 0.0f); glVertex3f(-DESK_TOP_SIZE/2, DESK_HEIGHT, -DESK_TOP_SIZE/2); glTexCoord2f(1.0f, 0.0f); glVertex3f(DESK_TOP_SIZE/2, DESK_HEIGHT, -DESK_TOP_SIZE/2); glTexCoord2f(1.0f, 1.0f); glVertex3f(DESK_TOP_SIZE/2, DESK_HEIGHT, DESK_TOP_SIZE/2); glTexCoord2f(0.0f, 1.0f); glVertex3f(-DESK_TOP_SIZE/2, DESK_HEIGHT, DESK_TOP_SIZE/2); glEnd(); // 绘制书桌桌腿 glBindTexture(GL_TEXTURE_2D, texDeskLeg); for(int i=0; i<4; i++){ glPushMatrix(); //调整桌腿位置,分布在桌面四角 if(i==0) glTranslatef(-DESK_TOP_SIZE/2 + DESK_LEG_SIZE/2, DESK_HEIGHT/2, -DESK_TOP_SIZE/2 + DESK_LEG_SIZE/2); if(i==1) glTranslatef(DESK_TOP_SIZE/2 - DESK_LEG_SIZE/2, DESK_HEIGHT/2, -DESK_TOP_SIZE/2 + DESK_LEG_SIZE/2); if(i==2) glTranslatef(-DESK_TOP_SIZE/2 + DESK_LEG_SIZE/2, DESK_HEIGHT/2, DESK_TOP_SIZE/2 - DESK_LEG_SIZE/2); if(i==3) glTranslatef(DESK_TOP_SIZE/2 - DESK_LEG_SIZE/2, DESK_HEIGHT/2, DESK_TOP_SIZE/2 - DESK_LEG_SIZE/2); // 绘制桌腿 glBegin(GL_QUADS); glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(0, 0, 0); glTexCoord2f(1.0f, 0.0f); glVertex3f(DESK_LEG_SIZE, 0, 0); glTexCoord2f(1.0f, 1.0f); glVertex3f(DESK_LEG_SIZE, DESK_HEIGHT, 0); glTexCoord2f(0.0f, 1.0f); glVertex3f(0, DESK_HEIGHT, 0); // 其余5个面绘制逻辑 glEnd(); glPopMatrix(); // 恢复矩阵 } glDisable(GL_TEXTURE_2D); // 关闭纹理映射,避免影响其他物体 } // 绘制辅助形体(足球、相框、台灯),底层逻辑与书桌一致,整合纹理和灯光 void DrawFootball(){ glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texFootball); GLUquadricObj* ballObj = gluNewQuadric(); // 初始化二次曲面,绘制球体 gluQuadricNormals(ballObj, GLU_SMOOTH); // 平滑法线,提升光照效果 gluQuadricTexture(ballObj, GL_TRUE); // 启用纹理映射 glTranslatef(DESK_TOP_SIZE/4, DESK_HEIGHT + 0.3f, DESK_TOP_SIZE/4); gluSphere(ballObj, 0.3f, 80, 80); // 绘制球体,自动绑定纹理坐标 glDisable(GL_TEXTURE_2D); gluDeleteQuadric(ballObj); // 释放资源 } void DrawFrame(){······} void DrawLamp(){······} // 整合所有形体,绘制完整核心场景,设置相机视角 void DrawDeskScene(){ gluLookAt(0.0f, 1.5f, 3.0f*scale, 0.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f); // 相机视角 glRotatef(rotX, 1.0f, 0.0f, 0.0f); // 应用旋转 glRotatef(rotY, 0.0f, 1.0f, 0.0f); DrawDesk(); DrawFootball(); DrawFrame(); DrawLamp(); }Step6:旋转地球、旋转立方功能实现
实现“旋转立方”与“旋转地球”功能,并优化渲染,实现自动旋转。
// 绘制旋转地球,贴地球纹理,实现自动自转 void DrawEarth(){ glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texEarth); GLUquadricObj* earthObj = gluNewQuadric(); gluQuadricNormals(earthObj, GLU_SMOOTH); gluQuadricTexture(earthObj, GL_TRUE); glPushMatrix(); glRotatef(dayRot, 0.0f, 0.0f, 1.0f); // 应用自转角度 gluSphere(earthObj, EARTH_RADIUS, 80, 80); glPopMatrix(); glDisable(GL_TEXTURE_2D); gluDeleteQuadric(earthObj); } // 用显示列表绘制旋转立方 void CreateCubeList(void){ cubeList = glGenLists(1); // 创建显示列表 glNewList(cubeList, GL_COMPILE); // 编译绘制逻辑 glEnable(GL_TEXTURE_2D); // 立方6个面,分别绑定不同纹理,绑定纹理坐标与几何坐标 glBindTexture(GL_TEXTURE_2D, texCube[0]); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glEnd(); // 其余5个面绘制逻辑 glDisable(GL_TEXTURE_2D); glEndList(); // 结束编译,后续直接调用即可 }Step7:交互功能实现
交互功能的底层核心是“回调函数绑定”,通过GLUT库绑定鼠标、菜单、定时器回调,修改全局参数,刷新场景,实现交互效果。
// 右键菜单回调,修改场景标识,实现场景切换 void MenuFunc(int value){ currentScene = (SceneType)value; // 修改当前场景标识 glutPostRedisplay(); // 刷新场景,让切换生效 } // 鼠标操作回调,记录鼠标状态,实现滚轮缩放 void Mouse(int btn,int state,int x,int y){ if(currentScene == SCENE_DESK){ // 仅核心场景生效 if(btn == GLUT_LEFT_BUTTON){ mouseDown = (state == GLUT_DOWN); // 记录左键是否按下 lastX = x; lastY = y; // 记录鼠标初始位置 } // 修改缩放参数,限制范围 if(btn == 3 || btn == 4){ scale += (btn == 3) ? 0.1f : -0.1f; scale = (scale < 0.3f) ? 0.3f : scale; scale = (scale > 2.0f) ? 2.0f : scale; glutPostRedisplay(); } } } // 鼠标拖动回调,修改旋转参数,实现场景旋转 void Motion(int x,int y){ if(currentScene == SCENE_DESK && mouseDown){ rotY += (x - lastX) * 0.3f; // 水平拖动,修改y轴旋转角度 rotX += (y - lastY) * 0.3f; // 垂直拖动,修改x轴旋转角度 lastX = x; lastY = y; glutPostRedisplay(); } } // 定时器回调,修改旋转角度,实现自动旋转 void OnTimer(int value){ dayRot += 5.0f; // 地球自转角度递增 if(dayRot > 360.0f) dayRot = 0.0f; xRotCube += 1.0f; yRotCube += 1.5f; zRotCube += 0.8f; // 立方旋转角度递增 glutPostRedisplay(); glutTimerFunc(33, OnTimer, 1); // 33ms循环调用,实现流畅旋转 } // 核心显示回调,根据场景标识绘制对应场景,整合所有绘制逻辑 void OnDisplay(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除缓存,避免叠加 glLoadIdentity(); // 重置矩阵,避免异常 switch(currentScene){ case SCENE_DESK: DrawDeskScene(); break; case SCENE_EARTH: gluLookAt(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); DrawEarth(); break; case SCENE_CUBE: glTranslatef(0.0f, 0.0f, -5.0f); glRotatef(xRotCube, 1.0f, 0.0f, 0.0f); glRotatef(yRotCube, 0.0f, 1.0f, 0.0f); glRotatef(zRotCube, 0.0f, 0.0f, 1.0f); glCallList(cubeList); // 调用显示列表,绘制立方 break; } glutSwapBuffers(); // 双缓冲切换,避免绘制闪烁 } // 初始化函数,统一初始化所有资源,程序启动后最先执行 void Init(){ glShadeModel(GL_SMOOTH); glClearColor(0.1f, 0.1f, 0.1f, 1); glEnable(GL_DEPTH_TEST); LoadAllTex(); SetSunLight(); CreateCubeList(); } // 主函数,程序入口,绑定回调函数,启动消息循环 int main(int argc,char* argv[]){ glutInit(&argc,argv); glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH); glutInitWindowSize(1280,720); glutCreateWindow("3D场景纹理映射实验"); Init(); // 初始化资源 // 绑定右键菜单,实现场景切换 glutCreateMenu(MenuFunc); glutAddMenuEntry("室内书桌场景", SCENE_DESK); glutAddMenuEntry("旋转地球场景", SCENE_EARTH); glutAddMenuEntry("旋转立方场景", SCENE_CUBE); glutAttachMenu(GLUT_RIGHT_BUTTON); // 绑定所有回调函数 glutDisplayFunc(OnDisplay); glutMouseFunc(Mouse); glutMotionFunc(Motion); glutTimerFunc(33, OnTimer, 1); glutMainLoop(); // 启动消息循环,持续响应交互 return 0; }Step8:调试优化
排查纹理路径、命名、格式是否符合底层要求,修改LoadSingleTex函数中的图片有效性判断,避免空指针闪退;检查回调函数是否正确绑定,场景标识判断是否准确,修改回调函数中的参数传递逻辑;优化显示列表(立方绘制)、纹理过滤参数,减少重复编译和绘制操作,提升底层渲染效率。
Step9:编译运行与效果展示
图 3D房屋
图 3D房屋旋转
图 旋转地球
图 旋转立方