1. 程式人生 > >opengl中使用多重紋理

opengl中使用多重紋理

兩個原始紋理


混合後的效果圖


頂點shader

attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;

uniform mat4 M;
uniform mat4 P;
uniform mat4 V;

varying vec2 V_Texcoord;
void main()
{
	V_Texcoord=texcoord;
	gl_Position=P*V*M*vec4(pos,1.0);
}

片元shader
varying vec2 V_Texcoord;
uniform sampler2D U_MainTexture;
uniform sampler2D U_Wood;
void main()
{
	gl_FragColor=texture2D(U_MainTexture,V_Texcoord)*0.8+texture2D(U_Wood,V_Texcoord)*0.8;
}

程式入口
#include <windows.h>
#include "glew.h"
#include <stdio.h>
#include <math.h>
#include "utils.h"
#include "GPUProgram.h"
#include "ObjModel.h"
#include "Glm/glm.hpp"
#include "Glm/ext.hpp"
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glew32.lib")

LRESULT CALLBACK GLWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_CLOSE:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProc(hwnd,msg,wParam,lParam);
}

float* CreatePerspective(float fov, float aspect, float zNear, float zFar)
{
	float *matrix = new float[16];
	float half = fov / 2.0f;
	float randiansOfHalf = (half / 180.0f)*3.14f;
	float yscale = cosf(randiansOfHalf) / sinf(randiansOfHalf);
	float xscale = yscale / aspect;
	memset(matrix, 0, sizeof(float) * 16);
	matrix[0] = xscale;
	matrix[5] = yscale;
	matrix[10] = (zNear + zFar) / (zNear - zFar);
	matrix[11] = -1.0f;
	matrix[14] = (2.0f*zNear*zFar) / (zNear - zFar);
	return matrix;
}


