N-body Simulation
prepare
- graphics.queueFamilyIndex
- compute.queueFamilyIndex
- 前两个的参数设置为了区别计算队列与图形队列
- loadAssets:加载粒子使用的贴图
- setupDescriptorPool
- 这里多设置了一个池: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
- vkCreateDescriptorPool
- prepareGraphics
- prepareStorageBuffers
- std::vector particleBuffer(numParticles);
- 存放的粒子
- createBuffer: 创建stagingBuffer用于存放粒子(STORAGE)
- createBuffer: 注意这个buffer的创建使用的参数(计算管线的stagingbuffer, vs 的vbo)
- createCommandBuffer
- vkCmdCopyBuffer
- graphics.queueFamilyIndex != compute.queueFamilyIndex
- vkCmdPipelineBarrier: 如果现在管线是计算管线, 将vbo数据拷贝到计算管线
- flushCommandBuffer
- destroy
- Binding description
- 将绑定初始的粒子顶点所需描述符
- std::vector particleBuffer(numParticles);
- prepareUniformBuffers: 主要是计算管线uniform
- createBuffer
- updateComputeUniformBuffers
- updateGraphicsUniformBuffers
- setupDescriptorSetLayout
- vkCreateDescriptorSetLayout
- vkCreatePipelineLayout
- preparePipelines
- vkCreateGraphicsPipelines
- setupDescriptorSet
- vkAllocateDescriptorSets
- vkUpdateDescriptorSets
- vkCreateSemaphore
- prepareStorageBuffers
- prepareCompute
- vkGetDeviceQueue
- VkDescriptorSetLayoutBinding
- descriptorSetLayoutBinding: Particle position storage buffer
- descriptorSetLayoutBinding: Uniform buffer
- vkCreateDescriptorSetLayout
- vkCreatePipelineLayout
- vkAllocateDescriptorSets
- VkWriteDescriptorSet: 这里多了一个write描述符
- writeDescriptorSet: Particle position storage buffer
- writeDescriptorSet: Uniform buffer
- vkUpdateDescriptorSets
- vkCreateComputePipelines: particle_calculate, shader在此传入, 共两个pass
- vkCreateComputePipelines: particle_integrate
- vkCreateCommandPool
- createCommandBuffer
- vkCreateSemaphore
- vkQueueSubmit
- vkQueueWaitIdle
- buildComputeCommandBuffer
- vkBeginCommandBuffer
- graphics.queueFamilyIndex != compute.queueFamilyIndex
- vkCmdPipelineBarrier:将stagingbuffer写入的计算管线中
- vkCmdBindPipeline
- vkCmdBindDescriptorSets
- vkCmdDispatch(PARTICLE_COUNT / 256) 一维工作组
- vkCmdPipelineBarrier: 该barrier确保缓冲区写入已完成
- vkCmdBindPipeline: 第二个pass
- vkCmdDispatch
- graphics.queueFamilyIndex != compute.queueFamilyIndex
- 将计算管线数据写入到VS管线中
- vkEndCommandBuffer
- graphics.queueFamilyIndex != compute.queueFamilyIndex
- vkCmdPipelineBarrier: 将vs数据Barrier到计算管线中
- vkCmdPipelineBarrier: 将计算管线数据Barrier到VS管线中
- flushCommandBuffer
- buildCommandBuffers
- loop
- vkBeginCommandBuffer
- graphics.queueFamilyIndex != compute.queueFamilyIndex
- vkCmdPipelineBarrier: CS复制到VS中
- vkCmdBeginRenderPass
- vkCmdSetViewport
- vkCmdSetScissor
- vkCmdBindPipeline
- vkCmdBindDescriptorSets
- vkCmdPushConstants
- vkCmdBindVertexBuffers
- vkCmdDraw
- drawUI
- vkCmdEndRenderPass
- graphics.queueFamilyIndex != compute.queueFamilyIndex
- vkCmdPipelineBarrier: VS复制到CS中
- vkEndCommandBuffer
- loop
render
- draw
- prepareFrame
- vkQueueSubmit: Submit graphics commands
- submitFrame
- vkQueueSubmit:Submit compute commands
- updateComputeUniformBuffers
- updateGraphicsUniformBuffers
shader
- CS1
- 常量设置
- layout (constant_id = 0) const int SHARED_DATA_SIZE = 512
- 这个值不能超过显卡最大支持的size
- 共享数据设置(共享应该只针对当前工作组)
- shared vec4 sharedData[SHARED_DATA_SIZE]
- 共享数据设置在vkCreateComputePipelines设置
- specializationData.sharedDataSize
- sharedData[gl_LocalInvocationID.x] = vec4(0.0): 共享数据直接读写即可
- 获取共享数据结果需要等待所有计算单元完成对共享数据的操作, 因此需要使用同步信号
- memoryBarrierShared();
- barrier();
for (int i = 0; i < ubo.particleCount; i += SHARED_DATA_SIZE) { if (i + gl_LocalInvocationID.x < ubo.particleCount) { sharedData[gl_LocalInvocationID.x] = particles[i + gl_LocalInvocationID.x].pos; } else { sharedData[gl_LocalInvocationID.x] = vec4(0.0); } memoryBarrierShared(); barrier(); //遍历所有工作单元获取当前工作单元共享数据 for (int j = 0; j < gl_WorkGroupSize.x; j++) { vec4 other = sharedData[j]; vec3 len = other.xyz - position.xyz; acceleration.xyz += GRAVITY * len * other.w / pow(dot(len, len) + SOFTEN, POWER); } memoryBarrierShared(); barrier(); }
- 常量设置
- CS2
- 做粒子运动计算
- VS: 与上节相同
- FS: 正常渲染即可
小结
本节主要讲compute shader 共享数据的使用. 从代码上看,共享数据只针对当前工作组. 需要后续学习中确认.
Ray tracing
prepare
- prepareStorageBuffers
- std::vector spheres: 渲染使用的球
- createBuffer: stagingBuffer
- createBuffer: SSBO(cs的buffer, vs的VBO)
- Copy to staging buffer
- createCommandBuffer
- vkCmdCopyBuffer
- flushCommandBuffer
- destroy
- std::vector planes: 渲染使用的盒子
- createBuffer: stagingBuffer
- createBuffer: SSBO
- createCommandBuffer
- vkCmdCopyBuffer
- flushCommandBuffer
- destroy
- prepareUniformBuffers
- createBuffer: CS parameter
- updateUniformBuffers
- prepareTextureTarget: 准备一个纹理, 用于存放渲染结果
- vkGetPhysicalDeviceFormatProperties
- vkCreateImage
- vkGetImageMemoryRequirements
- vkAllocateMemory
- vkBindImageMemory
- createCommandBuffer
- setImageLayout
- flushCommandBuffer
- vkCreateSampler
- vkCreateImageView
- setupDescriptorSetLayout
- vkCreateDescriptorSetLayout: fs 读取GS结构sampler
- vkCreatePipelineLayout:
- preparePipelines
- vkCreateGraphicsPipelines
- setupDescriptorPool
- 这里多设置了一个池: VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
- 和 VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
- vkCreateDescriptorPool
- setupDescriptorSet
- vkAllocateDescriptorSets
- vkUpdateDescriptorSets
- prepareCompute
- vkGetDeviceQueue
- vkCreateDescriptorSetLayout
- vkCreatePipelineLayout
- vkAllocateDescriptorSets
- vkUpdateDescriptorSets
- Create compute shader pipelines
- vkCreateComputePipelines
- vkCreateCommandPool
- vkAllocateCommandBuffers
- vkCreateFence
- buildComputeCommandBuffer
- vkBeginCommandBuffer
- vkCmdBindPipeline
- vkCmdBindDescriptorSets
- vkCmdDispatch
- vkEndCommandBuffer
- buildCommandBuffers
- loop
- vkBeginCommandBuffer
- vkCmdPipelineBarrier
- vkCmdBeginRenderPass
- vkCmdSetViewport
- vkCmdSetScissor
- vkCmdBindDescriptorSets
- vkCmdBindPipeline
- vkCmdDraw
- drawUI
- vkCmdEndRenderPass
- vkEndCommandBuffer
- loop
render
- draw
- prepareFrame
- vkQueueSubmit: Submit graphics commands
- submitFrame
- vkWaitForFences
- vkResetFences
- vkQueueSubmit:Submit compute commands
- updateUniformBuffers
shader
- CS
- 工作单元:
- local_size_x = 16, local_size_y = 16
- 结果使用texture
- uniform writeonly image2D resultImage
- 绑定渲染模型buffer
- buffer Spheres: Sphere
- buffer Planes: Planes
- main
- imageSize获取图像大小
- 获取texture uv
- vec2 uv = vec2(gl_GlobalInvocationID.xy) / dim: 当先像素坐标/宽高即: 0-1的uv值
- rayO: 相机位置
- rayD: 应该是以像素点为原点, 入射场景射线的方向
- Basic color path
- renderScene
- #define MAXLEN 1000.0
- float t = MAXLEN;
- intersect(rayO, rayD, t)
- loop spheres
- sphereIntersect(rayO, rayD, spheres[i]): 判断当前射线是否与球相交
- 获取与射线相交的模型id
- 射线与球相交的交点(tSphere, 射线上交点相对t的参数(p = o + t*n))
- loop planes: 与 loop spheres相同
- 注意: 这里存在一个问题, 有可能一条射线同时和球与平面相交, 这里的选择是平面为最终相交模型
- loop spheres
- 获取相交顶点
- 计算lv(lightPos - pos)
- loop 平面 获取模型法线并计算该点的最终颜色(使用blinn phong模型)
- loop 模型: 同样操作(感觉这个代码相当臃肿)
- 计算阴影 calcShadow
- loop spheres
- 跳过当前相交的模型
- sphereIntersect, 计算是否与射线相交
- 如果相交即为阴影(即灯光和模型之间有模型遮挡)
- color *= calcShadow(pos, lightVec, id, t)
- loop spheres
- reflectRay: 折射阶段
- 计算下个一个射线的方向
- rayO = pos: 更新相机位置为当前渲染的顶点
- renderScene
- Reflection 折射阶段
- loop RAYBOUNCES: 折射次数
- renderScene 重复Basic color path
- finalColor: 混合颜色
- 更新reflectionStrength
- loop RAYBOUNCES: 折射次数
- imageStore 存储渲染结果
- 工作单元:
- VS: 正常渲染即可
- FS: 正常渲染即可
小结
本节主要讲光追的基本方法, 感觉写的比较随意. 但是已经将光追诠释的相当清楚了. 个人理解, 这节的光追方法其实就是离线光追渲染方法. 只是这里没有去解渲染方程. 光追主要使用Computer Shader. 而VS和FS只是用来显示结果而已.