1. 程式人生 > >OpenGL(十五) OpenCV+OpenGL實現水面倒影

OpenGL(十五) OpenCV+OpenGL實現水面倒影

有兩幅原始圖片,一個是景物影象,一個是水面影象,嘗試生成景物在水中的倒影:



在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;  
}  

這個是沒有使用倒影的效果:


倒影效果: