关于模板测试的概念和原理就不再赘述,具体可参照LearnOpenGL CN 模板测试章节。
一、轮廓绘制原理
为物体创建轮廓的步骤如下:
- 在绘制(需要添加轮廓的)物体之前,将模板函数设置为VK_COMPARE_OP_ALWAYS,每当物体的片段被渲染时,将模板缓冲更新为1。
- 渲染物体。
- 禁用模板写入以及深度测试。
- 将每个物体缩放一点点。
- 使用一个不同的片段着色器,输出一个单独的(边框)颜色。
- 再次绘制物体,但只在它们片段的模板值不等于1时才绘制。
- 再次启用模板写入和深度测试。
因为要绘制物体轮廓那么在场景中分别画了两遍模型,第一次是原始的模型,第二遍是带有模板写入并且是带有纯色模型。最后是对原物体关闭深度测试,不写入模板测试,而对第二遍绘制的模型对其位置进行了法线方向的移动。
二、vulkan实现
2.1 管线布局
// 模型管线及模板填充
shaderStages[0] = loadShader(getShadersPath() + "model.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "model.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
rasterizationState.cullMode = VK_CULL_MODE_NONE;
depthStencilState.stencilTestEnable = VK_TRUE;
// 在物体片段被渲染时,总是将模板缓冲更新为1
depthStencilState.back.compareOp = VK_COMPARE_OP_ALWAYS;
depthStencilState.back.failOp = VK_STENCIL_OP_REPLACE;
depthStencilState.back.depthFailOp = VK_STENCIL_OP_REPLACE;
depthStencilState.back.passOp = VK_STENCIL_OP_REPLACE;
depthStencilState.back.compareMask = 0xff;
depthStencilState.back.writeMask = 0xff;
depthStencilState.back.reference = 1;
depthStencilState.front = depthStencilState.back;
vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.stencil);
// 轮廓管线
// 如果新片段的深度值不等于旧片段的深度值时(针对外扩部分)
depthStencilState.back.compareOp = VK_COMPARE_OP_NOT_EQUAL;
depthStencilState.back.failOp = VK_STENCIL_OP_KEEP;
depthStencilState.back.depthFailOp = VK_STENCIL_OP_KEEP;
depthStencilState.back.passOp = VK_STENCIL_OP_REPLACE;
depthStencilState.front = depthStencilState.back;
depthStencilState.depthTestEnable = VK_FALSE;
shaderStages[0] = loadShader(getShadersPath() + "outline.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "outline.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.outline);
此处使用了两个管线,并具体可以看出其管线深度测试的开启关闭模式,第一个管线为正常渲染模型管线(简单的光照模型管线不再赘述),第二个管线为针对轮廓处理的模型(偏移固定轮廓宽度)。
2.2 轮廓管线
轮廓管线很简单:在顶点着色器中进行偏移,片元着色器中进行纯色输出。
顶点着色器:
#version 450
layout (location = 0) in vec4 inPos;
layout (location = 2) in vec3 inNormal;
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 model;
vec4 lightPos;
float outlineWidth;
} ubo;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
// 轮廓偏移
vec4 pos = vec4(inPos.xyz + inNormal * ubo.outlineWidth, inPos.w);
gl_Position = ubo.projection * ubo.model * pos;
}
片元着色器:
#version 450
layout (location = 0) out vec4 outFragColor;
void main()
{
// 纯色输出
outFragColor = vec4(vec3(0.55,1.0,0.0), 1.0);
}
可分别进行渲染输入对比如下: