initial setup

This commit is contained in:
CDaut 2024-05-10 15:45:27 +02:00
parent c008702d1c
commit df41e985b3
Signed by: clara
GPG key ID: 223391B52FAD4463
347 changed files with 333 additions and 43659 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use_flake

178
.gitignore vendored
View file

@ -2,3 +2,181 @@
/build/*
/out/build/x64-Debug
/.vs
# cmake
cmake-build-debug
# direnv
.direnv
#binarys
bin_x64
# Created by https://www.toptal.com/developers/gitignore/api/clion,cmake,c++
# Edit at https://www.toptal.com/developers/gitignore?templates=clion,cmake,c++
### C++ ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### CLion ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### CLion Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
# Azure Toolkit for IntelliJ plugin
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
.idea/**/azureSettings.xml
### CMake ###
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
### CMake Patch ###
# External projects
*-prefix/
# End of https://www.toptal.com/developers/gitignore/api/clion,cmake,c++

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "nvpro_core"]
path = nvpro_core
url = git@github.com:nvpro-samples/nvpro_core.git

8
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

1
.idea/.name generated Normal file
View file

@ -0,0 +1 @@
vk_raytracing_tutorial

19
.idea/codeStyles/Project.xml generated Normal file
View file

@ -0,0 +1,19 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="2" />
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_CLASS_MEMBERS" value="2" />
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
</Objective-C>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
<codeStyleSettings language="ObjectiveC">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

7
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakePythonSetting">
<option name="pythonIntegrationState" value="YES" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/vk_raytracing_tutorial_KHR.iml" filepath="$PROJECT_DIR$/.idea/vk_raytracing_tutorial_KHR.iml" />
</modules>
</component>
</project>

7
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/nvpro_core" vcs="Git" />
</component>
</project>

2
.idea/vk_raytracing_tutorial_KHR.iml generated Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

View file

@ -41,25 +41,7 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/media DESTINATION ${OUTPUT_PATH})
#--------------------------------------------------------------------------------------------------
# Sub examples
add_subdirectory(ray_tracing__advance)
add_subdirectory(ray_tracing__before)
add_subdirectory(ray_tracing__simple)
add_subdirectory(ray_tracing_animation)
add_subdirectory(ray_tracing_anyhit)
add_subdirectory(ray_tracing_callable)
add_subdirectory(ray_tracing_gltf)
add_subdirectory(ray_tracing_instances)
add_subdirectory(ray_tracing_intersection)
add_subdirectory(ray_tracing_jitter_cam)
add_subdirectory(ray_tracing_manyhits)
add_subdirectory(ray_tracing_rayquery)
add_subdirectory(ray_tracing_reflections)
add_subdirectory(ray_tracing_ao)
add_subdirectory(ray_tracing_indirect_scissor)
add_subdirectory(ray_tracing_specialization)
add_subdirectory(ray_tracing_advanced_compilation)
add_subdirectory(ray_tracing_motionblur)
#--------------------------------------------------------------------------------------------------

27
flake.lock generated Normal file
View file

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1715266358,
"narHash": "sha256-doPgfj+7FFe9rfzWo1siAV2mVCasW+Bh8I1cToAXEE4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "f1010e0469db743d14519a1efd37e23f8513d714",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

66
flake.nix Normal file
View file

@ -0,0 +1,66 @@
{
description = "Nix flake for developing with vulkan and cpp";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
};
outputs = { self, nixpkgs }:
let
allSystems = [
"x86_64-linux" # 64bit AMD/Intel x86
];
forAllSystems = fn:
nixpkgs.lib.genAttrs allSystems
(system: fn { pkgs = import nixpkgs { inherit system; }; });
in
{
devShells = forAllSystems ({ pkgs }: {
default = pkgs.mkShell {
name = "vulkan CPP dev shell";
# gcc appears to be broken
nativeBuildInputs = with pkgs ;[
(hiPrio clang-tools.override {
llvmPackages = llvmPackages_16;
enableLibcxx = false;
})
# CPP deps
clang-tools
glm
cmake
# Vulkan Deps
vulkan-tools
vulkan-headers
vulkan-loader
vulkan-validation-layers
shaderc
shaderc.dev
shaderc.lib
shaderc.bin
shaderc.static
glslang
# X11 Deps
xorg.libX11
xorg.libXi
xorg.libXxf86vm
xorg.libXrandr
xorg.libXinerama
xorg.libXcursor
glfw
# Wayland deps
glfw-wayland
waylandpp
wayland
# dev tools
valgrind
# general deps
imgui
];
# TODO: include deps depending on XDG_SESSION_TYPE here (wayland vs X11)
};
});
};
}

1
nvpro_core Submodule

@ -0,0 +1 @@
Subproject commit c98df794436fed19c17d3eb12f6ff8e5002ce2c4

View file

@ -1,80 +0,0 @@
#*****************************************************************************
# Copyright 2020 NVIDIA Corporation. All rights reserved.
#*****************************************************************************
cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
#--------------------------------------------------------------------------------------------------
# Project setting
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(PROJNAME vk_${PROJNAME}_KHR)
project(${PROJNAME} LANGUAGES C CXX)
message(STATUS "-------------------------------")
message(STATUS "Processing Project ${PROJNAME}:")
#--------------------------------------------------------------------------------------------------
# C++ target and defines
set(CMAKE_CXX_STANDARD 17)
add_executable(${PROJNAME})
_add_project_definitions(${PROJNAME})
#--------------------------------------------------------------------------------------------------
# Source files for this project
#
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
file(GLOB EXTRA_COMMON ${TUTO_KHR_DIR}/common/*.*)
list(APPEND COMMON_SOURCE_FILES ${EXTRA_COMMON})
include_directories(${TUTO_KHR_DIR}/common)
#--------------------------------------------------------------------------------------------------
# GLSL to SPIR-V custom build
compile_glsl_directory(
SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders"
DST "${CMAKE_CURRENT_SOURCE_DIR}/spv"
VULKAN_TARGET "vulkan1.2"
DEPENDENCY ${VULKAN_BUILD_DEPENDENCIES}
)
#--------------------------------------------------------------------------------------------------
# Sources
target_sources(${PROJNAME} PUBLIC ${SOURCE_FILES} ${HEADER_FILES})
target_sources(${PROJNAME} PUBLIC ${COMMON_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${PACKAGE_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${GLSL_SOURCES} ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Sub-folders in Visual Studio
#
source_group("Common" FILES ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES})
source_group("Sources" FILES ${SOURCE_FILES})
source_group("Headers" FILES ${HEADER_FILES})
source_group("Shader Sources" FILES ${GLSL_SOURCES})
source_group("Shader Headers" FILES ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Linkage
#
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core)
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.)
#
_finalize_target( ${PROJNAME} )
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/spv")
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/spv")

View file

@ -1,7 +0,0 @@
# NVIDIA Vulkan Ray Tracing Tutorial
This example is the combination of all tutorials.
If you haven't done the tutorials, you can start [here](https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/vkrt_tutorial.md.html)
![](../docs/Images/ray_tracing__advance.png)

View file

@ -1,568 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include <sstream>
#define VMA_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "obj_loader.h"
#include "stb_image.h"
#include "hello_vulkan.h"
#include "nvh/cameramanipulator.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/images_vk.hpp"
#include "nvvk/pipeline_vk.hpp"
#include "nvh/fileoperations.hpp"
#include "nvvk/commands_vk.hpp"
#include "nvvk/renderpasses_vk.hpp"
#include "nvvk/buffers_vk.hpp"
extern std::vector<std::string> defaultSearchPaths;
//--------------------------------------------------------------------------------------------------
// Keep the handle on the device
// Initialize the tool to do all our allocations: buffers, images
//
void HelloVulkan::setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily)
{
AppBaseVk::setup(instance, device, physicalDevice, queueFamily);
m_alloc.init(instance, device, physicalDevice);
m_debug.setup(m_device);
m_offscreen.setup(device, physicalDevice, &m_alloc, queueFamily);
m_raytrace.setup(device, physicalDevice, &m_alloc, queueFamily);
}
//--------------------------------------------------------------------------------------------------
// Called at each frame to update the camera matrix
//
void HelloVulkan::updateUniformBuffer(const VkCommandBuffer& cmdBuf)
{
// Prepare new UBO contents on host.
const float aspectRatio = m_size.width / static_cast<float>(m_size.height);
GlobalUniforms hostUBO = {};
const auto& view = CameraManip.getMatrix();
glm::mat4 proj = glm::perspectiveRH_ZO(glm::radians(CameraManip.getFov()), aspectRatio, 0.1f, 1000.0f);
proj[1][1] *= -1; // Inverting Y for Vulkan (not needed with perspectiveVK).
hostUBO.viewProj = proj * view;
hostUBO.viewInverse = glm::inverse(view);
hostUBO.projInverse = glm::inverse(proj);
// UBO on the device, and what stages access it.
VkBuffer deviceUBO = m_bGlobals.buffer;
auto uboUsageStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
// Ensure that the modified UBO is not visible to previous frames.
VkBufferMemoryBarrier beforeBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
beforeBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
beforeBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
beforeBarrier.buffer = deviceUBO;
beforeBarrier.offset = 0;
beforeBarrier.size = sizeof(hostUBO);
vkCmdPipelineBarrier(cmdBuf, uboUsageStages, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_DEPENDENCY_DEVICE_GROUP_BIT, 0,
nullptr, 1, &beforeBarrier, 0, nullptr);
// Schedule the host-to-device upload. (hostUBO is copied into the cmd
// buffer so it is okay to deallocate when the function returns).
vkCmdUpdateBuffer(cmdBuf, m_bGlobals.buffer, 0, sizeof(GlobalUniforms), &hostUBO);
// Making sure the updated UBO will be visible.
VkBufferMemoryBarrier afterBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
afterBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
afterBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
afterBarrier.buffer = deviceUBO;
afterBarrier.offset = 0;
afterBarrier.size = sizeof(hostUBO);
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, uboUsageStages, VK_DEPENDENCY_DEVICE_GROUP_BIT, 0,
nullptr, 1, &afterBarrier, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Describing the layout pushed when rendering
//
void HelloVulkan::createDescriptorSetLayout()
{
auto nbTxt = static_cast<uint32_t>(m_textures.size());
// Camera matrices
m_descSetLayoutBind.addBinding(SceneBindings::eGlobals, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_RAYGEN_BIT_KHR);
// Obj descriptions
m_descSetLayoutBind.addBinding(SceneBindings::eObjDescs, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT
| VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_ANY_HIT_BIT_KHR);
// Textures
m_descSetLayoutBind.addBinding(SceneBindings::eTextures, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nbTxt,
VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_ANY_HIT_BIT_KHR);
// Storing implicit obj (binding = 3)
m_descSetLayoutBind.addBinding(eImplicits, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_INTERSECTION_BIT_KHR
| VK_SHADER_STAGE_ANY_HIT_BIT_KHR);
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<VkWriteDescriptorSet> writes;
// Camera matrices and scene description
VkDescriptorBufferInfo dbiUnif{m_bGlobals.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eGlobals, &dbiUnif));
VkDescriptorBufferInfo dbiSceneDesc{m_bObjDesc.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eObjDescs, &dbiSceneDesc));
// All texture samplers
std::vector<VkDescriptorImageInfo> diit;
for(auto& texture : m_textures)
{
diit.emplace_back(texture.descriptor);
}
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, SceneBindings::eTextures, diit.data()));
VkDescriptorBufferInfo dbiImplDesc{m_implObjects.implBuf.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, 3, &dbiImplDesc));
// Writing the information
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Creating the pipeline layout
//
void HelloVulkan::createGraphicsPipeline()
{
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstantRaster)};
// Creating the Pipeline Layout
VkPipelineLayoutCreateInfo createInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &m_descSetLayout;
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRanges;
vkCreatePipelineLayout(m_device, &createInfo, nullptr, &m_pipelineLayout);
// Creating the Pipeline
std::vector<std::string> paths = defaultSearchPaths;
nvvk::GraphicsPipelineGeneratorCombined gpb(m_device, m_pipelineLayout, m_offscreen.renderPass());
gpb.depthStencilState.depthTestEnable = true;
gpb.addShader(nvh::loadFile("spv/vert_shader.vert.spv", true, paths, true), VK_SHADER_STAGE_VERTEX_BIT);
gpb.addShader(nvh::loadFile("spv/frag_shader.frag.spv", true, paths, true), VK_SHADER_STAGE_FRAGMENT_BIT);
gpb.addBindingDescription({0, sizeof(VertexObj)});
gpb.addAttributeDescriptions({
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, pos))},
{1, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, nrm))},
{2, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, color))},
{3, 0, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, texCoord))},
});
m_graphicsPipeline = gpb.createPipeline();
m_debug.setObjectName(m_graphicsPipeline, "Graphics");
}
//--------------------------------------------------------------------------------------------------
// Loading the OBJ file and setting up all buffers
//
void HelloVulkan::loadModel(const std::string& filename, glm::mat4 transform)
{
LOGI("Loading File: %s \n", filename.c_str());
ObjLoader loader;
loader.loadModel(filename);
// Converting from Srgb to linear
for(auto& m : loader.m_materials)
{
m.ambient = glm::pow(m.ambient, glm::vec3(2.2f));
m.diffuse = glm::pow(m.diffuse, glm::vec3(2.2f));
m.specular = glm::pow(m.specular, glm::vec3(2.2f));
}
ObjModel model;
model.nbIndices = static_cast<uint32_t>(loader.m_indices.size());
model.nbVertices = static_cast<uint32_t>(loader.m_vertices.size());
// Create the buffers on Device and copy vertices, indices and materials
nvvk::CommandPool cmdBufGet(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = cmdBufGet.createCommandBuffer();
VkBufferUsageFlags flag = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
VkBufferUsageFlags rayTracingFlags = // used also for building acceleration structures
flag | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
model.vertexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_vertices, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | rayTracingFlags);
model.indexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_indices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | rayTracingFlags);
model.matColorBuffer = m_alloc.createBuffer(cmdBuf, loader.m_materials, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
model.matIndexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_matIndx, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
// Creates all textures found and find the offset for this model
auto txtOffset = static_cast<uint32_t>(m_textures.size());
createTextureImages(cmdBuf, loader.m_textures);
cmdBufGet.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
std::string objNb = std::to_string(m_objModel.size());
m_debug.setObjectName(model.vertexBuffer.buffer, (std::string("vertex_" + objNb)));
m_debug.setObjectName(model.indexBuffer.buffer, (std::string("index_" + objNb)));
m_debug.setObjectName(model.matColorBuffer.buffer, (std::string("mat_" + objNb)));
m_debug.setObjectName(model.matIndexBuffer.buffer, (std::string("matIdx_" + objNb)));
// Keeping transformation matrix of the instance
ObjInstance instance;
instance.transform = transform;
instance.objIndex = static_cast<uint32_t>(m_objModel.size());
m_instances.push_back(instance);
// Creating information for device access
ObjDesc desc;
desc.txtOffset = txtOffset;
desc.vertexAddress = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
desc.indexAddress = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
desc.materialAddress = nvvk::getBufferDeviceAddress(m_device, model.matColorBuffer.buffer);
desc.materialIndexAddress = nvvk::getBufferDeviceAddress(m_device, model.matIndexBuffer.buffer);
// Keeping the obj host model and device description
m_objModel.emplace_back(model);
m_objDesc.emplace_back(desc);
}
//--------------------------------------------------------------------------------------------------
// Creating the uniform buffer holding the camera matrices
// - Buffer is host visible
//
void HelloVulkan::createUniformBuffer()
{
m_bGlobals = m_alloc.createBuffer(sizeof(GlobalUniforms), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_debug.setObjectName(m_bGlobals.buffer, "Globals");
}
//--------------------------------------------------------------------------------------------------
// Create a storage buffer containing the description of the scene elements
// - Which geometry is used by which instance
// - Transformation
// - Offset for texture
//
void HelloVulkan::createObjDescriptionBuffer()
{
nvvk::CommandPool cmdGen(m_device, m_graphicsQueueIndex);
auto cmdBuf = cmdGen.createCommandBuffer();
m_bObjDesc = m_alloc.createBuffer(cmdBuf, m_objDesc, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
cmdGen.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
m_debug.setObjectName(m_bObjDesc.buffer, "ObjDescs");
}
//--------------------------------------------------------------------------------------------------
// Creating all textures and samplers
//
void HelloVulkan::createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures)
{
VkSamplerCreateInfo samplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCreateInfo.maxLod = FLT_MAX;
VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
// If no textures are present, create a dummy one to accommodate the pipeline layout
if(textures.empty() && m_textures.empty())
{
nvvk::Texture texture;
std::array<uint8_t, 4> color{255u, 255u, 255u, 255u};
VkDeviceSize bufferSize = sizeof(color);
auto imgSize = VkExtent2D{1, 1};
auto imageCreateInfo = nvvk::makeImage2DCreateInfo(imgSize, format);
// Creating the dummy texture
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, color.data(), imageCreateInfo);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
texture = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
// The image format must be in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
nvvk::cmdBarrierImageLayout(cmdBuf, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_textures.push_back(texture);
}
else
{
// Uploading all images
for(const auto& texture : textures)
{
std::stringstream o;
int texWidth, texHeight, texChannels;
o << "media/textures/" << texture;
std::string txtFile = nvh::findFile(o.str(), defaultSearchPaths, true);
stbi_uc* stbi_pixels = stbi_load(txtFile.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
std::array<stbi_uc, 4> color{255u, 0u, 255u, 255u};
stbi_uc* pixels = stbi_pixels;
// Handle failure
if(!stbi_pixels)
{
texWidth = texHeight = 1;
texChannels = 4;
pixels = reinterpret_cast<stbi_uc*>(color.data());
}
VkDeviceSize bufferSize = static_cast<uint64_t>(texWidth) * texHeight * sizeof(uint8_t) * 4;
auto imgSize = VkExtent2D{(uint32_t)texWidth, (uint32_t)texHeight};
auto imageCreateInfo = nvvk::makeImage2DCreateInfo(imgSize, format, VK_IMAGE_USAGE_SAMPLED_BIT, true);
{
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, pixels, imageCreateInfo);
nvvk::cmdGenerateMipmaps(cmdBuf, image.image, format, imgSize, imageCreateInfo.mipLevels);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
nvvk::Texture texture = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
m_textures.push_back(texture);
}
stbi_image_free(stbi_pixels);
}
}
}
//--------------------------------------------------------------------------------------------------
// Destroying all allocations
//
void HelloVulkan::destroyResources()
{
vkDestroyPipeline(m_device, m_graphicsPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_descPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_descSetLayout, nullptr);
m_alloc.destroy(m_bGlobals);
m_alloc.destroy(m_bObjDesc);
m_alloc.destroy(m_implObjects.implBuf);
m_alloc.destroy(m_implObjects.implMatBuf);
for(auto& m : m_objModel)
{
m_alloc.destroy(m.vertexBuffer);
m_alloc.destroy(m.indexBuffer);
m_alloc.destroy(m.matColorBuffer);
m_alloc.destroy(m.matIndexBuffer);
}
for(auto& t : m_textures)
{
m_alloc.destroy(t);
}
//#Post
m_offscreen.destroy();
// #VKRay
m_raytrace.destroy();
m_alloc.deinit();
}
//--------------------------------------------------------------------------------------------------
// Drawing the scene in raster mode
//
void HelloVulkan::rasterize(const VkCommandBuffer& cmdBuf)
{
VkDeviceSize offset{0};
m_debug.beginLabel(cmdBuf, "Rasterize");
// Dynamic Viewport
setViewport(cmdBuf);
// Drawing all triangles
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descSet, 0, nullptr);
auto nbInst = static_cast<uint32_t>(m_instances.size() - 1); // Remove the implicit object
for(uint32_t i = 0; i < nbInst; ++i)
{
auto& inst = m_instances[i];
auto& model = m_objModel[inst.objIndex];
m_pcRaster.objIndex = inst.objIndex; // Telling which object is drawn
m_pcRaster.modelMatrix = inst.transform;
vkCmdPushConstants(cmdBuf, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(PushConstantRaster), &m_pcRaster);
vkCmdBindVertexBuffers(cmdBuf, 0, 1, &model.vertexBuffer.buffer, &offset);
vkCmdBindIndexBuffer(cmdBuf, model.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmdBuf, model.nbIndices, 1, 0, 0, 0);
}
m_debug.endLabel(cmdBuf);
}
//--------------------------------------------------------------------------------------------------
// Handling resize of the window
//
void HelloVulkan::onResize(int /*w*/, int /*h*/)
{
m_offscreen.createFramebuffer(m_size);
m_offscreen.updateDescriptorSet();
m_raytrace.updateRtDescriptorSet(m_offscreen.colorTexture().descriptor.imageView);
resetFrame();
}
//--------------------------------------------------------------------------------------------------
// Initialize offscreen rendering
//
void HelloVulkan::initOffscreen()
{
m_offscreen.createFramebuffer(m_size);
m_offscreen.createDescriptor();
m_offscreen.createPipeline(m_renderPass);
m_offscreen.updateDescriptorSet();
}
//--------------------------------------------------------------------------------------------------
// Initialize Vulkan ray tracing
//
void HelloVulkan::initRayTracing()
{
m_raytrace.createBottomLevelAS(m_objModel, m_implObjects);
m_raytrace.createTopLevelAS(m_instances, m_implObjects);
m_raytrace.createRtDescriptorSet(m_offscreen.colorTexture().descriptor.imageView);
m_raytrace.createRtPipeline(m_descSetLayout);
}
//--------------------------------------------------------------------------------------------------
// Ray trace the scene
//
void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const glm::vec4& clearColor)
{
updateFrame();
if(m_pcRaster.frame >= m_maxFrames)
return;
m_raytrace.raytrace(cmdBuf, clearColor, m_descSet, m_size, m_pcRaster);
}
//--------------------------------------------------------------------------------------------------
// If the camera matrix has changed, resets the frame.
// otherwise, increments frame.
//
void HelloVulkan::updateFrame()
{
static glm::mat4 refCamMatrix;
static float refFov{CameraManip.getFov()};
const auto& m = CameraManip.getMatrix();
const auto fov = CameraManip.getFov();
if(refCamMatrix != m || refFov != fov)
{
resetFrame();
refCamMatrix = m;
refFov = fov;
}
m_pcRaster.frame++;
}
void HelloVulkan::resetFrame()
{
m_pcRaster.frame = -1;
}
void HelloVulkan::addImplSphere(glm::vec3 center, float radius, int matId)
{
ObjImplicit impl;
impl.minimum = center - radius;
impl.maximum = center + radius;
impl.objType = EObjType::eSphere;
impl.matId = matId;
m_implObjects.objImpl.push_back(impl);
}
void HelloVulkan::addImplCube(glm::vec3 minumum, glm::vec3 maximum, int matId)
{
ObjImplicit impl;
impl.minimum = minumum;
impl.maximum = maximum;
impl.objType = EObjType::eCube;
impl.matId = matId;
m_implObjects.objImpl.push_back(impl);
}
void HelloVulkan::addImplMaterial(const MaterialObj& mat)
{
m_implObjects.implMat.push_back(mat);
}
//--------------------------------------------------------------------------------------------------
// Create a storage buffer containing the description of the scene elements
// - Which geometry is used by which instance
// - Transformation
// - Offset for texture
//
void HelloVulkan::createImplictBuffers()
{
using vkBU = VkBufferUsageFlagBits;
nvvk::CommandPool cmdGen(m_device, m_graphicsQueueIndex);
// Not allowing empty buffers
if(m_implObjects.objImpl.empty())
m_implObjects.objImpl.push_back({});
if(m_implObjects.implMat.empty())
m_implObjects.implMat.push_back({});
auto cmdBuf = cmdGen.createCommandBuffer();
m_implObjects.implBuf = m_alloc.createBuffer(cmdBuf, m_implObjects.objImpl,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR);
m_implObjects.implMatBuf = m_alloc.createBuffer(cmdBuf, m_implObjects.implMat,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT);
cmdGen.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
m_debug.setObjectName(m_implObjects.implBuf.buffer, "implicitObj");
m_debug.setObjectName(m_implObjects.implMatBuf.buffer, "implicitMat");
// Adding an extra instance to get access to the material buffers
ObjDesc objDesc{};
objDesc.materialAddress = nvvk::getBufferDeviceAddress(m_device, m_implObjects.implMatBuf.buffer);
m_objDesc.emplace_back(objDesc);
ObjInstance instance{};
instance.objIndex = static_cast<uint32_t>(m_objModel.size());
m_instances.emplace_back(instance);
}

View file

@ -1,137 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "nvvkhl/appbase_vk.hpp"
#include "nvvk/debug_util_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
#include "shaders/host_device.h"
// #VKRay
#include "nvvk/raytraceKHR_vk.hpp"
#include "offscreen.hpp"
#include "obj.hpp"
#include "raytrace.hpp"
#include <memory>
// Choosing the allocator to use
#define ALLOC_DMA
//#define ALLOC_DEDICATED
//#define ALLOC_VMA
#include <nvvk/resourceallocator_vk.hpp>
#if defined(ALLOC_DMA)
#include <nvvk/memallocator_dma_vk.hpp>
using Allocator = nvvk::ResourceAllocatorDma;
#elif defined(ALLOC_VMA)
#include <nvvk/memallocator_vma_vk.hpp>
using Allocator = nvvk::ResourceAllocatorVma;
#else
using Allocator = nvvk::ResourceAllocatorDedicated;
#endif
//--------------------------------------------------------------------------------------------------
// 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 nvvkhl::AppBaseVk
{
public:
void setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily) override;
void createDescriptorSetLayout();
void createGraphicsPipeline();
void loadModel(const std::string& filename, glm::mat4 transform = glm::mat4(1));
void updateDescriptorSet();
void createUniformBuffer();
void createObjDescriptionBuffer();
void createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures);
void updateUniformBuffer(const VkCommandBuffer& cmdBuf);
void onResize(int /*w*/, int /*h*/) override;
void destroyResources();
void rasterize(const VkCommandBuffer& cmdBuff);
Offscreen& offscreen() { return m_offscreen; }
Raytracer& raytracer() { return m_raytrace; }
// Information pushed at each draw call
PushConstantRaster m_pcRaster{
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, // Identity matrix
{10.f, 15.f, 8.f}, // light position
0, // instance Id
{-1.f, -1.f, -.4f}, // lightDirection;
0.939692621f, // {cos(glm::radians(20.0f))}, // lightSpotCutoff;
0.866025404f, // {cos(glm::radians(30.0f))}, // lightSpotOuterCutoff;
100.f, // light intensity
0 // light type
};
// Array of objects and instances in the scene
std::vector<ObjModel> m_objModel; // Model on host
std::vector<ObjDesc> m_objDesc; // Model description for device access
std::vector<ObjInstance> m_instances; // Scene model instances
// Graphic pipeline
VkPipelineLayout m_pipelineLayout;
VkPipeline m_graphicsPipeline;
nvvk::DescriptorSetBindings m_descSetLayoutBind;
VkDescriptorPool m_descPool;
VkDescriptorSetLayout m_descSetLayout;
VkDescriptorSet m_descSet;
int m_maxFrames{500};
void resetFrame();
void updateFrame();
nvvk::Buffer m_bGlobals; // Device-Host of the camera matrices
nvvk::Buffer m_bObjDesc; // Device buffer of the OBJ descriptions
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
Allocator m_alloc; // Allocator for buffer, images, acceleration structures
nvvk::DebugUtil m_debug; // Utility to name objects
// #Post
Offscreen m_offscreen;
void initOffscreen();
// #VKRay
Raytracer m_raytrace;
void initRayTracing();
void raytrace(const VkCommandBuffer& cmdBuf, const glm::vec4& clearColor);
// Implicit
ImplInst m_implObjects;
void addImplSphere(glm::vec3 center, float radius, int matId);
void addImplCube(glm::vec3 minumum, glm::vec3 maximum, int matId);
void addImplMaterial(const MaterialObj& mat);
void createImplictBuffers();
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

View file

@ -1,365 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
// 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>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_vulkan.h"
#include "imgui.h"
#include "imgui/imgui_helper.h"
#include "hello_vulkan.h"
#include "imgui/imgui_camera_widget.h"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvpsystem.hpp"
#include "nvvk/commands_vk.hpp"
#include "nvvk/context_vk.hpp"
#include <random>
//////////////////////////////////////////////////////////////////////////
#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)
{
bool changed = false;
changed |= ImGuiH::CameraWidget();
if(ImGui::CollapsingHeader("Light"))
{
auto& pc = helloVk.m_pcRaster;
changed |= ImGui::RadioButton("Point", &pc.lightType, 0);
ImGui::SameLine();
changed |= ImGui::RadioButton("Spot", &pc.lightType, 1);
ImGui::SameLine();
changed |= ImGui::RadioButton("Infinite", &pc.lightType, 2);
if(pc.lightType < 2)
{
changed |= ImGui::SliderFloat3("Light Position", &pc.lightPosition.x, -20.f, 20.f);
}
if(pc.lightType > 0)
{
changed |= ImGui::SliderFloat3("Light Direction", &pc.lightDirection.x, -1.f, 1.f);
}
if(pc.lightType < 2)
{
changed |= ImGui::SliderFloat("Light Intensity", &pc.lightIntensity, 0.f, 500.f);
}
if(pc.lightType == 1)
{
float dCutoff = glm::degrees(acos(pc.lightSpotCutoff));
float dOutCutoff = glm::degrees(acos(pc.lightSpotOuterCutoff));
changed |= ImGui::SliderFloat("Cutoff", &dCutoff, 0.f, 45.f);
changed |= ImGui::SliderFloat("OutCutoff", &dOutCutoff, 0.f, 45.f);
dCutoff = dCutoff > dOutCutoff ? dOutCutoff : dCutoff;
pc.lightSpotCutoff = cos(glm::radians(dCutoff));
pc.lightSpotOuterCutoff = cos(glm::radians(dOutCutoff));
}
}
changed |= ImGui::SliderInt("Max Frames", &helloVk.m_maxFrames, 1, 1000);
if(changed)
helloVk.resetFrame();
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
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, PROJECT_NAME, nullptr, nullptr);
// Setup camera
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
CameraManip.setLookat({8.440, 9.041, -8.973}, {-2.462, 3.661, -0.286}, {0.000, 1.000, 0.000});
// 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(PROJECT_NAME);
// Search path for shaders and other media
defaultSearchPaths = {
NVPSystem::exePath() + PROJECT_RELDIRECTORY,
NVPSystem::exePath() + PROJECT_RELDIRECTORY "..",
std::string(PROJECT_NAME),
};
// Vulkan required extensions
assert(glfwVulkanSupported() == 1);
uint32_t count{0};
auto reqExtensions = glfwGetRequiredInstanceExtensions(&count);
// Requesting Vulkan extensions and layers
nvvk::ContextCreateInfo contextInfo;
contextInfo.setVersion(1, 2); // Using Vulkan 1.2
for(uint32_t ext_id = 0; ext_id < count; ext_id++) // Adding required extensions (surface, win32, linux, ..)
contextInfo.addInstanceExtension(reqExtensions[ext_id]);
contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true); // FPS in titlebar
contextInfo.addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true); // Allow debug names
contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // Enabling ability to present rendering
// #VKRay: Activate the ray tracing extension
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false, &accelFeature); // To build acceleration structures
VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false, &rtPipelineFeature); // To use vkCmdTraceRaysKHR
contextInfo.addDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); // Required by ray tracing pipeline
// 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 VkSurfaceKHR 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.createSwapchain(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.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths, true));
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true));
helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths, true),
glm::scale(glm::mat4(1.f), glm::vec3(0.5f)) * glm::translate(glm::mat4(1.f), glm::vec3(0.0f, 0.0f, 6.0f)));
std::random_device rd; // Will be used to obtain a seed for the random number engine
std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
std::normal_distribution<float> dis(2.0f, 2.0f);
std::normal_distribution<float> disn(0.5f, 0.2f);
auto wusonIndex = static_cast<int>(helloVk.m_objModel.size() - 1);
for(int n = 0; n < 50; ++n)
{
ObjInstance inst;
inst.objIndex = wusonIndex;
float scale = fabsf(disn(gen));
glm::mat4 mat = glm::translate(glm::mat4(1), glm::vec3{dis(gen), 0.f, dis(gen) + 6});
// mat = mat * glm::rotation_mat4_x(dis(gen));
mat = glm::scale(mat, glm::vec3(scale));
inst.transform = mat;
helloVk.m_instances.push_back(inst);
}
// Creation of implicit geometry
MaterialObj mat;
// Reflective
mat.diffuse = glm::vec3(0, 0, 0);
mat.specular = glm::vec3(1.f);
mat.shininess = 0.0;
mat.illum = 3;
helloVk.addImplMaterial(mat);
// Transparent
mat.diffuse = glm::vec3(0.4, 0.4, 1);
mat.illum = 4;
mat.dissolve = 0.5;
helloVk.addImplMaterial(mat);
helloVk.addImplCube({-6.1, 0, -6}, {-6, 10, 6}, 0);
helloVk.addImplSphere({1, 2, 4}, 1.f, 1);
helloVk.initOffscreen();
Offscreen& offscreen = helloVk.offscreen();
helloVk.createImplictBuffers();
helloVk.createDescriptorSetLayout();
helloVk.createGraphicsPipeline();
helloVk.createUniformBuffer();
helloVk.createObjDescriptionBuffer();
helloVk.updateDescriptorSet();
// #VKRay
helloVk.initRayTracing();
glm::vec4 clearColor = glm::vec4(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();
// Show UI window.
if(helloVk.showGui())
{
ImGuiH::Panel::Begin();
bool changed = false;
// Edit 3 floats representing a color
changed |= ImGui::ColorEdit3("Clear color", reinterpret_cast<float*>(&clearColor));
// Switch between raster and ray tracing
changed |= ImGui::Checkbox("Ray Tracer mode", &useRaytracer);
if(changed)
helloVk.resetFrame();
renderUI(helloVk);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGuiH::Control::Info("", "", "(F10) Toggle Pane", ImGuiH::Control::Flags::Disabled);
ImGuiH::Panel::End();
}
// Start rendering the scene
helloVk.prepareFrame();
// Start command buffer of this frame
auto curFrame = helloVk.getCurFrame();
const VkCommandBuffer& cmdBuf = helloVk.getCommandBuffers()[curFrame];
VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuf, &beginInfo);
// Updating camera buffer
helloVk.updateUniformBuffer(cmdBuf);
// Clearing screen
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{clearColor[0], clearColor[1], clearColor[2], clearColor[3]}};
clearValues[1].depthStencil = {1.0f, 0};
// Offscreen render pass
{
VkRenderPassBeginInfo offscreenRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
offscreenRenderPassBeginInfo.clearValueCount = 2;
offscreenRenderPassBeginInfo.pClearValues = clearValues.data();
offscreenRenderPassBeginInfo.renderPass = offscreen.renderPass();
offscreenRenderPassBeginInfo.framebuffer = offscreen.frameBuffer();
offscreenRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering Scene
if(useRaytracer)
{
helloVk.raytrace(cmdBuf, clearColor);
}
else
{
vkCmdBeginRenderPass(cmdBuf, &offscreenRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.rasterize(cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
}
// 2nd rendering pass: tone mapper, UI
{
VkRenderPassBeginInfo postRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
postRenderPassBeginInfo.clearValueCount = 2;
postRenderPassBeginInfo.pClearValues = clearValues.data();
postRenderPassBeginInfo.renderPass = helloVk.getRenderPass();
postRenderPassBeginInfo.framebuffer = helloVk.getFramebuffers()[curFrame];
postRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering tonemapper
vkCmdBeginRenderPass(cmdBuf, &postRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
offscreen.draw(cmdBuf, helloVk.getSize());
// Rendering UI
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
// Submit for display
vkEndCommandBuffer(cmdBuf);
helloVk.submitFrame();
}
// Cleanup
vkDeviceWaitIdle(helloVk.getDevice());
helloVk.destroyResources();
helloVk.destroy();
vkctx.deinit();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "obj_loader.h"
// The OBJ model
struct ObjModel
{
uint32_t nbIndices{0};
uint32_t nbVertices{0};
nvvk::Buffer vertexBuffer; // Device buffer of all 'Vertex'
nvvk::Buffer indexBuffer; // Device buffer of the indices forming triangles
nvvk::Buffer matColorBuffer; // Device buffer of array of 'Wavefront material'
nvvk::Buffer matIndexBuffer; // Device buffer of array of 'Wavefront material'
};
struct ObjInstance
{
glm::mat4 transform; // Matrix of the instance
uint32_t objIndex{0}; // Model index reference
};
enum EObjType
{
eSphere = 0,
eCube
};
// One single implicit object
struct ObjImplicit
{
glm::vec3 minimum{0, 0, 0}; // Aabb
glm::vec3 maximum{0, 0, 0}; // Aabb
int objType{0}; // 0: Sphere, 1: Cube
int matId{0};
};
// All implicit objects
struct ImplInst
{
std::vector<ObjImplicit> objImpl; // All objects
std::vector<MaterialObj> implMat; // All materials used by implicit obj
nvvk::Buffer implBuf; // Buffer of objects
nvvk::Buffer implMatBuf; // Buffer of material
int blasId{0};
glm::mat4 transform{1};
};

View file

@ -1,191 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "offscreen.hpp"
#include "nvh/fileoperations.hpp"
#include "nvvk/commands_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/images_vk.hpp"
#include "nvvk/pipeline_vk.hpp"
#include "nvvk/renderpasses_vk.hpp"
extern std::vector<std::string> defaultSearchPaths;
//////////////////////////////////////////////////////////////////////////
// Post-processing
//////////////////////////////////////////////////////////////////////////
void Offscreen::setup(const VkDevice& device, const VkPhysicalDevice& physicalDevice, nvvk::ResourceAllocator* allocator, uint32_t queueFamily)
{
m_device = device;
m_alloc = allocator;
m_graphicsQueueIndex = queueFamily;
m_debug.setup(m_device);
m_depthFormat = nvvk::findDepthFormat(physicalDevice);
}
void Offscreen::destroy()
{
vkDestroyPipeline(m_device, m_pipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_descPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_dsetLayout, nullptr);
vkDestroyRenderPass(m_device, m_renderPass, nullptr);
vkDestroyFramebuffer(m_device, m_framebuffer, nullptr);
m_alloc->destroy(m_colorTexture);
m_alloc->destroy(m_depthTexture);
}
//--------------------------------------------------------------------------------------------------
// Creating an offscreen frame buffer and the associated render pass
//
void Offscreen::createFramebuffer(const VkExtent2D& size)
{
m_alloc->destroy(m_colorTexture);
m_alloc->destroy(m_depthTexture);
// Creating the color image
{
auto colorCreateInfo = nvvk::makeImage2DCreateInfo(
size, m_colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT);
nvvk::Image image = m_alloc->createImage(colorCreateInfo);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, colorCreateInfo);
VkSamplerCreateInfo sampler{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
m_colorTexture = m_alloc->createTexture(image, ivInfo, sampler);
m_colorTexture.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
// Creating the depth buffer
{
auto depthCreateInfo = nvvk::makeImage2DCreateInfo(size, m_depthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
nvvk::Image image = m_alloc->createImage(depthCreateInfo);
VkImageViewCreateInfo depthStencilView{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthStencilView.format = m_depthFormat;
depthStencilView.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
depthStencilView.image = image.image;
m_depthTexture = 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_colorTexture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
nvvk::cmdBarrierImageLayout(cmdBuf, m_depthTexture.image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT);
genCmdBuf.submitAndWait(cmdBuf);
}
// Creating a renderpass for the offscreen
if(!m_renderPass)
{
m_renderPass = nvvk::createRenderPass(m_device, {m_colorFormat}, m_depthFormat, 1, true, true,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
}
// Creating the frame buffer for offscreen
std::vector<VkImageView> attachments = {m_colorTexture.descriptor.imageView, m_depthTexture.descriptor.imageView};
vkDestroyFramebuffer(m_device, m_framebuffer, nullptr);
VkFramebufferCreateInfo info{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
info.renderPass = m_renderPass;
info.attachmentCount = 2;
info.pAttachments = attachments.data();
info.width = size.width;
info.height = size.height;
info.layers = 1;
vkCreateFramebuffer(m_device, &info, nullptr, &m_framebuffer);
}
//--------------------------------------------------------------------------------------------------
// The pipeline is how things are rendered, which shaders, type of primitives, depth test and more
// The incoming render pass, is in which rendering pass it will be displayed (framebuffer)
//
void Offscreen::createPipeline(VkRenderPass& renderPass)
{
// Push constants in the fragment shader
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float)};
// Creating the pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
pipelineLayoutCreateInfo.setLayoutCount = 1;
pipelineLayoutCreateInfo.pSetLayouts = &m_dsetLayout;
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRanges;
vkCreatePipelineLayout(m_device, &pipelineLayoutCreateInfo, nullptr, &m_pipelineLayout);
// Pipeline: completely generic, no vertices
nvvk::GraphicsPipelineGeneratorCombined pipelineGenerator(m_device, m_pipelineLayout, renderPass);
pipelineGenerator.addShader(nvh::loadFile("spv/passthrough.vert.spv", true, defaultSearchPaths, true), VK_SHADER_STAGE_VERTEX_BIT);
pipelineGenerator.addShader(nvh::loadFile("spv/post.frag.spv", true, defaultSearchPaths, true), VK_SHADER_STAGE_FRAGMENT_BIT);
pipelineGenerator.rasterizationState.cullMode = VK_CULL_MODE_NONE;
m_pipeline = pipelineGenerator.createPipeline();
m_debug.setObjectName(m_pipeline, "post");
}
//--------------------------------------------------------------------------------------------------
// The descriptor layout is the description of the data that is passed to the vertex or the
// fragment program.
//
void Offscreen::createDescriptor()
{
m_dsetLayoutBinding.addBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
m_dsetLayout = m_dsetLayoutBinding.createLayout(m_device);
m_descPool = m_dsetLayoutBinding.createPool(m_device);
m_dset = nvvk::allocateDescriptorSet(m_device, m_descPool, m_dsetLayout);
}
//--------------------------------------------------------------------------------------------------
// Update the output
//
void Offscreen::updateDescriptorSet()
{
VkWriteDescriptorSet writeDescriptorSets = m_dsetLayoutBinding.makeWrite(m_dset, 0, &m_colorTexture.descriptor);
vkUpdateDescriptorSets(m_device, 1, &writeDescriptorSets, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Draw a full screen quad with the attached image
//
void Offscreen::draw(VkCommandBuffer cmdBuf, const VkExtent2D& size)
{
m_debug.beginLabel(cmdBuf, "Post");
VkViewport viewport{0, 0, (float)size.width, (float)size.height, 0, 1};
vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
VkRect2D scissor{{0, 0}, {size.width, size.height}};
vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
auto aspectRatio = static_cast<float>(size.width) / static_cast<float>(size.height);
vkCmdPushConstants(cmdBuf, m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float), &aspectRatio);
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_dset, 0, nullptr);
vkCmdDraw(cmdBuf, 3, 1, 0, 0);
m_debug.endLabel(cmdBuf);
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "nvvk/debug_util_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
//--------------------------------------------------------------------------------------------------
// Class to render in off-screen framebuffers. Instead of rendering directly to the
// screen back buffer, this class create the output frame buffer 'createFramebuffer',
// and use the pipeline from 'createPipeline' to render a quad 'draw' with the colorTexture
// image
class Offscreen
{
public:
void setup(const VkDevice& device, const VkPhysicalDevice& physicalDevice, nvvk::ResourceAllocator* allocator, uint32_t queueFamily);
void destroy();
void createFramebuffer(const VkExtent2D& size);
void createPipeline(VkRenderPass& renderPass);
void createDescriptor();
void updateDescriptorSet();
void draw(VkCommandBuffer cmdBuf, const VkExtent2D& size);
const VkRenderPass& renderPass() { return m_renderPass; }
const VkFramebuffer& frameBuffer() { return m_framebuffer; }
const nvvk::Texture& colorTexture() { return m_colorTexture; }
private:
nvvk::DescriptorSetBindings m_dsetLayoutBinding;
VkDescriptorPool m_descPool{VK_NULL_HANDLE};
VkDescriptorSetLayout m_dsetLayout{VK_NULL_HANDLE};
VkDescriptorSet m_dset{VK_NULL_HANDLE};
VkPipeline m_pipeline{VK_NULL_HANDLE};
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
VkRenderPass m_renderPass{VK_NULL_HANDLE};
VkFramebuffer m_framebuffer{VK_NULL_HANDLE};
nvvk::Texture m_colorTexture;
VkFormat m_colorFormat{VK_FORMAT_R32G32B32A32_SFLOAT};
nvvk::Texture m_depthTexture;
VkFormat m_depthFormat{VK_FORMAT_X8_D24_UNORM_PACK32};
nvvk::ResourceAllocator* m_alloc{nullptr}; // Allocator for buffer, images, acceleration structures
VkDevice m_device;
int m_graphicsQueueIndex{0};
nvvk::DebugUtil m_debug; // Utility to name objects
};

View file

@ -1,439 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "raytrace.hpp"
#include "nvh/fileoperations.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvh/alignment.hpp"
#include "nvvk/shaders_vk.hpp"
#include "obj_loader.h"
#include "nvvk/buffers_vk.hpp"
extern std::vector<std::string> defaultSearchPaths;
void Raytracer::setup(const VkDevice& device, const VkPhysicalDevice& physicalDevice, nvvk::ResourceAllocator* allocator, uint32_t queueFamily)
{
m_device = device;
m_physicalDevice = physicalDevice;
m_alloc = allocator;
m_graphicsQueueIndex = queueFamily;
// Requesting ray tracing properties
VkPhysicalDeviceProperties2 prop2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
prop2.pNext = &m_rtProperties;
vkGetPhysicalDeviceProperties2(m_physicalDevice, &prop2);
m_rtBuilder.setup(m_device, allocator, m_graphicsQueueIndex);
m_sbtWrapper.setup(device, queueFamily, allocator, m_rtProperties);
m_debug.setup(device);
}
void Raytracer::destroy()
{
m_sbtWrapper.destroy();
m_rtBuilder.destroy();
vkDestroyDescriptorPool(m_device, m_rtDescPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_rtDescSetLayout, nullptr);
vkDestroyPipeline(m_device, m_rtPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_rtPipelineLayout, nullptr);
m_alloc->destroy(m_rtSBTBuffer);
}
//--------------------------------------------------------------------------------------------------
// Converting a OBJ primitive to the ray tracing geometry used for the BLAS
//
auto Raytracer::objectToVkGeometryKHR(const ObjModel& model)
{
// Building part
VkDeviceAddress vertexAddress = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
VkDeviceAddress indexAddress = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
VkAccelerationStructureGeometryTrianglesDataKHR triangles{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR};
triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
triangles.vertexData.deviceAddress = vertexAddress;
triangles.vertexStride = sizeof(VertexObj);
triangles.indexType = VK_INDEX_TYPE_UINT32;
triangles.indexData.deviceAddress = indexAddress;
triangles.transformData = {};
triangles.maxVertex = model.nbVertices - 1;
// Setting up the build info of the acceleration
VkAccelerationStructureGeometryKHR asGeom{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
asGeom.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
asGeom.flags = VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR; // For AnyHit
asGeom.geometry.triangles = triangles;
VkAccelerationStructureBuildRangeInfoKHR offset;
offset.firstVertex = 0;
offset.primitiveCount = model.nbIndices / 3; // Nb triangles
offset.primitiveOffset = 0;
offset.transformOffset = 0;
nvvk::RaytracingBuilderKHR::BlasInput input;
input.asGeometry.emplace_back(asGeom);
input.asBuildOffsetInfo.emplace_back(offset);
return input;
}
//--------------------------------------------------------------------------------------------------
// Returning the ray tracing geometry used for the BLAS, containing all spheres
//
auto Raytracer::implicitToVkGeometryKHR(const ImplInst& implicitObj)
{
VkDeviceAddress dataAddress = nvvk::getBufferDeviceAddress(m_device, implicitObj.implBuf.buffer);
VkAccelerationStructureGeometryAabbsDataKHR aabbs{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR};
aabbs.data.deviceAddress = dataAddress;
aabbs.stride = sizeof(ObjImplicit);
// Setting up the build info of the acceleration
VkAccelerationStructureGeometryKHR asGeom{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
asGeom.geometryType = VK_GEOMETRY_TYPE_AABBS_KHR;
asGeom.flags = VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR; // For AnyHit
asGeom.geometry.aabbs = aabbs;
VkAccelerationStructureBuildRangeInfoKHR offset;
offset.firstVertex = 0;
offset.primitiveCount = static_cast<uint32_t>(implicitObj.objImpl.size()); // Nb aabb
offset.primitiveOffset = 0;
offset.transformOffset = 0;
nvvk::RaytracingBuilderKHR::BlasInput input;
input.asGeometry.emplace_back(asGeom);
input.asBuildOffsetInfo.emplace_back(offset);
return input;
}
void Raytracer::createBottomLevelAS(std::vector<ObjModel>& models, ImplInst& implicitObj)
{
// BLAS - Storing each primitive in a geometry
std::vector<nvvk::RaytracingBuilderKHR::BlasInput> allBlas;
allBlas.reserve(models.size());
for(const auto& obj : models)
{
auto blas = objectToVkGeometryKHR(obj);
// We could add more geometry in each BLAS, but we add only one for now
allBlas.emplace_back(blas);
}
// Adding implicit
if(!implicitObj.objImpl.empty())
{
auto blas = implicitToVkGeometryKHR(implicitObj);
allBlas.emplace_back(blas);
implicitObj.blasId = static_cast<int>(allBlas.size() - 1); // remember blas ID for tlas
}
m_rtBuilder.buildBlas(allBlas, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR
| VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR);
}
void Raytracer::createTopLevelAS(std::vector<ObjInstance>& instances, ImplInst& implicitObj)
{
std::vector<VkAccelerationStructureInstanceKHR> tlas;
auto nbObj = static_cast<uint32_t>(instances.size()) - 1; // minus the implicit (for material)
tlas.reserve(instances.size());
for(uint32_t i = 0; i < nbObj; i++)
{
VkAccelerationStructureInstanceKHR rayInst{};
rayInst.transform = nvvk::toTransformMatrixKHR(instances[i].transform); // Position of the instance
rayInst.instanceCustomIndex = instances[i].objIndex; // gl_InstanceCustomIndexEXT
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(instances[i].objIndex);
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF; // Only be hit if rayMask & instance.mask != 0
rayInst.instanceShaderBindingTableRecordOffset = 0; // We will use the same hit group for all objects
tlas.emplace_back(rayInst);
}
// Add the blas containing all implicit
if(!implicitObj.objImpl.empty())
{
VkAccelerationStructureInstanceKHR rayInst{};
rayInst.transform = nvvk::toTransformMatrixKHR(implicitObj.transform); // Position of the instance
rayInst.instanceCustomIndex = instances[nbObj].objIndex;
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(static_cast<uint32_t>(implicitObj.blasId));
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF; // Only be hit if rayMask & instance.mask != 0
rayInst.instanceShaderBindingTableRecordOffset = 1; // We will use the same hit group for all objects (the second one)
tlas.emplace_back(rayInst);
}
m_rtBuilder.buildTlas(tlas, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR);
}
//--------------------------------------------------------------------------------------------------
// This descriptor set holds the Acceleration structure and the output image
//
void Raytracer::createRtDescriptorSet(const VkImageView& outputImage)
{
using vkDSLB = VkDescriptorSetLayoutBinding;
m_rtDescSetLayoutBind.addBinding(RtxBindings::eTlas, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR); // TLAS
m_rtDescSetLayoutBind.addBinding(RtxBindings::eOutImage, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_RAYGEN_BIT_KHR); // Output image
m_rtDescPool = m_rtDescSetLayoutBind.createPool(m_device);
m_rtDescSetLayout = m_rtDescSetLayoutBind.createLayout(m_device);
VkDescriptorSetAllocateInfo allocateInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
allocateInfo.descriptorPool = m_rtDescPool;
allocateInfo.descriptorSetCount = 1;
allocateInfo.pSetLayouts = &m_rtDescSetLayout;
vkAllocateDescriptorSets(m_device, &allocateInfo, &m_rtDescSet);
VkAccelerationStructureKHR tlas = m_rtBuilder.getAccelerationStructure();
VkWriteDescriptorSetAccelerationStructureKHR descASInfo{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR};
descASInfo.accelerationStructureCount = 1;
descASInfo.pAccelerationStructures = &tlas;
VkDescriptorImageInfo imageInfo{{}, outputImage, VK_IMAGE_LAYOUT_GENERAL};
std::vector<VkWriteDescriptorSet> writes;
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eTlas, &descASInfo));
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eOutImage, &imageInfo));
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Writes the output image to the descriptor set
// - Required when changing resolution
//
void Raytracer::updateRtDescriptorSet(const VkImageView& outputImage)
{
VkDescriptorImageInfo imageInfo{{}, outputImage, VK_IMAGE_LAYOUT_GENERAL};
VkWriteDescriptorSet wds = m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eOutImage, &imageInfo);
vkUpdateDescriptorSets(m_device, 1, &wds, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Pipeline for the ray tracer: all shaders, raygen, chit, miss
//
void Raytracer::createRtPipeline(VkDescriptorSetLayout& sceneDescLayout)
{
enum StageIndices
{
eRaygen,
eMiss,
eMiss2,
eClosestHit,
eAnyHit,
eClosestHit1,
eAnyHit1,
eIntersect,
eCall0,
eCall1,
eCall2,
eShaderGroupCount
};
// All stages
std::array<VkPipelineShaderStageCreateInfo, eShaderGroupCount> stages{};
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
stage.pName = "main"; // All the same entry point
// Raygen
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rgen.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
stages[eRaygen] = stage;
// Miss
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rmiss.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_MISS_BIT_KHR;
stages[eMiss] = stage;
// The second miss shader is invoked when a shadow ray misses the geometry. It simply indicates that no occlusion has been found
stage.module =
nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytraceShadow.rmiss.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_MISS_BIT_KHR;
stages[eMiss2] = stage;
// Hit Group - Closest Hit
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rchit.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
stages[eClosestHit] = stage;
// Hit Group - Closest Hit
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rahit.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
stages[eAnyHit] = stage;
// Hit Group - 1
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace2.rchit.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
stages[eClosestHit1] = stage;
// Hit
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace2.rahit.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
stages[eAnyHit1] = stage;
// Hit
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rint.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
stages[eIntersect] = stage;
// Call0
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_point.rcall.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
stages[eCall0] = stage;
// Call1
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_spot.rcall.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
stages[eCall1] = stage;
// Call2
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/light_inf.rcall.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CALLABLE_BIT_KHR;
stages[eCall2] = stage;
// Shader groups
VkRayTracingShaderGroupCreateInfoKHR group{VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR};
group.anyHitShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = VK_SHADER_UNUSED_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.intersectionShader = VK_SHADER_UNUSED_KHR;
// Raygen
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eRaygen;
m_rtShaderGroups.push_back(group);
// Miss
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eMiss;
m_rtShaderGroups.push_back(group);
// Shadow Miss
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eMiss2;
m_rtShaderGroups.push_back(group);
// closest hit shader
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = eClosestHit;
group.anyHitShader = eAnyHit;
m_rtShaderGroups.push_back(group);
// closest hit shader
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = eClosestHit1;
group.anyHitShader = eAnyHit1;
group.intersectionShader = eIntersect;
m_rtShaderGroups.push_back(group);
// Callable shaders
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.closestHitShader = VK_SHADER_UNUSED_KHR;
group.anyHitShader = VK_SHADER_UNUSED_KHR;
group.intersectionShader = VK_SHADER_UNUSED_KHR;
group.generalShader = eCall0;
m_rtShaderGroups.push_back(group);
group.generalShader = eCall1;
m_rtShaderGroups.push_back(group);
group.generalShader = eCall2;
m_rtShaderGroups.push_back(group);
// Push constant: we want to be able to update constants used by the shaders
VkPushConstantRange pushConstant{VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR
| VK_SHADER_STAGE_MISS_BIT_KHR | VK_SHADER_STAGE_CALLABLE_BIT_KHR,
0, sizeof(PushConstantRay)};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstant;
// Descriptor sets: one specific to ray tracing, and one shared with the rasterization pipeline
std::vector<VkDescriptorSetLayout> rtDescSetLayouts = {m_rtDescSetLayout, sceneDescLayout};
pipelineLayoutCreateInfo.setLayoutCount = static_cast<uint32_t>(rtDescSetLayouts.size());
pipelineLayoutCreateInfo.pSetLayouts = rtDescSetLayouts.data();
vkCreatePipelineLayout(m_device, &pipelineLayoutCreateInfo, nullptr, &m_rtPipelineLayout);
// Assemble the shader stages and recursion depth info into the ray tracing pipeline
VkRayTracingPipelineCreateInfoKHR rayPipelineInfo{VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR};
rayPipelineInfo.stageCount = static_cast<uint32_t>(stages.size()); // Stages are shaders
rayPipelineInfo.pStages = stages.data();
rayPipelineInfo.groupCount = static_cast<uint32_t>(m_rtShaderGroups.size());
rayPipelineInfo.pGroups = m_rtShaderGroups.data();
// The ray tracing process can shoot rays from the camera, and a shadow ray can be shot from the
// hit points of the camera rays, hence a recursion level of 2. This number should be kept as low
// as possible for performance reasons. Even recursive ray tracing should be flattened into a loop
// in the ray generation to avoid deep recursion.
rayPipelineInfo.maxPipelineRayRecursionDepth = 2; // Ray depth
rayPipelineInfo.layout = m_rtPipelineLayout;
vkCreateRayTracingPipelinesKHR(m_device, {}, {}, 1, &rayPipelineInfo, nullptr, &m_rtPipeline);
m_sbtWrapper.create(m_rtPipeline, rayPipelineInfo);
for(auto& s : stages)
vkDestroyShaderModule(m_device, s.module, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Ray Tracing the scene
//
void Raytracer::raytrace(const VkCommandBuffer& cmdBuf,
const glm::vec4& clearColor,
VkDescriptorSet& sceneDescSet,
VkExtent2D& size,
PushConstantRaster& sceneConstants)
{
m_debug.beginLabel(cmdBuf, "Ray trace");
// Initializing push constant values
m_pcRay.clearColor = clearColor;
m_pcRay.lightPosition = sceneConstants.lightPosition;
m_pcRay.lightIntensity = sceneConstants.lightIntensity;
m_pcRay.lightDirection = sceneConstants.lightDirection;
m_pcRay.lightSpotCutoff = sceneConstants.lightSpotCutoff;
m_pcRay.lightSpotOuterCutoff = sceneConstants.lightSpotOuterCutoff;
m_pcRay.lightType = sceneConstants.lightType;
m_pcRay.frame = sceneConstants.frame;
std::vector<VkDescriptorSet> descSets{m_rtDescSet, sceneDescSet};
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipelineLayout, 0,
(uint32_t)descSets.size(), descSets.data(), 0, nullptr);
vkCmdPushConstants(cmdBuf, m_rtPipelineLayout,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR
| VK_SHADER_STAGE_CALLABLE_BIT_KHR,
0, sizeof(PushConstantRay), &m_pcRay);
auto& regions = m_sbtWrapper.getRegions();
vkCmdTraceRaysKHR(cmdBuf, &regions[0], &regions[1], &regions[2], &regions[3], size.width, size.height, 1);
m_debug.endLabel(cmdBuf);
}

View file

@ -1,65 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include <glm/glm.hpp>
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/raytraceKHR_vk.hpp"
#include "nvvk/sbtwrapper_vk.hpp"
#include "obj.hpp"
#include "shaders/host_device.h"
class Raytracer
{
public:
void setup(const VkDevice& device, const VkPhysicalDevice& physicalDevice, nvvk::ResourceAllocator* allocator, uint32_t queueFamily);
void destroy();
auto objectToVkGeometryKHR(const ObjModel& model);
auto implicitToVkGeometryKHR(const ImplInst& implicitObj);
void createBottomLevelAS(std::vector<ObjModel>& models, ImplInst& implicitObj);
void createTopLevelAS(std::vector<ObjInstance>& instances, ImplInst& implicitObj);
void createRtDescriptorSet(const VkImageView& outputImage);
void updateRtDescriptorSet(const VkImageView& outputImage);
void createRtPipeline(VkDescriptorSetLayout& sceneDescLayout);
void raytrace(const VkCommandBuffer& cmdBuf, const glm::vec4& clearColor, VkDescriptorSet& sceneDescSet, VkExtent2D& size, PushConstantRaster& sceneConstants);
private:
nvvk::ResourceAllocator* m_alloc{nullptr}; // Allocator for buffer, images, acceleration structures
VkPhysicalDevice m_physicalDevice;
VkDevice m_device;
int m_graphicsQueueIndex{0};
nvvk::DebugUtil m_debug; // Utility to name objects
nvvk::SBTWrapper m_sbtWrapper;
VkPhysicalDeviceRayTracingPipelinePropertiesKHR m_rtProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR};
nvvk::RaytracingBuilderKHR m_rtBuilder;
nvvk::DescriptorSetBindings m_rtDescSetLayoutBind;
VkDescriptorPool m_rtDescPool;
VkDescriptorSetLayout m_rtDescSetLayout;
VkDescriptorSet m_rtDescSet;
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
PushConstantRay m_pcRay{};
};

View file

@ -1,114 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "wavefront.glsl"
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
// clang-format off
// Incoming
layout(location = 1) in vec3 i_worldPos;
layout(location = 2) in vec3 i_worldNrm;
layout(location = 3) in vec3 i_viewDir;
layout(location = 4) in vec2 i_texCoord;
// Outgoing
layout(location = 0) out vec4 o_color;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(binding = eTextures) uniform sampler2D[] textureSamplers;
// clang-format on
void main()
{
// Material of the object
ObjDesc objResource = objDesc.i[pcRaster.objIndex];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
int matIndex = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIndex];
vec3 N = normalize(i_worldNrm);
// Vector toward light
vec3 LightDir;
float lightIntensity;
// Point light
if(pcRaster.lightType == 0)
{
vec3 lDir = pcRaster.lightPosition - i_worldPos;
float lightDistance = length(lDir);
lightIntensity = pcRaster.lightIntensity / (lightDistance * lightDistance);
LightDir = normalize(lDir);
}
else if(pcRaster.lightType == 1)
{
vec3 lDir = pcRaster.lightPosition - i_worldPos;
float lightDistance = length(lDir);
lightIntensity = pcRaster.lightIntensity / (lightDistance * lightDistance);
LightDir = normalize(lDir);
float theta = dot(LightDir, normalize(-pcRaster.lightDirection));
float epsilon = pcRaster.lightSpotCutoff - pcRaster.lightSpotOuterCutoff;
float spotIntensity = clamp((theta - pcRaster.lightSpotOuterCutoff) / epsilon, 0.0, 1.0);
lightIntensity *= spotIntensity;
}
else // Directional light
{
LightDir = normalize(-pcRaster.lightDirection);
lightIntensity = 1.0;
}
// Diffuse
vec3 diffuse = computeDiffuse(mat, LightDir, N);
if(mat.textureId >= 0)
{
int txtOffset = objDesc.i[pcRaster.objIndex].txtOffset;
uint txtId = txtOffset + mat.textureId;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], i_texCoord).xyz;
diffuse *= diffuseTxt;
}
// Specular
vec3 specular = computeSpecular(mat, i_viewDir, LightDir, N);
// Result
o_color = vec4(lightIntensity * (diffuse + specular), 1);
}

View file

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

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460 core
#extension GL_EXT_ray_tracing : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "host_device.h"
layout(location = 3) callableDataInEXT rayLight cLight;
layout(push_constant) uniform _PushConstantRay
{
PushConstantRay pcRay;
};
void main()
{
cLight.outLightDistance = 10000000;
cLight.outIntensity = 1.0;
cLight.outLightDir = normalize(-pcRay.lightDirection);
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460 core
#extension GL_EXT_ray_tracing : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "host_device.h"
layout(location = 3) callableDataInEXT rayLight cLight;
layout(push_constant) uniform _PushConstantRay
{
PushConstantRay pcRay;
};
void main()
{
vec3 lDir = pcRay.lightPosition - cLight.inHitPosition;
cLight.outLightDistance = length(lDir);
cLight.outIntensity = pcRay.lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
cLight.outLightDir = normalize(lDir);
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460 core
#extension GL_EXT_ray_tracing : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "host_device.h"
layout(location = 3) callableDataInEXT rayLight cLight;
layout(push_constant) uniform _PushConstantRay
{
PushConstantRay pcRay;
};
void main()
{
vec3 lDir = pcRay.lightPosition - cLight.inHitPosition;
cLight.outLightDistance = length(lDir);
cLight.outIntensity = pcRay.lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
cLight.outLightDir = normalize(lDir);
float theta = dot(cLight.outLightDir, normalize(-pcRay.lightDirection));
float epsilon = pcRay.lightSpotCutoff - pcRay.lightSpotOuterCutoff;
float spotIntensity = clamp((theta - pcRay.lightSpotOuterCutoff) / epsilon, 0.0, 1.0);
cLight.outIntensity *= spotIntensity;
}

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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);
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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));
}

View file

@ -1,53 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
// 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));
}

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
struct hitPayload
{
vec3 hitValue;
uint seed;
int depth;
vec3 attenuation;
int done;
vec3 rayOrigin;
vec3 rayDir;
};
struct rayLight
{
vec3 inHitPosition;
float outLightDistance;
vec3 outLightDir;
float outIntensity;
};
struct Implicit
{
vec3 minimum;
vec3 maximum;
int objType;
int matId;
};
struct Sphere
{
vec3 center;
float radius;
};
struct Aabb
{
vec3 minimum;
vec3 maximum;
};
#define KIND_SPHERE 0
#define KIND_CUBE 1

View file

@ -1,60 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "random.glsl"
#include "raycommon.glsl"
#include "wavefront.glsl"
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
// clang-format on
void main()
{
// Object of this instance
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
int matIdx = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIdx];
if(mat.illum != 4)
return;
uint seed = prd.seed; // We don't want to modify the PRD
if(mat.dissolve == 0.0)
ignoreIntersectionEXT;
else if(rnd(seed) > mat.dissolve)
ignoreIntersectionEXT;
}

View file

@ -1,176 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
hitAttributeEXT vec2 attribs;
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(location = 1) rayPayloadEXT bool isShadowed;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {ivec3 i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eTextures) uniform sampler2D textureSamplers[];
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
layout(location = 3) callableDataEXT rayLight cLight;
void main()
{
// Object data
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
Indices indices = Indices(objResource.indexAddress);
Vertices vertices = Vertices(objResource.vertexAddress);
// Indices of the triangle
ivec3 ind = indices.i[gl_PrimitiveID];
// Vertex of the triangle
Vertex v0 = vertices.v[ind.x];
Vertex v1 = vertices.v[ind.y];
Vertex v2 = vertices.v[ind.z];
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
// Computing the normal at hit position
vec3 normal = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
// Transforming the normal to world space
normal = normalize(vec3(normal * gl_WorldToObjectEXT));
// Computing the coordinates of the hit position
vec3 worldPos = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;
// Transforming the position to world space
worldPos = vec3(gl_ObjectToWorldEXT * vec4(worldPos, 1.0));
cLight.inHitPosition = worldPos;
//#define DONT_USE_CALLABLE
#if defined(DONT_USE_CALLABLE)
// Point light
if(pcRay.lightType == 0)
{
vec3 lDir = pcRay.lightPosition - cLight.inHitPosition;
float lightDistance = length(lDir);
cLight.outIntensity = pcRay.lightIntensity / (lightDistance * lightDistance);
cLight.outLightDir = normalize(lDir);
cLight.outLightDistance = lightDistance;
}
else if(pcRay.lightType == 1)
{
vec3 lDir = pcRay.lightPosition - cLight.inHitPosition;
cLight.outLightDistance = length(lDir);
cLight.outIntensity = pcRay.lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
cLight.outLightDir = normalize(lDir);
float theta = dot(cLight.outLightDir, normalize(-pcRay.lightDirection));
float epsilon = pcRay.lightSpotCutoff - pcRay.lightSpotOuterCutoff;
float spotIntensity = clamp((theta - pcRay.lightSpotOuterCutoff) / epsilon, 0.0, 1.0);
cLight.outIntensity *= spotIntensity;
}
else // Directional light
{
cLight.outLightDir = normalize(-pcRay.lightDirection);
cLight.outIntensity = 1.0;
cLight.outLightDistance = 10000000;
}
#else
executeCallableEXT(pcRay.lightType, 3);
#endif
// Material of the object
int matIdx = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIdx];
// Diffuse
vec3 diffuse = computeDiffuse(mat, cLight.outLightDir, normal);
if(mat.textureId >= 0)
{
uint txtId = mat.textureId + objDesc.i[gl_InstanceCustomIndexEXT].txtOffset;
vec2 texCoord = v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;
diffuse *= texture(textureSamplers[nonuniformEXT(txtId)], texCoord).xyz;
}
vec3 specular = vec3(0);
float attenuation = 1;
// Tracing shadow ray only if the light is visible from the surface
if(dot(normal, cLight.outLightDir) > 0)
{
float tMin = 0.001;
float tMax = cLight.outLightDistance;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 rayDir = cLight.outLightDir;
uint flags = 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, cLight.outLightDir, normal);
}
}
// Reflection
if(mat.illum == 3)
{
vec3 origin = worldPos;
vec3 rayDir = reflect(gl_WorldRayDirectionEXT, normal);
prd.attenuation *= mat.specular;
prd.done = 0;
prd.rayOrigin = origin;
prd.rayDir = rayDir;
}
prd.hitValue = vec3(cLight.outIntensity * attenuation * (diffuse + specular));
}

View file

@ -1,119 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "random.glsl"
#include "raycommon.glsl"
#include "wavefront.glsl"
// clang-format off
layout(location = 0) rayPayloadEXT hitPayload prd;
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 0, binding = eOutImage, rgba32f) uniform image2D image;
layout(set = 1, binding = eGlobals) uniform _GlobalUniforms { GlobalUniforms uni; };
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
const int NBSAMPLES = 5;
void main()
{
// Initialize the random number
uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, pcRay.frame * NBSAMPLES);
prd.seed = seed;
vec3 hitValues = vec3(0);
for(int smpl = 0; smpl < NBSAMPLES; smpl++)
{
float r1 = rnd(seed);
float r2 = rnd(seed);
// Subpixel jitter: send the ray through a different position inside the pixel
// each time, to provide antialiasing.
vec2 subpixel_jitter = pcRay.frame == 0 ? vec2(0.5f, 0.5f) : vec2(r1, r2);
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + subpixel_jitter;
const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy);
vec2 d = inUV * 2.0 - 1.0;
vec4 origin = uni.viewInverse * vec4(0, 0, 0, 1);
vec4 target = uni.projInverse * vec4(d.x, d.y, 1, 1);
vec4 direction = uni.viewInverse * vec4(normalize(target.xyz), 0);
uint rayFlags = gl_RayFlagsNoneEXT;
float tMin = 0.001;
float tMax = 10000.0;
prd.done = 1;
prd.rayOrigin = origin.xyz;
prd.rayDir = direction.xyz;
prd.depth = 0;
prd.hitValue = vec3(0);
prd.attenuation = vec3(1.f, 1.f, 1.f);
for(;;)
{
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)
);
hitValues += prd.hitValue * prd.attenuation;
prd.depth++;
if(prd.done == 1 || prd.depth >= 10)
break;
origin.xyz = prd.rayOrigin;
direction.xyz = prd.rayDir;
prd.done = 1; // Will stop if a reflective material isn't hit
}
}
prd.hitValue = hitValues / NBSAMPLES;
if(pcRay.frame == 0)
{
// First frame, replace the value in the buffer
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(prd.hitValue, 1.f));
}
else
{
// Do accumulation over time
float a = 1.0f / float(pcRay.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));
}
}

View file

@ -1,110 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
hitAttributeEXT vec3 HitAttribute;
layout(set = 1, binding = eImplicits, scalar) buffer allImpl_
{
Implicit i[];
}
allImplicits;
struct Ray
{
vec3 origin;
vec3 direction;
};
// Ray-Sphere intersection
// http://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/
float hitSphere(const Sphere s, const Ray r)
{
vec3 oc = r.origin - s.center;
float a = dot(r.direction, r.direction);
float b = 2.0 * dot(oc, r.direction);
float c = dot(oc, oc) - s.radius * s.radius;
float discriminant = b * b - 4 * a * c;
if(discriminant < 0)
{
return -1.0;
}
else
{
return (-b - sqrt(discriminant)) / (2.0 * a);
}
}
// Ray-AABB intersection
float hitAabb(const Aabb aabb, const Ray r)
{
vec3 invDir = 1.0 / r.direction;
vec3 tbot = invDir * (aabb.minimum - r.origin);
vec3 ttop = invDir * (aabb.maximum - r.origin);
vec3 tmin = min(ttop, tbot);
vec3 tmax = max(ttop, tbot);
float t0 = max(tmin.x, max(tmin.y, tmin.z));
float t1 = min(tmax.x, min(tmax.y, tmax.z));
return t1 > max(t0, 0.0) ? t0 : -1.0;
}
void main()
{
Ray ray;
ray.origin = gl_WorldRayOriginEXT;
ray.direction = gl_WorldRayDirectionEXT;
// Sphere data
Implicit impl = allImplicits.i[gl_PrimitiveID];
float tHit = -1;
int hitKind = impl.objType;
if(hitKind == KIND_SPHERE)
{
Sphere sphere;
sphere.center = (impl.maximum + impl.minimum) * 0.5;
sphere.radius = impl.maximum.y - sphere.center.y;
// Sphere intersection
tHit = hitSphere(sphere, ray);
}
else
{
// AABB intersection
Aabb aabb;
aabb.minimum = impl.minimum;
aabb.maximum = impl.maximum;
tHit = hitAabb(aabb, ray);
}
// Report hit point
if(tHit > 0)
reportIntersectionEXT(tHit, hitKind);
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(push_constant) uniform _PushConstantRay
{
PushConstantRay pcRay;
};
void main()
{
prd.hitValue = pcRay.clearColor.xyz * 0.8;
}

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "random.glsl"
#include "raycommon.glsl"
#include "wavefront.glsl"
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eImplicits, scalar) buffer allImplicits_ {Implicit i[];} allImplicits;
// clang-format on
void main()
{
// Material of the object
Implicit impl = allImplicits.i[gl_PrimitiveID];
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
Materials materials = Materials(objResource.materialAddress);
WaveFrontMaterial mat = materials.m[impl.matId];
if(mat.illum != 4)
return;
uint seed = prd.seed; // We don't want to modify the PRD
if(mat.dissolve == 0.0)
ignoreIntersectionEXT;
else if(rnd(seed) > mat.dissolve)
ignoreIntersectionEXT;
}

View file

@ -1,145 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
hitAttributeEXT vec2 attribs;
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(location = 1) rayPayloadEXT bool isShadowed;
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eImplicits, scalar) buffer allImplicits_ {Implicit i[];} allImplicits;
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
layout(location = 3) callableDataEXT rayLight cLight;
void main()
{
vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
Implicit impl = allImplicits.i[gl_PrimitiveID];
// Computing the normal at hit position
vec3 normal;
if(gl_HitKindEXT == KIND_SPHERE)
{
vec3 center = (impl.maximum + impl.minimum) * 0.5;
normal = normalize(worldPos - center);
}
else if(gl_HitKindEXT == KIND_CUBE)
{
const float epsilon = 0.00001;
if(abs(impl.maximum.x - worldPos.x) < epsilon)
normal = vec3(1, 0, 0);
else if(abs(impl.maximum.y - worldPos.y) < epsilon)
normal = vec3(0, 1, 0);
else if(abs(impl.maximum.z - worldPos.z) < epsilon)
normal = vec3(0, 0, 1);
else if(abs(impl.minimum.x - worldPos.x) < epsilon)
normal = vec3(-1, 0, 0);
else if(abs(impl.minimum.y - worldPos.y) < epsilon)
normal = vec3(0, -1, 0);
else if(abs(impl.minimum.z - worldPos.z) < epsilon)
normal = vec3(0, 0, -1);
}
cLight.inHitPosition = worldPos;
executeCallableEXT(pcRay.lightType, 3);
// Material of the object
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
Materials materials = Materials(objResource.materialAddress);
WaveFrontMaterial mat = materials.m[impl.matId];
// Diffuse
vec3 diffuse = computeDiffuse(mat, cLight.outLightDir, normal);
vec3 specular = vec3(0);
float attenuation = 1;
// Tracing shadow ray only if the light is visible from the surface
if(dot(normal, cLight.outLightDir) > 0)
{
float tMin = 0.001;
float tMax = cLight.outLightDistance;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 rayDir = cLight.outLightDir;
uint flags = 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, cLight.outLightDir, normal);
}
}
// Reflection
if(mat.illum == 3)
{
vec3 origin = worldPos;
vec3 rayDir = reflect(gl_WorldRayDirectionEXT, normal);
prd.attenuation *= mat.specular;
prd.done = 0;
prd.rayOrigin = origin;
prd.rayDir = rayDir;
}
prd.hitValue = vec3(cLight.outIntensity * attenuation * (diffuse + specular));
}

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 1) rayPayloadInEXT bool isShadowed;
void main()
{
isShadowed = false;
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "wavefront.glsl"
layout(binding = 0) uniform _GlobalUniforms
{
GlobalUniforms uni;
};
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
layout(location = 0) in vec3 i_position;
layout(location = 1) in vec3 i_normal;
layout(location = 2) in vec3 i_color;
layout(location = 3) in vec2 i_texCoord;
layout(location = 1) out vec3 o_worldPos;
layout(location = 2) out vec3 o_worldNrm;
layout(location = 3) out vec3 o_viewDir;
layout(location = 4) out vec2 o_texCoord;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
vec3 origin = vec3(uni.viewInverse * vec4(0, 0, 0, 1));
o_worldPos = vec3(pcRaster.modelMatrix * vec4(i_position, 1.0));
o_viewDir = vec3(o_worldPos - origin);
o_texCoord = i_texCoord;
o_worldNrm = mat3(pcRaster.modelMatrix) * i_normal;
gl_Position = uni.viewProj * vec4(o_worldPos, 1.0);
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "host_device.h"
vec3 computeDiffuse(WaveFrontMaterial mat, vec3 lightDir, vec3 normal)
{
// Lambertian
float dotNL = max(dot(normal, lightDir), 0.0);
vec3 c = mat.diffuse * dotNL;
if(mat.illum >= 1)
c += mat.ambient;
return c;
}
vec3 computeSpecular(WaveFrontMaterial mat, vec3 viewDir, vec3 lightDir, vec3 normal)
{
if(mat.illum < 2)
return vec3(0);
// Compute specular only if not in shadow
const float kPi = 3.14159265;
const float kShininess = max(mat.shininess, 4.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(mat.specular * specular);
}

View file

@ -1,80 +0,0 @@
#*****************************************************************************
# Copyright 2020 NVIDIA Corporation. All rights reserved.
#*****************************************************************************
cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
#--------------------------------------------------------------------------------------------------
# Project setting
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(PROJNAME vk_${PROJNAME}_KHR)
project(${PROJNAME} LANGUAGES C CXX)
message(STATUS "-------------------------------")
message(STATUS "Processing Project ${PROJNAME}:")
#--------------------------------------------------------------------------------------------------
# C++ target and defines
set(CMAKE_CXX_STANDARD 17)
add_executable(${PROJNAME})
_add_project_definitions(${PROJNAME})
#--------------------------------------------------------------------------------------------------
# Source files for this project
#
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
file(GLOB EXTRA_COMMON ${TUTO_KHR_DIR}/common/*.*)
list(APPEND COMMON_SOURCE_FILES ${EXTRA_COMMON})
include_directories(${TUTO_KHR_DIR}/common)
#--------------------------------------------------------------------------------------------------
# GLSL to SPIR-V custom build
compile_glsl_directory(
SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders"
DST "${CMAKE_CURRENT_SOURCE_DIR}/spv"
VULKAN_TARGET "vulkan1.2"
DEPENDENCY ${VULKAN_BUILD_DEPENDENCIES}
)
#--------------------------------------------------------------------------------------------------
# Sources
target_sources(${PROJNAME} PUBLIC ${SOURCE_FILES} ${HEADER_FILES})
target_sources(${PROJNAME} PUBLIC ${COMMON_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${PACKAGE_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${GLSL_SOURCES} ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Sub-folders in Visual Studio
#
source_group("Common" FILES ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES})
source_group("Sources" FILES ${SOURCE_FILES})
source_group("Headers" FILES ${HEADER_FILES})
source_group("Shader Sources" FILES ${GLSL_SOURCES})
source_group("Shader Headers" FILES ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Linkage
#
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core)
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.)
#
_finalize_target( ${PROJNAME} )
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/spv")
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/spv")

View file

@ -1,142 +0,0 @@
# NVIDIA Vulkan Ray Tracing Tutorial
This sample is a simple OBJ viewer in Vulkan, without any ray tracing functionality.
It is the starting point of the [ray tracing tutorial](https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/vkrt_tutorial.md.html),
the source of the application where ray tracing will be added.
![](images/vk_ray_tracing__before.png)
Before starting the tutorial and adding what is necessary to enable ray tracing, here is a brief description of how this basic example was created.
## Structure
* main.cpp : creates a Vulkan context, call for loading OBJ files, drawing loop
* hello_vulkan.[cpp|h]: example class that loads and store OBJ in Vulkan buffers, load textures, creates pipelines and render the scene
* [nvvk](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk): library of independent Vulkan helpers.
## Overview
The following describes the function calls in main(). The helpers will not be described here, but following the link should give more details on how they work.
### Window and Vulkan Setup
* Creates a window using [GLFW](https://www.glfw.org/).
* Creates a Vulkan context using the [`nvvk::Context`](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#context_vkhpp)
helper. The result is a `VkInstance`, `VkDevice`, `VkPhisicalDevice`, the queues and the extensions initialized.
* The window was created, but not the surface to draw in this window. This is done using the information from `VkInstance` and `GLFWwindow`. The
`VkSurfaceKHR` will be used to find the proper queue.
### Sample Setup
* Hello vulkan setup:
* [base] Keep a copy of `VkInstance`, `VkDevice`, `VkPhysicalDevice` and graphics Queue Index.
* [base] Create a command pool
* Initialize the [Vulkan resource allocator](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#resourceallocator_vkhpp) (nvvk::alloc).
* Create Swapchain [base]:
* Using the base class method and the help of [`nvvk::Swapchain`](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#swapchain_vkhpp),
initialize the swapchain for rendering onto the surface.
* Finds the surface color and depth format.
* Create *n* `VkFence` for synchronization.
* Create depth buffer [base]: depth buffer image with the format query in the swapchain
* Create render pass [base]:
* Simple render pass, with two attachments: color and depth
* Using swapchain color and depth format.
* Create frame buffers [base]:
* Create *n images* of the number returned by the swapchain
* Create as many frame buffers and attach the images
* Initialize GUI [base]: we are using [Dear ImGui](https://github.com/ocornut/imgui) and
this is doing the initialization for Vulkan.
* Loading [Wavefront .obj file](https://en.wikipedia.org/wiki/Wavefront_.obj_file):
* This is a very simple format, but it allows us to make manual modifications in the file, to load several .obj files and to combine them to obtain a more complex scene. We can also easily create several instances of the loaded objects.
* Loading the .obj is done through the [ObjLoader](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/3e399adf0a3e991795fbdf91e0e6c23b9f492bb8/common/obj_loader.cpp#L26) which is using [tinyobjloader](https://github.com/tinyobjloader/tinyobjloader).
* All vertices are in the form of: position, normal, color, texture coordinates
* Every 3 vertex indices are creating a triangle.
* Every triangle has an index of the material it is using.
* There are M materials and T textures.
* We allocate buffers and staging the transfer to the GPU using our resource allocator.
* We create an instance of the loaded model.
* :warning: We keep the addresses of the created buffers and append this to a vector of `ObjDesc`, which allows easy access to the model information from a shader.
### Offscreen image & Graphic Pipeline
We have a swapchain and window frame buffers, but we will not render the scene directly to them. Instead, we will render to an RGBA32F image, and then render that image to a full screen triangle using a tone mapper. We could have rendered directly to one of the swapchain framebuffers and applied a tone mapper, but this separation will be very useful for rendering G-Buffers and doing something similar with ray tracing.
* Create offscreen render:
* Create a color and depth image, format are VK_FORMAT_R32G32B32A32_SFLOAT and best depth for the physical device using [`nvvk::findDepthFormat`](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#renderpasses_vkhpp).
* Create a render pass to render using those images [`nvvk::createRenderPass`](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#renderpasses_vkhpp).
* Create a frame buffer, to use as a target when rendering offline.
* Create descriptor set layout:
* Using the [`nvvk::DescriptorSetBindings`](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#class-nvvkdescriptorsetbindings), describing the resources that will be used in the vertex and fragment shader.
* Create graphic pipeline:
* Using [`nvvk::GraphicsPipelineGeneratorCombined`](https://github.com/nvpro-samples/nvpro_core/tree/master/nvvk#class-nvvkgraphicspipelinegeneratorcombined)
* Load both vertex and fragment SpirV shaders
* Describe the vertex attributes: pos, nrm, color, uv
* Create uniform buffer:
* The uniform buffer is holding global scene information and is updated at each frame.
* The information in this sample consist of camera matrices and will be
updated in the drawing loop using `updateUniformBuffer()`.
* Create Obj description buffer:
* When loading .obj, we stored the addresses of the OBJ buffers.
* If we have loaded many .obj, the array will be as large as the number of obj loaded.
* Each instance has the index of the OBJ, therefore the information of the model will be easily retrievable in the shader.
* This function creates the buffer holding the vector of `ObjDesc`.
* Update descriptor set:
* The descriptor set has been created, but here we are writing the information, meaning all the buffers and textures we have created.
At this point, the OBJs are loaded and their information is uploaded to the GPU. The graphic pipeline and the information describing the resources are also created.
### Post Pipeline
We need to create a "post pipeline". This is specific to the raster of this sample. As mentioned before, we render in a full screen triangle, the result of the raster and apply a tonemapper. This step will be rendered directly into the framebuffer of the swapchain.
![](images/base_pipeline.png)
* Create post descriptor:
* Adding the image input (result of rendering)
* Create post pipeline:
* Passtrough vertex shader (full screen triangle)
* Fragment tone-mapper shader
* Update post descriptor:
* Writing the image (RGB32F) address
At this point we are ready to start the rendering loop.
* Setup Glfw callback:
* Our base class has many functions to react on Glfw, such as change of window size, mouse and keyboard inputs.
* All callback functions are virtual, which allow to override the base functionality, but for most there isn't anything to do.
* Except for `onResize`, since we render to an intermediate image (RGB32F), this image will need to be re-created with the new size and the post pipeline will need the address of this image. Therefore, there is an overload of this function.
### Rendering Loop
* Render indefinitely until Glfw receive a close action
* Glfw pulling events
* Imgui new frame and rendering (not drawing)
* Prepare Frame
* This is part of the swapchain, we can have a double or triple buffer and
this call will be waiting (fence) to make sure that one swapchain frame buffer
is available.
* Get current frame
* Depending if we have a double or triple buffer, it will return: 0 or 1, or 0, 1 or 2.
* Resources in the base class exist in that amount: color images, frame buffer, command buffer, fence
* Get the command buffer for the current frame (better to re-use command buffers than creating new ones)
* Update the uniform buffer:
* Push the current camera matrices
#### Offscreen
* Start offscreen rendering pass
* We will be rendering (raster) in the RGBA32F image using the offscreen render pass and frame buffer.
* Rasterize
* This is rasterizing the entire scene, all instances of each object.
#### Final
Start another rendering, this time in the swapchain frame buffer
* Use the base class render pass
* Use the swapchain frame buffer
* Render a full-screen triangle with the raster image and tone-mapping it.
* Actually rendering Imgui in Vulkan
* Submit the frame for execution and display
### Clean up
The rest is clean up all allocations

View file

@ -1,568 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include <sstream>
#define STB_IMAGE_IMPLEMENTATION
#include "obj_loader.h"
#include "stb_image.h"
#include "hello_vulkan.h"
#include "nvh/alignment.hpp"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvvk/commands_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/images_vk.hpp"
#include "nvvk/pipeline_vk.hpp"
#include "nvvk/renderpasses_vk.hpp"
#include "nvvk/shaders_vk.hpp"
#include "nvvk/buffers_vk.hpp"
extern std::vector<std::string> defaultSearchPaths;
//--------------------------------------------------------------------------------------------------
// Keep the handle on the device
// Initialize the tool to do all our allocations: buffers, images
//
void HelloVulkan::setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily)
{
AppBaseVk::setup(instance, device, physicalDevice, queueFamily);
m_alloc.init(instance, device, physicalDevice);
m_debug.setup(m_device);
m_offscreenDepthFormat = nvvk::findDepthFormat(physicalDevice);
}
//--------------------------------------------------------------------------------------------------
// Called at each frame to update the camera matrix
//
void HelloVulkan::updateUniformBuffer(const VkCommandBuffer& cmdBuf)
{
// Prepare new UBO contents on host.
const float aspectRatio = m_size.width / static_cast<float>(m_size.height);
GlobalUniforms hostUBO = {};
const auto& view = CameraManip.getMatrix();
glm::mat4 proj = glm::perspectiveRH_ZO(glm::radians(CameraManip.getFov()), aspectRatio, 0.1f, 1000.0f);
proj[1][1] *= -1; // Inverting Y for Vulkan (not needed with perspectiveVK).
hostUBO.viewProj = proj * view;
hostUBO.viewInverse = glm::inverse(view);
hostUBO.projInverse = glm::inverse(proj);
// UBO on the device, and what stages access it.
VkBuffer deviceUBO = m_bGlobals.buffer;
auto uboUsageStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
// Ensure that the modified UBO is not visible to previous frames.
VkBufferMemoryBarrier beforeBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
beforeBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
beforeBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
beforeBarrier.buffer = deviceUBO;
beforeBarrier.offset = 0;
beforeBarrier.size = sizeof(hostUBO);
vkCmdPipelineBarrier(cmdBuf, uboUsageStages, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_DEPENDENCY_DEVICE_GROUP_BIT, 0,
nullptr, 1, &beforeBarrier, 0, nullptr);
// Schedule the host-to-device upload. (hostUBO is copied into the cmd
// buffer so it is okay to deallocate when the function returns).
vkCmdUpdateBuffer(cmdBuf, m_bGlobals.buffer, 0, sizeof(GlobalUniforms), &hostUBO);
// Making sure the updated UBO will be visible.
VkBufferMemoryBarrier afterBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
afterBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
afterBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
afterBarrier.buffer = deviceUBO;
afterBarrier.offset = 0;
afterBarrier.size = sizeof(hostUBO);
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, uboUsageStages, VK_DEPENDENCY_DEVICE_GROUP_BIT, 0,
nullptr, 1, &afterBarrier, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Describing the layout pushed when rendering
//
void HelloVulkan::createDescriptorSetLayout()
{
auto nbTxt = static_cast<uint32_t>(m_textures.size());
// Camera matrices
m_descSetLayoutBind.addBinding(SceneBindings::eGlobals, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_RAYGEN_BIT_KHR);
// Obj descriptions
m_descSetLayoutBind.addBinding(SceneBindings::eObjDescs, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
// Textures
m_descSetLayoutBind.addBinding(SceneBindings::eTextures, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nbTxt,
VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
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<VkWriteDescriptorSet> writes;
// Camera matrices and scene description
VkDescriptorBufferInfo dbiUnif{m_bGlobals.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eGlobals, &dbiUnif));
VkDescriptorBufferInfo dbiSceneDesc{m_bObjDesc.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eObjDescs, &dbiSceneDesc));
// All texture samplers
std::vector<VkDescriptorImageInfo> diit;
for(auto& texture : m_textures)
{
diit.emplace_back(texture.descriptor);
}
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, SceneBindings::eTextures, diit.data()));
// Writing the information
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Creating the pipeline layout
//
void HelloVulkan::createGraphicsPipeline()
{
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstantRaster)};
// Creating the Pipeline Layout
VkPipelineLayoutCreateInfo createInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &m_descSetLayout;
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRanges;
vkCreatePipelineLayout(m_device, &createInfo, nullptr, &m_pipelineLayout);
// 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("spv/vert_shader.vert.spv", true, paths, true), VK_SHADER_STAGE_VERTEX_BIT);
gpb.addShader(nvh::loadFile("spv/frag_shader.frag.spv", true, paths, true), VK_SHADER_STAGE_FRAGMENT_BIT);
gpb.addBindingDescription({0, sizeof(VertexObj)});
gpb.addAttributeDescriptions({
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, pos))},
{1, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, nrm))},
{2, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, color))},
{3, 0, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, texCoord))},
});
m_graphicsPipeline = gpb.createPipeline();
m_debug.setObjectName(m_graphicsPipeline, "Graphics");
}
//--------------------------------------------------------------------------------------------------
// Loading the OBJ file and setting up all buffers
//
void HelloVulkan::loadModel(const std::string& filename, glm::mat4 transform)
{
LOGI("Loading File: %s \n", filename.c_str());
ObjLoader loader;
loader.loadModel(filename);
// Converting from Srgb to linear
for(auto& m : loader.m_materials)
{
m.ambient = glm::pow(m.ambient, glm::vec3(2.2f));
m.diffuse = glm::pow(m.diffuse, glm::vec3(2.2f));
m.specular = glm::pow(m.specular, glm::vec3(2.2f));
}
ObjModel model;
model.nbIndices = static_cast<uint32_t>(loader.m_indices.size());
model.nbVertices = static_cast<uint32_t>(loader.m_vertices.size());
// Create the buffers on Device and copy vertices, indices and materials
nvvk::CommandPool cmdBufGet(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = cmdBufGet.createCommandBuffer();
VkBufferUsageFlags flag = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
model.vertexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_vertices, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | flag);
model.indexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_indices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | flag);
model.matColorBuffer = m_alloc.createBuffer(cmdBuf, loader.m_materials, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
model.matIndexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_matIndx, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
// Creates all textures found and find the offset for this model
auto txtOffset = static_cast<uint32_t>(m_textures.size());
createTextureImages(cmdBuf, loader.m_textures);
cmdBufGet.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
std::string objNb = std::to_string(m_objModel.size());
m_debug.setObjectName(model.vertexBuffer.buffer, (std::string("vertex_" + objNb)));
m_debug.setObjectName(model.indexBuffer.buffer, (std::string("index_" + objNb)));
m_debug.setObjectName(model.matColorBuffer.buffer, (std::string("mat_" + objNb)));
m_debug.setObjectName(model.matIndexBuffer.buffer, (std::string("matIdx_" + objNb)));
// Keeping transformation matrix of the instance
ObjInstance instance;
instance.transform = transform;
instance.objIndex = static_cast<uint32_t>(m_objModel.size());
m_instances.push_back(instance);
// Creating information for device access
ObjDesc desc;
desc.txtOffset = txtOffset;
desc.vertexAddress = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
desc.indexAddress = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
desc.materialAddress = nvvk::getBufferDeviceAddress(m_device, model.matColorBuffer.buffer);
desc.materialIndexAddress = nvvk::getBufferDeviceAddress(m_device, model.matIndexBuffer.buffer);
// Keeping the obj host model and device description
m_objModel.emplace_back(model);
m_objDesc.emplace_back(desc);
}
//--------------------------------------------------------------------------------------------------
// Creating the uniform buffer holding the camera matrices
// - Buffer is host visible
//
void HelloVulkan::createUniformBuffer()
{
m_bGlobals = m_alloc.createBuffer(sizeof(GlobalUniforms), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_debug.setObjectName(m_bGlobals.buffer, "Globals");
}
//--------------------------------------------------------------------------------------------------
// Create a storage buffer containing the description of the scene elements
// - Which geometry is used by which instance
// - Transformation
// - Offset for texture
//
void HelloVulkan::createObjDescriptionBuffer()
{
nvvk::CommandPool cmdGen(m_device, m_graphicsQueueIndex);
auto cmdBuf = cmdGen.createCommandBuffer();
m_bObjDesc = m_alloc.createBuffer(cmdBuf, m_objDesc, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
cmdGen.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
m_debug.setObjectName(m_bObjDesc.buffer, "ObjDescs");
}
//--------------------------------------------------------------------------------------------------
// Creating all textures and samplers
//
void HelloVulkan::createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures)
{
VkSamplerCreateInfo samplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCreateInfo.maxLod = FLT_MAX;
VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
// If no textures are present, create a dummy one to accommodate the pipeline layout
if(textures.empty() && m_textures.empty())
{
nvvk::Texture texture;
std::array<uint8_t, 4> color{255u, 255u, 255u, 255u};
VkDeviceSize bufferSize = sizeof(color);
auto imgSize = VkExtent2D{1, 1};
auto imageCreateInfo = nvvk::makeImage2DCreateInfo(imgSize, format);
// Creating the dummy texture
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, color.data(), imageCreateInfo);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
texture = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
// The image format must be in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
nvvk::cmdBarrierImageLayout(cmdBuf, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_textures.push_back(texture);
}
else
{
// Uploading all images
for(const auto& texture : textures)
{
std::stringstream o;
int texWidth, texHeight, texChannels;
o << "media/textures/" << texture;
std::string txtFile = nvh::findFile(o.str(), defaultSearchPaths, true);
stbi_uc* stbi_pixels = stbi_load(txtFile.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
std::array<stbi_uc, 4> color{255u, 0u, 255u, 255u};
stbi_uc* pixels = stbi_pixels;
// Handle failure
if(!stbi_pixels)
{
texWidth = texHeight = 1;
texChannels = 4;
pixels = reinterpret_cast<stbi_uc*>(color.data());
}
VkDeviceSize bufferSize = static_cast<uint64_t>(texWidth) * texHeight * sizeof(uint8_t) * 4;
auto imgSize = VkExtent2D{(uint32_t)texWidth, (uint32_t)texHeight};
auto imageCreateInfo = nvvk::makeImage2DCreateInfo(imgSize, format, VK_IMAGE_USAGE_SAMPLED_BIT, true);
{
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, pixels, imageCreateInfo);
nvvk::cmdGenerateMipmaps(cmdBuf, image.image, format, imgSize, imageCreateInfo.mipLevels);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
nvvk::Texture texture = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
m_textures.push_back(texture);
}
stbi_image_free(stbi_pixels);
}
}
}
//--------------------------------------------------------------------------------------------------
// Destroying all allocations
//
void HelloVulkan::destroyResources()
{
vkDestroyPipeline(m_device, m_graphicsPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_descPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_descSetLayout, nullptr);
m_alloc.destroy(m_bGlobals);
m_alloc.destroy(m_bObjDesc);
for(auto& m : m_objModel)
{
m_alloc.destroy(m.vertexBuffer);
m_alloc.destroy(m.indexBuffer);
m_alloc.destroy(m.matColorBuffer);
m_alloc.destroy(m.matIndexBuffer);
}
for(auto& t : m_textures)
{
m_alloc.destroy(t);
}
//#Post
m_alloc.destroy(m_offscreenColor);
m_alloc.destroy(m_offscreenDepth);
vkDestroyPipeline(m_device, m_postPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_postPipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_postDescPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_postDescSetLayout, nullptr);
vkDestroyRenderPass(m_device, m_offscreenRenderPass, nullptr);
vkDestroyFramebuffer(m_device, m_offscreenFramebuffer, nullptr);
m_alloc.deinit();
}
//--------------------------------------------------------------------------------------------------
// Drawing the scene in raster mode
//
void HelloVulkan::rasterize(const VkCommandBuffer& cmdBuf)
{
VkDeviceSize offset{0};
m_debug.beginLabel(cmdBuf, "Rasterize");
// Dynamic Viewport
setViewport(cmdBuf);
// Drawing all triangles
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descSet, 0, nullptr);
for(const HelloVulkan::ObjInstance& inst : m_instances)
{
auto& model = m_objModel[inst.objIndex];
m_pcRaster.objIndex = inst.objIndex; // Telling which object is drawn
m_pcRaster.modelMatrix = inst.transform;
vkCmdPushConstants(cmdBuf, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(PushConstantRaster), &m_pcRaster);
vkCmdBindVertexBuffers(cmdBuf, 0, 1, &model.vertexBuffer.buffer, &offset);
vkCmdBindIndexBuffer(cmdBuf, model.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmdBuf, model.nbIndices, 1, 0, 0, 0);
}
m_debug.endLabel(cmdBuf);
}
//--------------------------------------------------------------------------------------------------
// Handling resize of the window
//
void HelloVulkan::onResize(int /*w*/, int /*h*/)
{
createOffscreenRender();
updatePostDescriptorSet();
}
//////////////////////////////////////////////////////////////////////////
// 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_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT
| VK_IMAGE_USAGE_STORAGE_BIT);
nvvk::Image image = m_alloc.createImage(colorCreateInfo);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, colorCreateInfo);
VkSamplerCreateInfo sampler{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
m_offscreenColor = m_alloc.createTexture(image, ivInfo, sampler);
m_offscreenColor.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
// Creating the depth buffer
auto depthCreateInfo = nvvk::makeImage2DCreateInfo(m_size, m_offscreenDepthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
{
nvvk::Image image = m_alloc.createImage(depthCreateInfo);
VkImageViewCreateInfo depthStencilView{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthStencilView.format = m_offscreenDepthFormat;
depthStencilView.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
depthStencilView.image = 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_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
nvvk::cmdBarrierImageLayout(cmdBuf, m_offscreenDepth.image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT);
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_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
}
// Creating the frame buffer for offscreen
std::vector<VkImageView> attachments = {m_offscreenColor.descriptor.imageView, m_offscreenDepth.descriptor.imageView};
vkDestroyFramebuffer(m_device, m_offscreenFramebuffer, nullptr);
VkFramebufferCreateInfo info{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
info.renderPass = m_offscreenRenderPass;
info.attachmentCount = 2;
info.pAttachments = attachments.data();
info.width = m_size.width;
info.height = m_size.height;
info.layers = 1;
vkCreateFramebuffer(m_device, &info, nullptr, &m_offscreenFramebuffer);
}
//--------------------------------------------------------------------------------------------------
// 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
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float)};
// Creating the pipeline layout
VkPipelineLayoutCreateInfo createInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &m_postDescSetLayout;
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRanges;
vkCreatePipelineLayout(m_device, &createInfo, nullptr, &m_postPipelineLayout);
// Pipeline: completely generic, no vertices
nvvk::GraphicsPipelineGeneratorCombined pipelineGenerator(m_device, m_postPipelineLayout, m_renderPass);
pipelineGenerator.addShader(nvh::loadFile("spv/passthrough.vert.spv", true, defaultSearchPaths, true), VK_SHADER_STAGE_VERTEX_BIT);
pipelineGenerator.addShader(nvh::loadFile("spv/post.frag.spv", true, defaultSearchPaths, true), VK_SHADER_STAGE_FRAGMENT_BIT);
pipelineGenerator.rasterizationState.cullMode = VK_CULL_MODE_NONE;
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()
{
m_postDescSetLayoutBind.addBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
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()
{
VkWriteDescriptorSet writeDescriptorSets = m_postDescSetLayoutBind.makeWrite(m_postDescSet, 0, &m_offscreenColor.descriptor);
vkUpdateDescriptorSets(m_device, 1, &writeDescriptorSets, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Draw a full screen quad with the attached image
//
void HelloVulkan::drawPost(VkCommandBuffer cmdBuf)
{
m_debug.beginLabel(cmdBuf, "Post");
setViewport(cmdBuf);
auto aspectRatio = static_cast<float>(m_size.width) / static_cast<float>(m_size.height);
vkCmdPushConstants(cmdBuf, m_postPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float), &aspectRatio);
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_postPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_postPipelineLayout, 0, 1, &m_postDescSet, 0, nullptr);
vkCmdDraw(cmdBuf, 3, 1, 0, 0);
m_debug.endLabel(cmdBuf);
}

View file

@ -1,122 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "nvvkhl/appbase_vk.hpp"
#include "nvvk/debug_util_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/memallocator_dma_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
#include "shaders/host_device.h"
//--------------------------------------------------------------------------------------------------
// 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 nvvkhl::AppBaseVk
{
public:
void setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily) override;
void createDescriptorSetLayout();
void createGraphicsPipeline();
void loadModel(const std::string& filename, glm::mat4 transform = glm::mat4(1));
void updateDescriptorSet();
void createUniformBuffer();
void createObjDescriptionBuffer();
void createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures);
void updateUniformBuffer(const VkCommandBuffer& cmdBuf);
void onResize(int /*w*/, int /*h*/) override;
void destroyResources();
void rasterize(const VkCommandBuffer& cmdBuff);
// The OBJ model
struct ObjModel
{
uint32_t nbIndices{0};
uint32_t nbVertices{0};
nvvk::Buffer vertexBuffer; // Device buffer of all 'Vertex'
nvvk::Buffer indexBuffer; // Device buffer of the indices forming triangles
nvvk::Buffer matColorBuffer; // Device buffer of array of 'Wavefront material'
nvvk::Buffer matIndexBuffer; // Device buffer of array of 'Wavefront material'
};
struct ObjInstance
{
glm::mat4 transform; // Matrix of the instance
uint32_t objIndex{0}; // Model index reference
};
// Information pushed at each draw call
PushConstantRaster m_pcRaster{
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, // Identity matrix
{10.f, 15.f, 8.f}, // light position
0, // instance Id
100.f, // light intensity
0 // light type
};
// Array of objects and instances in the scene
std::vector<ObjModel> m_objModel; // Model on host
std::vector<ObjDesc> m_objDesc; // Model description for device access
std::vector<ObjInstance> m_instances; // Scene model instances
// Graphic pipeline
VkPipelineLayout m_pipelineLayout;
VkPipeline m_graphicsPipeline;
nvvk::DescriptorSetBindings m_descSetLayoutBind;
VkDescriptorPool m_descPool;
VkDescriptorSetLayout m_descSetLayout;
VkDescriptorSet m_descSet;
nvvk::Buffer m_bGlobals; // Device-Host of the camera matrices
nvvk::Buffer m_bObjDesc; // Device buffer of the OBJ descriptions
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
nvvk::ResourceAllocatorDma m_alloc; // Allocator for buffer, images, acceleration structures
nvvk::DebugUtil m_debug; // Utility to name objects
// #Post - Draw the rendered image on a quad using a tonemapper
void createOffscreenRender();
void createPostPipeline();
void createPostDescriptor();
void updatePostDescriptorSet();
void drawPost(VkCommandBuffer cmdBuf);
nvvk::DescriptorSetBindings m_postDescSetLayoutBind;
VkDescriptorPool m_postDescPool{VK_NULL_HANDLE};
VkDescriptorSetLayout m_postDescSetLayout{VK_NULL_HANDLE};
VkDescriptorSet m_postDescSet{VK_NULL_HANDLE};
VkPipeline m_postPipeline{VK_NULL_HANDLE};
VkPipelineLayout m_postPipelineLayout{VK_NULL_HANDLE};
VkRenderPass m_offscreenRenderPass{VK_NULL_HANDLE};
VkFramebuffer m_offscreenFramebuffer{VK_NULL_HANDLE};
nvvk::Texture m_offscreenColor;
nvvk::Texture m_offscreenDepth;
VkFormat m_offscreenColorFormat{VK_FORMAT_R32G32B32A32_SFLOAT};
VkFormat m_offscreenDepthFormat{VK_FORMAT_X8_D24_UNORM_PACK32};
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View file

@ -1,265 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
// 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>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_vulkan.h"
#include "imgui.h"
#include "imgui/imgui_helper.h"
#include "hello_vulkan.h"
#include "imgui/imgui_camera_widget.h"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvpsystem.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)
{
ImGuiH::CameraWidget();
if(ImGui::CollapsingHeader("Light"))
{
ImGui::RadioButton("Point", &helloVk.m_pcRaster.lightType, 0);
ImGui::SameLine();
ImGui::RadioButton("Infinite", &helloVk.m_pcRaster.lightType, 1);
ImGui::SliderFloat3("Position", &helloVk.m_pcRaster.lightPosition.x, -20.f, 20.f);
ImGui::SliderFloat("Intensity", &helloVk.m_pcRaster.lightIntensity, 0.f, 150.f);
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
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, PROJECT_NAME, nullptr, nullptr);
// Setup camera
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
CameraManip.setLookat(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0, 0, 0), glm::vec3(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(PROJECT_NAME);
// Search path for shaders and other media
defaultSearchPaths = {
NVPSystem::exePath() + PROJECT_RELDIRECTORY,
NVPSystem::exePath() + PROJECT_RELDIRECTORY "..",
std::string(PROJECT_NAME),
};
// Vulkan required extensions
assert(glfwVulkanSupported() == 1);
uint32_t count{0};
auto reqExtensions = glfwGetRequiredInstanceExtensions(&count);
// Requesting Vulkan extensions and layers
nvvk::ContextCreateInfo contextInfo;
contextInfo.setVersion(1, 2); // Using Vulkan 1.2
for(uint32_t ext_id = 0; ext_id < count; ext_id++) // Adding required extensions (surface, win32, linux, ..)
contextInfo.addInstanceExtension(reqExtensions[ext_id]);
contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true); // FPS in titlebar
contextInfo.addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true); // Allow debug names
contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // Enabling ability to present rendering
// 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 VkSurfaceKHR 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.createSwapchain(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.loadModel(nvh::findFile("media/scenes/cube_multi.obj", defaultSearchPaths, true));
helloVk.createOffscreenRender();
helloVk.createDescriptorSetLayout();
helloVk.createGraphicsPipeline();
helloVk.createUniformBuffer();
helloVk.createObjDescriptionBuffer();
helloVk.updateDescriptorSet();
helloVk.createPostDescriptor();
helloVk.createPostPipeline();
helloVk.updatePostDescriptorSet();
glm::vec4 clearColor = glm::vec4(1, 1, 1, 1.00f);
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();
// Show UI window.
if(helloVk.showGui())
{
ImGuiH::Panel::Begin();
ImGui::ColorEdit3("Clear color", reinterpret_cast<float*>(&clearColor));
renderUI(helloVk);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGuiH::Control::Info("", "", "(F10) Toggle Pane", ImGuiH::Control::Flags::Disabled);
ImGuiH::Panel::End();
}
// Start rendering the scene
helloVk.prepareFrame();
// Start command buffer of this frame
auto curFrame = helloVk.getCurFrame();
const VkCommandBuffer& cmdBuf = helloVk.getCommandBuffers()[curFrame];
VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuf, &beginInfo);
// Updating camera buffer
helloVk.updateUniformBuffer(cmdBuf);
// Clearing screen
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{clearColor[0], clearColor[1], clearColor[2], clearColor[3]}};
clearValues[1].depthStencil = {1.0f, 0};
// Offscreen render pass
{
VkRenderPassBeginInfo offscreenRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
offscreenRenderPassBeginInfo.clearValueCount = 2;
offscreenRenderPassBeginInfo.pClearValues = clearValues.data();
offscreenRenderPassBeginInfo.renderPass = helloVk.m_offscreenRenderPass;
offscreenRenderPassBeginInfo.framebuffer = helloVk.m_offscreenFramebuffer;
offscreenRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering Scene
vkCmdBeginRenderPass(cmdBuf, &offscreenRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.rasterize(cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
// 2nd rendering pass: tone mapper, UI
{
VkRenderPassBeginInfo postRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
postRenderPassBeginInfo.clearValueCount = 2;
postRenderPassBeginInfo.pClearValues = clearValues.data();
postRenderPassBeginInfo.renderPass = helloVk.getRenderPass();
postRenderPassBeginInfo.framebuffer = helloVk.getFramebuffers()[curFrame];
postRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering tonemapper
vkCmdBeginRenderPass(cmdBuf, &postRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.drawPost(cmdBuf);
// Rendering UI
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
// Submit for display
vkEndCommandBuffer(cmdBuf);
helloVk.submitFrame();
}
// Cleanup
vkDeviceWaitIdle(helloVk.getDevice());
helloVk.destroyResources();
helloVk.destroy();
vkctx.deinit();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "wavefront.glsl"
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
// clang-format off
// Incoming
layout(location = 1) in vec3 i_worldPos;
layout(location = 2) in vec3 i_worldNrm;
layout(location = 3) in vec3 i_viewDir;
layout(location = 4) in vec2 i_texCoord;
// Outgoing
layout(location = 0) out vec4 o_color;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(binding = eTextures) uniform sampler2D[] textureSamplers;
// clang-format on
void main()
{
// Material of the object
ObjDesc objResource = objDesc.i[pcRaster.objIndex];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
int matIndex = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIndex];
vec3 N = normalize(i_worldNrm);
// Vector toward light
vec3 L;
float lightIntensity = pcRaster.lightIntensity;
if(pcRaster.lightType == 0)
{
vec3 lDir = pcRaster.lightPosition - i_worldPos;
float d = length(lDir);
lightIntensity = pcRaster.lightIntensity / (d * d);
L = normalize(lDir);
}
else
{
L = normalize(pcRaster.lightPosition);
}
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, N);
if(mat.textureId >= 0)
{
int txtOffset = objDesc.i[pcRaster.objIndex].txtOffset;
uint txtId = txtOffset + mat.textureId;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], i_texCoord).xyz;
diffuse *= diffuseTxt;
}
// Specular
vec3 specular = computeSpecular(mat, i_viewDir, L, N);
// Result
o_color = vec4(lightIntensity * (diffuse + specular), 1);
}

View file

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

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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);
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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));
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "wavefront.glsl"
layout(binding = 0) uniform _GlobalUniforms
{
GlobalUniforms uni;
};
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
layout(location = 0) in vec3 i_position;
layout(location = 1) in vec3 i_normal;
layout(location = 2) in vec3 i_color;
layout(location = 3) in vec2 i_texCoord;
layout(location = 1) out vec3 o_worldPos;
layout(location = 2) out vec3 o_worldNrm;
layout(location = 3) out vec3 o_viewDir;
layout(location = 4) out vec2 o_texCoord;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
vec3 origin = vec3(uni.viewInverse * vec4(0, 0, 0, 1));
o_worldPos = vec3(pcRaster.modelMatrix * vec4(i_position, 1.0));
o_viewDir = vec3(o_worldPos - origin);
o_texCoord = i_texCoord;
o_worldNrm = mat3(pcRaster.modelMatrix) * i_normal;
gl_Position = uni.viewProj * vec4(o_worldPos, 1.0);
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "host_device.h"
vec3 computeDiffuse(WaveFrontMaterial mat, vec3 lightDir, vec3 normal)
{
// Lambertian
float dotNL = max(dot(normal, lightDir), 0.0);
vec3 c = mat.diffuse * dotNL;
if(mat.illum >= 1)
c += mat.ambient;
return c;
}
vec3 computeSpecular(WaveFrontMaterial mat, vec3 viewDir, vec3 lightDir, vec3 normal)
{
if(mat.illum < 2)
return vec3(0);
// Compute specular only if not in shadow
const float kPi = 3.14159265;
const float kShininess = max(mat.shininess, 4.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(mat.specular * specular);
}

View file

@ -1,80 +0,0 @@
#*****************************************************************************
# Copyright 2020 NVIDIA Corporation. All rights reserved.
#*****************************************************************************
cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
#--------------------------------------------------------------------------------------------------
# Project setting
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(PROJNAME vk_${PROJNAME}_KHR)
project(${PROJNAME} LANGUAGES C CXX)
message(STATUS "-------------------------------")
message(STATUS "Processing Project ${PROJNAME}:")
#--------------------------------------------------------------------------------------------------
# C++ target and defines
set(CMAKE_CXX_STANDARD 17)
add_executable(${PROJNAME})
_add_project_definitions(${PROJNAME})
#--------------------------------------------------------------------------------------------------
# Source files for this project
#
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
file(GLOB EXTRA_COMMON ${TUTO_KHR_DIR}/common/*.*)
list(APPEND COMMON_SOURCE_FILES ${EXTRA_COMMON})
include_directories(${TUTO_KHR_DIR}/common)
#--------------------------------------------------------------------------------------------------
# GLSL to SPIR-V custom build
compile_glsl_directory(
SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders"
DST "${CMAKE_CURRENT_SOURCE_DIR}/spv"
VULKAN_TARGET "vulkan1.2"
DEPENDENCY ${VULKAN_BUILD_DEPENDENCIES}
)
#--------------------------------------------------------------------------------------------------
# Sources
target_sources(${PROJNAME} PUBLIC ${SOURCE_FILES} ${HEADER_FILES})
target_sources(${PROJNAME} PUBLIC ${COMMON_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${PACKAGE_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${GLSL_SOURCES} ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Sub-folders in Visual Studio
#
source_group("Common" FILES ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES})
source_group("Sources" FILES ${SOURCE_FILES})
source_group("Headers" FILES ${HEADER_FILES})
source_group("Shader Sources" FILES ${GLSL_SOURCES})
source_group("Shader Headers" FILES ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Linkage
#
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core)
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.)
#
_finalize_target( ${PROJNAME} )
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/spv")
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/spv")

View file

@ -1,14 +0,0 @@
# NVIDIA Vulkan Ray Tracing Tutorial
This example is the result of the ray tracing tutorial.
The tutorial is adding ray tracing capability to an OBJ rasterizer in Vulkan
If you haven't done it, [**Start Ray Tracing Tutorial**](https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/vkrt_tutorial.md.html).
![resultRaytraceShadowMedieval](../docs/Images/resultRaytraceShadowMedieval.png)
## Going Further
Once the tutorial completed and the basics of ray tracing are in place, other tuturials are going further from this code base.
See all other [additional ray tracing tutorials](../README.md#extra-tutorials)

View file

@ -1,936 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include <sstream>
#define STB_IMAGE_IMPLEMENTATION
#include "obj_loader.h"
#include "stb_image.h"
#include "hello_vulkan.h"
#include "nvh/alignment.hpp"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvvk/commands_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/images_vk.hpp"
#include "nvvk/pipeline_vk.hpp"
#include "nvvk/renderpasses_vk.hpp"
#include "nvvk/shaders_vk.hpp"
#include "nvvk/buffers_vk.hpp"
extern std::vector<std::string> defaultSearchPaths;
//--------------------------------------------------------------------------------------------------
// Keep the handle on the device
// Initialize the tool to do all our allocations: buffers, images
//
void HelloVulkan::setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily)
{
AppBaseVk::setup(instance, device, physicalDevice, queueFamily);
m_alloc.init(instance, device, physicalDevice);
m_debug.setup(m_device);
m_offscreenDepthFormat = nvvk::findDepthFormat(physicalDevice);
}
//--------------------------------------------------------------------------------------------------
// Called at each frame to update the camera matrix
//
void HelloVulkan::updateUniformBuffer(const VkCommandBuffer& cmdBuf)
{
// Prepare new UBO contents on host.
const float aspectRatio = m_size.width / static_cast<float>(m_size.height);
GlobalUniforms hostUBO = {};
const auto& view = CameraManip.getMatrix();
glm::mat4 proj = glm::perspectiveRH_ZO(glm::radians(CameraManip.getFov()), aspectRatio, 0.1f, 1000.0f);
proj[1][1] *= -1; // Inverting Y for Vulkan (not needed with perspectiveVK).
hostUBO.viewProj = proj * view;
hostUBO.viewInverse = glm::inverse(view);
hostUBO.projInverse = glm::inverse(proj);
// UBO on the device, and what stages access it.
VkBuffer deviceUBO = m_bGlobals.buffer;
auto uboUsageStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR;
// Ensure that the modified UBO is not visible to previous frames.
VkBufferMemoryBarrier beforeBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
beforeBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
beforeBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
beforeBarrier.buffer = deviceUBO;
beforeBarrier.offset = 0;
beforeBarrier.size = sizeof(hostUBO);
vkCmdPipelineBarrier(cmdBuf, uboUsageStages, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_DEPENDENCY_DEVICE_GROUP_BIT, 0,
nullptr, 1, &beforeBarrier, 0, nullptr);
// Schedule the host-to-device upload. (hostUBO is copied into the cmd
// buffer so it is okay to deallocate when the function returns).
vkCmdUpdateBuffer(cmdBuf, m_bGlobals.buffer, 0, sizeof(GlobalUniforms), &hostUBO);
// Making sure the updated UBO will be visible.
VkBufferMemoryBarrier afterBarrier{VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER};
afterBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
afterBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
afterBarrier.buffer = deviceUBO;
afterBarrier.offset = 0;
afterBarrier.size = sizeof(hostUBO);
vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, uboUsageStages, VK_DEPENDENCY_DEVICE_GROUP_BIT, 0,
nullptr, 1, &afterBarrier, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Describing the layout pushed when rendering
//
void HelloVulkan::createDescriptorSetLayout()
{
auto nbTxt = static_cast<uint32_t>(m_textures.size());
// Camera matrices
m_descSetLayoutBind.addBinding(SceneBindings::eGlobals, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_RAYGEN_BIT_KHR);
// Obj descriptions
m_descSetLayoutBind.addBinding(SceneBindings::eObjDescs, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
// Textures
m_descSetLayoutBind.addBinding(SceneBindings::eTextures, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, nbTxt,
VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR);
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<VkWriteDescriptorSet> writes;
// Camera matrices and scene description
VkDescriptorBufferInfo dbiUnif{m_bGlobals.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eGlobals, &dbiUnif));
VkDescriptorBufferInfo dbiSceneDesc{m_bObjDesc.buffer, 0, VK_WHOLE_SIZE};
writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, SceneBindings::eObjDescs, &dbiSceneDesc));
// All texture samplers
std::vector<VkDescriptorImageInfo> diit;
for(auto& texture : m_textures)
{
diit.emplace_back(texture.descriptor);
}
writes.emplace_back(m_descSetLayoutBind.makeWriteArray(m_descSet, SceneBindings::eTextures, diit.data()));
// Writing the information
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Creating the pipeline layout
//
void HelloVulkan::createGraphicsPipeline()
{
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstantRaster)};
// Creating the Pipeline Layout
VkPipelineLayoutCreateInfo createInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &m_descSetLayout;
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRanges;
vkCreatePipelineLayout(m_device, &createInfo, nullptr, &m_pipelineLayout);
// 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("spv/vert_shader.vert.spv", true, paths, true), VK_SHADER_STAGE_VERTEX_BIT);
gpb.addShader(nvh::loadFile("spv/frag_shader.frag.spv", true, paths, true), VK_SHADER_STAGE_FRAGMENT_BIT);
gpb.addBindingDescription({0, sizeof(VertexObj)});
gpb.addAttributeDescriptions({
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, pos))},
{1, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, nrm))},
{2, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, color))},
{3, 0, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(VertexObj, texCoord))},
});
m_graphicsPipeline = gpb.createPipeline();
m_debug.setObjectName(m_graphicsPipeline, "Graphics");
}
//--------------------------------------------------------------------------------------------------
// Loading the OBJ file and setting up all buffers
//
void HelloVulkan::loadModel(const std::string& filename, glm::mat4 transform)
{
LOGI("Loading File: %s \n", filename.c_str());
ObjLoader loader;
loader.loadModel(filename);
// Converting from Srgb to linear
for(auto& m : loader.m_materials)
{
m.ambient = glm::pow(m.ambient, glm::vec3(2.2f));
m.diffuse = glm::pow(m.diffuse, glm::vec3(2.2f));
m.specular = glm::pow(m.specular, glm::vec3(2.2f));
}
ObjModel model;
model.nbIndices = static_cast<uint32_t>(loader.m_indices.size());
model.nbVertices = static_cast<uint32_t>(loader.m_vertices.size());
// Create the buffers on Device and copy vertices, indices and materials
nvvk::CommandPool cmdBufGet(m_device, m_graphicsQueueIndex);
VkCommandBuffer cmdBuf = cmdBufGet.createCommandBuffer();
VkBufferUsageFlags flag = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
VkBufferUsageFlags rayTracingFlags = // used also for building acceleration structures
flag | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
model.vertexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_vertices, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | rayTracingFlags);
model.indexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_indices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | rayTracingFlags);
model.matColorBuffer = m_alloc.createBuffer(cmdBuf, loader.m_materials, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
model.matIndexBuffer = m_alloc.createBuffer(cmdBuf, loader.m_matIndx, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | flag);
// Creates all textures found and find the offset for this model
auto txtOffset = static_cast<uint32_t>(m_textures.size());
createTextureImages(cmdBuf, loader.m_textures);
cmdBufGet.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
std::string objNb = std::to_string(m_objModel.size());
m_debug.setObjectName(model.vertexBuffer.buffer, (std::string("vertex_" + objNb)));
m_debug.setObjectName(model.indexBuffer.buffer, (std::string("index_" + objNb)));
m_debug.setObjectName(model.matColorBuffer.buffer, (std::string("mat_" + objNb)));
m_debug.setObjectName(model.matIndexBuffer.buffer, (std::string("matIdx_" + objNb)));
// Keeping transformation matrix of the instance
ObjInstance instance;
instance.transform = transform;
instance.objIndex = static_cast<uint32_t>(m_objModel.size());
m_instances.push_back(instance);
// Creating information for device access
ObjDesc desc;
desc.txtOffset = txtOffset;
desc.vertexAddress = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
desc.indexAddress = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
desc.materialAddress = nvvk::getBufferDeviceAddress(m_device, model.matColorBuffer.buffer);
desc.materialIndexAddress = nvvk::getBufferDeviceAddress(m_device, model.matIndexBuffer.buffer);
// Keeping the obj host model and device description
m_objModel.emplace_back(model);
m_objDesc.emplace_back(desc);
}
//--------------------------------------------------------------------------------------------------
// Creating the uniform buffer holding the camera matrices
// - Buffer is host visible
//
void HelloVulkan::createUniformBuffer()
{
m_bGlobals = m_alloc.createBuffer(sizeof(GlobalUniforms), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
m_debug.setObjectName(m_bGlobals.buffer, "Globals");
}
//--------------------------------------------------------------------------------------------------
// Create a storage buffer containing the description of the scene elements
// - Which geometry is used by which instance
// - Transformation
// - Offset for texture
//
void HelloVulkan::createObjDescriptionBuffer()
{
nvvk::CommandPool cmdGen(m_device, m_graphicsQueueIndex);
auto cmdBuf = cmdGen.createCommandBuffer();
m_bObjDesc = m_alloc.createBuffer(cmdBuf, m_objDesc, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
cmdGen.submitAndWait(cmdBuf);
m_alloc.finalizeAndReleaseStaging();
m_debug.setObjectName(m_bObjDesc.buffer, "ObjDescs");
}
//--------------------------------------------------------------------------------------------------
// Creating all textures and samplers
//
void HelloVulkan::createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures)
{
VkSamplerCreateInfo samplerCreateInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerCreateInfo.maxLod = FLT_MAX;
VkFormat format = VK_FORMAT_R8G8B8A8_SRGB;
// If no textures are present, create a dummy one to accommodate the pipeline layout
if(textures.empty() && m_textures.empty())
{
nvvk::Texture texture;
std::array<uint8_t, 4> color{255u, 255u, 255u, 255u};
VkDeviceSize bufferSize = sizeof(color);
auto imgSize = VkExtent2D{1, 1};
auto imageCreateInfo = nvvk::makeImage2DCreateInfo(imgSize, format);
// Creating the dummy texture
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, color.data(), imageCreateInfo);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
texture = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
// The image format must be in VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
nvvk::cmdBarrierImageLayout(cmdBuf, texture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_textures.push_back(texture);
}
else
{
// Uploading all images
for(const auto& texture : textures)
{
std::stringstream o;
int texWidth, texHeight, texChannels;
o << "media/textures/" << texture;
std::string txtFile = nvh::findFile(o.str(), defaultSearchPaths, true);
stbi_uc* stbi_pixels = stbi_load(txtFile.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
std::array<stbi_uc, 4> color{255u, 0u, 255u, 255u};
stbi_uc* pixels = stbi_pixels;
// Handle failure
if(!stbi_pixels)
{
texWidth = texHeight = 1;
texChannels = 4;
pixels = reinterpret_cast<stbi_uc*>(color.data());
}
VkDeviceSize bufferSize = static_cast<uint64_t>(texWidth) * texHeight * sizeof(uint8_t) * 4;
auto imgSize = VkExtent2D{(uint32_t)texWidth, (uint32_t)texHeight};
auto imageCreateInfo = nvvk::makeImage2DCreateInfo(imgSize, format, VK_IMAGE_USAGE_SAMPLED_BIT, true);
{
nvvk::Image image = m_alloc.createImage(cmdBuf, bufferSize, pixels, imageCreateInfo);
nvvk::cmdGenerateMipmaps(cmdBuf, image.image, format, imgSize, imageCreateInfo.mipLevels);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo);
nvvk::Texture texture = m_alloc.createTexture(image, ivInfo, samplerCreateInfo);
m_textures.push_back(texture);
}
stbi_image_free(stbi_pixels);
}
}
}
//--------------------------------------------------------------------------------------------------
// Destroying all allocations
//
void HelloVulkan::destroyResources()
{
vkDestroyPipeline(m_device, m_graphicsPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_descPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_descSetLayout, nullptr);
m_alloc.destroy(m_bGlobals);
m_alloc.destroy(m_bObjDesc);
for(auto& m : m_objModel)
{
m_alloc.destroy(m.vertexBuffer);
m_alloc.destroy(m.indexBuffer);
m_alloc.destroy(m.matColorBuffer);
m_alloc.destroy(m.matIndexBuffer);
}
for(auto& t : m_textures)
{
m_alloc.destroy(t);
}
//#Post
m_alloc.destroy(m_offscreenColor);
m_alloc.destroy(m_offscreenDepth);
vkDestroyPipeline(m_device, m_postPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_postPipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_postDescPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_postDescSetLayout, nullptr);
vkDestroyRenderPass(m_device, m_offscreenRenderPass, nullptr);
vkDestroyFramebuffer(m_device, m_offscreenFramebuffer, nullptr);
// #VKRay
m_rtBuilder.destroy();
vkDestroyPipeline(m_device, m_rtPipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_rtPipelineLayout, nullptr);
vkDestroyDescriptorPool(m_device, m_rtDescPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_rtDescSetLayout, nullptr);
m_alloc.destroy(m_rtSBTBuffer);
m_alloc.deinit();
}
//--------------------------------------------------------------------------------------------------
// Drawing the scene in raster mode
//
void HelloVulkan::rasterize(const VkCommandBuffer& cmdBuf)
{
VkDeviceSize offset{0};
m_debug.beginLabel(cmdBuf, "Rasterize");
// Dynamic Viewport
setViewport(cmdBuf);
// Drawing all triangles
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_graphicsPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, &m_descSet, 0, nullptr);
for(const HelloVulkan::ObjInstance& inst : m_instances)
{
auto& model = m_objModel[inst.objIndex];
m_pcRaster.objIndex = inst.objIndex; // Telling which object is drawn
m_pcRaster.modelMatrix = inst.transform;
vkCmdPushConstants(cmdBuf, m_pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0,
sizeof(PushConstantRaster), &m_pcRaster);
vkCmdBindVertexBuffers(cmdBuf, 0, 1, &model.vertexBuffer.buffer, &offset);
vkCmdBindIndexBuffer(cmdBuf, model.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmdBuf, model.nbIndices, 1, 0, 0, 0);
}
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_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT
| VK_IMAGE_USAGE_STORAGE_BIT);
nvvk::Image image = m_alloc.createImage(colorCreateInfo);
VkImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, colorCreateInfo);
VkSamplerCreateInfo sampler{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
m_offscreenColor = m_alloc.createTexture(image, ivInfo, sampler);
m_offscreenColor.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
}
// Creating the depth buffer
auto depthCreateInfo = nvvk::makeImage2DCreateInfo(m_size, m_offscreenDepthFormat, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
{
nvvk::Image image = m_alloc.createImage(depthCreateInfo);
VkImageViewCreateInfo depthStencilView{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthStencilView.format = m_offscreenDepthFormat;
depthStencilView.subresourceRange = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1};
depthStencilView.image = 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_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
nvvk::cmdBarrierImageLayout(cmdBuf, m_offscreenDepth.image, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT);
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_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL);
}
// Creating the frame buffer for offscreen
std::vector<VkImageView> attachments = {m_offscreenColor.descriptor.imageView, m_offscreenDepth.descriptor.imageView};
vkDestroyFramebuffer(m_device, m_offscreenFramebuffer, nullptr);
VkFramebufferCreateInfo info{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
info.renderPass = m_offscreenRenderPass;
info.attachmentCount = 2;
info.pAttachments = attachments.data();
info.width = m_size.width;
info.height = m_size.height;
info.layers = 1;
vkCreateFramebuffer(m_device, &info, nullptr, &m_offscreenFramebuffer);
}
//--------------------------------------------------------------------------------------------------
// 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
VkPushConstantRange pushConstantRanges = {VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float)};
// Creating the pipeline layout
VkPipelineLayoutCreateInfo createInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &m_postDescSetLayout;
createInfo.pushConstantRangeCount = 1;
createInfo.pPushConstantRanges = &pushConstantRanges;
vkCreatePipelineLayout(m_device, &createInfo, nullptr, &m_postPipelineLayout);
// Pipeline: completely generic, no vertices
nvvk::GraphicsPipelineGeneratorCombined pipelineGenerator(m_device, m_postPipelineLayout, m_renderPass);
pipelineGenerator.addShader(nvh::loadFile("spv/passthrough.vert.spv", true, defaultSearchPaths, true), VK_SHADER_STAGE_VERTEX_BIT);
pipelineGenerator.addShader(nvh::loadFile("spv/post.frag.spv", true, defaultSearchPaths, true), VK_SHADER_STAGE_FRAGMENT_BIT);
pipelineGenerator.rasterizationState.cullMode = VK_CULL_MODE_NONE;
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()
{
m_postDescSetLayoutBind.addBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
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()
{
VkWriteDescriptorSet writeDescriptorSets = m_postDescSetLayoutBind.makeWrite(m_postDescSet, 0, &m_offscreenColor.descriptor);
vkUpdateDescriptorSets(m_device, 1, &writeDescriptorSets, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Draw a full screen quad with the attached image
//
void HelloVulkan::drawPost(VkCommandBuffer cmdBuf)
{
m_debug.beginLabel(cmdBuf, "Post");
setViewport(cmdBuf);
auto aspectRatio = static_cast<float>(m_size.width) / static_cast<float>(m_size.height);
vkCmdPushConstants(cmdBuf, m_postPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(float), &aspectRatio);
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_postPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_postPipelineLayout, 0, 1, &m_postDescSet, 0, nullptr);
vkCmdDraw(cmdBuf, 3, 1, 0, 0);
m_debug.endLabel(cmdBuf);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------------------------------------
// Initialize Vulkan ray tracing
// #VKRay
void HelloVulkan::initRayTracing()
{
// Requesting ray tracing properties
VkPhysicalDeviceProperties2 prop2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
prop2.pNext = &m_rtProperties;
vkGetPhysicalDeviceProperties2(m_physicalDevice, &prop2);
m_rtBuilder.setup(m_device, &m_alloc, m_graphicsQueueIndex);
}
//--------------------------------------------------------------------------------------------------
// Convert an OBJ model into the ray tracing geometry used to build the BLAS
//
auto HelloVulkan::objectToVkGeometryKHR(const ObjModel& model)
{
// BLAS builder requires raw device addresses.
VkDeviceAddress vertexAddress = nvvk::getBufferDeviceAddress(m_device, model.vertexBuffer.buffer);
VkDeviceAddress indexAddress = nvvk::getBufferDeviceAddress(m_device, model.indexBuffer.buffer);
uint32_t maxPrimitiveCount = model.nbIndices / 3;
// Describe buffer as array of VertexObj.
VkAccelerationStructureGeometryTrianglesDataKHR triangles{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR};
triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; // vec3 vertex position data.
triangles.vertexData.deviceAddress = vertexAddress;
triangles.vertexStride = sizeof(VertexObj);
// Describe index data (32-bit unsigned int)
triangles.indexType = VK_INDEX_TYPE_UINT32;
triangles.indexData.deviceAddress = indexAddress;
// Indicate identity transform by setting transformData to null device pointer.
//triangles.transformData = {};
triangles.maxVertex = model.nbVertices - 1;
// Identify the above data as containing opaque triangles.
VkAccelerationStructureGeometryKHR asGeom{VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
asGeom.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
asGeom.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
asGeom.geometry.triangles = triangles;
// The entire array will be used to build the BLAS.
VkAccelerationStructureBuildRangeInfoKHR offset;
offset.firstVertex = 0;
offset.primitiveCount = maxPrimitiveCount;
offset.primitiveOffset = 0;
offset.transformOffset = 0;
// Our blas is made from only one geometry, but could be made of many geometries
nvvk::RaytracingBuilderKHR::BlasInput input;
input.asGeometry.emplace_back(asGeom);
input.asBuildOffsetInfo.emplace_back(offset);
return input;
}
//--------------------------------------------------------------------------------------------------
//
//
void HelloVulkan::createBottomLevelAS()
{
// BLAS - Storing each primitive in a geometry
std::vector<nvvk::RaytracingBuilderKHR::BlasInput> allBlas;
allBlas.reserve(m_objModel.size());
for(const auto& obj : m_objModel)
{
auto blas = objectToVkGeometryKHR(obj);
// We could add more geometry in each BLAS, but we add only one for now
allBlas.emplace_back(blas);
}
m_rtBuilder.buildBlas(allBlas, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR);
}
//--------------------------------------------------------------------------------------------------
//
//
void HelloVulkan::createTopLevelAS()
{
std::vector<VkAccelerationStructureInstanceKHR> tlas;
tlas.reserve(m_instances.size());
for(const HelloVulkan::ObjInstance& inst : m_instances)
{
VkAccelerationStructureInstanceKHR rayInst{};
rayInst.transform = nvvk::toTransformMatrixKHR(inst.transform); // Position of the instance
rayInst.instanceCustomIndex = inst.objIndex; // gl_InstanceCustomIndexEXT
rayInst.accelerationStructureReference = m_rtBuilder.getBlasDeviceAddress(inst.objIndex);
rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
rayInst.mask = 0xFF; // Only be hit if rayMask & instance.mask != 0
rayInst.instanceShaderBindingTableRecordOffset = 0; // We will use the same hit group for all objects
tlas.emplace_back(rayInst);
}
m_rtBuilder.buildTlas(tlas, VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR);
}
//--------------------------------------------------------------------------------------------------
// This descriptor set holds the Acceleration structure and the output image
//
void HelloVulkan::createRtDescriptorSet()
{
// Top-level acceleration structure, usable by both the ray generation and the closest hit (to shoot shadow rays)
m_rtDescSetLayoutBind.addBinding(RtxBindings::eTlas, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR); // TLAS
m_rtDescSetLayoutBind.addBinding(RtxBindings::eOutImage, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1,
VK_SHADER_STAGE_RAYGEN_BIT_KHR); // Output image
m_rtDescPool = m_rtDescSetLayoutBind.createPool(m_device);
m_rtDescSetLayout = m_rtDescSetLayoutBind.createLayout(m_device);
VkDescriptorSetAllocateInfo allocateInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
allocateInfo.descriptorPool = m_rtDescPool;
allocateInfo.descriptorSetCount = 1;
allocateInfo.pSetLayouts = &m_rtDescSetLayout;
vkAllocateDescriptorSets(m_device, &allocateInfo, &m_rtDescSet);
VkAccelerationStructureKHR tlas = m_rtBuilder.getAccelerationStructure();
VkWriteDescriptorSetAccelerationStructureKHR descASInfo{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR};
descASInfo.accelerationStructureCount = 1;
descASInfo.pAccelerationStructures = &tlas;
VkDescriptorImageInfo imageInfo{{}, m_offscreenColor.descriptor.imageView, VK_IMAGE_LAYOUT_GENERAL};
std::vector<VkWriteDescriptorSet> writes;
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eTlas, &descASInfo));
writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eOutImage, &imageInfo));
vkUpdateDescriptorSets(m_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Writes the output image to the descriptor set
// - Required when changing resolution
//
void HelloVulkan::updateRtDescriptorSet()
{
// (1) Output buffer
VkDescriptorImageInfo imageInfo{{}, m_offscreenColor.descriptor.imageView, VK_IMAGE_LAYOUT_GENERAL};
VkWriteDescriptorSet wds = m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, RtxBindings::eOutImage, &imageInfo);
vkUpdateDescriptorSets(m_device, 1, &wds, 0, nullptr);
}
//--------------------------------------------------------------------------------------------------
// Pipeline for the ray tracer: all shaders, raygen, chit, miss
//
void HelloVulkan::createRtPipeline()
{
enum StageIndices
{
eRaygen,
eMiss,
eMiss2,
eClosestHit,
eShaderGroupCount
};
// All stages
std::array<VkPipelineShaderStageCreateInfo, eShaderGroupCount> stages{};
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
stage.pName = "main"; // All the same entry point
// Raygen
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rgen.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR;
stages[eRaygen] = stage;
// Miss
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rmiss.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_MISS_BIT_KHR;
stages[eMiss] = stage;
// The second miss shader is invoked when a shadow ray misses the geometry. It simply indicates that no occlusion has been found
stage.module =
nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytraceShadow.rmiss.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_MISS_BIT_KHR;
stages[eMiss2] = stage;
// Hit Group - Closest Hit
stage.module = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rchit.spv", true, defaultSearchPaths, true));
stage.stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
stages[eClosestHit] = stage;
// Shader groups
VkRayTracingShaderGroupCreateInfoKHR group{VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR};
group.anyHitShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = VK_SHADER_UNUSED_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.intersectionShader = VK_SHADER_UNUSED_KHR;
// Raygen
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eRaygen;
m_rtShaderGroups.push_back(group);
// Miss
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eMiss;
m_rtShaderGroups.push_back(group);
// Shadow Miss
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = eMiss2;
m_rtShaderGroups.push_back(group);
// closest hit shader
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = eClosestHit;
m_rtShaderGroups.push_back(group);
// Push constant: we want to be able to update constants used by the shaders
VkPushConstantRange pushConstant{VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR,
0, sizeof(PushConstantRay)};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstant;
// Descriptor sets: one specific to ray tracing, and one shared with the rasterization pipeline
std::vector<VkDescriptorSetLayout> rtDescSetLayouts = {m_rtDescSetLayout, m_descSetLayout};
pipelineLayoutCreateInfo.setLayoutCount = static_cast<uint32_t>(rtDescSetLayouts.size());
pipelineLayoutCreateInfo.pSetLayouts = rtDescSetLayouts.data();
vkCreatePipelineLayout(m_device, &pipelineLayoutCreateInfo, nullptr, &m_rtPipelineLayout);
// Assemble the shader stages and recursion depth info into the ray tracing pipeline
VkRayTracingPipelineCreateInfoKHR rayPipelineInfo{VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR};
rayPipelineInfo.stageCount = static_cast<uint32_t>(stages.size()); // Stages are shaders
rayPipelineInfo.pStages = stages.data();
// In this case, m_rtShaderGroups.size() == 4: we have one raygen group,
// two miss shader groups, and one hit group.
rayPipelineInfo.groupCount = static_cast<uint32_t>(m_rtShaderGroups.size());
rayPipelineInfo.pGroups = m_rtShaderGroups.data();
// The ray tracing process can shoot rays from the camera, and a shadow ray can be shot from the
// hit points of the camera rays, hence a recursion level of 2. This number should be kept as low
// as possible for performance reasons. Even recursive ray tracing should be flattened into a loop
// in the ray generation to avoid deep recursion.
rayPipelineInfo.maxPipelineRayRecursionDepth = 2; // Ray depth
rayPipelineInfo.layout = m_rtPipelineLayout;
vkCreateRayTracingPipelinesKHR(m_device, {}, {}, 1, &rayPipelineInfo, nullptr, &m_rtPipeline);
// Spec only guarantees 1 level of "recursion". Check for that sad possibility here.
if(m_rtProperties.maxRayRecursionDepth <= 1)
{
throw std::runtime_error("Device fails to support ray recursion (m_rtProperties.maxRayRecursionDepth <= 1)");
}
for(auto& s : stages)
vkDestroyShaderModule(m_device, s.module, nullptr);
}
//--------------------------------------------------------------------------------------------------
// The Shader Binding Table (SBT)
// - getting all shader handles and write them in a SBT buffer
// - Besides exception, this could be always done like this
//
void HelloVulkan::createRtShaderBindingTable()
{
uint32_t missCount{2};
uint32_t hitCount{1};
auto handleCount = 1 + missCount + hitCount;
uint32_t handleSize = m_rtProperties.shaderGroupHandleSize;
// The SBT (buffer) need to have starting groups to be aligned and handles in the group to be aligned.
uint32_t handleSizeAligned = nvh::align_up(handleSize, m_rtProperties.shaderGroupHandleAlignment);
m_rgenRegion.stride = nvh::align_up(handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_rgenRegion.size = m_rgenRegion.stride; // The size member of pRayGenShaderBindingTable must be equal to its stride member
m_missRegion.stride = handleSizeAligned;
m_missRegion.size = nvh::align_up(missCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
m_hitRegion.stride = handleSizeAligned;
m_hitRegion.size = nvh::align_up(hitCount * handleSizeAligned, m_rtProperties.shaderGroupBaseAlignment);
// Get the shader group handles
uint32_t dataSize = handleCount * handleSize;
std::vector<uint8_t> handles(dataSize);
auto result = vkGetRayTracingShaderGroupHandlesKHR(m_device, m_rtPipeline, 0, handleCount, dataSize, handles.data());
assert(result == VK_SUCCESS);
// Allocate a buffer for storing the SBT.
VkDeviceSize sbtSize = m_rgenRegion.size + m_missRegion.size + m_hitRegion.size + m_callRegion.size;
m_rtSBTBuffer = m_alloc.createBuffer(sbtSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT
| VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
m_debug.setObjectName(m_rtSBTBuffer.buffer, std::string("SBT")); // Give it a debug name for NSight.
// Find the SBT addresses of each group
VkBufferDeviceAddressInfo info{VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, m_rtSBTBuffer.buffer};
VkDeviceAddress sbtAddress = vkGetBufferDeviceAddress(m_device, &info);
m_rgenRegion.deviceAddress = sbtAddress;
m_missRegion.deviceAddress = sbtAddress + m_rgenRegion.size;
m_hitRegion.deviceAddress = sbtAddress + m_rgenRegion.size + m_missRegion.size;
// Helper to retrieve the handle data
auto getHandle = [&](int i) { return handles.data() + i * handleSize; };
// Map the SBT buffer and write in the handles.
auto* pSBTBuffer = reinterpret_cast<uint8_t*>(m_alloc.map(m_rtSBTBuffer));
uint8_t* pData{nullptr};
uint32_t handleIdx{0};
// Raygen
pData = pSBTBuffer;
memcpy(pData, getHandle(handleIdx++), handleSize);
// Miss
pData = pSBTBuffer + m_rgenRegion.size;
for(uint32_t c = 0; c < missCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_missRegion.stride;
}
// Hit
pData = pSBTBuffer + m_rgenRegion.size + m_missRegion.size;
for(uint32_t c = 0; c < hitCount; c++)
{
memcpy(pData, getHandle(handleIdx++), handleSize);
pData += m_hitRegion.stride;
}
m_alloc.unmap(m_rtSBTBuffer);
m_alloc.finalizeAndReleaseStaging();
}
//--------------------------------------------------------------------------------------------------
// Ray Tracing the scene
//
void HelloVulkan::raytrace(const VkCommandBuffer& cmdBuf, const glm::vec4& clearColor)
{
m_debug.beginLabel(cmdBuf, "Ray trace");
// Initializing push constant values
m_pcRay.clearColor = clearColor;
m_pcRay.lightPosition = m_pcRaster.lightPosition;
m_pcRay.lightIntensity = m_pcRaster.lightIntensity;
m_pcRay.lightType = m_pcRaster.lightType;
std::vector<VkDescriptorSet> descSets{m_rtDescSet, m_descSet};
vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipeline);
vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, m_rtPipelineLayout, 0,
(uint32_t)descSets.size(), descSets.data(), 0, nullptr);
vkCmdPushConstants(cmdBuf, m_rtPipelineLayout,
VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR,
0, sizeof(PushConstantRay), &m_pcRay);
vkCmdTraceRaysKHR(cmdBuf, &m_rgenRegion, &m_missRegion, &m_hitRegion, &m_callRegion, m_size.width, m_size.height, 1);
m_debug.endLabel(cmdBuf);
}

View file

@ -1,156 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "nvvkhl/appbase_vk.hpp"
#include "nvvk/debug_util_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/memallocator_dma_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
#include "shaders/host_device.h"
// #VKRay
#include "nvvk/raytraceKHR_vk.hpp"
//--------------------------------------------------------------------------------------------------
// 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 nvvkhl::AppBaseVk
{
public:
void setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily) override;
void createDescriptorSetLayout();
void createGraphicsPipeline();
void loadModel(const std::string& filename, glm::mat4 transform = glm::mat4(1));
void updateDescriptorSet();
void createUniformBuffer();
void createObjDescriptionBuffer();
void createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures);
void updateUniformBuffer(const VkCommandBuffer& cmdBuf);
void onResize(int /*w*/, int /*h*/) override;
void destroyResources();
void rasterize(const VkCommandBuffer& cmdBuff);
// The OBJ model
struct ObjModel
{
uint32_t nbIndices{0};
uint32_t nbVertices{0};
nvvk::Buffer vertexBuffer; // Device buffer of all 'Vertex'
nvvk::Buffer indexBuffer; // Device buffer of the indices forming triangles
nvvk::Buffer matColorBuffer; // Device buffer of array of 'Wavefront material'
nvvk::Buffer matIndexBuffer; // Device buffer of array of 'Wavefront material'
};
struct ObjInstance
{
glm::mat4 transform; // Matrix of the instance
uint32_t objIndex{0}; // Model index reference
};
// Information pushed at each draw call
PushConstantRaster m_pcRaster{
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, // Identity matrix
{10.f, 15.f, 8.f}, // light position
0, // instance Id
100.f, // light intensity
0 // light type
};
// Array of objects and instances in the scene
std::vector<ObjModel> m_objModel; // Model on host
std::vector<ObjDesc> m_objDesc; // Model description for device access
std::vector<ObjInstance> m_instances; // Scene model instances
// Graphic pipeline
VkPipelineLayout m_pipelineLayout;
VkPipeline m_graphicsPipeline;
nvvk::DescriptorSetBindings m_descSetLayoutBind;
VkDescriptorPool m_descPool;
VkDescriptorSetLayout m_descSetLayout;
VkDescriptorSet m_descSet;
nvvk::Buffer m_bGlobals; // Device-Host of the camera matrices
nvvk::Buffer m_bObjDesc; // Device buffer of the OBJ descriptions
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
nvvk::ResourceAllocatorDma m_alloc; // Allocator for buffer, images, acceleration structures
nvvk::DebugUtil m_debug; // Utility to name objects
// #Post - Draw the rendered image on a quad using a tonemapper
void createOffscreenRender();
void createPostPipeline();
void createPostDescriptor();
void updatePostDescriptorSet();
void drawPost(VkCommandBuffer cmdBuf);
nvvk::DescriptorSetBindings m_postDescSetLayoutBind;
VkDescriptorPool m_postDescPool{VK_NULL_HANDLE};
VkDescriptorSetLayout m_postDescSetLayout{VK_NULL_HANDLE};
VkDescriptorSet m_postDescSet{VK_NULL_HANDLE};
VkPipeline m_postPipeline{VK_NULL_HANDLE};
VkPipelineLayout m_postPipelineLayout{VK_NULL_HANDLE};
VkRenderPass m_offscreenRenderPass{VK_NULL_HANDLE};
VkFramebuffer m_offscreenFramebuffer{VK_NULL_HANDLE};
nvvk::Texture m_offscreenColor;
nvvk::Texture m_offscreenDepth;
VkFormat m_offscreenColorFormat{VK_FORMAT_R32G32B32A32_SFLOAT};
VkFormat m_offscreenDepthFormat{VK_FORMAT_X8_D24_UNORM_PACK32};
// #VKRay
void initRayTracing();
auto objectToVkGeometryKHR(const ObjModel& model);
void createBottomLevelAS();
void createTopLevelAS();
void createRtDescriptorSet();
void updateRtDescriptorSet();
void createRtPipeline();
void createRtShaderBindingTable();
void raytrace(const VkCommandBuffer& cmdBuf, const glm::vec4& clearColor);
VkPhysicalDeviceRayTracingPipelinePropertiesKHR m_rtProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR};
nvvk::RaytracingBuilderKHR m_rtBuilder;
nvvk::DescriptorSetBindings m_rtDescSetLayoutBind;
VkDescriptorPool m_rtDescPool;
VkDescriptorSetLayout m_rtDescSetLayout;
VkDescriptorSet m_rtDescSet;
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::Buffer m_rtSBTBuffer;
VkStridedDeviceAddressRegionKHR m_rgenRegion{};
VkStridedDeviceAddressRegionKHR m_missRegion{};
VkStridedDeviceAddressRegionKHR m_hitRegion{};
VkStridedDeviceAddressRegionKHR m_callRegion{};
// Push constant for ray tracer
PushConstantRay m_pcRay{};
};

View file

@ -1,293 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
// 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>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_vulkan.h"
#include "imgui.h"
#include "imgui/imgui_helper.h"
#include "hello_vulkan.h"
#include "imgui/imgui_camera_widget.h"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvpsystem.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)
{
ImGuiH::CameraWidget();
if(ImGui::CollapsingHeader("Light"))
{
ImGui::RadioButton("Point", &helloVk.m_pcRaster.lightType, 0);
ImGui::SameLine();
ImGui::RadioButton("Infinite", &helloVk.m_pcRaster.lightType, 1);
ImGui::SliderFloat3("Position", &helloVk.m_pcRaster.lightPosition.x, -20.f, 20.f);
ImGui::SliderFloat("Intensity", &helloVk.m_pcRaster.lightIntensity, 0.f, 150.f);
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
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, PROJECT_NAME, nullptr, nullptr);
// Setup camera
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
CameraManip.setLookat(glm::vec3(5, 4, -4), glm::vec3(0, 1, 0), glm::vec3(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(PROJECT_NAME);
// Search path for shaders and other media
defaultSearchPaths = {
NVPSystem::exePath() + PROJECT_RELDIRECTORY,
NVPSystem::exePath() + PROJECT_RELDIRECTORY "..",
std::string(PROJECT_NAME),
};
// Vulkan required extensions
assert(glfwVulkanSupported() == 1);
uint32_t count{0};
auto reqExtensions = glfwGetRequiredInstanceExtensions(&count);
// Requesting Vulkan extensions and layers
nvvk::ContextCreateInfo contextInfo;
contextInfo.setVersion(1, 2); // Using Vulkan 1.2
for(uint32_t ext_id = 0; ext_id < count; ext_id++) // Adding required extensions (surface, win32, linux, ..)
contextInfo.addInstanceExtension(reqExtensions[ext_id]);
contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true); // FPS in titlebar
contextInfo.addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true); // Allow debug names
contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // Enabling ability to present rendering
// #VKRay: Activate the ray tracing extension
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false, &accelFeature); // To build acceleration structures
VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false, &rtPipelineFeature); // To use vkCmdTraceRaysKHR
contextInfo.addDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); // Required by ray tracing pipeline
// 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 VkSurfaceKHR 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.createSwapchain(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.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths, true));
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true));
helloVk.createOffscreenRender();
helloVk.createDescriptorSetLayout();
helloVk.createGraphicsPipeline();
helloVk.createUniformBuffer();
helloVk.createObjDescriptionBuffer();
helloVk.updateDescriptorSet();
// #VKRay
helloVk.initRayTracing();
helloVk.createBottomLevelAS();
helloVk.createTopLevelAS();
helloVk.createRtDescriptorSet();
helloVk.createRtPipeline();
helloVk.createRtShaderBindingTable();
helloVk.createPostDescriptor();
helloVk.createPostPipeline();
helloVk.updatePostDescriptorSet();
glm::vec4 clearColor = glm::vec4(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();
// Show UI window.
if(helloVk.showGui())
{
ImGuiH::Panel::Begin();
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);
ImGuiH::Control::Info("", "", "(F10) Toggle Pane", ImGuiH::Control::Flags::Disabled);
ImGuiH::Panel::End();
}
// Start rendering the scene
helloVk.prepareFrame();
// Start command buffer of this frame
auto curFrame = helloVk.getCurFrame();
const VkCommandBuffer& cmdBuf = helloVk.getCommandBuffers()[curFrame];
VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuf, &beginInfo);
// Updating camera buffer
helloVk.updateUniformBuffer(cmdBuf);
// Clearing screen
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{clearColor[0], clearColor[1], clearColor[2], clearColor[3]}};
clearValues[1].depthStencil = {1.0f, 0};
// Offscreen render pass
{
VkRenderPassBeginInfo offscreenRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
offscreenRenderPassBeginInfo.clearValueCount = 2;
offscreenRenderPassBeginInfo.pClearValues = clearValues.data();
offscreenRenderPassBeginInfo.renderPass = helloVk.m_offscreenRenderPass;
offscreenRenderPassBeginInfo.framebuffer = helloVk.m_offscreenFramebuffer;
offscreenRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering Scene
if(useRaytracer)
{
helloVk.raytrace(cmdBuf, clearColor);
}
else
{
vkCmdBeginRenderPass(cmdBuf, &offscreenRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.rasterize(cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
}
// 2nd rendering pass: tone mapper, UI
{
VkRenderPassBeginInfo postRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
postRenderPassBeginInfo.clearValueCount = 2;
postRenderPassBeginInfo.pClearValues = clearValues.data();
postRenderPassBeginInfo.renderPass = helloVk.getRenderPass();
postRenderPassBeginInfo.framebuffer = helloVk.getFramebuffers()[curFrame];
postRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering tonemapper
vkCmdBeginRenderPass(cmdBuf, &postRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.drawPost(cmdBuf);
// Rendering UI
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
// Submit for display
vkEndCommandBuffer(cmdBuf);
helloVk.submitFrame();
}
// Cleanup
vkDeviceWaitIdle(helloVk.getDevice());
helloVk.destroyResources();
helloVk.destroy();
vkctx.deinit();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "wavefront.glsl"
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
// clang-format off
// Incoming
layout(location = 1) in vec3 i_worldPos;
layout(location = 2) in vec3 i_worldNrm;
layout(location = 3) in vec3 i_viewDir;
layout(location = 4) in vec2 i_texCoord;
// Outgoing
layout(location = 0) out vec4 o_color;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(binding = eTextures) uniform sampler2D[] textureSamplers;
// clang-format on
void main()
{
// Material of the object
ObjDesc objResource = objDesc.i[pcRaster.objIndex];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
int matIndex = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIndex];
vec3 N = normalize(i_worldNrm);
// Vector toward light
vec3 L;
float lightIntensity = pcRaster.lightIntensity;
if(pcRaster.lightType == 0)
{
vec3 lDir = pcRaster.lightPosition - i_worldPos;
float d = length(lDir);
lightIntensity = pcRaster.lightIntensity / (d * d);
L = normalize(lDir);
}
else
{
L = normalize(pcRaster.lightPosition);
}
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, N);
if(mat.textureId >= 0)
{
int txtOffset = objDesc.i[pcRaster.objIndex].txtOffset;
uint txtId = txtOffset + mat.textureId;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], i_texCoord).xyz;
diffuse *= diffuseTxt;
}
// Specular
vec3 specular = computeSpecular(mat, i_viewDir, L, N);
// Result
o_color = vec4(lightIntensity * (diffuse + specular), 1);
}

View file

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

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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);
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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));
}

View file

@ -1,23 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
struct hitPayload
{
vec3 hitValue;
};

View file

@ -1,145 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
hitAttributeEXT vec2 attribs;
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(location = 1) rayPayloadEXT bool isShadowed;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {ivec3 i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eTextures) uniform sampler2D textureSamplers[];
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
void main()
{
// Object data
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
Indices indices = Indices(objResource.indexAddress);
Vertices vertices = Vertices(objResource.vertexAddress);
// Indices of the triangle
ivec3 ind = indices.i[gl_PrimitiveID];
// Vertex of the triangle
Vertex v0 = vertices.v[ind.x];
Vertex v1 = vertices.v[ind.y];
Vertex v2 = vertices.v[ind.z];
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
// Computing the coordinates of the hit position
const vec3 pos = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;
const vec3 worldPos = vec3(gl_ObjectToWorldEXT * vec4(pos, 1.0)); // Transforming the position to world space
// Computing the normal at hit position
const vec3 nrm = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
const vec3 worldNrm = normalize(vec3(nrm * gl_WorldToObjectEXT)); // Transforming the normal to world space
// Vector toward the light
vec3 L;
float lightIntensity = pcRay.lightIntensity;
float lightDistance = 100000.0;
// Point light
if(pcRay.lightType == 0)
{
vec3 lDir = pcRay.lightPosition - worldPos;
lightDistance = length(lDir);
lightIntensity = pcRay.lightIntensity / (lightDistance * lightDistance);
L = normalize(lDir);
}
else // Directional light
{
L = normalize(pcRay.lightPosition);
}
// Material of the object
int matIdx = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIdx];
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, worldNrm);
if(mat.textureId >= 0)
{
uint txtId = mat.textureId + objDesc.i[gl_InstanceCustomIndexEXT].txtOffset;
vec2 texCoord = v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;
diffuse *= texture(textureSamplers[nonuniformEXT(txtId)], texCoord).xyz;
}
vec3 specular = vec3(0);
float attenuation = 1;
// Tracing shadow ray only if the light is visible from the surface
if(dot(worldNrm, 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, worldNrm);
}
}
prd.hitValue = vec3(lightIntensity * attenuation * (diffuse + specular));
}

View file

@ -1,67 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "host_device.h"
// clang-format off
layout(location = 0) rayPayloadEXT hitPayload prd;
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 0, binding = eOutImage, rgba32f) uniform image2D image;
layout(set = 1, binding = eGlobals) uniform _GlobalUniforms { GlobalUniforms uni; };
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
void main()
{
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 = uni.viewInverse * vec4(0, 0, 0, 1);
vec4 target = uni.projInverse * vec4(d.x, d.y, 1, 1);
vec4 direction = uni.viewInverse * vec4(normalize(target.xyz), 0);
uint rayFlags = gl_RayFlagsOpaqueEXT;
float tMin = 0.001;
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));
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(push_constant) uniform _PushConstantRay
{
PushConstantRay pcRay;
};
void main()
{
prd.hitValue = pcRay.clearColor.xyz * 0.8;
}

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 1) rayPayloadInEXT bool isShadowed;
void main()
{
isShadowed = false;
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "wavefront.glsl"
layout(binding = 0) uniform _GlobalUniforms
{
GlobalUniforms uni;
};
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
layout(location = 0) in vec3 i_position;
layout(location = 1) in vec3 i_normal;
layout(location = 2) in vec3 i_color;
layout(location = 3) in vec2 i_texCoord;
layout(location = 1) out vec3 o_worldPos;
layout(location = 2) out vec3 o_worldNrm;
layout(location = 3) out vec3 o_viewDir;
layout(location = 4) out vec2 o_texCoord;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
vec3 origin = vec3(uni.viewInverse * vec4(0, 0, 0, 1));
o_worldPos = vec3(pcRaster.modelMatrix * vec4(i_position, 1.0));
o_viewDir = vec3(o_worldPos - origin);
o_texCoord = i_texCoord;
o_worldNrm = mat3(pcRaster.modelMatrix) * i_normal;
gl_Position = uni.viewProj * vec4(o_worldPos, 1.0);
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "host_device.h"
vec3 computeDiffuse(WaveFrontMaterial mat, vec3 lightDir, vec3 normal)
{
// Lambertian
float dotNL = max(dot(normal, lightDir), 0.0);
vec3 c = mat.diffuse * dotNL;
if(mat.illum >= 1)
c += mat.ambient;
return c;
}
vec3 computeSpecular(WaveFrontMaterial mat, vec3 viewDir, vec3 lightDir, vec3 normal)
{
if(mat.illum < 2)
return vec3(0);
// Compute specular only if not in shadow
const float kPi = 3.14159265;
const float kShininess = max(mat.shininess, 4.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(mat.specular * specular);
}

View file

@ -1,80 +0,0 @@
#*****************************************************************************
# Copyright 2020 NVIDIA Corporation. All rights reserved.
#*****************************************************************************
cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
#--------------------------------------------------------------------------------------------------
# Project setting
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(PROJNAME vk_${PROJNAME}_KHR)
project(${PROJNAME} LANGUAGES C CXX)
message(STATUS "-------------------------------")
message(STATUS "Processing Project ${PROJNAME}:")
#--------------------------------------------------------------------------------------------------
# C++ target and defines
set(CMAKE_CXX_STANDARD 17)
add_executable(${PROJNAME})
_add_project_definitions(${PROJNAME})
#--------------------------------------------------------------------------------------------------
# Source files for this project
#
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
file(GLOB EXTRA_COMMON ${TUTO_KHR_DIR}/common/*.*)
list(APPEND COMMON_SOURCE_FILES ${EXTRA_COMMON})
include_directories(${TUTO_KHR_DIR}/common)
#--------------------------------------------------------------------------------------------------
# GLSL to SPIR-V custom build
compile_glsl_directory(
SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders"
DST "${CMAKE_CURRENT_SOURCE_DIR}/spv"
VULKAN_TARGET "vulkan1.2"
DEPENDENCY ${VULKAN_BUILD_DEPENDENCIES}
)
#--------------------------------------------------------------------------------------------------
# Sources
target_sources(${PROJNAME} PUBLIC ${SOURCE_FILES} ${HEADER_FILES})
target_sources(${PROJNAME} PUBLIC ${COMMON_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${PACKAGE_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${GLSL_SOURCES} ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Sub-folders in Visual Studio
#
source_group("Common" FILES ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES})
source_group("Sources" FILES ${SOURCE_FILES})
source_group("Headers" FILES ${HEADER_FILES})
source_group("Shader Sources" FILES ${GLSL_SOURCES})
source_group("Shader Headers" FILES ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Linkage
#
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core)
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.)
#
_finalize_target( ${PROJNAME} )
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/spv")
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/spv")

View file

@ -1,277 +0,0 @@
# Advanced Compilation
![](images/advanced_compilation.png)
The simplest way of defining ray tracing pipelines is by using monolithic `VkRayTracingPipelineCreateInfoKHR` structures specifying the pipeline stages and shader groups. Even if pipelines share some stages, they are compiled separately. Furthermore, by default `vkCreateRayTracingPipelinesKHR` blocks the current thread until completion:
![](images/high_level_regular_compilation.png)
However, when using multiple pipelines, some of them may have some shader stages in common. Large pipelines may also take a long time to compile on a single thread.
This sample introduces the [VK_KHR_pipeline_library](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_pipeline_library.html) extension to create shader libraries that can be separately compiled and reused in ray tracing pipelines. The compilation of the final pipeline is carried out on multiple threads using the [VK_KHR_deferred_host_operations](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_deferred_host_operations.html) extension.
The code below is based on the [`ray_tracing_specialization`](../ray_tracing_specialization) sample, which introduces compile-time variations of a hit shader.
## Pipeline Library
Using monolithic pipeline definitions all stages are compiled for each pipeline, regardless of potential reuse. The shader groups are then referenced in the Shader Binding Table using their indices:
![](images/regular_pipeline.png)
Pipeline libraries are `VkPipeline` objects that cannot be bound directly. Instead, they can be compiled once and linked into as many pipelines as necessary. The Shader Binding Table of the resulting pipeline references the shader groups of the library as if they had been appended to the groups and stages in the main `VkRayTracingPipelineCreateInfo`
![](images/library.png)
We start by adding the device extension in main()
~~~~ C
contextInfo.addDeviceExtension(VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME);
~~~~
Following by adding a new member in the `HelloVulkan` class:
~~~~ C
// Ray tracing shader library
VkPipeline m_rtShaderLibrary;
~~~~
In `HelloVulkan::createRtPipeline()` the `StageIndices` enumeration describes the indices of the stages defined in the pipeline creation structure. The hit groups will be moved to our library, hence we remove them from the enumeration:
~~~~ C
enum StageIndices
{
eRaygen,
eMiss,
eMiss2,
eShaderGroupCount
};
~~~~
The shader modules will be referenced partly in the main pipeline, and partly in the pipeline library. To ensure proper deletion of the modules after use, we will store their handles in
~~~~ C
// Store the created modules for later cleanup
std::vector<VkShaderModule> modules;
~~~~
Then, after each call to `nvvk::createShaderModule` we store the resulting module:
~~~~ C
modules.push_back(stage.module);
~~~~
The specialization constants sample creates one shader module per specialization. Instead, we load that module once and reuse it for each specialization. Those specializations are then stored in the stages of the pipeline library:
~~~~ C
// Hit Group - Closest Hit
// Create many variation of the closest hit
stage.module = nvvk::createShaderModule(
m_device, nvh::loadFile("spv/raytrace.rchit.spv", true, defaultSearchPaths, true));
modules.push_back(stage.module);
// Store the hit groups for compilation in a separate pipeline library object
std::vector<VkPipelineShaderStageCreateInfo> libraryStages{};
for(uint32_t s = 0; s < (uint32_t)specializations.size(); s++)
{
stage.stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
stage.pSpecializationInfo = specializations[s].getSpecialization();
libraryStages.push_back(stage);
}
~~~~
Similarly, the hit groups will be stored in the library by replacing the storage of the hit groups in `m_rtShaderGroups` by:
~~~~ C
// Shader groups for the pipeline library containing the closest hit shaders
std::vector<VkRayTracingShaderGroupCreateInfoKHR> libraryShaderGroups;
VkRayTracingShaderGroupCreateInfoKHR libraryGroup{VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR};
libraryGroup.anyHitShader = VK_SHADER_UNUSED_KHR;
libraryGroup.closestHitShader = VK_SHADER_UNUSED_KHR;
libraryGroup.generalShader = VK_SHADER_UNUSED_KHR;
libraryGroup.intersectionShader = VK_SHADER_UNUSED_KHR;
libraryGroup.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
// Hit Group - Closest Hit + AnyHit
// Creating many Hit groups, one for each specialization
for(uint32_t s = 0; s < (uint32_t)specializations.size(); s++)
{
// The indices of the stages are local to the pipeline library
libraryGroup.closestHitShader = s; // Using variation of the closest hit
libraryShaderGroups.push_back(libraryGroup);
}
~~~~
It is important to note that the stage indices are local to the pipeline library, regardless of where they will be used in the final pipeline. Those indices will be later offset depending on the contents of the pipeline.
Once the groups and stages are defined we can create the pipeline library. After the creation of the ray tracing pipeline layout, we define the base of the library creation information:
~~~~ C
// Creation of the pipeline library object
VkRayTracingPipelineCreateInfoKHR pipelineLibraryInfo{VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR};
// Flag the object as a pipeline library, which is a specific object that cannot be used directly.
pipelineLibraryInfo.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
// Use the same layout as the target pipeline
pipelineLibraryInfo.layout = m_rtPipelineLayout;
// As for the interface the maximum recursion depth must also be consistent across the pipeline
pipelineLibraryInfo.maxPipelineRayRecursionDepth = 2;
~~~~
Pipeline libraries are technically independent from the pipeline they will be linked into. However, linking can only be achieved by enforcing strong consistency constraints, such as having the same pipeline layout and maximum recursion depth. If the recursion depth differs the compilation of the final pipeline will fail.
In addition, the pipeline libraries need to have the same pipeline interface. This interface defines the maximum amount of data passed across stages:
~~~~ C
// Pipeline libraries need to define an interface, defined by the maximum hit attribute size (typically 2 for
// the built-in triangle intersector) and the maximum payload size (3 floating-point values in this sample).
// Pipeline libraries can be linked into a final pipeline only if their interface matches
VkRayTracingPipelineInterfaceCreateInfoKHR pipelineInterface{VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_INTERFACE_CREATE_INFO_KHR};
pipelineInterface.maxPipelineRayHitAttributeSize = sizeof(glm::vec2);
pipelineInterface.maxPipelineRayPayloadSize = sizeof(glm::vec3);
pipelineLibraryInfo.pLibraryInterface = &pipelineInterface;
~~~~
Finally we provide the stage and shader groups information to the library creation information, and create the pipeline library in the same way as any other pipeline:
~~~~ C
// Shader groups and stages for the library
pipelineLibraryInfo.groupCount = static_cast<uint32_t>(libraryShaderGroups.size());
pipelineLibraryInfo.pGroups = libraryShaderGroups.data();
pipelineLibraryInfo.stageCount = static_cast<uint32_t>(libraryStages.size());
pipelineLibraryInfo.pStages = libraryStages.data();
// Creation of the pipeline library
vkCreateRayTracingPipelinesKHR(m_device, {}, {}, 1, &pipelineLibraryInfo, nullptr, &m_rtShaderLibrary);
~~~~
The pipeline library is now created, but the application cannot run yet: we still need to indicate that the final pipeline will link with our library. Before calling `vkCreateRayTracingPipelinesKHR` for the final pipeline, we insert the reference to the library:
~~~~ C
// The library will be linked into the final pipeline by specifying its handle and shared interface
VkPipelineLibraryCreateInfoKHR inputLibrary{VK_STRUCTURE_TYPE_PIPELINE_LIBRARY_CREATE_INFO_KHR};
inputLibrary.libraryCount = 1;
inputLibrary.pLibraries = &m_rtShaderLibrary;
rayPipelineInfo.pLibraryInfo = &inputLibrary;
rayPipelineInfo.pLibraryInterface = &pipelineInterface;
~~~~
The pipeline is now built from the specified shader groups and stages as well as the library containing the hit groups. The groups and stages are linked together using indices, and indices are local to each library. To avoid collisions the pipeline creation will consider the stages of the libraries as if they had been appended to the stage list of the original `VkRayTracingPipelineCreateInfoKHR`, in the order in which the libraries are defined in `pLibraries`.
Therefore, the Shader Binding Table needs to be updated accordingly, by making the wrapper aware of the contents of the library:
~~~~ C
// The Shader Binding Table is built accounting for the entire pipeline, including the
// stages contained in the library. Passing the library information allows the wrapper
// to shift the shader group indices accordingly
m_sbtWrapper.create(m_rtPipeline, rayPipelineInfo, {pipelineLibraryInfo});
~~~~
At the end of the function we destroy the shader modules using our vector of modules instead of iterating over the stages of the main pipeline:
~~~~ C
// Destroy all the created modules, for both libraries and main pipeline
for(auto& m : modules)
vkDestroyShaderModule(m_device, m, nullptr);
~~~~
The pipeline library has the same lifetime as the pipeline that uses it. The final step of this Section is the destruction of the library in the `HelloVulkan::destroy()` method:
~~~~ C
// Pipeline libraries have the same lifetime as the pipelines that uses them
vkDestroyPipeline(m_device, m_rtShaderLibrary, nullptr);
~~~~
As an exercise, it is possible to create another library containing the other shader stages, and link those libraries together into the pipeline.
## Parallel Compilation Using Deferred Host Operations
By default pipelines compilations are blocking the calling thread until completion:
![Pipeline compilation on a single thread](images/single_threaded_compilation.png)
Ray tracing pipelines are often complex, and can benefit from multithreaded compilation. This can drastically reduce the compilation times of complex pipelines. The deferred host operations extension allows the work involved in `vkCreateRayTracingPipelinesKHR` to be split into multiple threads provided by the application:
![Deferred Host Operations use app-provided threads to parallelize the compilation](images/deferred_host_operations.png)
We start by including the support of C++ threading using `std::async` at the beginning of the source file:
~~~~ C
#include <future>
~~~~
In this sample we will distribute the compilation of the final ray tracing pipeline using a `VkDeferredOperation`, created just before calling `vkCreateRayTracingPipelinesKHR`:
~~~~ C
// Deferred operations allow the driver to parallelize the pipeline compilation on several threads
// Create a deferred operation
VkDeferredOperationKHR deferredOperation;
VkResult result = vkCreateDeferredOperationKHR(m_device, nullptr, &deferredOperation);
assert(result == VK_SUCCESS);
~~~~
Then we modify the pipeline creation to indicate we defer the operation:
~~~~ C
// The pipeline creation is called with the deferred operation. Instead of blocking until
// the compilation is done, the call returns immediately
vkCreateRayTracingPipelinesKHR(m_device, deferredOperation, {}, 1, &rayPipelineInfo, nullptr, &m_rtPipeline);
~~~~
Instead of immediately launching the compilation and blocking execution until completion, this call will return immediately with value `VK_OPERATION_DEFERRED_KHR` if deferred operations are supported by the system.
Threading control is left to the application. Therefore, our application will allocate a number of threads for compilation:
~~~~ C
// The compilation will be split into a maximum of 8 threads, or the maximum supported by the
// driver for that operation
uint32_t maxThreads{8};
uint32_t threadCount = std::min(vkGetDeferredOperationMaxConcurrencyKHR(m_device, deferredOperation), maxThreads);
~~~~
We then launch those threads using `std::async`:
~~~~ C
std::vector<std::future<void>> joins;
for(uint32_t i = 0; i < threadCount; i++)
{
VkDevice device{m_device};
joins.emplace_back(std::async(std::launch::async, [device, deferredOperation]() {
// Wait until the thread has finished its work
VkResult result = vkDeferredOperationJoinKHR(device, deferredOperation);
// A return value of SUCCESS means the pipeline compilation is done.
// THREAD_DONE indicates that thread has no work to do for this task
// (e.g. the operation could not be split into that many threads)
// THREAD_IDLE indicates the thread has finished its task, but the overall pipeline
// compilation is not finished.
// In the last two cases, more work could be performed by the thread, such as waiting
// for another deferred operation
assert(result == VK_SUCCESS || result == VK_THREAD_DONE_KHR || result == VK_THREAD_IDLE_KHR);
}));
}
~~~~
Each thread executes a blocking function taking care of a subset of the compilation. When a thread has finished its task, the pipeline compilation may be complete (`VK_SUCCESS`) or there may be no more work for this thread. In this case one could consider executing more work using those threads, such as compiling another pipeline.
Since there is only one pipeline to compile, we wait for all threads to finish and check whether the pipeline compilation succeeded:
~~~~ C
// Wait for all threads to finish
for(auto& f : joins)
{
f.get();
}
// Once the deferred operation is complete, check for compilation success
result = vkGetDeferredOperationResultKHR(m_device, deferredOperation);
assert(result == VK_SUCCESS);
~~~~
Once the compilation is finished we can destroy the deferred operation:
~~~~ C
// Destroy the deferred operation
vkDestroyDeferredOperationKHR(m_device, deferredOperation, nullptr);
~~~~
Congratulations! The ray tracing pipeline is now built using explicit stages and a pipeline library, and the final compilation is executed on multiple threads. As an exercise, the pipeline library described at the beginning of this tutorial can also be compiled in parallel.
This approach can be extended to compile multiple pipelines sharing some components using multiple threads:
![](images/high_level_advanced_compilation.png)
## References
* [VK_KHR_pipeline_library](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_pipeline_library.html)
* [VK_KHR_deferred_host_operations](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_deferred_host_operations.html)

File diff suppressed because it is too large Load diff

View file

@ -1,155 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "nvvkhl/appbase_vk.hpp"
#include "nvvk/debug_util_vk.hpp"
#include "nvvk/descriptorsets_vk.hpp"
#include "nvvk/memallocator_dma_vk.hpp"
#include "nvvk/resourceallocator_vk.hpp"
#include "shaders/host_device.h"
// #VKRay
#include "nvvk/raytraceKHR_vk.hpp"
#include "nvvk/sbtwrapper_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 nvvkhl::AppBaseVk
{
public:
void setup(const VkInstance& instance, const VkDevice& device, const VkPhysicalDevice& physicalDevice, uint32_t queueFamily) override;
void createDescriptorSetLayout();
void createGraphicsPipeline();
void loadModel(const std::string& filename, glm::mat4 transform = glm::mat4(1));
void updateDescriptorSet();
void createUniformBuffer();
void createObjDescriptionBuffer();
void createTextureImages(const VkCommandBuffer& cmdBuf, const std::vector<std::string>& textures);
void updateUniformBuffer(const VkCommandBuffer& cmdBuf);
void onResize(int /*w*/, int /*h*/) override;
void destroyResources();
void rasterize(const VkCommandBuffer& cmdBuff);
// The OBJ model
struct ObjModel
{
uint32_t nbIndices{0};
uint32_t nbVertices{0};
nvvk::Buffer vertexBuffer; // Device buffer of all 'Vertex'
nvvk::Buffer indexBuffer; // Device buffer of the indices forming triangles
nvvk::Buffer matColorBuffer; // Device buffer of array of 'Wavefront material'
nvvk::Buffer matIndexBuffer; // Device buffer of array of 'Wavefront material'
};
struct ObjInstance
{
glm::mat4 transform; // Matrix of the instance
uint32_t objIndex{0}; // Model index reference
};
// Information pushed at each draw call
PushConstantRaster m_pcRaster{
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, // Identity matrix
{10.f, 15.f, 8.f}, // light position
0, // instance Id
100.f, // light intensity
0 // light type
};
// Array of objects and instances in the scene
std::vector<ObjModel> m_objModel; // Model on host
std::vector<ObjDesc> m_objDesc; // Model description for device access
std::vector<ObjInstance> m_instances; // Scene model instances
// Graphic pipeline
VkPipelineLayout m_pipelineLayout;
VkPipeline m_graphicsPipeline;
nvvk::DescriptorSetBindings m_descSetLayoutBind;
VkDescriptorPool m_descPool;
VkDescriptorSetLayout m_descSetLayout;
VkDescriptorSet m_descSet;
nvvk::Buffer m_bGlobals; // Device-Host of the camera matrices
nvvk::Buffer m_bObjDesc; // Device buffer of the OBJ instances
nvvk::Buffer m_bufReference; // Buffer references of the OBJ
std::vector<nvvk::Texture> m_textures; // vector of all textures of the scene
nvvk::ResourceAllocatorDma m_alloc; // Allocator for buffer, images, acceleration structures
nvvk::DebugUtil m_debug; // Utility to name objects
// #Post - Draw the rendered image on a quad using a tonemapper
void createOffscreenRender();
void createPostPipeline();
void createPostDescriptor();
void updatePostDescriptorSet();
void drawPost(VkCommandBuffer cmdBuf);
nvvk::DescriptorSetBindings m_postDescSetLayoutBind;
VkDescriptorPool m_postDescPool{VK_NULL_HANDLE};
VkDescriptorSetLayout m_postDescSetLayout{VK_NULL_HANDLE};
VkDescriptorSet m_postDescSet{VK_NULL_HANDLE};
VkPipeline m_postPipeline{VK_NULL_HANDLE};
VkPipelineLayout m_postPipelineLayout{VK_NULL_HANDLE};
VkRenderPass m_offscreenRenderPass{VK_NULL_HANDLE};
VkFramebuffer m_offscreenFramebuffer{VK_NULL_HANDLE};
nvvk::Texture m_offscreenColor;
nvvk::Texture m_offscreenDepth;
VkFormat m_offscreenColorFormat{VK_FORMAT_R32G32B32A32_SFLOAT};
VkFormat m_offscreenDepthFormat{VK_FORMAT_X8_D24_UNORM_PACK32};
// #VKRay
void initRayTracing();
auto objectToVkGeometryKHR(const ObjModel& model);
void createBottomLevelAS();
void createTopLevelAS();
void createRtDescriptorSet();
void updateRtDescriptorSet();
void createRtPipeline();
void raytrace(const VkCommandBuffer& cmdBuf, const glm::vec4& clearColor);
VkPhysicalDeviceRayTracingPipelinePropertiesKHR m_rtProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR};
nvvk::RaytracingBuilderKHR m_rtBuilder;
nvvk::DescriptorSetBindings m_rtDescSetLayoutBind;
VkDescriptorPool m_rtDescPool;
VkDescriptorSetLayout m_rtDescSetLayout;
VkDescriptorSet m_rtDescSet;
std::vector<VkRayTracingShaderGroupCreateInfoKHR> m_rtShaderGroups;
VkPipelineLayout m_rtPipelineLayout;
VkPipeline m_rtPipeline;
nvvk::SBTWrapper m_sbtWrapper;
// Push constant for ray tracer
VkPipeline m_rtShaderLibrary;
PushConstantRay m_pcRay{{}, {}, 0, 0, 7};
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -1,303 +0,0 @@
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
// 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>
#define IMGUI_DEFINE_MATH_OPERATORS
#include "backends/imgui_impl_glfw.h"
#include "backends/imgui_impl_vulkan.h"
#include "imgui.h"
#include "imgui/imgui_helper.h"
#include "hello_vulkan.h"
#include "imgui/imgui_camera_widget.h"
#include "nvh/cameramanipulator.hpp"
#include "nvh/fileoperations.hpp"
#include "nvpsystem.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)
{
ImGuiH::CameraWidget();
if(ImGui::CollapsingHeader("Light"))
{
ImGui::RadioButton("Point", &helloVk.m_pcRaster.lightType, 0);
ImGui::SameLine();
ImGui::RadioButton("Infinite", &helloVk.m_pcRaster.lightType, 1);
ImGui::SliderFloat3("Position", &helloVk.m_pcRaster.lightPosition.x, -20.f, 20.f);
ImGui::SliderFloat("Intensity", &helloVk.m_pcRaster.lightIntensity, 0.f, 150.f);
}
// Specialization
ImGui::SliderInt("Specialization", &helloVk.m_pcRay.specialization, 0, 7);
int s = helloVk.m_pcRay.specialization;
int a = ((s >> 2) % 2) == 1;
int b = ((s >> 1) % 2) == 1;
int c = ((s >> 0) % 2) == 1;
ImGui::Checkbox("Use Diffuse", (bool*)&a);
ImGui::Checkbox("Use Specular", (bool*)&b);
ImGui::Checkbox("Trace shadow", (bool*)&c);
helloVk.m_pcRay.specialization = (a << 2) + (b << 1) + c;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
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, PROJECT_NAME, nullptr, nullptr);
// Setup camera
CameraManip.setWindowSize(SAMPLE_WIDTH, SAMPLE_HEIGHT);
CameraManip.setLookat(glm::vec3(5, 4, -4), glm::vec3(0, 1, 0), glm::vec3(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(PROJECT_NAME);
// Search path for shaders and other media
defaultSearchPaths = {
NVPSystem::exePath() + PROJECT_RELDIRECTORY,
NVPSystem::exePath() + PROJECT_RELDIRECTORY "..",
std::string(PROJECT_NAME),
};
// Vulkan required extensions
assert(glfwVulkanSupported() == 1);
uint32_t count{0};
auto reqExtensions = glfwGetRequiredInstanceExtensions(&count);
// Requesting Vulkan extensions and layers
nvvk::ContextCreateInfo contextInfo;
contextInfo.setVersion(1, 2); // Using Vulkan 1.2
for(uint32_t ext_id = 0; ext_id < count; ext_id++) // Adding required extensions (surface, win32, linux, ..)
contextInfo.addInstanceExtension(reqExtensions[ext_id]);
contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true); // FPS in titlebar
contextInfo.addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true); // Allow debug names
contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // Enabling ability to present rendering
// #VKRay: Activate the ray tracing extension
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, false, &accelFeature); // To build acceleration structures
VkPhysicalDeviceRayTracingPipelineFeaturesKHR rtPipelineFeature{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR};
contextInfo.addDeviceExtension(VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, false, &rtPipelineFeature); // To use vkCmdTraceRaysKHR
contextInfo.addDeviceExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME); // Required by ray tracing pipeline
contextInfo.addDeviceExtension(VK_KHR_PIPELINE_LIBRARY_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 VkSurfaceKHR 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.createSwapchain(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.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths, true));
helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true));
helloVk.createOffscreenRender();
helloVk.createDescriptorSetLayout();
helloVk.createGraphicsPipeline();
helloVk.createUniformBuffer();
helloVk.createObjDescriptionBuffer();
helloVk.updateDescriptorSet();
// #VKRay
helloVk.initRayTracing();
helloVk.createBottomLevelAS();
helloVk.createTopLevelAS();
helloVk.createRtDescriptorSet();
helloVk.createRtPipeline();
helloVk.createPostDescriptor();
helloVk.createPostPipeline();
helloVk.updatePostDescriptorSet();
glm::vec4 clearColor = glm::vec4(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();
// Show UI window.
if(helloVk.showGui())
{
ImGuiH::Panel::Begin();
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);
ImGuiH::Control::Info("", "", "(F10) Toggle Pane", ImGuiH::Control::Flags::Disabled);
ImGuiH::Panel::End();
}
// Start rendering the scene
helloVk.prepareFrame();
// Start command buffer of this frame
auto curFrame = helloVk.getCurFrame();
const VkCommandBuffer& cmdBuf = helloVk.getCommandBuffers()[curFrame];
VkCommandBufferBeginInfo beginInfo{VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmdBuf, &beginInfo);
// Updating camera buffer
helloVk.updateUniformBuffer(cmdBuf);
// Clearing screen
std::array<VkClearValue, 2> clearValues{};
clearValues[0].color = {{clearColor[0], clearColor[1], clearColor[2], clearColor[3]}};
clearValues[1].depthStencil = {1.0f, 0};
// Offscreen render pass
{
VkRenderPassBeginInfo offscreenRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
offscreenRenderPassBeginInfo.clearValueCount = 2;
offscreenRenderPassBeginInfo.pClearValues = clearValues.data();
offscreenRenderPassBeginInfo.renderPass = helloVk.m_offscreenRenderPass;
offscreenRenderPassBeginInfo.framebuffer = helloVk.m_offscreenFramebuffer;
offscreenRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering Scene
if(useRaytracer)
{
helloVk.raytrace(cmdBuf, clearColor);
}
else
{
vkCmdBeginRenderPass(cmdBuf, &offscreenRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.rasterize(cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
}
// 2nd rendering pass: tone mapper, UI
{
VkRenderPassBeginInfo postRenderPassBeginInfo{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
postRenderPassBeginInfo.clearValueCount = 2;
postRenderPassBeginInfo.pClearValues = clearValues.data();
postRenderPassBeginInfo.renderPass = helloVk.getRenderPass();
postRenderPassBeginInfo.framebuffer = helloVk.getFramebuffers()[curFrame];
postRenderPassBeginInfo.renderArea = {{0, 0}, helloVk.getSize()};
// Rendering tonemapper
vkCmdBeginRenderPass(cmdBuf, &postRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
helloVk.drawPost(cmdBuf);
// Rendering UI
ImGui::Render();
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmdBuf);
vkCmdEndRenderPass(cmdBuf);
}
// Submit for display
vkEndCommandBuffer(cmdBuf);
helloVk.submitFrame();
}
// Cleanup
vkDeviceWaitIdle(helloVk.getDevice());
helloVk.destroyResources();
helloVk.destroy();
vkctx.deinit();
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "wavefront.glsl"
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
// clang-format off
// Incoming
layout(location = 1) in vec3 i_worldPos;
layout(location = 2) in vec3 i_worldNrm;
layout(location = 3) in vec3 i_viewDir;
layout(location = 4) in vec2 i_texCoord;
// Outgoing
layout(location = 0) out vec4 o_color;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {uint i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(binding = eTextures) uniform sampler2D[] textureSamplers;
// clang-format on
void main()
{
// Material of the object
ObjDesc objResource = objDesc.i[pcRaster.objIndex];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
int matIndex = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIndex];
vec3 N = normalize(i_worldNrm);
// Vector toward light
vec3 L;
float lightIntensity = pcRaster.lightIntensity;
if(pcRaster.lightType == 0)
{
vec3 lDir = pcRaster.lightPosition - i_worldPos;
float d = length(lDir);
lightIntensity = pcRaster.lightIntensity / (d * d);
L = normalize(lDir);
}
else
{
L = normalize(pcRaster.lightPosition);
}
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, N);
if(mat.textureId >= 0)
{
int txtOffset = objDesc.i[pcRaster.objIndex].txtOffset;
uint txtId = txtOffset + mat.textureId;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], i_texCoord).xyz;
diffuse *= diffuseTxt;
}
// Specular
vec3 specular = computeSpecular(mat, i_viewDir, L, N);
// Result
o_color = vec4(lightIntensity * (diffuse + specular), 1);
}

View file

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

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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);
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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));
}

View file

@ -1,23 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
struct hitPayload
{
vec3 hitValue;
};

View file

@ -1,160 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#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
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#extension GL_EXT_buffer_reference2 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
hitAttributeEXT vec2 attribs;
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(location = 1) rayPayloadEXT bool isShadowed;
layout(buffer_reference, scalar) buffer Vertices {Vertex v[]; }; // Positions of an object
layout(buffer_reference, scalar) buffer Indices {ivec3 i[]; }; // Triangle indices
layout(buffer_reference, scalar) buffer Materials {WaveFrontMaterial m[]; }; // Array of all materials on an object
layout(buffer_reference, scalar) buffer MatIndices {int i[]; }; // Material ID for each triangle
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 1, binding = eObjDescs, scalar) buffer ObjDesc_ { ObjDesc i[]; } objDesc;
layout(set = 1, binding = eTextures) uniform sampler2D textureSamplers[];
layout(constant_id = 0) const int USE_DIFFUSE = 1;
layout(constant_id = 1) const int USE_SPECULAR = 1;
layout(constant_id = 2) const int TRACE_SHADOW = 1;
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
void main()
{
// Object data
ObjDesc objResource = objDesc.i[gl_InstanceCustomIndexEXT];
MatIndices matIndices = MatIndices(objResource.materialIndexAddress);
Materials materials = Materials(objResource.materialAddress);
Indices indices = Indices(objResource.indexAddress);
Vertices vertices = Vertices(objResource.vertexAddress);
// Indices of the triangle
ivec3 ind = indices.i[gl_PrimitiveID];
// Vertex of the triangle
Vertex v0 = vertices.v[ind.x];
Vertex v1 = vertices.v[ind.y];
Vertex v2 = vertices.v[ind.z];
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
// Computing the coordinates of the hit position
const vec3 pos = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;
const vec3 worldPos = vec3(gl_ObjectToWorldEXT * vec4(pos, 1.0)); // Transforming the position to world space
// Computing the normal at hit position
const vec3 nrm = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
const vec3 worldNrm = normalize(vec3(nrm * gl_WorldToObjectEXT)); // Transforming the normal to world space
// Vector toward the light
vec3 L;
float lightIntensity = pcRay.lightIntensity;
float lightDistance = 100000.0;
// Point light
if(pcRay.lightType == 0)
{
vec3 lDir = pcRay.lightPosition - worldPos;
lightDistance = length(lDir);
lightIntensity = pcRay.lightIntensity / (lightDistance * lightDistance);
L = normalize(lDir);
}
else // Directional light
{
L = normalize(pcRay.lightPosition);
}
// Material of the object
int matIdx = matIndices.i[gl_PrimitiveID];
WaveFrontMaterial mat = materials.m[matIdx];
// Diffuse
vec3 diffuse = vec3(0);
if(USE_DIFFUSE == 1)
{
diffuse = computeDiffuse(mat, L, worldNrm);
if(mat.textureId >= 0)
{
uint txtId = mat.textureId + objDesc.i[gl_InstanceCustomIndexEXT].txtOffset;
vec2 texCoord = v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;
diffuse *= texture(textureSamplers[nonuniformEXT(txtId)], texCoord).xyz;
}
}
vec3 specular = vec3(0);
float attenuation = 1;
// Tracing shadow ray only if the light is visible from the surface
if(dot(worldNrm, L) > 0)
{
if(TRACE_SHADOW == 1)
{
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)
);
}
else
isShadowed = false;
if(isShadowed)
{
attenuation = 0.3;
}
else
{
// Specular
if(USE_SPECULAR == 1)
{
specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, worldNrm);
}
}
}
prd.hitValue = vec3(lightIntensity * attenuation * (diffuse + specular));
}

View file

@ -1,67 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
// clang-format off
layout(location = 0) rayPayloadEXT hitPayload prd;
layout(set = 0, binding = eTlas) uniform accelerationStructureEXT topLevelAS;
layout(set = 0, binding = eOutImage, rgba32f) uniform image2D image;
layout(set = 1, binding = eGlobals) uniform _GlobalUniforms { GlobalUniforms uni; };
layout(push_constant) uniform _PushConstantRay { PushConstantRay pcRay; };
// clang-format on
void main()
{
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 = uni.viewInverse * vec4(0, 0, 0, 1);
vec4 target = uni.projInverse * vec4(d.x, d.y, 1, 1);
vec4 direction = uni.viewInverse * vec4(normalize(target.xyz), 0);
uint rayFlags = gl_RayFlagsOpaqueEXT;
float tMin = 0.001;
float tMax = 10000.0;
traceRayEXT(topLevelAS, // acceleration structure
rayFlags, // rayFlags
0xFF, // cullMask
pcRay.specialization, // 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));
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "raycommon.glsl"
#include "wavefront.glsl"
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(push_constant) uniform _PushConstantRay
{
PushConstantRay pcRay;
};
void main()
{
prd.hitValue = pcRay.clearColor.xyz * 0.8;
}

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 1) rayPayloadInEXT bool isShadowed;
void main()
{
isShadowed = false;
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require
#include "wavefront.glsl"
layout(binding = 0) uniform _GlobalUniforms
{
GlobalUniforms uni;
};
layout(push_constant) uniform _PushConstantRaster
{
PushConstantRaster pcRaster;
};
layout(location = 0) in vec3 i_position;
layout(location = 1) in vec3 i_normal;
layout(location = 2) in vec3 i_color;
layout(location = 3) in vec2 i_texCoord;
layout(location = 1) out vec3 o_worldPos;
layout(location = 2) out vec3 o_worldNrm;
layout(location = 3) out vec3 o_viewDir;
layout(location = 4) out vec2 o_texCoord;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
vec3 origin = vec3(uni.viewInverse * vec4(0, 0, 0, 1));
o_worldPos = vec3(pcRaster.modelMatrix * vec4(i_position, 1.0));
o_viewDir = vec3(o_worldPos - origin);
o_texCoord = i_texCoord;
o_worldNrm = mat3(pcRaster.modelMatrix) * i_normal;
gl_Position = uni.viewProj * vec4(o_worldPos, 1.0);
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#include "host_device.h"
vec3 computeDiffuse(WaveFrontMaterial mat, vec3 lightDir, vec3 normal)
{
// Lambertian
float dotNL = max(dot(normal, lightDir), 0.0);
vec3 c = mat.diffuse * dotNL;
if(mat.illum >= 1)
c += mat.ambient;
return c;
}
vec3 computeSpecular(WaveFrontMaterial mat, vec3 viewDir, vec3 lightDir, vec3 normal)
{
if(mat.illum < 2)
return vec3(0);
// Compute specular only if not in shadow
const float kPi = 3.14159265;
const float kShininess = max(mat.shininess, 4.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(mat.specular * specular);
}

View file

@ -1,80 +0,0 @@
#*****************************************************************************
# Copyright 2020 NVIDIA Corporation. All rights reserved.
#*****************************************************************************
cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
#--------------------------------------------------------------------------------------------------
# Project setting
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(PROJNAME vk_${PROJNAME}_KHR)
project(${PROJNAME} LANGUAGES C CXX)
message(STATUS "-------------------------------")
message(STATUS "Processing Project ${PROJNAME}:")
#--------------------------------------------------------------------------------------------------
# C++ target and defines
set(CMAKE_CXX_STANDARD 17)
add_executable(${PROJNAME})
_add_project_definitions(${PROJNAME})
#--------------------------------------------------------------------------------------------------
# Source files for this project
#
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
file(GLOB EXTRA_COMMON ${TUTO_KHR_DIR}/common/*.*)
list(APPEND COMMON_SOURCE_FILES ${EXTRA_COMMON})
include_directories(${TUTO_KHR_DIR}/common)
#--------------------------------------------------------------------------------------------------
# GLSL to SPIR-V custom build
compile_glsl_directory(
SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders"
DST "${CMAKE_CURRENT_SOURCE_DIR}/spv"
VULKAN_TARGET "vulkan1.2"
DEPENDENCY ${VULKAN_BUILD_DEPENDENCIES}
)
#--------------------------------------------------------------------------------------------------
# Sources
target_sources(${PROJNAME} PUBLIC ${SOURCE_FILES} ${HEADER_FILES})
target_sources(${PROJNAME} PUBLIC ${COMMON_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${PACKAGE_SOURCE_FILES})
target_sources(${PROJNAME} PUBLIC ${GLSL_SOURCES} ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Sub-folders in Visual Studio
#
source_group("Common" FILES ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES})
source_group("Sources" FILES ${SOURCE_FILES})
source_group("Headers" FILES ${HEADER_FILES})
source_group("Shader Sources" FILES ${GLSL_SOURCES})
source_group("Shader Headers" FILES ${GLSL_HEADERS})
#--------------------------------------------------------------------------------------------------
# Linkage
#
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core)
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.)
#
_finalize_target( ${PROJNAME} )
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/spv")
install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/spv")

Some files were not shown because too many files have changed in this diff Show more