{"id":15942121,"url":"https://github.com/discretetom/simple-3d-room","last_synced_at":"2025-03-30T04:22:15.946Z","repository":{"id":114055582,"uuid":"189316944","full_name":"DiscreteTom/simple-3D-room","owner":"DiscreteTom","description":"homework of computer graphics, coding with cpp and opengl","archived":false,"fork":false,"pushed_at":"2019-06-06T12:44:56.000Z","size":530,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-05T06:38:58.050Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DiscreteTom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-30T00:17:43.000Z","updated_at":"2019-07-01T06:41:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"deb6c799-9e08-4ea6-914e-cc48e5ac2517","html_url":"https://github.com/DiscreteTom/simple-3D-room","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiscreteTom%2Fsimple-3D-room","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiscreteTom%2Fsimple-3D-room/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiscreteTom%2Fsimple-3D-room/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DiscreteTom%2Fsimple-3D-room/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DiscreteTom","download_url":"https://codeload.github.com/DiscreteTom/simple-3D-room/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246274636,"owners_count":20751111,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-07T07:40:24.490Z","updated_at":"2025-03-30T04:22:15.929Z","avatar_url":"https://github.com/DiscreteTom.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# simple-3D-room\n\n## 题目\n\n试设计一个室内三维环境, 并利用OPEN GL展示它的三维效果。要求：\n1. 包含基本的实体元素：球、多面体、锥体、柱体、曲面等\n2. 有全局光照效果和纹理功能\n3. 程序具有交互功能\n\n## 环境\n\n- IDE - VS2017\n- 语言 - C++\n- 三方库\n  - GLUT(freeglut3.0.0)\n  - SOIL(Simple OpenGL Image Library)\n\n## 效果\n\n全局效果，一个空心立方体作为房间轮廓，房顶为曲面。为了方便调试，画出了坐标轴和房顶的贝塞尔曲面控制点。\n\n![](img/1.png)\n\n房间内有一个桌子，桌子的正面使用纹理填充，桌子上放置一个圆锥、一个球、一个圆柱和一个棱柱。可以看到所有物体的反光效果\n\n![](img/2.png)\n\n屋顶为贝塞尔曲面，可以看到明显的光照效果\n\n![](img/3.png)\n\n交互：使用第一人称视角，键盘WASD提供前进、后退、左平移、右平移的操作，QE提供向左旋转和向右旋转的操作。\n\n## 实现\n\n### 程序结构\n\nmain函数内容：\n\n- 初始化环境与应用\n- 注册事件（重绘事件、改变窗口大小事件、按键交互事件）\n- OpenGL自动机初始化\n- 启动事件循环\n\n### 构造实体\n\n房间轮廓使用glutWireCube函数构造\n\n桌子上的球体和圆锥分别使用glutSolidSphere和glutSolidCone函数构造\n\n其他实体均使用OpenGL构造多边形实现。以构造圆柱为例，代码逻辑如下：\n\n```cpp\n// build a cylinder, use (0, 0, 0) as center, Y as axis\nvoid buildCylinder(double radius, double height, int slices, bool line)\n{\n\t// calculate x and z\n\tstd::vector\u003cdouble\u003e x;\n\tstd::vector\u003cdouble\u003e z;\n\tfor (int i = 0; i \u003c slices; ++i)\n\t{\n\t\tx.push_back(radius * cos(radians(360 / slices * i)));\n\t\tz.push_back(radius * sin(radians(360 / slices * i)));\n\t}\n\t// link head and tail\n\tx.push_back(x[0]);\n\tz.push_back(z[0]);\n\n\t// build underside\n\tif (line)\n\t\tglBegin(GL_LINE_LOOP);\n\telse\n\t\tglBegin(GL_POLYGON);\n\tfor (int i = 0; i \u003c slices; ++i)\n\t{\n\t\tglVertex3d(x[i], height / 2, z[i]);\n\t}\n\tglEnd();\n\tif (line)\n\t\tglBegin(GL_LINE_LOOP);\n\telse\n\t\tglBegin(GL_POLYGON);\n\tfor (int i = slices - 1; i \u003e= 0; --i)\n\t{\n\t\tglVertex3d(x[i], -height / 2, z[i]);\n\t}\n\tglEnd();\n\n\t//build flank\n\tfor (int i = 0; i \u003c slices; ++i)\n\t{\n\t\tif (line)\n\t\t\tglBegin(GL_LINE_LOOP);\n\t\telse\n\t\t\tglBegin(GL_POLYGON);\n\t\tglVertex3d(x[i], -height / 2, z[i]);\n\t\tglVertex3d(x[i + 1], -height / 2, z[i + 1]);\n\t\tglVertex3d(x[i + 1], height / 2, z[i + 1]);\n\t\tglVertex3d(x[i], height / 2, z[i]);\n\t\tglEnd();\n\t}\n}\n```\n\n### 启动光照\n\n设置光照参数\n\n```cpp\n// Light values and coordinates\nGLfloat globalAmbient[] = {0.2f, 0.2f, 0.2f, 1.0f}; // RGBA\nGLfloat ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};\nGLfloat diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};\nGLfloat specular[] = {1.0f, 1.0f, 1.0f, 1.0f};\nGLfloat specref[] = {3.0f, 3.0f, 3.0f, 3.0f};\n// light position (x, y, z, w)\n// w=0 means the light is a directional source, ignore its position\n// otherwise take its position into consideration\nGLfloat light0Position[] = {3.0f, 2.0f, 1.0f, 1.0f};\n```\n\n在OpenGL自动机初始化函数setupRC中启动光照，设置全局泛光和一个固定光源，设置物体默认纹理为颜色\n\n```cpp\n// Enable lighting\nglEnable(GL_LIGHTING);\n// setup global env light\nglLightModelfv(GL_LIGHT_MODEL_AMBIENT, globalAmbient);\n// set view point to local view point\nglLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);\n// setup double side light\nglLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);\n// Setup and enable light 0\nglLightfv(GL_LIGHT0, GL_AMBIENT, ambient);\nglLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);\nglLightfv(GL_LIGHT0, GL_SPECULAR, specular);\nglEnable(GL_LIGHT0);\n// setup shade model\nglShadeModel(GL_SMOOTH);\n\n// Set Material properties to follow glColor values\nglColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);\n\n// Enable meterial\nglEnable(GL_COLOR_MATERIAL);\n// All materials hereafter have full specular reflectivity\n// with a high shine\nglMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specref);\nglMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 50);\n```\n\n然后在render中设置光源位置（叠加了model view matrix的位置）\n\n```cpp\nglLightfv(GL_LIGHT0, GL_POSITION, light0Position);\n```\n\n### 应用纹理\n\n在setupRC函数中启动纹理，使用SOIL库载入jpeg格式纹理图片并载入OpenGL中\n\n```cpp\n// enable texture\nglEnable(GL_TEXTURE_2D);\n\n// init texture\nglGenTextures(1, \u0026tex); // generate one texture id to \u0026tex\nglBindTexture(GL_TEXTURE_2D, tex); // operations to 2d texture after this will apply to tex\nglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // repeat texture if space is wider than texture\nglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // repeat texture if space is higher than texture\nglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\nglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\nglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);\nint width, height;\nunsigned char *img = SOIL_load_image(\"me.jpg\", \u0026width, \u0026height, 0, SOIL_LOAD_RGB); // load image to memory\n// bind img to current 2d texture(aka: tex)\nglTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, img);\nSOIL_free_image_data(img); // free img memory, texture will still be stored in memory\nglBindTexture(GL_TEXTURE_2D, 0); // end operation about texture\n```\n\n在应用纹理时，使用glBindTexture启动指定二维纹理即可，比如构造桌子正面的纹理的代码逻辑如下：\n\n```cpp\n// draw the front face with texture\nglBindTexture(GL_TEXTURE_2D, tex); // enable texture\nglBegin(GL_POLYGON);\nglTexCoord2d(0, 0);\nglVertex3d(room.table.centerX + room.table.lenX / 2,\n\t\t\t\t room.table.centerY + room.table.lenY / 2,\n\t\t\t\t room.table.centerZ - room.table.lenZ / 2); // left top\nglTexCoord2d(room.table.lenX, 0);\nglVertex3d(room.table.centerX - room.table.lenX / 2,\n\t\t\t\t room.table.centerY + room.table.lenY / 2,\n\t\t\t\t room.table.centerZ - room.table.lenZ / 2); // right top\nglTexCoord2d(room.table.lenX, room.table.lenY);\nglVertex3d(room.table.centerX - room.table.lenX / 2,\n\t\t\t\t room.table.centerY - room.table.lenY / 2,\n\t\t\t\t room.table.centerZ - room.table.lenZ / 2); // right bottom\nglTexCoord2d(0, room.table.lenY);\nglVertex3d(room.table.centerX + room.table.lenX / 2,\n\t\t\t\t room.table.centerY - room.table.lenY / 2,\n\t\t\t\t room.table.centerZ - room.table.lenZ / 2); // left bottom\nglEnd();\nglBindTexture(GL_TEXTURE_2D, 0); // disable texture\n```\n\n即，在构造多边形的时候，除了定义顶点vertex，还要定义对应的纹理坐标texture coordinates\n\n使用完毕后要使用glBingTexture把纹理清空，防止其他对象也应用此纹理。\n\n### 实现交互\n\n注册能够识别ascii码的回调函数keyPressEvent，根据用户按键修改player结构体的位置属性和角度属性，然后在render函数中使用gluLookAt函数指定摄像机位置。\n\n需要注意的是gluLookAt是通过修改model view matrix实现的，所以在render函数中需要在其他物体构建之前使用gluLookAt函数（包括光源）。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiscretetom%2Fsimple-3d-room","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiscretetom%2Fsimple-3d-room","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiscretetom%2Fsimple-3d-room/lists"}