Multitexturing

Om te beginnen met deze tutorial zul je eerst een OpenGL extensie moeten downloaden om multitexturing toe te kunnen passen. Het bestand dat je moet hebben is glext.h en kun je ergens op de silicon graphics website vinden.


#define BITMAP_ID 0x4d42

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

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

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

texture_t *smiley;
texture_t *sky;

PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL;
PFNGLACTIVETEXTUREARBPROC    glActiveTextureARB = NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC  glClientActiveTextureARB = NULL;
int maxTextureUnits = 0;

float hoek;
Uiteraard als eerste weer de includes waar nu ook een OpenGL extensie bij staat voor multitexturing. Na de includes komt een struct om op een makkelijke manier de bitmap gegevens in op te slaan.

De PFN..... objecten die je als laatste tegenkomt zijn extensie functies die je gebruikt voor multitexturing. Om deze te kunnen gebruiken zul je eerst een wgl functie moeten gebruiken om toegang te krijgen. Het object "glMultiTexCoordxfARB" geeft het aantal texture units aan die je wilt gebruiken waar x een getal is tussen 0 en 1 minder dan het maximaal aantal ondersteunde texture units. De texture units worden aan textures gekoppeld waarna alle texture units worden samengevoegd tot 1 "texture". Het object PFNGLACTIVETEXTUREARBPROC geeft de huidige texture unit aan waar je een texture aan kunt binden. Met het object PFNGLCLIENTACTIVETEXTUREARBPROC kun je vervolgens pointer-array opdrachten uitvoeren op de huidige texture unit.


/////////////////////////////////////////////////////////////////////
//controleer of er multitexturing support is
/////////////////////////////////////////////////////////////////////
bool instr(char *searchstr, char *str)
{
	char *extension;
	char *endofstr;
	int idx = 0;

	endofstr = str + strlen(str);

	while (str < endofstr)
	{
		idx = strcspn(str," ");

		if ( (strlen(searchstr) == idx) && 
		   (strncmp(searchstr, str, idx) == 0))
		{
			return true;
		}

		str += (idx + 1);

	}

	return false;
}
De bovenstaande functie doet niks anders dan de string "str" die alle ondersteunde functies bevat doorzoeken naar de string in "searchstr". Als de string, in dit geval GL_ARB_MULTITEXTURE, in "str" voorkomt wordt er true geretourneerd een gaat het programma verder, komt de string niet voor dan stopt het programma.

//////////////////////////////////////////////////////////////////////
//initialiseer multitexturing
//////////////////////////////////////////////////////////////////////
bool initmultitex()
{
	char *extensionstr;

	extensionstr = (char*)glGetString(GL_EXTENSIONS);

	if (extensionstr == 0)
		return false;

	if (instr("GL_ARB_multitexture", extensionstr))
	{
		glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits);

		glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)
			wglGetProcAddress("glMultiTexCoord2fARB");
		glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)
			wglGetProcAddress("glActiveTextureARB");
		glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)
			wglGetProcAddress("glClientActiveTextureARB");

			return true;
	}
	else
		return false;
}

De functie om multitexturing te gebruiken zet eerst alle extentie functies in extensionstr en controleert of multitexturing wordt ondersteund door de functie instr op te roepen. Als multitexturing wordt ondersteund zoeken we eerst uit hoeveel texture units er zijn toegestaan waarna we een (wgl) koppeling maken om de extensie functies te kunnen gebruiken.

/////////////////////////////////////////////////////////////////////////
//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;
}
De functie om een bitmap te laden.

/////////////////////////////////////////////////////////////////////////////
//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;
}
De functie texture_t zal ook niet zo'n probleem zijn, hier wordt namelijk een bitmap geladen en de gegevens worden in de struct texture_t gezet.

