触摸心中的那一道门!!

来源:互联网 发布:雷锋源码 编辑:程序博客网 时间:2024/05/17 03:49

上了台阶, 门在前方, 可是我心中的那道门呀, 你到底在哪里呢?????

最近几天不知怎么搞得, 要不就是登不上gameres, 登上了很多帖子也打不开, 而且自己的blog竟然不能贴新的帖子, 真是郁闷呀!!!

第四章渲染3D物体

(译者语录1:连续狂写了3天, 不过所发的帖子均没有回复, 浏览的次数也有限. 是大家的水平都很高不屑于看看基础的东东呢, 还是我起的文章名字又或是我的翻译文学功底实在是太差呢, 嘻嘻, anyway, 我将会继续下去, 不过呢, 快要回家过年了, 所以老板的活, 系里的活都得赶紧, 所以翻译这个工作可能就会慢下来了 ^_^)

(译者语录2:其实我并不是游戏科班出生, 甚至不是计算机科班的, 学习编游戏全属于个人爱好而已, 所以本书中有很多专业名字完全不知如何入手, 于是乎只有直文直译出来了, 见笑了 @_@)


DirectX的核心部分是Direct3D. Direct3D包含了相当全面的函数, 过程, 属性和常量. 大部分的书和指南都是从最最最基本的东东, 比如点, 线, 三角形开始讲述Direct3D编程的, 然后再介绍如何去生成顶点缓存来画多个点和三角形. 然而当我开始Direct3D编程的时候, 我想要做的是载入并渲染一个模型, 而不是简单的去画点或是三角形. 我并不是否认这些基本东东的重要性, 毕竟一个模型本身就是由许多点和三角形所组成的. 但是我还是要从模型看是讲起. 
(译者语录:译者很同意作者的观点, 学习基本的东西最多只是让我们画几个会动的立方体而已仄, 当今3D游戏制作都是利用3DMax之类的工具直接生成3D模型, 然后再在游戏中载入这些模型而已, 所以模型的载入和渲染才是最最最重要的部分, 所以本书里面将会跳过基本3D的绘制, 而直接介绍有关模型的知识.)

本章所包含的主题有:

生成我们的3D模型类
设置一个简单的视角类和光线类
用我们的模型类来渲染一个3D模型

DirectX非常友好的提供了我们一个模型的格式, 使得我们可以直接载入模型而不需要自己去编写臃肿的代码来读取模型. X格式就是DirectX的模型格式, 各种流行的3D建模软件都提供了生成.X格式文件的输出程序插件. 本章中, 我们将编写一个模型类(Model), 目的是调用一些函数来载入一个X格式的模型并将其渲染在屏幕上面. 由于我们将要调用来生成模型类的这些函数只是比上章中2D精灵类中的函数稍微的复杂那么一点点. 所以建立显示我们模型的类所需要的工作量也只是多了那么一点点, 有很多细节部分我们将会在第5章中再去介绍.

让我们先来看看第4章中模型类Model的代码. 在构造和析构函数之后我们看到的第一个函数是载入模型函数loadModel. loadModel函数有2个参量: D3D设备对象和将要载入模型的文件名. 函数中我们要做的第一件事情就是设置默认的显示比例大小, 显示位置和旋转矢量的值, 也就是分别设置模型Model的scale(显示比例大小), position(显示位置)和rotation(旋转矢量)的x, y, z的数值. 我们调用D3DXLoadMeshFromX函数来载入我们的模型. D3DXLoadMeshFromX有8个参量, 你可以从下面的代码中看到它们. 其中比较重要的是载入模型的文件名, 储存模型原料的指针和模型原料的个数. 通常一个模型是由几个部分组成的, 不同的组分拥有不同的颜色和纹理数据. 当我们载入模型的时候materialBuffer记录每个组分所含有的原料(颜色,纹理等等)信息, materialCount返回了我们载入的模型所拥有的组分个数. 一旦模型被载入, 我们需要为储存每个组分的原料信息, 因为当渲染模型的时候我们会用到这些信息. 所以我们生成两个数组来储存我们的原料和纹理的信息. 然后我们遍历每一个原料, 并将其储存在原料数组中, 如果还有一个纹理文件同原料连结在一起, D3DXCreateTextureFromFile函数将被调用来载入纹理文件. 我们将载入的纹理储存在纹理数组里面. 当这一切都完成了的时候, 我们将释放materialBuffer, 因为我们以后都不会再需要它了.

