终于要见到三角形了:)。
这一次要填写创建这个demo时,预写的那个DrawFrame()函数了,这个函数填写完,就应该能看到三角形了吧。
在每帧的渲染过程中,一般要执行三步操作:
现在开始,先在头文件中添加一个uint32_t类型的变量:m_currentFrame = 0;用来表示绘制时,从交换链中取出的当前帧的索引。
第一步:
先查询fence同步的状态,通过调用vkWaitForFences()函数进行查询。其参数:
第一个参数:m_device
第二个参数:fence的数量,这里为1
第三个参数:要查询的fence的指针,这里是&m_fences[m_currentFrame]
第四个参数:成功解除阻塞等待必须满足的条件,这里为VK_TRUE,表示查询的对象中,所有的fence都发出信号。
第五个参数:超时时间,系统可能会超出这个时间,单位为纳秒。这里设置为64位uint的最大值。
再从交换链中取下一张图像,调用vkAcquireNextImageKHR其参数:
第一个参数:逻辑设备对象m_device
第二个参数:要取图的交换链m_swapchain
第三个参数:超时限制,这里还是uint64的最大值。
第四个参数:对应的semaphore,这里应该是m_imageAvailableSemaphores[m_currentFrame]
第五个参数:这里为VK_NULL_HANDLE不能为m_fences[m_currentFrame],否则校验层提示fence在提交前要被重置。
第六个参数:对应的图像的ID,在提交指令时,会用到。需要用一个局部变量来记录一下这个ID,这里是 uint32_t imageIndex,传入引用&imageIndex。
取出图像后,要进行图像变换。这里执行旋转缩放平移等操作,最基础的就是执行MVP变换。这个具体的运算过程可放到顶点的shader中进行变换,但是要通过代码将变换的矩阵传给shader,这种变量在shader中定义为uniform类型的变量。所以在这个地方,先补一个函数,向shader传递具体的矩阵值:void UpdateUniformBuffer()参数就是获取到的这个图像的ID。
在这个UpdateUniformBuffer函数中,先定义一个UniformData的对象 ud = {};。通过对memset函数初始化为全0。
将ud.model数组的下标为0、5、10、15的元素赋值为1.0f。
将ud.proj数组的下标为0、5、10、11、15的元素分别赋值为:45.0f -135.0f -1.0f -1.0f 1.0f
将ud.view数组的下标为0、1、2、4、5、6、9、10、14、15的元素分别赋值为: -0.7f -0. 408248335f 0.577350259f 0.707106769f -0.408248335f 0.577350259 0.816496670f 0.577350259f -173.205078f 1.0f
然后将ud中的数据内容拷贝到GPU:
回到DrawFrame函数,调用UpdateUniformBuffer(imageIndex)。
第二步执行渲染指令:
在执行渲染指令之前,要确认一下,当前帧图象对应的fence是否已经有了,如果已经有了需要清理一下。如果没有就将之前生成好的对应的fence赋值给当前帧图像的fence,所以补充一下std::vector
m_currentImageFences.resize(m_swapchainImages.size(), VK_NULL_HANDLE);
那在DrawFrame函数中继续。
在执行指令之前,要先判断一下,m_currentImageFences对应的fence是否为VK_NULL_HANDLE,如果是,说明刚开始渲染,可以将m_fences中的fence赋值给m_currentImageFences中相应图像的对象。如果不为VK_NULL_HANDLE,则说明正在绘制。需等待绘制完成后,再将m_fences中的fence赋值给m_currentImageFences中相应图像的对象。赋完值之后,就要重置m_fences中对应的fence。
之后就可以提交队列了,提交队列,调用vkQueueSubmit,其参数:
第一个参数:要提交的队列,这里是m_graphicsQueue
第二个参数:是下一个参数提交信息VkSubmitInfo数组的个数
第三个参数:VkSubmitInfo数组,这里需要在vkQueueSubmit的上方补一个数据结构VkSubmitInfo submitInfo = {};
第四个参数:当前图像对应的fence,即m_fences[m_currentFrame]。
补齐第三个参数:submitInfo的数据:
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
需要补一个VkSemaphore的数组std::vector< VkSemaphore > semaphores,意思是在队列开始前需要等待的信号量。其值为:则下面两个参数的值是:
submitInfo.waitSemaphoreCount = semaphores.size();
submitInfo.pWaitSemaphores = semaphores.data();
在这里需要将颜色输出到图像,定义一个数组VkPipelineStageFlags waitStages[]取值VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT。
submitInfo.pWaitDstStageMask = waitStages;
commandBuffer的个数,现在提交一个,所以为1
submitInfo.commandBufferCount = 1;
commandBuffer的对象imageIndex对应的m_commandBuffers[imageIndex]
submitInfo.pCommandBuffers = m_commandBuffers[imageIndex];
在指令缓冲执行完成时发出信号,所以这里使用Render信号量,m_renderFinishedSemaphores[m_currentFrame]
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = m_renderFinishedSemaphores[m_currentFrame];
对于semaphores的赋值,可以将之前生成的对应的图像信号量赋值给semaphores,所以semaphores数组中对象的数目为1,且值为m_fences[m_currentFrame]
队列提交后,需要进行呈现,将绘制的内容显示出来。可调用vkQueuePresentKHR函数进行呈现。其参数:
第一个参数:要呈现的队列,之前已取得的m_presentQueue
第二个参数:VkPresentInfoKHR类型的对象指针或引用,可以定义一个局部变量VkPresentInfoKHR presentInfo = {}其属性赋值:
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = nullptr;
还使用上面写过的渲染信号量
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &m_renderFinishedSemaphores[m_currentFrame];
使用之前创建好的swapchain。
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &m_swapchain;
图像的id
presentInfo.pImageIndices = &imageIndex;
返回的结果是个数组,数目与swapchian的数目相同,且结果也与swapchain一一对应。这里不需要这个结果,所以直接设置为nullptr。
presentInfo.pResults = nullptr;
然后对m_currentFrame的值进行更新,可以让m_currentFrame++,但是一直加下去会超出m_swapchainImages.size()这个最大值。所以还要取一下模:
m_currentFrame = m_currentFrame % m_swapchainImages.size()
函数的最后,设置一下vkQueueWaitIdle,等待指令队列执行结束。
执行程序:
终于看到三角形了:)