Refactoring

This commit is contained in:
mklefrancois 2021-09-07 09:42:21 +02:00
parent 3e399adf0a
commit d90ce79135
222 changed files with 9045 additions and 5734 deletions

View file

@ -8,8 +8,8 @@ This is an extension of the Vulkan ray tracing [tutorial](https://nvpro-samples.
We will implement two animation methods: only the transformation matrices, and animating the geometry itself.
## Animating the Matrices
This first example shows how we can update the matrices used for instances in the TLAS.
### Creating a Scene
@ -24,26 +24,30 @@ and the acceleration structure does not deal with this well.
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths),
nvmath::scale_mat4(nvmath::vec3f(2.f, 1.f, 2.f)));
helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths));
HelloVulkan::ObjInstance inst = helloVk.m_objInstance.back();
uint32_t wusonId = 1;
nvmath::mat4f identity{1};
for(int i = 0; i < 20; i++)
helloVk.m_objInstance.push_back(inst);
~~~~
helloVk.m_instances.push_back({identity, wusonId});
~~~~
### Animation Function
We want to have all of the Wuson models running in a circle, and we will first modify the rasterizer to handle this.
Animating the transformation matrices will be done entirely on the CPU, and we will copy the computed transformation to the GPU.
In the next example, the animation will be done on the GPU using a compute shader.
Add the declaration of the animation to the `HelloVulkan` class.
~~~~ C++
~~~~ C++
void animationInstances(float time);
~~~~
~~~~
The first part computes the transformations for all of the Wuson models, placing each one behind another.
~~~~ C++
void HelloVulkan::animationInstances(float time)
{
const int32_t nbWuson = static_cast<int32_t>(m_objInstance.size() - 1);
const int32_t nbWuson = static_cast<int32_t>(m_instances.size() - 1);
const float deltaAngle = 6.28318530718f / static_cast<float>(nbWuson);
const float wusonLength = 3.f;
const float radius = wusonLength / (2.f * sin(deltaAngle / 2.0f));
@ -52,106 +56,73 @@ void HelloVulkan::animationInstances(float time)
for(int i = 0; i < nbWuson; i++)
{
int wusonIdx = i + 1;
ObjInstance& inst = m_objInstance[wusonIdx];
inst.transform = nvmath::rotation_mat4_y(i * deltaAngle + offset)
auto& transform = m_instances[wusonIdx].transform;
transform = nvmath::rotation_mat4_y(i * deltaAngle + offset)
* nvmath::translation_mat4(radius, 0.f, 0.f);
inst.transformIT = nvmath::transpose(nvmath::invert(inst.transform));
}
~~~~
Next, we update the buffer that describes the scene, which is used by the rasterizer to set each object's position, and also by the ray tracer to compute shading normals.
~~~~ C++
// Update the buffer
VkDeviceSize bufferSize = m_objInstance.size() * sizeof(ObjInstance);
nvvk::Buffer stagingBuffer =
m_alloc.createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
// Copy data to staging buffer
auto* gInst = m_alloc.map(stagingBuffer);
memcpy(gInst, m_objInstance.data(), bufferSize);
m_alloc.unmap(stagingBuffer);
// Copy staging buffer to the Scene Description buffer
nvvk::CommandPool genCmdBuf(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = genCmdBuf.createCommandBuffer();
VkBufferCopy region{0, 0, bufferSize};
vkCmdCopyBuffer(cmdBuf, stagingBuffer.buffer, m_sceneDesc.buffer, 1, &region);
m_debug.endLabel(cmdBuf);
genCmdBuf.submitAndWait(cmdBuf);
m_alloc.destroy(stagingBuffer);
}
~~~~
**Note:**
We could have used `cmdBuf.updateBuffer<ObjInstance>(m_sceneDesc.buffer, 0, m_objInstance)` to
update the buffer, but this function only works for buffers with less than 65,536 bytes. If we had 2000 Wuson models, this
call wouldn't work.
### Loop Animation
In `main()`, just before the main loop, add a variable to hold the start time.
We will use this time in our animation function.
~~~~ C++
auto start = std::chrono::system_clock::now();
~~~~
~~~~
Inside the `while` loop, just before calling `appBase.prepareFrame()`, invoke the animation function.
~~~~ C++
std::chrono::duration<float> diff = std::chrono::system_clock::now() - start;
helloVk.animationInstances(diff.count());
~~~~
~~~~
If you run the application, the Wuson models will be running in a circle when using the rasterizer, but
they will still be at their original positions in the ray traced version. We will need to update the TLAS for this.
## Update TLAS
Since we want to update the transformation matrices in the TLAS, we need to keep some of the objects used to create it.
First, move the vector of `nvvk::RaytracingBuilder::Instance` objects from `HelloVulkan::createTopLevelAS()` to the
`HelloVulkan` class.
First, move the vector of `nvvk::RaytracingBuilder::Instance` objects from `HelloVulkan::createTopLevelAS()` to the
`HelloVulkan` class.
~~~~ C++
std::vector<nvvk::RaytracingBuilder::Instance> m_tlas;
~~~~
~~~~
Make sure to rename it to `m_tlas`, instead of `tlas`.
Make sure to rename it to `m_tlas`, instead of `tlas`.
One important point is that we need to set the TLAS build flags to allow updates, by adding the`VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR` flag.
One important point is that we need to set the TLAS build flags to allow updates, by adding the`VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR` flag.
This is absolutely needed, since otherwise the TLAS cannot be updated.
~~~~ C++
//--------------------------------------------------------------------------------------------------
//
//
void HelloVulkan::createTopLevelAS()
{
m_tlas.reserve(m_objInstance.size());
for(uint32_t i = 0; i < static_cast<uint32_t>(m_objInstance.size()); i++)
m_tlas.reserve(m_instances.size());
for(const HelloVulkan::ObjInstance& inst : m_instances)
{
nvvk::RaytracingBuilderKHR::Instance rayInst;
rayInst.transform = m_objInstance[i].transform; // Position of the instance
rayInst.instanceCustomId = i; // gl_InstanceCustomIndexEXT
rayInst.blasId = m_objInstance[i].objIndex;
rayInst.hitGroupId = 0;
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
VkAccelerationStructureInstanceKHR rayInst{};
rayInst.transform = nvvk::toTransformMatrixKHR(inst.transform); // Position of the instance
rayInst.instanceCustomIndex = inst.objIndex; // gl_InstanceCustomIndexEXT
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(inst.objIndex);
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF; // Only be hit if rayMask & instance.mask != 0
rayInst.instanceShaderBindingTableRecordOffset = 0; // We will use the same hit group for all objects
m_tlas.emplace_back(rayInst);
}
m_rtFlags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR;
m_rtBuilder.buildTlas(m_tlas, m_rtFlags);
}
~~~~
Back in `HelloVulkan::animationInstances()`, we need to copy the new computed transformation
matrices to the vector of `nvvk::RaytracingBuilder::Instance` objects.
In the `for` loop, add at the end
~~~~ C++
nvvk::RaytracingBuilder::Instance& tinst = m_tlas[wusonIdx];
tinst.transform = inst.transform;
~~~~
The last point is to call the update at the end of the function.
Back in `HelloVulkan::animationInstances()`, we need to update the TLAS by calling
`buildTlas` with the update to `true`.
~~~~ C++
m_rtBuilder.buildTlas(m_tlas, m_rtFlags, true);
@ -167,9 +138,9 @@ differences are:
* The `VkAccelerationStructureBuildGeometryInfoKHR` mode will be set to `VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR`
* We will **not** create the acceleration structure, but reuse it.
* The source and destination of `VkAccelerationStructureCreateInfoKHR` will both use the previously created acceleration structure.
* The source and destination of `VkAccelerationStructureCreateInfoKHR` will both use the previously created acceleration structure.
What is happening is the buffer containing all matrices will be updated and the `vkCmdBuildAccelerationStructuresKHR` will update the acceleration in place.
What is happening is the buffer containing all matrices will be updated and the `vkCmdBuildAccelerationStructuresKHR` will update the acceleration in place.
## BLAS Animation
@ -180,19 +151,22 @@ In the previous chapter, we updated the transformation matrices. In this one we
In this chapter, we will animate a sphere. In `main.cpp`, set up the scene like this:
~~~~ C++
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths),
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true),
nvmath::scale_mat4(nvmath::vec3f(2.f, 1.f, 2.f)));
helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths));
HelloVulkan::ObjInstance inst = helloVk.m_objInstance.back();
helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths, true));
uint32_t wusonId = 1;
nvmath::mat4f identity{1};
for(int i = 0; i < 5; i++)
helloVk.m_objInstance.push_back(inst);
helloVk.loadModel(nvh::findFile("media/scenes/sphere.obj", defaultSearchPaths));
{
helloVk.m_instances.push_back({identity, wusonId});
}
helloVk.loadModel(nvh::findFile("media/scenes/sphere.obj", defaultSearchPaths, true));
~~~~
Because we now have a new instance, we have to adjust the calculation of the number of Wuson models in `HelloVulkan::animationInstances()`.
~~~~ C++
const int32_t nbWuson = static_cast<int32_t>(m_objInstance.size() - 2);
const int32_t nbWuson = static_cast<int32_t>(m_instances.size() - 2); // All except sphere and plane
~~~~
### Compute Shader
@ -279,7 +253,7 @@ Finally, destroy the resources in `HelloVulkan::destroyResources()`:
vkDestroyDescriptorSetLayout(m_device, m_compDescSetLayout, nullptr);
~~~~
### `anim.comp`
### `anim.comp`
The compute shader will be simple. We need to add a new shader file, `anim.comp`, to the `shaders` filter in the solution.
@ -347,7 +321,8 @@ The implementation only pushes the current time and calls the compute shader (`d
~~~~ C++
void HelloVulkan::animationObject(float time)
{
ObjModel& model = m_objModel[2];
const uint32_t sphereId = 2;
ObjModel& model = m_objModel[sphereId];
updateCompDescriptors(model.vertexBuffer);
@ -378,17 +353,16 @@ In the rendering loop, **before** the call to `animationInstances`, call the obj
helloVk.animationObject(diff.count());
~~~~
**Note:** Always update the TLAS when BLAS are modified. This will make sure that the TLAS knows about the new bounding box sizes.
**Note:** At this point, the object should be animated when using the rasterizer, but should still be immobile when using the ray tracer.
**:warning: Note:** Always update the TLAS when BLAS are modified. This will make sure that the TLAS knows about the new bounding box sizes.
**:warning: Note:** At this point, the object should be animated when using the rasterizer, but should still be immobile when using the ray tracer.
## Update BLAS
In `nvvk::RaytracingBuilder` in `raytrace_vkpp.hpp`, we can add a function to update a BLAS whose vertex buffer was previously updated. This function is very similar to the one used for instances, but in this case, there is no buffer transfer to do.
~~~~ C++
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// Refit BLAS number blasIdx from updated buffer contents.
//
void nvvk::RaytracingBuilderKHR::updateBlas(uint32_t blasIdx, BlasInput& blas, VkBuildAccelerationStructureFlagsKHR flags)
@ -415,12 +389,11 @@ void nvvk::RaytracingBuilderKHR::updateBlas(uint32_t blasIdx, BlasInput& blas, V
// Allocate the scratch buffer and setting the scratch info
nvvk::Buffer scratchBuffer =
m_alloc->createBuffer(sizeInfo.buildScratchSize, VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
m_alloc->createBuffer(sizeInfo.buildScratchSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
VkBufferDeviceAddressInfo bufferInfo{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO};
bufferInfo.buffer = scratchBuffer.buffer;
buildInfos.scratchData.deviceAddress = vkGetBufferDeviceAddress(m_device, &bufferInfo);
NAME_VK(scratchBuffer.buffer);
std::vector<const VkAccelerationStructureBuildRangeInfoKHR*> pBuildOffset(blas.asBuildOffsetInfo.size());
for(size_t i = 0; i < blas.asBuildOffsetInfo.size(); i++)
@ -440,7 +413,7 @@ void nvvk::RaytracingBuilderKHR::updateBlas(uint32_t blasIdx, BlasInput& blas, V
}
~~~~
The previous function (`updateBlas`) uses geometry information stored in `m_blas`.
The previous function (`updateBlas`) uses geometry information stored in `m_blas`.
To be able to re-use this information, we need to keep the structure of `nvvk::RaytracingBuilderKHR::Blas` objects
used for its creation.
@ -450,8 +423,8 @@ Move the `nvvk::RaytracingBuilderKHR::Blas` vector from `HelloVulkan::createBott
std::vector<nvvk::RaytracingBuilderKHR::Blas> m_blas;
~~~~
As with the TLAS, the BLAS needs to allow updates. We will also enable the
`VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR` flag, which indicates that the given
As with the TLAS, the BLAS needs to allow updates. We will also enable the
`VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR` flag, which indicates that the given
acceleration structure build should prioritize build time over trace performance.
~~~~ C++
@ -474,7 +447,8 @@ void HelloVulkan::createBottomLevelAS()
Finally, we can add a line at the end of `HelloVulkan::animationObject()` to update the BLAS.
~~~~ C++
m_rtBuilder.updateBlas(2, m_blas[2], VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR);
m_rtBuilder.updateBlas(sphereId, m_blas[sphereId],
VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR);
~~~~
![](images/animation2.gif)

View file

@ -40,17 +40,6 @@
extern std::vector<std::string> defaultSearchPaths;
// Holding the camera matrices
struct CameraMatrices
{
nvmath::mat4f view;
nvmath::mat4f proj;
nvmath::mat4f viewInverse;
// #VKRay
nvmath::mat4f projInverse;
};
//--------------------------------------------------------------------------------------------------
// Keep the handle on the device
// Initialize the tool to do all our allocations: buffers, images
@ -70,16 +59,17 @@ void HelloVulkan::updateUniformBuffer(const VkCommandBuffer& cmdBuf)
{
// Prepare new UBO contents on host.
const float aspectRatio = m_size.width / static_cast<float>(m_size.height);
CameraMatrices hostUBO = {};
hostUBO.view = CameraManip.getMatrix();
hostUBO.proj = nvmath::perspectiveVK(CameraManip.getFov(), aspectRatio, 0.1f, 1000.0f);
// hostUBO.proj[1][1] *= -1; // Inverting Y for Vulkan (not needed with perspectiveVK).
hostUBO.viewInverse = nvmath::invert(hostUBO.view);
// #VKRay
hostUBO.projInverse = nvmath::invert(hostUBO.proj);
GlobalUniforms hostUBO = {};
const auto& view = CameraManip.getMatrix();
const auto& proj = nvmath::perspectiveVK(CameraManip.getFov(), aspectRatio, 0.1f, 1000.0f);
// proj[1][1] *= -1; // Inverting Y for Vulkan (not needed with perspectiveVK).
hostUBO.viewProj = proj * view;
hostUBO.viewInverse = nvmath::invert(view);
hostUBO.projInverse = nvmath::invert(proj);
// UBO on the device, and what stages access it.
VkBuffer deviceUBO = m_cameraMat.buffer;
VkBuffer deviceUBO = m_bGlobals.buffer;
auto uboUsageStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
// Ensure that the modified UBO is not visible to previous frames.
@ -95,7 +85,7 @@ void HelloVulkan::updateUniformBuffer(const VkCommandBuffer& cmdBuf)
// Schedule the host-to-device upload. (hostUBO is copied into the cmd
// buffer so it is okay to deallocate when the function returns).
vkCmdUpdateBuffer(cmdBuf, m_cameraMat.buffer, 0, sizeof(CameraMatrices), &hostUBO);
vkCmdUpdateBuffer(cmdBuf, m_bGlobals.buffer, 0, sizeof(GlobalUniforms), &hostUBO);
// Making sure the updated UBO will be visible.
VkBufferMemoryBarrier afterBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
@ -115,13 +105,14 @@ void HelloVulkan::createDescriptorSetLayout()
{
auto nbTxt = static_cast<uint32_t>(m_textures.size());
// Camera matrices (binding = 0)
m_descSetLayoutBind.addBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_RAYGEN_BIT_KHR);
// Scene description (binding = 1)
m_descSetLayoutBind.addBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
// Camera matrices
m_descSetLayoutBind.addBinding(SceneBindings::eGlobals, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_RAYGEN_BIT_KHR);
// Obj descriptions
m_descSetLayoutBind.addBinding(SceneBindings::eObjDescs, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
// Textures (binding = 3)
m_descSetLayoutBind.addBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nbTxt,
// Textures
m_descSetLayoutBind.addBinding(SceneBindings::eTextures, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nbTxt,
VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
@ -138,11 +129,11 @@ void HelloVulkan::updateDescriptorSet()
std::vector<VkWriteDescriptorSet> writes;
// Camera matrices and scene description
VkDescriptorBufferInfo dbiUnif{m_cameraMat.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, 0, &dbiUnif));
VkDescriptorBufferInfo dbiUnif{m_bGlobals.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eGlobals, &dbiUnif));
VkDescriptorBufferInfo dbiSceneDesc{m_sceneDesc.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, 1, &dbiSceneDesc));
VkDescriptorBufferInfo dbiSceneDesc{m_bObjDesc.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eObjDescs, &dbiSceneDesc));
// All texture samplers
std::vector<VkDescriptorImageInfo> diit;
@ -150,7 +141,7 @@ void HelloVulkan::updateDescriptorSet()
{
diit.emplace_back(texture.descriptor);
}
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, 2, diit.data()));
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, SceneBindings::eTextures, diit.data()));
// Writing the information
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
@ -162,7 +153,7 @@ void HelloVulkan::updateDescriptorSet()
//
void HelloVulkan::createGraphicsPipeline()
{
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ObjPushConstant)};
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstantRaster)};
// Creating the Pipeline Layout
VkPipelineLayoutCreateInfo createInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
@ -222,30 +213,35 @@ void HelloVulkan::loadModel(const std::string& filename, nvmath::mat4f transform
model.indexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_indices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | rayTracingFlags);
model.matColorBuffer = m_alloc.createBuffer(cmdBuf, loader.m_materials, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
model.matIndexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_matIndx, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
// Creates all textures found
uint32_t txtOffset = static_cast<uint32_t>(m_textures.size());
// Creates all textures found and find the offset for this model
auto txtOffset = static_cast<uint32_t>(m_textures.size());
createTextureImages(cmdBuf, loader.m_textures);
cmdBufGet.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
std::string objNb = std::to_string(m_objModel.size());
m_debug.setObjectName(model.vertexBuffer.buffer, (std::string("vertex_" + objNb).c_str()));
m_debug.setObjectName(model.indexBuffer.buffer, (std::string("index_" + objNb).c_str()));
m_debug.setObjectName(model.matColorBuffer.buffer, (std::string("mat_" + objNb).c_str()));
m_debug.setObjectName(model.matIndexBuffer.buffer, (std::string("matIdx_" + objNb).c_str()));
m_debug.setObjectName(model.vertexBuffer.buffer, (std::string("vertex_" + objNb)));
m_debug.setObjectName(model.indexBuffer.buffer, (std::string("index_" + objNb)));
m_debug.setObjectName(model.matColorBuffer.buffer, (std::string("mat_" + objNb)));
m_debug.setObjectName(model.matIndexBuffer.buffer, (std::string("matIdx_" + objNb)));
// Keeping transformation matrix of the instance
ObjInstance instance;
instance.objIndex = static_cast<uint32_t>(m_objModel.size());
instance.transform = transform;
instance.transformIT = nvmath::transpose(nvmath::invert(transform));
instance.txtOffset = txtOffset;
instance.vertices = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
instance.indices = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
instance.materials = nvvk::getBufferDeviceAddress(m_device, model.matColorBuffer.buffer);
instance.materialIndices = nvvk::getBufferDeviceAddress(m_device, model.matIndexBuffer.buffer);
instance.transform = transform;
instance.objIndex = static_cast<uint32_t>(m_objModel.size());
m_instances.push_back(instance);
// Creating information for device access
ObjDesc desc;
desc.txtOffset = txtOffset;
desc.vertexAddress = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
desc.indexAddress = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
desc.materialAddress = nvvk::getBufferDeviceAddress(m_device, model.matColorBuffer.buffer);
desc.materialIndexAddress = nvvk::getBufferDeviceAddress(m_device, model.matIndexBuffer.buffer);
// Keeping the obj host model and device description
m_objModel.emplace_back(model);
m_objInstance.emplace_back(instance);
m_objDesc.emplace_back(desc);
}
@ -255,9 +251,9 @@ void HelloVulkan::loadModel(const std::string& filename, nvmath::mat4f transform
//
void HelloVulkan::createUniformBuffer()
{
m_cameraMat = m_alloc.createBuffer(sizeof(CameraMatrices), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_debug.setObjectName(m_cameraMat.buffer, "cameraMat");
m_bGlobals = m_alloc.createBuffer(sizeof(GlobalUniforms), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_debug.setObjectName(m_bGlobals.buffer, "Globals");
}
//--------------------------------------------------------------------------------------------------
@ -266,15 +262,15 @@ void HelloVulkan::createUniformBuffer()
// - Transformation
// - Offset for texture
//
void HelloVulkan::createSceneDescriptionBuffer()
void HelloVulkan::createObjDescriptionBuffer()
{
nvvk::CommandPool cmdGen(m_device, m_graphicsQueueIndex);
auto cmdBuf = cmdGen.createCommandBuffer();
m_sceneDesc = m_alloc.createBuffer(cmdBuf, m_objInstance, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
m_bObjDesc = m_alloc.createBuffer(cmdBuf, m_objDesc, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
cmdGen.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
m_debug.setObjectName(m_sceneDesc.buffer, "sceneDesc");
m_debug.setObjectName(m_bObjDesc.buffer, "ObjDescs");
}
//--------------------------------------------------------------------------------------------------
@ -360,8 +356,8 @@ void HelloVulkan::destroyResources()
vkDestroyDescriptorPool(m_device, m_descPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_descSetLayout, nullptr);
m_alloc.destroy(m_cameraMat);
m_alloc.destroy(m_sceneDesc);
m_alloc.destroy(m_bGlobals);
m_alloc.destroy(m_bObjDesc);
for(auto& m : m_objModel)
{
@ -422,14 +418,14 @@ void HelloVulkan::rasterize(const VkCommandBuffer& cmdBuf)
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descSet, 0, nullptr);
for(int i = 0; i < m_objInstance.size(); ++i)
for(const HelloVulkan::ObjInstance& inst : m_instances)
{
auto& inst = m_objInstance[i];
auto& model = m_objModel[inst.objIndex];
m_pushConstant.instanceId = i; // Telling which instance is drawn
auto& model = m_objModel[inst.objIndex];
m_pcRaster.objIndex = inst.objIndex; // Telling which object is drawn
m_pcRaster.modelMatrix = inst.transform;
vkCmdPushConstants(cmdBuf, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(ObjPushConstant), &m_pushConstant);
sizeof(PushConstantRaster), &m_pcRaster);
vkCmdBindVertexBuffers(cmdBuf, 0, 1, &model.vertexBuffer.buffer, &offset);
vkCmdBindIndexBuffer(cmdBuf, model.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmdBuf, model.nbIndices, 1, 0, 0, 0);
@ -620,7 +616,7 @@ auto HelloVulkan::objectToVkGeometryKHR(const ObjModel& model)
// Describe buffer as array of VertexObj.
VkAccelerationStructureGeometryTrianglesDataKHR triangles{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR};
triangles.vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT; // vec3 vertex position data.
triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; // vec3 vertex position data.
triangles.vertexData.deviceAddress = vertexAddress;
triangles.vertexStride = sizeof(VertexObj);
// Describe index data (32-bit unsigned int)
@ -669,17 +665,20 @@ void HelloVulkan::createBottomLevelAS()
| VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR);
}
//--------------------------------------------------------------------------------------------------
//
//
void HelloVulkan::createTopLevelAS()
{
for(uint32_t i = 0; i < static_cast<uint32_t>(m_objInstance.size()); i++)
for(const HelloVulkan::ObjInstance& inst : m_instances)
{
VkAccelerationStructureInstanceKHR rayInst;
rayInst.transform = nvvk::toTransformMatrixKHR(m_objInstance[i].transform); // Position of the instance
rayInst.instanceCustomIndex = i; // gl_InstanceCustomIndexEXT
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(m_objInstance[i].objIndex);
VkAccelerationStructureInstanceKHR rayInst{};
rayInst.transform = nvvk::toTransformMatrixKHR(inst.transform); // Position of the instance
rayInst.instanceCustomIndex = inst.objIndex; // gl_InstanceCustomIndexEXT
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(inst.objIndex);
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF; // Only be hit if rayMask & instance.mask != 0
rayInst.instanceShaderBindingTableRecordOffset = 0; // We will use the same hit group for all objects
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF;
m_tlas.emplace_back(rayInst);
}
@ -694,9 +693,9 @@ void HelloVulkan::createRtDescriptorSet()
{
// Top-level acceleration structure, usable by both the ray generation and the closest hit (to
// shoot shadow rays)
m_rtDescSetLayoutBind.addBinding(0, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,
m_rtDescSetLayoutBind.addBinding(RtxBindings::eTlas, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR); // TLAS
m_rtDescSetLayoutBind.addBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1,
m_rtDescSetLayoutBind.addBinding(RtxBindings::eOutImage, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR); // Output image
m_rtDescPool = m_rtDescSetLayoutBind.createPool(m_device);
@ -716,8 +715,8 @@ void HelloVulkan::createRtDescriptorSet()
VkDescriptorImageInfo imageInfo{{}, m_offscreenColor.descriptor.imageView, VK_IMAGE_LAYOUT_GENERAL};
std::vector<VkWriteDescriptorSet> writes;
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 0, &descASInfo));
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 1, &imageInfo));
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eTlas, &descASInfo));
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eOutImage, &imageInfo));
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
@ -730,7 +729,7 @@ void HelloVulkan::updateRtDescriptorSet()
{
// (1) Output buffer
VkDescriptorImageInfo imageInfo{{}, m_offscreenColor.descriptor.imageView, VK_IMAGE_LAYOUT_GENERAL};
VkWriteDescriptorSet wds = m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 1, &imageInfo);
VkWriteDescriptorSet wds = m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eOutImage, &imageInfo);
vkUpdateDescriptorSets(m_device, 1, &wds, 0, nullptr);
}
@ -801,7 +800,7 @@ void HelloVulkan::createRtPipeline()
// Push constant: we want to be able to update constants used by the shaders
VkPushConstantRange pushConstant{VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR,
0, sizeof(RtPushConstant)};
0, sizeof(PushConstantRay)};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
@ -849,10 +848,10 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
{
m_debug.beginLabel(cmdBuf, "Ray trace");
// Initializing push constant values
m_rtPushConstants.clearColor = clearColor;
m_rtPushConstants.lightPosition = m_pushConstant.lightPosition;
m_rtPushConstants.lightIntensity = m_pushConstant.lightIntensity;
m_rtPushConstants.lightType = m_pushConstant.lightType;
m_pcRay.clearColor = clearColor;
m_pcRay.lightPosition = m_pcRaster.lightPosition;
m_pcRay.lightIntensity = m_pcRaster.lightIntensity;
m_pcRay.lightType = m_pcRaster.lightType;
std::vector<VkDescriptorSet> descSets{m_rtDescSet, m_descSet};
@ -861,7 +860,7 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
(uint32_t)descSets.size(), descSets.data(), 0, nullptr);
vkCmdPushConstants(cmdBuf, m_rtPipelineLayout,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR,
0, sizeof(RtPushConstant), &m_rtPushConstants);
0, sizeof(PushConstantRay), &m_pcRay);
auto& regions = m_sbtWrapper.getRegions();
@ -873,9 +872,12 @@ void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const nvmath::vec4f& c
//////////////////////////////////////////////////////////////////////////
// #VK_animation
//--------------------------------------------------------------------------------------------------
// Making the Wuson running in circle
//
void HelloVulkan::animationInstances(float time)
{
const auto nbWuson = static_cast<int32_t>(m_objInstance.size() - 2);
const auto nbWuson = static_cast<int32_t>(m_instances.size() - 2); // All except sphere and plane
const float deltaAngle = 6.28318530718f / static_cast<float>(nbWuson);
const float wusonLength = 3.f;
const float radius = wusonLength / (2.f * sin(deltaAngle / 2.0f));
@ -883,40 +885,25 @@ void HelloVulkan::animationInstances(float time)
for(int i = 0; i < nbWuson; i++)
{
int wusonIdx = i + 1;
ObjInstance& inst = m_objInstance[wusonIdx];
inst.transform = nvmath::rotation_mat4_y(i * deltaAngle + offset) * nvmath::translation_mat4(radius, 0.f, 0.f);
inst.transformIT = nvmath::transpose(nvmath::invert(inst.transform));
int wusonIdx = i + 1;
auto& transform = m_instances[wusonIdx].transform;
transform = nvmath::rotation_mat4_y(i * deltaAngle + offset) * nvmath::translation_mat4(radius, 0.f, 0.f);
VkAccelerationStructureInstanceKHR& tinst = m_tlas[wusonIdx];
tinst.transform = nvvk::toTransformMatrixKHR(inst.transform);
tinst.transform = nvvk::toTransformMatrixKHR(transform);
}
// Update the buffer
VkDeviceSize bufferSize = m_objInstance.size() * sizeof(ObjInstance);
nvvk::Buffer stagingBuffer =
m_alloc.createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
// Copy data to staging buffer
auto* gInst = m_alloc.map(stagingBuffer);
memcpy(gInst, m_objInstance.data(), bufferSize);
m_alloc.unmap(stagingBuffer);
// Copy staging buffer to the Scene Description buffer
nvvk::CommandPool genCmdBuf(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = genCmdBuf.createCommandBuffer();
VkBufferCopy region{0, 0, bufferSize};
vkCmdCopyBuffer(cmdBuf, stagingBuffer.buffer, m_sceneDesc.buffer, 1, &region);
m_debug.endLabel(cmdBuf);
genCmdBuf.submitAndWait(cmdBuf);
m_alloc.destroy(stagingBuffer);
// Updating the top level acceleration structure
m_rtBuilder.buildTlas(m_tlas, m_rtFlags, true);
}
//--------------------------------------------------------------------------------------------------
// Animating the sphere vertices using a compute shader
//
void HelloVulkan::animationObject(float time)
{
ObjModel& model = m_objModel[2];
const uint32_t sphereId = 2;
ObjModel& model = m_objModel[sphereId];
updateCompDescriptors(model.vertexBuffer);
@ -929,7 +916,8 @@ void HelloVulkan::animationObject(float time)
vkCmdDispatch(cmdBuf, model.nbVertices, 1, 1);
genCmdBuf.submitAndWait(cmdBuf);
m_rtBuilder.updateBlas(2, m_blas[2], VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR);
m_rtBuilder.updateBlas(sphereId, m_blas[sphereId],
VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR);
}
//////////////////////////////////////////////////////////////////////////

View file

@ -24,6 +24,7 @@
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/memallocator_dma_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
#include "shaders/host_device.h"
// #VKRay
#include "nvvk/raytraceKHR_vk.hpp"
@ -45,7 +46,7 @@ public:
void loadModel(const std::string& filename, nvmath::mat4f transform = nvmath::mat4f(1));
void updateDescriptorSet();
void createUniformBuffer();
void createSceneDescriptionBuffer();
void createObjDescriptionBuffer();
void createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures);
void updateUniformBuffer(const VkCommandBuffer& cmdBuf);
void onResize(int /*w*/, int /*h*/) override;
@ -63,32 +64,27 @@ public:
nvvk::Buffer matIndexBuffer; // Device buffer of array of 'Wavefront material'
};
// Instance of the OBJ
struct ObjInstance
{
nvmath::mat4f transform{1}; // Position of the instance
nvmath::mat4f transformIT{1}; // Inverse transpose
uint32_t objIndex{0}; // Reference to the `m_objModel`
uint32_t txtOffset{0}; // Offset in `m_textures`
VkDeviceAddress vertices;
VkDeviceAddress indices;
VkDeviceAddress materials;
VkDeviceAddress materialIndices;
nvmath::mat4f transform; // Matrix of the instance
uint32_t objIndex{0}; // Model index reference
};
// Information pushed at each draw call
struct ObjPushConstant
{
nvmath::vec3f lightPosition{10.f, 15.f, 8.f};
int instanceId{0}; // To retrieve the transformation matrix
float lightIntensity{100.f};
int lightType{0}; // 0: point, 1: infinite
PushConstantRaster m_pcRaster{
{1}, // Identity matrix
{10.f, 15.f, 8.f}, // light position
0, // instance Id
100.f, // light intensity
0 // light type
};
ObjPushConstant m_pushConstant;
// Array of objects and instances in the scene
std::vector<ObjModel> m_objModel;
std::vector<ObjInstance> m_objInstance;
std::vector<ObjModel> m_objModel; // Model on host
std::vector<ObjDesc> m_objDesc; // Model description for device access
std::vector<ObjInstance> m_instances; // Scene model instances
// Graphic pipeline
VkPipelineLayout m_pipelineLayout;
@ -98,8 +94,8 @@ public:
VkDescriptorSetLayout m_descSetLayout;
VkDescriptorSet m_descSet;
nvvk::Buffer m_cameraMat; // Device-Host of the camera matrices
nvvk::Buffer m_sceneDesc; // Device buffer of the OBJ instances
nvvk::Buffer m_bGlobals; // Device-Host of the camera matrices
nvvk::Buffer m_bObjDesc; // Device buffer of the OBJ descriptions
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
@ -108,7 +104,7 @@ public:
nvvk::DebugUtil m_debug; // Utility to name objects
// #Post
// #Post - Draw the rendered image on a quad using a tonemapper
void createOffscreenRender();
void createPostPipeline();
void createPostDescriptor();
@ -150,16 +146,11 @@ public:
VkPipeline m_rtPipeline;
nvvk::SBTWrapper m_sbtWrapper;
std::vector<VkAccelerationStructureInstanceKHR> m_tlas;
std::vector<VkAccelerationStructureInstanceKHR> m_tlas;
std::vector<nvvk::RaytracingBuilderKHR::BlasInput> m_blas;
struct RtPushConstant
{
nvmath::vec4f clearColor;
nvmath::vec3f lightPosition;
float lightIntensity{100.0f};
int lightType{0};
} m_rtPushConstants;
// Push constant for ray tracer
PushConstantRay m_pcRay{};
// #VK_animation
void animationInstances(float time);

View file

@ -56,12 +56,12 @@ void renderUI(HelloVulkan& helloVk)
ImGuiH::CameraWidget();
if(ImGui::CollapsingHeader("Light"))
{
ImGui::RadioButton("Point", &helloVk.m_pushConstant.lightType, 0);
ImGui::RadioButton("Point", &helloVk.m_pcRaster.lightType, 0);
ImGui::SameLine();
ImGui::RadioButton("Infinite", &helloVk.m_pushConstant.lightType, 1);
ImGui::RadioButton("Infinite", &helloVk.m_pcRaster.lightType, 1);
ImGui::SliderFloat3("Position", &helloVk.m_pushConstant.lightPosition.x, -20.f, 20.f);
ImGui::SliderFloat("Intensity", &helloVk.m_pushConstant.lightIntensity, 0.f, 150.f);
ImGui::SliderFloat3("Position", &helloVk.m_pcRaster.lightPosition.x, -20.f, 20.f);
ImGui::SliderFloat("Intensity", &helloVk.m_pcRaster.lightIntensity, 0.f, 150.f);
}
}
@ -88,6 +88,7 @@ int main(int argc, char** argv)
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window = glfwCreateWindow(SAMPLE_WIDTH, SAMPLE_HEIGHT, PROJECT_NAME, nullptr, nullptr);
// Setup camera
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
CameraManip.setLookat(nvmath::vec3f(5, 4, -4), nvmath::vec3f(0, 1, 0), nvmath::vec3f(0, 1, 0));
@ -159,16 +160,19 @@ int main(int argc, char** argv)
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true),
nvmath::scale_mat4(nvmath::vec3f(2.f, 1.f, 2.f)));
helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths, true));
HelloVulkan::ObjInstance inst = helloVk.m_objInstance.back();
uint32_t wusonId = 1;
nvmath::mat4f identity{1};
for(int i = 0; i < 5; i++)
helloVk.m_objInstance.push_back(inst);
{
helloVk.m_instances.push_back({identity, wusonId});
}
helloVk.loadModel(nvh::findFile("media/scenes/sphere.obj", defaultSearchPaths, true));
helloVk.createOffscreenRender();
helloVk.createDescriptorSetLayout();
helloVk.createGraphicsPipeline();
helloVk.createUniformBuffer();
helloVk.createSceneDescriptionBuffer();
helloVk.createObjDescriptionBuffer();
helloVk.updateDescriptorSet();
// #VKRay

