Environment mapping

Ik zal je hier laten zien hoe je een environment map op een voorwerp toe kunt passen. Door een environment map kun je ervoor zorgen dat een voorwerp de omgeving "lijkt" te weerspiegelen. Een environment map weerspiegelt namelijk de omgeving niet er wordt namelijk een texture map op geplaatst die hetzelfde lijkt als de omgeving. OpenGL berekent vervolgens zelf de texture co÷rdinaten zodat het lijkt alsof de omgeving in het voorwerp wordt weerspiegeld.

Het lijkt heel moeilijk maar het is eigenlijk heel simpel en dus ook een goede manier om je werelden wat meer tot leven te brengen. De code die ik hier voor heb gebruikt is van de heightfield terrain met een paar aanpassingen om de textures te laden zoals je gezien hebt in de multitexturing tutorial.


#define BITMAP_ID 0x4d42
#define MAP_X     32
#define MAP_Z     32
#define MAP_SCALE 40.0f

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>

HDC g_HDC;
bool key[256];
bool fullscreen = false;

typedef struct
{
	int width;
	int height;
	unsigned int texID;
	unsigned char *data;
}texture_t;

texture_t *envtex;
texture_t *landtex;
texture_t *heighttex;

float terrain[MAP_X][MAP_Z][3];
float hoek;

//////////////////////////////////////////////////////////////////////////////
//laden van een bitmap
//////////////////////////////////////////////////////////////////////////////
unsigned char *loadbmp(char *filename, BITMAPINFOHEADER *bmpinfo)
{
	FILE *fileptr;
	BITMAPFILEHEADER bmpfile;
	unsigned char *bmpimage;
	int imageidx = 0;
	unsigned char swaprgb;

	fileptr = fopen(filename, "rb");
	if (fileptr == NULL){return 0;}

	fread(&bmpfile, sizeof(BITMAPFILEHEADER), 1, fileptr);
	if (bmpfile.bfType != BITMAP_ID)
	{
		fclose(fileptr);
		return 0;
	}

	fread(bmpinfo, sizeof(BITMAPINFOHEADER), 1, fileptr);

	fseek(fileptr, bmpfile.bfOffBits, SEEK_SET);

	bmpimage=(unsigned char*)malloc(bmpinfo->biSizeImage);
	if (!bmpimage)
	{
		free(bmpimage);
		fclose(fileptr);
		return 0;
	}

	fread(bmpimage, 1, bmpinfo->biSizeImage, fileptr);
	if (bmpimage == NULL)
	{
		fclose(fileptr);
		return 0;
	}

	for (imageidx = 0; imageidx < bmpinfo->biSizeImage; imageidx+=3)
	{
		swaprgb = bmpimage[imageidx];
		bmpimage[imageidx] = bmpimage[imageidx + 2];
		bmpimage[imageidx + 2] = swaprgb;
	}

	fclose(fileptr);
	return bmpimage;
}

//////////////////////////////////////////////////////////////////////////////
//laad de textures in het geheugen
//////////////////////////////////////////////////////////////////////////////
texture_t *loadtex(char *filename)
{
	BITMAPINFOHEADER texinfo;
	texture_t *thistex;

	thistex = (texture_t*)malloc(sizeof(texture_t));
	if (thistex == NULL)
		return NULL;

	thistex->data = loadbmp(filename, &texinfo);
	if (thistex->data == NULL)
	{
		free(thistex);
		return NULL;
	}

	thistex->width = texinfo.biWidth;
	thistex->height = texinfo.biHeight;

	glGenTextures(1, &thistex->texID);

	return thistex;
}
////////////////////////////////////////////////////////////////////////////
//zet de waarden uit de bitmap in een array die de x y en z as voorstelt
////////////////////////////////////////////////////////////////////////////
void InitializeTerrain()
{

	for (int z = 0; z < MAP_Z; z++)
	{
		for (int x = 0; x < MAP_X; x++)
		{
			terrain[x][z][0] = float(x)*MAP_SCALE;				
			terrain[x][z][1] = (float)heighttex->data[(z*MAP_Z+x)*3];
			terrain[x][z][2] = -float(z)*MAP_SCALE;
		}
	}
}

Tot hier is er weinig verandert.

//////////////////////////////////////////////////////////////////////////////
//Rendering
//////////////////////////////////////////////////////////////////////////////
void render()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	gluLookAt(1050.0f, 220.0f, -950.0f, 0.0f, 0.0f, 0.0f, 0.0, 1.0, 0.0);

	
	glBindTexture(GL_TEXTURE_2D, landtex->texID);
	for (int z = 0; z < MAP_Z-1; z++)
	{
		glBegin(GL_TRIANGLE_STRIP);
		for (int x = 0; x < MAP_X-1; x++)
		{
		
			glColor3f(terrain[x][z][1]/255.0f, 
			terrain[x][z][1]/255.0f, terrain[x][z][1]/255.0f);
			glTexCoord2f(0.0f, 0.0f);
			glVertex3f(terrain[x][z][0], terrain[x][z][1], 
			terrain[x][z][2]);

			glTexCoord2f(1.0f, 0.0f);
			glColor3f(terrain[x+1][z][1]/255.0f, 
			terrain[x+1][z][1]/255.0f, terrain[x+1][z][1]/255.0f);
			glVertex3f(terrain[x+1][z][0], terrain[x+1][z][1], 
			terrain[x+1][z][2]);

			glTexCoord2f(0.0f, 1.0f);
			glColor3f(terrain[x][z+1][1]/255.0f, 
			terrain[x][z+1][1]/255.0f, terrain[x][z+1][1]/255.0f);
			glVertex3f(terrain[x][z+1][0], terrain[x][z+1][1], 
			terrain[x][z+1][2]);

			glColor3f(terrain[x+1][z+1][1]/255.0f, 
			terrain[x+1][z+1][1]/255.0f, terrain[x+1][z+1][1]/255.0f);
			glTexCoord2f(1.0f, 1.0f);
			glVertex3f(terrain[x+1][z+1][0], terrain[x+1][z+1][1], 
			terrain[x+1][z+1][2]);
		}
		glEnd();
	}
	
				
		glTranslatef(900.0f,200.0f,-810.0f);
		glRotatef(hoek,1.0f,1.0f,1.0f);

		glEnable(GL_TEXTURE_GEN_S);
		glEnable(GL_TEXTURE_GEN_T);
		glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
		glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

		glBindTexture(GL_TEXTURE_2D, envtex->texID);
		auxSolidTorus(10.0f,20.0f);

		glDisable(GL_TEXTURE_GEN_S);
		glDisable(GL_TEXTURE_GEN_T);

	glFlush();
	SwapBuffers(g_HDC);
	
	if (hoek > 360.0)
		hoek = 0.0;

	hoek +=0.2;
}

Hier komen we het eerste stukje code tegen wat we nodig hebben voor environment mapping, onder het stuk code voor de heightfield. De enigste regels die je nodig hebt voor environment mapping zijn de glEnable() en glTexGenf() functies, dan het voorwerp wat een torus is, en dan moet je weer glDisable gebruiken anders wordt op alles environment mapping toegepast. Voor de torus moet je natuurlijk wel even glaux.lib linken.

////////////////////////////////////////////////////////////////////////////
//laden van textures
////////////////////////////////////////////////////////////////////////////
bool loadtextures()
{
	
	landtex=loadtex("land.bmp");
	envtex=loadtex("env.bmp");
	heighttex=loadtex("heightmap.bmp");
	
	//glGenTextures(1, &land);
	glBindTexture(GL_TEXTURE_2D, landtex->texID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, landtex->width, 
		landtex->height, GL_RGB, GL_UNSIGNED_BYTE, landtex->data);

	//glGenTextures(1, &kubus);
	glBindTexture(GL_TEXTURE_2D, envtex->texID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, envtex->width, 
		envtex->height, GL_RGB, GL_UNSIGNED_BYTE, envtex->data);	

	return true;
}
En dan het laden en binden van de textures natuurlijk wat weinig nieuws met zich meebrengt.

///////////////////////////////////////////////////////////////////////////////
//initialisatie van opengl
///////////////////////////////////////////////////////////////////////////////
void initgl()
{
	glClearColor(0.0f,0.0f,0.0f,0.0f);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glEnable(GL_TEXTURE_2D);
	
	
	loadtextures();

	InitializeTerrain();
	loadtextures();
	
}


