在着色器中使用推式常量(push constants)
爲了能夠更好的進行下部分投光物的練習,我們先創建十個相同大小,位置不同的箱子,故可使用push constants來實時更新不同位置創建箱子。
簡介
當我們向着色器提供數據時,通常使用uniform緩衝區,存儲(storage)緩衝區或其他類型的描述符資源。 遺憾的是,更新此類資源可能不太方便,尤其是當我們需要提供頻繁更改的數據時。
爲此,引入了推式常量。 通過它們,我們可以通過更新描述符資源以簡化和快速的方式提供數據。 但是,我們需要適應更小的可用空間。
在GLSL着色器中訪問推送常量類似於使用統一緩衝區。
怎麼做呢?
-
創建一個着色器文件。
-
定義uniform塊:
1.提供push_constant佈局限定符: layout( push_constant )
2.使用uniform限定符
3.提供塊的唯一名稱
4.在大括號內,定義一組統一變量
5.指定塊實例的名稱。 -
在void main()函數內部,使用塊實例名稱訪問uniform變量: < instance name >.< variable name >
這個怎麼運作呢?
按照與GLSL着色器中指定的uniform塊類似的方式定義和訪問推送常量,但是我們需要記住一些差異:
- 我們需要在塊的定義之前使用佈局(push_constant)限定符
- 我們必須爲塊指定實例名稱
- 我們每個着色器只能定義一個這樣的塊
- 我們通過在名稱前加上塊的實例名來訪問push常量變量: < instance name >.< variable name >
注意:
推送常量對於提供頻繁更改的少量數據非常有用,例如轉換矩陣或當前時間值 - 更新推送常量塊應該比更新描述符資源(如uniform緩衝區)快得多。 我們只需要記住比描述符資源小得多的數據大小。 規範要求推送常量至少存儲128個字節的數據。 每個硬件平臺可以允許更多存儲,但可能不會大得多。
提示:推送常量可以存儲至少128個字節的數據。
一、循環創建模型
首先我們定是十個不同的位置:
glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
在創建createCommandBuffers函數中循環十次創建正方體,並將位置實時推送到頂點着色器中:
void createCommandBuffers() {
...
for (size_t j = 0; j < 10; j++)
{
glm::vec3 pos = cubePositions[j];
vkCmdPushConstants(commandBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::vec3), &pos);
vkCmdDrawIndexed(commandBuffers[i], static_cast<uint32_t>(indices.size()), 1, 0, 0, 0);
}
...
}
在運行代碼之前,我們還需要在創建管線佈局中將VkPipelineLayoutCreateInfo結構體的pPushConstantRanges和pushConstantRangeCount填充完整.
void createGraphicsPipeline() {
...
VkPushConstantRange pushConstantRange{};
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(glm::vec3);
...
pipelineLayoutInfo.pushConstantRangeCount = 1; // Optional
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; // Optional
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
...
}
接下來我們在頂點着色器中使用使用推式常量:
...
layout(push_constant) uniform PushConsts {
vec3 objPos;
} pushConsts;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition + pushConsts.objPos, 1.0);
fragPos =vec3( ubo.model * vec4(inPosition+ pushConsts.objPos, 1.0));;
...
}
這兒將位置推送到頂點着色器中,並實時更新,編譯着色器及程序運行,可得到下圖:
下一階段,我們將繼續來研究光照中的投光物。