View file

@ -29,60 +29,55 @@
#include "wavefront.glsl"
layout(push_constant) uniform shaderInformation
layout(push_constant) uniform _PushConstantRaster
{
vec3 lightPosition;
uint instanceId;
float lightIntensity;
int lightType;
}
pushC;
PushConstantRaster pcRaster;
};
// clang-format off
// Incoming
layout(location = 1) in vec2 fragTexCoord;
layout(location = 2) in vec3 fragNormal;
layout(location = 3) in vec3 viewDir;
layout(location = 4) in vec3 worldPos;
layout(location = 1) in vec3 i_worldPos;
layout(location = 2) in vec3 i_worldNrm;
layout(location = 3) in vec3 i_viewDir;
layout(location = 4) in vec2 i_texCoord;
// Outgoing
layout(location = 0) out vec4 outColor;
layout(location = 0) out vec4 o_color;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(binding = 1, scalar) buffer SceneDesc_ { SceneDesc i[]; } sceneDesc;
layout(binding = 2) uniform sampler2D[] textureSamplers;
layout(binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(binding = eTextures) uniform sampler2D[] textureSamplers;
// clang-format on
void main()
{
// Material of the object
SceneDesc objResource = sceneDesc.i[pushC.instanceId];
ObjDesc objResource = objDesc.i[pcRaster.objIndex];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
int matIndex = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIndex];
vec3 N = normalize(fragNormal);
vec3 N = normalize(i_worldNrm);
// Vector toward light
vec3 L;
float lightIntensity = pushC.lightIntensity;
if(pushC.lightType == 0)
float lightIntensity = pcRaster.lightIntensity;
if(pcRaster.lightType == 0)
{
vec3 lDir = pushC.lightPosition - worldPos;
vec3 lDir = pcRaster.lightPosition - i_worldPos;
float d = length(lDir);
lightIntensity = pushC.lightIntensity / (d * d);
lightIntensity = pcRaster.lightIntensity / (d * d);
L = normalize(lDir);
}
else
{
L = normalize(pushC.lightPosition - vec3(0));
L = normalize(pcRaster.lightPosition);
}
@ -90,15 +85,15 @@ void main()
vec3 diffuse = computeDiffuse(mat, L, N);
if(mat.textureId >= 0)
{
int txtOffset = sceneDesc.i[pushC.instanceId].txtOffset;
int txtOffset = objDesc.i[pcRaster.objIndex].txtOffset;
uint txtId = txtOffset + mat.textureId;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], fragTexCoord).xyz;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], i_texCoord).xyz;
diffuse *= diffuseTxt;
}
// Specular
vec3 specular = computeSpecular(mat, viewDir, L, N);
vec3 specular = computeSpecular(mat, i_viewDir, L, N);
// Result
outColor = vec4(lightIntensity * (diffuse + specular), 1);
o_color = vec4(lightIntensity * (diffuse + specular), 1);
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef COMMON_HOST_DEVICE
#define COMMON_HOST_DEVICE
#ifdef __cplusplus
#include "nvmath/nvmath.h"
// GLSL Type
using vec2 = nvmath::vec2f;
using vec3 = nvmath::vec3f;
using vec4 = nvmath::vec4f;
using mat4 = nvmath::mat4f;
using uint = unsigned int;
#endif
// clang-format off
#ifdef __cplusplus // Descriptor binding helper for C++ and GLSL
#define START_BINDING(a) enum a {
#define END_BINDING() }
#else
#define START_BINDING(a) const uint
#define END_BINDING()
#endif
START_BINDING(SceneBindings)
eGlobals = 0, // Global uniform containing camera matrices
eObjDescs = 1, // Access to the object descriptions
eTextures = 2 // Access to textures
END_BINDING();
START_BINDING(RtxBindings)
eTlas = 0, // Top-level acceleration structure
eOutImage = 1 // Ray tracer output image
END_BINDING();
// clang-format on
// Information of a obj model when referenced in a shader
struct ObjDesc
{
int txtOffset; // Texture index offset in the array of textures
uint64_t vertexAddress; // Address of the Vertex buffer
uint64_t indexAddress; // Address of the index buffer
uint64_t materialAddress; // Address of the material buffer
uint64_t materialIndexAddress; // Address of the triangle material index buffer
};
// Uniform buffer set at each frame
struct GlobalUniforms
{
mat4 viewProj; // Camera view * projection
mat4 viewInverse; // Camera inverse view matrix
mat4 projInverse; // Camera inverse projection matrix
};
// Push constant structure for the raster
struct PushConstantRaster
{
mat4 modelMatrix; // matrix of the instance
vec3 lightPosition;
uint objIndex;
float lightIntensity;
int lightType;
};
// Push constant structure for the ray tracer
struct PushConstantRay
{
vec4 clearColor;
vec3 lightPosition;
float lightIntensity;
int lightType;
};
struct Vertex // See ObjLoader, copy of VertexObj, could be compressed for device
{
vec3 pos;
vec3 nrm;
vec3 color;
vec2 texCoord;
};
struct WaveFrontMaterial // See ObjLoader, copy of MaterialObj, could be compressed for device
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
vec3 transmittance;
vec3 emission;
float shininess;
float ior; // index of refraction
float dissolve; // 1 == opaque; 0 == fully transparent
int illum; // illumination model (see http://www.fileformat.info/format/material/)
int textureId;
};
#endif

