diff --git a/Applications/Plasma/Camera.cpp b/Applications/Plasma/Camera.cpp index 855a53b..fd2e30a 100644 --- a/Applications/Plasma/Camera.cpp +++ b/Applications/Plasma/Camera.cpp @@ -20,12 +20,19 @@ static constexpr vec3 kForward = vec3(0.0f, 0.0f, -1.0f); static constexpr vec3 kRight = vec3(1.0f, 0.0f, 0.0f); static constexpr vec3 kUp = vec3(0.0f, 1.0f, 0.0f); +#define DUMP_CAMERA_POSITION 0 + const mat4& Camera::viewMatrix() { // Create a view matrix if it needs to be updated. if (_isViewDirty) { - _viewMatrix = lookAt(_eye, _target, _up); + _viewMatrix = lookAt(_eye, _target, _up); + // If dev flag is set dump camera position. +#if DUMP_CAMERA_POSITION + AU_INFO("--eye=%0.2f,%0.2f,%0.2f --target=%0.2f,%0.2f,%0.2f", _eye.x, _eye.y, _eye.z, + _target.x, _target.y, _target.z); +#endif _isViewDirty = false; } diff --git a/Applications/Plasma/Loaders.h b/Applications/Plasma/Loaders.h index 2231f5b..4e9bc21 100644 --- a/Applications/Plasma/Loaders.h +++ b/Applications/Plasma/Loaders.h @@ -47,6 +47,9 @@ class ImageCache using LoadSceneFunc = function; +// If true OBJ loader will try and load a materialX file for each OBJ material. +void loadMaterialXForOBJMaterials(bool enabled); + // Loads a Wavefront OBJ (.obj) file into the specified renderer and scene, from the specified file // path. bool loadOBJFile(Aurora::IRenderer* pRenderer, Aurora::IScene* pScene, const string& filePath, diff --git a/Applications/Plasma/OBJLoader.cpp b/Applications/Plasma/OBJLoader.cpp index 28261b7..c0499c6 100644 --- a/Applications/Plasma/OBJLoader.cpp +++ b/Applications/Plasma/OBJLoader.cpp @@ -17,6 +17,8 @@ #include "Loaders.h" #include "SceneContents.h" +bool gEnableMaterialXMaterials = false; + uint32_t ImageCache::whitePixels[1] = { 0xFFFFFFFF }; #define PLASMA_HAS_TANGENTS 0 @@ -62,6 +64,11 @@ struct hashOBJIndex // An unordered (hash) map from an OBJIndex (with vertex channel indices) to a single index. using OBJIndexMap = unordered_map; +void loadMaterialXForOBJMaterials(bool enabled) +{ + gEnableMaterialXMaterials = enabled; +} + // Loads a Wavefront OBJ file into the specified renderer and scene, from the specified file path. bool loadOBJFile(Aurora::IRenderer* /*pRenderer*/, Aurora::IScene* pScene, const string& filePath, SceneContents& sceneContents) @@ -103,9 +110,40 @@ bool loadOBJFile(Aurora::IRenderer* /*pRenderer*/, Aurora::IScene* pScene, const int mtlCount = 0; for (auto& objMaterial : objMaterials) { + // Flag is set try and load a materialX file with the OBJ material name. + if (gEnableMaterialXMaterials && !objMaterial.name.empty()) + { + // Build MaterialX file path from OBJ material name and OBJ path. + std::string directory = std::filesystem::path(filePath).parent_path().u8string(); + string mtlxFilename = directory + "/" + objMaterial.name + ".mtlx"; + ifstream is(mtlxFilename, ifstream::binary); + if (is.good()) + { + // If the file exists load it into a string. + string mtlString; + is.seekg(0, is.end); + size_t length = is.tellg(); + is.seekg(0, is.beg); + mtlString.resize(length + 1); + is.read((char*)&mtlString[0], length); + + // Create a material from the MaterialX string, and use it in place of the OBJ + // material. + Aurora::Path materialPath = + filePath + "-" + objMaterial.name + ":MaterialX-" + to_string(mtlCount++); + pScene->setMaterialType(materialPath, Names::MaterialTypes::kMaterialX, mtlString); + lstMaterials.push_back(materialPath); + AU_INFO("Found materialX document %s for OBJ material %s", mtlxFilename.c_str(), + objMaterial.name.c_str()); + continue; + } + } + // Collect material properties. // NOTE: This includes support for some of the properties in the PBR extension here: // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr + // See TinyOBJ parsing code for mapping from OBJ mtl values to ObjMaterial properties: + // https://github.com/tinyobjloader/tinyobjloader/blob/ee45fb41db95bf9563f2a41bc63adfa18475c2ee/tiny_obj_loader.h#L2127 // > We use the Standard Surface IOR default of 1.5 when no IOR is specified in the file, // which is parsed as 1.0. // > Transmission is derived from the "dissolve" property. This is normally intended for @@ -178,7 +216,8 @@ bool loadOBJFile(Aurora::IRenderer* /*pRenderer*/, Aurora::IScene* pScene, const { "normal_image", normalImage } }; // clang-format on - Aurora::Path materialPath = filePath + ":OBJFileMaterial-" + to_string(mtlCount++); + Aurora::Path materialPath = + filePath + "-" + objMaterial.name + ":OBJFileMaterial-" + to_string(mtlCount++); pScene->setMaterialProperties(materialPath, properties); lstMaterials.push_back(materialPath); }; diff --git a/Applications/Plasma/Plasma.cpp b/Applications/Plasma/Plasma.cpp index d72c26e..236e3aa 100644 --- a/Applications/Plasma/Plasma.cpp +++ b/Applications/Plasma/Plasma.cpp @@ -151,18 +151,11 @@ bool Plasma::run(int argc, char* argv[]) #endif // Creates a sample scene. -Aurora::IScenePtr Plasma::createSampleScene(Aurora::IRenderer* pRenderer, SceneContents& contents) +void Plasma::createSampleScene(Aurora::IScene* pScene, SceneContents& contents) { // Clear the result. contents.reset(); - // Create an Aurora scene. - Aurora::IScenePtr pScene = pRenderer->createScene(); - if (!pScene) - { - return nullptr; - } - // Create sample geometry, a single triangle. const Aurora::Path kGeomPath = "PlasmaDefaultSceneGeometry"; @@ -269,7 +262,7 @@ Aurora::IScenePtr Plasma::createSampleScene(Aurora::IRenderer* pRenderer, SceneC vec3 max(1.0f, 1.0f, 0.01f); contents.bounds = Foundation::BoundingBox(min, max); - return pScene; + return; } bool Plasma::getFloat3Option(const string& name, glm::vec3& value) const @@ -360,6 +353,7 @@ void Plasma::parseOptions(int argc, char* argv[]) ("fov", "Camera field of view in degrees.", cxxopts::value()) ("env", "Environment map path to load (lat-long format .HDR file)", cxxopts::value()) ("mtlx", "MaterialX document path to apply.",cxxopts::value()) + ("loadMtlXForObj", "Try and load MaterialX documents for each OBJ material.", cxxopts::value()) ("h,help", "Print help"); // clang-format on @@ -437,12 +431,11 @@ bool Plasma::initialize() return false; } - // Setup asset search paths. Including the path to the ProteinX test folder (if running within - // the Github repo). - // TODO: A more reliable solution would be to set based on the loaded materialX file path. - _assetPaths = { "", - Foundation::getModulePath() + - "../../../Renderers/Tests/Data/Materials/mtlx_ProteinSubset/" }; + // Setup asset search paths. Including the path to the unit test assets folder (if running + // within the Github repo). + // TODO: A more reliable solution would be to set based on the loaded MaterialX file path. + _assetPaths = { "", Foundation::getModulePath() + "../../../Tests/Assets/Materials/", + Foundation::getModulePath() + "../../../Tests/Assets/Textures/" }; // Setup the resource loading function to use asset search paths. auto loadResourceFunc = [this](const string& uri, vector* pBufferOut, @@ -479,6 +472,12 @@ bool Plasma::initialize() }; _pRenderer->setLoadResourceFunction(loadResourceFunc); + // The load materialX flag for OBJ loader if argument set. + if (_pArguments->count("loadMtlXForObj") > 0) + { + loadMaterialXForOBJMaterials(true); + } + // Set reference flag. if (_pArguments->count("reference") > 0) { @@ -536,8 +535,14 @@ bool Plasma::initialize() // If a file was not loaded, create a sample scene. if (!bFileLoaded) { + // Create new empty scene + _pScene = _pRenderer->createScene(); + + // Set the scene on the renderer. + _pRenderer->setScene(_pScene); + // Create the sample scene, and return if it not successful. - _pScene = createSampleScene(_pRenderer.get(), _sceneContents); + createSampleScene(_pScene.get(), _sceneContents); if (!_pScene) { return false; @@ -605,7 +610,7 @@ bool Plasma::initialize() bool Plasma::addDecal(const string& decalMtlXPath) { - // Load the materialX file for decal. + // Load the MaterialX file for decal. auto materialPath = loadMaterialXFile(decalMtlXPath); if (materialPath.empty()) return false; @@ -705,10 +710,6 @@ void Plasma::updateNewScene() _animationTimer.reset(!_isAnimating); _frameNumber = 0; - // Set the scene on the renderer, and assign the environment and ground plane. Set the ground - // plane position to the bottom corner, of the scene bounds. - _pRenderer->setScene(_pScene); - // Setup environment for new scene. _pScene->setEnvironmentProperties( _environmentPath, { { Aurora::Names::EnvironmentProperties::kBackgroundUseScreen, true } }); @@ -1003,12 +1004,30 @@ void Plasma::selectFile( // works as desired here because the load blocks the application. ::SetCursor(::LoadCursor(nullptr, IDC_WAIT)); - // Load the file. + addAssetPathContainingFile(Foundation::w2s(filePath.data())); loadFunc(Foundation::w2s(filePath.data())); } #endif +void Plasma::addAssetPathContainingFile(const string& filePath) +{ + string directory = filesystem::path(filePath).parent_path().u8string(); + if (directory.empty()) + return; + addAssetPath(directory + "/"); +} + +void Plasma::addAssetPath(const string& filePath) +{ + for (int i = 0; i < _assetPaths.size(); i++) + { + if (_assetPaths[i].compare(filePath) == 0) + return; + } + _assetPaths.push_back(filePath); +} + // Loads an environment image file with the specified path. bool Plasma::loadEnvironmentImageFile(const string& filePath) { @@ -1052,10 +1071,16 @@ bool Plasma::loadSceneFile(const string& filePath) auto directory = filesystem::path(filePath).parent_path(); filesystem::current_path(directory); + // Create new empty scene + _pScene = _pRenderer->createScene(); + + // Set the scene on the renderer. + _pRenderer->setScene(_pScene); + // Create a new scene and load the scene file into it. If that fails, return immediately. _sceneContents.reset(); - Aurora::IScenePtr pScene = _pRenderer->createScene(); - if (!loadSceneFunc(_pRenderer.get(), pScene.get(), filePath, _sceneContents)) + addAssetPathContainingFile(filePath); + if (!loadSceneFunc(_pRenderer.get(), _pScene.get(), filePath, _sceneContents)) { ::errorMessage("Unable to load the specified scene file: \"" + filePath + "\""); @@ -1064,7 +1089,6 @@ bool Plasma::loadSceneFile(const string& filePath) _instanceLayers = vector(_sceneContents.instances.size()); // Record the new scene and update application state for the new scene. - _pScene = pScene; updateNewScene(); // Report the load time. @@ -1139,7 +1163,7 @@ Aurora::Path Plasma::loadMaterialXFile(const string& filePath) processedMtlXString = regex_replace(processedMtlXString, regex(R"(\.\.\/\.\.\/\.\.)"), mtlxResourcesPath); - // Create a new material from the materialX document. + // Create a new material from the MaterialX document. _pScene->setMaterialType( materialPath, Aurora::Names::MaterialTypes::kMaterialX, processedMtlXString); @@ -1147,7 +1171,7 @@ Aurora::Path Plasma::loadMaterialXFile(const string& filePath) } bool Plasma::applyMaterialXFile(const string& filePath) { - // Create a new material from the materialX document. + // Create a new material from the MaterialX document. Aurora::Path materialPath = loadMaterialXFile(filePath); if (materialPath.empty()) { @@ -1327,6 +1351,7 @@ void Plasma::onFilesDropped(HDROP hDrop) } else { + addAssetPathContainingFile(filePath); (*funcIt).second(filePath); } } @@ -1482,8 +1507,8 @@ void Plasma::onKeyPressed(WPARAM keyCode) } else { - _isOpaqueShadowsEnabled = !_isOpaqueShadowsEnabled; - options.setBoolean("isOpaqueShadowsEnabled", _isOpaqueShadowsEnabled); + _isForceOpaqueShadowsEnabled = !_isForceOpaqueShadowsEnabled; + options.setBoolean("isForceOpaqueShadowsEnabled", _isForceOpaqueShadowsEnabled); requestUpdate(); } break; @@ -1515,7 +1540,7 @@ void Plasma::onKeyPressed(WPARAM keyCode) adjustUnit(+1); break; - // W: Add decal from materialX file. + // W: Add decal from MaterialX file. case 0x57: if (::GetAsyncKeyState(VK_CONTROL) != 0) { diff --git a/Applications/Plasma/Plasma.h b/Applications/Plasma/Plasma.h index 77aa014..538441b 100644 --- a/Applications/Plasma/Plasma.h +++ b/Applications/Plasma/Plasma.h @@ -59,8 +59,7 @@ class Plasma #if defined(INTERACTIVE_PLASMA) static LRESULT __stdcall wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); #endif - static Aurora::IScenePtr createSampleScene( - Aurora::IRenderer* pRenderer, SceneContents& contentsOut); + static void createSampleScene(Aurora::IScene* pRenderer, SceneContents& contentsOut); /*** Private Functions ***/ @@ -96,6 +95,8 @@ class Plasma void saveImage(const wstring& filePath, const uvec2& dimensions); bool applyMaterialXFile(const string& mtlxPath); Aurora::Path loadMaterialXFile(const string& filePath); + void addAssetPath(const string& path); + void addAssetPathContainingFile(const string& filePath); void resetMaterials(); bool addDecal(const string& decalMtlXPath); @@ -139,9 +140,9 @@ class Plasma vec3 _lightDirection = normalize(vec3(1.0f, -0.5f, 0.0f)); bool _isDenoisingEnabled = false; #if defined(INTERACTIVE_PLASMA) - bool _isDiffuseOnlyEnabled = false; - bool _isOpaqueShadowsEnabled = false; - bool _isToneMappingEnabled = false; + bool _isDiffuseOnlyEnabled = false; + bool _isForceOpaqueShadowsEnabled = false; + bool _isToneMappingEnabled = false; #endif bool _isGroundPlaneShadowEnabled = false; bool _isGroundPlaneReflectionEnabled = false; diff --git a/CMakeLists.txt b/CMakeLists.txt index bfa3d99..1bdb7a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,17 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(FATAL_ERROR "In-source build is not allowed!") endif() +# See if USD was built with python. +find_library(USD_usdviewq_LIBRARY_RELEASE usd_usdviewq) +find_library(USD_usdviewq_LIBRARY_DEBUG usd_usdviewqd) +if(USD_usdviewq_LIBRARY_RELEASE OR USD_usdviewq_LIBRARY_DEBUG) + # iF USDView is found, then we know USD was built with python. + set(USD_BUILT_WITH_PYTHON "True") + message(STATUS "USD built with Python, system Python libraries required.") +endif() + # Set the project name and project variables -project(Aurora VERSION 23.07.0.0) +project(Aurora VERSION 24.03.0.0) # Create a folder for the version header files set(VERSION_FOLDER "${PROJECT_BINARY_DIR}/VersionFiles") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eab08a5..a2420c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,10 +3,10 @@ ## Contributor License Agreement # Before contributing code to this project, we ask that you sign the appropriate Contributor License Agreement (CLA): -+ [ADSKFormCorpContribAgmtForOpenSource.docx](Doc/CLA/ADSKFormCorpContribAgmtforOpenSource.docx?raw=1): please sign this one for corporate use. -+ [ADSKFormIndContribAgmtForOpenSource.docx](Doc/CLA/ADSKFormIndContribAgmtforOpenSource.docx?raw=1): please sign this one if you're an individual contributor ++ [ADSKFormCorpContribAgmtForOpenSource.docx](Doc/CLA/ADSKFormCorpContribAgmtforOpenSource.docx): please sign this one for corporate use. ++ [ADSKFormIndContribAgmtForOpenSource.docx](Doc/CLA/ADSKFormIndContribAgmtforOpenSource.docx): please sign this one if you're an individual contributor -The documents include instructions on where to send the completed forms. Once a signed form has been received you will be able to submit pull requests. +Return the completed form to aurora@autodesk.com. Once a signed form has been received you will be able to submit pull requests. Contributors are expected to follow the [Contributor Covenant](CODE_OF_CONDUCT.md), which describes the code of conduct for Autodesk open source projects like this one. @@ -34,6 +34,6 @@ The Aurora project accepts and greatly appreciates contributions. The project fo When contributing code, please also include appropriate tests as part of the pull request, and follow the published [coding standards](Doc/CodingStandards.md). In particular, use the same style as the code you are modifying. -All development should happen against the "main" (default) branch of the repository. Please make sure the base branch of your pull request is set to the "main" branch when filing your pull request. +Contributor pull requests should target the latest `contrib` branch of the repository, this will have a suffix of the latest released version, so contributions based on version v23.07 should target the `contrib/v23.07`. Please make sure the base branch of your pull request is set to the correct contrib branch when filing your pull request. Contributor pull requests that are accepted will be merged into the contrib branch and then included in the next Aurora release from there. They will be merged into the main branch along with any other changes for the next release. It is highly recommended that an issue be logged on GitHub before any work is started. This will allow for early feedback from other developers and will help avoid duplicate effort. diff --git a/Doc/Build.md b/Doc/Build.md index 03d6fca..78aaf0f 100644 --- a/Doc/Build.md +++ b/Doc/Build.md @@ -40,7 +40,7 @@ Aurora includes a script that retrieves and builds dependencies ("externals") fr 2. **Start a command line** with access to your C++ compiler tools. When using Visual Studio, the "x64 Native Tools Command Prompt for VS 2019" (or 2022) shortcut will provide the proper environment. The CMake and Python executables must also be available, through the PATH environment variable. -3. **Installing externals:** Run *[Scripts/installExternals.py](Scripts/installExternals.py)* with Python in *AURORA_DIR* to build and install externals. +3. **Installing externals:** Run *[Scripts/installExternals.py](/Scripts/installExternals.py)* with Python in *AURORA_DIR* to build and install externals. - You can run the install script with the desired location for storing and compiling externals as the only argument. We will refer to this location as *EXTERNALS_ROOT*. ``` @@ -59,7 +59,7 @@ Aurora includes a script that retrieves and builds dependencies ("externals") fr - You may specify the externals installation directory (*EXTERNALS_ROOT*, above) as a CMake path variable called `EXTERNALS_ROOT`. This must be specified as an absolute path, e.g. with a drive letter on Windows. If no `EXTERNALS_ROOT` is specified, the `EXTERNALS_ROOT` built by the latest run of *installExternals.py* will be used automatically. If you are using cmake-gui, you should specify this variable before generating. - - You must specify a build directory, and we refer to this location as *AURORA_BUILD_DIR*. The recommended build directory is *{AURORA_DIR}/Build*, which is filtered by [.gitignore](.gitignore) so it won't appear as local changes for Git. + - You must specify a build directory, and we refer to this location as *AURORA_BUILD_DIR*. The recommended build directory is *{AURORA_DIR}/Build*, which is filtered by [.gitignore](/.gitignore) so it won't appear as local changes for Git. - You can use CMake on the command line or the GUI (cmake-gui). The CMake command to generate projects is as follows: diff --git a/Jenkinsfile b/Jenkinsfile index b6dc438..d1539a7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,6 +1,45 @@ #!/usr/bin/env groovy @Library('PSL@master') _ +def isPrimaryBranch(){ + return env.BRANCH_NAME == "dev" || env.BRANCH_NAME == "main" +} + +def isReleaseBranch(){ + def match = env.BRANCH_NAME =~/(?i)^release-\/.*/ + def result = match ? true : false + match = null + return result +} + +def isInventorBranch(){ + def match = env.BRANCH_NAME =~/(?i)^inventor_fy.*/ + def result = match ? true : false + match = null + return result +} + +def isPullRequest(){ + def match = env.BRANCH_NAME =~/(?i)^pr-.*/ + def result = match ? true : false + match = null + return result +} + +// Build upon primary branches or those with PR opened, +// and skip for the rest to save build resources +if (!(isPrimaryBranch() || isReleaseBranch() || isInventorBranch() || isPullRequest())) { + echo "Skip CI build other than 'dev', 'main', 'release' and branches with open PRs" + return +} + +def jenkinsProperties = [] +// Abort previous running build after any new commit pushed to PRs +if (isPullRequest()) { + jenkinsProperties.add(disableConcurrentBuilds(abortPrevious: true)) +} +properties(jenkinsProperties) + /////////////////////////////////////////////////// // Global constants COMMONSHELL = new ors.utils.CommonShell(steps, env) @@ -70,7 +109,10 @@ def windowsBuild(Config) { call set VULKAN_SDK=C:\\VulkanSDK\\1.3.231.1 :: Set up Visual Studio 2019 Environment - call C:\\"Program Files (x86)"\\"Microsoft Visual Studio"\\2019\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat + for /f "usebackq delims=" %%i in (`"%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere" -property installationPath`) do ( + if exist "%%i\\MSBuild\\Microsoft\\VisualStudio\\v16.0" set "VSInstallPath=%%i" + ) + call "%VSInstallPath%\\VC\\Auxiliary\\Build\\vcvars64.bat" :: install externals python -u Scripts\\installExternals.py ${EXTERNALS_DIR_AURORA} --build-variant=${Config} -v -v diff --git a/Libraries/Aurora/API/Aurora/Aurora.h b/Libraries/Aurora/API/Aurora/Aurora.h index 2cc5d85..fced74c 100644 --- a/Libraries/Aurora/API/Aurora/Aurora.h +++ b/Libraries/Aurora/API/Aurora/Aurora.h @@ -281,6 +281,56 @@ struct PropertyValue } } + /// Convert value to string. + string toString() const + { + // Compare based on type value. + switch (type) + { + case Type::Bool: + return to_string(_bool); + case Type::Int: + return to_string(_int); + case Type::Float: + return to_string(_float); + case Type::Float2: + return to_string(_float2.x) + ", " + to_string(_float2.y); + case Type::Float3: + return to_string(_float3.x) + ", " + to_string(_float3.y) + ", " + to_string(_float3.z); + case Type::Float4: + return to_string(_float4.x) + ", " + to_string(_float4.y) + ", " + + to_string(_float4.z) + ", " + to_string(_float4.w); + case Type::Matrix4: + return to_string(_matrix4[0][0]) + ", " + to_string(_matrix4[0][1]) + ", " + + to_string(_matrix4[0][2]) + ", " + to_string(_matrix4[0][3]) + ", " + + to_string(_matrix4[1][0]) + ", " + to_string(_matrix4[1][1]) + ", " + + to_string(_matrix4[1][2]) + ", " + to_string(_matrix4[1][3]) + ", " + + to_string(_matrix4[2][0]) + ", " + to_string(_matrix4[2][1]) + ", " + + to_string(_matrix4[2][2]) + ", " + to_string(_matrix4[2][3]) + ", " + + to_string(_matrix4[3][0]) + ", " + to_string(_matrix4[3][1]) + ", " + + to_string(_matrix4[3][2]) + ", " + to_string(_matrix4[3][3]); + + case Type::String: + return _string; + case Type::Strings: + { + string res; + // If any string does not match equality is false. + for (size_t i = 0; i < _strings.size(); i++) + { + res += _strings[i]; + if (i < _strings.size()) + res += ", "; + } + + // Return true if all strings match. + return res; + } + default: + return ""; + } + } + union { bool _bool; @@ -467,60 +517,64 @@ struct GeometryDescriptor AttributeUpdateCompleteFunction attributeUpdateComplete = nullptr; }; -/// \struct Image pixel data desription, filed in by getPixelData callback when IImage is activated. -struct PixelData +/// \struct Image data description, filed in by getData callback when IImage is activated. +struct ImageData { - /// Buffer address. - const void* address = nullptr; + /// Pixel buffer address. + const void* pPixelBuffer = nullptr; /// Size of one row of pixels. size_t bytesPerRow = 0; - /// The total size in bytes of the buffer. - size_t size = 0; + /// The total size in bytes of the pixel buffer. + size_t bufferSize = 0; + + /// Size of the image. + glm::ivec2 dimensions = glm::ivec2(0, 0); + + /// The format of the image. + ImageFormat format = ImageFormat::Integer_RGBA; + + /// Should we override the linearize flag the image was created with. + bool overrideLinearize = false; + + /// Whether the image should be linearized from sRGB to linear color space. + /// Ignored unless overrideLinearize is set. + bool linearize = true; }; -/// Callback function to get the pixel data for an image object from the +/// Callback function, passed to the getData callback functions to allocate buffers to use in the +/// returned data (e.g. pixel buffers). +/// +/// \param size The size of buffer to be allocated. +using AllocateBufferFunction = std::function; + +/// Callback function to get the data, including size and pixel buffer, for an image object from the /// client. /// -/// \param dataOut The pixel data, including pointer to actual pixels, to be filled in by client. -/// \param bottomLeft The bottom left of the image region to be filled in. Currently always the -/// bottom left corner of entire image. \param topRight The top right of the image region to be -/// filled in. Currently always the top right corner of entire image. \return false if client was -/// not able to create image data. -using GetPixelDataFunction = - std::function; +/// \param dataOut The image data, including pointer to actual pixels, to be filled in by client. +using GetImageDataFunction = std::function; /// Callback function to signal the vertex and index attribute data for the geometry object has been /// updated, and the pointers provided by the GetAttributeDataFunction can be freed by the client. /// Provides the same arguments that were provided by getAttributeData. -using PixelUpdateCompleteFunction = - std::function; +using ImageUpdateCompleteFunction = std::function; /// Input image description. struct ImageDescriptor { - /// The format of the image. - ImageFormat format = ImageFormat::Integer_RGBA; - /// Whether the image should be linearized from sRGB to linear color space. - bool linearize; + bool linearize = true; /// Whether the image is to be used to represent an environment. bool isEnvironment = false; - /// The width of image in pixels. - uint32_t width = 0; - - /// The height of image in pixels. - uint32_t height = 0; - /// Callback for getting the pixel data, called when geometry resource is activated. - GetPixelDataFunction getPixelData = nullptr; + GetImageDataFunction getData = nullptr; - /// Optional completion callback, called after image resource is activated and pixel data is no - /// longer in use by the renderer. - PixelUpdateCompleteFunction pixelUpdateComplete = nullptr; + /// Optional completion callback, called after image resource is activated and image pixel data + /// is no longer in use by the renderer. + ImageUpdateCompleteFunction updateComplete = nullptr; }; /// Instance definition, all the data required to create an instance resource. diff --git a/Libraries/Aurora/CMakeLists.txt b/Libraries/Aurora/CMakeLists.txt index 38eb6ca..b01ed12 100644 --- a/Libraries/Aurora/CMakeLists.txt +++ b/Libraries/Aurora/CMakeLists.txt @@ -109,24 +109,26 @@ file(MAKE_DIRECTORY "${COMPILED_SHADERS_DIR}") # The slang shaders that are shared by DX and HGI backends. set(COMMON_SHADERS - "Source/Shaders/BackgroundMissShader.slang" "Source/Shaders/BSDF.slang" "Source/Shaders/BSDFCommon.slang" "Source/Shaders/Colors.slang" + "Source/Shaders/DefaultMaterialUniformAccessors.slang" + "Source/Shaders/DefaultMaterialUniformBuffer.slang" + "Source/Shaders/DefaultMaterial.slang" "Source/Shaders/Environment.slang" "Source/Shaders/Frame.slang" "Source/Shaders/Geometry.slang" "Source/Shaders/Globals.slang" "Source/Shaders/GLSLToHLSL.slang" "Source/Shaders/GroundPlane.slang" - "Source/Shaders/InitializeDefaultMaterialType.slang" + "Source/Shaders/InstancePipelineState.slang" + "Source/Shaders/GlobalPipelineState.slang" "Source/Shaders/MainEntryPoints.slang" "Source/Shaders/Material.slang" + "Source/Shaders/GlobalBufferAccessors.slang" "Source/Shaders/MaterialXCommon.slang" + "Source/Shaders/Options.slang" "Source/Shaders/PathTracingCommon.slang" - "Source/Shaders/ShadowMissShader.slang" - "Source/Shaders/RadianceMissShader.slang" - "Source/Shaders/RayGenShader.slang" "Source/Shaders/ShadeFunctions.slang" "Source/Shaders/Random.slang" "Source/Shaders/RayTrace.slang" @@ -145,9 +147,8 @@ endif() # Set MINIFIED_SHADERS_HEADER to filename of auto-generated header file containing minified Slang shader strings. set(MINIFIED_COMMON_SHADERS_HEADER "${COMPILED_SHADERS_DIR}/CommonShaders.h") -# Add commands to minifiy common shaders. -minify_shaders("${MINIFIED_COMMON_SHADERS_HEADER}" "${PROJECT_SOURCE_DIR}/Source/Shaders" "${COMMON_SHADERS}") - +# Add commands to minifiy common shaders, and compile the default main entry points. +minify_shaders("${MINIFIED_COMMON_SHADERS_HEADER}" "${PROJECT_SOURCE_DIR}/Source/Shaders" "${COMMON_SHADERS}" ${Slang_COMPILER}) if(ENABLE_DIRECTX_BACKEND) if(ENABLE_MATERIALX) @@ -273,6 +274,8 @@ source_group("DirectX/Shaders" FILES ${DIRECTX_PRECOMPILED_SHADERS}) source_group("HGI/Shaders" FILES ${HGI_SHADERS}) if(WIN32) + + if(ENABLE_DIRECTX_BACKEND) # Create custom targets that will copy DLLs of the DirectX shader compiler to the runtime folder. add_custom_target(CopyDXCompilerDLLs ALL diff --git a/Libraries/Aurora/Source/DirectX/MemoryPool.cpp b/Libraries/Aurora/Source/DirectX/MemoryPool.cpp index dc6c052..6657c24 100644 --- a/Libraries/Aurora/Source/DirectX/MemoryPool.cpp +++ b/Libraries/Aurora/Source/DirectX/MemoryPool.cpp @@ -22,10 +22,11 @@ BEGIN_AURORA uint8_t* TransferBuffer::map(size_t sz, size_t offset) { // Ensure buffer is not already mapped. - AU_ASSERT(mappedRange.Begin == SIZE_MAX, "Already mapped"); + AU_ASSERT(!isMapped(), "Already mapped"); // Set the mapped range from size and offset. - mappedRange = { offset, sz }; + mappedRange = { offset, sz }; + isUploadBufferMapped = true; // Map the DX buffer resource, call AU_FAIL if HRESULT indicates an error. uint8_t* pMappedData = nullptr; @@ -39,11 +40,13 @@ uint8_t* TransferBuffer::map(size_t sz, size_t offset) void TransferBuffer::unmap() { // Ensure buffer is mapped. - AU_ASSERT(mappedRange.Begin != SIZE_MAX, "Not mapped"); + AU_ASSERT(isMapped(), "Not mapped"); // Unmap the DX buffer resource for the upload buffer. pUploadBuffer->Unmap(0, mappedRange.End == 0 ? nullptr : &mappedRange); // no HRESULT + isUploadBufferMapped = false; + // Pass this buffer to renderer to add to the pending upload list. pRenderer->transferBufferUpdated(*this); diff --git a/Libraries/Aurora/Source/DirectX/MemoryPool.h b/Libraries/Aurora/Source/DirectX/MemoryPool.h index 4c3306f..fba6921 100644 --- a/Libraries/Aurora/Source/DirectX/MemoryPool.h +++ b/Libraries/Aurora/Source/DirectX/MemoryPool.h @@ -129,6 +129,8 @@ struct TransferBuffer D3D12_RESOURCE_STATES finalState = D3D12_RESOURCE_STATE_GENERIC_READ; // Has this buffer been uploaded ? bool wasUploaded = false; + // Is this buffer mapped? + bool isUploadBufferMapped = false; // Is the buffer valid? bool valid() { return pGPUBuffer != nullptr; } @@ -141,6 +143,7 @@ struct TransferBuffer size = 0; pRenderer = nullptr; } + bool isMapped() const { return isUploadBufferMapped; } // Map the upload buffer in CPU memory. uint8_t* map(size_t size = 0, size_t offset = 0); // Unmap the upload buffer in CPU memory. This will trigger an upload to the GPU buffer, in the diff --git a/Libraries/Aurora/Source/DirectX/PTDevice.cpp b/Libraries/Aurora/Source/DirectX/PTDevice.cpp index d7f55d4..413905e 100644 --- a/Libraries/Aurora/Source/DirectX/PTDevice.cpp +++ b/Libraries/Aurora/Source/DirectX/PTDevice.cpp @@ -92,6 +92,8 @@ bool PTDevice::initialize(PTDevice::Features features, int sampleCount) // NOTE: The debug layer requires installation of Graphics Tools for Windows 10, which may not // be desired by some clients. #if AU_DEVICE_DEBUG_ENABLED + AU_INFO("AU_DEVICE_DEBUG_ENABLED macro set to 1. Creating debug device."); + // Enable the D3D12 debug layer. // D3D12GetDebugInterface return a null pointer if the Graphics Tools are not installed. ComPtr pDebugInterface; diff --git a/Libraries/Aurora/Source/DirectX/PTMaterial.cpp b/Libraries/Aurora/Source/DirectX/PTMaterial.cpp index e1f9807..93454f2 100644 --- a/Libraries/Aurora/Source/DirectX/PTMaterial.cpp +++ b/Libraries/Aurora/Source/DirectX/PTMaterial.cpp @@ -21,137 +21,9 @@ BEGIN_AURORA -// Convenience macro to compare reflection data from shader to CPU offset -// NOTE: Can only be used inside PTMaterial::validateOffsets(). -#define VALIDATE_OFFSET(_member) \ - if (structDescriptions[#_member].Offset != uniformBuffer.getOffsetForVariable(#_member)) \ - { \ - AU_ERROR("%s offset mismatch: %d!=%d\n", #_member, structDescriptions[#_member].Offset, \ - uniformBuffer.getOffsetForVariable(#_member)); \ - isValid = false; \ - } - -bool PTMaterial::validateOffsets(const PTShaderLibrary& pShaderLibrary) -{ - ComPtr pShaderLibraryReflection = pShaderLibrary.reflection(); - - // Get the first function in library. - auto pFuncRefl = pShaderLibraryReflection->GetFunctionByIndex(0); - - // Get the reflection data for constant buffer - auto pCBRefl = pFuncRefl->GetConstantBufferByName("gMaterialConstants"); - - // If we fail to get reflection data just abandon validation (should not be failure case.) - if (!pCBRefl) - return true; - - // Get the description of the constant buffer (which includes size). - D3D12_SHADER_BUFFER_DESC cbDesc; - pCBRefl->GetDesc(&cbDesc); - - // Get the first variable in the constant buffer. - // NOTE:this is actually the struct representing the members. - ID3D12ShaderReflectionVariable* pStructRefl = pCBRefl->GetVariableByIndex(0); - - // If we fail to get reflection data just abandon validation (should not be failure case.) - if (!pStructRefl) - return true; - - // Get the type for the struct, and its description. - ID3D12ShaderReflectionType* pStructType = pStructRefl->GetType(); - - // If we fail to get reflection struct data just abandon validation (should not be failure - // case.) - if (!pStructType) - return true; - - // Get description of struct. - // If the GetDesc call fails, which will happen if the struct is not referenced, just abandon - // validation (should not be failure case.) - D3D12_SHADER_TYPE_DESC structDesc; - if (pStructType->GetDesc(&structDesc) != S_OK) - return true; - - // Create a map of member name to description. - map structDescriptions; - - // Iterate through the members in the struct. - for (unsigned int i = 0; i < structDesc.Members; i++) - { - // Get the name and type of member. - const char* pVarName = pStructType->GetMemberTypeName(i); - ID3D12ShaderReflectionType* pVarType = pStructType->GetMemberTypeByIndex(i); - - // Get the description from the type - D3D12_SHADER_TYPE_DESC varDesc; - pVarType->GetDesc(&varDesc); - - // Add it to the map. - structDescriptions[pVarName] = varDesc; - } - - UniformBuffer uniformBuffer(StandardSurfaceUniforms, StandardSurfaceDefaults.properties); - // AU_INFO("%s", uniformBuffer.generateHLSLStruct().c_str()); - // Validate each offset; isValid flag will get set to false if invalid. - bool isValid = true; - VALIDATE_OFFSET(base); - VALIDATE_OFFSET(baseColor); - VALIDATE_OFFSET(diffuseRoughness); - VALIDATE_OFFSET(metalness); - VALIDATE_OFFSET(specular); - VALIDATE_OFFSET(specularColor); - VALIDATE_OFFSET(specularRoughness); - VALIDATE_OFFSET(specularIOR); - VALIDATE_OFFSET(specularAnisotropy); - VALIDATE_OFFSET(specularRotation); - VALIDATE_OFFSET(transmission); - VALIDATE_OFFSET(transmissionColor); - VALIDATE_OFFSET(subsurface); - VALIDATE_OFFSET(subsurfaceColor); - VALIDATE_OFFSET(subsurfaceRadius); - VALIDATE_OFFSET(subsurfaceScale); - VALIDATE_OFFSET(subsurfaceAnisotropy); - VALIDATE_OFFSET(sheen); - VALIDATE_OFFSET(sheenColor); - VALIDATE_OFFSET(sheenRoughness); - VALIDATE_OFFSET(coat); - VALIDATE_OFFSET(coatColor); - VALIDATE_OFFSET(coatRoughness); - VALIDATE_OFFSET(coatAnisotropy); - VALIDATE_OFFSET(coatRotation); - VALIDATE_OFFSET(coatIOR); - VALIDATE_OFFSET(coatAffectColor); - VALIDATE_OFFSET(coatAffectRoughness); - VALIDATE_OFFSET(opacity); - VALIDATE_OFFSET(thinWalled); - VALIDATE_OFFSET(hasBaseColorTex); - VALIDATE_OFFSET(baseColorTexRotation); - VALIDATE_OFFSET(hasSpecularRoughnessTex); - VALIDATE_OFFSET(specularRoughnessTexOffset); - VALIDATE_OFFSET(hasEmissionColorTex); - VALIDATE_OFFSET(emissionColorTexOffset); - VALIDATE_OFFSET(emissionColorTexScale); - VALIDATE_OFFSET(emissionColorTexPivot); - VALIDATE_OFFSET(emissionColorTexRotation); - VALIDATE_OFFSET(hasOpacityTex); - VALIDATE_OFFSET(opacityTexPivot); - VALIDATE_OFFSET(hasNormalTex); - VALIDATE_OFFSET(normalTexScale); - VALIDATE_OFFSET(normalTexRotation); - - // Validate structure size - if (cbDesc.Size != uniformBuffer.size()) - { - AU_ERROR("%s struct sizes differ: %d!=%d", cbDesc.Name, cbDesc.Size, uniformBuffer.size()); - isValid = false; - } - - return isValid; -} - -PTMaterial::PTMaterial( - PTRenderer* pRenderer, MaterialShaderPtr pShader, shared_ptr pDef) : - MaterialBase(pShader, pDef), _pRenderer(pRenderer) +PTMaterial::PTMaterial(PTRenderer* pRenderer, const string& name, MaterialShaderPtr pShader, + shared_ptr pDef) : + MaterialBase(name, pShader, pDef), _pRenderer(pRenderer) { } @@ -183,72 +55,4 @@ bool PTMaterial::update() return true; } -size_t PTMaterial::computeSamplerHash() const -{ - PTSamplerPtr pSampler; - - // Compute hash for base color image sampler. - pSampler = dynamic_pointer_cast(_values.asSampler("base_color_image_sampler")); - size_t res = pSampler ? pSampler->hash() : _pRenderer->defaultSampler()->hash(); - - // Combine with hash for opacity sampler. - pSampler = dynamic_pointer_cast(_values.asSampler("opacity_image_sampler")); - Foundation::hashCombine( - res, pSampler ? pSampler->hash() : _pRenderer->defaultSampler()->hash()); - - return res; -} - -void PTMaterial::createSamplerDescriptors( - CD3DX12_CPU_DESCRIPTOR_HANDLE& handle, UINT increment) const -{ - PTSamplerPtr pSampler; - - // Create a sampler descriptor on the descriptor heap for the base color sampler, if any. - pSampler = dynamic_pointer_cast(_values.asSampler("base_color_image_sampler")); - PTSampler::createDescriptor( - *_pRenderer, pSampler ? pSampler.get() : _pRenderer->defaultSampler().get(), handle); - - // Increment the heap location pointed to by the handle past the base color image sampler - // descriptor. - handle.Offset(increment); - - // Create a sampler descriptor on the descriptor heap for the opacity sampler, if any. - pSampler = dynamic_pointer_cast(_values.asSampler("opacity_image_sampler")); - PTSampler::createDescriptor( - *_pRenderer, pSampler ? pSampler.get() : _pRenderer->defaultSampler().get(), handle); - - // Increment the heap location pointed to by the handle past the opacity image sampler - // descriptor. - handle.Offset(increment); -} - -void PTMaterial::createDescriptors(CD3DX12_CPU_DESCRIPTOR_HANDLE& handle, UINT increment) const -{ - // Create a SRV (descriptor) on the descriptor heap for the base color image, if any. - PTImagePtr pImage = dynamic_pointer_cast(_values.asImage("base_color_image")); - PTImage::createSRV(*_pRenderer, pImage.get(), handle); - handle.Offset(increment); - - // Create a SRV (descriptor) on the descriptor heap for the specular roughness image, if any. - pImage = dynamic_pointer_cast(_values.asImage("specular_roughness_image")); - PTImage::createSRV(*_pRenderer, pImage.get(), handle); - handle.Offset(increment); - - // Create a SRV (descriptor) on the descriptor heap for the normal image, if any. - pImage = dynamic_pointer_cast(_values.asImage("normal_image")); - PTImage::createSRV(*_pRenderer, pImage.get(), handle); - handle.Offset(increment); - - // Create a SRV (descriptor) on the descriptor heap for the emission color image, if any. - pImage = dynamic_pointer_cast(_values.asImage("emission_color_image")); - PTImage::createSRV(*_pRenderer, pImage.get(), handle); - handle.Offset(increment); - - // Create a SRV (descriptor) on the descriptor heap for the opacity image, if any. - pImage = dynamic_pointer_cast(_values.asImage("opacity_image")); - PTImage::createSRV(*_pRenderer, pImage.get(), handle); - handle.Offset(increment); -} - END_AURORA diff --git a/Libraries/Aurora/Source/DirectX/PTMaterial.h b/Libraries/Aurora/Source/DirectX/PTMaterial.h index 06b4881..457b6a1 100644 --- a/Libraries/Aurora/Source/DirectX/PTMaterial.h +++ b/Libraries/Aurora/Source/DirectX/PTMaterial.h @@ -22,6 +22,7 @@ BEGIN_AURORA class PTRenderer; class MaterialShader; class PTShaderLibrary; +class PTImage; struct MaterialDefaultValues; struct TextureTransform @@ -38,41 +39,18 @@ class PTMaterial : public MaterialBase public: /*** Static Functions ***/ - /// Validates that the offsets with CPU material structure match the GPU version in shader. - /// - /// \return True if valid. - static bool validateOffsets(const PTShaderLibrary& pShaderLibrary); - /*** Lifetime Management ***/ - PTMaterial( - PTRenderer* pRenderer, MaterialShaderPtr pShader, shared_ptr pDef); + PTMaterial(PTRenderer* pRenderer, const string& name, MaterialShaderPtr pShader, + shared_ptr pDef); ~PTMaterial() {}; /*** Functions ***/ - // The total number of texture descriptors for each material instance. - static uint32_t descriptorCount() - { - // Base color, specular roughness, normal, emission color, and opacity textures. - return 5; - } - - // The total number of sampler descriptors for each material instance. - static uint32_t samplerDescriptorCount() - { - // Base color + opacity only for now. - // TODO: Support samplers for other textures. - return 2; - } - // Gets the constant buffer for this material. ID3D12Resource* buffer() const { return _constantBuffer.pGPUBuffer.Get(); } bool update(); - void createDescriptors(CD3DX12_CPU_DESCRIPTOR_HANDLE& handle, UINT increment) const; - void createSamplerDescriptors(CD3DX12_CPU_DESCRIPTOR_HANDLE& handle, UINT increment) const; - size_t computeSamplerHash() const; private: /*** Private Types ***/ diff --git a/Libraries/Aurora/Source/DirectX/PTRenderer.cpp b/Libraries/Aurora/Source/DirectX/PTRenderer.cpp index 53e7131..7d8814c 100644 --- a/Libraries/Aurora/Source/DirectX/PTRenderer.cpp +++ b/Libraries/Aurora/Source/DirectX/PTRenderer.cpp @@ -40,7 +40,6 @@ // Development flag to dump materialX documents to disk. // NOTE: This should never be enabled in committed code; it is only for local development. -#define AU_DEV_DUMP_MATERIALX_DOCUMENTS 0 #define AU_DEV_DUMP_PROCESSED_MATERIALX_DOCUMENTS 0 // Development flag to turn of exception catching during the render loop. @@ -74,27 +73,6 @@ PTRenderer::PTRenderer(uint32_t taskCount) : RendererBase(taskCount) // allocator for each simultaneously active task. initCommandList(); - // Initialize the shader library. - // TODO: Should be per-scene not per-renderer. - _pShaderLibrary = make_unique(_pDXDevice); - - // Enable layer shaders. - _pShaderLibrary->setOption("ENABLE_LAYERS", true); - -#if ENABLE_MATERIALX - // Get the materialX folder relative to the module path. - string mtlxFolder = Foundation::getModulePath() + "MaterialX"; - // Initialize the MaterialX code generator. - _pMaterialXGenerator = make_unique(mtlxFolder); - - // Default to MaterialX distance unit to centimeters. - _pShaderLibrary->setOption( - "DISTANCE_UNIT", _pMaterialXGenerator->codeGenerator().units().indices.at("centimeter")); - - // Set the importance sampling mode. - _pShaderLibrary->setOption("IMPORTANCE_SAMPLING_MODE", kImportanceSamplingModeMIS); -#endif - // Initialize a per-frame data buffer. initFrameData(); @@ -159,174 +137,13 @@ ISamplerPtr PTRenderer::createSamplerPointer(const Properties& props) IMaterialPtr PTRenderer::createMaterialPointer( const string& materialType, const string& document, const string& name) { - // Validate material type. - AU_ASSERT(materialType.compare(Names::MaterialTypes::kBuiltIn) == 0 || - materialType.compare(Names::MaterialTypes::kMaterialX) == 0 || - materialType.compare(Names::MaterialTypes::kMaterialXPath) == 0, - "Invalid material type:", materialType.c_str()); - - // Set the global "flipY" flag on the asset manager, to match option. - // This has no overhead, so just do it each time. - _pAssetMgr->enableVerticalFlipOnImageLoad(_values.asBoolean(kLabelIsFlipImageYEnabled)); - - // The material shader and definition for this material. - MaterialShaderPtr pShader; - shared_ptr pDef; - - // Create a material type based on the material type name provided. - if (materialType.compare(Names::MaterialTypes::kBuiltIn) == 0) - { - // Work out built-in type. - string builtInType = document; - - // Get the built-in material type and definition for built-in. - pShader = _pShaderLibrary->getBuiltInShader(builtInType); - pDef = _pShaderLibrary->getBuiltInMaterialDefinition(builtInType); - - // Print error and provide null material shader if built-in not found. - // TODO: Proper error handling for this case. - if (!pShader) - { - AU_ERROR("Unknown built-in material type %s for material %s", document.c_str(), - name.c_str()); - pShader = nullptr; - } - } - else if (materialType.compare(Names::MaterialTypes::kMaterialX) == 0) - { - // Generate a material shader and definition from the materialX document. - pShader = generateMaterialX(document, &pDef); - - // If flag is set dump the document to disk for development purposes. - if (AU_DEV_DUMP_MATERIALX_DOCUMENTS) - { - string mltxPath = name + "Dumped.mtlx"; - Foundation::sanitizeFileName(mltxPath); - if (Foundation::writeStringToFile(document, mltxPath)) - AU_INFO("Dumping MTLX document to:%s", mltxPath.c_str()); - else - AU_WARN("Failed to dump MTLX document to:%s", mltxPath.c_str()); - } - } - else if (materialType.compare(Names::MaterialTypes::kMaterialXPath) == 0) - { - // Load the MaterialX file using asset manager. - auto pMtlxDocument = _pAssetMgr->acquireTextFile(document); - - // Print error and provide default material type if built-in not found. - // TODO: Proper error handling for this case. - if (!pMtlxDocument) - { - AU_ERROR("Failed to load MaterialX document %s for material %s", document.c_str(), - name.c_str()); - pShader = nullptr; - } - else - { - // If Material XML document loaded, use it to generate the material shader and - // definition. - pShader = generateMaterialX(*pMtlxDocument, &pDef); - } - } - else - { - // Print error and return null material shader if material type not found. - // TODO: Proper error handling for this case. - AU_ERROR( - "Unrecognized material type %s for material %s.", materialType.c_str(), name.c_str()); - pShader = nullptr; - } - - // Error case, just return null material. - if (!pShader || !pDef) - return nullptr; - - // Create the material object with the material shader and definition. - auto pNewMtl = make_shared(this, pShader, pDef); - - // Set the default textures on the new material. - for (int i = 0; i < pDef->defaults().textures.size(); i++) - { - auto txtDef = pDef->defaults().textures[i]; - - // Image default values are provided as strings and must be loaded. - auto textureFilename = txtDef.defaultFilename; - if (!textureFilename.empty()) - { - // Load the pixels for the image using asset manager. - auto pImageData = _pAssetMgr->acquireImage(textureFilename); - if (!pImageData) - { - // Print error if image fails to load, and then ignore default. - // TODO: Proper error handling here. - AU_ERROR("Failed to load image data %s for material %s", textureFilename.c_str(), - name.c_str()); - } - else - { - // Set the linearize flag. - // TODO: Should effect caching. - pImageData->data.linearize = txtDef.linearize; - - // Create Ultra image from the loaded pixels. - // TODO: This should be cached by filename. - auto pImage = createImagePointer(pImageData->data); - if (!pImage) - { - // Print error if image creation fails, and then ignore default. - // TODO: Proper error handling here. - AU_ERROR("Failed to create image %s for material %s", textureFilename.c_str(), - name.c_str()); - } - else - { - // Set the default image. - pNewMtl->setImage(txtDef.name, pImage); - } - } - } - - // If we have an address mode, create a sampler for texture. - // Currently only the first two hardcoded textures have samplers, so only do this for first - // two textures. - // TODO: Move to fully data driven textures and samplers. - if (i < 2 && (!txtDef.addressModeU.empty() || !txtDef.addressModeV.empty())) - { - Properties samplerProps; - - // Set U address mode. - if (txtDef.addressModeU.compare("periodic") == 0) - samplerProps[Names::SamplerProperties::kAddressModeU] = Names::AddressModes::kWrap; - else if (txtDef.addressModeU.compare("clamp") == 0) - samplerProps[Names::SamplerProperties::kAddressModeU] = Names::AddressModes::kClamp; - else if (txtDef.addressModeU.compare("mirror") == 0) - samplerProps[Names::SamplerProperties::kAddressModeU] = - Names::AddressModes::kMirror; - - // Set V address mode. - if (txtDef.addressModeV.compare("periodic") == 0) - samplerProps[Names::SamplerProperties::kAddressModeV] = Names::AddressModes::kWrap; - else if (txtDef.addressModeV.compare("clamp") == 0) - samplerProps[Names::SamplerProperties::kAddressModeV] = Names::AddressModes::kClamp; - else if (txtDef.addressModeV.compare("mirror") == 0) - samplerProps[Names::SamplerProperties::kAddressModeV] = - Names::AddressModes::kMirror; - - // Create a sampler and set in the material. - // TODO: Don't assume hardcoded _sampler prefix. - auto pSampler = createSamplerPointer(samplerProps); - pNewMtl->setSampler(txtDef.name + "_sampler", pSampler); - } - } - - // Return new material. - return pNewMtl; + return dxScene()->createMaterialPointer(materialType, document, name); } IScenePtr PTRenderer::createScene() { // Create and return a new scene object. - return make_shared(this, _pShaderLibrary.get(), kDescriptorCount); + return make_shared(this, kDescriptorCount); } IEnvironmentPtr PTRenderer::createEnvironmentPointer() @@ -358,7 +175,9 @@ void PTRenderer::setScene(const IScenePtr& pScene) // Assign the new scene. _pScene = dynamic_pointer_cast(pScene); - ; + + // Scene's default resources cannot be created until the scene is attached to a renderer. + _pScene->createDefaultResources(); } void PTRenderer::setTargets(const TargetAssignments& targetAssignments) @@ -439,10 +258,14 @@ void PTRenderer::waitForTask() ::WaitForSingleObject(_hTaskEvent, INFINITE); } } - const vector& PTRenderer::builtInMaterials() { - return _pShaderLibrary->builtInMaterials(); + return shaderLibrary().builtInMaterials(); +} + +PTShaderLibrary& PTRenderer::shaderLibrary() +{ + return dxScene()->shaderLibrary(); } void PTRenderer::setLoadResourceFunction(LoadResourceFunction func) @@ -530,6 +353,9 @@ D3D12_GPU_VIRTUAL_ADDRESS PTRenderer::getScratchBuffer(size_t size) void PTRenderer::transferBufferUpdated(const TransferBuffer& buffer) { + + AU_ASSERT(!buffer.isMapped(), "Buffer is mapped"); + // Get the mapped range for buffer (set the end to buffer size, in the case where end==0.) size_t beginMap = buffer.mappedRange.Begin; size_t endMap = buffer.mappedRange.End == 0 ? buffer.size : buffer.mappedRange.End; @@ -583,6 +409,7 @@ void PTRenderer::uploadTransferBuffers() { // Get pending buffer. auto& buffer = iter->second; + AU_ASSERT(!buffer.isMapped(), "Buffer was not unmapped"); // Calculate the byte count from the mapped range (which will be the maximum range mapped // this frame.) @@ -826,7 +653,7 @@ void PTRenderer::updateRayGenShaderTable() // Write the shader identifier for the ray gen shader. ::memcpy_s(pShaderTableMappedData, _rayGenShaderTableSize, - _pShaderLibrary->getSharedEntryPointShaderID(EntryPointTypes::kRayGen), SHADER_ID_SIZE); + shaderLibrary().getShaderID(PTShaderLibrary::kRayGenEntryPointName), SHADER_ID_SIZE); // Unmap the shader table buffer. _rayGenShaderTable.unmap(); @@ -884,40 +711,28 @@ void PTRenderer::renderInternal(uint32_t sampleStart, uint32_t sampleCount) // Have any options changed? if (_bIsDirty) { -#if ENABLE_MATERIALX - // Get the units option. string unit = _values.asString(kLabelUnits); - // Lookup the unit in the code generator, and ensure it is valid. - auto unitIter = _pMaterialXGenerator->codeGenerator().units().indices.find(unit); - if (unitIter == _pMaterialXGenerator->codeGenerator().units().indices.end()) - { - AU_ERROR("Invalid unit:" + unit); - } - else - { - // Set the option in the shader library. - _pShaderLibrary->setOption("DISTANCE_UNIT", unitIter->second); - } -#endif - - // Set the importance sampling mode. - _pShaderLibrary->setOption( - "IMPORTANCE_SAMPLING_MODE", _values.asInt(kLabelImportanceSamplingMode)); + dxScene()->setUnit(unit); // Set the default BSDF (if true will use Reference, if false will use Standard Surface.) - _pShaderLibrary->setOption( + shaderLibrary().setOption( "USE_REFERENCE_BSDF", _values.asBoolean(kLabelIsReferenceBSDFEnabled)); // Clear the dirty flag. _bIsDirty = false; } + // Update the scene. + dxScene()->update(); + // Rebuild shader library if needed. - if (_pShaderLibrary->rebuildRequired()) + if (shaderLibrary().rebuildRequired()) { // If shader rebuild required, wait for GPU to be idle, then rebuild. waitForTask(); - _pShaderLibrary->rebuild(); + int globalTextureCount, globalSamplerCount; + dxScene()->computeMaterialTextureCount(globalTextureCount, globalSamplerCount); + shaderLibrary().rebuild(globalTextureCount, globalSamplerCount); // Update the ray gen shader table after rebuild. updateRayGenShaderTable(); @@ -927,7 +742,7 @@ void PTRenderer::renderInternal(uint32_t sampleStart, uint32_t sampleCount) } // Update the scene. - dxScene()->update(); + dxScene()->updateResources(); // Update the resources associated with the scene, output (targets), and denoising. // NOTE: These updates are very fast if nothing relevant has changed since the last render. @@ -1136,6 +951,10 @@ void PTRenderer::updateOutputResources() } // Create UAVs for the textures as the first entries in the descriptor heap. + // The order they are written to the descriptor heap must match the ray gen root descriptor + // defined in PTShaderLibrary::initRootSignatures and the AOV RWTexture2D defined in + // MainEntryPoints.slang. + // TODO: These do not seem to match the values in the shader, why is that? CD3DX12_CPU_DESCRIPTOR_HANDLE handle( _pDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); createUAV(_pTexFinal.Get(), handle); @@ -1214,6 +1033,10 @@ void PTRenderer::updateDenoisingResources() // Create UAVs for the denoising textures as the entries in the descriptor heap *after* the // descriptors for the output-related textures. + // The order they are written to the descriptor heap must match the ray gen root descriptor + // defined in PTShaderLibrary::initRootSignatures and the AOV RWTexture2D defined in + // MainEntryPoints.slang. + // TODO: These do not seem to match the values in the shader, why is that? CD3DX12_CPU_DESCRIPTOR_HANDLE handle(_pDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), kOutputDescriptorCount, _handleIncrementSize); createUAV(_pDenoisingTexDepthView.Get(), handle); @@ -1315,10 +1138,12 @@ void PTRenderer::submitRayDispatch( // Prepare the root signature, and pipeline state. pCommandList->SetDescriptorHeaps(2, pDescriptorHeaps); - pCommandList->SetComputeRootSignature(_pShaderLibrary->globalRootSignature().Get()); - pCommandList->SetPipelineState1(_pShaderLibrary->pipelineState().Get()); + pCommandList->SetComputeRootSignature(shaderLibrary().globalRootSignature().Get()); + pCommandList->SetPipelineState1(shaderLibrary().pipelineState().Get()); // Set the global root signature arguments. + // Must match the root signature defined in PTShaderLibrary::initRootSignatures and the GPU + // version in GlobalRootSignature.slang. { // 0) The acceleration structure. D3D12_GPU_VIRTUAL_ADDRESS accelStructureAddress = @@ -1363,6 +1188,31 @@ void PTRenderer::submitRayDispatch( // 7) The null acceleration structure (used for layer material shading). D3D12_GPU_VIRTUAL_ADDRESS nullAccelStructAddress = 0; pCommandList->SetComputeRootShaderResourceView(7, nullAccelStructAddress); + + // 8) The global material buffer + pCommandList->SetComputeRootShaderResourceView( + 8, dxScene()->globalMaterialBuffer().pGPUBuffer->GetGPUVirtualAddress()); + + // 9) The global instance buffer + pCommandList->SetComputeRootShaderResourceView( + 9, dxScene()->globalInstanceBuffer().pGPUBuffer->GetGPUVirtualAddress()); + + // 10) The layer geometry buffer + pCommandList->SetComputeRootShaderResourceView( + 10, dxScene()->layerGeometryBuffer().pGPUBuffer->GetGPUVirtualAddress()); + + // 11) The transform matrix buffer + pCommandList->SetComputeRootShaderResourceView( + 11, dxScene()->transformMatrixBuffer().pGPUBuffer->GetGPUVirtualAddress()); + + // 12) The global material texture array + handle.Offset(dxScene()->environment()->descriptorCount(), _handleIncrementSize); + pCommandList->SetComputeRootDescriptorTable(12, handle); + + // 13) The global material sampler array + CD3DX12_GPU_DESCRIPTOR_HANDLE samplerHandle( + _pSamplerDescriptorHeap->GetGPUDescriptorHandleForHeapStart()); + pCommandList->SetComputeRootDescriptorTable(13, samplerHandle); } // Launch the ray generation shader with the dispatch, which performs path tracing. @@ -1427,14 +1277,19 @@ void PTRenderer::submitAccumulation(uint32_t sampleIndex) pCommandList->SetPipelineState(_pAccumulationPipelineState.Get()); // Set the root signature arguments for accumulation: the descriptor table and the accumulation - // settings. The descriptor table must start with the accumulation texture. Then dispatch the - // accumulation shader, which performs (optional) deferred shading and merges new path tracing - // samples with the previous results. + // settings. The descriptor table must start with the accumulation texture. CD3DX12_GPU_DESCRIPTOR_HANDLE handle(_pDescriptorHeap->GetGPUDescriptorHandleForHeapStart(), kAccumulationDescriptorOffset, _handleIncrementSize); pCommandList->SetComputeRootDescriptorTable(0, handle); pCommandList->SetComputeRoot32BitConstants(1, sizeof(Accumulation) / 4, &_accumData, 0); - pCommandList->Dispatch(_outputDimensions.x, _outputDimensions.y, 1); + + // Dispatch the accumulation shader, which performs (optional) deferred shading and merges new + // path tracing samples with the previous results. + // NOTE: The dispatch is performed with thread group dimensions that provide good occupancy for + // the current compute shader code. This must match the values in the compute shader. + constexpr uvec2 kThreadGroupCount(16, 8); + pCommandList->Dispatch((_outputDimensions.x + kThreadGroupCount.x - 1) / kThreadGroupCount.x, + (_outputDimensions.y + kThreadGroupCount.y - 1) / kThreadGroupCount.y, 1); // Submit the command list. submitCommandList(); @@ -1460,17 +1315,23 @@ void PTRenderer::submitPostProcessing() // Set the root signature arguments for post-processing: the descriptor table (at the start of // the heap) and the post-processing settings. The descriptor table must start with the final - // texture. Then dispatch the post-processing shader, which tone maps (as needed) the - // accumulation texture (HDR) to the final texture (usually SDR). - // NOTE: Even if the settings mean no post-processing is performed, there is still an implicit - // format conversion, from the accumulation texture (UAV) format to the output texture format, - // e.g. floating-point to integer. + // texture. CD3DX12_GPU_DESCRIPTOR_HANDLE handle(_pDescriptorHeap->GetGPUDescriptorHandleForHeapStart(), kFinalDescriptorOffset, _handleIncrementSize); pCommandList->SetComputeRootDescriptorTable(0, handle); pCommandList->SetComputeRoot32BitConstants( 1, sizeof(PostProcessing) / 4, &_postProcessingData, 0); - pCommandList->Dispatch(_outputDimensions.x, _outputDimensions.y, 1); + + // Dispatch the post-processing shader, which tone maps(as needed) the accumulation texture + // (HDR) to the final texture (usually SDR). + // NOTE: This should be done even if the settings mean no post-processing is performed as there + // is still an implicit format conversion, from the accumulation texture (UAV) format to the + // output texture format, e.g. floating-point to integer. + // NOTE: The dispatch is performed with thread group dimensions that provide good occupancy for + // the current compute shader code. This must match the values in the compute shader. + constexpr uvec2 kThreadGroupCount(16, 8); + pCommandList->Dispatch((_outputDimensions.x + kThreadGroupCount.x - 1) / kThreadGroupCount.x, + (_outputDimensions.y + kThreadGroupCount.y - 1) / kThreadGroupCount.y, 1); // Copy the output textures to their associated targets, if any. copyTextureToTarget(_pTexFinal.Get(), _pTargetFinal.get()); @@ -1520,31 +1381,4 @@ bool PTRenderer::isDenoisingAOVsEnabled() const _values.asInt(kLabelDebugMode) > kDebugModeErrors; } -shared_ptr PTRenderer::generateMaterialX([[maybe_unused]] const string& document, - [[maybe_unused]] shared_ptr* pDefOut) -{ -#if ENABLE_MATERIALX - // Generate the material definition for the materialX document, this contains the source code, - // default values, and a unique name. - shared_ptr pDef = _pMaterialXGenerator->generate(document); - if (!pDef) - { - return nullptr; - } - - // Acquire a material shader for the definition. - // This will create a new one if needed (and trigger a rebuild), otherwise will it will return - // existing one. - auto pShader = _pShaderLibrary->acquireShader(*pDef); - - // Output the definition pointer. - if (pDefOut) - *pDefOut = pDef; - - return pShader; -#else - return nullptr; -#endif -} - END_AURORA diff --git a/Libraries/Aurora/Source/DirectX/PTRenderer.h b/Libraries/Aurora/Source/DirectX/PTRenderer.h index 6d9ae30..309006c 100644 --- a/Libraries/Aurora/Source/DirectX/PTRenderer.h +++ b/Libraries/Aurora/Source/DirectX/PTRenderer.h @@ -19,6 +19,7 @@ #include "PTMaterial.h" #include "PTSampler.h" #include "PTScene.h" +#include "PTShaderLibrary.h" #include "PTTarget.h" #include "RendererBase.h" @@ -29,11 +30,6 @@ BEGIN_AURORA -namespace MaterialXCodeGen -{ -class MaterialGenerator; -} // namespace MaterialXCodeGen - // Forward references. class AssetManager; class PTDevice; @@ -145,9 +141,8 @@ class PTRenderer : public RendererBase void createUAV(ID3D12Resource* pTexture, CD3DX12_CPU_DESCRIPTOR_HANDLE& handle); void copyTextureToTarget(ID3D12Resource* pTexture, PTTarget* pTarget); bool isDenoisingAOVsEnabled() const; - shared_ptr generateMaterialX( - const string& document, shared_ptr* pDefOut); PTScenePtr dxScene() { return static_pointer_cast(_pScene); } + PTShaderLibrary& shaderLibrary(); /*** Private Variables ***/ @@ -164,14 +159,6 @@ class PTRenderer : public RendererBase PTSamplerPtr _pDefaultSampler; PTGroundPlanePtr _pDefaultGroundPlane; - // The shader library used to compile DXIL shader libraries. - unique_ptr _pShaderLibrary; - - // Code generator used to generate MaterialX files. -#if ENABLE_MATERIALX - unique_ptr _pMaterialXGenerator; -#endif - /*** DirectX 12 Objects ***/ ID3D12Device5Ptr _pDXDevice; diff --git a/Libraries/Aurora/Source/DirectX/PTSampler.cpp b/Libraries/Aurora/Source/DirectX/PTSampler.cpp index 601bce3..565a9ec 100644 --- a/Libraries/Aurora/Source/DirectX/PTSampler.cpp +++ b/Libraries/Aurora/Source/DirectX/PTSampler.cpp @@ -60,7 +60,7 @@ void PTSampler::createDescriptor( D3D12_TEXTURE_ADDRESS_MODE PTSampler::valueToDXAddressMode(const PropertyValue& value) { - // Convert property string to DX addres mode. + // Convert property string to DX address mode. string valStr = value.asString(); if (valStr.compare(Names::AddressModes::kWrap) == 0) return D3D12_TEXTURE_ADDRESS_MODE_WRAP; diff --git a/Libraries/Aurora/Source/DirectX/PTScene.cpp b/Libraries/Aurora/Source/DirectX/PTScene.cpp index 654f279..ecbd215 100644 --- a/Libraries/Aurora/Source/DirectX/PTScene.cpp +++ b/Libraries/Aurora/Source/DirectX/PTScene.cpp @@ -18,72 +18,105 @@ #include "PTEnvironment.h" #include "PTGeometry.h" #include "PTGroundPlane.h" +#include "PTImage.h" #include "PTLight.h" #include "PTMaterial.h" #include "PTRenderer.h" #include "PTShaderLibrary.h" +#if ENABLE_MATERIALX +#include "MaterialX/MaterialGenerator.h" +#endif + +#include + +#define AU_DEV_DUMP_MATERIALX_DOCUMENTS 0 + BEGIN_AURORA -#define kBuiltInMissShaderCount 4 // "built-in" miss shaders: null, background, radiance, and shadow -PTLayerIndexTable::PTLayerIndexTable(PTRenderer* pRenderer, const vector& indices) : - _pRenderer(pRenderer) +#define kBuiltInMissShaderCount 2 // "built-in" miss shaders: null and shadow + +// Maximum number of textures per material from +#define kMaterialMaxTextures 8 +#define kTransformMatrixSize (sizeof(float) * 12) + +// Fix-sized header that appears before material constants in gGlobalMaterialConstants buffer. +struct MaterialHeader +{ + int shaderIndex; + int textureIndices[kMaterialMaxTextures]; + int samplerIndices[kMaterialMaxTextures]; +}; + +// Fixed size header size. +// NOTE: Must match value in Material.slang and size of MaterialHeader. +#define kMaterialHeaderSize 68 + +bool cmpProperties(const pair& i, const pair& j) { - if (!indices.empty()) - set(indices); + return (i.first < j.first); } -void PTLayerIndexTable::set(const vector& indices) +// Compute hash from properties vector. +size_t hashProperties(const Properties& properties) { - // Create a constant buffer for the material data if it doesn't already exist. - static const size_t BUFFER_SIZE = sizeof(int) * kMaxMaterialLayers; - if (!_pConstantBuffer) + vector> propVec; + for (auto prop : properties) { - _pConstantBuffer = _pRenderer->createBuffer(BUFFER_SIZE); + propVec.push_back(prop); } - - _count = static_cast(indices.size()); - - // Copy the indices to the constant buffer. - int* pMappedData = nullptr; - checkHR(_pConstantBuffer->Map(0, nullptr, (void**)&pMappedData)); - for (int i = 0; i < kMaxMaterialLayers; i++) + sort(propVec.begin(), propVec.end(), cmpProperties); + size_t res = hash {}(propVec.size()); + for (auto prop : propVec) { - if (i < indices.size()) - pMappedData[i] = indices[i]; - else - pMappedData[i] = -1; + string val = prop.second.toString(); + size_t valHash = hash {}(val); + Foundation::hashCombine(res, valHash); + size_t propHash = hash {}(prop.first); + Foundation::hashCombine(res, propHash); } - _pConstantBuffer->Unmap(0, nullptr); // no HRESULT + + return res; } +// Fixed size header for instance data in global buffer. +// Followed by variable size array of layer information. +// NOTE: Must be exactly 2 32-bit words long to match accessors in GlobalBufferAccessors.slang. +struct InstanceDataHeader +{ + // Offset within material buffer for this instance's material. + int materialBufferOffset; + + // Number of material layers for this instance. + // If non-zero, the information for each layer will be stored after this header in the buffer. + int materialLayerCount; +}; + +// Fixed instance data stride (transform matrix and material buffer offset.) Must match GPU value +// in GlobalBufferAccessors.slang. +#define kInstanceDataHeaderSize (2 * sizeof(int)) + // A structure that contains the properties for a hit group shader record, laid out for direct // copying to a shader table buffer. +// Must match the GPU layout defined by InstancePipelineState.slang struct HitGroupShaderRecord { // Constructor. HitGroupShaderRecord(const void* pShaderIdentifier, const PTGeometry::GeometryBuffers& geometry, - const PTMaterial& material, ID3D12Resource* pMaterialLayerIndexBuffer, - D3D12_GPU_DESCRIPTOR_HANDLE texturesHeapAddress, - D3D12_GPU_DESCRIPTOR_HANDLE samplersHeapAddress, int materialLayerCount) + int instanceBufferOffset, bool isOpaque) { ::memcpy_s(&ShaderIdentifier, SHADER_ID_SIZE, pShaderIdentifier, SHADER_ID_SIZE); - IndexBufferAddress = geometry.IndexBuffer; - PositionBufferAddress = geometry.PositionBuffer; - HasNormals = geometry.NormalBuffer ? 1 : 0; - NormalBufferAddress = HasNormals ? geometry.NormalBuffer : 0; - HasTangents = geometry.TangentBuffer != 0; - TangentBufferAddress = HasTangents ? geometry.TangentBuffer : 0; - HasTexCoords = geometry.TexCoordBuffer != 0; - TexCoordBufferAddress = HasTexCoords ? geometry.TexCoordBuffer : 0; - IsOpaque = material.isOpaque(); - MaterialLayerCount = materialLayerCount; - MaterialBufferAddress = material.buffer()->GetGPUVirtualAddress(); - MaterialLayerIndexTableAddress = pMaterialLayerIndexBuffer - ? pMaterialLayerIndexBuffer->GetGPUVirtualAddress() - : (D3D12_GPU_VIRTUAL_ADDRESS) nullptr; - TexturesHeapAddress = texturesHeapAddress; - SamplersHeapAddress = samplersHeapAddress; + IndexBufferAddress = geometry.IndexBuffer; + PositionBufferAddress = geometry.PositionBuffer; + HasNormals = geometry.NormalBuffer ? 1 : 0; + NormalBufferAddress = HasNormals ? geometry.NormalBuffer : 0; + HasTangents = geometry.TangentBuffer != 0; + TangentBufferAddress = HasTangents ? geometry.TangentBuffer : 0; + HasTexCoords = geometry.TexCoordBuffer != 0; + TexCoordBufferAddress = HasTexCoords ? geometry.TexCoordBuffer : 0; + IsOpaque = isOpaque; + + InstanceBufferOffset = instanceBufferOffset; } // Copies the contents of the shader record to the specified mapped buffer. @@ -99,23 +132,24 @@ struct HitGroupShaderRecord } // These are the hit group arguments, in the order declared in the associated local root - // signature. These must be aligned by their size, e.g. a root descriptor must be aligned on an - // 8-byte (its size) boundary. + // signature and the layout in InstancePipelineState.slang. These must be aligned by their size, + // e.g. a root descriptor must be aligned on an 8-byte (its size) boundary. + + // Shader identifier which is part of the hit group record, not the user defined root signature. array ShaderIdentifier; + + // Geometry data defined in ByteAddressBuffers: registers t0-t4, space 1. D3D12_GPU_VIRTUAL_ADDRESS IndexBufferAddress; D3D12_GPU_VIRTUAL_ADDRESS PositionBufferAddress; D3D12_GPU_VIRTUAL_ADDRESS NormalBufferAddress; D3D12_GPU_VIRTUAL_ADDRESS TangentBufferAddress; D3D12_GPU_VIRTUAL_ADDRESS TexCoordBufferAddress; + // Instance data defined in cbuffer gInstanceData: register c0, space 1. uint32_t HasNormals; uint32_t HasTangents; uint32_t HasTexCoords; - uint32_t MaterialLayerCount; uint32_t IsOpaque; - D3D12_GPU_VIRTUAL_ADDRESS MaterialBufferAddress; - D3D12_GPU_VIRTUAL_ADDRESS MaterialLayerIndexTableAddress; - D3D12_GPU_DESCRIPTOR_HANDLE TexturesHeapAddress; - D3D12_GPU_DESCRIPTOR_HANDLE SamplersHeapAddress; + uint32_t InstanceBufferOffset; }; PTInstance::PTInstance(PTScene* pScene, const PTGeometryPtr& pGeometry, @@ -128,13 +162,10 @@ PTInstance::PTInstance(PTScene* pScene, const PTGeometryPtr& pGeometry, _pGeometry = pGeometry; _transform = transform; setMaterial(pMaterial); - for (size_t i = 0; i < layers.size(); i++) { _layers.push_back(make_pair(dynamic_pointer_cast(layers[i].first), dynamic_pointer_cast(layers[i].second))); - - _layers[i].first->shader()->incrementRefCount(EntryPointTypes::kLayerMiss); } } @@ -142,21 +173,20 @@ PTInstance::~PTInstance() { if (_pMaterial) { - _pMaterial->shader()->decrementRefCount(EntryPointTypes::kRadianceHit); - // The shadow anyhit is always attached. - _pMaterial->shader()->decrementRefCount(EntryPointTypes::kShadowAnyHit); + _pMaterial->shader()->decrementRefCount(EntryPointTypes::kInitializeMaterialExport); } - +#if 0 for (size_t i = 0; i < _layers.size(); i++) { _layers[i].first->shader()->decrementRefCount(EntryPointTypes::kLayerMiss); } +#endif } void PTInstance::setMaterial(const IMaterialPtr& pMaterial) { if (_pMaterial) - _pMaterial->shader()->decrementRefCount(EntryPointTypes::kRadianceHit); + _pMaterial->shader()->decrementRefCount(EntryPointTypes::kInitializeMaterialExport); // Cast the (optional) material to the renderer implementation. Use the default material if one // is / not specified. @@ -164,11 +194,7 @@ void PTInstance::setMaterial(const IMaterialPtr& pMaterial) ? dynamic_pointer_cast(pMaterial) : dynamic_pointer_cast(_pScene->defaultMaterialResource()->resource()); - _pMaterial->shader()->incrementRefCount(EntryPointTypes::kRadianceHit); - // The shadow anyhit is always attached. This is needed as the ordering is arbitrary for anyhit - // shader invocations, so we cannot mix shaders with and without anyhit shadow shaders in the - // same scene. - _pMaterial->shader()->incrementRefCount(EntryPointTypes::kShadowAnyHit); + _pMaterial->shader()->incrementRefCount(EntryPointTypes::kInitializeMaterialExport); // Set the instance as dirty. _bIsDirty = true; @@ -211,23 +237,291 @@ bool PTInstance::update() return wasDirty; } -PTScene::PTScene( - PTRenderer* pRenderer, PTShaderLibrary* pShaderLibrary, uint32_t numRendererDescriptors) : - SceneBase(pRenderer) +PTScene::PTScene(PTRenderer* pRenderer, uint32_t numRendererDescriptors) : SceneBase(pRenderer) { - _pRenderer = pRenderer; - _pShaderLibrary = pShaderLibrary; + _pRenderer = pRenderer; + + // Initialize the shader library. + // TODO: Should be per-scene not per-renderer. + _pShaderLibrary = make_unique(_pRenderer->dxDevice()); + _numRendererDescriptors = numRendererDescriptors; // Compute the shader record strides. _missShaderRecordStride = HitGroupShaderRecord::stride(); // shader ID, no other parameters - _missShaderRecordCount = kBuiltInMissShaderCount; // background, radiance, and shadow + _missShaderRecordCount = kBuiltInMissShaderCount; // null, and shadow _hitGroupShaderRecordStride = HitGroupShaderRecord::stride(); // Use the default environment and ground plane. _pGroundPlane = pRenderer->defaultGroundPlane(); - createDefaultResources(); + // Create arbitrary sized material buffer (will be resized to fit material constants for scene.) + _globalMaterialBuffer = _pRenderer->createTransferBuffer(512, "GlobalMaterialBuffer"); + + // Create arbitrary sized instance buffer (will be resized to fit instance constants for scene.) + _globalInstanceBuffer = _pRenderer->createTransferBuffer(512, "GlobalInstanceBuffer"); + + // Create arbitrary sized matrix buffer (will be resized to fit instance constants for scene.) + _transformMatrixBuffer = _pRenderer->createTransferBuffer(512, "TransformMatrixBuffer"); + + // Create arbitrary sized instance buffer (will be resized to fit layer geomtry for scene.) + _layerGeometryBuffer = _pRenderer->createTransferBuffer(512, "LayerGeometryBuffer"); + + // Enable layer shaders. + _pShaderLibrary->setOption("ENABLE_LAYERS", true); + +#if ENABLE_MATERIALX + // Get the materialX folder relative to the module path. + string mtlxFolder = Foundation::getModulePath() + "MaterialX"; + // Initialize the MaterialX code generator. + _pMaterialXGenerator = make_unique(mtlxFolder); + + // Default to MaterialX distance unit to centimeters. + _pShaderLibrary->setOption( + "DISTANCE_UNIT", _pMaterialXGenerator->codeGenerator().units().indices.at("centimeter")); +#endif +} +void PTScene::setUnit(const string& unit) +{ +#if ENABLE_MATERIALX + // Get the units option. + // Lookup the unit in the code generator, and ensure it is valid. + auto unitIter = _pMaterialXGenerator->codeGenerator().units().indices.find(unit); + if (unitIter == _pMaterialXGenerator->codeGenerator().units().indices.end()) + { + AU_ERROR("Invalid unit:" + unit); + } + else + { + // Set the option in the shader library. + shaderLibrary().setOption("DISTANCE_UNIT", unitIter->second); + } +#endif +} + +IMaterialPtr PTScene::createMaterialPointer( + const string& materialType, const string& document, const string& name) +{ + // Validate material type. + AU_ASSERT(materialType.compare(Names::MaterialTypes::kBuiltIn) == 0 || + materialType.compare(Names::MaterialTypes::kMaterialX) == 0 || + materialType.compare(Names::MaterialTypes::kMaterialXPath) == 0, + "Invalid material type:", materialType.c_str()); + + // Set the global "flipY" flag on the asset manager, to match option. + // This has no overhead, so just do it each time. + _pRenderer->assetManager()->enableVerticalFlipOnImageLoad( + _pRenderer->asBoolean(kLabelIsFlipImageYEnabled)); + + // The material shader and definition for this material. + MaterialShaderPtr pShader; + shared_ptr pDef; + + // Create a material type based on the material type name provided. + if (materialType.compare(Names::MaterialTypes::kBuiltIn) == 0) + { + // Work out built-in type. + string builtInType = document; + + // Get the built-in material type and definition for built-in. + pShader = shaderLibrary().getBuiltInShader(builtInType); + pDef = shaderLibrary().getBuiltInMaterialDefinition(builtInType); + + // Print error and provide null material shader if built-in not found. + // TODO: Proper error handling for this case. + if (!pShader) + { + AU_ERROR("Unknown built-in material type %s for material %s", document.c_str(), + name.c_str()); + pShader = nullptr; + } + } + else if (materialType.compare(Names::MaterialTypes::kMaterialX) == 0) + { + // Generate a material shader and definition from the materialX document. + pShader = generateMaterialX(document, &pDef); + + // If flag is set dump the document to disk for development purposes. + if (AU_DEV_DUMP_MATERIALX_DOCUMENTS) + { + string mltxPath = name + "Dumped.mtlx"; + Foundation::sanitizeFileName(mltxPath); + if (Foundation::writeStringToFile(document, mltxPath)) + AU_INFO("Dumping MTLX document to:%s", mltxPath.c_str()); + else + AU_WARN("Failed to dump MTLX document to:%s", mltxPath.c_str()); + } + } + else if (materialType.compare(Names::MaterialTypes::kMaterialXPath) == 0) + { + // Load the MaterialX file using asset manager. + auto pMtlxDocument = _pRenderer->assetManager()->acquireTextFile(document); + + // Print error and provide default material type if built-in not found. + // TODO: Proper error handling for this case. + if (!pMtlxDocument) + { + AU_ERROR("Failed to load MaterialX document %s for material %s", document.c_str(), + name.c_str()); + pShader = nullptr; + } + else + { + // If Material XML document loaded, use it to generate the material shader and + // definition. + pShader = generateMaterialX(*pMtlxDocument, &pDef); + } + } + else + { + // Print error and return null material shader if material type not found. + // TODO: Proper error handling for this case. + AU_ERROR( + "Unrecognized material type %s for material %s.", materialType.c_str(), name.c_str()); + pShader = nullptr; + } + + // Error case, just return null material. + if (!pShader || !pDef) + return nullptr; + + // Create the material object with the material shader and definition. + auto pNewMtl = make_shared(_pRenderer, name, pShader, pDef); + + // Set the default textures on the new material. + for (int i = 0; i < pDef->defaults().textures.size(); i++) + { + auto txtDef = pDef->defaults().textures[i]; + + // Image default values are provided as strings and must be loaded. + auto textureFilename = txtDef.defaultFilename; + if (!textureFilename.empty()) + { + // Compute cache name for texture from filename and linearize flag. + string cacheName = textureFilename; + if (txtDef.linearize) + cacheName += "-linearize"; + + // Look for image in cache + auto iter = _imageCache.find(cacheName); + IImagePtr pImage; + if (iter != _imageCache.end()) + { + pImage = iter->second.lock(); + } + + // If not found in cache, create it. + if (!pImage) + { + + // Load the pixels for the image using asset manager. + auto pImageData = _pRenderer->assetManager()->acquireImage(textureFilename); + if (!pImageData) + { + // Print error if image fails to load, and then ignore default. + // TODO: Proper error handling here. + AU_ERROR("Failed to load image data %s for material %s", + textureFilename.c_str(), name.c_str()); + } + else + { + // Set the linearize flag. + pImageData->data.linearize = txtDef.linearize; + + // Create image from the loaded pixels. + pImage = _pRenderer->createImagePointer(pImageData->data); + if (!pImage) + { + // Print error if image creation fails, and then ignore default. + // TODO: Proper error handling here. + AU_ERROR("Failed to create image %s for material %s", + textureFilename.c_str(), name.c_str()); + } + else + _imageCache[cacheName] = pImage; + } + } + + // If loaded successfully. + if (pImage) + { + // Set the default image for this texture definition. + pNewMtl->setImage(txtDef.name.image, pImage); + } + } + + // If we have an address mode, create a sampler for the texture. + if (!txtDef.addressModeU.empty() || !txtDef.addressModeV.empty()) + { + Properties samplerProps; + + // Set U address mode. + if (txtDef.addressModeU.compare("periodic") == 0) + samplerProps[Names::SamplerProperties::kAddressModeU] = Names::AddressModes::kWrap; + else if (txtDef.addressModeU.compare("clamp") == 0) + samplerProps[Names::SamplerProperties::kAddressModeU] = Names::AddressModes::kClamp; + else if (txtDef.addressModeU.compare("mirror") == 0) + samplerProps[Names::SamplerProperties::kAddressModeU] = + Names::AddressModes::kMirror; + + // Set V address mode. + if (txtDef.addressModeV.compare("periodic") == 0) + samplerProps[Names::SamplerProperties::kAddressModeV] = Names::AddressModes::kWrap; + else if (txtDef.addressModeV.compare("clamp") == 0) + samplerProps[Names::SamplerProperties::kAddressModeV] = Names::AddressModes::kClamp; + else if (txtDef.addressModeV.compare("mirror") == 0) + samplerProps[Names::SamplerProperties::kAddressModeV] = + Names::AddressModes::kMirror; + + // Compute hash from sampler properties, and use it to see if sampler already exists in + // the cache. + size_t propHash = hashProperties(samplerProps); + ISamplerPtr pSampler; + if (_samplerCache.find(propHash) == _samplerCache.end()) + { + // Create new sampler, and add to cache, if none found. + pSampler = _pRenderer->createSamplerPointer(samplerProps); + _samplerCache[propHash] = pSampler; + } + else + { + // Use sampler for cache if one exists. + pSampler = _samplerCache[propHash]; + } + // Set the sampler on the material. + pNewMtl->setSampler(txtDef.name.sampler, pSampler); + } + } + + // Return new material. + return pNewMtl; +} + +shared_ptr PTScene::generateMaterialX([[maybe_unused]] const string& document, + [[maybe_unused]] shared_ptr* pDefOut) +{ +#if ENABLE_MATERIALX + // Generate the material definition for the materialX document, this contains the source code, + // default values, and a unique name. + shared_ptr pDef = _pMaterialXGenerator->generate(document); + if (!pDef) + { + return nullptr; + } + + // Acquire a material shader for the definition. + // This will create a new one if needed (and trigger a rebuild), otherwise it will return an + // existing one. + auto pShader = shaderLibrary().acquireShader(*pDef); + + // Output the definition pointer. + if (pDefOut) + *pDefOut = pDef; + + return pShader; +#else + return nullptr; +#endif } ILightPtr PTScene::addLightPointer(const string& lightType) @@ -239,7 +533,7 @@ ILightPtr PTScene::addLightPointer(const string& lightType) // The remaining operations are not yet thread safe. std::lock_guard lock(_mutex); - // Assign arbritary index to ensure deterministic ordering. + // Assign arbitrary index to ensure deterministic ordering. int index = _currentLightIndex++; // Create the light object. @@ -308,7 +602,64 @@ void PTScene::update() { // Update base class. SceneBase::update(); +} + +void PTScene::computeMaterialTextureCount(int& textureCountOut, int& samplerCountOut) +{ + // Clear material texture vector and lookup map. + _activeMaterialTextures.clear(); + _materialTextureIndexLookup.clear(); + _activeMaterialSamplers.clear(); + _materialSamplerIndexLookup.clear(); + + // Create a SRV (descriptor) on the descriptor heap for the default texture (this ensures heap + // never empty.) + PTImage& defaultImage = _images.active().resources().front(); + _materialTextureIndexLookup[&defaultImage] = int(_activeMaterialTextures.size()); + _activeMaterialTextures.push_back(&defaultImage); + + // Add the default sampler as the first sampler in array. + _activeMaterialSamplers.push_back(_pRenderer->defaultSampler().get()); + _materialSamplerIndexLookup[_pRenderer->defaultSampler().get()] = 0; + + // Iterate through all active materials. + for (PTMaterial& mtl : _materials.active().resources()) + { + + // Iterate through all material's textures. + for (int i = 0; i < mtl.textures().count(); i++) + { + // Add the texture to the active texture array and lookup (If we've not seen this + // texture before in this loop.) + const auto& txtProp = mtl.textures().get(i); + auto pTxt = static_pointer_cast(txtProp.image); + if (pTxt && + _materialTextureIndexLookup.find(pTxt.get()) == _materialTextureIndexLookup.end()) + { + _materialTextureIndexLookup[pTxt.get()] = int(_activeMaterialTextures.size()); + _activeMaterialTextures.push_back(pTxt.get()); + } + // Add the sampler to the active sampler array and lookup (If we've not seen this + // sampler before in this loop.) + auto pSampler = static_pointer_cast(txtProp.sampler); + if (pSampler && + _materialSamplerIndexLookup.find(pSampler.get()) == + _materialSamplerIndexLookup.end()) + { + _materialSamplerIndexLookup[pSampler.get()] = int(_activeMaterialSamplers.size()); + _activeMaterialSamplers.push_back(pSampler.get()); + } + } + } + + // Return count. + textureCountOut = int(_activeMaterialTextures.size()); + samplerCountOut = int(_activeMaterialSamplers.size()); +} + +void PTScene::updateResources() +{ // Delete the transfer buffers that were uploaded last frame. _pRenderer->deleteUploadedTransferBuffers(); @@ -328,6 +679,7 @@ void PTScene::update() // See if any distant lights have been changed this frame, and build vector of active lights. bool distantLightsUpdated = false; vector currLights; + vector lightsToDelete; for (auto iter = _distantLights.begin(); iter != _distantLights.end(); iter++) { // Get the light and ensure weak pointer still valid. @@ -346,13 +698,19 @@ void PTScene::update() } else { - // If the weak pointer is not valid remove it from the map, and ensure GPU data is - // updated (as a light has been removed.) - _distantLights.erase(iter->first); + // If the weak pointer is not valid, add it to the list to be removed. + lightsToDelete.push_back(iter->first); distantLightsUpdated = true; } } + // Remove the invalid pointers from the map. + // Outside the iterator loop as it's not safe to call erase from inside the loop. + for (int i = 0; i < lightsToDelete.size(); i++) + { + _distantLights.erase(lightsToDelete[i]); + } + // If distant lights have changed update the LightData struct that is passed to the GPU. if (distantLightsUpdated) { @@ -399,19 +757,105 @@ void PTScene::update() // If any active material resources have been modified update them and build a list of unique // samplers for all the active materials. - if (_materials.changedThisFrame()) + if (_materials.changedThisFrame() || _images.changedThisFrame()) { - map materialSamplerIndicesMap; - _samplerLookup.clear(); + // Clear the material offset map. + _materialOffsetLookup.clear(); + + // Rebuild material texture lookup map. + int globalTextureCount, globalSamplerCount; + computeMaterialTextureCount(globalTextureCount, globalSamplerCount); + + // Starting at beginning of buffer, work out where each material is global byte address + // buffer. + size_t globalMaterialBufferSize = 0; + // Iterate through the all the active materials, even ones that have not changed. for (PTMaterial& mtl : _materials.active().resources()) { + // Update the material. mtl.update(); - // Further ensure combination of samplers in material is unique. - _samplerLookup.add(mtl); + // Add the offset for this material to the lookup map. + _materialOffsetLookup[&mtl] = int(globalMaterialBufferSize); + + // Increment by size of fixed-length header. + globalMaterialBufferSize += sizeof(MaterialHeader); + + AU_ASSERT(sizeof(MaterialHeader) == kMaterialHeaderSize, "Header size mismatch"); + + // Increment by size of this material's properties. + globalMaterialBufferSize += mtl.uniformBuffer().size(); + } + + // If the global material buffer too small recreate it. + if (globalMaterialBufferSize > _globalMaterialBuffer.size) + { + _globalMaterialBuffer = + _pRenderer->createTransferBuffer(globalMaterialBufferSize, "GlobalMaterialBuffer"); } + // Map the global material buffer. + uint8_t* pMtlDataStart = _globalMaterialBuffer.map(); + uint8_t* pMtlData = pMtlDataStart; + size_t sizeLeft = _globalMaterialBuffer.size; + for (PTMaterial& mtl : _materials.active().resources()) + { + // Ensure offset matches the lookup value. + int offset = _materialOffsetLookup[&mtl]; + AU_ASSERT(pMtlDataStart + offset == pMtlData, "Offset mismatch"); + + // Get pointer to material header and add shader index. + MaterialHeader* pHdr = (MaterialHeader*)pMtlData; + pHdr->shaderIndex = mtl.shader()->libraryIndex(); + + // Add the material texture indices (fill in unused values as invalid) + // TODO: No need for this to be fixed length. + for (int j = 0; j < mtl.textures().count() && j < kMaterialMaxTextures; j++) + { + if (!mtl.textures().get(j).image) + { + pHdr->textureIndices[j] = kInvalidOffset; + } + else + { + pHdr->textureIndices[j] = + _materialTextureIndexLookup[mtl.textures().get(j).image.get()]; + } + + if (!mtl.textures().get(j).sampler) + { + pHdr->samplerIndices[j] = 0; + } + else + { + pHdr->samplerIndices[j] = + _materialSamplerIndexLookup[mtl.textures().get(j).sampler.get()]; + } + } + for (int j = int(mtl.textures().count()); j < kMaterialMaxTextures; j++) + { + pHdr->textureIndices[j] = kInvalidOffset; + pHdr->samplerIndices[j] = 0; + } + + // Move pointer past header. + size_t headerSize = sizeof(MaterialHeader); + sizeLeft -= headerSize; + pMtlData += headerSize; + + // Write the properties from material's uniform buffer. + auto& uniformBuffer = mtl.uniformBuffer(); + size_t bufferSize = uniformBuffer.size(); + float* pSrcData = (float*)uniformBuffer.data(); + ::memcpy_s(pMtlData, sizeLeft, pSrcData, bufferSize); + + // Move pointer to next material. + sizeLeft -= bufferSize; + pMtlData += bufferSize; + } + _globalMaterialBuffer.unmap(); + // Wait for previous render tasks and then clear the descriptor heap. // TODO: Only do this if any texture parameters have changed. _pRenderer->waitForTask(); @@ -471,6 +915,24 @@ void PTScene::clearShaderData() _pMissShaderTable.Reset(); } +PTScene::InstanceData PTScene::createInstanceData(const PTInstance& instance) +{ + InstanceData res(instance); + res.mtlBufferOffset = _materialOffsetLookup[instance.material().get()]; + res.pGeometry = instance.dxGeometry(); + res.isOpaque = instance.material() ? instance.material()->isOpaque() : true; + + for (int i = 0; i < instance.materialLayers().size(); i++) + { + auto& layer = instance.materialLayers()[i]; + int mtlOffset = _materialOffsetLookup[layer.first.get()]; + int geomOffset = _layerGeometryOffsetLookup[layer.second.get()]; + + res.layers.push_back(make_pair(mtlOffset, geomOffset)); + } + return res; +} + void PTScene::updateAccelerationStructure() { // Do nothing if the acceleration structure already exists. @@ -479,86 +941,88 @@ void PTScene::updateAccelerationStructure() return; } - // Build a list of *unique* instance data objects, i.e. unique combinations of geometry and - // material (but not transform). This can be used to build a minimal set of shader records, e.g. - // a large assembly with hundreds of identical screws can have the same shader record for all of - // the screws. At the same time, assign an identifier (index) so that each instance in the TLAS - // (built later) can refer to the correct shader record. - uint32_t nextIndex = 0; - InstanceDataMap instanceDataMap; - LayerIndicesMap layerIndicesMap; - vector instanceDataIndices; - map materialSamplerIndicesMap; - instanceDataIndices.reserve(_instances.active().count()); + // Build a list of instance data in the global instance buffer. As the buffer contains a copy + // of the transform matrix, there must be an entry (and a hit group) for every instance, even if + // they share all the same data other than transform matrix. + // TODO: If it was possible access the TLAS instance matrix from the ray generation shader we + // could remove this copy of the transform data, and share hit groups between instances. + _instanceDataIndexLookup.clear(); + _layerGeometryOffsetLookup.clear(); + _layerGeometry.clear(); _lstInstanceData.clear(); - _lstLayerData.clear(); + InstanceDataMap instanceDataMap; - // Iterate through all the active instances in the scene. + // Iterate through all the active instances in the scene to compute instance data (the unique + // data for each instance, excluding transform matrix.). + int instanceBufferOffset = 0; + int layerGeomtryBufferOffset = 0; for (PTInstance& instance : _instances.active().resources()) { - // Find the index of this instance's material within list of active materials. - uint32_t mtlIndex = _materials.active().findActiveIndex(instance.material()); - AU_ASSERT(mtlIndex != -1, "Material not active"); - - // Build the data required to implement material layers. - vector layerIndices; - PTLayerIndexTablePtr layerIndexTable; - if (instance.materialLayers().size()) + // Compute the layer geometry offsets for any layers. + for (int j = 0; j < instance.materialLayers().size(); j++) { - auto& materialLayers = instance.materialLayers(); - for (int i = 0; i < materialLayers.size(); i++) + PTGeometryPtr pGeom = instance.materialLayers()[j].second; + auto iter = _layerGeometryOffsetLookup.find(pGeom.get()); + if (iter == _layerGeometryOffsetLookup.end()) { - // Find the index of this layer's material within list of active materials. - PTMaterialPtr pLayerMtl = materialLayers[i].first; - uint32_t layerMtlIndex = _materials.active().findActiveIndex(pLayerMtl); - - // Build an array of layer indices for each instance. - layerIndices.push_back( - static_cast(_lstLayerData.size()) + kBuiltInMissShaderCount); - - // For each layer material create the layer data required to build shader. - // TODO: We could remove duplicates here, as we do with the instance data. - LayerData layerData(materialLayers[i], layerMtlIndex); - _lstLayerData.push_back(layerData); + _layerGeometry.push_back(pGeom.get()); + AU_ASSERT(instance.dxGeometry()->vertexCount() == pGeom->vertexCount(), + "Layer geometry vertex count does not match base geometry vertex count."); + _layerGeometryOffsetLookup[pGeom.get()] = layerGeomtryBufferOffset; + layerGeomtryBufferOffset += pGeom->vertexCount() * sizeof(float) * 2; } - - if (layerIndicesMap.find(layerIndices) == layerIndicesMap.end()) - { - layerIndexTable = make_shared(_pRenderer, layerIndices); - layerIndicesMap[layerIndices] = layerIndexTable; - } - else - layerIndexTable = layerIndicesMap[layerIndices]; } - // If there is no matching instance data in the map, create a new index, add a new instance - // data object to the map, and add to the (ordered) list of instance data. Otherwise, use - // the the index for the existing instance data. - uint32_t instanceDataIndex = 0; - InstanceData instanceData(instance, layerIndexTable, mtlIndex); - if (instanceDataMap.find(instanceData) == instanceDataMap.end()) + // Compute the unique instance data for this instance, from the material, geometry, and + // layer data, but not the transform matrix. + InstanceData instanceData = createInstanceData(instance); + + // Have we encountered this instance data before? + auto instanceIter = instanceDataMap.find(instanceData); + int instanceDataIndex; + if (instanceIter == instanceDataMap.end()) { - instanceDataIndex = nextIndex++; - instanceDataMap[instanceData] = instanceDataIndex; + // If this is first time we have seen this instance data, it will have an entry in the + // instance data buffer. + + // Get the index of this instance data. + instanceDataIndex = int(_lstInstanceData.size()); + + // Get the offset within buffer. + instanceData.bufferOffset = instanceBufferOffset; + + // Move offset past header. + instanceBufferOffset += kInstanceDataHeaderSize; + AU_ASSERT(kInstanceDataHeaderSize == sizeof(InstanceDataHeader), + "Instance header size mismatch"); + + // Move offset past layer information. + instanceBufferOffset += int(instance.materialLayers().size() * sizeof(int) * 2); + + // Add instance data to list of unique instance data. _lstInstanceData.push_back(instanceData); + + // Set in the index in the data lookup table. + instanceDataMap[instanceData] = instanceDataIndex; } else { - instanceDataIndex = instanceDataMap[instanceData]; + // We've seen this data before, so just get the index + instanceDataIndex = instanceIter->second; } - for (int i = 0; i < layerIndices.size(); i++) - { - int idx = layerIndices[i] - kBuiltInMissShaderCount; - _lstLayerData[idx].index = instanceDataIndex; - } - - // Add the instance data index to the list, i.e. one per instance in the scene. - instanceDataIndices.push_back(instanceDataIndex); + // Add the index to the look table for this instance. + _instanceDataIndexLookup[&instance] = instanceDataIndex; } + // Set the required instance buffer size. + _instanceBufferSize = instanceBufferOffset; + _layerGeometryBufferSize = layerGeomtryBufferOffset; + _transformMatrixBufferSize = + _instances.active().resources().size() * kTransformMatrixSize; + // Build the top-level acceleration structure (TLAS). - _pAccelStructure = buildTLAS(instanceDataIndices); + _pAccelStructure = buildTLAS(); // If the acceleration structure was rebuilt, then the descriptor heap, as well as the miss and // hit group shader tables must likewise be rebuilt, as they rely on the instance data. @@ -575,34 +1039,21 @@ void PTScene::updateDescriptorHeap() // Get the device. ID3D12Device5Ptr pDevice = _pRenderer->dxDevice(); - // Determine the number of texture and sampler descriptors used for all the instances' - // materials. - UINT uniqueMaterialDescriptorCount = 0; - for (PTMaterial& mtl : _materials.active().resources()) - { - uniqueMaterialDescriptorCount += mtl.descriptorCount(); - } - - UINT uniqueMaterialSamplerDescriptorCount = 0; - for (PTMaterial& mtl : _samplerLookup.unique()) - { - uniqueMaterialSamplerDescriptorCount += mtl.samplerDescriptorCount(); - } - // Create a descriptor heap for CBV/SRV/UAVs needed by shader records. Currently this means // the descriptors for the instance materials, plus the number of descriptors needed by the // renderer and environment. D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; - heapDesc.NumDescriptors = uniqueMaterialDescriptorCount + _numRendererDescriptors + + heapDesc.NumDescriptors = _numRendererDescriptors + int(_activeMaterialTextures.size()) + _pEnvironment->descriptorCount(); heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; checkHR(pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_pDescriptorHeap))); - // Create a descriptor heap for samplers needed by shader records. Currently this means - // the descriptors for the instance (and layer) material samplers. + // Create a descriptor heap for samplers needed by shader records. + // TODO: Only the default sampler currentl support, should add full sampler support to + // non-recursive renderer. D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {}; - samplerHeapDesc.NumDescriptors = uniqueMaterialSamplerDescriptorCount; + samplerHeapDesc.NumDescriptors = int(_activeMaterialSamplers.size()); samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; checkHR(pDevice->CreateDescriptorHeap( @@ -613,6 +1064,11 @@ void PTScene::updateDescriptorHeap() _isHitGroupDescriptorsDirty = true; } + // If nothing is dirty early out. + if (!_isEnvironmentDescriptorsDirty && !_materials.changedThisFrame() && + !_isHitGroupDescriptorsDirty) + return; + // Get a CPU handle to the start of the sampler descriptor heap, offset by the number of // descriptors reserved for the renderer. CD3DX12_CPU_DESCRIPTOR_HANDLE handle(_pDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); @@ -620,28 +1076,16 @@ void PTScene::updateDescriptorHeap() D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); handle.Offset(_numRendererDescriptors, handleIncrement); - // Update the environment descriptors if needed. - if (_isEnvironmentDescriptorsDirty) - { - // Create the descriptors for the environment textures. - // NOTE: This will also increment the handle past the new descriptors. - _pEnvironment->createDescriptors(handle, handleIncrement); - - // Clear the dirty flag. - _isEnvironmentDescriptorsDirty = false; - } + // Create the descriptors for the environment textures. + // NOTE: This will also increment the handle past the new descriptors. + _pEnvironment->createDescriptors(handle, handleIncrement); - // Update the hit group descriptors if needed. - if (_isHitGroupDescriptorsDirty) + // Create the descriptors for the material textures. + for (int i = 0; i < _activeMaterialTextures.size(); i++) { - // Iterate the list of active materials, creating descriptors for each as needed from the - // material. - for (PTMaterial& mtl : _materials.active().resources()) - { - // Create the descriptors for the material's textures. - // NOTE: This will also increment the handle past the new descriptors. - mtl.createDescriptors(handle, handleIncrement); - } + // Create a SRV (descriptor) on the descriptor heap for the texture. + PTImage::createSRV(*_pRenderer, _activeMaterialTextures[i], handle); + handle.Offset(handleIncrement); } // Get a CPU handle to the start of the sampler descriptor heap. @@ -650,77 +1094,137 @@ void PTScene::updateDescriptorHeap() UINT samplerHandleIncrement = _pRenderer->dxDevice()->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); - if (_isHitGroupDescriptorsDirty) + // Create the descriptors for the material samplers. + for (int i = 0; i < _activeMaterialSamplers.size(); i++) { - // Iterate the list of unique samplers, creating sampler descriptors for each as needed. - for (PTMaterial& mtl : _samplerLookup.unique()) - { - // Create the descriptors for the material's samplers. - // NOTE: This will also increment the handle past the new descriptors. - mtl.createSamplerDescriptors(samplerHandle, samplerHandleIncrement); - } - - // Clear the dirty flag. - _isHitGroupDescriptorsDirty = false; + // Add descriptor for default sampler. + PTSampler::createDescriptor(*_pRenderer, _activeMaterialSamplers[i], samplerHandle); + samplerHandle.Offset(samplerHandleIncrement); } + + // Clear the dirty flags. + _isEnvironmentDescriptorsDirty = false; + _isHitGroupDescriptorsDirty = false; } void PTScene::updateShaderTables() { - - // Create and populate the hit shader table if it doesn't exist and there are instances. - auto instanceCount = static_cast(_instances.active().count()); - // Texture and sampler descriptors for unique materials in the scene. - vector lstUniqueMaterialTextureDescriptors; vector lstUniqueMaterialSamplerDescriptors; - // Get currently active materials. - auto& activeMaterialResources = _materials.active().resources(); - - if (!_pHitGroupShaderTable && instanceCount > 0) + if (!_pHitGroupShaderTable && _lstInstanceData.size() > 0) { - // Build a list of texture and sampler GPU handles for all unique materials. + // Resize the global instance buffer if too small for all the instances. + if (_globalInstanceBuffer.size < _instanceBufferSize) { - CD3DX12_GPU_DESCRIPTOR_HANDLE handle( - _pDescriptorHeap->GetGPUDescriptorHandleForHeapStart()); - UINT handleIncrement = _pRenderer->dxDevice()->GetDescriptorHandleIncrementSize( - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + _globalInstanceBuffer = + _pRenderer->createTransferBuffer(_instanceBufferSize, "GlobalInstanceBuffer"); + } + + // Resize the global instance buffer if too small for all the instances. + if (_transformMatrixBuffer.size < _transformMatrixBufferSize) + { + _transformMatrixBuffer = _pRenderer->createTransferBuffer( + _transformMatrixBufferSize, "TransformMatrixBuffer"); + } + + // Fill in the global transform matrix buffer. + uint8_t* pMatrixDataStart = _transformMatrixBuffer.map(); + int mtxOffset = 0; + for (PTInstance& instance : _instances.active().resources()) + { + // Get pointer to instance data in buffer. + uint8_t* pInstanceData = pMatrixDataStart + mtxOffset; + float* pData = (float*)pInstanceData; + + // Copy transposed transform matrix into buffer. + mat4 matrix = transpose(instance.transform()); + ::memcpy_s(pData, kTransformMatrixSize, &matrix, kTransformMatrixSize); + + // Move offset to next matrix in buffer. + mtxOffset += kTransformMatrixSize; + } + AU_ASSERT(mtxOffset == _transformMatrixBufferSize, "Buffer size mismatch"); + // Unmap buffer. + _transformMatrixBuffer.unmap(); + + // Fill in the global instance data buffer. + uint8_t* pInstanceDataStart = _globalInstanceBuffer.map(); + int offset = 0; + for (InstanceData& instData : _lstInstanceData) + { + // Validate offset within buffer matches offset in instance lookup table. + AU_ASSERT(offset == instData.bufferOffset, "Offset incorrect"); + + // Get pointer to instance data in buffer. + uint8_t* pInstanceData = pInstanceDataStart + offset; + InstanceDataHeader* pData = (InstanceDataHeader*)pInstanceData; + + // Set material buffer offset from value in material lookup table. + pData->materialBufferOffset = instData.mtlBufferOffset; + + // Set layer count. + pData->materialLayerCount = int(instData.layers.size()); - // Offset texture handle by the number of descriptors reserved for the renderer and - // environment. - handle.Offset( - _numRendererDescriptors + _pEnvironment->descriptorCount(), handleIncrement); + // Move offset past header. + offset += kInstanceDataHeaderSize; - for (size_t i = 0; i < _materials.active().count(); i++) + // Fill in layer information. + int* pLayerData = reinterpret_cast(pInstanceDataStart + offset); + for (int j = 0; j < pData->materialLayerCount; j++) { - // Store the handle. - lstUniqueMaterialTextureDescriptors.push_back(handle); - // Increment the handle by the number of descriptors for each material. - handle.Offset(PTMaterial::descriptorCount(), handleIncrement); + // Copy layer material offset to buffer. + *pLayerData = instData.layers[j].first; + pLayerData++; + + // Copy UV offset to buffer. + *pLayerData = instData.layers[j].second; + pLayerData++; + + // Move offset past layer information. + offset += sizeof(int) * 2; } + } + + // Unmap buffer. + _globalInstanceBuffer.unmap(); - CD3DX12_GPU_DESCRIPTOR_HANDLE samplerHandle( - _pSamplerDescriptorHeap->GetGPUDescriptorHandleForHeapStart()); - UINT samplerHandleIncrement = _pRenderer->dxDevice()->GetDescriptorHandleIncrementSize( - D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + if (_layerGeometryBufferSize) + { + // Resize the global instance buffer if too small for all the instances. + if (_layerGeometryBuffer.size < _layerGeometryBufferSize) + { + _layerGeometryBuffer = _pRenderer->createTransferBuffer( + _layerGeometryBufferSize, "LayerGeometryBuffer"); + } - for (int i = 0; i < _samplerLookup.unique().size(); i++) + // Fill in the layer geometry buffer. + uint8_t* pLayerGeometryDataStart = _layerGeometryBuffer.map(); + int layerGeometryOffset = 0; + for (int i = 0; i < _layerGeometry.size(); i++) { - // Store the handle. - lstUniqueMaterialSamplerDescriptors.push_back(samplerHandle); + // Validate offset within buffer matches offset in instance lookup table. + AU_ASSERT(layerGeometryOffset == _layerGeometryOffsetLookup[_layerGeometry[i]], + "Offset incorrect"); + uint8_t* pLayerGeometryData = pLayerGeometryDataStart + layerGeometryOffset; + + size_t uvBufferSize = _layerGeometry[i]->vertexCount() * 2 * sizeof(float); - // Increment the sampler handle by the number of sample descriptors for each - // material. - samplerHandle.Offset(PTMaterial::samplerDescriptorCount(), samplerHandleIncrement); + const vector& uvs = _layerGeometry[i]->texCoords(); + AU_ASSERT(uvBufferSize <= sizeof(float) * 2 * uvs.size(), "Count mismatch"); + ::memcpy_s(pLayerGeometryData, _layerGeometryBufferSize - layerGeometryOffset, + uvs.data(), uvBufferSize); + + layerGeometryOffset += int(uvBufferSize); } + _layerGeometryBuffer.unmap(); } size_t recordStride = HitGroupShaderRecord::stride(); // Create a transfer buffer for the shader table, and map it for writing. - size_t shaderTableSize = recordStride * instanceCount; + size_t shaderTableSize = recordStride * _lstInstanceData.size(); TransferBuffer hitGroupTransferBuffer = _pRenderer->createTransferBuffer(shaderTableSize, "HitGroupShaderTable"); uint8_t* pShaderTableMappedData = hitGroupTransferBuffer.map(); @@ -729,33 +1233,21 @@ void PTScene::updateShaderTables() // renderer once upload complete. _pHitGroupShaderTable = hitGroupTransferBuffer.pGPUBuffer; - // Iterate the instance data objects, creating a hit group shader record for each one, and + // Iterate the instances, creating a hit group shader record for each one, and // copying the shader record data to the shader table. - for (int i = 0; i < _lstInstanceData.size(); i++) + for (InstanceData& instData : _lstInstanceData) { - const auto& instanceData = _lstInstanceData[i]; // Get the hit group shader ID from the material shader, which will change if the shader // library is rebuilt. - PTMaterial& instanceMtl = activeMaterialResources[instanceData.mtlIndex]; const DirectXShaderIdentifier hitGroupShaderID = - _pShaderLibrary->getShaderID(instanceMtl.shader()); - - // Lookup texture and sampler handle for instance. - CD3DX12_GPU_DESCRIPTOR_HANDLE mtlTextureHandle = - lstUniqueMaterialTextureDescriptors[instanceData.mtlIndex]; - CD3DX12_GPU_DESCRIPTOR_HANDLE mtlSamplerHandle = - lstUniqueMaterialSamplerDescriptors[_samplerLookup.getUniqueIndex( - instanceData.mtlIndex)]; - - // Shader record data includes the geometry buffers, the material constant buffer, and - // the descriptor table (offset into the SRV heap) needed for textures. - PTGeometry::GeometryBuffers geometryBuffers = instanceData.pGeometry->buffers(); - ID3D12Resource* pMaterialLayerIndexBuffer = - instanceData.pLayerIndices ? instanceData.pLayerIndices->buffer() : nullptr; - int materialLayerCount = - instanceData.pLayerIndices ? instanceData.pLayerIndices->count() : 0; - HitGroupShaderRecord record(hitGroupShaderID, geometryBuffers, instanceMtl, - pMaterialLayerIndexBuffer, mtlTextureHandle, mtlSamplerHandle, materialLayerCount); + _pShaderLibrary->getShaderID(PTShaderLibrary::kInstanceHitGroupName); + + // Shader record data includes the geometry buffers, the instance constant buffer + // offset, and opaque flag. + PTGeometry::GeometryBuffers geometryBuffers = instData.pGeometry->buffers(); + int instanceBufferOffset = instData.bufferOffset; + HitGroupShaderRecord record( + hitGroupShaderID, geometryBuffers, instanceBufferOffset, instData.isOpaque); record.copyTo(pShaderTableMappedData); pShaderTableMappedData += recordStride; } @@ -763,11 +1255,12 @@ void PTScene::updateShaderTables() // Close the shader table buffer. hitGroupTransferBuffer.unmap(); } + // Create and populate the miss shader table if necessary. if (!_pMissShaderTable) { // Calculate miss shader record count (built-ins plus all the layer material miss shaders) - _missShaderRecordCount = kBuiltInMissShaderCount + (uint32_t)_lstLayerData.size(); + _missShaderRecordCount = kBuiltInMissShaderCount; // Create a buffer for the shader table and write the shader identifiers for the miss // shaders. @@ -790,94 +1283,23 @@ void PTScene::updateShaderTables() ::memcpy_s(pShaderTableMappedData, SHADER_ID_SIZE, kNullShaderID.data(), SHADER_ID_SIZE); pShaderTableMappedData += _missShaderRecordStride; ::memcpy_s(pShaderTableMappedData, SHADER_ID_SIZE, - _pShaderLibrary->getSharedEntryPointShaderID(EntryPointTypes::kBackgroundMiss), - SHADER_ID_SIZE); - pShaderTableMappedData += _missShaderRecordStride; - ::memcpy_s(pShaderTableMappedData, SHADER_ID_SIZE, - _pShaderLibrary->getSharedEntryPointShaderID(EntryPointTypes::kRadianceMiss), - SHADER_ID_SIZE); - pShaderTableMappedData += _missShaderRecordStride; - ::memcpy_s(pShaderTableMappedData, SHADER_ID_SIZE, - _pShaderLibrary->getSharedEntryPointShaderID(EntryPointTypes::kShadowMiss), + _pShaderLibrary->getShaderID(PTShaderLibrary::kShadowMissEntryPointName), SHADER_ID_SIZE); pShaderTableMappedData += _missShaderRecordStride; - // Fill in layer material miss shaders.. - for (int i = 0; i < _lstLayerData.size(); i++) - { - // Get the layer data and the parent instance data for this layer. - auto& layerData = _lstLayerData[i]; - auto& parentInstanceData = _lstInstanceData[layerData.index]; - PTMaterial& layerMtl = activeMaterialResources[layerData.instanceData.mtlIndex]; - - // Get the material shader - auto pShader = layerMtl.shader(); - - // Create the geometry by merging the layer geometry with parent instance's geometry. - PTGeometry::GeometryBuffers geometryLayerBuffers = - parentInstanceData.pGeometry->buffers(); - if (layerData.instanceData.pGeometry) - { - // Ensure the layer geometry matches parent geometry - if (layerData.instanceData.pGeometry->vertexCount() != - parentInstanceData.pGeometry->vertexCount()) - { - // If vertex counts don't match raise an error and then fail. - AU_ERROR( - "Layer geometry %s vertex count (%d) does not match base geometry %s " - "vertex count " - "(%d) for instance at index %d", - layerData.instanceData.pGeometry->name().c_str(), - layerData.instanceData.pGeometry->vertexCount(), - parentInstanceData.pGeometry->name().c_str(), - parentInstanceData.pGeometry->vertexCount(), layerData.index); - - // This will be caught by the try statement around render(). - AU_FAIL("Invalid geometry"); - } - - // Set any UVs from the layer geometry. - if (layerData.instanceData.pGeometry->buffers().TexCoordBuffer) - { - geometryLayerBuffers.TexCoordBuffer = - layerData.instanceData.pGeometry->buffers().TexCoordBuffer; - } - - // Set any normals from the layer geometry. - if (layerData.instanceData.pGeometry->buffers().NormalBuffer) - geometryLayerBuffers.NormalBuffer = - layerData.instanceData.pGeometry->buffers().NormalBuffer; - - // Set any positions from the layer geometry. - if (layerData.instanceData.pGeometry->buffers().PositionBuffer) - geometryLayerBuffers.PositionBuffer = - layerData.instanceData.pGeometry->buffers().PositionBuffer; - } - - // Lookup texture and sampler handle for layer. - CD3DX12_GPU_DESCRIPTOR_HANDLE mtlTextureHandle = - lstUniqueMaterialTextureDescriptors[layerData.instanceData.mtlIndex]; - CD3DX12_GPU_DESCRIPTOR_HANDLE mtlSamplerHandle = - lstUniqueMaterialSamplerDescriptors[_samplerLookup.getUniqueIndex( - layerData.instanceData.mtlIndex)]; - - // Create hit group (as layer miss shader has same layout as luminance closest hit - // shader) - HitGroupShaderRecord layerRecord(_pShaderLibrary->getLayerShaderID(pShader), - geometryLayerBuffers, layerMtl, nullptr, mtlTextureHandle, mtlSamplerHandle, 0); - layerRecord.copyTo(pShaderTableMappedData); - pShaderTableMappedData += _missShaderRecordStride; - } - // Ensure we didn't over run the shader table buffer. AU_ASSERT(pShaderTableMappedData == pEndOfShaderTableMappedData, "Shader table overrun"); // Unmap the table. missShaderTableTransferBuffer.unmap(); // no HRESULT + + // Upload any transfer buffers that have changed to GPU, so they can be accessed by GPU + // commands. + _pRenderer->uploadTransferBuffers(); } } -ID3D12ResourcePtr PTScene::buildTLAS(const vector& instanceDataIndices) +ID3D12ResourcePtr PTScene::buildTLAS() { // Create and populate a buffer with instance data, if there are any instances. auto instanceCount = static_cast(_instances.active().count()); @@ -914,9 +1336,9 @@ ID3D12ResourcePtr PTScene::buildTLAS(const vector& instanceDataIndices // everywhere to be safe! D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {}; instanceDesc.AccelerationStructure = pBLAS->GetGPUVirtualAddress(); - instanceDesc.InstanceID = 0; + instanceDesc.InstanceID = instanceIndex++; instanceDesc.InstanceMask = 0xFF; - instanceDesc.InstanceContributionToHitGroupIndex = instanceDataIndices[instanceIndex++]; + instanceDesc.InstanceContributionToHitGroupIndex = _instanceDataIndexLookup[&instance]; ::memcpy_s(instanceDesc.Transform, sizeof(instanceDesc.Transform), &matrix, sizeof(instanceDesc.Transform)); instanceDesc.Flags = D3D12_RAYTRACING_INSTANCE_FLAG_NONE; diff --git a/Libraries/Aurora/Source/DirectX/PTScene.h b/Libraries/Aurora/Source/DirectX/PTScene.h index 6550120..a35881e 100644 --- a/Libraries/Aurora/Source/DirectX/PTScene.h +++ b/Libraries/Aurora/Source/DirectX/PTScene.h @@ -18,43 +18,30 @@ #include "PTGroundPlane.h" #include "PTLight.h" #include "PTMaterial.h" +#include "PTSampler.h" #include "SceneBase.h" #include BEGIN_AURORA +namespace MaterialXCodeGen +{ +class MaterialGenerator; +} // namespace MaterialXCodeGen + +// -1 is used to indicate an invalid offset in offset buffers passed to GPU. +#define kInvalidOffset -1 + // Forward declarations. class PTRenderer; class PTScene; class PTShaderLibrary; +class PTSampler; // Definition of a layer material (material+geometry) using PTLayerDefinition = pair; -// An internal implementation for the table of layer material indices. -class PTLayerIndexTable -{ -public: - /*** Lifetime Management ***/ - - PTLayerIndexTable(PTRenderer* pRenderer, const vector& indices = {}); - void set(const vector& indices); - - // The maximum number of supported material layers, must match value in PathTracingCommon.hlsl - // shader. - static const int kMaxMaterialLayers = 64; - - int count() { return _count; } - ID3D12Resource* buffer() const { return _pConstantBuffer.Get(); } - -private: - int _count = 0; - PTRenderer* _pRenderer = nullptr; - ID3D12ResourcePtr _pConstantBuffer; -}; -MAKE_AURORA_PTR(PTLayerIndexTable); - // An internal implementation for IInstance. class PTInstance : public IInstance { @@ -95,13 +82,14 @@ class PTInstance : public IInstance }; MAKE_AURORA_PTR(PTInstance); +class PTShaderOptions; + // An internal implementation for IScene. class PTScene : public SceneBase { public: /*** Lifetime Management ***/ - PTScene( - PTRenderer* pRenderer, PTShaderLibrary* pShaderLibrary, uint32_t numRendererDescriptors); + PTScene(PTRenderer* pRenderer, uint32_t numRendererDescriptors); ~PTScene() = default; /*** IScene Functions ***/ @@ -112,9 +100,10 @@ class PTScene : public SceneBase ILightPtr addLightPointer(const string& lightType) override; void update(); + void updateResources(); /*** Functions ***/ - + void computeMaterialTextureCount(int& textureCountOut, int& samplerCountOut); int instanceCount() const { return static_cast(_instances.active().count()); } PTEnvironmentPtr environment() const { return _pEnvironment; } PTGroundPlanePtr groundPlane() const { return _pGroundPlane; } @@ -127,6 +116,17 @@ class PTScene : public SceneBase void clearDesciptorHeap(); PTRenderer* renderer() { return _pRenderer; } + const TransferBuffer& globalMaterialBuffer() { return _globalMaterialBuffer; } + const TransferBuffer& globalInstanceBuffer() { return _globalInstanceBuffer; } + const TransferBuffer& layerGeometryBuffer() { return _layerGeometryBuffer; } + const TransferBuffer& transformMatrixBuffer() { return _transformMatrixBuffer; } + PTShaderLibrary& shaderLibrary() { return *_pShaderLibrary.get(); } + IMaterialPtr createMaterialPointer( + const string& materialType, const string& document, const string& name); + shared_ptr generateMaterialX( + const string& document, shared_ptr* pDefOut); + void setUnit(const string& unit); + private: /*** Private Types ***/ @@ -134,38 +134,44 @@ class PTScene : public SceneBase // of using it in a shader table. struct InstanceData { - InstanceData() : pGeometry(nullptr), mtlIndex((uint32_t)-1), pLayerIndices(nullptr) {} - - InstanceData( - const PTInstance& instance, PTLayerIndexTablePtr pLayerIndices, uint32_t mtlIndex) : - pGeometry(instance.dxGeometry()), mtlIndex(mtlIndex), pLayerIndices(pLayerIndices) + InstanceData(const PTInstance& instance) : + pGeometry(nullptr), + mtlBufferOffset((int)-1), + layers({}), + bufferOffset(-1), + isOpaque(true), + pInstance(&instance) { } bool operator==(const InstanceData& other) const { - return pGeometry == other.pGeometry && mtlIndex == other.mtlIndex && - pLayerIndices == other.pLayerIndices; + if (pGeometry != other.pGeometry || mtlBufferOffset != other.mtlBufferOffset || + layers.size() != other.layers.size()) + return false; + + for (int i = 0; i < layers.size(); i++) + { + if (layers[i].first != other.layers[i].first) + return false; + if (layers[i].second != other.layers[i].second) + return false; + } + return true; } + // Properties used to compute hash. + // Geometry for instance PTGeometryPtr pGeometry; - PTLayerIndexTablePtr pLayerIndices; - // Index into array of unique materials for scsne. - uint32_t mtlIndex; - }; - - // Structure containing the contents of material layer, and an index back to parent instance - // data. - struct LayerData - { - LayerData(const PTLayerDefinition& def, uint32_t mtlIndex, int idx = -1) : index(idx) - { - instanceData.mtlIndex = mtlIndex; - instanceData.pGeometry = def.second; - } - - InstanceData instanceData; - int index; + // Offset in global material buffer and layer geometry buffer for each layer. + vector> layers; + // Offset into global material buffer for base layer material. + int mtlBufferOffset; + + // Convenience properties, do not effect hash. + const PTInstance* pInstance; + bool isOpaque; + int bufferOffset; }; // A functor that hashes the contents of an instance, i.e. the pointers to the geometry and @@ -174,40 +180,57 @@ class PTScene : public SceneBase { size_t operator()(const InstanceData& object) const { - hash hasher1; - hash hasher2; - hash hasher3; - return hasher1(object.pGeometry.get()) ^ (hasher2(object.mtlIndex) << 1) ^ - (hasher3(object.pLayerIndices.get()) << 2); + hash geomHasher; + hash indexHasher; + size_t res = + geomHasher(object.pGeometry.get()) ^ (indexHasher(object.mtlBufferOffset) << 1); + for (int i = 0; i < object.layers.size(); i++) + { + size_t layerHash = indexHasher(object.layers[i].first) ^ + (indexHasher(object.layers[i].second) << 1); + res = res ^ (layerHash << (i + 1)); + } + return res; } }; using InstanceList = set; - using InstanceDataMap = unordered_map; - using LayerIndicesMap = map, PTLayerIndexTablePtr>; + using InstanceDataMap = unordered_map; using InstanceDataList = vector; // not needed; known to be unique - using LayerDataList = vector; /*** Private Functions ***/ void updateAccelerationStructure(); void updateDescriptorHeap(); void updateShaderTables(); - ID3D12ResourcePtr buildTLAS(const vector& instanceDataIndices); - static size_t GetSamplerHash(const PTMaterial& mtl) { return mtl.computeSamplerHash(); } + ID3D12ResourcePtr buildTLAS(); + InstanceData createInstanceData(const PTInstance& instance); /*** Private Variables ***/ - PTRenderer* _pRenderer = nullptr; - PTShaderLibrary* _pShaderLibrary = nullptr; + PTRenderer* _pRenderer = nullptr; + unique_ptr _pShaderLibrary; InstanceDataList _lstInstanceData; - UniqueHashLookup _samplerLookup; - LayerDataList _lstLayerData; + map _materialOffsetLookup; + map _instanceDataIndexLookup; + map _layerGeometryOffsetLookup; + vector _layerGeometry; + size_t _instanceBufferSize = 0; + size_t _layerGeometryBufferSize = 0; + size_t _transformMatrixBufferSize = 0; + map _materialTextureIndexLookup; + map _materialSamplerIndexLookup; + vector _activeMaterialTextures; + vector _activeMaterialSamplers; PTGroundPlanePtr _pGroundPlane; PTEnvironmentPtr _pEnvironment; uint32_t _numRendererDescriptors = 0; map> _distantLights; int _currentLightIndex = 0; + TransferBuffer _globalMaterialBuffer; + TransferBuffer _globalInstanceBuffer; + TransferBuffer _layerGeometryBuffer; + TransferBuffer _transformMatrixBuffer; /*** DirectX 12 Objects ***/ @@ -222,7 +245,15 @@ class PTScene : public SceneBase bool _isEnvironmentDescriptorsDirty = true; bool _isHitGroupDescriptorsDirty = true; std::mutex _mutex; + + map _samplerCache; + map> _imageCache; + // Code generator used to generate MaterialX files. +#if ENABLE_MATERIALX + unique_ptr _pMaterialXGenerator; +#endif }; + MAKE_AURORA_PTR(PTScene); END_AURORA diff --git a/Libraries/Aurora/Source/DirectX/PTShaderLibrary.cpp b/Libraries/Aurora/Source/DirectX/PTShaderLibrary.cpp index a50a42b..6cc1ea0 100644 --- a/Libraries/Aurora/Source/DirectX/PTShaderLibrary.cpp +++ b/Libraries/Aurora/Source/DirectX/PTShaderLibrary.cpp @@ -16,6 +16,7 @@ #include "PTShaderLibrary.h" #include "CompiledShaders/CommonShaders.h" +#include "CompiledShaders/MainEntryPoints.h" #include "PTGeometry.h" #include "PTImage.h" #include "PTMaterial.h" @@ -26,7 +27,10 @@ #include "Transpiler.h" // Development flag to enable/disable multithreaded compilation. -#define AU_DEV_MULTITHREAD_COMPILATION 1 +#define AU_DEV_MULTITHREAD_COMPILATION 0 + +// Dump individual compilation times for each shader. +#define AU_DEV_DUMP_INDIVIDUAL_COMPILATION_TIME 0 #if AU_DEV_MULTITHREAD_COMPILATION #include @@ -63,21 +67,12 @@ struct CompileJob (static_cast(static_cast(ch3)) << 24)) // Define the entry point type strings. -const string EntryPointTypes::kRadianceHit = "RADIANCE_HIT"; -const string EntryPointTypes::kLayerMiss = "LAYER_MISS"; -const string EntryPointTypes::kShadowAnyHit = "SHADOW_ANY_HIT"; -const string EntryPointTypes::kRayGen = "RAY_GEN"; -const string EntryPointTypes::kBackgroundMiss = "BACKGROUND_MISS"; -const string EntryPointTypes::kRadianceMiss = "RADIANCE_MISS"; -const string EntryPointTypes::kShadowMiss = "SHADOW_MISS"; +const string EntryPointTypes::kInitializeMaterialExport = "INITIALIZE_MATERIAL_EXPORT"; // Array of entry point names. -const vector PTShaderLibrary::DefaultEntryPoints = { EntryPointTypes::kRadianceHit, - EntryPointTypes::kShadowAnyHit, EntryPointTypes::kLayerMiss }; - -// Combine the source code for the reference and Standard Surface BSDF to produce the default built -// in shader. Use USE_REFERENCE_BSDF ifdef so that the material's BSDF can be selected via an -// option. +const vector PTShaderLibrary::DefaultEntryPoints = { + EntryPointTypes::kInitializeMaterialExport +}; // DXC include handler, used by PTShaderLibrary::compileLibrary class IncludeHandler : public IDxcIncludeHandler @@ -184,13 +179,20 @@ string PTShaderOptions::toHLSL() const // Add each option as #defines statement. for (size_t i = 0; i < _data.size(); i++) { - hlslStr += "#define " + _data[i].first + " " + to_string(_data[i].second) + "\n"; + hlslStr += "#define " + _data[i].first + " " + to_string(_data[i].second) + "\n"; } // Return HLSL. return hlslStr; } +// Define entry point name constants. +const LPWSTR PTShaderLibrary::kInstanceHitGroupName = L"InstanceClosestHitShaderGroup"; +const LPWSTR PTShaderLibrary::kInstanceClosestHitEntryPointName = L"InstanceClosestHitShader"; +const LPWSTR PTShaderLibrary::kInstanceShadowAnyHitEntryPointName = L"InstanceShadowAnyHitShader"; +const LPWSTR PTShaderLibrary::kRayGenEntryPointName = L"RayGenShader"; +const LPWSTR PTShaderLibrary::kShadowMissEntryPointName = L"ShadowMissShader"; + bool PTShaderLibrary::compileLibrary(const ComPtr& pDXCLibrary, const string source, const string& name, const string& target, const string& entryPoint, const vector>& defines, bool debug, ComPtr& pOutput, @@ -380,16 +382,19 @@ ID3D12RootSignaturePtr PTShaderLibrary::createRootSignature(const D3D12_ROOT_SIG return pSignature; } -void PTShaderLibrary::initRootSignatures() +void PTShaderLibrary::initRootSignatures(int globalTextureCount, int globalSamplerCount) { // Specify the global root signature for all shaders. This includes a global static sampler // which shaders can use by default, as well as dynamic samplers per-material. CD3DX12_DESCRIPTOR_RANGE texRange; CD3DX12_DESCRIPTOR_RANGE samplerRange; - array globalRootParameters = {}; // NOLINT(modernize-avoid-c-arrays) - globalRootParameters[0].InitAsShaderResourceView(0); // gScene: acceleration structure - globalRootParameters[1].InitAsConstants(2, 0); // sampleIndex + seedOffset + // Create the global root signature. + // Must match the root signature data setup in PTRenderer::submitRayDispatch and the GPU + // version in GlobalRootSignature.slang. + array globalRootParameters = {}; // NOLINT(modernize-avoid-c-arrays) + globalRootParameters[0].InitAsShaderResourceView(0); // gScene: acceleration structure + globalRootParameters[1].InitAsConstants(2, 0); // sampleIndex + seedOffset globalRootParameters[2].InitAsConstantBufferView(1); // gFrameData: per-frame constant buffer globalRootParameters[3].InitAsConstantBufferView(2); // gEnvironmentConstants globalRootParameters[4].InitAsShaderResourceView(1); // gEnvironmentAliasMap @@ -399,13 +404,42 @@ void PTShaderLibrary::initRootSignatures() globalRootParameters[5].InitAsDescriptorTable(_countof(globalRootRanges), globalRootRanges); globalRootParameters[6].InitAsConstantBufferView(3); // gGroundPlane globalRootParameters[7].InitAsShaderResourceView(4); // gNullScene: null acceleration structure - CD3DX12_STATIC_SAMPLER_DESC samplerDesc(0, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR); - CD3DX12_ROOT_SIGNATURE_DESC globalDesc(static_cast(globalRootParameters.size()), - globalRootParameters.data(), 1, &samplerDesc); + globalRootParameters[8].InitAsShaderResourceView( + 5); // gGlobalMaterialConstants: global material constants. + globalRootParameters[9].InitAsShaderResourceView( + 6); // gGlobalInstanceBuffer: global instance data. + globalRootParameters[10].InitAsShaderResourceView( + 7); // gLayerGeometryBuffer: UVs for geometry layers. + globalRootParameters[11].InitAsShaderResourceView( + 8); // gTransformMatrixBuffer: Tranform matrices for all instances. + + texRange.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, globalTextureCount, + 9); // gGlobalMaterialTextures: Global scene textures. + CD3DX12_DESCRIPTOR_RANGE sceneTextureRanges[] = { + texRange + }; // NOLINT(modernize-avoid-c-arrays) + globalRootParameters[12].InitAsDescriptorTable( + _countof(sceneTextureRanges), sceneTextureRanges); + + // Sampler descriptors in gGlobalMaterialSamplers array starting at register(s0) + samplerRange.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, globalSamplerCount, 0); + CD3DX12_DESCRIPTOR_RANGE globalSamplerRanges[] = { + samplerRange + }; // NOLINT(modernize-avoid-c-arrays) + globalRootParameters[13].InitAsDescriptorTable( + _countof(globalSamplerRanges), globalSamplerRanges); + + // Create the global root signature object, there are no static samplers (all the samplers + // including default sampler are stored in gSamplerArray.) + CD3DX12_ROOT_SIGNATURE_DESC globalDesc( + static_cast(globalRootParameters.size()), globalRootParameters.data(), 0, nullptr); _pGlobalRootSignature = createRootSignature(globalDesc); _pGlobalRootSignature->SetName(L"Global Root Signature"); // Specify a local root signature for the ray gen shader. + // The AOV descriptor count defined here must match the AOV RWTexture2Ds defined in + // MainEntryPoints.slang. and the descriptors uploaded to the heap in + // PTRenderer::updateOutputResources and PTRenderer::updateDenoisingResources. // NOTE: The output UAVs are typed, and therefore can't be used with a root descriptor; they // must come from a descriptor heap. CD3DX12_DESCRIPTOR_RANGE uavRange; @@ -419,95 +453,34 @@ void PTShaderLibrary::initRootSignatures() _pRayGenRootSignature = createRootSignature(rayGenDesc); _pRayGenRootSignature->SetName(L"Ray Gen Local Root Signature"); - // Start a local root signature for the radiance hit group. - // NOTE: All shaders in the hit group must have the same local root signature. - array radianceHitParameters = {}; + // Start a local root signature for the instance hit group (that is shared by all instances.) + // Must match the GPU layout defined in InstancePipelineState.slang and the HitGroupShaderRecord + // structure. + array instanceHitParameters = {}; // Geometry buffers: indices, positions, normals, tangents, and texture coordinates. - const int kGeometryBufferCount = 5; - radianceHitParameters[0].InitAsShaderResourceView(0, 1); // gIndices - radianceHitParameters[1].InitAsShaderResourceView(1, 1); // gPositions - radianceHitParameters[2].InitAsShaderResourceView(2, 1); // gNormals - radianceHitParameters[3].InitAsShaderResourceView(3, 1); // gTangents - radianceHitParameters[4].InitAsShaderResourceView(4, 1); // gTexCoords + instanceHitParameters[0].InitAsShaderResourceView(0, 1); // gIndices + instanceHitParameters[1].InitAsShaderResourceView(1, 1); // gPositions + instanceHitParameters[2].InitAsShaderResourceView(2, 1); // gNormals + instanceHitParameters[3].InitAsShaderResourceView(3, 1); // gTangents + instanceHitParameters[4].InitAsShaderResourceView(4, 1); // gTexCoords - // Constants: gHasNormals, gHasTangents gHasTexCoords, gLayerMissShaderIndex, and gIsOpaque. - radianceHitParameters[5].InitAsConstants(5, 0, 1); - - // gMaterialConstants: material data (stored after geometry data and textures). - radianceHitParameters[6].InitAsShaderResourceView( - kGeometryBufferCount + PTMaterial::descriptorCount(), 1); + // Constants from HitGroupShaderRecord: gHasNormals, gHasTangents, gHasTexCoords, + // gIsOpaque, and gInstanceBufferOffset. + instanceHitParameters[5].InitAsConstants(5, 0, 1); // gMaterialLayerIDs: indices for layer material shaders. - radianceHitParameters[7].InitAsConstantBufferView(2, 1); - - // Texture descriptors starting at register(tX, space1), where X is the number of byte address - // buffers for geometry data. - texRange.Init( - D3D12_DESCRIPTOR_RANGE_TYPE_SRV, PTMaterial::descriptorCount(), kGeometryBufferCount, 1); - CD3DX12_DESCRIPTOR_RANGE radianceHitRanges[] = { texRange }; // NOLINT(modernize-avoid-c-arrays) - radianceHitParameters[8].InitAsDescriptorTable(_countof(radianceHitRanges), radianceHitRanges); - - // Sampler descriptors starting at register(s1) - samplerRange.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, PTMaterial::samplerDescriptorCount(), 1); - CD3DX12_DESCRIPTOR_RANGE radianceHitSamplerRanges[] = { - samplerRange - }; // NOLINT(modernize-avoid-c-arrays) - radianceHitParameters[9].InitAsDescriptorTable( - _countof(radianceHitSamplerRanges), radianceHitSamplerRanges); - - CD3DX12_ROOT_SIGNATURE_DESC radianceHitDesc( - static_cast(radianceHitParameters.size()), radianceHitParameters.data()); - radianceHitDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; - _pRadianceHitRootSignature = createRootSignature(radianceHitDesc); - _pRadianceHitRootSignature->SetName(L"Radiance Hit Group Local Root Signature"); - - // Start a local root signature for the layer miss shader. - // NOTE: All shaders in the hit group must have the same local root signature. - array layerMissParameters = {}; - - // Geometry buffers. - layerMissParameters[0].InitAsShaderResourceView(0, 1); // gIndices: indices - layerMissParameters[1].InitAsShaderResourceView(1, 1); // gPositions: positions - layerMissParameters[2].InitAsShaderResourceView(2, 1); // gNormals: normals - layerMissParameters[3].InitAsShaderResourceView(3, 1); // gTangents: tangents - layerMissParameters[4].InitAsShaderResourceView(4, 1); // gTexCoords: texture coordinates - - // Constants: gHasNormals, gHasTangents gHasTexCoords, gLayerMissShaderIndex, and gIsOpaque. - layerMissParameters[5].InitAsConstants(5, 0, 1); - - // gMaterialConstants: material data (stored after geometry data and textures). - layerMissParameters[6].InitAsShaderResourceView( - kGeometryBufferCount + PTMaterial::descriptorCount(), 1); - - // gMaterialLayerIDs: indices for layer material shaders. - layerMissParameters[7].InitAsConstantBufferView(2, 1); - - // Texture descriptors starting at register(tX, space1), where X is the number of byte address - // buffers for geometry data. - texRange.Init( - D3D12_DESCRIPTOR_RANGE_TYPE_SRV, PTMaterial::descriptorCount(), kGeometryBufferCount, 1); - CD3DX12_DESCRIPTOR_RANGE layerMissRanges[] = { texRange }; // NOLINT(modernize-avoid-c-arrays) - layerMissParameters[8].InitAsDescriptorTable(_countof(layerMissRanges), layerMissRanges); - - // Sampler descriptors starting at register(s1) - samplerRange.Init( - D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, PTMaterial::samplerDescriptorCount(), 1); // Samplers - CD3DX12_DESCRIPTOR_RANGE layerMissSamplerRanges[] = { - samplerRange - }; // NOLINT(modernize-avoid-c-arrays) - layerMissParameters[9].InitAsDescriptorTable( - _countof(layerMissSamplerRanges), layerMissSamplerRanges); - - // Create layer miss root signature. - CD3DX12_ROOT_SIGNATURE_DESC layerMissDesc( - static_cast(layerMissParameters.size()), layerMissParameters.data()); - layerMissDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; - _pLayerMissRootSignature = createRootSignature(layerMissDesc); - _pLayerMissRootSignature->SetName(L"Layer Miss Local Root Signature"); + instanceHitParameters[6].InitAsConstantBufferView(1, 1); + + // Create the instance root signature. + CD3DX12_ROOT_SIGNATURE_DESC instanceHitDesc( + static_cast(instanceHitParameters.size()), instanceHitParameters.data()); + instanceHitDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; + _pInstanceHitRootSignature = createRootSignature(instanceHitDesc); + _pInstanceHitRootSignature->SetName(L"Instance Hit Group Local Root Signature"); } -DirectXShaderIdentifier PTShaderLibrary::getShaderID(const wchar_t* entryPoint) +DirectXShaderIdentifier PTShaderLibrary::getShaderID(LPWSTR name) { // Assert if a rebuild is required, as the pipeline state will be invalid. AU_ASSERT(!rebuildRequired(), @@ -516,55 +489,58 @@ DirectXShaderIdentifier PTShaderLibrary::getShaderID(const wchar_t* entryPoint) // Get the shader ID from the pipeline state. ID3D12StateObjectPropertiesPtr stateObjectProps; _pPipelineState.As(&stateObjectProps); - return stateObjectProps->GetShaderIdentifier(entryPoint); + return stateObjectProps->GetShaderIdentifier(name); } -DirectXShaderIdentifier PTShaderLibrary::getShaderID(MaterialShaderPtr pShader) +// IDxcBlock interface to static pointer. +class StaticBlob : public IDxcBlob { - auto& compiledShader = _compiledShaders[pShader->libraryIndex()]; +public: + StaticBlob(const void* data, size_t size) : _data(data), _size(size) {} + virtual void* GetBufferPointer(void) override { return const_cast(_data); } + virtual size_t GetBufferSize(void) override { return _size; } - return getShaderID(Foundation::s2w(compiledShader.exportName).c_str()); -} + // Ref counting interface not needed for static buffer. + virtual HRESULT QueryInterface(const IID&, void**) override { return S_OK; } + virtual ULONG AddRef(void) override { return 0; } + virtual ULONG Release(void) override { return 0; } + +private: + const void* _data; + size_t _size; +}; -DirectXShaderIdentifier PTShaderLibrary::getLayerShaderID(MaterialShaderPtr pShader) +void PTShaderLibrary::initialize() { - auto& compiledShader = _compiledShaders[pShader->libraryIndex()]; - return getShaderID( - Foundation::s2w(compiledShader.entryPoints[EntryPointTypes::kLayerMiss]).c_str()); -} + // Create a static block from the precomplied main entry point DXIL. + _pDefaultShaderDXIL.Attach(new StaticBlob(g_sMainEntryPointsDXIL.data(), + g_sMainEntryPointsDXIL.size() * sizeof(g_sMainEntryPointsDXIL[0]))); -DirectXShaderIdentifier PTShaderLibrary::getSharedEntryPointShaderID(const string& entryPoint) -{ - // Get the default compiled shader, which contains all the shared entry points. - const auto& defaultCompiledShader = getDefaultShader(); + // Create a string for the default shader options (remove the first line which is added by + // minify script) + _defaultOptions = CommonShaders::g_sOptions; + _defaultOptions.erase(0, _defaultOptions.find("\n") + 1); - // Get the shader ID for entry point. - return getShaderID(Foundation::s2w(defaultCompiledShader.entryPoints.at(entryPoint)).c_str()); -} + // Always use runtime compiled evaluateMaterialForShader. + // TODO: Use pre-compiled default version when only default shader is used in scene. + _options.set("RUNTIME_COMPILE_EVALUATE_MATERIAL_FUNCTION", 1); + + _optionsSource = _options.toHLSL(); -void PTShaderLibrary::initialize() -{ // Create an emptry array of Slang transpilers. _transpilerArray = {}; - // Initialize root signatures (these are shared by all shaders, and don't change.) - initRootSignatures(); - // Clear the source and built ins vector. Not strictly needed, but this function could be called // repeatedly in the future. _compiledShaders.clear(); _builtInMaterialNames = {}; - // Create source code for the default shader. - MaterialShaderSource defaultMaterialSource( - "Default", CommonShaders::g_sInitializeDefaultMaterialType); + // Create source code for the default shader, containg the main entry points used for all + // shaders.. + MaterialShaderSource defaultMaterialSource("Default", CommonShaders::g_sMainEntryPoints); // Add the shared entry points to the default shader's definitions source. - defaultMaterialSource.definitions = "#include \"BackgroundMissShader.slang\"\n"; - defaultMaterialSource.definitions += "#include \"RadianceMissShader.slang\"\n"; - defaultMaterialSource.definitions += "#include \"ShadowMissShader.slang\"\n"; - defaultMaterialSource.definitions += "#include \"RayGenShader.slang\"\n"; // Create the material definition for default shader. _builtInMaterialDefinitions[defaultMaterialSource.uniqueId] = @@ -576,8 +552,8 @@ void PTShaderLibrary::initialize() _builtInMaterialDefinitions[defaultMaterialSource.uniqueId]->getShaderDefinition(shaderDef); MaterialShaderPtr pDefaultShader = _shaderLibrary.acquire(shaderDef); - // Ensure the radiance hit entry point is compiled for default shader. - pDefaultShader->incrementRefCount(EntryPointTypes::kRadianceHit); + // Ensure the instance hit entry point is compiled for default shader. + pDefaultShader->incrementRefCount(EntryPointTypes::kInitializeMaterialExport); // Add default shader to the built-in array. _builtInMaterialNames.push_back(defaultMaterialSource.uniqueId); @@ -615,21 +591,11 @@ void PTShaderLibrary::setupCompileJobForShader(const MaterialShader& shader, Com // Add shared common code. auto& source = shader.definition().source; - // Setup the preprocessor defines to enable the entry points in the source code, based on shader - // ref-counts. - jobOut.code = "#define RADIANCE_HIT " + - to_string(shader.hasEntryPoint(EntryPointTypes::kRadianceHit)) + "\n"; - jobOut.code += "#define LAYER_MISS " + - to_string(shader.hasEntryPoint(EntryPointTypes::kLayerMiss)) + "\n\n"; - jobOut.code += "#define SHADOW_ANYHIT " + - to_string(shader.hasEntryPoint(EntryPointTypes::kShadowAnyHit)) + "\n\n"; - jobOut.code += "#define SHADOW_ANYHIT_ALWAYS_OPAQUE " + - to_string(shader.definition().isAlwaysOpaque) + "\n\n"; - - // Create the shader entry points, by replacing template tags with the shader name. - string entryPointSource = - regex_replace(CommonShaders::g_sMainEntryPoints, regex("___Material___"), source.uniqueId); - jobOut.code += entryPointSource; + jobOut.code += "#include \"Options.slang\"\n"; + + jobOut.code += "#include \"Definitions.slang\"\n\n"; + + jobOut.code += source.setup; // Get the compiled shader for this material shader. auto& compiledShader = _compiledShaders[shader.libraryIndex()]; @@ -639,26 +605,103 @@ void PTShaderLibrary::setupCompileJobForShader(const MaterialShader& shader, Com // Set the includes from the shader's source code and the options source. jobOut.includes = { - { "InitializeMaterial.slang", source.setup }, { "Options.slang", _optionsSource }, { "Definitions.slang", source.definitions }, }; // Set the entry points to be post-processed (Slang will remove the [shader] tags). - jobOut.entryPoints = { { "closesthit", - compiledShader.entryPoints[EntryPointTypes::kRadianceHit] }, - { "anyhit", compiledShader.entryPoints[EntryPointTypes::kShadowAnyHit] }, - { "miss", compiledShader.entryPoints[EntryPointTypes::kLayerMiss] } }; + jobOut.entryPoints = {}; // Set the index to map back to the compiled shader array. jobOut.index = shader.libraryIndex(); } -void PTShaderLibrary::rebuild() +void PTShaderLibrary::generateEvaluateMaterialFunction(CompileJob& job) { + // Add includes to source code. + job.code = "#include \"Material.slang\"\n"; + job.code += "#include \"Geometry.slang\"\n"; + + // Add declartion for default evaluate material function. + job.code += + "Material evaluateDefaultMaterial(ShadingData shading, int offset, out float3 " + "materialNormal, out bool isGeneratedNormal);\n"; + + // Add declaration for each of the runtime compiled evaluate material functions (these will be + // compiled seperately and linked in.) + for (int i = 1; i < _compiledShaders.size(); i++) + { + auto& compiledShader = _compiledShaders[i]; + job.code += compiledShader.setupFunctionDeclaration + ";\n"; + } + + // Add evaluate function definition. + job.code += R""""( + export Material evaluateMaterialForShader(int shaderIndex, ShadingData shading, int offset, out float3 materialNormal, + out bool isGeneratedNormal) { +)""""; + + // If there are multiple shaders to choose from, add a switch statment. + if (_compiledShaders.size() > 1) + { + job.code += R""""( + switch(shaderIndex) { + +)""""; + + // Add condition to switch statement for each shader, that calls its evaluateMaterial + // function. + for (int i = 1; i < _compiledShaders.size(); i++) + { + auto& compiledShader = _compiledShaders[i]; + if (compiledShader.valid()) + { + job.code += "\t\tcase " + to_string(i) + ":\n"; + job.code += "\t\t\treturn evaluateMaterial_" + compiledShader.id + + "(shading, offset, " + "materialNormal, " + "isGeneratedNormal);\n"; + } + } + + // Complete switch statement and call default evaluate material function in default case. + job.code += R""""( + default: + } +)""""; + } + + // Return default shader. + job.code += R""""( + return evaluateDefaultMaterial(shading, offset, materialNormal, + isGeneratedNormal); + } +)""""; + + // Set the library name to the shader name. + job.libName = "EvaluateMaterialFunction"; + + // Set the options include. + job.includes = { + { "Options.slang", _optionsSource }, + }; + + // No entry points to pre-process + job.entryPoints = {}; + + // Job index is -1 as this does not correspond to a materialShader in the shadersToLink array. + job.index = -1; +} + +void PTShaderLibrary::rebuild(int globalTextureCount, int globalSamplerCount) +{ + // Start timer. _timer.reset(); + // Initialize root signatures (these are shared by all shaders, and don't change.) + initRootSignatures(globalTextureCount, globalSamplerCount); + // Should only be called if required (rebuilding requires stalling the GPU pipeline.) AU_ASSERT(rebuildRequired(), "Rebuild not needed"); @@ -667,7 +710,7 @@ void PTShaderLibrary::rebuild() // Compile function is executed by MaterialShaderLibrary::update for any shaders that need // recompiling. - auto compileShaderFunction = [this, &compileJobs](const MaterialShader& shader) { + auto setupShaderFunction = [this, &compileJobs](const MaterialShader& shader) { // If there is no compiled shader object in the array for this shader, then create one. if (_compiledShaders.size() <= shader.libraryIndex()) { @@ -682,34 +725,13 @@ void PTShaderLibrary::rebuild() // If the compiled shader object is empty, fill in the entry points, etc. if (compiledShader.id.empty()) { - // Create entry points - _compiledShaders[shader.libraryIndex()].entryPoints = { - { EntryPointTypes::kRadianceHit, shader.id() + "RadianceHitShader" }, - { EntryPointTypes::kLayerMiss, shader.id() + "LayerMissShader" }, - { EntryPointTypes::kShadowAnyHit, shader.id() + "ShadowAnyHitShader" }, - }; - - // Default shader (at index 0) has all the shared entry points. - if (shader.libraryIndex() == 0) - { - _compiledShaders[shader.libraryIndex()] - .entryPoints[EntryPointTypes::kRadianceMiss] = "RadianceMissShader"; - _compiledShaders[shader.libraryIndex()].entryPoints[EntryPointTypes::kRayGen] = - "RayGenShader"; - _compiledShaders[shader.libraryIndex()].entryPoints[EntryPointTypes::kShadowMiss] = - "ShadowMissShader"; - _compiledShaders[shader.libraryIndex()] - .entryPoints[EntryPointTypes::kBackgroundMiss] = "BackgroundMissShader"; - } - - // Set the hit group export name from the entry point name. - _compiledShaders[shader.libraryIndex()].exportName = - _compiledShaders[shader.libraryIndex()].entryPoints[EntryPointTypes::kRadianceHit] + - "Group"; // Set the ID from the shader ID. _compiledShaders[shader.libraryIndex()].id = shader.id(); _compiledShaders[shader.libraryIndex()].hlslFilename = shader.id() + ".hlsl"; + _compiledShaders[shader.libraryIndex()].setupFunctionDeclaration = + shader.definition().source.setupFunctionDeclaration; + Foundation::sanitizeFileName(_compiledShaders[shader.libraryIndex()].hlslFilename); } @@ -725,12 +747,16 @@ void PTShaderLibrary::rebuild() // If this is the default shader (which always library index 0), add the shared entry points // (used by all shaders) - if (shader.libraryIndex() == 0) + if (shader.libraryIndex() == kDefaultShaderIndex) { - compileJobs.back().entryPoints.push_back({ "miss", "BackgroundMissShader" }); - compileJobs.back().entryPoints.push_back({ "miss", "RadianceMissShader" }); - compileJobs.back().entryPoints.push_back({ "miss", "ShadowMissShader" }); - compileJobs.back().entryPoints.push_back({ "raygeneration", "RayGenShader" }); + compileJobs.back().entryPoints.push_back( + { "miss", Foundation::w2s(kShadowMissEntryPointName) }); + compileJobs.back().entryPoints.push_back( + { "raygeneration", Foundation::w2s(kRayGenEntryPointName) }); + compileJobs.back().entryPoints.push_back( + { "closesthit", Foundation::w2s(kInstanceClosestHitEntryPointName) }); + compileJobs.back().entryPoints.push_back( + { "anyhit", Foundation::w2s(kInstanceShadowAnyHitEntryPointName) }); } return true; @@ -738,12 +764,24 @@ void PTShaderLibrary::rebuild() // Destroy function is executed by MaterialShaderLibrary::update for any shaders that need // releasing. - auto destroyShaderFunction = [this](int index) { _compiledShaders[index].reset(); }; + auto destroyShaderFunction = [this](int index) { + if (_compiledShaders.size() <= index) + return; + + _compiledShaders[index].reset(); + }; - // Run the MaterialShaderLibrary update function and return if shaders were compiled. - if (!_shaderLibrary.update(compileShaderFunction, destroyShaderFunction)) + // Run the MaterialShaderLibrary update function and return if no shaders were compiled. + if (!_shaderLibrary.update(setupShaderFunction, destroyShaderFunction)) return; + // Generate the evaluateMaterialForShader function, and add to compile jobs. + compileJobs.push_back(CompileJob(int(compileJobs.size()))); + generateEvaluateMaterialFunction(compileJobs.back()); + + // Binary for the compiled evaluateMaterial function. + ComPtr evaluateMaterialBinary; + // If shaders were rebuild we must completely rebuild the pipeline state with the following // subjects: // - Pipeline configuration: the max trace recursion depth. @@ -753,18 +791,7 @@ void PTShaderLibrary::rebuild() // - The global root signature: inputs for all shaders. // - Any local root signatures: inputs for specific shaders. // - Associations between local root signatures and specific shaders. - // - Hit groups: combinations of closest hit / any hit / intersection shaders. - - // Prepare an empty pipeline state object description. - CD3DX12_STATE_OBJECT_DESC pipelineStateDesc(D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE); - - // Create a pipeline configuration subobject, which simply specifies the recursion depth. - // NOTE: Tracing beyond this depth leads to undefined behavior, e.g. incorrect rendering or - // device removal. The shaders track the depth to ensure it is not exceeded. We allow one more - // than the maximum to so that shadow rays can still be traced at the maximum trace depth. - auto* pPipelineConfigSubobject = - pipelineStateDesc.CreateSubobject(); - pPipelineConfigSubobject->Config(PTRenderer::kMaxTraceDepth + 1); + // - Hit groups: combinations of closest hit / any hit / intersection shaders // Create the DXC library, linker, and compiler. // NOTE: DXCompiler.dll is set DELAYLOAD in the linker settings, so this will abort if DLL @@ -773,24 +800,37 @@ void PTShaderLibrary::rebuild() DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(_pDXCompiler.GetAddressOf())); DxcCreateInstance(CLSID_DxcLinker, IID_PPV_ARGS(_pDXLinker.GetAddressOf())); - // Create a DXIL library subobject. - // NOTE: Shaders are not subobjects, but the library containing them is a subobject. All of the - // shader entry points are exported by default; if only specific entry points should be - // exported, then DefineExport() on the subobject should be used. - auto* pLibrarySubObject = pipelineStateDesc.CreateSubobject(); - // As the transpiler is not thread safe we must create one for each thread. // TODO: These are around 30-40Mb each so we could look at cleaning them up once a certain // number are allocated. - for (size_t i = _transpilerArray.size(); i < compileJobs.size(); i++) + float scStart = _timer.elapsed(); + size_t transpilerCount = AU_DEV_MULTITHREAD_COMPILATION ? compileJobs.size() : 1; + for (size_t i = _transpilerArray.size(); i < transpilerCount; i++) { _transpilerArray.push_back(make_shared(CommonShaders::g_sDirectory)); } + float scEnd = _timer.elapsed(); // Transpilation and DXC Compile function is called from parallel threads. - auto compileFunc = [this](CompileJob& job) { - // Get the transpiler for this thread - auto pTranspiler = _transpilerArray[job.jobIndex]; + auto compileFunc = [this, &evaluateMaterialBinary](CompileJob& job) { + // If this is the default shader and no shader options have been changed, use the + // precompiled version. + if (job.index == kDefaultShaderIndex && _defaultOptions.compare(_optionsSource) == 0) + { + // Set the compiled shader's binary to the pre-compiled DXIL blob. + _compiledShaders[job.index].binary = _pDefaultShaderDXIL; + + // Return without running the compiler. + return; + } + +#if AU_DEV_DUMP_INDIVIDUAL_COMPILATION_TIME + float jobStart = _timer.elapsed(); +#endif + + // Get the transpiler for this thread (in non-multithread case, there is only one.) + int transpilerIndex = std::min(job.jobIndex, int(_transpilerArray.size()) - 1); + auto pTranspiler = _transpilerArray[transpilerIndex]; // If development flag set dump HLSL library to a file. if (AU_DEV_DUMP_SHADER_CODE) @@ -835,10 +875,11 @@ void PTShaderLibrary::rebuild() // If development flag set dump transpiled library to a file. if (AU_DEV_DUMP_TRANSPILED_CODE) { - if (Foundation::writeStringToFile(transpiledHLSL, job.libName)) - AU_INFO("Dumping transpiled code to:%s", job.libName.c_str()); + string transpFilename = job.libName + ".transpiled"; + if (Foundation::writeStringToFile(transpiledHLSL, transpFilename)) + AU_INFO("Dumping transpiled code to:%s", transpFilename.c_str()); else - AU_WARN("Failed to write transpiled code to:%s", job.libName.c_str()); + AU_WARN("Failed to write transpiled code to:%s", transpFilename.c_str()); } // Compile the HLSL source for this shader. @@ -865,36 +906,51 @@ void PTShaderLibrary::rebuild() } // Set the compiled binary in the compiled shader obect for this shader. - _compiledShaders[job.index].binary = compiledShader; + if (job.index < 0) + evaluateMaterialBinary = compiledShader; + else + _compiledShaders[job.index].binary = compiledShader; + +#if AU_DEV_DUMP_INDIVIDUAL_COMPILATION_TIME + float jobEnd = _timer.elapsed(); + AU_INFO("Compiled shader %s in %d ms", job.libName.c_str(), + static_cast(jobEnd - jobStart)); +#endif }; // Compile all the shaders in parallel (if AU_DEV_MULTITHREAD_COMPILATION is set.) float compStart = _timer.elapsed(); -#if AU_DEV_MULTITHREAD_COMPILATION // Set to 0 to force single threaded. +#if AU_DEV_MULTITHREAD_COMPILATION for_each(execution::par, compileJobs.begin(), compileJobs.end(), [compileFunc](CompileJob& job) { compileFunc(job); }); #else // Otherwise run in single thread. - for (auto& job : compileJobs) + for (auto& j : compileJobs) { - compileFunc(job); + compileFunc(j); } #endif float compEnd = _timer.elapsed(); // Build array of all the shader binaries (not just the ones compiled this frame) and names for // linking. - vector> compiledShaders; - vector compiledShaderNames; + vector> shadersToLink; + vector shaderNamesToLink; + + // Add the evaluate material shader blob and name to shader binaries to link. + shadersToLink.push_back(evaluateMaterialBinary); + shaderNamesToLink.push_back("EvaluateMaterialFunction"); + + // Add the binaries for all the compiled material shaders. for (int i = 0; i < _compiledShaders.size(); i++) { auto& compiledShader = _compiledShaders[i]; if (compiledShader.binary) { - compiledShaders.push_back(compiledShader.binary); + shadersToLink.push_back(compiledShader.binary); string libName = compiledShader.id + ".hlsl"; Foundation::sanitizeFileName(libName); - compiledShaderNames.push_back(libName); + shaderNamesToLink.push_back(libName); } } @@ -902,7 +958,7 @@ void PTShaderLibrary::rebuild() float linkStart = _timer.elapsed(); ComPtr linkedShader; string linkErrorMessage; - if (!linkLibrary(compiledShaders, compiledShaderNames, + if (!linkLibrary(shadersToLink, shaderNamesToLink, "lib_6_3", // Used DXIL 6.3 shader target "", // DXIL has empty entry point. false, // Don't use debug mode. @@ -918,10 +974,29 @@ void PTShaderLibrary::rebuild() } float linkEnd = _timer.elapsed(); + size_t libraryHash = Foundation::hashInts((uint32_t*)linkedShader->GetBufferPointer(), + linkedShader->GetBufferSize() / sizeof(uint32_t)); + // Create bytecode from compiled blob. D3D12_SHADER_BYTECODE shaderByteCode = CD3DX12_SHADER_BYTECODE(linkedShader->GetBufferPointer(), linkedShader->GetBufferSize()); + // Prepare an empty pipeline state object description. + CD3DX12_STATE_OBJECT_DESC pipelineStateDesc(D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE); + + // Create a pipeline configuration subobject, which simply specifies the recursion depth. + // NOTE: The recursion depth is set to 1 as we are using a non-recusive model with entire path + // traced in the ray generation shader without recursion. + auto* pPipelineConfigSubobject = + pipelineStateDesc.CreateSubobject(); + pPipelineConfigSubobject->Config(1); + + // Create a DXIL library subobject. + // NOTE: Shaders are not subobjects, but the library containing them is a subobject. All of the + // shader entry points are exported by default; if only specific entry points should be + // exported, then DefineExport() on the subobject should be used. + auto* pLibrarySubObject = pipelineStateDesc.CreateSubobject(); + // Set DXIL library object bytecode. pLibrarySubObject->SetDXILLibrary(&shaderByteCode); @@ -942,16 +1017,19 @@ void PTShaderLibrary::rebuild() shaderIdx, __uuidof(ID3D12LibraryReflection), (void**)&_pShaderLibraryReflection); // Create a shader configuration subobject, which indicates the maximum sizes of the ray payload - // (as defined in the shaders; see "RadianceRayPayload" and "LayerData") and intersection - // attributes (UV barycentric coordinates). - const unsigned int kRayPayloadSize = 48 * sizeof(float); + // (as defined in the RayTrace.slang; in the ray payload structs "InstanceRayPayload" and + // "ShadowRayPayload") and intersection attributes (UV barycentric coordinates). + // If the structures used in the shader exceed these values the result will be a rendering + // failure. + const unsigned int kRayPayloadSize = 19 * sizeof(float); const unsigned int kIntersectionSize = 2 * sizeof(float); auto* pShaderConfigSubobject = pipelineStateDesc.CreateSubobject(); pShaderConfigSubobject->Config(kRayPayloadSize, kIntersectionSize); // NOTE: The shader configuration is assumed to apply to all shaders, so it is *not* associated - // with specific shaders (which would use CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT). + // with specific shaders (which would use + // CD3DX12_SUBOBJECT_TO_EXPORTS_ASSOCIATION_SUBOBJECT). // Create the global root signature subobject. auto* pGlobalRootSignatureSubobject = @@ -966,8 +1044,26 @@ void PTShaderLibrary::rebuild() auto* pAssociationSubobject = pipelineStateDesc.CreateSubobject(); pAssociationSubobject->SetSubobjectToAssociate(*pRayGenRootSignatureSubobject); - pAssociationSubobject->AddExport( - Foundation::s2w(getDefaultShader().entryPoints[EntryPointTypes::kRayGen]).c_str()); + pAssociationSubobject->AddExport(kRayGenEntryPointName); + + // Create hit group use by all instances (required even if only has miss shader.) + auto* pInstanceClosestHitRootSigSubobject = + pipelineStateDesc.CreateSubobject(); + pInstanceClosestHitRootSigSubobject->SetRootSignature(_pInstanceHitRootSignature.Get()); + pAssociationSubobject = + pipelineStateDesc.CreateSubobject(); + pAssociationSubobject->SetSubobjectToAssociate(*pInstanceClosestHitRootSigSubobject); + pAssociationSubobject->AddExport(kInstanceClosestHitEntryPointName); + + auto* pInstanceShaderSubobject = + pipelineStateDesc.CreateSubobject(); + pInstanceShaderSubobject->SetHitGroupExport(kInstanceHitGroupName); + + pInstanceShaderSubobject->SetClosestHitShaderImport(kInstanceClosestHitEntryPointName); + pInstanceShaderSubobject->SetAnyHitShaderImport(kInstanceShadowAnyHitEntryPointName); + pInstanceShaderSubobject->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + pAssociationSubobject->AddExport(kInstanceHitGroupName); // Keep track of number of active shaders. int activeShaders = 0; @@ -979,80 +1075,8 @@ void PTShaderLibrary::rebuild() MaterialShaderPtr pShader = _shaderLibrary.get(i); if (pShader) { - // Get the compiled shader object for this shader. - auto& compiledShader = _compiledShaders[i]; - - // Ensure some of the hit points are active. - if (!pShader->hasEntryPoint(EntryPointTypes::kRadianceHit) && - !pShader->hasEntryPoint(EntryPointTypes::kShadowAnyHit) && - !pShader->hasEntryPoint(EntryPointTypes::kLayerMiss)) - { - AU_WARN("Invalid shader %s: all entry point reference counts are zero!", - pShader->id().c_str()); - } - // Increment active shader. activeShaders++; - - // Create the local root signature subobject associated with the hit group. - // All shaders are based on the same radiance hit root signature currently. - auto* pRadianceHitRootSignatureSubobject = - pipelineStateDesc.CreateSubobject(); - pRadianceHitRootSignatureSubobject->SetRootSignature(_pRadianceHitRootSignature.Get()); - pAssociationSubobject = - pipelineStateDesc - .CreateSubobject(); - pAssociationSubobject->SetSubobjectToAssociate(*pRadianceHitRootSignatureSubobject); - - // Create the radiance hit group subobject, which aggregates closest hit, any hit, and - // intersection shaders as a group. In this case, there is a closest hit shader for - // radiance rays, and an any hit shader for shadow rays. - // NOTE: Normally a hit group corresponds to a single ray type, but here we are able to - // combine code for both radiance and shadow rays into a single hit group. If we needed - // an any hit shader for radiance rays, then a separate hit group for shadow rays would - // have to be created, and referenced with an offset in the related TraceRay() calls. - - // Create hit group (required even if only has miss shader.) - auto* pShaderSubobject = - pipelineStateDesc.CreateSubobject(); - pShaderSubobject->SetHitGroupExport(Foundation::s2w(compiledShader.exportName).c_str()); - pAssociationSubobject->AddExport(Foundation::s2w(compiledShader.exportName).c_str()); - - // Setup the ClosestHitShaderImport (for radiance hit entry point) and, if needed, - // AnyHitShaderImport (for shadow hit entry point) on the hit group if - // the radiance hit reference count is non-zero. - if (pShader->refCount(EntryPointTypes::kRadianceHit) > 0) - { - pShaderSubobject->SetClosestHitShaderImport( - Foundation::s2w(compiledShader.entryPoints[EntryPointTypes::kRadianceHit]) - .c_str()); - // Only add the shadow anyhit sub-object if needed (as indicated by refcount) - if (pShader->refCount(EntryPointTypes::kShadowAnyHit) > 0) - { - pShaderSubobject->SetAnyHitShaderImport( - Foundation::s2w(compiledShader.entryPoints[EntryPointTypes::kShadowAnyHit]) - .c_str()); - } - pShaderSubobject->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); - - pAssociationSubobject->AddExport( - Foundation::s2w(compiledShader.exportName).c_str()); - } - - // Setup layer miss subobject is the layer miss reference count is non-zero. - if (pShader->refCount(EntryPointTypes::kLayerMiss) > 0) - { - auto* pLayerMissRootSignatureSubobject = - pipelineStateDesc.CreateSubobject(); - pLayerMissRootSignatureSubobject->SetRootSignature(_pLayerMissRootSignature.Get()); - pAssociationSubobject = - pipelineStateDesc - .CreateSubobject(); - pAssociationSubobject->SetSubobjectToAssociate(*pLayerMissRootSignatureSubobject); - pAssociationSubobject->AddExport( - Foundation::s2w(compiledShader.entryPoints[EntryPointTypes::kLayerMiss]) - .c_str()); - } } } @@ -1061,10 +1085,6 @@ void PTShaderLibrary::rebuild() checkHR(_pDXDevice->CreateStateObject(pipelineStateDesc, IID_PPV_ARGS(&_pPipelineState))); float plEnd = _timer.elapsed(); - // Ensure GPU material structure matches CPU version. - AU_ASSERT( - PTMaterial::validateOffsets(*this), "Mismatch between GPU and CPU material structure"); - // Get the total time taken to rebuild. // TODO: This should go into a stats property set and exposed to client properly. float elapsedMillisec = _timer.elapsed(); @@ -1072,8 +1092,10 @@ void PTShaderLibrary::rebuild() // Dump breakdown of rebuild timing. AU_INFO("Compiled %d shaders and linked %d in %d ms", compileJobs.size(), activeShaders, static_cast(elapsedMillisec)); + AU_INFO(" - Slang session creation took %d ms", static_cast(scEnd - scStart)); AU_INFO(" - Transpilation and DXC compile took %d ms", static_cast(compEnd - compStart)); - AU_INFO(" - DXC link took %d ms", static_cast(linkEnd - linkStart)); + AU_INFO( + " - DXC link took %d ms (Hash:%llx)", static_cast(linkEnd - linkStart), libraryHash); AU_INFO(" - Pipeline creation took %d ms", static_cast(plEnd - plStart)); } diff --git a/Libraries/Aurora/Source/DirectX/PTShaderLibrary.h b/Libraries/Aurora/Source/DirectX/PTShaderLibrary.h index 9573510..4421836 100644 --- a/Libraries/Aurora/Source/DirectX/PTShaderLibrary.h +++ b/Libraries/Aurora/Source/DirectX/PTShaderLibrary.h @@ -26,14 +26,15 @@ class MaterialBase; struct MaterialDefaultValues; struct CompileJob; +#define kDefaultShaderIndex 0 + // The DX data for a compiled. struct CompiledShader { ComPtr binary = nullptr; - string exportName; - string id; string hlslFilename; + string setupFunctionDeclaration; map entryPoints; void destroyBinary() { binary = nullptr; } // Reset the struct (as indices are re-used.) @@ -41,9 +42,11 @@ struct CompiledShader { destroyBinary(); entryPoints.clear(); - exportName.clear(); id.clear(); + hlslFilename.clear(); + setupFunctionDeclaration.clear(); } + bool valid() { return !id.empty(); } }; // Shader options represented as set of HLSL #define statements. @@ -73,13 +76,7 @@ class PTShaderOptions // Types of shader entry point used in the library. struct EntryPointTypes { - static const string kRadianceHit; - static const string kLayerMiss; - static const string kShadowAnyHit; - static const string kRayGen; - static const string kBackgroundMiss; - static const string kRadianceMiss; - static const string kShadowMiss; + static const string kInitializeMaterialExport; }; /** @@ -109,6 +106,12 @@ class PTShaderLibrary } ~PTShaderLibrary() {} + static const LPWSTR kInstanceHitGroupName; + static const LPWSTR kInstanceClosestHitEntryPointName; + static const LPWSTR kInstanceShadowAnyHitEntryPointName; + static const LPWSTR kRayGenEntryPointName; + static const LPWSTR kShadowMissEntryPointName; + /// Get the named built-in material shader. These are hand-coded and do not require runtime /// code generation, so this will never trigger a rebuild. MaterialShaderPtr getBuiltInShader(const string& name); @@ -143,10 +146,6 @@ class PTShaderLibrary return _pPipelineState; } - // Get the DX shader identifier for one of the shared entry points (that are used by all - // materials). entryPointType must be one of the strings in EntryPointTypes. - DirectXShaderIdentifier getSharedEntryPointShaderID(const string& entryPointType); - /// Get the names of the built-in material types. const vector& builtInMaterials() const { return _builtInMaterialNames; } @@ -154,7 +153,7 @@ class PTShaderLibrary ID3D12RootSignaturePtr globalRootSignature() const { return _pGlobalRootSignature; } /// Rebuild the shader library and its pipeline state. GPU must be idle before calling this. - void rebuild(); + void rebuild(int globalTextureCount, int globalSamplerCount); // Get the DirectX shader reflection for library. ID3D12LibraryReflection* reflection() const { return _pShaderLibraryReflection; } @@ -164,20 +163,20 @@ class PTShaderLibrary bool setOption(const string& name, int value); // Get the DirectX shader ID for the provided shader. - DirectXShaderIdentifier getShaderID(MaterialShaderPtr pShader); - // Get the DirectX shader ID for the layer shader for provided shader. - DirectXShaderIdentifier getLayerShaderID(MaterialShaderPtr pShader); + DirectXShaderIdentifier getShaderID(LPWSTR name); bool rebuildRequired() { return _shaderLibrary.rebuildRequired(); } private: + // Initialize the library. + void initialize(); + + void generateEvaluateMaterialFunction(CompileJob& jobOut); + /*** Private Functions ***/ void setupCompileJobForShader(const MaterialShader& shader, CompileJob& shadersOut); - // Initialize the library. - void initialize(); - // Compile HLSL source code using DXCompiler. bool compileLibrary(const ComPtr& pDXCLibrary, const string source, const string& name, const string& target, const string& entryPoint, @@ -196,26 +195,22 @@ class PTShaderLibrary ID3D12RootSignaturePtr createRootSignature(const D3D12_ROOT_SIGNATURE_DESC& desc); // Initialize the shared root signatures. - void initRootSignatures(); + void initRootSignatures(int globalTextureCount, int globalSamplerCount); // Remove the HLSL source for the associated index. Called by friend class MaterialShader. void removeSource(int sourceIndex); - CompiledShader& getDefaultShader() { return _compiledShaders[0]; } + CompiledShader& getDefaultShader() { return _compiledShaders[kDefaultShaderIndex]; } /*** Private Variables ***/ - // Get the shader identifier for an entry point. - DirectXShaderIdentifier getShaderID(const wchar_t* entryPoint); - vector _builtInMaterialNames; ID3D12Device5Ptr _pDXDevice; ID3D12RootSignaturePtr _pGlobalRootSignature; ID3D12RootSignaturePtr _pRayGenRootSignature; - ID3D12RootSignaturePtr _pRadianceHitRootSignature; - ID3D12RootSignaturePtr _pLayerMissRootSignature; + ID3D12RootSignaturePtr _pInstanceHitRootSignature; ID3D12StateObjectPtr _pPipelineState; @@ -234,6 +229,9 @@ class PTShaderLibrary vector> _transpilerArray; Foundation::CPUTimer _timer; + int _globalTextureCount = 0; + ComPtr _pDefaultShaderDXIL; + string _defaultOptions; }; END_AURORA diff --git a/Libraries/Aurora/Source/DirectX/Shaders/Accumulation.hlsl b/Libraries/Aurora/Source/DirectX/Shaders/Accumulation.hlsl index 87f3076..5e1cd82 100644 --- a/Libraries/Aurora/Source/DirectX/Shaders/Accumulation.hlsl +++ b/Libraries/Aurora/Source/DirectX/Shaders/Accumulation.hlsl @@ -43,11 +43,21 @@ struct Accumulation ConstantBuffer gSettings : register(b0); // A compute shader that accumulates path tracing results, optionally with denoising. +// NOTE: The indicated thread group dimensions provide good occupancy for the current code, and +// must match the values at the Dispatch() call. [RootSignature(ROOT_SIGNATURE)] -[numthreads(1, 1, 1)] +[numthreads(16, 8, 1)] void Accumulation(uint3 threadID : SV_DispatchThreadID) { - uint sampleIndex = gSettings.sampleIndex; + // Skip any shader invocation where the thread ID is outside the screen dimensions, as the + // shader will be invoked with more threads than pixels when the dimension are not evenly + // divided by the thread group dimensions. + uint2 screenDims; + gAccumulation.GetDimensions(screenDims.x, screenDims.y); + if (any(threadID.xy >= screenDims)) + { + return; + } // Get the screen coordinates (2D) from the thread ID, and the color / alpha from the result of // the most recent sample. Treat the result as the "extra" shading value, optionally used below. @@ -76,6 +86,7 @@ void Accumulation(uint3 threadID : SV_DispatchThreadID) // If the sample index is greater than zero, blend the new result color with the previous // accumulation color. + uint sampleIndex = gSettings.sampleIndex; if (sampleIndex > 0) { // Get the previous result. If it has an infinity component, then it represents an error diff --git a/Libraries/Aurora/Source/DirectX/Shaders/PostProcessing.hlsl b/Libraries/Aurora/Source/DirectX/Shaders/PostProcessing.hlsl index 953f66a..e1daf91 100644 --- a/Libraries/Aurora/Source/DirectX/Shaders/PostProcessing.hlsl +++ b/Libraries/Aurora/Source/DirectX/Shaders/PostProcessing.hlsl @@ -70,10 +70,22 @@ float normalizeDepthView(float depthView) } // A compute shader that applies post-processing to the source texture. +// NOTE: The indicated thread group dimensions provide good occupancy for the current code, and +// must match the values at the Dispatch() call. [RootSignature(ROOT_SIGNATURE)] -[numthreads(1, 1, 1)] +[numthreads(16, 8, 1)] void PostProcessing(uint3 threadID : SV_DispatchThreadID) { + // Skip any shader invocation where the thread ID is outside the screen dimensions, as the + // shader will be invoked with more threads than pixels when the dimension are not evenly + // divided by the thread group dimensions. + uint2 screenDims; + gAccumulation.GetDimensions(screenDims.x, screenDims.y); + if (any(threadID.xy >= screenDims)) + { + return; + } + // Get the screen coordinates (2D) from the thread ID. float2 coords = threadID.xy; diff --git a/Libraries/Aurora/Source/GeometryBase.cpp b/Libraries/Aurora/Source/GeometryBase.cpp index 3782b3a..c1b21b8 100644 --- a/Libraries/Aurora/Source/GeometryBase.cpp +++ b/Libraries/Aurora/Source/GeometryBase.cpp @@ -70,9 +70,9 @@ static void copyVertexChannelData(vector& dst, const AttributeDat GeometryBase::GeometryBase(const std::string& name, const GeometryDescriptor& descriptor) : _name(name) { - // Position values must be provided, there must be at least three vertices. If indices are - // provided, there must be at least three of them. All other data is optional. - AU_ASSERT(descriptor.vertexDesc.count >= 3, "Invalid vertex data"); + // If there is vertex data provided, there must be three or more vertices. + AU_ASSERT(descriptor.vertexDesc.count == 0 || descriptor.vertexDesc.count >= 3, + "Invalid vertex data"); _incomplete = !descriptor.vertexDesc.hasAttribute(Names::VertexAttributes::kPosition); diff --git a/Libraries/Aurora/Source/GeometryBase.h b/Libraries/Aurora/Source/GeometryBase.h index 1b102cd..c55c6c5 100644 --- a/Libraries/Aurora/Source/GeometryBase.h +++ b/Libraries/Aurora/Source/GeometryBase.h @@ -34,6 +34,11 @@ class GeometryBase : public IGeometry // Is the geometry complete (has posiitons can be used as primary geometry for instance.) bool isIncomplete() { return _incomplete; } + const vector& positions() { return _positions; } + const vector& normals() { return _normals; } + const vector& tangents() { return _tangents; } + const vector& texCoords() { return _texCoords; } + protected: /*** Private Functions ***/ diff --git a/Libraries/Aurora/Source/HGI/HGIMaterial.cpp b/Libraries/Aurora/Source/HGI/HGIMaterial.cpp index 438c0c9..1e94684 100644 --- a/Libraries/Aurora/Source/HGI/HGIMaterial.cpp +++ b/Libraries/Aurora/Source/HGI/HGIMaterial.cpp @@ -20,9 +20,9 @@ using namespace pxr; BEGIN_AURORA -HGIMaterial::HGIMaterial( - HGIRenderer* pRenderer, MaterialShaderPtr pShader, shared_ptr pDef) : - MaterialBase(pShader, pDef), _pRenderer(pRenderer) +HGIMaterial::HGIMaterial(HGIRenderer* pRenderer, const string& name, MaterialShaderPtr pShader, + shared_ptr pDef) : + MaterialBase(name, pShader, pDef), _pRenderer(pRenderer) { // Create buffer descriptor, passing material as initial data. HgiBufferDesc uboDesc; diff --git a/Libraries/Aurora/Source/HGI/HGIMaterial.h b/Libraries/Aurora/Source/HGI/HGIMaterial.h index 8578be3..0f10444 100644 --- a/Libraries/Aurora/Source/HGI/HGIMaterial.h +++ b/Libraries/Aurora/Source/HGI/HGIMaterial.h @@ -26,8 +26,8 @@ struct MaterialData; class HGIMaterial : public MaterialBase { public: - HGIMaterial( - HGIRenderer* pRenderer, MaterialShaderPtr pShader, shared_ptr pDef); + HGIMaterial(HGIRenderer* pRenderer, const string& name, MaterialShaderPtr pShader, + shared_ptr pDef); ~HGIMaterial() {}; void update(); diff --git a/Libraries/Aurora/Source/HGI/HGIRenderer.cpp b/Libraries/Aurora/Source/HGI/HGIRenderer.cpp index fef6c7c..09e6d39 100644 --- a/Libraries/Aurora/Source/HGI/HGIRenderer.cpp +++ b/Libraries/Aurora/Source/HGI/HGIRenderer.cpp @@ -328,7 +328,8 @@ IMaterialPtr HGIRenderer::createMaterialPointer( } // Create and return a new material object. - return make_shared(this, _pDefaultMaterialShader, _pDefaultMaterialDefinition); + return make_shared( + this, name, _pDefaultMaterialShader, _pDefaultMaterialDefinition); } IGeometryPtr HGIRenderer::createGeometryPointer( @@ -365,7 +366,7 @@ void HGIRenderer::render(uint32_t sampleStart, uint32_t sampleCount) pHGIScene->update(); // Must have opaque shadows on HGI currently. - _values.setValue(kLabelIsOpaqueShadowsEnabled, true); + _values.setValue(kLabelIsForceOpaqueShadowsEnabled, true); // Render all the samples. for (uint32_t i = 0; i < sampleCount; i++) diff --git a/Libraries/Aurora/Source/MaterialBase.cpp b/Libraries/Aurora/Source/MaterialBase.cpp index 2016ec9..8ca910a 100644 --- a/Libraries/Aurora/Source/MaterialBase.cpp +++ b/Libraries/Aurora/Source/MaterialBase.cpp @@ -17,58 +17,6 @@ BEGIN_AURORA -static PropertySetPtr g_pPropertySet; - -static PropertySetPtr propertySet() -{ - if (g_pPropertySet) - { - return g_pPropertySet; - } - - g_pPropertySet = make_shared(); - - // Images (textures) and associated transforms. - // NOTE: Default values must be nullptr, as the property set has a lifetime that could exceed - // the renderer, and images can't be retained outside their renderer. - g_pPropertySet->add("base_color_image", IImagePtr()); - g_pPropertySet->add("base_color_image_sampler", ISamplerPtr()); - g_pPropertySet->add("base_color_image_offset", vec2()); - g_pPropertySet->add("base_color_image_scale", vec2(1, 1)); - g_pPropertySet->add("base_color_image_pivot", vec2()); - g_pPropertySet->add("base_color_image_rotation", 0.0f); - g_pPropertySet->add("specular_roughness_image", IImagePtr()); - g_pPropertySet->add("specular_roughness_image_offset", vec2()); - g_pPropertySet->add("specular_roughness_image_scale", vec2(1, 1)); - g_pPropertySet->add("specular_roughness_image_pivot", vec2()); - g_pPropertySet->add("specular_roughness_image_rotation", 0.0f); - g_pPropertySet->add("specular_color_image", IImagePtr()); - g_pPropertySet->add("specular_color_image_transform", mat4()); - g_pPropertySet->add("coat_color_image", IImagePtr()); - g_pPropertySet->add("coat_color_image_transform", mat4()); - g_pPropertySet->add("coat_roughness_image", IImagePtr()); - g_pPropertySet->add("coat_roughness_image_transform", mat4()); - g_pPropertySet->add("emission_color_image", IImagePtr()); - g_pPropertySet->add("emission_color_image_offset", vec2()); - g_pPropertySet->add("emission_color_image_scale", vec2(1, 1)); - g_pPropertySet->add("emission_color_image_pivot", vec2()); - g_pPropertySet->add("emission_color_image_rotation", 0.0f); - g_pPropertySet->add("opacity_image", IImagePtr()); - g_pPropertySet->add("opacity_image_offset", vec2()); - g_pPropertySet->add("opacity_image_scale", vec2(1, 1)); - g_pPropertySet->add("opacity_image_pivot", vec2()); - g_pPropertySet->add("opacity_image_rotation", 0.0f); - g_pPropertySet->add("opacity_image_sampler", ISamplerPtr()); - g_pPropertySet->add("normal_image", IImagePtr()); - g_pPropertySet->add("normal_image_offset", vec2()); - g_pPropertySet->add("normal_image_scale", vec2(1, 1)); - g_pPropertySet->add("normal_image_pivot", vec2()); - g_pPropertySet->add("normal_image_rotation", 0.0f); - g_pPropertySet->add("displacement_image", IImagePtr()); - - return g_pPropertySet; -} - // A shortcut macro for defining a uniform buffer property definition. #define PROPERTY_DEF(NAME1, NAME2, TYPE) \ UniformBufferPropertyDefinition(NAME1, NAME2, PropertyValue::Type::TYPE) @@ -138,20 +86,22 @@ UniformBufferDefinition MaterialBase::StandardSurfaceUniforms = { }; // clang-format on -// Textures used by the built-in Standard Surface material type. -vector MaterialBase::StandardSurfaceTextures = { - "base_color_image", - "specular_roughness_image", - "opacity_image", - "normal_image", +// Textures (and samplers) used by the built-in Standard Surface material type. +vector MaterialBase::StandardSurfaceTextures = { + TextureIdentifier("base_color_image", "base_color_image_sampler"), + TextureIdentifier("specular_roughness_image", "specular_roughness_image_sampler"), + TextureIdentifier("opacity_image", "opacity_image_sampler"), + TextureIdentifier("normal_image", "normal_image_sampler"), + TextureIdentifier("emission_color_image", "emission_color_image_sampler") }; // Default values for textures used in Standard Surface material type. vector StandardSurfaceDefaultTextures = { - { "base_color_image", false }, - { "specular_roughness_image", true }, - { "opacity_image", true }, - { "normal_image", true }, + { MaterialBase::StandardSurfaceTextures[0], false }, + { MaterialBase::StandardSurfaceTextures[1], true }, + { MaterialBase::StandardSurfaceTextures[2], true }, + { MaterialBase::StandardSurfaceTextures[3], true }, + { MaterialBase::StandardSurfaceTextures[4], true }, }; // Default values for Standard Surface properties. @@ -219,11 +169,14 @@ vector StandardSurfaceDefaultProperties = { MaterialDefaultValues MaterialBase::StandardSurfaceDefaults( StandardSurfaceUniforms, StandardSurfaceDefaultProperties, StandardSurfaceDefaultTextures); -MaterialBase::MaterialBase(MaterialShaderPtr pShader, MaterialDefinitionPtr pDef) : - FixedValues(propertySet()), +MaterialBase::MaterialBase( + const string& name, MaterialShaderPtr pShader, MaterialDefinitionPtr pDef) : + _textures(pDef->defaults().textureNames), _pDef(pDef), _pShader(pShader), - _uniformBuffer(pDef->defaults().propertyDefinitions, pDef->defaults().properties) + _uniformBuffer(pDef->defaults().propertyDefinitions, pDef->defaults().properties), + _name(name) + { } @@ -233,20 +186,21 @@ void MaterialBase::updateBuiltInMaterial(MaterialBase& mtl) // opacity image, and transmission is zero. static vec3 kOpaque(1.0f); vec3 opacity = mtl.uniformBuffer().get("opacity"); - bool hasOpacityImage = mtl._values.asImage("opacity_image") ? 1 : 0; + bool hasOpacityImage = mtl.textures().getTexture("opacity_image") ? 1 : 0; float transmission = mtl.uniformBuffer().get("transmission"); mtl.setIsOpaque(opacity == kOpaque && !hasOpacityImage && transmission == 0.0f); // Set the image flags used by built-in materials. mtl.uniformBuffer().set( - "has_base_color_image", mtl._values.asImage("base_color_image") ? true : false); + "has_base_color_image", mtl.textures().getTexture("base_color_image") ? true : false); mtl.uniformBuffer().set("has_specular_roughness_image", - mtl._values.asImage("specular_roughness_image") ? true : false); + mtl.textures().getTexture("specular_roughness_image") ? true : false); + mtl.uniformBuffer().set("has_emission_color_image", + mtl.textures().getTexture("emission_color_image") ? true : false); mtl.uniformBuffer().set( - "has_emission_color_image", mtl._values.asImage("emission_color_image") ? true : false); + "has_opacity_image", mtl.textures().getTexture("opacity_image") ? true : false); mtl.uniformBuffer().set( - "has_opacity_image", mtl._values.asImage("opacity_image") ? true : false); - mtl.uniformBuffer().set("has_normal_image", mtl._values.asImage("normal_image") ? true : false); + "has_normal_image", mtl.textures().getTexture("normal_image") ? true : false); } END_AURORA diff --git a/Libraries/Aurora/Source/MaterialBase.h b/Libraries/Aurora/Source/MaterialBase.h index 13ed000..ec4a9d9 100644 --- a/Libraries/Aurora/Source/MaterialBase.h +++ b/Libraries/Aurora/Source/MaterialBase.h @@ -19,13 +19,83 @@ BEGIN_AURORA +class TextureProperties +{ +public: + struct TextureProperty + { + TextureIdentifier name; + IImagePtr image; + ISamplerPtr sampler; + }; + + TextureProperties(const vector& names) + { + for (int i = 0; i < names.size(); i++) + { + auto& name = names[i]; + _textureNameLookup[name.image] = int(_properties.size()); + if (name.hasSampler()) + _samplerNameLookup[name.sampler] = int(_properties.size()); + _properties.push_back({ name, nullptr, nullptr }); + } + } + const TextureProperty& get(int n) const { return _properties[n]; } + int findTexture(const string& name) const + { + auto iter = _textureNameLookup.find(name); + if (iter == _textureNameLookup.end()) + return -1; + return iter->second; + } + int findSampler(const string& name) const + { + auto iter = _samplerNameLookup.find(name); + if (iter == _samplerNameLookup.end()) + return -1; + return iter->second; + } + + void setTexture(const string& name, IImagePtr pImage) + { + int idx = findTexture(name); + AU_ASSERT(idx >= 0, "Invalid index"); + _properties[idx].image = pImage; + } + void setSampler(const string& name, ISamplerPtr pSampler) + { + int idx = findSampler(name); + AU_ASSERT(idx >= 0, "Invalid index"); + _properties[idx].sampler = pSampler; + } + IImagePtr getTexture(const string& name) const + { + int idx = findTexture(name); + AU_ASSERT(idx >= 0, "Invalid index"); + return _properties[idx].image; + } + ISamplerPtr getSampler(const string& name) const + { + int idx = findSampler(name); + AU_ASSERT(idx >= 0, "Invalid index"); + return _properties[idx].sampler; + } + + size_t count() const { return _properties.size(); } + +private: + vector _properties; + map _textureNameLookup; + map _samplerNameLookup; +}; + // A base class for implementations of IMaterial. -class MaterialBase : public IMaterial, public FixedValues +class MaterialBase : public IMaterial, public IValues { public: /*** Lifetime Management ***/ - MaterialBase(MaterialShaderPtr pShader, MaterialDefinitionPtr pDef); + MaterialBase(const string& name, MaterialShaderPtr pShader, MaterialDefinitionPtr pDef); /*** IMaterial Functions ***/ @@ -77,17 +147,51 @@ class MaterialBase : public IMaterial, public FixedValues } // Override default setter. - // TODO: Correctly map arbritrary texture names. void setImage(const string& name, const IImagePtr& value) override { - FixedValues::setImage(name, value); + _textures.setTexture(name, value); + _bIsDirty = true; } // Override default setter. - // TODO: Correctly map arbritrary texture names. void setSampler(const string& name, const ISamplerPtr& value) override { - FixedValues::setSampler(name, value); + _textures.setSampler(name, value); + _bIsDirty = true; + } + + void setString(const string& /* name*/, const string& /* value*/) override + { + AU_FAIL("Cannot set arbitrary string on material."); + } + IValues::Type type(const std::string& name) override + { + if (_uniformBuffer.contains(name)) + { + switch (_uniformBuffer.getType(name)) + { + case PropertyValue::Type::Bool: + return IValues ::Type::Boolean; + case PropertyValue::Type::Int: + return IValues ::Type::Int; + case PropertyValue::Type::Float: + return IValues ::Type::Float; + case PropertyValue::Type::Float2: + return IValues ::Type::Float2; + case PropertyValue::Type::Float3: + return IValues ::Type::Float3; + default: + return IValues::Type::Undefined; + } + } + else + { + if (_textures.findSampler(name) >= 0) + return IValues::Type::Sampler; + if (_textures.findTexture(name) >= 0) + return IValues::Type::Image; + } + return IValues::Type::Undefined; } // Override default clear function. @@ -96,10 +200,24 @@ class MaterialBase : public IMaterial, public FixedValues if (_uniformBuffer.contains(name)) { _uniformBuffer.reset(name); - _bIsDirty = true; - return; } - FixedValues::clearValue(name); + else + { + int idx; + idx = _textures.findTexture(name); + if (idx >= 0) + { + _textures.setTexture(name, nullptr); + } + else + { + idx = _textures.findSampler(name); + if (idx >= 0) + _textures.setSampler(name, nullptr); + } + } + _bIsDirty = true; + return; } // Is this material opaque? @@ -114,22 +232,27 @@ class MaterialBase : public IMaterial, public FixedValues // Hard-coded Standard Surface properties, textures and defaults used by built-in materials. static UniformBufferDefinition StandardSurfaceUniforms; - static vector StandardSurfaceTextures; + static vector StandardSurfaceTextures; static MaterialDefaultValues StandardSurfaceDefaults; // Update function for built-in materials. static void updateBuiltInMaterial(MaterialBase& mtl); shared_ptr definition() { return _pDef; } - const shared_ptr definition() const { return _pDef; } + const shared_ptr definition() const { return _pDef; } + + const TextureProperties& textures() { return _textures; } protected: bool _isOpaque = true; + bool _bIsDirty = true; private: MaterialDefinitionPtr _pDef; MaterialShaderPtr _pShader; UniformBuffer _uniformBuffer; + TextureProperties _textures; + string _name; }; END_AURORA diff --git a/Libraries/Aurora/Source/MaterialDefinition.h b/Libraries/Aurora/Source/MaterialDefinition.h index 37b6da3..dac6f55 100644 --- a/Libraries/Aurora/Source/MaterialDefinition.h +++ b/Libraries/Aurora/Source/MaterialDefinition.h @@ -27,7 +27,7 @@ struct MaterialDefaultValues const vector& defaultProps, const vector& defaultTxt); // The names of the textures defined for this material. - vector textureNames; + vector textureNames; // The definitions of the properties defined for this material. UniformBufferDefinition propertyDefinitions; diff --git a/Libraries/Aurora/Source/MaterialShader.cpp b/Libraries/Aurora/Source/MaterialShader.cpp index e88ff5a..188a06e 100644 --- a/Libraries/Aurora/Source/MaterialShader.cpp +++ b/Libraries/Aurora/Source/MaterialShader.cpp @@ -20,7 +20,6 @@ MaterialShader::MaterialShader(MaterialShaderLibrary* pShaderLibrary, int librar _entryPointsTypes(entryPoints), _def(def) { - // Initialize ref. counts to zero. for (int i = 0; i < entryPoints.size(); i++) { diff --git a/Libraries/Aurora/Source/MaterialShader.h b/Libraries/Aurora/Source/MaterialShader.h index 60eb10a..5137bb4 100644 --- a/Libraries/Aurora/Source/MaterialShader.h +++ b/Libraries/Aurora/Source/MaterialShader.h @@ -48,10 +48,11 @@ struct MaterialShaderSource // Reset the contents to empty strings. void reset() { - uniqueId = ""; - setup = ""; - bsdf = ""; - definitions = ""; + uniqueId = ""; + setup = ""; + bsdf = ""; + definitions = ""; + setupFunctionDeclaration = ""; } // Is there actually source associated with this material shader? @@ -68,6 +69,9 @@ struct MaterialShaderSource // Optional function definitions. string definitions; + + // Function declaration for material setup function. + string setupFunctionDeclaration; }; // The definitions of a material shader. @@ -80,7 +84,7 @@ struct MaterialShaderDefinition // Definitions of properties for this shader (without the default values.) UniformBufferDefinition propertyDefinitions; // Names of the textures defined by this shader. - vector textureNames; + vector textureNames; // Is this shader always opaque regardless of properties. bool isAlwaysOpaque; diff --git a/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.cpp b/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.cpp index 16940f4..dd7de40 100644 --- a/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.cpp +++ b/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.cpp @@ -406,8 +406,8 @@ void BSDFCodeGenerator::processInput(MaterialX::ShaderInput* input, return; } - // Compare connection name. - string variableName = connection->getFullName(); + // Get full variable name (with postfix numeral) for connection. + string variableName = connection->getVariable(); if (input->getConnection()->getNode()->getName() == "BSDFCodeGeneratorShaderGraph") { // If this a connection from the high-level shader graph, its a material input. @@ -752,7 +752,7 @@ bool BSDFCodeGenerator::materialXValueToAuroraPropertyValue( *pValueOut = valData; return true; } - AU_FAIL("Unrecognized MateriaLX value type:%s", glslType.c_str()); + AU_FAIL("Unrecognized MaterialX value type:%s", glslType.c_str()); return false; } @@ -892,7 +892,7 @@ bool BSDFCodeGenerator::generate(const string& document, BSDFCodeGenerator::Resu // Create the texture definition for this parameter. TextureDefinition tex; - tex.name = param.path; + tex.name = TextureIdentifier(param.path, param.path + "_sampler"); tex.defaultFilename = valueElem->getValue()->asA(); // Set linearize to if no color space or srgb_texture color space. @@ -949,7 +949,7 @@ bool BSDFCodeGenerator::generate(const string& document, BSDFCodeGenerator::Resu // Create the texture definition for this parameter. TextureDefinition tex; - tex.name = param.path; + tex.name = TextureIdentifier(param.path, param.path + "_sampler"); tex.defaultFilename = elem->getAttribute("value"); // Set linearize to if no color space or srgb_texture color space. @@ -1014,7 +1014,14 @@ bool BSDFCodeGenerator::generate(const string& document, BSDFCodeGenerator::Resu // Find renderable nodes. vector elements; unordered_set processedOutputs; + // TODO: This was deprecated in between MaterialX 1.38.7 and 1.38.8. Work out the reasons for + // this and move to new API. + // See + // https://github.com/AcademySoftwareFoundation/MaterialX/commit/f49359877ea41e7fc8ced47d3334e1d608b35a1a +#pragma warning(push) +#pragma warning(disable : 4996) MaterialX::findRenderableMaterialNodes(mtlxDocument, elements, false, processedOutputs); +#pragma warning(pop) // Return false if no renderable nodes. // TODO: Better error handling. @@ -1080,7 +1087,7 @@ bool BSDFCodeGenerator::generate(const string& document, BSDFCodeGenerator::Resu return false; } - // Get the node for materialX shader. + // Get the node for MaterialX shader. MaterialX::NodePtr shaderNode = shaderNodeElement->asA(); // Build a set of the BSDF inputs used by the shader nodes. These will be the outputs to the @@ -1209,7 +1216,7 @@ bool BSDFCodeGenerator::generate(const string& document, BSDFCodeGenerator::Resu pResultOut->materialStructName = sstream.str(); // Create the GLSL code for the material struct (containing all material properties). - pResultOut->materialStructCode.append("struct " + pResultOut->materialStructName + " {\n"); + pResultOut->materialStructCode = "struct " + pResultOut->materialStructName + " {\n"; pResultOut->materialStructCode.append(structProperties); pResultOut->materialStructCode.append("};\n"); @@ -1229,7 +1236,7 @@ bool BSDFCodeGenerator::generate(const string& document, BSDFCodeGenerator::Resu // Add the code for this input to the prototype. pResultOut->materialSetupCode.append( - "\tsampler2D " + _textures[i].name + "_image_parameter"); + "\tsampler2D " + _textures[i].name.image + "_image_parameter"); numParams++; } diff --git a/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.h b/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.h index ec8ad7f..22f420e 100644 --- a/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.h +++ b/Libraries/Aurora/Source/MaterialX/BSDFCodeGenerator.h @@ -13,7 +13,7 @@ #include // Forward declare MaterialX types. -namespace MaterialX_v1_38_5 +namespace MaterialX_v1_38_8 { class Document; class FileSearchPath; @@ -26,7 +26,7 @@ class UnitConverterRegistry; class UnitSystem; class ShaderNode; class TypeDesc; -} // namespace MaterialX_v1_38_5 +} // namespace MaterialX_v1_38_8 #include "Properties.h" @@ -82,7 +82,7 @@ class BSDFCodeGenerator /// function. vector materialProperties; /// \brief Vector of textures used by this material in the setup function. - vector textures; + vector textures; /// \brief Vector of BSDF inputs output by the setup function. vector bsdfInputs; /// \desc True if the setup fuction takes an integer unit parameter (index into unit names @@ -143,7 +143,7 @@ class BSDFCodeGenerator // Convert GLSL type string to Aurora type (asserts if conversion fails.) PropertyValue::Type glslTypeToAuroraType(const string glslType); - // Convert materialX value to Aurora value (asserts if conversion fails.) + // Convert MaterialX value to Aurora value (asserts if conversion fails.) bool materialXValueToAuroraValue(Value* pValueOut, shared_ptr pMtlXValue); bool materialXValueToAuroraPropertyValue( diff --git a/Libraries/Aurora/Source/MaterialX/MaterialGenerator.cpp b/Libraries/Aurora/Source/MaterialX/MaterialGenerator.cpp index 4fe9913..a7d0db3 100644 --- a/Libraries/Aurora/Source/MaterialX/MaterialGenerator.cpp +++ b/Libraries/Aurora/Source/MaterialX/MaterialGenerator.cpp @@ -148,7 +148,7 @@ shared_ptr MaterialGenerator::generate(const string& documen supportedBSDFInputs.insert(iter.first); } - // Run the code generator overriding materialX document name, so that regardless of the name in + // Run the code generator overriding MaterialX document name, so that regardless of the name in // the document, the generated HLSL is based on a hardcoded name string for caching purposes. // NOTE: This will immediate run the code generator and invoke the inputMapper and outputMapper // function populate hardcodedInputs and set modifiedNormal. @@ -171,7 +171,7 @@ shared_ptr MaterialGenerator::generate(const string& documen map textures; for (int i = 0; i < res.textures.size(); i++) { - textures[res.textures[i]] = i; + textures[res.textures[i].image] = i; } // Normal modified if the normal BSDF input is active. @@ -180,6 +180,11 @@ shared_ptr MaterialGenerator::generate(const string& documen // Create material name from the hash provided by code generator. string materialName = "MaterialX_" + Foundation::sHash(res.functionHash); + string functionInterface = "Material evaluateMaterial_" + materialName + + "(ShadingData shading, int headerOffset, out float3 " + "materialNormal, out bool " + "isGeneratedNormal)"; + // Append the material accessor functions used to read material properties from // ByteAddressBuffer. UniformBuffer mtlConstantsBuffer(res.materialProperties, res.materialPropertyDefaults); @@ -192,11 +197,8 @@ shared_ptr MaterialGenerator::generate(const string& documen generatedMtlxSetupFunction += GLSLToHLSL(res.materialSetupCode); // Create a wrapper function that is called by the ray hit entry point to initialize material. - generatedMtlxSetupFunction += - "\nMaterial initializeMaterial" - "(ShadingData shading, float3x4 objToWorld, out float3 " - "materialNormal, out bool " - "isGeneratedNormal) {\n"; + generatedMtlxSetupFunction += "\nexport " + functionInterface + " {\n"; + generatedMtlxSetupFunction += "\tint offset = headerOffset + kMaterialHeaderSize;\n"; // Fill in the global vertexData struct (used by generated code to access vertex attributes). generatedMtlxSetupFunction += "\tvertexData = shading;\n"; @@ -208,81 +210,16 @@ shared_ptr MaterialGenerator::generate(const string& documen // Reset material to default values. generatedMtlxSetupFunction += "\tMaterial material = defaultMaterial();\n"; - // Add code to create local texture variables for the textures used by generated code, based on - // hard-code texture names from Standard Surface. - // TODO: Data-driven textures that are not mapped to hard coded texture names. - vector textureVars; - if (res.textures.size() >= 1) - { - // Map first texture to the base_color_image and sampler. - generatedMtlxSetupFunction += - "\tsampler2D base_color_image = createSampler2D(gBaseColorTexture, " - "gBaseColorSampler);\n"; - - // Add to the texture array. - TextureDefinition txtDef = res.textureDefaults[0]; - txtDef.name = "base_color_image"; - textureVars.push_back(txtDef); - } - - if (res.textures.size() >= 2) - { - // Map second texture to the opacity_image and sampler. - generatedMtlxSetupFunction += - "\tsampler2D opacity_image = createSampler2D(gOpacityTexture, " - "gOpacitySampler);\n"; - - // Add to the texture array. - TextureDefinition txtDef = res.textureDefaults[1]; - txtDef.name = "opacity_image"; - textureVars.push_back(txtDef); - } - - if (res.textures.size() >= 3) + // Create sampler structs for each of the textures in the materialX material. + vector samplerNames; + for (int i = 0; i < res.textureDefaults.size(); i++) { - // Map third texture to the normal_image and the default sampler. - generatedMtlxSetupFunction += - "\tsampler2D normal_image = createSampler2D(gNormalTexture, " - "gDefaultSampler);\n"; - - // Add to the texture array. - TextureDefinition txtDef = res.textureDefaults[2]; - txtDef.name = "normal_image"; - textureVars.push_back(txtDef); - } - - if (res.textures.size() >= 4) - { - // Map fourth texture to the specular_roughness_image and the default sampler. - generatedMtlxSetupFunction += - "\tsampler2D specular_roughness_image = createSampler2D(gSpecularRoughnessTexture, " - "gDefaultSampler);\n"; - - // Add to the texture array. - TextureDefinition txtDef = res.textureDefaults[3]; - txtDef.name = "specular_roughness_image"; - textureVars.push_back(txtDef); - } - - if (res.textures.size() >= 5) - { - // Map fifth texture to the emission_color_image and the default sampler. - generatedMtlxSetupFunction += - "\tsampler2D emission_color_image = createSampler2D(gEmissionColorTexture, " - "gDefaultSampler);\n"; - - // Add to the texture array. - TextureDefinition txtDef = res.textureDefaults[4]; - txtDef.name = "emission_color_image"; - textureVars.push_back(txtDef); - } - - // Map any additional textures to base color image. - for (size_t i = textureVars.size(); i < res.textures.size(); i++) - { - TextureDefinition txtDef = textureVars[0]; - txtDef.name = "base_color_image"; - textureVars.push_back(txtDef); + string samplerName = "sampler" + to_string(i); + // Create sampler from Nth texture and sampler. + generatedMtlxSetupFunction += "\tsampler2D " + samplerName + + " = createSampler2D( materialTexture(headerOffset, " + to_string(i) + + "), materialSampler(headerOffset, " + to_string(i) + ") );\n"; + samplerNames.push_back(samplerName); } // Use the define DISTANCE_UNIT which is set in the shader library options. @@ -296,7 +233,7 @@ shared_ptr MaterialGenerator::generate(const string& documen { generatedMtlxSetupFunction += "\tsetupMaterialStruct." + res.materialProperties[i].variableName + " = " + res.materialStructName + "_" + - res.materialProperties[i].variableName + "(gMaterialConstants);\n"; + res.materialProperties[i].variableName + "(gGlobalMaterialConstants, offset);\n"; ; } @@ -307,13 +244,13 @@ shared_ptr MaterialGenerator::generate(const string& documen generatedMtlxSetupFunction += "setupMaterialStruct"; // Add code for all the texture arguments. - for (int i = 0; i < textureVars.size(); i++) + for (int i = 0; i < samplerNames.size(); i++) { // Add texture name. generatedMtlxSetupFunction += ",\n"; // Add texture name. - generatedMtlxSetupFunction += textureVars[i].name; + generatedMtlxSetupFunction += samplerNames[i]; } // Add distance unit parameter, if used. @@ -350,9 +287,9 @@ shared_ptr MaterialGenerator::generate(const string& documen generatedMtlxSetupFunction += ");\n"; // Replace overwrite metal color with base color. - // TODO: Metal color is not supported by materialX and not in Standard Surface definition. So + // TODO: Metal color is not supported by MaterialX and not in Standard Surface definition. So // maybe remove it? - generatedMtlxSetupFunction += "material.metalColor = material.baseColor;"; + generatedMtlxSetupFunction += "material.metalColor = material.baseColor;\n"; // Return the generated material struct. generatedMtlxSetupFunction += "\treturn material;\n"; @@ -371,16 +308,21 @@ shared_ptr MaterialGenerator::generate(const string& documen source.definitions = "#include \"GLSLToHLSL.slang\"\n"; source.definitions += "#include \"MaterialXCommon.slang\"\n"; source.definitions += (GLSLToHLSL(definitionGLSL)); + source.definitions += "#include \"GlobalPipelineState.slang\"\n"; + source.definitions += "#include \"GlobalBufferAccessors.slang\"\n"; + source.definitions += "#include \"Material.slang\"\n"; + + source.setupFunctionDeclaration = functionInterface; // Create the default values object from the generated properties and textures. MaterialDefaultValues defaults( - res.materialProperties, res.materialPropertyDefaults, textureVars); + res.materialProperties, res.materialPropertyDefaults, res.textureDefaults); // Material is opaque if neither opacity or transmission input is used. bool isOpaque = bsdfInputs.find("opacity") == bsdfInputs.end() && bsdfInputs.find("transmission") == bsdfInputs.end(); - // Create update function which just sets opacity flag based on materialX inputs. + // Create update function which just sets opacity flag based on MaterialX inputs. function updateFunc = [isOpaque](MaterialBase& mtl) { mtl.setIsOpaque(isOpaque); }; diff --git a/Libraries/Aurora/Source/RendererBase.cpp b/Libraries/Aurora/Source/RendererBase.cpp index bb36954..34ce79c 100644 --- a/Libraries/Aurora/Source/RendererBase.cpp +++ b/Libraries/Aurora/Source/RendererBase.cpp @@ -45,7 +45,7 @@ static PropertySetPtr propertySet() gpPropertySet->add(kLabelImportanceSamplingMode, kImportanceSamplingModeMIS); gpPropertySet->add(kLabelIsFlipImageYEnabled, true); gpPropertySet->add(kLabelIsReferenceBSDFEnabled, false); - gpPropertySet->add(kLabelIsOpaqueShadowsEnabled, false); + gpPropertySet->add(kLabelIsForceOpaqueShadowsEnabled, false); return gpPropertySet; } @@ -160,12 +160,13 @@ bool RendererBase::updateFrameDataGPUStruct(FrameData* pStaging) // Copy the current light buffer for the scene to this frame's light data. memcpy(&frameData.lights, &_pScene->lights(), sizeof(frameData.lights)); - int debugMode = _values.asInt(kLabelDebugMode); - int traceDepth = _values.asInt(kLabelTraceDepth); - traceDepth = glm::max(1, glm::min(kMaxTraceDepth, traceDepth)); - frameData.traceDepth = traceDepth; - frameData.isDenoisingEnabled = _values.asBoolean(kLabelIsDenoisingEnabled) ? 1 : 0; - frameData.isOpaqueShadowsEnabled = _values.asBoolean(kLabelIsOpaqueShadowsEnabled) ? 1 : 0; + int debugMode = _values.asInt(kLabelDebugMode); + int traceDepth = _values.asInt(kLabelTraceDepth); + traceDepth = glm::max(1, glm::min(kMaxTraceDepth, traceDepth)); + frameData.traceDepth = traceDepth; + frameData.isDenoisingEnabled = _values.asBoolean(kLabelIsDenoisingEnabled) ? 1 : 0; + frameData.isForceOpaqueShadowsEnabled = + _values.asBoolean(kLabelIsForceOpaqueShadowsEnabled) ? 1 : 0; frameData.isDiffuseOnlyEnabled = _values.asBoolean(kLabelIsDiffuseOnlyEnabled) ? 1 : 0; frameData.maxLuminance = _values.asFloat(kLabelMaxLuminance); frameData.isDisplayErrorsEnabled = debugMode == kDebugModeErrors ? 1 : 0; diff --git a/Libraries/Aurora/Source/RendererBase.h b/Libraries/Aurora/Source/RendererBase.h index 26cbb0e..1f49c99 100644 --- a/Libraries/Aurora/Source/RendererBase.h +++ b/Libraries/Aurora/Source/RendererBase.h @@ -22,21 +22,21 @@ BEGIN_AURORA class SceneBase; // Property names as constants. -static const string kLabelIsResetHistoryEnabled = "isResetHistoryEnabled"; -static const string kLabelIsDenoisingEnabled = "isDenoisingEnabled"; -static const string kLabelIsDiffuseOnlyEnabled = "isDiffuseOnlyEnabled"; -static const string kLabelDebugMode = "debugMode"; -static const string kLabelMaxLuminance = "maxLuminance"; -static const string kLabelTraceDepth = "traceDepth"; -static const string kLabelIsToneMappingEnabled = "isToneMappingEnabled"; -static const string kLabelIsGammaCorrectionEnabled = "isGammaCorrectionEnabled"; -static const string kLabelIsAlphaEnabled = "alphaEnabled"; -static const string kLabelBrightness = "brightness"; -static const string kLabelUnits = "units"; -static const string kLabelImportanceSamplingMode = "importanceSamplingMode"; -static const string kLabelIsFlipImageYEnabled = "isFlipImageYEnabled"; -static const string kLabelIsReferenceBSDFEnabled = "isReferenceBSDFEnabled"; -static const string kLabelIsOpaqueShadowsEnabled = "isOpaqueShadowsEnabled"; +static const string kLabelIsResetHistoryEnabled = "isResetHistoryEnabled"; +static const string kLabelIsDenoisingEnabled = "isDenoisingEnabled"; +static const string kLabelIsDiffuseOnlyEnabled = "isDiffuseOnlyEnabled"; +static const string kLabelDebugMode = "debugMode"; +static const string kLabelMaxLuminance = "maxLuminance"; +static const string kLabelTraceDepth = "traceDepth"; +static const string kLabelIsToneMappingEnabled = "isToneMappingEnabled"; +static const string kLabelIsGammaCorrectionEnabled = "isGammaCorrectionEnabled"; +static const string kLabelIsAlphaEnabled = "alphaEnabled"; +static const string kLabelBrightness = "brightness"; +static const string kLabelUnits = "units"; +static const string kLabelImportanceSamplingMode = "importanceSamplingMode"; +static const string kLabelIsFlipImageYEnabled = "isFlipImageYEnabled"; +static const string kLabelIsReferenceBSDFEnabled = "isReferenceBSDFEnabled"; +static const string kLabelIsForceOpaqueShadowsEnabled = "isForceOpaqueShadowsEnabled"; // The debug modes include: // - 0 Output (accumulation) @@ -130,7 +130,7 @@ class RendererBase : public IRenderer, public FixedValues // Whether shadow evaluation should treat all objects as opaque, as a performance // optimization. - int isOpaqueShadowsEnabled; + int isForceOpaqueShadowsEnabled; // Whether to write the NDC depth result to an output texture. int isDepthNDCEnabled; diff --git a/Libraries/Aurora/Source/Resources.cpp b/Libraries/Aurora/Source/Resources.cpp index d4e8c83..ef46650 100644 --- a/Libraries/Aurora/Source/Resources.cpp +++ b/Libraries/Aurora/Source/Resources.cpp @@ -187,6 +187,8 @@ ImageResource::ImageResource(const Aurora::Path& path, const ResourceMap& contai { } +vector ImageResource::_defaultImageData = { 0x00000000 }; + void ImageResource::createResource() { // Ensure descriptor as been set. @@ -195,26 +197,48 @@ void ImageResource::createResource() // Setup InitData object for image. // TODO: This is legacy struct, replace with descriptor in pointer inteface. IImage::InitData initData; - initData.format = _descriptor.format; - initData.width = _descriptor.width; - initData.height = _descriptor.height; initData.isEnvironment = _descriptor.isEnvironment; initData.linearize = _descriptor.linearize; initData.name = path(); - // Use the getPixelData callback to get the contents of the image. - PixelData pixelData; - _descriptor.getPixelData(pixelData, ivec2(0, 0), ivec2(_descriptor.width, _descriptor.height)); - initData.pImageData = pixelData.address; + // Use the getData callback to get the contents of the image. + + vector> buffers; + + AllocateBufferFunction allocFunc = [&buffers](size_t numBytes) { + buffers.push_back(vector(numBytes)); + buffers.back().resize(numBytes); + return buffers.back().data(); + }; + ImageData pixelData; + if (!_descriptor.getData(pixelData, allocFunc)) + { + // If the getData function failed, print warning. + AU_WARN("Failed to get data for image resource %s", path().c_str()); + + // Then fill in with 1x1 default black image. + initData.format = ImageFormat::Integer_RGBA; + initData.width = 1; + initData.height = 1; + initData.pImageData = _defaultImageData.data(); + } + else + { + initData.format = pixelData.format; + initData.width = pixelData.dimensions.x; + initData.height = pixelData.dimensions.y; + initData.pImageData = pixelData.pPixelBuffer; + if (pixelData.overrideLinearize) + initData.linearize = pixelData.linearize; + } // Create the actual renderer image resource. _resource = _pRenderer->createImagePointer(initData); - // If we have a completion callback, execute that as pixels ahve been passed to renderer and can + // If we have a completion callback, execute that as pixels have been passed to renderer and can // be deleted. - if (_descriptor.pixelUpdateComplete) - _descriptor.pixelUpdateComplete( - pixelData, ivec2(0, 0), ivec2(_descriptor.width, _descriptor.height)); + if (_descriptor.updateComplete) + _descriptor.updateComplete(); } void ImageResource::destroyResource() diff --git a/Libraries/Aurora/Source/Resources.h b/Libraries/Aurora/Source/Resources.h index 927a860..6bd4b2e 100644 --- a/Libraries/Aurora/Source/Resources.h +++ b/Libraries/Aurora/Source/Resources.h @@ -127,6 +127,7 @@ class ImageResource : public ResourceStub IRenderer* _pRenderer; ImageDescriptor _descriptor; bool _hasDescriptor = false; + static vector _defaultImageData; }; /// ResourceStub sub-class that implements a renderer sampler resource. diff --git a/Libraries/Aurora/Source/SceneBase.cpp b/Libraries/Aurora/Source/SceneBase.cpp index e71f8bd..ba72178 100644 --- a/Libraries/Aurora/Source/SceneBase.cpp +++ b/Libraries/Aurora/Source/SceneBase.cpp @@ -25,6 +25,7 @@ Path SceneBase::kDefaultEnvironmentName = "__AuroraDefaultEnvironment"; Path SceneBase::kDefaultMaterialName = "__AuroraDefaultMaterial"; Path SceneBase::kDefaultGeometryName = "__AuroraDefaultInstance"; Path SceneBase::kDefaultInstanceName = "__AuroraDefaultGeometry"; +Path SceneBase::kDefaultImageName = "__AuroraDefaultImage"; RendererBase* SceneBase::rendererBase() { @@ -33,6 +34,13 @@ RendererBase* SceneBase::rendererBase() void SceneBase::createDefaultResources() { + if (_resources.find(kDefaultEnvironmentName) != _resources.end()) + _resources.erase(kDefaultEnvironmentName); + if (_resources.find(kDefaultInstanceName) != _resources.end()) + _resources.erase(kDefaultInstanceName); + if (_resources.find(kDefaultImageName) != _resources.end()) + _resources.erase(kDefaultImageName); + // Create a default environment resource, and set it as current environment. _pDefaultEnvironmentResource = make_shared( kDefaultEnvironmentName, _resources, _environments, _pRenderer); @@ -59,7 +67,6 @@ void SceneBase::createDefaultResources() _resources[kDefaultMaterialName] = _pDefaultMaterialResource; _pDefaultMaterialResource->incrementPermanentRefCount(); - const Path kDefaultQuadPath = "DefaultQuadGeometry"; GeometryDescriptor geomDesc; geomDesc.type = PrimitiveType::Triangles; geomDesc.vertexDesc.attributes[Names::VertexAttributes::kPosition] = AttributeFormat::Float3; @@ -92,6 +99,23 @@ void SceneBase::createDefaultResources() _pDefaultInstanceResource->setProperties( { { Names::InstanceProperties::kGeometry, kDefaultGeometryName } }); _resources[kDefaultInstanceName] = _pDefaultInstanceResource; + + ImageDescriptor imageDesc; + imageDesc.isEnvironment = false; + imageDesc.linearize = true; + imageDesc.getData = [this](ImageData& dataOut, AllocateBufferFunction alloc) { + dataOut.pPixelBuffer = _defaultImagePixels.data(); + dataOut.bufferSize = _defaultImagePixels.size(); + dataOut.dimensions = { 2, 2 }; + dataOut.format = ImageFormat::Integer_RGBA; + return true; + }; + + _pDefaultImageResource = + make_shared(kDefaultImageName, _resources, _images, _pRenderer); + _pDefaultImageResource->setDescriptor(imageDesc); + _resources[kDefaultImageName] = _pDefaultImageResource; + _pDefaultImageResource->incrementPermanentRefCount(); } SceneBase::~SceneBase() @@ -183,42 +207,48 @@ void SceneBase::setImageFromFilePath( if (imagePath.empty()) imagePath = atPath; - // Lookup in loaded images map, return - shared_ptr pImageAsset; - auto iter = _loadedImages.find(filePath); - if (iter != _loadedImages.end()) - { - pImageAsset = iter->second; - } - else - { - pImageAsset = rendererBase()->assetManager()->acquireImage(imagePath); - if (!pImageAsset) - { - pImageAsset = _pErrorImageData; - AU_ERROR("Failed to load image %s", imagePath.c_str()); - } - _loadedImages[filePath] = pImageAsset; - } - Aurora::ImageDescriptor imageDesc; - imageDesc.format = pImageAsset->data.format; - imageDesc.width = pImageAsset->data.width; - imageDesc.height = pImageAsset->data.height; - imageDesc.linearize = forceLinear ? false : pImageAsset->data.linearize; + imageDesc.linearize = forceLinear; imageDesc.isEnvironment = isEnvironment; // Setup pixel data callback to get address and size from buffer from cache entry. - string pathToLoad = filePath; - imageDesc.getPixelData = [this, pathToLoad]( - Aurora::PixelData& dataOut, glm::ivec2, glm::ivec2) { - shared_ptr pAsset = _loadedImages[pathToLoad]; - dataOut.address = pAsset->pixels.get(); - dataOut.size = pAsset->sizeBytes; + string pathToLoad = filePath; + imageDesc.getData = [this, forceLinear, pathToLoad]( + Aurora::ImageData& dataOut, AllocateBufferFunction /* alloc*/) { + shared_ptr pImageAsset; + + // Lookup in loaded images map, return + auto iter = _loadedImages.find(pathToLoad); + if (iter != _loadedImages.end()) + { + pImageAsset = iter->second; + } + else + { + pImageAsset = rendererBase()->assetManager()->acquireImage(pathToLoad); + + if (!pImageAsset) + { + pImageAsset = _pErrorImageData; + AU_ERROR("Failed to load image %s", pathToLoad.c_str()); + } + _loadedImages[pathToLoad] = pImageAsset; + } + + // Fill in output data. + dataOut.format = pImageAsset->data.format; + dataOut.dimensions = { pImageAsset->data.width, pImageAsset->data.height }; + dataOut.pPixelBuffer = pImageAsset->pixels.get(); + dataOut.bufferSize = pImageAsset->sizeBytes; + + // Override the linearize value with the one inferred from the image file, unless client + // passed forceLinear flag. + dataOut.overrideLinearize = !forceLinear; + dataOut.linearize = pImageAsset->data.linearize; + return true; }; - imageDesc.pixelUpdateComplete = [this, pathToLoad](const Aurora::PixelData&, glm::ivec2, - glm::ivec2) { _loadedImages.erase(pathToLoad); }; + imageDesc.updateComplete = [this, pathToLoad]() { _loadedImages.erase(pathToLoad); }; setImageDescriptor(atPath, imageDesc); } diff --git a/Libraries/Aurora/Source/SceneBase.h b/Libraries/Aurora/Source/SceneBase.h index da14967..d73ccc9 100644 --- a/Libraries/Aurora/Source/SceneBase.h +++ b/Libraries/Aurora/Source/SceneBase.h @@ -104,12 +104,14 @@ class SceneBase : public IScene LightData& lights() { return _lights; } + // Create the default resources for the scene (default material, instance, image, etc.) + // NOTE: Must be called *after* scene is attached to renderer via setScene. + void createDefaultResources(); + protected: // Is the path a valid resource path. virtual bool isPathValid(const Path& path); - void createDefaultResources(); - template shared_ptr getResource(const Path& path) { @@ -140,6 +142,7 @@ class SceneBase : public IScene shared_ptr _pDefaultEnvironmentResource; shared_ptr _pDefaultMaterialResource; shared_ptr _pDefaultInstanceResource; + shared_ptr _pDefaultImageResource; // Images loaded with setImageFromFilePath. map> _loadedImages; @@ -148,8 +151,10 @@ class SceneBase : public IScene static Path kDefaultMaterialName; static Path kDefaultGeometryName; static Path kDefaultInstanceName; + static Path kDefaultImageName; vector _defaultGeometryVerts = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; vector _defaultGeometryIndices = { 0, 1, 2 }; + vector _defaultImagePixels = { -1, -1, -1, -1 }; shared_ptr _pErrorImageData; }; diff --git a/Libraries/Aurora/Source/Shaders/BSDFCommon.slang b/Libraries/Aurora/Source/Shaders/BSDFCommon.slang index 7039864..4aabda9 100644 --- a/Libraries/Aurora/Source/Shaders/BSDFCommon.slang +++ b/Libraries/Aurora/Source/Shaders/BSDFCommon.slang @@ -33,6 +33,11 @@ #define SHEEN_LOBE 6 // sheen_brdf #define DIFFUSE_LOBE 7 // diffuse_brdf +// Ray types (in addition to the lobe IDs defined above.) +#define NO_SECONDARY_RAY -1 +#define CAMERA_RAY -2 +#define GROUND_PLANE_REFLECTION_RAY -3 + // ================================================================================================= // Vector Conventions // ================================================================================================= diff --git a/Libraries/Aurora/Source/Shaders/BackgroundMissShader.slang b/Libraries/Aurora/Source/Shaders/BackgroundMissShader.slang deleted file mode 100644 index d504cdb..0000000 --- a/Libraries/Aurora/Source/Shaders/BackgroundMissShader.slang +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "PathTracingCommon.slang" - -// The background miss shader, which evaluates the environment as a background. This is typically -// done from primary, transmission, and transparency rays. -[shader("miss")] -void BackgroundMissShader(inout RayPayload rayPayload) -{ - // Initialize the radiance ray payload for a miss. - rayPayload.radianceRay.clear(); - - // Evaluate the environment, as a background. - Environment environment = prepareEnvironmentValues(); - float3 color = evaluateEnvironment(environment, WorldRayDirection(), true); - - // Store the environment color. - // NOTE: The miss result will not be denoised, so it is included in the "extra" shading. - rayPayload.radianceRay.color = color; - rayPayload.radianceRay.extra = color; -} \ No newline at end of file diff --git a/Libraries/Aurora/Source/Shaders/DefaultMaterial.slang b/Libraries/Aurora/Source/Shaders/DefaultMaterial.slang new file mode 100644 index 0000000..66a2723 --- /dev/null +++ b/Libraries/Aurora/Source/Shaders/DefaultMaterial.slang @@ -0,0 +1,232 @@ +// Copyright 2023 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __DEFAULTMATERIAL_H__ +#define __DEFAULTMATERIAL_H__ + +#include "Geometry.slang" +#include "Material.slang" +#include "GlobalBufferAccessors.slang" + +#if DIRECTX +// DirectX backend uses the autogenerated byte-array accessors. +#include "DefaultMaterialUniformAccessors.slang" +#else + +// HGI backend uses the autogenerated uniform buffers. +#include "DefaultMaterialUniformBuffer.slang" +#endif + +#if DIRECTX + +// Indices of default material textures. Must match order of StandardSurfaceTextures array in MaterialBase.cpp. +#define BASE_COLOR_TEXTURE_INDEX 0 +#define SPECULAR_ROUGHNESS_TEXTURE_INDEX 1 +#define OPACITY_TEXTURE_INDEX 2 +#define NORMAL_TEXTURE_INDEX 3 +#define EMISSION_TEXTURE_INDEX 4 + +float4 sampleBaseColorTexture(int mtlOffset, float2 uv, float level) +{ + return sampleTexture(mtlOffset, BASE_COLOR_TEXTURE_INDEX, uv, level); +} + +float4 sampleSpecularRoughnessTexture(int mtlOffset, float2 uv, float level) +{ + return sampleTexture(mtlOffset, SPECULAR_ROUGHNESS_TEXTURE_INDEX, uv, level); +} + +float4 sampleNormalTexture(int mtlOffset, float2 uv, float level) +{ + return sampleTexture(mtlOffset, NORMAL_TEXTURE_INDEX, uv, level); +} + +float4 sampleEmissionColorTexture(int mtlOffset, float2 uv, float level) +{ + return sampleTexture(mtlOffset, EMISSION_TEXTURE_INDEX, uv, level); +} + +float4 sampleOpacityTexture(int mtlOffset, float2 uv, float level) +{ + return sampleTexture(mtlOffset, OPACITY_TEXTURE_INDEX, uv, level); +} + +#else +// Vulkan GLSL versions are forward declared and implemented in raw GLSL suffix file. +MaterialConstants getMaterial(); +float4 sampleBaseColorTexture(float2 uv, float level); +float4 sampleSpecularRoughnessTexture(float2 uv, float level); +float4 sampleEmissionColorTexture(float2 uv, float level); +float4 sampleOpacityTexture(float2 uv, float level); +float4 sampleNormalTexture(float2 uv, float level); +#endif + +// Normal map spaces definitions. +#define TANGENT_SPACE 0 +#define OBJECT_SPACE 1 + +// Rotate a 2D vector by given number of degrees. +// Based on MaterialX mx_rotate_vector2 functionn. +float2 rotateUV(float2 uv, float amountDegrees) +{ + float rotationRadians = radians(amountDegrees); + float sa = sin(rotationRadians); + float ca = cos(rotationRadians); + return float2(ca * uv.x + sa * uv.y, -sa * uv.x + ca * uv.y); +} + +// Rotate a 2D vector by given number of degrees. +// Based on MaterialX NG_place2d_vector2 function. +// NOTE: Applies transform in order Scale-Rotation-Translation +float2 applyUVTransform(float2 uv, float2 pivot, float2 scale, float rotate, float2 offset) +{ + float2 subpivot = uv - pivot; + float2 scaled = subpivot / scale; + float2 rotated = rotateUV(scaled, rotate); + float2 translated = rotated - offset; + float2 addpivot = translated + pivot; + return addpivot; +} + +// Calculate a per-pixel normal from a normal map texel value. +// NOTE: This is based on the MaterialX function mx_normalmap(). +float3 calculateNormalFromMap(float3 texelValue, int space, float scale, float3 N, float3 T) +{ + // Remap texel components from [0.0, 1.0] to [-1.0, 1.0]. + float3 v = texelValue * 2.0 - 1.0; + + // If the texel normal is in tangent space, transform it to the coordinate system defined by N + // and T. + if (space == TANGENT_SPACE) + { + float3 B = normalize(cross(N, T)); + return normalize(T * v.x * scale + B * v.y * scale + N * v.z); + } + + // Otherwise the texel normal is in object space, and is simply normalized. + else + { + return normalize(v); + } +} + +// Initializes the full set of property values for a material, for the specified shading data. +export Material evaluateDefaultMaterial( + ShadingData shading, int headerOffset, out float3 materialNormal, out bool isGeneratedNormal) +{ + int offset = headerOffset + kMaterialHeaderSize; + + // Copy the constant values to the material from the constant buffer. + Material material; + material.base = Material_base(gGlobalMaterialConstants, offset); + material.baseColor = Material_baseColor(gGlobalMaterialConstants, offset); + material.diffuseRoughness = Material_diffuseRoughness(gGlobalMaterialConstants, offset); + material.metalness = Material_metalness(gGlobalMaterialConstants, offset); + material.specular = Material_specular(gGlobalMaterialConstants, offset); + material.specularColor = Material_specularColor(gGlobalMaterialConstants, offset); + material.specularRoughness = Material_specularRoughness(gGlobalMaterialConstants, offset); + material.specularIOR = Material_specularIOR(gGlobalMaterialConstants, offset); + material.specularAnisotropy = Material_specularAnisotropy(gGlobalMaterialConstants, offset); + material.specularRotation = Material_specularRotation(gGlobalMaterialConstants, offset); + material.transmission = Material_transmission(gGlobalMaterialConstants, offset); + material.transmissionColor = Material_transmissionColor(gGlobalMaterialConstants, offset); + material.subsurface = Material_subsurface(gGlobalMaterialConstants, offset); + material.subsurfaceColor = Material_subsurfaceColor(gGlobalMaterialConstants, offset); + material.subsurfaceRadius = Material_subsurfaceRadius(gGlobalMaterialConstants, offset); + material.subsurfaceScale = Material_subsurfaceScale(gGlobalMaterialConstants, offset); + material.subsurfaceAnisotropy = Material_subsurfaceAnisotropy(gGlobalMaterialConstants, offset); + material.sheen = Material_sheen(gGlobalMaterialConstants, offset); + material.sheenColor = Material_sheenColor(gGlobalMaterialConstants, offset); + material.sheenRoughness = Material_sheenRoughness(gGlobalMaterialConstants, offset); + material.coat = Material_coat(gGlobalMaterialConstants, offset); + material.coatColor = Material_coatColor(gGlobalMaterialConstants, offset); + material.coatRoughness = Material_coatRoughness(gGlobalMaterialConstants, offset); + material.coatAnisotropy = Material_coatAnisotropy(gGlobalMaterialConstants, offset); + material.coatRotation = Material_coatRotation(gGlobalMaterialConstants, offset); + material.coatIOR = Material_coatIOR(gGlobalMaterialConstants, offset); + material.coatAffectColor = Material_coatAffectColor(gGlobalMaterialConstants, offset); + material.coatAffectRoughness = Material_coatAffectRoughness(gGlobalMaterialConstants, offset); + material.emission = Material_emission(gGlobalMaterialConstants, offset); + material.emissionColor = Material_emissionColor(gGlobalMaterialConstants, offset); + material.opacity = Material_opacity(gGlobalMaterialConstants, offset); + material.thinWalled = Material_thinWalled(gGlobalMaterialConstants, offset); + + // Sample base color from a texture if necessary. + float4 texCoord = float4(shading.texCoord, 0.0f, 1.0f); + if (Material_hasBaseColorTex(gGlobalMaterialConstants, offset)) + { + float2 uv = + applyUVTransform(shading.texCoord, Material_baseColorTexPivot(gGlobalMaterialConstants, offset), + Material_baseColorTexScale(gGlobalMaterialConstants, offset), + Material_baseColorTexRotation(gGlobalMaterialConstants, offset), + Material_baseColorTexOffset(gGlobalMaterialConstants, offset)); + material.baseColor = sampleBaseColorTexture(headerOffset, uv, 0.0f).rgb; + } + + // Sample specular roughness from a texture if necessary. + if (Material_hasSpecularRoughnessTex(gGlobalMaterialConstants, offset)) + { + float2 uv = applyUVTransform(shading.texCoord, + Material_specularRoughnessTexPivot(gGlobalMaterialConstants, offset), + Material_specularRoughnessTexScale(gGlobalMaterialConstants, offset), + Material_specularRoughnessTexRotation(gGlobalMaterialConstants, offset), + Material_specularRoughnessTexOffset(gGlobalMaterialConstants, offset)); + + material.specularRoughness = sampleSpecularRoughnessTexture(headerOffset, uv, 0.0f).r; + } + + // Sample emission color from a texture if necessary. + if (Material_hasEmissionColorTex(gGlobalMaterialConstants, offset)) + { + float2 uv = applyUVTransform(shading.texCoord, + Material_emissionColorTexPivot(gGlobalMaterialConstants, offset), + Material_emissionColorTexScale(gGlobalMaterialConstants, offset), + Material_emissionColorTexRotation(gGlobalMaterialConstants, offset), + Material_emissionColorTexOffset(gGlobalMaterialConstants, offset)); + material.emissionColor = sampleEmissionColorTexture(headerOffset, uv, 0.0f).rgb; + } + + // Sample opacity from a texture if necessary. + if (Material_hasOpacityTex(gGlobalMaterialConstants, offset)) + { + float2 uv = applyUVTransform(shading.texCoord, Material_opacityTexPivot(gGlobalMaterialConstants, offset), + Material_opacityTexScale(gGlobalMaterialConstants, offset), + Material_opacityTexRotation(gGlobalMaterialConstants, offset), + Material_opacityTexOffset(gGlobalMaterialConstants, offset)); + material.opacity = sampleOpacityTexture(headerOffset, uv, 0.0f).rgb; + } + + // Sample a normal from the normal texture, convert it to an object-space normal, transform to + // world space, and store it in the output value. + isGeneratedNormal = false; + if (Material_hasNormalTex(gGlobalMaterialConstants, offset)) + { + float2 uv = applyUVTransform(shading.texCoord, Material_normalTexPivot(gGlobalMaterialConstants, offset), + Material_normalTexScale(gGlobalMaterialConstants, offset), + Material_normalTexRotation(gGlobalMaterialConstants, offset), + Material_normalTexOffset(gGlobalMaterialConstants, offset)); + float3 normalTexel = sampleNormalTexture(headerOffset, uv, 0.0f).rgb; + float3 objectSpaceNormal = calculateNormalFromMap( + normalTexel, TANGENT_SPACE, 1.0, shading.normal, shading.tangent); + materialNormal = normalize(mul((float3x3) shading.objToWorld, objectSpaceNormal)); + + isGeneratedNormal = true; + } + + // Copy the base color to the (internal) metal color. + material.metalColor = material.baseColor; + + return material; +} + +#endif // __MATERIAL_H__ diff --git a/Libraries/Aurora/Source/Shaders/DefaultMaterialUniformAccessors.slang b/Libraries/Aurora/Source/Shaders/DefaultMaterialUniformAccessors.slang index 1e50936..e8c90d9 100644 --- a/Libraries/Aurora/Source/Shaders/DefaultMaterialUniformAccessors.slang +++ b/Libraries/Aurora/Source/Shaders/DefaultMaterialUniformAccessors.slang @@ -1,287 +1,287 @@ // Auto-generated by unit test BasicTest in AuroraExternals test suite. // Get property base from byte address buffer -float Material_base(ByteAddressBuffer buf) { - return asfloat(buf.Load(0)); +float Material_base(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 0)); } // Get property base_color from byte address buffer -float3 Material_baseColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(4)); +float3 Material_baseColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 4)); } // Get property diffuse_roughness from byte address buffer -float Material_diffuseRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(16)); +float Material_diffuseRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 16)); } // Get property metalness from byte address buffer -float Material_metalness(ByteAddressBuffer buf) { - return asfloat(buf.Load(20)); +float Material_metalness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 20)); } // Get property specular from byte address buffer -float Material_specular(ByteAddressBuffer buf) { - return asfloat(buf.Load(24)); +float Material_specular(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 24)); } // Get property specular_color from byte address buffer -float3 Material_specularColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(32)); +float3 Material_specularColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 32)); } // Get property specular_roughness from byte address buffer -float Material_specularRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(44)); +float Material_specularRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 44)); } // Get property specular_IOR from byte address buffer -float Material_specularIOR(ByteAddressBuffer buf) { - return asfloat(buf.Load(48)); +float Material_specularIOR(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 48)); } // Get property specular_anisotropy from byte address buffer -float Material_specularAnisotropy(ByteAddressBuffer buf) { - return asfloat(buf.Load(52)); +float Material_specularAnisotropy(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 52)); } // Get property specular_rotation from byte address buffer -float Material_specularRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(56)); +float Material_specularRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 56)); } // Get property transmission from byte address buffer -float Material_transmission(ByteAddressBuffer buf) { - return asfloat(buf.Load(60)); +float Material_transmission(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 60)); } // Get property transmission_color from byte address buffer -float3 Material_transmissionColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(64)); +float3 Material_transmissionColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 64)); } // Get property subsurface from byte address buffer -float Material_subsurface(ByteAddressBuffer buf) { - return asfloat(buf.Load(76)); +float Material_subsurface(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 76)); } // Get property subsurface_color from byte address buffer -float3 Material_subsurfaceColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(80)); +float3 Material_subsurfaceColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 80)); } // Get property subsurface_radius from byte address buffer -float3 Material_subsurfaceRadius(ByteAddressBuffer buf) { - return asfloat(buf.Load3(96)); +float3 Material_subsurfaceRadius(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 96)); } // Get property subsurface_scale from byte address buffer -float Material_subsurfaceScale(ByteAddressBuffer buf) { - return asfloat(buf.Load(108)); +float Material_subsurfaceScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 108)); } // Get property subsurface_anisotropy from byte address buffer -float Material_subsurfaceAnisotropy(ByteAddressBuffer buf) { - return asfloat(buf.Load(112)); +float Material_subsurfaceAnisotropy(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 112)); } // Get property sheen from byte address buffer -float Material_sheen(ByteAddressBuffer buf) { - return asfloat(buf.Load(116)); +float Material_sheen(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 116)); } // Get property sheen_color from byte address buffer -float3 Material_sheenColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(128)); +float3 Material_sheenColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 128)); } // Get property sheen_roughness from byte address buffer -float Material_sheenRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(140)); +float Material_sheenRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 140)); } // Get property coat from byte address buffer -float Material_coat(ByteAddressBuffer buf) { - return asfloat(buf.Load(144)); +float Material_coat(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 144)); } // Get property coat_color from byte address buffer -float3 Material_coatColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(148)); +float3 Material_coatColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 148)); } // Get property coat_roughness from byte address buffer -float Material_coatRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(160)); +float Material_coatRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 160)); } // Get property coat_anisotropy from byte address buffer -float Material_coatAnisotropy(ByteAddressBuffer buf) { - return asfloat(buf.Load(164)); +float Material_coatAnisotropy(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 164)); } // Get property coat_rotation from byte address buffer -float Material_coatRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(168)); +float Material_coatRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 168)); } // Get property coat_IOR from byte address buffer -float Material_coatIOR(ByteAddressBuffer buf) { - return asfloat(buf.Load(172)); +float Material_coatIOR(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 172)); } // Get property coat_affect_color from byte address buffer -float Material_coatAffectColor(ByteAddressBuffer buf) { - return asfloat(buf.Load(176)); +float Material_coatAffectColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 176)); } // Get property coat_affect_roughness from byte address buffer -float Material_coatAffectRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(180)); +float Material_coatAffectRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 180)); } // Get property emission from byte address buffer -float Material_emission(ByteAddressBuffer buf) { - return asfloat(buf.Load(184)); +float Material_emission(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 184)); } // Get property emission_color from byte address buffer -float3 Material_emissionColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(192)); +float3 Material_emissionColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 192)); } // Get property opacity from byte address buffer -float3 Material_opacity(ByteAddressBuffer buf) { - return asfloat(buf.Load3(208)); +float3 Material_opacity(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 208)); } // Get property thin_walled from byte address buffer -int Material_thinWalled(ByteAddressBuffer buf) { - return buf.Load(220); +int Material_thinWalled(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 220); } // Get property has_base_color_image from byte address buffer -int Material_hasBaseColorTex(ByteAddressBuffer buf) { - return buf.Load(224); +int Material_hasBaseColorTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 224); } // Get property base_color_image_offset from byte address buffer -float2 Material_baseColorTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(228)); +float2 Material_baseColorTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 228)); } // Get property base_color_image_scale from byte address buffer -float2 Material_baseColorTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(240)); +float2 Material_baseColorTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 240)); } // Get property base_color_image_pivot from byte address buffer -float2 Material_baseColorTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(248)); +float2 Material_baseColorTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 248)); } // Get property base_color_image_rotation from byte address buffer -float Material_baseColorTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(256)); +float Material_baseColorTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 256)); } // Get property has_specular_roughness_image from byte address buffer -int Material_hasSpecularRoughnessTex(ByteAddressBuffer buf) { - return buf.Load(260); +int Material_hasSpecularRoughnessTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 260); } // Get property specular_roughness_image_offset from byte address buffer -float2 Material_specularRoughnessTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(264)); +float2 Material_specularRoughnessTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 264)); } // Get property specular_roughness_image_scale from byte address buffer -float2 Material_specularRoughnessTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(272)); +float2 Material_specularRoughnessTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 272)); } // Get property specular_roughness_image_pivot from byte address buffer -float2 Material_specularRoughnessTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(280)); +float2 Material_specularRoughnessTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 280)); } // Get property specular_roughness_image_rotation from byte address buffer -float Material_specularRoughnessTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(288)); +float Material_specularRoughnessTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 288)); } // Get property has_emission_color_image from byte address buffer -int Material_hasEmissionColorTex(ByteAddressBuffer buf) { - return buf.Load(292); +int Material_hasEmissionColorTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 292); } // Get property emission_color_image_offset from byte address buffer -float2 Material_emissionColorTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(296)); +float2 Material_emissionColorTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 296)); } // Get property emission_color_image_scale from byte address buffer -float2 Material_emissionColorTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(304)); +float2 Material_emissionColorTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 304)); } // Get property emission_color_image_pivot from byte address buffer -float2 Material_emissionColorTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(312)); +float2 Material_emissionColorTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 312)); } // Get property emission_color_image_rotation from byte address buffer -float Material_emissionColorTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(320)); +float Material_emissionColorTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 320)); } // Get property has_opacity_image from byte address buffer -int Material_hasOpacityTex(ByteAddressBuffer buf) { - return buf.Load(324); +int Material_hasOpacityTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 324); } // Get property opacity_image_offset from byte address buffer -float2 Material_opacityTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(328)); +float2 Material_opacityTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 328)); } // Get property opacity_image_scale from byte address buffer -float2 Material_opacityTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(336)); +float2 Material_opacityTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 336)); } // Get property opacity_image_pivot from byte address buffer -float2 Material_opacityTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(344)); +float2 Material_opacityTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 344)); } // Get property opacity_image_rotation from byte address buffer -float Material_opacityTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(352)); +float Material_opacityTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 352)); } // Get property has_normal_image from byte address buffer -int Material_hasNormalTex(ByteAddressBuffer buf) { - return buf.Load(356); +int Material_hasNormalTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 356); } // Get property normal_image_offset from byte address buffer -float2 Material_normalTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(360)); +float2 Material_normalTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 360)); } // Get property normal_image_scale from byte address buffer -float2 Material_normalTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(368)); +float2 Material_normalTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 368)); } // Get property normal_image_pivot from byte address buffer -float2 Material_normalTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(376)); +float2 Material_normalTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 376)); } // Get property normal_image_rotation from byte address buffer -float Material_normalTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(384)); +float Material_normalTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 384)); } diff --git a/Libraries/Aurora/Source/Shaders/Environment.slang b/Libraries/Aurora/Source/Shaders/Environment.slang index c4ed646..160d969 100644 --- a/Libraries/Aurora/Source/Shaders/Environment.slang +++ b/Libraries/Aurora/Source/Shaders/Environment.slang @@ -17,35 +17,7 @@ #include "Colors.slang" #include "Globals.slang" #include "Sampling.slang" - -// Layout of environment constant properties. -struct EnvironmentConstants -{ - float3 lightTop; - float _padding1; - float3 lightBottom; - float lightTexLuminanceIntegral; - float4x4 lightTransform; - float4x4 lightTransformInv; - float3 backgroundTop; - float _padding3; - float3 backgroundBottom; - float _padding4; - float4x4 backgroundTransform; - bool backgroundUseScreen; - bool hasLightTex; - bool hasBackgroundTex; -}; - -// Layout of an alias map entry. -// NOTE: Padding to 16 byte (float4) alignment is added for best performance. -struct AliasEntry -{ - uint alias; - float probability; - float pdf; - float _padding1; -}; +#include "GlobalPipelineState.slang" // Environment properties. struct Environment diff --git a/Libraries/Aurora/Source/Shaders/EvaluateMaterial.slang b/Libraries/Aurora/Source/Shaders/EvaluateMaterial.slang new file mode 100644 index 0000000..f1b9641 --- /dev/null +++ b/Libraries/Aurora/Source/Shaders/EvaluateMaterial.slang @@ -0,0 +1,40 @@ +// Copyright 2023 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __EVALUATE_MATERIAL_H__ +#define __EVALUATE_MATERIAL_H__ + +// Pre-processor define to enable runtime compiled evaluateMaterialForShader function. +#if RUNTIME_COMPILE_EVALUATE_MATERIAL_FUNCTION + +// Declaration for runtime generated evaluate material function. Generated by PTShaderLibrary and linked in at runtime. +Material evaluateMaterialForShader( + int shaderIndex, ShadingData shading, int mtlOffset, out float3 materialNormal, out bool isGeneratedNormal); + +#else + +// Ignore the shaderIndex and assume all materials use default shader. +Material evaluateMaterialForShader( + int shaderIndex, ShadingData shading, int mtlOffset, out float3 materialNormal, out bool isGeneratedNormal) +{ + + mtlOffset = mtlOffset + kMaterialHeaderSize; + + return evaluateDefaultMaterial( + shading, mtlOffset, + materialNormal, isGeneratedNormal); +} + +#endif + +#endif diff --git a/Libraries/Aurora/Source/Shaders/Frame.slang b/Libraries/Aurora/Source/Shaders/Frame.slang index 619adc1..826f273 100644 --- a/Libraries/Aurora/Source/Shaders/Frame.slang +++ b/Libraries/Aurora/Source/Shaders/Frame.slang @@ -87,7 +87,7 @@ struct FrameData float sceneSize; // Whether shadow evaluation should treat all objects as opaque, as a performance optimization. - bool isOpaqueShadowsEnabled; + bool isForceOpaqueShadowsEnabled; // Whether to write the NDC depth result to an output texture. bool isDepthNDCEnabled; diff --git a/Libraries/Aurora/Source/Shaders/Geometry.slang b/Libraries/Aurora/Source/Shaders/Geometry.slang index a5a9403..5528d1c 100644 --- a/Libraries/Aurora/Source/Shaders/Geometry.slang +++ b/Libraries/Aurora/Source/Shaders/Geometry.slang @@ -14,6 +14,8 @@ #ifndef __GEOMETRY_H__ #define __GEOMETRY_H__ +#include "Globals.slang" + interface IGeometry { uint3 getIndices(int bufferLocation); @@ -40,8 +42,12 @@ struct ShadingData // materialX generated code. float3 objectPosition; // the shading normal in object space (not world space). Used only in // materialX generated code. + float3x4 objToWorld; + uint3 indices; + float3 barycentrics; }; + // Computes the barycentric coordinates from the specified triangle intersection. float3 computeBarycentrics(BuiltInTriangleIntersectionAttributes attribs) { @@ -49,6 +55,11 @@ float3 computeBarycentrics(BuiltInTriangleIntersectionAttributes attribs) return float3(1.0f - bary2.x - bary2.y, bary2.x, bary2.y); } +// Computes the barycentric coordinates from the specified triangle intersection. +float3 computeBarycentrics(float2 bary2) +{ + return float3(1.0f - bary2.x - bary2.y, bary2.x, bary2.y); +} // Projects the specified position (point) onto the plane with the specified origin and normal. float3 projectOnPlane(float3 position, float3 origin, float3 normal) { @@ -83,8 +94,11 @@ float3 computeShadingPosition(float3 geomPosition, float3 shadingNormal, float3 // Computes the shading data (e.g. interpolated vertex data) for the triangle with the specified // (primitive) index, at the specified barycentric coordinates. ShadingData computeShadingData( - GeometryType geometry, uint triangleIndex, float3 barycentrics, float3x4 objToWorld) + GeometryType geometry, uint triangleIndex, float2 bary2, float3x4 objToWorld) { + // Expand barycentrics to 3d. + float3 barycentrics = computeBarycentrics(bary2); + // Initialize a result structure. ShadingData shading; shading.position = float3(0.0f, 0.0f, 0.0f); @@ -154,17 +168,23 @@ ShadingData computeShadingData( shading.geomPosition = mul(objToWorld, float4(shading.geomPosition, 1.0f)); shading.normal = normalize(mul((float3x3)objToWorld, shading.objectNormal)); - // Arbritary up vector used to calculate tangents, if none provided. + // arbitrary up vector used to calculate tangents, if none provided. float3 up = abs(shading.normal.y) < 0.999f ? float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f); - // Compute arbritary shading tangent from up vector, or use provided one transformed in world space. + // Compute arbitrary shading tangent from up vector, or use provided one transformed in world space. shading.tangent = normalize(geometry.hasTangents() ? normalize(mul((float3x3)objToWorld, shading.tangent)) : cross(shading.normal, up)); // Generate bitangent from normal and tangent. shading.bitangent = computeBitangent(shading.normal, shading.tangent); + shading.objToWorld = objToWorld; + + shading.indices = indices; + + shading.barycentrics = barycentrics; + return shading; } diff --git a/Libraries/Aurora/Source/Shaders/GlobalBufferAccessors.slang b/Libraries/Aurora/Source/Shaders/GlobalBufferAccessors.slang new file mode 100644 index 0000000..58b7f6a --- /dev/null +++ b/Libraries/Aurora/Source/Shaders/GlobalBufferAccessors.slang @@ -0,0 +1,145 @@ +// Copyright 2023 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __GLOBAL_BUFFER_ACCESSORS_H__ +#define __GLOBAL_BUFFER_ACCESSORS_H__ + +#include "GlobalPipelineState.slang" + +// Maxiumum number of textures per-material. +// NOTE: Must match value defined in PTScene.cpp, +#define kMaterialMaxTextures 8 + +// Total size of fixed-length material header in bytes. +// Material header occurs before each material's properties in the gGlobalMaterialConstants buffer. +// NOTE: Must match size of MaterialHeader in PTScene.cpp. +#define kMaterialHeaderSize 68 + +// Total size of fixed-length instance header in bytes. +// Instance header occurs before the layer data in the gGlobalInstanceArray buffer. +// NOTE: Must match size of InstanceDataHeader in PTScene.cpp. +#define kInstanceDataHeaderSize (2*4) + +// Size of a 3x4 transform matrix, +#define kTransformMatrixSize (12*4) + +// ================================================================================================= +// Instance buffer accessor functions +// ================================================================================================= + +// Get the material offset from instance header. +int instanceMaterialBufferOffset(int instanceOffset) +{ + return gGlobalInstanceBuffer.Load(instanceOffset + 4 * 0); +} + +// Get the layer count from instance header. +int instanceLayerCount(int instanceOffset) +{ + return gGlobalInstanceBuffer.Load(instanceOffset + 4 * 1); +} + +// Get the layer material buffer offset for Nth layer. +// Layer data is stored after the header in the instance buffer. +int instanceLayerMaterialBufferOffset(int instanceOffset, int layerIdx) +{ + return gGlobalInstanceBuffer.Load(instanceOffset + kInstanceDataHeaderSize + 4 * (0 + layerIdx * 2)); +} + +// Get the layer UV buffer offset for Nth layer. +// Layer data is stored after the header in the instance buffer. +int instanceLayerUVBufferOffset(int instanceOffset, int layerIdx) +{ + return gGlobalInstanceBuffer.Load(instanceOffset + kInstanceDataHeaderSize + 4 * (1 + layerIdx * 2)); +} + +// Get row-major 4x3 world transform matrix for the BLAS indicated by index +float3x4 transformMatrixForBLAS(int blasIndex) +{ + int startOffset = blasIndex * kTransformMatrixSize; + return float3x4( + asfloat(gTransformMatrixBuffer.Load4(startOffset + 4 * 0)), + asfloat(gTransformMatrixBuffer.Load4(startOffset + 4 * 4)), + asfloat(gTransformMatrixBuffer.Load4(startOffset + 4 * 8)) + ); +} + +float2 getLayerUV(int offset, int index) +{ + return asfloat(gLayerGeometryBuffer.Load2(offset + index * 8)); +} + +float2 computeLayerUV(int instanceOffset, int layerIdx, ShadingData shading) +{ + int uvOffset = instanceLayerUVBufferOffset(instanceOffset, layerIdx); + + float2 uv = 0; + for (uint i = 0; i < 3; i++) + { + uv += shading.barycentrics[i] * getLayerUV(uvOffset, shading.indices[i]); + } + + return uv; +} + + +// ================================================================================================= +// Common material functions +// ================================================================================================= + +// Get shader index from material header. +int materialShaderIndex(int mtlOffset) +{ + return gGlobalMaterialConstants.Load(mtlOffset + 0); +} + +// Get Nth texture index from material header. +int materialTextureIndex(int mtlOffset, int textureNo) +{ + return gGlobalMaterialConstants.Load(mtlOffset + 4 * (1 + textureNo)); +} + +// Get Nth sampler index from material header. +int materialSamplerIndex(int mtlOffset, int textureNo) +{ + return gGlobalMaterialConstants.Load(mtlOffset + 4 * (1 + kMaterialMaxTextures + textureNo)); +} +// Get Nth texture using index from material header. +Texture2D materialTexture(int mtlOffset, int textureNo) +{ + int idx = materialTextureIndex(mtlOffset, textureNo); + + return gGlobalMaterialTextures[idx]; +} + +// Get Nth sampler using index from material header. +SamplerState materialSampler(int mtlOffset, int textureNo) +{ + int idx = materialSamplerIndex(mtlOffset, textureNo); + + return gSamplerArray[idx]; +} + +// Sample Nth texture using index from material header. +float4 sampleTexture(int mtlOffset, int textureNo, float2 uv, float level) +{ +#if VALIDATE_TEXTURE_INDICES + if (textureNo < 0 || textureNo >= kMaterialMaxTextures) + return float4(1, 0, 0, 1); +#endif + Texture2D txt = materialTexture(mtlOffset, textureNo); + SamplerState smp = materialSampler(mtlOffset, textureNo); + return txt.SampleLevel(smp, uv, level); +} + +#endif // __MATERIALHEADER_H__ diff --git a/Libraries/Aurora/Source/Shaders/GlobalPipelineState.slang b/Libraries/Aurora/Source/Shaders/GlobalPipelineState.slang new file mode 100644 index 0000000..35b6d9b --- /dev/null +++ b/Libraries/Aurora/Source/Shaders/GlobalPipelineState.slang @@ -0,0 +1,112 @@ +// Copyright 2023 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Prefix containing the common code used by all material types. +#ifndef GLOBALPIPELINESTATE_H +#define GLOBALPIPELINESTATE_H + +#include "Geometry.slang" +#include "Frame.slang" + +// ================================================================================================= +// Structure defintions, used in global pipeline state +// ================================================================================================= + +// Layout of environment constant properties. +struct EnvironmentConstants +{ + float3 lightTop; + float _padding1; + float3 lightBottom; + float lightTexLuminanceIntegral; + float4x4 lightTransform; + float4x4 lightTransformInv; + float3 backgroundTop; + float _padding3; + float3 backgroundBottom; + float _padding4; + float4x4 backgroundTransform; + bool backgroundUseScreen; + bool hasLightTex; + bool hasBackgroundTex; +}; + +// Layout of an alias map entry. +// Must match CPU struct Entry in AliasMap.h. +// NOTE: Padding to 16 byte (float4) alignment is added for best performance. +struct AliasEntry +{ + uint alias; + float probability; + float pdf; + float _padding1; +}; + +// Layout of ground plane properties. +// Must match CPU struct GroundPlaneData in PTGroundPlane.h. +struct GroundPlane +{ + bool enabled; + float3 position; + float3 normal; + float3 tangent; + float3 bitangent; + float shadowOpacity; + float3 shadowColor; + float reflectionOpacity; + float3 reflectionColor; + float reflectionRoughness; +}; + +// ================================================================================================= +// Global Variables - For All Shaders +// Must match global root signature defined defined in PTShaderLibrary::initRootSignatures and data +// setup in PTRenderer::submitRayDispatch. +// ================================================================================================= + +// The top-level acceleration structure with the scene contents. +[[vk::binding(0)]] RaytracingAccelerationStructure gScene : register(t0); + +// Constant buffers of sample and per-frame values. +[[vk::binding(4)]] ConstantBuffer gSampleData : register(b0); +[[vk::binding(2)]] ConstantBuffer gFrameData : register(b1); + +// Environment data. +[[vk::binding(5)]] ConstantBuffer gEnvironmentConstants : register(b2); +StructuredBuffer gEnvironmentAliasMap : register(t1); +[[vk::binding(8)]] Texture2D gEnvironmentLightTexture : register(t2); +[[vk::binding(7)]] Texture2D gEnvironmentBackgroundTexture : register(t3); +ConstantBuffer gGroundPlane : register(b3); +RaytracingAccelerationStructure gNullScene : register(t4); + +// Material properties for all scene materials are stored in this ByteAddressBuffer, and accessed with Material_XXX() +// functions, specific to the material type, using the offset included in the instance data for each ray hit. +ByteAddressBuffer gGlobalMaterialConstants : register(t5); + +// Instance properties for all scene instances are stored in this ByteAddressBuffer. This is the instance transform matrix and layer properties, if any. +ByteAddressBuffer gGlobalInstanceBuffer : register(t6); + +// Instance properties for all scene instances are stored in this ByteAddressBuffer. This is the instance transform matrix and layer properties, if any. +ByteAddressBuffer gLayerGeometryBuffer : register(t7); + +// Transform matrices for all the instances. +ByteAddressBuffer gTransformMatrixBuffer : register(t8); + +// Material textures for all the scene materials. Looked up using indices stored in material header. +Texture2D gGlobalMaterialTextures[] : register(t9); + +// Material textures for all the scene materials. Looked up using indices stored in material header. +SamplerState gSamplerArray[] : register(s0); + +#endif // PATHTRACINGCOMMON_H diff --git a/Libraries/Aurora/Source/Shaders/Globals.slang b/Libraries/Aurora/Source/Shaders/Globals.slang index 39fc419..249e4e7 100644 --- a/Libraries/Aurora/Source/Shaders/Globals.slang +++ b/Libraries/Aurora/Source/Shaders/Globals.slang @@ -21,30 +21,8 @@ #define M_RAY_TMIN 0.01f // Miss shader indices. -#if DIRECTX #define kMissNull 0 // for a disabled miss shader (no effect) -#define kMissBackground 1 // for the background, visible by primary rays -#define kMissRadiance 2 // for lighting (radiance) -#define kMissShadow 3 // for shadow rays, forced to opaque -#else -// In HGI backend, backround and radiance miss shader are the same. -#define kMissBackground 0 // for the background, visible by primary rays -#define kMissRadiance 1 // for lighting (radiance) -#define kMissShadow 2 // for shadow rays, forced to opaque -#endif - -// Importance sampling modes. This is set with the IMPORTANCE_SAMPLING_MODE preprocessor symbol. -#define IMPORTANCE_SAMPLING_BSDF 0 // sample the BSDF alone -#define IMPORTANCE_SAMPLING_ENVIRONMENT 1 // sample the environment alone -#define IMPORTANCE_SAMPLING_MIS 2 // sample both the BSDF and environment -#if !defined(IMPORTANCE_SAMPLING_MODE) -#if DIRECTX -#define IMPORTANCE_SAMPLING_MODE IMPORTANCE_SAMPLING_MIS -#else -#define IMPORTANCE_SAMPLING_MODE IMPORTANCE_SAMPLING_BSDF -#endif - -#endif +#define kMissShadow 1 // for shadow rays, forced to opaque // Black and white constant values, which can also be used as a float3. #define BLACK 0.0f diff --git a/Libraries/Aurora/Source/Shaders/GroundPlane.slang b/Libraries/Aurora/Source/Shaders/GroundPlane.slang index 9762114..c0a3252 100644 --- a/Libraries/Aurora/Source/Shaders/GroundPlane.slang +++ b/Libraries/Aurora/Source/Shaders/GroundPlane.slang @@ -18,26 +18,13 @@ #include "Globals.slang" #include "Random.slang" #include "RayTrace.slang" +#include "GlobalPipelineState.slang" -// Layout of ground plane properties. -struct GroundPlane -{ - bool enabled; - float3 position; - float3 normal; - float3 tangent; - float3 bitangent; - float shadowOpacity; - float3 shadowColor; - float reflectionOpacity; - float3 reflectionColor; - float reflectionRoughness; -}; +// TODO: Re-enable ground plane. // Shades a matte reflection effect with the specified properties. -float4 shadeMatteReflection(float opacity, float3 color, float roughness, - RaytracingAccelerationStructure scene, Environment environment, float3 rayDir, float3 normal, - float3 tangent, float3 bitangent, float3 position, uint maxDepth, inout Random rng) +int setupMatteReflectionIndirectLightRay(float opacity, float3 color, float roughness, float3 rayDir, float3 normal, + float3 tangent, float3 bitangent, float3 position, uint maxDepth, inout Random rng, out float3 indirectRayDirec, out float3 indirectRayOrigin, out float3 indirectColorOut) { // Transform the ray to a view direction in tangent space. float3 V = worldToTangent(-rayDir, tangent, bitangent, normal); @@ -46,13 +33,13 @@ float4 shadeMatteReflection(float opacity, float3 color, float roughness, // material. This gives a BSDF result, which is scaled by the cosine term. float3 L; float pdf; - float3 f0 = color; - float2 random = random2D(rng); + float3 f0 = color; + float2 random = random2D(rng); BSDF_AND_COSINE bsdfAndCosine = sampleGGXSmithBRDF(V, f0, roughness, 0.0f, random, L, pdf); bsdfAndCosine *= abs(L.z); if (isBlack(bsdfAndCosine)) { - return BLACK; + return NO_SECONDARY_RAY; } // Trace a radiance ray with the sampled light direction. If the ray payload alpha is zero, @@ -60,16 +47,20 @@ float4 shadeMatteReflection(float opacity, float3 color, float roughness, // compute the outgoing radiance as the BSDF (with cosine term) multipled by the light radiance, // divided by the PDF. float3 Lw = tangentToWorld(L, tangent, bitangent, normal); - RadianceRayPayload rayPayload = - traceRadianceRay(scene, environment, position, Lw, M_RAY_TMIN, 1, maxDepth, false, rng); - if (rayPayload.alpha == 0.0f) - { - return BLACK; - } - float3 radiance = rayPayload.color * bsdfAndCosine / pdf; - - // Return the result as the radiance for color, and reflection opacity for alpha. - return float4(radiance, opacity); + + // Compute the indirect ray, i.e. the next step in tracing the current path. + + // If the view (ray) direction is on the back side of the geometry, or the ray is + // from a transmission lobe, use the geometric position to avoid self-intersection. + indirectRayOrigin = position; + + // Set the direction from the sampled light direction. + indirectRayDirec = Lw; + + // Set the color from the sampled color scaled by the inverse of the PDF. + indirectColorOut = bsdfAndCosine / pdf; + + return GROUND_PLANE_REFLECTION_RAY; } // Shades a matte shadow effect with the specified environment light and position. @@ -77,14 +68,14 @@ float4 shadeMatteReflection(float opacity, float3 color, float roughness, // i.e. shadowed and unshadowed components. This also does not consider the directional light. Both // of these issues should be addressed in the future. float4 shadeMatteShadow(float opacity, float3 color, RaytracingAccelerationStructure scene, - Environment environment, float3 normal, float3 position, bool isOpaque, uint maxDepth, + Environment environment, float3 normal, float3 position, bool onlyOpaqueShadowHits, uint maxDepth, inout Random rng) { // Sample the environment to determine a sample (light) direction and the corresponding // probability density function (PDF) value for that direction. float3 L; float pdf; - float2 random = random2D(rng); + float2 random = random2D(rng); float3 lightRadiance = sampleEnvironment(environment, random, L, pdf); // If the light direction is below the ground plane, the light does not contribute to shadowing. @@ -94,76 +85,107 @@ float4 shadeMatteShadow(float opacity, float3 color, RaytracingAccelerationStruc } // Use a shadow ray to compute the visibility of the light source at the hit position. - float3 lightVisibility = traceShadowRay(scene, position, L, M_RAY_TMIN, isOpaque, 1, maxDepth); + float3 lightVisibility = traceShadowRay(scene, position, L, M_RAY_TMIN, onlyOpaqueShadowHits, 1, maxDepth); // The result color is the shadow color, and the alpha (blend factor) is the luminance of the // inverse light visibility (i.e. zero visibility means full shadow) scaled by the shadow // opacity. float4 result = BLACK; - result.rgb = color; - result.a = luminance(1.0f - lightVisibility) * opacity; - + result.rgb = color; + result.a = luminance(1.0f - lightVisibility) * opacity; + + // Return result color. return result; } -// Shades the specified ground plane for the specified ray. The result is intended to be blended -// with whatever is behind the ground plane. This returns black with zero alpha if the ground plane -// is not hit. -float4 shadeGroundPlane(GroundPlane groundPlane, RaytracingAccelerationStructure scene, - Environment environment, float3 rayOrigin, float3 rayDir, float maxDist, - bool isOpaqueShadowsEnabled, uint maxDepth, inout Random rng) +bool intersectGroundPlane(GroundPlane groundPlane, float3 rayOrigin, float3 rayDir, inout float3 hitPosition, inout float distance) { // Collect ground plane properties. - float3 position = groundPlane.position; - float3 normal = groundPlane.normal; - float3 tangent = groundPlane.tangent; - float3 bitangent = groundPlane.bitangent; - float shadowOpacity = groundPlane.shadowOpacity; - float3 shadowColor = groundPlane.shadowColor; - float reflectionOpacity = groundPlane.reflectionOpacity; - float3 reflectionColor = groundPlane.reflectionColor; - float reflectionRoughness = groundPlane.reflectionRoughness; - + float3 position = groundPlane.position; + float3 normal = groundPlane.normal; + // Compute the dot product of the ray direction and plane normal. If this is greater than zero, // the ray direction is below the plane and the plane is not visible. float planeDot = dot(rayDir, normal); if (planeDot >= 0.0) { - return BLACK; + return false; } - + // Compute the distance to the plane along the ray direction, i.e ray-plane intersection. If the // distance is less than zero (behind the ray) or greater than the maximum distance, the plane // is not visible. - float distance = dot(position - rayOrigin, normal) / planeDot; - if (distance <= 0.0f || distance >= maxDist) - { - return BLACK; - } - + distance = dot(position - rayOrigin, normal) / planeDot; + if (distance < 0.0) + return false; + // Compute the hit position. - float3 hitPosition = rayOrigin + rayDir * distance; + hitPosition = rayOrigin + rayDir * distance; + + return true; + +} - // Shade with a matte reflection effect, if enabled. - float4 result = BLACK; +// Shades the specified ground plane for the specified ray. The result is intended to be blended +// with whatever is behind the ground plane. This returns black with zero alpha if the ground plane +// is not hit. +int setupGroundPlaneRay(float3 hitPosition, GroundPlane groundPlane, RaytracingAccelerationStructure scene, + Environment environment, float3 rayOrigin, float3 rayDir, + bool isForceOpaqueShadowsEnabled, uint maxDepth, inout Random rng, + out float3 groundPlaneRayDirecOut, out float3 groundPlaneRayOriginOut, out float3 groundPlaneColorOut) +{ + // Collect ground plane properties. + float3 position = groundPlane.position; + float3 normal = groundPlane.normal; + float3 tangent = groundPlane.tangent; + float3 bitangent = groundPlane.bitangent; + float shadowOpacity = groundPlane.shadowOpacity; + float3 shadowColor = groundPlane.shadowColor; + float reflectionOpacity = groundPlane.reflectionOpacity; + float3 reflectionColor = groundPlane.reflectionColor; + float reflectionRoughness = groundPlane.reflectionRoughness; + + // Shade with a matte reflection layer, if enabled. + groundPlaneColorOut = WHITE; if (reflectionOpacity > 0.0f) { - result = shadeMatteReflection(reflectionOpacity, reflectionColor, reflectionRoughness, - scene, environment, rayDir, normal, tangent, bitangent, hitPosition, maxDepth, rng); + // Compute transparency from inverse of reflection opacity. + float3 transparency = 1.0f - reflectionOpacity; + + // Randomly choose if next ray is a reflection ray, based on reflection opacity. + float Pr = luminance(transparency); + if (random2D(rng).x >= Pr) + { + // Setup the next ray as a reflection ray. + int rayType = setupMatteReflectionIndirectLightRay(reflectionOpacity, reflectionColor, reflectionRoughness, + rayDir, normal, tangent, bitangent, hitPosition, maxDepth, rng, + groundPlaneRayDirecOut, groundPlaneRayOriginOut, groundPlaneColorOut); + + // Normalize based on probability of reflecting. + groundPlaneColorOut *= reflectionOpacity / (1.0f - Pr); + + // Return the reflection ray type. + return rayType; + } + else + groundPlaneColorOut = transparency / Pr; // Normalize based on probability of not reflecting. } - - // Shade with a matte shadow effect, if enabled. + + // Shade with a matte shadow layer, if enabled. if (shadowOpacity > 0.0f) { + // Shade ground plane shadow. float4 shadow = shadeMatteShadow(shadowOpacity, shadowColor, scene, environment, normal, - hitPosition, isOpaqueShadowsEnabled, maxDepth, rng); + hitPosition, true, maxDepth, rng); + + // Compute interpolated shadow value (ground plane shadow color in shadowed regions, white elsewhere.) + float3 interpolatedShadowColor = lerp(WHITE, shadow.rgb, shadow.a); + + // Scale returned color by shadow value. + groundPlaneColorOut *= interpolatedShadowColor; - // Blend the shadow result with the prior result, using the shadow alpha. Also update the - // result alpha to the combined alpha of the shadow and the prior result, as if they are - // two overlapping layers with alpha as opacity. - result.rgb = lerp(result.rgb, shadow.rgb, shadow.a); - result.a = 1.0f - ((1.0f - shadow.a) * (1.0f - result.a)); } - - return result; + + return CAMERA_RAY; } + diff --git a/Libraries/Aurora/Source/Shaders/InitializeDefaultMaterialType.slang b/Libraries/Aurora/Source/Shaders/InitializeDefaultMaterialType.slang deleted file mode 100644 index 3cbf43e..0000000 --- a/Libraries/Aurora/Source/Shaders/InitializeDefaultMaterialType.slang +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2023 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -Material initializeMaterial(ShadingData shading, - float3x4 objToWorld, - out float3 materialNormal, out bool isGeneratedNormal) -{ - return initializeDefaultMaterial( - shading, - objToWorld, materialNormal, isGeneratedNormal); -} - diff --git a/Libraries/Aurora/Source/Shaders/InstancePipelineState.slang b/Libraries/Aurora/Source/Shaders/InstancePipelineState.slang new file mode 100644 index 0000000..b3bfed1 --- /dev/null +++ b/Libraries/Aurora/Source/Shaders/InstancePipelineState.slang @@ -0,0 +1,109 @@ +// Copyright 2023 Autodesk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef __INSTANCEDATA_H__ +#define __INSTANCEDATA_H__ + +// ================================================================================================= +// Instance pipeline state. +// Must match CPU version in HitGroupShaderRecord in PTScene.cpp. +// ================================================================================================= + + +#if DIRECTX +// Geometry data, for the current instance only. +ByteAddressBuffer gIndices : register(t0, space1); +ByteAddressBuffer gPositions : register(t1, space1); +ByteAddressBuffer gNormals : register(t2, space1); +ByteAddressBuffer gTangents : register(t3, space1); +ByteAddressBuffer gTexCoords : register(t4, space1); + +// The data for instance and its associate geometry and material. +// Must match CPU struct HitGroupShaderRecord in PTScene.cpp. +cbuffer gInstanceData : register(b0, space1) +{ + bool gHasNormals; // Are there normals? + bool gHasTangents; // Are there tangents? + bool gHasTexCoords; // Are there texture coordinates? + bool gIsOpaque; // Is the geometry opaque? + int gInstanceBufferOffset; // Offset of instance's data within global instance byte buffer. +} + +// To hide DX-Vulkan differences, expose geometry access using functions. +uint3 getIndicesForTriangle(int triangleIndex) +{ + return gIndices.Load3((triangleIndex * 3) * 4); +} + +float3 getPositionForVertex(int vertexIndex) +{ + return asfloat(gPositions.Load3(vertexIndex * 3 * 4)); +} + +float3 getNormalForVertex(int vertexIndex) +{ + return asfloat(gNormals.Load3(vertexIndex * 3 * 4)); +} + +float3 getTangentForVertex(int vertexIndex) +{ + return asfloat(gTangents.Load3(vertexIndex * 3 * 4)); +} + +float2 getTexCoordForVertex(int vertexIndex) +{ + return asfloat(gTexCoords.Load2(vertexIndex * 2 * 4)); +} + +bool instanceHasNormals() +{ + return gHasNormals; +} +bool instanceHasTangents() +{ + return gHasTangents; +} +bool instanceHasTexCoords() +{ + return gHasTexCoords; +} + +#else + +// Forward declare these functions on Vulkan GLSL. As we need a platform-specific suffix file +// containing the implementation of these functions. +uint3 getIndicesForTriangle(int bufferLocation); +float3 getPositionForVertex(int bufferLocation); +float3 getNormalForVertex(int bufferLocation); +float3 getTangentForVertex(int bufferLocation); +float2 getTexCoordForVertex(int bufferLocation); +bool instanceHasNormals(); +bool instanceHasTangents(); +bool instanceHasTexCoords(); + +#endif + +// Slang interface implementation to handle geometry access in platform-independent way. +struct Geometry : IGeometry +{ + uint3 getIndices(int triangleIndex) { return getIndicesForTriangle(triangleIndex); } + float3 getPosition(int vertexIndex) { return getPositionForVertex(vertexIndex); } + float3 getNormal(int vertexIndex) { return getNormalForVertex(vertexIndex); } + float3 getTangent(int vertexIndex) { return getTangentForVertex(vertexIndex); } + float2 getTexCoord(int vertexIndex) { return getTexCoordForVertex(vertexIndex); } + bool hasTexCoords() { return instanceHasTexCoords(); } + bool hasNormals() { return instanceHasNormals(); } + bool hasTangents() { return instanceHasTangents(); } +} gGeometry; + +#endif diff --git a/Libraries/Aurora/Source/Shaders/MainEntryPoints.slang b/Libraries/Aurora/Source/Shaders/MainEntryPoints.slang index 31b90be..01d73ac 100644 --- a/Libraries/Aurora/Source/Shaders/MainEntryPoints.slang +++ b/Libraries/Aurora/Source/Shaders/MainEntryPoints.slang @@ -12,224 +12,578 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Entry point template that defines all the entry points for a material shader. -// NOTE: This file is not valid HLSL as is. This template must be configured at runtime by -// replacing the tags surrounded by the ___ characters: -// -___Material___ with the unique material shader name for this shader. - -#include "Definitions.slang" +// Prefix containing the common code used by all material types. #include "Options.slang" - -#include "InitializeMaterial.slang" #include "PathTracingCommon.slang" #include "ShadeFunctions.slang" +#include "DefaultMaterial.slang" +#include "GlobalBufferAccessors.slang" +#include "InstancePipelineState.slang" +#include "EvaluateMaterial.slang" + +// ================================================================================================= +// Ray Gen Shader +// ================================================================================================= +// Must match root signature defined in PTShaderLibrary::initRootSignatures and the descriptors +// uploaded to the heap in PTRenderer::updateOutputResources and PTRenderer::updateDenoisingResources. +[[vk::binding(1)]] RWTexture2D gResult : register(u0); +RWTexture2D gDepthNDC : register(u1); +RWTexture2D gDepthView : register(u2); +RWTexture2D gNormalRoughness : register(u3); +RWTexture2D gBaseColorMetalness : register(u4); +RWTexture2D gDiffuse : register(u5); +RWTexture2D gGlossy : register(u6); + +// Results of the path tracing sample. +struct Output +{ + // Depth in Normalized Device Coordinates, for entire path. + float depthNDC; + // Normal of first collision in path. + float3 normal; + // Material metalness of first collision in path. + float metalness; + // Material roughness of first collision in path. + float roughness; + // Material base color of first collision in path. + float3 baseColor; + // Depth in Normalized Device Coordinates, for entire path. + float depthView; + // Direct lighting contribution for path. + float3 direct; + // Indirect lighting contribution for path (split into diffuse and glossy.) + IndirectOutput indirect; + // Alpha for path (0.0 only if all ray segments were transparent) + float alpha; + + [mutating] + void clear() + { + indirect.clear(); + depthNDC = 1.0f; + normal = -1.0f; + metalness = 0.1f; + roughness = 0.1f; + baseColor = BLACK; + depthView = INFINITY; + direct = 0.0f; + alpha = 0.0; + } +} -// RADIANCE_HIT is defined to 1 for shaders with the radiance hit entry point. -#if RADIANCE_HIT -// Closest hit shader for radiance rays. -[shader("closesthit")] void ___Material___RadianceHitShader( - inout RayPayload rayPayload, in BuiltInTriangleIntersectionAttributes hit) { - int depth = rayPayload.radianceRay.depth; - int maxDepth = gFrameData.traceDepth; - bool isOpaqueShadowsEnabled = gFrameData.isOpaqueShadowsEnabled; +// The ray generation shader, iteratively shades the entire ray path. +[ shader("raygeneration")] +void RayGenShader() +{ + // Get the max trace depth from frame settings. + int maxTraceDepth = gFrameData.traceDepth; + + // Is opaque shadow hits only enabled in frame settings. + bool onlyOpaqueShadowHits = gFrameData.isForceOpaqueShadowsEnabled; + + // Get the dispatch dimensions (screen size), dispatch index (screen coordinates), and sample + // index. + uint2 screenSize = DispatchRaysDimensions().xy; + uint2 screenCoords = DispatchRaysIndex().xy; + uint sampleIndex = gSampleData.sampleIndex; + + // Initialize a random number generator, so that each sample and pixel gets a unique seed. + Random rng = initRand(sampleIndex, screenSize, screenCoords); + + // Compute a camera ray (origin and direction) for the current screen coordinates. This applies + // a random offset to support antialiasing and depth of field. + float3 rayOrigin; + float3 rayDirection; + int rayType = CAMERA_RAY; + computeCameraRay(screenCoords, screenSize, gFrameData.cameraInvView, gFrameData.viewSize, + gFrameData.isOrthoProjection, gFrameData.focalDistance, gFrameData.lensRadius, rng, rayOrigin, + rayDirection); + + // The results of the tracing the entire ray path through the scene. + Output output; + output.clear(); + + // The ray t (distance along path.) + float t = 0; + + // Prepare the environment data structure. + Environment environment = prepareEnvironmentValues(); - // Get the interpolated vertex data for the hit triangle, at the hit barycentric coordinates. - uint triangleIndex = PrimitiveIndex(); - float3 barycentrics = computeBarycentrics(hit); - ShadingData shading = - computeShadingData(gGeometry, triangleIndex, barycentrics, ObjectToWorld3x4()); + // The ray contribution is how much the current ray segment contributes to the overall path. + // It starts at 1.0 and then is scaled downwards at each subsequent segment. + float3 rayContribution = 1.0f; - // Initialize the view direction. - float3 V = -WorldRayDirection(); - float3 materialNormal = shading.normal; - bool isGeneratedNormal; + // Is the current ray segment a primary ray? + bool isPrimaryRay = true; - // Initialize the material values for repeated evaluations at the current shading point. Also - // initialize the environment. - Material material = - initializeMaterial(shading, ObjectToWorld3x4(), materialNormal, isGeneratedNormal); - Environment environment = prepareEnvironmentValues(); + // Has the path been terminated? + bool pathEnded = false; + InstanceRayPayload rayPayload; - // If a new normal has been generated, transform it to world space and build a corresponding - // basis from it. - if (isGeneratedNormal) + // Iterate through every segment in the ray path, until maximum depth is reached or path is ended. + for (int rayDepth = 0; rayDepth <= maxTraceDepth && !pathEnded; rayDepth++) { - shading.normal = materialNormal; - buildBasis(shading.normal, shading.tangent, shading.bitangent); - } + // The minimum T for this ray segment is zero for the initial camera ray otherwise use + // a small epsilon to avoid self-intersection. + float minT = rayDepth == 0 ? 0.0f : M_RAY_TMIN; - // Modify the material properties if only the diffuse component should be renderered. - if (gFrameData.isDiffuseOnlyEnabled) - { - material.base = 1.0f; - material.specular = 0.0f; - material.metalness = 0.0f; - } + // If this ray collides with nothing, should the background or lighting environment be shaded? + bool shadeBackgroundOnMiss = isPrimaryRay || rayType == TRANSMISSION_LOBE || rayType == GROUND_PLANE_REFLECTION_RAY; - // Shade any material layers (for front facing hits only) if the ENABLE_LAYERS option is - // defined. - // TODO: Should this be done after the transparency evaluation below, i.e. it is possible this - // work will be thrown away for transparent samples. -#if ENABLE_LAYERS - if (dot(shading.normal, V) > 0.0) - { - // Iterate in reverse order, so innermost layer is shaded last. - for (int layer = gMaterialLayerCount - 1; layer >= 0; layer--) + // Direct light contribution for this ray segment. + float3 direct = 0; + + // Indirect light contribution for this ray segment. + IndirectOutput indirect; + indirect.clear(); + + // How much the contribution will be scaled for the subsequent ray segments (based on sampling PDF of this surface's material.) + float3 nextRayContribution = 1.0; + + // The ray type of the next ray (based on sampling lobe produced from sampling this surface's material.) + int nextRayType; + + // Have we exceeded ray depth for this path? + if (rayDepth == maxTraceDepth) + { + // If we have reached the end of this path, evaluate the environment and terminate the path. + direct = evaluateEnvironment(environment, rayDirection, shadeBackgroundOnMiss); + pathEnded = true; + } + else { - int layerMissShaderIdx = getMaterialLayerIndex(layer); + bool absorbedByGroundPlane = false; + + // Trace an instance ray that will return the instance shading result from the closest hit, if any. + traceInstanceRay(gScene, rayOrigin, rayDirection, minT, rayPayload); - // Shade the layer, and return if the ray was absorbed. - if (shadeMaterialLayer(gNullScene, layerMissShaderIdx, rayPayload.radianceRay, shading, - hit, depth, maxDepth)) + // Add the contribution of the ground plane, if enabled. + float3 groundPlanePosition = 0; + float groundPlaneDistance = INFINITY; + if (isPrimaryRay && gGroundPlane.enabled && intersectGroundPlane(gGroundPlane, rayOrigin, rayDirection, groundPlanePosition, groundPlaneDistance) && groundPlaneDistance < rayPayload.distance) { - return; + float3 groundPlaneRayDirection; + float3 groundPlaneRayOrigin; + float3 groundPlaneRayColor; + + // Shade the ground plane. + int groundPlaneRayType = setupGroundPlaneRay(groundPlanePosition, gGroundPlane, gScene, environment, rayOrigin, rayDirection, + onlyOpaqueShadowHits, maxTraceDepth, rng, + groundPlaneRayDirection, groundPlaneRayOrigin, groundPlaneRayColor); + + if (groundPlaneRayType == GROUND_PLANE_REFLECTION_RAY) + { + // The ground plane reflection path was selected, continue the path as a reflection ray. + nextRayType = GROUND_PLANE_REFLECTION_RAY; + + // Setup the next ray direction, origin and contribution from ground plane. + nextRayContribution = groundPlaneRayColor; + rayDirection = groundPlaneRayDirection; + rayOrigin = groundPlaneRayOrigin; + + // The ray was absorbed. + absorbedByGroundPlane = true; + + // This ray is no longer a primary ray. + isPrimaryRay = false; + } + else + { + // If the ray was not absorbed scale the contribution based on ground plane color. + rayContribution *= groundPlaneRayColor; + } } - } - } + + if (!absorbedByGroundPlane) + { + // Did the ray segment hit any geometry? + if (rayPayload.hit()) + { + + // Increment t by the distance of the ray hit. + t += rayPayload.distance; + + // View direction is inverse of ray direction. + float3 V = -rayDirection; + + // Shading data and normal are taken from ray hit results. + ShadingData shading = shadingDataFromInstanceResult(rayPayload); + + // Were any layers hit? If this remains false, ray was not absorbed by any of the layer, including base layer.. + bool hitLayer = false; + + // Get the UV for the base layer (as the UVs in the shading struct will be over-written in layer loop.) + float2 baseLayerUV = shading.texCoord; + +// Only include code for layer loop if ENABLE_LAYERS is set to 1 (otherwise just set layerIndex to zero.) +#if ENABLE_LAYERS + // Get layer count from the instance header. + int layerCount = instanceLayerCount(rayPayload.instanceBufferOffset); + + // Iterate through all the layers in reverse order, ending with base layer. + for (int li = layerCount; li >= 0; li--) + { +#else + int li = 0; + { #endif - // Handle opacity (transparency) by stochastically determining whether the hit should be - // skipped, based on the luminance of the material opacity. If so, use the ray payload from a - // ray traced at the hit point, i.e. passing straight through the geometry, and return. - // NOTE: This could also be done with an any hit shader, with possibly better or worse - // performance. - float3 transparency = 1.0f - material.opacity; - float P = luminance(transparency); - if (random2D(rayPayload.radianceRay.rng).x < P) - { - // Trace a radiance ray with the unmodified ray direction, and the hit position. Use the - // background miss shader so that the background is sampled, since this is for transparency. - // NOTE: The geometric position (geomPosition) is used to avoid self-intersection. - rayPayload.radianceRay = traceRadianceRay(gScene, environment, shading.geomPosition, -V, - M_RAY_TMIN, depth, maxDepth, true, rayPayload.radianceRay.rng); - - // Scale the color components of the ray payload by transparency, and normalized by the - // probability of this segment being considered transparent. - rayPayload.radianceRay.scaleColor(transparency / P); - - // Nothing more to do. - return; - } - // Compute the NDC depth and view depth of the hit position: - // - NDC depth: Compute the clip space position using the supplied view-projection matrix, then - // divide the Z component by W, and remap from [-1.0, 1.0] to [0.0, 1.0]. - // - View Depth: For a primary ray, this is simply the T value of the ray. We don't use this - // value for non-primary rays; it is recorded here but not used. - float4 positionClip = mul(gFrameData.cameraViewProj, float4(shading.geomPosition, 1.0f)); - float depthNDC = (positionClip.z / positionClip.w + 1.0f) / 2.0f; - float depthView = RayTCurrent(); - - // Clamp roughness for the ray payload to a minimum, because the denoiser handles materials with - // low (near-zero) roughness poorly, leading to a noisy result. This addresses two separate but - // related issues: low-roughness metallic materials reflecting noisy surroundings, and low- - // roughness dielectric materials where the glossy lobe is sparsely sampled. - // NOTE: The roughness in the ray payload is used for denoising only. The material specular - // roughness (used in shading) is not modified here. - static const float kMinRoughness = 0.05f; - float clampedRoughness = max(material.specularRoughness, kMinRoughness); - - // Store initial data in the ray payload. - rayPayload.radianceRay.color = BLACK; - rayPayload.radianceRay.alpha = 1.0f; - rayPayload.radianceRay.extra = BLACK; - rayPayload.radianceRay.depthNDC = depthNDC; - rayPayload.radianceRay.depthView = depthView; - rayPayload.radianceRay.normal = shading.normal; - rayPayload.radianceRay.baseColor = material.baseColor; - rayPayload.radianceRay.roughness = clampedRoughness; - rayPayload.radianceRay.metalness = material.metalness; - rayPayload.radianceRay.indirect.clear(); - - // Shade with material emission. - rayPayload.radianceRay.extra += shadeEmission(material); - - // Shade with a randomly selected global distant light. Skip this if there are no distant - // lights. - if (gFrameData.lights.distantLightCount > 0) - { - // Choose a random distant light for this sample, by computing an index in range the 0 to - // (distantLightCount-1). - int lightIdx = int( - random2D(rayPayload.radianceRay.rng).x * float(gFrameData.lights.distantLightCount)); - - // Skip if the light has zero intensity. - if (gFrameData.lights.distantLights[lightIdx].colorAndIntensity.a > 0.0f) + // Compute material buffer offset, either for layer material or base layer. + int materialBufferOffset; + if (li == 0) + { + materialBufferOffset = instanceMaterialBufferOffset(rayPayload.instanceBufferOffset); + shading.texCoord = baseLayerUV; + } + else + { + int layerIndex = li - 1; + materialBufferOffset = instanceLayerMaterialBufferOffset(rayPayload.instanceBufferOffset, layerIndex); + shading.texCoord = computeLayerUV(rayPayload.instanceBufferOffset, layerIndex, shading); + } + + // Retrieve the shader index for the layer material. + // This is stored in the material header in the global material byte buffer, + // at a position indicated by instance's gMaterialBufferOffset. + int shaderIndex = materialShaderIndex(materialBufferOffset); + + // Was a normal generated by the material? + bool isGeneratedNormal = false; + + // Material normal (overriden by material setup, if material has normal map.) + float3 materialNormal = shading.normal; + + // Initialize the material using the shader index with the externally defined evaluate function. + // This function will be generated at runtime and linked with the rest of the code. + Material material = evaluateMaterialForShader(shaderIndex, shading, + materialBufferOffset, materialNormal, isGeneratedNormal); + + // Is this a primary ray? + if (isPrimaryRay) + { + // Set the output normal from the first hit. + output.normal = materialNormal; + + // Clamp the output roughness for the ray path to a minimum, because the denoiser + // handles materials with low (near-zero) roughness poorly, leading to a noisy result. This addresses + // two separate but related issues: low-roughness metallic materials reflecting + // noisy surroundings, and low- roughness dielectric materials where the glossy + // lobe is sparsely sampled. NOTE: The roughness in the ray payload is used for + // denoising only. The material specular roughness (used in shading) is not + // modified here. + static const float kMinRoughness = 0.05f; + float clampedRoughness = max(material.specularRoughness, kMinRoughness); + + // Set the output materal properties for the first hit. + output.metalness = material.metalness; + output.roughness = clampedRoughness; + output.baseColor = material.baseColor; + + // Set the output view-space depth from ray t. + output.depthView = t; + + // Set the output Normalized Device Coordinate (NDC) depth from hit position. + float4 positionClip = + mul(gFrameData.cameraViewProj, float4(shading.geomPosition, 1.0f)); + output.depthNDC = (positionClip.z / positionClip.w + 1.0f) / 2.0f; + } + + // Compute transparency from inverse of opacity. + float3 transparency = 1.0f - material.opacity; + + // Randomly choose if next ray segment hit any opaque geometry for this layer based on the transparency of the surface. + float P = luminance(transparency); + hitLayer = random2D(rng).x >= P; + float3 directionalShadowRayDirection = 0; + float3 directionalLightColor = 0; + bool hasDirectionalLight = false; + if (hitLayer) + { + // This ray struck opaque geometry, shade the collision as the next ray segment in the path. + + // If a new normal has been generated, transform it to world space and build a + // corresponding basis from it. + if (isGeneratedNormal) + { + shading.normal = materialNormal; + buildBasis(shading.normal, shading.tangent, shading.bitangent); + } + + // Add the emissive light for this collision to the direct light for this ray segement. + direct += shadeEmission(material); + + // Shade with a randomly selected global distant light. Skip this if there are + // no distant lights. + if (gFrameData.lights.distantLightCount > 0) + { + // Choose a random distant light for this sample, by computing an index in + // range the 0 to (distantLightCount-1). + int lightIdx = + int(random2D(rng).x * float(gFrameData.lights.distantLightCount)); + + // Skip if the light has zero intensity. + if (gFrameData.lights.distantLights[lightIdx].colorAndIntensity.a > 0.0f) + { + // Shade the light, computing a color and a shadow ray direction to be emitted later. + directionalLightColor = shadeDirectionalLight( + gFrameData.lights.distantLights[lightIdx].direction, + gFrameData.lights.distantLights[lightIdx].colorAndIntensity, + gFrameData.lights.distantLights[lightIdx].cosRadius, + material, shading, V, rng, directionalShadowRayDirection); + hasDirectionalLight = true; + } + } + + // The multiple importance sampling (MIS) environment shade function will return these values. + // They will be used to emit shadow rays for MIS material and light at the end of the loop. + int misLobeID; + bool misEmitsMaterialShadowRay; + float3 misMaterialResult = 0; + float3 misMaterialShadowRayDirection = 0; + bool misEmitsLightShadowRay; + float3 misLightResult = 0; + float3 misLightShadowRayDirection = 0; + + // Shade with the environment light with multiple importance sampling. + shadeEnvironmentLightMIS(environment, material, shading, V, rng, misLobeID, + misEmitsMaterialShadowRay, misMaterialResult, misMaterialShadowRayDirection, + misEmitsLightShadowRay, misLightResult, misLightShadowRayDirection + ); + + // Scale the ray contribution (for this ray segment and subsequent ones) by the inverse of + // the transparency, and normalized by the probability of this segment being considered opaque. + rayContribution *= material.opacity / (1.0f - P); + + // If the output alpha is zero and this was a transmission lobe ray, pass along an + // alpha of zero. If all the path segments beyond this hit have transparent or transmissive + // the final result will be zero, otherwise the alpha is will be one (opaque). + output.alpha = (output.alpha == 0.0f && rayType == TRANSMISSION_LOBE) ? 0.0f : 1.0f; + + // Sample the PDF for the current surface material, and compute the next ray direction and contribution. + nextRayType = setupIndirectLightRay(material, shading, V, rayDepth, maxTraceDepth, rng, + rayDirection, rayOrigin, nextRayContribution); + + // Emit all the shadows rays (for the directional light and MIS environment). + // Emitting them here at the end of loop avoids storing all the material state in registers, and is a huge performance optimisation. + // All the shadow rays use the same origin, calculated from geometric position or intersection position (depending on whether back-face hit.) + float3 shadowRayOrigin = dot(V, shading.normal) < 0.0f ? shading.geomPosition : shading.position; + + // Trace shadow ray for directional light, if we have one. + float3 lightVisibility; + if (hasDirectionalLight) + { + // Add directional result to the direct light. + lightVisibility = traceShadowRay(gScene, shadowRayOrigin, directionalShadowRayDirection, M_RAY_TMIN, onlyOpaqueShadowHits, rayDepth, maxTraceDepth); + direct += lightVisibility * directionalLightColor; + } + + // Trace shadow rays for material and light component of MIS environment (if we have them.) + if (misEmitsLightShadowRay || misEmitsMaterialShadowRay) + { + float3 misResult = 0; + if (misEmitsMaterialShadowRay) + { + lightVisibility = traceShadowRay(gScene, shadowRayOrigin, misMaterialShadowRayDirection, M_RAY_TMIN, onlyOpaqueShadowHits, rayDepth, maxTraceDepth); + misResult += lightVisibility * misMaterialResult; + } + if (misEmitsLightShadowRay) + { + lightVisibility = traceShadowRay(gScene, shadowRayOrigin, misLightShadowRayDirection, M_RAY_TMIN, onlyOpaqueShadowHits, rayDepth, maxTraceDepth); + misResult += lightVisibility * misLightResult; + } + + // If the material sampling for MIS environment returned a diffuse lobe, add the MIS result to the diffuse indirect result, otherwise add to glossy result. + // Set the hit distance to infinity (as the environment assumed to be at infinite distance.) + if (misLobeID == DIFFUSE_LOBE) + { + indirect.diffuse += misResult; + indirect.diffuseHitDist = INFINITY; + } + else + { + indirect.glossy += misResult; + indirect.glossyHitDist = INFINITY; + } + + } + + // If next ray not needed (as contribution is too small) terminate the path. + if (nextRayType == NO_SECONDARY_RAY) + pathEnded = true; + + // This is no longer a primary ray. + isPrimaryRay = false; + + // Break out of loop when ray is absorbed by a layer (if we are in a loop.) +#if ENABLE_LAYERS + break; +#endif + } + // Scale the ray contribution (for this ray segment and subsequent ones) by transparency, and normalized + // by the probability of this layer being considered transparent. + else + rayContribution *= transparency / P; + } + + // Handle case were no layers were hit, including base layer. + if (!hitLayer) + { + // No opaque geometry was hit in any layer, continue the ray path as if no collision occurred. + rayDirection = -V; + rayOrigin = shading.geomPosition; + + // All other ray variables remain the same. + + // Continue to the next path segment without applying contribution (as if this + // hit didn't happen.) + continue; + } + } + else + { + // No geometry was hit, shade the ray using the background color. + // If needed otherwise do nothing, as environment lighting was calculated during MIS environment shading. + if (shadeBackgroundOnMiss) + direct = evaluateEnvironment(environment, rayDirection, true); + + // Terminate the path. + pathEnded = true; + } + } + } + + // Scale all the lighting components for the current ray segment, indirect and direct, by the contribution. + direct *= rayContribution; + indirect.diffuse *= rayContribution; + indirect.glossy *= rayContribution; + + // How the result is accumulated depends on whether this ray is a primary ray. + if (isPrimaryRay) { - // Shade the light. - rayPayload.radianceRay.extra += - shadeDirectionalLight(gFrameData.lights.distantLights[lightIdx].direction, - gFrameData.lights.distantLights[lightIdx].colorAndIntensity, - gFrameData.lights.distantLights[lightIdx].cosRadius, gScene, material, shading, - V, isOpaqueShadowsEnabled, depth, maxDepth, rayPayload.radianceRay.rng); + // If it is a primary ray, accumulate direct and indirect components in to equivalent component in the output data. + output.direct = direct; + output.indirect = indirect; } + else + { + // If this secondary ray compute the total radiance from all the lighting components. + float3 radiance = direct + indirect.diffuse + indirect.glossy; + + // Accumulate the total radiance into diffuse or glossy component of output indirect + // component, based on lobe this ray segment originated from. + if (rayType == DIFFUSE_LOBE) + { + output.indirect.diffuse += radiance; + output.indirect.diffuseHitDist = t; + } + else + { + output.indirect.glossy += radiance; + output.indirect.glossyHitDist = t; + } + } + + // Scale the contribution to be used in subsequent segments. + rayContribution *= nextRayContribution; + + // Set the ray type for the next segment. + rayType = nextRayType; } - // Add the extra shading (computed above) to the color result. - // NOTE: Extra shading is stored separately in order to not be denoised, but rather to be - // combined with denoised shading later. It does not include shading from the environment light - // (below), as that may be denoised. - rayPayload.radianceRay.color += rayPayload.radianceRay.extra; + float3 finalRadiance = output.direct + output.indirect.diffuse + output.indirect.glossy; + float4 result = float4(finalRadiance, output.alpha); + + // Adjust the radiance of the sample, e.g. to perform corrections. + adjustRadiance(gFrameData.maxLuminance, gFrameData.isDisplayErrorsEnabled, result.rgb); + + // Store the result in the "result" output texture. If denoising is enabled, only the "direct" + // (non-denoised) shading is included in the result texture, as the rest of shading is stored in + // the denoising AOVs below. + result.rgb = gFrameData.isDenoisingEnabled ? output.direct : result.rgb; + gResult[screenCoords] = result; - // When denoising, primary rays for indirect lighting should not include the base color (also - // known as albedo, as used by diffuse lobes) in order to avoid blurring out color details. So - // the base color is set to white here and the color is added back after denoising. - if (gFrameData.isDenoisingEnabled && rayPayload.radianceRay.depth == 1) + // Store the NDC depth value, if enabled. The value is only stored if the first sample was + // computed (i.e. there is no previously stored value), or it is less than the previously stored + // value. + if (gFrameData.isDepthNDCEnabled && (sampleIndex == 0 || output.depthNDC < gDepthNDC[screenCoords])) { - material.baseColor = WHITE; + gDepthNDC[screenCoords] = output.depthNDC; } - // Shade with the environment light depending on the importance sampling type: - // - BSDF: The environment is treated as indirect light, and is evaluated in the miss shader. - // - Environment: Sample the environment light as direct lighting. - // - MIS: Use multiple importance sampling on both the material and the light. - // NOTE: For denoising purposes, "indirect" lighting includes the environment light. - if (IMPORTANCE_SAMPLING_MODE != IMPORTANCE_SAMPLING_BSDF) + // Prepare and store the data for the denoising AOVs, if enabled. + // NOTE: Only the data for the last sample is stored, with the expectation that the sample count + // will be one when these AOVs are enabled. + if (gFrameData.isDenoisingAOVsEnabled) { - float3 environmentRadiance = BLACK; - if (IMPORTANCE_SAMPLING_MODE == IMPORTANCE_SAMPLING_ENVIRONMENT) - { - // TODO: This does not currently contribute to indirect lighting for denoising purposes. - // That will require having the material evaluation return separate diffuse and glossy - // components. - environmentRadiance = shadeEnvironmentLightDirect(environment, gScene, material, - shading, V, isOpaqueShadowsEnabled, depth, maxDepth, rayPayload.radianceRay.rng); - } - else if (IMPORTANCE_SAMPLING_MODE == IMPORTANCE_SAMPLING_MIS) - { - environmentRadiance = shadeEnvironmentLightMIS(environment, gScene, material, shading, - V, isOpaqueShadowsEnabled, depth, maxDepth, rayPayload.radianceRay.rng, - rayPayload.radianceRay.indirect); - } - - // Add the radiance from the environment light to the color result. - rayPayload.radianceRay.color += environmentRadiance; + // Remap the normal components from [-1.0, 1.0] to [0.0, 1.0] for unsigned normalized + // output. + float3 normalEncoded = (output.normal + 1.0f) * 0.5f; + + // Prepare the diffuse and glossy radiance and hit distances. The hit distances are + // normalized relative to the scene size, so they are always in [0.0, 1.0]. + float diffuseHitDist = saturate(output.indirect.diffuseHitDist / gFrameData.sceneSize); + float glossyHitDist = saturate(output.indirect.glossyHitDist / gFrameData.sceneSize); + float4 diffusePacked = + float4(output.indirect.diffuse, diffuseHitDist); // TODO: use NRD packing + float4 glossyPacked = float4(output.indirect.glossy, glossyHitDist); // TODO: use NRD packing + + // Store the data for the denoising AOVs. + gDepthView[screenCoords] = output.depthView; + gNormalRoughness[screenCoords] = float4(normalEncoded, output.roughness); + gBaseColorMetalness[screenCoords] = float4(output.baseColor, output.metalness); + gDiffuse[screenCoords] = diffusePacked; + gGlossy[screenCoords] = glossyPacked; } +} - // Shade with indirect light from the surrounding scene (i.e. path tracing). - rayPayload.radianceRay.color += shadeIndirectLight(gScene, environment, material, shading, V, - depth, maxDepth, rayPayload.radianceRay.rng, rayPayload.radianceRay.alpha, - rayPayload.radianceRay.indirect); +// ================================================================================================= +// Shadow miss shader +// ================================================================================================= - // Scale the color components of the ray payload by opacity, and normalized by the probability - // of this segment being considered opaque. - // NOTE: The shading functions do not individually consider opacity, so that it can be handled - // in one place here. - rayPayload.radianceRay.scaleColor(material.opacity / (1.0f - P)); +// Shadow miss results sets the visibility to WHITE. +[shader("miss")]void ShadowMissShader(inout ShadowRayPayload rayPayload) +{ + rayPayload.visibility = WHITE; +} + +// ================================================================================================= +// Instance closest-hit shader +// ================================================================================================= + +// Closest hit shader for instance rays. Just returns the shading data. +[shader("closesthit")]void InstanceClosestHitShader( + inout InstanceRayPayload rayPayload, in BuiltInTriangleIntersectionAttributes hit) +{ + + // Get the interpolated vertex data for the hit triangle, at the hit barycentric coordinates. + uint triangleIndex = PrimitiveIndex(); + + // Compute barycentrics for hit. + float3 barycentrics = computeBarycentrics(hit); + + // Set the instance index. + rayPayload.instanceIndex = InstanceID(); + + // Return the shading data in the ray payload. + ShadingData shading = computeShadingData(gGeometry, triangleIndex, hit.barycentrics, ObjectToWorld3x4()); + rayPayload.geomPosition = shading.geomPosition; + rayPayload.texCoord = shading.texCoord; + rayPayload.objectNormal = shading.objectNormal; + rayPayload.objectPosition = shading.objectPosition; + rayPayload.indices = shading.indices; + rayPayload.bary2 = hit.barycentrics; + + // Return the ray distance from the collision t. + rayPayload.distance = RayTCurrent(); + + // Return the offset within global instance buffer. + rayPayload.instanceBufferOffset = gInstanceBufferOffset; } -#endif -// SHADOW_ANYHIT is defined to 1 for shaders with the shadow anyhit entry point. -#if SHADOW_ANYHIT - [shader("anyhit")] void ___Material___ShadowAnyHitShader( +[shader("anyhit")]void InstanceShadowAnyHitShader( inout ShadowRayPayload rayPayload, in BuiltInTriangleIntersectionAttributes hit) { -// If the materials for this shader are always opaque, keep this shader to a minimum to save -// compile and runtime performance. -#if SHADOW_ANYHIT_ALWAYS_OPAQUE - rayPayload.visibility = BLACK; - AcceptHitAndEndSearch(); -#else // If the material is opaque, set the visibility to zero, accept the hit, and stop searching for // hits, as the shadow ray is completely blocked. Doing this here is a performance optimization, @@ -241,32 +595,34 @@ } // Get the interpolated vertex data for the hit triangle, at the hit barycentric coordinates. - uint triangleIndex = PrimitiveIndex(); - float3 barycentrics = computeBarycentrics(hit); + uint triangleIndex = PrimitiveIndex(); ShadingData shading = - computeShadingData(gGeometry, triangleIndex, barycentrics, ObjectToWorld3x4()); - - // Initialize the material values. - // NOTE: This evaluates all material properties, when only visibility (or opacity) is needed. It - // may be more efficient to generate a dedicated function for getting the visibility alone. - float3 materialNormal = shading.normal; + computeShadingData(gGeometry, triangleIndex, hit.barycentrics, ObjectToWorld3x4()); + + // Get material buffer offset from instance data header. + int materialBufferOffset = instanceMaterialBufferOffset(gInstanceBufferOffset); + + // Retrieve the shader index for intersected surface material. + // This is stored in the material header in the global material byte buffer, + // at a position indicated by instance's gMaterialBufferOffset. + int shaderIndex = materialShaderIndex(materialBufferOffset); + + // Normal parameters generated by material's evaluate function (not used in shadow any hit shader.) + float3 materialNormal = shading.normal; bool isGeneratedNormal = false; - Material material = - initializeMaterial(shading, ObjectToWorld3x4(), materialNormal, isGeneratedNormal); - - // Compute the opacity at the current hit from the opacity, transmission, and transmission color - // properties. Then accumulate that (inverted) with the current visibility on the ray payload. - // NOTE: Visibility accumulation like this is order-independent, i.e. it does not matter what - // order intersections are processed. This is important because any hit shader intersections are - // processed in an arbitrary order. Also, while this does consider *transmission* to determine - // visibility, this technique does not support refraction (caustics), just simple straight - // shadow rays. + + // Initialize the material using the shader index with the externally defined evaluate function. + // This function will be generated at runtime and linked with the rest of the code. + Material material = evaluateMaterialForShader(shaderIndex, shading, + materialBufferOffset, materialNormal, isGeneratedNormal); + + // Compute opacity from material opacity and transmission. float3 opacity = material.opacity * (WHITE - material.transmission * material.transmissionColor); rayPayload.visibility *= 1.0f - opacity; // If the visibility is zero (opaque) at this point, accept this hit and stop searching for - // hits, as the shadow ray is now completely blocked. Otherwise, ignore the hit so that + // hits, as the shad ray is now completely blocked. Otherwise, ignore the hit so that // visibility can continue to be accumulated from other any hit intersections. if (isBlack(rayPayload.visibility)) { @@ -281,158 +637,4 @@ // useful for shadow rays. It does not happen here, because of the intrinsic function calls // above. -#endif -} -#endif - -// LAYER_MISS is defined to 1 for shaders with the layer miss entry point. -#if LAYER_MISS -[shader("miss")] void ___Material___LayerMissShader(inout RayPayload rayPayload) { - int depth = rayPayload.radianceRay.depth; - int maxDepth = gFrameData.traceDepth; - bool isOpaqueShadowsEnabled = gFrameData.isOpaqueShadowsEnabled; - - // Get the interpolated vertex data for the hit triangle, at the hit barycentric - // coordinates. - uint triangleIndex = rayPayload.primitiveIndex; - float3 barycentrics = computeBarycentrics(rayPayload.hit); - ShadingData shading = - computeShadingData(gGeometry, triangleIndex, barycentrics, rayPayload.objToWorld); - - // Initialize the view direction. - float3 V = rayPayload.viewDirection; - float3 materialNormal = shading.normal; - bool isGeneratedNormal; - - // Initialize the material values for repeated evaluations at the current shading point. - Material material = - initializeMaterial(shading, rayPayload.objToWorld, materialNormal, isGeneratedNormal); - Environment environment = prepareEnvironmentValues(); - - // If a new normal has been generated, transform it to world space and build a corresponding - // basis from it. - if (isGeneratedNormal) - { - shading.normal = materialNormal; - buildBasis(shading.normal, shading.tangent, shading.bitangent); - } - float3 transparency = 1.0f - material.opacity; - float P = luminance(transparency); - - if (random2D(rayPayload.radianceRay.rng).x < P) - { - // Nothing more to do. - return; - } - - // Compute the NDC depth and view depth of the hit position: - // - NDC depth: Compute the clip space position using the supplied view-projection matrix, then - // divide the Z component by W, and remap from [-1.0, 1.0] to [0.0, 1.0]. - // - View Depth: For a primary ray, this is simply the T value of the ray. We don't use this - // value for non-primary rays; it is recorded here but not used. - float4 positionClip = mul(gFrameData.cameraViewProj, float4(shading.geomPosition, 1.0f)); - float depthNDC = (positionClip.z / positionClip.w + 1.0f) / 2.0f; - float depthView = RayTCurrent(); - - // Clamp roughness for the ray payload to a minimum, because the denoiser handles materials with - // low (near-zero) roughness poorly, leading to a noisy result. This addresses two separate but - // related issues: low-roughness metallic materials reflecting noisy surroundings, and low- - // roughness dielectric materials where the glossy lobe is sparsely sampled. - // NOTE: The roughness in the ray payload is used for denoising only. The material specular - // roughness (used in shading) is not modified here. - static const float kMinRoughness = 0.05f; - float clampedRoughness = max(material.specularRoughness, kMinRoughness); - - // Store initial data in the ray payload. - rayPayload.radianceRay.color = BLACK; - rayPayload.radianceRay.alpha = 1.0f; - rayPayload.radianceRay.extra = BLACK; - rayPayload.radianceRay.depthNDC = depthNDC; - rayPayload.radianceRay.depthView = depthView; - rayPayload.radianceRay.normal = shading.normal; - rayPayload.radianceRay.baseColor = material.baseColor; - rayPayload.radianceRay.roughness = clampedRoughness; - rayPayload.radianceRay.metalness = material.metalness; - rayPayload.radianceRay.indirect.clear(); - - // Shade with material emission. - rayPayload.radianceRay.extra += shadeEmission(material); - - // Shade with a randomly selected global distant light. Skip this if there are no distant - // lights. - if (gFrameData.lights.distantLightCount > 0) - { - // Choose a random distant light for this sample, by computing an index in range the 0 to - // (distantLightCount-1). - int lightIdx = int( - random2D(rayPayload.radianceRay.rng).x * float(gFrameData.lights.distantLightCount)); - - // Skip if the light has zero intensity. - if (gFrameData.lights.distantLights[lightIdx].colorAndIntensity.a > 0.0f) - { - // Shade the light. - rayPayload.radianceRay.extra += - shadeDirectionalLight(gFrameData.lights.distantLights[lightIdx].direction, - gFrameData.lights.distantLights[lightIdx].colorAndIntensity, - gFrameData.lights.distantLights[lightIdx].cosRadius, gScene, material, shading, - V, isOpaqueShadowsEnabled, depth, maxDepth, rayPayload.radianceRay.rng); - } - } - - // Add the extra shading (computed above) to the color result. - // NOTE: Extra shading is stored separately in order to not be denoised, but rather to be - // combined with denoised shading later. It does not include shading from the environment light - // (below), as that may be denoised. - rayPayload.radianceRay.color += rayPayload.radianceRay.extra; - - // When denoising, primary rays for indirect lighting should not include the base color (also - // known as albedo, as used by diffuse lobes) in order to avoid blurring out color details. So - // the base color is set to white here and the color is added back after denoising. - if (gFrameData.isDenoisingEnabled && rayPayload.radianceRay.depth == 1) - { - material.baseColor = WHITE; - } - - // Shade with the environment light depending on the importance sampling type: - // - BSDF: The environment is treated as indirect light, and is evaluated in the miss shader. - // - Environment: Sample the environment light as direct lighting. - // - MIS: Use multiple importance sampling on both the material and the light. - // NOTE: For denoising purposes, "indirect" lighting includes the environment light. - if (IMPORTANCE_SAMPLING_MODE != IMPORTANCE_SAMPLING_BSDF) - { - float3 environmentRadiance = BLACK; - Environment environment = prepareEnvironmentValues(); - if (IMPORTANCE_SAMPLING_MODE == IMPORTANCE_SAMPLING_ENVIRONMENT) - { - // TODO: This does not currently contribute to indirect lighting for denoising purposes. - // That will require having the material evaluation return separate diffuse and glossy - // components. - environmentRadiance = shadeEnvironmentLightDirect(environment, gScene, material, - shading, V, isOpaqueShadowsEnabled, depth, maxDepth, rayPayload.radianceRay.rng); - } - else if (IMPORTANCE_SAMPLING_MODE == IMPORTANCE_SAMPLING_MIS) - { - environmentRadiance = shadeEnvironmentLightMIS(environment, gScene, material, shading, - V, isOpaqueShadowsEnabled, depth, maxDepth, rayPayload.radianceRay.rng, - rayPayload.radianceRay.indirect); - } - - // Add the radiance from the environment light to the color result. - rayPayload.radianceRay.color += environmentRadiance; - } - - // Shade with indirect light from the surrounding scene (i.e. path tracing). - rayPayload.radianceRay.color += shadeIndirectLight(gScene, environment, material, shading, V, - depth, maxDepth, rayPayload.radianceRay.rng, rayPayload.radianceRay.alpha, - rayPayload.radianceRay.indirect); - - // Scale the color components of the ray payload by opacity, and normalized by the probability - // of this segment being considered opaque. - // NOTE: The shading functions do not individually consider opacity, so that it can be handled - // in one place here. - rayPayload.radianceRay.scaleColor(material.opacity / (1.0f - P)); - - rayPayload.absorbedByLayer = true; } - -#endif diff --git a/Libraries/Aurora/Source/Shaders/Material.slang b/Libraries/Aurora/Source/Shaders/Material.slang index a3c78fa..e44c750 100644 --- a/Libraries/Aurora/Source/Shaders/Material.slang +++ b/Libraries/Aurora/Source/Shaders/Material.slang @@ -14,75 +14,7 @@ #ifndef __MATERIAL_H__ #define __MATERIAL_H__ -#include "Geometry.slang" - -#if DIRECTX -// DirectX backend uses the autogenerated byte-array accessors. -#include "DefaultMaterialUniformAccessors.slang" -#else -// HGI backend uses the autogenerated uniform buffers. -#include "DefaultMaterialUniformBuffer.slang" -#endif - -// The global sampler state, used by default for texture sampling. -[[vk::binding(6)]] SamplerState gDefaultSampler : register(s0); - -#if DIRECTX - -// Material properties are stored in this ByteAddressBuffer, and accessed with Material_XXX() -// functions, specific to the material type. They are stored in the register space after the vertex -// and texture buffers. -// NOTE: An untyped ByteAddressBuffer is used instead of a typed constant buffer (e.g. -// ConstantBuffer) because it is not possible in DirectX to have constant buffers with -// different types assigned to the same register. -ByteAddressBuffer gMaterialConstants : register(t10, space1); - -// Textures, with hardcoded names. They are stored in the register space after the vertex buffers. -Texture2D gBaseColorTexture : register(t5, space1); -Texture2D gSpecularRoughnessTexture : register(t6, space1); -Texture2D gNormalTexture : register(t7, space1); -Texture2D gEmissionColorTexture : register(t8, space1); -Texture2D gOpacityTexture : register(t9, space1); - -// Samplers for base color and opacity. -// TODO: Add for other textures. -SamplerState gBaseColorSampler : register(s1); -SamplerState gOpacitySampler : register(s2); - -float4 sampleBaseColorTexture(float2 uv, float level) -{ - return gBaseColorTexture.SampleLevel( - gBaseColorSampler, uv, level); // Use the base color sampler, not default sampler. -} -float4 sampleSpecularRoughnessTexture(float2 uv, float level) -{ - return gSpecularRoughnessTexture.SampleLevel( - gDefaultSampler, uv, level); // Use the default sampler. -} -float4 sampleEmissionColorTexture(float2 uv, float level) -{ - return gEmissionColorTexture.SampleLevel( - gDefaultSampler, uv, level); // Use the default sampler. -} -float4 sampleOpacityTexture(float2 uv, float level) -{ - return gOpacityTexture.SampleLevel( - gOpacitySampler, uv, level); // Use the opacity sampler, not default sampler. -} -float4 sampleNormalTexture(float2 uv, float level) -{ - return gNormalTexture.SampleLevel(gDefaultSampler, uv, level); // Use the default sampler. -} - -#else -// Vulkan GLSL versions are forward declared and implemented in raw GLSL suffix file. -MaterialConstants getMaterial(); -float4 sampleBaseColorTexture(float2 uv, float level); -float4 sampleSpecularRoughnessTexture(float2 uv, float level); -float4 sampleEmissionColorTexture(float2 uv, float level); -float4 sampleOpacityTexture(float2 uv, float level); -float4 sampleNormalTexture(float2 uv, float level); -#endif +#include "Globals.slang" // Material values used during material evaluation. struct Material @@ -124,165 +56,6 @@ struct Material bool isOpaque; }; -// Normal map spaces definitions. -#define TANGENT_SPACE 0 -#define OBJECT_SPACE 1 - -// Rotate a 2D vector by given number of degrees. -// Based on MaterialX mx_rotate_vector2 functionn. -float2 rotateUV(float2 uv, float amountDegrees) -{ - float rotationRadians = radians(amountDegrees); - float sa = sin(rotationRadians); - float ca = cos(rotationRadians); - return float2(ca * uv.x + sa * uv.y, -sa * uv.x + ca * uv.y); -} - -// Rotate a 2D vector by given number of degrees. -// Based on MaterialX NG_place2d_vector2 function. -float2 applyUVTransform(float2 uv, float2 pivot, float2 scale, float rotate, float2 offset) -{ - float2 subpivot = uv - pivot; - float2 scaled = subpivot / scale; - float2 rotated = rotateUV(scaled, rotate); - float2 translated = rotated - offset; - float2 addpivot = translated + pivot; - return addpivot; -} - -// Calculate a per-pixel normal from a normal map texel value. -// NOTE: This is based on the MaterialX function mx_normalmap(). -float3 calculateNormalFromMap(float3 texelValue, int space, float scale, float3 N, float3 T) -{ - // Remap texel components from [0.0, 1.0] to [-1.0, 1.0]. - float3 v = texelValue * 2.0 - 1.0; - - // If the texel normal is in tangent space, transform it to the coordinate system defined by N - // and T. - if (space == TANGENT_SPACE) - { - float3 B = normalize(cross(N, T)); - return normalize(T * v.x * scale + B * v.y * scale + N * v.z); - } - - // Otherwise the texel normal is in object space, and is simply normalized. - else - { - return normalize(v); - } -} - -// Initializes the full set of property values for a material, for the specified shading data. -Material initializeDefaultMaterial( - ShadingData shading, float3x4 objToWorld, out float3 materialNormal, out bool isGeneratedNormal) -{ -#if !DIRECTX - MaterialConstants gMaterialConstants = getMaterial(); -#endif - - // Copy the constant values to the material from the constant buffer. - Material material; - material.base = Material_base(gMaterialConstants); - material.baseColor = Material_baseColor(gMaterialConstants); - material.diffuseRoughness = Material_diffuseRoughness(gMaterialConstants); - material.metalness = Material_metalness(gMaterialConstants); - material.specular = Material_specular(gMaterialConstants); - material.specularColor = Material_specularColor(gMaterialConstants); - material.specularRoughness = Material_specularRoughness(gMaterialConstants); - material.specularIOR = Material_specularIOR(gMaterialConstants); - material.specularAnisotropy = Material_specularAnisotropy(gMaterialConstants); - material.specularRotation = Material_specularRotation(gMaterialConstants); - material.transmission = Material_transmission(gMaterialConstants); - material.transmissionColor = Material_transmissionColor(gMaterialConstants); - material.subsurface = Material_subsurface(gMaterialConstants); - material.subsurfaceColor = Material_subsurfaceColor(gMaterialConstants); - material.subsurfaceRadius = Material_subsurfaceRadius(gMaterialConstants); - material.subsurfaceScale = Material_subsurfaceScale(gMaterialConstants); - material.subsurfaceAnisotropy = Material_subsurfaceAnisotropy(gMaterialConstants); - material.sheen = Material_sheen(gMaterialConstants); - material.sheenColor = Material_sheenColor(gMaterialConstants); - material.sheenRoughness = Material_sheenRoughness(gMaterialConstants); - material.coat = Material_coat(gMaterialConstants); - material.coatColor = Material_coatColor(gMaterialConstants); - material.coatRoughness = Material_coatRoughness(gMaterialConstants); - material.coatAnisotropy = Material_coatAnisotropy(gMaterialConstants); - material.coatRotation = Material_coatRotation(gMaterialConstants); - material.coatIOR = Material_coatIOR(gMaterialConstants); - material.coatAffectColor = Material_coatAffectColor(gMaterialConstants); - material.coatAffectRoughness = Material_coatAffectRoughness(gMaterialConstants); - material.emission = Material_emission(gMaterialConstants); - material.emissionColor = Material_emissionColor(gMaterialConstants); - material.opacity = Material_opacity(gMaterialConstants); - material.thinWalled = Material_thinWalled(gMaterialConstants); - - // Sample base color from a texture if necessary. - float4 texCoord = float4(shading.texCoord, 0.0f, 1.0f); - if (Material_hasBaseColorTex(gMaterialConstants)) - { - float2 uv = - applyUVTransform(shading.texCoord, Material_baseColorTexPivot(gMaterialConstants), - Material_baseColorTexScale(gMaterialConstants), - Material_baseColorTexRotation(gMaterialConstants), - Material_baseColorTexOffset(gMaterialConstants)); - material.baseColor = sampleBaseColorTexture(uv, 0.0f).rgb; - } - - // Sample specular roughness from a texture if necessary. - if (Material_hasSpecularRoughnessTex(gMaterialConstants)) - { - float2 uv = applyUVTransform(shading.texCoord, - Material_specularRoughnessTexPivot(gMaterialConstants), - Material_specularRoughnessTexScale(gMaterialConstants), - Material_specularRoughnessTexRotation(gMaterialConstants), - Material_specularRoughnessTexOffset(gMaterialConstants)); - - material.specularRoughness = sampleSpecularRoughnessTexture(uv, 0.0f).r; - } - - // Sample emission color from a texture if necessary. - if (Material_hasEmissionColorTex(gMaterialConstants)) - { - float2 uv = applyUVTransform(shading.texCoord, - Material_emissionColorTexPivot(gMaterialConstants), - Material_emissionColorTexScale(gMaterialConstants), - Material_emissionColorTexRotation(gMaterialConstants), - Material_emissionColorTexOffset(gMaterialConstants)); - material.emissionColor = sampleEmissionColorTexture(uv, 0.0f).rgb; - } - - // Sample opacity from a texture if necessary. - if (Material_hasOpacityTex(gMaterialConstants)) - { - float2 uv = applyUVTransform(shading.texCoord, Material_opacityTexPivot(gMaterialConstants), - Material_opacityTexScale(gMaterialConstants), - Material_opacityTexRotation(gMaterialConstants), - Material_opacityTexOffset(gMaterialConstants)); - material.opacity = sampleOpacityTexture(uv, 0.0f).rgb; - } - - // Sample a normal from the normal texture, convert it to an object-space normal, transform to - // world space, and store it in the output value. - isGeneratedNormal = false; - if (Material_hasNormalTex(gMaterialConstants)) - { - float2 uv = applyUVTransform(shading.texCoord, Material_normalTexPivot(gMaterialConstants), - Material_normalTexScale(gMaterialConstants), - Material_normalTexRotation(gMaterialConstants), - Material_normalTexOffset(gMaterialConstants)); - float3 normalTexel = sampleNormalTexture(uv, 0.0f).rgb; - float3 objectSpaceNormal = calculateNormalFromMap( - normalTexel, TANGENT_SPACE, 1.0, shading.normal, shading.tangent); - materialNormal = normalize(mul((float3x3)objToWorld, objectSpaceNormal)); - - isGeneratedNormal = true; - } - - // Copy the base color to the (internal) metal color. - material.metalColor = material.baseColor; - - return material; -} - Material defaultMaterial() { // Copy the constant values to the material from the constant buffer. diff --git a/Libraries/Aurora/Source/Shaders/Options.slang b/Libraries/Aurora/Source/Shaders/Options.slang new file mode 100644 index 0000000..93169a4 --- /dev/null +++ b/Libraries/Aurora/Source/Shaders/Options.slang @@ -0,0 +1,4 @@ +#define RUNTIME_COMPILE_EVALUATE_MATERIAL_FUNCTION 1 +#define ENABLE_LAYERS 1 +#define DISTANCE_UNIT 3 +#define USE_REFERENCE_BSDF 0 \ No newline at end of file diff --git a/Libraries/Aurora/Source/Shaders/PathTracingCommon.slang b/Libraries/Aurora/Source/Shaders/PathTracingCommon.slang index c6f59b5..45cc1e4 100644 --- a/Libraries/Aurora/Source/Shaders/PathTracingCommon.slang +++ b/Libraries/Aurora/Source/Shaders/PathTracingCommon.slang @@ -31,146 +31,7 @@ #include "RayTrace.slang" #include "Sampling.slang" -// ================================================================================================= -// Global Variables - For All Shaders -// ================================================================================================= - -// The top-level acceleration structure with the scene contents. -[[vk::binding(0)]] RaytracingAccelerationStructure gScene : register(t0); - -// Constant buffers of sample and per-frame values. -[[vk::binding(4)]] ConstantBuffer gSampleData : register(b0); -[[vk::binding(2)]] ConstantBuffer gFrameData : register(b1); - -// Environment data. -[[vk::binding(5)]] ConstantBuffer gEnvironmentConstants : register(b2); -StructuredBuffer gEnvironmentAliasMap : register(t1); -[[vk::binding(8)]] Texture2D gEnvironmentLightTexture : register(t2); -[[vk::binding(7)]] Texture2D gEnvironmentBackgroundTexture : register(t3); -ConstantBuffer gGroundPlane : register(b3); -RaytracingAccelerationStructure gNullScene : register(t4); - -// ================================================================================================= -// Ray Gen Shader Variables -// ================================================================================================= - -// The output textures (AOVs) as UAVs. -[[vk::binding(1)]] RWTexture2D gResult : register(u0); -RWTexture2D gDepthNDC : register(u1); -RWTexture2D gDepthView : register(u2); -RWTexture2D gNormalRoughness : register(u3); -RWTexture2D gBaseColorMetalness : register(u4); -RWTexture2D gDiffuse : register(u5); -RWTexture2D gGlossy : register(u6); - -// ================================================================================================= -// Radiance Hit Shader Variables -// ================================================================================================= - -// The maximum number of supported material layers, must match value in -// PTLayerIndexTable::kMaxMaterialLayers C++ code. -#define kMaxMaterialLayers 64 - -// The layer material shader IDs. -struct MaterialLayerShaderIDs -{ - // Shader indices (must be stored as floattors due to HLSL packing rules). - int4 shaderIDs[kMaxMaterialLayers / WORD_SIZE]; -}; - -#if DIRECTX -// Geometry data, for the current instance only. -ByteAddressBuffer gIndices : register(t0, space1); -ByteAddressBuffer gPositions : register(t1, space1); -ByteAddressBuffer gNormals : register(t2, space1); -ByteAddressBuffer gTangents : register(t3, space1); -ByteAddressBuffer gTexCoords : register(t4, space1); - -// NOTE: Material variables are inserted at register(b1, space1) between gGeometryMetadata and -// gMaterialLayerIDs by Material.slang. -cbuffer gGeometryMetadata : register(b0, space1) -{ - bool gHasNormals; // Are there normals? - bool gHasTangents; // Are there tangents? - bool gHasTexCoords; // Are there texture coordinates? - int gMaterialLayerCount; // Number of material layer miss shaders. - bool gIsOpaque; // Is the geometry opaque? -} - -// To hide DX-Vulkan differences, expose geometry access using functions. -uint3 getIndicesForTriangle(int triangleIndex) { - return gIndices.Load3((triangleIndex * 3) * 4); -} - -float3 getPositionForVertex(int vertexIndex) -{ - return asfloat(gPositions.Load3(vertexIndex * 3 * 4)); -} - -float3 getNormalForVertex(int vertexIndex) -{ - return asfloat(gNormals.Load3(vertexIndex * 3 * 4)); -} - -float3 getTangentForVertex(int vertexIndex) -{ - return asfloat(gTangents.Load3(vertexIndex * 3 * 4)); -} - -float2 getTexCoordForVertex(int vertexIndex) -{ - return asfloat(gTexCoords.Load2(vertexIndex * 2 * 4)); -} - -bool instanceHasNormals() -{ - return gHasNormals; -} -bool instanceHasTangents() -{ - return gHasTangents; -} -bool instanceHasTexCoords() -{ - return gHasTexCoords; -} - -#else - -// Forward declare these functions on Vulkan GLSL. As we need a platform-specific suffix file containing the implementation of these functions. -uint3 getIndicesForTriangle(int bufferLocation); -float3 getPositionForVertex(int bufferLocation); -float3 getNormalForVertex(int bufferLocation); -float3 getTangentForVertex(int bufferLocation); -float2 getTexCoordForVertex(int bufferLocation); -bool instanceHasNormals(); -bool instanceHasTangents(); -bool instanceHasTexCoords(); - -#endif - -// Slang interface implementation to handle geometry access in platform-independent way. -struct Geometry : IGeometry -{ - uint3 getIndices(int triangleIndex) { return getIndicesForTriangle(triangleIndex); } - float3 getPosition(int vertexIndex) { return getPositionForVertex(vertexIndex); } - float3 getNormal(int vertexIndex) { return getNormalForVertex(vertexIndex); } - float3 getTangent(int vertexIndex) { return getTangentForVertex(vertexIndex); } - float2 getTexCoord(int vertexIndex) { return getTexCoordForVertex(vertexIndex); } - bool hasTexCoords() { return instanceHasTexCoords(); } - bool hasNormals() { return instanceHasNormals(); } - bool hasTangents() { return instanceHasTangents(); } -} gGeometry; - -// Constant buffer for layer material shader IDs. -ConstantBuffer gMaterialLayerIDs : register(b2, space1); - -// Get the layer material index for given layer. -int getMaterialLayerIndex(int layer) -{ - // TODO: Optimize the float->scalar look-up. - return gMaterialLayerIDs.shaderIDs[layer / 4][layer % 4]; -} +#include "GlobalPipelineState.slang" // ================================================================================================= // Utility Functions @@ -180,12 +41,12 @@ int getMaterialLayerIndex(int layer) Environment prepareEnvironmentValues() { Environment values; - values.constants = gEnvironmentConstants; + values.constants = gEnvironmentConstants; values.backgroundTexture = gEnvironmentBackgroundTexture; - values.sampler = gDefaultSampler; + values.sampler = gSamplerArray[0]; values.lightTexture = gEnvironmentLightTexture; #if DIRECTX - values.aliasMap = gEnvironmentAliasMap; + values.aliasMap = gEnvironmentAliasMap; #endif return values; } diff --git a/Libraries/Aurora/Source/Shaders/RadianceMissShader.slang b/Libraries/Aurora/Source/Shaders/RadianceMissShader.slang deleted file mode 100644 index fc34650..0000000 --- a/Libraries/Aurora/Source/Shaders/RadianceMissShader.slang +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2023 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "PathTracingCommon.slang" - -// The radiance miss shader, which evaluates the environment as an environment light. -[shader("miss")] -void RadianceMissShader(inout RayPayload rayPayload) -{ - // Initialize the radiance ray payload for a miss. - rayPayload.radianceRay.clear(); - - // For BSDF importance sampling, evaluate the environment as a light for the final segment of - // the current path. For any other importance sampling, the environment will be evaluated or - // sampled for shading at each hit (not here). - float3 color = BLACK; - if (IMPORTANCE_SAMPLING_MODE == IMPORTANCE_SAMPLING_BSDF) - { - // Evaluate the environment, as a light. - Environment environment = prepareEnvironmentValues(); - color = evaluateEnvironment(environment, WorldRayDirection(), false); - } - - // Store the environment color. - // NOTE: The miss result will not be denoised, so it is included in the "extra" shading. - rayPayload.radianceRay.color = color; - rayPayload.radianceRay.extra = color; -} \ No newline at end of file diff --git a/Libraries/Aurora/Source/Shaders/RayGenShader.slang b/Libraries/Aurora/Source/Shaders/RayGenShader.slang deleted file mode 100644 index bfe270a..0000000 --- a/Libraries/Aurora/Source/Shaders/RayGenShader.slang +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2023 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Prefix containing the common code used by all material types. -#include"PathTracingCommon.slang" - -// The ray generation shader. -[shader("raygeneration")] void RayGenShader() { - int maxDepth = gFrameData.traceDepth; - - // Get the dispatch dimensions (screen size), dispatch index (screen coordinates), and sample - // index. - uint2 screenSize = DispatchRaysDimensions().xy; - uint2 screenCoords = DispatchRaysIndex().xy; - uint sampleIndex = gSampleData.sampleIndex; - - // Initialize a random number generator, so that each sample and pixel gets a unique seed. - Random rng = initRand(sampleIndex, screenSize, screenCoords); - - // Compute a camera ray (origin and direction) for the current screen coordinates. This applies - // a random offset to support antialiasing and depth of field. - float3 origin; - float3 dir; - computeCameraRay(screenCoords, screenSize, gFrameData.cameraInvView, gFrameData.viewSize, - gFrameData.isOrthoProjection, gFrameData.focalDistance, gFrameData.lensRadius, rng, origin, - dir); - - // Trace a radiance ray, and use the returned radiance (color) and alpha. - // NOTE: Use the background miss shader index for these primary (eye) rays. - Environment environment = prepareEnvironmentValues(); - RadianceRayPayload rayPayload = - traceRadianceRay(gScene, environment, origin, dir, 0.0f, 0, maxDepth, true, rng); - float4 result = float4(rayPayload.color, rayPayload.alpha); -#if DIRECTX - // Add the contribution of the ground plane, if enabled. - if (gGroundPlane.enabled) - { - // Shade the ground plane. - Environment environment = prepareEnvironmentValues(); - float4 groundPlaneResult = shadeGroundPlane(gGroundPlane, gScene, environment, origin, dir, - rayPayload.depthView, gFrameData.isOpaqueShadowsEnabled, maxDepth, rng); - - // Blend the ground plane result with the radiance and "extra" shading. The extra shading is - // not itself denoised but is added to denoised output. - // TODO: The ground plane is currently treated as extra shading and is not denoised; it - // needs to contribute to the denoising input in order to be denoised. - result.rgb = lerp(result.rgb, groundPlaneResult.rgb, groundPlaneResult.a); - rayPayload.extra = lerp(rayPayload.extra, groundPlaneResult.rgb, groundPlaneResult.a); - } -#endif - - // Adjust the radiance of the sample, e.g. to perform corrections. - adjustRadiance(gFrameData.maxLuminance, gFrameData.isDisplayErrorsEnabled, result.rgb); - - // Store the result in the "result" output texture. If denoising is enabled, only the "extra" - // (non-denoised) shading is included in the result texture, as the rest of shading is stored in - // the denoising AOVs below. - result.rgb = gFrameData.isDenoisingEnabled ? rayPayload.extra : result.rgb; - gResult[screenCoords] = result; - -#if DIRECTX - // Store the NDC depth value, if enabled. The value is only stored if the first sample was - // computed (i.e. there is no previously stored value), or it is less than the previously stored - // value. - float depthNDC = rayPayload.depthNDC; - if (gFrameData.isDepthNDCEnabled && (sampleIndex == 0 || depthNDC < gDepthNDC[screenCoords])) - { - gDepthNDC[screenCoords] = depthNDC; - } - - // Prepare and store the data for the denoising AOVs, if enabled. - // NOTE: Only the data for the last sample is stored, with the expectation that the sample count - // will be one when these AOVs are enabled. - if (gFrameData.isDenoisingAOVsEnabled) - { - // Remap the normal components from [-1.0, 1.0] to [0.0, 1.0] for unsigned normalized - // output. - float3 normalEncoded = (rayPayload.normal + 1.0f) * 0.5f; - - // Prepare the diffuse and glossy radiance and hit distances. The hit distances are - // normalized relative to the scene size, so they are always in [0.0, 1.0]. - IndirectOutput indirect = rayPayload.indirect; - float diffuseHitDist = saturate(indirect.diffuseHitDist / gFrameData.sceneSize); - float glossyHitDist = saturate(indirect.glossyHitDist / gFrameData.sceneSize); - float4 diffusePacked = float4(indirect.diffuse, diffuseHitDist); // TODO: use NRD packing - float4 glossyPacked = float4(indirect.glossy, glossyHitDist); // TODO: use NRD packing - - // Store the data for the denoising AOVs. - gDepthView[screenCoords] = rayPayload.depthView; - gNormalRoughness[screenCoords] = float4(normalEncoded, rayPayload.roughness); - gBaseColorMetalness[screenCoords] = float4(rayPayload.baseColor, rayPayload.metalness); - gDiffuse[screenCoords] = diffusePacked; - gGlossy[screenCoords] = glossyPacked; - } -#endif - -} diff --git a/Libraries/Aurora/Source/Shaders/RayTrace.slang b/Libraries/Aurora/Source/Shaders/RayTrace.slang index 0490593..772ce63 100644 --- a/Libraries/Aurora/Source/Shaders/RayTrace.slang +++ b/Libraries/Aurora/Source/Shaders/RayTrace.slang @@ -29,11 +29,8 @@ struct IndirectOutput float glossyHitDist; // Clears the indirect output to values expected for a miss. -#if 1 //! DIRECTX [mutating] -#endif - void - clear() + void clear() { diffuse = BLACK; diffuseHitDist = INFINITY; @@ -42,127 +39,70 @@ struct IndirectOutput } }; -// The radiance ray payload structure. -// NOTE: This is currently 28 floats in size. -struct RadianceRayPayload -{ - // The color value for outgoing radiance from the path. - float3 color; - - // The opacity accumulated along the path. This is stochastic, i.e. a zero or one value for the - // entire path, where zero means transparent or transmissive for the entire path and one means - // at least one opaque segment on the path. - float alpha; - - // The color value for outgoing radiance that is not intended to be denoised. This includes - // shading from material emission, discrete lights, and the background. Only data from the first - // hit (primary rays) is stored here. - float3 extra; - - // The NDC depth is the Z component of NDC space, i.e. after dividing the clip space position by - // W. This is the value normally stored in the depth buffer for rasterization. The projection - // matrix must generate Z components in [-1.0, 1.0], which is remapped to [0.0, 1.0] as the NDC - // depth. - float depthNDC; - - // The view depth is the distance from the eye to the hit position. This value is in world - // units. - float depthView; - - // The normal in world space of the first hit surface. This is based on the material, and may - // include geometric normals perturbed by a bump map. - float3 normal; - - // The albedo of the first hit surface, as defined by Standard Surface. - float3 baseColor; - - // The roughness of the first hit surface, as defined by Standard Surface. - float roughness; - - // The metalness of the first hit surface, as defined by Standard Surface. - float metalness; - - // Outgoing radiance and hit distances for diffuse and glossy lobes. This is based on shading - // from indirect light or sampled environment light, and only data from the first hit (primary - // rays) is stored here. This is used for denoising. - IndirectOutput indirect; - - // The ray depth or number of segments in the path at this point in path evaluation. This is - // use to prevent a path from exceeding a certain length. At the first intersection, the depth - // value is 1, indicating a first hit (primary ray). - int depth; - - // Random number generation state, which is updated during path evaluation so that different - // random numbers are generated along the path. - Random rng; - - // Clears the radiance ray payload to values expected for a miss. - // NOTE: The normal value (-1.0, -1.0, -1.0) will appear as black when remapped for output. - // NDC depth is typically in the range [-1.0, 1.0] or [0.0, 1.0] so a max value of 1.0 is used, - // while view depth is in world units so has a max value of INF. -#if 1//!DIRECTX - [mutating] -#endif - void - clear() - { - color = BLACK; - alpha = 0.0; - extra = BLACK; - depthNDC = 1.0f; - depthView = INFINITY; - normal = -1.0f; - baseColor = BLACK; - roughness = 0.1f; - metalness = 0.1f; - indirect.clear(); - }; - - // Scales the radiance ray payload color components by the specified values. -#if 1//!DIRECTX +// Instance ray payload. Used by all closest hit shaders. +struct InstanceRayPayload { + float3 geomPosition; // the geometric position, interpolated from adjancent vertices + float2 texCoord; // the shading texture coordinates, interpolated from adjancent vertices + float3 objectNormal; // the shading position in object space (not world space). Used only in + // materialX generated code. + float3 objectPosition; // the shading normal in object space (not world space). Used only in + // materialX generated code. + // Offset withing instance data buffer for instance. + int instanceBufferOffset; + // Triangle indices for hit. + uint3 indices; + // Barycentric coordinates for hit. + float2 bary2; + // Distance from ray origin collision occurred. + float distance = INFINITY; + // BLAS index of instance that was hit (or -1 if not hit) + int instanceIndex; + + // Clears the indirect output to values expected for a miss. [mutating] -#endif - void scaleColor(float3 scale) + void clear() { - color *= scale; - extra *= scale; - baseColor *= scale; - indirect.diffuse *= scale; - indirect.glossy *= scale; + instanceIndex = -1; + distance = INFINITY; + geomPosition = 0.0; + texCoord = 0.0; + objectNormal = 0.0; + objectPosition = 0.0; + } + bool hit() { + return instanceIndex>=0; } + }; -// The shared ray payload that must be used by all TraceRay calls in the ray tree. -// NOTE: See: https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#ray-payload-structure -struct RayPayload +ShadingData shadingDataFromInstanceResult(InstanceRayPayload result) { - // ================================================================================================= - // Radiance ray payload. - // Used by all shaders (radiance, shadow miss, and layer miss) - // ================================================================================================= - - // The incoming radiance ray data. - RadianceRayPayload radianceRay; - - // ================================================================================================= - // Hit and transform information from previous intersection. - // Used by ray miss shader. - // ================================================================================================= - - // The intersection data for radiance hit on base layer. - BuiltInTriangleIntersectionAttributes hit; - // The object-to-world matrix for base layer instance. - // TODO: This could be moved to constant buffer. - float3x4 objToWorld; - // The view direction for parent radiance ray. - float3 viewDirection; - // The primitive index for parent ray collision. - uint primitiveIndex; - // The current T for parent ray collision. - float currentT; - // Set to true if the layer absorbs the ray. - bool absorbedByLayer; -}; + // Fill in the shading data from hit result. + ShadingData shading; + shading.geomPosition = result.geomPosition; + shading.texCoord = result.texCoord; + shading.objectNormal = result.objectNormal; + shading.objectPosition = result.objectPosition; + shading.barycentrics = computeBarycentrics(result.bary2); + shading.indices = result.indices; + + // Get transform matrix from instance buffer. + shading.objToWorld = transformMatrixForBLAS(result.instanceIndex); + + // Transform object space attributes. + shading.position = mul(shading.objToWorld, float4(shading.objectPosition, 1.0f)); + shading.normal = normalize(mul((float3x3) shading.objToWorld, shading.objectNormal)); + + // As we don't currently support user-supplied tangents, we always use arbitrary tangent computed from normal. + float3 up = + abs(shading.normal.y) < 0.999f ? float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f); + shading.tangent = normalize(cross(shading.normal, up)); + + // Generate bitangent from normal and tangent. + shading.bitangent = computeBitangent(shading.normal, shading.tangent); + + return shading; +} // Computes the origin and direction of a ray at the specified screen coordinates, using the // specified view parameters (orientation, position, and FOV). @@ -222,23 +162,13 @@ void computeCameraRay(float2 screenCoords, float2 screenSize, float4x4 invView, } // Traces a ray in the specified direction, returning the radiance from that direction. -RadianceRayPayload traceRadianceRay(RaytracingAccelerationStructure scene, Environment environment, - float3 origin, float3 dir, float tMin, int depth, int maxDepth, bool useBackgroundMiss, - inout Random rng) +bool traceInstanceRay(RaytracingAccelerationStructure scene, + float3 origin, float3 dir, float tMin, inout InstanceRayPayload rayPayload) { - // If the maximum trace recursion depth has been reached, simply return black. - if (depth == maxDepth) - { - RadianceRayPayload rayPayload; - rayPayload.color = evaluateEnvironment(environment, dir, useBackgroundMiss); - - return rayPayload; - } // Set the force opaque ray flag to treat all objects as opaque, so that the any hit shader is - // not called. Also set the radiance or background miss shader. + // not called. uint rayFlags = RAY_FLAG_FORCE_OPAQUE; - uint missShaderIndex = useBackgroundMiss ? kMissBackground : kMissRadiance; // Prepare the ray. RayDesc ray; @@ -247,10 +177,7 @@ RadianceRayPayload traceRadianceRay(RaytracingAccelerationStructure scene, Envir ray.TMin = tMin; ray.TMax = INFINITY; - // Prepare the ray payload. - RayPayload rayPayload; - rayPayload.radianceRay.depth = depth + 1; - rayPayload.radianceRay.rng = rng; + rayPayload.clear(); // Trace the ray. TraceRay(scene, // acceleration structure to be traced against @@ -258,16 +185,14 @@ RadianceRayPayload traceRadianceRay(RaytracingAccelerationStructure scene, Envir 0xFF, // instance mask 0, // ray contribution to hit group index 0, // multiplier for geometry contribution to hit group index - missShaderIndex, // miss shader index + kMissNull, // miss shader index (always null shader for instance rays) ray, // ray to be traced rayPayload); // in/out data for shaders invoked during tracing - // Update the random number generator. - rng = rayPayload.radianceRay.rng; - - return rayPayload.radianceRay; + return rayPayload.hit(); } + // The payload for shadow rays. struct ShadowRayPayload { @@ -277,7 +202,7 @@ struct ShadowRayPayload // Traces a shadow ray for the specified sample position and light direction, returning the // visibiliity of the light in that direction. float3 traceShadowRay(RaytracingAccelerationStructure scene, float3 origin, float3 L, float tMin, - bool isOpaque, int depth, int maxDepth) + bool onlyOpaqueHits, int depth, int maxDepth) { // If the maximum trace recursion depth has been reached, treat the light as visible. This will // mean there is more light than expected, but that works better than blocking the light, for @@ -295,7 +220,7 @@ float3 traceShadowRay(RaytracingAccelerationStructure scene, float3 origin, floa // - Zero visibility for shadow rays that treat all objects as opaque. if the miss shader is // evaluated, it will set full visibility. ShadowRayPayload rayPayload; - rayPayload.visibility = isOpaque ? BLACK : WHITE; + rayPayload.visibility = onlyOpaqueHits ? BLACK : WHITE; // Prepare the shadow ray, with a small offset from the origin to avoid self-intersection. RayDesc ray; @@ -314,9 +239,9 @@ float3 traceShadowRay(RaytracingAccelerationStructure scene, float3 origin, floa // - Treat all intersections as opaque, so that the shadow any hit shader is never called. // - Use the shadow miss shader, which simply sets full visibility on ray payload. uint rayFlags = RAY_FLAG_SKIP_CLOSEST_HIT_SHADER; - rayFlags |= isOpaque ? RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE : 0; + rayFlags |= onlyOpaqueHits ? RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_FORCE_OPAQUE : 0; #if DIRECTX - uint missShaderIndex = isOpaque ? kMissShadow : kMissNull; + uint missShaderIndex = onlyOpaqueHits ? kMissShadow : kMissNull; #else uint missShaderIndex = kMissShadow; #endif @@ -327,60 +252,4 @@ float3 traceShadowRay(RaytracingAccelerationStructure scene, float3 origin, floa return rayPayload.visibility; } -// ================================================================================================= -// Layer material code -// ================================================================================================= - -// Shade the layer material for provided miss shader index. -// Returns true if absorbed by layer. -bool shadeMaterialLayer(RaytracingAccelerationStructure nullScene, int layerMissShaderIdx, - in out RadianceRayPayload rayPayload, ShadingData shading, - BuiltInTriangleIntersectionAttributes hit, int depth, int maxDepth) -{ - // If the maximum trace recursion depth has been reached, don't shade the layer. - if (depth == maxDepth) - { - return false; - } - - // Create a dummy ray, as we use a null scene this is never actuall intersected with anything. - RayDesc ray; - ray.Origin = float3(0, 0, 0); - ray.Direction = float3(0, 0, 1); - ray.TMin = 0; - ray.TMax = 0; - - // Create the layer data from the current intersection. - RayPayload layerData; - layerData.hit = hit; - layerData.radianceRay = rayPayload; - layerData.objToWorld = ObjectToWorld3x4(); - layerData.currentT = RayTCurrent(); - layerData.viewDirection = -WorldRayDirection(); - layerData.absorbedByLayer = false; - layerData.primitiveIndex = PrimitiveIndex(); - - // Increment ray depth. - layerData.radianceRay.depth = depth + 1; - - // "trace" the ray (this will go straight to the miss shader) - TraceRay(nullScene, // Null scene (so ray goes straight to miss) - 0, // flags to control ray behavior - 0xFF, // instance mask - 0, // ray contribution to hit group index - 0, // multiplier for geometry contribution to hit group index - layerMissShaderIdx, // miss shader index for layer material shader. - ray, // ray to be traced - layerData); // The layer data including details of current intersection. - - // If the ray is absorbed by the layer, then update the ray payload and return true. - if (layerData.absorbedByLayer) - { - rayPayload = layerData.radianceRay; - return true; - } - - // Return false (as not absorbed and ray payload unaffected) - return false; -} #endif // __RAY_TRACE_H__ diff --git a/Libraries/Aurora/Source/Shaders/ShadeFunctions.slang b/Libraries/Aurora/Source/Shaders/ShadeFunctions.slang index 85e6914..407e6e7 100644 --- a/Libraries/Aurora/Source/Shaders/ShadeFunctions.slang +++ b/Libraries/Aurora/Source/Shaders/ShadeFunctions.slang @@ -26,8 +26,8 @@ float3 shadeEmission(Material material) // Compute shading with a directional light. float3 shadeDirectionalLight(float3 dir, float4 colorAndIntensity, float cosRadius, - RaytracingAccelerationStructure scene, Material material, ShadingData shading, float3 V, - bool isOpaqueShadowsEnabled, int depth, int maxDepth, inout Random rng) + Material material, ShadingData shading, float3 V, + inout Random rng, out float3 shadowRayDirection) { // Perform shading with the global directional light. Treat the light as having a disc area by // sampling a random direction in a cone around the light direction. @@ -45,19 +45,17 @@ float3 shadeDirectionalLight(float3 dir, float4 colorAndIntensity, float cosRadi // visibility of the light source at the hit position. If the view (ray) direction is on the // back side of the geometry, use the geometric position to avoid self-intersection. float3 lightRadiance = colorAndIntensity.a * colorAndIntensity.rgb; - float3 position = dot(V, shading.normal) < 0.0f ? shading.geomPosition : shading.position; - float3 lightVisibility = - traceShadowRay(scene, position, L, M_RAY_TMIN, isOpaqueShadowsEnabled, depth, maxDepth); + shadowRayDirection = L; // Compute the outgoing radiance as the BSDF (with cosine term) multipled by the light // visibility and radiance. - return lightVisibility * lightRadiance * bsdfAndCosine; + return lightRadiance * bsdfAndCosine; } // Compute shading with an environment light as direct lighting. float3 shadeEnvironmentLightDirect(Environment environment, RaytracingAccelerationStructure scene, Material material, ShadingData shading, float3 V, - bool isOpaqueShadowsEnabled, int depth, int maxDepth, inout Random rng) + bool onlyOpaqueShadowHits, int depth, int maxDepth, inout Random rng) { // Sample the environment to determine a sample (light) direction and the corresponding // probability density function (PDF) value for that direction. @@ -78,7 +76,7 @@ float3 shadeEnvironmentLightDirect(Environment environment, // self-intersection. float3 position = dot(V, shading.normal) < 0.0f ? shading.geomPosition : shading.position; float3 lightVisibility = - traceShadowRay(scene, position, L, M_RAY_TMIN, isOpaqueShadowsEnabled, depth, maxDepth); + traceShadowRay(scene, position, L, M_RAY_TMIN, onlyOpaqueShadowHits, depth, maxDepth); // Compute the outgoing radiance as the BSDF (with cosine term) multipled by the light // visibility and radiance, divided by the PDF. @@ -86,35 +84,34 @@ float3 shadeEnvironmentLightDirect(Environment environment, } // Compute shading with an environment light using multiple importance sampling. -float3 shadeEnvironmentLightMIS(Environment environment, RaytracingAccelerationStructure scene, Material material, - ShadingData shading, float3 V, bool isOpaqueShadowsEnabled, int depth, int maxDepth, - inout Random rng, inout IndirectOutput indirect) +void shadeEnvironmentLightMIS(Environment environment, Material material, + ShadingData shading, float3 V, + inout Random rng, out int lobeID, + out bool emitsMaterialShadowRay, inout float3 materialResult, inout float3 materialShadowRayDirection, + out bool emitsLightShadowRay, inout float3 lightResult, inout float3 lightShadowRayDirection ) { - float3 result = BLACK; + // Set the emit flags to false. + emitsMaterialShadowRay = false; + emitsLightShadowRay = false; // Generate random numbers for sampling. float2 random = random2D(rng); - // If the view (ray) direction is on the back side of the geometry, use the geometric position - // for shadow rays to avoid self-intersection. Otherwise use the shading normal. - float3 position = dot(V, shading.normal) < 0.0f ? shading.geomPosition : shading.position; - // Multiple importance sampling (MIS) proceeds as follows: // 1) Sample the *material* BSDF-and-cosine to get a new light direction and calculate the PDF. // 2) Evaluate the light in the light direction and calculate the PDF. - // 3) Trace a shadow ray in the light direction to compute visibility. - // 4) Weight the outgoing radiance with a balance heuristic and store that result. + // 3) Compute a shadow ray in the light direction to compute visibility. + // 4) Weight the outgoing radiance with a balance heuristic and store that result as materialResult. // 5) Sample the *light* and calculate the PDF. // 6) Evaluate the material BSDF-and-cosine in the light direction and calculate the PDF. - // 7) Trace a shadow ray in the light direction to compute visibility. - // 8) Weight the outgoing radiance with a balance heuristic and that to the result from step #4. + // 7) Compute a shadow ray in the light direction to compute visibility. + // 8) Weight the outgoing radiance with a balance heuristic and store that result as materialResult. // Step 1: Sample the BSDF of the material, using the view direction. In addition to the BSDF // value, this computes a sample (light) direction and the corresponding PDF value for that // direction. float3 L; float materialPDF; - int lobeID; float3 bsdfAndCosine = sampleMaterial(material, shading, V, random, L, materialPDF, lobeID); @@ -122,7 +119,7 @@ float3 shadeEnvironmentLightMIS(Environment environment, RaytracingAccelerationS // and does not repond to lighting. if (lobeID == TRANSMISSION_LOBE) { - return BLACK; + return; } // Step 2: Evaluate the environment for the light direction. @@ -139,15 +136,19 @@ float3 shadeEnvironmentLightMIS(Environment environment, RaytracingAccelerationS ? (luminance(lightRadiance) / environment.constants.lightTexLuminanceIntegral) : (0.25f * M_PI_INV); - // Step 3: Use a shadow ray to compute the visibility of the light at the hit position. - float3 lightVisibility = - traceShadowRay(scene, position, L, M_RAY_TMIN, isOpaqueShadowsEnabled, depth, maxDepth); - // Step 4: Compute a weight based on the PDF of the material and the light, and apply that - // weight to the outgoing radiance: visibility * radiance * BSDF * cosine / material PDF. + // weight to the material result: radiance * BSDF * cosine / material PDF. // Add that to the prior weighted result. float weight = computeMISWeight(1, materialPDF, 1, lightPDF); - result += weight * lightVisibility * lightRadiance * bsdfAndCosine / materialPDF; + + // Return the weighted result via the output parameter. + materialResult = weight * lightRadiance * bsdfAndCosine / materialPDF; + + // Return the material shadow direction via the output parameter (the actual shadow ray will be emitted by the calling code.) + materialShadowRayDirection = L; + + // Set the emit flag to true to tell the calling code to emit a material shadow ray. + emitsMaterialShadowRay = true; } // Step 5: Sample the environment to determine a sample (light) direction and the corresponding @@ -162,41 +163,28 @@ float3 shadeEnvironmentLightMIS(Environment environment, RaytracingAccelerationS // Skip the following steps if the light radiance or BSDF-and-cosine is black. if (!isBlack(lightRadiance) && !isBlack(bsdfAndCosine)) { - // Step 7: Use a shadow ray to compute the visibility of the light at the hit position. - float3 lightVisibility = - traceShadowRay(scene, position, L, M_RAY_TMIN, isOpaqueShadowsEnabled, depth, maxDepth); // Step 8: Compute a weight based on the PDF of the light and the material, and apply that - // weight to the outgoing radiance: visibility * radiance * BSDF * cosine / light PDF. + // weight to the light result: visibility * radiance * BSDF * cosine / light PDF. float weight = computeMISWeight(1, lightPDF, 1, materialPDF); - result += weight * lightVisibility * lightRadiance * bsdfAndCosine / lightPDF; - } - // Update the indirect output values based on the sampled lobe at Step 1: diffuse for the - // diffuse lobe, and treat everything else as glossy. As this is an environment light, the hit - // distances are set to infinity. - // NOTE: This works because the material sampling (Step 1) and evaluation (Step 6) are using the - // same random numbers and therefore are expected to use the same lobe. An alternative is for - // Step 6 to perform a complete evaluation (rather than selecting a single lobe) and combine the - // PDFs of the lobes, with separate diffuse and glossy components returned from the evaluation. - if (lobeID == DIFFUSE_LOBE) - { - indirect.diffuse += result; - indirect.diffuseHitDist = INFINITY; - } - else - { - indirect.glossy += result; - indirect.glossyHitDist = INFINITY; - } + // Return the weighted result via the output parameter. + lightResult = weight * lightRadiance * bsdfAndCosine / lightPDF; - return result; + // Return the light shadow direction via the output parameter (the actual shadow ray will be emitted by the calling code.) + lightShadowRayDirection = L; + + // Set the emit flag to true to tell the calling code to emit a light shadow ray. + emitsLightShadowRay = true; + } + + return; } -// Compute shading with indirect light, from a random direction. -float3 shadeIndirectLight(RaytracingAccelerationStructure scene, - Environment environment, Material material, ShadingData shading, float3 V, int depth, - int maxDepth, inout Random rng, out float alpha, inout IndirectOutput indirect) +// Sample the BSDF to generate the next indirect ray in the path, and set output parameters for the next ray's origin, direction, and color. +// Return the ID of the lobe that was sampled. +int setupIndirectLightRay(Material material, ShadingData shading, float3 V, int depth, + int maxDepth, inout Random rng, out float3 indirectRayDirec, out float3 indirectRayOrigin, out float3 indirectColorOut) { // Generate random numbers for sampling. float2 random = random2D(rng); @@ -211,41 +199,23 @@ float3 shadeIndirectLight(RaytracingAccelerationStructure scene, sampleMaterial(material, shading, V, random, L, pdf, lobeID); if (isBlack(bsdfAndCosine)) { - return BLACK; + return NO_SECONDARY_RAY; } - // Trace a radiance ray with the sampled light direction, i.e. the next step in tracing the - // current path. If the view (ray) direction is on the back side of the geometry, or the ray is - // from a transmission lobe, use the geometric position to avoid self-intersection. Also, if the - // ray is from a transmission lobe, use the background miss shader so that the background is - // sampled. + // Compute the indirect ray, i.e. the next step in tracing the current path. + + // If the view (ray) direction is on the back side of the geometry, or the ray is + // from a transmission lobe, use the geometric position to avoid self-intersection. bool isTransmission = lobeID == TRANSMISSION_LOBE; - float3 position = - dot(V, shading.normal) < 0.0f || isTransmission ? shading.geomPosition : shading.position; - RadianceRayPayload rayPayload = traceRadianceRay( - scene, environment, position, L, M_RAY_TMIN, depth, maxDepth, isTransmission, rng); - - // Compute the outgoing radiance as the BSDF (with cosine term) multipled by the light radiance, - // divided by the PDF. - float3 radiance = rayPayload.color * bsdfAndCosine / pdf; - - // If the payload alpha is zero and this was a transmission lobe, pass along an alpha of zero. - // This indicates that all the path segments beyond this hit have been transparent or - // transmissive. Otherwise the alpha is assumed to be one (opaque). - alpha = (rayPayload.alpha == 0.0f && lobeID == TRANSMISSION_LOBE) ? 0.0f : 1.0f; - - // Update the indirect output values based on the sampled lobe: diffuse for the diffuse lobe, - // and treat everything else as glossy. - if (lobeID == DIFFUSE_LOBE) - { - indirect.diffuse += radiance; - indirect.diffuseHitDist = rayPayload.depthView; - } - else - { - indirect.glossy += radiance; - indirect.glossyHitDist = rayPayload.depthView; - } - - return radiance; + indirectRayOrigin = + (dot(V, shading.normal) < 0.0f || isTransmission) ? shading.geomPosition : shading.position; + + // Set the direction from the sampled light direction. + indirectRayDirec = L; + + // Set the color from the sampled color scaled by the inverse of the PDF. + indirectColorOut = bsdfAndCosine / pdf; + + // Return lobe ID. + return lobeID; } diff --git a/Libraries/Aurora/Source/Shaders/ShadowMissShader.slang b/Libraries/Aurora/Source/Shaders/ShadowMissShader.slang deleted file mode 100644 index 03f391f..0000000 --- a/Libraries/Aurora/Source/Shaders/ShadowMissShader.slang +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 Autodesk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// The shadow miss shader. This simply sets full visibility on the ray payload, indicating that -// nothing was hit. -#include "Globals.slang" -#include "Geometry.slang" -#include "RayTrace.slang" - -[shader("miss")] void ShadowMissShader( - inout ShadowRayPayload rayPayload) { rayPayload.visibility = WHITE; } \ No newline at end of file diff --git a/Libraries/Aurora/Source/Shaders/StandardSurfaceBSDF.slang b/Libraries/Aurora/Source/Shaders/StandardSurfaceBSDF.slang index 2468d04..55a61c8 100644 --- a/Libraries/Aurora/Source/Shaders/StandardSurfaceBSDF.slang +++ b/Libraries/Aurora/Source/Shaders/StandardSurfaceBSDF.slang @@ -14,6 +14,8 @@ #ifndef __STANDARD_SURFACE_H__ #define __STANDARD_SURFACE_H__ +#include "BSDFCommon.slang" + // NOTE: The following shader code is ported to HLSL from the MaterialX GLSL implementations. // Determines whether vector a and b are in the same hemisphere relative to normal n. diff --git a/Libraries/Aurora/Source/Transpiler.cpp b/Libraries/Aurora/Source/Transpiler.cpp index c856a3f..09f205f 100644 --- a/Libraries/Aurora/Source/Transpiler.cpp +++ b/Libraries/Aurora/Source/Transpiler.cpp @@ -188,14 +188,16 @@ bool Transpiler::transpile( slang::ICompileRequest* pRequest; [[maybe_unused]] const int reqIndex = _pSession->createCompileRequest(&pRequest); + auto profileId = _pSession->findProfile("sm_6_3"); + // Set the file system and compile fiags. pRequest->setFileSystem(_pFileSystem.get()); pRequest->setCompileFlags(SLANG_COMPILE_FLAG_NO_MANGLING); - // Create code gen target (with GLSL or HLSL language as required). const int targetIndex = pRequest->addCodeGenTarget(target == Language::GLSL ? SLANG_GLSL : SLANG_HLSL); + pRequest->setTargetProfile(targetIndex, profileId); // Set target flags to generate whole program. pRequest->setTargetFlags(targetIndex, SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM); // TODO: The buffer layout might be an issue, need to work out correct flags. diff --git a/Libraries/Aurora/Source/UniformBuffer.cpp b/Libraries/Aurora/Source/UniformBuffer.cpp index f141335..bf34b4e 100644 --- a/Libraries/Aurora/Source/UniformBuffer.cpp +++ b/Libraries/Aurora/Source/UniformBuffer.cpp @@ -145,30 +145,30 @@ string UniformBuffer::generateByteAddressBufferAccessors(const string& prefix) c ss << " // Get property " << def.name << " from byte address buffer" << endl; ss << getHLSLStringFromType(def.type) << " " << prefix << def.variableName - << "(ByteAddressBuffer buf) {" << endl; + << "(ByteAddressBuffer buf, int materialOffset = 0) {" << endl; size_t offset = _fields[i].bufferIndex * sizeof(_data[0]); switch (def.type) { case PropertyValue::Type::Bool: case PropertyValue::Type::Int: - ss << "\treturn buf.Load(" << offset << ");" << endl; + ss << "\treturn buf.Load(materialOffset + " << offset << ");" << endl; break; case PropertyValue::Type::Float: - ss << "\treturn asfloat(buf.Load(" << offset << "));" << endl; + ss << "\treturn asfloat(buf.Load(materialOffset + " << offset << "));" << endl; break; case PropertyValue::Type::Float2: - ss << "\treturn asfloat(buf.Load2(" << offset << "));" << endl; + ss << "\treturn asfloat(buf.Load2(materialOffset + " << offset << "));" << endl; break; case PropertyValue::Type::Float3: - ss << "\treturn asfloat(buf.Load3(" << offset << "));" << endl; + ss << "\treturn asfloat(buf.Load3(materialOffset + " << offset << "));" << endl; break; case PropertyValue::Type::Float4: - ss << "\treturn asfloat(buf.Load4(" << offset << "));" << endl; + ss << "\treturn asfloat(buf.Load4(materialOffset + " << offset << "));" << endl; case PropertyValue::Type::Matrix4: for (int j = 0; j < 16; j++) { - ss << "\tfloat4 m" << j << " = asfloat(buf.Load(" << offset + (j * 4) << "));" - << endl; + ss << "\tfloat4 m" << j << " = asfloat(buf.Load(materialOffset + " + << offset + (j * 4) << "));" << endl; ; } ss << "float4x4 mtx = {"; diff --git a/Libraries/Aurora/Source/UniformBuffer.h b/Libraries/Aurora/Source/UniformBuffer.h index 35622ca..90a2b55 100644 --- a/Libraries/Aurora/Source/UniformBuffer.h +++ b/Libraries/Aurora/Source/UniformBuffer.h @@ -36,12 +36,28 @@ struct UniformBufferPropertyDefinition // currently in UniformBuffer. using UniformBufferDefinition = vector; +// Texture indentifier, containing image name (e.g. "base_color_image") and optional sampler name +// (e.g. "base_color_image_sampler"). +struct TextureIdentifier +{ + string image; + string sampler; + TextureIdentifier(const char* pImageName = nullptr) : + image(pImageName ? pImageName : ""), sampler("") + { + } + TextureIdentifier(const string& imageName, const string& samplerName = "") : + image(imageName), sampler(samplerName) + { + } + bool hasSampler() const { return !sampler.empty(); } +}; + // Definition of a texture. -// TODO: Should be part of equivalent TextureArray class, similar to UniformBuffer. struct TextureDefinition { // Texture name. - string name; + TextureIdentifier name; // Is true, the texture is sRGB that should be converted to linear color space on the GPU. bool linearize = true; // Default filename for texture. diff --git a/Libraries/HdAurora/CMakeLists.txt b/Libraries/HdAurora/CMakeLists.txt index aed7b30..59d237f 100644 --- a/Libraries/HdAurora/CMakeLists.txt +++ b/Libraries/HdAurora/CMakeLists.txt @@ -8,6 +8,12 @@ find_package(MaterialX REQUIRED) # MaterialX SDK find_package(pxr REQUIRED) # Find the Universal Scene Description (USD) library # Alias the namespace to meet the cmake convention on imported targets add_library(MaterialX::GenGlsl ALIAS MaterialXGenGlsl) +# Include system python libraries if needed by USD. +if(USD_BUILT_WITH_PYTHON) + find_package (Python3 COMPONENTS Development) + set(PYTHON_DEPENDENCY Python3::Python) +endif() + # On windows, the version resource and header files that add the version information to the DLL. if(WIN32) @@ -67,6 +73,7 @@ PRIVATE pxr::hd pxr::hdx Foundation + ${PYTHON_DEPENDENCY} Aurora ) @@ -88,7 +95,9 @@ if(WIN32) add_dependencies(CopyUSDExecutables MakeRuntimeDir) add_dependencies(${PROJECT_NAME} CopyUSDExecutables) - if(ENABLE_USDVIEW) + # If USD was built with python, add a custom build step to copy PXR python libs. + if(USD_BUILT_WITH_PYTHON) + message(STATUS "USD built with python, adding python copy step") add_custom_target(CopyUSDPythonExecutables ALL COMMAND ${CMAKE_COMMAND} -E copy_directory ${USD_PYTHON_LIB_DIR} ${RUNTIME_OUTPUT_DIR}/python ) @@ -111,6 +120,7 @@ endif() target_include_directories(${PROJECT_NAME} PRIVATE ${VERSION_FOLDER} + ${PYTHON_INCLUDE_DIRS} ) # Install the binaries. diff --git a/Libraries/HdAurora/HdAuroraImageCache.cpp b/Libraries/HdAurora/HdAuroraImageCache.cpp index 4442d39..3973731 100644 --- a/Libraries/HdAurora/HdAuroraImageCache.cpp +++ b/Libraries/HdAurora/HdAuroraImageCache.cpp @@ -34,22 +34,24 @@ Aurora::Path HdAuroraImageCache::acquireImage( if (iter != _cache.end()) return auroraImagePath; - _cache[auroraImagePath] = HdAuroraImageCacheEntry(); - HdAuroraImageCacheEntry& newEntry = _cache[auroraImagePath]; - newEntry.imageDesc.getPixelData = [this, auroraImagePath]( - Aurora::PixelData& dataOut, glm::ivec2, glm::ivec2) { - const HdAuroraImageCacheEntry& entry = _cache[auroraImagePath]; - dataOut.address = entry.pPixelData.get(); - dataOut.size = entry.sizeInBytes; - return true; - }; - newEntry.imageDesc.isEnvironment = isEnvironmentImage; - newEntry.imageDesc.linearize = !forceLinear; + _cache.insert(auroraImagePath); + + Aurora::ImageDescriptor descriptor; + descriptor.getData = [this, auroraImagePath, sFilePath, forceLinear]( + Aurora::ImageData& dataOut, Aurora::AllocateBufferFunction alloc) { + // Get resolved image path from ArResolver. + std::string resolvedPath = ArGetResolver().CreateIdentifier(sFilePath); + + // Load image using resolved path, return false if none found. + pxr::HioImageSharedPtr const image = pxr::HioImage::OpenForReading(resolvedPath); + if (!image) + { + AU_ERROR("Failed to open image file %s with resolved path %s", sFilePath.c_str(), + resolvedPath.c_str()); + return false; + } - std::string resolvedPath = ArGetResolver().CreateIdentifier(sFilePath); - pxr::HioImageSharedPtr const image = pxr::HioImage::OpenForReading(resolvedPath); - if (image) - { + // Fill in HIO storage struct. pxr::HioImage::StorageSpec imageData; auto hioFormat = image->GetFormat(); imageData.width = image->GetWidth(); @@ -59,82 +61,87 @@ Aurora::Path HdAuroraImageCache::acquireImage( imageData.format = image->GetFormat(); bool paddingRequired = hioFormat == HioFormatUNorm8Vec3srgb || hioFormat == HioFormatUNorm8Vec3; - unique_ptr pTempPixels; + + // If we need padding create working buffer to read into, otherwise read straight into the + // pixel buffer. + unique_ptr pUnpaddedPixels; + // Get temp pixels for image. + uint8_t* pPixelData; if (paddingRequired) { - hioFormat = hioFormat == HioFormatUNorm8Vec3srgb ? HioFormatUNorm8Vec4srgb - : HioFormatUNorm8Vec4; - newEntry.sizeInBytes = image->GetWidth() * image->GetHeight() * 4; - pTempPixels.reset( + hioFormat = hioFormat == HioFormatUNorm8Vec3srgb ? HioFormatUNorm8Vec4srgb + : HioFormatUNorm8Vec4; + dataOut.bufferSize = image->GetWidth() * image->GetHeight() * 4; + pUnpaddedPixels.reset( new uint8_t[image->GetWidth() * image->GetHeight() * image->GetBytesPerPixel()]); - newEntry.pPixelData.reset(new uint8_t[newEntry.sizeInBytes]); - imageData.data = pTempPixels.get(); + imageData.data = pUnpaddedPixels.get(); + pPixelData = static_cast(alloc(dataOut.bufferSize)); } else { - newEntry.sizeInBytes = - image->GetWidth() * image->GetHeight() * image->GetBytesPerPixel(); - newEntry.pPixelData.reset(new uint8_t[newEntry.sizeInBytes]); - imageData.data = newEntry.pPixelData.get(); + dataOut.bufferSize = image->GetWidth() * image->GetHeight() * image->GetBytesPerPixel(); + pPixelData = static_cast(alloc(dataOut.bufferSize)); + imageData.data = pPixelData; } + // Set output data point to the pixel buffer. + dataOut.pPixelBuffer = pPixelData; + + // Set output image dimensions. + dataOut.dimensions = { image->GetWidth(), image->GetHeight() }; + + // Read the pixels. bool res = image->Read(imageData); - if (res) + if (!res) { - newEntry.imageDesc.width = image->GetWidth(); - newEntry.imageDesc.height = image->GetHeight(); - if (paddingRequired) - { - for (size_t idx = 0; idx < newEntry.imageDesc.width * newEntry.imageDesc.height; - idx++) - { - newEntry.pPixelData[idx * 4 + 0] = pTempPixels[idx * 3 + 0]; - newEntry.pPixelData[idx * 4 + 1] = pTempPixels[idx * 3 + 1]; - newEntry.pPixelData[idx * 4 + 2] = pTempPixels[idx * 3 + 2]; - newEntry.pPixelData[idx * 4 + 3] = 0xFF; - } - } + AU_ERROR("Failed to read image file %s with resolved path %s", sFilePath.c_str(), + resolvedPath.c_str()); + return false; + } - switch (hioFormat) + // Pad to RGBA if required. + if (paddingRequired) + { + for (size_t idx = 0; idx < image->GetWidth() * image->GetWidth(); idx++) { - case HioFormatUNorm8Vec4srgb: - // Set linearize flag, unless force linear is true. - newEntry.imageDesc.linearize = !forceLinear; - // Fall through... - case HioFormatUNorm8Vec4: - newEntry.imageDesc.format = Aurora::ImageFormat::Integer_RGBA; - break; - case HioFormatFloat32Vec3: - newEntry.imageDesc.format = Aurora::ImageFormat::Float_RGB; - break; - case HioFormatFloat32Vec4: - newEntry.imageDesc.format = Aurora::ImageFormat::Float_RGBA; - break; - case HioFormatUNorm8: - newEntry.imageDesc.format = Aurora::ImageFormat::Byte_R; - break; - default: - AU_ERROR("%s: Unsupported image format:%x", sFilePath.c_str(), image->GetFormat()); - newEntry.pPixelData.reset(); - break; + pPixelData[idx * 4 + 0] = pUnpaddedPixels[idx * 3 + 0]; + pPixelData[idx * 4 + 1] = pUnpaddedPixels[idx * 3 + 1]; + pPixelData[idx * 4 + 2] = pUnpaddedPixels[idx * 3 + 2]; + pPixelData[idx * 4 + 3] = 0xFF; } } - else - newEntry.pPixelData.reset(); - } - if (!newEntry.pPixelData) - { - AU_ERROR("Failed to load image image :%s, using placeholder", sFilePath.c_str()); - newEntry.sizeInBytes = 8; - newEntry.pPixelData.reset(new uint8_t[8]); - memset(newEntry.pPixelData.get(), 0xff, newEntry.sizeInBytes); - newEntry.pPixelData[0] = 0xff; - newEntry.imageDesc.width = 2; - newEntry.imageDesc.height = 1; - newEntry.imageDesc.format = Aurora::ImageFormat::Integer_RGBA; - } - - _pAuroraScene->setImageDescriptor(auroraImagePath, newEntry.imageDesc); + + switch (hioFormat) + { + case HioFormatUNorm8Vec4srgb: + // Set linearize flag, unless force linear is true. + dataOut.overrideLinearize = !forceLinear; + dataOut.linearize = true; + // Fall through... + case HioFormatUNorm8Vec4: + dataOut.format = Aurora::ImageFormat::Integer_RGBA; + break; + case HioFormatFloat32Vec3: + dataOut.format = Aurora::ImageFormat::Float_RGB; + break; + case HioFormatFloat32Vec4: + dataOut.format = Aurora::ImageFormat::Float_RGBA; + break; + case HioFormatUNorm8: + dataOut.format = Aurora::ImageFormat::Byte_R; + break; + default: + AU_ERROR("%s: Unsupported image format:%x", sFilePath.c_str(), image->GetFormat()); + return false; + break; + } + + return true; + }; + descriptor.isEnvironment = isEnvironmentImage; + descriptor.linearize = forceLinear; + + _pAuroraScene->setImageDescriptor(auroraImagePath, descriptor); return auroraImagePath; } diff --git a/Libraries/HdAurora/HdAuroraImageCache.h b/Libraries/HdAurora/HdAuroraImageCache.h index 2fb6ab9..c4669c2 100644 --- a/Libraries/HdAurora/HdAuroraImageCache.h +++ b/Libraries/HdAurora/HdAuroraImageCache.h @@ -14,17 +14,6 @@ #pragma once -// Cache entry, use in cache map lookup. -struct HdAuroraImageCacheEntry -{ - // The pixels for the image. - std::unique_ptr pPixelData; - // Total bytes of pixel data. - size_t sizeInBytes; - // The Aurora descriptor describing the image. - Aurora::ImageDescriptor imageDesc; -}; - // Image cache used by materials and environments to load Aurora images. class HdAuroraImageCache { @@ -41,7 +30,7 @@ class HdAuroraImageCache const string& sFilePath, bool isEnvironmentImage = false, bool linearize = false); private: - map _cache; + set _cache; Aurora::IScenePtr _pAuroraScene; bool _isYFlipped = true; }; diff --git a/Libraries/HdAurora/HdAuroraPlugin.cpp b/Libraries/HdAurora/HdAuroraPlugin.cpp index 9127ee5..0e19566 100644 --- a/Libraries/HdAurora/HdAuroraPlugin.cpp +++ b/Libraries/HdAurora/HdAuroraPlugin.cpp @@ -76,7 +76,13 @@ void HdAuroraRendererPlugin::DeleteRenderDelegate(HdRenderDelegate* renderDelega delete renderDelegate; } +// The extra arguments to IsSupported were added with Hydra version 50. As we need to support +// both USD 23.08.01 and older versions, make arguments conditional at compile time. +#if HD_API_VERSION >= 50 +bool HdAuroraRendererPlugin::IsSupported(bool /*gpuEnabled*/, TfToken /* hgiToken*/) const +#else bool HdAuroraRendererPlugin::IsSupported() const +#endif { return true; } diff --git a/Libraries/HdAurora/HdAuroraPlugin.h b/Libraries/HdAurora/HdAuroraPlugin.h index f78781e..19dd62e 100644 --- a/Libraries/HdAurora/HdAuroraPlugin.h +++ b/Libraries/HdAurora/HdAuroraPlugin.h @@ -25,8 +25,13 @@ class HdAuroraRendererPlugin : public HdRendererPlugin void DeleteRenderDelegate(HdRenderDelegate* renderDelegate) override; +// The extra arguments to IsSupported were added with Hydra version 50. As we need to support +// both USD 23.08.01 and older versions, make arguments conditional at compile time. +#if HD_API_VERSION >= 50 + virtual bool IsSupported(bool gpuEnabled = true, TfToken hgiToken = TfToken("")) const override; +#else bool IsSupported() const override; - +#endif private: HdAuroraRendererPlugin(const HdAuroraRendererPlugin&) = delete; HdAuroraRendererPlugin& operator=(const HdAuroraRendererPlugin&) = delete; diff --git a/Libraries/ImageProcessingResolver/CMakeLists.txt b/Libraries/ImageProcessingResolver/CMakeLists.txt index 9a4fb9a..e8f906f 100644 --- a/Libraries/ImageProcessingResolver/CMakeLists.txt +++ b/Libraries/ImageProcessingResolver/CMakeLists.txt @@ -8,6 +8,11 @@ find_package(pxr REQUIRED) # Find the Universal Scene Description (USD) library find_package(OpenImageIO REQUIRED) find_package(miniz REQUIRED) find_package(uriparser REQUIRED) +# Include system python libraries if needed by USD. +if(USD_BUILT_WITH_PYTHON) + find_package (Python3 COMPONENTS Development) + set(PYTHON_DEPENDENCY Python3::Python) +endif() # On windows, the version resource and header files that add the version information to the DLL. if(WIN32) @@ -54,6 +59,7 @@ PRIVATE pxr::hd pxr::hdx Foundation + ${PYTHON_DEPENDENCY} Aurora ) diff --git a/Scripts/cmake/modules/FindSlang.cmake b/Scripts/cmake/modules/FindSlang.cmake index d9d9ced..3634d1e 100644 --- a/Scripts/cmake/modules/FindSlang.cmake +++ b/Scripts/cmake/modules/FindSlang.cmake @@ -25,6 +25,13 @@ if (DEFINED Slang_ROOT) PATH_SUFFIXES release debug DOC "path to slang library files" ) + find_program(Slang_COMPILER # Set variable Slang_LIBRARY + slangc # Find library path with libslang.so, slang.dll, or slang.lib + NO_DEFAULT_PATH + PATHS "${Slang_ROOT}/bin/${Slang_BUILD_CONFIG}" + PATH_SUFFIXES release debug + DOC "path to slangc compiler executable" + ) endif() # Once the prioritized find_path succeeds the result variable will be set and stored in the cache # so that no call will search again. @@ -36,7 +43,11 @@ find_library(Slang_LIBRARY # Set variable Slang_LIBRARY slang # Find library path with libslang.so, slang.dll, or slang.lib DOC "path to slang library files" ) - +find_program(Slang_COMPILER # Set variable Slang_LIBRARY + slangc # Find library path with libslang.so, slang.dll, or slang.lib + DOC "path to slangc compiler executable" +) + set(Slang_LIBRARIES ${Slang_LIBRARY}) set(Slang_INCLUDE_DIRS ${Slang_INCLUDE_DIR}) diff --git a/Scripts/cmake/toolbox.cmake b/Scripts/cmake/toolbox.cmake index c746075..df914de 100644 --- a/Scripts/cmake/toolbox.cmake +++ b/Scripts/cmake/toolbox.cmake @@ -44,7 +44,7 @@ macro(minify_shaders header shader_folder shaders) # Add a custom command to create minified shader. add_custom_command( OUTPUT ${header} - COMMAND ${Python3_EXECUTABLE} ${SCRIPTS_DIR}/minifyShadersFolder.py ${shader_folder} ${header} + COMMAND ${Python3_EXECUTABLE} ${SCRIPTS_DIR}/minifyShadersFolder.py ${shader_folder} ${header} ${Slang_COMPILER} COMMENT "Minifying path tracing shaders to ${header}" DEPENDS ${shaders} ) diff --git a/Scripts/installExternals.py b/Scripts/installExternals.py index 54ed279..9d3b27b 100644 --- a/Scripts/installExternals.py +++ b/Scripts/installExternals.py @@ -597,7 +597,7 @@ def InstallOpenSubdiv(context, force, buildArgs): ############################################################ # MaterialX -MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.38.5.zip" +MATERIALX_URL = "https://github.com/AcademySoftwareFoundation/MaterialX/archive/v1.38.8.zip" MATERIALX_INSTALL_FOLDER = "MaterialX" MATERIALX_PACKAGE_NAME = "MaterialX" diff --git a/Scripts/minifyShadersFolder.py b/Scripts/minifyShadersFolder.py index 15b8dc9..b425f0a 100644 --- a/Scripts/minifyShadersFolder.py +++ b/Scripts/minifyShadersFolder.py @@ -10,18 +10,23 @@ # Dump usage and exit if we have incorrect number of args. if(len(sys.argv)<3 or len(sys.argv)>4): - print("Usage: python minifyHLSLFolder.py inputFolder outputFile [outputNamespace]") - print(" inputFolder - Folder containing HLSL and HLSLI files") - print(" outputFile - Output C++ header file") - print(" outputNamespace - Namespace wrapping output C++ header file (if omitted, generated from outputFile)") + print("Usage: python minifyHLSLFolder.py inputFolder outputFile [outputNamespace] [slangCompiler] [slangCompiler]") + print(" inputFolder - Folder containing Slang files") + print(" outputMinifiedFile - Output minified C++ header file") + print(" slangCompiler - Folder for slang executable to compile main entryPoint file (if omitted, just minifies no compilation)") + print(" mainEntryPoint - Main entry point filename, also used for precompiled DXIL C++ header filename (if omitted, assumes MainEntryPoint)") sys.exit(-1) # Parse the command line arguments. hlslFolder = sys.argv[1] outputFile = sys.argv[2] nameSpaceName = os.path.splitext(os.path.basename(outputFile))[0] # Default namespace is output filename without extension or path. +slangc = "" if(len(sys.argv)==4): - nameSpaceName = sys.argv[3] + slangc = sys.argv[3] +mainEntryPoint = "MainEntryPoints" +if(len(sys.argv)==5): + mainEntryPoint = sys.argv[4] # Build list of files files = glob.glob(hlslFolder+'/*.*sl*') @@ -37,16 +42,49 @@ numFiles = len(files) n = 0 rebuildNeeded = False +entryPointFile = "" for hlslFile in files: + if(mainEntryPoint in hlslFile): + entryPointFile = hlslFile fileModTime = os.stat(hlslFile).st_mtime if(fileModTime>outputModTime): rebuildNeeded=True - break # Early out, if nothing to be done. if(not rebuildNeeded): print("Nothing to be done, output header file %s up to date" % (outputFile)) sys.exit(0) + +# If we have a slang compiler and entry point filename, then precompile it. +if(len(slangc)>0 and len(entryPointFile)>0): + compiledFile = os.path.dirname(outputFile) + "/"+mainEntryPoint +".h" + print("Compiling main entry point %s with Slang compiler %s to DXIL in header %s"%(entryPointFile, slangc, compiledFile)) + compiledTempFile = os.path.dirname(outputFile) +"/"+ mainEntryPoint +".dxil" + variableName = "g_s"+mainEntryPoint+"DXIL"; + cmd = [slangc, entryPointFile, "-DDIRECTX=1", "-DRUNTIME_COMPILE_EVALUATE_MATERIAL_FUNCTION=1", "-target", "dxil", "-profile","lib_6_3","-o", compiledTempFile]; + result = subprocess.run( cmd, capture_output=True) + if(result.returncode!=0): + print("Compliation failed on "+entryPointFile) + print(result.stderr.decode()) + sys.exit(-1) + f = open(compiledTempFile, mode="rb") + data = bytearray(f.read()) + numBytes = len(data) + numWords = int(numBytes/4) + if(numWords*4 !=numBytes): + numWords=numWords+1; + headerStr = "static const array "+variableName+" = {\n"; + for i in range(numWords): + wordBytes = bytes([data[i*4+3],data[i*4+2],data[i*4+1],data[i*4+0]]) + headerStr += "0x"+str(wordBytes.hex()); + if(i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/Assets/TextFiles/DefaultMaterialUniformAccessors.slang b/Tests/Assets/TextFiles/DefaultMaterialUniformAccessors.slang index 1e50936..e8c90d9 100644 --- a/Tests/Assets/TextFiles/DefaultMaterialUniformAccessors.slang +++ b/Tests/Assets/TextFiles/DefaultMaterialUniformAccessors.slang @@ -1,287 +1,287 @@ // Auto-generated by unit test BasicTest in AuroraExternals test suite. // Get property base from byte address buffer -float Material_base(ByteAddressBuffer buf) { - return asfloat(buf.Load(0)); +float Material_base(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 0)); } // Get property base_color from byte address buffer -float3 Material_baseColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(4)); +float3 Material_baseColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 4)); } // Get property diffuse_roughness from byte address buffer -float Material_diffuseRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(16)); +float Material_diffuseRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 16)); } // Get property metalness from byte address buffer -float Material_metalness(ByteAddressBuffer buf) { - return asfloat(buf.Load(20)); +float Material_metalness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 20)); } // Get property specular from byte address buffer -float Material_specular(ByteAddressBuffer buf) { - return asfloat(buf.Load(24)); +float Material_specular(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 24)); } // Get property specular_color from byte address buffer -float3 Material_specularColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(32)); +float3 Material_specularColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 32)); } // Get property specular_roughness from byte address buffer -float Material_specularRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(44)); +float Material_specularRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 44)); } // Get property specular_IOR from byte address buffer -float Material_specularIOR(ByteAddressBuffer buf) { - return asfloat(buf.Load(48)); +float Material_specularIOR(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 48)); } // Get property specular_anisotropy from byte address buffer -float Material_specularAnisotropy(ByteAddressBuffer buf) { - return asfloat(buf.Load(52)); +float Material_specularAnisotropy(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 52)); } // Get property specular_rotation from byte address buffer -float Material_specularRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(56)); +float Material_specularRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 56)); } // Get property transmission from byte address buffer -float Material_transmission(ByteAddressBuffer buf) { - return asfloat(buf.Load(60)); +float Material_transmission(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 60)); } // Get property transmission_color from byte address buffer -float3 Material_transmissionColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(64)); +float3 Material_transmissionColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 64)); } // Get property subsurface from byte address buffer -float Material_subsurface(ByteAddressBuffer buf) { - return asfloat(buf.Load(76)); +float Material_subsurface(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 76)); } // Get property subsurface_color from byte address buffer -float3 Material_subsurfaceColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(80)); +float3 Material_subsurfaceColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 80)); } // Get property subsurface_radius from byte address buffer -float3 Material_subsurfaceRadius(ByteAddressBuffer buf) { - return asfloat(buf.Load3(96)); +float3 Material_subsurfaceRadius(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 96)); } // Get property subsurface_scale from byte address buffer -float Material_subsurfaceScale(ByteAddressBuffer buf) { - return asfloat(buf.Load(108)); +float Material_subsurfaceScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 108)); } // Get property subsurface_anisotropy from byte address buffer -float Material_subsurfaceAnisotropy(ByteAddressBuffer buf) { - return asfloat(buf.Load(112)); +float Material_subsurfaceAnisotropy(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 112)); } // Get property sheen from byte address buffer -float Material_sheen(ByteAddressBuffer buf) { - return asfloat(buf.Load(116)); +float Material_sheen(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 116)); } // Get property sheen_color from byte address buffer -float3 Material_sheenColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(128)); +float3 Material_sheenColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 128)); } // Get property sheen_roughness from byte address buffer -float Material_sheenRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(140)); +float Material_sheenRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 140)); } // Get property coat from byte address buffer -float Material_coat(ByteAddressBuffer buf) { - return asfloat(buf.Load(144)); +float Material_coat(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 144)); } // Get property coat_color from byte address buffer -float3 Material_coatColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(148)); +float3 Material_coatColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 148)); } // Get property coat_roughness from byte address buffer -float Material_coatRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(160)); +float Material_coatRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 160)); } // Get property coat_anisotropy from byte address buffer -float Material_coatAnisotropy(ByteAddressBuffer buf) { - return asfloat(buf.Load(164)); +float Material_coatAnisotropy(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 164)); } // Get property coat_rotation from byte address buffer -float Material_coatRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(168)); +float Material_coatRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 168)); } // Get property coat_IOR from byte address buffer -float Material_coatIOR(ByteAddressBuffer buf) { - return asfloat(buf.Load(172)); +float Material_coatIOR(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 172)); } // Get property coat_affect_color from byte address buffer -float Material_coatAffectColor(ByteAddressBuffer buf) { - return asfloat(buf.Load(176)); +float Material_coatAffectColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 176)); } // Get property coat_affect_roughness from byte address buffer -float Material_coatAffectRoughness(ByteAddressBuffer buf) { - return asfloat(buf.Load(180)); +float Material_coatAffectRoughness(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 180)); } // Get property emission from byte address buffer -float Material_emission(ByteAddressBuffer buf) { - return asfloat(buf.Load(184)); +float Material_emission(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 184)); } // Get property emission_color from byte address buffer -float3 Material_emissionColor(ByteAddressBuffer buf) { - return asfloat(buf.Load3(192)); +float3 Material_emissionColor(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 192)); } // Get property opacity from byte address buffer -float3 Material_opacity(ByteAddressBuffer buf) { - return asfloat(buf.Load3(208)); +float3 Material_opacity(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load3(materialOffset + 208)); } // Get property thin_walled from byte address buffer -int Material_thinWalled(ByteAddressBuffer buf) { - return buf.Load(220); +int Material_thinWalled(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 220); } // Get property has_base_color_image from byte address buffer -int Material_hasBaseColorTex(ByteAddressBuffer buf) { - return buf.Load(224); +int Material_hasBaseColorTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 224); } // Get property base_color_image_offset from byte address buffer -float2 Material_baseColorTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(228)); +float2 Material_baseColorTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 228)); } // Get property base_color_image_scale from byte address buffer -float2 Material_baseColorTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(240)); +float2 Material_baseColorTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 240)); } // Get property base_color_image_pivot from byte address buffer -float2 Material_baseColorTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(248)); +float2 Material_baseColorTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 248)); } // Get property base_color_image_rotation from byte address buffer -float Material_baseColorTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(256)); +float Material_baseColorTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 256)); } // Get property has_specular_roughness_image from byte address buffer -int Material_hasSpecularRoughnessTex(ByteAddressBuffer buf) { - return buf.Load(260); +int Material_hasSpecularRoughnessTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 260); } // Get property specular_roughness_image_offset from byte address buffer -float2 Material_specularRoughnessTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(264)); +float2 Material_specularRoughnessTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 264)); } // Get property specular_roughness_image_scale from byte address buffer -float2 Material_specularRoughnessTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(272)); +float2 Material_specularRoughnessTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 272)); } // Get property specular_roughness_image_pivot from byte address buffer -float2 Material_specularRoughnessTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(280)); +float2 Material_specularRoughnessTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 280)); } // Get property specular_roughness_image_rotation from byte address buffer -float Material_specularRoughnessTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(288)); +float Material_specularRoughnessTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 288)); } // Get property has_emission_color_image from byte address buffer -int Material_hasEmissionColorTex(ByteAddressBuffer buf) { - return buf.Load(292); +int Material_hasEmissionColorTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 292); } // Get property emission_color_image_offset from byte address buffer -float2 Material_emissionColorTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(296)); +float2 Material_emissionColorTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 296)); } // Get property emission_color_image_scale from byte address buffer -float2 Material_emissionColorTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(304)); +float2 Material_emissionColorTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 304)); } // Get property emission_color_image_pivot from byte address buffer -float2 Material_emissionColorTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(312)); +float2 Material_emissionColorTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 312)); } // Get property emission_color_image_rotation from byte address buffer -float Material_emissionColorTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(320)); +float Material_emissionColorTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 320)); } // Get property has_opacity_image from byte address buffer -int Material_hasOpacityTex(ByteAddressBuffer buf) { - return buf.Load(324); +int Material_hasOpacityTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 324); } // Get property opacity_image_offset from byte address buffer -float2 Material_opacityTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(328)); +float2 Material_opacityTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 328)); } // Get property opacity_image_scale from byte address buffer -float2 Material_opacityTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(336)); +float2 Material_opacityTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 336)); } // Get property opacity_image_pivot from byte address buffer -float2 Material_opacityTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(344)); +float2 Material_opacityTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 344)); } // Get property opacity_image_rotation from byte address buffer -float Material_opacityTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(352)); +float Material_opacityTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 352)); } // Get property has_normal_image from byte address buffer -int Material_hasNormalTex(ByteAddressBuffer buf) { - return buf.Load(356); +int Material_hasNormalTex(ByteAddressBuffer buf, int materialOffset = 0) { + return buf.Load(materialOffset + 356); } // Get property normal_image_offset from byte address buffer -float2 Material_normalTexOffset(ByteAddressBuffer buf) { - return asfloat(buf.Load2(360)); +float2 Material_normalTexOffset(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 360)); } // Get property normal_image_scale from byte address buffer -float2 Material_normalTexScale(ByteAddressBuffer buf) { - return asfloat(buf.Load2(368)); +float2 Material_normalTexScale(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 368)); } // Get property normal_image_pivot from byte address buffer -float2 Material_normalTexPivot(ByteAddressBuffer buf) { - return asfloat(buf.Load2(376)); +float2 Material_normalTexPivot(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load2(materialOffset + 376)); } // Get property normal_image_rotation from byte address buffer -float Material_normalTexRotation(ByteAddressBuffer buf) { - return asfloat(buf.Load(384)); +float Material_normalTexRotation(ByteAddressBuffer buf, int materialOffset = 0) { + return asfloat(buf.Load(materialOffset + 384)); } diff --git a/Tests/Assets/TextFiles/HdAuroraTest_Setup.glsl b/Tests/Assets/TextFiles/HdAuroraTest_Setup.glsl new file mode 100644 index 0000000..c4f235b --- /dev/null +++ b/Tests/Assets/TextFiles/HdAuroraTest_Setup.glsl @@ -0,0 +1,46 @@ +void setupMaterial_d183faa1b8cb18d7( + Material_d183faa1b8cb18d7 material, + out vec3 base_color, + out vec3 specular_color, + out float specular_roughness, + out float specular_IOR, + out float coat, + out float coat_roughness, + out vec3 emission_color) +{ + // Graph input SS_ShaderRef1/base_color + //{ +vec3 SS_ShaderRef1_base_color//}; + = material.base_color; + base_color = SS_ShaderRef1_base_color;// Output connection + // Graph input SS_ShaderRef1/specular_color + //{ +vec3 SS_ShaderRef1_specular_color//}; + = material.specular_color; + specular_color = SS_ShaderRef1_specular_color;// Output connection + // Graph input SS_ShaderRef1/specular_roughness + //{ +float SS_ShaderRef1_specular_roughness//}; + = material.specular_roughness; + specular_roughness = SS_ShaderRef1_specular_roughness;// Output connection + // Graph input SS_ShaderRef1/specular_IOR + //{ +float SS_ShaderRef1_specular_IOR//}; + = material.specular_IOR; + specular_IOR = SS_ShaderRef1_specular_IOR;// Output connection + // Graph input SS_ShaderRef1/coat + //{ +float SS_ShaderRef1_coat//}; + = material.coat; + coat = SS_ShaderRef1_coat;// Output connection + // Graph input SS_ShaderRef1/coat_roughness + //{ +float SS_ShaderRef1_coat_roughness//}; + = material.coat_roughness; + coat_roughness = SS_ShaderRef1_coat_roughness;// Output connection + // Graph input SS_ShaderRef1/emission_color + //{ +vec3 SS_ShaderRef1_emission_color//}; + = material.emission_color; + emission_color = SS_ShaderRef1_emission_color;// Output connection +} diff --git a/Tests/Assets/TextFiles/HdAuroraTest_Struct.glsl b/Tests/Assets/TextFiles/HdAuroraTest_Struct.glsl new file mode 100644 index 0000000..c404550 --- /dev/null +++ b/Tests/Assets/TextFiles/HdAuroraTest_Struct.glsl @@ -0,0 +1,9 @@ +struct Material_d183faa1b8cb18d7 { + vec3 base_color; + vec3 specular_color; + float specular_roughness; + float specular_IOR; + vec3 emission_color; + float coat; + float coat_roughness; +}; diff --git a/Tests/Assets/TextFiles/HdAuroraTextureTest_Defs.glsl b/Tests/Assets/TextFiles/HdAuroraTextureTest_Defs.glsl new file mode 100644 index 0000000..1ea1bd8 --- /dev/null +++ b/Tests/Assets/TextFiles/HdAuroraTextureTest_Defs.glsl @@ -0,0 +1,33 @@ + +// Definition for implementation IM_texcoord_vector2_genglsl +//{ +//}; + +// Definition for implementation IM_image_color3_genglsl +//{ + // Included from lib/$fileTransformUv + vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset) + { + uv = uv * uv_scale + uv_offset; + return uv; + } + + + void mx_image_color3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) + { + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rgb; + } + +//}; + +// Definition for implementation IM_image_float_genglsl +//{ + + void mx_image_float(sampler2D tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result) + { + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).r; + } + +//}; diff --git a/Tests/Assets/TextFiles/HdAuroraTextureTest_Setup.glsl b/Tests/Assets/TextFiles/HdAuroraTextureTest_Setup.glsl new file mode 100644 index 0000000..6272dfb --- /dev/null +++ b/Tests/Assets/TextFiles/HdAuroraTextureTest_Setup.glsl @@ -0,0 +1,212 @@ +void setupMaterial_498292f537214e2a( + Material_498292f537214e2a material, + sampler2D base_color_image_image_parameter, + sampler2D specular_roughness_image_image_parameter, + out vec3 base_color, + out float metalness, + out float specular_roughness, + out float specular_IOR, + out float transmission, + out float coat, + out float coat_roughness, + out vec3 emission_color) +{ + //Temp input variables for base_color_image + vec3 nodeOutTmp_base_color_image_out; //Temp output variable for out + sampler2D nodeTmp_base_color_image_file; //Temp input variable for file + int nodeTmp_base_color_image_layer; //Temp input variable for layer + vec3 nodeTmp_base_color_image_default; //Temp input variable for default + vec2 nodeTmp_base_color_image_texcoord; //Temp input variable for texcoord + int nodeTmp_base_color_image_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_base_color_image_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_base_color_image_filtertype; //Temp input variable for filtertype + int nodeTmp_base_color_image_framerange; //Temp input variable for framerange + int nodeTmp_base_color_image_frameoffset; //Temp input variable for frameoffset + int nodeTmp_base_color_image_frameendaction; //Temp input variable for frameendaction + vec2 nodeTmp_base_color_image_uv_scale; //Temp input variable for uv_scale + vec2 nodeTmp_base_color_image_uv_offset; //Temp input variable for uv_offset + // Graph input NG1/base_color_image/file + //{ +sampler2D base_color_image_file1//}; + = base_color_image_image_parameter; + nodeTmp_base_color_image_file = base_color_image_file1;// Output connection + // Graph input NG1/base_color_image/layer + //{ +int base_color_image_layer1 = 0//}; +; + nodeTmp_base_color_image_layer = base_color_image_layer1;// Output connection + // Graph input NG1/base_color_image/default + //{ +vec3 base_color_image_default1 = vec3(0, 0, 0)//}; +; + nodeTmp_base_color_image_default = base_color_image_default1;// Output connection + //Temp input variables for geomprop_UV0 + vec2 nodeOutTmp_geomprop_UV0_out; //Temp output variable for out + int nodeTmp_geomprop_UV0_index; //Temp input variable for index + // Graph input function call texcoord (See definition IM_texcoord_vector2_genglsl) +{ + //{ + vec2 geomprop_UV0_out1 = vertexData.texCoord; +//}; + nodeOutTmp_geomprop_UV0_out = geomprop_UV0_out1;// Output connection + nodeTmp_base_color_image_texcoord = geomprop_UV0_out1;// Output connection +} + // Graph input NG1/base_color_image/uaddressmode + //{ +int base_color_image_uaddressmode1 = 2//}; +; + nodeTmp_base_color_image_uaddressmode = base_color_image_uaddressmode1;// Output connection + // Graph input NG1/base_color_image/vaddressmode + //{ +int base_color_image_vaddressmode1 = 2//}; +; + nodeTmp_base_color_image_vaddressmode = base_color_image_vaddressmode1;// Output connection + // Graph input NG1/base_color_image/filtertype + //{ +int base_color_image_filtertype1 = 1//}; +; + nodeTmp_base_color_image_filtertype = base_color_image_filtertype1;// Output connection + // Graph input NG1/base_color_image/framerange + //{ +int base_color_image_framerange1 = 0//}; +; + nodeTmp_base_color_image_framerange = base_color_image_framerange1;// Output connection + // Graph input NG1/base_color_image/frameoffset + //{ +int base_color_image_frameoffset1 = 0//}; +; + nodeTmp_base_color_image_frameoffset = base_color_image_frameoffset1;// Output connection + // Graph input NG1/base_color_image/frameendaction + //{ +int base_color_image_frameendaction1 = 0//}; +; + nodeTmp_base_color_image_frameendaction = base_color_image_frameendaction1;// Output connection + // Graph input + //{ +vec2 base_color_image_uv_scale1 = vec2(1, 1)//}; +; + nodeTmp_base_color_image_uv_scale = base_color_image_uv_scale1;// Output connection + // Graph input + //{ +vec2 base_color_image_uv_offset1 = vec2(0, 0)//}; +; + nodeTmp_base_color_image_uv_offset = base_color_image_uv_offset1;// Output connection + // Graph input function call base_color (See definition IM_image_color3_genglsl) +{ + //{ + vec3 base_color_image_out = vec3(0.0); + mx_image_color3(nodeTmp_base_color_image_file, nodeTmp_base_color_image_layer, nodeTmp_base_color_image_default, nodeTmp_base_color_image_texcoord, nodeTmp_base_color_image_uaddressmode, nodeTmp_base_color_image_vaddressmode, nodeTmp_base_color_image_filtertype, nodeTmp_base_color_image_framerange, nodeTmp_base_color_image_frameoffset, nodeTmp_base_color_image_frameendaction, nodeTmp_base_color_image_uv_scale, nodeTmp_base_color_image_uv_offset, base_color_image_out); +//}; + nodeOutTmp_base_color_image_out = base_color_image_out;// Output connection + base_color = base_color_image_out;// Output connection +} + // Graph input SS_Material/metalness + //{ +float SS_Material_metalness//}; + = material.metalness; + metalness = SS_Material_metalness;// Output connection + //Temp input variables for specular_roughness_image + float nodeOutTmp_specular_roughness_image_out; //Temp output variable for out + sampler2D nodeTmp_specular_roughness_image_file; //Temp input variable for file + int nodeTmp_specular_roughness_image_layer; //Temp input variable for layer + float nodeTmp_specular_roughness_image_default; //Temp input variable for default + vec2 nodeTmp_specular_roughness_image_texcoord; //Temp input variable for texcoord + int nodeTmp_specular_roughness_image_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_specular_roughness_image_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_specular_roughness_image_filtertype; //Temp input variable for filtertype + int nodeTmp_specular_roughness_image_framerange; //Temp input variable for framerange + int nodeTmp_specular_roughness_image_frameoffset; //Temp input variable for frameoffset + int nodeTmp_specular_roughness_image_frameendaction; //Temp input variable for frameendaction + vec2 nodeTmp_specular_roughness_image_uv_scale; //Temp input variable for uv_scale + vec2 nodeTmp_specular_roughness_image_uv_offset; //Temp input variable for uv_offset + // Graph input NG2/specular_roughness_image/file + //{ +sampler2D specular_roughness_image_file1//}; + = specular_roughness_image_image_parameter; + nodeTmp_specular_roughness_image_file = specular_roughness_image_file1;// Output connection + // Graph input NG2/specular_roughness_image/layer + //{ +int specular_roughness_image_layer1 = 0//}; +; + nodeTmp_specular_roughness_image_layer = specular_roughness_image_layer1;// Output connection + // Graph input NG2/specular_roughness_image/default + //{ +float specular_roughness_image_default1 = 0//}; +; + nodeTmp_specular_roughness_image_default = specular_roughness_image_default1;// Output connection + nodeTmp_specular_roughness_image_texcoord = nodeOutTmp_geomprop_UV0_out;// Output connection + // Graph input NG2/specular_roughness_image/uaddressmode + //{ +int specular_roughness_image_uaddressmode1 = 2//}; +; + nodeTmp_specular_roughness_image_uaddressmode = specular_roughness_image_uaddressmode1;// Output connection + // Graph input NG2/specular_roughness_image/vaddressmode + //{ +int specular_roughness_image_vaddressmode1 = 2//}; +; + nodeTmp_specular_roughness_image_vaddressmode = specular_roughness_image_vaddressmode1;// Output connection + // Graph input NG2/specular_roughness_image/filtertype + //{ +int specular_roughness_image_filtertype1 = 1//}; +; + nodeTmp_specular_roughness_image_filtertype = specular_roughness_image_filtertype1;// Output connection + // Graph input NG2/specular_roughness_image/framerange + //{ +int specular_roughness_image_framerange1 = 0//}; +; + nodeTmp_specular_roughness_image_framerange = specular_roughness_image_framerange1;// Output connection + // Graph input NG2/specular_roughness_image/frameoffset + //{ +int specular_roughness_image_frameoffset1 = 0//}; +; + nodeTmp_specular_roughness_image_frameoffset = specular_roughness_image_frameoffset1;// Output connection + // Graph input NG2/specular_roughness_image/frameendaction + //{ +int specular_roughness_image_frameendaction1 = 0//}; +; + nodeTmp_specular_roughness_image_frameendaction = specular_roughness_image_frameendaction1;// Output connection + // Graph input + //{ +vec2 specular_roughness_image_uv_scale1 = vec2(1, 1)//}; +; + nodeTmp_specular_roughness_image_uv_scale = specular_roughness_image_uv_scale1;// Output connection + // Graph input + //{ +vec2 specular_roughness_image_uv_offset1 = vec2(0, 0)//}; +; + nodeTmp_specular_roughness_image_uv_offset = specular_roughness_image_uv_offset1;// Output connection + // Graph input function call specular_roughness (See definition IM_image_float_genglsl) +{ + //{ + float specular_roughness_image_out = 0.0; + mx_image_float(nodeTmp_specular_roughness_image_file, nodeTmp_specular_roughness_image_layer, nodeTmp_specular_roughness_image_default, nodeTmp_specular_roughness_image_texcoord, nodeTmp_specular_roughness_image_uaddressmode, nodeTmp_specular_roughness_image_vaddressmode, nodeTmp_specular_roughness_image_filtertype, nodeTmp_specular_roughness_image_framerange, nodeTmp_specular_roughness_image_frameoffset, nodeTmp_specular_roughness_image_frameendaction, nodeTmp_specular_roughness_image_uv_scale, nodeTmp_specular_roughness_image_uv_offset, specular_roughness_image_out); +//}; + nodeOutTmp_specular_roughness_image_out = specular_roughness_image_out;// Output connection + specular_roughness = specular_roughness_image_out;// Output connection +} + // Graph input SS_Material/specular_IOR + //{ +float SS_Material_specular_IOR//}; + = material.specular_IOR; + specular_IOR = SS_Material_specular_IOR;// Output connection + // Graph input SS_Material/transmission + //{ +float SS_Material_transmission//}; + = material.transmission; + transmission = SS_Material_transmission;// Output connection + // Graph input SS_Material/coat + //{ +float SS_Material_coat//}; + = material.coat; + coat = SS_Material_coat;// Output connection + // Graph input SS_Material/coat_roughness + //{ +float SS_Material_coat_roughness//}; + = material.coat_roughness; + coat_roughness = SS_Material_coat_roughness;// Output connection + // Graph input SS_Material/emission_color + //{ +vec3 SS_Material_emission_color//}; + = material.emission_color; + emission_color = SS_Material_emission_color;// Output connection +} diff --git a/Tests/Assets/TextFiles/HdAuroraTextureTest_Struct.glsl b/Tests/Assets/TextFiles/HdAuroraTextureTest_Struct.glsl new file mode 100644 index 0000000..bf9b823 --- /dev/null +++ b/Tests/Assets/TextFiles/HdAuroraTextureTest_Struct.glsl @@ -0,0 +1,8 @@ +struct Material_498292f537214e2a { + float specular_IOR; + vec3 emission_color; + float transmission; + float coat; + float coat_roughness; + float metalness; +}; diff --git a/Tests/Assets/TextFiles/Mask_Defs.glsl b/Tests/Assets/TextFiles/Mask_Defs.glsl new file mode 100644 index 0000000..230a44c --- /dev/null +++ b/Tests/Assets/TextFiles/Mask_Defs.glsl @@ -0,0 +1,110 @@ + +// Definition for implementation IM_texcoord_vector2_genglsl +//{ +//}; + +// Definition for implementation IM_image_color3_genglsl +//{ + // Included from lib/$fileTransformUv + vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset) + { + uv = uv * uv_scale + uv_offset; + return uv; + } + + + void mx_image_color3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) + { + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rgb; + } + +//}; + +// Definition for implementation IM_multiply_vector2FA_genglsl +//{ +//}; + +// Definition for implementation adsk:NG_adsk_bitmap_color3 +//{ + void mx_rotate_vector2(vec2 _in, float amount, out vec2 result) + { + float rotationRadians = radians(amount); + float sa = sin(rotationRadians); + float ca = cos(rotationRadians); + result = vec2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y); + } + + void NG_place2d_vector2(vec2 texcoord1, vec2 pivot, vec2 scale, float rotate, vec2 offset, int operationorder, out vec2 out1) + { + vec2 N_subpivot_out = texcoord1 - pivot; + vec2 N_applyscale_out = N_subpivot_out / scale; + vec2 N_applyoffset2_out = N_subpivot_out - offset; + vec2 N_applyrot_out = vec2(0.0); + mx_rotate_vector2(N_applyscale_out, rotate, N_applyrot_out); + vec2 N_applyrot2_out = vec2(0.0); + mx_rotate_vector2(N_applyoffset2_out, rotate, N_applyrot2_out); + vec2 N_applyoffset_out = N_applyrot_out - offset; + vec2 N_applyscale2_out = N_applyrot2_out / scale; + vec2 N_addpivot_out = N_applyoffset_out + pivot; + vec2 N_addpivot2_out = N_applyscale2_out + pivot; + vec2 N_switch_operationorder_out = vec2(0.0); + if (float(operationorder) < float(1)) + { + N_switch_operationorder_out = N_addpivot_out; + } + else if (float(operationorder) < float(2)) + { + N_switch_operationorder_out = N_addpivot2_out; + } + else if (float(operationorder) < float(3)) + { + N_switch_operationorder_out = vec2(0, 0); + } + else if (float(operationorder) < float(4)) + { + N_switch_operationorder_out = vec2(0, 0); + } + else if (float(operationorder) < float(5)) + { + N_switch_operationorder_out = vec2(0, 0); + } + out1 = N_switch_operationorder_out; + } + + void adsk_NG_adsk_bitmap_color3(sampler2D file, vec2 realworld_offset, vec2 realworld_scale, vec2 uv_offset, vec2 uv_scale, float rotation_angle, float rgbamount, bool invert, int uaddressmode, int vaddressmode, int uv_index, out vec3 out1) + { + vec2 texcoord1_out = vertexData.texCoord; + vec2 total_offset_out = realworld_offset + uv_offset; + vec2 total_scale_out = realworld_scale / uv_scale; + const float rotation_angle_param_in2_tmp = -1; + float rotation_angle_param_out = rotation_angle * rotation_angle_param_in2_tmp; + vec2 a_place2d_out = vec2(0.0); + NG_place2d_vector2(texcoord1_out, vec2(0, 0), total_scale_out, rotation_angle_param_out, total_offset_out, 0, a_place2d_out); + vec3 b_image_out = vec3(0.0); + mx_image_color3(file, 0, vec3(0, 0, 0), a_place2d_out, uaddressmode, vaddressmode, 1, 0, 0, 0, vec2(1, 1), vec2(0, 0), b_image_out); + vec3 image_brightness_out = b_image_out * rgbamount; + const vec3 image_invert_amount_tmp = vec3(1, 1, 1); + vec3 image_invert_out = image_invert_amount_tmp - image_brightness_out; + const bool image_convert_value2_tmp = true; + vec3 image_convert_out = (invert == image_convert_value2_tmp) ? image_invert_out : image_brightness_out; + out1 = image_convert_out; + } + +//}; + +// Definition for implementation IM_difference_color3_genglsl +//{ +//}; + +// Definition for implementation IM_convert_color3_vector3_genglsl +//{ +//}; + +// Definition for implementation IM_magnitude_vector3_genglsl +//{ +//}; + +// Definition for implementation IM_ifgreater_color3_genglsl +//{ +//}; diff --git a/Tests/Assets/TextFiles/Mask_Setup.glsl b/Tests/Assets/TextFiles/Mask_Setup.glsl new file mode 100644 index 0000000..ce8d7ab --- /dev/null +++ b/Tests/Assets/TextFiles/Mask_Setup.glsl @@ -0,0 +1,408 @@ +void setupMaterial_54a1fee27fdeec69( + Material_54a1fee27fdeec69 material, + sampler2D basecolor_bitmap_image_parameter, + sampler2D opacity_bitmap_image_parameter, + out float base, + out vec3 base_color, + out float metalness, + out float specular, + out float specular_roughness, + out float specular_IOR, + out vec3 opacity, + out bool thin_walled) +{ + // Graph input TestMaskWithChromeKeyDecal/base + //{ +float TestMaskWithChromeKeyDecal_base//}; + = material.base; + base = TestMaskWithChromeKeyDecal_base;// Output connection + //Temp input variables for basecolor_bitmap + vec3 nodeOutTmp_basecolor_bitmap_out; //Temp output variable for out + sampler2D nodeTmp_basecolor_bitmap_file; //Temp input variable for file + vec2 nodeTmp_basecolor_bitmap_realworld_offset; //Temp input variable for realworld_offset + vec2 nodeTmp_basecolor_bitmap_realworld_scale; //Temp input variable for realworld_scale + vec2 nodeTmp_basecolor_bitmap_uv_offset; //Temp input variable for uv_offset + vec2 nodeTmp_basecolor_bitmap_uv_scale; //Temp input variable for uv_scale + float nodeTmp_basecolor_bitmap_rotation_angle; //Temp input variable for rotation_angle + float nodeTmp_basecolor_bitmap_rgbamount; //Temp input variable for rgbamount + bool nodeTmp_basecolor_bitmap_invert; //Temp input variable for invert + int nodeTmp_basecolor_bitmap_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_basecolor_bitmap_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_basecolor_bitmap_uv_index; //Temp input variable for uv_index + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/file + //{ +sampler2D basecolor_bitmap_file1//}; + = basecolor_bitmap_image_parameter; + nodeTmp_basecolor_bitmap_file = basecolor_bitmap_file1;// Output connection + //Temp input variables for basecolor_bitmap_realworld_offset_unit + vec2 nodeOutTmp_basecolor_bitmap_realworld_offset_unit_out; //Temp output variable for out + vec2 nodeTmp_basecolor_bitmap_realworld_offset_unit_in1; //Temp input variable for in1 + float nodeTmp_basecolor_bitmap_realworld_offset_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/realworld_offset + //{ +vec2 basecolor_bitmap_realworld_offset_unit_in11//}; + = material.basecolor_bitmap_realworld_offset; + nodeTmp_basecolor_bitmap_realworld_offset_unit_in1 = basecolor_bitmap_realworld_offset_unit_in11;// Output connection + // Graph input + //{ +float basecolor_bitmap_realworld_offset_unit_in21 = 1//}; +; + nodeTmp_basecolor_bitmap_realworld_offset_unit_in2 = basecolor_bitmap_realworld_offset_unit_in21;// Output connection + // Graph input function call realworld_offset (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 basecolor_bitmap_realworld_offset_unit_out = nodeTmp_basecolor_bitmap_realworld_offset_unit_in1 * nodeTmp_basecolor_bitmap_realworld_offset_unit_in2; +//}; + nodeOutTmp_basecolor_bitmap_realworld_offset_unit_out = basecolor_bitmap_realworld_offset_unit_out;// Output connection + nodeTmp_basecolor_bitmap_realworld_offset = basecolor_bitmap_realworld_offset_unit_out;// Output connection +} + //Temp input variables for basecolor_bitmap_realworld_scale_unit + vec2 nodeOutTmp_basecolor_bitmap_realworld_scale_unit_out; //Temp output variable for out + vec2 nodeTmp_basecolor_bitmap_realworld_scale_unit_in1; //Temp input variable for in1 + float nodeTmp_basecolor_bitmap_realworld_scale_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/realworld_scale + //{ +vec2 basecolor_bitmap_realworld_scale_unit_in11//}; + = material.basecolor_bitmap_realworld_scale; + nodeTmp_basecolor_bitmap_realworld_scale_unit_in1 = basecolor_bitmap_realworld_scale_unit_in11;// Output connection + // Graph input + //{ +float basecolor_bitmap_realworld_scale_unit_in21 = 1//}; +; + nodeTmp_basecolor_bitmap_realworld_scale_unit_in2 = basecolor_bitmap_realworld_scale_unit_in21;// Output connection + // Graph input function call realworld_scale (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 basecolor_bitmap_realworld_scale_unit_out = nodeTmp_basecolor_bitmap_realworld_scale_unit_in1 * nodeTmp_basecolor_bitmap_realworld_scale_unit_in2; +//}; + nodeOutTmp_basecolor_bitmap_realworld_scale_unit_out = basecolor_bitmap_realworld_scale_unit_out;// Output connection + nodeTmp_basecolor_bitmap_realworld_scale = basecolor_bitmap_realworld_scale_unit_out;// Output connection +} + //Temp input variables for basecolor_bitmap_uv_offset_unit + vec2 nodeOutTmp_basecolor_bitmap_uv_offset_unit_out; //Temp output variable for out + vec2 nodeTmp_basecolor_bitmap_uv_offset_unit_in1; //Temp input variable for in1 + float nodeTmp_basecolor_bitmap_uv_offset_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/uv_offset + //{ +vec2 basecolor_bitmap_uv_offset_unit_in11//}; + = material.basecolor_bitmap_uv_offset; + nodeTmp_basecolor_bitmap_uv_offset_unit_in1 = basecolor_bitmap_uv_offset_unit_in11;// Output connection + // Graph input + //{ +float basecolor_bitmap_uv_offset_unit_in21 = 1//}; +; + nodeTmp_basecolor_bitmap_uv_offset_unit_in2 = basecolor_bitmap_uv_offset_unit_in21;// Output connection + // Graph input function call uv_offset (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 basecolor_bitmap_uv_offset_unit_out = nodeTmp_basecolor_bitmap_uv_offset_unit_in1 * nodeTmp_basecolor_bitmap_uv_offset_unit_in2; +//}; + nodeOutTmp_basecolor_bitmap_uv_offset_unit_out = basecolor_bitmap_uv_offset_unit_out;// Output connection + nodeTmp_basecolor_bitmap_uv_offset = basecolor_bitmap_uv_offset_unit_out;// Output connection +} + //Temp input variables for basecolor_bitmap_uv_scale_unit + vec2 nodeOutTmp_basecolor_bitmap_uv_scale_unit_out; //Temp output variable for out + vec2 nodeTmp_basecolor_bitmap_uv_scale_unit_in1; //Temp input variable for in1 + float nodeTmp_basecolor_bitmap_uv_scale_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/uv_scale + //{ +vec2 basecolor_bitmap_uv_scale_unit_in11//}; + = material.basecolor_bitmap_uv_scale; + nodeTmp_basecolor_bitmap_uv_scale_unit_in1 = basecolor_bitmap_uv_scale_unit_in11;// Output connection + // Graph input + //{ +float basecolor_bitmap_uv_scale_unit_in21 = 1//}; +; + nodeTmp_basecolor_bitmap_uv_scale_unit_in2 = basecolor_bitmap_uv_scale_unit_in21;// Output connection + // Graph input function call uv_scale (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 basecolor_bitmap_uv_scale_unit_out = nodeTmp_basecolor_bitmap_uv_scale_unit_in1 * nodeTmp_basecolor_bitmap_uv_scale_unit_in2; +//}; + nodeOutTmp_basecolor_bitmap_uv_scale_unit_out = basecolor_bitmap_uv_scale_unit_out;// Output connection + nodeTmp_basecolor_bitmap_uv_scale = basecolor_bitmap_uv_scale_unit_out;// Output connection +} + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/rotation_angle + //{ +float basecolor_bitmap_rotation_angle1//}; + = material.basecolor_bitmap_rotation_angle; + nodeTmp_basecolor_bitmap_rotation_angle = basecolor_bitmap_rotation_angle1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/rgbamount + //{ +float basecolor_bitmap_rgbamount1 = 1//}; +; + nodeTmp_basecolor_bitmap_rgbamount = basecolor_bitmap_rgbamount1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/invert + //{ +bool basecolor_bitmap_invert1 = false//}; +; + nodeTmp_basecolor_bitmap_invert = basecolor_bitmap_invert1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/uaddressmode + //{ +int basecolor_bitmap_uaddressmode1 = 1//}; +; + nodeTmp_basecolor_bitmap_uaddressmode = basecolor_bitmap_uaddressmode1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/vaddressmode + //{ +int basecolor_bitmap_vaddressmode1 = 1//}; +; + nodeTmp_basecolor_bitmap_vaddressmode = basecolor_bitmap_vaddressmode1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/basecolor_bitmap/uv_index + //{ +int basecolor_bitmap_uv_index1 = 0//}; +; + nodeTmp_basecolor_bitmap_uv_index = basecolor_bitmap_uv_index1;// Output connection + // Graph input function call base_color (See definition adsk:NG_adsk_bitmap_color3) +{ + //{ + vec3 basecolor_bitmap_out = vec3(0.0); + adsk_NG_adsk_bitmap_color3(nodeTmp_basecolor_bitmap_file, nodeTmp_basecolor_bitmap_realworld_offset, nodeTmp_basecolor_bitmap_realworld_scale, nodeTmp_basecolor_bitmap_uv_offset, nodeTmp_basecolor_bitmap_uv_scale, nodeTmp_basecolor_bitmap_rotation_angle, nodeTmp_basecolor_bitmap_rgbamount, nodeTmp_basecolor_bitmap_invert, nodeTmp_basecolor_bitmap_uaddressmode, nodeTmp_basecolor_bitmap_vaddressmode, nodeTmp_basecolor_bitmap_uv_index, basecolor_bitmap_out); +//}; + nodeOutTmp_basecolor_bitmap_out = basecolor_bitmap_out;// Output connection + base_color = basecolor_bitmap_out;// Output connection +} + // Graph input TestMaskWithChromeKeyDecal/metalness + //{ +float TestMaskWithChromeKeyDecal_metalness//}; + = material.metalness; + metalness = TestMaskWithChromeKeyDecal_metalness;// Output connection + // Graph input TestMaskWithChromeKeyDecal/specular + //{ +float TestMaskWithChromeKeyDecal_specular//}; + = material.specular; + specular = TestMaskWithChromeKeyDecal_specular;// Output connection + // Graph input TestMaskWithChromeKeyDecal/specular_roughness + //{ +float TestMaskWithChromeKeyDecal_specular_roughness//}; + = material.specular_roughness; + specular_roughness = TestMaskWithChromeKeyDecal_specular_roughness;// Output connection + // Graph input TestMaskWithChromeKeyDecal/specular_IOR + //{ +float TestMaskWithChromeKeyDecal_specular_IOR//}; + = material.specular_IOR; + specular_IOR = TestMaskWithChromeKeyDecal_specular_IOR;// Output connection + //Temp input variables for convert_to_opacity_white_black + vec3 nodeOutTmp_convert_to_opacity_white_black_out; //Temp output variable for out + float nodeTmp_convert_to_opacity_white_black_value1; //Temp input variable for value1 + float nodeTmp_convert_to_opacity_white_black_value2; //Temp input variable for value2 + vec3 nodeTmp_convert_to_opacity_white_black_in1; //Temp input variable for in1 + vec3 nodeTmp_convert_to_opacity_white_black_in2; //Temp input variable for in2 + //Temp input variables for magnitude_vector3 + float nodeOutTmp_magnitude_vector3_out; //Temp output variable for out + vec3 nodeTmp_magnitude_vector3_in; //Temp input variable for in + //Temp input variables for convert_color3_to_vector3 + vec3 nodeOutTmp_convert_color3_to_vector3_out; //Temp output variable for out + vec3 nodeTmp_convert_color3_to_vector3_in; //Temp input variable for in + //Temp input variables for difference_image_with_chromekey + vec3 nodeOutTmp_difference_image_with_chromekey_out; //Temp output variable for out + vec3 nodeTmp_difference_image_with_chromekey_fg; //Temp input variable for fg + vec3 nodeTmp_difference_image_with_chromekey_bg; //Temp input variable for bg + float nodeTmp_difference_image_with_chromekey_mix; //Temp input variable for mix + //Temp input variables for opacity_bitmap + vec3 nodeOutTmp_opacity_bitmap_out; //Temp output variable for out + sampler2D nodeTmp_opacity_bitmap_file; //Temp input variable for file + vec2 nodeTmp_opacity_bitmap_realworld_offset; //Temp input variable for realworld_offset + vec2 nodeTmp_opacity_bitmap_realworld_scale; //Temp input variable for realworld_scale + vec2 nodeTmp_opacity_bitmap_uv_offset; //Temp input variable for uv_offset + vec2 nodeTmp_opacity_bitmap_uv_scale; //Temp input variable for uv_scale + float nodeTmp_opacity_bitmap_rotation_angle; //Temp input variable for rotation_angle + float nodeTmp_opacity_bitmap_rgbamount; //Temp input variable for rgbamount + bool nodeTmp_opacity_bitmap_invert; //Temp input variable for invert + int nodeTmp_opacity_bitmap_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_opacity_bitmap_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_opacity_bitmap_uv_index; //Temp input variable for uv_index + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/file + //{ +sampler2D opacity_bitmap_file1//}; + = opacity_bitmap_image_parameter; + nodeTmp_opacity_bitmap_file = opacity_bitmap_file1;// Output connection + //Temp input variables for opacity_bitmap_realworld_offset_unit + vec2 nodeOutTmp_opacity_bitmap_realworld_offset_unit_out; //Temp output variable for out + vec2 nodeTmp_opacity_bitmap_realworld_offset_unit_in1; //Temp input variable for in1 + float nodeTmp_opacity_bitmap_realworld_offset_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/realworld_offset + //{ +vec2 opacity_bitmap_realworld_offset_unit_in11//}; + = material.opacity_bitmap_realworld_offset; + nodeTmp_opacity_bitmap_realworld_offset_unit_in1 = opacity_bitmap_realworld_offset_unit_in11;// Output connection + // Graph input + //{ +float opacity_bitmap_realworld_offset_unit_in21 = 1//}; +; + nodeTmp_opacity_bitmap_realworld_offset_unit_in2 = opacity_bitmap_realworld_offset_unit_in21;// Output connection + // Graph input function call realworld_offset (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 opacity_bitmap_realworld_offset_unit_out = nodeTmp_opacity_bitmap_realworld_offset_unit_in1 * nodeTmp_opacity_bitmap_realworld_offset_unit_in2; +//}; + nodeOutTmp_opacity_bitmap_realworld_offset_unit_out = opacity_bitmap_realworld_offset_unit_out;// Output connection + nodeTmp_opacity_bitmap_realworld_offset = opacity_bitmap_realworld_offset_unit_out;// Output connection +} + //Temp input variables for opacity_bitmap_realworld_scale_unit + vec2 nodeOutTmp_opacity_bitmap_realworld_scale_unit_out; //Temp output variable for out + vec2 nodeTmp_opacity_bitmap_realworld_scale_unit_in1; //Temp input variable for in1 + float nodeTmp_opacity_bitmap_realworld_scale_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/realworld_scale + //{ +vec2 opacity_bitmap_realworld_scale_unit_in11//}; + = material.opacity_bitmap_realworld_scale; + nodeTmp_opacity_bitmap_realworld_scale_unit_in1 = opacity_bitmap_realworld_scale_unit_in11;// Output connection + // Graph input + //{ +float opacity_bitmap_realworld_scale_unit_in21 = 1//}; +; + nodeTmp_opacity_bitmap_realworld_scale_unit_in2 = opacity_bitmap_realworld_scale_unit_in21;// Output connection + // Graph input function call realworld_scale (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 opacity_bitmap_realworld_scale_unit_out = nodeTmp_opacity_bitmap_realworld_scale_unit_in1 * nodeTmp_opacity_bitmap_realworld_scale_unit_in2; +//}; + nodeOutTmp_opacity_bitmap_realworld_scale_unit_out = opacity_bitmap_realworld_scale_unit_out;// Output connection + nodeTmp_opacity_bitmap_realworld_scale = opacity_bitmap_realworld_scale_unit_out;// Output connection +} + //Temp input variables for opacity_bitmap_uv_offset_unit + vec2 nodeOutTmp_opacity_bitmap_uv_offset_unit_out; //Temp output variable for out + vec2 nodeTmp_opacity_bitmap_uv_offset_unit_in1; //Temp input variable for in1 + float nodeTmp_opacity_bitmap_uv_offset_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/uv_offset + //{ +vec2 opacity_bitmap_uv_offset_unit_in11//}; + = material.opacity_bitmap_uv_offset; + nodeTmp_opacity_bitmap_uv_offset_unit_in1 = opacity_bitmap_uv_offset_unit_in11;// Output connection + // Graph input + //{ +float opacity_bitmap_uv_offset_unit_in21 = 1//}; +; + nodeTmp_opacity_bitmap_uv_offset_unit_in2 = opacity_bitmap_uv_offset_unit_in21;// Output connection + // Graph input function call uv_offset (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 opacity_bitmap_uv_offset_unit_out = nodeTmp_opacity_bitmap_uv_offset_unit_in1 * nodeTmp_opacity_bitmap_uv_offset_unit_in2; +//}; + nodeOutTmp_opacity_bitmap_uv_offset_unit_out = opacity_bitmap_uv_offset_unit_out;// Output connection + nodeTmp_opacity_bitmap_uv_offset = opacity_bitmap_uv_offset_unit_out;// Output connection +} + //Temp input variables for opacity_bitmap_uv_scale_unit + vec2 nodeOutTmp_opacity_bitmap_uv_scale_unit_out; //Temp output variable for out + vec2 nodeTmp_opacity_bitmap_uv_scale_unit_in1; //Temp input variable for in1 + float nodeTmp_opacity_bitmap_uv_scale_unit_in2; //Temp input variable for in2 + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/uv_scale + //{ +vec2 opacity_bitmap_uv_scale_unit_in11//}; + = material.opacity_bitmap_uv_scale; + nodeTmp_opacity_bitmap_uv_scale_unit_in1 = opacity_bitmap_uv_scale_unit_in11;// Output connection + // Graph input + //{ +float opacity_bitmap_uv_scale_unit_in21 = 1//}; +; + nodeTmp_opacity_bitmap_uv_scale_unit_in2 = opacity_bitmap_uv_scale_unit_in21;// Output connection + // Graph input function call uv_scale (See definition IM_multiply_vector2FA_genglsl) +{ + //{ + vec2 opacity_bitmap_uv_scale_unit_out = nodeTmp_opacity_bitmap_uv_scale_unit_in1 * nodeTmp_opacity_bitmap_uv_scale_unit_in2; +//}; + nodeOutTmp_opacity_bitmap_uv_scale_unit_out = opacity_bitmap_uv_scale_unit_out;// Output connection + nodeTmp_opacity_bitmap_uv_scale = opacity_bitmap_uv_scale_unit_out;// Output connection +} + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/rotation_angle + //{ +float opacity_bitmap_rotation_angle1//}; + = material.opacity_bitmap_rotation_angle; + nodeTmp_opacity_bitmap_rotation_angle = opacity_bitmap_rotation_angle1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/rgbamount + //{ +float opacity_bitmap_rgbamount1 = 1//}; +; + nodeTmp_opacity_bitmap_rgbamount = opacity_bitmap_rgbamount1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/invert + //{ +bool opacity_bitmap_invert1 = false//}; +; + nodeTmp_opacity_bitmap_invert = opacity_bitmap_invert1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/uaddressmode + //{ +int opacity_bitmap_uaddressmode1 = 1//}; +; + nodeTmp_opacity_bitmap_uaddressmode = opacity_bitmap_uaddressmode1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/vaddressmode + //{ +int opacity_bitmap_vaddressmode1 = 1//}; +; + nodeTmp_opacity_bitmap_vaddressmode = opacity_bitmap_vaddressmode1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/opacity_bitmap/uv_index + //{ +int opacity_bitmap_uv_index1 = 0//}; +; + nodeTmp_opacity_bitmap_uv_index = opacity_bitmap_uv_index1;// Output connection + // Graph input function call fg (See definition adsk:NG_adsk_bitmap_color3) +{ + //{ + vec3 opacity_bitmap_out = vec3(0.0); + adsk_NG_adsk_bitmap_color3(nodeTmp_opacity_bitmap_file, nodeTmp_opacity_bitmap_realworld_offset, nodeTmp_opacity_bitmap_realworld_scale, nodeTmp_opacity_bitmap_uv_offset, nodeTmp_opacity_bitmap_uv_scale, nodeTmp_opacity_bitmap_rotation_angle, nodeTmp_opacity_bitmap_rgbamount, nodeTmp_opacity_bitmap_invert, nodeTmp_opacity_bitmap_uaddressmode, nodeTmp_opacity_bitmap_vaddressmode, nodeTmp_opacity_bitmap_uv_index, opacity_bitmap_out); +//}; + nodeOutTmp_opacity_bitmap_out = opacity_bitmap_out;// Output connection + nodeTmp_difference_image_with_chromekey_fg = opacity_bitmap_out;// Output connection +} + // Graph input TestMaskWithChromeKeyDecal_nodegraph/difference_image_with_chromekey/bg + //{ +vec3 difference_image_with_chromekey_bg1//}; + = material.difference_image_with_chromekey_bg; + nodeTmp_difference_image_with_chromekey_bg = difference_image_with_chromekey_bg1;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/difference_image_with_chromekey/mix + //{ +float difference_image_with_chromekey_mix1//}; + = material.difference_image_with_chromekey_mix; + nodeTmp_difference_image_with_chromekey_mix = difference_image_with_chromekey_mix1;// Output connection + // Graph input function call in (See definition IM_difference_color3_genglsl) +{ + //{ + vec3 difference_image_with_chromekey_out = (nodeTmp_difference_image_with_chromekey_mix*abs(nodeTmp_difference_image_with_chromekey_bg - nodeTmp_difference_image_with_chromekey_fg)) + ((1.0-nodeTmp_difference_image_with_chromekey_mix)*nodeTmp_difference_image_with_chromekey_bg); +//}; + nodeOutTmp_difference_image_with_chromekey_out = difference_image_with_chromekey_out;// Output connection + nodeTmp_convert_color3_to_vector3_in = difference_image_with_chromekey_out;// Output connection +} + // Graph input function call in (See definition IM_convert_color3_vector3_genglsl) +{ + //{ + vec3 convert_color3_to_vector3_out = vec3(nodeTmp_convert_color3_to_vector3_in.x, nodeTmp_convert_color3_to_vector3_in.y, nodeTmp_convert_color3_to_vector3_in.z); +//}; + nodeOutTmp_convert_color3_to_vector3_out = convert_color3_to_vector3_out;// Output connection + nodeTmp_magnitude_vector3_in = convert_color3_to_vector3_out;// Output connection +} + // Graph input function call value1 (See definition IM_magnitude_vector3_genglsl) +{ + //{ + float magnitude_vector3_out = length(nodeTmp_magnitude_vector3_in); +//}; + nodeOutTmp_magnitude_vector3_out = magnitude_vector3_out;// Output connection + nodeTmp_convert_to_opacity_white_black_value1 = magnitude_vector3_out;// Output connection +} + // Graph input TestMaskWithChromeKeyDecal_nodegraph/convert_to_opacity_white_black/value2 + //{ +float convert_to_opacity_white_black_value21//}; + = material.convert_to_opacity_white_black_value2; + nodeTmp_convert_to_opacity_white_black_value2 = convert_to_opacity_white_black_value21;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/convert_to_opacity_white_black/in1 + //{ +vec3 convert_to_opacity_white_black_in11//}; + = material.convert_to_opacity_white_black_in1; + nodeTmp_convert_to_opacity_white_black_in1 = convert_to_opacity_white_black_in11;// Output connection + // Graph input TestMaskWithChromeKeyDecal_nodegraph/convert_to_opacity_white_black/in2 + //{ +vec3 convert_to_opacity_white_black_in21//}; + = material.convert_to_opacity_white_black_in2; + nodeTmp_convert_to_opacity_white_black_in2 = convert_to_opacity_white_black_in21;// Output connection + // Graph input function call opacity (See definition IM_ifgreater_color3_genglsl) +{ + //{ + vec3 convert_to_opacity_white_black_out = (nodeTmp_convert_to_opacity_white_black_value1 > nodeTmp_convert_to_opacity_white_black_value2) ? nodeTmp_convert_to_opacity_white_black_in1 : nodeTmp_convert_to_opacity_white_black_in2; +//}; + nodeOutTmp_convert_to_opacity_white_black_out = convert_to_opacity_white_black_out;// Output connection + opacity = convert_to_opacity_white_black_out;// Output connection +} + // Graph input TestMaskWithChromeKeyDecal/thin_walled + //{ +bool TestMaskWithChromeKeyDecal_thin_walled//}; + = material.thin_walled; + thin_walled = TestMaskWithChromeKeyDecal_thin_walled;// Output connection +} diff --git a/Tests/Assets/TextFiles/Mask_Struct.glsl b/Tests/Assets/TextFiles/Mask_Struct.glsl new file mode 100644 index 0000000..89d44a4 --- /dev/null +++ b/Tests/Assets/TextFiles/Mask_Struct.glsl @@ -0,0 +1,23 @@ +struct Material_54a1fee27fdeec69 { + float base; + float metalness; + float specular; + float specular_roughness; + float specular_IOR; + int thin_walled; + vec2 basecolor_bitmap_realworld_offset; + vec2 basecolor_bitmap_realworld_scale; + vec2 basecolor_bitmap_uv_offset; + vec2 basecolor_bitmap_uv_scale; + float basecolor_bitmap_rotation_angle; + vec2 opacity_bitmap_realworld_offset; + vec2 opacity_bitmap_realworld_scale; + vec2 opacity_bitmap_uv_offset; + vec2 opacity_bitmap_uv_scale; + float opacity_bitmap_rotation_angle; + vec3 difference_image_with_chromekey_bg; + float difference_image_with_chromekey_mix; + float convert_to_opacity_white_black_value2; + vec3 convert_to_opacity_white_black_in1; + vec3 convert_to_opacity_white_black_in2; +}; diff --git a/Tests/Assets/TextFiles/TestMaterialX0_Defs.glsl b/Tests/Assets/TextFiles/TestMaterialX0_Defs.glsl new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Assets/TextFiles/TestMaterialX0_Setup.glsl b/Tests/Assets/TextFiles/TestMaterialX0_Setup.glsl new file mode 100644 index 0000000..c4f235b --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX0_Setup.glsl @@ -0,0 +1,46 @@ +void setupMaterial_d183faa1b8cb18d7( + Material_d183faa1b8cb18d7 material, + out vec3 base_color, + out vec3 specular_color, + out float specular_roughness, + out float specular_IOR, + out float coat, + out float coat_roughness, + out vec3 emission_color) +{ + // Graph input SS_ShaderRef1/base_color + //{ +vec3 SS_ShaderRef1_base_color//}; + = material.base_color; + base_color = SS_ShaderRef1_base_color;// Output connection + // Graph input SS_ShaderRef1/specular_color + //{ +vec3 SS_ShaderRef1_specular_color//}; + = material.specular_color; + specular_color = SS_ShaderRef1_specular_color;// Output connection + // Graph input SS_ShaderRef1/specular_roughness + //{ +float SS_ShaderRef1_specular_roughness//}; + = material.specular_roughness; + specular_roughness = SS_ShaderRef1_specular_roughness;// Output connection + // Graph input SS_ShaderRef1/specular_IOR + //{ +float SS_ShaderRef1_specular_IOR//}; + = material.specular_IOR; + specular_IOR = SS_ShaderRef1_specular_IOR;// Output connection + // Graph input SS_ShaderRef1/coat + //{ +float SS_ShaderRef1_coat//}; + = material.coat; + coat = SS_ShaderRef1_coat;// Output connection + // Graph input SS_ShaderRef1/coat_roughness + //{ +float SS_ShaderRef1_coat_roughness//}; + = material.coat_roughness; + coat_roughness = SS_ShaderRef1_coat_roughness;// Output connection + // Graph input SS_ShaderRef1/emission_color + //{ +vec3 SS_ShaderRef1_emission_color//}; + = material.emission_color; + emission_color = SS_ShaderRef1_emission_color;// Output connection +} diff --git a/Tests/Assets/TextFiles/TestMaterialX0_Struct.glsl b/Tests/Assets/TextFiles/TestMaterialX0_Struct.glsl new file mode 100644 index 0000000..c404550 --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX0_Struct.glsl @@ -0,0 +1,9 @@ +struct Material_d183faa1b8cb18d7 { + vec3 base_color; + vec3 specular_color; + float specular_roughness; + float specular_IOR; + vec3 emission_color; + float coat; + float coat_roughness; +}; diff --git a/Tests/Assets/TextFiles/TestMaterialX1_Defs.glsl b/Tests/Assets/TextFiles/TestMaterialX1_Defs.glsl new file mode 100644 index 0000000..e69de29 diff --git a/Tests/Assets/TextFiles/TestMaterialX1_Setup.glsl b/Tests/Assets/TextFiles/TestMaterialX1_Setup.glsl new file mode 100644 index 0000000..c4f235b --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX1_Setup.glsl @@ -0,0 +1,46 @@ +void setupMaterial_d183faa1b8cb18d7( + Material_d183faa1b8cb18d7 material, + out vec3 base_color, + out vec3 specular_color, + out float specular_roughness, + out float specular_IOR, + out float coat, + out float coat_roughness, + out vec3 emission_color) +{ + // Graph input SS_ShaderRef1/base_color + //{ +vec3 SS_ShaderRef1_base_color//}; + = material.base_color; + base_color = SS_ShaderRef1_base_color;// Output connection + // Graph input SS_ShaderRef1/specular_color + //{ +vec3 SS_ShaderRef1_specular_color//}; + = material.specular_color; + specular_color = SS_ShaderRef1_specular_color;// Output connection + // Graph input SS_ShaderRef1/specular_roughness + //{ +float SS_ShaderRef1_specular_roughness//}; + = material.specular_roughness; + specular_roughness = SS_ShaderRef1_specular_roughness;// Output connection + // Graph input SS_ShaderRef1/specular_IOR + //{ +float SS_ShaderRef1_specular_IOR//}; + = material.specular_IOR; + specular_IOR = SS_ShaderRef1_specular_IOR;// Output connection + // Graph input SS_ShaderRef1/coat + //{ +float SS_ShaderRef1_coat//}; + = material.coat; + coat = SS_ShaderRef1_coat;// Output connection + // Graph input SS_ShaderRef1/coat_roughness + //{ +float SS_ShaderRef1_coat_roughness//}; + = material.coat_roughness; + coat_roughness = SS_ShaderRef1_coat_roughness;// Output connection + // Graph input SS_ShaderRef1/emission_color + //{ +vec3 SS_ShaderRef1_emission_color//}; + = material.emission_color; + emission_color = SS_ShaderRef1_emission_color;// Output connection +} diff --git a/Tests/Assets/TextFiles/TestMaterialX1_Struct.glsl b/Tests/Assets/TextFiles/TestMaterialX1_Struct.glsl new file mode 100644 index 0000000..c404550 --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX1_Struct.glsl @@ -0,0 +1,9 @@ +struct Material_d183faa1b8cb18d7 { + vec3 base_color; + vec3 specular_color; + float specular_roughness; + float specular_IOR; + vec3 emission_color; + float coat; + float coat_roughness; +}; diff --git a/Tests/Assets/TextFiles/TestMaterialX2_Defs.glsl b/Tests/Assets/TextFiles/TestMaterialX2_Defs.glsl new file mode 100644 index 0000000..1ea1bd8 --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX2_Defs.glsl @@ -0,0 +1,33 @@ + +// Definition for implementation IM_texcoord_vector2_genglsl +//{ +//}; + +// Definition for implementation IM_image_color3_genglsl +//{ + // Included from lib/$fileTransformUv + vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset) + { + uv = uv * uv_scale + uv_offset; + return uv; + } + + + void mx_image_color3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) + { + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rgb; + } + +//}; + +// Definition for implementation IM_image_float_genglsl +//{ + + void mx_image_float(sampler2D tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result) + { + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).r; + } + +//}; diff --git a/Tests/Assets/TextFiles/TestMaterialX2_Setup.glsl b/Tests/Assets/TextFiles/TestMaterialX2_Setup.glsl new file mode 100644 index 0000000..6272dfb --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX2_Setup.glsl @@ -0,0 +1,212 @@ +void setupMaterial_498292f537214e2a( + Material_498292f537214e2a material, + sampler2D base_color_image_image_parameter, + sampler2D specular_roughness_image_image_parameter, + out vec3 base_color, + out float metalness, + out float specular_roughness, + out float specular_IOR, + out float transmission, + out float coat, + out float coat_roughness, + out vec3 emission_color) +{ + //Temp input variables for base_color_image + vec3 nodeOutTmp_base_color_image_out; //Temp output variable for out + sampler2D nodeTmp_base_color_image_file; //Temp input variable for file + int nodeTmp_base_color_image_layer; //Temp input variable for layer + vec3 nodeTmp_base_color_image_default; //Temp input variable for default + vec2 nodeTmp_base_color_image_texcoord; //Temp input variable for texcoord + int nodeTmp_base_color_image_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_base_color_image_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_base_color_image_filtertype; //Temp input variable for filtertype + int nodeTmp_base_color_image_framerange; //Temp input variable for framerange + int nodeTmp_base_color_image_frameoffset; //Temp input variable for frameoffset + int nodeTmp_base_color_image_frameendaction; //Temp input variable for frameendaction + vec2 nodeTmp_base_color_image_uv_scale; //Temp input variable for uv_scale + vec2 nodeTmp_base_color_image_uv_offset; //Temp input variable for uv_offset + // Graph input NG1/base_color_image/file + //{ +sampler2D base_color_image_file1//}; + = base_color_image_image_parameter; + nodeTmp_base_color_image_file = base_color_image_file1;// Output connection + // Graph input NG1/base_color_image/layer + //{ +int base_color_image_layer1 = 0//}; +; + nodeTmp_base_color_image_layer = base_color_image_layer1;// Output connection + // Graph input NG1/base_color_image/default + //{ +vec3 base_color_image_default1 = vec3(0, 0, 0)//}; +; + nodeTmp_base_color_image_default = base_color_image_default1;// Output connection + //Temp input variables for geomprop_UV0 + vec2 nodeOutTmp_geomprop_UV0_out; //Temp output variable for out + int nodeTmp_geomprop_UV0_index; //Temp input variable for index + // Graph input function call texcoord (See definition IM_texcoord_vector2_genglsl) +{ + //{ + vec2 geomprop_UV0_out1 = vertexData.texCoord; +//}; + nodeOutTmp_geomprop_UV0_out = geomprop_UV0_out1;// Output connection + nodeTmp_base_color_image_texcoord = geomprop_UV0_out1;// Output connection +} + // Graph input NG1/base_color_image/uaddressmode + //{ +int base_color_image_uaddressmode1 = 2//}; +; + nodeTmp_base_color_image_uaddressmode = base_color_image_uaddressmode1;// Output connection + // Graph input NG1/base_color_image/vaddressmode + //{ +int base_color_image_vaddressmode1 = 2//}; +; + nodeTmp_base_color_image_vaddressmode = base_color_image_vaddressmode1;// Output connection + // Graph input NG1/base_color_image/filtertype + //{ +int base_color_image_filtertype1 = 1//}; +; + nodeTmp_base_color_image_filtertype = base_color_image_filtertype1;// Output connection + // Graph input NG1/base_color_image/framerange + //{ +int base_color_image_framerange1 = 0//}; +; + nodeTmp_base_color_image_framerange = base_color_image_framerange1;// Output connection + // Graph input NG1/base_color_image/frameoffset + //{ +int base_color_image_frameoffset1 = 0//}; +; + nodeTmp_base_color_image_frameoffset = base_color_image_frameoffset1;// Output connection + // Graph input NG1/base_color_image/frameendaction + //{ +int base_color_image_frameendaction1 = 0//}; +; + nodeTmp_base_color_image_frameendaction = base_color_image_frameendaction1;// Output connection + // Graph input + //{ +vec2 base_color_image_uv_scale1 = vec2(1, 1)//}; +; + nodeTmp_base_color_image_uv_scale = base_color_image_uv_scale1;// Output connection + // Graph input + //{ +vec2 base_color_image_uv_offset1 = vec2(0, 0)//}; +; + nodeTmp_base_color_image_uv_offset = base_color_image_uv_offset1;// Output connection + // Graph input function call base_color (See definition IM_image_color3_genglsl) +{ + //{ + vec3 base_color_image_out = vec3(0.0); + mx_image_color3(nodeTmp_base_color_image_file, nodeTmp_base_color_image_layer, nodeTmp_base_color_image_default, nodeTmp_base_color_image_texcoord, nodeTmp_base_color_image_uaddressmode, nodeTmp_base_color_image_vaddressmode, nodeTmp_base_color_image_filtertype, nodeTmp_base_color_image_framerange, nodeTmp_base_color_image_frameoffset, nodeTmp_base_color_image_frameendaction, nodeTmp_base_color_image_uv_scale, nodeTmp_base_color_image_uv_offset, base_color_image_out); +//}; + nodeOutTmp_base_color_image_out = base_color_image_out;// Output connection + base_color = base_color_image_out;// Output connection +} + // Graph input SS_Material/metalness + //{ +float SS_Material_metalness//}; + = material.metalness; + metalness = SS_Material_metalness;// Output connection + //Temp input variables for specular_roughness_image + float nodeOutTmp_specular_roughness_image_out; //Temp output variable for out + sampler2D nodeTmp_specular_roughness_image_file; //Temp input variable for file + int nodeTmp_specular_roughness_image_layer; //Temp input variable for layer + float nodeTmp_specular_roughness_image_default; //Temp input variable for default + vec2 nodeTmp_specular_roughness_image_texcoord; //Temp input variable for texcoord + int nodeTmp_specular_roughness_image_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_specular_roughness_image_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_specular_roughness_image_filtertype; //Temp input variable for filtertype + int nodeTmp_specular_roughness_image_framerange; //Temp input variable for framerange + int nodeTmp_specular_roughness_image_frameoffset; //Temp input variable for frameoffset + int nodeTmp_specular_roughness_image_frameendaction; //Temp input variable for frameendaction + vec2 nodeTmp_specular_roughness_image_uv_scale; //Temp input variable for uv_scale + vec2 nodeTmp_specular_roughness_image_uv_offset; //Temp input variable for uv_offset + // Graph input NG2/specular_roughness_image/file + //{ +sampler2D specular_roughness_image_file1//}; + = specular_roughness_image_image_parameter; + nodeTmp_specular_roughness_image_file = specular_roughness_image_file1;// Output connection + // Graph input NG2/specular_roughness_image/layer + //{ +int specular_roughness_image_layer1 = 0//}; +; + nodeTmp_specular_roughness_image_layer = specular_roughness_image_layer1;// Output connection + // Graph input NG2/specular_roughness_image/default + //{ +float specular_roughness_image_default1 = 0//}; +; + nodeTmp_specular_roughness_image_default = specular_roughness_image_default1;// Output connection + nodeTmp_specular_roughness_image_texcoord = nodeOutTmp_geomprop_UV0_out;// Output connection + // Graph input NG2/specular_roughness_image/uaddressmode + //{ +int specular_roughness_image_uaddressmode1 = 2//}; +; + nodeTmp_specular_roughness_image_uaddressmode = specular_roughness_image_uaddressmode1;// Output connection + // Graph input NG2/specular_roughness_image/vaddressmode + //{ +int specular_roughness_image_vaddressmode1 = 2//}; +; + nodeTmp_specular_roughness_image_vaddressmode = specular_roughness_image_vaddressmode1;// Output connection + // Graph input NG2/specular_roughness_image/filtertype + //{ +int specular_roughness_image_filtertype1 = 1//}; +; + nodeTmp_specular_roughness_image_filtertype = specular_roughness_image_filtertype1;// Output connection + // Graph input NG2/specular_roughness_image/framerange + //{ +int specular_roughness_image_framerange1 = 0//}; +; + nodeTmp_specular_roughness_image_framerange = specular_roughness_image_framerange1;// Output connection + // Graph input NG2/specular_roughness_image/frameoffset + //{ +int specular_roughness_image_frameoffset1 = 0//}; +; + nodeTmp_specular_roughness_image_frameoffset = specular_roughness_image_frameoffset1;// Output connection + // Graph input NG2/specular_roughness_image/frameendaction + //{ +int specular_roughness_image_frameendaction1 = 0//}; +; + nodeTmp_specular_roughness_image_frameendaction = specular_roughness_image_frameendaction1;// Output connection + // Graph input + //{ +vec2 specular_roughness_image_uv_scale1 = vec2(1, 1)//}; +; + nodeTmp_specular_roughness_image_uv_scale = specular_roughness_image_uv_scale1;// Output connection + // Graph input + //{ +vec2 specular_roughness_image_uv_offset1 = vec2(0, 0)//}; +; + nodeTmp_specular_roughness_image_uv_offset = specular_roughness_image_uv_offset1;// Output connection + // Graph input function call specular_roughness (See definition IM_image_float_genglsl) +{ + //{ + float specular_roughness_image_out = 0.0; + mx_image_float(nodeTmp_specular_roughness_image_file, nodeTmp_specular_roughness_image_layer, nodeTmp_specular_roughness_image_default, nodeTmp_specular_roughness_image_texcoord, nodeTmp_specular_roughness_image_uaddressmode, nodeTmp_specular_roughness_image_vaddressmode, nodeTmp_specular_roughness_image_filtertype, nodeTmp_specular_roughness_image_framerange, nodeTmp_specular_roughness_image_frameoffset, nodeTmp_specular_roughness_image_frameendaction, nodeTmp_specular_roughness_image_uv_scale, nodeTmp_specular_roughness_image_uv_offset, specular_roughness_image_out); +//}; + nodeOutTmp_specular_roughness_image_out = specular_roughness_image_out;// Output connection + specular_roughness = specular_roughness_image_out;// Output connection +} + // Graph input SS_Material/specular_IOR + //{ +float SS_Material_specular_IOR//}; + = material.specular_IOR; + specular_IOR = SS_Material_specular_IOR;// Output connection + // Graph input SS_Material/transmission + //{ +float SS_Material_transmission//}; + = material.transmission; + transmission = SS_Material_transmission;// Output connection + // Graph input SS_Material/coat + //{ +float SS_Material_coat//}; + = material.coat; + coat = SS_Material_coat;// Output connection + // Graph input SS_Material/coat_roughness + //{ +float SS_Material_coat_roughness//}; + = material.coat_roughness; + coat_roughness = SS_Material_coat_roughness;// Output connection + // Graph input SS_Material/emission_color + //{ +vec3 SS_Material_emission_color//}; + = material.emission_color; + emission_color = SS_Material_emission_color;// Output connection +} diff --git a/Tests/Assets/TextFiles/TestMaterialX2_Struct.glsl b/Tests/Assets/TextFiles/TestMaterialX2_Struct.glsl new file mode 100644 index 0000000..bf9b823 --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX2_Struct.glsl @@ -0,0 +1,8 @@ +struct Material_498292f537214e2a { + float specular_IOR; + vec3 emission_color; + float transmission; + float coat; + float coat_roughness; + float metalness; +}; diff --git a/Tests/Assets/TextFiles/TestMaterialX3_Defs.glsl b/Tests/Assets/TextFiles/TestMaterialX3_Defs.glsl new file mode 100644 index 0000000..0fabc86 --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX3_Defs.glsl @@ -0,0 +1,22 @@ + +// Definition for implementation IM_texcoord_vector2_genglsl +//{ +//}; + +// Definition for implementation IM_image_color3_genglsl +//{ + // Included from lib/$fileTransformUv + vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset) + { + uv = uv * uv_scale + uv_offset; + return uv; + } + + + void mx_image_color3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) + { + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rgb; + } + +//}; diff --git a/Tests/Assets/TextFiles/TestMaterialX3_Setup.glsl b/Tests/Assets/TextFiles/TestMaterialX3_Setup.glsl new file mode 100644 index 0000000..6d67b7f --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX3_Setup.glsl @@ -0,0 +1,95 @@ +void setupMaterial_a07c7c12bcd815f6( + Material_a07c7c12bcd815f6 material, + sampler2D base_color_image_image_parameter, + out vec3 base_color) +{ + //Temp input variables for base_color_image + vec3 nodeOutTmp_base_color_image_out; //Temp output variable for out + sampler2D nodeTmp_base_color_image_file; //Temp input variable for file + int nodeTmp_base_color_image_layer; //Temp input variable for layer + vec3 nodeTmp_base_color_image_default; //Temp input variable for default + vec2 nodeTmp_base_color_image_texcoord; //Temp input variable for texcoord + int nodeTmp_base_color_image_uaddressmode; //Temp input variable for uaddressmode + int nodeTmp_base_color_image_vaddressmode; //Temp input variable for vaddressmode + int nodeTmp_base_color_image_filtertype; //Temp input variable for filtertype + int nodeTmp_base_color_image_framerange; //Temp input variable for framerange + int nodeTmp_base_color_image_frameoffset; //Temp input variable for frameoffset + int nodeTmp_base_color_image_frameendaction; //Temp input variable for frameendaction + vec2 nodeTmp_base_color_image_uv_scale; //Temp input variable for uv_scale + vec2 nodeTmp_base_color_image_uv_offset; //Temp input variable for uv_offset + // Graph input NG1/base_color_image/file + //{ +sampler2D base_color_image_file1//}; + = base_color_image_image_parameter; + nodeTmp_base_color_image_file = base_color_image_file1;// Output connection + // Graph input NG1/base_color_image/layer + //{ +int base_color_image_layer1 = 0//}; +; + nodeTmp_base_color_image_layer = base_color_image_layer1;// Output connection + // Graph input NG1/base_color_image/default + //{ +vec3 base_color_image_default1 = vec3(0, 0, 0)//}; +; + nodeTmp_base_color_image_default = base_color_image_default1;// Output connection + //Temp input variables for geomprop_UV0 + vec2 nodeOutTmp_geomprop_UV0_out; //Temp output variable for out + int nodeTmp_geomprop_UV0_index; //Temp input variable for index + // Graph input function call texcoord (See definition IM_texcoord_vector2_genglsl) +{ + //{ + vec2 geomprop_UV0_out1 = vertexData.texCoord; +//}; + nodeOutTmp_geomprop_UV0_out = geomprop_UV0_out1;// Output connection + nodeTmp_base_color_image_texcoord = geomprop_UV0_out1;// Output connection +} + // Graph input NG1/base_color_image/uaddressmode + //{ +int base_color_image_uaddressmode1 = 2//}; +; + nodeTmp_base_color_image_uaddressmode = base_color_image_uaddressmode1;// Output connection + // Graph input NG1/base_color_image/vaddressmode + //{ +int base_color_image_vaddressmode1 = 2//}; +; + nodeTmp_base_color_image_vaddressmode = base_color_image_vaddressmode1;// Output connection + // Graph input NG1/base_color_image/filtertype + //{ +int base_color_image_filtertype1 = 1//}; +; + nodeTmp_base_color_image_filtertype = base_color_image_filtertype1;// Output connection + // Graph input NG1/base_color_image/framerange + //{ +int base_color_image_framerange1 = 0//}; +; + nodeTmp_base_color_image_framerange = base_color_image_framerange1;// Output connection + // Graph input NG1/base_color_image/frameoffset + //{ +int base_color_image_frameoffset1 = 0//}; +; + nodeTmp_base_color_image_frameoffset = base_color_image_frameoffset1;// Output connection + // Graph input NG1/base_color_image/frameendaction + //{ +int base_color_image_frameendaction1 = 0//}; +; + nodeTmp_base_color_image_frameendaction = base_color_image_frameendaction1;// Output connection + // Graph input + //{ +vec2 base_color_image_uv_scale1 = vec2(1, 1)//}; +; + nodeTmp_base_color_image_uv_scale = base_color_image_uv_scale1;// Output connection + // Graph input + //{ +vec2 base_color_image_uv_offset1 = vec2(0, 0)//}; +; + nodeTmp_base_color_image_uv_offset = base_color_image_uv_offset1;// Output connection + // Graph input function call base_color (See definition IM_image_color3_genglsl) +{ + //{ + vec3 base_color_image_out = vec3(0.0); + mx_image_color3(nodeTmp_base_color_image_file, nodeTmp_base_color_image_layer, nodeTmp_base_color_image_default, nodeTmp_base_color_image_texcoord, nodeTmp_base_color_image_uaddressmode, nodeTmp_base_color_image_vaddressmode, nodeTmp_base_color_image_filtertype, nodeTmp_base_color_image_framerange, nodeTmp_base_color_image_frameoffset, nodeTmp_base_color_image_frameendaction, nodeTmp_base_color_image_uv_scale, nodeTmp_base_color_image_uv_offset, base_color_image_out); +//}; + nodeOutTmp_base_color_image_out = base_color_image_out;// Output connection + base_color = base_color_image_out;// Output connection +} +} diff --git a/Tests/Assets/TextFiles/TestMaterialX3_Struct.glsl b/Tests/Assets/TextFiles/TestMaterialX3_Struct.glsl new file mode 100644 index 0000000..1f078d0 --- /dev/null +++ b/Tests/Assets/TextFiles/TestMaterialX3_Struct.glsl @@ -0,0 +1,2 @@ +struct Material_a07c7c12bcd815f6 { +}; diff --git a/Tests/Aurora/BaselineImages/Images/TestNormalMapImage_0.DirectX.png b/Tests/Aurora/BaselineImages/Images/TestNormalMapImage_0.DirectX.png index 7cc9ca7..a604cec 100644 --- a/Tests/Aurora/BaselineImages/Images/TestNormalMapImage_0.DirectX.png +++ b/Tests/Aurora/BaselineImages/Images/TestNormalMapImage_0.DirectX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3deb261f1e578c4740868503b2ee5d5a79c7250ad27f39ef2304987d76afe2e -size 16014 +oid sha256:ccf226f0e8122b90ceb51318c3e4e80c08d5f1e85bae61b62295590ac63b5c61 +size 16048 diff --git a/Tests/Aurora/BaselineImages/Light/TestMultipleLights_0.DirectXDeleted.png b/Tests/Aurora/BaselineImages/Light/TestMultipleLights_0.DirectXDeleted.png new file mode 100644 index 0000000..e9ca77a --- /dev/null +++ b/Tests/Aurora/BaselineImages/Light/TestMultipleLights_0.DirectXDeleted.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:693aa69836a18bfac0247e52db691b0a2f61c91269ab6a18373a52a1b9e9bd21 +size 1510 diff --git a/Tests/Aurora/BaselineImages/Materials/TestHdAuroraTextureMaterialX_0.DirectX_HdAuroraMtlX.png b/Tests/Aurora/BaselineImages/Materials/TestHdAuroraTextureMaterialX_0.DirectX_HdAuroraMtlX.png index 786925e..e4863f9 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestHdAuroraTextureMaterialX_0.DirectX_HdAuroraMtlX.png +++ b/Tests/Aurora/BaselineImages/Materials/TestHdAuroraTextureMaterialX_0.DirectX_HdAuroraMtlX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bfd3e1dd678ccdc7674f1194cacf1bc624e2c42336cd28f365e4141527623aa2 -size 8578 +oid sha256:5ee40a8d8b560d711438f993ecc40e14eb3460c862d6a965189fbe4465e1a095 +size 8596 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_Reference.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_Reference.png index f0b62e2..aac03d7 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_Reference.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_Reference.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edae910a5352694c890c1f2975ab4131d84658d68fb332513fd23cfa6c99d2d4 -size 10800 +oid sha256:bca3570092df1878a2b13d810d60eeeb88530c3fc406d83abffb1477c56d8b09 +size 10758 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_StandardSurface.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_StandardSurface.png index db3ad22..f2da97d 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_StandardSurface.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_CoatAffect_StandardSurface.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:471d7654948f6acaa6073017ee8e608a68a2dd35b77c04efa2525924d175ff39 -size 15450 +oid sha256:205954368fea2d789a467ce3f2f630cc52b6bdd7defc9fede2ee1790acc6f7f8 +size 16236 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_Reference.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_Reference.png index f0b62e2..aac03d7 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_Reference.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_Reference.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edae910a5352694c890c1f2975ab4131d84658d68fb332513fd23cfa6c99d2d4 -size 10800 +oid sha256:bca3570092df1878a2b13d810d60eeeb88530c3fc406d83abffb1477c56d8b09 +size 10758 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_StandardSurface.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_StandardSurface.png index 946fbf9..25dd9f3 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_StandardSurface.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Coat_StandardSurface.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36705a53c16751759cca72ebd17807ad52ed30d8ee4a8b72d5cfebc88daa3281 -size 15364 +oid sha256:932b64770951c842412e66e38f5a426519da862043d04f93a55a83e017d21396 +size 15306 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Sheen_Reference.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Sheen_Reference.png index 918ee4b..74313a6 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Sheen_Reference.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Sheen_Reference.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d38f12140c3f9bc44c6f1a8101d64af08969565da459dbf984c0f22d35fde88d -size 12928 +oid sha256:600735915cae0a33f0e3c3da2dfbba458f59a5c0d8b7319166ee0d44d9b90df1 +size 13271 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Subsurface_Reference.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Subsurface_Reference.png index 918ee4b..74313a6 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Subsurface_Reference.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Subsurface_Reference.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d38f12140c3f9bc44c6f1a8101d64af08969565da459dbf984c0f22d35fde88d -size 12928 +oid sha256:600735915cae0a33f0e3c3da2dfbba458f59a5c0d8b7319166ee0d44d9b90df1 +size 13271 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Transmission_Reference.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Transmission_Reference.png index c357812..a70be99 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Transmission_Reference.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialAdvancedMaterialProperties_0.DirectX_Transmission_Reference.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d24113eee0feb41eb41b5baf58fe21c970ca9069944d28d5701931aed89dc9b -size 13368 +oid sha256:d6c1496349081bdc3d361de43f21ccef55be5b6dbea2fdc1c8e993b90ac175df +size 13823 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialEmission_0.DirectXEmissionImage.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialEmission_0.DirectXEmissionImage.png index 6b171d2..d7a7cb0 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestMaterialEmission_0.DirectXEmissionImage.png +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialEmission_0.DirectXEmissionImage.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fd98966e3acbf6e57882237aa89273ae952025e42070631e027d8b88986e8b6 -size 8893 +oid sha256:41587df931752bcdb1c655ee1aef75faeb21069b25880f9d326c3fe6147f8119 +size 8815 diff --git a/Tests/Aurora/BaselineImages/Materials/TestMaterialXImageNode_0.DirectX.png b/Tests/Aurora/BaselineImages/Materials/TestMaterialXImageNode_0.DirectX.png new file mode 100644 index 0000000..1405b94 --- /dev/null +++ b/Tests/Aurora/BaselineImages/Materials/TestMaterialXImageNode_0.DirectX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4209a260b97e7c7bd08bc51aaf67b55d279a0258cb900f33876a26e5211dcbc +size 9915 diff --git a/Tests/Aurora/BaselineImages/Materials/TestNormalMapMaterialX_0.DirectX.png b/Tests/Aurora/BaselineImages/Materials/TestNormalMapMaterialX_0.DirectX.png index ce50e6c..8254374 100644 --- a/Tests/Aurora/BaselineImages/Materials/TestNormalMapMaterialX_0.DirectX.png +++ b/Tests/Aurora/BaselineImages/Materials/TestNormalMapMaterialX_0.DirectX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b39e94a23fdd9606cc26a187c76a28d553c2a76ec2d39609e1120f0ab21c095 -size 25891 +oid sha256:b1676e2c9b3fe22b8ae9c2ceec27f2e3d11eb454a85878ac37579467c7cfad01 +size 25747 diff --git a/Tests/Aurora/BaselineImages/TestDebugNormals_0.DirectX.png b/Tests/Aurora/BaselineImages/TestDebugNormals_0.DirectX.png index 075657e..c371da4 100644 --- a/Tests/Aurora/BaselineImages/TestDebugNormals_0.DirectX.png +++ b/Tests/Aurora/BaselineImages/TestDebugNormals_0.DirectX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d67cd680f41adc7e24613a7b747b24cb2b118d9122fb19bb976ba5202633302 -size 28728 +oid sha256:733a8d5b6503724aa1804002dadad7d7689c3d89a01d1173765376de6f24c3b5 +size 28631 diff --git a/Tests/Aurora/BaselineImages/TestMaterialMaterialXLayerTransforms_0.DirectX.png b/Tests/Aurora/BaselineImages/TestMaterialMaterialXLayerTransforms_0.DirectX.png new file mode 100644 index 0000000..4b756ce --- /dev/null +++ b/Tests/Aurora/BaselineImages/TestMaterialMaterialXLayerTransforms_0.DirectX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a131672a9783e879fd9113317f5e793b612e2aa232b98eb8536daf766cef4e21 +size 15500 diff --git a/Tests/Aurora/BaselineImages/TestRendererGroundPlane_0.DirectX.png b/Tests/Aurora/BaselineImages/TestRendererGroundPlane_0.DirectX.png index d8848e2..a82f5e1 100644 --- a/Tests/Aurora/BaselineImages/TestRendererGroundPlane_0.DirectX.png +++ b/Tests/Aurora/BaselineImages/TestRendererGroundPlane_0.DirectX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2022924057623f5ba86b2dac9df94ecf4503837d1c7aef63d12177e539779f02 -size 24103 +oid sha256:62313f10e6e17f7c75a53e7a187d1a318fa3654cc713d1639771bc692f451424 +size 21961 diff --git a/Tests/Aurora/BaselineImages/TestRendererMultipleMaterialLayers_0.DirectX.png b/Tests/Aurora/BaselineImages/TestRendererMultipleMaterialLayers_0.DirectX.png index d774ad7..6565789 100644 --- a/Tests/Aurora/BaselineImages/TestRendererMultipleMaterialLayers_0.DirectX.png +++ b/Tests/Aurora/BaselineImages/TestRendererMultipleMaterialLayers_0.DirectX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:175340de7c639e349d0b118a99cb79e896a52f0699fb6074e0ef8c84e731df80 -size 15519 +oid sha256:40575109c8c36a8c3c846450a7a98b4c6c252314dbe7183a678932e38960e0fb +size 15546 diff --git a/Tests/Aurora/Tests/TestImage.cpp b/Tests/Aurora/Tests/TestImage.cpp index 4cbdeb9..0a53043 100644 --- a/Tests/Aurora/Tests/TestImage.cpp +++ b/Tests/Aurora/Tests/TestImage.cpp @@ -53,25 +53,9 @@ TEST_P(ImageTest, TestImageDefault) // Load pixels for test image file. const std::string txtName = dataPath() + "/Textures/Hieroglyphs.jpg"; - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image. - const Path kImagePath = "DefaultImage"; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); - - const std::string txtName1 = dataPath() + "/Textures/Mandrill.png"; - TestHelpers::ImageData imageData1; - loadImage(txtName, &imageData1); - - // Create the image. - const Path kImagePath1 = "OtherImage"; - pScene->setImageDescriptor(kImagePath1, imageData1.descriptor); - // Create a material. const Path kMaterialPath = "DefaultMaterial"; - pScene->setMaterialProperties(kMaterialPath, { { "base_color_image", kImagePath } }); + pScene->setMaterialProperties(kMaterialPath, { { "base_color_image", loadImage(txtName) } }); // Create plane instance. Path geomPath = createPlaneGeometry(*pScene); @@ -86,6 +70,7 @@ TEST_P(ImageTest, TestImageDefault) } // Basic image test. +// TODO: Re-enable once samplers working. TEST_P(ImageTest, TestImageSamplers) { // Create the default scene (also creates renderer) @@ -98,14 +83,7 @@ TEST_P(ImageTest, TestImageSamplers) // Load pixels for test image file. const std::string txtName = dataPath() + "/Textures/Mandrill.png"; - - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image. - const Path kImagePath = "ClampImage"; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); + Path imagePath = loadImage(txtName); // Create plane instance. Path geomPath = createPlaneGeometry(*pScene, vec2(2, 2), vec2(-0.5, -0.5)); @@ -122,7 +100,7 @@ TEST_P(ImageTest, TestImageSamplers) // Create a material. const Path kClampMaterialPath = "ClampSamplerMaterial"; pScene->setMaterialProperties(kClampMaterialPath, - { { "base_color_image", kImagePath }, + { { "base_color_image", imagePath }, { "base_color_image_sampler", kClampSamplerPath, @@ -148,7 +126,7 @@ TEST_P(ImageTest, TestImageSamplers) const Path kMirrorMaterialPath = "MirrorSamplerMaterial"; pScene->setMaterialProperties(kMirrorMaterialPath, - { { "base_color_image", kImagePath }, + { { "base_color_image", imagePath }, { "base_color_image_sampler", kMirrorSamplerPath, @@ -172,20 +150,12 @@ TEST_P(ImageTest, TestImageOpacity) if (!pRenderer) return; - // Load pixels for test image file. + // Path to test image. const std::string txtName = dataPath() + "/Textures/Triangle.png"; - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image. - const Path kImagePath = "OpacityImage"; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); - // Create a material. const Path kMaterialPath = "OpacityMaterial"; - pScene->setMaterialProperties(kMaterialPath, { { "opacity_image", kImagePath } }); + pScene->setMaterialProperties(kMaterialPath, { { "opacity_image", loadImage(txtName) } }); // Create plane instance. Path geomPath = createPlaneGeometry(*pScene); @@ -212,32 +182,23 @@ TEST_P(ImageTest, TestGammaImage) // Load pixels for test image file. const std::string txtName = dataPath() + "/Textures/Mandrill.png"; - - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image.with default gamma settings (linearize from sRGB.) - const Path kImagePath0 = "GammaImage0"; - pScene->setImageDescriptor(kImagePath0, imageData.descriptor); + Path linearImagePath = loadImage(txtName, true); // Create the same image.with linearize set to false. - const Path kImagePath1 = "GammaImage1"; - imageData.descriptor.linearize = false; - pScene->setImageDescriptor(kImagePath1, imageData.descriptor); + Path gammaImagePath = loadImage(txtName, false); // Create plane instance. Path geomPath = createPlaneGeometry(*pScene); // Create matrix and material for first instance (which is linearized). const Path kMaterialPath0 = "GammaMaterial0"; - pScene->setMaterialProperties(kMaterialPath0, { { "base_color_image", kImagePath0 } }); + pScene->setMaterialProperties(kMaterialPath0, { { "base_color_image", linearImagePath } }); mat4 mtx0 = translate(glm::vec3(-1.0, 0, -0.4)); // Create matrix and material for second instance (which is not linearized). const Path kMaterialPath1 = "GammaMaterial1"; - pScene->setMaterialProperties(kMaterialPath1, { { "base_color_image", kImagePath1 } }); + pScene->setMaterialProperties(kMaterialPath1, { { "base_color_image", gammaImagePath } }); mat4 mtx1 = translate(glm::vec3(+1.0, 0, -0.4)); // Set the test image @@ -271,16 +232,8 @@ TEST_P(ImageTest, TestNormalMapImage) defaultDistantLight()->values().setFloat3( Aurora::Names::LightProperties::kColor, value_ptr(glm::vec3(1, 1, 1))); - // Load pixels for test image file. + // Path to test normal map const std::string txtName = dataPath() + "/Textures/fishscale_normal.png"; - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image. - const Path kImagePath = "NormalImage"; - imageData.descriptor.linearize = false; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); // Create geometry. Path planePath = createPlaneGeometry(*pScene); @@ -288,7 +241,7 @@ TEST_P(ImageTest, TestNormalMapImage) // Create matrix and material for first instance (which is linearized). const Path kMaterialPath = "NormalMaterial"; - pScene->setMaterialProperties(kMaterialPath, { { "normal_image", kImagePath } }); + pScene->setMaterialProperties(kMaterialPath, { { "normal_image", loadImage(txtName, false) } }); mat4 scaleMtx = scale(vec3(2, 2, 2)); // Create geometry with the material. @@ -316,11 +269,8 @@ TEST_P(ImageTest, TestCreateImageAfterSceneCreation) const Path kImagePath = "DefaultImage"; ImageDescriptor imageDesc; - imageDesc.width = 2048; - imageDesc.height = 1024; imageDesc.isEnvironment = false; imageDesc.linearize = true; - imageDesc.format = ImageFormat::Integer_RGBA; // Load pixels for test image file. std::vector buffer(1024 * 2048 * 4); @@ -328,12 +278,13 @@ TEST_P(ImageTest, TestCreateImageAfterSceneCreation) bool loaded = false; // Set up the pixel data callback - imageDesc.getPixelData = [&buffer, &loaded](PixelData& dataOut, glm::ivec2, glm::ivec2) { + imageDesc.getData = [&buffer, &loaded](ImageData& dataOut, AllocateBufferFunction alloc) { // Get address and size from buffer (assumes will be called from scope of test, so buffer // still valid) - dataOut.address = buffer.data(); - dataOut.size = buffer.size(); - loaded = true; + dataOut.pPixelBuffer = buffer.data(); + dataOut.bufferSize = buffer.size(); + dataOut.dimensions = { 2048, 1024 }; + loaded = true; return true; }; diff --git a/Tests/Aurora/Tests/TestLight.cpp b/Tests/Aurora/Tests/TestLight.cpp index 3f96fcc..504a8f4 100644 --- a/Tests/Aurora/Tests/TestLight.cpp +++ b/Tests/Aurora/Tests/TestLight.cpp @@ -45,75 +45,75 @@ class LightTest : public TestHelpers::FixtureBase // If lightIntensity non-zero bright region added around lightDirec vector. void createTestEnv(IScenePtr scene, const Path& path, int height, const array& colors, glm::vec3 lightDirection, glm::vec3 lightColor, float lightAngle, float lightIntensity, - std::vector* pBufferOut, bool isRGBA) + bool isRGBA) { - // Width is always 2x height for lat-long. - int width = height * 2; - - // Allocate data for image. - int numComp = isRGBA ? 4 : 3; - pBufferOut->resize(width * height * sizeof(float) * numComp); - - // Get float pixels for image. - float* pPixels = (float*)pBufferOut->data(); + // Create and return Aurora Environment Map Path. + ImageDescriptor imageDesc; + imageDesc.isEnvironment = true; + imageDesc.linearize = true; + imageDesc.getData = [lightAngle, lightIntensity, lightColor, lightDirection, colors, height, + isRGBA](ImageData& dataOut, AllocateBufferFunction alloc) { + // Width is always 2x height for lat-long. + int width = height * 2; - float lightAngleRad = glm::radians(lightAngle); + // Allocate data for image. + int numComp = isRGBA ? 4 : 3; + size_t numBytes = (width * height * sizeof(float) * numComp); + float* pPixelsStart = (float*)alloc(numBytes); + float* pPixels = pPixelsStart; - // Iterate through rows of image. - for (int row = 0; row < height; row++) - { - // Compute Phi angle in radians from row. - float v = (float)row / (float)(height - 1); - float phi = v * (float)M_PI; + float lightAngleRad = glm::radians(lightAngle); - // Iterate through columns. - for (int col = 0; col < width; col++) + // Iterate through rows of image. + for (int row = 0; row < height; row++) { - // Compute Theta angle in radians from column. - float u = (float)col / (float)(width - 1); - float theta = u * 2.0f * (float)M_PI; + // Compute Phi angle in radians from row. + float v = (float)row / (float)(height - 1); + float phi = v * (float)M_PI; - // Compute direction from spherical coordinate. - glm::vec3 dir(sinf(theta) * sinf(phi), cosf(phi), cosf(theta) * sinf(phi)); + // Iterate through columns. + for (int col = 0; col < width; col++) + { + // Compute Theta angle in radians from column. + float u = (float)col / (float)(width - 1); + float theta = u * 2.0f * (float)M_PI; - // Compute color for each axis in direction. - glm::vec3 color; - color += dir.x > 0.0 ? colors[0] * dir.x : colors[1] * -1.0f * dir.x; - color += dir.y > 0.0 ? colors[2] * dir.y : colors[3] * -1.0f * dir.y; - color += dir.z > 0.0 ? colors[4] * dir.z : colors[5] * -1.0f * dir.z; + // Compute direction from spherical coordinate. + glm::vec3 dir(sinf(theta) * sinf(phi), cosf(phi), cosf(theta) * sinf(phi)); - if (lightIntensity > 0.0f) - { - float currLightAngleRad = acosf(glm::dot(dir, normalize(lightDirection))); - if (currLightAngleRad < lightAngleRad) + // Compute color for each axis in direction. + glm::vec3 color; + color += dir.x > 0.0 ? colors[0] * dir.x : colors[1] * -1.0f * dir.x; + color += dir.y > 0.0 ? colors[2] * dir.y : colors[3] * -1.0f * dir.y; + color += dir.z > 0.0 ? colors[4] * dir.z : colors[5] * -1.0f * dir.z; + + if (lightIntensity > 0.0f) { - color = lightColor * lightIntensity; + float currLightAngleRad = acosf(glm::dot(dir, normalize(lightDirection))); + if (currLightAngleRad < lightAngleRad) + { + color = lightColor * lightIntensity; + } } - } - // Put into pixels array. - *(pPixels++) = color.x; - *(pPixels++) = color.y; - *(pPixels++) = color.z; + // Put into pixels array. + *(pPixels++) = color.x; + *(pPixels++) = color.y; + *(pPixels++) = color.z; - // Add alpha component if needed. - if (isRGBA) - *(pPixels++) = 1.0f; + // Add alpha component if needed. + if (isRGBA) + *(pPixels++) = 1.0f; + } } - } + AU_ASSERT(pPixelsStart + (width * height * numComp) == pPixels, "Buffer overrun"); - // Create and return Aurora Environment Map Path. - ImageDescriptor imageDesc; - imageDesc.width = width; - imageDesc.height = height; - imageDesc.format = isRGBA ? ImageFormat::Float_RGBA : ImageFormat::Float_RGB; - imageDesc.isEnvironment = true; - imageDesc.linearize = true; - imageDesc.getPixelData = [pBufferOut](PixelData& dataOut, glm::ivec2, glm::ivec2) { - // Get addres and size from buffer (assumes will be called from scope of test, so buffer + // Get address and size from buffer (assumes will be called from scope of test, so buffer // still valid) - dataOut.address = pBufferOut->data(); - dataOut.size = pBufferOut->size(); + dataOut.pPixelBuffer = pPixelsStart; + dataOut.bufferSize = numBytes; + dataOut.dimensions = { width, height }; + dataOut.format = isRGBA ? ImageFormat::Float_RGBA : ImageFormat::Float_RGB; return true; }; @@ -138,7 +138,6 @@ TEST_P(LightTest, TestLightEnvTexture) Aurora::Names::LightProperties::kColor, value_ptr(glm::vec3(0, 0, 0))); // Create procedural image data. - std::vector buffer; array colors = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), @@ -149,7 +148,7 @@ TEST_P(LightTest, TestLightEnvTexture) }; const Path kBackgroundEnvironmentImagePath = "BackgroundEnvironmentImage"; createTestEnv(pScene, kBackgroundEnvironmentImagePath, 512, colors, glm::vec3(), glm::vec3(), 0, - 0, &buffer, false); + 0, false); // Create environment and set background and light image. const Path kBackgroundEnvironmentPath = "BackgroundEnvironment"; @@ -194,7 +193,6 @@ TEST_P(LightTest, TestChangeLightEnvTexture) Aurora::Names::LightProperties::kColor, value_ptr(glm::vec3(0, 0, 0))); // Create procedural image data. - std::vector buffer; array colors = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), @@ -205,7 +203,7 @@ TEST_P(LightTest, TestChangeLightEnvTexture) }; const Path kBackgroundEnvironmentImagePath = "BackgroundEnvironmentImage"; createTestEnv(pScene, kBackgroundEnvironmentImagePath, 512, colors, glm::vec3(), glm::vec3(), 0, - 0, &buffer, false); + 0, false); // Create environment and set background and light image. const Path kBackgroundEnvironmentPath = "BackgroundEnvironment"; @@ -233,7 +231,6 @@ TEST_P(LightTest, TestChangeLightEnvTexture) ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName() + "0", "Light"); // Create procedural image data. - std::vector buffer1; array colors1 = { glm::vec3(1.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.1f, 0.0f), @@ -244,7 +241,7 @@ TEST_P(LightTest, TestChangeLightEnvTexture) }; const Path kSecondBackgroundEnvironmentImagePath = "SecondBackgroundEnvironmentImage"; createTestEnv(pScene, kSecondBackgroundEnvironmentImagePath, 512, colors1, glm::vec3(), - glm::vec3(), 0, 0, &buffer1, false); + glm::vec3(), 0, 0, false); pScene->setEnvironmentProperties(kBackgroundEnvironmentPath, { { Names::EnvironmentProperties::kLightImage, kSecondBackgroundEnvironmentImagePath }, @@ -276,7 +273,6 @@ TEST_P(LightTest, TestLightEnvTextureMIS) Aurora::Names::LightProperties::kColor, value_ptr(glm::vec3(0, 0, 0))); // Create procedural image data with small bright region. - std::vector buffer; array colors = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), @@ -287,7 +283,7 @@ TEST_P(LightTest, TestLightEnvTextureMIS) }; const Path kBackgroundEnvironmentImagePath = "BackgroundEnvironmentImage"; createTestEnv(pScene, kBackgroundEnvironmentImagePath, 1024, colors, glm::vec3(0, 0.2f, 1), - glm::vec3(0.9f, 0.8f, -0.8f), 0.5f, 5000.0f, &buffer, false); + glm::vec3(0.9f, 0.8f, -0.8f), 0.5f, 5000.0f, false); // Create environment and set background and light image. const Path kBackgroundEnvironmentPath = "BackgroundEnvironment"; @@ -405,7 +401,7 @@ TEST_P(LightTest, TestMultipleLights) }; const Path kBackgroundEnvironmentImagePath = "BackgroundEnvironmentImage"; createTestEnv(pScene, kBackgroundEnvironmentImagePath, 1024, colors, glm::vec3(0, 0.2f, 1), - glm::vec3(0.9f, 0.8f, -0.8f), 0.0001f, 0.0f, &buffer, false); + glm::vec3(0.9f, 0.8f, -0.8f), 0.0001f, 0.0f, false); // Create environment and set background and light image. const Path kBackgroundEnvironmentPath = "BackgroundEnvironment"; @@ -450,6 +446,10 @@ TEST_P(LightTest, TestMultipleLights) // Render the scene and check baseline image. ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName(), "Light"); + + // Delete one of the lights (the fifth light will now be used.) + pFourthLight.reset(); + ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName() + "Deleted", "Light"); } INSTANTIATE_TEST_SUITE_P(LightTests, LightTest, TEST_SUITE_RENDERER_TYPES()); diff --git a/Tests/Aurora/Tests/TestMaterial.cpp b/Tests/Aurora/Tests/TestMaterial.cpp index cfc353e..90bfbf3 100644 --- a/Tests/Aurora/Tests/TestMaterial.cpp +++ b/Tests/Aurora/Tests/TestMaterial.cpp @@ -116,6 +116,8 @@ TEST_P(MaterialTest, TestMaterialDefault) // Create a material IScenePtr pScene = pRenderer->createScene(); + pRenderer->setScene(pScene); + Path testMaterial("testMaterial"); pScene->setMaterialType(testMaterial); pScene->addPermanent(testMaterial); @@ -585,13 +587,10 @@ TEST_P(MaterialTest, TestMaterialEmission) // Load a test image from disk as an Aurora image. const std::string imageFilePath = dataPath() + "/Textures/Mr._Smiley_Face.png"; - TestHelpers::ImageData imageData; - loadImage(imageFilePath, &imageData); - const Path kImagePath = "EmissionColorImage"; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); + const Path imagePath = loadImage(imageFilePath); // Set the image as the emission color image on the material. - pScene->setMaterialProperties(material, { { "emission_color_image", kImagePath } }); + pScene->setMaterialProperties(material, { { "emission_color_image", imagePath } }); // Render and compare against the baseline image. ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName() + "EmissionImage", "Materials"); @@ -1273,6 +1272,46 @@ TEST_P(MaterialTest, TestMaterialXBMP) ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName(), "Materials"); } +TEST_P(MaterialTest, TestMaterialXImageNode) +{ + + // Create the default scene (also creates renderer) + auto pScene = createDefaultScene(); + auto pRenderer = defaultRenderer(); + setDefaultRendererPathTracingIterations(256); + + // If pRenderer is null this renderer type not supported, skip rest of the test. + if (!pRenderer) + return; + + // No MaterialX on HGI yet. + if (!isDirectX()) + return; + + // Create teapot geom. + Path geometry = createTeapotGeometry(*pScene); + + // Read the materialX document from file. + string materialXFullPath = dataPath() + "/Materials/TestImageNode.mtlx"; + + // Load and process the MaterialX document and ensure it loaded correctly. + string processedMtlXString = loadAndProcessMaterialXFile(materialXFullPath); + EXPECT_FALSE(processedMtlXString.empty()); + + // Create material with current flip-Y value. + Path material("Texture"); + pScene->setMaterialType(material, Names::MaterialTypes::kMaterialX, processedMtlXString); + + // Add to scene. + Path instance("Mtl"); + Properties instProps; + instProps[Names::InstanceProperties::kMaterial] = material; + EXPECT_TRUE(pScene->addInstance(instance, geometry, instProps)); + + // Render the scene and check baseline image. + ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName(), "Materials"); +} + TEST_P(MaterialTest, TestMaterialTransparency) { // Create the default scene (also creates renderer) @@ -1326,6 +1365,7 @@ TEST_P(MaterialTest, TestMaterialTransparency) ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName() + "Opacity", "Materials"); } +// TODO: Re-enable test when shadow anyhit shaders are working. TEST_P(MaterialTest, TestMaterialShadowTransparency) { // Create the default scene (also creates renderer) @@ -1346,14 +1386,7 @@ TEST_P(MaterialTest, TestMaterialShadowTransparency) // Load pixels for test image file. const std::string txtName = dataPath() + "/Textures/Triangle.png"; - - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image. - const Path kImagePath = "OpacityImage"; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); + Path imagePath = loadImage(txtName); // Constant colors. vec3 color0(0.5f, 1.0f, 0.3f); @@ -1388,7 +1421,7 @@ TEST_P(MaterialTest, TestMaterialShadowTransparency) ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName() + "Transmission", "Materials"); // Render baseline image with transmission and thin_walled flag set. - pScene->setMaterialProperties(transpMtl, { { "opacity_image", kImagePath } }); + pScene->setMaterialProperties(transpMtl, { { "opacity_image", imagePath } }); ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName() + "OpacityImage", "Materials"); // Render baseline image with opacity, no transmission and thin_walled flag not set. @@ -1399,6 +1432,7 @@ TEST_P(MaterialTest, TestMaterialShadowTransparency) } // Disabled as this testcase fails with error in MaterialGenerator::generate +// TODO: Re-enable once samplers working. TEST_P(MaterialTest, TestMtlXSamplers) { // This mtlx file requires support ADSK materialX support. @@ -1567,6 +1601,158 @@ TEST_P(MaterialTest, TestMaterialMaterialXLayers) ASSERT_BASELINE_IMAGE_PASSES(currentTestName() + "_Removed"); } +// MaterialX as layered materials +// Disabled as this testcase fails with error in MaterialGenerator::generate +TEST_P(MaterialTest, TestMaterialMaterialXLayerTransforms) +{ + // This mtlx file requires support ADSK MaterialX support. + if (!adskMaterialXSupport()) + return; + + // No MaterialX on HGI yet. + if (!isDirectX()) + return; + + auto pScene = createDefaultScene(); + auto pRenderer = defaultRenderer(); + + // If pRenderer is null this renderer type not supported, skip rest of the test. + if (!pRenderer) + return; + + setupAssetPaths(); + + Properties options = { { "isFlipImageYEnabled", false } }; + pRenderer->setOptions(options); + + std::string proteinXFullPath = dataPath() + "/Materials/FishScale.mtlx"; + string processedBaseMaterialXString = loadAndProcessMaterialXFile(proteinXFullPath); + ASSERT_FALSE(processedBaseMaterialXString.empty()); + + string materialLayer0XFullPath = dataPath() + "/Materials/Decals/test_decal_mask.mtlx"; + string processedLayer0MaterialXString = loadAndProcessMaterialXFile(materialLayer0XFullPath); + ASSERT_FALSE(processedLayer0MaterialXString.empty()); + + vector materialLayers = { "LayerMaterial0", "LayerMaterial1", "LayerMaterial2", + "LayerMaterial3" }; + pScene->setMaterialType( + materialLayers[0], Names::MaterialTypes::kMaterialX, processedLayer0MaterialXString); + pScene->setMaterialProperties(materialLayers[0], + { + { "basecolor_bitmap/rotation_angle", 45.0f }, + { "basecolor_bitmap/uv_offset", glm::vec2(0, 0) }, + { "basecolor_bitmap/uv_scale", glm::vec2(2, 0.5) }, + { "opacity_bitmap/rotation_angle", 45.0f }, + { "opacity_bitmap/uv_offset", glm::vec2(0, 0) }, + { "opacity_bitmap/uv_scale", glm::vec2(2, 0.5) }, + }); + + pScene->setMaterialType( + materialLayers[1], Names::MaterialTypes::kMaterialX, processedLayer0MaterialXString); + pScene->setMaterialProperties(materialLayers[1], + { + { "basecolor_bitmap/rotation_angle", -15.0f }, + { "basecolor_bitmap/uv_offset", glm::vec2(3.0, 0) }, + { "basecolor_bitmap/uv_scale", glm::vec2(4, 4) }, + { "opacity_bitmap/rotation_angle", -15.0f }, + { "opacity_bitmap/uv_offset", glm::vec2(3.0, 0) }, + { "opacity_bitmap/uv_scale", glm::vec2(4, 4) }, + }); + + pScene->setMaterialType( + materialLayers[2], Names::MaterialTypes::kMaterialX, processedLayer0MaterialXString); + pScene->setMaterialProperties(materialLayers[2], + { + { "basecolor_bitmap/rotation_angle", 5.0f }, + { "basecolor_bitmap/uv_offset", glm::vec2(3.0, 2.0) }, + { "basecolor_bitmap/uv_scale", glm::vec2(4, 4) }, + { "opacity_bitmap/rotation_angle", 5.0f }, + { "opacity_bitmap/uv_offset", glm::vec2(3.0, 2.0) }, + { "opacity_bitmap/uv_scale", glm::vec2(4, 4) }, + }); + + pScene->setMaterialType( + materialLayers[3], Names::MaterialTypes::kMaterialX, processedLayer0MaterialXString); + pScene->setMaterialProperties(materialLayers[3], + { + { "basecolor_bitmap/rotation_angle", -60.0f }, + { "basecolor_bitmap/uv_offset", glm::vec2(1.5, 0.0) }, + { "basecolor_bitmap/uv_scale", glm::vec2(0.5, 4) }, + { "opacity_bitmap/rotation_angle", -60.0f }, + { "opacity_bitmap/uv_offset", glm::vec2(1.5, 0.0) }, + { "opacity_bitmap/uv_scale", glm::vec2(0.5, 4) }, + }); + + Path baseMaterial("BaseMaterial"); + pScene->setMaterialType( + baseMaterial, Names::MaterialTypes::kMaterialX, processedBaseMaterialXString); + + int numGeomLayers = 1; + vector geometryLayers; + + float scales[] = { 1, 2, 1.5f, 0.4f }; + glm::vec2 origins[] = { glm::vec2(0.75f, 1.0f), glm::vec2(-0.25f, 1.2f), glm::vec2(0.25f, 0.6f), + glm::vec2(-0.5f, 1.4f) }; + + vector> xformedUVs(numGeomLayers); + + for (int layer = 0; layer < numGeomLayers; layer++) + { + auto& uvs = xformedUVs[layer]; + uvs.resize(TestHelpers::TeapotModel::verticesCount()); + + for (uint32_t i = 0; i < uvs.size(); i++) + { + uvs[i].x = *(TestHelpers::TeapotModel::vertices() + (i * 3 + 0)); + uvs[i].y = *(TestHelpers::TeapotModel::vertices() + (i * 3 + 1)); + uvs[i] = origins[layer] - uvs[i]; + uvs[i] *= scales[layer]; + } + + const Path kDecalUVGeomPath = "DecalUVGeomPath" + to_string(layer); + GeometryDescriptor geomDesc; + geomDesc.type = PrimitiveType::Triangles; + geomDesc.vertexDesc.attributes[Names::VertexAttributes::kTexCoord0] = + AttributeFormat::Float2; + geomDesc.vertexDesc.count = TestHelpers::TeapotModel::verticesCount(); + geomDesc.getAttributeData = [&xformedUVs, layer](AttributeDataMap& buffers, + size_t firstVertex, size_t vertexCount, size_t firstIndex, + size_t indexCount) { + AU_ASSERT(firstVertex == 0, "Partial update not supported"); + AU_ASSERT(vertexCount == TestHelpers::TeapotModel::verticesCount(), + "Partial update not supported"); + AU_ASSERT(firstIndex == 0, "Partial update not supported"); + AU_ASSERT(indexCount == 0, "Partial update not supported"); + buffers[Names::VertexAttributes::kTexCoord0].address = xformedUVs[layer].data(); + buffers[Names::VertexAttributes::kTexCoord0].size = + TestHelpers::TeapotModel::verticesCount() * sizeof(vec2); + buffers[Names::VertexAttributes::kTexCoord0].stride = sizeof(vec2); + + return true; + }; + pScene->setGeometryDescriptor(kDecalUVGeomPath, geomDesc); + } + + for (int layer = 0; layer < materialLayers.size(); layer++) + { + const Path kDecalUVGeomPath = "DecalUVGeomPath0"; + geometryLayers.push_back(kDecalUVGeomPath); + } + + // Create a teapot instance with a default material. + Path geometry = createTeapotGeometry(*pScene); + Path planeGeometry = createPlaneGeometry(*pScene); + EXPECT_TRUE(pScene->addInstance("TeapotInstance", geometry, + { { Names::InstanceProperties::kMaterial, baseMaterial }, + { Names::InstanceProperties::kMaterialLayers, materialLayers }, + { Names::InstanceProperties::kGeometryLayers, geometryLayers }, + { Names::InstanceProperties::kTransform, glm::translate(glm::vec3(0, -0.5, -0.8)) } })); + EXPECT_TRUE(pScene->addInstance( + "Plane", planeGeometry, { { Names::InstanceProperties::kMaterial, baseMaterial } })); + + ASSERT_BASELINE_IMAGE_PASSES(currentTestName()); +} + // Normal map image test. TEST_P(MaterialTest, TestNormalMapMaterialX) { @@ -1613,10 +1799,10 @@ TEST_P(MaterialTest, TestNormalMapMaterialX) ASSERT_BASELINE_IMAGE_PASSES_IN_FOLDER(currentTestName(), "Materials"); } -// Test object space normal in materialX. +// Test object space normal in MaterialX. TEST_P(MaterialTest, TestObjectSpaceMaterialX) { - // This mtlx file requires support ADSK materialX support. + // This mtlx file requires support ADSK MaterialX support. if (!adskMaterialXSupport()) return; diff --git a/Tests/Aurora/Tests/TestPaths.cpp b/Tests/Aurora/Tests/TestPaths.cpp index 32f5247..f600766 100644 --- a/Tests/Aurora/Tests/TestPaths.cpp +++ b/Tests/Aurora/Tests/TestPaths.cpp @@ -51,27 +51,29 @@ class PathTest : public TestHelpers::FixtureBase PathTest() {} ~PathTest() {} - GetPixelDataFunction createImageFunction( + GetImageDataFunction createImageFunction( int w, int h, int s, vec3 color0, vec3 color1, string name) { _imageFunctionInvocations[name] = 0; - return [this, w, h, s, color0, color1, name](PixelData& dataOut, glm::ivec2, glm::ivec2) { - _pPixelData = make_unique>(); - _pPixelData->resize(w * h * 4); - int idx = 0; + return [this, w, h, s, color0, color1, name]( + ImageData& dataOut, AllocateBufferFunction alloc) { + uint8_t* pPixelData = static_cast(alloc(w * h * 4)); + int idx = 0; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { - vec3 color = ((i / s) % 2 == (j / s) % 2) ? color0 : color1; - _pPixelData->at(idx++) = static_cast(color.x * 255.0f); - _pPixelData->at(idx++) = static_cast(color.y * 255.0f); - _pPixelData->at(idx++) = static_cast(color.z * 255.0f); - _pPixelData->at(idx++) = 255; + vec3 color = ((i / s) % 2 == (j / s) % 2) ? color0 : color1; + pPixelData[idx++] = static_cast(color.x * 255.0f); + pPixelData[idx++] = static_cast(color.y * 255.0f); + pPixelData[idx++] = static_cast(color.z * 255.0f); + pPixelData[idx++] = 255; } } - dataOut.address = _pPixelData->data(); - dataOut.size = _pPixelData->size(); + dataOut.pPixelBuffer = pPixelData; + dataOut.bufferSize = w * h * 4; + dataOut.dimensions = { w, h }; + dataOut.format = Aurora::ImageFormat::Integer_RGBA; _imageFunctionInvocations[name]++; return true; }; @@ -79,7 +81,6 @@ class PathTest : public TestHelpers::FixtureBase map _imageFunctionInvocations; unique_ptr _pGeometryDataP; - unique_ptr> _pPixelData; }; // Basic path test. @@ -93,49 +94,29 @@ TEST_P(PathTest, TestPathDefault) if (!pRenderer) return; - auto pixelUpdateCompleteFunction = [this](const PixelData&, glm::ivec2, glm::ivec2) { - ASSERT_TRUE(_pPixelData); - _pPixelData.reset(); - }; - // Create the first image. const Path kImagePath0 = nextPath("Image"); ImageDescriptor imageDesc0; - imageDesc0.width = 4; - imageDesc0.height = 4; imageDesc0.linearize = true; - imageDesc0.format = ImageFormat::Integer_RGBA; - imageDesc0.getPixelData = - createImageFunction(4, 4, 2, vec3(1, 0, 0), vec3(0, 1, 0), kImagePath0); - imageDesc0.pixelUpdateComplete = pixelUpdateCompleteFunction; + imageDesc0.getData = createImageFunction(4, 4, 2, vec3(1, 0, 0), vec3(0, 1, 0), kImagePath0); pScene->setImageDescriptor(kImagePath0, imageDesc0); // Create the second simage. const Path kImagePath1 = nextPath("Image"); ImageDescriptor imageDesc1; - imageDesc1.width = 8; - imageDesc1.height = 8; imageDesc1.linearize = true; - imageDesc1.format = ImageFormat::Integer_RGBA; - imageDesc1.getPixelData = + imageDesc1.getData = createImageFunction(8, 8, 2, vec3(0, 1, 0.8), vec3(0.2, 0.0, 0.75), kImagePath1); - imageDesc1.pixelUpdateComplete = pixelUpdateCompleteFunction; pScene->setImageDescriptor(kImagePath1, imageDesc1); // Create the third image. const Path kImagePath2 = nextPath("Image"); ImageDescriptor imageDesc2; - imageDesc2.width = 16; - imageDesc2.height = 16; imageDesc2.linearize = true; - imageDesc2.format = ImageFormat::Integer_RGBA; - imageDesc2.getPixelData = - createImageFunction(16, 16, 4, vec3(0, 0, 0), vec3(1, 1, 1), kImagePath2); - imageDesc2.pixelUpdateComplete = pixelUpdateCompleteFunction; + imageDesc2.getData = createImageFunction(16, 16, 4, vec3(0, 0, 0), vec3(1, 1, 1), kImagePath2); pScene->setImageDescriptor(kImagePath2, imageDesc2); // Pixel data yet, only created when activated. - ASSERT_TRUE(!_pPixelData); // No renderer data has been created yet, nothing has been activated. ASSERT_EQ(_imageFunctionInvocations[kImagePath0], 0); @@ -168,7 +149,7 @@ TEST_P(PathTest, TestPathDefault) ASSERT_EQ(instPaths.size(), 3); - // Now the image getPixelData function should have been invoced once. + // Now the image getData function should have been invoced once. ASSERT_EQ(_imageFunctionInvocations[kImagePath0], 1); // Render the scene and check baseline image. @@ -177,7 +158,7 @@ TEST_P(PathTest, TestPathDefault) pScene->setInstanceProperties( instPaths[1], { { Names::InstanceProperties::kMaterial, kMaterialPath1 } }); - // Now the image getPixelData function should have been invoced for first two images. + // Now the image getData function should have been invoced for first two images. ASSERT_EQ(_imageFunctionInvocations[kImagePath0], 1); ASSERT_EQ(_imageFunctionInvocations[kImagePath1], 1); @@ -185,7 +166,7 @@ TEST_P(PathTest, TestPathDefault) pScene->setMaterialProperties(kMaterialPath0, { { "base_color_image", kImagePath2 } }); - // Now the image getPixelData function should have been invoced for all the images. + // Now the image getData function should have been invoced for all the images. ASSERT_EQ(_imageFunctionInvocations[kImagePath0], 1); ASSERT_EQ(_imageFunctionInvocations[kImagePath1], 1); ASSERT_EQ(_imageFunctionInvocations[kImagePath2], 1); diff --git a/Tests/Aurora/Tests/TestRenderer.cpp b/Tests/Aurora/Tests/TestRenderer.cpp index f5cb5ea..37bdf8e 100644 --- a/Tests/Aurora/Tests/TestRenderer.cpp +++ b/Tests/Aurora/Tests/TestRenderer.cpp @@ -60,6 +60,19 @@ TEST_P(RendererTest, TestRendererDefault) ASSERT_EQ(pRenderer->backend(), backend) << rendererDescription(); } +// Ensure debug flag is not set on device. +TEST_P(RendererTest, TestRendererDebugDevice) +{ + if (!backendSupported()) + return; + + auto renderer = createRenderer(rendererBackend()); + ASSERT_NE(renderer.get(), nullptr); + + // Look for the string that DXDevice::initialize will print to console if debug enabled. + ASSERT_THAT(lastLogMessage(), ::testing::Not(::testing::HasSubstr("AU_DEVICE_DEBUG_ENABLED"))); +} + // Test creating, destroying renderer then rendering. TEST_P(RendererTest, TestRendererCreateDestroyThenRenderFull) { @@ -220,7 +233,7 @@ TEST_P(RendererTest, TestRendererDestroyRenderBufferFirst) // Create a scene. IScenePtr pScene = pRenderer->createScene(); - // Set arbritary bounds. + // Set arbitrary bounds. vec3 boundsMin(-1, -1, -1); vec3 boundsMax(+1, +1, +1); pScene->setBounds(boundsMin, boundsMax); @@ -260,7 +273,7 @@ TEST_P(RendererTest, TestRendererEmptyScene) // Create a scene. IScenePtr pScene = pRenderer->createScene(); - // Set arbritary bounds. + // Set arbitrary bounds. vec3 boundsMin(-1, -1, -1); vec3 boundsMax(+1, +1, +1); pScene->setBounds(boundsMin, boundsMax); @@ -443,6 +456,7 @@ TEST_P(RendererTest, TestRendererEmptySceneBounds) } // Test ground plane. +// TODO: Re-enable once ground plane re-enabled. TEST_P(RendererTest, TestRendererGroundPlane) { auto pScene = createDefaultScene(); @@ -462,7 +476,7 @@ TEST_P(RendererTest, TestRendererGroundPlane) setDefaultRendererCamera(vec3(0, 1, -5), vec3(0, 0, 0)); - // Set arbritary bounds. + // Set arbitrary bounds. vec3 boundsMin(-1, -1, -1); vec3 boundsMax(+1, +1, +1); pScene->setBounds(boundsMin, boundsMax); @@ -518,7 +532,7 @@ TEST_P(RendererTest, TestRendererInstanceProperties) if (!pRenderer) return; - // Set arbritary bounds. + // Set arbitrary bounds. vec3 boundsMin(-1, -1, -1); vec3 boundsMax(+1, +1, +1); pScene->setBounds(boundsMin, boundsMax); @@ -552,7 +566,7 @@ TEST_P(RendererTest, TestRendererNonIndexedGeom) if (!pRenderer) return; - // Set arbritary bounds. + // Set arbitrary bounds. vec3 boundsMin(-1, -1, -1); vec3 boundsMax(+1, +1, +1); pScene->setBounds(boundsMin, boundsMax); @@ -615,7 +629,7 @@ TEST_P(RendererTest, TestRendererRemoveInstance) if (!pRenderer) return; - // Set arbritary bounds. + // Set arbitrary bounds. vec3 boundsMin(-1, -1, -1); vec3 boundsMax(+1, +1, +1); pScene->setBounds(boundsMin, boundsMax); @@ -641,6 +655,7 @@ TEST_P(RendererTest, TestRendererRemoveInstance) } // Test instance with layer materials +// TODO: Re-enable test when layers are working. TEST_P(RendererTest, TestRendererMaterialLayers) { auto pScene = createDefaultScene(); @@ -650,41 +665,26 @@ TEST_P(RendererTest, TestRendererMaterialLayers) if (!pRenderer) return; - const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; - TestHelpers::ImageData normalImageData; - loadImage(txtNormalName, &normalImageData); // Create the image. - const Path kNormalImagePath = "NormalImage"; - normalImageData.descriptor.linearize = false; - pScene->setImageDescriptor(kNormalImagePath, normalImageData.descriptor); - + const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; + const Path normalImagePath = loadImage(txtNormalName, false); Path kMaterialPath = "BaseMaterial"; - pScene->setMaterialProperties(kMaterialPath, {{"normal_image", kNormalImagePath}, { "base_color", vec3(1, 0, 0) } }); + pScene->setMaterialProperties(kMaterialPath, {{"normal_image", normalImagePath}, { "base_color", vec3(1, 0, 0) } }); - // Load pixels for test image file. + // Load opacity image file. const std::string txtName = dataPath() + "/Textures/Triangle.png"; + Path opacityImagePath = loadImage(txtName); - // Load image - TestHelpers::ImageData opactiyImageData; - loadImage(txtName, &opactiyImageData); - - // Create the image. - const Path kOpacityImagePath = "OpacityImage"; - pScene->setImageDescriptor(kOpacityImagePath, opactiyImageData.descriptor); - + // Load diffuse image file. const std::string mandrillTxPath = dataPath() + "/Textures/Mandrill.png"; - - const Path kMandrillImagePath = "MandrillImage"; - TestHelpers::ImageData imageData; - loadImage(mandrillTxPath, &imageData); - pScene->setImageDescriptor(kMandrillImagePath, imageData.descriptor); - + Path mandrillImagePath = loadImage(mandrillTxPath); + // Create a material. const Path kDecalMaterialPath0 = "kDecalMaterial0"; - pScene->setMaterialProperties(kDecalMaterialPath0, { { "opacity_image", kOpacityImagePath }, { "base_color_image", kMandrillImagePath }, {"diffuse_roughness",0.9f} }); + pScene->setMaterialProperties(kDecalMaterialPath0, { { "opacity_image", opacityImagePath }, { "base_color_image", mandrillImagePath }, {"diffuse_roughness",0.9f} }); ; vector xformedUVs(TestHelpers::TeapotModel::verticesCount()); @@ -731,7 +731,8 @@ TEST_P(RendererTest, TestRendererMaterialLayers) ASSERT_BASELINE_IMAGE_PASSES(currentTestName()); } -// Test instance with layer materials +// Test instance with invalid layer material paths +// TODO: Re-enable test when layers are working. TEST_P(RendererTest, TestRendererInvalidMaterialLayerPaths) { // No layers on HGI currently. @@ -745,41 +746,26 @@ TEST_P(RendererTest, TestRendererInvalidMaterialLayerPaths) if (!pRenderer) return; - const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; - TestHelpers::ImageData normalImageData; - loadImage(txtNormalName, &normalImageData); // Create the image. - const Path kNormalImagePath = "NormalImage"; - normalImageData.descriptor.linearize = false; - pScene->setImageDescriptor(kNormalImagePath, normalImageData.descriptor); - + const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; + const Path normalImagePath = loadImage(txtNormalName, false); Path kMaterialPath = "BaseMaterial"; - pScene->setMaterialProperties(kMaterialPath, {{"normal_image", kNormalImagePath}, { "base_color", vec3(1, 0, 0) } }); + pScene->setMaterialProperties(kMaterialPath, {{"normal_image", normalImagePath}, { "base_color", vec3(1, 0, 0) } }); - // Load pixels for test image file. + // Load opacity image. const std::string txtName = dataPath() + "/Textures/Triangle.png"; + const Path opacityImagePath = loadImage(txtName); - // Load image - TestHelpers::ImageData opactiyImageData; - loadImage(txtName, &opactiyImageData); - - // Create the image. - const Path kOpacityImagePath = "OpacityImage"; - pScene->setImageDescriptor(kOpacityImagePath, opactiyImageData.descriptor); - + // Load diffuse image. const std::string mandrillTxPath = dataPath() + "/Textures/Mandrill.png"; - - const Path kMandrillImagePath = "MandrillImage"; - TestHelpers::ImageData imageData; - loadImage(mandrillTxPath, &imageData); - pScene->setImageDescriptor(kMandrillImagePath, imageData.descriptor); + const Path mandrillImagePath = loadImage(mandrillTxPath); // Create a material. const Path kDecalMaterialPath0 = "kDecalMaterial0"; - pScene->setMaterialProperties(kDecalMaterialPath0, { { "opacity_image", kOpacityImagePath }, { "base_color_image", kMandrillImagePath }, {"diffuse_roughness",0.9f} }); + pScene->setMaterialProperties(kDecalMaterialPath0, { { "opacity_image", opacityImagePath }, { "base_color_image", mandrillImagePath }, {"diffuse_roughness",0.9f} }); ; vector xformedUVs(TestHelpers::TeapotModel::verticesCount()); @@ -835,6 +821,7 @@ TEST_P(RendererTest, TestRendererInvalidMaterialLayerPaths) ::testing::StartsWith("AU_ASSERT test failed:\nEXPRESSION: iter != _container.end()")); } +// TODO: Re-enable test when layers are working. TEST_P(RendererTest, TestRendererInvalidGeometryLayers) { // No layers on HGI backed. @@ -849,40 +836,24 @@ TEST_P(RendererTest, TestRendererInvalidGeometryLayers) return; const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; - TestHelpers::ImageData normalImageData; - loadImage(txtNormalName, &normalImageData); - // Create the image. - const Path kNormalImagePath = "NormalImage"; - normalImageData.descriptor.linearize = false; - pScene->setImageDescriptor(kNormalImagePath, normalImageData.descriptor); - + Path normalImagePath = loadImage(txtNormalName, false); Path kMaterialPath = "BaseMaterial"; - pScene->setMaterialProperties(kMaterialPath, {{"normal_image", kNormalImagePath}, { "base_color", vec3(1, 0, 0) } }); + pScene->setMaterialProperties(kMaterialPath, {{"normal_image", normalImagePath}, { "base_color", vec3(1, 0, 0) } }); - // Load pixels for test image file. + // Load opacity image. const std::string txtName = dataPath() + "/Textures/Triangle.png"; + const Path opacityImagePath = loadImage(txtName);; - // Load image - TestHelpers::ImageData opactiyImageData; - loadImage(txtName, &opactiyImageData); - - // Create the image. - const Path kOpacityImagePath = "OpacityImage"; - pScene->setImageDescriptor(kOpacityImagePath, opactiyImageData.descriptor); - + // Load diffuse image. const std::string mandrillTxPath = dataPath() + "/Textures/Mandrill.png"; - - const Path kMandrillImagePath = "MandrillImage"; - TestHelpers::ImageData imageData; - loadImage(mandrillTxPath, &imageData); - pScene->setImageDescriptor(kMandrillImagePath, imageData.descriptor); - + const Path mandrillImagePath = loadImage(mandrillTxPath);; + // Create a material. const Path kDecalMaterialPath0 = "kDecalMaterial0"; - pScene->setMaterialProperties(kDecalMaterialPath0, { { "opacity_image", kOpacityImagePath }, { "base_color_image", kMandrillImagePath }, {"diffuse_roughness",0.9f} }); + pScene->setMaterialProperties(kDecalMaterialPath0, { { "opacity_image", opacityImagePath }, { "base_color_image", mandrillImagePath }, {"diffuse_roughness",0.9f} }); ; vector xformedUVs(TestHelpers::TeapotModel::verticesCount()); @@ -951,6 +922,7 @@ TEST_P(RendererTest, TestRendererInvalidGeometryLayers) } // Test instance with layer materials +// TODO: Re-enable test when layers are working. TEST_P(RendererTest, TestRendererMultipleMaterialLayers) { if (!isDirectX()) @@ -963,47 +935,30 @@ TEST_P(RendererTest, TestRendererMultipleMaterialLayers) if (!pRenderer) return; - const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; - TestHelpers::ImageData normalImageData; - loadImage(txtNormalName, &normalImageData); - // Create the image. - const Path kNormalImagePath = "NormalImage"; - normalImageData.descriptor.linearize = false; - pScene->setImageDescriptor(kNormalImagePath, normalImageData.descriptor); - + const std::string txtNormalName = dataPath() + "/Textures/fishscale_normal.png"; + const Path normalImagePath = loadImage(txtNormalName); Path kMaterialPath = "BaseMaterial"; pScene->setMaterialProperties(kMaterialPath, { { "base_color", vec3(1, 0, 0) } }); - // Load pixels for test image file. + // Load opacity image. const std::string txtName = dataPath() + "/Textures/Triangle.png"; + const Path opacityImagePath = loadImage(txtName); - // Load image - TestHelpers::ImageData opactiyImageData; - loadImage(txtName, &opactiyImageData); - - // Create the image. - const Path kOpacityImagePath = "OpacityImage"; - pScene->setImageDescriptor(kOpacityImagePath, opactiyImageData.descriptor); - + // Load metallic image. const std::string metalTxtPath = dataPath() + "/Textures/fishscale_roughness.png"; - const Path kMetalImage = "MetalImage"; - TestHelpers::ImageData imageData; - loadImage(metalTxtPath, &imageData); - pScene->setImageDescriptor(kMetalImage, imageData.descriptor); - + const Path metalImagePath = loadImage(metalTxtPath);; + // Load diffuse image const std::string mandrillTxPath = dataPath() + "/Textures/Mandrill.png"; - const Path kMandrillImagePath = "MandrillImage"; - loadImage(mandrillTxPath, &imageData); - pScene->setImageDescriptor(kMandrillImagePath, imageData.descriptor); + const Path mandrillImagePath = loadImage(mandrillTxPath); // Create two decal materials. vector materialLayers = {"DecalMaterial0","DecalMaterial1"} ; - pScene->setMaterialProperties(materialLayers[0], { {"normal_image", kNormalImagePath},{ "opacity_image", kOpacityImagePath }, { "base_color_image", kMetalImage }, {"diffuse_roughness",0.2f} }); - pScene->setMaterialProperties(materialLayers[1], { { "opacity_image", kOpacityImagePath }, { "base_color_image", kMandrillImagePath }, {"diffuse_roughness",0.6f}, {"metalness",0.9f} }); + pScene->setMaterialProperties(materialLayers[0], { {"normal_image", normalImagePath},{ "opacity_image", opacityImagePath }, { "base_color_image", metalImagePath }, {"diffuse_roughness",0.2f} }); + pScene->setMaterialProperties(materialLayers[1], { { "opacity_image", opacityImagePath }, { "base_color_image", mandrillImagePath }, {"diffuse_roughness",0.6f}, {"metalness",0.9f} }); int numLayers = 2; vector geometryLayers; @@ -1273,14 +1228,7 @@ TEST_P(RendererTest, TestDebugNormals) // Load pixels for test image file. const std::string txtName = dataPath() + "/Textures/Verde_Guatemala_Slatted_Marble_normal.png"; - // Load image - TestHelpers::ImageData imageData; - loadImage(txtName, &imageData); - - // Create the image. - const Path kImagePath = "NormalImage"; - imageData.descriptor.linearize = false; - pScene->setImageDescriptor(kImagePath, imageData.descriptor); + const Path imagePath = loadImage(txtName, false); // Create geometry. Path planePath = createPlaneGeometry(*pScene, vec2(0.5f,0.5f)); @@ -1288,7 +1236,7 @@ TEST_P(RendererTest, TestDebugNormals) // Create matrix and material for first instance (which is linearized). const Path kMaterialPath = "NormalMaterial"; - pScene->setMaterialProperties(kMaterialPath, { { "normal_image", kImagePath },{ "normal_image_scale", vec2(5.f, 5.f)} }); + pScene->setMaterialProperties(kMaterialPath, { { "normal_image", imagePath },{ "normal_image_scale", vec2(5.f, 5.f)} }); const Path kBasicMaterialPath = "BasicMaterial"; pScene->setMaterialProperties(kBasicMaterialPath, { { "base_color", vec3(0,1,0) } }); diff --git a/Tests/AuroraInternals/Common/TestMaterialGenerator.cpp b/Tests/AuroraInternals/Common/TestMaterialGenerator.cpp index 1f4a472..f8fd36d 100644 --- a/Tests/AuroraInternals/Common/TestMaterialGenerator.cpp +++ b/Tests/AuroraInternals/Common/TestMaterialGenerator.cpp @@ -15,6 +15,8 @@ #if !defined(DISABLE_UNIT_TESTS) #include "TestHelpers.h" + +#include #include // Include the Aurora PCH (this is an internal test so needs all the internal Aurora includes) @@ -33,10 +35,29 @@ class MaterialGeneratorTest : public ::testing::Test MaterialGeneratorTest() : _dataPath(TestHelpers::kSourceRoot + "/Tests/Assets") {} ~MaterialGeneratorTest() {} const std::string& dataPath() { return _dataPath; } + // Test for the existence of the ADSK materialX libraries (in the working folder for the tests) + bool adskMaterialXSupport() { return std::filesystem::exists("MaterialX/libraries/adsk"); } protected: std::string _dataPath; }; + +bool readTextFile(const string& filename, string& textOut) +{ + ifstream is(filename, ifstream::binary); + + // If file could not be opened, continue. + if (!is) + return false; + is.seekg(0, is.end); + size_t length = is.tellg(); + is.seekg(0, is.beg); + textOut.resize(length); + is.read((char*)&textOut[0], length); + + return true; +} + const char* materialXString0 = R""""( @@ -110,6 +131,28 @@ const char* materialXString2 = R""""( )""""; +const char* materialXString3 = R""""( + + + + + + + + + + + + + + + + + + + +)""""; + // Basic material generator test. TEST_F(MaterialGeneratorTest, BasicTest) { @@ -143,12 +186,18 @@ TEST_F(MaterialGeneratorTest, BasicTest) Aurora::MaterialDefinitionPtr pMtlDef2 = matGen.generate(materialXString2); ASSERT_NE(pMtlDef2, nullptr); - ASSERT_STREQ(pMtlDef2->source().uniqueId.c_str(), "MaterialX_58010dd45510ab94"); + ASSERT_STREQ(pMtlDef2->source().uniqueId.c_str(), "MaterialX_ef09b35c573765bd"); ASSERT_EQ(pMtlDef2->defaults().properties.size(), 6); ASSERT_EQ(pMtlDef2->defaults().propertyDefinitions.size(), 6); ASSERT_EQ(pMtlDef2->defaults().textureNames.size(), 2); ASSERT_EQ(pMtlDef2->defaults().textures.size(), 2); - ASSERT_STREQ(pMtlDef2->defaults().textureNames[1].c_str(), "opacity_image"); + ASSERT_STREQ(pMtlDef2->defaults().textureNames[0].image.c_str(), "base_color_image"); + ASSERT_STREQ(pMtlDef2->defaults().textureNames[0].sampler.c_str(), "base_color_image_sampler"); + ASSERT_STREQ( + pMtlDef2->defaults().textures[0].defaultFilename.c_str(), "../Textures/CoatOfArms.bmp"); + ASSERT_STREQ(pMtlDef2->defaults().textureNames[1].image.c_str(), "specular_roughness_image"); + ASSERT_STREQ( + pMtlDef2->defaults().textureNames[1].sampler.c_str(), "specular_roughness_image_sampler"); ASSERT_STREQ(pMtlDef2->defaults().textures[1].defaultFilename.c_str(), "../Textures/fishscale_roughness.png"); @@ -229,6 +278,167 @@ TEST_F(MaterialGeneratorTest, MaterialShaderLibraryTest) ASSERT_EQ(numDestroyed, 1); } +TEST_F(MaterialGeneratorTest, CodeGenTest) +{ + string mtlxFolder = Foundation::getModulePath() + "MaterialX"; + Aurora::MaterialXCodeGen::BSDFCodeGenerator codeGen(mtlxFolder); + + map bsdfInputParamMapping = { { "base", "base" }, { "base_color", "baseColor" }, + { "coat", "coat" }, { "coat_color", "coatColor" }, { "coat_roughness", "coatRoughness" }, + { "coat_anisotropy", "coatAnisotropy" }, { "coat_rotation", "coatRotation" }, + { "coat_IOR", "coatIOR" }, { "diffuse_roughness", "diffuseRoughness" }, + { "metalness", "metalness" }, { "specular", "specular" }, + { "specular_color", "specularColor" }, { "specular_roughness", "specularRoughness" }, + { "specular_anisotropy", "specularAnisotropy" }, { "specular_IOR", "specularIOR" }, + { "specular_rotation", "specularRotation" }, { "transmission", "transmission" }, + { "transmission_color", "transmissionColor" }, { "subsurface", "subsurface" }, + { "subsurfaceColor", "subsurfaceColor" }, { "subsurface_scale", "subsurfaceScale" }, + { "subsurface_radius", "subsurfaceRadius" }, { "sheen", "sheen" }, + { "sheen_color", "sheenColor" }, { "sheen_roughness", "sheenRoughness" }, + { "coat", "coat" }, { "coat_color", "coatColor" }, { "coat_roughness", "coatRoughness" }, + { "coat_anisotropy", "coatAnisotropy" }, { "coat_rotation", "coatRotation" }, + { "coat_IOR", "coatIOR" }, { "coat_affect_color", "coatAffectColor" }, + { "coat_affect_roughness", "coatAffectRoughness" }, { "emission", "emission" }, + { "emission_color", "emissionColor" }, { "opacity", "opacity" }, + { "base_color_image_scale", "baseColorTexTransform.scale" }, + { "base_color_image_offset", "baseColorTexTransform.offset" }, + { "base_color_image_rotation", "baseColorTexTransform.rotation" }, + { "emission_color_image_scale", "emissionColorTexTransform.scale" }, + { "emission_color_image_offset", "emissionColorTexTransform.offset" }, + { "emission_color_image_rotation", "emissionColorTexTransform.rotation" }, + { "opacity_image_scale", "opacityTexTransform.scale" }, + { "opacity_image_offset", "opacityTexTransform.offset" }, + { "opacity_image_rotation", "opacityTexTransform.rotation" }, + { "normal_image_scale", "normalTexTransform.scale" }, + { "normal_image_offset", "normalTexTransform.offset" }, + { "normal_image_rotation", "normalTexTransform.rotation" }, + { "specular_roughness_image_scale", "specularRoughnessTexTransform.scale" }, + { "specular_roughness_image_offset", "specularRoughnessTexTransform.offset" }, + { "specular_roughness_image_rotation", "specularRoughnessTexTransform.rotation" }, + { "thin_walled", "thinWalled" }, { "normal", "normal" } }; + + // Create set of supported BSDF inputs. + set supportedBSDFInputs; + for (auto iter : bsdfInputParamMapping) + { + supportedBSDFInputs.insert(iter.first); + } + + Aurora::MaterialXCodeGen::BSDFCodeGenerator::Result res; + bool ok; + string errMsg, defStr; + + ok = codeGen.generate(materialXString0, &res, supportedBSDFInputs); + ASSERT_TRUE(ok); + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX0_Setup.glsl", + res.materialSetupCode, "Generated setup code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX0_Struct.glsl", + res.materialStructCode, "Generated struct code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.generateDefinitions(&defStr); + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX0_Defs.glsl", + defStr, "Generated definitions code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.clearDefinitions(); + ok = codeGen.generate(materialXString1, &res, supportedBSDFInputs); + ASSERT_TRUE(ok); + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX1_Setup.glsl", + res.materialSetupCode, "Generated setup code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX1_Struct.glsl", + res.materialStructCode, "Generated struct code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.generateDefinitions(&defStr); + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX1_Defs.glsl", + defStr, "Generated definitions code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.clearDefinitions(); + ok = codeGen.generate(materialXString2, &res, supportedBSDFInputs); + ASSERT_TRUE(ok); + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX2_Setup.glsl", + res.materialSetupCode, "Generated setup code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX2_Struct.glsl", + res.materialStructCode, "Generated struct code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.generateDefinitions(&defStr); + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX2_Defs.glsl", + defStr, "Generated definitions code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.clearDefinitions(); + ok = codeGen.generate(materialXString3, &res, supportedBSDFInputs); + ASSERT_TRUE(ok); + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX3_Setup.glsl", + res.materialSetupCode, "Generated setup code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX3_Struct.glsl", + res.materialStructCode, "Generated struct code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.generateDefinitions(&defStr); + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/TestMaterialX3_Defs.glsl", + defStr, "Generated definitions code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + string readMtlXText; + + if (adskMaterialXSupport()) + { + ok = readTextFile(dataPath() + "/Materials/Decals/test_decal_mask.mtlx", readMtlXText); + ASSERT_TRUE(ok); + + ok = codeGen.generate(readMtlXText, &res, supportedBSDFInputs); + ASSERT_TRUE(ok); + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/Mask_Setup.glsl", + res.materialSetupCode, "Generated setup code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/Mask_Struct.glsl", + res.materialStructCode, "Generated struct code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.generateDefinitions(&defStr); + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/Mask_Defs.glsl", defStr, + "Generated definitions code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + } + + ok = readTextFile(dataPath() + "/Materials/HdAuroraTextureTest.mtlx", readMtlXText); + ASSERT_TRUE(ok); + + ok = codeGen.generate(readMtlXText, &res, supportedBSDFInputs); + ASSERT_TRUE(ok); + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/HdAuroraTextureTest_Setup.glsl", + res.materialSetupCode, "Generated setup code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/HdAuroraTextureTest_Struct.glsl", + res.materialStructCode, "Generated struct code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; + + codeGen.generateDefinitions(&defStr); + errMsg = TestHelpers::compareTextFile(dataPath() + "/TextFiles/HdAuroraTextureTest_Defs.glsl", + defStr, "Generated definitions code comparison failed"); + ASSERT_TRUE(errMsg.empty()) << errMsg; +} + } // namespace #endif diff --git a/Tests/Helpers/AuroraTestHelpers.cpp b/Tests/Helpers/AuroraTestHelpers.cpp index a372733..32490f2 100644 --- a/Tests/Helpers/AuroraTestHelpers.cpp +++ b/Tests/Helpers/AuroraTestHelpers.cpp @@ -169,75 +169,78 @@ uint32_t TeapotModel::indicesCount() #define ASSERT_VALUE_FAIL(_expr) ASSERT_ANY_THROW(_expr) #endif -// Helper function to load an image from disk. -void FixtureBase::loadImage(const string& filename, ImageData* pImageOut) +// Helper function to load an image from disk, the image will be created with the filename used as +// the Aurora path. +Aurora::Path FixtureBase::loadImage(const string& filename, bool linearize) { - // Use STB to load image file. - int width, height, components; - unsigned char* pPixels = stbi_load(filename.c_str(), &width, &height, &components, 0); - AU_ASSERT(pPixels != nullptr, "Failed to load image:%s", filename.c_str()); - - // Create Aurora image data struct from the pixels. - ImageDescriptor& imageDesc = pImageOut->descriptor; - imageDesc.width = width; - imageDesc.height = height; - imageDesc.linearize = true; - imageDesc.format = ImageFormat::Integer_RGBA; - size_t numPixels = static_cast(width * height * 4); - pImageOut->buffer.resize(numPixels * 4); - unsigned char* pImageData = &(pImageOut->buffer)[0]; - - // STB usually provides RGB, pad to RGBA if needed. - if (components == 3) - { - // Flip vertically as STB loads upside down. - for (int i = 0; i < height; i++) + ImageDescriptor imageDesc; + imageDesc.linearize = linearize; + Path path = "TestImage:" + filename + "-linearize=" + to_string(linearize); + + // Set up the pixel data callback + imageDesc.getData = [this, path, filename](ImageData& dataOut, AllocateBufferFunction alloc) { + // Use STB to load image file. + int width, height, components; + unsigned char* pReadPixels = stbi_load(filename.c_str(), &width, &height, &components, 0); + AU_ASSERT(pReadPixels != nullptr, "Failed to load image:%s", filename.c_str()); + + size_t numPixels = static_cast(width * height); + size_t numBytes = numPixels * 4; + unsigned char* pImageData = static_cast(alloc(numBytes)); + + // Create Aurora image data struct from the pixels. + dataOut.dimensions = { width, height }; + dataOut.format = ImageFormat::Integer_RGBA; + dataOut.pPixelBuffer = pImageData; + dataOut.bufferSize = numBytes; + + // STB usually provides RGB, pad to RGBA if needed. + if (components == 3) { - int flippedRow = (height - 1 - i); - unsigned char* pIn = pPixels + (flippedRow * width * 3); - unsigned char* pOut = ((unsigned char*)pImageData) + (i * width * 4); - for (int j = 0; j < width; j++) + // Flip vertically as STB loads upside down. + for (int i = 0; i < height; i++) { - *(pOut++) = *(pIn++); - *(pOut++) = *(pIn++); - *(pOut++) = *(pIn++); - *(pOut++) = 255; + int flippedRow = (height - 1 - i); + unsigned char* pIn = pReadPixels + (flippedRow * width * 3); + unsigned char* pOut = ((unsigned char*)pImageData) + (i * width * 4); + for (int j = 0; j < width; j++) + { + *(pOut++) = *(pIn++); + *(pOut++) = *(pIn++); + *(pOut++) = *(pIn++); + *(pOut++) = 255; + } } } - } - else if (components == 4) - { - // Flip vertically as STB loads upside down. - for (int i = 0; i < height; i++) + else if (components == 4) { - int flippedRow = (height - 1 - i); - unsigned char* pIn = pPixels + (flippedRow * width * 4); - unsigned char* pOut = ((unsigned char*)pImageData) + (i * width * 4); - for (int j = 0; j < width; j++) + // Flip vertically as STB loads upside down. + for (int i = 0; i < height; i++) { - *(pOut++) = *(pIn++); - *(pOut++) = *(pIn++); - *(pOut++) = *(pIn++); - *(pOut++) = *(pIn++); + int flippedRow = (height - 1 - i); + unsigned char* pIn = pReadPixels + (flippedRow * width * 4); + unsigned char* pOut = ((unsigned char*)pImageData) + (i * width * 4); + for (int j = 0; j < width; j++) + { + *(pOut++) = *(pIn++); + *(pOut++) = *(pIn++); + *(pOut++) = *(pIn++); + *(pOut++) = *(pIn++); + } } } - } - else - { - AU_FAIL("%s invalid number of components %d", filename.c_str(), components); - } - - free(pPixels); + else + { + AU_FAIL("%s invalid number of components %d", filename.c_str(), components); + } + free(pReadPixels); - // Set up the pixel data callback - imageDesc.getPixelData = [pImageOut](PixelData& dataOut, glm::ivec2, glm::ivec2) { - // Get addres and size from buffer (assumes will be called from scope of test, so buffer - // still valid) - dataOut.address = pImageOut->buffer.data(); - dataOut.size = pImageOut->buffer.size(); return true; }; -}; + _pDefaultScene->setImageDescriptor(path, imageDesc); + + return path; +} // Convenience function to test a float3 value. void FixtureBase::testFloat3Value( diff --git a/Tests/Helpers/AuroraTestHelpers.h b/Tests/Helpers/AuroraTestHelpers.h index 4c5830c..f1b3603 100644 --- a/Tests/Helpers/AuroraTestHelpers.h +++ b/Tests/Helpers/AuroraTestHelpers.h @@ -39,13 +39,6 @@ using namespace std; namespace TestHelpers { -// Loaded image data -struct ImageData -{ - Aurora::ImageDescriptor descriptor; - vector buffer; -}; - // Geometry for teapot test model created from OBJ class TeapotModel { @@ -82,7 +75,7 @@ class FixtureBase : public testing::TestWithParam /// Get the data path used for test assets. const string& dataPath() { return _dataPath; } - void loadImage(const string& filename, ImageData* pImageOut); + Aurora::Path loadImage(const string& filename, bool linearize = true); /// \brief Creates a default renderer and render buffer for renderer tests to use if needed for /// conciseness (and associated render buffer.) diff --git a/Tests/Helpers/TestHelpers.cpp b/Tests/Helpers/TestHelpers.cpp index bf77627..2456d0a 100644 --- a/Tests/Helpers/TestHelpers.cpp +++ b/Tests/Helpers/TestHelpers.cpp @@ -49,17 +49,30 @@ float as_float(const uint32_t x) std::string compareTextFile( const std::string filename, const std::string cmpString, const std::string message) { + std::string errMsg = ""; std::stringstream cmpsstream(cmpString); std::fstream infile; infile.open(filename, std::ios::in); - if (infile.bad()) + if (infile.bad() || !infile.is_open()) { errMsg = "Failed to open baseline text file:" + filename; } if (errMsg.empty()) { + if (cmpString.empty()) + { + infile.seekg(0, infile.end); + size_t length = infile.tellg(); + infile.seekg(0, infile.beg); + + if (length == 0) + return ""; + + return message + " comparison string is empty but " + filename + " contains " + + std::to_string(length) + "characters."; + } std::string inlinestr; std::string cmplinestr; bool atEof = false;