OpenGL 中的三維紋理操作
阿新 • • 發佈:2019-01-29
#define _CRT_SECURE_NO_WARNINGS
#include <gl/glut.h>
#include <stdio.h>
#include <stdlib.h>
#define WindowWidth 400
#define WindowHeight 400
#define WindowTitle "OpenGL紋理測試"
/* 函式grab
* 抓取視窗中的畫素
* 假設視窗寬度為WindowWidth,高度為WindowHeight
*/
#define BMP_Header_Length 54
void grab( void )
{ FILE * pDummyFile;
FILE * pWritingFile;
GLubyte* pPixelData;
GLubyte BMP_Header[BMP_Header_Length];
GLint i, j;
GLint PixelDataLength;
// 計算畫素資料的實際長度
i = WindowWidth * 3;
// 得到每一行的畫素資料長度
while ( i%4 != 0 )
// 補充資料,直到i是的倍數
++i;
// 本來還有更快的演算法,
// 但這裡僅追求直觀,對速度沒有太高要求
PixelDataLength = i * WindowHeight; // 分配記憶體和開啟檔案
pPixelData = (GLubyte*) malloc (PixelDataLength);
if ( pPixelData == 0 )
exit (0);
pDummyFile =
fopen ( "dummy.bmp" , "rb" );
if ( pDummyFile == 0 )
exit (0);
pWritingFile =
fopen ( "grab.bmp" , "wb" );
if ( pWritingFile == 0 )
exit (0);
// 把dummy.bmp的檔案頭複製為新檔案的檔案頭
fread (BMP_Header,
sizeof (BMP_Header), 1, pDummyFile); fwrite (BMP_Header,
sizeof (BMP_Header), 1, pWritingFile);
fseek (pWritingFile, 0x0012, SEEK_SET);
i = WindowWidth;
j = WindowHeight;
fwrite (&i,
sizeof (i), 1, pWritingFile);
fwrite (&j,
sizeof (j), 1, pWritingFile);
// 讀取畫素,寫入畫素資料
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glReadPixels(0, 0, WindowWidth, WindowHeight,GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);
fseek (pWritingFile, 0, SEEK_END);
fwrite (pPixelData, PixelDataLength, 1, pWritingFile);
// 釋放記憶體和關閉檔案
fclose (pDummyFile);
fclose (pWritingFile);
free (pPixelData);
}
// 判斷是否為偶數
int power_of_two( int
n)
{
if ( n <= 0 )
return
0;
return
(n & (n-1)) == 0;
}
/* 函式load_texture
讀取一個BMP檔案作為紋理
如果失敗,返回0,如果成功,返回紋理編號
*/
GLuint load_texture( const
char * file_name)
{
GLint width, height, total_bytes;
GLubyte *pixels = 0;
GLint last_texture_ID;
GLuint texture_ID = 0;
// 開啟檔案,如果失敗,返回
FILE
*pFile = fopen (file_name, "rb" );
if ( pFile == 0 )
return
0;
// 讀取檔案中影象的寬度和高度
fseek (pFile, 0x0012, SEEK_SET);
fread (&width, 4, 1, pFile);
fread (&height, 4, 1, pFile);
fseek (pFile, BMP_Header_Length, SEEK_SET);
// 計算每行畫素所佔位元組數,並根據此資料計算總畫素位元組數
{
GLint line_bytes = width * 3;
while ( line_bytes % 4 != 0 )
++line_bytes;
total_bytes = line_bytes * height;
}
// 根據總畫素位元組數分配記憶體
pixels = (GLubyte*) malloc (total_bytes);
if ( pixels == 0 )
{
fclose (pFile);
return
0;
}
// 讀取畫素資料
if (
fread (pixels, total_bytes, 1, pFile) <= 0 )
{
free (pixels);
fclose (pFile);
return
0;
}
// 在舊版本的OpenGL中、如果影象的寬度和高度不是的整數次方,則需要進行縮放
// 這裡並沒有檢查OpenGL版本,出於對版本相容性的考慮,按舊版本處理
// 另外,無論是舊版本還是新版本, 當影象的寬度和高度超過當前OpenGL實現所支援的最大值時,也要進行縮放
GLint max;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
if ( !power_of_two(width)|| !power_of_two(height)
|| width > max || height > max )
{
const
GLint new_width = 256;
const
GLint new_height = 256; // 規定縮放後新的大小為邊長的正方形
GLint new_line_bytes, new_total_bytes;
GLubyte* new_pixels = 0;
// 計算每行需要的位元組數和總位元組數
new_line_bytes = new_width * 3;
while ( new_line_bytes % 4 != 0 )
++new_line_bytes;
new_total_bytes = new_line_bytes * new_height;
// 分配記憶體
new_pixels = (GLubyte*) malloc (new_total_bytes);
if ( new_pixels == 0 )
{
free (pixels);
fclose (pFile);
return
0;
}
// 進行畫素縮放
gluScaleImage(GL_RGB, width, height, GL_UNSIGNED_BYTE, pixels,
new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);
// 釋放原來的畫素資料,把pixels指向新的畫素資料,並重新設定width和height
free (pixels);
pixels = new_pixels;
width = new_width;
height = new_height;
}
// 分配一個新的紋理編號
glGenTextures(1, &texture_ID);
if ( texture_ID == 0 )
{
free (pixels);
fclose (pFile);
return
0;
}
// 繫結新的紋理,載入紋理並設定紋理引數.
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture_ID); //在繫結前,先獲得原來繫結的紋理編號,以便在最後進行恢復
glBindTexture(GL_TEXTURE_2D, texture_ID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //指當紋理影象被使用到一個大於它的形狀上時,應該如何處理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
glBindTexture(GL_TEXTURE_2D, last_texture_ID);
// 之前為pixels分配的記憶體可在使用glTexImage2D以後釋放
// 因為此時畫素資料已經被OpenGL另行儲存了一份(可能被儲存到專門的圖形硬體中)
free (pixels);
return
texture_ID;
}
/* 兩個紋理物件的編號
*/
GLuint texGround;
GLuint texWall;
void display( void )
{
// 清除螢幕
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 設定視角
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); //在進行變換前,將當前矩陣變為單位矩陣
gluPerspective(75, 1, 1, 21);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(1, 5, 5, 0, 0, 0, 0, 0, 1);
//前三個引數表示了觀察點的位置,中間三個引數表示了觀察目標的位置,
//最後三個引數代表從(0,0,0)到(x,y,z)的直線,它表示了觀察者認為的“上”方向
// 使用“地”紋理繪製土地
glBindTexture(GL_TEXTURE_2D, texGround);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-8.0f, -8.0f, 0.0f);
glTexCoord2f(0.0f, 5.0f); glVertex3f(-8.0f, 8.0f, 0.0f);
glTexCoord2f(5.0f, 5.0f); glVertex3f(8.0f, 8.0f, 0.0f);
glTexCoord2f(5.0f, 0.0f); glVertex3f(8.0f, -8.0f, 0.0f);
glEnd();
// 使用“牆”紋理繪製柵欄
glBindTexture(GL_TEXTURE_2D, texWall);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);
glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);
glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);
glEnd();
// 旋轉後再繪製一個
glRotatef(-90, 0, 0, 1);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-6.0f, -3.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-6.0f, -3.0f, 1.5f);
glTexCoord2f(5.0f, 1.0f); glVertex3f(6.0f, -3.0f, 1.5f);
glTexCoord2f(5.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f);
glEnd();
// 交換緩衝區,並儲存畫素資料到檔案
glutSwapBuffers();
grab();
}
int main( int
argc, char * argv[])
{
// GLUT初始化
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(WindowWidth, WindowHeight);
glutCreateWindow(WindowTitle);
glutDisplayFunc(&display);
// 在這裡做一些初始化
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
texGround = load_texture( "ground.bmp" );
texWall = load_texture( "wall.bmp" );
// 開始顯示
glutMainLoop();
return
0;
}
|