最新公告
  • 欢迎您光临波比源码,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • DirectX的OBJ模型加载与渲染

    在之前的DirectX例子里我用的模型是.x文件,DirectX有1个方法D3DXLoadMeshFromX可以加载.x模型,但是这里有个问题,.x文件是没法用文本编辑器打开查看结构的,这里我来演示1下如何解析.obj模型.

    首先让我们看1下.obj模型的组成部份和结构,1个完全的obj模型1共分为3个部份:obj模型文件,mtl材质文件,纹理贴图;其中obj文件和mtl文件是可以用文本编辑器打开的,先打开obj文件,可以看到这样的内容:

    v ⑶.000767 2.993211 2.014205
    v ⑶.000767 -0.006789 2.014205
    v ⑵.750767 2.993211 2.014205
    v ⑵.750767 -0.006789 2.014205
    v ⑵.750767 2.993211 2.014205
    v ⑵.750767 -0.006789 2.014205
    v ⑵.750767 2.993211 ⑴.985795
    v ⑵.750767 -0.006789 ⑴.985795
    v ⑵.750767 2.993211 ⑴.985795

    vt 0.948633 0.500977
    vt 0.948633 0.000977
    vt 0.998633 0.500977
    vt 0.998633 0.000977
    vt 0.000000 0.500000
    vt 0.000000 0.000000
    vt 1.000000 0.500000
    vt 1.000000 0.000000
    vt 1.000000 0.501343
    vt 0.000000 0.501343
    vt 1.000000 0.438843

    vn 0.000000 0.000000 1.000000
    vn 1.000000 0.000000 0.000000
    vn 0.000000 0.000000 ⑴.000000
    vn ⑴.000000 0.000000 0.000000
    vn 0.000000 1.000000 0.000000
    vn 0.000000 ⑴.000000 0.000000
    vn -0.000000 -0.707107 -0.707107
    vn 0.000000 0.707107 0.707107
    vn -0.000000 0.707107 -0.707106

    g Left
    usemtl wood
    s 1
    f 1/1/1 2/2/1 3/3/1
    f 2/2/1 4/4/1 3/3/1
    s 2
    f 5/5/2 6/6/2 7/7/2
    f 6/6/2 8/8/2 7/7/2
    s 1
    f 9/3/3 10/4/3 11/1/3
    f 10/4/3 12/2/3 11/1/3
    s 2
    f 13/7/4 14/8/4 15/5/4
    f 14/8/4 16/6/4 15/5/4
    s 3
    f 17/9/5 18/10/5 19/11/5
    f 18/10/5 20/12/5 19/11/5
    f 21/10/6 22/9/6 23/12/6
    f 22/9/6 24/11/6 23/12/6

    让我来解释1下字段:

    首先v和其后3个值表示1个顶点的xyz坐标值;

    vt和其后两个或3个值表示顶点的纹理坐标uv(w);

    vn和其后3个值表示顶点的法向量;

    g表示1组面;

    usemtl表示这个组用的mtl文件里那个材质的名称;

    f及其后3组值表示1个面的3个 顶点/纹理/法线 在之前v,vt,vn集合里边的索引值.

    打开mtl文件就会看到:

    newmtl wood
    illum 2
    Kd 0.800000 0.800000 0.800000
    Ka 0.200000 0.200000 0.200000
    Ks 0.000000 0.000000 0.000000
    Ke 0.000000 0.000000 0.000000
    Ns 0.000000
    map_Ka house/house.bmp
    map_Kd house/house.bmp

    newmtl后面的是材质名对应obj文件usemtl后面跟的值;

    为了简单代码里只用到了map_Kd,它表示漫反射所使用的纹理名称;

    其他的是光照属性,代码里采取默许材质的光照属性;

    为了解析模型,首先要把材质文件给解析出来,把材质名称与纹理名称放入数组,这样解析obj的时候通过材质名称就可以够找到对应材质数组的下标就可以找到对应的纹理:

    void MtlObj::getLineNum() {
    ifstream infile(path.c_str()); //打开指定文件
    string sline;//每行
    while(getline(infile,sline)) {//从指定文件逐行读取
    if(sline[0]=='n'&&sline[1]=='e')//newmtl
    mtlNum++;
    }
    infile.close();
    }

    void MtlObj::readfile() {
    getLineNum();
    names=new string[mtlNum];
    textures=new string[mtlNum];
    int n=0;
    int t=0;

    ifstream infile(path.c_str()); //打开指定文件
    string sline;//每行

    string value,name,texture;
    while(getline(infile,sline)) {//从指定文件逐行读取
    if(sline!="") {
    istringstream ins(sline);
    ins>>value;
    if(value=="newmtl") {
    ins>>name;
    names[n]=name;
    n++;
    } else if(value=="map_Kd") {
    ins>>texture;
    textures[t]=texture;
    t++;
    }
    }
    }
    infile.close();
    }

    int MtlObj::getIndexByName(string name) {
    int index=⑴;
    for(int i=0;i<mtlNum;i++) {
    if(names[i]==name) {
    index=i;
    break;
    }
    }
    return index;
    }

    准备好了材质信息以后开始解析obj文件:

    void ModelObj::getLineNum() {
    ifstream infile(path.c_str()); //打开指定文件
    string sline;//每行

    while(getline(infile,sline)) {//从指定文件逐行读取
    if(sline[0]=='v') {
    if(sline[1]=='n')
    vnNum++;
    else if(sline[1]=='t')
    vtNum++;
    else
    vNum++;
    }
    if(sline[0]=='f')
    fNum++;
    }
    infile.close();

    ifstream ifile(path.c_str());
    string value,um,group,face;
    mtArr=new string[fNum];
    groupArr=new int[fNum];
    groupNum=0;
    int fi=0;
    while(getline(ifile,sline)) {
    istringstream ins(sline);
    ins>>value;
    if(value=="usemtl") {
    ins>>um;
    int mtlId=mtl->getIndexByName(um);
    groupMtlMap.insert(pair<int,int>(groupNum,mtlId));
    } else if(value=="g") {
    ins>>group;
    groupNum++;
    } else if(value=="f") {
    ins>>face;
    mtArr[fi]=um;
    groupArr[fi]=groupNum;
    fi++;
    }
    }
    ifile.close();
    }

    通过材质名字查找到该材质在之前的材质数组中的id,这边需要groupMtlMap保存面组id与材质id,那样在渲染时就能够通过面组id找到对应的纹理贴图.

    获得基本信息后读入文件的详细内容:

    void ModelObj::readfile() {
    getLineNum();
    vertices=new NormalTexVertex[fNum*3];
    indices=new int[fNum*3];

    //new2维数组
    vArr=new float*[vNum];
    for (int i=0;i<vNum;i++)
    vArr[i]=new float[3];

    vnArr=new float*[vnNum];
    for (int i=0;i<vnNum;i++)
    vnArr[i]=new float[3];

    vtArr=new float*[vtNum];
    for (int i=0;i<vtNum;i++)
    vtArr[i]=new float[3];

    fvArr=new int*[fNum];
    ftArr=new int*[fNum];
    fnArr=new int*[fNum];
    for (int i=0;i<fNum;i++) {
    fvArr[i]=new int[3];
    ftArr[i]=new int[3];
    fnArr[i]=new int[3];
    }
    ifstream infile(path.c_str());
    string sline;//每行
    int ii=0,tt=0,jj=0,kk=0;

    std::string s1;
    float f2,f3,f4;

    while(getline(infile,sline)) {
    if(sline[0]=='v') {
    if(sline[1]=='n') {//vn
    istringstream ins(sline);
    ins>>s1>>f2>>f3>>f4;
    vnArr[ii][0]=f2;
    vnArr[ii][1]=f3;
    vnArr[ii][2]=f4;
    ii++;
    } else if(sline[1]=='t') {//vt
    istringstream ins(sline);
    ins>>s1>>f2>>f3>>f4;
    vtArr[tt][0]=f2;
    vtArr[tt][1]=1-f3;
    vtArr[tt][2]=f4;
    tt++;
    } else {//v
    istringstream ins(sline);
    ins>>s1>>f2>>f3>>f4;
    vArr[jj][0]=f2;
    vArr[jj][1]=f3;
    vArr[jj][2]=f4;
    jj++;
    }
    }
    if (sline[0]=='f') { //存储面
    istringstream in(sline);
    float a;
    in>>s1;//去掉f
    int i,k;
    for(i=0;i<3;i++) {
    in>>s1;
    //取出第1个顶点和法线索引
    a=0;
    for(k=0;s1[k]!='/';k++)
    a=a*10+(s1[k]⑷8);
    fvArr[kk][i]=a;

    a=0;
    for(k=k+1;s1[k]!='/';k++)
    a=a*10+(s1[k]⑷8);
    ftArr[kk][i]=a;

    a=0;
    for(k=k+1;s1[k];k++)
    a=a*10+(s1[k]⑷8);
    fnArr[kk][i]=a;
    }

    kk++;
    }
    }
    infile.close();
    }

    这里需要面数3倍的顶点,由于有n个3角形就有n*3个顶点,由于顶点之间可能不是同享法线和纹理坐标数据,因此不同3角形在同1个位置的顶点要分开来寄存;

    由于DirectX的纹理坐标轴v是朝下而模型的v坐标轴朝上,那末读取的纹理坐标v必须变成1-v才能有DirectX正确渲染,因而就有类似vtArr[tt][1]=1-f3这样的做法;

    渲染的时候需要索引指针,大小是模型面的数量*3;

    然后通过各种索引组装3角形:

    void ModelObj::initTriangles() {
    for (int i=0;i<fNum;i++) {
    int v1Index=i*3;
    int v2Index=i*3+1;
    int v3Index=i*3+2;

    vertices[v1Index].x=vArr[fvArr[i][0]⑴][0];
    vertices[v1Index].y=vArr[fvArr[i][0]⑴][1];
    vertices[v1Index].z=vArr[fvArr[i][0]⑴][2];
    vertices[v1Index].nx=vnArr[fnArr[i][0]⑴][0];
    vertices[v1Index].ny=vnArr[fnArr[i][0]⑴][1];
    vertices[v1Index].nz=vnArr[fnArr[i][0]⑴][2];
    vertices[v1Index].u=vtArr[ftArr[i][0]⑴][0];
    vertices[v1Index].v=vtArr[ftArr[i][0]⑴][1];

    vertices[v2Index].x=vArr[fvArr[i][1]⑴][0];
    vertices[v2Index].y=vArr[fvArr[i][1]⑴][1];
    vertices[v2Index].z=vArr[fvArr[i][1]⑴][2];
    vertices[v2Index].nx=vnArr[fnArr[i][1]⑴][0];
    vertices[v2Index].ny=vnArr[fnArr[i][1]⑴][1];
    vertices[v2Index].nz=vnArr[fnArr[i][1]⑴][2];
    vertices[v2Index].u=vtArr[ftArr[i][1]⑴][0];
    vertices[v2Index].v=vtArr[ftArr[i][1]⑴][1];

    vertices[v3Index].x=vArr[fvArr[i][2]⑴][0];
    vertices[v3Index].y=vArr[fvArr[i][2]⑴][1];
    vertices[v3Index].z=vArr[fvArr[i][2]⑴][2];
    vertices[v3Index].nx=vnArr[fnArr[i][2]⑴][0];
    vertices[v3Index].ny=vnArr[fnArr[i][2]⑴][1];
    vertices[v3Index].nz=vnArr[fnArr[i][2]⑴][2];
    vertices[v3Index].u=vtArr[ftArr[i][2]⑴][0];
    vertices[v3Index].v=vtArr[ftArr[i][2]⑴][1];

    indices[i*3]=v1Index;
    indices[i*3+1]=v2Index;
    indices[i*3+2]=v3Index;
    }

    clearTriangles();
    }

    组装结束,清除读取文件用的内存:

    void ModelObj::clearTriangles() {
    for(int i=0;i<vNum;i++)
    delete[] *(vArr+i);
    for(int i=0;i<vnNum;i++)
    delete[] *(vnArr+i);
    for(int i=0;i<vtNum;i++)
    delete[] *(vtArr+i);
    for(int i=0;i<fNum;i++) {
    delete[] *(fvArr+i);
    delete[] *(ftArr+i);
    delete[] *(fnArr+i);
    }

    delete[] vArr;
    delete[] vnArr;
    delete[] vtArr;
    delete[] fvArr;
    delete[] ftArr;
    delete[] fnArr;

    delete[] mtArr;
    }

    然后建立1个网格对象,把这些数据塞进去:

    ObjModel::ObjModel(ModelObj* obj) {
    objLoader=obj;
    D3DXCreateMeshFVF(objLoader->fNum,objLoader->fNum*3,D3DXMESH_MANAGED,normalTexVertFvf,d3d,&mesh);
    initVertices();
    initTextures();

    DWORD* aAdjacency=new DWORD[objLoader->fNum*3];
    mesh->GenerateAdjacency(0.001,aAdjacency);
    mesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT|D3DXMESHOPT_VERTEXCACHE,aAdjacency,NULL,NULL,NULL);
    delete[] aAdjacency;
    }

    void ObjModel::initVertices() {
    NormalTexVertex* vertices=NULL;
    mesh->LockVertexBuffer(0,(void**)&vertices);
    for(int i=0;i<objLoader->fNum*3;i++)
    vertices[i]=objLoader->vertices[i];
    mesh->UnlockVertexBuffer();

    WORD* indices=NULL;
    mesh->LockIndexBuffer(0,(void**)&indices);
    for(int i=0;i<objLoader->fNum*3;i++)
    indices[i]=objLoader->indices[i];
    mesh->UnlockIndexBuffer();

    DWORD* attributes=NULL;
    mesh->LockAttributeBuffer(0,&attributes);
    for(int i=0;i<objLoader->fNum;i++)
    attributes[i]=objLoader->groupArr[i]⑴;
    mesh->UnlockAttributeBuffer();
    }

    接着通过之前的材质数组创建需要的纹理对象:

    void ObjModel::initTextures() {
    objLoader->mtl->getLength(mtlNum);
    textures=new LPDIRECT3DTEXTURE9[mtlNum];
    for(int i=0;i<mtlNum;i++) {
    string texFile=TEX_PATH+objLoader->mtl->textures[i];
    D3DXCreateTextureFromFile(d3d,texFile.c_str(),&textures[i]);
    }
    }

    这下数据都准备好了,渲染吧:

    void ObjModel::render() {
    for(DWORD i=0;i<(DWORD)objLoader->groupNum;i++) {
    map<int,int>::iterator itor=objLoader->groupMtlMap.find((int)(i+1));
    int mtlId=itor->second;
    d3d->SetTexture(1,textures[mtlId]);
    mesh->DrawSubset(i);
    }
    }

    最后创建对象并渲染之:

    void initObjModel() {
    objLoader=new ModelObj(MODEL_TANK,MTL_TANK);
    objModel=new ObjModel(objLoader);
    }
    void renderObjModel() {
    objModel->render();
    }
    void releaseObjModel() {
    delete objModel;
    delete objLoader;
    }

    我导入了1个坦克模型,终究效果就像这样:

    波比源码 – 精品源码模版分享 | www.bobi11.com
    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!

    波比源码 » DirectX的OBJ模型加载与渲染

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    波比源码
    一个高级程序员模板开发平台
    升级波友尊享更多特权立即升级