INT WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
	WNDCLASSEX wndClass;
	wndClass.cbClsExtra = 0;
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.cbWndExtra = 0;
	wndClass.hbrBackground = NULL;
	wndClass.hCursor = LoadCursor(NULL,IDC_ARROW);
	wndClass.hIcon = NULL;
	wndClass.hIconSm = NULL;
	wndClass.hInstance = hInstance;
	wndClass.lpfnWndProc=GLWindowProc;
	wndClass.lpszClassName = L"OpenGL";
	wndClass.lpszMenuName = NULL;
	wndClass.style = CS_VREDRAW | CS_HREDRAW;
	ATOM atom = RegisterClassEx(&wndClass);

	HWND hwnd = CreateWindowEx(NULL, L"OpenGL", L"RenderWindow", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, NULL, NULL, hInstance, NULL);
	HDC dc = GetDC(hwnd);
	PIXELFORMATDESCRIPTOR pfd;
	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_TYPE_RGBA | PFD_DOUBLEBUFFER;
	pfd.iLayerType = PFD_MAIN_PLANE;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 32;
	pfd.cDepthBits = 24;
	pfd.cStencilBits = 8;

	int pixelFormatID = ChoosePixelFormat(dc, &pfd);

	SetPixelFormat(dc,pixelFormatID,&pfd);

	HGLRC rc = wglCreateContext(dc);
	wglMakeCurrent(dc, rc);

	glewInit();//初始化環境
	//建立兩個紋理
	GLuint mainTexture = CreateTextureFromFile("Debug/res/image/test.bmp");
	GLuint woodTexture = CreateTextureFromFile("Debug/res/image/wood.bmp");
	//初始化並編譯shader
	GPUProgram gpuProgram;
	gpuProgram.AttachShader(GL_VERTEX_SHADER, "Debug/res/shader/sample.vs");
	gpuProgram.AttachShader(GL_FRAGMENT_SHADER, "Debug/res/shader/sample.fs");
	gpuProgram.Link();

	//獲取shader中變數的引用
	gpuProgram.DetectAttribute("pos");
	gpuProgram.DetectAttribute("texcoord");
	gpuProgram.DetectAttribute("normal");
	gpuProgram.DetectUniform("M");
	gpuProgram.DetectUniform("V");
	gpuProgram.DetectUniform("P");
	gpuProgram.DetectUniform("U_MainTexture");
	gpuProgram.DetectUniform("U_Wood");
	//載入並解析3D模型
	ObjModel model;
	model.Init("Debug/res/model/Cube.obj");
	//單位矩陣 作為View矩陣
	float identity[] = {
		1.0f,0,0,0,
		0,1.0f,0,0,
		0,0,1.0f,0,
		0,0,0,1.0f
	};
	float M[] = {
		1.0f,0,0,0,
		0,1.0f,0,0,
		0,0,1.0f,0,
		0,0,-2.0f,1.0f
	};
	//模型矩陣
	glm::mat4 modelMatrix = glm::translate<float>(0.0f,0.0f,-2.0f)*glm::rotate<float>(-30.0f,0.0f,1.0f,1.0f);
	//建立投影矩陣
	float *projection = CreatePerspective(50.0f,800.0f/600.0f,0.1f,1000.0f);

	glClearColor(41.0f/255.0f,  71.0f/255.0f, 121.0f / 255.0f, 1.0f);
	ShowWindow(hwnd, SW_SHOW);
	UpdateWindow(hwnd);

	MSG msg;
	while (true)
	{
		if (PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
		{
			if (msg.message==WM_QUIT)
			{
				break;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		//清除顏色緩衝深度緩衝
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
		//啟用深度緩衝
		glEnable(GL_DEPTH_TEST);
		//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		//啟用shader
		glUseProgram(gpuProgram.mProgram);

		//給shader中的MVP矩陣分別設定值
		glUniformMatrix4fv(gpuProgram.GetLocation("M"), 1,GL_FALSE, glm::value_ptr(modelMatrix));
		glUniformMatrix4fv(gpuProgram.GetLocation("V"), 1, GL_FALSE, identity);
		glUniformMatrix4fv(gpuProgram.GetLocation("P"), 1, GL_FALSE, projection);


		glActiveTexture(GL_TEXTURE0);//啟用0號紋理
		glBindTexture(GL_TEXTURE_2D, mainTexture);//繫結0號紋理
		glUniform1i(gpuProgram.GetLocation("U_MainTexture"),0);//將0號紋理繫結到shader中的紋理變數

		glActiveTexture(GL_TEXTURE1);//啟用1號紋理
		glBindTexture(GL_TEXTURE_2D,woodTexture);//繫結1號紋理
		glUniform1i(gpuProgram.GetLocation("U_Wood"),1);//將1號紋理繫結到shader中的紋理變數

		model.Bind(gpuProgram.GetLocation("pos"), gpuProgram.GetLocation("texcoord"), gpuProgram.GetLocation("normal"));
		model.Draw();

		glUseProgram(0);
		glFinish();
		SwapBuffers(dc);
	}
	return 0;
}
模型解析
#include "ObjModel.h"
#include "utils.h"
#include <stdio.h>
#include <sstream>
#include <string>
#include <vector>

void ObjModel::Init(const char*modelFilePath)
{
	struct VertexInfo
	{
		float v[3];
		VertexInfo()
		{
			memset(v, 0, sizeof(float) * 3);
		}
	};

	struct VertexDefine
	{
		int positionIndex;
		int texcoordIndex;
		int normalIndex;
	};
	std::vector<VertexInfo> positions;
	std::vector<VertexInfo> texcoords;
	std::vector<VertexInfo> normals;

	std::vector<VertexDefine> vertices;
	std::vector<unsigned int> faces;

	//load model from file
	char*fileContent = LoadFileContent(modelFilePath);
	//decode model
	std::stringstream ssFileContent(fileContent);
	char szOneLine[256];
	std::string temp;
	while (!ssFileContent.eof())
	{
		memset(szOneLine, 0, 256);
		ssFileContent.getline(szOneLine, 256);
		if (strlen(szOneLine)>0)
		{
			std::stringstream ssOneLine(szOneLine);
			if (szOneLine[0]=='v')
			{
				if (szOneLine[1]=='t')
				{
					VertexInfo vi;
					ssOneLine >> temp;//vt
					ssOneLine >> vi.v[0];
					ssOneLine >> vi.v[1];
					texcoords.push_back(vi);
				}
				else if (szOneLine[1]=='n')
				{
					VertexInfo vi;
					ssOneLine >> temp;//vn
					ssOneLine >> vi.v[0];
					ssOneLine >> vi.v[1];
					ssOneLine >> vi.v[2];
					normals.push_back(vi);
				}
				else
				{
					VertexInfo vi;
					ssOneLine >> temp;//v
					ssOneLine >> vi.v[0];
					ssOneLine >> vi.v[1];
					ssOneLine >> vi.v[2];
					positions.push_back(vi);
				}
			}
			else if(szOneLine[0]=='f')
			{
				//
				ssOneLine >> temp;//f
				std::string vertexStr;
				for (int i = 0; i < 3; ++i)
				{
					ssOneLine >> vertexStr;
					size_t pos = vertexStr.find_first_of('/');
					std::string positionIndexStr = vertexStr.substr(0, pos);
					size_t pos2 = vertexStr.find_first_of('/', pos + 1);
					std::string texcoordIndexStr = vertexStr.substr(pos + 1, pos2 - pos - 1);
					std::string normalIndexStr = vertexStr.substr(pos2 + 1, vertexStr.length() - pos2 - 1);
					VertexDefine vd;
					vd.positionIndex = atoi(positionIndexStr.c_str())-1;
					vd.texcoordIndex = atoi(texcoordIndexStr.c_str())-1;
					vd.normalIndex = atoi(normalIndexStr.c_str())-1;
					//trim the same vertice
					int nCurrentVertexIndex = -1;
					size_t nCurrentVerticeCount = vertices.size();
					for(int j=0;j<nCurrentVerticeCount;++j)
					{
						if (vertices[j].positionIndex==vd.positionIndex&&
							vertices[j].texcoordIndex == vd.texcoordIndex&&
							vertices[j].normalIndex == vd.normalIndex)
						{
							nCurrentVertexIndex = j;
							break;
						}
					}
					if (nCurrentVertexIndex==-1)
					{
						nCurrentVertexIndex = (int)vertices.size();
						vertices.push_back(vd);
					}
					faces.push_back(nCurrentVertexIndex);
				}
			}
		}
	}
	//convert to opengl vbo & ibo
	int vertexCount = (int)vertices.size();
	VertexData*vertexes = new VertexData[vertexCount];
	for (int i=0;i<vertexCount;++i)
	{
		memcpy(vertexes[i].position, positions[vertices[i].positionIndex].v, sizeof(float) * 3);
		memcpy(vertexes[i].texcoord, texcoords[vertices[i].texcoordIndex].v, sizeof(float) * 2);
		memcpy(vertexes[i].normal, normals[vertices[i].normalIndex].v, sizeof(float) * 3);
	}
	//create vbo
	glGenBuffers(1, &mVBO);
	glBindBuffer(GL_ARRAY_BUFFER, mVBO);
	glBufferData(GL_ARRAY_BUFFER,sizeof(VertexData)*vertexCount,vertexes,GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	delete vertexes;

	//ibo
	mIndexCount = (int)faces.size();
	unsigned int *indexes = new unsigned int[mIndexCount];
	for (int i=0;i<mIndexCount;++i)
	{
		indexes[i] = faces[i];
	}
	glGenBuffers(1, &mIBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*mIndexCount, indexes, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	delete indexes;

	delete fileContent;
}

void ObjModel::Bind(GLint posLoc, GLint texcoordLoc, GLint normalLoc)
{
	glBindBuffer(GL_ARRAY_BUFFER, mVBO);//繫結到VBO
	glEnableVertexAttribArray(posLoc);//啟用頂點屬性
	glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), 0);//設定讀取模型頂點的起始地址

	glEnableVertexAttribArray(texcoordLoc);//啟用頂點紋理屬性
	//設定紋理資料的起始地址
	glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)(sizeof(float) * 3));

	glEnableVertexAttribArray(normalLoc);//啟用頂點法線屬性
	//設定讀取法線的起始地址
	glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)(sizeof(float) * 5));
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void ObjModel::Draw()
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
	glDrawElements(GL_TRIANGLES, mIndexCount, GL_UNSIGNED_INT, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

工具類
#include <stdio.h>
#include <windows.h>
#include "utils.h"

char* LoadFileContent(const char*path)
{
	char *pFileContent = NULL;
	FILE*pFile = fopen(path, "rb");
	if (pFile)
	{
		fseek(pFile, 0, SEEK_END);
		int nLen = ftell(pFile);
		if (nLen > 0)
		{
			rewind(pFile);
			pFileContent = new char[nLen + 1];
			fread(pFileContent, 1, nLen, pFile);
			pFileContent[nLen] = '\0';
		}
		fclose(pFile);
	}
	return pFileContent;
}



unsigned char* LoadBMP(const char*path, int &width, int &height)
{
	unsigned char*imageData = nullptr;
	FILE *pFile = fopen(path, "rb");
	if (pFile)
	{
		BITMAPFILEHEADER bfh;
		fread(&bfh, sizeof(BITMAPFILEHEADER), 1, pFile);
		if (bfh.bfType == 0x4D42)
		{
			BITMAPINFOHEADER bih;
			fread(&bih, sizeof(BITMAPINFOHEADER), 1, pFile);
			width = bih.biWidth;
			height = bih.biHeight;
			int pixelCount = width*height * 3;
			imageData = new unsigned char[pixelCount];
			fseek(pFile, bfh.bfOffBits, SEEK_SET);
			fread(imageData, 1, pixelCount, pFile);

			unsigned char temp;
			for (int i = 0; i < pixelCount; i += 3)
			{
				temp = imageData[i + 2];
				imageData[i + 2] = imageData[i];
				imageData[i] = temp;
			}
		}
		fclose(pFile);
	}
	return imageData;
}


GLuint CreateTextureFromFile(const char*filePath)
{
	unsigned char*imageData;
	int width, height;
	imageData = LoadBMP(filePath,width,height);

	GLuint texture;
	glGenTextures(1, &texture);//建立一個紋理
	glBindTexture(GL_TEXTURE_2D,texture);//繫結
	//GL_TEXTURE_WRAP系列引數用來設定當這些超出邊界時應該怎樣處理
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	//當攝像機離物體的距離發生變化時,紋理內容的插值方式
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		//將紋理內容上傳至GPU
	glTexImage2D(GL_TEXTURE_2D,
					0,
					GL_RGBA,
					width,
					height,
					0,
					GL_RGB,
					GL_UNSIGNED_BYTE,
					imageData
					);
	glBindTexture(GL_TEXTURE_2D,0);
	return texture;

}

shader載入編譯類
#include "GPUProgram.h"
#include "utils.h"
#include <stdio.h>

GLuint CompileShader(const char*shaderPath, GLenum shaderType)
{
	GLuint shader = glCreateShader(shaderType);
	const char* code = LoadFileContent(shaderPath);
	if (code == nullptr)
	{
		printf("cannot load shader source from file %s\n", shaderPath);
		return 0;
	}
	glShaderSource(shader, 1, &code, nullptr);//ram -> vram
	glCompileShader(shader);
	GLint nResult;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &nResult);
	if (nResult == GL_FALSE)
	{
		printf("compile shader %s fail\n", shaderPath);
		GLint logLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
		char*log = new char[logLength];
		GLsizei writed = 0;
		glGetShaderInfoLog(shader, logLength, &writed, log);
		printf("%s\n", log);
		delete log;
		glDeleteShader(shader);
		return 0;
	}
	return shader;
}


