テクスチャ付きOBJファイルを簡単なshaderでレンダリング

14833 ワード

ネット上の資料がばらばらであることを考慮して、ここでは自分で使う文章をまとめたほうがいい.
1.いくつかの基礎操作
このブログはとても詳しくて、勉強したり調べたりして見ることができます.
http://blog.csdn.net/wangdingqiaoit/article/details/51457675
2.ここではネット上で書かれたツール類GLFundamentals.hppを紹介します.定義された機能がたくさん入っていて、便利です.後でここにアップロードします.
 2.1リードライトシェーダー
方法1.ツールクラスの使用
直接含めるだけでいいです.もちろんopenGLの環境は自分で配置します.
GLuint program = gl::init_program("vertex.glsl", "fragment.glsl")

ここでvertex.glslとfragment.glslは別々のshaderテキストファイルであり、上記の一言で直接①ポインタがshaderを指す②compile shader③プログラムを作成する④link shader⑤現在のプログラムに戻る
方法2.shaderをcppファイルに簡単に直接書き、簡単なshaderスクリプトに適しています.くりを一つあげる
#define VPROG_SRC_TEX(ver)									\
	ver											\
	"layout(location = 0) in vec3 pos1;
" \ "layout(location = 2) in vec2 vTexCoord;
" \ "
" \ " out vec2 fTexCoord;
" \ "
" \ "uniform mat4 worldMatrix;
" \ "uniform mat4 projMatrix;
" \ "
" \ "void main()
" \ "{
" \ " gl_Position = (projMatrix * worldMatrix) * vec4(pos1,1);
" \ " fTexCoord = vTexCoord;
" \ "}
" \ static const char* kGlesVProgTex = VPROG_SRC_TEX("#version 330
"); #undef VPROG_SRC_TEX #define FSHADER_SRC_TEX(ver) \ ver \ "uniform sampler2D DiffuseTexture;
" \ "in vec2 fTexCoord;
" \ " out vec4 fragColor;
" \ "
" \ "void main()
" \ "{
" \ "vec4 tD = texture(DiffuseTexture, fTexCoord);
"\ "fragColor.rgb = vec3(tD);
" \ "fragColor.a = tD.a;
" \ "}
" \ static const char* kGlesFShaderTex = FSHADER_SRC_TEX("#version 330
"); #undef FSHADER_SRC_TEX
このようにshaderのポインタを直接得て、それからいつものやり方です
static GLuint CreateShader(GLenum type, const char* text)
{
	GLuint ret = glCreateShader(type);
	glShaderSource(ret, 1, &text, NULL);
	glCompileShader(ret);

    GLint compileResult = GL_TRUE;  
    glGetShaderiv(ret,GL_COMPILE_STATUS,&compileResult); 
    if(compileResult == GL_FALSE){
        char szLog[1024] = {0};  
        GLsizei logLen = 0;  
        glGetShaderInfoLog(ret,1024,&logLen,szLog); 
        LogToFile::log("shaderError.txt","log: %s 
code: %s
",szLog,text); } return ret; } g_VProg_tex = CreateShader(GL_VERTEX_SHADER, kGlesVProgTex); g_FShader_tex = CreateShader_temp(GL_FRAGMENT_SHADER, kGlesFShaderTex); g_Program_tex = glCreateProgram(); //glBindAttribLocation(g_Program_tex, 0, "pos1"); //glBindAttribLocation(g_Program_tex, 2, "fTexCoord"); glAttachShader(g_Program_tex, g_VProg_tex); glAttachShader(g_Program_tex, g_FShader_tex); //glBindFragDataLocation(g_Program_tex, 0, "fragColor"); glLinkProgram(g_Program_tex);

フォーマットはやや醜いが,次は本文だ
3.OBJの読み出し
OBJというフォーマットは分かりやすく、ネットでも詳しく説明されています.簡単なmeshであれば、頂点posとindexだけを読めばいいのですが、次は私が書いたUtilで、テクスチャ付きのOBJを読めます.
GLuint  LoadModelTexture(string filename){
    GLuint texture_ID;

    Mat I = imread(filename);
    //    
    int width = I.cols;
    int height = I.rows;
    //      
    GLubyte* pixels;
	//      
	int pixellength = width*height * 3;
	pixels = new GLubyte[pixellength];
	memcpy(pixels, I.data, pixellength*sizeof(char));
	

	// texture_ID   2D    
	glGenTextures(1, &texture_ID);
	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);
    
    //LogToFile::log("errorcpp.txt","LoadModelTexture1
"); // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); // free(pixels); delete [] pixels; //LogToFile::log("errorcpp.txt","LoadModelTexture2
"); return texture_ID; }
の上には、画像を読み取り、バインドしてIDを返します.OpenCVを使って読み取ったものは、もちろんSOIL、devILなどでもいいですが、画像ポインタが得られればOKです.
次にOBJを解析し、bufferにバインドします.
tag3DModel ImportOBJModel(string objName){
    // LogToFile::log("errorcpp.txt","start do ImportOBJModel
"); tag3DModel t3DModel; std::ifstream fin(objName.c_str()); char chkeyword = 0; string s,s1,s2,s3; while (getline(fin, s)){ istringstream in(s); string sNAN; chkeyword = s[0]; switch(chkeyword){ case 'm': { t3DModel.bIsTextured = true; break; } case 'u':{ t3DObject t3DObj; // string s1 = s.substr(7); //"usemtl " tagMaterialInfo tinfo; tinfo.strName = "textured_" + s1 + "_map_Kd.png"; //LogToFile::log("errorcpp.txt","strName = %s
",tinfo.strName.c_str()); GLuint texture_ID = LoadModelTexture(tinfo.strName); // LogToFile::log("errorcpp.txt","end u
"); tinfo.ID = texture_ID; t3DModel.tMatInfoVec.push_back(tinfo); t3DObj.nMaterialID = texture_ID; t3DModel.t3DObjVec.push_back(t3DObj); t3DModel.objCount++; break; } case 'v':{ chkeyword = s[1]; if(chkeyword == ' '){ vec3 vNewVert; in>>sNAN>>s1>>s2>>s3; //LogToFile::log("errorcpp.txt","%s %s %s
",s1.c_str(),s2.c_str(),s3.c_str()); vNewVert.x = atof(s1.c_str()); vNewVert.y = -atof(s2.c_str()); vNewVert.z = -atof(s3.c_str()); // LogToFile::log("errorcpp.txt","v %f %f %f
",vNewVert.x,vNewVert.y,vNewVert.z); t3DModel.m_pos.push_back(vNewVert); }else if(chkeyword == 't'){ in>>sNAN>>s1>>s2; vec2 newTex; newTex.u = atof(s1.c_str()); newTex.v =1.0 - atof(s2.c_str()); //LogToFile::log("errorcpp.txt","vt %f %f
",newTex.u,newTex.v); t3DModel.m_texcoord.push_back(newTex); }else if(chkeyword == 'n'){ in>>sNAN>>s1>>s2>>s3; vec3 newNormal; newNormal.x = atof(s1.c_str()); newNormal.y = atof(s2.c_str()); newNormal.z = atof(s3.c_str()); //LogToFile::log("errorcpp.txt","vn %f %f %f
",newNormal.x,newNormal.y,newNormal.z); t3DModel.m_normal.push_back(newNormal); } break; } case 'f':{ //v/t/n //LogToFile::log("errorcpp.txt","f
"); t3DObject *pCurObj = &t3DModel.t3DObjVec[t3DModel.objCount-1]; unsigned int vIdx = 0, tIdx = 0, nIdx = 0; vec3 vPosVert; vec2 vTexcoord; vec3 vNormal; size_t nDistance = 0; in>>sNAN>>s1>>s2>>s3; string pattern = "/"; //LogToFile::log("errorcpp.txt","%s %s %s
",s1.c_str(),s2.c_str(),s3.c_str()); int pos = s1.find(pattern.c_str(),0); string sTemp = s1.substr(0,pos); vIdx = atoi(sTemp.c_str()); // LogToFile::log("errorcpp.txt","%d %s
",vIdx,sTemp.c_str()); int pos1 = s1.find(pattern.c_str(),pos+1); sTemp = s1.substr(pos+1,pos1-pos-1); tIdx = atoi(sTemp.c_str()); //LogToFile::log("errorcpp.txt","%d %s
",tIdx,sTemp.c_str()); sTemp = s1.substr(pos1+1); nIdx = atoi(sTemp.c_str()); //LogToFile::log("errorcpp.txt","%d %s
",nIdx,sTemp.c_str()); //LogToFile::log("errorcpp.txt","f %d/%d/%d ",vIdx,tIdx,nIdx); //LogToFile::log("errorcpp.txt","m_pos size = %d
",t3DModel.m_pos.size()); std::map::iterator pFindPos = t3DModel.m_VObjectIndexMap.find(tVertInfo(t3DModel.m_pos[vIdx - 1], t3DModel.m_texcoord[tIdx - 1], t3DModel.m_normal[nIdx - 1])); if(t3DModel.m_VObjectIndexMap.end() != pFindPos) { pCurObj->Indexes.push_back(pFindPos->second); }else{ pCurObj->PosVerts.push_back(t3DModel.m_pos[vIdx - 1]); pCurObj->Texcoords.push_back (t3DModel.m_texcoord[tIdx - 1]); pCurObj->Normals.push_back(t3DModel.m_normal[nIdx - 1]); pCurObj->Indexes.push_back(pCurObj->PosVerts.size()-1); } pos = s2.find(pattern.c_str(),0); sTemp = s2.substr(0,pos); vIdx = atoi(sTemp.c_str()); pos1 = s2.find(pattern.c_str(),pos+1); sTemp = s2.substr(pos+1,pos1-pos-1); tIdx = atoi(sTemp.c_str()); sTemp = s2.substr(pos1+1); nIdx = atoi(sTemp.c_str()); // pCurObj->PosVerts.push_back(t3DModel.m_pos[vIdx - 1]); // pCurObj->Texcoords.push_back (t3DModel.m_texcoord[tIdx - 1]); // pCurObj->Normals.push_back(t3DModel.m_normal[nIdx - 1]); // pCurObj->Indexes.push_back(pCurObj->PosVerts.size()-1); //LogToFile::log("errorcpp.txt"," %d/%d/%d ",vIdx,tIdx,nIdx); std::map::iterator pFindPos1 = t3DModel.m_VObjectIndexMap.find(tVertInfo(t3DModel.m_pos[vIdx - 1], t3DModel.m_texcoord[tIdx - 1], t3DModel.m_normal[nIdx - 1])); if(t3DModel.m_VObjectIndexMap.end() != pFindPos1) { pCurObj->Indexes.push_back(pFindPos1->second); }else{ pCurObj->PosVerts.push_back(t3DModel.m_pos[vIdx - 1]); pCurObj->Texcoords.push_back (t3DModel.m_texcoord[tIdx - 1]); pCurObj->Normals.push_back(t3DModel.m_normal[nIdx - 1]); pCurObj->Indexes.push_back(pCurObj->PosVerts.size()-1); } pos = s3.find(pattern.c_str(),0); sTemp = s3.substr(0,pos); vIdx = atoi(sTemp.c_str()); pos1 = s3.find(pattern.c_str(),pos+1); sTemp = s3.substr(pos+1,pos1-pos-1); tIdx = atoi(sTemp.c_str()); sTemp = s3.substr(pos1+1); nIdx = atoi(sTemp.c_str()); // pCurObj->PosVerts.push_back(t3DModel.m_pos[vIdx - 1]); // pCurObj->Texcoords.push_back (t3DModel.m_texcoord[tIdx - 1]); // pCurObj->Normals.push_back(t3DModel.m_normal[nIdx - 1]); // pCurObj->Indexes.push_back(pCurObj->PosVerts.size()-1); //LogToFile::log("errorcpp.txt"," %d/%d/%d
",vIdx,tIdx,nIdx); std::map::iterator pFindPos2 = t3DModel.m_VObjectIndexMap.find(tVertInfo(t3DModel.m_pos[vIdx - 1], t3DModel.m_texcoord[tIdx - 1], t3DModel.m_normal[nIdx - 1])); if(t3DModel.m_VObjectIndexMap.end() != pFindPos2) { pCurObj->Indexes.push_back(pFindPos2->second); }else{ pCurObj->PosVerts.push_back(t3DModel.m_pos[vIdx - 1]); pCurObj->Texcoords.push_back (t3DModel.m_texcoord[tIdx - 1]); pCurObj->Normals.push_back(t3DModel.m_normal[nIdx - 1]); pCurObj->Indexes.push_back(pCurObj->PosVerts.size()-1); } break; } } } //LogToFile::log("errorcpp.txt","allocate buffer
"); for(unsigned int i = 0; i < t3DModel.t3DObjVec.size(); i++) { glGenBuffers(1, &t3DModel.t3DObjVec[i].nPosVBO); glBindBuffer(GL_ARRAY_BUFFER, t3DModel.t3DObjVec[i].nPosVBO); glBufferData(GL_ARRAY_BUFFER, t3DModel.t3DObjVec[i].PosVerts.size() * sizeof(vec3), (GLvoid*)&t3DModel.t3DObjVec[i].PosVerts[0], usage); glGenBuffers(1, &t3DModel.t3DObjVec[i].nNormVBO); glBindBuffer(GL_ARRAY_BUFFER, t3DModel.t3DObjVec[i].nNormVBO); glBufferData(GL_ARRAY_BUFFER, t3DModel.t3DObjVec[i].Normals.size() * sizeof(vec3), (GLvoid*)&t3DModel.t3DObjVec[i].Normals[0], usage); glGenBuffers(1, &t3DModel.t3DObjVec[i].nTexcoordVBO); glBindBuffer(GL_ARRAY_BUFFER, t3DModel.t3DObjVec[i].nTexcoordVBO); glBufferData(GL_ARRAY_BUFFER, t3DModel.t3DObjVec[i].Texcoords.size() * sizeof(vec2), (GLvoid*)&t3DModel.t3DObjVec[i].Texcoords[0], usage); glGenBuffers(1, &t3DModel.t3DObjVec[i].nIndexVBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, t3DModel.t3DObjVec[i].nIndexVBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, t3DModel.t3DObjVec[i].Indexes.size() * sizeof(unsigned int), (GLvoid*)&t3DModel.t3DObjVec[i].Indexes[0], usage); t3DModel.t3DObjVec[i].nNumIndexes = t3DModel.t3DObjVec[i].Indexes.size(); } return t3DModel; }

「f」を扱うときはコードが美しくなく、ループ処理に変えればいいのですが、私は直すのがおっくうだとは言いません.hiahiiahia~~~そして中の構造体は自分で勝手に定義して、欲しい情報を含んでいればいいのです.
呼び出しにはわずか2ステップしかかかりません.
tag3DModel t3DModel = ImportOBJModel("textured.obj");
drawModel(t3DModel,g_Word2Cam,g_ProjMat);


4.TIPS
シェーダーを書くときにエラーをチェックするのは難しいので、glGetShaderInfoLogでチェックすることをお勧めします.それからLogToFileファイルはerrorをテキストファイルに書く補助クラスツールで、直接coutやprintを削除することができます.
テクスチャのUVを読み取る場合、座標系が異なるため、V座標は逆になりますので、1-Vが必要です
MyOpenGLUtil.h
LogToFile.h
差は多くありませんが、何か漏れがあったら後で補充します...