继续上一篇文章的例子:OpenGL的几何变换2之内观察立方体
上一篇是通过绘图方式得到的立方体,没有贴图,这次加上纹理贴图。
通过纹理贴图有两种方案:
1、图片分割化,即是把一张完整的全景图片(就是支持720度全景图片)人工的分隔成前后左右上下六张图片(静态),然后分别加载这六张图片;
2、数据分割化,即是保留一张完整的全景图片,加载图片以后,对图片数据进行上下左右前后进行数据切割,或者应该说进行图片切割(动态)。
这一篇文章主要用到的技术点是纹理映射,具体不再累述,可以参考OpenGL的glTexCoord2f纹理坐标配置
本篇文章的案例主要是采用第一种(图片分割化),先附上代码:
1 #include <stdio.h> 2 #include <windows.h> 3 #include <gl/glut.h> //引用相关包 4 #include <gl/glaux.h> 5 6 GLfloat xangle = 0.0; //X 旋转量 7 GLfloat yangle = 0.0; //Y 旋转量 8 GLfloat zangle = 0.0; //Z 旋转量 9 GLuint texturesArr[6]; //存储6个纹理 10 11 //交叉点的坐标 12 int cx = 0; 13 int cy = 0; 14 15 //载入位图图象 16 AUX_RGBImageRec *loadBMP(CHAR *Filename) 17 { 18 FILE *File = NULL; //文件句柄 19 if (!Filename) //确保文件名已提供 20 { 21 return NULL; //如果没提供,返回 NULL 22 } 23 24 File = fopen(Filename,"r"); //尝试打开文件 25 if (File) //文件存在么? 26 { 27 fclose(File); //关闭句柄 28 return auxDIBImageLoadA(Filename); //载入位图并返回指针 29 } 30 return NULL; //如果载入失败,返回 NULL 31 } 32 33 //载入位图(调用上面的代码)并转换成纹理 34 int loadGLTextures() 35 { 36 int Status = FALSE; //状态指示器 37 char *imgArr[6] = { 38 "pano/pano_f.bmp", //前面 39 "pano/pano_b.bmp", //后面 40 "pano/pano_u.bmp", //顶面 41 "pano/pano_d.bmp", //底面 42 "pano/pano_r.bmp", //右面 43 "pano/pano_l.bmp" //左面 44 }; 45 AUX_RGBImageRec *textureImage[6]; //创建纹理的存储空间 46 47 memset(texturesArr, 0x0, sizeof(texturesArr)); 48 memset(textureImage,0,sizeof(textureImage)); //将指针设为NULL 49 50 for (int i = 0; i < 6; i++) { 51 Status = FALSE; 52 //载入位图,检查有无错误,如果位图没找到则退出 53 if (textureImage[i] = loadBMP(imgArr[i])) 54 { 55 Status = TRUE; //将 Status 设为 TRUE 56 glGenTextures(1, &texturesArr[i]); //创建纹理 57 58 //使用来自位图数据生成 的典型纹理 59 glBindTexture(GL_TEXTURE_2D, texturesArr[i]); 60 //生成纹理 61 glTexImage2D(GL_TEXTURE_2D, 0, 3, textureImage[i]->sizeX, textureImage[i]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, textureImage[i]->data); 62 63 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); //线形滤波 64 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); //线形滤波 65 } 66 67 if (textureImage[i]) //纹理是否存在 68 { 69 if (textureImage[i]->data) //纹理图像是否存在 70 { 71 free(textureImage[i]->data); //释放纹理图像占用的内存 72 } 73 free(textureImage[i]); //释放图像结构 74 } 75 if (Status == FALSE) { 76 break; 77 } 78 } 79 return Status; //返回 Status 80 } 81 82 //从这里开始进行所有的绘制 83 void drawCube(void) 84 { 85 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存 86 glMatrixMode(GL_MODELVIEW); 87 glLoadIdentity(); //重置当前的模型观察矩阵 88 89 glPushMatrix(); 90 { 91 gluLookAt(0, 0, -5, 0, 0, 0, 0, 1, 0); 92 glTranslatef(0.0f, 0.0f, -5.0f); //移入屏幕 5 个单位 93 glRotatef(xangle, 1.0f, 0.0f, 0.0f); //绕X轴旋转 94 glRotatef(yangle, 0.0f, 1.0f, 0.0f); //绕Y轴旋转 95 glRotatef(zangle, 0.0f, 0.0f, 1.0f); //绕Z轴旋转 96 #if (0) //显示反面 97 glBindTexture(GL_TEXTURE_2D, texturesArr[0]); //选择纹理 98 glBegin(GL_QUADS); { 99 //前面:逆时针 100 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的左下 101 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的右下 102 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //纹理和四边形的右上 103 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //纹理和四边形的左上 104 }glEnd(); 105 106 glBindTexture(GL_TEXTURE_2D, texturesArr[1]); //选择纹理 107 glBegin(GL_QUADS); { 108 //后面:逆时针 109 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理和四边形的左下 110 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理和四边形的右下 111 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理和四边形的右上 112 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //纹理和四边形的左上 113 }glEnd(); 114 115 glBindTexture(GL_TEXTURE_2D, texturesArr[2]); //选择纹理 116 glBegin(GL_QUADS); { 117 //顶面:逆时针 118 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //纹理和四边形的左下 119 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //纹理和四边形的右下 120 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //纹理和四边形的右上 121 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理和四边形的左上 122 }glEnd(); 123 124 glBindTexture(GL_TEXTURE_2D, texturesArr[3]); //选择纹理 125 glBegin(GL_QUADS); { 126 //底面:逆时针 127 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理和四边形的左下 128 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理和四边形的右下 129 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的右上 130 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的左上 131 }glEnd(); 132 133 glBindTexture(GL_TEXTURE_2D, texturesArr[4]); //选择纹理 134 glBegin(GL_QUADS); { 135 //右面:逆时针 136 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的左下 137 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理和四边形的右下 138 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //纹理和四边形的右上 139 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //纹理和四边形的左上 140 }glEnd(); 141 142 glBindTexture(GL_TEXTURE_2D, texturesArr[5]); //选择纹理 143 glBegin(GL_QUADS); { 144 //左面:逆时针 145 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理和四边形的左下 146 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的右下 147 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //纹理和四边形的右上 148 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理和四边形的左上 149 }glEnd(); 150 #else //显示正面 151 glBindTexture(GL_TEXTURE_2D, texturesArr[0]); //选择纹理 152 glBegin(GL_QUADS); { 153 //前面:纹理顺时针,立方体逆时针 154 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的左下 155 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //纹理和四边形的左上 156 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //纹理和四边形的右上 157 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的右下 158 }glEnd(); 159 160 glBindTexture(GL_TEXTURE_2D, texturesArr[1]); //选择纹理 161 glBegin(GL_QUADS); { 162 //后面:纹理顺时针,立方体逆时针 163 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理和四边形的左下 164 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理和四边形的左上 165 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //纹理和四边形的右上 166 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理和四边形的右下 167 }glEnd(); 168 169 glBindTexture(GL_TEXTURE_2D, texturesArr[2]); //选择纹理 170 glBegin(GL_QUADS); { 171 //顶面:纹理顺时针,立方体逆时针 172 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //纹理和四边形的左下 173 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //纹理和四边形的左上 174 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理和四边形的右上 175 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //纹理和四边形的右下 176 }glEnd(); 177 178 glBindTexture(GL_TEXTURE_2D, texturesArr[3]); //选择纹理 179 glBegin(GL_QUADS); { 180 //底面:纹理顺时针,立方体逆时针 181 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理和四边形的左下 182 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的左上 183 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的右上 184 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理和四边形的右下 185 }glEnd(); 186 187 glBindTexture(GL_TEXTURE_2D, texturesArr[5]); //选择纹理 188 glBegin(GL_QUADS); { 189 //右面:纹理顺时针,立方体逆时针 190 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理和四边形的左下 191 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //纹理和四边形的左上 192 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //纹理和四边形的右上 193 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的右下 194 }glEnd(); 195 196 glBindTexture(GL_TEXTURE_2D, texturesArr[4]); //选择纹理 197 glBegin(GL_QUADS); { 198 //左面:纹理顺时针,立方体逆时针 199 200 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的左下 201 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //纹理和四边形的左上 202 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理和四边形的右上 203 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理和四边形的右下 204 }glEnd(); 205 #endif 206 }glPopMatrix(); 207 glFlush(); 208 } 209 210 //初始化 211 void init(void) 212 { 213 glClearColor (0.0, 0.0, 0.0, 0.0); //清理颜色,为黑色,(也可认为是背景颜色) 214 215 glCullFace(GL_FRONT); //背面裁剪(背面不可见) 216 glEnable(GL_CULL_FACE); //启用裁剪 217 glEnable(GL_TEXTURE_2D); 218 loadGLTextures(); //载入纹理贴图 219 } 220 221 void display(void) 222 { 223 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清楚颜色数据和深度数据(清屏) 224 glLoadIdentity(); //Reset The View 225 226 drawCube(); 227 228 glutSwapBuffers(); //交换缓冲区。显示图形 229 230 //xangle += 0.3f; 231 //yangle += 0.3f; 232 //zangle += 0.3f; 233 Sleep(10); 234 } 235 236 //当窗口大小改变时,会调用这个函数 237 void reshape(GLsizei w,GLsizei h) 238 { 239 //这里小说明一下:矩阵模式是不同的,他们各自有一个矩阵。投影相关 240 //只能用投影矩阵。(只是目前情况下哦,等我学多了可能就知道为什么了。) 241 242 glViewport(0,0,w,h); //设置视口 243 glMatrixMode(GL_PROJECTION); //设置矩阵模式为投影变换矩阵, 244 glLoadIdentity(); //变为单位矩阵 245 gluPerspective(90, (GLfloat)w / h, 0.1f, 100.0f); //设置投影矩阵 246 glMatrixMode(GL_MODELVIEW); //设置矩阵模式为视图矩阵(模型) 247 glLoadIdentity(); //变为单位矩阵 248 } 249 250 //处理鼠标点击 251 void Mouse(int button, int state, int x, int y) 252 { 253 if(state == GLUT_DOWN) //第一次鼠标按下时,记录鼠标在窗口中的初始坐标 254 { 255 //记住鼠标点击后光标坐标 256 cx = x; 257 cy = y; 258 //printf("Mouse: x=%d, y=%d, oldx_Translatef=%f, oldy_Translatef=%f\n", x, y, oldx_Translatef, oldy_Translatef); 259 } 260 } 261 262 //处理鼠标拖动 263 void onMouseMove(int x, int y) 264 { 265 float offset = 0.18; 266 //计算拖动后的偏移量,然后进行xy叠加减 267 yangle -= ((x - cx) * offset); 268 269 if (xangle < 90 && y > cy) {//往下拉 270 xangle += ((y - cy) * offset); 271 } else if (xangle > -90 && y < cy) {//往上拉 272 xangle += ((y - cy) * offset); 273 } 274 //printf("Move: x=%d(%d)[%d], y=%d(%d)[%d], xangle=%f, yangle=%f\n", 275 // x, cx, x-cx, 276 // y, cy, y-cy, 277 // xangle, yangle); 278 glutPostRedisplay(); 279 280 //保存好当前拖放后光标坐标点 281 cx = x; 282 cy = y; 283 } 284 285 //键盘输入事件函数 286 void keyboard(unsigned char key,int x,int y) 287 { 288 switch(key) 289 { 290 case 'x': //当按下键盘上d时,以沿X轴旋转为主 291 if (xangle < 85.0f) 292 { 293 xangle += 1.0f; //设置旋转增量 294 } 295 break; 296 case 'X': 297 if (xangle > -85.0f) 298 { 299 xangle -= 1.0f; //设置旋转增量 300 } 301 break; 302 case 'y': 303 yangle += 1.0f; 304 break; 305 case 'Y': 306 yangle -= 1.0f; 307 break; 308 //case 'z': 309 // zangle += 1.0f; 310 // break; 311 //case 'Z': 312 // zangle -= 1.0f; 313 // break; 314 default: 315 return; 316 } 317 glutPostRedisplay(); //重绘函数 318 } 319 320 //特殊按键 321 void specialKey(int key, int x, int y) 322 { 323 float offset = 1.5; 324 printf("key=%d\n", key); 325 switch (key) 326 { 327 case GLUT_KEY_UP: //脑袋向上往前看 328 if (xangle < 90.0f) 329 { 330 xangle += offset; //设置旋转增量 331 } 332 break; 333 case GLUT_KEY_DOWN: //脑袋向下往前看 334 if (xangle > -90.0f) 335 { 336 xangle -= offset; //设置旋转增量 337 } 338 break; 339 case GLUT_KEY_LEFT: //脑袋想左往前看 340 yangle -= offset; 341 break; 342 case GLUT_KEY_RIGHT: //脑袋向右往前看 343 yangle += offset; 344 break; 345 default: 346 break; 347 } 348 glutPostRedisplay(); 349 } 350 351 int main(int argc, char *argv[]) 352 { 353 printf("可通过↑↓←→按键或者鼠标控制全景图绕旋转\n"); 354 355 glutInit(&argc, argv); //固定格式 356 glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); 357 glutInitWindowSize(1024/2, 1024/2); //显示框的大小 358 glutInitWindowPosition(100,100); //确定显示框左上角的位置 359 glutCreateWindow("OpenGL纹理贴图"); 360 361 init(); //初始化资源,这里一定要在创建窗口以后,不然会无效。 362 glutDisplayFunc(display); 363 //glutIdleFunc(display); 364 glutReshapeFunc(reshape); //绘制图形时的回调 365 //glutKeyboardFunc(keyboard); 366 glutMouseFunc(Mouse); 367 glutMotionFunc(onMouseMove); 368 glutKeyboardFunc(keyboard); 369 glutSpecialFunc(specialKey); //特殊按键 370 glutMainLoop(); 371 return 0; 372 }
附上全景图片:
附上代码运行结果:
最后附上可执行的EXE链接: https://pan.baidu.com/s/1dGf0GAt 密码: xzd5
最后测试结果还发现个问题:
这是立方体,所以在屏幕窗口的宽高不想等时,拖动图片时立方体面与面连接点看到的效果会很别扭,初步的猜测是承载贴图的图形问题,比如可以改成球体,或者等宽高固定化。