工程GIT地址:https://gitee.com/yaksue/yaksue-graphics
目标
“顶点缓冲”最初是在《图形API学习工程(5):图形管线&顶点缓冲》进行了引入,不过当时没有好的封装,这让后续新内容的添加会导致较多的代码的调整。例如:
- 当着色器所需的顶点数据的布局变化时:我需要改动现有的顶点数据与之匹配;还需要改动各个图形API版本中关于顶点数据布局的代码。
- 如果未来一个mesh将送往多个管线中,而他们的顶点数据布局不一样,则一个mesh应该对应多个顶点缓冲。
本篇的目标是重构代码来解决这些问题。另外为了能确定代码结构没问题,我将实验是否能设定多份顶点缓冲并调用多个DrawCall来实现渲染多个mesh。
明确重构方向
1. 顶点数据布局
首先要明确的是 “顶点数据布局” 的概念,我这里指的是着色器中“每个顶点所携带的属性”:
可以明白:
- 当管线中的shader确定下来之后,顶点数据布局也随之确定下来。也就是说,管线只能有一个顶点数据布局。
因此接下来的重构方向之一是,将顶点数据布局放到“管线”对象中。同时以一个统一的方式指定它们。
2. 模型数据
如果一个mesh将被送到多个管线渲染且它们的顶点数据布局不一样,则实际上需要多个顶点缓冲。
可以明白:
- “模型”和顶点缓冲应该是一对多的关系。每个布局都有其匹配的 顶点缓冲 。
因此,我决定将模型数据抽象出来,并提供接口,让其为每一个管线都生成其匹配的顶点缓冲(精确上讲,不需要为每个管线都设定,因为管线之间的顶点数据布局有可能重复,此处待后续更复杂的重构)
实现
详见代码改动:
- 蓝色部分是对顶点数据布局相关代码进行重构。
- 红色部分是抽象出“模型数据”的概念,作为测试让其实现渲染多个mesh。
其中需要注意的是,我将顶点的属性种类以以enum来指定:
enum VertexInputAttributeType
{
VIA_POSITION, //顶点位置
VIA_NORMAL, //顶点法线
VIA_COLOR, //顶点颜色
VIA_TEXCOORD, //贴图UV坐标
};
而关于每一种属性的占位是多少,目前是硬编码的:
//描述一个顶点属性
struct VertexInputAttributeDescription
{
VertexInputAttributeType Type; //种类
unsigned int Float32Cout; //这个属性有多少个Float
VertexInputAttributeDescription(VertexInputAttributeType InType)
{
Type = InType;
//在此依照种类对Float32Cout做自动的设置:
switch (Type)
{
case VIA_POSITION:
Float32Cout = 3; //XYZ三个方向
break;
case VIA_NORMAL:
Float32Cout = 3; //XYZ三个方向
break;
case VIA_COLOR:
Float32Cout = 4; //RGBA四个通道
break;
case VIA_TEXCOORD:
Float32Cout = 2; //UV两个方向
break;
default:
break;
}
}
};
这样,我便可以用它来指定顶点属性的布局了:
//作为测试的图形管线数据:
GraphicsPipelineInfo Info;
//shader文件:
Info.VertexShaderFile = "TestShader_vs";
Info.PixelShaderFile = "TestShader_ps";
//顶点属性:
Info.VertexInputAttributeTypes.push_back(VIA_POSITION); //位置
Info.VertexInputAttributeTypes.push_back(VIA_NORMAL); //法线
Info.VertexInputAttributeTypes.push_back(VIA_TEXCOORD); //贴图UV
随后各个图形API将根据这一信息来“布置”自己的管线(详见代码)。而模型也知道了该如何提供自己的顶点数据来匹配这个布局。
其实理论上,顶点属性应该是“没有具体含义”的,其含义应该被体现在shader算法中。我这里显式的赋予其“含义”仅仅是为了使用上更容易并让代码可读性更高。不过,这也和HLSL语义对应了起来。(关于HLSL语义的意义详细见官方文档,目前我还没有较深的理解)
效果
后续的发展与问题
- 模型应该根据顶点布局来生成对应的顶点缓冲,而不是管线,因为管线可能存在相同顶点布局的情况,此时则会造成顶点缓冲数据的重复
- Vulkan画面颠倒问题待修正
- 随后需要对管线相关代码进行重构,支持多个UniformBuffer/贴图,支持多个管线。