//////////////////////////////////////////////////////////////////////////
//Voorwerp Box
//////////////////////////////////////////////////////////////////////////
void box(float xpos,float ypos,float zpos)
{
	glPushMatrix();
		glTranslatef(xpos,ypos,zpos);
				
		glBegin(GL_QUADS);// Front Face
		glNormal3f(0.0f,0.0f,1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
		glVertex3f( 1.0f, -1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f); 
		glVertex3f( 1.0f,  1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f(-1.0f,  1.0f,  1.0f);
		glEnd();

		glBegin(GL_QUADS);// Back Face
		glNormal3f(0.0f,0.0f,-1.0f);
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
		glVertex3f(-1.0f,  1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f( 1.0f,  1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
		glVertex3f( 1.0f, -1.0f, -1.0f);	
		glEnd();

		glBegin(GL_QUADS);// Top Face
		glNormal3f(0.0f,1.0f,0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f(-1.0f,  1.0f, -1.0f);	

		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f); 
		glVertex3f(-1.0f,  1.0f,  1.0f);
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f); 
		glVertex3f( 1.0f,  1.0f,  1.0f);
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
		glVertex3f( 1.0f,  1.0f, -1.0f);
		glEnd();

		glBegin(GL_QUADS);// Bottom Face
		glNormal3f(0.0f,-1.0f,0.0f);
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f( 1.0f, -1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
		glVertex3f( 1.0f, -1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f,  1.0f);	
		glEnd();

		glBegin(GL_QUADS);// Right face
		glNormal3f(1.0f,0.0f,0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
		glVertex3f( 1.0f, -1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
		glVertex3f( 1.0f,  1.0f, -1.0f);
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f); 
		glVertex3f( 1.0f,  1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
		glVertex3f( 1.0f, -1.0f,  1.0f);	
		glEnd();

		glBegin(GL_QUADS);// Left Face
		glNormal3f(-1.0f,0.0f,0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f, -1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
		glVertex3f(-1.0f, -1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
		glVertex3f(-1.0f,  1.0f,  1.0f);	
		
		glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
		glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
		glVertex3f(-1.0f,  1.0f, -1.0f);	
	glEnd();

	glPopMatrix();
}
De box functie die de kubus maakt moeten we nu ook weer uitbreiden zodat er 2 texture co÷rdinaten worden toegekend aan een co÷rdinaat. Hoe dit gaat moet wel duidelijk zijn neem ik aan, het is alleen veel werk.

////////////////////////////////////////////////////////////////////////
//box1
////////////////////////////////////////////////////////////////////////
void box1(float xpos,float ypos,float zpos)
{
	glPushMatrix();
		glColor3f(1.0f,1.0f,1.0f);
		glTranslatef(xpos,ypos,zpos);
		glScalef(1.0f,1.0f,1.0f);
		glRotatef(hoek, 1.0f,1.0f,1.0f);
		box(0.0f,0.0f,0.0f);
	glPopMatrix();
}
Dit is dus je box met multitexturing.

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


	glPushMatrix();
		glLoadIdentity();
		glTranslatef(0.0f,0.0f,-5.0f);
		box1(0.0f,0.0f,0.0f);
	glPopMatrix();
	
	glFlush();
	SwapBuffers(g_HDC);

	hoek +=0.2;
	if (hoek>=360){hoek=0;}
}
je render functie.

////////////////////////////////////////////////////////////////////////////
//laden van textures
////////////////////////////////////////////////////////////////////////////
bool loadtextures()
{
	smiley = loadtex("tex2.bmp");
		if (smiley == NULL)
			return false;

	sky = loadtex("tex1.bmp");
	if (sky == NULL)
		return false;


	glBindTexture(GL_TEXTURE_2D, smiley->texID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, smiley->width, smiley->height, 
			 GL_RGb, GL_UNSIGNED_BYTE, smiley->data);

	glBindTexture(GL_TEXTURE_2D, sky->texID);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, sky->width, sky->height, 
			  GL_RGB, GL_UNSIGNED_BYTE, sky->data);

	glActiveTextureARB(GL_TEXTURE0_ARB);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, smiley->texID); 

	glActiveTextureARB(GL_TEXTURE1_ARB);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, sky->texID); 

	return true;
}
In deze functie worden de textures dus geladen en gekoppeld aan de huidige texture unit.

////////////////////////////////////////////////////////////////////////////
//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);

	initmultitex();
	loadtextures();

}
Initgl is natuurlijk ook de functie om multitexturing te inintialiseren en de textures te laden.

////////////////////////////////////////////////////////////////////////////
//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);
}

En het laatste stuk code natuurlijk nog wat je ondertussen al wel kunt dromen.

Succes

Vampire,