Difference between revisions of "Tutor2 - 渲染几何体数据"

From KlayGE
Jump to: navigation, search
m (4个修订)
m (pre formatting)
Line 2: Line 2:
  
 
头文件和框架类的定义:
 
头文件和框架类的定义:
 
+
<pre>
 
  #include <KlayGE/KlayGE.hpp>
 
  #include <KlayGE/KlayGE.hpp>
 
  #include <KlayGE/App3D.hpp>
 
  #include <KlayGE/App3D.hpp>
Line 40: Line 40:
 
     KlayGE::SceneObjectHelperPtr renderableMesh_;
 
     KlayGE::SceneObjectHelperPtr renderableMesh_;
 
  };
 
  };
 
+
</pre>
 
这里与上一单元有所不同的是,我们新增了一些成员变量来进行场景对象的管理以及相机的控制。除此之外,我们还将从静态网格模型StaticMesh派生得到一个用户类,用于构建来自顶点数据或者文件的几何体对象:
 
这里与上一单元有所不同的是,我们新增了一些成员变量来进行场景对象的管理以及相机的控制。除此之外,我们还将从静态网格模型StaticMesh派生得到一个用户类,用于构建来自顶点数据或者文件的几何体对象:
 
+
<pre>
 
  class RenderPolygon : public KlayGE::StaticMesh
 
  class RenderPolygon : public KlayGE::StaticMesh
 
  {
 
  {
Line 49: Line 49:
 
     virtual void OnRenderBegin();
 
     virtual void OnRenderBegin();
 
  };
 
  };
 
+
</pre>
 
我们的主函数与上一个例子相比不会有任何变化:
 
我们的主函数与上一个例子相比不会有任何变化:
 
+
<pre>
 
  int main(int argc, char** argv)
 
  int main(int argc, char** argv)
 
  {
 
  {
Line 63: Line 63:
 
     return 0;
 
     return 0;
 
  }
 
  }
 
+
</pre>
 
在初始化场景对象时,我们依次构建一个预设的立方体对象,一个从meshml文件中读取的模型,以及一个自定义的模型:
 
在初始化场景对象时,我们依次构建一个预设的立方体对象,一个从meshml文件中读取的模型,以及一个自定义的模型:
 
+
<pre>
 
  void TutorFramework::InitObjects()
 
  void TutorFramework::InitObjects()
 
  {
 
  {
Line 170: Line 170:
 
     tbController_.Scalers(0.01f, 0.05f);
 
     tbController_.Scalers(0.01f, 0.05f);
 
  }
 
  }
 
+
</pre>
 
覆盖显示的文字,以及主框架的更新函数与上一个例子相比没有太大的区别:
 
覆盖显示的文字,以及主框架的更新函数与上一个例子相比没有太大的区别:
 
+
<pre>
 
  void TutorFramework::DoUpdateOverlay()
 
  void TutorFramework::DoUpdateOverlay()
 
  {
 
  {
Line 191: Line 191:
 
     return KlayGE::App3DFramework::URV_Need_Flush | KlayGE::App3DFramework::URV_Finished;
 
     return KlayGE::App3DFramework::URV_Need_Flush | KlayGE::App3DFramework::URV_Finished;
 
  }
 
  }
 
+
</pre>
 
我们已经注意到,上面构建的几何体都是使用自定义的RenderPolygon作为网格数据的承载者的(预设辅助对象除外),
 
我们已经注意到,上面构建的几何体都是使用自定义的RenderPolygon作为网格数据的承载者的(预设辅助对象除外),
 
在自定义类的构造函数中,我们需要设置一个RenderTechnique给网格对象,以保证它被正确地渲染:
 
在自定义类的构造函数中,我们需要设置一个RenderTechnique给网格对象,以保证它被正确地渲染:
 
+
<pre>
 
  RenderPolygon::RenderPolygon(KlayGE::RenderModelPtr model, std::wstring const& name)
 
  RenderPolygon::RenderPolygon(KlayGE::RenderModelPtr model, std::wstring const& name)
 
  :  KlayGE::StaticMesh(model, name)
 
  :  KlayGE::StaticMesh(model, name)