View file

@ -39,25 +39,18 @@ layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of
layout(buffer_reference, scalar) buffer Indices {ivec3 i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 1, set = 1, scalar) buffer SceneDesc_ { SceneDesc i[]; } sceneDesc;
layout(binding = 2, set = 1) uniform sampler2D textureSamplers[];
// clang-format on
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eTextures) uniform sampler2D textureSamplers[];
layout(push_constant) uniform Constants
{
vec4 clearColor;
vec3 lightPosition;
float lightIntensity;
int lightType;
}
pushC;
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
void main()
{
// Object data
SceneDesc objResource = sceneDesc.i[gl_InstanceCustomIndexEXT];
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
Indices indices = Indices(objResource.indexAddress);
@ -73,32 +66,29 @@ void main()
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
// Computing the normal at hit position
vec3 normal = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
// Transforming the normal to world space
normal = normalize(vec3(sceneDesc.i[gl_InstanceCustomIndexEXT].transfoIT * vec4(normal, 0.0)));
// Computing the coordinates of the hit position
vec3 worldPos = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;
// Transforming the position to world space
worldPos = vec3(sceneDesc.i[gl_InstanceCustomIndexEXT].transfo * vec4(worldPos, 1.0));
const vec3 pos = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;
const vec3 worldPos = vec3(gl_ObjectToWorldEXT * vec4(pos, 1.0)); // Transforming the position to world space
// Computing the normal at hit position
const vec3 nrm = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
const vec3 worldNrm = normalize(vec3(nrm * gl_WorldToObjectEXT)); // Transforming the normal to world space
// Vector toward the light
vec3 L;
float lightIntensity = pushC.lightIntensity;
float lightIntensity = pcRay.lightIntensity;
float lightDistance = 100000.0;
// Point light
if(pushC.lightType == 0)
if(pcRay.lightType == 0)
{
vec3 lDir = pushC.lightPosition - worldPos;
vec3 lDir = pcRay.lightPosition - worldPos;
lightDistance = length(lDir);
lightIntensity = pushC.lightIntensity / (lightDistance * lightDistance);
lightIntensity = pcRay.lightIntensity / (lightDistance * lightDistance);
L = normalize(lDir);
}
else // Directional light
{
L = normalize(pushC.lightPosition - vec3(0));
L = normalize(pcRay.lightPosition);
}
// Material of the object
@ -107,10 +97,10 @@ void main()
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, normal);
vec3 diffuse = computeDiffuse(mat, L, worldNrm);
if(mat.textureId >= 0)
{
uint txtId = mat.textureId + sceneDesc.i[gl_InstanceCustomIndexEXT].txtOffset;
uint txtId = mat.textureId + objDesc.i[gl_InstanceCustomIndexEXT].txtOffset;
vec2 texCoord = v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;
diffuse *= texture(textureSamplers[nonuniformEXT(txtId)], texCoord).xyz;
}
@ -119,7 +109,7 @@ void main()
float attenuation = 1;
// Tracing shadow ray only if the light is visible from the surface
if(dot(normal, L) > 0)
if(dot(worldNrm, L) > 0)
{
float tMin = 0.001;
float tMax = lightDistance;
@ -147,7 +137,7 @@ void main()
else
{
// Specular
specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, normal);
specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, worldNrm);
}
}