bool Model::loadModel(LPDIRECT3DDEVICE9 device, std::string filename)
{
     HRESULT hr;
     mesh = NULL; 

     scale = D3DXVECTOR3(1.0f, 1.0f , 1.0f); // 初始化我们的显示比例大小为1
     position = D3DXVECTOR3(0.0f, 0.0f , 0.0f); // 初始化模型的显示位置为点(0,0,0)
     rotation = D3DXVECTOR3(0.0f, 0.0f , 0.0f); // 初始化模型的渲染矩阵, 因为我们还没有旋转模型, 所以设为0
     
     // 载入模型和模型的原料信息
     hr = D3DXLoadMeshFromX(filename.c_str(), //我们希望载入的模型文件名
     D3DXMESH_SYSTEMMEM, //储存模型的地方
     device, //d3d设备对象
     NULL,
     &materialBuffer, // 记录模型原料的指针
     NULL,
     &materialCount, // 拥有的原料个数
     &mesh);

     if FAILED(hr)
     {
         return false;
     }

     // 得到原料缓存的指针
     D3DXMATERIAL* modelMaterials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();
     
     //生成一个数组来存放纹理和原料
     materials = new D3DMATERIAL9[materialCount];
     textures = new LPDIRECT3DTEXTURE9[materialCount];
     
     // 对于我们的每一个原料, 将原料和纹理载入我们的数组
     for(DWORD i = 0; i < materialCount; i++)
     {
         
         // 将原料载入我们的数组
         materials[i] = modelMaterials[i].MatD3D;

         // 将纹理载入我们的数组
         hr = D3DXCreateTextureFromFile( device, modelMaterials[i].pTextureFilename, &textures[i] );
         // 如果没有纹理, 则设为NULL
         if FAILED(hr)
         {
             textures[i] = NULL;
         }
     }

     // 删除原料缓存
     if (materialBuffer)
     {
         materialBuffer->Release();
     }

     return true;
}

接下来的函数便是渲染函数render. render仅仅只需要D3D设备对象作为参数, 我们期望做的事情便是将我们的模型在缓存区中渲染出来. 然而在渲染之前, 我们需要设置我们模型的位置, 比例大小和旋转状态. 所有这些信息都储存在一个个矩阵里面, directx提供我们一些函数来生成这些个矩阵. 这里我们用到的函数是D3DXMatrixTranslation, D3DXMatrixScaling, and D3DXMatrixRotationYawPitchRoll分别用来生成平移矩阵transMatrix, 显示比例矩阵scaleMatrix和旋转矩阵rotationMatrix. 当生成这些矩阵以后我们用D3DXMatrixMultiply函数把它们乘在一起得到一个新的矩阵, 这个新生成的矩阵将包含我们模型所有的位置转换信息. 然后我们调用SetTransform函数, 以D3DTS_WORLD为第一个参数, 以我们新生成的包含所有模型位置转换信息的矩阵为第二个参数. 这个函数将会在渲染模型的世界空间里面设置我们模型的位置. 现在我们模型的位置已经设置好了, 接下来的工作就是遍历模型的所有组分, 设置么个组分相连结的原料和纹理, 并将其渲染到缓存区.

void Model::render(LPDIRECT3DDEVICE9 device)
{
     DWORD i; 
     // 设置显示比例大小矩阵scaleMatrix
     D3DXMatrixScaling(&scaleMatrix, scale.x,scale.y,scale.z);

     // 将显示位置信息储存到平移矩阵transMatrix中
     D3DXMatrixTranslation(&transMatrix, position.x, position.y, position.z);

     // 设置旋转矩阵rotationMatrix
     D3DXMatrixRotationYawPitchRoll(&rotationMatrix,rotation.x,rotation.y,rotation.z);

     // 旋转矩阵rotationMatrix乘以平移矩阵transMatrix, 结果储存再平移矩阵transMatrix中
     D3DXMatrixMultiply(&transMatrix, &rotationMatrix, &transMatrix);
     // 显示比例矩阵scaleMatrix乘以平移矩阵transMatrix, 结果储存再平移矩阵transMatrix中
     D3DXMatrixMultiply(&transMatrix, &scaleMatrix, &transMatrix);
     
     // 渲染的3D物体位置依赖于position, rotation and scaleing的数值.
     device->SetTransform(D3DTS_WORLD, &transMatrix);

     //渲染3D物体
     for(i = 0; i < materialCount; i++ )
     {
         // 设置原料 
         device->SetMaterial( &materials[i] );
         // 如果有则设置纹理
         if (textures[i]!= NULL){
             device->SetTexture( 0, textures[i] );
         }
         // 渲染组分
         mesh->DrawSubset( i );
     }
}

模型函数里面还有4个相对简单一点的函数, 设置位置, 改变位置, 设置比例大小, 设置旋转. 每一个函数都有一个参数, 一个D3DXVECTOR3型的变量, D3DXVECTOR3型变量有3个组分, x, y和z. setPosition设置模型在世界空间的位置, changePosition将以当前的位置为基础来设置模型的新位置, setScale设置模型在3个方向上面的显示比例大小, setRotation设置模型以x,y和z轴为基准轴的转动角度.

现在我们已经写好了一个模型类, 然而我们并没有准备好在屏幕上面渲染这个模型. 首先我们需要一个场景来告诉directx世界空间的哪些部分需要被渲染. 我们还需要生成一个光源, 否则我们的模型看起来将会是全黑的. 我们将在下一章中详细的介绍如何设置光线和摄像机, 但是现在我们要增加一个quickAndDrityViewSetup函数到我们的dxMgr类中去. 这个函数将会为我们建立一个简单的场景和光线以用来测试我们的模型类.

下面就是加入dxMgr类中的quickAndDrityViewSetup函数, 具体的细节我们将在下章中详细的介绍, 现在我们可以通过注释来大致了解一个我们将要设置一些什么东东.

void dxMgr::quickAndDrityViewSetup()

     D3DXMATRIX viewMatrix; // 场景矩阵
     D3DXMATRIX projectionMatrix; // 投影矩阵
     D3DXVECTOR3 position = D3DXVECTOR3(0.0f, 0.0f, 30.0f); // 我们摄像机的位置
     D3DXVECTOR3 target = D3DXVECTOR3(0.0f, 0.0f, 0.0f); // 我们摄像机所面朝的方向

     float aspect = 1.333f; // 屏幕的宽高比例
     float nearClip = 1.0f; // 最近的距离, 只有大于此距离的物体才会被渲染
     float farClip = 1000.0f; // 最远的距离, 只有小于此距离的物体才会被渲染     

     // 设置投影矩阵
     D3DXMatrixPerspectiveFovLH(&projectionMatrix, D3DX_PI / 4.0f, aspect, nearClip, farClip);
     //设置投影
     pd3dDevice->SetTransform(D3DTS_PROJECTION, &projectionMatrix);

     // 设置场景
     D3DXMatrixLookAtLH(&viewMatrix, 
     &position, // 摄像机的位置
     &target, // 摄像机所面朝的方向
     &D3DXVECTOR3(0.0f, 1.0f, 0.0f));// 设置向上的方向
     // 设置场景
     pd3dDevice->SetTransform(D3DTS_VIEW, &viewMatrix);
     
     // 打开光源
     pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

     // 生成一道光线
     D3DLIGHT9 light;
     light.Type = D3DLIGHT_POINT; // 点光源
     light.Diffuse.r = light.Diffuse.g = light.Diffuse.b = 1.0f;
     light.Specular.r = light.Specular.g = light.Specular.b = 0.0f;
     light.Ambient.r = light.Ambient.g = light.Ambient.b = 0.3f;
     light.Position = D3DXVECTOR3( 0.0f, 10.0f, 25.0f );
     light.Attenuation0 = light.Attenuation1 = light.Attenuation2 = 0.0f;
     light.Range = 60.0f;

     // 设置光线
     pd3dDevice->SetLight(0, &light );
     // 打开光线
     pd3dDevice->LightEnable(0, TRUE );

}

现在我们已经生成了一个场景并设置了一些光线, 我们可以测试一下我们的模型类了, 所以我们应该将模型类添加进GameMain类里面去.

首先我们在头文件里面增加对模型类Model的声明, 然后定义一个变量来储存我们的测试模型.

#pragma once
class dxMgr;
class dxText;
class Surface;
class Model; 
.....

private:

dxMgr* dxManager;
dxText* textManager;
Surface* testSurface;
Model* testModel;
};


接下来我们增加一些调用来生成我们的模型, 并在初始化函数init里面载入sphere.x(我们用来测试的模型)文件. 同样我们还将调用函数来快速设置我们的场景.

