跳到主要内容

定义渲染节点

我们可以用node对象来完整地表示一个模型,它可以包含任意多个实体,还可以包含一个Node数组。比如一个包含了很多家具、房间、甚至室外花圃的房子,也可以被当作一个模型来操作。

node对象的定义接口

Node对象需要用_node接口定义,让我们一起看看如何使用它。

  // 调用方式一
std::shared_ptr<Node> _node(const std::shared_ptr<Mesh>& mesh = nullptr,
const std::shared_ptr<std::vector<std::shared_ptr<Node>>>& children = nullptr,
const std::shared_ptr<Matrix4f>& transform = nullptr,
const std::shared_ptr<BoundingBox>& boundingBox = nullptr,
const std::string& name = "Node",
const std::shared_ptr<Material>& material = nullptr,
const std::shared_ptr<Geometry>& geometry = nullptr,
const std::shared_ptr<Query>& query = nullptr);
// 调用方式二
std::shared_ptr<Node> _node(const std::vector<std::shared_ptr<Primitive>>& primitives,
const std::shared_ptr<Matrix4f>& transform = nullptr);

// 调用方式三
std::shared_ptr<Node> _node(const std::vector<std::shared_ptr<Node>>& nodes,
const std::shared_ptr<Matrix4f>& transform = nullptr);
// 调用方式四
std::shared_ptr<Node> _node(const std::vector<std::shared_ptr<Primitive>>& primitives,
const std::vector<std::shared_ptr<Node>>& nodes,
const std::shared_ptr<Matrix4f>& transform = nullptr);
// 调用方式五:_nodes接口,用来生成一个指向children节点数组的指针
std::shared_ptr<std::vector<std::shared_ptr<Node>>> _nodes(const std::vector<std::shared_ptr<Node>>& nodes = {});

案例学习

下面我们以一个具体的例子,来掌握node对象的定义方式。这个例子中,我们只定义了一个几何geometry,构成一个渲染单元。三个不同的渲染节点node, subnode1, subnode2都使用了该渲染单元,但subnode1, subnode2中对该渲染单元的位置做了变换。另外,这三个渲染节点中分别定义了不同颜色的材质,方便我们识别不同节点的渲染几何。最后,我们将subnode1, subnode2都指定为node的子节点,然后利用node的transform矩阵对所有渲染几何同步变换。

node_organize_1 node_organize_2
    // 定义了一个材质,指定了着色器程序和深度测试状态,但没有指定颜色
auto material = _material(
_program(
vertexSource, fragmentSource
),
nullptr,
_states(
{
_depth(true)
}
)
);

auto min = vec3(-38.21760177612305, -25.4783992767334, 0);
auto max = vec3(43.67150115966797, 25.4783992767334, 40.12839889526367);
auto interval = max - min;
interval *= 1.5;

// 定义了一个物体几何
auto geometry = _teapot();

// 利用上面的材质、几何构成一个渲染单元,该渲染单元对象将被后续的各个渲染节点共同使用
auto primitive = _primitive(geometry, material);

// 定义了一个变换矩阵1
auto transform1 = _mat4();
// 向X轴正向平移interval[0]的距离
transform1->translate(vec3(interval[0], 0, 0));
// 围绕Z轴负向旋转了90度
transform1->rotate(quat(vec3(0, 0, 1), -M_PI/2 ));

// 采用_node的调用方式二,定义了一个渲染节点subnode1,具备上面定义的渲染单元对象和变换矩阵1
auto subnode1 = _node({ primitive }, transform1);

// 为渲染节点subnode1定义了一个材质,只指定了颜色
subnode1->material = _material({{"color", _clr3(0.2, 0.6, 0.2)}});

// 定义了一个变换矩阵2
auto transform2 = _mat4();
// 向X轴正向平移2 * interval[0]的距离
transform2->translate(vec3(2 * interval[0], 0, 0));
// 围绕Z轴正向旋转了90度
transform2->rotate(quat(vec3(0, 0, 1), M_PI/2 ));

// 采用_node的调用方式二,又定义了一个渲染节点subnode2,具备上面定义的渲染单元对象和变换矩阵2
auto subnode2 = _node({ primitive }, transform2);

// 为渲染节点subnode2定义了一个材质,只指定了颜色
subnode2->material = _material({{"color", _clr3(0.4, 0.2, 0.2)}});

// 定义了一个新的渲染节点node
auto node = _node();

// 指定了渲染节点node的mesh对象,包含了上面定义的渲染单元
node->mesh = _mesh({primitive});

// 为渲染节点node定义了一个材质,只指定了颜色
node->material = _material({{"color", _clr3(0.4, 0.7, 0.8)}});

// 指定了渲染节点node的子模型渲染节点,也就是上面定义的subnode1, subnode2
node->children = _nodes({subnode1, subnode2});

// 定义了一个变换矩阵
auto transform = _mat4();
// 围绕X轴正向旋转了90度
transform->rotate(quat(vec3(1, 0, 0), M_PI/2 ));

// 将上面定义的变换矩阵作用到渲染节点node上,发现该节点中所有的渲染对象(包括子节点的渲染对象)都被同步旋转了。
node->transform = transform;

auto camera = _camera();
auto pass = _pass(node, background, camera);
auto scene = _scene({ pass });

auto boundingBox = _bbox(min, min + vec3(interval[0] * 4, interval[1] * 4, interval[2] * 2));

home(camera, *boundingBox, *background->size);