OpenGL(十五) OpenCV+OpenGL實現水面倒影
阿新 • • 發佈:2019-01-02
有兩幅原始圖片,一個是景物影象,一個是水面影象,嘗試生成景物在水中的倒影:
在OpenGL中,載入並顯示這個景物影象可以把這個影象作為紋理載入即可,把影象直接選擇180度的效果就相當於是在鏡面中倒影的效果,剩下水紋的效果本來也想作為紋理疊加上去的,但是試了一下沒有成功,乾脆直接把水面和景物先融合一下,作為倒影的影象,一次加入到倒影平面的紋理中。融合使用了OpenCV。
OpenCV兩幅影象融合程式碼:
調節水面影象和景物影象的融合比例:#include "core/core.hpp" #include "highgui/highgui.hpp" #include "imgproc/imgproc.hpp" #include <iostream> using namespace cv; Mat image,image1,image2; char* windowName="Image Fusion"; char* trackBarName="TrackBar"; int trackBarValue=0; int trackBarMax=50; //控制條回撥函式 void TrackBarFunc(int ,void(*)); int main(int argc,char *argv[]) { image1=imread("shanghai.bmp"); image2=imread("water.bmp"); //判斷讀入是否成功 if(!image1.data|!image2.data) { std::cout<<"開啟圖片失敗,請檢查路徑!"<<std::endl; return 0; } //調整image2的大小與image1的大小一致,融合函式addWeighted()要求輸入的兩個圖形尺寸相同 resize(image2,image2,Size(image1.cols,image1.rows)); //建立顯示視窗 namedWindow(windowName,0); //在影象視窗上建立控制條 createTrackbar(trackBarName,windowName,&trackBarValue,trackBarMax,TrackBarFunc); TrackBarFunc(0,0); waitKey(); imwrite("E:\\water.bmp",image); return 0; } void TrackBarFunc(int ,void(*)) { //轉換成融合比例 float rate=(float)trackBarValue/trackBarMax; addWeighted(image1,rate,image2,1-rate,0,image); // namedWindow(windowName,0); imshow(windowName,image); }
最後選的這一張作為倒影紋理:
使用OpenGL把這兩幅影象作為紋理載入,實現倒影效果,OpenGL程式碼:
#define WindowWidth 600 #define WindowHeight 600 #define WindowTitle "OpenGL水面倒影" #include <glut.h> #include <stdio.h> #include <stdlib.h> //定義兩個紋理物件編號 GLuint shanghai; GLuint water; #define BMP_Header_Length 54 //影象資料在記憶體塊中的偏移量 // 函式power_of_two用於判斷一個整數是不是2的整數次冪 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; GLuint last_texture_ID=0, 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規定的最大值,也縮放 { 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 = 1024; const GLint new_height = 1024; // 規定縮放後新的大小為邊長的正方形 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; } // 繫結新的紋理,載入紋理並設定紋理引數 // 在繫結前,先獲得原來繫結的紋理編號,以便在最後進行恢復 GLint lastTextureID=last_texture_ID; glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID); 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, lastTextureID); //恢復之前的紋理繫結 free(pixels); return texture_ID; } void display(void) { // 清除螢幕 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 設定視角 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(70, 1, 1, 21); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 7,-1.5, 0, 0, 0, 0, 0, -1); // 繪製倒影 glBindTexture(GL_TEXTURE_2D, water); 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, 5.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(6.0f, -3.0f, 5.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f); glEnd(); //繪製真實場景 glBindTexture(GL_TEXTURE_2D, shanghai); glTranslatef(0,-6,0); glRotatef(180,1,0,0); 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, 5.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(6.0f, -3.0f, 5.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(6.0f, -3.0f, 0.0f); glEnd(); glutSwapBuffers(); } int main(int argc, char* argv[]) { // GLUT初始化 glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100, 100); glutInitWindowSize(WindowWidth, WindowHeight); glutCreateWindow(WindowTitle); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); // 啟用紋理 shanghai = load_texture("shanghai.bmp"); //載入紋理 water= load_texture("water.bmp"); glutDisplayFunc(&display); //註冊函式 glutMainLoop(); //迴圈呼叫 return 0; }
這個是沒有使用倒影的效果:
倒影效果: