New example, loading glTF scenes instead of individual OBJ. Showing simple path tracing and how to make it 3x faster.
This commit is contained in:
parent
813f392fdf
commit
2eb9b6e522
25 changed files with 4410 additions and 2 deletions
|
|
@ -39,6 +39,7 @@ add_subdirectory(ray_tracing__simple)
|
||||||
add_subdirectory(ray_tracing_animation)
|
add_subdirectory(ray_tracing_animation)
|
||||||
add_subdirectory(ray_tracing_anyhit)
|
add_subdirectory(ray_tracing_anyhit)
|
||||||
add_subdirectory(ray_tracing_callable)
|
add_subdirectory(ray_tracing_callable)
|
||||||
|
add_subdirectory(ray_tracing_gltf)
|
||||||
add_subdirectory(ray_tracing_instances)
|
add_subdirectory(ray_tracing_instances)
|
||||||
add_subdirectory(ray_tracing_intersection)
|
add_subdirectory(ray_tracing_intersection)
|
||||||
add_subdirectory(ray_tracing_jitter_cam)
|
add_subdirectory(ray_tracing_jitter_cam)
|
||||||
|
|
@ -46,5 +47,3 @@ add_subdirectory(ray_tracing_manyhits)
|
||||||
add_subdirectory(ray_tracing_rayquery)
|
add_subdirectory(ray_tracing_rayquery)
|
||||||
add_subdirectory(ray_tracing_reflections)
|
add_subdirectory(ray_tracing_reflections)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
BIN
docs/Images/vk_ray_tracing_gltf_KHR.png
Normal file
BIN
docs/Images/vk_ray_tracing_gltf_KHR.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/Images/vk_ray_tracing_gltf_KHR_2.png
Normal file
BIN
docs/Images/vk_ray_tracing_gltf_KHR_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 320 KiB |
BIN
media/scenes/cornellBox.bin
Normal file
BIN
media/scenes/cornellBox.bin
Normal file
Binary file not shown.
1570
media/scenes/cornellBox.gltf
Normal file
1570
media/scenes/cornellBox.gltf
Normal file
File diff suppressed because it is too large
Load diff
104
ray_tracing_gltf/CMakeLists.txt
Normal file
104
ray_tracing_gltf/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||||
|
SET(PROJNAME vk_${PROJNAME}_KHR)
|
||||||
|
|
||||||
|
Project(${PROJNAME})
|
||||||
|
Message(STATUS "-------------------------------")
|
||||||
|
Message(STATUS "Processing Project ${PROJNAME}:")
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
_add_project_definitions(${PROJNAME})
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# Source files for this project
|
||||||
|
#
|
||||||
|
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
|
||||||
|
file(GLOB EXTRA_COMMON "../common/*.*")
|
||||||
|
list(APPEND COMMON_SOURCE_FILES ${EXTRA_COMMON})
|
||||||
|
include_directories("../common")
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# GLSL to SPIR-V custom build
|
||||||
|
#
|
||||||
|
# more than one file can be given: _compile_GLSL("GLSL_mesh.vert;GLSL_mesh.frag" "GLSL_mesh.spv" GLSL_SOURCES)
|
||||||
|
# the SpirV validator is fine as long as files are for different pipeline stages (entry points still need to be main())
|
||||||
|
#_compile_GLSL(<source(s)> <target spv> <LIST where files are appended>)
|
||||||
|
SET(VULKAN_TARGET_ENV vulkan1.2)
|
||||||
|
|
||||||
|
UNSET(GLSL_SOURCES)
|
||||||
|
UNSET(SPV_OUTPUT)
|
||||||
|
file(GLOB_RECURSE GLSL_HEADER_FILES "shaders/*.h" "shaders/*.glsl")
|
||||||
|
file(GLOB_RECURSE GLSL_SOURCE_FILES
|
||||||
|
"shaders/*.comp"
|
||||||
|
"shaders/*.frag"
|
||||||
|
"shaders/*.vert"
|
||||||
|
"shaders/*.rchit"
|
||||||
|
"shaders/*.rahit"
|
||||||
|
"shaders/*.rmiss"
|
||||||
|
"shaders/*.rgen"
|
||||||
|
)
|
||||||
|
foreach(GLSL ${GLSL_SOURCE_FILES})
|
||||||
|
get_filename_component(FILE_NAME ${GLSL} NAME)
|
||||||
|
_compile_GLSL(${GLSL} "shaders/${FILE_NAME}.spv" GLSL_SOURCES SPV_OUTPUT)
|
||||||
|
endforeach(GLSL)
|
||||||
|
|
||||||
|
list(APPEND GLSL_SOURCES ${GLSL_HEADER_FILES})
|
||||||
|
source_group(Shader_Files FILES ${GLSL_SOURCES})
|
||||||
|
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# Executable
|
||||||
|
#
|
||||||
|
# if(WIN32 AND NOT GLUT_FOUND)
|
||||||
|
# add_definitions(/wd4996) #remove printf warning
|
||||||
|
# add_definitions(/wd4244) #remove double to float conversion warning
|
||||||
|
# add_definitions(/wd4305) #remove double to float truncation warning
|
||||||
|
# else()
|
||||||
|
# add_definitions(-fpermissive)
|
||||||
|
# endif()
|
||||||
|
add_executable(${PROJNAME} ${SOURCE_FILES} ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES} ${GLSL_SOURCES} ${CUDA_FILES} ${CUBIN_SOURCES})
|
||||||
|
|
||||||
|
#_set_subsystem_console(${PROJNAME})
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# common source code needed for this sample
|
||||||
|
#
|
||||||
|
source_group(common FILES
|
||||||
|
${COMMON_SOURCE_FILES}
|
||||||
|
${PACKAGE_SOURCE_FILES}
|
||||||
|
)
|
||||||
|
source_group("Source Files" FILES ${SOURCE_FILES})
|
||||||
|
|
||||||
|
# if(UNIX)
|
||||||
|
# set(UNIXLINKLIBS dl pthread)
|
||||||
|
# else()
|
||||||
|
# set(UNIXLINKLIBS)
|
||||||
|
# endif()
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# Linkage
|
||||||
|
#
|
||||||
|
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} shared_sources)
|
||||||
|
|
||||||
|
foreach(DEBUGLIB ${LIBRARIES_DEBUG})
|
||||||
|
target_link_libraries(${PROJNAME} debug ${DEBUGLIB})
|
||||||
|
endforeach(DEBUGLIB)
|
||||||
|
|
||||||
|
foreach(RELEASELIB ${LIBRARIES_OPTIMIZED})
|
||||||
|
target_link_libraries(${PROJNAME} optimized ${RELEASELIB})
|
||||||
|
endforeach(RELEASELIB)
|
||||||
|
|
||||||
|
#####################################################################################
|
||||||
|
# copies binaries that need to be put next to the exe files (ZLib, etc.)
|
||||||
|
#
|
||||||
|
_copy_binaries_to_target( ${PROJNAME} )
|
||||||
|
|
||||||
|
|
||||||
|
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/shaders")
|
||||||
|
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/shaders")
|
||||||
|
install(FILES ${CUBIN_SOURCES} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}")
|
||||||
|
install(FILES ${CUBIN_SOURCES} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}")
|
||||||
|
install(DIRECTORY "../media" CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}")
|
||||||
|
install(DIRECTORY "../media" CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}")
|
||||||
525
ray_tracing_gltf/README.md
Normal file
525
ray_tracing_gltf/README.md
Normal file
|
|
@ -0,0 +1,525 @@
|
||||||
|
# NVIDIA Vulkan Ray Tracing Tutorial - glTF Scene
|
||||||
|
|
||||||
|
This example is the result of the modification of the [simple ray tracing](../ray_tracing__simple) tutorial.
|
||||||
|
Instead of loading separated OBJ objects, the example was modified to load glTF scene files containing multiple objects.
|
||||||
|
|
||||||
|
This example is not about shading, but using more complex data than OBJ.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Scene Data
|
||||||
|
|
||||||
|
The OBJ models were loaded and stored in four buffers:
|
||||||
|
|
||||||
|
* vertices: array of structure of position, normal, texcoord, color
|
||||||
|
* indices: index of the vertex, every three makes a triangle
|
||||||
|
* materials: the wavefront material structure
|
||||||
|
* material index: material index per triangle.
|
||||||
|
|
||||||
|
Since we could have multiple OBJ, we would have arrays of those buffers.
|
||||||
|
|
||||||
|
With glTF scene, the data will be organized differently a choice we have made for convenience. Instead of having structure of vertices,
|
||||||
|
positions, normals and other attributes will be in separate buffers. There will be one single position buffer,
|
||||||
|
for all geometries of the scene, same for indices and other attributes. But for each geometry there is the information
|
||||||
|
of the number of elements and offsets.
|
||||||
|
|
||||||
|
From the source tutorial, we will not need the following and therefore remove it:
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
struct ObjModel {..};
|
||||||
|
struct ObjInstance {..};
|
||||||
|
std::vector<ObjModel> m_objModel;
|
||||||
|
std::vector<ObjInstance> m_objInstance;
|
||||||
|
nvvk::Buffer m_sceneDesc; // Device buffer of the OBJ instances
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
But instead, we will use this following structure to retrieve the information of the primitive that has been hit in the closest hit shader;
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
// Structure used for retrieving the primitive information in the closest hit
|
||||||
|
// The gl_InstanceCustomIndexNV
|
||||||
|
struct RtPrimitiveLookup
|
||||||
|
{
|
||||||
|
uint32_t indexOffset;
|
||||||
|
uint32_t vertexOffset;
|
||||||
|
int materialIndex;
|
||||||
|
};
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
And for holding the information, we will be using a helper class to hold glTF scene and buffers for the data.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
nvh::GltfScene m_gltfScene;
|
||||||
|
nvvk::Buffer m_vertexBuffer;
|
||||||
|
nvvk::Buffer m_normalBuffer;
|
||||||
|
nvvk::Buffer m_uvBuffer;
|
||||||
|
nvvk::Buffer m_indexBuffer;
|
||||||
|
nvvk::Buffer m_materialBuffer;
|
||||||
|
nvvk::Buffer m_matrixBuffer;
|
||||||
|
nvvk::Buffer m_rtPrimLookup;
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
## Loading glTF scene
|
||||||
|
|
||||||
|
To load the scene, we will be using [TinyGLTF](https://github.com/syoyo/tinygltf) from Syoyo Fujita, then to avoid traversing
|
||||||
|
the scene graph, the information will be flatten using the helper [gltfScene](https://github.com/nvpro-samples/shared_sources/tree/master/nvh#gltfscenehpp).
|
||||||
|
|
||||||
|
### Loading Scene
|
||||||
|
|
||||||
|
Instead of loading a model, we will be loading a scene, so we are replacing `loadModel()` by `loadScene()`.
|
||||||
|
|
||||||
|
In the source file, loading the scene `loadScene()` will have first the glTF import with TinyGLTF.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
tinygltf::Model tmodel;
|
||||||
|
tinygltf::TinyGLTF tcontext;
|
||||||
|
std::string warn, error;
|
||||||
|
|
||||||
|
if(!tcontext.LoadASCIIFromFile(&tmodel, &error, &warn, filename))
|
||||||
|
assert(!"Error while loading scene");
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Then we will flatten the scene graph and grab the information we will need using the gltfScene helper.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
m_gltfScene.importMaterials(tmodel);
|
||||||
|
m_gltfScene.importDrawableNodes(tmodel,
|
||||||
|
nvh::GltfAttributes::Normal | nvh::GltfAttributes::Texcoord_0);
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
The next par is to allocate the buffers to hold the information, such as the positions, normals, texture coordinates, etc.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
m_vertexBuffer =
|
||||||
|
m_alloc.createBuffer(cmdBuf, m_gltfScene.m_positions,
|
||||||
|
vkBU::eVertexBuffer | vkBU::eStorageBuffer | vkBU::eShaderDeviceAddress);
|
||||||
|
m_indexBuffer =
|
||||||
|
m_alloc.createBuffer(cmdBuf, m_gltfScene.m_indices,
|
||||||
|
vkBU::eIndexBuffer | vkBU::eStorageBuffer | vkBU::eShaderDeviceAddress);
|
||||||
|
m_normalBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_normals,
|
||||||
|
vkBU::eVertexBuffer | vkBU::eStorageBuffer);
|
||||||
|
m_uvBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_texcoords0,
|
||||||
|
vkBU::eVertexBuffer | vkBU::eStorageBuffer);
|
||||||
|
m_materialBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_materials, vkBU::eStorageBuffer);
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
We could use `push_constant` to set the matrix of the node, but instead, we will push the index of the
|
||||||
|
node to draw and fetch the matrix from a buffer.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
std::vector<nvmath::mat4f> nodeMatrices;
|
||||||
|
for(auto& node : m_gltfScene.m_nodes)
|
||||||
|
nodeMatrices.emplace_back(node.worldMatrix);
|
||||||
|
m_matrixBuffer = m_alloc.createBuffer(cmdBuf, nodeMatrices, vkBU::eStorageBuffer);
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
To find the positions of the triangle hit in the closest hit shader, as well as the other
|
||||||
|
attributes, we will store the offsets information of that geometry.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
// The following is used to find the primitive mesh information in the CHIT
|
||||||
|
std::vector<RtPrimitiveLookup> primLookup;
|
||||||
|
for(auto& primMesh : m_gltfScene.m_primMeshes)
|
||||||
|
primLookup.push_back({primMesh.firstIndex, primMesh.vertexOffset, primMesh.materialIndex});
|
||||||
|
m_rtPrimLookup =
|
||||||
|
m_alloc.createBuffer(cmdBuf, primLookup, vk::BufferUsageFlagBits::eStorageBuffer);
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
|
||||||
|
## Converting geometry to BLAS
|
||||||
|
|
||||||
|
Instead of `objectToVkGeometryKHR()`, we will be using `primitiveToGeometry(const nvh::GltfPrimMesh& prim)`.
|
||||||
|
The function is similar, only the input is different.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Converting a GLTF primitive in the Raytracing Geometry used for the BLAS
|
||||||
|
//
|
||||||
|
nvvk::RaytracingBuilderKHR::Blas HelloVulkan::primitiveToGeometry(const nvh::GltfPrimMesh& prim)
|
||||||
|
{
|
||||||
|
// Setting up the creation info of acceleration structure
|
||||||
|
vk::AccelerationStructureCreateGeometryTypeInfoKHR asCreate;
|
||||||
|
asCreate.setGeometryType(vk::GeometryTypeKHR::eTriangles);
|
||||||
|
asCreate.setIndexType(vk::IndexType::eUint32);
|
||||||
|
asCreate.setVertexFormat(vk::Format::eR32G32B32Sfloat);
|
||||||
|
asCreate.setMaxPrimitiveCount(prim.indexCount / 3); // Nb triangles
|
||||||
|
asCreate.setMaxVertexCount(prim.vertexCount);
|
||||||
|
asCreate.setAllowsTransforms(VK_FALSE); // No adding transformation matrices
|
||||||
|
|
||||||
|
// Building part
|
||||||
|
vk::DeviceAddress vertexAddress = m_device.getBufferAddress({m_vertexBuffer.buffer});
|
||||||
|
vk::DeviceAddress indexAddress = m_device.getBufferAddress({m_indexBuffer.buffer});
|
||||||
|
|
||||||
|
vk::AccelerationStructureGeometryTrianglesDataKHR triangles;
|
||||||
|
triangles.setVertexFormat(asCreate.vertexFormat);
|
||||||
|
triangles.setVertexData(vertexAddress);
|
||||||
|
triangles.setVertexStride(sizeof(nvmath::vec3f));
|
||||||
|
triangles.setIndexType(asCreate.indexType);
|
||||||
|
triangles.setIndexData(indexAddress);
|
||||||
|
triangles.setTransformData({});
|
||||||
|
|
||||||
|
// Setting up the build info of the acceleration
|
||||||
|
vk::AccelerationStructureGeometryKHR asGeom;
|
||||||
|
asGeom.setGeometryType(asCreate.geometryType);
|
||||||
|
asGeom.setFlags(vk::GeometryFlagBitsKHR::eNoDuplicateAnyHitInvocation); // For AnyHit
|
||||||
|
asGeom.geometry.setTriangles(triangles);
|
||||||
|
|
||||||
|
|
||||||
|
vk::AccelerationStructureBuildOffsetInfoKHR offset;
|
||||||
|
offset.setFirstVertex(prim.vertexOffset);
|
||||||
|
offset.setPrimitiveCount(prim.indexCount / 3);
|
||||||
|
offset.setPrimitiveOffset(prim.firstIndex * sizeof(uint32_t));
|
||||||
|
offset.setTransformOffset(0);
|
||||||
|
|
||||||
|
nvvk::RaytracingBuilderKHR::Blas blas;
|
||||||
|
blas.asGeometry.emplace_back(asGeom);
|
||||||
|
blas.asCreateGeometryInfo.emplace_back(asCreate);
|
||||||
|
blas.asBuildOffsetInfo.emplace_back(offset);
|
||||||
|
return blas;
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
## Top Level creation
|
||||||
|
|
||||||
|
There are almost no changes for creating the TLAS but is actually even simpler. Each
|
||||||
|
drawable node has a matrix and an index to the geometry, which in our case, also
|
||||||
|
correspond directly to the BLAS ID. To know which geometry is used, and to find back
|
||||||
|
all the data (see structure `RtPrimitiveLookup`), we will set the `instanceId` member
|
||||||
|
to the primitive mesh id. This value will be recovered with `gl_InstanceCustomIndexEXT`
|
||||||
|
in the closest hit shader.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
for(auto& node : m_gltfScene.m_nodes)
|
||||||
|
{
|
||||||
|
nvvk::RaytracingBuilderKHR::Instance rayInst;
|
||||||
|
rayInst.transform = node.worldMatrix;
|
||||||
|
rayInst.instanceId = node.primMesh; // gl_InstanceCustomIndexEXT: to find which primitive
|
||||||
|
rayInst.blasId = node.primMesh;
|
||||||
|
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
|
||||||
|
rayInst.hitGroupId = 0; // We will use the same hit group for all objects
|
||||||
|
tlas.emplace_back(rayInst);
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
|
||||||
|
## Raster Rendering
|
||||||
|
|
||||||
|
Raster rendering is simple. The shader was changed to use vertex, normal and texture coordinates. For
|
||||||
|
each node, we will be pushing the instance Id (retrieve the matrix) and the material Id. Since we
|
||||||
|
don't have a scene graph, we could loop over all drawable nodes.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
std::vector<vk::Buffer> vertexBuffers = {m_vertexBuffer.buffer, m_normalBuffer.buffer,
|
||||||
|
m_uvBuffer.buffer};
|
||||||
|
cmdBuf.bindVertexBuffers(0, static_cast<uint32_t>(vertexBuffers.size()), vertexBuffers.data(),
|
||||||
|
offsets.data());
|
||||||
|
cmdBuf.bindIndexBuffer(m_indexBuffer.buffer, 0, vk::IndexType::eUint32);
|
||||||
|
|
||||||
|
uint32_t idxNode = 0;
|
||||||
|
for(auto& node : m_gltfScene.m_nodes)
|
||||||
|
{
|
||||||
|
auto& primitive = m_gltfScene.m_primMeshes[node.primMesh];
|
||||||
|
|
||||||
|
m_pushConstant.instanceId = idxNode++;
|
||||||
|
m_pushConstant.materialId = primitive.materialIndex;
|
||||||
|
cmdBuf.pushConstants<ObjPushConstant>(
|
||||||
|
m_pipelineLayout, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0,
|
||||||
|
m_pushConstant);
|
||||||
|
cmdBuf.drawIndexed(primitive.indexCount, 1, primitive.firstIndex, primitive.vertexOffset, 0);
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
|
||||||
|
## Ray tracing change
|
||||||
|
|
||||||
|
In `createRtDescriptorSet()`, the only change we will add is the primitive info buffer to retrieve
|
||||||
|
the data when hitting a triangle.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
m_rtDescSetLayoutBind.addBinding(
|
||||||
|
vkDSLB(2, vkDT::eStorageBuffer, 1, vkSS::eClosestHitNV | vkSS::eAnyHitNV)); // Primitive info
|
||||||
|
....
|
||||||
|
vk::DescriptorBufferInfo primitiveInfoDesc{m_rtPrimLookup.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
....
|
||||||
|
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 2, &primitiveInfoDesc));
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
|
||||||
|
## Descriptors and Pipeline Changes
|
||||||
|
|
||||||
|
Since we are using different buffers and the vertex is no longer a struct but is using
|
||||||
|
3 different buffers for the position, normal and texture coord.
|
||||||
|
The methods `createDescriptorSetLayout()`, `updateDescriptorSet()` and `createGraphicsPipeline()`
|
||||||
|
will be changed accordingly.
|
||||||
|
|
||||||
|
See [hello_vulkan](hello_vulkan.cpp)
|
||||||
|
|
||||||
|
|
||||||
|
## Shaders
|
||||||
|
|
||||||
|
The shading is the same and is not reflecting the glTF PBR shading model, but the shaders were nevertheless
|
||||||
|
changed to fit the new incoming format.
|
||||||
|
|
||||||
|
* Raster : [vertex](shaders/vert_shader.vert), [fragment](shaders/frag_shader.frag)
|
||||||
|
* Ray Trace: [RayGen](shaders/raytrace.rgen), [ClosestHit](shaders/raytrace.rchit)
|
||||||
|
|
||||||
|
|
||||||
|
## Other changes
|
||||||
|
|
||||||
|
Small other changes were done, a different scene, different camera and light position.
|
||||||
|
|
||||||
|
Camera position
|
||||||
|
~~~~C
|
||||||
|
CameraManip.setLookat(nvmath::vec3f(0, 0, 15), nvmath::vec3f(0, 0, 0), nvmath::vec3f(0, 1, 0));
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Scene
|
||||||
|
~~~~C
|
||||||
|
helloVk.loadScene(nvh::findFile("media/scenes/cornellBox.gltf", defaultSearchPaths));
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Light Position
|
||||||
|
~~~~C
|
||||||
|
nvmath::vec3f lightPosition{0.f, 4.5f, 0.f};
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
# Simple Path Tracing
|
||||||
|
|
||||||
|
To convert this example to a simple path tracer (see Wikipedia [Path Tracing](https://en.wikipedia.org/wiki/Path_tracing)), we need to change the `RayGen` and the `ClosestHit` shaders.
|
||||||
|
Before doing this, we will modify the application to send the current rendering frame, allowing to accumulate
|
||||||
|
samples.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Add the following two functions in `hello_vulkan.cpp`:
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// If the camera matrix has changed, resets the frame.
|
||||||
|
// otherwise, increments frame.
|
||||||
|
//
|
||||||
|
void HelloVulkan::updateFrame()
|
||||||
|
{
|
||||||
|
static nvmath::mat4f refCamMatrix;
|
||||||
|
|
||||||
|
auto& m = CameraManip.getMatrix();
|
||||||
|
if(memcmp(&refCamMatrix.a00, &m.a00, sizeof(nvmath::mat4f)) != 0)
|
||||||
|
{
|
||||||
|
resetFrame();
|
||||||
|
refCamMatrix = m;
|
||||||
|
}
|
||||||
|
m_rtPushConstants.frame++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloVulkan::resetFrame()
|
||||||
|
{
|
||||||
|
m_rtPushConstants.frame = -1;
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
And call `updateFrame()` in the begining of the `raytrace()` function.
|
||||||
|
|
||||||
|
In `hello_vulkan.cpp`, add the function declarations
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
void updateFrame();
|
||||||
|
void resetFrame();
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
And add a new `frame` member at the end of `RtPushConstant` structure.
|
||||||
|
|
||||||
|
## Ray Generation
|
||||||
|
|
||||||
|
There are a few modifications to be done in the ray generation. First, it will use the clock for its random seed number.
|
||||||
|
|
||||||
|
This is done by adding the [`GL_ARB_shader_clock`](https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shader_clock.txt) extension.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
#extension GL_ARB_shader_clock : enable
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
The random number generator is in `sampling.glsl`, `#include` this file.
|
||||||
|
|
||||||
|
In `main()`, we will initialize the random number like this: (see tutorial on jitter camera)
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
// Initialize the random number
|
||||||
|
uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, int(clockARB()));
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
To accumulate the samples, instead of only write to the image, we will also use the previous frame.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
// Do accumulation over time
|
||||||
|
if(pushC.frame > 0)
|
||||||
|
{
|
||||||
|
float a = 1.0f / float(pushC.frame + 1);
|
||||||
|
vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz;
|
||||||
|
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(mix(old_color, prd.hitValue, a), 1.f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First frame, replace the value in the buffer
|
||||||
|
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(prd.hitValue, 1.f));
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Extra information will be needed in the ray payload `hitPayload`, the `seed` and the `depth`.
|
||||||
|
|
||||||
|
The modification in `raycommon.glsl`
|
||||||
|
~~~~C
|
||||||
|
struct hitPayload
|
||||||
|
{
|
||||||
|
vec3 hitValue;
|
||||||
|
uint seed;
|
||||||
|
uint depth;
|
||||||
|
};
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
## Closest Hit Shader
|
||||||
|
|
||||||
|
This modification will recursively trace until the `depth`hits 10 (hardcoded) or hit an emissive element (light).
|
||||||
|
|
||||||
|
The only information that we will keep from the shader, is the calculation of the hit state: position, normal. So
|
||||||
|
all code from `// Vector toward the light` to the end can be remove and be replaced by the following.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
// https://en.wikipedia.org/wiki/Path_tracing
|
||||||
|
// Material of the object
|
||||||
|
GltfMaterial mat = materials[nonuniformEXT(matIndex)];
|
||||||
|
vec3 emittance = mat.emissiveFactor;
|
||||||
|
|
||||||
|
// Pick a random direction from here and keep going.
|
||||||
|
vec3 tangent, bitangent;
|
||||||
|
createCoordinateSystem(world_normal, tangent, bitangent);
|
||||||
|
vec3 rayOrigin = world_position;
|
||||||
|
vec3 rayDirection = samplingHemisphere(prd.seed, tangent, bitangent, world_normal);
|
||||||
|
|
||||||
|
// Probability of the newRay (cosine distributed)
|
||||||
|
const float p = 1 / M_PI;
|
||||||
|
|
||||||
|
// Compute the BRDF for this ray (assuming Lambertian reflection)
|
||||||
|
float cos_theta = dot(rayDirection, world_normal);
|
||||||
|
vec3 BRDF = mat.pbrBaseColorFactor.xyz / M_PI;
|
||||||
|
|
||||||
|
// Recursively trace reflected light sources.
|
||||||
|
if(prd.depth < 10)
|
||||||
|
{
|
||||||
|
prd.depth++;
|
||||||
|
float tMin = 0.001;
|
||||||
|
float tMax = 100000000.0;
|
||||||
|
uint flags = gl_RayFlagsOpaqueEXT;
|
||||||
|
traceRayEXT(topLevelAS, // acceleration structure
|
||||||
|
flags, // rayFlags
|
||||||
|
0xFF, // cullMask
|
||||||
|
0, // sbtRecordOffset
|
||||||
|
0, // sbtRecordStride
|
||||||
|
0, // missIndex
|
||||||
|
rayOrigin, // ray origin
|
||||||
|
tMin, // ray min range
|
||||||
|
rayDirection, // ray direction
|
||||||
|
tMax, // ray max range
|
||||||
|
0 // payload (location = 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
vec3 incoming = prd.hitValue;
|
||||||
|
|
||||||
|
// Apply the Rendering Equation here.
|
||||||
|
prd.hitValue = emittance + (BRDF * incoming * cos_theta / p);
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
|
||||||
|
## Miss Shader
|
||||||
|
|
||||||
|
To avoid contribution from the environment.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if(prd.depth == 0)
|
||||||
|
prd.hitValue = clearColor.xyz * 0.8;
|
||||||
|
else
|
||||||
|
prd.hitValue = vec3(0.01); // Tiny contribution from environment
|
||||||
|
prd.depth = 100; // Ending trace
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
# Faster Path Tracer
|
||||||
|
|
||||||
|
The implementation above is recursive and this is really not optimal. As described in the [reflection](../vk_ray_tracing_reflection)
|
||||||
|
tutorial, the best is to break the recursivity and do most of the work in the `RayGen`.
|
||||||
|
|
||||||
|
The following change can give up to **3 time faster** rendering.
|
||||||
|
|
||||||
|
To be able to do this, we need to extend the ray `payload` to bring data from the `Closest Hit` to the `RayGen`, which is the
|
||||||
|
ray origin and direction and the BRDF weight.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
struct hitPayload
|
||||||
|
{
|
||||||
|
vec3 hitValue;
|
||||||
|
uint seed;
|
||||||
|
uint depth;
|
||||||
|
vec3 rayOrigin;
|
||||||
|
vec3 rayDirection;
|
||||||
|
vec3 weight;
|
||||||
|
};
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
## Closest Hit
|
||||||
|
|
||||||
|
We don't need to trace anymore, so before tracing a new ray, we can store the information in
|
||||||
|
the `payload` and return before the recursion code.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
prd.rayOrigin = rayOrigin;
|
||||||
|
prd.rayDirection = rayDirection;
|
||||||
|
prd.hitValue = emittance;
|
||||||
|
prd.weight = BRDF * cos_theta / p;
|
||||||
|
return;
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
## Ray Generation
|
||||||
|
|
||||||
|
The ray generation is the one that will do the trace loop.
|
||||||
|
|
||||||
|
First initialize the `payload` and variable to compute the accumulation.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
prd.rayOrigin = origin.xyz;
|
||||||
|
prd.rayDirection = direction.xyz;
|
||||||
|
prd.weight = vec3(0);
|
||||||
|
|
||||||
|
vec3 curWeight = vec3(1);
|
||||||
|
vec3 hitValue = vec3(0);
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
Now the loop over the trace function, will be like the following.
|
||||||
|
|
||||||
|
**Note:** the depth is hardcode, but could be a parameter to the `push constant`.
|
||||||
|
|
||||||
|
~~~~C
|
||||||
|
for(; prd.depth < 10; prd.depth++)
|
||||||
|
{
|
||||||
|
traceRayEXT(topLevelAS, // acceleration structure
|
||||||
|
rayFlags, // rayFlags
|
||||||
|
0xFF, // cullMask
|
||||||
|
0, // sbtRecordOffset
|
||||||
|
0, // sbtRecordStride
|
||||||
|
0, // missIndex
|
||||||
|
prd.rayOrigin, // ray origin
|
||||||
|
tMin, // ray min range
|
||||||
|
prd.rayDirection, // ray direction
|
||||||
|
tMax, // ray max range
|
||||||
|
0 // payload (location = 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
hitValue += prd.hitValue * curWeight;
|
||||||
|
curWeight *= prd.weight;
|
||||||
|
}
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
**Note:** do not forget to use `hitValue` in the `imageStore`.
|
||||||
|
|
||||||
|
|
||||||
914
ray_tracing_gltf/hello_vulkan.cpp
Normal file
914
ray_tracing_gltf/hello_vulkan.cpp
Normal file
|
|
@ -0,0 +1,914 @@
|
||||||
|
/* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
extern std::vector<std::string> defaultSearchPaths;
|
||||||
|
|
||||||
|
#define TINYGLTF_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
|
||||||
|
#include "hello_vulkan.h"
|
||||||
|
#include "nvh/cameramanipulator.hpp"
|
||||||
|
#include "nvh/fileoperations.hpp"
|
||||||
|
#include "nvh/gltfscene.hpp"
|
||||||
|
#include "nvvk/commands_vk.hpp"
|
||||||
|
#include "nvvk/descriptorsets_vk.hpp"
|
||||||
|
#include "nvvk/pipeline_vk.hpp"
|
||||||
|
#include "nvvk/renderpasses_vk.hpp"
|
||||||
|
#include "nvvk/shaders_vk.hpp"
|
||||||
|
|
||||||
|
#include "shaders/binding.glsl"
|
||||||
|
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
void HelloVulkan::setup(const vk::Instance& instance,
|
||||||
|
const vk::Device& device,
|
||||||
|
const vk::PhysicalDevice& physicalDevice,
|
||||||
|
uint32_t queueFamily)
|
||||||
|
{
|
||||||
|
AppBase::setup(instance, device, physicalDevice, queueFamily);
|
||||||
|
m_alloc.init(device, physicalDevice);
|
||||||
|
m_debug.setup(m_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Called at each frame to update the camera matrix
|
||||||
|
//
|
||||||
|
void HelloVulkan::updateUniformBuffer()
|
||||||
|
{
|
||||||
|
const float aspectRatio = m_size.width / static_cast<float>(m_size.height);
|
||||||
|
|
||||||
|
CameraMatrices ubo = {};
|
||||||
|
ubo.view = CameraManip.getMatrix();
|
||||||
|
ubo.proj = nvmath::perspectiveVK(CameraManip.getFov(), aspectRatio, 0.1f, 1000.0f);
|
||||||
|
// ubo.proj[1][1] *= -1; // Inverting Y for Vulkan
|
||||||
|
ubo.viewInverse = nvmath::invert(ubo.view);
|
||||||
|
// #VKRay
|
||||||
|
ubo.projInverse = nvmath::invert(ubo.proj);
|
||||||
|
|
||||||
|
void* data = m_device.mapMemory(m_cameraMat.allocation, 0, sizeof(ubo));
|
||||||
|
memcpy(data, &ubo, sizeof(ubo));
|
||||||
|
m_device.unmapMemory(m_cameraMat.allocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Describing the layout pushed when rendering
|
||||||
|
//
|
||||||
|
void HelloVulkan::createDescriptorSetLayout()
|
||||||
|
{
|
||||||
|
using vkDS = vk::DescriptorSetLayoutBinding;
|
||||||
|
using vkDT = vk::DescriptorType;
|
||||||
|
using vkSS = vk::ShaderStageFlagBits;
|
||||||
|
uint32_t nbTxt = static_cast<uint32_t>(m_textures.size());
|
||||||
|
|
||||||
|
auto& bind = m_descSetLayoutBind;
|
||||||
|
// Camera matrices (binding = 0)
|
||||||
|
bind.addBinding(vkDS(B_CAMERA, vkDT::eUniformBuffer, 1, vkSS::eVertex | vkSS::eRaygenKHR));
|
||||||
|
bind.addBinding(
|
||||||
|
vkDS(B_VERTICES, vkDT::eStorageBuffer, 1, vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
|
||||||
|
bind.addBinding(
|
||||||
|
vkDS(B_INDICES, vkDT::eStorageBuffer, 1, vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
|
||||||
|
bind.addBinding(vkDS(B_NORMALS, vkDT::eStorageBuffer, 1, vkSS::eClosestHitKHR));
|
||||||
|
bind.addBinding(vkDS(B_TEXCOORDS, vkDT::eStorageBuffer, 1, vkSS::eClosestHitKHR));
|
||||||
|
bind.addBinding(vkDS(B_MATERIALS, vkDT::eStorageBuffer, 1,
|
||||||
|
vkSS::eFragment | vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
|
||||||
|
bind.addBinding(vkDS(B_MATRICES, vkDT::eStorageBuffer, 1,
|
||||||
|
vkSS::eVertex | vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
|
||||||
|
auto nbTextures = static_cast<uint32_t>(m_textures.size());
|
||||||
|
bind.addBinding(vkDS(B_TEXTURES, vkDT::eCombinedImageSampler, nbTextures,
|
||||||
|
vkSS::eFragment | vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
|
||||||
|
|
||||||
|
|
||||||
|
m_descSetLayout = m_descSetLayoutBind.createLayout(m_device);
|
||||||
|
m_descPool = m_descSetLayoutBind.createPool(m_device, 1);
|
||||||
|
m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Setting up the buffers in the descriptor set
|
||||||
|
//
|
||||||
|
void HelloVulkan::updateDescriptorSet()
|
||||||
|
{
|
||||||
|
std::vector<vk::WriteDescriptorSet> writes;
|
||||||
|
|
||||||
|
// Camera matrices and scene description
|
||||||
|
vk::DescriptorBufferInfo dbiUnif{m_cameraMat.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo vertexDesc{m_vertexBuffer.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo indexDesc{m_indexBuffer.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo normalDesc{m_normalBuffer.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo uvDesc{m_uvBuffer.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo materialDesc{m_materialBuffer.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo matrixDesc{m_matrixBuffer.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_CAMERA, &dbiUnif));
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_VERTICES, &vertexDesc));
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_INDICES, &indexDesc));
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_NORMALS, &normalDesc));
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_TEXCOORDS, &uvDesc));
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_MATERIALS, &materialDesc));
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, B_MATRICES, &matrixDesc));
|
||||||
|
|
||||||
|
// All texture samplers
|
||||||
|
std::vector<vk::DescriptorImageInfo> diit;
|
||||||
|
for(auto& texture : m_textures)
|
||||||
|
diit.emplace_back(texture.descriptor);
|
||||||
|
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, B_TEXTURES, diit.data()));
|
||||||
|
|
||||||
|
// Writing the information
|
||||||
|
m_device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Creating the pipeline layout
|
||||||
|
//
|
||||||
|
void HelloVulkan::createGraphicsPipeline()
|
||||||
|
{
|
||||||
|
using vkSS = vk::ShaderStageFlagBits;
|
||||||
|
|
||||||
|
vk::PushConstantRange pushConstantRanges = {vkSS::eVertex | vkSS::eFragment, 0,
|
||||||
|
sizeof(ObjPushConstant)};
|
||||||
|
|
||||||
|
// Creating the Pipeline Layout
|
||||||
|
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
|
||||||
|
vk::DescriptorSetLayout descSetLayout(m_descSetLayout);
|
||||||
|
pipelineLayoutCreateInfo.setSetLayoutCount(1);
|
||||||
|
pipelineLayoutCreateInfo.setPSetLayouts(&descSetLayout);
|
||||||
|
pipelineLayoutCreateInfo.setPushConstantRangeCount(1);
|
||||||
|
pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRanges);
|
||||||
|
m_pipelineLayout = m_device.createPipelineLayout(pipelineLayoutCreateInfo);
|
||||||
|
|
||||||
|
// Creating the Pipeline
|
||||||
|
std::vector<std::string> paths = defaultSearchPaths;
|
||||||
|
nvvk::GraphicsPipelineGeneratorCombined gpb(m_device, m_pipelineLayout, m_offscreenRenderPass);
|
||||||
|
gpb.depthStencilState.depthTestEnable = true;
|
||||||
|
gpb.addShader(nvh::loadFile("shaders/vert_shader.vert.spv", true, paths), vkSS::eVertex);
|
||||||
|
gpb.addShader(nvh::loadFile("shaders/frag_shader.frag.spv", true, paths), vkSS::eFragment);
|
||||||
|
gpb.addBindingDescriptions(
|
||||||
|
{{0, sizeof(nvmath::vec3)}, {1, sizeof(nvmath::vec3)}, {2, sizeof(nvmath::vec2)}});
|
||||||
|
gpb.addAttributeDescriptions({
|
||||||
|
{0, 0, vk::Format::eR32G32B32Sfloat, 0}, // Position
|
||||||
|
{1, 1, vk::Format::eR32G32B32Sfloat, 0}, // Normal
|
||||||
|
{2, 2, vk::Format::eR32G32Sfloat, 0}, // Texcoord0
|
||||||
|
});
|
||||||
|
m_graphicsPipeline = gpb.createPipeline();
|
||||||
|
m_debug.setObjectName(m_graphicsPipeline, "Graphics");
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Loading the OBJ file and setting up all buffers
|
||||||
|
//
|
||||||
|
void HelloVulkan::loadScene(const std::string& filename)
|
||||||
|
{
|
||||||
|
using vkBU = vk::BufferUsageFlagBits;
|
||||||
|
tinygltf::Model tmodel;
|
||||||
|
tinygltf::TinyGLTF tcontext;
|
||||||
|
std::string warn, error;
|
||||||
|
|
||||||
|
if(!tcontext.LoadASCIIFromFile(&tmodel, &error, &warn, filename))
|
||||||
|
assert(!"Error while loading scene");
|
||||||
|
|
||||||
|
|
||||||
|
m_gltfScene.importMaterials(tmodel);
|
||||||
|
m_gltfScene.importDrawableNodes(tmodel,
|
||||||
|
nvh::GltfAttributes::Normal | nvh::GltfAttributes::Texcoord_0);
|
||||||
|
|
||||||
|
// Create the buffers on Device and copy vertices, indices and materials
|
||||||
|
nvvk::CommandPool cmdBufGet(m_device, m_graphicsQueueIndex);
|
||||||
|
vk::CommandBuffer cmdBuf = cmdBufGet.createCommandBuffer();
|
||||||
|
|
||||||
|
m_vertexBuffer =
|
||||||
|
m_alloc.createBuffer(cmdBuf, m_gltfScene.m_positions,
|
||||||
|
vkBU::eVertexBuffer | vkBU::eStorageBuffer | vkBU::eShaderDeviceAddress);
|
||||||
|
m_indexBuffer =
|
||||||
|
m_alloc.createBuffer(cmdBuf, m_gltfScene.m_indices,
|
||||||
|
vkBU::eIndexBuffer | vkBU::eStorageBuffer | vkBU::eShaderDeviceAddress);
|
||||||
|
m_normalBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_normals,
|
||||||
|
vkBU::eVertexBuffer | vkBU::eStorageBuffer);
|
||||||
|
m_uvBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_texcoords0,
|
||||||
|
vkBU::eVertexBuffer | vkBU::eStorageBuffer);
|
||||||
|
m_materialBuffer = m_alloc.createBuffer(cmdBuf, m_gltfScene.m_materials, vkBU::eStorageBuffer);
|
||||||
|
|
||||||
|
// Instance Matrices used by rasterizer
|
||||||
|
std::vector<nvmath::mat4f> nodeMatrices;
|
||||||
|
for(auto& node : m_gltfScene.m_nodes)
|
||||||
|
{
|
||||||
|
nodeMatrices.emplace_back(node.worldMatrix);
|
||||||
|
}
|
||||||
|
m_matrixBuffer = m_alloc.createBuffer(cmdBuf, nodeMatrices, vkBU::eStorageBuffer);
|
||||||
|
|
||||||
|
// The following is used to find the primitive mesh information in the CHIT
|
||||||
|
std::vector<RtPrimitiveLookup> primLookup;
|
||||||
|
for(auto& primMesh : m_gltfScene.m_primMeshes)
|
||||||
|
{
|
||||||
|
primLookup.push_back({primMesh.firstIndex, primMesh.vertexOffset, primMesh.materialIndex});
|
||||||
|
}
|
||||||
|
m_rtPrimLookup =
|
||||||
|
m_alloc.createBuffer(cmdBuf, primLookup, vk::BufferUsageFlagBits::eStorageBuffer);
|
||||||
|
|
||||||
|
|
||||||
|
// Creates all textures found
|
||||||
|
createTextureImages(cmdBuf, tmodel);
|
||||||
|
cmdBufGet.submitAndWait(cmdBuf);
|
||||||
|
m_alloc.finalizeAndReleaseStaging();
|
||||||
|
|
||||||
|
m_debug.setObjectName(m_vertexBuffer.buffer, "Vertex");
|
||||||
|
m_debug.setObjectName(m_indexBuffer.buffer, "Index");
|
||||||
|
m_debug.setObjectName(m_normalBuffer.buffer, "Normal");
|
||||||
|
m_debug.setObjectName(m_uvBuffer.buffer, "TexCoord");
|
||||||
|
m_debug.setObjectName(m_materialBuffer.buffer, "Material");
|
||||||
|
m_debug.setObjectName(m_matrixBuffer.buffer, "Matrix");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Creating the uniform buffer holding the camera matrices
|
||||||
|
// - Buffer is host visible
|
||||||
|
//
|
||||||
|
void HelloVulkan::createUniformBuffer()
|
||||||
|
{
|
||||||
|
using vkBU = vk::BufferUsageFlagBits;
|
||||||
|
using vkMP = vk::MemoryPropertyFlagBits;
|
||||||
|
|
||||||
|
m_cameraMat = m_alloc.createBuffer(sizeof(CameraMatrices), vkBU::eUniformBuffer,
|
||||||
|
vkMP::eHostVisible | vkMP::eHostCoherent);
|
||||||
|
m_debug.setObjectName(m_cameraMat.buffer, "cameraMat");
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Creating all textures and samplers
|
||||||
|
//
|
||||||
|
void HelloVulkan::createTextureImages(const vk::CommandBuffer& cmdBuf, tinygltf::Model& gltfModel)
|
||||||
|
{
|
||||||
|
using vkIU = vk::ImageUsageFlagBits;
|
||||||
|
|
||||||
|
vk::SamplerCreateInfo samplerCreateInfo{
|
||||||
|
{}, vk::Filter::eLinear, vk::Filter::eLinear, vk::SamplerMipmapMode::eLinear};
|
||||||
|
samplerCreateInfo.setMaxLod(FLT_MAX);
|
||||||
|
vk::Format format = vk::Format::eR8G8B8A8Srgb;
|
||||||
|
|
||||||
|
if(gltfModel.images.empty())
|
||||||
|
{
|
||||||
|
// Make dummy image(1,1), needed as we cannot have an empty array
|
||||||
|
nvvk::ScopeCommandBuffer cmdBuf(m_device, m_graphicsQueueIndex);
|
||||||
|
std::array<uint8_t, 4> white = {255, 255, 255, 255};
|
||||||
|
m_textures.emplace_back(m_alloc.createTexture(
|
||||||
|
cmdBuf, 4, white.data(), nvvk::makeImage2DCreateInfo(vk::Extent2D{1, 1}), {}));
|
||||||
|
m_debug.setObjectName(m_textures[0].image, "dummy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_textures.resize(gltfModel.images.size());
|
||||||
|
for(size_t i = 0; i < gltfModel.images.size(); i++)
|
||||||
|
{
|
||||||
|
auto& gltfimage = gltfModel.images[i];
|
||||||
|
void* buffer = &gltfimage.image[0];
|
||||||
|
VkDeviceSize bufferSize = gltfimage.image.size();
|
||||||
|
auto imgSize = vk::Extent2D(gltfimage.width, gltfimage.height);
|
||||||
|
vk::ImageCreateInfo imageCreateInfo =
|
||||||
|
nvvk::makeImage2DCreateInfo(imgSize, format, vkIU::eSampled, true);
|
||||||
|
|
||||||
|
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, buffer, imageCreateInfo);
|
||||||
|
nvvk::cmdGenerateMipmaps(cmdBuf, image.image, format, imgSize, imageCreateInfo.mipLevels);
|
||||||
|
vk::ImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
|
||||||
|
m_textures[i] = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
|
||||||
|
|
||||||
|
m_debug.setObjectName(m_textures[i].image, std::string("Txt" + std::to_string(i)).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Destroying all allocations
|
||||||
|
//
|
||||||
|
void HelloVulkan::destroyResources()
|
||||||
|
{
|
||||||
|
m_device.destroy(m_graphicsPipeline);
|
||||||
|
m_device.destroy(m_pipelineLayout);
|
||||||
|
m_device.destroy(m_descPool);
|
||||||
|
m_device.destroy(m_descSetLayout);
|
||||||
|
m_alloc.destroy(m_cameraMat);
|
||||||
|
|
||||||
|
m_alloc.destroy(m_vertexBuffer);
|
||||||
|
m_alloc.destroy(m_normalBuffer);
|
||||||
|
m_alloc.destroy(m_uvBuffer);
|
||||||
|
m_alloc.destroy(m_indexBuffer);
|
||||||
|
m_alloc.destroy(m_materialBuffer);
|
||||||
|
m_alloc.destroy(m_matrixBuffer);
|
||||||
|
m_alloc.destroy(m_rtPrimLookup);
|
||||||
|
|
||||||
|
for(auto& t : m_textures)
|
||||||
|
{
|
||||||
|
m_alloc.destroy(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#Post
|
||||||
|
m_device.destroy(m_postPipeline);
|
||||||
|
m_device.destroy(m_postPipelineLayout);
|
||||||
|
m_device.destroy(m_postDescPool);
|
||||||
|
m_device.destroy(m_postDescSetLayout);
|
||||||
|
m_alloc.destroy(m_offscreenColor);
|
||||||
|
m_alloc.destroy(m_offscreenDepth);
|
||||||
|
m_device.destroy(m_offscreenRenderPass);
|
||||||
|
m_device.destroy(m_offscreenFramebuffer);
|
||||||
|
|
||||||
|
// #VKRay
|
||||||
|
m_rtBuilder.destroy();
|
||||||
|
m_device.destroy(m_rtDescPool);
|
||||||
|
m_device.destroy(m_rtDescSetLayout);
|
||||||
|
m_device.destroy(m_rtPipeline);
|
||||||
|
m_device.destroy(m_rtPipelineLayout);
|
||||||
|
m_alloc.destroy(m_rtSBTBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Drawing the scene in raster mode
|
||||||
|
//
|
||||||
|
void HelloVulkan::rasterize(const vk::CommandBuffer& cmdBuf)
|
||||||
|
{
|
||||||
|
using vkPBP = vk::PipelineBindPoint;
|
||||||
|
using vkSS = vk::ShaderStageFlagBits;
|
||||||
|
|
||||||
|
std::vector<vk::DeviceSize> offsets = {0, 0, 0};
|
||||||
|
|
||||||
|
m_debug.beginLabel(cmdBuf, "Rasterize");
|
||||||
|
|
||||||
|
// Dynamic Viewport
|
||||||
|
cmdBuf.setViewport(0, {vk::Viewport(0, 0, (float)m_size.width, (float)m_size.height, 0, 1)});
|
||||||
|
cmdBuf.setScissor(0, {{{0, 0}, {m_size.width, m_size.height}}});
|
||||||
|
|
||||||
|
// Drawing all triangles
|
||||||
|
cmdBuf.bindPipeline(vkPBP::eGraphics, m_graphicsPipeline);
|
||||||
|
cmdBuf.bindDescriptorSets(vkPBP::eGraphics, m_pipelineLayout, 0, {m_descSet}, {});
|
||||||
|
std::vector<vk::Buffer> vertexBuffers = {m_vertexBuffer.buffer, m_normalBuffer.buffer,
|
||||||
|
m_uvBuffer.buffer};
|
||||||
|
cmdBuf.bindVertexBuffers(0, static_cast<uint32_t>(vertexBuffers.size()), vertexBuffers.data(),
|
||||||
|
offsets.data());
|
||||||
|
cmdBuf.bindIndexBuffer(m_indexBuffer.buffer, 0, vk::IndexType::eUint32);
|
||||||
|
|
||||||
|
uint32_t idxNode = 0;
|
||||||
|
for(auto& node : m_gltfScene.m_nodes)
|
||||||
|
{
|
||||||
|
auto& primitive = m_gltfScene.m_primMeshes[node.primMesh];
|
||||||
|
|
||||||
|
m_pushConstant.instanceId = idxNode++;
|
||||||
|
m_pushConstant.materialId = primitive.materialIndex;
|
||||||
|
cmdBuf.pushConstants<ObjPushConstant>(
|
||||||
|
m_pipelineLayout, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0,
|
||||||
|
m_pushConstant);
|
||||||
|
cmdBuf.drawIndexed(primitive.indexCount, 1, primitive.firstIndex, primitive.vertexOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_debug.endLabel(cmdBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Handling resize of the window
|
||||||
|
//
|
||||||
|
void HelloVulkan::onResize(int /*w*/, int /*h*/)
|
||||||
|
{
|
||||||
|
createOffscreenRender();
|
||||||
|
updatePostDescriptorSet();
|
||||||
|
updateRtDescriptorSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Post-processing
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Creating an offscreen frame buffer and the associated render pass
|
||||||
|
//
|
||||||
|
void HelloVulkan::createOffscreenRender()
|
||||||
|
{
|
||||||
|
m_alloc.destroy(m_offscreenColor);
|
||||||
|
m_alloc.destroy(m_offscreenDepth);
|
||||||
|
|
||||||
|
// Creating the color image
|
||||||
|
{
|
||||||
|
auto colorCreateInfo = nvvk::makeImage2DCreateInfo(m_size, m_offscreenColorFormat,
|
||||||
|
vk::ImageUsageFlagBits::eColorAttachment
|
||||||
|
| vk::ImageUsageFlagBits::eSampled
|
||||||
|
| vk::ImageUsageFlagBits::eStorage);
|
||||||
|
|
||||||
|
|
||||||
|
nvvk::Image image = m_alloc.createImage(colorCreateInfo);
|
||||||
|
vk::ImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, colorCreateInfo);
|
||||||
|
m_offscreenColor = m_alloc.createTexture(image, ivInfo, vk::SamplerCreateInfo());
|
||||||
|
m_offscreenColor.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the depth buffer
|
||||||
|
auto depthCreateInfo =
|
||||||
|
nvvk::makeImage2DCreateInfo(m_size, m_offscreenDepthFormat,
|
||||||
|
vk::ImageUsageFlagBits::eDepthStencilAttachment);
|
||||||
|
{
|
||||||
|
nvvk::Image image = m_alloc.createImage(depthCreateInfo);
|
||||||
|
|
||||||
|
vk::ImageViewCreateInfo depthStencilView;
|
||||||
|
depthStencilView.setViewType(vk::ImageViewType::e2D);
|
||||||
|
depthStencilView.setFormat(m_offscreenDepthFormat);
|
||||||
|
depthStencilView.setSubresourceRange({vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1});
|
||||||
|
depthStencilView.setImage(image.image);
|
||||||
|
|
||||||
|
m_offscreenDepth = m_alloc.createTexture(image, depthStencilView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting the image layout for both color and depth
|
||||||
|
{
|
||||||
|
nvvk::CommandPool genCmdBuf(m_device, m_graphicsQueueIndex);
|
||||||
|
auto cmdBuf = genCmdBuf.createCommandBuffer();
|
||||||
|
nvvk::cmdBarrierImageLayout(cmdBuf, m_offscreenColor.image, vk::ImageLayout::eUndefined,
|
||||||
|
vk::ImageLayout::eGeneral);
|
||||||
|
nvvk::cmdBarrierImageLayout(cmdBuf, m_offscreenDepth.image, vk::ImageLayout::eUndefined,
|
||||||
|
vk::ImageLayout::eDepthStencilAttachmentOptimal,
|
||||||
|
vk::ImageAspectFlagBits::eDepth);
|
||||||
|
|
||||||
|
genCmdBuf.submitAndWait(cmdBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating a renderpass for the offscreen
|
||||||
|
if(!m_offscreenRenderPass)
|
||||||
|
{
|
||||||
|
m_offscreenRenderPass =
|
||||||
|
nvvk::createRenderPass(m_device, {m_offscreenColorFormat}, m_offscreenDepthFormat, 1, true,
|
||||||
|
true, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the frame buffer for offscreen
|
||||||
|
std::vector<vk::ImageView> attachments = {m_offscreenColor.descriptor.imageView,
|
||||||
|
m_offscreenDepth.descriptor.imageView};
|
||||||
|
|
||||||
|
m_device.destroy(m_offscreenFramebuffer);
|
||||||
|
vk::FramebufferCreateInfo info;
|
||||||
|
info.setRenderPass(m_offscreenRenderPass);
|
||||||
|
info.setAttachmentCount(2);
|
||||||
|
info.setPAttachments(attachments.data());
|
||||||
|
info.setWidth(m_size.width);
|
||||||
|
info.setHeight(m_size.height);
|
||||||
|
info.setLayers(1);
|
||||||
|
m_offscreenFramebuffer = m_device.createFramebuffer(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// The pipeline is how things are rendered, which shaders, type of primitives, depth test and more
|
||||||
|
//
|
||||||
|
void HelloVulkan::createPostPipeline()
|
||||||
|
{
|
||||||
|
// Push constants in the fragment shader
|
||||||
|
vk::PushConstantRange pushConstantRanges = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(float)};
|
||||||
|
|
||||||
|
// Creating the pipeline layout
|
||||||
|
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
|
||||||
|
pipelineLayoutCreateInfo.setSetLayoutCount(1);
|
||||||
|
pipelineLayoutCreateInfo.setPSetLayouts(&m_postDescSetLayout);
|
||||||
|
pipelineLayoutCreateInfo.setPushConstantRangeCount(1);
|
||||||
|
pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRanges);
|
||||||
|
m_postPipelineLayout = m_device.createPipelineLayout(pipelineLayoutCreateInfo);
|
||||||
|
|
||||||
|
// Pipeline: completely generic, no vertices
|
||||||
|
std::vector<std::string> paths = defaultSearchPaths;
|
||||||
|
|
||||||
|
nvvk::GraphicsPipelineGeneratorCombined pipelineGenerator(m_device, m_postPipelineLayout,
|
||||||
|
m_renderPass);
|
||||||
|
pipelineGenerator.addShader(nvh::loadFile("shaders/passthrough.vert.spv", true, paths),
|
||||||
|
vk::ShaderStageFlagBits::eVertex);
|
||||||
|
pipelineGenerator.addShader(nvh::loadFile("shaders/post.frag.spv", true, paths),
|
||||||
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
|
pipelineGenerator.rasterizationState.setCullMode(vk::CullModeFlagBits::eNone);
|
||||||
|
m_postPipeline = pipelineGenerator.createPipeline();
|
||||||
|
m_debug.setObjectName(m_postPipeline, "post");
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// The descriptor layout is the description of the data that is passed to the vertex or the
|
||||||
|
// fragment program.
|
||||||
|
//
|
||||||
|
void HelloVulkan::createPostDescriptor()
|
||||||
|
{
|
||||||
|
using vkDS = vk::DescriptorSetLayoutBinding;
|
||||||
|
using vkDT = vk::DescriptorType;
|
||||||
|
using vkSS = vk::ShaderStageFlagBits;
|
||||||
|
|
||||||
|
m_postDescSetLayoutBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment));
|
||||||
|
m_postDescSetLayout = m_postDescSetLayoutBind.createLayout(m_device);
|
||||||
|
m_postDescPool = m_postDescSetLayoutBind.createPool(m_device);
|
||||||
|
m_postDescSet = nvvk::allocateDescriptorSet(m_device, m_postDescPool, m_postDescSetLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Update the output
|
||||||
|
//
|
||||||
|
void HelloVulkan::updatePostDescriptorSet()
|
||||||
|
{
|
||||||
|
vk::WriteDescriptorSet writeDescriptorSets =
|
||||||
|
m_postDescSetLayoutBind.makeWrite(m_postDescSet, 0, &m_offscreenColor.descriptor);
|
||||||
|
m_device.updateDescriptorSets(writeDescriptorSets, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Draw a full screen quad with the attached image
|
||||||
|
//
|
||||||
|
void HelloVulkan::drawPost(vk::CommandBuffer cmdBuf)
|
||||||
|
{
|
||||||
|
m_debug.beginLabel(cmdBuf, "Post");
|
||||||
|
|
||||||
|
cmdBuf.setViewport(0, {vk::Viewport(0, 0, (float)m_size.width, (float)m_size.height, 0, 1)});
|
||||||
|
cmdBuf.setScissor(0, {{{0, 0}, {m_size.width, m_size.height}}});
|
||||||
|
|
||||||
|
auto aspectRatio = static_cast<float>(m_size.width) / static_cast<float>(m_size.height);
|
||||||
|
cmdBuf.pushConstants<float>(m_postPipelineLayout, vk::ShaderStageFlagBits::eFragment, 0,
|
||||||
|
aspectRatio);
|
||||||
|
cmdBuf.bindPipeline(vk::PipelineBindPoint::eGraphics, m_postPipeline);
|
||||||
|
cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, m_postPipelineLayout, 0,
|
||||||
|
m_postDescSet, {});
|
||||||
|
cmdBuf.draw(3, 1, 0, 0);
|
||||||
|
|
||||||
|
m_debug.endLabel(cmdBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Initialize Vulkan ray tracing
|
||||||
|
// #VKRay
|
||||||
|
void HelloVulkan::initRayTracing()
|
||||||
|
{
|
||||||
|
// Requesting ray tracing properties
|
||||||
|
auto properties = m_physicalDevice.getProperties2<vk::PhysicalDeviceProperties2,
|
||||||
|
vk::PhysicalDeviceRayTracingPropertiesKHR>();
|
||||||
|
m_rtProperties = properties.get<vk::PhysicalDeviceRayTracingPropertiesKHR>();
|
||||||
|
m_rtBuilder.setup(m_device, &m_alloc, m_graphicsQueueIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Converting a GLTF primitive in the Raytracing Geometry used for the BLAS
|
||||||
|
//
|
||||||
|
nvvk::RaytracingBuilderKHR::Blas HelloVulkan::primitiveToGeometry(const nvh::GltfPrimMesh& prim)
|
||||||
|
{
|
||||||
|
// Setting up the creation info of acceleration structure
|
||||||
|
vk::AccelerationStructureCreateGeometryTypeInfoKHR asCreate;
|
||||||
|
asCreate.setGeometryType(vk::GeometryTypeKHR::eTriangles);
|
||||||
|
asCreate.setIndexType(vk::IndexType::eUint32);
|
||||||
|
asCreate.setVertexFormat(vk::Format::eR32G32B32Sfloat);
|
||||||
|
asCreate.setMaxPrimitiveCount(prim.indexCount / 3); // Nb triangles
|
||||||
|
asCreate.setMaxVertexCount(prim.vertexCount);
|
||||||
|
asCreate.setAllowsTransforms(VK_FALSE); // No adding transformation matrices
|
||||||
|
|
||||||
|
// Building part
|
||||||
|
vk::DeviceAddress vertexAddress = m_device.getBufferAddress({m_vertexBuffer.buffer});
|
||||||
|
vk::DeviceAddress indexAddress = m_device.getBufferAddress({m_indexBuffer.buffer});
|
||||||
|
|
||||||
|
vk::AccelerationStructureGeometryTrianglesDataKHR triangles;
|
||||||
|
triangles.setVertexFormat(asCreate.vertexFormat);
|
||||||
|
triangles.setVertexData(vertexAddress);
|
||||||
|
triangles.setVertexStride(sizeof(nvmath::vec3f));
|
||||||
|
triangles.setIndexType(asCreate.indexType);
|
||||||
|
triangles.setIndexData(indexAddress);
|
||||||
|
triangles.setTransformData({});
|
||||||
|
|
||||||
|
// Setting up the build info of the acceleration
|
||||||
|
vk::AccelerationStructureGeometryKHR asGeom;
|
||||||
|
asGeom.setGeometryType(asCreate.geometryType);
|
||||||
|
asGeom.setFlags(vk::GeometryFlagBitsKHR::eNoDuplicateAnyHitInvocation); // For AnyHit
|
||||||
|
asGeom.geometry.setTriangles(triangles);
|
||||||
|
|
||||||
|
|
||||||
|
vk::AccelerationStructureBuildOffsetInfoKHR offset;
|
||||||
|
offset.setFirstVertex(prim.vertexOffset);
|
||||||
|
offset.setPrimitiveCount(prim.indexCount / 3);
|
||||||
|
offset.setPrimitiveOffset(prim.firstIndex * sizeof(uint32_t));
|
||||||
|
offset.setTransformOffset(0);
|
||||||
|
|
||||||
|
nvvk::RaytracingBuilderKHR::Blas blas;
|
||||||
|
blas.asGeometry.emplace_back(asGeom);
|
||||||
|
blas.asCreateGeometryInfo.emplace_back(asCreate);
|
||||||
|
blas.asBuildOffsetInfo.emplace_back(offset);
|
||||||
|
return blas;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
void HelloVulkan::createBottomLevelAS()
|
||||||
|
{
|
||||||
|
// BLAS - Storing each primitive in a geometry
|
||||||
|
std::vector<nvvk::RaytracingBuilderKHR::Blas> allBlas;
|
||||||
|
allBlas.reserve(m_gltfScene.m_primMeshes.size());
|
||||||
|
for(auto& primMesh : m_gltfScene.m_primMeshes)
|
||||||
|
{
|
||||||
|
auto geo = primitiveToGeometry(primMesh);
|
||||||
|
allBlas.push_back({geo});
|
||||||
|
}
|
||||||
|
m_rtBuilder.buildBlas(allBlas, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloVulkan::createTopLevelAS()
|
||||||
|
{
|
||||||
|
std::vector<nvvk::RaytracingBuilderKHR::Instance> tlas;
|
||||||
|
tlas.reserve(m_gltfScene.m_nodes.size());
|
||||||
|
uint32_t instID = 0;
|
||||||
|
for(auto& node : m_gltfScene.m_nodes)
|
||||||
|
{
|
||||||
|
nvvk::RaytracingBuilderKHR::Instance rayInst;
|
||||||
|
rayInst.transform = node.worldMatrix;
|
||||||
|
rayInst.instanceId = node.primMesh; // gl_InstanceCustomIndexEXT: to find which primitive
|
||||||
|
rayInst.blasId = node.primMesh;
|
||||||
|
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
|
||||||
|
rayInst.hitGroupId = 0; // We will use the same hit group for all objects
|
||||||
|
tlas.emplace_back(rayInst);
|
||||||
|
}
|
||||||
|
m_rtBuilder.buildTlas(tlas, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// This descriptor set holds the Acceleration structure and the output image
|
||||||
|
//
|
||||||
|
void HelloVulkan::createRtDescriptorSet()
|
||||||
|
{
|
||||||
|
using vkDT = vk::DescriptorType;
|
||||||
|
using vkSS = vk::ShaderStageFlagBits;
|
||||||
|
using vkDSLB = vk::DescriptorSetLayoutBinding;
|
||||||
|
|
||||||
|
m_rtDescSetLayoutBind.addBinding(vkDSLB(0, vkDT::eAccelerationStructureKHR, 1,
|
||||||
|
vkSS::eRaygenKHR | vkSS::eClosestHitKHR)); // TLAS
|
||||||
|
m_rtDescSetLayoutBind.addBinding(
|
||||||
|
vkDSLB(1, vkDT::eStorageImage, 1, vkSS::eRaygenKHR)); // Output image
|
||||||
|
m_rtDescSetLayoutBind.addBinding(
|
||||||
|
vkDSLB(2, vkDT::eStorageBuffer, 1, vkSS::eClosestHitNV | vkSS::eAnyHitNV)); // Primitive info
|
||||||
|
|
||||||
|
m_rtDescPool = m_rtDescSetLayoutBind.createPool(m_device);
|
||||||
|
m_rtDescSetLayout = m_rtDescSetLayoutBind.createLayout(m_device);
|
||||||
|
m_rtDescSet = m_device.allocateDescriptorSets({m_rtDescPool, 1, &m_rtDescSetLayout})[0];
|
||||||
|
|
||||||
|
vk::AccelerationStructureKHR tlas = m_rtBuilder.getAccelerationStructure();
|
||||||
|
vk::WriteDescriptorSetAccelerationStructureKHR descASInfo;
|
||||||
|
descASInfo.setAccelerationStructureCount(1);
|
||||||
|
descASInfo.setPAccelerationStructures(&tlas);
|
||||||
|
vk::DescriptorImageInfo imageInfo{
|
||||||
|
{}, m_offscreenColor.descriptor.imageView, vk::ImageLayout::eGeneral};
|
||||||
|
vk::DescriptorBufferInfo primitiveInfoDesc{m_rtPrimLookup.buffer, 0, VK_WHOLE_SIZE};
|
||||||
|
|
||||||
|
std::vector<vk::WriteDescriptorSet> 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, 2, &primitiveInfoDesc));
|
||||||
|
m_device.updateDescriptorSets(static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Writes the output image to the descriptor set
|
||||||
|
// - Required when changing resolution
|
||||||
|
//
|
||||||
|
void HelloVulkan::updateRtDescriptorSet()
|
||||||
|
{
|
||||||
|
using vkDT = vk::DescriptorType;
|
||||||
|
|
||||||
|
// (1) Output buffer
|
||||||
|
vk::DescriptorImageInfo imageInfo{
|
||||||
|
{}, m_offscreenColor.descriptor.imageView, vk::ImageLayout::eGeneral};
|
||||||
|
vk::WriteDescriptorSet wds{m_rtDescSet, 1, 0, 1, vkDT::eStorageImage, &imageInfo};
|
||||||
|
m_device.updateDescriptorSets(wds, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Pipeline for the ray tracer: all shaders, raygen, chit, miss
|
||||||
|
//
|
||||||
|
void HelloVulkan::createRtPipeline()
|
||||||
|
{
|
||||||
|
std::vector<std::string> paths = defaultSearchPaths;
|
||||||
|
|
||||||
|
vk::ShaderModule raygenSM =
|
||||||
|
nvvk::createShaderModule(m_device, //
|
||||||
|
nvh::loadFile("shaders/pathtrace.rgen.spv", true, paths));
|
||||||
|
vk::ShaderModule missSM =
|
||||||
|
nvvk::createShaderModule(m_device, //
|
||||||
|
nvh::loadFile("shaders/pathtrace.rmiss.spv", true, paths));
|
||||||
|
|
||||||
|
// The second miss shader is invoked when a shadow ray misses the geometry. It
|
||||||
|
// simply indicates that no occlusion has been found
|
||||||
|
vk::ShaderModule shadowmissSM =
|
||||||
|
nvvk::createShaderModule(m_device,
|
||||||
|
nvh::loadFile("shaders/raytraceShadow.rmiss.spv", true, paths));
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<vk::PipelineShaderStageCreateInfo> stages;
|
||||||
|
|
||||||
|
// Raygen
|
||||||
|
vk::RayTracingShaderGroupCreateInfoKHR rg{vk::RayTracingShaderGroupTypeKHR::eGeneral,
|
||||||
|
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
|
||||||
|
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
|
||||||
|
stages.push_back({{}, vk::ShaderStageFlagBits::eRaygenKHR, raygenSM, "main"});
|
||||||
|
rg.setGeneralShader(static_cast<uint32_t>(stages.size() - 1));
|
||||||
|
m_rtShaderGroups.push_back(rg);
|
||||||
|
// Miss
|
||||||
|
vk::RayTracingShaderGroupCreateInfoKHR mg{vk::RayTracingShaderGroupTypeKHR::eGeneral,
|
||||||
|
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
|
||||||
|
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
|
||||||
|
stages.push_back({{}, vk::ShaderStageFlagBits::eMissKHR, missSM, "main"});
|
||||||
|
mg.setGeneralShader(static_cast<uint32_t>(stages.size() - 1));
|
||||||
|
m_rtShaderGroups.push_back(mg);
|
||||||
|
// Shadow Miss
|
||||||
|
stages.push_back({{}, vk::ShaderStageFlagBits::eMissKHR, shadowmissSM, "main"});
|
||||||
|
mg.setGeneralShader(static_cast<uint32_t>(stages.size() - 1));
|
||||||
|
m_rtShaderGroups.push_back(mg);
|
||||||
|
|
||||||
|
// Hit Group - Closest Hit + AnyHit
|
||||||
|
vk::ShaderModule chitSM =
|
||||||
|
nvvk::createShaderModule(m_device, //
|
||||||
|
nvh::loadFile("shaders/pathtrace.rchit.spv", true, paths));
|
||||||
|
|
||||||
|
vk::RayTracingShaderGroupCreateInfoKHR hg{vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
|
||||||
|
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
|
||||||
|
VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
|
||||||
|
stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, chitSM, "main"});
|
||||||
|
hg.setClosestHitShader(static_cast<uint32_t>(stages.size() - 1));
|
||||||
|
m_rtShaderGroups.push_back(hg);
|
||||||
|
|
||||||
|
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
|
||||||
|
|
||||||
|
// Push constant: we want to be able to update constants used by the shaders
|
||||||
|
vk::PushConstantRange pushConstant{vk::ShaderStageFlagBits::eRaygenKHR
|
||||||
|
| vk::ShaderStageFlagBits::eClosestHitKHR
|
||||||
|
| vk::ShaderStageFlagBits::eMissKHR,
|
||||||
|
0, sizeof(RtPushConstant)};
|
||||||
|
pipelineLayoutCreateInfo.setPushConstantRangeCount(1);
|
||||||
|
pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstant);
|
||||||
|
|
||||||
|
// Descriptor sets: one specific to ray tracing, and one shared with the rasterization pipeline
|
||||||
|
std::vector<vk::DescriptorSetLayout> rtDescSetLayouts = {m_rtDescSetLayout, m_descSetLayout};
|
||||||
|
pipelineLayoutCreateInfo.setSetLayoutCount(static_cast<uint32_t>(rtDescSetLayouts.size()));
|
||||||
|
pipelineLayoutCreateInfo.setPSetLayouts(rtDescSetLayouts.data());
|
||||||
|
|
||||||
|
m_rtPipelineLayout = m_device.createPipelineLayout(pipelineLayoutCreateInfo);
|
||||||
|
|
||||||
|
// Assemble the shader stages and recursion depth info into the ray tracing pipeline
|
||||||
|
|
||||||
|
vk::RayTracingPipelineCreateInfoKHR rayPipelineInfo;
|
||||||
|
rayPipelineInfo.setStageCount(static_cast<uint32_t>(stages.size())); // Stages are shaders
|
||||||
|
rayPipelineInfo.setPStages(stages.data());
|
||||||
|
|
||||||
|
rayPipelineInfo.setGroupCount(static_cast<uint32_t>(
|
||||||
|
m_rtShaderGroups.size())); // 1-raygen, n-miss, n-(hit[+anyhit+intersect])
|
||||||
|
rayPipelineInfo.setPGroups(m_rtShaderGroups.data());
|
||||||
|
|
||||||
|
rayPipelineInfo.setMaxRecursionDepth(2); // Ray depth
|
||||||
|
rayPipelineInfo.setLayout(m_rtPipelineLayout);
|
||||||
|
m_rtPipeline =
|
||||||
|
static_cast<const vk::Pipeline&>(m_device.createRayTracingPipelineKHR({}, rayPipelineInfo));
|
||||||
|
|
||||||
|
m_device.destroy(raygenSM);
|
||||||
|
m_device.destroy(missSM);
|
||||||
|
m_device.destroy(shadowmissSM);
|
||||||
|
m_device.destroy(chitSM);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// The Shader Binding Table (SBT)
|
||||||
|
// - getting all shader handles and writing them in a SBT buffer
|
||||||
|
// - Besides exception, this could be always done like this
|
||||||
|
// See how the SBT buffer is used in run()
|
||||||
|
//
|
||||||
|
void HelloVulkan::createRtShaderBindingTable()
|
||||||
|
{
|
||||||
|
auto groupCount =
|
||||||
|
static_cast<uint32_t>(m_rtShaderGroups.size()); // 3 shaders: raygen, miss, chit
|
||||||
|
uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier
|
||||||
|
uint32_t baseAlignment = m_rtProperties.shaderGroupBaseAlignment; // Size of shader alignment
|
||||||
|
|
||||||
|
// Fetch all the shader handles used in the pipeline, so that they can be written in the SBT
|
||||||
|
uint32_t sbtSize = groupCount * baseAlignment;
|
||||||
|
|
||||||
|
std::vector<uint8_t> shaderHandleStorage(sbtSize);
|
||||||
|
m_device.getRayTracingShaderGroupHandlesKHR(m_rtPipeline, 0, groupCount, sbtSize,
|
||||||
|
shaderHandleStorage.data());
|
||||||
|
// Write the handles in the SBT
|
||||||
|
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize, vk::BufferUsageFlagBits::eTransferSrc,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible
|
||||||
|
| vk::MemoryPropertyFlagBits::eHostCoherent);
|
||||||
|
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT").c_str());
|
||||||
|
|
||||||
|
// Write the handles in the SBT
|
||||||
|
void* mapped = m_alloc.map(m_rtSBTBuffer);
|
||||||
|
auto* pData = reinterpret_cast<uint8_t*>(mapped);
|
||||||
|
for(uint32_t g = 0; g < groupCount; g++)
|
||||||
|
{
|
||||||
|
memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize); // raygen
|
||||||
|
pData += baseAlignment;
|
||||||
|
}
|
||||||
|
m_alloc.unmap(m_rtSBTBuffer);
|
||||||
|
|
||||||
|
|
||||||
|
m_alloc.finalizeAndReleaseStaging();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Ray Tracing the scene
|
||||||
|
//
|
||||||
|
void HelloVulkan::raytrace(const vk::CommandBuffer& cmdBuf, const nvmath::vec4f& clearColor)
|
||||||
|
{
|
||||||
|
updateFrame();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
cmdBuf.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, m_rtPipeline);
|
||||||
|
cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingKHR, m_rtPipelineLayout, 0,
|
||||||
|
{m_rtDescSet, m_descSet}, {});
|
||||||
|
cmdBuf.pushConstants<RtPushConstant>(m_rtPipelineLayout,
|
||||||
|
vk::ShaderStageFlagBits::eRaygenKHR
|
||||||
|
| vk::ShaderStageFlagBits::eClosestHitKHR
|
||||||
|
| vk::ShaderStageFlagBits::eMissKHR,
|
||||||
|
0, m_rtPushConstants);
|
||||||
|
|
||||||
|
vk::DeviceSize progSize =
|
||||||
|
m_rtProperties.shaderGroupBaseAlignment; // Size of a program identifier
|
||||||
|
vk::DeviceSize rayGenOffset = 0u * progSize; // Start at the beginning of m_sbtBuffer
|
||||||
|
vk::DeviceSize missOffset = 1u * progSize; // Jump over raygen
|
||||||
|
vk::DeviceSize hitGroupOffset = 3u * progSize; // Jump over the previous shaders
|
||||||
|
|
||||||
|
vk::DeviceSize sbtSize = progSize * (vk::DeviceSize)m_rtShaderGroups.size();
|
||||||
|
|
||||||
|
const vk::StridedBufferRegionKHR raygenShaderBindingTable = {m_rtSBTBuffer.buffer, rayGenOffset,
|
||||||
|
progSize, sbtSize};
|
||||||
|
const vk::StridedBufferRegionKHR missShaderBindingTable = {m_rtSBTBuffer.buffer, missOffset,
|
||||||
|
progSize, sbtSize};
|
||||||
|
const vk::StridedBufferRegionKHR hitShaderBindingTable = {m_rtSBTBuffer.buffer, hitGroupOffset,
|
||||||
|
progSize, sbtSize};
|
||||||
|
const vk::StridedBufferRegionKHR callableShaderBindingTable;
|
||||||
|
|
||||||
|
cmdBuf.traceRaysKHR(&raygenShaderBindingTable, &missShaderBindingTable, &hitShaderBindingTable,
|
||||||
|
&callableShaderBindingTable, //
|
||||||
|
m_size.width, m_size.height, 1); //
|
||||||
|
|
||||||
|
|
||||||
|
m_debug.endLabel(cmdBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// If the camera matrix has changed, resets the frame.
|
||||||
|
// otherwise, increments frame.
|
||||||
|
//
|
||||||
|
void HelloVulkan::updateFrame()
|
||||||
|
{
|
||||||
|
static nvmath::mat4f refCamMatrix;
|
||||||
|
|
||||||
|
auto& m = CameraManip.getMatrix();
|
||||||
|
if(memcmp(&refCamMatrix.a00, &m.a00, sizeof(nvmath::mat4f)) != 0)
|
||||||
|
{
|
||||||
|
resetFrame();
|
||||||
|
refCamMatrix = m;
|
||||||
|
}
|
||||||
|
m_rtPushConstants.frame++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HelloVulkan::resetFrame()
|
||||||
|
{
|
||||||
|
m_rtPushConstants.frame = -1;
|
||||||
|
}
|
||||||
162
ray_tracing_gltf/hello_vulkan.h
Normal file
162
ray_tracing_gltf/hello_vulkan.h
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
/* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
#define NVVK_ALLOC_DEDICATED
|
||||||
|
#include "nvvk/allocator_vk.hpp"
|
||||||
|
#include "nvvk/appbase_vkpp.hpp"
|
||||||
|
#include "nvvk/debug_util_vk.hpp"
|
||||||
|
#include "nvvk/descriptorsets_vk.hpp"
|
||||||
|
|
||||||
|
// #VKRay
|
||||||
|
#include "nvh/gltfscene.hpp"
|
||||||
|
#include "nvvk/raytraceKHR_vk.hpp"
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Simple rasterizer of OBJ objects
|
||||||
|
// - Each OBJ loaded are stored in an `ObjModel` and referenced by a `ObjInstance`
|
||||||
|
// - It is possible to have many `ObjInstance` referencing the same `ObjModel`
|
||||||
|
// - Rendering is done in an offscreen framebuffer
|
||||||
|
// - The image of the framebuffer is displayed in post-process in a full-screen quad
|
||||||
|
//
|
||||||
|
class HelloVulkan : public nvvk::AppBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void setup(const vk::Instance& instance,
|
||||||
|
const vk::Device& device,
|
||||||
|
const vk::PhysicalDevice& physicalDevice,
|
||||||
|
uint32_t queueFamily) override;
|
||||||
|
void createDescriptorSetLayout();
|
||||||
|
void createGraphicsPipeline();
|
||||||
|
void loadScene(const std::string& filename);
|
||||||
|
void updateDescriptorSet();
|
||||||
|
void createUniformBuffer();
|
||||||
|
void createTextureImages(const vk::CommandBuffer& cmdBuf, tinygltf::Model& gltfModel);
|
||||||
|
void updateUniformBuffer();
|
||||||
|
void onResize(int /*w*/, int /*h*/) override;
|
||||||
|
void destroyResources();
|
||||||
|
void rasterize(const vk::CommandBuffer& cmdBuff);
|
||||||
|
|
||||||
|
// Structure used for retrieving the primitive information in the closest hit
|
||||||
|
// The gl_InstanceCustomIndexNV
|
||||||
|
struct RtPrimitiveLookup
|
||||||
|
{
|
||||||
|
uint32_t indexOffset;
|
||||||
|
uint32_t vertexOffset;
|
||||||
|
int materialIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
nvh::GltfScene m_gltfScene;
|
||||||
|
nvvk::Buffer m_vertexBuffer;
|
||||||
|
nvvk::Buffer m_normalBuffer;
|
||||||
|
nvvk::Buffer m_uvBuffer;
|
||||||
|
nvvk::Buffer m_indexBuffer;
|
||||||
|
nvvk::Buffer m_materialBuffer;
|
||||||
|
nvvk::Buffer m_matrixBuffer;
|
||||||
|
nvvk::Buffer m_rtPrimLookup;
|
||||||
|
|
||||||
|
// Information pushed at each draw call
|
||||||
|
struct ObjPushConstant
|
||||||
|
{
|
||||||
|
nvmath::vec3f lightPosition{0.f, 4.5f, 0.f};
|
||||||
|
int instanceId{0}; // To retrieve the transformation matrix
|
||||||
|
float lightIntensity{10.f};
|
||||||
|
int lightType{0}; // 0: point, 1: infinite
|
||||||
|
int materialId{0};
|
||||||
|
};
|
||||||
|
ObjPushConstant m_pushConstant;
|
||||||
|
|
||||||
|
// Graphic pipeline
|
||||||
|
vk::PipelineLayout m_pipelineLayout;
|
||||||
|
vk::Pipeline m_graphicsPipeline;
|
||||||
|
nvvk::DescriptorSetBindings m_descSetLayoutBind;
|
||||||
|
vk::DescriptorPool m_descPool;
|
||||||
|
vk::DescriptorSetLayout m_descSetLayout;
|
||||||
|
vk::DescriptorSet m_descSet;
|
||||||
|
|
||||||
|
nvvk::Buffer m_cameraMat; // Device-Host of the camera matrices
|
||||||
|
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
|
||||||
|
|
||||||
|
nvvk::AllocatorDedicated m_alloc; // Allocator for buffer, images, acceleration structures
|
||||||
|
nvvk::DebugUtil m_debug; // Utility to name objects
|
||||||
|
|
||||||
|
// #Post
|
||||||
|
void createOffscreenRender();
|
||||||
|
void createPostPipeline();
|
||||||
|
void createPostDescriptor();
|
||||||
|
void updatePostDescriptorSet();
|
||||||
|
void drawPost(vk::CommandBuffer cmdBuf);
|
||||||
|
|
||||||
|
nvvk::DescriptorSetBindings m_postDescSetLayoutBind;
|
||||||
|
vk::DescriptorPool m_postDescPool;
|
||||||
|
vk::DescriptorSetLayout m_postDescSetLayout;
|
||||||
|
vk::DescriptorSet m_postDescSet;
|
||||||
|
vk::Pipeline m_postPipeline;
|
||||||
|
vk::PipelineLayout m_postPipelineLayout;
|
||||||
|
vk::RenderPass m_offscreenRenderPass;
|
||||||
|
vk::Framebuffer m_offscreenFramebuffer;
|
||||||
|
nvvk::Texture m_offscreenColor;
|
||||||
|
vk::Format m_offscreenColorFormat{vk::Format::eR32G32B32A32Sfloat};
|
||||||
|
nvvk::Texture m_offscreenDepth;
|
||||||
|
vk::Format m_offscreenDepthFormat{vk::Format::eD32Sfloat};
|
||||||
|
|
||||||
|
// #VKRay
|
||||||
|
nvvk::RaytracingBuilderKHR::Blas primitiveToGeometry(const nvh::GltfPrimMesh& prim);
|
||||||
|
|
||||||
|
void initRayTracing();
|
||||||
|
void createBottomLevelAS();
|
||||||
|
void createTopLevelAS();
|
||||||
|
void createRtDescriptorSet();
|
||||||
|
void updateRtDescriptorSet();
|
||||||
|
void createRtPipeline();
|
||||||
|
void createRtShaderBindingTable();
|
||||||
|
void raytrace(const vk::CommandBuffer& cmdBuf, const nvmath::vec4f& clearColor);
|
||||||
|
void updateFrame();
|
||||||
|
void resetFrame();
|
||||||
|
|
||||||
|
vk::PhysicalDeviceRayTracingPropertiesKHR m_rtProperties;
|
||||||
|
nvvk::RaytracingBuilderKHR m_rtBuilder;
|
||||||
|
nvvk::DescriptorSetBindings m_rtDescSetLayoutBind;
|
||||||
|
vk::DescriptorPool m_rtDescPool;
|
||||||
|
vk::DescriptorSetLayout m_rtDescSetLayout;
|
||||||
|
vk::DescriptorSet m_rtDescSet;
|
||||||
|
std::vector<vk::RayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
|
||||||
|
vk::PipelineLayout m_rtPipelineLayout;
|
||||||
|
vk::Pipeline m_rtPipeline;
|
||||||
|
nvvk::Buffer m_rtSBTBuffer;
|
||||||
|
|
||||||
|
struct RtPushConstant
|
||||||
|
{
|
||||||
|
nvmath::vec4f clearColor;
|
||||||
|
nvmath::vec3f lightPosition;
|
||||||
|
float lightIntensity;
|
||||||
|
int lightType;
|
||||||
|
int frame{0};
|
||||||
|
} m_rtPushConstants;
|
||||||
|
};
|
||||||
302
ray_tracing_gltf/main.cpp
Normal file
302
ray_tracing_gltf/main.cpp
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
/* Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ImGui - standalone example application for Glfw + Vulkan, using programmable
|
||||||
|
// pipeline If you are new to ImGui, see examples/README.txt and documentation
|
||||||
|
// at the top of imgui.cpp.
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_glfw.h"
|
||||||
|
|
||||||
|
#include "hello_vulkan.h"
|
||||||
|
#include "nvh/cameramanipulator.hpp"
|
||||||
|
#include "nvh/fileoperations.hpp"
|
||||||
|
#include "nvpsystem.hpp"
|
||||||
|
#include "nvvk/appbase_vkpp.hpp"
|
||||||
|
#include "nvvk/commands_vk.hpp"
|
||||||
|
#include "nvvk/context_vk.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Default search path for shaders
|
||||||
|
std::vector<std::string> defaultSearchPaths;
|
||||||
|
|
||||||
|
// GLFW Callback functions
|
||||||
|
static void onErrorCallback(int error, const char* description)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra UI
|
||||||
|
void renderUI(HelloVulkan& helloVk)
|
||||||
|
{
|
||||||
|
static int item = 1;
|
||||||
|
if(ImGui::Combo("Up Vector", &item, "X\0Y\0Z\0\0"))
|
||||||
|
{
|
||||||
|
nvmath::vec3f pos, eye, up;
|
||||||
|
CameraManip.getLookat(pos, eye, up);
|
||||||
|
up = nvmath::vec3f(item == 0, item == 1, item == 2);
|
||||||
|
CameraManip.setLookat(pos, eye, up);
|
||||||
|
}
|
||||||
|
ImGui::SliderFloat3("Light Position", &helloVk.m_pushConstant.lightPosition.x, -20.f, 20.f);
|
||||||
|
ImGui::SliderFloat("Light Intensity", &helloVk.m_pushConstant.lightIntensity, 0.f, 100.f);
|
||||||
|
ImGui::RadioButton("Point", &helloVk.m_pushConstant.lightType, 0);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::RadioButton("Infinite", &helloVk.m_pushConstant.lightType, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
static int const SAMPLE_WIDTH = 1280;
|
||||||
|
static int const SAMPLE_HEIGHT = 720;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------
|
||||||
|
// Application Entry
|
||||||
|
//
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
UNUSED(argc);
|
||||||
|
|
||||||
|
// Setup GLFW window
|
||||||
|
glfwSetErrorCallback(onErrorCallback);
|
||||||
|
if(!glfwInit())
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
GLFWwindow* window = glfwCreateWindow(SAMPLE_WIDTH, SAMPLE_HEIGHT,
|
||||||
|
"NVIDIA Vulkan Raytracing Tutorial", nullptr, nullptr);
|
||||||
|
|
||||||
|
// Setup camera
|
||||||
|
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
|
||||||
|
CameraManip.setLookat(nvmath::vec3f(0, 0, 15), nvmath::vec3f(0, 0, 0), nvmath::vec3f(0, 1, 0));
|
||||||
|
|
||||||
|
// Setup Vulkan
|
||||||
|
if(!glfwVulkanSupported())
|
||||||
|
{
|
||||||
|
printf("GLFW: Vulkan Not Supported\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup some basic things for the sample, logging file for example
|
||||||
|
NVPSystem system(argv[0], PROJECT_NAME);
|
||||||
|
|
||||||
|
// Search path for shaders and other media
|
||||||
|
defaultSearchPaths = {
|
||||||
|
PROJECT_ABSDIRECTORY,
|
||||||
|
PROJECT_ABSDIRECTORY "../",
|
||||||
|
NVPSystem::exePath() + std::string(PROJECT_RELDIRECTORY),
|
||||||
|
NVPSystem::exePath() + std::string(PROJECT_RELDIRECTORY) + std::string("../"),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Requesting Vulkan extensions and layers
|
||||||
|
nvvk::ContextCreateInfo contextInfo(true);
|
||||||
|
contextInfo.setVersion(1, 2);
|
||||||
|
contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true);
|
||||||
|
contextInfo.addInstanceExtension(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
|
#ifdef WIN32
|
||||||
|
contextInfo.addInstanceExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||||
|
#else
|
||||||
|
contextInfo.addInstanceExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||||
|
contextInfo.addInstanceExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
|
||||||
|
#endif
|
||||||
|
contextInfo.addInstanceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||||
|
// #VKRay: Activate the ray tracing extension
|
||||||
|
vk::PhysicalDeviceRayTracingFeaturesKHR raytracingFeature;
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_EXTENSION_NAME, false, &raytracingFeature);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_MAINTENANCE3_EXTENSION_NAME);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
|
||||||
|
contextInfo.addDeviceExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
|
||||||
|
|
||||||
|
|
||||||
|
// Creating Vulkan base application
|
||||||
|
nvvk::Context vkctx{};
|
||||||
|
vkctx.initInstance(contextInfo);
|
||||||
|
// Find all compatible devices
|
||||||
|
auto compatibleDevices = vkctx.getCompatibleDevices(contextInfo);
|
||||||
|
assert(!compatibleDevices.empty());
|
||||||
|
// Use a compatible device
|
||||||
|
vkctx.initDevice(compatibleDevices[0], contextInfo);
|
||||||
|
|
||||||
|
|
||||||
|
// Create example
|
||||||
|
HelloVulkan helloVk;
|
||||||
|
|
||||||
|
// Window need to be opened to get the surface on which to draw
|
||||||
|
const vk::SurfaceKHR surface = helloVk.getVkSurface(vkctx.m_instance, window);
|
||||||
|
vkctx.setGCTQueueWithPresent(surface);
|
||||||
|
|
||||||
|
helloVk.setup(vkctx.m_instance, vkctx.m_device, vkctx.m_physicalDevice,
|
||||||
|
vkctx.m_queueGCT.familyIndex);
|
||||||
|
helloVk.createSurface(surface, SAMPLE_WIDTH, SAMPLE_HEIGHT);
|
||||||
|
helloVk.createDepthBuffer();
|
||||||
|
helloVk.createRenderPass();
|
||||||
|
helloVk.createFrameBuffers();
|
||||||
|
|
||||||
|
// Setup Imgui
|
||||||
|
helloVk.initGUI(0); // Using sub-pass 0
|
||||||
|
|
||||||
|
// Creation of the example
|
||||||
|
helloVk.loadScene(nvh::findFile("media/scenes/cornellBox.gltf", defaultSearchPaths));
|
||||||
|
|
||||||
|
|
||||||
|
helloVk.createOffscreenRender();
|
||||||
|
helloVk.createDescriptorSetLayout();
|
||||||
|
helloVk.createGraphicsPipeline();
|
||||||
|
helloVk.createUniformBuffer();
|
||||||
|
helloVk.updateDescriptorSet();
|
||||||
|
|
||||||
|
// #VKRay
|
||||||
|
helloVk.initRayTracing();
|
||||||
|
helloVk.createBottomLevelAS();
|
||||||
|
helloVk.createTopLevelAS();
|
||||||
|
helloVk.createRtDescriptorSet();
|
||||||
|
helloVk.createRtPipeline();
|
||||||
|
helloVk.createRtShaderBindingTable();
|
||||||
|
|
||||||
|
helloVk.createPostDescriptor();
|
||||||
|
helloVk.createPostPipeline();
|
||||||
|
helloVk.updatePostDescriptorSet();
|
||||||
|
|
||||||
|
|
||||||
|
nvmath::vec4f clearColor = nvmath::vec4f(1, 1, 1, 1.00f);
|
||||||
|
bool useRaytracer = true;
|
||||||
|
|
||||||
|
|
||||||
|
helloVk.setupGlfwCallbacks(window);
|
||||||
|
ImGui_ImplGlfw_InitForVulkan(window, true);
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while(!glfwWindowShouldClose(window))
|
||||||
|
{
|
||||||
|
glfwPollEvents();
|
||||||
|
if(helloVk.isMinimized())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Start the Dear ImGui frame
|
||||||
|
ImGui_ImplGlfw_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
// Updating camera buffer
|
||||||
|
helloVk.updateUniformBuffer();
|
||||||
|
|
||||||
|
// Show UI window.
|
||||||
|
if(1 == 1)
|
||||||
|
{
|
||||||
|
ImGui::ColorEdit3("Clear color", reinterpret_cast<float*>(&clearColor));
|
||||||
|
ImGui::Checkbox("Ray Tracer mode", &useRaytracer); // Switch between raster and ray tracing
|
||||||
|
|
||||||
|
renderUI(helloVk);
|
||||||
|
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
|
||||||
|
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||||
|
ImGui::Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start rendering the scene
|
||||||
|
helloVk.prepareFrame();
|
||||||
|
|
||||||
|
// Start command buffer of this frame
|
||||||
|
auto curFrame = helloVk.getCurFrame();
|
||||||
|
const vk::CommandBuffer& cmdBuff = helloVk.getCommandBuffers()[curFrame];
|
||||||
|
|
||||||
|
cmdBuff.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||||||
|
|
||||||
|
// Clearing screen
|
||||||
|
vk::ClearValue clearValues[2];
|
||||||
|
clearValues[0].setColor(
|
||||||
|
std::array<float, 4>({clearColor[0], clearColor[1], clearColor[2], clearColor[3]}));
|
||||||
|
clearValues[1].setDepthStencil({1.0f, 0});
|
||||||
|
|
||||||
|
// Offscreen render pass
|
||||||
|
{
|
||||||
|
vk::RenderPassBeginInfo offscreenRenderPassBeginInfo;
|
||||||
|
offscreenRenderPassBeginInfo.setClearValueCount(2);
|
||||||
|
offscreenRenderPassBeginInfo.setPClearValues(clearValues);
|
||||||
|
offscreenRenderPassBeginInfo.setRenderPass(helloVk.m_offscreenRenderPass);
|
||||||
|
offscreenRenderPassBeginInfo.setFramebuffer(helloVk.m_offscreenFramebuffer);
|
||||||
|
offscreenRenderPassBeginInfo.setRenderArea({{}, helloVk.getSize()});
|
||||||
|
|
||||||
|
// Rendering Scene
|
||||||
|
if(useRaytracer)
|
||||||
|
{
|
||||||
|
helloVk.raytrace(cmdBuff, clearColor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cmdBuff.beginRenderPass(offscreenRenderPassBeginInfo, vk::SubpassContents::eInline);
|
||||||
|
helloVk.rasterize(cmdBuff);
|
||||||
|
cmdBuff.endRenderPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd rendering pass: tone mapper, UI
|
||||||
|
{
|
||||||
|
vk::RenderPassBeginInfo postRenderPassBeginInfo;
|
||||||
|
postRenderPassBeginInfo.setClearValueCount(2);
|
||||||
|
postRenderPassBeginInfo.setPClearValues(clearValues);
|
||||||
|
postRenderPassBeginInfo.setRenderPass(helloVk.getRenderPass());
|
||||||
|
postRenderPassBeginInfo.setFramebuffer(helloVk.getFramebuffers()[curFrame]);
|
||||||
|
postRenderPassBeginInfo.setRenderArea({{}, helloVk.getSize()});
|
||||||
|
|
||||||
|
cmdBuff.beginRenderPass(postRenderPassBeginInfo, vk::SubpassContents::eInline);
|
||||||
|
// Rendering tonemapper
|
||||||
|
helloVk.drawPost(cmdBuff);
|
||||||
|
// Rendering UI
|
||||||
|
ImGui::RenderDrawDataVK(cmdBuff, ImGui::GetDrawData());
|
||||||
|
cmdBuff.endRenderPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit for display
|
||||||
|
cmdBuff.end();
|
||||||
|
helloVk.submitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
helloVk.getDevice().waitIdle();
|
||||||
|
helloVk.destroyResources();
|
||||||
|
helloVk.destroy();
|
||||||
|
|
||||||
|
vkctx.deinit();
|
||||||
|
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
glfwTerminate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
10
ray_tracing_gltf/shaders/binding.glsl
Normal file
10
ray_tracing_gltf/shaders/binding.glsl
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
|
||||||
|
#define B_CAMERA 0
|
||||||
|
#define B_VERTICES 1
|
||||||
|
#define B_NORMALS 2
|
||||||
|
#define B_TEXCOORDS 3
|
||||||
|
#define B_INDICES 4
|
||||||
|
#define B_MATERIALS 5
|
||||||
|
#define B_MATRICES 6
|
||||||
|
#define B_TEXTURES 7
|
||||||
74
ray_tracing_gltf/shaders/frag_shader.frag
Normal file
74
ray_tracing_gltf/shaders/frag_shader.frag
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
#extension GL_EXT_nonuniform_qualifier : enable
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
#extension GL_EXT_scalar_block_layout : enable
|
||||||
|
|
||||||
|
#include "binding.glsl"
|
||||||
|
#include "gltf.glsl"
|
||||||
|
|
||||||
|
|
||||||
|
layout(push_constant) uniform shaderInformation
|
||||||
|
{
|
||||||
|
vec3 lightPosition;
|
||||||
|
uint instanceId;
|
||||||
|
float lightIntensity;
|
||||||
|
int lightType;
|
||||||
|
int matetrialId;
|
||||||
|
}
|
||||||
|
pushC;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// Incoming
|
||||||
|
//layout(location = 0) flat in int matIndex;
|
||||||
|
layout(location = 1) in vec2 fragTexCoord;
|
||||||
|
layout(location = 2) in vec3 fragNormal;
|
||||||
|
layout(location = 3) in vec3 viewDir;
|
||||||
|
layout(location = 4) in vec3 worldPos;
|
||||||
|
// Outgoing
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
// Buffers
|
||||||
|
layout(set = 0, binding = B_MATERIALS) buffer _GltfMaterial { GltfMaterial materials[]; };
|
||||||
|
layout(set = 0, binding = B_TEXTURES) uniform sampler2D[] textureSamplers;
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Material of the object
|
||||||
|
GltfMaterial mat = materials[nonuniformEXT(pushC.matetrialId)];
|
||||||
|
|
||||||
|
vec3 N = normalize(fragNormal);
|
||||||
|
|
||||||
|
// Vector toward light
|
||||||
|
vec3 L;
|
||||||
|
float lightIntensity = pushC.lightIntensity;
|
||||||
|
if(pushC.lightType == 0)
|
||||||
|
{
|
||||||
|
vec3 lDir = pushC.lightPosition - worldPos;
|
||||||
|
float d = length(lDir);
|
||||||
|
lightIntensity = pushC.lightIntensity / (d * d);
|
||||||
|
L = normalize(lDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
L = normalize(pushC.lightPosition - vec3(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Diffuse
|
||||||
|
vec3 diffuse = computeDiffuse(mat, L, N);
|
||||||
|
if(mat.pbrBaseColorTexture > -1)
|
||||||
|
{
|
||||||
|
uint txtId = mat.pbrBaseColorTexture;
|
||||||
|
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], fragTexCoord).xyz;
|
||||||
|
diffuse *= diffuseTxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specular
|
||||||
|
vec3 specular = computeSpecular(mat, viewDir, L, N);
|
||||||
|
|
||||||
|
// Result
|
||||||
|
outColor = vec4(lightIntensity * (diffuse + specular), 1);
|
||||||
|
}
|
||||||
60
ray_tracing_gltf/shaders/gltf.glsl
Normal file
60
ray_tracing_gltf/shaders/gltf.glsl
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
struct GltfMaterial
|
||||||
|
{
|
||||||
|
int shadingModel; // 0: metallic-roughness, 1: specular-glossiness
|
||||||
|
|
||||||
|
// PbrMetallicRoughness
|
||||||
|
vec4 pbrBaseColorFactor;
|
||||||
|
int pbrBaseColorTexture;
|
||||||
|
float pbrMetallicFactor;
|
||||||
|
float pbrRoughnessFactor;
|
||||||
|
int pbrMetallicRoughnessTexture;
|
||||||
|
|
||||||
|
// KHR_materials_pbrSpecularGlossiness
|
||||||
|
vec4 khrDiffuseFactor;
|
||||||
|
int khrDiffuseTexture;
|
||||||
|
vec3 khrSpecularFactor;
|
||||||
|
float khrGlossinessFactor;
|
||||||
|
int khrSpecularGlossinessTexture;
|
||||||
|
|
||||||
|
int emissiveTexture;
|
||||||
|
vec3 emissiveFactor;
|
||||||
|
int alphaMode;
|
||||||
|
float alphaCutoff;
|
||||||
|
bool doubleSided;
|
||||||
|
|
||||||
|
int normalTexture;
|
||||||
|
float normalTextureScale;
|
||||||
|
int occlusionTexture;
|
||||||
|
float occlusionTextureStrength;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PrimMeshInfo
|
||||||
|
{
|
||||||
|
uint indexOffset;
|
||||||
|
uint vertexOffset;
|
||||||
|
int materialIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
vec3 computeDiffuse(GltfMaterial mat, vec3 lightDir, vec3 normal)
|
||||||
|
{
|
||||||
|
// Lambertian
|
||||||
|
float dotNL = max(dot(normal, lightDir), 0.0);
|
||||||
|
return mat.pbrBaseColorFactor.xyz * dotNL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 computeSpecular(GltfMaterial mat, vec3 viewDir, vec3 lightDir, vec3 normal)
|
||||||
|
{
|
||||||
|
// Compute specular only if not in shadow
|
||||||
|
const float kPi = 3.14159265;
|
||||||
|
const float kShininess = 60.0;
|
||||||
|
|
||||||
|
// Specular
|
||||||
|
const float kEnergyConservation = (2.0 + kShininess) / (2.0 * kPi);
|
||||||
|
vec3 V = normalize(-viewDir);
|
||||||
|
vec3 R = reflect(-lightDir, normal);
|
||||||
|
float specular = kEnergyConservation * pow(max(dot(V, R), 0.0), kShininess);
|
||||||
|
|
||||||
|
return vec3(specular);
|
||||||
|
}
|
||||||
15
ray_tracing_gltf/shaders/passthrough.vert
Normal file
15
ray_tracing_gltf/shaders/passthrough.vert
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#version 450
|
||||||
|
layout (location = 0) out vec2 outUV;
|
||||||
|
|
||||||
|
|
||||||
|
out gl_PerVertex
|
||||||
|
{
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||||
|
gl_Position = vec4(outUV * 2.0f - 1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
162
ray_tracing_gltf/shaders/pathtrace.rchit
Normal file
162
ray_tracing_gltf/shaders/pathtrace.rchit
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
#extension GL_EXT_nonuniform_qualifier : enable
|
||||||
|
#extension GL_EXT_scalar_block_layout : enable
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
|
#include "binding.glsl"
|
||||||
|
#include "gltf.glsl"
|
||||||
|
#include "raycommon.glsl"
|
||||||
|
#include "sampling.glsl"
|
||||||
|
|
||||||
|
|
||||||
|
hitAttributeEXT vec2 attribs;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
layout(location = 0) rayPayloadInEXT hitPayload prd;
|
||||||
|
layout(location = 1) rayPayloadEXT bool isShadowed;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0 ) uniform accelerationStructureEXT topLevelAS;
|
||||||
|
layout(set = 0, binding = 2) readonly buffer _InstanceInfo {PrimMeshInfo primInfo[];};
|
||||||
|
|
||||||
|
layout(set = 1, binding = B_VERTICES) readonly buffer _VertexBuf {float vertices[];};
|
||||||
|
layout(set = 1, binding = B_INDICES) readonly buffer _Indices {uint indices[];};
|
||||||
|
layout(set = 1, binding = B_NORMALS) readonly buffer _NormalBuf {float normals[];};
|
||||||
|
layout(set = 1, binding = B_TEXCOORDS) readonly buffer _TexCoordBuf {float texcoord0[];};
|
||||||
|
layout(set = 1, binding = B_MATERIALS) readonly buffer _MaterialBuffer {GltfMaterial materials[];};
|
||||||
|
layout(set = 1, binding = B_TEXTURES) uniform sampler2D texturesMap[]; // all textures
|
||||||
|
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
layout(push_constant) uniform Constants
|
||||||
|
{
|
||||||
|
vec4 clearColor;
|
||||||
|
vec3 lightPosition;
|
||||||
|
float lightIntensity;
|
||||||
|
int lightType;
|
||||||
|
}
|
||||||
|
pushC;
|
||||||
|
|
||||||
|
// Return the vertex position
|
||||||
|
vec3 getVertex(uint index)
|
||||||
|
{
|
||||||
|
vec3 vp;
|
||||||
|
vp.x = vertices[3 * index + 0];
|
||||||
|
vp.y = vertices[3 * index + 1];
|
||||||
|
vp.z = vertices[3 * index + 2];
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getNormal(uint index)
|
||||||
|
{
|
||||||
|
vec3 vp;
|
||||||
|
vp.x = normals[3 * index + 0];
|
||||||
|
vp.y = normals[3 * index + 1];
|
||||||
|
vp.z = normals[3 * index + 2];
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getTexCoord(uint index)
|
||||||
|
{
|
||||||
|
vec2 vp;
|
||||||
|
vp.x = texcoord0[2 * index + 0];
|
||||||
|
vp.y = texcoord0[2 * index + 1];
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Retrieve the Primitive mesh buffer information
|
||||||
|
PrimMeshInfo pinfo = primInfo[gl_InstanceCustomIndexEXT];
|
||||||
|
|
||||||
|
// Getting the 'first index' for this mesh (offset of the mesh + offset of the triangle)
|
||||||
|
uint indexOffset = pinfo.indexOffset + (3 * gl_PrimitiveID);
|
||||||
|
uint vertexOffset = pinfo.vertexOffset; // Vertex offset as defined in glTF
|
||||||
|
uint matIndex = max(0, pinfo.materialIndex); // material of primitive mesh
|
||||||
|
|
||||||
|
// Getting the 3 indices of the triangle (local)
|
||||||
|
ivec3 triangleIndex = ivec3(indices[nonuniformEXT(indexOffset + 0)], //
|
||||||
|
indices[nonuniformEXT(indexOffset + 1)], //
|
||||||
|
indices[nonuniformEXT(indexOffset + 2)]);
|
||||||
|
triangleIndex += ivec3(vertexOffset); // (global)
|
||||||
|
|
||||||
|
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
|
||||||
|
|
||||||
|
// Vertex of the triangle
|
||||||
|
const vec3 pos0 = getVertex(triangleIndex.x);
|
||||||
|
const vec3 pos1 = getVertex(triangleIndex.y);
|
||||||
|
const vec3 pos2 = getVertex(triangleIndex.z);
|
||||||
|
const vec3 position = pos0 * barycentrics.x + pos1 * barycentrics.y + pos2 * barycentrics.z;
|
||||||
|
const vec3 world_position = vec3(gl_ObjectToWorldEXT * vec4(position, 1.0));
|
||||||
|
|
||||||
|
// Normal
|
||||||
|
const vec3 nrm0 = getNormal(triangleIndex.x);
|
||||||
|
const vec3 nrm1 = getNormal(triangleIndex.y);
|
||||||
|
const vec3 nrm2 = getNormal(triangleIndex.z);
|
||||||
|
vec3 normal = normalize(nrm0 * barycentrics.x + nrm1 * barycentrics.y + nrm2 * barycentrics.z);
|
||||||
|
const vec3 world_normal = normalize(vec3(normal * gl_WorldToObjectEXT));
|
||||||
|
const vec3 geom_normal = normalize(cross(pos1 - pos0, pos2 - pos0));
|
||||||
|
|
||||||
|
// TexCoord
|
||||||
|
const vec2 uv0 = getTexCoord(triangleIndex.x);
|
||||||
|
const vec2 uv1 = getTexCoord(triangleIndex.y);
|
||||||
|
const vec2 uv2 = getTexCoord(triangleIndex.z);
|
||||||
|
const vec2 texcoord0 = uv0 * barycentrics.x + uv1 * barycentrics.y + uv2 * barycentrics.z;
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Path_tracing
|
||||||
|
// Material of the object
|
||||||
|
GltfMaterial mat = materials[nonuniformEXT(matIndex)];
|
||||||
|
vec3 emittance = mat.emissiveFactor;
|
||||||
|
|
||||||
|
// Pick a random direction from here and keep going.
|
||||||
|
vec3 tangent, bitangent;
|
||||||
|
createCoordinateSystem(world_normal, tangent, bitangent);
|
||||||
|
vec3 rayOrigin = world_position;
|
||||||
|
vec3 rayDirection = samplingHemisphere(prd.seed, tangent, bitangent, world_normal);
|
||||||
|
|
||||||
|
// Probability of the newRay (cosine distributed)
|
||||||
|
const float p = 1 / M_PI;
|
||||||
|
|
||||||
|
// Compute the BRDF for this ray (assuming Lambertian reflection)
|
||||||
|
float cos_theta = dot(rayDirection, world_normal);
|
||||||
|
vec3 albedo = mat.pbrBaseColorFactor.xyz;
|
||||||
|
if(mat.pbrBaseColorTexture > -1)
|
||||||
|
{
|
||||||
|
uint txtId = mat.pbrBaseColorTexture;
|
||||||
|
albedo *= texture(texturesMap[nonuniformEXT(txtId)], texcoord0).xyz;
|
||||||
|
}
|
||||||
|
vec3 BRDF = albedo / M_PI;
|
||||||
|
|
||||||
|
prd.rayOrigin = rayOrigin;
|
||||||
|
prd.rayDirection = rayDirection;
|
||||||
|
prd.hitValue = emittance;
|
||||||
|
prd.weight = BRDF * cos_theta / p;
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Recursively trace reflected light sources.
|
||||||
|
if(prd.depth < 10)
|
||||||
|
{
|
||||||
|
prd.depth++;
|
||||||
|
float tMin = 0.001;
|
||||||
|
float tMax = 100000000.0;
|
||||||
|
uint flags = gl_RayFlagsOpaqueEXT;
|
||||||
|
traceRayEXT(topLevelAS, // acceleration structure
|
||||||
|
flags, // rayFlags
|
||||||
|
0xFF, // cullMask
|
||||||
|
0, // sbtRecordOffset
|
||||||
|
0, // sbtRecordStride
|
||||||
|
0, // missIndex
|
||||||
|
rayOrigin, // ray origin
|
||||||
|
tMin, // ray min range
|
||||||
|
rayDirection, // ray direction
|
||||||
|
tMax, // ray max range
|
||||||
|
0 // payload (location = 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
vec3 incoming = prd.hitValue;
|
||||||
|
|
||||||
|
// Apply the Rendering Equation here.
|
||||||
|
prd.hitValue = emittance + (BRDF * incoming * cos_theta / p);
|
||||||
|
}
|
||||||
93
ray_tracing_gltf/shaders/pathtrace.rgen
Normal file
93
ray_tracing_gltf/shaders/pathtrace.rgen
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
#extension GL_ARB_shader_clock : enable
|
||||||
|
|
||||||
|
|
||||||
|
#include "binding.glsl"
|
||||||
|
#include "raycommon.glsl"
|
||||||
|
#include "sampling.glsl"
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform accelerationStructureEXT topLevelAS;
|
||||||
|
layout(set = 0, binding = 1, rgba32f) uniform image2D image;
|
||||||
|
|
||||||
|
layout(location = 0) rayPayloadEXT hitPayload prd;
|
||||||
|
|
||||||
|
layout(set = 1, binding = B_CAMERA) uniform CameraProperties
|
||||||
|
{
|
||||||
|
mat4 view;
|
||||||
|
mat4 proj;
|
||||||
|
mat4 viewInverse;
|
||||||
|
mat4 projInverse;
|
||||||
|
}
|
||||||
|
cam;
|
||||||
|
|
||||||
|
layout(push_constant) uniform Constants
|
||||||
|
{
|
||||||
|
vec4 clearColor;
|
||||||
|
vec3 lightPosition;
|
||||||
|
float lightIntensity;
|
||||||
|
int lightType;
|
||||||
|
int frame;
|
||||||
|
}
|
||||||
|
pushC;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Initialize the random number
|
||||||
|
uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, int(clockARB()));
|
||||||
|
|
||||||
|
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
|
||||||
|
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);
|
||||||
|
|
||||||
|
uint rayFlags = gl_RayFlagsOpaqueEXT;
|
||||||
|
float tMin = 0.001;
|
||||||
|
float tMax = 10000.0;
|
||||||
|
|
||||||
|
prd.hitValue = vec3(0);
|
||||||
|
prd.seed = seed;
|
||||||
|
prd.depth = 0;
|
||||||
|
prd.rayOrigin = origin.xyz;
|
||||||
|
prd.rayDirection = direction.xyz;
|
||||||
|
prd.weight = vec3(0);
|
||||||
|
|
||||||
|
vec3 curWeight = vec3(1);
|
||||||
|
vec3 hitValue = vec3(0);
|
||||||
|
|
||||||
|
for(; prd.depth < 10; prd.depth++)
|
||||||
|
{
|
||||||
|
traceRayEXT(topLevelAS, // acceleration structure
|
||||||
|
rayFlags, // rayFlags
|
||||||
|
0xFF, // cullMask
|
||||||
|
0, // sbtRecordOffset
|
||||||
|
0, // sbtRecordStride
|
||||||
|
0, // missIndex
|
||||||
|
prd.rayOrigin, // ray origin
|
||||||
|
tMin, // ray min range
|
||||||
|
prd.rayDirection, // ray direction
|
||||||
|
tMax, // ray max range
|
||||||
|
0 // payload (location = 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
hitValue += prd.hitValue * curWeight;
|
||||||
|
curWeight *= prd.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do accumulation over time
|
||||||
|
if(pushC.frame > 0)
|
||||||
|
{
|
||||||
|
float a = 1.0f / float(pushC.frame + 1);
|
||||||
|
vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz;
|
||||||
|
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(mix(old_color, hitValue, a), 1.f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First frame, replace the value in the buffer
|
||||||
|
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 1.f));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
ray_tracing_gltf/shaders/pathtrace.rmiss
Normal file
20
ray_tracing_gltf/shaders/pathtrace.rmiss
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
#include "raycommon.glsl"
|
||||||
|
|
||||||
|
layout(location = 0) rayPayloadInEXT hitPayload prd;
|
||||||
|
|
||||||
|
layout(push_constant) uniform Constants
|
||||||
|
{
|
||||||
|
vec4 clearColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if(prd.depth == 0)
|
||||||
|
prd.hitValue = clearColor.xyz * 0.8;
|
||||||
|
else
|
||||||
|
prd.hitValue = vec3(0.01); // No contribution from environment
|
||||||
|
prd.depth = 100; // Ending trace
|
||||||
|
}
|
||||||
18
ray_tracing_gltf/shaders/post.frag
Normal file
18
ray_tracing_gltf/shaders/post.frag
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#version 450
|
||||||
|
layout(location = 0) in vec2 outUV;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform sampler2D noisyTxt;
|
||||||
|
|
||||||
|
layout(push_constant) uniform shaderInformation
|
||||||
|
{
|
||||||
|
float aspectRatio;
|
||||||
|
}
|
||||||
|
pushc;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 uv = outUV;
|
||||||
|
float gamma = 1. / 2.2;
|
||||||
|
fragColor = pow(texture(noisyTxt, uv).rgba, vec4(gamma));
|
||||||
|
}
|
||||||
9
ray_tracing_gltf/shaders/raycommon.glsl
Normal file
9
ray_tracing_gltf/shaders/raycommon.glsl
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
struct hitPayload
|
||||||
|
{
|
||||||
|
vec3 hitValue;
|
||||||
|
uint seed;
|
||||||
|
uint depth;
|
||||||
|
vec3 rayOrigin;
|
||||||
|
vec3 rayDirection;
|
||||||
|
vec3 weight;
|
||||||
|
};
|
||||||
172
ray_tracing_gltf/shaders/raytrace.rchit
Normal file
172
ray_tracing_gltf/shaders/raytrace.rchit
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
#extension GL_EXT_nonuniform_qualifier : enable
|
||||||
|
#extension GL_EXT_scalar_block_layout : enable
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
|
#include "binding.glsl"
|
||||||
|
#include "gltf.glsl"
|
||||||
|
#include "raycommon.glsl"
|
||||||
|
|
||||||
|
hitAttributeEXT vec2 attribs;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
layout(location = 0) rayPayloadInEXT hitPayload prd;
|
||||||
|
layout(location = 1) rayPayloadEXT bool isShadowed;
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0 ) uniform accelerationStructureEXT topLevelAS;
|
||||||
|
layout(set = 0, binding = 2) readonly buffer _InstanceInfo {PrimMeshInfo primInfo[];};
|
||||||
|
|
||||||
|
layout(set = 1, binding = B_VERTICES) readonly buffer _VertexBuf {float vertices[];};
|
||||||
|
layout(set = 1, binding = B_INDICES) readonly buffer _Indices {uint indices[];};
|
||||||
|
layout(set = 1, binding = B_NORMALS) readonly buffer _NormalBuf {float normals[];};
|
||||||
|
layout(set = 1, binding = B_TEXCOORDS) readonly buffer _TexCoordBuf {float texcoord0[];};
|
||||||
|
layout(set = 1, binding = B_MATERIALS) readonly buffer _MaterialBuffer {GltfMaterial materials[];};
|
||||||
|
layout(set = 1, binding = B_TEXTURES) uniform sampler2D texturesMap[]; // all textures
|
||||||
|
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
layout(push_constant) uniform Constants
|
||||||
|
{
|
||||||
|
vec4 clearColor;
|
||||||
|
vec3 lightPosition;
|
||||||
|
float lightIntensity;
|
||||||
|
int lightType;
|
||||||
|
}
|
||||||
|
pushC;
|
||||||
|
|
||||||
|
// Return the vertex position
|
||||||
|
vec3 getVertex(uint index)
|
||||||
|
{
|
||||||
|
vec3 vp;
|
||||||
|
vp.x = vertices[3 * index + 0];
|
||||||
|
vp.y = vertices[3 * index + 1];
|
||||||
|
vp.z = vertices[3 * index + 2];
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 getNormal(uint index)
|
||||||
|
{
|
||||||
|
vec3 vp;
|
||||||
|
vp.x = normals[3 * index + 0];
|
||||||
|
vp.y = normals[3 * index + 1];
|
||||||
|
vp.z = normals[3 * index + 2];
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getTexCoord(uint index)
|
||||||
|
{
|
||||||
|
vec2 vp;
|
||||||
|
vp.x = texcoord0[2 * index + 0];
|
||||||
|
vp.y = texcoord0[2 * index + 1];
|
||||||
|
return vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Retrieve the Primitive mesh buffer information
|
||||||
|
PrimMeshInfo pinfo = primInfo[gl_InstanceCustomIndexEXT];
|
||||||
|
|
||||||
|
// Getting the 'first index' for this mesh (offset of the mesh + offset of the triangle)
|
||||||
|
uint indexOffset = pinfo.indexOffset + (3 * gl_PrimitiveID);
|
||||||
|
uint vertexOffset = pinfo.vertexOffset; // Vertex offset as defined in glTF
|
||||||
|
uint matIndex = max(0, pinfo.materialIndex); // material of primitive mesh
|
||||||
|
|
||||||
|
// Getting the 3 indices of the triangle (local)
|
||||||
|
ivec3 triangleIndex = ivec3(indices[nonuniformEXT(indexOffset + 0)], //
|
||||||
|
indices[nonuniformEXT(indexOffset + 1)], //
|
||||||
|
indices[nonuniformEXT(indexOffset + 2)]);
|
||||||
|
triangleIndex += ivec3(vertexOffset); // (global)
|
||||||
|
|
||||||
|
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
|
||||||
|
|
||||||
|
// Vertex of the triangle
|
||||||
|
const vec3 pos0 = getVertex(triangleIndex.x);
|
||||||
|
const vec3 pos1 = getVertex(triangleIndex.y);
|
||||||
|
const vec3 pos2 = getVertex(triangleIndex.z);
|
||||||
|
const vec3 position = pos0 * barycentrics.x + pos1 * barycentrics.y + pos2 * barycentrics.z;
|
||||||
|
const vec3 world_position = vec3(gl_ObjectToWorldEXT * vec4(position, 1.0));
|
||||||
|
|
||||||
|
// Normal
|
||||||
|
const vec3 nrm0 = getNormal(triangleIndex.x);
|
||||||
|
const vec3 nrm1 = getNormal(triangleIndex.y);
|
||||||
|
const vec3 nrm2 = getNormal(triangleIndex.z);
|
||||||
|
vec3 normal = normalize(nrm0 * barycentrics.x + nrm1 * barycentrics.y + nrm2 * barycentrics.z);
|
||||||
|
const vec3 world_normal = normalize(vec3(normal * gl_WorldToObjectEXT));
|
||||||
|
const vec3 geom_normal = normalize(cross(pos1 - pos0, pos2 - pos0));
|
||||||
|
|
||||||
|
// TexCoord
|
||||||
|
const vec2 uv0 = getTexCoord(triangleIndex.x);
|
||||||
|
const vec2 uv1 = getTexCoord(triangleIndex.y);
|
||||||
|
const vec2 uv2 = getTexCoord(triangleIndex.z);
|
||||||
|
const vec2 texcoord0 = uv0 * barycentrics.x + uv1 * barycentrics.y + uv2 * barycentrics.z;
|
||||||
|
|
||||||
|
// Vector toward the light
|
||||||
|
vec3 L;
|
||||||
|
float lightIntensity = pushC.lightIntensity;
|
||||||
|
float lightDistance = 100000.0;
|
||||||
|
// Point light
|
||||||
|
if(pushC.lightType == 0)
|
||||||
|
{
|
||||||
|
vec3 lDir = pushC.lightPosition - world_position;
|
||||||
|
lightDistance = length(lDir);
|
||||||
|
lightIntensity = pushC.lightIntensity / (lightDistance * lightDistance);
|
||||||
|
L = normalize(lDir);
|
||||||
|
}
|
||||||
|
else // Directional light
|
||||||
|
{
|
||||||
|
L = normalize(pushC.lightPosition - vec3(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Material of the object
|
||||||
|
GltfMaterial mat = materials[nonuniformEXT(matIndex)];
|
||||||
|
|
||||||
|
// Diffuse
|
||||||
|
vec3 diffuse = computeDiffuse(mat, L, world_normal);
|
||||||
|
if(mat.pbrBaseColorTexture > -1)
|
||||||
|
{
|
||||||
|
uint txtId = mat.pbrBaseColorTexture;
|
||||||
|
diffuse *= texture(texturesMap[nonuniformEXT(txtId)], texcoord0).xyz;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 specular = vec3(0);
|
||||||
|
float attenuation = 1;
|
||||||
|
|
||||||
|
// Tracing shadow ray only if the light is visible from the surface
|
||||||
|
if(dot(world_normal, L) > 0)
|
||||||
|
{
|
||||||
|
float tMin = 0.001;
|
||||||
|
float tMax = lightDistance;
|
||||||
|
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
|
||||||
|
vec3 rayDir = L;
|
||||||
|
uint flags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT
|
||||||
|
| gl_RayFlagsSkipClosestHitShaderEXT;
|
||||||
|
isShadowed = true;
|
||||||
|
traceRayEXT(topLevelAS, // acceleration structure
|
||||||
|
flags, // rayFlags
|
||||||
|
0xFF, // cullMask
|
||||||
|
0, // sbtRecordOffset
|
||||||
|
0, // sbtRecordStride
|
||||||
|
1, // missIndex
|
||||||
|
origin, // ray origin
|
||||||
|
tMin, // ray min range
|
||||||
|
rayDir, // ray direction
|
||||||
|
tMax, // ray max range
|
||||||
|
1 // payload (location = 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(isShadowed)
|
||||||
|
{
|
||||||
|
attenuation = 0.3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Specular
|
||||||
|
specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, world_normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prd.hitValue = vec3(lightIntensity * attenuation * (diffuse + specular));
|
||||||
|
}
|
||||||
49
ray_tracing_gltf/shaders/raytrace.rgen
Normal file
49
ray_tracing_gltf/shaders/raytrace.rgen
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
#include "binding.glsl"
|
||||||
|
#include "raycommon.glsl"
|
||||||
|
|
||||||
|
layout(set = 0, binding = 0) uniform accelerationStructureEXT topLevelAS;
|
||||||
|
layout(set = 0, binding = 1, rgba32f) uniform image2D image;
|
||||||
|
|
||||||
|
layout(location = 0) rayPayloadEXT hitPayload prd;
|
||||||
|
|
||||||
|
layout(set = 1, binding = B_CAMERA) uniform CameraProperties
|
||||||
|
{
|
||||||
|
mat4 view;
|
||||||
|
mat4 proj;
|
||||||
|
mat4 viewInverse;
|
||||||
|
mat4 projInverse;
|
||||||
|
}
|
||||||
|
cam;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
|
||||||
|
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);
|
||||||
|
|
||||||
|
uint rayFlags = gl_RayFlagsOpaqueEXT;
|
||||||
|
float tMin = 0.001;
|
||||||
|
float tMax = 10000.0;
|
||||||
|
|
||||||
|
traceRayEXT(topLevelAS, // acceleration structure
|
||||||
|
rayFlags, // rayFlags
|
||||||
|
0xFF, // cullMask
|
||||||
|
0, // sbtRecordOffset
|
||||||
|
0, // sbtRecordStride
|
||||||
|
0, // missIndex
|
||||||
|
origin.xyz, // ray origin
|
||||||
|
tMin, // ray min range
|
||||||
|
direction.xyz, // ray direction
|
||||||
|
tMax, // ray max range
|
||||||
|
0 // payload (location = 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(prd.hitValue, 1.0));
|
||||||
|
}
|
||||||
16
ray_tracing_gltf/shaders/raytrace.rmiss
Normal file
16
ray_tracing_gltf/shaders/raytrace.rmiss
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
#include "raycommon.glsl"
|
||||||
|
|
||||||
|
layout(location = 0) rayPayloadInEXT hitPayload prd;
|
||||||
|
|
||||||
|
layout(push_constant) uniform Constants
|
||||||
|
{
|
||||||
|
vec4 clearColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
prd.hitValue = clearColor.xyz * 0.8;
|
||||||
|
}
|
||||||
9
ray_tracing_gltf/shaders/raytraceShadow.rmiss
Normal file
9
ray_tracing_gltf/shaders/raytraceShadow.rmiss
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#version 460
|
||||||
|
#extension GL_EXT_ray_tracing : require
|
||||||
|
|
||||||
|
layout(location = 1) rayPayloadInEXT bool isShadowed;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
isShadowed = false;
|
||||||
|
}
|
||||||
64
ray_tracing_gltf/shaders/sampling.glsl
Normal file
64
ray_tracing_gltf/shaders/sampling.glsl
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Generate a random unsigned int from two unsigned int values, using 16 pairs
|
||||||
|
// of rounds of the Tiny Encryption Algorithm. See Zafar, Olano, and Curtis,
|
||||||
|
// "GPU Random Numbers via the Tiny Encryption Algorithm"
|
||||||
|
uint tea(uint val0, uint val1)
|
||||||
|
{
|
||||||
|
uint v0 = val0;
|
||||||
|
uint v1 = val1;
|
||||||
|
uint s0 = 0;
|
||||||
|
|
||||||
|
for(uint n = 0; n < 16; n++)
|
||||||
|
{
|
||||||
|
s0 += 0x9e3779b9;
|
||||||
|
v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
|
||||||
|
v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random unsigned int in [0, 2^24) given the previous RNG state
|
||||||
|
// using the Numerical Recipes linear congruential generator
|
||||||
|
uint lcg(inout uint prev)
|
||||||
|
{
|
||||||
|
uint LCG_A = 1664525u;
|
||||||
|
uint LCG_C = 1013904223u;
|
||||||
|
prev = (LCG_A * prev + LCG_C);
|
||||||
|
return prev & 0x00FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random float in [0, 1) given the previous RNG state
|
||||||
|
float rnd(inout uint prev)
|
||||||
|
{
|
||||||
|
return (float(lcg(prev)) / float(0x01000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------
|
||||||
|
// Sampling
|
||||||
|
//-------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Randomly sampling around +Z
|
||||||
|
vec3 samplingHemisphere(inout uint seed, in vec3 x, in vec3 y, in vec3 z)
|
||||||
|
{
|
||||||
|
#define M_PI 3.141592
|
||||||
|
|
||||||
|
float r1 = rnd(seed);
|
||||||
|
float r2 = rnd(seed);
|
||||||
|
float sq = sqrt(1.0 - r2);
|
||||||
|
|
||||||
|
vec3 direction = vec3(cos(2 * M_PI * r1) * sq, sin(2 * M_PI * r1) * sq, sqrt(r2));
|
||||||
|
direction = direction.x * x + direction.y * y + direction.z * z;
|
||||||
|
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the tangent and binormal from the incoming normal
|
||||||
|
void createCoordinateSystem(in vec3 N, out vec3 Nt, out vec3 Nb)
|
||||||
|
{
|
||||||
|
if(abs(N.x) > abs(N.y))
|
||||||
|
Nt = vec3(N.z, 0, -N.x) / sqrt(N.x * N.x + N.z * N.z);
|
||||||
|
else
|
||||||
|
Nt = vec3(0, -N.z, N.y) / sqrt(N.y * N.y + N.z * N.z);
|
||||||
|
Nb = cross(N, Nt);
|
||||||
|
}
|
||||||
61
ray_tracing_gltf/shaders/vert_shader.vert
Normal file
61
ray_tracing_gltf/shaders/vert_shader.vert
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
#extension GL_EXT_scalar_block_layout : enable
|
||||||
|
#extension GL_GOOGLE_include_directive : enable
|
||||||
|
|
||||||
|
#include "binding.glsl"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
layout( set = 0, binding = B_MATRICES) readonly buffer _Matrix { mat4 matrices[]; };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
layout(binding = 0) uniform UniformBufferObject
|
||||||
|
{
|
||||||
|
mat4 view;
|
||||||
|
mat4 proj;
|
||||||
|
mat4 viewI;
|
||||||
|
}
|
||||||
|
ubo;
|
||||||
|
|
||||||
|
layout(push_constant) uniform shaderInformation
|
||||||
|
{
|
||||||
|
vec3 lightPosition;
|
||||||
|
uint instanceId;
|
||||||
|
float lightIntensity;
|
||||||
|
int lightType;
|
||||||
|
int materialId;
|
||||||
|
}
|
||||||
|
pushC;
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 inPosition;
|
||||||
|
layout(location = 1) in vec3 inNormal;
|
||||||
|
layout(location = 2) in vec2 inTexCoord;
|
||||||
|
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
|
out gl_PerVertex
|
||||||
|
{
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
mat4 objMatrix = matrices[pushC.instanceId];
|
||||||
|
mat4 objMatrixIT = transpose(inverse(objMatrix));
|
||||||
|
|
||||||
|
vec3 origin = vec3(ubo.viewI * vec4(0, 0, 0, 1));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue