mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
Some renaming
This commit is contained in:
1
betterRenderer/.clang-format
Normal file
1
betterRenderer/.clang-format
Normal file
@@ -0,0 +1 @@
|
||||
BasedOnStyle: Google
|
||||
109
betterRenderer/CMakeLists.txt
Normal file
109
betterRenderer/CMakeLists.txt
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
set(LIBMANUL_NAME "betterRenderer")
|
||||
|
||||
set(LIBMANUL_WITH_DX12 OFF)
|
||||
set(LIBMANUL_WITH_VULKAN OFF)
|
||||
|
||||
if (WIN32)
|
||||
set(LIBMANUL_WITH_DX12 ON)
|
||||
elseif (UNIX)
|
||||
set(LIBMANUL_WITH_VULKAN ON)
|
||||
endif ()
|
||||
|
||||
if (LIBMANUL_WITH_VULKAN)
|
||||
find_package(Vulkan 1.3.230 REQUIRED)
|
||||
include_directories(${Vulkan_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
# Third-party deps
|
||||
add_subdirectory("thirdparty/nvrhi")
|
||||
add_subdirectory("thirdparty/yaml-cpp")
|
||||
add_subdirectory("thirdparty/fmt")
|
||||
add_subdirectory("thirdparty/entt")
|
||||
add_subdirectory("thirdparty/fsr2")
|
||||
|
||||
add_subdirectory("mashadercompiler")
|
||||
add_subdirectory("shaders")
|
||||
|
||||
# Source code
|
||||
file(GLOB_RECURSE src
|
||||
"renderer/*.cpp"
|
||||
"renderer/*.hpp"
|
||||
"renderer/*.h")
|
||||
|
||||
add_library(${LIBMANUL_NAME} ${src})
|
||||
|
||||
target_include_directories(${LIBMANUL_NAME} PUBLIC
|
||||
"renderer/include")
|
||||
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
"renderer/source"
|
||||
"${GLOBAL_INCLUDE_DIR}"
|
||||
"${IMGUI_DIR}"
|
||||
"${GLM_INCLUDE_DIR}")
|
||||
|
||||
# Needs more refactoring to get rid of that unfortunately...
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
"${GLOBAL_ROOT_DIR}")
|
||||
|
||||
# DynObj requires MOVER.h
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
"${GLOBAL_ROOT_DIR}/McZapkie")
|
||||
|
||||
# OpenAL required by MOVER.h
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
"${OPENAL_INCLUDE_DIR}")
|
||||
|
||||
# GLuint, GLint typedef'd here
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
"${DEPS_DIR}/glad/include")
|
||||
|
||||
# Python.h
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
"${PYTHON_INCLUDE_DIR}")
|
||||
|
||||
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
|
||||
GLM_ENABLE_EXPERIMENTAL
|
||||
GLM_FORCE_SWIZZLE)
|
||||
|
||||
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
|
||||
_USE_MATH_DEFINES)
|
||||
|
||||
# windows headers included SOMEWHERE
|
||||
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
|
||||
NOMINMAX)
|
||||
|
||||
# to avoid global_settings changing size...
|
||||
target_compile_definitions(${LIBMANUL_NAME} PRIVATE
|
||||
${DEFINITIONS})
|
||||
|
||||
# we need to include libserialport stuff to include uart because of
|
||||
# the setting class having mismatched size across TUs otherwise
|
||||
target_include_directories(${LIBMANUL_NAME} PRIVATE
|
||||
${libserialport_INCLUDE_DIR} ${LUAJIT_INCLUDE_DIR})
|
||||
|
||||
if (LIBMANUL_WITH_DX12)
|
||||
target_link_libraries(${LIBMANUL_NAME} nvrhi_d3d12 dxgi.lib)
|
||||
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_D3D12=1" "GLFW_EXPOSE_NATIVE_WIN32")
|
||||
else ()
|
||||
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_D3D12=0")
|
||||
endif ()
|
||||
|
||||
if (LIBMANUL_WITH_VULKAN)
|
||||
target_link_libraries(${LIBMANUL_NAME} nvrhi_vk Vulkan::Headers)
|
||||
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_VULKAN=1" "GLFW_INCLUDE_VULKAN")
|
||||
else ()
|
||||
target_compile_definitions(${LIBMANUL_NAME} PUBLIC "LIBMANUL_WITH_VULKAN=0")
|
||||
endif ()
|
||||
|
||||
## For double-precision vector goodness
|
||||
#if (WIN32)
|
||||
# target_compile_options(${LIBMANUL_NAME} PRIVATE "/arch:AVX2")
|
||||
#elseif (UNIX)
|
||||
# target_compile_options(${LIBMANUL_NAME} PRIVATE "-mavx2")
|
||||
#endif ()
|
||||
|
||||
target_sources(${LIBMANUL_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/eu07_source/register.cpp")
|
||||
set_target_properties(nvrhi yaml-cpp fmt EnTT glfw yaml-cpp-parse yaml-cpp-read yaml-cpp-sandbox PROPERTIES FOLDER "libraries")
|
||||
target_link_libraries(${LIBMANUL_NAME} ${LIBMANUL_NAME}_fsr2 nvrhi yaml-cpp fmt EnTT glfw)
|
||||
add_dependencies(${LIBMANUL_NAME} ${LIBMANUL_NAME}_shaders)
|
||||
31
betterRenderer/eu07_source/register.cpp
Normal file
31
betterRenderer/eu07_source/register.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "Logs.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
namespace {
|
||||
std::unique_ptr<gfx_renderer> create_nvrenderer_for_d3d12() {
|
||||
return std::make_unique<NvRenderer>(NvRenderer::Api::D3D12);
|
||||
}
|
||||
std::unique_ptr<gfx_renderer> create_nvrenderer_for_vulkan() {
|
||||
return std::make_unique<NvRenderer>(NvRenderer::Api::Vulkan);
|
||||
}
|
||||
std::unique_ptr<gfx_renderer> create_nvrenderer_default() {
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
return std::make_unique<NvRenderer>(NvRenderer::Api::D3D12);
|
||||
#elif LIBMANUL_WITH_VULKAN
|
||||
return std::make_unique<NvRenderer>(NvRenderer::Api::Vulkan);
|
||||
#endif
|
||||
ErrorLog("Failed to initialize any NVRHI Renderer");
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool register_manul_d3d12 =
|
||||
LIBMANUL_WITH_D3D12 &&
|
||||
gfx_renderer_factory::get_instance()->register_backend(
|
||||
"advanced_d3d12", &create_nvrenderer_for_d3d12);
|
||||
bool register_manul_vulkan =
|
||||
LIBMANUL_WITH_VULKAN &&
|
||||
gfx_renderer_factory::get_instance()->register_backend(
|
||||
"advanced_vk", &create_nvrenderer_for_vulkan);
|
||||
bool register_manul = gfx_renderer_factory::get_instance()->register_backend(
|
||||
"advanced", &create_nvrenderer_default);
|
||||
25
betterRenderer/mashadercompiler/CMakeLists.txt
Normal file
25
betterRenderer/mashadercompiler/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
project(MaSzynaShaderCompiler)
|
||||
|
||||
file(GLOB_RECURSE src "src/*.cpp" "src/*.hpp")
|
||||
|
||||
# Confirmed to work with DXC distributed via VCPKG. Your mileage may vary
|
||||
# TODO Make sure that it works as well for people who do not use VCPKG
|
||||
find_package(directx-dxc CONFIG REQUIRED)
|
||||
|
||||
add_executable(MaSzynaShaderCompiler ${src})
|
||||
|
||||
target_link_libraries(MaSzynaShaderCompiler nvrhi yaml-cpp Microsoft::DirectXShaderCompiler Microsoft::DXIL)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(MaSzynaShaderCompiler delayimp)
|
||||
target_link_options(MaSzynaShaderCompiler PRIVATE "/DELAYLOAD:dxcompiler.dll")
|
||||
endif ()
|
||||
|
||||
# We later pass the DXIL.dll parent directory as first argument to MaSzynaShaderCompiler so that it can sign DX12 shaders
|
||||
get_target_property(dxil_dll_path Microsoft::DXIL IMPORTED_LOCATION)
|
||||
get_filename_component(dxil_dll_path ${dxil_dll_path} DIRECTORY)
|
||||
set_target_properties(MaSzynaShaderCompiler PROPERTIES dxil_path "${dxil_dll_path}")
|
||||
18
betterRenderer/mashadercompiler/src/main.cpp
Normal file
18
betterRenderer/mashadercompiler/src/main.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "shader_compiler.hpp"
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
if (argc != 4) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef WIN32
|
||||
std::cout << "DXIL path: " << argv[1] << std::endl;
|
||||
SetDllDirectoryA(argv[1]);
|
||||
#endif
|
||||
setlocale(LC_ALL, ".UTF8");
|
||||
MaShaderCompiler compiler{};
|
||||
compiler.m_project_path = argv[2];
|
||||
compiler.m_output_path = argv[3];
|
||||
return compiler.Run();
|
||||
}
|
||||
312
betterRenderer/mashadercompiler/src/shader_compiler.cpp
Normal file
312
betterRenderer/mashadercompiler/src/shader_compiler.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#include "shader_compiler.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
int MaShaderCompiler::Run() {
|
||||
ParseOptions();
|
||||
Init();
|
||||
m_shader_path = m_project_path.parent_path();
|
||||
YAML::Node src = YAML::LoadFile((m_project_path).generic_string());
|
||||
|
||||
create_directories(m_output_path);
|
||||
|
||||
{
|
||||
YAML::Node dest;
|
||||
if (!CompileProject(dest, src["shaders"], src["templates"],
|
||||
ShaderPlatform::D3D12)) {
|
||||
return E_FAIL;
|
||||
}
|
||||
std::ofstream fout(m_output_path / "shaders_dxil.manul");
|
||||
fout << dest;
|
||||
}
|
||||
|
||||
{
|
||||
YAML::Node dest;
|
||||
if (!CompileProject(dest, src["shaders"], src["templates"],
|
||||
ShaderPlatform::Vulkan)) {
|
||||
return E_FAIL;
|
||||
}
|
||||
std::ofstream fout(m_output_path / "shaders_spirv.manul");
|
||||
fout << dest;
|
||||
}
|
||||
|
||||
{
|
||||
std::ofstream fout(m_output_path / "project.manul");
|
||||
fout << src;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void MaShaderCompiler::ParseOptions() {}
|
||||
|
||||
void MaShaderCompiler::Init() {
|
||||
const char *shader_path = getenv("shader_path");
|
||||
if (shader_path) {
|
||||
m_shader_path = shader_path;
|
||||
std::cout << "shader path: " << m_shader_path.generic_string() << std::endl;
|
||||
} else {
|
||||
m_shader_path = "";
|
||||
std::cout << "no custom shader path specified" << std::endl;
|
||||
}
|
||||
DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&m_dxc_utils));
|
||||
DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&m_dxc_compiler));
|
||||
m_dxc_utils->CreateDefaultIncludeHandler(&m_dxc_include_handler);
|
||||
}
|
||||
|
||||
YAML::Binary MaShaderCompiler::CompileShaderToBlob(
|
||||
std::filesystem::path file_name, std::wstring entry_name,
|
||||
std::vector<std::tuple<std::wstring, std::wstring>> defines,
|
||||
std::string target, ShaderPlatform platform) {
|
||||
file_name.replace_extension(".hlsl");
|
||||
static std::unordered_map<std::string, std::wstring> targets{
|
||||
{"compute", L"cs_6_0"}, {"vertex", L"vs_6_0"}, {"hull", L"hs_6_0"},
|
||||
{"domain", L"ds_6_0"}, {"geometry", L"gs_6_0"}, {"pixel", L"ps_6_0"}};
|
||||
|
||||
const wchar_t *target_str;
|
||||
if (auto target_found = targets.find(target); target_found != targets.end()) {
|
||||
target_str = target_found->second.c_str();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
RefCountPtr<IDxcBlobEncoding> source_blob;
|
||||
if (FAILED(m_dxc_utils->LoadFile(file_name.generic_wstring().c_str(), nullptr,
|
||||
&source_blob))) {
|
||||
return {};
|
||||
}
|
||||
DxcBuffer Source;
|
||||
Source.Ptr = source_blob->GetBufferPointer();
|
||||
Source.Size = source_blob->GetBufferSize();
|
||||
Source.Encoding = DXC_CP_ACP;
|
||||
|
||||
std::vector<std::wstring> include_paths{
|
||||
m_shader_path.generic_wstring(),
|
||||
file_name.parent_path().generic_wstring()};
|
||||
|
||||
std::vector<const wchar_t *> args;
|
||||
|
||||
args.emplace_back(L"-HV");
|
||||
args.emplace_back(L"2021");
|
||||
args.emplace_back(L"-Zpc");
|
||||
|
||||
for (const auto &include_path : include_paths) {
|
||||
args.emplace_back(L"-I");
|
||||
args.emplace_back(include_path.c_str());
|
||||
}
|
||||
|
||||
// if (m_generate_debug) {
|
||||
// args.emplace_back(L"-Od");
|
||||
// args.emplace_back(L"-Zi");
|
||||
// } else {
|
||||
// //args.emplace_back(L"-O3");
|
||||
// }
|
||||
|
||||
args.emplace_back(L"-Zi");
|
||||
args.emplace_back(L"-Qembed_debug");
|
||||
|
||||
std::vector<std::wstring> reg_shifts;
|
||||
|
||||
// Gather SPIRV register shifts once
|
||||
static const wchar_t *regShiftArgs[] = {
|
||||
L"-fvk-s-shift",
|
||||
L"-fvk-t-shift",
|
||||
L"-fvk-b-shift",
|
||||
L"-fvk-u-shift",
|
||||
};
|
||||
|
||||
uint32_t regShifts[] = {128, 0, 256, 384};
|
||||
|
||||
switch (platform) {
|
||||
case ShaderPlatform::D3D12:
|
||||
args.emplace_back(L"-Gis");
|
||||
break;
|
||||
case ShaderPlatform::Vulkan:
|
||||
args.emplace_back(L"-spirv");
|
||||
args.emplace_back(L"-fspv-target-env=vulkan1.2");
|
||||
args.emplace_back(L"-fvk-use-dx-layout");
|
||||
defines.emplace_back(L"SPIRV", L"1");
|
||||
|
||||
for (uint32_t reg = 0; reg < 4; reg++) {
|
||||
for (uint32_t space = 0; space < 8; space++) {
|
||||
wchar_t buf[64];
|
||||
|
||||
reg_shifts.emplace_back(regShiftArgs[reg]);
|
||||
|
||||
swprintf(buf, std::size(buf), L"%u", regShifts[reg]);
|
||||
reg_shifts.emplace_back(buf);
|
||||
|
||||
swprintf(buf, std::size(buf), L"%u", space);
|
||||
reg_shifts.emplace_back(buf);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::wstring &arg : reg_shifts) {
|
||||
args.emplace_back(arg.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<DxcDefine> dxc_defines{};
|
||||
dxc_defines.reserve(defines.size());
|
||||
for (const auto &[name, definition] : defines) {
|
||||
auto &define = dxc_defines.emplace_back();
|
||||
define.Name = name.c_str();
|
||||
define.Value = definition.c_str();
|
||||
}
|
||||
|
||||
RefCountPtr<IDxcCompilerArgs> compiler_args;
|
||||
m_dxc_utils->BuildArguments(file_name.stem().generic_wstring().c_str(),
|
||||
entry_name.c_str(), target_str, args.data(),
|
||||
args.size(), dxc_defines.data(),
|
||||
dxc_defines.size(), &compiler_args);
|
||||
|
||||
std::vector<LPCWSTR> wargs(
|
||||
compiler_args->GetArguments(),
|
||||
compiler_args->GetArguments() + compiler_args->GetCount());
|
||||
|
||||
RefCountPtr<IDxcResult> result;
|
||||
HRESULT hr = m_dxc_compiler->Compile(
|
||||
&Source, // Source buffer.
|
||||
compiler_args->GetArguments(), // Array of pointers to arguments.
|
||||
compiler_args->GetCount(), // Number of arguments.
|
||||
m_dxc_include_handler, // User-provided interface to handle
|
||||
// #include
|
||||
// directives (optional).
|
||||
IID_PPV_ARGS(&result) // Compiler output status, buffer, and errors.
|
||||
);
|
||||
|
||||
RefCountPtr<IDxcBlobUtf8> errors;
|
||||
result->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&errors), nullptr);
|
||||
auto errors_length = errors->GetStringLength();
|
||||
|
||||
if (errors && errors_length) {
|
||||
std::string message(errors->GetStringPointer());
|
||||
std::cout << "Shader compile log: " << file_name.generic_string()
|
||||
<< std::endl;
|
||||
std::cout << message;
|
||||
// WriteLog("Shader compile log: " + fileName.generic_string());
|
||||
// WriteLog(message);
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
std::cout << "Shader compile failed: " << file_name.generic_string()
|
||||
<< std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
RefCountPtr<IDxcBlob> binary_blob;
|
||||
result->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&binary_blob), nullptr);
|
||||
|
||||
auto buf_start =
|
||||
static_cast<unsigned char *>(binary_blob->GetBufferPointer());
|
||||
auto buf_end = buf_start + binary_blob->GetBufferSize();
|
||||
std::vector<unsigned char> buf{buf_start, buf_end};
|
||||
YAML::Binary binary{};
|
||||
binary.swap(buf);
|
||||
return binary;
|
||||
}
|
||||
|
||||
bool MaShaderCompiler::CompileUtilityShader(YAML::Node &dest,
|
||||
const YAML::Node &src,
|
||||
const YAML::Node &templates,
|
||||
ShaderPlatform platform) {
|
||||
std::vector<std::tuple<std::wstring, std::wstring>> definitions{};
|
||||
for (const auto definition :
|
||||
TemplateOverride("definitions", src, templates)) {
|
||||
auto &[name, define] = definitions.emplace_back();
|
||||
name = ToWide(definition.first.as<std::string>());
|
||||
define = ToWide(definition.second.as<std::string>());
|
||||
}
|
||||
const std::filesystem::path file_name =
|
||||
m_shader_path /
|
||||
TemplateOverride("source", src, templates).as<std::string>();
|
||||
const auto entry_name =
|
||||
TemplateOverride("entrypoint", src, templates).as<std::string>();
|
||||
const auto target =
|
||||
TemplateOverride("target", src, templates).as<std::string>();
|
||||
const auto blob = CompileShaderToBlob(file_name, ToWide(entry_name),
|
||||
definitions, target, platform);
|
||||
|
||||
if (!blob.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest["binary"] = blob;
|
||||
dest["entrypoint"] = entry_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MaShaderCompiler::CompileMaterial(YAML::Node &dest, const YAML::Node &src,
|
||||
const YAML::Node &templates,
|
||||
ShaderPlatform platform) {
|
||||
std::vector<std::tuple<std::wstring, std::wstring>> definitions{};
|
||||
for (const auto definition :
|
||||
TemplateOverride("definitions", src, templates)) {
|
||||
auto &[name, define] = definitions.emplace_back();
|
||||
name = ToWide(definition.first.as<std::string>());
|
||||
define = ToWide(definition.second.as<std::string>());
|
||||
}
|
||||
const std::filesystem::path file_name =
|
||||
m_shader_path /
|
||||
TemplateOverride("source", src, templates).as<std::string>();
|
||||
const std::string entry_name = "main";
|
||||
const std::string target = "pixel";
|
||||
|
||||
for (int i = 0; i < static_cast<int>(MaterialRenderPass::Count); ++i) {
|
||||
auto pass = static_cast<MaterialRenderPass>(i);
|
||||
const static std::unordered_map<MaterialRenderPass, std::string> pass_names{
|
||||
{MaterialRenderPass::Deferred, "deferred"},
|
||||
{MaterialRenderPass::Forward, "forward"},
|
||||
{MaterialRenderPass::CubeMap, "cubemap"},
|
||||
};
|
||||
static std::unordered_map<MaterialRenderPass, std::wstring>
|
||||
pass_definitions{
|
||||
{MaterialRenderPass::Deferred, L"PASS_GBUFFER"},
|
||||
{MaterialRenderPass::Forward, L"PASS_FORWARD"},
|
||||
{MaterialRenderPass::CubeMap, L"PASS_CUBEMAP"},
|
||||
};
|
||||
std::vector<std::tuple<std::wstring, std::wstring>> local_definitions{};
|
||||
local_definitions.emplace_back(L"PASS", pass_definitions.at(pass));
|
||||
local_definitions.insert(local_definitions.end(), definitions.begin(),
|
||||
definitions.end());
|
||||
|
||||
auto blob = CompileShaderToBlob(file_name, ToWide(entry_name),
|
||||
local_definitions, target, platform);
|
||||
|
||||
if (!blob.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dest[pass_names.at(pass)]["binary"] = blob;
|
||||
dest[pass_names.at(pass)]["entrypoint"] = entry_name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MaShaderCompiler::CompileProject(YAML::Node &dest, const YAML::Node &src,
|
||||
const YAML::Node &templates,
|
||||
ShaderPlatform platform) {
|
||||
{
|
||||
YAML::Node dest_materials = dest["materials"];
|
||||
for (const auto material : src["materials"]) {
|
||||
YAML::Node dest_material;
|
||||
if (!CompileMaterial(dest_material, material.second, templates, platform))
|
||||
return false;
|
||||
dest_materials[material.first] = dest_material;
|
||||
}
|
||||
}
|
||||
{
|
||||
for (const auto material : src["utility"]) {
|
||||
YAML::Node dest_shader;
|
||||
if (!CompileUtilityShader(dest_shader, material.second, templates,
|
||||
platform))
|
||||
return false;
|
||||
dest["utility"][material.first] = dest_shader;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
47
betterRenderer/mashadercompiler/src/shader_compiler.hpp
Normal file
47
betterRenderer/mashadercompiler/src/shader_compiler.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <WinAdapter.h>
|
||||
#else
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
#include <dxcapi.h>
|
||||
|
||||
template <typename T>
|
||||
using RefCountPtr = nvrhi::RefCountPtr<T>;
|
||||
|
||||
enum class MaterialRenderPass : int { Deferred, Forward, CubeMap, Count };
|
||||
|
||||
enum class ShaderPlatform { D3D12, Vulkan };
|
||||
|
||||
class MaShaderCompiler {
|
||||
public:
|
||||
int Run();
|
||||
std::filesystem::path m_project_path;
|
||||
std::filesystem::path m_output_path;
|
||||
|
||||
private:
|
||||
std::filesystem::path m_shader_path;
|
||||
bool m_generate_debug = false;
|
||||
void ParseOptions();
|
||||
void Init();
|
||||
YAML::Binary CompileShaderToBlob(
|
||||
std::filesystem::path file_name, std::wstring entry_name,
|
||||
std::vector<std::tuple<std::wstring, std::wstring>> defines,
|
||||
std::string target, ShaderPlatform platform);
|
||||
bool CompileUtilityShader(YAML::Node& dest, const YAML::Node& src,
|
||||
const YAML::Node& templates,
|
||||
ShaderPlatform platform);
|
||||
bool CompileMaterial(YAML::Node& dest, const YAML::Node& src,
|
||||
const YAML::Node& templates, ShaderPlatform platform);
|
||||
bool CompileProject(YAML::Node& dest, const YAML::Node& src,
|
||||
const YAML::Node& templates, ShaderPlatform platform);
|
||||
RefCountPtr<IDxcUtils> m_dxc_utils;
|
||||
RefCountPtr<IDxcCompiler3> m_dxc_compiler;
|
||||
RefCountPtr<IDxcIncludeHandler> m_dxc_include_handler;
|
||||
};
|
||||
31
betterRenderer/mashadercompiler/src/utils.hpp
Normal file
31
betterRenderer/mashadercompiler/src/utils.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
inline std::wstring ToWide(const std::string& str) {
|
||||
std::wstring wstr{};
|
||||
wstr.resize(std::mbstowcs(nullptr, str.c_str(), static_cast<uint64_t>(-1)));
|
||||
std::mbstowcs(wstr.data(), str.c_str(), wstr.size());
|
||||
return wstr;
|
||||
}
|
||||
|
||||
inline std::string ToNarrow(const std::wstring& wstr) {
|
||||
std::string str{};
|
||||
str.resize(std::wcstombs(nullptr, wstr.c_str(), static_cast<uint64_t>(-1)));
|
||||
std::wcstombs(str.data(), wstr.c_str(), str.size());
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename KeyType>
|
||||
YAML::Node TemplateOverride(const KeyType& key, const YAML::Node& container,
|
||||
const YAML::Node& templates) {
|
||||
YAML::Node local = container[key];
|
||||
YAML::Node use_template = container["use_template"];
|
||||
if (!local.IsDefined() && use_template.IsDefined() &&
|
||||
templates[use_template.as<std::string_view>()].IsDefined()) {
|
||||
return templates[use_template.as<std::string_view>()][key];
|
||||
}
|
||||
return local;
|
||||
}
|
||||
735
betterRenderer/renderer/include/nvrenderer/nvrenderer.h
Normal file
735
betterRenderer/renderer/include/nvrenderer/nvrenderer.h
Normal file
@@ -0,0 +1,735 @@
|
||||
#pragma once
|
||||
|
||||
//STL includes required by EU07 includes
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <math.h>
|
||||
|
||||
// OpenGL includes required by EU07 includes
|
||||
#include <glad/glad.h> // for GLuint, GLint
|
||||
|
||||
// Div. includes required by EU07 includes
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <entt/container/dense_map.hpp>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
|
||||
// EU07 includes
|
||||
#include <Classes.h>
|
||||
#include <scene.h>
|
||||
|
||||
#include "quadtree.h"
|
||||
#include "renderer.h"
|
||||
#include "nvrenderer_enums.h"
|
||||
#include "resource_registry.h"
|
||||
#include "sky.h"
|
||||
|
||||
template <typename Renderer>
|
||||
struct MaRendererConstants {
|
||||
static constexpr size_t NumMaterialPasses() noexcept {
|
||||
return static_cast<size_t>(Renderer::RenderPassType::Num);
|
||||
}
|
||||
static constexpr size_t NumDrawTypes() noexcept {
|
||||
return static_cast<size_t>(Renderer::DrawType::Num);
|
||||
}
|
||||
static constexpr size_t NumShadowPipelines() noexcept {
|
||||
return NumDrawTypes() << 1;
|
||||
}
|
||||
static constexpr size_t NumMaterialPipelines() noexcept {
|
||||
return NumDrawTypes() * NumMaterialPasses();
|
||||
}
|
||||
|
||||
static constexpr size_t GetPipelineIndex(
|
||||
typename Renderer::RenderPassType pass_type,
|
||||
typename Renderer::DrawType draw_type) {
|
||||
return static_cast<size_t>(draw_type) * NumMaterialPasses() +
|
||||
static_cast<size_t>(pass_type);
|
||||
}
|
||||
|
||||
static constexpr size_t GetDefaultShadowPipelineIndex(
|
||||
typename Renderer::DrawType type, const bool masked) {
|
||||
return static_cast<size_t>(type) << 1 | !!masked & 1;
|
||||
}
|
||||
};
|
||||
|
||||
class NvRenderer : public gfx_renderer, public MaResourceRegistry {
|
||||
public:
|
||||
enum class Api { D3D11, D3D12, Vulkan };
|
||||
|
||||
using RenderPassType = RendererEnums::RenderPassType;
|
||||
using DrawType = RendererEnums::DrawType;
|
||||
|
||||
public:
|
||||
using Constants = MaRendererConstants<NvRenderer>;
|
||||
// SZ renderer interface implementation
|
||||
virtual ~NvRenderer() override {}
|
||||
virtual bool Init(GLFWwindow *Window) override;
|
||||
virtual bool AddViewport(
|
||||
const global_settings::extraviewport_config &conf) override;
|
||||
virtual void Shutdown() override;
|
||||
virtual bool Render() override;
|
||||
virtual void SwapBuffers() override;
|
||||
virtual float Framerate() override;
|
||||
virtual gfx::geometrybank_handle Create_Bank() override;
|
||||
virtual gfx::geometry_handle Insert(gfx::index_array &Indices,
|
||||
gfx::vertex_array &Vertices,
|
||||
gfx::userdata_array &Userdata,
|
||||
gfx::geometrybank_handle const &Geometry,
|
||||
int const Type) override;
|
||||
virtual gfx::geometry_handle Insert(gfx::vertex_array &Vertices,
|
||||
gfx::userdata_array &Userdata,
|
||||
gfx::geometrybank_handle const &Geometry,
|
||||
int const Type) override;
|
||||
virtual bool Replace(gfx::vertex_array &Vertices,
|
||||
gfx::userdata_array &Userdata,
|
||||
gfx::geometry_handle const &Geometry, int const Type,
|
||||
std::size_t const Offset = 0) override;
|
||||
virtual bool Append(gfx::vertex_array &Vertices,
|
||||
gfx::userdata_array &Userdata,
|
||||
gfx::geometry_handle const &Geometry,
|
||||
int const Type) override;
|
||||
virtual gfx::index_array const &Indices(
|
||||
gfx::geometry_handle const &Geometry) const override;
|
||||
virtual gfx::vertex_array const &Vertices(
|
||||
gfx::geometry_handle const &Geometry) const override;
|
||||
const gfx::userdata_array &UserData(
|
||||
const gfx::geometry_handle &Geometry) const override;
|
||||
virtual material_handle Fetch_Material(std::string const &Filename,
|
||||
bool const Loadnow = true) override;
|
||||
virtual void Bind_Material(material_handle const Material,
|
||||
TSubModel const *sm = nullptr,
|
||||
lighting_data const *lighting = nullptr) override;
|
||||
virtual IMaterial const *Material(
|
||||
material_handle const Material) const override;
|
||||
virtual std::shared_ptr<gl::program> Fetch_Shader(
|
||||
std::string const &name) override;
|
||||
virtual texture_handle Fetch_Texture(
|
||||
std::string const &Filename, bool const Loadnow = true,
|
||||
GLint format_hint = GL_SRGB_ALPHA) override;
|
||||
virtual void Bind_Texture(texture_handle const Texture) override;
|
||||
virtual void Bind_Texture(std::size_t const Unit,
|
||||
texture_handle const Texture) override;
|
||||
virtual ITexture &Texture(texture_handle const Texture) override;
|
||||
virtual ITexture const &Texture(texture_handle const Texture) const override;
|
||||
virtual void Pick_Control_Callback(
|
||||
std::function<void(TSubModel const *, const glm::vec2)> Callback)
|
||||
override;
|
||||
virtual void Pick_Node_Callback(
|
||||
std::function<void(scene::basic_node *)> Callback) override;
|
||||
virtual TSubModel const *Pick_Control() const override;
|
||||
virtual scene::basic_node const *Pick_Node() const override;
|
||||
virtual glm::dvec3 Mouse_Position() const override;
|
||||
virtual void Update(double const Deltatime) override;
|
||||
virtual void Update_Pick_Control() override;
|
||||
virtual void Update_Pick_Node() override;
|
||||
virtual glm::dvec3 Update_Mouse_Position() override;
|
||||
virtual bool Debug_Ui_State(std::optional<bool>) override;
|
||||
virtual std::string const &info_times() const override;
|
||||
virtual std::string const &info_stats() const override;
|
||||
virtual imgui_renderer *GetImguiRenderer() override;
|
||||
virtual void MakeScreenshot() override;
|
||||
NvRenderer(Api api) : m_api(api), MaResourceRegistry(nullptr) {}
|
||||
|
||||
static bool d3d11_renderer_register;
|
||||
static bool d3d12_renderer_register;
|
||||
static bool vk_renderer_register;
|
||||
static bool renderer_register;
|
||||
|
||||
struct IFrustumTester {
|
||||
virtual bool IntersectSphere(const glm::dvec3 &origin,
|
||||
double radius) const = 0;
|
||||
virtual bool IntersectBox(const glm::dvec3 &origin,
|
||||
const glm::dvec3 &extent) const = 0;
|
||||
virtual bool IntersectBox(const glm::dmat4x3 &transform,
|
||||
const glm::dvec3 &origin,
|
||||
const glm::dvec3 &extent) const = 0;
|
||||
};
|
||||
|
||||
class NvRendererBackend *GetBackend() const { return m_backend.get(); }
|
||||
|
||||
// private:
|
||||
friend class NvRendererBackend;
|
||||
friend class NvRendererMessageCallback;
|
||||
friend class NvImguiRenderer;
|
||||
friend class NvTextureManager;
|
||||
friend struct NvGbuffer;
|
||||
friend struct NvSsao;
|
||||
friend struct FullScreenPass;
|
||||
friend struct NvTAA;
|
||||
friend struct NvFSR;
|
||||
friend struct MaEnvironment;
|
||||
friend struct MaShadowMap;
|
||||
Api m_api;
|
||||
std::shared_ptr<class NvRendererBackend> m_backend;
|
||||
std::shared_ptr<class NvRendererMessageCallback> m_message_callback;
|
||||
std::shared_ptr<class NvImguiRenderer> m_imgui_renderer;
|
||||
std::shared_ptr<struct NvGbuffer> m_gbuffer;
|
||||
std::shared_ptr<struct NvGbuffer> m_gbuffer_cube;
|
||||
std::shared_ptr<struct NvGbuffer> m_gbuffer_shadow;
|
||||
std::shared_ptr<struct GbufferLighting> m_gbuffer_lighting;
|
||||
std::shared_ptr<struct GbufferBlitPass> m_gbuffer_blit;
|
||||
std::shared_ptr<struct NvSsao> m_ssao;
|
||||
std::shared_ptr<struct MotionCache> m_motion_cache;
|
||||
std::shared_ptr<struct InstanceBank> m_instance_cache;
|
||||
std::shared_ptr<struct NvTAA> m_taa;
|
||||
std::shared_ptr<struct TonemapPass> m_tonemap;
|
||||
std::shared_ptr<struct NvFSR> m_fsr;
|
||||
std::shared_ptr<struct MaEnvironment> m_environment;
|
||||
std::shared_ptr<struct MaShadowMap> m_shadow_map;
|
||||
std::shared_ptr<struct MaContactShadows> m_contact_shadows;
|
||||
std::shared_ptr<struct Bloom> m_bloom;
|
||||
std::shared_ptr<struct Sky> m_sky;
|
||||
std::shared_ptr<struct MaAutoExposure> m_auto_exposure;
|
||||
|
||||
std::shared_ptr<struct MaConfig> m_config;
|
||||
struct MaConfig *GetConfig() const { return m_config.get(); }
|
||||
static struct MaConfig *Config();
|
||||
|
||||
struct GeometryBounds {
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
};
|
||||
GeometryBounds GetGeometryBounds(gfx::geometry_handle const &handle) const;
|
||||
|
||||
size_t GetCurrentFrame() const;
|
||||
void DebugUi();
|
||||
|
||||
glm::dmat4 m_previous_view_proj;
|
||||
glm::dmat4 m_previous_view;
|
||||
|
||||
bool m_debug_ui_active;
|
||||
|
||||
nvrhi::CommandListHandle m_command_list;
|
||||
nvrhi::EventQueryHandle m_upload_event_query;
|
||||
|
||||
std::mutex m_mtx_context_lock;
|
||||
glm::dvec3 m_previous_env_position;
|
||||
uint64_t m_previous_env_frame;
|
||||
|
||||
using section_sequence = std::vector<scene::basic_section *>;
|
||||
using distancecell_pair = std::pair<double, scene::basic_cell *>;
|
||||
using cell_sequence = std::vector<distancecell_pair>;
|
||||
struct RenderPass {
|
||||
RenderPassType m_type;
|
||||
double m_draw_range;
|
||||
uint64_t m_frame_index;
|
||||
bool m_draw_shapes = true;
|
||||
bool m_draw_lines = true;
|
||||
bool m_draw_tanimobj = true;
|
||||
bool m_draw_dynamic = true;
|
||||
bool m_draw_track = true;
|
||||
bool m_draw_instances = true;
|
||||
bool m_sort_batches = true;
|
||||
bool m_sort_transparents = true;
|
||||
nvrhi::CommandListHandle m_command_list_draw;
|
||||
nvrhi::CommandListHandle m_command_list_preparation;
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
nvrhi::ViewportState m_viewport_state;
|
||||
glm::dmat4 m_projection;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dmat3 m_transform;
|
||||
glm::dmat3 m_history_transform;
|
||||
double ScreenRadius(const glm::dvec3 &origin, double radius) const {
|
||||
glm::dvec4 ndc =
|
||||
m_projection *
|
||||
glm::dvec4(0., radius, -glm::length(origin - m_origin), 1.);
|
||||
return glm::abs(ndc.y / ndc.w);
|
||||
}
|
||||
double ScreenRadius(double sqr_distance, double radius) const {
|
||||
glm::dvec4 ndc =
|
||||
m_projection * glm::dvec4(0., radius, -glm::sqrt(sqr_distance), 1.);
|
||||
return glm::abs(ndc.y / ndc.w);
|
||||
}
|
||||
bool NeedsHistory() const { return m_type == RenderPassType::Deferred; }
|
||||
bool SphereVisible(const glm::dvec3 &origin, double radius) const {
|
||||
return !m_frustum_tester ||
|
||||
m_frustum_tester->IntersectSphere(origin - m_origin, radius);
|
||||
}
|
||||
bool BoxVisible(const glm::dvec3 &origin, const glm::dvec3 &extent) const {
|
||||
return !m_frustum_tester ||
|
||||
m_frustum_tester->IntersectBox(origin - m_origin, extent);
|
||||
}
|
||||
bool BoxVisible(glm::dmat4x3 transform, const glm::dvec3 &origin,
|
||||
const glm::dvec3 &extent) const {
|
||||
transform[3] -= m_origin;
|
||||
return !m_frustum_tester ||
|
||||
m_frustum_tester->IntersectBox(transform, origin, extent);
|
||||
}
|
||||
bool Visible(scene::bounding_area const &area) const {
|
||||
return SphereVisible(area.center, area.radius);
|
||||
}
|
||||
bool Visible(TDynamicObject *dynamic) const {
|
||||
return SphereVisible(dynamic->GetPosition(), dynamic->radius() * 1.25);
|
||||
}
|
||||
const IFrustumTester *m_frustum_tester;
|
||||
};
|
||||
|
||||
struct GeometryChunk {
|
||||
std::vector<gfx::basic_vertex> m_vertices;
|
||||
std::vector<gfx::basic_index> m_indices;
|
||||
size_t m_vertex_offset;
|
||||
size_t m_index_offset;
|
||||
size_t m_vertex_count;
|
||||
size_t m_index_count;
|
||||
bool m_indexed;
|
||||
bool m_is_uptodate = false;
|
||||
uint64_t m_last_frame_requested = static_cast<uint64_t>(-1);
|
||||
uint64_t m_last_frame_updated = static_cast<uint64_t>(-1);
|
||||
uint64_t m_last_frame_uploaded = static_cast<uint64_t>(-1);
|
||||
nvrhi::BufferHandle m_index_buffer;
|
||||
nvrhi::BufferHandle m_vertex_buffer;
|
||||
nvrhi::EventQueryHandle m_chunk_ready_handle;
|
||||
std::future<void> m_chunk_upload;
|
||||
glm::vec3 m_origin;
|
||||
glm::vec3 m_extent;
|
||||
glm::dvec3 m_last_position_requested;
|
||||
std::function<bool(gfx::vertex_array &, int)> m_replace_impl;
|
||||
void UpdateBounds();
|
||||
};
|
||||
|
||||
struct GeometryBank {
|
||||
nvrhi::BufferHandle m_index_buffer;
|
||||
nvrhi::BufferHandle m_vertex_buffer;
|
||||
size_t m_vertex_count;
|
||||
size_t m_index_count;
|
||||
std::vector<GeometryChunk> m_chunks;
|
||||
bool m_is_uptodate = false;
|
||||
nvrhi::EventQueryHandle m_uploaded_query;
|
||||
uint64_t m_last_frame_requested = static_cast<uint64_t>(-1);
|
||||
};
|
||||
std::vector<GeometryBank> m_geometry_banks;
|
||||
|
||||
nvrhi::BufferHandle m_culling_view_data;
|
||||
struct GeometryBatch {
|
||||
struct CullingViewData {
|
||||
glm::vec4 m_frustum_planes[6];
|
||||
glm::mat4 m_projection;
|
||||
};
|
||||
struct CullingPushConstants {
|
||||
glm::vec3 m_origin;
|
||||
float m_instance_radius;
|
||||
float m_min_radius;
|
||||
float m_max_radius;
|
||||
uint32_t m_num_instances;
|
||||
};
|
||||
struct CullingCommandBuffer {
|
||||
uint32_t m_index_count_per_instance;
|
||||
uint32_t m_instance_count;
|
||||
uint32_t m_start_index_location;
|
||||
int32_t m_base_vertex_location;
|
||||
uint32_t m_start_instance_location;
|
||||
};
|
||||
gfx::geometry_handle m_geometry;
|
||||
material_handle m_material;
|
||||
std::vector<glm::dmat4> m_transforms;
|
||||
glm::vec3 m_diffuse;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
glm::dvec3 m_instance_origin;
|
||||
nvrhi::BufferHandle m_static_instance_buffer;
|
||||
nvrhi::BufferHandle m_instance_buffer;
|
||||
nvrhi::BufferHandle m_static_command_buffer;
|
||||
nvrhi::BufferHandle m_command_buffer;
|
||||
nvrhi::BindingSetHandle m_culling_binding_set;
|
||||
double m_sqr_distance_max;
|
||||
double m_sqr_distance_min;
|
||||
double m_instance_radius;
|
||||
std::string m_name;
|
||||
};
|
||||
std::vector<GeometryBatch> m_geometry_batches;
|
||||
QuadTree m_batch_quadtree;
|
||||
|
||||
struct TrackBatch {
|
||||
std::string m_name;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
glm::dvec3 m_geometry_origin;
|
||||
glm::vec3 m_diffuse;
|
||||
std::vector<nvrhi::DrawArguments> m_draw_commands;
|
||||
gfx::geometry_handle m_geometry;
|
||||
material_handle m_material;
|
||||
nvrhi::BufferHandle m_command_buffer;
|
||||
struct BufferRegion {
|
||||
size_t m_start_vertex;
|
||||
size_t m_vertex_count;
|
||||
size_t m_start_index;
|
||||
size_t m_index_count;
|
||||
};
|
||||
std::vector<BufferRegion> m_regions;
|
||||
std::vector<bool> m_regions_need_update;
|
||||
};
|
||||
|
||||
bool TrackBatchPartialUpdate(size_t batch_index, size_t track_index,
|
||||
gfx::vertex_array &vertices);
|
||||
|
||||
std::vector<TrackBatch> m_track_batches;
|
||||
QuadTree m_track_quadtree;
|
||||
|
||||
struct Shape {
|
||||
std::string m_name;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
glm::dvec3 m_geometry_origin;
|
||||
gfx::geometry_handle m_geometry;
|
||||
bool m_draw_in_opaque;
|
||||
bool m_draw_in_transparent;
|
||||
material_handle m_material;
|
||||
glm::vec3 m_diffuse;
|
||||
};
|
||||
|
||||
std::vector<Shape> m_shapes;
|
||||
QuadTree m_shape_quadtree;
|
||||
|
||||
struct Line {
|
||||
std::string m_name;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
glm::dvec3 m_geometry_origin;
|
||||
gfx::geometry_handle m_geometry;
|
||||
float m_line_width;
|
||||
glm::vec3 m_color;
|
||||
float m_metalness;
|
||||
float m_roughness;
|
||||
};
|
||||
|
||||
std::vector<Line> m_lines;
|
||||
QuadTree m_line_quadtree;
|
||||
|
||||
struct Renderable {
|
||||
struct RenderableItem {
|
||||
bool m_render_in_forward = false;
|
||||
glm::dmat4x3 m_transform;
|
||||
glm::dmat4x3 m_history_transform;
|
||||
gfx::geometry_handle m_geometry;
|
||||
material_handle m_material;
|
||||
double m_sqr_distance_max;
|
||||
double m_sqr_distance_min;
|
||||
float m_opacity;
|
||||
float m_selfillum;
|
||||
glm::vec3 m_diffuse;
|
||||
};
|
||||
struct SpotLight {
|
||||
glm::vec3 m_color;
|
||||
glm::dvec3 m_origin;
|
||||
glm::vec3 m_direction;
|
||||
float m_radius;
|
||||
float m_cos_inner_cone;
|
||||
float m_cos_outer_cone;
|
||||
};
|
||||
std::vector<RenderableItem> m_items;
|
||||
std::vector<SpotLight> m_spotlights;
|
||||
bool m_render_in_deferred = false;
|
||||
bool m_render_in_forward = false;
|
||||
void Reset() {
|
||||
m_items.clear();
|
||||
m_spotlights.clear();
|
||||
m_render_in_deferred = false;
|
||||
m_render_in_forward = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimObj {
|
||||
std::string m_name;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
bool m_render_in_deferred;
|
||||
bool m_render_in_forward;
|
||||
bool m_animatable;
|
||||
bool m_was_once_animated = false;
|
||||
double m_sqr_distance_max;
|
||||
double m_sqr_distance_min;
|
||||
double m_distance;
|
||||
TAnimModel *m_model;
|
||||
Renderable m_renderable;
|
||||
};
|
||||
|
||||
std::vector<AnimObj> m_animateds;
|
||||
QuadTree m_animated_quadtree;
|
||||
|
||||
struct DynObj {
|
||||
TDynamicObject *m_dynamic;
|
||||
glm::dvec3 m_location;
|
||||
double m_radius;
|
||||
double m_distance;
|
||||
Renderable m_renderable;
|
||||
Renderable m_renderable_kabina;
|
||||
};
|
||||
uint32_t m_dynamic_with_kabina;
|
||||
std::vector<DynObj> m_dynamics;
|
||||
std::vector<uint32_t> m_renderable_dynamics;
|
||||
std::vector<Renderable::SpotLight> m_renderable_spotlights;
|
||||
|
||||
struct Cell {
|
||||
scene::basic_cell *m_cell;
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
};
|
||||
|
||||
std::vector<Cell> m_cells;
|
||||
QuadTree m_cell_quadtree;
|
||||
|
||||
void GatherSpotLights(const RenderPass &pass);
|
||||
|
||||
void UpdateGeometry(gfx::geometry_handle handle, const RenderPass &pass,
|
||||
bool *p_is_uploading = nullptr);
|
||||
section_sequence m_sectionqueue;
|
||||
cell_sequence m_cellqueue;
|
||||
struct MaterialCache;
|
||||
struct MaterialTemplate : public MaResourceRegistry {
|
||||
struct TextureBinding {
|
||||
std::string m_name;
|
||||
std::string m_sampler_name;
|
||||
int m_hint;
|
||||
texture_handle m_default_texture;
|
||||
};
|
||||
nvrhi::static_vector<TextureBinding, 8> m_texture_bindings;
|
||||
std::array<nvrhi::GraphicsPipelineHandle, Constants::NumMaterialPipelines()>
|
||||
m_pipelines;
|
||||
std::array<MaResourceMappingSet, Constants::NumMaterialPipelines()>
|
||||
m_resource_mappings;
|
||||
std::string m_name;
|
||||
std::string m_masked_shadow_texture;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
virtual bool CreateBindingSet(size_t pipeline_index, MaterialCache &cache);
|
||||
[[nodiscard]] nvrhi::IGraphicsPipeline *GetPipeline(
|
||||
RenderPassType pass_type, DrawType draw_type, bool alpha_masked) const;
|
||||
virtual void Init(const YAML::Node &conf);
|
||||
MaterialTemplate(const std::string &name, NvRenderer *renderer)
|
||||
: m_name(name), MaResourceRegistry(renderer), m_renderer(renderer) {}
|
||||
NvRenderer *m_renderer;
|
||||
};
|
||||
struct MaterialCache : public IMaterial, public MaResourceRegistry {
|
||||
explicit MaterialCache(MaterialTemplate *my_template)
|
||||
: MaResourceRegistry(my_template), m_template(my_template) {}
|
||||
void Init() { InitResourceRegistry(); }
|
||||
std::array<uint64_t, Constants::NumMaterialPipelines()>
|
||||
m_last_texture_updates{};
|
||||
std::string m_name;
|
||||
std::array<nvrhi::IGraphicsPipeline *, Constants::NumMaterialPipelines()>
|
||||
m_pipelines;
|
||||
std::array<nvrhi::BindingSetHandle, Constants::NumMaterialPipelines()>
|
||||
m_binding_sets;
|
||||
std::array<texture_handle, 8> m_texture_handles;
|
||||
nvrhi::BindingSetHandle m_binding_set;
|
||||
nvrhi::BindingSetHandle m_binding_set_cubemap;
|
||||
nvrhi::BindingSetHandle m_binding_set_shadow;
|
||||
glm::dvec3 m_last_position_requested;
|
||||
MaterialTemplate *m_template;
|
||||
bool m_masked_shadow;
|
||||
int m_shadow_rank;
|
||||
uint64_t m_last_frame_requested = 0;
|
||||
std::optional<float> m_opacity;
|
||||
glm::dvec2 m_size;
|
||||
[[nodiscard]] bool ShouldRenderInPass(const RenderPass &pass) const;
|
||||
void finalize(bool Loadnow) override {}
|
||||
bool update() override { return false; }
|
||||
[[nodiscard]] float get_or_guess_opacity() const override {
|
||||
if (m_opacity.has_value()) return *m_opacity;
|
||||
if (m_masked_shadow)
|
||||
return 0.0f;
|
||||
else
|
||||
return 0.5f;
|
||||
}
|
||||
bool is_translucent() const override { return m_masked_shadow; }
|
||||
glm::vec2 GetSize() const override { return m_size; }
|
||||
std::string GetName() const override { return m_name; }
|
||||
std::optional<float> GetSelfillum() const override { return std::nullopt; }
|
||||
texture_handle GetTexture(int slot) const override { return m_texture_handles[slot]; };
|
||||
int GetShadowRank() const override { return m_shadow_rank; }
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, material_handle> m_material_map;
|
||||
std::vector<MaterialCache> m_material_cache;
|
||||
bool m_scene_initialized = false;
|
||||
bool m_envir_can_filter = false;
|
||||
|
||||
std::array<nvrhi::InputLayoutHandle, static_cast<size_t>(DrawType::Num)>
|
||||
m_input_layouts;
|
||||
nvrhi::InputLayoutHandle GetInputLayout(DrawType draw_type);
|
||||
struct DrawConstants {
|
||||
glm::mat4 m_jittered_projection;
|
||||
glm::mat4 m_projection;
|
||||
glm::mat4 m_projection_history;
|
||||
};
|
||||
struct CubeDrawConstants {
|
||||
glm::mat4 m_face_projection;
|
||||
};
|
||||
struct PushConstantsDraw {
|
||||
glm::mat3x4 m_modelview;
|
||||
glm::mat3x4 m_modelview_history;
|
||||
glm::vec3 m_diffuse;
|
||||
float m_alpha_threshold;
|
||||
float m_alpha_mult;
|
||||
float m_selfillum;
|
||||
};
|
||||
typedef PushConstantsDraw PushConstantsCubemap;
|
||||
struct PushConstantsLine {
|
||||
glm::mat3x4 m_modelview;
|
||||
glm::mat3x4 m_modelview_history;
|
||||
glm::vec3 m_color;
|
||||
float m_line_weight;
|
||||
float m_metalness;
|
||||
float m_roughness;
|
||||
};
|
||||
|
||||
struct PushConstantsShadow {
|
||||
glm::mat4 m_modelviewprojection;
|
||||
float m_alpha_threshold;
|
||||
};
|
||||
bool InitMaterials();
|
||||
std::unordered_map<std::string_view, std::shared_ptr<MaterialTemplate>>
|
||||
m_material_templates;
|
||||
|
||||
nvrhi::FramebufferHandle m_framebuffer_forward;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout_drawconstants;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout_cubedrawconstants;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout_shadowdrawconstants;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout_forward;
|
||||
nvrhi::BindingSetHandle m_binding_set_drawconstants;
|
||||
nvrhi::BindingSetHandle m_binding_set_cubedrawconstants;
|
||||
nvrhi::BindingSetHandle m_binding_set_shadowdrawconstants;
|
||||
nvrhi::BindingSetHandle m_binding_set_line;
|
||||
std::array<nvrhi::BindingSetHandle, 2> m_binding_set_forward;
|
||||
nvrhi::BufferHandle m_cubedrawconstant_buffer;
|
||||
nvrhi::BufferHandle m_drawconstant_buffer;
|
||||
std::array<nvrhi::ShaderHandle, Constants::NumDrawTypes()> m_vertex_shader;
|
||||
std::array<nvrhi::ShaderHandle, Constants::NumDrawTypes()>
|
||||
m_vertex_shader_cubemap;
|
||||
std::array<nvrhi::ShaderHandle, Constants::NumDrawTypes()>
|
||||
m_vertex_shader_shadow;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout_shadow_masked;
|
||||
nvrhi::ShaderHandle m_pixel_shader_shadow_masked;
|
||||
std::array<nvrhi::InputLayoutHandle, Constants::NumDrawTypes()>
|
||||
m_input_layout;
|
||||
std::array<nvrhi::GraphicsPipelineHandle, Constants::NumShadowPipelines()>
|
||||
m_pso_shadow;
|
||||
nvrhi::GraphicsPipelineHandle m_pso_line;
|
||||
|
||||
std::condition_variable m_cv_next_frame;
|
||||
glm::mat4 m_previous_projection;
|
||||
|
||||
struct DrawCallStat {
|
||||
void Reset() {
|
||||
m_drawcalls = 0;
|
||||
m_triangles = 0;
|
||||
}
|
||||
int m_drawcalls = 0;
|
||||
int m_triangles = 0;
|
||||
void Draw(nvrhi::DrawArguments const &args) {
|
||||
++m_drawcalls;
|
||||
m_triangles += args.vertexCount * args.instanceCount / 3;
|
||||
}
|
||||
};
|
||||
DrawCallStat m_drawcalls_shape{};
|
||||
DrawCallStat m_drawcalls_line{};
|
||||
DrawCallStat m_drawcalls_tanimobj{};
|
||||
DrawCallStat m_drawcalls_dynamic{};
|
||||
DrawCallStat m_drawcalls_track{};
|
||||
DrawCallStat m_drawcalls_instances{};
|
||||
DrawCallStat *m_drawcall_counter = nullptr;
|
||||
bool m_draw_shapes = true;
|
||||
bool m_draw_lines = true;
|
||||
bool m_draw_tanimobj = true;
|
||||
bool m_draw_dynamic = true;
|
||||
bool m_draw_track = true;
|
||||
bool m_draw_instances = true;
|
||||
bool m_sort_batches = true;
|
||||
bool m_sort_transparents = true;
|
||||
bool m_pause_animations = false;
|
||||
std::unordered_set<const void *> m_batched_instances;
|
||||
|
||||
static const void *CombinePointers(const void *a, const void *b) {
|
||||
union {
|
||||
struct {
|
||||
uint32_t a;
|
||||
uint32_t b;
|
||||
};
|
||||
const void *c;
|
||||
} val{};
|
||||
val.a = static_cast<uint32_t>(reinterpret_cast<uint64_t>(a));
|
||||
val.b = static_cast<uint32_t>(reinterpret_cast<uint64_t>(b));
|
||||
return val.c;
|
||||
}
|
||||
|
||||
static double SqrDistToScreenSize(double sqr_dist, double radius) {
|
||||
double dist = glm::sqrt(sqr_dist);
|
||||
glm::dmat4 std_projection =
|
||||
glm::perspectiveRH_ZO(glm::radians(45.), 1., .1, 2e5);
|
||||
glm::dvec4 ndc = std_projection * glm::dvec4(0., radius, -dist, 1.);
|
||||
return glm::abs(ndc.y / ndc.w);
|
||||
};
|
||||
|
||||
void UpdateDrawData(const RenderPass &pass, const glm::dmat4 &transform,
|
||||
const glm::dmat4 &history_transform,
|
||||
float opacity_threshold, float opacity_mult,
|
||||
float selfillum, const glm::vec3& diffuse);
|
||||
|
||||
void UpdateDrawDataLine(const RenderPass &pass, const glm::dmat4 &transform,
|
||||
const glm::dmat4 &history_transform,
|
||||
const Line &line);
|
||||
|
||||
void BindConstants(const RenderPass &pass, nvrhi::GraphicsState &gfx_state);
|
||||
|
||||
bool BindMaterial(material_handle handle, DrawType draw_type,
|
||||
const RenderPass &pass, nvrhi::GraphicsState &gfx_state,
|
||||
float &alpha_threshold);
|
||||
|
||||
bool BindLineMaterial(DrawType draw_type, const RenderPass &pass,
|
||||
nvrhi::GraphicsState &gfx_state);
|
||||
|
||||
bool BindGeometry(gfx::geometry_handle handle, const RenderPass &pass,
|
||||
nvrhi::GraphicsState &gfx_state, nvrhi::DrawArguments &args,
|
||||
bool &indexed);
|
||||
|
||||
void GatherModelsForBatching();
|
||||
void GatherTracksForBatching();
|
||||
void GatherShapesForBatching();
|
||||
void GatherAnimatedsForBatching();
|
||||
void GatherCellsForAnimation();
|
||||
void GatherDynamics();
|
||||
|
||||
void RenderBatches(const RenderPass &pass);
|
||||
void RenderTracks(const RenderPass &pass);
|
||||
void RenderShapes(const RenderPass &pass);
|
||||
void RenderLines(const RenderPass &pass);
|
||||
void RenderAnimateds(const RenderPass &pass);
|
||||
void RenderKabina(const RenderPass &pass);
|
||||
void Render(const Renderable &renderable, const RenderPass &pass,
|
||||
const glm::dvec3 &history_origin, const double distance);
|
||||
void Animate(const glm::dvec3 &origin, double radius, uint64_t frame);
|
||||
|
||||
void Render(TAnimModel *Instance, const RenderPass &pass);
|
||||
bool Render(TDynamicObject *Dynamic, const RenderPass &pass);
|
||||
bool Render(TModel3d *Model, material_data const *Material,
|
||||
float const Squaredistance, const RenderPass &pass);
|
||||
void Render(TSubModel *Submodel, const RenderPass &pass);
|
||||
bool Render_cab(TDynamicObject const *Dynamic, const RenderPass &pass);
|
||||
|
||||
void Animate(AnimObj &instance, const glm::dvec3 &origin, uint64_t frame);
|
||||
void Animate(DynObj &instance, const glm::dvec3 &origin, uint64_t frame);
|
||||
void Animate(Renderable &renderable, TModel3d *Model,
|
||||
const material_data *material, double distance,
|
||||
const glm::dvec3 &position, const glm::vec3 &angle,
|
||||
uint64_t frame);
|
||||
void Animate(Renderable &renderable, TModel3d *Model,
|
||||
const material_data *material, double distance,
|
||||
const glm::dmat4 &transform, uint64_t frame);
|
||||
void Animate(Renderable &renderable, TSubModel *Submodel,
|
||||
const material_data *material, double distance,
|
||||
const glm::dmat4 &transform, uint64_t frame);
|
||||
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
bool InitForD3D12(GLFWwindow *Window);
|
||||
#endif
|
||||
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
bool InitForVulkan(GLFWwindow *Window);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
namespace RendererEnums {
|
||||
|
||||
enum class RenderPassType {
|
||||
Deferred,
|
||||
Forward,
|
||||
CubeMap,
|
||||
ShadowMap,
|
||||
Num,
|
||||
|
||||
RendererWarmUp
|
||||
|
||||
};
|
||||
|
||||
enum class DrawType : uint8_t { Model, InstancedModel, Num };
|
||||
|
||||
}
|
||||
96
betterRenderer/renderer/include/nvrenderer/quadtree.h
Normal file
96
betterRenderer/renderer/include/nvrenderer/quadtree.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/component_wise.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct QuadTreeLeave {
|
||||
QuadTreeLeave(const glm::dvec3& origin, const glm::dvec3& extent,
|
||||
uint32_t index)
|
||||
: m_origin(origin), m_extent(extent), m_index(index) {}
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
uint32_t m_index;
|
||||
};
|
||||
|
||||
struct QuadTreeBuilder {
|
||||
struct Node {
|
||||
std::array<std::shared_ptr<Node>, 4> m_children;
|
||||
std::vector<QuadTreeLeave> m_leaves;
|
||||
glm::dvec2 m_min;
|
||||
glm::dvec2 m_max;
|
||||
glm::dvec3 m_actual_min;
|
||||
glm::dvec3 m_actual_max;
|
||||
[[nodiscard]] int GetChildIndex(glm::dvec3 origin) const {
|
||||
glm::dvec2 mid = .5 * (m_min + m_max);
|
||||
return (origin.x < mid.x ? 0 : 1) | (origin.z < mid.y ? 0 : 2);
|
||||
}
|
||||
[[nodiscard]] glm::dvec4 GetChildBounds(int index) const {
|
||||
glm::dvec2 mid = .5 * (m_min + m_max);
|
||||
return glm::dvec4{
|
||||
index & 1 ? mid.x : m_min.x, index & 2 ? mid.y : m_min.y,
|
||||
index & 1 ? m_max.x : mid.x, index & 2 ? m_max.y : mid.y};
|
||||
}
|
||||
Node(const double min_x, const double min_z, const double max_x,
|
||||
const double max_z)
|
||||
: m_min(min_x, min_z),
|
||||
m_max(max_x, max_z),
|
||||
m_actual_min(std::numeric_limits<double>::max()),
|
||||
m_actual_max(-std::numeric_limits<double>::max()) {}
|
||||
explicit Node(const glm::dvec4& minmax_xz)
|
||||
: m_min(minmax_xz.x, minmax_xz.y),
|
||||
m_max(minmax_xz.z, minmax_xz.w),
|
||||
m_actual_min(std::numeric_limits<double>::max()),
|
||||
m_actual_max(-std::numeric_limits<double>::max()) {}
|
||||
[[nodiscard]] int NumChildren() const {
|
||||
int result = 0;
|
||||
for (const auto& child : m_children) {
|
||||
if (child) ++result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
[[nodiscard]] const Node* FirstChild() const {
|
||||
for (const auto& child : m_children) {
|
||||
if (child) return child.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void Insert(uint32_t index, glm::dvec3 min_, glm::dvec3 max_, int level);
|
||||
};
|
||||
std::shared_ptr<Node> m_root;
|
||||
glm::dvec3 m_min;
|
||||
glm::dvec3 m_max;
|
||||
int m_max_levels;
|
||||
QuadTreeBuilder(double min_x, double min_z, double max_x, double max_z);
|
||||
void Insert(uint32_t index, glm::dvec3 min, glm::dvec3 max) const;
|
||||
};
|
||||
|
||||
struct QuadTree {
|
||||
struct Node {
|
||||
glm::dvec3 m_origin;
|
||||
glm::dvec3 m_extent;
|
||||
uint32_t m_first_child;
|
||||
uint32_t m_child_count;
|
||||
uint32_t m_first_leave;
|
||||
uint32_t m_leave_count;
|
||||
};
|
||||
std::vector<Node> m_nodes;
|
||||
std::vector<QuadTreeLeave> m_leaves;
|
||||
void Build(const QuadTreeBuilder& builder);
|
||||
void Add(Node& node, const QuadTreeBuilder::Node& item);
|
||||
template <typename Tester, typename CallbackFn>
|
||||
void ForEach(const Tester& tester, CallbackFn fn, size_t subtree_index = 0) {
|
||||
const auto& subtree = m_nodes[subtree_index];
|
||||
if (!tester.BoxVisible(subtree.m_origin, subtree.m_extent)) return;
|
||||
for (int i = 0; i < subtree.m_leave_count; ++i) {
|
||||
const auto& leave = m_leaves[subtree.m_first_leave + i];
|
||||
if (!tester.BoxVisible(leave.m_origin, leave.m_extent)) continue;
|
||||
fn(leave.m_index);
|
||||
}
|
||||
for (int i = 0; i < subtree.m_child_count; ++i) {
|
||||
ForEach(tester, fn, subtree.m_first_child + i);
|
||||
}
|
||||
}
|
||||
};
|
||||
110
betterRenderer/renderer/include/nvrenderer/resource_registry.h
Normal file
110
betterRenderer/renderer/include/nvrenderer/resource_registry.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class NvTextureManager;
|
||||
class MaResourceRegistry;
|
||||
|
||||
struct MaResourceMapping {
|
||||
int m_slot;
|
||||
int m_size;
|
||||
entt::hashed_string m_key;
|
||||
nvrhi::ResourceType m_type;
|
||||
|
||||
#define MA_RESOURCE_MAPPING_INITIALIZER(type) \
|
||||
template <typename KeyType> \
|
||||
static MaResourceMapping type(int slot, const KeyType& key) { \
|
||||
MaResourceMapping mapping{}; \
|
||||
mapping.m_slot = slot; \
|
||||
mapping.m_key = static_cast<entt::hashed_string>(key); \
|
||||
mapping.m_type = nvrhi::ResourceType::type; \
|
||||
return mapping; \
|
||||
}
|
||||
|
||||
template <typename KeyType>
|
||||
static MaResourceMapping Texture_SRV(int slot, const KeyType& key) {
|
||||
MaResourceMapping mapping{};
|
||||
mapping.m_slot = slot;
|
||||
mapping.m_key = static_cast<entt::hashed_string>(key);
|
||||
mapping.m_type = nvrhi::ResourceType::Texture_SRV;
|
||||
return mapping;
|
||||
}
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(Texture_UAV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(TypedBuffer_SRV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(TypedBuffer_UAV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(StructuredBuffer_SRV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(StructuredBuffer_UAV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(RawBuffer_SRV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(RawBuffer_UAV)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(ConstantBuffer)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(VolatileConstantBuffer)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(Sampler)
|
||||
MA_RESOURCE_MAPPING_INITIALIZER(RayTracingAccelStruct)
|
||||
|
||||
#undef MA_RESOURCE_MAPPING_INITIALIZER
|
||||
|
||||
static MaResourceMapping PushConstants(const int slot, const int size) {
|
||||
MaResourceMapping mapping{};
|
||||
mapping.m_slot = slot;
|
||||
mapping.m_type = nvrhi::ResourceType::PushConstants;
|
||||
mapping.m_size = size;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
template <typename PushType>
|
||||
static MaResourceMapping PushConstants(const int slot) {
|
||||
MaResourceMapping mapping{};
|
||||
mapping.m_slot = slot;
|
||||
mapping.m_type = nvrhi::ResourceType::PushConstants;
|
||||
mapping.m_size = sizeof(PushType);
|
||||
return mapping;
|
||||
}
|
||||
};
|
||||
|
||||
struct MaResourceMappingSet {
|
||||
nvrhi::static_vector<MaResourceMapping, 128> m_mappings;
|
||||
MaResourceMappingSet& Add(const MaResourceMapping& mapping);
|
||||
void ToBindingLayout(nvrhi::BindingLayoutDesc& desc) const;
|
||||
bool ToBindingSet(nvrhi::BindingSetDesc& desc,
|
||||
const MaResourceRegistry* registry,
|
||||
const nvrhi::BindingLayoutDesc* reference_layout = nullptr) const;
|
||||
};
|
||||
|
||||
struct MaResourceView {
|
||||
nvrhi::IResource* m_resource = nullptr;
|
||||
nvrhi::ResourceType m_type = nvrhi::ResourceType::None;
|
||||
[[nodiscard]] bool IsValid() const {
|
||||
return m_resource && m_type != nvrhi::ResourceType::None;
|
||||
}
|
||||
};
|
||||
|
||||
class MaResourceRegistry {
|
||||
public:
|
||||
[[nodiscard]] MaResourceView GetResource(
|
||||
const entt::hashed_string& key, nvrhi::ResourceType requested_type) const;
|
||||
[[nodiscard]] NvTextureManager* GetTextureManager() const;
|
||||
[[nodiscard]] uint64_t GetLastStreamingTextureUpdate() const;
|
||||
void UpdateLastStreamingTextureUse(const glm::dvec3& location) const;
|
||||
void RegisterResource(bool global, const entt::hashed_string& key,
|
||||
nvrhi::IResource* resource, nvrhi::ResourceType type);
|
||||
void RegisterTexture(const entt::hashed_string& key, size_t handle);
|
||||
void RegisterResource(bool global, const char* key,
|
||||
nvrhi::IResource* resource, nvrhi::ResourceType type);
|
||||
void RegisterTexture(const char* key, size_t handle);
|
||||
virtual ~MaResourceRegistry() = default;
|
||||
|
||||
protected:
|
||||
explicit MaResourceRegistry(MaResourceRegistry* parent);
|
||||
void InitResourceRegistry();
|
||||
|
||||
private:
|
||||
[[nodiscard]] MaResourceView GetResourceLocal(
|
||||
const entt::hashed_string& key, nvrhi::ResourceType requested_type) const;
|
||||
MaResourceRegistry* m_parent = nullptr;
|
||||
std::shared_ptr<NvTextureManager> m_texture_manager = nullptr;
|
||||
entt::dense_map<entt::id_type, MaResourceView> m_resource_views{};
|
||||
entt::dense_map<entt::id_type, size_t> m_streaming_textures{};
|
||||
};
|
||||
142
betterRenderer/renderer/source/IntelTAA.h
Normal file
142
betterRenderer/renderer/source/IntelTAA.h
Normal file
@@ -0,0 +1,142 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Author(s): Lukasz, Migas (Lukasz.Migas@intel.com)
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// See https://github.com/GameTechDev/TAA
|
||||
|
||||
#ifndef __INTEL_TAA_H__
|
||||
#define __INTEL_TAA_H__
|
||||
|
||||
#define INTEL_TAA_NUM_THREADS_X 8
|
||||
#define INTEL_TAA_NUM_THREADS_Y 8
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace IntelTAA
|
||||
{
|
||||
// cpp<->hlsl mapping
|
||||
typedef glm::mat4 Matrix4x4;
|
||||
typedef glm::vec4 Vector4;
|
||||
typedef glm::vec3 Vector3;
|
||||
typedef glm::vec2 Vector2;
|
||||
typedef glm::ivec2 Vector2i;
|
||||
typedef glm::ivec4 Vector4i;
|
||||
typedef glm::uvec4 Vector4ui;
|
||||
typedef unsigned int uint32;
|
||||
|
||||
#else // #ifdef __cplusplus
|
||||
|
||||
// cpp<->hlsl mapping
|
||||
#define Matrix4x4 float4x4
|
||||
#define Vector4 float4
|
||||
#define Vector3 float3
|
||||
#define Vector2 float2
|
||||
#define Vector2i int2
|
||||
#define Vector4i int4
|
||||
#define Vector4ui uint4
|
||||
#define uint32 uint
|
||||
|
||||
#endif
|
||||
|
||||
// Constant buffer
|
||||
struct FTAAResolve
|
||||
{
|
||||
Vector4 Resolution; //width, height, 1/width, 1/height
|
||||
Vector2 Jitter;
|
||||
uint32 FrameNumber;
|
||||
uint32 DebugFlags; // AllowLongestVelocityVector | AllowNeighbourhoodSampling | AllowYCoCg | AllowVarianceClipping | AllowBicubicFilter | AllowDepthThreshold | MarkNoHistoryPixels
|
||||
float LerpMul;
|
||||
float LerpPow;
|
||||
float VarClipGammaMin; // (MIN_VARIANCE_GAMMA)
|
||||
float VarClipGammaMax; // (MAX_VARIANCE_GAMMA)
|
||||
float PreExposureNewOverOld;
|
||||
float Padding0;
|
||||
float Padding1;
|
||||
float Padding2;
|
||||
};
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
typedef float fp32_t;
|
||||
typedef float2 fp32_t2;
|
||||
typedef float3 fp32_t3;
|
||||
typedef float4 fp32_t4;
|
||||
|
||||
#if 0 == USE_FP16
|
||||
typedef float fp16_t;
|
||||
typedef float2 fp16_t2;
|
||||
typedef float3 fp16_t3;
|
||||
typedef float4 fp16_t4;
|
||||
|
||||
typedef int i16_t;
|
||||
typedef int2 i16_t2;
|
||||
typedef int3 i16_t3;
|
||||
typedef int4 i16_t4;
|
||||
|
||||
typedef uint ui16_t;
|
||||
typedef uint2 ui16_t2;
|
||||
typedef uint3 ui16_t3;
|
||||
typedef uint4 ui16_t4;
|
||||
#else
|
||||
typedef float16_t fp16_t;
|
||||
typedef float16_t2 fp16_t2;
|
||||
typedef float16_t3 fp16_t3;
|
||||
typedef float16_t4 fp16_t4;
|
||||
|
||||
typedef int16_t i16_t;
|
||||
typedef int16_t2 i16_t2;
|
||||
typedef int16_t3 i16_t3;
|
||||
typedef int16_t4 i16_t4;
|
||||
|
||||
typedef uint16_t ui16_t;
|
||||
typedef uint16_t2 ui16_t2;
|
||||
typedef uint16_t3 ui16_t3;
|
||||
typedef uint16_t4 ui16_t4;
|
||||
#endif
|
||||
|
||||
// this is all that the effect requires
|
||||
struct TAAParams
|
||||
{
|
||||
FTAAResolve CBData;
|
||||
|
||||
// in current MiniEngine's format
|
||||
Texture2D<fp16_t3> VelocityBuffer;
|
||||
|
||||
// Current colour buffer - rgb used
|
||||
Texture2D<fp16_t3> ColourTexture;
|
||||
|
||||
// Stored temporal antialiased pixel - .a should be sufficient enough to handle weight stored as float [0.5f, 1.f)
|
||||
Texture2D<fp32_t4> HistoryTexture;
|
||||
|
||||
// Current linear depth buffer - used only when USE_DEPTH_THRESHOLD is set
|
||||
Texture2D<fp16_t> DepthBuffer;
|
||||
|
||||
// Previous linear frame depth buffer - used only when USE_DEPTH_THRESHOLD is set
|
||||
Texture2D<fp16_t> PrvDepthBuffer;
|
||||
|
||||
// Antialiased colour buffer (used in a next frame as the HistoryTexture)
|
||||
RWTexture2D<fp16_t4> OutTexture;
|
||||
|
||||
// Samplers
|
||||
SamplerState MinMagLinearMipPointClamp;
|
||||
SamplerState MinMagMipPointClamp;
|
||||
};
|
||||
|
||||
#endif // #ifndef __cplusplus
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // namespace
|
||||
#endif // #ifdef __cplusplus
|
||||
|
||||
#endif // #ifndef __INTEL_TAA_H__
|
||||
263
betterRenderer/renderer/source/XeGTAO.h
Normal file
263
betterRenderer/renderer/source/XeGTAO.h
Normal file
@@ -0,0 +1,263 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
|
||||
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
|
||||
//
|
||||
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
|
||||
// Version: 1.02 (='.'=)
|
||||
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
|
||||
//
|
||||
// Version history:
|
||||
// 1.00 (2021-08-09): Initial release
|
||||
// 1.01 (2021-09-02): Fix for depth going to inf for 'far' depth buffer values that are out of fp16 range
|
||||
// 1.02 (2021-09-03): More fast_acos use and made final horizon cos clamping optional (off by default): 3-4% perf boost
|
||||
// 1.10 (2021-09-03): Added a couple of heuristics to combat over-darkening errors in certain scenarios
|
||||
// 1.20 (2021-09-06): Optional normal from depth generation is now a standalone pass: no longer integrated into
|
||||
// main XeGTAO pass to reduce complexity and allow reuse; also quality of generated normals improved
|
||||
// 1.21 (2021-09-28): Replaced 'groupshared'-based denoiser with a slightly slower multi-pass one where a 2-pass new
|
||||
// equals 1-pass old. However, 1-pass new is faster than the 1-pass old and enough when TAA enabled.
|
||||
// 1.22 (2021-09-28): Added 'XeGTAO_' prefix to all local functions to avoid name clashes with various user codebases.
|
||||
// 1.30 (2021-10-10): Added support for directional component (bent normals).
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __XE_GTAO_TYPES_H__
|
||||
#define __XE_GTAO_TYPES_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace XeGTAO
|
||||
{
|
||||
|
||||
// cpp<->hlsl mapping
|
||||
struct Matrix4x4 { float m[16]; };
|
||||
struct Vector3 { float x,y,z; };
|
||||
struct Vector2 { float x,y; };
|
||||
struct Vector2i { int x,y; };
|
||||
typedef unsigned int uint;
|
||||
|
||||
#else // #ifdef __cplusplus
|
||||
|
||||
// cpp<->hlsl mapping
|
||||
#define Matrix4x4 float4x4
|
||||
#define Vector3 float3
|
||||
#define Vector2 float2
|
||||
#define Vector2i int2
|
||||
|
||||
#endif
|
||||
|
||||
// Global consts that need to be visible from both shader and cpp side
|
||||
#define XE_GTAO_DEPTH_MIP_LEVELS 5 // this one is hard-coded to 5 for now
|
||||
#define XE_GTAO_NUMTHREADS_X 8 // these can be changed
|
||||
#define XE_GTAO_NUMTHREADS_Y 8 // these can be changed
|
||||
|
||||
struct GTAOConstants
|
||||
{
|
||||
Vector2i ViewportSize;
|
||||
Vector2 ViewportPixelSize; // .zw == 1.0 / ViewportSize.xy
|
||||
|
||||
Vector2 DepthUnpackConsts;
|
||||
Vector2 CameraTanHalfFOV;
|
||||
|
||||
Vector2 NDCToViewMul;
|
||||
Vector2 NDCToViewAdd;
|
||||
|
||||
Vector2 NDCToViewMul_x_PixelSize;
|
||||
float EffectRadius; // world (viewspace) maximum size of the shadow
|
||||
float EffectFalloffRange;
|
||||
|
||||
float RadiusMultiplier;
|
||||
float Padding0;
|
||||
float FinalValuePower;
|
||||
float DenoiseBlurBeta;
|
||||
|
||||
float SampleDistributionPower;
|
||||
float ThinOccluderCompensation;
|
||||
float DepthMIPSamplingOffset;
|
||||
int NoiseIndex; // frameIndex % 64 if using TAA or 0 otherwise
|
||||
};
|
||||
|
||||
// This is used only for the development (ray traced ground truth).
|
||||
struct ReferenceRTAOConstants
|
||||
{
|
||||
float TotalRaysLength ; // similar to Radius from GTAO
|
||||
float Albedo ; // the assumption on the average material albedo
|
||||
int MaxBounces ; // how many rays to recurse before stopping
|
||||
int AccumulatedFrames ; // how many frames have we accumulated so far (after resetting/clearing). If 0 - this is the first.
|
||||
int AccumulateFrameMax ; // how many frames are we aiming to accumulate; stop when we hit!
|
||||
int Padding0;
|
||||
int Padding1;
|
||||
int Padding2;
|
||||
#ifdef __cplusplus
|
||||
ReferenceRTAOConstants( ) { TotalRaysLength = 1.0f; Albedo = 0.0f; MaxBounces = 1; AccumulatedFrames = 0; AccumulateFrameMax = 0; }
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef XE_GTAO_USE_DEFAULT_CONSTANTS
|
||||
#define XE_GTAO_USE_DEFAULT_CONSTANTS 1
|
||||
#endif
|
||||
|
||||
// some constants reduce performance if provided as dynamic values; if these constants are not required to be dynamic and they match default values,
|
||||
// set XE_GTAO_USE_DEFAULT_CONSTANTS and the code will compile into a more efficient shader
|
||||
#define XE_GTAO_DEFAULT_RADIUS_MULTIPLIER (1.457f ) // allows us to use different value as compared to ground truth radius to counter inherent screen space biases
|
||||
#define XE_GTAO_DEFAULT_FALLOFF_RANGE (0.615f ) // distant samples contribute less
|
||||
#define XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER (2.0f ) // small crevices more important than big surfaces
|
||||
#define XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION (0.0f ) // the new 'thickness heuristic' approach
|
||||
#define XE_GTAO_DEFAULT_FINAL_VALUE_POWER (2.2f ) // modifies the final ambient occlusion value using power function - this allows some of the above heuristics to do different things
|
||||
#define XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET (3.30f ) // main trade-off between performance (memory bandwidth) and quality (temporal stability is the first affected, thin objects next)
|
||||
|
||||
#define XE_GTAO_OCCLUSION_TERM_SCALE (1.5f) // for packing in UNORM (because raw, pre-denoised occlusion term can overshoot 1 but will later average out to 1)
|
||||
|
||||
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
|
||||
#define XE_HILBERT_LEVEL 6U
|
||||
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
|
||||
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
|
||||
inline uint HilbertIndex( uint posX, uint posY )
|
||||
{
|
||||
uint index = 0U;
|
||||
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
|
||||
{
|
||||
uint regionX = ( posX & curLevel ) > 0U;
|
||||
uint regionY = ( posY & curLevel ) > 0U;
|
||||
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
|
||||
if( regionY == 0U )
|
||||
{
|
||||
if( regionX == 1U )
|
||||
{
|
||||
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
|
||||
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
|
||||
}
|
||||
|
||||
uint temp = posX;
|
||||
posX = posY;
|
||||
posY = temp;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
struct GTAOSettings
|
||||
{
|
||||
int QualityLevel = 2; // 0: low; 1: medium; 2: high; 3: ultra
|
||||
int DenoisePasses = 1; // 0: disabled; 1: sharp; 2: medium; 3: soft
|
||||
float Radius = 0.5f; // [0.0, ~ ] World (view) space size of the occlusion sphere.
|
||||
|
||||
// auto-tune-d settings
|
||||
float RadiusMultiplier = XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
|
||||
float FalloffRange = XE_GTAO_DEFAULT_FALLOFF_RANGE;
|
||||
float SampleDistributionPower = XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER;
|
||||
float ThinOccluderCompensation = XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION;
|
||||
float FinalValuePower = XE_GTAO_DEFAULT_FINAL_VALUE_POWER;
|
||||
float DepthMIPSamplingOffset = XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET;
|
||||
};
|
||||
|
||||
template<class T> inline T clamp( T const & v, T const & min, T const & max ) { assert( max >= min ); if( v < min ) return min; if( v > max ) return max; return v; }
|
||||
|
||||
// If using TAA then set noiseIndex to frameIndex % 64 - otherwise use 0
|
||||
inline void GTAOUpdateConstants( XeGTAO::GTAOConstants& consts, int viewportWidth, int viewportHeight, const XeGTAO::GTAOSettings & settings, const float projMatrix[16], bool rowMajor, unsigned int frameCounter )
|
||||
{
|
||||
consts.ViewportSize = { viewportWidth, viewportHeight };
|
||||
consts.ViewportPixelSize = { 1.0f / (float)viewportWidth, 1.0f / (float)viewportHeight };
|
||||
|
||||
float depthLinearizeMul = (rowMajor)?(-projMatrix[3 * 4 + 2]):(-projMatrix[3 + 2 * 4]); // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear );
|
||||
float depthLinearizeAdd = (rowMajor)?( projMatrix[2 * 4 + 2]):( projMatrix[2 + 2 * 4]); // float depthLinearizeAdd = clipFar / ( clipFar - clipNear );
|
||||
|
||||
// correct the handedness issue. need to make sure this below is correct, but I think it is.
|
||||
if( depthLinearizeMul * depthLinearizeAdd < 0 )
|
||||
depthLinearizeAdd = -depthLinearizeAdd;
|
||||
consts.DepthUnpackConsts = { -depthLinearizeMul, depthLinearizeAdd };
|
||||
|
||||
float tanHalfFOVY = 1.0f / ((rowMajor)?(projMatrix[1 * 4 + 1]):(projMatrix[1 + 1 * 4])); // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f );
|
||||
float tanHalfFOVX = 1.0F / ((rowMajor)?(projMatrix[0 * 4 + 0]):(projMatrix[0 + 0 * 4])); // = tanHalfFOVY * drawContext.Camera.GetAspect( );
|
||||
consts.CameraTanHalfFOV = { tanHalfFOVX, tanHalfFOVY };
|
||||
|
||||
consts.NDCToViewMul = { consts.CameraTanHalfFOV.x * 2.0f, consts.CameraTanHalfFOV.y * -2.0f };
|
||||
consts.NDCToViewAdd = { consts.CameraTanHalfFOV.x * -1.0f, consts.CameraTanHalfFOV.y * 1.0f };
|
||||
|
||||
consts.NDCToViewMul_x_PixelSize = { consts.NDCToViewMul.x * consts.ViewportPixelSize.x, consts.NDCToViewMul.y * consts.ViewportPixelSize.y };
|
||||
|
||||
consts.EffectRadius = settings.Radius;
|
||||
|
||||
consts.EffectFalloffRange = settings.FalloffRange;
|
||||
consts.DenoiseBlurBeta = (settings.DenoisePasses==0)?(1e4f):(1.2f); // high value disables denoise - more elegant & correct way would be do set all edges to 0
|
||||
|
||||
consts.RadiusMultiplier = settings.RadiusMultiplier;
|
||||
consts.SampleDistributionPower = settings.SampleDistributionPower;
|
||||
consts.ThinOccluderCompensation = settings.ThinOccluderCompensation;
|
||||
consts.FinalValuePower = settings.FinalValuePower;
|
||||
consts.DepthMIPSamplingOffset = settings.DepthMIPSamplingOffset;
|
||||
consts.NoiseIndex = (settings.DenoisePasses>0)?(frameCounter % 64):(0);
|
||||
consts.Padding0 = 0;
|
||||
}
|
||||
|
||||
#ifdef IMGUI_API
|
||||
inline bool GTAOImGuiSettings( XeGTAO::GTAOSettings & settings )
|
||||
{
|
||||
bool hadChanges = false;
|
||||
|
||||
ImGui::PushItemWidth( 120.0f );
|
||||
|
||||
ImGui::Text( "Performance/quality settings:" );
|
||||
|
||||
ImGui::Combo( "Quality Level", &settings.QualityLevel, "Low\0Medium\0High\0Ultra\00");
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Higher quality settings use more samples per pixel but are slower" );
|
||||
settings.QualityLevel = clamp( settings.QualityLevel , 0, 3 );
|
||||
|
||||
ImGui::Combo( "Denoising level", &settings.DenoisePasses, "Disabled\0Sharp\0Medium\0Soft\00");
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "The amount of edge-aware spatial denoise applied" );
|
||||
settings.DenoisePasses = clamp( settings.DenoisePasses , 0, 3 );
|
||||
|
||||
ImGui::Text( "Visual settings:" );
|
||||
|
||||
settings.Radius = clamp( settings.Radius, 0.0f, 100000.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Effect radius", &settings.Radius , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "World (viewspace) effect radius\nExpected range: depends on the scene & requirements, anything from 0.01 to 1000+" );
|
||||
settings.Radius = clamp( settings.Radius , 0.0f, 10000.0f );
|
||||
|
||||
if( ImGui::CollapsingHeader( "Auto-tuned settings (heuristics)" ) )
|
||||
{
|
||||
hadChanges |= ImGui::InputFloat( "Radius multiplier", &settings.RadiusMultiplier , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Multiplies the 'Effect Radius' - used by the auto-tune to best match raytraced ground truth\nExpected range: [0.3, 3.0], defaults to %.3f", XE_GTAO_DEFAULT_RADIUS_MULTIPLIER );
|
||||
settings.RadiusMultiplier = clamp( settings.RadiusMultiplier , 0.3f, 3.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Falloff range", &settings.FalloffRange , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Gently reduce sample impact as it gets out of 'Effect radius' bounds\nExpected range: [0.0, 1.0], defaults to %.3f", XE_GTAO_DEFAULT_FALLOFF_RANGE );
|
||||
settings.FalloffRange = clamp( settings.FalloffRange , 0.0f, 1.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Sample distribution power", &settings.SampleDistributionPower , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Make samples on a slice equally distributed (1.0) or focus more towards the center (>1.0)\nExpected range: [1.0, 3.0], 2defaults to %.3f", XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER );
|
||||
settings.SampleDistributionPower = clamp( settings.SampleDistributionPower , 1.0f, 3.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Thin occluder compensation", &settings.ThinOccluderCompensation, 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Slightly reduce impact of samples further back to counter the bias from depth-based (incomplete) input scene geometry data\nExpected range: [0.0, 0.7], defaults to %.3f", XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION );
|
||||
settings.ThinOccluderCompensation = clamp( settings.ThinOccluderCompensation , 0.0f, 0.7f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Final power", &settings.FinalValuePower, 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Applies power function to the final value: occlusion = pow( occlusion, finalPower )\nExpected range: [0.5, 5.0], defaults to %.3f", XE_GTAO_DEFAULT_FINAL_VALUE_POWER );
|
||||
settings.FinalValuePower = clamp( settings.FinalValuePower , 0.5f, 5.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Depth MIP sampling offset", &settings.DepthMIPSamplingOffset, 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Mainly performance (texture memory bandwidth) setting but as a side-effect reduces overshadowing by thin objects and increases temporal instability\nExpected range: [2.0, 6.0], defaults to %.3f", XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET );
|
||||
settings.DepthMIPSamplingOffset = clamp( settings.DepthMIPSamplingOffset , 0.0f, 30.0f );
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth( );
|
||||
|
||||
return hadChanges;
|
||||
}
|
||||
#endif // IMGUI_API
|
||||
|
||||
} // close the namespace
|
||||
|
||||
#endif // #ifdef __cplusplus
|
||||
|
||||
|
||||
#endif // __XE_GTAO_TYPES_H__
|
||||
113
betterRenderer/renderer/source/auto_exposure.cpp
Normal file
113
betterRenderer/renderer/source/auto_exposure.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "auto_exposure.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
MaAutoExposure::MaAutoExposure(NvRenderer *renderer)
|
||||
: MaResourceRegistry(renderer), m_backend(renderer->GetBackend()) {}
|
||||
|
||||
void MaAutoExposure::Render(nvrhi::ICommandList *command_list) {
|
||||
PushConstantsAutoExposure push_constants{};
|
||||
push_constants.m_delta_time = .01f;
|
||||
push_constants.m_num_mips = m_num_mips;
|
||||
push_constants.m_num_workgroups =
|
||||
((m_width + 63) / 64) * ((m_height + 64) / 64);
|
||||
push_constants.m_min_luminance_ev =
|
||||
NvRenderer::Config()->m_lighting.m_min_luminance_ev;
|
||||
push_constants.m_max_luminance_ev =
|
||||
NvRenderer::Config()->m_lighting.m_max_luminance_ev;
|
||||
command_list->setComputeState(nvrhi::ComputeState()
|
||||
.addBindingSet(m_bindings_auto_exposure)
|
||||
.setPipeline(m_pso_auto_exposure));
|
||||
command_list->setPushConstants(&push_constants, sizeof(push_constants));
|
||||
command_list->dispatch((m_width + 63) / 64, (m_height + 64) / 64, 1);
|
||||
|
||||
command_list->setComputeState(
|
||||
nvrhi::ComputeState()
|
||||
.addBindingSet(m_bindings_auto_exposure_apply)
|
||||
.setPipeline(m_pso_auto_exposure_apply));
|
||||
command_list->dispatch((m_width + 7) / 8, (m_height + 7) / 8, 1);
|
||||
}
|
||||
|
||||
void MaAutoExposure::Init() {
|
||||
nvrhi::TextureHandle input_texture = dynamic_cast<nvrhi::ITexture *>(
|
||||
GetResource("scene_lit_texture", nvrhi::ResourceType::Texture_SRV)
|
||||
.m_resource);
|
||||
|
||||
auto desc = input_texture->getDesc();
|
||||
m_width = desc.width;
|
||||
m_height = desc.height;
|
||||
m_num_mips =
|
||||
glm::floor(glm::log2(static_cast<float>(glm::max(m_width, m_height))));
|
||||
|
||||
m_6th_mip_img = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(nvrhi::Format::R32_FLOAT)
|
||||
.setWidth(m_width >> 6)
|
||||
.setHeight(m_height >> 6)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Auto Exposure 6th Mip"));
|
||||
|
||||
m_output_exposure_fsr2 = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(nvrhi::Format::RG16_FLOAT)
|
||||
.setWidth(1)
|
||||
.setHeight(1)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Auto Exposure Output (FSR2)"));
|
||||
|
||||
m_atomic_counter = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc()
|
||||
.setByteSize(sizeof(uint32_t))
|
||||
.setStructStride(sizeof(uint32_t))
|
||||
.setCanHaveUAVs(true)
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Auto Exposure Atomic Counter"));
|
||||
|
||||
auto cs_auto_exposure = m_backend->CreateShader(
|
||||
"autoexp_compute_avg_luminance", nvrhi::ShaderType::Compute);
|
||||
|
||||
nvrhi::BindingLayoutHandle binding_layout;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::PushConstants(
|
||||
1, sizeof(PushConstantsAutoExposure)))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, input_texture))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::Texture_UAV(0, m_output_exposure_fsr2))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::StructuredBuffer_UAV(2, m_atomic_counter))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(3, m_6th_mip_img)),
|
||||
binding_layout, m_bindings_auto_exposure);
|
||||
|
||||
m_pso_auto_exposure = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout)
|
||||
.setComputeShader(cs_auto_exposure));
|
||||
|
||||
auto cs_auto_exposure_apply =
|
||||
m_backend->CreateShader("autoexp_apply", nvrhi::ShaderType::Compute);
|
||||
|
||||
nvrhi::BindingLayoutHandle binding_layout_apply;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::Texture_SRV(0, m_output_exposure_fsr2))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, input_texture)),
|
||||
binding_layout_apply, m_bindings_auto_exposure_apply);
|
||||
|
||||
m_pso_auto_exposure_apply = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout_apply)
|
||||
.setComputeShader(cs_auto_exposure_apply));
|
||||
}
|
||||
39
betterRenderer/renderer/source/auto_exposure.h
Normal file
39
betterRenderer/renderer/source/auto_exposure.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvrenderer/resource_registry.h"
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
class NvRenderer;
|
||||
class NvRendererBackend;
|
||||
|
||||
struct MaAutoExposure : public MaResourceRegistry {
|
||||
MaAutoExposure(NvRenderer* renderer);
|
||||
|
||||
void Init();
|
||||
void Render(nvrhi::ICommandList* command_list);
|
||||
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
uint32_t m_num_mips;
|
||||
|
||||
struct PushConstantsAutoExposure {
|
||||
uint32_t m_num_mips;
|
||||
uint32_t m_num_workgroups;
|
||||
float m_delta_time;
|
||||
float m_min_luminance_ev;
|
||||
float m_max_luminance_ev;
|
||||
};
|
||||
|
||||
NvRendererBackend* m_backend;
|
||||
|
||||
nvrhi::TextureHandle m_output_exposure_fsr2;
|
||||
nvrhi::TextureHandle m_6th_mip_img;
|
||||
nvrhi::BufferHandle m_atomic_counter;
|
||||
|
||||
nvrhi::BindingSetHandle m_bindings_auto_exposure;
|
||||
nvrhi::ComputePipelineHandle m_pso_auto_exposure;
|
||||
|
||||
nvrhi::BindingSetHandle m_bindings_auto_exposure_apply;
|
||||
nvrhi::ComputePipelineHandle m_pso_auto_exposure_apply;
|
||||
};
|
||||
178
betterRenderer/renderer/source/bloom.cpp
Normal file
178
betterRenderer/renderer/source/bloom.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "bloom.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
void Bloom::UpdateConstants(nvrhi::ICommandList* command_list) {
|
||||
BloomConstants constants{};
|
||||
constants.m_prefilter_vector =
|
||||
glm::vec4{m_threshold, m_threshold - m_knee, m_knee * 2.f, .25f / m_knee};
|
||||
constants.m_mix_factor = m_mix_factor;
|
||||
command_list->writeBuffer(m_constant_buffer, &constants, sizeof(constants));
|
||||
}
|
||||
|
||||
void Bloom::OnGui() {
|
||||
if (!m_gui_active) return;
|
||||
if (ImGui::Begin("Bloom settings", &m_gui_active,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::SliderFloat("Threshold", &m_threshold, 0.f, 100.f);
|
||||
ImGui::SliderFloat("Knee", &m_knee, 0.f, 100.f);
|
||||
ImGui::SliderFloat("Mix Factor", &m_mix_factor, 0.f, 100.f);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
void Bloom::ShowGui() { m_gui_active = true; }
|
||||
|
||||
void Bloom::Init(nvrhi::ITexture* texture) {
|
||||
auto source_desc = texture->getDesc();
|
||||
|
||||
int mip_count = 7;
|
||||
m_working_texture = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setWidth(source_desc.width)
|
||||
.setHeight(source_desc.height)
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setMipLevels(mip_count)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_constant_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(sizeof(BloomConstants),
|
||||
"Bloom Constants", 16)
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
auto shader_prefilter =
|
||||
m_backend->CreateShader("bloom_prefilter", nvrhi::ShaderType::Compute);
|
||||
auto shader_downsample =
|
||||
m_backend->CreateShader("bloom_downsample", nvrhi::ShaderType::Compute);
|
||||
auto shader_upsample =
|
||||
m_backend->CreateShader("bloom_upsample", nvrhi::ShaderType::Compute);
|
||||
auto shader_final =
|
||||
m_backend->CreateShader("bloom_apply", nvrhi::ShaderType::Compute);
|
||||
auto binding_layout = m_backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::Compute)
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_UAV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(0)));
|
||||
auto binding_layout_final = m_backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::Compute)
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(1))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_UAV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(0)));
|
||||
auto pso_prefilter = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.setComputeShader(shader_prefilter)
|
||||
.addBindingLayout(binding_layout));
|
||||
auto pso_downsample = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.setComputeShader(shader_downsample)
|
||||
.addBindingLayout(binding_layout));
|
||||
auto pso_upsample = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.setComputeShader(shader_upsample)
|
||||
.addBindingLayout(binding_layout));
|
||||
auto pso_final = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.setComputeShader(shader_final)
|
||||
.addBindingLayout(binding_layout_final));
|
||||
auto sampler_linear_clamp = m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc().setAllFilters(true).setAllAddressModes(
|
||||
nvrhi::SamplerAddressMode::ClampToEdge));
|
||||
{
|
||||
auto& stage = m_stages.emplace_back();
|
||||
stage.m_width = source_desc.width;
|
||||
stage.m_height = source_desc.height;
|
||||
stage.m_pso = pso_prefilter;
|
||||
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
0, texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
|
||||
binding_layout);
|
||||
}
|
||||
for (int i = 1; i < mip_count; ++i) {
|
||||
auto& stage = m_stages.emplace_back();
|
||||
stage.m_width = glm::max(1u, source_desc.width >> i);
|
||||
stage.m_height = glm::max(1u, source_desc.height >> i);
|
||||
stage.m_pso = pso_downsample;
|
||||
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(glm::max(0, i - 1), 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(i, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
|
||||
binding_layout);
|
||||
}
|
||||
for (int i = mip_count - 2; i >= 1; --i) {
|
||||
auto& stage = m_stages.emplace_back();
|
||||
stage.m_width = glm::max(1u, source_desc.width >> i);
|
||||
stage.m_height = glm::max(1u, source_desc.height >> i);
|
||||
stage.m_pso = pso_upsample;
|
||||
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(i + 1, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(i, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
|
||||
binding_layout);
|
||||
}
|
||||
{
|
||||
auto& stage = m_stages.emplace_back();
|
||||
stage.m_width = source_desc.width;
|
||||
stage.m_height = source_desc.height;
|
||||
stage.m_pso = pso_final;
|
||||
stage.m_binding_set = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(1, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
1, texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
0, m_working_texture, nvrhi::Format::UNKNOWN,
|
||||
nvrhi::TextureSubresourceSet(0, 1, 0, 1)))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, sampler_linear_clamp)),
|
||||
binding_layout_final);
|
||||
}
|
||||
}
|
||||
|
||||
void Bloom::Render(nvrhi::ICommandList* command_list) {
|
||||
command_list->beginMarker("Bloom");
|
||||
UpdateConstants(command_list);
|
||||
for (const auto& stage : m_stages) {
|
||||
command_list->setComputeState(nvrhi::ComputeState()
|
||||
.setPipeline(stage.m_pso)
|
||||
.addBindingSet(stage.m_binding_set));
|
||||
command_list->dispatch((stage.m_width + 7) / 8,
|
||||
(stage.m_height + 7) / 8, 1);
|
||||
}
|
||||
command_list->endMarker();
|
||||
}
|
||||
36
betterRenderer/renderer/source/bloom.h
Normal file
36
betterRenderer/renderer/source/bloom.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
struct Bloom {
|
||||
struct BloomConstants {
|
||||
glm::vec4 m_prefilter_vector;
|
||||
float m_mix_factor;
|
||||
};
|
||||
float m_threshold = 3.f;
|
||||
float m_knee = .6f;
|
||||
float m_mix_factor = .04f;
|
||||
bool m_gui_active = false;
|
||||
void UpdateConstants(nvrhi::ICommandList* command_list);
|
||||
void OnGui();
|
||||
void ShowGui();
|
||||
Bloom(class NvRendererBackend* backend) : m_backend(backend) {}
|
||||
|
||||
void Init(nvrhi::ITexture* texture);
|
||||
void Render(nvrhi::ICommandList* command_list);
|
||||
|
||||
nvrhi::TextureHandle m_working_texture;
|
||||
nvrhi::BufferHandle m_constant_buffer;
|
||||
|
||||
struct BloomStage {
|
||||
int m_width;
|
||||
int m_height;
|
||||
nvrhi::BindingSetHandle m_binding_set;
|
||||
nvrhi::ComputePipelineHandle m_pso;
|
||||
};
|
||||
|
||||
std::vector<BloomStage> m_stages;
|
||||
|
||||
class NvRendererBackend* m_backend;
|
||||
};
|
||||
55
betterRenderer/renderer/source/config.cpp
Normal file
55
betterRenderer/renderer/source/config.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
void MaConfig::Init(const std::filesystem::path& config_file) {
|
||||
Init(YAML::LoadFile(config_file.generic_string()));
|
||||
}
|
||||
|
||||
void MaConfig::Init(const YAML::Node& node) {
|
||||
m_display.Init(node["display"]);
|
||||
m_shadow.Init(node["shadows"]);
|
||||
m_lighting.Init(node["lighting"]);
|
||||
m_weight_lines = node["wire_diameter_lines"].as<float>(.01f);
|
||||
m_weight_tractions = node["wire_diameter_tractions"].as<float>(.005f);
|
||||
m_envmap_resolution = node["envmap_resolution"].as<int>(256);
|
||||
}
|
||||
|
||||
void MaConfig::MaConfigDisplay::Init(const YAML::Node& node) {
|
||||
m_hdr_configs[0].Init(node["hdr_config"]["sdr"]);
|
||||
m_hdr_configs[0].m_tonemap_function = MaTonemapType::Default_Srgb;
|
||||
m_hdr_configs[1].Init(node["hdr_config"]["hdr"]);
|
||||
m_hdr_configs[1].m_tonemap_function = MaTonemapType::Linear_Rec2020;
|
||||
m_adapter_index = node["adapter"].as<int>();
|
||||
m_output_index = node["output"].as<int>();
|
||||
m_disable_hdr = node["disable_hdr"].as<bool>(false);
|
||||
}
|
||||
|
||||
MaConfig::MaConfigDisplay::MaConfigHDR MaConfig::MaConfigDisplay::GetHDRConfig()
|
||||
const {
|
||||
auto renderer = dynamic_cast<NvRenderer*>(GfxRenderer.get());
|
||||
return GetHDRConfig(renderer ? renderer->GetBackend()->IsHDRDisplay()
|
||||
: false);
|
||||
}
|
||||
|
||||
void MaConfig::MaConfigDisplay::MaConfigHDR::Init(const YAML::Node& node) {
|
||||
m_scene_exposure = node["pre_tonemap_exposure"].as<float>();
|
||||
m_ui_nits = node["ui_nits"].as<float>();
|
||||
m_scene_nits = node["scene_nits"].as<float>();
|
||||
m_scene_gamma = node["scene_gamma"].as<float>();
|
||||
}
|
||||
|
||||
void MaConfig::MaConfigShadow::Init(const YAML::Node& node) {
|
||||
m_resolution = node["resolution"].as<int>();
|
||||
m_cascade_distances = node["cascades"].as<std::vector<double>>();
|
||||
}
|
||||
|
||||
void MaConfig::MaConfigLighting::Init(const YAML::Node& node) {
|
||||
m_max_lights_per_scene = node["max_lights_per_scene"].as<int>(2048);
|
||||
m_selfillum_multiplier = node["selfillum_factor"].as<float>(1.f);
|
||||
m_freespot_multiplier = node["freespot_factor"].as<float>(10.f);
|
||||
m_luminance_threshold = node["luminance_threshold"].as<float>(.01f);
|
||||
m_min_luminance_ev = node["min_luminance_ev"].as<float>(-3.f);
|
||||
m_max_luminance_ev = node["max_luminance_ev"].as<float>(3.f);
|
||||
}
|
||||
49
betterRenderer/renderer/source/config.h
Normal file
49
betterRenderer/renderer/source/config.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
struct MaConfig {
|
||||
enum MaTonemapType { Default_Srgb = 0, Linear_Rec2020 };
|
||||
void Init(const std::filesystem::path& config_file);
|
||||
void Init(const YAML::Node& node);
|
||||
struct MaConfigDisplay {
|
||||
void Init(const YAML::Node& node);
|
||||
struct MaConfigHDR {
|
||||
void Init(const YAML::Node& node);
|
||||
MaTonemapType m_tonemap_function;
|
||||
float m_ui_nits;
|
||||
float m_scene_nits;
|
||||
float m_scene_exposure;
|
||||
float m_scene_gamma;
|
||||
float GetMaxOutputNits() const {
|
||||
return std::max(m_ui_nits, m_scene_nits);
|
||||
}
|
||||
float GetMinOutputNits() const { return 1.e-3f; }
|
||||
};
|
||||
std::array<MaConfigHDR, 2> m_hdr_configs;
|
||||
int m_adapter_index;
|
||||
int m_output_index;
|
||||
bool m_disable_hdr = false;
|
||||
MaConfigHDR GetHDRConfig(bool is_hdr) const { return m_hdr_configs[is_hdr]; }
|
||||
MaConfigHDR GetHDRConfig() const;
|
||||
} m_display;
|
||||
struct MaConfigLighting{
|
||||
void Init(const YAML::Node& node);
|
||||
int m_max_lights_per_scene;
|
||||
float m_selfillum_multiplier;
|
||||
float m_freespot_multiplier;
|
||||
float m_luminance_threshold;
|
||||
float m_min_luminance_ev;
|
||||
float m_max_luminance_ev;
|
||||
} m_lighting;
|
||||
int m_envmap_resolution;
|
||||
float m_weight_lines;
|
||||
float m_weight_tractions;
|
||||
struct MaConfigShadow {
|
||||
void Init(const YAML::Node& node);
|
||||
int m_resolution;
|
||||
std::vector<double> m_cascade_distances;
|
||||
} m_shadow;
|
||||
};
|
||||
76
betterRenderer/renderer/source/contactshadows.cpp
Normal file
76
betterRenderer/renderer/source/contactshadows.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "contactshadows.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "gbuffer.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
MaContactShadows::MaContactShadows(NvRenderer* renderer, NvGbuffer* gbuffer)
|
||||
: m_backend(renderer->GetBackend()),
|
||||
m_gbuffer(gbuffer),
|
||||
m_width(0),
|
||||
m_height(0) {}
|
||||
|
||||
void MaContactShadows::Init() {
|
||||
m_width = m_gbuffer->m_gbuffer_depth->getDesc().width;
|
||||
m_height = m_gbuffer->m_gbuffer_depth->getDesc().height;
|
||||
m_compute_shader = m_backend->CreateShader("contact_shadows",
|
||||
nvrhi::ShaderType::Compute);
|
||||
m_constant_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(sizeof(ComputeConstants),
|
||||
"Contact shadow constants")
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
m_output_texture = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(nvrhi::Format::R8_UNORM)
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::Texture_SRV(0, m_gbuffer->m_gbuffer_depth))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_output_texture))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
0, m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllFilters(true)
|
||||
.setAllAddressModes(
|
||||
nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setReductionType(
|
||||
nvrhi::SamplerReductionType::Maximum)))),
|
||||
m_binding_layout, m_binding_set);
|
||||
m_pipeline = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.setComputeShader(m_compute_shader)
|
||||
.addBindingLayout(m_binding_layout));
|
||||
}
|
||||
|
||||
void MaContactShadows::Render(nvrhi::ICommandList* command_list) {
|
||||
command_list->setComputeState(nvrhi::ComputeState()
|
||||
.setPipeline(m_pipeline)
|
||||
.addBindingSet(m_binding_set));
|
||||
command_list->dispatch((m_width + 7) / 8, (m_height + 7) / 8, 1);
|
||||
}
|
||||
|
||||
void MaContactShadows::UpdateConstants(nvrhi::ICommandList* command_list,
|
||||
const glm::dmat4& projection,
|
||||
const glm::dmat4& view,
|
||||
const glm::dvec3& light,
|
||||
uint64_t frame_index) {
|
||||
ComputeConstants constants{};
|
||||
constants.g_InverseProjection = glm::inverse(projection);
|
||||
constants.g_Projection = projection;
|
||||
constants.g_LightDirView = view * glm::dvec4(-light, 0.);
|
||||
constants.g_NumSamples = 16;
|
||||
constants.g_SampleRange = .1;
|
||||
constants.g_Thickness = .01;
|
||||
constants.g_FrameIndex = frame_index % 64;
|
||||
command_list->writeBuffer(m_constant_buffer, &constants, sizeof(constants));
|
||||
}
|
||||
36
betterRenderer/renderer/source/contactshadows.h
Normal file
36
betterRenderer/renderer/source/contactshadows.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct MaContactShadows {
|
||||
MaContactShadows(class NvRenderer* renderer, class NvGbuffer* gbuffer);
|
||||
void Init();
|
||||
void Render(nvrhi::ICommandList* command_list);
|
||||
void UpdateConstants(nvrhi::ICommandList* command_list,
|
||||
const glm::dmat4& projection, const glm::dmat4& view,
|
||||
const glm::dvec3& light, uint64_t frame_index);
|
||||
|
||||
struct ComputeConstants {
|
||||
glm::mat4 g_Projection;
|
||||
glm::mat4 g_InverseProjection;
|
||||
glm::vec3 g_LightDirView;
|
||||
float g_NumSamples;
|
||||
float g_SampleRange;
|
||||
float g_Thickness;
|
||||
uint32_t g_FrameIndex;
|
||||
};
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
nvrhi::BindingSetHandle m_binding_set;
|
||||
nvrhi::ComputePipelineHandle m_pipeline;
|
||||
nvrhi::ShaderHandle m_compute_shader;
|
||||
nvrhi::BufferHandle m_constant_buffer;
|
||||
|
||||
nvrhi::TextureHandle m_output_texture;
|
||||
|
||||
class NvRendererBackend* m_backend;
|
||||
class NvGbuffer* m_gbuffer;
|
||||
int m_width;
|
||||
int m_height;
|
||||
};
|
||||
37
betterRenderer/renderer/source/copypass.cpp
Normal file
37
betterRenderer/renderer/source/copypass.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "copypass.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
void CopyPass::Init() {
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::Pixel, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, m_source))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
0, m_backend->GetDevice()->createSampler(nvrhi::SamplerDesc()))),
|
||||
m_binding_layout, m_binding_set);
|
||||
m_pixel_shader = m_backend->CreateShader("copy", nvrhi::ShaderType::Pixel);
|
||||
m_framebuffer = m_backend->GetDevice()->createFramebuffer(
|
||||
nvrhi::FramebufferDesc().addColorAttachment(m_output));
|
||||
FullScreenPass::Init();
|
||||
}
|
||||
|
||||
void CopyPass::CreatePipelineDesc(nvrhi::GraphicsPipelineDesc& pipeline_desc) {
|
||||
FullScreenPass::CreatePipelineDesc(pipeline_desc);
|
||||
pipeline_desc.addBindingLayout(m_binding_layout);
|
||||
pipeline_desc.setPixelShader(m_pixel_shader);
|
||||
}
|
||||
|
||||
void CopyPass::Render(nvrhi::ICommandList* command_list) {
|
||||
nvrhi::GraphicsState graphics_state;
|
||||
InitState(graphics_state);
|
||||
graphics_state.addBindingSet(m_binding_set);
|
||||
command_list->setGraphicsState(graphics_state);
|
||||
Draw(command_list);
|
||||
}
|
||||
|
||||
nvrhi::IFramebuffer* CopyPass::GetFramebuffer() {
|
||||
return m_framebuffer;
|
||||
}
|
||||
28
betterRenderer/renderer/source/copypass.h
Normal file
28
betterRenderer/renderer/source/copypass.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "fullscreenpass.h"
|
||||
|
||||
struct CopyPass : public FullScreenPass {
|
||||
CopyPass(class NvRendererBackend* backend, nvrhi::ITexture* source, nvrhi::ITexture* output)
|
||||
: FullScreenPass(backend),
|
||||
m_source(source), m_output(output) {}
|
||||
|
||||
virtual void Init() override;
|
||||
|
||||
virtual void CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
|
||||
|
||||
virtual void Render(nvrhi::ICommandList* command_list) override;
|
||||
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
nvrhi::BindingSetHandle m_binding_set;
|
||||
nvrhi::ShaderHandle m_pixel_shader;
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
|
||||
nvrhi::ITexture* m_source;
|
||||
nvrhi::ITexture* m_output;
|
||||
};
|
||||
188
betterRenderer/renderer/source/csm.cpp
Normal file
188
betterRenderer/renderer/source/csm.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "csm.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
MaShadowMap::MaShadowMap(NvRenderer* renderer)
|
||||
: MaResourceRegistry(renderer), m_backend(renderer->m_backend.get()) {}
|
||||
|
||||
void MaShadowMap::Init() {
|
||||
InitResourceRegistry();
|
||||
const auto& shadow_config = NvRenderer::Config()->m_shadow;
|
||||
int num_cascades = shadow_config.m_cascade_distances.size();
|
||||
m_cascade_matrices.resize(num_cascades);
|
||||
m_cascade_matrices_clamped.resize(num_cascades);
|
||||
m_shadow_planes.resize(num_cascades + 1);
|
||||
m_far_plane_first = shadow_config.m_cascade_distances.front();
|
||||
m_far_plane_last = shadow_config.m_cascade_distances.back();
|
||||
m_projection_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(
|
||||
sizeof(ShadowProjectionConstants), "Shadow projections")
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
m_inverse_projection_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(
|
||||
sizeof(ShadowProjectionConstants), "Inverse shadow projections")
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
RegisterResource(true, "shadow_projection_buffer", m_projection_buffer,
|
||||
nvrhi::ResourceType::ConstantBuffer);
|
||||
}
|
||||
|
||||
void MaShadowMap::CalcCascades(const glm::dmat4& projection,
|
||||
const glm::dmat4& view,
|
||||
const glm::dvec3& light) {
|
||||
const auto& shadow_config = NvRenderer::Config()->m_shadow;
|
||||
|
||||
glm::dmat4 inverse_projection = glm::inverse(projection);
|
||||
glm::dmat4 inverse_view = glm::inverse(view);
|
||||
|
||||
double projection_near, projection_far;
|
||||
{
|
||||
glm::dvec4 ndc_near{0., 0., 0., 1.};
|
||||
glm::dvec4 ndc_far{0., 0., 1., 1.};
|
||||
ndc_near = inverse_projection * ndc_near;
|
||||
ndc_far = inverse_projection * ndc_far;
|
||||
|
||||
projection_near = -ndc_near.z / ndc_near.w;
|
||||
projection_far = -ndc_far.z / ndc_far.w;
|
||||
}
|
||||
|
||||
// Set near and far plane offset
|
||||
m_shadow_planes.front() = projection_near;
|
||||
for (int i = 1; i < shadow_config.m_cascade_distances.size(); ++i)
|
||||
m_shadow_planes[i] = shadow_config.m_cascade_distances[i];
|
||||
// m_shadow_planes.back() = std::min(m_far_plane_last, projection_far);
|
||||
// m_shadow_planes[1] = std::min(m_far_plane_first, m_shadow_planes.back());
|
||||
//
|
||||
//// Fill intermediate plane offsets
|
||||
// double base = m_shadow_planes.back() / m_shadow_planes[1];
|
||||
// double denom = 1. / (m_shadow_planes.size() - 2.);
|
||||
// for (int i = 2; i < m_shadow_planes.size() - 1; ++i) {
|
||||
// m_shadow_planes[i] = m_shadow_planes[1] * glm::pow(base, (i - 1) *
|
||||
// denom);
|
||||
// }
|
||||
|
||||
static std::vector<glm::dvec3> plane_corners;
|
||||
plane_corners.resize(4 * (m_shadow_planes.size()));
|
||||
|
||||
// for (int i = 0; i < m_shadow_planes.size(); ++i) {
|
||||
// glm::dvec3* plane = &plane_corners[i * 4];
|
||||
// GetViewPlaneCornersWorldSpace(projection, inverse_projection,
|
||||
// inverse_view,
|
||||
// m_shadow_planes[i], plane[0], plane[1],
|
||||
// plane[2], plane[3]);
|
||||
// }
|
||||
|
||||
glm::dvec3 up = glm::abs(light.y) > .999 ? glm::dvec3{0., 0., 1.}
|
||||
: glm::dvec3{0., 1., 0.};
|
||||
|
||||
for (int i = 0; i < m_cascade_matrices.size(); ++i) {
|
||||
glm::dvec3 plane[8];
|
||||
GetViewPlaneCornersWorldSpace(projection, inverse_projection, inverse_view,
|
||||
m_shadow_planes[i], true, plane[0], plane[1],
|
||||
plane[2], plane[3]);
|
||||
GetViewPlaneCornersWorldSpace(projection, inverse_projection, inverse_view,
|
||||
m_shadow_planes[i + 1], false, plane[4],
|
||||
plane[5], plane[6], plane[7]);
|
||||
|
||||
glm::dvec3 world_min{std::numeric_limits<double>::max()};
|
||||
glm::dvec3 world_max{-std::numeric_limits<double>::max()};
|
||||
double offset = 0.;
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
world_min = glm::min(world_min, plane[j]);
|
||||
world_max = glm::max(world_max, plane[j]);
|
||||
}
|
||||
|
||||
double actual_size = glm::max(glm::distance(plane[0], plane[7]),
|
||||
glm::distance(plane[4], plane[7]));
|
||||
|
||||
glm::dvec3 center = -Global.pCamera.Pos;
|
||||
|
||||
glm::dmat4 shadow_view = glm::lookAtRH(center, center - light, up);
|
||||
|
||||
glm::dvec3 min{std::numeric_limits<double>::max()};
|
||||
glm::dvec3 max{-std::numeric_limits<double>::max()};
|
||||
|
||||
double frustumchunkdepth =
|
||||
m_shadow_planes[i + 1] -
|
||||
(m_shadow_planes[i] > 1.f ? m_shadow_planes[i] : 0.f);
|
||||
double quantizationstep{glm::min(frustumchunkdepth, 50.)};
|
||||
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
glm::dvec3 v = shadow_view * glm::dvec4(plane[j], 1.f);
|
||||
min = glm::min(min, v);
|
||||
max = glm::max(max, v);
|
||||
}
|
||||
|
||||
glm::dvec2 size = max - min;
|
||||
glm::dvec2 diff = static_cast<glm::dvec2>(actual_size) - size;
|
||||
max.xy += glm::max(diff * .5, 0.);
|
||||
min.xy -= glm::max(diff * .5, 0.);
|
||||
|
||||
glm::dvec2 pixel_size =
|
||||
static_cast<glm::dvec2>(actual_size / shadow_config.m_resolution);
|
||||
min.xy = glm::round(min.xy / pixel_size) * pixel_size;
|
||||
max.xy = glm::round(max.xy / pixel_size) * pixel_size;
|
||||
|
||||
// max = quantizationstep * glm::ceil(max / quantizationstep);
|
||||
// min = quantizationstep * glm::floor(min / quantizationstep);
|
||||
|
||||
glm::dmat4 shadow_proj = glm::orthoRH_ZO(min.x, max.x, min.y, max.y,
|
||||
-max.z - 500., -min.z + 500.);
|
||||
glm::dmat4 shadow_proj_clamped = glm::orthoRH_ZO(
|
||||
min.x, max.x, min.y, max.y, -max.z - 500., -min.z + 500.);
|
||||
|
||||
m_cascade_matrices[i] = shadow_proj * shadow_view;
|
||||
m_cascade_matrices_clamped[i] = shadow_proj_clamped * shadow_view;
|
||||
}
|
||||
}
|
||||
|
||||
void MaShadowMap::UpdateConstants(nvrhi::ICommandList* command_list) {
|
||||
{
|
||||
ShadowProjectionConstants constants{};
|
||||
for (int i = 0; i < std::min(m_cascade_matrices.size(),
|
||||
constants.m_shadow_projection.size());
|
||||
++i) {
|
||||
constants.m_shadow_projection[i] = m_cascade_matrices[i];
|
||||
constants.m_shadow_planes[i] =
|
||||
m_shadow_planes[i + 1] * m_shadow_planes[i + 1];
|
||||
}
|
||||
command_list->writeBuffer(m_projection_buffer, &constants,
|
||||
sizeof(constants));
|
||||
}
|
||||
}
|
||||
|
||||
void MaShadowMap::GetViewPlaneCornersWorldSpace(
|
||||
const glm::dmat4& projection, const glm::dmat4& inverse_projection,
|
||||
const glm::dmat4& inverse_view, const double& d, bool normalize,
|
||||
glm::dvec3& ll, glm::dvec3& lr, glm::dvec3& ul, glm::dvec3& ur) {
|
||||
glm::dvec4 lll{-1., -1., 1., 1.};
|
||||
glm::dvec4 llr{1., -1., 1., 1.};
|
||||
glm::dvec4 lul{-1., 1., 1., 1.};
|
||||
glm::dvec4 lur{1., 1., 1., 1.};
|
||||
|
||||
auto unproject = [&](glm::dvec4& v) -> void {
|
||||
v = inverse_projection * v;
|
||||
if (normalize) {
|
||||
v /= v.w;
|
||||
v *= d / glm::length(glm::dvec3(v.xyz));
|
||||
} else {
|
||||
v *= d / -v.z;
|
||||
}
|
||||
v.w = 1.;
|
||||
};
|
||||
|
||||
unproject(lll);
|
||||
unproject(llr);
|
||||
unproject(lul);
|
||||
unproject(lur);
|
||||
|
||||
ll = inverse_view * lll;
|
||||
lr = inverse_view * llr;
|
||||
ul = inverse_view * lul;
|
||||
ur = inverse_view * lur;
|
||||
}
|
||||
30
betterRenderer/renderer/source/csm.h
Normal file
30
betterRenderer/renderer/source/csm.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "nvrenderer/resource_registry.h"
|
||||
|
||||
struct MaShadowMap : public MaResourceRegistry {
|
||||
MaShadowMap(class NvRenderer* renderer);
|
||||
void Init();
|
||||
std::vector<double> m_shadow_planes;
|
||||
std::vector<glm::dmat4> m_cascade_matrices;
|
||||
std::vector<glm::dmat4> m_cascade_matrices_clamped;
|
||||
double m_far_plane_first;
|
||||
double m_far_plane_last;
|
||||
double m_shadow_far_plane;
|
||||
nvrhi::BufferHandle m_projection_buffer;
|
||||
nvrhi::BufferHandle m_inverse_projection_buffer;
|
||||
class NvRendererBackend* m_backend;
|
||||
void CalcCascades(const glm::dmat4& projection, const glm::dmat4& view,
|
||||
const glm::dvec3& light);
|
||||
void UpdateConstants(nvrhi::ICommandList* command_list);
|
||||
static void GetViewPlaneCornersWorldSpace(
|
||||
const glm::dmat4& projection, const glm::dmat4& inverse_projection,
|
||||
const glm::dmat4& inverse_view, const double& d, bool normalize,
|
||||
glm::dvec3& ll, glm::dvec3& lr, glm::dvec3& ul, glm::dvec3& ur);
|
||||
struct ShadowProjectionConstants {
|
||||
std::array<glm::mat4, 8> m_shadow_projection;
|
||||
std::array<float, 8> m_shadow_planes;
|
||||
};
|
||||
};
|
||||
21
betterRenderer/renderer/source/ddspp/LICENSE
Normal file
21
betterRenderer/renderer/source/ddspp/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2023 Emilio López
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1122
betterRenderer/renderer/source/ddspp/ddspp.h
Normal file
1122
betterRenderer/renderer/source/ddspp/ddspp.h
Normal file
File diff suppressed because it is too large
Load Diff
942
betterRenderer/renderer/source/environment.cpp
Normal file
942
betterRenderer/renderer/source/environment.cpp
Normal file
@@ -0,0 +1,942 @@
|
||||
#include "environment.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "gbuffer.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "simulationenvironment.h"
|
||||
#include "sky.h"
|
||||
|
||||
int MaEnvironment::GetCurrentSetIndex() {
|
||||
if (m_locked_set_index >= 0 && IsReady()) {
|
||||
m_current_set_index = std::exchange(m_locked_set_index, -1);
|
||||
}
|
||||
return m_current_set_index;
|
||||
}
|
||||
|
||||
nvrhi::BindingSetHandle MaEnvironment::GetBindingSet(int pass, int mip, int set,
|
||||
NvGbuffer* gbuffer_cube) {
|
||||
using namespace nvrhi;
|
||||
static std::map<int, BindingSetHandle> cache{};
|
||||
auto& handle =
|
||||
cache[((set & 0xf) << 28) | ((pass & 0xfff) << 16) | (mip & 0xffff)];
|
||||
if (!handle) {
|
||||
switch (pass) {
|
||||
case 1:
|
||||
if (!mip) {
|
||||
int previous_set = set - 1;
|
||||
while (previous_set < 0)
|
||||
previous_set += std::size(m_dynamic_envmap_diffuse);
|
||||
handle = m_backend->GetDevice()->createBindingSet(
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::Texture_SRV(0, m_skybox))
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
1, gbuffer_cube->m_gbuffer_diffuse, Format::UNKNOWN,
|
||||
AllSubresources))
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
2, gbuffer_cube->m_gbuffer_normal, Format::UNKNOWN,
|
||||
AllSubresources))
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
3, gbuffer_cube->m_gbuffer_params, Format::UNKNOWN,
|
||||
AllSubresources))
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
4, gbuffer_cube->m_gbuffer_depth, Format::UNKNOWN,
|
||||
AllSubresources))
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
5, m_dynamic_envmap_diffuse[previous_set],
|
||||
Format::UNKNOWN, AllSubresources))
|
||||
.addItem(BindingSetItem::Texture_SRV(13, m_sky_texture))
|
||||
.addItem(BindingSetItem::Texture_SRV(14, m_aerial_lut))
|
||||
.addItem(BindingSetItem::Texture_UAV(
|
||||
0, m_dynamic_skybox[set], Format::UNKNOWN,
|
||||
TextureSubresourceSet(
|
||||
0, 1, 0, TextureSubresourceSet::AllArraySlices),
|
||||
TextureDimension::Texture2DArray))
|
||||
.addItem(BindingSetItem::Sampler(0, m_sampler_linear_clamp))
|
||||
.addItem(BindingSetItem::Sampler(1, m_sampler_point_clamp))
|
||||
.addItem(BindingSetItem::Sampler(13, m_sampler_linear_clamp))
|
||||
.addItem(BindingSetItem::ConstantBuffer(
|
||||
13, m_sky->m_sky_constants))
|
||||
.addItem(BindingSetItem::ConstantBuffer(
|
||||
1, m_face_inverse_projection_buffer))
|
||||
.addItem(BindingSetItem::PushConstants(
|
||||
0, sizeof(CubeLightingConstants))),
|
||||
m_bl_gbuffer_lighting);
|
||||
} else {
|
||||
handle = m_backend->GetDevice()->createBindingSet(
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
1, m_dynamic_skybox[set], Format::UNKNOWN,
|
||||
TextureSubresourceSet(
|
||||
mip - 1, 1, 0,
|
||||
TextureSubresourceSet::AllArraySlices)))
|
||||
.addItem(BindingSetItem::Texture_UAV(
|
||||
0, m_dynamic_skybox[set], Format::UNKNOWN,
|
||||
TextureSubresourceSet(
|
||||
mip, 1, 0, TextureSubresourceSet::AllArraySlices),
|
||||
TextureDimension::Texture2DArray))
|
||||
.addItem(BindingSetItem::Sampler(1, m_sampler_linear_clamp))
|
||||
.addItem(BindingSetItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))),
|
||||
m_bl_diffuse_ibl);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
handle = m_backend->GetDevice()->createBindingSet(
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
1, m_dynamic_skybox[set], Format::UNKNOWN,
|
||||
TextureSubresourceSet(
|
||||
0, TextureSubresourceSet::AllMipLevels, 0,
|
||||
TextureSubresourceSet::AllArraySlices)))
|
||||
.addItem(BindingSetItem::Texture_UAV(
|
||||
0, m_dynamic_envmap_diffuse[set], Format::UNKNOWN,
|
||||
AllSubresources, TextureDimension::Texture2DArray))
|
||||
.addItem(BindingSetItem::Sampler(1, m_sampler_linear_clamp))
|
||||
.addItem(BindingSetItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))),
|
||||
m_bl_diffuse_ibl);
|
||||
break;
|
||||
case 3:
|
||||
handle = m_backend->GetDevice()->createBindingSet(
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::Texture_SRV(
|
||||
1, m_dynamic_skybox[set], Format::UNKNOWN,
|
||||
TextureSubresourceSet(
|
||||
0, TextureSubresourceSet::AllMipLevels, 0,
|
||||
TextureSubresourceSet::AllArraySlices)))
|
||||
.addItem(BindingSetItem::Texture_UAV(
|
||||
0, m_dynamic_envmap_specular[set], Format::UNKNOWN,
|
||||
TextureSubresourceSet(
|
||||
mip, 1, 0, TextureSubresourceSet::AllArraySlices),
|
||||
TextureDimension::Texture2DArray))
|
||||
.addItem(BindingSetItem::Sampler(1, m_sampler_linear_clamp))
|
||||
.addItem(BindingSetItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))),
|
||||
m_bl_diffuse_ibl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
MaEnvironment::MaEnvironment(NvRenderer* renderer)
|
||||
: MaResourceRegistry(renderer),
|
||||
m_backend(renderer->m_backend.get()),
|
||||
m_gbuffer(renderer->m_gbuffer.get()),
|
||||
m_sky(renderer->m_sky.get()) {}
|
||||
|
||||
void MaEnvironment::Init(const std::string& texture, float pre_exposure) {
|
||||
InitResourceRegistry();
|
||||
using namespace nvrhi;
|
||||
m_cs_sample_equirectangular = m_backend->CreateShader(
|
||||
"envmap_CSSampleEquirectangular", ShaderType::Compute);
|
||||
m_cs_diffuse_ibl =
|
||||
m_backend->CreateShader("envmap_CSDiffuseIBL", ShaderType::Compute);
|
||||
m_cs_specular_ibl =
|
||||
m_backend->CreateShader("envmap_CSSpecularIBL", ShaderType::Compute);
|
||||
m_cs_generate_mip =
|
||||
m_backend->CreateShader("envmap_CSGenerateCubeMip", ShaderType::Compute);
|
||||
m_cs_integrate_brdf =
|
||||
m_backend->CreateShader("envmap_CSIntegrateBRDF", ShaderType::Compute);
|
||||
m_cs_gbuffer_lighting =
|
||||
m_backend->CreateShader("gbuffer_cube_lighting", ShaderType::Compute);
|
||||
|
||||
m_face_inverse_projection_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(
|
||||
sizeof(glm::mat4), "Inverse Face Projection Buffer")
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
{
|
||||
auto command_list = m_backend->GetDevice()->createCommandList();
|
||||
glm::mat4 inverse_projection = glm::inverse(
|
||||
glm::perspectiveFovRH_ZO(glm::radians(90.f), 1.f, 1.f, .1f,
|
||||
Global.reflectiontune.range_instances));
|
||||
command_list->open();
|
||||
command_list->writeBuffer(m_face_inverse_projection_buffer,
|
||||
&inverse_projection, sizeof(inverse_projection));
|
||||
command_list->close();
|
||||
m_backend->GetDevice()->executeCommandList(command_list);
|
||||
}
|
||||
|
||||
m_bl_gbuffer_lighting = m_backend->GetDevice()->createBindingLayout(
|
||||
BindingLayoutDesc()
|
||||
.setVisibility(ShaderType::Compute)
|
||||
.addItem(BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(1))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(2))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(3))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(4))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(5))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(13))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(14))
|
||||
.addItem(BindingLayoutItem::Texture_UAV(0))
|
||||
.addItem(BindingLayoutItem::Sampler(0))
|
||||
.addItem(BindingLayoutItem::Sampler(1))
|
||||
.addItem(BindingLayoutItem::Sampler(13))
|
||||
.addItem(BindingLayoutItem::VolatileConstantBuffer(13))
|
||||
.addItem(BindingLayoutItem::ConstantBuffer(1))
|
||||
.addItem(BindingLayoutItem::PushConstants(
|
||||
0, sizeof(CubeLightingConstants))));
|
||||
m_pso_gbuffer_lighting = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.addBindingLayout(m_bl_gbuffer_lighting)
|
||||
.setComputeShader(m_cs_gbuffer_lighting));
|
||||
|
||||
m_bl_sample_equirectangular = m_backend->GetDevice()->createBindingLayout(
|
||||
BindingLayoutDesc()
|
||||
.setVisibility(ShaderType::Compute)
|
||||
.addItem(BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(BindingLayoutItem::Texture_UAV(0))
|
||||
.addItem(BindingLayoutItem::Sampler(0))
|
||||
.addItem(BindingLayoutItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))));
|
||||
m_pso_sample_equirectangular = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.addBindingLayout(m_bl_sample_equirectangular)
|
||||
.setComputeShader(m_cs_sample_equirectangular));
|
||||
|
||||
m_bl_diffuse_ibl = m_backend->GetDevice()->createBindingLayout(
|
||||
BindingLayoutDesc()
|
||||
.setVisibility(ShaderType::Compute)
|
||||
.addItem(BindingLayoutItem::Texture_SRV(1))
|
||||
.addItem(BindingLayoutItem::Texture_UAV(0))
|
||||
.addItem(BindingLayoutItem::Sampler(1))
|
||||
.addItem(BindingLayoutItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))));
|
||||
m_pso_diffuse_ibl = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.addBindingLayout(m_bl_diffuse_ibl)
|
||||
.setComputeShader(m_cs_diffuse_ibl));
|
||||
|
||||
m_pso_generate_mip = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.addBindingLayout(m_bl_diffuse_ibl)
|
||||
.setComputeShader(m_cs_generate_mip));
|
||||
|
||||
m_pso_specular_ibl = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.addBindingLayout(m_bl_diffuse_ibl)
|
||||
.setComputeShader(m_cs_specular_ibl));
|
||||
|
||||
m_bl_integrate_brdf = m_backend->GetDevice()->createBindingLayout(
|
||||
BindingLayoutDesc()
|
||||
.setVisibility(ShaderType::Compute)
|
||||
.addItem(BindingLayoutItem::Texture_UAV(1))
|
||||
.addItem(BindingLayoutItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))));
|
||||
|
||||
m_pso_integrate_brdf = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.addBindingLayout(m_bl_integrate_brdf)
|
||||
.setComputeShader(m_cs_integrate_brdf));
|
||||
|
||||
int skybox_width = NvRenderer::Config()->m_envmap_resolution;
|
||||
int dynamic_skybox_width = NvRenderer::Config()->m_envmap_resolution;
|
||||
int skybox_mips = static_cast<int>(floor(log2(skybox_width))) + 1;
|
||||
int dynamic_skybox_mips =
|
||||
static_cast<int>(floor(log2(dynamic_skybox_width))) + 1;
|
||||
int diffuse_width = 64;
|
||||
int specular_width = NvRenderer::Config()->m_envmap_resolution;
|
||||
int dynamic_specular_width = NvRenderer::Config()->m_envmap_resolution;
|
||||
int specular_mips = 6;
|
||||
|
||||
m_skybox = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setArraySize(6)
|
||||
.setWidth(skybox_width)
|
||||
.setHeight(skybox_width)
|
||||
.setMipLevels(skybox_mips)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Skybox"));
|
||||
|
||||
m_dynamic_skybox[0] = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setArraySize(6)
|
||||
.setWidth(dynamic_skybox_width)
|
||||
.setHeight(dynamic_skybox_width)
|
||||
.setMipLevels(dynamic_skybox_mips)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Dynamic Skybox"));
|
||||
|
||||
m_dynamic_skybox[1] = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setArraySize(6)
|
||||
.setWidth(dynamic_skybox_width)
|
||||
.setHeight(dynamic_skybox_width)
|
||||
.setMipLevels(dynamic_skybox_mips)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Dynamic Skybox"));
|
||||
|
||||
m_sampler_linear_clamp_v_repeat_h = m_backend->GetDevice()->createSampler(
|
||||
SamplerDesc()
|
||||
.setAllAddressModes(SamplerAddressMode::Clamp)
|
||||
.setAddressU(SamplerAddressMode::Repeat)
|
||||
.setAllFilters(true));
|
||||
|
||||
m_sampler_linear_clamp = m_backend->GetDevice()->createSampler(
|
||||
SamplerDesc()
|
||||
.setAllAddressModes(SamplerAddressMode::Clamp)
|
||||
.setAllFilters(true));
|
||||
|
||||
m_sampler_point_clamp = m_backend->GetDevice()->createSampler(
|
||||
SamplerDesc()
|
||||
.setAllAddressModes(SamplerAddressMode::Clamp)
|
||||
.setAllFilters(false));
|
||||
CommandListHandle command_list = m_backend->GetDevice()->createCommandList();
|
||||
command_list->open();
|
||||
|
||||
RegisterResource(true, "sampler_linear_clamp_v_repeat_h",
|
||||
m_sampler_linear_clamp_v_repeat_h, ResourceType::Sampler);
|
||||
|
||||
{
|
||||
m_brdf_lut = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setFormat(Format::RG16_FLOAT)
|
||||
.setDimension(TextureDimension::Texture2D)
|
||||
.setWidth(512)
|
||||
.setHeight(512)
|
||||
.setMipLevels(1)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("BRDF LUT"));
|
||||
|
||||
RegisterResource(true, "env_brdf_lut", m_brdf_lut,
|
||||
ResourceType::Texture_SRV);
|
||||
|
||||
BindingSetHandle binding_set = m_backend->GetDevice()->createBindingSet(
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::Texture_UAV(1, m_brdf_lut))
|
||||
.addItem(BindingSetItem::PushConstants(
|
||||
0, sizeof(EnvironmentFilterConstants))),
|
||||
m_bl_integrate_brdf);
|
||||
|
||||
ComputeState compute_state = ComputeState()
|
||||
.setPipeline(m_pso_integrate_brdf)
|
||||
.addBindingSet(binding_set);
|
||||
|
||||
int target_width = m_brdf_lut->getDesc().width;
|
||||
int target_height = m_brdf_lut->getDesc().height;
|
||||
command_list->setComputeState(compute_state);
|
||||
|
||||
EnvironmentFilterConstants filter_constants{};
|
||||
filter_constants.m_sample_count = 4096;
|
||||
command_list->setPushConstants(&filter_constants, sizeof(filter_constants));
|
||||
|
||||
command_list->dispatch((target_width + 31) / 32, (target_height + 31) / 32,
|
||||
1);
|
||||
}
|
||||
|
||||
{
|
||||
m_envmap_diffuse = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setArraySize(6)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setWidth(diffuse_width)
|
||||
.setHeight(diffuse_width)
|
||||
.setMipLevels(1)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Diffuse IBL"));
|
||||
for (int i = 0; i < std::size(m_dynamic_envmap_diffuse); ++i) {
|
||||
auto debug_name = fmt::format("Dynamic Diffuse IBL[{}]", i);
|
||||
auto resource_name = fmt::format("env_dynamic_diffuse_{}", i);
|
||||
m_dynamic_envmap_diffuse[i] = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setArraySize(6)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setWidth(diffuse_width)
|
||||
.setHeight(diffuse_width)
|
||||
.setMipLevels(1)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(i ? ResourceStates::UnorderedAccess
|
||||
: ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName(debug_name.c_str()));
|
||||
}
|
||||
RegisterResource(true, "env_dynamic_diffuse", m_dynamic_envmap_diffuse[0],
|
||||
ResourceType::Texture_SRV);
|
||||
}
|
||||
|
||||
{
|
||||
m_envmap_specular = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setArraySize(6)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setWidth(specular_width)
|
||||
.setHeight(specular_width)
|
||||
.setMipLevels(specular_mips)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Specular IBL"));
|
||||
for (int i = 0; i < std::size(m_dynamic_envmap_specular); ++i) {
|
||||
auto debug_name = fmt::format("Dynamic Specular IBL[{}]", i);
|
||||
auto resource_name = fmt::format("env_dynamic_specular_{}", i);
|
||||
m_dynamic_envmap_specular[i] = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc()
|
||||
.setArraySize(6)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setDimension(TextureDimension::TextureCube)
|
||||
.setWidth(dynamic_specular_width)
|
||||
.setHeight(dynamic_specular_width)
|
||||
.setMipLevels(specular_mips)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(i ? ResourceStates::UnorderedAccess
|
||||
: ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName(debug_name.c_str()));
|
||||
}
|
||||
RegisterResource(true, "env_dynamic_specular", m_dynamic_envmap_specular[0],
|
||||
ResourceType::Texture_SRV);
|
||||
}
|
||||
|
||||
m_sky_texture = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc(m_sky->m_aerial_lut->m_sky_texture->getDesc())
|
||||
.setDebugName("Envmap Sky Texture"));
|
||||
|
||||
m_aerial_lut = m_backend->GetDevice()->createTexture(
|
||||
TextureDesc(m_sky->m_aerial_lut->m_lut->getDesc())
|
||||
.setDebugName("Envmap Aerial LUT"));
|
||||
|
||||
command_list->close();
|
||||
m_backend->GetDevice()->executeCommandList(command_list);
|
||||
|
||||
m_environment_pass = std::make_shared<EnvironmentRenderPass>(this);
|
||||
m_environment_pass->Init();
|
||||
}
|
||||
|
||||
void MaEnvironment::Render(nvrhi::ICommandList* command_list,
|
||||
glm::dmat4 const& jitter,
|
||||
glm::dmat4 const& projection, glm::dmat4 const& view,
|
||||
glm::dmat4 const& previous_view_projection) {
|
||||
m_environment_pass->UpdateConstants(command_list, jitter, projection, view,
|
||||
previous_view_projection);
|
||||
m_environment_pass->Render(command_list);
|
||||
}
|
||||
|
||||
void MaEnvironment::Filter(uint64_t sky_instance_id, NvGbuffer* gbuffer_cube) {
|
||||
using namespace nvrhi;
|
||||
int pixels_per_pass_sqr = m_pixels_per_pass * m_pixels_per_pass;
|
||||
int target_set_index = 1;
|
||||
int source_set_index = 0;
|
||||
|
||||
static uint64_t prev_instance = -1;
|
||||
if (filter_step == 0) {
|
||||
if (!IsReady()) {
|
||||
return;
|
||||
}
|
||||
// m_locked_set_index = (GetCurrentSetIndex() + 1) % 2;
|
||||
m_ready_handle = m_backend->GetDevice()->createEventQuery();
|
||||
|
||||
// CommandListHandle transition_command_list =
|
||||
// m_backend->GetDevice()->createCommandList();
|
||||
// transition_command_list->open();
|
||||
//
|
||||
// transition_command_list->setTextureState(m_sky->m_aerial_lut->m_sky_texture,
|
||||
// nvrhi::AllSubresources,
|
||||
// nvrhi::ResourceStates::Common);
|
||||
// transition_command_list->setTextureState(m_sky->m_aerial_lut->m_lut,
|
||||
// nvrhi::AllSubresources,
|
||||
// nvrhi::ResourceStates::Common);
|
||||
//
|
||||
// transition_command_list->close();
|
||||
// auto transition_id =
|
||||
// m_backend->GetDevice()->executeCommandList(transition_command_list);
|
||||
//
|
||||
// m_backend->GetDevice()->queueWaitForCommandList(
|
||||
// nvrhi::CommandQueue::Copy, nvrhi::CommandQueue::Graphics,
|
||||
// transition_id);
|
||||
|
||||
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
|
||||
CommandListParameters()
|
||||
.setQueueType(CommandQueue::Graphics)
|
||||
.setEnableImmediateExecution(false));
|
||||
command_list->open();
|
||||
|
||||
{
|
||||
auto diff_desc = m_sky_texture->getDesc();
|
||||
for (int i = 0; i < diff_desc.arraySize; ++i) {
|
||||
for (int j = 0; j < diff_desc.mipLevels; ++j) {
|
||||
auto slice = nvrhi::TextureSlice()
|
||||
.setArraySlice(i)
|
||||
.setMipLevel(j)
|
||||
.setWidth(-1)
|
||||
.setHeight(-1)
|
||||
.resolve(diff_desc);
|
||||
command_list->copyTexture(m_sky_texture, slice,
|
||||
m_sky->m_aerial_lut->m_sky_texture, slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto diff_desc = m_aerial_lut->getDesc();
|
||||
for (int i = 0; i < diff_desc.arraySize; ++i) {
|
||||
for (int j = 0; j < diff_desc.mipLevels; ++j) {
|
||||
auto slice = nvrhi::TextureSlice()
|
||||
.setArraySlice(i)
|
||||
.setMipLevel(j)
|
||||
.setWidth(-1)
|
||||
.setHeight(-1)
|
||||
.resolve(diff_desc);
|
||||
command_list->copyTexture(m_aerial_lut, slice,
|
||||
m_sky->m_aerial_lut->m_lut, slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// auto diff_desc = m_dynamic_envmap_diffuse[0]->getDesc();
|
||||
// for (int i = 0; i < 6; ++i) {
|
||||
// for (int j = 0; j < diff_desc.mipLevels; ++j) {
|
||||
// auto slice = nvrhi::TextureSlice()
|
||||
// .setArraySlice(i)
|
||||
// .setMipLevel(j)
|
||||
// .setWidth(-1)
|
||||
// .setHeight(-1)
|
||||
// .resolve(diff_desc);
|
||||
// command_list->copyTexture(m_dynamic_envmap_diffuse[0], slice,
|
||||
// m_dynamic_envmap_diffuse[1], slice);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// auto spec_desc = m_dynamic_envmap_specular[0]->getDesc();
|
||||
// for (int i = 0; i < 6; ++i) {
|
||||
// for (int j = 0; j < spec_desc.mipLevels; ++j) {
|
||||
// auto slice = nvrhi::TextureSlice()
|
||||
// .setArraySlice(i)
|
||||
// .setMipLevel(j)
|
||||
// .setWidth(-1)
|
||||
// .setHeight(-1)
|
||||
// .resolve(spec_desc);
|
||||
// command_list->copyTexture(m_dynamic_envmap_specular[0], slice,
|
||||
// m_dynamic_envmap_specular[1], slice);
|
||||
// }
|
||||
//}
|
||||
|
||||
// command_list->setTextureState(m_sky_texture, nvrhi::AllSubresources,
|
||||
// nvrhi::ResourceStates::Common);
|
||||
// command_list->setTextureState(m_aerial_lut, nvrhi::AllSubresources,
|
||||
// nvrhi::ResourceStates::Common);
|
||||
command_list->close();
|
||||
prev_instance = m_backend->GetDevice()->executeCommandList(
|
||||
command_list, nvrhi::CommandQueue::Graphics);
|
||||
// CommandListHandle transition_command_list =
|
||||
// m_backend->GetDevice()->createCommandList();
|
||||
// transition_command_list->open();
|
||||
//
|
||||
// transition_command_list->setTextureState(m_skybox, AllSubresources,
|
||||
// ResourceStates::UnorderedAccess);
|
||||
// transition_command_list->setTextureState(m_dynamic_skybox[target_set_index],
|
||||
// AllSubresources,
|
||||
// ResourceStates::UnorderedAccess);
|
||||
// transition_command_list->setTextureState(
|
||||
// m_dynamic_envmap_diffuse[target_set_index], AllSubresources,
|
||||
// ResourceStates::UnorderedAccess);
|
||||
// transition_command_list->setTextureState(
|
||||
// m_dynamic_envmap_specular[target_set_index], AllSubresources,
|
||||
// ResourceStates::UnorderedAccess);
|
||||
// transition_command_list->setTextureState(
|
||||
// m_dynamic_envmap_diffuse[source_set_index], AllSubresources,
|
||||
// ResourceStates::ShaderResource);
|
||||
// transition_command_list->setTextureState(
|
||||
// m_dynamic_envmap_specular[source_set_index], AllSubresources,
|
||||
// ResourceStates::ShaderResource);
|
||||
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_diffuse,
|
||||
// AllSubresources,
|
||||
// ResourceStates::ShaderResource);
|
||||
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_normal,
|
||||
// AllSubresources,
|
||||
// ResourceStates::ShaderResource);
|
||||
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_params,
|
||||
// AllSubresources,
|
||||
// ResourceStates::ShaderResource);
|
||||
// transition_command_list->setTextureState(gbuffer_cube->m_gbuffer_depth,
|
||||
// AllSubresources,
|
||||
// ResourceStates::ShaderResource);
|
||||
// transition_command_list->commitBarriers();
|
||||
//
|
||||
// transition_command_list->close();
|
||||
// auto instance =
|
||||
// m_backend->GetDevice()->executeCommandList(transition_command_list);
|
||||
// m_backend->GetDevice()->queueWaitForCommandList(
|
||||
// nvrhi::CommandQueue::Compute, nvrhi::CommandQueue::Graphics,
|
||||
// instance);
|
||||
++filter_step;
|
||||
mip = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter_step == 1) {
|
||||
// CommandListHandle command_list_transition =
|
||||
// m_backend->GetDevice()->createCommandList();
|
||||
// command_list_transition->open();
|
||||
//
|
||||
// command_list_transition->setTextureState(
|
||||
// m_sky->m_aerial_lut->m_sky_texture, nvrhi::AllSubresources,
|
||||
// nvrhi::ResourceStates::ShaderResource);
|
||||
//
|
||||
// command_list_transition->close();
|
||||
// auto instance =
|
||||
// m_backend->GetDevice()->executeCommandList(command_list_transition);
|
||||
|
||||
if (prev_instance != -1) {
|
||||
// m_backend->GetDevice()->queueWaitForCommandList(
|
||||
// nvrhi::CommandQueue::Compute, nvrhi::CommandQueue::Graphics,
|
||||
// prev_instance);
|
||||
// prev_instance = -1;
|
||||
}
|
||||
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
|
||||
CommandListParameters()
|
||||
.setQueueType(CommandQueue::Graphics)
|
||||
.setEnableImmediateExecution(false));
|
||||
// m_backend->GetDevice()->queueWaitForCommandList(
|
||||
// nvrhi::CommandQueue::Compute, nvrhi::CommandQueue::Graphics,
|
||||
// instance);
|
||||
command_list->open();
|
||||
|
||||
while (true) {
|
||||
int target_width = std::max(
|
||||
m_dynamic_skybox[target_set_index]->getDesc().width >> mip, 1u);
|
||||
int target_height = std::max(
|
||||
m_dynamic_skybox[target_set_index]->getDesc().height >> mip, 1u);
|
||||
|
||||
int pixels_x = std::min(m_pixels_per_pass, target_width - x);
|
||||
int pixels_y = std::min(m_pixels_per_pass, target_height - y);
|
||||
|
||||
if (pixels_x * pixels_y > pixels_per_pass_sqr) {
|
||||
break;
|
||||
}
|
||||
|
||||
pixels_per_pass_sqr -= pixels_x * pixels_y;
|
||||
|
||||
if (mip >= m_dynamic_skybox[target_set_index]->getDesc().mipLevels) {
|
||||
break;
|
||||
}
|
||||
if (!mip) m_sky->UpdateConstants(command_list);
|
||||
ComputeState compute_state;
|
||||
compute_state
|
||||
.setPipeline(mip ? m_pso_generate_mip : m_pso_gbuffer_lighting)
|
||||
.addBindingSet(
|
||||
GetBindingSet(filter_step, mip, target_set_index, gbuffer_cube));
|
||||
command_list->setComputeState(compute_state);
|
||||
|
||||
if (!mip) {
|
||||
CubeLightingConstants filter_constants{};
|
||||
const auto& daylight = Global.DayLight;
|
||||
filter_constants.m_offset_x = x;
|
||||
filter_constants.m_offset_y = y;
|
||||
filter_constants.m_offset_z = 0;
|
||||
filter_constants.m_light_direction = -daylight.direction;
|
||||
filter_constants.m_height = Global.pCamera.Pos.y;
|
||||
|
||||
m_sky->CalcLighting(filter_constants.m_light_direction,
|
||||
filter_constants.m_light_color);
|
||||
|
||||
filter_constants.m_light_color = glm::vec4(m_sky->CalcSunColor(), 1.f);
|
||||
command_list->setPushConstants(&filter_constants,
|
||||
sizeof(filter_constants));
|
||||
} else {
|
||||
EnvironmentFilterConstants filter_constants{};
|
||||
filter_constants.m_offset_x = x;
|
||||
filter_constants.m_offset_y = y;
|
||||
filter_constants.m_offset_z = 0;
|
||||
command_list->setPushConstants(&filter_constants,
|
||||
sizeof(filter_constants));
|
||||
}
|
||||
|
||||
command_list->dispatch((pixels_x + 31) / 32, (pixels_y + 31) / 32, 6);
|
||||
x += m_pixels_per_pass;
|
||||
if (x >= target_width) {
|
||||
x = 0;
|
||||
y += m_pixels_per_pass;
|
||||
}
|
||||
if (y >= target_height) {
|
||||
y = 0;
|
||||
++mip;
|
||||
}
|
||||
}
|
||||
|
||||
if (mip >= m_dynamic_skybox[target_set_index]->getDesc().mipLevels) {
|
||||
mip = 0;
|
||||
++filter_step;
|
||||
}
|
||||
|
||||
command_list->close();
|
||||
m_backend->GetDevice()->executeCommandList(command_list,
|
||||
nvrhi::CommandQueue::Graphics);
|
||||
}
|
||||
|
||||
if (filter_step == 2) {
|
||||
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
|
||||
CommandListParameters()
|
||||
.setQueueType(CommandQueue::Graphics)
|
||||
.setEnableImmediateExecution(false));
|
||||
command_list->open();
|
||||
|
||||
ComputeState compute_state =
|
||||
ComputeState()
|
||||
.setPipeline(m_pso_diffuse_ibl)
|
||||
.addBindingSet(
|
||||
GetBindingSet(filter_step, 0, target_set_index, gbuffer_cube));
|
||||
|
||||
int target_width =
|
||||
m_dynamic_envmap_diffuse[target_set_index]->getDesc().width;
|
||||
int target_height =
|
||||
m_dynamic_envmap_diffuse[target_set_index]->getDesc().height;
|
||||
command_list->setComputeState(compute_state);
|
||||
|
||||
EnvironmentFilterConstants filter_constants{};
|
||||
filter_constants.m_sample_count = 32;
|
||||
filter_constants.m_source_width =
|
||||
m_dynamic_skybox[target_set_index]->getDesc().width;
|
||||
filter_constants.m_offset_x = 0;
|
||||
filter_constants.m_offset_y = 0;
|
||||
filter_constants.m_offset_z = 0;
|
||||
filter_constants.m_mip_bias = 1.;
|
||||
command_list->setPushConstants(&filter_constants, sizeof(filter_constants));
|
||||
|
||||
command_list->dispatch((target_width + 31) / 32, (target_height + 31) / 32,
|
||||
6);
|
||||
command_list->close();
|
||||
m_backend->GetDevice()->executeCommandList(command_list,
|
||||
nvrhi::CommandQueue::Graphics);
|
||||
++filter_step;
|
||||
}
|
||||
|
||||
if (filter_step == 3) {
|
||||
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
|
||||
CommandListParameters()
|
||||
.setQueueType(CommandQueue::Graphics)
|
||||
.setEnableImmediateExecution(false));
|
||||
command_list->open();
|
||||
|
||||
while (true) {
|
||||
int target_width = std::max(
|
||||
1u,
|
||||
m_dynamic_envmap_specular[target_set_index]->getDesc().width >> mip);
|
||||
int target_height = std::max(
|
||||
1u,
|
||||
m_dynamic_envmap_specular[target_set_index]->getDesc().height >> mip);
|
||||
|
||||
int pixels_x = std::min(m_pixels_per_pass, target_width - x);
|
||||
int pixels_y = std::min(m_pixels_per_pass, target_height - y);
|
||||
|
||||
if (pixels_x * pixels_y > pixels_per_pass_sqr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mip >=
|
||||
m_dynamic_envmap_specular[target_set_index]->getDesc().mipLevels) {
|
||||
break;
|
||||
}
|
||||
|
||||
pixels_per_pass_sqr -= pixels_x * pixels_y;
|
||||
|
||||
ComputeState compute_state =
|
||||
ComputeState()
|
||||
.setPipeline(m_pso_specular_ibl)
|
||||
.addBindingSet(GetBindingSet(filter_step, mip, target_set_index,
|
||||
gbuffer_cube));
|
||||
|
||||
EnvironmentFilterConstants filter_constants{};
|
||||
filter_constants.m_sample_count = 32;
|
||||
filter_constants.m_roughness =
|
||||
glm::pow(static_cast<double>(mip) /
|
||||
(m_dynamic_envmap_specular[target_set_index]
|
||||
->getDesc()
|
||||
.mipLevels -
|
||||
1.),
|
||||
2.);
|
||||
filter_constants.m_source_width =
|
||||
m_dynamic_skybox[target_set_index]->getDesc().width;
|
||||
filter_constants.m_mip_bias =
|
||||
mip ? 1.
|
||||
: log2(target_width /
|
||||
static_cast<double>(filter_constants.m_source_width));
|
||||
filter_constants.m_offset_x = x;
|
||||
filter_constants.m_offset_y = y;
|
||||
filter_constants.m_offset_z = 0;
|
||||
command_list->setComputeState(compute_state);
|
||||
command_list->setPushConstants(&filter_constants,
|
||||
sizeof(filter_constants));
|
||||
|
||||
command_list->dispatch((pixels_x + 31) / 32, (pixels_y + 31) / 32, 6);
|
||||
|
||||
x += m_pixels_per_pass;
|
||||
if (x >= target_width) {
|
||||
x = 0;
|
||||
y += m_pixels_per_pass;
|
||||
}
|
||||
if (y >= target_height) {
|
||||
y = 0;
|
||||
++mip;
|
||||
}
|
||||
}
|
||||
command_list->close();
|
||||
m_backend->GetDevice()->executeCommandList(command_list,
|
||||
nvrhi::CommandQueue::Graphics);
|
||||
if (mip >=
|
||||
m_dynamic_envmap_specular[target_set_index]->getDesc().mipLevels) {
|
||||
mip = 0;
|
||||
filter_step = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (filter_step == 4) {
|
||||
CommandListHandle command_list = m_backend->GetDevice()->createCommandList(
|
||||
CommandListParameters()
|
||||
.setQueueType(CommandQueue::Graphics)
|
||||
.setEnableImmediateExecution(false));
|
||||
command_list->open();
|
||||
|
||||
auto diff_desc = m_dynamic_envmap_diffuse[0]->getDesc();
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
for (int j = 0; j < diff_desc.mipLevels; ++j) {
|
||||
auto slice = nvrhi::TextureSlice()
|
||||
.setArraySlice(i)
|
||||
.setMipLevel(j)
|
||||
.setWidth(-1)
|
||||
.setHeight(-1)
|
||||
.resolve(diff_desc);
|
||||
command_list->copyTexture(m_dynamic_envmap_diffuse[0], slice,
|
||||
m_dynamic_envmap_diffuse[1], slice);
|
||||
}
|
||||
}
|
||||
|
||||
auto spec_desc = m_dynamic_envmap_specular[0]->getDesc();
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
for (int j = 0; j < spec_desc.mipLevels; ++j) {
|
||||
auto slice = nvrhi::TextureSlice()
|
||||
.setArraySlice(i)
|
||||
.setMipLevel(j)
|
||||
.setWidth(-1)
|
||||
.setHeight(-1)
|
||||
.resolve(spec_desc);
|
||||
command_list->copyTexture(m_dynamic_envmap_specular[0], slice,
|
||||
m_dynamic_envmap_specular[1], slice);
|
||||
}
|
||||
}
|
||||
|
||||
command_list->close();
|
||||
m_backend->GetDevice()->executeCommandList(command_list,
|
||||
nvrhi::CommandQueue::Graphics);
|
||||
filter_step = 0;
|
||||
m_backend->GetDevice()->setEventQuery(m_ready_handle,
|
||||
nvrhi::CommandQueue::Graphics);
|
||||
}
|
||||
}
|
||||
|
||||
bool MaEnvironment::IsReady() const {
|
||||
return !m_ready_handle ||
|
||||
m_backend->GetDevice()->pollEventQuery(m_ready_handle);
|
||||
}
|
||||
|
||||
EnvironmentRenderPass::EnvironmentRenderPass(MaEnvironment* environment)
|
||||
: FullScreenPass(environment->m_backend),
|
||||
m_environment(environment),
|
||||
m_gbuffer(environment->m_gbuffer),
|
||||
m_skybox_texture(environment->m_skybox) {}
|
||||
|
||||
void EnvironmentRenderPass::Init() {
|
||||
m_environment_constants = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(
|
||||
sizeof(EnvironmentConstants), "Skybox Draw Constants", 16)
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
m_binding_layout = m_backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::Pixel)
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(13))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(13))
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(0)));
|
||||
auto sampler = m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::Clamp)
|
||||
.setAllFilters(true));
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
m_binding_set[i] = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::Texture_SRV(0, m_environment->m_skybox))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
13, m_environment->m_sky->m_aerial_lut->m_sky_texture))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
0, m_environment->m_sampler_linear_clamp))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
13, m_environment->m_sampler_linear_clamp_v_repeat_h))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
0, m_environment_constants)),
|
||||
m_binding_layout);
|
||||
}
|
||||
|
||||
m_pixel_shader = m_backend->CreateShader("skybox", nvrhi::ShaderType::Pixel);
|
||||
FullScreenPass::Init();
|
||||
}
|
||||
|
||||
void EnvironmentRenderPass::CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
|
||||
FullScreenPass::CreatePipelineDesc(pipeline_desc);
|
||||
pipeline_desc.addBindingLayout(m_binding_layout);
|
||||
pipeline_desc.setPixelShader(m_pixel_shader);
|
||||
pipeline_desc.renderState.setDepthStencilState(
|
||||
nvrhi::DepthStencilState()
|
||||
.enableDepthTest()
|
||||
.disableDepthWrite()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::GreaterOrEqual)
|
||||
.disableStencil());
|
||||
}
|
||||
|
||||
void EnvironmentRenderPass::UpdateConstants(
|
||||
nvrhi::ICommandList* command_list, glm::dmat4 const& jitter,
|
||||
glm::dmat4 const& projection, glm::dmat4 const& view,
|
||||
glm::dmat4 const& previous_view_projection) {
|
||||
EnvironmentConstants constants{};
|
||||
|
||||
constants.m_reproject_matrix =
|
||||
glm::inverse(projection * view) * previous_view_projection;
|
||||
constants.m_inverse_view_projection =
|
||||
glm::inverse(jitter * projection * glm::dmat4(glm::dmat3(view)));
|
||||
constants.m_sun_direction = simulation::Environment.sun().getDirection();
|
||||
constants.m_moon_direction = simulation::Environment.moon().getDirection();
|
||||
constants.m_height = Global.pCamera.Pos.y;
|
||||
|
||||
command_list->writeBuffer(m_environment_constants, &constants,
|
||||
sizeof(constants));
|
||||
}
|
||||
|
||||
void EnvironmentRenderPass::Render(nvrhi::ICommandList* command_list) {
|
||||
nvrhi::GraphicsState graphics_state;
|
||||
InitState(graphics_state);
|
||||
graphics_state.addBindingSet(
|
||||
m_binding_set[m_environment->GetCurrentSetIndex()]);
|
||||
command_list->setGraphicsState(graphics_state);
|
||||
Draw(command_list);
|
||||
}
|
||||
|
||||
nvrhi::IFramebuffer* EnvironmentRenderPass::GetFramebuffer() {
|
||||
return m_gbuffer->m_framebuffer;
|
||||
}
|
||||
140
betterRenderer/renderer/source/environment.h
Normal file
140
betterRenderer/renderer/source/environment.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include "fullscreenpass.h"
|
||||
#include "nvrenderer/resource_registry.h"
|
||||
#include "nvrhi/nvrhi.h"
|
||||
|
||||
struct MaEnvironment : public MaResourceRegistry {
|
||||
nvrhi::TextureHandle m_skybox;
|
||||
nvrhi::TextureHandle m_dynamic_skybox[2];
|
||||
nvrhi::TextureHandle m_envmap_specular;
|
||||
nvrhi::TextureHandle m_dynamic_envmap_specular[2];
|
||||
nvrhi::TextureHandle m_envmap_diffuse;
|
||||
nvrhi::TextureHandle m_dynamic_envmap_diffuse[2];
|
||||
nvrhi::TextureHandle m_hdri;
|
||||
nvrhi::TextureHandle m_brdf_lut;
|
||||
nvrhi::TextureHandle m_sky_texture;
|
||||
nvrhi::TextureHandle m_aerial_lut;
|
||||
|
||||
nvrhi::SamplerHandle m_sampler_linear_clamp_v_repeat_h;
|
||||
nvrhi::SamplerHandle m_sampler_linear_clamp;
|
||||
nvrhi::SamplerHandle m_sampler_point_clamp;
|
||||
|
||||
nvrhi::ShaderHandle m_cs_sample_equirectangular;
|
||||
nvrhi::ComputePipelineHandle m_pso_sample_equirectangular;
|
||||
nvrhi::BindingLayoutHandle m_bl_sample_equirectangular;
|
||||
|
||||
nvrhi::ShaderHandle m_cs_gbuffer_lighting;
|
||||
nvrhi::ComputePipelineHandle m_pso_gbuffer_lighting;
|
||||
nvrhi::BindingLayoutHandle m_bl_gbuffer_lighting;
|
||||
|
||||
nvrhi::ShaderHandle m_cs_diffuse_ibl;
|
||||
nvrhi::ComputePipelineHandle m_pso_diffuse_ibl;
|
||||
nvrhi::BindingLayoutHandle m_bl_diffuse_ibl;
|
||||
|
||||
nvrhi::ShaderHandle m_cs_specular_ibl;
|
||||
nvrhi::ComputePipelineHandle m_pso_specular_ibl;
|
||||
|
||||
nvrhi::ShaderHandle m_cs_generate_mip;
|
||||
nvrhi::ComputePipelineHandle m_pso_generate_mip;
|
||||
|
||||
nvrhi::ShaderHandle m_cs_integrate_brdf;
|
||||
nvrhi::ComputePipelineHandle m_pso_integrate_brdf;
|
||||
nvrhi::BindingLayoutHandle m_bl_integrate_brdf;
|
||||
nvrhi::EventQueryHandle m_ready_handle;
|
||||
|
||||
nvrhi::BufferHandle m_face_inverse_projection_buffer;
|
||||
|
||||
class NvRendererBackend* m_backend;
|
||||
struct NvGbuffer* m_gbuffer;
|
||||
struct Sky* m_sky;
|
||||
int filter_step = 0;
|
||||
int mip = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
int m_current_set_index = 0;
|
||||
int m_locked_set_index = 0;
|
||||
int GetCurrentSetIndex();
|
||||
|
||||
std::shared_ptr<struct EnvironmentRenderPass> m_environment_pass;
|
||||
|
||||
const int m_pixels_per_pass = 256;
|
||||
|
||||
nvrhi::BindingSetHandle GetBindingSet(int pass, int mip, int set,
|
||||
NvGbuffer* gbuffer_cube);
|
||||
|
||||
struct EnvironmentFilterConstants {
|
||||
EnvironmentFilterConstants() {
|
||||
m_pre_exposure_mul = 1.f;
|
||||
m_roughness = 0.f;
|
||||
m_sample_count = 32.f;
|
||||
m_source_width = 2048.f;
|
||||
m_mip_bias = 0.f;
|
||||
m_offset_x = 0;
|
||||
m_offset_y = 0;
|
||||
m_offset_z = 0;
|
||||
}
|
||||
float m_pre_exposure_mul;
|
||||
float m_roughness;
|
||||
float m_sample_count;
|
||||
float m_source_width;
|
||||
float m_mip_bias;
|
||||
uint32_t m_offset_x;
|
||||
uint32_t m_offset_y;
|
||||
uint32_t m_offset_z;
|
||||
};
|
||||
|
||||
struct CubeLightingConstants {
|
||||
uint32_t m_offset_x;
|
||||
uint32_t m_offset_y;
|
||||
uint32_t m_offset_z;
|
||||
uint32_t m_unused;
|
||||
glm::vec3 m_light_direction;
|
||||
float m_height;
|
||||
glm::vec3 m_light_color;
|
||||
};
|
||||
|
||||
MaEnvironment(class NvRenderer* renderer);
|
||||
void Init(const std::string& texture, float pre_exposure);
|
||||
void Render(nvrhi::ICommandList* command_list, glm::dmat4 const& jitter,
|
||||
glm::dmat4 const& projection, glm::dmat4 const& view,
|
||||
glm::dmat4 const& previous_view_projection);
|
||||
void Filter(uint64_t sky_instance_id, NvGbuffer* gbuffer_cube);
|
||||
bool IsReady() const;
|
||||
};
|
||||
|
||||
struct EnvironmentRenderPass : public FullScreenPass {
|
||||
EnvironmentRenderPass(MaEnvironment* environment);
|
||||
|
||||
struct EnvironmentConstants {
|
||||
glm::mat4 m_inverse_view_projection;
|
||||
glm::mat4 m_reproject_matrix;
|
||||
glm::vec3 m_sun_direction;
|
||||
float m_height;
|
||||
glm::vec3 m_moon_direction;
|
||||
};
|
||||
|
||||
virtual void Init() override;
|
||||
|
||||
virtual void CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
|
||||
|
||||
void UpdateConstants(nvrhi::ICommandList* command_list,
|
||||
glm::dmat4 const& jitter, glm::dmat4 const& projection,
|
||||
glm::dmat4 const& view,
|
||||
glm::dmat4 const& previous_view_projection);
|
||||
|
||||
virtual void Render(nvrhi::ICommandList* command_list) override;
|
||||
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
|
||||
|
||||
struct NvGbuffer* m_gbuffer;
|
||||
MaEnvironment* m_environment;
|
||||
nvrhi::BufferHandle m_environment_constants;
|
||||
nvrhi::TextureHandle m_skybox_texture;
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
nvrhi::BindingSetHandle m_binding_set[2];
|
||||
nvrhi::ShaderHandle m_pixel_shader;
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
};
|
||||
201
betterRenderer/renderer/source/fsr.cpp
Normal file
201
betterRenderer/renderer/source/fsr.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "fsr.h"
|
||||
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
#include <ffx-fsr2-api/dx12/ffx_fsr2_dx12.h>
|
||||
|
||||
#include "nvrenderer_d3d12.h"
|
||||
#endif
|
||||
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
#include <ffx-fsr2-api/vk/ffx_fsr2_vk.h>
|
||||
#include <nvrhi/vulkan.h>
|
||||
|
||||
#include "nvrenderer_vulkan.h"
|
||||
#endif
|
||||
|
||||
#include "Globals.h"
|
||||
#include "gbuffer.h"
|
||||
#include "gbufferblitpass.h"
|
||||
|
||||
NvFSR::NvFSR(NvRenderer* renderer)
|
||||
: m_backend(renderer->m_backend.get()),
|
||||
m_inputpass(renderer->m_gbuffer_blit.get()),
|
||||
m_gbuffer(renderer->m_gbuffer.get()) {}
|
||||
|
||||
void OnFsrMessage(FfxFsr2MsgType type, const wchar_t* msg) {
|
||||
//__debugbreak();
|
||||
}
|
||||
|
||||
void NvFSR::Init() {
|
||||
m_width = m_backend->GetCurrentFramebuffer()->getFramebufferInfo().width;
|
||||
m_height = m_backend->GetCurrentFramebuffer()->getFramebufferInfo().height;
|
||||
m_input_width = m_gbuffer->m_framebuffer->getFramebufferInfo().width;
|
||||
m_input_height = m_gbuffer->m_framebuffer->getFramebufferInfo().height;
|
||||
|
||||
m_color = m_inputpass->m_output;
|
||||
m_depth = m_gbuffer->m_gbuffer_depth;
|
||||
m_motion = m_gbuffer->m_gbuffer_motion;
|
||||
|
||||
m_output = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setDebugName("FSR Output")
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
m_auto_exposure_buffer = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(nvrhi::Format::RG32_FLOAT)
|
||||
.setWidth(1)
|
||||
.setHeight(1)
|
||||
.setIsUAV(true)
|
||||
.setDebugName("FSR Auto Exposure Buffer")
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true));
|
||||
auto device = m_backend->GetDevice();
|
||||
FfxFsr2ContextDescription context_description{};
|
||||
switch (device->getGraphicsAPI()) {
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
case nvrhi::GraphicsAPI::D3D12:
|
||||
m_scratch_buffer.resize(ffxFsr2GetScratchMemorySizeDX12());
|
||||
ffxFsr2GetInterfaceDX12(&m_fsr2_interface,
|
||||
m_backend->GetDevice()->getNativeObject(
|
||||
nvrhi::ObjectTypes::D3D12_Device),
|
||||
m_scratch_buffer.data(), m_scratch_buffer.size());
|
||||
context_description.device = m_backend->GetDevice()->getNativeObject(
|
||||
nvrhi::ObjectTypes::D3D12_Device);
|
||||
break;
|
||||
#endif
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
case nvrhi::GraphicsAPI::VULKAN:
|
||||
m_scratch_buffer.resize(
|
||||
ffxFsr2GetScratchMemorySizeVK(m_backend->GetDevice()->getNativeObject(
|
||||
nvrhi::ObjectTypes::VK_PhysicalDevice)));
|
||||
ffxFsr2GetInterfaceVK(&m_fsr2_interface, m_scratch_buffer.data(),
|
||||
m_scratch_buffer.size(),
|
||||
m_backend->GetDevice()->getNativeObject(
|
||||
nvrhi::ObjectTypes::VK_PhysicalDevice),
|
||||
dynamic_cast<NvRendererBackend_Vulkan*>(m_backend)
|
||||
->GetDeviceProcAddr());
|
||||
context_description.device =
|
||||
ffxGetDeviceVK(m_backend->GetDevice()->getNativeObject(
|
||||
nvrhi::ObjectTypes::VK_Device));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
context_description.callbacks = m_fsr2_interface;
|
||||
context_description.displaySize.width = m_width;
|
||||
context_description.displaySize.height = m_height;
|
||||
context_description.maxRenderSize.width = 3440;
|
||||
context_description.maxRenderSize.height = 1440;
|
||||
context_description.flags = // FFX_FSR2_ENABLE_DEBUG_CHECKING |
|
||||
FFX_FSR2_ENABLE_AUTO_EXPOSURE | FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE |
|
||||
FFX_FSR2_ENABLE_DEPTH_INVERTED;
|
||||
context_description.fpMessage = &OnFsrMessage;
|
||||
ffxFsr2ContextCreate(&m_fsr2_context, &context_description);
|
||||
}
|
||||
|
||||
void NvFSR::Render(nvrhi::ICommandList* command_list, double far_plane,
|
||||
double near_plane, double fov) {
|
||||
FfxFsr2DispatchDescription dispatch_description{};
|
||||
switch (m_backend->GetDevice()->getGraphicsAPI()) {
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
case nvrhi::GraphicsAPI::D3D12:
|
||||
dispatch_description.commandList =
|
||||
ffxGetCommandListDX12(command_list->getNativeObject(
|
||||
nvrhi::ObjectTypes::D3D12_GraphicsCommandList));
|
||||
break;
|
||||
#endif
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
case nvrhi::GraphicsAPI::VULKAN:
|
||||
dispatch_description.commandList = ffxGetCommandListVK(
|
||||
command_list->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dispatch_description.color = GetFfxResource(m_color, L"FSR2 Input Color");
|
||||
dispatch_description.depth = GetFfxResource(m_depth, L"FSR2 Input Depth");
|
||||
dispatch_description.motionVectors =
|
||||
GetFfxResource(m_motion, L"FSR2 Input Motion");
|
||||
dispatch_description.exposure =
|
||||
GetFfxResource(nullptr, L"FSR2 Empty Auto Exposure");
|
||||
dispatch_description.reactive =
|
||||
GetFfxResource(nullptr, L"FSR2 Empty Input Reactive Map");
|
||||
dispatch_description.transparencyAndComposition =
|
||||
GetFfxResource(nullptr, L"FSR2 Empty Transparency And Composition Map");
|
||||
dispatch_description.output = GetFfxResource(
|
||||
m_output, L"FSR2 Output", FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||
|
||||
dispatch_description.jitterOffset.x = m_current_jitter.x;
|
||||
dispatch_description.jitterOffset.y = m_current_jitter.y;
|
||||
dispatch_description.motionVectorScale.x = static_cast<float>(m_input_width);
|
||||
dispatch_description.motionVectorScale.y = static_cast<float>(m_input_height);
|
||||
dispatch_description.reset = false;
|
||||
dispatch_description.enableSharpening = true;
|
||||
dispatch_description.sharpness = .4;
|
||||
dispatch_description.frameTimeDelta = 10.f;
|
||||
dispatch_description.preExposure = 1.0f;
|
||||
dispatch_description.renderSize.width = m_input_width;
|
||||
dispatch_description.renderSize.height = m_input_height;
|
||||
dispatch_description.cameraFar = far_plane;
|
||||
dispatch_description.cameraNear = near_plane;
|
||||
dispatch_description.cameraFovAngleVertical = fov;
|
||||
|
||||
ffxFsr2ContextDispatch(&m_fsr2_context, &dispatch_description);
|
||||
command_list->clearState();
|
||||
}
|
||||
|
||||
FfxResource NvFSR::GetFfxResource(nvrhi::ITexture* texture, wchar_t const* name,
|
||||
FfxResourceStates state) {
|
||||
switch (m_backend->GetDevice()->getGraphicsAPI()) {
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
case nvrhi::GraphicsAPI::D3D12:
|
||||
if (!texture) {
|
||||
return ffxGetResourceDX12(&m_fsr2_context, nullptr, name, state);
|
||||
}
|
||||
return ffxGetResourceDX12(
|
||||
&m_fsr2_context,
|
||||
texture->getNativeObject(nvrhi::ObjectTypes::D3D12_Resource), name,
|
||||
state);
|
||||
#endif
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
case nvrhi::GraphicsAPI::VULKAN: {
|
||||
if (!texture) {
|
||||
return ffxGetTextureResourceVK(&m_fsr2_context, nullptr, nullptr, 1, 1,
|
||||
VK_FORMAT_UNDEFINED, name, state);
|
||||
}
|
||||
const auto& desc = texture->getDesc();
|
||||
return ffxGetTextureResourceVK(
|
||||
&m_fsr2_context,
|
||||
texture->getNativeObject(nvrhi::ObjectTypes::VK_Image),
|
||||
texture->getNativeView(nvrhi::ObjectTypes::VK_ImageView), desc.width,
|
||||
desc.height, nvrhi::vulkan::convertFormat(desc.format), name, state);
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void NvFSR::BeginFrame() {
|
||||
++m_current_frame;
|
||||
ComputeJitter();
|
||||
}
|
||||
|
||||
glm::dmat4 NvFSR::GetJitterMatrix() const {
|
||||
return glm::translate(glm::dvec3(2. * m_current_jitter.x / m_input_width,
|
||||
-2. * m_current_jitter.y / m_input_height,
|
||||
0.));
|
||||
}
|
||||
|
||||
void NvFSR::ComputeJitter() {
|
||||
int phase_count = ffxFsr2GetJitterPhaseCount(m_input_width, m_width);
|
||||
ffxFsr2GetJitterOffset(&m_current_jitter.x, &m_current_jitter.y,
|
||||
m_current_frame % phase_count, phase_count);
|
||||
}
|
||||
45
betterRenderer/renderer/source/fsr.h
Normal file
45
betterRenderer/renderer/source/fsr.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <ffx-fsr2-api/ffx_fsr2.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
struct NvFSR {
|
||||
NvFSR(class NvRenderer* renderer);
|
||||
void Init();
|
||||
void Render(nvrhi::ICommandList* command_list, double far_plane,
|
||||
double near_plane, double fov);
|
||||
|
||||
FfxFsr2Interface m_fsr2_interface;
|
||||
FfxFsr2Context m_fsr2_context;
|
||||
std::string m_scratch_buffer;
|
||||
|
||||
nvrhi::ITexture* m_color;
|
||||
nvrhi::ITexture* m_auto_exposure_buffer;
|
||||
nvrhi::ITexture* m_depth;
|
||||
nvrhi::ITexture* m_motion;
|
||||
nvrhi::TextureHandle m_output;
|
||||
|
||||
FfxResource GetFfxResource(
|
||||
nvrhi::ITexture* texture, wchar_t const* name = nullptr,
|
||||
FfxResourceStates state = FFX_RESOURCE_STATE_COMPUTE_READ);
|
||||
|
||||
int m_input_width;
|
||||
int m_input_height;
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
glm::vec2 m_current_jitter;
|
||||
|
||||
class NvRendererBackend* m_backend;
|
||||
struct NvGbuffer* m_gbuffer;
|
||||
struct GbufferBlitPass* m_inputpass;
|
||||
struct MaAutoExposure* m_auto_exposure;
|
||||
|
||||
size_t m_current_frame = 0;
|
||||
|
||||
void BeginFrame();
|
||||
glm::dmat4 GetJitterMatrix() const;
|
||||
void ComputeJitter();
|
||||
};
|
||||
96
betterRenderer/renderer/source/fullscreenpass.cpp
Normal file
96
betterRenderer/renderer/source/fullscreenpass.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
#include "fullscreenpass.h"
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
FullScreenPass::FullScreenPass(NvRendererBackend* backend)
|
||||
: m_backend(backend) {}
|
||||
|
||||
nvrhi::IShader* FullScreenPass::GetVertexShader() {
|
||||
static nvrhi::ShaderHandle vertex_shader = nullptr;
|
||||
if (!vertex_shader)
|
||||
vertex_shader = m_backend->CreateShader("fx_vertex", nvrhi::ShaderType::Vertex);
|
||||
return vertex_shader;
|
||||
}
|
||||
|
||||
nvrhi::IInputLayout* FullScreenPass::GetInputLayout() {
|
||||
static nvrhi::InputLayoutHandle input_layout = nullptr;
|
||||
if (!input_layout) {
|
||||
nvrhi::VertexAttributeDesc attribute_desc =
|
||||
nvrhi::VertexAttributeDesc()
|
||||
.setName("Position")
|
||||
.setElementStride(sizeof(glm::vec2))
|
||||
.setOffset(0)
|
||||
.setFormat(nvrhi::Format::RG32_FLOAT)
|
||||
.setBufferIndex(0)
|
||||
.setIsInstanced(false);
|
||||
input_layout = m_backend->GetDevice()->createInputLayout(&attribute_desc, 1,
|
||||
GetVertexShader());
|
||||
}
|
||||
return input_layout;
|
||||
}
|
||||
|
||||
nvrhi::IBuffer* FullScreenPass::GetVertexBuffer() {
|
||||
static nvrhi::BufferHandle vertex_buffer = nullptr;
|
||||
if (!vertex_buffer) {
|
||||
glm::vec2 vertices[]{{0.f, 0.f}, {1.f, 0.f}, {0.f, 1.f}, {1.f, 1.f}};
|
||||
vertex_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc()
|
||||
.setIsVertexBuffer(true)
|
||||
.setByteSize(sizeof(vertices))
|
||||
.setInitialState(nvrhi::ResourceStates::VertexBuffer)
|
||||
.setKeepInitialState(true)
|
||||
.setCpuAccess(nvrhi::CpuAccessMode::Write));
|
||||
void* map = m_backend->GetDevice()->mapBuffer(vertex_buffer,
|
||||
nvrhi::CpuAccessMode::Write);
|
||||
memcpy(map, vertices, sizeof(vertices));
|
||||
m_backend->GetDevice()->unmapBuffer(vertex_buffer);
|
||||
}
|
||||
return vertex_buffer;
|
||||
}
|
||||
|
||||
void FullScreenPass::Init() { CreatePipeline(); }
|
||||
|
||||
void FullScreenPass::CreatePipeline() {
|
||||
nvrhi::GraphicsPipelineDesc pipeline_desc;
|
||||
CreatePipelineDesc(pipeline_desc);
|
||||
m_pipeline = m_backend->GetDevice()->createGraphicsPipeline(pipeline_desc,
|
||||
GetFramebuffer());
|
||||
}
|
||||
|
||||
void FullScreenPass::CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
|
||||
pipeline_desc.setVertexShader(GetVertexShader())
|
||||
.setInputLayout(GetInputLayout())
|
||||
.setPrimType(nvrhi::PrimitiveType::TriangleStrip)
|
||||
.setRenderState(
|
||||
nvrhi::RenderState()
|
||||
.setBlendState(nvrhi::BlendState().setRenderTarget(
|
||||
0, nvrhi::BlendState::RenderTarget().disableBlend()))
|
||||
.setRasterState(nvrhi::RasterState()
|
||||
.setCullNone()
|
||||
.disableScissor()
|
||||
.disableDepthClip()
|
||||
.setFillSolid())
|
||||
.setDepthStencilState(nvrhi::DepthStencilState()
|
||||
.disableDepthTest()
|
||||
.disableDepthWrite()
|
||||
.disableStencil()));
|
||||
}
|
||||
|
||||
nvrhi::IFramebuffer* FullScreenPass::GetFramebuffer() { return m_backend->GetCurrentFramebuffer(); }
|
||||
|
||||
void FullScreenPass::InitState(nvrhi::GraphicsState& render_state) {
|
||||
nvrhi::IFramebuffer* framebuffer = GetFramebuffer();
|
||||
render_state.setFramebuffer(framebuffer);
|
||||
render_state.setViewport(nvrhi::ViewportState().addViewportAndScissorRect(
|
||||
framebuffer->getFramebufferInfo().getViewport()));
|
||||
render_state.setPipeline(m_pipeline);
|
||||
render_state.addVertexBuffer(
|
||||
nvrhi::VertexBufferBinding().setSlot(0).setBuffer(GetVertexBuffer()));
|
||||
}
|
||||
|
||||
void FullScreenPass::Draw(nvrhi::ICommandList* command_list) {
|
||||
command_list->draw(nvrhi::DrawArguments().setVertexCount(4));
|
||||
}
|
||||
21
betterRenderer/renderer/source/fullscreenpass.h
Normal file
21
betterRenderer/renderer/source/fullscreenpass.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
struct FullScreenPass {
|
||||
|
||||
FullScreenPass(class NvRendererBackend* backend);
|
||||
virtual ~FullScreenPass() = default;
|
||||
nvrhi::IShader* GetVertexShader();
|
||||
nvrhi::IInputLayout* GetInputLayout();
|
||||
nvrhi::IBuffer* GetVertexBuffer();
|
||||
virtual void Init();
|
||||
void CreatePipeline();
|
||||
virtual void CreatePipelineDesc(nvrhi::GraphicsPipelineDesc& pipeline_desc);
|
||||
nvrhi::GraphicsPipelineHandle m_pipeline;
|
||||
class NvRendererBackend* m_backend;
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer();
|
||||
void InitState(nvrhi::GraphicsState& render_state);
|
||||
void Draw(nvrhi::ICommandList* command_list);
|
||||
virtual void Render(nvrhi::ICommandList* command_list) = 0;
|
||||
};
|
||||
117
betterRenderer/renderer/source/gbuffer.cpp
Normal file
117
betterRenderer/renderer/source/gbuffer.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "gbuffer.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
NvGbuffer::NvGbuffer(NvRenderer* renderer)
|
||||
: m_backend(renderer->m_backend.get()) {}
|
||||
|
||||
void NvGbuffer::Init(int width, int height, bool is_cube, bool depth_only,
|
||||
int slices) {
|
||||
m_depth_only = depth_only;
|
||||
nvrhi::TextureDesc buffer_desc =
|
||||
nvrhi::TextureDesc()
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setIsRenderTarget(true)
|
||||
.setKeepInitialState(true)
|
||||
.setClearValue(nvrhi::Color(0.f, 0.f, 0.f, 0.f))
|
||||
.setUseClearValue(true);
|
||||
if (is_cube) {
|
||||
buffer_desc.setDimension(nvrhi::TextureDimension::TextureCube);
|
||||
buffer_desc.setArraySize(6);
|
||||
} else if (slices > 1) {
|
||||
buffer_desc.setDimension(nvrhi::TextureDimension::Texture2DArray);
|
||||
buffer_desc.setArraySize(slices);
|
||||
}
|
||||
if (!depth_only) {
|
||||
m_gbuffer_diffuse = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(buffer_desc)
|
||||
.setFormat(nvrhi::Format::R10G10B10A2_UNORM)
|
||||
.setDebugName("GBuffer Diffuse"));
|
||||
m_gbuffer_emission = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(buffer_desc)
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setDebugName("GBuffer Emission"));
|
||||
m_gbuffer_params = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(buffer_desc)
|
||||
.setFormat(nvrhi::Format::RGBA8_UNORM)
|
||||
.setDebugName("GBuffer Params"));
|
||||
m_gbuffer_normal = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(buffer_desc)
|
||||
.setFormat(nvrhi::Format::R10G10B10A2_UNORM)
|
||||
.setClearValue(nvrhi::Color(.5f, .5f, 1.f, 0.f))
|
||||
.setDebugName("GBuffer Normal"));
|
||||
m_gbuffer_motion = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(buffer_desc)
|
||||
.setFormat(nvrhi::Format::RG16_FLOAT)
|
||||
.setClearValue(nvrhi::Color(0.f, 0.f, 0.f, 0.f))
|
||||
.setDebugName("GBuffer Motion"));
|
||||
}
|
||||
m_gbuffer_depth = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(buffer_desc)
|
||||
.setIsTypeless(true)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setFormat(nvrhi::Format::D32)
|
||||
.setDebugName("GBuffer Deph"));
|
||||
nvrhi::TextureSubresourceSet subresource_set =
|
||||
nvrhi::TextureSubresourceSet(0, 1, 0, buffer_desc.arraySize);
|
||||
nvrhi::FramebufferDesc fb_desc = nvrhi::FramebufferDesc().setDepthAttachment(
|
||||
m_gbuffer_depth, subresource_set);
|
||||
if (!depth_only) {
|
||||
fb_desc.addColorAttachment(m_gbuffer_diffuse, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_emission, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_params, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_normal, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_motion, subresource_set);
|
||||
}
|
||||
m_framebuffer = m_backend->GetDevice()->createFramebuffer(fb_desc);
|
||||
m_slice_framebuffers.resize(buffer_desc.arraySize);
|
||||
for (int i = 0; i < buffer_desc.arraySize; ++i) {
|
||||
nvrhi::TextureSubresourceSet subresource_set =
|
||||
nvrhi::TextureSubresourceSet(0, 1, i, 1);
|
||||
nvrhi::FramebufferDesc fb_desc =
|
||||
nvrhi::FramebufferDesc().setDepthAttachment(m_gbuffer_depth,
|
||||
subresource_set);
|
||||
if (!depth_only) {
|
||||
fb_desc.addColorAttachment(m_gbuffer_diffuse, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_emission, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_params, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_normal, subresource_set)
|
||||
.addColorAttachment(m_gbuffer_motion, subresource_set);
|
||||
}
|
||||
m_slice_framebuffers[i] =
|
||||
m_backend->GetDevice()->createFramebuffer(fb_desc);
|
||||
}
|
||||
}
|
||||
|
||||
void NvGbuffer::Clear(nvrhi::ICommandList* command_list) {
|
||||
if (!m_depth_only) {
|
||||
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 0,
|
||||
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
|
||||
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 1,
|
||||
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
|
||||
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 2,
|
||||
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
|
||||
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 3,
|
||||
nvrhi::Color(.5f, .5f, 1.f, 0.f));
|
||||
nvrhi::utils::ClearColorAttachment(command_list, m_framebuffer, 4,
|
||||
nvrhi::Color(0.f, 0.f, 0.f, 0.f));
|
||||
}
|
||||
ClearDepthStencilAttachment(command_list, m_framebuffer,
|
||||
m_depth_only ? 0.f : 0.f, 0);
|
||||
}
|
||||
|
||||
void NvGbuffer::ClearDepthStencilAttachment(nvrhi::ICommandList* commandList,
|
||||
nvrhi::IFramebuffer* framebuffer,
|
||||
float depth, uint32_t stencil) {
|
||||
const nvrhi::FramebufferAttachment& att =
|
||||
framebuffer->getDesc().depthAttachment;
|
||||
if (att.texture) {
|
||||
commandList->clearDepthStencilTexture(att.texture, att.subresources, true,
|
||||
depth, false, stencil);
|
||||
}
|
||||
}
|
||||
27
betterRenderer/renderer/source/gbuffer.h
Normal file
27
betterRenderer/renderer/source/gbuffer.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
struct NvGbuffer {
|
||||
NvGbuffer(class NvRenderer* renderer);
|
||||
void Init(int width, int height, bool is_cube, bool depth_only = false, int slices = 1);
|
||||
void Clear(nvrhi::ICommandList* command_list);
|
||||
|
||||
nvrhi::TextureHandle m_gbuffer_diffuse;
|
||||
nvrhi::TextureHandle m_gbuffer_emission;
|
||||
nvrhi::TextureHandle m_gbuffer_normal;
|
||||
nvrhi::TextureHandle m_gbuffer_params;
|
||||
nvrhi::TextureHandle m_gbuffer_motion;
|
||||
nvrhi::TextureHandle m_gbuffer_depth;
|
||||
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
std::vector<nvrhi::FramebufferHandle> m_slice_framebuffers;
|
||||
|
||||
class NvRendererBackend* m_backend;
|
||||
|
||||
bool m_depth_only;
|
||||
|
||||
static void ClearDepthStencilAttachment(nvrhi::ICommandList* commandList,
|
||||
nvrhi::IFramebuffer* framebuffer,
|
||||
float depth, uint32_t stencil);
|
||||
};
|
||||
218
betterRenderer/renderer/source/gbufferblitpass.cpp
Normal file
218
betterRenderer/renderer/source/gbufferblitpass.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "gbufferblitpass.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <Timer.h>
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "contactshadows.h"
|
||||
#include "csm.h"
|
||||
#include "environment.h"
|
||||
#include "gbuffer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "sky.h"
|
||||
#include "ssao.h"
|
||||
|
||||
GbufferBlitPass::GbufferBlitPass(NvRenderer* renderer, NvGbuffer* gbuffer,
|
||||
NvGbuffer* gbuffer_shadow, NvSsao* ssao,
|
||||
MaEnvironment* environment,
|
||||
MaShadowMap* shadow_map,
|
||||
MaContactShadows* contact_shadows, Sky* sky)
|
||||
: FullScreenPass(renderer->GetBackend()),
|
||||
MaResourceRegistry(renderer),
|
||||
m_gbuffer(gbuffer),
|
||||
m_gbuffer_shadow(gbuffer_shadow),
|
||||
m_ssao(ssao),
|
||||
m_environment(environment),
|
||||
m_shadow_map(shadow_map),
|
||||
m_contact_shadows(contact_shadows),
|
||||
m_sky(sky) {}
|
||||
|
||||
void GbufferBlitPass::Init() {
|
||||
InitResourceRegistry();
|
||||
m_draw_constants = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(
|
||||
sizeof(DrawConstants), "GBuffer Lighting Constants", 16)
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
RegisterResource(true, "gbuffer_lighting_constants", m_draw_constants,
|
||||
nvrhi::ResourceType::VolatileConstantBuffer);
|
||||
const nvrhi::SamplerHandle sampler_shadow_comp =
|
||||
m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setReductionType(nvrhi::SamplerReductionType::Comparison)
|
||||
.setComparisonFunc(nvrhi::ComparisonFunc::Greater)
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setAllFilters(true));
|
||||
const nvrhi::SamplerHandle sampler_linear =
|
||||
m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc().setAllFilters(true));
|
||||
const nvrhi::SamplerHandle sampler_linear_clamp =
|
||||
m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setAllFilters(true));
|
||||
const nvrhi::SamplerHandle sampler_linear_clamp_v_repeat_h =
|
||||
m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setAddressU(nvrhi::SamplerAddressMode::Repeat)
|
||||
.setAllFilters(true));
|
||||
RegisterResource(true, "shadow_sampler_comp", sampler_shadow_comp,
|
||||
nvrhi::ResourceType::Sampler);
|
||||
RegisterResource(true, "sampler_linear_wrap", sampler_linear,
|
||||
nvrhi::ResourceType::Sampler);
|
||||
RegisterResource(true, "sampler_linear_clamp", sampler_linear_clamp,
|
||||
nvrhi::ResourceType::Sampler);
|
||||
RegisterResource(true, "sampler_linear_clamp_v_repeat_h", sampler_linear_clamp_v_repeat_h,
|
||||
nvrhi::ResourceType::Sampler);
|
||||
m_scene_depth = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(m_gbuffer->m_gbuffer_depth->getDesc())
|
||||
.setFormat(nvrhi::Format::R32_FLOAT)
|
||||
.setDebugName("Scene Depth")
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setUseClearValue(false)
|
||||
.setIsRenderTarget(false));
|
||||
RegisterResource(true, "gbuffer_depth", m_scene_depth,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
m_output = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setWidth(m_gbuffer->m_framebuffer->getFramebufferInfo().width)
|
||||
.setHeight(m_gbuffer->m_framebuffer->getFramebufferInfo().height)
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setIsUAV(true)
|
||||
.setIsRenderTarget(true)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
RegisterResource(true, "scene_lit_texture", m_output,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
m_binding_layout = m_backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(2))
|
||||
.addItem(nvrhi::BindingLayoutItem::ConstantBuffer(11))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(1))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(2))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(3))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(4))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(5))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(8))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(9))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(10))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(11))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(12))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(14))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(16))
|
||||
.addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(17))
|
||||
.addItem(nvrhi::BindingLayoutItem::StructuredBuffer_SRV(18))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(8))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(11))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(13))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_UAV(0))
|
||||
.setVisibility(nvrhi::ShaderType::Compute));
|
||||
for (int i = 0; i < std::size(m_binding_set); ++i) {
|
||||
m_binding_set[i] = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(2, m_draw_constants))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
11, m_shadow_map->m_projection_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
0, m_gbuffer->m_gbuffer_diffuse))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
1, m_gbuffer->m_gbuffer_emission))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
2, m_gbuffer->m_gbuffer_params))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
3, m_gbuffer->m_gbuffer_normal))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
4, m_gbuffer->m_gbuffer_depth))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(5, m_ssao->m_outputAO))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
8, m_environment->m_dynamic_envmap_diffuse[0]))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
9, m_environment->m_dynamic_envmap_specular[0]))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
10, m_environment->m_brdf_lut))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
11, m_gbuffer_shadow->m_gbuffer_depth))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
12, m_contact_shadows->m_output_texture))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
14, m_sky->m_aerial_lut->m_lut))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
16, static_cast<nvrhi::ITexture*>(
|
||||
GetResource("forwardplus_index_grid_opaque", nvrhi::ResourceType::Texture_SRV)
|
||||
.m_resource)))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(
|
||||
17, static_cast<nvrhi::IBuffer*>(
|
||||
GetResource("forwardplus_index_buffer_opaque", nvrhi::ResourceType::StructuredBuffer_SRV)
|
||||
.m_resource)))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(
|
||||
18, static_cast<nvrhi::IBuffer*>(
|
||||
GetResource("forwardplus_light_buffer", nvrhi::ResourceType::StructuredBuffer_SRV)
|
||||
.m_resource)))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(8, sampler_linear))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(11, sampler_shadow_comp))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(13, sampler_linear_clamp_v_repeat_h))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_output)),
|
||||
m_binding_layout);
|
||||
}
|
||||
m_pixel_shader =
|
||||
m_backend->CreateShader("gbuffer_lighting", nvrhi::ShaderType::Compute);
|
||||
m_pso = m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.setComputeShader(m_pixel_shader)
|
||||
.addBindingLayout(m_binding_layout));
|
||||
// m_framebuffer = m_backend->GetDevice()->createFramebuffer(
|
||||
// nvrhi::FramebufferDesc().addColorAttachment(m_output));
|
||||
// FullScreenPass::Init();
|
||||
}
|
||||
|
||||
void GbufferBlitPass::CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
|
||||
FullScreenPass::CreatePipelineDesc(pipeline_desc);
|
||||
pipeline_desc.addBindingLayout(m_binding_layout);
|
||||
pipeline_desc.setPixelShader(m_pixel_shader);
|
||||
}
|
||||
|
||||
void GbufferBlitPass::UpdateConstants(nvrhi::ICommandList* command_list,
|
||||
glm::dmat4& view,
|
||||
const glm::dmat4& projection) {
|
||||
DrawConstants constants{};
|
||||
constants.m_inverse_model_view = glm::inverse(glm::dmat4(glm::dmat3(view)));
|
||||
constants.m_inverse_projection = glm::inverse(projection);
|
||||
|
||||
const auto& daylight = Global.DayLight;
|
||||
|
||||
m_sky->CalcLighting(constants.m_light_dir,
|
||||
constants.m_light_color);
|
||||
constants.m_altitude = Global.pCamera.Pos.y;
|
||||
constants.m_time = Timer::GetTime();
|
||||
|
||||
command_list->writeBuffer(m_draw_constants, &constants, sizeof(constants));
|
||||
}
|
||||
|
||||
void GbufferBlitPass::Render(nvrhi::ICommandList* command_list,
|
||||
glm::dmat4& view, const glm::dmat4& projection) {
|
||||
UpdateConstants(command_list, view, projection);
|
||||
Render(command_list);
|
||||
command_list->copyTexture(
|
||||
m_scene_depth, nvrhi::TextureSlice().resolve(m_scene_depth->getDesc()),
|
||||
m_gbuffer->m_gbuffer_depth,
|
||||
nvrhi::TextureSlice().resolve(m_scene_depth->getDesc()));
|
||||
}
|
||||
|
||||
void GbufferBlitPass::Render(nvrhi::ICommandList* command_list) {
|
||||
nvrhi::ComputeState graphics_state;
|
||||
auto desc = m_output->getDesc();
|
||||
// InitState(graphics_state);
|
||||
graphics_state.setPipeline(m_pso);
|
||||
graphics_state.addBindingSet(
|
||||
m_binding_set[m_environment->GetCurrentSetIndex()]);
|
||||
command_list->setComputeState(graphics_state);
|
||||
#define DISPATCH_SIZE(size, groupsize) ((size + groupsize - 1) / groupsize)
|
||||
command_list->dispatch(DISPATCH_SIZE(desc.width, 8),
|
||||
DISPATCH_SIZE(desc.height, 8), 1);
|
||||
// Draw(command_list);
|
||||
}
|
||||
|
||||
nvrhi::IFramebuffer* GbufferBlitPass::GetFramebuffer() { return m_framebuffer; }
|
||||
53
betterRenderer/renderer/source/gbufferblitpass.h
Normal file
53
betterRenderer/renderer/source/gbufferblitpass.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "fullscreenpass.h"
|
||||
#include "nvrenderer/resource_registry.h"
|
||||
|
||||
struct GbufferBlitPass : public FullScreenPass, public MaResourceRegistry {
|
||||
GbufferBlitPass(class NvRenderer* renderer, struct NvGbuffer* gbuffer,
|
||||
struct NvGbuffer* gbuffer_shadow, struct NvSsao* ssao,
|
||||
struct MaEnvironment* environment,
|
||||
struct MaShadowMap* shadow_map,
|
||||
struct MaContactShadows* contact_shadows, struct Sky* sky);
|
||||
|
||||
virtual void Init() override;
|
||||
|
||||
virtual void CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
|
||||
|
||||
void UpdateConstants(nvrhi::ICommandList* command_list, glm::dmat4& view,
|
||||
const glm::dmat4& projection);
|
||||
|
||||
void Render(nvrhi::ICommandList* command_list, glm::dmat4& view,
|
||||
const glm::dmat4& projection);
|
||||
virtual void Render(nvrhi::ICommandList* command_list) override;
|
||||
|
||||
struct DrawConstants {
|
||||
glm::mat4 m_inverse_model_view;
|
||||
glm::mat4 m_inverse_projection;
|
||||
glm::vec3 m_light_dir;
|
||||
float m_altitude;
|
||||
glm::vec3 m_light_color;
|
||||
float m_time;
|
||||
};
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
nvrhi::BindingSetHandle m_binding_set[2];
|
||||
nvrhi::ShaderHandle m_pixel_shader;
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
nvrhi::BufferHandle m_draw_constants;
|
||||
nvrhi::TextureHandle m_scene_depth;
|
||||
|
||||
NvGbuffer* m_gbuffer;
|
||||
NvGbuffer* m_gbuffer_shadow;
|
||||
NvSsao* m_ssao;
|
||||
MaEnvironment* m_environment;
|
||||
MaShadowMap* m_shadow_map;
|
||||
MaContactShadows* m_contact_shadows;
|
||||
Sky* m_sky;
|
||||
|
||||
nvrhi::ComputePipelineHandle m_pso;
|
||||
|
||||
nvrhi::TextureHandle m_output;
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
|
||||
};
|
||||
365
betterRenderer/renderer/source/gbufferlighting.cpp
Normal file
365
betterRenderer/renderer/source/gbufferlighting.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
#include "gbufferlighting.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
#include "gbuffer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "ssao.h"
|
||||
#include "config.h"
|
||||
|
||||
void GbufferLighting::Init() {
|
||||
auto fb_info = m_renderer->m_gbuffer->m_framebuffer->getFramebufferInfo();
|
||||
m_width = fb_info.width;
|
||||
m_height = fb_info.height;
|
||||
|
||||
m_tiles_x = (m_width + 7) / 8;
|
||||
m_tiles_y = (m_height + 7) / 8;
|
||||
|
||||
m_frustum_buffer = m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc()
|
||||
.setDebugName("Forward Plus Frustum Buffer")
|
||||
.setStructStride(4 * sizeof(glm::vec4) + 10 * sizeof(float))
|
||||
.setByteSize(m_tiles_x * m_tiles_y * (4 * sizeof(glm::vec4) + 10 * sizeof(float)))
|
||||
.setCanHaveUAVs(true)
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_view_params_buffer = m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(
|
||||
sizeof(ViewParams), "Forward Plus View Parameters", 16)
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_dispatch_params_buffer =
|
||||
m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(
|
||||
sizeof(ViewParams), "Forward Plus Dispatch Parameters", 16)
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
{
|
||||
auto atomic_counter_desc =
|
||||
nvrhi::BufferDesc()
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setStructStride(sizeof(uint32_t))
|
||||
.setKeepInitialState(true)
|
||||
.setByteSize(sizeof(uint32_t))
|
||||
.setCanHaveUAVs(true);
|
||||
|
||||
m_atomic_counter_lights_opaque =
|
||||
m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc(atomic_counter_desc)
|
||||
.setDebugName("Forward Plus Atomic Counter Lights Opaque"));
|
||||
|
||||
m_atomic_counter_lights_transparent =
|
||||
m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc(atomic_counter_desc)
|
||||
.setDebugName(
|
||||
"Forward Plus Atomic Counter Lights Transparent"));
|
||||
}
|
||||
|
||||
{
|
||||
auto index_buffer_desc =
|
||||
nvrhi::BufferDesc()
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setStructStride(sizeof(uint32_t))
|
||||
.setKeepInitialState(true)
|
||||
.setByteSize(sizeof(uint32_t) * m_tiles_x * m_tiles_y * 64)
|
||||
.setCanHaveUAVs(true);
|
||||
|
||||
m_index_buffer_lights_opaque =
|
||||
m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc(index_buffer_desc)
|
||||
.setDebugName("Forward Plus Index Buffer Lights Opaque"));
|
||||
|
||||
m_index_buffer_lights_transparent =
|
||||
m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc(index_buffer_desc)
|
||||
.setDebugName("Forward Plus Index Buffer Lights Transparent"));
|
||||
}
|
||||
|
||||
{
|
||||
auto index_grid_desc =
|
||||
nvrhi::TextureDesc()
|
||||
.setInitialState(nvrhi::ResourceStates::UnorderedAccess)
|
||||
.setFormat(nvrhi::Format::RG32_UINT)
|
||||
.setKeepInitialState(true)
|
||||
.setWidth(m_tiles_x)
|
||||
.setHeight(m_tiles_y)
|
||||
.setIsUAV(true);
|
||||
|
||||
m_index_grid_lights_opaque =
|
||||
m_renderer->GetBackend()->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(index_grid_desc)
|
||||
.setDebugName("Forward Plus Index Grid Lights Opaque"));
|
||||
|
||||
m_index_grid_lights_transparent =
|
||||
m_renderer->GetBackend()->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc(index_grid_desc)
|
||||
.setDebugName("Forward Plus Index Grid Lights Transparent"));
|
||||
}
|
||||
|
||||
{
|
||||
m_lighting_buffer_capacity =
|
||||
NvRenderer::Config()->m_lighting.m_max_lights_per_scene;
|
||||
m_light_buffer = m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
nvrhi::BufferDesc()
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true)
|
||||
.setByteSize(m_lighting_buffer_capacity *
|
||||
sizeof(m_packed_buffer[0]))
|
||||
.setStructStride(sizeof(m_packed_buffer[0]))
|
||||
.setDebugName("Light Buffer"));
|
||||
}
|
||||
|
||||
RegisterResource(true, "forwardplus_light_buffer", m_light_buffer,
|
||||
nvrhi::ResourceType::StructuredBuffer_SRV);
|
||||
RegisterResource(true, "forwardplus_index_grid_opaque",
|
||||
m_index_grid_lights_opaque,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
RegisterResource(true, "forwardplus_index_grid_transparent",
|
||||
m_index_grid_lights_transparent,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
RegisterResource(true, "forwardplus_index_buffer_opaque",
|
||||
m_index_buffer_lights_opaque,
|
||||
nvrhi::ResourceType::StructuredBuffer_SRV);
|
||||
RegisterResource(true, "forwardplus_index_buffer_transparent",
|
||||
m_index_buffer_lights_transparent,
|
||||
nvrhi::ResourceType::StructuredBuffer_SRV);
|
||||
|
||||
{
|
||||
nvrhi::BindingLayoutHandle binding_layout_comp_frustums;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(3, m_view_params_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
4, m_dispatch_params_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
|
||||
0, m_frustum_buffer)),
|
||||
binding_layout_comp_frustums, m_bindings_comp_frustums);
|
||||
|
||||
auto cs_comp_frustums = m_renderer->m_backend->CreateShader(
|
||||
"forwardplus_compute_frustums", nvrhi::ShaderType::Compute);
|
||||
|
||||
m_pso_comp_frustums =
|
||||
m_renderer->GetBackend()->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout_comp_frustums)
|
||||
.setComputeShader(cs_comp_frustums));
|
||||
}
|
||||
|
||||
{
|
||||
nvrhi::BindingLayoutHandle binding_layout_cull_lights;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(3, m_view_params_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
4, m_dispatch_params_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
3, m_renderer->m_gbuffer->m_gbuffer_depth))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::StructuredBuffer_SRV(8, m_light_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_SRV(
|
||||
9, m_frustum_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
|
||||
1, m_atomic_counter_lights_opaque))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
|
||||
2, m_atomic_counter_lights_transparent))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
|
||||
3, m_index_buffer_lights_opaque))
|
||||
.addItem(nvrhi::BindingSetItem::StructuredBuffer_UAV(
|
||||
4, m_index_buffer_lights_transparent))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
5, m_index_grid_lights_opaque))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
6, m_index_grid_lights_transparent)),
|
||||
binding_layout_cull_lights, m_bindings_cull_lights);
|
||||
|
||||
auto cs_cull_lights = m_renderer->m_backend->CreateShader(
|
||||
"forwardplus_cull_lights", nvrhi::ShaderType::Compute);
|
||||
|
||||
m_pso_cull_lights =
|
||||
m_renderer->GetBackend()->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout_cull_lights)
|
||||
.setComputeShader(cs_cull_lights));
|
||||
}
|
||||
|
||||
// auto texture_desc =
|
||||
// nvrhi::TextureDesc()
|
||||
// .setWidth(m_width)
|
||||
// .setHeight(m_height)
|
||||
// .setFormat(nvrhi::Format::R11G11B10_FLOAT)
|
||||
// .setIsUAV(true)
|
||||
// .setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
// .setKeepInitialState(true);
|
||||
//texture_desc
|
||||
// texture_desc.setDebugName("Deferred Diffuse Buffer");
|
||||
// m_diffuse_texture =
|
||||
// m_renderer->GetBackend()->GetDevice()->createTexture(texture_desc);
|
||||
// RegisterResource(true, "deferred_lights", m_diffuse_texture,
|
||||
// nvrhi::ResourceType::Texture_SRV);
|
||||
//
|
||||
// m_view_constants = m_renderer->GetBackend()->GetDevice()->createBuffer(
|
||||
// nvrhi::utils::CreateVolatileConstantBufferDesc(
|
||||
// sizeof(ViewConstants), "Deferred Lighting Constants", 16)
|
||||
// .setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
// .setKeepInitialState(true));
|
||||
//
|
||||
// nvrhi::BindingLayoutHandle binding_layout_spot;
|
||||
// nvrhi::BindingLayoutHandle binding_layout_clear;
|
||||
//
|
||||
// nvrhi::utils::CreateBindingSetAndLayout(
|
||||
// m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
// nvrhi::BindingSetDesc().addItem(
|
||||
// nvrhi::BindingSetItem::Texture_UAV(0, m_diffuse_texture)),
|
||||
// binding_layout_clear, m_binding_set_clear);
|
||||
//
|
||||
// nvrhi::utils::CreateBindingSetAndLayout(
|
||||
// m_renderer->GetBackend()->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
// nvrhi::BindingSetDesc()
|
||||
// .addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_view_constants))
|
||||
// .addItem(nvrhi::BindingSetItem::PushConstants(
|
||||
// 1, sizeof(PushConstantsSpotLight)))
|
||||
// .addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_diffuse_texture))
|
||||
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
// 0, m_renderer->m_gbuffer->m_gbuffer_depth))
|
||||
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
// 1, m_renderer->m_gbuffer->m_gbuffer_normal))
|
||||
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
// 2, m_renderer->m_gbuffer->m_gbuffer_params))
|
||||
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
// 3, m_renderer->m_gbuffer->m_gbuffer_diffuse))
|
||||
// .addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
// 5, m_renderer->m_ssao->m_outputAO)),
|
||||
// binding_layout_spot, m_binding_set_spotlight);
|
||||
//
|
||||
// auto clear_shader = m_renderer->m_backend->CreateShader(
|
||||
// "gbuffer_light_clear", nvrhi::ShaderType::Compute);
|
||||
// auto spotlight_shader = m_renderer->m_backend->CreateShader(
|
||||
// "gbuffer_light_spot", nvrhi::ShaderType::Compute);
|
||||
//
|
||||
// m_pso_clear = m_renderer->m_backend->GetDevice()->createComputePipeline(
|
||||
// nvrhi::ComputePipelineDesc()
|
||||
// .setComputeShader(clear_shader)
|
||||
// .addBindingLayout(binding_layout_clear));
|
||||
//
|
||||
// m_pso_spotlight = m_renderer->m_backend->GetDevice()->createComputePipeline(
|
||||
// nvrhi::ComputePipelineDesc()
|
||||
// .setComputeShader(spotlight_shader)
|
||||
// .addBindingLayout(binding_layout_spot));
|
||||
}
|
||||
|
||||
void GbufferLighting::Render(const NvRenderer::RenderPass& pass) {
|
||||
// ViewConstants view_constants{};
|
||||
// view_constants.m_inverse_model_view =
|
||||
// static_cast<glm::mat4>(glm::inverse(pass.m_transform));
|
||||
// view_constants.m_inverse_projection =
|
||||
// static_cast<glm::mat4>(glm::inverse(pass.m_projection));
|
||||
// pass.m_command_list_draw->writeBuffer(m_view_constants, &view_constants,
|
||||
// sizeof(view_constants));
|
||||
m_packed_buffer.clear();
|
||||
for (int i = 0; i < m_renderer->m_renderable_spotlights.size(); ++i) {
|
||||
if (i >= m_lighting_buffer_capacity) break;
|
||||
auto& light = m_packed_buffer.emplace_back(
|
||||
m_renderer->m_renderable_spotlights[i], pass.m_origin);
|
||||
}
|
||||
|
||||
pass.m_command_list_draw->writeBuffer(
|
||||
m_light_buffer, m_packed_buffer.data(),
|
||||
m_packed_buffer.size() * sizeof(m_packed_buffer[0]));
|
||||
|
||||
ViewParams view_params{};
|
||||
view_params.m_inverse_projection =
|
||||
static_cast<glm::mat4>(glm::inverse(pass.m_projection));
|
||||
view_params.m_view = static_cast<glm::mat4>(pass.m_transform);
|
||||
view_params.m_screen_dimensions = {m_width, m_height};
|
||||
view_params.m_num_lights = m_packed_buffer.size();
|
||||
pass.m_command_list_draw->writeBuffer(m_view_params_buffer, &view_params,
|
||||
sizeof(view_params));
|
||||
|
||||
{
|
||||
DispatchParams dispatch_params{};
|
||||
dispatch_params.m_num_threads = {m_tiles_x, m_tiles_y, 1u};
|
||||
dispatch_params.m_num_thread_groups =
|
||||
(dispatch_params.m_num_threads + 7u) / 8u;
|
||||
pass.m_command_list_draw->writeBuffer(
|
||||
m_dispatch_params_buffer, &dispatch_params, sizeof(dispatch_params));
|
||||
|
||||
pass.m_command_list_draw->setComputeState(
|
||||
nvrhi::ComputeState()
|
||||
.setPipeline(m_pso_comp_frustums)
|
||||
.addBindingSet(m_bindings_comp_frustums));
|
||||
pass.m_command_list_draw->dispatch(dispatch_params.m_num_thread_groups.x,
|
||||
dispatch_params.m_num_thread_groups.y,
|
||||
dispatch_params.m_num_thread_groups.z);
|
||||
}
|
||||
|
||||
{
|
||||
DispatchParams dispatch_params{};
|
||||
dispatch_params.m_num_thread_groups = {m_tiles_x, m_tiles_y, 1u};
|
||||
dispatch_params.m_num_threads = dispatch_params.m_num_thread_groups * 8u;
|
||||
pass.m_command_list_draw->writeBuffer(
|
||||
m_dispatch_params_buffer, &dispatch_params, sizeof(dispatch_params));
|
||||
|
||||
uint32_t zero = 0;
|
||||
pass.m_command_list_draw->writeBuffer(m_atomic_counter_lights_opaque, &zero,
|
||||
sizeof(zero));
|
||||
pass.m_command_list_draw->writeBuffer(m_atomic_counter_lights_transparent,
|
||||
&zero, sizeof(zero));
|
||||
|
||||
pass.m_command_list_draw->setComputeState(
|
||||
nvrhi::ComputeState()
|
||||
.setPipeline(m_pso_cull_lights)
|
||||
.addBindingSet(m_bindings_cull_lights));
|
||||
pass.m_command_list_draw->dispatch(dispatch_params.m_num_thread_groups.x,
|
||||
dispatch_params.m_num_thread_groups.y,
|
||||
dispatch_params.m_num_thread_groups.z);
|
||||
}
|
||||
|
||||
// {
|
||||
// pass.m_command_list_draw->setComputeState(
|
||||
// nvrhi::ComputeState()
|
||||
// .setPipeline(m_pso_clear)
|
||||
// .addBindingSet(m_binding_set_clear));
|
||||
// pass.m_command_list_draw->dispatch((m_width + 7) / 8, (m_height + 7) / 8);
|
||||
// }
|
||||
//
|
||||
// for (const auto& spotlight : m_renderer->m_renderable_spotlights) {
|
||||
// PushConstantsSpotLight push_constants{};
|
||||
// push_constants.m_origin = spotlight.m_origin - pass.m_origin;
|
||||
// push_constants.m_radius_sqr = spotlight.m_radius * spotlight.m_radius;
|
||||
// push_constants.m_direction = spotlight.m_direction;
|
||||
// push_constants.m_cos_inner_cone = spotlight.m_cos_inner_cone;
|
||||
// push_constants.m_cos_outer_cone = spotlight.m_cos_outer_cone;
|
||||
// push_constants.m_color = spotlight.m_color;
|
||||
//
|
||||
// pass.m_command_list_draw->setComputeState(
|
||||
// nvrhi::ComputeState()
|
||||
// .setPipeline(m_pso_spotlight)
|
||||
// .addBindingSet(m_binding_set_spotlight));
|
||||
// pass.m_command_list_draw->setPushConstants(&push_constants,
|
||||
// sizeof(push_constants));
|
||||
// pass.m_command_list_draw->dispatch((m_width + 7) / 8, (m_height + 7) / 8);
|
||||
// }
|
||||
}
|
||||
|
||||
GbufferLighting::PackedSpotLight::PackedSpotLight(
|
||||
const NvRenderer::Renderable::SpotLight& spotlight,
|
||||
glm::dvec3 const& origin) {
|
||||
m_origin = spotlight.m_origin - origin;
|
||||
m_radius = spotlight.m_radius;
|
||||
m_direction_cos_inner =
|
||||
glm::packHalf4x16({spotlight.m_direction, spotlight.m_cos_inner_cone});
|
||||
glm::vec3 color =
|
||||
spotlight.m_color.r * glm::vec3(0.4123908f, 0.21263901f, 0.01933082f) +
|
||||
spotlight.m_color.g * glm::vec3(0.35758434f, 0.71516868f, 0.11919478f) +
|
||||
spotlight.m_color.b * glm::vec3(0.18048079f, 0.07219232f, 0.95053215f);
|
||||
m_color_cos_outer = glm::packHalf4x16({color, spotlight.m_cos_outer_cone});
|
||||
}
|
||||
84
betterRenderer/renderer/source/gbufferlighting.h
Normal file
84
betterRenderer/renderer/source/gbufferlighting.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrenderer/nvrenderer.h>
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct GbufferLighting : public MaResourceRegistry {
|
||||
GbufferLighting(NvRenderer* renderer)
|
||||
: MaResourceRegistry(renderer), m_renderer(renderer) {}
|
||||
|
||||
struct ViewConstants {
|
||||
glm::mat4 m_inverse_model_view;
|
||||
glm::mat4 m_inverse_projection;
|
||||
};
|
||||
|
||||
struct PushConstantsSpotLight {
|
||||
glm::vec3 m_origin;
|
||||
float m_radius_sqr;
|
||||
glm::vec3 m_direction;
|
||||
float m_cos_inner_cone;
|
||||
glm::vec3 m_color;
|
||||
float m_cos_outer_cone;
|
||||
};
|
||||
|
||||
struct PackedSpotLight {
|
||||
explicit PackedSpotLight(NvRenderer::Renderable::SpotLight const& spotlight,
|
||||
glm::dvec3 const& origin);
|
||||
glm::vec3 m_origin;
|
||||
float m_radius;
|
||||
uint64_t m_direction_cos_inner;
|
||||
uint64_t m_color_cos_outer;
|
||||
};
|
||||
|
||||
struct DispatchParams {
|
||||
alignas(sizeof(glm::vec4)) glm::uvec3 m_num_thread_groups;
|
||||
alignas(sizeof(glm::vec4)) glm::uvec3 m_num_threads;
|
||||
};
|
||||
|
||||
struct ViewParams {
|
||||
glm::mat4 m_inverse_projection;
|
||||
glm::mat4 m_view;
|
||||
glm::vec2 m_screen_dimensions;
|
||||
uint32_t m_num_lights;
|
||||
};
|
||||
|
||||
std::vector<PackedSpotLight> m_packed_buffer;
|
||||
size_t m_lighting_buffer_capacity;
|
||||
|
||||
nvrhi::BindingSetHandle m_bindings_cull_lights;
|
||||
nvrhi::ComputePipelineHandle m_pso_cull_lights;
|
||||
|
||||
nvrhi::BindingSetHandle m_bindings_comp_frustums;
|
||||
nvrhi::ComputePipelineHandle m_pso_comp_frustums;
|
||||
|
||||
nvrhi::BufferHandle m_atomic_counter_lights_opaque;
|
||||
nvrhi::BufferHandle m_atomic_counter_lights_transparent;
|
||||
nvrhi::BufferHandle m_index_buffer_lights_opaque;
|
||||
nvrhi::BufferHandle m_index_buffer_lights_transparent;
|
||||
nvrhi::TextureHandle m_index_grid_lights_opaque;
|
||||
nvrhi::TextureHandle m_index_grid_lights_transparent;
|
||||
|
||||
nvrhi::TextureHandle m_diffuse_texture;
|
||||
nvrhi::TextureHandle m_specular_texture;
|
||||
nvrhi::BufferHandle m_light_buffer;
|
||||
nvrhi::BufferHandle m_view_constants;
|
||||
nvrhi::BufferHandle m_frustum_buffer;
|
||||
nvrhi::BufferHandle m_dispatch_params_buffer;
|
||||
nvrhi::BufferHandle m_view_params_buffer;
|
||||
nvrhi::BindingSetHandle m_binding_set_spotlight;
|
||||
nvrhi::ComputePipelineHandle m_pso_spotlight;
|
||||
nvrhi::BindingSetHandle m_binding_set_clear;
|
||||
nvrhi::ComputePipelineHandle m_pso_clear;
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_tiles_x;
|
||||
int m_tiles_y;
|
||||
|
||||
void Init();
|
||||
void Render(const NvRenderer::RenderPass& pass);
|
||||
|
||||
NvRenderer* m_renderer;
|
||||
};
|
||||
408
betterRenderer/renderer/source/imgui_nvrhi.cpp
Normal file
408
betterRenderer/renderer/source/imgui_nvrhi.cpp
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
License for Dear ImGui
|
||||
|
||||
Copyright (c) 2014-2019 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "imgui_nvrhi.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "config.h"
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
struct ImguiPushConstants {
|
||||
glm::vec2 m_inv_display_size;
|
||||
float m_ui_nits;
|
||||
MaConfig::MaTonemapType m_tonemap_function;
|
||||
};
|
||||
|
||||
bool ImGui_NVRHI::createFontTexture(nvrhi::ICommandList* commandList)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char *pixels;
|
||||
int width, height;
|
||||
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
{
|
||||
nvrhi::TextureDesc desc;
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.format = nvrhi::Format::RGBA8_UNORM;
|
||||
desc.debugName = "ImGui font texture";
|
||||
|
||||
fontTexture = renderer->createTexture(desc);
|
||||
|
||||
commandList->beginTrackingTextureState(fontTexture, nvrhi::AllSubresources, nvrhi::ResourceStates::Common);
|
||||
|
||||
if (fontTexture == nullptr)
|
||||
return false;
|
||||
|
||||
commandList->writeTexture(fontTexture, 0, 0, pixels, width * 4);
|
||||
|
||||
commandList->setPermanentTextureState(fontTexture, nvrhi::ResourceStates::ShaderResource);
|
||||
commandList->commitBarriers();
|
||||
|
||||
io.Fonts->TexID = fontTexture;
|
||||
}
|
||||
|
||||
{
|
||||
const auto desc = nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::Wrap)
|
||||
.setAllFilters(true);
|
||||
|
||||
fontSampler = renderer->createSampler(desc);
|
||||
|
||||
if (fontSampler == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_NVRHI::init(NvRendererBackend *backend)
|
||||
{
|
||||
this->renderer = renderer;
|
||||
|
||||
m_commandList = renderer->createCommandList();
|
||||
|
||||
m_commandList->open();
|
||||
|
||||
vertexShader = backend->CreateShader("imgui_vertex", nvrhi::ShaderType::Vertex);
|
||||
if (vertexShader == nullptr)
|
||||
{
|
||||
printf("error creating NVRHI vertex shader object\n");
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
pixelShader = backend->CreateShader("imgui_pixel", nvrhi::ShaderType::Pixel);
|
||||
if (pixelShader == nullptr)
|
||||
{
|
||||
printf("error creating NVRHI pixel shader object\n");
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create attribute layout object
|
||||
nvrhi::VertexAttributeDesc vertexAttribLayout[] = {
|
||||
{ "POSITION", nvrhi::Format::RG32_FLOAT, 1, 0, offsetof(ImDrawVert,pos), sizeof(ImDrawVert), false },
|
||||
{ "TEXCOORD", nvrhi::Format::RG32_FLOAT, 1, 0, offsetof(ImDrawVert,uv), sizeof(ImDrawVert), false },
|
||||
{ "COLOR", nvrhi::Format::RGBA8_UNORM, 1, 0, offsetof(ImDrawVert,col), sizeof(ImDrawVert), false },
|
||||
};
|
||||
|
||||
shaderAttribLayout = renderer->createInputLayout(vertexAttribLayout, sizeof(vertexAttribLayout) / sizeof(vertexAttribLayout[0]), vertexShader);
|
||||
|
||||
// add the default font - before creating the font texture
|
||||
auto& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontDefault();
|
||||
|
||||
// create font texture
|
||||
if (!createFontTexture(m_commandList))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// create PSO
|
||||
{
|
||||
nvrhi::BlendState blendState;
|
||||
blendState.targets[0].setBlendEnable(true)
|
||||
.setSrcBlend(nvrhi::BlendFactor::SrcAlpha)
|
||||
.setDestBlend(nvrhi::BlendFactor::InvSrcAlpha)
|
||||
.setSrcBlendAlpha(nvrhi::BlendFactor::InvSrcAlpha)
|
||||
.setDestBlendAlpha(nvrhi::BlendFactor::Zero);
|
||||
|
||||
auto rasterState = nvrhi::RasterState()
|
||||
.setFillSolid()
|
||||
.setCullNone()
|
||||
.setScissorEnable(true)
|
||||
.setDepthClipEnable(true);
|
||||
|
||||
auto depthStencilState = nvrhi::DepthStencilState()
|
||||
.disableDepthTest()
|
||||
.enableDepthWrite()
|
||||
.disableStencil()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::Always);
|
||||
|
||||
nvrhi::RenderState renderState;
|
||||
renderState.blendState = blendState;
|
||||
renderState.depthStencilState = depthStencilState;
|
||||
renderState.rasterState = rasterState;
|
||||
|
||||
nvrhi::BindingLayoutDesc layoutDesc;
|
||||
layoutDesc.visibility = nvrhi::ShaderType::All;
|
||||
layoutDesc.bindings = {
|
||||
nvrhi::BindingLayoutItem::PushConstants(0, sizeof(ImguiPushConstants)),
|
||||
nvrhi::BindingLayoutItem::Texture_SRV(0),
|
||||
nvrhi::BindingLayoutItem::Sampler(0)
|
||||
};
|
||||
bindingLayout = renderer->createBindingLayout(layoutDesc);
|
||||
|
||||
basePSODesc.primType = nvrhi::PrimitiveType::TriangleList;
|
||||
basePSODesc.inputLayout = shaderAttribLayout;
|
||||
basePSODesc.VS = vertexShader;
|
||||
basePSODesc.PS = pixelShader;
|
||||
basePSODesc.renderState = renderState;
|
||||
basePSODesc.bindingLayouts = { bindingLayout };
|
||||
}
|
||||
|
||||
m_commandList->close();
|
||||
renderer->executeCommandList(m_commandList);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_NVRHI::reallocateBuffer(nvrhi::BufferHandle& buffer, size_t requiredSize, size_t reallocateSize, const bool indexBuffer)
|
||||
{
|
||||
if (buffer == nullptr || size_t(buffer->getDesc().byteSize) < requiredSize)
|
||||
{
|
||||
nvrhi::BufferDesc desc;
|
||||
desc.byteSize = uint32_t(reallocateSize);
|
||||
desc.structStride = 0;
|
||||
desc.debugName = indexBuffer ? "ImGui index buffer" : "ImGui vertex buffer";
|
||||
desc.canHaveUAVs = false;
|
||||
desc.isVertexBuffer = !indexBuffer;
|
||||
desc.isIndexBuffer = indexBuffer;
|
||||
desc.isDrawIndirectArgs = false;
|
||||
desc.isVolatile = false;
|
||||
desc.initialState = indexBuffer ? nvrhi::ResourceStates::IndexBuffer : nvrhi::ResourceStates::VertexBuffer;
|
||||
desc.keepInitialState = true;
|
||||
|
||||
buffer = renderer->createBuffer(desc);
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_NVRHI::beginFrame()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
nvrhi::IGraphicsPipeline* ImGui_NVRHI::getPSO(nvrhi::IFramebuffer* fb)
|
||||
{
|
||||
if (pso)
|
||||
return pso;
|
||||
|
||||
pso = renderer->createGraphicsPipeline(basePSODesc, fb);
|
||||
assert(pso);
|
||||
|
||||
return pso;
|
||||
}
|
||||
|
||||
nvrhi::IBindingSet* ImGui_NVRHI::getBindingSet(void* handle)
|
||||
{
|
||||
nvrhi::ITexture* texture = reinterpret_cast<nvrhi::ITexture*>(handle);
|
||||
auto iter = bindingsCache.find(texture);
|
||||
if (iter != bindingsCache.end())
|
||||
{
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
nvrhi::BindingSetDesc desc;
|
||||
|
||||
desc.bindings = {
|
||||
nvrhi::BindingSetItem::PushConstants(0, sizeof(ImguiPushConstants)),
|
||||
nvrhi::BindingSetItem::Texture_SRV(0, texture),
|
||||
nvrhi::BindingSetItem::Sampler(0, fontSampler)
|
||||
};
|
||||
|
||||
nvrhi::BindingSetHandle binding;
|
||||
binding = renderer->createBindingSet(desc, bindingLayout);
|
||||
assert(binding);
|
||||
|
||||
bindingsCache[texture] = binding;
|
||||
return binding;
|
||||
}
|
||||
|
||||
bool ImGui_NVRHI::updateGeometry(nvrhi::ICommandList* commandList)
|
||||
{
|
||||
ImDrawData *drawData = ImGui::GetDrawData();
|
||||
|
||||
// create/resize vertex and index buffers if needed
|
||||
if (!reallocateBuffer(vertexBuffer,
|
||||
drawData->TotalVtxCount * sizeof(ImDrawVert),
|
||||
(drawData->TotalVtxCount + 5000) * sizeof(ImDrawVert),
|
||||
false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!reallocateBuffer(indexBuffer,
|
||||
drawData->TotalIdxCount * sizeof(ImDrawIdx),
|
||||
(drawData->TotalIdxCount + 5000) * sizeof(ImDrawIdx),
|
||||
true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
vtxBuffer.resize(vertexBuffer->getDesc().byteSize / sizeof(ImDrawVert));
|
||||
idxBuffer.resize(indexBuffer->getDesc().byteSize / sizeof(ImDrawIdx));
|
||||
|
||||
// copy and convert all vertices into a single contiguous buffer
|
||||
ImDrawVert *vtxDst = &vtxBuffer[0];
|
||||
ImDrawIdx *idxDst = &idxBuffer[0];
|
||||
|
||||
for(int n = 0; n < drawData->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList *cmdList = drawData->CmdLists[n];
|
||||
|
||||
memcpy(vtxDst, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idxDst, cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
|
||||
vtxDst += cmdList->VtxBuffer.Size;
|
||||
idxDst += cmdList->IdxBuffer.Size;
|
||||
}
|
||||
|
||||
commandList->writeBuffer(vertexBuffer, &vtxBuffer[0], vertexBuffer->getDesc().byteSize);
|
||||
commandList->writeBuffer(indexBuffer, &idxBuffer[0], indexBuffer->getDesc().byteSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_NVRHI::render(nvrhi::IFramebuffer* framebuffer)
|
||||
{
|
||||
ImDrawData *drawData = ImGui::GetDrawData();
|
||||
const auto& io = ImGui::GetIO();
|
||||
|
||||
m_commandList->open();
|
||||
m_commandList->beginMarker("ImGUI");
|
||||
|
||||
if (!updateGeometry(m_commandList))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle DPI scaling
|
||||
drawData->ScaleClipRects(io.DisplayFramebufferScale);
|
||||
|
||||
ImguiPushConstants push_constants{};
|
||||
push_constants.m_inv_display_size = {1.f / io.DisplaySize.x, 1.f / io.DisplaySize.y};
|
||||
push_constants.m_ui_nits = NvRenderer::Config()->m_display.GetHDRConfig().m_ui_nits;
|
||||
push_constants.m_tonemap_function = NvRenderer::Config()->m_display.GetHDRConfig().m_tonemap_function;
|
||||
|
||||
// set up graphics state
|
||||
nvrhi::GraphicsState drawState;
|
||||
|
||||
drawState.framebuffer = framebuffer;
|
||||
assert(drawState.framebuffer);
|
||||
|
||||
drawState.pipeline = getPSO(drawState.framebuffer);
|
||||
|
||||
drawState.viewport.viewports.push_back(nvrhi::Viewport(io.DisplaySize.x * io.DisplayFramebufferScale.x,
|
||||
io.DisplaySize.y * io.DisplayFramebufferScale.y));
|
||||
drawState.viewport.scissorRects.resize(1); // updated below
|
||||
|
||||
nvrhi::VertexBufferBinding vbufBinding;
|
||||
vbufBinding.buffer = vertexBuffer;
|
||||
vbufBinding.slot = 0;
|
||||
vbufBinding.offset = 0;
|
||||
drawState.vertexBuffers.push_back(vbufBinding);
|
||||
|
||||
drawState.indexBuffer.buffer = indexBuffer;
|
||||
drawState.indexBuffer.format = (sizeof(ImDrawIdx) == 2 ? nvrhi::Format::R16_UINT : nvrhi::Format::R32_UINT);
|
||||
drawState.indexBuffer.offset = 0;
|
||||
|
||||
// render command lists
|
||||
int vtxOffset = 0;
|
||||
int idxOffset = 0;
|
||||
for(int n = 0; n < drawData->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList *cmdList = drawData->CmdLists[n];
|
||||
for(int i = 0; i < cmdList->CmdBuffer.Size; i++)
|
||||
{
|
||||
const ImDrawCmd *pCmd = &cmdList->CmdBuffer[i];
|
||||
|
||||
if (pCmd->UserCallback)
|
||||
{
|
||||
pCmd->UserCallback(cmdList, pCmd);
|
||||
} else {
|
||||
drawState.bindings = { getBindingSet(pCmd->TextureId) };
|
||||
assert(drawState.bindings[0]);
|
||||
|
||||
drawState.viewport.scissorRects[0] = nvrhi::Rect(int(pCmd->ClipRect.x),
|
||||
int(pCmd->ClipRect.z),
|
||||
int(pCmd->ClipRect.y),
|
||||
int(pCmd->ClipRect.w));
|
||||
|
||||
nvrhi::DrawArguments drawArguments;
|
||||
drawArguments.vertexCount = pCmd->ElemCount;
|
||||
drawArguments.startIndexLocation = idxOffset;
|
||||
drawArguments.startVertexLocation = vtxOffset;
|
||||
|
||||
m_commandList->setGraphicsState(drawState);
|
||||
m_commandList->setPushConstants(&push_constants, sizeof(push_constants));
|
||||
m_commandList->drawIndexed(drawArguments);
|
||||
}
|
||||
|
||||
idxOffset += pCmd->ElemCount;
|
||||
}
|
||||
|
||||
vtxOffset += cmdList->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
m_commandList->endMarker();
|
||||
m_commandList->close();
|
||||
renderer->executeCommandList(m_commandList);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_NVRHI::backbufferResizing()
|
||||
{
|
||||
pso = nullptr;
|
||||
}
|
||||
98
betterRenderer/renderer/source/imgui_nvrhi.h
Normal file
98
betterRenderer/renderer/source/imgui_nvrhi.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
License for Dear ImGui
|
||||
|
||||
Copyright (c) 2014-2019 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include "nvtexture.h"
|
||||
|
||||
struct ImGui_NVRHI
|
||||
{
|
||||
nvrhi::DeviceHandle renderer;
|
||||
nvrhi::CommandListHandle m_commandList;
|
||||
|
||||
nvrhi::ShaderHandle vertexShader;
|
||||
nvrhi::ShaderHandle pixelShader;
|
||||
nvrhi::InputLayoutHandle shaderAttribLayout;
|
||||
|
||||
nvrhi::TextureHandle fontTexture;
|
||||
nvrhi::SamplerHandle fontSampler;
|
||||
|
||||
nvrhi::BufferHandle vertexBuffer;
|
||||
nvrhi::BufferHandle indexBuffer;
|
||||
|
||||
nvrhi::BindingLayoutHandle bindingLayout;
|
||||
nvrhi::GraphicsPipelineDesc basePSODesc;
|
||||
|
||||
nvrhi::GraphicsPipelineHandle pso;
|
||||
std::unordered_map<nvrhi::ITexture*, nvrhi::BindingSetHandle> bindingsCache;
|
||||
|
||||
std::vector<ImDrawVert> vtxBuffer;
|
||||
std::vector<ImDrawIdx> idxBuffer;
|
||||
|
||||
NvTextureManager* textureManager;
|
||||
|
||||
bool init(class NvRendererBackend *backend);
|
||||
bool beginFrame();
|
||||
bool render(nvrhi::IFramebuffer* framebuffer);
|
||||
void backbufferResizing();
|
||||
|
||||
private:
|
||||
bool reallocateBuffer(nvrhi::BufferHandle& buffer, size_t requiredSize, size_t reallocateSize, bool isIndexBuffer);
|
||||
|
||||
bool createFontTexture(nvrhi::ICommandList* commandList);
|
||||
|
||||
nvrhi::IGraphicsPipeline* getPSO(nvrhi::IFramebuffer* fb);
|
||||
nvrhi::IBindingSet* getBindingSet(void* handle);
|
||||
bool updateGeometry(nvrhi::ICommandList* commandList);
|
||||
};
|
||||
1
betterRenderer/renderer/source/instancecache.cpp
Normal file
1
betterRenderer/renderer/source/instancecache.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "instancecache.h"
|
||||
54
betterRenderer/renderer/source/instancecache.h
Normal file
54
betterRenderer/renderer/source/instancecache.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
struct InstanceCacheId {
|
||||
gfx::geometry_handle m_geometry;
|
||||
material_handle m_material;
|
||||
InstanceCacheId(gfx::geometry_handle geometry, material_handle material)
|
||||
: m_geometry(geometry), m_material(material) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<InstanceCacheId> {
|
||||
std::size_t operator()(const InstanceCacheId& op) const {
|
||||
size_t hash_geometry = std::hash<uint64_t>()(op.m_geometry);
|
||||
size_t hash_material = std::hash<uint32_t>()(op.m_material);
|
||||
return hash_geometry ^ hash_material + 0x9e3779b9 + (hash_geometry << 6) +
|
||||
(hash_geometry >> 2);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::equal_to<InstanceCacheId> {
|
||||
bool operator()(const InstanceCacheId& lhs,
|
||||
const InstanceCacheId& rhs) const {
|
||||
return lhs.m_geometry == rhs.m_geometry && lhs.m_material == rhs.m_material;
|
||||
}
|
||||
};
|
||||
|
||||
struct InstanceCache {
|
||||
struct Instance {
|
||||
glm::mat3x4 m_transform;
|
||||
glm::mat3x4 m_transform_history;
|
||||
Instance(const glm::dmat4& transform, const glm::dmat4& transform_history)
|
||||
: m_transform(static_cast<glm::mat3x4>(glm::transpose(transform))),
|
||||
m_transform_history(
|
||||
static_cast<glm::mat3x4>(glm::transpose(transform_history))) {}
|
||||
};
|
||||
std::vector<Instance> m_instances;
|
||||
};
|
||||
|
||||
struct InstanceBank {
|
||||
std::unordered_map<InstanceCacheId, InstanceCache> m_cache;
|
||||
void Clear() {
|
||||
for (auto& [id, cache] : m_cache) {
|
||||
cache.m_instances.clear();
|
||||
}
|
||||
}
|
||||
void AddInstance(gfx::geometry_handle geometry, material_handle material,
|
||||
const glm::dmat4& transform, const glm::dmat4& history) {
|
||||
auto& cache = m_cache[{geometry, material}];
|
||||
cache.m_instances.emplace_back(transform, history);
|
||||
}
|
||||
};
|
||||
149
betterRenderer/renderer/source/ma_dds.cpp
Normal file
149
betterRenderer/renderer/source/ma_dds.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "ddspp/ddspp.h"
|
||||
#include "nvtexture.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
bool NvTexture::LoadDDS(std::string &data, int size_bias) {
|
||||
ddspp::Descriptor desc{};
|
||||
if (ddspp::decode_header(reinterpret_cast<uint8_t *>(data.data()), desc) !=
|
||||
ddspp::Success)
|
||||
return false;
|
||||
std::stringstream ss(data, std::ios::in | std::ios::binary);
|
||||
// auto sd = opengl_texture::deserialize_ddsd(ss);
|
||||
// int blksize = 16;
|
||||
// m_bc = true;
|
||||
m_has_alpha = false;
|
||||
switch (desc.format) {
|
||||
case ddspp::DXGIFormat::BC1_UNORM:
|
||||
case ddspp::DXGIFormat::BC1_UNORM_SRGB:
|
||||
m_format =
|
||||
m_srgb ? nvrhi::Format::BC1_UNORM_SRGB : nvrhi::Format::BC1_UNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC2_UNORM:
|
||||
case ddspp::DXGIFormat::BC2_UNORM_SRGB:
|
||||
m_format =
|
||||
m_srgb ? nvrhi::Format::BC2_UNORM_SRGB : nvrhi::Format::BC2_UNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC3_UNORM:
|
||||
case ddspp::DXGIFormat::BC3_UNORM_SRGB:
|
||||
m_format =
|
||||
m_srgb ? nvrhi::Format::BC3_UNORM_SRGB : nvrhi::Format::BC3_UNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC4_UNORM:
|
||||
m_format = nvrhi::Format::BC4_UNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC4_SNORM:
|
||||
m_format = nvrhi::Format::BC4_SNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC5_UNORM:
|
||||
m_format = nvrhi::Format::BC5_UNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC5_SNORM:
|
||||
m_format = nvrhi::Format::BC5_SNORM;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC6H_SF16:
|
||||
m_format = nvrhi::Format::BC6H_SFLOAT;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC6H_UF16:
|
||||
m_format = nvrhi::Format::BC6H_UFLOAT;
|
||||
break;
|
||||
|
||||
case ddspp::DXGIFormat::BC7_UNORM:
|
||||
case ddspp::DXGIFormat::BC7_UNORM_SRGB:
|
||||
m_format =
|
||||
m_srgb ? nvrhi::Format::BC7_UNORM_SRGB : nvrhi::Format::BC7_UNORM;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
//
|
||||
m_width = desc.width;
|
||||
m_height = desc.height;
|
||||
|
||||
struct Bc2Block {
|
||||
uint64_t alpha;
|
||||
uint16_t color0;
|
||||
uint16_t color1;
|
||||
uint32_t pixels;
|
||||
[[nodiscard]] bool HasTransparency() const {
|
||||
return alpha != static_cast<uint64_t>(-1);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Bc2Block) == 16);
|
||||
|
||||
struct Bc3Block {
|
||||
uint16_t alpha;
|
||||
uint16_t pixels0;
|
||||
uint16_t pixels1;
|
||||
uint16_t pixels2;
|
||||
uint64_t color_block;
|
||||
[[nodiscard]] bool HasTransparency() const {
|
||||
return alpha != static_cast<uint16_t>(-1);
|
||||
}
|
||||
};
|
||||
|
||||
struct Bc7Block {
|
||||
uint8_t data[16];
|
||||
[[nodiscard]] bool HasTransparency() const {
|
||||
int mode = 0;
|
||||
if (!data[0]) return false;
|
||||
for (; !(data[0] & (1 << (7 - mode))); ++mode)
|
||||
;
|
||||
return mode > 3;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Bc3Block) == 16);
|
||||
for (int mip = 0; mip < desc.numMips; ++mip) {
|
||||
int width = std::max(m_width >> mip, 1);
|
||||
int height = std::max(m_height >> mip, 1);
|
||||
int blkx = (width + desc.blockWidth - 1) / desc.blockWidth;
|
||||
int blky = (height + desc.blockHeight - 1) / desc.blockHeight;
|
||||
auto &[pitch, mip_data] = m_data.emplace_back();
|
||||
pitch = ddspp::get_row_pitch(desc, mip);
|
||||
mip_data.resize(pitch * blky, '\0');
|
||||
ss.seekg(desc.headerSize + ddspp::get_offset(desc, mip, 0));
|
||||
ss.read(mip_data.data(), mip_data.size());
|
||||
if (!mip) {
|
||||
switch (desc.format) {
|
||||
case ddspp::DXGIFormat::BC2_UNORM:
|
||||
case ddspp::DXGIFormat::BC2_UNORM_SRGB:
|
||||
for (size_t j = 0; !m_has_alpha && j < blkx * blky; ++j) {
|
||||
const Bc2Block &block =
|
||||
reinterpret_cast<const Bc2Block *>(data.data())[j];
|
||||
if (block.HasTransparency()) m_has_alpha = true;
|
||||
}
|
||||
break;
|
||||
case ddspp::DXGIFormat::BC3_UNORM:
|
||||
case ddspp::DXGIFormat::BC3_UNORM_SRGB:
|
||||
for (size_t j = 0; !m_has_alpha && j < blkx * blky; ++j) {
|
||||
const Bc3Block &block =
|
||||
reinterpret_cast<const Bc3Block *>(data.data())[j];
|
||||
if (block.HasTransparency()) m_has_alpha = true;
|
||||
}
|
||||
break;
|
||||
case ddspp::DXGIFormat::BC7_UNORM:
|
||||
case ddspp::DXGIFormat::BC7_UNORM_SRGB:
|
||||
for (size_t j = 0; !m_has_alpha && j < blkx * blky; ++j) {
|
||||
const Bc7Block &block =
|
||||
reinterpret_cast<const Bc7Block *>(data.data())[j];
|
||||
if (block.HasTransparency()) m_has_alpha = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
363
betterRenderer/renderer/source/materialparser.cpp
Normal file
363
betterRenderer/renderer/source/materialparser.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "materialparser.h"
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
#include <Globals.h>
|
||||
#include <Logs.h>
|
||||
#include <fmt/format.h>
|
||||
#include <material.h>
|
||||
#include <parser.h>
|
||||
#include <utilities.h>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include <string>
|
||||
|
||||
std::string_view MaterialAdapter::GetTexturePathForEntry(
|
||||
std::string_view key) const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetTexturePathForEntry(key);
|
||||
}
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
int MaterialAdapter::GetTextureSizeBiasForEntry(std::string_view key) const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetTextureSizeBiasForEntry(key);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapter::GetShader() const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetShader();
|
||||
}
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
int MaterialAdapter::GetShadowRank() const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetShadowRank();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
glm::dvec2 MaterialAdapter::GetSize() const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetSize();
|
||||
}
|
||||
return {-1., -1.};
|
||||
}
|
||||
|
||||
std::optional<float> MaterialAdapter::GetOpacity() const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetOpacity();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool MaterialAdapter::Parse(std::string_view filepath) {
|
||||
if (m_material_internal = MaterialAdapterManul::Parse(filepath);
|
||||
m_material_internal) {
|
||||
return true;
|
||||
}
|
||||
if (m_material_internal = MaterialAdapterLegacyMatFile::Parse(filepath);
|
||||
m_material_internal) {
|
||||
return true;
|
||||
}
|
||||
if (m_material_internal = MaterialAdapterSingleTexture::Parse(filepath);
|
||||
m_material_internal) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<bool> MaterialAdapter::GetMaskedShadowOverride() const {
|
||||
if (m_material_internal) {
|
||||
return m_material_internal->GetMaskedShadowOverride();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<MaterialAdapterInternal> MaterialAdapterLegacyMatFile::Parse(
|
||||
std::string_view path) {
|
||||
auto const locator{material_manager::find_on_disk(std::string(path))};
|
||||
|
||||
if (locator.first.size()) {
|
||||
// try to parse located file resource
|
||||
cParser materialparser(locator.first + locator.second,
|
||||
cParser::buffer_FILE);
|
||||
auto material = std::make_shared<MaterialAdapterLegacyMatFile>();
|
||||
while (material->ParseLevel(materialparser, 0)) {
|
||||
}
|
||||
return material;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapterLegacyMatFile::GetTexturePathForEntry(
|
||||
std::string_view key) const {
|
||||
if (key == "diffuse") {
|
||||
if (auto it = m_texture_mapping.find("_diffuse:");
|
||||
it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
if (auto it = m_texture_mapping.find("1:"); it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
} else if (key == "normal") {
|
||||
if (auto it = m_texture_mapping.find("_normals:");
|
||||
it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
if (auto it = m_texture_mapping.find("_normalmap:");
|
||||
it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
if (auto it = m_texture_mapping.find("_reflmap:");
|
||||
it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
if (auto it = m_texture_mapping.find("2:"); it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
} else {
|
||||
if (auto it = m_texture_mapping.find(fmt::format("_{}:", key));
|
||||
it != m_texture_mapping.end()) {
|
||||
return it->second.m_path;
|
||||
}
|
||||
}
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapterLegacyMatFile::GetShader() const {
|
||||
if (m_shader == "water") {
|
||||
return "legacy_water";
|
||||
}
|
||||
if (IsSpecGlossShader() && HasSpecGlossMap()) {
|
||||
if (IsNormalMapShader() && HasNormalMap()) {
|
||||
return "legacy_normalmap_specgloss";
|
||||
}
|
||||
return "legacy_specgloss";
|
||||
}
|
||||
if (IsNormalMapShader() && HasNormalMap()) {
|
||||
return "legacy_normalmap";
|
||||
}
|
||||
if (HasNormalMap()) {
|
||||
return "legacy_reflection";
|
||||
}
|
||||
return "legacy";
|
||||
}
|
||||
|
||||
bool MaterialAdapterLegacyMatFile::ParseLevel(cParser& parser, int priority) {
|
||||
// NOTE: comma can be part of legacy file names, so we don't treat it as a
|
||||
// separator here
|
||||
auto key{parser.getToken<std::string>(true, "\n\r\t ;[]")};
|
||||
// key can be an actual key or block end
|
||||
if ((true == key.empty()) || (key == "}")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (priority != -1) {
|
||||
// regular attribute processing mode
|
||||
|
||||
// mark potential material change
|
||||
// update_on_weather_change |= is_weather(key);
|
||||
// update_on_season_change |= is_season(key);
|
||||
|
||||
if (key == Global.Weather) {
|
||||
// weather textures override generic (pri 0) and seasonal (pri 1) textures
|
||||
// seasonal weather textures (pri 1+2=3) override generic weather (pri 2)
|
||||
// textures skip the opening bracket
|
||||
auto const value{parser.getToken<std::string>(true, "\n\r\t ;")};
|
||||
while (ParseLevel(parser, priority + 2)) {
|
||||
; // all work is done in the header
|
||||
}
|
||||
} else if (key == Global.Season) {
|
||||
// seasonal textures override generic textures
|
||||
// skip the opening bracket
|
||||
auto const value{parser.getToken<std::string>(true, "\n\r\t ;")};
|
||||
while (ParseLevel(parser, priority + 1)) {
|
||||
; // all work is done in the header
|
||||
}
|
||||
}
|
||||
|
||||
else if (key.compare(0, 7, "texture") == 0) {
|
||||
key.erase(0, 7);
|
||||
|
||||
auto value{deserialize_random_set(parser)};
|
||||
replace_slashes(value);
|
||||
auto& [tex_priority, texture] = m_texture_mapping[key];
|
||||
if (tex_priority < priority) {
|
||||
texture = value;
|
||||
tex_priority = priority;
|
||||
}
|
||||
} else if (key.compare(0, 5, "param") == 0) {
|
||||
// key.erase(0, 5);
|
||||
//
|
||||
parser.getToken<std::string>(true, "\n\r\t;");
|
||||
// std::istringstream stream(value);
|
||||
// glm::vec4 data;
|
||||
// stream >> data.r;
|
||||
// stream >> data.g;
|
||||
// stream >> data.b;
|
||||
// stream >> data.a;
|
||||
//
|
||||
// auto it = parse_info->param_mapping.find(key);
|
||||
// if (it == parse_info->param_mapping.end())
|
||||
// parse_info->param_mapping.emplace(
|
||||
// std::make_pair(key, parse_info_s::param_def({data, Priority})));
|
||||
// else if (Priority > it->second.priority) {
|
||||
// parse_info->param_mapping.erase(it);
|
||||
// parse_info->param_mapping.emplace(
|
||||
// std::make_pair(key, parse_info_s::param_def({data, Priority})));
|
||||
// }
|
||||
} else if (key == "shader:" &&
|
||||
(m_shader.empty() || priority > m_shader_priority)) {
|
||||
m_shader = deserialize_random_set(parser);
|
||||
m_shader_priority = priority;
|
||||
} else if (key == "opacity:" && priority > m_opacity_priority) {
|
||||
std::string value = deserialize_random_set(parser);
|
||||
m_opacity = std::stof(value); // m7t: handle exception
|
||||
m_opacity_priority = priority;
|
||||
} else if (key == "selfillum:" && priority > m_selfillum_priority) {
|
||||
std::string value = deserialize_random_set(parser);
|
||||
m_selfillum = std::stof(value); // m7t: handle exception
|
||||
m_selfillum_priority = priority;
|
||||
} else if (key == "glossiness:" /* && Priority > m_glossiness_priority*/) {
|
||||
deserialize_random_set(parser);
|
||||
// glossiness = std::stof(value); // m7t: handle exception
|
||||
// m_glossiness_priority = Priority;
|
||||
} else if (key == "shadow_rank:") {
|
||||
std::string value = deserialize_random_set(parser);
|
||||
m_shadow_rank = std::stoi(value); // m7t: handle exception
|
||||
} else if (key == "size:") {
|
||||
parser.getTokens(2);
|
||||
parser >> m_size.x >> m_size.y;
|
||||
} else {
|
||||
auto const value = parser.getToken<std::string>(true, "\n\r\t ;");
|
||||
if (value == "{") {
|
||||
// unrecognized or ignored token, but comes with attribute block and
|
||||
// potential further nesting go through it and discard the content
|
||||
while (ParseLevel(parser, -1)) {
|
||||
; // all work is done in the header
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// discard mode; ignores all retrieved tokens
|
||||
auto const value{parser.getToken<std::string>(true, "\n\r\t ;")};
|
||||
if (value == "{") {
|
||||
// ignored tokens can come with their own blocks, ignore these recursively
|
||||
// go through it and discard the content
|
||||
while (true == ParseLevel(parser, -1)) {
|
||||
; // all work is done in the header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // return value marks a key: value pair was extracted, nothing
|
||||
// about whether it's recognized
|
||||
}
|
||||
|
||||
bool MaterialAdapterLegacyMatFile::HasNormalMap() const {
|
||||
return m_texture_mapping.find("_normals:") != m_texture_mapping.end() ||
|
||||
m_texture_mapping.find("_normalmap:") != m_texture_mapping.end() ||
|
||||
m_texture_mapping.find("_reflmap:") != m_texture_mapping.end() ||
|
||||
m_texture_mapping.find("2:") != m_texture_mapping.end();
|
||||
}
|
||||
|
||||
bool MaterialAdapterLegacyMatFile::HasSpecGlossMap() const {
|
||||
return m_texture_mapping.find("_specgloss:") != m_texture_mapping.end();
|
||||
}
|
||||
|
||||
bool MaterialAdapterLegacyMatFile::IsNormalMapShader() const {
|
||||
return m_shader.find("normalmap") != std::string::npos ||
|
||||
m_shader.find("parallax") != std::string::npos;
|
||||
}
|
||||
|
||||
bool MaterialAdapterLegacyMatFile::IsReflectionShader() const {
|
||||
return m_shader.find("reflmap") != std::string::npos;
|
||||
}
|
||||
|
||||
bool MaterialAdapterLegacyMatFile::IsSpecGlossShader() const {
|
||||
return m_shader.find("specgloss") != std::string::npos;
|
||||
}
|
||||
|
||||
std::optional<float> MaterialAdapterLegacyMatFile::GetOpacity() const {
|
||||
return m_opacity;
|
||||
}
|
||||
|
||||
std::shared_ptr<MaterialAdapterInternal> MaterialAdapterSingleTexture::Parse(
|
||||
std::string_view path) {
|
||||
return std::make_shared<MaterialAdapterSingleTexture>(path);
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapterSingleTexture::GetTexturePathForEntry(
|
||||
std::string_view key) const {
|
||||
if (key == "diffuse") {
|
||||
return m_tex_diffuse;
|
||||
}
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapterSingleTexture::GetShader() const {
|
||||
return "legacy";
|
||||
}
|
||||
|
||||
std::optional<float> MaterialAdapterSingleTexture::GetOpacity() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<MaterialAdapterInternal> MaterialAdapterManul::Parse(
|
||||
std::string_view path) {
|
||||
std::string entries[]{
|
||||
Global.asCurrentTexturePath + std::string(path) + ".manul",
|
||||
std::string(path) + ".manul"};
|
||||
for (auto it = std::begin(entries); it != std::end(entries); ++it) {
|
||||
if (!std::filesystem::exists(*it)) {
|
||||
continue;
|
||||
}
|
||||
auto root = YAML::LoadFile(*it);
|
||||
auto material = std::make_shared<MaterialAdapterManul>();
|
||||
material->m_shader = root["shader"].as<std::string>();
|
||||
for (const auto it : root["textures"]) {
|
||||
material->m_textures[it.first.as<std::string>()] =
|
||||
it.second.as<std::string>();
|
||||
}
|
||||
if (auto alpha_threshold = root["alpha_threshold"];
|
||||
alpha_threshold.IsScalar()) {
|
||||
material->m_alpha_threshold = alpha_threshold.as<float>();
|
||||
}
|
||||
if (auto size_bias = root["max_resolution_bias"]; size_bias.IsScalar())
|
||||
material->m_size_bias = size_bias.as<int>();
|
||||
return material;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapterManul::GetTexturePathForEntry(
|
||||
std::string_view key) const {
|
||||
if (auto it = m_textures.find(std::string(key)); it != m_textures.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
int MaterialAdapterManul::GetTextureSizeBiasForEntry(
|
||||
std::string_view key) const {
|
||||
return m_size_bias;
|
||||
}
|
||||
|
||||
std::string_view MaterialAdapterManul::GetShader() const { return m_shader; }
|
||||
|
||||
std::optional<float> MaterialAdapterManul::GetOpacity() const {
|
||||
return m_alpha_threshold;
|
||||
}
|
||||
|
||||
std::optional<bool> MaterialAdapterInternal::GetMaskedShadowOverride() const {
|
||||
auto threshold = GetOpacity();
|
||||
if (threshold.has_value()) {
|
||||
return !!*threshold;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
99
betterRenderer/renderer/source/materialparser.h
Normal file
99
betterRenderer/renderer/source/materialparser.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <string>
|
||||
|
||||
struct MaterialAdapterInternal {
|
||||
virtual ~MaterialAdapterInternal() = default;
|
||||
virtual std::string_view GetTexturePathForEntry(
|
||||
std::string_view key) const = 0;
|
||||
virtual int GetTextureSizeBiasForEntry(
|
||||
std::string_view key) const = 0;
|
||||
virtual std::string_view GetShader() const = 0;
|
||||
virtual int GetShadowRank() const { return 0; }
|
||||
virtual glm::dvec2 GetSize() const { return {-1., -1.}; }
|
||||
virtual std::optional<float> GetOpacity() const = 0;
|
||||
virtual std::optional<bool> GetMaskedShadowOverride() const;
|
||||
};
|
||||
|
||||
struct MaterialAdapter {
|
||||
std::string_view GetTexturePathForEntry(std::string_view key) const;
|
||||
int GetTextureSizeBiasForEntry(std::string_view key) const;
|
||||
std::string_view GetShader() const;
|
||||
int GetShadowRank() const;
|
||||
glm::dvec2 GetSize() const;
|
||||
std::optional<float> GetOpacity() const;
|
||||
bool Parse(std::string_view filepath);
|
||||
std::shared_ptr<MaterialAdapterInternal> m_material_internal;
|
||||
std::optional<bool> GetMaskedShadowOverride() const;
|
||||
};
|
||||
|
||||
struct MaterialAdapterManul : public MaterialAdapterInternal {
|
||||
static std::shared_ptr<MaterialAdapterInternal> Parse(std::string_view path);
|
||||
virtual std::string_view GetTexturePathForEntry(
|
||||
std::string_view key) const override;
|
||||
virtual int GetTextureSizeBiasForEntry(std::string_view key) const override;
|
||||
virtual std::string_view GetShader() const override;
|
||||
virtual std::optional<float> GetOpacity() const override;
|
||||
std::string m_shader;
|
||||
std::unordered_map<std::string, std::string> m_textures;
|
||||
int m_size_bias;
|
||||
std::optional<float> m_alpha_threshold = std::nullopt;
|
||||
glm::dvec2 m_size{-1., -1.};
|
||||
virtual glm::dvec2 GetSize() const override { return m_size; }
|
||||
};
|
||||
|
||||
struct MaterialAdapterLegacyMatFile : public MaterialAdapterInternal {
|
||||
static std::shared_ptr<MaterialAdapterInternal> Parse(std::string_view path);
|
||||
virtual std::string_view GetTexturePathForEntry(
|
||||
std::string_view key) const override;
|
||||
virtual int GetTextureSizeBiasForEntry(std::string_view key) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual std::string_view GetShader() const override;
|
||||
bool ParseLevel(class cParser& parser, int priority);
|
||||
std::optional<float> m_opacity = std::nullopt;
|
||||
int m_shadow_rank;
|
||||
float m_selfillum;
|
||||
std::string m_tex_diffuse;
|
||||
std::string m_tex_normal;
|
||||
std::string m_shader;
|
||||
int m_shader_priority = -1;
|
||||
struct TextureEntry {
|
||||
int m_priority = -1;
|
||||
std::string m_path = "";
|
||||
};
|
||||
glm::dvec2 m_size{-1., -1.};
|
||||
virtual glm::dvec2 GetSize() const override { return m_size; }
|
||||
bool HasNormalMap() const;
|
||||
bool HasSpecGlossMap() const;
|
||||
bool IsSpecGlossShader() const;
|
||||
bool IsNormalMapShader() const;
|
||||
bool IsReflectionShader() const;
|
||||
std::unordered_map<std::string, TextureEntry> m_texture_mapping;
|
||||
virtual int GetShadowRank() const override { return m_shadow_rank; }
|
||||
virtual std::optional<float> GetOpacity() const override;
|
||||
int m_opacity_priority = -1;
|
||||
int m_selfillum_priority = -1;
|
||||
int m_diffuse_priority = -1;
|
||||
int m_normal_priority = -1;
|
||||
};
|
||||
|
||||
struct MaterialAdapterSingleTexture : public MaterialAdapterInternal {
|
||||
static std::shared_ptr<MaterialAdapterInternal> Parse(std::string_view path);
|
||||
MaterialAdapterSingleTexture(std::string_view path) : m_tex_diffuse(path){};
|
||||
virtual std::string_view GetTexturePathForEntry(
|
||||
std::string_view key) const override;
|
||||
virtual int GetTextureSizeBiasForEntry(std::string_view key) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual std::string_view GetShader() const override;
|
||||
virtual std::optional<float> GetOpacity() const override;
|
||||
std::string m_tex_diffuse;
|
||||
};
|
||||
10
betterRenderer/renderer/source/motioncache.cpp
Normal file
10
betterRenderer/renderer/source/motioncache.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "motioncache.h"
|
||||
|
||||
MotionCacheScope::MotionCacheScope(void const* instance, MotionCache* cache)
|
||||
: m_instance(instance), m_cache(cache) {
|
||||
m_cache->SetActiveScope(this);
|
||||
}
|
||||
|
||||
MotionCacheScope::~MotionCacheScope() {
|
||||
m_cache->UnsetActiveScope(this);
|
||||
}
|
||||
76
betterRenderer/renderer/source/motioncache.h
Normal file
76
betterRenderer/renderer/source/motioncache.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct MotionCacheId {
|
||||
union {
|
||||
struct {
|
||||
void const* m_instance;
|
||||
void const* m_sub;
|
||||
};
|
||||
struct {
|
||||
size_t m_key[2];
|
||||
};
|
||||
};
|
||||
MotionCacheId(void const* instance, void const* sub)
|
||||
: m_instance(instance), m_sub(sub) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::hash<MotionCacheId> {
|
||||
std::size_t operator()(const MotionCacheId& k) const {
|
||||
using hash = std::hash<size_t>;
|
||||
size_t hash_instance = hash()(k.m_key[0]);
|
||||
size_t hash_sub = hash()(k.m_key[1]);
|
||||
return hash_instance ^
|
||||
hash_sub + 0x9e3779b9 + (hash_instance << 6) + (hash_instance >> 2);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct std::equal_to<MotionCacheId> {
|
||||
bool operator()(const MotionCacheId& lhs, const MotionCacheId& rhs) const{
|
||||
return lhs.m_key[0] == rhs.m_key[0] && lhs.m_key[1] == rhs.m_key[1];
|
||||
}
|
||||
};
|
||||
|
||||
struct MotionCacheEntry {
|
||||
glm::dmat4x3 m_cached_transform{};
|
||||
glm::dmat4x3 m_history_transform{};
|
||||
size_t m_cached_frame = static_cast<size_t>(-1);
|
||||
};
|
||||
|
||||
struct MotionCacheScope {
|
||||
friend struct MotionCache;
|
||||
~MotionCacheScope();
|
||||
|
||||
private:
|
||||
MotionCacheScope(void const* instance, struct MotionCache* cache);
|
||||
void const* m_instance;
|
||||
MotionCache* m_cache;
|
||||
};
|
||||
|
||||
struct MotionCache {
|
||||
friend struct MotionCacheScope;
|
||||
[[nodiscard]] MotionCacheScope SetInstance(void const* instance) {
|
||||
return {instance, this};
|
||||
}
|
||||
MotionCacheEntry& Get(void const* sub) {
|
||||
assert(m_active_scope);
|
||||
return m_cache[MotionCacheId{m_active_scope->m_instance, sub}];
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<MotionCacheId, MotionCacheEntry> m_cache{};
|
||||
MotionCacheScope* m_active_scope{};
|
||||
void SetActiveScope(MotionCacheScope* scope) {
|
||||
assert(!m_active_scope);
|
||||
m_active_scope = scope;
|
||||
}
|
||||
void UnsetActiveScope(const MotionCacheScope* scope) {
|
||||
assert(m_active_scope == scope);
|
||||
m_active_scope = nullptr;
|
||||
}
|
||||
};
|
||||
590
betterRenderer/renderer/source/nvmaterial.cpp
Normal file
590
betterRenderer/renderer/source/nvmaterial.cpp
Normal file
@@ -0,0 +1,590 @@
|
||||
#include "nvmaterial.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "Logs.h"
|
||||
#include "csm.h"
|
||||
#include "environment.h"
|
||||
#include "gbuffer.h"
|
||||
#include "gbufferblitpass.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "nvtexture.h"
|
||||
#include "sky.h"
|
||||
|
||||
bool NvRenderer::MaterialTemplate::CreateBindingSet(size_t pipeline_index,
|
||||
MaterialCache &cache) {
|
||||
// for (size_t i = 0; i < cache.m_pipelines.size(); ++i) {
|
||||
auto pipeline = cache.m_pipelines[pipeline_index];
|
||||
if (!pipeline) return false;
|
||||
|
||||
nvrhi::BindingSetDesc binding_set_desc{};
|
||||
|
||||
auto binding_layout = pipeline->getDesc().bindingLayouts[0];
|
||||
|
||||
if (!m_resource_mappings[pipeline_index].ToBindingSet(
|
||||
binding_set_desc, &cache, binding_layout->getDesc()))
|
||||
return false;
|
||||
|
||||
cache.m_binding_sets[pipeline_index] =
|
||||
m_renderer->GetBackend()->GetDevice()->createBindingSet(binding_set_desc,
|
||||
binding_layout);
|
||||
//}
|
||||
// nvrhi::BindingSetDesc binding_set_desc = nvrhi::BindingSetDesc();
|
||||
// nvrhi::BindingSetDesc binding_set_desc_cube = nvrhi::BindingSetDesc();
|
||||
// nvrhi::BindingSetDesc binding_set_desc_shadow = nvrhi::BindingSetDesc();
|
||||
// nvrhi::static_vector<nvrhi::SamplerHandle, 8 + 1> samplers{};
|
||||
// for (int i = 0; i < m_texture_bindings.size(); ++i) {
|
||||
// const auto &binding = m_texture_bindings[i];
|
||||
// if (binding.m_name.empty()) continue;
|
||||
// auto texture = m_renderer->m_texture_manager->GetRhiTexture(
|
||||
// cache.m_textures[i], pass.m_command_list_preparation);
|
||||
// if (!texture) {
|
||||
// texture = m_renderer->m_texture_manager->GetRhiTexture(
|
||||
// 1, pass.m_command_list_preparation);
|
||||
// }
|
||||
// auto &sampler = samplers.emplace_back();
|
||||
// auto &sampler_cubemap = samplers.emplace_back();
|
||||
// sampler = m_renderer->m_texture_manager->GetSamplerForTraits(
|
||||
// m_renderer->m_texture_manager->GetTraits(cache.m_textures[i]),
|
||||
// RenderPassType::Deferred);
|
||||
// sampler_cubemap = m_renderer->m_texture_manager->GetSamplerForTraits(
|
||||
// m_renderer->m_texture_manager->GetTraits(cache.m_textures[i]),
|
||||
// RenderPassType::CubeMap);
|
||||
// binding_set_desc.addItem(nvrhi::BindingSetItem::Texture_SRV(i, texture));
|
||||
// binding_set_desc.addItem(nvrhi::BindingSetItem::Sampler(i, sampler));
|
||||
// binding_set_desc_cube.addItem(
|
||||
// nvrhi::BindingSetItem::Texture_SRV(i, texture));
|
||||
// binding_set_desc_cube.addItem(
|
||||
// nvrhi::BindingSetItem::Sampler(i, sampler_cubemap));
|
||||
// if (cache.m_masked_shadow && i == m_masked_shadow_texture) {
|
||||
// auto &shadow_sampler = samplers.emplace_back();
|
||||
// shadow_sampler = m_renderer->m_texture_manager->GetSamplerForTraits(
|
||||
// m_renderer->m_texture_manager->GetTraits(cache.m_textures[i]),
|
||||
// RenderPassType::ShadowMap);
|
||||
// binding_set_desc_shadow.addItem(
|
||||
// nvrhi::BindingSetItem::Texture_SRV(0, texture));
|
||||
// binding_set_desc_shadow.addItem(
|
||||
// nvrhi::BindingSetItem::Sampler(0, shadow_sampler));
|
||||
// }
|
||||
// }
|
||||
// cache.m_binding_set =
|
||||
// m_renderer->GetBackend()->GetDevice()->createBindingSet(
|
||||
// binding_set_desc, m_binding_layout);
|
||||
// cache.m_binding_set_cubemap =
|
||||
// m_renderer->GetBackend()->GetDevice()->createBindingSet(
|
||||
// binding_set_desc_cube, m_binding_layout);
|
||||
// if (cache.m_masked_shadow) {
|
||||
// cache.m_binding_set_shadow =
|
||||
// m_renderer->GetBackend()->GetDevice()->createBindingSet(
|
||||
// binding_set_desc_shadow,
|
||||
// m_renderer->m_binding_layout_shadow_masked);
|
||||
// }
|
||||
cache.m_last_texture_updates[pipeline_index] = NvTexture::s_change_counter;
|
||||
return true;
|
||||
}
|
||||
|
||||
nvrhi::IGraphicsPipeline *NvRenderer::MaterialTemplate::GetPipeline(
|
||||
RenderPassType pass_type, DrawType draw_type, bool alpha_masked) const {
|
||||
if (auto pipeline =
|
||||
m_pipelines[Constants::GetPipelineIndex(pass_type, draw_type)])
|
||||
return pipeline;
|
||||
if (pass_type == RenderPassType::ShadowMap) {
|
||||
return m_renderer->m_pso_shadow[Constants::GetDefaultShadowPipelineIndex(
|
||||
draw_type, alpha_masked)];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NvRenderer::MaterialTemplate::Init(const YAML::Node &conf) {
|
||||
InitResourceRegistry();
|
||||
std::vector<MaResourceMapping> texture_mappings{};
|
||||
std::vector<MaResourceMapping> sampler_mappings{};
|
||||
MaResourceMapping texture_mapping_shadow{};
|
||||
MaResourceMapping sampler_mapping_shadow{};
|
||||
m_pipelines.fill(nullptr);
|
||||
m_masked_shadow_texture = conf["masked_shadow_texture"].as<std::string>();
|
||||
for (const auto it : conf["textures"]) {
|
||||
auto index = it.second["binding"].as<int>();
|
||||
m_texture_bindings.resize(
|
||||
glm::max(m_texture_bindings.size(), static_cast<size_t>(index) + 1));
|
||||
auto &[name, sampler_name, hint, default_texture] =
|
||||
m_texture_bindings[index];
|
||||
|
||||
name = it.first.as<std::string>();
|
||||
sampler_name = fmt::format("{:s}_sampler", name.c_str());
|
||||
|
||||
texture_mappings.emplace_back(
|
||||
MaResourceMapping::Texture_SRV(index, name.c_str()));
|
||||
sampler_mappings.emplace_back(
|
||||
MaResourceMapping::Sampler(index, sampler_name.c_str()));
|
||||
|
||||
RegisterTexture(name.c_str(), 1);
|
||||
RegisterResource(
|
||||
false, "masked_shadow_sampler",
|
||||
GetTextureManager()->GetSamplerForTraits(0, RenderPassType::ShadowMap),
|
||||
nvrhi::ResourceType::Sampler);
|
||||
|
||||
const static std::unordered_map<std::string_view, int> hints{
|
||||
{"color", GL_SRGB_ALPHA}, {"linear", GL_RGBA}, {"normalmap", GL_RG}};
|
||||
hint = hints.at(it.second["hint"].as<std::string_view>());
|
||||
|
||||
if (it.first.Scalar() == conf["masked_shadow_texture"].Scalar()) {
|
||||
texture_mapping_shadow = MaResourceMapping::Texture_SRV(0, name.c_str());
|
||||
sampler_mapping_shadow =
|
||||
MaResourceMapping::Sampler(0, "masked_shadow_sampler");
|
||||
}
|
||||
}
|
||||
std::array<nvrhi::ShaderHandle, static_cast<size_t>(RenderPassType::Num)>
|
||||
pixel_shaders;
|
||||
for (int i = 0; i < pixel_shaders.size(); ++i) {
|
||||
pixel_shaders[i] = m_renderer->GetBackend()->CreateMaterialShader(
|
||||
m_name, static_cast<RenderPassType>(i));
|
||||
}
|
||||
|
||||
{
|
||||
nvrhi::BindingLayoutDesc binding_layout_desc;
|
||||
binding_layout_desc.setVisibility(nvrhi::ShaderType::Pixel);
|
||||
for (int i = 0; i < m_texture_bindings.size(); ++i) {
|
||||
if (m_texture_bindings[i].m_name.empty()) continue;
|
||||
binding_layout_desc.addItem(nvrhi::BindingLayoutItem::Texture_SRV(i))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(i));
|
||||
}
|
||||
m_binding_layout =
|
||||
m_renderer->GetBackend()->GetDevice()->createBindingLayout(
|
||||
binding_layout_desc);
|
||||
}
|
||||
for (size_t i = 0; i < Constants::NumMaterialPasses(); ++i) {
|
||||
auto pass = static_cast<RenderPassType>(i);
|
||||
for (size_t j = 0; j < Constants::NumDrawTypes(); ++j) {
|
||||
auto draw = static_cast<DrawType>(j);
|
||||
auto &mappings =
|
||||
m_resource_mappings[Constants::GetPipelineIndex(pass, draw)];
|
||||
nvrhi::GraphicsPipelineDesc pipeline_desc{};
|
||||
auto pixel_shader = pixel_shaders[static_cast<size_t>(pass)];
|
||||
if (!pixel_shader) {
|
||||
if (pass == RenderPassType::ShadowMap) {
|
||||
mappings.Add(MaResourceMapping::PushConstants<PushConstantsShadow>(1))
|
||||
.Add(texture_mapping_shadow)
|
||||
.Add(sampler_mapping_shadow);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
pipeline_desc.setPixelShader(pixel_shader)
|
||||
.setPrimType(nvrhi::PrimitiveType::TriangleList);
|
||||
|
||||
for (const auto &mapping : texture_mappings) mappings.Add(mapping);
|
||||
for (const auto &mapping : sampler_mappings) mappings.Add(mapping);
|
||||
|
||||
nvrhi::IFramebuffer *framebuffer;
|
||||
pipeline_desc.setInputLayout(
|
||||
m_renderer->m_input_layout[static_cast<size_t>(draw)]);
|
||||
switch (pass) {
|
||||
case RenderPassType::Deferred:
|
||||
case RenderPassType::Forward:
|
||||
pipeline_desc.setVertexShader(
|
||||
m_renderer->m_vertex_shader[static_cast<size_t>(draw)]);
|
||||
break;
|
||||
case RenderPassType::CubeMap:
|
||||
pipeline_desc.setVertexShader(
|
||||
m_renderer->m_vertex_shader_cubemap[static_cast<size_t>(draw)]);
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
switch (pass) {
|
||||
case RenderPassType::Deferred:
|
||||
case RenderPassType::Forward:
|
||||
mappings.Add(MaResourceMapping::PushConstants<PushConstantsDraw>(1))
|
||||
.Add(MaResourceMapping::ConstantBuffer(
|
||||
0, "cb_draw_constants_static"));
|
||||
break;
|
||||
case RenderPassType::CubeMap:
|
||||
mappings
|
||||
.Add(MaResourceMapping::PushConstants<PushConstantsCubemap>(1))
|
||||
.Add(MaResourceMapping::ConstantBuffer(
|
||||
0, "cb_draw_constants_static_cubemap"));
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
switch (pass) {
|
||||
case RenderPassType::Forward:
|
||||
mappings
|
||||
.Add(MaResourceMapping::VolatileConstantBuffer(
|
||||
2, "gbuffer_lighting_constants"))
|
||||
.Add(MaResourceMapping::ConstantBuffer(
|
||||
11, "shadow_projection_buffer"))
|
||||
.Add(MaResourceMapping::Texture_SRV(8, "env_dynamic_diffuse"))
|
||||
.Add(MaResourceMapping::Texture_SRV(9, "env_dynamic_specular"))
|
||||
.Add(MaResourceMapping::Texture_SRV(10, "env_brdf_lut"))
|
||||
.Add(MaResourceMapping::Texture_SRV(11, "shadow_depths"))
|
||||
.Add(MaResourceMapping::Texture_SRV(12, "gbuffer_depth"))
|
||||
.Add(MaResourceMapping::Texture_SRV(14, "sky_aerial_lut"))
|
||||
.Add(MaResourceMapping::Texture_SRV(16, "forwardplus_index_grid_transparent"))
|
||||
.Add(MaResourceMapping::StructuredBuffer_SRV(17, "forwardplus_index_buffer_transparent"))
|
||||
.Add(MaResourceMapping::StructuredBuffer_SRV(18, "forwardplus_light_buffer"))
|
||||
.Add(MaResourceMapping::Sampler(8, "sampler_linear_wrap"))
|
||||
.Add(MaResourceMapping::Sampler(11, "shadow_sampler_comp"))
|
||||
.Add(MaResourceMapping::Sampler(13, "sampler_linear_clamp_v_repeat_h"));
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
switch (pass) {
|
||||
case RenderPassType::Deferred:
|
||||
framebuffer = m_renderer->m_gbuffer->m_framebuffer;
|
||||
break;
|
||||
case RenderPassType::Forward:
|
||||
framebuffer = m_renderer->m_framebuffer_forward;
|
||||
break;
|
||||
case RenderPassType::CubeMap:
|
||||
framebuffer = m_renderer->m_gbuffer_cube->m_framebuffer;
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
switch (pass) {
|
||||
case RenderPassType::Deferred:
|
||||
case RenderPassType::CubeMap:
|
||||
pipeline_desc.setRenderState(
|
||||
nvrhi::RenderState()
|
||||
.setDepthStencilState(
|
||||
nvrhi::DepthStencilState()
|
||||
.enableDepthTest()
|
||||
.enableDepthWrite()
|
||||
.disableStencil()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
|
||||
.setRasterState(nvrhi::RasterState()
|
||||
.setFillSolid()
|
||||
.enableDepthClip()
|
||||
.disableScissor()
|
||||
.setCullFront())
|
||||
.setBlendState(nvrhi::BlendState().setRenderTarget(
|
||||
0, nvrhi::BlendState::RenderTarget().disableBlend())));
|
||||
break;
|
||||
case RenderPassType::Forward:
|
||||
pipeline_desc.setRenderState(
|
||||
nvrhi::RenderState()
|
||||
.setDepthStencilState(
|
||||
nvrhi::DepthStencilState()
|
||||
.enableDepthTest()
|
||||
.disableDepthWrite()
|
||||
.disableStencil()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
|
||||
.setRasterState(nvrhi::RasterState()
|
||||
.setFillSolid()
|
||||
.enableDepthClip()
|
||||
.disableScissor()
|
||||
.setCullFront())
|
||||
.setBlendState(
|
||||
nvrhi::BlendState()
|
||||
.setRenderTarget(
|
||||
0, nvrhi::BlendState::RenderTarget()
|
||||
.enableBlend()
|
||||
.setBlendOp(nvrhi::BlendOp::Add)
|
||||
.setSrcBlend(nvrhi::BlendFactor::One)
|
||||
.setDestBlend(
|
||||
nvrhi::BlendFactor::OneMinusSrcAlpha))
|
||||
.setRenderTarget(1, nvrhi::BlendState::RenderTarget()
|
||||
.disableBlend())));
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
nvrhi::BindingLayoutDesc bl_desc{};
|
||||
bl_desc.visibility = nvrhi::ShaderType::All;
|
||||
// for (const auto &bl : pipeline_desc.bindingLayouts) {
|
||||
// bl_desc.visibility = bl_desc.visibility | bl->getDesc()->visibility;
|
||||
// for (const auto &element : bl->getDesc()->bindings) {
|
||||
// bl_desc.addItem(element);
|
||||
// }
|
||||
// }
|
||||
|
||||
mappings.ToBindingLayout(bl_desc);
|
||||
|
||||
pipeline_desc.bindingLayouts.resize(0);
|
||||
pipeline_desc.addBindingLayout(
|
||||
m_renderer->GetBackend()->GetDevice()->createBindingLayout(bl_desc));
|
||||
|
||||
m_pipelines[Constants::GetPipelineIndex(pass, draw)] =
|
||||
m_renderer->GetBackend()->GetDevice()->createGraphicsPipeline(
|
||||
pipeline_desc, framebuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NvRenderer::InitMaterials() {
|
||||
m_drawconstant_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(sizeof(DrawConstants),
|
||||
"Draw Constants")
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
m_cubedrawconstant_buffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(sizeof(CubeDrawConstants),
|
||||
"Draw Data")
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
RegisterResource(true, "cb_draw_constants_static", m_drawconstant_buffer,
|
||||
nvrhi::ResourceType::ConstantBuffer);
|
||||
RegisterResource(true, "cb_draw_constants_static_cubemap",
|
||||
m_cubedrawconstant_buffer,
|
||||
nvrhi::ResourceType::ConstantBuffer);
|
||||
|
||||
{
|
||||
nvrhi::CommandListHandle command_list =
|
||||
GetBackend()->GetDevice()->createCommandList();
|
||||
command_list->open();
|
||||
CubeDrawConstants data;
|
||||
data.m_face_projection = glm::perspectiveFovRH_ZO(
|
||||
M_PI_2, 1., 1., .1,
|
||||
static_cast<double>(Global.reflectiontune.range_instances));
|
||||
command_list->writeBuffer(m_cubedrawconstant_buffer, &data, sizeof(data));
|
||||
command_list->close();
|
||||
GetBackend()->GetDevice()->executeCommandList(command_list);
|
||||
}
|
||||
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_drawconstant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::PushConstants(
|
||||
1, sizeof(PushConstantsDraw))),
|
||||
m_binding_layout_drawconstants, m_binding_set_drawconstants);
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
0, m_cubedrawconstant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::PushConstants(
|
||||
1, sizeof(PushConstantsCubemap))),
|
||||
m_binding_layout_cubedrawconstants, m_binding_set_cubedrawconstants);
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
|
||||
nvrhi::BindingSetDesc().addItem(
|
||||
nvrhi::BindingSetItem::PushConstants(1, sizeof(PushConstantsShadow))),
|
||||
m_binding_layout_shadowdrawconstants, m_binding_set_shadowdrawconstants);
|
||||
m_binding_layout_forward = GetBackend()->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::Pixel)
|
||||
.addItem(nvrhi::BindingLayoutItem::VolatileConstantBuffer(2))
|
||||
.addItem(nvrhi::BindingLayoutItem::ConstantBuffer(11))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(8))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(9))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(10))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(11))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(12))
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(14))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(8))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(11))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(13)));
|
||||
for (int i = 0; i < m_binding_set_forward.size(); ++i) {
|
||||
m_binding_set_forward[i] = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
2, m_gbuffer_blit->m_draw_constants))
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(
|
||||
11, m_shadow_map->m_projection_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
8, m_environment->m_dynamic_envmap_diffuse[0]))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
9, m_environment->m_dynamic_envmap_specular[0]))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
10, m_environment->m_brdf_lut))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
11, m_gbuffer_shadow->m_gbuffer_depth))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
12, m_gbuffer->m_gbuffer_depth))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
14, m_sky->m_aerial_lut->m_lut))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
8, m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc().setAllFilters(true))))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
11, m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setReductionType(
|
||||
nvrhi::SamplerReductionType::Comparison)
|
||||
.setComparisonFunc(nvrhi::ComparisonFunc::Greater)
|
||||
.setAllAddressModes(
|
||||
nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setAllFilters(true))))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
13, m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(
|
||||
nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setAllFilters(true)))),
|
||||
m_binding_layout_forward);
|
||||
}
|
||||
|
||||
m_vertex_shader[static_cast<size_t>(DrawType::Model)] =
|
||||
m_backend->CreateShader("default_vertex", nvrhi::ShaderType::Vertex);
|
||||
m_vertex_shader[static_cast<size_t>(DrawType::InstancedModel)] =
|
||||
m_backend->CreateShader("instanced_vertex", nvrhi::ShaderType::Vertex);
|
||||
m_vertex_shader_shadow[static_cast<size_t>(DrawType::Model)] =
|
||||
m_backend->CreateShader("shadow_vertex", nvrhi::ShaderType::Vertex);
|
||||
m_vertex_shader_shadow[static_cast<size_t>(DrawType::InstancedModel)] =
|
||||
m_backend->CreateShader("instanced_shadow_vertex",
|
||||
nvrhi::ShaderType::Vertex);
|
||||
m_vertex_shader_cubemap[static_cast<size_t>(DrawType::Model)] =
|
||||
m_backend->CreateShader("cubemap_vertex", nvrhi::ShaderType::Vertex);
|
||||
m_vertex_shader_cubemap[static_cast<size_t>(DrawType::InstancedModel)] =
|
||||
m_backend->CreateShader("instanced_cubemap_vertex",
|
||||
nvrhi::ShaderType::Vertex);
|
||||
m_pixel_shader_shadow_masked =
|
||||
m_backend->CreateShader("shadow_masked", nvrhi::ShaderType::Pixel);
|
||||
|
||||
nvrhi::VertexAttributeDesc desc[]{
|
||||
nvrhi::VertexAttributeDesc()
|
||||
.setBufferIndex(0)
|
||||
.setElementStride(sizeof(gfx::basic_vertex))
|
||||
.setFormat(nvrhi::Format::RGB32_FLOAT)
|
||||
.setName("Position")
|
||||
.setIsInstanced(false)
|
||||
.setOffset(offsetof(gfx::basic_vertex, position)),
|
||||
nvrhi::VertexAttributeDesc()
|
||||
.setBufferIndex(0)
|
||||
.setElementStride(sizeof(gfx::basic_vertex))
|
||||
.setFormat(nvrhi::Format::RGB32_FLOAT)
|
||||
.setName("Normal")
|
||||
.setIsInstanced(false)
|
||||
.setOffset(offsetof(gfx::basic_vertex, normal)),
|
||||
nvrhi::VertexAttributeDesc()
|
||||
.setBufferIndex(0)
|
||||
.setElementStride(sizeof(gfx::basic_vertex))
|
||||
.setFormat(nvrhi::Format::RG32_FLOAT)
|
||||
.setName("TexCoord")
|
||||
.setIsInstanced(false)
|
||||
.setOffset(offsetof(gfx::basic_vertex, texture)),
|
||||
nvrhi::VertexAttributeDesc()
|
||||
.setBufferIndex(0)
|
||||
.setElementStride(sizeof(gfx::basic_vertex))
|
||||
.setFormat(nvrhi::Format::RGBA32_FLOAT)
|
||||
.setName("Tangent")
|
||||
.setIsInstanced(false)
|
||||
.setOffset(offsetof(gfx::basic_vertex, tangent)),
|
||||
nvrhi::VertexAttributeDesc()
|
||||
.setBufferIndex(1)
|
||||
.setElementStride(sizeof(glm::mat3x4))
|
||||
.setFormat(nvrhi::Format::RGBA32_FLOAT)
|
||||
.setName("InstanceTransform")
|
||||
.setIsInstanced(true)
|
||||
.setArraySize(3)
|
||||
.setOffset(0)};
|
||||
m_input_layout[static_cast<size_t>(DrawType::Model)] =
|
||||
m_backend->GetDevice()->createInputLayout(
|
||||
desc, std::size(desc) - 1,
|
||||
m_vertex_shader[static_cast<size_t>(DrawType::Model)]);
|
||||
m_input_layout[static_cast<size_t>(DrawType::InstancedModel)] =
|
||||
m_backend->GetDevice()->createInputLayout(
|
||||
desc, std::size(desc),
|
||||
m_vertex_shader[static_cast<size_t>(DrawType::InstancedModel)]);
|
||||
|
||||
{
|
||||
m_binding_layout_shadow_masked =
|
||||
m_backend->GetDevice()->createBindingLayout(
|
||||
nvrhi::BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::Pixel)
|
||||
.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(0)));
|
||||
for (int i = 0; i < Constants::NumDrawTypes(); ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
DrawType draw = static_cast<DrawType>(i);
|
||||
bool masked = j;
|
||||
nvrhi::BindingLayoutDesc desc =
|
||||
*m_binding_layout_shadowdrawconstants->getDesc();
|
||||
desc.visibility = nvrhi::ShaderType::All;
|
||||
if (masked)
|
||||
desc.addItem(nvrhi::BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(nvrhi::BindingLayoutItem::Sampler(0));
|
||||
auto binding_layout =
|
||||
GetBackend()->GetDevice()->createBindingLayout(desc);
|
||||
auto pipeline_desc =
|
||||
nvrhi::GraphicsPipelineDesc()
|
||||
.setRenderState(
|
||||
nvrhi::RenderState()
|
||||
.setDepthStencilState(
|
||||
nvrhi::DepthStencilState()
|
||||
.enableDepthTest()
|
||||
.enableDepthWrite()
|
||||
.disableStencil()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
|
||||
.setRasterState(nvrhi::RasterState()
|
||||
.setFillSolid()
|
||||
.disableDepthClip()
|
||||
.disableScissor()
|
||||
.setCullBack()
|
||||
.setDepthBias(1)
|
||||
.setSlopeScaleDepthBias(-4.f))
|
||||
.setBlendState(nvrhi::BlendState().setRenderTarget(
|
||||
0,
|
||||
nvrhi::BlendState::RenderTarget().disableBlend())))
|
||||
.setVertexShader(
|
||||
m_vertex_shader_shadow[static_cast<size_t>(draw)])
|
||||
.setInputLayout(m_input_layout[static_cast<size_t>(draw)]);
|
||||
if (masked) {
|
||||
pipeline_desc.setPixelShader(m_pixel_shader_shadow_masked);
|
||||
}
|
||||
pipeline_desc.addBindingLayout(binding_layout);
|
||||
|
||||
m_pso_shadow[Constants::GetDefaultShadowPipelineIndex(draw, masked)] =
|
||||
GetBackend()->GetDevice()->createGraphicsPipeline(
|
||||
pipeline_desc, m_gbuffer_shadow->m_framebuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto vs_line =
|
||||
m_backend->CreateShader("vtx_line", nvrhi::ShaderType::Vertex);
|
||||
auto gs_line =
|
||||
m_backend->CreateShader("geo_line", nvrhi::ShaderType::Geometry);
|
||||
auto ps_line =
|
||||
m_backend->CreateShader("pix_line", nvrhi::ShaderType::Pixel);
|
||||
nvrhi::BindingLayoutHandle binding_layout_line;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
GetBackend()->GetDevice(), nvrhi::ShaderType::AllGraphics, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_drawconstant_buffer))
|
||||
.addItem(nvrhi::BindingSetItem::PushConstants(
|
||||
1, sizeof(PushConstantsLine))),
|
||||
binding_layout_line, m_binding_set_line);
|
||||
|
||||
m_pso_line = GetBackend()->GetDevice()->createGraphicsPipeline(
|
||||
nvrhi::GraphicsPipelineDesc()
|
||||
.setRenderState(
|
||||
nvrhi::RenderState()
|
||||
.setDepthStencilState(
|
||||
nvrhi::DepthStencilState()
|
||||
.enableDepthTest()
|
||||
.enableDepthWrite()
|
||||
.disableStencil()
|
||||
.setDepthFunc(nvrhi::ComparisonFunc::Greater))
|
||||
.setRasterState(nvrhi::RasterState()
|
||||
.setFillSolid()
|
||||
.enableDepthClip()
|
||||
.disableScissor()
|
||||
.setCullFront())
|
||||
.setBlendState(nvrhi::BlendState().setRenderTarget(
|
||||
0, nvrhi::BlendState::RenderTarget().disableBlend())))
|
||||
.setPrimType(nvrhi::PrimitiveType::LineList)
|
||||
.setVertexShader(vs_line)
|
||||
.setGeometryShader(gs_line)
|
||||
.setPixelShader(ps_line)
|
||||
.setInputLayout(
|
||||
m_input_layout[static_cast<size_t>(DrawType::Model)])
|
||||
.addBindingLayout(binding_layout_line),
|
||||
m_gbuffer->m_framebuffer);
|
||||
}
|
||||
|
||||
auto config = YAML::LoadFile("shaders/project.manul");
|
||||
for (const auto item : config["shaders"]["materials"]) {
|
||||
auto material =
|
||||
std::make_shared<MaterialTemplate>(item.first.as<std::string>(), this);
|
||||
m_material_templates[material->m_name] = material;
|
||||
material->Init(item.second);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
7
betterRenderer/renderer/source/nvmaterial.h
Normal file
7
betterRenderer/renderer/source/nvmaterial.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
struct NvMaterial {
|
||||
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
#include "nvmaterial.h"
|
||||
2008
betterRenderer/renderer/source/nvrenderer.cpp
Normal file
2008
betterRenderer/renderer/source/nvrenderer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
413
betterRenderer/renderer/source/nvrenderer_d3d12.cpp
Normal file
413
betterRenderer/renderer/source/nvrenderer_d3d12.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
#include "nvrenderer_d3d12.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Logs.h"
|
||||
#include "config.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
|
||||
#include <GLFW/glfw3native.h>
|
||||
#include <nvrhi/validation.h>
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
#define HR_RETURN(op) \
|
||||
hr = (HRESULT)(op); \
|
||||
if (FAILED(hr)) { \
|
||||
ErrorLog(fmt::format(#op " returned HRESULT={}", hr)); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
bool NvRenderer::InitForD3D12(GLFWwindow *Window) {
|
||||
// Create d3d device
|
||||
std::shared_ptr<NvRendererBackend_D3D12> Backend =
|
||||
std::make_shared<NvRendererBackend_D3D12>();
|
||||
m_backend = Backend;
|
||||
|
||||
Backend->m_renderer = this;
|
||||
Backend->m_hwnd = glfwGetWin32Window(Window);
|
||||
|
||||
if (!Backend->CreateDevice()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Backend->CreateSwapChain(Window)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Backend->CreateRenderTarget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string NvRendererBackend_D3D12::LoadShaderBlob(
|
||||
const std::string &filename, const std::string &entrypoint) {
|
||||
std::string path = "shaders/shaders_dxil/" + filename;
|
||||
if (entrypoint != "main") path += "_" + entrypoint;
|
||||
path += ".bin";
|
||||
std::ifstream ifs(path, std::ios::binary | std::ios::ate);
|
||||
std::string blob(ifs.tellg(), '\0');
|
||||
ifs.seekg(0, std::ios_base::beg);
|
||||
ifs.read(blob.data(), blob.size());
|
||||
return blob;
|
||||
}
|
||||
|
||||
int NvRendererBackend_D3D12::GetCurrentBackBufferIndex() {
|
||||
return m_swapchain->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
int NvRendererBackend_D3D12::GetBackBufferCount() {
|
||||
return m_swapchain_desc.BufferCount;
|
||||
}
|
||||
|
||||
nvrhi::TextureHandle NvRendererBackend_D3D12::GetBackBuffer(int index) {
|
||||
return m_RhiSwapChainBuffers[index];
|
||||
}
|
||||
|
||||
bool NvRendererBackend_D3D12::BeginFrame() {
|
||||
WaitForSingleObject(
|
||||
m_FrameFenceEvents[m_swapchain->GetCurrentBackBufferIndex()], INFINITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NvRendererBackend_D3D12::SetHDRMetaData(float MaxOutputNits /*=1000.0f*/,
|
||||
float MinOutputNits /*=0.001f*/,
|
||||
float MaxCLL /*=2000.0f*/,
|
||||
float MaxFALL /*=500.0f*/) {
|
||||
HRESULT hr;
|
||||
struct DisplayChromaticities {
|
||||
float RedX;
|
||||
float RedY;
|
||||
float GreenX;
|
||||
float GreenY;
|
||||
float BlueX;
|
||||
float BlueY;
|
||||
float WhiteX;
|
||||
float WhiteY;
|
||||
};
|
||||
|
||||
if (!m_swapchain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto hdr_config =
|
||||
NvRenderer::Config()->m_display.GetHDRConfig(IsHDRDisplay());
|
||||
|
||||
// Clean the hdr metadata if the display doesn't support HDR
|
||||
// if (!m_hdrSupport) {
|
||||
// ThrowIfFailed(
|
||||
// m_swapChain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_NONE, 0, nullptr));
|
||||
// return;
|
||||
//}
|
||||
|
||||
static const DisplayChromaticities DisplayChromaticityList[] = {
|
||||
{0.64000f, 0.33000f, 0.30000f, 0.60000f, 0.15000f, 0.06000f, 0.31270f,
|
||||
0.32900f}, // Display Gamut Rec709
|
||||
{0.70800f, 0.29200f, 0.17000f, 0.79700f, 0.13100f, 0.04600f, 0.31270f,
|
||||
0.32900f}, // Display Gamut Rec2020
|
||||
};
|
||||
|
||||
// Select the chromaticity based on HDR format of the DWM.
|
||||
int selectedChroma = 1;
|
||||
// if (true//m_currentSwapChainBitDepth == _16 &&
|
||||
// m_currentSwapChainColorSpace ==
|
||||
// DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709) {
|
||||
// selectedChroma = 0;
|
||||
// } else if (m_currentSwapChainBitDepth == _10 &&
|
||||
// m_currentSwapChainColorSpace ==
|
||||
// DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
|
||||
// selectedChroma = 1;
|
||||
// } else {
|
||||
// // Reset the metadata since this is not a supported HDR format.
|
||||
// HR_RETURN(
|
||||
// m_swapChain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_NONE, 0,
|
||||
// nullptr));
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Set HDR meta data
|
||||
const DisplayChromaticities &Chroma = DisplayChromaticityList[selectedChroma];
|
||||
DXGI_HDR_METADATA_HDR10 HDR10MetaData = {};
|
||||
HDR10MetaData.RedPrimary[0] = static_cast<UINT16>(Chroma.RedX * 50000.0f);
|
||||
HDR10MetaData.RedPrimary[1] = static_cast<UINT16>(Chroma.RedY * 50000.0f);
|
||||
HDR10MetaData.GreenPrimary[0] = static_cast<UINT16>(Chroma.GreenX * 50000.0f);
|
||||
HDR10MetaData.GreenPrimary[1] = static_cast<UINT16>(Chroma.GreenY * 50000.0f);
|
||||
HDR10MetaData.BluePrimary[0] = static_cast<UINT16>(Chroma.BlueX * 50000.0f);
|
||||
HDR10MetaData.BluePrimary[1] = static_cast<UINT16>(Chroma.BlueY * 50000.0f);
|
||||
HDR10MetaData.WhitePoint[0] = static_cast<UINT16>(Chroma.WhiteX * 50000.0f);
|
||||
HDR10MetaData.WhitePoint[1] = static_cast<UINT16>(Chroma.WhiteY * 50000.0f);
|
||||
HDR10MetaData.MaxMasteringLuminance =
|
||||
static_cast<UINT>(hdr_config.GetMaxOutputNits() * 10000.0f);
|
||||
HDR10MetaData.MinMasteringLuminance =
|
||||
static_cast<UINT>(hdr_config.GetMinOutputNits() * 10000.0f);
|
||||
HDR10MetaData.MaxContentLightLevel = static_cast<UINT16>(MaxCLL);
|
||||
HDR10MetaData.MaxFrameAverageLightLevel = static_cast<UINT16>(MaxFALL);
|
||||
if (IsHDRDisplay()) {
|
||||
HR_RETURN(m_swapchain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10,
|
||||
sizeof(DXGI_HDR_METADATA_HDR10),
|
||||
&HDR10MetaData))
|
||||
HR_RETURN(
|
||||
m_swapchain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020))
|
||||
} else {
|
||||
HR_RETURN(
|
||||
m_swapchain->SetColorSpace1(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709))
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NvRendererBackend_D3D12::Present() {
|
||||
// if (!m_windowVisible)
|
||||
// return;
|
||||
|
||||
auto bufferIndex = m_swapchain->GetCurrentBackBufferIndex();
|
||||
|
||||
UINT presentFlags = 0;
|
||||
if (false /*!vsync_enabled*/ && !Global.bFullScreen && m_TearingSupported)
|
||||
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
|
||||
|
||||
m_swapchain->Present(1, presentFlags);
|
||||
|
||||
m_FrameFence->SetEventOnCompletion(m_FrameCount,
|
||||
m_FrameFenceEvents[bufferIndex]);
|
||||
m_graphics_queue->Signal(m_FrameFence, m_FrameCount);
|
||||
m_FrameCount++;
|
||||
}
|
||||
|
||||
bool NvRendererBackend_D3D12::IsHDRDisplay() const {
|
||||
return m_output_desc.BitsPerColor > 8 &&
|
||||
!NvRenderer::Config()->m_display.m_disable_hdr;
|
||||
}
|
||||
|
||||
bool NvRendererBackend_D3D12::CreateDevice() {
|
||||
HRESULT hr;
|
||||
HR_RETURN(
|
||||
CreateDXGIFactory2(Global.gfx_gldebug ? DXGI_CREATE_FACTORY_DEBUG : 0,
|
||||
IID_PPV_ARGS(&m_dxgi_factory)))
|
||||
|
||||
if (Global.gfx_gldebug) {
|
||||
nvrhi::RefCountPtr<ID3D12Debug> pDebug;
|
||||
HR_RETURN(D3D12GetDebugInterface(IID_PPV_ARGS(&pDebug)))
|
||||
|
||||
pDebug->EnableDebugLayer();
|
||||
}
|
||||
|
||||
WriteLog("Available graphics adapters:");
|
||||
for (int i = 0;; ++i) {
|
||||
nvrhi::RefCountPtr<IDXGIAdapter> adapter = nullptr;
|
||||
if (FAILED(m_dxgi_factory->EnumAdapters(i, &adapter))) break;
|
||||
DXGI_ADAPTER_DESC desc{};
|
||||
HR_RETURN(adapter->GetDesc(&desc));
|
||||
std::string str_desc{};
|
||||
str_desc.resize(
|
||||
wcstombs(nullptr, desc.Description, std::size(desc.Description)));
|
||||
wcstombs(str_desc.data(), desc.Description, str_desc.size());
|
||||
WriteLog(fmt::format("- adapter{}: {}", i, str_desc));
|
||||
}
|
||||
|
||||
if (FAILED(m_dxgi_factory->EnumAdapters(
|
||||
NvRenderer::Config()->m_display.m_adapter_index, &m_dxgi_adapter))) {
|
||||
ErrorLog(fmt::format("Invalid adapter index: {}",
|
||||
NvRenderer::Config()->m_display.m_adapter_index));
|
||||
return false;
|
||||
}
|
||||
|
||||
nvrhi::RefCountPtr<IDXGIOutput> output;
|
||||
nvrhi::RefCountPtr<IDXGIOutput6> output6;
|
||||
if (FAILED(m_dxgi_adapter->EnumOutputs(
|
||||
NvRenderer::Config()->m_display.m_output_index, &output))) {
|
||||
ErrorLog(fmt::format("Invalid output index: {}",
|
||||
NvRenderer::Config()->m_display.m_output_index));
|
||||
}
|
||||
|
||||
HR_RETURN(output->QueryInterface<IDXGIOutput6>(&output6));
|
||||
HR_RETURN(output6->GetDesc1(&m_output_desc));
|
||||
|
||||
// {
|
||||
// DXGI_ADAPTER_DESC aDesc;
|
||||
// m_DxgiAdapter->GetDesc(&aDesc);
|
||||
//
|
||||
// m_RendererString = GetAdapterName(aDesc);
|
||||
// m_IsNvidia = IsNvDeviceID(aDesc.VendorId);
|
||||
// }
|
||||
|
||||
HR_RETURN(D3D12CreateDevice(m_dxgi_adapter, D3D_FEATURE_LEVEL_12_0,
|
||||
IID_PPV_ARGS(&m_d3d_device)))
|
||||
|
||||
if (Global.gfx_gldebug) {
|
||||
nvrhi::RefCountPtr<ID3D12InfoQueue> pInfoQueue;
|
||||
m_d3d_device->QueryInterface(&pInfoQueue);
|
||||
|
||||
if (pInfoQueue) {
|
||||
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
|
||||
pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
|
||||
|
||||
D3D12_MESSAGE_ID disableMessageIDs[] = {
|
||||
D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
|
||||
D3D12_MESSAGE_ID_COMMAND_LIST_STATIC_DESCRIPTOR_RESOURCE_DIMENSION_MISMATCH, // descriptor validation doesn't understand acceleration structures
|
||||
};
|
||||
|
||||
D3D12_INFO_QUEUE_FILTER filter = {};
|
||||
filter.DenyList.pIDList = disableMessageIDs;
|
||||
filter.DenyList.NumIDs =
|
||||
sizeof(disableMessageIDs) / sizeof(disableMessageIDs[0]);
|
||||
pInfoQueue->AddStorageFilterEntries(&filter);
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc;
|
||||
ZeroMemory(&queueDesc, sizeof(queueDesc));
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.NodeMask = 1;
|
||||
HR_RETURN(m_d3d_device->CreateCommandQueue(&queueDesc,
|
||||
IID_PPV_ARGS(&m_graphics_queue)))
|
||||
m_graphics_queue->SetName(L"Graphics Queue");
|
||||
|
||||
if (true) {
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
|
||||
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
|
||||
HR_RETURN(m_d3d_device->CreateCommandQueue(&queueDesc,
|
||||
IID_PPV_ARGS(&m_compute_queue)))
|
||||
m_compute_queue->SetName(L"Compute Queue");
|
||||
}
|
||||
|
||||
if (true) {
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COPY;
|
||||
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
|
||||
HR_RETURN(m_d3d_device->CreateCommandQueue(&queueDesc,
|
||||
IID_PPV_ARGS(&m_copy_queue)))
|
||||
m_copy_queue->SetName(L"Copy Queue");
|
||||
}
|
||||
|
||||
nvrhi::d3d12::DeviceDesc deviceDesc;
|
||||
deviceDesc.errorCB = GetMessageCallback();
|
||||
deviceDesc.pDevice = m_d3d_device;
|
||||
deviceDesc.pGraphicsCommandQueue = m_graphics_queue;
|
||||
deviceDesc.pComputeCommandQueue = m_compute_queue;
|
||||
deviceDesc.pCopyCommandQueue = m_copy_queue;
|
||||
|
||||
m_device = nvrhi::d3d12::createDevice(deviceDesc);
|
||||
|
||||
if (Global.gfx_gldebug) {
|
||||
m_device = nvrhi::validation::createValidationLayer(m_device);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NvRendererBackend_D3D12::CreateSwapChain(GLFWwindow *window) {
|
||||
HRESULT hr;
|
||||
ZeroMemory(&m_swapchain_desc, sizeof(m_swapchain_desc));
|
||||
|
||||
if (Global.bFullScreen) {
|
||||
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetWindowMonitor(window));
|
||||
m_swapchain_desc.Width = mode->width;
|
||||
m_swapchain_desc.Height = mode->height;
|
||||
} else {
|
||||
int fbWidth = 0, fbHeight = 0;
|
||||
glfwGetFramebufferSize(window, &fbWidth, &fbHeight);
|
||||
m_swapchain_desc.Width = fbWidth;
|
||||
m_swapchain_desc.Height = fbHeight;
|
||||
}
|
||||
|
||||
m_swapchain_desc.BufferCount = 3;
|
||||
m_swapchain_desc.BufferUsage =
|
||||
DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
m_swapchain_desc.SampleDesc.Count = 1;
|
||||
m_swapchain_desc.SampleDesc.Quality = 0;
|
||||
m_swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
m_swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
m_swapchain_desc.Format = IsHDRDisplay() ? DXGI_FORMAT_R10G10B10A2_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
nvrhi::RefCountPtr<IDXGIFactory5> pDxgiFactory5;
|
||||
if (SUCCEEDED(m_dxgi_factory->QueryInterface(IID_PPV_ARGS(&pDxgiFactory5)))) {
|
||||
BOOL supported = 0;
|
||||
if (SUCCEEDED(pDxgiFactory5->CheckFeatureSupport(
|
||||
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &supported, sizeof(supported))))
|
||||
m_TearingSupported = (supported != 0);
|
||||
}
|
||||
|
||||
if (m_TearingSupported) {
|
||||
m_swapchain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
}
|
||||
|
||||
m_fullscreen_desc = {};
|
||||
m_fullscreen_desc.RefreshRate.Numerator = 0;
|
||||
m_fullscreen_desc.RefreshRate.Denominator = 1;
|
||||
m_fullscreen_desc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
|
||||
m_fullscreen_desc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
||||
m_fullscreen_desc.Windowed = !Global.bFullScreen;
|
||||
|
||||
nvrhi::RefCountPtr<IDXGISwapChain1> pSwapChain1;
|
||||
HR_RETURN(m_dxgi_factory->CreateSwapChainForHwnd(
|
||||
m_graphics_queue, m_hwnd, &m_swapchain_desc, &m_fullscreen_desc, nullptr,
|
||||
&pSwapChain1))
|
||||
|
||||
HR_RETURN(pSwapChain1->QueryInterface(IID_PPV_ARGS(&m_swapchain)))
|
||||
|
||||
if (!CreateRenderTarget()) return false;
|
||||
|
||||
HR_RETURN(m_d3d_device->CreateFence(0, D3D12_FENCE_FLAG_NONE,
|
||||
IID_PPV_ARGS(&m_FrameFence)));
|
||||
|
||||
for (UINT bufferIndex = 0; bufferIndex < m_swapchain_desc.BufferCount;
|
||||
bufferIndex++) {
|
||||
m_FrameFenceEvents.push_back(CreateEvent(nullptr, false, true, nullptr));
|
||||
}
|
||||
if (!SetHDRMetaData()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NvRendererBackend_D3D12::CreateRenderTarget() {
|
||||
m_SwapChainBuffers.resize(m_swapchain_desc.BufferCount);
|
||||
m_RhiSwapChainBuffers.resize(m_swapchain_desc.BufferCount);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
for (UINT n = 0; n < m_swapchain_desc.BufferCount; n++) {
|
||||
HR_RETURN(m_swapchain->GetBuffer(n, IID_PPV_ARGS(&m_SwapChainBuffers[n])))
|
||||
|
||||
nvrhi::TextureDesc textureDesc;
|
||||
textureDesc.width = m_swapchain_desc.Width;
|
||||
textureDesc.height = m_swapchain_desc.Height;
|
||||
textureDesc.sampleCount = m_swapchain_desc.SampleDesc.Count;
|
||||
textureDesc.sampleQuality = m_swapchain_desc.SampleDesc.Quality;
|
||||
textureDesc.format = IsHDRDisplay() ? nvrhi::Format::R10G10B10A2_UNORM : nvrhi::Format::RGBA8_UNORM;
|
||||
textureDesc.debugName = "SwapChainBuffer";
|
||||
textureDesc.isRenderTarget = true;
|
||||
textureDesc.isUAV = false;
|
||||
textureDesc.initialState = nvrhi::ResourceStates::Present;
|
||||
textureDesc.keepInitialState = true;
|
||||
|
||||
m_RhiSwapChainBuffers[n] = m_device->createHandleForNativeTexture(
|
||||
nvrhi::ObjectTypes::D3D12_Resource,
|
||||
nvrhi::Object(m_SwapChainBuffers[n]), textureDesc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NvRendererBackend_D3D12::ReleaseRenderTarget() {
|
||||
// Make sure that all frames have finished rendering
|
||||
m_device->waitForIdle();
|
||||
|
||||
// Release all in-flight references to the render targets
|
||||
m_device->runGarbageCollection();
|
||||
|
||||
// Set the events so that WaitForSingleObject in OneFrame will not hang later
|
||||
for (auto e : m_FrameFenceEvents) SetEvent(e);
|
||||
|
||||
// Release the old buffers because ResizeBuffers requires that
|
||||
m_RhiSwapChainBuffers.clear();
|
||||
m_SwapChainBuffers.clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
72
betterRenderer/renderer/source/nvrenderer_d3d12.h
Normal file
72
betterRenderer/renderer/source/nvrenderer_d3d12.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
|
||||
#include <Windows.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <dxgidebug.h>
|
||||
#include <nvrhi/d3d12.h>
|
||||
|
||||
class NvRendererBackend_D3D12 : public NvRendererBackend {
|
||||
friend class NvRenderer;
|
||||
|
||||
virtual std::string LoadShaderBlob(const std::string &filename,
|
||||
const std::string &entrypoint) override;
|
||||
virtual int GetCurrentBackBufferIndex() override;
|
||||
virtual int GetBackBufferCount() override;
|
||||
virtual nvrhi::TextureHandle GetBackBuffer(int index) override;
|
||||
virtual bool BeginFrame() override;
|
||||
virtual void Present() override;
|
||||
virtual bool IsHDRDisplay() const override;
|
||||
virtual void UpdateWindowSize() override{}
|
||||
|
||||
bool SetHDRMetaData(float MaxOutputNits = 1000.0f,
|
||||
float MinOutputNits = 0.001f, float MaxCLL = 2000.0f,
|
||||
float MaxFALL = 500.0f);
|
||||
|
||||
// Window handle
|
||||
HWND m_hwnd;
|
||||
|
||||
// Direct3D resources
|
||||
nvrhi::RefCountPtr<ID3D12Device> m_d3d_device;
|
||||
nvrhi::RefCountPtr<ID3D12CommandQueue> m_graphics_queue;
|
||||
nvrhi::RefCountPtr<ID3D12CommandQueue> m_aux_graphics_queue;
|
||||
nvrhi::RefCountPtr<ID3D12CommandQueue> m_compute_queue;
|
||||
nvrhi::RefCountPtr<ID3D12CommandQueue> m_copy_queue;
|
||||
nvrhi::RefCountPtr<IDXGIFactory2> m_dxgi_factory;
|
||||
nvrhi::RefCountPtr<IDXGIAdapter> m_dxgi_adapter;
|
||||
nvrhi::RefCountPtr<IDXGISwapChain4> m_swapchain;
|
||||
DXGI_SWAP_CHAIN_DESC1 m_swapchain_desc;
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_fullscreen_desc;
|
||||
DXGI_OUTPUT_DESC1 m_output_desc;
|
||||
|
||||
bool m_TearingSupported = false;
|
||||
|
||||
std::vector<nvrhi::RefCountPtr<ID3D12Resource>> m_SwapChainBuffers;
|
||||
std::vector<nvrhi::TextureHandle> m_RhiSwapChainBuffers;
|
||||
nvrhi::RefCountPtr<ID3D12Fence> m_FrameFence;
|
||||
std::vector<HANDLE> m_FrameFenceEvents;
|
||||
|
||||
UINT64 m_FrameCount = 1;
|
||||
|
||||
// RHI resources
|
||||
|
||||
// Utility functions
|
||||
bool CreateDevice();
|
||||
bool CreateSwapChain(GLFWwindow *window);
|
||||
bool CreateRenderTarget();
|
||||
void ReleaseRenderTarget();
|
||||
virtual void LoadShaderBlobs() override;
|
||||
|
||||
nvrhi::DeviceHandle m_device;
|
||||
|
||||
public:
|
||||
nvrhi::IDevice *GetDevice() override { return m_device.Get(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
19
betterRenderer/renderer/source/nvrenderer_imgui.cpp
Normal file
19
betterRenderer/renderer/source/nvrenderer_imgui.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "nvrenderer_imgui.h"
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
bool NvImguiRenderer::Init() { return m_rhi.init(m_backend.get()); }
|
||||
|
||||
void NvImguiRenderer::Shutdown() {}
|
||||
|
||||
void NvImguiRenderer::BeginFrame() { m_rhi.beginFrame(); }
|
||||
|
||||
void NvImguiRenderer::Render() {
|
||||
m_rhi.render(m_backend->GetCurrentFramebuffer());
|
||||
}
|
||||
|
||||
NvImguiRenderer::NvImguiRenderer(NvRenderer *renderer)
|
||||
: m_backend(renderer->m_backend) {
|
||||
m_rhi.renderer = m_backend->GetDevice();
|
||||
m_rhi.textureManager = renderer->GetTextureManager();
|
||||
}
|
||||
17
betterRenderer/renderer/source/nvrenderer_imgui.h
Normal file
17
betterRenderer/renderer/source/nvrenderer_imgui.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "imgui_nvrhi.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
class NvImguiRenderer : public imgui_renderer {
|
||||
virtual bool Init() override;
|
||||
virtual void Shutdown() override;
|
||||
virtual void BeginFrame() override;
|
||||
virtual void Render() override;
|
||||
|
||||
std::shared_ptr<class NvRendererBackend> m_backend;
|
||||
ImGui_NVRHI m_rhi;
|
||||
|
||||
public:
|
||||
NvImguiRenderer(class NvRenderer *renderer);
|
||||
};
|
||||
1088
betterRenderer/renderer/source/nvrenderer_vulkan.cpp
Normal file
1088
betterRenderer/renderer/source/nvrenderer_vulkan.cpp
Normal file
File diff suppressed because it is too large
Load Diff
182
betterRenderer/renderer/source/nvrenderer_vulkan.h
Normal file
182
betterRenderer/renderer/source/nvrenderer_vulkan.h
Normal file
@@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
|
||||
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
||||
#include <nvrhi/vulkan.h>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
class NvRendererBackend_Vulkan : public NvRendererBackend {
|
||||
friend class NvRenderer;
|
||||
|
||||
nvrhi::IDevice *GetDevice() override;
|
||||
std::string LoadShaderBlob(const std::string &filename,
|
||||
const std::string &entrypoint) override;
|
||||
virtual int GetCurrentBackBufferIndex() override;
|
||||
virtual int GetBackBufferCount() override;
|
||||
virtual nvrhi::TextureHandle GetBackBuffer(int index) override;
|
||||
virtual bool BeginFrame() override;
|
||||
virtual void Present() override;
|
||||
virtual void UpdateWindowSize() override;
|
||||
bool IsHDRDisplay() const override;
|
||||
|
||||
// bool SetHDRMetaData(float MaxOutputNits = 1000.0f,
|
||||
// float MinOutputNits = 0.001f, float MaxCLL = 2000.0f,
|
||||
// float MaxFALL = 500.0f);
|
||||
|
||||
//// Window handle
|
||||
// HWND m_hwnd;
|
||||
//
|
||||
//// Direct3D resources
|
||||
// nvrhi::RefCountPtr<ID3D12Device> m_d3d_device;
|
||||
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_graphics_queue;
|
||||
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_aux_graphics_queue;
|
||||
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_compute_queue;
|
||||
// nvrhi::RefCountPtr<ID3D12CommandQueue> m_copy_queue;
|
||||
// nvrhi::RefCountPtr<IDXGIFactory2> m_dxgi_factory;
|
||||
// nvrhi::RefCountPtr<IDXGIAdapter> m_dxgi_adapter;
|
||||
// nvrhi::RefCountPtr<IDXGISwapChain4> m_swapchain;
|
||||
// DXGI_SWAP_CHAIN_DESC1 m_swapchain_desc;
|
||||
// DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_fullscreen_desc;
|
||||
// DXGI_OUTPUT_DESC1 m_output_desc;
|
||||
|
||||
// bool m_TearingSupported = false;
|
||||
//
|
||||
// std::vector<nvrhi::RefCountPtr<ID3D12Resource>> m_SwapChainBuffers;
|
||||
// std::vector<nvrhi::TextureHandle> m_RhiSwapChainBuffers;
|
||||
// nvrhi::RefCountPtr<ID3D12Fence> m_FrameFence;
|
||||
// std::vector<HANDLE> m_FrameFenceEvents;
|
||||
|
||||
// UINT64 m_FrameCount = 1;
|
||||
|
||||
// RHI resources
|
||||
|
||||
// Utility functions
|
||||
// bool CreateDevice();
|
||||
// bool CreateSwapChain(GLFWwindow *window);
|
||||
// bool CreateRenderTarget();
|
||||
// void ReleaseRenderTarget();
|
||||
virtual void LoadShaderBlobs() override;
|
||||
|
||||
public:
|
||||
bool CreateInstance();
|
||||
bool CreateDevice();
|
||||
bool CreateSwapChain();
|
||||
void ResizeSwapChain();
|
||||
[[nodiscard]] PFN_vkGetDeviceProcAddr GetDeviceProcAddr();
|
||||
|
||||
private:
|
||||
int32_t m_BackBufferWidth;
|
||||
int32_t m_BackBufferHeight;
|
||||
bool m_VsyncEnabled;
|
||||
bool m_EnableNvrhiValidationLayer;
|
||||
bool m_EnableDebugRuntime = false;
|
||||
bool createInstance();
|
||||
bool createWindowSurface();
|
||||
void installDebugCallback();
|
||||
bool pickPhysicalDevice();
|
||||
bool findQueueFamilies(vk::PhysicalDevice physicalDevice);
|
||||
bool createDevice();
|
||||
bool createSwapChain();
|
||||
void destroySwapChain();
|
||||
|
||||
struct VulkanExtensionSet {
|
||||
std::unordered_set<std::string> instance;
|
||||
std::unordered_set<std::string> layers;
|
||||
std::unordered_set<std::string> device;
|
||||
};
|
||||
|
||||
// minimal set of required extensions
|
||||
VulkanExtensionSet enabledExtensions = {
|
||||
// instance
|
||||
{
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
|
||||
},
|
||||
// layers
|
||||
{},
|
||||
// device
|
||||
{
|
||||
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
|
||||
VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
// optional extensions
|
||||
VulkanExtensionSet optionalExtensions = {
|
||||
// instance
|
||||
{VK_EXT_DEBUG_UTILS_EXTENSION_NAME},
|
||||
// layers
|
||||
{},
|
||||
// device
|
||||
{VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
|
||||
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
|
||||
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
|
||||
VK_NV_MESH_SHADER_EXTENSION_NAME,
|
||||
VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME,
|
||||
VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
|
||||
VK_KHR_MAINTENANCE_4_EXTENSION_NAME,
|
||||
VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME},
|
||||
};
|
||||
|
||||
std::string m_RendererString;
|
||||
|
||||
vk::DynamicLoader m_dynamicLoader;
|
||||
|
||||
vk::Instance m_VulkanInstance;
|
||||
vk::DebugReportCallbackEXT m_DebugReportCallback;
|
||||
|
||||
vk::SurfaceKHR m_WindowSurface;
|
||||
|
||||
vk::PhysicalDevice m_VulkanPhysicalDevice;
|
||||
|
||||
vk::Device m_VulkanDevice;
|
||||
vk::Queue m_GraphicsQueue;
|
||||
vk::Queue m_ComputeQueue;
|
||||
vk::Queue m_TransferQueue;
|
||||
vk::Queue m_PresentQueue;
|
||||
|
||||
int m_GraphicsQueueFamily = -1;
|
||||
int m_ComputeQueueFamily = -1;
|
||||
int m_TransferQueueFamily = -1;
|
||||
int m_PresentQueueFamily = -1;
|
||||
|
||||
int m_SwapChainBufferCount;
|
||||
|
||||
vk::SurfaceFormatKHR m_SwapChainFormat;
|
||||
vk::SwapchainKHR m_SwapChain;
|
||||
|
||||
struct SwapChainImage {
|
||||
vk::Image image;
|
||||
nvrhi::TextureHandle rhiHandle;
|
||||
};
|
||||
|
||||
std::vector<SwapChainImage> m_SwapChainImages;
|
||||
uint32_t m_SwapChainIndex = static_cast<uint32_t>(-1);
|
||||
int m_MaxFramesInFlight = 2;
|
||||
|
||||
nvrhi::vulkan::DeviceHandle m_NvrhiDevice;
|
||||
nvrhi::DeviceHandle m_ValidationLayer;
|
||||
|
||||
nvrhi::CommandListHandle m_BarrierCommandList;
|
||||
std::vector<vk::Semaphore> m_PresentSemaphores;
|
||||
uint32_t m_PresentSemaphoreIndex = 0;
|
||||
|
||||
std::queue<nvrhi::EventQueryHandle> m_FramesInFlight;
|
||||
std::vector<nvrhi::EventQueryHandle> m_QueryPool;
|
||||
|
||||
GLFWwindow *m_Window;
|
||||
|
||||
bool m_EnableComputeQueue;
|
||||
bool m_EnableCopyQueue;
|
||||
bool m_SwapChainMutableFormatSupported = false;
|
||||
bool m_BufferDeviceAddressSupported = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
86
betterRenderer/renderer/source/nvrendererbackend.cpp
Normal file
86
betterRenderer/renderer/source/nvrendererbackend.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
#include "Logs.h"
|
||||
#include <fmt/format.h>
|
||||
|
||||
void NvRendererMessageCallback::message(nvrhi::MessageSeverity severity,
|
||||
const char *messageText) {
|
||||
switch (severity) {
|
||||
case nvrhi::MessageSeverity::Info:
|
||||
case nvrhi::MessageSeverity::Warning:
|
||||
WriteLog(messageText);
|
||||
break;
|
||||
case nvrhi::MessageSeverity::Error:
|
||||
ErrorLog(messageText);
|
||||
//__debugbreak();
|
||||
break;
|
||||
case nvrhi::MessageSeverity::Fatal:
|
||||
ErrorLog(messageText);
|
||||
//__debugbreak();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NvRendererBackend::Init() {
|
||||
LoadShaderBlobs();
|
||||
for (int i = 0; i < GetBackBufferCount(); ++i) {
|
||||
m_framebuffers.emplace_back(GetDevice()->createFramebuffer(
|
||||
nvrhi::FramebufferDesc().addColorAttachment(
|
||||
nvrhi::FramebufferAttachment().setTexture(GetBackBuffer(i)))));
|
||||
}
|
||||
}
|
||||
|
||||
nvrhi::FramebufferHandle NvRendererBackend::GetCurrentFramebuffer() {
|
||||
return m_framebuffers[GetCurrentBackBufferIndex()];
|
||||
}
|
||||
|
||||
nvrhi::ShaderHandle NvRendererBackend::CreateShader(const std::string &name,
|
||||
nvrhi::ShaderType type) {
|
||||
if (auto blob_found = m_shader_blobs.find(name);
|
||||
blob_found != m_shader_blobs.end()) {
|
||||
const auto &blob = blob_found->second;
|
||||
nvrhi::ShaderDesc shader_desc{};
|
||||
shader_desc.shaderType = type;
|
||||
shader_desc.debugName = name;
|
||||
shader_desc.entryName = blob.m_entrypoint;
|
||||
return GetDevice()->createShader(shader_desc, blob.m_blob.data(),
|
||||
blob.m_blob.size());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nvrhi::ShaderHandle NvRendererBackend::CreateMaterialShader(
|
||||
const std::string &name, NvRenderer::RenderPassType pass) {
|
||||
if (auto blob_found = m_material_blobs.find(name);
|
||||
blob_found != m_material_blobs.end()) {
|
||||
const auto &blob = blob_found->second[static_cast<size_t>(pass)];
|
||||
WriteLog(name);
|
||||
WriteLog(blob.m_entrypoint);
|
||||
WriteLog(fmt::format("{:d}", static_cast<int>(pass)));
|
||||
if (blob.m_blob.empty()) return nullptr;
|
||||
nvrhi::ShaderDesc shader_desc{};
|
||||
shader_desc.shaderType = nvrhi::ShaderType::Pixel;
|
||||
shader_desc.debugName = name;
|
||||
shader_desc.entryName = blob.m_entrypoint;
|
||||
return GetDevice()->createShader(shader_desc, blob.m_blob.data(),
|
||||
blob.m_blob.size());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NvRendererBackend::AddMaterialShaderBlob(std::string_view material,
|
||||
std::string_view pass,
|
||||
std::string blob,
|
||||
const std::string &entrypoint) {
|
||||
const static std::unordered_map<std::string_view, NvRenderer::RenderPassType>
|
||||
material_passes{{"deferred", NvRenderer::RenderPassType::Deferred},
|
||||
{"forward", NvRenderer::RenderPassType::Forward},
|
||||
{"cubemap", NvRenderer::RenderPassType::CubeMap}};
|
||||
m_material_blobs[std::string(material)][static_cast<size_t>(
|
||||
material_passes.at(pass))] = {entrypoint, blob};
|
||||
}
|
||||
|
||||
void NvRendererBackend::AddShaderBlob(std::string_view shader, std::string blob,
|
||||
const std::string &entrypoint) {
|
||||
m_shader_blobs[std::string(shader)] = {entrypoint, blob};
|
||||
}
|
||||
58
betterRenderer/renderer/source/nvrendererbackend.h
Normal file
58
betterRenderer/renderer/source/nvrendererbackend.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
struct ShaderDefine {
|
||||
std::string m_name;
|
||||
std::string m_definition;
|
||||
};
|
||||
|
||||
class NvRendererBackend {
|
||||
protected:
|
||||
friend class NvRenderer;
|
||||
NvRenderer *m_renderer;
|
||||
virtual std::string LoadShaderBlob(const std::string &fileName,
|
||||
const std::string &entryName) = 0;
|
||||
virtual bool BeginFrame() = 0;
|
||||
virtual void Present() = 0;
|
||||
virtual void UpdateWindowSize() = 0;
|
||||
virtual int GetCurrentBackBufferIndex() = 0;
|
||||
virtual int GetBackBufferCount() = 0;
|
||||
virtual nvrhi::TextureHandle GetBackBuffer(int index) = 0;
|
||||
class NvRendererMessageCallback *GetMessageCallback() const {
|
||||
return m_renderer->m_message_callback.get();
|
||||
}
|
||||
void AddMaterialShaderBlob(std::string_view material, std::string_view pass,
|
||||
std::string blob, const std::string &entrypoint);
|
||||
void AddShaderBlob(std::string_view shader, std::string blob,
|
||||
const std::string &entrypoint);
|
||||
virtual void LoadShaderBlobs() {}
|
||||
struct ShaderBlob {
|
||||
std::string m_entrypoint;
|
||||
std::string m_blob;
|
||||
};
|
||||
std::unordered_map<std::string, ShaderBlob> m_shader_blobs;
|
||||
std::unordered_map<
|
||||
std::string,
|
||||
std::array<ShaderBlob, NvRenderer::Constants::NumMaterialPasses()>>
|
||||
m_material_blobs;
|
||||
|
||||
public:
|
||||
void Init();
|
||||
virtual bool IsHDRDisplay() const { return false; }
|
||||
// nvrhi::DeviceHandle m_device;
|
||||
virtual nvrhi::IDevice *GetDevice() { return nullptr; };
|
||||
std::vector<nvrhi::FramebufferHandle> m_framebuffers;
|
||||
nvrhi::FramebufferHandle GetCurrentFramebuffer();
|
||||
nvrhi::ShaderHandle CreateShader(const std::string &name,
|
||||
nvrhi::ShaderType type);
|
||||
nvrhi::ShaderHandle CreateMaterialShader(const std::string &name,
|
||||
NvRenderer::RenderPassType pass);
|
||||
};
|
||||
|
||||
class NvRendererMessageCallback : public nvrhi::IMessageCallback {
|
||||
virtual void message(nvrhi::MessageSeverity severity,
|
||||
const char *messageText) override;
|
||||
};
|
||||
512
betterRenderer/renderer/source/nvtexture.cpp
Normal file
512
betterRenderer/renderer/source/nvtexture.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
#include "nvtexture.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
#include <Globals.h>
|
||||
#include <PyInt.h>
|
||||
#include <application.h>
|
||||
#include <dictionary.h>
|
||||
#include <fmt/format.h>
|
||||
#include <utilities.h>
|
||||
|
||||
#include "Logs.h"
|
||||
// #include "Texture.h"
|
||||
#include "nvrendererbackend.h"
|
||||
#include "stbi/stb_image.h"
|
||||
#define STB_IMAGE_RESIZE2_IMPLEMENTATION
|
||||
#include "stbi/stb_image_resize2.h"
|
||||
#undef STB_IMAGE_RESIZE2_IMPLEMENTATION
|
||||
|
||||
uint64_t NvTexture::s_change_counter = 0;
|
||||
|
||||
void NvTexture::Load(int size_bias) {
|
||||
if (m_sz_texture->is_stub()) {
|
||||
m_width = 2;
|
||||
m_height = 2;
|
||||
auto &[pitch, data] = m_data.emplace_back();
|
||||
pitch = m_width * 4;
|
||||
m_format = nvrhi::Format::RGBA8_UNORM;
|
||||
data.resize(pitch * m_height, '\0');
|
||||
return;
|
||||
}
|
||||
std::string buf{};
|
||||
{
|
||||
std::ifstream file(fmt::format("{}{}", m_sz_texture->get_name().data(),
|
||||
m_sz_texture->get_type().data()),
|
||||
std::ios::binary | std::ios::ate);
|
||||
buf.resize(file.tellg(), '\0');
|
||||
file.seekg(0);
|
||||
file.read(buf.data(), buf.size());
|
||||
};
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
if (LoadDDS(buf, size_bias)) {
|
||||
} else if (int channels; stbi_uc *source = stbi_load_from_memory(
|
||||
reinterpret_cast<stbi_uc const *>(buf.data()),
|
||||
buf.size(), &m_width, &m_height, &channels, 4)) {
|
||||
int mipcount =
|
||||
static_cast<int>(floor(log2(std::max(m_width, m_height)))) + 1;
|
||||
|
||||
m_has_alpha = channels == 4;
|
||||
|
||||
m_format =
|
||||
m_srgb ? nvrhi::Format::SRGBA8_UNORM : nvrhi::Format::RGBA8_UNORM;
|
||||
int width = m_width, prev_width = m_width, height = m_height,
|
||||
prev_height = m_height;
|
||||
for (int i = 0; i < mipcount; ++i) {
|
||||
auto &[pitch, data] = m_data.emplace_back();
|
||||
pitch = width * 4;
|
||||
data.resize(pitch * height, '\0');
|
||||
if (!i) {
|
||||
memcpy(data.data(), source, data.size());
|
||||
} else {
|
||||
stbir_resize(m_data[i - 1].second.data(), prev_width, prev_height,
|
||||
prev_width * 4, data.data(), width, height, width * 4,
|
||||
STBIR_4CHANNEL,
|
||||
m_srgb ? STBIR_TYPE_UINT8_SRGB : STBIR_TYPE_UINT8,
|
||||
STBIR_EDGE_WRAP, STBIR_FILTER_MITCHELL);
|
||||
}
|
||||
prev_width = std::exchange(width, std::max(1, width >> 1));
|
||||
prev_height = std::exchange(height, std::max(1, height >> 1));
|
||||
}
|
||||
|
||||
stbi_image_free(source);
|
||||
} else if (m_sz_texture->get_type() == ".tga") {
|
||||
std::stringstream ss(buf, std::ios::in | std::ios::binary);
|
||||
// Read the header of the TGA, compare it with the known headers for
|
||||
// compressed and uncompressed TGAs
|
||||
unsigned char tgaheader[18];
|
||||
ss.read((char *)tgaheader, sizeof(unsigned char) * 18);
|
||||
|
||||
while (tgaheader[0] > 0) {
|
||||
--tgaheader[0];
|
||||
|
||||
unsigned char temp;
|
||||
ss.read((char *)&temp, sizeof(unsigned char));
|
||||
}
|
||||
|
||||
m_width = tgaheader[13] * 256 + tgaheader[12];
|
||||
m_height = tgaheader[15] * 256 + tgaheader[14];
|
||||
m_format =
|
||||
m_srgb ? nvrhi::Format::SRGBA8_UNORM : nvrhi::Format::RGBA8_UNORM;
|
||||
int const bytesperpixel = tgaheader[16] / 8;
|
||||
|
||||
// check whether width, height an BitsPerPixel are valid
|
||||
if ((m_width <= 0) || (m_height <= 0) ||
|
||||
((bytesperpixel != 1) && (bytesperpixel != 3) &&
|
||||
(bytesperpixel != 4))) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_has_alpha = false;
|
||||
|
||||
auto &[pitch, data] = m_data.emplace_back();
|
||||
pitch = m_width * 4;
|
||||
// allocate the data buffer
|
||||
int const datasize = pitch * m_height;
|
||||
data.resize(datasize);
|
||||
|
||||
// call the appropriate loader-routine
|
||||
if (tgaheader[2] == 2) {
|
||||
// uncompressed TGA
|
||||
// rgb or greyscale image, expand to bgra
|
||||
unsigned char buffer[4] = {255, 255, 255,
|
||||
255}; // alpha channel will be white
|
||||
|
||||
unsigned int *datapointer = (unsigned int *)&data[0];
|
||||
unsigned int *bufferpointer = (unsigned int *)&buffer[0];
|
||||
|
||||
int const pixelcount = m_width * m_height;
|
||||
|
||||
for (int i = 0; i < pixelcount; ++i) {
|
||||
ss.read((char *)&buffer[0], sizeof(unsigned char) * bytesperpixel);
|
||||
if (bytesperpixel == 1) {
|
||||
// expand greyscale data
|
||||
buffer[1] = buffer[0];
|
||||
buffer[2] = buffer[0];
|
||||
}
|
||||
std::swap(buffer[0], buffer[2]);
|
||||
// copy all four values in one operation
|
||||
(*datapointer) = (*bufferpointer);
|
||||
++datapointer;
|
||||
}
|
||||
} else if (tgaheader[2] == 10) {
|
||||
// compressed TGA
|
||||
int currentpixel = 0;
|
||||
|
||||
unsigned char buffer[4] = {255, 255, 255, 255};
|
||||
const int pixelcount = m_width * m_height;
|
||||
|
||||
unsigned int *datapointer = (unsigned int *)&data[0];
|
||||
unsigned int *bufferpointer = (unsigned int *)&buffer[0];
|
||||
|
||||
do {
|
||||
unsigned char chunkheader = 0;
|
||||
|
||||
ss.read((char *)&chunkheader, sizeof(unsigned char));
|
||||
|
||||
if ((chunkheader & 0x80) == 0) {
|
||||
// if the high bit is not set, it means it is the number of RAW color
|
||||
// packets, plus 1
|
||||
for (int i = 0; i <= chunkheader; ++i) {
|
||||
ss.read((char *)&buffer[0], bytesperpixel);
|
||||
m_has_alpha |= bytesperpixel == 4 && buffer[3] != 255;
|
||||
|
||||
if (bytesperpixel == 1) {
|
||||
// expand greyscale data
|
||||
buffer[1] = buffer[0];
|
||||
buffer[2] = buffer[0];
|
||||
}
|
||||
std::swap(buffer[0], buffer[2]);
|
||||
// copy all four values in one operation
|
||||
(*datapointer) = (*bufferpointer);
|
||||
|
||||
++datapointer;
|
||||
++currentpixel;
|
||||
}
|
||||
} else {
|
||||
// rle chunk, the color supplied afterwards is reapeated header + 1
|
||||
// times (not including the highest bit)
|
||||
chunkheader &= ~0x80;
|
||||
// read the current color
|
||||
ss.read((char *)&buffer[0], bytesperpixel);
|
||||
m_has_alpha |= bytesperpixel == 4 && buffer[3] != 255;
|
||||
|
||||
if (bytesperpixel == 1) {
|
||||
// expand greyscale data
|
||||
buffer[1] = buffer[0];
|
||||
buffer[2] = buffer[0];
|
||||
}
|
||||
std::swap(buffer[0], buffer[2]);
|
||||
// copy the color into the image data as many times as dictated
|
||||
for (int i = 0; i <= chunkheader; ++i) {
|
||||
(*datapointer) = (*bufferpointer);
|
||||
++datapointer;
|
||||
++currentpixel;
|
||||
}
|
||||
}
|
||||
|
||||
} while (currentpixel < pixelcount);
|
||||
}
|
||||
}
|
||||
int max_size = Global.iMaxTextureSize >> size_bias;
|
||||
while (m_data.size() > 1) {
|
||||
if (m_width <= max_size && m_height <= max_size) {
|
||||
break;
|
||||
}
|
||||
m_width = std::max(m_width >> 1, 1);
|
||||
m_height = std::max(m_height >> 1, 1);
|
||||
m_data.erase(m_data.begin());
|
||||
}
|
||||
}
|
||||
|
||||
void NvTexture::set_components_hint(int format_hint) {
|
||||
m_srgb = format_hint == GL_SRGB || format_hint == GL_SRGB8 ||
|
||||
format_hint == GL_SRGB_ALPHA || format_hint == GL_SRGB8_ALPHA8;
|
||||
}
|
||||
|
||||
void NvTexture::make_from_memory(size_t width, size_t height,
|
||||
const uint8_t *data) {
|
||||
m_data.clear();
|
||||
m_rhi_texture = nullptr;
|
||||
auto &[pitch, slice] = m_data.emplace_back();
|
||||
m_format = nvrhi::Format::RGBA8_UNORM;
|
||||
pitch = 4 * width;
|
||||
slice.resize(pitch * height);
|
||||
memcpy(slice.data(), data, slice.size());
|
||||
}
|
||||
|
||||
void NvTexture::update_from_memory(size_t width, size_t height,
|
||||
const uint8_t *data) {
|
||||
auto renderer = dynamic_cast<NvRenderer *>(GfxRenderer.get());
|
||||
if (!renderer) return;
|
||||
auto backend = renderer->GetBackend();
|
||||
if (width != m_width || height != m_height ||
|
||||
m_format != nvrhi::Format::RGBA8_UNORM || !m_rhi_texture) {
|
||||
m_data.clear();
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
auto &[pitch, slice] = m_data.emplace_back();
|
||||
m_format = nvrhi::Format::RGBA8_UNORM;
|
||||
pitch = 4 * width;
|
||||
slice.resize(pitch * height);
|
||||
m_rhi_texture = backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName(std::string(m_sz_texture->get_name()))
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setMipLevels(m_data.size())
|
||||
.setFormat(m_format)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
m_last_change = ++s_change_counter;
|
||||
}
|
||||
auto &slice = std::get<1>(m_data.front());
|
||||
memcpy(slice.data(), data, slice.size());
|
||||
nvrhi::CommandListHandle command_list =
|
||||
backend->GetDevice()->createCommandList(
|
||||
nvrhi::CommandListParameters()
|
||||
.setQueueType(nvrhi::CommandQueue::Graphics)
|
||||
.setEnableImmediateExecution(false));
|
||||
command_list->open();
|
||||
for (int mip = 0; mip < m_data.size(); ++mip) {
|
||||
const auto &[pitch, data] = m_data[mip];
|
||||
command_list->writeTexture(m_rhi_texture, 0, mip, data.data(), pitch);
|
||||
}
|
||||
command_list->close();
|
||||
backend->GetDevice()->executeCommandList(command_list,
|
||||
nvrhi::CommandQueue::Graphics);
|
||||
}
|
||||
|
||||
bool NvTexture::IsLoaded() const { return false; }
|
||||
|
||||
NvTextureManager::NvTextureManager(NvRenderer *renderer)
|
||||
: m_backend(renderer->m_backend.get()) {}
|
||||
|
||||
size_t NvTextureManager::FetchTexture(std::string path, int format_hint,
|
||||
int size_bias, bool unload_on_location) {
|
||||
if (contains(path, '|')) {
|
||||
path.erase(path.find('|')); // po | może być nazwa kolejnej tekstury
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> locator; // resource name, resource type
|
||||
std::string traits;
|
||||
|
||||
// discern textures generated by a script
|
||||
// TBD: support file: for file resources?
|
||||
auto const isgenerated{path.find("make:") == 0};
|
||||
auto const isinternalsrc{path.find("internal_src:") == 0};
|
||||
// process supplied resource name
|
||||
if (isgenerated || isinternalsrc) {
|
||||
// generated resource
|
||||
// scheme:(user@)path?query
|
||||
|
||||
// remove scheme indicator
|
||||
path.erase(0, path.find(':') + 1);
|
||||
// TODO: extract traits specification from the query
|
||||
// clean up slashes
|
||||
erase_leading_slashes(path);
|
||||
} else {
|
||||
// regular file resource
|
||||
// (filepath/)(#)filename.extension(:traits)
|
||||
|
||||
// extract trait specifications
|
||||
auto const traitpos = path.rfind(':');
|
||||
if (traitpos != std::string::npos) {
|
||||
// po dwukropku mogą być podane dodatkowe informacje niebędące nazwą
|
||||
// tekstury
|
||||
if (path.size() > traitpos + 1) {
|
||||
traits = path.substr(traitpos + 1);
|
||||
}
|
||||
path.erase(traitpos);
|
||||
}
|
||||
// potentially trim file type indicator since we check for multiple types
|
||||
erase_extension(path);
|
||||
// clean up slashes
|
||||
erase_leading_slashes(path);
|
||||
path = ToLower(path);
|
||||
// temporary code for legacy assets -- textures with names beginning with #
|
||||
// are to be sharpened
|
||||
if ((starts_with(path, "#")) || (contains(path, "/#"))) {
|
||||
traits += '#';
|
||||
}
|
||||
}
|
||||
// try to locate requested texture in the databank
|
||||
// TBD, TODO: include trait specification in the resource id in the databank?
|
||||
|
||||
{
|
||||
const std::array<std::string, 3> filenames{
|
||||
Global.asCurrentTexturePath + path, path, szTexturePath + path};
|
||||
for (const auto &filename : filenames) {
|
||||
if (auto found = m_texture_map.find(filename);
|
||||
found != m_texture_map.end())
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
|
||||
// if the lookup fails...
|
||||
if (isgenerated) {
|
||||
// TODO: verify presence of the generator script
|
||||
locator.first = path;
|
||||
locator.second = "make:";
|
||||
} else if (isinternalsrc) {
|
||||
locator.first = path;
|
||||
locator.second = "internalsrc:";
|
||||
} else {
|
||||
// ...for file resources check if it's on disk
|
||||
locator = texture_manager::find_on_disk(path);
|
||||
if (true == locator.first.empty()) {
|
||||
// there's nothing matching in the databank nor on the disk, report
|
||||
// failure
|
||||
ErrorLog("Bad file: failed to locate texture file \"" + path + "\"",
|
||||
logtype::file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto &new_texture = m_texture_cache.emplace_back();
|
||||
auto index = m_texture_cache.size();
|
||||
m_texture_map.emplace(locator.first, index);
|
||||
|
||||
new_texture = std::make_shared<NvTexture>();
|
||||
|
||||
auto sz_texture = std::make_shared<opengl_texture>();
|
||||
new_texture->m_sz_texture = sz_texture;
|
||||
sz_texture->is_texstub = isgenerated;
|
||||
sz_texture->name = locator.first;
|
||||
sz_texture->type = locator.second;
|
||||
sz_texture->traits = traits;
|
||||
sz_texture->components_hint = format_hint;
|
||||
|
||||
new_texture->set_components_hint(format_hint);
|
||||
|
||||
new_texture->Load(size_bias);
|
||||
WriteLog("Created texture object for \"" + locator.first + "\"",
|
||||
logtype::texture);
|
||||
|
||||
if (unload_on_location) {
|
||||
new_texture->m_gc_slot = m_unloadable_textures.size();
|
||||
auto &gc_slot = m_unloadable_textures.emplace_back();
|
||||
gc_slot.m_index = index - 1;
|
||||
} else {
|
||||
new_texture->m_gc_slot = static_cast<size_t>(-1);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
bool NvTexture::CreateRhiTexture() {
|
||||
if (m_rhi_texture) return true;
|
||||
m_last_change = ++s_change_counter;
|
||||
auto renderer = dynamic_cast<NvRenderer *>(GfxRenderer.get());
|
||||
if (!renderer) return false;
|
||||
auto backend = renderer->GetBackend();
|
||||
m_rhi_texture = renderer->GetBackend()->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName(std::string(m_sz_texture->get_name()))
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setMipLevels(m_data.size())
|
||||
.setFormat(m_format)
|
||||
.setInitialState(nvrhi::ResourceStates::CopyDest)
|
||||
.setKeepInitialState(true));
|
||||
nvrhi::CommandListHandle command_list =
|
||||
backend->GetDevice()->createCommandList(
|
||||
nvrhi::CommandListParameters()
|
||||
.setQueueType(nvrhi::CommandQueue::Copy)
|
||||
.setEnableImmediateExecution(false));
|
||||
command_list->open();
|
||||
for (int mip = 0; mip < m_data.size(); ++mip) {
|
||||
const auto &[pitch, data] = m_data[mip];
|
||||
command_list->writeTexture(m_rhi_texture, 0, mip, data.data(), pitch);
|
||||
}
|
||||
command_list->close();
|
||||
backend->GetDevice()->executeCommandList(command_list,
|
||||
nvrhi::CommandQueue::Copy);
|
||||
if (m_sz_texture->get_type() == "make:") {
|
||||
auto const components{Split(std::string(m_sz_texture->get_name()), '?')};
|
||||
|
||||
auto dictionary = std::make_shared<dictionary_source>(components.back());
|
||||
|
||||
auto rt = std::make_shared<python_rt>();
|
||||
rt->shared_tex = this;
|
||||
|
||||
if (!Application.request(
|
||||
{ToLower(components.front()), dictionary, rt})) /*__debugbreak()*/
|
||||
;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NvTextureManager::IsValidHandle(size_t handle) {
|
||||
return handle > 0 && handle <= m_texture_cache.size();
|
||||
}
|
||||
|
||||
NvTexture *NvTextureManager::GetTexture(size_t handle) {
|
||||
if (!handle || handle > m_texture_cache.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_texture_cache[handle - 1].get();
|
||||
}
|
||||
|
||||
nvrhi::ITexture *NvTextureManager::GetRhiTexture(
|
||||
size_t handle, nvrhi::ICommandList *command_list) {
|
||||
if (!IsValidHandle(handle)) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto &texture = m_texture_cache[handle - 1];
|
||||
// if (!texture.m_rhi_texture) {
|
||||
// texture.m_rhi_texture = m_backend->GetDevice()->createTexture(
|
||||
// nvrhi::TextureDesc()
|
||||
// .setDebugName(texture.m_sz_texture.name)
|
||||
// .setWidth(texture.m_width)
|
||||
// .setHeight(texture.m_height)
|
||||
// .setMipLevels(texture.m_mipcount)
|
||||
// .setFormat(texture.m_format)
|
||||
// .setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
// .setKeepInitialState(true));
|
||||
//
|
||||
// for (int mip = 0; mip < texture.m_mipcount; ++mip) {
|
||||
// const auto &[pitch, data] = texture.m_data[mip];
|
||||
// command_list->writeTexture(texture.m_rhi_texture, 0, mip, data.data(),
|
||||
// pitch);
|
||||
// }
|
||||
// }
|
||||
texture->CreateRhiTexture();
|
||||
return texture->m_rhi_texture;
|
||||
}
|
||||
|
||||
TextureTraitFlags NvTextureManager::GetTraits(size_t handle) {
|
||||
if (!IsValidHandle(handle)) {
|
||||
return 0;
|
||||
}
|
||||
return m_texture_cache[handle - 1]->GetTraits();
|
||||
}
|
||||
|
||||
void NvTextureManager::UpdateLastUse(size_t handle,
|
||||
const glm::dvec3 &location) {
|
||||
if (!IsValidHandle(handle)) {
|
||||
return;
|
||||
}
|
||||
const auto &texture = m_texture_cache[handle - 1];
|
||||
if (texture->m_gc_slot < m_unloadable_textures.size()) {
|
||||
m_unloadable_textures[texture->m_gc_slot].m_last_position_requested =
|
||||
location;
|
||||
}
|
||||
}
|
||||
|
||||
nvrhi::SamplerHandle NvTextureManager::GetSamplerForTraits(
|
||||
TextureTraitFlags traits, NvRenderer::RenderPassType pass) {
|
||||
switch (pass) {
|
||||
case NvRenderer::RenderPassType::CubeMap:
|
||||
case NvRenderer::RenderPassType::ShadowMap:
|
||||
traits[MaTextureTraits_Sharpen] = false;
|
||||
traits[MaTextureTraits_NoAnisotropy] = true;
|
||||
traits[MaTextureTraits_NoMipBias] = true;
|
||||
break;
|
||||
}
|
||||
auto &sampler = m_samplers[traits];
|
||||
if (!sampler) {
|
||||
nvrhi::SamplerDesc desc =
|
||||
nvrhi::SamplerDesc()
|
||||
.setAddressU(traits[MaTextureTraits_ClampS]
|
||||
? nvrhi::SamplerAddressMode::Clamp
|
||||
: nvrhi::SamplerAddressMode::Wrap)
|
||||
.setAddressV(traits[MaTextureTraits_ClampT]
|
||||
? nvrhi::SamplerAddressMode::Clamp
|
||||
: nvrhi::SamplerAddressMode::Wrap)
|
||||
.setAllFilters(true)
|
||||
.setMipFilter(false)
|
||||
.setMaxAnisotropy(traits[MaTextureTraits_NoAnisotropy] ? 0.f : 16.f)
|
||||
.setMipBias(traits[MaTextureTraits_NoMipBias] ? 0.f : -1.76f);
|
||||
sampler = m_backend->GetDevice()->createSampler(desc);
|
||||
}
|
||||
return sampler;
|
||||
}
|
||||
|
||||
void NvTextureManager::Cleanup(const glm::dvec3 &location) {
|
||||
for (const auto &slot : m_unloadable_textures) {
|
||||
if (glm::distance2(slot.m_last_position_requested, location) >
|
||||
Global.BaseDrawRange * Global.BaseDrawRange) {
|
||||
m_texture_cache[slot.m_index]->m_rhi_texture = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
betterRenderer/renderer/source/nvtexture.h
Normal file
102
betterRenderer/renderer/source/nvtexture.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "interfaces/ITexture.h"
|
||||
#include "nvrenderer/nvrenderer_enums.h"
|
||||
|
||||
enum MaTextureTraits {
|
||||
MaTextureTraits_Sharpen,
|
||||
MaTextureTraits_ClampS,
|
||||
MaTextureTraits_ClampT,
|
||||
|
||||
MaTextureTraits_NoMipBias,
|
||||
MaTextureTraits_NoAnisotropy,
|
||||
|
||||
MaTextureTraits_Num
|
||||
};
|
||||
|
||||
using TextureTraitFlags = std::bitset<MaTextureTraits_Num>;
|
||||
|
||||
struct opengl_texture;
|
||||
|
||||
struct NvTexture : public ITexture {
|
||||
virtual bool create(bool Static = false) override {
|
||||
return CreateRhiTexture();
|
||||
}
|
||||
virtual int get_width() const override { return m_width; }
|
||||
virtual int get_height() const override { return m_height; }
|
||||
virtual size_t get_id() const override {
|
||||
return reinterpret_cast<size_t>(m_rhi_texture.Get());
|
||||
}
|
||||
virtual void release() override {}
|
||||
virtual void make_stub() override {}
|
||||
virtual std::string_view get_traits() const override { return ""; }
|
||||
virtual std::string_view get_name() const override {
|
||||
return m_sz_texture->get_name();
|
||||
}
|
||||
virtual std::string_view get_type() const override {
|
||||
return m_sz_texture->get_type();
|
||||
}
|
||||
virtual bool is_stub() const override { return false; }
|
||||
virtual bool get_has_alpha() const override { return m_has_alpha; }
|
||||
virtual bool get_is_ready() const override { return m_rhi_texture; }
|
||||
virtual void set_components_hint(int format_hint) override;
|
||||
virtual void make_from_memory(size_t width, size_t height,
|
||||
const uint8_t* data) override;
|
||||
virtual void update_from_memory(size_t width, size_t height,
|
||||
const uint8_t* data) override;
|
||||
|
||||
void Load(int size_bias);
|
||||
bool LoadDDS(std::string& data, int size_bias);
|
||||
bool IsLoaded() const;
|
||||
std::shared_ptr<ITexture> m_sz_texture;
|
||||
nvrhi::TextureHandle m_rhi_texture;
|
||||
std::vector<std::pair<int, std::string>> m_data;
|
||||
TextureTraitFlags GetTraits() const { return 0; }
|
||||
int m_width;
|
||||
int m_height;
|
||||
int m_mipcount;
|
||||
int m_slicecount;
|
||||
bool m_srgb;
|
||||
bool m_bc;
|
||||
bool m_has_alpha;
|
||||
nvrhi::Format m_format;
|
||||
size_t m_gc_slot;
|
||||
bool CreateRhiTexture();
|
||||
uint64_t m_last_change;
|
||||
static uint64_t s_change_counter;
|
||||
uint64_t m_own_handle;
|
||||
};
|
||||
|
||||
class NvTextureManager {
|
||||
public:
|
||||
struct TextureGCSlot {
|
||||
size_t m_index;
|
||||
glm::dvec3 m_last_position_requested;
|
||||
};
|
||||
NvTextureManager(class NvRenderer* renderer);
|
||||
size_t FetchTexture(std::string path, int format_hint, int size_bias,
|
||||
bool unload_on_location);
|
||||
void UpdateLastUse(size_t handle, const glm::dvec3& location);
|
||||
bool IsValidHandle(size_t handle);
|
||||
NvTexture* GetTexture(size_t handle);
|
||||
nvrhi::ITexture* GetRhiTexture(size_t handle,
|
||||
nvrhi::ICommandList* command_list = nullptr);
|
||||
TextureTraitFlags GetTraits(size_t handle);
|
||||
nvrhi::SamplerHandle GetSamplerForTraits(TextureTraitFlags traits,
|
||||
RendererEnums::RenderPassType pass);
|
||||
std::vector<TextureGCSlot> m_unloadable_textures;
|
||||
|
||||
void Cleanup(const glm::dvec3& location);
|
||||
|
||||
private:
|
||||
std::unordered_map<TextureTraitFlags, nvrhi::SamplerHandle> m_samplers;
|
||||
class NvRendererBackend* m_backend;
|
||||
std::unordered_map<std::string, size_t> m_texture_map;
|
||||
std::vector<std::shared_ptr<NvTexture>> m_texture_cache;
|
||||
};
|
||||
94
betterRenderer/renderer/source/quadtree.cpp
Normal file
94
betterRenderer/renderer/source/quadtree.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "nvrenderer/quadtree.h"
|
||||
|
||||
void QuadTreeBuilder::Node::Insert(uint32_t index, glm::dvec3 min_,
|
||||
glm::dvec3 max_, int level) {
|
||||
if (level > 0) {
|
||||
int child = GetChildIndex(.5 * (min_ + max_));
|
||||
if (!m_children[child]) {
|
||||
m_children[child] = std::make_shared<Node>(GetChildBounds(child));
|
||||
}
|
||||
m_children[child]->Insert(index, min_, max_, level - 1);
|
||||
} else {
|
||||
m_leaves.emplace_back(.5 * (max_ + min_), .5 * (max_ - min_), index);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (compMin(m_actual_min) < -1.e10) {
|
||||
__debugbreak();
|
||||
}
|
||||
if (compMax(m_actual_max) > 1.e10) {
|
||||
__debugbreak();
|
||||
}
|
||||
#endif
|
||||
|
||||
m_actual_min = min(min_, m_actual_min);
|
||||
m_actual_max = max(max_, m_actual_max);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (compMax(glm::abs(m_actual_min)) > 1.e10) {
|
||||
__debugbreak();
|
||||
}
|
||||
if (compMax(glm::abs(m_actual_max)) > 1.e10) {
|
||||
__debugbreak();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QuadTreeBuilder::QuadTreeBuilder(double min_x, double min_z, double max_x,
|
||||
double max_z)
|
||||
: m_root(std::make_shared<Node>(min_x, min_z, max_x, max_z)),
|
||||
m_min(min_x, -std::numeric_limits<double>::max(), min_z),
|
||||
m_max(max_x, std::numeric_limits<double>::max(), max_z),
|
||||
m_max_levels(32) {
|
||||
// Ensure outer bounds are square
|
||||
glm::dvec2 origin = .5 * (m_max + m_min).xz;
|
||||
glm::dvec2 extent = .5 * (m_max - m_min).xz;
|
||||
extent = glm::dvec2(glm::compMax(extent));
|
||||
m_min.xz = origin - extent;
|
||||
m_max.xz = origin + extent;
|
||||
// glm::dvec3 scale_v = (m_max - m_min) / 50.;
|
||||
// double scale = glm::min(scale_v.z, scale_v.x);
|
||||
// m_max_levels = glm::max(1, (int)glm::ceil(glm::log2(scale)));
|
||||
}
|
||||
|
||||
void QuadTreeBuilder::Insert(uint32_t index, glm::dvec3 min,
|
||||
glm::dvec3 max) const {
|
||||
glm::dvec3 scale_v = (m_max - m_min) / (max - min);
|
||||
double scale = glm::min(scale_v.z, scale_v.x);
|
||||
int level =
|
||||
glm::clamp((int)glm::floor(glm::log2(scale)), 0, m_max_levels - 1);
|
||||
m_root->Insert(index, min, max, level);
|
||||
}
|
||||
|
||||
void QuadTree::Build(const QuadTreeBuilder& builder) {
|
||||
Add(m_nodes.emplace_back(), *builder.m_root);
|
||||
}
|
||||
|
||||
void QuadTree::Add(Node& node, const QuadTreeBuilder::Node& item) {
|
||||
if (item.m_leaves.empty() && item.NumChildren() == 1) {
|
||||
return Add(node, *item.FirstChild());
|
||||
}
|
||||
node.m_origin = .5 * (item.m_actual_max + item.m_actual_min);
|
||||
node.m_extent = .5 * (item.m_actual_max - item.m_actual_min);
|
||||
node.m_first_leave = m_leaves.size();
|
||||
node.m_leave_count = item.m_leaves.size();
|
||||
m_leaves.insert(m_leaves.end(), item.m_leaves.begin(), item.m_leaves.end());
|
||||
uint32_t first_child = m_nodes.size();
|
||||
uint32_t child_count = 0;
|
||||
for (const auto& child : item.m_children) {
|
||||
if (child) ++child_count;
|
||||
}
|
||||
node.m_first_child = first_child;
|
||||
node.m_child_count = child_count;
|
||||
// node reference invalidated from here
|
||||
for (int i = 0; i < child_count; ++i) {
|
||||
m_nodes.emplace_back();
|
||||
}
|
||||
{
|
||||
int child_index = first_child;
|
||||
for (const auto& child : item.m_children) {
|
||||
if (!child) continue;
|
||||
Add(m_nodes[child_index++], *child);
|
||||
}
|
||||
}
|
||||
}
|
||||
181
betterRenderer/renderer/source/resource_registry.cpp
Normal file
181
betterRenderer/renderer/source/resource_registry.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "nvrenderer/resource_registry.h"
|
||||
|
||||
#include "Logs.h"
|
||||
#include "fmt/compile.h"
|
||||
#include "nvtexture.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
MaResourceMappingSet& MaResourceMappingSet::Add(
|
||||
const MaResourceMapping& mapping) {
|
||||
m_mappings.push_back(mapping);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MaResourceMappingSet::ToBindingLayout(
|
||||
nvrhi::BindingLayoutDesc& desc) const {
|
||||
for (const auto& [slot, size, key, type] : m_mappings) {
|
||||
nvrhi::BindingLayoutItem item{};
|
||||
item.type = type;
|
||||
item.slot = slot;
|
||||
item.size = size;
|
||||
desc.addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
bool MaResourceMappingSet::ToBindingSet(
|
||||
nvrhi::BindingSetDesc& desc, const MaResourceRegistry* registry,
|
||||
const nvrhi::BindingLayoutDesc* reference_layout) const {
|
||||
bool is_valid = true;
|
||||
for (const auto& [slot, size, key, type] : m_mappings) {
|
||||
MaResourceView resource{};
|
||||
switch (type) {
|
||||
case nvrhi::ResourceType::PushConstants:
|
||||
case nvrhi::ResourceType::None:
|
||||
break;
|
||||
default:
|
||||
resource = registry->GetResource(key, type);
|
||||
}
|
||||
bool item_is_valid = resource.IsValid();
|
||||
bool item_is_present = true;
|
||||
nvrhi::BindingSetItem item{};
|
||||
item.slot = slot;
|
||||
item.type = type;
|
||||
item.resourceHandle = resource.m_resource;
|
||||
item.dimension = nvrhi::TextureDimension::Unknown;
|
||||
item.format = nvrhi::Format::UNKNOWN;
|
||||
switch (type) {
|
||||
case nvrhi::ResourceType::PushConstants:
|
||||
item.range.byteOffset = 0;
|
||||
item.range.byteSize = size;
|
||||
item_is_valid = true;
|
||||
break;
|
||||
case nvrhi::ResourceType::RawBuffer_SRV:
|
||||
case nvrhi::ResourceType::RawBuffer_UAV:
|
||||
case nvrhi::ResourceType::StructuredBuffer_SRV:
|
||||
case nvrhi::ResourceType::StructuredBuffer_UAV:
|
||||
case nvrhi::ResourceType::TypedBuffer_SRV:
|
||||
case nvrhi::ResourceType::TypedBuffer_UAV:
|
||||
case nvrhi::ResourceType::ConstantBuffer:
|
||||
case nvrhi::ResourceType::VolatileConstantBuffer:
|
||||
item.range = nvrhi::EntireBuffer;
|
||||
break;
|
||||
case nvrhi::ResourceType::Texture_SRV:
|
||||
case nvrhi::ResourceType::Texture_UAV:
|
||||
item.subresources = nvrhi::AllSubresources;
|
||||
default:;
|
||||
}
|
||||
if (reference_layout) {
|
||||
item_is_present = false;
|
||||
for (const auto& binding : reference_layout->bindings) {
|
||||
if (binding.type != item.type || binding.slot != item.slot) continue;
|
||||
item_is_present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item_is_valid && item_is_present) desc.addItem(item);
|
||||
is_valid &= item_is_valid;
|
||||
}
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
MaResourceView MaResourceRegistry::GetResource(
|
||||
const entt::hashed_string& key,
|
||||
const nvrhi::ResourceType requested_type) const {
|
||||
auto registry = this;
|
||||
while (registry) {
|
||||
if (const auto resource = registry->GetResourceLocal(key, requested_type);
|
||||
resource.IsValid())
|
||||
return resource;
|
||||
registry = registry->m_parent;
|
||||
}
|
||||
std::string what = fmt::format("Unable to locate resource {:s} [{:#x}]",
|
||||
key.data(), key.value());
|
||||
ErrorLog(fmt::format("Unable to locate resource {:s} [{:#x}]", key.data(),
|
||||
key.value()));
|
||||
throw std::runtime_error(what.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
MaResourceRegistry::MaResourceRegistry(MaResourceRegistry* parent)
|
||||
: m_parent(parent) {}
|
||||
|
||||
void MaResourceRegistry::InitResourceRegistry() {
|
||||
if (m_parent) {
|
||||
m_texture_manager =
|
||||
std::shared_ptr<NvTextureManager>(m_parent->GetTextureManager());
|
||||
} else {
|
||||
m_texture_manager =
|
||||
std::make_shared<NvTextureManager>(dynamic_cast<NvRenderer*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
void MaResourceRegistry::RegisterResource(const bool global,
|
||||
const entt::hashed_string& key,
|
||||
nvrhi::IResource* resource,
|
||||
const nvrhi::ResourceType type) {
|
||||
auto registry = this;
|
||||
while (global && registry->m_parent) registry = registry->m_parent;
|
||||
registry->m_resource_views[key] = {resource, type};
|
||||
}
|
||||
|
||||
void MaResourceRegistry::RegisterTexture(const entt::hashed_string& key,
|
||||
const size_t handle) {
|
||||
m_streaming_textures[key] = handle;
|
||||
}
|
||||
|
||||
void MaResourceRegistry::RegisterResource(const bool global, const char* key,
|
||||
nvrhi::IResource* resource,
|
||||
const nvrhi::ResourceType type) {
|
||||
RegisterResource(global, static_cast<entt::hashed_string>(key), resource,
|
||||
type);
|
||||
}
|
||||
|
||||
void MaResourceRegistry::RegisterTexture(const char* key, const size_t handle) {
|
||||
RegisterTexture(static_cast<entt::hashed_string>(key), handle);
|
||||
}
|
||||
|
||||
MaResourceView MaResourceRegistry::GetResourceLocal(
|
||||
const entt::hashed_string& key,
|
||||
const nvrhi::ResourceType requested_type) const {
|
||||
// Attempt to fetch streaming texture
|
||||
if (requested_type == nvrhi::ResourceType::Texture_SRV) {
|
||||
if (const auto it = m_streaming_textures.find(key);
|
||||
it != m_streaming_textures.end()) {
|
||||
if (const auto texture_manager = GetTextureManager();
|
||||
texture_manager && texture_manager->IsValidHandle(it->second))
|
||||
return {texture_manager->GetRhiTexture(it->second),
|
||||
nvrhi::ResourceType::Texture_SRV};
|
||||
}
|
||||
}
|
||||
// Attempt to fetch static resource
|
||||
if (const auto it = m_resource_views.find(key);
|
||||
it != m_resource_views.end() && requested_type == it->second.m_type) {
|
||||
return it->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
NvTextureManager* MaResourceRegistry::GetTextureManager() const {
|
||||
auto registry = this;
|
||||
while (!registry->m_texture_manager && registry->m_parent)
|
||||
registry = registry->m_parent;
|
||||
return registry->m_texture_manager.get();
|
||||
}
|
||||
|
||||
uint64_t MaResourceRegistry::GetLastStreamingTextureUpdate() const {
|
||||
uint64_t last_update = 1;
|
||||
const auto texture_manager = GetTextureManager();
|
||||
for (const auto& [key, texture] : m_streaming_textures) {
|
||||
if (!texture_manager->IsValidHandle(texture)) continue;
|
||||
last_update = std::max(last_update,
|
||||
texture_manager->GetTexture(texture)->m_last_change);
|
||||
}
|
||||
return last_update;
|
||||
}
|
||||
void MaResourceRegistry::UpdateLastStreamingTextureUse(
|
||||
const glm::dvec3& location) const {
|
||||
const auto texture_manager = GetTextureManager();
|
||||
for (const auto& [key, texture] : m_streaming_textures) {
|
||||
texture_manager->UpdateLastUse(texture, location);
|
||||
}
|
||||
}
|
||||
31
betterRenderer/renderer/source/shaders_d3d12.cpp
Normal file
31
betterRenderer/renderer/source/shaders_d3d12.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "nvrenderer_d3d12.h"
|
||||
|
||||
#if LIBMANUL_WITH_D3D12
|
||||
|
||||
void NvRendererBackend_D3D12::LoadShaderBlobs() {
|
||||
auto root = YAML::LoadFile("shaders/shaders_dxil.manul");
|
||||
|
||||
for (const auto entry : root["materials"]) {
|
||||
std::string_view material = entry.first.as<std::string_view>();
|
||||
for (const auto shader : entry.second) {
|
||||
auto binary = shader.second["binary"].as<YAML::Binary>();
|
||||
auto entrypoint = shader.second["entrypoint"].as<std::string>();
|
||||
AddMaterialShaderBlob(
|
||||
material, shader.first.as<std::string_view>(),
|
||||
std::string(binary.data(), binary.data() + binary.size()),
|
||||
entrypoint);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto shader : root["utility"]) {
|
||||
auto binary = shader.second["binary"].as<YAML::Binary>();
|
||||
auto entrypoint = shader.second["entrypoint"].as<std::string>();
|
||||
AddShaderBlob(shader.first.as<std::string_view>(),
|
||||
std::string(binary.data(), binary.data() + binary.size()),
|
||||
entrypoint);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
31
betterRenderer/renderer/source/shaders_vulkan.cpp
Normal file
31
betterRenderer/renderer/source/shaders_vulkan.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "nvrenderer_vulkan.h"
|
||||
|
||||
#if LIBMANUL_WITH_VULKAN
|
||||
|
||||
void NvRendererBackend_Vulkan::LoadShaderBlobs() {
|
||||
auto root = YAML::LoadFile("shaders/shaders_spirv.manul");
|
||||
|
||||
for (const auto entry : root["materials"]) {
|
||||
std::string_view material = entry.first.as<std::string_view>();
|
||||
for (const auto shader : entry.second) {
|
||||
auto binary = shader.second["binary"].as<YAML::Binary>();
|
||||
auto entrypoint = shader.second["entrypoint"].as<std::string>();
|
||||
AddMaterialShaderBlob(
|
||||
material, shader.first.as<std::string_view>(),
|
||||
std::string(binary.data(), binary.data() + binary.size()),
|
||||
entrypoint);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto shader : root["utility"]) {
|
||||
auto binary = shader.second["binary"].as<YAML::Binary>();
|
||||
auto entrypoint = shader.second["entrypoint"].as<std::string>();
|
||||
AddShaderBlob(shader.first.as<std::string_view>(),
|
||||
std::string(binary.data(), binary.data() + binary.size()),
|
||||
entrypoint);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
466
betterRenderer/renderer/source/sky.cpp
Normal file
466
betterRenderer/renderer/source/sky.cpp
Normal file
@@ -0,0 +1,466 @@
|
||||
#include "sky.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "nvrendererbackend.h"
|
||||
#include "simulationenvironment.h"
|
||||
#include "simulationtime.h"
|
||||
|
||||
namespace {
|
||||
static const float PI = 3.14159265358979323846f;
|
||||
static const float INV_PI = 0.31830988618379067154f;
|
||||
static const float INV_4PI = 0.25f * INV_PI;
|
||||
static const float PHASE_ISOTROPIC = INV_4PI;
|
||||
static const float RAYLEIGH_PHASE_SCALE = (3.f / 16.f) * INV_PI;
|
||||
static const float g = 0.8f;
|
||||
static const float gg = g * g;
|
||||
|
||||
static const float EARTH_RADIUS = 6371.0e3f; // km
|
||||
static const float ATMOSPHERE_THICKNESS = 100.0e3f; // km
|
||||
static const float ATMOSPHERE_RADIUS = EARTH_RADIUS + ATMOSPHERE_THICKNESS;
|
||||
static const float EXPOSURE = -4.0;
|
||||
|
||||
// Extraterrestial Solar Irradiance Spectra, units W * m^-2 * nm^-1
|
||||
// https://www.nrel.gov/grid/solar-resource/spectra.html
|
||||
static const glm::vec4 sun_spectral_irradiance =
|
||||
glm::vec4(1.679, 1.828, 1.986, 1.307);
|
||||
// Rayleigh scattering coefficient at sea level, units km^-1
|
||||
// "Rayleigh-scattering calculations for the terrestrial atmosphere"
|
||||
// by Anthony Bucholtz (1995).
|
||||
static const glm::vec4 molecular_scattering_coefficient_base =
|
||||
glm::vec4(6.605e-6, 1.067e-5, 1.842e-5, 3.156e-5);
|
||||
// Fog scattering/extinction cross section, units m^2 / molecules
|
||||
// Mie theory results for IOR of 1.333. Particle size is a log normal
|
||||
// distribution of mean diameter=15 and std deviation=0.4
|
||||
static const glm::vec4 fog_scattering_cross_section =
|
||||
glm::vec4(5.015e-10, 4.987e-10, 4.966e-10, 4.949e-10);
|
||||
// Ozone absorption cross section, units m^2 / molecules
|
||||
// "High spectral resolution ozone absorption cross-sections"
|
||||
// by V. Gorshelev et al. (2014).
|
||||
static const glm::vec4 ozone_absorption_cross_section =
|
||||
glm::vec4(3.472e-25, 3.914e-25, 1.349e-25, 11.03e-27);
|
||||
static const glm::mat4x3 M = {
|
||||
137.672389239975, -8.632904716299537, -1.7181567391931372,
|
||||
32.549094028629234, 91.29801417199785, -12.005406444382531,
|
||||
-38.91428392614275, 34.31665471469816, 29.89044807197628,
|
||||
8.572844237945445, -11.103384660054624, 117.47585277566478};
|
||||
} // namespace
|
||||
|
||||
Sky::Sky(NvRenderer* renderer)
|
||||
: m_backend(renderer->GetBackend()), MaResourceRegistry(renderer) {
|
||||
m_transmittance_pass = std::make_shared<SkyTransmittancePass>(this);
|
||||
m_aerial_lut = std::make_shared<SkyAerialLut>(this);
|
||||
}
|
||||
|
||||
void Sky::Init() {
|
||||
InitResourceRegistry();
|
||||
m_sky_constants = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(sizeof(SkyConstants),
|
||||
"Sky constants", 16)
|
||||
.setInitialState(nvrhi::ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
m_transmittance_pass->Init();
|
||||
m_aerial_lut->Init();
|
||||
RegisterResource(true, "sky_aerial_lut", m_aerial_lut->m_lut,
|
||||
nvrhi::ResourceType::Texture_SRV);
|
||||
}
|
||||
|
||||
void Sky::Render(nvrhi::ICommandList* command_list,
|
||||
const glm::dmat4& projection, const glm::dmat4& view) {
|
||||
UpdateConstants(command_list);
|
||||
m_transmittance_pass->Render(command_list);
|
||||
m_aerial_lut->Render(command_list, projection, view);
|
||||
}
|
||||
|
||||
float Sky::GetOzoneMean() const {
|
||||
float month = glm::saturate(simulation::Time.year_day() / 366.f) * 12.f + .5f;
|
||||
float month_fract = glm::fract(month);
|
||||
int month_l = glm::floor(month);
|
||||
static std::array<float, 12> ozone_means{347.f, 370.f, 381.f, 384.f,
|
||||
372.f, 352.f, 333.f, 317.f,
|
||||
298.f, 285.f, 290.f, 315.f};
|
||||
return glm::lerp(ozone_means[month_l], ozone_means[(month_l + 1) % 12],
|
||||
month_fract);
|
||||
}
|
||||
|
||||
glm::vec4 Sky::GetAerosolAbsorptionCrossSection() const {
|
||||
switch (m_aerosol_preset) {
|
||||
case AerosolPreset::Rural:
|
||||
return {5.0393e-23f, 8.0765e-23f, 1.3823e-22f, 2.3383e-22f};
|
||||
case AerosolPreset::Urban:
|
||||
return {2.8722e-24f, 4.6168e-24f, 7.9706e-24f, 1.3578e-23f};
|
||||
default:
|
||||
return glm::vec4(0.f);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec4 Sky::GetAerosolScatteringCrossSection() const {
|
||||
switch (m_aerosol_preset) {
|
||||
case AerosolPreset::Rural:
|
||||
return {2.6004e-22f, 2.4844e-22f, 2.8362e-22f, 2.7494e-22f};
|
||||
case AerosolPreset::Urban:
|
||||
return {1.5908e-22f, 1.7711e-22f, 2.0942e-22f, 2.4033e-22f};
|
||||
default:
|
||||
return glm::vec4(0.f);
|
||||
}
|
||||
}
|
||||
|
||||
float Sky::GetAerosolBaseDensity() const {
|
||||
switch (m_aerosol_preset) {
|
||||
case AerosolPreset::Rural:
|
||||
return 8.544e15f;
|
||||
case AerosolPreset::Urban:
|
||||
return 1.3681e17f;
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float Sky::GetAerosolBackgroundDividedByBaseDensity() const {
|
||||
switch (m_aerosol_preset) {
|
||||
case AerosolPreset::Rural:
|
||||
return 2e3f / GetAerosolBaseDensity();
|
||||
case AerosolPreset::Urban:
|
||||
return 2e3f / GetAerosolBaseDensity();
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float Sky::GetAerosolHeightScale() const {
|
||||
switch (m_aerosol_preset) {
|
||||
case AerosolPreset::Rural:
|
||||
return .73f;
|
||||
case AerosolPreset::Urban:
|
||||
return .73f;
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
float Sky::GetFogDensity() const {
|
||||
return 3e7f * glm::pow(0.99937f, glm::min(3e7f, m_visibility));
|
||||
}
|
||||
|
||||
void Sky::UpdateConstants(nvrhi::ICommandList* command_list) {
|
||||
SkyConstants constants{};
|
||||
constants.g_AerosolAbsorptionCrossSection =
|
||||
GetAerosolAbsorptionCrossSection();
|
||||
constants.g_AerosolScatteringCrossSection =
|
||||
GetAerosolScatteringCrossSection();
|
||||
constants.g_AerosolHeightScale = GetAerosolHeightScale();
|
||||
constants.g_AerosolBaseDensity = GetAerosolBaseDensity();
|
||||
constants.g_AerosolBackgroundDividedByBaseDensity =
|
||||
GetAerosolBackgroundDividedByBaseDensity();
|
||||
constants.g_GroundAlbedo = glm::vec4{m_ground_albedo};
|
||||
constants.g_AerosolTurbidity = m_aerosol_turbidity;
|
||||
constants.g_OzoneMean = GetOzoneMean();
|
||||
constants.g_FogDensity = GetFogDensity();
|
||||
constants.g_FogHeightOffset = m_fog_height_offset;
|
||||
constants.g_FogHeightScale = m_fog_height_scale;
|
||||
command_list->writeBuffer(m_sky_constants, &constants, sizeof(constants));
|
||||
}
|
||||
|
||||
void Sky::OnGui() {
|
||||
if (!m_gui_active) return;
|
||||
if (ImGui::Begin("Sky settings", &m_gui_active,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::SliderFloat("Visibility", &m_visibility, 10.f, 3e4f);
|
||||
ImGui::SliderFloat("Fog Offset", &m_fog_height_offset, 0.f, 10.f);
|
||||
ImGui::SliderFloat("Fog Height", &m_fog_height_scale, .01f, 10.f);
|
||||
if (ImGui::BeginCombo("Aerosol Type",
|
||||
GetAerosolTypeDesc(m_aerosol_preset))) {
|
||||
for (int i = 0; i < static_cast<int>(AerosolPreset::Num); ++i) {
|
||||
AerosolPreset preset = static_cast<AerosolPreset>(i);
|
||||
if (ImGui::Selectable(GetAerosolTypeDesc(preset),
|
||||
preset == m_aerosol_preset)) {
|
||||
m_aerosol_preset = preset;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SliderFloat("Aerosol Turbidity", &m_aerosol_turbidity, 0., 10.);
|
||||
ImGui::SliderFloat("Ground Albedo", &m_ground_albedo, 0., 10.);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
void Sky::ShowGui() { m_gui_active = true; }
|
||||
|
||||
const char* Sky::GetAerosolTypeDesc(AerosolPreset preset) {
|
||||
switch (preset) {
|
||||
case AerosolPreset::Rural:
|
||||
return "Rural";
|
||||
case AerosolPreset::Urban:
|
||||
return "Urban";
|
||||
default:
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec4 Sky::GetMolecularScatteringCoefficient(float h) const {
|
||||
return molecular_scattering_coefficient_base *
|
||||
glm::exp(-0.07771971f * glm::pow(h, 1.16364243f));
|
||||
}
|
||||
|
||||
glm::vec4 Sky::GetMolecularAbsorptionCoefficient(float h) const {
|
||||
h += 1e-4f; // Avoid division by 0
|
||||
float t = glm::log(h) - 3.22261f;
|
||||
float density = 3.78547397e17f * (1.f / h) * glm::exp(-t * t * 5.55555555f);
|
||||
return ozone_absorption_cross_section * GetOzoneMean() * density;
|
||||
}
|
||||
|
||||
glm::vec4 Sky::GetFogScatteringCoefficient(float h) const {
|
||||
if (GetFogDensity() > 0.f) {
|
||||
return fog_scattering_cross_section * GetFogDensity() *
|
||||
glm::min(1.f,
|
||||
glm::exp((-h + m_fog_height_offset) / m_fog_height_scale));
|
||||
} else {
|
||||
return glm::vec4{0.f};
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Sky::CalcSunColor() const {
|
||||
glm::vec4 transmittance =
|
||||
ComputeTransmittance(simulation::Environment.sun().getDirection(), 32);
|
||||
return LinearSrgbFromSpectralSamples(sun_spectral_irradiance *
|
||||
transmittance) *
|
||||
glm::exp(EXPOSURE);
|
||||
}
|
||||
|
||||
glm::vec3 Sky::CalcMoonColor() const {
|
||||
float phase = glm::acos(dot(simulation::Environment.sun().getDirection(),
|
||||
simulation::Environment.moon().getDirection())) *
|
||||
glm::one_over_pi<float>();
|
||||
glm::vec4 transmittance =
|
||||
ComputeTransmittance(simulation::Environment.moon().getDirection(), 32);
|
||||
return .07f * phase *
|
||||
LinearSrgbFromSpectralSamples(sun_spectral_irradiance *
|
||||
transmittance) *
|
||||
glm::exp(EXPOSURE);
|
||||
}
|
||||
|
||||
void Sky::CalcLighting(glm::vec3& direction, glm::vec3& color) const {
|
||||
const glm::vec3 moon = CalcMoonColor();
|
||||
const glm::vec3 sun = CalcSunColor();
|
||||
if (dot(moon, moon) > dot(sun, sun)) {
|
||||
direction = simulation::Environment.moon().getDirection();
|
||||
color = moon;
|
||||
} else {
|
||||
direction = simulation::Environment.sun().getDirection();
|
||||
color = sun;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Sky::LinearSrgbFromSpectralSamples(glm::vec4 L) {
|
||||
glm::vec3 c = M * L;
|
||||
return c.r * glm::vec3(0.4123908f, 0.21263901f, 0.01933082f) +
|
||||
c.g * glm::vec3(0.35758434f, 0.71516868f, 0.11919478f) +
|
||||
c.b * glm::vec3(0.18048079f, 0.07219232f, 0.95053215f);
|
||||
}
|
||||
|
||||
float Sky::RaySphereIntersection(glm::vec3 ro, glm::vec3 rd, float radius) {
|
||||
float b = glm::dot(ro, rd);
|
||||
float c = glm::dot(ro, ro) - radius * radius;
|
||||
if (c > 0.f && b > 0.f) return -1.f;
|
||||
float d = b * b - c;
|
||||
if (d < 0.f) return -1.f;
|
||||
if (d > b * b) return (-b + glm::sqrt(d));
|
||||
return (-b - glm::sqrt(d));
|
||||
}
|
||||
|
||||
float Sky::GetAerosolDensity(float h) const {
|
||||
return GetAerosolBaseDensity() * (exp(-h / GetAerosolHeightScale()) +
|
||||
GetAerosolBackgroundDividedByBaseDensity());
|
||||
}
|
||||
|
||||
void Sky::GetAtmosphereCollisionCoefficients(
|
||||
float h, glm::vec4& aerosol_absorption, glm::vec4& aerosol_scattering,
|
||||
glm::vec4& molecular_absorption, glm::vec4& molecular_scattering,
|
||||
glm::vec4& fog_scattering, glm::vec4& extinction) const {
|
||||
h = glm::max(h, 1.e-3f); // In case height is negative
|
||||
h *= 1.e-3f;
|
||||
float aerosol_density = GetAerosolDensity(h);
|
||||
aerosol_absorption = GetAerosolAbsorptionCrossSection() * aerosol_density *
|
||||
m_aerosol_turbidity;
|
||||
aerosol_scattering = GetAerosolScatteringCrossSection() * aerosol_density *
|
||||
m_aerosol_turbidity;
|
||||
molecular_absorption = GetMolecularAbsorptionCoefficient(h);
|
||||
molecular_scattering = GetMolecularScatteringCoefficient(h);
|
||||
fog_scattering = GetFogScatteringCoefficient(h);
|
||||
extinction = aerosol_absorption + aerosol_scattering + molecular_absorption +
|
||||
molecular_scattering + fog_scattering;
|
||||
}
|
||||
|
||||
glm::vec4 Sky::ComputeTransmittance(const glm::vec3& ray_dir, int steps) const {
|
||||
glm::vec3 ray_origin{0.f, EARTH_RADIUS + Global.pCamera.Pos.y, 0.f};
|
||||
glm::vec4 transmittance{1.f};
|
||||
float t_max = std::numeric_limits<float>::max();
|
||||
float atmos_dist =
|
||||
RaySphereIntersection(ray_origin, ray_dir, ATMOSPHERE_RADIUS);
|
||||
|
||||
if (RaySphereIntersection(ray_origin, ray_dir, EARTH_RADIUS) >= 0.0) {
|
||||
return glm::vec4(0.f, 0.f, 0.f, 0.f);
|
||||
}
|
||||
|
||||
t_max = glm::min(t_max, atmos_dist);
|
||||
float dt = t_max / float(steps);
|
||||
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
float t = (float(i) + 0.5) * dt;
|
||||
glm::vec3 x_t = ray_origin + ray_dir * t;
|
||||
|
||||
float distance_to_earth_center = glm::length(x_t);
|
||||
float altitude = distance_to_earth_center - EARTH_RADIUS;
|
||||
|
||||
glm::vec4 aerosol_absorption, aerosol_scattering;
|
||||
glm::vec4 molecular_absorption, molecular_scattering;
|
||||
glm::vec4 fog_scattering;
|
||||
glm::vec4 extinction;
|
||||
GetAtmosphereCollisionCoefficients(
|
||||
altitude, aerosol_absorption, aerosol_scattering, molecular_absorption,
|
||||
molecular_scattering, fog_scattering, extinction);
|
||||
|
||||
glm::vec4 step_transmittance = glm::exp(-dt * extinction);
|
||||
transmittance *= step_transmittance;
|
||||
}
|
||||
return transmittance;
|
||||
}
|
||||
|
||||
void SkyTransmittancePass::Init() {
|
||||
m_pixel_shader =
|
||||
m_backend->CreateShader("sky_transmittance", nvrhi::ShaderType::Pixel);
|
||||
m_output = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(nvrhi::Format::RGBA8_UNORM)
|
||||
.setWidth(128)
|
||||
.setHeight(128)
|
||||
.setIsRenderTarget(true)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::Pixel, 0,
|
||||
nvrhi::BindingSetDesc().addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(13, m_sky->m_sky_constants)),
|
||||
m_binding_layout, m_binding_set);
|
||||
m_framebuffer = m_backend->GetDevice()->createFramebuffer(
|
||||
nvrhi::FramebufferDesc().addColorAttachment(m_output));
|
||||
FullScreenPass::Init();
|
||||
}
|
||||
|
||||
void SkyTransmittancePass::CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
|
||||
FullScreenPass::CreatePipelineDesc(pipeline_desc);
|
||||
pipeline_desc.setPixelShader(m_pixel_shader);
|
||||
pipeline_desc.addBindingLayout(m_binding_layout);
|
||||
}
|
||||
|
||||
void SkyTransmittancePass::Render(nvrhi::ICommandList* command_list) {
|
||||
command_list->beginMarker("Sky transmittance");
|
||||
nvrhi::GraphicsState graphics_state;
|
||||
InitState(graphics_state);
|
||||
graphics_state.addBindingSet(m_binding_set);
|
||||
command_list->setGraphicsState(graphics_state);
|
||||
Draw(command_list);
|
||||
command_list->endMarker();
|
||||
}
|
||||
|
||||
nvrhi::IFramebuffer* SkyTransmittancePass::GetFramebuffer() {
|
||||
return m_framebuffer;
|
||||
}
|
||||
|
||||
void SkyAerialLut::Init() {
|
||||
m_sky_width = 256;
|
||||
m_sky_height = 512;
|
||||
m_lut_width = 128;
|
||||
m_lut_height = 256;
|
||||
m_lut_slices = 16;
|
||||
m_constant_buffer = m_sky->m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateVolatileConstantBufferDesc(
|
||||
sizeof(DispatchConstants), "Sky Aerial LUT Dispatch Constants", 16));
|
||||
m_lut = m_sky->m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDimension(nvrhi::TextureDimension::Texture2DArray)
|
||||
.setWidth(m_lut_width)
|
||||
.setHeight(m_lut_height)
|
||||
.setArraySize(m_lut_slices)
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::Common)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Sky Aerial LUT"));
|
||||
m_sky_texture = m_sky->m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setWidth(m_sky_width)
|
||||
.setHeight(m_sky_height)
|
||||
.setFormat(nvrhi::Format::RGBA16_FLOAT)
|
||||
.setIsUAV(true)
|
||||
.setInitialState(nvrhi::ResourceStates::Common)
|
||||
.setKeepInitialState(true)
|
||||
.setDebugName("Sky Texture"));
|
||||
auto shader_lut = m_sky->m_backend->CreateShader("sky_aerial_lut",
|
||||
nvrhi::ShaderType::Compute);
|
||||
auto shader_sky =
|
||||
m_sky->m_backend->CreateShader("sky", nvrhi::ShaderType::Compute);
|
||||
auto sampler = m_sky->m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(nvrhi::SamplerAddressMode::ClampToEdge)
|
||||
.setAllFilters(true));
|
||||
nvrhi::BindingLayoutHandle binding_layout_lut;
|
||||
nvrhi::BindingLayoutHandle binding_layout_sky;
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_sky->m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(13, m_sky->m_sky_constants))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(0, m_lut))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
13, m_sky->m_transmittance_pass->m_output))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(13, sampler)),
|
||||
binding_layout_lut, m_bindings_lut);
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_sky->m_backend->GetDevice(), nvrhi::ShaderType::Compute, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::ConstantBuffer(0, m_constant_buffer))
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(13, m_sky->m_sky_constants))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(1, m_sky_texture))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
13, m_sky->m_transmittance_pass->m_output))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(13, sampler)),
|
||||
binding_layout_sky, m_bindings_sky);
|
||||
m_pso_lut = m_sky->m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout_lut)
|
||||
.setComputeShader(shader_lut));
|
||||
m_pso_sky = m_sky->m_backend->GetDevice()->createComputePipeline(
|
||||
nvrhi::ComputePipelineDesc()
|
||||
.addBindingLayout(binding_layout_sky)
|
||||
.setComputeShader(shader_sky));
|
||||
}
|
||||
|
||||
void SkyAerialLut::Render(nvrhi::ICommandList* command_list,
|
||||
const glm::dmat4& projection,
|
||||
const glm::dmat4& view) {
|
||||
{
|
||||
DispatchConstants constants{};
|
||||
constants.g_InverseView = static_cast<glm::mat3>(glm::inverse(view));
|
||||
constants.g_InverseProjection = glm::inverse(projection);
|
||||
constants.g_SunDir = simulation::Environment.sun().getDirection();
|
||||
constants.g_MoonDir = simulation::Environment.moon().getDirection();
|
||||
constants.g_Altitude = Global.pCamera.Pos.y;
|
||||
constants.g_MaxDepth = Global.BaseDrawRange * Global.fDistanceFactor;
|
||||
command_list->writeBuffer(m_constant_buffer, &constants, sizeof(constants));
|
||||
}
|
||||
|
||||
command_list->setComputeState(
|
||||
nvrhi::ComputeState().setPipeline(m_pso_lut).addBindingSet(
|
||||
m_bindings_lut));
|
||||
command_list->dispatch((m_lut_width + 7) / 8, (m_lut_height + 7) / 8, 1);
|
||||
|
||||
command_list->setComputeState(
|
||||
nvrhi::ComputeState().setPipeline(m_pso_sky).addBindingSet(
|
||||
m_bindings_sky));
|
||||
command_list->dispatch((m_sky_width + 7) / 8, (m_sky_height + 7) / 8, 1);
|
||||
}
|
||||
118
betterRenderer/renderer/source/sky.h
Normal file
118
betterRenderer/renderer/source/sky.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include "fullscreenpass.h"
|
||||
#include "nvrenderer/resource_registry.h"
|
||||
|
||||
struct Sky : public MaResourceRegistry {
|
||||
Sky(class NvRenderer* renderer);
|
||||
|
||||
void Init();
|
||||
void Render(nvrhi::ICommandList* command_list, const glm::dmat4& projection,
|
||||
const glm::dmat4& view);
|
||||
struct SkyConstants {
|
||||
glm::vec4 g_AerosolAbsorptionCrossSection;
|
||||
glm::vec4 g_AerosolScatteringCrossSection;
|
||||
glm::vec4 g_GroundAlbedo;
|
||||
float g_AerosolHeightScale;
|
||||
float g_AerosolTurbidity;
|
||||
float g_AerosolBaseDensity;
|
||||
float g_AerosolBackgroundDividedByBaseDensity;
|
||||
float g_OzoneMean;
|
||||
float g_FogDensity;
|
||||
float g_FogHeightOffset;
|
||||
float g_FogHeightScale;
|
||||
};
|
||||
nvrhi::BufferHandle m_sky_constants;
|
||||
enum class AerosolPreset {
|
||||
Rural,
|
||||
Urban,
|
||||
Num
|
||||
} m_aerosol_preset = AerosolPreset::Rural;
|
||||
float m_visibility = 1e4f;
|
||||
float m_fog_height_offset = 0.f;
|
||||
float m_fog_height_scale = .1f;
|
||||
float m_aerosol_turbidity = 1.f;
|
||||
float m_ground_albedo = .3f;
|
||||
bool m_gui_active = false;
|
||||
void UpdateConstants(nvrhi::ICommandList* command_list);
|
||||
void OnGui();
|
||||
void ShowGui();
|
||||
static const char* GetAerosolTypeDesc(AerosolPreset preset);
|
||||
class NvRendererBackend* m_backend;
|
||||
std::shared_ptr<struct SkyTransmittancePass> m_transmittance_pass;
|
||||
std::shared_ptr<struct SkyAerialLut> m_aerial_lut;
|
||||
float GetOzoneMean() const;
|
||||
glm::vec4 GetAerosolAbsorptionCrossSection() const;
|
||||
glm::vec4 GetAerosolScatteringCrossSection() const;
|
||||
float GetAerosolBaseDensity() const;
|
||||
float GetAerosolBackgroundDividedByBaseDensity() const;
|
||||
float GetAerosolHeightScale() const;
|
||||
float GetFogDensity() const;
|
||||
glm::vec4 GetMolecularScatteringCoefficient(float h) const;
|
||||
glm::vec4 GetMolecularAbsorptionCoefficient(float h) const;
|
||||
glm::vec4 GetFogScatteringCoefficient(float h) const;
|
||||
glm::vec3 CalcSunColor() const;
|
||||
glm::vec3 CalcMoonColor() const;
|
||||
void CalcLighting(glm::vec3& direction, glm::vec3& color) const;
|
||||
static glm::vec3 LinearSrgbFromSpectralSamples(glm::vec4 L);
|
||||
static float RaySphereIntersection(glm::vec3 ro, glm::vec3 rd, float radius);
|
||||
float GetAerosolDensity(float h) const;
|
||||
void GetAtmosphereCollisionCoefficients(
|
||||
float h, glm::vec4& aerosol_absorption, glm::vec4& aerosol_scattering,
|
||||
glm::vec4& molecular_absorption, glm::vec4& molecular_scattering,
|
||||
glm::vec4& fog_scattering, glm::vec4& extinction) const;
|
||||
glm::vec4 ComputeTransmittance(const glm::vec3& dir, int steps) const;
|
||||
};
|
||||
|
||||
struct SkyTransmittancePass : public FullScreenPass {
|
||||
SkyTransmittancePass(Sky* sky) : FullScreenPass(sky->m_backend), m_sky(sky) {}
|
||||
|
||||
virtual void Init() override;
|
||||
|
||||
virtual void CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
|
||||
|
||||
virtual void Render(nvrhi::ICommandList* command_list) override;
|
||||
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
nvrhi::BindingSetHandle m_binding_set;
|
||||
nvrhi::ShaderHandle m_pixel_shader;
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
|
||||
nvrhi::ITexture* m_source;
|
||||
nvrhi::Format m_output_format;
|
||||
|
||||
nvrhi::TextureHandle m_output;
|
||||
|
||||
Sky* m_sky;
|
||||
};
|
||||
|
||||
struct SkyAerialLut {
|
||||
struct DispatchConstants {
|
||||
glm::mat4 g_InverseView; // DO NOT TRANSPOSE
|
||||
glm::mat4 g_InverseProjection;
|
||||
glm::vec3 g_SunDir;
|
||||
float g_Altitude;
|
||||
glm::vec3 g_MoonDir;
|
||||
float g_MaxDepth;
|
||||
};
|
||||
SkyAerialLut(Sky* sky) : m_sky(sky) {}
|
||||
void Init();
|
||||
void Render(nvrhi::ICommandList* command_list, const glm::dmat4& projection,
|
||||
const glm::dmat4& view);
|
||||
nvrhi::TextureHandle m_lut;
|
||||
nvrhi::TextureHandle m_sky_texture;
|
||||
nvrhi::BufferHandle m_constant_buffer;
|
||||
nvrhi::ComputePipelineHandle m_pso_lut;
|
||||
nvrhi::ComputePipelineHandle m_pso_sky;
|
||||
int m_sky_width;
|
||||
int m_sky_height;
|
||||
int m_lut_width;
|
||||
int m_lut_height;
|
||||
int m_lut_slices;
|
||||
nvrhi::BindingSetHandle m_bindings_lut;
|
||||
nvrhi::BindingSetHandle m_bindings_sky;
|
||||
Sky* m_sky;
|
||||
};
|
||||
262
betterRenderer/renderer/source/ssao.cpp
Normal file
262
betterRenderer/renderer/source/ssao.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
#include "ssao.h"
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "XeGTAO.h"
|
||||
#include "gbuffer.h"
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
NvSsao::NvSsao(NvRenderer* renderer)
|
||||
: m_backend(renderer->m_backend.get()),
|
||||
m_gbuffer(renderer->m_gbuffer.get()),
|
||||
m_width(0),
|
||||
m_height(0) {}
|
||||
|
||||
void NvSsao::Init() {
|
||||
using namespace nvrhi;
|
||||
m_CSPrefilterDepths16x16 = m_backend->CreateShader(
|
||||
"gtao_CSPrefilterDepths16x16", ShaderType::Compute);
|
||||
m_CSGTAOLow = m_backend->CreateShader("gtao_CSGTAOLow", ShaderType::Compute);
|
||||
m_CSGTAOMedium =
|
||||
m_backend->CreateShader("gtao_CSGTAOMedium", ShaderType::Compute);
|
||||
m_CSGTAOHigh =
|
||||
m_backend->CreateShader("gtao_CSGTAOHigh", ShaderType::Compute);
|
||||
m_CSGTAOUltra =
|
||||
m_backend->CreateShader("gtao_CSGTAOUltra", ShaderType::Compute);
|
||||
m_CSDenoisePass =
|
||||
m_backend->CreateShader("gtao_CSDenoisePass", ShaderType::Compute);
|
||||
m_CSDenoiseLastPass =
|
||||
m_backend->CreateShader("gtao_CSDenoiseLastPass", ShaderType::Compute);
|
||||
|
||||
m_width = m_gbuffer->m_framebuffer->getFramebufferInfo().width;
|
||||
m_height = m_gbuffer->m_framebuffer->getFramebufferInfo().height;
|
||||
|
||||
m_constantBuffer = m_backend->GetDevice()->createBuffer(
|
||||
nvrhi::utils::CreateStaticConstantBufferDesc(
|
||||
sizeof(XeGTAO::GTAOConstants), "XeGTAO Constants")
|
||||
.setInitialState(ResourceStates::ConstantBuffer)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_workingDepths = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("XeGTAO Working Depths")
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setFormat(Format::R32_FLOAT)
|
||||
.setMipLevels(XE_GTAO_DEPTH_MIP_LEVELS)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_workingEdges = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("XeGTAO Working Edges")
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setFormat(Format::R8_UNORM)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_debugImage = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("XeGTAO Debug Image")
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setFormat(Format::R11G11B10_FLOAT)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_workingAOTerm = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("XeGTAO Working AO Term A")
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setFormat(Format::R8_UINT)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_workingAOTermPong = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("XeGTAO Working AO Term B")
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setFormat(Format::R8_UINT)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
m_outputAO = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setDebugName("XeGTAO Output AO")
|
||||
.setWidth(m_width)
|
||||
.setHeight(m_height)
|
||||
.setIsUAV(true)
|
||||
.setFormat(Format::R8_UNORM)
|
||||
.setIsTypeless(true)
|
||||
.setInitialState(ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
|
||||
SamplerHandle sampler_point = m_SamplerPoint = m_backend->GetDevice()->createSampler(
|
||||
nvrhi::SamplerDesc()
|
||||
.setAllAddressModes(SamplerAddressMode::Clamp)
|
||||
.setAllFilters(false));
|
||||
|
||||
{
|
||||
BindingLayoutHandle blPrefilterDepths;
|
||||
BindingSetDesc bsDescPrefilter =
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer))
|
||||
.addItem(BindingSetItem::Texture_SRV(0, m_gbuffer->m_gbuffer_depth))
|
||||
.addItem(BindingSetItem::Sampler(0, sampler_point));
|
||||
for (int i = 0; i < XE_GTAO_DEPTH_MIP_LEVELS; ++i) {
|
||||
bsDescPrefilter.addItem(
|
||||
BindingSetItem::Texture_UAV(i, m_workingDepths, Format::UNKNOWN,
|
||||
TextureSubresourceSet(i, 1, 0, 1)));
|
||||
}
|
||||
utils::CreateBindingSetAndLayout(m_backend->GetDevice(),
|
||||
ShaderType::Compute, 0, bsDescPrefilter,
|
||||
blPrefilterDepths, m_BSPrefilterDepths);
|
||||
m_PSOPrefilterDepths = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.setComputeShader(m_CSPrefilterDepths16x16)
|
||||
.addBindingLayout(blPrefilterDepths));
|
||||
}
|
||||
|
||||
{
|
||||
BindingLayoutHandle blGTAO;
|
||||
utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), ShaderType::Compute, 0,
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer))
|
||||
.addItem(BindingSetItem ::Texture_SRV(0, m_workingDepths))
|
||||
.addItem(
|
||||
BindingSetItem::Texture_SRV(1, m_gbuffer->m_gbuffer_normal))
|
||||
.addItem(BindingSetItem::Texture_UAV(0, m_workingAOTerm))
|
||||
.addItem(BindingSetItem::Texture_UAV(1, m_workingEdges))
|
||||
.addItem(BindingSetItem::Texture_UAV(2, m_debugImage))
|
||||
.addItem(BindingSetItem::Sampler(0, sampler_point)),
|
||||
blGTAO, m_BSGTAO);
|
||||
m_PSOGTAO = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.setComputeShader(m_CSGTAOHigh)
|
||||
.addBindingLayout(blGTAO));
|
||||
}
|
||||
|
||||
{
|
||||
BindingLayoutHandle blDenoise;
|
||||
m_BLDenoise = m_backend->GetDevice()->createBindingLayout(
|
||||
BindingLayoutDesc()
|
||||
.setVisibility(nvrhi::ShaderType::Compute)
|
||||
.addItem(BindingLayoutItem::ConstantBuffer(0))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(0))
|
||||
.addItem(BindingLayoutItem::Texture_SRV(1))
|
||||
.addItem(BindingLayoutItem::Texture_UAV(0))
|
||||
.addItem(BindingLayoutItem::Texture_UAV(1))
|
||||
.addItem(BindingLayoutItem::Texture_UAV(2))
|
||||
.addItem(BindingLayoutItem::Sampler(0)));
|
||||
utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), ShaderType::Compute, 0,
|
||||
BindingSetDesc()
|
||||
.addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer))
|
||||
.addItem(BindingSetItem::Texture_SRV(1, m_workingEdges))
|
||||
.addItem(BindingSetItem::Texture_UAV(2, m_debugImage))
|
||||
.addItem(BindingSetItem::Sampler(0, sampler_point)),
|
||||
blDenoise, m_BSDenoise);
|
||||
m_PSODenoise = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.setComputeShader(m_CSDenoisePass)
|
||||
//.addBindingLayout(blDenoise)
|
||||
.addBindingLayout(m_BLDenoise));
|
||||
m_PSODenoiseLastPass = m_backend->GetDevice()->createComputePipeline(
|
||||
ComputePipelineDesc()
|
||||
.setComputeShader(m_CSDenoiseLastPass)
|
||||
//.addBindingLayout(blDenoise)
|
||||
.addBindingLayout(m_BLDenoise));
|
||||
}
|
||||
}
|
||||
|
||||
void NvSsao::Render(nvrhi::ICommandList* command_list,
|
||||
const glm::mat4& projection, size_t frame_index) {
|
||||
command_list->beginMarker("XeGTAO");
|
||||
|
||||
XeGTAO::GTAOConstants constants{};
|
||||
XeGTAO::GTAOSettings settings{};
|
||||
settings.QualityLevel = 2;
|
||||
settings.Radius = 10.f;
|
||||
settings.ThinOccluderCompensation = .7f;
|
||||
XeGTAO::GTAOUpdateConstants(constants, m_width, m_height, settings,
|
||||
glm::value_ptr(projection), false, frame_index);
|
||||
command_list->writeBuffer(m_constantBuffer, &constants, sizeof(constants));
|
||||
|
||||
{
|
||||
command_list->beginMarker("Prefilter Depth");
|
||||
nvrhi::ComputeState state;
|
||||
state.setPipeline(m_PSOPrefilterDepths);
|
||||
state.addBindingSet(m_BSPrefilterDepths);
|
||||
|
||||
command_list->setComputeState(state);
|
||||
command_list->dispatch((m_width + 16 - 1) / 16, (m_height + 16 - 1) / 16,
|
||||
1);
|
||||
command_list->endMarker();
|
||||
}
|
||||
|
||||
{
|
||||
command_list->beginMarker("Main Pass");
|
||||
nvrhi::ComputeState state;
|
||||
state.setPipeline(m_PSOGTAO);
|
||||
state.addBindingSet(m_BSGTAO);
|
||||
|
||||
command_list->setComputeState(state);
|
||||
command_list->dispatch(
|
||||
(m_width + XE_GTAO_NUMTHREADS_X - 1) / XE_GTAO_NUMTHREADS_X,
|
||||
(m_height + XE_GTAO_NUMTHREADS_Y - 1) / XE_GTAO_NUMTHREADS_Y, 1);
|
||||
command_list->endMarker();
|
||||
}
|
||||
|
||||
{
|
||||
command_list->beginMarker("Denoise Pass");
|
||||
int num_passes = 4;
|
||||
nvrhi::ITexture* ping = m_workingAOTerm;
|
||||
nvrhi::ITexture* pong = m_workingAOTermPong;
|
||||
for (int i = 0; i < num_passes; ++i) {
|
||||
nvrhi::ComputeState state;
|
||||
bool last_pass = i == num_passes - 1;
|
||||
auto bindingSet = m_backend->GetDevice()->createBindingSet(
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(
|
||||
nvrhi::BindingSetItem::ConstantBuffer(0, m_constantBuffer))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, ping))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
1, m_gbuffer->m_gbuffer_normal))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(
|
||||
0, last_pass ? m_outputAO.Get() : pong,
|
||||
nvrhi::Format::R8_UINT))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(1, m_workingEdges))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_UAV(2, m_debugImage))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(0, m_SamplerPoint)),
|
||||
m_BLDenoise);
|
||||
state.setPipeline(last_pass ? m_PSODenoiseLastPass : m_PSODenoise);
|
||||
// state.addBindingSet(m_BSDenoise);
|
||||
state.addBindingSet(bindingSet);
|
||||
|
||||
command_list->setComputeState(state);
|
||||
command_list->dispatch(
|
||||
(m_width + (XE_GTAO_NUMTHREADS_X * 2) - 1) /
|
||||
(XE_GTAO_NUMTHREADS_X * 2),
|
||||
(m_height + XE_GTAO_NUMTHREADS_Y - 1) / XE_GTAO_NUMTHREADS_Y, 1);
|
||||
std::swap(ping, pong);
|
||||
}
|
||||
command_list->endMarker();
|
||||
|
||||
command_list->setTextureState(m_outputAO, nvrhi::AllSubresources,
|
||||
nvrhi::ResourceStates::ShaderResource);
|
||||
}
|
||||
command_list->endMarker();
|
||||
}
|
||||
55
betterRenderer/renderer/source/ssao.h
Normal file
55
betterRenderer/renderer/source/ssao.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
struct NvSsao {
|
||||
NvSsao(class NvRenderer* renderer);
|
||||
void Init();
|
||||
|
||||
class NvRendererBackend* m_backend;
|
||||
struct NvGbuffer* m_gbuffer;
|
||||
|
||||
nvrhi::ShaderHandle m_CSPrefilterDepths16x16;
|
||||
nvrhi::ComputePipelineHandle m_PSOPrefilterDepths;
|
||||
nvrhi::BindingSetHandle m_BSPrefilterDepths;
|
||||
|
||||
nvrhi::SamplerHandle m_SamplerPoint;
|
||||
|
||||
nvrhi::ShaderHandle m_CSGTAOLow;
|
||||
nvrhi::ShaderHandle m_CSGTAOMedium;
|
||||
nvrhi::ShaderHandle m_CSGTAOHigh;
|
||||
nvrhi::ShaderHandle m_CSGTAOUltra;
|
||||
nvrhi::ComputePipelineHandle m_PSOGTAO;
|
||||
nvrhi::BindingSetHandle m_BSGTAO;
|
||||
|
||||
nvrhi::ShaderHandle m_CSDenoisePass;
|
||||
nvrhi::ShaderHandle m_CSDenoiseLastPass;
|
||||
nvrhi::ComputePipelineHandle m_PSODenoise;
|
||||
nvrhi::ComputePipelineHandle m_PSODenoiseLastPass;
|
||||
nvrhi::BindingLayoutHandle m_BLDenoise;
|
||||
nvrhi::BindingSetHandle m_BSDenoise;
|
||||
|
||||
nvrhi::ShaderHandle m_CSGenerateNormals;
|
||||
nvrhi::ComputePipelineHandle m_PSOGenerateNormals;
|
||||
nvrhi::BindingSetHandle m_BSGenerateNormals;
|
||||
|
||||
nvrhi::TextureHandle m_workingDepths;
|
||||
nvrhi::TextureHandle m_workingEdges;
|
||||
nvrhi::TextureHandle m_workingAOTerm;
|
||||
nvrhi::TextureHandle m_workingAOTermPong;
|
||||
nvrhi::TextureHandle m_workingNormals;
|
||||
nvrhi::TextureHandle m_debugImage;
|
||||
nvrhi::TextureHandle m_outputAO;
|
||||
nvrhi::BufferHandle m_constantBuffer;
|
||||
|
||||
|
||||
int m_width;
|
||||
int m_height;
|
||||
|
||||
void Render(nvrhi::ICommandList* command_list, const glm::mat4& projection,
|
||||
size_t frame_index);
|
||||
};
|
||||
7985
betterRenderer/renderer/source/stbi/stb_image.h
Normal file
7985
betterRenderer/renderer/source/stbi/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
10365
betterRenderer/renderer/source/stbi/stb_image_resize2.h
Normal file
10365
betterRenderer/renderer/source/stbi/stb_image_resize2.h
Normal file
File diff suppressed because it is too large
Load Diff
9307
betterRenderer/renderer/source/tinyexr.h
Normal file
9307
betterRenderer/renderer/source/tinyexr.h
Normal file
File diff suppressed because it is too large
Load Diff
68
betterRenderer/renderer/source/tonemap.cpp
Normal file
68
betterRenderer/renderer/source/tonemap.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "tonemap.h"
|
||||
|
||||
#include <nvrhi/utils.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "nvrendererbackend.h"
|
||||
|
||||
void TonemapPass::Init() {
|
||||
nvrhi::utils::CreateBindingSetAndLayout(
|
||||
m_backend->GetDevice(), nvrhi::ShaderType::Pixel, 0,
|
||||
nvrhi::BindingSetDesc()
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(0, m_source))
|
||||
.addItem(nvrhi::BindingSetItem::Texture_SRV(
|
||||
1, static_cast<nvrhi::ITexture*>(
|
||||
m_renderer
|
||||
->GetResource("noise_2d_ldr",
|
||||
nvrhi::ResourceType::Texture_SRV)
|
||||
.m_resource)))
|
||||
.addItem(nvrhi::BindingSetItem::Sampler(
|
||||
0, m_backend->GetDevice()->createSampler(nvrhi::SamplerDesc())))
|
||||
.addItem(nvrhi::BindingSetItem::PushConstants(0, sizeof(glm::vec4))),
|
||||
m_binding_layout, m_binding_set);
|
||||
m_pixel_shader = m_backend->CreateShader("tonemap", nvrhi::ShaderType::Pixel);
|
||||
m_output = m_backend->GetDevice()->createTexture(
|
||||
nvrhi::TextureDesc()
|
||||
.setFormat(m_output_format)
|
||||
.setWidth(m_source->getDesc().width)
|
||||
.setHeight(m_source->getDesc().height)
|
||||
.setIsRenderTarget(true)
|
||||
.setInitialState(nvrhi::ResourceStates::ShaderResource)
|
||||
.setKeepInitialState(true));
|
||||
m_framebuffer = m_backend->GetDevice()->createFramebuffer(
|
||||
nvrhi::FramebufferDesc().addColorAttachment(m_output));
|
||||
FullScreenPass::Init();
|
||||
}
|
||||
|
||||
void TonemapPass::CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) {
|
||||
FullScreenPass::CreatePipelineDesc(pipeline_desc);
|
||||
pipeline_desc.addBindingLayout(m_binding_layout);
|
||||
pipeline_desc.setPixelShader(m_pixel_shader);
|
||||
}
|
||||
|
||||
void TonemapPass::Render(nvrhi::ICommandList* command_list) {
|
||||
nvrhi::GraphicsState graphics_state;
|
||||
InitState(graphics_state);
|
||||
graphics_state.addBindingSet(m_binding_set);
|
||||
command_list->setGraphicsState(graphics_state);
|
||||
|
||||
auto hdr_config = NvRenderer::Config()->m_display.GetHDRConfig();
|
||||
|
||||
struct TonemapConstants {
|
||||
MaConfig::MaTonemapType m_tonemap_function;
|
||||
float m_scene_exposure;
|
||||
float m_scene_nits;
|
||||
float m_scene_gamma;
|
||||
} tonemap_constants{hdr_config.m_tonemap_function,
|
||||
glm::pow(2.f, hdr_config.m_scene_exposure), hdr_config.m_scene_nits,
|
||||
hdr_config.m_scene_gamma};
|
||||
|
||||
command_list->setPushConstants(&tonemap_constants, sizeof(tonemap_constants));
|
||||
|
||||
Draw(command_list);
|
||||
}
|
||||
|
||||
nvrhi::IFramebuffer* TonemapPass::GetFramebuffer() {
|
||||
return FullScreenPass::GetFramebuffer(); // m_framebuffer;
|
||||
}
|
||||
39
betterRenderer/renderer/source/tonemap.h
Normal file
39
betterRenderer/renderer/source/tonemap.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <nvrhi/nvrhi.h>
|
||||
|
||||
#include "nvrenderer/nvrenderer.h"
|
||||
|
||||
#include "fullscreenpass.h"
|
||||
|
||||
struct TonemapPass : public FullScreenPass {
|
||||
TonemapPass(class NvRenderer* renderer, nvrhi::ITexture* source,
|
||||
nvrhi::Format output_format = nvrhi::Format::UNKNOWN)
|
||||
: FullScreenPass(renderer->GetBackend()),
|
||||
m_renderer(renderer),
|
||||
m_source(source),
|
||||
m_output_format(output_format == nvrhi::Format::UNKNOWN
|
||||
? source->getDesc().format
|
||||
: output_format) {}
|
||||
|
||||
virtual void Init() override;
|
||||
|
||||
virtual void CreatePipelineDesc(
|
||||
nvrhi::GraphicsPipelineDesc& pipeline_desc) override;
|
||||
|
||||
virtual void Render(nvrhi::ICommandList* command_list) override;
|
||||
|
||||
virtual nvrhi::IFramebuffer* GetFramebuffer() override;
|
||||
|
||||
nvrhi::BindingLayoutHandle m_binding_layout;
|
||||
nvrhi::BindingSetHandle m_binding_set;
|
||||
nvrhi::ShaderHandle m_pixel_shader;
|
||||
nvrhi::FramebufferHandle m_framebuffer;
|
||||
|
||||
nvrhi::ITexture* m_source;
|
||||
nvrhi::Format m_output_format;
|
||||
|
||||
nvrhi::TextureHandle m_output;
|
||||
|
||||
class NvRenderer* m_renderer;
|
||||
};
|
||||
0
betterRenderer/renderer/source/topo_utils.cpp
Normal file
0
betterRenderer/renderer/source/topo_utils.cpp
Normal file
10
betterRenderer/renderer/source/topo_utils.h
Normal file
10
betterRenderer/renderer/source/topo_utils.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <Model3d.h>
|
||||
|
||||
struct MaTopologyUtils {
|
||||
|
||||
void ConvertTopology(gfx::index_array &Indices, gfx::vertex_array &Vertices,
|
||||
int const Typ) const;
|
||||
|
||||
};
|
||||
1103
betterRenderer/renderer/source/track_batching.cpp
Normal file
1103
betterRenderer/renderer/source/track_batching.cpp
Normal file
File diff suppressed because it is too large
Load Diff
35
betterRenderer/renderer/source/vaTAAShared.h
Normal file
35
betterRenderer/renderer/source/vaTAAShared.h
Normal file
@@ -0,0 +1,35 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Author(s): Lukasz, Migas (Lukasz.Migas@intel.com) - TAA code, Filip Strugar (filip.strugar@intel.com) - integration
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __TAA_SHARED_H__
|
||||
#define __TAA_SHARED_H__
|
||||
|
||||
#include "IntelTAA.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace IntelTAA
|
||||
{
|
||||
#endif // #ifdef __cplusplus
|
||||
|
||||
#define TAA_CONSTANTSBUFFERSLOT 0
|
||||
|
||||
// used in a generic way depending on the shader
|
||||
struct TAAConstants
|
||||
{
|
||||
Matrix4x4 ReprojectionMatrix;
|
||||
FTAAResolve Consts;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // close the namespace
|
||||
#endif // #ifdef __cplusplus
|
||||
|
||||
|
||||
#endif // __TAA_SHARED_H__
|
||||
10
betterRenderer/shaders/CMakeLists.txt
Normal file
10
betterRenderer/shaders/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
file(GLOB_RECURSE src "*.hlsl*" "*.h")
|
||||
list(APPEND src "project.manul")
|
||||
|
||||
get_target_property(dxil_path MaSzynaShaderCompiler dxil_path)
|
||||
|
||||
add_custom_target(
|
||||
${LIBMANUL_NAME}_shaders
|
||||
MaSzynaShaderCompiler "${dxil_path}" "${CMAKE_CURRENT_SOURCE_DIR}/project.manul" "$<TARGET_FILE_DIR:${PROJECT_NAME}>/shaders"
|
||||
DEPENDS ${src})
|
||||
162
betterRenderer/shaders/XeGTAO/GTAO.hlsl
Normal file
162
betterRenderer/shaders/XeGTAO/GTAO.hlsl
Normal file
@@ -0,0 +1,162 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
|
||||
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
|
||||
//
|
||||
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
|
||||
// Version: (see XeGTAO.h) (='.'=)
|
||||
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
|
||||
//
|
||||
// Version history: see XeGTAO.h
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//#include "vaShared.hlsl"
|
||||
//#include "vaNoise.hlsl"
|
||||
|
||||
#ifndef __INTELLISENSE__ // avoids some pesky intellisense errors
|
||||
#include "XeGTAO.h"
|
||||
#endif
|
||||
|
||||
cbuffer GTAOConstantBuffer : register( b0 )
|
||||
{
|
||||
GTAOConstants g_GTAOConsts;
|
||||
}
|
||||
|
||||
#include "XeGTAO.hlsli"
|
||||
|
||||
// input output textures for the first pass (XeGTAO_PrefilterDepths16x16)
|
||||
Texture2D<float> g_srcRawDepth : register( t0 ); // source depth buffer data (in NDC space in DirectX)
|
||||
RWTexture2D<lpfloat> g_outWorkingDepthMIP0 : register( u0 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
|
||||
RWTexture2D<lpfloat> g_outWorkingDepthMIP1 : register( u1 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
|
||||
RWTexture2D<lpfloat> g_outWorkingDepthMIP2 : register( u2 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
|
||||
RWTexture2D<lpfloat> g_outWorkingDepthMIP3 : register( u3 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
|
||||
RWTexture2D<lpfloat> g_outWorkingDepthMIP4 : register( u4 ); // output viewspace depth MIP (these are views into g_srcWorkingDepth MIP levels)
|
||||
|
||||
// input output textures for the second pass (XeGTAO_MainPass)
|
||||
Texture2D<lpfloat> g_srcWorkingDepth : register( t0 ); // viewspace depth with MIPs, output by XeGTAO_PrefilterDepths16x16 and consumed by XeGTAO_MainPass
|
||||
Texture2D<lpfloat3> g_srcNormalmap : register( t1 ); // source normal map (if used)
|
||||
Texture2D<uint> g_srcHilbertLUT : register( t5 ); // hilbert lookup table (if any)
|
||||
RWTexture2D<uint> g_outWorkingAOTerm : register( u0 ); // output AO term (includes bent normals if enabled - packed as R11G11B10 scaled by AO)
|
||||
RWTexture2D<unorm float> g_outWorkingEdges : register( u1 ); // output depth-based edges used by the denoiser
|
||||
RWTexture2D<uint> g_outNormalmap : register( u0 ); // output viewspace normals if generating from depth
|
||||
|
||||
// input output textures for the third pass (XeGTAO_Denoise)
|
||||
Texture2D<uint> g_srcWorkingAOTerm : register( t0 ); // coming from previous pass
|
||||
Texture2D<lpfloat> g_srcWorkingEdges : register( t1 ); // coming from previous pass
|
||||
RWTexture2D<uint> g_outFinalAOTerm : register( u0 ); // final AO term - just 'visibility' or 'visibility + bent normals'
|
||||
|
||||
SamplerState g_samplerPointClamp : register( s0 );
|
||||
|
||||
// Engine-specific normal map loader
|
||||
lpfloat3 LoadNormal( int2 pos )
|
||||
{
|
||||
#if 0
|
||||
// special decoding for external normals stored in 11_11_10 unorm - modify appropriately to support your own encoding
|
||||
uint packedInput = g_srcNormalmap.Load( int3(pos, 0) ).x;
|
||||
float3 unpackedOutput = XeGTAO_R11G11B10_UNORM_to_FLOAT3( packedInput );
|
||||
float3 normal = normalize(unpackedOutput * 2.0.xxx - 1.0.xxx);
|
||||
#else
|
||||
// example of a different encoding
|
||||
float3 encodedNormal = g_srcNormalmap.Load(int3(pos, 0)).xyz;
|
||||
float3 normal = normalize(encodedNormal * 2.0.xxx - 1.0.xxx);
|
||||
normal.z *= -1.0;
|
||||
#endif
|
||||
|
||||
#if 0 // compute worldspace to viewspace here if your engine stores normals in worldspace; if generating normals from depth here, they're already in viewspace
|
||||
normal = mul( (float3x3)g_globals.View, normal );
|
||||
#endif
|
||||
|
||||
return (lpfloat3)normal;
|
||||
}
|
||||
|
||||
// Engine-specific screen & temporal noise loader
|
||||
lpfloat2 SpatioTemporalNoise( uint2 pixCoord, uint temporalIndex ) // without TAA, temporalIndex is always 0
|
||||
{
|
||||
float2 noise;
|
||||
#if 1 // Hilbert curve driving R2 (see https://www.shadertoy.com/view/3tB3z3)
|
||||
#ifdef XE_GTAO_HILBERT_LUT_AVAILABLE // load from lookup texture...
|
||||
uint index = g_srcHilbertLUT.Load( uint3( pixCoord % 64, 0 ) ).x;
|
||||
#else // ...or generate in-place?
|
||||
uint index = HilbertIndex( pixCoord.x, pixCoord.y );
|
||||
#endif
|
||||
index += 288*(temporalIndex%64); // why 288? tried out a few and that's the best so far (with XE_HILBERT_LEVEL 6U) - but there's probably better :)
|
||||
// R2 sequence - see http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
|
||||
return lpfloat2( frac( 0.5 + index * float2(0.75487766624669276005, 0.5698402909980532659114) ) );
|
||||
#else // Pseudo-random (fastest but looks bad - not a good choice)
|
||||
uint baseHash = Hash32( pixCoord.x + (pixCoord.y << 15) );
|
||||
baseHash = Hash32Combine( baseHash, temporalIndex );
|
||||
return lpfloat2( Hash32ToFloat( baseHash ), Hash32ToFloat( Hash32( baseHash ) ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Engine-specific entry point for the first pass
|
||||
[numthreads(8, 8, 1)] // <- hard coded to 8x8; each thread computes 2x2 blocks so processing 16x16 block: Dispatch needs to be called with (width + 16-1) / 16, (height + 16-1) / 16
|
||||
void CSPrefilterDepths16x16( uint2 dispatchThreadID : SV_DispatchThreadID, uint2 groupThreadID : SV_GroupThreadID )
|
||||
{
|
||||
XeGTAO_PrefilterDepths16x16( dispatchThreadID, groupThreadID, g_GTAOConsts, g_srcRawDepth, g_samplerPointClamp, g_outWorkingDepthMIP0, g_outWorkingDepthMIP1, g_outWorkingDepthMIP2, g_outWorkingDepthMIP3, g_outWorkingDepthMIP4 );
|
||||
}
|
||||
|
||||
// Engine-specific entry point for the second pass
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSGTAOLow( const uint2 pixCoord : SV_DispatchThreadID )
|
||||
{
|
||||
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
|
||||
XeGTAO_MainPass( pixCoord, 1, 2, SpatioTemporalNoise(pixCoord, g_GTAOConsts.NoiseIndex), LoadNormal(pixCoord), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
|
||||
}
|
||||
|
||||
// Engine-specific entry point for the second pass
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSGTAOMedium( const uint2 pixCoord : SV_DispatchThreadID )
|
||||
{
|
||||
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
|
||||
XeGTAO_MainPass( pixCoord, 2, 2, SpatioTemporalNoise(pixCoord, g_GTAOConsts.NoiseIndex), LoadNormal(pixCoord), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
|
||||
}
|
||||
|
||||
// Engine-specific entry point for the second pass
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSGTAOHigh( const uint2 pixCoord : SV_DispatchThreadID )
|
||||
{
|
||||
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
|
||||
XeGTAO_MainPass( pixCoord, 3, 3, SpatioTemporalNoise(pixCoord, g_GTAOConsts.NoiseIndex), LoadNormal(pixCoord), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
|
||||
}
|
||||
|
||||
// Engine-specific entry point for the second pass
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSGTAOUltra( const uint2 pixCoord : SV_DispatchThreadID )
|
||||
{
|
||||
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
|
||||
XeGTAO_MainPass( pixCoord, 9, 3, SpatioTemporalNoise( pixCoord, g_GTAOConsts.NoiseIndex ), LoadNormal( pixCoord ), g_GTAOConsts, g_srcWorkingDepth, g_samplerPointClamp, g_outWorkingAOTerm, g_outWorkingEdges );
|
||||
}
|
||||
|
||||
// Engine-specific entry point for the third pass
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSDenoisePass( const uint2 dispatchThreadID : SV_DispatchThreadID )
|
||||
{
|
||||
const uint2 pixCoordBase = dispatchThreadID * uint2( 2, 1 ); // we're computing 2 horizontal pixels at a time (performance optimization)
|
||||
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
|
||||
XeGTAO_Denoise( pixCoordBase, g_GTAOConsts, g_srcWorkingAOTerm, g_srcWorkingEdges, g_samplerPointClamp, g_outFinalAOTerm, false );
|
||||
}
|
||||
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSDenoiseLastPass( const uint2 dispatchThreadID : SV_DispatchThreadID )
|
||||
{
|
||||
const uint2 pixCoordBase = dispatchThreadID * uint2( 2, 1 ); // we're computing 2 horizontal pixels at a time (performance optimization)
|
||||
// g_samplerPointClamp is a sampler with D3D12_FILTER_MIN_MAG_MIP_POINT filter and D3D12_TEXTURE_ADDRESS_MODE_CLAMP addressing mode
|
||||
XeGTAO_Denoise( pixCoordBase, g_GTAOConsts, g_srcWorkingAOTerm, g_srcWorkingEdges, g_samplerPointClamp, g_outFinalAOTerm, true );
|
||||
}
|
||||
|
||||
// Optional screen space viewspace normals from depth generation
|
||||
[numthreads(XE_GTAO_NUMTHREADS_X, XE_GTAO_NUMTHREADS_Y, 1)]
|
||||
void CSGenerateNormals( const uint2 pixCoord : SV_DispatchThreadID )
|
||||
{
|
||||
float3 viewspaceNormal = XeGTAO_ComputeViewspaceNormal( pixCoord, g_GTAOConsts, g_srcRawDepth, g_samplerPointClamp );
|
||||
|
||||
// pack from [-1, 1] to [0, 1] and then to R11G11B10_UNORM
|
||||
g_outNormalmap[ pixCoord ] = XeGTAO_FLOAT3_to_R11G11B10_UNORM( saturate( viewspaceNormal * 0.5 + 0.5 ) );
|
||||
}
|
||||
///
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
263
betterRenderer/shaders/XeGTAO/XeGTAO.h
Normal file
263
betterRenderer/shaders/XeGTAO/XeGTAO.h
Normal file
@@ -0,0 +1,263 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
|
||||
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
|
||||
//
|
||||
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
|
||||
// Version: 1.02 (='.'=)
|
||||
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
|
||||
//
|
||||
// Version history:
|
||||
// 1.00 (2021-08-09): Initial release
|
||||
// 1.01 (2021-09-02): Fix for depth going to inf for 'far' depth buffer values that are out of fp16 range
|
||||
// 1.02 (2021-09-03): More fast_acos use and made final horizon cos clamping optional (off by default): 3-4% perf boost
|
||||
// 1.10 (2021-09-03): Added a couple of heuristics to combat over-darkening errors in certain scenarios
|
||||
// 1.20 (2021-09-06): Optional normal from depth generation is now a standalone pass: no longer integrated into
|
||||
// main XeGTAO pass to reduce complexity and allow reuse; also quality of generated normals improved
|
||||
// 1.21 (2021-09-28): Replaced 'groupshared'-based denoiser with a slightly slower multi-pass one where a 2-pass new
|
||||
// equals 1-pass old. However, 1-pass new is faster than the 1-pass old and enough when TAA enabled.
|
||||
// 1.22 (2021-09-28): Added 'XeGTAO_' prefix to all local functions to avoid name clashes with various user codebases.
|
||||
// 1.30 (2021-10-10): Added support for directional component (bent normals).
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef __XE_GTAO_TYPES_H__
|
||||
#define __XE_GTAO_TYPES_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace XeGTAO
|
||||
{
|
||||
|
||||
// cpp<->hlsl mapping
|
||||
struct Matrix4x4 { float m[16]; };
|
||||
struct Vector3 { float x,y,z; };
|
||||
struct Vector2 { float x,y; };
|
||||
struct Vector2i { int x,y; };
|
||||
typedef unsigned int uint;
|
||||
|
||||
#else // #ifdef __cplusplus
|
||||
|
||||
// cpp<->hlsl mapping
|
||||
#define Matrix4x4 float4x4
|
||||
#define Vector3 float3
|
||||
#define Vector2 float2
|
||||
#define Vector2i int2
|
||||
|
||||
#endif
|
||||
|
||||
// Global consts that need to be visible from both shader and cpp side
|
||||
#define XE_GTAO_DEPTH_MIP_LEVELS 5 // this one is hard-coded to 5 for now
|
||||
#define XE_GTAO_NUMTHREADS_X 8 // these can be changed
|
||||
#define XE_GTAO_NUMTHREADS_Y 8 // these can be changed
|
||||
|
||||
struct GTAOConstants
|
||||
{
|
||||
Vector2i ViewportSize;
|
||||
Vector2 ViewportPixelSize; // .zw == 1.0 / ViewportSize.xy
|
||||
|
||||
Vector2 DepthUnpackConsts;
|
||||
Vector2 CameraTanHalfFOV;
|
||||
|
||||
Vector2 NDCToViewMul;
|
||||
Vector2 NDCToViewAdd;
|
||||
|
||||
Vector2 NDCToViewMul_x_PixelSize;
|
||||
float EffectRadius; // world (viewspace) maximum size of the shadow
|
||||
float EffectFalloffRange;
|
||||
|
||||
float RadiusMultiplier;
|
||||
float Padding0;
|
||||
float FinalValuePower;
|
||||
float DenoiseBlurBeta;
|
||||
|
||||
float SampleDistributionPower;
|
||||
float ThinOccluderCompensation;
|
||||
float DepthMIPSamplingOffset;
|
||||
int NoiseIndex; // frameIndex % 64 if using TAA or 0 otherwise
|
||||
};
|
||||
|
||||
// This is used only for the development (ray traced ground truth).
|
||||
struct ReferenceRTAOConstants
|
||||
{
|
||||
float TotalRaysLength ; // similar to Radius from GTAO
|
||||
float Albedo ; // the assumption on the average material albedo
|
||||
int MaxBounces ; // how many rays to recurse before stopping
|
||||
int AccumulatedFrames ; // how many frames have we accumulated so far (after resetting/clearing). If 0 - this is the first.
|
||||
int AccumulateFrameMax ; // how many frames are we aiming to accumulate; stop when we hit!
|
||||
int Padding0;
|
||||
int Padding1;
|
||||
int Padding2;
|
||||
#ifdef __cplusplus
|
||||
ReferenceRTAOConstants( ) { TotalRaysLength = 1.0f; Albedo = 0.0f; MaxBounces = 1; AccumulatedFrames = 0; AccumulateFrameMax = 0; }
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef XE_GTAO_USE_DEFAULT_CONSTANTS
|
||||
#define XE_GTAO_USE_DEFAULT_CONSTANTS 1
|
||||
#endif
|
||||
|
||||
// some constants reduce performance if provided as dynamic values; if these constants are not required to be dynamic and they match default values,
|
||||
// set XE_GTAO_USE_DEFAULT_CONSTANTS and the code will compile into a more efficient shader
|
||||
#define XE_GTAO_DEFAULT_RADIUS_MULTIPLIER (1.457f ) // allows us to use different value as compared to ground truth radius to counter inherent screen space biases
|
||||
#define XE_GTAO_DEFAULT_FALLOFF_RANGE (0.615f ) // distant samples contribute less
|
||||
#define XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER (2.0f ) // small crevices more important than big surfaces
|
||||
#define XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION (0.0f ) // the new 'thickness heuristic' approach
|
||||
#define XE_GTAO_DEFAULT_FINAL_VALUE_POWER (2.2f ) // modifies the final ambient occlusion value using power function - this allows some of the above heuristics to do different things
|
||||
#define XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET (3.30f ) // main trade-off between performance (memory bandwidth) and quality (temporal stability is the first affected, thin objects next)
|
||||
|
||||
#define XE_GTAO_OCCLUSION_TERM_SCALE (1.5f) // for packing in UNORM (because raw, pre-denoised occlusion term can overshoot 1 but will later average out to 1)
|
||||
|
||||
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
|
||||
#define XE_HILBERT_LEVEL 6U
|
||||
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
|
||||
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
|
||||
inline uint HilbertIndex( uint posX, uint posY )
|
||||
{
|
||||
uint index = 0U;
|
||||
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
|
||||
{
|
||||
uint regionX = ( posX & curLevel ) > 0U;
|
||||
uint regionY = ( posY & curLevel ) > 0U;
|
||||
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
|
||||
if( regionY == 0U )
|
||||
{
|
||||
if( regionX == 1U )
|
||||
{
|
||||
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
|
||||
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
|
||||
}
|
||||
|
||||
uint temp = posX;
|
||||
posX = posY;
|
||||
posY = temp;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
struct GTAOSettings
|
||||
{
|
||||
int QualityLevel = 2; // 0: low; 1: medium; 2: high; 3: ultra
|
||||
int DenoisePasses = 1; // 0: disabled; 1: sharp; 2: medium; 3: soft
|
||||
float Radius = 0.5f; // [0.0, ~ ] World (view) space size of the occlusion sphere.
|
||||
|
||||
// auto-tune-d settings
|
||||
float RadiusMultiplier = XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
|
||||
float FalloffRange = XE_GTAO_DEFAULT_FALLOFF_RANGE;
|
||||
float SampleDistributionPower = XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER;
|
||||
float ThinOccluderCompensation = XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION;
|
||||
float FinalValuePower = XE_GTAO_DEFAULT_FINAL_VALUE_POWER;
|
||||
float DepthMIPSamplingOffset = XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET;
|
||||
};
|
||||
|
||||
template<class T> inline T clamp( T const & v, T const & min, T const & max ) { assert( max >= min ); if( v < min ) return min; if( v > max ) return max; return v; }
|
||||
|
||||
// If using TAA then set noiseIndex to frameIndex % 64 - otherwise use 0
|
||||
inline void GTAOUpdateConstants( XeGTAO::GTAOConstants& consts, int viewportWidth, int viewportHeight, const XeGTAO::GTAOSettings & settings, const float projMatrix[16], bool rowMajor, unsigned int frameCounter )
|
||||
{
|
||||
consts.ViewportSize = { viewportWidth, viewportHeight };
|
||||
consts.ViewportPixelSize = { 1.0f / (float)viewportWidth, 1.0f / (float)viewportHeight };
|
||||
|
||||
float depthLinearizeMul = (rowMajor)?(-projMatrix[3 * 4 + 2]):(-projMatrix[3 + 2 * 4]); // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear );
|
||||
float depthLinearizeAdd = (rowMajor)?( projMatrix[2 * 4 + 2]):( projMatrix[2 + 2 * 4]); // float depthLinearizeAdd = clipFar / ( clipFar - clipNear );
|
||||
|
||||
// correct the handedness issue. need to make sure this below is correct, but I think it is.
|
||||
if( depthLinearizeMul * depthLinearizeAdd < 0 )
|
||||
depthLinearizeAdd = -depthLinearizeAdd;
|
||||
consts.DepthUnpackConsts = { depthLinearizeMul, depthLinearizeAdd };
|
||||
|
||||
float tanHalfFOVY = 1.0f / ((rowMajor)?(projMatrix[1 * 4 + 1]):(projMatrix[1 + 1 * 4])); // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f );
|
||||
float tanHalfFOVX = 1.0F / ((rowMajor)?(projMatrix[0 * 4 + 0]):(projMatrix[0 + 0 * 4])); // = tanHalfFOVY * drawContext.Camera.GetAspect( );
|
||||
consts.CameraTanHalfFOV = { tanHalfFOVX, tanHalfFOVY };
|
||||
|
||||
consts.NDCToViewMul = { consts.CameraTanHalfFOV.x * 2.0f, consts.CameraTanHalfFOV.y * -2.0f };
|
||||
consts.NDCToViewAdd = { consts.CameraTanHalfFOV.x * -1.0f, consts.CameraTanHalfFOV.y * 1.0f };
|
||||
|
||||
consts.NDCToViewMul_x_PixelSize = { consts.NDCToViewMul.x * consts.ViewportPixelSize.x, consts.NDCToViewMul.y * consts.ViewportPixelSize.y };
|
||||
|
||||
consts.EffectRadius = settings.Radius;
|
||||
|
||||
consts.EffectFalloffRange = settings.FalloffRange;
|
||||
consts.DenoiseBlurBeta = (settings.DenoisePasses==0)?(1e4f):(1.2f); // high value disables denoise - more elegant & correct way would be do set all edges to 0
|
||||
|
||||
consts.RadiusMultiplier = settings.RadiusMultiplier;
|
||||
consts.SampleDistributionPower = settings.SampleDistributionPower;
|
||||
consts.ThinOccluderCompensation = settings.ThinOccluderCompensation;
|
||||
consts.FinalValuePower = settings.FinalValuePower;
|
||||
consts.DepthMIPSamplingOffset = settings.DepthMIPSamplingOffset;
|
||||
consts.NoiseIndex = (settings.DenoisePasses>0)?(frameCounter % 64):(0);
|
||||
consts.Padding0 = 0;
|
||||
}
|
||||
|
||||
#ifdef IMGUI_API
|
||||
inline bool GTAOImGuiSettings( XeGTAO::GTAOSettings & settings )
|
||||
{
|
||||
bool hadChanges = false;
|
||||
|
||||
ImGui::PushItemWidth( 120.0f );
|
||||
|
||||
ImGui::Text( "Performance/quality settings:" );
|
||||
|
||||
ImGui::Combo( "Quality Level", &settings.QualityLevel, "Low\0Medium\0High\0Ultra\00");
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Higher quality settings use more samples per pixel but are slower" );
|
||||
settings.QualityLevel = clamp( settings.QualityLevel , 0, 3 );
|
||||
|
||||
ImGui::Combo( "Denoising level", &settings.DenoisePasses, "Disabled\0Sharp\0Medium\0Soft\00");
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "The amount of edge-aware spatial denoise applied" );
|
||||
settings.DenoisePasses = clamp( settings.DenoisePasses , 0, 3 );
|
||||
|
||||
ImGui::Text( "Visual settings:" );
|
||||
|
||||
settings.Radius = clamp( settings.Radius, 0.0f, 100000.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Effect radius", &settings.Radius , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "World (viewspace) effect radius\nExpected range: depends on the scene & requirements, anything from 0.01 to 1000+" );
|
||||
settings.Radius = clamp( settings.Radius , 0.0f, 10000.0f );
|
||||
|
||||
if( ImGui::CollapsingHeader( "Auto-tuned settings (heuristics)" ) )
|
||||
{
|
||||
hadChanges |= ImGui::InputFloat( "Radius multiplier", &settings.RadiusMultiplier , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Multiplies the 'Effect Radius' - used by the auto-tune to best match raytraced ground truth\nExpected range: [0.3, 3.0], defaults to %.3f", XE_GTAO_DEFAULT_RADIUS_MULTIPLIER );
|
||||
settings.RadiusMultiplier = clamp( settings.RadiusMultiplier , 0.3f, 3.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Falloff range", &settings.FalloffRange , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Gently reduce sample impact as it gets out of 'Effect radius' bounds\nExpected range: [0.0, 1.0], defaults to %.3f", XE_GTAO_DEFAULT_FALLOFF_RANGE );
|
||||
settings.FalloffRange = clamp( settings.FalloffRange , 0.0f, 1.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Sample distribution power", &settings.SampleDistributionPower , 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Make samples on a slice equally distributed (1.0) or focus more towards the center (>1.0)\nExpected range: [1.0, 3.0], 2defaults to %.3f", XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER );
|
||||
settings.SampleDistributionPower = clamp( settings.SampleDistributionPower , 1.0f, 3.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Thin occluder compensation", &settings.ThinOccluderCompensation, 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Slightly reduce impact of samples further back to counter the bias from depth-based (incomplete) input scene geometry data\nExpected range: [0.0, 0.7], defaults to %.3f", XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION );
|
||||
settings.ThinOccluderCompensation = clamp( settings.ThinOccluderCompensation , 0.0f, 0.7f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Final power", &settings.FinalValuePower, 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Applies power function to the final value: occlusion = pow( occlusion, finalPower )\nExpected range: [0.5, 5.0], defaults to %.3f", XE_GTAO_DEFAULT_FINAL_VALUE_POWER );
|
||||
settings.FinalValuePower = clamp( settings.FinalValuePower , 0.5f, 5.0f );
|
||||
|
||||
hadChanges |= ImGui::InputFloat( "Depth MIP sampling offset", &settings.DepthMIPSamplingOffset, 0.05f, 0.0f, "%.2f" );
|
||||
if( ImGui::IsItemHovered( ) ) ImGui::SetTooltip( "Mainly performance (texture memory bandwidth) setting but as a side-effect reduces overshadowing by thin objects and increases temporal instability\nExpected range: [2.0, 6.0], defaults to %.3f", XE_GTAO_DEFAULT_DEPTH_MIP_SAMPLING_OFFSET );
|
||||
settings.DepthMIPSamplingOffset = clamp( settings.DepthMIPSamplingOffset , 0.0f, 30.0f );
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth( );
|
||||
|
||||
return hadChanges;
|
||||
}
|
||||
#endif // IMGUI_API
|
||||
|
||||
} // close the namespace
|
||||
|
||||
#endif // #ifdef __cplusplus
|
||||
|
||||
|
||||
#endif // __XE_GTAO_TYPES_H__
|
||||
855
betterRenderer/shaders/XeGTAO/XeGTAO.hlsli
Normal file
855
betterRenderer/shaders/XeGTAO/XeGTAO.hlsli
Normal file
@@ -0,0 +1,855 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// XeGTAO is based on GTAO/GTSO "Jimenez et al. / Practical Real-Time Strategies for Accurate Indirect Occlusion",
|
||||
// https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
|
||||
//
|
||||
// Implementation: Filip Strugar (filip.strugar@intel.com), Steve Mccalla <stephen.mccalla@intel.com> (\_/)
|
||||
// Version: (see XeGTAO.h) (='.'=)
|
||||
// Details: https://github.com/GameTechDev/XeGTAO (")_(")
|
||||
//
|
||||
// Version history: see XeGTAO.h
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
|
||||
#include "vaShared.hlsl"
|
||||
#endif
|
||||
|
||||
#if defined( XE_GTAO_SHOW_NORMALS ) || defined( XE_GTAO_SHOW_EDGES ) || defined( XE_GTAO_SHOW_BENT_NORMALS )
|
||||
RWTexture2D<float4> g_outputDbgImage : register( u2 );
|
||||
#endif
|
||||
|
||||
#include "XeGTAO.h"
|
||||
|
||||
#define XE_GTAO_PI (3.1415926535897932384626433832795)
|
||||
#define XE_GTAO_PI_HALF (1.5707963267948966192313216916398)
|
||||
|
||||
#ifndef XE_GTAO_USE_HALF_FLOAT_PRECISION
|
||||
#define XE_GTAO_USE_HALF_FLOAT_PRECISION 1
|
||||
#endif
|
||||
|
||||
#if defined(XE_GTAO_FP32_DEPTHS) && XE_GTAO_USE_HALF_FLOAT_PRECISION
|
||||
#error Using XE_GTAO_USE_HALF_FLOAT_PRECISION with 32bit depths is not supported yet unfortunately (it is possible to apply fp16 on parts not related to depth but this has not been done yet)
|
||||
#endif
|
||||
|
||||
|
||||
#if (XE_GTAO_USE_HALF_FLOAT_PRECISION != 0)
|
||||
#if 1 // old fp16 approach (<SM6.2)
|
||||
typedef min16float lpfloat;
|
||||
typedef min16float2 lpfloat2;
|
||||
typedef min16float3 lpfloat3;
|
||||
typedef min16float4 lpfloat4;
|
||||
typedef min16float3x3 lpfloat3x3;
|
||||
#else // new fp16 approach (requires SM6.2 and -enable-16bit-types) - WARNING: perf degradation noticed on some HW, while the old (min16float) path is mostly at least a minor perf gain so this is more useful for quality testing
|
||||
typedef float16_t lpfloat;
|
||||
typedef float16_t2 lpfloat2;
|
||||
typedef float16_t3 lpfloat3;
|
||||
typedef float16_t4 lpfloat4;
|
||||
typedef float16_t3x3 lpfloat3x3;
|
||||
#endif
|
||||
#else
|
||||
typedef float lpfloat;
|
||||
typedef float2 lpfloat2;
|
||||
typedef float3 lpfloat3;
|
||||
typedef float4 lpfloat4;
|
||||
typedef float3x3 lpfloat3x3;
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// R11G11B10_UNORM <-> float3
|
||||
float3 XeGTAO_R11G11B10_UNORM_to_FLOAT3( uint packedInput )
|
||||
{
|
||||
float3 unpackedOutput;
|
||||
unpackedOutput.x = (float)( ( packedInput ) & 0x000007ff ) / 2047.0f;
|
||||
unpackedOutput.y = (float)( ( packedInput >> 11 ) & 0x000007ff ) / 2047.0f;
|
||||
unpackedOutput.z = (float)( ( packedInput >> 22 ) & 0x000003ff ) / 1023.0f;
|
||||
return unpackedOutput;
|
||||
}
|
||||
// 'unpackedInput' is float3 and not float3 on purpose as half float lacks precision for below!
|
||||
uint XeGTAO_FLOAT3_to_R11G11B10_UNORM( float3 unpackedInput )
|
||||
{
|
||||
uint packedOutput;
|
||||
packedOutput =( ( uint( VA_SATURATE( unpackedInput.x ) * 2047 + 0.5f ) ) |
|
||||
( uint( VA_SATURATE( unpackedInput.y ) * 2047 + 0.5f ) << 11 ) |
|
||||
( uint( VA_SATURATE( unpackedInput.z ) * 1023 + 0.5f ) << 22 ) );
|
||||
return packedOutput;
|
||||
}
|
||||
//
|
||||
lpfloat4 XeGTAO_R8G8B8A8_UNORM_to_FLOAT4( uint packedInput )
|
||||
{
|
||||
lpfloat4 unpackedOutput;
|
||||
unpackedOutput.x = (lpfloat)( packedInput & 0x000000ff ) / (lpfloat)255;
|
||||
unpackedOutput.y = (lpfloat)( ( ( packedInput >> 8 ) & 0x000000ff ) ) / (lpfloat)255;
|
||||
unpackedOutput.z = (lpfloat)( ( ( packedInput >> 16 ) & 0x000000ff ) ) / (lpfloat)255;
|
||||
unpackedOutput.w = (lpfloat)( packedInput >> 24 ) / (lpfloat)255;
|
||||
return unpackedOutput;
|
||||
}
|
||||
//
|
||||
uint XeGTAO_FLOAT4_to_R8G8B8A8_UNORM( lpfloat4 unpackedInput )
|
||||
{
|
||||
return (( uint( saturate( unpackedInput.x ) * (lpfloat)255 + (lpfloat)0.5 ) ) |
|
||||
( uint( saturate( unpackedInput.y ) * (lpfloat)255 + (lpfloat)0.5 ) << 8 ) |
|
||||
( uint( saturate( unpackedInput.z ) * (lpfloat)255 + (lpfloat)0.5 ) << 16 ) |
|
||||
( uint( saturate( unpackedInput.w ) * (lpfloat)255 + (lpfloat)0.5 ) << 24 ) );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Inputs are screen XY and viewspace depth, output is viewspace position
|
||||
float3 XeGTAO_ComputeViewspacePosition( const float2 screenPos, const float viewspaceDepth, const GTAOConstants consts )
|
||||
{
|
||||
float3 ret;
|
||||
ret.xy = (consts.NDCToViewMul * screenPos.xy + consts.NDCToViewAdd) * viewspaceDepth;
|
||||
ret.z = viewspaceDepth;
|
||||
return ret;
|
||||
}
|
||||
|
||||
float XeGTAO_ScreenSpaceToViewSpaceDepth( const float screenDepth, const GTAOConstants consts )
|
||||
{
|
||||
float depthLinearizeMul = consts.DepthUnpackConsts.x;
|
||||
float depthLinearizeAdd = consts.DepthUnpackConsts.y;
|
||||
// Optimised version of "-cameraClipNear / (cameraClipFar - projDepth * (cameraClipFar - cameraClipNear)) * cameraClipFar"
|
||||
//return 2500. / (.1 - screenDepth * (.1 - 2500.)) * .1;
|
||||
return depthLinearizeMul / (depthLinearizeAdd - screenDepth);
|
||||
}
|
||||
|
||||
lpfloat4 XeGTAO_CalculateEdges( const lpfloat centerZ, const lpfloat leftZ, const lpfloat rightZ, const lpfloat topZ, const lpfloat bottomZ )
|
||||
{
|
||||
lpfloat4 edgesLRTB = lpfloat4( leftZ, rightZ, topZ, bottomZ ) - (lpfloat)centerZ;
|
||||
|
||||
lpfloat slopeLR = (edgesLRTB.y - edgesLRTB.x) * 0.5;
|
||||
lpfloat slopeTB = (edgesLRTB.w - edgesLRTB.z) * 0.5;
|
||||
lpfloat4 edgesLRTBSlopeAdjusted = edgesLRTB + lpfloat4( slopeLR, -slopeLR, slopeTB, -slopeTB );
|
||||
edgesLRTB = min( abs( edgesLRTB ), abs( edgesLRTBSlopeAdjusted ) );
|
||||
return lpfloat4(saturate( ( 1.25 - edgesLRTB / (centerZ * 0.011) ) ));
|
||||
}
|
||||
|
||||
// packing/unpacking for edges; 2 bits per edge mean 4 gradient values (0, 0.33, 0.66, 1) for smoother transitions!
|
||||
lpfloat XeGTAO_PackEdges( lpfloat4 edgesLRTB )
|
||||
{
|
||||
// integer version:
|
||||
// edgesLRTB = saturate(edgesLRTB) * 2.9.xxxx + 0.5.xxxx;
|
||||
// return (((uint)edgesLRTB.x) << 6) + (((uint)edgesLRTB.y) << 4) + (((uint)edgesLRTB.z) << 2) + (((uint)edgesLRTB.w));
|
||||
//
|
||||
// optimized, should be same as above
|
||||
edgesLRTB = round( saturate( edgesLRTB ) * 2.9 );
|
||||
return dot( edgesLRTB, lpfloat4( 64.0 / 255.0, 16.0 / 255.0, 4.0 / 255.0, 1.0 / 255.0 ) ) ;
|
||||
}
|
||||
|
||||
float3 XeGTAO_CalculateNormal( const float4 edgesLRTB, float3 pixCenterPos, float3 pixLPos, float3 pixRPos, float3 pixTPos, float3 pixBPos )
|
||||
{
|
||||
// Get this pixel's viewspace normal
|
||||
float4 acceptedNormals = saturate( float4( edgesLRTB.x*edgesLRTB.z, edgesLRTB.z*edgesLRTB.y, edgesLRTB.y*edgesLRTB.w, edgesLRTB.w*edgesLRTB.x ) + 0.01 );
|
||||
|
||||
pixLPos = normalize(pixLPos - pixCenterPos);
|
||||
pixRPos = normalize(pixRPos - pixCenterPos);
|
||||
pixTPos = normalize(pixTPos - pixCenterPos);
|
||||
pixBPos = normalize(pixBPos - pixCenterPos);
|
||||
|
||||
float3 pixelNormal = acceptedNormals.x * cross( pixLPos, pixTPos ) +
|
||||
+ acceptedNormals.y * cross( pixTPos, pixRPos ) +
|
||||
+ acceptedNormals.z * cross( pixRPos, pixBPos ) +
|
||||
+ acceptedNormals.w * cross( pixBPos, pixLPos );
|
||||
pixelNormal = normalize( pixelNormal );
|
||||
|
||||
return pixelNormal;
|
||||
}
|
||||
|
||||
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
|
||||
float4 DbgGetSliceColor(int slice, int sliceCount, bool mirror)
|
||||
{
|
||||
float red = (float)slice / (float)sliceCount; float green = 0.01; float blue = 1.0 - (float)slice / (float)sliceCount;
|
||||
return (mirror)?(float4(blue, green, red, 0.9)):(float4(red, green, blue, 0.9));
|
||||
}
|
||||
#endif
|
||||
|
||||
// http://h14s.p5r.org/2012/09/0x5f3759df.html, [Drobot2014a] Low Level Optimizations for GCN, https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf slide 63
|
||||
lpfloat XeGTAO_FastSqrt( float x )
|
||||
{
|
||||
return (lpfloat)(asfloat( 0x1fbd1df5 + ( asint( x ) >> 1 ) ));
|
||||
}
|
||||
// input [-1, 1] and output [0, PI], from https://seblagarde.wordpress.com/2014/12/01/inverse-trigonometric-functions-gpu-optimization-for-amd-gcn-architecture/
|
||||
lpfloat XeGTAO_FastACos( lpfloat inX )
|
||||
{
|
||||
const lpfloat PI = 3.141593;
|
||||
const lpfloat HALF_PI = 1.570796;
|
||||
lpfloat x = abs(inX);
|
||||
lpfloat res = -0.156583 * x + HALF_PI;
|
||||
res *= XeGTAO_FastSqrt(1.0 - x);
|
||||
return (inX >= 0) ? res : PI - res;
|
||||
}
|
||||
|
||||
uint XeGTAO_EncodeVisibilityBentNormal( lpfloat visibility, lpfloat3 bentNormal )
|
||||
{
|
||||
return XeGTAO_FLOAT4_to_R8G8B8A8_UNORM( lpfloat4( bentNormal * 0.5 + 0.5, visibility ) );
|
||||
}
|
||||
|
||||
void XeGTAO_DecodeVisibilityBentNormal( const uint packedValue, out lpfloat visibility, out lpfloat3 bentNormal )
|
||||
{
|
||||
lpfloat4 decoded = XeGTAO_R8G8B8A8_UNORM_to_FLOAT4( packedValue );
|
||||
bentNormal = decoded.xyz * 2.0.xxx - 1.0.xxx; // could normalize - don't want to since it's done so many times, better to do it at the final step only
|
||||
visibility = decoded.w;
|
||||
}
|
||||
|
||||
void XeGTAO_OutputWorkingTerm( const uint2 pixCoord, lpfloat visibility, lpfloat3 bentNormal, RWTexture2D<uint> outWorkingAOTerm )
|
||||
{
|
||||
visibility = saturate( visibility / lpfloat(XE_GTAO_OCCLUSION_TERM_SCALE) );
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
outWorkingAOTerm[pixCoord] = XeGTAO_EncodeVisibilityBentNormal( visibility, bentNormal );
|
||||
#else
|
||||
outWorkingAOTerm[pixCoord] = uint(visibility * 255.0 + 0.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
// "Efficiently building a matrix to rotate one vector to another"
|
||||
// http://cs.brown.edu/research/pubs/pdfs/1999/Moller-1999-EBA.pdf / https://dl.acm.org/doi/10.1080/10867651.1999.10487509
|
||||
// (using https://github.com/assimp/assimp/blob/master/include/assimp/matrix3x3.inl#L275 as a code reference as it seems to be best)
|
||||
lpfloat3x3 XeGTAO_RotFromToMatrix( lpfloat3 from, lpfloat3 to )
|
||||
{
|
||||
const lpfloat e = dot(from, to);
|
||||
const lpfloat f = abs(e); //(e < 0)? -e:e;
|
||||
|
||||
// WARNING: This has not been tested/worked through, especially not for 16bit floats; seems to work in our special use case (from is always {0, 0, -1}) but wouldn't use it in general
|
||||
if( f > lpfloat( 1.0 - 0.0003 ) )
|
||||
return lpfloat3x3( 1, 0, 0, 0, 1, 0, 0, 0, 1 );
|
||||
|
||||
const lpfloat3 v = cross( from, to );
|
||||
/* ... use this hand optimized version (9 mults less) */
|
||||
const lpfloat h = (1.0)/(1.0 + e); /* optimization by Gottfried Chen */
|
||||
const lpfloat hvx = h * v.x;
|
||||
const lpfloat hvz = h * v.z;
|
||||
const lpfloat hvxy = hvx * v.y;
|
||||
const lpfloat hvxz = hvx * v.z;
|
||||
const lpfloat hvyz = hvz * v.y;
|
||||
|
||||
lpfloat3x3 mtx;
|
||||
mtx[0][0] = e + hvx * v.x;
|
||||
mtx[0][1] = hvxy - v.z;
|
||||
mtx[0][2] = hvxz + v.y;
|
||||
|
||||
mtx[1][0] = hvxy + v.z;
|
||||
mtx[1][1] = e + h * v.y * v.y;
|
||||
mtx[1][2] = hvyz - v.x;
|
||||
|
||||
mtx[2][0] = hvxz - v.y;
|
||||
mtx[2][1] = hvyz + v.x;
|
||||
mtx[2][2] = e + hvz * v.z;
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
void XeGTAO_MainPass( const uint2 pixCoord, lpfloat sliceCount, lpfloat stepsPerSlice, const lpfloat2 localNoise, lpfloat3 viewspaceNormal, const GTAOConstants consts,
|
||||
Texture2D<lpfloat> sourceViewspaceDepth, SamplerState depthSampler, RWTexture2D<uint> outWorkingAOTerm, RWTexture2D<unorm float> outWorkingEdges )
|
||||
{
|
||||
float2 normalizedScreenPos = (pixCoord + 0.5.xx) * consts.ViewportPixelSize;
|
||||
|
||||
lpfloat4 valuesUL = sourceViewspaceDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ) );
|
||||
lpfloat4 valuesBR = sourceViewspaceDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ), int2( 1, 1 ) );
|
||||
|
||||
// viewspace Z at the center
|
||||
lpfloat viewspaceZ = valuesUL.y; //sourceViewspaceDepth.SampleLevel( depthSampler, normalizedScreenPos, 0 ).x;
|
||||
|
||||
// viewspace Zs left top right bottom
|
||||
const lpfloat pixLZ = valuesUL.x;
|
||||
const lpfloat pixTZ = valuesUL.z;
|
||||
const lpfloat pixRZ = valuesBR.z;
|
||||
const lpfloat pixBZ = valuesBR.x;
|
||||
|
||||
lpfloat4 edgesLRTB = XeGTAO_CalculateEdges( (lpfloat)viewspaceZ, (lpfloat)pixLZ, (lpfloat)pixRZ, (lpfloat)pixTZ, (lpfloat)pixBZ );
|
||||
outWorkingEdges[pixCoord] = XeGTAO_PackEdges(edgesLRTB);
|
||||
|
||||
// Generating screen space normals in-place is faster than generating normals in a separate pass but requires
|
||||
// use of 32bit depth buffer (16bit works but visibly degrades quality) which in turn slows everything down. So to
|
||||
// reduce complexity and allow for screen space normal reuse by other effects, we've pulled it out into a separate
|
||||
// pass.
|
||||
// However, we leave this code in, in case anyone has a use-case where it fits better.
|
||||
#ifdef XE_GTAO_GENERATE_NORMALS_INPLACE
|
||||
float3 CENTER = XeGTAO_ComputeViewspacePosition( normalizedScreenPos, viewspaceZ, consts );
|
||||
float3 LEFT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2(-1, 0) * consts.ViewportPixelSize, pixLZ, consts );
|
||||
float3 RIGHT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 1, 0) * consts.ViewportPixelSize, pixRZ, consts );
|
||||
float3 TOP = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, -1) * consts.ViewportPixelSize, pixTZ, consts );
|
||||
float3 BOTTOM = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, 1) * consts.ViewportPixelSize, pixBZ, consts );
|
||||
viewspaceNormal = (lpfloat3)XeGTAO_CalculateNormal( edgesLRTB, CENTER, LEFT, RIGHT, TOP, BOTTOM );
|
||||
#endif
|
||||
|
||||
// Move center pixel slightly towards camera to avoid imprecision artifacts due to depth buffer imprecision; offset depends on depth texture format used
|
||||
#ifdef XE_GTAO_FP32_DEPTHS
|
||||
viewspaceZ *= 0.99999; // this is good for FP32 depth buffer
|
||||
#else
|
||||
viewspaceZ *= 0.99920; // this is good for FP16 depth buffer
|
||||
#endif
|
||||
|
||||
const float3 pixCenterPos = XeGTAO_ComputeViewspacePosition( normalizedScreenPos, viewspaceZ, consts );
|
||||
const lpfloat3 viewVec = (lpfloat3)normalize(-pixCenterPos);
|
||||
|
||||
// prevents normals that are facing away from the view vector - xeGTAO struggles with extreme cases, but in Vanilla it seems rare so it's disabled by default
|
||||
// viewspaceNormal = normalize( viewspaceNormal + max( 0, -dot( viewspaceNormal, viewVec ) ) * viewVec );
|
||||
|
||||
#ifdef XE_GTAO_SHOW_NORMALS
|
||||
g_outputDbgImage[pixCoord] = float4( DisplayNormalSRGB( viewspaceNormal.xyz ), 1 );
|
||||
#endif
|
||||
|
||||
#ifdef XE_GTAO_SHOW_EDGES
|
||||
g_outputDbgImage[pixCoord] = 1.0 - float4( edgesLRTB.x, edgesLRTB.y * 0.5 + edgesLRTB.w * 0.5, edgesLRTB.z, 1.0 );
|
||||
#endif
|
||||
|
||||
#if XE_GTAO_USE_DEFAULT_CONSTANTS != 0
|
||||
const lpfloat effectRadius = (lpfloat)consts.EffectRadius * (lpfloat)XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
|
||||
const lpfloat sampleDistributionPower = (lpfloat)XE_GTAO_DEFAULT_SAMPLE_DISTRIBUTION_POWER;
|
||||
const lpfloat thinOccluderCompensation = (lpfloat)XE_GTAO_DEFAULT_THIN_OCCLUDER_COMPENSATION;
|
||||
const lpfloat falloffRange = (lpfloat)XE_GTAO_DEFAULT_FALLOFF_RANGE * effectRadius;
|
||||
#else
|
||||
const lpfloat effectRadius = (lpfloat)consts.EffectRadius * (lpfloat)consts.RadiusMultiplier;
|
||||
const lpfloat sampleDistributionPower = (lpfloat)consts.SampleDistributionPower;
|
||||
const lpfloat thinOccluderCompensation = (lpfloat)consts.ThinOccluderCompensation;
|
||||
const lpfloat falloffRange = (lpfloat)consts.EffectFalloffRange * effectRadius;
|
||||
#endif
|
||||
|
||||
const lpfloat falloffFrom = effectRadius * ((lpfloat)1-(lpfloat)consts.EffectFalloffRange);
|
||||
|
||||
// fadeout precompute optimisation
|
||||
const lpfloat falloffMul = (lpfloat)-1.0 / ( falloffRange );
|
||||
const lpfloat falloffAdd = falloffFrom / ( falloffRange ) + (lpfloat)1.0;
|
||||
|
||||
lpfloat visibility = 0;
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
lpfloat3 bentNormal = 0;
|
||||
#else
|
||||
lpfloat3 bentNormal = viewspaceNormal;
|
||||
#endif
|
||||
|
||||
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
|
||||
float3 dbgWorldPos = mul(g_globals.ViewInv, float4(pixCenterPos, 1)).xyz;
|
||||
#endif
|
||||
|
||||
// see "Algorithm 1" in https://www.activision.com/cdn/research/Practical_Real_Time_Strategies_for_Accurate_Indirect_Occlusion_NEW%20VERSION_COLOR.pdf
|
||||
{
|
||||
const lpfloat noiseSlice = (lpfloat)localNoise.x;
|
||||
const lpfloat noiseSample = (lpfloat)localNoise.y;
|
||||
|
||||
// quality settings / tweaks / hacks
|
||||
const lpfloat pixelTooCloseThreshold = 1.3; // if the offset is under approx pixel size (pixelTooCloseThreshold), push it out to the minimum distance
|
||||
|
||||
// approx viewspace pixel size at pixCoord; approximation of NDCToViewspace( normalizedScreenPos.xy + consts.ViewportPixelSize.xy, pixCenterPos.z ).xy - pixCenterPos.xy;
|
||||
const float2 pixelDirRBViewspaceSizeAtCenterZ = viewspaceZ.xx * consts.NDCToViewMul_x_PixelSize;
|
||||
|
||||
lpfloat screenspaceRadius = effectRadius / (lpfloat)pixelDirRBViewspaceSizeAtCenterZ.x;
|
||||
|
||||
// fade out for small screen radii
|
||||
visibility += saturate((10 - screenspaceRadius)/100)*0.5;
|
||||
|
||||
#if 0 // sensible early-out for even more performance; disabled because not yet tested
|
||||
[branch]
|
||||
if( screenspaceRadius < pixelTooCloseThreshold )
|
||||
{
|
||||
XeGTAO_OutputWorkingTerm( pixCoord, 1, viewspaceNormal, outWorkingAOTerm );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
|
||||
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
|
||||
{
|
||||
float3 dbgWorldNorm = mul((float3x3)g_globals.ViewInv, viewspaceNormal).xyz;
|
||||
float3 dbgWorldViewVec = mul((float3x3)g_globals.ViewInv, viewVec).xyz;
|
||||
//DebugDraw3DArrow(dbgWorldPos, dbgWorldPos + 0.5 * dbgWorldViewVec, 0.02, float4(0, 1, 0, 0.95));
|
||||
//DebugDraw2DCircle(pixCoord, screenspaceRadius, float4(1, 0, 0.2, 1));
|
||||
DebugDraw3DSphere(dbgWorldPos, effectRadius, float4(1, 0.2, 0, 0.1));
|
||||
//DebugDraw3DText(dbgWorldPos, float2(0, 0), float4(0.6, 0.3, 0.3, 1), float4( pixelDirRBViewspaceSizeAtCenterZ.xy, 0, screenspaceRadius) );
|
||||
}
|
||||
#endif
|
||||
|
||||
// this is the min distance to start sampling from to avoid sampling from the center pixel (no useful data obtained from sampling center pixel)
|
||||
const lpfloat minS = (lpfloat)pixelTooCloseThreshold / screenspaceRadius;
|
||||
|
||||
//[unroll]
|
||||
for( lpfloat slice = 0; slice < sliceCount; slice++ )
|
||||
{
|
||||
lpfloat sliceK = (slice+noiseSlice) / sliceCount;
|
||||
// lines 5, 6 from the paper
|
||||
lpfloat phi = sliceK * XE_GTAO_PI;
|
||||
lpfloat cosPhi = cos(phi);
|
||||
lpfloat sinPhi = sin(phi);
|
||||
lpfloat2 omega = lpfloat2(cosPhi, -sinPhi); //lpfloat2 on omega causes issues with big radii
|
||||
|
||||
// convert to screen units (pixels) for later use
|
||||
omega *= screenspaceRadius;
|
||||
|
||||
// line 8 from the paper
|
||||
const lpfloat3 directionVec = lpfloat3(cosPhi, sinPhi, 0);
|
||||
|
||||
// line 9 from the paper
|
||||
const lpfloat3 orthoDirectionVec = directionVec - (dot(directionVec, viewVec) * viewVec);
|
||||
|
||||
// line 10 from the paper
|
||||
//axisVec is orthogonal to directionVec and viewVec, used to define projectedNormal
|
||||
const lpfloat3 axisVec = normalize( cross(orthoDirectionVec, viewVec) );
|
||||
|
||||
// alternative line 9 from the paper
|
||||
// float3 orthoDirectionVec = cross( viewVec, axisVec );
|
||||
|
||||
// line 11 from the paper
|
||||
lpfloat3 projectedNormalVec = viewspaceNormal - axisVec * dot(viewspaceNormal, axisVec);
|
||||
|
||||
// line 13 from the paper
|
||||
lpfloat signNorm = (lpfloat)sign( dot( orthoDirectionVec, projectedNormalVec ) );
|
||||
|
||||
// line 14 from the paper
|
||||
lpfloat projectedNormalVecLength = length(projectedNormalVec);
|
||||
lpfloat cosNorm = (lpfloat)saturate(dot(projectedNormalVec, viewVec) / projectedNormalVecLength);
|
||||
|
||||
// line 15 from the paper
|
||||
lpfloat n = signNorm * XeGTAO_FastACos(cosNorm);
|
||||
|
||||
// this is a lower weight target; not using -1 as in the original paper because it is under horizon, so a 'weight' has different meaning based on the normal
|
||||
const lpfloat lowHorizonCos0 = cos(n+XE_GTAO_PI_HALF);
|
||||
const lpfloat lowHorizonCos1 = cos(n-XE_GTAO_PI_HALF);
|
||||
|
||||
// lines 17, 18 from the paper, manually unrolled the 'side' loop
|
||||
lpfloat horizonCos0 = lowHorizonCos0; //-1;
|
||||
lpfloat horizonCos1 = lowHorizonCos1; //-1;
|
||||
|
||||
[unroll]
|
||||
for( lpfloat step = 0; step < stepsPerSlice; step++ )
|
||||
{
|
||||
// R1 sequence (http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/)
|
||||
const lpfloat stepBaseNoise = lpfloat(slice + step * stepsPerSlice) * 0.6180339887498948482; // <- this should unroll
|
||||
lpfloat stepNoise = frac(noiseSample + stepBaseNoise);
|
||||
|
||||
// approx line 20 from the paper, with added noise
|
||||
lpfloat s = (step+stepNoise) / (stepsPerSlice); // + (lpfloat2)1e-6f);
|
||||
|
||||
// additional distribution modifier
|
||||
s = (lpfloat)pow( s, (lpfloat)sampleDistributionPower );
|
||||
|
||||
// avoid sampling center pixel
|
||||
s += minS;
|
||||
|
||||
// approx lines 21-22 from the paper, unrolled
|
||||
lpfloat2 sampleOffset = s * omega;
|
||||
|
||||
lpfloat sampleOffsetLength = length( sampleOffset );
|
||||
|
||||
// note: when sampling, using point_point_point or point_point_linear sampler works, but linear_linear_linear will cause unwanted interpolation between neighbouring depth values on the same MIP level!
|
||||
const lpfloat mipLevel = (lpfloat)clamp( log2( sampleOffsetLength ) - consts.DepthMIPSamplingOffset, 0, XE_GTAO_DEPTH_MIP_LEVELS );
|
||||
|
||||
// Snap to pixel center (more correct direction math, avoids artifacts due to sampling pos not matching depth texel center - messes up slope - but adds other
|
||||
// artifacts due to them being pushed off the slice). Also use full precision for high res cases.
|
||||
sampleOffset = round(sampleOffset) * (lpfloat2)consts.ViewportPixelSize;
|
||||
|
||||
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
|
||||
int mipLevelU = (int)round(mipLevel);
|
||||
float4 mipColor = saturate( float4( mipLevelU>=3, mipLevelU>=1 && mipLevelU<=3, mipLevelU<=1, 1.0 ) );
|
||||
if( all( sampleOffset == 0 ) )
|
||||
DebugDraw2DText( pixCoord, float4( 1, 0, 0, 1), pixelTooCloseThreshold );
|
||||
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
|
||||
{
|
||||
//DebugDraw2DText( (normalizedScreenPos + sampleOffset) * consts.ViewportSize, mipColor, mipLevelU );
|
||||
//DebugDraw2DText( (normalizedScreenPos + sampleOffset) * consts.ViewportSize, mipColor, (uint)slice );
|
||||
//DebugDraw2DText( (normalizedScreenPos - sampleOffset) * consts.ViewportSize, mipColor, (uint)slice );
|
||||
//DebugDraw2DText( (normalizedScreenPos - sampleOffset) * consts.ViewportSize, saturate( float4( mipLevelU>=3, mipLevelU>=1 && mipLevelU<=3, mipLevelU<=1, 1.0 ) ), mipLevelU );
|
||||
}
|
||||
#endif
|
||||
|
||||
float2 sampleScreenPos0 = normalizedScreenPos + sampleOffset;
|
||||
float SZ0 = sourceViewspaceDepth.SampleLevel( depthSampler, sampleScreenPos0, mipLevel ).x;
|
||||
float3 samplePos0 = XeGTAO_ComputeViewspacePosition( sampleScreenPos0, SZ0, consts );
|
||||
|
||||
float2 sampleScreenPos1 = normalizedScreenPos - sampleOffset;
|
||||
float SZ1 = sourceViewspaceDepth.SampleLevel( depthSampler, sampleScreenPos1, mipLevel ).x;
|
||||
float3 samplePos1 = XeGTAO_ComputeViewspacePosition( sampleScreenPos1, SZ1, consts );
|
||||
|
||||
float3 sampleDelta0 = (samplePos0 - float3(pixCenterPos)); // using lpfloat for sampleDelta causes precision issues
|
||||
float3 sampleDelta1 = (samplePos1 - float3(pixCenterPos)); // using lpfloat for sampleDelta causes precision issues
|
||||
lpfloat sampleDist0 = (lpfloat)length( sampleDelta0 );
|
||||
lpfloat sampleDist1 = (lpfloat)length( sampleDelta1 );
|
||||
|
||||
// approx lines 23, 24 from the paper, unrolled
|
||||
lpfloat3 sampleHorizonVec0 = (lpfloat3)(sampleDelta0 / sampleDist0);
|
||||
lpfloat3 sampleHorizonVec1 = (lpfloat3)(sampleDelta1 / sampleDist1);
|
||||
|
||||
// any sample out of radius should be discarded - also use fallof range for smooth transitions; this is a modified idea from "4.3 Implementation details, Bounding the sampling area"
|
||||
#if XE_GTAO_USE_DEFAULT_CONSTANTS != 0 && XE_GTAO_DEFAULT_THIN_OBJECT_HEURISTIC == 0
|
||||
lpfloat weight0 = saturate( sampleDist0 * falloffMul + falloffAdd );
|
||||
lpfloat weight1 = saturate( sampleDist1 * falloffMul + falloffAdd );
|
||||
#else
|
||||
// this is our own thickness heuristic that relies on sooner discarding samples behind the center
|
||||
lpfloat falloffBase0 = length( lpfloat3(sampleDelta0.x, sampleDelta0.y, sampleDelta0.z * (1+thinOccluderCompensation) ) );
|
||||
lpfloat falloffBase1 = length( lpfloat3(sampleDelta1.x, sampleDelta1.y, sampleDelta1.z * (1+thinOccluderCompensation) ) );
|
||||
lpfloat weight0 = saturate( falloffBase0 * falloffMul + falloffAdd );
|
||||
lpfloat weight1 = saturate( falloffBase1 * falloffMul + falloffAdd );
|
||||
#endif
|
||||
|
||||
// sample horizon cos
|
||||
lpfloat shc0 = (lpfloat)dot(sampleHorizonVec0, viewVec);
|
||||
lpfloat shc1 = (lpfloat)dot(sampleHorizonVec1, viewVec);
|
||||
|
||||
// discard unwanted samples
|
||||
shc0 = lerp( lowHorizonCos0, shc0, weight0 ); // this would be more correct but too expensive: cos(lerp( acos(lowHorizonCos0), acos(shc0), weight0 ));
|
||||
shc1 = lerp( lowHorizonCos1, shc1, weight1 ); // this would be more correct but too expensive: cos(lerp( acos(lowHorizonCos1), acos(shc1), weight1 ));
|
||||
|
||||
// thickness heuristic - see "4.3 Implementation details, Height-field assumption considerations"
|
||||
#if 0 // (disabled, not used) this should match the paper
|
||||
lpfloat newhorizonCos0 = max( horizonCos0, shc0 );
|
||||
lpfloat newhorizonCos1 = max( horizonCos1, shc1 );
|
||||
horizonCos0 = (horizonCos0 > shc0)?( lerp( newhorizonCos0, shc0, thinOccluderCompensation ) ):( newhorizonCos0 );
|
||||
horizonCos1 = (horizonCos1 > shc1)?( lerp( newhorizonCos1, shc1, thinOccluderCompensation ) ):( newhorizonCos1 );
|
||||
#elif 0 // (disabled, not used) this is slightly different from the paper but cheaper and provides very similar results
|
||||
horizonCos0 = lerp( max( horizonCos0, shc0 ), shc0, thinOccluderCompensation );
|
||||
horizonCos1 = lerp( max( horizonCos1, shc1 ), shc1, thinOccluderCompensation );
|
||||
#else // this is a version where thicknessHeuristic is completely disabled
|
||||
horizonCos0 = max( horizonCos0, shc0 );
|
||||
horizonCos1 = max( horizonCos1, shc1 );
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XE_GTAO_SHOW_DEBUG_VIZ
|
||||
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
|
||||
{
|
||||
float3 WS_samplePos0 = mul(g_globals.ViewInv, float4(samplePos0, 1)).xyz;
|
||||
float3 WS_samplePos1 = mul(g_globals.ViewInv, float4(samplePos1, 1)).xyz;
|
||||
float3 WS_sampleHorizonVec0 = mul( (float3x3)g_globals.ViewInv, sampleHorizonVec0).xyz;
|
||||
float3 WS_sampleHorizonVec1 = mul( (float3x3)g_globals.ViewInv, sampleHorizonVec1).xyz;
|
||||
// DebugDraw3DSphere( WS_samplePos0, effectRadius * 0.02, DbgGetSliceColor(slice, sliceCount, false) );
|
||||
// DebugDraw3DSphere( WS_samplePos1, effectRadius * 0.02, DbgGetSliceColor(slice, sliceCount, true) );
|
||||
DebugDraw3DSphere( WS_samplePos0, effectRadius * 0.02, mipColor );
|
||||
DebugDraw3DSphere( WS_samplePos1, effectRadius * 0.02, mipColor );
|
||||
// DebugDraw3DArrow( WS_samplePos0, WS_samplePos0 - WS_sampleHorizonVec0, 0.002, float4(1, 0, 0, 1 ) );
|
||||
// DebugDraw3DArrow( WS_samplePos1, WS_samplePos1 - WS_sampleHorizonVec1, 0.002, float4(1, 0, 0, 1 ) );
|
||||
// DebugDraw3DText( WS_samplePos0, float2(0, 0), float4( 1, 0, 0, 1), weight0 );
|
||||
// DebugDraw3DText( WS_samplePos1, float2(0, 0), float4( 1, 0, 0, 1), weight1 );
|
||||
|
||||
// DebugDraw2DText( float2( 500, 94+(step+slice*3)*12 ), float4( 0, 1, 0, 1 ), float4( projectedNormalVecLength, 0, horizonCos0, horizonCos1 ) );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 1 // I can't figure out the slight overdarkening on high slopes, so I'm adding this fudge - in the training set, 0.05 is close (PSNR 21.34) to disabled (PSNR 21.45)
|
||||
projectedNormalVecLength = lerp( projectedNormalVecLength, 1, 0.05 );
|
||||
#endif
|
||||
|
||||
// line ~27, unrolled
|
||||
lpfloat h0 = -XeGTAO_FastACos((lpfloat)horizonCos1);
|
||||
lpfloat h1 = XeGTAO_FastACos((lpfloat)horizonCos0);
|
||||
#if 0 // we can skip clamping for a tiny little bit more performance
|
||||
h0 = n + clamp( h0-n, (lpfloat)-XE_GTAO_PI_HALF, (lpfloat)XE_GTAO_PI_HALF );
|
||||
h1 = n + clamp( h1-n, (lpfloat)-XE_GTAO_PI_HALF, (lpfloat)XE_GTAO_PI_HALF );
|
||||
#endif
|
||||
lpfloat iarc0 = ((lpfloat)cosNorm + (lpfloat)2 * (lpfloat)h0 * (lpfloat)sin(n)-(lpfloat)cos((lpfloat)2 * (lpfloat)h0-n))/(lpfloat)4;
|
||||
lpfloat iarc1 = ((lpfloat)cosNorm + (lpfloat)2 * (lpfloat)h1 * (lpfloat)sin(n)-(lpfloat)cos((lpfloat)2 * (lpfloat)h1-n))/(lpfloat)4;
|
||||
lpfloat localVisibility = (lpfloat)projectedNormalVecLength * (lpfloat)(iarc0+iarc1);
|
||||
visibility += localVisibility;
|
||||
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
// see "Algorithm 2 Extension that computes bent normals b."
|
||||
lpfloat t0 = (6*sin(h0-n)-sin(3*h0-n)+6*sin(h1-n)-sin(3*h1-n)+16*sin(n)-3*(sin(h0+n)+sin(h1+n)))/12;
|
||||
lpfloat t1 = (-cos(3 * h0-n)-cos(3 * h1-n) +8 * cos(n)-3 * (cos(h0+n) +cos(h1+n)))/12;
|
||||
lpfloat3 localBentNormal = lpfloat3( directionVec.x * (lpfloat)t0, directionVec.y * (lpfloat)t0, -lpfloat(t1) );
|
||||
localBentNormal = (lpfloat3)mul( XeGTAO_RotFromToMatrix( lpfloat3(0,0,-1), viewVec ), localBentNormal ) * projectedNormalVecLength;
|
||||
bentNormal += localBentNormal;
|
||||
#endif
|
||||
}
|
||||
visibility /= (lpfloat)sliceCount;
|
||||
visibility = pow( visibility, (lpfloat)consts.FinalValuePower );
|
||||
visibility = max( (lpfloat)0.03, visibility ); // disallow total occlusion (which wouldn't make any sense anyhow since pixel is visible but also helps with packing bent normals)
|
||||
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
bentNormal = normalize(bentNormal) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(XE_GTAO_SHOW_DEBUG_VIZ) && defined(XE_GTAO_COMPUTE_BENT_NORMALS)
|
||||
[branch] if (IsUnderCursorRange(pixCoord, int2(1, 1)))
|
||||
{
|
||||
float3 dbgWorldViewNorm = mul((float3x3)g_globals.ViewInv, viewspaceNormal).xyz;
|
||||
float3 dbgWorldBentNorm = mul((float3x3)g_globals.ViewInv, bentNormal).xyz;
|
||||
DebugDraw3DSphereCone( dbgWorldPos, dbgWorldViewNorm, 0.3, VA_PI*0.5 - acos(saturate(visibility)), float4( 0.2, 0.2, 0.2, 0.5 ) );
|
||||
DebugDraw3DSphereCone( dbgWorldPos, dbgWorldBentNorm, 0.3, VA_PI*0.5 - acos(saturate(visibility)), float4( 0.0, 1.0, 0.0, 0.7 ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
XeGTAO_OutputWorkingTerm( pixCoord, visibility, bentNormal, outWorkingAOTerm );
|
||||
}
|
||||
|
||||
// weighted average depth filter
|
||||
lpfloat XeGTAO_DepthMIPFilter( lpfloat depth0, lpfloat depth1, lpfloat depth2, lpfloat depth3, const GTAOConstants consts )
|
||||
{
|
||||
lpfloat maxDepth = max( max( depth0, depth1 ), max( depth2, depth3 ) );
|
||||
|
||||
const lpfloat depthRangeScaleFactor = 0.75; // found empirically :)
|
||||
#if XE_GTAO_USE_DEFAULT_CONSTANTS != 0
|
||||
const lpfloat effectRadius = depthRangeScaleFactor * (lpfloat)consts.EffectRadius * (lpfloat)XE_GTAO_DEFAULT_RADIUS_MULTIPLIER;
|
||||
const lpfloat falloffRange = (lpfloat)XE_GTAO_DEFAULT_FALLOFF_RANGE * effectRadius;
|
||||
#else
|
||||
const lpfloat effectRadius = depthRangeScaleFactor * (lpfloat)consts.EffectRadius * (lpfloat)consts.RadiusMultiplier;
|
||||
const lpfloat falloffRange = (lpfloat)consts.EffectFalloffRange * effectRadius;
|
||||
#endif
|
||||
const lpfloat falloffFrom = effectRadius * ((lpfloat)1-(lpfloat)consts.EffectFalloffRange);
|
||||
// fadeout precompute optimisation
|
||||
const lpfloat falloffMul = (lpfloat)-1.0 / ( falloffRange );
|
||||
const lpfloat falloffAdd = falloffFrom / ( falloffRange ) + (lpfloat)1.0;
|
||||
|
||||
lpfloat weight0 = saturate( (maxDepth-depth0) * falloffMul + falloffAdd );
|
||||
lpfloat weight1 = saturate( (maxDepth-depth1) * falloffMul + falloffAdd );
|
||||
lpfloat weight2 = saturate( (maxDepth-depth2) * falloffMul + falloffAdd );
|
||||
lpfloat weight3 = saturate( (maxDepth-depth3) * falloffMul + falloffAdd );
|
||||
|
||||
lpfloat weightSum = weight0 + weight1 + weight2 + weight3;
|
||||
return (weight0 * depth0 + weight1 * depth1 + weight2 * depth2 + weight3 * depth3) / weightSum;
|
||||
}
|
||||
|
||||
// This is also a good place to do non-linear depth conversion for cases where one wants the 'radius' (effectively the threshold between near-field and far-field GI),
|
||||
// is required to be non-linear (i.e. very large outdoors environments).
|
||||
lpfloat XeGTAO_ClampDepth( float depth )
|
||||
{
|
||||
#ifdef XE_GTAO_USE_HALF_FLOAT_PRECISION
|
||||
return (lpfloat)clamp( depth, 0.0, 65504.0 );
|
||||
#else
|
||||
return clamp( depth, 0.0, 3.402823466e+38 );
|
||||
#endif
|
||||
}
|
||||
|
||||
groupshared lpfloat g_scratchDepths[8][8];
|
||||
void XeGTAO_PrefilterDepths16x16( uint2 dispatchThreadID /*: SV_DispatchThreadID*/, uint2 groupThreadID /*: SV_GroupThreadID*/, const GTAOConstants consts, Texture2D<float> sourceNDCDepth, SamplerState depthSampler, RWTexture2D<lpfloat> outDepth0, RWTexture2D<lpfloat> outDepth1, RWTexture2D<lpfloat> outDepth2, RWTexture2D<lpfloat> outDepth3, RWTexture2D<lpfloat> outDepth4 )
|
||||
{
|
||||
// MIP 0
|
||||
const uint2 baseCoord = dispatchThreadID;
|
||||
const uint2 pixCoord = baseCoord * 2;
|
||||
float4 depths4 = sourceNDCDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ), int2(1,1) );
|
||||
lpfloat depth0 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.w, consts ) );
|
||||
lpfloat depth1 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.z, consts ) );
|
||||
lpfloat depth2 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.x, consts ) );
|
||||
lpfloat depth3 = XeGTAO_ClampDepth( XeGTAO_ScreenSpaceToViewSpaceDepth( depths4.y, consts ) );
|
||||
outDepth0[ pixCoord + uint2(0, 0) ] = (lpfloat)depth0;
|
||||
outDepth0[ pixCoord + uint2(1, 0) ] = (lpfloat)depth1;
|
||||
outDepth0[ pixCoord + uint2(0, 1) ] = (lpfloat)depth2;
|
||||
outDepth0[ pixCoord + uint2(1, 1) ] = (lpfloat)depth3;
|
||||
|
||||
// MIP 1
|
||||
lpfloat dm1 = XeGTAO_DepthMIPFilter( depth0, depth1, depth2, depth3, consts );
|
||||
outDepth1[ baseCoord ] = (lpfloat)dm1;
|
||||
g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm1;
|
||||
|
||||
GroupMemoryBarrierWithGroupSync( );
|
||||
|
||||
// MIP 2
|
||||
[branch]
|
||||
if( all( ( groupThreadID.xy % 2.xx ) == 0 ) )
|
||||
{
|
||||
lpfloat inTL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+0];
|
||||
lpfloat inTR = g_scratchDepths[groupThreadID.x+1][groupThreadID.y+0];
|
||||
lpfloat inBL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+1];
|
||||
lpfloat inBR = g_scratchDepths[groupThreadID.x+1][groupThreadID.y+1];
|
||||
|
||||
lpfloat dm2 = XeGTAO_DepthMIPFilter( inTL, inTR, inBL, inBR, consts );
|
||||
outDepth2[ baseCoord / 2 ] = (lpfloat)dm2;
|
||||
g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm2;
|
||||
}
|
||||
|
||||
GroupMemoryBarrierWithGroupSync( );
|
||||
|
||||
// MIP 3
|
||||
[branch]
|
||||
if( all( ( groupThreadID.xy % 4.xx ) == 0 ) )
|
||||
{
|
||||
lpfloat inTL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+0];
|
||||
lpfloat inTR = g_scratchDepths[groupThreadID.x+2][groupThreadID.y+0];
|
||||
lpfloat inBL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+2];
|
||||
lpfloat inBR = g_scratchDepths[groupThreadID.x+2][groupThreadID.y+2];
|
||||
|
||||
lpfloat dm3 = XeGTAO_DepthMIPFilter( inTL, inTR, inBL, inBR, consts );
|
||||
outDepth3[ baseCoord / 4 ] = (lpfloat)dm3;
|
||||
g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm3;
|
||||
}
|
||||
|
||||
GroupMemoryBarrierWithGroupSync( );
|
||||
|
||||
// MIP 4
|
||||
[branch]
|
||||
if( all( ( groupThreadID.xy % 8.xx ) == 0 ) )
|
||||
{
|
||||
lpfloat inTL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+0];
|
||||
lpfloat inTR = g_scratchDepths[groupThreadID.x+4][groupThreadID.y+0];
|
||||
lpfloat inBL = g_scratchDepths[groupThreadID.x+0][groupThreadID.y+4];
|
||||
lpfloat inBR = g_scratchDepths[groupThreadID.x+4][groupThreadID.y+4];
|
||||
|
||||
lpfloat dm4 = XeGTAO_DepthMIPFilter( inTL, inTR, inBL, inBR, consts );
|
||||
outDepth4[ baseCoord / 8 ] = (lpfloat)dm4;
|
||||
//g_scratchDepths[ groupThreadID.x ][ groupThreadID.y ] = dm4;
|
||||
}
|
||||
}
|
||||
|
||||
lpfloat4 XeGTAO_UnpackEdges( lpfloat _packedVal )
|
||||
{
|
||||
uint packedVal = (uint)(_packedVal * 255.5);
|
||||
lpfloat4 edgesLRTB;
|
||||
edgesLRTB.x = lpfloat((packedVal >> 6) & 0x03) / 3.0; // there's really no need for mask (as it's an 8 bit input) but I'll leave it in so it doesn't cause any trouble in the future
|
||||
edgesLRTB.y = lpfloat((packedVal >> 4) & 0x03) / 3.0;
|
||||
edgesLRTB.z = lpfloat((packedVal >> 2) & 0x03) / 3.0;
|
||||
edgesLRTB.w = lpfloat((packedVal >> 0) & 0x03) / 3.0;
|
||||
|
||||
return saturate( edgesLRTB );
|
||||
}
|
||||
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
typedef lpfloat4 AOTermType; // .xyz is bent normal, .w is visibility term
|
||||
#else
|
||||
typedef lpfloat AOTermType; // .x is visibility term
|
||||
#endif
|
||||
|
||||
void XeGTAO_AddSample( AOTermType ssaoValue, lpfloat edgeValue, inout AOTermType sum, inout lpfloat sumWeight )
|
||||
{
|
||||
lpfloat weight = edgeValue;
|
||||
|
||||
sum += (weight * ssaoValue);
|
||||
sumWeight += weight;
|
||||
}
|
||||
|
||||
void XeGTAO_Output( uint2 pixCoord, RWTexture2D<uint> outputTexture, AOTermType outputValue, const uniform bool finalApply )
|
||||
{
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
lpfloat visibility = outputValue.w * ((finalApply)?((lpfloat)XE_GTAO_OCCLUSION_TERM_SCALE):(1));
|
||||
lpfloat3 bentNormal = normalize(outputValue.xyz);
|
||||
outputTexture[pixCoord.xy] = XeGTAO_EncodeVisibilityBentNormal( visibility, bentNormal );
|
||||
#else
|
||||
outputValue *= (finalApply)?((lpfloat)XE_GTAO_OCCLUSION_TERM_SCALE):(1);
|
||||
outputTexture[pixCoord.xy] = uint(outputValue * 255.0 + 0.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
void XeGTAO_DecodeGatherPartial( const uint4 packedValue, out AOTermType outDecoded[4] )
|
||||
{
|
||||
for( int i = 0; i < 4; i++ )
|
||||
#ifdef XE_GTAO_COMPUTE_BENT_NORMALS
|
||||
XeGTAO_DecodeVisibilityBentNormal( packedValue[i], outDecoded[i].w, outDecoded[i].xyz );
|
||||
#else
|
||||
outDecoded[i] = lpfloat(packedValue[i]) / lpfloat(255.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void XeGTAO_Denoise( const uint2 pixCoordBase, const GTAOConstants consts, Texture2D<uint> sourceAOTerm, Texture2D<lpfloat> sourceEdges, SamplerState texSampler, RWTexture2D<uint> outputTexture, const uniform bool finalApply )
|
||||
{
|
||||
const lpfloat blurAmount = (finalApply)?((lpfloat)consts.DenoiseBlurBeta):((lpfloat)consts.DenoiseBlurBeta/(lpfloat)5.0);
|
||||
const lpfloat diagWeight = 0.85 * 0.5;
|
||||
|
||||
AOTermType aoTerm[2]; // pixel pixCoordBase and pixel pixCoordBase + int2( 1, 0 )
|
||||
lpfloat4 edgesC_LRTB[2];
|
||||
lpfloat weightTL[2];
|
||||
lpfloat weightTR[2];
|
||||
lpfloat weightBL[2];
|
||||
lpfloat weightBR[2];
|
||||
|
||||
// gather edge and visibility quads, used later
|
||||
const float2 gatherCenter = float2( pixCoordBase.x, pixCoordBase.y ) * consts.ViewportPixelSize;
|
||||
lpfloat4 edgesQ0 = sourceEdges.GatherRed( texSampler, gatherCenter, int2( 0, 0 ) );
|
||||
lpfloat4 edgesQ1 = sourceEdges.GatherRed( texSampler, gatherCenter, int2( 2, 0 ) );
|
||||
lpfloat4 edgesQ2 = sourceEdges.GatherRed( texSampler, gatherCenter, int2( 1, 2 ) );
|
||||
|
||||
AOTermType visQ0[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 0, 0 ) ), visQ0 );
|
||||
AOTermType visQ1[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 2, 0 ) ), visQ1 );
|
||||
AOTermType visQ2[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 0, 2 ) ), visQ2 );
|
||||
AOTermType visQ3[4]; XeGTAO_DecodeGatherPartial( sourceAOTerm.GatherRed( texSampler, gatherCenter, int2( 2, 2 ) ), visQ3 );
|
||||
|
||||
for( int side = 0; side < 2; side++ )
|
||||
{
|
||||
const int2 pixCoord = int2( pixCoordBase.x + side, pixCoordBase.y );
|
||||
|
||||
lpfloat4 edgesL_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ0.x):(edgesQ0.y) );
|
||||
lpfloat4 edgesT_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ0.z):(edgesQ1.w) );
|
||||
lpfloat4 edgesR_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ1.x):(edgesQ1.y) );
|
||||
lpfloat4 edgesB_LRTB = XeGTAO_UnpackEdges( (side==0)?(edgesQ2.w):(edgesQ2.z) );
|
||||
|
||||
edgesC_LRTB[side] = XeGTAO_UnpackEdges( (side==0)?(edgesQ0.y):(edgesQ1.x) );
|
||||
|
||||
// Edges aren't perfectly symmetrical: edge detection algorithm does not guarantee that a left edge on the right pixel will match the right edge on the left pixel (although
|
||||
// they will match in majority of cases). This line further enforces the symmetricity, creating a slightly sharper blur. Works real nice with TAA.
|
||||
edgesC_LRTB[side] *= lpfloat4( edgesL_LRTB.y, edgesR_LRTB.x, edgesT_LRTB.w, edgesB_LRTB.z );
|
||||
|
||||
#if 1 // this allows some small amount of AO leaking from neighbours if there are 3 or 4 edges; this reduces both spatial and temporal aliasing
|
||||
const lpfloat leak_threshold = 2.5; const lpfloat leak_strength = 0.5;
|
||||
lpfloat edginess = (saturate(4.0 - leak_threshold - dot( edgesC_LRTB[side], 1.xxxx )) / (4-leak_threshold)) * leak_strength;
|
||||
edgesC_LRTB[side] = saturate( edgesC_LRTB[side] + edginess );
|
||||
#endif
|
||||
|
||||
#ifdef XE_GTAO_SHOW_EDGES
|
||||
g_outputDbgImage[pixCoord] = 1.0 - lpfloat4( edgesC_LRTB[side].x, edgesC_LRTB[side].y * 0.5 + edgesC_LRTB[side].w * 0.5, edgesC_LRTB[side].z, 1.0 );
|
||||
//g_outputDbgImage[pixCoord] = 1 - float4( edgesC_LRTB[side].z, edgesC_LRTB[side].w , 1, 0 );
|
||||
//g_outputDbgImage[pixCoord] = edginess.xxxx;
|
||||
#endif
|
||||
|
||||
// for diagonals; used by first and second pass
|
||||
weightTL[side] = diagWeight * (edgesC_LRTB[side].x * edgesL_LRTB.z + edgesC_LRTB[side].z * edgesT_LRTB.x);
|
||||
weightTR[side] = diagWeight * (edgesC_LRTB[side].z * edgesT_LRTB.y + edgesC_LRTB[side].y * edgesR_LRTB.z);
|
||||
weightBL[side] = diagWeight * (edgesC_LRTB[side].w * edgesB_LRTB.x + edgesC_LRTB[side].x * edgesL_LRTB.w);
|
||||
weightBR[side] = diagWeight * (edgesC_LRTB[side].y * edgesR_LRTB.w + edgesC_LRTB[side].w * edgesB_LRTB.y);
|
||||
|
||||
// first pass
|
||||
AOTermType ssaoValue = (side==0)?(visQ0[1]):(visQ1[0]);
|
||||
AOTermType ssaoValueL = (side==0)?(visQ0[0]):(visQ0[1]);
|
||||
AOTermType ssaoValueT = (side==0)?(visQ0[2]):(visQ1[3]);
|
||||
AOTermType ssaoValueR = (side==0)?(visQ1[0]):(visQ1[1]);
|
||||
AOTermType ssaoValueB = (side==0)?(visQ2[2]):(visQ3[3]);
|
||||
AOTermType ssaoValueTL = (side==0)?(visQ0[3]):(visQ0[2]);
|
||||
AOTermType ssaoValueBR = (side==0)?(visQ3[3]):(visQ3[2]);
|
||||
AOTermType ssaoValueTR = (side==0)?(visQ1[3]):(visQ1[2]);
|
||||
AOTermType ssaoValueBL = (side==0)?(visQ2[3]):(visQ2[2]);
|
||||
|
||||
lpfloat sumWeight = blurAmount;
|
||||
AOTermType sum = ssaoValue * sumWeight;
|
||||
|
||||
XeGTAO_AddSample( ssaoValueL, edgesC_LRTB[side].x, sum, sumWeight );
|
||||
XeGTAO_AddSample( ssaoValueR, edgesC_LRTB[side].y, sum, sumWeight );
|
||||
XeGTAO_AddSample( ssaoValueT, edgesC_LRTB[side].z, sum, sumWeight );
|
||||
XeGTAO_AddSample( ssaoValueB, edgesC_LRTB[side].w, sum, sumWeight );
|
||||
|
||||
XeGTAO_AddSample( ssaoValueTL, weightTL[side], sum, sumWeight );
|
||||
XeGTAO_AddSample( ssaoValueTR, weightTR[side], sum, sumWeight );
|
||||
XeGTAO_AddSample( ssaoValueBL, weightBL[side], sum, sumWeight );
|
||||
XeGTAO_AddSample( ssaoValueBR, weightBR[side], sum, sumWeight );
|
||||
|
||||
aoTerm[side] = sum / sumWeight;
|
||||
|
||||
XeGTAO_Output( pixCoord, outputTexture, aoTerm[side], finalApply );
|
||||
|
||||
#ifdef XE_GTAO_SHOW_BENT_NORMALS
|
||||
if( finalApply )
|
||||
{
|
||||
g_outputDbgImage[pixCoord] = float4( DisplayNormalSRGB( aoTerm[side].xyz /** aoTerm[side].www*/ ), 1 );
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generic viewspace normal generate pass
|
||||
float3 XeGTAO_ComputeViewspaceNormal( const uint2 pixCoord, const GTAOConstants consts, Texture2D<float> sourceNDCDepth, SamplerState depthSampler )
|
||||
{
|
||||
float2 normalizedScreenPos = (pixCoord + 0.5.xx) * consts.ViewportPixelSize;
|
||||
|
||||
float4 valuesUL = sourceNDCDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ) );
|
||||
float4 valuesBR = sourceNDCDepth.GatherRed( depthSampler, float2( pixCoord * consts.ViewportPixelSize ), int2( 1, 1 ) );
|
||||
|
||||
// viewspace Z at the center
|
||||
float viewspaceZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesUL.y, consts ); //sourceViewspaceDepth.SampleLevel( depthSampler, normalizedScreenPos, 0 ).x;
|
||||
|
||||
// viewspace Zs left top right bottom
|
||||
const float pixLZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesUL.x, consts );
|
||||
const float pixTZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesUL.z, consts );
|
||||
const float pixRZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesBR.z, consts );
|
||||
const float pixBZ = XeGTAO_ScreenSpaceToViewSpaceDepth( valuesBR.x, consts );
|
||||
|
||||
lpfloat4 edgesLRTB = XeGTAO_CalculateEdges( (lpfloat)viewspaceZ, (lpfloat)pixLZ, (lpfloat)pixRZ, (lpfloat)pixTZ, (lpfloat)pixBZ );
|
||||
|
||||
float3 CENTER = XeGTAO_ComputeViewspacePosition( normalizedScreenPos, viewspaceZ, consts );
|
||||
float3 LEFT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2(-1, 0) * consts.ViewportPixelSize, pixLZ, consts );
|
||||
float3 RIGHT = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 1, 0) * consts.ViewportPixelSize, pixRZ, consts );
|
||||
float3 TOP = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, -1) * consts.ViewportPixelSize, pixTZ, consts );
|
||||
float3 BOTTOM = XeGTAO_ComputeViewspacePosition( normalizedScreenPos + float2( 0, 1) * consts.ViewportPixelSize, pixBZ, consts );
|
||||
return XeGTAO_CalculateNormal( edgesLRTB, CENTER, LEFT, RIGHT, TOP, BOTTOM );
|
||||
}
|
||||
82
betterRenderer/shaders/XeGTAO/vaShaderCore.h
Normal file
82
betterRenderer/shaders/XeGTAO/vaShaderCore.h
Normal file
@@ -0,0 +1,82 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016-2021, Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Author(s): Filip Strugar (filip.strugar@intel.com)
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef VA_SHADER_CORE_H
|
||||
#define VA_SHADER_CORE_H
|
||||
|
||||
#ifndef VA_COMPILED_AS_SHADER_CODE
|
||||
|
||||
namespace Vanilla
|
||||
{
|
||||
#define VA_SATURATE vaMath::Saturate
|
||||
#define VA_MIN vaComponentMin
|
||||
#define VA_MAX vaComponentMax
|
||||
#define VA_LENGTH vaLength
|
||||
#define VA_INLINE inline
|
||||
#define VA_REFERENCE &
|
||||
#define VA_CONST const
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define VA_SATURATE saturate
|
||||
#define VA_MIN min
|
||||
#define VA_MAX max
|
||||
#define VA_LENGTH length
|
||||
#define VA_INLINE
|
||||
#define VA_REFERENCE
|
||||
#define VA_CONST
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef VA_COMPILED_AS_SHADER_CODE
|
||||
|
||||
#include "Core/vaCoreIncludes.h"
|
||||
|
||||
#else
|
||||
|
||||
// Vanilla-specific; this include gets intercepted and macros are provided through it to allow for some macro
|
||||
// shenanigans that don't work through the normal macro string pairs (like #include macros!).
|
||||
#include "MagicMacrosMagicFile.h"
|
||||
|
||||
// Vanilla defaults to column-major matrices in shaders because that is the DXC default with no arguments, and it
|
||||
// seems to be more common* in general. (*AFAIK)
|
||||
// This is in contrast to the C++ side, which is row-major, so the ordering of matrix operations in shaders needs
|
||||
// to be inverted, which is fine, for ex., "projectedPos = mul( g_globals.ViewProj, worldspacePos )"
|
||||
// One nice side-effect is that it's easy to drop the 4th column for 4x3 matrix (on c++ side), which becomes 3x4
|
||||
// (on the shader side), which is useful for reducing memory traffic for affine transforms.
|
||||
// See https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#appendix-a-matrix-representation
|
||||
// and http://www.mindcontrol.org/~hplus/graphics/matrix-layout.html for more detail.
|
||||
|
||||
#define vaMatrix4x4 column_major float4x4
|
||||
#define vaMatrix4x3 column_major float3x4
|
||||
#define vaMatrix3x3 column_major float3x3
|
||||
#define vaVector4 float4
|
||||
#define vaVector3 float3
|
||||
#define vaVector2 float2
|
||||
#define vaVector2i int2
|
||||
#define vaVector2ui uint2
|
||||
#define vaVector4i int4
|
||||
#define vaVector4ui uint4
|
||||
|
||||
#define CONCATENATE_HELPER(a, b) a##b
|
||||
#define CONCATENATE(a, b) CONCATENATE_HELPER(a, b)
|
||||
|
||||
#define B_CONCATENATER(x) CONCATENATE(b,x)
|
||||
#define S_CONCATENATER(x) CONCATENATE(s,x)
|
||||
#define T_CONCATENATER(x) CONCATENATE(t,x)
|
||||
#define U_CONCATENATER(x) CONCATENATE(u,x)
|
||||
|
||||
#define ShaderMin( x, y ) min( x, y )
|
||||
|
||||
#endif
|
||||
|
||||
#endif // VA_SHADER_CORE_H
|
||||
149
betterRenderer/shaders/contact_shadows.hlsl
Normal file
149
betterRenderer/shaders/contact_shadows.hlsl
Normal file
@@ -0,0 +1,149 @@
|
||||
Texture2D<float> g_DepthTexture : register(t0);
|
||||
RWTexture2D<float> g_Output : register(u0);
|
||||
sampler g_SamplerLinearClamp : register(s0);
|
||||
|
||||
// From https://www.shadertoy.com/view/3tB3z3 - except we're using R2 here
|
||||
#define XE_HILBERT_LEVEL 6U
|
||||
#define XE_HILBERT_WIDTH ( (1U << XE_HILBERT_LEVEL) )
|
||||
#define XE_HILBERT_AREA ( XE_HILBERT_WIDTH * XE_HILBERT_WIDTH )
|
||||
inline uint HilbertIndex( uint posX, uint posY )
|
||||
{
|
||||
uint index = 0U;
|
||||
for( uint curLevel = XE_HILBERT_WIDTH/2U; curLevel > 0U; curLevel /= 2U )
|
||||
{
|
||||
uint regionX = ( posX & curLevel ) > 0U;
|
||||
uint regionY = ( posY & curLevel ) > 0U;
|
||||
index += curLevel * curLevel * ( (3U * regionX) ^ regionY);
|
||||
if( regionY == 0U )
|
||||
{
|
||||
if( regionX == 1U )
|
||||
{
|
||||
posX = uint( (XE_HILBERT_WIDTH - 1U) ) - posX;
|
||||
posY = uint( (XE_HILBERT_WIDTH - 1U) ) - posY;
|
||||
}
|
||||
|
||||
uint temp = posX;
|
||||
posX = posY;
|
||||
posY = temp;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// Engine-specific screen & temporal noise loader
|
||||
float2 SpatioTemporalNoise( uint2 pixCoord, uint temporalIndex ) // without TAA, temporalIndex is always 0
|
||||
{
|
||||
float2 noise;
|
||||
#if 1 // Hilbert curve driving R2 (see https://www.shadertoy.com/view/3tB3z3)
|
||||
#ifdef XE_GTAO_HILBERT_LUT_AVAILABLE // load from lookup texture...
|
||||
uint index = g_srcHilbertLUT.Load( uint3( pixCoord % 64, 0 ) ).x;
|
||||
#else // ...or generate in-place?
|
||||
uint index = HilbertIndex( pixCoord.x, pixCoord.y );
|
||||
#endif
|
||||
index += 288*(temporalIndex%64); // why 288? tried out a few and that's the best so far (with XE_HILBERT_LEVEL 6U) - but there's probably better :)
|
||||
// R2 sequence - see http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
|
||||
return float2( frac( 0.5 + index * float2(0.75487766624669276005, 0.5698402909980532659114) ) );
|
||||
#else // Pseudo-random (fastest but looks bad - not a good choice)
|
||||
uint baseHash = Hash32( pixCoord.x + (pixCoord.y << 15) );
|
||||
baseHash = Hash32Combine( baseHash, temporalIndex );
|
||||
return float2( Hash32ToFloat( baseHash ), Hash32ToFloat( Hash32( baseHash ) ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
cbuffer DrawConstants : register(b0) {
|
||||
float4x4 g_Projection;
|
||||
float4x4 g_InverseProjection;
|
||||
float3 g_LightDirView;
|
||||
float g_NumSamples;
|
||||
float g_SampleRange;
|
||||
float g_Thickness;
|
||||
uint g_FrameIndex;
|
||||
}
|
||||
|
||||
uint2 NdcToPixel(in float2 size, in float4 ndc);
|
||||
float2 NdcToUv(in float4 ndc);
|
||||
float4 PixelToNdc(in float2 size, in uint2 pixel, in float depth);
|
||||
float4 UvToNdc(in float2 uv, in float depth);
|
||||
uint2 ViewToPixel(in float2 size, in float3 view);
|
||||
float2 ViewToUv(in float3 view);
|
||||
float GetPixelDepth(in float2 size, in uint2 pixel);
|
||||
float GetUvDepth(in float2 uv);
|
||||
float3 PixelToViewWithDepth(in float2 size, in uint2 pixel);
|
||||
float3 UvToViewWithDepth(in float2 uv);
|
||||
|
||||
[numthreads(8, 8, 1)]
|
||||
void main(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
float2 size;
|
||||
g_DepthTexture.GetDimensions(size.x, size.y);
|
||||
uint2 pixel = PixCoord.xy;
|
||||
|
||||
float occlusion = 0;
|
||||
g_Output[pixel] = 1.;
|
||||
|
||||
float2 noise = SpatioTemporalNoise(PixCoord.xy, g_FrameIndex);
|
||||
float3 viewPosition = PixelToViewWithDepth(size, pixel);
|
||||
float3 sample = viewPosition;
|
||||
float3 step = g_LightDirView * g_SampleRange / g_NumSamples;
|
||||
sample += noise.x * step;
|
||||
for(int i = 0; i < g_NumSamples; ++i) {
|
||||
sample += step;
|
||||
float2 sample_uv = ViewToUv(sample);
|
||||
if(all(and(sample_uv > 0, sample_uv < 1.))) {
|
||||
float depthDelta = sample.z * .998 - GetUvDepth(sample_uv);
|
||||
if(depthDelta < 0. && depthDelta > -g_Thickness) {
|
||||
occlusion = 1.;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_Output[pixel] = 1. - occlusion;
|
||||
}
|
||||
|
||||
uint2 NdcToPixel(in float2 size, in float4 ndc) {
|
||||
return (uint2)floor(NdcToUv(ndc) * size);
|
||||
}
|
||||
|
||||
float2 NdcToUv(in float4 ndc) {
|
||||
return ndc.xy * float2(.5, -.5) + .5;
|
||||
}
|
||||
|
||||
float4 PixelToNdc(in float2 size, in uint2 pixel, in float ndc_depth) {
|
||||
return float4(float2(2., -2.) * ((((float2)pixel + .5) / size) - .5), ndc_depth, 1.);
|
||||
}
|
||||
|
||||
float4 UvToNdc(in float2 uv, in float ndc_depth) {
|
||||
return float4(float2(2., -2.) * (uv - .5), ndc_depth, 1.);
|
||||
}
|
||||
|
||||
uint2 ViewToPixel(in float2 size, in float3 view) {
|
||||
float4 ndc = mul(g_Projection, float4(view, 1.));
|
||||
ndc /= ndc.w;
|
||||
return NdcToPixel(size, ndc);
|
||||
}
|
||||
|
||||
float2 ViewToUv(in float3 view) {
|
||||
float4 ndc = mul(g_Projection, float4(view, 1.));
|
||||
ndc /= ndc.w;
|
||||
return NdcToUv(ndc);
|
||||
}
|
||||
|
||||
float GetPixelDepth(in float2 size, in uint2 pixel) {
|
||||
return PixelToViewWithDepth(size, pixel).z;
|
||||
}
|
||||
|
||||
float GetUvDepth(in float2 uv) {
|
||||
return UvToViewWithDepth(uv).z;
|
||||
}
|
||||
|
||||
float3 PixelToViewWithDepth(in float2 size, in uint2 pixel) {
|
||||
float4 ndc = PixelToNdc(size, pixel, g_DepthTexture[pixel]);
|
||||
ndc = mul(g_InverseProjection, ndc);
|
||||
return ndc.xyz / ndc.w;
|
||||
}
|
||||
|
||||
float3 UvToViewWithDepth(in float2 uv) {
|
||||
float4 ndc = UvToNdc(uv, g_DepthTexture.SampleLevel(g_SamplerLinearClamp, uv, 0.));
|
||||
ndc = mul(g_InverseProjection, ndc);
|
||||
return ndc.xyz / ndc.w;
|
||||
}
|
||||
|
||||
18
betterRenderer/shaders/copy.hlsl
Normal file
18
betterRenderer/shaders/copy.hlsl
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
struct VertexOutput {
|
||||
float2 m_Position : Position;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
};
|
||||
|
||||
struct PixelOutput {
|
||||
float4 m_Output : SV_Target0;
|
||||
};
|
||||
|
||||
sampler source_sampler : register(s0);
|
||||
Texture2D source : register(t0);
|
||||
|
||||
PixelOutput main(VertexOutput ps_in) {
|
||||
PixelOutput result;
|
||||
result.m_Output = source.Sample(source_sampler, ps_in.m_Position);
|
||||
return result;
|
||||
}
|
||||
52
betterRenderer/shaders/cubemap_utils.hlsli
Normal file
52
betterRenderer/shaders/cubemap_utils.hlsli
Normal file
@@ -0,0 +1,52 @@
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define TWO_PI 6.283185307179586476925286766559
|
||||
#define ONE_OVER_PI 0.31830988618379067153776752674503
|
||||
#define TWO_OVER_PI 0.63661977236758134307553505349006
|
||||
#define ONE_OVER_TWO_PI 0.15915494309189533576888376337251
|
||||
|
||||
float3 CalcNormal(in uint3 PixCoord);
|
||||
|
||||
float2 EquirectFromNormal(in float3 Normal);
|
||||
|
||||
float2 PixCoordToFloat(in uint2 Coord, in uint2 Size);
|
||||
|
||||
float2 PixCoordToFloat(in uint2 Coord, in uint2 Size) {
|
||||
return ((float2)Coord + .5.xx) / (float2)Size;
|
||||
}
|
||||
|
||||
float3 CalcNormal(in uint3 PixCoord) {
|
||||
static const float3x3 FaceTransform[6] = {
|
||||
// +X
|
||||
float3x3( 0., 0., -1.,
|
||||
0., -1., 0.,
|
||||
1., 0., 0. ),
|
||||
// -X
|
||||
float3x3( 0., 0., 1.,
|
||||
0., -1., 0.,
|
||||
-1., 0., 0. ),
|
||||
// +Y
|
||||
float3x3( 1., 0., 0.,
|
||||
0., 0., 1.,
|
||||
0., 1., 0. ),
|
||||
// -Y
|
||||
float3x3( 1., 0., 0.,
|
||||
0., 0., -1.,
|
||||
0., -1., 0. ),
|
||||
// +Z
|
||||
float3x3( 1., 0., 0.,
|
||||
0., -1., 0.,
|
||||
0., 0., 1. ),
|
||||
// -Z
|
||||
float3x3( -1., 0., 0.,
|
||||
0., -1., 0.,
|
||||
0., 0., -1. )
|
||||
};
|
||||
uint2 FaceSize;
|
||||
uint Elements;
|
||||
g_OutCubemap.GetDimensions(FaceSize.x, FaceSize.y, Elements);
|
||||
return normalize(mul(float3(PixCoordToFloat(PixCoord.xy, FaceSize) * 2. - 1., 1.), FaceTransform[PixCoord.z]));
|
||||
}
|
||||
|
||||
float2 EquirectFromNormal(in float3 Normal) {
|
||||
return float2(atan2(Normal.x, Normal.z) * ONE_OVER_TWO_PI, -asin(Normal.y) * ONE_OVER_PI) + .5.xx;
|
||||
}
|
||||
39
betterRenderer/shaders/cubemap_vertex.hlsl
Normal file
39
betterRenderer/shaders/cubemap_vertex.hlsl
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma pack_matrix(column_major)
|
||||
|
||||
struct VertexInput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
};
|
||||
|
||||
cbuffer VertexConstants : register(b0) {
|
||||
float4x4 g_FaceProjection;
|
||||
};
|
||||
|
||||
#include "manul/draw_constants.hlsli"
|
||||
|
||||
VertexOutput main(in VertexInput vs_in) {
|
||||
VertexOutput result;
|
||||
float4x3 model_view = GetModelView();
|
||||
result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz;
|
||||
result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz;
|
||||
result.m_Normal = vs_in.m_Normal;
|
||||
result.m_TexCoord = vs_in.m_TexCoord;
|
||||
result.m_Tangent.xyz = vs_in.m_Tangent.xyz;
|
||||
result.m_Tangent.w = vs_in.m_Tangent.w;
|
||||
result.m_PositionSV = mul(g_FaceProjection, float4(result.m_Position, 1.));
|
||||
result.m_PositionCS = result.m_PositionSV;
|
||||
result.m_HistoryPositionCS = result.m_PositionSV;
|
||||
return result;
|
||||
}
|
||||
43
betterRenderer/shaders/culling.hlsl
Normal file
43
betterRenderer/shaders/culling.hlsl
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
StructuredBuffer<float4x3> g_InTransforms : register(t0);
|
||||
RWStructuredBuffer<float4x3> g_OutCulledTransforms : register(u0);
|
||||
|
||||
struct CommandBuffer {
|
||||
uint g_IndexCountPerInstance;
|
||||
uint g_InstanceCount;
|
||||
uint g_StartIndexLocation;
|
||||
int g_BaseVertexLocation;
|
||||
uint g_StartInstanceLocation;
|
||||
};
|
||||
|
||||
RWStructuredBuffer<CommandBuffer> g_CommandBuffer : register(u1);
|
||||
|
||||
cbuffer ViewData : register(b0) {
|
||||
float4 g_FrustumPlanes[6];
|
||||
float4x4 g_Projection;
|
||||
}
|
||||
|
||||
cbuffer PushConstants : register(b1) {
|
||||
float3 g_Origin;
|
||||
float g_InstanceRadius;
|
||||
float g_MinRadius;
|
||||
float g_MaxRadius;
|
||||
uint g_NumInstances;
|
||||
}
|
||||
|
||||
[numthreads(64, 1, 1)]
|
||||
void main(uint3 DispatchId : SV_DispatchThreadID) {
|
||||
if(DispatchId.x >= g_NumInstances) return;
|
||||
float4x3 instance = g_InTransforms[DispatchId.x];
|
||||
//float3 position = mul(float4(0., 0., 0., 1.), instance) + g_Origin;
|
||||
//for(int i = 0; i < 6; ++i) {
|
||||
// if(dot(float4(position, 1.), g_FrustumPlanes[i]) < -g_InstanceRadius) return;
|
||||
//}
|
||||
//float4 ndc = mul(g_Projection, float4(0., g_InstanceRadius, -length(position), 1.));
|
||||
//float radius = ndc.y / ndc.w;
|
||||
//if(radius > g_MinRadius && radius <= g_MaxRadius) {
|
||||
uint index;
|
||||
InterlockedAdd(g_CommandBuffer[0].g_InstanceCount, 1, index);
|
||||
g_OutCulledTransforms[index] = instance;
|
||||
//}
|
||||
}
|
||||
42
betterRenderer/shaders/default_vertex.hlsl
Normal file
42
betterRenderer/shaders/default_vertex.hlsl
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma pack_matrix(column_major)
|
||||
|
||||
struct VertexInput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
};
|
||||
|
||||
cbuffer VertexConstants : register(b0) {
|
||||
float4x4 g_JitteredProjection;
|
||||
float4x4 g_Projection;
|
||||
float4x4 g_ProjectionHistory;
|
||||
};
|
||||
|
||||
#include "manul/draw_constants.hlsli"
|
||||
|
||||
VertexOutput main(in VertexInput vs_in) {
|
||||
VertexOutput result;
|
||||
float4x3 model_view = GetModelView();
|
||||
float4x3 model_view_history = GetModelViewHistory();
|
||||
result.m_Position = mul(float4(vs_in.m_Position, 1.), model_view).xyz;
|
||||
result.m_Normal = mul(float4(vs_in.m_Normal, 0.), model_view).xyz;
|
||||
result.m_TexCoord = vs_in.m_TexCoord;
|
||||
result.m_Tangent.xyz = mul(float4(vs_in.m_Tangent.xyz, 0.), model_view).xyz;
|
||||
result.m_Tangent.w = vs_in.m_Tangent.w;
|
||||
result.m_PositionSV = mul(g_JitteredProjection, float4(result.m_Position, 1.));
|
||||
result.m_PositionCS = mul(g_Projection, float4(result.m_Position, 1.));
|
||||
float3 history_position = mul(float4(vs_in.m_Position, 1.), model_view_history);
|
||||
result.m_HistoryPositionCS = mul(g_ProjectionHistory, float4(history_position, 1.));
|
||||
return result;
|
||||
}
|
||||
213
betterRenderer/shaders/envmap.hlsl
Normal file
213
betterRenderer/shaders/envmap.hlsl
Normal file
@@ -0,0 +1,213 @@
|
||||
|
||||
RWTexture2DArray<float4> g_OutCubemap : register(u0);
|
||||
RWTexture2D<float2> g_OutBRDF : register(u1);
|
||||
|
||||
Texture2D<float3> g_SourceEquirect : register(t0);
|
||||
TextureCube<float3> g_SourceCube : register(t1);
|
||||
Texture1D<float2> g_SampleKernel : register(t2);
|
||||
SamplerState g_SamplerLinearClampV : register(s0);
|
||||
SamplerState g_SamplerLinearClamp : register(s1);
|
||||
|
||||
#include "cubemap_utils.hlsli"
|
||||
|
||||
float DistributionGGX(float NdotH, float roughness);
|
||||
float GeometrySchlickGGX(float NdotV, float roughness);
|
||||
float GeometrySmith(float roughness, float NdotV, float NdotL);
|
||||
float3 ImportanceSampleGGX(in float2 Xi,in float Roughness , in float3 N);
|
||||
void ImportanceSampleCosDir(in float2 u, in float3 N, out float3 L, out float NdotL, out float pdf);
|
||||
|
||||
float2 GetSample(uint sample, uint sampleCount);
|
||||
|
||||
struct FilterParameters {
|
||||
float m_PreExposureMul;
|
||||
float m_Roughness;
|
||||
float m_SampleCount;
|
||||
float m_SourceWidth;
|
||||
float m_MipBias;
|
||||
uint3 m_Offset;
|
||||
};
|
||||
|
||||
#ifdef SPIRV
|
||||
|
||||
[[vk::push_constant]] ConstantBuffer<FilterParameters> g_FilterParams;
|
||||
|
||||
#else
|
||||
|
||||
cbuffer g_Const : register(b0) { FilterParameters g_FilterParams; }
|
||||
|
||||
#endif
|
||||
|
||||
#include "manul/sky.hlsli"
|
||||
|
||||
[numthreads(32, 32, 1)]
|
||||
void CSSampleEquirectangular(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
//Sky(g_OutCubemap[PixCoord + g_FilterParams.m_Offset], CalcNormal(PixCoord + g_FilterParams.m_Offset), normalize(float3(0., .1, 1.)), 0.);
|
||||
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = clamp(g_SourceEquirect.SampleLevel(g_SamplerLinearClampV, EquirectFromNormal(CalcNormal(PixCoord)), 0.) * g_FilterParams.m_PreExposureMul, 0., 65535.);
|
||||
}
|
||||
|
||||
[numthreads(32, 32, 1)]
|
||||
void CSDiffuseIBL(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
float3 N = CalcNormal(PixCoord + g_FilterParams.m_Offset);
|
||||
|
||||
float sampleCount = g_FilterParams.m_SampleCount;
|
||||
float width = g_FilterParams.m_SourceWidth;
|
||||
float mipBias = g_FilterParams.m_MipBias;
|
||||
|
||||
float3 accBrdf = 0;
|
||||
for ( uint i =0; i < sampleCount ; ++ i ) {
|
||||
float2 eta = GetSample (i , sampleCount ) ;
|
||||
float3 L ;
|
||||
float NdotL ;
|
||||
float pdf ;
|
||||
ImportanceSampleCosDir ( eta , N , L , NdotL , pdf ) ;
|
||||
if ( NdotL >0) {
|
||||
float omegaS = 1. / ( sampleCount * pdf );
|
||||
float omegaP = 4. * PI / (6. * width * width );
|
||||
float mipLevel = max(0., mipBias + .5 * log2 ( omegaS / omegaP ));
|
||||
accBrdf += g_SourceCube . SampleLevel ( g_SamplerLinearClamp , L, mipLevel ) ;
|
||||
}
|
||||
}
|
||||
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = accBrdf * (1. / sampleCount);
|
||||
}
|
||||
|
||||
[numthreads(32, 32, 1)]
|
||||
void CSSpecularIBL(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
float3 N = CalcNormal(PixCoord + g_FilterParams.m_Offset);
|
||||
|
||||
float roughness = g_FilterParams.m_Roughness;
|
||||
float width = g_FilterParams.m_SourceWidth;
|
||||
float sampleCount = g_FilterParams.m_SampleCount;
|
||||
float mipBias = g_FilterParams.m_MipBias;
|
||||
|
||||
float3 accBrdf = 0;
|
||||
float accWeight = 0;
|
||||
for ( uint i =0; i < sampleCount ; ++ i ) {
|
||||
float2 eta = GetSample (i , sampleCount ) ;
|
||||
float pdf ;
|
||||
float3 H = ImportanceSampleGGX(eta, roughness, N);
|
||||
float3 L = normalize(2 * dot( N, H ) * H - N);
|
||||
float NdotL = dot(N, L);
|
||||
float NdotH = dot(N, H);
|
||||
if(NdotL > 0) {
|
||||
float D = DistributionGGX(NdotH, roughness);
|
||||
float pdf = D / 4.;
|
||||
float omegaS = 1. / ( sampleCount * pdf );
|
||||
float omegaP = 4. * PI / (6. * width * width );
|
||||
float mipLevel = roughness == 0. ? mipBias : max(0., mipBias + .5 * log2 ( omegaS / omegaP ));
|
||||
accBrdf += g_SourceCube . SampleLevel ( g_SamplerLinearClamp , L, mipLevel ) * NdotL;
|
||||
accWeight += NdotL;
|
||||
}
|
||||
}
|
||||
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = accBrdf * (1. / accWeight);
|
||||
}
|
||||
|
||||
[numthreads(32, 32, 1)]
|
||||
void CSIntegrateBRDF(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
uint2 FaceSize;
|
||||
g_OutBRDF.GetDimensions(FaceSize.x, FaceSize.y);
|
||||
float NoV = ((float)PixCoord.x + .5) / (float)FaceSize.x;
|
||||
float Roughness = ((float)PixCoord.y + .5) / (float)FaceSize.y;
|
||||
float3 N = float3(0., 0., 1.);
|
||||
|
||||
float3 V;
|
||||
V.x = sqrt( 1. - NoV * NoV ); // sin
|
||||
V.y = 0.;
|
||||
V.z = NoV; // cos
|
||||
float A = 0.;
|
||||
float B = 0.;
|
||||
float sampleCount = 1024.;
|
||||
for( uint i = 0; i < sampleCount; i++ )
|
||||
{
|
||||
float2 Xi = GetSample (i , sampleCount );
|
||||
float3 H = ImportanceSampleGGX(Xi, Roughness, N);
|
||||
float3 L = normalize(2. * dot( V, H ) * H - V);
|
||||
float NoL = saturate( L.z );
|
||||
float NoH = saturate( H.z );
|
||||
float VoH = saturate( dot( V, H ) );
|
||||
if( NoL > 0. )
|
||||
{
|
||||
float G = GeometrySmith( Roughness, NoV, NoL );
|
||||
float G_Vis = G * VoH / (NoH * NoV);
|
||||
float Fc = pow( 1. - VoH, 5. );
|
||||
A += (1. - Fc) * G_Vis;
|
||||
B += Fc * G_Vis;
|
||||
}
|
||||
}
|
||||
g_OutBRDF[PixCoord.xy] = float2( A, B ) / sampleCount;
|
||||
}
|
||||
|
||||
[numthreads(32, 32, 1)]
|
||||
void CSGenerateCubeMip(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = g_SourceCube.SampleLevel(g_SamplerLinearClamp, CalcNormal(PixCoord + g_FilterParams.m_Offset), 0.);
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
||||
{
|
||||
// note that we use a different k for IBL
|
||||
float a = roughness;
|
||||
float k = (a * a) / 2.0;
|
||||
|
||||
float nom = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
float GeometrySmith(float roughness, float NdotV, float NdotL)
|
||||
{
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
float DistributionGGX(float NdotH, float roughness)
|
||||
{
|
||||
float a = roughness*roughness;
|
||||
float a2 = a*a;
|
||||
|
||||
float nom = a2;
|
||||
float denom = (NdotH*NdotH * (a2 - 1.0) + 1.0);
|
||||
denom = PI * denom * denom;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
void ImportanceSampleCosDir (in float2 Xi, in float3 N, out float3 L, out float NdotL, out float pdf) {
|
||||
// Local referencial
|
||||
float3 upVector = abs (N.z) < .999 ? float3 (0., 0., 1.) : float3 (1., 0., 0.) ;
|
||||
float3 tangentX = normalize ( cross ( upVector , N ) ) ;
|
||||
float3 tangentY = cross ( N , tangentX ) ;
|
||||
|
||||
float r = sqrt ( Xi.x ) ;
|
||||
float phi = Xi.y * TWO_PI;
|
||||
|
||||
L = float3 ( r * cos ( phi ) , r * sin ( phi ) , sqrt ( max (0. ,1. - Xi.x ) ) ) ;
|
||||
L = normalize ( tangentX * L . y + tangentY * L . x + N * L . z ) ;
|
||||
|
||||
NdotL = dot (L , N ) ;
|
||||
pdf = NdotL * ONE_OVER_PI ;
|
||||
}
|
||||
|
||||
float3 ImportanceSampleGGX(in float2 Xi,in float Roughness , in float3 N)
|
||||
{
|
||||
double a = Roughness * Roughness;
|
||||
float Phi = TWO_PI * Xi.x;
|
||||
float CosTheta = sqrt( (float)((1. - Xi.y) / ( 1. + (a*a - 1.) * Xi.y ) ));
|
||||
float SinTheta = sqrt( 1. - CosTheta * CosTheta );
|
||||
float3 H = float3( SinTheta * cos( Phi ), SinTheta * sin( Phi ), CosTheta);
|
||||
float3 UpVector = abs(N.z) < .999 ? float3(0.,0.,1.) : float3(1.,0.,0.);
|
||||
float3 TangentX = normalize( cross( UpVector , N ) );
|
||||
float3 TangentY = cross( N, TangentX );
|
||||
// Tangent to world space
|
||||
return normalize( TangentX * H.x + TangentY * H.y + N * H.z );
|
||||
}
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
return float(reversebits(bits)) * 2.3283064365386963e-10; // / 0x100000000
|
||||
}
|
||||
|
||||
float2 GetSample(uint i, uint N) {
|
||||
return float2((float)i/(float)N, RadicalInverse_VdC(i));
|
||||
}
|
||||
16
betterRenderer/shaders/fx_vertex.hlsl
Normal file
16
betterRenderer/shaders/fx_vertex.hlsl
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
struct VertexInput {
|
||||
float2 m_Position : Position;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float2 m_Position : Position;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
};
|
||||
|
||||
VertexOutput main(in VertexInput vs_in) {
|
||||
VertexOutput result;
|
||||
result.m_Position = vs_in.m_Position;
|
||||
result.m_PositionSV = float4(vs_in.m_Position * float2(2., -2.) + float2(-1., 1.), 0., 1.);
|
||||
return result;
|
||||
}
|
||||
69
betterRenderer/shaders/gbuffer_cube_lighting.hlsl
Normal file
69
betterRenderer/shaders/gbuffer_cube_lighting.hlsl
Normal file
@@ -0,0 +1,69 @@
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define TWO_PI 6.283185307179586476925286766559
|
||||
#define ONE_OVER_PI 0.31830988618379067153776752674503
|
||||
#define TWO_OVER_PI 0.63661977236758134307553505349006
|
||||
#define ONE_OVER_TWO_PI 0.15915494309189533576888376337251
|
||||
|
||||
RWTexture2DArray<float4> g_OutCubemap : register(u0);
|
||||
|
||||
TextureCube<float3> g_Skybox : register(t0);
|
||||
TextureCube<float3> g_Diffuse : register(t1);
|
||||
TextureCube<float3> g_Normal : register(t2);
|
||||
TextureCube<float4> g_Params : register(t3);
|
||||
TextureCube<float> g_Depth : register(t4);
|
||||
TextureCube<float3> g_DiffuseIBL : register(t5);
|
||||
|
||||
SamplerState g_SamplerLinearClamp : register(s0);
|
||||
SamplerState g_SamplerPointClamp : register(s1);
|
||||
|
||||
#include "cubemap_utils.hlsli"
|
||||
#include "manul/sky.hlsli"
|
||||
|
||||
struct FilterParameters {
|
||||
uint3 m_Offset;
|
||||
uint unused;
|
||||
float3 m_LightVector;
|
||||
float m_Altitude;
|
||||
float3 m_LightColor;
|
||||
};
|
||||
|
||||
#ifdef SPIRV
|
||||
|
||||
[[vk::push_constant]] ConstantBuffer<FilterParameters> g_FilterParams;
|
||||
|
||||
#else
|
||||
|
||||
cbuffer g_Const : register(b0) { FilterParameters g_FilterParams; }
|
||||
|
||||
#endif
|
||||
|
||||
cbuffer FilterConstants : register(b1) {
|
||||
float4x4 g_InverseProjection;
|
||||
};
|
||||
|
||||
[numthreads(32, 32, 1)]
|
||||
void main(uint3 PixCoord : SV_DispatchThreadID) {
|
||||
float3 normal = CalcNormal(PixCoord + g_FilterParams.m_Offset);
|
||||
float3 size;
|
||||
g_OutCubemap.GetDimensions(size.x, size.y, size.z);
|
||||
|
||||
//g_OutCubemap[PixCoord + g_Offset] = g_Skybox.SampleLevel(g_SamplerLinearClamp, normal, 0.);
|
||||
float3 color = 1.e-7;
|
||||
CalcAtmosphere(color, normal, g_FilterParams.m_LightVector);
|
||||
//CalcAtmosphere(g_OutCubemap[PixCoord + g_Offset], 1., normal, g_LightVector, g_Altitude, SKY_INF, g_LightColor.rgb, 10);
|
||||
float3 normal_flipped = normal * float3(-1., 1., 1.);
|
||||
float depth = g_Depth.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.);
|
||||
float linear_depth = 2500.;
|
||||
if(depth > 0.) {
|
||||
float3 material_albedo = g_Diffuse.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.);
|
||||
float4 material_params = g_Params.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.);
|
||||
float3 material_normal = (g_Normal.SampleLevel(g_SamplerPointClamp, normal_flipped, 0.) - .5) * 2.;
|
||||
float NdotL = max(dot(material_normal, g_FilterParams.m_LightVector), 0.);
|
||||
color = material_albedo * (g_DiffuseIBL.SampleLevel(g_SamplerPointClamp, material_normal, 0.) + ONE_OVER_PI * NdotL * g_FilterParams.m_LightColor.rgb) * material_params.b;
|
||||
float4 position_ndc = mul(g_InverseProjection, float4(PixCoordToFloat(PixCoord.xy + g_FilterParams.m_Offset.xy, size.xy) * 2. - 1., depth, 1.));
|
||||
position_ndc /= position_ndc.w;
|
||||
linear_depth = length(position_ndc.xyz);
|
||||
}
|
||||
ApplyAerialPerspective(color, 1., normal, g_FilterParams.m_LightVector, linear_depth/2500.);
|
||||
g_OutCubemap[PixCoord + g_FilterParams.m_Offset].rgb = color;
|
||||
}
|
||||
118
betterRenderer/shaders/gbufferblit.hlsl
Normal file
118
betterRenderer/shaders/gbufferblit.hlsl
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "manul/math.hlsli"
|
||||
|
||||
struct VertexOutput {
|
||||
float2 m_Position : Position;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
};
|
||||
|
||||
struct PixelOutput {
|
||||
float4 m_Color : SV_Target0;
|
||||
};
|
||||
|
||||
Texture2D<float3> gbuffer_diffuse : register(t0);
|
||||
Texture2D<float3> gbuffer_emission : register(t1);
|
||||
Texture2D<float4> gbuffer_params : register(t2);
|
||||
Texture2D<float3> gbuffer_normal : register(t3);
|
||||
Texture2D<float> gbuffer_depth : register(t4);
|
||||
|
||||
RWTexture2D<float4> output : register(u0);
|
||||
|
||||
#define DEFERRED_LIGHTING_PASS
|
||||
|
||||
#include "manul/gbuffer_ssao.hlsli"
|
||||
//#include "manul/gbuffer_contact_shadows.hlsli"
|
||||
#include "manul/shadow.hlsli"
|
||||
#include "manul/lighting.hlsli"
|
||||
#include "manul/sky.hlsli"
|
||||
|
||||
#define BLOCK_SIZE 8
|
||||
#define TILE_BORDER 1
|
||||
#define TILE_SIZE (BLOCK_SIZE + 2 * TILE_BORDER)
|
||||
|
||||
groupshared float2 tile_XY[TILE_SIZE*TILE_SIZE];
|
||||
groupshared float tile_Z[TILE_SIZE*TILE_SIZE];
|
||||
|
||||
uint2 unflatten2D(uint idx, uint2 dim)
|
||||
{
|
||||
return uint2(idx % dim.x, idx / dim.x);
|
||||
}
|
||||
|
||||
uint flatten2D(uint2 coord, uint2 dim)
|
||||
{
|
||||
return coord.x + coord.y * dim.x;
|
||||
}
|
||||
|
||||
[numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)]
|
||||
void main(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) {
|
||||
uint2 gbuffer_dimensions;
|
||||
gbuffer_depth.GetDimensions(gbuffer_dimensions.x, gbuffer_dimensions.y);
|
||||
|
||||
const int2 tile_upperleft = GroupID.xy * BLOCK_SIZE - TILE_BORDER;
|
||||
for (uint t = GroupIndex; t < TILE_SIZE * TILE_SIZE; t += BLOCK_SIZE * BLOCK_SIZE)
|
||||
{
|
||||
const uint2 pixel = tile_upperleft + unflatten2D(t, TILE_SIZE);
|
||||
const float depth = gbuffer_depth[pixel];
|
||||
const float3 position = ReconstructPos(PixelToCS(pixel, gbuffer_dimensions), depth);
|
||||
tile_XY[t] = position.xy;
|
||||
tile_Z[t] = position.z;
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
// Decode material data
|
||||
MaterialData material;
|
||||
|
||||
material.m_PixelCoord = PixCoord.xy;
|
||||
float2 uv = ( material.m_PixelCoord + .5) / gbuffer_dimensions;
|
||||
|
||||
uint2 tile_co = material.m_PixelCoord - tile_upperleft;
|
||||
|
||||
//float depth = gbuffer_depth[ material.m_PixelCoord];
|
||||
uint co = flatten2D(tile_co, TILE_SIZE);
|
||||
|
||||
uint co_px = flatten2D(tile_co + int2(1, 0), TILE_SIZE);
|
||||
uint co_nx = flatten2D(tile_co + int2(-1, 0), TILE_SIZE);
|
||||
uint co_py = flatten2D(tile_co + int2(0, 1), TILE_SIZE);
|
||||
uint co_ny = flatten2D(tile_co + int2(0, -1), TILE_SIZE);
|
||||
|
||||
float depth = tile_Z[co];
|
||||
float depth_px = tile_Z[co_px];
|
||||
float depth_nx = tile_Z[co_nx];
|
||||
float depth_py = tile_Z[co_py];
|
||||
float depth_ny = tile_Z[co_ny];
|
||||
|
||||
material.m_Position = float3(tile_XY[co], depth);
|
||||
if(abs(depth_px - depth) < abs(depth_nx - depth)) {
|
||||
material.m_PositionDDX.xy = tile_XY[co_px];
|
||||
material.m_PositionDDX.z = depth_px;
|
||||
}
|
||||
else{
|
||||
material.m_PositionDDX.xy = tile_XY[co_nx];
|
||||
material.m_PositionDDX.z = depth_nx;
|
||||
}
|
||||
if(abs(depth_py - depth) < abs(depth_ny - depth)) {
|
||||
material.m_PositionDDY.xy = tile_XY[co_py];
|
||||
material.m_PositionDDY.z = depth_py;
|
||||
}
|
||||
else{
|
||||
material.m_PositionDDY.xy = tile_XY[co_ny];
|
||||
material.m_PositionDDY.z = depth_ny;
|
||||
}
|
||||
|
||||
material.m_MaterialAlbedoAlpha.rgb = gbuffer_diffuse[ material.m_PixelCoord];
|
||||
material.m_MaterialAlbedoAlpha.a = 1.;
|
||||
material.m_MaterialEmission = gbuffer_emission[ material.m_PixelCoord];
|
||||
material.m_MaterialParams = gbuffer_params[ material.m_PixelCoord];
|
||||
material.m_MaterialNormal = UnpackNormalXYZ(gbuffer_normal[ material.m_PixelCoord]);
|
||||
|
||||
PixelOutput ps_out;
|
||||
#if LIGHTING_NEEDS_PIXELPOSITION
|
||||
ApplyMaterialLighting(output[material.m_PixelCoord], material, material.m_PixelCoord);
|
||||
#else
|
||||
ApplyMaterialLighting(output[material.m_PixelCoord], material);
|
||||
#endif
|
||||
float3 view_world = mul((float3x3)g_InverseModelView, material.m_Position);
|
||||
ApplyAerialPerspective(output[material.m_PixelCoord].rgb, 1., normalize(view_world), g_LightDir, length(view_world)/2500.);
|
||||
//if(depth < 1.){
|
||||
// CalcAtmosphere(ps_out.m_Color.rgb, 1., normalize(mul((float3x3) g_InverseModelView, material.m_Position)), g_LightDir.xyz, g_Altitude, length(view.xyz), g_LightColor.rgb, 10);
|
||||
//}
|
||||
//return ps_out;
|
||||
}
|
||||
46
betterRenderer/shaders/gs_cubemap_viewports.hlsl
Normal file
46
betterRenderer/shaders/gs_cubemap_viewports.hlsl
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma pack_matrix(column_major)
|
||||
|
||||
struct VertexInput {
|
||||
float3 m_Position : Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float3 m_Position : Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
uint m_ViewportIndex : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
cbuffer CubeDrawConstants : register(b0) {
|
||||
float4x4 g_FaceProjections[6];
|
||||
};
|
||||
|
||||
[maxvertexcount(18)]
|
||||
void main(triangle in VertexInput gs_in[3], inout TriangleStream<VertexOutput> gs_out) {
|
||||
for(uint vp = 0; vp < 6; ++vp) {
|
||||
for(uint vertex = 0; vertex < 3; ++vertex) {
|
||||
VertexOutput output;
|
||||
VertexInput input = gs_in[2 - vertex];
|
||||
output.m_Position = input.m_Position;
|
||||
output.m_PositionCS = mul(g_FaceProjections[vp], float4(output.m_Position * float3(-1., 1., 1.), 1.));
|
||||
output.m_HistoryPositionCS = output.m_PositionCS;
|
||||
output.m_PositionSV = output.m_PositionCS;
|
||||
output.m_Normal = input.m_Normal;
|
||||
output.m_TexCoord = input.m_TexCoord;
|
||||
output.m_Tangent = input.m_Tangent;
|
||||
output.m_ViewportIndex = vp;
|
||||
gs_out.Append(output);
|
||||
}
|
||||
gs_out.RestartStrip();
|
||||
}
|
||||
}
|
||||
48
betterRenderer/shaders/gs_shadowcascade_viewports.hlsl
Normal file
48
betterRenderer/shaders/gs_shadowcascade_viewports.hlsl
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma pack_matrix(column_major)
|
||||
|
||||
struct VertexInput {
|
||||
float3 m_Position : Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float3 m_Position : Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
uint m_ViewportIndex : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
#define CASCADE_COUNT 4
|
||||
|
||||
cbuffer CubeDrawConstants : register(b0) {
|
||||
float4x4 g_CascadeProjections[CASCADE_COUNT];
|
||||
};
|
||||
|
||||
[maxvertexcount(CASCADE_COUNT*3)]
|
||||
void main(triangle in VertexInput gs_in[3], inout TriangleStream<VertexOutput> gs_out) {
|
||||
for(uint vp = 0; vp < CASCADE_COUNT; ++vp) {
|
||||
for(uint vertex = 0; vertex < 3; ++vertex) {
|
||||
VertexOutput output;
|
||||
VertexInput input = gs_in[vertex];
|
||||
output.m_Position = input.m_Position;
|
||||
output.m_PositionCS = mul(g_CascadeProjections[vp], float4(output.m_Position, 1.));
|
||||
output.m_HistoryPositionCS = output.m_PositionCS;
|
||||
output.m_PositionSV = output.m_PositionCS;
|
||||
output.m_Normal = input.m_Normal;
|
||||
output.m_TexCoord = input.m_TexCoord;
|
||||
output.m_Tangent = input.m_Tangent;
|
||||
output.m_ViewportIndex = vp;
|
||||
gs_out.Append(output);
|
||||
}
|
||||
gs_out.RestartStrip();
|
||||
}
|
||||
}
|
||||
109
betterRenderer/shaders/imgui_pixel.hlsl
Normal file
109
betterRenderer/shaders/imgui_pixel.hlsl
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#define MA_TONEMAP_SRGB_REC709 0
|
||||
#define MA_TONEMAP_LINEAR_REC2020 1
|
||||
|
||||
struct Constants
|
||||
{
|
||||
float2 invDisplaySize;
|
||||
float outputNits;
|
||||
int m_TonemapFunction;
|
||||
};
|
||||
|
||||
#ifdef SPIRV
|
||||
|
||||
[[vk::push_constant]] ConstantBuffer<Constants> g_Const;
|
||||
|
||||
#else
|
||||
|
||||
cbuffer g_Const : register(b0) { Constants g_Const; }
|
||||
|
||||
#endif
|
||||
|
||||
struct PS_INPUT
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float4 col : COLOR0;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
float3 LinearToSRGB(float3 color)
|
||||
{
|
||||
// Approximately pow(color, 1.0 / 2.2)
|
||||
return select(color < 0.0031308, 12.92 * color, 1.055 * pow(abs(color), 1.0 / 2.4) - 0.055);
|
||||
}
|
||||
|
||||
float3 SRGBToLinear(float3 color)
|
||||
{
|
||||
// Approximately pow(color, 2.2)
|
||||
return select(color < 0.04045, color / 12.92, pow(abs(color + 0.055) / 1.055, 2.4));
|
||||
}
|
||||
|
||||
float3 Rec709ToRec2020(float3 color)
|
||||
{
|
||||
static const float3x3 conversion =
|
||||
{
|
||||
0.627402, 0.329292, 0.043306,
|
||||
0.069095, 0.919544, 0.011360,
|
||||
0.016394, 0.088028, 0.895578
|
||||
};
|
||||
return mul(conversion, color);
|
||||
}
|
||||
|
||||
float3 LinearToST2084(float3 color)
|
||||
{
|
||||
float m1 = 2610.0 / 4096.0 / 4;
|
||||
float m2 = 2523.0 / 4096.0 * 128;
|
||||
float c1 = 3424.0 / 4096.0;
|
||||
float c2 = 2413.0 / 4096.0 * 32;
|
||||
float c3 = 2392.0 / 4096.0 * 32;
|
||||
float3 cp = pow(abs(color), m1);
|
||||
return pow((c1 + c2 * cp) / (1 + c3 * cp), m2);
|
||||
}
|
||||
|
||||
sampler sampler0 : register(s0);
|
||||
Texture2D texture0 : register(t0);
|
||||
|
||||
float4 main(PS_INPUT input) : SV_Target
|
||||
{
|
||||
float4 tex_col = texture0.Sample(sampler0, input.uv);
|
||||
float4 input_col = input.col;
|
||||
switch(g_Const.m_TonemapFunction) {
|
||||
case MA_TONEMAP_LINEAR_REC2020:
|
||||
{
|
||||
input_col.rgb = SRGBToLinear(input_col.rgb);
|
||||
float4 out_col = input_col * tex_col;
|
||||
const float st2084max = 10000.;
|
||||
const float hdrScalar = g_Const.outputNits / st2084max;
|
||||
//out_col.rgb = Rec709ToRec2020(out_col.rgb);
|
||||
out_col.rgb *= hdrScalar;
|
||||
out_col.rgb = LinearToST2084(out_col.rgb);
|
||||
return out_col;
|
||||
}
|
||||
default:
|
||||
{
|
||||
tex_col.rgb = LinearToSRGB(tex_col.rgb);
|
||||
return input_col * tex_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
betterRenderer/shaders/imgui_vertex.hlsl
Normal file
62
betterRenderer/shaders/imgui_vertex.hlsl
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
struct Constants
|
||||
{
|
||||
float2 invDisplaySize;
|
||||
float outputNits;
|
||||
};
|
||||
|
||||
#ifdef SPIRV
|
||||
|
||||
[[vk::push_constant]] ConstantBuffer<Constants> g_Const;
|
||||
|
||||
#else
|
||||
|
||||
cbuffer g_Const : register(b0) { Constants g_Const; }
|
||||
|
||||
#endif
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
float2 pos : POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float4 col : COLOR0;
|
||||
};
|
||||
|
||||
struct PS_INPUT
|
||||
{
|
||||
float4 out_pos : SV_POSITION;
|
||||
float4 out_col : COLOR0;
|
||||
float2 out_uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
PS_INPUT main(VS_INPUT input)
|
||||
{
|
||||
PS_INPUT output;
|
||||
output.out_pos.xy = input.pos.xy * g_Const.invDisplaySize * float2(2.0, -2.0) + float2(-1.0, 1.0);
|
||||
output.out_pos.zw = float2(0, 1);
|
||||
output.out_col = input.col;
|
||||
output.out_uv = input.uv;
|
||||
return output;
|
||||
}
|
||||
|
||||
41
betterRenderer/shaders/instanced_cubemap_vertex.hlsl
Normal file
41
betterRenderer/shaders/instanced_cubemap_vertex.hlsl
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma pack_matrix(column_major)
|
||||
|
||||
struct VertexInput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_InstanceTransform[3] : InstanceTransform;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
float3 m_Position : Position;
|
||||
float3 m_Normal : Normal;
|
||||
float2 m_TexCoord : TexCoord;
|
||||
float4 m_Tangent : Tangent;
|
||||
float4 m_PositionSV : SV_Position;
|
||||
float4 m_PositionCS : PositionCS;
|
||||
float4 m_HistoryPositionCS : HistoryPositionCS;
|
||||
};
|
||||
|
||||
cbuffer VertexConstants : register(b0) {
|
||||
float4x4 g_FaceProjection;
|
||||
};
|
||||
|
||||
#include "manul/draw_constants.hlsli"
|
||||
|
||||
VertexOutput main(in VertexInput vs_in) {
|
||||
VertexOutput result;
|
||||
float4x3 model_view = GetModelView();
|
||||
float4x4 instance_transform = transpose(float4x4(vs_in.m_InstanceTransform[0], vs_in.m_InstanceTransform[1], vs_in.m_InstanceTransform[2], float4(0., 0., 0., 1.)));
|
||||
float3 position = mul(float4(vs_in.m_Position, 1.), instance_transform).xyz;
|
||||
result.m_Position = mul(float4(position, 1.), model_view).xyz;
|
||||
result.m_Normal = mul(float4(mul(float4(vs_in.m_Normal, 0.), instance_transform)), model_view).xyz;
|
||||
result.m_TexCoord = vs_in.m_TexCoord;
|
||||
result.m_Tangent.xyz = mul(float4(mul(float4(vs_in.m_Tangent.xyz, 0.), instance_transform)), model_view).xyz;
|
||||
result.m_Tangent.w = vs_in.m_Tangent.w;
|
||||
result.m_PositionSV = mul(g_FaceProjection, float4(result.m_Position, 1.));
|
||||
result.m_PositionCS = result.m_PositionSV;
|
||||
result.m_HistoryPositionCS = result.m_PositionSV;
|
||||
return result;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user