GPUProgram::GPUProgram()
{
	mProgram = glCreateProgram();
}

GPUProgram::~GPUProgram()
{

}

void GPUProgram::AttachShader(GLenum shaderType, const char*shaderPath)
{
	GLuint shader = CompileShader(shaderPath, shaderType);
	if (shader!=0)
	{
		glAttachShader(mProgram, shader);
		mAttachedShaders.push(shader);
	}
}

void GPUProgram::Link()
{
	glLinkProgram(mProgram);
	GLint nResult;
	glGetProgramiv(mProgram, GL_LINK_STATUS, &nResult);
	if (nResult == GL_FALSE)
	{
		printf("create gpu program fail,link error\n");
		GLint logLength;
		glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &logLength);
		char*log = new char[logLength];
		GLsizei writed = 0;
		glGetProgramInfoLog(mProgram, logLength, &writed, log);
		printf("%s\n", log);
		delete log;
		glDeleteProgram(mProgram);
		mProgram = 0;
	}
	while (!mAttachedShaders.empty())
	{
		GLuint shader = mAttachedShaders.top();
		glDetachShader(mProgram, shader);
		glDeleteShader(shader);
		mAttachedShaders.pop();
	}
}

void GPUProgram::DetectAttribute(const char*attributeName)
{
	GLint loc = glGetAttribLocation(mProgram, attributeName);
	if (loc!=-1)
	{
		mLocations.insert(std::pair<std::string,GLint>(attributeName,loc));
	}
}

void GPUProgram::DetectUniform(const char*uniformName)
{
	GLint loc = glGetUniformLocation(mProgram, uniformName);
	if (loc != -1)
	{
		mLocations.insert(std::pair<std::string, GLint>(uniformName, loc));
	}
}

GLint GPUProgram::GetLocation(const char*name)
{
	auto iter = mLocations.find(name);
	if (iter ==mLocations.end())
	{
		return -1;
	}
	return iter->second;
}