Win10 OBJ Color Model


Windows10(Store) DirectX3D で OBJ Color Model を描画します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. Windows10(ストアアプリ)で Wavefront の「頂点+法線+色」が設定されたモデルを描画します。
    Win10 OBJ Model では頂点座標だけのモデルを描画しました。
    Win10 OBJ Normal Model では「頂点+法線」のモデルを描画しました。
    Win10 OBJ Preset Color Model ではプリセットのカラーモデルを描画しました。
    ここまでは比較的簡単ですがマテリアルファイル(*.mtl) を使うモデルは結構面倒です。
  2. 「頂点+法線+色」のモデルを定義した cube.obj です。プロジェクトには cube.txt の名前で組み込みます。
    マテリアル(色やテクスチャ)の設定は *.obj とは別のファイル(今回の例では material.mtl)に記述されています。
    mtllib で始まる行がマテリアルファイルの指定です。
    usemtl emerald がマテリアルの選択で、ここから先は emerald を使用します。
    同様に usemtl jade では、ここから jade を使用します。
    emerald, jade などは material.mtl で宣言されたIDです。
    # cube object(マテリアルの設定)
    mtllib material.mtl
    
    g cube
    v -1 -1 -1
    v 1 -1 -1
    v -1 1 -1
    v 1 1 -1
    v -1 -1 1
    v 1 -1 1
    v -1 1 1
    v 1 1 1
    
    vn 0 0 -1
    vn -1 0 0
    vn 1 0 0
    vn 0 -1 0
    vn 0 1 0
    vn 0 0 1
    
    g face1
    usemtl emerald
    f 1//1 3//1 4//1 2//1
    g face2
    usemtl pearl
    f 1//2 5//2 7//2 3//2
    g face3
    usemtl sapphire
    f 2//3 4//3 8//3 6//3
    g face4
    usemtl jade
    f 1//4 2//4 6//4 5//4
    g face5
    usemtl ruby
    f 3//5 7//5 8//5 4//5
    g face6
    usemtl gold
    f 5//6 6//6 8//6 7//6
    
  3. material.mtl のソースコードです。
    OBJ Model File と同様に Shift-JIS でタイプして Content\ のフォルダーに格納して下さい。
    画像ファイルと *.txt はプロジェクトに組み込むと自動的にリソースに設定されますが *.mtl はプロパティからリソースに設定して下さい。
    # Created By Carrara 7.0
    
    #エメラルド
    newmtl emerald
    Ns 600
    d 1
    Ni 0.001
    illum 2
    Ka 0.0215  0.1745   0.0215
    Kd 0.07568 0.61424  0.07568
    Ks 0.633   0.727811 0.633
    
    #真珠
    newmtl pearl
    Ns 88
    d 1
    Ni 0.001
    illum 2
    Ka 0.25     0.20725  0.20725
    Kd 1.0      0.829    0.829
    Ks 0.296648 0.296648 0.296648
    
    #サファイア
    newmtl sapphire
    Ns 100
    d 1
    Ni 0.001
    illum 2
    Ka 0.05375  0.05     0.1625
    Kd 0.3275   0.375    0.925
    Ks 0.332741 0.328634 0.346435
    map_Kd star.jpg
    
    #ひすい
    newmtl jade
    Ns 100
    d 1
    Ni 0.001
    illum 2
    Ka 0.135    0.2225   0.1575
    Kd 0.54     0.89     0.63
    Ks 0.316228 0.316228 0.316228
    
    #ルビー
    newmtl ruby
    Ns 60
    d 1
    Ni 0.001
    illum 2
    Ka 0.1745   0.01175   0.01175
    Kd 0.61424  0.04136   0.04136
    Ks 0.727811 0.626959  0.626959
    
    #金  
    newmtl gold
    Ns 400
    d 1
    Ni 0.001
    illum 2
    Ka 0.24725  0.1995    0.0745
    Kd 0.75164  0.60648   0.22648
    Ks 0.628281 0.555802  0.366065
    
  4. マテリアルファイルは TEXT 形式で、全ての行がキーワードで始まります。
    今回関係するのは newmtl と Kd だけです。
  5. マテリアルを定義する構造体です。
    一応領域を定義していますが、今回関係するのは matID と Kd だけです。
    struct  Mat
    {   string  matID;  //Mat ID
        float   Ns;     //スペキュラー係数(スペキュラー量)
        float   d;      //ディゾルブ(1.0 - 透過量)
        float   Ni;
        int     illum;  //lighting Mode
        float3  Ka;     //アンビエント(ルミナンスの色) 
        float3  Kd;     //ディフューズ(ディフューズの色)
        float3  Ks;     //スペキュラー(スペキュラーの色)
        string  map_Kd; //ディフューズマップ(ディフューズの画像マップ)
    };
    
  6. mtllib material.mtl の行を検出すると Mat 構造体にマテリアルを登録します。
    SetMat(string matlib) 関数がマテリアルを MTBL に登録する関数です。
        vector<Mat>     MTBL;       // Material 構造体(MTBL)
        vector<string>  MT;         // 行(\n)で切り分け
        uint16          MT_num;     // 参照中の MT 番号
    
  7. このプログラムで面倒なのが usemtl を検出する毎にマテリアルを切り替えて頂点データを作成しなければならないことです。
    そこで m_Vertex と m_Index の領域を vector で定義して追加登録が出来るようにしています。
    AddVertex() 関数がマテリアルが切り替わったときに頂点データを追加する関数です。
        vector<VertexPosition>  m_Vertex;
        vector<uint16>          m_Index;
    
  8. OBJ ファイルの最後まで到達すると m_Vertex と m_Index を参照して CreateModel() 関数でモデルを生成します。
        CreateModel(vertexBuffer, indexBuffer, indexCount);
    
  9. プロジェクトには cube.txt に加えて material.mtl も格納して下さい。
    cube.txt はページの最初に掲載した cube.obj のモデルです。
    material.mtl はプロパティからリソースを「はい」に設定します。
    今回描画するモデルは「頂点+法線+4色」モデルなので、Shader を入れ替えて下さい。
    また Sample3DSceneRenderer.cpp の vertexDesc [] も修正します。
        static const D3D11_INPUT_ELEMENT_DESC vertexDesc [] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 }, 
        };
    
  10. Load_OBJ.h のソースコードです。
    Debug() 関数は省略しています。
    #pragma once
    
    #include <vector>
    #include "..\Common\DeviceResources.h"
    #include "..\Common\StepTimer.h"
    #include "ShaderStructures.h"
    
    using namespace std;
    using namespace Platform;
    struct float3
    {
        float   x;
        float   y;
        float   z;
    };
    struct float2
    {
        float   x;
        float   y;
    };
    
    // Material 構造体(MTBL)
    struct  Mat
    {   string  matID;  //Mat ID
        float   Ns;     //スペキュラー係数(スペキュラー量)
        float   d;      //ディゾルブ(1.0 - 透過量)
        float   Ni;
        int     illum;  //lighting Mode
        float3  Ka;     //アンビエント(ルミナンスの色) 
        float3  Kd;     //ディフューズ(ディフューズの色)
        float3  Ks;     //スペキュラー(スペキュラーの色)
        string  map_Kd; //ディフューズマップ(ディフューズの画像マップ)
    };
    
    namespace App1
    {
        class Load_OBJ
        {
        public:
            Load_OBJ(const std::shared_ptr<DX::DeviceResources>& deviceResources);
            void Load(
                ID3D11Buffer **vertexBuffer,
                ID3D11Buffer **indexBuffer,
                uint32 *indexCount);
            void SetCullMode();     // カリングの設定
    
        private:
            std::shared_ptr<DX::DeviceResources> m_deviceResources;
            ID3D11RasterizerState  *m_RasterizerState;
    
            std::string str;
            vector<string>  VT;         // 行(\n)で切り分け
            int             VT_size;    // VT の大きさ
            vector<string>  TK;         // VT[m_pt] の行をトークンで切り分け
            vector<string>  STK;        // TK[i] からトークンを切り分け
    
            vector<Mat>     MTBL;       // Material 構造体(MTBL)
            vector<string>  MT;         // 行(\n)で切り分け
            uint16          MT_num;     // 参照中の MT 番号
    
            vector<float3>  m_pos;      // 頂点座標
            vector<float3>  m_norm;     // 法線ベクトル
            vector<float2>  m_tex;      // テクスチャ座標
    
            vector<uint16> m_idxP;      // 頂点 Index の並び(角付)
            vector<uint16> m_idxT;      // テクスチャ Index の並び(角付)
            vector<uint16> m_idxN;      // 法線 Index の並び(角付)
            vector<uint16> m_idxP3;     // 頂点 Index(3P) の並び
            vector<uint16> m_idxT3;     // テクスチャ Index(3P) の並び
            vector<uint16> m_idxN3;     // 法線 Index(3P) の並び
    
            vector<VertexPosition>  m_Vertex;
            vector<uint16>          m_Index;
    
            int         m_pt;           // VT の行 Index
            bool        m_NormFlag;     // 法線設定フラグ
            string      m_group;        // Model Group
            string      Word;           // string Work Area
    
            void SetMat(string matlib);
            void AddVertex();
            void ChangeMat(string matname);
            void CreateModel(
                ID3D11Buffer **vertexBuffer,
                ID3D11Buffer **indexBuffer,
                uint32 *indexCount);
            void PX_P3(vector<uint16> PX, vector<uint16> *P3);
            void ComputeNorm(VertexPosition *Vertices, int siz);
            void Token(string st);
            void SToken(string st);
        };
    }
    
  11. Load_OBJ.cpp のソースコードです。
    掲載されていない関数は Windows10 DirectX Library を参照して下さい。
    MTBL には Index Error にならないように "white" をプリセットしています。
    今回はテクスチャは使われませんが、この次のページで利用できるように処理しています。
    f 形式で負の値が使われたときは、最後に登録された値からさかのぼります。
    // OBJ Color モデルをロード
    #include "pch.h"
    #include "..\Common\DirectXHelper.h"
    #include "Load_OBJ.h"
    #include "BasicReaderWriter.h"
    
    using namespace App1;
    using namespace DirectX;
    using namespace Windows::Foundation;
    
    Load_OBJ::Load_OBJ(const std::shared_ptr<DX::DeviceResources>& deviceResources) :
        m_deviceResources(deviceResources)
    {
        m_NormFlag = true;
        MT_num = 0;
        Mat premat =
        { "white", 100.0f, 1.0f, 0.001f, 2, f3(0.0f, 0.0f, 0.0f), f3(1.0f, 1.0f, 1.0f), f3(0.0f, 0.0f, 0.0f), "" };
        MTBL.clear();
        MTBL.push_back(premat);
    }
    
    // Load OBJ Model
    void Load_OBJ::Load(
        ID3D11Buffer **vertexBuffer,
        ID3D11Buffer **indexBuffer,
        uint32 *indexCount)
    {
        float3  wf3;
        float2  wf2;
        int     num, pt, pw;
    
        m_pos.clear();
        m_idxP.clear();
        m_idxP3.clear();
    
        BasicReaderWriter^ reader = ref new BasicReaderWriter();
        Array<byte>^ buf = reader->ReadData("cube.txt");
    
        // String を行で切り分ける(buf⇒VT)
        str = (char *)&(buf[0]);
        VT.clear();
        num = str.size();
        for (pt = 0; pt < num;)
        {   pw = pt;
            for (pt++; pt < num && str[pt] != '\n'; pt++);
            if (pt >= num)  break;
            pt++;
            Word = Word.assign(str, pw, pt - pw);
            VT.push_back(Word);
            for(; str[pt] == '\r' || str[pt] == '\n' || str[pt] == ' '; pt++);
        }
        VT_size = VT.size();
    
    //for(m_pt=0; m_pt<VT_size; m_pt++) Debug((char *)VT[m_pt].data());
        for (m_pt = 0; m_pt < VT_size; m_pt++)
        {
            Token(VT[m_pt]);
            if (TK.size() < 1 || TK[0][0]=='#')
            {
                continue;
            }
            if (TK[0] == "g")
            {
                if (TK.size()>1)    m_group = TK[1];
            }
            else    if (TK[0] == "mtllib")
            {
                SetMat(TK[1]);
            }
            else    if (TK[0] == "usemtl")
            {
                if (m_idxP.size()>0)    AddVertex();
                ChangeMat(TK[1]);
            }
            else    if (TK[0] == "v")
            {
                wf3.x = (float)atof((char *)TK[1].data());
                wf3.y = (float)atof((char *)TK[2].data());
                wf3.z = (float)atof((char *)TK[3].data());
                m_pos.push_back(wf3);
            }
            else    if (TK[0] == "vn")
            {
                wf3.x = (float)atof((char *)TK[1].data());
                wf3.y = (float)atof((char *)TK[2].data());
                wf3.z = (float)atof((char *)TK[3].data());
                m_norm.push_back(wf3);
            }
            else    if (TK[0] == "vt")
            {
                wf2.x = (float)atof((char *)TK[1].data());
                wf2.y = (float)atof((char *)TK[2].data());
                m_tex.push_back(wf2);
            }
            else    if (TK[0] == "f")
            {
                uint32  nn;
                int     norm_s = (int)m_norm.size();
                int     tex_s = (int)m_tex.size();
                int     pos_s = (int)m_pos.size();
                nn = TK.size() - 1;
                m_idxP.push_back(nn);
                m_idxT.push_back(nn);
                m_idxN.push_back(nn);
                for (uint32 j = 1; j <= nn; j++)
                {
                    SToken(TK[j]);
                    switch (STK.size())
                    {
                    case 3:
                        num = (uint16)atoi((char *)STK[2].data());
                        if (num>0)  m_idxN.push_back(num);
                        else  if (num<0)    m_idxN.push_back(norm_s+num+1);
                    case 2:
                        num = (uint16)atoi((char *)STK[1].data());
                        if (num>0)  m_idxT.push_back(num);
                        else  if (num<0)    m_idxT.push_back(tex_s+num+1);
                    case 1:
                        num = (uint16)atoi((char *)STK[0].data());
                        if (num>0)  m_idxP.push_back(num);
                        else  if (num<0)    m_idxP.push_back(pos_s+num+1);
                        break;
                    default:
                        Debug(L"f SToken Error", STK.size());
                        break;
                    }
                }
            }
            else
            {
                Debug((char *)VT[m_pt].data());
            }
        }
        if (m_idxP.size()>0)
        {   AddVertex();
        }
        CreateModel(vertexBuffer, indexBuffer, indexCount);
    }
        
    // Vertex を追加
    void Load_OBJ::AddVertex()
    {
        if (m_idxP.size() > m_idxN.size())  m_NormFlag = false;
        // 4角ポリゴン ⇒ 3角ポリゴン
        PX_P3(m_idxP, &m_idxP3);
        if (m_idxP.size() == m_idxN.size())
        {
            PX_P3(m_idxN, &m_idxN3);
        }
        // 頂点データとインデックスデータを作成
        uint32 vtx_size = m_idxP3.size();
        VertexPosition Vertex;
        uint16 bs = (uint16)m_Index.size();
    
        for(uint32 i=0; i<vtx_size; i++)
        {
            uint32  idx = m_idxP3[i]-1;
            if (idx<m_pos.size())
            {   Vertex.pos.x = m_pos[idx].x;
                Vertex.pos.y = m_pos[idx].y;
                Vertex.pos.z = m_pos[idx].z;
            }
            else    Debug(L"m_pos Index Error", idx);
            if (m_NormFlag)
            {
                idx = m_idxN3[i]-1;
                if (idx<m_norm.size())
                {   Vertex.norm.x = m_norm[idx].x;
                    Vertex.norm.y = m_norm[idx].y;
                    Vertex.norm.z = m_norm[idx].z;
                }
                else    Debug(L"m_norm Index Error", idx);
            }
            Vertex.color.x = MTBL[MT_num].Kd.x;
            Vertex.color.y = MTBL[MT_num].Kd.y;
            Vertex.color.z = MTBL[MT_num].Kd.z;
            Vertex.color.w = 1.0f;
    
            m_Vertex.push_back(Vertex);
            m_Index.push_back(bs + i);
        }
    }
    
    // マテリアルの切り替え
    void Load_OBJ::ChangeMat(string matname)
    {
        m_idxP.clear();
        m_idxP3.clear();
        m_idxN.clear();
        m_idxN3.clear();
        for(MT_num=0; MT_num<MTBL.size(); MT_num++)
        {   if (MTBL[MT_num].matID == matname)  break;
        }
        if (MT_num>=MTBL.size())    MT_num = 0;
    }
    
    // Model を作成
    void Load_OBJ::CreateModel(
        ID3D11Buffer **vertexBuffer,
        ID3D11Buffer **indexBuffer,
        uint32 *indexCount)
    {
        // 法線ベクトルの計算
        if (m_NormFlag==false)  ComputeNorm(m_Vertex.data(), m_Vertex.size());
    
        *indexCount = m_Index.size();
        // メッシュを生成
        D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
        vertexBufferData.pSysMem = m_Vertex.data();
        vertexBufferData.SysMemPitch = 0;
        vertexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC vertexBufferDesc(sizeof(VertexPosition)*(*indexCount), D3D11_BIND_VERTEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &vertexBufferDesc, &vertexBufferData, vertexBuffer));
    
        // インデックスを生成
        D3D11_SUBRESOURCE_DATA indexBufferData = {0};
        indexBufferData.pSysMem = m_Index.data();
        indexBufferData.SysMemPitch = 0;
        indexBufferData.SysMemSlicePitch = 0;
        CD3D11_BUFFER_DESC indexBufferDesc(sizeof(uint16)*(*indexCount), D3D11_BIND_INDEX_BUFFER);
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateBuffer(
                &indexBufferDesc, &indexBufferData, indexBuffer));
    
        // 領域の解放
        m_Vertex.clear();
        m_Index.clear();
    
        // カリング設定
        D3D11_RASTERIZER_DESC rasterizerDesc;
        ZeroMemory( &rasterizerDesc, sizeof( D3D11_RASTERIZER_DESC ) );
        //rasterizerDesc.CullMode = D3D11_CULL_BACK;
        rasterizerDesc.CullMode = D3D11_CULL_FRONT;
        rasterizerDesc.FillMode = D3D11_FILL_SOLID;
        rasterizerDesc.DepthClipEnable = FALSE;
        rasterizerDesc.MultisampleEnable = TRUE;
        rasterizerDesc.DepthBiasClamp = 0;
        rasterizerDesc.SlopeScaledDepthBias = 0;
        DX::ThrowIfFailed(
            m_deviceResources->GetD3DDevice()->CreateRasterizerState(
                &rasterizerDesc, &m_RasterizerState));
    }
    
    // Load material Library
    void Load_OBJ::SetMat(string matlib)
    {
        Mat     mat;        // Material 構造体
        WCHAR   wk[256];
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, matlib.data(), -1, wk, 256);
        String ^mlib = ref new Platform::String(wk);
        int     num, pt, pw;
    
        BasicReaderWriter^ reader = ref new BasicReaderWriter();
        Array<byte>^ buf = reader->ReadData(mlib);
        // ストリングを行で切り分ける(buf⇒MT)
        str = (char *)&(buf[0]);
        MT.clear();
        num = str.size();
        for (pt = 0; pt < num;)
        {   pw = pt;
            for (pt++; pt < num && str[pt] != '\n'; pt++);
            if (pt >= num)  break;
            pt++;
            Word = Word.assign(str, pw, pt - pw);
            MT.push_back(Word);
            for(; str[pt] == '\r' || str[pt] == '\n' || str[pt] == ' '; pt++);
        }
    //for(uint16 i=0; i<MT.size(); i++)   Debug((char *)MT[i].data());
    
        for(uint16 i=0; i<MT.size(); i++)
        {
            Token(MT[i]);
            if (TK.size()<1 || TK[0][0]=='#')   continue;
            if (TK[0] == "newmtl")
            {
                num = MTBL.size();
                MTBL.push_back(mat);
                MTBL[num].matID = TK[1];
            }
            else    if (MTBL.size() == 0)   continue;
            else    if (TK[0] == "Ns")
            {
                MTBL[num].Ns = (float)atof((char *)TK[1].data());
            }
            else    if (TK[0] == "d")
            {
                MTBL[num].d = (float)atof((char *)TK[1].data());
            }
            else    if (TK[0] == "Ni")
            {
                MTBL[num].Ni = (float)atof((char *)TK[1].data());
            }
            else    if (TK[0] == "illum")
            {
                MTBL[num].illum = (int)atoi((char *)TK[1].data());
            }
            else    if (TK[0] == "Ka")
            {
                MTBL[num].Ka.x = (float)atof((char *)TK[1].data());
                MTBL[num].Ka.y = (float)atof((char *)TK[2].data());
                MTBL[num].Ka.z = (float)atof((char *)TK[3].data());
            }
            else    if (TK[0] == "Kd")
            {
                MTBL[num].Kd.x = (float)atof((char *)TK[1].data());
                MTBL[num].Kd.y = (float)atof((char *)TK[2].data());
                MTBL[num].Kd.z = (float)atof((char *)TK[3].data());
            }       
            else    if (TK[0] == "Ks")
            {
                MTBL[num].Ks.x = (float)atof((char *)TK[1].data());
                MTBL[num].Ks.y = (float)atof((char *)TK[2].data());
                MTBL[num].Ks.z = (float)atof((char *)TK[3].data());
            }
            else    if (TK[0] == "map_Kd")
            {
                MTBL[num].map_Kd = TK[1];
            }
        }
    }
    
    // TK[i]⇒st をトークンで切り分けて STK に格納
    void Load_OBJ::SToken(string st)
    {
        uint32  pt, wk;
    
        STK.clear();
        for (pt=0; pt<st.length(); )
        {
            pt = st.find_first_not_of(" ,;", pt);
            if (st[pt]=='\r' || st[pt]=='\n')   return;
            if (st[pt]=='/' && st[pt+1]=='/')
            {   STK.push_back("");
                pt+= 2;
                continue;
            }
            wk = st.find_first_of(" /\r\n", pt);
            if (wk>pt)
            {   Word = Word.assign(st, pt, wk-pt);
                STK.push_back(Word);
                pt = wk;
            }
            else    pt++;
        }
    }
    