Line 209: Line 209:
 
     *(effect->ParameterByName("color")) = KlayGE::float4(1.0f, 0.0f, 0.0f, 1.0f);
 
     *(effect->ParameterByName("color")) = KlayGE::float4(1.0f, 0.0f, 0.0f, 1.0f);
 
  }
 
  }
 
+
</pre>
 
OnRenderBegin()将在每帧的更新过程中被自动调用,我们可以在其中不断更新着色器效果的参数,或者对网格的属性进行改变:
 
OnRenderBegin()将在每帧的更新过程中被自动调用,我们可以在其中不断更新着色器效果的参数,或者对网格的属性进行改变:
 
+
<pre>
 
  void RenderPolygon::OnRenderBegin()
 
  void RenderPolygon::OnRenderBegin()
 
  {
 
  {
Line 221: Line 221:
 
     *(GetRenderTechnique()->Effect().ParameterByName("matViewProj")) = view_proj;
 
     *(GetRenderTechnique()->Effect().ParameterByName("matViewProj")) = view_proj;
 
  }
 
  }
 +
</pre>

Revision as of 10:55, 5 July 2011

这一节当中我们将尝试使用三种不同的方法来渲染几何体:分别是使用预设的辅助对象类,从文件读取,以及在程序中使用顶点和索引数组来进行构建。

头文件和框架类的定义:

 #include <KlayGE/KlayGE.hpp>
 #include <KlayGE/App3D.hpp>
 #include <KlayGE/ResLoader.hpp>
 #include <KlayGE/Context.hpp>
 #include <KlayGE/CameraController.hpp>
 #include <KlayGE/Font.hpp>
 #include <KlayGE/RenderEngine.hpp>
 #include <KlayGE/RenderFactory.hpp>
 #include <KlayGE/RenderEffect.hpp>
 #include <KlayGE/SceneObjectHelper.hpp>
 #include <KlayGE/Mesh.hpp>
 
 #include <vector>
 #include <sstream>
 
 class TutorFramework : public KlayGE::App3DFramework
 {
 public:
     TutorFramework();
    
 protected:
     virtual void InitObjects();
     
 private:
     virtual void DoUpdateOverlay();
     virtual KlayGE::uint32_t DoUpdate(KlayGE::uint32_t pass);
     
     // 使用轨迹球控制器来控制相机,浏览场景
     KlayGE::TrackballCameraController tbController_;
     
     KlayGE::FontPtr font_;
     
     // 使用SceneObjectHelper来管理场景中的对象
     KlayGE::SceneObjectHelperPtr renderableBox_;
     KlayGE::SceneObjectHelperPtr renderableFile_;
     KlayGE::SceneObjectHelperPtr renderableMesh_;
 };

这里与上一单元有所不同的是,我们新增了一些成员变量来进行场景对象的管理以及相机的控制。除此之外,我们还将从静态网格模型StaticMesh派生得到一个用户类,用于构建来自顶点数据或者文件的几何体对象:

 class RenderPolygon : public KlayGE::StaticMesh
 {
 public:
     RenderPolygon(KlayGE::RenderModelPtr model, std::wstring const& name);
     virtual void OnRenderBegin();
 };

我们的主函数与上一个例子相比不会有任何变化:

 int main(int argc, char** argv)
 {
     KlayGE::ResLoader::Instance().AddPath("../Samples/media/Common");
     KlayGE::Context::Instance().LoadCfg("KlayGE.cfg");
 
     TutorFramework app;
     app.Create();
     app.Run();
 
     return 0;
 }

在初始化场景对象时,我们依次构建一个预设的立方体对象,一个从meshml文件中读取的模型,以及一个自定义的模型:

 void TutorFramework::InitObjects()
 {
     font_ = KlayGE::Context::Instance().RenderFactoryInstance().MakeFont("gkai00mp.kfont");
     
     // 首先构建的是预设的辅助几何体,例如三角条带组成的立方体RenderableTriBox,它的输入参数为一个由最小/最大坐标
     // 构建的Box对象,以及立方体的颜色。KlayGE中的所有可视物体(即Renderable的派生类,包括RenderableTriBox等)
     // 都必须对应有RenderTechnique,即渲染这个对象的方法——通常从FX文件中读取并获得
     KlayGE::Box boxRange(KlayGE::float3(-1.0f,-0.25f,-0.25f), KlayGE::float3(-0.5f, 0.25f, 0.25f));
     KlayGE::Color boxColor(1.0f, 0.0f, 0.0f, 1.0f);
     
     // 构建一个场景对象SceneObjectHelper并使用AddToSceneManager将其添加到场景管理器中,从而在窗口中进行渲染
     // SceneObjectHelper的传入参数除了辅助几何体的实例以外,还有一个属性参数,它的取值可以为:
     // SOA_Cullable:这个对象参与裁减。即,当它位于视锥体之外时,它会被自动排除出渲染队列之外,从而降低渲染负担。
     //               如果没有设置这一参数,那么系统将总是渲染这个对象,无论它是否在可见区域之内
     // SOA_Overlay:这个对象的渲染始终位于其它对象之前,即覆盖在默认场景之上
     // SOA_Moveable:这个对象是可以移动的。此时系统在计算它是否位于视锥体内时,会将GetModelMatrix()考虑在结果当中
     // SOA_Unvisible:这个对象是不可见的
     renderableBox_ = KlayGE::MakeSharedPtr<KlayGE::SceneObjectHelper>(
         KlayGE::MakeSharedPtr<KlayGE::RenderableTriBox>(boxRange, boxColor), KlayGE::SceneObject::SOA_Cullable);
     renderableBox_->AddToSceneManager();
     
     // 第二个要构建的几何体,我们选择从.meshml模型文件中读取(这里的teapot.meshml保存在Samples/media/Common目录下)
     // LoadModel()的第一个参数为文件名,第二个参数影响了D3D11下的数据访问策略(在OpenGL下无用处),之后的两个参数指定
     // 模型和网格数据对象实例的构建方法。注意这里的RenderPolygon就是我们之前自定义的StaticMesh派生类
     KlayGE::RenderModelPtr loadedModel = KlayGE::LoadModel("teapot.meshml", KlayGE::EAH_GPU_Read,
         KlayGE::CreateModelFactory<KlayGE::RenderModel>(), KlayGE::CreateMeshFactory<RenderPolygon>())();
     
     // 将模型加入到场景对象中
     renderableFile_ = KlayGE::MakeSharedPtr<KlayGE::SceneObjectHelper>(loadedModel, KlayGE::SceneObject::SOA_Cullable);
     renderableFile_->AddToSceneManager();
     
     // 下一步我们将要自己定义一串顶点数据,以及用户绘制这些顶点所需的图元和索引数据
     // 这里我们将试图通过8个顶点来绘制一个完整的立方体
     std::vector<KlayGE::float3> vertices;
     vertices.push_back(KlayGE::float3(0.5f,-0.25f, 0.25f));
     vertices.push_back(KlayGE::float3(1.0f,-0.25f, 0.25f));
     vertices.push_back(KlayGE::float3(1.0f,-0.25f,-0.25f));
     vertices.push_back(KlayGE::float3(0.5f,-0.25f,-0.25f));
     vertices.push_back(KlayGE::float3(0.5f, 0.25f, 0.25f));
     vertices.push_back(KlayGE::float3(1.0f, 0.25f, 0.25f));
     vertices.push_back(KlayGE::float3(1.0f, 0.25f,-0.25f));
     vertices.push_back(KlayGE::float3(0.5f, 0.25f,-0.25f));
     
     // 首先我们需要构建一个几何体模型RenderModel,它是所有StaticMesh,也就是静态网格的载体。一个RenderModel可以包含
     // 一个或多个网格对象,每个网格对象都有自己的MaterialID(材质ID),RenderTechnique等属性
     KlayGE::RenderModelPtr model = KlayGE::MakeSharedPtr<KlayGE::RenderModel>(L"model");
     
     // 显而易见,要构建一个最简单的立方体,我们至少需要两个网格对象(表达立方体侧面和顶面的图元信息)
     std::vector<KlayGE::StaticMeshPtr> meshes(2);
     
     // 第一个网格对象用于生成立方体的侧面,我们通过索引数组来反复引用指定位置的顶点,构成立方体侧面的三角条带图元
     std::vector<KlayGE::uint16_t> indices1;
     indices1.push_back(0); indices1.push_back(4); indices1.push_back(1); indices1.push_back(5);
     indices1.push_back(2); indices1.push_back(6); indices1.push_back(3); indices1.push_back(7);
     indices1.push_back(0); indices1.push_back(4);
     
     // 创建立方体侧面的网格对象
     meshes[0] = KlayGE::MakeSharedPtr<RenderPolygon>(model, L"side_mesh");
     
     // 将顶点数据的地址和大小传递给网格对象,并指定元素类型,以及D3D11下的数据访问策略
     // 这里的元素类型vertex_element由三个参数组成:
     // 第一个VEU_Position即顶点属性类型,除了顶点坐标之外,还可以为法线VEU_Normal,纹理坐标VEU_TextureCoord等等
     // 第二个参数为索引值,对于纹理坐标属性,它表示该纹理坐标对应的纹理通道
     // 第三个参数表示数据的类型,例如EF_GR32F(float2),EF_BGR32F(float3),EF_ABGR32F(float4)等
     meshes[0]->AddVertexStream(&vertices[0], static_cast<KlayGE::uint32_t>(sizeof(vertices[0]) * vertices.size()),
                           KlayGE::vertex_element(KlayGE::VEU_Position, 0, KlayGE::EF_BGR32F), KlayGE::EAH_GPU_Read);
     
     // 将索引数据的地址和大小传递给网格对象,此外还有索引数据的类型(EF_R16UI表示16位无符号整数)和数据访问策略
     meshes[0]->AddIndexStream(&indices1[0], static_cast<KlayGE::uint32_t>(sizeof(indices1[0]) * indices1.size()),
                               KlayGE::EF_R16UI, KlayGE::EAH_GPU_Read);
     
     // 设置图元的绘制方式,这里我们设置立方体侧面采取三角条带化的方法进行表达
     meshes[0]->GetRenderLayout()->TopologyType(KlayGE::RenderLayout::TT_TriangleStrip);
     
     //第二个网格对象用来构建立方体的顶面和底面,这一次我们会使用三角面的图元绘制方式,以及与之对应的顶点索引数组
     std::vector<KlayGE::uint16_t> indices2;
     indices2.push_back(0); indices2.push_back(1); indices2.push_back(2);
     indices2.push_back(0); indices2.push_back(2); indices2.push_back(3);
     indices2.push_back(7); indices2.push_back(6); indices2.push_back(5);
     indices2.push_back(7); indices2.push_back(5); indices2.push_back(4);
     
     // 构建网格对象并传递顶点数组和索引数组数据
     meshes[1] = KlayGE::MakeSharedPtr<RenderPolygon>(model, L"cap_mesh");
     meshes[1]->AddVertexStream(&vertices[0], static_cast<KlayGE::uint32_t>(sizeof(vertices[0]) * vertices.size()),
                           KlayGE::vertex_element(KlayGE::VEU_Position, 0, KlayGE::EF_BGR32F), KlayGE::EAH_GPU_Read);
     meshes[1]->AddIndexStream(&indices2[0], static_cast<KlayGE::uint32_t>(sizeof(indices2[0]) * indices2.size()),
                               KlayGE::EF_R16UI, KlayGE::EAH_GPU_Read);
     meshes[1]->GetRenderLayout()->TopologyType(KlayGE::RenderLayout::TT_TriangleList);
     
     // 将所有的网格对象传递给RenderModel几何模型
     model->AssignMeshes(meshes.begin(), meshes.end());
     
     // 将几何模型传递给场景对象,加入到场景当中
     renderableMesh_ = KlayGE::MakeSharedPtr<KlayGE::SceneObjectHelper>(model, KlayGE::SceneObject::SOA_Cullable);
     renderableMesh_->AddToSceneManager();
     
     // 为了观察场景中建立的物体,我们需要设置一个合适的观察矩阵和投影矩阵
     this->LookAt(KlayGE::float3(0, 0,-4.0f), KlayGE::float3(0, 0, 0));
     this->Proj(0.1f, 20.0f);
     
     // 我们可以将场景主相机传递给轨迹球控制器,并设置交互浏览时的放缩比例。之后就可以通过鼠标拖动来自由观察场景了
     tbController_.AttachCamera(this->ActiveCamera());
     tbController_.Scalers(0.01f, 0.05f);
 }

