跳到主要内容

开始使用

我们的教程从建立一个空白的渲染窗口开始,这虽然是最简单的,却包含了丰富的知识点:

  • 如何利用GLFW
  • 建立绘制窗口
  • 设置窗口的背景(渲染舞台)
  • 构造场景
  • 绘制场景

利用GLFW

在我们的教程中借助了GLFW库,它可以创建并管理渲染窗口和OpenGL上下文,同时还提供了处理键盘和鼠标输入的功能。

Application是系统的核心类,它负责初始化应用程序环境、处理事件循环、并管理窗口部件。Application定义在application.h中:

#pragma once

class Application
{
public:
Application();
virtual ~Application();

// 进入事件循环,直到所有窗口都被关闭。当窗口没有被关闭时,会检查键盘、鼠标和其他输入设备的状态,并将这些消息传递给应用程序。
void exec();
}

OpenGLWidget类封装了与GLFW相关的窗口,能够接收和处理来自用户的各类事件,如鼠标点击、键盘输入、窗口重绘等。其定义在openglwidget.h中:

#pragma once

#include <cilcore.h>

#include <string>
#include <functional>

class OpenGLWidget
{
public:
// 以下函数可用于在后续的案例程序中定义鼠标和键盘发生的响应事件。
std::function<void(MouseEvent* event)> onMouseDown;
std::function<void(MouseEvent* event)> onMouseUp;
std::function<void(MouseEvent* event)> onMouseMove;
std::function<void(MouseEvent* event)> onMouseDrag;
std::function<void(WheelEvent* event)> onWheel;
std::function<void(KeyEvent* event)> onKeyPress;
std::function<void(ResizeEvent* event)> onResize;

public:
// 创建一个窗口
OpenGLWidget();
virtual ~OpenGLWidget();
// 设置窗口标题
void setTitle(const std::string& title);
std::string title();
// 设置窗口尺寸
void setSize(int w, int h);
cil::Vector2i size();
cil::Vector2i pixelSize();
// 窗口重绘,确保最新的图像显示在屏幕上。
void update();
}

建立绘制窗口

我们在主程序中,引入了上文介绍的Application和OpenGLWidget,另外我们还需要引入包含了illustrator的核心模块和各种插件的头文件。可以参考下面的代码:

#define STB_IMAGE_IMPLEMENTATION
#include "../common/stb_image.h" // 用于加载外部图片
#include "../common/cilglfw.h" // 引入Application和OpenGLWidget

#include <cilcore.h> // 渲染核心模块
#include <cilcamera.h> // 按照实际的业务需求选择包含camera插件

初始化窗口

定义一个窗口,并且为它定义窗口名称。

int main() 
{
Application app;

auto widget = std::make_shared<OpenGLWidget>();
widget->setTitle("tutorial01_blank");
// 获取窗口的大小
auto pixelSize = widget->pixelSize();

...

app.exec();

return 0;
}

设置屏幕背景

利用_simpleBackground接口来创建一个background对象,它包含的成员变量有:屏幕大小、背景颜色、DepthMask、stencilMask、坐标系。一般您只需要提供屏幕大小和背景颜色两个参数。

auto background = _simpleBackground(_ivec2(pixelSize), _clr4(0.9, 0.8, 0.7, 1.0));

构造场景

在第一章中我们介绍了场景树组织,其中渲染通道是一个与场景管理相关的概念,它具备独立的背景、摄像机和渲染节点。因此,我们将屏幕背景作为一个输入参数,来创建一个渲染通道也就是pass对象。

// 这个渲染通道中,我们只设置了背景
auto pass = _pass(nullptr, background);
// 该渲染通道构成了等待绘制的渲染场景
auto scene = _scene({ pass });

绘制场景

尽管我们目前只在场景中添加了一个背景对象,但足够我们来学习如何去绘制一个渲染场景。您需要创建一个指向渲染器对象的智能指针renderer,当窗口没有被关闭时,主程序会一直更新窗口中的渲染场景,比如当用户修改窗口大小,程序会响应Resize事件,立即再次绘制场景。

auto renderer = std::make_shared<OpenGLRenderer>(widget);

// 绘制场景,形成图像缓冲区
renderer->render(scene);
// 将图像显示在屏幕上
widget->update();

widget->onResize = [&](ResizeEvent* event) -> void {
auto pixelSize = widget->pixelSize();
*background->size = pixelSize;
renderer->render(scene);
widget->update();
};