View file

@ -20,21 +20,21 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 1, set = 0, rgba32f) uniform image2D image;
// clang-format off
layout(location = 0) rayPayloadEXT hitPayload prd;
layout(binding = 0, set = 1) uniform CameraProperties
{
mat4 view;
mat4 proj;
mat4 viewInverse;
mat4 projInverse;
}
cam;
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 0, binding = eOutImage, rgba32f) uniform image2D image;
layout(set = 1, binding = eGlobals) uniform _GlobalUniforms { GlobalUniforms uni; };
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
void main()
{
@ -42,9 +42,9 @@ void main()
const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy);
vec2 d = inUV * 2.0 - 1.0;
vec4 origin = cam.viewInverse * vec4(0, 0, 0, 1);
vec4 target = cam.projInverse * vec4(d.x, d.y, 1, 1);
vec4 direction = cam.viewInverse * vec4(normalize(target.xyz), 0);
vec4 origin = uni.viewInverse * vec4(0, 0, 0, 1);
vec4 target = uni.projInverse * vec4(d.x, d.y, 1, 1);
vec4 direction = uni.viewInverse * vec4(normalize(target.xyz), 0);
uint rayFlags = gl_RayFlagsOpaqueEXT;
float tMin = 0.001;

View file

@ -20,16 +20,19 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(push_constant) uniform Constants
layout(push_constant) uniform _PushConstantRay
{
vec4 clearColor;
PushConstantRay pcRay;
};
void main()
{
prd.hitValue = clearColor.xyz * 0.8;
prd.hitValue = pcRay.clearColor.xyz * 0.8;
}

