繼續上一篇文章的例子: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
最后測試結果還發現個問題:
這是立方體,所以在屏幕窗口的寬高不想等時,拖動圖片時立方體面與面連接點看到的效果會很別扭,初步的猜測是承載貼圖的圖形問題,比如可以改成球體,或者等寬高固定化。
