Vulkan_Shader_Day04-s—推式常量(push constants)

在着色器中使用推式常量(push constants)

爲了能夠更好的進行下部分投光物的練習,我們先創建十個相同大小,位置不同的箱子,故可使用push constants來實時更新不同位置創建箱子。

簡介

當我們向着色器提供數據時,通常使用uniform緩衝區,存儲(storage)緩衝區或其他類型的描述符資源。 遺憾的是,更新此類資源可能不太方便,尤其是當我們需要提供頻繁更改的數據時。

爲此,引入了推式常量。 通過它們,我們可以通過更新描述符資源以簡化和快速的方式提供數據。 但是,我們需要適應更小的可用空間。

在GLSL着色器中訪問推送常量類似於使用統一緩衝區。

怎麼做呢?

  1. 創建一個着色器文件。

  2. 定義uniform塊:

    1.提供push_constant佈局限定符: layout( push_constant )
    2.使用uniform限定符
    3.提供塊的唯一名稱
    4.在大括號內,定義一組統一變量
    5.指定塊實例的名稱。

  3. 在void main()函數內部,使用塊實例名稱訪問uniform變量: < instance name >.< variable name >

這個怎麼運作呢?
按照與GLSL着色器中指定的uniform塊類似的方式定義和訪問推送常量,但是我們需要記住一些差異:

  1. 我們需要在塊的定義之前使用佈局(push_constant)限定符
  2. 我們必須爲塊指定實例名稱
  3. 我們每個着色器只能定義一個這樣的塊
  4. 我們通過在名稱前加上塊的實例名來訪問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));;
    ...
}

這兒將位置推送到頂點着色器中,並實時更新,編譯着色器及程序運行,可得到下圖:
在這裏插入圖片描述
下一階段,我們將繼續來研究光照中的投光物。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章