覆盖显示的文字,以及主框架的更新函数与上一个例子相比没有太大的区别:

 void TutorFramework::DoUpdateOverlay()
 {
     std::wostringstream stream;
     stream.precision(2);
     stream << std::fixed << this->FPS() << " FPS";
 
     font_->RenderText(0, 0, KlayGE::Color(1, 1, 0, 1), L"几何体绘制例子", 16);
     font_->RenderText(0, 18, KlayGE::Color(1, 1, 0, 1), stream.str(), 16);
 }

 KlayGE::uint32_t TutorFramework::DoUpdate(KlayGE::uint32_t pass)
 {
     KlayGE::RenderEngine& re = KlayGE::Context::Instance().RenderFactoryInstance().RenderEngineInstance();
     re.CurFrameBuffer()->Clear(KlayGE::FrameBuffer::CBM_Color | KlayGE::FrameBuffer::CBM_Depth,
                                KlayGE::Color(0.2f, 0.4f, 0.6f, 1), 1.0f, 0);
     
     return KlayGE::App3DFramework::URV_Need_Flush | KlayGE::App3DFramework::URV_Finished;
 }

我们已经注意到,上面构建的几何体都是使用自定义的RenderPolygon作为网格数据的承载者的(预设辅助对象除外), 在自定义类的构造函数中,我们需要设置一个RenderTechnique给网格对象,以保证它被正确地渲染:

 RenderPolygon::RenderPolygon(KlayGE::RenderModelPtr model, std::wstring const& name)
 :   KlayGE::StaticMesh(model, name)
 {
     // 这里我们直接取得RenderFactory对象并通过它来读取一个fxml效果文件。这里的RenderableHelper.fxml位于KlayGE
     // 目录的media/RenderFX子目录下,这个子目录已经被记录在ResLoader的资源路径当中
     KlayGE::RenderFactory& rf = KlayGE::Context::Instance().RenderFactoryInstance();
     KlayGE::RenderEffectPtr effect = rf.LoadEffect("RenderableHelper.fxml");
     
     // 设置网格所用的渲染方法为TriangleTec,这个方法记录在fxml文件的<technique>元素里
     SetRenderTechnique(effect->TechniqueByName("TriangleTec"));
     
     // 获取记录在fxml文件<parameter>元素中的color参数,并设置颜色参数值
     *(effect->ParameterByName("color")) = KlayGE::float4(1.0f, 0.0f, 0.0f, 1.0f);
 }

OnRenderBegin()将在每帧的更新过程中被自动调用,我们可以在其中不断更新着色器效果的参数,或者对网格的属性进行改变:

 void RenderPolygon::OnRenderBegin()
 {
     KlayGE::App3DFramework const& app = KlayGE::Context::Instance().AppInstance();
     
     // 通过阅读RenderableHelper.fxml中的内容可以发现,它需要获取实时的view-projection矩阵并参与顶点着色器的运算,
     // 因此这里我们通过设置matViewProj参数来实现这一要求
     KlayGE::float4x4 view_proj = app.ActiveCamera().ViewMatrix() * app.ActiveCamera().ProjMatrix();
     *(GetRenderTechnique()->Effect().ParameterByName("matViewProj")) = view_proj;
 }