diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1ee0c6d29..c0ee2969d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 821100d967e1737d96414a308e3f7cbe0d1abf18 + committish: a993be073c6baea8674117f3eaed6e2d2486aaae cache: ${{ env.USE_CACHE }} # This doesn't work when the Visual Studio C++ CLI was set up first (maybe needs a setup with 2019 version) @@ -176,7 +176,7 @@ jobs: compression-level: 9 linux-build: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 name: Build on Linux # Run both builds in parallel and don't cancel if one fails strategy: @@ -206,7 +206,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 821100d967e1737d96414a308e3f7cbe0d1abf18 + committish: a993be073c6baea8674117f3eaed6e2d2486aaae cache: ${{ env.USE_CACHE }} - name: Setup Ninja @@ -225,8 +225,10 @@ jobs: shell: bash # Add additional scripting steps here run: | - sudo apt-get update - sudo apt-get install libgl1-mesa-dev + sudo apt update + sudo apt install libgl1-mesa-dev autoconf automake libtool pkg-config libltdl-dev g++-14 gcc-14 -y + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 14 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 14 cd ${{ github.workspace }} ${{ github.workspace }}/vcpkg/vcpkg install --clean-after-build --triplet=x64-linux rm -r vcpkg_installed @@ -337,7 +339,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 821100d967e1737d96414a308e3f7cbe0d1abf18 + committish: a993be073c6baea8674117f3eaed6e2d2486aaae cache: ${{ env.USE_CACHE }} - name: Setup Ninja diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b810a45e2..e40ec23a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 821100d967e1737d96414a308e3f7cbe0d1abf18 + committish: a993be073c6baea8674117f3eaed6e2d2486aaae - name: Delete MSVC tool version shell: pwsh @@ -110,7 +110,7 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 821100d967e1737d96414a308e3f7cbe0d1abf18 + committish: a993be073c6baea8674117f3eaed6e2d2486aaae - name: Setup Ninja uses: ashutoshvarma/setup-ninja@master @@ -178,7 +178,8 @@ jobs: uses: friendlyanon/setup-vcpkg@v1 # Committish: The commit sha of the vcpkg repo, same as in vcpkg.json with: - committish: 821100d967e1737d96414a308e3f7cbe0d1abf18 + committish: a993be073c6baea8674117f3eaed6e2d2486aaae + cache: ${{ env.USE_CACHE }} - name: Setup Ninja uses: ashutoshvarma/setup-ninja@master diff --git a/.gitignore b/.gitignore index 1bfefbe88..91cc42453 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,7 @@ data/pirates data/meadow .vscode data/elven +data/dungeon +data/namaqualand +data/quarry +data/hiddenalley diff --git a/CMakeLists.txt b/CMakeLists.txt index 7efc9d3fd..c7a8ae9db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,11 +8,12 @@ if(COMMAND cmake_policy) cmake_policy(SET CMP0074 NEW) cmake_policy(SET CMP0011 NEW) cmake_policy(SET CMP0042 NEW) + cmake_policy(SET CMP0141 NEW) endif(COMMAND cmake_policy) -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 3.25) -project(AtlasEngine VERSION 0.2.0) +project(AtlasEngine VERSION 0.2.1) # Only 64 bit is supported ################################################################### @@ -31,6 +32,7 @@ option(ATLAS_ASSIMP "Activate Assimp integration" ON) option(ATLAS_HEADLESS "Activate support for running the engine in headless mode" OFF) option(ATLAS_BINDLESS "Activate support for running the engine with bindless resources turned on" ON) option(ATLAS_BUNDLE "Allows the applications to be bundled and installed on MacOS" OFF) +option(ATLAS_MSVC_HOT_RELOAD "Configures all targets to be hot-reloadable" OFF) if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) option(ATLAS_TESTS "Activate support for running the engine with bindless resources turned on" ON) @@ -38,6 +40,11 @@ else() option(ATLAS_TESTS "Activate support for running the engine with bindless resources turned on" OFF) endif() +# Hot reloading in Windows, enabled for all targets +if ((CMAKE_CXX_COMPILER_ID MATCHES "MSVC") AND ATLAS_MSVC_HOT_RELOAD) + set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:EditAndContinue>") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /INCREMENTAL:YES" ) +endif() if (ATLAS_DEMO) set (ATLAS_IMGUI ON) @@ -49,13 +56,13 @@ if (ATLAS_EDITOR) set (ATLAS_IMGUI ON) endif() -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) if (CYGWIN OR MINGW) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -O3 -std=gnu++20" ) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -O3 -std=gnu++23" ) endif() if (ANDROID) @@ -91,7 +98,7 @@ find_package(VulkanMemoryAllocator CONFIG REQUIRED) find_package(glslang CONFIG REQUIRED) find_package(SPIRV-Tools-opt CONFIG REQUIRED) find_package(nlohmann_json CONFIG REQUIRED) -find_package(unofficial-joltphysics CONFIG REQUIRED) +find_package(Jolt CONFIG REQUIRED) find_package(Lua REQUIRED) find_package(sol2 CONFIG REQUIRED) diff --git a/README.md b/README.md index c37594ff2..6de3a092d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This is a cross platform toy engine developed in my spare time that is available >The current version (0.2.0) contains many API changes and is still an active WIP ## Requirements - Vulkan SDK -- C++20 compatible compiler +- C++23 compatible compiler - CMake - Vcpkg ## Set up diff --git a/data/scenes/sponza.aescene b/data/scenes/sponza.aescene index 80a1641a0..0f5cc95e0 100644 --- a/data/scenes/sponza.aescene +++ b/data/scenes/sponza.aescene @@ -1 +1 @@ -{"aabb":{"max":{"x":128.0,"y":128.0,"z":128.0},"min":{"x":-128.0,"y":-128.0,"z":-128.0}},"ao":{"enable":false,"halfResolution":true,"opacityCheck":false,"radius":3.0,"rt":true,"sampleCount":16,"strength":1.0},"depth":5.0,"entities":[{"entities":[{"id":1,"light":{"color":{"x":1.0,"y":0.9254902005195618,"z":0.8196078538894653},"intensity":10.0,"isMain":true,"mobility":0,"properties":{"direction":{"x":0.0,"y":-1.0,"z":0.2800000011920929}},"shadow":{"allowDynamicEntities":false,"allowTerrain":true,"bias":0.800000011920929,"cascadeBlendDistance":2.5,"center":{"x":0.0,"y":0.0,"z":0.0},"distance":75.0,"edgeSoftness":0.05000000074505806,"followMainCamera":false,"isCascaded":false,"longRange":false,"longRangeDistance":1024.0,"resolution":2048,"splitCorrection":0.0,"useCubemap":false,"viewCount":1,"views":[{"farDistance":75.0,"frustumMatrix":{"j0":{"w":0.0,"x":0.019999999552965164,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-0.02380952425301075,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":-0.0002500000118743628},"j3":{"w":1.0,"x":0.0,"y":0.02380952425301075,"z":0.5}},"nearDistance":0.0,"orthoSize":{"w":43.0,"x":-50.0,"y":50.0,"z":-41.0},"projectionMatrix":{"j0":{"w":0.0,"x":0.019999999552965164,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-0.02380952425301075,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":-0.0002500000118743628},"j3":{"w":1.0,"x":0.0,"y":0.02380952425301075,"z":0.5}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":0.019999999552965164,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-0.02380952425301075,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":-0.0002500000118743628},"j3":{"w":1.0,"x":0.0,"y":0.02380952425301075,"z":0.5}},"viewMatrix":{"j0":{"w":0.0,"x":-1.0,"y":0.0,"z":-0.0},"j1":{"w":0.0,"x":0.0,"y":0.26962992548942566,"z":0.9629640579223633},"j2":{"w":0.0,"x":0.0,"y":0.9629640579223633,"z":-0.26962992548942566},"j3":{"w":1.0,"x":-0.0,"y":-0.0,"z":0.0}}}]},"type":0,"volumetric":true},"name":{"name":"Directional light"}},{"id":2,"mesh":{"dontCull":false,"resourcePath":"sponza/meshes/sponza.aemesh","visible":true},"name":{"name":"Sponza"},"rigidBody":{"bodyIndex":0,"creationSettings":{"angularDampening":0.0,"linearDampening":0.0,"shape":{"meshSettings":{"resourcePath":"sponza/meshes/sponza.aemesh","scale":{"x":0.012193998321890831,"y":0.012000000104308128,"z":0.012193998321890831}}}},"layer":0},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.012193998321890831,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":0.012000000104308128,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":0.012193998321890831},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}}}},{"id":3,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":1,"creationSettings":{"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":1.000000238418579,"y":1.000000238418579,"z":1.000000238418579}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.4756948947906494,"y":0.00011953715875279158,"z":-0.8796106576919556},"j1":{"w":0.0,"x":0.5917145013809204,"y":-0.739871621131897,"z":-0.32010072469711304},"j2":{"w":0.0,"x":-0.6508373022079468,"y":-0.6727486252784729,"z":0.3518824875354767},"j3":{"w":1.0,"x":-15.995047569274902,"y":0.9694054126739502,"z":0.2113814800977707}}}},{"id":4,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Wall"},"rigidBody":{"bodyIndex":2,"creationSettings":{"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":1.0,"scale":{"x":0.1481112539768219,"y":0.17914418876171112,"z":2.874448299407959}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.12404608726501465,"y":-7.875383403188607e-07,"z":0.08092907071113586},"j1":{"w":0.0,"x":-6.167340416141087e-07,"y":0.17914418876171112,"z":2.688605945877498e-06},"j2":{"w":0.0,"x":-1.5706194639205933,"y":-4.1537619836162776e-05,"z":2.407406806945801},"j3":{"w":1.0,"x":7.4162163734436035,"y":-0.04500603675842285,"z":-0.9572893381118774}}}},{"id":5,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":3,"creationSettings":{"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":0.9999986290931702,"y":0.9999986290931702,"z":0.9999986290931702}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.30071619153022766,"y":0.9535101652145386,"z":0.01963132992386818},"j1":{"w":0.0,"x":-0.02151578664779663,"y":-0.013796210289001465,"z":0.9996733665466309},"j2":{"w":0.0,"x":0.9534696936607361,"y":-0.3010404407978058,"z":0.016366761177778244},"j3":{"w":1.0,"x":4.233027935028076,"y":0.966492235660553,"z":1.2846850156784058}}}},{"id":6,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":4,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920523762703,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0475580133497715,"y":-8.188627589333919e-07,"z":0.06712339073419571},"j1":{"w":0.0,"x":-3.60343005922914e-06,"y":-0.1039920523762703,"z":1.2844517414123402e-06},"j2":{"w":0.0,"x":0.8159534335136414,"y":-3.541418118402362e-05,"z":-0.5781162977218628},"j3":{"w":1.0,"x":-9.601022720336914,"y":1.530970811843872,"z":-0.44825008511543274}}}},{"camera":{"aspectRatio":2.0,"exposure":1.0,"farPlane":400.0,"fieldOfView":55.20000076293945,"isMain":true,"location":{"x":0.0,"y":1.2000000476837158,"z":0.0},"nearPlane":1.0,"rotation":{"x":51.511505126953125,"y":0.15597067773342133},"thirdPerson":true,"thirdPersonDistance":3.0,"useEntityRotation":true,"useEntityTranslation":true},"id":7,"mesh":{"dontCull":false,"resourcePath":"meshes/capsule.aemesh","visible":true},"name":{"name":"Player"},"player":{"allowInput":true,"creationSettings":{"shape":{"capsuleShapeSettings":{"density":1.0,"height":1.2000000476837158,"radius":0.30000001192092896,"scale":{"x":1.0,"y":1.0,"z":1.0}}}},"fastVelocity":4.0,"jumpVelocity":4.0,"slowVelocity":1.600000023841858},"text":{"halfSize":{"x":0.5,"y":1.0},"outlineColor":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"outlineFactor":0.0,"position":{"x":0.0,"y":1.600000023841858,"z":0.0},"resourcePath":"font/roboto.ttf","rotation":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"text":"This is the player","textColor":{"w":1.0,"x":1.0,"y":1.0,"z":1.0},"textScale":0.07999999821186066},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":-10.923478126525879,"y":-0.030071211978793144,"z":-1.5454801321029663}}}},{"audio":{"cutoff":0.0010000000474974513,"falloffFactor":0.10000000149011612,"falloffPower":2.0,"permanentPlay":false,"stream":{"loop":false,"pitch":1.0,"resourcePath":"/music.wav","time":0.0,"volume":0.0},"volume":1.0},"id":8,"name":{"name":"WelcomeText"},"text":{"halfSize":{"x":1.899999976158142,"y":1.0},"outlineColor":{"w":1.0,"x":1.0,"y":0.0,"z":0.0},"outlineFactor":0.15000000596046448,"position":{"x":0.0,"y":0.0,"z":1.2999999523162842},"resourcePath":"font/roboto.ttf","rotation":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"text":"Welcome to this demo scene!","textColor":{"w":1.0,"x":1.0,"y":1.0,"z":1.0},"textScale":0.1899999976158142},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.01578390598297119,"y":0.00355260306969285,"z":0.9998670816421509},"j1":{"w":0.0,"x":0.40691956877708435,"y":0.9134560227394104,"z":0.003178075887262821},"j2":{"w":0.0,"x":-0.913324773311615,"y":0.4069172441959381,"z":-0.015863558277487755},"j3":{"w":1.0,"x":2.9802322387695313e-08,"y":4.168000221252441,"z":-0.02871951460838318}}}},{"entities":[{"id":10,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":5,"creationSettings":{"angularVelocity":{"x":0.3400594890117645,"y":0.002075438853353262,"z":-0.2711464762687683},"linearVelocity":{"x":0.027797559276223183,"y":-0.06926704198122025,"z":0.03678030148148537},"motionQuality":1,"objectLayer":1,"restitution":-0.0,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226305991411209,"y":0.10399112105369568,"z":0.24259740114212036}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.06749320030212402,"y":-0.0013515298487618566,"z":0.04701119661331177},"j1":{"w":0.0,"x":-0.05943548306822777,"y":-5.547449109144509e-05,"z":-0.08533213287591934},"j2":{"w":0.0,"x":0.003344531636685133,"y":-0.2425646185874939,"z":-0.0021718384232372046},"j3":{"w":1.0,"x":-6.993166923522949,"y":0.07752954214811325,"z":-0.49586528539657593}}}},{"id":11,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":6,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226487040519714,"y":0.10399449616670609,"z":1.000009298324585}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.001214503077790141,"y":4.0089940739562735e-05,"z":0.08225589245557785},"j1":{"w":0.0,"x":-0.10398011654615402,"y":-0.000795417174231261,"z":0.0015356455696746707},"j2":{"w":0.0,"x":0.0076551553793251514,"y":-0.9999799728393555,"z":0.00037435046397149563},"j3":{"w":1.0,"x":-0.18109244108200073,"y":1.1424553394317627,"z":-2.4032680988311768}}}},{"id":12,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":7,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399213433265686,"z":0.9999992847442627}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.004975396674126387,"y":-3.6774740692635532e-06,"z":0.0821131020784378},"j1":{"w":0.0,"x":-0.10380175709724426,"y":-6.8306521825434174e-06,"z":-0.006289551965892315},"j2":{"w":0.0,"x":6.824726733611897e-05,"y":-0.9999992847442627,"z":-4.065033863298595e-05},"j3":{"w":1.0,"x":0.49817052483558655,"y":0.3519432246685028,"z":-2.3503992557525635}}}},{"id":13,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":8,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226367831230164,"y":0.10399220138788223,"z":0.9999990463256836}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.006919844541698694,"y":0.0009341590339317918,"z":0.08196679502725601},"j1":{"w":0.0,"x":-0.10362283140420914,"y":-0.00030928864725865424,"z":0.008751623332500458},"j2":{"w":0.0,"x":0.003919091075658798,"y":-0.9999301433563232,"z":0.011065145023167133},"j3":{"w":1.0,"x":2.0737833976745605,"y":0.3635621964931488,"z":-1.7112125158309937}}}},{"id":14,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":9,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.10399205982685089,"z":0.9999991655349731}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.00037123600486665964,"y":-0.00020935118664056063,"z":0.08226258307695389},"j1":{"w":0.0,"x":-0.10399100184440613,"y":-7.686027743147861e-07,"z":0.000469290855107829},"j2":{"w":0.0,"x":-4.082914983882802e-06,"y":-0.9999959468841553,"z":-0.0025448778178542852},"j3":{"w":1.0,"x":3.7019379138946533,"y":0.35346531867980957,"z":-2.4043333530426025}}}},{"id":15,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":10,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.0822637677192688,"y":0.10399221628904343,"z":1.0000003576278687}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0014320656191557646,"y":-0.005514409858733416,"z":0.08206624537706375},"j1":{"w":0.0,"x":-0.1039608046412468,"y":-0.0019218007801100612,"z":0.0016850243555381894},"j2":{"w":0.0,"x":0.01734951324760914,"y":-0.9975799322128296,"z":-0.06733495742082596},"j3":{"w":1.0,"x":1.3664464950561523,"y":1.2094060182571411,"z":-2.3293323516845703}}}},{"id":16,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":11,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920523762703,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.007250844966620207,"y":-0.00029188839835114777,"z":0.08194299042224884},"j1":{"w":0.0,"x":-0.10357026010751724,"y":0.0019194179913029075,"z":-0.009157725609838963},"j2":{"w":0.0,"x":-0.01807291805744171,"y":-0.9998225569725037,"z":-0.005160685162991285},"j3":{"w":1.0,"x":3.177853584289551,"y":1.137428641319275,"z":-2.286043643951416}}}},{"id":17,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":12,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399207472801208,"z":0.9999994039535522}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.007074753753840923,"y":-0.0009124425705522299,"z":0.08195383846759796},"j1":{"w":0.0,"x":-0.10360674560070038,"y":2.178741397074191e-06,"z":-0.008943937718868256},"j2":{"w":0.0,"x":0.0009330803295597434,"y":-0.999937891960144,"z":-0.01105236355215311},"j3":{"w":1.0,"x":4.739034652709961,"y":1.1431937217712402,"z":-2.344177484512329}}}},{"id":18,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":13,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399207472801208,"z":0.9999992847442627}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-2.9814507797709666e-05,"y":-0.00011468570301076397,"z":0.08226361125707626},"j1":{"w":0.0,"x":-0.1039920523762703,"y":-5.103151488583535e-05,"z":-3.7763817090308294e-05},"j2":{"w":0.0,"x":0.0004912313306704164,"y":-0.9999982118606567,"z":-0.0013939131749793887},"j3":{"w":1.0,"x":5.269709587097168,"y":0.3537576496601105,"z":-2.396353006362915}}}},{"id":19,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":14,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226397633552551,"y":0.10399535298347473,"z":1.0000017881393433}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0029943662229925394,"y":-0.004902660381048918,"z":0.08206314593553543},"j1":{"w":0.0,"x":-0.1038932278752327,"y":-0.00239846995100379,"z":-0.0039341989904642105},"j2":{"w":0.0,"x":0.025261566042900085,"y":-0.9979578852653503,"z":-0.05869881808757782},"j3":{"w":1.0,"x":0.4539576470851898,"y":1.9912066459655762,"z":-2.3605332374572754}}}},{"id":20,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":15,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399205982685089,"z":0.9999991059303284}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0047220797277987,"y":-0.006254853215068579,"z":0.08188952505588531},"j1":{"w":0.0,"x":-0.10381996631622314,"y":0.000815314007923007,"z":-0.005924399942159653},"j2":{"w":0.0,"x":-0.0034728916361927986,"y":-0.9970735311508179,"z":-0.0763583704829216},"j3":{"w":1.0,"x":2.0099921226501465,"y":1.9823397397994995,"z":-2.1112253665924072}}}},{"id":21,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":16,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399207472801208,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0015184117946773767,"y":-0.001873633824288845,"z":0.08222834020853043},"j1":{"w":0.0,"x":-0.10390593856573105,"y":0.0038146909791976213,"z":-0.0018317853100597858},"j2":{"w":0.0,"x":-0.036265525966882706,"y":-0.9990666508674622,"z":-0.02343420498073101},"j3":{"w":1.0,"x":3.611302614212036,"y":1.9326788187026978,"z":-2.369572162628174}}}},{"id":22,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":17,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370811462402,"y":0.10399207472801208,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0011607652995735407,"y":-0.005252636969089508,"z":0.08208763599395752},"j1":{"w":0.0,"x":-0.1039327085018158,"y":-0.003092399565503001,"z":-0.0016675429651513696},"j2":{"w":0.0,"x":0.03069702349603176,"y":-0.9975154995918274,"z":-0.06339509040117264},"j3":{"w":1.0,"x":0.911769449710846,"y":2.8139381408691406,"z":-2.3714351654052734}}}},{"id":23,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":18,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399208217859268,"z":0.9999986886978149}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.002328230533748865,"y":-0.01567736454308033,"z":0.08072245121002197},"j1":{"w":0.0,"x":-0.10387608408927917,"y":0.0032992407213896513,"z":0.0036367876455187798},"j2":{"w":0.0,"x":-0.03779618442058563,"y":-0.9811586737632751,"z":-0.189463809132576},"j3":{"w":1.0,"x":2.5983128547668457,"y":2.8227591514587402,"z":-2.0519001483917236}}}},{"id":24,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":19,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920449256897,"z":0.9999991655349731}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.006420230958610773,"y":-0.0025190962478518486,"z":0.0819740742444992},"j1":{"w":0.0,"x":-0.10356941819190979,"y":0.004437707830220461,"z":0.008247951045632362},"j2":{"w":0.0,"x":-0.04495199769735336,"y":-0.9986188411712646,"z":-0.027167268097400665},"j3":{"w":1.0,"x":4.155186176300049,"y":2.7027957439422607,"z":-2.4178249835968018}}}},{"id":25,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":20,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399205982685089,"z":0.9999993443489075}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.007614439819008112,"y":-0.014711390249431133,"z":0.08057859539985657},"j1":{"w":0.0,"x":-0.1032203733921051,"y":-0.006378653459250927,"z":-0.010918588377535343},"j2":{"w":0.0,"x":0.07885781675577164,"y":-0.9819651246070862,"z":-0.17182737588882446},"j3":{"w":1.0,"x":1.3723328113555908,"y":3.7512500286102295,"z":-2.243227481842041}}}},{"id":26,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":21,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399207472801208,"z":0.9999992847442627}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.005531982518732548,"y":-0.015606719069182873,"z":0.08058004826307297},"j1":{"w":0.0,"x":-0.10365156084299088,"y":0.005922866519540548,"z":-0.005968747194856405},"j2":{"w":0.0,"x":-0.0449003241956234,"y":-0.9801850318908691,"z":-0.19292448461055756},"j3":{"w":1.0,"x":3.1063742637634277,"y":3.642221689224243,"z":-2.205120325088501}}}},{"id":27,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":22,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399164259433746,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.009233793243765831,"y":-2.8461645484867404e-08,"z":-0.08174382150173187},"j1":{"w":0.0,"x":5.111483005748596e-07,"y":-0.10399164259433746,"z":-2.153150546746474e-08},"j2":{"w":0.0,"x":-0.9936796426773071,"y":-4.907456968794577e-06,"z":0.11224624514579773},"j3":{"w":1.0,"x":5.397507667541504,"y":1.5309886932373047,"z":-0.8703097105026245}}}},{"id":28,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":23,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.1039920523762703,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.004304870497435331,"y":-0.016259904950857162,"z":0.08052577078342438},"j1":{"w":0.0,"x":-0.10374186187982559,"y":0.0035653808154165745,"z":0.006265922449529171},"j2":{"w":0.0,"x":-0.04547032341361046,"y":-0.9796709418296814,"z":-0.19538608193397522},"j3":{"w":1.0,"x":3.7071003913879395,"y":4.404808521270752,"z":-2.0641326904296875}}}},{"id":29,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":24,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226374536752701,"y":0.1039920300245285,"z":1.0000001192092896}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.004521988797932863,"y":-0.014796562492847443,"z":0.08079565316438675},"j1":{"w":0.0,"x":-0.10348106175661087,"y":-0.007410035468637943,"z":-0.0071486919187009335},"j2":{"w":0.0,"x":0.0823487713932991,"y":-0.9811068177223206,"z":-0.17506666481494904},"j3":{"w":1.0,"x":1.740257740020752,"y":4.569649696350098,"z":-2.0765440464019775}}}},{"id":30,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":25,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226379007101059,"y":0.1486254185438156,"z":0.9800000190734863}}}},"layer":1},"script":{"permanentExecution":false,"resourcePath":"scripts/example.lua","scriptProperties":{"duplicateEntityName":{"type":"string","value":""}}},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0822434350848198,"y":-3.559059393865027e-07,"z":-0.0018298403592780232},"j1":{"w":0.0,"x":6.938781211829337e-07,"y":0.1486254185438156,"z":2.2790266029915074e-06},"j2":{"w":0.0,"x":0.021798700094223022,"y":-1.5125399841053877e-05,"z":0.97975754737854},"j3":{"w":1.0,"x":-0.42584455013275146,"y":-0.042439818382263184,"z":0.7894344925880432}}}},{"id":31,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":26,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226378262042999,"y":0.015076023526489735,"z":0.9800000190734863}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.07320480048656464,"y":4.977956632501446e-05,"z":-0.03752845898270607},"j1":{"w":0.0,"x":0.006859811022877693,"y":-0.0011024783598259091,"z":0.013379612006247044},"j2":{"w":0.0,"x":-0.032167207449674606,"y":-0.9773759245872498,"z":-0.0640433058142662},"j3":{"w":1.0,"x":2.4300098419189453,"y":0.3631786108016968,"z":1.207587718963623}}}},{"id":32,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":27,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226379007101059,"y":0.1486254185438156,"z":0.9800000190734863}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.06837532669305801,"y":-0.0017373006558045745,"z":-0.04570697247982025},"j1":{"w":0.0,"x":0.05685741454362869,"y":0.11100539565086365,"z":0.08083657920360565},"j2":{"w":0.0,"x":0.39542150497436523,"y":-0.6513306498527527,"z":0.6162874698638916},"j3":{"w":1.0,"x":3.135746955871582,"y":0.2095150351524353,"z":-1.027677297592163}}}},{"id":33,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":28,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226379007101059,"y":0.1486254185438156,"z":0.9800000786781311}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0716107115149498,"y":-4.5638077494913887e-07,"z":-0.040487490594387054},"j1":{"w":0.0,"x":0.0005247447988949716,"y":0.14862160384655,"z":0.0009264472755603492},"j2":{"w":0.0,"x":0.4823108911514282,"y":-0.007020606193691492,"z":0.8530691862106323},"j3":{"w":1.0,"x":2.5021331310272217,"y":-0.04527640342712402,"z":0.6787291169166565}}}}],"id":9,"name":{"name":"Staircase"},"root":false},{"id":34,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":29,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920523762703,"z":0.9999991655349731}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-3.9226383705681656e-06,"y":0.0,"z":0.08226368576288223},"j1":{"w":0.0,"x":-0.1039920523762703,"y":-2.479363736540563e-08,"z":-4.958727458870271e-06},"j2":{"w":0.0,"x":2.384183801495965e-07,"y":-0.9999991655349731,"z":0.0},"j3":{"w":1.0,"x":3.1176745891571045,"y":5.388330459594727,"z":-4.729436874389648}}}},{"entities":[{"entities":[{"id":37,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":32,"creationSettings":{"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":1.0000007152557373,"y":1.0000007152557373,"z":1.0000007152557373}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.9554479122161865,"y":0.034463346004486084,"z":0.29313671588897705},"j1":{"w":0.0,"x":-0.09065454453229904,"y":0.9794178605079651,"z":0.18033018708229065},"j2":{"w":0.0,"x":-0.28088951110839844,"y":-0.19888851046562195,"z":0.9386930465698242},"j3":{"w":0.999999463558197,"x":-1.679502010345459,"y":-0.44969987869262695,"z":6.6491780281066895}}}}],"id":36,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":31,"creationSettings":{"angularVelocity":{"x":-0.003580757649615407,"y":-0.0010163785191252828,"z":-0.001152479788288474},"linearVelocity":{"x":0.0011589848436415195,"y":-0.276141881942749,"z":-0.003600968746468425},"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":1.0000016689300537,"y":1.0000016689300537,"z":1.0000016689300537}}}},"layer":1},"root":false,"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.7511749267578125,"y":0.22571837902069092,"z":-0.6203151345252991},"j1":{"w":0.0,"x":-0.39222919940948486,"y":0.6032152771949768,"z":0.6944692730903625},"j2":{"w":0.0,"x":0.5309374332427979,"y":0.7649720907211304,"z":-0.3645859360694885},"j3":{"w":1.0,"x":-11.712557792663574,"y":4.754024505615234,"z":-4.003259658813477}}}}],"id":35,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":30,"creationSettings":{"angularVelocity":{"x":0.16539807617664337,"y":0.1814352571964264,"z":-0.11671467125415802},"linearVelocity":{"x":0.11671468615531921,"y":2.4347741600649897e-09,"z":0.16539809107780457},"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":1.0,"y":1.0,"z":1.0}}}},"layer":1},"root":false,"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.6672989130020142,"y":0.0363946259021759,"z":0.7439002990722656},"j1":{"w":0.0,"x":0.5453938245773315,"y":0.7040697336196899,"z":0.45478731393814087},"j2":{"w":0.0,"x":-0.5072058439254761,"y":0.7091977000236511,"z":-0.48967432975769043},"j3":{"w":1.0,"x":-12.277113914489746,"y":0.9699291586875916,"z":-0.5359781384468079}}}},{"id":38,"name":{"name":"Entity 39"},"script":{"permanentExecution":false,"resourcePath":"scripts/entitySpawner.lua","scriptProperties":{"spawnEntityName":{"type":"string","value":"Sphere"},"spawnRate":{"type":"double","value":1.0}}},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":9.527641296386719,"z":0.0}}}},{"id":39,"name":{"name":"Entity 39"},"script":{"permanentExecution":false,"resourcePath":"scripts/entitySpawner.lua","scriptProperties":{"spawnEntityName":{"type":"string","value":"Sphere"},"spawnRate":{"type":"double","value":1.0}}},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":-5.397964000701904,"y":12.993012428283691,"z":0.0}}}}],"id":0,"name":{"name":"Root"},"root":false}],"fog":{"ambientFactor":0.009999999776482582,"density":0.0005000000237487257,"enable":true,"extinctionCoefficients":{"w":1.0,"x":0.9300000071525574,"y":0.9649999737739563,"z":1.0},"extinctionFactor":0.1599999964237213,"height":0.0,"heightFalloff":0.0284000001847744,"rayMarchStepCount":10,"rayMarching":true,"scatteringAnisotropy":-0.6000000238418579,"scatteringFactor":2.0,"volumetricIntensity":1.0},"irradianceVolume":{"aabb":{"max":{"x":38.237998962402344,"y":18.340999603271484,"z":31.85099983215332},"min":{"x":-38.63600158691406,"y":-9.817999839782715,"z":-21.163000106811523}},"bias":0.30000001192092896,"cascadeCount":1,"enable":true,"gamma":5.0,"hysteresis":0.9800000190734863,"lowerResMoments":true,"opacityCheck":false,"optimizeProbes":true,"probeCount":{"x":20,"y":20,"z":20},"rayCount":100,"rayCountInactive":32,"sampleEmissives":false,"scroll":false,"sharpness":50.0,"splitCorrection":2.0,"strength":1.0,"useShadowMap":false},"name":"sponza","physicsWorld":null,"postProcessing":{"chromaticAberration":{"colorsReversed":false,"enable":false,"strength":1.0},"contrast":1.0,"filmGrain":{"enable":false,"strength":0.10000000149011612},"filmicTonemapping":false,"fsr2":true,"paperWhiteLuminance":150.0,"saturation":1.0,"screenMaxLuminance":1600.0,"sharpen":{"enable":true,"factor":0.15000000596046448},"taa":{"enable":true,"jitterRange":0.9900000095367432},"tint":{"x":1.0,"y":1.0,"z":1.0},"vignette":{"color":{"x":0.0,"y":0.0,"z":0.0},"enable":false,"offset":0.0,"power":0.0,"strength":0.0}},"reflection":{"bias":0.0,"currentClipFactor":2.0,"ddgi":true,"enable":true,"halfResolution":true,"historyClipMax":1.0,"opacityCheck":false,"radianceLimit":10.0,"roughnessCutoff":0.8999999761581421,"rt":true,"spatialFilterStrength":5.234000205993652,"temporalWeight":0.9520000219345093,"textureLevel":4,"useNormalMaps":true,"useShadowMap":false},"sky":{"atmosphere":{"height":100000.0,"mieHeightScale":1200.0,"mieScatteringCoeff":2.099999983329326e-05,"probeResolution":128,"rayleighHeightScale":8000.0,"rayleighScatteringCoeff":{"x":5.500000042957254e-06,"y":1.2999999853491317e-05,"z":2.2399999579647556e-05}},"clouds":{"castShadow":false,"coverageResolution":512,"coverageScale":0.25,"coverageSpeed":5.0,"darkEdgeAmbient":0.10000000149011612,"darkEdgeFocus":2.0,"densityMultiplier":0.800000011920929,"detailResolution":32,"detailScale":16.0,"detailSpeed":10.0,"detailStrength":0.15000000596046448,"distanceLimit":8000.0,"enable":true,"halfResolution":true,"heightStretch":0.5,"maxHeight":1700.0,"minHeight":1400.0,"occlusionSampleCount":5,"sampleCount":64,"scattering":{"eccentricityFirstPhase":0.0,"eccentricitySecondPhase":-0.5,"extinctionCoefficients":{"w":1.0,"x":0.9300000071525574,"y":0.9649999737739563,"z":1.0},"extinctionFactor":0.1599999964237213,"phaseAlpha":0.5,"scatteringFactor":2.0},"shadowResolution":512,"shadowSampleFraction":4,"shapeResolution":128,"shapeScale":2.0,"shapeSpeed":5.0,"stochasticOcclusionSampling":true},"intensity":1.0,"planetCenter":{"x":0.0,"y":-650000.0,"z":0.0},"planetRadius":649000.0},"ssgi":{"aoStrength":1.0,"enable":true,"enableAo":true,"halfResolution":true,"irradianceLimit":10.0,"radius":1.0,"rayCount":2,"sampleCount":8},"sss":{"enable":true,"maxLength":0.4970000088214874,"sampleCount":8,"thickness":0.30000001192092896},"wind":{"direction":{"x":1.0,"y":1.0},"speed":10.0}} \ No newline at end of file +{"aabb":{"max":{"x":128.0,"y":128.0,"z":128.0},"min":{"x":-128.0,"y":-128.0,"z":-128.0}},"ao":{"enable":false,"halfResolution":true,"opacityCheck":false,"radius":3.0,"rt":true,"sampleCount":16,"strength":1.0},"depth":5.0,"entities":[{"entities":[{"id":1,"light":{"color":{"x":1.0,"y":0.9254902005195618,"z":0.8196078538894653},"intensity":10.0,"isMain":true,"mobility":1,"properties":{"direction":{"x":0.0,"y":-1.0,"z":0.2800000011920929}},"shadow":{"allowDynamicEntities":false,"allowTerrain":true,"bias":0.800000011920929,"cascadeBlendDistance":2.5,"center":{"x":0.0,"y":0.0,"z":0.0},"distance":75.0,"edgeSoftness":0.05000000074505806,"followMainCamera":false,"isCascaded":false,"longRange":false,"longRangeDistance":1024.0,"resolution":2048,"splitCorrection":0.8999999761581421,"useCubemap":false,"viewCount":1,"views":[{"farDistance":75.0,"frustumMatrix":{"j0":{"w":0.0,"x":0.019999999552965164,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-0.02380952425301075,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":-0.0002500000118743628},"j3":{"w":1.0,"x":0.0,"y":0.02380952425301075,"z":0.5}},"nearDistance":0.0,"orthoSize":{"w":43.0,"x":-50.0,"y":50.0,"z":-41.0},"projectionMatrix":{"j0":{"w":0.0,"x":0.019999999552965164,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-0.02380952425301075,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":-0.0002500000118743628},"j3":{"w":1.0,"x":0.0,"y":0.02380952425301075,"z":0.5}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":0.019999999552965164,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-0.02380952425301075,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":-0.0002500000118743628},"j3":{"w":1.0,"x":0.0,"y":0.02380952425301075,"z":0.5}},"viewMatrix":{"j0":{"w":0.0,"x":-1.0,"y":0.0,"z":-0.0},"j1":{"w":0.0,"x":0.0,"y":0.26962992548942566,"z":0.9629640579223633},"j2":{"w":0.0,"x":0.0,"y":0.9629640579223633,"z":-0.26962992548942566},"j3":{"w":1.0,"x":-0.0,"y":-0.0,"z":0.0}}}]},"type":0,"volumetric":true},"name":{"name":"Directional light"}},{"id":2,"mesh":{"dontCull":false,"resourcePath":"sponza/meshes/sponza.aemesh","visible":true},"name":{"name":"Sponza"},"rigidBody":{"bodyIndex":0,"creationSettings":{"angularDampening":0.0,"linearDampening":0.0,"shape":{"meshSettings":{"resourcePath":"sponza/meshes/sponza.aemesh","scale":{"x":0.012193998321890831,"y":0.012000000104308128,"z":0.012193998321890831}}}},"layer":0},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.012193998321890831,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":0.012000000104308128,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":0.012193998321890831},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}}}},{"id":3,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":1,"creationSettings":{"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":0.09999998658895493,"y":0.09999998658895493,"z":0.09999998658895493}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.047569502145051956,"y":1.195073127746582e-05,"z":-0.08796101808547974},"j1":{"w":0.0,"x":0.05917143449187279,"y":-0.07398712635040283,"z":-0.03201008588075638},"j2":{"w":0.0,"x":-0.06508366018533707,"y":-0.06727483868598938,"z":0.03518824651837349},"j3":{"w":1.0,"x":-15.995047569274902,"y":0.9694054126739502,"z":0.2113814800977707}}}},{"id":4,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Wall"},"rigidBody":{"bodyIndex":2,"creationSettings":{"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":1.0,"scale":{"x":0.1481112539768219,"y":0.17914418876171112,"z":2.874448299407959}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.12404608726501465,"y":-7.875383403188607e-07,"z":0.08092907071113586},"j1":{"w":0.0,"x":-6.167340416141087e-07,"y":0.17914418876171112,"z":2.688605945877498e-06},"j2":{"w":0.0,"x":-1.5706194639205933,"y":-4.1537619836162776e-05,"z":2.407406806945801},"j3":{"w":1.0,"x":7.4162163734436035,"y":-0.04500603675842285,"z":-0.9572893381118774}}}},{"id":5,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":3,"creationSettings":{"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":0.9999986290931702,"y":0.9999986290931702,"z":0.9999986290931702}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.30071619153022766,"y":0.9535101652145386,"z":0.01963132992386818},"j1":{"w":0.0,"x":-0.02151578664779663,"y":-0.013796210289001465,"z":0.9996733665466309},"j2":{"w":0.0,"x":0.9534696936607361,"y":-0.3010404407978058,"z":0.016366761177778244},"j3":{"w":1.0,"x":4.233027935028076,"y":0.966492235660553,"z":1.2846850156784058}}}},{"camera":{"aspectRatio":2.0,"exposure":1.0,"farPlane":400.0,"fieldOfView":55.20000076293945,"isMain":true,"location":{"x":0.0,"y":1.2000000476837158,"z":0.0},"nearPlane":1.0,"rotation":{"x":51.511505126953125,"y":0.15597067773342133},"thirdPerson":true,"thirdPersonDistance":3.0,"useEntityRotation":true,"useEntityTranslation":true},"id":6,"mesh":{"dontCull":false,"resourcePath":"meshes/capsule.aemesh","visible":true},"name":{"name":"Player"},"player":{"allowInput":true,"creationSettings":{"shape":{"capsuleShapeSettings":{"density":1.0,"height":1.2000000476837158,"radius":0.30000001192092896,"scale":{"x":1.0,"y":1.0,"z":1.0}}}},"fastVelocity":4.0,"jumpVelocity":4.0,"slowVelocity":1.600000023841858},"text":{"halfSize":{"x":0.5,"y":1.0},"outlineColor":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"outlineFactor":0.0,"position":{"x":0.0,"y":1.600000023841858,"z":0.0},"resourcePath":"font/roboto.ttf","rotation":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"text":"This is the player","textColor":{"w":1.0,"x":1.0,"y":1.0,"z":1.0},"textScale":0.07999999821186066},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":-10.923478126525879,"y":-0.030071211978793144,"z":-1.5454801321029663}}}},{"audio":{"cutoff":0.0010000000474974513,"falloffFactor":0.10000000149011612,"falloffPower":2.0,"permanentPlay":false,"stream":{"loop":false,"pitch":1.0,"resourcePath":"/music.wav","time":0.0,"volume":0.0},"volume":1.0},"id":7,"name":{"name":"WelcomeText"},"text":{"halfSize":{"x":1.899999976158142,"y":1.0},"outlineColor":{"w":1.0,"x":1.0,"y":0.0,"z":0.0},"outlineFactor":0.15000000596046448,"position":{"x":0.0,"y":0.0,"z":1.2999999523162842},"resourcePath":"font/roboto.ttf","rotation":{"w":1.0,"x":0.0,"y":0.0,"z":0.0},"text":"Welcome to this demo scene!","textColor":{"w":1.0,"x":1.0,"y":1.0,"z":1.0},"textScale":0.1899999976158142},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.01578390598297119,"y":0.00355260306969285,"z":0.9998670816421509},"j1":{"w":0.0,"x":0.40691956877708435,"y":0.9134560227394104,"z":0.003178075887262821},"j2":{"w":0.0,"x":-0.913324773311615,"y":0.4069172441959381,"z":-0.015863558277487755},"j3":{"w":1.0,"x":2.9802322387695313e-08,"y":4.168000221252441,"z":-0.02871951460838318}}}},{"entities":[{"id":9,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":4,"creationSettings":{"angularVelocity":{"x":0.3400594890117645,"y":0.002075438853353262,"z":-0.2711464762687683},"linearVelocity":{"x":0.027797559276223183,"y":-0.06926704198122025,"z":0.03678030148148537},"motionQuality":1,"objectLayer":1,"restitution":-0.0,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226305991411209,"y":0.10399112105369568,"z":0.24259740114212036}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.06749320030212402,"y":-0.0013515298487618566,"z":0.04701119661331177},"j1":{"w":0.0,"x":-0.05943548306822777,"y":-5.547449109144509e-05,"z":-0.08533213287591934},"j2":{"w":0.0,"x":0.003344531636685133,"y":-0.2425646185874939,"z":-0.0021718384232372046},"j3":{"w":1.0,"x":-6.993166923522949,"y":0.07752954214811325,"z":-0.49586528539657593}}}},{"id":10,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":5,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226487040519714,"y":0.10399449616670609,"z":1.000009298324585}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.001214503077790141,"y":4.0089940739562735e-05,"z":0.08225589245557785},"j1":{"w":0.0,"x":-0.10398011654615402,"y":-0.000795417174231261,"z":0.0015356455696746707},"j2":{"w":0.0,"x":0.0076551553793251514,"y":-0.9999799728393555,"z":0.00037435046397149563},"j3":{"w":1.0,"x":-0.18109244108200073,"y":1.1424553394317627,"z":-2.4032680988311768}}}},{"id":11,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":6,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399213433265686,"z":0.9999992847442627}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.004975396674126387,"y":-3.6774740692635532e-06,"z":0.0821131020784378},"j1":{"w":0.0,"x":-0.10380175709724426,"y":-6.8306521825434174e-06,"z":-0.006289551965892315},"j2":{"w":0.0,"x":6.824726733611897e-05,"y":-0.9999992847442627,"z":-4.065033863298595e-05},"j3":{"w":1.0,"x":0.49817052483558655,"y":0.3519432246685028,"z":-2.3503992557525635}}}},{"id":12,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":7,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226367831230164,"y":0.10399220138788223,"z":0.9999990463256836}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.006919844541698694,"y":0.0009341590339317918,"z":0.08196679502725601},"j1":{"w":0.0,"x":-0.10362283140420914,"y":-0.00030928864725865424,"z":0.008751623332500458},"j2":{"w":0.0,"x":0.003919091075658798,"y":-0.9999301433563232,"z":0.011065145023167133},"j3":{"w":1.0,"x":2.0737833976745605,"y":0.3635621964931488,"z":-1.7112125158309937}}}},{"id":13,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":8,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.10399205982685089,"z":0.9999991655349731}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.00037123600486665964,"y":-0.00020935118664056063,"z":0.08226258307695389},"j1":{"w":0.0,"x":-0.10399100184440613,"y":-7.686027743147861e-07,"z":0.000469290855107829},"j2":{"w":0.0,"x":-4.082914983882802e-06,"y":-0.9999959468841553,"z":-0.0025448778178542852},"j3":{"w":1.0,"x":3.7019379138946533,"y":0.35346531867980957,"z":-2.4043333530426025}}}},{"id":14,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":9,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.0822637677192688,"y":0.10399221628904343,"z":1.0000003576278687}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0014320656191557646,"y":-0.005514409858733416,"z":0.08206624537706375},"j1":{"w":0.0,"x":-0.1039608046412468,"y":-0.0019218007801100612,"z":0.0016850243555381894},"j2":{"w":0.0,"x":0.01734951324760914,"y":-0.9975799322128296,"z":-0.06733495742082596},"j3":{"w":1.0,"x":1.3664464950561523,"y":1.2094060182571411,"z":-2.3293323516845703}}}},{"id":15,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":10,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920523762703,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.007250844966620207,"y":-0.00029188839835114777,"z":0.08194299042224884},"j1":{"w":0.0,"x":-0.10357026010751724,"y":0.0019194179913029075,"z":-0.009157725609838963},"j2":{"w":0.0,"x":-0.01807291805744171,"y":-0.9998225569725037,"z":-0.005160685162991285},"j3":{"w":1.0,"x":3.177853584289551,"y":1.137428641319275,"z":-2.286043643951416}}}},{"id":16,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":11,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399207472801208,"z":0.9999994039535522}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.007074753753840923,"y":-0.0009124425705522299,"z":0.08195383846759796},"j1":{"w":0.0,"x":-0.10360674560070038,"y":2.178741397074191e-06,"z":-0.008943937718868256},"j2":{"w":0.0,"x":0.0009330803295597434,"y":-0.999937891960144,"z":-0.01105236355215311},"j3":{"w":1.0,"x":4.739034652709961,"y":1.1431937217712402,"z":-2.344177484512329}}}},{"id":17,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":12,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399207472801208,"z":0.9999992847442627}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-2.9814507797709666e-05,"y":-0.00011468570301076397,"z":0.08226361125707626},"j1":{"w":0.0,"x":-0.1039920523762703,"y":-5.103151488583535e-05,"z":-3.7763817090308294e-05},"j2":{"w":0.0,"x":0.0004912313306704164,"y":-0.9999982118606567,"z":-0.0013939131749793887},"j3":{"w":1.0,"x":5.269709587097168,"y":0.3537576496601105,"z":-2.396353006362915}}}},{"id":18,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":13,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226397633552551,"y":0.10399535298347473,"z":1.0000017881393433}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0029943662229925394,"y":-0.004902660381048918,"z":0.08206314593553543},"j1":{"w":0.0,"x":-0.1038932278752327,"y":-0.00239846995100379,"z":-0.0039341989904642105},"j2":{"w":0.0,"x":0.025261566042900085,"y":-0.9979578852653503,"z":-0.05869881808757782},"j3":{"w":1.0,"x":0.4539576470851898,"y":1.9912066459655762,"z":-2.3605332374572754}}}},{"id":19,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":14,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399205982685089,"z":0.9999991059303284}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0047220797277987,"y":-0.006254853215068579,"z":0.08188952505588531},"j1":{"w":0.0,"x":-0.10381996631622314,"y":0.000815314007923007,"z":-0.005924399942159653},"j2":{"w":0.0,"x":-0.0034728916361927986,"y":-0.9970735311508179,"z":-0.0763583704829216},"j3":{"w":1.0,"x":2.0099921226501465,"y":1.9823397397994995,"z":-2.1112253665924072}}}},{"id":20,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":15,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399207472801208,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0015184117946773767,"y":-0.001873633824288845,"z":0.08222834020853043},"j1":{"w":0.0,"x":-0.10390593856573105,"y":0.0038146909791976213,"z":-0.0018317853100597858},"j2":{"w":0.0,"x":-0.036265525966882706,"y":-0.9990666508674622,"z":-0.02343420498073101},"j3":{"w":1.0,"x":3.611302614212036,"y":1.9326788187026978,"z":-2.369572162628174}}}},{"id":21,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":16,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370811462402,"y":0.10399207472801208,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.0011607652995735407,"y":-0.005252636969089508,"z":0.08208763599395752},"j1":{"w":0.0,"x":-0.1039327085018158,"y":-0.003092399565503001,"z":-0.0016675429651513696},"j2":{"w":0.0,"x":0.03069702349603176,"y":-0.9975154995918274,"z":-0.06339509040117264},"j3":{"w":1.0,"x":0.911769449710846,"y":2.8139381408691406,"z":-2.3714351654052734}}}},{"id":22,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":17,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399208217859268,"z":0.9999986886978149}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.002328230533748865,"y":-0.01567736454308033,"z":0.08072245121002197},"j1":{"w":0.0,"x":-0.10387608408927917,"y":0.0032992407213896513,"z":0.0036367876455187798},"j2":{"w":0.0,"x":-0.03779618442058563,"y":-0.9811586737632751,"z":-0.189463809132576},"j3":{"w":1.0,"x":2.5983128547668457,"y":2.8227591514587402,"z":-2.0519001483917236}}}},{"id":23,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":18,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920449256897,"z":0.9999991655349731}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.006420230958610773,"y":-0.0025190962478518486,"z":0.0819740742444992},"j1":{"w":0.0,"x":-0.10356941819190979,"y":0.004437707830220461,"z":0.008247951045632362},"j2":{"w":0.0,"x":-0.04495199769735336,"y":-0.9986188411712646,"z":-0.027167268097400665},"j3":{"w":1.0,"x":4.155186176300049,"y":2.7027957439422607,"z":-2.4178249835968018}}}},{"id":24,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":19,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399205982685089,"z":0.9999993443489075}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.007614439819008112,"y":-0.014711390249431133,"z":0.08057859539985657},"j1":{"w":0.0,"x":-0.1032203733921051,"y":-0.006378653459250927,"z":-0.010918588377535343},"j2":{"w":0.0,"x":0.07885781675577164,"y":-0.9819651246070862,"z":-0.17182737588882446},"j3":{"w":1.0,"x":1.3723328113555908,"y":3.7512500286102295,"z":-2.243227481842041}}}},{"id":25,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":20,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226370066404343,"y":0.10399207472801208,"z":0.9999992847442627}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.005531982518732548,"y":-0.015606719069182873,"z":0.08058004826307297},"j1":{"w":0.0,"x":-0.10365156084299088,"y":0.005922866519540548,"z":-0.005968747194856405},"j2":{"w":0.0,"x":-0.0449003241956234,"y":-0.9801850318908691,"z":-0.19292448461055756},"j3":{"w":1.0,"x":3.1063742637634277,"y":3.642221689224243,"z":-2.205120325088501}}}},{"id":26,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":21,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.10399164259433746,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.009233793243765831,"y":-2.8461645484867404e-08,"z":-0.08174382150173187},"j1":{"w":0.0,"x":5.111483005748596e-07,"y":-0.10399164259433746,"z":-2.153150546746474e-08},"j2":{"w":0.0,"x":-0.9936796426773071,"y":-4.907456968794577e-06,"z":0.11224624514579773},"j3":{"w":1.0,"x":5.397507667541504,"y":1.5309886932373047,"z":-0.8703097105026245}}}},{"id":27,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":22,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226369321346283,"y":0.1039920523762703,"z":0.9999992251396179}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.004304870497435331,"y":-0.016259904950857162,"z":0.08052577078342438},"j1":{"w":0.0,"x":-0.10374186187982559,"y":0.0035653808154165745,"z":0.006265922449529171},"j2":{"w":0.0,"x":-0.04547032341361046,"y":-0.9796709418296814,"z":-0.19538608193397522},"j3":{"w":1.0,"x":3.7071003913879395,"y":4.404808521270752,"z":-2.0641326904296875}}}},{"id":28,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":23,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226374536752701,"y":0.1039920300245285,"z":1.0000001192092896}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.004521988797932863,"y":-0.014796562492847443,"z":0.08079565316438675},"j1":{"w":0.0,"x":-0.10348106175661087,"y":-0.007410035468637943,"z":-0.0071486919187009335},"j2":{"w":0.0,"x":0.0823487713932991,"y":-0.9811068177223206,"z":-0.17506666481494904},"j3":{"w":1.0,"x":1.740257740020752,"y":4.569649696350098,"z":-2.0765440464019775}}}},{"id":29,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":24,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226379007101059,"y":0.1486254185438156,"z":0.9800000190734863}}}},"layer":1},"script":{"permanentExecution":false,"resourcePath":"scripts/example.lua","scriptProperties":{"duplicateEntityName":{"type":"string","value":""}}},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0822434350848198,"y":-3.559059393865027e-07,"z":-0.0018298403592780232},"j1":{"w":0.0,"x":6.938781211829337e-07,"y":0.1486254185438156,"z":2.2790266029915074e-06},"j2":{"w":0.0,"x":0.021798700094223022,"y":-1.5125399841053877e-05,"z":0.97975754737854},"j3":{"w":1.0,"x":-0.42584455013275146,"y":-0.042439818382263184,"z":0.7894344925880432}}}},{"id":30,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":25,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226378262042999,"y":0.015076023526489735,"z":0.9800000190734863}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.07320480048656464,"y":4.977956632501446e-05,"z":-0.03752845898270607},"j1":{"w":0.0,"x":0.006859811022877693,"y":-0.0011024783598259091,"z":0.013379612006247044},"j2":{"w":0.0,"x":-0.032167207449674606,"y":-0.9773759245872498,"z":-0.0640433058142662},"j3":{"w":1.0,"x":2.4300098419189453,"y":0.3631786108016968,"z":1.207587718963623}}}},{"id":31,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":26,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226379007101059,"y":0.1486254185438156,"z":0.9800000190734863}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.06837532669305801,"y":-0.0017373006558045745,"z":-0.04570697247982025},"j1":{"w":0.0,"x":0.05685741454362869,"y":0.11100539565086365,"z":0.08083657920360565},"j2":{"w":0.0,"x":0.39542150497436523,"y":-0.6513306498527527,"z":0.6162874698638916},"j3":{"w":1.0,"x":3.135746955871582,"y":0.2095150351524353,"z":-1.027677297592163}}}},{"id":32,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":27,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226379007101059,"y":0.1486254185438156,"z":0.9800000786781311}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.0716107115149498,"y":-4.5638077494913887e-07,"z":-0.040487490594387054},"j1":{"w":0.0,"x":0.0005247447988949716,"y":0.14862160384655,"z":0.0009264472755603492},"j2":{"w":0.0,"x":0.4823108911514282,"y":-0.007020606193691492,"z":0.8530691862106323},"j3":{"w":1.0,"x":2.5021331310272217,"y":-0.04527640342712402,"z":0.6787291169166565}}}}],"id":8,"name":{"name":"Staircase"},"root":false},{"id":33,"mesh":{"dontCull":false,"resourcePath":"meshes/metallicwall.aemesh","visible":true},"name":{"name":"Entity 7"},"rigidBody":{"bodyIndex":28,"creationSettings":{"motionQuality":1,"objectLayer":1,"shape":{"boundingBoxSettings":{"aabb":{"max":{"x":7.46401834487915,"y":15.011363983154297,"z":0.4019695222377777},"min":{"x":-7.46401834487915,"y":0.0833272933959961,"z":-0.4019695222377777}},"density":20.139999389648438,"scale":{"x":0.08226368576288223,"y":0.1039920523762703,"z":0.9999991655349731}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-3.9226383705681656e-06,"y":0.0,"z":0.08226368576288223},"j1":{"w":0.0,"x":-0.1039920523762703,"y":-2.479363736540563e-08,"z":-4.958727458870271e-06},"j2":{"w":0.0,"x":2.384183801495965e-07,"y":-0.9999991655349731,"z":0.0},"j3":{"w":1.0,"x":3.1176745891571045,"y":5.388330459594727,"z":-4.729436874389648}}}},{"entities":[{"entities":[{"id":36,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":31,"creationSettings":{"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":0.10000021010637283,"y":0.10000021010637283,"z":0.10000021010637283}}}},"layer":1},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":0.9554491639137268,"y":0.03446396440267563,"z":0.2931367754936218},"j1":{"w":0.0,"x":-0.09065470844507217,"y":0.9794172048568726,"z":0.18032999336719513},"j2":{"w":0.0,"x":-0.28088951110839844,"y":-0.19888849556446075,"z":0.9386932849884033},"j3":{"w":0.9999988675117493,"x":-1.6799697875976563,"y":-0.4502716064453125,"z":6.649372100830078}}}}],"id":35,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":30,"creationSettings":{"angularVelocity":{"x":-0.003580757649615407,"y":-0.0010163785191252828,"z":-0.001152479788288474},"linearVelocity":{"x":0.0011589848436415195,"y":-0.276141881942749,"z":-0.003600968746468425},"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":0.10000015050172806,"y":0.10000015050172806,"z":0.10000015050172806}}}},"layer":1},"root":false,"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.751175045967102,"y":0.22571831941604614,"z":-0.6203150749206543},"j1":{"w":0.0,"x":-0.39222919940948486,"y":0.6032153367996216,"z":0.6944690942764282},"j2":{"w":0.0,"x":0.5309373736381531,"y":0.7649720311164856,"z":-0.3645856976509094},"j3":{"w":1.0,"x":-11.712562561035156,"y":4.7540283203125,"z":-4.003265380859375}}}}],"id":34,"mesh":{"dontCull":false,"resourcePath":"meshes/chromesphere.aemesh","visible":true},"name":{"name":"Sphere"},"rigidBody":{"bodyIndex":29,"creationSettings":{"angularVelocity":{"x":0.16539807617664337,"y":0.1814352571964264,"z":-0.11671467125415802},"linearVelocity":{"x":0.11671468615531921,"y":2.4347741600649897e-09,"z":0.16539809107780457},"objectLayer":1,"restitution":0.8999999761581421,"shape":{"sphereSettings":{"density":1.0,"radius":1.0000001192092896,"scale":{"x":0.09999997913837433,"y":0.09999997913837433,"z":0.09999997913837433}}}},"layer":1},"root":false,"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":-0.06672985851764679,"y":0.0036394596099853516,"z":0.07439003139734268},"j1":{"w":0.0,"x":0.054539382457733154,"y":0.070406973361969,"z":0.045478712767362595},"j2":{"w":0.0,"x":-0.05072058364748955,"y":0.07091975212097168,"z":-0.0489673987030983},"j3":{"w":1.0,"x":-12.277113914489746,"y":0.9699291586875916,"z":-0.5359781384468079}}}},{"id":37,"name":{"name":"Entity 39"},"script":{"permanentExecution":false,"resourcePath":"scripts/entitySpawner.lua","scriptProperties":{"spawnEntityName":{"type":"string","value":"Sphere"},"spawnRate":{"type":"double","value":1.0}}},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":9.527641296386719,"z":0.0}}}},{"id":38,"name":{"name":"Entity 39"},"script":{"permanentExecution":false,"resourcePath":"scripts/entitySpawner.lua","scriptProperties":{"spawnEntityName":{"type":"string","value":"Sphere"},"spawnRate":{"type":"double","value":1.0}}},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":-5.397964000701904,"y":12.993012428283691,"z":0.0}}}},{"id":39,"light":{"color":{"x":1.0,"y":1.0,"z":1.0},"intensity":1.0,"isMain":false,"mobility":1,"properties":{"attenuation":1.0,"position":{"x":0.0,"y":0.0,"z":0.0},"radius":10.0},"shadow":{"allowDynamicEntities":false,"allowTerrain":false,"bias":0.4000000059604645,"cascadeBlendDistance":2.5,"center":{"x":0.0,"y":0.0,"z":0.0},"distance":0.0,"edgeSoftness":0.0,"followMainCamera":false,"isCascaded":false,"longRange":false,"longRangeDistance":1024.0,"resolution":1024,"splitCorrection":0.8999999761581421,"useCubemap":true,"viewCount":6,"views":[{"farDistance":0.0,"frustumMatrix":{"j0":{"w":1.0,"x":0.0,"y":0.0,"z":1.010101079940796},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":-1.0,"y":0.0,"z":0.0},"j3":{"w":0.4823928773403168,"x":-0.8340781927108765,"y":-0.8387702107429504,"z":0.3862554430961609}},"nearDistance":0.0,"orthoSize":{"w":0.0,"x":0.0,"y":0.0,"z":0.0},"projectionMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":0.0,"x":0.0,"y":0.0,"z":-0.10101010650396347}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}},"viewMatrix":{"j0":{"w":0.0,"x":0.0,"y":0.0,"z":-1.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":-0.0},"j2":{"w":0.0,"x":-1.0,"y":0.0,"z":-0.0},"j3":{"w":1.0,"x":-0.8340781927108765,"y":0.8387702107429504,"z":-0.4823928773403168}}},{"farDistance":0.0,"frustumMatrix":{"j0":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j3":{"w":-0.4823928773403168,"x":0.8340781927108765,"y":-0.8387702107429504,"z":-0.588275671005249}},"nearDistance":0.0,"orthoSize":{"w":0.0,"x":0.0,"y":0.0,"z":0.0},"projectionMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":0.0,"x":0.0,"y":0.0,"z":-0.10101010650396347}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}},"viewMatrix":{"j0":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":-0.0},"j2":{"w":0.0,"x":1.0,"y":0.0,"z":-0.0},"j3":{"w":1.0,"x":0.8340781927108765,"y":0.8387702107429504,"z":0.4823928773403168}}},{"farDistance":0.0,"frustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j2":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j3":{"w":0.8387702107429504,"x":0.4823928773403168,"y":0.8340781927108765,"z":0.7462326288223267}},"nearDistance":0.0,"orthoSize":{"w":0.0,"x":0.0,"y":0.0,"z":0.0},"projectionMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":0.0,"x":0.0,"y":0.0,"z":-0.10101010650396347}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}},"viewMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":-0.0},"j1":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j2":{"w":0.0,"x":0.0,"y":-1.0,"z":-0.0},"j3":{"w":1.0,"x":0.4823928773403168,"y":-0.8340781927108765,"z":-0.8387702107429504}}},{"farDistance":0.0,"frustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":1.0,"x":0.0,"y":0.0,"z":1.010101079940796},"j2":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j3":{"w":-0.8387702107429504,"x":0.4823928773403168,"y":-0.8340781927108765,"z":-0.94825279712677}},"nearDistance":0.0,"orthoSize":{"w":0.0,"x":0.0,"y":0.0,"z":0.0},"projectionMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":0.0,"x":0.0,"y":0.0,"z":-0.10101010650396347}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}},"viewMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":-0.0},"j1":{"w":0.0,"x":0.0,"y":0.0,"z":-1.0},"j2":{"w":0.0,"x":0.0,"y":1.0,"z":-0.0},"j3":{"w":1.0,"x":0.4823928773403168,"y":0.8340781927108765,"z":0.8387702107429504}}},{"farDistance":0.0,"frustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":1.0,"x":0.0,"y":0.0,"z":1.010101079940796},"j3":{"w":0.8340781927108765,"x":0.4823928773403168,"y":-0.8387702107429504,"z":0.7414932250976563}},"nearDistance":0.0,"orthoSize":{"w":0.0,"x":0.0,"y":0.0,"z":0.0},"projectionMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":0.0,"x":0.0,"y":0.0,"z":-0.10101010650396347}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}},"viewMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":-0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":-0.0},"j2":{"w":0.0,"x":-0.0,"y":0.0,"z":-1.0},"j3":{"w":1.0,"x":0.4823928773403168,"y":0.8387702107429504,"z":-0.8340781927108765}}},{"farDistance":0.0,"frustumMatrix":{"j0":{"w":0.0,"x":-1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":-0.8340781927108765,"x":-0.4823928773403168,"y":-0.8387702107429504,"z":-0.9435133934020996}},"nearDistance":0.0,"orthoSize":{"w":0.0,"x":0.0,"y":0.0,"z":0.0},"projectionMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":-1.0,"z":0.0},"j2":{"w":-1.0,"x":0.0,"y":0.0,"z":-1.010101079940796},"j3":{"w":0.0,"x":0.0,"y":0.0,"z":-0.10101010650396347}},"terrainFrustumMatrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":0.0,"y":0.0,"z":0.0}},"viewMatrix":{"j0":{"w":0.0,"x":-1.0,"y":0.0,"z":-0.0},"j1":{"w":0.0,"x":-0.0,"y":-1.0,"z":-0.0},"j2":{"w":0.0,"x":-0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":-0.4823928773403168,"y":0.8387702107429504,"z":0.8340781927108765}}}]},"type":1,"volumetric":true},"name":{"name":"Point light"},"transform":{"isStatic":false,"matrix":{"j0":{"w":0.0,"x":1.0,"y":0.0,"z":0.0},"j1":{"w":0.0,"x":0.0,"y":1.0,"z":0.0},"j2":{"w":0.0,"x":0.0,"y":0.0,"z":1.0},"j3":{"w":1.0,"x":-0.4823928773403168,"y":0.8387702107429504,"z":-0.8340781927108765}}}}],"id":0,"name":{"name":"Root"},"root":false}],"fog":{"ambientFactor":0.009999999776482582,"density":0.0005000000237487257,"enable":false,"extinctionCoefficients":{"w":1.0,"x":0.9300000071525574,"y":0.9649999737739563,"z":1.0},"extinctionFactor":0.1599999964237213,"height":0.0,"heightFalloff":0.0284000001847744,"rayMarchStepCount":10,"rayMarching":true,"scatteringAnisotropy":-0.6000000238418579,"scatteringFactor":2.0,"volumetricIntensity":1.0},"irradianceVolume":{"aabb":{"max":{"x":38.237998962402344,"y":18.340999603271484,"z":31.85099983215332},"min":{"x":-38.63600158691406,"y":-9.817999839782715,"z":-21.163000106811523}},"bias":0.30000001192092896,"cascadeCount":1,"enable":true,"gamma":5.0,"hysteresis":0.9800000190734863,"lowerResMoments":true,"opacityCheck":false,"optimizeProbes":true,"probeCount":{"x":20,"y":20,"z":20},"rayCount":100,"rayCountInactive":32,"sampleEmissives":false,"scroll":false,"sharpness":50.0,"splitCorrection":2.0,"strength":1.0,"useShadowMap":false},"name":"sponza","physicsWorld":null,"postProcessing":{"chromaticAberration":{"colorsReversed":false,"enable":false,"strength":1.0},"contrast":1.0,"filmGrain":{"enable":false,"strength":0.10000000149011612},"filmicTonemapping":false,"fsr2":true,"paperWhiteLuminance":150.0,"saturation":1.0,"screenMaxLuminance":1600.0,"sharpen":{"enable":true,"factor":0.15000000596046448},"taa":{"enable":true,"jitterRange":0.9900000095367432},"tint":{"x":1.0,"y":1.0,"z":1.0},"vignette":{"color":{"x":0.0,"y":0.0,"z":0.0},"enable":false,"offset":0.0,"power":0.0,"strength":0.0}},"reflection":{"bias":0.0,"currentClipFactor":2.0,"ddgi":true,"enable":true,"halfResolution":true,"historyClipMax":1.0,"opacityCheck":false,"radianceLimit":10.0,"roughnessCutoff":0.8999999761581421,"rt":true,"spatialFilterStrength":5.234000205993652,"temporalWeight":0.9520000219345093,"textureLevel":4,"useNormalMaps":true,"useShadowMap":false},"sky":{"atmosphere":{"height":100000.0,"mieHeightScale":1200.0,"mieScatteringCoeff":2.099999983329326e-05,"probeResolution":128,"rayleighHeightScale":8000.0,"rayleighScatteringCoeff":{"x":5.500000042957254e-06,"y":1.2999999853491317e-05,"z":2.2399999579647556e-05}},"clouds":{"castShadow":false,"coverageResolution":512,"coverageScale":0.25,"coverageSpeed":5.0,"darkEdgeAmbient":0.10000000149011612,"darkEdgeFocus":2.0,"densityMultiplier":0.800000011920929,"detailResolution":32,"detailScale":16.0,"detailSpeed":10.0,"detailStrength":0.15000000596046448,"distanceLimit":8000.0,"enable":false,"halfResolution":true,"heightStretch":0.5,"maxHeight":1700.0,"minHeight":1400.0,"occlusionSampleCount":5,"sampleCount":64,"scattering":{"eccentricityFirstPhase":0.0,"eccentricitySecondPhase":-0.5,"extinctionCoefficients":{"w":1.0,"x":0.9300000071525574,"y":0.9649999737739563,"z":1.0},"extinctionFactor":0.1599999964237213,"phaseAlpha":0.5,"scatteringFactor":2.0},"shadowResolution":512,"shadowSampleFraction":4,"shapeResolution":128,"shapeScale":2.0,"shapeSpeed":5.0,"stochasticOcclusionSampling":true},"intensity":1.0,"planetCenter":{"x":0.0,"y":-650000.0,"z":0.0},"planetRadius":649000.0},"ssgi":{"aoStrength":1.0,"enable":true,"enableAo":true,"halfResolution":true,"irradianceLimit":10.0,"radius":1.0,"rayCount":2,"sampleCount":8},"sss":{"enable":true,"maxLength":0.4970000088214874,"sampleCount":8,"thickness":0.30000001192092896},"wind":{"direction":{"x":1.0,"y":1.0},"speed":10.0}} \ No newline at end of file diff --git a/data/shader/bloom b/data/shader/bloom deleted file mode 100644 index d612e268e..000000000 --- a/data/shader/bloom +++ /dev/null @@ -1,12 +0,0 @@ -uniform bool bloom; - -vec3 CalculateBloom(vec3 color) { - - if(bloom) { - - float brightness = dot(color, vec3(1.0f)); - return max(color * (brightness), 0.0f); - - } - -} \ No newline at end of file diff --git a/data/shader/bloom.fsh b/data/shader/bloom.fsh deleted file mode 100644 index b0fa66582..000000000 --- a/data/shader/bloom.fsh +++ /dev/null @@ -1,16 +0,0 @@ -in vec2 fTexCoord; - -uniform sampler2D diffuseMap; -uniform vec3 bloomThreshold; -uniform float bloomPower; - -out vec3 color; - -void main() { - - vec3 fragColor = texture(diffuseMap, fTexCoord).rgb; - - float bloomBrightness = pow(dot(fragColor.xyz, bloomThreshold.xyz), bloomPower); - color = fragColor.xyz * bloomBrightness; - -} \ No newline at end of file diff --git a/data/shader/bloom.vsh b/data/shader/bloom.vsh deleted file mode 100644 index d0f26ddaf..000000000 --- a/data/shader/bloom.vsh +++ /dev/null @@ -1,10 +0,0 @@ -layout(location=0)in vec2 vPosition; - -out vec2 fTexCoord; - -void main() { - - fTexCoord = (vPosition + 1.0) / 2.0; - gl_Position = vec4(vPosition, 0.0, 1.0); - -} \ No newline at end of file diff --git a/data/shader/bloom/bloom.hsh b/data/shader/bloom/bloom.hsh new file mode 100644 index 000000000..e69de29bb diff --git a/data/shader/bloom/bloomDownsample.csh b/data/shader/bloom/bloomDownsample.csh new file mode 100644 index 000000000..eb6b0a988 --- /dev/null +++ b/data/shader/bloom/bloomDownsample.csh @@ -0,0 +1,112 @@ +layout (local_size_x = 16, local_size_y = 16) in; + +#include <../common/utility.hsh> +#include <../common/types.hsh> +#include <../common/flatten.hsh> + +layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D textureOut; +layout (set = 3, binding = 1) uniform sampler2D textureIn; + +layout(push_constant) uniform constants { + int mipLevel; + float threshold; +} pushConstants; + +const uint supportSize = 2; +const uint sharedDataSize = (2u * gl_WorkGroupSize.x + 2u * supportSize) * (2u * gl_WorkGroupSize.y + 2u * supportSize); +const ivec2 unflattenedSharedDataSize = 2 * ivec2(gl_WorkGroupSize) + 2 * int(supportSize); + +shared vec3 sharedMemory[sharedDataSize]; + +const ivec2 pixelOffsets[4] = ivec2[4]( + ivec2(0, 0), + ivec2(1, 0), + ivec2(0, 1), + ivec2(1, 1) +); + +AeF16 Luma(AeF16x3 color) { + + const AeF16x3 luma = AeF16x3(0.299, 0.587, 0.114); + return dot(color, luma); + +} + +AeF16x3 Prefilter(AeF16x3 color) { + + AeF16 brightness = Luma(color); + AeF16 contribution = max(AeF16(0.0), brightness - AeF16(pushConstants.threshold)); + contribution /= max(brightness, AeF16(0.001)); + return color * contribution; + +} + + +AeF16x3 Sample(vec2 texCoord) { + + if (pushConstants.mipLevel == 0) { + return Prefilter(AeF16x3(textureLod(textureIn, texCoord, float(pushConstants.mipLevel)).rgb)); + } + else { + return AeF16x3(textureLod(textureIn, texCoord, float(pushConstants.mipLevel)).rgb); + } + +} + +void main() { + + ivec2 size = imageSize(textureOut); + ivec2 coord = ivec2(gl_GlobalInvocationID); + + if (coord.x < size.x && coord.y < size.y) { + + // Lower mip tex coord + vec2 texCoord = (coord + 0.5) / size; + // Upper mip texel size + vec2 texelSize = 1.0 / vec2(textureSize(textureIn, pushConstants.mipLevel)); + + // We always sample at pixel border, not centers + AeF16x3 outer00 = Sample(texCoord + vec2(-2.0 * texelSize.x, -2.0 * texelSize.y)); + AeF16x3 outer10 = Sample(texCoord + vec2(0.0, -2.0 * texelSize.y)); + AeF16x3 outer20 = Sample(texCoord + vec2(2.0 * texelSize.x, -2.0 * texelSize.y)); + + AeF16x3 outer01 = Sample(texCoord + vec2(-2.0 * texelSize.x, 0.0)); + AeF16x3 outer11 = Sample(texCoord + vec2(0.0, 0.0)); + AeF16x3 outer21 = Sample(texCoord + vec2(2.0 * texelSize.x, 0.0)); + + AeF16x3 outer02 = Sample(texCoord + vec2(-2.0 * texelSize.x, 2.0 * texelSize.y)); + AeF16x3 outer12 = Sample(texCoord + vec2(0.0, 2.0 * texelSize.y)); + AeF16x3 outer22 = Sample(texCoord + vec2(2.0 * texelSize.x, 2.0 * texelSize.y)); + + AeF16x3 inner00 = Sample(texCoord + vec2(-texelSize.x, -texelSize.y)); + AeF16x3 inner10 = Sample(texCoord + vec2(texelSize.x, -texelSize.y)); + AeF16x3 inner01 = Sample(texCoord + vec2(-texelSize.x, texelSize.y)); + AeF16x3 inner11 = Sample(texCoord + vec2(texelSize.x, texelSize.y)); + + AeF16x3 outerGroup0 = AeF16(0.125 * 0.25) * (outer00 + outer10 + outer01 + outer11); + AeF16x3 outerGroup1 = AeF16(0.125 * 0.25) * (outer10 + outer20 + outer11 + outer21); + AeF16x3 outerGroup2 = AeF16(0.125 * 0.25) * (outer01 + outer11 + outer02 + outer12); + AeF16x3 outerGroup3 = AeF16(0.125 * 0.25) * (outer11 + outer21 + outer12 + outer22); + AeF16x3 innerGroup = AeF16(0.5 * 0.25) * (inner00 + inner10 + inner01 + inner11); + + if (pushConstants.mipLevel == 0) { + AeF16 outerGroup0Weight = (AeF16(1.0) / (AeF16(1.0) + Luma(outerGroup0))); + AeF16 outerGroup1Weight = (AeF16(1.0) / (AeF16(1.0) + Luma(outerGroup1))); + AeF16 outerGroup2Weight = (AeF16(1.0) / (AeF16(1.0) + Luma(outerGroup2))); + AeF16 outerGroup3Weight = (AeF16(1.0) / (AeF16(1.0) + Luma(outerGroup3))); + AeF16 innerGroupWeight = (AeF16(1.0) / (AeF16(1.0) + Luma(innerGroup))); + + outerGroup0 *= outerGroup0Weight; + outerGroup1 *= outerGroup1Weight; + outerGroup2 *= outerGroup2Weight; + outerGroup3 *= outerGroup3Weight; + innerGroup *= innerGroupWeight; + } + + vec3 filtered = outerGroup0 + outerGroup1 + outerGroup2 + outerGroup3 + innerGroup; + + imageStore(textureOut, coord, vec4(filtered, 1.0)); + + } + +} \ No newline at end of file diff --git a/data/shader/bloom/bloomUpsample.csh b/data/shader/bloom/bloomUpsample.csh new file mode 100644 index 000000000..d533c1d9e --- /dev/null +++ b/data/shader/bloom/bloomUpsample.csh @@ -0,0 +1,64 @@ +#include <../common/types.hsh> + +layout (local_size_x = 8, local_size_y = 8) in; + +layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D textureOut; +layout (set = 3, binding = 1) uniform sampler2D textureIn; + +layout(push_constant) uniform constants { + int additive; + int mipLevel; + float filterSize; +} pushConstants; + +AeF16x3 Sample(vec2 texCoord) { + + return AeF16x3(textureLod(textureIn, texCoord, float(pushConstants.mipLevel)).rgb); + +} + +void main() { + + ivec2 size = imageSize(textureOut); + ivec2 coord = ivec2(gl_GlobalInvocationID); + + if (coord.x < size.x && + coord.y < size.y) { + + // Lower mip tex coord + vec2 texCoord = (coord + 0.5) / size; + // Upper mip texel size + vec2 texelSize = 1.0 / textureSize(textureIn, pushConstants.mipLevel); + vec2 filterSize = vec2(pushConstants.filterSize); + texelSize = max(filterSize, filterSize); + + // We always sample at pixel border, not centers + AeF16x3 filter00 = Sample(texCoord + vec2(-texelSize.x, -texelSize.y)); + AeF16x3 filter10 = Sample(texCoord + vec2(0.0, -texelSize.y)); + AeF16x3 filter20 = Sample(texCoord + vec2(texelSize.x, -texelSize.y)); + + AeF16x3 filter01 = Sample(texCoord + vec2(-texelSize.x, 0.0)); + AeF16x3 filter11 = Sample(texCoord + vec2(0.0, 0.0)); + AeF16x3 filter21 = Sample(texCoord + vec2(texelSize.x, 0.0)); + + AeF16x3 filter02 = Sample(texCoord + vec2(-texelSize.x, texelSize.y)); + AeF16x3 filter12 = Sample(texCoord + vec2(0.0, texelSize.y)); + AeF16x3 filter22 = Sample(texCoord + vec2(texelSize.x, texelSize.y)); + + AeF16x3 filtered = AeF16x3(0.0); + + filtered += AeF16(4.0) * filter11; + filtered += AeF16(2.0) * (filter10 + filter01 + filter21 + filter12); + filtered += AeF16(1.0) * (filter00 + filter20 + filter02 + filter22); + filtered /= AeF16(16.0); + + if (pushConstants.additive > 0) { + imageStore(textureOut, coord, vec4(filtered + filter11, 1.0)); + } + else { + imageStore(textureOut, coord, vec4(filtered, 1.0)); + } + + } + +} \ No newline at end of file diff --git a/data/shader/clouds/clouds.hsh b/data/shader/clouds/clouds.hsh index de2ac5995..db5b116b3 100644 --- a/data/shader/clouds/clouds.hsh +++ b/data/shader/clouds/clouds.hsh @@ -1,12 +1,12 @@ #include <../common/utility.hsh> -#include <../structures> +#include <../structures.hsh> layout(set = 3, binding = 1) uniform sampler2D depthTexture; layout(set = 3, binding = 2) uniform sampler3D shapeTexture; layout(set = 3, binding = 3) uniform sampler3D detailTexture; layout(set = 3, binding = 4) uniform sampler2D coverageTexture; -layout(set = 1, binding = 10) uniform samplerCube diffuseProbe; +layout(set = 1, binding = 12) uniform samplerCube diffuseProbe; layout(std140, set = 3, binding = 5) uniform CloudUniformBuffer { Light light; diff --git a/data/shader/clouds/integrate.csh b/data/shader/clouds/integrate.csh index 62072e567..0df456bb1 100644 --- a/data/shader/clouds/integrate.csh +++ b/data/shader/clouds/integrate.csh @@ -1,5 +1,5 @@ #include <../globals.hsh> -#include <../structures> +#include <../structures.hsh> #include <../common/stencil.hsh> #include <../common/convert.hsh> #include <../common/utility.hsh> diff --git a/data/shader/clouds/shadow.csh b/data/shader/clouds/shadow.csh index 6eb2b169d..01490c8ac 100644 --- a/data/shader/clouds/shadow.csh +++ b/data/shader/clouds/shadow.csh @@ -1,5 +1,5 @@ #include <../globals.hsh> -#include <../structures> +#include <../structures.hsh> #include <../common/convert.hsh> #include <../common/utility.hsh> #include <../common/random.hsh> diff --git a/data/shader/clouds/shadow.hsh b/data/shader/clouds/shadow.hsh index 2474a8ce5..a29adf922 100644 --- a/data/shader/clouds/shadow.hsh +++ b/data/shader/clouds/shadow.hsh @@ -1,7 +1,20 @@ -#include <../structures> #include <../globals.hsh> #include <../common/convert.hsh> +struct CloudShadow { + + mat4 vMatrix; + mat4 pMatrix; + + mat4 ivMatrix; + mat4 ipMatrix; + +}; + +layout(std140, set = 1, binding = 19) uniform CloudShadowUniformBuffer { + CloudShadow cloudShadow; +} cloudShadowUniforms; + float CalculateCloudShadow(vec3 P, CloudShadow cloudShadow, sampler2D shadowMap) { vec4 cloudShadowViewCoords = cloudShadow.vMatrix * vec4(P, 1.0); diff --git a/data/shader/common/ign.hsh b/data/shader/common/ign.hsh index 1cd603c63..9f36a67b1 100644 --- a/data/shader/common/ign.hsh +++ b/data/shader/common/ign.hsh @@ -6,6 +6,15 @@ float GetInterleavedGradientNoise(vec2 screenPos) { float x = float(screenPos.x) + 5.588238 * float(frame); float y = float(screenPos.y) + 5.588238 * float(frame); + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); + return fract(magic.z * fract(dot(vec2(x, y), magic.xy))); +} + +float GetInterleavedGradientNoise(vec2 screenPos, uint frameCount) { + uint frame = globalData.frameCount % frameCount; + float x = float(screenPos.x) + 5.588238 * float(frame); + float y = float(screenPos.y) + 5.588238 * float(frame); + vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); return fract(magic.z * fract(dot(vec2(x, y), magic.xy))); } \ No newline at end of file diff --git a/data/shader/common/light.hsh b/data/shader/common/light.hsh new file mode 100644 index 000000000..886cf6d4d --- /dev/null +++ b/data/shader/common/light.hsh @@ -0,0 +1,13 @@ +#include + +float GetDistanceAttenuation(float dist, float radius) { + + return saturate(1.0 - pow(dist / radius, 4.0)) / (dist * dist); + +} + +float GetAngleAttenuation() { + + + +} \ No newline at end of file diff --git a/data/shader/common/material.hsh b/data/shader/common/material.hsh index e2ad60f1c..d3dc9e87d 100644 --- a/data/shader/common/material.hsh +++ b/data/shader/common/material.hsh @@ -8,8 +8,9 @@ #define FEATURE_ROUGHNESS_MAP (1 << 4) #define FEATURE_METALNESS_MAP (1 << 5) #define FEATURE_AO_MAP (1 << 6) -#define FEATURE_TRANSMISSION (1 << 7) -#define FEATURE_VERTEX_COLORS (1 << 8) +#define FEATURE_EMISSIVE_MAP (1 << 7) +#define FEATURE_TRANSMISSION (1 << 8) +#define FEATURE_VERTEX_COLORS (1 << 9) struct Material { uint ID; @@ -36,6 +37,7 @@ struct Material { bool roughnessMap; bool metalnessMap; bool aoMap; + bool emissiveMap; bool transmissive; bool vertexColors; }; @@ -45,7 +47,7 @@ struct PackedMaterial { vec4 data1; }; -layout (std430, set = 1, binding = 14) buffer PackedMaterials { +layout (std430, set = 1, binding = 15) buffer PackedMaterials { PackedMaterial packedMaterials[]; }; @@ -87,6 +89,7 @@ Material UnpackMaterial(uint idx) { material.roughnessMap = (features & FEATURE_ROUGHNESS_MAP) > 0; material.metalnessMap = (features & FEATURE_METALNESS_MAP) > 0; material.aoMap = (features & FEATURE_AO_MAP) > 0; + material.emissiveMap = (features & FEATURE_EMISSIVE_MAP) > 0; material.transmissive = (features & FEATURE_TRANSMISSION) > 0; material.vertexColors = (features & FEATURE_VERTEX_COLORS) > 0; diff --git a/data/shader/common/traceScreenSpace.hsh b/data/shader/common/traceScreenSpace.hsh new file mode 100644 index 000000000..29bc8b027 --- /dev/null +++ b/data/shader/common/traceScreenSpace.hsh @@ -0,0 +1,276 @@ +// By Morgan McGuire and Michael Mara at Williams College 2014 +// Released as open source under the BSD 2-Clause License +// http://opensource.org/licenses/BSD-2-Clause + +// Some ideas: https://willpgfx.com/2015/07/screen-space-glossy-reflections/ +// More: https://interplayoflight.wordpress.com/2019/09/07/hybrid-screen-space-reflections/ + +#include +#include <../globals.hsh> + +#define point2 vec2 +#define point3 vec3 + +float distanceSquared(vec2 a, vec2 b) { a -= b; return dot(a, a); } + +bool intersectsDepthBuffer(float z, float minZ, float maxZ, float thickness, float strideCutoff) { + /* + * Based on how far away from the camera the depth is, + * adding a bit of extra thickness can help improve some + * artifacts. Driving this value up too high can cause + * artifacts of its own. + */ + float depthScale = min(1.0, -z * strideCutoff); + thickness *= mix(0.0, 4.0, depthScale); + return (maxZ >= z - thickness) && (minZ < z); +} + +bool validPixel(vec2 hitPixel, vec2 resolution) { + + return hitPixel.x >= 0 && hitPixel.y >= 0 && hitPixel.x < resolution.x && hitPixel.y < resolution.y; + +} + +// Returns true if the ray hit something +bool traceScreenSpace( + // Camera-space ray origin, which must be within the view volume + point3 csOrig, + + // Unit length camera-space ray direction + vec3 csDir, + + // The camera-space Z buffer (all negative values) + sampler2D csZBuffer, + + // Camera space thickness to ascribe to each pixel in the depth buffer + float zThickness, + + // Step in horizontal or vertical pixels between samples. This is a float + // because integer math is slow on GPUs, but should be set to an integer >= 1 + float stride, + + // Number between 0 and 1 for how far to bump the ray in stride units + // to conceal banding artifacts + float jitter, + + // Maximum number of iterations. Higher gives better images but may be slow + const float maxSteps, + + // Maximum camera-space distance to trace before returning a miss + float maxDistance, + + // Pixel coordinates of the first intersection with the scene + out point2 hitPixel, + + // Camera space location of the ray hit + out point3 hitPoint, + + out point3 minPoint, + + out point3 maxPoint) { + + mat4 ipMatrix = globalData.ipMatrix; + + // Clip to the near plane + float rayLength = ((csOrig.z + csDir.z * maxDistance) > -globalData.cameraNearPlane) ? + (-globalData.cameraNearPlane - csOrig.z) / csDir.z : maxDistance; + point3 csEndPoint = csOrig + csDir * rayLength; + + vec2 csZBufferSize = vec2(textureSize(csZBuffer, 0)); + + // Project into homogeneous clip space + vec4 H0 = globalData.pMatrix * vec4(csOrig, 1.0); + vec4 H1 = globalData.pMatrix * vec4(csEndPoint, 1.0); + float k0 = 1.0 / H0.w, k1 = 1.0 / H1.w; + + // The interpolated homogeneous version of the camera-space points + point3 Q0 = csOrig * k0; + point3 Q1 = csEndPoint * k1; + + // Screen-space endpoints + point2 P0 = (H0.xy * k0) * 0.5 + 0.5; + point2 P1 = 0.5 * (H1.xy * k1) + 0.5; + P0 *= csZBufferSize; + P1 *= csZBufferSize; + + // If the line is degenerate, make it cover at least one pixel + // to avoid handling zero-pixel extent as a special case later + P1 += vec2((distanceSquared(P0, P1) < 0.0001) ? 0.01 : 0.0); + vec2 delta = P1 - P0; + + // Permute so that the primary iteration is in x to collapse + // all quadrant-specific DDA cases later + bool permute = false; + if (abs(delta.x) < abs(delta.y)) { + // This is a more-vertical line + permute = true; delta = delta.yx; P0 = P0.yx; P1 = P1.yx; + } + + float stepDir = sign(delta.x); + float invdx = stepDir / delta.x; + + // Track the derivatives of Q and k + vec3 dQ = (Q1 - Q0) * invdx; + float dk = (k1 - k0) * invdx; + vec2 dP = vec2(stepDir, delta.y * invdx); + + // Scale derivatives by the desired pixel stride and then + // offset the starting values by the jitter fraction + + float strideScale = 1.0 - min(1.0, abs(csOrig.z) * 0.01); + stride = 1.0 + strideScale * stride; + + P0 += dP; + Q0 += dQ; + k0 += dk; + + dP *= stride; + dQ *= stride; + dk *= stride; + + P0 += dP * jitter; + Q0 += dQ * jitter; + k0 += dk * jitter; + + // Slide P from P0 to P1, (now-homogeneous) Q from Q0 to Q1, k from k0 to k1 + point3 Q = Q0; + + // Adjust end condition for iteration direction + float end = P1.x * stepDir; + + float k = k0, stepCount = 0.0, prevZMaxEstimate = csOrig.z; + float rayZMin = prevZMaxEstimate, rayZMax = prevZMaxEstimate; + float sceneZMax = rayZMax + maxDistance; + point2 P = P0; + for (; + ((P.x * stepDir) <= end) && (stepCount < maxSteps) && + !intersectsDepthBuffer(sceneZMax, rayZMin, rayZMax, zThickness, 0.01) && + (sceneZMax != 0); + P += dP, Q.z += dQ.z, k += dk, ++stepCount) { + + rayZMin = prevZMaxEstimate; + rayZMax = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k); + prevZMaxEstimate = rayZMax; + if (rayZMin > rayZMax) { + float t = rayZMin; rayZMin = rayZMax; rayZMax = t; + } + + hitPixel = permute ? P.yx : P; + // You may need hitPixel.y = csZBufferSize.y - hitPixel.y; here if your vertical axis + // is different than ours in screen space + sceneZMax = ConvertDepthToViewSpaceDepth(texelFetch(csZBuffer, ivec2(hitPixel), 0).r, ipMatrix); + } + + minPoint = Q0 + dQ * (stepCount - 2.0); + maxPoint = Q0 + dQ * (stepCount - 1.0); + + minPoint /= (k - 2.0 * dk); + maxPoint /= (k - 1.0 * dk); + + // Advance Q based on the number of steps + Q.xy += dQ.xy * stepCount; + hitPoint = Q * (1.0 / k); + + return intersectsDepthBuffer(sceneZMax, rayZMin, rayZMax, zThickness, 0.01) + && validPixel(hitPixel, csZBufferSize); +} + +bool traceScreenSpace( + // Camera-space ray origin, which must be within the view volume + point3 csOrig, + + // Unit length camera-space ray direction + vec3 csDir, + + // The camera-space Z buffer (all negative values) + sampler2D csZBuffer, + + // Camera space thickness to ascribe to each pixel in the depth buffer + float zThickness, + + // Step in horizontal or vertical pixels between samples. This is a float + // because integer math is slow on GPUs, but should be set to an integer >= 1 + float stride, + + // Number between 0 and 1 for how far to bump the ray in stride units + // to conceal banding artifacts + float jitter, + + // Maximum number of iterations. Higher gives better images but may be slow + const float maxSteps, + + // Maximum camera-space distance to trace before returning a miss + float maxDistance, + + // Pixel coordinates of the first intersection with the scene + out point2 hitPixel, + + // Camera space location of the ray hit + out point3 hitPoint) { + + point3 minPoint, maxPoint; + return traceScreenSpace(csOrig, csDir, csZBuffer, zThickness, stride, jitter, maxSteps, maxDistance, hitPixel, hitPoint, minPoint, maxPoint); + +} + +bool traceScreenSpaceAdvanced( + // Camera-space ray origin, which must be within the view volume + point3 csOrig, + + // Unit length camera-space ray direction + vec3 csDir, + + // The camera-space Z buffer (all negative values) + sampler2D csZBuffer, + + // Camera space thickness to ascribe to each pixel in the depth buffer + float zThickness, + + // Step in horizontal or vertical pixels between samples. This is a float + // because integer math is slow on GPUs, but should be set to an integer >= 1 + float stride, + + // Number between 0 and 1 for how far to bump the ray in stride units + // to conceal banding artifacts + float jitter, + + // Maximum number of iterations. Higher gives better images but may be slow + float maxSteps, + + // Maximum camera-space distance to trace before returning a miss + float maxDistance, + + bool allowFallback, + + // Pixel coordinates of the first intersection with the scene + out point2 hitPixel, + + // Camera space location of the ray hit + out point3 hitPoint) { + + bool hit = false; + + point3 minPoint, maxPoint; + if(traceScreenSpace(csOrig, csDir, csZBuffer, zThickness, stride, jitter, maxSteps, maxDistance, hitPixel, hitPoint, minPoint, maxPoint)) { + hit = allowFallback; + } + + // maxSteps /= 2.0; + + csOrig = minPoint; + csDir = normalize(maxPoint - minPoint); + stride = max(1.0, 2.0 * stride / maxSteps);; + //zThickness /= (2.0 * maxSteps); + //maxDistance = min(maxDistance, 20.0 * distance(maxPoint, minPoint)); + + vec2 newHitPixel; + vec3 newHitPoint; + if (traceScreenSpace(csOrig, csDir, csZBuffer, zThickness, stride, jitter, maxSteps, maxDistance, newHitPixel, newHitPoint, minPoint, maxPoint)) { + hitPixel = newHitPixel; + hitPoint = newHitPoint; + hit = true; + } + + return hit; + +} \ No newline at end of file diff --git a/data/shader/common/types.hsh b/data/shader/common/types.hsh new file mode 100644 index 000000000..dd790c8a6 --- /dev/null +++ b/data/shader/common/types.hsh @@ -0,0 +1,48 @@ +#extension GL_EXT_shader_explicit_arithmetic_types : require +#extension GL_EXT_shader_16bit_storage : require + +#define AeBool bool +#define AeF32 float +#define AeF32x2 vec2 +#define AeF32x3 vec3 +#define AeF32x4 vec4 +#define AeUI32 uint +#define AeUI32x2 uvec2 +#define AeUI32x3 uvec3 +#define AeUI32x4 uvec4 +#define AeI32 int +#define AeI32x2 ivec2 +#define AeI32x3 ivec3 +#define AeI32x4 ivec4 + +#ifdef AE_HALF_FLOAT +#define AeF16 float16_t +#define AeF16x2 f16vec2 +#define AeF16x3 f16vec3 +#define AeF16x4 f16vec4 +#define AeUI16 uint16_t +#define AeUI16x2 u16vec2 +#define AeUI16x3 u16vec3 +#define AeUI16x4 u16vec4 +#define AeI16 int16_t +#define AeI16x2 i16vec2 +#define AeI16x3 i16vec3 +#define AeI16x4 i16vec4 +#define AeF16x4x4 f16mat4x4 +#define AeF16x3x3 f16mat3x3 +#else +#define AeF16 float +#define AeF16x2 vec2 +#define AeF16x3 vec3 +#define AeF16x4 vec4 +#define AeUI16 uint +#define AeUI16x2 uvec2 +#define AeUI16x3 uvec3 +#define AeUI16x4 uvec4 +#define AeI16 int +#define AeI16x2 ivec2 +#define AeI16x3 ivec3 +#define AeI16x4 ivec4 +#define AeF16x4x4 mat4 +#define AeF16x3x3 mat3 +#endif \ No newline at end of file diff --git a/data/shader/common/utility.hsh b/data/shader/common/utility.hsh index 0d629b20a..5594ccf52 100644 --- a/data/shader/common/utility.hsh +++ b/data/shader/common/utility.hsh @@ -1,5 +1,6 @@ // Contains useful functions #include +#include float saturate(float value) { @@ -25,6 +26,32 @@ vec4 saturate(vec4 value) { } +#ifdef AE_HALF_FLOAT +AeF16 saturate(AeF16 value) { + + return clamp(value, AeF16(0.0), AeF16(1.0)); + +} + +AeF16x2 saturate(AeF16x2 value) { + + return clamp(value, AeF16x2(0.0), AeF16x2(1.0)); + +} + +AeF16x3 saturate(AeF16x3 value) { + + return clamp(value, AeF16x3(0.0), AeF16x3(1.0)); + +} + +AeF16x4 saturate(AeF16x4 value) { + + return clamp(value, AeF16x4(0.0), AeF16x4(1.0)); + +} +#endif + float sqr(float value) { return value * value; diff --git a/data/shader/common/ycocg.hsh b/data/shader/common/ycocg.hsh index b19cc8990..6aaa91106 100644 --- a/data/shader/common/ycocg.hsh +++ b/data/shader/common/ycocg.hsh @@ -1,6 +1,11 @@ +#include + const mat3 RGBToYCoCgMatrix = mat3(0.25, 0.5, -0.25, 0.5, 0.0, 0.5, 0.25, -0.5, -0.25); const mat3 YCoCgToRGBMatrix = mat3(1.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, 1.0, -1.0); +const AeF16x3x3 F16RGBToYCoCgMatrix = AeF16x3x3(0.25, 0.5, -0.25, 0.5, 0.0, 0.5, 0.25, -0.5, -0.25); +const AeF16x3x3 F16YCoCgToRGBMatrix = AeF16x3x3(1.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, 1.0, -1.0); + vec3 RGBToYCoCg(vec3 RGB) { return RGBToYCoCgMatrix * RGB; @@ -12,3 +17,17 @@ vec3 YCoCgToRGB(vec3 YCoCg) { return YCoCgToRGBMatrix * YCoCg; } + +#ifdef AE_HALF_FLOAT +AeF16x3 RGBToYCoCg(AeF16x3 RGB) { + + return F16RGBToYCoCgMatrix * RGB; + +} + +AeF16x3 YCoCgToRGB(AeF16x3 YCoCg) { + + return F16YCoCgToRGBMatrix * YCoCg; + +} +#endif \ No newline at end of file diff --git a/data/shader/ddgi/ddgi.hsh b/data/shader/ddgi/ddgi.hsh index 0c0d46dc9..83466ccd5 100644 --- a/data/shader/ddgi/ddgi.hsh +++ b/data/shader/ddgi/ddgi.hsh @@ -10,7 +10,8 @@ #define PROBE_STATE_NEW 0 #define PROBE_STATE_ACTIVE 1 -#define PROBE_STATE_INACTIVE 2 +#define PROBE_STATE_INVISIBLE 2 +#define PROBE_STATE_INACTIVE 3 #define VOLUME_CASCADE_COUNT 8 layout(set = 2, binding = 24) uniform sampler2DArray irradianceVolume; @@ -98,7 +99,7 @@ PackedRayHit PackRayHit(RayHit hit) { } uint GetProbeRayCount(uint state) { - return state == PROBE_STATE_INACTIVE ? ddgiData.inactiveRayCount : ddgiData.rayCount; + return state == PROBE_STATE_INACTIVE || state == PROBE_STATE_INVISIBLE ? ddgiData.inactiveRayCount : ddgiData.rayCount; } vec3 GetProbePosition(ivec3 probeIdx, int cascadeIndex) { @@ -144,6 +145,24 @@ bool IsInsideCascade(vec3 position, int cascadeIndex) { } +vec3 GetCellSize(vec3 position) { + + int cascadeIndex = 0; + while (cascadeIndex < ddgiData.cascadeCount) { + + bool isInside = IsInsideCascade(position, cascadeIndex); + if (!isInside) + break; + + cascadeIndex++; + } + + cascadeIndex = max(cascadeIndex - 1, 0); + + return ddgiData.cascades[cascadeIndex].cellSize.xyz; + +} + void GetProbeHistoryInfo(ivec3 probeIndex, int cascadeIndex, out ivec3 historyProbeCoord, out bool reset) { ivec3 cascadeProbeOffset = ivec3(0, cascadeIndex * ddgiData.volumeProbeCount.y, 0); diff --git a/data/shader/ddgi/probeDebug.fsh b/data/shader/ddgi/probeDebug.fsh index c40eaad86..ae63d67aa 100644 --- a/data/shader/ddgi/probeDebug.fsh +++ b/data/shader/ddgi/probeDebug.fsh @@ -1,12 +1,14 @@ #include <../globals.hsh> +#include <../common/normalencode.hsh> #include layout (location = 0) out vec3 baseColorFS; -layout (location = 1) out vec3 normalFS; -layout (location = 2) out vec3 geometryNormalFS; +layout (location = 1) out vec2 normalFS; +layout (location = 2) out vec2 geometryNormalFS; layout (location = 3) out vec3 roughnessMetalnessAoFS; -layout (location = 4) out uint materialIdxFS; -layout (location = 5) out vec2 velocityFS; +layout (location = 4) out vec3 emissiveFS; +layout (location = 5) out uint materialIdxFS; +layout (location = 6) out vec2 velocityFS; layout(location=0) in vec3 normalVS; @@ -42,8 +44,7 @@ void main() { baseColorFS = vec3(probeAge / 32.0); //baseColorFS = vec3(moments.x) / length(ddgiData.cascades[probeCascadeIndex].cellSize.xyz); - geometryNormalFS = normalize(normalVS); - geometryNormalFS = 0.5 * geometryNormalFS + 0.5; + geometryNormalFS = EncodeNormal(normalize(normalVS)); float roughnessFactor = 1.0; float metalnessFactor = 1.0; diff --git a/data/shader/ddgi/rayHit.csh b/data/shader/ddgi/rayHit.csh index 9bfa24ea7..511eda0fa 100644 --- a/data/shader/ddgi/rayHit.csh +++ b/data/shader/ddgi/rayHit.csh @@ -21,19 +21,23 @@ #include <../brdf/surface.hsh> #include <../shadow.hsh> - +#include <../clouds/shadow.hsh> #include layout (local_size_x = 32) in; layout(set = 3, binding = 0) uniform sampler2DArrayShadow cascadeMaps; +#ifdef CLOUD_SHADOWS +layout(set = 3, binding = 1) uniform sampler2D cloudMap; +#endif + // Instead of write ray array use hits array -layout(std430, set = 3, binding = 1) buffer RayHits { +layout(std430, set = 3, binding = 2) buffer RayHits { PackedRayHit hits[]; }; -layout(std140, set = 3, binding = 2) uniform UniformBuffer { +layout(std140, set = 3, binding = 3) uniform UniformBuffer { float seed; Shadow shadow; } Uniforms; @@ -126,11 +130,20 @@ vec3 EvaluateDirectLight(inout Surface surface) { surface.geometryNormal, saturate(dot(surface.L, surface.geometryNormal))); radiance *= shadowFactor; #else - radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; + if (light.castShadow) + radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; + +#ifdef CLOUD_SHADOWS + vec3 P = vec3(globalData.vMatrix * vec4(surface.P, 1.0)); + float cloudShadowFactor = CalculateCloudShadow(P, cloudShadowUniforms.cloudShadow, cloudMap); + + radiance *= cloudShadowFactor; +#endif #endif } else { - radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; + if (light.castShadow) + radiance *= CheckVisibility(surface, lightDistance) ? 1.0 : 0.0; } return reflectance * radiance * surface.NdotL / lightPdf; diff --git a/data/shader/deferred/decal.fsh b/data/shader/deferred/decal.fsh index 86a46edef..1cc45dda3 100644 --- a/data/shader/deferred/decal.fsh +++ b/data/shader/deferred/decal.fsh @@ -1,4 +1,4 @@ -#include "../structures" +#include <../structures.hsh> #include <../common/convert.hsh> in vec3 fTexCoordProj; diff --git a/data/shader/deferred/deferred.hsh b/data/shader/deferred/deferred.hsh index d639798ee..837abf6f5 100644 --- a/data/shader/deferred/deferred.hsh +++ b/data/shader/deferred/deferred.hsh @@ -7,12 +7,13 @@ layout(set = 1, binding = 3) uniform sampler2D baseColorTexture; layout(set = 1, binding = 4) uniform sampler2D normalTexture; layout(set = 1, binding = 5) uniform sampler2D geometryNormalTexture; layout(set = 1, binding = 6) uniform sampler2D roughnessMetalnessAoTexture; -layout(set = 1, binding = 7) uniform usampler2D materialIdxTexture; -layout(set = 1, binding = 8) uniform sampler2D depthTexture; -layout(set = 1, binding = 9) uniform sampler2D volumetricTexture; +layout(set = 1, binding = 7) uniform sampler2D emissiveTexture; +layout(set = 1, binding = 8) uniform usampler2D materialIdxTexture; +layout(set = 1, binding = 9) uniform sampler2D depthTexture; +layout(set = 1, binding = 10) uniform sampler2D volumetricTexture; -layout(set = 1, binding = 10) uniform samplerCube specularProbe; -layout(set = 1, binding = 11) uniform samplerCube diffuseProbe; +layout(set = 1, binding = 11) uniform samplerCube specularProbe; +layout(set = 1, binding = 12) uniform samplerCube diffuseProbe; Material GetMaterial(vec2 texCoord) { uint materialIdx = textureLod(materialIdxTexture, texCoord, 0).r; @@ -30,6 +31,9 @@ Material GetMaterial(vec2 texCoord) { if (material.aoMap) { material.ao *= textureLod(roughnessMetalnessAoTexture, texCoord, 0).b; } + if (material.emissiveMap) { + material.emissiveColor *= textureLod(emissiveTexture, texCoord, 0).rgb; + } return material; } diff --git a/data/shader/deferred/direct.csh b/data/shader/deferred/direct.csh index 33d39911a..dfc3b722b 100644 --- a/data/shader/deferred/direct.csh +++ b/data/shader/deferred/direct.csh @@ -1,9 +1,14 @@ +#extension GL_EXT_nonuniform_qualifier : require + #define SHADOW_FILTER_VOGEL #define SHADOW_CASCADE_BLENDING +layout (local_size_x = 16, local_size_y = 16) in; + #include +#include -#include <../structures> +#include <../structures.hsh> #include <../shadow.hsh> #include <../globals.hsh> @@ -13,29 +18,54 @@ #include <../common/octahedron.hsh> #include <../clouds/shadow.hsh> -layout (local_size_x = 8, local_size_y = 8) in; - layout(set = 3, binding = 0, rgba16f) uniform image2D image; -#ifdef SHADOWS -layout(set = 3, binding = 1) uniform sampler2DArrayShadow cascadeMaps; -#endif + #ifdef SCREEN_SPACE_SHADOWS -layout(set = 3, binding = 2) uniform sampler2D sssTexture; +layout(set = 3, binding = 1) uniform sampler2D sssTexture; #endif #ifdef CLOUD_SHADOWS -layout(set = 3, binding = 3) uniform sampler2D cloudMap; +layout(set = 3, binding = 2) uniform sampler2D cloudMap; #endif -layout(std140, set = 3, binding = 4) uniform UniformBuffer { - Light light; -} uniforms; +layout(set = 3, binding = 4) uniform sampler shadowSampler; + +layout(std430, set = 3, binding = 6) buffer LightBucketsBuffer { + int lightBuckets[]; +}; + +layout(set = 3, binding = 7) uniform texture2DArray cascadeMaps[8]; +layout(set = 3, binding = 15) uniform textureCube cubeMaps[8]; + +layout(push_constant) uniform constants { + int lightCount; + int lightBucketCount; + int padding1; + int padding2; +} pushConstants; + +shared uint sharedLightBuckets[lightBucketCount]; + +vec3 EvaluateLight(Light light, Surface surface, vec3 geometryNormal, bool isMain); +float GetShadowFactor(Light light, Surface surface, uint lightType, vec3 geometryNormal, bool isMain); + +void LoadGroupSharedData() { -layout(std140, set = 3, binding = 5) uniform CloudShadowUniformBuffer { - CloudShadow cloudShadow; -} cloudShadowUniforms; + int lightBucketOffset = GetLightBucketsGroupOffset(); + int groupSize = int(gl_WorkGroupSize.x * gl_WorkGroupSize.y); + + int localOffset = int(gl_LocalInvocationIndex); + for (int i = localOffset; i < pushConstants.lightBucketCount; i += groupSize) { + sharedLightBuckets[i] = uint(lightBuckets[lightBucketOffset + i]); + } + + barrier(); + +} void main() { + LoadGroupSharedData(); + ivec2 resolution = imageSize(image); ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); @@ -43,62 +73,183 @@ void main() { float depth = texelFetch(depthTexture, pixel, 0).r; + int lightCount = 0; + // Load to get the atmosphere which was rendered before vec3 direct = imageLoad(image, pixel).rgb; + if (depth < 1.0) { vec3 geometryNormal; // We don't have any light direction, that's why we use vec3(0.0, -1.0, 0.0) as a placeholder - Surface surface = GetSurface(texCoord, depth, -uniforms.light.direction.xyz, geometryNormal); + Surface surface = GetSurface(texCoord, depth, vec3(0.0, -1.0, 0.0), geometryNormal); - float shadowFactor = 1.0; + direct = vec3(0.0); - // Direct diffuse + specular BRDF - vec3 directDiffuse = EvaluateDiffuseBRDF(surface); - vec3 directSpecular = EvaluateSpecularBRDF(surface); + for (uint i = 0u; i < pushConstants.lightBucketCount; i++) { + uint lightBucket = sharedLightBuckets[i]; + if (lightBucket == 0) continue; - direct = directDiffuse + directSpecular; + // Get most significant bit index + uint bucketBitCount = findMSB(lightBucket) + 1u; + uint offsetIdx = i * 32u; -#ifdef SHADOWS - // Only need to test in the direction of the light and can the be used - // for both the transmission and reflection. The inversion is only done - // for transmissive materials - vec3 shadowNormal = surface.material.transmissive ? dot(-uniforms.light.direction.xyz, geometryNormal) < 0.0 ? - -geometryNormal : geometryNormal : geometryNormal; - shadowFactor = CalculateCascadedShadow(uniforms.light.shadow, cascadeMaps, surface.P, vec3(vec2(pixel) + 0.5, 0.0), - shadowNormal, saturate(dot(-uniforms.light.direction.xyz, shadowNormal))); -#endif -#ifdef CLOUD_SHADOWS - float cloudShadowFactor = CalculateCloudShadow(surface.P, cloudShadowUniforms.cloudShadow, cloudMap); + for (uint j = 0u; j < bucketBitCount; j++) { - shadowFactor = min(shadowFactor, cloudShadowFactor); -#endif - float shadowFactorTransmissive = shadowFactor; -#ifdef SCREEN_SPACE_SHADOWS - float sssFactor = textureLod(sssTexture, texCoord, 0).r; - shadowFactor = min(sssFactor, shadowFactor); -#endif - - vec3 radiance = uniforms.light.color.rgb * uniforms.light.intensity; - direct = direct * radiance * surface.NdotL * shadowFactor; + bool lightVisible = (lightBucket & (1 << j)) > 0u; + if (!lightVisible) continue; - if (surface.material.transmissive) { - Surface backSurface = CreateSurface(surface.V, -surface.N, surface.L, surface.material); + lightCount++; - float viewDependency = saturate(dot(-surface.V, surface.L)); - // viewDependency = sqr(viewDependency); + Light light = lights[offsetIdx + j]; - // Direct diffuse BRDF backside - directDiffuse = viewDependency * surface.material.transmissiveColor * EvaluateDiffuseBRDF(backSurface); - direct += directDiffuse * radiance * backSurface.NdotL * shadowFactorTransmissive; +#ifndef AE_BINDLESS + light.shadow.mapIdx = int(offsetIdx + j); +#endif + bool isMain = i + j == 0u ? true : false; + direct += EvaluateLight(light, surface, geometryNormal, isMain); + } } + if (dot(surface.material.emissiveColor, vec3(1.0)) > 0.01) { direct += surface.material.emissiveColor; } + } - //direct = cloudShadowFactor; + //direct = vec3(float(lightCount) / 64.0); + imageStore(image, pixel, vec4(direct, 1.0)); + +} + +uint GetLightType(Light light) { + + return floatBitsToUint(light.color.a); + +} +vec3 EvaluateLight(Light light, Surface surface, vec3 geometryNormal, bool isMain) { + + uint lightType = GetLightType(light); + + float attenuation = 1.0; + float radius = light.direction.w; + + if (lightType == DIRECTIONAL_LIGHT) { + surface.L = normalize(-light.direction.xyz); } + else if (lightType == POINT_LIGHT) { + vec3 pointToLight = light.location.xyz - surface.P; + float sqrDistance = dot(pointToLight, pointToLight); + float dist = sqrt(sqrDistance); + + attenuation = saturate(1.0 - pow(dist / radius, 4.0)) / sqrDistance; + + surface.L = pointToLight / dist; + } + else if (lightType == SPOT_LIGHT) { + vec3 pointToLight = light.location.xyz - surface.P; + float sqrDistance = dot(pointToLight, pointToLight); + float dist = sqrt(sqrDistance); - imageStore(image, pixel, vec4(direct, 1.0)); + surface.L = pointToLight / dist; + + float strength = dot(surface.L, normalize(-light.direction.xyz)); + float angleAttenuation = saturate(strength * light.specific0 + light.specific1); + float distAttenuation = saturate(1.0 - pow(dist / radius, 4.0)) / sqrDistance; + attenuation = distAttenuation * sqr(angleAttenuation); + } + + if (attenuation == 0.0) + return vec3(0.0); + + float shadowFactor = GetShadowFactor(light, surface, lightType, geometryNormal, isMain); + + UpdateSurface(surface); + + // Direct diffuse + specular BRDF + vec3 directDiffuse = EvaluateDiffuseBRDF(surface); + vec3 directSpecular = EvaluateSpecularBRDF(surface); + + vec3 direct = directDiffuse + directSpecular; + + vec3 radiance = light.color.rgb * light.intensity * attenuation; + direct = direct * radiance * surface.NdotL * shadowFactor; + + if (surface.material.transmissive) { + Surface backSurface = CreateSurface(surface.V, -surface.N, surface.L, surface.material); + + float viewDependency = saturate(dot(-surface.V, surface.L)); + // viewDependency = sqr(viewDependency); + + // Direct diffuse BRDF backside + directDiffuse = viewDependency * surface.material.transmissiveColor * EvaluateDiffuseBRDF(backSurface); + // Need to change this back + // direct += directDiffuse * radiance * backSurface.NdotL * shadowFactorTransmissive; + direct += directDiffuse * radiance * backSurface.NdotL * shadowFactor; + } + + return direct; + +} + +float GetShadowFactor(Light light, Surface surface, uint lightType, vec3 geometryNormal, bool isMain) { + + if (light.shadow.mapIdx < 0) + return 1.0; + + ivec2 resolution = imageSize(image); + ivec2 pixel = ivec2(gl_GlobalInvocationID); + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(resolution); + + float shadowFactor = 1.0; + + vec3 shadowNormal = surface.material.transmissive ? dot(surface.L, geometryNormal) < 0.0 ? + -geometryNormal : geometryNormal : geometryNormal; + + if (lightType == DIRECTIONAL_LIGHT) { +#ifdef AE_BINDLESS + shadowFactor = CalculateCascadedShadow(light.shadow, bindlessTextureArrays[nonuniformEXT(light.shadow.mapIdx)], + shadowSampler, surface.P, vec3(vec2(pixel) + 0.5, 0.0), + shadowNormal, saturate(dot(-light.direction.xyz, shadowNormal))); +#else + shadowFactor = CalculateCascadedShadow(light.shadow, cascadeMaps[nonuniformEXT(light.shadow.mapIdx)], + shadowSampler, surface.P, vec3(vec2(pixel) + 0.5, 0.0), + shadowNormal, saturate(dot(-light.direction.xyz, shadowNormal))); +#endif + } + else if (lightType == POINT_LIGHT) { +#ifdef AE_BINDLESS + shadowFactor = CalculatePointShadow(light.shadow, bindlessCubemaps[nonuniformEXT(light.shadow.mapIdx)], + shadowSampler, surface.P, shadowNormal, saturate(dot(-light.direction.xyz, shadowNormal))); +#else + shadowFactor = CalculatePointShadow(light.shadow, cubeMaps[nonuniformEXT(light.shadow.mapIdx)], + shadowSampler, surface.P, shadowNormal, saturate(dot(-light.direction.xyz, shadowNormal))); +#endif + } + else if (lightType == SPOT_LIGHT) { +#ifdef AE_BINDLESS + shadowFactor = CalculateSpotShadow(light.shadow, bindlessTextureArrays[nonuniformEXT(light.shadow.mapIdx)], + shadowSampler, surface.P, vec3(vec2(pixel) + 0.5, 0.0), + shadowNormal, saturate(dot(-light.direction.xyz, shadowNormal))); +#else + shadowFactor = CalculateSpotShadow(light.shadow, cascadeMaps[nonuniformEXT(light.shadow.mapIdx)], + shadowSampler, surface.P, vec3(vec2(pixel) + 0.5, 0.0), + shadowNormal, saturate(dot(-light.direction.xyz, shadowNormal))); +#endif + } + + if (isMain) { +#ifdef CLOUD_SHADOWS + float cloudShadowFactor = CalculateCloudShadow(surface.P, cloudShadowUniforms.cloudShadow, cloudMap); + + shadowFactor *= cloudShadowFactor; +#endif + float shadowFactorTransmissive = shadowFactor; +#ifdef SCREEN_SPACE_SHADOWS + float sssFactor = textureLod(sssTexture, texCoord, 0).r; + shadowFactor = min(sssFactor, shadowFactor); +#endif + } + + return shadowFactor; } \ No newline at end of file diff --git a/data/shader/deferred/geometry.fsh b/data/shader/deferred/geometry.fsh index 0cb443b56..6f73d1293 100644 --- a/data/shader/deferred/geometry.fsh +++ b/data/shader/deferred/geometry.fsh @@ -7,8 +7,9 @@ layout (location = 0) out vec3 baseColorFS; layout (location = 1) out vec2 normalFS; layout (location = 2) out vec2 geometryNormalFS; layout (location = 3) out vec3 roughnessMetalnessAoFS; -layout (location = 4) out uint materialIdxFS; -layout (location = 5) out vec2 velocityFS; +layout (location = 4) out vec3 emissiveFS; +layout (location = 5) out uint materialIdxFS; +layout (location = 6) out vec2 velocityFS; layout(location=0) in vec3 positionVS; layout(location=1) in vec3 normalVS; @@ -40,6 +41,9 @@ layout(push_constant) uniform constants { float windTextureLod; float windBendScale; float windWiggleScale; + float uvAnimationX; + float uvAnimationY; + float uvTiling; uint baseColorTextureIdx; uint opacityTextureIdx; uint normalTextureIdx; @@ -47,6 +51,7 @@ layout(push_constant) uniform constants { uint metalnessTextureIdx; uint aoTextureIdx; uint heightTextureIdx; + uint emissiveTextureIdx; } PushConstants; vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir) { @@ -102,7 +107,7 @@ void main() { #endif // Check if usage is valid (otherwise texCoords won't be used) -#if defined(HEIGHT_MAP) && (defined(BASE_COLOR_MAP) || defined(NORMAL_MAP) || defined(ROUGHNESS_MAP) || defined(METALNESS_MAP) || defined(AO_MAP)) +#if defined(HEIGHT_MAP) && (defined(BASE_COLOR_MAP) || defined(NORMAL_MAP) || defined(ROUGHNESS_MAP) || defined(METALNESS_MAP) || defined(AO_MAP) || defined(EMISSIVE_MAP)) vec3 viewDir = normalize(transpose(TBN) * -positionVS); texCoords = ParallaxMapping(texCoords, viewDir); #endif @@ -126,6 +131,11 @@ void main() { baseColorFS *= textureColor.rgb; #endif +#ifdef EMISSIVE_MAP + vec3 emissiveColor = SampleEmissive(texCoords, PushConstants.emissiveTextureIdx, globalData.mipLodBias); + emissiveFS = emissiveColor.rgb; +#endif + #ifdef VERTEX_COLORS baseColorFS *= vertexColorsVS.rgb; #endif diff --git a/data/shader/deferred/geometry.vsh b/data/shader/deferred/geometry.vsh index 37a2f8be9..83d78e1a9 100644 --- a/data/shader/deferred/geometry.vsh +++ b/data/shader/deferred/geometry.vsh @@ -25,11 +25,11 @@ layout(location=3) in vec4 vTangent; layout(location=4) in vec4 vVertexColors; #endif -layout(std430, set = 1, binding = 1) buffer CurrentMatrices { +layout(std430, set = 1, binding = 1) readonly buffer CurrentMatrices { mat3x4 currentMatrices[]; }; -layout(std430, set = 1, binding = 2) buffer LastMatrices { +layout(std430, set = 1, binding = 2) readonly buffer LastMatrices { mat3x4 lastMatrices[]; }; @@ -53,7 +53,7 @@ layout(location=6) out float normalInversionVS; layout(location=7) out mat3 TBN; #endif -layout(set = 3, binding = 7) uniform sampler2D windNoiseMap; +layout(set = 3, binding = 8) uniform sampler2D windNoiseMap; layout(push_constant) uniform constants { uint vegetation; @@ -66,6 +66,9 @@ layout(push_constant) uniform constants { float windTextureLod; float windBendScale; float windWiggleScale; + float uvAnimationX; + float uvAnimationY; + float uvTiling; uint baseColorTextureIdx; uint opacityTextureIdx; uint normalTextureIdx; @@ -73,6 +76,7 @@ layout(push_constant) uniform constants { uint metalnessTextureIdx; uint aoTextureIdx; uint heightTextureIdx; + uint emissiveTextureIdx; } PushConstants; // Functions @@ -84,7 +88,9 @@ void main() { normalInversionVS = determinant(mMatrix) > 0.0 ? 1.0 : -1.0; #ifdef TEX_COORDS - texCoordVS = PushConstants.invertUVs > 0 ? vec2(vTexCoord.x, 1.0 - vTexCoord.y) : vTexCoord; + texCoordVS = PushConstants.invertUVs > 0 ? vec2(vTexCoord.x, 1.0 - vTexCoord.y) : vTexCoord; + texCoordVS += vec2(PushConstants.uvAnimationX, PushConstants.uvAnimationY) * globalData.time; + texCoordVS *= PushConstants.uvTiling; #endif mat4 mvMatrix = globalData.vMatrix * mMatrix; @@ -123,7 +129,7 @@ void main() { #if defined(NORMAL_MAP) || defined(HEIGHT_MAP) vec3 normal = normalize(normalVS); - float correctionFactor = vTangent.w * (PushConstants.invertUVs > 0 ? -1.0 : 1.0); + float correctionFactor = vTangent.w * (PushConstants.invertUVs > 0 ? 1.0 : -1.0); vec3 tangent = normalize(mat3(mvMatrix) * vTangent.xyz); vec3 bitangent = normalize(correctionFactor * diff --git a/data/shader/deferred/indirect.csh b/data/shader/deferred/indirect.csh index eeb5aec3f..52a536d62 100644 --- a/data/shader/deferred/indirect.csh +++ b/data/shader/deferred/indirect.csh @@ -235,8 +235,13 @@ void main() { #else #ifdef DDGI vec3 prefilteredDiffuse = textureLod(diffuseProbe, worldNormal, 0).rgb; +#ifdef DDGI_SCROLL vec4 prefilteredDiffuseLocal = ddgiData.volumeEnabled > 0 ? GetLocalIrradianceInterpolated(worldPosition, worldView, worldNormal, geometryWorldNormal, prefilteredDiffuse) : vec4(0.0, 0.0, 0.0, 1.0); +#else + vec4 prefilteredDiffuseLocal = ddgiData.volumeEnabled > 0 ? + GetLocalIrradiance(worldPosition, worldView, worldNormal, geometryWorldNormal) : vec4(0.0, 0.0, 0.0, 1.0); +#endif prefilteredDiffuseLocal = IsInsideVolume(worldPosition) ? prefilteredDiffuseLocal : vec4(0.0, 0.0, 0.0, 1.0); prefilteredDiffuse = prefilteredDiffuseLocal.rgb + prefilteredDiffuse * prefilteredDiffuseLocal.a; vec3 indirectDiffuse = prefilteredDiffuse * EvaluateIndirectDiffuseBRDF(surface) * ddgiData.volumeStrength; @@ -265,7 +270,13 @@ void main() { #endif indirectSpecular *= EvaluateIndirectSpecularBRDF(surface); +#if !defined(SSGI) || defined(RTGI) indirect = (indirectDiffuse + indirectSpecular) * surface.material.ao; +#else + // This is just there if the new SSGI is enabled + // indirect = (indirectSpecular) * surface.material.ao; + indirect = (indirectDiffuse + indirectSpecular) * surface.material.ao; +#endif #ifdef SSGI vec4 ssgi = Uniforms.giDownsampled2x > 0 ? upsampleResult.gi : textureLod(giTexture, texCoord, 0.0); diff --git a/data/shader/deferred/lightCulling.csh b/data/shader/deferred/lightCulling.csh new file mode 100644 index 000000000..24193c19a --- /dev/null +++ b/data/shader/deferred/lightCulling.csh @@ -0,0 +1,133 @@ +layout (local_size_x = 16, local_size_y = 16) in; + +#include +#include + +#include <../structures.hsh> +#include <../shadow.hsh> +#include <../globals.hsh> + +#include <../common/convert.hsh> +#include <../common/utility.hsh> + +layout(push_constant) uniform constants { + int lightCount; +} pushConstants; + +layout(std430, set = 3, binding = 6) buffer LightBucketsBuffer { + int lightBuckets[]; +}; + +const int groupSize = int(gl_WorkGroupSize.x * gl_WorkGroupSize.y); + +// Results in 32 * 128 = 4096 possible lights in the tile +shared int sharedLightBuckets[lightBucketCount]; +shared uint sharedDepthMin; +shared uint sharedDepthMax; +shared int depthMask; + +void main() { + + ivec2 resolution = textureSize(depthTexture, 0); + ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); + + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(resolution); + + float depth = texelFetch(depthTexture, pixel, 0).r; + float viewDepth = ConvertDepthToViewSpaceDepth(depth); + + if (gl_LocalInvocationIndex == 0u) { + sharedDepthMin = floatBitsToUint(depth); + sharedDepthMax = floatBitsToUint(depth); + depthMask = 0; + } + + int localOffset = int(gl_LocalInvocationIndex); + for (int i = localOffset; i < lightBucketCount; i += groupSize) { + sharedLightBuckets[i] = 0; + } + + barrier(); + + if (depth != 1.0) { + atomicMin(sharedDepthMin, floatBitsToUint(depth)); + atomicMax(sharedDepthMax, floatBitsToUint(depth)); + } + + barrier(); + + float depthMin = ConvertDepthToViewSpaceDepth(uintBitsToFloat(sharedDepthMin)); + float depthMax = ConvertDepthToViewSpaceDepth(uintBitsToFloat(sharedDepthMax)); + + float depthRange = depthMax - depthMin; + float invBinSize = 32.0 / depthRange; + int depthBin = clamp(int((viewDepth - depthMin) * invBinSize), 0, 31); + atomicOr(depthMask, 1 << depthBin); + + barrier(); + + vec3 frustumCorners[8]; + GetFrustumCorners(frustumCorners, resolution, uintBitsToFloat(sharedDepthMin), uintBitsToFloat(sharedDepthMax)); + + Plane frustumPlanes[4]; + GetFrustumPlanes(frustumPlanes, frustumCorners); + + AABB frustumAABB = CalculateAABB(frustumCorners); + + vec3 viewPos = ConvertDepthToViewSpace(depth, texCoord); + + for (int i = localOffset; i < pushConstants.lightCount; i += groupSize) { + int lightBucketIdx = i / 32; + int lightBuckedBit = i % 32; + + bool visible = true; + + Light light = lights[i]; + float radius = light.direction.w; + + uint lightType = floatBitsToUint(light.color.a); + + int startBin = 0; + int endBin = 31; + + Sphere sphere; + + // Remember: Forward is in -z direction + if (lightType == POINT_LIGHT) { + sphere.center = light.location.xyz; + sphere.radius = radius; + + startBin = clamp(int((light.location.z + radius - depthMin) * invBinSize), 0, 31); + endBin = clamp(int((light.location.z - radius - depthMin) * invBinSize), 0, 31); + } + else if (lightType == SPOT_LIGHT) { + sphere = CalculateSphereFromSpotLight(light.location, light.direction, radius); + + startBin = clamp(int((light.location.z + radius - depthMin) * invBinSize), 0, 31); + endBin = clamp(int((light.location.z - radius - depthMin) * invBinSize), 0, 31); + } + + if (lightType != DIRECTIONAL_LIGHT) { + visible = visible && SphereAABBIntersection(sphere, frustumAABB); + if (visible) { + visible = SphereFrustumIntersection(sphere, frustumPlanes); + } + } + + int shiftCount = 31 - endBin + startBin; + int downShift = (0xFFFFFFFF >> shiftCount); + int lightMask = downShift << startBin; + + if (visible && (lightMask & depthMask) != 0) { + atomicOr(sharedLightBuckets[lightBucketIdx], 1 << lightBuckedBit); + } + } + + barrier(); + + int lightBucketOffset = GetLightBucketsGroupOffset(); + for (int i = localOffset; i < lightBucketCount; i += groupSize) { + lightBuckets[i + lightBucketOffset] = sharedLightBuckets[i]; + } + +} \ No newline at end of file diff --git a/data/shader/deferred/lightCulling.hsh b/data/shader/deferred/lightCulling.hsh new file mode 100644 index 000000000..5b2dfff32 --- /dev/null +++ b/data/shader/deferred/lightCulling.hsh @@ -0,0 +1,134 @@ +#include <../structures.hsh> +#include <../common/convert.hsh> +#include <../common/utility.hsh> + +const int lightBucketCount = 128; + +struct AABB { + vec3 center; + vec3 extent; +}; + +struct Sphere { + vec3 center; + float radius; +}; + +struct Plane { + vec3 normal; + float dist; +}; + +int GetLightBucketsGroupOffset() { + + int groupOffset = int(gl_WorkGroupID.x + gl_WorkGroupID.y * gl_NumWorkGroups.x); + return groupOffset * lightBucketCount; + +} + +void GetFrustumCorners(out vec3 frustumCorners[8], ivec2 resolution, float depthMin, float depthMax) { + + ivec2 basePixel = ivec2(gl_WorkGroupID) * ivec2(gl_WorkGroupSize); + vec2 baseTexCoord = vec2(basePixel) / vec2(resolution); + vec2 texCoordSpan = vec2(basePixel + ivec2(gl_WorkGroupSize)) / vec2(resolution) - baseTexCoord; + + frustumCorners[0] = ConvertDepthToViewSpace(depthMin, baseTexCoord + vec2(0.0, 0.0)); + frustumCorners[1] = ConvertDepthToViewSpace(depthMin, baseTexCoord + vec2(texCoordSpan.x, 0.0)); + frustumCorners[2] = ConvertDepthToViewSpace(depthMin, baseTexCoord + vec2(0.0, texCoordSpan.y)); + frustumCorners[3] = ConvertDepthToViewSpace(depthMin, baseTexCoord + vec2(texCoordSpan.x, texCoordSpan.y)); + frustumCorners[4] = ConvertDepthToViewSpace(depthMax, baseTexCoord + vec2(0.0, 0.0)); + frustumCorners[5] = ConvertDepthToViewSpace(depthMax, baseTexCoord + vec2(texCoordSpan.x, 0.0)); + frustumCorners[6] = ConvertDepthToViewSpace(depthMax, baseTexCoord + vec2(0.0, texCoordSpan.y)); + frustumCorners[7] = ConvertDepthToViewSpace(depthMax, baseTexCoord + vec2(texCoordSpan.x, texCoordSpan.y)); + +} + +Plane CalculatePlane(vec3 v0, vec3 v1, vec3 v2) { + + Plane plane; + + v0 = v0 - v1; + v2 = v2 - v1; + + plane.normal = normalize(cross(v2, v0)); + plane.dist = -dot(plane.normal, v1); + + return plane; + +} + +void GetFrustumPlanes(out Plane planes[4], vec3 frustumCorners[8]) { + + planes[0] = CalculatePlane(frustumCorners[7], frustumCorners[6], frustumCorners[2]); + planes[1] = CalculatePlane(frustumCorners[4], frustumCorners[5], frustumCorners[1]); + planes[2] = CalculatePlane(frustumCorners[5], frustumCorners[7], frustumCorners[1]); + planes[3] = CalculatePlane(frustumCorners[6], frustumCorners[4], frustumCorners[0]); + +} + +AABB CalculateAABB(vec3 corners[8]) { + + vec3 aabbMin = corners[0]; + vec3 aabbMax = corners[0]; + + for (int i = 1; i < 8; i++) { + aabbMin = min(corners[i], aabbMin); + aabbMax = max(corners[i], aabbMax); + } + + AABB aabb; + aabb.extent = (aabbMax - aabbMin) * 0.5; + aabb.center = aabbMin + aabb.extent; + return aabb; + +} + +#define COS_PI_QUATER 0.7071067811865475244008443621048490392848359376884740365883398689 + +Sphere CalculateSphereFromSpotLight(vec4 lightLocation, vec4 lightDirection, float radius) { + + Sphere sphere; + + vec2 coneTrig = unpackHalf2x16(floatBitsToUint(lightLocation.w)); + float coneCos = coneTrig.x; + float coneTan = coneTrig.y; + + if (coneCos > COS_PI_QUATER) { + sphere.radius = radius * coneTan; + } + else { + sphere.radius = radius * 0.5 / sqr(coneCos); + } + + sphere.center = lightLocation.xyz + lightDirection.xyz * sphere.radius; + + return sphere; + +} + + +bool SphereAABBIntersection(Sphere sphere, AABB aabb) { + + vec3 diff = max(vec3(0.0), abs(aabb.center - sphere.center) - aabb.extent); + return dot(diff, diff) <= sphere.radius * sphere.radius; + +} + +bool SphereBehindPlane(Sphere sphere, Plane plane) { + + return plane.dist + dot(plane.normal, sphere.center) > -sphere.radius; + +} + +bool SphereFrustumIntersection(Sphere sphere, Plane planes[4]) { + + bool intersect = true; + + intersect = intersect && SphereBehindPlane(sphere, planes[0]); + intersect = intersect && SphereBehindPlane(sphere, planes[1]); + intersect = intersect && SphereBehindPlane(sphere, planes[2]); + intersect = intersect && SphereBehindPlane(sphere, planes[3]); + + return intersect; + +} \ No newline at end of file diff --git a/data/shader/deferred/point.fsh b/data/shader/deferred/point.fsh deleted file mode 100644 index b8a107239..000000000 --- a/data/shader/deferred/point.fsh +++ /dev/null @@ -1,151 +0,0 @@ -#include "../structures" -#include <../common/convert.hsh> -#include <../common/material.hsh> - -in vec3 fTexCoordProj; -in vec3 viewSpacePosition; -out vec4 fragColor; - -layout(binding = 0) uniform sampler2D diffuseTexture; -layout(binding = 1) uniform sampler2D normalTexture; -layout(binding = 2) uniform sampler2D geometryNormalTexture; -layout(binding = 3) uniform sampler2D specularTexture; -layout(binding = 4) uniform usampler2D materialIdxTexture; -layout(binding = 5) uniform sampler2D depthTexture; -layout(binding = 6) uniform samplerCubeShadow shadowCubemap; - -uniform Light light; -uniform vec3 viewSpaceLightLocation; -uniform mat4 lvMatrix; -uniform mat4 lpMatrix; - -uniform bool shadowEnabled; - -// Improved filtering: https://kosmonautblog.wordpress.com/2017/03/25/shadow-filtering-for-pointlights/ - -vec3 sampleOffsetDirections[20] = vec3[] -( - vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), - vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), - vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), - vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), - vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) -); - -void main() { - - vec2 texCoord = ((fTexCoordProj.xy / fTexCoordProj.z) + 1.0) / 2.0; - - float depth = texture(depthTexture, texCoord).r; - - vec3 fragPos = ConvertDepthToViewSpace(depth, texCoord); - - if (fragPos.z - viewSpaceLightLocation.z > light.radius) - discard; - - vec3 fragToLight = viewSpaceLightLocation.xyz - fragPos.xyz; - float fragToLightDistance = length(fragToLight); - - uint materialIdx = texture(materialIdxTexture, texCoord).r; - Material material = UnpackMaterial(materialIdx); - - vec3 normal = normalize(2.0 * texture(normalTexture, texCoord).rgb - 1.0); - vec3 reconstructedNormal = normalize(2.0 * texture(geometryNormalTexture, texCoord).rgb - 1.0); - - normal = material.normalMap ? normal : reconstructedNormal; - - vec3 surfaceColor = texture(diffuseTexture, texCoord).rgb; - - // Specular properties - float specularIntensity = 0.0; - float specularHardness = 1.0; - - if (material.specularMap) { - vec2 specularProp = texture(specularTexture, texCoord).rg; - specularIntensity = max(specularProp.r, 0.0); - specularHardness = max(specularProp.g, 1.0); - } - else { - specularIntensity = material.specularIntensity; - specularHardness = material.specularHardness; - } - - float shadowFactor = 0.0; - - vec3 specular = vec3(0.0); - vec3 diffuse = vec3(1.0); - vec3 ambient = vec3(light.ambient * surfaceColor); - vec3 volumetric = 0.0 * vec3(light.color); - - float occlusionFactor = 1.0; - - vec3 lightDir = fragToLight / fragToLightDistance; - - vec4 lsPosition = lvMatrix * vec4(fragPos, 1.0); - vec4 absPosition = abs(lsPosition); - depth = -max(absPosition.x, max(absPosition.y, absPosition.z)); - vec4 clip = lpMatrix * vec4(0.0, 0.0, depth, 1.0); - depth = (clip.z - 0.005) / clip.w * 0.5 + 0.5; - - int samples = 20; - float diskRadius = 0.0075; - - if (shadowEnabled) { - for(int i = 0; i < samples; i++) { - shadowFactor += clamp(texture(shadowCubemap, vec4(lsPosition.xyz + sampleOffsetDirections[i] * diskRadius, depth)), 0.0, 1.0); - } - shadowFactor /= float(samples + 1); - } - - diffuse = max(dot(normal, lightDir), 0.0) * light.color * shadowFactor * surfaceColor; - - fragColor = vec4(max((diffuse + ambient) * (light.radius - fragToLightDistance) / light.radius, 0.0) + volumetric, 1.0); - -} - - - - -float ComputeScattering(vec3 fragPos) { - - // We also need to consider that we shouldn't use the radius of the sphere when we're inside - // the volume but should use the distance of the viewSpacePosition to the camera. - const int sampleCount = 100; - const float offset = 0.5f; // Because of geometry getting cut off by near plane (offset = 2 * nearPlane) - - float positionLength = length(viewSpacePosition); - float fragLength = length(fragPos); - - vec3 rayDirection = viewSpacePosition / positionLength; - - vec3 rayEnd = vec3(viewSpacePosition.xyz) - rayDirection * offset / 2.0f; - - vec3 rayStart = positionLength < light.radius - offset / 2.0f ? vec3(0.0f) : - vec3(viewSpacePosition.xyz) - rayDirection * (light.radius - offset / 2.0f); - - float rayLength = distance(rayStart, rayEnd); - - float stepLength = rayLength / float(sampleCount); - vec3 stepVector = rayDirection * stepLength; - - float foginess = 0.0f; - float scattering = 0.15f; - - vec3 currentPosition = vec3(rayStart); - - for (int i = 0; i < sampleCount; i++) { - - // Normally we should take a shadow map into consideration - float fragToLightDistance = length(viewSpaceLightLocation - currentPosition); - float shadowValue = fragToLightDistance < light.radius ? 1.0f : 0.0f; - shadowValue = fragLength < length(currentPosition) ? 0.0f : shadowValue; - - foginess += max(scattering * shadowValue * (light.radius - fragToLightDistance - 0.25f) / (light.radius- 0.25f), 0.0f); - - currentPosition += stepVector; - - } - - return foginess / float(sampleCount); - -} \ No newline at end of file diff --git a/data/shader/deferred/point.vsh b/data/shader/deferred/point.vsh deleted file mode 100644 index 8064b9fd8..000000000 --- a/data/shader/deferred/point.vsh +++ /dev/null @@ -1,21 +0,0 @@ -#include "../structures" - -layout(location=0)in vec3 vPosition; - -out vec3 fTexCoordProj; -out vec3 viewSpacePosition; - -uniform mat4 vMatrix; -uniform mat4 pMatrix; -uniform Light light; - -void main() { - - vec4 position = vMatrix * vec4(vPosition * light.radius + light.location, 1.0); - - viewSpacePosition = position.xyz; - gl_Position = pMatrix * position; - - fTexCoordProj = gl_Position.xyw; - -} \ No newline at end of file diff --git a/data/shader/deferred/sss.csh b/data/shader/deferred/sss.csh index f7cf29c28..4b1d1af7a 100644 --- a/data/shader/deferred/sss.csh +++ b/data/shader/deferred/sss.csh @@ -8,8 +8,6 @@ #include <../common/utility.hsh> #include <../common/normalencode.hsh> -#define TRACE_WORLD_SPACE - layout (local_size_x = 8, local_size_y = 8) in; layout(set = 3, binding = 0, r16f) uniform image2D image; @@ -20,6 +18,7 @@ layout(push_constant) uniform constants { vec4 lightDirection; int sampleCount; float maxLength; + float minLengthWorldSpace; float thickness; } pushConstants; @@ -63,23 +62,28 @@ void main() { vec2 texCoord = (vec2(pixel) + 0.5) / vec2(resolution); - vec3 rayPos = ConvertDepthToViewSpace(depth, texCoord); + vec3 rayPos = ConvertDepthToViewSpace(depth, texCoord) + normal * 0.01; float startDepth = -rayPos.z; vec3 rayDir = normalize(-pushConstants.lightDirection.xyz); float stepLength = pushConstants.maxLength / (float(pushConstants.sampleCount)); #ifdef TRACE_WORLD_SPACE - float depthThickness = pushConstants.thickness; - vec3 rayEndPos = rayPos + pushConstants.maxLength * rayDir; + stepLength *= 100.0; + float depthThickness = pushConstants.thickness * 100.0; + vec3 rayEndPos = rayPos + pushConstants.maxLength * 100.0 * rayDir; #else float depthThickness = pushConstants.thickness; vec2 stepPos = PosToUV(rayPos + rayDir); float stepPosLength = length(stepPos - texCoord); - vec3 rayEndPos = rayPos + (pushConstants.maxLength / max(0.01, stepPosLength)) * rayDir; - depthThickness *= stepPosLength; - depthThickness = max(abs(rayPos.z), abs(rayPos.z - rayEndPos.z)) * pushConstants.thickness; - vec2 uvDir = normalize(stepPos - texCoord); + float worldSpaceRayLength = (pushConstants.maxLength / max(0.000001, stepPosLength)); + + float worldSpaceCorrection = clamp(pushConstants.minLengthWorldSpace / worldSpaceRayLength, 1.0, 1000.0); + + vec3 rayEndPos = rayPos + worldSpaceRayLength * worldSpaceCorrection * rayDir; + stepLength *= worldSpaceCorrection; + + depthThickness = max(abs(rayPos.z), abs(rayPos.z - rayEndPos.z)) * pushConstants.thickness * worldSpaceCorrection; #endif float resultDelta = 0.0; @@ -90,15 +94,12 @@ void main() { float rayLength = distance(rayPos, rayEndPos); - //depthThickness = abs(rayPos.z - rayEndPos.z) * stepLength; - vec2 uvPos = texCoord; float noiseOffset = GetInterleavedGradientNoise(texCoord * vec2(resolution)); #ifdef TRACE_WORLD_SPACE rayPos += noiseOffset * stepLength * rayDir; #else - uvPos += noiseOffset * stepLength * uvDir; rayPos += noiseOffset * stepLength * rayDir / stepPosLength; #endif @@ -113,8 +114,11 @@ void main() { offset.xyz /= offset.w; uvPos = offset.xy * 0.5 + 0.5; #else - uvPos += uvDir * stepLength; rayPos += rayDir * stepLength / stepPosLength; + + vec4 offset = globalData.pMatrix * vec4(rayPos, 1.0); + offset.xyz /= offset.w; + uvPos = offset.xy * 0.5 + 0.5; #endif if (uvPos.x < 0.0 || uvPos.x > 1.0 || uvPos.y < 0.0 || uvPos.y > 1.0) continue; @@ -133,7 +137,7 @@ void main() { // Check if the camera can't "see" the ray (ray depth must be larger than the camera depth, so positive depth_delta) if (depthDelta > 0.0 && depthDelta < depthThickness && - depthDelta < 0.5 * rayLength && depthDelta > 0.2 * depthThickness) { + depthDelta < 0.5 * rayLength) { // Mark as occluded occlusion = 1.0; resultDelta = depthDelta; diff --git a/data/shader/deferred/texture.hsh b/data/shader/deferred/texture.hsh index 55fe7b9b7..c225188af 100644 --- a/data/shader/deferred/texture.hsh +++ b/data/shader/deferred/texture.hsh @@ -22,102 +22,119 @@ layout(set = 3, binding = 5) uniform sampler2D aoMap; #ifdef HEIGHT_MAP layout(set = 3, binding = 6) uniform sampler2D heightMap; #endif +#ifdef EMISSIVE_MAP +layout(set = 3, binding = 7) uniform sampler2D emissiveMap; +#endif #endif vec3 SampleBaseColor(vec2 texCoords, uint textureID, float bias) { +#ifdef BASE_COLOR_MAP #ifdef BINDLESS_TEXTURES return texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).rgb; #else -#ifdef BASE_COLOR_MAP return texture(baseColorMap, texCoords, bias).rgb; -#else - return vec3(0.0); #endif +#else + return vec3(1.0); #endif } float SampleOpacity(vec2 texCoords, uint textureID, float bias) { +#ifdef OPACITY_MAP #ifdef BINDLESS_TEXTURES return texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).r; #else -#ifdef OPACITY_MAP return texture(opacityMap, texCoords, bias).r; +#endif #else return 1.0; #endif -#endif } vec3 SampleNormal(vec2 texCoords, uint textureID, float bias) { +#ifdef NORMAL_MAP #ifdef BINDLESS_TEXTURES return 2.0 * texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).rgb - 1.0; #else -#ifdef NORMAL_MAP return 2.0 * texture(normalMap, texCoords, bias).rgb - 1.0; +#endif #else return vec3(0.0); #endif -#endif } float SampleRoughness(vec2 texCoords, uint textureID, float bias) { +#ifdef ROUGHNESS_MAP #ifdef BINDLESS_TEXTURES return texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).r; #else -#ifdef ROUGHNESS_MAP return texture(roughnessMap, texCoords, bias).r; +#endif #else return 1.0; #endif -#endif } float SampleMetalness(vec2 texCoords, uint textureID, float bias) { +#ifdef METALNESS_MAP #ifdef BINDLESS_TEXTURES return texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).r; #else -#ifdef METALNESS_MAP return texture(metalnessMap, texCoords, bias).r; +#endif #else return 0.0; #endif -#endif } float SampleAo(vec2 texCoords, uint textureID, float bias) { +#ifdef AO_MAP #ifdef BINDLESS_TEXTURES return texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).r; #else -#ifdef AO_MAP return texture(aoMap, texCoords, bias).r; +#endif #else return 1.0; #endif -#endif } float SampleHeight(vec2 texCoords, uint textureID, vec2 ddx, vec2 ddy) { +#ifdef HEIGHT_MAP #ifdef BINDLESS_TEXTURES return textureGrad(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, ddx, ddy).r; #else -#ifdef HEIGHT_MAP return textureGrad(heightMap, texCoords, ddx, ddy).r; +#endif #else return 0.0; #endif + +} + +vec3 SampleEmissive(vec2 texCoords, uint textureID, float bias) { + +#ifdef EMISSIVE_MAP +#ifdef BINDLESS_TEXTURES + return texture(sampler2D(bindlessTextures[textureID], bindlessSampler), texCoords, bias).rgb; +#else + return texture(emissiveMap, texCoords, bias).rgb; +#endif +#else + return vec3(0.0); #endif } \ No newline at end of file diff --git a/data/shader/gbuffer/downsampleGBuffer2x.csh b/data/shader/gbuffer/downsampleGBuffer2x.csh index 3e15a4e92..6ed30fa5f 100644 --- a/data/shader/gbuffer/downsampleGBuffer2x.csh +++ b/data/shader/gbuffer/downsampleGBuffer2x.csh @@ -20,12 +20,18 @@ layout (set = 3, binding = 10, rg16f) writeonly uniform image2D velocityOut; layout (set = 3, binding = 11, r16ui) writeonly uniform uimage2D materialIdxOut; layout (set = 3, binding = 12, r8i) writeonly uniform iimage2D offsetOut; -float Checkerboard(ivec2 coord) { +float Checkerboard2x(ivec2 coord) { return float((coord.x + coord.y % 2) % 2); } +float Checkerboard4x(ivec2 coord) { + + return float((coord.x + coord.y % 4) % 4); + +} + int MinDepth(vec4 depthVec, out float minDepth) { int idx = 0; @@ -64,7 +70,7 @@ int MaxDepth(vec4 depthVec, out float maxDepth) { int CheckerboardDepth(vec4 depthVec, ivec2 coord, out float depth) { - float minmax = 0.0; + float minmax = Checkerboard2x(coord); float maxDepth; int maxIdx = MaxDepth(depthVec, maxDepth); @@ -90,8 +96,9 @@ void main() { float depth11 = texelFetch(depthIn, coord * 2 + ivec2(1, 1), 0).r; vec4 depthVec = vec4(depth00, depth10, depth01, depth11); - int depthIdx = CheckerboardDepth(depthVec, coord, depthVec.x); - //depthIdx = 0; + //int depthIdx = (CheckerboardDepth(depthVec, coord, depthVec.x) + int(globalData.frameCount)) % 4; + int depthIdx = int(globalData.frameCount) % 4; + //int depthIdx = (int(Checkerboard4x(coord)) + int(globalData.frameCount)) % 4; float depth = depthVec[depthIdx]; imageStore(depthOut, coord, vec4(depth, 0.0, 0.0, 1.0)); diff --git a/data/shader/gbuffer/generateReactiveMask.csh b/data/shader/gbuffer/generateReactiveMask.csh index f2128fe8c..2c9124dac 100644 --- a/data/shader/gbuffer/generateReactiveMask.csh +++ b/data/shader/gbuffer/generateReactiveMask.csh @@ -4,6 +4,7 @@ layout (local_size_x = 8, local_size_y = 8) in; layout (set = 3, binding = 0, r8) uniform image2D reactiveMaskImage; layout (set = 3, binding = 1) uniform usampler2D stencilTexture; +layout (set = 3, binding = 2) uniform sampler2D roughnessMetalnessAoTexture; void main() { @@ -18,6 +19,10 @@ void main() { StencilFeatures features = DecodeStencilFeatures(texelFetch(stencilTexture, pixel, 0).r); reactivity = features.responsivePixel ? 0.8 : reactivity; + vec3 roughnessMetallicAo = texelFetch(roughnessMetalnessAoTexture, pixel, 0).rgb; + float reflectiveReactivity = min(1.0, 5.0 * roughnessMetallicAo.r); + //reactivity = max(reactivity, mix(1.0, 0.0, reflectiveReactivity)); + imageStore(reactiveMaskImage, pixel, vec4(reactivity, 0.0, 0.0, 0.0)); } diff --git a/data/shader/gbuffer/patchGBufferNormals.csh b/data/shader/gbuffer/patchGBuffer.csh similarity index 50% rename from data/shader/gbuffer/patchGBufferNormals.csh rename to data/shader/gbuffer/patchGBuffer.csh index d92c25f0b..355295dc7 100644 --- a/data/shader/gbuffer/patchGBufferNormals.csh +++ b/data/shader/gbuffer/patchGBuffer.csh @@ -3,8 +3,9 @@ layout (local_size_x = 8, local_size_y = 8) in; layout (set = 3, binding = 0, rg16f) uniform image2D normalImage; -layout (set = 3, binding = 1) uniform sampler2D geometryNormalTexture; -layout (set = 3, binding = 2) uniform usampler2D materialIdxTexture; +layout (set = 3, binding = 1, rgba16f) uniform image2D roughnessMetalnessAoImage; +layout (set = 3, binding = 2) uniform sampler2D geometryNormalTexture; +layout (set = 3, binding = 3) uniform usampler2D materialIdxTexture; void main() { @@ -15,6 +16,8 @@ void main() { coord.y < size.y) { vec2 normal = imageLoad(normalImage, coord).rg; + vec3 roughnessMetalnessAo = imageLoad(roughnessMetalnessAoImage, coord).rgb; + vec2 geometryNormal = texelFetch(geometryNormalTexture, coord, 0).rg; uint materialIdx = texelFetch(materialIdxTexture, coord, 0).r; @@ -22,7 +25,12 @@ void main() { normal = material.normalMap ? normal : geometryNormal; + roughnessMetalnessAo.r = material.roughnessMap ? roughnessMetalnessAo.r * material.roughness : material.roughness; + roughnessMetalnessAo.g = material.metalnessMap ? roughnessMetalnessAo.g * material.metalness : material.metalness; + roughnessMetalnessAo.b = material.aoMap ? roughnessMetalnessAo.b * material.ao : material.ao; + imageStore(normalImage, coord, vec4(normal, 0.0, 0.0)); + imageStore(roughnessMetalnessAoImage, coord, vec4(roughnessMetalnessAo, 0.0)); } diff --git a/data/shader/globals.hsh b/data/shader/globals.hsh index 73d7bccbc..bcfbbcee0 100644 --- a/data/shader/globals.hsh +++ b/data/shader/globals.hsh @@ -1,7 +1,11 @@ #ifdef AE_BINDLESS #define TEXTURE_COUNT 16384 +#define CUBEMAP_COUNT 1024 +#define TEXTURE_ARRAY_COUNT 128 #else #define TEXTURE_COUNT 1 +#define CUBEMAP_COUNT 1 +#define TEXTURE_ARRAY_COUNT 1 #endif layout(set = 1, binding = 31, std140) uniform GlobalBuffer { @@ -14,6 +18,7 @@ layout(set = 1, binding = 31, std140) uniform GlobalBuffer { mat4 pvMatrixCurrent; mat4 ipvMatrixLast; mat4 ipvMatrixCurrent; + mat4 vMatrixLast; vec2 jitterLast; vec2 jitterCurrent; vec4 cameraLocation; @@ -28,11 +33,16 @@ layout(set = 1, binding = 31, std140) uniform GlobalBuffer { float deltaTime; uint frameCount; float mipLodBias; + float cameraNearPlane; + float cameraFarPlane; } globalData; layout(set = 0, binding = 3) uniform texture2D bindlessTextures[TEXTURE_COUNT]; +layout(set = 0, binding = 4) uniform textureCube bindlessCubemaps[CUBEMAP_COUNT]; +layout(set = 0, binding = 5) uniform texture2DArray bindlessTextureArrays[TEXTURE_ARRAY_COUNT]; -layout(set = 1, binding = 12) uniform sampler2D dfgTexture; +layout(set = 1, binding = 13) uniform sampler2D dfgTexture; -layout(set = 1, binding = 13) uniform sampler bindlessSampler; -layout(set = 1, binding = 15) uniform sampler bindlessNearestSampler; \ No newline at end of file +layout(set = 1, binding = 14) uniform sampler bindlessSampler; +layout(set = 1, binding = 16) uniform sampler bindlessNearestSampler; +layout(set = 1, binding = 17) uniform sampler bindlessClampToEdgeSampler; \ No newline at end of file diff --git a/data/shader/impostor/impostor.fsh b/data/shader/impostor/impostor.fsh index 494b6ab55..a2f1b7d08 100644 --- a/data/shader/impostor/impostor.fsh +++ b/data/shader/impostor/impostor.fsh @@ -5,8 +5,9 @@ layout (location = 0) out vec4 baseColorFS; layout (location = 1) out vec2 normalFS; layout (location = 2) out vec2 geometryNormalFS; layout (location = 3) out vec3 roughnessMetalnessAoFS; -layout (location = 4) out uint materialIdxFS; -layout (location = 5) out vec2 velocityFS; +layout (location = 4) out vec3 emissiveFS; +layout (location = 5) out uint materialIdxFS; +layout (location = 6) out vec2 velocityFS; layout(location=0) in vec3 positionVS; layout(location=1) in vec2 texCoordVS; diff --git a/data/shader/impostor/impostor.vsh b/data/shader/impostor/impostor.vsh index 18cf7a6b4..da85ffb7b 100644 --- a/data/shader/impostor/impostor.vsh +++ b/data/shader/impostor/impostor.vsh @@ -10,11 +10,11 @@ struct ViewPlane { vec4 up; }; -layout(std430, set = 1, binding = 3) buffer Matrices { +layout(std430, set = 1, binding = 3) readonly buffer Matrices { mat3x4 matrices[]; }; -layout (std430, set = 3, binding = 4) buffer ViewPlanes { +layout (std430, set = 3, binding = 4) readonly buffer ViewPlanes { ViewPlane viewPlanes[]; }; diff --git a/data/shader/impostor/impostorShadow.vsh b/data/shader/impostor/impostorShadow.vsh index 71e8a3dd2..1cec78ef4 100644 --- a/data/shader/impostor/impostorShadow.vsh +++ b/data/shader/impostor/impostorShadow.vsh @@ -26,11 +26,11 @@ struct ViewPlane { vec4 up; }; -layout(std430, set = 1, binding = 3) buffer Matrices { +layout(std430, set = 1, binding = 3) readonly buffer Matrices { mat3x4 matrices[]; }; -layout (std430, set = 3, binding = 1) buffer ViewPlanes { +layout (std430, set = 3, binding = 1) readonly buffer ViewPlanes { ViewPlane viewPlanes[]; }; diff --git a/data/shader/ocean/caustics.csh b/data/shader/ocean/caustics.csh index 597eeeca2..ffac818b8 100644 --- a/data/shader/ocean/caustics.csh +++ b/data/shader/ocean/caustics.csh @@ -1,8 +1,8 @@ layout (local_size_x = 8, local_size_y = 8) in; -#define SHADOW_FILTER_3x3 +#define SHADOW_FILTER_VOGEL -#include <../structures> +#include <../structures.hsh> #include <../shadow.hsh> #include <../globals.hsh> #include <../common/convert.hsh> diff --git a/data/shader/ocean/ocean.fsh b/data/shader/ocean/ocean.fsh index 0803a5f4e..3986fae49 100644 --- a/data/shader/ocean/ocean.fsh +++ b/data/shader/ocean/ocean.fsh @@ -8,7 +8,7 @@ #include <../common/normalreconstruction.hsh> #include <../clouds/shadow.hsh> #include <../volumetric/volumetric.hsh> -#include <../structures> +#include <../structures.hsh> #include @@ -101,7 +101,7 @@ void main() { #ifdef CLOUD_SHADOWS float cloudShadowFactor = CalculateCloudShadow(fPosition, cloudShadowUniforms.cloudShadow, cloudShadowMap); - shadowFactor = min(shadowFactor, cloudShadowFactor); + shadowFactor *= cloudShadowFactor; #endif shadowFactor = max(shadowFactor, 0.01); diff --git a/data/shader/ocean/sharedUniforms.hsh b/data/shader/ocean/sharedUniforms.hsh index 2ed613413..f70ffedec 100644 --- a/data/shader/ocean/sharedUniforms.hsh +++ b/data/shader/ocean/sharedUniforms.hsh @@ -1,5 +1,5 @@ #include <../globals.hsh> -#include <../structures> +#include <../structures.hsh> #include <../volumetric/fog.hsh> layout (set = 3, binding = 11, std140) uniform UniformBuffer { @@ -43,10 +43,6 @@ layout (set = 3, binding = 12, std140) uniform LightUniformBuffer { Light light; } LightUniforms; -layout (set = 3, binding = 14, std140) uniform CloudShadowUniformBuffer { - CloudShadow cloudShadow; -} cloudShadowUniforms; - layout(push_constant) uniform constants { float nodeSideLength; float tileScale; diff --git a/data/shader/ocean/underwater.csh b/data/shader/ocean/underwater.csh index bfbd3d327..c414e48cc 100644 --- a/data/shader/ocean/underwater.csh +++ b/data/shader/ocean/underwater.csh @@ -1,11 +1,11 @@ layout (local_size_x = 8, local_size_y = 8) in; -#define SHADOW_FILTER_3x3 +#define SHADOW_FILTER_VOGEL #include #include -#include <../structures> +#include <../structures.hsh> #include <../shadow.hsh> #include <../globals.hsh> #include <../common/convert.hsh> diff --git a/data/shader/pathtracer/rayGen.csh b/data/shader/pathtracer/rayGen.csh index eaa857fa5..149546f46 100644 --- a/data/shader/pathtracer/rayGen.csh +++ b/data/shader/pathtracer/rayGen.csh @@ -4,6 +4,7 @@ #include <../raytracer/buffers.hsh> #include <../raytracer/tracing.hsh> #include <../common/random.hsh> +#include <../common/convert.hsh> #include <../common/flatten.hsh> layout (local_size_x = 8, local_size_y = 8) in; @@ -28,6 +29,11 @@ void main() { int(gl_GlobalInvocationID.y) < Uniforms.tileSize.y) { ivec2 pixel = ivec2(gl_GlobalInvocationID.xy) + Uniforms.pixelOffset; + + Ray ray; + + ray.ID = Flatten2D(pixel, Uniforms.resolution) * int(gl_NumWorkGroups.z) + int(gl_WorkGroupID.z); + ray.hitID = 0; // Apply a subpixel jitter to get supersampling float jitterX = random(vec2(float(Uniforms.sampleCount), 0.0)); @@ -35,20 +41,17 @@ void main() { #ifndef REALTIME vec2 coord = (vec2(pixel) + vec2(jitterX, jitterY)) / vec2(float(Uniforms.resolution.x), float(Uniforms.resolution.y)); -#else - vec2 coord = globalData.jitterCurrent * 0.25 + (vec2(pixel) + vec2(0.5)) / - vec2(float(Uniforms.resolution.x), float(Uniforms.resolution.y)); -#endif - - Ray ray; - - ray.ID = Flatten2D(pixel, Uniforms.resolution) * int(gl_NumWorkGroups.z) + int(gl_WorkGroupID.z); - ray.direction = normalize(Uniforms.origin.xyz + Uniforms.right.xyz * coord.x + Uniforms.bottom.xyz * coord.y - globalData.cameraLocation.xyz); ray.origin = globalData.cameraLocation.xyz; +#else + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(Uniforms.resolution); + vec3 nearPosition = vec3(globalData.ivMatrix * vec4(ConvertDepthToViewSpace(0.0, texCoord), 1.0)); + vec3 farPosition = vec3(globalData.ivMatrix * vec4(ConvertDepthToViewSpace(1.0, texCoord), 1.0)); - ray.hitID = 0; + ray.origin = nearPosition; + ray.direction = normalize(farPosition - nearPosition); +#endif // Calculate number of potential overlapping pixels at the borders of a tile ivec2 overlappingPixels = Uniforms.tileSize % ivec2(gl_WorkGroupSize); diff --git a/data/shader/pathtracer/rayHit.csh b/data/shader/pathtracer/rayHit.csh index 674b7a69c..932828e45 100644 --- a/data/shader/pathtracer/rayHit.csh +++ b/data/shader/pathtracer/rayHit.csh @@ -102,20 +102,21 @@ void main() { vec4 viewSpacePos = globalData.vMatrix * vec4(position, 1.0); vec4 projPositionCurrent = globalData.pMatrix * viewSpacePos; - vec4 projPositionLast = globalData.pvMatrixLast * vec4(lastPos, 1.0); + vec4 projPositionLast = globalData.pMatrix * globalData.vMatrixLast * vec4(lastPos, 1.0); vec2 ndcCurrent = projPositionCurrent.xy / projPositionCurrent.w; vec2 ndcLast = projPositionLast.xy / projPositionLast.w; vec2 velocity = (ndcLast - ndcCurrent) * 0.5; + imageStore(velocityImage, pixel, vec4(velocity, 0.0, 0.0)); - imageStore(depthImage, pixel, vec4(ray.hitID >= 0 ? viewSpacePos.z : INF, 0.0, 0.0, 0.0)); + imageStore(depthImage, pixel, vec4(ray.hitID >= 0 ? projPositionCurrent.z / projPositionCurrent.w : INF, 0.0, 0.0, 0.0)); imageStore(materialIdxImage, pixel, uvec4(materialId, 0.0, 0.0, 0.0)); - imageStore(normalImage, pixel, vec4(EncodeNormal(geometryNormal), 0.0, 0.0)); + imageStore(normalImage, pixel, vec4(EncodeNormal(geometryNormal), 0.0, 0.0)); } #endif - + if (energy == 0 || Uniforms.bounceCount == Uniforms.maxBounces) { #ifndef REALTIME if (Uniforms.sampleCount > 0) @@ -170,17 +171,24 @@ Surface EvaluateBounce(inout Ray ray, inout RayPayload payload) { payload.throughput = vec3(0.0); return surface; } + + int lod = int(ray.hitDistance / 30.0); + lod = 0; // Unpack the compressed triangle and extract surface parameters Instance instance = GetInstance(ray); Triangle tri = GetTriangle(ray, instance); - surface = GetSurfaceParameters(instance, tri, ray, true, 0); + surface = GetSurfaceParameters(instance, tri, ray, true, lod); // If we hit an emissive surface we need to terminate the ray +#ifndef REALTIME if (dot(surface.material.emissiveColor, vec3(1.0)) > 0.0 && Uniforms.bounceCount == 0) { payload.radiance += surface.material.emissiveColor; } +#else + payload.radiance += payload.throughput * surface.material.emissiveColor; +#endif // Evaluate direct and indirect light vec3 radiance = payload.throughput * surface.material.opacity @@ -228,7 +236,8 @@ vec3 EvaluateDirectLight(inout Surface surface) { // Check for visibilty. This is important to get an // estimate of the solid angle of the light from point P // on the surface. - radiance *= CheckVisibility(surface, lightDistance); + if (light.castShadow) + radiance *= CheckVisibility(surface, lightDistance); return reflectance * radiance * surface.NdotL / lightPdf; diff --git a/data/shader/pathtracer/temporal.csh b/data/shader/pathtracer/temporal.csh index a7a2883b5..9401b866e 100644 --- a/data/shader/pathtracer/temporal.csh +++ b/data/shader/pathtracer/temporal.csh @@ -11,7 +11,7 @@ layout (local_size_x = 16, local_size_y = 16) in; -layout (set = 3, binding = 0, rgba8) writeonly uniform image2D resolveImage; +layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D outputImage; layout (set = 3, binding = 1, r32ui) readonly uniform uimage2DArray frameAccumImage; layout (set = 3, binding = 2) uniform sampler2D inAccumImage; layout (set = 3, binding = 3, rgba32f) writeonly uniform image2D outAccumImage; @@ -34,8 +34,8 @@ layout(push_constant) uniform constants { float maxRadiance; } pushConstants; -vec2 invResolution = 1.0 / vec2(imageSize(resolveImage)); -vec2 resolution = vec2(imageSize(resolveImage)); +vec2 invResolution = 1.0 / vec2(imageSize(outAccumImage)); +vec2 resolution = vec2(imageSize(outAccumImage)); const int kernelRadius = 5; @@ -105,8 +105,8 @@ void LoadGroupSharedData() { texel = clamp(texel, ivec2(0), ivec2(resolution) - ivec2(1)); - sharedRadianceDepth[i].rgb = FetchTexel(texel); - sharedRadianceDepth[i].a = texelFetch(depthTexture, texel, 0).r; + sharedRadianceDepth[i].rgb = RGBToYCoCg(FetchTexel(texel)); + sharedRadianceDepth[i].a = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, texel, 0).r); } barrier(); @@ -140,7 +140,7 @@ ivec2 FindNearest3x3(ivec2 pixel) { for (int i = 0; i < 9; i++) { ivec2 offsetPixel = clamp(pixel + offsets[i], ivec2(0), ivec2(resolution) - ivec2(1)); - float currDepth = texelFetch(depthTexture, offsetPixel, 0).r; + float currDepth = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, offsetPixel, 0).r); if (currDepth < depth) { depth = currDepth; offset = offsets[i]; @@ -182,7 +182,7 @@ float IsHistoryPixelValid(ivec2 pixel, float linearDepth, uint materialIdx, vec3 confidence *= historyMaterialIdx != materialIdx ? 0.0 : 1.0; float depthPhi = 16.0 / abs(linearDepth); - float historyDepth = texelFetch(historyDepthTexture, pixel, 0).r; + float historyDepth = ConvertDepthToViewSpaceDepth(texelFetch(historyDepthTexture, pixel, 0).r); float historyLinearDepth = historyDepth; confidence *= min(1.0 , exp(-abs(linearDepth - historyLinearDepth) * depthPhi)); @@ -203,7 +203,7 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, vec2 velocity, out vec4 histo uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); - float depth = texelFetch(depthTexture, pixel, 0).r; + float depth = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, pixel, 0).r); float linearDepth = depth; float depthPhi = 16.0 / abs(linearDepth); @@ -258,7 +258,7 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, vec2 velocity, out vec4 histo vec4 GetCatmullRomSample(ivec2 pixel, inout float weight, float linearDepth, uint materialIdx, vec3 normal) { - pixel = clamp(pixel, ivec2(0), ivec2(imageSize(resolveImage) - 1)); + pixel = clamp(pixel, ivec2(0), ivec2(imageSize(outAccumImage) - 1)); weight *= IsHistoryPixelValid(pixel, linearDepth, materialIdx, normal); @@ -275,7 +275,7 @@ bool SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; vec3 normal = DecodeNormal(texelFetch(normalTexture, pixel, 0).rg); - float depth = texelFetch(depthTexture, pixel, 0).r; + float depth = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, pixel, 0).r); vec2 position = uv * resolution; @@ -333,7 +333,8 @@ void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { const int radius = kernelRadius; ivec2 pixel = ivec2(gl_GlobalInvocationID); - float depth = texelFetch(depthTexture, pixel, 0).r; + float depth = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, pixel, 0).r); + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; float linearDepth = depth; float totalWeight = 0.0; @@ -342,11 +343,15 @@ void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { for (int j = -radius; j <= radius; j++) { int sharedMemoryIdx = GetSharedMemoryIndex(ivec2(i, j)); - vec3 sampleRadiance = RGBToYCoCg(FetchCurrentRadiance(sharedMemoryIdx)); + vec3 sampleRadiance = FetchCurrentRadiance(sharedMemoryIdx); float sampleLinearDepth = FetchDepth(sharedMemoryIdx); - float depthPhi = max(1.0, abs(0.025 * linearDepth)); - float weight = min(1.0 , exp(-abs(linearDepth - sampleLinearDepth) / depthPhi)); + uint sampleMaterialIdx = texelFetch(materialIdxTexture, pixel + ivec2(i, j), 0).r; + + float depthPhi = 16.0 / abs(linearDepth); + float weight = min(1.0 , exp(-abs(linearDepth - sampleLinearDepth) * depthPhi)); + + weight = sampleMaterialIdx != materialIdx ? 0.0 : weight; m1 += sampleRadiance * weight; m2 += sampleRadiance * sampleRadiance * weight; @@ -364,11 +369,11 @@ void main() { LoadGroupSharedData(); ivec2 pixel = ivec2(gl_GlobalInvocationID); - if (pixel.x > imageSize(resolveImage).x || - pixel.y > imageSize(resolveImage).y) + if (pixel.x > imageSize(outAccumImage).x || + pixel.y > imageSize(outAccumImage).y) return; - uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; ivec2 offset = FindNearest3x3(pixel); @@ -424,9 +429,21 @@ void main() { factor = min(factor, historyLength / (historyLength + 1.0)); + /* + const vec3 luma = vec3(0.299, 0.587, 0.114); + float weightHistory = factor / (1.0 + dot(historyRadiance.rgb, luma)); + float weightCurrent = (1.0 - factor) / (1.0 + dot(currentRadiance.rgb, luma)); + + vec3 resolve = historyRadiance.rgb * weightHistory + + currentRadiance.rgb * weightCurrent; + + resolve /= (weightHistory + weightCurrent); + */ + vec3 resolve = mix(currentRadiance, historyRadiance, factor); imageStore(outAccumImage, pixel, vec4(resolve, historyLength + 1.0)); + imageStore(outputImage, pixel, vec4(resolve, 1.0)); //imageStore(outAccumImage, pixel, vec4(vec3(valid ? 1.0 : 0.0), historyLength + 1.0)); //imageStore(outAccumImage, pixel, vec4(vec3(historyLength / 10.0), historyLength + 1.0)); diff --git a/data/shader/postprocessing.fsh b/data/shader/postprocessing.fsh index 08cd7a055..30fddb3ed 100644 --- a/data/shader/postprocessing.fsh +++ b/data/shader/postprocessing.fsh @@ -7,9 +7,11 @@ layout (location = 0) out vec4 outColor; layout (location = 0) in vec2 positionVS; layout(set = 3, binding = 0) uniform sampler2D hdrTexture; -layout(set = 3, binding = 1) uniform sampler2D bloomFirstTexture; -layout(set = 3, binding = 2) uniform sampler2D bloomSecondTexture; -layout(set = 3, binding = 3) uniform sampler2D bloomThirdTexture; +layout(set = 3, binding = 1) uniform sampler2D bloomTexture; + +#ifdef BLOOM_DIRT +layout(set = 3, binding = 2) uniform sampler2D bloomDirtTexture; +#endif layout(set = 3, binding = 4) uniform UniformBuffer { float exposure; @@ -18,12 +20,16 @@ layout(set = 3, binding = 4) uniform UniformBuffer { float saturation; float contrast; float filmGrainStrength; - int bloomPasses; + float bloomStrength; + float bloomDirtStrength; float aberrationStrength; float aberrationReversed; float vignetteOffset; float vignettePower; float vignetteStrength; + float padding0; + float padding1; + float padding2; vec4 vignetteColor; vec4 tintColor; } Uniforms; @@ -89,52 +95,38 @@ void main() { vec2 texCoord = 0.5 * positionVS + 0.5; vec3 color = vec3(0.0); + vec3 bloom = vec3(0.0); + #ifdef CHROMATIC_ABERRATION - vec2 uvRedChannel = (positionVS - positionVS * 0.005f * Uniforms.aberrationStrength - * Uniforms.aberrationReversed) * 0.5f + 0.5f; - vec2 uvGreenChannel = (positionVS - positionVS * 0.0025f * Uniforms.aberrationStrength) * 0.5f + 0.5f; - vec2 uvBlueChannel = (positionVS - positionVS * 0.005f * Uniforms.aberrationStrength - * (1.0f - Uniforms.aberrationReversed)) * 0.5f + 0.5f; + vec2 uvRedChannel = (positionVS - positionVS * 0.005 * Uniforms.aberrationStrength + * Uniforms.aberrationReversed) * 0.5 + 0.5; + vec2 uvGreenChannel = (positionVS - positionVS * 0.0025 * Uniforms.aberrationStrength) * 0.5 + 0.5; + vec2 uvBlueChannel = (positionVS - positionVS * 0.005 * Uniforms.aberrationStrength + * (1.0 - Uniforms.aberrationReversed)) * 0.5 + 0.5; + + color.r = textureLod(hdrTexture, uvRedChannel, 0.0).r; + color.g = textureLod(hdrTexture, uvGreenChannel, 0.0).g; + color.b = textureLod(hdrTexture, uvBlueChannel, 0.0).b; - color.r = texture(hdrTexture, uvRedChannel).r; - color.g = texture(hdrTexture, uvGreenChannel).g; - color.b = texture(hdrTexture, uvBlueChannel).b; #ifdef BLOOM - // We want to keep a constant expression in texture[const] - // because OpenGL ES doesn't support dynamic texture fetches - // inside a loop - if (Uniforms.bloomPasses > 0) { - color.r += texture(bloomFirstTexture, uvRedChannel).r; - color.g += texture(bloomFirstTexture, uvGreenChannel).g; - color.b += texture(bloomFirstTexture, uvBlueChannel).b; - } - if (Uniforms.bloomPasses > 1) { - color.r += texture(bloomSecondTexture, uvRedChannel).r; - color.g += texture(bloomSecondTexture, uvGreenChannel).g; - color.b += texture(bloomSecondTexture, uvBlueChannel).b; - } - if (Uniforms.bloomPasses > 2) { - color.r += texture(bloomThirdTexture, uvRedChannel).r; - color.g += texture(bloomThirdTexture, uvGreenChannel).g; - color.b += texture(bloomThirdTexture, uvBlueChannel).b; - } + bloom.r += Uniforms.bloomStrength * textureLod(bloomTexture, uvRedChannel, 0.0).r; + bloom.g += Uniforms.bloomStrength * textureLod(bloomTexture, uvGreenChannel, 0.0).g; + bloom.b += Uniforms.bloomStrength * textureLod(bloomTexture, uvBlueChannel, 0.0).b; #endif #else - color = texture(hdrTexture, texCoord).rgb; + color = textureLod(hdrTexture, texCoord, 0.0).rgb; #ifdef BLOOM - if (Uniforms.bloomPasses > 0) { - color += texture(bloomFirstTexture, texCoord).rgb; - } - if (Uniforms.bloomPasses > 1) { - color += texture(bloomSecondTexture, texCoord).rgb; - } - if (Uniforms.bloomPasses > 2) { - color += texture(bloomThirdTexture, texCoord).rgb; - } + bloom += Uniforms.bloomStrength * textureLod(bloomTexture, texCoord, 0.0).rgb; #endif #endif +#ifdef BLOOM_DIRT + bloom = Uniforms.bloomDirtStrength * textureLod(bloomDirtTexture, texCoord, 0.0).r * bloom + bloom; +#endif + + color += bloom; + color *= Uniforms.exposure; #ifdef FILM_GRAIN @@ -187,10 +179,10 @@ void main() { color = ((color - 0.5) * max(Uniforms.contrast, 0.0)) + 0.5; #ifdef VIGNETTE - float vignetteFactor = max(1.0 - max(pow(length(fPosition) - Uniforms.vignetteOffset, + float vignetteFactor = max(1.0 - max(pow(length(positionVS) - Uniforms.vignetteOffset, Uniforms.vignettePower), 0.0) * Uniforms.vignetteStrength, 0.0); - color = mix(Uniforms.vignetteColor.rgb, color, Uniforms.vignetteFactor); + color = mix(Uniforms.vignetteColor.rgb, color, vignetteFactor); #endif outColor = vec4(color, 1.0); diff --git a/data/shader/raytracer/common.hsh b/data/shader/raytracer/common.hsh index 1b1d888a7..3cfdb5b6e 100644 --- a/data/shader/raytracer/common.hsh +++ b/data/shader/raytracer/common.hsh @@ -6,10 +6,12 @@ #include <../common/octahedron.hsh> #define INF 1000000000000.0 -#define EPSILON 0.1 +#define EPSILON 0.001 #define DIRECTIONAL_LIGHT 0 #define TRIANGLE_LIGHT 1 +#define POINT_LIGHT 2 +#define SPOT_LIGHT 3 #define INSTANCE_MASK_ALL (1 << 7) #define INSTANCE_MASK_SHADOW (1 << 6) @@ -102,18 +104,25 @@ Light UnpackLight(PackedLight compressed) { Light light; light.P = compressed.P.xyz; - light.N = compressed.N.xyz; + light.N = normalize(compressed.N.xyz); light.radiance = compressed.color.rgb; - uint data = floatBitsToUint(compressed.data.x); - light.type = ((data & 0xF0000000u) >> 28u); + uint data0 = floatBitsToUint(compressed.data.x); + uint data1 = floatBitsToUint(compressed.P.w); - light.triangleIdx = int(data & 0x0FFFFFFFu); + light.type = ((data0 & 0xF0000000u) >> 28u); + + light.triangleIdx = int(data0 & 0x0FFFFFFFu); light.instanceIdx = floatBitsToInt(compressed.data.w); + light.castShadow = data1 > 0u; + light.pdf = compressed.data.y; - light.area = compressed.data.z; + light.area = compressed.N.w; + light.radius = compressed.N.w; + light.angleScale = compressed.data.z; + light.angleOffset = compressed.data.w; light.brightness = dot(light.radiance, vec3(0.33333)); return light; diff --git a/data/shader/raytracer/direct.hsh b/data/shader/raytracer/direct.hsh index ec3e32a43..22d190681 100644 --- a/data/shader/raytracer/direct.hsh +++ b/data/shader/raytracer/direct.hsh @@ -24,7 +24,7 @@ Light GetLight(Surface surface, float seed0, float seed1, out float lightPdf) { float sqrDistance = dot(pointToLight, pointToLight); float lightDistance = sqrt(sqrDistance); - vec3 L = normalize(pointToLight); + vec3 L = pointToLight / lightDistance; float NdotL = max(dot(light.N, -L), 0.0); weight = light.brightness * light.area * NdotL / sqrDistance; @@ -32,7 +32,25 @@ Light GetLight(Surface surface, float seed0, float seed1, out float lightPdf) { else if (light.type == uint(DIRECTIONAL_LIGHT)) { weight = light.brightness; } - weight = clamp(weight, 0.0000001, 1.0); + else if (light.type == uint(POINT_LIGHT)) { + vec3 pointToLight = light.P - surface.P; + float sqrDistance = dot(pointToLight, pointToLight); + float lightDistance = sqrt(sqrDistance); + + weight = light.brightness * light.radius / sqrDistance; + } + else if (light.type == uint(SPOT_LIGHT)) { + vec3 pointToLight = light.P - surface.P; + float sqrDistance = dot(pointToLight, pointToLight); + float lightDistance = sqrt(sqrDistance); + + vec3 L = pointToLight / lightDistance; + float NdotL = max(dot(light.N, -L), 0.0); + + weight = light.brightness * NdotL / sqrDistance; + } + + weight = clamp(weight, 0.0000001, 1000000000000.0); totalWeight += weight; float rnd = random(seed0, seed1); @@ -78,9 +96,33 @@ void SampleLight(Light light, inout Surface surface, float seed0, float seed1, else { // Punctual lights solidAngle = 1.0; if (light.type == uint(DIRECTIONAL_LIGHT)) { - surface.L = normalize(-light.N); + surface.L = -light.N; UpdateSurface(surface); + solidAngle = 1.0; dist = INF; } + else if (light.type == uint(POINT_LIGHT)) { + vec3 pointToLight = light.P - surface.P; + float sqrDistance = dot(pointToLight, pointToLight); + dist = sqrt(sqrDistance); + + float attenuation = saturate(1.0 - pow(dist / light.radius, 4.0)); + solidAngle = attenuation / sqrDistance; + + surface.L = pointToLight / dist; + UpdateSurface(surface); + } + else if (light.type == uint(SPOT_LIGHT)) { + vec3 pointToLight = light.P - surface.P; + float sqrDistance = dot(pointToLight, pointToLight); + dist = sqrt(sqrDistance); + surface.L = pointToLight / dist; + + float strength = dot(surface.L, -light.N); + float attenuation = saturate(strength * light.angleScale + light.angleOffset); + solidAngle = sqr(attenuation) / sqrDistance; + + UpdateSurface(surface); + } } } \ No newline at end of file diff --git a/data/shader/raytracer/structures.hsh b/data/shader/raytracer/structures.hsh index 7d2a31254..c40ed6e63 100644 --- a/data/shader/raytracer/structures.hsh +++ b/data/shader/raytracer/structures.hsh @@ -122,7 +122,10 @@ struct RaytraceMaterial { float reflectance; float normalScale; - + + float tiling; + float terrainTiling; + int invertUVs; int twoSided; int cullBackFaces; @@ -134,6 +137,10 @@ struct RaytraceMaterial { int roughnessTexture; int metalnessTexture; int aoTexture; + int emissiveTexture; + int terrainNormalTexture; + + int padding; }; @@ -154,8 +161,13 @@ struct Light { int triangleIdx; int instanceIdx; + bool castShadow; + float pdf; float area; + float radius; + float angleScale; + float angleOffset; float brightness; }; diff --git a/data/shader/raytracer/surface.hsh b/data/shader/raytracer/surface.hsh index 546b6c0fe..ec95b1772 100644 --- a/data/shader/raytracer/surface.hsh +++ b/data/shader/raytracer/surface.hsh @@ -9,19 +9,19 @@ Instance GetInstance(Ray ray) { - return bvhInstances[ray.hitInstanceID]; + return bvhInstances[nonuniformEXT(ray.hitInstanceID)]; } Triangle GetTriangle(Ray ray, Instance instance) { - return UnpackTriangle(triangles[nonuniformEXT(instance.meshOffset)].data[ray.hitID]); + return UnpackTriangle(triangles[nonuniformEXT(instance.meshOffset)].data[nonuniformEXT(ray.hitID)]); } Material GetTriangleMaterial(Triangle tri, int materialOffset, out RaytraceMaterial rayMat) { Material mat; - rayMat = materials[tri.materialIndex + materialOffset]; + rayMat = materials[nonuniformEXT(tri.materialIndex + materialOffset)]; mat.ID = rayMat.ID; @@ -87,8 +87,6 @@ Surface GetSurfaceParameters(Instance instance, Triangle tri, Ray ray, vec3 normal = normalize(r * tri.n0 + s * tri.n1 + t * tri.n2); vec4 vertexColor = r * tri.color0 + s * tri.color1 + t * tri.color2; - texCoord = rayMat.invertUVs > 0 ? vec2(texCoord.x, 1.0 - texCoord.y) : texCoord; - // Produces some problems in the bottom left corner of the Sponza scene, // but fixes the cube. Should work in theory. vec3 triangleNormal = normalize(cross(tri.v0 - tri.v1, tri.v0 - tri.v2)); @@ -98,6 +96,16 @@ Surface GetSurfaceParameters(Instance instance, Triangle tri, Ray ray, vec3 geometryNormal = triangleNormal; + if (rayMat.terrainNormalTexture >= 0) { + vec2 terrainTexCoord = texCoord * rayMat.terrainTiling; + terrainTexCoord = rayMat.invertUVs > 0 ? vec2(terrainTexCoord.x, 1.0 - terrainTexCoord.y) : terrainTexCoord; + geometryNormal = normal; + normal = normalize(2.0 * SampleTerrainNormalBilinear(rayMat.terrainNormalTexture, 0.0, terrainTexCoord) - 1.0); + } + + texCoord = rayMat.invertUVs > 0 ? vec2(texCoord.x, 1.0 - texCoord.y) : texCoord; + texCoord *= rayMat.tiling; + int level = textureLevel; //mat.opacity *= vertexColor.a; mat.baseColor *= rayMat.useVertexColors > 0 ? vertexColor.rgb : vec3(1.0); @@ -107,6 +115,8 @@ Surface GetSurfaceParameters(Instance instance, Triangle tri, Ray ray, mat.roughness *= SampleRoughnessBilinear(rayMat.roughnessTexture, float(level), texCoord); mat.metalness *= SampleMetalnessBilinear(rayMat.metalnessTexture, float(level), texCoord); mat.ao *= SampleAoBilinear(rayMat.aoTexture, float(level), texCoord); + // Need to have the texture level to go to near zero, otherwise we will have light leaks + mat.emissiveColor *= SampleEmissiveBilinear(rayMat.emissiveTexture, max(0.0, float(level - 3.0)), texCoord); // Account for changing texture coord direction vec3 bitangent = rayMat.invertUVs > 0 ? tri.bt : -tri.bt; @@ -131,8 +141,7 @@ Surface GetSurfaceParameters(Instance instance, Triangle tri, Ray ray, surface.geometryNormal = geometryNormal; - surface.F0 = mix(vec3(0.04), surface.material.baseColor, - surface.material.metalness); + surface.F0 = mix(vec3(0.04), surface.material.baseColor, surface.material.metalness); surface.F90 = 1.0; return surface; @@ -155,6 +164,6 @@ float GetOpacity(Triangle tri, vec2 barrycentric, int materialOffset, int textur vec2 texCoord = r * tri.uv0 + s * tri.uv1 + t * tri.uv2; texCoord = rayMat.invertUVs > 0 ? vec2(texCoord.x, 1.0 - texCoord.y) : texCoord; - return SampleOpacityBilinear(rayMat.opacityTexture, float(textureLevel), texCoord) + return SampleOpacityBilinear(rayMat.opacityTexture, float(textureLevel), texCoord * rayMat.tiling) * rayMat.opacity; } \ No newline at end of file diff --git a/data/shader/raytracer/texture.hsh b/data/shader/raytracer/texture.hsh index 16e7b7ffd..591c858ea 100644 --- a/data/shader/raytracer/texture.hsh +++ b/data/shader/raytracer/texture.hsh @@ -60,6 +60,25 @@ float SampleAoBilinear(int textureId, float mip, vec2 texCoord) { } +vec3 SampleEmissiveBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) + return vec3(0.0); + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessSampler), texCoord, mip).rgb; + +} + +vec3 SampleTerrainNormalBilinear(int textureId, float mip, vec2 texCoord) { + + if (textureId < 0) + return vec3(1.0); + + return textureLod(sampler2D(bindlessTextures[nonuniformEXT(textureId)], bindlessClampToEdgeSampler), texCoord, mip).rgb; + +} + + vec4 SampleEnvironmentMap(vec3 v) { return textureLod(environmentMap, v, 0); diff --git a/data/shader/reflection/atrous.csh b/data/shader/reflection/atrous.csh index cc7eded9f..2f12ae863 100644 --- a/data/shader/reflection/atrous.csh +++ b/data/shader/reflection/atrous.csh @@ -1,3 +1,5 @@ +#include <../common/types.hsh> + #include <../common/utility.hsh> #include <../common/convert.hsh> #include <../common/flatten.hsh> @@ -36,7 +38,8 @@ struct PixelData { struct PackedPixelData { // Contains 16 bit color, variance, normal and roughness - uvec4 data; + f16vec4 color; + f16vec4 data; float depth; }; @@ -52,10 +55,9 @@ const float kernelWeights[3] = { 1.0, 2.0 / 3.0, 1.0 / 6.0 }; PackedPixelData PackPixelData(PixelData data) { PackedPixelData compressed; - compressed.data.x = packHalf2x16(data.color.rg); - compressed.data.y = packHalf2x16(data.color.ba); - compressed.data.z = packHalf2x16(data.normal.xy); - compressed.data.w = packHalf2x16(vec2(data.normal.z, data.roughness)); + compressed.color.rgba = f16vec4(data.color); + compressed.data.xyz = f16vec3(data.normal); + compressed.data.w = float16_t(data.roughness); compressed.depth = data.depth; return compressed; @@ -64,12 +66,9 @@ PackedPixelData PackPixelData(PixelData data) { PixelData UnpackPixelData(PackedPixelData compressed) { PixelData data; - data.color.rg = unpackHalf2x16(compressed.data.x); - data.color.ba = unpackHalf2x16(compressed.data.y); - data.normal.xy = unpackHalf2x16(compressed.data.z); - vec2 temp = unpackHalf2x16(compressed.data.w); - data.normal.z = temp.x; - data.roughness = temp.y; + data.color.rgba = compressed.color.rgba; + data.normal.xyz = compressed.data.rgb; + data.roughness = compressed.data.a; data.depth = compressed.depth; return data; @@ -92,12 +91,7 @@ void LoadGroupSharedData() { data.color = texelFetch(inputTexture, texel, 0); data.depth = texelFetch(depthTexture, texel, 0).r; - - uint materialIdx = texelFetch(materialIdxTexture, texel, 0).r; - Material material = UnpackMaterial(materialIdx); - - data.roughness = material.roughness; - data.roughness *= material.roughnessMap ? texelFetch(roughnessTexture, texel, 0).r : 1.0; + data.roughness = texelFetch(roughnessTexture, texel, 0).r; data.normal = DecodeNormal(texelFetch(normalTexture, texel, 0).rg); @@ -243,6 +237,7 @@ void main() { outputColor = outputColor / vec4(vec3(totalWeight), totalWeight * totalWeight); + imageStore(outputImage, pixel, vec4(centerColor.a)); imageStore(outputImage, pixel, outputColor); } \ No newline at end of file diff --git a/data/shader/reflection/rtreflection.csh b/data/shader/reflection/rtreflection.csh index 66efc078f..ed4578f0f 100644 --- a/data/shader/reflection/rtreflection.csh +++ b/data/shader/reflection/rtreflection.csh @@ -20,10 +20,11 @@ #include <../ddgi/ddgi.hsh> #include <../shadow.hsh> +#include <../clouds/shadow.hsh> layout (local_size_x = 8, local_size_y = 4) in; -layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D rtrImage; +layout (set = 3, binding = 0, rgba16f) uniform image2D rtrImage; layout(set = 3, binding = 1) uniform sampler2D normalTexture; layout(set = 3, binding = 2) uniform sampler2D depthTexture; @@ -35,6 +36,10 @@ layout(set = 3, binding = 6) uniform sampler2DArrayShadow cascadeMaps; layout(set = 3, binding = 7) uniform sampler2D scramblingRankingTexture; layout(set = 3, binding = 8) uniform sampler2D sobolSequenceTexture; +#ifdef CLOUD_SHADOWS +layout(set = 3, binding = 9) uniform sampler2D cloudMap; +#endif + const ivec2 offsets[4] = ivec2[4]( ivec2(0, 0), ivec2(1, 0), @@ -42,19 +47,23 @@ const ivec2 offsets[4] = ivec2[4]( ivec2(1, 1) ); -layout(std140, set = 3, binding = 9) uniform UniformBuffer { +layout(std140, set = 3, binding = 10) uniform UniformBuffer { float radianceLimit; uint frameSeed; float bias; + int sampleCount; + int lightSampleCount; int textureLevel; float roughnessCutoff; int halfRes; + int padding0; + int padding1; ivec2 resolution; Shadow shadow; } uniforms; vec3 EvaluateHit(inout Ray ray); -vec3 EvaluateDirectLight(inout Surface surface); +vec3 EvaluateDirectLight(inout Surface surface, inout float seed); float CheckVisibility(Surface surface, float lightDistance); void main() { @@ -68,6 +77,12 @@ void main() { vec2 texCoord = (vec2(pixel) + vec2(0.5)) / vec2(resolution); +#ifdef SSR + vec4 reflection = imageLoad(rtrImage, pixel); +#else + vec4 reflection = vec4(0.0); +#endif + // No need, there is no offset right now int offsetIdx = texelFetch(offsetTexture, pixel, 0).r; ivec2 offset = offsets[offsetIdx]; @@ -78,86 +93,93 @@ void main() { if (uniforms.halfRes > 0) recontructTexCoord = (2.0 * (vec2(pixel)) + offset + 0.5) / (2.0 * vec2(resolution)); else - recontructTexCoord = (vec2(pixel) + 0.5) / (vec2(resolution)); + recontructTexCoord = (2.0 * (vec2(pixel)) + offset + 0.5) / (2.0 * vec2(resolution)); vec3 viewPos = ConvertDepthToViewSpace(depth, recontructTexCoord); vec3 worldPos = vec3(globalData.ivMatrix * vec4(viewPos, 1.0)); vec3 viewVec = vec3(globalData.ivMatrix * vec4(viewPos, 0.0)); vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); - int sampleIdx = int(uniforms.frameSeed); - vec2 blueNoiseVec = vec2( - SampleBlueNoise(pixel, sampleIdx, 0, scramblingRankingTexture, sobolSequenceTexture), - SampleBlueNoise(pixel, sampleIdx, 1, scramblingRankingTexture, sobolSequenceTexture) - ); - uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; Material material = UnpackMaterial(materialIdx); float roughness = texelFetch(roughnessMetallicAoTexture, pixel, 0).r; - material.roughness *= material.roughnessMap ? roughness : 1.0; + material.roughness *= material.roughnessMap ? roughness : 1.0; - vec3 reflection = vec3(0.0); + if (material.roughness <= 1.0 && depth < 1.0 && reflection.a == 0.0) { - if (material.roughness <= 1.0 && depth < 1.0) { + const int sampleCount = uniforms.sampleCount; - float alpha = sqr(material.roughness); + for (int i = 0; i < sampleCount; i++) { + int sampleIdx = int(uniforms.frameSeed) * sampleCount + i; + vec2 blueNoiseVec = vec2( + SampleBlueNoise(pixel, sampleIdx, 0, scramblingRankingTexture, sobolSequenceTexture), + SampleBlueNoise(pixel, sampleIdx, 1, scramblingRankingTexture, sobolSequenceTexture) + ); - vec3 V = normalize(-viewVec); - vec3 N = worldNorm; + float alpha = sqr(max(0.0, material.roughness)); - Surface surface = CreateSurface(V, N, vec3(1.0), material); + vec3 V = normalize(-viewVec); + vec3 N = worldNorm; - Ray ray; - blueNoiseVec.y *= (1.0 - uniforms.bias); + Surface surface = CreateSurface(V, N, vec3(1.0), material); - float pdf = 1.0; - BRDFSample brdfSample; - if (material.roughness > 0.01) { - ImportanceSampleGGXVNDF(blueNoiseVec, N, V, alpha, - ray.direction, pdf); - } - else { - ray.direction = normalize(reflect(-V, N)); - } + Ray ray; + ray.ID = i; + blueNoiseVec.y *= (1.0 - uniforms.bias); + + float pdf = 1.0; + BRDFSample brdfSample; + if (material.roughness >= 0.0) { + ImportanceSampleGGXVNDF(blueNoiseVec, N, V, alpha, + ray.direction, pdf); + } + else { + ray.direction = normalize(reflect(-V, N)); + } - bool isRayValid = !isnan(ray.direction.x) || !isnan(ray.direction.y) || - !isnan(ray.direction.z) || dot(N, ray.direction) >= 0.0; + bool isRayValid = !isnan(ray.direction.x) || !isnan(ray.direction.y) || + !isnan(ray.direction.z) || dot(N, ray.direction) >= 0.0; - if (isRayValid) { - // Scale offset by depth since the depth buffer inaccuracies increase at a distance and might not match the ray traced geometry anymore - float viewOffset = max(1.0, length(viewPos)) * 0.1; - ray.origin = worldPos + ray.direction * EPSILON * 0.1 * viewOffset + worldNorm * EPSILON * viewOffset * 0.1; + if (isRayValid) { + // Scale offset by depth since the depth buffer inaccuracies increase at a distance and might not match the ray traced geometry anymore + float viewOffset = max(1.0, length(viewPos)); + ray.origin = worldPos + ray.direction * EPSILON * viewOffset + worldNorm * EPSILON * viewOffset; - ray.hitID = -1; - ray.hitDistance = 0.0; + ray.hitID = -1; + ray.hitDistance = 0.0; - vec3 radiance = vec3(0.0); + vec3 radiance = vec3(0.0); - if (material.roughness <= uniforms.roughnessCutoff) { - #ifdef OPACITY_CHECK - HitClosestTransparency(ray, INSTANCE_MASK_ALL, 0.0, INF); - #else - HitClosest(ray, INSTANCE_MASK_ALL, 0.0, INF); - #endif + if (material.roughness <= uniforms.roughnessCutoff) { +#ifdef OPACITY_CHECK + HitClosestTransparency(ray, INSTANCE_MASK_ALL, 0.0, INF); +#else + HitClosest(ray, INSTANCE_MASK_ALL, 0.0, INF); +#endif - radiance = EvaluateHit(ray); - } - else { - #ifdef DDGI - radiance = GetLocalIrradiance(worldPos, V, N).rgb; - radiance = IsInsideVolume(worldPos) ? radiance : vec3(0.0); - #endif - } + radiance = EvaluateHit(ray); + } + else { +#ifdef DDGI + radiance = GetLocalIrradiance(worldPos, V, N).rgb; + radiance = IsInsideVolume(worldPos) ? radiance : vec3(0.0); +#endif + } - float radianceMax = max(max(max(radiance.r, - max(radiance.g, radiance.b)), uniforms.radianceLimit), 0.01); - reflection = radiance * (uniforms.radianceLimit / radianceMax); + float radianceMax = max(max(max(radiance.r, + max(radiance.g, radiance.b)), uniforms.radianceLimit), 0.01); + reflection.rgb += radiance * (uniforms.radianceLimit / radianceMax); + } } + reflection.rgb /= float(sampleCount); + + //reflection.rgb = vec3(0.0); + } - imageStore(rtrImage, pixel, vec4(reflection, 1.0)); + imageStore(rtrImage, pixel, vec4(reflection.rgb, 0.0)); } } @@ -178,39 +200,44 @@ vec3 EvaluateHit(inout Ray ray) { bool backfaceHit; Surface surface = GetSurfaceParameters(instance, tri, ray, false, backfaceHit, uniforms.textureLevel); - - radiance += surface.material.emissiveColor; - - // Evaluate direct light - radiance += EvaluateDirectLight(surface); // Evaluate indirect lighting #ifdef DDGI vec3 irradiance = GetLocalIrradiance(surface.P, surface.V, surface.N).rgb; // Approximate indirect specular for ray by using the irradiance grid // This enables metallic materials to have some kind of secondary reflection + + surface.NdotV = saturate(dot(surface.N, surface.V)); vec3 indirect = EvaluateIndirectDiffuseBRDF(surface) * irradiance + EvaluateIndirectSpecularBRDF(surface) * irradiance; - radiance += IsInsideVolume(surface.P) ? indirect * ddgiData.volumeStrength : vec3(0.0); + radiance += IsInsideVolume(surface.P) ? indirect : vec3(0.0); #endif + + radiance += surface.material.emissiveColor; + + float curSeed = float(uniforms.frameSeed) / 255.0 + float(ray.ID) * float(uniforms.sampleCount); + // Evaluate direct light + for (int i = 0; i < uniforms.lightSampleCount; i++) { + radiance += (EvaluateDirectLight(surface, curSeed) / float(uniforms.lightSampleCount)); + curSeed += 1.0 / float(uniforms.lightSampleCount); + } return radiance; } -vec3 EvaluateDirectLight(inout Surface surface) { +vec3 EvaluateDirectLight(inout Surface surface, inout float seed) { if (GetLightCount() == 0) return vec3(0.0); - - float curSeed = float(uniforms.frameSeed) / 255.0; - float raySeed = float(gl_GlobalInvocationID.x); + + float raySeed = float(gl_GlobalInvocationID.x * uniforms.resolution.y + gl_GlobalInvocationID.y); float lightPdf; - Light light = GetLight(surface, raySeed, curSeed, lightPdf); + Light light = GetLight(surface, raySeed, seed, lightPdf); float solidAngle, lightDistance; - SampleLight(light, surface, raySeed, curSeed, solidAngle, lightDistance); + SampleLight(light, surface, raySeed, seed, solidAngle, lightDistance); // Evaluate the BRDF vec3 reflectance = EvaluateDiffuseBRDF(surface) + EvaluateSpecularBRDF(surface); @@ -224,7 +251,17 @@ vec3 EvaluateDirectLight(inout Surface surface) { radiance *= CalculateShadowWorldSpace(uniforms.shadow, cascadeMaps, surface.P, surface.geometryNormal, saturate(dot(surface.L, surface.geometryNormal))); #else - radiance *= CheckVisibility(surface, lightDistance); + if (light.castShadow) + radiance *= CheckVisibility(surface, lightDistance); +#endif + +#ifdef CLOUD_SHADOWS + if (light.type == uint(DIRECTIONAL_LIGHT)) { + vec3 P = vec3(globalData.vMatrix * vec4(surface.P, 1.0)); + float cloudShadowFactor = CalculateCloudShadow(P, cloudShadowUniforms.cloudShadow, cloudMap); + + radiance *= cloudShadowFactor; + } #endif return reflectance * radiance * surface.NdotL / lightPdf; diff --git a/data/shader/reflection/ssr.csh b/data/shader/reflection/ssr.csh new file mode 100644 index 000000000..14a4c70f0 --- /dev/null +++ b/data/shader/reflection/ssr.csh @@ -0,0 +1,174 @@ +#define SHADOW_FILTER_1x1 + +#include <../globals.hsh> +#include <../raytracer/lights.hsh> +#include <../raytracer/tracing.hsh> +#include <../raytracer/direct.hsh> + +#include <../common/random.hsh> +#include <../common/utility.hsh> +#include <../common/flatten.hsh> +#include <../common/convert.hsh> +#include <../common/normalencode.hsh> +#include <../common/PI.hsh> +#include <../common/bluenoise.hsh> +#include <../common/traceScreenSpace.hsh> + +#include <../brdf/brdfEval.hsh> +#include <../brdf/brdfSample.hsh> +#include <../brdf/importanceSample.hsh> +#include <../brdf/surface.hsh> + +#include <../ddgi/ddgi.hsh> +#include <../shadow.hsh> + +layout (local_size_x = 8, local_size_y = 8) in; + +layout (set = 3, binding = 0, rgba16f) writeonly uniform image2D rtrImage; + +layout(set = 3, binding = 1) uniform sampler2D normalTexture; +layout(set = 3, binding = 2) uniform sampler2D depthTexture; +layout(set = 3, binding = 3) uniform sampler2D roughnessMetallicAoTexture; +layout(set = 3, binding = 4) uniform isampler2D offsetTexture; +layout(set = 3, binding = 5) uniform usampler2D materialIdxTexture; +layout(set = 3, binding = 6) uniform sampler2DArrayShadow cascadeMaps; + +layout(set = 3, binding = 7) uniform sampler2D scramblingRankingTexture; +layout(set = 3, binding = 8) uniform sampler2D sobolSequenceTexture; + +layout(set = 3, binding = 9) uniform sampler2D lightingTexture; + +const ivec2 offsets[4] = ivec2[4]( + ivec2(0, 0), + ivec2(1, 0), + ivec2(0, 1), + ivec2(1, 1) +); + +layout(std140, set = 3, binding = 10) uniform UniformBuffer { + float radianceLimit; + uint frameSeed; + float bias; + int sampleCount; + int lightSampleCount; + int textureLevel; + float roughnessCutoff; + int halfRes; + int padding0; + int padding1; + ivec2 resolution; + Shadow shadow; +} uniforms; + +void main() { + + ivec2 resolution = uniforms.resolution; + + if (int(gl_GlobalInvocationID.x) < resolution.x && + int(gl_GlobalInvocationID.y) < resolution.y) { + + ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); + + vec2 texCoord = (vec2(pixel) + vec2(0.5)) / vec2(resolution); + + // No need, there is no offset right now + int offsetIdx = texelFetch(offsetTexture, pixel, 0).r; + ivec2 offset = offsets[offsetIdx]; + + float depth = texelFetch(depthTexture, pixel, 0).r; + + vec2 recontructTexCoord; + if (uniforms.halfRes > 0) + recontructTexCoord = (2.0 * (vec2(pixel)) + offset + 0.5) / (2.0 * vec2(resolution)); + else + recontructTexCoord = (vec2(pixel) + 0.5) / (vec2(resolution)); + + vec3 viewPos = ConvertDepthToViewSpace(depth, recontructTexCoord); + vec3 worldPos = vec3(globalData.ivMatrix * vec4(viewPos, 1.0)); + vec3 viewVec = vec3(globalData.ivMatrix * vec4(viewPos, 0.0)); + vec3 viewNormal = normalize(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg)); + vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(viewNormal, 0.0))); + + uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; + Material material = UnpackMaterial(materialIdx); + + float roughness = texelFetch(roughnessMetallicAoTexture, pixel, 0).r; + material.roughness *= material.roughnessMap ? roughness : 1.0; + + vec3 reflection = vec3(0.0); + float hits = 0.0; + + if (material.roughness <= 1.0 && depth < 1.0) { + + const int sampleCount = uniforms.sampleCount; + + for (int i = 0; i < sampleCount; i++) { + int sampleIdx = int(uniforms.frameSeed) * sampleCount + i; + vec2 blueNoiseVec = vec2( + SampleBlueNoise(pixel, sampleIdx, 0, scramblingRankingTexture, sobolSequenceTexture), + SampleBlueNoise(pixel, sampleIdx, 1, scramblingRankingTexture, sobolSequenceTexture) + ); + + float alpha = sqr(max(0.0, material.roughness)); + + vec3 V = normalize(-viewVec); + vec3 N = worldNorm; + + Surface surface = CreateSurface(V, N, vec3(1.0), material); + + Ray ray; + ray.ID = i; + blueNoiseVec.y *= (1.0 - uniforms.bias); + + float pdf = 1.0; + BRDFSample brdfSample; + if (material.roughness >= 0.05) { + ImportanceSampleGGXVNDF(blueNoiseVec.xy, N, V, alpha, + ray.direction, pdf); + } + else { + ray.direction = normalize(reflect(-V, N)); + } + + vec3 viewDir = normalize(vec3(globalData.vMatrix * vec4(ray.direction, 0.0))); + + bool isRayValid = !isnan(ray.direction.x) || !isnan(ray.direction.y) || + !isnan(ray.direction.z) || dot(N, ray.direction) >= 0.0; + + if (isRayValid) { + // Scale offset by depth since the depth buffer inaccuracies increase at a distance and might not match the ray traced geometry anymore + float viewOffset = max(1.0, 1.0 * length(viewPos)); + ray.origin = worldPos + ray.direction * EPSILON * viewOffset + worldNorm * EPSILON * viewOffset; + + ray.hitID = -1; + ray.hitDistance = 0.0; + + vec3 radiance = vec3(0.0); + if (material.roughness <= uniforms.roughnessCutoff) { + vec3 viewRayOrigin = viewPos + 10.0 * viewNormal * EPSILON * viewOffset + viewDir * EPSILON * viewOffset; + float rayLength = globalData.cameraFarPlane; + + vec2 hitPixel; + vec3 hitPoint; + float jitter = GetInterleavedGradientNoise(vec2(pixel), 4u) / float(sampleCount) + i / float(sampleCount); + if (traceScreenSpaceAdvanced(viewRayOrigin, viewDir, depthTexture, 0.1, 16.0, 0.5, 64.0, rayLength, false, hitPixel, hitPoint)) { + vec2 hitTexCoord = vec2(hitPixel + 0.5) / vec2(textureSize(depthTexture, 0)); + radiance = textureLod(lightingTexture, hitTexCoord, 1).rgb; + hits += 1.0; + } + } + + float radianceMax = max(max(max(radiance.r, + max(radiance.g, radiance.b)), uniforms.radianceLimit), 0.01); + reflection += radiance * (uniforms.radianceLimit / radianceMax); + } + } + + reflection /= float(sampleCount); + + } + + imageStore(rtrImage, pixel, vec4(reflection, hits)); + } + +} \ No newline at end of file diff --git a/data/shader/reflection/temporal.csh b/data/shader/reflection/temporal.csh index 8f84dd523..887304f31 100644 --- a/data/shader/reflection/temporal.csh +++ b/data/shader/reflection/temporal.csh @@ -91,7 +91,7 @@ void LoadGroupSharedData() { texel = clamp(texel, ivec2(0), ivec2(resolution) - ivec2(1)); - sharedRadianceDepth[i].rgb = FetchTexel(texel); + sharedRadianceDepth[i].rgb = RGBToYCoCg(FetchTexel(texel)); sharedRadianceDepth[i].a = ConvertDepthToViewSpaceDepth(texelFetch(depthTexture, texel, 0).r); } @@ -310,14 +310,14 @@ bool SampleCatmullRom(ivec2 pixel, vec2 uv, out vec4 history) { return false; } -void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { +void ComputeVarianceMinMax(float roughness, out vec3 mean, out vec3 std) { vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); // This could be varied using the temporal variance estimation // By using a wide neighborhood for variance estimation (8x8) we introduce block artifacts // These are similiar to video compression artifacts, the spatial filter mostly clears them up - const int radius = kernelRadius; + const int radius = int(mix(5.0, float(kernelRadius), min(1.0, roughness * 2.0))); ivec2 pixel = ivec2(gl_GlobalInvocationID); float depth = texelFetch(depthTexture, pixel, 0).r; @@ -331,7 +331,7 @@ void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { for (int j = -radius; j <= radius; j++) { int sharedMemoryIdx = GetSharedMemoryIndex(ivec2(i, j)); - vec3 sampleRadiance = RGBToYCoCg(FetchCurrentRadiance(sharedMemoryIdx)); + vec3 sampleRadiance = FetchCurrentRadiance(sharedMemoryIdx); float sampleLinearDepth = FetchDepth(sharedMemoryIdx); float depthPhi = max(1.0, abs(0.025 * linearDepth)); @@ -357,12 +357,14 @@ void main() { pixel.y > imageSize(resolveImage).y) return; + float roughness = texelFetch(roughnessMetallicAoTexture, pixel, 0).r; + vec3 mean, std; - ComputeVarianceMinMax(mean, std); + ComputeVarianceMinMax(roughness, mean, std); ivec2 velocityPixel = pixel; vec2 velocity = texelFetch(velocityTexture, velocityPixel, 0).rg; - + vec2 uv = (vec2(pixel) + vec2(0.5)) * invResolution + velocity; vec2 historyPixel = vec2(pixel) + velocity * resolution; @@ -380,6 +382,7 @@ void main() { vec3 historyColor = RGBToYCoCg(history.rgb); vec3 currentColor = RGBToYCoCg(texelFetch(currentTexture, pixel, 0).rgb); + float validPixel = texelFetch(currentTexture, pixel, 0).a; vec2 currentMoments; currentMoments.r = currentColor.r; @@ -402,16 +405,10 @@ void main() { historyColor = YCoCgToRGB(historyColor); currentColor = YCoCgToRGB(currentColor); - uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; - Material material = UnpackMaterial(materialIdx); - - float roughness = material.roughness; - roughness *= material.roughnessMap ? texelFetch(roughnessMetallicAoTexture, pixel, 0).r : 1.0; - float temporalWeight = mix(pushConstants.temporalWeight, 0.5, adjClipBlend); float factor = clamp(32.0 * log(roughness + 1.0), 0.5, temporalWeight); - factor = (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 - || uv.y > 1.0) ? 0.0 : factor; + valid = (uv.x < 0.0 || uv.y < 0.0 || uv.x > 1.0 + || uv.y > 1.0) ? false : valid; factor = pushConstants.resetHistory > 0 ? 0.0 : factor; @@ -424,6 +421,10 @@ void main() { factor = min(factor, historyLength / (historyLength + 1.0)); +#ifdef UPSCALE + factor = validPixel >= 1.5 ? factor : (valid ? mix(1.0, 0.0, adjClipBlend) : factor); +#endif + vec3 resolve = factor <= 0.0 ? currentColor : mix(currentColor, historyColor, factor); vec2 momentsResolve = factor <= 0.0 ? currentMoments : mix(currentMoments, historyMoments.rg, factor); @@ -432,9 +433,9 @@ void main() { float variance = max(0.0, momentsResolve.g - momentsResolve.r * momentsResolve.r); variance *= varianceBoost; - variance = roughness <= 0.1 ? 0.0 : variance; + variance = roughness <= 0.1 ? variance * 0.0 : variance; imageStore(momentsImage, pixel, vec4(momentsResolve, historyLength + 1.0, 0.0)); - imageStore(resolveImage, pixel, vec4(resolve, variance)); + imageStore(resolveImage, pixel, vec4(vec3(resolve), variance)); } \ No newline at end of file diff --git a/data/shader/reflection/upsample.csh b/data/shader/reflection/upsample.csh index c13c7acdc..d0f7b448a 100644 --- a/data/shader/reflection/upsample.csh +++ b/data/shader/reflection/upsample.csh @@ -14,6 +14,7 @@ layout(set = 3, binding = 0, rgba16f) writeonly uniform image2D image; layout(set = 3, binding = 1) uniform sampler2D lowResTexture; layout(set = 3, binding = 2) uniform sampler2D lowResDepthTexture; layout(set = 3, binding = 3) uniform sampler2D lowResNormalTexture; +layout(set = 3, binding = 4) uniform isampler2D offsetTexture; // (localSize / 2 + 2)^2 shared float depths[36]; @@ -91,6 +92,9 @@ vec4 Upsample(float referenceDepth, vec3 referenceNormal, vec2 highResPixel) { float normalWeight = min(pow(max(dot(referenceNormal, normals[sharedMemoryOffset]), 0.0), 256.0), 1.0); float weight = depthWeight * normalWeight * weights[i]; + result += vec4(data[sharedMemoryOffset], 1.0) * weight; + + totalWeight += weight; if (weight > maxWeight) { maxWeight = weight; closestMemoryOffset = sharedMemoryOffset; @@ -98,9 +102,9 @@ vec4 Upsample(float referenceDepth, vec3 referenceNormal, vec2 highResPixel) { } - result = vec4(data[closestMemoryOffset], 1.0); + //result = vec4(data[closestMemoryOffset], 1.0); - return result; + return vec4(result.rgb, 1.0); } @@ -115,6 +119,10 @@ void main() { ivec2 resolution = imageSize(image); ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); + ivec2 downSamplePixel = pixel / 2; + int offsetIdx = texelFetch(offsetTexture, downSamplePixel, 0).r; + ivec2 offset = pixelOffsets[offsetIdx]; + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(resolution); float depth = texelFetch(depthTexture, pixel, 0).r; @@ -125,6 +133,15 @@ void main() { vec4 upsampleResult = Upsample(depth, surface.N, vec2(pixel)); + upsampleResult.a = downSamplePixel * 2 + offset == pixel ? 2.0 : 0.0; + + //upsampleResult.rgb = vec3(offsetIdx); + if (downSamplePixel * 2 + offset == pixel) { + ivec2 samplePixel = ivec2(gl_LocalInvocationID) / 2 + ivec2(1); + int sharedMemoryOffset = Flatten2D(samplePixel, unflattenedDepthDataSize); + upsampleResult.rgb = data[sharedMemoryOffset]; + } + imageStore(image, pixel, upsampleResult); } \ No newline at end of file diff --git a/data/shader/rtgi/rtgi.csh b/data/shader/rtgi/rtgi.csh index 7da8e2401..a0e31f944 100644 --- a/data/shader/rtgi/rtgi.csh +++ b/data/shader/rtgi/rtgi.csh @@ -19,6 +19,7 @@ #include <../brdf/surface.hsh> #include <../ddgi/ddgi.hsh> +#include <../clouds/shadow.hsh> #include <../shadow.hsh> layout (local_size_x = 8, local_size_y = 4) in; @@ -35,6 +36,10 @@ layout(set = 3, binding = 6) uniform sampler2DArrayShadow cascadeMaps; layout(set = 3, binding = 7) uniform sampler2D scramblingRankingTexture; layout(set = 3, binding = 8) uniform sampler2D sobolSequenceTexture; +#ifdef CLOUD_SHADOWS +layout(set = 3, binding = 9) uniform sampler2D cloudMap; +#endif + const ivec2 offsets[4] = ivec2[4]( ivec2(0, 0), ivec2(1, 0), @@ -42,19 +47,23 @@ const ivec2 offsets[4] = ivec2[4]( ivec2(1, 1) ); -layout(std140, set = 3, binding = 9) uniform UniformBuffer { +layout(std140, set = 3, binding = 10) uniform UniformBuffer { float radianceLimit; uint frameSeed; float bias; + int sampleCount; + int lightSampleCount; int textureLevel; float roughnessCutoff; int halfRes; + int padding0; + int padding1; ivec2 resolution; Shadow shadow; } uniforms; vec3 EvaluateHit(inout Ray ray); -vec3 EvaluateDirectLight(inout Surface surface); +vec3 EvaluateDirectLight(inout Surface surface, inout float seed); float CheckVisibility(Surface surface, float lightDistance); void main() { @@ -82,68 +91,82 @@ void main() { vec3 viewPos = ConvertDepthToViewSpace(depth, recontructTexCoord); vec3 worldPos = vec3(globalData.ivMatrix * vec4(viewPos, 1.0)); - vec3 viewVec = vec3(globalData.ivMatrix * vec4(viewPos, 0.0)); + vec3 worldView = normalize(vec3(globalData.ivMatrix * vec4(viewPos, 0.0))); vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); - int sampleIdx = int(uniforms.frameSeed); - vec2 blueNoiseVec = vec2( - SampleBlueNoise(pixel, sampleIdx, 0, scramblingRankingTexture, sobolSequenceTexture), - SampleBlueNoise(pixel, sampleIdx, 1, scramblingRankingTexture, sobolSequenceTexture) - ); - uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; Material material = UnpackMaterial(materialIdx); - float roughness = texelFetch(roughnessMetallicAoTexture, pixel, 0).r; - material.roughness *= material.roughnessMap ? roughness : 1.0; +#ifdef DDGI + bool insideVolume = IsInsideVolume(worldPos); + vec3 probeIrradiance = GetLocalIrradiance(worldPos, -worldView, worldNorm).rgb * ddgiData.volumeStrength; + probeIrradiance = insideVolume ? probeIrradiance : vec3(0.0); +#else + bool insideVolume = false; + vec3 probeIrradiance = vec3(0.0); +#endif vec3 reflection = vec3(0.0); if (depth < 1.0) { - float alpha = sqr(material.roughness); + const int sampleCount = uniforms.sampleCount; + + for (int i = 0; i < sampleCount; i++) { + int sampleIdx = int(uniforms.frameSeed) * sampleCount + i; + vec2 blueNoiseVec = vec2( + SampleBlueNoise(pixel, sampleIdx, 0, scramblingRankingTexture, sobolSequenceTexture), + SampleBlueNoise(pixel, sampleIdx, 1, scramblingRankingTexture, sobolSequenceTexture) + ); + + vec3 V = normalize(-worldView); + vec3 N = worldNorm; - vec3 V = normalize(-viewVec); - vec3 N = worldNorm; + Surface surface = CreateSurface(V, N, vec3(1.0), material); - Surface surface = CreateSurface(V, N, vec3(1.0), material); + blueNoiseVec.x *= (1.0 - uniforms.bias); - blueNoiseVec.x *= (1.0 - uniforms.bias); + Ray ray; - Ray ray; + float pdf = 1.0; + float NdotL; + ImportanceSampleCosDir(N, blueNoiseVec, + ray.direction, NdotL, pdf); - float pdf = 1.0; - BRDFSample brdfSample; - float NdotL; - ImportanceSampleCosDir(N, blueNoiseVec, - ray.direction, NdotL, pdf); + bool isRayValid = !isnan(ray.direction.x) || !isnan(ray.direction.y) || + !isnan(ray.direction.z) || dot(N, ray.direction) > 0.0; - bool isRayValid = !isnan(ray.direction.x) || !isnan(ray.direction.y) || - !isnan(ray.direction.z) || dot(N, ray.direction) > 0.0; + if (isRayValid) { - if (isRayValid) { + // Scale offset by depth since the depth buffer inaccuracies increase at a distance and might not match the ray traced geometry anymore + float viewOffset = max(1.0, 4.0 * length(viewPos)); + ray.origin = worldPos + ray.direction * EPSILON * viewOffset + worldNorm * EPSILON * viewOffset; - // Scale offset by depth since the depth buffer inaccuracies increase at a distance and might not match the ray traced geometry anymore - float viewOffset = max(1.0, length(viewPos)); - ray.origin = worldPos + ray.direction * EPSILON * viewOffset * 0.01 + worldNorm * EPSILON * 0.01 * viewOffset; + ray.hitID = -1; + ray.hitDistance = 0.0; - ray.hitID = -1; - ray.hitDistance = 0.0; + float rayLength = insideVolume ? length(GetCellSize(worldPos)) : INF; - vec3 radiance = vec3(0.0); + vec3 radiance = vec3(0.0); #ifdef OPACITY_CHECK - HitClosestTransparency(ray, INSTANCE_MASK_ALL, 0.0, INF); + HitClosestTransparency(ray, INSTANCE_MASK_ALL, 0.0, rayLength); #else - HitClosest(ray, INSTANCE_MASK_ALL, 0.0, INF); + HitClosest(ray, INSTANCE_MASK_ALL, 0.0, rayLength); #endif - radiance = EvaluateHit(ray); + if (ray.hitID >= 0 || !insideVolume) + radiance = EvaluateHit(ray); + else + radiance = probeIrradiance; - float radianceMax = max(max(max(radiance.r, - max(radiance.g, radiance.b)), uniforms.radianceLimit), 0.01); - reflection = radiance * (uniforms.radianceLimit / radianceMax); + float radianceMax = max(max(max(radiance.r, + max(radiance.g, radiance.b)), uniforms.radianceLimit), 0.01); + reflection += radiance * (uniforms.radianceLimit / radianceMax); + } } + reflection /= float(sampleCount); + } imageStore(rtrImage, pixel, vec4(reflection, 1.0)); @@ -167,53 +190,69 @@ vec3 EvaluateHit(inout Ray ray) { bool backfaceHit; Surface surface = GetSurfaceParameters(instance, tri, ray, false, backfaceHit, uniforms.textureLevel); - - radiance += surface.material.emissiveColor; - - // Evaluate direct light - radiance += EvaluateDirectLight(surface); - // Evaluate indirect lighting #ifdef DDGI + surface.NdotV = saturate(dot(surface.N, surface.V)); vec3 irradiance = GetLocalIrradiance(surface.P, surface.V, surface.N).rgb; + //vec3 irradiance = GetLocalIrradiance(surface.P, surface.V, surface.N).rgb; // Approximate indirect specular for ray by using the irradiance grid // This enables metallic materials to have some kind of secondary reflection vec3 indirect = EvaluateIndirectDiffuseBRDF(surface) * irradiance + EvaluateIndirectSpecularBRDF(surface) * irradiance; radiance += IsInsideVolume(surface.P) ? indirect * ddgiData.volumeStrength : vec3(0.0); #endif + + radiance += surface.material.emissiveColor; + float curSeed = float(uniforms.frameSeed) / 255.0; + // Evaluate direct light + for (int i = 0; i < uniforms.lightSampleCount; i++) { + radiance += (EvaluateDirectLight(surface, curSeed) / float(uniforms.lightSampleCount)); + curSeed += 1.0 / float(uniforms.lightSampleCount); + } + return radiance; } -vec3 EvaluateDirectLight(inout Surface surface) { +vec3 EvaluateDirectLight(inout Surface surface, inout float seed) { if (GetLightCount() == 0) return vec3(0.0); - float curSeed = float(uniforms.frameSeed) / 255.0; - float raySeed = float(gl_GlobalInvocationID.x); + float raySeed = float(gl_GlobalInvocationID.x * uniforms.resolution.y + gl_GlobalInvocationID.y); float lightPdf; - Light light = GetLight(surface, raySeed, curSeed, lightPdf); + Light light = GetLight(surface, raySeed, seed, lightPdf); float solidAngle, lightDistance; - SampleLight(light, surface, raySeed, curSeed, solidAngle, lightDistance); + SampleLight(light, surface, raySeed, seed, solidAngle, lightDistance); + +#ifdef USE_SHADOW_MAP + float shadowFactor = CalculateShadowWorldSpace(uniforms.shadow, cascadeMaps, surface.P, + surface.geometryNormal, saturate(dot(surface.L, surface.geometryNormal))); +#else + float shadowFactor = 1.0; + if (light.castShadow) + shadowFactor *= CheckVisibility(surface, lightDistance); +#endif // Evaluate the BRDF vec3 reflectance = EvaluateDiffuseBRDF(surface) + EvaluateSpecularBRDF(surface); reflectance *= surface.material.opacity; - vec3 radiance = light.radiance * solidAngle; + vec3 radiance = light.radiance * solidAngle * shadowFactor; // Check for visibilty. This is important to get an // estimate of the solid angle of the light from point P // on the surface. -#ifdef USE_SHADOW_MAP - radiance *= CalculateShadowWorldSpace(uniforms.shadow, cascadeMaps, surface.P, - surface.geometryNormal, saturate(dot(surface.L, surface.geometryNormal))); -#else - radiance *= CheckVisibility(surface, lightDistance); + +#ifdef CLOUD_SHADOWS + if (light.type == uint(DIRECTIONAL_LIGHT)) { + vec3 P = vec3(globalData.vMatrix * vec4(surface.P, 1.0)); + float cloudShadowFactor = CalculateCloudShadow(P, cloudShadowUniforms.cloudShadow, cloudMap); + + radiance *= cloudShadowFactor; + } #endif return reflectance * radiance * surface.NdotL / lightPdf; diff --git a/data/shader/rtgi/temporal.csh b/data/shader/rtgi/temporal.csh index 7448e2d81..1dccc5a59 100644 --- a/data/shader/rtgi/temporal.csh +++ b/data/shader/rtgi/temporal.csh @@ -93,7 +93,7 @@ void LoadGroupSharedData() { texel = clamp(texel, ivec2(0), ivec2(resolution) - ivec2(1)); - sharedRadianceDepth[i].rgb = FetchTexel(texel); + sharedRadianceDepth[i].rgb = RGBToYCoCg(FetchTexel(texel)); sharedRadianceDepth[i].a = texelFetch(depthTexture, texel, 0).r; sharedMaterialIdx[i].r = texelFetch(materialIdxTexture, texel, 0).r; } @@ -197,7 +197,7 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out vec4 hi offsetPixel = clamp(offsetPixel, ivec2(0), ivec2(resolution) - ivec2(1)); vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); - float normalWeight = GetEdgePreservingNormalWeight(normal, historyNormal, 4.0); + float normalWeight = GetEdgePreservingNormalWeight(normal, historyNormal, 1.0); float historyDepth = ConvertDepthToViewSpaceDepth(texelFetch(historyDepthTexture, offsetPixel, 0).r); float depthWeight = min(1.0 , exp(-abs(linearDepth - historyDepth) * depthPhi)); @@ -223,7 +223,7 @@ bool SampleHistory(ivec2 pixel, vec2 historyPixel, out vec4 history, out vec4 hi offsetPixel = clamp(offsetPixel, ivec2(0), ivec2(resolution) - ivec2(1)); vec3 historyNormal = DecodeNormal(texelFetch(historyNormalTexture, offsetPixel, 0).rg); - float normalWeight = GetEdgePreservingNormalWeight(normal, historyNormal, 4.0); + float normalWeight = GetEdgePreservingNormalWeight(normal, historyNormal, 1.0); float historyDepth = ConvertDepthToViewSpaceDepth(texelFetch(historyDepthTexture, offsetPixel, 0).r); float depthWeight = min(1.0 , exp(-abs(linearDepth - historyDepth) * depthPhi)); @@ -361,7 +361,7 @@ void ComputeVarianceMinMax(out vec3 mean, out vec3 std) { int sharedMemoryIdx = GetSharedMemoryIndex(ivec2(i, j)); - vec3 sampleRadiance = RGBToYCoCg(FetchCurrentRadiance(sharedMemoryIdx)); + vec3 sampleRadiance = FetchCurrentRadiance(sharedMemoryIdx); float sampleDepth = FetchDepth(sharedMemoryIdx); uint sampleMaterialIdx = FetchMaterialIdx(sharedMemoryIdx); @@ -401,7 +401,7 @@ void main() { vec2 velocity = texelFetch(velocityTexture, velocityPixel, 0).rg; vec2 uv = (vec2(pixel) + vec2(0.5)) * invResolution + velocity; - vec2 historyPixel = vec2(pixel) + (velocity * resolution) + 0.5; + vec2 historyPixel = vec2(pixel) + (velocity * resolution); bool valid = true; vec4 history; @@ -468,7 +468,7 @@ void main() { imageStore(resolveImage, pixel, vec4(vec3(historyLength / 32.0), variance)); //imageStore(resolveImage, pixel, vec4(vec3(adjClipBlend), variance)); //imageStore(resolveImage, pixel, vec4(vec3(abs(velocity.x) + abs(velocity.y)), variance)); - imageStore(resolveImage, pixel, vec4(vec3(success ? 1.0 : 0.0), variance)); + //imageStore(resolveImage, pixel, vec4(vec3(success ? 1.0 : 0.0), variance)); //imageStore(resolveImage, pixel, vec4(vec3(materialIdx) / 3000.0, variance)); imageStore(resolveImage, pixel, vec4(resolve, variance)); diff --git a/data/shader/shadow.hsh b/data/shader/shadow.hsh index 260c45125..f2e1951ec 100644 --- a/data/shader/shadow.hsh +++ b/data/shader/shadow.hsh @@ -15,7 +15,7 @@ struct Cascade { float aligment0; float aligment1; - mat4 cascadeSpace; + mat3x4 cascadeSpace; }; @@ -30,7 +30,7 @@ struct Shadow { int cascadeCount; - float aligment1; + int mapIdx; vec2 resolution; @@ -119,116 +119,276 @@ float cascadeLookup(Shadow shadow, sampler2DArrayShadow cascadeMaps, float casca #ifdef SHADOW_FILTER_1x1 visibility += offsetLookup(cascadeMaps, shadowCoords.xy, 0.0, 0.0, float(cascadeIndex), resInv, shadowCoords.z, bias); #endif -#ifdef SHADOW_FILTER_3x3 - float uw0 = (3.0 - 2.0 * s); - float uw1 = (1.0 + 2.0 * s); - float u0 = (2.0 - s) / uw0 - 1.0; - float u1 = s / uw1 + 1.0; +#ifdef SHADOW_FILTER_VOGEL + vec2 texelSize = vec2(shadow.cascades[int(cascadeIndex)].texelSize); + for (int i = 0; i < 16; i++) { + visibility += offsetLookup(cascadeMaps, uv, float(cascadeIndex), shadow.edgeSoftness, texelSize, resInv, position, i, shadowCoords.z, bias); + } - float vw0 = (3.0 - 2.0 * t); - float vw1 = (1.0 + 2.0 * t); + visibility /= 16.0; +#endif - float v0 = (2.0 - t) / vw0 - 1.0; - float v1 = t / vw1 + 1.0; + // Fade out shadow in the distance + return clamp(visibility + fadeout, 0.0, 1.0); - visibility += uw0 * vw0 * offsetLookup(cascadeMaps, flooredUV, u0, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw0 * offsetLookup(cascadeMaps, flooredUV, u1, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw0 * vw1 * offsetLookup(cascadeMaps, flooredUV, u0, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw1 * offsetLookup(cascadeMaps, flooredUV, u1, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); +} - visibility /= 16.0; +float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, vec3 fragmentPosition, vec3 normal, float cosTheta) { + + // Note: The code below is actually the fastest code on every platform tested + // Some platforms have problems directly indexing the cascade array. + // We allow 6 cascades +#ifdef SHADOW_CASCADE_BLENDING + float distance = -fragmentPosition.z - shadow.cascadeBlendDistance; +#else + float distance = -fragmentPosition.z; +#endif + int cascadeIndex = 0; + cascadeIndex = distance >= shadow.cascades[0].distance ? 1 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[1].distance ? 2 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[2].distance ? 3 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[3].distance ? 4 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[4].distance ? 5 : cascadeIndex; + cascadeIndex = min(shadow.cascadeCount - 1, cascadeIndex); + + mat3x4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 3 ? shadow.cascades[4].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 4 ? shadow.cascades[5].cascadeSpace : cascadeMatrix; + + float texelSize = shadow.cascades[0].texelSize; + texelSize = cascadeIndex > 0 ? shadow.cascades[1].texelSize : texelSize; + texelSize = cascadeIndex > 1 ? shadow.cascades[2].texelSize : texelSize; + texelSize = cascadeIndex > 2 ? shadow.cascades[3].texelSize : texelSize; + texelSize = cascadeIndex > 3 ? shadow.cascades[4].texelSize : texelSize; + texelSize = cascadeIndex > 4 ? shadow.cascades[5].texelSize : texelSize; + + vec3 bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); + fragmentPosition += bias; + + float visibility = cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, fragmentPosition, 0.0, true); + +#ifdef SHADOW_CASCADE_BLENDING + if (cascadeIndex < shadow.cascadeCount - 1) { + + float cascadeDistance = shadow.cascades[0].distance; + cascadeDistance = cascadeIndex > 0 ? shadow.cascades[1].distance : cascadeDistance; + cascadeDistance = cascadeIndex > 1 ? shadow.cascades[2].distance : cascadeDistance; + cascadeDistance = cascadeIndex > 2 ? shadow.cascades[3].distance : cascadeDistance; + cascadeDistance = cascadeIndex > 3 ? shadow.cascades[4].distance : cascadeDistance; + + float blend = (cascadeDistance - distance) + / shadow.cascadeBlendDistance; + blend = clamp(blend, 0.0, 1.0); + + if (blend <= 1.0) { + + cascadeIndex += 1; + fragmentPosition -= bias; + + cascadeMatrix = shadow.cascades[0].cascadeSpace; + cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 3 ? shadow.cascades[4].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 4 ? shadow.cascades[5].cascadeSpace : cascadeMatrix; + + texelSize = shadow.cascades[0].texelSize; + texelSize = cascadeIndex > 0 ? shadow.cascades[1].texelSize : texelSize; + texelSize = cascadeIndex > 1 ? shadow.cascades[2].texelSize : texelSize; + texelSize = cascadeIndex > 2 ? shadow.cascades[3].texelSize : texelSize; + texelSize = cascadeIndex > 3 ? shadow.cascades[4].texelSize : texelSize; + texelSize = cascadeIndex > 4 ? shadow.cascades[5].texelSize : texelSize; + + bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); + fragmentPosition += bias; + + visibility = mix(cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, fragmentPosition, 0.0, true), visibility, blend); + } + } +#endif + + return visibility; +} + +float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, vec3 fragmentPosition, vec3 position, vec3 normal, float cosTheta) { + + // Note: The code below is actually the fastest code on every platform tested + // Some platforms have problems directly indexing the cascade array. + // We allow 6 cascades +#ifdef SHADOW_CASCADE_BLENDING + float distance = -fragmentPosition.z - shadow.cascadeBlendDistance; +#else + float distance = -fragmentPosition.z; +#endif + int cascadeIndex = 0; + cascadeIndex = distance >= shadow.cascades[0].distance ? 1 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[1].distance ? 2 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[2].distance ? 3 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[3].distance ? 4 : cascadeIndex; + cascadeIndex = distance >= shadow.cascades[4].distance ? 5 : cascadeIndex; + cascadeIndex = min(shadow.cascadeCount - 1, cascadeIndex); + + mat3x4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 3 ? shadow.cascades[4].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 4 ? shadow.cascades[5].cascadeSpace : cascadeMatrix; + + float texelSize = shadow.cascades[0].texelSize; + texelSize = cascadeIndex > 0 ? shadow.cascades[1].texelSize : texelSize; + texelSize = cascadeIndex > 1 ? shadow.cascades[2].texelSize : texelSize; + texelSize = cascadeIndex > 2 ? shadow.cascades[3].texelSize : texelSize; + texelSize = cascadeIndex > 3 ? shadow.cascades[4].texelSize : texelSize; + texelSize = cascadeIndex > 4 ? shadow.cascades[5].texelSize : texelSize; + + vec3 bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); + fragmentPosition += bias; + + float visibility = cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, position, 0.0, true); + +#ifdef SHADOW_CASCADE_BLENDING + if (cascadeIndex < shadow.cascadeCount - 1) { + + float cascadeDistance = shadow.cascades[0].distance; + cascadeDistance = cascadeIndex > 0 ? shadow.cascades[1].distance : cascadeDistance; + cascadeDistance = cascadeIndex > 1 ? shadow.cascades[2].distance : cascadeDistance; + cascadeDistance = cascadeIndex > 2 ? shadow.cascades[3].distance : cascadeDistance; + cascadeDistance = cascadeIndex > 3 ? shadow.cascades[4].distance : cascadeDistance; + + float blend = (cascadeDistance - distance) + / shadow.cascadeBlendDistance; + blend = clamp(blend, 0.0, 1.0); + + if (blend <= 1.0) { + + cascadeIndex += 1; + fragmentPosition -= bias; + + cascadeMatrix = shadow.cascades[0].cascadeSpace; + cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 3 ? shadow.cascades[4].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 4 ? shadow.cascades[5].cascadeSpace : cascadeMatrix; + + texelSize = shadow.cascades[0].texelSize; + texelSize = cascadeIndex > 0 ? shadow.cascades[1].texelSize : texelSize; + texelSize = cascadeIndex > 1 ? shadow.cascades[2].texelSize : texelSize; + texelSize = cascadeIndex > 2 ? shadow.cascades[3].texelSize : texelSize; + texelSize = cascadeIndex > 3 ? shadow.cascades[4].texelSize : texelSize; + texelSize = cascadeIndex > 4 ? shadow.cascades[5].texelSize : texelSize; + + bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); + fragmentPosition += bias; + + visibility = mix(cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, position, 0.0, true), visibility, blend); + } + } #endif -#ifdef SHADOW_FILTER_5x5 - float uw0 = (4.0 - 3.0 * s); - float uw1 = 7.0; - float uw2 = (1.0 + 3.0 * s); - float u0 = (3.0 - 2.0 * s) / uw0 - 2.0; - float u1 = (3.0 + s) / uw1; - float u2 = s / uw2 + 2.0; + return visibility; +} - float vw0 = (4.0 - 3.0 * t); - float vw1 = 7.0; - float vw2 = (1.0 + 3.0 * t); +float CalculateShadowWorldSpace(Shadow shadow, sampler2DArrayShadow cascadeMaps, vec3 position, vec3 normal, float cosTheta) { + mat3x4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + float texelSize = shadow.cascades[0].texelSize; - float v0 = (3.0 - 2.0 * t) / vw0 - 2.0; - float v1 = (3.0 + t) / vw1; - float v2 = t / vw2 + 2.0; + vec3 bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); + position += bias; - visibility += uw0 * vw0 * offsetLookup(cascadeMaps, flooredUV, u0, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw0 * offsetLookup(cascadeMaps, flooredUV, u1, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw0 * offsetLookup(cascadeMaps, flooredUV, u2, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); + return cascadeLookup(shadow, cascadeMaps, 0.0, + mat4(transpose(cascadeMatrix)), position, position, 0.0, false); +} - visibility += uw0 * vw1 * offsetLookup(cascadeMaps, flooredUV, u0, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw1 * offsetLookup(cascadeMaps, flooredUV, u1, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw1 * offsetLookup(cascadeMaps, flooredUV, u2, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); +float offsetLookup(texture2DArray cascadeMaps, sampler shadowSampler, vec2 flooredUV, float u, float v, float cascadeIndex, + vec2 texelSize, float depth, float bias) { - visibility += uw0 * vw2 * offsetLookup(cascadeMaps, flooredUV, u0, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw2 * offsetLookup(cascadeMaps, flooredUV, u1, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw2 * offsetLookup(cascadeMaps, flooredUV, u2, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); + vec2 uv = 0.5 * (flooredUV + vec2(u, v) * texelSize) + 0.5; - visibility /= 144.0; +#ifdef AE_TEXTURE_SHADOW_LOD + // This fixes issues that can occur at cascade borders + return textureLod(sampler2DArrayShadow(cascadeMaps, shadowSampler), + vec4(uv, cascadeIndex, depth + bias), 0); +#else + return texture(sampler2DArrayShadow(cascadeMaps, shadowSampler), + vec4(uv, cascadeIndex, depth + bias)); #endif -#ifdef SHADOW_FILTER_7x7 - float uw0 = (5.0 * s - 6.0); - float uw1 = (11.0 * s - 28.0); - float uw2 = -(11.0 * s + 17.0); - float uw3 = -(5.0 * s + 1.0); - - float u0 = (4.0 * s - 5.0) / uw0 - 3.0; - float u1 = (4.0 * s - 16.0) / uw1 - 1.0; - float u2 = -(7.0 * s + 5.0) / uw2 + 1.0; - float u3 = -s / uw3 + 3.0; - - float vw0 = (5.0 * t - 6.0); - float vw1 = (11.0 * t - 28.0); - float vw2 = -(11.0 * t + 17.0); - float vw3 = -(5.0 * t + 1.0); - - float v0 = (4.0 * t - 5.0) / vw0 - 3.0; - float v1 = (4.0 * t - 16.0) / vw1 - 1.0; - float v2 = -(7.0 * t + 5.0) / vw2 + 1.0; - float v3 = -t / vw3 + 3.0; - - visibility += uw0 * vw0 * offsetLookup(cascadeMaps, flooredUV, u0, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw0 * offsetLookup(cascadeMaps, flooredUV, u1, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw0 * offsetLookup(cascadeMaps, flooredUV, u2, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw3 * vw0 * offsetLookup(cascadeMaps, flooredUV, u3, v0, float(cascadeIndex), resInv, shadowCoords.z, bias); - - visibility += uw0 * vw1 * offsetLookup(cascadeMaps, flooredUV, u0, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw1 * offsetLookup(cascadeMaps, flooredUV, u1, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw1 * offsetLookup(cascadeMaps, flooredUV, u2, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw3 * vw1 * offsetLookup(cascadeMaps, flooredUV, u3, v1, float(cascadeIndex), resInv, shadowCoords.z, bias); - - visibility += uw0 * vw2 * offsetLookup(cascadeMaps, flooredUV, u0, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw2 * offsetLookup(cascadeMaps, flooredUV, u1, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw2 * offsetLookup(cascadeMaps, flooredUV, u2, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw3 * vw2 * offsetLookup(cascadeMaps, flooredUV, u3, v2, float(cascadeIndex), resInv, shadowCoords.z, bias); - - visibility += uw0 * vw3 * offsetLookup(cascadeMaps, flooredUV, u0, v3, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw1 * vw3 * offsetLookup(cascadeMaps, flooredUV, u1, v3, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw2 * vw3 * offsetLookup(cascadeMaps, flooredUV, u2, v3, float(cascadeIndex), resInv, shadowCoords.z, bias); - visibility += uw3 * vw3 * offsetLookup(cascadeMaps, flooredUV, u3, v3, float(cascadeIndex), resInv, shadowCoords.z, bias); - - visibility /= 2704.0; + +} + +float offsetLookup(texture2DArray cascadeMaps, sampler shadowSampler, vec2 uv, float cascadeIndex, float edgeSoftness, + vec2 texelSize, vec2 invResolution, vec3 fragmentPosition, int lookupIndex, float depth, float bias) { + + uv = 0.5 * (uv * invResolution) + 0.5; + vec2 samplingSpread = edgeSoftness * invResolution * 64.0; + + float phi = 2.0 * PI * GetInterleavedGradientNoise(fragmentPosition.xy); + vec2 offset = VogelDiskSample(lookupIndex, 16, phi) * samplingSpread; + +#ifdef AE_TEXTURE_SHADOW_LOD + // This fixes issues that can occur at cascade borders + return textureLod(sampler2DArrayShadow(cascadeMaps, shadowSampler), + vec4(uv + offset, cascadeIndex, depth + bias), 0); +#else + return texture(sampler2DArrayShadow(cascadeMaps, shadowSampler), + vec4(uv + offset, cascadeIndex, depth + bias)); #endif +} + + +float cascadeLookup(inout Shadow shadow, texture2DArray cascadeMaps, sampler shadowSampler, float cascadeIndex, mat4 cascadeTransform, + vec3 fragmentPosition, vec3 position, vec2 texelSize, float bias, bool fade) { + + vec4 shadowCoords = cascadeTransform * vec4(fragmentPosition, 1.0); + shadowCoords.xyz /= shadowCoords.w; + + float fadeout = fade ? clamp((-fragmentPosition.z + 2.0 - shadow.distance) * 0.5, 0.0, 1.0) : 0.0; + + //shadowCoords.z = shadowCoords.z * 0.5 + 0.5; + + if (abs(fadeout - 1.0) < 1e-6) + return 1.0; + + vec2 res = shadow.resolution; + vec2 resInv = 1.0 / res; + + vec2 uv = shadowCoords.xy * res; + + vec2 flooredUV = vec2(floor(uv.x), floor(uv.y)); + + float s = fract(uv.x); + float t = fract(uv.y); + + flooredUV *= resInv; + + float visibility = 0.0; + +#ifdef SHADOW_FILTER_1x1 + visibility += offsetLookup(cascadeMaps, shadowSampler, shadowCoords.xy, 0.0, 0.0, float(cascadeIndex), resInv, shadowCoords.z, bias); +#endif #ifdef SHADOW_FILTER_VOGEL - vec2 texelSize = vec2(shadow.cascades[int(cascadeIndex)].texelSize); for (int i = 0; i < 16; i++) { - visibility += offsetLookup(cascadeMaps, uv, float(cascadeIndex), shadow.edgeSoftness, texelSize, resInv, position, i, shadowCoords.z, bias); + visibility += offsetLookup(cascadeMaps, shadowSampler, uv, float(cascadeIndex), shadow.edgeSoftness, texelSize, resInv, position, i, shadowCoords.z, bias); } - - visibility /= 16.0; #endif + visibility /= 16.0; // Fade out shadow in the distance return clamp(visibility + fadeout, 0.0, 1.0); } -float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, vec3 fragmentPosition, vec3 normal, float cosTheta) { +float CalculateCascadedShadow(inout Shadow shadow, texture2DArray cascadeMaps, sampler shadowSampler, vec3 fragmentPosition, vec3 normal, float cosTheta) { // Note: The code below is actually the fastest code on every platform tested // Some platforms have problems directly indexing the cascade array. @@ -246,7 +406,7 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v cascadeIndex = distance >= shadow.cascades[4].distance ? 5 : cascadeIndex; cascadeIndex = min(shadow.cascadeCount - 1, cascadeIndex); - mat4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + mat3x4 cascadeMatrix = shadow.cascades[0].cascadeSpace; cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; @@ -263,8 +423,8 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v vec3 bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); fragmentPosition += bias; - float visibility = cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), - cascadeMatrix, fragmentPosition, fragmentPosition, 0.0, true); + float visibility = cascadeLookup(shadow, cascadeMaps, shadowSampler, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, fragmentPosition, vec2(texelSize), 0.0, true); #ifdef SHADOW_CASCADE_BLENDING if (cascadeIndex < shadow.cascadeCount - 1) { @@ -284,14 +444,14 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v cascadeIndex += 1; fragmentPosition -= bias; - mat4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + cascadeMatrix = shadow.cascades[0].cascadeSpace; cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 3 ? shadow.cascades[4].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 4 ? shadow.cascades[5].cascadeSpace : cascadeMatrix; - float texelSize = shadow.cascades[0].texelSize; + texelSize = shadow.cascades[0].texelSize; texelSize = cascadeIndex > 0 ? shadow.cascades[1].texelSize : texelSize; texelSize = cascadeIndex > 1 ? shadow.cascades[2].texelSize : texelSize; texelSize = cascadeIndex > 2 ? shadow.cascades[3].texelSize : texelSize; @@ -301,8 +461,8 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); fragmentPosition += bias; - visibility = mix(cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), - cascadeMatrix, fragmentPosition, fragmentPosition, 0.0, true), visibility, blend); + visibility = mix(cascadeLookup(shadow, cascadeMaps, shadowSampler, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, fragmentPosition, vec2(texelSize), 0.0, true), visibility, blend); } } #endif @@ -310,7 +470,8 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v return visibility; } -float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, vec3 fragmentPosition, vec3 position, vec3 normal, float cosTheta) { +float CalculateCascadedShadow(inout Shadow shadow, texture2DArray cascadeMaps, sampler shadowSampler, + vec3 fragmentPosition, vec3 position, vec3 normal, float cosTheta) { // Note: The code below is actually the fastest code on every platform tested // Some platforms have problems directly indexing the cascade array. @@ -328,7 +489,7 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v cascadeIndex = distance >= shadow.cascades[4].distance ? 5 : cascadeIndex; cascadeIndex = min(shadow.cascadeCount - 1, cascadeIndex); - mat4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + mat3x4 cascadeMatrix = shadow.cascades[0].cascadeSpace; cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; @@ -345,8 +506,8 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v vec3 bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); fragmentPosition += bias; - float visibility = cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), - cascadeMatrix, fragmentPosition, position, 0.0, true); + float visibility = cascadeLookup(shadow, cascadeMaps, shadowSampler, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, position, vec2(texelSize), -0.00005, true); #ifdef SHADOW_CASCADE_BLENDING if (cascadeIndex < shadow.cascadeCount - 1) { @@ -366,14 +527,14 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v cascadeIndex += 1; fragmentPosition -= bias; - mat4 cascadeMatrix = shadow.cascades[0].cascadeSpace; + cascadeMatrix = shadow.cascades[0].cascadeSpace; cascadeMatrix = cascadeIndex > 0 ? shadow.cascades[1].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 1 ? shadow.cascades[2].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 2 ? shadow.cascades[3].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 3 ? shadow.cascades[4].cascadeSpace : cascadeMatrix; cascadeMatrix = cascadeIndex > 4 ? shadow.cascades[5].cascadeSpace : cascadeMatrix; - float texelSize = shadow.cascades[0].texelSize; + texelSize = shadow.cascades[0].texelSize; texelSize = cascadeIndex > 0 ? shadow.cascades[1].texelSize : texelSize; texelSize = cascadeIndex > 1 ? shadow.cascades[2].texelSize : texelSize; texelSize = cascadeIndex > 2 ? shadow.cascades[3].texelSize : texelSize; @@ -383,8 +544,8 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); fragmentPosition += bias; - visibility = mix(cascadeLookup(shadow, cascadeMaps, float(cascadeIndex), - cascadeMatrix, fragmentPosition, position, 0.0, true), visibility, blend); + visibility = mix(cascadeLookup(shadow, cascadeMaps, shadowSampler, float(cascadeIndex), + mat4(transpose(cascadeMatrix)), fragmentPosition, position, vec2(texelSize), -0.0001, true), visibility, blend); } } #endif @@ -392,13 +553,76 @@ float CalculateCascadedShadow(Shadow shadow, sampler2DArrayShadow cascadeMaps, v return visibility; } -float CalculateShadowWorldSpace(Shadow shadow, sampler2DArrayShadow cascadeMaps, vec3 position, vec3 normal, float cosTheta) { - mat4 cascadeMatrix = shadow.cascades[0].cascadeSpace; +float CalculateShadowWorldSpace(inout Shadow shadow, texture2DArray cascadeMaps, sampler shadowSampler, vec3 position, vec3 normal, float cosTheta) { + mat3x4 cascadeMatrix = shadow.cascades[0].cascadeSpace; float texelSize = shadow.cascades[0].texelSize; vec3 bias = shadow.bias * texelSize * normal / max(cosTheta, 0.5); position += bias; - return cascadeLookup(shadow, cascadeMaps, 0.0, - cascadeMatrix, position, position, 0.0, false); + return cascadeLookup(shadow, cascadeMaps, shadowSampler, 0.0, + mat4(transpose(cascadeMatrix)), position, position, vec2(texelSize), 0.0, false); +} + +vec3 sampleOffsetDirections[20] = vec3[] ( + vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), + vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), + vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), + vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), + vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) +); + + +float CalculatePointShadow(inout Shadow shadow, textureCube cubeMap, sampler shadowSampler, vec3 position, + vec3 normal, float cosTheta) { + + int samples = 20; + float diskRadius = shadow.edgeSoftness; + + vec3 bias = shadow.bias * (1.0 / 2048.0) * normal / max(cosTheta * cosTheta, 0.01); + position += bias; + + mat4 projectionMatrix; + projectionMatrix[0] = shadow.cascades[1].cascadeSpace[0]; + projectionMatrix[1] = shadow.cascades[1].cascadeSpace[1]; + projectionMatrix[2] = shadow.cascades[1].cascadeSpace[2]; + projectionMatrix[3] = shadow.cascades[2].cascadeSpace[0]; + + vec4 shadowCoords = mat4(transpose(shadow.cascades[0].cascadeSpace)) * vec4(position, 1.0); + shadowCoords.y *= -1.0; + vec4 absPosition = abs(shadowCoords); + float depth = -max(absPosition.x, max(absPosition.y, absPosition.z)); + vec4 clip = projectionMatrix * vec4(0.0, 0.0, depth, 1.0); + depth = clip.z / clip.w; + + float shadowFactor = 0.0; + for(int i = 0; i < samples; i++) { + shadowFactor += clamp(texture(samplerCubeShadow(cubeMap, shadowSampler), + vec4(shadowCoords.xyz + sampleOffsetDirections[i] * diskRadius, depth - 0.0001)), 0.0, 1.0); + } + shadowFactor /= float(samples + 1); + + return shadowFactor; + +} + +float CalculateSpotShadow(inout Shadow shadow, texture2DArray cascadeMaps, sampler shadowSampler, + vec3 fragmentPosition, vec3 position, vec3 normal, float cosTheta) { + + float distance = -fragmentPosition.z; + + mat4 shadowMatrix; + shadowMatrix[0] = shadow.cascades[0].cascadeSpace[0]; + shadowMatrix[1] = shadow.cascades[0].cascadeSpace[1]; + shadowMatrix[2] = shadow.cascades[0].cascadeSpace[2]; + shadowMatrix[3] = shadow.cascades[1].cascadeSpace[0]; + + float texelSize = shadow.cascades[0].texelSize; + + vec3 bias = shadow.bias * (1.0 / 256.0) * normal / max(cosTheta * cosTheta, 0.01); + fragmentPosition += bias; + + return cascadeLookup(shadow, cascadeMaps, shadowSampler, 0.0, + shadowMatrix, fragmentPosition, position, vec2(texelSize), -0.0001, false); + } \ No newline at end of file diff --git a/data/shader/shadowMapping.vsh b/data/shader/shadowMapping.vsh index 597f16988..87af352ce 100644 --- a/data/shader/shadowMapping.vsh +++ b/data/shader/shadowMapping.vsh @@ -10,7 +10,7 @@ layout(location=2) in vec2 vTexCoord; layout(location=0) out vec2 texCoordVS; #endif -layout(std430, set = 1, binding = 1) buffer Matrices { +layout(std430, set = 1, binding = 1) readonly buffer Matrices { mat3x4 matrices[]; }; diff --git a/data/shader/ssgi/ssgi.csh b/data/shader/ssgi/ssgi.csh index 203d73866..12fd79ef2 100644 --- a/data/shader/ssgi/ssgi.csh +++ b/data/shader/ssgi/ssgi.csh @@ -1,3 +1,6 @@ +#define DDGI +#define DDGI_VISIBILITY + #include <../globals.hsh> #include <../raytracer/lights.hsh> #include <../raytracer/tracing.hsh> @@ -10,6 +13,7 @@ #include <../common/normalencode.hsh> #include <../common/PI.hsh> #include <../common/bluenoise.hsh> +#include <../common/traceScreenSpace.hsh> #include <../brdf/brdfEval.hsh> #include <../brdf/brdfSample.hsh> @@ -33,6 +37,8 @@ layout(set = 3, binding = 6) uniform sampler2D directLightTexture; layout(set = 3, binding = 7) uniform sampler2D scramblingRankingTexture; layout(set = 3, binding = 8) uniform sampler2D sobolSequenceTexture; +layout(set = 1, binding = 12) uniform samplerCube diffuseProbe; + const ivec2 offsets[4] = ivec2[4]( ivec2(0, 0), ivec2(1, 0), @@ -76,25 +82,32 @@ void main() { vec3 viewPos = ConvertDepthToViewSpace(depth, texCoord); vec3 worldPos = vec3(globalData.ivMatrix * vec4(viewPos, 1.0)); vec3 viewVec = vec3(globalData.ivMatrix * vec4(viewPos, 0.0)); - vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg), 0.0))); + vec3 viewNorm = normalize(DecodeNormal(textureLod(normalTexture, texCoord, 0).rg)); + vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(viewNorm, 0.0))); + vec3 worldView = normalize(vec3(globalData.ivMatrix * vec4(viewVec, 0.0))); uint materialIdx = texelFetch(materialIdxTexture, pixel, 0).r; Material material = UnpackMaterial(materialIdx); - - float roughness = texelFetch(roughnessMetallicAoTexture, pixel, 0).r; - material.roughness *= material.roughnessMap ? roughness : 1.0; + + vec3 globalProbeFallback = textureLod(diffuseProbe, worldNorm, 0).rgb; +#ifdef DDGI + //rayIrradiance = GetLocalIrradianceInterpolated(worldPos, -V, N, N, globalProbeFallback).rgb * ddgiData.volumeStrength; + vec3 probeIrradiance = GetLocalIrradiance(worldPos, -worldView, worldNorm).rgb * ddgiData.volumeStrength; + probeIrradiance = IsInsideVolume(worldPos) ? probeIrradiance : globalProbeFallback; +#else + vec3 probeIrradiance = globalProbeFallback; +#endif vec3 irradiance = vec3(0.0); float hits = 0.0; if (depth < 1.0) { - float alpha = sqr(material.roughness); - vec3 V = normalize(-viewVec); vec3 N = worldNorm; Surface surface = CreateSurface(V, N, vec3(1.0), material); + int totalCount = 0; for (uint j = 0; j < uniforms.rayCount; j++) { int sampleIdx = int(uniforms.frameSeed * uniforms.rayCount + j); @@ -107,7 +120,9 @@ void main() { Ray ray; float pdf = 1.0; - BRDFSample brdfSample = SampleDiffuseBRDF(surface, blueNoiseVec.xy); + float NdotL; + ImportanceSampleCosDir(N, blueNoiseVec.xy, + ray.direction, NdotL, pdf); ray.hitID = -1; ray.hitDistance = 0.0; @@ -117,10 +132,52 @@ void main() { float stepSize = rayLength / float(uniforms.sampleCount); - ray.direction = brdfSample.L; ray.origin = worldPos + surface.N * 0.1 + ray.direction * blueNoiseVec.z * stepSize; - - bool hit = false; + + vec3 rayIrradiance = vec3(0.0); + + vec3 viewDir = normalize(vec3(globalData.vMatrix * vec4(ray.direction, 0.0))); + float viewOffset = max(1.0, length(viewPos)); + vec3 viewRayOrigin = viewPos + viewNorm * EPSILON * viewOffset + viewDir * EPSILON * viewOffset; + + /* + vec2 hitPixel; + vec3 hitPoint; + float hit = 0.0; + float jitter = GetInterleavedGradientNoise(vec2(pixel)) / float(uniforms.rayCount) + j / float(uniforms.rayCount); + if (traceScreenSpaceAdvanced(viewRayOrigin, viewDir, depthTexture, 0.5, 16.0, jitter, 64.0, viewOffset, false, hitPixel, hitPoint)) { + vec2 hitTexCoord = vec2(hitPixel + 0.5) / vec2(textureSize(depthTexture, 0)); + vec3 stepViewNorm = normalize(DecodeNormal(texelFetch(normalTexture, ivec2(hitPixel), 0).rg)); + float depth = texelFetch(depthTexture, ivec2(hitPixel), 0).r; + hitPoint = ConvertDepthToViewSpace(depth, hitTexCoord); + + vec3 worldHitNorm = normalize(vec3(globalData.ivMatrix * vec4(stepViewNorm, 0.0))); + vec3 worldHitPoint = vec3(globalData.ivMatrix * vec4(hitPoint, 1.0)); + + float NdotV = saturate(dot(-viewDir, stepViewNorm)); + NdotL = saturate(dot(viewNorm, hitPoint)); + if (NdotV > 0.0) { + // rayIrradiance = mix(probeIrradiance, textureLod(directLightTexture, hitTexCoord, 0).rgb, 1.0); + rayIrradiance = textureLod(directLightTexture, hitTexCoord, 0).rgb; + +#ifdef DDGI + vec3 bounceRayIrradiance = GetLocalIrradiance(worldHitPoint, -ray.direction, worldHitNorm).rgb * ddgiData.volumeStrength; + bounceRayIrradiance = IsInsideVolume(worldHitPoint) ? bounceRayIrradiance : vec3(0.0); + + rayIrradiance += bounceRayIrradiance; +#endif + } + hit = 1.0; + } + else { + rayIrradiance = probeIrradiance; + } + + //rayIrradiance = probeIrradiance; + */ + + + float hit = 0.0; for (uint i = 0; i < uniforms.sampleCount; i++) { vec3 rayPos = vec3(globalData.vMatrix * vec4(ray.origin + float(i) * ray.direction * stepSize, 1.0)); @@ -136,33 +193,43 @@ void main() { float stepDepth = texelFetch(depthTexture, stepPixel, 0).r; vec3 stepPos = ConvertDepthToViewSpace(stepDepth, uvPos); - float stepLinearDepth = -stepPos.z; - float rayDepth = -rayPos.z; + float stepLinearDepth = abs(stepPos.z); + float rayDepth = abs(rayPos.z); float depthDelta = rayDepth - stepLinearDepth; - vec3 worldNorm = normalize(vec3(globalData.ivMatrix * vec4(DecodeNormal(texelFetch(normalTexture, stepPixel, 0).rg), 0.0))); + vec3 stepViewNorm = normalize(DecodeNormal(texelFetch(normalTexture, stepPixel, 0).rg)); + vec3 stepWorldNorm = normalize(vec3(globalData.ivMatrix * vec4(stepViewNorm, 0.0))); + + vec3 rayDir = normalize(stepPos - viewPos); // Check if we are now behind the depth buffer, use that hit as the source of radiance - if (stepLinearDepth < rayDepth && abs(depthDelta) < rayLength) { - float NdotV = dot(worldNorm, -ray.direction) * 0.5 + 0.5; - float NdotL = dot(-worldNorm, surface.N) * 0.5 + 0.5; - if (true) { + if (rayDepth >= stepLinearDepth) { + float NdotV = dot(stepViewNorm, -rayDir); + if (NdotV > 0.0 && abs(depthDelta) < stepSize) { ivec2 lightPixel = uniforms.downsampled2x > 0 ? stepPixel * 2 + pixelOffset : stepPixel; - vec3 rayIrradiance = texelFetch(directLightTexture, lightPixel, 0).rgb; - float dist = distance(viewPos, stepPos); - irradiance += rayIrradiance * max(NdotL, 0.0) * NdotV * surface.NdotL / max(0.1, dist); + vec3 hitRadiance = texelFetch(directLightTexture, lightPixel, 0).rgb; + rayIrradiance += hitRadiance * NdotV * max(dot(viewNorm, rayDir), 0.0) / pdf / PI; + totalCount += 1; + } - hit = true; - break; + if (abs(depthDelta) < float(i + 1) * stepSize) { + //hit = max(0.0, 1.0 - ((distance(stepPos, rayPos)) / rayLength)); + hit = 1.0; + break; + } + //hit = max(0.0, 1.0 - ((distance(stepPos, rayPos)) / rayLength)); } } + + + irradiance += (rayIrradiance / max(1.0, float(totalCount))); - hits += hit ? 1.0 : 0.0; + hits += hit; } - irradiance /= float(uniforms.rayCount); + irradiance /= (float(uniforms.rayCount)); float irradianceMax = max(max(max(irradiance.r, max(irradiance.g, irradiance.b)), uniforms.radianceLimit), 0.01); @@ -171,7 +238,7 @@ void main() { float ao = max(1.0 - (hits / float(uniforms.rayCount)), 0.0); - imageStore(giImage, pixel, vec4(irradiance, ao)); + imageStore(giImage, pixel, vec4(vec3(irradiance), ao)); } } \ No newline at end of file diff --git a/data/shader/structures b/data/shader/structures deleted file mode 100644 index b1a1234e5..000000000 --- a/data/shader/structures +++ /dev/null @@ -1,29 +0,0 @@ -#include - -//Light struct has to be implemented like this -struct Light { - - vec4 location; - vec4 direction; - - vec4 color; - float intensity; - - float scatteringFactor; - - float radius; - float alignment; - - Shadow shadow; - -}; - -struct CloudShadow { - - mat4 vMatrix; - mat4 pMatrix; - - mat4 ivMatrix; - mat4 ipMatrix; - -}; \ No newline at end of file diff --git a/data/shader/structures.hsh b/data/shader/structures.hsh new file mode 100644 index 000000000..684fa49ce --- /dev/null +++ b/data/shader/structures.hsh @@ -0,0 +1,40 @@ +#include + +#define DIRECTIONAL_LIGHT 0 +#define POINT_LIGHT 1 +#define SPOT_LIGHT 2 + +//Light struct has to be implemented like this +struct Light { + + vec4 location; + vec4 direction; + + vec4 color; + float intensity; + + float scatteringFactor; + + float specific0; + float specific1; + + Shadow shadow; + +}; + +struct VolumetricLight { + vec4 location; + vec4 direction; + + vec4 color; + float intensity; + + float specific0; + float specific1; + + int shadowIdx; +}; + +layout (std430, set = 1, binding = 18) buffer Lights { + Light lights[]; +}; \ No newline at end of file diff --git a/data/shader/terrain/terrain.fsh b/data/shader/terrain/terrain.fsh index ac577dba8..2614dd39a 100644 --- a/data/shader/terrain/terrain.fsh +++ b/data/shader/terrain/terrain.fsh @@ -9,8 +9,9 @@ layout (location = 0) out vec3 baseColorFS; layout (location = 1) out vec2 normalFS; layout (location = 2) out vec2 geometryNormalFS; layout (location = 3) out vec3 roughnessMetalnessAoFS; -layout (location = 4) out uint materialIdxFS; -layout (location = 5) out vec2 velocityFS; +layout (location = 4) out vec3 emissiveFS; +layout (location = 5) out uint materialIdxFS; +layout (location = 6) out vec2 velocityFS; layout (set = 3, binding = 1) uniform sampler2D normalMap; layout (set = 3, binding = 2) uniform usampler2D splatMap; @@ -56,10 +57,10 @@ layout(location=3) in vec3 ndcLast; vec3 SampleBaseColor(vec2 off, uvec4 indices, vec4 tiling) { - vec3 q00 = nonuniformEXT(texture(baseColorMaps, vec3(materialTexCoords / 4.0 * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).rgb); - vec3 q10 = nonuniformEXT(indices.y != indices.x ? texture(baseColorMaps, vec3(materialTexCoords / 4.0 * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).rgb : q00); - vec3 q01 = nonuniformEXT(indices.z != indices.x ? texture(baseColorMaps, vec3(materialTexCoords / 4.0 * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).rgb : q00); - vec3 q11 = nonuniformEXT(indices.w != indices.x ? texture(baseColorMaps, vec3(materialTexCoords / 4.0 * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).rgb : q00); + vec3 q00 = nonuniformEXT(texture(baseColorMaps, vec3(materialTexCoords * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).rgb); + vec3 q10 = nonuniformEXT(indices.y != indices.x ? texture(baseColorMaps, vec3(materialTexCoords * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).rgb : q00); + vec3 q01 = nonuniformEXT(indices.z != indices.x ? texture(baseColorMaps, vec3(materialTexCoords * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).rgb : q00); + vec3 q11 = nonuniformEXT(indices.w != indices.x ? texture(baseColorMaps, vec3(materialTexCoords * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).rgb : q00); // Interpolate samples horizontally vec3 h0 = mix(q00, q10, off.x); @@ -72,10 +73,10 @@ vec3 SampleBaseColor(vec2 off, uvec4 indices, vec4 tiling) { float SampleRoughness(vec2 off, uvec4 indices, vec4 tiling) { - float q00 = nonuniformEXT(texture(roughnessMaps, vec3(materialTexCoords / 4.0 * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).r); - float q10 = nonuniformEXT(indices.y != indices.x ? texture(roughnessMaps, vec3(materialTexCoords / 4.0 * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).r : q00); - float q01 = nonuniformEXT(indices.z != indices.x ? texture(roughnessMaps, vec3(materialTexCoords / 4.0 * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).r : q00); - float q11 = nonuniformEXT(indices.w != indices.x ? texture(roughnessMaps, vec3(materialTexCoords / 4.0 * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).r : q00); + float q00 = nonuniformEXT(texture(roughnessMaps, vec3(materialTexCoords* tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).r); + float q10 = nonuniformEXT(indices.y != indices.x ? texture(roughnessMaps, vec3(materialTexCoords * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).r : q00); + float q01 = nonuniformEXT(indices.z != indices.x ? texture(roughnessMaps, vec3(materialTexCoords * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).r : q00); + float q11 = nonuniformEXT(indices.w != indices.x ? texture(roughnessMaps, vec3(materialTexCoords * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).r : q00); // Interpolate samples horizontally float h0 = mix(q00, q10, off.x); @@ -88,10 +89,10 @@ float SampleRoughness(vec2 off, uvec4 indices, vec4 tiling) { float SampleAo(vec2 off, uvec4 indices, vec4 tiling) { - float q00 = nonuniformEXT(texture(aoMaps, vec3(materialTexCoords / 4.0 * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).r); - float q10 = nonuniformEXT(indices.y != indices.x ? texture(aoMaps, vec3(materialTexCoords / 4.0 * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).r : q00); - float q01 = nonuniformEXT(indices.z != indices.x ? texture(aoMaps, vec3(materialTexCoords / 4.0 * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).r : q00); - float q11 = nonuniformEXT(indices.w != indices.x ? texture(aoMaps, vec3(materialTexCoords / 4.0 * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).r : q00); + float q00 = nonuniformEXT(texture(aoMaps, vec3(materialTexCoords * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).r); + float q10 = nonuniformEXT(indices.y != indices.x ? texture(aoMaps, vec3(materialTexCoords * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).r : q00); + float q01 = nonuniformEXT(indices.z != indices.x ? texture(aoMaps, vec3(materialTexCoords * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).r : q00); + float q11 = nonuniformEXT(indices.w != indices.x ? texture(aoMaps, vec3(materialTexCoords * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).r : q00); // Interpolate samples horizontally float h0 = mix(q00, q10, off.x); @@ -104,10 +105,10 @@ float SampleAo(vec2 off, uvec4 indices, vec4 tiling) { vec3 SampleNormal(vec2 off, uvec4 indices, vec4 tiling) { - vec3 q00 = nonuniformEXT(texture(normalMaps, vec3(materialTexCoords / 4.0 * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).rgb); - vec3 q10 = nonuniformEXT(indices.y != indices.x ? texture(normalMaps, vec3(materialTexCoords / 4.0 * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).rgb : q00); - vec3 q01 = nonuniformEXT(indices.z != indices.x ? texture(normalMaps, vec3(materialTexCoords / 4.0 * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).rgb : q00); - vec3 q11 = nonuniformEXT(indices.w != indices.x ? texture(normalMaps, vec3(materialTexCoords / 4.0 * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).rgb : q00); + vec3 q00 = nonuniformEXT(texture(normalMaps, vec3(materialTexCoords * tiling.x, nonuniformEXT(float(indices.x))), globalData.mipLodBias).rgb); + vec3 q10 = nonuniformEXT(indices.y != indices.x ? texture(normalMaps, vec3(materialTexCoords * tiling.y, nonuniformEXT(float(indices.y))), globalData.mipLodBias).rgb : q00); + vec3 q01 = nonuniformEXT(indices.z != indices.x ? texture(normalMaps, vec3(materialTexCoords * tiling.z, nonuniformEXT(float(indices.z))), globalData.mipLodBias).rgb : q00); + vec3 q11 = nonuniformEXT(indices.w != indices.x ? texture(normalMaps, vec3(materialTexCoords * tiling.w, nonuniformEXT(float(indices.w))), globalData.mipLodBias).rgb : q00); // Interpolate samples horizontally vec3 h0 = mix(q00, q10, off.x); diff --git a/data/shader/terrain/terrain.tesh b/data/shader/terrain/terrain.tesh index ab38d7500..d19e112d7 100644 --- a/data/shader/terrain/terrain.tesh +++ b/data/shader/terrain/terrain.tesh @@ -51,12 +51,12 @@ float SampleDisplacement(vec2 off, uvec4 indices) { if (indices.x == indices.y && indices.x == indices.z && indices.x == indices.w) - return texture(displacementMaps, vec3(materialTexCoords / 4.0, float(indices.x))).r; + return texture(displacementMaps, vec3(materialTexCoords, float(indices.x))).r; - float q00 = texture(displacementMaps, vec3(materialTexCoords / 4.0, float(indices.x))).r; - float q10 = texture(displacementMaps, vec3(materialTexCoords / 4.0, float(indices.y))).r; - float q01 = texture(displacementMaps, vec3(materialTexCoords / 4.0, float(indices.z))).r; - float q11 = texture(displacementMaps, vec3(materialTexCoords / 4.0, float(indices.w))).r; + float q00 = texture(displacementMaps, vec3(materialTexCoords, float(indices.x))).r; + float q10 = texture(displacementMaps, vec3(materialTexCoords, float(indices.y))).r; + float q01 = texture(displacementMaps, vec3(materialTexCoords, float(indices.z))).r; + float q11 = texture(displacementMaps, vec3(materialTexCoords, float(indices.w))).r; // Interpolate samples horizontally float h0 = mix(q00, q10, off.x); diff --git a/data/shader/text.vsh b/data/shader/text.vsh index ea5276f3a..ec84c473f 100644 --- a/data/shader/text.vsh +++ b/data/shader/text.vsh @@ -12,11 +12,11 @@ struct GlyphInfo { vec2 size; }; -layout (set = 3, binding = 1, std430) buffer GlyphBuffer { +layout (set = 3, binding = 1, std430) readonly buffer GlyphBuffer { GlyphInfo glyphs[]; }; -layout (set = 3, binding = 2, std430) buffer InstancesBuffer { +layout (set = 3, binding = 2, std430) readonly buffer InstancesBuffer { vec4 instances[]; }; diff --git a/data/shader/upsampleSpatial.csh b/data/shader/upsampleSpatial.csh index 9cf2fdd2f..08e53fc29 100644 --- a/data/shader/upsampleSpatial.csh +++ b/data/shader/upsampleSpatial.csh @@ -65,7 +65,7 @@ void main() { pixel.y > imageSize(textureOut).y) return; - vec2 uv = (vec2(pixel) + vec2(0.5)) / vec2(imageSize(textureOut)); + vec2 uv = (vec2(pixel)) / vec2(imageSize(textureOut)); vec4 color = SampleCatmullRom(uv); diff --git a/data/shader/vegetation/vegetation.fsh b/data/shader/vegetation/vegetation.fsh index e0f47c8dc..94a033287 100644 --- a/data/shader/vegetation/vegetation.fsh +++ b/data/shader/vegetation/vegetation.fsh @@ -6,8 +6,9 @@ layout (location = 0) out vec3 baseColorFS; layout (location = 1) out vec2 normalFS; layout (location = 2) out vec2 geometryNormalFS; layout (location = 3) out vec3 roughnessMetalnessAoFS; -layout (location = 4) out uint materialIdxFS; -layout (location = 5) out vec2 velocityFS; +layout (location = 4) out vec3 emissiveFS; +layout (location = 5) out uint materialIdxFS; +layout (location = 6) out vec2 velocityFS; #ifdef BASE_COLOR_MAP layout(set = 3, binding = 0) uniform sampler2D baseColorMap; @@ -47,7 +48,7 @@ layout(location=5) in vec4 vertexColorsVS; #endif #if defined(NORMAL_MAP) || defined(HEIGHT_MAP) -layout(location=6) in mat3 TBN; +layout(location=7) in mat3 TBN; #endif layout(push_constant) uniform constants { @@ -84,7 +85,7 @@ void main() { baseColorFS *= textureColor.rgb; #endif #ifdef VERTEX_COLORS - baseColorFS.rgb *= vertexColorsVS.rgb; + //baseColorFS.rgb *= vertexColorsVS.rgb; #endif vec3 geometryNormal = normalize(normalVS); diff --git a/data/shader/vegetation/vegetation.vsh b/data/shader/vegetation/vegetation.vsh index e8d33ab75..3b96165db 100644 --- a/data/shader/vegetation/vegetation.vsh +++ b/data/shader/vegetation/vegetation.vsh @@ -18,7 +18,7 @@ layout(location=4) in vec4 vVertexColors; #endif // Vertex out parameters -#ifdef NORMAl_MAP +#ifdef NORMAL_MAP layout(location=0) out vec3 positionVS; #endif layout(location=1) out vec3 normalVS; @@ -32,6 +32,11 @@ layout(location=4) out vec3 ndcLastVS; layout(location=5) out vec4 vertexColorsVS; #endif +#if defined(NORMAL_MAP) || defined(HEIGHT_MAP) +layout(location=7) out mat3 TBN; +#endif + + layout(set = 3, binding = 7) uniform sampler2D windNoiseMap; layout(std430, set = 3, binding = 8) readonly buffer InstanceData { @@ -84,7 +89,7 @@ void main() { #if defined(NORMAL_MAP) || defined(HEIGHT_MAP) vec3 normal = normalize(normalVS); - float correctionFactor = vTangent.w * (PushConstants.invertUVs > 0 ? -1.0 : 1.0); + float correctionFactor = vTangent.w * (pushConstants.invertUVs > 0 ? -1.0 : 1.0); vec3 tangent = normalize(mat3(mvMatrix) * vTangent.xyz); vec3 bitangent = normalize(correctionFactor * diff --git a/data/shader/volumetric/fog.hsh b/data/shader/volumetric/fog.hsh index 9b1f0e71a..e7d87370d 100644 --- a/data/shader/volumetric/fog.hsh +++ b/data/shader/volumetric/fog.hsh @@ -37,10 +37,18 @@ float GetVolumetricFogDensity(Fog fog, vec3 worldPos) { // Henyey-Greenstein phase function https://www.astro.umd.edu/~jph/HG_note.pdf float ComputeScattering(float scatteringAnisotropy, float lightDotView) { + // Range [-1;1] float g = scatteringAnisotropy; float g2 = g * g; - float result = 1.0 - g2; - result /= (4.0 * 3.14 * pow(1.0 + g2 - (2.0 * g) * lightDotView, 1.5)); - return result; + return (1.0 - g2) / (4.0 * 3.14 * pow(1.0 + g2 - (2.0 * g) * lightDotView, 1.5)); + + + /* + float g = scatteringAnisotropy; + float k = 1.55*g - 0.55*g*g*g; + float k2 = k * k; + return (1.0 - k2) / (4.0 * 3.14 * pow(1.0 - k * lightDotView, 2.0)); + */ + } diff --git a/data/shader/volumetric/lightCulling.csh b/data/shader/volumetric/lightCulling.csh new file mode 100644 index 000000000..e2928118f --- /dev/null +++ b/data/shader/volumetric/lightCulling.csh @@ -0,0 +1,120 @@ +layout (local_size_x = 16, local_size_y = 16) in; + +#include <../deferred/lightCulling.hsh> + +#include <../structures.hsh> +#include <../shadow.hsh> +#include <../globals.hsh> + +#include <../common/convert.hsh> +#include <../common/utility.hsh> + +layout(set = 3, binding = 1) uniform sampler2D depthTexture; + +layout (std430, set = 3, binding = 8) buffer VolumetricLights { + VolumetricLight volumetricLights[]; +}; + +layout(std430, set = 3, binding = 10) buffer LightIndicesBuffer { + int lightIndices[]; +}; + +layout(push_constant) uniform constants { + int lightCount; +} pushConstants; + +const int lightCountPerTile = 63; +const int groupSize = int(gl_WorkGroupSize.x * gl_WorkGroupSize.y); + +// Results in 32 * 128 = 4096 possible lights in the tile +shared int sharedLightIndices[lightCountPerTile]; +shared int sharedLightCount; +shared uint sharedDepthMax; + +int GetLightIndicesOffset() { + + int groupOffset = int(gl_WorkGroupID.x + gl_WorkGroupID.y * gl_NumWorkGroups.x); + return groupOffset * (lightCountPerTile + 1); + +} + +void main() { + + ivec2 resolution = textureSize(depthTexture, 0); + ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); + + vec2 texCoord = (vec2(pixel) + 0.5) / vec2(resolution); + + float depth = texelFetch(depthTexture, pixel, 0).r; + float viewDepth = ConvertDepthToViewSpaceDepth(depth); + + if (gl_LocalInvocationIndex == 0u) { + sharedDepthMax = floatBitsToUint(depth); + sharedLightCount = 0; + } + + barrier(); + + atomicMax(sharedDepthMax, floatBitsToUint(depth)); + + barrier(); + + float depthMin = 0.0; + float depthMax = ConvertDepthToViewSpaceDepth(uintBitsToFloat(sharedDepthMax)); + + vec3 frustumCorners[8]; + GetFrustumCorners(frustumCorners, resolution, 0.0, uintBitsToFloat(sharedDepthMax)); + + Plane frustumPlanes[4]; + GetFrustumPlanes(frustumPlanes, frustumCorners); + + AABB frustumAABB = CalculateAABB(frustumCorners); + + vec3 viewPos = ConvertDepthToViewSpace(depth, texCoord); + + int localOffset = int(gl_LocalInvocationIndex); + int lightIdx = 0; + for (int i = localOffset; i < pushConstants.lightCount && lightIdx < lightCountPerTile; i += groupSize) { + bool visible = true; + + VolumetricLight light = volumetricLights[i]; + float radius = light.direction.w; + + uint lightType = floatBitsToUint(light.color.a); + Sphere sphere; + + // Remember: Forward is in -z direction + if (lightType == POINT_LIGHT) { + sphere.center = light.location.xyz; + sphere.radius = radius; + } + else if (lightType == SPOT_LIGHT) { + sphere = CalculateSphereFromSpotLight(light.location, light.direction, radius); + } + + visible = visible && SphereAABBIntersection(sphere, frustumAABB); + if (visible) { + visible = SphereFrustumIntersection(sphere, frustumPlanes); + } + + if (visible) { + lightIdx = atomicAdd(sharedLightCount, 1); + + if (lightIdx < lightCountPerTile) + sharedLightIndices[lightIdx] = i; + } + } + + barrier(); + + int lightCount = min(lightCountPerTile, sharedLightCount); + int lightIndicesOffset = GetLightIndicesOffset(); + for (int i = localOffset; i < lightCount; i += groupSize) { + lightIndices[i + lightIndicesOffset + 1] = sharedLightIndices[i]; + } + + if (gl_LocalInvocationIndex == 0u) { + lightIndices[lightIndicesOffset] = lightCount; + } + +} \ No newline at end of file diff --git a/data/shader/volumetric/volumetric.csh b/data/shader/volumetric/volumetric.csh index e5affd9e2..d69dcde57 100644 --- a/data/shader/volumetric/volumetric.csh +++ b/data/shader/volumetric/volumetric.csh @@ -1,40 +1,110 @@ -#ifdef AE_TEXTURE_SHADOW_LOD -#extension GL_EXT_texture_shadow_lod : require -#endif +#extension GL_EXT_nonuniform_qualifier : require -layout (local_size_x = 8, local_size_y = 8) in; +layout (local_size_x = 16, local_size_y = 16) in; #include <../globals.hsh> -#include <../structures> +#include <../structures.hsh> #include <../common/ign.hsh> #include <../common/convert.hsh> #include <../common/stencil.hsh> #include <../common/utility.hsh> #include <../common/random.hsh> #include <../clouds/shadow.hsh> +#include <../common/bluenoise.hsh> #include layout(set = 3, binding = 0, rgba16f) writeonly uniform image2D volumetricImage; layout(set = 3, binding = 1) uniform sampler2D depthTexture; -layout(set = 3, binding = 2) uniform sampler2DArrayShadow cascadeMaps; -layout(set = 3, binding = 3) uniform sampler2D cloudMap; -layout(set = 3, binding = 4) uniform sampler2D oceanDepthTexture; -layout(set = 3, binding = 5) uniform usampler2D oceanStencilTexture; -layout(set = 3, binding = 6) uniform sampler2D volumetricCloudTexture; +layout(set = 3, binding = 2) uniform sampler2D cloudMap; +layout(set = 3, binding = 3) uniform sampler2D oceanDepthTexture; +layout(set = 3, binding = 4) uniform usampler2D oceanStencilTexture; +layout(set = 3, binding = 5) uniform sampler2D volumetricCloudTexture; +layout(set = 3, binding = 6) uniform sampler shadowSampler; layout(std140, set = 3, binding = 7) uniform UniformBuffer { int sampleCount; float intensity; int fogEnabled; float oceanHeight; + int lightCount; + int offsetX; + int offsetY; + int directionalLightCount; vec4 planetCenterAndRadius; Fog fog; - Light light; CloudShadow cloudShadow; } uniforms; -vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords); +layout (std430, set = 3, binding = 8) buffer VolumetricLights { + VolumetricLight volumetricLights[]; +}; + +layout (std430, set = 3, binding = 9) buffer VolumetricShadows { + Shadow volumetricShadows[]; +}; + +layout(std430, set = 3, binding = 10) buffer LightIndicesBuffer { + int lightIndices[]; +}; + +layout(set = 3, binding = 11) uniform texture2DArray cascadeMaps[8]; +layout(set = 3, binding = 19) uniform textureCube cubeMaps[8]; + +const int lightCountPerTile = 63; + +shared uint sharedLightIndices[64]; +shared int sharedLightIndicesCount; + +vec4 ComputeVolumetricDirectionalLights(vec3 fragPos, float startDepth, vec2 texCoords); +vec4 ComputeVolumetricPunctualLights(uint lightType, vec3 fragPos, float startDepth, vec2 texCoords); +float GetShadowFactorDirectionalLight(int lightIdx, int shadowIdx, uint lightType, bool isMain, vec3 currentPosition); +float GetShadowFactorPunctualLight(int lightIdx, int shadowIdx, uint lightType, bool isMain, vec3 currentPosition); + +int GetLightIndicesOffset() { + + int groupOffset = int(gl_WorkGroupID.x + gl_WorkGroupID.y * gl_NumWorkGroups.x); + return groupOffset * (lightCountPerTile + 1); + +} + +void LoadLightTypeData(uint requestedLightType) { + + barrier(); + + int groupSize = int(gl_WorkGroupSize.x * gl_WorkGroupSize.y); + int localOffset = int(gl_LocalInvocationIndex); + + if (localOffset == 0) { + sharedLightIndicesCount = 0; + } + + barrier(); + + int groupOffset = GetLightIndicesOffset(); + int lightCount = min(lightCountPerTile, lightIndices[groupOffset]); + + int lastIdx = 0; + for (int i = localOffset; i < lightCount; i += groupSize) { + uint lightIdx = lightIndices[groupOffset + i + 1]; + uint lightType = floatBitsToUint(volumetricLights[lightIdx].color.a); + if (requestedLightType != lightType) + continue; + + int idx = atomicAdd(sharedLightIndicesCount, 1); + if (idx < lightCountPerTile) + sharedLightIndices[idx] = lightIdx; + } + + barrier(); + + if (localOffset == 0) { + sharedLightIndicesCount = min(lightCount, sharedLightIndicesCount); + } + + barrier(); + +} void main() { @@ -64,13 +134,28 @@ void main() { } #endif + bool validPixel = pixel.x < imageSize(volumetricImage).x && + pixel.y < imageSize(volumetricImage).y; + vec4 radiance = vec4(0.0); - radiance = ComputeVolumetric(endPos, startDepth, texCoord); - imageStore(volumetricImage, pixel, radiance); + radiance = ComputeVolumetricDirectionalLights(endPos, startDepth, texCoord); + +#ifdef LOCAL_LIGHTS + LoadLightTypeData(POINT_LIGHT); + if (sharedLightIndicesCount > 0) + radiance.rgb += ComputeVolumetricPunctualLights(POINT_LIGHT, endPos, startDepth, texCoord).rgb; + + LoadLightTypeData(SPOT_LIGHT); + if (sharedLightIndicesCount > 0) + radiance.rgb += ComputeVolumetricPunctualLights(SPOT_LIGHT, endPos, startDepth, texCoord).rgb; +#endif + + if (validPixel) + imageStore(volumetricImage, pixel, radiance); } -vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { +vec4 ComputeVolumetricDirectionalLights(vec3 fragPos, float startDepth, vec2 texCoords) { vec2 resolution = vec2(imageSize(volumetricImage)); vec3 viewPosition = vec3(globalData.ivMatrix * vec4(fragPos, 1.0)); @@ -91,60 +176,187 @@ vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { float noiseOffset = GetInterleavedGradientNoise(interleavedTexCoords); vec3 currentPosition = stepVector * (noiseOffset + startDepth); - int cascadeIndex = 0; - int lastCascadeIndex = 0; - mat4 cascadeMatrix = uniforms.light.shadow.cascades[0].cascadeSpace; - #ifdef CLOUDS bool receivedCloudExtinction = false; float cloudExtinction = min(textureLod(volumetricCloudTexture, texCoords, 0.0).a, 1.0); #endif for (int i = 0; i < uniforms.sampleCount; i++) { + + float t0 = float(i) / float(uniforms.sampleCount); + float t1 = float(i + 1) / float(uniforms.sampleCount); + + t0 = t0 * t0; + t1 = t1 * t1; + + float delta = t1 - t0; + float t = t0 + delta * noiseOffset; + + currentPosition = rayDirection * t * rayLength + startDepth; + stepLength = delta * rayLength; + + vec3 worldPosition = vec3(globalData.ivMatrix * vec4(currentPosition, 1.0)); + + vec3 lightScattering = vec3(0.0); + vec3 lightAmbient = vec3(1.0); - float dist = -currentPosition.z; + // First all directional lights + for(int j = 0; j < uniforms.directionalLightCount; j++) { + VolumetricLight light = volumetricLights[j]; - int cascadeIndex = 0; + uint lightType = floatBitsToUint(light.color.a); + + bool isMain = j == 0 ? true : false; + float shadowValue = GetShadowFactorDirectionalLight(j, light.shadowIdx, lightType, isMain, currentPosition); + + vec3 lightPosition = currentPosition - 10000.0 * light.direction.xyz; + lightPosition = vec3(globalData.ivMatrix * vec4(lightPosition, 1.0)); + float extinctionToLight = ComputeVolumetricFog(uniforms.fog, worldPosition, lightPosition); + float NdotL = dot(normalize(rayDirection), normalize(light.direction.xyz)); + + float phaseFunction = uniforms.fogEnabled > 0 ? + ComputeScattering(uniforms.fog.scatteringAnisotropy, NdotL) : 1.0; - cascadeIndex = dist >= uniforms.light.shadow.cascades[0].distance ? 1 : cascadeIndex; - cascadeIndex = dist >= uniforms.light.shadow.cascades[1].distance ? 2 : cascadeIndex; - cascadeIndex = dist >= uniforms.light.shadow.cascades[2].distance ? 3 : cascadeIndex; - cascadeIndex = dist >= uniforms.light.shadow.cascades[3].distance ? 4 : cascadeIndex; - cascadeIndex = dist >= uniforms.light.shadow.cascades[4].distance ? 5 : cascadeIndex; - - cascadeIndex = min(uniforms.light.shadow.cascadeCount - 1, cascadeIndex); - - if (lastCascadeIndex != cascadeIndex) { - cascadeMatrix = uniforms.light.shadow.cascades[0].cascadeSpace; - cascadeMatrix = cascadeIndex > 0 ? uniforms.light.shadow.cascades[1].cascadeSpace : cascadeMatrix; - cascadeMatrix = cascadeIndex > 1 ? uniforms.light.shadow.cascades[2].cascadeSpace : cascadeMatrix; - cascadeMatrix = cascadeIndex > 2 ? uniforms.light.shadow.cascades[3].cascadeSpace : cascadeMatrix; - cascadeMatrix = cascadeIndex > 3 ? uniforms.light.shadow.cascades[4].cascadeSpace : cascadeMatrix; - cascadeMatrix = cascadeIndex > 4 ? uniforms.light.shadow.cascades[5].cascadeSpace : cascadeMatrix; + lightScattering += shadowValue * phaseFunction * light.color.rgb * light.intensity * extinctionToLight; } - lastCascadeIndex = cascadeIndex; +#ifdef CLOUDS + vec3 planetCenter = uniforms.planetCenterAndRadius.xyz; + float cloudInnerRadius = uniforms.planetCenterAndRadius.w; - vec4 cascadeSpace = cascadeMatrix * vec4(currentPosition, 1.0); - cascadeSpace.xyz /= cascadeSpace.w; + float distToPlanetCenter = distance(worldPosition, planetCenter); +#endif - cascadeSpace.xy = cascadeSpace.xy * 0.5 + 0.5; + float density = uniforms.fog.density * GetVolumetricFogDensity(uniforms.fog, worldPosition); + + vec3 scatteringCoefficient = uniforms.fog.scatteringFactor * + uniforms.fog.extinctionCoefficients.rgb * density; + vec4 extinctionCoefficient = uniforms.fog.extinctionFactor * + uniforms.fog.extinctionCoefficients * density; + + vec4 clampedExtinction = max(extinctionCoefficient, 0.0000001); + vec4 stepExtinction = exp(-extinctionCoefficient * stepLength); + + vec3 stepScattering = scatteringCoefficient * (lightScattering + vec3(uniforms.fog.ambientFactor) * lightAmbient); - float shadowValue = 1.0; -#ifdef SHADOWS -#ifdef AE_TEXTURE_SHADOW_LOD - // This fixes issues that can occur at cascade borders - shadowValue = textureLod(cascadeMaps, - vec4(cascadeSpace.xy, cascadeIndex, cascadeSpace.z), 0); + vec3 luminanceIntegral = (stepScattering - stepScattering * stepExtinction.rgb) / clampedExtinction.rgb; +#ifdef CLOUDS + foginess += luminanceIntegral * extinctionWithClouds.rgb; + + if (distToPlanetCenter > cloudInnerRadius && !receivedCloudExtinction) { + extinctionWithClouds *= uniforms.fogEnabled > 0 ? stepExtinction * cloudExtinction : vec4(1.0); + receivedCloudExtinction = true; + } + else { + extinctionWithClouds *= uniforms.fogEnabled > 0 ? stepExtinction : vec4(1.0); + } #else - shadowValue = texture(cascadeMaps, - vec4(cascadeSpace.xy, cascadeIndex, cascadeSpace.z)); + foginess += luminanceIntegral * extinction.rgb; #endif + + extinction *= uniforms.fogEnabled > 0 ? stepExtinction : vec4(1.0); + + currentPosition += stepVector; + + } + + return vec4(foginess * uniforms.intensity, extinction.a); + +} + +vec4 ComputeVolumetricPunctualLights(uint lightType, vec3 fragPos, float startDepth, vec2 texCoords) { + + vec2 resolution = vec2(imageSize(volumetricImage)); + vec3 viewPosition = vec3(globalData.ivMatrix * vec4(fragPos, 1.0)); + + // We compute this in view space + vec3 rayVector = fragPos; + float rayLength = clamp(length(rayVector) - startDepth, 0.0, 100.0); + vec3 rayDirection = normalize(rayVector); + float stepLength = rayLength / float(uniforms.sampleCount); + vec3 stepVector = rayDirection * stepLength; + + vec3 foginess = vec3(0.0); + vec4 extinction = vec4(1.0); + vec4 extinctionWithClouds = vec4(1.0); + + vec2 interleavedTexCoords = (0.5 * texCoords + 0.5) * resolution; + + float noiseOffset = GetInterleavedGradientNoise(interleavedTexCoords); + vec3 currentPosition = stepVector * (noiseOffset + startDepth); + +#ifdef CLOUDS + bool receivedCloudExtinction = false; + float cloudExtinction = min(textureLod(volumetricCloudTexture, texCoords, 0.0).a, 1.0); #endif - //shadowValue = distance > uniforms.light.shadow.distance ? 1.0 : shadowValue; + for (int i = 0; i < uniforms.sampleCount; i++) { + + float t0 = float(i) / float(uniforms.sampleCount); + float t1 = float(i + 1) / float(uniforms.sampleCount); + + t0 = t0 * t0; + t1 = t1 * t1; + + float delta = t1 - t0; + float t = t0 + delta * noiseOffset; + + //currentPosition = rayDirection * t * rayLength + startDepth; + //stepLength = delta * rayLength; + vec3 worldPosition = vec3(globalData.ivMatrix * vec4(currentPosition, 1.0)); + vec3 lightScattering = vec3(0.0); + + // Then all punctual lights + for(int j = 0; j < sharedLightIndicesCount; j++) { + int lightIdx = int(sharedLightIndices[j]); + VolumetricLight light = volumetricLights[lightIdx]; + + float NdotL = 1.0; + float attenuation = 1.0; + float sqrDistance = 1.0; + + float shadowValue = 1.0; + + float radius = light.direction.w; + if (lightType == POINT_LIGHT) { + vec3 pointToLight = light.location.xyz - currentPosition; + sqrDistance = max(0.0, dot(pointToLight, pointToLight)); + + attenuation = saturate(1.0 - pow(sqrDistance / (radius * radius), 4.0)); + if (attenuation == 0.0) + continue; + + NdotL = dot(-normalize(light.location.xyz), rayDirection); + shadowValue = GetShadowFactorPunctualLight(lightIdx, light.shadowIdx, POINT_LIGHT, false, currentPosition); + } + else if (lightType == SPOT_LIGHT) { + vec3 pointToLight = light.location.xyz - currentPosition; + sqrDistance = dot(pointToLight, pointToLight); + + vec3 L = pointToLight / sqrt(sqrDistance); + + float strength = dot(L, normalize(-light.direction.xyz)); + float angleAttenuation = saturate(strength * light.specific0 + light.specific1); + float distAttenuation = saturate(1.0 - pow(sqrDistance / (radius * radius), 4.0)); + if (angleAttenuation == 0.0 || distAttenuation == 0.0) + continue; + + NdotL = dot(-normalize(light.location.xyz), rayDirection); + attenuation = min(distAttenuation * sqr(angleAttenuation), 1.0); + shadowValue = GetShadowFactorPunctualLight(lightIdx, light.shadowIdx, SPOT_LIGHT, false, currentPosition); + } + + // No shadows for now + //shadowValue = 1.0; + + float phaseFunction = uniforms.fogEnabled > 0 ? + ComputeScattering(uniforms.fog.scatteringAnisotropy, NdotL) : 1.0; + + lightScattering += phaseFunction * shadowValue * light.color.rgb * attenuation * light.intensity / max(sqrDistance, 0.1); + } + #ifdef CLOUDS vec3 planetCenter = uniforms.planetCenterAndRadius.xyz; float cloudInnerRadius = uniforms.planetCenterAndRadius.w; @@ -152,12 +364,6 @@ vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { float distToPlanetCenter = distance(worldPosition, planetCenter); #endif -#ifdef CLOUD_SHADOWS - float cloudShadowValue = CalculateCloudShadow(currentPosition, uniforms.cloudShadow, cloudMap); - cloudShadowValue = distToPlanetCenter < cloudInnerRadius ? cloudShadowValue : 1.0; - shadowValue = min(shadowValue, cloudShadowValue); -#endif - float density = uniforms.fog.density * GetVolumetricFogDensity(uniforms.fog, worldPosition); vec3 scatteringCoefficient = uniforms.fog.scatteringFactor * @@ -165,15 +371,10 @@ vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { vec4 extinctionCoefficient = uniforms.fog.extinctionFactor * uniforms.fog.extinctionCoefficients * density; - float NdotL = dot(rayDirection, uniforms.light.direction.xyz); - vec4 clampedExtinction = max(extinctionCoefficient, 0.0000001); vec4 stepExtinction = exp(-extinctionCoefficient * stepLength); - float phaseFunction = uniforms.fogEnabled > 0 ? - ComputeScattering(uniforms.fog.scatteringAnisotropy, NdotL) : 1.0; - vec3 stepScattering = scatteringCoefficient * (shadowValue * phaseFunction * uniforms.light.color.rgb - + vec3(uniforms.fog.ambientFactor)); + vec3 stepScattering = scatteringCoefficient * lightScattering; vec3 luminanceIntegral = (stepScattering - stepScattering * stepExtinction.rgb) / clampedExtinction.rgb; #ifdef CLOUDS @@ -198,4 +399,124 @@ vec4 ComputeVolumetric(vec3 fragPos, float startDepth, vec2 texCoords) { return vec4(foginess * uniforms.intensity, extinction.a); +} + +float GetShadowFactorDirectionalLight(int lightIdx, int shadowIdx, uint lightType, bool isMain, vec3 currentPosition) { + + float dist = -currentPosition.z; + +#ifndef AE_BINDLESS + int mapIdx = lightIdx; +#else + int mapIdx = volumetricShadows[shadowIdx].mapIdx; +#endif + + if (shadowIdx < 0 || dist > volumetricShadows[shadowIdx].distance || mapIdx < 0) + return 1.0; + + float shadowValue = 1.0; + + + int cascadeIndex = 0; + cascadeIndex = dist >= volumetricShadows[shadowIdx].cascades[0].distance ? 1 : cascadeIndex; + cascadeIndex = dist >= volumetricShadows[shadowIdx].cascades[1].distance ? 2 : cascadeIndex; + cascadeIndex = dist >= volumetricShadows[shadowIdx].cascades[2].distance ? 3 : cascadeIndex; + cascadeIndex = dist >= volumetricShadows[shadowIdx].cascades[3].distance ? 4 : cascadeIndex; + cascadeIndex = dist >= volumetricShadows[shadowIdx].cascades[4].distance ? 5 : cascadeIndex; + + cascadeIndex = min(volumetricShadows[shadowIdx].cascadeCount - 1, cascadeIndex); + + mat3x4 cascadeMatrix = volumetricShadows[shadowIdx].cascades[0].cascadeSpace; + cascadeMatrix = cascadeIndex > 0 ? volumetricShadows[shadowIdx].cascades[1].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 1 ? volumetricShadows[shadowIdx].cascades[2].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 2 ? volumetricShadows[shadowIdx].cascades[3].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 3 ? volumetricShadows[shadowIdx].cascades[4].cascadeSpace : cascadeMatrix; + cascadeMatrix = cascadeIndex > 4 ? volumetricShadows[shadowIdx].cascades[5].cascadeSpace : cascadeMatrix; + + float positionLength = dist; + float positionRatio = positionLength / volumetricShadows[shadowIdx].distance; + vec3 shadowPosition = positionRatio <= 1.0 ? currentPosition : currentPosition / positionRatio; + vec4 cascadeSpace = mat4(transpose(cascadeMatrix)) * vec4(currentPosition, 1.0); + cascadeSpace.xyz /= cascadeSpace.w; + + cascadeSpace.xy = cascadeSpace.xy * 0.5 + 0.5; + +#ifdef AE_BINDLESS + shadowValue = texture(sampler2DArrayShadow(bindlessTextureArrays[nonuniformEXT(mapIdx)], shadowSampler), + vec4(cascadeSpace.xy, cascadeIndex, cascadeSpace.z)); +#else + shadowValue = texture(sampler2DArrayShadow(cascadeMaps[nonuniformEXT(mapIdx)], shadowSampler), + vec4(cascadeSpace.xy, cascadeIndex, cascadeSpace.z)); +#endif + + vec3 worldPosition = vec3(globalData.ivMatrix * vec4(currentPosition, 1.0)); + +#ifdef CLOUD_SHADOWS + if (isMain) { + vec3 planetCenter = uniforms.planetCenterAndRadius.xyz; + float cloudInnerRadius = uniforms.planetCenterAndRadius.w; + float distToPlanetCenter = distance(worldPosition, planetCenter); + float cloudShadowValue = CalculateCloudShadow(currentPosition, uniforms.cloudShadow, cloudMap); + cloudShadowValue = distToPlanetCenter < cloudInnerRadius ? cloudShadowValue : 1.0; + shadowValue = min(shadowValue, cloudShadowValue); + } +#endif + + return min(shadowValue, 1.0); + +} + +float GetShadowFactorPunctualLight(int lightIdx, int shadowIdx, uint lightType, bool isMain, vec3 currentPosition) { + + float dist = -currentPosition.z; + +#ifndef AE_BINDLESS + int mapIdx = lightIdx; +#else + int mapIdx = volumetricShadows[shadowIdx].mapIdx; +#endif + + if (shadowIdx < 0 || dist > volumetricShadows[shadowIdx].distance || mapIdx < 0) + return 1.0; + + float shadowValue = 1.0; + if (lightType == POINT_LIGHT) { +#ifdef AE_BINDLESS + mat4 projectionMatrix; + projectionMatrix[0] = volumetricShadows[shadowIdx].cascades[1].cascadeSpace[0]; + projectionMatrix[1] = volumetricShadows[shadowIdx].cascades[1].cascadeSpace[1]; + projectionMatrix[2] = volumetricShadows[shadowIdx].cascades[1].cascadeSpace[2]; + projectionMatrix[3] = volumetricShadows[shadowIdx].cascades[2].cascadeSpace[0]; + + vec4 shadowCoords = mat4(transpose(volumetricShadows[shadowIdx].cascades[0].cascadeSpace)) * vec4(currentPosition, 1.0); + shadowCoords.y *= -1.0; + vec4 absPosition = abs(shadowCoords); + float depth = -max(absPosition.x, max(absPosition.y, absPosition.z)); + vec4 clip = projectionMatrix * vec4(0.0, 0.0, depth, 1.0); + depth = clip.z / clip.w; + + shadowValue = clamp(texture(samplerCubeShadow(bindlessCubemaps[nonuniformEXT(mapIdx)], shadowSampler), + vec4(shadowCoords.xyz , depth - 0.0001)), 0.0, 1.0); +#endif + } + else if (lightType == SPOT_LIGHT) { +#ifdef AE_BINDLESS + mat4 shadowMatrix; + shadowMatrix[0] = volumetricShadows[shadowIdx].cascades[0].cascadeSpace[0]; + shadowMatrix[1] = volumetricShadows[shadowIdx].cascades[0].cascadeSpace[1]; + shadowMatrix[2] = volumetricShadows[shadowIdx].cascades[0].cascadeSpace[2]; + shadowMatrix[3] = volumetricShadows[shadowIdx].cascades[1].cascadeSpace[0]; + + vec4 cascadeSpace = shadowMatrix * vec4(currentPosition, 1.0); + cascadeSpace.xyz /= cascadeSpace.w; + + cascadeSpace.xy = cascadeSpace.xy * 0.5 + 0.5; + + shadowValue = texture(sampler2DArrayShadow(bindlessTextureArrays[nonuniformEXT(mapIdx)], shadowSampler), + vec4(cascadeSpace.xy, 0.0, cascadeSpace.z)); +#endif + } + + return min(shadowValue, 1.0); + } \ No newline at end of file diff --git a/data/shader/volumetric/volumetricResolve.csh b/data/shader/volumetric/volumetricResolve.csh index fd02e60f7..e1ee9079f 100644 --- a/data/shader/volumetric/volumetricResolve.csh +++ b/data/shader/volumetric/volumetricResolve.csh @@ -20,6 +20,8 @@ layout(set = 3, binding = 3) uniform sampler2D lowResVolumetricCloudsTexture; layout(set = 3, binding = 5) uniform UniformBuffer { Fog fog; vec4 planetCenter; + vec4 mainLightColor; + vec4 mainLightDirection; int downsampled2x; int cloudsEnabled; int fogEnabled; @@ -70,6 +72,13 @@ const ivec2 offsets[9] = ivec2[9]( ivec2(1, 1) ); +const ivec2 pixelOffsets[4] = ivec2[4]( + ivec2(0, 0), + ivec2(1, 0), + ivec2(0, 1), + ivec2(1, 1) +); + int NearestDepth(float referenceDepth, float[9] depthVec) { int idx = 4; @@ -88,14 +97,41 @@ int NearestDepth(float referenceDepth, float[9] depthVec) { void Upsample2x(float referenceDepth, vec2 texCoord, out vec4 volumetric, out vec4 volumetricClouds) { ivec2 pixel = ivec2(gl_LocalInvocationID) / 2 + ivec2(1); + vec2 highResPixel = texCoord * vec2(imageSize(resolveImage)); - float invocationDepths[9]; + highResPixel /= 2.0; - float minWeight = 1.0; + float x = fract(highResPixel.x); + float y = fract(highResPixel.y); + + float weights[4] = { (1 - x) * (1 - y), x * (1 - y), (1 - x) * y, x * y }; referenceDepth = ConvertDepthToViewSpaceDepth(referenceDepth); float depthPhi = 128.0 / max(1.0, abs(referenceDepth)); + volumetric = vec4(0.0); + + float totalWeight = 0.0; + for (uint i = 0; i < 4; i++) { + int sharedMemoryOffset = Flatten2D(pixel + pixelOffsets[i], unflattenedDepthDataSize); + + float depth = ConvertDepthToViewSpaceDepth(depths[sharedMemoryOffset]); + + float depthDiff = abs(referenceDepth - depth); + float depthWeight = min(exp(-depthDiff * depthPhi), 1.0); + + float edgeWeight = depthWeight; + float weight = edgeWeight * weights[i]; + + volumetric += volumetrics[sharedMemoryOffset] * weight; + totalWeight += weight; + } + + volumetric /= totalWeight; + + float invocationDepths[9]; + + float minWeight = 1.0; for (uint i = 0; i < 9; i++) { int sharedMemoryOffset = Flatten2D(pixel + offsets[i], unflattenedDepthDataSize); @@ -112,7 +148,9 @@ void Upsample2x(float referenceDepth, vec2 texCoord, out vec4 volumetric, out ve int idx = NearestDepth(referenceDepth, invocationDepths); int offset = Flatten2D(pixel + offsets[idx], unflattenedDepthDataSize); - volumetric = volumetrics[offset]; + if (totalWeight < 10e-9) { + volumetric = volumetrics[offset]; + } #ifdef CLOUDS vec4 bilinearCloudScattering = texture(lowResVolumetricCloudsTexture, texCoord); volumetricClouds = mix(clouds[offset], bilinearCloudScattering, minWeight); @@ -151,10 +189,24 @@ void main() { vec3 resolve = imageLoad(resolveImage, pixel).rgb; #ifndef RAYMARCHED_FOG + float LdotV = dot(worldDirection, normalize(uniforms.mainLightDirection.xyz)); + float phaseFunction = ComputeScattering(uniforms.fog.scatteringAnisotropy, LdotV); + vec3 worldPosition = vec3(globalData.ivMatrix * vec4(viewPosition, 1.0)); volumetricFog.a = ComputeVolumetricFog(uniforms.fog, globalData.cameraLocation.xyz, worldPosition); - volumetricFog.rgb = uniforms.fog.extinctionCoefficients.rgb * clamp(1.0 - volumetricFog.a, 0.0, 1.0); + vec3 lightPosition = worldPosition - 10000.0 * normalize(uniforms.mainLightDirection.xyz); + lightPosition = vec3(globalData.ivMatrix * vec4(lightPosition, 1.0)); + float extinctionToLight = ComputeVolumetricFog(uniforms.fog, worldPosition, lightPosition); + + vec3 scatteringCoefficient = uniforms.fog.scatteringFactor * + uniforms.fog.extinctionCoefficients.rgb; + + vec3 lightScattering = phaseFunction * uniforms.mainLightColor.rgb; + + vec3 scattering = scatteringCoefficient * (lightScattering + vec3(uniforms.fog.ambientFactor)); + + volumetricFog.rgb = scattering * clamp(1.0 - volumetricFog.a, 0.0, 1.0); #endif resolve = ApplyVolumetrics(uniforms.fog, resolve, volumetricFog, volumetricClouds, diff --git a/libs/ImguiExtension/CMakeLists.txt b/libs/ImguiExtension/CMakeLists.txt index 16e8521de..d792b3a74 100644 --- a/libs/ImguiExtension/CMakeLists.txt +++ b/libs/ImguiExtension/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.25) project(ImguiExtension) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../src/engine) @@ -24,9 +24,10 @@ foreach(SOURCE_FILE IN ITEMS ${IMGUI_SOURCE_FILES}) endforeach() find_package(imgui CONFIG REQUIRED) +find_package(implot CONFIG REQUIRED) # We cam do this here, since the ImguiExtension dependency uses the engine and therefore is always included # after the engine is added add_library(${PROJECT_NAME} STATIC ${IMGUI_SOURCE_FILES}) target_compile_definitions(${PROJECT_NAME} PUBLIC ${ATLAS_ENGINE_COMPILE_DEFINITIONS}) -target_link_libraries(${PROJECT_NAME} imgui::imgui volk::volk volk::volk_headers SDL2::SDL2 GPUOpen::VulkanMemoryAllocator) \ No newline at end of file +target_link_libraries(${PROJECT_NAME} imgui::imgui implot::implot volk::volk volk::volk_headers SDL2::SDL2 GPUOpen::VulkanMemoryAllocator Jolt::Jolt) \ No newline at end of file diff --git a/libs/ImguiExtension/ImguiWrapper.cpp b/libs/ImguiExtension/ImguiWrapper.cpp index 6d0cadf92..b9344db59 100644 --- a/libs/ImguiExtension/ImguiWrapper.cpp +++ b/libs/ImguiExtension/ImguiWrapper.cpp @@ -11,6 +11,8 @@ namespace Atlas::ImguiExtension { void ImguiWrapper::Load(Atlas::Window *window) { + ImPlot::CreateContext(); + // Setup back-end capabilities flags ImGuiIO &io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) @@ -65,6 +67,8 @@ namespace Atlas::ImguiExtension { // Unsubscribe from all events ImGui_ImplVulkan_Shutdown(); + ImPlot::DestroyContext(); + pool.reset(); initialized = false; diff --git a/libs/ImguiExtension/ImguiWrapper.h b/libs/ImguiExtension/ImguiWrapper.h index caa847174..3284061dd 100644 --- a/libs/ImguiExtension/ImguiWrapper.h +++ b/libs/ImguiExtension/ImguiWrapper.h @@ -4,6 +4,9 @@ #include #include +// Implot +#include + // Atlas engine includes #include #include diff --git a/libs/ImguiExtension/panels/FogPanel.cpp b/libs/ImguiExtension/panels/FogPanel.cpp index 2b8935f92..fefa1f27c 100644 --- a/libs/ImguiExtension/panels/FogPanel.cpp +++ b/libs/ImguiExtension/panels/FogPanel.cpp @@ -22,7 +22,8 @@ namespace Atlas::ImguiExtension { "%.3f", ImGuiSliderFlags_Logarithmic); ImGui::Text("Volumetric"); ImGui::Checkbox("Raymarching", &fog->rayMarching); - ImGui::SliderInt("Raymarch step count", &fog->rayMarchStepCount, 1, 32); + ImGui::Checkbox("Local lights", &fog->localLights); + ImGui::DragInt("Raymarch step count", &fog->rayMarchStepCount, 1, 128); ImGui::SliderFloat("Intensity", &fog->volumetricIntensity, 0.0f, 1.0f); ImGui::PopID(); diff --git a/libs/ImguiExtension/panels/GPUProfilerPanel.cpp b/libs/ImguiExtension/panels/GPUProfilerPanel.cpp index 9a668e253..87818084f 100644 --- a/libs/ImguiExtension/panels/GPUProfilerPanel.cpp +++ b/libs/ImguiExtension/panels/GPUProfilerPanel.cpp @@ -1,6 +1,7 @@ #include "GPUProfilerPanel.h" #include "graphics/Profiler.h" +#include "Clock.h" namespace Atlas::ImguiExtension { @@ -10,8 +11,20 @@ namespace Atlas::ImguiExtension { bool enabled = Graphics::Profiler::enable; ImGui::Checkbox("Enable##Profiler", &enabled); + ImGui::Checkbox("Show graph##Profiler", &showGraph); Graphics::Profiler::enable = enabled; + if (showGraph) + perfGraphPanel.Render(); + else + RenderTable(); + + ImGui::PopID(); + + } + + void GPUProfilerPanel::RenderTable() { + const char* items[] = { "Chronologically", "Max time", "Min time" }; static int item = 0; ImGui::Combo("Sort##Performance", &item, items, IM_ARRAYSIZE(items)); @@ -48,7 +61,6 @@ namespace Atlas::ImguiExtension { ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; if (ImGui::BeginTable("PerfTable", 2, flags)) { - // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); ImGui::TableSetupColumn("Elapsed (ms)", ImGuiTableColumnFlags_NoHide); ImGui::TableHeadersRow(); @@ -75,8 +87,6 @@ namespace Atlas::ImguiExtension { ImGui::EndTable(); } - ImGui::PopID(); - } } \ No newline at end of file diff --git a/libs/ImguiExtension/panels/GPUProfilerPanel.h b/libs/ImguiExtension/panels/GPUProfilerPanel.h index 7af0ef17e..fcc28b2d2 100644 --- a/libs/ImguiExtension/panels/GPUProfilerPanel.h +++ b/libs/ImguiExtension/panels/GPUProfilerPanel.h @@ -1,8 +1,9 @@ #pragma once #include "Panel.h" +#include "PerformanceGraphPanel.h" -#include "lighting/IrradianceVolume.h" +#include namespace Atlas::ImguiExtension { @@ -13,6 +14,12 @@ namespace Atlas::ImguiExtension { void Render(); + void RenderTable(); + + bool showGraph = false; + + PerformanceGraphPanel perfGraphPanel; + }; } \ No newline at end of file diff --git a/libs/ImguiExtension/panels/MaterialPanel.cpp b/libs/ImguiExtension/panels/MaterialPanel.cpp index 8b6754f85..3ed4c7ab7 100644 --- a/libs/ImguiExtension/panels/MaterialPanel.cpp +++ b/libs/ImguiExtension/panels/MaterialPanel.cpp @@ -11,6 +11,7 @@ namespace Atlas::ImguiExtension { auto widthAfterImage = availableWidth - padding - ImGui::GetTextLineHeight(); auto renderWithImagePreview = [&](ResourceHandle& texture, std::function element) { + if (texture.IsLoaded()) { UIElements::TexturePreview(wrapper, &texture); ImGui::SameLine(); @@ -21,10 +22,11 @@ namespace Atlas::ImguiExtension { texture = textureSelector.value()(texture); ImGui::PopItemWidth(); } + ImGui::PushID(texture.GetID()); } else { if (textureSelector.has_value()) - textureSelector.value()(texture); + texture = textureSelector.value()(texture); } element(); if (texture.IsLoaded() && !textureSelector.has_value()) @@ -35,8 +37,11 @@ namespace Atlas::ImguiExtension { renderWithImagePreview(material->baseColorMap, [&]() { ImGui::ColorEdit3("Base color", glm::value_ptr(material->baseColor)); }); - ImGui::ColorEdit3("Emissive color", glm::value_ptr(material->emissiveColor)); - ImGui::DragFloat("Emissive intensity", &material->emissiveIntensity, 0.1f, 0.0f, 10000.0f, "%.2f"); + + renderWithImagePreview(material->emissiveMap, [&]() { + ImGui::ColorEdit3("Emissive color", glm::value_ptr(material->emissiveColor)); + ImGui::DragFloat("Emissive intensity", &material->emissiveIntensity, 0.1f, 0.0f, 10000.0f, "%.2f"); + }); ImGui::Separator(); ImGui::Text("Opacity"); @@ -73,8 +78,13 @@ namespace Atlas::ImguiExtension { ImGui::ColorEdit3("Transmissive color", glm::value_ptr(material->transmissiveColor)); + ImGui::DragFloat2("UV animation", glm::value_ptr(material->uvAnimation), 0.01f, -1.0f, 1.0f); + ImGui::DragFloat("UV tiling", &material->tiling, 0.01f, 0.01f, 100.0f); + ImGui::Checkbox("Two sided", &material->twoSided); + + ImGui::PopID(); } diff --git a/libs/ImguiExtension/panels/MaterialsPanel.h b/libs/ImguiExtension/panels/MaterialsPanel.h index 046f2baf5..f6c259175 100644 --- a/libs/ImguiExtension/panels/MaterialsPanel.h +++ b/libs/ImguiExtension/panels/MaterialsPanel.h @@ -11,7 +11,7 @@ namespace Atlas::ImguiExtension { class MaterialsPanel : public Panel { - using MaterialSelector = std::optional(ResourceHandle)>>; + using MaterialSelector = std::optional(ResourceHandle)>>; public: MaterialsPanel() : Panel("Materials properties") {} diff --git a/libs/ImguiExtension/panels/PerformanceGraphPanel.cpp b/libs/ImguiExtension/panels/PerformanceGraphPanel.cpp new file mode 100644 index 000000000..9b7f19e7f --- /dev/null +++ b/libs/ImguiExtension/panels/PerformanceGraphPanel.cpp @@ -0,0 +1,91 @@ +#include "PerformanceGraphPanel.h" + +#include "graphics/Profiler.h" +#include "Clock.h" + +namespace Atlas::ImguiExtension { + + void PerformanceGraphPanel::Render(ivec2 size, float alpha) { + + ImGui::PushID(GetNameID()); + + UpdateGraphData(); + + RenderGraph(size, alpha); + + ImGui::PopID(); + + } + + void PerformanceGraphPanel::RenderGraph(ivec2 size, float alpha) { + + auto dataSize = int32_t(frameTimes.size()); + + std::vector localFrameTimes(frameTimes.begin(), frameTimes.end()); + std::vector localGpuTimes(gpuTimes.begin(), gpuTimes.end()); + + auto availSize = ImGui::GetContentRegionAvail(); + if (size != ivec2(0)) + availSize = ImVec2(float(size.x), float(size.y)); + + auto frameColor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg); + if (alpha >= 0.0f) + frameColor.w = alpha; + + ImPlot::PushStyleColor(ImPlotCol_PlotBorder, frameColor); + ImPlot::PushStyleColor(ImPlotCol_FrameBg, frameColor); + ImPlot::PushStyleColor(ImPlotCol_PlotBg, frameColor); + ImPlot::PushStyleColor(ImPlotCol_Fill, frameColor); + ImPlot::PushStyleVar(ImPlotStyleVar_PlotBorderSize, 0.0f); + + if (ImPlot::BeginPlot("Performance graph", availSize)) { + ImPlot::SetupAxes("Frames", "Time (ms)"); + ImPlot::SetupAxesLimits(0, timeWindowSize, 0, std::min(200.0f, maxFrameTime), ImPlotCond_Always); + ImPlot::SetupLegend(ImPlotLocation_SouthWest); + + ImPlot::PlotLine("Frame time", localFrameTimes.data(), dataSize); + ImPlot::PlotLine("GPU time", localGpuTimes.data(), dataSize); + + ImPlot::EndPlot(); + } + + ImPlot::PopStyleVar(); + ImPlot::PopStyleColor(); + ImPlot::PopStyleColor(); + + } + + void PerformanceGraphPanel::UpdateGraphData() { + + auto gpuProfilerData = Graphics::Profiler::GetQueries(Graphics::Profiler::OrderBy::MAX_TIME); + + double slowestTime = 0.0; + for (int32_t i = 0; i < int32_t(gpuProfilerData.size()); i++) { + const auto& threadData = gpuProfilerData[i]; + double threadTime = 0.0; + for (const auto& query : threadData.queries) { + threadTime += query.timer.elapsedTime; + } + if (threadTime > slowestTime) { + slowestTime = threadTime; + } + } + + frameTimes.push_back(Clock::GetDelta() * 1000.0f); + gpuTimes.push_back(float(slowestTime / 1000000.0)); + + if (int32_t(frameTimes.size()) > timeWindowSize) { + frameTimes.pop_front(); + gpuTimes.pop_front(); + } + + maxFrameTime = 0.0f; + maxGpuTime = 0.0f; + for (int32_t i = 0; i < timeWindowSize && i < int32_t(frameTimes.size()); i++) { + maxFrameTime = std::max(frameTimes[i], maxFrameTime); + maxGpuTime = std::max(gpuTimes[i], maxGpuTime); + } + + } + +} \ No newline at end of file diff --git a/libs/ImguiExtension/panels/PerformanceGraphPanel.h b/libs/ImguiExtension/panels/PerformanceGraphPanel.h new file mode 100644 index 000000000..91d3e01ac --- /dev/null +++ b/libs/ImguiExtension/panels/PerformanceGraphPanel.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Panel.h" + +#include + +namespace Atlas::ImguiExtension { + + class PerformanceGraphPanel : public Panel { + + public: + PerformanceGraphPanel() : Panel("GPU profiler hierarchy") {} + + void Render(ivec2 size = ivec2(0), float alpha = -1.0f); + + void RenderGraph(ivec2 size, float alpha); + + void UpdateGraphData(); + + bool showGraph = false; + + int32_t timeWindowSize = 1024; + + float maxFrameTime = 0.0f; + float maxGpuTime = 0.0f; + + std::deque frameTimes; + std::deque gpuTimes; + + }; + +} \ No newline at end of file diff --git a/libs/ImguiExtension/panels/PostProcessingPanel.cpp b/libs/ImguiExtension/panels/PostProcessingPanel.cpp index 04b4aea86..a1b0dedf1 100644 --- a/libs/ImguiExtension/panels/PostProcessingPanel.cpp +++ b/libs/ImguiExtension/panels/PostProcessingPanel.cpp @@ -2,7 +2,7 @@ namespace Atlas::ImguiExtension { - void PostProcessingPanel::Render(PostProcessing::PostProcessing &postProcessing) { + void PostProcessingPanel::Render(PostProcessing::PostProcessing &postProcessing, TextureSelector textureSelector) { ImGui::PushID(GetNameID()); @@ -27,10 +27,30 @@ namespace Atlas::ImguiExtension { ImGui::Text("Chromatic aberration"); ImGui::Checkbox("Enable##Chromatic aberration", &postProcessing.chromaticAberration.enable); ImGui::Checkbox("Colors reversed", &postProcessing.chromaticAberration.colorsReversed); - ImGui::SliderFloat("Strength##v", &postProcessing.chromaticAberration.strength, 0.0f, 4.0f); + ImGui::SliderFloat("Strength##Chromatic abberation", &postProcessing.chromaticAberration.strength, 0.0f, 4.0f); + ImGui::Separator(); ImGui::Text("Film grain"); ImGui::Checkbox("Enable##Film grain", &postProcessing.filmGrain.enable); ImGui::SliderFloat("Strength##Film grain", &postProcessing.filmGrain.strength, 0.0f, 1.0f); + ImGui::Separator(); + ImGui::Text("Vignette"); + ImGui::Checkbox("Enable##Vignette", &postProcessing.vignette.enable); + ImGui::ColorEdit3("Color##Vignette", glm::value_ptr(postProcessing.vignette.color)); + ImGui::SliderFloat("Strength##Vignette", &postProcessing.vignette.strength, 0.0f, 1.0f); + ImGui::SliderFloat("Power##Vignette", &postProcessing.vignette.power, 0.0f, 10.0f); + ImGui::SliderFloat("Offset##Vignette", &postProcessing.vignette.offset, -1.0f, 1.0f); + ImGui::Separator(); + ImGui::Text("Bloom"); + ImGui::Checkbox("Enable##Bloom", &postProcessing.bloom.enable); + if (textureSelector.has_value()) + postProcessing.bloom.dirtMap = textureSelector.value()(postProcessing.bloom.dirtMap); + ImGui::DragFloat("Strength##Bloom", &postProcessing.bloom.strength, 0.001f, 0.0f, 1.0f); + ImGui::DragFloat("Dirt strength##Bloom", &postProcessing.bloom.dirtStrength, 0.01f, 0.0f, 10.0f); + ImGui::DragFloat("Threshold##Bloom", &postProcessing.bloom.threshold, 0.01f, 0.0f, 10.0f); + ImGui::DragFloat("Filter size##Bloom", &postProcessing.bloom.filterSize, 0.001f, 0.0f, 1.0f); + auto mipLevels = int32_t(postProcessing.bloom.mipLevels); + ImGui::DragInt("Mip levels##Bloom", &mipLevels, 1, 2, 12); + postProcessing.bloom.mipLevels = mipLevels; ImGui::PopID(); diff --git a/libs/ImguiExtension/panels/PostProcessingPanel.h b/libs/ImguiExtension/panels/PostProcessingPanel.h index 72742a8be..1da21855a 100644 --- a/libs/ImguiExtension/panels/PostProcessingPanel.h +++ b/libs/ImguiExtension/panels/PostProcessingPanel.h @@ -8,10 +8,12 @@ namespace Atlas::ImguiExtension { class PostProcessingPanel : public Panel { + using TextureSelector = std::optional(ResourceHandle)>>; + public: PostProcessingPanel() : Panel("Post process properties") {} - void Render(PostProcessing::PostProcessing& postProcessing); + void Render(PostProcessing::PostProcessing& postProcessing, TextureSelector textureSelector = {}); }; diff --git a/libs/ImguiExtension/panels/RTGIPanel.cpp b/libs/ImguiExtension/panels/RTGIPanel.cpp index 647457c22..2b7b657bc 100644 --- a/libs/ImguiExtension/panels/RTGIPanel.cpp +++ b/libs/ImguiExtension/panels/RTGIPanel.cpp @@ -26,7 +26,9 @@ namespace Atlas::ImguiExtension { ImGui::SetTooltip("Limits the amount of incoming radiance. Is internally scale by the camera exposure."); } ImGui::SliderFloat("Bias", &rtgi->bias, 0.0f, 1.0f); - ImGui::SliderInt("Texture level##Reflection", &rtgi->textureLevel, 0, 10); + ImGui::SliderInt("Texture level##RTGI", &rtgi->textureLevel, 0, 10); + ImGui::SliderInt("Sample count##RTGI", &rtgi->sampleCount, 1, 10); + ImGui::SliderInt("Light sample count##RTGI", &rtgi->lightSampleCount, 1, 10); ImGui::Text("Denoiser"); ImGui::SliderFloat("Spatial filter strength", &rtgi->spatialFilterStrength, 0.0f, 10.0f); ImGui::SliderFloat("Temporal weight", &rtgi->temporalWeight, 0.0f, 1.0f); diff --git a/libs/ImguiExtension/panels/ReflectionPanel.cpp b/libs/ImguiExtension/panels/ReflectionPanel.cpp index b103876c3..0b8d626c6 100644 --- a/libs/ImguiExtension/panels/ReflectionPanel.cpp +++ b/libs/ImguiExtension/panels/ReflectionPanel.cpp @@ -7,6 +7,8 @@ namespace Atlas::ImguiExtension { ImGui::PushID(GetNameID()); ImGui::Checkbox("Enable", &reflection->enable); + ImGui::Checkbox("Enable RT", &reflection->rt); + ImGui::Checkbox("Enable SSR", &reflection->ssr); ImGui::Checkbox("Half resolution", &reflection->halfResolution); ImGui::Checkbox("Upsample before filtering", &reflection->upsampleBeforeFiltering); @@ -27,6 +29,8 @@ namespace Atlas::ImguiExtension { ImGui::SliderFloat("Bias", &reflection->bias, 0.0f, 1.0f); ImGui::SliderFloat("Roughness cuttoff", &reflection->roughnessCutoff, 0.0f, 1.0f); ImGui::SliderInt("Texture level##Reflection", &reflection->textureLevel, 0, 10); + ImGui::SliderInt("Sample count##Reflection", &reflection->sampleCount, 1, 10); + ImGui::SliderInt("Light sample count##Reflection", &reflection->lightSampleCount, 1, 10); ImGui::Text("Denoiser"); ImGui::SliderFloat("Spatial filter strength", &reflection->spatialFilterStrength, 0.0f, 10.0f); ImGui::SliderFloat("Temporal weight", &reflection->temporalWeight, 0.0f, 1.0f); diff --git a/libs/ImguiExtension/panels/SSSPanel.cpp b/libs/ImguiExtension/panels/SSSPanel.cpp index fe0a1b8b8..0798bd61f 100644 --- a/libs/ImguiExtension/panels/SSSPanel.cpp +++ b/libs/ImguiExtension/panels/SSSPanel.cpp @@ -7,8 +7,10 @@ namespace Atlas::ImguiExtension { ImGui::PushID(GetNameID()); ImGui::Checkbox("Enable", &sss->enable); - ImGui::SliderInt("Sample count", &sss->sampleCount, 2.0, 16.0); + ImGui::Checkbox("Trace in world space", &sss->traceWorldSpace); + ImGui::DragInt("Sample count", &sss->sampleCount, 2, 64); ImGui::SliderFloat("Max length", &sss->maxLength, 0.01f, 1.0f); + ImGui::SliderFloat("Min length world space", &sss->minLengthWorldSpace, 0.01f, 1.0f); ImGui::SliderFloat("Thickness", &sss->thickness, 0.001f, 1.0f, "%.3f", ImGuiSliderFlags_Logarithmic); ImGui::PopID(); diff --git a/libs/fsr2/CMakeLists.txt b/libs/fsr2/CMakeLists.txt index 2161467be..f44844e54 100644 --- a/libs/fsr2/CMakeLists.txt +++ b/libs/fsr2/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.7) +cmake_minimum_required(VERSION 3.25) project(fsr2) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ffx-fsr2-api) diff --git a/libs/fsr2/ffx-fsr2-api/ffx_fsr2.cpp b/libs/fsr2/ffx-fsr2-api/ffx_fsr2.cpp index 220900983..b1705035f 100644 --- a/libs/fsr2/ffx-fsr2-api/ffx_fsr2.cpp +++ b/libs/fsr2/ffx-fsr2-api/ffx_fsr2.cpp @@ -338,12 +338,12 @@ static FfxErrorCode patchResourceBindings(FfxPipelineState* inoutPipeline) for (uint32_t srvIndex = 0; srvIndex < inoutPipeline->srvCount; ++srvIndex) { int32_t mapIndex = 0; - for (mapIndex = 0; mapIndex < std::size(srvResourceBindingTable); ++mapIndex) + for (mapIndex = 0; mapIndex < std::ranges::size(srvResourceBindingTable); ++mapIndex) { if (0 == wcscmp(srvResourceBindingTable[mapIndex].name, inoutPipeline->srvResourceBindings[srvIndex].name)) break; } - if (mapIndex == std::size(srvResourceBindingTable)) + if (mapIndex == std::ranges::size(srvResourceBindingTable)) return FFX_ERROR_INVALID_ARGUMENT; inoutPipeline->srvResourceBindings[srvIndex].resourceIdentifier = srvResourceBindingTable[mapIndex].index; @@ -352,12 +352,12 @@ static FfxErrorCode patchResourceBindings(FfxPipelineState* inoutPipeline) for (uint32_t uavIndex = 0; uavIndex < inoutPipeline->uavCount; ++uavIndex) { int32_t mapIndex = 0; - for (mapIndex = 0; mapIndex < std::size(uavResourceBindingTable); ++mapIndex) + for (mapIndex = 0; mapIndex < std::ranges::size(uavResourceBindingTable); ++mapIndex) { if (0 == wcscmp(uavResourceBindingTable[mapIndex].name, inoutPipeline->uavResourceBindings[uavIndex].name)) break; } - if (mapIndex == std::size(uavResourceBindingTable)) + if (mapIndex == std::ranges::size(uavResourceBindingTable)) return FFX_ERROR_INVALID_ARGUMENT; inoutPipeline->uavResourceBindings[uavIndex].resourceIdentifier = uavResourceBindingTable[mapIndex].index; @@ -366,12 +366,12 @@ static FfxErrorCode patchResourceBindings(FfxPipelineState* inoutPipeline) for (uint32_t cbIndex = 0; cbIndex < inoutPipeline->constCount; ++cbIndex) { int32_t mapIndex = 0; - for (mapIndex = 0; mapIndex < std::size(cbResourceBindingTable); ++mapIndex) + for (mapIndex = 0; mapIndex < std::ranges::size(cbResourceBindingTable); ++mapIndex) { if (0 == wcscmp(cbResourceBindingTable[mapIndex].name, inoutPipeline->cbResourceBindings[cbIndex].name)) break; } - if (mapIndex == std::size(cbResourceBindingTable)) + if (mapIndex == std::ranges::size(cbResourceBindingTable)) return FFX_ERROR_INVALID_ARGUMENT; inoutPipeline->cbResourceBindings[cbIndex].resourceIdentifier = cbResourceBindingTable[mapIndex].index; diff --git a/src/demo/App.cpp b/src/demo/App.cpp index c0bff4dbb..e37376ec1 100644 --- a/src/demo/App.cpp +++ b/src/demo/App.cpp @@ -21,7 +21,6 @@ using namespace Atlas::ImguiExtension; void App::LoadContent() { renderTarget = Atlas::CreateRef(1920, 1080); - pathTraceTarget = Atlas::CreateRef(1920, 1080); viewport = Atlas::CreateRef(0, 0, renderTarget->GetWidth(), renderTarget->GetHeight()); @@ -123,10 +122,13 @@ void App::UnloadContent() { } void App::Update(float deltaTime) { + + scene->WaitForAsyncWorkCompletion(); if (sceneReload) { UnloadScene(); LoadScene(); + scene->WaitForAsyncWorkCompletion(); sceneReload = false; } @@ -216,6 +218,18 @@ void App::Update(float deltaTime) { auto& rigidBodyComponent = entity.AddComponent(bodySettings); rigidBodyComponent.SetRestitution(sphereRestitution); + if (attachLightToSphers) { + auto& lightComponent = entity.AddComponent(Atlas::LightType::PointLight); + + lightComponent.color.r = Atlas::Common::Random::SampleUniformFloat(); + lightComponent.color.g = Atlas::Common::Random::SampleUniformFloat(); + lightComponent.color.b = Atlas::Common::Random::SampleUniformFloat(); + + lightComponent.intensity = 30.0f; + + lightComponent.properties.point.radius = 5.0f; + } + entities.push_back(entity); lastSpawn = Atlas::Clock::Get(); } @@ -248,6 +262,18 @@ void App::Update(float deltaTime) { }; entity.AddComponent(bodySettings); + if (attachLightToSphers) { + auto& lightComponent = entity.AddComponent(Atlas::LightType::PointLight); + + lightComponent.color.r = Atlas::Common::Random::SampleUniformFloat(); + lightComponent.color.g = Atlas::Common::Random::SampleUniformFloat(); + lightComponent.color.b = Atlas::Common::Random::SampleUniformFloat(); + + lightComponent.intensity = 30.0f; + + lightComponent.properties.point.radius = 5.0f; + } + entities.push_back(entity); lastSpawn = Atlas::Clock::Get(); } @@ -299,9 +325,10 @@ void App::Render(float deltaTime) { if (animateLight) directionalLight.properties.directional.direction = glm::vec3(0.0f, -1.0f, sin(Atlas::Clock::Get() / 10.0f)); + viewport->Set(0, 0, renderTarget->GetWidth(), renderTarget->GetHeight()); + if (pathTrace) { - viewport->Set(0, 0, pathTraceTarget->GetWidth(), pathTraceTarget->GetHeight()); - mainRenderer->PathTraceScene(viewport, pathTraceTarget, scene); + mainRenderer->PathTraceScene(viewport, renderTarget, scene); } else { mainRenderer->RenderScene(viewport, renderTarget, scene); @@ -585,6 +612,7 @@ void App::Render(float deltaTime) { ImGui::SliderFloat("Sphere scale##PhysicsBody", &sphereScale, 1.0f, 10.0f); ImGui::SliderFloat("Sphere density##PhysicsBody", &sphereDensity, 1.0f, 100.0f); ImGui::SliderFloat("Sphere restitution##PhysicsBody", &sphereRestitution, 0.0f, 1.0f); + ImGui::Checkbox("Attach lights ##PhysicsBody", &attachLightToSphers); ImGui::Text("Sphere emitter"); ImGui::Checkbox("Enable##PhysicsEmitter", &emitSpheresEnabled); ImGui::SliderFloat("Spawn rate##PhysicsEmitter", &emitSpawnRate, 0.001f, 1.0f); @@ -640,6 +668,7 @@ void App::Render(float deltaTime) { } recreateSwapchain = false; + } if (slowMode) { using namespace std::chrono_literals; std::this_thread::sleep_for(60ms); } @@ -650,6 +679,8 @@ void App::Render(float deltaTime) { Atlas::Clock::ResetAverage(); firstFrame = false; } + + scene->WaitForAsyncWorkCompletion(); } @@ -900,8 +931,9 @@ bool App::LoadScene() { scene->fog->volumetricIntensity = 0.0f; } else if (sceneSelection == FOREST) { - auto otherScene = Atlas::Loader::ModelImporter::ImportScene("forest/forest.gltf", -glm::vec3(2048.0f), glm::vec3(2048.0f), 5); + auto otherScene = Atlas::Loader::ModelImporter::ImportScene("forest/forest.gltf", -glm::vec3(2048.0f), glm::vec3(2048.0f), 5, false, false, false, 2048); otherScene->Timestep(1.0f); + otherScene->Update(); CopyActors(otherScene); @@ -917,8 +949,9 @@ bool App::LoadScene() { scene->fog->volumetricIntensity = 0.08f; } else if (sceneSelection == EMERALDSQUARE) { - auto otherScene = Atlas::Loader::ModelImporter::ImportScene("emeraldsquare/square.gltf", -glm::vec3(2048.0f), glm::vec3(2048.0f), 5); + auto otherScene = Atlas::Loader::ModelImporter::ImportScene("emeraldsquare/square.gltf", -glm::vec3(2048.0f), glm::vec3(2048.0f), 5, false, false, false, 2048); otherScene->Timestep(1.0f); + otherScene->Update(); CopyActors(otherScene); @@ -1080,22 +1113,6 @@ void App::CheckLoadScene() { graphicsDevice->WaitForPreviousFrameSubmission(); - static Atlas::JobGroup buildBvhGroup; - - auto buildRTStructure = [&](Atlas::JobData) { - auto sceneMeshes = scene->GetMeshes(); - - for (const auto& mesh : sceneMeshes) { - - if (mesh->IsBVHBuilt()) continue; - - Atlas::JobSystem::Execute(buildBvhGroup, [mesh](Atlas::JobData&) { mesh->BuildBVH(); }); - - } - }; - - Atlas::JobSystem::Execute(buildBvhGroup, buildRTStructure); - auto sceneAABB = Atlas::Volume::AABB(glm::vec3(std::numeric_limits::max()), glm::vec3(-std::numeric_limits::max())); @@ -1226,7 +1243,6 @@ void App::CheckLoadScene() { void App::SetResolution(int32_t width, int32_t height) { renderTarget->Resize(width, height); - pathTraceTarget->Resize(width, height); } @@ -1249,4 +1265,4 @@ Atlas::EngineInstance* GetEngineInstance() { return new App(); -} \ No newline at end of file +} diff --git a/src/demo/App.h b/src/demo/App.h index 7478acea7..6e90c012d 100644 --- a/src/demo/App.h +++ b/src/demo/App.h @@ -59,7 +59,6 @@ class App : public Atlas::EngineInstance { SceneSelection sceneSelection = SPONZA; - Ref pathTraceTarget; Ref renderTarget; Ref viewport; @@ -115,7 +114,9 @@ class App : public Atlas::EngineInstance { float sphereRestitution = 0.2f; bool emitSpheresEnabled = false; - float emitSpawnRate = 0.1f; + float emitSpawnRate = 0.01f; + + bool attachLightToSphers = false; bool shootSpheresEnabled = false; bool shootSphere = false; diff --git a/src/demo/CMakeLists.txt b/src/demo/CMakeLists.txt index 31a4b619f..9377dd597 100644 --- a/src/demo/CMakeLists.txt +++ b/src/demo/CMakeLists.txt @@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 3.25) -project(AtlasEngineDemo VERSION 0.2.0) +project(AtlasEngineDemo VERSION 0.2.1) # Note: For this project, the root CMakeLists.txt turns # the ATLAS_IMGUI and ATLAS_EXPORT_MAIN options on. @@ -34,6 +34,10 @@ endforeach() if (UNIX AND NOT APPLE AND NOT ANDROID) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath='$ORIGIN'") endif() +# Need to reference the vulkan SDK for MacOS to search at runtime +if (APPLE) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,'/usr/local/lib'") +endif() # We use the exported main file from the AtlasEngine library to able to use # the app class. Alternatively, you can write a main function yourself. diff --git a/src/editor/App.cpp b/src/editor/App.cpp index 58994e34c..5518beff0 100644 --- a/src/editor/App.cpp +++ b/src/editor/App.cpp @@ -5,7 +5,9 @@ #include "Notifications.h" #include "ContentDiscovery.h" #include "ui/panels/PopupPanels.h" +#include "tools/FileSystemHelper.h" #include +#include #include #include @@ -25,14 +27,17 @@ namespace Atlas::Editor { ContentDiscovery::Update(); - auto icon = Atlas::Texture::Texture2D("icon.png"); - window.SetIcon(&icon); - ImGui::CreateContext(); - ImGuiIO &io = ImGui::GetIO(); - (void) io; + ImPlot::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + SetDefaultWindowResolution(); + + auto icon = Atlas::Texture::Texture2D("icon.png"); + window.SetIcon(&icon); + // Add font with enlarged size and scale it down again // This means we can use scaled text up to 2x the size io.Fonts->AddFontFromFileTTF( @@ -52,16 +57,19 @@ namespace Atlas::Editor { Singletons::icons = CreateRef(); Singletons::blockingOperation = CreateRef(); Singletons::renderTarget = CreateRef(1280, 720); - Singletons::pathTraceRenderTarget = CreateRef(1280, 720); Singletons::mainRenderer = mainRenderer; mouseHandler = Input::MouseHandler(1.5f, 8.0f); keyboardHandler = Input::KeyboardHandler(7.0f, 5.0f); - if (Singletons::config->darkMode) + if (Singletons::config->darkMode) { ImGui::StyleColorsDark(); - else + ImPlot::StyleColorsDark(); + } + else { ImGui::StyleColorsLight(); + ImPlot::StyleColorsLight(); + } } @@ -69,6 +77,8 @@ namespace Atlas::Editor { Singletons::imguiWrapper->Unload(); + CopyPasteHelper::Clear(); + for (const auto& sceneWindow : sceneWindows) { if (sceneWindow->isPlaying) sceneWindow->StopPlaying(); @@ -80,11 +90,13 @@ namespace Atlas::Editor { Singletons::Destruct(); + ImPlot::DestroyContext(); + } void App::Update(float deltaTime) { - const ImGuiIO &io = ImGui::GetIO(); + const ImGuiIO& io = ImGui::GetIO(); ContentDiscovery::Update(); Singletons::imguiWrapper->Update(&window, deltaTime); @@ -194,7 +206,7 @@ namespace Atlas::Editor { sceneWindow->isActiveWindow = true; else sceneWindow->isActiveWindow = false; - + // Need to reset this each frame in order to reenable selection sceneWindow->lockSelection = false; sceneWindow->Update(deltaTime); @@ -202,32 +214,6 @@ namespace Atlas::Editor { ImGuizmo::Enable(activeSceneWindow->needGuizmoEnabled); - graphicsDevice->WaitForPreviousFrameSubmission(); - - -#ifdef AE_BINDLESS - // This crashes when we start with path tracing and do the bvh build async - // Launch BVH builds asynchronously - auto buildRTStructure = [&](JobData) { - auto sceneMeshes = ResourceManager::GetResources(); - - for (const auto& mesh : sceneMeshes) { - if (!mesh.IsLoaded()) - continue; - if (mesh->IsBVHBuilt()) - continue; - JobSystem::Execute(bvhBuilderGroup, [mesh](JobData&) { - mesh->BuildBVH(false); - }); - } - }; - - if (bvhBuilderGroup.HasFinished()) { - JobSystem::Execute(bvhBuilderGroup, buildRTStructure); - return; - } -#endif - } void App::Render(float deltaTime) { @@ -251,106 +237,181 @@ namespace Atlas::Editor { ImGuizmo::BeginFrame(); // ImGui::ShowDemoWindow(); + // ImPlot::ShowDemoWindow(); - ImGuiViewport *viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->Pos); - ImGui::SetNextWindowSize(viewport->Size); - ImGui::SetNextWindowViewport(viewport->ID); - ImGui::SetNextWindowBgAlpha(0.0f); + ImGuiViewport* viewport = ImGui::GetMainViewport(); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + bool playingMaximized = false; + auto activeSceneWindow = sceneWindows.empty() ? nullptr : sceneWindows[activeSceneIdx]; + if (activeSceneWindow) { + playingMaximized = activeSceneWindow->isPlaying && activeSceneWindow->playMaximized; + } + if (!playingMaximized) { + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::SetNextWindowBgAlpha(0.0f); + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking | + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin("MainDockspace Window", nullptr, window_flags); + ImGui::PopStyleVar(3); + + ImGuiID mainDsId = ImGui::GetID("MainDS"); + if (!ImGui::DockBuilderGetNode(mainDsId) || resetDockspaceLayout) { + SetupMainDockspace(mainDsId); + } - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::Begin("MainDockspace Window", nullptr, window_flags); - ImGui::PopStyleVar(3); + ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode; + ImGui::DockSpace(mainDsId, ImVec2(0.0f, 0.0f), dockspace_flags); + + if (ImGui::BeginMainMenuBar()) { + static bool openProject = false, saveProject = false, newScene = false, importFiles = false; + bool saveScene = false, exitEditor = false; + if (ImGui::BeginMenu("File")) { + /* + ImGui::MenuItem("Open project", nullptr, &openProject); + ImGui::MenuItem("Save project", nullptr, &saveProject); + ImGui::Separator(); + */ + ImGui::MenuItem("New scene", nullptr, &newScene); + ImGui::MenuItem("Save scene", nullptr, &saveScene); + ImGui::MenuItem("Exit", nullptr, &exitEditor); + /* + ImGui::Separator(); + ImGui::MenuItem("Import files", nullptr, &importFiles); + */ + ImGui::EndMenu(); + } - ImGuiID mainDsId = ImGui::GetID("MainDS"); - if (!ImGui::DockBuilderGetNode(mainDsId) || resetDockspaceLayout) { - SetupMainDockspace(mainDsId); - } + if (ImGui::BeginMenu("View")) { + if (ImGui::MenuItem("Dark mode", nullptr, &config->darkMode)) { + if (config->darkMode) { + ImGui::StyleColorsDark(); + ImPlot::StyleColorsDark(); + } + else { + ImGui::StyleColorsLight(); + ImPlot::StyleColorsLight(); + } + } - ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode; - ImGui::DockSpace(mainDsId, ImVec2(0.0f, 0.0f), dockspace_flags); - - if (ImGui::BeginMainMenuBar()) { - static bool openProject = false, saveProject = false, newScene = false, importFiles = false; - bool saveScene = false; - if (ImGui::BeginMenu("File")) { - /* - ImGui::MenuItem("Open project", nullptr, &openProject); - ImGui::MenuItem("Save project", nullptr, &saveProject); - ImGui::Separator(); - */ - ImGui::MenuItem("New scene", nullptr, &newScene); - ImGui::MenuItem("Save scene", nullptr, &saveScene); - /* - ImGui::Separator(); - ImGui::MenuItem("Import files", nullptr, &importFiles); - */ - ImGui::EndMenu(); - } + ImGui::MenuItem("Reset layout", nullptr, &resetDockspaceLayout); + ImGui::MenuItem("Show logs", nullptr, &logWindow.show); + ImGui::MenuItem("Show content browser", nullptr, &contentBrowserWindow.show); + ImGui::MenuItem("Show profiler", nullptr, &profilerWindow.show); + ImGui::MenuItem("Show geometry brush", nullptr, &geometryBrushWindow.show); + ImGui::EndMenu(); + } - if (ImGui::BeginMenu("View")) { - if (ImGui::MenuItem("Dark mode", nullptr, &config->darkMode)) { - if (config->darkMode) - ImGui::StyleColorsDark(); - else - ImGui::StyleColorsLight(); + if (ImGui::BeginMenu("Renderer")) { + ImGui::MenuItem("VSync", nullptr, &config->vsync); + ImGui::MenuItem("Pathtracer", nullptr, &config->pathTrace); + ImGui::EndMenu(); } - ImGui::MenuItem("Reset layout", nullptr, &resetDockspaceLayout); - ImGui::MenuItem("Show logs", nullptr, &logWindow.show); - ImGui::MenuItem("Show content browser", nullptr, &contentBrowserWindow.show); - ImGui::MenuItem("Show profiler", nullptr, &profilerWindow.show); - ImGui::MenuItem("Show geometry brush", nullptr, &geometryBrushWindow.show); - ImGui::EndMenu(); - } + if (newScene) { + UI::PopupPanels::isNewScenePopupVisible = true; + newScene = false; + } - if (ImGui::BeginMenu("Renderer")) { - ImGui::MenuItem("VSync", nullptr, &config->vsync); - ImGui::MenuItem("Pathtracer", nullptr, &config->pathTrace); - ImGui::EndMenu(); - } + if (saveScene && activeSceneWindow != nullptr) { + activeSceneWindow->SaveScene(); + } - if (newScene) { - UI::PopupPanels::isNewScenePopupVisible = true; - newScene = false; - } + if (exitEditor) + Exit(); - if (saveScene) { - auto activeSceneWindow = sceneWindows.empty() ? nullptr : sceneWindows[activeSceneIdx]; - if (activeSceneWindow != nullptr) - activeSceneWindow->SaveScene(); + ImGui::EndMainMenuBar(); } - ImGui::EndMainMenuBar(); - } + UI::PopupPanels::Render(); - UI::PopupPanels::Render(); + geometryBrushWindow.Render(activeSceneWindow); - geometryBrushWindow.Render(sceneWindows.empty() ? nullptr : sceneWindows[activeSceneIdx]); + for (auto& sceneWindow : sceneWindows) { + sceneWindow->Render(); + } - for (auto& sceneWindow : sceneWindows) { - sceneWindow->Render(); - } + contentBrowserWindow.Render(); + logWindow.Render(); + profilerWindow.Render(); - contentBrowserWindow.Render(); - logWindow.Render(); - profilerWindow.Render(); + ImGui::End(); + } + else { + RenderSceneMaximized(); + } Notifications::Display(); - ImGui::End(); - ImGui::Render(); Singletons::imguiWrapper->Render(true); } + void App::RenderSceneMaximized() { + + auto activeSceneWindow = sceneWindows.empty() ? nullptr : sceneWindows[activeSceneIdx]; + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); + ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize); + ImGui::Begin("RenderWindow", nullptr, window_flags); + + auto renderArea = ImGui::GetContentRegionAvail(); + activeSceneWindow->viewportPanel.RenderScene(activeSceneWindow->scene.Get(), + ivec2(0), ivec2(int32_t(renderArea.x), int32_t(renderArea.y)), true); + auto set = Singletons::imguiWrapper->GetTextureDescriptorSet(&activeSceneWindow->viewportPanel.viewportTexture); + ImGui::Image(set, renderArea); + + if (activeSceneWindow->perfOverlayMaximized) { + ImGui::SetCursorPos(ImVec2(0.0f, 0.0f)); + auto gpuProfilerData = Graphics::Profiler::GetQueriesAverage(32, Graphics::Profiler::OrderBy::MAX_TIME); + + std::string perfString; + double slowestTime = 0.0; + for (int32_t i = 0; i < int32_t(gpuProfilerData.size()); i++) { + const auto& threadData = gpuProfilerData[i]; + double threadTime = 0.0; + for (const auto& query : threadData.queries) { + threadTime += query.timer.elapsedTime; + } + if (threadTime > slowestTime) { + slowestTime = threadTime; + } + } + + auto target = Singletons::renderTarget; + + ImGui::SetWindowFontScale(1.5f); + ImGui::Text("Frame time: %.3fms, GPU time: %.3fms", Clock::GetAverage() * 1000.0f, float(slowestTime / 1000000.0)); + ImGui::Text("Resolution %dx%d upscaled from %dx%d", target->GetWidth(), target->GetHeight(), target->GetScaledWidth(), target->GetScaledHeight()); + ImGui::SetWindowFontScale(1.0f); + + performanceGraphPanel.Render(ivec2(400, 300), 0.05f); + } + + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + + ImGui::End(); + + } + void App::SetupMainDockspace(ImGuiID dsID) { ImGui::SetWindowSize(ImVec2(370.0f, 680.0f)); @@ -397,31 +458,27 @@ namespace Atlas::Editor { ResourceManager::Subscribe(ResourceTopic::ResourceCreate, [&](Ref>& scene) { - waitToLoadScenes.push_back(ResourceHandle(scene)); - Singletons::config->openedScenes.push_back(ResourceHandle(scene)); - }); + waitToLoadScenes.push_back(ResourceHandle(scene)); + Singletons::config->openedScenes.push_back(ResourceHandle(scene)); + }); // Also kind of a resource event Events::EventManager::DropEventDelegate.Subscribe([&](Events::DropEvent event) { if (!event.file.empty() && contentBrowserWindow.show) { - // Need to create a copy here auto destinationDirectory = Common::Path::GetAbsolute(contentBrowserWindow.currentDirectory); - JobSystem::Execute(fileImportGroup, [&, event, destinationDirectory](JobData&) { - try { - std::filesystem::copy(event.file, destinationDirectory, - std::filesystem::copy_options::overwrite_existing | - std::filesystem::copy_options::recursive); - } - catch (std::exception& e) { - auto message = "Error copying " + event.file + " to asset directory: " + e.what(); - Notifications::Push({ message, vec3(1.0f, 0.0f, 0.0f) }); - Log::Error(message); - } - }); - - Notifications::Push({"Copying " + event.file + " to asset directory"}); + + FileSystemHelper::Copy(event.file, destinationDirectory); } - }); + }); + + } + + void App::SetDefaultWindowResolution() { + + auto resolution = GetScreenSize(); + + window.SetSize(resolution.x - 200, resolution.y - 200); + window.SetPosition(100, 100); } diff --git a/src/editor/App.h b/src/editor/App.h index 28ff83a99..26915705c 100644 --- a/src/editor/App.h +++ b/src/editor/App.h @@ -34,10 +34,14 @@ namespace Atlas::Editor { virtual void Render(float deltaTime) final; private: + void RenderSceneMaximized(); + void SetupMainDockspace(ImGuiID dsID); void SubscribeToResourceEvents(); + void SetDefaultWindowResolution(); + Input::MouseHandler mouseHandler; Input::KeyboardHandler keyboardHandler; @@ -46,13 +50,14 @@ namespace Atlas::Editor { UI::ProfilerWindow profilerWindow = UI::ProfilerWindow(false); UI::GeometryBrushWindow geometryBrushWindow = UI::GeometryBrushWindow(false); + ImguiExtension::PerformanceGraphPanel performanceGraphPanel; + std::vector> waitToLoadScenes; std::vector> sceneWindows; size_t activeSceneIdx = 0; JobGroup bvhBuilderGroup; - JobGroup fileImportGroup; ImGuiID upperDockNodeID; diff --git a/src/editor/CMakeLists.txt b/src/editor/CMakeLists.txt index 55dd58a44..340f53453 100644 --- a/src/editor/CMakeLists.txt +++ b/src/editor/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 3.7) -project(AtlasEngineEditor VERSION 0.2.0) +cmake_minimum_required(VERSION 3.25) +project(AtlasEngineEditor VERSION 0.2.1) # Note: For this project, the root CMakeLists.txt turns # the ATLAS_IMGUI and ATLAS_EXPORT_MAIN options on. @@ -47,7 +47,8 @@ endif() # We want to use both ImGui and the AtlasEngine. For ImGui, the ATLAS_IMGUI option # needs to be turned on. -target_link_libraries (${PROJECT_NAME} AtlasEngine ImguiExtension unofficial::imguizmo::imguizmo) +target_link_libraries (${PROJECT_NAME} AtlasEngine ImguiExtension + unofficial::imguizmo::imguizmo Jolt::Jolt) if (APPLE AND ATLAS_BUNDLE) set(path ${Vulkan_LIBRARY}) @@ -79,4 +80,4 @@ if (APPLE AND ATLAS_BUNDLE) install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION ${CMAKE_BINARY_DIR} ) -endif() \ No newline at end of file +endif() diff --git a/src/editor/ContentDiscovery.cpp b/src/editor/ContentDiscovery.cpp index e31528388..1ec93aa20 100644 --- a/src/editor/ContentDiscovery.cpp +++ b/src/editor/ContentDiscovery.cpp @@ -5,6 +5,7 @@ #include "Log.h" #include +#include namespace Atlas::Editor { @@ -12,9 +13,16 @@ namespace Atlas::Editor { Ref ContentDiscovery::nextContent = CreateRef(); JobGroup ContentDiscovery::contentDiscoveryJob; + std::atomic_bool ContentDiscovery::execute = false; const float ContentDiscovery::discoverFrequency = 3.0f; float ContentDiscovery::lastDiscoveryTime = -ContentDiscovery::discoverFrequency; + void ContentDiscovery::Execute() { + + execute = true; + + } + const Ref ContentDiscovery::GetContent() { return content->rootDirectory; @@ -52,23 +60,24 @@ namespace Atlas::Editor { void ContentDiscovery::Update() { - bool canRediscover = (Clock::Get() - lastDiscoveryTime) >= discoverFrequency; + bool canRediscover = (Clock::Get() - lastDiscoveryTime) >= discoverFrequency + || content->contentDirectories.empty() || execute; if (contentDiscoveryJob.HasFinished() && canRediscover) { - // Might be that it took longer than the timeout time - content = nextContent; + // Swap here to not immediately release the memory of content (causes stutter due to freeing memory) + std::swap(content, nextContent); + lastDiscoveryTime = Clock::Get(); JobSystem::Execute(contentDiscoveryJob, [&](JobData&) { + // Now release the swapped memory here in the job async + nextContent.reset(); + nextContent = PerformContentDiscovery(); }); - return; - } - - if (!contentDiscoveryJob.HasFinished()) - return; - content = nextContent; + return; + } } Ref ContentDiscovery::PerformContentDiscovery() { @@ -100,7 +109,11 @@ namespace Atlas::Editor { assetPath.erase(assetPath.begin()); if (dirEntry.is_directory()) { + auto dirname = Common::Path::GetFileName(dirEntry.path().string()); + std::transform(dirname.begin(), dirname.end(), dirname.begin(), ::tolower); + auto childDirectory = CreateRef({ + .name = dirname, .path = dirEntry.path(), .assetPath = assetPath }); @@ -131,13 +144,13 @@ namespace Atlas::Editor { // Sort for directories to be ordered alphabetically std::sort(directory->directories.begin(), directory->directories.end(), [](const Ref& dir0, const Ref& dir1) { - return dir0->path < dir1->path; + return dir0->name < dir1->name; }); // Sort for files to be ordered alphabetically std::sort(directory->files.begin(), directory->files.end(), [](const Content& file0, const Content& file1) { - return file0.path < file1.path; + return file0.name < file1.name; }); } diff --git a/src/editor/ContentDiscovery.h b/src/editor/ContentDiscovery.h index 6462fe43e..83ca699b3 100644 --- a/src/editor/ContentDiscovery.h +++ b/src/editor/ContentDiscovery.h @@ -6,10 +6,12 @@ #include #include +#include namespace Atlas::Editor { struct ContentDirectory { + std::string name; std::filesystem::path path; std::string assetPath; @@ -22,6 +24,8 @@ namespace Atlas::Editor { public: static void Shutdown() { JobSystem::Wait(contentDiscoveryJob); } + static void Execute(); + static const Ref GetContent(); static std::vector GetContent(const ContentType type); @@ -47,6 +51,7 @@ namespace Atlas::Editor { static Ref nextContent; static JobGroup contentDiscoveryJob; + static std::atomic_bool execute; static const float discoverFrequency; static float lastDiscoveryTime; diff --git a/src/editor/DataCreator.cpp b/src/editor/DataCreator.cpp index e39ffa5fd..a92b4718d 100644 --- a/src/editor/DataCreator.cpp +++ b/src/editor/DataCreator.cpp @@ -131,6 +131,7 @@ namespace Atlas::Editor { scene->postProcessing.fsr2 = true; scene->Timestep(1.0f); + scene->Update(); min = glm::vec3(std::numeric_limits::max()); max = glm::vec3(-std::numeric_limits::max()); diff --git a/src/editor/Notifications.cpp b/src/editor/Notifications.cpp index 4abd0231a..31e3d83b8 100644 --- a/src/editor/Notifications.cpp +++ b/src/editor/Notifications.cpp @@ -22,8 +22,8 @@ namespace Atlas::Editor { void Notifications::Display() { - const auto notificationWindowFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoFocusOnAppearing | - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDecoration; + const auto notificationWindowFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking | + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing; auto viewport = ImGui::GetMainViewport(); auto viewportSize = viewport->Size; diff --git a/src/editor/Serialization.cpp b/src/editor/Serialization.cpp index 3ffc8c15d..30840adb9 100644 --- a/src/editor/Serialization.cpp +++ b/src/editor/Serialization.cpp @@ -89,6 +89,8 @@ namespace Atlas::Editor { { "cameraMovementSpeed", sceneWindow->cameraMovementSpeed }, { "cameraRotationSpeed", sceneWindow->cameraRotationSpeed }, { "depthTestBoundingVolumes", sceneWindow->depthTestBoundingVolumes }, + { "playMaximized", sceneWindow->playMaximized }, + { "perfOverlayMaximized", sceneWindow->perfOverlayMaximized }, { "camera", camera } }; @@ -124,6 +126,8 @@ namespace Atlas::Editor { try_get_json(j, "cameraMovementSpeed", sceneWindow->cameraMovementSpeed); try_get_json(j, "cameraRotationSpeed", sceneWindow->cameraRotationSpeed); try_get_json(j, "depthTestBoundingVolumes", sceneWindow->depthTestBoundingVolumes); + try_get_json(j, "playMaximized", sceneWindow->playMaximized); + try_get_json(j, "perfOverlayMaximized", sceneWindow->perfOverlayMaximized); try_get_json(j, "camera", camera); sceneWindow->cameraEntity = sceneWindow->scene->CreateEntity(); diff --git a/src/editor/Singletons.cpp b/src/editor/Singletons.cpp index cc03a9bca..9612f4d84 100644 --- a/src/editor/Singletons.cpp +++ b/src/editor/Singletons.cpp @@ -4,7 +4,6 @@ namespace Atlas::Editor { Ref Singletons::imguiWrapper; Ref Singletons::renderTarget; - Ref Singletons::pathTraceRenderTarget; Ref Singletons::mainRenderer; Ref Singletons::icons; Ref Singletons::config; @@ -14,7 +13,6 @@ namespace Atlas::Editor { imguiWrapper.reset(); renderTarget.reset(); - pathTraceRenderTarget.reset(); mainRenderer.reset(); icons.reset(); config.reset(); diff --git a/src/editor/Singletons.h b/src/editor/Singletons.h index f178bc519..6500e3258 100644 --- a/src/editor/Singletons.h +++ b/src/editor/Singletons.h @@ -2,7 +2,6 @@ #include "ImguiExtension/ImguiWrapper.h" #include "renderer/target/RenderTarget.h" -#include "renderer/target/PathTraceRenderTarget.h" #include "renderer/MainRenderer.h" #include "Icons.h" #include "Config.h" @@ -17,7 +16,6 @@ namespace Atlas::Editor { static Ref imguiWrapper; static Ref renderTarget; - static Ref pathTraceRenderTarget; static Ref mainRenderer; static Ref icons; static Ref config; diff --git a/src/editor/tools/CopyPasteHelper.cpp b/src/editor/tools/CopyPasteHelper.cpp new file mode 100644 index 000000000..4867b4752 --- /dev/null +++ b/src/editor/tools/CopyPasteHelper.cpp @@ -0,0 +1,12 @@ +#include "CopyPasteHelper.h" + +namespace Atlas::Editor { + + std::type_index CopyPasteHelper::typeInfo = typeid(CopyPasteHelper); + + void* CopyPasteHelper::data = nullptr; + size_t CopyPasteHelper::elementCount = 0; + size_t CopyPasteHelper::elementSize = 0; + std::function CopyPasteHelper::destructor; + +} \ No newline at end of file diff --git a/src/editor/tools/CopyPasteHelper.h b/src/editor/tools/CopyPasteHelper.h new file mode 100644 index 000000000..610bea178 --- /dev/null +++ b/src/editor/tools/CopyPasteHelper.h @@ -0,0 +1,115 @@ +#pragma once + +#include "../FileImporter.h" + +#include "resource/ResourceManager.h" + +#include +#include + +namespace Atlas::Editor { + + class CopyPasteHelper { + + public: + template + static void Copy(T& copy) { + + Copy(©, 1); + + } + + template + static void CopyMulti(std::vector& copy) { + + Copy(copy.data(), copy.size()); + + } + + template + static bool AcceptPaste(bool acceptMultiValue = false) { + + return typeid(T) == typeInfo && data != nullptr && + (elementCount == 1 || acceptMultiValue && elementCount >= 1); + + } + + template + static void Paste(T& paste) { + + AE_ASSERT(elementCount == 1 && "Can't paste multi value, call PasteMulti() instead"); + Paste(&paste, 1); + + } + + template + static void PasteMulti(std::vector& paste) { + + if (paste.size() != elementCount) + paste.resize(elementCount); + + Paste(paste.data(), elementCount); + + } + + static void Clear() { + + if (data != nullptr) { + auto ptr = static_cast(data); + for (int32_t i = 0; i < elementCount; i++) { + destructor(static_cast(ptr)); + ptr += elementSize; + } + std::free(data); + } + + data = nullptr; + elementCount = 0; + elementSize = 0; + + } + + private: + template + static void Copy(T* source, size_t count) { + + Clear(); + + elementCount = count; + elementSize = sizeof(T); + + typeInfo = typeid(T); + data = std::malloc(sizeof(T) * elementCount); + std::memset(data, 0, sizeof(T) * elementCount); + + T* typeData = static_cast(data); + for (size_t i = 0; i < elementCount; i++) { + new (&typeData[i])T(); + typeData[i] = source[i]; + } + + destructor = [](void* ptr) { + static_cast(ptr)->~T(); + }; + + } + + template + static void Paste(T* dst, size_t count) { + + T* typeData = static_cast(data); + for (size_t i = 0; i < count; i++) + dst[i] = typeData[i]; + + } + + static std::type_index typeInfo; + + static void* data; + static size_t elementCount; + static size_t elementSize; + static std::function destructor; + + }; + +} \ No newline at end of file diff --git a/src/editor/tools/FileSystemHelper.cpp b/src/editor/tools/FileSystemHelper.cpp new file mode 100644 index 000000000..800033382 --- /dev/null +++ b/src/editor/tools/FileSystemHelper.cpp @@ -0,0 +1,108 @@ +#include "FileSystemHelper.h" +#include "Notifications.h" +#include "Log.h" +#include "common/Path.h" +#include "ContentDiscovery.h" + +#include + +namespace Atlas::Editor { + + JobGroup FileSystemHelper::copyGroup; + JobGroup FileSystemHelper::deleteGroup; + JobGroup FileSystemHelper::duplicateGroup; + + void FileSystemHelper::Copy(const std::string& path, const std::string& destination) { + + std::vector paths = { path }; + Copy(paths, destination); + + } + + void FileSystemHelper::Copy(const std::vector& paths, const std::string& destination) { + + JobSystem::Execute(copyGroup, [paths, destination](JobData&) { + bool success = true; + for (const auto& path : paths) { + try { + std::filesystem::copy(path, destination, + std::filesystem::copy_options::overwrite_existing | + std::filesystem::copy_options::recursive); + } + catch (std::exception& e) { + success = false; + auto message = "Error copying " + path + " to asset directory: " + e.what(); + Notifications::Push({ message, vec3(1.0f, 0.0f, 0.0f) }); + Log::Error(message); + } + } + if (success) { + Notifications::Push({ "Finished copying file(s) to " + Common::Path::GetAbsolute(destination) }); + ContentDiscovery::Execute(); + } + }); + + } + + void FileSystemHelper::Delete(const std::string& path) { + + std::vector paths = { path }; + Delete(paths); + + } + + void FileSystemHelper::Delete(const std::vector& paths) { + + JobSystem::Execute(deleteGroup, [paths](JobData&) { + bool success = true; + for (const auto& path : paths) { + try { + std::filesystem::remove_all(path); + } + catch (std::exception& e) { + success = false; + auto message = "Error deleting " + path + ": " + e.what(); + Notifications::Push({ message, vec3(1.0f, 0.0f, 0.0f) }); + Log::Error(message); + } + } + if (success) { + Notifications::Push({ "Successfully deleted files(s)" }); + ContentDiscovery::Execute(); + } + }); + + } + + void FileSystemHelper::Duplicate(const std::string& path) { + + JobSystem::Execute(duplicateGroup, [path](JobData&) { + + try { + int32_t counter = 0; + auto filePath = std::filesystem::path(path); + auto dupFilePath = std::filesystem::path(path); + + do { + dupFilePath = path; + dupFilePath.replace_extension(""); + auto replacement = dupFilePath.filename().string() + "(" + std::to_string(++counter) + ")"; + dupFilePath.replace_filename(replacement); + dupFilePath.replace_extension(filePath.extension()); + } while (std::filesystem::exists(dupFilePath) && counter < 20); + + std::filesystem::copy(filePath, dupFilePath); + Notifications::Push({ "Successfully duplicated " + path }); + ContentDiscovery::Execute(); + } + catch (std::exception& e) { + auto message = "Error duplicating " + path + ": " + e.what(); + Notifications::Push({ message, vec3(1.0f, 0.0f, 0.0f) }); + Log::Error(message); + } + }); + + + } + +} \ No newline at end of file diff --git a/src/editor/tools/FileSystemHelper.h b/src/editor/tools/FileSystemHelper.h new file mode 100644 index 000000000..85079d750 --- /dev/null +++ b/src/editor/tools/FileSystemHelper.h @@ -0,0 +1,31 @@ +#pragma once + +#include "jobsystem/JobSystem.h" + +#include +#include + + +namespace Atlas::Editor { + + class FileSystemHelper { + + public: + static void Copy(const std::string& path, const std::string& destination); + + static void Copy(const std::vector& paths, const std::string& destination); + + static void Delete(const std::string& path); + + static void Delete(const std::vector& paths); + + static void Duplicate(const std::string& path); + + private: + static JobGroup copyGroup; + static JobGroup deleteGroup; + static JobGroup duplicateGroup; + + }; + +} \ No newline at end of file diff --git a/src/editor/ui/panels/EntityPropertiesPanel.cpp b/src/editor/ui/panels/EntityPropertiesPanel.cpp index 748beccb9..d7dd31311 100644 --- a/src/editor/ui/panels/EntityPropertiesPanel.cpp +++ b/src/editor/ui/panels/EntityPropertiesPanel.cpp @@ -1,5 +1,7 @@ #include "EntityPropertiesPanel.h" +#include "Notifications.h" + namespace Atlas::Editor::UI { void EntityPropertiesPanel::Render(Ref& scene, EntityProperties entityProperties) { @@ -102,6 +104,8 @@ namespace Atlas::Editor::UI { entity.AddComponent(mat4(1.0f), false); if (!entity.HasComponent() && ImGui::MenuItem("Add mesh component")) entity.AddComponent(); + if (!entity.HasComponent() && ImGui::MenuItem("Add light component")) + entity.AddComponent(LightType::PointLight); if (!entity.HasComponent() && ImGui::MenuItem("Add audio component")) entity.AddComponent(); if (!entity.HasComponent() && ImGui::MenuItem("Add audio volume component")) @@ -157,6 +161,8 @@ namespace Atlas::Editor::UI { entity.RemoveComponent(); if (entity.HasComponent() && ImGui::MenuItem("Remove mesh component")) entity.RemoveComponent(); + if (entity.HasComponent() && ImGui::MenuItem("Remove light component")) + entity.RemoveComponent(); if (entity.HasComponent() && ImGui::MenuItem("Remove audio component")) entity.RemoveComponent(); if (entity.HasComponent() && ImGui::MenuItem("Remove audio volume component")) @@ -176,6 +182,27 @@ namespace Atlas::Editor::UI { } } + // Paste components + if (ImGui::BeginPopupContextWindow(nullptr, ImGuiPopupFlags_NoOpenOverItems | ImGuiPopupFlags_MouseButtonRight)) { + if (ImGui::MenuItem("Paste")) { + if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else if (HandleComponentPaste(scene, entity)) {} + else { + Notifications::Push({"Invalid type to paste as a component."}); + } + } + + ImGui::EndPopup(); + } + } } \ No newline at end of file diff --git a/src/editor/ui/panels/EntityPropertiesPanel.h b/src/editor/ui/panels/EntityPropertiesPanel.h index 71c57a107..6f3d9a7ed 100644 --- a/src/editor/ui/panels/EntityPropertiesPanel.h +++ b/src/editor/ui/panels/EntityPropertiesPanel.h @@ -15,7 +15,12 @@ #include "components/CameraComponentPanel.h" #include "components/TextComponentPanel.h" +#include "tools/CopyPasteHelper.h" +#include "Notifications.h" + #include +#include +#include namespace Atlas::Editor::UI { @@ -32,6 +37,12 @@ namespace Atlas::Editor::UI { void Render(Ref& scene, EntityProperties entityProperties); private: + template + struct ComponentCopy { + Scene::Scene* scene; + T component; + }; + NameComponentPanel nameComponentPanel; TransformComponentPanel transformComponentPanel; MeshComponentPanel meshComponentPanel; @@ -52,7 +63,24 @@ namespace Atlas::Editor::UI { ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_Framed; - if (ImGui::TreeNodeEx(name.c_str(), nodeFlags)) { + bool open = ImGui::TreeNodeEx(name.c_str(), nodeFlags); + + if (ImGui::BeginPopupContextItem(name.c_str())) { + // We shouldn't allow the user to delete the root entity + if (ImGui::MenuItem("Copy")) { + if constexpr (std::is_same_v) { + // Restore body creation settings, after the copy the current body id is not valid anymore due + // to body recreation + component.creationSettings = CreateRef(component.GetBodyCreationSettings()); + } + ComponentCopy copy { scene.get(), component }; + CopyPasteHelper::Copy(copy); + } + + ImGui::EndPopup(); + } + + if (open) { resourceChanged = panel.Render(scene, entity, component); ImGui::TreePop(); @@ -61,6 +89,32 @@ namespace Atlas::Editor::UI { return resourceChanged; } + template + bool HandleComponentPaste(Ref& scene, Scene::Entity entity, std::optional> func = std::nullopt) { + + if (CopyPasteHelper::AcceptPaste>()) { + ComponentCopy copy; + CopyPasteHelper::Paste(copy); + + if (copy.scene != scene.get()) { + Notifications::Push({ "Cannot copy component from one scene to another", vec3(1.0f, 0.0f, 0.0f) }); + return true; + } + + if (entity.HasComponent()) + entity.RemoveComponent(); + + auto& comp = entity.AddComponent(copy.component); + if (func.has_value()) + func.value()(comp); + + return true; + } + + return false; + + } + }; } \ No newline at end of file diff --git a/src/editor/ui/panels/PopupPanels.cpp b/src/editor/ui/panels/PopupPanels.cpp index d796caf54..0e7c4df97 100644 --- a/src/editor/ui/panels/PopupPanels.cpp +++ b/src/editor/ui/panels/PopupPanels.cpp @@ -174,7 +174,7 @@ namespace Atlas::Editor::UI { ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); } else { - ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(1.0f, 1.0f, 1.0f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(1.0f, 1.0f, 1.0f, 0.8f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); } ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 2.0f); diff --git a/src/editor/ui/panels/ResourceSelectionPanel.h b/src/editor/ui/panels/ResourceSelectionPanel.h index f58773f66..51be3ba5a 100644 --- a/src/editor/ui/panels/ResourceSelectionPanel.h +++ b/src/editor/ui/panels/ResourceSelectionPanel.h @@ -2,6 +2,7 @@ #include "Panel.h" #include "Singletons.h" +#include "Notifications.h" #include "ui/popups/ResourceSelectionPopup.h" #include "tools/ResourcePayloadHelper.h" @@ -31,12 +32,22 @@ namespace Atlas::Editor::UI { resourceChanged = false; auto buttonName = resourceHandle.IsValid() ? resourceHandle.GetResource()->GetFileName() : - "Drop resource here"; + "Drop resource here##" + std::to_string(counter++); - popup.SetID(resourceHandle.GetID()); + ImGui::PushID(resourceHandle.IsValid() ? int32_t(resourceHandle.GetID()) : counter); + + popup.SetID(resourceHandle.IsValid() ? resourceHandle.GetID() : counter); if (ImGui::Button(buttonName.c_str(), { resourceButtonSize, 0 })) popup.Open(); + if (resourceHandle.IsValid() && ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem("Copy name")) { + ImGui::SetClipboardText(buttonName.c_str()); + Notifications::Push({ .message = "Copied file name to clipboard.", .displayTime = 3.0f }); + } + ImGui::EndPopup(); + } + // Such that drag and drop will work from the content browser if (ImGui::IsDragDropActive() && ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly)) { ImGui::SetWindowFocus(); @@ -67,6 +78,8 @@ namespace Atlas::Editor::UI { auto resources = ResourceManager::GetResources(); handle = popup.Render(resources); + ImGui::PopID(); + if (handle.IsValid()) { resourceHandle = handle; resourceChanged = true; @@ -76,9 +89,17 @@ namespace Atlas::Editor::UI { } + void Reset() { + + counter = 0; + + } + private: ResourceSelectionPopup popup; + int32_t counter = 0; + }; } \ No newline at end of file diff --git a/src/editor/ui/panels/SceneHierarchyPanel.cpp b/src/editor/ui/panels/SceneHierarchyPanel.cpp index f14a97b94..a665cb8f9 100644 --- a/src/editor/ui/panels/SceneHierarchyPanel.cpp +++ b/src/editor/ui/panels/SceneHierarchyPanel.cpp @@ -61,12 +61,15 @@ namespace Atlas::Editor::UI { ImGui::EndPopup(); } + std::string prevEntitySearch = entitySearch; ImGui::InputTextWithHint("Search", "Type to search for entity", &entitySearch); + + bool searchChanged = entitySearch != prevEntitySearch; if (ImGui::IsItemClicked()) selectionChanged = true; JobSystem::Wait(searchJob); - TraverseHierarchy(scene, root, matchSet, inFocus, &selectionChanged); + TraverseHierarchy(scene, root, matchSet, inFocus, searchChanged, &selectionChanged); RenderExtendedHierarchy(scene, &selectionChanged); @@ -79,7 +82,7 @@ namespace Atlas::Editor::UI { #endif if (inFocus && controlDown && ImGui::IsKeyPressed(ImGuiKey_D, false)) DuplicateSelectedEntity(scene); - if (inFocus && ImGui::IsKeyPressed(ImGuiKey_Delete, false)) + if (inFocus && controlDown && ImGui::IsKeyPressed(ImGuiKey_Delete, false)) DeleteSelectedEntity(scene); if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !selectionChanged) { @@ -95,10 +98,10 @@ namespace Atlas::Editor::UI { } void SceneHierarchyPanel::TraverseHierarchy(Ref& scene, Scene::Entity entity, - std::unordered_set& matchSet, bool inFocus, bool* selectionChanged) { + std::unordered_set& matchSet, bool inFocus, bool searchChanged, bool* selectionChanged) { ImGuiTreeNodeFlags baseFlags = ImGuiTreeNodeFlags_OpenOnArrow | - ImGuiTreeNodeFlags_OpenOnDoubleClick; + ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; auto hierarchyComponent = entity.TryGetComponent(); auto nameComponent = entity.TryGetComponent(); @@ -118,6 +121,11 @@ namespace Atlas::Editor::UI { if (!rootNode && !validSearch) return; + if (matchSet.contains(entity) && !entitySearch.empty() && searchChanged) + ImGui::SetNextItemOpen(true, ImGuiCond_Always); + else if (entitySearch.empty() && searchChanged || !matchSet.contains(entity) && searchChanged) + ImGui::SetNextItemOpen(false, ImGuiCond_Always); + auto nodeFlags = baseFlags; nodeFlags |= entity == selectedEntity ? ImGuiTreeNodeFlags_Selected : 0; auto entityId = static_cast(entity); @@ -171,7 +179,7 @@ namespace Atlas::Editor::UI { auto children = hierarchyComponent->GetChildren(); for (auto childEntity : children) { - TraverseHierarchy(scene, childEntity, matchSet, inFocus, selectionChanged); + TraverseHierarchy(scene, childEntity, matchSet, inFocus, searchChanged, selectionChanged); } diff --git a/src/editor/ui/panels/SceneHierarchyPanel.h b/src/editor/ui/panels/SceneHierarchyPanel.h index 9d66de77f..a0d3db3fa 100644 --- a/src/editor/ui/panels/SceneHierarchyPanel.h +++ b/src/editor/ui/panels/SceneHierarchyPanel.h @@ -35,7 +35,7 @@ namespace Atlas::Editor::UI { private: void TraverseHierarchy(Ref& scene, Scene::Entity entity, - std::unordered_set& matchSet, bool inFocus, bool* selectionChanged); + std::unordered_set& matchSet, bool inFocus, bool searchChanged, bool* selectionChanged); void RenderExtendedHierarchy(const Ref& scene, bool* selectionChanged); diff --git a/src/editor/ui/panels/ScenePropertiesPanel.h b/src/editor/ui/panels/ScenePropertiesPanel.h index 76deec600..e332c71fb 100644 --- a/src/editor/ui/panels/ScenePropertiesPanel.h +++ b/src/editor/ui/panels/ScenePropertiesPanel.h @@ -75,13 +75,19 @@ namespace Atlas::Editor::UI { } else if constexpr (std::is_same_v) { RenderHeading("Post processing"); - postProcessingPanel.Render(t); + postProcessingPanel.Render(t, + [&](ResourceHandle handle) { + return textureSelectionPanel.Render(handle); + }); } else if constexpr (std::is_same_v>) { RenderHeading("Scene statistics"); sceneStatisticsPanel.Render(t); } + cubemapSelectionPanel.Reset(); + textureSelectionPanel.Reset(); + ImGui::End(); } @@ -103,6 +109,7 @@ namespace Atlas::Editor::UI { SceneStatisticsPanel sceneStatisticsPanel; ResourceSelectionPanel cubemapSelectionPanel; + ResourceSelectionPanel textureSelectionPanel; ImguiExtension::FogPanel fogPanel; ImguiExtension::VolumetricCloudsPanel volumetricCloudsPanel; diff --git a/src/editor/ui/panels/SceneStatisticsPanel.cpp b/src/editor/ui/panels/SceneStatisticsPanel.cpp index 6a25f0368..27551237e 100644 --- a/src/editor/ui/panels/SceneStatisticsPanel.cpp +++ b/src/editor/ui/panels/SceneStatisticsPanel.cpp @@ -22,6 +22,9 @@ namespace Atlas::Editor::UI { auto meshes = scene->GetMeshes(); auto materials = scene->GetMaterials(); + auto lightCount = scene->GetComponentCount(); + auto meshInstanceCount = scene->GetComponentCount(); + ImGui::SeparatorText("Dimensions"); ImGui::Text("Min: %.2f, %.2f, %.2f", min.x, min.y, min.z); ImGui::Text("Max: %.2f, %.2f, %.2f", max.x, max.y, max.z); @@ -31,6 +34,9 @@ namespace Atlas::Editor::UI { ImGui::Text("Entity count: %d", int32_t(scene->GetEntityCount())); ImGui::Text("Mesh count: %d", int32_t(meshes.size())); ImGui::Text("Material count: %d", int32_t(materials.size())); + ImGui::Text("Light count: %d", int32_t(lightCount)); + ImGui::Text("Mesh instance count: %d", int32_t(meshInstanceCount)); + ImGui::Text("Physics body count: %d", scene->physicsWorld->GetBodyCount()); } diff --git a/src/editor/ui/panels/ViewportPanel.cpp b/src/editor/ui/panels/ViewportPanel.cpp index c871eb304..45b84b6d7 100644 --- a/src/editor/ui/panels/ViewportPanel.cpp +++ b/src/editor/ui/panels/ViewportPanel.cpp @@ -52,47 +52,9 @@ namespace Atlas::Editor::UI { auto region = ImGui::GetContentRegionAvail(); auto windowPos = ImGui::GetWindowPos(); - bool validRegion = region.x > 0.0f && region.y > 0.0f; - - if ((viewportTexture.width != int32_t(region.x) || - viewportTexture.height != int32_t(region.y)) && validRegion) { - viewport->Set(int32_t(windowPos.x), int32_t(windowPos.y), int32_t(region.x), int32_t(region.y)); - viewportTexture.Resize(int32_t(region.x), int32_t(region.y)); - CreateRenderPass(); - } - - if (scene != nullptr && validRegion && isActiveWindow && !isBlocked) { - auto& config = Singletons::config; - - if (config->pathTrace) { - auto& pathTraceRenderTarget = Singletons::pathTraceRenderTarget; - - if (pathTraceRenderTarget->GetWidth() != viewportTexture.width || - pathTraceRenderTarget->GetHeight() != viewportTexture.height) { - pathTraceRenderTarget->Resize(viewportTexture.width, viewportTexture.height); - } - - Singletons::mainRenderer->PathTraceScene(viewport, pathTraceRenderTarget, scene, &viewportTexture); - } - else { - auto& renderTarget = Singletons::renderTarget; - - if (renderTarget->GetWidth() != viewportTexture.width || - renderTarget->GetHeight() != viewportTexture.height) { - renderTarget->Resize(viewportTexture.width, viewportTexture.height); - } - - Singletons::mainRenderer->RenderScene(viewport, renderTarget, scene, - primitiveBatchWrapper.primitiveBatch, &viewportTexture); - - if (visualization != Lit) { - RenderVisualization(); - } - } - - primitiveBatchWrapper.primitiveBatch->Clear(); - - } + auto pos = ivec2(int32_t(windowPos.x), int32_t(windowPos.y)); + auto size = ivec2(int32_t(region.x), int32_t(region.y)); + RenderScene(scene, pos, size, isActiveWindow); if (viewportTexture.IsValid() && viewportTexture.image->layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { auto set = Singletons::imguiWrapper->GetTextureDescriptorSet(&viewportTexture); @@ -116,6 +78,43 @@ namespace Atlas::Editor::UI { } + void ViewportPanel::RenderScene(Ref& scene, ivec2 pos, ivec2 size, bool isActive) { + + bool validSize = size.x > 0 && size.y > 0; + + if ((viewportTexture.width != size.x || + viewportTexture.height != size.y) && validSize) { + viewport->Set(pos.x, pos.y, size.x, size.y); + viewportTexture.Resize(size.x, size.y); + CreateRenderPass(); + } + + if (scene != nullptr && validSize && isActive && !Singletons::blockingOperation->block) { + auto& config = Singletons::config; + auto& renderTarget = Singletons::renderTarget; + + if (renderTarget->GetWidth() != viewportTexture.width || + renderTarget->GetHeight() != viewportTexture.height) { + renderTarget->Resize(viewportTexture.width, viewportTexture.height); + } + + if (config->pathTrace) { + Singletons::mainRenderer->PathTraceScene(viewport, renderTarget, scene, &viewportTexture); + } + else { + Singletons::mainRenderer->RenderScene(viewport, renderTarget, scene, + primitiveBatchWrapper.primitiveBatch, &viewportTexture); + + if (visualization != Lit) { + RenderVisualization(); + } + } + + primitiveBatchWrapper.primitiveBatch->Clear(); + } + + } + void ViewportPanel::RenderVisualization() { auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; @@ -137,6 +136,10 @@ namespace Atlas::Editor::UI { mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->roughnessMetallicAoTexture.get(), 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); } + if (visualization == GBufferEmissive) { + mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->emissiveTexture.get(), + 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); + } if (visualization == GBufferNormals) { mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->normalTexture.get(), 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); @@ -145,6 +148,14 @@ namespace Atlas::Editor::UI { mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->geometryNormalTexture.get(), 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); } + if (visualization == GBufferMaterialIdx) { + mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->materialIdxTexture.get(), + 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f / 10000.0f, false, true); + } + if (visualization == GBufferStencil) { + mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->stencilTexture.get(), + 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f / 255.0f, false, true); + } if (visualization == GBufferDepth) { mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, rtData->depthTexture.get(), 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); @@ -157,6 +168,10 @@ namespace Atlas::Editor::UI { mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, &renderTarget->reflectionTexture, 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); } + else if (visualization == Volumetrics) { + mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, &renderTarget->volumetricTexture, + 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); + } else if (visualization == Clouds) { mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, &renderTarget->volumetricCloudsTexture, 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0, 1.0f, false, true); diff --git a/src/editor/ui/panels/ViewportPanel.h b/src/editor/ui/panels/ViewportPanel.h index b08b28838..f1e1eb3c0 100644 --- a/src/editor/ui/panels/ViewportPanel.h +++ b/src/editor/ui/panels/ViewportPanel.h @@ -11,10 +11,14 @@ namespace Atlas::Editor::UI { Lit = 0, GBufferBaseColor, GBufferRoughnessMetalnessAo, + GBufferEmissive, GBufferNormals, GBufferGeometryNormals, + GBufferMaterialIdx, + GBufferStencil, GBufferDepth, GBufferVelocity, + Volumetrics, Clouds, Reflections, SSS, @@ -32,6 +36,8 @@ namespace Atlas::Editor::UI { void Render(Ref& scene, bool isActiveWindow); + void RenderScene(Ref& scene, ivec2 pos, ivec2 size, bool isActive); + Ref viewport; Texture::Texture2D viewportTexture; diff --git a/src/editor/ui/panels/components/AudioComponentPanel.cpp b/src/editor/ui/panels/components/AudioComponentPanel.cpp index 47785e0e9..1405b6165 100644 --- a/src/editor/ui/panels/components/AudioComponentPanel.cpp +++ b/src/editor/ui/panels/components/AudioComponentPanel.cpp @@ -30,6 +30,8 @@ namespace Atlas::Editor::UI { ImGui::Checkbox("Loop stream", &audioComponent.stream->loop); } + audioSelectionPanel.Reset(); + return resourceChanged; } diff --git a/src/editor/ui/panels/components/AudioVolumeComponentPanel.cpp b/src/editor/ui/panels/components/AudioVolumeComponentPanel.cpp index aefb3a840..d13c0710d 100644 --- a/src/editor/ui/panels/components/AudioVolumeComponentPanel.cpp +++ b/src/editor/ui/panels/components/AudioVolumeComponentPanel.cpp @@ -34,6 +34,8 @@ namespace Atlas::Editor::UI { ImGui::Checkbox("Loop stream", &audioVolumeComponent.stream->loop); } + audioSelectionPanel.Reset(); + return resourceChanged; } diff --git a/src/editor/ui/panels/components/CameraComponentPanel.cpp b/src/editor/ui/panels/components/CameraComponentPanel.cpp index dda85c129..2fd7d12c7 100644 --- a/src/editor/ui/panels/components/CameraComponentPanel.cpp +++ b/src/editor/ui/panels/components/CameraComponentPanel.cpp @@ -17,6 +17,8 @@ namespace Atlas::Editor::UI { ImGui::Checkbox("Third person", &cameraComponent.thirdPerson); ImGui::DragFloat("Third person distance", &cameraComponent.thirdPersonDistance, 0.1f, 0.0f, 100.0f); + ImGui::Checkbox("Main", &cameraComponent.isMain); + ImGui::Text("Lens properties"); ImGui::DragFloat("Field of view", &cameraComponent.fieldOfView, 0.1f, 1.0f, 180.0f); @@ -25,7 +27,7 @@ namespace Atlas::Editor::UI { ImGui::DragFloat("Near plane", &cameraComponent.nearPlane, 0.01f, 0.01f, 10.0f); ImGui::DragFloat("Far plane", &cameraComponent.farPlane, 1.0f, 1.0f, 2000.0f); - ImGui::Checkbox("Main", &cameraComponent.isMain); + ImGui::DragFloat("Exposure", &cameraComponent.exposure, 0.01f, 0.01f, 100.0f), ImGui::PopID(); diff --git a/src/editor/ui/panels/components/LightComponentPanel.cpp b/src/editor/ui/panels/components/LightComponentPanel.cpp index a2a1c26e9..c898bab97 100644 --- a/src/editor/ui/panels/components/LightComponentPanel.cpp +++ b/src/editor/ui/panels/components/LightComponentPanel.cpp @@ -9,7 +9,7 @@ namespace Atlas::Editor::UI { bool LightComponentPanel::Render(Ref& scene, Scene::Entity entity, LightComponent &lightComponent) { - const char* typeItems[] = { "Directional" }; + const char* typeItems[] = { "Directional", "Point", "Spot"}; int typeItem = static_cast(lightComponent.type); ImGui::Combo("Light type", &typeItem, typeItems, IM_ARRAYSIZE(typeItems)); lightComponent.type = static_cast(typeItem); @@ -25,10 +25,20 @@ namespace Atlas::Editor::UI { if (lightComponent.type == LightType::DirectionalLight) { ImGui::Checkbox("Main", &lightComponent.isMain); auto& directional = lightComponent.properties.directional; - ImGui::DragFloat3("Direction", &directional.direction[0], 0.005f, -1.0f, 1.0f); + ImGui::DragFloat3("Direction", glm::value_ptr(directional.direction), 0.005f, -1.0f, 1.0f); } else if (lightComponent.type == LightType::PointLight) { - + auto& point = lightComponent.properties.point; + ImGui::DragFloat3("Position", glm::value_ptr(point.position), 0.1f, -10000.0f, 10000.0f); + ImGui::DragFloat("Radius", &point.radius, 0.01f, 0.01f, 10000.0f); + } + else if (lightComponent.type == LightType::SpotLight) { + auto& spot = lightComponent.properties.spot; + ImGui::DragFloat3("Position", glm::value_ptr(spot.position), 0.1f, -10000.0f, 10000.0f); + ImGui::DragFloat3("Direction", glm::value_ptr(spot.direction), 0.01f, -1.0f, 1.0f); + ImGui::DragFloat("Radius", &spot.radius, 0.01f, 0.01f, 10000.0f); + ImGui::DragFloat("Inner cone angle", &spot.innerConeAngle, 0.01f, 0.01f, 2.0f); + ImGui::DragFloat("Outer cone angle", &spot.outerConeAngle, 0.01f, 0.01f, 2.0f); } ImGui::Separator(); @@ -39,11 +49,18 @@ namespace Atlas::Editor::UI { ImGui::Checkbox("Shadow", &castShadow); ImGui::ColorEdit3("Color", &lightComponent.color[0]); ImGui::DragFloat("Intensity", &lightComponent.intensity, 0.1f, 0.0f, 1000.0f); + ImGui::DragFloat("Volumetric intensity", &lightComponent.volumetricIntensity, 0.1f, 0.0f, 10.0f); if (!lightComponent.shadow && castShadow) { if (lightComponent.type == LightType::DirectionalLight) { lightComponent.AddDirectionalShadow(300.0f, 3.0f, 1024, 0.05f, 3, 0.95f, true, 2048.0f); } + if (lightComponent.type == LightType::PointLight) { + lightComponent.AddPointShadow(0.25f, 1024); + } + if (lightComponent.type == LightType::SpotLight) { + lightComponent.AddSpotShadow(2.0f, 1024); + } } else if (lightComponent.shadow && !castShadow) { lightComponent.shadow = nullptr; diff --git a/src/editor/ui/panels/components/LuaScriptComponentPanel.cpp b/src/editor/ui/panels/components/LuaScriptComponentPanel.cpp index 2930a9b16..c16c5630f 100644 --- a/src/editor/ui/panels/components/LuaScriptComponentPanel.cpp +++ b/src/editor/ui/panels/components/LuaScriptComponentPanel.cpp @@ -52,7 +52,14 @@ namespace Atlas::Editor::UI case LuaScriptComponent::PropertyType::String: ImGui::InputText(name.c_str(), &property.stringValue); break; + case LuaScriptComponent::PropertyType::Vec2: + ImGui::DragFloat2(name.c_str(), glm::value_ptr(property.vec2Value)); + break; + case LuaScriptComponent::PropertyType::Vec3: + ImGui::DragFloat3(name.c_str(), glm::value_ptr(property.vec3Value)); + break; case LuaScriptComponent::PropertyType::Undefined: + default: break; } } diff --git a/src/editor/ui/panels/components/MeshComponentPanel.cpp b/src/editor/ui/panels/components/MeshComponentPanel.cpp index c96a94726..f9d482bdd 100644 --- a/src/editor/ui/panels/components/MeshComponentPanel.cpp +++ b/src/editor/ui/panels/components/MeshComponentPanel.cpp @@ -26,18 +26,23 @@ namespace Atlas::Editor::UI { ImGui::Combo("Mobility", &mobilityItem, mobilityItems, IM_ARRAYSIZE(mobilityItems)); mesh->mobility = static_cast(mobilityItem); - ImGui::Checkbox("Invert UVs", &mesh->invertUVs); ImGui::Checkbox("Cull backfaces", &mesh->cullBackFaces); + ImGui::Checkbox("Ray trace", &mesh->rayTrace); ImGui::Checkbox("Is vegetation", &mesh->vegetation); ImGui::Checkbox("Cast shadow", &mesh->castShadow); ImGui::SliderInt("Shadow cascades", &mesh->allowedShadowCascades, 1, 6); + auto region = ImGui::GetContentRegionAvail(); + if (ImGui::Button("Invert normals", { region.x, 0.0 })) + mesh->InvertNormals(); + ImGui::Separator(); ImGui::Text("Culling settings"); ImGui::DragFloat("Distance culling", &mesh->distanceCulling, 1.0f); ImGui::DragFloat("Shadow distance culling", &mesh->shadowDistanceCulling, 1.0f); + ImGui::DragFloat("Ray trace distance culling", &mesh->rayTraceDistanceCulling, 1.0f); ImGui::Separator(); ImGui::Text("Wind settings"); @@ -57,7 +62,11 @@ namespace Atlas::Editor::UI { // Just update materials regardless of any change mesh->UpdatePipelines(); - } + } + + meshSelectionPanel.Reset(); + materialSelectionPanel.Reset(); + textureSelectionPanel.Reset(); return resourceChanged; diff --git a/src/editor/ui/panels/components/PlayerComponentPanel.cpp b/src/editor/ui/panels/components/PlayerComponentPanel.cpp index ea9591c61..11816c154 100644 --- a/src/editor/ui/panels/components/PlayerComponentPanel.cpp +++ b/src/editor/ui/panels/components/PlayerComponentPanel.cpp @@ -9,6 +9,9 @@ namespace Atlas::Editor::UI { ImGui::PushID(GetNameID()); + if (!entity.HasComponent()) + ImGui::Text("Player component needs a transform component to work properly"); + ImGui::Text("Shape"); RenderShapeSettings(entity, playerComponent, *playerComponent.creationSettings); diff --git a/src/editor/ui/panels/components/RigidBodyComponentPanel.cpp b/src/editor/ui/panels/components/RigidBodyComponentPanel.cpp index 88f46b481..73728a6b7 100644 --- a/src/editor/ui/panels/components/RigidBodyComponentPanel.cpp +++ b/src/editor/ui/panels/components/RigidBodyComponentPanel.cpp @@ -7,7 +7,14 @@ namespace Atlas::Editor::UI { bool RigidBodyComponentPanel::Render(const Ref& scene, Scene::Entity entity, RigidBodyComponent &rigidBodyComponent) { + if (!entity.HasComponent()) + ImGui::Text("Rigid body component needs a transform component to work properly"); + auto creationSettings = rigidBodyComponent.GetBodyCreationSettings(); + if (!creationSettings.shape) { + ImGui::Text("Couldn't get body creation settings or shape"); + return false; + } ImGui::Text("Shape"); @@ -23,6 +30,8 @@ namespace Atlas::Editor::UI { if (scene->physicsWorld->pauseSimulation) rigidBodyComponent.creationSettings = CreateRef(creationSettings); + meshSelectionPanel.Reset(); + return false; } diff --git a/src/editor/ui/popups/ResourceSelectionPopup.h b/src/editor/ui/popups/ResourceSelectionPopup.h index f9f359698..13e798fb1 100644 --- a/src/editor/ui/popups/ResourceSelectionPopup.h +++ b/src/editor/ui/popups/ResourceSelectionPopup.h @@ -7,6 +7,8 @@ #include #include +#include + namespace Atlas::Editor::UI { class ResourceSelectionPopup : public Popup { diff --git a/src/editor/ui/windows/ContentBrowserWindow.cpp b/src/editor/ui/windows/ContentBrowserWindow.cpp index ce7944870..b4624d0b2 100644 --- a/src/editor/ui/windows/ContentBrowserWindow.cpp +++ b/src/editor/ui/windows/ContentBrowserWindow.cpp @@ -3,6 +3,8 @@ #include "DataCreator.h" #include "Notifications.h" #include "ui/panels/PopupPanels.h" +#include "tools/CopyPasteHelper.h" +#include "tools/FileSystemHelper.h" #include "mesh/Mesh.h" #include "scene/Scene.h" @@ -14,6 +16,7 @@ #include #ifdef AE_OS_WINDOWS +#define NOMINMAX #include #include #endif @@ -22,7 +25,7 @@ namespace Atlas::Editor::UI { ContentBrowserWindow::ContentBrowserWindow(bool show) : Window("Content browser", show) { - + selectionStorage.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return uint32_t(idx); }; } @@ -99,7 +102,7 @@ namespace Atlas::Editor::UI { ImGui::Begin("ResourceTypeOverview", nullptr); // Use a child as a dummy to create a drop target, since it doesn't work directly on a window - ImGui::BeginChild("ResourceTypeDropChild"); + ImGui::BeginChild("ResourceTypeDropChild", ImVec2(0.0, 0.0)); RenderDirectoryControl(); @@ -224,10 +227,10 @@ namespace Atlas::Editor::UI { void ContentBrowserWindow::RenderDirectoryContent() { float totalWidth = ImGui::GetContentRegionAvail().x; - auto columnItemCount = int32_t(totalWidth / itemSize); - columnItemCount = columnItemCount <= 0 ? 1 : columnItemCount; + auto columnCount = int32_t(totalWidth / itemSize); + columnCount = columnCount <= 0 ? 1 : columnCount; - ImGui::Columns(columnItemCount, nullptr, false); + float columnSize = totalWidth / float(columnCount); if (!std::filesystem::exists(currentDirectory)) { auto message = "Content directory " + Common::Path::Normalize(currentDirectory) + " has been moved or deleted."; @@ -241,14 +244,32 @@ namespace Atlas::Editor::UI { nextDirectory = std::string(); + auto entryCount = int32_t(directories.size()) + int32_t(files.size()); + ImGuiMultiSelectIO* multiSelectionIO = ImGui::BeginMultiSelect(ImGuiMultiSelectFlags_BoxSelect2d | + ImGuiMultiSelectFlags_ClearOnClickVoid, selectionStorage.Size, entryCount); + selectionStorage.ApplyRequests(multiSelectionIO); + + ImGui::SetCursorPosX(padding); + + int32_t entryIdx = 0; + float columnHeight = 0.0f; for (const auto& directory : directories) { - RenderContentEntry(directory->path, directory->assetPath, ContentType::None); + // Ignore 'invisible' directories + if (directory->assetPath.at(0) == '.') + continue; + + RenderContentEntry(directory->path, directory->assetPath, ContentType::None, + entryIdx++, columnCount, columnSize, columnHeight); } for (const auto& file : files) { - RenderContentEntry(file.path, file.assetPath, file.type); + RenderContentEntry(file.path, file.assetPath, file.type, + entryIdx++, columnCount, columnSize, columnHeight); } + multiSelectionIO = ImGui::EndMultiSelect(); + selectionStorage.ApplyRequests(multiSelectionIO); + if (ImGui::BeginPopupContextWindow(nullptr, ImGuiPopupFlags_NoOpenOverItems | ImGuiPopupFlags_MouseButtonRight)) { if (ImGui::BeginMenu("Create")) { if (ImGui::MenuItem("Folder")) { @@ -260,6 +281,12 @@ namespace Atlas::Editor::UI { ImGui::EndMenu(); } + if (CopyPasteHelper::AcceptPaste() && ImGui::MenuItem("Paste")) { + ContentCopy copy; + CopyPasteHelper::Paste(copy); + FileSystemHelper::Copy(copy.paths, Common::Path::GetAbsolute(currentDirectory)); + } + ImGui::EndPopup(); } @@ -277,20 +304,19 @@ namespace Atlas::Editor::UI { } - void ContentBrowserWindow::RenderContentEntry(const std::filesystem::path& path, const std::string& assetPath, ContentType contentType) { + void ContentBrowserWindow::RenderContentEntry(const std::filesystem::path& path, const std::string& assetPath, + ContentType contentType, int32_t entryIdx, int32_t columnCount, float columnSize, float& columnHeight) { bool isDirectory = contentType == ContentType::None; - // Ignore 'invisible' directories - if (isDirectory && assetPath.at(0) == '.') - return; auto filename = Common::Path::GetFileName(assetPath); ImVec2 buttonSize = ImVec2(iconSize, iconSize); + auto cursorPos = ImGui::GetCursorPos(); ImGui::BeginGroup(); - ImGui::PushID(path.c_str()); + ImGui::PushID(entryIdx); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.0f, 0.0f)); @@ -310,15 +336,23 @@ namespace Atlas::Editor::UI { type = Content::contentTypeMapping.at(fileType); auto assetRelativePath = Common::Path::Normalize(assetPath); - auto buttonFlags = isDirectory || type == ContentType::Scene ? ImGuiButtonFlags_PressedOnDoubleClick : 0; - if (ImGui::ImageButtonEx(ImGui::GetID("ImageButton"), set, buttonSize, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), - ImVec4(0.0f, 0.0f, 0.0f, 0.0f), ImVec4(1.0f, 1.0f, 1.0f, 1.0f), buttonFlags)) { - if (isDirectory) - nextDirectory = path.string(); - else if (type == ContentType::Scene) - FileImporter::ImportFile(assetRelativePath); + + // Add selectable to allow for multi-select + bool selected = selectionStorage.Contains((ImGuiID)entryIdx); + ImGui::SetNextItemSelectionUserData(entryIdx); + ImGui::Selectable("##Selectable", &selected, ImGuiSelectableFlags_None, buttonSize); + + if (ImGui::IsItemHovered()) { + if (ImGui::IsMouseDoubleClicked(0)) { + if (isDirectory) + nextDirectory = path.string(); + else if (type == ContentType::Scene) + FileImporter::ImportFile(assetRelativePath); + } } + ImGui::SetItemAllowOverlap(); + if (!isDirectory && ImGui::BeginDragDropSource()) { // Size doesn't count the termination character, so add +1 ImGui::SetDragDropPayload("ContentBrowserResource", assetRelativePath.data(), assetRelativePath.size() + 1); @@ -328,46 +362,79 @@ namespace Atlas::Editor::UI { } if (ImGui::BeginPopupContextItem()) { + auto singleSelect = selectionStorage.Size == 1; // Do a direct import here without relying on the file importer - if (type == ContentType::MeshSource && ImGui::MenuItem("Import as scene")) { + if (type == ContentType::MeshSource && singleSelect && ImGui::MenuItem("Import as scene")) { PopupPanels::filename = assetRelativePath; PopupPanels::isImportScenePopupVisible = true; } // We shouldn't allow the user to delete the root entity - if (ImGui::MenuItem("Open externally")) + if (singleSelect && ImGui::MenuItem("Open externally")) OpenExternally(std::filesystem::absolute(path).string(), isDirectory); - if (ImGui::MenuItem("Rename")) { + if (singleSelect && ImGui::MenuItem("Rename")) { renamePopupVisible = true; auto dirEntryFilename = path.filename(); renameString = dirEntryFilename.replace_extension("").string(); renamePath = path; } - if (ImGui::MenuItem("Delete")) - std::filesystem::remove_all(path); + if (singleSelect && ImGui::MenuItem("Duplicate")) { + FileSystemHelper::Duplicate(path.string()); + } + + if (ImGui::MenuItem("Copy")) { + ContentCopy copy { GetSelectedPaths() }; + CopyPasteHelper::Copy(copy); + } + + if (ImGui::MenuItem("Delete")) { + auto paths = GetSelectedPaths(); + FileSystemHelper::Delete(paths); + } ImGui::EndPopup(); } + // Just draw the icon if it is actually visible + if (ImGui::IsItemVisible()) { + ImGui::SameLine(); + ImGui::SetCursorPosX(cursorPos.x); + ImGui::Image(set, buttonSize); + } + ImGui::PopStyleColor(); auto offset = 0.0f; auto textSize = ImGui::CalcTextSize(filename.c_str()); - if (textSize.x < iconSize + padding) - offset = (iconSize + padding - textSize.x) / 2.0f; + if (textSize.x < iconSize) + offset = (iconSize - textSize.x) / 2.0f; auto cursorX = ImGui::GetCursorPosX(); ImGui::SetCursorPosX(cursorX + offset); - ImGui::TextWrapped("%s", filename.c_str()); + + ImGui::PushTextWrapPos(cursorX + buttonSize.x); + ImGui::Text("%s", filename.c_str()); + ImGui::PopTextWrapPos(); ImGui::PopID(); ImGui::EndGroup(); - ImGui::NextColumn(); + // Advance to next column + columnHeight = std::max(ImGui::GetCursorPosY() - cursorPos.y, columnHeight); + + ImGui::SetCursorPosX(((entryIdx + 1) % columnCount) * columnSize + padding); + if ((entryIdx + 1) % columnCount == 0) { + cursorPos.y = cursorPos.y + columnHeight; + ImGui::SetCursorPosY(cursorPos.y); + columnHeight = 0.0f; + } + else { + ImGui::SetCursorPosY(cursorPos.y); + } } @@ -514,4 +581,27 @@ namespace Atlas::Editor::UI { } + std::vector ContentBrowserWindow::GetSelectedPaths() { + + std::vector paths; + + int32_t entryIdx = 0; + for (const auto& directory : directories) { + // Ignore 'invisible' directories + if (directory->assetPath.at(0) == '.') + continue; + + if (selectionStorage.Contains(entryIdx++)) + paths.push_back(directory->path.string()); + } + + for (const auto& file : files) { + if (selectionStorage.Contains(entryIdx++)) + paths.push_back(file.path.string()); + } + + return paths; + + } + } \ No newline at end of file diff --git a/src/editor/ui/windows/ContentBrowserWindow.h b/src/editor/ui/windows/ContentBrowserWindow.h index d330e88e8..49bb7d158 100644 --- a/src/editor/ui/windows/ContentBrowserWindow.h +++ b/src/editor/ui/windows/ContentBrowserWindow.h @@ -28,11 +28,16 @@ namespace Atlas::Editor::UI { std::string currentDirectory = Loader::AssetLoader::GetAssetDirectory(); private: + struct ContentCopy { + std::vector paths; + }; + void RenderDirectoryControl(); void RenderDirectoryContent(); - void RenderContentEntry(const std::filesystem::path& path, const std::string& assetPath, ContentType contentType); + void RenderContentEntry(const std::filesystem::path& path, const std::string& assetPath, + ContentType contentType, int32_t entryIdx, int32_t columnCount, float columnSize, float& columnHeight); bool IsValidFileType(const std::string& filename); @@ -47,6 +52,8 @@ namespace Atlas::Editor::UI { bool TextInputPopup(const char* name, bool& isVisible, std::string& input); + std::vector GetSelectedPaths(); + int selectedFilter = -1; std::string nextDirectory; @@ -61,6 +68,8 @@ namespace Atlas::Editor::UI { JobGroup searchAndFilterJob{ JobPriority::Medium }; + ImGuiSelectionBasicStorage selectionStorage; + const float padding = 8.0f; const float iconSize = 64.f; const float itemSize = iconSize + 2.0f * padding; diff --git a/src/editor/ui/windows/LogWindow.cpp b/src/editor/ui/windows/LogWindow.cpp index 2014ff86c..19aaaca35 100644 --- a/src/editor/ui/windows/LogWindow.cpp +++ b/src/editor/ui/windows/LogWindow.cpp @@ -3,6 +3,8 @@ #include "Log.h" #include "../../Singletons.h" +#include + namespace Atlas::Editor::UI { void LogWindow::Render() { @@ -12,10 +14,17 @@ namespace Atlas::Editor::UI { auto darkMode = Singletons::config->darkMode; - static size_t entriesCount = 0; - auto entries = Atlas::Log::GetLatestEntries(10000); for (auto& entry : entries) { + + bool hasNewLine = entry.message.find_first_of('\n') != std::string::npos; + if (!hasNewLine) { + ImGui::TextUnformatted(""); + if (!ImGui::IsItemVisible()) + continue; + ImGui::SameLine(); + } + std::string logText; logText.append("[" + std::to_string(entry.time) + "] "); logText.append(entry.message); @@ -40,9 +49,9 @@ namespace Atlas::Editor::UI { ImGui::PopStyleColor(); } - if (entriesCount != entries.size()) { + if (logEntryCount != entries.size()) { + logEntryCount = entries.size(); ImGui::SetScrollHereY(1.0f); - entriesCount = entries.size(); } End(); diff --git a/src/editor/ui/windows/LogWindow.h b/src/editor/ui/windows/LogWindow.h index cb150c818..c6af70a7a 100644 --- a/src/editor/ui/windows/LogWindow.h +++ b/src/editor/ui/windows/LogWindow.h @@ -14,6 +14,9 @@ namespace Atlas::Editor::UI { private: std::string logSearch; + size_t logEntryCount = 0; + bool scroll = false; + }; } \ No newline at end of file diff --git a/src/editor/ui/windows/SceneWindow.cpp b/src/editor/ui/windows/SceneWindow.cpp index ebee824c9..bca125515 100644 --- a/src/editor/ui/windows/SceneWindow.cpp +++ b/src/editor/ui/windows/SceneWindow.cpp @@ -60,6 +60,10 @@ namespace Atlas::Editor::UI { if (inFocus && controlDown && ImGui::IsKeyPressed(ImGuiKey_S, false) && !isPlaying) { SaveScene(); } + + if (controlDown && playMaximized && isPlaying && ImGui::IsKeyPressed(ImGuiKey_Q, false)) { + StopPlaying(); + } } @@ -338,6 +342,7 @@ namespace Atlas::Editor::UI { auto& camera = cameraEntity.GetComponent(); + ImGui::Separator(); ImGui::Text("Editor camera"); ImGui::DragFloat("Exposure", &camera.exposure, 0.1f, 0.01f, 180.0f); @@ -346,6 +351,7 @@ namespace Atlas::Editor::UI { ImGui::DragFloat("Near plane", &camera.nearPlane, 0.01f, 0.01f, 10.0f); ImGui::DragFloat("Far plane", &camera.farPlane, 1.0f, 1.0f, 20000.0f); + ImGui::Separator(); ImGui::Text("Rendering scale"); ImGui::DragFloat("Resolution scale##Rendering", &resolutionScale, 0.01f, 0.1f, 1.0f); @@ -353,9 +359,15 @@ namespace Atlas::Editor::UI { if (Singletons::renderTarget->GetScalingFactor() != resolutionScale) Singletons::renderTarget->SetScalingFactor(resolutionScale); + ImGui::Separator(); ImGui::Text("Path traces samples"); ImGui::DragInt("Sample count", &Singletons::mainRenderer->pathTracingRenderer.realTimeSamplesPerFrame, 1, 1, 16); + ImGui::Separator(); + ImGui::Text("Playing"); + ImGui::Checkbox("Play maximized", &playMaximized); + ImGui::Checkbox("Show performance overlay", &perfOverlayMaximized); + ImGui::EndPopup(); } @@ -383,13 +395,17 @@ namespace Atlas::Editor::UI { if (ImGui::BeginMenu("GBuffer")) { menuItem("Base color", ViewportVisualization::GBufferBaseColor); menuItem("Roughness/Metalness/Ao", ViewportVisualization::GBufferRoughnessMetalnessAo); + menuItem("Emissive color", ViewportVisualization::GBufferEmissive); menuItem("Depth", ViewportVisualization::GBufferDepth); menuItem("Normals", ViewportVisualization::GBufferNormals); menuItem("Geometry normals", ViewportVisualization::GBufferGeometryNormals); menuItem("Velocity", ViewportVisualization::GBufferVelocity); + menuItem("Material index", ViewportVisualization::GBufferMaterialIdx); + menuItem("Stencil", ViewportVisualization::GBufferStencil); ImGui::EndMenu(); } + menuItem("Volumetrics", ViewportVisualization::Volumetrics); menuItem("Clouds", ViewportVisualization::Clouds); menuItem("Reflections", ViewportVisualization::Reflections); menuItem("SSS", ViewportVisualization::SSS); @@ -604,6 +620,10 @@ namespace Atlas::Editor::UI { isPlaying = true; + if (playMaximized) { + Notifications::Push({ "To stop playing, press Ctrl + Q" }); + } + } void SceneWindow::StopPlaying() { diff --git a/src/editor/ui/windows/SceneWindow.h b/src/editor/ui/windows/SceneWindow.h index 10c3b9686..d02287efb 100644 --- a/src/editor/ui/windows/SceneWindow.h +++ b/src/editor/ui/windows/SceneWindow.h @@ -65,6 +65,8 @@ namespace Atlas::Editor::UI { bool hasMainCamera = false; bool hasPlayer = false; bool isPlaying = false; + bool playMaximized = false; + bool perfOverlayMaximized = false; bool isActiveWindow = false; bool lockSelection = false; diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 357122628..32a15b277 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -3,7 +3,7 @@ if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) endif() project(AtlasEngine) -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 3.25) # Validate options ################################################################################ if (ATLAS_NO_APP AND ATLAS_EXPORT_MAIN) @@ -53,7 +53,7 @@ if(WIN32) set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SPIRV-Tools-opt volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator - unofficial::spirv-reflect glslang::SPIRV unofficial::joltphysics::Jolt + unofficial::spirv-reflect glslang::SPIRV Jolt::Jolt ${LUA_LIBRARIES} sol2 fsr2) endif() @@ -67,8 +67,8 @@ if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_MACOS_MVK -DVK_EXAMPLE_XCODE_GENERATED") set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static - volk::volk SPIRV-Tools-opt unofficial::joltphysics::Jolt fsr2 - GPUOpen::VulkanMemoryAllocator ${Vulkan_LIBRARIES} Vulkan::Headers + volk::volk SPIRV-Tools-opt Jolt::Jolt fsr2 + GPUOpen::VulkanMemoryAllocator Vulkan::Vulkan Vulkan::Headers unofficial::spirv-reflect glslang::SPIRV ${LUA_LIBRARIES} sol2) endif() @@ -84,16 +84,20 @@ if(UNIX AND NOT APPLE AND NOT ANDROID) set(ATLAS_ENGINE_COMPILE_DEFINITIONS ${ATLAS_ENGINE_COMPILE_DEFINITIONS} AE_OS_LINUX) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") - set (CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl") - - set(CMAKE_SKIP_BUILD_RPATH FALSE) - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - set(CMAKE_INSTALL_RPATH "./") - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -ldl") set(ATLAS_ENGINE_LIBS assimp::assimp SDL2::SDL2 SDL2::SDL2main SDL2::SDL2-static fsr2 - volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator unofficial::joltphysics::Jolt + volk::volk volk::volk_headers GPUOpen::VulkanMemoryAllocator Jolt::Jolt unofficial::spirv-reflect SPIRV-Tools-opt glslang::SPIRV ${LUA_LIBRARIES} sol2) + + if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 14.0) + message("USe gcc14!") + set(ATLAS_ENGINE_LIBS ${ATLAS_ENGINE_LIBS} stdc++exp) + elseif(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 12.0) + message("USe gcc12!") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --enable-libstdcxx-backtrace=yes") + set(ATLAS_ENGINE_LIBS ${ATLAS_ENGINE_LIBS} stdc++_libbacktrace) + endif() endif() # Due to lua bindings we need more addressable functions (but not for clang) diff --git a/src/engine/Clock.cpp b/src/engine/Clock.cpp index b965f2d52..4320a75e0 100644 --- a/src/engine/Clock.cpp +++ b/src/engine/Clock.cpp @@ -1,6 +1,7 @@ #include "Clock.h" #include +#include #include diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 8b22c8185..7ad49ba42 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -9,6 +9,7 @@ #include "texture/Texture.h" #include "input/KeyboardMap.h" #include "events/EventManager.h" +#include "raytracing/RayTracingManager.h" #include "jobsystem/JobSystem.h" #include "graphics/ShaderCompiler.h" @@ -117,10 +118,13 @@ namespace Atlas { Clock::Update(); Graphics::Profiler::BeginFrame(); + // First reset keyboard state before new events + Input::KeyboardMap::Update(); Events::EventManager::Update(); PipelineManager::Update(); Audio::AudioManager::Update(); Physics::ShapesManager::Update(); + RayTracing::RayTracingManager::Update(); } diff --git a/src/engine/Main.cpp b/src/engine/Main.cpp index 0a5f728fc..c24e9323b 100644 --- a/src/engine/Main.cpp +++ b/src/engine/Main.cpp @@ -28,7 +28,7 @@ int main(int argc, char* argv[]) { } #if defined(AE_OS_MACOS) && defined(AE_BINDLESS) - setenv("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "2", 1); + setenv("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1", 1); setenv("MVK_DEBUG", "0", 1); #elif defined(AE_OS_MACOS) && defined(AE_BINDLESS) setenv("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "0", 1); diff --git a/src/engine/Material.cpp b/src/engine/Material.cpp index 2f666ddca..00bf977c3 100644 --- a/src/engine/Material.cpp +++ b/src/engine/Material.cpp @@ -50,4 +50,10 @@ namespace Atlas { } + bool Material::HasEmissiveMap() const { + + return emissiveMap.IsValid(); + + } + } \ No newline at end of file diff --git a/src/engine/Material.h b/src/engine/Material.h index efbd4adb3..828b64d7a 100644 --- a/src/engine/Material.h +++ b/src/engine/Material.h @@ -24,6 +24,7 @@ namespace Atlas { bool HasMetalnessMap() const; bool HasAoMap() const; bool HasDisplacementMap() const; + bool HasEmissiveMap() const; std::string name; @@ -34,6 +35,7 @@ namespace Atlas { ResourceHandle metalnessMap; ResourceHandle aoMap; ResourceHandle displacementMap; + ResourceHandle emissiveMap; vec3 baseColor = vec3(1.0f); vec3 transmissiveColor = vec3(0.0f); @@ -57,6 +59,8 @@ namespace Atlas { bool twoSided = false; bool vertexColors = false; + vec2 uvAnimation = vec2(0.0f); + uint32_t uvChannel = 0; }; diff --git a/src/engine/Serializer.cpp b/src/engine/Serializer.cpp index 83bdbdcdb..a9e179f86 100644 --- a/src/engine/Serializer.cpp +++ b/src/engine/Serializer.cpp @@ -153,7 +153,7 @@ namespace Atlas { [&](JobData& data) { auto& mesh = meshes[data.idx]; - if (!mesh.IsLoaded()) return; + if (!mesh.IsLoaded() || mesh.IsGenerated()) return; Loader::MeshLoader::SaveMesh(mesh.Get(), mesh.GetResource()->path, true); }); @@ -164,7 +164,7 @@ namespace Atlas { for (const auto& mesh : meshes) { if (!mesh.IsLoaded()) continue; - if (!multithreaded) + if (!multithreaded && !mesh.IsGenerated()) Loader::MeshLoader::SaveMesh(mesh.Get(), mesh.GetResource()->path, true); for (const auto& material : mesh->data.materials) @@ -172,7 +172,7 @@ namespace Atlas { } for (const auto& [_, material] : materials) { - if (!material.IsLoaded()) continue; + if (!material.IsLoaded() || material.IsGenerated()) continue; Loader::MaterialLoader::SaveMaterial(material.Get(), material.GetResource()->path); } diff --git a/src/engine/audio/AudioSerializer.cpp b/src/engine/audio/AudioSerializer.cpp index 00c28272e..f291b3ecd 100644 --- a/src/engine/audio/AudioSerializer.cpp +++ b/src/engine/audio/AudioSerializer.cpp @@ -11,7 +11,7 @@ namespace Atlas::Audio { {"loop", p.loop}, }; - if (p.data.IsValid()) + if (p.data.IsValid() && !p.data.IsGenerated()) j["resourcePath"] = p.data.GetResource()->path; } diff --git a/src/engine/buffer/Buffer.cpp b/src/engine/buffer/Buffer.cpp index 267900c58..6f5fa27dd 100644 --- a/src/engine/buffer/Buffer.cpp +++ b/src/engine/buffer/Buffer.cpp @@ -161,6 +161,19 @@ namespace Atlas { } + void Buffer::Reset() { + + sizeInBytes = 0; + + if (multiBuffered) { + multiBuffer.reset(); + } + else { + buffer.reset(); + } + + } + void Buffer::Reallocate(void *data) { auto device = Graphics::GraphicsDevice::DefaultDevice; @@ -189,6 +202,7 @@ namespace Atlas { .usageFlags = usageFlags | VK_BUFFER_USAGE_TRANSFER_DST_BIT, .domain = hostAccessible ? Graphics::BufferDomain::Host : Graphics::BufferDomain::Device, .data = data, + .dataSize = elementCount * elementSize, .size = sizeInBytes, .dedicatedMemory = (usageFlags & BufferUsageBits::DedicatedMemoryBit) > 0, .priority = (usageFlags & BufferUsageBits::HighPriorityMemoryBit) > 0 ? 1.0f : 0.5f diff --git a/src/engine/buffer/Buffer.h b/src/engine/buffer/Buffer.h index 98b288a33..9a7bdadc7 100644 --- a/src/engine/buffer/Buffer.h +++ b/src/engine/buffer/Buffer.h @@ -147,6 +147,13 @@ namespace Atlas { */ size_t GetAlignedOffset(size_t elementIndex); + /** + * Returns the aligned offset for dynamic uniform buffer binding + * @param elementIndex The offset in elements + * @return The offset in bytes + */ + void Reset(); + protected: void Reallocate(void* data); diff --git a/src/engine/common/Image.h b/src/engine/common/Image.h index 736818ab7..942c4a337 100644 --- a/src/engine/common/Image.h +++ b/src/engine/common/Image.h @@ -7,7 +7,7 @@ #include #include - +#include namespace Atlas { diff --git a/src/engine/ecs/EntityManager.cpp b/src/engine/ecs/EntityManager.cpp index 11760cf16..ce13c4416 100644 --- a/src/engine/ecs/EntityManager.cpp +++ b/src/engine/ecs/EntityManager.cpp @@ -39,6 +39,8 @@ namespace Atlas { entities[pos] = EntityConfig::InvalidEntity; for (auto& poolData : pools.data) { + if (!poolData.storage) + continue; if (poolData.storage->Contains(entity)) poolData.storage->Erase(entity); } @@ -52,7 +54,7 @@ namespace Atlas { entities.clear(); destroyed.clear(); - pools.data.clear(); + pools.Clear(); } @@ -83,4 +85,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/ecs/EntityManager.h b/src/engine/ecs/EntityManager.h index 2ed672422..190cd07c9 100644 --- a/src/engine/ecs/EntityManager.h +++ b/src/engine/ecs/EntityManager.h @@ -200,7 +200,13 @@ namespace Atlas { * to create a copy instead of a reference. */ template - std::vector& GetComponents(); + std::vector& GetAll(); + + /* + * Returns the amount of components for a type Comp + */ + template + size_t GetCount(); template size_t SubscribeToTopic(const Topic topic, std::function function); @@ -296,12 +302,19 @@ namespace Atlas { } template - std::vector& EntityManager::GetComponents() { + std::vector& EntityManager::GetAll() { return pools.Get().GetAll(); } + template + size_t EntityManager::GetCount() { + + return pools.Get().GetCount(); + + } + template size_t EntityManager::SubscribeToTopic(const Topic topic, std::function function) { diff --git a/src/engine/ecs/Pool.h b/src/engine/ecs/Pool.h index 73b216bd6..31846cfc1 100644 --- a/src/engine/ecs/Pool.h +++ b/src/engine/ecs/Pool.h @@ -28,6 +28,8 @@ namespace Atlas { std::vector& GetAll(); + size_t GetCount() const; + size_t Subscribe(const Topic topic, std::function function); private: @@ -106,6 +108,13 @@ namespace Atlas { } + template + size_t Pool::GetCount() const { + + return components.size(); + + } + template size_t Pool::Subscribe(const Topic topic, std::function function) { diff --git a/src/engine/ecs/Pools.h b/src/engine/ecs/Pools.h index ddd5f24bd..4704fa64f 100644 --- a/src/engine/ecs/Pools.h +++ b/src/engine/ecs/Pools.h @@ -3,7 +3,7 @@ #include "Pool.h" #include "TypeIndex.h" -#include +#include #include #include @@ -18,14 +18,23 @@ namespace Atlas { template Pool& Get(); + + void Clear() { + + for (auto& poolData : data) { + poolData.storage.reset(); + } + + } private: struct PoolData { - uint64_t idx; - std::shared_ptr storage; + std::shared_ptr storage = nullptr; }; + + static constexpr uint64_t dataCount = 1024; - std::vector data; + std::array data; friend class EntityManager; @@ -36,19 +45,21 @@ namespace Atlas { Storage* storage = nullptr; auto idx = TypeIndex::Get(); - auto find = std::find_if(data.begin(), data.end(), [idx](const auto& poolData) { return idx == poolData.idx; }); + + AE_ASSERT(idx < dataCount && "Too many component types stored. Please increase pool capacity"); + auto& poolData = data[idx]; - if (find != data.end()) { + if (poolData.storage != nullptr) { - storage = find->storage.get(); + storage = poolData.storage.get(); } else { // https://stackoverflow.com/questions/15783342/should-i-use-c11-emplace-back-with-pointers-containters // Need shared_ptr here such that destructor of Pool is called, not destructor of Storage - data.emplace_back(PoolData{ idx, std::make_shared>() }); - storage = data.back().storage.get(); + poolData.storage = std::make_shared>(); + storage = poolData.storage.get(); } @@ -58,4 +69,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/ecs/Storage.cpp b/src/engine/ecs/Storage.cpp index 562a29e42..5cccddac0 100644 --- a/src/engine/ecs/Storage.cpp +++ b/src/engine/ecs/Storage.cpp @@ -1,5 +1,7 @@ #include "Storage.h" +#include + namespace Atlas { namespace ECS { diff --git a/src/engine/ecs/TypeIndex.h b/src/engine/ecs/TypeIndex.h index 24e2bafea..73fb8f28d 100644 --- a/src/engine/ecs/TypeIndex.h +++ b/src/engine/ecs/TypeIndex.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Atlas { @@ -28,7 +29,7 @@ namespace Atlas { private: static uint64_t Identifier() noexcept { - static uint64_t value = 0; + static std::atomic value = 0; return value++; } @@ -37,4 +38,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/graphics/ASBuilder.cpp b/src/engine/graphics/ASBuilder.cpp index fb41afbdb..4a76388c6 100644 --- a/src/engine/graphics/ASBuilder.cpp +++ b/src/engine/graphics/ASBuilder.cpp @@ -7,7 +7,7 @@ namespace Atlas { namespace Graphics { BLASDesc ASBuilder::GetBLASDescForTriangleGeometry(Ref vertexBuffer, Ref indexBuffer, - size_t vertexCount, size_t vertexSize, size_t indexSize, std::vector regions) { + size_t vertexCount, size_t vertexSize, size_t indexSize, std::span regions) { VkDeviceAddress vertexAddress = vertexBuffer->GetDeviceAddress(); VkDeviceAddress indexAddress = indexBuffer->GetDeviceAddress(); @@ -25,7 +25,7 @@ namespace Atlas { BLASDesc desc; - for (auto& region : regions) { + for (const auto& region : regions) { VkAccelerationStructureGeometryKHR geometry = {}; geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; @@ -49,7 +49,7 @@ namespace Atlas { } - void ASBuilder::BuildBLAS(std::vector> &blases) { + int32_t ASBuilder::BuildBLAS(std::span> blases, CommandList* commandList) { auto device = GraphicsDevice::DefaultDevice; @@ -70,7 +70,9 @@ namespace Atlas { auto scratchBuffer = device->CreateBuffer(scratchBufferDesc); Ref queryPool = nullptr; - if (compactionCount == blases.size()) { + // Only on a direct submission we can actually afford to wait for the queries, otherwise we can't + // afford the compaction if the BVH build is in frame + if (compactionCount == blases.size() && !commandList) { auto queryPoolDesc = QueryPoolDesc{ .queryType = VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, .queryCount = uint32_t(compactionCount) @@ -82,6 +84,7 @@ namespace Atlas { size_t batchSizeLimit = 256000000; std::vector batchIndices; + batchIndices.reserve(blases.size()); for (size_t i = 0; i < blases.size(); i++) { batchIndices.push_back(uint32_t(i)); @@ -93,29 +96,44 @@ namespace Atlas { queryPool->Reset(); } - BuildBLASBatch(batchIndices, blases, scratchBuffer, queryPool); + BuildBLASBatch(batchIndices, blases, scratchBuffer, queryPool, commandList); if (queryPool) { - CompactBLASBatch(batchIndices, blases, queryPool); + CompactBLASBatch(batchIndices, blases, queryPool, commandList); } batchIndices.clear(); batchSize = 0; + for (size_t j = 0; j <= i; j++) { + blases[j]->isBuilt = true; + } + + return int32_t(i + 1); + // With one commandlist it only makes sense to create one batch per frame + if (commandList) { + return int32_t(i + 1); + } + } } + return int32_t(blases.size()); + } - Ref ASBuilder::BuildTLAS(Ref &tlas, - std::vector &instances) { + Buffer* ASBuilder::BuildTLAS(Ref& tlas, + std::span instances, CommandList* commandList) { auto device = GraphicsDevice::DefaultDevice; - auto commandList = device->GetCommandList(GraphicsQueue); + bool immediateSubmission = commandList == nullptr; + if (!commandList) { + commandList = device->GetCommandList(GraphicsQueue); - commandList->BeginCommands(); + commandList->BeginCommands(); + } BufferDesc desc = { .usageFlags = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR @@ -124,9 +142,12 @@ namespace Atlas { .data = instances.data(), .size = sizeof(VkAccelerationStructureInstanceKHR) * instances.size(), }; - auto instanceBuffer = device->CreateBuffer(desc); + if (!instanceBuffer || instanceBuffer->size < desc.size) + instanceBuffer = device->CreateMultiBuffer(desc); + else + instanceBuffer->SetData(instances.data(), 0, desc.size); - tlas->Allocate(instanceBuffer->GetDeviceAddress(), uint32_t(instances.size()), false); + tlas->Allocate(instanceBuffer->GetCurrent()->GetDeviceAddress(), uint32_t(instances.size()), false); commandList->MemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); @@ -146,22 +167,27 @@ namespace Atlas { commandList->BuildTLAS(tlas, buildInfo); - commandList->EndCommands(); + if (immediateSubmission) { + commandList->EndCommands(); - device->SubmitCommandList(commandList); + device->SubmitCommandList(commandList); + } - return instanceBuffer; + return instanceBuffer->GetCurrent(); } - void ASBuilder::BuildBLASBatch(const std::vector &batchIndices, - std::vector> &blases, Ref& scratchBuffer, Ref& queryPool) { + void ASBuilder::BuildBLASBatch(const std::span &batchIndices, std::span> &blases, + Ref& scratchBuffer, Ref& queryPool, CommandList* commandList) { auto device = GraphicsDevice::DefaultDevice; - auto commandList = device->GetCommandList(GraphicsQueue, true); + bool immediateSubmission = commandList == nullptr; + if (!commandList) { + commandList = device->GetCommandList(GraphicsQueue, true); - commandList->BeginCommands(); + commandList->BeginCommands(); + } VkDeviceAddress scratchAddress = scratchBuffer->GetDeviceAddress(); @@ -178,7 +204,7 @@ namespace Atlas { commandList->BuildBLAS(blas, buildInfo); commandList->MemoryBarrier(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, - VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, + VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); @@ -189,20 +215,25 @@ namespace Atlas { } } - commandList->EndCommands(); + if (immediateSubmission) { + commandList->EndCommands(); - device->FlushCommandList(commandList); + device->FlushCommandList(commandList); + } } - void ASBuilder::CompactBLASBatch(const std::vector& batchIndices, - std::vector>& blases, Ref& queryPool) { + void ASBuilder::CompactBLASBatch(const std::span& batchIndices, + std::span>& blases, Ref& queryPool, CommandList* commandList) { auto device = GraphicsDevice::DefaultDevice; - auto commandList = device->GetCommandList(GraphicsQueue, true); + bool immediateSubmission = commandList == nullptr; + if (!commandList) { + commandList = device->GetCommandList(GraphicsQueue, true); - commandList->BeginCommands(); + commandList->BeginCommands(); + } std::vector compactSizes(batchIndices.size()); queryPool->GetResult(0, uint32_t(batchIndices.size()), batchIndices.size() * sizeof(size_t), @@ -239,9 +270,16 @@ namespace Atlas { } - commandList->EndCommands(); + commandList->MemoryBarrier(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR, + VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR); + + if (immediateSubmission) { + commandList->EndCommands(); - device->FlushCommandList(commandList); + device->FlushCommandList(commandList); + } } diff --git a/src/engine/graphics/ASBuilder.h b/src/engine/graphics/ASBuilder.h index fc0154894..1287288f0 100644 --- a/src/engine/graphics/ASBuilder.h +++ b/src/engine/graphics/ASBuilder.h @@ -5,6 +5,9 @@ #include "BLAS.h" #include "TLAS.h" #include "QueryPool.h" +#include "CommandList.h" + +#include namespace Atlas { @@ -23,18 +26,21 @@ namespace Atlas { ASBuilder() = default; BLASDesc GetBLASDescForTriangleGeometry(Ref vertexBuffer, Ref indexBuffer, - size_t vertexCount, size_t vertexSize, size_t indexSize, std::vector regions); + size_t vertexCount, size_t vertexSize, size_t indexSize, std::span regions); - void BuildBLAS(std::vector>& blases); + int32_t BuildBLAS(std::span> blases, CommandList* commandList = nullptr); - Ref BuildTLAS(Ref& tlas, std::vector& instances); + Buffer* BuildTLAS(Ref& tlas, std::span instances, CommandList* commandList = nullptr); private: - void BuildBLASBatch(const std::vector& batchIndices, - std::vector>& blases, Ref& scratchBuffer, Ref& queryPool); + void BuildBLASBatch(const std::span& batchIndices, std::span>& blases, + Ref& scratchBuffer, Ref& queryPool, CommandList* commandList); + + void CompactBLASBatch(const std::span& batchIndices, + std::span>& blases, Ref& queryPool, CommandList* commandList); - void CompactBLASBatch(const std::vector& batchIndices, - std::vector>& blases, Ref& queryPool); + Ref scratchBuffer = nullptr; + Ref instanceBuffer = nullptr; }; diff --git a/src/engine/graphics/BLAS.cpp b/src/engine/graphics/BLAS.cpp index f5eb7bc05..de6d92474 100644 --- a/src/engine/graphics/BLAS.cpp +++ b/src/engine/graphics/BLAS.cpp @@ -6,8 +6,8 @@ namespace Atlas { namespace Graphics { - BLAS::BLAS(GraphicsDevice* device, BLASDesc desc) : device(device), geometries(desc.geometries), - buildRanges(desc.buildRanges), flags(desc.flags) { + BLAS::BLAS(GraphicsDevice* device, BLASDesc desc) : isDynamic(desc.isDynamic), device(device), + geometries(desc.geometries), buildRanges(desc.buildRanges), flags(desc.flags) { buildGeometryInfo = {}; buildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; diff --git a/src/engine/graphics/BLAS.h b/src/engine/graphics/BLAS.h index 873f24e52..d44a9c4ce 100644 --- a/src/engine/graphics/BLAS.h +++ b/src/engine/graphics/BLAS.h @@ -4,6 +4,7 @@ #include "Buffer.h" #include +#include namespace Atlas { @@ -16,6 +17,8 @@ namespace Atlas { struct BLASDesc { VkBuildAccelerationStructureFlagsKHR flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + bool isDynamic = false; + std::vector geometries; std::vector buildRanges; }; @@ -41,6 +44,9 @@ namespace Atlas { VkDeviceAddress bufferDeviceAddress; + bool isDynamic = false; + std::atomic_bool isBuilt = false; + private: GraphicsDevice* device; diff --git a/src/engine/graphics/Buffer.cpp b/src/engine/graphics/Buffer.cpp index f93c85512..5940daf37 100644 --- a/src/engine/graphics/Buffer.cpp +++ b/src/engine/graphics/Buffer.cpp @@ -28,7 +28,7 @@ namespace Atlas { } if (desc.priority == 1.0f) - allocationCreateInfo.pool = memoryManager->hightPriorityBufferPool; + allocationCreateInfo.pool = memoryManager->highPriorityMemoryPool; if (desc.dedicatedMemory) allocationCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; @@ -42,7 +42,7 @@ namespace Atlas { &allocationCreateInfo, alignment, &buffer, &allocation, nullptr)) } - if (desc.data) SetData(desc.data, 0, desc.size); + if (desc.data) SetData(desc.data, 0, desc.dataSize > 0 ? desc.dataSize : desc.size); } diff --git a/src/engine/graphics/Buffer.h b/src/engine/graphics/Buffer.h index d6768eff2..48428b6ba 100644 --- a/src/engine/graphics/Buffer.h +++ b/src/engine/graphics/Buffer.h @@ -29,6 +29,8 @@ namespace Atlas { BufferHostAccess hostAccess = BufferHostAccess::Sequential; void* data = nullptr; + size_t dataSize = 0; + size_t size; size_t alignment = 0; diff --git a/src/engine/graphics/CommandList.cpp b/src/engine/graphics/CommandList.cpp index f5c2885ad..8a2020082 100644 --- a/src/engine/graphics/CommandList.cpp +++ b/src/engine/graphics/CommandList.cpp @@ -176,7 +176,7 @@ namespace Atlas { rpInfo.renderArea.extent = frameBuffer->extent; rpInfo.framebuffer = frameBuffer->frameBuffer; - std::vector clearValues; + clearValues.clear(); for (auto& attachment : renderPass->colorAttachments) { if (!attachment.isValid) continue; if (attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) { @@ -350,7 +350,7 @@ namespace Atlas { void CommandList::ClearAttachments() { - std::vector clearAttachments; + clearAttachments.clear(); VkClearRect clearRect = {}; if (swapChainInUse) { VkClearAttachment colorClear = {}, depthClear = {}; @@ -493,7 +493,7 @@ namespace Atlas { } - void CommandList::BindBuffers(const std::vector> &buffers, uint32_t set, uint32_t binding) { + void CommandList::BindBuffers(const std::span> buffers, uint32_t set, uint32_t binding) { AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); @@ -501,7 +501,8 @@ namespace Atlas { if (!buffers.size()) return; std::vector buffersPtr; - for (auto& buffer : buffers) { + buffersPtr.reserve(buffers.size()); + for (const auto& buffer : buffers) { AE_ASSERT(buffer->size > 0 && "Invalid buffer size"); AE_ASSERT(buffer->usageFlags & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT && "Only storage buffers support array bindings"); @@ -592,7 +593,7 @@ namespace Atlas { } - void CommandList::BindSampledImages(const std::vector>& images, uint32_t set, uint32_t binding) { + void CommandList::BindSampledImages(const std::span> images, uint32_t set, uint32_t binding) { AE_ASSERT(set < DESCRIPTOR_SET_COUNT && "Descriptor set not allowed for use"); AE_ASSERT(binding < BINDINGS_PER_DESCRIPTOR_SET && "The binding point is not allowed for use"); @@ -600,6 +601,7 @@ namespace Atlas { if (!images.size()) return; std::vector imagesPtr; + imagesPtr.reserve(images.size()); for (const auto& image : images) imagesPtr.push_back(image.get()); descriptorBindingData.ResetBinding(set, binding); @@ -743,8 +745,8 @@ namespace Atlas { } - void CommandList::PipelineBarrier(std::vector& imageBarriers, - std::vector& bufferBarriers, VkPipelineStageFlags srcStageMask, + void CommandList::PipelineBarrier(std::span imageBarriers, + std::span bufferBarriers, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask) { // Not so sure about the cost of these vector allocations on the heap diff --git a/src/engine/graphics/CommandList.h b/src/engine/graphics/CommandList.h index 27badaca7..cd5a12758 100644 --- a/src/engine/graphics/CommandList.h +++ b/src/engine/graphics/CommandList.h @@ -15,7 +15,9 @@ #include "../common/Ref.h" #include +#include #include +#include namespace Atlas { @@ -89,7 +91,7 @@ namespace Atlas { void BindBufferOffset(const Ref& buffer, size_t offset, uint32_t set, uint32_t binding); - void BindBuffers(const std::vector>& buffers, uint32_t set, uint32_t binding); + void BindBuffers(const std::span> buffers, uint32_t set, uint32_t binding); void BindBuffer(const Ref& buffer, uint32_t set, uint32_t binding); @@ -99,7 +101,7 @@ namespace Atlas { void BindImage(const Ref& image, const Ref& sampler, uint32_t set, uint32_t binding); - void BindSampledImages(const std::vector>& images, uint32_t set, uint32_t binding); + void BindSampledImages(const std::span> images, uint32_t set, uint32_t binding); void BindSampler(const Ref& sampler, uint32_t set, uint32_t binding); @@ -135,7 +137,7 @@ namespace Atlas { void PipelineBarrier(VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - void PipelineBarrier(std::vector& imageBarriers, std::vector& bufferBarriers, + void PipelineBarrier(std::span imageBarriers, std::span bufferBarriers, VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); @@ -326,6 +328,10 @@ namespace Atlas { std::vector semaphores; + // Resources that are used each frame where we don't want allocate memory each time + std::vector clearValues; + std::vector clearAttachments; + }; } diff --git a/src/engine/graphics/DescriptorSetLayout.cpp b/src/engine/graphics/DescriptorSetLayout.cpp index c18031f1a..c059fce9a 100644 --- a/src/engine/graphics/DescriptorSetLayout.cpp +++ b/src/engine/graphics/DescriptorSetLayout.cpp @@ -9,9 +9,12 @@ namespace Atlas { DescriptorSetLayout::DescriptorSetLayout(GraphicsDevice* device, const DescriptorSetLayoutDesc& desc) : device(device) { + // We need this flag to support arrays of descriptors even in non-bindless mode (e.g. array of textures, where only one index is bound) + VkDescriptorBindingFlags defaultBindingFlags = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + bindings.resize(desc.bindingCount); layoutBindings.resize(desc.bindingCount); - layoutBindingFlags.resize(desc.bindingCount); + layoutBindingFlags.resize(desc.bindingCount, defaultBindingFlags); bool bindlessAllowed = true; bool bindlessNeeded = false; @@ -73,13 +76,13 @@ namespace Atlas { layoutBindingFlags[i] = bindingFlags; } + } - extendedInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO; - extendedInfo.bindingCount = desc.bindingCount; - extendedInfo.pBindingFlags = desc.bindingCount ? layoutBindingFlags.data() : VK_NULL_HANDLE; + extendedInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO; + extendedInfo.bindingCount = desc.bindingCount; + extendedInfo.pBindingFlags = desc.bindingCount ? layoutBindingFlags.data() : VK_NULL_HANDLE; - setInfo.pNext = &extendedInfo; - } + setInfo.pNext = &extendedInfo; VK_CHECK(vkCreateDescriptorSetLayout(device->device, &setInfo, nullptr, &layout)) @@ -140,4 +143,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/graphics/Framebuffer.cpp b/src/engine/graphics/Framebuffer.cpp index 05d2e661a..7ea26558f 100644 --- a/src/engine/graphics/Framebuffer.cpp +++ b/src/engine/graphics/Framebuffer.cpp @@ -50,11 +50,12 @@ namespace Atlas { void FrameBuffer::Refresh() { - std::vector imageViews; - const auto& rpColorAttachments = renderPass->colorAttachments; const auto& rpDepthAttachment = renderPass->depthAttachment; + imageViews.reserve(MAX_COLOR_ATTACHMENTS + 1); + imageViews.clear(); + // NOTE: The frame buffer requires to have an image view for each attachment in the render pass // This doesn't mean we need to write to all attachments. The writing to attachments is being configured // in the pipeline setup and is based on which color attachments the user specified in this frame buffer diff --git a/src/engine/graphics/Framebuffer.h b/src/engine/graphics/Framebuffer.h index 2a925a7f6..06363f92b 100644 --- a/src/engine/graphics/Framebuffer.h +++ b/src/engine/graphics/Framebuffer.h @@ -59,6 +59,8 @@ namespace Atlas { FrameBufferAttachment colorAttachments[MAX_COLOR_ATTACHMENTS]; FrameBufferAttachment depthAttachment; + std::vector imageViews; + VkExtent2D extent; uint32_t layers; diff --git a/src/engine/graphics/GraphicsDevice.cpp b/src/engine/graphics/GraphicsDevice.cpp index 2a6ba5d07..e9d734de6 100644 --- a/src/engine/graphics/GraphicsDevice.cpp +++ b/src/engine/graphics/GraphicsDevice.cpp @@ -434,7 +434,7 @@ namespace Atlas { VK_CHECK(vkQueueSubmit(queue.queue, 1, &submit, cmd->fence)) queue.Unlock(); - VK_CHECK(vkWaitForFences(device, 1, &cmd->fence, true, 9999999999)); + VK_CHECK(vkWaitForFences(device, 1, &cmd->fence, true, 30000000000)); VK_CHECK(vkResetFences(device, 1, &cmd->fence)); // Is submitted now and must be unlocked diff --git a/src/engine/graphics/Image.cpp b/src/engine/graphics/Image.cpp index 19e251c6d..9fb083fda 100644 --- a/src/engine/graphics/Image.cpp +++ b/src/engine/graphics/Image.cpp @@ -39,7 +39,9 @@ namespace Atlas { VMA_MEMORY_USAGE_AUTO; // Seems to be faster to just use that pool everywhere for all images. // My guess is it is actually faster since the memory doesn't change constantly (not many new allocations) - allocationCreateInfo.pool = memoryManager->hightPriorityBufferPool; + + if (desc.dedicatedMemoryPool != nullptr) + allocationCreateInfo.pool = *desc.dedicatedMemoryPool; VK_CHECK(vmaCreateImage(memoryManager->allocator, &imageInfo, &allocationCreateInfo, &image, &allocation, nullptr)) @@ -68,22 +70,19 @@ namespace Atlas { imageViewInfo.subresourceRange.levelCount = mipLevels; } VK_CHECK(vkCreateImageView(device->device, &imageViewInfo, nullptr, &view)) - - // This will just duplicate the view for single-layered images, don't care for now - if (desc.type != ImageType::ImageCube) { - attachmentViews.resize(layers); - for (uint32_t i = 0; i < layers; i++) { - imageViewInfo.subresourceRange.baseArrayLayer = i; - imageViewInfo.subresourceRange.layerCount = 1; - // For attachments, we only want one mip level - imageViewInfo.subresourceRange.levelCount = 1; - VK_CHECK(vkCreateImageView(device->device, &imageViewInfo, nullptr, &attachmentViews[i])) - } + + if (desc.type == ImageType::ImageCube) { + // We need to set it back to a 2D view type when rendering to each cubemap face + imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; } - else { - // A cubemap can only have one valid view - attachmentViews.resize(1); - VK_CHECK(vkCreateImageView(device->device, &imageViewInfo, nullptr, &attachmentViews[0])) + + attachmentViews.resize(layers); + for (uint32_t i = 0; i < layers; i++) { + imageViewInfo.subresourceRange.baseArrayLayer = i; + imageViewInfo.subresourceRange.layerCount = 1; + // For attachments, we only want one mip level + imageViewInfo.subresourceRange.levelCount = 1; + VK_CHECK(vkCreateImageView(device->device, &imageViewInfo, nullptr, &attachmentViews[i])) } if (desc.data) SetData(desc.data, 0, 0, 0, desc.width, desc.height, desc.depth, 0, desc.layers); diff --git a/src/engine/graphics/Image.h b/src/engine/graphics/Image.h index 21a9c7941..0f95a17bf 100644 --- a/src/engine/graphics/Image.h +++ b/src/engine/graphics/Image.h @@ -43,7 +43,7 @@ namespace Atlas { void* data = nullptr; - bool dedicatedMemory = false; + VmaPool* dedicatedMemoryPool = nullptr; }; struct ImageAllocation { diff --git a/src/engine/graphics/Instance.cpp b/src/engine/graphics/Instance.cpp index 1cd1ef07e..61d90ab97 100644 --- a/src/engine/graphics/Instance.cpp +++ b/src/engine/graphics/Instance.cpp @@ -8,6 +8,10 @@ #include #include +#if !defined(__clang__) && defined(AE_BUILDTYPE_DEBUG) +#include +#endif + namespace Atlas { namespace Graphics { @@ -92,8 +96,9 @@ namespace Atlas { #ifdef AE_BUILDTYPE_DEBUG validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; - validationFeatures.enabledValidationFeatureCount = std::size(enables); - validationFeatures.pEnabledValidationFeatures = enables; + // This doesn't seem to work anymore with newer VulkanSDKs on Nvidia hardware + //validationFeatures.enabledValidationFeatureCount = std::size(enables); + //validationFeatures.pEnabledValidationFeatures = enables; structureChainBuilder.Append(validationFeatures); #endif @@ -259,6 +264,7 @@ namespace Atlas { VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT; + createInfo.pfnUserCallback = DebugCallback; createInfo.pUserData = static_cast(const_cast(name.c_str())); return createInfo; @@ -315,6 +321,10 @@ namespace Atlas { case Log::Type::TYPE_ERROR: Log::Error(pCallbackData->pMessage, logSeverity); break; } +#if !defined(__clang__) && defined(AE_BUILDTYPE_DEBUG) + output.append("\nStack trace:\n" + std::to_string(std::stacktrace::current())); +#endif + #ifndef AE_BUILDTYPE_RELEASE if (logSeverity == Log::Severity::SEVERITY_HIGH) throw std::runtime_error(output); diff --git a/src/engine/graphics/MemoryManager.cpp b/src/engine/graphics/MemoryManager.cpp index 7e7bafcd1..df3f5367c 100644 --- a/src/engine/graphics/MemoryManager.cpp +++ b/src/engine/graphics/MemoryManager.cpp @@ -46,8 +46,19 @@ namespace Atlas { VmaPoolCreateInfo poolCreateInfo = {}; poolCreateInfo.memoryTypeIndex = memTypeIndex; + VK_CHECK(vmaCreatePool(allocator, &poolCreateInfo, &highPriorityMemoryPool)); - VK_CHECK(vmaCreatePool(allocator, &poolCreateInfo, &hightPriorityBufferPool)); + VkExtent3D imageExtent{ 1, 1, 1 }; + VkImageCreateInfo imageCreateInfo = Initializers::InitImageCreateInfo(VK_FORMAT_R16_SFLOAT, 0, imageExtent); + imageCreateInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + res = vmaFindMemoryTypeIndexForImageInfo(allocator, &imageCreateInfo, + &sampleAllocCreateInfo, &memTypeIndex); + + poolCreateInfo.memoryTypeIndex = memTypeIndex; + VK_CHECK(vmaCreatePool(allocator, &poolCreateInfo, &highPriorityRenderTargetPool)); vkGetPhysicalDeviceProperties(device->physicalDevice, &deviceProperties); @@ -61,7 +72,8 @@ namespace Atlas { DestroyAllImmediate(); - vmaDestroyPool(allocator, hightPriorityBufferPool); + vmaDestroyPool(allocator, highPriorityMemoryPool); + vmaDestroyPool(allocator, highPriorityRenderTargetPool); vmaDestroyAllocator(allocator); } diff --git a/src/engine/graphics/MemoryManager.h b/src/engine/graphics/MemoryManager.h index 3a112c0b6..cbc227967 100644 --- a/src/engine/graphics/MemoryManager.h +++ b/src/engine/graphics/MemoryManager.h @@ -87,7 +87,8 @@ namespace Atlas { void DestroyAllImmediate(); VmaAllocator allocator; - VmaPool hightPriorityBufferPool; + VmaPool highPriorityMemoryPool; + VmaPool highPriorityRenderTargetPool; VkPhysicalDeviceProperties deviceProperties; diff --git a/src/engine/graphics/Profiler.cpp b/src/engine/graphics/Profiler.cpp index 6f6b16673..eb14ab2e6 100644 --- a/src/engine/graphics/Profiler.cpp +++ b/src/engine/graphics/Profiler.cpp @@ -207,7 +207,7 @@ namespace Atlas { ThreadData data; data.name = name; - auto idx = threadHistory.historyIdx > 0 ? threadHistory.historyIdx - 1 : 64; + auto idx = threadHistory.historyIdx > 0 ? threadHistory.historyIdx - 1 : 63; data.queries = threadHistory.history[idx]; if (order != OrderBy::CHRONO) diff --git a/src/engine/graphics/Shader.cpp b/src/engine/graphics/Shader.cpp index 094858651..d14c723b6 100644 --- a/src/engine/graphics/Shader.cpp +++ b/src/engine/graphics/Shader.cpp @@ -69,6 +69,10 @@ namespace Atlas { macros.push_back("AE_BINDLESS"); } + if (device->support.shaderFloat16) { + macros.push_back("AE_HALF_FLOAT"); + } + #ifdef AE_OS_MACOS macros.push_back("AE_OS_MACOS"); #endif diff --git a/src/engine/input/KeyboardMap.cpp b/src/engine/input/KeyboardMap.cpp index 300ed284b..0133670e8 100644 --- a/src/engine/input/KeyboardMap.cpp +++ b/src/engine/input/KeyboardMap.cpp @@ -14,6 +14,13 @@ namespace Atlas::Input { + } + + void KeyboardMap::Update() { + + for (auto& [keyCode, state] : keyMap) + state.repeat = true; + } uint8_t KeyboardMap::GetKeyState(Keycode code) { diff --git a/src/engine/input/KeyboardMap.h b/src/engine/input/KeyboardMap.h index 263d7c25f..91f438a34 100644 --- a/src/engine/input/KeyboardMap.h +++ b/src/engine/input/KeyboardMap.h @@ -13,6 +13,8 @@ namespace Atlas::Input { static void Shutdown(); + static void Update(); + static uint8_t GetKeyState(Keycode keyCode); static bool IsKeyPressed(Keycode keyCode, bool repeat = true); diff --git a/src/engine/jobsystem/Job.h b/src/engine/jobsystem/Job.h index 59c789d63..5bc1c0d0a 100644 --- a/src/engine/jobsystem/Job.h +++ b/src/engine/jobsystem/Job.h @@ -16,6 +16,9 @@ namespace Atlas { struct JobData { int32_t idx = 0; + int32_t workerIdx = 0; + JobPriority priority; + void* userData = nullptr; }; @@ -27,6 +30,15 @@ namespace Atlas { std::function function; void* userData = nullptr; + + inline JobData GetJobData(int32_t workerIdx = 0) const { + return JobData { + .idx = idx, + .workerIdx = 0, + .priority = priority, + .userData = userData + }; + } }; } \ No newline at end of file diff --git a/src/engine/jobsystem/JobSemaphore.h b/src/engine/jobsystem/JobSemaphore.h new file mode 100644 index 000000000..c6a5c6e45 --- /dev/null +++ b/src/engine/jobsystem/JobSemaphore.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace Atlas { + + class JobSignal { + + public: + inline void Reset() { counter.store(0); } + + inline void Release() { counter.fetch_add(1); } + + inline bool TryAquire() { return counter.load() > 0; } + + private: + std::atomic_int counter = 0; + + }; + +} \ No newline at end of file diff --git a/src/engine/jobsystem/JobSystem.cpp b/src/engine/jobsystem/JobSystem.cpp index b0874e606..bab9b45f5 100644 --- a/src/engine/jobsystem/JobSystem.cpp +++ b/src/engine/jobsystem/JobSystem.cpp @@ -10,6 +10,9 @@ #include #endif +// Use this macro to make the job system single threaded +// #define JOBS_SINGLE_THREADED + namespace Atlas { PriorityPool JobSystem::priorityPools[static_cast(JobPriority::Count)]; @@ -28,7 +31,7 @@ namespace Atlas { // Need to set our own main thread priority, otherwise we will loose when in contention with other threads #ifdef AE_OS_WINDOWS auto threadHandle = GetCurrentThread(); - success = SetThreadPriority(threadHandle, THREAD_PRIORITY_HIGHEST) > 0; + success = SetThreadPriority(threadHandle, THREAD_PRIORITY_TIME_CRITICAL) > 0; #endif #if defined(AE_OS_MACOS) || defined(AE_OS_LINUX) auto maxPriority = sched_get_priority_max(SCHED_RR); @@ -53,27 +56,30 @@ namespace Atlas { void JobSystem::Execute(JobGroup& group, std::function func, void* userData) { - auto& priorityPool = priorityPools[static_cast(group.priority)]; - group.counter.fetch_add(1); - Job job = { .priority = group.priority, .counter = &group.counter, - .function = func, + .function = std::move(func), .userData = userData }; +#ifdef JOBS_SINGLE_THREADED + auto jobData = job.GetJobData(); + job.function(jobData); + return; +#endif + + auto& priorityPool = priorityPools[static_cast(group.priority)]; + group.counter.fetch_add(1); + auto& worker = priorityPool.GetNextWorker(); worker.queue.Push(job); - worker.semaphore.release(); + worker.signal.Notify(); } void JobSystem::ExecuteMultiple(JobGroup& group, int32_t count, std::function func, void* userData) { - auto& priorityPool = priorityPools[static_cast(group.priority)]; - group.counter += count; - Job job = { .priority = group.priority, .counter = &group.counter, @@ -81,13 +87,25 @@ namespace Atlas { .userData = userData }; +#ifdef JOBS_SINGLE_THREADED + for (size_t i = 0; i < count; i++) { + job.idx = int32_t(i); + auto jobData = job.GetJobData(); + job.function(jobData); + } + return; +#endif + + auto& priorityPool = priorityPools[static_cast(group.priority)]; + group.counter += count; + if (count <= priorityPool.workerCount) { for (int32_t i = 0; i < count; i++) { auto& worker = priorityPool.GetNextWorker(); job.idx = i; worker.queue.Push(job); - worker.semaphore.release(); + worker.signal.Notify(); } return; } @@ -110,7 +128,7 @@ namespace Atlas { auto& worker = priorityPool.GetNextWorker(); worker.queue.PushMultiple(jobs); - worker.semaphore.release(); + worker.signal.Notify(); jobs.clear(); remainingJobs -= jobsToPush; @@ -118,8 +136,27 @@ namespace Atlas { } + void JobSystem::Wait(JobSignal& signal, JobPriority priority) { + +#ifdef JOBS_SINGLE_THREADED + return; +#endif + + auto& priorityPool = priorityPools[static_cast(priority)]; + + while (!signal.TryAquire()) { + auto& worker = priorityPool.GetNextWorker(); + priorityPool.Work(worker.workerId); + } + + } + void JobSystem::Wait(JobGroup& group) { +#ifdef JOBS_SINGLE_THREADED + return; +#endif + if (!group.HasFinished()) { auto& priorityPool = priorityPools[static_cast(group.priority)]; @@ -156,4 +193,11 @@ namespace Atlas { } + int32_t JobSystem::GetWorkerCount(const JobPriority priority) { + + const auto& priorityPool = priorityPools[static_cast(priority)]; + return priorityPool.workerCount; + + } + } \ No newline at end of file diff --git a/src/engine/jobsystem/JobSystem.h b/src/engine/jobsystem/JobSystem.h index e028c12d7..0a05fe743 100644 --- a/src/engine/jobsystem/JobSystem.h +++ b/src/engine/jobsystem/JobSystem.h @@ -10,14 +10,16 @@ #include "Job.h" #include "JobGroup.h" +#include "JobSemaphore.h" #include "PriorityPool.h" namespace Atlas { + // Idea: Keep 8 threads to the high priority pool and main thread, split the rest in two struct JobSystemConfig { int32_t highPriorityThreadCount = int32_t(std::thread::hardware_concurrency()) - 1; - int32_t mediumPriorityThreadCount = int32_t(std::thread::hardware_concurrency()) - 3; - int32_t lowPriorityThreadCount = int32_t(std::thread::hardware_concurrency()) - 4; + int32_t mediumPriorityThreadCount = std::max(1, std::max(int32_t(std::thread::hardware_concurrency()) - 8, int32_t(std::thread::hardware_concurrency()) / 2) / 2); + int32_t lowPriorityThreadCount = std::max(1, std::max(int32_t(std::thread::hardware_concurrency()) - 8, int32_t(std::thread::hardware_concurrency()) / 2) / 2); }; class JobSystem { @@ -33,11 +35,15 @@ namespace Atlas { static void ExecuteMultiple(JobGroup& group, int32_t count, std::function func, void* userData = nullptr); + static void Wait(JobSignal& signal, JobPriority priority); + static void Wait(JobGroup& group); static void WaitSpin(JobGroup& group); static void WaitAll(); + + static int32_t GetWorkerCount(const JobPriority priority); private: static PriorityPool priorityPools[static_cast(JobPriority::Count)]; diff --git a/src/engine/jobsystem/PriorityPool.cpp b/src/engine/jobsystem/PriorityPool.cpp index 87ab6f9a5..87567d5b1 100644 --- a/src/engine/jobsystem/PriorityPool.cpp +++ b/src/engine/jobsystem/PriorityPool.cpp @@ -15,9 +15,11 @@ namespace Atlas { for (int32_t i = 0; i < workerCount; i++) { workers[i].Start([&](Worker& worker) { while (!shutdown) { - worker.semaphore.acquire(); + worker.signal.Wait(); + Work(worker.workerId); } + worker.quit = true; }); } @@ -27,25 +29,16 @@ namespace Atlas { shutdown = true; for (auto& worker : workers) { - worker.semaphore.release(); + // Try to get it to quit + while(!worker.quit) { + worker.signal.Notify(); + std::this_thread::yield(); + } worker.thread.join(); } } - void PriorityPool::Work(int32_t workerId) { - - auto& worker = workers[workerId]; - worker.Work(); - - for (int32_t i = 1; i < workerCount; i++) { - auto stealIdx = (-i + workerId + workerCount) % workerCount; - auto& stealFrom = workers[stealIdx]; - stealFrom.Work(); - } - - } - Worker& PriorityPool::GetNextWorker() { auto counter = workerCounter++; @@ -60,4 +53,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/jobsystem/PriorityPool.h b/src/engine/jobsystem/PriorityPool.h index 51c9efd6d..56cc0181b 100644 --- a/src/engine/jobsystem/PriorityPool.h +++ b/src/engine/jobsystem/PriorityPool.h @@ -15,12 +15,23 @@ namespace Atlas { void Shutdown(); - void Work(int32_t workerId); - Worker& GetNextWorker(); std::vector& GetAllWorkers(); + inline void Work(int32_t workerId) { + + auto& worker = workers[workerId]; + worker.Work(); + + for (int32_t i = 1; i < workerCount; i++) { + auto stealIdx = (-i + workerId + workerCount) % workerCount; + auto& stealFrom = workers[stealIdx]; + stealFrom.Work(); + } + + } + JobPriority priority; int32_t workerCount; std::atomic_uint32_t spinCounter = 0; diff --git a/src/engine/jobsystem/Signal.h b/src/engine/jobsystem/Signal.h new file mode 100644 index 000000000..accd675b1 --- /dev/null +++ b/src/engine/jobsystem/Signal.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace Atlas { + + // This whole construct is here to avoid undefined behaviour of the semaphore when released more than once before one acquire + class Signal { + + public: + inline void Notify() { + + bool expected = false; + if (condition.compare_exchange_strong(expected, true)) { + semaphore.release(); + } + + } + + inline void Wait() { + + semaphore.acquire(); + condition = false; + + } + + private: + std::atomic_bool condition {false}; + std::binary_semaphore semaphore {0}; + + }; + +} \ No newline at end of file diff --git a/src/engine/jobsystem/ThreadSafeJobQueue.cpp b/src/engine/jobsystem/ThreadSafeJobQueue.cpp index f5da60ea8..e32a2ac6b 100644 --- a/src/engine/jobsystem/ThreadSafeJobQueue.cpp +++ b/src/engine/jobsystem/ThreadSafeJobQueue.cpp @@ -2,6 +2,13 @@ namespace Atlas { + bool ThreadSafeJobQueue::Empty() { + + std::scoped_lock lock(mutex); + return jobs.empty(); + + } + void ThreadSafeJobQueue::Push(const Job& job) { std::scoped_lock lock(mutex); diff --git a/src/engine/jobsystem/ThreadSafeJobQueue.h b/src/engine/jobsystem/ThreadSafeJobQueue.h index 1e3053473..159a78830 100644 --- a/src/engine/jobsystem/ThreadSafeJobQueue.h +++ b/src/engine/jobsystem/ThreadSafeJobQueue.h @@ -14,6 +14,8 @@ namespace Atlas { public: ThreadSafeJobQueue() = default; + bool Empty(); + void Push(const Job& job); void PushMultiple(const std::vector& jobs); diff --git a/src/engine/jobsystem/Worker.cpp b/src/engine/jobsystem/Worker.cpp index e373b4bfd..26e684b26 100644 --- a/src/engine/jobsystem/Worker.cpp +++ b/src/engine/jobsystem/Worker.cpp @@ -9,7 +9,6 @@ #include #endif - namespace Atlas { Worker::Worker(int32_t workerId, JobPriority priority) : workerId(workerId), priority(priority) { @@ -31,10 +30,12 @@ namespace Atlas { #ifdef AE_OS_WINDOWS HANDLE threadHandle = static_cast(thread.native_handle()); switch (priority) { - case JobPriority::High: success &= SetThreadPriority(threadHandle, THREAD_PRIORITY_HIGHEST); break; - case JobPriority::Low: success &= SetThreadPriority(threadHandle, THREAD_PRIORITY_LOWEST); break; - default: break; - } + case JobPriority::High: success &= SetThreadPriority(threadHandle, THREAD_PRIORITY_TIME_CRITICAL) > 0; break; + case JobPriority::Low: success &= SetThreadPriority(threadHandle, THREAD_PRIORITY_IDLE) > 0; break; + default: success &= SetThreadPriority(threadHandle, THREAD_PRIORITY_NORMAL) > 0; break; + } + std::wstring threadName = L"Worker " + std::to_wstring(workerId) + L" priority " + std::to_wstring(static_cast(priority)); + SetThreadDescription(threadHandle, threadName.c_str()); #endif #if defined(AE_OS_MACOS) || defined(AE_OS_LINUX) auto minPriority = sched_get_priority_min(SCHED_RR); @@ -54,4 +55,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/jobsystem/Worker.h b/src/engine/jobsystem/Worker.h index 40dde4253..2b1777b2c 100644 --- a/src/engine/jobsystem/Worker.h +++ b/src/engine/jobsystem/Worker.h @@ -2,10 +2,10 @@ #include "../System.h" +#include "Signal.h" #include "ThreadSafeJobQueue.h" #include -#include namespace Atlas { @@ -36,7 +36,9 @@ namespace Atlas { JobPriority priority; std::thread thread; - std::binary_semaphore semaphore{0}; + std::atomic_bool quit = false; + + Signal signal; ThreadSafeJobQueue queue; private: @@ -44,6 +46,7 @@ namespace Atlas { JobData data = { .idx = job.idx, + .workerIdx = workerId, .userData = job.userData }; @@ -55,4 +58,4 @@ namespace Atlas { }; -} \ No newline at end of file +} diff --git a/src/engine/lighting/Fog.h b/src/engine/lighting/Fog.h index df6df75ed..9861b7b23 100644 --- a/src/engine/lighting/Fog.h +++ b/src/engine/lighting/Fog.h @@ -27,6 +27,7 @@ namespace Atlas { float ambientFactor = 0.01f; bool rayMarching = true; + bool localLights = false; int32_t rayMarchStepCount = 10; float volumetricIntensity = 1.0f; diff --git a/src/engine/lighting/IrradianceVolume.cpp b/src/engine/lighting/IrradianceVolume.cpp index 781141255..259404c76 100644 --- a/src/engine/lighting/IrradianceVolume.cpp +++ b/src/engine/lighting/IrradianceVolume.cpp @@ -1,5 +1,7 @@ #include "IrradianceVolume.h" +#include + namespace Atlas { namespace Lighting { @@ -141,16 +143,25 @@ namespace Atlas { probeCount.x * probeCount.y * probeCount.z * cascadeCount); irradianceArray0 = Texture::Texture2DArray(irrRes.x, irrRes.y, probeCount.y * cascadeCount, - VK_FORMAT_A2B10G10R10_UNORM_PACK32, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_A2B10G10R10_UNORM_PACK32, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); momentsArray0 = Texture::Texture2DArray(momRes.x, momRes.y, probeCount.y * cascadeCount, - VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); irradianceArray1 = Texture::Texture2DArray(irrRes.x, irrRes.y, probeCount.y * cascadeCount, - VK_FORMAT_A2B10G10R10_UNORM_PACK32, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_A2B10G10R10_UNORM_PACK32, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); momentsArray1 = Texture::Texture2DArray(momRes.x, momRes.y, probeCount.y * cascadeCount, - VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + + probeDebugMaterial = CreateRef(); + probeDebugActiveMaterial = CreateRef(); + probeDebugInactiveMaterial = CreateRef(); + probeDebugOffsetMaterial = CreateRef(); + + probeDebugActiveMaterial->emissiveColor = vec3(0.0f, 1.0f, 0.0f); + probeDebugInactiveMaterial->emissiveColor = vec3(1.0f, 0.0f, 0.0f); + probeDebugOffsetMaterial->emissiveColor = vec3(0.0f, 0.0f, 1.0f); SwapTextures(); ClearProbes(irrRes, momRes, probeCount, cascadeCount); diff --git a/src/engine/lighting/IrradianceVolume.h b/src/engine/lighting/IrradianceVolume.h index bef1f6441..4b8a7f4e3 100644 --- a/src/engine/lighting/IrradianceVolume.h +++ b/src/engine/lighting/IrradianceVolume.h @@ -55,6 +55,12 @@ namespace Atlas { Buffer::Buffer probeStateBuffer; Buffer::Buffer historyProbeStateBuffer; + // Used for debugging + Ref probeDebugMaterial; + Ref probeDebugActiveMaterial; + Ref probeDebugInactiveMaterial; + Ref probeDebugOffsetMaterial; + private: void FillRayBuffers(); diff --git a/src/engine/lighting/LightingSerializer.cpp b/src/engine/lighting/LightingSerializer.cpp index 10c889de1..2a8f88ef4 100644 --- a/src/engine/lighting/LightingSerializer.cpp +++ b/src/engine/lighting/LightingSerializer.cpp @@ -31,7 +31,7 @@ namespace Atlas::Lighting { {"position", p.GetPosition()} }; - if (p.cubemap.IsValid()) { + if (p.cubemap.IsValid() && !p.cubemap.IsGenerated()) { j["cubemapPath"] = p.cubemap.GetResource()->path; } } @@ -80,6 +80,7 @@ namespace Atlas::Lighting { {"heightFalloff", p.heightFalloff}, {"scatteringAnisotropy", p.scatteringAnisotropy}, {"rayMarching", p.rayMarching}, + {"localLights", p.localLights}, {"rayMarchStepCount", p.rayMarchStepCount}, {"volumetricIntensity", p.volumetricIntensity}, }; @@ -98,6 +99,8 @@ namespace Atlas::Lighting { j.at("rayMarching").get_to(p.rayMarching); j.at("rayMarchStepCount").get_to(p.rayMarchStepCount); j.at("volumetricIntensity").get_to(p.volumetricIntensity); + + try_get_json(j, "localLights", p.localLights); } void to_json(json& j, const IrradianceVolume& p) { @@ -158,7 +161,9 @@ namespace Atlas::Lighting { {"useShadowMap", p.useShadowMap}, {"useNormalMaps", p.useNormalMaps}, {"opacityCheck", p.opacityCheck}, - {"halfResolution", p.halfResolution} + {"halfResolution", p.halfResolution}, + {"lightSampleCount", p.lightSampleCount}, + {"sampleCount", p.sampleCount}, }; } @@ -178,6 +183,8 @@ namespace Atlas::Lighting { j.at("opacityCheck").get_to(p.opacityCheck); try_get_json(j, "halfResolution", p.halfResolution); try_get_json(j, "roughnessCutoff", p.roughnessCutoff); + try_get_json(j, "lightSampleCount", p.lightSampleCount); + try_get_json(j, "sampleCount", p.sampleCount); } void to_json(json& j, const RTGI& p) { @@ -194,7 +201,9 @@ namespace Atlas::Lighting { {"useShadowMap", p.useShadowMap}, {"useNormalMap", p.useNormalMaps}, {"opacityCheck", p.opacityCheck}, - {"halfResolution", p.halfResolution} + {"halfResolution", p.halfResolution}, + {"lightSampleCount", p.lightSampleCount}, + {"sampleCount", p.sampleCount}, }; } @@ -212,6 +221,8 @@ namespace Atlas::Lighting { j.at("useNormalMap").get_to(p.useNormalMaps); j.at("opacityCheck").get_to(p.opacityCheck); try_get_json(j, "halfResolution", p.halfResolution); + try_get_json(j, "lightSampleCount", p.lightSampleCount); + try_get_json(j, "sampleCount", p.sampleCount); } void to_json(json& j, const ShadowView& p) { @@ -306,8 +317,10 @@ namespace Atlas::Lighting { j = json { {"sampleCount", p.sampleCount}, {"maxLength", p.maxLength}, + {"minLengthWorldSpace", p.minLengthWorldSpace}, {"thickness", p.thickness}, {"enable", p.enable}, + {"traceWorldSpace", p.traceWorldSpace}, }; } @@ -316,6 +329,9 @@ namespace Atlas::Lighting { j.at("maxLength").get_to(p.maxLength); j.at("thickness").get_to(p.thickness); j.at("enable").get_to(p.enable); + + try_get_json(j, "minLengthWorldSpace", p.minLengthWorldSpace); + try_get_json(j, "traceWorldSpace", p.traceWorldSpace); } void to_json(json& j, const VolumetricClouds::Scattering& p) { diff --git a/src/engine/lighting/RTGI.h b/src/engine/lighting/RTGI.h index e5490286a..2cf802b6a 100644 --- a/src/engine/lighting/RTGI.h +++ b/src/engine/lighting/RTGI.h @@ -13,6 +13,9 @@ namespace Atlas { RTGI() = default; int32_t textureLevel = 4; + int32_t sampleCount = 2; + int32_t lightSampleCount = 1; + float radianceLimit = 5.0f; float bias = 0.15f; float spatialFilterStrength = 5.0f; diff --git a/src/engine/lighting/Reflection.h b/src/engine/lighting/Reflection.h index 1636f7313..4b009dd5e 100644 --- a/src/engine/lighting/Reflection.h +++ b/src/engine/lighting/Reflection.h @@ -13,6 +13,9 @@ namespace Atlas { Reflection() = default; int32_t textureLevel = 3; + int32_t sampleCount = 1; + int32_t lightSampleCount = 2; + float radianceLimit = 10.0f; float roughnessCutoff = 0.9f; float bias = 0.15f; @@ -24,6 +27,7 @@ namespace Atlas { bool enable = true; bool rt = true; + bool ssr = true; bool ddgi = true; bool useShadowMap = false; bool useNormalMaps = true; diff --git a/src/engine/lighting/SSS.h b/src/engine/lighting/SSS.h index 1b773b3b8..8c86f39e7 100644 --- a/src/engine/lighting/SSS.h +++ b/src/engine/lighting/SSS.h @@ -12,10 +12,12 @@ namespace Atlas { SSS() = default; int sampleCount = 8; - float maxLength = 0.5f; - float thickness = 0.3f; + float maxLength = 0.04f; + float minLengthWorldSpace = 0.5f; + float thickness = 0.02f; bool enable = true; + bool traceWorldSpace = false; }; diff --git a/src/engine/lighting/Shadow.cpp b/src/engine/lighting/Shadow.cpp index 3d5ac61a4..5e97a69a7 100644 --- a/src/engine/lighting/Shadow.cpp +++ b/src/engine/lighting/Shadow.cpp @@ -14,7 +14,7 @@ namespace Atlas { isCascaded = true; useCubemap = false; - maps = Texture::Texture2DArray(resolution, resolution, cascadeCount, + maps = CreateRef(resolution, resolution, cascadeCount, VK_FORMAT_D16_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); views = std::vector(cascadeCount); @@ -24,20 +24,20 @@ namespace Atlas { } Shadow::Shadow(float distance, float bias, int32_t resolution, float edgeSoftness, bool useCubemap) : - distance(distance), bias(bias), resolution(resolution), useCubemap(useCubemap), edgeSoftness(edgeSoftness) { + distance(distance), bias(bias), resolution(resolution), useCubemap(useCubemap), edgeSoftness(edgeSoftness) { isCascaded = false; if (useCubemap) { viewCount = 6; - cubemap = Texture::Cubemap(resolution, resolution, VK_FORMAT_D16_UNORM, + cubemap = CreateRef(resolution, resolution, VK_FORMAT_D16_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); } else { viewCount = 1; - maps = Texture::Texture2DArray(resolution, resolution, 1, VK_FORMAT_D16_UNORM, + maps = CreateRef(resolution, resolution, 1, VK_FORMAT_D16_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); } @@ -52,11 +52,11 @@ namespace Atlas { this->resolution = resolution; if (useCubemap) { - cubemap = Texture::Cubemap(resolution, resolution, VK_FORMAT_D16_UNORM, + cubemap = CreateRef(resolution, resolution, VK_FORMAT_D16_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); } else { - maps = Texture::Texture2DArray(resolution, resolution, viewCount, VK_FORMAT_D16_UNORM, + maps = CreateRef(resolution, resolution, viewCount, VK_FORMAT_D16_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); } diff --git a/src/engine/lighting/Shadow.h b/src/engine/lighting/Shadow.h index 89cc9aab3..33d86b52e 100644 --- a/src/engine/lighting/Shadow.h +++ b/src/engine/lighting/Shadow.h @@ -13,16 +13,16 @@ namespace Atlas { struct ShadowView { - float nearDistance; - float farDistance; + float nearDistance = 0.0f; + float farDistance = 100.0f; - mat4 viewMatrix; - mat4 projectionMatrix; + mat4 viewMatrix = mat4(1.0f); + mat4 projectionMatrix = mat4(1.0f); - mat4 frustumMatrix; - mat4 terrainFrustumMatrix; + mat4 frustumMatrix = mat4(1.0f); + mat4 terrainFrustumMatrix = mat4(1.0f); - vec4 orthoSize; + vec4 orthoSize = vec4(1.0f); }; @@ -50,13 +50,13 @@ namespace Atlas { float cascadeBlendDistance = 2.5f; - int32_t resolution; + int32_t resolution = 1024; std::vector views; - int32_t viewCount; + int32_t viewCount = 1; - Texture::Texture2DArray maps; - Texture::Cubemap cubemap; + Ref maps; + Ref cubemap; bool isCascaded = false; bool followMainCamera = false; diff --git a/src/engine/lighting/VolumetricClouds.cpp b/src/engine/lighting/VolumetricClouds.cpp index 79eac047c..44f210b21 100644 --- a/src/engine/lighting/VolumetricClouds.cpp +++ b/src/engine/lighting/VolumetricClouds.cpp @@ -31,8 +31,7 @@ namespace Atlas { auto cameraLocation = camera.GetLocation(); - auto cascadeCenter = cameraLocation + camera.direction * - (camera.nearPlane + camera.farPlane * 0.5f); + auto cascadeCenter = cameraLocation; // A near enough up vector. This is because if the light location is // (0.0f, 1.0f, 0.0f) the shadows wouldn't render correctly due to the @@ -40,7 +39,7 @@ namespace Atlas { vec3 up = glm::vec3(0.0000000000000001f, 1.0f, 0.0000000000000001f); viewMatrix = lookAt(cascadeCenter, cascadeCenter + lightDirection, up); - std::vector corners = camera.GetFrustumCorners(camera.nearPlane, camera.farPlane); + auto corners = camera.GetFrustumCorners(-camera.farPlane, camera.farPlane); vec3 maxProj = vec3(viewMatrix * vec4(corners.at(0), 1.0f)); vec3 minProj = maxProj; diff --git a/src/engine/loader/AssetLoader.cpp b/src/engine/loader/AssetLoader.cpp index d3e875e55..890335845 100644 --- a/src/engine/loader/AssetLoader.cpp +++ b/src/engine/loader/AssetLoader.cpp @@ -171,14 +171,15 @@ namespace Atlas { const int32_t tryCount = 2; + auto fileTime = defaultTime; for (int32_t i = 0; i < tryCount; i++) { try { - return std::filesystem::last_write_time(GetFullPath(path)); + fileTime = std::filesystem::last_write_time(GetFullPath(path)); } catch (...) {} } - return defaultTime; + return fileTime; } diff --git a/src/engine/loader/ModelImporter.cpp b/src/engine/loader/ModelImporter.cpp index d5ffeb3bd..0656fe10d 100644 --- a/src/engine/loader/ModelImporter.cpp +++ b/src/engine/loader/ModelImporter.cpp @@ -104,6 +104,7 @@ namespace Atlas { } + auto radius = 0.0f; auto min = vec3(std::numeric_limits::max()); auto max = vec3(-std::numeric_limits::max()); @@ -159,6 +160,7 @@ namespace Atlas { max = glm::max(vertex, max); min = glm::min(vertex, min); + radius = glm::dot(vertex, vertex); vec3 normal = vec3(matrix * vec4(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z, 0.0f)); @@ -218,7 +220,8 @@ namespace Atlas { state.importer.FreeScene(); meshData.aabb = Volume::AABB(min, max); - meshData.radius = glm::length(max - min) * 0.5; + meshData.radius = glm::sqrt(radius); + meshData.name = Common::Path::GetFileNameWithoutExtension(filename); @@ -251,6 +254,8 @@ namespace Atlas { std::vector meshes; meshes.resize(state.scene->mNumMeshes); + + std::mutex vertexColorMutex; JobGroup group; JobSystem::ExecuteMultiple(group, int32_t(meshes.size()), [&](const JobData& data) { @@ -303,9 +308,13 @@ namespace Atlas { tangents.SetElementCount(hasTangents ? vertexCount : 0); colors.SetElementCount(hasVertexColors ? vertexCount : 0); - material->vertexColors &= hasVertexColors; + { + std::scoped_lock lock(vertexColorMutex); + material->vertexColors &= hasVertexColors; + } meshData.materials.push_back(material); + auto radius = 0.0f; auto min = vec3(std::numeric_limits::max()); auto max = vec3(-std::numeric_limits::max()); @@ -319,6 +328,7 @@ namespace Atlas { max = glm::max(vertex, max); min = glm::min(vertex, min); + radius = glm::dot(vertex, vertex); vec3 normal = vec3(assimpMesh->mNormals[j].x, assimpMesh->mNormals[j].y, assimpMesh->mNormals[j].z); @@ -369,7 +379,7 @@ namespace Atlas { } meshData.aabb = Volume::AABB(min, max); - meshData.radius = glm::length(max - min) * 0.5; + meshData.radius = glm::sqrt(radius); meshData.subData.push_back({ .indicesOffset = 0, @@ -399,6 +409,18 @@ namespace Atlas { auto scene = CreateRef(filename, min, max, depth); + std::map lightMap; + for (uint32_t i = 0; i < state.scene->mNumLights; i++) { + auto light = state.scene->mLights[i]; + lightMap[light->mName.C_Str()] = light; + } + + std::map cameraMap; + for (uint32_t i = 0; i < state.scene->mNumCameras; i++) { + auto camera = state.scene->mCameras[i]; + cameraMap[camera->mName.C_Str()] = camera; + } + auto rootEntity = scene->CreateEntity(); rootEntity.AddComponent("Root"); auto& rootHierarchy = rootEntity.AddComponent(); @@ -408,6 +430,8 @@ namespace Atlas { traverseNodeTree = [&](aiNode* node, Scene::Entity parentEntity) { auto nodeTransform = glm::transpose(glm::make_mat4(&node->mTransformation.a1)); + auto& parentNameComp = parentEntity.GetComponent(); + for (uint32_t i = 0; i < node->mNumMeshes; i++) { auto meshId = node->mMeshes[i]; auto& meshInfo = meshes[meshId]; @@ -423,6 +447,49 @@ namespace Atlas { parentEntity.GetComponent().AddChild(entity); } + if (lightMap.contains(node->mName.C_Str()) && node->mNumChildren == 0) { + auto light = lightMap[node->mName.C_Str()]; + + auto lightType = LightType::DirectionalLight; + switch (light->mType) { + case aiLightSource_POINT: lightType = LightType::PointLight; break; + case aiLightSource_SPOT: lightType = LightType::SpotLight; break; + default: lightType = LightType::PointLight; break; + } + + auto& lightComp = parentEntity.AddComponent(lightType, LightMobility::StationaryLight); + lightComp.color = vec3(light->mColorDiffuse.r, light->mColorDiffuse.g, light->mColorDiffuse.b); + lightComp.intensity = std::max(lightComp.color.r, std::max(lightComp.color.g, lightComp.color.b)); + lightComp.color /= std::max(lightComp.intensity, 1e-9f); + lightComp.color = Common::ColorConverter::ConvertLinearToSRGB(lightComp.color); + + float intensityRadius = glm::sqrt(lightComp.intensity / 0.15f); + + if (lightType == LightType::PointLight) { + lightComp.properties.point.position = vec3(light->mPosition.x, light->mPosition.y, light->mPosition.z); + lightComp.properties.point.radius = std::max(glm::sqrt(100.0f * light->mAttenuationQuadratic), intensityRadius); + lightComp.AddPointShadow(3.0f, 1024); + } + if (lightType == LightType::SpotLight) { + lightComp.properties.spot.position = vec3(light->mPosition.x, light->mPosition.y, light->mPosition.z); + lightComp.properties.spot.direction = vec3(light->mDirection.x, light->mDirection.y, light->mDirection.z); + lightComp.properties.spot.innerConeAngle = light->mAngleInnerCone; + lightComp.properties.spot.outerConeAngle = light->mAngleOuterCone; + lightComp.properties.point.radius = std::max(glm::sqrt(100.0f * light->mAttenuationQuadratic), intensityRadius); + lightComp.AddSpotShadow(3.0f, 1024); + } + } + + if (cameraMap.contains(node->mName.C_Str())) { + auto camera = cameraMap[node->mName.C_Str()]; + + float verticalFOV = glm::degrees(camera->mHorizontalFOV) / camera->mAspect; + vec3 position = vec3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z); + vec2 rotation = vec2(0.0f); + parentEntity.AddComponent(verticalFOV, camera->mAspect, camera->mClipPlaneNear, + camera->mClipPlaneFar, position, rotation); + } + for (uint32_t i = 0; i < node->mNumChildren; i++) { auto nodeEntity = scene->CreatePrefab(node->mChildren[i]->mName.C_Str(), nodeTransform); auto const& hierarchy = nodeEntity.GetComponent(); @@ -486,10 +553,12 @@ namespace Atlas { auto imagesToSave = ImagesToTextures(state); - JobSystem::ExecuteMultiple(group, int32_t(imagesToSave.size()), [&](const JobData& data) { - ImageLoader::SaveImage(imagesToSave[data.idx], imagesToSave[data.idx]->fileName); - }); - JobSystem::Wait(group); + if (saveToDisk) { + JobSystem::ExecuteMultiple(group, int32_t(imagesToSave.size()), [&](const JobData& data) { + ImageLoader::SaveImage(imagesToSave[data.idx], imagesToSave[data.idx]->fileName); + }); + JobSystem::Wait(group); + } std::vector> materials; for (uint32_t i = 0; i < state.scene->mNumMaterials; i++) { @@ -647,8 +716,15 @@ namespace Atlas { } if (assimpMaterial->GetTextureCount(aiTextureType_EMISSION_COLOR) > 0 || assimpMaterial->GetTextureCount(aiTextureType_EMISSIVE) > 0) { - // We don't support this right now - material.emissiveIntensity = 0.0f; + aiString aiPath; + if (assimpMaterial->GetTextureCount(aiTextureType_EMISSION_COLOR) > 0) + assimpMaterial->GetTexture(aiTextureType_EMISSION_COLOR, 0, &aiPath); + else + assimpMaterial->GetTexture(aiTextureType_EMISSIVE, 0, &aiPath); + auto path = Common::Path::Normalize(directory + std::string(aiPath.C_Str())); + if (images.emissiveTextures.contains(path)) { + material.emissiveMap = images.emissiveTextures[path]; + } } // Probably foliage @@ -699,8 +775,10 @@ namespace Atlas { image->ExpandToChannelCount(4, 255); images.Add(MaterialImageType::BaseColor, path, image); - if (!images.Contains(MaterialImageType::Opacity, path) && opacityImage != nullptr) - images.Add(MaterialImageType::Opacity, path, opacityImage); + if (!images.Contains(MaterialImageType::Opacity, path) && opacityImage != nullptr) { + if (IsImageValid(opacityImage)) + images.Add(MaterialImageType::Opacity, path, opacityImage); + } } } if (material->GetTextureCount(aiTextureType_OPACITY) > 0) { @@ -709,7 +787,8 @@ namespace Atlas { auto path = Common::Path::Normalize(directory + std::string(aiPath.C_Str())); if (!images.Contains(MaterialImageType::Opacity, path)) { auto image = ImageLoader::LoadImage(path, false, 1, maxTextureResolution); - images.Add(MaterialImageType::Opacity, path, image); + if (IsImageValid(image)) + images.Add(MaterialImageType::Opacity, path, image); } } if ((material->GetTextureCount(aiTextureType_NORMALS) > 0 || @@ -734,7 +813,8 @@ namespace Atlas { } image->ExpandToChannelCount(4, 255); - images.Add(MaterialImageType::Normal, path, image); + if (IsImageValid(image)) + images.Add(MaterialImageType::Normal, path, image); if (!images.Contains(MaterialImageType::Displacement, path) && displacementImage != nullptr) images.Add(MaterialImageType::Displacement, path, displacementImage); } @@ -754,9 +834,11 @@ namespace Atlas { image = image->GetChannelImage(1, 1); } - images.Add(MaterialImageType::Roughness, path, image); + if (IsImageValid(image)) + images.Add(MaterialImageType::Roughness, path, image); if (!images.Contains(MaterialImageType::Metallic, path) && metallicImage != nullptr) - images.Add(MaterialImageType::Metallic, path, metallicImage); + if (IsImageValid(metallicImage)) + images.Add(MaterialImageType::Metallic, path, metallicImage); } } if (material->GetTextureCount(aiTextureType_METALNESS) > 0) { @@ -765,7 +847,8 @@ namespace Atlas { auto path = Common::Path::Normalize(directory + std::string(aiPath.C_Str())); if (!images.Contains(MaterialImageType::Metallic, path)) { auto image = ImageLoader::LoadImage(path, false, 1, maxTextureResolution); - images.Add(MaterialImageType::Metallic, path, image); + if (IsImageValid(image)) + images.Add(MaterialImageType::Metallic, path, image); } } if (material->GetTextureCount(aiTextureType_SPECULAR) > 0) { @@ -774,7 +857,8 @@ namespace Atlas { auto path = Common::Path::Normalize(directory + std::string(aiPath.C_Str())); if (!images.Contains(MaterialImageType::Metallic, path)) { auto image = ImageLoader::LoadImage(path, false, 1, maxTextureResolution); - images.Add(MaterialImageType::Metallic, path, image); + if (IsImageValid(image)) + images.Add(MaterialImageType::Metallic, path, image); } } if (material->GetTextureCount(aiTextureType_HEIGHT) > 0 && !state.isObj && hasTangents) { @@ -786,6 +870,19 @@ namespace Atlas { images.Add(MaterialImageType::Displacement, path, image); } } + if (material->GetTextureCount(aiTextureType_EMISSION_COLOR) > 0 || + material->GetTextureCount(aiTextureType_EMISSIVE) > 0) { + aiString aiPath; + if (material->GetTextureCount(aiTextureType_EMISSION_COLOR) > 0) + material->GetTexture(aiTextureType_EMISSION_COLOR, 0, &aiPath); + else + material->GetTexture(aiTextureType_EMISSIVE, 0, &aiPath); + auto path = Common::Path::Normalize(directory + std::string(aiPath.C_Str())); + if (!images.Contains(MaterialImageType::Emissive, path)) { + auto image = ImageLoader::LoadImage(path, false, 0, maxTextureResolution); + images.Add(MaterialImageType::Emissive, path, image); + } + } } std::vector>> ModelImporter::ImagesToTextures(ImporterState& state) { @@ -829,6 +926,12 @@ namespace Atlas { images.displacementTextures[path] = ResourceManager::AddResource(image->fileName, texture); imagesToSave.push_back(image); } + for (const auto& [path, image] : images.emissiveImages) { + auto texture = std::make_shared(image); + image->fileName = GetMaterialImageImportPath(state, MaterialImageType::Emissive, path); + images.emissiveTextures[path] = ResourceManager::AddResource(image->fileName, texture); + imagesToSave.push_back(image); + } return imagesToSave; @@ -867,6 +970,8 @@ namespace Atlas { typeName = "Normal"; break; case MaterialImageType::Displacement: typeName = "Displacement"; break; + case MaterialImageType::Emissive: + typeName = "Emissive"; break; default: typeName = "Invalid"; break; } @@ -878,6 +983,22 @@ namespace Atlas { } + bool ModelImporter::IsImageValid(Ref>& image) { + + auto& data = image->GetData(); + + if (data.empty()) + return false; + + bool invalidImage = true; + for (int32_t i = image->channels; i < data.size(); i += image->channels) { + for (int32_t j = 0; j < image->channels; j++) + invalidImage &= data[(i - image->channels) + j] == data[i + j]; + } + + return !invalidImage; + } + } -} \ No newline at end of file +} diff --git a/src/engine/loader/ModelImporter.h b/src/engine/loader/ModelImporter.h index cfd7707ae..7a59ade2e 100644 --- a/src/engine/loader/ModelImporter.h +++ b/src/engine/loader/ModelImporter.h @@ -46,6 +46,8 @@ namespace Atlas { Metallic, Normal, Displacement, + Emissive, + Count }; struct MaterialImages { @@ -55,6 +57,7 @@ namespace Atlas { std::map>> metallicImages; std::map>> normalImages; std::map>> displacementImages; + std::map>> emissiveImages; std::map> baseColorTextures; std::map> opacityTextures; @@ -62,8 +65,9 @@ namespace Atlas { std::map> metallicTextures; std::map> normalTextures; std::map> displacementTextures; + std::map> emissiveTextures; - std::mutex mutexes[6]; + std::mutex mutexes[static_cast(MaterialImageType::Count)]; void Add(MaterialImageType type, const std::string& path, const Ref>& image) { std::scoped_lock lock(mutexes[static_cast(type)]); @@ -87,6 +91,9 @@ namespace Atlas { case MaterialImageType::Displacement: displacementImages[path] = image; break; + case MaterialImageType::Emissive: + emissiveImages[path] = image; + break; default: break; } @@ -108,6 +115,8 @@ namespace Atlas { return normalImages.contains(path); case MaterialImageType::Displacement: return displacementImages.contains(path); + case MaterialImageType::Emissive: + return emissiveImages.contains(path); default: return true; } @@ -139,6 +148,8 @@ namespace Atlas { static std::string GetMaterialImageImportPath(const ImporterState& state, MaterialImageType type, const std::string& filename); + static bool IsImageValid(Ref>& image); + template static Ref> ApplySobelFilter(const Ref>& image, const float strength = 1.0f) { diff --git a/src/engine/loader/TerrainLoader.cpp b/src/engine/loader/TerrainLoader.cpp index c2a8025e4..35a233f15 100644 --- a/src/engine/loader/TerrainLoader.cpp +++ b/src/engine/loader/TerrainLoader.cpp @@ -9,7 +9,7 @@ namespace Atlas { namespace Loader { - void TerrainLoader::SaveTerrain(Ref terrain, std::string filename) { + void TerrainLoader::SaveTerrain(Ref terrain, const std::string& filename) { auto fileStream = AssetLoader::WriteFile(filename, std::ios::out | std::ios::binary); @@ -90,15 +90,15 @@ namespace Atlas { } // Here we assume that all cells are present - auto heightData = cell->heightField.GetData(); + auto heightData = cell->heightField->GetData(); fileStream.write(reinterpret_cast(heightData.data()), heightData.size() * 2); - auto data = cell->normalMap.GetData(); + auto data = cell->normalMap->GetData(); fileStream.write(reinterpret_cast(data.data()), data.size()); - data = cell->splatMap.GetData(); + data = cell->splatMap->GetData(); fileStream.write(reinterpret_cast(data.data()), data.size()); @@ -111,7 +111,7 @@ namespace Atlas { } - Ref TerrainLoader::LoadTerrain(std::string filename) { + Ref TerrainLoader::LoadTerrain(const std::string& filename) { auto fileStream = AssetLoader::ReadFile(filename, std::ios::in); @@ -196,7 +196,7 @@ namespace Atlas { } void TerrainLoader::LoadStorageCell(Ref terrain, Terrain::TerrainStorageCell* cell, - std::string filename, bool initWithHeightData) { + const std::string& filename, bool initWithHeightData) { auto fileStream = AssetLoader::ReadFile(filename, std::ios::in | std::ios::binary); @@ -252,26 +252,31 @@ namespace Atlas { tileSideCount *= 2; } + cell->storage = &terrain->storage; + fileStream.seekg(currPos, std::ios_base::cur); std::vector heightFieldData(tileResolution * tileResolution); fileStream.read(reinterpret_cast(heightFieldData.data()), heightFieldData.size() * 2); - cell->heightField = Texture::Texture2D(tileResolution, tileResolution, + cell->heightField = CreateRef(tileResolution, tileResolution, VK_FORMAT_R16_UINT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); - cell->heightField.SetData(heightFieldData); + cell->heightField->SetData(heightFieldData); Common::Image image(normalDataResolution, normalDataResolution, 3); fileStream.read(reinterpret_cast(image.GetData().data()), image.GetData().size()); + cell->normalData = image.GetData(); + image.ExpandToChannelCount(4, 255); - cell->normalMap = Texture::Texture2D(normalDataResolution, normalDataResolution, + cell->normalMap = CreateRef(normalDataResolution, normalDataResolution, VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Anisotropic); - cell->normalMap.SetData(image.GetData()); + cell->normalMap->SetData(image.GetData()); std::vector splatMapData(heightFieldData.size()); fileStream.read(reinterpret_cast(splatMapData.data()), splatMapData.size()); - cell->splatMap = Texture::Texture2D(tileResolution, tileResolution, + cell->splatMap = CreateRef(tileResolution, tileResolution, VK_FORMAT_R8_UINT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); - cell->splatMap.SetData(splatMapData); + cell->splatMap->SetData(splatMapData); + cell->materialIdxData = splatMapData; if (initWithHeightData) { cell->heightData.resize(tileResolution * tileResolution); diff --git a/src/engine/loader/TerrainLoader.h b/src/engine/loader/TerrainLoader.h index 943bbe5ab..066fcd773 100644 --- a/src/engine/loader/TerrainLoader.h +++ b/src/engine/loader/TerrainLoader.h @@ -16,7 +16,7 @@ namespace Atlas { * @return * @note This method just loads the terrain information, not the nodes. */ - static Ref LoadTerrain(std::string filename); + static Ref LoadTerrain(const std::string& filename); /** * Stores the terrain in a directory on the hard drive @@ -24,7 +24,7 @@ namespace Atlas { * @param filename * @warning All storage cells of the terrain must be loaded. */ - static void SaveTerrain(Ref terrain, std::string filename); + static void SaveTerrain(Ref terrain, const std::string& filename); /** * @@ -34,7 +34,7 @@ namespace Atlas { * @param initWithHeightData */ static void LoadStorageCell(Ref terrain, Terrain::TerrainStorageCell* cell, - std::string filename, bool initWithHeightData = false); + const std::string& filename, bool initWithHeightData = false); private: static int32_t ReadInt(const char* ptr, std::string line, size_t& offset); diff --git a/src/engine/mesh/DataComponent.h b/src/engine/mesh/DataComponent.h index 50717f143..a34d95a08 100644 --- a/src/engine/mesh/DataComponent.h +++ b/src/engine/mesh/DataComponent.h @@ -156,6 +156,18 @@ namespace Atlas { */ const T& operator[](std::size_t idx) const; + /** + * + * @return + */ + typename std::vector::iterator begin(); + + /** + * + * @return + */ + typename std::vector::iterator end(); + /** * * @return @@ -416,6 +428,20 @@ namespace Atlas { } + template + typename std::vector::iterator DataComponent::begin() { + + return data.begin(); + + } + + template + typename std::vector::iterator DataComponent::end() { + + return data.end(); + + } + template typename std::vector::const_iterator DataComponent::begin() const { diff --git a/src/engine/mesh/Mesh.cpp b/src/engine/mesh/Mesh.cpp index 1826e8c1c..a057625cc 100644 --- a/src/engine/mesh/Mesh.cpp +++ b/src/engine/mesh/Mesh.cpp @@ -82,6 +82,19 @@ namespace Atlas { } + void Mesh::InvertNormals() { + + if (!data.normals.ContainsData()) + return; + + for (auto& normal : data.normals) + normal = vec4(-vec3(normal.x, normal.y, normal.z), normal.w); + + UpdateData(); + blas->isBvhBuilt = false; + + } + void Mesh::BuildBVH(bool parallelBuild) { auto device = Graphics::GraphicsDevice::DefaultDevice; @@ -90,27 +103,17 @@ namespace Atlas { AE_ASSERT(data.indexCount > 0 && "There is no data in this mesh"); - if (data.indexCount == 0 || !bindless) return; + if (data.indexCount == 0 || !bindless || !vertexBuffer.elementCount || !indexBuffer.elementCount) return; - data.BuildBVH(parallelBuild); + std::vector triangles; + data.BuildBVHData(triangles); - triangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(GPUTriangle)); - triangleBuffer.SetSize(data.gpuTriangles.size()); - triangleBuffer.SetData(data.gpuTriangles.data(), 0, data.gpuTriangles.size()); - - if (!hardwareRayTracing) { - blasNodeBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(GPUBVHNode)); - blasNodeBuffer.SetSize(data.gpuBvhNodes.size()); - blasNodeBuffer.SetData(data.gpuBvhNodes.data(), 0, data.gpuBvhNodes.size()); - - bvhTriangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(GPUBVHTriangle)); - bvhTriangleBuffer.SetSize(data.gpuBvhTriangles.size()); - bvhTriangleBuffer.SetData(data.gpuBvhTriangles.data(), 0, data.gpuBvhTriangles.size()); - } - else { - Graphics::ASBuilder asBuilder; + blas = CreateRef(); + if (hardwareRayTracing) { std::vector geometryRegions; + geometryRegions.reserve(data.subData.size()); + for (auto& subData : data.subData) { geometryRegions.emplace_back(Graphics::ASGeometryRegion{ .indexCount = subData.indicesCount, @@ -119,36 +122,46 @@ namespace Atlas { }); } - auto blasDesc = asBuilder.GetBLASDescForTriangleGeometry(vertexBuffer.buffer, indexBuffer.buffer, - vertexBuffer.elementCount, vertexBuffer.elementSize, indexBuffer.elementSize, geometryRegions); + blas->Build(triangles, data.materials, vertexBuffer, indexBuffer, geometryRegions); + } + else { + blas->Build(triangles, data.materials); + } - blas = device->CreateBLAS(blasDesc); + } - std::vector triangleOffsets; - for (const auto& subData : data.subData) { - auto triangleOffset = subData.indicesOffset / 3; - triangleOffsets.push_back(triangleOffset); - } + void Mesh::ClearBVH() { - triangleOffsetBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(uint32_t)); - triangleOffsetBuffer.SetSize(triangleOffsets.size(), triangleOffsets.data()); + /* + // This whole operation can only be done when no ray tracing jobs or bindless updates are running + isBvhBuilt = false; - needsBvhRefresh = true; - } + auto device = Graphics::GraphicsDevice::DefaultDevice; + bool hardwareRayTracing = device->support.hardwareRayTracing; + bool bindless = device->support.bindless; - data.gpuBvhNodes.clear(); - data.gpuBvhNodes.shrink_to_fit(); + AE_ASSERT(data.indexCount > 0 && "There is no data in this mesh"); + + if (data.indexCount == 0 || !bindless) return; - data.gpuBvhTriangles.clear(); - data.gpuBvhTriangles.shrink_to_fit(); + data.gpuTriangles.clear(); + data.gpuTriangles.shrink_to_fit(); - isBvhBuilt = true; + triangleBuffer.Reset(); + if (!hardwareRayTracing) { + blasNodeBuffer.Reset(); + bvhTriangleBuffer.Reset(); + } + else { + triangleOffsetBuffer.Reset(); + } + */ } bool Mesh::IsBVHBuilt() const { - return isBvhBuilt; + return blas && blas->IsBuilt(); } diff --git a/src/engine/mesh/Mesh.h b/src/engine/mesh/Mesh.h index 35a496fae..a86d67cfe 100644 --- a/src/engine/mesh/Mesh.h +++ b/src/engine/mesh/Mesh.h @@ -4,6 +4,7 @@ #include "../Material.h" #include "resource/Resource.h" #include "../buffer/VertexArray.h" +#include "../raytracing/BLAS.h" #include "MeshData.h" #include "Impostor.h" @@ -59,11 +60,14 @@ namespace Atlas { */ void UpdateVertexArray(); + void InvertNormals(); + /** * Builds up BVH and fills raytracing related buffers */ void BuildBVH(bool parallelBuild = true); + void ClearBVH(); bool IsBVHBuilt() const; @@ -82,17 +86,12 @@ namespace Atlas { Buffer::VertexBuffer tangentBuffer; Buffer::VertexBuffer colorBuffer; - Buffer::Buffer blasNodeBuffer; - Buffer::Buffer triangleBuffer; - Buffer::Buffer bvhTriangleBuffer; - Buffer::Buffer triangleOffsetBuffer; - - Ref blas = nullptr; - + Ref blas = nullptr; Ref impostor = nullptr; bool cullBackFaces = true; bool depthTest = true; + bool rayTrace = true; bool castShadow = true; @@ -105,6 +104,7 @@ namespace Atlas { float distanceCulling = 10000.0f; float shadowDistanceCulling = 10000.0f; + float rayTraceDistanceCulling = 10000.0f; float impostorDistance = 300.0f; float impostorShadowDistance = 100.0f; @@ -112,9 +112,7 @@ namespace Atlas { private: bool isLoaded = false; - - std::atomic_bool isBvhBuilt = false; - std::atomic_bool needsBvhRefresh = false; + }; diff --git a/src/engine/mesh/MeshData.cpp b/src/engine/mesh/MeshData.cpp index 6e020758b..0e615ab0c 100644 --- a/src/engine/mesh/MeshData.cpp +++ b/src/engine/mesh/MeshData.cpp @@ -60,42 +60,18 @@ namespace Atlas { } - void MeshData::BuildBVH(bool parallelBuild) { + void MeshData::BuildBVHData(std::vector& triangles) { auto device = Graphics::GraphicsDevice::DefaultDevice; bool hardwareRayTracing = device->support.hardwareRayTracing; - struct Triangle { - vec3 v0; - vec3 v1; - vec3 v2; - - vec3 n0; - vec3 n1; - vec3 n2; - - vec2 uv0; - vec2 uv1; - vec2 uv2; - - vec4 color0; - vec4 color1; - vec4 color2; - - int32_t materialIdx; - float opacity; - }; - uint32_t triangleCount = 0; for (auto& sub : subData) { triangleCount += sub.indicesCount / 3; } - std::vector triangles(triangleCount); - - std::vector aabbs(triangleCount); - std::vector bvhTriangles(triangleCount); + triangles.resize(triangleCount); triangleCount = 0; @@ -140,140 +116,12 @@ namespace Atlas { triangles[k].materialIdx = sub.materialIdx; triangles[k].opacity = sub.material->HasOpacityMap() ? -1.0f : sub.material->opacity; - auto min = glm::min(glm::min(triangles[k].v0, - triangles[k].v1), triangles[k].v2); - auto max = glm::max(glm::max(triangles[k].v0, - triangles[k].v1), triangles[k].v2); - - bvhTriangles[k].v0 = triangles[k].v0; - bvhTriangles[k].v1 = triangles[k].v1; - bvhTriangles[k].v2 = triangles[k].v2; - bvhTriangles[k].idx = k; - - aabbs[k] = Volume::AABB(min, max); - } triangleCount += subDataTriangleCount; } - Volume::BVH bvh; - if (!hardwareRayTracing) { - // Generate BVH - bvh = Volume::BVH(aabbs, bvhTriangles, parallelBuild); - - bvhTriangles.clear(); - bvhTriangles.shrink_to_fit(); - } - - auto& data = hardwareRayTracing ? bvhTriangles : bvh.data; - - for (auto& bvhTriangle : data) { - - auto& triangle = triangles[bvhTriangle.idx]; - - auto v0v1 = triangle.v1 - triangle.v0; - auto v0v2 = triangle.v2 - triangle.v0; - - auto uv0uv1 = triangle.uv1 - triangle.uv0; - auto uv0uv2 = triangle.uv2 - triangle.uv0; - - auto r = 1.0f / (uv0uv1.x * uv0uv2.y - uv0uv2.x * uv0uv1.y); - - auto s = vec3(uv0uv2.y * v0v1.x - uv0uv1.y * v0v2.x, - uv0uv2.y * v0v1.y - uv0uv1.y * v0v2.y, - uv0uv2.y * v0v1.z - uv0uv1.y * v0v2.z) * r; - - auto t = vec3(uv0uv1.x * v0v2.x - uv0uv2.x * v0v1.x, - uv0uv1.x * v0v2.y - uv0uv2.x * v0v1.y, - uv0uv1.x * v0v2.z - uv0uv2.x * v0v1.z) * r; - - auto normal = glm::normalize(triangle.n0 + triangle.n1 + triangle.n2); - - auto tangent = glm::normalize(s - normal * dot(normal, s)); - auto handedness = (glm::dot(glm::cross(tangent, normal), t) < 0.0f ? 1.0f : -1.0f); - - auto bitangent = handedness * glm::normalize(glm::cross(tangent, normal)); - - // Compress data - auto pn0 = Common::Packing::PackSignedVector3x10_1x2(vec4(triangle.n0, 0.0f)); - auto pn1 = Common::Packing::PackSignedVector3x10_1x2(vec4(triangle.n1, 0.0f)); - auto pn2 = Common::Packing::PackSignedVector3x10_1x2(vec4(triangle.n2, 0.0f)); - - auto pt = Common::Packing::PackSignedVector3x10_1x2(vec4(tangent, 0.0f)); - auto pbt = Common::Packing::PackSignedVector3x10_1x2(vec4(bitangent, 0.0f)); - - auto puv0 = glm::packHalf2x16(triangle.uv0); - auto puv1 = glm::packHalf2x16(triangle.uv1); - auto puv2 = glm::packHalf2x16(triangle.uv2); - - auto pc0 = glm::packUnorm4x8(triangle.color0); - auto pc1 = glm::packUnorm4x8(triangle.color1); - auto pc2 = glm::packUnorm4x8(triangle.color2); - - auto cn0 = reinterpret_cast(pn0); - auto cn1 = reinterpret_cast(pn1); - auto cn2 = reinterpret_cast(pn2); - - auto ct = reinterpret_cast(pt); - auto cbt = reinterpret_cast(pbt); - - auto cuv0 = reinterpret_cast(puv0); - auto cuv1 = reinterpret_cast(puv1); - auto cuv2 = reinterpret_cast(puv2); - - auto cc0 = reinterpret_cast(pc0); - auto cc1 = reinterpret_cast(pc1); - auto cc2 = reinterpret_cast(pc2); - - GPUTriangle gpuTriangle; - - gpuTriangle.v0 = vec4(triangle.v0, cn0); - gpuTriangle.v1 = vec4(triangle.v1, cn1); - gpuTriangle.v2 = vec4(triangle.v2, cn2); - gpuTriangle.d0 = vec4(cuv0, cuv1, cuv2, reinterpret_cast(triangle.materialIdx)); - gpuTriangle.d1 = vec4(ct, cbt, bvhTriangle.endOfNode ? 1.0f : -1.0f, 0.0f); - gpuTriangle.d2 = vec4(cc0, cc1, cc2, triangle.opacity); - - gpuTriangles.push_back(gpuTriangle); - - if (!hardwareRayTracing) { - GPUBVHTriangle gpuBvhTriangle; - gpuBvhTriangle.v0 = vec4(triangle.v0, bvhTriangle.endOfNode ? 1.0f : -1.0f); - gpuBvhTriangle.v1 = vec4(triangle.v1, reinterpret_cast(triangle.materialIdx)); - gpuBvhTriangle.v2 = vec4(triangle.v2, triangle.opacity); - - gpuBvhTriangles.push_back(gpuBvhTriangle); - } - - } - - if (!hardwareRayTracing) { - triangles.clear(); - triangles.shrink_to_fit(); - - auto& nodes = bvh.GetTree(); - gpuBvhNodes = std::vector(nodes.size()); - // Copy to GPU format - for (size_t i = 0; i < nodes.size(); i++) { - gpuBvhNodes[i].leftPtr = nodes[i].leftPtr; - gpuBvhNodes[i].rightPtr = nodes[i].rightPtr; - - gpuBvhNodes[i].leftAABB.min = nodes[i].leftAABB.min; - gpuBvhNodes[i].leftAABB.max = nodes[i].leftAABB.max; - - gpuBvhNodes[i].rightAABB.min = nodes[i].rightAABB.min; - gpuBvhNodes[i].rightAABB.max = nodes[i].rightAABB.max; - } - } - - } - - bool MeshData::IsBVHBuilt() { - - return gpuBvhTriangles.size() > 0; - } void MeshData::DeepCopy(const MeshData& that) { diff --git a/src/engine/mesh/MeshData.h b/src/engine/mesh/MeshData.h index 1e7a5d982..dd4f77e56 100644 --- a/src/engine/mesh/MeshData.h +++ b/src/engine/mesh/MeshData.h @@ -2,7 +2,7 @@ #include "../System.h" #include "../volume/AABB.h" -#include "raytracing/RTStructures.h" +#include "raytracing/BLAS.h" #include "resource/Resource.h" #include "DataComponent.h" #include "Material.h" @@ -85,13 +85,7 @@ namespace Atlas { /** * Builds a blas from the data */ - void BuildBVH(bool parallelBuild); - - /** - * - * @return - */ - bool IsBVHBuilt(); + void BuildBVHData(std::vector& triangles); std::string name; @@ -117,10 +111,6 @@ namespace Atlas { int32_t indexCount = 0; int32_t vertexCount = 0; - std::vector gpuTriangles; - std::vector gpuBvhTriangles; - std::vector gpuBvhNodes; - }; } diff --git a/src/engine/mesh/MeshSerializer.cpp b/src/engine/mesh/MeshSerializer.cpp index b422aa906..9089c7b22 100644 --- a/src/engine/mesh/MeshSerializer.cpp +++ b/src/engine/mesh/MeshSerializer.cpp @@ -14,6 +14,7 @@ namespace Atlas::Mesh { {"mobility", mobility}, {"usage", usage}, {"cullBackFaces", p.cullBackFaces}, + {"rayTrace", p.rayTrace}, {"castShadow", p.castShadow}, {"vegetation", p.vegetation}, {"windNoiseTextureLod", p.windNoiseTextureLod}, @@ -22,6 +23,7 @@ namespace Atlas::Mesh { {"allowedShadowCascades", p.allowedShadowCascades}, {"distanceCulling", p.distanceCulling}, {"shadowDistanceCulling", p.shadowDistanceCulling}, + {"rayTraceDistanceCulling", p.rayTraceDistanceCulling}, {"impostorDistance", p.impostorDistance}, {"impostorShadowDistance", p.impostorShadowDistance}, {"invertUVs", p.invertUVs}, @@ -51,6 +53,9 @@ namespace Atlas::Mesh { j.at("invertUVs").get_to(p.invertUVs); j.at("data").get_to(p.data); + try_get_json(j, "rayTrace", p.rayTrace); + try_get_json(j, "rayTraceDistanceCulling", p.rayTraceDistanceCulling); + p.mobility = static_cast(mobility); p.usage = static_cast(usage); @@ -190,22 +195,25 @@ namespace Atlas { {"twoSided", p.twoSided}, {"vertexColors", p.vertexColors}, {"uvChannel", p.uvChannel}, + {"uvAnimation", p.uvAnimation}, }; - if (p.baseColorMap.IsValid()) + if (p.baseColorMap.IsValid() && !p.baseColorMap.IsGenerated()) j["baseColorMapPath"] = p.baseColorMap.GetResource()->path; - if (p.opacityMap.IsValid()) + if (p.opacityMap.IsValid() && !p.opacityMap.IsGenerated()) j["opacityMapPath"] = p.opacityMap.GetResource()->path; - if (p.normalMap.IsValid()) + if (p.normalMap.IsValid() && !p.normalMap.IsGenerated()) j["normalMapPath"] = p.normalMap.GetResource()->path; - if (p.roughnessMap.IsValid()) + if (p.roughnessMap.IsValid() && !p.roughnessMap.IsGenerated()) j["roughnessMapPath"] = p.roughnessMap.GetResource()->path; - if (p.metalnessMap.IsValid()) + if (p.metalnessMap.IsValid() && !p.metalnessMap.IsGenerated()) j["metalnessMapPath"] = p.metalnessMap.GetResource()->path; - if (p.aoMap.IsValid()) + if (p.aoMap.IsValid() && !p.aoMap.IsGenerated()) j["aoMapPath"] = p.aoMap.GetResource()->path; - if (p.displacementMap.IsValid()) + if (p.displacementMap.IsValid() && !p.displacementMap.IsGenerated()) j["displacementMapPath"] = p.displacementMap.GetResource()->path; + if (p.emissiveMap.IsValid() && !p.emissiveMap.IsGenerated()) + j["emissiveMapPath"] = p.emissiveMap.GetResource()->path; } @@ -224,19 +232,22 @@ namespace Atlas { j.at("normalScale").get_to(p.normalScale); j.at("displacementScale").get_to(p.displacementScale); j.at("tiling").get_to(p.tiling); + j.at("tiling").get_to(p.tiling); j.at("twoSided").get_to(p.twoSided); j.at("vertexColors").get_to(p.vertexColors); j.at("uvChannel").get_to(p.uvChannel); - auto getTextureHandle = [](const std::string& path, bool colorSpaceConversion) -> auto { + try_get_json(j, "uvAnimation", p.uvAnimation); + + auto getTextureHandle = [](const std::string& path, bool colorSpaceConversion, bool priority = false) -> auto { return ResourceManager::GetOrLoadResource(path, colorSpaceConversion, - Texture::Wrapping::Repeat, Texture::Filtering::Anisotropic, 0); + Texture::Wrapping::Repeat, Texture::Filtering::Anisotropic, 0, false, priority); }; if (j.contains("baseColorMapPath")) p.baseColorMap = getTextureHandle(j["baseColorMapPath"], false); if (j.contains("opacityMapPath")) - p.opacityMap = getTextureHandle(j["opacityMapPath"], false); + p.opacityMap = getTextureHandle(j["opacityMapPath"], false, true); if (j.contains("normalMapPath")) p.normalMap = getTextureHandle(j["normalMapPath"], false); if (j.contains("roughnessMapPath")) @@ -247,6 +258,8 @@ namespace Atlas { p.aoMap = getTextureHandle(j["aoMapPath"], false); if (j.contains("displacementMapPath")) p.displacementMap = getTextureHandle(j["displacementMapPath"], false); + if (j.contains("emissiveMapPath")) + p.emissiveMap = getTextureHandle(j["emissiveMapPath"], false); } diff --git a/src/engine/ocean/OceanSimulation.cpp b/src/engine/ocean/OceanSimulation.cpp index 646fedaa1..ff7b86584 100644 --- a/src/engine/ocean/OceanSimulation.cpp +++ b/src/engine/ocean/OceanSimulation.cpp @@ -188,14 +188,11 @@ namespace Atlas { }; commandList->PushConstants("constants", &constants); - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {hTD.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {h0K.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT} }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(hTD.image, 3, 0); commandList->BindImage(h0K.image, 3, 1); @@ -232,29 +229,26 @@ namespace Atlas { commandList->BindPipeline(pipeline); - std::vector imageBarriers; - std::vector bufferBarriers; - for (int32_t i = 0; i < log2n; i++) { if (!pingpong) { - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {hTD.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT}, {hTDPingpong.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} }; commandList->BindImage(hTD.image, 3, 1); commandList->BindImage(hTDPingpong.image, 3, 2); + commandList->PipelineBarrier(imageBarriers, {}); } else { - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {hTD.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {hTDPingpong.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT} }; commandList->BindImage(hTDPingpong.image, 3, 1); commandList->BindImage(hTD.image, 3, 2); - } - - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); + } auto preTwiddle = (float)N / powf(2.0f, (float)i + 1.0f); @@ -288,14 +282,11 @@ namespace Atlas { image = hTDPingpong.image; } - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {displacementMap.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(displacementMap.image, 3, 0); commandList->BindImage(image, 3, 1); @@ -342,14 +333,11 @@ namespace Atlas { }; commandList->PushConstants("constants", &constants); - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {displacementMap.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT}, {normalMap.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(displacementMap.image, 3, 0); commandList->BindImage(normalMap.image, 3, 1); diff --git a/src/engine/physics/Body.h b/src/engine/physics/Body.h index c9b5e055a..4aff3dc06 100644 --- a/src/engine/physics/Body.h +++ b/src/engine/physics/Body.h @@ -13,7 +13,7 @@ namespace Atlas::Physics { Body() = default; Body(BodyID bodyId, PhysicsWorld* world) : bodyId(bodyId), world(world) {} - bool IsValid() const { return world != nullptr; } + bool IsValid() const { return world != nullptr && !bodyId.IsInvalid(); } void SetMatrix(mat4 matrix); diff --git a/src/engine/physics/PhysicsSerializer.cpp b/src/engine/physics/PhysicsSerializer.cpp index 742fc0cc6..b2234fc6c 100644 --- a/src/engine/physics/PhysicsSerializer.cpp +++ b/src/engine/physics/PhysicsSerializer.cpp @@ -13,7 +13,7 @@ namespace Atlas::Physics { {"scale", p.scale}, }; - if (p.mesh.IsValid()) { + if (p.mesh.IsValid() && !p.mesh.IsGenerated()) { j["resourcePath"] = p.mesh.GetResource()->path; } } diff --git a/src/engine/physics/PhysicsWorld.cpp b/src/engine/physics/PhysicsWorld.cpp index e1227778a..22b359a25 100644 --- a/src/engine/physics/PhysicsWorld.cpp +++ b/src/engine/physics/PhysicsWorld.cpp @@ -93,6 +93,15 @@ namespace Atlas { } + bool PhysicsWorld::ContainsBody(Body body) const { + + if (body.bodyId.IsInvalid()) + return false; + + return bodyToShapeMap.contains(body.bodyId); + + } + void PhysicsWorld::SetBodyMatrix(BodyID bodyId, const mat4& matrix) { JPH::Vec3 pos; @@ -243,6 +252,12 @@ namespace Atlas { } + uint32_t PhysicsWorld::GetBodyCount() const { + + return system->GetNumBodies(); + + } + Volume::RayResult PhysicsWorld::CastRay(Volume::Ray& ray) { JPH::RayCastResult hit; diff --git a/src/engine/physics/PhysicsWorld.h b/src/engine/physics/PhysicsWorld.h index 538d86379..c64de2283 100644 --- a/src/engine/physics/PhysicsWorld.h +++ b/src/engine/physics/PhysicsWorld.h @@ -42,6 +42,8 @@ namespace Atlas { void DestroyBody(Body body); + bool ContainsBody(Body body) const; + void SetBodyMatrix(BodyID bodyId, const mat4& matrix); mat4 GetBodyMatrix(BodyID bodyId); @@ -76,6 +78,8 @@ namespace Atlas { vec3 GetGravity(); + uint32_t GetBodyCount() const; + Volume::RayResult CastRay(Volume::Ray& ray); void OptimizeBroadphase(); diff --git a/src/engine/physics/Player.cpp b/src/engine/physics/Player.cpp index d3c7d763c..55f0ee291 100644 --- a/src/engine/physics/Player.cpp +++ b/src/engine/physics/Player.cpp @@ -17,9 +17,18 @@ namespace Atlas::Physics { settings.mPredictiveContactDistance = predictiveContactDistance; - settings.mCharacterPadding = shapePadding; + settings.mMaxCollisionIterations = 10; + settings.mMaxConstraintIterations = 10; settings.mShapeOffset = VecToJPHVec(shapeOffset); settings.mShape = shape->ref; + settings.mInnerBodyShape = shape->ref; + settings.mInnerBodyLayer = Layers::Movable; + settings.mCharacterPadding = shapePadding; + + if (shape->type == ShapeType::Capsule) { + auto shapeSettings = static_cast(shape->settings.get()); + settings.mSupportingVolume = { JPH::Vec3::sAxisY(), -shapeSettings->radius }; + } return settings; @@ -140,6 +149,8 @@ namespace Atlas::Physics { auto gravityVector = -GetUp() * glm::length(world->GetGravity()); + character->SetEnhancedInternalEdgeRemoval(true); + JPH::CharacterVirtual::ExtendedUpdateSettings settings; settings.mStickToFloorStepDown = VecToJPHVec(stickToGroundDist); @@ -160,6 +171,15 @@ namespace Atlas::Physics { character = CreateRef(settings, VecToJPHVec(initialPosition), QuatToJPHQuat(initialRotation), world->system.get()); + character->SetListener(this); + + } + + void Player::OnContactAdded(const JPH::CharacterVirtual *inCharacter, const JPH::BodyID &inBodyID2, const JPH::SubShapeID &inSubShapeID2, + JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings &ioSettings) { + + ioSettings.mCanPushCharacter = false; + } } \ No newline at end of file diff --git a/src/engine/physics/Player.h b/src/engine/physics/Player.h index b36346a4e..b46843fe4 100644 --- a/src/engine/physics/Player.h +++ b/src/engine/physics/Player.h @@ -21,12 +21,12 @@ namespace Atlas::Physics { float predictiveContactDistance = 0.1f; float shapePadding = 0.02f; - vec3 shapeOffset; + vec3 shapeOffset = vec3(0.0f); Ref shape; }; - class Player { + class Player : JPH::CharacterContactListener { public: Player() = default; @@ -70,6 +70,9 @@ namespace Atlas::Physics { protected: void Init(PhysicsWorld* world, vec3 initialPosition, quat initialRotation); + virtual void OnContactAdded(const JPH::CharacterVirtual *inCharacter, const JPH::BodyID &inBodyID2, const JPH::SubShapeID &inSubShapeID2, + JPH::RVec3Arg inContactPosition, JPH::Vec3Arg inContactNormal, JPH::CharacterContactSettings &ioSettings) override; + Ref character = nullptr; PhysicsWorld* world = nullptr; diff --git a/src/engine/physics/ShapesManager.cpp b/src/engine/physics/ShapesManager.cpp index 8a03bdeb6..78c2fa030 100644 --- a/src/engine/physics/ShapesManager.cpp +++ b/src/engine/physics/ShapesManager.cpp @@ -29,7 +29,9 @@ namespace Atlas { const auto& scale = settings.scale; // This all assumes right now that mesh data doesn't change in the objects lifetime + meshShapeCacheMutex.lock(); if (!meshShapeCache.contains(mesh.GetID())) { + meshShapeCacheMutex.unlock(); JPH::VertexList vertexList; JPH::IndexedTriangleList triangleList; @@ -59,6 +61,7 @@ namespace Atlas { shape->ref = result.Get(); + meshShapeCacheMutex.lock(); meshShapeCache[mesh.GetID()] = { mesh.Get(), shape->ref }; } @@ -67,6 +70,7 @@ namespace Atlas { shape->ref = meshShapeCache[mesh.GetID()].ref; } + meshShapeCacheMutex.unlock(); if (scale.x != 1.0f || scale.y != 1.0f || scale.z != 1.0f) { return shape->Scale(scale); @@ -196,10 +200,11 @@ namespace Atlas { void ShapesManager::Update() { + std::scoped_lock lock(meshShapeCacheMutex); std::erase_if(meshShapeCache, [](auto& item) { return item.second.resource.expired(); } ); } } -} \ No newline at end of file +} diff --git a/src/engine/pipeline/PipelineConfig.cpp b/src/engine/pipeline/PipelineConfig.cpp index c8bd7f939..55dc05967 100644 --- a/src/engine/pipeline/PipelineConfig.cpp +++ b/src/engine/pipeline/PipelineConfig.cpp @@ -67,14 +67,14 @@ namespace Atlas { } - bool PipelineConfig::HasMacro(const std::string& macro) { + bool PipelineConfig::HasMacro(const char* macro) { return std::any_of(macros.begin(), macros.end(), - [macro](const auto& value) { return value == macro; }); + [macro](const auto& value) { return std::strcmp(value.c_str(), macro) == 0; }); } - bool PipelineConfig::ManageMacro(const std::string& macro, bool enable) { + bool PipelineConfig::ManageMacro(const char* macro, bool enable) { bool hasMacro = HasMacro(macro); if (enable && !hasMacro) { diff --git a/src/engine/pipeline/PipelineConfig.h b/src/engine/pipeline/PipelineConfig.h index e26a7a5c6..b5b6d58dd 100644 --- a/src/engine/pipeline/PipelineConfig.h +++ b/src/engine/pipeline/PipelineConfig.h @@ -33,9 +33,9 @@ namespace Atlas { void RemoveMacro(const std::string& macro); - bool HasMacro(const std::string& macro); + bool HasMacro(const char* macro); - bool ManageMacro(const std::string& macro, bool enable); + bool ManageMacro(const char* macro, bool enable); bool IsValid() const; diff --git a/src/engine/postprocessing/Bloom.h b/src/engine/postprocessing/Bloom.h new file mode 100644 index 000000000..49638215a --- /dev/null +++ b/src/engine/postprocessing/Bloom.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../System.h" + +#include "resource/Resource.h" +#include "texture/Texture2D.h" + +namespace Atlas::PostProcessing { + + class Bloom { + + public: + Bloom() = default; + + bool enable = true; + float strength = 0.01f; + float dirtStrength = 2.0f; + float threshold = 1.0f; + + float filterSize = 0.02f; + uint32_t mipLevels = 6; + + ResourceHandle dirtMap; + + }; + +} \ No newline at end of file diff --git a/src/engine/postprocessing/PostProcessing.h b/src/engine/postprocessing/PostProcessing.h index d12cd9ef9..7964800c6 100644 --- a/src/engine/postprocessing/PostProcessing.h +++ b/src/engine/postprocessing/PostProcessing.h @@ -7,6 +7,7 @@ #include "Sharpen.h" #include "FilmGrain.h" #include "TAA.h" +#include "Bloom.h" namespace Atlas { @@ -31,6 +32,7 @@ namespace Atlas { ChromaticAberration chromaticAberration; FilmGrain filmGrain; Sharpen sharpen; + Bloom bloom; }; diff --git a/src/engine/postprocessing/PostProcessingSerializer.cpp b/src/engine/postprocessing/PostProcessingSerializer.cpp index 3239151d2..afd6110c1 100644 --- a/src/engine/postprocessing/PostProcessingSerializer.cpp +++ b/src/engine/postprocessing/PostProcessingSerializer.cpp @@ -1,5 +1,7 @@ #include "PostProcessingSerializer.h" +#include "resource/ResourceManager.h" + namespace Atlas::PostProcessing { void to_json(json& j, const ChromaticAberration& p) { @@ -70,6 +72,35 @@ namespace Atlas::PostProcessing { j.at("color").get_to(p.color); } + void to_json(json& j, const Bloom& p) { + j = json { + {"enable", p.enable}, + {"filterSize", p.filterSize}, + {"mipLevels", p.mipLevels}, + {"strength", p.strength}, + {"dirtStrength", p.dirtStrength}, + {"threshold", p.threshold}, + }; + + if (p.dirtMap.IsValid() && !p.dirtMap.IsGenerated()) + j["dirtMap"] = p.dirtMap.GetResource()->path; + + } + + void from_json(const json& j, Bloom& p) { + j.at("enable").get_to(p.enable); + j.at("filterSize").get_to(p.filterSize); + j.at("mipLevels").get_to(p.mipLevels); + j.at("strength").get_to(p.strength); + j.at("threshold").get_to(p.threshold); + + try_get_json(j, "dirtStrength", p.dirtStrength); + + if (j.contains("dirtMap")) + p.dirtMap = ResourceManager::GetOrLoadResource(j["dirtMap"], false, + Texture::Wrapping::Repeat, Texture::Filtering::Linear, 0); + } + void to_json(json& j, const PostProcessing& p) { j = json { {"tint", p.tint}, @@ -84,6 +115,7 @@ namespace Atlas::PostProcessing { {"chromaticAberration", p.chromaticAberration}, {"filmGrain", p.filmGrain}, {"sharpen", p.sharpen}, + {"bloom", p.bloom}, }; } @@ -100,6 +132,7 @@ namespace Atlas::PostProcessing { j.at("chromaticAberration").get_to(p.chromaticAberration); j.at("filmGrain").get_to(p.filmGrain); j.at("sharpen").get_to(p.sharpen); + try_get_json(j, "bloom", p.bloom); } } \ No newline at end of file diff --git a/src/engine/postprocessing/PostProcessingSerializer.h b/src/engine/postprocessing/PostProcessingSerializer.h index 86331802f..5b2a1a949 100644 --- a/src/engine/postprocessing/PostProcessingSerializer.h +++ b/src/engine/postprocessing/PostProcessingSerializer.h @@ -26,6 +26,10 @@ namespace Atlas::PostProcessing { void from_json(const json& j, Vignette& p); + void to_json(json& j, const Bloom& p); + + void from_json(const json& j, Bloom& p); + void to_json(json& j, const PostProcessing& p); void from_json(const json& j, PostProcessing& p); diff --git a/src/engine/postprocessing/Vignette.h b/src/engine/postprocessing/Vignette.h index 14f07d952..f926c0cf6 100644 --- a/src/engine/postprocessing/Vignette.h +++ b/src/engine/postprocessing/Vignette.h @@ -16,11 +16,11 @@ namespace Atlas { bool enable = false; - float offset; - float power; - float strength; + float offset = 0.1f; + float power = 1.0f; + float strength = 0.1f; - vec3 color; + vec3 color = vec3(0.0f); }; diff --git a/src/engine/raytracing/BLAS.cpp b/src/engine/raytracing/BLAS.cpp new file mode 100644 index 000000000..d4ed01085 --- /dev/null +++ b/src/engine/raytracing/BLAS.cpp @@ -0,0 +1,213 @@ +#include "BLAS.h" + +#include "../volume/BVH.h" +#include "../common/Packing.h" + +namespace Atlas::RayTracing { + + void BLAS::Build(std::vector& triangles, std::vector>& materials) { + + BuildBuffers(triangles, materials); + + isBvhBuilt = true; + + } + + void BLAS::Build(std::vector& triangles, std::vector>& materials, + Buffer::VertexBuffer& vertexBuffer, Buffer::IndexBuffer& indexBuffer, std::span geometryRegions) { + + auto device = Graphics::GraphicsDevice::DefaultDevice; + bool hardwareRayTracing = device->support.hardwareRayTracing; + + BuildBuffers(triangles, materials); + + if (hardwareRayTracing) { + Graphics::ASBuilder asBuilder; + + auto blasDesc = asBuilder.GetBLASDescForTriangleGeometry(vertexBuffer.buffer, indexBuffer.buffer, + vertexBuffer.elementCount, vertexBuffer.elementSize, indexBuffer.elementSize, geometryRegions); + + blas = device->CreateBLAS(blasDesc); + + std::vector triangleOffsets; + triangleOffsets.reserve(geometryRegions.size()); + + for (const auto& region : geometryRegions) { + auto triangleOffset = uint32_t(region.indexOffset) / 3; + triangleOffsets.push_back(triangleOffset); + } + + triangleOffsetBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(uint32_t)); + triangleOffsetBuffer.SetSize(triangleOffsets.size(), triangleOffsets.data()); + + needsBvhRefresh = true; + } + + isBvhBuilt = true; + + } + + bool BLAS::IsBuilt() const { + + return isBvhBuilt; + + } + + void BLAS::BuildBuffers(std::vector& triangles, std::vector>& materials) { + + auto device = Graphics::GraphicsDevice::DefaultDevice; + bool hardwareRayTracing = device->support.hardwareRayTracing; + + this->materials = materials; + + auto triangleCount = triangles.size(); + + std::vector aabbs(triangleCount); + std::vector bvhTriangles(triangleCount); + + for (size_t i = 0; i < triangleCount; i++) { + auto min = glm::min(glm::min(triangles[i].v0, + triangles[i].v1), triangles[i].v2); + auto max = glm::max(glm::max(triangles[i].v0, + triangles[i].v1), triangles[i].v2); + + bvhTriangles[i].v0 = triangles[i].v0; + bvhTriangles[i].v1 = triangles[i].v1; + bvhTriangles[i].v2 = triangles[i].v2; + bvhTriangles[i].idx = i; + + aabbs[i] = Volume::AABB(min, max); + } + + Volume::BVH bvh; + if (!hardwareRayTracing) { + // Generate BVH + bvh = Volume::BVH(aabbs, bvhTriangles, false); + + bvhTriangles.clear(); + bvhTriangles.shrink_to_fit(); + + aabbs.clear(); + aabbs.shrink_to_fit(); + } + + auto& data = hardwareRayTracing ? bvhTriangles : bvh.data; + + std::vector gpuTriangles; + std::vector gpuBvhTriangles; + + gpuTriangles.reserve(triangleCount); + if (!hardwareRayTracing) + gpuBvhTriangles.reserve(triangleCount); + + for (auto& bvhTriangle : data) { + + auto& triangle = triangles[bvhTriangle.idx]; + + auto v0v1 = triangle.v1 - triangle.v0; + auto v0v2 = triangle.v2 - triangle.v0; + + auto uv0uv1 = triangle.uv1 - triangle.uv0; + auto uv0uv2 = triangle.uv2 - triangle.uv0; + + auto r = 1.0f / (uv0uv1.x * uv0uv2.y - uv0uv2.x * uv0uv1.y); + + auto s = vec3(uv0uv2.y * v0v1.x - uv0uv1.y * v0v2.x, + uv0uv2.y * v0v1.y - uv0uv1.y * v0v2.y, + uv0uv2.y * v0v1.z - uv0uv1.y * v0v2.z) * r; + + auto t = vec3(uv0uv1.x * v0v2.x - uv0uv2.x * v0v1.x, + uv0uv1.x * v0v2.y - uv0uv2.x * v0v1.y, + uv0uv1.x * v0v2.z - uv0uv2.x * v0v1.z) * r; + + auto normal = glm::normalize(triangle.n0 + triangle.n1 + triangle.n2); + + auto tangent = glm::normalize(s - normal * dot(normal, s)); + auto handedness = (glm::dot(glm::cross(tangent, normal), t) < 0.0f ? 1.0f : -1.0f); + + auto bitangent = handedness * glm::normalize(glm::cross(tangent, normal)); + + // Compress data + auto pn0 = Common::Packing::PackSignedVector3x10_1x2(vec4(triangle.n0, 0.0f)); + auto pn1 = Common::Packing::PackSignedVector3x10_1x2(vec4(triangle.n1, 0.0f)); + auto pn2 = Common::Packing::PackSignedVector3x10_1x2(vec4(triangle.n2, 0.0f)); + + auto pt = Common::Packing::PackSignedVector3x10_1x2(vec4(tangent, 0.0f)); + auto pbt = Common::Packing::PackSignedVector3x10_1x2(vec4(bitangent, 0.0f)); + + auto puv0 = glm::packHalf2x16(triangle.uv0); + auto puv1 = glm::packHalf2x16(triangle.uv1); + auto puv2 = glm::packHalf2x16(triangle.uv2); + + auto pc0 = glm::packUnorm4x8(triangle.color0); + auto pc1 = glm::packUnorm4x8(triangle.color1); + auto pc2 = glm::packUnorm4x8(triangle.color2); + + auto cn0 = reinterpret_cast(pn0); + auto cn1 = reinterpret_cast(pn1); + auto cn2 = reinterpret_cast(pn2); + + auto ct = reinterpret_cast(pt); + auto cbt = reinterpret_cast(pbt); + + auto cuv0 = reinterpret_cast(puv0); + auto cuv1 = reinterpret_cast(puv1); + auto cuv2 = reinterpret_cast(puv2); + + auto cc0 = reinterpret_cast(pc0); + auto cc1 = reinterpret_cast(pc1); + auto cc2 = reinterpret_cast(pc2); + + GPUTriangle gpuTriangle; + + gpuTriangle.v0 = vec4(triangle.v0, cn0); + gpuTriangle.v1 = vec4(triangle.v1, cn1); + gpuTriangle.v2 = vec4(triangle.v2, cn2); + gpuTriangle.d0 = vec4(cuv0, cuv1, cuv2, reinterpret_cast(triangle.materialIdx)); + gpuTriangle.d1 = vec4(ct, cbt, bvhTriangle.endOfNode ? 1.0f : -1.0f, 0.0f); + gpuTriangle.d2 = vec4(cc0, cc1, cc2, triangle.opacity); + + gpuTriangles.push_back(gpuTriangle); + + if (!hardwareRayTracing) { + GPUBVHTriangle gpuBvhTriangle; + gpuBvhTriangle.v0 = vec4(triangle.v0, bvhTriangle.endOfNode ? 1.0f : -1.0f); + gpuBvhTriangle.v1 = vec4(triangle.v1, reinterpret_cast(triangle.materialIdx)); + gpuBvhTriangle.v2 = vec4(triangle.v2, triangle.opacity); + + gpuBvhTriangles.push_back(gpuBvhTriangle); + } + + } + + triangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(GPUTriangle)); + triangleBuffer.SetSize(gpuTriangles.size(), gpuTriangles.data()); + + if (!hardwareRayTracing) { + bvhTriangleBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(GPUBVHTriangle)); + bvhTriangleBuffer.SetSize(gpuBvhTriangles.size(), gpuBvhTriangles.data()); + + gpuBvhTriangles.clear(); + gpuBvhTriangles.shrink_to_fit(); + + auto& nodes = bvh.GetTree(); + std::vector gpuBvhNodes(nodes.size()); + // Copy to GPU format + for (size_t i = 0; i < nodes.size(); i++) { + gpuBvhNodes[i].leftPtr = nodes[i].leftPtr; + gpuBvhNodes[i].rightPtr = nodes[i].rightPtr; + + gpuBvhNodes[i].leftAABB.min = nodes[i].leftAABB.min; + gpuBvhNodes[i].leftAABB.max = nodes[i].leftAABB.max; + + gpuBvhNodes[i].rightAABB.min = nodes[i].rightAABB.min; + gpuBvhNodes[i].rightAABB.max = nodes[i].rightAABB.max; + } + + blasNodeBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | Buffer::BufferUsageBits::DedicatedMemoryBit, sizeof(GPUBVHNode)); + blasNodeBuffer.SetSize(gpuBvhNodes.size(), gpuBvhNodes.data()); + } + + } + +} \ No newline at end of file diff --git a/src/engine/raytracing/BLAS.h b/src/engine/raytracing/BLAS.h new file mode 100644 index 000000000..684ebda8b --- /dev/null +++ b/src/engine/raytracing/BLAS.h @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include "Material.h" +#include "resource/Resource.h" +#include "buffer/Buffer.h" +#include "buffer/IndexBuffer.h" +#include "buffer/VertexBuffer.h" +#include "graphics/BLAS.h" +#include "graphics/ASBuilder.h" +#include "RTStructures.h" + +namespace Atlas::RayTracing { + + class BLAS { + + public: + struct Triangle { + vec3 v0; + vec3 v1; + vec3 v2; + + vec3 n0; + vec3 n1; + vec3 n2; + + vec2 uv0; + vec2 uv1; + vec2 uv2; + + vec4 color0; + vec4 color1; + vec4 color2; + + int32_t materialIdx = 0; + float opacity = -1.0f; + }; + + BLAS() = default; + + void Build(std::vector& triangles, std::vector>& materials); + + void Build(std::vector& triangles, std::vector>& materials, + Buffer::VertexBuffer& vertexBuffer, Buffer::IndexBuffer& indexBuffer, std::span geometryRegions); + + bool IsBuilt() const; + + std::vector> materials; + + std::vector gpuTriangles; + + Buffer::Buffer blasNodeBuffer; + Buffer::Buffer triangleBuffer; + Buffer::Buffer bvhTriangleBuffer; + Buffer::Buffer triangleOffsetBuffer; + + Ref blas = nullptr; + + std::atomic_bool needsBvhRefresh{ false }; + std::atomic_bool isBvhBuilt { false }; + + private: + void BuildBuffers(std::vector& triangles, std::vector>& materials); + + }; + +} \ No newline at end of file diff --git a/src/engine/raytracing/RTStructures.h b/src/engine/raytracing/RTStructures.h index 7e0235b0b..c37915772 100644 --- a/src/engine/raytracing/RTStructures.h +++ b/src/engine/raytracing/RTStructures.h @@ -48,7 +48,7 @@ namespace Atlas { int32_t valid = -1; }; - struct GPUMaterial { + struct alignas(16) GPUMaterial { int32_t ID = 0; vec3 baseColor = vec3(1.0f); @@ -64,6 +64,9 @@ namespace Atlas { float normalScale = 1.0f; + float tiling = 1.0f; + float terrainTiling = 1.0f; + int32_t invertUVs = 0; int32_t twoSided = 0; int32_t cullBackFaces = 0; @@ -75,6 +78,8 @@ namespace Atlas { int32_t roughnessTexture = -1; int32_t metalnessTexture = -1; int32_t aoTexture = -1; + int32_t emissiveTexture = -1; + int32_t terrainNormalTexture = -1; }; struct GPUAABB { diff --git a/src/engine/raytracing/RayTracingManager.cpp b/src/engine/raytracing/RayTracingManager.cpp new file mode 100644 index 000000000..b9e8bc69a --- /dev/null +++ b/src/engine/raytracing/RayTracingManager.cpp @@ -0,0 +1,109 @@ +#include "RayTracingManager.h" + +#include "graphics/GraphicsDevice.h" +#include "graphics/ASBuilder.h" +#include "resource/ResourceManager.h" +#include "mesh/Mesh.h" +#include "terrain/Terrain.h" + +namespace Atlas::RayTracing { + + JobGroup RayTracingManager::bvhUpdateGroup; + std::vector> RayTracingManager::blases; + + void RayTracingManager::Update() { + +#ifdef AE_BINDLESS + // This crashes when we start with path tracing and do the bvh build async + // Launch BVH builds asynchronously + auto buildRTStructure = [&](JobData) { + auto meshes = ResourceManager::GetResources(); + + JobGroup bvhBuildGroup; + for (const auto& mesh : meshes) { + if (!mesh.IsLoaded()) + continue; + if (mesh->IsBVHBuilt() || !mesh->rayTrace) + continue; + if (mesh->data.GetIndexCount() == 0 || + mesh->data.GetVertexCount() == 0) + continue; + + JobSystem::Execute(bvhBuildGroup, [mesh](JobData&) { + mesh->BuildBVH(false); + }); + } + + auto terrains = ResourceManager::GetResources(); + for (const auto& terrain : terrains) { + if (!terrain.IsLoaded()) + continue; + + auto& storage = terrain->storage; + for (int32_t i = 0; i < storage.cells.size(); i++) { + auto& lodCells = storage.cells[i]; + + for (auto& cell : lodCells) { + if (!cell.IsLoaded()) + continue; + if (cell.blas && cell.blas->IsBuilt()) + continue; + + float nodeStretch = terrain->resolution * powf(2.0f, + (float)(terrain->LoDCount - cell.LoD) - 1.0f); + float heightStretch = terrain->heightScale; + JobSystem::Execute(bvhBuildGroup, [cell = &cell, nodeStretch, heightStretch](JobData&) { + cell->BuildBVH(nodeStretch, heightStretch); + }); + } + } + } + + JobSystem::Wait(bvhBuildGroup); + + auto device = Graphics::GraphicsDevice::DefaultDevice; + if (device->support.hardwareRayTracing) + BuildStaticBLAS(meshes, terrains); + }; + + if (bvhUpdateGroup.HasFinished()) { + JobSystem::Execute(bvhUpdateGroup, buildRTStructure); + } +#endif + + } + + void RayTracingManager::BuildStaticBLAS(std::vector>& meshes, + std::vector>& terrains) { + + Graphics::ASBuilder asBuilder; + + for (auto it = meshes.begin(); it != meshes.end();) { + auto& mesh = *it; + + // Only want static meshes + if (!mesh.IsLoaded() || !mesh->IsBVHBuilt() || !mesh->blas->needsBvhRefresh || mesh->blas->blas->isDynamic) { + it = meshes.erase(it); + } + else { + blases.push_back(mesh->blas->blas); + ++it; + } + } + + size_t blasBuiltCount = 0; + if (!blases.empty()) { + blasBuiltCount = asBuilder.BuildBLAS(blases); + } + + // Copy the non-compacted versions over + for (size_t i = 0; i < blasBuiltCount; i++) { + meshes[i]->blas->blas = blases[i]; + meshes[i]->blas->needsBvhRefresh = false; + } + + blases.clear(); + + } + +} \ No newline at end of file diff --git a/src/engine/raytracing/RayTracingManager.h b/src/engine/raytracing/RayTracingManager.h new file mode 100644 index 000000000..77dc3180b --- /dev/null +++ b/src/engine/raytracing/RayTracingManager.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../System.h" +#include "jobsystem/JobGroup.h" +#include "graphics/BLAS.h" + +#include "terrain/Terrain.h" +#include "mesh/Mesh.h" + +namespace Atlas::RayTracing { + + class RayTracingManager { + + public: + static void Update(); + + private: + static void BuildStaticBLAS(std::vector>& meshes, + std::vector>& terrains); + + static JobGroup bvhUpdateGroup; + static std::vector> blases; + + }; + +} \ No newline at end of file diff --git a/src/engine/raytracing/RayTracingWorld.cpp b/src/engine/raytracing/RayTracingWorld.cpp index 0714843b3..08079c969 100644 --- a/src/engine/raytracing/RayTracingWorld.cpp +++ b/src/engine/raytracing/RayTracingWorld.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace Atlas { namespace RayTracing { @@ -36,67 +38,117 @@ namespace Atlas { if (!device->swapChain->isComplete) return; if (!subset.Any()) return; - blases.clear(); + auto renderState = &scene->renderState; + + buildBlases.clear(); auto meshes = scene->GetMeshes(); int32_t meshCount = 0; - JobSystem::Wait(scene->bindlessMeshMapUpdateJob); + JobSystem::Wait(renderState->bindlessBlasMapUpdateJob); + + std::swap(prevBlasInfos, blasInfos); + blasInfos.clear(); for (auto& mesh : meshes) { // Only need to check for this, since that means that the BVH was built and the mesh is loaded - if (!scene->meshIdToBindlessIdx.contains(mesh.GetID())) + if (!mesh.IsLoaded() || !renderState->blasToBindlessIdx.contains(mesh->blas)) continue; - if (!meshInfos.contains(mesh.GetID())) { - meshInfos[mesh.GetID()] = {}; + if (!prevBlasInfos.contains(mesh->blas)) { + blasInfos[mesh->blas] = BlasInfo{ + .mesh = mesh, + }; BuildTriangleLightsForMesh(mesh); } + else { + std::swap(blasInfos[mesh->blas], prevBlasInfos[mesh->blas]); + } - auto &meshInfo = meshInfos[mesh.GetID()]; - meshInfo.offset = int32_t(scene->meshIdToBindlessIdx[mesh.GetID()]); + auto &blasInfo = blasInfos[mesh->blas]; + blasInfo.offset = int32_t(renderState->blasToBindlessIdx[mesh->blas]); + blasInfo.cullingDistanceSqr = mesh->rayTraceDistanceCulling * mesh->rayTraceDistanceCulling; // Some extra path for hardware raytracing, don't want to do work twice if (hardwareRayTracing) { - if (mesh->needsBvhRefresh) { - blases.push_back(mesh->blas); - mesh->needsBvhRefresh = false; + if (mesh->blas->needsBvhRefresh && mesh->blas->blas->isDynamic) { + buildBlases.push_back(mesh->blas->blas); + mesh->blas->needsBvhRefresh = false; } - meshInfo.blas = mesh->blas; - meshInfo.idx = meshCount++; + blasInfo.idx = meshCount++; } } - if (hardwareRayTracing) { + for (const auto node : renderState->terrainLeafNodes) { + + if (!node->cell || !node->cell->IsLoaded() || !renderState->blasToBindlessIdx.contains(node->cell->blas)) + continue; + + if (!prevBlasInfos.contains(node->cell->blas)) { + blasInfos[node->cell->blas] = {}; + } + else { + std::swap(blasInfos[node->cell->blas], prevBlasInfos[node->cell->blas]); + } + + auto &blasInfo = blasInfos[node->cell->blas]; + + blasInfo.node = node; + blasInfo.offset = int32_t(renderState->blasToBindlessIdx[node->cell->blas]); + blasInfo.cullingDistanceSqr = 100000000000.0f; + } + + if (hardwareRayTracing) { Graphics::ASBuilder asBuilder; - if (!blases.empty()) - asBuilder.BuildBLAS(blases); + if (!buildBlases.empty()) { + auto commandList = device->GetCommandList(); + commandList->BeginCommands(); + asBuilder.BuildBLAS(buildBlases, commandList); + commandList->EndCommands(); + device->SubmitCommandList(commandList); + } } - for (auto& [_, meshInfo] : meshInfos) { - meshInfo.instanceIndices.clear(); - meshInfo.matrices.clear(); + for (auto& [_, blasInfo] : blasInfos) { + blasInfo.instanceIndices.clear(); + blasInfo.matrices.clear(); } hardwareInstances.clear(); gpuBvhInstances.clear(); - actorAABBs.clear(); + instanceAABBs.clear(); lastMatrices.clear(); - JobSystem::Wait(scene->bindlessTextureMapUpdateJob); + JobSystem::Wait(renderState->bindlessTextureMapUpdateJob); UpdateMaterials(); + JobSystem::Wait(renderState->mainCameraSignal, JobPriority::High); + + vec3 cameraLocation; + auto hasCamera = scene->HasMainCamera(); + if (hasCamera) { + auto& camera = scene->GetMainCamera(); + cameraLocation = camera.GetLocation(); + } + for (auto entity : subset) { const auto& [meshComponent, transformComponent] = subset.Get(entity); - if (!scene->meshIdToBindlessIdx.contains(meshComponent.mesh.GetID())) + if (!meshComponent.mesh.IsLoaded() || !renderState->blasToBindlessIdx.contains(meshComponent.mesh->blas)) continue; - actorAABBs.push_back(meshComponent.aabb); - auto &meshInfo = meshInfos[meshComponent.mesh.GetID()]; + auto &blasInfo = blasInfos[meshComponent.mesh->blas]; + auto distSqd = glm::distance2( + vec3(transformComponent.globalMatrix[3]), + cameraLocation); + if (hasCamera && distSqd > blasInfo.cullingDistanceSqr) + continue; + if (hardwareRayTracing && !meshComponent.mesh->blas->blas->isBuilt || meshComponent.mesh->blas->needsBvhRefresh) + continue; + instanceAABBs.push_back(meshComponent.aabb); auto inverseMatrix = mat3x4(glm::transpose(transformComponent.inverseGlobalMatrix)); uint32_t mask = InstanceCullMasks::MaskAll; @@ -104,14 +156,14 @@ namespace Atlas { GPUBVHInstance gpuBvhInstance = { .inverseMatrix = inverseMatrix, - .meshOffset = meshInfo.offset, - .materialOffset = meshInfo.materialOffset, + .meshOffset = blasInfo.offset, + .materialOffset = blasInfo.materialOffset, .mask = mask - }; + }; - meshInfo.matrices.emplace_back(transformComponent.globalMatrix); - meshInfo.instanceIndices.push_back(uint32_t(gpuBvhInstances.size())); - gpuBvhInstances.push_back(gpuBvhInstance); + blasInfo.matrices.emplace_back(transformComponent.globalMatrix); + blasInfo.instanceIndices.push_back(uint32_t(gpuBvhInstances.size())); + gpuBvhInstances.emplace_back(gpuBvhInstance); if (includeObjectHistory) lastMatrices.emplace_back(glm::transpose(transformComponent.lastGlobalMatrix)); @@ -125,8 +177,58 @@ namespace Atlas { std::memcpy(&transform, &transposed, sizeof(VkTransformMatrixKHR)); inst.transform = transform; - inst.instanceCustomIndex = meshInfo.offset; - inst.accelerationStructureReference = meshInfo.blas->bufferDeviceAddress; + inst.instanceCustomIndex = blasInfo.offset; + inst.accelerationStructureReference = meshComponent.mesh->blas->blas->bufferDeviceAddress; + inst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; + inst.mask = mask; + inst.instanceShaderBindingTableRecordOffset = 0; + hardwareInstances.push_back(inst); + } + } + + for (const auto node : renderState->terrainLeafNodes) { + if (!node->cell || !node->cell->IsLoaded() || !renderState->blasToBindlessIdx.contains(node->cell->blas)) + continue; + + auto &blasInfo = blasInfos[node->cell->blas]; + if (hardwareRayTracing && !node->cell->blas->blas->isBuilt || node->cell->blas->needsBvhRefresh) + continue; + + auto nodePosition = vec3(node->location.x, 0.0f, node->location.y); + mat4 globalMatrix = glm::translate(nodePosition), nodeScale; + mat4 inverseGlobalMatrix = glm::inverse(globalMatrix); + + instanceAABBs.push_back(node->cell->aabb.Translate(nodePosition)); + auto inverseMatrix = mat3x4(glm::transpose(inverseGlobalMatrix)); + + uint32_t mask = InstanceCullMasks::MaskAll; + mask |= InstanceCullMasks::MaskShadow; + + GPUBVHInstance gpuBvhInstance = { + .inverseMatrix = inverseMatrix, + .meshOffset = blasInfo.offset, + .materialOffset = blasInfo.materialOffset, + .mask = mask + }; + + blasInfo.matrices.emplace_back(globalMatrix); + blasInfo.instanceIndices.push_back(uint32_t(gpuBvhInstances.size())); + gpuBvhInstances.push_back(gpuBvhInstance); + + if (includeObjectHistory) + lastMatrices.emplace_back(glm::transpose(globalMatrix)); + + // Some extra path for hardware raytracing, don't want to do work twice + if (hardwareRayTracing) { + VkAccelerationStructureInstanceKHR inst = {}; + VkTransformMatrixKHR transform; + + auto transposed = glm::transpose(globalMatrix); + std::memcpy(&transform, &transposed, sizeof(VkTransformMatrixKHR)); + + inst.transform = transform; + inst.instanceCustomIndex = blasInfo.offset; + inst.accelerationStructureReference = node->cell->blas->blas->bufferDeviceAddress; inst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; inst.mask = mask; inst.instanceShaderBindingTableRecordOffset = 0; @@ -134,14 +236,17 @@ namespace Atlas { } } - if (gpuBvhInstances.empty()) + if (gpuBvhInstances.empty()) { + // Need to reset the TLAS in this case, since it might contain invalid memory addresses + tlas = nullptr; return; + } if (hardwareRayTracing) { UpdateForHardwareRayTracing(subset, gpuBvhInstances.size()); } else { - UpdateForSoftwareRayTracing(gpuBvhInstances, lastMatrices, actorAABBs); + UpdateForSoftwareRayTracing(gpuBvhInstances, lastMatrices, instanceAABBs); } if (updateTriangleLights) @@ -162,8 +267,7 @@ namespace Atlas { } void RayTracingWorld::UpdateMaterials() { - - std::vector materials; + UpdateMaterials(materials); } @@ -172,28 +276,28 @@ namespace Atlas { std::lock_guard lock(mutex); - auto meshes = scene->GetMeshes(); - materials.clear(); + auto sceneState = &scene->renderState; - for (auto& mesh : meshes) { - if (!meshInfos.contains(mesh.GetID()) || !mesh.IsLoaded()) - continue; + materials.clear(); - auto& meshInfo = meshInfos[mesh.GetID()]; - meshInfo.materialOffset = int32_t(materials.size()); + for (auto& [blas, blasInfo] : blasInfos) { + blasInfo.materialOffset = int32_t(materials.size()); int32_t meshMaterialID = 0; - for (auto& material : mesh->data.materials) { + for (auto& material : blas->materials) { GPUMaterial gpuMaterial; - size_t hash = mesh.GetID(); + size_t hash = 0; + HashCombine(hash, blas.get()); HashCombine(hash, meshMaterialID++); // Only is persistent when no materials are reorderd in mesh gpuMaterial.ID = int32_t(hash % 65535); if (material.IsLoaded()) { + auto& mesh = blasInfo.mesh; + gpuMaterial.baseColor = Common::ColorConverter::ConvertSRGBToLinear(material->baseColor); gpuMaterial.emissiveColor = Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor) * material->emissiveIntensity; @@ -208,33 +312,44 @@ namespace Atlas { gpuMaterial.normalScale = material->normalScale; - gpuMaterial.invertUVs = mesh->invertUVs ? 1 : 0; + gpuMaterial.tiling = material->tiling; + gpuMaterial.terrainTiling = blasInfo.node ? 1.0f / blasInfo.node->sideLength : 1.0f; + + gpuMaterial.invertUVs = mesh.IsLoaded() ? (mesh->invertUVs ? 1 : 0) : 0; gpuMaterial.twoSided = material->twoSided ? 1 : 0; - gpuMaterial.cullBackFaces = mesh->cullBackFaces ? 1 : 0; + gpuMaterial.cullBackFaces = mesh.IsLoaded() ? (mesh->cullBackFaces ? 1 : 0) : 0; gpuMaterial.useVertexColors = material->vertexColors ? 1 : 0; if (material->HasBaseColorMap()) { - gpuMaterial.baseColorTexture = scene->textureToBindlessIdx[material->baseColorMap.Get()]; + gpuMaterial.baseColorTexture = sceneState->textureToBindlessIdx[material->baseColorMap.Get()]; } if (material->HasOpacityMap()) { - gpuMaterial.opacityTexture = scene->textureToBindlessIdx[material->opacityMap.Get()]; + gpuMaterial.opacityTexture = sceneState->textureToBindlessIdx[material->opacityMap.Get()]; } if (material->HasNormalMap()) { - gpuMaterial.normalTexture = scene->textureToBindlessIdx[material->normalMap.Get()]; + gpuMaterial.normalTexture = sceneState->textureToBindlessIdx[material->normalMap.Get()]; } if (material->HasRoughnessMap()) { - gpuMaterial.roughnessTexture = scene->textureToBindlessIdx[material->roughnessMap.Get()]; + gpuMaterial.roughnessTexture = sceneState->textureToBindlessIdx[material->roughnessMap.Get()]; } if (material->HasMetalnessMap()) { - gpuMaterial.metalnessTexture = scene->textureToBindlessIdx[material->metalnessMap.Get()]; + gpuMaterial.metalnessTexture = sceneState->textureToBindlessIdx[material->metalnessMap.Get()]; } if (material->HasAoMap()) { - gpuMaterial.aoTexture = scene->textureToBindlessIdx[material->aoMap.Get()]; + gpuMaterial.aoTexture = sceneState->textureToBindlessIdx[material->aoMap.Get()]; + } + + if (material->HasEmissiveMap()) { + gpuMaterial.emissiveTexture = sceneState->textureToBindlessIdx[material->emissiveMap.Get()]; + } + + if (blasInfo.node) { + gpuMaterial.terrainNormalTexture = sceneState->textureToBindlessIdx[blasInfo.node->cell->normalMap]; } } @@ -245,14 +360,18 @@ namespace Atlas { if (materials.empty()) return; - materialBuffer.SetSize(materials.size()); - materialBuffer.SetData(materials.data(), 0, materials.size()); + if (materialBuffer.GetElementCount() < materials.size()) { + materialBuffer.SetSize(materials.size(), materials.data()); + } + else { + materialBuffer.SetData(materials.data(), 0, materials.size()); + } } void RayTracingWorld::Clear() { - meshInfos.clear(); + blasInfos.clear(); } @@ -260,14 +379,14 @@ namespace Atlas { auto device = Graphics::GraphicsDevice::DefaultDevice; - return materialBuffer.GetSize() > 0 && device->support.bindless && !meshInfos.empty(); + return materialBuffer.GetSize() > 0 && device->support.bindless && !blasInfos.empty(); } void RayTracingWorld::UpdateForSoftwareRayTracing(std::vector& gpuBvhInstances, - std::vector& lastMatrices, std::vector& actorAABBs) { + std::vector& lastMatrices, std::vector& instanceAABBs) { - auto bvh = Volume::BVH(actorAABBs); + auto bvh = Volume::BVH(instanceAABBs); auto& nodes = bvh.GetTree(); auto gpuBvhNodes = std::vector(nodes.size()); @@ -310,22 +429,21 @@ namespace Atlas { TransformComponent>& entitySubset, size_t instanceCount) { auto device = Graphics::GraphicsDevice::DefaultDevice; - - Graphics::ASBuilder asBuilder; +; auto tlasDesc = Graphics::TLASDesc(); tlas = device->CreateTLAS(tlasDesc); - asBuilder.BuildTLAS(tlas, hardwareInstances); + tlasBuilder.BuildTLAS(tlas, hardwareInstances); } void RayTracingWorld::BuildTriangleLightsForMesh(ResourceHandle &mesh) { - auto& gpuTriangles = mesh->data.gpuTriangles; - auto& materials = mesh->data.materials; + auto& gpuTriangles = mesh->blas->gpuTriangles; + auto& materials = mesh->blas->materials; - auto& meshInfo = meshInfos[mesh.GetID()]; - meshInfo.triangleLights.clear(); + auto& blasInfo = blasInfos[mesh->blas]; + blasInfo.triangleLights.clear(); // Triangle lights for (size_t i = 0; i < gpuTriangles.size(); i++) { @@ -365,11 +483,11 @@ namespace Atlas { GPULight light; light.P = vec4(P, 1.0f); - light.N = vec4(N, 0.0f); + light.N = vec4(N, area); light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(radiance) * material->emissiveIntensity, 0.0f); - light.data = vec4(cd, weight, area, 0.0f); + light.data = vec4(cd, weight, 0.0, 0.0f); - meshInfo.triangleLights.push_back(light); + blasInfo.triangleLights.push_back(light); } } @@ -379,14 +497,14 @@ namespace Atlas { triangleLights.clear(); - for (auto& [meshIdx, meshInfo] : meshInfos) { + for (auto& [meshIdx, blasInfo] : blasInfos) { - for (auto& light : meshInfo.triangleLights) { + for (auto& light : blasInfo.triangleLights) { - for (size_t i = 0; i < meshInfo.instanceIndices.size(); i++) { + for (size_t i = 0; i < blasInfo.instanceIndices.size(); i++) { - auto instanceIdx = meshInfo.instanceIndices[i]; - auto& matrix = meshInfo.matrices[i]; + auto instanceIdx = blasInfo.instanceIndices[i]; + auto& matrix = blasInfo.matrices[i]; vec3 P = matrix * vec4(vec3(light.P), 1.0f); vec3 N = matrix * vec4(vec3(light.N), 0.0f); diff --git a/src/engine/raytracing/RayTracingWorld.h b/src/engine/raytracing/RayTracingWorld.h index bbc65e2fe..f3f87b407 100644 --- a/src/engine/raytracing/RayTracingWorld.h +++ b/src/engine/raytracing/RayTracingWorld.h @@ -3,8 +3,11 @@ #include "System.h" #include "RTStructures.h" +#include "BLAS.h" #include "scene/Subset.h" + +#include "terrain/TerrainNode.h" #include "scene/components/MeshComponent.h" #include "scene/components/TransformComponent.h" @@ -47,12 +50,15 @@ namespace Atlas { bool includeObjectHistory = false; private: - struct MeshInfo { - Ref blas = nullptr; + struct BlasInfo { + ResourceHandle mesh; + Terrain::TerrainNode* node = nullptr; int32_t offset = 0; int32_t materialOffset = 0; + float cullingDistanceSqr = 0.0f; + int32_t idx = 0; std::vector triangleLights; @@ -63,7 +69,7 @@ namespace Atlas { void UpdateMaterials(std::vector& materials); void UpdateForSoftwareRayTracing(std::vector& gpuBvhInstances, - std::vector& lastMatrices, std::vector& actorAABBs); + std::vector& lastMatrices, std::vector& instanceAABBs); void UpdateForHardwareRayTracing(Scene::Subset& entitySubset, size_t instanceCount); @@ -74,8 +80,10 @@ namespace Atlas { Scene::Scene* scene; + Graphics::ASBuilder tlasBuilder; Ref tlas; - std::vector> blases; + + std::vector> buildBlases; Buffer::Buffer materialBuffer; Buffer::Buffer bvhInstanceBuffer; @@ -84,12 +92,15 @@ namespace Atlas { std::vector hardwareInstances; std::vector gpuBvhInstances; - std::vector actorAABBs; + std::vector instanceAABBs; std::vector lastMatrices; std::vector triangleLights; - std::unordered_map meshInfos; + std::unordered_map, BlasInfo> blasInfos; + std::unordered_map, BlasInfo> prevBlasInfos; + + std::vector materials; bool hardwareRayTracing = false; diff --git a/src/engine/renderer/AORenderer.cpp b/src/engine/renderer/AORenderer.cpp index 13db76bd1..a3d1f4888 100644 --- a/src/engine/renderer/AORenderer.cpp +++ b/src/engine/renderer/AORenderer.cpp @@ -10,9 +10,16 @@ namespace Atlas { this->device = device; - const int32_t filterSize = 4; blurFilter.CalculateGaussianFilter(float(filterSize) / 3.0f, filterSize); + std::vector blurKernelWeights; + std::vector blurKernelOffsets; + blurFilter.GetLinearized(&blurKernelWeights, &blurKernelOffsets, false); + + auto mean = (blurKernelWeights.size() - 1) / 2; + blurKernelWeights = std::vector(blurKernelWeights.begin() + mean, blurKernelWeights.end()); + blurKernelOffsets = std::vector(blurKernelOffsets.begin() + mean, blurKernelOffsets.end()); + auto noiseImage = Loader::ImageLoader::LoadImage("scrambling_ranking.png", false, 4); scramblingRankingTexture = Texture::Texture2D(noiseImage->width, noiseImage->height, VK_FORMAT_R8G8B8A8_UNORM); @@ -36,7 +43,8 @@ namespace Atlas { ssUniformBuffer = Buffer::UniformBuffer(sizeof(SSUniforms)); // If we don't set the element size to the whole thing, otherwise uniform buffer element alignment kicks in ssSamplesUniformBuffer = Buffer::UniformBuffer(sizeof(vec4) * 64); - blurWeightsUniformBuffer = Buffer::UniformBuffer(sizeof(float) * (size_t(filterSize) + 1)); + blurWeightsUniformBuffer = Buffer::Buffer(Buffer::BufferUsageBits::UniformBufferBit, + sizeof(float) * (size_t(filterSize) + 1), 1, blurKernelWeights.data()); } @@ -56,12 +64,12 @@ namespace Atlas { if (!target->HasHistory()) { VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - std::vector bufferBarriers; - std::vector imageBarriers = { + + Graphics::ImageBarrier imageBarriers[] = { {target->historyAoTexture.image, layout, access}, {target->historyAoLengthTexture.image, layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); } auto downsampledRT = target->GetData(target->GetAOResolution()); @@ -158,9 +166,6 @@ namespace Atlas { if (ao->rt) { Graphics::Profiler::EndAndBeginQuery("Temporal filter"); - std::vector imageBarriers; - std::vector bufferBarriers; - ivec2 groupCount = ivec2(res.x / 16, res.y / 16); groupCount.x += ((groupCount.x * 16 == res.x) ? 0 : 1); groupCount.y += ((groupCount.y * 16 == res.y) ? 0 : 1); @@ -168,11 +173,11 @@ namespace Atlas { auto pipeline = PipelineManager::GetPipeline(temporalPipelineConfig); commandList->BindPipeline(pipeline); - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->aoTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {target->aoLengthTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(target->aoTexture.image, 3, 0); commandList->BindImage(target->aoLengthTexture.image, 3, 1); @@ -196,26 +201,26 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier preCopyImageBarriers[] = { {target->aoTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->aoLengthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->historyAoTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, {target->historyAoLengthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preCopyImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(target->aoTexture.image, target->historyAoTexture.image); commandList->CopyImage(target->aoLengthTexture.image, target->historyAoLengthTexture.image); // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier postCopyImageBarriers[] = { {target->aoTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->aoLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyAoTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyAoLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postCopyImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } @@ -224,37 +229,24 @@ namespace Atlas { const int32_t groupSize = 256; - std::vector kernelWeights; - std::vector kernelOffsets; - - blurFilter.GetLinearized(&kernelWeights, &kernelOffsets, false); - - auto mean = (kernelWeights.size() - 1) / 2; - kernelWeights = std::vector(kernelWeights.begin() + mean, kernelWeights.end()); - kernelOffsets = std::vector(kernelOffsets.begin() + mean, kernelOffsets.end()); - - auto kernelSize = int32_t(kernelWeights.size() - 1); + auto kernelSize = int32_t(filterSize); auto horizontalBlurPipeline = PipelineManager::GetPipeline(horizontalBlurPipelineConfig); auto verticalBlurPipeline = PipelineManager::GetPipeline(verticalBlurPipelineConfig); - blurWeightsUniformBuffer.SetData(kernelWeights.data(), 0); - commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 2); commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 3); commandList->BindBuffer(blurWeightsUniformBuffer.Get(), 3, 4); - std::vector imageBarriers; - std::vector bufferBarriers; - for (int32_t i = 0; i < 3; i++) { ivec2 groupCount = ivec2(res.x / groupSize, res.y); groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); - imageBarriers = { + + Graphics::ImageBarrier horizImageBarriers[] = { {target->aoTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->swapAoTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(horizImageBarriers, {}); commandList->BindPipeline(horizontalBlurPipeline); commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); @@ -267,11 +259,11 @@ namespace Atlas { groupCount = ivec2(res.x, res.y / groupSize); groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); - imageBarriers = { + Graphics::ImageBarrier vertImageBarriers[] = { {target->swapAoTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->aoTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(vertImageBarriers, {}); commandList->BindPipeline(verticalBlurPipeline); commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); diff --git a/src/engine/renderer/AORenderer.h b/src/engine/renderer/AORenderer.h index 23c04c5ca..4afde5683 100644 --- a/src/engine/renderer/AORenderer.h +++ b/src/engine/renderer/AORenderer.h @@ -46,7 +46,9 @@ namespace Atlas { Buffer::UniformBuffer rtUniformBuffer; Buffer::UniformBuffer ssUniformBuffer; Buffer::UniformBuffer ssSamplesUniformBuffer; - Buffer::UniformBuffer blurWeightsUniformBuffer; + Buffer::Buffer blurWeightsUniformBuffer; + + const int32_t filterSize = 4; }; diff --git a/src/engine/renderer/AtmosphereRenderer.cpp b/src/engine/renderer/AtmosphereRenderer.cpp index eb3502145..0ecb8c3a2 100644 --- a/src/engine/renderer/AtmosphereRenderer.cpp +++ b/src/engine/renderer/AtmosphereRenderer.cpp @@ -55,12 +55,11 @@ namespace Atlas { }; uniformBuffer.SetData(&uniforms, 0); - std::vector bufferBarriers; - std::vector imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {velocityTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(preImageBarriers, {}); commandList->BindImage(target->lightingTexture.image, 3, 0); commandList->BindImage(velocityTexture->image, 3, 1); @@ -76,11 +75,11 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {target->lightingTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {velocityTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); Graphics::Profiler::EndQuery(); diff --git a/src/engine/renderer/DDGIRenderer.cpp b/src/engine/renderer/DDGIRenderer.cpp index 1dbd13361..c04bcb01a 100644 --- a/src/engine/renderer/DDGIRenderer.cpp +++ b/src/engine/renderer/DDGIRenderer.cpp @@ -27,11 +27,6 @@ namespace Atlas { irradianceCopyEdgePipelineConfig = PipelineConfig("ddgi/copyEdge.csh", {"IRRADIANCE"}); momentsCopyEdgePipelineConfig = PipelineConfig("ddgi/copyEdge.csh"); - probeDebugMaterial = CreateRef(); - probeDebugActiveMaterial = CreateRef(); - probeDebugInactiveMaterial = CreateRef(); - probeDebugOffsetMaterial = CreateRef(); - auto samplerDesc = Graphics::SamplerDesc { .filter = VK_FILTER_NEAREST, .mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, @@ -50,10 +45,13 @@ namespace Atlas { Graphics::Profiler::BeginQuery("DDGI"); Ref shadow = nullptr; - auto mainLightEntity = GetMainLightEntity(scene); - if (mainLightEntity.IsValid()) - shadow = mainLightEntity.GetComponent().shadow; - + if (scene->HasMainLight()) + shadow = scene->GetMainLight().shadow; + + auto clouds = scene->sky.clouds; + auto cloudShadowEnabled = clouds && clouds->enable && clouds->castShadow; + + rayHitPipelineConfig.ManageMacro("CLOUD_SHADOWS", cloudShadowEnabled && scene->HasMainLight()); rayHitPipelineConfig.ManageMacro("DDGI_VISIBILITY", volume->visibility); rayHitPipelineConfig.ManageMacro("USE_SHADOW_MAP", shadow && volume->useShadowMap); @@ -144,7 +142,7 @@ namespace Atlas { shadowUniform.cascadeCount = shadow->viewCount; shadowUniform.resolution = vec2(shadow->resolution); - commandList->BindImage(shadow->maps.image, shadowSampler, 3, 0); + commandList->BindImage(shadow->maps->image, shadowSampler, 3, 0); auto componentCount = shadow->viewCount; for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { @@ -155,8 +153,8 @@ namespace Atlas { auto texelSize = glm::max(abs(corners[0].x - corners[1].x), abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; shadowUniform.cascades[i].distance = cascade->farDistance; - shadowUniform.cascades[i].cascadeSpace = cascade->projectionMatrix * - cascade->viewMatrix; + shadowUniform.cascades[i].cascadeSpace = glm::transpose(cascade->projectionMatrix * + cascade->viewMatrix); shadowUniform.cascades[i].texelSize = texelSize; } else { auto cascade = &shadow->views[componentCount - 1]; @@ -164,11 +162,16 @@ namespace Atlas { } } } + + if (cloudShadowEnabled && scene->HasMainLight()) { + clouds->shadowTexture.Bind(commandList, 3, 1); + } + rayHitUniformBuffer.SetData(&uniforms, 0); // Use this buffer instead of the default writeRays buffer of the helper - commandList->BindBuffer(rayHitBuffer.Get(), 3, 1); - commandList->BindBuffer(rayHitUniformBuffer.Get(), 3, 2); + commandList->BindBuffer(rayHitBuffer.Get(), 3, 2); + commandList->BindBuffer(rayHitUniformBuffer.Get(), 3, 3); } ); @@ -317,17 +320,13 @@ namespace Atlas { auto pipeline = PipelineManager::GetPipeline(pipelineConfig); commandList->BindPipeline(pipeline); - probeDebugActiveMaterial->emissiveColor = vec3(0.0f, 1.0f, 0.0f); - probeDebugInactiveMaterial->emissiveColor = vec3(1.0f, 0.0f, 0.0f); - probeDebugOffsetMaterial->emissiveColor = vec3(0.0f, 0.0f, 1.0f); - sphereArray.Bind(commandList); ProbeDebugConstants constants = { - .probeMaterialIdx = uint32_t(materialMap[probeDebugMaterial.get()]), - .probeActiveMaterialIdx = uint32_t(materialMap[probeDebugActiveMaterial.get()]), - .probeInactiveMaterialIdx = uint32_t(materialMap[probeDebugInactiveMaterial.get()]), - .probeOffsetMaterialIdx = uint32_t(materialMap[probeDebugOffsetMaterial.get()]) + .probeMaterialIdx = uint32_t(materialMap[internalVolume.probeDebugMaterial.get()]), + .probeActiveMaterialIdx = uint32_t(materialMap[internalVolume.probeDebugActiveMaterial.get()]), + .probeInactiveMaterialIdx = uint32_t(materialMap[internalVolume.probeDebugInactiveMaterial.get()]), + .probeOffsetMaterialIdx = uint32_t(materialMap[internalVolume.probeDebugOffsetMaterial.get()]) }; commandList->PushConstants("constants", &constants); diff --git a/src/engine/renderer/DDGIRenderer.h b/src/engine/renderer/DDGIRenderer.h index 65f9bb360..0036df0f6 100644 --- a/src/engine/renderer/DDGIRenderer.h +++ b/src/engine/renderer/DDGIRenderer.h @@ -23,12 +23,6 @@ namespace Atlas { void DebugProbes(Ref target, Ref scene, Graphics::CommandList* commandList, std::unordered_map& materialMap); - // Used for debugging - Ref probeDebugMaterial; - Ref probeDebugActiveMaterial; - Ref probeDebugInactiveMaterial; - Ref probeDebugOffsetMaterial; - private: struct alignas(16) RayGenUniforms { mat4 rotationMatrix; diff --git a/src/engine/renderer/DirectLightRenderer.cpp b/src/engine/renderer/DirectLightRenderer.cpp index 3c99ed226..990fbae8c 100644 --- a/src/engine/renderer/DirectLightRenderer.cpp +++ b/src/engine/renderer/DirectLightRenderer.cpp @@ -8,13 +8,7 @@ namespace Atlas { this->device = device; - auto bufferDesc = Graphics::BufferDesc { - .usageFlags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - .domain = Graphics::BufferDomain::Host, - .size = sizeof(Uniforms) - }; - uniformBuffer = Buffer::UniformBuffer(sizeof(Uniforms)); - cloudShadowUniformBuffer = Buffer::UniformBuffer(sizeof(CloudShadow)); + lightCullingBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(uint32_t)); pipelineConfig = PipelineConfig("deferred/direct.csh"); @@ -27,108 +21,104 @@ namespace Atlas { } - void DirectLightRenderer::Render(Ref target, Ref scene, Graphics::CommandList* commandList) { + void DirectLightRenderer::Render(Ref target, Ref scene, + Graphics::CommandList* commandList) { - auto mainLightEntity = GetMainLightEntity(scene); - if (!mainLightEntity.IsValid()) return; + auto renderState = &scene->renderState; + if (renderState->lightEntities.empty()) return; Graphics::Profiler::BeginQuery("Direct lighting"); auto& camera = scene->GetMainCamera(); - auto& light = mainLightEntity.GetComponent(); auto sss = scene->sss; auto clouds = scene->sky.clouds; - vec3 direction = normalize(vec3(camera.viewMatrix * - vec4(light.transformedProperties.directional.direction, 0.0f))); - - Uniforms uniforms; - - auto& lightUniform = uniforms.light; - lightUniform.location = vec4(0.0f); - lightUniform.direction = vec4(direction, 0.0f); - lightUniform.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(light.color), 0.0f); - lightUniform.intensity = light.intensity; - lightUniform.scatteringFactor = 1.0f; - lightUniform.radius = 1.0f; - - if (light.shadow) { - auto shadow = light.shadow; - auto& shadowUniform = lightUniform.shadow; - shadowUniform.distance = !shadow->longRange ? shadow->distance : shadow->longRangeDistance; - shadowUniform.bias = shadow->bias; - shadowUniform.edgeSoftness = shadow->edgeSoftness; - shadowUniform.cascadeBlendDistance = shadow->cascadeBlendDistance; - shadowUniform.cascadeCount = shadow->viewCount; - shadowUniform.resolution = vec2(shadow->resolution); - - if (shadow->useCubemap) { - commandList->BindImage(shadow->cubemap.image, shadowSampler, 3, 1); - } - else { - commandList->BindImage(shadow->maps.image, shadowSampler, 3, 1); - } + ivec2 res = ivec2(target->GetScaledWidth(), target->GetScaledHeight()); + int32_t groupSize = 16; + ivec2 groupCount = res / groupSize; + groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); + groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + + if (lightCullingBuffer.GetElementCount() < groupCount.x * groupCount.y * 128) { + lightCullingBuffer.SetSize(groupCount.x * groupCount.y * 128); + } + + Graphics::Profiler::BeginQuery("Light culling"); + + commandList->BufferMemoryBarrier(lightCullingBuffer.Get(), VK_ACCESS_SHADER_WRITE_BIT); + + CullingPushConstants cullingPushConstants; +#ifdef AE_BINDLESS + cullingPushConstants.lightCount = std::min(4096, int32_t(renderState->lightEntities.size())); +#else + cullingPushConstants.lightCount = std::min(8, int32_t(renderState->lightEntities.size())); +#endif + + lightCullingBuffer.Bind(commandList, 3, 6); + + auto cullingPipelineConfig = PipelineConfig("deferred/lightCulling.csh"); + auto pipeline = PipelineManager::GetPipeline(cullingPipelineConfig); + commandList->BindPipeline(pipeline); - auto componentCount = shadow->viewCount; - for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { - if (i < componentCount) { - auto cascade = &shadow->views[i]; - auto frustum = Volume::Frustum(cascade->frustumMatrix); - auto corners = frustum.GetCorners(); - auto texelSize = glm::max(abs(corners[0].x - corners[1].x), - abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; - shadowUniform.cascades[i].distance = cascade->farDistance; - shadowUniform.cascades[i].cascadeSpace = cascade->projectionMatrix * - cascade->viewMatrix * camera.invViewMatrix; - shadowUniform.cascades[i].texelSize = texelSize; + commandList->PushConstants("constants", &cullingPushConstants); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + commandList->BufferMemoryBarrier(lightCullingBuffer.Get(), VK_ACCESS_SHADER_READ_BIT); + + Graphics::Profiler::EndAndBeginQuery("Lighting"); + + PushConstants pushConstants; +#ifdef AE_BINDLESS + pushConstants.lightCount = std::min(4096, int32_t(renderState->lightEntities.size())); + pushConstants.lightBucketCount = int32_t(std::ceil(float(pushConstants.lightCount) / 32.0f)); +#else + pushConstants.lightCount = std::min(8, int32_t(renderState->lightEntities.size())); + pushConstants.lightBucketCount = 1; + + std::vector> cascadeMaps; + std::vector> cubeMaps; + for (int32_t i = 0; i < pushConstants.lightCount; i++) { + auto& comp = renderState->lightEntities[i].comp; + + if (comp.shadow) { + auto& shadow = comp.shadow; + if (shadow->useCubemap) { + cubeMaps.push_back(shadow->cubemap->image); } else { - auto cascade = &shadow->views[componentCount - 1]; - shadowUniform.cascades[i].distance = cascade->farDistance; + cascadeMaps.push_back(shadow->maps->image); } } } - uniformBuffer.SetData(&uniforms, 0); + commandList->BindSampledImages(cascadeMaps, 3, 7); + commandList->BindSampledImages(cubeMaps, 3, 15); +#endif - pipelineConfig.ManageMacro("SHADOWS", light.shadow != nullptr); pipelineConfig.ManageMacro("SCREEN_SPACE_SHADOWS", sss && sss->enable); - pipelineConfig.ManageMacro("CLOUD_SHADOWS", clouds && clouds->enable && clouds->castShadow); - auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + pipelineConfig.ManageMacro("CLOUD_SHADOWS", clouds && clouds->enable && clouds->castShadow && scene->HasMainLight()); + pipeline = PipelineManager::GetPipeline(pipelineConfig); commandList->BindPipeline(pipeline); commandList->BindImage(target->lightingTexture.image, 3, 0); - uniformBuffer.Bind(commandList, 3, 4); if (sss && sss->enable) { - commandList->BindImage(target->sssTexture.image, target->sssTexture.sampler, 3, 2); + commandList->BindImage(target->sssTexture.image, target->sssTexture.sampler, 3, 1); } - CloudShadow cloudShadowUniform; - if (clouds && clouds->enable && clouds->castShadow) { - clouds->shadowTexture.Bind(commandList, 3, 3); - - clouds->GetShadowMatrices(camera, glm::normalize(light.transformedProperties.directional.direction), - cloudShadowUniform.vMatrix, cloudShadowUniform.pMatrix); + commandList->BindSampler(shadowSampler, 3, 4); - cloudShadowUniform.ivMatrix = glm::inverse(cloudShadowUniform.vMatrix); - cloudShadowUniform.ipMatrix = glm::inverse(cloudShadowUniform.pMatrix); - - cloudShadowUniform.vMatrix = cloudShadowUniform.vMatrix * camera.invViewMatrix; + if (clouds && clouds->enable && clouds->castShadow && scene->HasMainLight()) { + clouds->shadowTexture.Bind(commandList, 3, 2); } - cloudShadowUniformBuffer.SetData(&cloudShadowUniform, 0); - cloudShadowUniformBuffer.Bind(commandList, 3, 5); - - ivec2 res = ivec2(target->GetScaledWidth(), target->GetScaledHeight()); - int32_t groupSize = 8; - ivec2 groupCount = res / groupSize; - groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); - groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + commandList->PushConstants("constants", &pushConstants); commandList->Dispatch(groupCount.x, groupCount.y, 1); Graphics::Profiler::EndQuery(); + Graphics::Profiler::EndQuery(); } diff --git a/src/engine/renderer/DirectLightRenderer.h b/src/engine/renderer/DirectLightRenderer.h index dab537b2e..2537958b5 100644 --- a/src/engine/renderer/DirectLightRenderer.h +++ b/src/engine/renderer/DirectLightRenderer.h @@ -13,17 +13,24 @@ namespace Atlas { void Init(Graphics::GraphicsDevice* device); - void Render(Ref target, Ref scene, Graphics::CommandList* commandList); + void Render(Ref target, Ref scene, + Graphics::CommandList* commandList); private: - struct alignas(16) Uniforms { - Light light; + struct alignas(16) CullingPushConstants { + int32_t lightCount; + }; + + struct alignas(16) PushConstants { + int32_t lightCount; + int32_t lightBucketCount; + int32_t padding1; + int32_t padding2; }; PipelineConfig pipelineConfig; - Buffer::UniformBuffer uniformBuffer; - Buffer::UniformBuffer cloudShadowUniformBuffer; + Buffer::Buffer lightCullingBuffer; Ref shadowSampler; }; diff --git a/src/engine/renderer/ExampleRenderer.cpp b/src/engine/renderer/ExampleRenderer.cpp index 5ad5b3a08..a727a6fdd 100644 --- a/src/engine/renderer/ExampleRenderer.cpp +++ b/src/engine/renderer/ExampleRenderer.cpp @@ -235,12 +235,11 @@ namespace Atlas { Graphics::Profiler::EndAndBeginQuery("Copy image"); { - auto imageBarriers = std::vector { + Graphics::ImageBarrier imageBarriers[] = { {destinationImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, {mainFrameBuffer->GetColorImage(0), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT} }; - auto bufferBarriers = std::vector(); - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); // Copy to other image diff --git a/src/engine/renderer/FSR2Renderer.cpp b/src/engine/renderer/FSR2Renderer.cpp index ba98a0abf..15c905d2c 100644 --- a/src/engine/renderer/FSR2Renderer.cpp +++ b/src/engine/renderer/FSR2Renderer.cpp @@ -831,17 +831,16 @@ namespace Atlas::Renderer { auto& taa = scene->postProcessing.taa; - std::vector bufferBarriers; - std::vector imageBarriers; - imageBarriers.push_back({ outputImage,VK_IMAGE_LAYOUT_GENERAL,VK_ACCESS_SHADER_WRITE_BIT }); - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + Graphics::ImageBarrier imageBarriers[] = {{outputImage,VK_IMAGE_LAYOUT_GENERAL,VK_ACCESS_SHADER_WRITE_BIT}}; + commandList->PipelineBarrier(imageBarriers, {}); FfxFsr2DispatchDescription dispatchParameters = {}; dispatchParameters.commandList = commandList; dispatchParameters.color = GetResource(colorImage, L"FSR2_InputColor", FFX_RESOURCE_STATE_COMPUTE_READ); dispatchParameters.depth = GetResource(depthImage, L"FSR2_InputDepth", FFX_RESOURCE_STATE_COMPUTE_READ); dispatchParameters.motionVectors = GetResource(velocityImage, L"FSR2_InputMotionVectors", FFX_RESOURCE_STATE_COMPUTE_READ); - dispatchParameters.reactive = GetResource(reactiveMaskImage, L"FSR2_EmptyInputReactiveMap", FFX_RESOURCE_STATE_COMPUTE_READ); + if (!target->IsUsedForPathTracing()) + dispatchParameters.reactive = GetResource(reactiveMaskImage, L"FSR2_EmptyInputReactiveMap", FFX_RESOURCE_STATE_COMPUTE_READ); //dispatchParameters.exposure = GetTextureResource(&context, nullptr, nullptr, 1, 1, VK_FORMAT_UNDEFINED, L"FSR2_InputExposure", FFX_RESOURCE_STATE_COMPUTE_READ); //dispatchParameters.reactive = GetTextureResource(&context, nullptr, nullptr, 1, 1, VK_FORMAT_UNDEFINED, L"FSR2_EmptyInputReactiveMap", FFX_RESOURCE_STATE_COMPUTE_READ); //dispatchParameters.transparencyAndComposition = GetTextureResource(&context, nullptr, nullptr, 1, 1, VK_FORMAT_UNDEFINED, L"FSR2_EmptyTransparencyAndCompositionMap", FFX_RESOURCE_STATE_COMPUTE_READ); diff --git a/src/engine/renderer/GBufferRenderer.cpp b/src/engine/renderer/GBufferRenderer.cpp index 117830b13..115ff9879 100644 --- a/src/engine/renderer/GBufferRenderer.cpp +++ b/src/engine/renderer/GBufferRenderer.cpp @@ -11,7 +11,7 @@ namespace Atlas { downscalePipelineConfig = PipelineConfig("gbuffer/downsampleGBuffer2x.csh"); downscaleDepthOnlyPipelineConfig = PipelineConfig("gbuffer/downsampleGBuffer2x.csh", {"DEPTH_ONLY"}); - patchNormalPipelineConfig = PipelineConfig("gbuffer/patchGBufferNormals.csh"); + patchNormalPipelineConfig = PipelineConfig("gbuffer/patchGBuffer.csh"); generateReactiveMaskPipelineConfig = PipelineConfig("gbuffer/generateReactiveMask.csh"); } @@ -48,9 +48,9 @@ namespace Atlas { } - void GBufferRenderer::FillNormalTexture(const Ref& target, Graphics::CommandList* commandList) { + void GBufferRenderer::Patch(const Ref& target, Graphics::CommandList* commandList) { - Graphics::Profiler::BeginQuery("Patch GBuffer normals"); + Graphics::Profiler::BeginQuery("Patch GBuffer"); auto pipeline = PipelineManager::GetPipeline(patchNormalPipelineConfig); commandList->BindPipeline(pipeline); @@ -59,6 +59,7 @@ namespace Atlas { auto normal = rt->normalTexture; auto geometryNormal = rt->geometryNormalTexture; + auto roughnessMetallicAo = rt->roughnessMetallicAoTexture; auto materialIdx = rt->materialIdxTexture; ivec2 res = ivec2(normal->width, normal->height); @@ -68,10 +69,12 @@ namespace Atlas { groupCount.y += ((res.y % 8 == 0) ? 0 : 1); commandList->BindImage(normal->image, 3, 0); - commandList->BindImage(geometryNormal->image, geometryNormal->sampler, 3, 1); - commandList->BindImage(materialIdx->image, materialIdx->sampler, 3, 2); + commandList->BindImage(roughnessMetallicAo->image, 3, 1); + commandList->BindImage(geometryNormal->image, geometryNormal->sampler, 3, 2); + commandList->BindImage(materialIdx->image, materialIdx->sampler, 3, 3); - commandList->ImageMemoryBarrier(normal->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); + commandList->ImageMemoryBarrier(normal->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + commandList->ImageMemoryBarrier(roughnessMetallicAo->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); commandList->Dispatch(groupCount.x, groupCount.y, 1); @@ -92,6 +95,7 @@ namespace Atlas { auto reactiveMaskTexture = target->reactiveMaskTexture; auto stencilTexture = rt->stencilTexture; + auto roughnessMetallicAo = rt->roughnessMetallicAoTexture; ivec2 res = ivec2(reactiveMaskTexture.width, reactiveMaskTexture.height); @@ -101,6 +105,7 @@ namespace Atlas { commandList->BindImage(reactiveMaskTexture.image, 3, 0); commandList->BindImage(stencilTexture->image, stencilTexture->sampler, 3, 1); + commandList->BindImage(roughnessMetallicAo->image, roughnessMetallicAo->sampler, 3, 2); commandList->ImageMemoryBarrier(reactiveMaskTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); @@ -144,17 +149,17 @@ namespace Atlas { commandList->BindImage(velocityIn->image, velocityIn->sampler, 3 , 4); commandList->BindImage(materialIdxIn->image, materialIdxIn->sampler, 3 , 5); - std::vector imageBarriers; - std::vector bufferBarriers; - imageBarriers.push_back({depthOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({normalOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({geometryNormalOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({roughnessMetallicAoOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({velocityOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({materialIdxOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({offsetOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}); + Graphics::ImageBarrier preImageBarriers[] = { + {depthOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {normalOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {geometryNormalOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {roughnessMetallicAoOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {velocityOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {materialIdxOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + {offsetOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} + }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(preImageBarriers, {}); commandList->BindImage(depthOut->image, 3 , 6); commandList->BindImage(normalOut->image, 3 , 7); @@ -166,16 +171,17 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); - imageBarriers.clear(); - imageBarriers.push_back({depthOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({normalOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({geometryNormalOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({roughnessMetallicAoOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({velocityOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({materialIdxOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({offsetOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}); - - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + Graphics::ImageBarrier postImageBarriers[] = { + {depthOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {normalOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {geometryNormalOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {roughnessMetallicAoOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {velocityOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {materialIdxOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {offsetOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT} + }; + + commandList->PipelineBarrier(postImageBarriers, {}); } diff --git a/src/engine/renderer/GBufferRenderer.h b/src/engine/renderer/GBufferRenderer.h index 38ec44ab0..705d4d60b 100644 --- a/src/engine/renderer/GBufferRenderer.h +++ b/src/engine/renderer/GBufferRenderer.h @@ -17,7 +17,7 @@ namespace Atlas { void DownscaleDepthOnly(const Ref& target, Graphics::CommandList* commandList); - void FillNormalTexture(const Ref& target, Graphics::CommandList* commandList); + void Patch(const Ref& target, Graphics::CommandList* commandList); void GenerateReactiveMask(const Ref& target, Graphics::CommandList* commandList); diff --git a/src/engine/renderer/ImpostorRenderer.cpp b/src/engine/renderer/ImpostorRenderer.cpp index c9c370d44..e947af02e 100644 --- a/src/engine/renderer/ImpostorRenderer.cpp +++ b/src/engine/renderer/ImpostorRenderer.cpp @@ -32,7 +32,7 @@ namespace Atlas { auto meshId = item.first; auto instance = item.second; - auto mesh = mainPass->meshIdToMeshMap[meshId]; + auto mesh = renderList->meshIdToMeshMap[meshId]; // If there aren't any impostors there won't be a buffer if (!instance.impostorCount) @@ -102,19 +102,17 @@ namespace Atlas { { // Transfer all framebuffer images including all mips into same layout/access as end of render pass - std::vector imageBarriers; - std::vector bufferBarriers; VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {impostor->baseColorTexture.image, layout, access}, {impostor->normalTexture.image, layout, access}, {impostor->roughnessMetalnessAoTexture.image, layout, access}, {impostor->depthTexture.image, layout, access}, {frameBuffer->GetDepthImage(), layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); } @@ -175,18 +173,15 @@ namespace Atlas { } { - std::vector imageBarriers; - std::vector bufferBarriers; - VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; VkAccessFlags access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {impostor->baseColorTexture.image, layout, access}, {impostor->normalTexture.image, layout, access}, {impostor->roughnessMetalnessAoTexture.image, layout, access}, {impostor->depthTexture.image, layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); commandList->GenerateMipMaps(impostor->baseColorTexture.image); @@ -194,13 +189,13 @@ namespace Atlas { commandList->GenerateMipMaps(impostor->roughnessMetalnessAoTexture.image); commandList->GenerateMipMaps(impostor->depthTexture.image); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {impostor->baseColorTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {impostor->normalTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {impostor->roughnessMetalnessAoTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {impostor->depthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); } diff --git a/src/engine/renderer/ImpostorShadowRenderer.cpp b/src/engine/renderer/ImpostorShadowRenderer.cpp index aad4c7c6e..8813167f5 100644 --- a/src/engine/renderer/ImpostorShadowRenderer.cpp +++ b/src/engine/renderer/ImpostorShadowRenderer.cpp @@ -15,7 +15,7 @@ namespace Atlas { } void ImpostorShadowRenderer::Render(Ref& frameBuffer, - Graphics::CommandList* commandList, RenderList::Pass* renderPass, + Graphics::CommandList* commandList, RenderList* renderList, RenderList::Pass* renderPass, mat4 lightViewMatrix, mat4 lightProjectionMatrix, vec3 lightLocation) { struct alignas(16) PushConstants { @@ -34,7 +34,7 @@ namespace Atlas { auto meshId = item.first; auto instance = item.second; - auto mesh = renderPass->meshIdToMeshMap[meshId]; + auto mesh = renderList->meshIdToMeshMap[meshId]; // If there aren't any impostors there won't be a buffer if (!instance.impostorCount) diff --git a/src/engine/renderer/ImpostorShadowRenderer.h b/src/engine/renderer/ImpostorShadowRenderer.h index c4648ceba..0cb5e2456 100644 --- a/src/engine/renderer/ImpostorShadowRenderer.h +++ b/src/engine/renderer/ImpostorShadowRenderer.h @@ -15,7 +15,7 @@ namespace Atlas { void Init(Graphics::GraphicsDevice* device); void Render(Ref& frameBuffer, - Graphics::CommandList* commandList, RenderList::Pass* renderPass, + Graphics::CommandList* commandList, RenderList* renderList, RenderList::Pass* renderPass, mat4 lightViewMatrix, mat4 lightProjectionMatrix, vec3 lightLocation); private: diff --git a/src/engine/renderer/IndirectLightRenderer.cpp b/src/engine/renderer/IndirectLightRenderer.cpp index d19c38e2e..fe6c7cce4 100644 --- a/src/engine/renderer/IndirectLightRenderer.cpp +++ b/src/engine/renderer/IndirectLightRenderer.cpp @@ -29,13 +29,14 @@ namespace Atlas { auto rtgiEnabled = rtgi && rtgi->enable && rtDataValid; auto ddgiEnabled = volume && volume->enable && !rtgiEnabled && rtDataValid; auto ddgiVisibility = volume && volume->enable && rtDataValid && volume->visibility; - auto reflectionEnabled = reflection && reflection->enable && rtDataValid; - auto aoEnabled = ao && ao->enable && (!ao->rt || rtDataValid); + auto reflectionEnabled = reflection && reflection->enable && (rtDataValid || reflection->ssr); + auto aoEnabled = ao && ao->enable && (!ao->rt || rtDataValid) && !rtgiEnabled; auto ssgiEnabled = ssgi && ssgi->enable && !rtgiEnabled; bool ssgiAo = ssgiEnabled && ssgi->enableAo; pipelineConfig.ManageMacro("RTGI", rtgiEnabled); pipelineConfig.ManageMacro("DDGI", ddgiEnabled); + pipelineConfig.ManageMacro("DDGI_SCROLL", ddgiEnabled && volume->scroll); pipelineConfig.ManageMacro("DDGI_VISIBILITY", ddgiVisibility); pipelineConfig.ManageMacro("REFLECTION", reflectionEnabled); pipelineConfig.ManageMacro("AO", aoEnabled); diff --git a/src/engine/renderer/MainRenderer.cpp b/src/engine/renderer/MainRenderer.cpp index 8dca4f868..73d1873a9 100644 --- a/src/engine/renderer/MainRenderer.cpp +++ b/src/engine/renderer/MainRenderer.cpp @@ -6,1336 +6,1166 @@ #include "../tools/PerformanceCounter.h" #include "../Clock.h" -#define FEATURE_BASE_COLOR_MAP (1 << 1) -#define FEATURE_OPACITY_MAP (1 << 2) -#define FEATURE_NORMAL_MAP (1 << 3) -#define FEATURE_ROUGHNESS_MAP (1 << 4) -#define FEATURE_METALNESS_MAP (1 << 5) -#define FEATURE_AO_MAP (1 << 6) -#define FEATURE_TRANSMISSION (1 << 7) -#define FEATURE_VERTEX_COLORS (1 << 8) - namespace Atlas { - namespace Renderer { - - void MainRenderer::Init(Graphics::GraphicsDevice *device) { - - this->device = device; - - CreateGlobalDescriptorSetLayout(); - - Helper::GeometryHelper::GenerateRectangleVertexArray(vertexArray); - Helper::GeometryHelper::GenerateCubeVertexArray(cubeVertexArray); - - haltonSequence = Helper::HaltonSequence::Generate(2, 3, 16 + 1); - - PreintegrateBRDF(); - - auto uniformBufferDesc = Graphics::BufferDesc { - .usageFlags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - .domain = Graphics::BufferDomain::Host, - .hostAccess = Graphics::BufferHostAccess::Sequential, - .size = sizeof(GlobalUniforms), - }; - globalUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); - pathTraceGlobalUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); - - uniformBufferDesc.size = sizeof(DDGIUniforms); - ddgiUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); - - opaqueRenderer.Init(device); - impostorRenderer.Init(device); - terrainRenderer.Init(device); - shadowRenderer.Init(device); - impostorShadowRenderer.Init(device); - terrainShadowRenderer.Init(device); - gBufferRenderer.Init(device); - ddgiRenderer.Init(device); - rtgiRenderer.Init(device); - ssgiRenderer.Init(device); - aoRenderer.Init(device); - rtrRenderer.Init(device); - sssRenderer.Init(device); - directLightRenderer.Init(device); - indirectLightRenderer.Init(device); - skyboxRenderer.Init(device); - atmosphereRenderer.Init(device); - oceanRenderer.Init(device); - volumetricCloudRenderer.Init(device); - volumetricRenderer.Init(device); - taaRenderer.Init(device); - postProcessRenderer.Init(device); - pathTracingRenderer.Init(device); - fsr2Renderer.Init(device); - textRenderer.Init(device); - textureRenderer.Init(device); - - font = Atlas::CreateRef("font/roboto.ttf", 22.0f, 5); - - } - - void MainRenderer::RenderScene(Ref viewport, Ref target, Ref scene, - Ref primitiveBatch, Texture::Texture2D* texture) { - - if (!device->swapChain->isComplete || !scene->HasMainCamera()) - return; - - auto& camera = scene->GetMainCamera(); - auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue); - - commandList->BeginCommands(); - - auto& taa = scene->postProcessing.taa; - if (taa.enable || scene->postProcessing.fsr2) { - vec2 jitter = vec2(0.0f); - if (scene->postProcessing.fsr2) { - jitter = fsr2Renderer.GetJitter(target, frameCount); - } - else { - jitter = 2.0f * haltonSequence[haltonIndex] - 1.0f; - jitter.x /= (float)target->GetScaledWidth(); - jitter.y /= (float)target->GetScaledHeight(); - } - - camera.Jitter(jitter * taa.jitterRange); - } - else { - // Even if there is no TAA we need to update the jitter for other techniques - // E.g. the reflections and ambient occlusion use reprojection - camera.Jitter(vec2(0.0f)); - } - - Graphics::Profiler::BeginThread("Main renderer", commandList); - Graphics::Profiler::BeginQuery("Render scene"); - - JobGroup fillRenderListGroup { JobPriority::High }; - JobSystem::Execute(fillRenderListGroup, [&](JobData&) { FillRenderList(scene, camera); }); - - Ref materialBuffer; - std::vector materials; - std::unordered_map materialMap; - - JobGroup prepareMaterialsGroup { JobPriority::High }; - JobSystem::Execute(prepareMaterialsGroup, [&](JobData&) { - PrepareMaterials(scene, materials, materialMap); - auto materialBufferDesc = Graphics::BufferDesc { - .usageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - .domain = Graphics::BufferDomain::Host, - .hostAccess = Graphics::BufferHostAccess::Sequential, - .data = materials.data(), - .size = sizeof(PackedMaterial) * glm::max(materials.size(), size_t(1)), - }; - materialBuffer = device->CreateBuffer(materialBufferDesc); - }); - - std::vector> images; - std::vector> blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers; - - JobGroup prepareBindlessGroup { JobPriority::High }; - JobSystem::Execute(prepareBindlessGroup, [&](JobData&) { - PrepareBindlessData(scene, images, blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers); - }); - - SetUniforms(target, scene, camera); - - commandList->BindBuffer(globalUniformBuffer, 1, 31); - commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 1, 12); - commandList->BindSampler(globalSampler, 1, 13); - commandList->BindSampler(globalNearestSampler, 1, 15); - - JobSystem::WaitSpin(prepareMaterialsGroup); - commandList->BindBuffer(materialBuffer, 1, 14); - - if (scene->clutter) - vegetationRenderer.helper.PrepareInstanceBuffer(*scene->clutter, camera, commandList); - - if (scene->ocean && scene->ocean->enable) - scene->ocean->simulation.Compute(commandList); - - if (scene->sky.probe) { - if (scene->sky.probe->update) { - FilterProbe(scene->sky.probe, commandList); - //scene->sky.probe->update = false; - } - } - else if (scene->sky.atmosphere) { - atmosphereRenderer.Render(scene->sky.atmosphere->probe, scene, commandList); - FilterProbe(scene->sky.atmosphere->probe, commandList); - } - - if (scene->irradianceVolume) { - commandList->BindBuffer(ddgiUniformBuffer, 2, 26); - } - - volumetricCloudRenderer.RenderShadow(target, scene, commandList); - - // Wait as long as possible for this to finish - JobSystem::WaitSpin(prepareBindlessGroup); - commandList->BindBuffers(triangleBuffers, 0, 1); - if (images.size()) - commandList->BindSampledImages(images, 0, 3); - - if (device->support.hardwareRayTracing) { - commandList->BindBuffers(triangleOffsetBuffers, 0, 2); - } - else { - commandList->BindBuffers(blasBuffers, 0, 0); - commandList->BindBuffers(bvhTriangleBuffers, 0, 2); - } - - { - shadowRenderer.Render(target, scene, commandList, &renderList); - - terrainShadowRenderer.Render(target, scene, commandList); - } - - if (scene->sky.GetProbe()) { - commandList->BindImage(scene->sky.GetProbe()->filteredSpecular.image, - scene->sky.GetProbe()->filteredSpecular.sampler, 1, 10); - commandList->BindImage(scene->sky.GetProbe()->filteredDiffuse.image, - scene->sky.GetProbe()->filteredDiffuse.sampler, 1, 11); - } - - { - VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - - std::vector bufferBarriers; - std::vector imageBarriers; - - auto lightSubset = scene->GetSubset(); - - for (auto& lightEntity : lightSubset) { - auto& light = lightEntity.GetComponent(); - if (!light.shadow || !light.shadow->update) - continue; - - auto shadow = light.shadow; - imageBarriers.push_back({ shadow->useCubemap ? - shadow->cubemap.image : shadow->maps.image, layout, access }); - } - - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); - } - - JobSystem::WaitSpin(scene->rayTracingWorldUpdateJob); - - ddgiRenderer.TraceAndUpdateProbes(scene, commandList); - - // Only here does the main pass need to be ready - JobSystem::Wait(fillRenderListGroup); - - { - Graphics::Profiler::BeginQuery("Main render pass"); - - commandList->BeginRenderPass(target->gBufferRenderPass, target->gBufferFrameBuffer, true); - - opaqueRenderer.Render(target, scene, commandList, &renderList, materialMap); - - ddgiRenderer.DebugProbes(target, scene, commandList, materialMap); - - vegetationRenderer.Render(target, scene, commandList, materialMap); - - terrainRenderer.Render(target, scene, commandList, materialMap); - - impostorRenderer.Render(target, scene, commandList, &renderList, materialMap); - - commandList->EndRenderPass(); - - Graphics::Profiler::EndQuery(); - } - - oceanRenderer.RenderDepthOnly(target, scene, commandList); - - auto targetData = target->GetData(FULL_RES); - - commandList->BindImage(targetData->baseColorTexture->image, targetData->baseColorTexture->sampler, 1, 3); - commandList->BindImage(targetData->normalTexture->image, targetData->normalTexture->sampler, 1, 4); - commandList->BindImage(targetData->geometryNormalTexture->image, targetData->geometryNormalTexture->sampler, 1, 5); - commandList->BindImage(targetData->roughnessMetallicAoTexture->image, targetData->roughnessMetallicAoTexture->sampler, 1, 6); - commandList->BindImage(targetData->materialIdxTexture->image, targetData->materialIdxTexture->sampler, 1, 7); - commandList->BindImage(targetData->depthTexture->image, targetData->depthTexture->sampler, 1, 8); - - if (!target->HasHistory()) { - auto rtHalfData = target->GetHistoryData(HALF_RES); - auto rtData = target->GetHistoryData(FULL_RES); - VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - std::vector bufferBarriers; - std::vector imageBarriers = { - {rtData->baseColorTexture->image, layout, access}, - {rtData->depthTexture->image, layout, access}, - {rtData->normalTexture->image, layout, access}, - {rtData->geometryNormalTexture->image, layout, access}, - {rtData->roughnessMetallicAoTexture->image, layout, access}, - {rtData->offsetTexture->image, layout, access}, - {rtData->materialIdxTexture->image, layout, access}, - {rtData->stencilTexture->image, layout, access}, - {rtData->velocityTexture->image, layout, access}, - {rtHalfData->baseColorTexture->image, layout, access}, - {rtHalfData->depthTexture->image, layout, access}, - {rtHalfData->normalTexture->image, layout, access}, - {rtHalfData->geometryNormalTexture->image, layout, access}, - {rtHalfData->roughnessMetallicAoTexture->image, layout, access}, - {rtHalfData->offsetTexture->image, layout, access}, - {rtHalfData->materialIdxTexture->image, layout, access}, - {rtHalfData->stencilTexture->image, layout, access}, - {rtHalfData->velocityTexture->image, layout, access}, - }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - } - - { - auto rtData = target->GetData(FULL_RES); - - VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - - std::vector bufferBarriers; - std::vector imageBarriers = { - {rtData->baseColorTexture->image, layout, access}, - {rtData->depthTexture->image, layout, access}, - {rtData->normalTexture->image, layout, access}, - {rtData->geometryNormalTexture->image, layout, access}, - {rtData->roughnessMetallicAoTexture->image, layout, access}, - {rtData->offsetTexture->image, layout, access}, - {rtData->materialIdxTexture->image, layout, access}, - {rtData->stencilTexture->image, layout, access}, - {rtData->velocityTexture->image, layout, access}, - {target->oceanStencilTexture.image, layout, access}, - {target->oceanDepthTexture.image, layout, access} - }; - - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); - } - - { - if (scene->sky.probe) { - skyboxRenderer.Render(target, scene, commandList); - } - else if (scene->sky.atmosphere) { - atmosphereRenderer.Render(target, scene, commandList); - } - } - - gBufferRenderer.FillNormalTexture(target, commandList); - - gBufferRenderer.Downscale(target, commandList); - - aoRenderer.Render(target, scene, commandList); - - rtgiRenderer.Render(target, scene, commandList); - - rtrRenderer.Render(target, scene, commandList); - - sssRenderer.Render(target, scene, commandList); - - { - Graphics::Profiler::BeginQuery("Lighting pass"); - - commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - - directLightRenderer.Render(target, scene, commandList); - - if (!scene->rtgi || !scene->rtgi->enable || !scene->IsRtDataValid()) { - commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - - ssgiRenderer.Render(target, scene, commandList); - } - - commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - - indirectLightRenderer.Render(target, scene, commandList); - - Graphics::ImageBarrier outBarrier(target->lightingTexture.image, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); - commandList->ImageMemoryBarrier(outBarrier, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - - Graphics::Profiler::EndQuery(); - } - - // This was needed after the ocean renderer, if we ever want to have alpha transparency we need it again - // downscaleRenderer.Downscale(target, commandList); - - { - commandList->BeginRenderPass(target->afterLightingRenderPass, target->afterLightingFrameBuffer); - - textRenderer.Render(target, scene, commandList); - - commandList->EndRenderPass(); - - auto rtData = target->GetData(FULL_RES); - - VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - - std::vector bufferBarriers; - std::vector imageBarriers = { - {target->lightingTexture.image, layout, access}, - {rtData->depthTexture->image, layout, access}, - {rtData->velocityTexture->image, layout, access}, - {rtData->stencilTexture->image, layout, access}, - }; - - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); - - } - - { - volumetricCloudRenderer.Render(target, scene, commandList); - - volumetricRenderer.Render(target, scene, commandList); - } - - oceanRenderer.Render(target, scene, commandList); - - if (primitiveBatch) - RenderPrimitiveBatch(viewport, target, primitiveBatch, scene->GetMainCamera(), commandList); - - { - if (scene->postProcessing.fsr2) { - gBufferRenderer.GenerateReactiveMask(target, commandList); - - fsr2Renderer.Render(target, scene, commandList); - } - else { - taaRenderer.Render(target, scene, commandList); - } - - target->Swap(); - - postProcessRenderer.Render(target, scene, commandList, texture); - } - - Graphics::Profiler::EndQuery(); - Graphics::Profiler::EndThread(); - - commandList->EndCommands(); - device->SubmitCommandList(commandList); - - renderList.Clear(); - - } - - void MainRenderer::PathTraceScene(Ref viewport, Ref target, - Ref scene, Texture::Texture2D *texture) { - - if (!scene->IsRtDataValid() || !device->swapChain->isComplete || !scene->HasMainCamera()) - return; - - static vec2 lastJitter = vec2(0.0f); - - auto& camera = scene->GetMainCamera(); - - auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue); - - auto jitter = 2.0f * haltonSequence[haltonIndex] - 1.0f; - jitter.x /= (float)target->GetWidth(); - jitter.y /= (float)target->GetHeight(); - - camera.Jitter(jitter * 0.0f); - - commandList->BeginCommands(); - - Graphics::Profiler::BeginThread("Path tracing", commandList); - Graphics::Profiler::BeginQuery("Buffer operations"); - - auto globalUniforms = GlobalUniforms{ - .vMatrix = camera.viewMatrix, - .pMatrix = camera.projectionMatrix, - .ivMatrix = camera.invViewMatrix, - .ipMatrix = camera.invProjectionMatrix, - .pvMatrixLast = camera.GetLastJitteredMatrix(), - .pvMatrixCurrent = camera.projectionMatrix * camera.viewMatrix, - .jitterLast = lastJitter, - .jitterCurrent = jitter, - .cameraLocation = vec4(camera.GetLocation(), 0.0f), - .cameraDirection = vec4(camera.direction, 0.0f), - .cameraUp = vec4(camera.up, 0.0f), - .cameraRight = vec4(camera.right, 0.0f), - .planetCenter = vec4(scene->sky.planetCenter, 0.0f), - .planetRadius = scene->sky.planetRadius, - .time = Clock::Get(), - .deltaTime = Clock::GetDelta(), - .frameCount = frameCount - }; - - lastJitter = jitter; - - pathTraceGlobalUniformBuffer->SetData(&globalUniforms, 0, sizeof(GlobalUniforms)); - - std::vector> images; - std::vector> blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers; - JobGroup prepareBindlessGroup { JobPriority::High }; - JobSystem::Execute(prepareBindlessGroup, [&](JobData&) { - PrepareBindlessData(scene, images, blasBuffers, triangleBuffers, bvhTriangleBuffers, triangleOffsetBuffers); - }); - - - JobSystem::WaitSpin(scene->rayTracingWorldUpdateJob); - JobSystem::WaitSpin(prepareBindlessGroup); - - commandList->BindBuffer(pathTraceGlobalUniformBuffer, 1, 31); - commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 1, 12); - commandList->BindSampler(globalSampler, 1, 13); - commandList->BindSampler(globalNearestSampler, 1, 15); - commandList->BindBuffers(triangleBuffers, 0, 1); - commandList->BindSampledImages(images, 0, 3); - - if (device->support.hardwareRayTracing) { - commandList->BindBuffers(triangleOffsetBuffers, 0, 2); - } - else { - commandList->BindBuffers(blasBuffers, 0, 0); - commandList->BindBuffers(bvhTriangleBuffers, 0, 2); - } - - Graphics::Profiler::EndQuery(); - - // No probe filtering required - if (scene->sky.atmosphere) { - atmosphereRenderer.Render(scene->sky.atmosphere->probe, scene, commandList); - } - - pathTracingRenderer.Render(target, scene, ivec2(1, 1), commandList); - - if (pathTracingRenderer.realTime) { - taaRenderer.Render(target, scene, commandList); + namespace Renderer { + + void MainRenderer::Init(Graphics::GraphicsDevice* device) { + + this->device = device; + + CreateGlobalDescriptorSetLayout(); + + Helper::GeometryHelper::GenerateRectangleVertexArray(vertexArray); + Helper::GeometryHelper::GenerateCubeVertexArray(cubeVertexArray); + + haltonSequence = Helper::HaltonSequence::Generate(2, 3, 16 + 1); + + PreintegrateBRDF(); + + auto uniformBufferDesc = Graphics::BufferDesc{ + .usageFlags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .domain = Graphics::BufferDomain::Host, + .hostAccess = Graphics::BufferHostAccess::Sequential, + .size = sizeof(GlobalUniforms), + }; + globalUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); + pathTraceGlobalUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); + + + uniformBufferDesc.size = sizeof(DDGIUniforms); + ddgiUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); + + uniformBufferDesc.size = sizeof(CloudShadow); + cloudShadowUniformBuffer = device->CreateMultiBuffer(uniformBufferDesc); + + opaqueRenderer.Init(device); + impostorRenderer.Init(device); + terrainRenderer.Init(device); + shadowRenderer.Init(device); + impostorShadowRenderer.Init(device); + terrainShadowRenderer.Init(device); + gBufferRenderer.Init(device); + ddgiRenderer.Init(device); + rtgiRenderer.Init(device); + ssgiRenderer.Init(device); + aoRenderer.Init(device); + rtrRenderer.Init(device); + sssRenderer.Init(device); + directLightRenderer.Init(device); + indirectLightRenderer.Init(device); + skyboxRenderer.Init(device); + atmosphereRenderer.Init(device); + oceanRenderer.Init(device); + volumetricCloudRenderer.Init(device); + volumetricRenderer.Init(device); + taaRenderer.Init(device); + postProcessRenderer.Init(device); + pathTracingRenderer.Init(device); + fsr2Renderer.Init(device); + textRenderer.Init(device); + textureRenderer.Init(device); + + } + + void MainRenderer::RenderScene(Ref viewport, Ref target, Ref scene, + Ref primitiveBatch, Texture::Texture2D* texture) { + + if (!device->swapChain->isComplete || !scene->HasMainCamera()) + return; + + if (target->IsUsedForPathTracing()) + target->UseForPathTracing(false); + + auto& camera = scene->GetMainCamera(); + auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue); + + commandList->BeginCommands(); + + auto& taa = scene->postProcessing.taa; + if (taa.enable || scene->postProcessing.fsr2) { + vec2 jitter = vec2(0.0f); + if (scene->postProcessing.fsr2) { + jitter = fsr2Renderer.GetJitter(target, frameCount); + } + else { + jitter = 2.0f * haltonSequence[haltonIndex] - 1.0f; + jitter.x /= (float)target->GetScaledWidth(); + jitter.y /= (float)target->GetScaledHeight(); + } + + camera.Jitter(jitter * taa.jitterRange); + } + else { + // Even if there is no TAA we need to update the jitter for other techniques + // E.g. the reflections and ambient occlusion use reprojection + camera.Jitter(vec2(0.0f)); + } + + auto renderState = &scene->renderState; + + Graphics::Profiler::BeginThread("Main renderer", commandList); + Graphics::Profiler::BeginQuery("Render scene"); + + SetUniforms(target, scene, camera); + + commandList->BindBuffer(globalUniformBuffer, 1, 31); + commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 1, 13); + commandList->BindSampler(globalSampler, 1, 14); + commandList->BindSampler(globalNearestSampler, 1, 16); + commandList->BindSampler(globalClampToEdgeSampler, 1, 18); + + if (scene->clutter) + vegetationRenderer.helper.PrepareInstanceBuffer(*scene->clutter, camera, commandList); + + if (scene->ocean && scene->ocean->enable) + scene->ocean->simulation.Compute(commandList); + + if (scene->sky.probe) { + if (scene->sky.probe->update) { + FilterProbe(scene->sky.probe, commandList); + //scene->sky.probe->update = false; + } + } + else if (scene->sky.atmosphere) { + atmosphereRenderer.Render(scene->sky.atmosphere->probe, scene, commandList); + FilterProbe(scene->sky.atmosphere->probe, commandList); + } + + if (scene->irradianceVolume) { + commandList->BindBuffer(ddgiUniformBuffer, 2, 26); + } + + volumetricCloudRenderer.RenderShadow(target, scene, commandList); + + JobSystem::WaitSpin(renderState->materialUpdateJob); + if (renderState->materialBuffer.GetElementCount()) + renderState->materialBuffer.Bind(commandList, 1, 15); + + // Wait as long as possible for this to finish + JobSystem::WaitSpin(renderState->prepareBindlessBlasesJob); + JobSystem::WaitSpin(renderState->bindlessTextureMapUpdateJob); + JobSystem::WaitSpin(renderState->bindlessOtherTextureMapUpdateJob); + commandList->BindBuffers(renderState->triangleBuffers, 0, 1); + if (renderState->textures.size()) + commandList->BindSampledImages(renderState->textures, 0, 3); + if (renderState->cubemaps.size()) + commandList->BindSampledImages(renderState->cubemaps, 0, 4); + if (renderState->textureArrays.size()) + commandList->BindSampledImages(renderState->textureArrays, 0, 5); + + if (device->support.hardwareRayTracing) { + commandList->BindBuffers(renderState->triangleOffsetBuffers, 0, 2); + } + else { + commandList->BindBuffers(renderState->blasBuffers, 0, 0); + commandList->BindBuffers(renderState->bvhTriangleBuffers, 0, 2); + } + + { + shadowRenderer.Render(target, scene, commandList, &renderState->renderList); + + terrainShadowRenderer.Render(target, scene, commandList); + } + + if (scene->sky.GetProbe()) { + commandList->BindImage(scene->sky.GetProbe()->filteredSpecular.image, + scene->sky.GetProbe()->filteredSpecular.sampler, 1, 11); + commandList->BindImage(scene->sky.GetProbe()->filteredDiffuse.image, + scene->sky.GetProbe()->filteredDiffuse.sampler, 1, 12); + } + + { + VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; + + auto lightSubset = scene->GetSubset(); + shadowImageBarriers.clear(); + + for (auto& lightEntity : lightSubset) { + auto& light = lightEntity.GetComponent(); + if (!light.shadow || !light.shadow->update) + continue; + + auto shadow = light.shadow; + shadow->update = false; + shadowImageBarriers.push_back({ shadow->useCubemap ? + shadow->cubemap->image : shadow->maps->image, layout, access }); + } + + commandList->PipelineBarrier(shadowImageBarriers, {}, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); + } + + JobSystem::WaitSpin(scene->renderState.rayTracingWorldUpdateJob); + + JobSystem::WaitSpin(renderState->cullAndSortLightsJob); + renderState->lightBuffer.Bind(commandList, 1, 18); + commandList->BindBuffer(cloudShadowUniformBuffer, 1, 19); + + ddgiRenderer.TraceAndUpdateProbes(scene, commandList); + + // Only here does the main pass need to be ready + JobSystem::Wait(renderState->fillRenderListJob); + + { + Graphics::Profiler::BeginQuery("Main render pass"); + + commandList->BeginRenderPass(target->gBufferRenderPass, target->gBufferFrameBuffer, true); + + opaqueRenderer.Render(target, scene, commandList, &renderState->renderList, renderState->materialMap); + + ddgiRenderer.DebugProbes(target, scene, commandList, renderState->materialMap); + + vegetationRenderer.Render(target, scene, commandList, renderState->materialMap); + + terrainRenderer.Render(target, scene, commandList, renderState->materialMap); + + impostorRenderer.Render(target, scene, commandList, &renderState->renderList, renderState->materialMap); + + commandList->EndRenderPass(); + + Graphics::Profiler::EndQuery(); + } + + oceanRenderer.RenderDepthOnly(target, scene, commandList); + + auto targetData = target->GetData(FULL_RES); + + commandList->BindImage(targetData->baseColorTexture->image, targetData->baseColorTexture->sampler, 1, 3); + commandList->BindImage(targetData->normalTexture->image, targetData->normalTexture->sampler, 1, 4); + commandList->BindImage(targetData->geometryNormalTexture->image, targetData->geometryNormalTexture->sampler, 1, 5); + commandList->BindImage(targetData->roughnessMetallicAoTexture->image, targetData->roughnessMetallicAoTexture->sampler, 1, 6); + commandList->BindImage(targetData->emissiveTexture->image, targetData->emissiveTexture->sampler, 1, 7); + commandList->BindImage(targetData->materialIdxTexture->image, targetData->materialIdxTexture->sampler, 1, 8); + commandList->BindImage(targetData->depthTexture->image, targetData->depthTexture->sampler, 1, 9); + + if (!target->HasHistory()) { + auto rtHalfData = target->GetHistoryData(HALF_RES); + auto rtData = target->GetHistoryData(FULL_RES); + VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; + Graphics::ImageBarrier imageBarriers[] = { + {rtData->baseColorTexture->image, layout, access}, + {rtData->depthTexture->image, layout, access}, + {rtData->normalTexture->image, layout, access}, + {rtData->geometryNormalTexture->image, layout, access}, + {rtData->roughnessMetallicAoTexture->image, layout, access}, + {rtData->emissiveTexture->image, layout, access}, + {rtData->offsetTexture->image, layout, access}, + {rtData->materialIdxTexture->image, layout, access}, + {rtData->stencilTexture->image, layout, access}, + {rtData->velocityTexture->image, layout, access}, + {rtHalfData->baseColorTexture->image, layout, access}, + {rtHalfData->depthTexture->image, layout, access}, + {rtHalfData->normalTexture->image, layout, access}, + {rtHalfData->geometryNormalTexture->image, layout, access}, + {rtHalfData->roughnessMetallicAoTexture->image, layout, access}, + {rtHalfData->emissiveTexture->image, layout, access}, + {rtHalfData->offsetTexture->image, layout, access}, + {rtHalfData->materialIdxTexture->image, layout, access}, + {rtHalfData->stencilTexture->image, layout, access}, + {rtHalfData->velocityTexture->image, layout, access}, + }; + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + } - postProcessRenderer.Render(target, scene, commandList, texture); - } - else { - Graphics::Profiler::BeginQuery("Post processing"); + { + auto rtData = target->GetData(FULL_RES); - if (device->swapChain->isComplete) { - commandList->BeginRenderPass(device->swapChain, true); + VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - textureRenderer.RenderTexture2D(commandList, viewport, &target->texture, - 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0f, 1.0f, false, true); + Graphics::ImageBarrier imageBarriers[] = { + {rtData->baseColorTexture->image, layout, access}, + {rtData->depthTexture->image, layout, access}, + {rtData->normalTexture->image, layout, access}, + {rtData->geometryNormalTexture->image, layout, access}, + {rtData->roughnessMetallicAoTexture->image, layout, access}, + {rtData->emissiveTexture->image, layout, access}, + {rtData->offsetTexture->image, layout, access}, + {rtData->materialIdxTexture->image, layout, access}, + {rtData->stencilTexture->image, layout, access}, + {rtData->velocityTexture->image, layout, access}, + {target->oceanStencilTexture.image, layout, access}, + {target->oceanDepthTexture.image, layout, access} + }; - commandList->EndRenderPass(); - } + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); + } - Graphics::Profiler::EndQuery(); - } + { + if (scene->sky.probe) { + skyboxRenderer.Render(target, scene, commandList); + } + else if (scene->sky.atmosphere) { + atmosphereRenderer.Render(target, scene, commandList); + } + } - Graphics::Profiler::EndThread(); + gBufferRenderer.Patch(target, commandList); - commandList->EndCommands(); + gBufferRenderer.Downscale(target, commandList); - device->SubmitCommandList(commandList); + aoRenderer.Render(target, scene, commandList); - } + rtgiRenderer.Render(target, scene, commandList); - void MainRenderer::RenderPrimitiveBatch(Ref viewport, Ref target, - Ref batch, const CameraComponent& camera, Graphics::CommandList* commandList) { + rtrRenderer.Render(target, scene, commandList); - bool localCommandList = !commandList; + sssRenderer.Render(target, scene, commandList); - if (localCommandList) { - commandList = device->GetCommandList(Graphics::GraphicsQueue); + { + Graphics::Profiler::BeginQuery("Lighting pass"); - commandList->BeginCommands(); - } + commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - batch->TransferData(); + directLightRenderer.Render(target, scene, commandList); - auto rtData = target->GetData(FULL_RES); + commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; + if (!scene->rtgi || !scene->rtgi->enable || !scene->IsRtDataValid()) { + ssgiRenderer.Render(target, scene, commandList); + } + - std::vector bufferBarriers; - std::vector imageBarriers = { - {target->lightingTexture.image, layout, access}, - {rtData->depthTexture->image, layout, access}, - {rtData->velocityTexture->image, layout, access}, - }; + commandList->ImageMemoryBarrier(target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + + indirectLightRenderer.Render(target, scene, commandList); + + Graphics::ImageBarrier outBarrier(target->lightingTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); + commandList->ImageMemoryBarrier(outBarrier, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + Graphics::Profiler::EndQuery(); + } - commandList->BeginRenderPass(target->afterLightingRenderPass, target->afterLightingFrameBuffer); + // This was needed after the ocean renderer, if we ever want to have alpha transparency we need it again + // downscaleRenderer.Downscale(target, commandList); - if (batch->GetLineCount()) { + { + commandList->BeginRenderPass(target->afterLightingRenderPass, target->afterLightingFrameBuffer); - auto pipelineConfig = GetPipelineConfigForPrimitives(target->afterLightingFrameBuffer, - batch->lineVertexArray, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, batch->testDepth); + textRenderer.Render(target, scene, commandList); - auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + commandList->EndRenderPass(); - commandList->BindPipeline(pipeline); - batch->lineVertexArray.Bind(commandList); + auto rtData = target->GetData(FULL_RES); - commandList->SetLineWidth(batch->GetLineWidth()); + VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - commandList->Draw(batch->GetLineCount() * 2); + Graphics::ImageBarrier imageBarriers[] = { + {target->lightingTexture.image, layout, access}, + {rtData->depthTexture->image, layout, access}, + {rtData->velocityTexture->image, layout, access}, + {rtData->stencilTexture->image, layout, access}, + }; - } + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); + } + { + volumetricCloudRenderer.Render(target, scene, commandList); - if (batch->GetTriangleCount()) { + volumetricRenderer.Render(target, scene, commandList); + } - auto pipelineConfig = GetPipelineConfigForPrimitives(target->afterLightingFrameBuffer, - batch->triangleVertexArray, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, batch->testDepth); + oceanRenderer.Render(target, scene, commandList); - auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + if (primitiveBatch) + RenderPrimitiveBatch(viewport, target, primitiveBatch, scene->GetMainCamera(), commandList); - commandList->BindPipeline(pipeline); - batch->triangleVertexArray.Bind(commandList); + if (scene->postProcessing.fsr2) { + gBufferRenderer.GenerateReactiveMask(target, commandList); - commandList->Draw(batch->GetTriangleCount() * 3); + fsr2Renderer.Render(target, scene, commandList); + } + else { + taaRenderer.Render(target, scene, commandList); + } - } + target->Swap(); + postProcessRenderer.Render(target, scene, commandList, texture); - commandList->EndRenderPass(); + Graphics::Profiler::EndQuery(); + Graphics::Profiler::EndThread(); - imageBarriers = { - {target->lightingTexture.image, layout, access}, - {rtData->depthTexture->image, layout, access}, - {rtData->velocityTexture->image, layout, access}, - {rtData->stencilTexture->image, layout, access}, - }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + commandList->EndCommands(); + device->SubmitCommandList(commandList); - if (localCommandList) { - commandList->EndCommands(); + renderState->renderList.Clear(); - device->SubmitCommandList(commandList); - } + } - } + void MainRenderer::PathTraceScene(Ref viewport, Ref target, + Ref scene, Texture::Texture2D* texture) { - void MainRenderer::RenderProbe(Ref probe, Ref target, Ref scene) { + if (!scene->IsRtDataValid() || !device->swapChain->isComplete || !scene->HasMainCamera()) + return; - /* - if (probe->resolution != target->GetWidth() || - probe->resolution != target->GetHeight()) - return; + if (!target->IsUsedForPathTracing()) + target->UseForPathTracing(true); - std::vector materials; - std::unordered_map materialMap; - Viewport viewport(0, 0, probe->resolution, probe->resolution); + auto renderState = &scene->renderState; - PrepareMaterials(scene, materials, materialMap); + auto& camera = scene->GetMainCamera(); + + auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue); - auto materialBuffer = Buffer::Buffer(AE_SHADER_STORAGE_BUFFER, sizeof(PackedMaterial), 0, - materials.size(), materials.data()); + auto& taa = scene->postProcessing.taa; + if (taa.enable || scene->postProcessing.fsr2) { + vec2 jitter = vec2(0.0f); + if (scene->postProcessing.fsr2) { + jitter = fsr2Renderer.GetJitter(target, frameCount); + } + else { + jitter = 2.0f * haltonSequence[haltonIndex] - 1.0f; + jitter.x /= (float)target->GetScaledWidth() * 0.75f; + jitter.y /= (float)target->GetScaledHeight() * 0.75f; + } + + camera.Jitter(jitter * taa.jitterRange); + } + else { + // Even if there is no TAA we need to update the jitter for other techniques + // E.g. the reflections and ambient occlusion use reprojection + camera.Jitter(vec2(0.0f)); + } + + commandList->BeginCommands(); - Lighting::EnvironmentProbe* skyProbe = nullptr; + Graphics::Profiler::BeginThread("Path tracing", commandList); + Graphics::Profiler::BeginQuery("Buffer operations"); - if (scene->sky.probe) { - skyProbe = scene->sky.probe; - scene->sky.probe = nullptr; - } + auto globalUniforms = GlobalUniforms{ + .vMatrix = camera.viewMatrix, + .pMatrix = camera.projectionMatrix, + .ivMatrix = camera.invViewMatrix, + .ipMatrix = camera.invProjectionMatrix, + .pvMatrixLast = camera.GetLastJitteredMatrix(), + .pvMatrixCurrent = camera.projectionMatrix * camera.viewMatrix, + .ipvMatrixLast = glm::inverse(camera.GetLastJitteredMatrix()), + .ipvMatrixCurrent = glm::inverse(camera.projectionMatrix * camera.viewMatrix), + .vMatrixLast = camera.GetLastViewMatrix(), + .jitterLast = camera.GetLastJitter(), + .jitterCurrent = camera.GetJitter(), + .cameraLocation = vec4(camera.GetLocation(), 0.0f), + .cameraDirection = vec4(camera.direction, 0.0f), + .cameraUp = vec4(camera.up, 0.0f), + .cameraRight = vec4(camera.right, 0.0f), + .planetCenter = vec4(scene->sky.planetCenter, 0.0f), + .windDir = glm::normalize(scene->wind.direction), + .windSpeed = scene->wind.speed, + .planetRadius = scene->sky.planetRadius, + .time = Clock::Get(), + .deltaTime = Clock::GetDelta(), + .frameCount = frameCount, + .mipLodBias = -1.0f / target->GetScalingFactor(), + .cameraNearPlane = camera.nearPlane, + .cameraFarPlane = camera.farPlane, + }; - vec3 faces[] = { vec3(1.0f, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), - vec3(0.0f, 1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), - vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f) }; + pathTraceGlobalUniformBuffer->SetData(&globalUniforms, 0, sizeof(GlobalUniforms)); - vec3 ups[] = { vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), - vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f), - vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f) }; + JobSystem::WaitSpin(scene->renderState.rayTracingWorldUpdateJob); + JobSystem::WaitSpin(renderState->prepareBindlessBlasesJob); + JobSystem::WaitSpin(renderState->bindlessTextureMapUpdateJob); - Camera camera(90.0f, 1.0f, 0.5f, 1000.0f); - camera.UpdateProjection(); + commandList->BindBuffer(pathTraceGlobalUniformBuffer, 1, 31); + commandList->BindImage(dfgPreintegrationTexture.image, dfgPreintegrationTexture.sampler, 1, 13); + commandList->BindSampler(globalSampler, 1, 14); + commandList->BindSampler(globalNearestSampler, 1, 16); + commandList->BindSampler(globalClampToEdgeSampler, 1, 17); + commandList->BindBuffers(renderState->triangleBuffers, 0, 1); + if (renderState->textures.size()) + commandList->BindSampledImages(renderState->textures, 0, 3); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); + if (device->support.hardwareRayTracing) { + commandList->BindBuffers(renderState->triangleOffsetBuffers, 0, 2); + } + else { + commandList->BindBuffers(renderState->blasBuffers, 0, 0); + commandList->BindBuffers(renderState->bvhTriangleBuffers, 0, 2); + } - for (uint8_t i = 0; i < 6; i++) { + Graphics::Profiler::EndQuery(); - vec3 dir = faces[i]; - vec3 up = ups[i]; - vec3 right = normalize(cross(up, dir)); - up = normalize(cross(dir, right)); + // No probe filtering required + if (scene->sky.atmosphere) { + atmosphereRenderer.Render(scene->sky.atmosphere->probe, scene, commandList); + } - camera.viewMatrix = glm::lookAt(probe->GetPosition(), probe->GetPosition() + dir, up); - camera.invViewMatrix = glm::inverse(camera.viewMatrix); - camera.location = probe->GetPosition(); - camera.direction = dir; - camera.right = right; - camera.up = up; + pathTracingRenderer.Render(target, scene, ivec2(1, 1), commandList); - camera.frustum = Volume::Frustum(camera.projectionMatrix * camera.viewMatrix); + if (pathTracingRenderer.realTime) { + if (scene->postProcessing.fsr2) { + fsr2Renderer.Render(target, scene, commandList); + } + else if (scene->postProcessing.taa.enable) { + taaRenderer.Render(target, scene, commandList); + } - scene->Update(&camera, 0.0f); + target->Swap(); - // Clear the lights depth maps - depthFramebuffer.Bind(); + postProcessRenderer.Render(target, scene, commandList, texture); + } + else { + Graphics::Profiler::BeginQuery("Post processing"); - auto lights = scene->GetLights(); + if (device->swapChain->isComplete && !texture) { + commandList->BeginRenderPass(device->swapChain, true); - if (scene->sky.sun) { - lights.push_back(scene->sky.sun); - } + textureRenderer.RenderTexture2D(commandList, viewport, &target->outputTexture, + 0.0f, 0.0f, float(viewport->width), float(viewport->height), 0.0f, 1.0f, false, true); - for (auto light : lights) { + commandList->EndRenderPass(); + } + else if (texture) { + postProcessRenderer.CopyToTexture(&target->outputTexture, texture, commandList); + } - if (!light->GetShadow()) - continue; - if (!light->GetShadow()->update) - continue; + target->Swap(); - for (int32_t j = 0; j < light->GetShadow()->componentCount; j++) { - if (light->GetShadow()->useCubemap) { - depthFramebuffer.AddComponentCubemap(GL_DEPTH_ATTACHMENT, - &light->GetShadow()->cubemap, j); - } - else { - depthFramebuffer.AddComponentTextureArray(GL_DEPTH_ATTACHMENT, - &light->GetShadow()->maps, j); - } + Graphics::Profiler::EndQuery(); + } - glClear(GL_DEPTH_BUFFER_BIT); - } - } + Graphics::Profiler::EndThread(); - shadowRenderer.Render(&viewport, target, &camera, scene); + commandList->EndCommands(); - glEnable(GL_CULL_FACE); + device->SubmitCommandList(commandList); - glCullFace(GL_FRONT); + renderState->WaitForAsyncWorkCompletion(); - terrainShadowRenderer.Render(&viewport, target, &camera, scene); + } - glCullFace(GL_BACK); + void MainRenderer::RenderPrimitiveBatch(Ref viewport, Ref target, + Ref batch, const CameraComponent& camera, Graphics::CommandList* commandList) { - // Shadows have been updated - for (auto light : lights) { - if (!light->GetShadow()) - continue; - light->GetShadow()->update = false; - } + bool localCommandList = !commandList; - materialBuffer.BindBase(0); + if (localCommandList) { + commandList = device->GetCommandList(Graphics::GraphicsQueue); - target->geometryFramebuffer.Bind(true); - target->geometryFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, - GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 }); + commandList->BeginCommands(); + } - glEnable(GL_CULL_FACE); + batch->TransferData(); - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + auto rtData = target->GetData(FULL_RES); - opaqueRenderer.Render(&viewport, target, &camera, scene, materialMap); + VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - terrainRenderer.Render(&viewport, target, &camera, scene, materialMap); + Graphics::ImageBarrier preImageBarriers[] = { + {target->lightingTexture.image, layout, access}, + {rtData->depthTexture->image, layout, access}, + {rtData->velocityTexture->image, layout, access}, + }; + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - glEnable(GL_CULL_FACE); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + commandList->BeginRenderPass(target->afterLightingRenderPass, target->afterLightingFrameBuffer); - target->geometryFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }); + if (batch->GetLineCount()) { - decalRenderer.Render(&viewport, target, &camera, scene); + auto pipelineConfig = GetPipelineConfigForPrimitives(target->afterLightingFrameBuffer, + batch->lineVertexArray, VK_PRIMITIVE_TOPOLOGY_LINE_LIST, batch->testDepth); - glDisable(GL_BLEND); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); - vertexArray.Bind(); + commandList->BindPipeline(pipeline); + batch->lineVertexArray.Bind(commandList); - target->lightingFramebuffer.Bind(true); - target->lightingFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0 }); + commandList->SetLineWidth(batch->GetLineWidth()); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + commandList->Draw(batch->GetLineCount() * 2); - directionalLightRenderer.Render(&viewport, target, &camera, scene); + } - glEnable(GL_DEPTH_TEST); - target->lightingFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1 }); + if (batch->GetTriangleCount()) { - glDepthMask(GL_TRUE); + auto pipelineConfig = GetPipelineConfigForPrimitives(target->afterLightingFrameBuffer, + batch->triangleVertexArray, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, batch->testDepth); - oceanRenderer.Render(&viewport, target, &camera, scene); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); - glDisable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); + commandList->BindPipeline(pipeline); + batch->triangleVertexArray.Bind(commandList); - vertexArray.Bind(); + commandList->Draw(batch->GetTriangleCount() * 3); - volumetricRenderer.Render(&viewport, target, &camera, scene); + } - createProbeFaceShader.Bind(); + commandList->EndRenderPass(); - createProbeFaceShader.GetUniform("faceIndex")->SetValue((int32_t)i); - createProbeFaceShader.GetUniform("ipMatrix")->SetValue(camera.invProjectionMatrix); + Graphics::ImageBarrier postImageBarriers[] = { + {target->lightingTexture.image, layout, access}, + {rtData->depthTexture->image, layout, access}, + {rtData->velocityTexture->image, layout, access}, + {rtData->stencilTexture->image, layout, access}, + }; + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - int32_t groupCount = probe->resolution / 8; - groupCount += ((groupCount * 8 == probe->resolution) ? 0 : 1); + if (localCommandList) { + commandList->EndCommands(); - probe->cubemap.Bind(GL_WRITE_ONLY, 0); - probe->depth.Bind(GL_WRITE_ONLY, 1); + device->SubmitCommandList(commandList); + } - target->lightingFramebuffer.GetComponentTexture(GL_COLOR_ATTACHMENT0)->Bind(0); - target->lightingFramebuffer.GetComponentTexture(GL_DEPTH_ATTACHMENT)->Bind(1); + } - glDispatchCompute(groupCount, groupCount, 1); + void MainRenderer::RenderProbe(Ref probe, Ref target, Ref scene) { - glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + /* + if (probe->resolution != target->GetWidth() || + probe->resolution != target->GetHeight()) + return; - } + std::vector materials; + std::unordered_map materialMap; + Viewport viewport(0, 0, probe->resolution, probe->resolution); - if (skyProbe) { - scene->sky.probe = skyProbe; - } - */ + PrepareMaterials(scene, materials, materialMap); - } + auto materialBuffer = Buffer::Buffer(AE_SHADER_STORAGE_BUFFER, sizeof(PackedMaterial), 0, + materials.size(), materials.data()); - void MainRenderer::FilterProbe(Ref probe, Graphics::CommandList* commandList) { + Lighting::EnvironmentProbe* skyProbe = nullptr; - Graphics::Profiler::BeginQuery("Filter probe"); + if (scene->sky.probe) { + skyProbe = scene->sky.probe; + scene->sky.probe = nullptr; + } - mat4 projectionMatrix = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 100.0f); - vec3 faces[] = { vec3(1.0f, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), - vec3(0.0f, 1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), - vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f) }; + vec3 faces[] = { vec3(1.0f, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), + vec3(0.0f, 1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), + vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f) }; - vec3 ups[] = { vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), - vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f), - vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f) }; + vec3 ups[] = { vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), + vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f), + vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f) }; - Graphics::Profiler::BeginQuery("Filter diffuse probe"); + Camera camera(90.0f, 1.0f, 0.5f, 1000.0f); + camera.UpdateProjection(); - auto pipelineConfig = PipelineConfig("brdf/filterProbe.csh", { "FILTER_DIFFUSE" }); - auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); - commandList->BindPipeline(pipeline); + for (uint8_t i = 0; i < 6; i++) { - //auto constantRange = pipeline->shader->GetPushConstantRange("constants"); - //commandList->PushConstants() + vec3 dir = faces[i]; + vec3 up = ups[i]; + vec3 right = normalize(cross(up, dir)); + up = normalize(cross(dir, right)); - // It's only accessed in compute shaders - commandList->ImageMemoryBarrier(probe->filteredDiffuse.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - - auto& cubemap = probe->GetCubemap(); - if (cubemap.image->layout == VK_IMAGE_LAYOUT_UNDEFINED) { - commandList->ImageMemoryBarrier(cubemap.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - } + camera.viewMatrix = glm::lookAt(probe->GetPosition(), probe->GetPosition() + dir, up); + camera.invViewMatrix = glm::inverse(camera.viewMatrix); + camera.location = probe->GetPosition(); + camera.direction = dir; + camera.right = right; + camera.up = up; - commandList->BindImage(probe->filteredDiffuse.image, 3, 0); - commandList->BindImage(cubemap.image, cubemap.sampler, 3, 1); + camera.frustum = Volume::Frustum(camera.projectionMatrix * camera.viewMatrix); - ivec2 res = ivec2(probe->filteredDiffuse.width, probe->filteredDiffuse.height); - ivec2 groupCount = ivec2(res.x / 8, res.y / 4); - groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); - groupCount.y += ((groupCount.y * 4 == res.y) ? 0 : 1); + scene->Update(&camera, 0.0f); - commandList->Dispatch(groupCount.x, groupCount.y, 6); + // Clear the lights depth maps + depthFramebuffer.Bind(); - commandList->ImageMemoryBarrier(probe->filteredDiffuse.image, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + auto lights = scene->GetLights(); - Graphics::Profiler::EndAndBeginQuery("Filter specular probe"); + if (scene->sky.sun) { + lights.push_back(scene->sky.sun); + } - pipelineConfig = PipelineConfig("brdf/filterProbe.csh", { "FILTER_SPECULAR" }); - pipeline = PipelineManager::GetPipeline(pipelineConfig); + for (auto light : lights) { - struct alignas(16) PushConstants { - int cubeMapMipLevels; - float roughness; - uint32_t mipLevel; - }; + if (!light->GetShadow()) + continue; + if (!light->GetShadow()->update) + continue; - commandList->BindPipeline(pipeline); + for (int32_t j = 0; j < light->GetShadow()->componentCount; j++) { + if (light->GetShadow()->useCubemap) { + depthFramebuffer.AddComponentCubemap(GL_DEPTH_ATTACHMENT, + &light->GetShadow()->cubemap, j); + } + else { + depthFramebuffer.AddComponentTextureArray(GL_DEPTH_ATTACHMENT, + &light->GetShadow()->maps, j); + } - commandList->ImageMemoryBarrier(probe->filteredSpecular.image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + glClear(GL_DEPTH_BUFFER_BIT); + } + } - int32_t width = int32_t(probe->filteredSpecular.width); - int32_t height = int32_t(probe->filteredSpecular.height); + shadowRenderer.Render(&viewport, target, &camera, scene); - for (uint32_t i = 0; i < probe->filteredSpecular.image->mipLevels; i++) { - Graphics::Profiler::BeginQuery("Mip level " + std::to_string(i)); + glEnable(GL_CULL_FACE); - ivec2 res = ivec2(width, height); + glCullFace(GL_FRONT); - commandList->BindImage(probe->filteredSpecular.image, 3, 0, i); + terrainShadowRenderer.Render(&viewport, target, &camera, scene); - PushConstants pushConstants = { - .cubeMapMipLevels = int32_t(probe->GetCubemap().image->mipLevels), - .roughness = float(i) / float(probe->filteredSpecular.image->mipLevels - 1), - .mipLevel = i - }; - commandList->PushConstants("constants", &pushConstants); - - ivec2 groupCount = ivec2(res.x / 8, res.y / 4); - groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); - groupCount.y += ((groupCount.y * 4 == res.y) ? 0 : 1); - - commandList->Dispatch(groupCount.x, groupCount.y, 6); - - width /= 2; - height /= 2; - - Graphics::Profiler::EndQuery(); - - } - - commandList->ImageMemoryBarrier(probe->filteredSpecular.image, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - Graphics::Profiler::EndQuery(); - - Graphics::Profiler::EndQuery(); - - } - - void MainRenderer::Update() { - - textRenderer.Update(); - - haltonIndex = (haltonIndex + 1) % haltonSequence.size(); - frameCount++; - - } - - void MainRenderer::CreateGlobalDescriptorSetLayout() { - - if (!device->support.bindless) - return; - - auto samplerDesc = Graphics::SamplerDesc { - .filter = VK_FILTER_LINEAR, - .mode = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .maxLod = 12, - .anisotropicFiltering = true - }; - globalSampler = device->CreateSampler(samplerDesc); - - samplerDesc = Graphics::SamplerDesc{ - .filter = VK_FILTER_NEAREST, - .mode = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, - .maxLod = 12, - .anisotropicFiltering = false - }; - globalNearestSampler = device->CreateSampler(samplerDesc); - - auto layoutDesc = Graphics::DescriptorSetLayoutDesc{ - .bindings = { - { - .bindingIdx = 0, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 8192, .bindless = true - }, - { - .bindingIdx = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 8192, .bindless = true - }, - { - .bindingIdx = 2, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .descriptorCount = 8192, .bindless = true - }, - { - .bindingIdx = 3, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, - .descriptorCount = 16384, .bindless = true - } - }, - .bindingCount = 4 - }; - globalDescriptorSetLayout = device->CreateDescriptorSetLayout(layoutDesc); - - PipelineManager::OverrideDescriptorSetLayout(globalDescriptorSetLayout, 0); - - } - - void MainRenderer::SetUniforms(const Ref& target, const Ref& scene, const CameraComponent& camera) { - - auto globalUniforms = GlobalUniforms { - .vMatrix = camera.viewMatrix, - .pMatrix = camera.projectionMatrix, - .ivMatrix = camera.invViewMatrix, - .ipMatrix = camera.invProjectionMatrix, - .pvMatrixLast = camera.GetLastJitteredMatrix(), - .pvMatrixCurrent = camera.projectionMatrix * camera.viewMatrix, - .ipvMatrixLast = glm::inverse(camera.GetLastJitteredMatrix()), - .ipvMatrixCurrent = glm::inverse(camera.projectionMatrix * camera.viewMatrix), - .jitterLast = camera.GetLastJitter(), - .jitterCurrent = camera.GetJitter(), - .cameraLocation = vec4(camera.GetLocation(), 0.0f), - .cameraDirection = vec4(camera.direction, 0.0f), - .cameraUp = vec4(camera.up, 0.0f), - .cameraRight = vec4(camera.right, 0.0f), - .planetCenter = vec4(scene->sky.planetCenter, 0.0f), - .windDir = glm::normalize(scene->wind.direction), - .windSpeed = scene->wind.speed, - .planetRadius = scene->sky.planetRadius, - .time = Clock::Get(), - .deltaTime = Clock::GetDelta(), - .frameCount = frameCount, - .mipLodBias = -1.0f / target->GetScalingFactor() - }; - - auto frustumPlanes = camera.frustum.GetPlanes(); - std::copy(frustumPlanes.begin(), frustumPlanes.end(), &globalUniforms.frustumPlanes[0]); - - globalUniformBuffer->SetData(&globalUniforms, 0, sizeof(GlobalUniforms)); - - if (scene->irradianceVolume) { - auto volume = scene->irradianceVolume; - - if (volume->scroll) { - //auto pos = vec3(0.4f, 12.7f, -43.0f); - //auto pos = glm::vec3(30.0f, 25.0f, 0.0f); - auto pos = camera.GetLocation(); - - auto volumeSize = volume->aabb.GetSize(); - auto volumeAABB = Volume::AABB(-volumeSize / 2.0f + pos, volumeSize / 2.0f + pos); - volume->SetAABB(volumeAABB); - } - - auto probeCountPerCascade = volume->probeCount.x * volume->probeCount.y * - volume->probeCount.z; - auto ddgiUniforms = DDGIUniforms { - .volumeCenter = vec4(camera.GetLocation(), 1.0f), - .volumeProbeCount = ivec4(volume->probeCount, probeCountPerCascade), - .cascadeCount = volume->cascadeCount, - .volumeBias = volume->bias, - .volumeIrradianceRes = volume->irrRes, - .volumeMomentsRes = volume->momRes, - .rayCount = volume->rayCount, - .inactiveRayCount = volume->rayCountInactive, - .hysteresis = volume->hysteresis, - .volumeGamma = volume->gamma, - .volumeStrength = volume->strength, - .depthSharpness = volume->sharpness, - .optimizeProbes = volume->optimizeProbes ? 1 : 0, - .volumeEnabled = volume->enable ? 1 : 0 - }; - - for (int32_t i = 0; i < volume->cascadeCount; i++) { - ddgiUniforms.cascades[i] = DDGICascade{ - .volumeMin = vec4(volume->cascades[i].aabb.min, 1.0f), - .volumeMax = vec4(volume->cascades[i].aabb.max, 1.0f), - .cellSize = vec4(volume->cascades[i].cellSize, glm::length(volume->cascades[i].cellSize)), - .offsetDifference = ivec4(volume->cascades[i].offsetDifferences, 0), - }; - } - - ddgiUniformBuffer->SetData(&ddgiUniforms, 0, sizeof(DDGIUniforms)); - } - else { - auto ddgiUniforms = DDGIUniforms { - .volumeEnabled = 0 - }; - - for (int32_t i = 0; i < MAX_IRRADIANCE_VOLUME_CASCADES; i++) { - ddgiUniforms.cascades[i] = DDGICascade { - .volumeMin = vec4(0.0f), - .volumeMax = vec4(0.0f), - .cellSize = vec4(0.0f), - }; - } - - ddgiUniformBuffer->SetData(&ddgiUniforms, 0, sizeof(DDGIUniforms)); - } - - auto meshes = scene->GetMeshes(); - for (auto& mesh : meshes) { - if (!mesh.IsLoaded() || !mesh->impostor) continue; - - auto impostor = mesh->impostor; - Mesh::Impostor::ImpostorInfo impostorInfo = { - .center = vec4(impostor->center, 1.0f), - .radius = impostor->radius, - .views = impostor->views, - .cutoff = impostor->cutoff, - .mipBias = impostor->mipBias - }; - - impostor->impostorInfoBuffer.SetData(&impostorInfo, 0); - } - - } - - void MainRenderer::PrepareMaterials(Ref scene, std::vector& materials, - std::unordered_map& materialMap) { - - auto sceneMaterials = scene->GetMaterials(); - - // For debugging purpose - if (scene->irradianceVolume && scene->irradianceVolume->debug) { - sceneMaterials.push_back(ddgiRenderer.probeDebugMaterial); - sceneMaterials.push_back(ddgiRenderer.probeDebugActiveMaterial); - sceneMaterials.push_back(ddgiRenderer.probeDebugInactiveMaterial); - sceneMaterials.push_back(ddgiRenderer.probeDebugOffsetMaterial); - } - - uint16_t idx = 0; - - for (auto material : sceneMaterials) { - // Might happen due to the scene giving back materials on a mesh basis - if (materialMap.contains(material.get())) - continue; - - PackedMaterial packed; - - packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->baseColor), 0.0f)); - packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor), 0.0f)); - packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->transmissiveColor), 0.0f)); - - packed.emissiveIntensityTiling = glm::packHalf2x16(vec2(material->emissiveIntensity, material->tiling)); - - vec4 data0, data1, data2; - - data0.x = material->opacity; - data0.y = material->roughness; - data0.z = material->metalness; - - data1.x = material->ao; - data1.y = material->HasNormalMap() ? material->normalScale : 0.0f; - data1.z = material->HasDisplacementMap() ? material->displacementScale : 0.0f; - - data2.x = material->reflectance; - // Note used - data2.y = 0.0f; - data2.z = 0.0f; + glCullFace(GL_BACK); - packed.data0 = Common::Packing::PackUnsignedVector3x10_1x2(data0); - packed.data1 = Common::Packing::PackUnsignedVector3x10_1x2(data1); - packed.data2 = Common::Packing::PackUnsignedVector3x10_1x2(data2); + // Shadows have been updated + for (auto light : lights) { + if (!light->GetShadow()) + continue; + light->GetShadow()->update = false; + } - packed.features = 0; + materialBuffer.BindBase(0); - packed.features |= material->HasBaseColorMap() ? FEATURE_BASE_COLOR_MAP : 0; - packed.features |= material->HasOpacityMap() ? FEATURE_OPACITY_MAP : 0; - packed.features |= material->HasNormalMap() ? FEATURE_NORMAL_MAP : 0; - packed.features |= material->HasRoughnessMap() ? FEATURE_ROUGHNESS_MAP : 0; - packed.features |= material->HasMetalnessMap() ? FEATURE_METALNESS_MAP : 0; - packed.features |= material->HasAoMap() ? FEATURE_AO_MAP : 0; - packed.features |= glm::length(material->transmissiveColor) > 0.0f ? FEATURE_TRANSMISSION : 0; - packed.features |= material->vertexColors ? FEATURE_VERTEX_COLORS : 0; + target->geometryFramebuffer.Bind(true); + target->geometryFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 }); - materials.push_back(packed); + glEnable(GL_CULL_FACE); - materialMap[material.get()] = idx++; - } - - auto meshes = scene->GetMeshes(); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - for (auto mesh : meshes) { - if (!mesh.IsLoaded()) - continue; + opaqueRenderer.Render(&viewport, target, &camera, scene, materialMap); - auto impostor = mesh->impostor; + terrainRenderer.Render(&viewport, target, &camera, scene, materialMap); - if (!impostor) - continue; + glEnable(GL_CULL_FACE); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - PackedMaterial packed; + target->geometryFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }); - packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(1.0f)); - packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(0.0f)); - packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(impostor->transmissiveColor), 1.0f)); + decalRenderer.Render(&viewport, target, &camera, scene); - vec4 data0, data1, data2; + glDisable(GL_BLEND); - data0.x = 1.0f; - data0.y = 1.0f; - data0.z = 1.0f; + vertexArray.Bind(); - data1.x = 1.0f; - data1.y = 0.0f; - data1.z = 0.0f; + target->lightingFramebuffer.Bind(true); + target->lightingFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0 }); - data2.x = 0.5f; - // Note used - data2.y = 0.0f; - data2.z = 0.0f; + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); - packed.data0 = Common::Packing::PackUnsignedVector3x10_1x2(data0); - packed.data1 = Common::Packing::PackUnsignedVector3x10_1x2(data1); - packed.data2 = Common::Packing::PackUnsignedVector3x10_1x2(data2); + directionalLightRenderer.Render(&viewport, target, &camera, scene); - packed.features = 0; + glEnable(GL_DEPTH_TEST); - packed.features |= FEATURE_BASE_COLOR_MAP | - FEATURE_ROUGHNESS_MAP | FEATURE_METALNESS_MAP | FEATURE_AO_MAP; - packed.features |= glm::length(impostor->transmissiveColor) > 0.0f ? FEATURE_TRANSMISSION : 0; + target->lightingFramebuffer.SetDrawBuffers({ GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1 }); - materials.push_back(packed); + glDepthMask(GL_TRUE); - materialMap[impostor.get()] = idx++; - } + oceanRenderer.Render(&viewport, target, &camera, scene); + glDisable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); - } + vertexArray.Bind(); - void MainRenderer::PrepareBindlessData(Ref scene, std::vector>& images, - std::vector>& blasBuffers, std::vector>& triangleBuffers, - std::vector>& bvhTriangleBuffers, std::vector>& triangleOffsetBuffers) { + volumetricRenderer.Render(&viewport, target, &camera, scene); - if (!device->support.bindless) - return; + createProbeFaceShader.Bind(); - JobSystem::WaitSpin(scene->bindlessMeshMapUpdateJob); + createProbeFaceShader.GetUniform("faceIndex")->SetValue((int32_t)i); + createProbeFaceShader.GetUniform("ipMatrix")->SetValue(camera.invProjectionMatrix); - blasBuffers.resize(scene->meshIdToBindlessIdx.size()); - triangleBuffers.resize(scene->meshIdToBindlessIdx.size()); - bvhTriangleBuffers.resize(scene->meshIdToBindlessIdx.size()); - triangleOffsetBuffers.resize(scene->meshIdToBindlessIdx.size()); + int32_t groupCount = probe->resolution / 8; + groupCount += ((groupCount * 8 == probe->resolution) ? 0 : 1); - for (const auto& [meshId, idx] : scene->meshIdToBindlessIdx) { - if (!scene->registeredMeshes.contains(meshId)) continue; + probe->cubemap.Bind(GL_WRITE_ONLY, 0); + probe->depth.Bind(GL_WRITE_ONLY, 1); - const auto& mesh = scene->registeredMeshes[meshId].resource; + target->lightingFramebuffer.GetComponentTexture(GL_COLOR_ATTACHMENT0)->Bind(0); + target->lightingFramebuffer.GetComponentTexture(GL_DEPTH_ATTACHMENT)->Bind(1); - auto blasBuffer = mesh->blasNodeBuffer.Get(); - auto triangleBuffer = mesh->triangleBuffer.Get(); - auto bvhTriangleBuffer = mesh->bvhTriangleBuffer.Get(); - auto triangleOffsetBuffer = mesh->triangleOffsetBuffer.Get(); + glDispatchCompute(groupCount, groupCount, 1); - AE_ASSERT(triangleBuffer != nullptr); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); - blasBuffers[idx] = blasBuffer; - triangleBuffers[idx] = triangleBuffer; - bvhTriangleBuffers[idx] = bvhTriangleBuffer; - triangleOffsetBuffers[idx] = triangleOffsetBuffer; - } + } - JobSystem::WaitSpin(scene->bindlessTextureMapUpdateJob); + if (skyProbe) { + scene->sky.probe = skyProbe; + } + */ - images.resize(scene->textureToBindlessIdx.size()); + } - for (const auto& [texture, idx] : scene->textureToBindlessIdx) { + void MainRenderer::FilterProbe(Ref probe, Graphics::CommandList* commandList) { - images[idx] = texture->image; + Graphics::Profiler::BeginQuery("Filter probe"); - } + mat4 projectionMatrix = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 100.0f); + vec3 faces[] = { vec3(1.0f, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), + vec3(0.0f, 1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), + vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f) }; - } + vec3 ups[] = { vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), + vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f), + vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f) }; - void MainRenderer::FillRenderList(Ref scene, const CameraComponent& camera) { + Graphics::Profiler::BeginQuery("Filter diffuse probe"); - auto meshes = scene->GetMeshes(); - renderList.NewFrame(scene); + auto pipelineConfig = PipelineConfig("brdf/filterProbe.csh", { "FILTER_DIFFUSE" }); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); - auto lightSubset = scene->GetSubset(); + commandList->BindPipeline(pipeline); - JobGroup group; - for (auto& lightEntity : lightSubset) { + //auto constantRange = pipeline->shader->GetPushConstantRange("constants"); + //commandList->PushConstants() - auto& light = lightEntity.GetComponent(); - if (!light.shadow || !light.shadow->update) - continue; + // It's only accessed in compute shaders + commandList->ImageMemoryBarrier(probe->filteredDiffuse.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - auto& shadow = light.shadow; + auto& cubemap = probe->GetCubemap(); + if (cubemap.image->layout == VK_IMAGE_LAYOUT_UNDEFINED) { + commandList->ImageMemoryBarrier(cubemap.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + } - auto componentCount = shadow->longRange ? - shadow->viewCount - 1 : shadow->viewCount; + commandList->BindImage(probe->filteredDiffuse.image, 3, 0); + commandList->BindImage(cubemap.image, cubemap.sampler, 3, 1); - JobSystem::ExecuteMultiple(group, componentCount, - [&, shadow=shadow, lightEntity=lightEntity](JobData& data) { - auto component = &shadow->views[data.idx]; - auto frustum = Volume::Frustum(component->frustumMatrix); + ivec2 res = ivec2(probe->filteredDiffuse.width, probe->filteredDiffuse.height); + ivec2 groupCount = ivec2(res.x / 8, res.y / 4); + groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 4 == res.y) ? 0 : 1); - auto shadowPass = renderList.GetShadowPass(lightEntity, data.idx); - if (shadowPass == nullptr) - shadowPass = renderList.NewShadowPass(lightEntity, data.idx); + commandList->Dispatch(groupCount.x, groupCount.y, 6); - shadowPass->NewFrame(scene, meshes); - scene->GetRenderList(frustum, shadowPass); - shadowPass->Update(camera.GetLocation()); - shadowPass->FillBuffers(); - renderList.FinishPass(shadowPass); - }); - } + commandList->ImageMemoryBarrier(probe->filteredDiffuse.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - JobSystem::Wait(group); + Graphics::Profiler::EndAndBeginQuery("Filter specular probe"); - auto mainPass = renderList.GetMainPass(); - if (mainPass == nullptr) - mainPass = renderList.NewMainPass(); + pipelineConfig = PipelineConfig("brdf/filterProbe.csh", { "FILTER_SPECULAR" }); + pipeline = PipelineManager::GetPipeline(pipelineConfig); - mainPass->NewFrame(scene, meshes); - scene->GetRenderList(camera.frustum, mainPass); - mainPass->Update(camera.GetLocation()); - mainPass->FillBuffers(); - renderList.FinishPass(mainPass); + struct alignas(16) PushConstants { + int cubeMapMipLevels; + float roughness; + uint32_t mipLevel; + }; - } + commandList->BindPipeline(pipeline); - void MainRenderer::PreintegrateBRDF() { + commandList->ImageMemoryBarrier(probe->filteredSpecular.image, VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - auto pipelineConfig = PipelineConfig("brdf/preintegrateDFG.csh"); - auto computePipeline = PipelineManager::GetPipeline(pipelineConfig); + int32_t width = int32_t(probe->filteredSpecular.width); + int32_t height = int32_t(probe->filteredSpecular.height); - const int32_t res = 256; - dfgPreintegrationTexture = Texture::Texture2D(res, res, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + for (uint32_t i = 0; i < probe->filteredSpecular.image->mipLevels; i++) { + Graphics::Profiler::BeginQuery("Mip level " + std::to_string(i)); - auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue, true); + ivec2 res = ivec2(width, height); - commandList->BeginCommands(); - commandList->BindPipeline(computePipeline); + commandList->BindImage(probe->filteredSpecular.image, 3, 0, i); - auto barrier = Graphics::ImageBarrier(VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - commandList->ImageMemoryBarrier(barrier.Update(dfgPreintegrationTexture.image), - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + PushConstants pushConstants = { + .cubeMapMipLevels = int32_t(probe->GetCubemap().image->mipLevels), + .roughness = float(i) / float(probe->filteredSpecular.image->mipLevels - 1), + .mipLevel = i + }; + commandList->PushConstants("constants", &pushConstants); - uint32_t groupCount = res / 8; + ivec2 groupCount = ivec2(res.x / 8, res.y / 4); + groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 4 == res.y) ? 0 : 1); - commandList->BindImage(dfgPreintegrationTexture.image, 3, 0); - commandList->Dispatch(groupCount, groupCount, 1); + commandList->Dispatch(groupCount.x, groupCount.y, 6); - barrier = Graphics::ImageBarrier(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_SHADER_READ_BIT); - commandList->ImageMemoryBarrier(barrier.Update(dfgPreintegrationTexture.image), - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + width /= 2; + height /= 2; - commandList->EndCommands(); - device->FlushCommandList(commandList); + Graphics::Profiler::EndQuery(); + + } + + commandList->ImageMemoryBarrier(probe->filteredSpecular.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + Graphics::Profiler::EndQuery(); + + Graphics::Profiler::EndQuery(); + + } + + void MainRenderer::Update() { + + textRenderer.Update(); + + haltonIndex = (haltonIndex + 1) % haltonSequence.size(); + frameCount++; + + } + + void MainRenderer::CreateGlobalDescriptorSetLayout() { + + if (!device->support.bindless) + return; + + auto samplerDesc = Graphics::SamplerDesc{ + .filter = VK_FILTER_LINEAR, + .mode = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .maxLod = 12, + .anisotropicFiltering = true + }; + globalSampler = device->CreateSampler(samplerDesc); + + samplerDesc = Graphics::SamplerDesc{ + .filter = VK_FILTER_LINEAR, + .mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .maxLod = 12, + .anisotropicFiltering = true + }; + globalClampToEdgeSampler = device->CreateSampler(samplerDesc); + + samplerDesc = Graphics::SamplerDesc{ + .filter = VK_FILTER_NEAREST, + .mode = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .maxLod = 12, + .anisotropicFiltering = false + }; + globalNearestSampler = device->CreateSampler(samplerDesc); + + + + auto layoutDesc = Graphics::DescriptorSetLayoutDesc{ + .bindings = { + { + .bindingIdx = 0, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 2, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 8192, .bindless = true + }, + { + .bindingIdx = 3, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = 16384, .bindless = true + } + }, + .bindingCount = 4 + }; + globalDescriptorSetLayout = device->CreateDescriptorSetLayout(layoutDesc); + + PipelineManager::OverrideDescriptorSetLayout(globalDescriptorSetLayout, 0); + + } + + void MainRenderer::SetUniforms(const Ref& target, const Ref& scene, const CameraComponent& camera) { + + auto globalUniforms = GlobalUniforms{ + .vMatrix = camera.viewMatrix, + .pMatrix = camera.projectionMatrix, + .ivMatrix = camera.invViewMatrix, + .ipMatrix = camera.invProjectionMatrix, + .pvMatrixLast = camera.GetLastJitteredMatrix(), + .pvMatrixCurrent = camera.projectionMatrix * camera.viewMatrix, + .ipvMatrixLast = glm::inverse(camera.GetLastJitteredMatrix()), + .ipvMatrixCurrent = glm::inverse(camera.projectionMatrix * camera.viewMatrix), + .vMatrixLast = camera.GetLastViewMatrix(), + .jitterLast = camera.GetLastJitter(), + .jitterCurrent = camera.GetJitter(), + .cameraLocation = vec4(camera.GetLocation(), 0.0f), + .cameraDirection = vec4(camera.direction, 0.0f), + .cameraUp = vec4(camera.up, 0.0f), + .cameraRight = vec4(camera.right, 0.0f), + .planetCenter = vec4(scene->sky.planetCenter, 0.0f), + .windDir = glm::normalize(scene->wind.direction), + .windSpeed = scene->wind.speed, + .planetRadius = scene->sky.planetRadius, + .time = Clock::Get(), + .deltaTime = Clock::GetDelta(), + .frameCount = frameCount, + .mipLodBias = -1.0f / target->GetScalingFactor(), + .cameraNearPlane = camera.nearPlane, + .cameraFarPlane = camera.farPlane, + }; + + auto frustumPlanes = camera.frustum.GetPlanes(); + std::copy(frustumPlanes.begin(), frustumPlanes.end(), &globalUniforms.frustumPlanes[0]); + + globalUniformBuffer->SetData(&globalUniforms, 0, sizeof(GlobalUniforms)); + + if (scene->irradianceVolume) { + auto volume = scene->irradianceVolume; + + if (volume->scroll) { + auto pos = camera.GetLocation(); + + auto volumeSize = volume->aabb.GetSize(); + auto volumeAABB = Volume::AABB(-volumeSize / 2.0f + pos, volumeSize / 2.0f + pos); + volume->SetAABB(volumeAABB); + } + + auto probeCountPerCascade = volume->probeCount.x * volume->probeCount.y * + volume->probeCount.z; + auto ddgiUniforms = DDGIUniforms{ + .volumeCenter = vec4(camera.GetLocation(), 1.0f), + .volumeProbeCount = ivec4(volume->probeCount, probeCountPerCascade), + .cascadeCount = volume->cascadeCount, + .volumeBias = volume->bias, + .volumeIrradianceRes = volume->irrRes, + .volumeMomentsRes = volume->momRes, + .rayCount = volume->rayCount, + .inactiveRayCount = volume->rayCountInactive, + .hysteresis = volume->hysteresis, + .volumeGamma = volume->gamma, + .volumeStrength = volume->strength, + .depthSharpness = volume->sharpness, + .optimizeProbes = volume->optimizeProbes ? 1 : 0, + .volumeEnabled = volume->enable ? 1 : 0 + }; + + for (int32_t i = 0; i < volume->cascadeCount; i++) { + ddgiUniforms.cascades[i] = DDGICascade{ + .volumeMin = vec4(volume->cascades[i].aabb.min, 1.0f), + .volumeMax = vec4(volume->cascades[i].aabb.max, 1.0f), + .cellSize = vec4(volume->cascades[i].cellSize, glm::length(volume->cascades[i].cellSize)), + .offsetDifference = ivec4(volume->cascades[i].offsetDifferences, 0), + }; + } + + ddgiUniformBuffer->SetData(&ddgiUniforms, 0, sizeof(DDGIUniforms)); + } + else { + auto ddgiUniforms = DDGIUniforms{ + .volumeEnabled = 0 + }; + + for (int32_t i = 0; i < MAX_IRRADIANCE_VOLUME_CASCADES; i++) { + ddgiUniforms.cascades[i] = DDGICascade{ + .volumeMin = vec4(0.0f), + .volumeMax = vec4(0.0f), + .cellSize = vec4(0.0f), + }; + } + + ddgiUniformBuffer->SetData(&ddgiUniforms, 0, sizeof(DDGIUniforms)); + } + + auto clouds = scene->sky.clouds; + if (clouds && clouds->enable && clouds->castShadow && scene->HasMainLight()) { + const auto& light = scene->GetMainLight(); + + CloudShadow cloudShadowUniform; + clouds->GetShadowMatrices(camera, glm::normalize(light.transformedProperties.directional.direction), + cloudShadowUniform.vMatrix, cloudShadowUniform.pMatrix); + + cloudShadowUniform.ivMatrix = glm::inverse(cloudShadowUniform.vMatrix); + cloudShadowUniform.ipMatrix = glm::inverse(cloudShadowUniform.pMatrix); + + cloudShadowUniform.vMatrix = cloudShadowUniform.vMatrix * camera.invViewMatrix; + + cloudShadowUniformBuffer->SetData(&cloudShadowUniform, 0, sizeof(CloudShadow)); + } + + auto meshes = scene->GetMeshes(); + for (auto& mesh : meshes) { + if (!mesh.IsLoaded() || !mesh->impostor) continue; + + auto impostor = mesh->impostor; + Mesh::Impostor::ImpostorInfo impostorInfo = { + .center = vec4(impostor->center, 1.0f), + .radius = impostor->radius, + .views = impostor->views, + .cutoff = impostor->cutoff, + .mipBias = impostor->mipBias + }; + + impostor->impostorInfoBuffer.SetData(&impostorInfo, 0); + } + + } + + void MainRenderer::PreintegrateBRDF() { + + auto pipelineConfig = PipelineConfig("brdf/preintegrateDFG.csh"); + auto computePipeline = PipelineManager::GetPipeline(pipelineConfig); + + const int32_t res = 256; + dfgPreintegrationTexture = Texture::Texture2D(res, res, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + + auto commandList = device->GetCommandList(Graphics::QueueType::GraphicsQueue, true); + + commandList->BeginCommands(); + commandList->BindPipeline(computePipeline); + + auto barrier = Graphics::ImageBarrier(VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + commandList->ImageMemoryBarrier(barrier.Update(dfgPreintegrationTexture.image), + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + uint32_t groupCount = res / 8; + + commandList->BindImage(dfgPreintegrationTexture.image, 3, 0); + commandList->Dispatch(groupCount, groupCount, 1); + + barrier = Graphics::ImageBarrier(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT); + commandList->ImageMemoryBarrier(barrier.Update(dfgPreintegrationTexture.image), + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + + commandList->EndCommands(); + device->FlushCommandList(commandList); - } + } - PipelineConfig MainRenderer::GetPipelineConfigForPrimitives(Ref &frameBuffer, - Buffer::VertexArray &vertexArray, VkPrimitiveTopology topology, bool testDepth) { + PipelineConfig MainRenderer::GetPipelineConfigForPrimitives(Ref& frameBuffer, + Buffer::VertexArray& vertexArray, VkPrimitiveTopology topology, bool testDepth) { - const auto shaderConfig = ShaderConfig { - { "primitive.vsh", VK_SHADER_STAGE_VERTEX_BIT}, - { "primitive.fsh", VK_SHADER_STAGE_FRAGMENT_BIT}, - }; + const auto shaderConfig = ShaderConfig{ + { "primitive.vsh", VK_SHADER_STAGE_VERTEX_BIT}, + { "primitive.fsh", VK_SHADER_STAGE_FRAGMENT_BIT}, + }; - auto pipelineDesc = Graphics::GraphicsPipelineDesc { - .frameBuffer = frameBuffer, - .vertexInputInfo = vertexArray.GetVertexInputState(), - }; + auto pipelineDesc = Graphics::GraphicsPipelineDesc{ + .frameBuffer = frameBuffer, + .vertexInputInfo = vertexArray.GetVertexInputState(), + }; - pipelineDesc.assemblyInputInfo.topology = topology; - pipelineDesc.depthStencilInputInfo.depthTestEnable = testDepth; - pipelineDesc.rasterizer.cullMode = VK_CULL_MODE_NONE; - pipelineDesc.rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + pipelineDesc.assemblyInputInfo.topology = topology; + pipelineDesc.depthStencilInputInfo.depthTestEnable = testDepth; + pipelineDesc.rasterizer.cullMode = VK_CULL_MODE_NONE; + pipelineDesc.rasterizer.polygonMode = VK_POLYGON_MODE_FILL; - return PipelineConfig(shaderConfig, pipelineDesc); + return PipelineConfig(shaderConfig, pipelineDesc); - } + } - } + } } diff --git a/src/engine/renderer/MainRenderer.h b/src/engine/renderer/MainRenderer.h index 620183526..18c59720a 100644 --- a/src/engine/renderer/MainRenderer.h +++ b/src/engine/renderer/MainRenderer.h @@ -48,7 +48,7 @@ namespace Atlas { void RenderScene(Ref viewport, Ref target, Ref scene, Ref primitiveBatch = nullptr, Texture::Texture2D* texture = nullptr); - void PathTraceScene(Ref viewport, Ref target, + void PathTraceScene(Ref viewport, Ref target, Ref scene, Texture::Texture2D* texture = nullptr); void RenderPrimitiveBatch(Ref viewport, Ref target, @@ -66,98 +66,11 @@ namespace Atlas { AtmosphereRenderer atmosphereRenderer; PathTracingRenderer pathTracingRenderer; - Ref font; - private: - struct alignas(16) PackedMaterial { - - int32_t baseColor; - int32_t emissiveColor; - int32_t transmissionColor; - - uint32_t emissiveIntensityTiling; - - int32_t data0; - int32_t data1; - int32_t data2; - - int32_t features; - - }; - - struct alignas(16) GlobalUniforms { - vec4 frustumPlanes[6]; - mat4 vMatrix; - mat4 pMatrix; - mat4 ivMatrix; - mat4 ipMatrix; - mat4 pvMatrixLast; - mat4 pvMatrixCurrent; - mat4 ipvMatrixLast; - mat4 ipvMatrixCurrent; - vec2 jitterLast; - vec2 jitterCurrent; - vec4 cameraLocation; - vec4 cameraDirection; - vec4 cameraUp; - vec4 cameraRight; - vec4 planetCenter; - vec2 windDir; - float windSpeed; - float planetRadius; - float time; - float deltaTime; - uint32_t frameCount; - float mipLodBias; - }; - - struct alignas(16) DDGICascade { - vec4 volumeMin; - vec4 volumeMax; - vec4 cellSize; - ivec4 offsetDifference; - }; - - struct alignas(16) DDGIUniforms { - - DDGICascade cascades[MAX_IRRADIANCE_VOLUME_CASCADES]; - - vec4 volumeCenter; - ivec4 volumeProbeCount; - int32_t cascadeCount; - - float volumeBias; - - int32_t volumeIrradianceRes; - int32_t volumeMomentsRes; - - uint32_t rayCount; - uint32_t inactiveRayCount; - - float hysteresis; - - float volumeGamma; - float volumeStrength; - - float depthSharpness; - int optimizeProbes; - - int32_t volumeEnabled; - }; - void CreateGlobalDescriptorSetLayout(); void SetUniforms(const Ref& target, const Ref& scene, const CameraComponent& camera); - void PrepareMaterials(Ref scene, std::vector& materials, - std::unordered_map& materialMap); - - void PrepareBindlessData(Ref scene, std::vector>& images, - std::vector>& blasBuffers, std::vector>& triangleBuffers, - std::vector>& bvhTriangleBuffers, std::vector>& triangleOffsetBuffers); - - void FillRenderList(Ref scene, const CameraComponent& camera); - void PreintegrateBRDF(); PipelineConfig GetPipelineConfigForPrimitives(Ref& frameBuffer, @@ -170,8 +83,11 @@ namespace Atlas { Ref globalUniformBuffer; Ref pathTraceGlobalUniformBuffer; Ref ddgiUniformBuffer; + Ref lightUniformBuffer; + Ref cloudShadowUniformBuffer; Ref globalDescriptorSetLayout; Ref globalSampler; + Ref globalClampToEdgeSampler; Ref globalNearestSampler; Buffer::VertexArray vertexArray; @@ -202,12 +118,12 @@ namespace Atlas { VolumetricCloudRenderer volumetricCloudRenderer; FSR2Renderer fsr2Renderer; - RenderList renderList; - std::vector haltonSequence; size_t haltonIndex = 0; uint32_t frameCount = 0; + std::vector shadowImageBarriers; + }; } diff --git a/src/engine/renderer/OceanRenderer.cpp b/src/engine/renderer/OceanRenderer.cpp index 1e532302e..ebf4534a5 100644 --- a/src/engine/renderer/OceanRenderer.cpp +++ b/src/engine/renderer/OceanRenderer.cpp @@ -19,7 +19,6 @@ namespace Atlas { uniformBuffer = Buffer::UniformBuffer(sizeof(Uniforms)); depthUniformBuffer = Buffer::UniformBuffer(sizeof(Uniforms)); lightUniformBuffer = Buffer::UniformBuffer(sizeof(Light)); - cloudShadowUniformBuffer = Buffer::UniformBuffer(sizeof(CloudShadow)); auto samplerDesc = Graphics::SamplerDesc{ .filter = VK_FILTER_NEAREST, @@ -81,7 +80,7 @@ namespace Atlas { shadowUniform.cascadeBlendDistance = shadow->cascadeBlendDistance; shadowUniform.resolution = vec2(shadow->resolution); - commandList->BindImage(shadow->maps.image, shadowSampler, 3, 8); + commandList->BindImage(shadow->maps->image, shadowSampler, 3, 8); auto componentCount = shadow->viewCount; for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { @@ -92,8 +91,8 @@ namespace Atlas { auto texelSize = glm::max(abs(corners[0].x - corners[1].x), abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; shadowUniform.cascades[i].distance = cascade->farDistance; - shadowUniform.cascades[i].cascadeSpace = cascade->projectionMatrix * - cascade->viewMatrix * camera.invViewMatrix; + shadowUniform.cascades[i].cascadeSpace = glm::transpose(cascade->projectionMatrix * + cascade->viewMatrix * camera.invViewMatrix); shadowUniform.cascades[i].texelSize = texelSize; } else { @@ -111,26 +110,10 @@ namespace Atlas { bool fogEnabled = fog && fog->enable; bool cloudsEnabled = clouds && clouds->enable; - bool cloudShadowsEnabled = clouds && clouds->enable && clouds->castShadow; - - CloudShadow cloudShadowUniform; - - if (cloudShadowsEnabled) { + if (cloudShadowsEnabled) clouds->shadowTexture.Bind(commandList, 3, 15); - clouds->GetShadowMatrices(camera, glm::normalize(light.transformedProperties.directional.direction), - cloudShadowUniform.vMatrix, cloudShadowUniform.pMatrix); - - cloudShadowUniform.vMatrix = cloudShadowUniform.vMatrix * camera.invViewMatrix; - - cloudShadowUniform.ivMatrix = glm::inverse(cloudShadowUniform.vMatrix); - cloudShadowUniform.ipMatrix = glm::inverse(cloudShadowUniform.pMatrix); - } - - cloudShadowUniformBuffer.SetData(&cloudShadowUniform, 0); - cloudShadowUniformBuffer.Bind(commandList, 3, 14); - { Graphics::Profiler::BeginQuery("Caustics"); @@ -177,28 +160,25 @@ namespace Atlas { depthImage->format); } - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {colorImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {depthImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {refractionTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, {depthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(colorImage, refractionTexture.image); commandList->CopyImage(depthImage, depthTexture.image); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {colorImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {depthImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {refractionTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {depthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); } @@ -214,7 +194,7 @@ namespace Atlas { if (cloudShadowsEnabled) config.AddMacro("CLOUD_SHADOWS"); if (ocean->rippleTexture.IsValid()) config.AddMacro("RIPPLE_TEXTURE"); if (ocean->foamTexture.IsValid()) config.AddMacro("FOAM_TEXTURE"); - if (scene->terrain && scene->terrain->shoreLine.IsValid()) config.AddMacro("TERRAIN"); + if (scene->terrain.IsLoaded() && scene->terrain->shoreLine.IsValid()) config.AddMacro("TERRAIN"); auto pipeline = PipelineManager::GetPipeline(config); @@ -276,7 +256,7 @@ namespace Atlas { depthTexture.Bind(commandList, 3, 5); target->oceanDepthTexture.Bind(commandList, 3, 20); - if (scene->terrain) { + if (scene->terrain.IsLoaded()) { if (scene->terrain->shoreLine.IsValid()) { auto terrain = scene->terrain; @@ -344,15 +324,14 @@ namespace Atlas { VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - std::vector bufferBarriers; - std::vector imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->lightingTexture.image, layout, access}, {rtData->depthTexture->image, layout, access}, {rtData->stencilTexture->image, layout, access}, {rtData->velocityTexture->image, layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); } if (ocean->underwaterShader) { @@ -360,23 +339,20 @@ namespace Atlas { auto& colorImage = target->afterLightingFrameBuffer->GetColorImage(0); - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {colorImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {refractionTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(colorImage, refractionTexture.image); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {colorImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {refractionTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); const int32_t groupSize = 8; @@ -386,7 +362,7 @@ namespace Atlas { groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); - underWaterPipelineConfig.ManageMacro("TERRAIN", scene->terrain && scene->terrain->shoreLine.IsValid()); + underWaterPipelineConfig.ManageMacro("TERRAIN", scene->terrain.IsLoaded() && scene->terrain->shoreLine.IsValid()); auto pipeline = PipelineManager::GetPipeline(underWaterPipelineConfig); commandList->BindPipeline(pipeline); @@ -431,7 +407,7 @@ namespace Atlas { target->oceanDepthOnlyFrameBuffer); auto config = GeneratePipelineConfig(target, true, ocean->wireframe); - if (scene->terrain && scene->terrain->shoreLine.IsValid()) config.AddMacro("TERRAIN"); + if (scene->terrain.IsLoaded() && scene->terrain->shoreLine.IsValid()) config.AddMacro("TERRAIN"); auto pipeline = PipelineManager::GetPipeline(config); @@ -482,7 +458,7 @@ namespace Atlas { refractionTexture.Bind(commandList, 3, 4); depthTexture.Bind(commandList, 3, 5); - if (scene->terrain) { + if (scene->terrain.IsLoaded()) { if (scene->terrain->shoreLine.IsValid()) { auto terrain = scene->terrain; diff --git a/src/engine/renderer/OceanRenderer.h b/src/engine/renderer/OceanRenderer.h index d408b39df..c9250d198 100644 --- a/src/engine/renderer/OceanRenderer.h +++ b/src/engine/renderer/OceanRenderer.h @@ -86,7 +86,6 @@ namespace Atlas { Buffer::UniformBuffer uniformBuffer; Buffer::UniformBuffer depthUniformBuffer; Buffer::UniformBuffer lightUniformBuffer; - Buffer::UniformBuffer cloudShadowUniformBuffer; Ref nearestSampler; Ref shadowSampler; diff --git a/src/engine/renderer/OpaqueRenderer.cpp b/src/engine/renderer/OpaqueRenderer.cpp index 883e1264a..00c417629 100644 --- a/src/engine/renderer/OpaqueRenderer.cpp +++ b/src/engine/renderer/OpaqueRenderer.cpp @@ -24,19 +24,21 @@ namespace Atlas { if (!mainPass) return; + auto sceneState = &scene->renderState; + commandList->BindBuffer(mainPass->currentMatricesBuffer, 1, 1); commandList->BindBuffer(mainPass->lastMatricesBuffer, 1, 2); commandList->BindBuffer(mainPass->impostorMatricesBuffer, 1, 3); // Bind wind map - scene->wind.noiseMap.Bind(commandList, 3, 7); + scene->wind.noiseMap.Bind(commandList, 3, 8); int32_t subDataCount = 0; // Retrieve all possible materials; for (const auto& [meshId, instances] : mainPass->meshToInstancesMap) { if (!instances.count) continue; - auto& mesh = mainPass->meshIdToMeshMap[meshId]; + auto& mesh = renderList->meshIdToMeshMap[meshId]; for (auto& subData : mesh->data.subData) { if (!subData.material.IsLoaded()) continue; @@ -103,6 +105,8 @@ namespace Atlas { material->aoMap->Bind(commandList, 3, 5); if (material->HasDisplacementMap()) material->displacementMap->Bind(commandList, 3, 6); + if (material->HasEmissiveMap()) + material->emissiveMap->Bind(commandList, 3, 7); #endif auto pushConstants = PushConstants { @@ -116,13 +120,17 @@ namespace Atlas { .windTextureLod = mesh->windNoiseTextureLod, .windBendScale = mesh->windBendScale, .windWiggleScale = mesh->windWiggleScale, - .baseColorTextureIdx = material->HasBaseColorMap() ? scene->textureToBindlessIdx[material->baseColorMap.Get()] : 0, - .opacityTextureIdx = material->HasOpacityMap() ? scene->textureToBindlessIdx[material->opacityMap.Get()] : 0, - .normalTextureIdx = material->HasNormalMap() ? scene->textureToBindlessIdx[material->normalMap.Get()] : 0, - .roughnessTextureIdx = material->HasRoughnessMap() ? scene->textureToBindlessIdx[material->roughnessMap.Get()] : 0, - .metalnessTextureIdx = material->HasMetalnessMap() ? scene->textureToBindlessIdx[material->metalnessMap.Get()] : 0, - .aoTextureIdx = material->HasAoMap() ? scene->textureToBindlessIdx[material->aoMap.Get()] : 0, - .heightTextureIdx = material->HasDisplacementMap() ? scene->textureToBindlessIdx[material->displacementMap.Get()] : 0, + .uvAnimationX = material->uvAnimation.x, + .uvAnimationY = material->uvAnimation.y, + .uvTiling = material->tiling, + .baseColorTextureIdx = material->HasBaseColorMap() ? sceneState->textureToBindlessIdx[material->baseColorMap.Get()] : 0, + .opacityTextureIdx = material->HasOpacityMap() ? sceneState->textureToBindlessIdx[material->opacityMap.Get()] : 0, + .normalTextureIdx = material->HasNormalMap() ? sceneState->textureToBindlessIdx[material->normalMap.Get()] : 0, + .roughnessTextureIdx = material->HasRoughnessMap() ? sceneState->textureToBindlessIdx[material->roughnessMap.Get()] : 0, + .metalnessTextureIdx = material->HasMetalnessMap() ? sceneState->textureToBindlessIdx[material->metalnessMap.Get()] : 0, + .aoTextureIdx = material->HasAoMap() ? sceneState->textureToBindlessIdx[material->aoMap.Get()] : 0, + .heightTextureIdx = material->HasDisplacementMap() ? sceneState->textureToBindlessIdx[material->displacementMap.Get()] : 0, + .emissiveTextureIdx = material->HasEmissiveMap() ? sceneState->textureToBindlessIdx[material->emissiveMap.Get()] : 0, }; commandList->PushConstants("constants", &pushConstants); @@ -181,6 +189,9 @@ namespace Atlas { if (material->HasDisplacementMap() && hasTangents && hasTexCoords) { macros.push_back("HEIGHT_MAP"); } + if (material->HasEmissiveMap() && hasTexCoords) { + macros.push_back("EMISSIVE_MAP"); + } // This is a check if we have any maps at all (no macros, no maps) if (macros.size()) { macros.push_back("TEX_COORDS"); diff --git a/src/engine/renderer/OpaqueRenderer.h b/src/engine/renderer/OpaqueRenderer.h index f19371056..af4d3aa90 100644 --- a/src/engine/renderer/OpaqueRenderer.h +++ b/src/engine/renderer/OpaqueRenderer.h @@ -39,6 +39,9 @@ namespace Atlas { float windTextureLod; float windBendScale; float windWiggleScale; + float uvAnimationX; + float uvAnimationY; + float uvTiling; uint32_t baseColorTextureIdx; uint32_t opacityTextureIdx; uint32_t normalTextureIdx; @@ -46,6 +49,7 @@ namespace Atlas { uint32_t metalnessTextureIdx; uint32_t aoTextureIdx; uint32_t heightTextureIdx; + uint32_t emissiveTextureIdx; }; diff --git a/src/engine/renderer/PathTracingRenderer.cpp b/src/engine/renderer/PathTracingRenderer.cpp index f90f21612..7beb40c50 100644 --- a/src/engine/renderer/PathTracingRenderer.cpp +++ b/src/engine/renderer/PathTracingRenderer.cpp @@ -18,6 +18,8 @@ namespace Atlas { this->device = device; + // helper.stochasticLightSelection = true; + rayGenPipelineConfig = PipelineConfig("pathtracer/rayGen.csh"); rayHitPipelineConfig = PipelineConfig("pathtracer/rayHit.csh"); @@ -26,7 +28,7 @@ namespace Atlas { } - void PathTracingRenderer::Render(Ref renderTarget, Ref scene, + void PathTracingRenderer::Render(Ref renderTarget, Ref scene, ivec2 imageSubdivisions, Graphics::CommandList* commandList) { if (!scene->IsRtDataValid()) @@ -35,8 +37,8 @@ namespace Atlas { Graphics::Profiler::BeginQuery("Path tracing"); auto& camera = scene->GetMainCamera(); - auto width = renderTarget->GetWidth(); - auto height = renderTarget->GetHeight(); + auto width = renderTarget->GetScaledWidth(); + auto height = renderTarget->GetScaledHeight(); auto rayCount = realTime ? 2 * width * height * realTimeSamplesPerFrame : 2 * width * height; @@ -56,7 +58,6 @@ namespace Atlas { imageSubdivisions = ivec2(1); sampleCount = 0; frameCount++; - renderTarget->Swap(); } rayGenPipelineConfig.ManageMacro("REALTIME", realTime); @@ -98,50 +99,63 @@ namespace Atlas { rayHitUniformBuffer.SetData(&uniforms, i); } - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers.push_back({ renderTarget->texture.image, + imageBarriers.clear(); + imageBarriers.push_back({ renderTarget->outputTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); imageBarriers.push_back({ renderTarget->radianceTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); imageBarriers.push_back({ renderTarget->historyRadianceTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT }); + auto rtData = renderTarget->GetData(FULL_RES); + auto velocityTexture = rtData->velocityTexture; + auto depthTexture = rtData->depthTexture; + auto normalTexture = rtData->normalTexture; + auto materialIdxTexture = rtData->materialIdxTexture; + auto baseColorTexture = rtData->baseColorTexture; + + auto historyRtData = renderTarget->GetHistoryData(FULL_RES); + auto historyDepthTexture = historyRtData->depthTexture; + auto historyNormalTexture = historyRtData->normalTexture; + auto historyMaterialIdxTexture = historyRtData->materialIdxTexture; + if (!realTime) { - commandList->BindImage(renderTarget->texture.image, 3, 1); + commandList->BindImage(renderTarget->outputTexture.image, 3, 1); commandList->BindImage(renderTarget->radianceTexture.image, 3, 3); commandList->BindImage(renderTarget->historyRadianceTexture.image, 3, 2); } else { + imageBarriers.push_back({ renderTarget->lightingTexture.image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); imageBarriers.push_back({ renderTarget->frameAccumTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); imageBarriers.push_back({ renderTarget->radianceTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); - imageBarriers.push_back({ renderTarget->velocityTexture.image, + imageBarriers.push_back({ velocityTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); - imageBarriers.push_back({ renderTarget->depthTexture.image, + imageBarriers.push_back({ depthTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); - imageBarriers.push_back({ renderTarget->normalTexture.image, + imageBarriers.push_back({ normalTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); - imageBarriers.push_back({ renderTarget->materialIdxTexture.image, + imageBarriers.push_back({ materialIdxTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); - imageBarriers.push_back({ renderTarget->albedoTexture.image, + imageBarriers.push_back({ baseColorTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT }); + commandList->BindImage(renderTarget->lightingTexture.image, 3, 0); commandList->BindImage(renderTarget->frameAccumTexture.image, 3, 1); commandList->BindImage(renderTarget->radianceTexture.image, 3, 3); renderTarget->historyRadianceTexture.Bind(commandList, 3, 2); - commandList->BindImage(renderTarget->velocityTexture.image, 3, 5); - commandList->BindImage(renderTarget->depthTexture.image, 3, 6); - commandList->BindImage(renderTarget->normalTexture.image, 3, 7); - commandList->BindImage(renderTarget->materialIdxTexture.image, 3, 8); - commandList->BindImage(renderTarget->albedoTexture.image, 3, 9); + commandList->BindImage(velocityTexture->image, 3, 5); + commandList->BindImage(depthTexture->image, 3, 6); + commandList->BindImage(normalTexture->image, 3, 7); + commandList->BindImage(materialIdxTexture->image, 3, 8); + commandList->BindImage(baseColorTexture->image, 3, 9); } - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); auto tileResolution = resolution / imageSubdivisions; auto groupCount = tileResolution / 8; @@ -178,7 +192,7 @@ namespace Atlas { commandList->ImageMemoryBarrier(renderTarget->frameAccumTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); - commandList->ImageMemoryBarrier(renderTarget->velocityTexture.image, VK_IMAGE_LAYOUT_GENERAL, + commandList->ImageMemoryBarrier(velocityTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); Graphics::Profiler::EndAndBeginQuery("Bounce " + std::to_string(i)); @@ -199,34 +213,32 @@ namespace Atlas { imageBarriers.push_back({ renderTarget->frameAccumTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->velocityTexture.image, + imageBarriers.push_back({ velocityTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->depthTexture.image, + imageBarriers.push_back({ depthTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->normalTexture.image, + imageBarriers.push_back({ normalTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->materialIdxTexture.image, + imageBarriers.push_back({ materialIdxTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->albedoTexture.image, + imageBarriers.push_back({ baseColorTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->historyDepthTexture.image, + imageBarriers.push_back({ historyDepthTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->historyNormalTexture.image, + imageBarriers.push_back({ historyNormalTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ renderTarget->historyMaterialIdxTexture.image, + imageBarriers.push_back({ historyMaterialIdxTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT }); - commandList->PipelineBarrier(imageBarriers, bufferBarriers); - - commandList->BindImage(renderTarget->texture.image, 3, 0); + commandList->PipelineBarrier(imageBarriers, {}); - renderTarget->velocityTexture.Bind(commandList, 3, 4); - renderTarget->depthTexture.Bind(commandList, 3, 5); - renderTarget->normalTexture.Bind(commandList, 3, 6); - renderTarget->materialIdxTexture.Bind(commandList, 3, 7); - renderTarget->historyDepthTexture.Bind(commandList, 3, 9); - renderTarget->historyNormalTexture.Bind(commandList, 3, 10); - renderTarget->historyMaterialIdxTexture.Bind(commandList, 3, 11); + velocityTexture->Bind(commandList, 3, 4); + depthTexture->Bind(commandList, 3, 5); + normalTexture->Bind(commandList, 3, 6); + materialIdxTexture->Bind(commandList, 3, 7); + historyDepthTexture->Bind(commandList, 3, 9); + historyNormalTexture->Bind(commandList, 3, 10); + historyMaterialIdxTexture->Bind(commandList, 3, 11); struct alignas(16) PushConstants { float historyClipMax; @@ -274,13 +286,11 @@ namespace Atlas { if (imageOffset.y == imageSubdivisions.y) { imageOffset.y = 0; sampleCount++; - - // We don't want to swap already here when rendering in realtime - if (!realTime) - renderTarget->Swap(); } - commandList->ImageTransition(renderTarget->texture.image, + commandList->ImageTransition(renderTarget->outputTexture.image, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); + commandList->ImageTransition(renderTarget->lightingTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); commandList->ImageTransition(renderTarget->radianceTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); diff --git a/src/engine/renderer/PathTracingRenderer.h b/src/engine/renderer/PathTracingRenderer.h index 8a68b5b57..10890059c 100644 --- a/src/engine/renderer/PathTracingRenderer.h +++ b/src/engine/renderer/PathTracingRenderer.h @@ -19,7 +19,7 @@ namespace Atlas { void Init(Graphics::GraphicsDevice* device); - void Render(Ref renderTarget, Ref scene, + void Render(Ref renderTarget, Ref scene, ivec2 imageSubdivisions, Graphics::CommandList* commandList); bool UpdateData(Scene::Scene* scene); @@ -84,6 +84,8 @@ namespace Atlas { size_t frameCount = 0; + std::vector imageBarriers; + }; } diff --git a/src/engine/renderer/PostProcessRenderer.cpp b/src/engine/renderer/PostProcessRenderer.cpp index cc18d4098..1742090e6 100644 --- a/src/engine/renderer/PostProcessRenderer.cpp +++ b/src/engine/renderer/PostProcessRenderer.cpp @@ -33,6 +33,7 @@ namespace Atlas { const auto& vignette = postProcessing.vignette; const auto& taa = postProcessing.taa; auto& sharpen = postProcessing.sharpen; + const auto& bloom = postProcessing.bloom; ivec2 resolution = ivec2(target->GetWidth(), target->GetHeight()); @@ -124,6 +125,10 @@ namespace Atlas { Graphics::Profiler::EndQuery(); } + if (bloom.enable) { + GenerateBloom(bloom, &target->hdrTexture, &target->bloomTexture, commandList); + } + { Graphics::Profiler::BeginQuery("Main"); @@ -145,10 +150,18 @@ namespace Atlas { pipelineConfig.ManageMacro("VIGNETTE", postProcessing.vignette.enable); pipelineConfig.ManageMacro("CHROMATIC_ABERRATION", postProcessing.chromaticAberration.enable); pipelineConfig.ManageMacro("FILM_GRAIN", postProcessing.filmGrain.enable); + pipelineConfig.ManageMacro("BLOOM", postProcessing.bloom.enable); + pipelineConfig.ManageMacro("BLOOM_DIRT", postProcessing.bloom.enable && postProcessing.bloom.dirtMap.IsLoaded()); auto pipeline = PipelineManager::GetPipeline(pipelineConfig); commandList->BindPipeline(pipeline); + if (bloom.enable) { + target->bloomTexture.Bind(commandList, 3, 1); + if (bloom.dirtMap.IsLoaded()) + bloom.dirtMap->Bind(commandList, 3, 2); + } + SetUniforms(camera, scene); readTexture->Bind(commandList, 3, 0); @@ -170,112 +183,120 @@ namespace Atlas { } - void PostProcessRenderer::Render(Ref target, Ref scene, - Graphics::CommandList* commandList, Texture::Texture2D* texture) { + void PostProcessRenderer::GenerateBloom(const PostProcessing::Bloom& bloom, Texture::Texture2D* hdrTexture, + Texture::Texture2D* bloomTexture, Graphics::CommandList* commandList) { + + const uint32_t maxDownsampleCount = 12; + ivec2 resolutions[maxDownsampleCount]; - Graphics::Profiler::BeginQuery("Postprocessing"); + Graphics::Profiler::BeginQuery("Bloom"); + Graphics::Profiler::BeginQuery("Copy texture"); - auto& postProcessing = scene->postProcessing; + CopyToTexture(hdrTexture, bloomTexture, commandList); - auto& camera = scene->GetMainCamera(); - const auto& chromaticAberration = postProcessing.chromaticAberration; - const auto& vignette = postProcessing.vignette; - const auto& taa = postProcessing.taa; - auto& sharpen = postProcessing.sharpen; + auto mipLevels = std::min(bloom.mipLevels, bloomTexture->image->mipLevels); + mipLevels = std::min(mipLevels, maxDownsampleCount); - ivec2 resolution = ivec2(target->GetWidth(), target->GetHeight()); + auto textureIn = bloomTexture; + auto textureOut = hdrTexture; - if (sharpen.enable && !postProcessing.fsr2) { - Graphics::Profiler::BeginQuery("Sharpen"); + // Downsample + { + Graphics::Profiler::EndAndBeginQuery("Downsample"); - auto pipeline = PipelineManager::GetPipeline(sharpenPipelineConfig); + struct PushConstants { + int mipLevel; + float threshold; + }; + auto pipelineConfig = PipelineConfig("bloom/bloomDownsample.csh"); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); commandList->BindPipeline(pipeline); - ivec2 groupCount = resolution / 8; - groupCount.x += ((groupCount.x * 8 == resolution.x) ? 0 : 1); - groupCount.y += ((groupCount.y * 8 == resolution.y) ? 0 : 1); - - const auto& image = target->historyPostProcessTexture.image; - - commandList->ImageMemoryBarrier(image, VK_IMAGE_LAYOUT_GENERAL, - VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); - - commandList->BindImage(image, 3, 0); - - if (taa.enable) { - target->postProcessTexture.Bind(commandList, 3, 1); - } - else { - target->radianceTexture.Bind(commandList, 3, 1); + ivec2 resolution = ivec2(bloomTexture->width, bloomTexture->height); + resolutions[0] = resolution; + resolution /= 2; + + for (int32_t i = 1; i < mipLevels; i++) { + Graphics::ImageBarrier imageBarriers[] = { + {textureIn->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {textureOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} + }; + commandList->PipelineBarrier(imageBarriers, {}); + + ivec2 groupCount = resolution / 16; + groupCount.x += ((groupCount.x * 16 == resolution.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 16 == resolution.y) ? 0 : 1); + + PushConstants constants { + .mipLevel = i - 1, + .threshold = bloom.threshold + }; + commandList->PushConstants("constants", &constants); + + commandList->BindImage(textureOut->image, 3, 0, i); + textureIn->Bind(commandList, 3, 1); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + std::swap(textureIn, textureOut); + + resolutions[i] = resolution; + resolution /= 2; } - - // Reduce the sharpening to bring it more in line with FSR2 sharpening - float sharpenFactor = sharpen.factor * 0.5f; - commandList->PushConstants("constants", &sharpenFactor, sizeof(float)); - - commandList->Dispatch(groupCount.x, groupCount.y, 1); - - commandList->ImageMemoryBarrier(image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - Graphics::Profiler::EndQuery(); } + // Upsample { - Graphics::Profiler::BeginQuery("Main"); - - // We can't return here because of the queries - if (device->swapChain->isComplete) { - PipelineConfig pipelineConfig; - - if (!texture) { - commandList->BeginRenderPass(device->swapChain, true); - pipelineConfig = GetMainPipelineConfig(); - } - else { - commandList->BeginRenderPass(target->outputRenderPass, - target->outputFrameBuffer, true); - pipelineConfig = GetMainPipelineConfig(target->outputFrameBuffer); - } - - pipelineConfig.ManageMacro("FILMIC_TONEMAPPING", postProcessing.filmicTonemapping); - pipelineConfig.ManageMacro("VIGNETTE", postProcessing.vignette.enable); - pipelineConfig.ManageMacro("CHROMATIC_ABERRATION", postProcessing.chromaticAberration.enable); - pipelineConfig.ManageMacro("FILM_GRAIN", postProcessing.filmGrain.enable); - - auto pipeline = PipelineManager::GetPipeline(pipelineConfig); - commandList->BindPipeline(pipeline); - - SetUniforms(camera, scene); - - if (sharpen.enable) { - target->historyPostProcessTexture.Bind(commandList, 3, 0); - } - else { - if (taa.enable) { - target->postProcessTexture.Bind(commandList, 3, 0); - } - else { - target->radianceTexture.Bind(commandList, 3, 0); - } - } - commandList->BindBuffer(uniformBuffer, 3, 4); + Graphics::Profiler::EndAndBeginQuery("Upsample"); - commandList->Draw(6, 1, 0, 0); + struct PushConstants { + int additive; + int mipLevel; + float filterSize = 2.0f; + }; - commandList->EndRenderPass(); - - if (texture) { - CopyToTexture(&target->outputTexture, texture, commandList); - } + auto pipelineConfig = PipelineConfig("bloom/bloomUpsample.csh"); + pipelineConfig.ManageMacro("DIRT_MAP", bloom.dirtMap.IsLoaded()); + + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); + commandList->BindPipeline(pipeline); + + for (int32_t i = mipLevels - 2; i >= 0; i--) { + Graphics::ImageBarrier imageBarriers[] = { + {textureIn->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {textureOut->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} + }; + commandList->PipelineBarrier(imageBarriers, {}); + + ivec2 groupCount = resolutions[i] / 8; + groupCount.x += ((groupCount.x * 8 == resolutions[i].x) ? 0 : 1); + groupCount.y += ((groupCount.y * 8 == resolutions[i].y) ? 0 : 1); + + PushConstants constants { + .additive = i != mipLevels - 2 ? 1 : 0, + .mipLevel = i + 1, + .filterSize = bloom.filterSize, + }; + commandList->PushConstants("constants", &constants); + + commandList->BindImage(textureOut->image, 3, 0, i); + textureIn->Bind(commandList, 3, 1); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + std::swap(textureIn, textureOut); } Graphics::Profiler::EndQuery(); + Graphics::Profiler::EndQuery(); } - Graphics::Profiler::EndQuery(); + Graphics::ImageBarrier imageBarriers[] = { + {textureIn->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {textureOut->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT} + }; + commandList->PipelineBarrier(imageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); } @@ -285,23 +306,20 @@ namespace Atlas { auto& srcImage = sourceTexture->image; auto& dstImage = texture->image; - std::vector imageBarriers; - std::vector bufferBarriers; - - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->BlitImage(srcImage, dstImage); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {srcImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {dstImage, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT); } @@ -313,6 +331,7 @@ namespace Atlas { const auto& chromaticAberration = postProcessing.chromaticAberration; const auto& vignette = postProcessing.vignette; const auto& filmGrain = postProcessing.filmGrain; + const auto& bloom = postProcessing.bloom; Uniforms uniforms = { .exposure = camera.exposure, @@ -342,6 +361,11 @@ namespace Atlas { uniforms.filmGrainStrength = filmGrain.strength; } + if (bloom.enable) { + uniforms.bloomStrength = bloom.strength; + uniforms.bloomDirtStrength = bloom.dirtStrength; + } + uniformBuffer->SetData(&uniforms, 0, sizeof(Uniforms)); } @@ -397,4 +421,4 @@ namespace Atlas { } - }} \ No newline at end of file + }} diff --git a/src/engine/renderer/PostProcessRenderer.h b/src/engine/renderer/PostProcessRenderer.h index 9af21337f..f4be22195 100644 --- a/src/engine/renderer/PostProcessRenderer.h +++ b/src/engine/renderer/PostProcessRenderer.h @@ -2,7 +2,6 @@ #include "../System.h" #include "Renderer.h" -#include "PathTracingRenderer.h" namespace Atlas { @@ -18,8 +17,8 @@ namespace Atlas { void Render(Ref target, Ref scene, Graphics::CommandList* commandList, Texture::Texture2D* texture = nullptr); - void Render(Ref target, Ref scene, - Graphics::CommandList* commandList, Texture::Texture2D* texture = nullptr); + void CopyToTexture(Texture::Texture2D* sourceTexture, Texture::Texture2D* texture, + Graphics::CommandList* commandList); private: struct alignas(16) Uniforms { @@ -29,18 +28,22 @@ namespace Atlas { float saturation; float contrast; float filmGrainStrength; - int32_t bloomPasses; + float bloomStrength; + float bloomDirtStrength; float aberrationStrength; float aberrationReversed; float vignetteOffset; float vignettePower; float vignetteStrength; + float padding0; + float padding1; + float padding2; vec4 vignetteColor; vec4 tintColor; }; - void CopyToTexture(Texture::Texture2D* sourceTexture, Texture::Texture2D* texture, - Graphics::CommandList* commandList); + void GenerateBloom(const PostProcessing::Bloom& bloom, Texture::Texture2D* hdrTexture, + Texture::Texture2D* bloomTexture, Graphics::CommandList* commandList); void SetUniforms(const CameraComponent& camera, Ref scene); diff --git a/src/engine/renderer/RTGIRenderer.cpp b/src/engine/renderer/RTGIRenderer.cpp index c7f0cca9e..a5b1e72d3 100644 --- a/src/engine/renderer/RTGIRenderer.cpp +++ b/src/engine/renderer/RTGIRenderer.cpp @@ -61,21 +61,19 @@ namespace Atlas { target->historyGiMomentsTexture.image->layout == VK_IMAGE_LAYOUT_UNDEFINED) { VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - std::vector bufferBarriers; - std::vector imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->historyGiTexture.image, layout, access}, {target->historyGiMomentsTexture.image, layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); } Graphics::Profiler::BeginQuery("Trace rays"); // Try to get a shadow map Ref shadow = nullptr; - auto mainLightEntity = GetMainLightEntity(scene); - if (mainLightEntity.IsValid()) - shadow = mainLightEntity.GetComponent().shadow; + if (scene->HasMainLight()) + shadow = scene->GetMainLight().shadow; auto mainCamera = scene->GetMainCamera(); @@ -114,13 +112,17 @@ namespace Atlas { groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); groupCount.y += ((groupCount.y * 4 == res.y) ? 0 : 1); + auto clouds = scene->sky.clouds; + auto ddgiEnabled = scene->irradianceVolume && scene->irradianceVolume->enable; auto ddgiVisibility = ddgiEnabled && scene->irradianceVolume->visibility; + auto cloudShadowEnabled = clouds && clouds->enable && clouds->castShadow; rtPipelineConfig.ManageMacro("USE_SHADOW_MAP", rtgi->useShadowMap && shadow); rtPipelineConfig.ManageMacro("DDGI", rtgi->ddgi && ddgiEnabled); rtPipelineConfig.ManageMacro("DDGI_VISIBILITY", rtgi->ddgi && ddgiVisibility); rtPipelineConfig.ManageMacro("OPACITY_CHECK", rtgi->opacityCheck); + rtPipelineConfig.ManageMacro("CLOUD_SHADOWS", cloudShadowEnabled && scene->HasMainLight()); auto pipeline = PipelineManager::GetPipeline(rtPipelineConfig); @@ -134,7 +136,9 @@ namespace Atlas { RTUniforms uniforms; uniforms.radianceLimit = rtgi->radianceLimit / glm::max(mainCamera.exposure, 0.00001f); uniforms.bias = rtgi->bias; - uniforms.frameSeed = frameCount++; + uniforms.frameSeed = frameCount++; + uniforms.sampleCount = rtgi->sampleCount; + uniforms.lightSampleCount = rtgi->lightSampleCount; uniforms.textureLevel = rtgi->textureLevel; uniforms.halfRes = target->GetGIResolution() == HALF_RES ? 1 : 0; uniforms.resolution = res; @@ -148,7 +152,7 @@ namespace Atlas { shadowUniform.cascadeCount = shadow->viewCount; shadowUniform.resolution = vec2(shadow->resolution); - commandList->BindImage(shadow->maps.image, shadowSampler, 3, 6); + commandList->BindImage(shadow->maps->image, shadowSampler, 3, 6); auto componentCount = shadow->viewCount; for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { @@ -159,8 +163,8 @@ namespace Atlas { auto texelSize = glm::max(abs(corners[0].x - corners[1].x), abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; shadowUniform.cascades[i].distance = cascade->farDistance; - shadowUniform.cascades[i].cascadeSpace = cascade->projectionMatrix * - cascade->viewMatrix; + shadowUniform.cascades[i].cascadeSpace = glm::transpose(cascade->projectionMatrix * + cascade->viewMatrix); shadowUniform.cascades[i].texelSize = texelSize; } else { @@ -169,8 +173,13 @@ namespace Atlas { } } } + + if (cloudShadowEnabled && scene->HasMainLight()) { + clouds->shadowTexture.Bind(commandList, 3, 9); + } + rtUniformBuffer.SetData(&uniforms, 0); - commandList->BindBuffer(rtUniformBuffer.Get(), 3, 9); + commandList->BindBuffer(rtUniformBuffer.Get(), 3, 10); }); @@ -181,9 +190,6 @@ namespace Atlas { Graphics::Profiler::EndAndBeginQuery("Temporal filter"); { - std::vector imageBarriers; - std::vector bufferBarriers; - ivec2 groupCount = ivec2(res.x / 16, res.y / 16); groupCount.x += ((groupCount.x * 16 == res.x) ? 0 : 1); groupCount.y += ((groupCount.y * 16 == res.y) ? 0 : 1); @@ -200,12 +206,12 @@ namespace Atlas { commandList->PushConstants("constants", &constants); - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->swapGiTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {target->giMomentsTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(target->swapGiTexture.image, 3, 0); commandList->BindImage(target->giMomentsTexture.image, 3, 1); @@ -226,30 +232,27 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); } - std::vector imageBarriers; - std::vector bufferBarriers; - // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {target->swapGiTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->giMomentsTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->historyGiTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, {target->historyGiMomentsTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(target->swapGiTexture.image, target->historyGiTexture.image); commandList->CopyImage(target->giMomentsTexture.image, target->historyGiMomentsTexture.image); // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {target->swapGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->giMomentsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyGiMomentsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); Graphics::Profiler::EndAndBeginQuery("Spatial filter"); @@ -279,22 +282,23 @@ namespace Atlas { commandList->PushConstants("constants", &constants); if (pingpong) { - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->swapGiTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; commandList->BindImage(target->swapGiTexture.image, 3, 0); commandList->BindImage(target->giTexture.image, target->giTexture.sampler, 3, 1); + commandList->PipelineBarrier(imageBarriers, {}); } else { - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->swapGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->giTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; commandList->BindImage(target->giTexture.image, 3, 0); commandList->BindImage(target->swapGiTexture.image, target->swapGiTexture.sampler, 3, 1); + commandList->PipelineBarrier(imageBarriers, {}); } - commandList->PipelineBarrier(imageBarriers, bufferBarriers); pingpong = !pingpong; diff --git a/src/engine/renderer/RTGIRenderer.h b/src/engine/renderer/RTGIRenderer.h index 8fc4a7a4b..0e06db310 100644 --- a/src/engine/renderer/RTGIRenderer.h +++ b/src/engine/renderer/RTGIRenderer.h @@ -23,9 +23,13 @@ namespace Atlas { float radianceLimit; uint32_t frameSeed; float bias; + int32_t sampleCount; + int32_t lightSampleCount; int32_t textureLevel; float roughnessCutoff; int32_t halfRes; + int32_t padding0; + int32_t padding1; ivec2 resolution; Shadow shadow; }; diff --git a/src/engine/renderer/RTReflectionRenderer.cpp b/src/engine/renderer/RTReflectionRenderer.cpp index b7380d876..258cc98f1 100644 --- a/src/engine/renderer/RTReflectionRenderer.cpp +++ b/src/engine/renderer/RTReflectionRenderer.cpp @@ -20,6 +20,7 @@ namespace Atlas { VK_FORMAT_R8G8B8A8_UNORM); sobolSequenceTexture.SetData(noiseImage->GetData()); + ssrPipelineConfig = PipelineConfig("reflection/ssr.csh"); rtrPipelineConfig = PipelineConfig("reflection/rtreflection.csh"); upsamplePipelineConfig = PipelineConfig("reflection/upsample.csh"); temporalPipelineConfig = PipelineConfig("reflection/temporal.csh"); @@ -42,7 +43,7 @@ namespace Atlas { void RTReflectionRenderer::Render(Ref target, Ref scene, Graphics::CommandList* commandList) { auto reflection = scene->reflection; - if (!reflection || !reflection->enable || !scene->IsRtDataValid()) return; + if (!reflection || !reflection->enable) return; if (reflection->halfResolution && !reflection->upsampleBeforeFiltering && target->GetReflectionResolution() == FULL_RES) target->SetReflectionResolution(HALF_RES); @@ -57,18 +58,17 @@ namespace Atlas { Graphics::Profiler::BeginQuery("Render RT Reflections"); if (target->historyReflectionTexture.image->layout == VK_IMAGE_LAYOUT_UNDEFINED || - target->historyReflectionMomentsTexture.image->layout == VK_IMAGE_LAYOUT_UNDEFINED) { + target->historyReflectionMomentsTexture.image->layout == VK_IMAGE_LAYOUT_UNDEFINED || + target->lightingTexture.image->layers == VK_IMAGE_LAYOUT_UNDEFINED) { VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - std::vector bufferBarriers; - std::vector imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->historyReflectionTexture.image, layout, access}, {target->historyReflectionMomentsTexture.image, layout, access}, + {target->lightingTexture.image, layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); - } - - Graphics::Profiler::BeginQuery("Trace rays"); + commandList->PipelineBarrier(imageBarriers, {}); + } // Try to get a shadow map Ref shadow = nullptr; @@ -86,6 +86,7 @@ namespace Atlas { auto offsetTexture = downsampledRT->offsetTexture; auto velocityTexture = downsampledRT->velocityTexture; auto materialIdxTexture = downsampledRT->materialIdxTexture; + auto lightingTexture = &target->lightingTexture; // Bind the geometry normal texure and depth texture commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 1); @@ -97,88 +98,131 @@ namespace Atlas { commandList->BindImage(scramblingRankingTexture.image, scramblingRankingTexture.sampler, 3, 7); commandList->BindImage(sobolSequenceTexture.image, sobolSequenceTexture.sampler, 3, 8); + commandList->BindImage(lightingTexture->image, lightingTexture->sampler, 3, 9); + Texture::Texture2D* reflectionTexture = reflection->upsampleBeforeFiltering ? &target->swapReflectionTexture : &target->reflectionTexture; Texture::Texture2D* swapReflectionTexture = reflection->upsampleBeforeFiltering ? &target->reflectionTexture : &target->swapReflectionTexture; - // Cast rays and calculate radiance - { - static uint32_t frameCount = 0; + static uint32_t frameCount = 0; + + RTRUniforms uniforms; + uniforms.radianceLimit = reflection->radianceLimit; + uniforms.bias = reflection->bias; + uniforms.roughnessCutoff = reflection->roughnessCutoff; + uniforms.frameSeed = frameCount++; + uniforms.sampleCount = reflection->sampleCount; + uniforms.lightSampleCount = reflection->lightSampleCount; + uniforms.textureLevel = reflection->textureLevel; + uniforms.halfRes = target->GetReflectionResolution() == HALF_RES ? 1 : 0; + uniforms.resolution = rayRes; + + if (shadow && reflection->useShadowMap) { + auto& shadowUniform = uniforms.shadow; + shadowUniform.distance = !shadow->longRange ? shadow->distance : shadow->longRangeDistance; + shadowUniform.bias = shadow->bias; + shadowUniform.edgeSoftness = shadow->edgeSoftness; + shadowUniform.cascadeBlendDistance = shadow->cascadeBlendDistance; + shadowUniform.cascadeCount = shadow->viewCount; + shadowUniform.resolution = vec2(shadow->resolution); + + commandList->BindImage(shadow->maps->image, shadowSampler, 3, 6); + + auto componentCount = shadow->viewCount; + for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { + if (i < componentCount) { + auto cascade = &shadow->views[i]; + auto frustum = Volume::Frustum(cascade->frustumMatrix); + auto corners = frustum.GetCorners(); + auto texelSize = glm::max(abs(corners[0].x - corners[1].x), + abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; + shadowUniform.cascades[i].distance = cascade->farDistance; + shadowUniform.cascades[i].cascadeSpace = glm::transpose(cascade->projectionMatrix * + cascade->viewMatrix); + shadowUniform.cascades[i].texelSize = texelSize; + } + else { + auto cascade = &shadow->views[componentCount - 1]; + shadowUniform.cascades[i].distance = cascade->farDistance; + } + } + } + rtrUniformBuffer.SetData(&uniforms, 0); + + // Screen space reflections + if (reflection->ssr) { + Graphics::Profiler::BeginQuery("SSR"); + + ivec2 groupCount = ivec2(rayRes.x / 8, rayRes.y / 8); + groupCount.x += ((groupCount.x * 8 == rayRes.x) ? 0 : 1); + groupCount.y += ((groupCount.y * 8 == rayRes.y) ? 0 : 1); + + auto ddgiEnabled = scene->irradianceVolume && scene->irradianceVolume->enable; + auto ddgiVisibility = ddgiEnabled && scene->irradianceVolume->visibility; + + ssrPipelineConfig.ManageMacro("USE_SHADOW_MAP", reflection->useShadowMap && shadow); + ssrPipelineConfig.ManageMacro("DDGI", reflection->ddgi && ddgiEnabled); + ssrPipelineConfig.ManageMacro("DDGI_VISIBILITY", reflection->ddgi && ddgiVisibility); + ssrPipelineConfig.ManageMacro("OPACITY_CHECK", reflection->opacityCheck); + + auto pipeline = PipelineManager::GetPipeline(ssrPipelineConfig); + commandList->BindPipeline(pipeline); + + commandList->ImageMemoryBarrier(reflectionTexture->image, + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); + + commandList->BindImage(reflectionTexture->image, 3, 0); + + commandList->BindBuffer(rtrUniformBuffer.Get(), 3, 10); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); + + Graphics::Profiler::EndQuery(); + } + Graphics::Profiler::BeginQuery("Trace rays"); + + // Cast rays and calculate radiance + if (scene->IsRtDataValid() && reflection->rt) { ivec2 groupCount = ivec2(rayRes.x / 8, rayRes.y / 4); groupCount.x += ((groupCount.x * 8 == rayRes.x) ? 0 : 1); groupCount.y += ((groupCount.y * 4 == rayRes.y) ? 0 : 1); + auto clouds = scene->sky.clouds; + auto ddgiEnabled = scene->irradianceVolume && scene->irradianceVolume->enable; auto ddgiVisibility = ddgiEnabled && scene->irradianceVolume->visibility; + auto cloudShadowEnabled = clouds && clouds->enable && clouds->castShadow; rtrPipelineConfig.ManageMacro("USE_SHADOW_MAP", reflection->useShadowMap && shadow); + rtrPipelineConfig.ManageMacro("SSR", reflection->ssr); rtrPipelineConfig.ManageMacro("DDGI", reflection->ddgi && ddgiEnabled); rtrPipelineConfig.ManageMacro("DDGI_VISIBILITY", reflection->ddgi && ddgiVisibility); - rtrPipelineConfig.ManageMacro("OPACITY_CHECK", reflection->opacityCheck); + rtrPipelineConfig.ManageMacro("OPACITY_CHECK", reflection->opacityCheck); + rtrPipelineConfig.ManageMacro("CLOUD_SHADOWS", cloudShadowEnabled && scene->HasMainLight()); auto pipeline = PipelineManager::GetPipeline(rtrPipelineConfig); commandList->ImageMemoryBarrier(reflectionTexture->image, - VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT); + VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); helper.DispatchAndHit(scene, commandList, pipeline, ivec3(groupCount, 1), [=]() { commandList->BindImage(reflectionTexture->image, 3, 0); - RTRUniforms uniforms; - uniforms.radianceLimit = reflection->radianceLimit; - uniforms.bias = reflection->bias; - uniforms.roughnessCutoff = reflection->roughnessCutoff; - uniforms.frameSeed = frameCount++; - uniforms.textureLevel = reflection->textureLevel; - uniforms.halfRes = target->GetReflectionResolution() == HALF_RES ? 1 : 0; - uniforms.resolution = rayRes; - - if (shadow && reflection->useShadowMap) { - auto& shadowUniform = uniforms.shadow; - shadowUniform.distance = !shadow->longRange ? shadow->distance : shadow->longRangeDistance; - shadowUniform.bias = shadow->bias; - shadowUniform.edgeSoftness = shadow->edgeSoftness; - shadowUniform.cascadeBlendDistance = shadow->cascadeBlendDistance; - shadowUniform.cascadeCount = shadow->viewCount; - shadowUniform.resolution = vec2(shadow->resolution); - - commandList->BindImage(shadow->maps.image, shadowSampler, 3, 6); - - auto componentCount = shadow->viewCount; - for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { - if (i < componentCount) { - auto cascade = &shadow->views[i]; - auto frustum = Volume::Frustum(cascade->frustumMatrix); - auto corners = frustum.GetCorners(); - auto texelSize = glm::max(abs(corners[0].x - corners[1].x), - abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; - shadowUniform.cascades[i].distance = cascade->farDistance; - shadowUniform.cascades[i].cascadeSpace = cascade->projectionMatrix * - cascade->viewMatrix; - shadowUniform.cascades[i].texelSize = texelSize; - } - else { - auto cascade = &shadow->views[componentCount - 1]; - shadowUniform.cascades[i].distance = cascade->farDistance; - } - } + if (cloudShadowEnabled && scene->HasMainLight()) { + clouds->shadowTexture.Bind(commandList, 3, 9); } - rtrUniformBuffer.SetData(&uniforms, 0); - commandList->BindBuffer(rtrUniformBuffer.Get(), 3, 9); + commandList->BindBuffer(rtrUniformBuffer.Get(), 3, 10); }); commandList->ImageMemoryBarrier(reflectionTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); } - if (reflection->upsampleBeforeFiltering) { + if (reflection->upsampleBeforeFiltering && reflection->halfResolution) { Graphics::Profiler::EndAndBeginQuery("Upscaling"); - std::vector imageBarriers; - std::vector bufferBarriers; - ivec2 groupCount = ivec2(res.x / 8, res.y / 8); groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); groupCount.y += ((groupCount.y * 8 == res.y) ? 0 : 1); @@ -186,11 +230,11 @@ namespace Atlas { auto pipeline = PipelineManager::GetPipeline(upsamplePipelineConfig); commandList->BindPipeline(pipeline); - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {reflectionTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {swapReflectionTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(swapReflectionTexture->image, 3, 0); @@ -224,13 +268,12 @@ namespace Atlas { Graphics::Profiler::EndAndBeginQuery("Temporal filter"); { - std::vector imageBarriers; - std::vector bufferBarriers; - ivec2 groupCount = ivec2(res.x / 16, res.y / 16); groupCount.x += ((groupCount.x * 16 == res.x) ? 0 : 1); groupCount.y += ((groupCount.y * 16 == res.y) ? 0 : 1); + temporalPipelineConfig.ManageMacro("UPSCALE", reflection->upsampleBeforeFiltering && reflection->halfResolution); + auto pipeline = PipelineManager::GetPipeline(temporalPipelineConfig); commandList->BindPipeline(pipeline); @@ -243,12 +286,12 @@ namespace Atlas { commandList->PushConstants("constants", &constants); - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {reflectionTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {swapReflectionTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {target->reflectionMomentsTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(swapReflectionTexture->image, 3, 0); commandList->BindImage(target->reflectionMomentsTexture.image, 3, 1); @@ -269,30 +312,27 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); } - std::vector imageBarriers; - std::vector bufferBarriers; - // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {swapReflectionTexture->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->reflectionMomentsTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->historyReflectionTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, {target->historyReflectionMomentsTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(swapReflectionTexture->image, target->historyReflectionTexture.image); commandList->CopyImage(target->reflectionMomentsTexture.image, target->historyReflectionMomentsTexture.image); // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {swapReflectionTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->reflectionMomentsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyReflectionTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyReflectionMomentsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); Graphics::Profiler::EndAndBeginQuery("Spatial filter"); @@ -322,22 +362,23 @@ namespace Atlas { commandList->PushConstants("constants", &constants); if (pingpong) { - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {reflectionTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {swapReflectionTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; commandList->BindImage(swapReflectionTexture->image, 3, 0); commandList->BindImage(reflectionTexture->image, reflectionTexture->sampler, 3, 1); + commandList->PipelineBarrier(imageBarriers, {}); } else { - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {swapReflectionTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {reflectionTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; commandList->BindImage(reflectionTexture->image, 3, 0); commandList->BindImage(swapReflectionTexture->image, swapReflectionTexture->sampler, 3, 1); + commandList->PipelineBarrier(imageBarriers, {}); } - commandList->PipelineBarrier(imageBarriers, bufferBarriers); pingpong = !pingpong; diff --git a/src/engine/renderer/RTReflectionRenderer.h b/src/engine/renderer/RTReflectionRenderer.h index 38ed51e00..8b990f6fd 100644 --- a/src/engine/renderer/RTReflectionRenderer.h +++ b/src/engine/renderer/RTReflectionRenderer.h @@ -23,10 +23,14 @@ namespace Atlas { float radianceLimit; uint32_t frameSeed; float bias; + int32_t sampleCount; + int32_t lightSampleCount; int32_t textureLevel; float roughnessCutoff; int32_t halfRes; - ivec2 resolution; + int32_t padding0; + int32_t padding1; + ivec2 resolution; Shadow shadow; }; @@ -47,6 +51,7 @@ namespace Atlas { Texture::Texture2D scramblingRankingTexture; Texture::Texture2D sobolSequenceTexture; + PipelineConfig ssrPipelineConfig; PipelineConfig rtrPipelineConfig; PipelineConfig upsamplePipelineConfig; PipelineConfig temporalPipelineConfig; diff --git a/src/engine/renderer/Renderer.h b/src/engine/renderer/Renderer.h index 11f695d7e..ce8a511e8 100644 --- a/src/engine/renderer/Renderer.h +++ b/src/engine/renderer/Renderer.h @@ -2,7 +2,6 @@ #include "../System.h" #include "target/RenderTarget.h" -#include "target/PathTraceRenderTarget.h" #include "../scene/Scene.h" #include "../Viewport.h" #include "../pipeline/PipelineManager.h" diff --git a/src/engine/renderer/SSGIRenderer.cpp b/src/engine/renderer/SSGIRenderer.cpp index 8b88da14e..b0aba7a3d 100644 --- a/src/engine/renderer/SSGIRenderer.cpp +++ b/src/engine/renderer/SSGIRenderer.cpp @@ -11,9 +11,16 @@ namespace Atlas { this->device = device; - const int32_t filterSize = 3; blurFilter.CalculateBoxFilter(filterSize); + std::vector blurKernelWeights; + std::vector blurKernelOffsets; + blurFilter.GetLinearized(&blurKernelWeights, &blurKernelOffsets, false); + + auto mean = (blurKernelWeights.size() - 1) / 2; + blurKernelWeights = std::vector(blurKernelWeights.begin() + mean, blurKernelWeights.end()); + blurKernelOffsets = std::vector(blurKernelOffsets.begin() + mean, blurKernelOffsets.end()); + auto noiseImage = Loader::ImageLoader::LoadImage("scrambling_ranking.png", false, 4); scramblingRankingTexture = Texture::Texture2D(noiseImage->width, noiseImage->height, VK_FORMAT_R8G8B8A8_UNORM); @@ -34,7 +41,8 @@ namespace Atlas { ssUniformBuffer = Buffer::UniformBuffer(sizeof(SSUniforms)); // If we don't set the element size to the whole thing, otherwise uniform buffer element alignment kicks in - blurWeightsUniformBuffer = Buffer::UniformBuffer(sizeof(float) * (size_t(filterSize) + 1)); + blurWeightsUniformBuffer = Buffer::Buffer(Buffer::BufferUsageBits::UniformBufferBit, + sizeof(float) * (size_t(filterSize) + 1), 1, blurKernelWeights.data()); } @@ -59,12 +67,11 @@ namespace Atlas { target->historyGiLengthTexture.image->layout == VK_IMAGE_LAYOUT_UNDEFINED) { VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAccessFlags access = VK_ACCESS_SHADER_READ_BIT; - std::vector bufferBarriers; - std::vector imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->historyGiTexture.image, layout, access}, {target->historyGiLengthTexture.image, layout, access}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); } // Try to get a shadow map @@ -136,13 +143,9 @@ namespace Atlas { VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT); } - { Graphics::Profiler::EndAndBeginQuery("Temporal filter"); - std::vector imageBarriers; - std::vector bufferBarriers; - ivec2 groupCount = ivec2(res.x / 8, res.y / 8); groupCount.x += ((groupCount.x * 8 == res.x) ? 0 : 1); groupCount.y += ((groupCount.y * 8 == res.y) ? 0 : 1); @@ -150,13 +153,13 @@ namespace Atlas { auto pipeline = PipelineManager::GetPipeline(temporalPipelineConfig); commandList->BindPipeline(pipeline); - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->giTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {target->giLengthTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {target->historyGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyGiLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT} }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(target->giTexture.image, 3, 0); commandList->BindImage(target->giLengthTexture.image, 3, 1); @@ -180,26 +183,26 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {target->giTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->giLengthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->historyGiTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, {target->historyGiLengthTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT} }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(target->giTexture.image, target->historyGiTexture.image); commandList->CopyImage(target->giLengthTexture.image, target->historyGiLengthTexture.image); // Need barriers for all four images - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->giLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyGiLengthTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } @@ -209,37 +212,24 @@ namespace Atlas { const int32_t groupSize = 256; - std::vector kernelWeights; - std::vector kernelOffsets; - - blurFilter.GetLinearized(&kernelWeights, &kernelOffsets, false); - - auto mean = (kernelWeights.size() - 1) / 2; - kernelWeights = std::vector(kernelWeights.begin() + mean, kernelWeights.end()); - kernelOffsets = std::vector(kernelOffsets.begin() + mean, kernelOffsets.end()); - - auto kernelSize = int32_t(kernelWeights.size() - 1); + auto kernelSize = int32_t(filterSize); auto horizontalBlurPipeline = PipelineManager::GetPipeline(horizontalBlurPipelineConfig); auto verticalBlurPipeline = PipelineManager::GetPipeline(verticalBlurPipelineConfig); - blurWeightsUniformBuffer.SetData(kernelWeights.data(), 0); - commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 2); commandList->BindImage(normalTexture->image, normalTexture->sampler, 3, 3); commandList->BindBuffer(blurWeightsUniformBuffer.Get(), 3, 4); - std::vector bufferBarriers; - for (int32_t i = 0; i < 3; i++) { ivec2 groupCount = ivec2(res.x / groupSize, res.y); groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); - std::vector imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {target->giTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->swapGiTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(preImageBarriers, {}); commandList->BindPipeline(horizontalBlurPipeline); commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); @@ -252,11 +242,11 @@ namespace Atlas { groupCount = ivec2(res.x, res.y / groupSize); groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {target->swapGiTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->giTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(postImageBarriers, {}); commandList->BindPipeline(verticalBlurPipeline); commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); diff --git a/src/engine/renderer/SSGIRenderer.h b/src/engine/renderer/SSGIRenderer.h index 335f08ebe..1043197ef 100644 --- a/src/engine/renderer/SSGIRenderer.h +++ b/src/engine/renderer/SSGIRenderer.h @@ -40,7 +40,9 @@ namespace Atlas { PipelineConfig verticalBlurPipelineConfig; Buffer::UniformBuffer ssUniformBuffer; - Buffer::UniformBuffer blurWeightsUniformBuffer; + Buffer::Buffer blurWeightsUniformBuffer; + + const int32_t filterSize = 3; }; diff --git a/src/engine/renderer/SSSRenderer.cpp b/src/engine/renderer/SSSRenderer.cpp index 8c81987cc..3b5d650b7 100644 --- a/src/engine/renderer/SSSRenderer.cpp +++ b/src/engine/renderer/SSSRenderer.cpp @@ -40,6 +40,8 @@ namespace Atlas { auto lightDirection = glm::normalize(vec3(camera.viewMatrix * vec4( light.transformedProperties.directional.direction, 0.0f))); + pipelineConfig.ManageMacro("TRACE_WORLD_SPACE", sss->traceWorldSpace); + auto pipeline = PipelineManager::GetPipeline(pipelineConfig); commandList->BindPipeline(pipeline); @@ -54,6 +56,7 @@ namespace Atlas { .lightDirection = vec4(lightDirection, 0.0), .sampleCount = sss->sampleCount, .maxLength = sss->maxLength, + .minLengthWorldSpace = sss->minLengthWorldSpace, .thickness = sss->thickness }; commandList->PushConstants("constants", &constants); diff --git a/src/engine/renderer/SSSRenderer.h b/src/engine/renderer/SSSRenderer.h index d16332e0f..5a2964765 100644 --- a/src/engine/renderer/SSSRenderer.h +++ b/src/engine/renderer/SSSRenderer.h @@ -20,6 +20,7 @@ namespace Atlas { vec4 lightDirection; int sampleCount; float maxLength; + float minLengthWorldSpace; float thickness; }; diff --git a/src/engine/renderer/ShadowRenderer.cpp b/src/engine/renderer/ShadowRenderer.cpp index 92c526a49..51de7b391 100644 --- a/src/engine/renderer/ShadowRenderer.cpp +++ b/src/engine/renderer/ShadowRenderer.cpp @@ -18,6 +18,7 @@ namespace Atlas { Graphics::Profiler::BeginQuery("Shadows"); + std::swap(prevLightMap, lightMap); lightMap.clear(); Ref shadowPass = renderList->PopPassFromQueue(RenderList::RenderPassType::Shadow); @@ -29,7 +30,7 @@ namespace Atlas { continue; } - ProcessPass(target, scene, commandList, shadowPass); + ProcessPass(target, scene, commandList, renderList, shadowPass); shadowPass = renderList->PopPassFromQueue(RenderList::RenderPassType::Shadow); } @@ -38,7 +39,7 @@ namespace Atlas { // another push to the queue has been happening. So check again here shadowPass = renderList->PopPassFromQueue(RenderList::RenderPassType::Shadow); if (shadowPass != nullptr) { - ProcessPass(target, scene, commandList, shadowPass); + ProcessPass(target, scene, commandList, renderList, shadowPass); } // Need to also keep track of non processed layer (e.g. long range layers) @@ -58,18 +59,18 @@ namespace Atlas { } - void ShadowRenderer::ProcessPass(Ref target, Ref scene, Graphics::CommandList* commandList, Ref shadowPass) { + void ShadowRenderer::ProcessPass(Ref target, Ref scene, Graphics::CommandList* commandList, + RenderList* renderList, Ref shadowPass) { + + Graphics::Profiler::BeginQuery("Entity pass " + std::to_string(shadowPass->lightEntity) + " layer " + std::to_string(shadowPass->layer)); auto lightEntity = shadowPass->lightEntity; auto& light = lightEntity.GetComponent(); if (!light.shadow || !light.shadow->update) return; - Ref frameBuffer = nullptr; - if (lightMap.contains(lightEntity)) - frameBuffer = lightMap[lightEntity]; - else - frameBuffer = GetOrCreateFrameBuffer(lightEntity); + auto sceneState = &scene->renderState; + auto frameBuffer = GetOrCreateFrameBuffer(lightEntity); lightMap[lightEntity] = frameBuffer; @@ -107,7 +108,7 @@ namespace Atlas { for (auto& [meshId, instances] : shadowPass->meshToInstancesMap) { if (!instances.count) continue; - auto& mesh = shadowPass->meshIdToMeshMap[meshId]; + auto& mesh = renderList->meshIdToMeshMap[meshId]; for (auto& subData : mesh->data.subData) { if (!subData.material.IsLoaded()) continue; @@ -171,7 +172,7 @@ namespace Atlas { .windTextureLod = mesh->windNoiseTextureLod, .windBendScale = mesh->windBendScale, .windWiggleScale = mesh->windWiggleScale, - .textureID = material->HasOpacityMap() ? scene->textureToBindlessIdx[material->opacityMap.Get()] : 0 + .textureID = material->HasOpacityMap() ? sceneState->textureToBindlessIdx[material->opacityMap.Get()] : 0 }; commandList->PushConstants("constants", &pushConstants); @@ -183,11 +184,13 @@ namespace Atlas { } - impostorRenderer.Render(frameBuffer, commandList, + impostorRenderer.Render(frameBuffer, commandList, renderList, shadowPass.get(), component->viewMatrix, component->projectionMatrix, lightLocation); commandList->EndRenderPass(); + Graphics::Profiler::EndQuery(); + } Ref ShadowRenderer::GetOrCreateFrameBuffer(Scene::Entity entity) { @@ -195,19 +198,22 @@ namespace Atlas { auto& light = entity.GetComponent(); auto& shadow = light.shadow; - /* - if (lightMap.contains(entity)) { - auto frameBuffer = lightMap[entity]; - if (frameBuffer->extent.width == shadow->resolution || - frameBuffer->extent.height == shadow->resolution) { - return frameBuffer; + if (prevLightMap.contains(entity)) { + auto frameBuffer = prevLightMap[entity]; + // The image for a light entity might change + if (light.shadow->useCubemap && frameBuffer->GetDepthImage() == light.shadow->cubemap->image || + !light.shadow->useCubemap && frameBuffer->GetDepthImage() == light.shadow->maps->image) { + // Also check if the resolution stayed the same + if (frameBuffer->extent.width == shadow->resolution || + frameBuffer->extent.height == shadow->resolution) { + return frameBuffer; + } } } - */ Graphics::RenderPassDepthAttachment attachment = { - .imageFormat = shadow->useCubemap ? shadow->cubemap.format : - shadow->maps.format, + .imageFormat = shadow->useCubemap ? shadow->cubemap->format : + shadow->maps->format, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL @@ -219,9 +225,10 @@ namespace Atlas { Graphics::FrameBufferDesc frameBufferDesc = { .renderPass = renderPass, - .depthAttachment = { shadow->useCubemap ? shadow->cubemap.image : shadow->maps.image, 0, true}, + .depthAttachment = { shadow->useCubemap ? shadow->cubemap->image : shadow->maps->image, 0, true}, .extent = { uint32_t(shadow->resolution), uint32_t(shadow->resolution) } }; + return device->CreateFrameBuffer(frameBufferDesc); } diff --git a/src/engine/renderer/ShadowRenderer.h b/src/engine/renderer/ShadowRenderer.h index b74fff1d0..53f23aa81 100644 --- a/src/engine/renderer/ShadowRenderer.h +++ b/src/engine/renderer/ShadowRenderer.h @@ -23,7 +23,8 @@ namespace Atlas { void Render(Ref target, Ref scene, Graphics::CommandList* commandList, RenderList* renderList); private: - void ProcessPass(Ref target, Ref scene, Graphics::CommandList* commandList, Ref shadowPass); + void ProcessPass(Ref target, Ref scene, Graphics::CommandList* commandList, + RenderList* renderList, Ref shadowPass); Ref GetOrCreateFrameBuffer(Scene::Entity entity); @@ -42,12 +43,14 @@ namespace Atlas { uint32_t textureID; }; + LightMap prevLightMap; LightMap lightMap; ImpostorShadowRenderer impostorRenderer; std::vector> subDatas; + }; } diff --git a/src/engine/renderer/SkyboxRenderer.cpp b/src/engine/renderer/SkyboxRenderer.cpp index 4931f5163..82d8598c4 100644 --- a/src/engine/renderer/SkyboxRenderer.cpp +++ b/src/engine/renderer/SkyboxRenderer.cpp @@ -25,12 +25,11 @@ namespace Atlas { auto velocityTexture = rtData->velocityTexture; auto depthTexture = rtData->depthTexture; - std::vector bufferBarriers; - std::vector imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, {velocityTexture->image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(preImageBarriers, {}); vec4 lastCameraLocation = vec4(camera.GetLastLocation(), 1.0f); commandList->PushConstants("constants", &lastCameraLocation); @@ -50,11 +49,11 @@ namespace Atlas { commandList->Dispatch(groupCount.x, groupCount.y, 1); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {target->lightingTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {velocityTexture->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); Graphics::Profiler::EndQuery(); diff --git a/src/engine/renderer/TemporalAARenderer.cpp b/src/engine/renderer/TemporalAARenderer.cpp index cdcf0a5ce..d0211ade6 100644 --- a/src/engine/renderer/TemporalAARenderer.cpp +++ b/src/engine/renderer/TemporalAARenderer.cpp @@ -43,12 +43,12 @@ namespace Atlas { const auto depth = targetData->depthTexture; const auto stencil = targetData->stencilTexture; - std::vector bufferBarriers; - std::vector imageBarriers; - imageBarriers.push_back({lastHistory->image,VK_IMAGE_LAYOUT_GENERAL,VK_ACCESS_SHADER_WRITE_BIT}); - imageBarriers.push_back({history->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT}); - imageBarriers.push_back({lastVelocity->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT}); - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + Graphics::ImageBarrier imageBarriers[] = { + {lastHistory->image,VK_IMAGE_LAYOUT_GENERAL,VK_ACCESS_SHADER_WRITE_BIT}, + {history->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT}, + {lastVelocity->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT} + }; + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(lastHistory->image, 3, 0); @@ -77,71 +77,6 @@ namespace Atlas { } - void TemporalAARenderer::Render(Ref target, Ref scene, Graphics::CommandList* commandList) { - - pipelineConfig.ManageMacro("PATHTRACE", true); - - const auto output = &target->postProcessTexture; - const auto history = &target->historyPostProcessTexture; - const auto lastVelocity = &target->historyVelocityTexture; - auto const velocity = &target->velocityTexture; - auto const depth = &target->depthTexture; - - std::vector bufferBarriers; - std::vector imageBarriers; - imageBarriers.push_back({ output->image,VK_IMAGE_LAYOUT_GENERAL,VK_ACCESS_SHADER_WRITE_BIT }); - imageBarriers.push_back({ history->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT }); - imageBarriers.push_back({ lastVelocity->image,VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,VK_ACCESS_SHADER_READ_BIT }); - commandList->PipelineBarrier(imageBarriers, bufferBarriers); - - PushConstants constants = { - .resetHistory = false - }; - Render(output, &target->radianceTexture, history, velocity, lastVelocity, depth, - nullptr, constants, commandList); - - commandList->ImageMemoryBarrier(output->image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - - } - - void TemporalAARenderer::Render(const Texture::Texture2D* outputTexture, const Texture::Texture2D* currentTexture, const Texture::Texture2D* historyTexture, - const Texture::Texture2D* velocityTexture, const Texture::Texture2D* historyVelocityTexture, const Texture::Texture2D* depthTexture, - const Texture::Texture2D* stencilTexture, PushConstants& constants, Graphics::CommandList* commandList) { - - Graphics::Profiler::BeginQuery("TAA"); - - auto pipeline = PipelineManager::GetPipeline(pipelineConfig); - commandList->BindPipeline(pipeline); - - auto res = ivec2(outputTexture->width, outputTexture->height); - - const int32_t groupSize = 8; - ivec2 groupCount = res / groupSize; - groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); - groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); - - commandList->BindImage(outputTexture->image, 3, 0); - commandList->BindImage(historyTexture->image, historyTexture->sampler, 3, 1); - commandList->BindImage(currentTexture->image, currentTexture->sampler, 3, 2); - commandList->BindImage(velocityTexture->image, velocityTexture->sampler, 3, 3); - commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 4); - commandList->BindImage(historyVelocityTexture->image, historyVelocityTexture->sampler, 3, 5); - - if (stencilTexture != nullptr) - commandList->BindImage(stencilTexture->image, stencilTexture->sampler, 3, 6); - - constants.resolution = vec2((float)outputTexture->width, (float)outputTexture->height); - constants.invResolution = 1.0f / vec2((float)outputTexture->width, (float)outputTexture->height); - - commandList->PushConstants("constants", &constants); - - commandList->Dispatch(groupCount.x, groupCount.y, 1); - - Graphics::Profiler::EndQuery(); - - } - } } \ No newline at end of file diff --git a/src/engine/renderer/TemporalAARenderer.h b/src/engine/renderer/TemporalAARenderer.h index ad1d56a1a..cd09d64b6 100644 --- a/src/engine/renderer/TemporalAARenderer.h +++ b/src/engine/renderer/TemporalAARenderer.h @@ -2,7 +2,6 @@ #include "../System.h" #include "Renderer.h" -#include "PathTracingRenderer.h" namespace Atlas { @@ -17,8 +16,6 @@ namespace Atlas { void Render(Ref target, Ref scene, Graphics::CommandList* commandList); - void Render(Ref target, Ref scene, Graphics::CommandList* commandList); - private: struct alignas(16) PushConstants { vec2 resolution; @@ -27,10 +24,6 @@ namespace Atlas { int32_t resetHistory; }; - void Render(const Texture::Texture2D* outputTexture, const Texture::Texture2D* currentTexture, const Texture::Texture2D* historyTexture, - const Texture::Texture2D* velocityTexture, const Texture::Texture2D* historyVelocityTexture, const Texture::Texture2D* depthTexture, - const Texture::Texture2D* stencilTexture, PushConstants& constants, Graphics::CommandList* commandList); - PipelineConfig pipelineConfig; }; diff --git a/src/engine/renderer/TerrainRenderer.cpp b/src/engine/renderer/TerrainRenderer.cpp index 5a44b0d64..87c631508 100644 --- a/src/engine/renderer/TerrainRenderer.cpp +++ b/src/engine/renderer/TerrainRenderer.cpp @@ -15,7 +15,7 @@ namespace Atlas { void TerrainRenderer::Render(Ref target, Ref scene, Graphics::CommandList* commandList, std::unordered_map materialMap) { - if (!scene->terrain) + if (!scene->terrain.IsLoaded()) return; Graphics::Profiler::BeginQuery("Terrain"); @@ -105,17 +105,17 @@ namespace Atlas { PipelineConfig config; switch (i) { - case 0: config = GeneratePipelineConfig(target, terrain, true, true); + case 0: config = GeneratePipelineConfig(target, terrain.Get(), true, true); terrain->vertexArray.Bind(commandList); Graphics::Profiler::BeginQuery("Detail with displacement"); nodes = detailDisplacementNodes; break; - case 1: config = GeneratePipelineConfig(target, terrain, false, true); + case 1: config = GeneratePipelineConfig(target, terrain.Get(), false, true); terrain->distanceVertexArray.Bind(commandList); Graphics::Profiler::BeginQuery("Detail"); nodes = detailNodes; break; - case 2: config = GeneratePipelineConfig(target, terrain, false, false); + case 2: config = GeneratePipelineConfig(target, terrain.Get(), false, false); terrain->distanceVertexArray.Bind(commandList); Graphics::Profiler::BeginQuery("Distance"); nodes = distanceNodes; @@ -128,9 +128,9 @@ namespace Atlas { for (auto node : nodes) { - node->cell->heightField.Bind(commandList, 3, 0); - node->cell->normalMap.Bind(commandList, 3, 1); - node->cell->splatMap.Bind(commandList, 3, 2); + node->cell->heightField->Bind(commandList, 3, 0); + node->cell->normalMap->Bind(commandList, 3, 1); + node->cell->splatMap->Bind(commandList, 3, 2); auto tileScale = terrain->resolution * powf(2.0f, (float)(terrain->LoDCount - node->cell->LoD) - 1.0f); @@ -139,7 +139,7 @@ namespace Atlas { .nodeSideLength = node->sideLength, .tileScale = tileScale, .patchSize = float(terrain->patchSizeFactor), - .normalTexelSize = 1.0f / float(node->cell->normalMap.width), + .normalTexelSize = 1.0f / float(node->cell->normalMap->width), .leftLoD = node->leftLoDStitch, .topLoD = node->topLoDStitch, diff --git a/src/engine/renderer/TerrainShadowRenderer.cpp b/src/engine/renderer/TerrainShadowRenderer.cpp index 4122fb1f8..c964c65e1 100644 --- a/src/engine/renderer/TerrainShadowRenderer.cpp +++ b/src/engine/renderer/TerrainShadowRenderer.cpp @@ -13,7 +13,7 @@ namespace Atlas { void TerrainShadowRenderer::Render(Ref target, Ref scene, Graphics::CommandList* commandList) { - if (!scene->terrain) + if (!scene->terrain.IsLoaded()) return; Graphics::Profiler::BeginQuery("Terrain shadows"); @@ -54,7 +54,7 @@ namespace Atlas { commandList->BeginRenderPass(frameBuffer->renderPass, frameBuffer); - auto config = GeneratePipelineConfig(frameBuffer, terrain); + auto config = GeneratePipelineConfig(frameBuffer, terrain.Get()); auto pipeline = PipelineManager::GetPipeline(config); commandList->BindPipeline(pipeline); @@ -72,7 +72,7 @@ namespace Atlas { for (auto node : terrain->renderList) { - node->cell->heightField.Bind(commandList, 3, 0); + node->cell->heightField->Bind(commandList, 3, 0); auto tileScale = terrain->resolution * powf(2.0f, (float)(terrain->LoDCount - node->cell->LoD) - 1.0f); @@ -147,8 +147,8 @@ namespace Atlas { */ Graphics::RenderPassDepthAttachment attachment = { - .imageFormat = shadow->useCubemap ? shadow->cubemap.format : - shadow->maps.format, + .imageFormat = shadow->useCubemap ? shadow->cubemap->format : + shadow->maps->format, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL @@ -160,7 +160,7 @@ namespace Atlas { Graphics::FrameBufferDesc frameBufferDesc = { .renderPass = renderPass, - .depthAttachment = { shadow->useCubemap ? shadow->cubemap.image : shadow->maps.image, 0, true}, + .depthAttachment = { shadow->useCubemap ? shadow->cubemap->image : shadow->maps->image, 0, true}, .extent = { uint32_t(shadow->resolution), uint32_t(shadow->resolution) } }; return device->CreateFrameBuffer(frameBufferDesc); diff --git a/src/engine/renderer/VolumetricCloudRenderer.cpp b/src/engine/renderer/VolumetricCloudRenderer.cpp index f07d6aac4..b64f5f3ba 100644 --- a/src/engine/renderer/VolumetricCloudRenderer.cpp +++ b/src/engine/renderer/VolumetricCloudRenderer.cpp @@ -68,9 +68,6 @@ namespace Atlas { auto velocityTexture = downsampledRT->velocityTexture; auto historyDepthTexture = downsampledHistoryRT->depthTexture; - - std::vector bufferBarriers; - std::vector imageBarriers; { Graphics::Profiler::BeginQuery("Integrate"); @@ -124,12 +121,11 @@ namespace Atlas { auto pipeline = PipelineManager::GetPipeline(temporalPipelineConfig); commandList->BindPipeline(pipeline); - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->swapVolumetricCloudsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->volumetricCloudsTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT} }; - - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); commandList->BindImage(target->volumetricCloudsTexture.image, 3, 0); commandList->BindImage(target->swapVolumetricCloudsTexture.image, target->swapVolumetricCloudsTexture.sampler, 3, 1); @@ -153,20 +149,20 @@ namespace Atlas { Graphics::Profiler::BeginQuery("Copy to history"); // Need barriers for both images - imageBarriers = { + Graphics::ImageBarrier preImageBarriers[] = { {target->volumetricCloudsTexture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT}, {target->historyVolumetricCloudsTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(preImageBarriers, {}, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); commandList->CopyImage(target->volumetricCloudsTexture.image, target->historyVolumetricCloudsTexture.image); - imageBarriers = { + Graphics::ImageBarrier postImageBarriers[] = { {target->volumetricCloudsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->historyVolumetricCloudsTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers, + commandList->PipelineBarrier(postImageBarriers, {}, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); Graphics::Profiler::EndQuery(); diff --git a/src/engine/renderer/VolumetricRenderer.cpp b/src/engine/renderer/VolumetricRenderer.cpp index 9024baa03..1496062dc 100644 --- a/src/engine/renderer/VolumetricRenderer.cpp +++ b/src/engine/renderer/VolumetricRenderer.cpp @@ -9,9 +9,26 @@ namespace Atlas { this->device = device; - const int32_t filterSize = 4; blurFilter.CalculateBoxFilter(filterSize); + std::vector blurKernelWeights; + std::vector blurKernelOffsets; + blurFilter.GetLinearized(&blurKernelWeights, &blurKernelOffsets, false); + + auto mean = (blurKernelWeights.size() - 1) / 2; + blurKernelWeights = std::vector(blurKernelWeights.begin() + mean, blurKernelWeights.end()); + blurKernelOffsets = std::vector(blurKernelOffsets.begin() + mean, blurKernelOffsets.end()); + + auto noiseImage = Loader::ImageLoader::LoadImage("scrambling_ranking.png", false, 4); + scramblingRankingTexture = Texture::Texture2D(noiseImage->width, noiseImage->height, + VK_FORMAT_R8G8B8A8_UNORM); + scramblingRankingTexture.SetData(noiseImage->GetData()); + + noiseImage = Loader::ImageLoader::LoadImage("sobol.png"); + sobolSequenceTexture = Texture::Texture2D(noiseImage->width, noiseImage->height, + VK_FORMAT_R8G8B8A8_UNORM); + sobolSequenceTexture.SetData(noiseImage->GetData()); + volumetricPipelineConfig = PipelineConfig("volumetric/volumetric.csh"); horizontalBlurPipelineConfig = PipelineConfig("bilateralBlur.csh", @@ -21,11 +38,15 @@ namespace Atlas { resolvePipelineConfig = PipelineConfig("volumetric/volumetricResolve.csh"); + lightCullingBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit, sizeof(uint32_t)); + volumetricUniformBuffer = Buffer::UniformBuffer(sizeof(VolumetricUniforms)); resolveUniformBuffer = Buffer::UniformBuffer(sizeof(ResolveUniforms)); - blurWeightsUniformBuffer = Buffer::UniformBuffer(sizeof(float) * (size_t(filterSize) + 1)); + + blurWeightsUniformBuffer = Buffer::Buffer(Buffer::BufferUsageBits::UniformBufferBit, + sizeof(float) * (size_t(filterSize) + 1), 1, blurKernelWeights.data()); - auto samplerDesc = Graphics::SamplerDesc { + auto samplerDesc = Graphics::SamplerDesc{ .filter = VK_FILTER_NEAREST, .mode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .compareEnabled = true @@ -38,6 +59,7 @@ namespace Atlas { Graphics::Profiler::BeginQuery("Render volumetric"); + auto renderState = &scene->renderState; auto& camera = scene->GetMainCamera(); auto fog = scene->fog; @@ -49,200 +71,216 @@ namespace Atlas { commandList->BindImage(target->volumetricTexture.image, 3, 0); commandList->BindImage(lowResDepthTexture->image, lowResDepthTexture->sampler, 3, 1); + renderState->volumetricLightBuffer.Bind(commandList, 3, 8); + renderState->volumetricShadowBuffer.Bind(commandList, 3, 9); ivec2 res = ivec2(target->volumetricTexture.width, target->volumetricTexture.height); - Graphics::Profiler::BeginQuery("Ray marching"); - auto mainLightEntity = GetMainLightEntity(scene); - auto lightSubset = scene->GetSubset(); - for (auto& lightEntity : lightSubset) { + VolumetricUniforms uniforms; + uniforms.sampleCount = fog ? fog->rayMarchStepCount : 1; + uniforms.intensity = fog ? fog->volumetricIntensity : 0.0f; + uniforms.lightCount = std::min(128, int32_t(renderState->volumetricLights.size())); + uniforms.directionalLightCount = 0; + + for (auto& light : renderState->volumetricLights) { + auto packedType = reinterpret_cast(light.color.a); + auto type = static_cast(packedType); + + if (type != LightType::DirectionalLight) + break; + + uniforms.directionalLightCount++; + } - auto& light = lightEntity.GetComponent(); - if (light.type != LightType::DirectionalLight || - !light.volumetric || !fog || !fog->rayMarching) - continue; + const int32_t groupSize = 16; - auto shadow = light.shadow; + res = ivec2(target->GetScaledWidth(), target->GetScaledHeight()); - const int32_t groupSize = 8; + ivec2 groupCount = res / groupSize; + groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); + groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); - res = ivec2(target->GetScaledWidth(), target->GetScaledHeight()); + if (lightCullingBuffer.GetElementCount() < groupCount.x * groupCount.y * 64) { + lightCullingBuffer.SetSize(groupCount.x * groupCount.y * 64); + } - ivec2 groupCount = res / groupSize; - groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); - groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + Graphics::Profiler::BeginQuery("Light culling"); - vec3 direction = normalize(vec3(camera.viewMatrix * - vec4(light.transformedProperties.directional.direction, 0.0f))); - - VolumetricUniforms uniforms; - uniforms.sampleCount = fog->rayMarchStepCount; - uniforms.intensity = fog->volumetricIntensity * light.intensity; - - uniforms.light.direction = vec4(direction, 0.0); - uniforms.light.color = vec4(Common::ColorConverter::ConvertSRGBToLinear(light.color), 0.0); - - if (light.shadow) { - uniforms.light.shadow.cascadeCount = shadow->viewCount; - uniforms.light.shadow.edgeSoftness = shadow->edgeSoftness; - - commandList->BindImage(shadow->maps.image, shadowSampler, 3, 2); - - auto& shadowUniform = uniforms.light.shadow; - for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { - auto& cascadeUniform = shadowUniform.cascades[i]; - auto cascadeString = "light.shadow.cascades[" + std::to_string(i) + "]"; - if (i < shadow->viewCount) { - auto cascade = &shadow->views[i]; - cascadeUniform.distance = cascade->farDistance; - cascadeUniform.cascadeSpace = cascade->projectionMatrix * - cascade->viewMatrix * camera.invViewMatrix; - } - else { - cascadeUniform.distance = camera.farPlane; - } - } - } + commandList->BufferMemoryBarrier(lightCullingBuffer.Get(), VK_ACCESS_SHADER_WRITE_BIT); - auto ocean = scene->ocean; - bool oceanEnabled = ocean && ocean->enable; + CullingPushConstants cullingPushConstants; +#ifdef AE_BINDLESS + cullingPushConstants.lightCount = std::min(4096, int32_t(renderState->volumetricLights.size())); +#else + cullingPushConstants.lightCount = std::min(8, int32_t(renderState->volumetricLights.size())); +#endif - if (oceanEnabled) { - // This is the full res depth buffer.. - uniforms.oceanHeight = ocean->translation.y; - target->oceanDepthTexture.Bind(commandList, 3, 4); - // Needs barrier - target->oceanStencilTexture.Bind(commandList, 3, 5); - } + lightCullingBuffer.Bind(commandList, 3, 10); - auto fog = scene->fog; - bool fogEnabled = fog && fog->enable; + auto cullingPipelineConfig = PipelineConfig("volumetric/lightCulling.csh"); + auto pipeline = PipelineManager::GetPipeline(cullingPipelineConfig); + commandList->BindPipeline(pipeline); - uniforms.fogEnabled = fogEnabled ? 1 : 0; + commandList->PushConstants("constants", &cullingPushConstants); - if (fogEnabled) { - auto& fogUniform = uniforms.fog; - fogUniform.extinctionCoefficient = fog->extinctionCoefficients; - fogUniform.scatteringFactor = fog->scatteringFactor; - fogUniform.extinctionFactor = fog->extinctionFactor; - fogUniform.density = fog->density; - fogUniform.heightFalloff = fog->heightFalloff; - fogUniform.height = fog->height; - fogUniform.ambientFactor = fog->ambientFactor; - fogUniform.scatteringAnisotropy = glm::clamp(fog->scatteringAnisotropy, -0.999f, 0.999f); - } + commandList->Dispatch(groupCount.x, groupCount.y, 1); - auto clouds = scene->sky.clouds; - bool cloudsEnabled = clouds && clouds->enable && mainLightEntity.IsValid(); - bool cloudShadowsEnabled = cloudsEnabled && clouds->castShadow; + commandList->BufferMemoryBarrier(lightCullingBuffer.Get(), VK_ACCESS_SHADER_READ_BIT); - if (cloudsEnabled) { - target->volumetricCloudsTexture.Bind(commandList, 3, 6); + Graphics::Profiler::EndAndBeginQuery("Ray marching"); - float cloudInnerRadius = scene->sky.planetRadius + clouds->minHeight; - uniforms.planetCenterAndRadius = vec4(scene->sky.planetCenter, cloudInnerRadius); - } + auto ocean = scene->ocean; + bool oceanEnabled = ocean && ocean->enable; - if (cloudShadowsEnabled) { - auto& cloudShadowUniform = uniforms.cloudShadow; + if (oceanEnabled) { + // This is the full res depth buffer.. + uniforms.oceanHeight = ocean->translation.y; + target->oceanDepthTexture.Bind(commandList, 3, 3); + // Needs barrier + target->oceanStencilTexture.Bind(commandList, 3, 4); + } - clouds->shadowTexture.Bind(commandList, 3, 3); + bool fogEnabled = fog && fog->enable; - clouds->GetShadowMatrices(camera, normalize(light.transformedProperties.directional.direction), - cloudShadowUniform.vMatrix, cloudShadowUniform.pMatrix); + uniforms.fogEnabled = fogEnabled ? 1 : 0; - cloudShadowUniform.vMatrix = cloudShadowUniform.vMatrix * camera.invViewMatrix; + if (fogEnabled) { + auto& fogUniform = uniforms.fog; + fogUniform.extinctionCoefficient = fog->extinctionCoefficients; + fogUniform.scatteringFactor = fog->scatteringFactor; + fogUniform.extinctionFactor = fog->extinctionFactor; + fogUniform.density = fog->density; + fogUniform.heightFalloff = fog->heightFalloff; + fogUniform.height = fog->height; + fogUniform.ambientFactor = fog->ambientFactor; + fogUniform.scatteringAnisotropy = glm::clamp(fog->scatteringAnisotropy, -0.999f, 0.999f); + } - cloudShadowUniform.ivMatrix = glm::inverse(cloudShadowUniform.vMatrix); - cloudShadowUniform.ipMatrix = glm::inverse(cloudShadowUniform.pMatrix); - } + auto clouds = scene->sky.clouds; + bool cloudsEnabled = clouds && clouds->enable && mainLightEntity.IsValid(); + bool cloudShadowsEnabled = cloudsEnabled && clouds->castShadow; - volumetricUniformBuffer.SetData(&uniforms, 0); - commandList->BindBuffer(volumetricUniformBuffer.Get(), 3, 7); + if (cloudsEnabled) { + target->volumetricCloudsTexture.Bind(commandList, 3, 5); - volumetricPipelineConfig.ManageMacro("SHADOWS", light.shadow != nullptr); - volumetricPipelineConfig.ManageMacro("CLOUDS", cloudsEnabled); - volumetricPipelineConfig.ManageMacro("CLOUD_SHADOWS", cloudShadowsEnabled); - volumetricPipelineConfig.ManageMacro("OCEAN", oceanEnabled); - auto volumetricPipeline = PipelineManager::GetPipeline(volumetricPipelineConfig); + float cloudInnerRadius = scene->sky.planetRadius + clouds->minHeight; + uniforms.planetCenterAndRadius = vec4(scene->sky.planetCenter, cloudInnerRadius); + } - commandList->BindPipeline(volumetricPipeline); + if (cloudShadowsEnabled) { + auto& mainLight = mainLightEntity.GetComponent(); + auto& cloudShadowUniform = uniforms.cloudShadow; - commandList->Dispatch(groupCount.x, groupCount.y, 1); + clouds->shadowTexture.Bind(commandList, 3, 2); + + clouds->GetShadowMatrices(camera, normalize(mainLight.transformedProperties.directional.direction), + cloudShadowUniform.vMatrix, cloudShadowUniform.pMatrix); + + cloudShadowUniform.vMatrix = cloudShadowUniform.vMatrix * camera.invViewMatrix; + + cloudShadowUniform.ivMatrix = glm::inverse(cloudShadowUniform.vMatrix); + cloudShadowUniform.ipMatrix = glm::inverse(cloudShadowUniform.pMatrix); + } + + commandList->BindSampler(shadowSampler, 3, 6); + +#ifndef AE_BINDLESS + std::vector> cascadeMaps; + std::vector> cubeMaps; + + uniforms.lightCount = std::min(8, int32_t(renderState->lightEntities.size())); + uniforms.directionalLightCount = std::min(8, uniforms.directionalLightCount); + for (int32_t i = 0; i < uniforms.lightCount; i++) { + auto& comp = renderState->lightEntities[i].comp; + + if (comp.shadow) { + auto& shadow = comp.shadow; + if (shadow->useCubemap) { + cubeMaps.push_back(shadow->cubemap->image); + } + else { + cascadeMaps.push_back(shadow->maps->image); + } + } } - Graphics::Profiler::EndQuery(); + commandList->BindSampledImages(cascadeMaps, 3, 11); + commandList->BindSampledImages(cubeMaps, 3, 19); +#endif - std::vector bufferBarriers; - std::vector imageBarriers; + volumetricUniformBuffer.SetData(&uniforms, 0); + commandList->BindBuffer(volumetricUniformBuffer.Get(), 3, 7); - if (fog && fog->enable && fog->rayMarching) { - Graphics::Profiler::BeginQuery("Bilateral blur"); + volumetricPipelineConfig.ManageMacro("CLOUDS", cloudsEnabled); + volumetricPipelineConfig.ManageMacro("CLOUD_SHADOWS", cloudShadowsEnabled); + volumetricPipelineConfig.ManageMacro("OCEAN", oceanEnabled); + volumetricPipelineConfig.ManageMacro("LOCAL_LIGHTS", fogEnabled && fog->localLights); + auto volumetricPipeline = PipelineManager::GetPipeline(volumetricPipelineConfig); - const int32_t groupSize = 256; + commandList->BindPipeline(volumetricPipeline); + + commandList->Dispatch(groupCount.x, groupCount.y, 1); - std::vector kernelWeights; - std::vector kernelOffsets; + Graphics::Profiler::EndQuery(); - blurFilter.GetLinearized(&kernelWeights, &kernelOffsets, false); + if (fog && fog->enable && fog->rayMarching) { + Graphics::Profiler::BeginQuery("Bilateral blur"); - auto mean = (kernelWeights.size() - 1) / 2; - kernelWeights = std::vector(kernelWeights.begin() + mean, kernelWeights.end()); - kernelOffsets = std::vector(kernelOffsets.begin() + mean, kernelOffsets.end()); + const int32_t groupSize = 256; - auto kernelSize = int32_t(kernelWeights.size() - 1); + auto kernelSize = int32_t(filterSize); auto horizontalBlurPipeline = PipelineManager::GetPipeline(horizontalBlurPipelineConfig); auto verticalBlurPipeline = PipelineManager::GetPipeline(verticalBlurPipelineConfig); - blurWeightsUniformBuffer.SetData(kernelWeights.data(), 0); - commandList->BindImage(lowResDepthTexture->image, lowResDepthTexture->sampler, 3, 2); commandList->BindBuffer(blurWeightsUniformBuffer.Get(), 3, 4); - ivec2 groupCount = ivec2(res.x / groupSize, res.y); - groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); - imageBarriers = { - {target->volumetricTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, - {target->swapVolumetricTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, - }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + for (int32_t j = 0; j < 3; j++) { + ivec2 groupCount = ivec2(res.x / groupSize, res.y); + groupCount.x += ((res.x % groupSize == 0) ? 0 : 1); + Graphics::ImageBarrier preImageBarriers[] = { + {target->volumetricTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->swapVolumetricTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + }; + commandList->PipelineBarrier(preImageBarriers, {}); - commandList->BindPipeline(horizontalBlurPipeline); - commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); + commandList->BindPipeline(horizontalBlurPipeline); + commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); - commandList->BindImage(target->swapVolumetricTexture.image, 3, 0); - commandList->BindImage(target->volumetricTexture.image, target->volumetricTexture.sampler, 3, 1); + commandList->BindImage(target->swapVolumetricTexture.image, 3, 0); + commandList->BindImage(target->volumetricTexture.image, target->volumetricTexture.sampler, 3, 1); - commandList->Dispatch(groupCount.x, groupCount.y, 1); + commandList->Dispatch(groupCount.x, groupCount.y, 1); - groupCount = ivec2(res.x, res.y / groupSize); - groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); + groupCount = ivec2(res.x, res.y / groupSize); + groupCount.y += ((res.y % groupSize == 0) ? 0 : 1); - imageBarriers = { - {target->swapVolumetricTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, - {target->volumetricTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, - }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + Graphics::ImageBarrier postImageBarriers[] = { + {target->swapVolumetricTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, + {target->volumetricTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_WRITE_BIT}, + }; + commandList->PipelineBarrier(postImageBarriers, {}); - commandList->BindPipeline(verticalBlurPipeline); - commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); + commandList->BindPipeline(verticalBlurPipeline); + commandList->PushConstants("constants", &kernelSize, sizeof(int32_t)); - commandList->BindImage(target->volumetricTexture.image, 3, 0); - commandList->BindImage(target->swapVolumetricTexture.image, target->swapVolumetricTexture.sampler, 3, 1); + commandList->BindImage(target->volumetricTexture.image, 3, 0); + commandList->BindImage(target->swapVolumetricTexture.image, target->swapVolumetricTexture.sampler, 3, 1); - commandList->Dispatch(groupCount.x, groupCount.y, 1); + commandList->Dispatch(groupCount.x, groupCount.y, 1); + } Graphics::Profiler::EndQuery(); } - imageBarriers = { + Graphics::ImageBarrier imageBarriers[] = { {target->volumetricTexture.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT}, {target->lightingTexture.image, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT}, }; - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier(imageBarriers, {}); { Graphics::Profiler::BeginQuery("Resolve"); @@ -274,6 +312,19 @@ namespace Atlas { commandList->BindImage(depthTexture->image, depthTexture->sampler, 3, 4); ResolveUniforms uniforms; + + if (scene->HasMainLight()) { + auto& mainLight = scene->GetMainLight(); + auto& lightDirection = mainLight.transformedProperties.directional.direction; + auto lightColor = Common::ColorConverter::ConvertSRGBToLinear(mainLight.color); + + uniforms.mainLightColor = vec4(lightColor * mainLight.intensity, 0.0f); + uniforms.mainLightDirection = vec4(lightDirection, 0.0f); + } + else { + uniforms.mainLightColor = vec4(0.0f); + } + uniforms.cloudsEnabled = cloudsEnabled ? 1 : 0; uniforms.fogEnabled = fogEnabled ? 1 : 0; uniforms.downsampled2x = target->GetVolumetricResolution() == RenderResolution::HALF_RES ? 1 : 0; diff --git a/src/engine/renderer/VolumetricRenderer.h b/src/engine/renderer/VolumetricRenderer.h index 50b7ea54b..67375bc27 100644 --- a/src/engine/renderer/VolumetricRenderer.h +++ b/src/engine/renderer/VolumetricRenderer.h @@ -18,14 +18,21 @@ namespace Atlas { void Render(Ref target, Ref scene, Graphics::CommandList* commandList); private: + struct alignas(16) CullingPushConstants { + int32_t lightCount; + }; + struct alignas(16) VolumetricUniforms { int sampleCount; float intensity; int fogEnabled; float oceanHeight; + int lightCount; + int offsetX; + int offsetY; + int directionalLightCount; vec4 planetCenterAndRadius; Fog fog; - Light light; CloudShadow cloudShadow; }; @@ -36,6 +43,8 @@ namespace Atlas { struct alignas(16) ResolveUniforms { Fog fog; vec4 planetCenter; + vec4 mainLightColor; + vec4 mainLightDirection; int downsampled2x; int cloudsEnabled; int fogEnabled; @@ -53,12 +62,18 @@ namespace Atlas { PipelineConfig resolvePipelineConfig; + Texture::Texture2D scramblingRankingTexture; + Texture::Texture2D sobolSequenceTexture; + + Buffer::Buffer lightCullingBuffer; + Buffer::Buffer blurWeightsUniformBuffer; Buffer::UniformBuffer volumetricUniformBuffer; - Buffer::UniformBuffer blurWeightsUniformBuffer; Buffer::UniformBuffer resolveUniformBuffer; - + Ref shadowSampler; + const int32_t filterSize = 4; + }; } diff --git a/src/engine/renderer/helper/CommonStructures.h b/src/engine/renderer/helper/CommonStructures.h index 30def4a6f..1e2da71de 100644 --- a/src/engine/renderer/helper/CommonStructures.h +++ b/src/engine/renderer/helper/CommonStructures.h @@ -2,16 +2,30 @@ #include "../../System.h" +#include "lighting/IrradianceVolume.h" + namespace Atlas { namespace Renderer { + enum MaterialFeatures { + FEATURE_BASE_COLOR_MAP = (1 << 1), + FEATURE_OPACITY_MAP = (1 << 2), + FEATURE_NORMAL_MAP = (1 << 3), + FEATURE_ROUGHNESS_MAP = (1 << 4), + FEATURE_METALNESS_MAP = (1 << 5), + FEATURE_AO_MAP = (1 << 6), + FEATURE_EMISSIVE_MAP = (1 << 7), + FEATURE_TRANSMISSION = (1 << 8), + FEATURE_VERTEX_COLORS = (1 << 9) + }; + struct alignas(16) Cascade { float distance; float texelSize; float aligment0; float aligment1; - mat4 cascadeSpace; + mat3x4 cascadeSpace; }; struct alignas(16) Shadow { @@ -23,7 +37,7 @@ namespace Atlas { int cascadeCount; - float aligment1; + int32_t mapIdx; vec2 resolution; @@ -39,13 +53,25 @@ namespace Atlas { float scatteringFactor; - float radius; - - float alignment; + float typeSpecific0; + float typeSpecific1; Shadow shadow; }; + struct alignas(16) VolumetricLight { + vec4 location; + vec4 direction; + + vec4 color; + float intensity; + + float typeSpecific0; + float typeSpecific1; + + int32_t shadowIdx; + }; + struct alignas(16) CloudShadow { mat4 vMatrix; mat4 pMatrix; @@ -65,6 +91,85 @@ namespace Atlas { float ambientFactor; }; + struct alignas(16) PackedMaterial { + + int32_t baseColor; + int32_t emissiveColor; + int32_t transmissionColor; + + uint32_t emissiveIntensityTiling; + + int32_t data0; + int32_t data1; + int32_t data2; + + int32_t features; + + }; + + struct alignas(16) GlobalUniforms { + vec4 frustumPlanes[6]; + mat4 vMatrix; + mat4 pMatrix; + mat4 ivMatrix; + mat4 ipMatrix; + mat4 pvMatrixLast; + mat4 pvMatrixCurrent; + mat4 ipvMatrixLast; + mat4 ipvMatrixCurrent; + mat4 vMatrixLast; + vec2 jitterLast; + vec2 jitterCurrent; + vec4 cameraLocation; + vec4 cameraDirection; + vec4 cameraUp; + vec4 cameraRight; + vec4 planetCenter; + vec2 windDir; + float windSpeed; + float planetRadius; + float time; + float deltaTime; + uint32_t frameCount; + float mipLodBias; + float cameraNearPlane; + float cameraFarPlane; + }; + + struct alignas(16) DDGICascade { + vec4 volumeMin; + vec4 volumeMax; + vec4 cellSize; + ivec4 offsetDifference; + }; + + struct alignas(16) DDGIUniforms { + + DDGICascade cascades[MAX_IRRADIANCE_VOLUME_CASCADES]; + + vec4 volumeCenter; + ivec4 volumeProbeCount; + int32_t cascadeCount; + + float volumeBias; + + int32_t volumeIrradianceRes; + int32_t volumeMomentsRes; + + uint32_t rayCount; + uint32_t inactiveRayCount; + + float hysteresis; + + float volumeGamma; + float volumeStrength; + + float depthSharpness; + int optimizeProbes; + + int32_t volumeEnabled; + }; + } } \ No newline at end of file diff --git a/src/engine/renderer/helper/RayTracingHelper.cpp b/src/engine/renderer/helper/RayTracingHelper.cpp index b27d676fe..ac98463b4 100644 --- a/src/engine/renderer/helper/RayTracingHelper.cpp +++ b/src/engine/renderer/helper/RayTracingHelper.cpp @@ -5,9 +5,12 @@ #include "../../volume/BVH.h" #include "../../graphics/Profiler.h" #include "../../pipeline/PipelineManager.h" +#include #define DIRECTIONAL_LIGHT 0 #define TRIANGLE_LIGHT 1 +#define POINT_LIGHT 2 +#define SPOT_LIGHT 3 namespace Atlas { @@ -74,7 +77,7 @@ namespace Atlas { auto lightCount = lightBuffer.GetElementCount(); selectedLights.clear(); // Randomly select lights (only at image offset 0) - if (lights.size() > lightCount) { + if (lights.size() > lightCount && stochasticLightSelection) { std::vector weights; weights.reserve(lights.size()); for (auto& light : lights) { @@ -95,8 +98,24 @@ namespace Atlas { light.data.y *= float(selectedLights.size()); } } + else if (lights.size() > lightCount && !stochasticLightSelection) { + auto& camera = scene->GetMainCamera(); + auto cameraLocation = camera.GetLocation(); + + std::sort(lights.begin(), lights.end(), [&](const GPULight& light0, const GPULight& light1) { + return glm::distance2(vec3(light0.P), cameraLocation) + < glm::distance2(vec3(light1.P), cameraLocation); + }); + + for (size_t i = 0; i < lightCount; i++) { + auto& light = lights[i]; + + light.data.y = 1.0f; + selectedLights.push_back(light); + } + } else { - for (auto light : lights) { + for (auto& light : lights) { light.data.y = 1.0f; selectedLights.push_back(light); } @@ -159,7 +178,7 @@ namespace Atlas { auto lightCount = lightBuffer.GetElementCount(); selectedLights.clear(); // Randomly select lights (only at image offset 0) - if (lights.size() > lightCount) { + if (lights.size() > lightCount && stochasticLightSelection) { std::vector weights; weights.reserve(lights.size()); for (auto& light : lights) { @@ -180,8 +199,24 @@ namespace Atlas { light.data.y *= float(selectedLights.size()); } } + else if (lights.size() > lightCount && !stochasticLightSelection) { + auto& camera = scene->GetMainCamera(); + auto cameraLocation = camera.GetLocation(); + + std::sort(lights.begin(), lights.end(), [&](const GPULight& light0, const GPULight& light1) { + return glm::distance2(vec3(light0.P), cameraLocation) + < glm::distance2(vec3(light1.P), cameraLocation); + }); + + for (size_t i = 0; i < lightCount; i++) { + auto& light = lights[i]; + + light.data.y = 1.0f; + selectedLights.push_back(light); + } + } else { - for (auto light : lights) { + for (auto& light : lights) { light.data.y = 1.0f; selectedLights.push_back(light); } @@ -255,9 +290,6 @@ namespace Atlas { commandList->BindBuffer(indirectDispatchBuffer.Get(), 2, 12); - std::vector bufferBarriers; - std::vector imageBarriers; - // Set up command buffer, reset ray count { auto pipeline = PipelineManager::GetPipeline(traceDispatchPipelineConfig); @@ -268,32 +300,39 @@ namespace Atlas { VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT); if (dispatchCounter % 2 == 0) { - bufferBarriers.push_back({ counterBuffer0.Get(), VK_ACCESS_SHADER_READ_BIT }); - bufferBarriers.push_back({ counterBuffer1.Get(), VK_ACCESS_SHADER_WRITE_BIT }); + Graphics::BufferBarrier bufferBarriers[] = { + { counterBuffer0.Get(), VK_ACCESS_SHADER_READ_BIT }, + { counterBuffer1.Get(), VK_ACCESS_SHADER_WRITE_BIT } + }; commandList->BindBuffer(counterBuffer0.Get(), 2, 13); commandList->BindBuffer(counterBuffer1.Get(), 2, 14); + + commandList->PipelineBarrier({}, bufferBarriers); } else { - bufferBarriers.push_back({ counterBuffer0.Get(), VK_ACCESS_SHADER_WRITE_BIT }); - bufferBarriers.push_back({ counterBuffer1.Get(), VK_ACCESS_SHADER_READ_BIT }); + Graphics::BufferBarrier bufferBarriers[] = { + { counterBuffer0.Get(), VK_ACCESS_SHADER_WRITE_BIT }, + { counterBuffer1.Get(), VK_ACCESS_SHADER_READ_BIT } + }; commandList->BindBuffer(counterBuffer0.Get(), 2, 14); commandList->BindBuffer(counterBuffer1.Get(), 2, 13); + + commandList->PipelineBarrier({}, bufferBarriers); } - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->Dispatch(1, 1, 1); } - bufferBarriers.clear(); - imageBarriers.clear(); - // Can't group barriers together because of different access patterns commandList->BufferMemoryBarrier(indirectDispatchBuffer.Get(), VK_ACCESS_INDIRECT_COMMAND_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT); - bufferBarriers.push_back({ rayBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); - bufferBarriers.push_back({ rayPayloadBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + Graphics::BufferBarrier bufferBarriers[] = { + { rayBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }, + { rayPayloadBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT } + }; + commandList->PipelineBarrier({}, bufferBarriers); commandList->BindBuffer(rayBuffer.Get(), 2, 15); commandList->BindBuffer(rayPayloadBuffer.Get(), 2, 16); @@ -365,19 +404,18 @@ namespace Atlas { Graphics::Profiler::EndAndBeginQuery("Execute hit shader"); - bufferBarriers.clear(); - imageBarriers.clear(); - - bufferBarriers.push_back({ rayBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); - bufferBarriers.push_back({ rayPayloadBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); + Graphics::BufferBarrier preShaderBufferBarriers[3] = { + { rayBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }, + { rayPayloadBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT } + }; if (dispatchCounter % 2 == 0) { - bufferBarriers.push_back({ counterBuffer1.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); + preShaderBufferBarriers[2] = {counterBuffer1.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT}; } else { - bufferBarriers.push_back({ counterBuffer0.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); + preShaderBufferBarriers[2] = {counterBuffer0.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT}; } - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + commandList->PipelineBarrier({}, preShaderBufferBarriers); // Shade rays { @@ -406,22 +444,22 @@ namespace Atlas { void RayTracingHelper::InvalidateRayBuffer(Graphics::CommandList* commandList) { - std::vector bufferBarriers; - std::vector imageBarriers; - - bufferBarriers.push_back({counterBuffer0.Get(), VK_ACCESS_TRANSFER_WRITE_BIT}); - bufferBarriers.push_back({counterBuffer1.Get(), VK_ACCESS_TRANSFER_WRITE_BIT}); - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Graphics::BufferBarrier preBufferBarriers[] = { + {counterBuffer0.Get(), VK_ACCESS_TRANSFER_WRITE_BIT}, + {counterBuffer1.Get(), VK_ACCESS_TRANSFER_WRITE_BIT} + }; + commandList->PipelineBarrier({}, preBufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); uint32_t zero = 0; commandList->FillBuffer(counterBuffer0.Get(), &zero); commandList->FillBuffer(counterBuffer1.Get(), &zero); - bufferBarriers.clear(); - bufferBarriers.push_back({counterBuffer0.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}); - bufferBarriers.push_back({counterBuffer1.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}); - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_TRANSFER_BIT, + Graphics::BufferBarrier postBufferBarriers[] = { + {counterBuffer0.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}, + {counterBuffer1.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT} + }; + commandList->PipelineBarrier({}, postBufferBarriers, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } @@ -437,6 +475,9 @@ namespace Atlas { lights.clear(); + auto& camera = scene->GetMainCamera(); + auto cameraLocation = camera.GetLocation(); + auto lightSubset = scene->GetSubset(); for (auto& lightEntity : lightSubset) { auto& light = lightEntity.GetComponent(); @@ -447,28 +488,55 @@ namespace Atlas { vec3 P = vec3(0.0f); vec3 N = vec3(0.0f); float weight = 0.0f; - float area = 0.0f; + float radius = 1.0f; + float specific0 = 0.0f; + float specific1 = 0.0f; - uint32_t data = 0; + uint32_t data0 = 0, data1 = 0; + const auto& prop = light.transformedProperties; // Parse individual light information based on type if (light.type == LightType::DirectionalLight) { - data |= (DIRECTIONAL_LIGHT << 28u); + data0 |= (DIRECTIONAL_LIGHT << 28u); weight = brightness; + P = cameraLocation; N = light.transformedProperties.directional.direction; } else if (light.type == LightType::PointLight) { + data0 |= (POINT_LIGHT << 28u); + weight = brightness; + P = light.transformedProperties.point.position; + radius = light.transformedProperties.point.radius; + } + else if (light.type == LightType::SpotLight) { + data0 |= (SPOT_LIGHT << 28u); + weight = brightness; + P = light.transformedProperties.spot.position; + N = light.transformedProperties.spot.direction; + radius = light.transformedProperties.spot.radius; + + auto cosOuter = cosf(prop.spot.outerConeAngle); + auto cosInner = cosf(prop.spot.innerConeAngle); + auto lightAngleScale = 1.0f / std::max(0.001f, cosInner - cosOuter); + auto lightAngleOffset = -cosOuter * lightAngleScale; + specific0 = lightAngleScale; + specific1 = lightAngleOffset; } - data |= uint32_t(lights.size()); - auto cd = reinterpret_cast(data); + if (light.shadow) { + data1 |= 1u; + } + + data0 |= uint32_t(lights.size()); + auto castData0 = reinterpret_cast(data0); + auto castData1 = reinterpret_cast(data1); GPULight gpuLight; - gpuLight.P = vec4(P, 1.0f); - gpuLight.N = vec4(N, 0.0f); + gpuLight.P = vec4(P, castData1); + gpuLight.N = vec4(N, radius); gpuLight.color = vec4(radiance, 0.0f); - gpuLight.data = vec4(cd, weight, area, 0.0f); + gpuLight.data = vec4(castData0, weight, specific0, specific1); lights.push_back(gpuLight); } @@ -477,25 +545,27 @@ namespace Atlas { const auto& rtData = scene->rayTracingWorld; lights.insert(lights.end(), rtData->triangleLights.begin(), rtData->triangleLights.end()); } - - // Find the maximum weight - auto maxWeight = 0.0f; - for (auto& light : lights) { - maxWeight = glm::max(maxWeight, light.data.y); - } + + if (stochasticLightSelection) { + // Find the maximum weight + auto maxWeight = 0.0f; + for (auto& light : lights) { + maxWeight = glm::max(maxWeight, light.data.y); + } - // Calculate min weight and adjust lights based on it - auto minWeight = 0.005f * maxWeight; - // Also calculate the total weight - auto totalWeight = 0.0f; + // Calculate min weight and adjust lights based on it + auto minWeight = 0.005f * maxWeight; + // Also calculate the total weight + auto totalWeight = 0.0f; - for (auto& light : lights) { - light.data.y = glm::max(light.data.y, minWeight); - totalWeight += light.data.y; - } + for (auto& light : lights) { + light.data.y = glm::max(light.data.y, minWeight); + totalWeight += light.data.y; + } - for (auto& light : lights) { - light.data.y /= totalWeight; + for (auto& light : lights) { + light.data.y /= totalWeight; + } } } diff --git a/src/engine/renderer/helper/RayTracingHelper.h b/src/engine/renderer/helper/RayTracingHelper.h index a75d526eb..a436036e6 100644 --- a/src/engine/renderer/helper/RayTracingHelper.h +++ b/src/engine/renderer/helper/RayTracingHelper.h @@ -43,6 +43,7 @@ namespace Atlas { void UpdateLights(Ref scene, bool useEmissivesAsLights = false); + bool stochasticLightSelection = false; private: struct alignas(16) PushConstants { diff --git a/src/engine/renderer/helper/RenderList.cpp b/src/engine/renderer/helper/RenderList.cpp index 0a4e8b205..f39110e3c 100644 --- a/src/engine/renderer/helper/RenderList.cpp +++ b/src/engine/renderer/helper/RenderList.cpp @@ -20,15 +20,25 @@ namespace Atlas { } - void RenderList::NewFrame(const Ref& scene) { + void RenderList::NewFrame(Scene::Scene* scene) { + std::scoped_lock lock(mutex); this->scene = scene; doneProcessingShadows = false; processedPasses.clear(); JobSystem::Wait(clearJob); + wasCleared = false; + + auto meshes = scene->GetMeshes(); + meshIdToMeshMap.reserve(meshes.size()); + // Fill in missing meshes since they are cleared at the end of each frame + for (const auto& mesh : meshes) { + auto id = mesh.GetID(); + meshIdToMeshMap[id] = mesh; + } } Ref RenderList::NewMainPass() { @@ -122,18 +132,21 @@ namespace Atlas { void RenderList::Clear() { // We can reset the scene now and delete the reference + wasCleared = true; scene = nullptr; JobSystem::Execute(clearJob, [&](JobData&) { std::erase_if(passes, [](const auto& item) { return item->wasUsed != true; }); + meshIdToMeshMap.clear(); for (auto& pass : passes) pass->Reset(); }); } - void RenderList::Pass::NewFrame(const Ref& scene, const std::vector>& meshes) { + void RenderList::Pass::NewFrame(Scene::Scene* scene, const std::vector>& meshes, + const std::unordered_map>& meshIdToMeshMap) { this->scene = scene; @@ -149,14 +162,6 @@ namespace Atlas { impostorMatrices.clear(); if (lastSize) impostorMatrices.reserve(lastSize); - meshIdToMeshMap.reserve(meshes.size()); - - // Fill in missing meshes since they are cleared at the end of each frame - for (const auto& mesh : meshes) { - auto id = mesh.GetID(); - meshIdToMeshMap[id] = mesh; - } - std::erase_if(meshToEntityMap, [&](const auto& item) { return !meshIdToMeshMap.contains(item.first); }); std::erase_if(meshToInstancesMap, [&](const auto& item) { return !meshIdToMeshMap.contains(item.first); }); @@ -178,18 +183,21 @@ namespace Atlas { batch.Add(entity); meshToEntityMap[id] = batch; - meshIdToMeshMap[id] = meshComponent.mesh; } } - void RenderList::Pass::Update(vec3 cameraLocation) { + void RenderList::Pass::Update(vec3 cameraLocation, const std::unordered_map>& meshIdToMeshMap) { size_t maxActorCount = 0; size_t maxImpostorCount = 0; for (auto& [meshId, batch] : meshToEntityMap) { - auto mesh = meshIdToMeshMap[meshId]; + auto item = meshIdToMeshMap.find(meshId); + // This happens when meshes are loaded async + if (item == meshIdToMeshMap.end()) + continue; + auto mesh = item->second; if (!mesh->castShadow && type == RenderPassType::Shadow) continue; @@ -199,7 +207,11 @@ namespace Atlas { } for (auto& [meshId, batch] : meshToEntityMap) { - auto mesh = meshIdToMeshMap[meshId]; + auto item = meshIdToMeshMap.find(meshId); + // This happens when meshes are loaded async + if (item == meshIdToMeshMap.end()) + continue; + auto mesh = item->second; if (!batch.count) continue; if (!mesh->castShadow && type == RenderPassType::Shadow) continue; @@ -313,7 +325,6 @@ namespace Atlas { } // Need to clear this to free the references - meshIdToMeshMap.clear(); meshToInstancesMap.clear(); wasUsed = false; @@ -321,4 +332,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/renderer/helper/RenderList.h b/src/engine/renderer/helper/RenderList.h index 6c9efcbde..047151fe7 100644 --- a/src/engine/renderer/helper/RenderList.h +++ b/src/engine/renderer/helper/RenderList.h @@ -54,11 +54,10 @@ namespace Atlas { Scene::Entity lightEntity; uint32_t layer; - Ref scene = nullptr; + Scene::Scene* scene = nullptr; std::unordered_map meshToEntityMap; std::unordered_map meshToInstancesMap; - std::unordered_map> meshIdToMeshMap; bool wasUsed = false; @@ -70,11 +69,12 @@ namespace Atlas { Ref lastMatricesBuffer; Ref impostorMatricesBuffer; - void NewFrame(const Ref& scene, const std::vector>& meshes); + void NewFrame(Scene::Scene* scene, const std::vector>& meshes, + const std::unordered_map>& meshIdToMeshMap); void Add(const ECS::Entity& entity, const MeshComponent& meshComponent); - void Update(vec3 cameraLocation); + void Update(vec3 cameraLocation, const std::unordered_map>& meshIdToMeshMap); void FillBuffers(); @@ -85,7 +85,7 @@ namespace Atlas { ~RenderList(); - void NewFrame(const Ref& scene); + void NewFrame(Scene::Scene* scene); // Note: The expected behaviour is to first create and process all shadow passes and then finally do the main pass last Ref NewMainPass(); @@ -102,13 +102,16 @@ namespace Atlas { void Clear(); - Ref scene = nullptr; + Scene::Scene* scene = nullptr; std::vector> passes; std::deque> processedPasses; std::mutex mutex; std::atomic_bool doneProcessingShadows; + std::atomic_bool wasCleared = false; + + std::unordered_map> meshIdToMeshMap; JobGroup clearJob { JobPriority::High }; }; diff --git a/src/engine/renderer/helper/VegetationHelper.cpp b/src/engine/renderer/helper/VegetationHelper.cpp index 3de59d36a..ec0017935 100644 --- a/src/engine/renderer/helper/VegetationHelper.cpp +++ b/src/engine/renderer/helper/VegetationHelper.cpp @@ -40,9 +40,6 @@ namespace Atlas { uint32_t binCount; }; - std::vector bufferBarriers; - std::vector imageBarriers; - Graphics::Profiler::BeginQuery("Culling"); auto meshes = vegetation.GetMeshes(); @@ -93,9 +90,8 @@ namespace Atlas { commandList->Dispatch(groupCount, 1, 1); } - bufferBarriers.clear(); - bufferBarriers.push_back({ binCounterBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + Graphics::BufferBarrier bufferBarriers[] = {{ binCounterBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }}; + commandList->PipelineBarrier({}, bufferBarriers); } Graphics::Profiler::EndAndBeginQuery("Compute bin offsets"); @@ -114,16 +110,17 @@ namespace Atlas { commandList->Dispatch(groupCount, 1, 1); - bufferBarriers.clear(); - bufferBarriers.push_back({binOffsetBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); - bufferBarriers.push_back({binCounterBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); - bufferBarriers.push_back({instanceCounterBuffer.Get(), VK_ACCESS_SHADER_READ_BIT}); + meshBufferBarriers.clear(); + meshBufferBarriers.push_back({ binOffsetBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); + meshBufferBarriers.push_back({ binCounterBuffer.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); + meshBufferBarriers.push_back({ instanceCounterBuffer.Get(), VK_ACCESS_SHADER_READ_BIT }); for (auto mesh : meshes) { auto buffers = vegetation.GetBuffers(mesh); - bufferBarriers.push_back({buffers->culledInstanceData.Get(), + meshBufferBarriers.push_back({buffers->culledInstanceData.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); } - commandList->PipelineBarrier(imageBarriers, bufferBarriers); + + commandList->PipelineBarrier({}, meshBufferBarriers); } Graphics::Profiler::EndAndBeginQuery("Sort by bins"); @@ -192,13 +189,14 @@ namespace Atlas { commandList->BufferMemoryBarrier(indirectDrawCallBuffer.Get(), VK_ACCESS_INDIRECT_COMMAND_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT); - bufferBarriers.clear(); + meshBufferBarriers.clear(); + for (auto mesh : meshes) { auto buffers = vegetation.GetBuffers(mesh); - bufferBarriers.push_back({buffers->binnedInstanceData.Get(), + meshBufferBarriers.push_back({buffers->binnedInstanceData.Get(), VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT }); } - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + commandList->PipelineBarrier({}, meshBufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT); Graphics::Profiler::EndQuery(); @@ -276,22 +274,22 @@ namespace Atlas { void VegetationHelper::ResetCounterBuffers(Graphics::CommandList* commandList) { - std::vector bufferBarriers; - std::vector imageBarriers; - - bufferBarriers.push_back({binCounterBuffer.Get(), VK_ACCESS_TRANSFER_WRITE_BIT}); - bufferBarriers.push_back({instanceCounterBuffer.Get(), VK_ACCESS_TRANSFER_WRITE_BIT}); - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + Graphics::BufferBarrier preBufferBarriers[] = { + {binCounterBuffer.Get(), VK_ACCESS_TRANSFER_WRITE_BIT}, + {instanceCounterBuffer.Get(), VK_ACCESS_TRANSFER_WRITE_BIT} + }; + commandList->PipelineBarrier({}, preBufferBarriers, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); uint32_t zero = 0; commandList->FillBuffer(binCounterBuffer.Get(), &zero); commandList->FillBuffer(instanceCounterBuffer.Get(), &zero); - bufferBarriers.clear(); - bufferBarriers.push_back({binCounterBuffer.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}); - bufferBarriers.push_back({instanceCounterBuffer.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}); - commandList->PipelineBarrier(imageBarriers, bufferBarriers, VK_PIPELINE_STAGE_TRANSFER_BIT, + Graphics::BufferBarrier postBufferBarriers[] = { + {binCounterBuffer.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}, + {instanceCounterBuffer.Get(), VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT} + }; + commandList->PipelineBarrier({}, postBufferBarriers, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); } diff --git a/src/engine/renderer/helper/VegetationHelper.h b/src/engine/renderer/helper/VegetationHelper.h index 442ba2baa..2377a7c86 100644 --- a/src/engine/renderer/helper/VegetationHelper.h +++ b/src/engine/renderer/helper/VegetationHelper.h @@ -69,6 +69,8 @@ namespace Atlas { std::map meshToIdxMap; std::vector meshSubdataInformation; + std::vector meshBufferBarriers; + }; } diff --git a/src/engine/renderer/target/PathTraceRenderTarget.cpp b/src/engine/renderer/target/PathTraceRenderTarget.cpp deleted file mode 100644 index 731a78328..000000000 --- a/src/engine/renderer/target/PathTraceRenderTarget.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "PathTraceRenderTarget.h" - -namespace Atlas::Renderer { - - PathTracerRenderTarget::PathTracerRenderTarget(int32_t width, int32_t height) : width(width), height(height) { - - auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; - - texture = Texture::Texture2D(width, height, VK_FORMAT_R8G8B8A8_UNORM, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - - frameAccumTexture = Texture::Texture2DArray(width, height, 3, VK_FORMAT_R32_UINT); - - albedoTexture = Texture::Texture2D(width, height, VK_FORMAT_R8G8B8A8_UNORM); - - velocityTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); - historyVelocityTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); - - depthTexture = Texture::Texture2D(width, height, VK_FORMAT_R32_SFLOAT); - historyDepthTexture = Texture::Texture2D(width, height, VK_FORMAT_R32_SFLOAT); - - normalTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); - historyNormalTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16_SFLOAT); - - materialIdxTexture = Texture::Texture2D(width, height, VK_FORMAT_R16_UINT); - historyMaterialIdxTexture = Texture::Texture2D(width, height, VK_FORMAT_R16_UINT); - - radianceTexture = Texture::Texture2D(width, height, VK_FORMAT_R32G32B32A32_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - historyRadianceTexture = Texture::Texture2D(width, height, VK_FORMAT_R32G32B32A32_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - - postProcessTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - historyPostProcessTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - - outputTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); - - { - Graphics::RenderPassColorAttachment colorAttachments[] = { - {.imageFormat = outputTexture.format} - }; - for (auto& attachment : colorAttachments) { - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - auto outputPassDesc = Graphics::RenderPassDesc{ - .colorAttachments = {colorAttachments[0]} - }; - outputRenderPass = graphicsDevice->CreateRenderPass(outputPassDesc); - } - - CreateFramebuffers(); - - } - - void PathTracerRenderTarget::Resize(int32_t width, int32_t height) { - this->width = width; - this->height = height; - - texture.Resize(width, height); - - frameAccumTexture.Resize(width, height, 3); - - albedoTexture.Resize(width, height); - - velocityTexture.Resize(width, height); - historyVelocityTexture.Resize(width, height); - - depthTexture.Resize(width, height); - historyDepthTexture.Resize(width, height); - - normalTexture.Resize(width, height); - historyNormalTexture.Resize(width, height); - - materialIdxTexture.Resize(width, height); - historyMaterialIdxTexture.Resize(width, height); - - radianceTexture.Resize(width, height); - historyRadianceTexture.Resize(width, height); - - postProcessTexture.Resize(width, height); - historyPostProcessTexture.Resize(width, height); - - outputTexture.Resize(width, height); - - CreateFramebuffers(); - - } - - void PathTracerRenderTarget::Swap() { - - std::swap(radianceTexture, historyRadianceTexture); - std::swap(velocityTexture, historyVelocityTexture); - std::swap(depthTexture, historyDepthTexture); - std::swap(normalTexture, historyNormalTexture); - std::swap(materialIdxTexture, historyMaterialIdxTexture); - std::swap(postProcessTexture, historyPostProcessTexture); - - } - - void PathTracerRenderTarget::CreateFramebuffers() { - - auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; - - auto outputFrameBufferDesc = Graphics::FrameBufferDesc{ - .renderPass = outputRenderPass, - .colorAttachments = { - {outputTexture.image, 0, true} - }, - .extent = {uint32_t(width), uint32_t(height)} - }; - outputFrameBuffer = graphicsDevice->CreateFrameBuffer(outputFrameBufferDesc); - - } - -} \ No newline at end of file diff --git a/src/engine/renderer/target/PathTraceRenderTarget.h b/src/engine/renderer/target/PathTraceRenderTarget.h deleted file mode 100644 index c2dcc3a18..000000000 --- a/src/engine/renderer/target/PathTraceRenderTarget.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "System.h" -#include "texture/Texture2D.h" -#include "texture/Texture2DArray.h" -#include "graphics/RenderPass.h" -#include "graphics/Framebuffer.h" - -namespace Atlas::Renderer { - - class PathTracerRenderTarget { - - public: - PathTracerRenderTarget() {} - - PathTracerRenderTarget(int32_t width, int32_t height); - - void Resize(int32_t width, int32_t height); - - void Swap(); - - int32_t GetWidth() const { return width; } - int32_t GetHeight() const { return height; } - - Texture::Texture2D texture; - - Texture::Texture2DArray frameAccumTexture; - - Texture::Texture2D albedoTexture; - Texture::Texture2D velocityTexture; - Texture::Texture2D depthTexture; - Texture::Texture2D normalTexture; - Texture::Texture2D materialIdxTexture; - - Texture::Texture2D historyVelocityTexture; - Texture::Texture2D historyDepthTexture; - Texture::Texture2D historyNormalTexture; - Texture::Texture2D historyMaterialIdxTexture; - - Texture::Texture2D radianceTexture; - Texture::Texture2D historyRadianceTexture; - - Texture::Texture2D postProcessTexture; - Texture::Texture2D historyPostProcessTexture; - - Texture::Texture2D outputTexture; - - Ref outputRenderPass; - Ref outputFrameBuffer; - - int32_t sampleCount = 0; - - private: - void CreateFramebuffers(); - - int32_t width = 0; - int32_t height = 0; - - }; - -} \ No newline at end of file diff --git a/src/engine/renderer/target/RenderTarget.cpp b/src/engine/renderer/target/RenderTarget.cpp index 0ecce5606..1e10f5f7d 100644 --- a/src/engine/renderer/target/RenderTarget.cpp +++ b/src/engine/renderer/target/RenderTarget.cpp @@ -21,119 +21,32 @@ namespace Atlas::Renderer { targetDataSwapDownsampled2x = RenderTargetData(halfRes, false); historyTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); swapHistoryTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); lightingTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); reactiveMaskTexture = Texture::Texture2D(scaledWidth, scaledWidth, VK_FORMAT_R8_UNORM, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); hdrTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::MipMapLinear, false, true); + bloomTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::MipMapLinear, false, true); outputTexture = Texture::Texture2D(width, height, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); oceanDepthTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_D32_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); oceanStencilTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R8_UINT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); - - { - Graphics::RenderPassColorAttachment colorAttachments[] = { - {.imageFormat = targetData.baseColorTexture->format}, - {.imageFormat = targetData.normalTexture->format}, - {.imageFormat = targetData.geometryNormalTexture->format}, - {.imageFormat = targetData.roughnessMetallicAoTexture->format}, - {.imageFormat = targetData.materialIdxTexture->format}, - {.imageFormat = targetData.velocityTexture->format}, - {.imageFormat = targetData.stencilTexture->format}, - }; - - for (auto &attachment: colorAttachments) { - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - Graphics::RenderPassDepthAttachment depthAttachment = { - .imageFormat = targetData.depthTexture->format, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }; - - auto gBufferRenderPassDesc = Graphics::RenderPassDesc{ - .colorAttachments = {colorAttachments[0], colorAttachments[1], colorAttachments[2], - colorAttachments[3], colorAttachments[4], colorAttachments[5], colorAttachments[6]}, - .depthAttachment = depthAttachment - }; - gBufferRenderPass = graphicsDevice->CreateRenderPass(gBufferRenderPassDesc); - } - { - Graphics::RenderPassColorAttachment colorAttachments[] = { - {.imageFormat = lightingTexture.format}, - {.imageFormat = targetData.velocityTexture->format}, - {.imageFormat = targetData.stencilTexture->format}, - }; - - for (auto &attachment: colorAttachments) { - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; - attachment.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); - Graphics::RenderPassDepthAttachment depthAttachment = { - .imageFormat = targetData.depthTexture->format, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, - .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }; - - auto afterLightingRenderPassDesc = Graphics::RenderPassDesc{ - .colorAttachments = {colorAttachments[0], colorAttachments[1], colorAttachments[2]}, - .depthAttachment = depthAttachment - }; - afterLightingRenderPass = graphicsDevice->CreateRenderPass(afterLightingRenderPassDesc); - } - { - Graphics::RenderPassColorAttachment colorAttachments[] = { - {.imageFormat = oceanStencilTexture.format} - }; - for (auto &attachment: colorAttachments) { - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - Graphics::RenderPassDepthAttachment depthAttachment = { - .imageFormat = oceanDepthTexture.format, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }; - - auto oceanRenderPassDesc = Graphics::RenderPassDesc { - .colorAttachments = {colorAttachments[0]}, - .depthAttachment = depthAttachment - }; - oceanRenderPass = graphicsDevice->CreateRenderPass(oceanRenderPassDesc); - } - - { - Graphics::RenderPassColorAttachment colorAttachments[] = { - {.imageFormat = outputTexture.format} - }; - for (auto &attachment: colorAttachments) { - attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - auto outputPassDesc = Graphics::RenderPassDesc { - .colorAttachments = {colorAttachments[0]} - }; - outputRenderPass = graphicsDevice->CreateRenderPass(outputPassDesc); - } + radianceTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R32G32B32A32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + historyRadianceTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R32G32B32A32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + frameAccumTexture = Texture::Texture2DArray(scaledWidth, scaledHeight, 3, VK_FORMAT_R32_UINT); + CreateRenderPasses(); CreateFrameBuffers(); SetGIResolution(HALF_RES, false); @@ -153,10 +66,7 @@ namespace Atlas::Renderer { // Need to at least have a size of 2x2 pixels scaledWidth = glm::max(2, int32_t(scalingFactor * width)); - scaledHeight = glm::max(2, int32_t(scalingFactor * height)); - - targetData.Resize(ivec2(scaledWidth, scaledHeight)); - targetDataSwap.Resize(ivec2(scaledWidth, scaledHeight)); + scaledHeight = glm::max(2, int32_t(scalingFactor * height)); // We have to also resize the other part of the history historyTexture.Resize(scaledWidth, scaledHeight); @@ -164,6 +74,7 @@ namespace Atlas::Renderer { lightingTexture.Resize(scaledWidth, scaledHeight); reactiveMaskTexture.Resize(scaledWidth, scaledHeight); hdrTexture.Resize(width, height); + bloomTexture.Resize(width, height); outputTexture.Resize(width, height); sssTexture.Resize(scaledWidth, scaledHeight); oceanDepthTexture.Resize(scaledWidth, scaledHeight); @@ -174,11 +85,7 @@ namespace Atlas::Renderer { SetVolumetricResolution(volumetricResolution); SetReflectionResolution(reflectionResolution); - ivec2 halfRes = GetRelativeResolution(HALF_RES); - targetDataDownsampled2x.Resize(halfRes); - targetDataSwapDownsampled2x.Resize(halfRes); - - CreateFrameBuffers(); + UseForPathTracing(useForPathTracing); hasHistory = false; @@ -213,6 +120,8 @@ namespace Atlas::Renderer { hasHistory = true; swap = !swap; + std::swap(historyRadianceTexture, radianceTexture); + CreateFrameBuffers(); } @@ -236,27 +145,27 @@ namespace Atlas::Renderer { giResolution = resolution; giTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); swapGiTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyGiTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); if (createMomentsTexture) { giLengthTexture.Reset(); historyGiLengthTexture.Reset(); giMomentsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyGiMomentsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); } else { giMomentsTexture.Reset(); historyGiMomentsTexture.Reset(); giLengthTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyGiLengthTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); } hasHistory = false; @@ -275,16 +184,16 @@ namespace Atlas::Renderer { aoResolution = resolution; aoTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); swapAoTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyAoTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); aoLengthTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyAoLengthTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); hasHistory = false; @@ -301,15 +210,17 @@ namespace Atlas::Renderer { auto res = GetRelativeResolution(resolution); volumetricResolution = resolution; - volumetricTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT); - swapVolumetricTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT); + volumetricTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + swapVolumetricTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); volumetricCloudsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); swapVolumetricCloudsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyVolumetricCloudsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); hasHistory = false; @@ -327,16 +238,16 @@ namespace Atlas::Renderer { reflectionResolution = resolution; reflectionTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); swapReflectionTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyReflectionTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); reflectionMomentsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); historyReflectionMomentsTexture = Texture::Texture2D(res.x, res.y, VK_FORMAT_R16G16B16A16_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); hasHistory = false; @@ -420,6 +331,156 @@ namespace Atlas::Renderer { } + void RenderTarget::UseForPathTracing(bool use) { + + hasHistory = false; + useForPathTracing = use; + + if (useForPathTracing) { + ivec2 res = GetRelativeResolution(FULL_RES); + targetData = RenderTargetData(res, false); + targetDataSwap = RenderTargetData(res, false); + + targetDataDownsampled2x = RenderTargetData(); + targetDataSwapDownsampled2x = RenderTargetData(); + + radianceTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R32G32B32A32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + historyRadianceTexture = Texture::Texture2D(scaledWidth, scaledHeight, VK_FORMAT_R32G32B32A32_SFLOAT, + Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + frameAccumTexture = Texture::Texture2DArray(scaledWidth, scaledHeight, 3, VK_FORMAT_R32_UINT, + Texture::Wrapping::Repeat, Texture::Filtering::Nearest, false, true); + } + else { + ivec2 res = GetRelativeResolution(FULL_RES); + targetData = RenderTargetData(res, true); + targetDataSwap = RenderTargetData(res, true); + + ivec2 halfRes = GetRelativeResolution(HALF_RES); + targetDataDownsampled2x = RenderTargetData(halfRes, false); + targetDataSwapDownsampled2x = RenderTargetData(halfRes, false); + + radianceTexture.Reset(); + historyRadianceTexture.Reset(); + frameAccumTexture.Reset(); + } + + // Pathtracing is kind of tricky with the depth buffer being in a SFLOAT format, + // so we can't attach this one to render passes or frame buffers + CreateRenderPasses(); + CreateFrameBuffers(); + + } + + bool RenderTarget::IsUsedForPathTracing() const { + + return useForPathTracing; + + } + + void RenderTarget::CreateRenderPasses() { + + auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; + + { + Graphics::RenderPassColorAttachment colorAttachments[] = { + {.imageFormat = targetData.baseColorTexture->format}, + {.imageFormat = targetData.normalTexture->format}, + {.imageFormat = targetData.geometryNormalTexture->format}, + {.imageFormat = targetData.roughnessMetallicAoTexture->format}, + {.imageFormat = targetData.emissiveTexture->format}, + {.imageFormat = targetData.materialIdxTexture->format}, + {.imageFormat = targetData.velocityTexture->format}, + {.imageFormat = targetData.stencilTexture->format}, + }; + + for (auto& attachment : colorAttachments) { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + Graphics::RenderPassDepthAttachment depthAttachment = { + .imageFormat = targetData.depthTexture->format, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + + auto gBufferRenderPassDesc = Graphics::RenderPassDesc{ + .colorAttachments = {colorAttachments[0], colorAttachments[1], colorAttachments[2], colorAttachments[3], + colorAttachments[4], colorAttachments[5], colorAttachments[6], colorAttachments[7]}, + .depthAttachment = useForPathTracing ? Graphics::RenderPassDepthAttachment() : depthAttachment + }; + gBufferRenderPass = graphicsDevice->CreateRenderPass(gBufferRenderPassDesc); + } + { + Graphics::RenderPassColorAttachment colorAttachments[] = { + {.imageFormat = lightingTexture.format}, + {.imageFormat = targetData.velocityTexture->format}, + {.imageFormat = targetData.stencilTexture->format}, + }; + + for (auto& attachment : colorAttachments) { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment.initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + Graphics::RenderPassDepthAttachment depthAttachment = { + .imageFormat = targetData.depthTexture->format, + .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + + auto afterLightingRenderPassDesc = Graphics::RenderPassDesc{ + .colorAttachments = {colorAttachments[0], colorAttachments[1], colorAttachments[2]}, + .depthAttachment = useForPathTracing ? Graphics::RenderPassDepthAttachment() : depthAttachment + }; + afterLightingRenderPass = graphicsDevice->CreateRenderPass(afterLightingRenderPassDesc); + } + { + Graphics::RenderPassColorAttachment colorAttachments[] = { + {.imageFormat = oceanStencilTexture.format} + }; + for (auto& attachment : colorAttachments) { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + Graphics::RenderPassDepthAttachment depthAttachment = { + .imageFormat = oceanDepthTexture.format, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }; + + auto oceanRenderPassDesc = Graphics::RenderPassDesc{ + .colorAttachments = {colorAttachments[0]}, + .depthAttachment = depthAttachment + }; + oceanRenderPass = graphicsDevice->CreateRenderPass(oceanRenderPassDesc); + } + + { + Graphics::RenderPassColorAttachment colorAttachments[] = { + {.imageFormat = outputTexture.format} + }; + for (auto& attachment : colorAttachments) { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.outputLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + auto outputPassDesc = Graphics::RenderPassDesc{ + .colorAttachments = {colorAttachments[0]} + }; + outputRenderPass = graphicsDevice->CreateRenderPass(outputPassDesc); + } + + } + void RenderTarget::CreateFrameBuffers() { auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; @@ -433,11 +494,12 @@ namespace Atlas::Renderer { {target->normalTexture->image, 0, true}, {target->geometryNormalTexture->image, 0, true}, {target->roughnessMetallicAoTexture->image, 0, true}, + {target->emissiveTexture->image, 0, true}, {target->materialIdxTexture->image, 0, true}, {target->velocityTexture->image, 0, true}, {target->stencilTexture->image, 0, false}, }, - .depthAttachment = {target->depthTexture->image, 0, true}, + .depthAttachment = { useForPathTracing ? nullptr : target->depthTexture->image, 0, !useForPathTracing }, .extent = {uint32_t(scaledWidth), uint32_t(scaledHeight)} }; gBufferFrameBuffer = graphicsDevice->CreateFrameBuffer(gBufferFrameBufferDesc); @@ -449,7 +511,7 @@ namespace Atlas::Renderer { {target->velocityTexture->image, 0, true}, {target->stencilTexture->image, 0, false} }, - .depthAttachment = {target->depthTexture->image, 0, true}, + .depthAttachment = { useForPathTracing ? nullptr : target->depthTexture->image, 0, !useForPathTracing}, .extent = {uint32_t(scaledWidth), uint32_t(scaledHeight)} }; afterLightingFrameBuffer = graphicsDevice->CreateFrameBuffer(afterLightingFrameBufferDesc); @@ -461,7 +523,7 @@ namespace Atlas::Renderer { {target->velocityTexture->image, 0, true}, {target->stencilTexture->image, 0, true} }, - .depthAttachment = {target->depthTexture->image, 0, true}, + .depthAttachment = { useForPathTracing ? nullptr : target->depthTexture->image, 0, !useForPathTracing}, .extent = {uint32_t(scaledWidth), uint32_t(scaledHeight)} }; afterLightingFrameBufferWithStencil = graphicsDevice->CreateFrameBuffer(afterLightingFrameBufferDesc); diff --git a/src/engine/renderer/target/RenderTarget.h b/src/engine/renderer/target/RenderTarget.h index 330968493..c679760d1 100644 --- a/src/engine/renderer/target/RenderTarget.h +++ b/src/engine/renderer/target/RenderTarget.h @@ -2,6 +2,7 @@ #include "System.h" #include "texture/Texture2D.h" +#include "texture/Texture2DArray.h" #include "graphics/RenderPass.h" #include "graphics/Framebuffer.h" @@ -21,26 +22,28 @@ namespace Atlas::Renderer { VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); if (useDepthFormat) { depthTexture = std::make_shared(resolution.x, resolution.y, VK_FORMAT_D32_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); } else { depthTexture = std::make_shared(resolution.x, resolution.y, VK_FORMAT_R32_SFLOAT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); } normalTexture = std::make_shared(resolution.x, resolution.y, - VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); geometryNormalTexture = std::make_shared(resolution.x, resolution.y, - VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); roughnessMetallicAoTexture = std::make_shared(resolution.x, resolution.y, - VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); + emissiveTexture = std::make_shared(resolution.x, resolution.y, + VK_FORMAT_R8G8B8A8_UNORM, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); offsetTexture = std::make_shared(resolution.x, resolution.y, VK_FORMAT_R8_SINT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); materialIdxTexture = std::make_shared(resolution.x, resolution.y, VK_FORMAT_R16_UINT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); stencilTexture = std::make_shared(resolution.x, resolution.y, VK_FORMAT_R8_UINT, - Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest); + Texture::Wrapping::ClampToEdge, Texture::Filtering::Nearest, false, true); velocityTexture = std::make_shared(resolution.x, resolution.y, - VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear); + VK_FORMAT_R16G16_SFLOAT, Texture::Wrapping::ClampToEdge, Texture::Filtering::Linear, false, true); } void Resize(ivec2 resolution) { @@ -49,6 +52,7 @@ namespace Atlas::Renderer { normalTexture->Resize(resolution.x, resolution.y); geometryNormalTexture->Resize(resolution.x, resolution.y); roughnessMetallicAoTexture->Resize(resolution.x, resolution.y); + emissiveTexture->Resize(resolution.x, resolution.y); offsetTexture->Resize(resolution.x, resolution.y); materialIdxTexture->Resize(resolution.x, resolution.y); stencilTexture->Resize(resolution.x, resolution.y); @@ -60,6 +64,7 @@ namespace Atlas::Renderer { Ref normalTexture = nullptr; Ref geometryNormalTexture = nullptr; Ref roughnessMetallicAoTexture = nullptr; + Ref emissiveTexture = nullptr; Ref offsetTexture = nullptr; Ref materialIdxTexture = nullptr; Ref stencilTexture = nullptr; @@ -182,6 +187,10 @@ namespace Atlas::Renderer { float GetScalingFactor() const; + void UseForPathTracing(bool use); + + bool IsUsedForPathTracing() const; + Ref gBufferRenderPass; Ref gBufferFrameBuffer; @@ -196,6 +205,7 @@ namespace Atlas::Renderer { Ref outputFrameBuffer; Texture::Texture2D outputTexture; + Texture::Texture2D bloomTexture; Texture::Texture2D giTexture; Texture::Texture2D swapGiTexture; @@ -229,11 +239,19 @@ namespace Atlas::Renderer { Texture::Texture2D reflectionMomentsTexture; Texture::Texture2D historyReflectionMomentsTexture; + Texture::Texture2D radianceTexture; + Texture::Texture2D historyRadianceTexture; + Texture::Texture2DArray frameAccumTexture; + Texture::Texture2D lightingTexture; Texture::Texture2D reactiveMaskTexture; Texture::Texture2D hdrTexture; + int32_t sampleCount = 0; + private: + void CreateRenderPasses(); + void CreateFrameBuffers(); Texture::Texture2D historyTexture; @@ -259,6 +277,7 @@ namespace Atlas::Renderer { bool swap = false; bool hasHistory = false; + bool useForPathTracing = false; }; diff --git a/src/engine/resource/Resource.h b/src/engine/resource/Resource.h index 6fdae3631..2a226af72 100644 --- a/src/engine/resource/Resource.h +++ b/src/engine/resource/Resource.h @@ -136,7 +136,7 @@ namespace Atlas { Ref data; std::atomic_bool isLoaded = false; - JobGroup jobGroup; + JobGroup jobGroup{ JobPriority::Low }; std::shared_future future; int32_t framesToDeletion = RESOURCE_RETENTION_FRAME_COUNT; @@ -148,7 +148,21 @@ namespace Atlas { public: ResourceHandle() = default; - ResourceHandle(Ref>& resource) : resource(resource) {} + ResourceHandle(const Ref& data) : isGenerated(true) { + + Hash hash; + HashCombine(hash, static_cast(data.get())); + + auto path = "generated/" + std::to_string(hash); + resource = CreateRef>(path, ResourceOrigin::User, data); + + } + + ResourceHandle(const Ref>& resource) : resource(resource) {} + + inline bool IsGenerated() const { + return isGenerated; + } inline bool IsValid() const { return resource != nullptr; @@ -205,6 +219,7 @@ namespace Atlas { private: Ref> resource = nullptr; + bool isGenerated = false; }; diff --git a/src/engine/resource/ResourceManager.h b/src/engine/resource/ResourceManager.h index 0bb1e453f..9666301ab 100644 --- a/src/engine/resource/ResourceManager.h +++ b/src/engine/resource/ResourceManager.h @@ -26,7 +26,7 @@ namespace Atlas { static ResourceHandle GetResource(const std::string& path) { CheckInitialization(); - + auto relativePath = GetAssetDirectoryPath(path); std::lock_guard lock(mutex); if (resources.contains(relativePath)) { @@ -50,7 +50,7 @@ namespace Atlas { static ResourceHandle GetOrLoadResource(const std::string& path, ResourceOrigin origin, Args&&... args) { static_assert(std::is_constructible() || - std::is_constructible(), + std::is_constructible(), "Resource class needs to implement constructor with provided argument type"); CheckInitialization(); @@ -72,7 +72,7 @@ namespace Atlas { template static ResourceHandle GetOrLoadResourceWithLoader(const std::string& path, - Ref (*loaderFunction)(const std::string&, Args...), Args... args) { + Ref(*loaderFunction)(const std::string&, Args...), Args... args) { return GetOrLoadResourceWithLoader(path, System, std::function(loaderFunction), std::forward(args)...); @@ -80,7 +80,7 @@ namespace Atlas { template static ResourceHandle GetOrLoadResourceWithLoader(const std::string& path, ResourceOrigin origin, - Ref (*loaderFunction)(const std::string&, Args...), Args... args) { + Ref(*loaderFunction)(const std::string&, Args...), Args... args) { return GetOrLoadResourceWithLoader(path, origin, std::function(loaderFunction), std::forward(args)...); @@ -137,7 +137,7 @@ namespace Atlas { auto resource = GetResourceInternal(relativePath); JobSystem::Execute(resource->jobGroup, [resource, args...](JobData&) { resource->Load(args...); - }); + }); NotifyAllSubscribers(ResourceTopic::ResourceCreate, resource); handle = ResourceHandle(resource); } @@ -148,7 +148,7 @@ namespace Atlas { template static ResourceHandle GetOrLoadResourceWithLoaderAsync(const std::string& path, - Ref (*loaderFunction)(const std::string&, Args...), Args... args) { + Ref(*loaderFunction)(const std::string&, Args...), Args... args) { return GetOrLoadResourceWithLoaderAsync(path, System, std::function(loaderFunction), std::forward(args)...); @@ -156,7 +156,7 @@ namespace Atlas { template static ResourceHandle GetOrLoadResourceWithLoaderAsync(const std::string& path, ResourceOrigin origin, - Ref (*loaderFunction)(const std::string&, Args...), Args... args) { + Ref(*loaderFunction)(const std::string&, Args...), Args... args) { return GetOrLoadResourceWithLoaderAsync(path, origin, std::function(loaderFunction), std::forward(args)...); @@ -182,7 +182,7 @@ namespace Atlas { auto resource = GetResourceInternal(relativePath); JobSystem::Execute(resource->jobGroup, [resource, loaderFunction, args...](JobData&) { resource->LoadWithExternalLoader(loaderFunction, args...); - }); + }); NotifyAllSubscribers(ResourceTopic::ResourceCreate, resource); handle = ResourceHandle(resource); } @@ -206,7 +206,7 @@ namespace Atlas { { std::lock_guard lock(mutex); if (resources.contains(relativePath)) { - auto &resource = resources[relativePath]; + auto& resource = resources[relativePath]; resource->framesToDeletion = RESOURCE_RETENTION_FRAME_COUNT; alreadyExisted = true; return ResourceHandle(resource); @@ -284,7 +284,7 @@ namespace Atlas { subscribers->emplace_back(ResourceSubscriber{ .ID = subscriberCount, - .function = function + .function = function }); return subscriberCount++; @@ -300,7 +300,7 @@ namespace Atlas { auto item = std::find_if(subscribers->begin(), subscribers->end(), [&](ResourceSubscriber subscriber) { return subscriber.ID == subscriptionID; - }); + }); if (item != subscribers->end()) { subscribers->erase(item); @@ -321,6 +321,8 @@ namespace Atlas { static std::atomic_int subscriberCount; + static JobGroup deallocationJobs; + static inline void CheckInitialization() { if (isInitialized) return; @@ -363,8 +365,9 @@ namespace Atlas { std::lock_guard lock(mutex); - for (auto it = resources.begin(); it != resources.end();) { - auto& resource = it->second; + // First loop to count and update the resources + size_t resourceUnloadCount = 0; + for (auto& [_, resource] : resources) { // Just one reference (the resource manager), so start countdown for future deletion // If resource is accessed in certain time frame we reset the counter if (resource.use_count() == 1 && !resource->permanent) { @@ -374,22 +377,43 @@ namespace Atlas { resource->framesToDeletion = RESOURCE_RETENTION_FRAME_COUNT; } + if (resource.use_count() == 1 && resource->framesToDeletion == 0) { + resourceUnloadCount++; + } + } + + std::vector>> resourcesToUnload; + resourcesToUnload.reserve(resourceUnloadCount); + + // Second loop for actual removal + for (auto it = resources.begin(); it != resources.end();) { + auto& resource = it->second; + // Delete if all conditions are met if (resource.use_count() == 1 && resource->framesToDeletion == 0) { NotifyAllSubscribers(ResourceTopic::ResourceDestroy, resource); AE_ASSERT(resource.use_count() == 1 && "Subscribers shouldn't claim ownership of to be deleted resources"); - resource->Unload(); + resourcesToUnload.push_back(resource); it = resources.erase(it); } else { ++it; } } + + // This is kind of fucked up, but we can avoid unwanted spikes when a bunch of data is suddenly needed anymore + // Also we shouldn't unload them multi-threaded, so not one job per resource. + if (!resourcesToUnload.empty()) { + JobSystem::Execute(deallocationJobs, [resourcesToUnload = std::move(resourcesToUnload)](JobData&) { + for (auto& resource : resourcesToUnload) + resource->Unload(); + }); + } } static void ShutdownHandler() { - + for (const auto& [_, resource] : resources) { if (!resource->future.valid()) continue; @@ -449,4 +473,7 @@ namespace Atlas { template std::atomic_int ResourceManager::subscriberCount = 0; + template + JobGroup ResourceManager::deallocationJobs{ JobPriority::Low }; + } \ No newline at end of file diff --git a/src/engine/scene/Entity.h b/src/engine/scene/Entity.h index 5201cc27b..366157489 100644 --- a/src/engine/scene/Entity.h +++ b/src/engine/scene/Entity.h @@ -81,6 +81,18 @@ namespace Atlas { } + inline uint64_t GetID() const { + + return uint64_t(entity); + + } + + inline uint32_t GetVersion() const { + + return entityManager->Version(entity); + + } + inline Scene* GetScene() const { return static_cast(entityManager->userData); diff --git a/src/engine/scene/Scene.cpp b/src/engine/scene/Scene.cpp index a282e5c83..ae2744ed1 100644 --- a/src/engine/scene/Scene.cpp +++ b/src/engine/scene/Scene.cpp @@ -97,14 +97,10 @@ namespace Atlas { // Do cleanup first such that we work with valid data CleanupUnusedResources(); -#ifdef AE_BINDLESS - UpdateBindlessIndexMaps(); -#endif - // Update scripting components (but only after the first timestep when everything else is settled) if (!firstTimestep) { // Work with a copy here - auto luaScriptComponents = entityManager.GetComponents(); + auto luaScriptComponents = entityManager.GetAll(); for (auto& luaScriptComponent : luaScriptComponents) { luaScriptComponent.Update(luaScriptManager, deltaTime); @@ -114,20 +110,47 @@ namespace Atlas { } } + // Do these updates before anything else (doesn't have newest camera position, but that doesn't matter too much + if (mainCameraEntity.IsValid()) { + auto& mainCamera = mainCameraEntity.GetComponent(); + + if (terrain.IsLoaded()) + terrain->Update(mainCamera); + + if (ocean) { + ocean->Update(mainCamera, deltaTime); + } + } + + // Can only update after scripts were run +#ifdef AE_BINDLESS + renderState.UpdateBlasBindlessData(); + renderState.UpdateTextureBindlessData(); + renderState.UpdateOtherTextureBindlessData(); +#endif + renderState.PrepareMaterials(); + TransformComponent rootTransform = {}; - auto hierarchyTransformSubset = entityManager.GetSubset(); + auto hierarchySubset = entityManager.GetSubset(); // Update hierarchy and their entities - for (auto entity : hierarchyTransformSubset) { - const auto& [hierarchyComponent, transformComponent] = hierarchyTransformSubset.Get(entity); + for (auto entity : hierarchySubset) { + auto& hierarchyComponent = hierarchySubset.Get(entity); if (hierarchyComponent.root) { - auto parentChanged = transformComponent.changed; - transformComponent.Update(rootTransform, false); - hierarchyComponent.Update(transformComponent, parentChanged); + auto transformComponent = entityManager.TryGet(entity); + if (transformComponent) { + auto parentChanged = transformComponent->changed; + transformComponent->Update(rootTransform, false); + hierarchyComponent.Update(*transformComponent, parentChanged); + } + else { + hierarchyComponent.Update(rootTransform, false); + } } } + auto hierarchyTransformSubset = entityManager.GetSubset(); // Update hierarchy components which are not part of a root hierarchy that also has a transform component // This might be the case if there is an entity that has just a hierarchy without a tranform for, e.g. grouping entities for (auto entity : hierarchyTransformSubset) { @@ -173,6 +196,11 @@ namespace Atlas { } playerComponent.Update(deltaTime); + + auto hierarchyComponent = entityManager.TryGet(entity); + if (hierarchyComponent) { + hierarchyComponent->Update(transformComponent, true); + } } auto rigidBodySubset = entityManager.GetSubset(); @@ -186,13 +214,14 @@ namespace Atlas { } // Apply update here (transform overwrite everything else in physics simulation for now) - if (transformComponent.changed && rigidBodyComponent.IsValid()) { + if (transformComponent.changed && rigidBodyComponent.IsValid()) { rigidBodyComponent.SetMatrix(transformComponent.globalMatrix); } } // This part only needs to be executed if the simulation is running if (!physicsWorld->pauseSimulation) { + physicsWorld->Update(deltaTime); for (auto entity : rigidBodySubset) { @@ -237,6 +266,40 @@ namespace Atlas { transformComponent.globalMatrix = playerComponent.GetMatrix(); transformComponent.inverseGlobalMatrix = glm::inverse(transformComponent.globalMatrix); } + + // Now we need to update all the hiearchies + auto rigidBodyHierarchySubset = entityManager.GetSubset(); + for (auto entity : rigidBodyHierarchySubset) { + auto& hierarchyComponent = entityManager.Get(entity); + + hierarchyComponent.updated = false; + } + + for (auto entity : rigidBodyHierarchySubset) { + const auto& [rigidBodyComponent, hierarchyComponent, transformComponent] = rigidBodyHierarchySubset.Get(entity); + + if (!hierarchyComponent.updated) { + auto parentChanged = transformComponent.changed; + hierarchyComponent.Update(transformComponent, parentChanged); + } + } + + // Now we need to update all the hiearchies + auto playerHierarchySubset = entityManager.GetSubset(); + for (auto entity : playerHierarchySubset) { + auto& hierarchyComponent = entityManager.Get(entity); + + hierarchyComponent.updated = false; + } + + for (auto entity : playerHierarchySubset) { + const auto& [playerComponent, hierarchyComponent, transformComponent] = playerHierarchySubset.Get(entity); + + if (!hierarchyComponent.updated) { + auto parentChanged = transformComponent.changed; + hierarchyComponent.Update(transformComponent, parentChanged); + } + } } } @@ -277,14 +340,19 @@ namespace Atlas { for (auto entity : lightSubset) { auto& lightComponent = lightSubset.Get(entity); + if (lightComponent.isMain && lightComponent.type == LightType::DirectionalLight) + mainLightEntity = Entity(entity, &entityManager); + auto transformComponent = entityManager.TryGet(entity); lightComponent.Update(transformComponent); } + renderState.mainCameraSignal.Reset(); + #ifdef AE_BINDLESS auto rayTracingSubset = GetSubset(); - JobSystem::Execute(rayTracingWorldUpdateJob, [this, rayTracingSubset](JobData&) { + JobSystem::Execute(renderState.rayTracingWorldUpdateJob, [this, rayTracingSubset](JobData&) { // Need to wait before updating graphic resources Graphics::GraphicsDevice::DefaultDevice->WaitForPreviousFrameSubmission(); if (rayTracingWorld) { @@ -297,7 +365,6 @@ namespace Atlas { #endif // We also need to reset the hierarchy components as well - auto hierarchySubset = entityManager.GetSubset(); for (auto entity : hierarchySubset) { auto& hierarchyComponent = hierarchySubset.Get(entity); @@ -345,7 +412,9 @@ namespace Atlas { if (camera.isMain && !mainCameraEntity.IsValid()) { mainCameraEntity = { entity, &entityManager }; } - } + } + + renderState.mainCameraSignal.Release(); AE_ASSERT(mainCameraEntity.IsValid() && "Couldn't find main camera component"); @@ -376,13 +445,8 @@ namespace Atlas { lightComponent.Update(mainCamera); } - if (terrain) { - terrain->Update(mainCamera); - } - - if (ocean) { - ocean->Update(mainCamera, deltaTime); - } + renderState.FillRenderList(); + renderState.CullAndSortLights(); } @@ -404,8 +468,9 @@ namespace Atlas { std::vector> materials; - if (terrain) { + if (terrain.IsLoaded()) { auto terrainMaterials = terrain->storage.GetMaterials(); + materials.reserve(terrainMaterials.size()); for (const auto& material : terrainMaterials) { if (!material) @@ -415,12 +480,14 @@ namespace Atlas { } } - + auto meshes = GetMeshes(); if (clutter) { auto vegMeshes = clutter->GetMeshes(); meshes.insert(meshes.end(), vegMeshes.begin(), vegMeshes.end()); } + + materials.reserve(materials.size() + meshes.size()); for (const auto& mesh : meshes) { if (!mesh.IsLoaded()) @@ -449,6 +516,18 @@ namespace Atlas { } + LightComponent& Scene::GetMainLight() { + + return mainLightEntity.GetComponent(); + + } + + bool Scene::HasMainLight() const { + + return mainLightEntity.IsValid() && mainLightEntity.HasComponent(); + + } + Volume::RayResult Scene::CastRay(Volume::Ray& ray, SceneQueryComponents queryComponents) { Volume::RayResult result; @@ -549,6 +628,8 @@ namespace Atlas { } void Scene::ClearRTStructures() { + + WaitForAsyncWorkCompletion(); rtDataValid = false; if (rayTracingWorld != nullptr) @@ -568,9 +649,7 @@ namespace Atlas { void Scene::WaitForAsyncWorkCompletion() { - JobSystem::Wait(bindlessMeshMapUpdateJob); - JobSystem::Wait(bindlessTextureMapUpdateJob); - JobSystem::Wait(rayTracingWorldUpdateJob); + renderState.WaitForAsyncWorkCompletion(); } @@ -618,81 +697,6 @@ namespace Atlas { } - void Scene::UpdateBindlessIndexMaps() { - - JobSystem::Execute(bindlessMeshMapUpdateJob, [&](JobData&) { - auto meshes = GetMeshes(); - meshIdToBindlessIdx.clear(); - - uint32_t bufferIdx = 0; - for (const auto& mesh : meshes) { - if (!mesh.IsLoaded()) continue; - - // Not all meshes might have a bvh - if (!mesh->IsBVHBuilt()) - continue; - - meshIdToBindlessIdx[mesh.GetID()] = bufferIdx++; - } - }); - - JobSystem::Execute(bindlessTextureMapUpdateJob, [&](JobData&) { - auto meshes = GetMeshes(); - textureToBindlessIdx.clear(); - - std::set> materials; - std::set> textures; - - uint32_t textureIdx = 0; - for (const auto& mesh : meshes) { - if (!mesh.IsLoaded()) continue; - - for (auto& material : mesh->data.materials) - if (material.IsLoaded()) - materials.insert(material.Get()); - } - - for (const auto& material : materials) { - if (material->HasBaseColorMap()) - textures.insert(material->baseColorMap.Get()); - if (material->HasOpacityMap()) - textures.insert(material->opacityMap.Get()); - if (material->HasNormalMap()) - textures.insert(material->normalMap.Get()); - if (material->HasRoughnessMap()) - textures.insert(material->roughnessMap.Get()); - if (material->HasMetalnessMap()) - textures.insert(material->metalnessMap.Get()); - if (material->HasAoMap()) - textures.insert(material->aoMap.Get()); - if (material->HasDisplacementMap()) - textures.insert(material->displacementMap.Get()); - } - - for (const auto& texture : textures) { - - textureToBindlessIdx[texture] = textureIdx++; - - } - }); - - auto lightSubset = entityManager.GetSubset(); - for (auto entity : lightSubset) { - const auto& lightComponent = lightSubset.Get(entity); - - if (!lightComponent.shadow) - continue; - - if (lightComponent.shadow->useCubemap) { - - } - else { - - } - } - - } - void Scene::CleanupUnusedResources() { CleanupUnusedResources(registeredMeshes); @@ -836,8 +840,10 @@ namespace Atlas { auto otherComp = srcEntity.GetComponent(); auto& comp = dstEntity.AddComponent(otherComp); // Need to create a new shadow, since right now the memory is shared between components - comp.shadow = CreateRef(*otherComp.shadow); - comp.shadow->SetResolution(comp.shadow->resolution); + if (otherComp.shadow) { + comp.shadow = CreateRef(*otherComp.shadow); + comp.shadow->SetResolution(comp.shadow->resolution); + } comp.isMain = false; } if (srcEntity.HasComponent()) { @@ -908,4 +914,4 @@ namespace Atlas { } -} \ No newline at end of file +} diff --git a/src/engine/scene/Scene.h b/src/engine/scene/Scene.h index 579f17851..9a5cc0868 100644 --- a/src/engine/scene/Scene.h +++ b/src/engine/scene/Scene.h @@ -21,6 +21,7 @@ #include "../mesh/Mesh.h" #include "SceneIterator.h" +#include "SceneRenderState.h" #include "SpacePartitioning.h" #include "Subset.h" #include "Wind.h" @@ -58,12 +59,13 @@ namespace Atlas { }; public: - Scene() : SpacePartitioning(this, vec3(-2048.0f), vec3(2048.0f), 5) { RegisterSubscribers(); } + Scene() : SpacePartitioning(this, vec3(-2048.0f), vec3(2048.0f), 5), renderState(this) + { RegisterSubscribers(); } Scene(const Scene& that) = delete; explicit Scene(const std::string& name) : SpacePartitioning(this, vec3(-2048.0f), vec3(2048.0f), 5), - name(name) { RegisterSubscribers(); } + name(name), renderState(this) { RegisterSubscribers(); } explicit Scene(const std::string& name, vec3 min, vec3 max, int32_t depth = 5) - : SpacePartitioning(this, min, max, depth), name(name) { RegisterSubscribers(); } + : SpacePartitioning(this, min, max, depth), name(name), renderState(this) { RegisterSubscribers(); } ~Scene(); @@ -85,6 +87,9 @@ namespace Atlas { template Subset GetSubset(); + template + size_t GetComponentCount(); + std::unordered_map Merge(const Ref& other); void Timestep(float deltaTime); @@ -99,6 +104,10 @@ namespace Atlas { bool HasMainCamera() const; + LightComponent& GetMainLight(); + + bool HasMainLight() const; + Volume::RayResult CastRay(Volume::Ray& ray, SceneQueryComponents queryComponents = SceneQueryComponentBits::AllComponentsBit); @@ -125,12 +134,13 @@ namespace Atlas { static Ref Restore(const std::vector& serialized); std::string name; - - Ref ocean = nullptr; - Ref terrain = nullptr; + Ref clutter = nullptr; Ref physicsWorld = nullptr; - Ref rayTracingWorld = nullptr; + Ref rayTracingWorld = nullptr; + + Ref ocean = nullptr; + ResourceHandle terrain; Wind wind; Lighting::Sky sky; @@ -143,12 +153,9 @@ namespace Atlas { Ref sss = nullptr; PostProcessing::PostProcessing postProcessing; - std::unordered_map, uint32_t> textureToBindlessIdx; - std::unordered_map meshIdToBindlessIdx; + SceneRenderState renderState; private: - void UpdateBindlessIndexMaps(); - Entity ToSceneEntity(ECS::Entity entity); void RegisterSubscribers(); @@ -173,6 +180,7 @@ namespace Atlas { std::map> registeredAudios; Entity mainCameraEntity; + Entity mainLightEntity; float deltaTime = 1.0f; bool firstTimestep = true; @@ -182,12 +190,9 @@ namespace Atlas { Scripting::LuaScriptManager luaScriptManager = Scripting::LuaScriptManager(this); - JobGroup rayTracingWorldUpdateJob { JobPriority::High }; - JobGroup bindlessMeshMapUpdateJob { JobPriority::High }; - JobGroup bindlessTextureMapUpdateJob { JobPriority::High }; - friend Entity; friend SpacePartitioning; + friend SceneRenderState; friend RayTracing::RayTracingWorld; friend HierarchyComponent; friend MeshComponent; @@ -220,6 +225,13 @@ namespace Atlas { } + template + size_t Scene::GetComponentCount() { + + return entityManager.GetCount(); + + } + template void Scene::RegisterResource(std::map>& resources, ResourceHandle resource) { diff --git a/src/engine/scene/SceneRenderState.cpp b/src/engine/scene/SceneRenderState.cpp new file mode 100644 index 000000000..9e3a16721 --- /dev/null +++ b/src/engine/scene/SceneRenderState.cpp @@ -0,0 +1,606 @@ +#include "SceneRenderState.h" +#include "Scene.h" +#include "renderer/helper/CommonStructures.h" + +#include "common/ColorConverter.h" +#include "common/Packing.h" + +#include "common/ColorConverter.h" +#include + +// Move most of the things in the main renderer, like the bindless update or the materials to here +// Also move rendering related map updates from the scene to here +namespace Atlas::Scene { + + SceneRenderState::SceneRenderState(Scene* scene) : scene(scene) { + + materialBuffer = Buffer::Buffer(Buffer::BufferUsageBits::StorageBufferBit | + Buffer::BufferUsageBits::HostAccessBit | Buffer::BufferUsageBits::MultiBufferedBit, sizeof(Renderer::PackedMaterial)); + + } + + SceneRenderState::~SceneRenderState() { + + WaitForAsyncWorkCompletion(); + + } + + void SceneRenderState::PrepareMaterials() { + + JobSystem::Wait(materialUpdateJob); + + JobSystem::Execute(materialUpdateJob, [&](JobData&) { + auto sceneMaterials = scene->GetMaterials(); + + // For debugging purpose + if (scene->irradianceVolume && scene->irradianceVolume->debug) { + const auto& internalVolume = scene->irradianceVolume->internal; + sceneMaterials.push_back(internalVolume.probeDebugMaterial); + sceneMaterials.push_back(internalVolume.probeDebugActiveMaterial); + sceneMaterials.push_back(internalVolume.probeDebugInactiveMaterial); + sceneMaterials.push_back(internalVolume.probeDebugOffsetMaterial); + } + + uint16_t idx = 0; + + if (!materials.empty()) { + materials.clear(); + materialMap.clear(); + } + + for (auto material : sceneMaterials) { + // Might happen due to the scene giving back materials on a mesh basis + if (materialMap.contains(material.get())) + continue; + + Renderer::PackedMaterial packed; + + packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->baseColor), 0.0f)); + packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->emissiveColor), 0.0f)); + packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(material->transmissiveColor), 0.0f)); + + packed.emissiveIntensityTiling = glm::packHalf2x16(vec2(material->emissiveIntensity, material->tiling)); + + vec4 data0, data1, data2; + + data0.x = material->opacity; + data0.y = material->roughness; + data0.z = material->metalness; + + data1.x = material->ao; + data1.y = material->HasNormalMap() ? material->normalScale : 0.0f; + data1.z = material->HasDisplacementMap() ? material->displacementScale : 0.0f; + + data2.x = material->reflectance; + // Note used + data2.y = 0.0f; + data2.z = 0.0f; + + packed.data0 = Common::Packing::PackUnsignedVector3x10_1x2(data0); + packed.data1 = Common::Packing::PackUnsignedVector3x10_1x2(data1); + packed.data2 = Common::Packing::PackUnsignedVector3x10_1x2(data2); + + packed.features = 0; + + packed.features |= material->HasBaseColorMap() ? Renderer::MaterialFeatures::FEATURE_BASE_COLOR_MAP : 0; + packed.features |= material->HasOpacityMap() ? Renderer::MaterialFeatures::FEATURE_OPACITY_MAP : 0; + packed.features |= material->HasNormalMap() ? Renderer::MaterialFeatures::FEATURE_NORMAL_MAP : 0; + packed.features |= material->HasRoughnessMap() ? Renderer::MaterialFeatures::FEATURE_ROUGHNESS_MAP : 0; + packed.features |= material->HasMetalnessMap() ? Renderer::MaterialFeatures::FEATURE_METALNESS_MAP : 0; + packed.features |= material->HasAoMap() ? Renderer::MaterialFeatures::FEATURE_AO_MAP : 0; + packed.features |= material->HasEmissiveMap() ? Renderer::MaterialFeatures::FEATURE_EMISSIVE_MAP : 0; + packed.features |= glm::length(material->transmissiveColor) > 0.0f ? Renderer::MaterialFeatures::FEATURE_TRANSMISSION : 0; + packed.features |= material->vertexColors ? Renderer::MaterialFeatures::FEATURE_VERTEX_COLORS : 0; + + materials.push_back(packed); + + materialMap[material.get()] = idx++; + } + + auto meshes = scene->GetMeshes(); + + for (auto mesh : meshes) { + if (!mesh.IsLoaded()) + continue; + + auto impostor = mesh->impostor; + + if (!impostor) + continue; + + Renderer::PackedMaterial packed; + + packed.baseColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(1.0f)); + packed.emissiveColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(0.0f)); + packed.transmissionColor = Common::Packing::PackUnsignedVector3x10_1x2(vec4(Common::ColorConverter::ConvertSRGBToLinear(impostor->transmissiveColor), 1.0f)); + + vec4 data0, data1, data2; + + data0.x = 1.0f; + data0.y = 1.0f; + data0.z = 1.0f; + + data1.x = 1.0f; + data1.y = 0.0f; + data1.z = 0.0f; + + data2.x = 0.5f; + // Note used + data2.y = 0.0f; + data2.z = 0.0f; + + packed.data0 = Common::Packing::PackUnsignedVector3x10_1x2(data0); + packed.data1 = Common::Packing::PackUnsignedVector3x10_1x2(data1); + packed.data2 = Common::Packing::PackUnsignedVector3x10_1x2(data2); + + packed.features = 0; + + packed.features |= Renderer::MaterialFeatures::FEATURE_BASE_COLOR_MAP | + Renderer::MaterialFeatures::FEATURE_ROUGHNESS_MAP | + Renderer::MaterialFeatures::FEATURE_METALNESS_MAP | + Renderer::MaterialFeatures::FEATURE_AO_MAP; + packed.features |= glm::length(impostor->transmissiveColor) > 0.0f ? + Renderer::MaterialFeatures::FEATURE_TRANSMISSION : 0; + + materials.push_back(packed); + + materialMap[impostor.get()] = idx++; + } + + if (materials.size() > materialBuffer.GetElementCount()) { + materialBuffer.SetSize(materials.size()); + } + + if (!materials.empty()) + materialBuffer.SetData(materials.data(), 0, materials.size()); + }); + + } + + void SceneRenderState::UpdateBlasBindlessData() { + + terrainLeafNodes.clear(); + if (scene->terrain.IsLoaded()) { + auto& leafNodes = scene->terrain->leafList; + terrainLeafNodes.reserve(leafNodes.size()); + std::copy(leafNodes.begin(), leafNodes.end(), std::back_inserter(terrainLeafNodes)); + } + + auto bindlessBlasBuffersUpdate = [&](JobData&) { + JobSystem::Wait(bindlessBlasMapUpdateJob); + + if (blasBuffers.size() != blasToBindlessIdx.size()) { + blasBuffers.resize(blasToBindlessIdx.size()); + triangleBuffers.resize(blasToBindlessIdx.size()); + bvhTriangleBuffers.resize(blasToBindlessIdx.size()); + triangleOffsetBuffers.resize(blasToBindlessIdx.size()); + } + + for (const auto& [blas, idx] : blasToBindlessIdx) { + auto blasBuffer = blas->blasNodeBuffer.Get(); + auto triangleBuffer = blas->triangleBuffer.Get(); + auto bvhTriangleBuffer = blas->bvhTriangleBuffer.Get(); + auto triangleOffsetBuffer = blas->triangleOffsetBuffer.Get(); + + AE_ASSERT(triangleBuffer != nullptr); + + blasBuffers[idx] = blasBuffer; + triangleBuffers[idx] = triangleBuffer; + bvhTriangleBuffers[idx] = bvhTriangleBuffer; + triangleOffsetBuffers[idx] = triangleOffsetBuffer; + } + }; + + auto bindlessBlasMapUpdate = [&, bindlessBlasBuffersUpdate](JobData&) { + auto meshes = scene->GetMeshes(); + + blasToBindlessIdx.clear(); + + uint32_t bufferIdx = 0; + for (const auto& mesh : meshes) { + if (!mesh.IsLoaded()) continue; + + // Not all meshes might have a bvh and not all blases will be built in frame, so skip them if they are not ready + if (!mesh->IsBVHBuilt() || mesh->IsBVHBuilt() && mesh->blas->blas && + !mesh->blas->blas->isDynamic && !mesh->blas->blas->isBuilt) + continue; + + blasToBindlessIdx[mesh->blas] = bufferIdx++; + } + + for (const auto leafNode : terrainLeafNodes) { + auto leafCell = leafNode->cell; + if (!leafCell || !leafCell->IsLoaded() || !leafCell->blas || !leafCell->blas->IsBuilt()) + continue; + + blasToBindlessIdx[leafCell->blas] = bufferIdx++; + } + }; + + JobSystem::Wait(bindlessBlasMapUpdateJob); + JobSystem::Wait(prepareBindlessBlasesJob); + + JobSystem::Execute(bindlessBlasMapUpdateJob, bindlessBlasMapUpdate); + JobSystem::Execute(prepareBindlessBlasesJob, bindlessBlasBuffersUpdate); + + } + + void SceneRenderState::UpdateTextureBindlessData() { + + auto bindlessTextureMapUpdate = [&](JobData&) { + auto meshes = scene->GetMeshes(); + textureToBindlessIdx.clear(); + textures.clear(); + + std::set> materialSet; + std::set> textureSet; + + uint32_t textureIdx = 0; + for (const auto& mesh : meshes) { + if (!mesh.IsLoaded()) continue; + + for (auto& material : mesh->data.materials) + if (material.IsLoaded()) + materialSet.insert(material.Get()); + } + + for (const auto leafNode : terrainLeafNodes) { + auto leafCell = leafNode->cell; + if (!leafCell || !leafCell->IsLoaded() || !leafCell->blas || !leafCell->blas->IsBuilt()) + continue; + + textureSet.insert(leafCell->normalMap); + + for (auto& material : leafCell->blas->materials) + if (material.IsLoaded()) + materialSet.insert(material.Get()); + } + + for (const auto& material : materialSet) { + if (material->HasBaseColorMap()) + textureSet.insert(material->baseColorMap.Get()); + if (material->HasOpacityMap()) + textureSet.insert(material->opacityMap.Get()); + if (material->HasNormalMap()) + textureSet.insert(material->normalMap.Get()); + if (material->HasRoughnessMap()) + textureSet.insert(material->roughnessMap.Get()); + if (material->HasMetalnessMap()) + textureSet.insert(material->metalnessMap.Get()); + if (material->HasAoMap()) + textureSet.insert(material->aoMap.Get()); + if (material->HasDisplacementMap()) + textureSet.insert(material->displacementMap.Get()); + if (material->HasEmissiveMap()) + textureSet.insert(material->emissiveMap.Get()); + } + + for (const auto& texture : textureSet) { + + textureToBindlessIdx[texture] = textureIdx++; + textures.push_back(texture->image); + + } + }; + + JobSystem::Wait(bindlessTextureMapUpdateJob); + + JobSystem::Execute(bindlessTextureMapUpdateJob, bindlessTextureMapUpdate); + + } + + void SceneRenderState::UpdateOtherTextureBindlessData() { + + auto lightSubset = scene->GetSubset(); + JobSystem::Wait(bindlessOtherTextureMapUpdateJob); + + JobSystem::Execute(bindlessOtherTextureMapUpdateJob, [&, lightSubset](JobData&) { + + cubemapToBindlessIdx.clear(); + textureArrayToBindlessIdx.clear(); + + cubemaps.clear(); + textureArrays.clear(); + + uint32_t textureArrayIdx = 0; + uint32_t cubemapIdx = 0; + for (auto entity : lightSubset) { + const auto& lightComponent = entity.GetComponent(); + + if (!lightComponent.shadow) + continue; + + if (lightComponent.shadow->useCubemap) { + cubemapToBindlessIdx[lightComponent.shadow->cubemap] = cubemapIdx++; + cubemaps.push_back(lightComponent.shadow->cubemap->image); + } + else { + textureArrayToBindlessIdx[lightComponent.shadow->maps] = textureArrayIdx++; + textureArrays.push_back(lightComponent.shadow->maps->image); + } + } + }); + + } + + void SceneRenderState::FillRenderList() { + + if (!scene->HasMainCamera()) + return; + + JobSystem::Wait(fillRenderListJob); + + auto lightSubset = scene->GetSubset(); + auto camera = scene->GetMainCamera(); + + JobSystem::Execute(fillRenderListJob, [&, lightSubset, camera](JobData&) { + + auto meshes = scene->GetMeshes(); + renderList.NewFrame(scene); + + JobGroup group{ JobPriority::High }; + for (auto& lightEntity : lightSubset) { + + auto& light = lightEntity.GetComponent(); + if (!light.shadow || !light.shadow->update) + continue; + + auto& shadow = light.shadow; + + auto componentCount = shadow->longRange ? + shadow->viewCount - 1 : shadow->viewCount; + + JobSystem::ExecuteMultiple(group, componentCount, + [&, shadow = shadow, lightEntity = lightEntity](JobData& data) { + auto component = &shadow->views[data.idx]; + auto frustum = Volume::Frustum(component->frustumMatrix); + + auto shadowPass = renderList.GetShadowPass(lightEntity, data.idx); + if (shadowPass == nullptr) + shadowPass = renderList.NewShadowPass(lightEntity, data.idx); + + shadowPass->NewFrame(scene, meshes, renderList.meshIdToMeshMap); + scene->GetRenderList(frustum, shadowPass); + shadowPass->Update(camera.GetLocation(), renderList.meshIdToMeshMap); + shadowPass->FillBuffers(); + renderList.FinishPass(shadowPass); + }); + } + + JobSystem::Wait(group); + + auto mainPass = renderList.GetMainPass(); + if (mainPass == nullptr) + mainPass = renderList.NewMainPass(); + + mainPass->NewFrame(scene, meshes, renderList.meshIdToMeshMap); + scene->GetRenderList(camera.frustum, mainPass); + mainPass->Update(camera.GetLocation(), renderList.meshIdToMeshMap); + mainPass->FillBuffers(); + renderList.FinishPass(mainPass); + }); + } + + void SceneRenderState::CullAndSortLights() { + + JobSystem::Wait(cullAndSortLightsJob); + + JobSystem::Execute(cullAndSortLightsJob, [&](JobData&) { + auto& camera = scene->GetMainCamera(); + + lightEntities.clear(); + lightEntities.reserve(scene->GetComponentCount()); + auto lightSubset = scene->GetSubset(); + for (auto& lightEntity : lightSubset) { + auto& light = lightEntity.GetComponent(); + if (!light.IsVisible(camera.frustum)) + continue; + lightEntities.emplace_back(LightEntity{ lightEntity, light }); + } + + if (lightEntities.size() > 1) { + auto cameraLocation = camera.GetLocation(); + auto getPositionForLight = [cameraLocation](const LightEntity& light) -> vec3 { + switch (light.comp.type) { + case LightType::PointLight: return light.comp.transformedProperties.point.position; + case LightType::SpotLight: return light.comp.transformedProperties.spot.position; + default: return cameraLocation; + } + }; + + std::sort(lightEntities.begin(), lightEntities.end(), + [&](const LightEntity& light0, const LightEntity& light1) { + if (light0.comp.isMain) + return true; + + if (light0.comp.type == LightType::DirectionalLight) + return true; + + return glm::distance2(getPositionForLight(light0), cameraLocation) + < glm::distance2(getPositionForLight(light1), cameraLocation); + }); + } + + JobSystem::Wait(bindlessOtherTextureMapUpdateJob); + + if (lightEntities.size()) { + lights.clear(); + volumetricLights.clear(); + volumetricShadows.clear(); + + for (const auto& entity : lightEntities) { + auto& light = entity.comp; + + auto type = static_cast(light.type); + auto packedType = reinterpret_cast(type); + Renderer::Light lightUniform { + .color = vec4(Common::ColorConverter::ConvertSRGBToLinear(light.color), packedType), + .intensity = light.intensity, + .scatteringFactor = 1.0f, + }; + + const auto& prop = light.transformedProperties; + if (light.type == LightType::DirectionalLight) { + lightUniform.direction = camera.viewMatrix * vec4(prop.directional.direction, 0.0f); + } + else if (light.type == LightType::PointLight) { + lightUniform.location = camera.viewMatrix * vec4(prop.point.position, 1.0f); + lightUniform.direction.w = prop.point.radius; + } + else if (light.type == LightType::SpotLight) { + lightUniform.location = camera.viewMatrix * vec4(prop.spot.position, 1.0f); + lightUniform.direction = camera.viewMatrix * vec4(prop.spot.direction, 0.0f); + lightUniform.direction.w = prop.spot.radius; + + auto tanOuter = tanf(prop.spot.outerConeAngle); + auto cosOuter = cosf(prop.spot.outerConeAngle); + auto cosInner = cosf(prop.spot.innerConeAngle); + auto angleScale = 1.0f / std::max(0.001f, cosInner - cosOuter); + auto angleOffset = -cosOuter * angleScale; + + lightUniform.typeSpecific0 = angleScale; + lightUniform.typeSpecific1 = angleOffset; + + uint32_t coneTrig = glm::packHalf2x16(vec2(cosOuter, tanOuter)); + lightUniform.location.w = reinterpret_cast(coneTrig); + } + + if (light.shadow) { + auto shadow = light.shadow; + auto& shadowUniform = lightUniform.shadow; + shadowUniform.distance = !shadow->longRange ? shadow->distance : shadow->longRangeDistance; + shadowUniform.bias = shadow->bias; + shadowUniform.edgeSoftness = shadow->edgeSoftness; + shadowUniform.cascadeBlendDistance = shadow->cascadeBlendDistance; + shadowUniform.cascadeCount = shadow->viewCount; + shadowUniform.resolution = vec2(shadow->resolution); + shadowUniform.mapIdx = shadow->useCubemap ? cubemapToBindlessIdx[shadow->cubemap] : textureArrayToBindlessIdx[shadow->maps]; + + auto componentCount = shadow->viewCount; + for (int32_t i = 0; i < MAX_SHADOW_VIEW_COUNT + 1; i++) { + if (i < componentCount) { + auto cascade = &shadow->views[i]; + auto frustum = Volume::Frustum(cascade->frustumMatrix); + auto corners = frustum.GetCorners(); + auto texelSize = glm::max(abs(corners[0].x - corners[1].x), + abs(corners[1].y - corners[3].y)) / (float)shadow->resolution; + shadowUniform.cascades[i].distance = cascade->farDistance; + if (light.type == LightType::DirectionalLight) { + auto matrix = cascade->projectionMatrix * + cascade->viewMatrix * camera.invViewMatrix; + shadowUniform.cascades[i].cascadeSpace = glm::transpose(matrix); + } + shadowUniform.cascades[i].texelSize = texelSize; + } + else { + auto cascade = &shadow->views[componentCount - 1]; + shadowUniform.cascades[i].distance = cascade->farDistance; + } + } + + if (light.type == LightType::PointLight) { + auto projectionMatrix = shadow->views[0].projectionMatrix; + shadowUniform.cascades[0].cascadeSpace = glm::transpose(glm::translate(mat4(1.0f), -prop.point.position) * camera.invViewMatrix); + shadowUniform.cascades[1].cascadeSpace[0] = projectionMatrix[0]; + shadowUniform.cascades[1].cascadeSpace[1] = projectionMatrix[1]; + shadowUniform.cascades[1].cascadeSpace[2] = projectionMatrix[2]; + shadowUniform.cascades[2].cascadeSpace[0] = projectionMatrix[3]; + } + else if (light.type == LightType::SpotLight) { + auto cascadeMatrix = shadow->views[0].projectionMatrix * + shadow->views[0].viewMatrix * camera.invViewMatrix; + shadowUniform.cascades[0].cascadeSpace[0] = cascadeMatrix[0]; + shadowUniform.cascades[0].cascadeSpace[1] = cascadeMatrix[1]; + shadowUniform.cascades[0].cascadeSpace[2] = cascadeMatrix[2]; + shadowUniform.cascades[1].cascadeSpace[0] = cascadeMatrix[3]; + } + } + else { + lightUniform.shadow.mapIdx = -1; + } + + lights.push_back(lightUniform); + + if (light.volumetricIntensity > 0.0f) { + Renderer::VolumetricLight volumetricLightUniform{ + .location = lightUniform.location, + .direction = lightUniform.direction, + .color = lightUniform.color, + .intensity = light.volumetricIntensity * light.intensity, + .typeSpecific0 = lightUniform.typeSpecific0, + .typeSpecific1 = lightUniform.typeSpecific1, + .shadowIdx = light.shadow ? int32_t(volumetricShadows.size()) : -1 + }; + + volumetricLights.push_back(volumetricLightUniform); + volumetricShadows.push_back(lightUniform.shadow); + } + } + } + else { + // We need to have at least a fake light + auto type = 0; + auto packedType = reinterpret_cast(type); + lights.emplace_back(Renderer::Light { + .direction = vec4(0.0f, -1.0f, 0.0f, 0.0f), + .color = vec4(vec3(0.0f), packedType), + .intensity = 0.0f, + }); + } + + if (lightBuffer.GetElementCount() < lights.size()) { + lightBuffer = Buffer::Buffer(Buffer::BufferUsageBits::HostAccessBit | Buffer::BufferUsageBits::MultiBufferedBit + | Buffer::BufferUsageBits::StorageBufferBit, sizeof(Renderer::Light), lights.size(), lights.data()); + } + else { + lightBuffer.SetData(lights.data(), 0, lights.size()); + } + + if (volumetricLights.empty()) { + volumetricLights.emplace_back(Renderer::VolumetricLight { + .intensity = 0.0f, + .shadowIdx = -1, + }); + volumetricShadows.emplace_back(Renderer::Shadow {}); + } + + if (volumetricLightBuffer.GetElementCount() < volumetricLights.size()) { + volumetricLightBuffer = Buffer::Buffer(Buffer::BufferUsageBits::HostAccessBit | Buffer::BufferUsageBits::MultiBufferedBit + | Buffer::BufferUsageBits::StorageBufferBit, sizeof(Renderer::VolumetricLight), volumetricLights.size(), volumetricLights.data()); + } + else { + volumetricLightBuffer.SetData(volumetricLights.data(), 0, volumetricLights.size()); + } + + if (volumetricShadowBuffer.GetElementCount() < volumetricShadows.size()) { + volumetricShadowBuffer = Buffer::Buffer(Buffer::BufferUsageBits::HostAccessBit | Buffer::BufferUsageBits::MultiBufferedBit + | Buffer::BufferUsageBits::StorageBufferBit, sizeof(Renderer::Shadow), volumetricShadows.size(), volumetricShadows.data()); + } + else { + volumetricShadowBuffer.SetData(volumetricShadows.data(), 0, volumetricShadows.size()); + } + + }); + + } + + void SceneRenderState::WaitForAsyncWorkCompletion() { + + // Assume scene work was done + mainCameraSignal.Release(); + + JobSystem::Wait(bindlessBlasMapUpdateJob); + JobSystem::Wait(bindlessTextureMapUpdateJob); + JobSystem::Wait(bindlessOtherTextureMapUpdateJob); + JobSystem::Wait(materialUpdateJob); + JobSystem::Wait(rayTracingWorldUpdateJob); + JobSystem::Wait(prepareBindlessBlasesJob); + JobSystem::Wait(fillRenderListJob); + JobSystem::Wait(cullAndSortLightsJob); + + if (!renderList.wasCleared) + renderList.Clear(); + + } + +} diff --git a/src/engine/scene/SceneRenderState.h b/src/engine/scene/SceneRenderState.h new file mode 100644 index 000000000..c44fd3ba0 --- /dev/null +++ b/src/engine/scene/SceneRenderState.h @@ -0,0 +1,88 @@ +#pragma once + +#include "Entity.h" +#include "jobsystem/JobGroup.h" +#include "texture/Texture2D.h" +#include "texture/Texture2DArray.h" +#include "texture/Cubemap.h" + +#include "buffer/UniformBuffer.h" + +#include "renderer/helper/CommonStructures.h" +#include "renderer/helper/RenderList.h" + +#include + +namespace Atlas::Scene { + + class Scene; + + struct LightEntity { + Entity entity; + LightComponent comp; + }; + + class SceneRenderState { + + public: + SceneRenderState(Scene* scene); + + ~SceneRenderState(); + + void PrepareMaterials(); + + void UpdateBlasBindlessData(); + + void UpdateTextureBindlessData(); + + void UpdateOtherTextureBindlessData(); + + void FillRenderList(); + + void CullAndSortLights(); + + void WaitForAsyncWorkCompletion(); + + Scene* scene; + RenderList renderList; + + Buffer::Buffer materialBuffer; + Buffer::Buffer lightBuffer; + Buffer::Buffer volumetricLightBuffer; + Buffer::Buffer volumetricShadowBuffer; + + std::vector> textures; + std::vector> textureArrays; + std::vector> cubemaps; + std::vector> blasBuffers; + std::vector> triangleBuffers; + std::vector> bvhTriangleBuffers; + std::vector> triangleOffsetBuffers; + + std::vector terrainLeafNodes; + std::vector materials; + std::unordered_map materialMap; + std::unordered_map, uint32_t> textureToBindlessIdx; + std::unordered_map, uint32_t> textureArrayToBindlessIdx; + std::unordered_map, uint32_t> cubemapToBindlessIdx; + std::unordered_map, uint32_t> blasToBindlessIdx; + + std::vector lightEntities; + std::vector lights; + std::vector volumetricLights; + std::vector volumetricShadows; + + JobSignal mainCameraSignal; + + JobGroup materialUpdateJob{ JobPriority::High }; + JobGroup rayTracingWorldUpdateJob{ JobPriority::High }; + JobGroup bindlessBlasMapUpdateJob{ JobPriority::High }; + JobGroup bindlessTextureMapUpdateJob{ JobPriority::High }; + JobGroup bindlessOtherTextureMapUpdateJob{ JobPriority::High }; + JobGroup prepareBindlessBlasesJob{ JobPriority::High }; + JobGroup fillRenderListJob{ JobPriority::High }; + JobGroup cullAndSortLightsJob{ JobPriority::High }; + + }; + +} diff --git a/src/engine/scene/components/CameraComponent.cpp b/src/engine/scene/components/CameraComponent.cpp index 056e47758..1818dc2e3 100644 --- a/src/engine/scene/components/CameraComponent.cpp +++ b/src/engine/scene/components/CameraComponent.cpp @@ -110,6 +110,12 @@ namespace Atlas { } + mat4 CameraComponent::GetLastViewMatrix() const { + + return lastViewMatrix; + + } + vec3 CameraComponent::GetLocation() const { return vec3(invViewMatrix[3]); @@ -122,9 +128,9 @@ namespace Atlas { } - std::vector CameraComponent::GetFrustumCorners(float nearPlane, float farPlane) const { + std::array CameraComponent::GetFrustumCorners(float nearPlane, float farPlane) const { - std::vector corners; + std::array corners; float radians = glm::radians(fieldOfView) / 2.0f; float tang = tanf(radians); @@ -138,15 +144,15 @@ namespace Atlas { vec3 farPoint = cameraLocation + globalDirection * farPlane; vec3 nearPoint = cameraLocation + globalDirection * nearPlane; - corners.push_back(farPoint + farHeight * globalUp - farWidth * globalRight); - corners.push_back(farPoint + farHeight * globalUp + farWidth * globalRight); - corners.push_back(farPoint - farHeight * globalUp - farWidth * globalRight); - corners.push_back(farPoint - farHeight * globalUp + farWidth * globalRight); + corners[0] = farPoint + farHeight * globalUp - farWidth * globalRight; + corners[1] = farPoint + farHeight * globalUp + farWidth * globalRight; + corners[2] = farPoint - farHeight * globalUp - farWidth * globalRight; + corners[3] = farPoint - farHeight * globalUp + farWidth * globalRight; - corners.push_back(nearPoint + nearHeight * globalUp - nearWidth * globalRight); - corners.push_back(nearPoint + nearHeight * globalUp + nearWidth * globalRight); - corners.push_back(nearPoint - nearHeight * globalUp - nearWidth * globalRight); - corners.push_back(nearPoint - nearHeight * globalUp + nearWidth * globalRight); + corners[4] = nearPoint + nearHeight * globalUp - nearWidth * globalRight; + corners[5] = nearPoint + nearHeight * globalUp + nearWidth * globalRight; + corners[6] = nearPoint - nearHeight * globalUp - nearWidth * globalRight; + corners[7] = nearPoint - nearHeight * globalUp + nearWidth * globalRight; return corners; diff --git a/src/engine/scene/components/CameraComponent.h b/src/engine/scene/components/CameraComponent.h index da3ccbeed..928d16eb4 100644 --- a/src/engine/scene/components/CameraComponent.h +++ b/src/engine/scene/components/CameraComponent.h @@ -29,11 +29,13 @@ namespace Atlas { mat4 GetLastJitteredMatrix() const; + mat4 GetLastViewMatrix() const; + vec3 GetLocation() const; vec3 GetLastLocation() const; - std::vector GetFrustumCorners(float nearPlane, float farPlane) const; + std::array GetFrustumCorners(float nearPlane, float farPlane) const; void UpdateFrustum(); diff --git a/src/engine/scene/components/ComponentSerializer.cpp b/src/engine/scene/components/ComponentSerializer.cpp index 0a17e4815..32019e84c 100644 --- a/src/engine/scene/components/ComponentSerializer.cpp +++ b/src/engine/scene/components/ComponentSerializer.cpp @@ -110,8 +110,16 @@ namespace Atlas::Scene::Components { else if (p.type == LightType::PointLight) { typeProperties = json { {"position", p.properties.point.position}, - {"radius", p.properties.point.radius}, - {"attenuation", p.properties.point.attenuation}, + {"radius", p.properties.point.radius} + }; + } + else if (p.type == LightType::SpotLight) { + typeProperties = json{ + {"position", p.properties.spot.position}, + {"direction", p.properties.spot.direction}, + {"radius", p.properties.spot.radius}, + {"outerConeAngle", p.properties.spot.outerConeAngle}, + {"innerConeAngle", p.properties.spot.innerConeAngle}, }; } @@ -124,29 +132,31 @@ namespace Atlas::Scene::Components { {"color", p.color}, {"intensity", p.intensity}, {"properties", typeProperties}, - {"shadow", *p.shadow}, {"isMain", p.isMain}, - {"volumetric", p.volumetric} + {"volumetric", p.volumetric}, + {"volumetricIntensity", p.volumetricIntensity} }; + + if (p.shadow) + j["shadow"] = *p.shadow; } void from_json(const json& j, LightComponent& p) { json typeProperties; - int type, mobility; - - p.shadow = CreateRef(); + int type, mobility; j.at("type").get_to(type); j.at("mobility").get_to(mobility); j.at("color").get_to(p.color); j.at("intensity").get_to(p.intensity); j.at("properties").get_to(typeProperties); - j.at("shadow").get_to(*p.shadow); j.at("isMain").get_to(p.isMain); j.at("volumetric").get_to(p.volumetric); + try_get_json(j, "volumetricIntensity", p.volumetricIntensity); + p.type = static_cast(type); - p.mobility = static_cast(type); + p.mobility = static_cast(mobility); if (p.type == LightType::DirectionalLight) { typeProperties.at("direction").get_to(p.properties.directional.direction); @@ -154,7 +164,18 @@ namespace Atlas::Scene::Components { else if (p.type == LightType::PointLight) { typeProperties.at("position").get_to(p.properties.point.position); typeProperties.at("radius").get_to(p.properties.point.radius); - typeProperties.at("attenuation").get_to(p.properties.point.attenuation); + } + else if (p.type == LightType::SpotLight) { + typeProperties.at("position").get_to(p.properties.spot.position); + typeProperties.at("direction").get_to(p.properties.spot.direction); + typeProperties.at("radius").get_to(p.properties.spot.radius); + typeProperties.at("outerConeAngle").get_to(p.properties.spot.outerConeAngle); + typeProperties.at("innerConeAngle").get_to(p.properties.spot.innerConeAngle); + } + + if (j.contains("shadow")) { + p.shadow = CreateRef(); + j.at("shadow").get_to(*p.shadow); } } @@ -164,7 +185,7 @@ namespace Atlas::Scene::Components { {"dontCull", p.dontCull} }; - if (p.mesh.IsValid()) + if (p.mesh.IsValid() && !p.mesh.IsGenerated()) j["resourcePath"] = p.mesh.GetResource()->path; } @@ -222,7 +243,7 @@ namespace Atlas::Scene::Components { {"textScale", p.textScale}, }; - if (p.font.IsValid()) + if (p.font.IsValid() && !p.font.IsGenerated()) j["resourcePath"] = p.font.GetResource()->path; } @@ -300,7 +321,7 @@ namespace Atlas::Scene::Components { void to_json(json &j, const LuaScriptComponent &p) { j = json{}; - if (p.script.IsValid()) + if (p.script.IsValid() && !p.script.IsGenerated()) j["resourcePath"] = p.script.GetResource()->path; j["permanentExecution"] = p.permanentExecution; @@ -323,8 +344,20 @@ namespace Atlas::Scene::Components { j["scriptProperties"][name]["type"] = "string"; j["scriptProperties"][name]["value"] = prop.stringValue; break; + case LuaScriptComponent::PropertyType::Vec2: + j["scriptProperties"][name]["type"] = "vec2"; + j["scriptProperties"][name]["value"] = prop.vec2Value; + break; + case LuaScriptComponent::PropertyType::Vec3: + j["scriptProperties"][name]["type"] = "vec3"; + j["scriptProperties"][name]["value"] = prop.vec3Value; + break; + case LuaScriptComponent::PropertyType::Vec4: + j["scriptProperties"][name]["type"] = "vec4"; + j["scriptProperties"][name]["value"] = prop.vec4Value; + break; default: - AE_ASSERT(false); + break; } } } @@ -363,6 +396,18 @@ namespace Atlas::Scene::Components { { p.SetPropertyValue(v.key(), value["value"].get()); } + else if (propertyTypeAsString == "vec2") + { + p.SetPropertyValue(v.key(), value["value"].get()); + } + else if (propertyTypeAsString == "vec3") + { + p.SetPropertyValue(v.key(), value["value"].get()); + } + else if (propertyTypeAsString == "vec4") + { + p.SetPropertyValue(v.key(), value["value"].get()); + } } } } diff --git a/src/engine/scene/components/LightComponent.cpp b/src/engine/scene/components/LightComponent.cpp index d968ffeca..01b643344 100644 --- a/src/engine/scene/components/LightComponent.cpp +++ b/src/engine/scene/components/LightComponent.cpp @@ -11,6 +11,23 @@ namespace Atlas { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 1.0f); + LightComponent::LightComponent(Scene* scene, const LightComponent& that) { + + if (this != &that) { + + *this = that; + + // Copy shadow, need to have separate resources + if (that.shadow != nullptr) { + shadow = CreateRef(*that.shadow); + shadow->SetResolution(shadow->resolution); + shadow->update = true; + } + + } + + } + LightComponent::LightComponent(LightType type, LightMobility mobility) : type(type), mobility(mobility), properties(type), transformedProperties(type) { @@ -60,7 +77,7 @@ namespace Atlas { shadow->views[0].frustumMatrix = clipMatrix * orthoProjection; shadow->views[0].terrainFrustumMatrix = clipMatrix * orthoProjection; shadow->views[0].viewMatrix = glm::lookAt(shadowCenter, shadowCenter + - properties.directional.direction, vec3(0.0f, 1.0f, 0.0f)); + properties.directional.direction, vec3(0.0f, 1.0f, 0.0f)); } @@ -68,10 +85,38 @@ namespace Atlas { AE_ASSERT(type == LightType::PointLight && "Component must be of type point light"); - shadow = CreateRef(0.0f, bias, resolution, true); + shadow = CreateRef(200.0f, bias, resolution, 0.005f, true); } + void LightComponent::AddSpotShadow(float bias, int32_t resolution) { + + AE_ASSERT(type == LightType::SpotLight && "Component must be of type spot light"); + + shadow = CreateRef(200.0f, bias, resolution, 0.005f, false); + + } + + bool LightComponent::IsVisible(const Volume::Frustum& frustum) const { + + if (type == LightType::DirectionalLight) + return true; + + Volume::AABB aabb; + + if (type == LightType::PointLight) { + auto min = transformedProperties.point.position - vec3(transformedProperties.point.radius); + auto max = transformedProperties.point.position + vec3(transformedProperties.point.radius); + aabb = Volume::AABB(min, max); + } + else if (type == LightType::SpotLight) { + aabb = Volume::AABB(-vec3(2000.0f), vec3(2000.0f)); + } + + return frustum.Intersects(aabb); + + } + void LightComponent::Update(const TransformComponent* transform) { transformedProperties = properties; @@ -83,6 +128,11 @@ namespace Atlas { else if (type == LightType::PointLight && transform) { transformedProperties.point.position = vec3(transform->globalMatrix * vec4(properties.point.position, 1.0f)); } + else if (type == LightType::SpotLight && transform) { + transformedProperties.spot.position = vec3(transform->globalMatrix * vec4(properties.spot.position, 1.0f)); + transformedProperties.spot.direction = glm::normalize(vec3(transform->globalMatrix * + vec4(properties.spot.direction, 0.0f))); + } } @@ -113,18 +163,18 @@ namespace Atlas { } else if (!shadow->isCascaded && type == LightType::DirectionalLight) { shadow->views[0].viewMatrix = glm::lookAt(shadow->center, - shadow->center + transformedProperties.directional.direction, vec3(0.0f, 1.0f, 0.0f)); + shadow->center + transformedProperties.directional.direction, vec3(1e-12f, 1.0f, 1e-12f)); } else if (type == LightType::PointLight) { vec3 position = transformedProperties.point.position; - mat4 projectionMatrix = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, properties.point.radius); - const vec3 faces[] = { vec3(1.0f, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), - vec3(0.0f, 1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), - vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f) }; + mat4 projectionMatrix = clipMatrix * glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, transformedProperties.point.radius); + vec3 faces[] = { vec3(1.0f, 0.0f, 0.0f), vec3(-1.0f, 0.0f, 0.0f), + vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f), + vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f) }; - const vec3 ups[] = { vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), - vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, 0.0f, -1.0f), + vec3 ups[] = { vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f), + vec3(0.0f, 0.0f, -1.0f), vec3(0.0f, 0.0f, 1.0f), vec3(0.0f, -1.0f, 0.0f), vec3(0.0f, -1.0f, 0.0f) }; for (uint8_t i = 0; i < 6; i++) { @@ -134,6 +184,14 @@ namespace Atlas { shadow->views[i].frustumMatrix = projectionMatrix * viewMatrix; } } + else if (type == LightType::SpotLight) { + auto viewMatrix = glm::lookAt(transformedProperties.spot.position, transformedProperties.spot.position + + transformedProperties.spot.direction, vec3(1e-12f, 1.0f, 1e-12f)); + auto projectionMatrix = glm::perspective(2.0f * transformedProperties.spot.outerConeAngle, 1.0f, 0.1f, transformedProperties.spot.radius); + shadow->views[0].viewMatrix = viewMatrix; + shadow->views[0].projectionMatrix = clipMatrix * projectionMatrix; + shadow->views[0].frustumMatrix = clipMatrix * projectionMatrix * viewMatrix; + } if (mobility == LightMobility::MovableLight) shadow->Update(); @@ -153,10 +211,10 @@ namespace Atlas { // A near enough up vector. This is because if the light location is // (0.0f, 1.0f, 0.0f) the shadows wouldn't render correctly due to the // shadows (or lights) view matrix. This is just a hack - vec3 up = glm::vec3(0.0000000000000001f, 1.0f, 0.0000000000000001f); + vec3 up = vec3(1e-12f, 1.0f, 1e-12f); cascade.viewMatrix = glm::lookAt(cascadeCenter, cascadeCenter + lightDirection, up); - std::vector corners = camera.GetFrustumCorners(cascade.nearDistance, + auto corners = camera.GetFrustumCorners(cascade.nearDistance, cascade.farDistance + shadow->cascadeBlendDistance); vec3 maxProj = vec3(cascade.viewMatrix * vec4(corners.at(0), 1.0f)); diff --git a/src/engine/scene/components/LightComponent.h b/src/engine/scene/components/LightComponent.h index a1e906fdf..9a6b2cc7f 100644 --- a/src/engine/scene/components/LightComponent.h +++ b/src/engine/scene/components/LightComponent.h @@ -19,7 +19,8 @@ namespace Atlas { enum class LightType { DirectionalLight = 0, - PointLight = 1 + PointLight = 1, + SpotLight = 2, }; enum class LightMobility { @@ -33,8 +34,15 @@ namespace Atlas { struct PointLightProperties { vec3 position = vec3(0.0f); - float radius = 10.0f; - float attenuation = 1.0f; + float radius = 20.0f; + }; + + struct SpotLightProperties { + vec3 position = vec3(0.0f); + vec3 direction = -vec3(1.0f); + float radius = 20.0f; + float innerConeAngle = 1.0f; + float outerConeAngle = 1.0f; }; struct TypeProperties { @@ -46,19 +54,21 @@ namespace Atlas { TypeProperties(LightType type) { switch (type) { case LightType::PointLight: point = PointLightProperties(); break; + case LightType::SpotLight: spot = SpotLightProperties(); break; default: directional = DirectionalLightProperties(); break; } } DirectionalLightProperties directional; PointLightProperties point; + SpotLightProperties spot; }; class LightComponent { public: LightComponent() = default; - LightComponent(const LightComponent& that) = default; + LightComponent(Scene* scene, const LightComponent& that); LightComponent(LightType type, LightMobility mobility = LightMobility::MovableLight); void AddDirectionalShadow(float distance, float bias, int32_t resolution, float edgeSoftness, @@ -69,11 +79,16 @@ namespace Atlas { void AddPointShadow(float bias, int32_t resolution); + void AddSpotShadow(float bias, int32_t resolution); + + bool IsVisible(const Volume::Frustum& frustum) const; + LightType type = LightType::DirectionalLight; LightMobility mobility = LightMobility::MovableLight; vec3 color = vec3(1.0f); float intensity = 1.0f; + float volumetricIntensity = 1.0f; TypeProperties properties; TypeProperties transformedProperties; diff --git a/src/engine/scene/components/LuaScriptComponent.cpp b/src/engine/scene/components/LuaScriptComponent.cpp index 3a4ae3b08..fe90498fd 100644 --- a/src/engine/scene/components/LuaScriptComponent.cpp +++ b/src/engine/scene/components/LuaScriptComponent.cpp @@ -96,7 +96,7 @@ namespace Atlas::Scene::Components { return; // and then get or update the properties from the script - GetOrUpdatePropertiesFromScript(); + GetOrUpdatePropertiesFromScript(scriptWasModifiedInLastUpdate); environmentNeedsInitialization = false; } @@ -225,6 +225,15 @@ namespace Atlas::Scene::Components { else if (typeValue == "boolean") { scriptProperty.type = PropertyType::Boolean; } + else if (typeValue == "vec2") { + scriptProperty.type = PropertyType::Vec2; + } + else if (typeValue == "vec3") { + scriptProperty.type = PropertyType::Vec3; + } + else if (typeValue == "vec4") { + scriptProperty.type = PropertyType::Vec4; + } else { continue; // unknown type } @@ -254,6 +263,21 @@ namespace Atlas::Scene::Components { continue; scriptProperty.booleanValue = value.get(); break; + case PropertyType::Vec2: + if (!value.is()) + continue; + scriptProperty.vec2Value = value.get(); + break; + case PropertyType::Vec3: + if (!value.is()) + continue; + scriptProperty.vec3Value = value.get(); + break; + case PropertyType::Vec4: + if (!value.is()) + continue; + scriptProperty.vec3Value = value.get(); + break; case PropertyType::Undefined: break; } @@ -264,7 +288,7 @@ namespace Atlas::Scene::Components { return foundProperties; } - void LuaScriptComponent::GetOrUpdatePropertiesFromScript() { + void LuaScriptComponent::GetOrUpdatePropertiesFromScript(bool update) { // obtain the new properties from the script auto newProperties = GetPropertiesFromScript(); @@ -275,7 +299,7 @@ namespace Atlas::Scene::Components { continue; const auto& existingProperty = properties[name]; - if (!existingProperty.wasChanged) + if (!existingProperty.wasChanged && !update) continue; // Only update if there was a server side change of the script properties @@ -304,6 +328,15 @@ namespace Atlas::Scene::Components { case PropertyType::Boolean: state["ScriptProperties"][propertyName]["value"] = property.booleanValue; break; + case PropertyType::Vec2: + state["ScriptProperties"][propertyName]["value"] = property.vec2Value; + break; + case PropertyType::Vec3: + state["ScriptProperties"][propertyName]["value"] = property.vec3Value; + break; + case PropertyType::Vec4: + state["ScriptProperties"][propertyName]["value"] = property.vec4Value; + break; case PropertyType::Undefined: break; } diff --git a/src/engine/scene/components/LuaScriptComponent.h b/src/engine/scene/components/LuaScriptComponent.h index be2a82078..39bf31dd2 100644 --- a/src/engine/scene/components/LuaScriptComponent.h +++ b/src/engine/scene/components/LuaScriptComponent.h @@ -24,7 +24,10 @@ namespace Atlas::Scene { String, Double, Integer, - Boolean + Boolean, + Vec2, + Vec3, + Vec4 }; class ScriptProperty { @@ -44,6 +47,10 @@ namespace Atlas::Scene { int integerValue = 0; bool booleanValue = false; + vec2 vec2Value = vec2(0.0f); + vec3 vec3Value = vec3(0.0f); + vec4 vec4Value = vec4(0.0f); + bool wasChanged = false; }; @@ -88,7 +95,7 @@ namespace Atlas::Scene { bool InitScriptEnvironment(); std::map GetPropertiesFromScript(); - void GetOrUpdatePropertiesFromScript(); + void GetOrUpdatePropertiesFromScript(bool update); void SetPropertyValuesInLuaState(); }; @@ -96,7 +103,8 @@ namespace Atlas::Scene { void LuaScriptComponent::ScriptProperty::SetValue(const T value) { static_assert(std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v, "Unsupported type"); + std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, "Unsupported type"); if constexpr (std::is_same_v) { stringValue = value; @@ -114,6 +122,18 @@ namespace Atlas::Scene { booleanValue = value; type = PropertyType::Boolean; } + else if constexpr (std::is_same_v) { + vec2Value = value; + type = PropertyType::Vec2; + } + else if constexpr (std::is_same_v) { + vec3Value = value; + type = PropertyType::Vec3; + } + else if constexpr (std::is_same_v) { + vec4Value = value; + type = PropertyType::Vec4; + } wasChanged = true; } @@ -123,8 +143,9 @@ namespace Atlas::Scene { AE_ASSERT(type != PropertyType::Undefined && "This property was most likely not defined properly"); - static_assert(std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v, "Unsupported type"); + static_assert(std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, "Unsupported type"); if constexpr (std::is_same_v) { return stringValue; @@ -138,6 +159,15 @@ namespace Atlas::Scene { else if constexpr (std::is_same_v) { return booleanValue; } + else if constexpr (std::is_same_v) { + return vec2Value; + } + else if constexpr (std::is_same_v) { + return vec3Value; + } + else if constexpr (std::is_same_v) { + return vec4Value; + } else { } diff --git a/src/engine/scene/components/RigidBodyComponent.cpp b/src/engine/scene/components/RigidBodyComponent.cpp index c313b500f..f95aa67fe 100644 --- a/src/engine/scene/components/RigidBodyComponent.cpp +++ b/src/engine/scene/components/RigidBodyComponent.cpp @@ -9,9 +9,19 @@ namespace Atlas { RigidBodyComponent::RigidBodyComponent(Scene* scene, Entity entity, const RigidBodyComponent& that) { if (this != &that) { + // In case this contains a valid body -> destroy, since we assume to own the body + if (world != nullptr && world->ContainsBody(*this)) { + world->DestroyBody(*this); + } + *this = that; + + // Reset body and create new settings + this->bodyId = Physics::BodyID(); + this->creationSettings = CreateRef(that.GetBodyCreationSettings()); } + // In a copy constructor we want to enforce to create a new body this->entity = entity; } diff --git a/src/engine/scripting/bindings/GraphicsBindings.cpp b/src/engine/scripting/bindings/GraphicsBindings.cpp index 20f1944f5..aa7a4b6fc 100644 --- a/src/engine/scripting/bindings/GraphicsBindings.cpp +++ b/src/engine/scripting/bindings/GraphicsBindings.cpp @@ -12,27 +12,47 @@ namespace Atlas::Scripting::Bindings { ns->new_usertype("Texture2D", "width", &Texture::Texture2D::width, "height", &Texture::Texture2D::height, - "channels", &Texture::Texture2D::channels + "channels", &Texture::Texture2D::channels, + "IsValid", &Texture::Texture2D::IsValid, + "Reset", &Texture::Texture2D::Reset, + "GetDataUInt8", &Texture::Texture2D::GetData, + "GetDataFloat16", &Texture::Texture2D::GetData, + "GetDataFloat32", &Texture::Texture2D::GetData ); ns->new_usertype("Texture2DArray", "width", &Texture::Texture2DArray::width, "height", &Texture::Texture2DArray::height, "depth", &Texture::Texture2DArray::depth, - "channels", &Texture::Texture2DArray::channels + "channels", &Texture::Texture2DArray::channels, + "IsValid", &Texture::Texture2D::IsValid, + "Reset", &Texture::Texture2D::Reset, + "GetDataUInt8", &Texture::Texture2D::GetData, + "GetDataFloat16", &Texture::Texture2D::GetData, + "GetDataFloat32", &Texture::Texture2D::GetData ); ns->new_usertype("Texture3D", "width", &Texture::Texture3D::width, "height", &Texture::Texture3D::height, "depth", &Texture::Texture3D::depth, - "channels", &Texture::Texture3D::channels + "channels", &Texture::Texture3D::channels, + "IsValid", &Texture::Texture2D::IsValid, + "Reset", &Texture::Texture2D::Reset, + "GetDataUInt8", &Texture::Texture2D::GetData, + "GetDataFloat16", &Texture::Texture2D::GetData, + "GetData", &Texture::Texture2D::GetData ); ns->new_usertype("Cubemap", "width", &Texture::Cubemap::width, "height", &Texture::Cubemap::height, - "channels", &Texture::Cubemap::channels + "channels", &Texture::Cubemap::channels, + "IsValid", &Texture::Texture2D::IsValid, + "Reset", &Texture::Texture2D::Reset, + "GetDataUInt8", &Texture::Texture2D::GetData, + "GetDataFloat16", &Texture::Texture2D::GetData, + "GetDataFloat32", &Texture::Texture2D::GetData ); } diff --git a/src/engine/scripting/bindings/LightingBindings.cpp b/src/engine/scripting/bindings/LightingBindings.cpp index c477c72a1..d1b630dc2 100644 --- a/src/engine/scripting/bindings/LightingBindings.cpp +++ b/src/engine/scripting/bindings/LightingBindings.cpp @@ -126,6 +126,37 @@ namespace Atlas::Scripting::Bindings { "probe", &Lighting::Sky::probe ); + ns->new_usertype("ShadowView", + "nearDistance", &Lighting::ShadowView::nearDistance, + "farDistance", &Lighting::ShadowView::farDistance, + "viewMatrix", &Lighting::ShadowView::viewMatrix, + "projectionMatrix", &Lighting::ShadowView::projectionMatrix, + "frustumMatrix", &Lighting::ShadowView::frustumMatrix, + "terrainFrustumMatrix", &Lighting::ShadowView::terrainFrustumMatrix, + "orthoSize", &Lighting::ShadowView::orthoSize + ); + + ns->new_usertype("Shadow", + "SetResolution", &Lighting::Shadow::SetResolution, + "Update", &Lighting::Shadow::Update, + "center", &Lighting::Shadow::center, + "distance", &Lighting::Shadow::distance, + "longRangeDistance", &Lighting::Shadow::longRangeDistance, + "bias", &Lighting::Shadow::bias, + "splitCorrection", &Lighting::Shadow::splitCorrection, + "edgeSoftness", &Lighting::Shadow::edgeSoftness, + "cascadeBlendDistance", &Lighting::Shadow::cascadeBlendDistance, + "resolution", &Lighting::Shadow::resolution, + "views", &Lighting::Shadow::views, + "viewCount", &Lighting::Shadow::viewCount, + "isCascaded", &Lighting::Shadow::isCascaded, + "followMainCamera", &Lighting::Shadow::followMainCamera, + "useCubemap", &Lighting::Shadow::useCubemap, + "allowTerrain", &Lighting::Shadow::allowTerrain, + "longRange", &Lighting::Shadow::longRange, + "update", &Lighting::Shadow::update + ); + } } \ No newline at end of file diff --git a/src/engine/scripting/bindings/MathBindings.cpp b/src/engine/scripting/bindings/MathBindings.cpp index 4257089b8..a2cb24081 100644 --- a/src/engine/scripting/bindings/MathBindings.cpp +++ b/src/engine/scripting/bindings/MathBindings.cpp @@ -72,6 +72,28 @@ namespace Atlas::Scripting::Bindings { [](const glm::quat& quat0, const glm::quat& quat1, const float factor) { return glm::mix(quat0, quat1, factor); } )); + ns->set_function("Clamp", sol::overload( + [](const float float0, const float float1, const float float2) { return glm::clamp(float0, float1, float2); }, + [](const int32_t int0, const int32_t int1, const int32_t int2) { return glm::clamp(int0, int1, int2); }, + [](const glm::vec2& vec0, const glm::vec2& vec1, const glm::vec2& vec2) { return glm::clamp(vec0, vec1, vec2); }, + [](const glm::vec3& vec0, const glm::vec3& vec1, const glm::vec3& vec2) { return glm::clamp(vec0, vec1, vec2); }, + [](const glm::vec4& vec0, const glm::vec4& vec1, const glm::vec4& vec2) { return glm::clamp(vec0, vec1, vec2); } + )); + + ns->set_function("Max", sol::overload( + [](const float float0, const float float1) { return glm::max(float0, float1); }, + [](const glm::vec2& vec0, const glm::vec2& vec1) { return glm::max(vec0, vec1); }, + [](const glm::vec3& vec0, const glm::vec3& vec1) { return glm::max(vec0, vec1); }, + [](const glm::vec4& vec0, const glm::vec4& vec1) { return glm::max(vec0, vec1); } + )); + + ns->set_function("Min", sol::overload( + [](const float float0, const float float1) { return glm::min(float0, float1); }, + [](const glm::vec2& vec0, const glm::vec2& vec1) { return glm::min(vec0, vec1); }, + [](const glm::vec3& vec0, const glm::vec3& vec1) { return glm::min(vec0, vec1); }, + [](const glm::vec4& vec0, const glm::vec4& vec1) { return glm::min(vec0, vec1); } + )); + ns->set_function("LookAt", [](const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up) { return glm::lookAt(eye, center, up); } ); diff --git a/src/engine/scripting/bindings/MeshBindings.cpp b/src/engine/scripting/bindings/MeshBindings.cpp index 092eaf512..0d03afd8f 100644 --- a/src/engine/scripting/bindings/MeshBindings.cpp +++ b/src/engine/scripting/bindings/MeshBindings.cpp @@ -73,7 +73,14 @@ namespace Atlas::Scripting::Bindings { "displacementScale", &Material::displacementScale, "tiling", &Material::tiling, "twoSided", &Material::twoSided, - "vertexColors", &Material::vertexColors + "vertexColors", &Material::vertexColors, + "baseColorMap", &Material::baseColorMap, + "opacityMap", &Material::opacityMap, + "normalMap", &Material::normalMap, + "roughnessMap", &Material::roughnessMap, + "metalnessMap", &Material::metalnessMap, + "aoMap", &Material::aoMap, + "displacementMap", &Material::displacementMap ); } diff --git a/src/engine/scripting/bindings/ResourceBindings.h b/src/engine/scripting/bindings/ResourceBindings.h index 0225addbc..304d415af 100644 --- a/src/engine/scripting/bindings/ResourceBindings.h +++ b/src/engine/scripting/bindings/ResourceBindings.h @@ -15,7 +15,8 @@ namespace Atlas::Scripting::Bindings { "IsValid", &ResourceHandle::IsValid, "IsLoaded", &ResourceHandle::IsLoaded, "WaitForLoad", &ResourceHandle::WaitForLoad, - "GetID", &ResourceHandle::GetID + "GetID", &ResourceHandle::GetID, + "Reset", &ResourceHandle::Reset ); type.set_function("GetResource", sol::resolve>&(void)>(&ResourceHandle::GetResource)); diff --git a/src/engine/scripting/bindings/SceneBindings.cpp b/src/engine/scripting/bindings/SceneBindings.cpp index 07b8c8de1..6a2c89de3 100644 --- a/src/engine/scripting/bindings/SceneBindings.cpp +++ b/src/engine/scripting/bindings/SceneBindings.cpp @@ -41,6 +41,8 @@ namespace Atlas::Scripting::Bindings { auto entityType = ns->new_usertype("Entity", "IsValid", &Scene::Entity::IsValid, + "GetID", &Scene::Entity::GetID, + "GetVersion", &Scene::Entity::GetVersion, // Add components "AddAudioComponent", &Scene::Entity::AddComponent&, float, bool>, "AddAudioVolumeComponent", &Scene::Entity::AddComponent&, Volume::AABB&, float>, @@ -152,21 +154,45 @@ namespace Atlas::Scripting::Bindings { ns->new_usertype("PointLightProperties", "position", &PointLightProperties::position, - "radius", &PointLightProperties::radius, - "attenuation", &PointLightProperties::attenuation + "radius", &PointLightProperties::radius + ); + + ns->new_usertype("SpotLightProperties", + "position", &SpotLightProperties::position, + "direction", &SpotLightProperties::direction, + "radius", &SpotLightProperties::radius, + "outerConeAngle", &SpotLightProperties::outerConeAngle, + "innerConeAngle", &SpotLightProperties::innerConeAngle ); ns->new_usertype("TypeProperties", "directional", &TypeProperties::directional, - "point", &TypeProperties::point + "point", &TypeProperties::point, + "spot", &TypeProperties::spot ); - // TODO: Extend this + ns->new_enum("LightType", { + { "DirectionalLight", LightType::DirectionalLight }, + { "PointLight", LightType::PointLight }, + { "SpotLight", LightType::SpotLight } + }); + + ns->new_enum("LightMobility", { + { "StationaryLight", LightMobility::StationaryLight }, + { "MovableLight", LightMobility::MovableLight } + }); + ns->new_usertype("LightComponent", + "AddPointShadow", &LightComponent::AddPointShadow, + "AddSpotShadow", &LightComponent::AddSpotShadow, + "IsVisible", &LightComponent::IsVisible, + "type", &LightComponent::type, + "mobility", &LightComponent::mobility, "color", &LightComponent::color, "intensity", &LightComponent::intensity, "properties", &LightComponent::properties, "transformedProperties", &LightComponent::transformedProperties, + "shadow", &LightComponent::shadow, "isMain", &LightComponent::isMain, "volumetric", &LightComponent::volumetric ); @@ -188,7 +214,6 @@ namespace Atlas::Scripting::Bindings { "Decompose", &TransformComponent::Decompose, "DecomposeGlobal", &TransformComponent::DecomposeGlobal, "ReconstructLocalMatrix", &TransformComponent::ReconstructLocalMatrix, - //"Compose", &TransformComponent::Compose, "matrix", &TransformComponent::matrix, "globalMatrix", &TransformComponent::globalMatrix ); diff --git a/src/engine/scripting/bindings/UtilityBindings.cpp b/src/engine/scripting/bindings/UtilityBindings.cpp index da5c9017c..46c363605 100644 --- a/src/engine/scripting/bindings/UtilityBindings.cpp +++ b/src/engine/scripting/bindings/UtilityBindings.cpp @@ -72,13 +72,13 @@ namespace Atlas::Scripting::Bindings { ); auto resizeFrustumOverload = sol::overload( - [](Volume::Frustum& frustum, const std::vector& corners) { frustum.Resize(corners); }, + [](Volume::Frustum& frustum, const std::array& corners) { frustum.Resize(corners); }, [](Volume::Frustum& frustum, const mat4& matrix) { frustum.Resize(matrix); } ); ns->new_usertype("Frustum", sol::call_constructor, - sol::constructors&), Volume::Frustum(glm::mat4)>(), + sol::constructors&), Volume::Frustum(glm::mat4)>(), "Resize", resizeFrustumOverload, "Intersects", &Volume::Frustum::Intersects, "IsInside", &Volume::Frustum::IsInside, diff --git a/src/engine/terrain/Terrain.cpp b/src/engine/terrain/Terrain.cpp index 033ee0d7e..ff0f738f4 100644 --- a/src/engine/terrain/Terrain.cpp +++ b/src/engine/terrain/Terrain.cpp @@ -157,8 +157,8 @@ namespace Atlas { z -= zPosition; // Cells have overlapping edges (last pixels are on next cell) - x *= float(cell->heightField.width - 1); - z *= float(cell->heightField.height - 1); + x *= float(cell->heightField->width - 1); + z *= float(cell->heightField->height - 1); xPosition = floorf(x); zPosition = floorf(z); @@ -182,14 +182,14 @@ namespace Atlas { topLeft is in x direction bottomRight is in z direction */ - float heightBottomLeft = cell->heightData[xIndex + cell->heightField.width * zIndex]; + float heightBottomLeft = cell->heightData[xIndex + cell->heightField->width * zIndex]; float heightBottomRight = 0.0f; float heightTopRight = 0.0f; float heightTopLeft = 0.0f; // Check if we must sample from a neighbour node (allows for errors while retrieving the height information at the edge of the terrain) - if (zIndex + 1 == cell->heightField.height && - xIndex + 1 == cell->heightField.width) { + if (zIndex + 1 == cell->heightField->height && + xIndex + 1 == cell->heightField->width) { auto neighbourCell = storage.GetCell(xIndex + 1, zIndex + 1, LoDCount - 1); if (!neighbourCell) { @@ -199,13 +199,13 @@ namespace Atlas { } else { heightTopLeft = neighbourCell->heightData[1]; - heightBottomRight = neighbourCell->heightData[neighbourCell->heightField.width]; - heightTopRight = neighbourCell->heightData[neighbourCell->heightField.width + 1]; + heightBottomRight = neighbourCell->heightData[neighbourCell->heightField->width]; + heightTopRight = neighbourCell->heightData[neighbourCell->heightField->width + 1]; } } - else if (zIndex + 1 == cell->heightField.height) { + else if (zIndex + 1 == cell->heightField->height) { - heightTopLeft = cell->heightData[xIndex + 1 + cell->heightField.width * zIndex]; + heightTopLeft = cell->heightData[xIndex + 1 + cell->heightField->width * zIndex]; auto neighbourCell = storage.GetCell(xIndex, zIndex + 1, LoDCount - 1); @@ -219,9 +219,9 @@ namespace Atlas { } } - else if (xIndex + 1 == cell->heightField.width) { + else if (xIndex + 1 == cell->heightField->width) { - heightBottomRight = cell->heightData[xIndex + cell->heightField.width * (zIndex + 1)]; + heightBottomRight = cell->heightData[xIndex + cell->heightField->width * (zIndex + 1)]; auto neighbourCell = storage.GetCell(xIndex + 1, zIndex, LoDCount - 1); @@ -230,16 +230,16 @@ namespace Atlas { heightTopRight = heightBottomRight; } else { - heightTopLeft = neighbourCell->heightData[zIndex * neighbourCell->heightField.width]; + heightTopLeft = neighbourCell->heightData[zIndex * neighbourCell->heightField->width]; heightTopRight = neighbourCell->heightData[(zIndex + 1) * - neighbourCell->heightField.width]; + neighbourCell->heightField->width]; } } else { - heightTopLeft = cell->heightData[xIndex + 1 + cell->heightField.width * zIndex]; - heightBottomRight = cell->heightData[xIndex + cell->heightField.width * (zIndex + 1)]; - heightTopRight = cell->heightData[xIndex + 1 + cell->heightField.width * (zIndex + 1)]; + heightTopLeft = cell->heightData[xIndex + 1 + cell->heightField->width * zIndex]; + heightBottomRight = cell->heightData[xIndex + cell->heightField->width * (zIndex + 1)]; + heightTopRight = cell->heightData[xIndex + 1 + cell->heightField->width * (zIndex + 1)]; } heightBottomLeft *= heightScale; @@ -313,7 +313,7 @@ namespace Atlas { AE_ASSERT(cell->IsLoaded() && "All cells in a given LoD must \ be loaded to be converted into height field"); - width += cell->heightField.width - 1; + width += cell->heightField->width - 1; } for (int32_t y = 0; y < lodCount; y++) { @@ -322,7 +322,7 @@ namespace Atlas { AE_ASSERT(cell->IsLoaded() && "All cells in a given LoD must \ be loaded to be converted into height field"); - height += cell->heightField.height - 1; + height += cell->heightField->height - 1; } Common::Image heightImage(width, height, 1); @@ -336,19 +336,19 @@ namespace Atlas { AE_ASSERT(cell->IsLoaded() && "All cells in a given LoD must \ be loaded to be converted into height field"); - for (int32_t y = 0; y < cell->heightField.height - 1; y++) { - for (int32_t x = 0; x < cell->heightField.width - 1; x++) { - auto idx = y * cell->heightField.width + x; + for (int32_t y = 0; y < cell->heightField->height - 1; y++) { + for (int32_t x = 0; x < cell->heightField->width - 1; x++) { + auto idx = y * cell->heightField->width + x; heightImage.SetData(width + x, height + y, 0, cell->heightData[idx]); } } - width += cell->heightField.width - 1; + width += cell->heightField->width - 1; } auto cell = storage.GetCell(0, cellY, LoD); - height += cell->heightField.height - 1; + height += cell->heightField->height - 1; } return heightImage; diff --git a/src/engine/terrain/Terrain.h b/src/engine/terrain/Terrain.h index a355af912..b9d56e8f7 100644 --- a/src/engine/terrain/Terrain.h +++ b/src/engine/terrain/Terrain.h @@ -171,6 +171,7 @@ namespace Atlas { Buffer::VertexArray vertexArray; Buffer::VertexArray distanceVertexArray; std::vector renderList; + std::vector leafList; Common::Image LoDImage; @@ -194,7 +195,6 @@ namespace Atlas { std::vector LoDDistances; std::vector rootNodes; - std::vector leafList; }; diff --git a/src/engine/terrain/TerrainStorage.h b/src/engine/terrain/TerrainStorage.h index 28ea4b52e..0a8951282 100644 --- a/src/engine/terrain/TerrainStorage.h +++ b/src/engine/terrain/TerrainStorage.h @@ -95,6 +95,9 @@ namespace Atlas { Texture::Texture2DArray normalMaps; Texture::Texture2DArray displacementMaps; + std::vector> materials; + std::vector> cells; + private: void BlitImageToImageArray(Ref& srcImage, Ref& dstImage, int32_t slot); @@ -107,10 +110,6 @@ namespace Atlas { int32_t* LoDSideLengths; - std::vector> materials; - - std::vector> cells; - Graphics::CommandList* commandList = nullptr; }; diff --git a/src/engine/terrain/TerrainStorageCell.cpp b/src/engine/terrain/TerrainStorageCell.cpp index b557bce97..736bfcf88 100644 --- a/src/engine/terrain/TerrainStorageCell.cpp +++ b/src/engine/terrain/TerrainStorageCell.cpp @@ -13,16 +13,114 @@ namespace Atlas { bool TerrainStorageCell::IsLoaded() { - if (!heightField.IsValid()) + if (!heightField || !heightField->IsValid()) return false; - if (!normalMap.IsValid()) + if (!normalMap || !normalMap->IsValid()) return false; return true; } + void TerrainStorageCell::BuildBVH(float stretchFactor, float heightFactor) { + + if (!IsLoaded()) return; + + int32_t heightFieldSideLength = int32_t(sqrtf(float(heightData.size()))); + + aabb.min = glm::vec3(std::numeric_limits::max()); + aabb.max = glm::vec3(-std::numeric_limits::max()); + + std::vector vertices(heightData.size()); + for (int32_t y = 0; y < heightFieldSideLength; y++) { + for (int32_t x = 0; x < heightFieldSideLength; x++) { + auto idx = y * heightFieldSideLength + x; + vertices[idx] = vec3(float(x) * stretchFactor, heightData[idx] * heightFactor, float(y) * stretchFactor); + + aabb.min = glm::min(aabb.min, vertices[idx]); + aabb.max = glm::max(aabb.max, vertices[idx]); + } + } + + auto vertexSideCount = heightFieldSideLength - 1; + + std::vector indices(vertexSideCount * vertexSideCount * 6); + for (int32_t y = 0; y < vertexSideCount; y++) { + for (int32_t x = 0; x < vertexSideCount; x++) { + auto idx = y * heightFieldSideLength + x; + auto baseIdx = (y * vertexSideCount + x) * 6; + + indices[baseIdx + 0] = idx; + indices[baseIdx + 1] = idx + 1; + indices[baseIdx + 2] = idx + heightFieldSideLength; + + indices[baseIdx + 3] = idx + 1; + indices[baseIdx + 4] = idx + heightFieldSideLength + 1; + indices[baseIdx + 5] = idx + heightFieldSideLength; + } + } + + Buffer::IndexBuffer indexBuffer(VK_INDEX_TYPE_UINT32, indices.size(), indices.data(), true); + Buffer::VertexBuffer vertexBuffer(VK_FORMAT_R32G32B32_SFLOAT, vertices.size(), vertices.data(), true); + + std::vector> materials; + for (size_t i = 0; i < storage->materials.size(); i++) { + if (storage->materials[i] != nullptr) { + materials.push_back(ResourceHandle(storage->materials[i])); + materials.back()->twoSided = false; + } + else { + materials.push_back(ResourceHandle()); + } + } + + std::vector triangles(indices.size() / 3); + for (size_t i = 0; i < triangles.size(); i++) { + + RayTracing::BLAS::Triangle triangle; + + triangle.v0 = vertices[indices[i * 3 + 0]]; + triangle.v1 = vertices[indices[i * 3 + 1]]; + triangle.v2 = vertices[indices[i * 3 + 2]]; + + vec3 normal = -glm::normalize(glm::cross(triangle.v0 - triangle.v1, triangle.v0 - triangle.v2)); + + normal *= normal.y < 0.0f ? -1.0f : 1.0f; + + triangle.n0 = normal; + triangle.n1 = normal; + triangle.n2 = normal; + + triangle.uv0 = vec2(triangle.v0.x, triangle.v0.z); + triangle.uv1 = vec2(triangle.v1.x, triangle.v1.z); + triangle.uv2 = vec2(triangle.v2.x, triangle.v2.z); + + triangle.materialIdx = int32_t(materialIdxData[indices[i * 3]]); + + triangles[i] = triangle; + } + + Graphics::ASGeometryRegion geometryRegions[] = {{ + .indexCount = indices.size(), + .indexOffset = 0, + .opaque = true + }}; + + blas = CreateRef(); + blas->Build(triangles, materials, vertexBuffer, indexBuffer, geometryRegions); + + if (Graphics::GraphicsDevice::DefaultDevice->support.hardwareRayTracing) { + Graphics::ASBuilder asBuilder; + std::vector> blases = { blas->blas }; + asBuilder.BuildBLAS(blases); + + blas->blas = blases.front(); + blas->needsBvhRefresh = false; + } + + } + } } \ No newline at end of file diff --git a/src/engine/terrain/TerrainStorageCell.h b/src/engine/terrain/TerrainStorageCell.h index dbf3f7213..55501a260 100644 --- a/src/engine/terrain/TerrainStorageCell.h +++ b/src/engine/terrain/TerrainStorageCell.h @@ -3,6 +3,8 @@ #include "../System.h" #include "../Material.h" #include "../physics/ShapesManager.h" +#include "../raytracing/BLAS.h" +#include "../volume/AABB.h" #include @@ -23,23 +25,29 @@ namespace Atlas { bool IsLoaded(); + void BuildBVH(float stretchFactor, float heightFactor); + int32_t x = 0; int32_t y = 0; int32_t LoD = 0; vec2 position; + Volume::AABB aabb; + mat3x4 inverseMatrix; + Physics::ShapeRef shape; + Ref blas; std::vector heightData; + std::vector materialIdxData; + std::vector normalData; - Texture::Texture2D heightField; - Texture::Texture2D normalMap; - Texture::Texture2D splatMap; - Texture::Texture2D diffuseMap; + Ref heightField = nullptr; + Ref normalMap = nullptr; + Ref splatMap = nullptr; - private: - TerrainStorage* const storage; + TerrainStorage* storage = nullptr; }; diff --git a/src/engine/texture/Texture.cpp b/src/engine/texture/Texture.cpp index cda10ef27..9ca2ec3d0 100644 --- a/src/engine/texture/Texture.cpp +++ b/src/engine/texture/Texture.cpp @@ -14,10 +14,11 @@ namespace Atlas { std::unordered_map> Texture::samplerMap; Texture::Texture(int32_t width, int32_t height, int32_t depth, VkFormat format, - Wrapping wrapping, Filtering filtering) : width(width), height(height), depth(depth), + Wrapping wrapping, Filtering filtering, bool dedicatedMemory) + : width(width), height(height), depth(depth), wrapping(wrapping), filtering(filtering), format(format) { - Reallocate(Graphics::ImageType::Image2D, width, height, depth, filtering, wrapping); + Reallocate(Graphics::ImageType::Image2D, width, height, depth, filtering, wrapping, dedicatedMemory); RecreateSampler(filtering, wrapping); } @@ -93,18 +94,21 @@ namespace Atlas { } void Texture::Reallocate(Graphics::ImageType imageType, int32_t width, int32_t height, int32_t depth, - Filtering filtering, Wrapping wrapping, bool dedicatedMemory) { + Filtering filtering, Wrapping wrapping, bool dedicatedMemory, bool usedForRenderTarget) { auto graphicsDevice = Graphics::GraphicsDevice::DefaultDevice; this->width = width; this->height = height; this->depth = depth; + this->dedicatedMemory = dedicatedMemory; + this->usedForRenderTarget = usedForRenderTarget; + channels = int32_t(Graphics::GetFormatChannels(format)); bool generateMipMaps = filtering == Filtering::MipMapLinear || filtering == Filtering::MipMapNearest || filtering == Filtering::Anisotropic; - bool depthFormat = format == VK_FORMAT_D32_SFLOAT || format == VK_FORMAT_D16_UNORM || + bool depthFormat = format == VK_FORMAT_D32_SFLOAT || format == VK_FORMAT_D16_UNORM || format == VK_FORMAT_D16_UNORM_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; VkImageUsageFlags additionalUsageFlags = {}; @@ -123,6 +127,14 @@ namespace Atlas { VkImageAspectFlags aspectFlag = depthFormat ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + VmaPool* vmaPool = nullptr; + if (usedForRenderTarget) { + vmaPool = &graphicsDevice->memoryManager->highPriorityRenderTargetPool; + } + else if (dedicatedMemory) { + vmaPool = &graphicsDevice->memoryManager->highPriorityMemoryPool; + } + auto imageDesc = Graphics::ImageDesc { .usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | additionalUsageFlags, @@ -134,7 +146,7 @@ namespace Atlas { .layers = arrayType ? uint32_t(depth) : 1, .format = format, .mipMapping = generateMipMaps, - .dedicatedMemory = true + .dedicatedMemoryPool = vmaPool }; image = graphicsDevice->CreateImage(imageDesc); diff --git a/src/engine/texture/Texture.h b/src/engine/texture/Texture.h index f30ae1406..6fa0128a8 100644 --- a/src/engine/texture/Texture.h +++ b/src/engine/texture/Texture.h @@ -45,7 +45,8 @@ namespace Atlas { * @param filtering The filtering of the texture. */ Texture(int32_t width, int32_t height, int32_t depth, VkFormat format, - Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Nearest); + Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Nearest, + bool dedicatedMemory = false); /** * Binds the image and sampler of the texture to the specified binding point @@ -124,6 +125,9 @@ namespace Atlas { Wrapping wrapping = Wrapping::Repeat; Filtering filtering = Filtering::Nearest; + bool dedicatedMemory = false; + bool usedForRenderTarget = false; + VkFormat format = {}; protected: @@ -131,7 +135,8 @@ namespace Atlas { int32_t width, int32_t height, int32_t depth); void Reallocate(Graphics::ImageType imageType, int32_t width, int32_t height, - int32_t depth, Filtering filtering, Wrapping wrapping, bool dedicatedMemory = false); + int32_t depth, Filtering filtering, Wrapping wrapping, + bool dedicatedMemory = false, bool usedForRenderTarget = false); void RecreateSampler(Filtering filtering, Wrapping wrapping); diff --git a/src/engine/texture/Texture2D.cpp b/src/engine/texture/Texture2D.cpp index b59ebf790..b6f569f6a 100644 --- a/src/engine/texture/Texture2D.cpp +++ b/src/engine/texture/Texture2D.cpp @@ -4,17 +4,19 @@ namespace Atlas { namespace Texture { - Texture2D::Texture2D(int32_t width, int32_t height, VkFormat format, Wrapping wrapping, Filtering filtering) { + Texture2D::Texture2D(int32_t width, int32_t height, VkFormat format, Wrapping wrapping, Filtering filtering, + bool dedicatedMemory, bool usedForRenderTarget) { this->format = format; - Reallocate(Graphics::ImageType::Image2D, width, height, 1, filtering, wrapping); + Reallocate(Graphics::ImageType::Image2D, width, height, 1, filtering, wrapping, dedicatedMemory, usedForRenderTarget); RecreateSampler(filtering, wrapping); } Texture2D::Texture2D(const std::string& filename, bool colorSpaceConversion, - Wrapping wrapping, Filtering filtering, int32_t forceChannels) { + Wrapping wrapping, Filtering filtering, int32_t forceChannels, + bool dedicatedMemory, bool usedForRenderTarget) { auto image = Loader::ImageLoader::LoadImage(filename, colorSpaceConversion, forceChannels); @@ -38,7 +40,8 @@ namespace Atlas { if (width != this->width || height != this->height) { - Reallocate(Graphics::ImageType::Image2D, width, height, 1, filtering, wrapping); + Reallocate(Graphics::ImageType::Image2D, width, height, 1, + filtering, wrapping, dedicatedMemory, usedForRenderTarget); } diff --git a/src/engine/texture/Texture2D.h b/src/engine/texture/Texture2D.h index 8fe6c8520..215892a8d 100644 --- a/src/engine/texture/Texture2D.h +++ b/src/engine/texture/Texture2D.h @@ -27,7 +27,7 @@ namespace Atlas { * @param filtering The filtering of the texture. */ Texture2D(int32_t width, int32_t height, VkFormat format, Wrapping wrapping = Wrapping::Repeat, - Filtering filtering = Filtering::Nearest); + Filtering filtering = Filtering::Nearest, bool dedicatedMemory = false, bool usedForRenderTarget = false); /** * Constructs a Texture2D object from an image file. @@ -39,7 +39,7 @@ namespace Atlas { */ explicit Texture2D(const std::string& filename, bool colorSpaceConversion = true, Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Anisotropic, - int32_t forceChannels = 0); + int32_t forceChannels = 0, bool dedicatedMemory = false, bool usedForRenderTarget = false); /** * Constructs a Texture2D object from an image object. @@ -79,7 +79,7 @@ namespace Atlas { private: template void InitializeInternal(const Ref>& image, Wrapping wrapping, - Filtering filtering); + Filtering filtering, bool dedicatedMemory = false, bool usedForRenderTarget = false); }; @@ -126,7 +126,8 @@ namespace Atlas { } template - void Texture2D::InitializeInternal(const Ref>& image, Wrapping wrapping, Filtering filtering) { + void Texture2D::InitializeInternal(const Ref>& image, Wrapping wrapping, Filtering filtering, + bool dedicatedMemory, bool usedForRenderTarget) { // RGB images are mostly not supported if (image->channels == 3) { @@ -155,7 +156,7 @@ namespace Atlas { } } - Reallocate(Graphics::ImageType::Image2D, image->width, image->height, 1, filtering, wrapping); + Reallocate(Graphics::ImageType::Image2D, image->width, image->height, 1, filtering, wrapping, dedicatedMemory, usedForRenderTarget); RecreateSampler(filtering, wrapping); SetData(image->GetData()); diff --git a/src/engine/texture/Texture2DArray.cpp b/src/engine/texture/Texture2DArray.cpp index 75453701e..9266b9b5e 100644 --- a/src/engine/texture/Texture2DArray.cpp +++ b/src/engine/texture/Texture2DArray.cpp @@ -7,10 +7,10 @@ namespace Atlas { namespace Texture { Texture2DArray::Texture2DArray(int32_t width, int32_t height, int32_t layers, VkFormat format, - Wrapping wrapping, Filtering filtering) { + Wrapping wrapping, Filtering filtering, bool dedicatedMemory, bool usedForRenderTarget) { this->format = format; - Reallocate(Graphics::ImageType::Image2DArray, width, height, layers, filtering, wrapping); + Reallocate(Graphics::ImageType::Image2DArray, width, height, layers, filtering, wrapping, dedicatedMemory, usedForRenderTarget); RecreateSampler(filtering, wrapping); } @@ -59,7 +59,8 @@ namespace Atlas { if (width != this->width || height != this->height || layers != this->depth) { - Reallocate(Graphics::ImageType::Image2DArray, width, height, layers, filtering, wrapping); + Reallocate(Graphics::ImageType::Image2DArray, width, height, layers, + filtering, wrapping, dedicatedMemory, usedForRenderTarget); } diff --git a/src/engine/texture/Texture2DArray.h b/src/engine/texture/Texture2DArray.h index a1b73283b..fb279bff4 100644 --- a/src/engine/texture/Texture2DArray.h +++ b/src/engine/texture/Texture2DArray.h @@ -25,7 +25,8 @@ namespace Atlas { * @param filtering The filtering of the texture. */ Texture2DArray(int32_t width, int32_t height, int32_t layers, VkFormat format, - Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Nearest); + Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Nearest, + bool dedicatedMemory = false, bool usedForRenderTarget = false); /** * Sets the data of the texture object. diff --git a/src/engine/texture/Texture3D.cpp b/src/engine/texture/Texture3D.cpp index 179313b95..416c18f7b 100644 --- a/src/engine/texture/Texture3D.cpp +++ b/src/engine/texture/Texture3D.cpp @@ -5,10 +5,10 @@ namespace Atlas { namespace Texture { Texture3D::Texture3D(int32_t width, int32_t height, int32_t depth, VkFormat format, - Wrapping wrapping, Filtering filtering) { + Wrapping wrapping, Filtering filtering, bool dedicatedMemory, bool usedForRenderTarget) { this->format = format; - Reallocate(Graphics::ImageType::Image3D, width, height, depth, filtering, wrapping); + Reallocate(Graphics::ImageType::Image3D, width, height, depth, filtering, wrapping, dedicatedMemory, usedForRenderTarget); RecreateSampler(filtering, wrapping); } @@ -18,7 +18,8 @@ namespace Atlas { if (width != this->width || height != this->height || depth != this->depth) { - Reallocate(Graphics::ImageType::Image3D, width, height, depth, filtering, wrapping); + Reallocate(Graphics::ImageType::Image3D, width, height, depth, + filtering, wrapping, dedicatedMemory, usedForRenderTarget); } diff --git a/src/engine/texture/Texture3D.h b/src/engine/texture/Texture3D.h index 62eb820cd..a9f8362c8 100644 --- a/src/engine/texture/Texture3D.h +++ b/src/engine/texture/Texture3D.h @@ -24,7 +24,8 @@ namespace Atlas { * @param filtering The filtering of the texture. */ Texture3D(int32_t width, int32_t height, int32_t depth, VkFormat format, - Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Nearest); + Wrapping wrapping = Wrapping::Repeat, Filtering filtering = Filtering::Nearest, + bool dedicatedMemory = false, bool usedForRenderTarget = false); /** * Resizes the texture diff --git a/src/engine/tools/TerrainTool.cpp b/src/engine/tools/TerrainTool.cpp index 7634c340c..71fcc1912 100644 --- a/src/engine/tools/TerrainTool.cpp +++ b/src/engine/tools/TerrainTool.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace Atlas { @@ -88,8 +89,8 @@ namespace Atlas { } } - cell->heightField.SetData(cellHeightData); - cell->splatMap.SetData(cellSplatData); + cell->heightField->SetData(cellHeightData); + cell->splatMap->SetData(cellSplatData); } } @@ -184,8 +185,8 @@ namespace Atlas { } } - cell->heightField.SetData(cellHeightData); - cell->splatMap.SetData(cellSplatData); + cell->heightField->SetData(cellHeightData); + cell->splatMap->SetData(cellSplatData); } } @@ -215,7 +216,7 @@ namespace Atlas { for (int32_t i = 0; i < tileSideCount; i++) { for (int32_t j = 0; j < tileSideCount; j++) { auto cell = terrain->storage.GetCell(i, j, terrain->LoDCount - 1); - auto cellSplatData = cell->splatMap.GetData(); + auto cellSplatData = cell->splatMap->GetData(); // Now copy a tile of the original image // We make sure that every tile has the same size @@ -378,9 +379,9 @@ namespace Atlas { } */ - cell->normalMap.SetData(tileNormalData); - cell->heightField.SetData(tileHeightData); - cell->splatMap.SetData(tileSplatData); + cell->normalMap->SetData(tileNormalData); + cell->heightField->SetData(tileHeightData); + cell->splatMap->SetData(tileSplatData); } } @@ -416,8 +417,8 @@ namespace Atlas { bottomLeft, bottomMiddle, bottomRight}; // Now bring all height data into one array (we assume that all tiles have the same size) - int32_t width = middleMiddle->heightField.width - 1; - int32_t height = middleMiddle->heightField.height - 1; + int32_t width = middleMiddle->heightField->width - 1; + int32_t height = middleMiddle->heightField->height - 1; std::vector heights(width * height * 9); @@ -511,7 +512,7 @@ namespace Atlas { cellHeightData[k] = (uint16_t)(cell->heightData[k] * 65535.0f); } - cell->heightField.SetData(cellHeightData); + cell->heightField->SetData(cellHeightData); } @@ -544,8 +545,8 @@ namespace Atlas { bottomLeft, bottomMiddle, bottomRight}; // Now bring all height data into one array (we assume that all tiles have the same size) - int32_t width = middleMiddle->heightField.width - 1; - int32_t height = middleMiddle->heightField.height - 1; + int32_t width = middleMiddle->heightField->width - 1; + int32_t height = middleMiddle->heightField->height - 1; std::vector heights(width * height * 9); @@ -660,7 +661,7 @@ namespace Atlas { cellHeightData[k] = (uint16_t)(cell->heightData[k] * 65535.0f); } - cell->heightField.SetData(cellHeightData); + cell->heightField->SetData(cellHeightData); } } @@ -693,8 +694,8 @@ namespace Atlas { std::vector cellDatas[9]; // Now bring all height data into one array (we assume that all tiles have the same size) - int32_t width = middleMiddle->splatMap.width - 1; - int32_t height = middleMiddle->splatMap.height - 1; + int32_t width = middleMiddle->splatMap->width - 1; + int32_t height = middleMiddle->splatMap->height - 1; std::vector combinedSplatMap(width * height * 9); @@ -708,7 +709,7 @@ namespace Atlas { if (cell == nullptr) continue; - cellDatas[i * 3 + j] = cell->splatMap.GetData(); + cellDatas[i * 3 + j] = cell->splatMap->GetData(); auto& splatData = cellDatas[i * 3 + j]; for (int32_t k = 0; k < height + 1; k++) { @@ -787,7 +788,7 @@ namespace Atlas { } } - cell->splatMap.SetData(splatData); + cell->splatMap->SetData(splatData); } @@ -812,7 +813,7 @@ namespace Atlas { for (int32_t i = 0; i < tileSideCount; i++) { for (int32_t j = 0; j < tileSideCount; j++) { auto cell = terrain->storage.GetCell(i, j, terrain->LoDCount - 1); - auto cellSplatData = cell->splatMap.GetData(); + auto cellSplatData = cell->splatMap->GetData(); // Now copy a tile of the original image // We make sure that every tile has the same size diff --git a/src/engine/volume/BVH.cpp b/src/engine/volume/BVH.cpp index 233198c5b..8dc5c8895 100644 --- a/src/engine/volume/BVH.cpp +++ b/src/engine/volume/BVH.cpp @@ -55,7 +55,7 @@ namespace Atlas { } - BVH::BVH(const std::vector& aabbs, bool parallelBuild) { + BVH::BVH(const std::vector& aabbs, bool parallelBuild) { refs.resize(aabbs.size()); for (size_t i = 0; i < refs.size(); i++) { @@ -63,13 +63,48 @@ namespace Atlas { refs[i].aabb = aabbs[i]; } + /* + // This can be faster when it is purely used for terrain BLASES + for (size_t j = 0; j < 0; j++) { + float avgLongestAxis = 0.0f; + + for (size_t i = 0; i < refs.size(); i++) { + auto size = refs[i].aabb.GetSize(); + auto maxAxis = size.x > size.y ? + (size.x > size.z ? 0 : 2) : + (size.y > size.z ? 1 : 2); + + avgLongestAxis += size[maxAxis]; + } + + auto refCount = refs.size(); + avgLongestAxis /= float(refCount); + + for (size_t i = 0; i < refs.size(); i++) { + auto size = refs[i].aabb.GetSize(); + auto maxAxis = size.x > size.y ? + (size.x > size.z ? 0 : 2) : + (size.y > size.z ? 1 : 2); + + if (size[maxAxis] * 0.01f > avgLongestAxis) { + auto ref = refs[i]; + + refs[i].aabb.max[maxAxis] = refs[i].aabb.min[maxAxis] + size[maxAxis] * 0.5f; + ref.aabb.min[maxAxis] = refs[i].aabb.max[maxAxis]; + + refs.push_back(ref); + } + } + } + */ + // Calculate initial aabb of root AABB aabb(glm::vec3(std::numeric_limits::max()), glm::vec3(-std::numeric_limits::max())); for (auto& ref : refs) aabb.Grow(aabbs[ref.idx]); - auto builder = new BVHBuilder(aabb, 0, refs.size(), 64); + auto builder = new BVHBuilder(aabb, 0, refs.size(), 128); JobGroup group { JobPriority::Medium }; builder->Build(refs, group, parallelBuild); @@ -307,7 +342,7 @@ namespace Atlas { refs.clear(); refs.shrink_to_fit(); - if (depth <= 6 && parallelBuild) { + if (depth <= 8 && parallelBuild) { auto leftRefSize = leftRefs.size(), rightRefSize = rightRefs.size(); auto leftLambda = [=, &jobGroup, leftRefs = std::move(leftRefs)](JobData&) { auto refs = std::move(leftRefs); @@ -320,11 +355,9 @@ namespace Atlas { rightChild = new BVHBuilder(split.rightAABB, depth + 1, refs.size(), binCount); rightChild->Build(refs, jobGroup, parallelBuild); }; - - if (leftRefSize > 0) - JobSystem::Execute(jobGroup, leftLambda); - if (rightRefSize > 0) - JobSystem::Execute(jobGroup, rightLambda); + + JobSystem::Execute(jobGroup, leftLambda); + JobSystem::Execute(jobGroup, rightLambda); } else { if (leftRefs.size()) { @@ -343,7 +376,7 @@ namespace Atlas { void BVHBuilder::Build(std::vector& refs, JobGroup& jobGroup, bool parallelBuild) { // Create leaf node - if (refs.size() == 1) { + if ((refs.size() == 1 || depth >= 24) && depth > 0) { CreateLeaf(refs); return; } @@ -372,7 +405,7 @@ namespace Atlas { refs.clear(); refs.shrink_to_fit(); - if (depth <= 6 && parallelBuild) { + if (depth <= 8 && parallelBuild) { auto leftRefSize = leftRefs.size(), rightRefSize = rightRefs.size(); auto leftLambda = [=, &jobGroup, leftRefs = std::move(leftRefs)](JobData&) { auto refs = std::move(leftRefs); @@ -386,10 +419,8 @@ namespace Atlas { rightChild->Build(refs, jobGroup, parallelBuild); }; - if (leftRefSize > 0) - JobSystem::Execute(jobGroup, leftLambda); - if (rightRefSize > 0) - JobSystem::Execute(jobGroup, rightLambda); + JobSystem::Execute(jobGroup, leftLambda); + JobSystem::Execute(jobGroup, rightLambda); } else { if (leftRefs.size()) { @@ -416,6 +447,9 @@ namespace Atlas { refs[refs.size() - 1].endOfNode = true; } else { + if (!leftChild || !rightChild) + return; + const auto nodeIdx = nodes.size(); nodes.push_back(BVHNode()); @@ -444,7 +478,7 @@ namespace Atlas { BVHBuilder::Split BVHBuilder::FindObjectSplit(std::vector& refs) { Split split; - const auto depthBinCount = std::max(binCount / (depth + 1), 16u); + const auto depthBinCount = std::max(binCount / (depth + 1), 8u); std::vector bins(depthBinCount); std::vector rightAABBs(bins.size()); @@ -530,7 +564,7 @@ namespace Atlas { void BVHBuilder::PerformObjectSplit(std::vector& refs, std::vector& rightRefs, std::vector& leftRefs, Split& split) { - const auto depthBinCount = std::max(binCount / (depth + 1), 16u); + const auto depthBinCount = std::max(binCount / (depth + 1), 8u); auto start = aabb.min[split.axis]; auto stop = aabb.max[split.axis]; diff --git a/src/engine/volume/Frustum.cpp b/src/engine/volume/Frustum.cpp index f6b9ad763..a729d5aae 100644 --- a/src/engine/volume/Frustum.cpp +++ b/src/engine/volume/Frustum.cpp @@ -4,7 +4,7 @@ namespace Atlas { namespace Volume { - Frustum::Frustum(const std::vector& corners) { + Frustum::Frustum(const std::array& corners) { Resize(corners); @@ -16,7 +16,7 @@ namespace Atlas { } - void Frustum::Resize(const std::vector& corners) { + void Frustum::Resize(const std::array& corners) { this->corners = corners; planes[NEAR_PLANE] = Plane(corners[4], corners[5], corners[7]); @@ -80,6 +80,7 @@ namespace Atlas { std::vector Frustum::GetPlanes() const { std::vector planes; + planes.reserve(6); for (uint8_t i = 0; i < 6; i++) { planes.push_back(vec4(this->planes[i].normal, @@ -90,7 +91,7 @@ namespace Atlas { } - std::vector Frustum::GetCorners() const { + std::array Frustum::GetCorners() const { return corners; @@ -110,12 +111,11 @@ namespace Atlas { vec3(1.0f, -1.0f, 1.0f) }; - corners.clear(); auto inverseMatrix = glm::inverse(matrix); for (uint8_t i = 0; i < 8; i++) { auto homogenous = inverseMatrix * vec4(vectors[i], 1.0f); - corners.push_back(vec3(homogenous) / homogenous.w); + corners[i] = vec3(homogenous) / homogenous.w; } } diff --git a/src/engine/volume/Frustum.h b/src/engine/volume/Frustum.h index c90b604cb..8ac81a636 100644 --- a/src/engine/volume/Frustum.h +++ b/src/engine/volume/Frustum.h @@ -5,6 +5,8 @@ #include "AABB.h" #include +#include +#include namespace Atlas { @@ -26,7 +28,7 @@ namespace Atlas { * Far plane: Upper left, upper right, bottom left, bottom right * Near plane: Upper left, upper right, bottom left, bottom right */ - explicit Frustum(const std::vector& corners); + explicit Frustum(const std::array& corners); /** * Constructs a Frustum object. @@ -42,7 +44,7 @@ namespace Atlas { * Far plane: Upper left, upper right, bottom left, bottom right * Near plane: Upper left, upper right, bottom left, bottom right */ - void Resize(const std::vector& corners); + void Resize(const std::array& corners); /** * Resizes the frustum. @@ -80,7 +82,7 @@ namespace Atlas { * Far plane: Upper left, upper right, bottom left, bottom right * Near plane: Upper left, upper right, bottom left, bottom right */ - std::vector GetCorners() const; + std::array GetCorners() const; private: void CalculateCorners(const mat4& matrix); @@ -104,7 +106,7 @@ namespace Atlas { float distance = 0.0f; }; - std::vector corners; + std::array corners; Plane planes[6]; }; diff --git a/src/tests/App.cpp b/src/tests/App.cpp index 07a54e8c4..054678ac0 100644 --- a/src/tests/App.cpp +++ b/src/tests/App.cpp @@ -17,17 +17,16 @@ void App::LoadContent(AppConfiguration config) { this->config = config; // Use lower resolution, we care only about correctness - renderTarget = Atlas::CreateRef(320, 240); - pathTraceTarget = Atlas::CreateRef(320, 240); + renderTarget = Atlas::CreateRef(240, 160); viewport = Atlas::CreateRef(0, 0, renderTarget->GetWidth(), renderTarget->GetHeight()); - auto icon = Atlas::Texture::Texture2D("icon.png"); + auto icon = Atlas::ResourceManager::GetOrLoadResource("icon.png"); window.SetIcon(&icon); loadingTexture = Atlas::CreateRef("loading.png"); - font = Atlas::CreateRef("font/roboto.ttf", 22.0f, 5); + font = Atlas::ResourceManager::GetOrLoadResource("font/roboto.ttf", 22.0f, 5); scene = Atlas::CreateRef("testscene", glm::vec3(-2048.0f), glm::vec3(2048.0f)); @@ -56,14 +55,18 @@ void App::LoadContent(AppConfiguration config) { Atlas::PipelineManager::EnableHotReload(); - directionalLightEntity = scene->CreateEntity(); - auto& directionalLight = directionalLightEntity.AddComponent(LightType::DirectionalLight); + scene->postProcessing.fsr2 = config.fsr; - directionalLight.properties.directional.direction = glm::vec3(0.0f, -1.0f, 0.33f); - directionalLight.color = glm::vec3(255, 236, 209) / 255.0f; - directionalLight.AddDirectionalShadow(200.0f, 3.0f, 4096, 0.025f, - glm::vec3(0.0f), glm::vec4(-100.0f, 100.0f, -70.0f, 120.0f)); - directionalLight.isMain = true; + if (config.light) { + auto directionalLightEntity = scene->CreateEntity(); + auto& directionalLight = directionalLightEntity.AddComponent(LightType::DirectionalLight); + + directionalLight.properties.directional.direction = glm::vec3(0.0f, -1.0f, 0.33f); + directionalLight.color = glm::vec3(255, 236, 209) / 255.0f; + directionalLight.AddDirectionalShadow(200.0f, 3.0f, 4096, 0.025f, + glm::vec3(0.0f), glm::vec4(-100.0f, 100.0f, -70.0f, 120.0f)); + directionalLight.isMain = true; + } scene->ao = Atlas::CreateRef(16); @@ -79,6 +82,10 @@ void App::LoadContent(AppConfiguration config) { scene->irradianceVolume->strength = 1.5f; } + if (config.rtgi) { + scene->rtgi = Atlas::CreateRef(); + } + if (config.ssgi) { scene->ssgi = Atlas::CreateRef(); } @@ -89,6 +96,9 @@ void App::LoadContent(AppConfiguration config) { scene->fog->density = 0.0068f; scene->fog->heightFalloff = 0.0284f; scene->fog->height = 0.0f; + + scene->fog->rayMarching = config.volumetric; + scene->fog->localLights = config.localVolumetric; } if (config.clouds) { @@ -113,17 +123,9 @@ void App::LoadContent(AppConfiguration config) { scene->sss = Atlas::CreateRef(); } - if (config.fog) { - if (config.volumetric) { - scene->fog->rayMarching = true; - } else { - scene->fog->rayMarching = false; - } - } - if (config.ocean) { scene->ocean = Atlas::CreateRef(9, 4096.0f, - glm::vec3(0.0f, 5.0f, 0.0f), 512, 86); + glm::vec3(0.0f, 5.0f, 0.0f), 128, 86); } scene->physicsWorld = Atlas::CreateRef(); @@ -152,6 +154,7 @@ void App::Update(float deltaTime) { if (sceneReload) { UnloadScene(); LoadScene(); + scene->WaitForAsyncWorkCompletion(); sceneReload = false; } @@ -209,6 +212,7 @@ void App::Render(float deltaTime) { if (!loadingComplete) { DisplayLoadingScreen(deltaTime); + scene->WaitForAsyncWorkCompletion(); return; } @@ -227,12 +231,14 @@ void App::Render(float deltaTime) { window.Maximize(); } + viewport->Set(0, 0, renderTarget->GetWidth(), renderTarget->GetHeight()); + if (config.exampleRenderer) { exampleRenderer.Render(camera); } else if (pathTrace) { - viewport->Set(0, 0, pathTraceTarget->GetWidth(), pathTraceTarget->GetHeight()); - mainRenderer->PathTraceScene(viewport, pathTraceTarget, scene); + + mainRenderer->PathTraceScene(viewport, renderTarget, scene); } else { mainRenderer->RenderScene(viewport, renderTarget, scene); @@ -267,7 +273,7 @@ void App::DisplayLoadingScreen(float deltaTime) { rotation += deltaTime * abs(sin(Atlas::Clock::Get())) * 10.0f; mainRenderer->textureRenderer.RenderTexture2D(commandList, viewport, - loadingTexture.get(), x, y, width, height, rotation); + loadingTexture.Get().get(), x, y, width, height, rotation); float textWidth, textHeight; font->ComputeDimensions("Loading...", 2.0f, &textWidth, &textHeight); @@ -276,7 +282,7 @@ void App::DisplayLoadingScreen(float deltaTime) { y = windowSize.y / 2 - textHeight / 2 + float(loadingTexture->height) + 20.0f; viewport->Set(0, 0, windowSize.x, windowSize.y); - mainRenderer->textRenderer.Render(commandList, viewport, font, + mainRenderer->textRenderer.Render(commandList, viewport, font.Get(), "Loading...", x, y, glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), 2.0f); commandList->EndRenderPass(); @@ -356,28 +362,6 @@ void App::CheckLoadScene() { if (!scene->IsFullyLoaded() || loadingComplete) return; - static Atlas::JobGroup buildBvhGroup; - static bool groupStarted = false; - - auto buildRTStructure = [&](Atlas::JobData) { - auto sceneMeshes = scene->GetMeshes(); - - for (const auto& mesh : sceneMeshes) { - - Atlas::JobSystem::Execute(buildBvhGroup, [mesh](Atlas::JobData&) { mesh->BuildBVH(); }); - - } - }; - - if (!groupStarted) { - Atlas::JobSystem::Execute(buildBvhGroup, buildRTStructure); - groupStarted = true; - return; - } - - if (!buildBvhGroup.HasFinished()) - return; - auto sceneAABB = Atlas::Volume::AABB(glm::vec3(std::numeric_limits::max()), glm::vec3(-std::numeric_limits::max())); @@ -401,7 +385,6 @@ void App::CheckLoadScene() { void App::SetResolution(int32_t width, int32_t height) { renderTarget->Resize(width, height); - pathTraceTarget->Resize(width, height); } diff --git a/src/tests/App.h b/src/tests/App.h index a7bc33409..c9be26f08 100644 --- a/src/tests/App.h +++ b/src/tests/App.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -21,9 +22,13 @@ struct AppConfiguration { bool sharpen = false; bool ssgi = true; bool ddgi = true; + bool rtgi = true; bool reflection = true; bool volumetric = true; + bool localVolumetric = true; bool ocean = true; + bool light = true; + bool fsr = true; bool resize = false; bool recreateSwapchain = false; bool minimizeWindow = false; @@ -57,18 +62,16 @@ class App : public Atlas::EngineInstance { void SetResolution(int32_t width, int32_t height); - Ref pathTraceTarget; Ref renderTarget; Ref viewport; - Ref font; + Atlas::ResourceHandle font; Ref scene; std::vector> meshes; Atlas::Scene::Entity cameraEntity; - Atlas::Scene::Entity directionalLightEntity; std::vector entities; Atlas::Lighting::EnvironmentProbe probe; @@ -76,7 +79,7 @@ class App : public Atlas::EngineInstance { Atlas::Input::MouseHandler mouseHandler; Atlas::Input::KeyboardHandler keyboardHandler; - Ref loadingTexture; + Atlas::ResourceHandle loadingTexture; Atlas::Renderer::ExampleRenderer exampleRenderer; diff --git a/src/tests/Main.cpp b/src/tests/Main.cpp index 8f383dfd4..3e03fe87c 100644 --- a/src/tests/Main.cpp +++ b/src/tests/Main.cpp @@ -29,8 +29,6 @@ class EngineEndToEndTest : public testing::TestWithParam { void TearDown() override { delete engineInstance; - Atlas::PipelineManager::Clear(); - graphicsDevice->ForceMemoryCleanup(); } @@ -98,14 +96,18 @@ auto testingValues = testing::Values( AppConfiguration { .ocean = false }, #ifdef AE_BINDLESS AppConfiguration { .ddgi = false }, + AppConfiguration { .rtgi = false }, AppConfiguration { .reflection = false }, #endif AppConfiguration { .volumetric = false }, + AppConfiguration { .localVolumetric = false }, AppConfiguration { .sharpen = false }, + AppConfiguration { .light = false }, + AppConfiguration { .fsr = false }, AppConfiguration { .recreateSwapchain = true }, AppConfiguration { .resize = true }, AppConfiguration { .exampleRenderer = true }, - AppConfiguration{ .minimizeWindow = true } + AppConfiguration { .minimizeWindow = true } ); INSTANTIATE_TEST_SUITE_P(DemoTestSuite, EngineEndToEndTest, testingValues); diff --git a/vcpkg.json b/vcpkg.json index e79971e89..6535129da 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,9 +1,9 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", "name": "atlas-engine", - "version-string": "0.2.0", + "version-string": "0.2.1", "port-version": 0, - "builtin-baseline": "821100d967e1737d96414a308e3f7cbe0d1abf18", + "builtin-baseline": "a993be073c6baea8674117f3eaed6e2d2486aaae", "homepage": "https://github.com/tippesi/Atlas-Engine", "description": "Cross platform toy render engine supporting physically based rendering and software ray tracing", "dependencies": [ @@ -38,6 +38,7 @@ "gtest", "joltphysics", "imguizmo", + "implot", "lua", "sol2" ], @@ -68,7 +69,11 @@ }, { "name": "imgui", - "version": "1.90.7#1" + "version": "1.91.0#0" + }, + { + "name": "implot", + "version": "0.16#0" }, { "name": "vulkan-memory-allocator", @@ -84,7 +89,7 @@ }, { "name": "joltphysics", - "version": "5.0.0#1" + "version": "5.1.0#0" }, { "name": "lua",