bool GameMain::init(HWND wndHandle)

     ...

     // 为了测试我们添加一个新的3D模型类, 
     
     testModel = new Model();
     testModel->loadModel(dxManager->getD3DDevice(),"sphere.x");
     if (!testModel)
     {
         return false;
     }
     
     dxManager->quickAndDrityViewSetup();

     return true;
}

最后我们在GameMain的渲染函数render里面调用我们的模型渲染函数, 我们增加两组调用, 每此的位置和比例大小各不相同

void GameMain::update(void)
{
     // 调用更新函数
     // 开始渲染
     dxManager->beginRender();
     // 在这里调用游戏渲染函数

     ....

     testModel->setScale(D3DXVECTOR3(1.0f, 1.0f , 1.0f));
     testModel->setPosition(D3DXVECTOR3(5.0f, 0.0f , 0.0f));
     testModel->render(dxManager->getD3DDevice());

     testModel->setScale(D3DXVECTOR3(1.0f, 2.0f , 1.0f));
     testModel->setPosition(D3DXVECTOR3(-5.0f, 0.0f , 0.0f));
     testModel->render(dxManager->getD3DDevice());
     
     // 渲染结束
     dxManager->endRender();

}

好了现在我们编译并运行我们的项目. 屏幕上面我们将会看到两个小球, 其中的一个在y方向上面被有趣的拉伸了.

第四章小结

本章里面我们生成了一个3D模型类来从x格式文件里面载入并渲染我们的3D模型. 模型类同样会载入和模型相连的原料和纹理文件. 在能看到我们载入的模型之前必需生成一个测试的场景, 因为direct3d需要知道世界空间的哪些部分将会被渲染. 在继续下面的内容之前, 如果你有工具可以生成3D模型的话, 可以试着载入你自己的模型. Milkshape 3D拥有X格式文件的导出插件并且可以在internet上面免费下载. 你同样可以试试试用版本的3D studio max(和panda directx输出插件一起使用, 这个仍然是免费的). 如果你的模型不能正常显示, 可以试着修改他们的显示比例大小, 通常模型都会比你想象的要来得大.

(又完了一章, mm答应我最迟最迟3月份给我答案, 因为我们离的实在是太远了. 女孩子大多都是希望自己的男朋友能够留在自己的身边, 能够好好的照顾自己的吧. 然而我却不能, 至少3年, 3年呀, 3年对于正处于青春年华的少女来说是多么的宝贵呀.... 找工作的时候人是会特别烦的, 在这样烦的时候还要考虑将给我的答案, 也许我自己的确是太自私了, 既然这个答案我已经等了一年有何必在乎再多等一阵子呢, 也许她的这个新年将会是自己一生中过得最不安心的一个新年吧....)

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 半夜2点3点胃疼怎么办 晚上吃多了胃疼怎么办 骨折打石膏后痒怎么办 脚脖子崴了肿了怎么办 喝酒喝的吐血了怎么办 感冒后咳嗽有痰怎么办 嗓子里老是有痰怎么办 物业把水停了怎么办 机洗衬衫缩水了怎么办 羊绒大衣洗缩水了怎么办 棉质衣服缩水了怎么办 衣服洗了变小了怎么办 毛衣洗后缩水了怎么办 鼻子又大又塌怎么办 苹果6被停用了怎么办 苹果6s手机停用怎么办 苹果4手机已停用怎么办 苹果手机5停用了怎么办 老公被骗300多万怎么办 苹果手机被抹除怎么办 钓鱼邮件点开了怎么办 幼犬吃多了拉稀怎么办 幼犬半夜醒了叫怎么办 相爱相杀的感情怎么办 冬天玩电脑手冷怎么办 被陌生人骗了钱怎么办 被网上骗了钱怎么办 20岁欠了10万怎么办 我赌博欠了10万怎么办 孩子见到生人不爱说话怎么办 18岁了个子矮小怎么办 1岁宝宝个子矮70怎么办 喋血街头2进监狱怎么办 360云盘收费了怎么办 头脑不清醒晕沉怎么办 一岁宝宝还不会走路怎么办 2岁宝宝受凉呕吐怎么办 3岁宝宝受凉呕吐怎么办 1岁宝宝受凉呕吐怎么办 3岁宝宝着凉呕吐怎么办 宝宝一进食就吐怎么办