代码改变世界

Bubble引擎的基本使用

2017-05-21 18:50  EasonWhite  阅读(691)  评论(0编辑  收藏  举报

这篇博客会介绍Bubble引擎的基本使用方法,同时会简单介绍Bubble的一些基本概念。

首先我们初始化引擎

EngineConfig config;
config.window_title = "Triangle";
config.window_width = 800;
config.window_height = 600;
config.res_directory = "C:/Users/EasonWhite/Documents/Bubble/bubble/res/";

Engine::Init(config);

其中EgineConfig表示初始化引擎所需的参数,window_title表示窗口的标题,window_width和window_height分别表示窗口的宽和高,res_directory表示资源所在的根目录,调用Egine::Init(config)方法执行初始化操作。改初始化操作会创建必须的引擎子系统,例如Window,Renderer和Resources,可以通过Get方法来获取想要的子系统,例如:

// 获取引擎子系统
WindowPtr window = Engine::GetWindow();
RendererPtr renderer = Engine::GetRenderer();
ResourcesPtr resources = Engine::GetResources();

接下来就可以创建场景了。Bubble引擎使用智能指针来管理动态分配的内存,因此ScenePtr实际上是shared_ptr<ScenePtr>,在引擎中是这样定义的:

typedef shared_ptr<Scene> ScenePtr;

创建场景的代码如下:

// 创建场景
ScenePtr scene = std::make_shared<Scene>();

首先我们创建一个摄像机:

// 创建摄像机
auto camera_node = scene->CreateNode("camera");
auto camera = camera_node->CreateComponent<Camera>();
camera_node->SetPosition(0.0f, 0.0f, 3.0f);
scene->SetCamera(camera);

场景由节点Node组成,Scene是管理Node的类,Node通常组织为一颗树,通常称为场景树。每个节点只包含空间变换信息(位置,缩放,旋转)和一个组件Component数组,除此之外不包含任何其他信息。模型,灯光,摄像机等都作为组件Component通过Node::CreateComponent<T>()方法附加在节点Node上,其中T是组件的类型,必须为Component的子类。这里摄像机类Camera就是Component的子类。首先通过Scene::CreateNode()方法创建一个节点,然后通过Node::CreateComponent<Camera>()方法创建一个摄像机组件,这样便在场景中添加了一个摄像机。通过调用Node的相关方法可以设置摄像机的空间信息。通过Scene::SetCamera(camera)方法将我们创建的摄像机设为场景当前的观察摄像机。

然后是创建灯光,原理与创建摄像机相同:

// 创建灯光
NodePtr light_node = scene->CreateNode("light");
LightPtr light = light_node->CreateComponent<Light>();
light_node->SetDirection(Vector3(1.0f, 1.0f, 1.0f));
scene->SetLight(light);

这里我们创建了一个平行光,通过Node::SetDirection()方法设置灯光的方向。

下面我们来创建一个纹理,创建纹理的代码如下:

// 创建材质
MaterialPtr material = Material::CreateMaterial();
PassPtr pass = material->GetTechnique(0)->GetPass(0);
pass->SetShader(resources->LoadShader("shader/phong_texture.vert", "shader/phong_texture.frag"));
pass->SetParameter("c_model_matrix", AutoBinding::MODEL_MATRIX);
pass->SetParameter("c_view_matrix", AutoBinding::VIEW_MATRIX);
pass->SetParameter("c_projection_matrix", AutoBinding::PROJECTION_MATRIX);
pass->SetParameter("c_light_ambient", AutoBinding::LIGHT_AMBIENT);
pass->SetParameter("c_light_intensity", AutoBinding::LIGHT_INTENSITY);
pass->SetParameter("c_light_direction", AutoBinding::LIGHT_DIRECTION);
pass->SetParameter("c_camera_position", AutoBinding::CAMERA_POSITION);
pass->SetParameter("c_texture", resources->LoadTexture2D("texture/Cow.png"));

我们的纹理Material由一系列技术Technique组成,每个Technique又有一系列Pass。Bubble是基于着色器的渲染引擎,因此这里我们设置调用Pass::SetShader()设置渲染所需的着色器,这里我们使用引擎内置着色器phone_texture,这个着色器执行利用纹理贴图执行冯氏光照,在片段着色器阶段对每个像素执行着色计算,然后我们向着色器传递参数。其中AutoBinding参数是指在渲染过程中会根据被渲染物体当前的状态来实时设置的状态,其他参数例如纹理,颜色等往往需要自己手动设置具体值。

然后我们创建可渲染对象:

// 创建模型
NodePtr model_node = scene->CreateNode("model");
ModelPtr model = model_node->CreateComponent<Model>();
model->SetMesh(resources->LoadMesh("model/Cow.obj"));
model->SetMaterial(material);

Model也是Component的子类,要想将模型添加到场景中并且可以渲染,必须添加到场景中的任意节点上。Model类代表可渲染对象,他主要由两部分组成:Mesh和Material。这里我们使用Model::SetMesh()和Model::SetMaterial()分别设置代表物体几何信息的网格和代表物体表面属性的纹理。

至此场景初始化工作已经完成,下面进入渲染阶段,下面是游戏循环的代码:

while (window->Update()) {
    renderer->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    renderer->ClearDepth(1.0f);

    scene->Update(window->GetTimePassed());

    window->SwapBuffer();
}

游戏循环很简单,首先更新窗口,调用Window::Update(),这个方法会处理并分发窗口事件,当返回false时,说明窗口已退出,跳出while循环。在循环体内,首先执行Renderer::ClearColor()和Renderer::ClearDepth()方法,情况帧缓冲区,然后调用Scene::Update()方法,该方法会更新场景中的节点信息,并绘制场景中的节点。绘制完成后调用Window::SwapBuffer()交换缓冲区,使绘制结果显示在屏幕上。

最后一定要记住写一下语句:

Engine::Exit();

退出引擎,释放引擎资源。

完整代码如下(这段代码包含了摄像机的实时控制,具体请见Bubble/samples/SimpleScene/SimpleScene.cpp文件):

#include <iostream>
#include <memory>
#include <cmath>

#include "Bubble\Base\Engine.h"
#include "Bubble\Base\Window.h"
#include "Bubble\Base\Resources.h"

#include "Bubble\Math\Math.h"

#include "Bubble\Render\Camera.h"
#include "Bubble\Render\Light.h"
#include "Bubble\Render\Mesh.h"
#include "Bubble\Render\Material.h"
#include "Bubble\Render\Model.h"
#include "Bubble\Render\Renderer.h"
#include "Bubble\Render\Shader.h"

#include "Bubble\Scene\Node.h"
#include "Bubble\Scene\Scene.h"

int main() {
    using namespace Bubble;

    // 初始化引擎
    EngineConfig config;
    config.window_title = "Triangle";
    config.window_width = 800;
    config.window_height = 600;
    config.res_directory = "C:/Users/EasonWhite/Documents/Bubble/bubble/res/";

    Engine::Init(config);

    // 获取引擎子系统
    WindowPtr window = Engine::GetWindow();
    RendererPtr renderer = Engine::GetRenderer();
    ResourcesPtr resources = Engine::GetResources();

    // 创建场景
    ScenePtr scene = std::make_shared<Scene>();

    // 创建摄像机
    auto camera_node = scene->CreateNode("camera");
    auto camera = camera_node->CreateComponent<Camera>();
    camera_node->SetPosition(0.0f, 0.0f, 3.0f);
    scene->SetCamera(camera);

    // 创建灯光
    NodePtr light_node = scene->CreateNode("light");
    LightPtr light = light_node->CreateComponent<Light>();
    light_node->SetDirection(Vector3(1.0f, 1.0f, 1.0f));
    scene->SetLight(light);

    // 创建材质
    MaterialPtr material = Material::CreateMaterial();
    PassPtr pass = material->GetTechnique(0)->GetPass(0);
    pass->SetShader(resources->LoadShader("shader/phong_texture.vert", "shader/phong_texture.frag"));
    pass->SetParameter("c_model_matrix", AutoBinding::MODEL_MATRIX);
    pass->SetParameter("c_view_matrix", AutoBinding::VIEW_MATRIX);
    pass->SetParameter("c_projection_matrix", AutoBinding::PROJECTION_MATRIX);
    pass->SetParameter("c_light_ambient", AutoBinding::LIGHT_AMBIENT);
    pass->SetParameter("c_light_intensity", AutoBinding::LIGHT_INTENSITY);
    pass->SetParameter("c_light_direction", AutoBinding::LIGHT_DIRECTION);
    pass->SetParameter("c_camera_position", AutoBinding::CAMERA_POSITION);
    pass->SetParameter("c_texture", resources->LoadTexture2D("texture/Cow.png"));
    
    // 创建模型
    NodePtr model_node = scene->CreateNode("model");
    ModelPtr model = model_node->CreateComponent<Model>();
    model->SetMesh(resources->LoadMesh("model/Cow.obj"));
    model->SetMaterial(material);

    while (window->Update()) {
        renderer->ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        renderer->ClearDepth(1.0f);
        
        std::cout << "Debug::Passed time = " << window->GetTimePassed() << std::endl;

        if (window->isKeyDown(KEY_W))
            camera_node->Translate(0.0f, 0.0f, -0.1f, TranformSpace::LOCAL);
        if (window->isKeyDown(KEY_S))
            camera_node->Translate(0.0f, 0.0f, 0.1f, TranformSpace::LOCAL);
        if (window->isKeyDown(KEY_A))
            camera_node->Translate(-0.1f, 0.0f, 0.0f, TranformSpace::LOCAL);
        if (window->isKeyDown(KEY_D))
            camera_node->Translate(0.1f, 0.0f, 0.0f, TranformSpace::LOCAL);

        if (window->isMouseButtonDown(BUTTON_LEFT)) {
            int dx, dy;
            window->GetMouseMove(dx, dy);

            std::cout << "DEBUG::::Mouse move: " << dx << " " << dy << std::endl;

            camera_node->Rotate(Vector3(0.0f, 1.0f, 0.0f), AngleToRadius(dx * 0.1f), TranformSpace::PARANT);
            camera_node->Rotate(Vector3(1.0f, 0.0f, 0.0f), AngleToRadius(dy * 0.1f), TranformSpace::LOCAL);
        }

        scene->Update(window->GetTimePassed());

        window->SwapBuffer();
    }

    Engine::Exit();

    return 0;
}