View file

@ -26,38 +26,26 @@
#include "wavefront.glsl"
// clang-format off
layout(binding = 1, scalar) buffer SceneDesc_ { SceneDesc i[]; } sceneDesc;
// clang-format on
layout(binding = 0) uniform UniformBufferObject
layout(binding = 0) uniform _GlobalUniforms
{
mat4 view;
mat4 proj;
mat4 viewI;
}
ubo;
GlobalUniforms uni;
};
layout(push_constant) uniform shaderInformation
layout(push_constant) uniform _PushConstantRaster
{
vec3 lightPosition;
uint instanceId;
float lightIntensity;
int lightType;
}
pushC;
PushConstantRaster pcRaster;
};
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 3) in vec2 inTexCoord;
layout(location = 0) in vec3 i_position;
layout(location = 1) in vec3 i_normal;
layout(location = 2) in vec3 i_color;
layout(location = 3) in vec2 i_texCoord;
//layout(location = 0) flat out int matIndex;
layout(location = 1) out vec2 fragTexCoord;
layout(location = 2) out vec3 fragNormal;
layout(location = 3) out vec3 viewDir;
layout(location = 4) out vec3 worldPos;
layout(location = 1) out vec3 o_worldPos;
layout(location = 2) out vec3 o_worldNrm;
layout(location = 3) out vec3 o_viewDir;
layout(location = 4) out vec2 o_texCoord;
out gl_PerVertex
{
@ -67,16 +55,12 @@ out gl_PerVertex
void main()
{
mat4 objMatrix = sceneDesc.i[pushC.instanceId].transfo;
mat4 objMatrixIT = sceneDesc.i[pushC.instanceId].transfoIT;
vec3 origin = vec3(uni.viewInverse * vec4(0, 0, 0, 1));
vec3 origin = vec3(ubo.viewI * vec4(0, 0, 0, 1));
o_worldPos = vec3(pcRaster.modelMatrix * vec4(i_position, 1.0));
o_viewDir = vec3(o_worldPos - origin);
o_texCoord = i_texCoord;
o_worldNrm = mat3(pcRaster.modelMatrix) * i_normal;
worldPos = vec3(objMatrix * vec4(inPosition, 1.0));
viewDir = vec3(worldPos - origin);
fragTexCoord = inTexCoord;
fragNormal = vec3(objMatrixIT * vec4(inNormal, 0.0));
// matIndex = inMatID;
gl_Position = ubo.proj * ubo.view * vec4(worldPos, 1.0);
gl_Position = uni.viewProj * vec4(o_worldPos, 1.0);
}

View file

@ -17,41 +17,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
struct Vertex
{
vec3 pos;
vec3 nrm;
vec3 color;
vec2 texCoord;
};
struct WaveFrontMaterial
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
vec3 transmittance;
vec3 emission;
float shininess;
float ior; // index of refraction
float dissolve; // 1 == opaque; 0 == fully transparent
int illum; // illumination model (see http://www.fileformat.info/format/material/)
int textureId;
};
struct SceneDesc
{
mat4 transfo;
mat4 transfoIT;
int objId;
int txtOffset;
uint64_t vertexAddress;
uint64_t indexAddress;
uint64_t materialAddress;
uint64_t materialIndexAddress;
};
#include "host_device.h"
vec3 computeDiffuse(WaveFrontMaterial mat, vec3 lightDir, vec3 normal)
{