////////////////////////////////////////////////////////////////////////////
//Pixelformatdescriptor
////////////////////////////////////////////////////////////////////////////
void setuppfd(HDC hDC)
{
	int nPixelFormat;

	static PIXELFORMATDESCRIPTOR pfd={
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW|
		PFD_SUPPORT_OPENGL|
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32,
		0,0,0,0,0,0,
		0,
		0,
		0,
		0,0,0,0,
		16,
		0,
		0,
		PFD_MAIN_PLANE,
		0,
		0,0,0};

		nPixelFormat = ChoosePixelFormat(hDC, &pfd);
		SetPixelFormat(hDC,nPixelFormat, &pfd);
}

///////////////////////////////////////////////////////////////////////////
//Windows Procedure
///////////////////////////////////////////////////////////////////////////

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM	lParam)	
{
	static HGLRC hRC;
	static HDC hDC;
	int width, height;

	switch (message)
	{
		case WM_CREATE:
			hDC = GetDC(hwnd);
			g_HDC = hDC;
			setuppfd(hDC);

			hRC = wglCreateContext(hDC);
			wglMakeCurrent(hDC,hRC);
			break;

		case WM_CLOSE:
			wglMakeCurrent(hDC,NULL);
			wglDeleteContext(hRC);

			PostQuitMessage(0);
			break;
			
		case WM_SIZE:
			height = HIWORD(lParam);
			width = LOWORD(lParam);

			if (height==0)
			{
				height=1;
			}

			glViewport(0,0,width,height);
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();

			gluPerspective(45.0f,(GLfloat)width/(GLfloat)
				       height,1.0f,1000.0f);

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			break;
			
		case WM_KEYDOWN:
			switch (wParam)
			{
				case VK_ESCAPE:
					wglMakeCurrent(hDC,NULL);
					wglDeleteContext(hRC);
					PostQuitMessage(0);
					break;
			}

	}
	return DefWindowProc(hwnd,message,wParam,lParam);
}

///////////////////////////////////////////////////////////////////////
//Main Functie
///////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,	HINSTANCE hPrevInstance,
		   LPSTR lpCmdLine, int	nShowCmd)
{
	WNDCLASSEX	wc;	
	HWND hwnd;
	MSG		msg;
	DWORD dwExStyle, dwStyle;
	bool	done=FALSE;
	RECT windowRect;

	int width=800,height=600,bits=32;
					
	windowRect.top = 0;
	windowRect.left = 0;
	windowRect.bottom = height;
	windowRect.right = width;

	wc.cbSize		= sizeof(WNDCLASSEX);	
	wc.style		= CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc		= WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;	
	wc.hIcon		= LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);	
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "OpenGL";
	wc.hIconSm		= LoadIcon(NULL,IDI_WINLOGO);

	RegisterClassEx(&wc);

	if(fullscreen)
	{
	DEVMODE devModeScreen;
	memset(&devModeScreen, 0,sizeof(devModeScreen));
	devModeScreen.dmSize = sizeof(devModeScreen);
	devModeScreen.dmPelsWidth = width;
	devModeScreen.dmPelsHeight = height;
	devModeScreen.dmBitsPerPel = bits;
	devModeScreen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;

	ChangeDisplaySettings(&devModeScreen, CDS_FULLSCREEN);

	dwExStyle = WS_EX_APPWINDOW;
	dwStyle = WS_POPUP;
	ShowCursor(FALSE);
	}

	else
	{
		dwExStyle = NULL;
		dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU;
	}

	
	AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

	hwnd=CreateWindowEx(dwExStyle,
			    "OpenGL",	
			    "glwindow",
			    dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
			    0,0,
			    windowRect.right - windowRect.left,
			    windowRect.bottom - windowRect.top,
			    NULL,
			    NULL,
			    hInstance,	
			    NULL);
	
	ShowWindow(hwnd, SW_SHOW);
	UpdateWindow(hwnd);
	initgl();
	
	while(!done)
	{
		PeekMessage(&msg,NULL,0,0,PM_REMOVE);	
			
			if (msg.message==WM_QUIT)
			{
				done=TRUE;
			}
			else
			{
				render();
				TranslateMessage(&msg);	
				DispatchMessage(&msg);
			}
	}
	return (msg.wParam);
}
Dat was hem dan alweer, een snelle tutorial dit keer maar toch zeer effectief.

Succes

Vampire,