opengl中使用多重紋理
阿新 • • 發佈:2019-02-20
兩個原始紋理
混合後的效果圖
頂點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;
}