初音ミクの OBJ モデル


  1. 初音ミクの OBJ モデルをダウンロードして描画します。
    初音ミクの美麗3Dモデルを表示する から次のファイルをダウンロードして下さい。
    両方のファイル共に Shift-JIS でタイプ(Shift-JIS に変換)して下さい。
    ファイル名 説明
    miku.obj OBJ モデルファイル
    miku.mtl マテリアルファイル
  2. Content\ のフォルダーに miku.obj を miku.txt の名前で格納して下さい。
    miku.mtl はそのまま Content\ のフォルダーに格納して、プロパティからリソースに設定して下さい。
    miku.obj はポリゴンに色を設定したモデルです。
    瞳だけにテクスチャが貼り付けられているのですが、今回はテクスチャは無視するので eyeM2.bmp の格納は不要です。
  3. Load_OBJ.cpp から miku.txt をロードします。
        BasicReaderWriter^ reader = ref new BasicReaderWriter();
        Array<byte>^ buf = reader->ReadData("miku.txt");
    
  4. Sample3DSceneRenderer.cpp でモデルのサイズと位置を調整します。
        static const XMVECTORF32 eye = { 0.0f, 1.5f, 5.0f, 0.0f };
        static const XMVECTORF32 at = { 0.0f, 2.0f, 0.0f, 0.0f };
        static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };
    
  5. 全体的に少し暗いので SamplePixelShader.hlsl を修正して明るくします。
    float4 main(PixelShaderInput input) : SV_TARGET
    {
        float3 lightDirection = normalize(float3(1, -1, 0));
        float lightMagnitude = 0.8f * saturate(dot( input.norm, -lightDirection)) + 0.2f;
        return 2.0f * input.color * lightMagnitude;
    }
    
  6. これで初音ミクの OBJ モデルが描画されます。
    miku.obj にはテクスチャ座標が設定されています。
    miku.mtl を見ればわかるように、テクスチャが使われているのは瞳(eyeM2.bmp)だけです。
    そこでかなり面倒なのですが、miku.obj からテクスチャ関係の設定を削除することをお勧めします。
    ①vt の行を削除します。
    vt 0.429590 0.505924
    vt 0.432052 0.505553
    vt 0.432324 0.505924
        ・・・
    
    ②f の行からテクスチャの指定を削除します。
    【修正前】
    f 3504/769 3503/768 3508/774
    f 3504/769 3512/780 3505/770
    f 3504/769 3513/781 3512/780
        ・・・
    
    【修正後】
    f 3504 3503 3508
    f 3504 3512 3505
    f 3504 3513 3512
        ・・・
    
    ③これでファイルのサイズがかなり小さくなります。

[Previous Chapter ↑] Win10 OBJ Model

超初心者のプログラム入門(DirectX Store)