Merge remote-tracking branch 'origin/master' into navigation-features
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# Redirect to our own Git LFS server
|
||||
[lfs]
|
||||
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
locksverify = false
|
||||
|
||||
BIN
Content/Shaders/AtmospherePreCompute.flax
(Stored with Git LFS)
BIN
Content/Shaders/AtmospherePreCompute.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Histogram.flax
(Stored with Git LFS)
BIN
Content/Shaders/Histogram.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/MotionBlur.flax
(Stored with Git LFS)
BIN
Content/Shaders/MotionBlur.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/PostProcessing.flax
(Stored with Git LFS)
BIN
Content/Shaders/PostProcessing.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
BIN
Content/Shaders/VolumetricFog.flax
(Stored with Git LFS)
Binary file not shown.
@@ -2,93 +2,56 @@
|
||||
|
||||
rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
rem Make sure the batch file exists in the root folder.
|
||||
if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_BatchFileInWrongLocation
|
||||
if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_InvalidLocation
|
||||
|
||||
rem Get the path to MSBuild executable.
|
||||
call "Development\Scripts\Windows\GetMSBuildPath.bat"
|
||||
if errorlevel 1 goto Error_NoVisualStudioEnvironment
|
||||
|
||||
rem If using VS2017, check that NuGet package manager is installed.
|
||||
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto NoVsWhere
|
||||
|
||||
set MSBUILD_15_EXE=
|
||||
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto Compile
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
for %%j in (15.0, Current) do (
|
||||
if exist "%%i\MSBuild\%%j\Bin\MSBuild.exe" (
|
||||
set MSBUILD_15_EXE="%%i\MSBuild\%%j\Bin\MSBuild.exe"
|
||||
goto FoundMsBuild15
|
||||
set MSBUILD_PATH="%%i\MSBuild\%%j\Bin\MSBuild.exe"
|
||||
goto Compile
|
||||
)
|
||||
)
|
||||
)
|
||||
:FoundMsBuild15
|
||||
|
||||
set MSBUILD_15_EXE_WITH_NUGET=
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -requires Microsoft.VisualStudio.Component.NuGet -property installationPath') do (
|
||||
if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" (
|
||||
set MSBUILD_15_EXE_WITH_NUGET="%%i\MSBuild\15.0\Bin\MSBuild.exe"
|
||||
goto FoundMsBuild15WithNuget
|
||||
)
|
||||
)
|
||||
:FoundMsBuild15WithNuget
|
||||
|
||||
if not [%MSBUILD_15_EXE%] == [] (
|
||||
set MSBUILD_EXE=%MSBUILD_15_EXE%
|
||||
goto NoVsWhere
|
||||
)
|
||||
|
||||
if not [%MSBUILD_EXE%] == [%MSBUILD_15_EXE_WITH_NUGET%] goto Error_RequireNugetPackageManager
|
||||
|
||||
:NoVsWhere
|
||||
|
||||
rem Check to see if the build tool source files have changed. Some can be included conditionally based on NDA access.
|
||||
:Compile
|
||||
md Cache\Intermediate >nul 2>nul
|
||||
dir /s /b Source\Tools\Flax.Build\*.cs >Cache\Intermediate\Flax.Build.Files.txt
|
||||
fc /b Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul 2>nul
|
||||
if not errorlevel 1 goto SkipClean
|
||||
copy /y Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul
|
||||
%MSBUILD_EXE% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Clean
|
||||
:SkipClean
|
||||
%MSBUILD_EXE% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Build
|
||||
if errorlevel 1 goto Error_CompileFailed
|
||||
|
||||
rem Run the build tool using the provided arguments.
|
||||
copy /y Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul
|
||||
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Clean
|
||||
:SkipClean
|
||||
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Build
|
||||
if errorlevel 1 goto Error_CompilationFailed
|
||||
|
||||
Binaries\Tools\Flax.Build.exe %*
|
||||
if errorlevel 1 goto Error_FlaxBuildFailed
|
||||
|
||||
rem Done.
|
||||
exit /B 0
|
||||
|
||||
:Error_BatchFileInWrongLocation
|
||||
:Error_InvalidLocation
|
||||
echo.
|
||||
echo CallBuildTool ERROR: The batch file does not appear to be located in the root directory. This script must be run from within that directory.
|
||||
echo CallBuildTool ERROR: The script is in invalid directory.
|
||||
echo.
|
||||
goto Exit
|
||||
|
||||
:Error_NoVisualStudioEnvironment
|
||||
echo.
|
||||
echo CallBuildTool ERROR: We couldn't find a valid installation of Visual Studio. This program requires Visual Studio 2015. Please check that you have Visual Studio installed, then verify that the HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\InstallDir (or HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\14.0\InstallDir on 32-bit machines) registry value is set. Visual Studio configures this value when it is installed, and this program expects it to be set to the '\Common7\IDE\' sub-folder under a valid Visual Studio installation directory.
|
||||
echo CallBuildTool ERROR: Missing Visual Studio 2015 or newer.
|
||||
echo.
|
||||
goto Exit
|
||||
|
||||
:Error_RequireNugetPackageManager
|
||||
echo.
|
||||
echo CallBuildTool ERROR: NuGet Package Manager is requried to be installed to use %MSBUILD_EXE%. Please run the Visual Studio Installer and add it from the individual components list (in the 'Code Tools' category).
|
||||
echo.
|
||||
goto Exit
|
||||
|
||||
:Error_CompileFailed
|
||||
:Error_CompilationFailed
|
||||
echo.
|
||||
echo CallBuildTool ERROR: Failed to compile Flax.Build project.
|
||||
echo.
|
||||
goto Exit
|
||||
|
||||
:Error_FlaxBuildFailed
|
||||
echo.
|
||||
echo CallBuildTool ERROR: Flax.Build tool failed.
|
||||
echo.
|
||||
goto Exit
|
||||
|
||||
:Exit
|
||||
rem Exit with error.
|
||||
exit /B 1
|
||||
|
||||
@@ -2,72 +2,57 @@
|
||||
|
||||
rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
set MSBUILD_EXE=
|
||||
set MSBUILD_PATH=
|
||||
|
||||
rem Try to get the MSBuild 15 path using vswhere (see https://github.com/Microsoft/vswhere).
|
||||
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto no_vswhere
|
||||
if not exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" goto VsWhereNotFound
|
||||
for /f "delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -products * -requires Microsoft.Component.MSBuild -property installationPath') do (
|
||||
if exist "%%i\MSBuild\15.0\Bin\MSBuild.exe" (
|
||||
set MSBUILD_EXE="%%i\MSBuild\15.0\Bin\MSBuild.exe"
|
||||
goto Succeeded
|
||||
set MSBUILD_PATH="%%i\MSBuild\15.0\Bin\MSBuild.exe"
|
||||
goto End
|
||||
)
|
||||
)
|
||||
:no_vswhere
|
||||
:VsWhereNotFound
|
||||
|
||||
rem Check for MSBuild 15. This is installed alongside Visual Studio 2017, so we get the path relative to that.
|
||||
call :ReadInstallPath Microsoft\VisualStudio\SxS\VS7 15.0 MSBuild\15.0\bin\MSBuild.exe
|
||||
if not errorlevel 1 goto Succeeded
|
||||
|
||||
rem Try to get the MSBuild 14.0 path directly (see https://msdn.microsoft.com/en-us/library/hh162058(v=vs.120).aspx).
|
||||
if exist "%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe" (
|
||||
set MSBUILD_EXE="%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe"
|
||||
goto Succeeded
|
||||
set MSBUILD_PATH="%ProgramFiles(x86)%\MSBuild\14.0\bin\MSBuild.exe"
|
||||
goto End
|
||||
)
|
||||
|
||||
rem Try to get the MSBuild 14.0 path from the registry.
|
||||
call :ReadInstallPath Microsoft\MSBuild\ToolsVersions\14.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto Succeeded
|
||||
call :GetInstallPath Microsoft\VisualStudio\SxS\VS7 15.0 MSBuild\15.0\bin\MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\14.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\12.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
call :GetInstallPath Microsoft\MSBuild\ToolsVersions\4.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto End
|
||||
|
||||
rem Check for older versions of MSBuild. These are registered as separate versions in the registry.
|
||||
call :ReadInstallPath Microsoft\MSBuild\ToolsVersions\12.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto Succeeded
|
||||
call :ReadInstallPath Microsoft\MSBuild\ToolsVersions\4.0 MSBuildToolsPath MSBuild.exe
|
||||
if not errorlevel 1 goto Succeeded
|
||||
|
||||
rem Searching failed.
|
||||
exit /B 1
|
||||
|
||||
rem Searching done!
|
||||
:Succeeded
|
||||
:End
|
||||
exit /B 0
|
||||
|
||||
rem Subroutine to query the registry under HKCU/HKLM Win32/Wow64 software registry keys for a certain install directory.
|
||||
rem Arguments:
|
||||
rem %1 = Registry path under the 'SOFTWARE' registry key
|
||||
rem %2 = Value name
|
||||
rem %3 = Relative path under this directory to look for an installed executable.
|
||||
:ReadInstallPath
|
||||
:GetInstallPath
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%%3" (
|
||||
set MSBUILD_EXE="%%B%3"
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%3" (
|
||||
set MSBUILD_EXE="%%B%3"
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKCU\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%%3" (
|
||||
set MSBUILD_EXE="%%B%3"
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
for /f "tokens=2,*" %%A in ('REG.exe query HKLM\SOFTWARE\Wow6432Node\%1 /v %2 2^>Nul') do (
|
||||
if exist "%%B%3" (
|
||||
set MSBUILD_EXE="%%B%3"
|
||||
set MSBUILD_PATH="%%B%3"
|
||||
exit /B 0
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 0,
|
||||
"Build": 6213
|
||||
"Build": 6214
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
setlocal
|
||||
pushd
|
||||
pushd %~dp0
|
||||
echo Registering Flax Engine project files...
|
||||
|
||||
rem Check the current versions config
|
||||
@@ -20,7 +20,7 @@ goto Done
|
||||
|
||||
rem Register the location (append to the end)
|
||||
:notfound
|
||||
echo Location '%EngineLocation%' is not registered. Adding it to the lsit of engine versions.
|
||||
echo Location '%EngineLocation%' is not registered. Adding it to the list of engine versions.
|
||||
echo %EngineLocation%>>"%appdata%\Flax\Versions.txt"
|
||||
goto Done
|
||||
|
||||
|
||||
@@ -122,6 +122,25 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
/// </summary>
|
||||
public Panel ItemsContainer => _panel;
|
||||
|
||||
/// <summary>
|
||||
/// The auto sort.
|
||||
/// </summary>
|
||||
private bool _autosort;
|
||||
|
||||
/// <summary>
|
||||
/// The auto sort property.
|
||||
/// </summary>
|
||||
public bool AutoSort
|
||||
{
|
||||
get => _autosort;
|
||||
set
|
||||
{
|
||||
_autosort = value;
|
||||
if (_autosort)
|
||||
SortButtons();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContextMenu"/> class.
|
||||
/// </summary>
|
||||
@@ -137,6 +156,24 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts all <see cref="ContextMenuButton"/> alphabetically.
|
||||
/// </summary>
|
||||
/// <param name="force">Overrides <see cref="AutoSort"/> property.</param>
|
||||
public void SortButtons(bool force = false)
|
||||
{
|
||||
if (!_autosort && !force)
|
||||
return;
|
||||
_panel.Children.Sort((control, control1) =>
|
||||
{
|
||||
if (control is ContextMenuButton cmb && control1 is ContextMenuButton cmb1)
|
||||
return string.Compare(cmb.Text, cmb1.Text, StringComparison.OrdinalIgnoreCase);
|
||||
if (!(control is ContextMenuButton))
|
||||
return 1;
|
||||
return -1;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all the added items (buttons, separators, etc.).
|
||||
/// </summary>
|
||||
@@ -158,6 +195,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
{
|
||||
var item = new ContextMenuButton(this, text);
|
||||
item.Parent = _panel;
|
||||
SortButtons();
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -171,6 +209,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
{
|
||||
var item = new ContextMenuButton(this, text, shortKeys);
|
||||
item.Parent = _panel;
|
||||
SortButtons();
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -185,6 +224,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
var item = new ContextMenuButton(this, text);
|
||||
item.Parent = _panel;
|
||||
item.Clicked += clicked;
|
||||
SortButtons();
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -199,6 +239,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
var item = new ContextMenuButton(this, text);
|
||||
item.Parent = _panel;
|
||||
item.ButtonClicked += clicked;
|
||||
SortButtons();
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -214,6 +255,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
var item = new ContextMenuButton(this, text, shortKeys);
|
||||
item.Parent = _panel;
|
||||
item.Clicked += clicked;
|
||||
SortButtons();
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
// Direction: up
|
||||
isUp = true;
|
||||
locationSS.Y -= dpiSize.Y;
|
||||
|
||||
// Offset to fix sub-menu location
|
||||
if (parent is ContextMenu menu && menu._childCM != null)
|
||||
locationSS.Y += 30.0f * dpiScale;
|
||||
}
|
||||
if (monitorBounds.Right < rightBottomLocationSS.X)
|
||||
{
|
||||
|
||||
@@ -193,41 +193,64 @@ namespace FlaxEditor.GUI
|
||||
if (!_window.IsMaximized)
|
||||
{
|
||||
var winSize = RootWindow.Size * Platform.DpiScale;
|
||||
|
||||
if (pos.Y > winSize.Y - 5 && pos.X < 5)
|
||||
/*
|
||||
* Distance from which the mouse is considered to be on the border/corner
|
||||
* You can change the distance by modifying the x value in : (int)(x * Platform.DpiScale)
|
||||
*/
|
||||
int distance = (int)(10 * Platform.DpiScale);
|
||||
|
||||
if (pos.Y > winSize.Y - distance && pos.X < distance)
|
||||
return WindowHitCodes.BottomLeft;
|
||||
|
||||
if (pos.X > winSize.X - 5 && pos.Y > winSize.Y - 5)
|
||||
if (pos.X > winSize.X - distance && pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.BottomRight;
|
||||
|
||||
if (pos.Y < 5 && pos.X < 5)
|
||||
if (pos.Y < distance && pos.X < distance)
|
||||
return WindowHitCodes.TopLeft;
|
||||
|
||||
if (pos.Y < 5 && pos.X > winSize.X - 5)
|
||||
if (pos.Y < distance && pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.TopRight;
|
||||
|
||||
if (pos.X > winSize.X - 5)
|
||||
if (pos.X > winSize.X - distance)
|
||||
return WindowHitCodes.Right;
|
||||
|
||||
if (pos.X < 5)
|
||||
if (pos.X < distance)
|
||||
return WindowHitCodes.Left;
|
||||
|
||||
if (pos.Y < 5)
|
||||
if (pos.Y < distance)
|
||||
return WindowHitCodes.Top;
|
||||
|
||||
if (pos.Y > winSize.Y - 5)
|
||||
if (pos.Y > winSize.Y - distance)
|
||||
return WindowHitCodes.Bottom;
|
||||
}
|
||||
|
||||
var menuPos = PointFromWindow(pos);
|
||||
var controlUnderMouse = GetChildAt(menuPos);
|
||||
var isMouseOverSth = controlUnderMouse != null && controlUnderMouse != _title;
|
||||
if (new Rectangle(Vector2.Zero, Size).Contains(ref menuPos) && !isMouseOverSth)
|
||||
var rb = GetRightButton();
|
||||
if (rb != null && _minimizeButton != null && new Rectangle(rb.UpperRight * Platform.DpiScale, (_minimizeButton.BottomLeft - rb.UpperRight) * Platform.DpiScale).Contains(ref menuPos) && !isMouseOverSth)
|
||||
return WindowHitCodes.Caption;
|
||||
|
||||
return WindowHitCodes.Client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the rightmost button.
|
||||
/// </summary>
|
||||
/// <returns>Rightmost button, null if there is no <see cref="MainMenuButton"/></returns>
|
||||
private MainMenuButton GetRightButton()
|
||||
{
|
||||
MainMenuButton b = null;
|
||||
foreach (var control in Children)
|
||||
{
|
||||
if (b == null && control is MainMenuButton)
|
||||
b = (MainMenuButton)control;
|
||||
if (control is MainMenuButton && control.Right > b.Right)
|
||||
b = (MainMenuButton)control;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the button.
|
||||
/// </summary>
|
||||
|
||||
@@ -98,7 +98,7 @@ void OnFinishBake(const ProbesRenderer::Entry& e)
|
||||
|
||||
void OnBrushModified(CSG::Brush* brush)
|
||||
{
|
||||
if (Editor::Managed->CanAutoBuildCSG())
|
||||
if (brush && Editor::Managed && Editor::Managed->CanAutoBuildCSG())
|
||||
{
|
||||
CSG::Builder::Build(brush->GetBrushScene(), ManagedEditor::ManagedEditorOptions.AutoRebuildCSGTimeoutMs);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace FlaxEditor.Utilities
|
||||
{
|
||||
public static readonly string DiscordUrl = "https://flaxengine.com/discord";
|
||||
public static readonly string DocsUrl = "https://docs.flaxengine.com/";
|
||||
public static readonly string BugTrackerUrl = "https://github.com/FlaxEngine/FlaxAPI/issues";
|
||||
public static readonly string BugTrackerUrl = "https://github.com/FlaxEngine/FlaxEngine/issues";
|
||||
public static readonly string WebsiteUrl = "https://flaxengine.com";
|
||||
public static readonly string FacebookUrl = "https://facebook.com/FlaxEngine";
|
||||
public static readonly string YoutubeUrl = "https://www.youtube.com/channel/UChdER2A3n19rJWIMOZJClhw";
|
||||
|
||||
@@ -160,8 +160,8 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
if (IsAnimatingMove)
|
||||
return;
|
||||
|
||||
EditorViewport.Input input;
|
||||
Viewport.GetInput(out input);
|
||||
Viewport.GetInput(out var input);
|
||||
Viewport.GetPrevInput(out var prevInput);
|
||||
var mainViewport = Viewport as MainEditorGizmoViewport;
|
||||
bool isUsingGizmo = mainViewport != null && mainViewport.TransformGizmo.ActiveAxis != TransformGizmo.Axis.None;
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
}
|
||||
|
||||
// Rotate or orbit
|
||||
if (input.IsRotating || (input.IsOrbiting && !isUsingGizmo))
|
||||
if (input.IsRotating || (input.IsOrbiting && !isUsingGizmo && prevInput.IsOrbiting))
|
||||
{
|
||||
yaw += mouseDelta.X;
|
||||
pitch += mouseDelta.Y;
|
||||
|
||||
@@ -699,6 +699,15 @@ namespace FlaxEditor.Viewport
|
||||
input = _input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input state data (from the previous update).
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
public void GetPrevInput(out Input input)
|
||||
{
|
||||
input = _prevInput;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the projection matrix.
|
||||
/// </summary>
|
||||
|
||||
@@ -377,13 +377,21 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
MarkAsEdited();
|
||||
UpdateToolstrip();
|
||||
_propertiesEditor1.BuildLayoutOnUpdate();
|
||||
_propertiesEditor2.BuildLayoutOnUpdate();
|
||||
|
||||
if (!_isEditingInstancedParameterValue)
|
||||
{
|
||||
_propertiesEditor1.BuildLayoutOnUpdate();
|
||||
_propertiesEditor2.BuildLayoutOnUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTimelineModified()
|
||||
{
|
||||
_tmpParticleSystemIsDirty = true;
|
||||
if (!_isEditingInstancedParameterValue)
|
||||
{
|
||||
_tmpParticleSystemIsDirty = true;
|
||||
}
|
||||
|
||||
MarkAsEdited();
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
c = cm.AddChildMenu("New");
|
||||
c.ContextMenu.Tag = item;
|
||||
c.ContextMenu.AutoSort = true;
|
||||
int newItems = 0;
|
||||
for (int i = 0; i < Editor.ContentDatabase.Proxy.Count; i++)
|
||||
{
|
||||
|
||||
@@ -7,15 +7,13 @@
|
||||
#include "QueueVulkan.h"
|
||||
#include "GPUContextVulkan.h"
|
||||
#include "GPUTimerQueryVulkan.h"
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
#include "DescriptorSetVulkan.h"
|
||||
#endif
|
||||
|
||||
void CmdBufferVulkan::AddWaitSemaphore(VkPipelineStageFlags waitFlags, SemaphoreVulkan* waitSemaphore)
|
||||
{
|
||||
WaitFlags.Add(waitFlags);
|
||||
ASSERT(!WaitSemaphores.Contains(waitSemaphore));
|
||||
WaitSemaphores.Add(waitSemaphore);
|
||||
_waitFlags.Add(waitFlags);
|
||||
ASSERT(!_waitSemaphores.Contains(waitSemaphore));
|
||||
_waitSemaphores.Add(waitSemaphore);
|
||||
}
|
||||
|
||||
void CmdBufferVulkan::Begin()
|
||||
@@ -25,11 +23,11 @@ void CmdBufferVulkan::Begin()
|
||||
VkCommandBufferBeginInfo beginInfo;
|
||||
RenderToolsVulkan::ZeroStruct(beginInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO);
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(CommandBufferHandle, &beginInfo));
|
||||
VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(_commandBufferHandle, &beginInfo));
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
// Acquire a descriptor pool set on
|
||||
if (CurrentDescriptorPoolSetContainer == nullptr)
|
||||
if (_descriptorPoolSetContainer == nullptr)
|
||||
{
|
||||
AcquirePoolSet();
|
||||
}
|
||||
@@ -72,27 +70,23 @@ void CmdBufferVulkan::BeginRenderPass(RenderPassVulkan* renderPass, FramebufferV
|
||||
info.clearValueCount = clearValueCount;
|
||||
info.pClearValues = clearValues;
|
||||
|
||||
vkCmdBeginRenderPass(CommandBufferHandle, &info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdBeginRenderPass(_commandBufferHandle, &info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
_state = State::IsInsideRenderPass;
|
||||
}
|
||||
|
||||
void CmdBufferVulkan::EndRenderPass()
|
||||
{
|
||||
ASSERT(IsInsideRenderPass());
|
||||
vkCmdEndRenderPass(CommandBufferHandle);
|
||||
vkCmdEndRenderPass(_commandBufferHandle);
|
||||
_state = State::IsInsideBegin;
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
void CmdBufferVulkan::AcquirePoolSet()
|
||||
{
|
||||
ASSERT(!CurrentDescriptorPoolSetContainer);
|
||||
CurrentDescriptorPoolSetContainer = &_device->DescriptorPoolsManager->AcquirePoolSetContainer();
|
||||
ASSERT(!_descriptorPoolSetContainer);
|
||||
_descriptorPoolSetContainer = &_device->DescriptorPoolsManager->AcquirePoolSetContainer();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if GPU_ALLOW_PROFILE_EVENTS
|
||||
|
||||
void CmdBufferVulkan::BeginEvent(const Char* name)
|
||||
@@ -138,19 +132,17 @@ void CmdBufferVulkan::RefreshFenceStatus()
|
||||
{
|
||||
_state = State::ReadyForBegin;
|
||||
|
||||
SubmittedWaitSemaphores.Clear();
|
||||
_submittedWaitSemaphores.Clear();
|
||||
|
||||
vkResetCommandBuffer(CommandBufferHandle, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
||||
vkResetCommandBuffer(_commandBufferHandle, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
||||
_fence->GetOwner()->ResetFence(_fence);
|
||||
FenceSignaledCounter++;
|
||||
_fenceSignaledCounter++;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
if (CurrentDescriptorPoolSetContainer)
|
||||
if (_descriptorPoolSetContainer)
|
||||
{
|
||||
_device->DescriptorPoolsManager->ReleasePoolSet(*CurrentDescriptorPoolSetContainer);
|
||||
CurrentDescriptorPoolSetContainer = nullptr;
|
||||
_device->DescriptorPoolsManager->ReleasePoolSet(*_descriptorPoolSetContainer);
|
||||
_descriptorPoolSetContainer = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -161,20 +153,20 @@ void CmdBufferVulkan::RefreshFenceStatus()
|
||||
|
||||
CmdBufferVulkan::CmdBufferVulkan(GPUDeviceVulkan* device, CmdBufferPoolVulkan* pool)
|
||||
: _device(device)
|
||||
, CommandBufferHandle(VK_NULL_HANDLE)
|
||||
, _commandBufferHandle(VK_NULL_HANDLE)
|
||||
, _state(State::ReadyForBegin)
|
||||
, _fence(nullptr)
|
||||
, FenceSignaledCounter(0)
|
||||
, SubmittedFenceCounter(0)
|
||||
, CommandBufferPool(pool)
|
||||
, _fenceSignaledCounter(0)
|
||||
, _submittedFenceCounter(0)
|
||||
, _commandBufferPool(pool)
|
||||
{
|
||||
VkCommandBufferAllocateInfo createCmdBufInfo;
|
||||
RenderToolsVulkan::ZeroStruct(createCmdBufInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO);
|
||||
createCmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
createCmdBufInfo.commandBufferCount = 1;
|
||||
createCmdBufInfo.commandPool = CommandBufferPool->GetHandle();
|
||||
createCmdBufInfo.commandPool = _commandBufferPool->GetHandle();
|
||||
|
||||
VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &CommandBufferHandle));
|
||||
VALIDATE_VULKAN_RESULT(vkAllocateCommandBuffers(_device->Device, &createCmdBufInfo, &_commandBufferHandle));
|
||||
_fence = _device->FenceManager.AllocateFence();
|
||||
}
|
||||
|
||||
@@ -193,7 +185,7 @@ CmdBufferVulkan::~CmdBufferVulkan()
|
||||
fenceManager.ReleaseFence(_fence);
|
||||
}
|
||||
|
||||
vkFreeCommandBuffers(_device->Device, CommandBufferPool->GetHandle(), 1, &CommandBufferHandle);
|
||||
vkFreeCommandBuffers(_device->Device, _commandBufferPool->GetHandle(), 1, &_commandBufferHandle);
|
||||
}
|
||||
|
||||
CmdBufferVulkan* CmdBufferPoolVulkan::Create()
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
class GPUDeviceVulkan;
|
||||
class CmdBufferPoolVulkan;
|
||||
class QueueVulkan;
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
class DescriptorPoolSetContainerVulkan;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the command buffer for the Vulkan backend.
|
||||
@@ -36,20 +34,20 @@ public:
|
||||
private:
|
||||
|
||||
GPUDeviceVulkan* _device;
|
||||
VkCommandBuffer CommandBufferHandle;
|
||||
VkCommandBuffer _commandBufferHandle;
|
||||
State _state;
|
||||
|
||||
Array<VkPipelineStageFlags> WaitFlags;
|
||||
Array<SemaphoreVulkan*> WaitSemaphores;
|
||||
Array<SemaphoreVulkan*> SubmittedWaitSemaphores;
|
||||
Array<VkPipelineStageFlags> _waitFlags;
|
||||
Array<SemaphoreVulkan*> _waitSemaphores;
|
||||
Array<SemaphoreVulkan*> _submittedWaitSemaphores;
|
||||
|
||||
void MarkSemaphoresAsSubmitted()
|
||||
{
|
||||
WaitFlags.Clear();
|
||||
_waitFlags.Clear();
|
||||
|
||||
// Move to pending delete list
|
||||
SubmittedWaitSemaphores = WaitSemaphores;
|
||||
WaitSemaphores.Clear();
|
||||
_submittedWaitSemaphores = _waitSemaphores;
|
||||
_waitSemaphores.Clear();
|
||||
}
|
||||
|
||||
FenceVulkan* _fence;
|
||||
@@ -58,12 +56,14 @@ private:
|
||||
#endif
|
||||
|
||||
// Last value passed after the fence got signaled
|
||||
volatile uint64 FenceSignaledCounter;
|
||||
volatile uint64 _fenceSignaledCounter;
|
||||
|
||||
// Last value when we submitted the cmd buffer; useful to track down if something waiting for the fence has actually been submitted
|
||||
volatile uint64 SubmittedFenceCounter;
|
||||
volatile uint64 _submittedFenceCounter;
|
||||
|
||||
CmdBufferPoolVulkan* CommandBufferPool;
|
||||
CmdBufferPoolVulkan* _commandBufferPool;
|
||||
|
||||
DescriptorPoolSetContainerVulkan* _descriptorPoolSetContainer = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
CmdBufferPoolVulkan* GetOwner()
|
||||
{
|
||||
return CommandBufferPool;
|
||||
return _commandBufferPool;
|
||||
}
|
||||
|
||||
State GetState()
|
||||
@@ -115,17 +115,17 @@ public:
|
||||
|
||||
inline VkCommandBuffer GetHandle() const
|
||||
{
|
||||
return CommandBufferHandle;
|
||||
return _commandBufferHandle;
|
||||
}
|
||||
|
||||
inline volatile uint64 GetFenceSignaledCounter() const
|
||||
{
|
||||
return FenceSignaledCounter;
|
||||
return _fenceSignaledCounter;
|
||||
}
|
||||
|
||||
inline volatile uint64 GetSubmittedFenceCounter() const
|
||||
{
|
||||
return SubmittedFenceCounter;
|
||||
return _submittedFenceCounter;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -138,11 +138,12 @@ public:
|
||||
void BeginRenderPass(RenderPassVulkan* renderPass, FramebufferVulkan* framebuffer, uint32 clearValueCount, VkClearValue* clearValues);
|
||||
void EndRenderPass();
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorPoolSetContainerVulkan* CurrentDescriptorPoolSetContainer = nullptr;
|
||||
DescriptorPoolSetContainerVulkan* GetDescriptorPoolSet() const
|
||||
{
|
||||
return _descriptorPoolSetContainer;
|
||||
}
|
||||
|
||||
void AcquirePoolSet();
|
||||
#endif
|
||||
|
||||
#if GPU_ALLOW_PROFILE_EVENTS
|
||||
void BeginEvent(const Char* name);
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
#include "GPUAdapterVulkan.h"
|
||||
#include "CmdBufferVulkan.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
|
||||
{
|
||||
@@ -23,7 +19,7 @@ void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
|
||||
static uint32 uniqueID = 1;
|
||||
static Dictionary<uint32, uint32> typesUsageHashMap;
|
||||
|
||||
const uint32 typesUsageHash = Crc::MemCrc32(LayoutTypes, sizeof(LayoutTypes));
|
||||
const uint32 typesUsageHash = Crc::MemCrc32(_layoutTypes, sizeof(_layoutTypes));
|
||||
ScopeLock lock(locker);
|
||||
uint32 id;
|
||||
if (!typesUsageHashMap.TryGet(typesUsageHash, id))
|
||||
@@ -34,19 +30,17 @@ void DescriptorSetLayoutInfoVulkan::CacheTypesUsageID()
|
||||
_typesUsageID = id;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void DescriptorSetLayoutInfoVulkan::AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor)
|
||||
{
|
||||
// Increment type usage
|
||||
LayoutTypes[descriptor.descriptorType]++;
|
||||
_layoutTypes[descriptor.descriptorType]++;
|
||||
|
||||
if (descriptorSetIndex >= SetLayouts.Count())
|
||||
if (descriptorSetIndex >= _setLayouts.Count())
|
||||
{
|
||||
SetLayouts.Resize(descriptorSetIndex + 1);
|
||||
_setLayouts.Resize(descriptorSetIndex + 1);
|
||||
}
|
||||
|
||||
SetLayout& descSetLayout = SetLayouts[descriptorSetIndex];
|
||||
SetLayout& descSetLayout = _setLayouts[descriptorSetIndex];
|
||||
descSetLayout.LayoutBindings.Add(descriptor);
|
||||
|
||||
// TODO: manual hash update method?
|
||||
@@ -92,51 +86,51 @@ void DescriptorSetLayoutVulkan::Compile()
|
||||
const VkPhysicalDeviceLimits& limits = _device->PhysicalDeviceLimits;
|
||||
|
||||
// Check for maxDescriptorSetSamplers
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER]
|
||||
+ LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLER]
|
||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
|
||||
< limits.maxDescriptorSetSamplers);
|
||||
|
||||
// Check for maxDescriptorSetUniformBuffers
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]
|
||||
+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER]
|
||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
|
||||
< limits.maxDescriptorSetUniformBuffers);
|
||||
|
||||
// Check for maxDescriptorSetUniformBuffersDynamic
|
||||
if (!_device->Adapter->IsAMD())
|
||||
{
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC]
|
||||
< limits.maxDescriptorSetUniformBuffersDynamic);
|
||||
}
|
||||
|
||||
// Check for maxDescriptorSetStorageBuffers
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]
|
||||
+ LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER]
|
||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
|
||||
< limits.maxDescriptorSetStorageBuffers);
|
||||
|
||||
// Check for maxDescriptorSetStorageBuffersDynamic
|
||||
if (LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > limits.maxDescriptorSetUniformBuffersDynamic)
|
||||
if (_layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC] > limits.maxDescriptorSetUniformBuffersDynamic)
|
||||
{
|
||||
// TODO: Downgrade to non-dynamic?
|
||||
}
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC]
|
||||
< limits.maxDescriptorSetStorageBuffersDynamic);
|
||||
|
||||
// Check for maxDescriptorSetSampledImages
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
|
||||
+ LayoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]
|
||||
+ LayoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER]
|
||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE]
|
||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER]
|
||||
< limits.maxDescriptorSetSampledImages);
|
||||
|
||||
// Check for maxDescriptorSetStorageImages
|
||||
ASSERT(LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]
|
||||
+ LayoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]
|
||||
ASSERT(_layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_IMAGE]
|
||||
+ _layoutTypes[VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER]
|
||||
< limits.maxDescriptorSetStorageImages);
|
||||
|
||||
_handles.Resize(SetLayouts.Count());
|
||||
_handles.Resize(_setLayouts.Count());
|
||||
|
||||
for (int32 i = 0; i < SetLayouts.Count(); i++)
|
||||
for (int32 i = 0; i < _setLayouts.Count(); i++)
|
||||
{
|
||||
auto& layout = SetLayouts[i];
|
||||
auto& layout = _setLayouts[i];
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo layoutInfo;
|
||||
RenderToolsVulkan::ZeroStruct(layoutInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
|
||||
@@ -146,7 +140,6 @@ void DescriptorSetLayoutVulkan::Compile()
|
||||
VALIDATE_VULKAN_RESULT(vkCreateDescriptorSetLayout(_device->Device, &layoutInfo, nullptr, &_handles[i]));
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
if (_typesUsageID == ~0)
|
||||
{
|
||||
CacheTypesUsageID();
|
||||
@@ -155,35 +148,27 @@ void DescriptorSetLayoutVulkan::Compile()
|
||||
RenderToolsVulkan::ZeroStruct(_allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
|
||||
_allocateInfo.descriptorSetCount = _handles.Count();
|
||||
_allocateInfo.pSetLayouts = _handles.Get();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout)
|
||||
#else
|
||||
DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device)
|
||||
#endif
|
||||
: _device(device)
|
||||
, _handle(VK_NULL_HANDLE)
|
||||
, MaxDescriptorSets(0)
|
||||
, NumAllocatedDescriptorSets(0)
|
||||
, PeakAllocatedDescriptorSets(0)
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
, DescriptorSetsMax(0)
|
||||
, AllocatedDescriptorSetsCount(0)
|
||||
, AllocatedDescriptorSetsCountMax(0)
|
||||
, Layout(layout)
|
||||
#endif
|
||||
{
|
||||
Array<VkDescriptorPoolSize, FixedAllocation<VK_DESCRIPTOR_TYPE_RANGE_SIZE>> types;
|
||||
Array<VkDescriptorPoolSize, FixedAllocation<VULKAN_DESCRIPTOR_TYPE_END + 1>> types;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
// Max number of descriptor sets layout allocations
|
||||
// The maximum amount of descriptor sets layout allocations to hold
|
||||
const uint32 MaxSetsAllocations = 256;
|
||||
|
||||
// Descriptor sets number required to allocate the max number of descriptor sets layout.
|
||||
// When we're hashing pools with types usage ID the descriptor pool can be used for different layouts so the initial layout does not make much sense.
|
||||
// In the latter case we'll be probably overallocating the descriptor types but given the relatively small number of max allocations this should not have
|
||||
// In the latter case we'll be probably over-allocating the descriptor types but given the relatively small number of max allocations this should not have
|
||||
// a serious impact.
|
||||
MaxDescriptorSets = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
|
||||
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; ++typeIndex)
|
||||
DescriptorSetsMax = MaxSetsAllocations * (VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? 1 : Layout.GetLayouts().Count());
|
||||
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
|
||||
{
|
||||
const VkDescriptorType descriptorType = (VkDescriptorType)typeIndex;
|
||||
const uint32 typesUsed = Layout.GetTypesUsed(descriptorType);
|
||||
@@ -195,55 +180,13 @@ DescriptorPoolVulkan::DescriptorPoolVulkan(GPUDeviceVulkan* device)
|
||||
type.descriptorCount = typesUsed * MaxSetsAllocations;
|
||||
}
|
||||
}
|
||||
#else
|
||||
MaxDescriptorSets = 16384;
|
||||
|
||||
Platform::MemoryClear(MaxAllocatedTypes, sizeof(MaxAllocatedTypes));
|
||||
Platform::MemoryClear(NumAllocatedTypes, sizeof(NumAllocatedTypes));
|
||||
Platform::MemoryClear(PeakAllocatedTypes, sizeof(PeakAllocatedTypes));
|
||||
|
||||
VkDescriptorPoolSize type;
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
type.descriptorCount = 2048;
|
||||
types.Add(type);
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
type.descriptorCount = 1024;
|
||||
types.Add(type);
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
type.descriptorCount = 512;
|
||||
types.Add(type);
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
type.descriptorCount = 512;
|
||||
types.Add(type);
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
type.descriptorCount = 512;
|
||||
types.Add(type);
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
type.descriptorCount = 512;
|
||||
types.Add(type);
|
||||
|
||||
type.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
type.descriptorCount = 2048;
|
||||
types.Add(type);
|
||||
|
||||
for (const VkDescriptorPoolSize& poolSize : types)
|
||||
{
|
||||
MaxAllocatedTypes[poolSize.type] = poolSize.descriptorCount;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkDescriptorPoolCreateInfo createInfo;
|
||||
RenderToolsVulkan::ZeroStruct(createInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO);
|
||||
createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
createInfo.poolSizeCount = types.Count();
|
||||
createInfo.pPoolSizes = types.Get();
|
||||
createInfo.maxSets = MaxDescriptorSets;
|
||||
createInfo.maxSets = DescriptorSetsMax;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateDescriptorPool(_device->Device, &createInfo, nullptr, &_handle));
|
||||
}
|
||||
|
||||
@@ -258,45 +201,33 @@ DescriptorPoolVulkan::~DescriptorPoolVulkan()
|
||||
void DescriptorPoolVulkan::TrackAddUsage(const DescriptorSetLayoutVulkan& layout)
|
||||
{
|
||||
// Check and increment our current type usage
|
||||
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
|
||||
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
ASSERT(Layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex));
|
||||
#else
|
||||
NumAllocatedTypes[typeIndex] += (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex);
|
||||
PeakAllocatedTypes[typeIndex] = Math::Max(PeakAllocatedTypes[typeIndex], NumAllocatedTypes[typeIndex]);
|
||||
#endif
|
||||
}
|
||||
|
||||
NumAllocatedDescriptorSets += layout.GetLayouts().Count();
|
||||
PeakAllocatedDescriptorSets = Math::Max(NumAllocatedDescriptorSets, PeakAllocatedDescriptorSets);
|
||||
AllocatedDescriptorSetsCount += layout.GetLayouts().Count();
|
||||
AllocatedDescriptorSetsCountMax = Math::Max(AllocatedDescriptorSetsCount, AllocatedDescriptorSetsCountMax);
|
||||
}
|
||||
|
||||
void DescriptorPoolVulkan::TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout)
|
||||
{
|
||||
// Check and increment our current type usage
|
||||
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
|
||||
for (uint32 typeIndex = VULKAN_DESCRIPTOR_TYPE_BEGIN; typeIndex <= VULKAN_DESCRIPTOR_TYPE_END; typeIndex++)
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
ASSERT(Layout.GetTypesUsed((VkDescriptorType)typeIndex) == layout.GetTypesUsed((VkDescriptorType)typeIndex));
|
||||
#else
|
||||
NumAllocatedTypes[typeIndex] -= (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex);
|
||||
ASSERT(NumAllocatedTypes[typeIndex] >= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
NumAllocatedDescriptorSets -= layout.GetLayouts().Count();
|
||||
AllocatedDescriptorSetsCount -= layout.GetLayouts().Count();
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
void DescriptorPoolVulkan::Reset()
|
||||
{
|
||||
if (_handle != VK_NULL_HANDLE)
|
||||
{
|
||||
VALIDATE_VULKAN_RESULT(vkResetDescriptorPool(_device->Device, _handle, 0));
|
||||
}
|
||||
NumAllocatedDescriptorSets = 0;
|
||||
AllocatedDescriptorSetsCount = 0;
|
||||
}
|
||||
|
||||
bool DescriptorPoolVulkan::AllocateDescriptorSets(const VkDescriptorSetAllocateInfo& descriptorSetAllocateInfo, VkDescriptorSet* result)
|
||||
@@ -370,10 +301,6 @@ void TypedDescriptorPoolSetVulkan::Reset()
|
||||
_poolListCurrent = _poolListHead;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
DescriptorPoolSetContainerVulkan::DescriptorPoolSetContainerVulkan(GPUDeviceVulkan* device)
|
||||
: _device(device)
|
||||
, _lastFrameUsed(Engine::FrameCount)
|
||||
@@ -464,8 +391,6 @@ void DescriptorPoolsManagerVulkan::GC()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
PipelineLayoutVulkan::PipelineLayoutVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutInfoVulkan& layout)
|
||||
: _device(device)
|
||||
, _handle(VK_NULL_HANDLE)
|
||||
@@ -491,84 +416,10 @@ PipelineLayoutVulkan::~PipelineLayoutVulkan()
|
||||
}
|
||||
}
|
||||
|
||||
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
DescriptorSetsVulkan::DescriptorSetsVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout, GPUContextVulkan* context)
|
||||
: _device(device)
|
||||
, _pool(nullptr)
|
||||
, _layout(&layout)
|
||||
{
|
||||
const auto& layoutHandles = layout.GetHandles();
|
||||
if (layoutHandles.HasItems())
|
||||
{
|
||||
VkDescriptorSetAllocateInfo allocateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(allocateInfo, VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO);
|
||||
allocateInfo.descriptorSetCount = layoutHandles.Count();
|
||||
allocateInfo.pSetLayouts = layoutHandles.Get();
|
||||
|
||||
_sets.AddZeroed(layoutHandles.Count());
|
||||
|
||||
_pool = context->AllocateDescriptorSets(allocateInfo, layout, _sets.Get());
|
||||
_pool->TrackAddUsage(layout);
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetsVulkan::~DescriptorSetsVulkan()
|
||||
{
|
||||
}
|
||||
|
||||
DescriptorSetRingBufferVulkan::DescriptorSetRingBufferVulkan(GPUDeviceVulkan* device)
|
||||
: _device(device)
|
||||
, _currDescriptorSets(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DescriptorSetsVulkan* DescriptorSetRingBufferVulkan::RequestDescriptorSets(GPUContextVulkan* context, CmdBufferVulkan* cmdBuffer, const PipelineLayoutVulkan* layout)
|
||||
{
|
||||
DescriptorSetsEntry* foundEntry = nullptr;
|
||||
for (DescriptorSetsEntry* descriptorSetsEntry : DescriptorSetsEntries)
|
||||
{
|
||||
if (descriptorSetsEntry->CmdBuffer == cmdBuffer)
|
||||
{
|
||||
foundEntry = descriptorSetsEntry;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundEntry)
|
||||
{
|
||||
if (!layout->HasDescriptors())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
foundEntry = New<DescriptorSetsEntry>(cmdBuffer);
|
||||
DescriptorSetsEntries.Add(foundEntry);
|
||||
}
|
||||
|
||||
const uint64 cmdBufferFenceSignaledCounter = cmdBuffer->GetFenceSignaledCounter();
|
||||
for (int32 i = 0; i < foundEntry->Pairs.Count(); i++)
|
||||
{
|
||||
DescriptorSetsPair& entry = foundEntry->Pairs[i];
|
||||
if (entry.FenceCounter < cmdBufferFenceSignaledCounter)
|
||||
{
|
||||
entry.FenceCounter = cmdBufferFenceSignaledCounter;
|
||||
return entry.DescriptorSets;
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetsPair newEntry;
|
||||
newEntry.DescriptorSets = New<DescriptorSetsVulkan>(_device, layout->GetDescriptorSetLayout(), context);
|
||||
newEntry.FenceCounter = cmdBufferFenceSignaledCounter;
|
||||
foundEntry->Pairs.Add(newEntry);
|
||||
return newEntry.DescriptorSets;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uint32 DescriptorSetWriterVulkan::SetupDescriptorWrites(const SpirvShaderDescriptorInfo& info, VkWriteDescriptorSet* writeDescriptors, VkDescriptorImageInfo* imageInfo, VkDescriptorBufferInfo* bufferInfo, uint8* bindingToDynamicOffsetMap)
|
||||
{
|
||||
WriteDescriptors = writeDescriptors;
|
||||
NumWrites = info.DescriptorTypesCount;
|
||||
WritesCount = info.DescriptorTypesCount;
|
||||
ASSERT(info.DescriptorTypesCount <= 64 && TEXT("Out of bits for Dirty Mask! More than 64 resources in one descriptor set!"));
|
||||
BindingToDynamicOffsetMap = bindingToDynamicOffsetMap;
|
||||
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
#include "IncludeVulkanHeaders.h"
|
||||
#include "Types.h"
|
||||
#include "Config.h"
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#endif
|
||||
|
||||
#if GRAPHICS_API_VULKAN
|
||||
|
||||
#define VULKAN_DESCRIPTOR_TYPE_BEGIN VK_DESCRIPTOR_TYPE_SAMPLER
|
||||
#define VULKAN_DESCRIPTOR_TYPE_END VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT
|
||||
|
||||
class GPUDeviceVulkan;
|
||||
class CmdBufferVulkan;
|
||||
class GPUContextVulkan;
|
||||
@@ -39,7 +40,7 @@ namespace DescriptorSet
|
||||
Domain = 4,
|
||||
|
||||
// Graphics pipeline stages count
|
||||
NumGfxStages = 5,
|
||||
GraphicsStagesCount = 5,
|
||||
|
||||
// Compute pipeline slot
|
||||
Compute = 0,
|
||||
@@ -110,48 +111,42 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
uint32 LayoutTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
|
||||
Array<SetLayout> SetLayouts;
|
||||
|
||||
uint32 _layoutTypes[VULKAN_DESCRIPTOR_TYPE_END];
|
||||
Array<SetLayout> _setLayouts;
|
||||
uint32 _hash = 0;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
uint32 _typesUsageID = ~0;
|
||||
|
||||
void CacheTypesUsageID();
|
||||
#endif
|
||||
void AddDescriptor(int32 descriptorSetIndex, const VkDescriptorSetLayoutBinding& descriptor);
|
||||
|
||||
public:
|
||||
|
||||
DescriptorSetLayoutInfoVulkan()
|
||||
{
|
||||
Platform::MemoryClear(LayoutTypes, sizeof(LayoutTypes));
|
||||
Platform::MemoryClear(_layoutTypes, sizeof(_layoutTypes));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
inline uint32 GetTypesUsed(VkDescriptorType type) const
|
||||
{
|
||||
return LayoutTypes[type];
|
||||
return _layoutTypes[type];
|
||||
}
|
||||
|
||||
const Array<SetLayout>& GetLayouts() const
|
||||
{
|
||||
return SetLayouts;
|
||||
return _setLayouts;
|
||||
}
|
||||
|
||||
inline const uint32* GetLayoutTypes() const
|
||||
{
|
||||
return LayoutTypes;
|
||||
return _layoutTypes;
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
inline uint32 GetTypesUsageID() const
|
||||
{
|
||||
return _typesUsageID;
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -159,40 +154,27 @@ public:
|
||||
|
||||
void CopyFrom(const DescriptorSetLayoutInfoVulkan& info)
|
||||
{
|
||||
Platform::MemoryCopy(LayoutTypes, info.LayoutTypes, sizeof(LayoutTypes));
|
||||
Platform::MemoryCopy(_layoutTypes, info._layoutTypes, sizeof(_layoutTypes));
|
||||
_hash = info._hash;
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
_typesUsageID = info._typesUsageID;
|
||||
#endif
|
||||
SetLayouts = info.SetLayouts;
|
||||
_setLayouts = info._setLayouts;
|
||||
}
|
||||
|
||||
inline bool operator ==(const DescriptorSetLayoutInfoVulkan& other) const
|
||||
{
|
||||
if (other.SetLayouts.Count() != SetLayouts.Count())
|
||||
{
|
||||
if (other._setLayouts.Count() != _setLayouts.Count())
|
||||
return false;
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
if (other._typesUsageID != _typesUsageID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int32 index = 0; index < other.SetLayouts.Count(); index++)
|
||||
for (int32 index = 0; index < other._setLayouts.Count(); index++)
|
||||
{
|
||||
const int32 numBindings = SetLayouts[index].LayoutBindings.Count();
|
||||
if (other.SetLayouts[index].LayoutBindings.Count() != numBindings)
|
||||
{
|
||||
const int32 bindingsCount = _setLayouts[index].LayoutBindings.Count();
|
||||
if (other._setLayouts[index].LayoutBindings.Count() != bindingsCount)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numBindings != 0 && Platform::MemoryCompare(other.SetLayouts[index].LayoutBindings.Get(), SetLayouts[index].LayoutBindings.Get(), numBindings * sizeof(VkDescriptorSetLayoutBinding)))
|
||||
{
|
||||
if (bindingsCount != 0 && Platform::MemoryCompare(other._setLayouts[index].LayoutBindings.Get(), _setLayouts[index].LayoutBindings.Get(), bindingsCount * sizeof(VkDescriptorSetLayoutBinding)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -214,9 +196,7 @@ private:
|
||||
|
||||
GPUDeviceVulkan* _device;
|
||||
DescriptorSetLayoutHandlesArray _handles;
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
VkDescriptorSetAllocateInfo _allocateInfo;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -225,24 +205,22 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
void Compile();
|
||||
|
||||
inline const DescriptorSetLayoutHandlesArray& GetHandles() const
|
||||
{
|
||||
return _handles;
|
||||
}
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
inline const VkDescriptorSetAllocateInfo& GetAllocateInfo() const
|
||||
{
|
||||
return _allocateInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
friend inline uint32 GetHash(const DescriptorSetLayoutVulkan& key)
|
||||
{
|
||||
return key._hash;
|
||||
}
|
||||
|
||||
void Compile();
|
||||
};
|
||||
|
||||
class DescriptorPoolVulkan
|
||||
@@ -252,25 +230,15 @@ private:
|
||||
GPUDeviceVulkan* _device;
|
||||
VkDescriptorPool _handle;
|
||||
|
||||
uint32 MaxDescriptorSets;
|
||||
uint32 NumAllocatedDescriptorSets;
|
||||
uint32 PeakAllocatedDescriptorSets;
|
||||
uint32 DescriptorSetsMax;
|
||||
uint32 AllocatedDescriptorSetsCount;
|
||||
uint32 AllocatedDescriptorSetsCountMax;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
const DescriptorSetLayoutVulkan& Layout;
|
||||
#else
|
||||
int32 MaxAllocatedTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
|
||||
int32 NumAllocatedTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
|
||||
int32 PeakAllocatedTypes[VK_DESCRIPTOR_TYPE_RANGE_SIZE];
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorPoolVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout);
|
||||
#else
|
||||
DescriptorPoolVulkan(GPUDeviceVulkan* device);
|
||||
#endif
|
||||
|
||||
~DescriptorPoolVulkan();
|
||||
|
||||
@@ -283,43 +251,28 @@ public:
|
||||
|
||||
inline bool IsEmpty() const
|
||||
{
|
||||
return NumAllocatedDescriptorSets == 0;
|
||||
return AllocatedDescriptorSetsCount == 0;
|
||||
}
|
||||
|
||||
inline bool CanAllocate(const DescriptorSetLayoutVulkan& layout) const
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
return MaxDescriptorSets > NumAllocatedDescriptorSets + layout.GetLayouts().Count();
|
||||
#else
|
||||
for (uint32 typeIndex = VK_DESCRIPTOR_TYPE_BEGIN_RANGE; typeIndex < VK_DESCRIPTOR_TYPE_END_RANGE; typeIndex++)
|
||||
{
|
||||
if (NumAllocatedTypes[typeIndex] + (int32)layout.GetTypesUsed((VkDescriptorType)typeIndex) > MaxAllocatedTypes[typeIndex])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
return DescriptorSetsMax > AllocatedDescriptorSetsCount + layout.GetLayouts().Count();
|
||||
}
|
||||
|
||||
inline uint32 GetAllocatedDescriptorSetsCount() const
|
||||
{
|
||||
return AllocatedDescriptorSetsCount;
|
||||
}
|
||||
|
||||
void TrackAddUsage(const DescriptorSetLayoutVulkan& layout);
|
||||
|
||||
void TrackRemoveUsage(const DescriptorSetLayoutVulkan& layout);
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
void Reset();
|
||||
|
||||
bool AllocateDescriptorSets(const VkDescriptorSetAllocateInfo& descriptorSetAllocateInfo, VkDescriptorSet* result);
|
||||
|
||||
inline uint32 GetNumAllocatedDescriptorSets() const
|
||||
{
|
||||
return NumAllocatedDescriptorSets;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
class DescriptorPoolSetContainerVulkan;
|
||||
|
||||
class TypedDescriptorPoolSetVulkan
|
||||
@@ -425,8 +378,6 @@ public:
|
||||
void GC();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class PipelineLayoutVulkan
|
||||
{
|
||||
private:
|
||||
@@ -476,115 +427,6 @@ struct DescriptorSetWriteContainerVulkan
|
||||
}
|
||||
};
|
||||
|
||||
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
class DescriptorSetsVulkan
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Array<VkDescriptorSet, FixedAllocation<DescriptorSet::NumGfxStages>> DescriptorSetArray;
|
||||
|
||||
private:
|
||||
|
||||
GPUDeviceVulkan* _device;
|
||||
DescriptorPoolVulkan* _pool;
|
||||
const DescriptorSetLayoutVulkan* _layout;
|
||||
DescriptorSetArray _sets;
|
||||
|
||||
public:
|
||||
|
||||
DescriptorSetsVulkan(GPUDeviceVulkan* device, const DescriptorSetLayoutVulkan& layout, GPUContextVulkan* context);
|
||||
~DescriptorSetsVulkan();
|
||||
|
||||
public:
|
||||
|
||||
inline const DescriptorSetArray& GetHandles() const
|
||||
{
|
||||
return _sets;
|
||||
}
|
||||
|
||||
inline void Bind(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, VkPipelineBindPoint bindPoint, const Array<uint32>& dynamicOffsets) const
|
||||
{
|
||||
vkCmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout, 0, _sets.Count(), _sets.Get(), dynamicOffsets.Count(), dynamicOffsets.Get());
|
||||
}
|
||||
};
|
||||
|
||||
class DescriptorSetRingBufferVulkan
|
||||
{
|
||||
private:
|
||||
|
||||
GPUDeviceVulkan* _device;
|
||||
DescriptorSetsVulkan* _currDescriptorSets;
|
||||
|
||||
struct DescriptorSetsPair
|
||||
{
|
||||
uint64 FenceCounter;
|
||||
DescriptorSetsVulkan* DescriptorSets;
|
||||
|
||||
DescriptorSetsPair()
|
||||
: FenceCounter(0)
|
||||
, DescriptorSets(nullptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct DescriptorSetsEntry
|
||||
{
|
||||
CmdBufferVulkan* CmdBuffer;
|
||||
Array<DescriptorSetsPair> Pairs;
|
||||
|
||||
DescriptorSetsEntry(CmdBufferVulkan* cmdBuffer)
|
||||
: CmdBuffer(cmdBuffer)
|
||||
{
|
||||
}
|
||||
|
||||
~DescriptorSetsEntry()
|
||||
{
|
||||
for (auto& pair : Pairs)
|
||||
{
|
||||
Delete(pair.DescriptorSets);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Array<DescriptorSetsEntry*> DescriptorSetsEntries;
|
||||
|
||||
public:
|
||||
|
||||
DescriptorSetRingBufferVulkan(GPUDeviceVulkan* device);
|
||||
|
||||
virtual ~DescriptorSetRingBufferVulkan()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void Reset()
|
||||
{
|
||||
_currDescriptorSets = nullptr;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
DescriptorSetsEntries.ClearDelete();
|
||||
}
|
||||
|
||||
void Set(DescriptorSetsVulkan* newDescriptorSets)
|
||||
{
|
||||
_currDescriptorSets = newDescriptorSets;
|
||||
}
|
||||
|
||||
inline void Bind(VkCommandBuffer cmdBuffer, VkPipelineLayout pipelineLayout, VkPipelineBindPoint bindPoint, const Array<uint32>& dynamicOffsets)
|
||||
{
|
||||
ASSERT(_currDescriptorSets);
|
||||
_currDescriptorSets->Bind(cmdBuffer, pipelineLayout, bindPoint, dynamicOffsets);
|
||||
}
|
||||
|
||||
DescriptorSetsVulkan* RequestDescriptorSets(GPUContextVulkan* context, CmdBufferVulkan* cmdBuffer, const PipelineLayoutVulkan* layout);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class DescriptorSetWriterVulkan
|
||||
{
|
||||
public:
|
||||
@@ -592,7 +434,7 @@ public:
|
||||
VkWriteDescriptorSet* WriteDescriptors;
|
||||
uint8* BindingToDynamicOffsetMap;
|
||||
uint32* DynamicOffsets;
|
||||
uint32 NumWrites;
|
||||
uint32 WritesCount;
|
||||
|
||||
public:
|
||||
|
||||
@@ -600,7 +442,7 @@ public:
|
||||
: WriteDescriptors(nullptr)
|
||||
, BindingToDynamicOffsetMap(nullptr)
|
||||
, DynamicOffsets(nullptr)
|
||||
, NumWrites(0)
|
||||
, WritesCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -610,7 +452,7 @@ public:
|
||||
|
||||
bool WriteUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
||||
ASSERT(bufferInfo);
|
||||
@@ -622,7 +464,7 @@ public:
|
||||
|
||||
bool WriteDynamicUniformBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range, uint32 dynamicOffset) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
|
||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
||||
ASSERT(bufferInfo);
|
||||
@@ -636,7 +478,7 @@ public:
|
||||
|
||||
bool WriteSampler(uint32 descriptorIndex, VkSampler sampler) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
||||
ASSERT(imageInfo);
|
||||
@@ -646,7 +488,7 @@ public:
|
||||
|
||||
bool WriteImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
||||
ASSERT(imageInfo);
|
||||
@@ -657,7 +499,7 @@ public:
|
||||
|
||||
bool WriteStorageImage(uint32 descriptorIndex, VkImageView imageView, VkImageLayout layout) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
|
||||
VkDescriptorImageInfo* imageInfo = const_cast<VkDescriptorImageInfo*>(WriteDescriptors[descriptorIndex].pImageInfo);
|
||||
ASSERT(imageInfo);
|
||||
@@ -668,7 +510,7 @@ public:
|
||||
|
||||
bool WriteStorageTexelBuffer(uint32 descriptorIndex, const VkBufferView* bufferView) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
|
||||
WriteDescriptors[descriptorIndex].pTexelBufferView = bufferView;
|
||||
return true;
|
||||
@@ -676,7 +518,7 @@ public:
|
||||
|
||||
bool WriteStorageBuffer(uint32 descriptorIndex, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
|
||||
VkDescriptorBufferInfo* bufferInfo = const_cast<VkDescriptorBufferInfo*>(WriteDescriptors[descriptorIndex].pBufferInfo);
|
||||
ASSERT(bufferInfo);
|
||||
@@ -688,14 +530,14 @@ public:
|
||||
|
||||
bool WriteUniformTexelBuffer(uint32 descriptorIndex, const VkBufferView* view) const
|
||||
{
|
||||
ASSERT(descriptorIndex < NumWrites);
|
||||
ASSERT(descriptorIndex < WritesCount);
|
||||
ASSERT(WriteDescriptors[descriptorIndex].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER);
|
||||
return DescriptorSet::CopyAndReturnNotEqual(WriteDescriptors[descriptorIndex].pTexelBufferView, view);
|
||||
}
|
||||
|
||||
void SetDescriptorSet(VkDescriptorSet descriptorSet) const
|
||||
{
|
||||
for (uint32 i = 0; i < NumWrites; i++)
|
||||
for (uint32 i = 0; i < WritesCount; i++)
|
||||
{
|
||||
WriteDescriptors[i].dstSet = descriptorSet;
|
||||
}
|
||||
|
||||
@@ -102,15 +102,11 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
|
||||
|
||||
GPUContextVulkan::~GPUContextVulkan()
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
for (int32 i = 0; i < _descriptorPools.Count(); i++)
|
||||
{
|
||||
_descriptorPools[i].ClearDelete();
|
||||
}
|
||||
_descriptorPools.Clear();
|
||||
#else
|
||||
_descriptorPools.ClearDelete();
|
||||
#endif
|
||||
Delete(_cmdBufferManager);
|
||||
}
|
||||
|
||||
@@ -297,7 +293,6 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto
|
||||
{
|
||||
VkResult result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
|
||||
VkDescriptorSetAllocateInfo allocateInfo = descriptorSetAllocateInfo;
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorPoolVulkan* pool = nullptr;
|
||||
|
||||
const uint32 hash = VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID ? layout.GetTypesUsageID() : GetHash(layout);
|
||||
@@ -317,15 +312,6 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto
|
||||
{
|
||||
typedDescriptorPools = &_descriptorPools.Add(hash, DescriptorPoolArray())->Value;
|
||||
}
|
||||
#else
|
||||
DescriptorPoolVulkan* pool = _descriptorPools.HasItems() ? _descriptorPools.Last() : nullptr;
|
||||
|
||||
if (pool && pool->CanAllocate(layout))
|
||||
{
|
||||
allocateInfo.descriptorPool = pool->GetHandle();
|
||||
result = vkAllocateDescriptorSets(_device->Device, &allocateInfo, outSets);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (result < VK_SUCCESS)
|
||||
{
|
||||
@@ -336,13 +322,8 @@ DescriptorPoolVulkan* GPUContextVulkan::AllocateDescriptorSets(const VkDescripto
|
||||
else
|
||||
{
|
||||
// Spec says any negative value could be due to fragmentation, so create a new Pool. If it fails here then we really are out of memory!
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
pool = New<DescriptorPoolVulkan>(_device, layout);
|
||||
typedDescriptorPools->Add(pool);
|
||||
#else
|
||||
pool = New<DescriptorPoolVulkan>(_device);
|
||||
_descriptorPools.Add(pool);
|
||||
#endif
|
||||
allocateInfo.descriptorPool = pool->GetHandle();
|
||||
VALIDATE_VULKAN_RESULT(vkAllocateDescriptorSets(_device->Device, &allocateInfo, outSets));
|
||||
}
|
||||
@@ -540,23 +521,13 @@ void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineStat
|
||||
ASSERT(pipelineLayout);
|
||||
bool needsWrite = false;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
// No current descriptor pools set - acquire one and reset
|
||||
bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
|
||||
const bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
|
||||
needsWrite |= newDescriptorPool;
|
||||
#else
|
||||
const auto newDescriptorSets = pipelineState->DSRingBuffer.RequestDescriptorSets(this, cmdBuffer, pipelineLayout);
|
||||
pipelineState->DSRingBuffer.Set(newDescriptorSets);
|
||||
if (!newDescriptorSets)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto& descriptorSetHandles = newDescriptorSets->GetHandles();
|
||||
#endif
|
||||
|
||||
// Update descriptors for every used shader stage
|
||||
uint32 remainingHasDescriptorsPerStageMask = pipelineState->HasDescriptorsPerStageMask;
|
||||
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages && remainingHasDescriptorsPerStageMask; stage++)
|
||||
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount && remainingHasDescriptorsPerStageMask; stage++)
|
||||
{
|
||||
// Only process stages that exist in this pipeline and use descriptors
|
||||
if (remainingHasDescriptorsPerStageMask & 1)
|
||||
@@ -568,27 +539,19 @@ void GPUContextVulkan::UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineStat
|
||||
}
|
||||
|
||||
// Allocate sets based on what changed
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
//if (needsWrite) // TODO: write on change only?
|
||||
#endif
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
if (!pipelineState->AllocateDescriptorSets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint32 remainingStagesMask = pipelineState->HasDescriptorsPerStageMask;
|
||||
uint32 stage = 0;
|
||||
while (remainingStagesMask)
|
||||
{
|
||||
if (remainingStagesMask & 1)
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
const VkDescriptorSet descriptorSet = pipelineState->DescriptorSetHandles[stage];
|
||||
#else
|
||||
const VkDescriptorSet descriptorSet = descriptorSetHandles[stage];
|
||||
#endif
|
||||
pipelineState->DSWriter[stage].SetDescriptorSet(descriptorSet);
|
||||
}
|
||||
|
||||
@@ -608,39 +571,21 @@ void GPUContextVulkan::UpdateDescriptorSets(ComputePipelineStateVulkan* pipeline
|
||||
|
||||
bool needsWrite = false;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
// No current descriptor pools set - acquire one and reset
|
||||
bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
|
||||
const bool newDescriptorPool = pipelineState->AcquirePoolSet(cmdBuffer);
|
||||
needsWrite |= newDescriptorPool;
|
||||
#else
|
||||
const auto newDescriptorSets = pipelineState->DSRingBuffer.RequestDescriptorSets(this, cmdBuffer, pipelineLayout);
|
||||
pipelineState->DSRingBuffer.Set(newDescriptorSets);
|
||||
if (!newDescriptorSets)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto& descriptorSetHandles = newDescriptorSets->GetHandles();
|
||||
#endif
|
||||
|
||||
// Update descriptors
|
||||
UpdateDescriptorSets(*pipelineState->DescriptorInfo, pipelineState->DSWriter, needsWrite);
|
||||
|
||||
// Allocate sets based on what changed
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
//if (needsWrite) // TODO: write on change only?
|
||||
#endif
|
||||
//if (needsWrite) // TODO: write on change only?f
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
if (!pipelineState->AllocateDescriptorSets())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
const VkDescriptorSet descriptorSet = pipelineState->DescriptorSetHandles[DescriptorSet::Compute];
|
||||
#else
|
||||
const VkDescriptorSet descriptorSet = descriptorSetHandles[DescriptorSet::Compute];
|
||||
#endif
|
||||
pipelineState->DSWriter.SetDescriptorSet(descriptorSet);
|
||||
|
||||
vkUpdateDescriptorSets(_device->Device, pipelineState->DSWriteContainer.DescriptorWrites.Count(), pipelineState->DSWriteContainer.DescriptorWrites.Get(), 0, nullptr);
|
||||
@@ -675,8 +620,6 @@ void GPUContextVulkan::OnDrawCall()
|
||||
if (_rtDirtyFlag && cmdBuffer->IsInsideRenderPass())
|
||||
EndRenderPass();
|
||||
|
||||
_currentState->Reset();
|
||||
|
||||
if (pipelineState->HasDescriptorsPerStageMask)
|
||||
{
|
||||
UpdateDescriptorSets(pipelineState);
|
||||
|
||||
@@ -114,12 +114,8 @@ private:
|
||||
DescriptorOwnerResourceVulkan* _uaHandles[GPU_MAX_UA_BINDED];
|
||||
DescriptorOwnerResourceVulkan** _handles[(int32)SpirvShaderResourceBindingType::MAX];
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
typedef Array<DescriptorPoolVulkan*> DescriptorPoolArray;
|
||||
Dictionary<uint32, DescriptorPoolArray> _descriptorPools;
|
||||
#else
|
||||
Array<DescriptorPoolVulkan*> _descriptorPools;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -1096,9 +1096,7 @@ GPUDeviceVulkan::GPUDeviceVulkan(ShaderProfile shaderProfile, GPUAdapterVulkan*
|
||||
, ValidationCache(VK_NULL_HANDLE)
|
||||
#endif
|
||||
, UniformBufferUploader(nullptr)
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
, DescriptorPoolsManager(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1871,9 +1869,7 @@ bool GPUDeviceVulkan::Init()
|
||||
// Prepare stuff
|
||||
FenceManager.Init(this);
|
||||
UniformBufferUploader = New<UniformBufferUploaderVulkan>(this);
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorPoolsManager = New<DescriptorPoolsManagerVulkan>(this);
|
||||
#endif
|
||||
MainContext = New<GPUContextVulkan>(this, GraphicsQueue);
|
||||
// TODO: create and load PipelineCache
|
||||
#if VULKAN_SUPPORTS_VALIDATION_CACHE
|
||||
@@ -1895,9 +1891,7 @@ void GPUDeviceVulkan::DrawBegin()
|
||||
// Flush resources
|
||||
DeferredDeletionQueue.ReleaseResources();
|
||||
StagingManager.ProcessPendingFree();
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorPoolsManager->GC();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPUDeviceVulkan::Dispose()
|
||||
@@ -1925,9 +1919,7 @@ void GPUDeviceVulkan::Dispose()
|
||||
StagingManager.Dispose();
|
||||
TimestampQueryPools.ClearDelete();
|
||||
SAFE_DELETE_GPU_RESOURCE(UniformBufferUploader);
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
Delete(DescriptorPoolsManager);
|
||||
#endif
|
||||
SAFE_DELETE(MainContext);
|
||||
SAFE_DELETE(GraphicsQueue);
|
||||
SAFE_DELETE(ComputeQueue);
|
||||
|
||||
@@ -26,9 +26,7 @@ class RenderPassVulkan;
|
||||
class FenceManagerVulkan;
|
||||
class GPUDeviceVulkan;
|
||||
class UniformBufferUploaderVulkan;
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
class DescriptorPoolsManagerVulkan;
|
||||
#endif
|
||||
|
||||
class SemaphoreVulkan
|
||||
{
|
||||
@@ -637,9 +635,10 @@ public:
|
||||
/// </summary>
|
||||
UniformBufferUploaderVulkan* UniformBufferUploader;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
/// <summary>
|
||||
/// The descriptor pools manager.
|
||||
/// </summary>
|
||||
DescriptorPoolsManagerVulkan* DescriptorPoolsManager;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The physical device limits.
|
||||
|
||||
@@ -47,11 +47,8 @@ ComputePipelineStateVulkan* GPUShaderProgramCSVulkan::GetOrCreateState()
|
||||
_pipelineState = New<ComputePipelineStateVulkan>(_device, pipeline, layout);
|
||||
|
||||
_pipelineState->DescriptorInfo = &DescriptorInfo;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
_pipelineState->DescriptorSetsLayout = &layout->GetDescriptorSetLayout();
|
||||
_pipelineState->DescriptorSetHandles.AddZeroed(_pipelineState->DescriptorSetsLayout->GetHandles().Count());
|
||||
#endif
|
||||
|
||||
uint32 totalNumDynamicOffsets = 0;
|
||||
|
||||
@@ -89,13 +86,9 @@ ComputePipelineStateVulkan::ComputePipelineStateVulkan(GPUDeviceVulkan* device,
|
||||
ComputePipelineStateVulkan::~ComputePipelineStateVulkan()
|
||||
{
|
||||
DSWriteContainer.Release();
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
CurrentTypedDescriptorPoolSet = nullptr;
|
||||
DescriptorSetsLayout = nullptr;
|
||||
DescriptorSetHandles.Resize(0);
|
||||
#else
|
||||
DSRingBuffer.Release();
|
||||
#endif
|
||||
DynamicOffsets.Resize(0);
|
||||
_device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::Pipeline, _handle);
|
||||
_layout = nullptr;
|
||||
@@ -105,9 +98,6 @@ GPUPipelineStateVulkan::GPUPipelineStateVulkan(GPUDeviceVulkan* device)
|
||||
: GPUResourceVulkan<GPUPipelineState>(device, StringView::Empty)
|
||||
, _pipelines(16)
|
||||
, _layout(nullptr)
|
||||
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
, DSRingBuffer(device)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -132,10 +122,8 @@ PipelineLayoutVulkan* GPUPipelineStateVulkan::GetLayout()
|
||||
|
||||
_layout = _device->GetOrCreateLayout(descriptorSetLayoutInfo);
|
||||
ASSERT(_layout);
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorSetsLayout = &_layout->GetDescriptorSetLayout();
|
||||
DescriptorSetHandles.AddZeroed(DescriptorSetsLayout->GetHandles().Count());
|
||||
#endif
|
||||
|
||||
return _layout;
|
||||
}
|
||||
@@ -192,13 +180,9 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass)
|
||||
void GPUPipelineStateVulkan::OnReleaseGPU()
|
||||
{
|
||||
DSWriteContainer.Release();
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
CurrentTypedDescriptorPoolSet = nullptr;
|
||||
DescriptorSetsLayout = nullptr;
|
||||
DescriptorSetHandles.Resize(0);
|
||||
#else
|
||||
DSRingBuffer.Release();
|
||||
#endif
|
||||
DynamicOffsets.Resize(0);
|
||||
for (auto i = _pipelines.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
@@ -290,6 +274,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
|
||||
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
|
||||
_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
|
||||
static_assert(ARRAY_COUNT(_dynamicStates) <= 3, "Invalid dynamic states array.");
|
||||
_desc.pDynamicState = &_descDynamic;
|
||||
|
||||
// Multisample
|
||||
@@ -350,7 +335,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
_desc.pColorBlendState = &_descColorBlend;
|
||||
|
||||
ASSERT(DSWriteContainer.DescriptorWrites.IsEmpty());
|
||||
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages; stage++)
|
||||
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount; stage++)
|
||||
{
|
||||
const auto descriptor = DescriptorInfoPerStage[stage];
|
||||
if (descriptor == nullptr || descriptor->DescriptorTypesCount == 0)
|
||||
@@ -369,9 +354,9 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
VkDescriptorImageInfo* currentImageInfo = DSWriteContainer.DescriptorImageInfo.Get();
|
||||
VkDescriptorBufferInfo* currentBufferInfo = DSWriteContainer.DescriptorBufferInfo.Get();
|
||||
uint8* currentBindingToDynamicOffsetMap = DSWriteContainer.BindingToDynamicOffsetMap.Get();
|
||||
uint32 dynamicOffsetsStart[DescriptorSet::NumGfxStages];
|
||||
uint32 dynamicOffsetsStart[DescriptorSet::GraphicsStagesCount];
|
||||
uint32 totalNumDynamicOffsets = 0;
|
||||
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages; stage++)
|
||||
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount; stage++)
|
||||
{
|
||||
dynamicOffsetsStart[stage] = totalNumDynamicOffsets;
|
||||
|
||||
@@ -389,7 +374,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
|
||||
}
|
||||
|
||||
DynamicOffsets.AddZeroed(totalNumDynamicOffsets);
|
||||
for (int32 stage = 0; stage < DescriptorSet::NumGfxStages; stage++)
|
||||
for (int32 stage = 0; stage < DescriptorSet::GraphicsStagesCount; stage++)
|
||||
{
|
||||
DSWriter[stage].DynamicOffsets = dynamicOffsetsStart[stage] + DynamicOffsets.Get();
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ public:
|
||||
DescriptorSetWriteContainerVulkan DSWriteContainer;
|
||||
DescriptorSetWriterVulkan DSWriter;
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
|
||||
const DescriptorSetLayoutVulkan* DescriptorSetsLayout = nullptr;
|
||||
TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr;
|
||||
Array<VkDescriptorSet> DescriptorSetHandles;
|
||||
@@ -53,7 +51,7 @@ public:
|
||||
inline bool AcquirePoolSet(CmdBufferVulkan* cmdBuffer)
|
||||
{
|
||||
// Pipeline state has no current descriptor pools set or set owner is not current - acquire a new pool set
|
||||
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->CurrentDescriptorPoolSetContainer;
|
||||
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->GetDescriptorPoolSet();
|
||||
if (CurrentTypedDescriptorPoolSet == nullptr || CurrentTypedDescriptorPoolSet->GetOwner() != cmdBufferPoolSet)
|
||||
{
|
||||
ASSERT(cmdBufferPoolSet);
|
||||
@@ -70,26 +68,12 @@ public:
|
||||
return CurrentTypedDescriptorPoolSet->AllocateDescriptorSets(*DescriptorSetsLayout, DescriptorSetHandles.Get());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
DescriptorSetRingBufferVulkan DSRingBuffer;
|
||||
|
||||
#endif
|
||||
|
||||
Array<uint32> DynamicOffsets;
|
||||
|
||||
public:
|
||||
|
||||
void Reset()
|
||||
{
|
||||
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DSRingBuffer.Reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Bind(CmdBufferVulkan* cmdBuffer)
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
vkCmdBindDescriptorSets(
|
||||
cmdBuffer->GetHandle(),
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
@@ -99,9 +83,6 @@ public:
|
||||
DescriptorSetHandles.Get(),
|
||||
DynamicOffsets.Count(),
|
||||
DynamicOffsets.Get());
|
||||
#else
|
||||
DSRingBuffer.Bind(cmdBuffer->GetHandle(), GetLayout()->GetHandle(), VK_PIPELINE_BIND_POINT_COMPUTE, DynamicOffsets);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -131,7 +112,7 @@ private:
|
||||
VkPipelineTessellationStateCreateInfo _descTessellation;
|
||||
VkPipelineViewportStateCreateInfo _descViewport;
|
||||
VkPipelineDynamicStateCreateInfo _descDynamic;
|
||||
VkDynamicState _dynamicStates[VK_DYNAMIC_STATE_RANGE_SIZE];
|
||||
VkDynamicState _dynamicStates[3];
|
||||
VkPipelineMultisampleStateCreateInfo _descMultisample;
|
||||
VkPipelineDepthStencilStateCreateInfo _descDepthStencil;
|
||||
VkPipelineRasterizationStateCreateInfo _descRasterization;
|
||||
@@ -162,17 +143,15 @@ public:
|
||||
/// <summary>
|
||||
/// The cached shader bindings per stage.
|
||||
/// </summary>
|
||||
const ShaderBindings* ShaderBindingsPerStage[DescriptorSet::NumGfxStages];
|
||||
const ShaderBindings* ShaderBindingsPerStage[DescriptorSet::GraphicsStagesCount];
|
||||
|
||||
/// <summary>
|
||||
/// The cached shader descriptor infos per stage.
|
||||
/// </summary>
|
||||
const SpirvShaderDescriptorInfo* DescriptorInfoPerStage[DescriptorSet::NumGfxStages];
|
||||
const SpirvShaderDescriptorInfo* DescriptorInfoPerStage[DescriptorSet::GraphicsStagesCount];
|
||||
|
||||
DescriptorSetWriteContainerVulkan DSWriteContainer;
|
||||
DescriptorSetWriterVulkan DSWriter[DescriptorSet::NumGfxStages];
|
||||
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DescriptorSetWriterVulkan DSWriter[DescriptorSet::GraphicsStagesCount];
|
||||
|
||||
const DescriptorSetLayoutVulkan* DescriptorSetsLayout = nullptr;
|
||||
TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr;
|
||||
@@ -181,7 +160,7 @@ public:
|
||||
inline bool AcquirePoolSet(CmdBufferVulkan* cmdBuffer)
|
||||
{
|
||||
// Pipeline state has no current descriptor pools set or set owner is not current - acquire a new pool set
|
||||
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->CurrentDescriptorPoolSetContainer;
|
||||
DescriptorPoolSetContainerVulkan* cmdBufferPoolSet = cmdBuffer->GetDescriptorPoolSet();
|
||||
if (CurrentTypedDescriptorPoolSet == nullptr || CurrentTypedDescriptorPoolSet->GetOwner() != cmdBufferPoolSet)
|
||||
{
|
||||
ASSERT(cmdBufferPoolSet);
|
||||
@@ -198,26 +177,12 @@ public:
|
||||
return CurrentTypedDescriptorPoolSet->AllocateDescriptorSets(*DescriptorSetsLayout, DescriptorSetHandles.Get());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
DescriptorSetRingBufferVulkan DSRingBuffer;
|
||||
|
||||
#endif
|
||||
|
||||
Array<uint32> DynamicOffsets;
|
||||
|
||||
public:
|
||||
|
||||
void Reset()
|
||||
{
|
||||
#if !VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
DSRingBuffer.Reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Bind(CmdBufferVulkan* cmdBuffer)
|
||||
{
|
||||
#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER
|
||||
vkCmdBindDescriptorSets(
|
||||
cmdBuffer->GetHandle(),
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
@@ -227,9 +192,6 @@ public:
|
||||
DescriptorSetHandles.Get(),
|
||||
DynamicOffsets.Count(),
|
||||
DynamicOffsets.Get());
|
||||
#else
|
||||
DSRingBuffer.Bind(cmdBuffer->GetHandle(), GetLayout()->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, DynamicOffsets);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -36,23 +36,23 @@ void QueueVulkan::Submit(CmdBufferVulkan* cmdBuffer, uint32 numSignalSemaphores,
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
|
||||
Array<VkSemaphore> waitSemaphores;
|
||||
if (cmdBuffer->WaitSemaphores.HasItems())
|
||||
if (cmdBuffer->_waitSemaphores.HasItems())
|
||||
{
|
||||
waitSemaphores.EnsureCapacity((uint32)cmdBuffer->WaitSemaphores.Count());
|
||||
for (auto semaphore : cmdBuffer->WaitSemaphores)
|
||||
waitSemaphores.EnsureCapacity((uint32)cmdBuffer->_waitSemaphores.Count());
|
||||
for (auto semaphore : cmdBuffer->_waitSemaphores)
|
||||
{
|
||||
waitSemaphores.Add(semaphore->GetHandle());
|
||||
}
|
||||
submitInfo.waitSemaphoreCount = (uint32)cmdBuffer->WaitSemaphores.Count();
|
||||
submitInfo.waitSemaphoreCount = (uint32)cmdBuffer->_waitSemaphores.Count();
|
||||
submitInfo.pWaitSemaphores = waitSemaphores.Get();
|
||||
submitInfo.pWaitDstStageMask = cmdBuffer->WaitFlags.Get();
|
||||
submitInfo.pWaitDstStageMask = cmdBuffer->_waitFlags.Get();
|
||||
}
|
||||
|
||||
VALIDATE_VULKAN_RESULT(vkQueueSubmit(_queue, 1, &submitInfo, fence->GetHandle()));
|
||||
|
||||
cmdBuffer->_state = CmdBufferVulkan::State::Submitted;
|
||||
cmdBuffer->MarkSemaphoresAsSubmitted();
|
||||
cmdBuffer->SubmittedFenceCounter = cmdBuffer->FenceSignaledCounter;
|
||||
cmdBuffer->_submittedFenceCounter = cmdBuffer->_fenceSignaledCounter;
|
||||
|
||||
#if 0
|
||||
// Wait for the GPU to be idle on every submit (useful for tracking GPU hangs)
|
||||
|
||||
@@ -244,7 +244,9 @@ String RenderToolsVulkan::GetVkErrorString(VkResult result)
|
||||
VKERR(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT);
|
||||
VKERR(VK_ERROR_FRAGMENTATION_EXT);
|
||||
VKERR(VK_ERROR_NOT_PERMITTED_EXT);
|
||||
#if VK_HEADER_VERSION < 140
|
||||
VKERR(VK_RESULT_RANGE_SIZE);
|
||||
#endif
|
||||
default:
|
||||
sb.AppendFormat(TEXT("0x{0:x}"), static_cast<uint32>(result));
|
||||
break;
|
||||
|
||||
@@ -443,6 +443,23 @@ void PlatformBase::TrackAllocation(uint64 size)
|
||||
|
||||
#endif
|
||||
|
||||
void PlatformBase::GetEnvironmentVariables(Dictionary<String, String>& result)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
|
||||
bool PlatformBase::GetEnvironmentVariable(const String& name, String& value)
|
||||
{
|
||||
// Not supported
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlatformBase::SetEnvironmentVariable(const String& name, const String& value)
|
||||
{
|
||||
// Not supported
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 PlatformBase::StartProcess(const StringView& filename, const StringView& args, const StringView& workingDir, bool hiddenWindow, bool waitForEnd)
|
||||
{
|
||||
// Not supported
|
||||
|
||||
@@ -61,11 +61,11 @@ protected:
|
||||
|
||||
PACK_STRUCT(struct Data
|
||||
{
|
||||
float FirstOrder;
|
||||
float First;
|
||||
float AtmosphereR;
|
||||
int AtmosphereLayer;
|
||||
float Dummy0;
|
||||
Vector4 DhdH;
|
||||
Vector4 dhdh;
|
||||
});
|
||||
|
||||
namespace AtmospherePreComputeImpl
|
||||
@@ -367,17 +367,17 @@ void AtmospherePreComputeService::Dispose()
|
||||
release();
|
||||
}
|
||||
|
||||
void GetLayerValue(int32 layer, float& atmosphereR, Vector4& DhdH)
|
||||
void GetLayerValue(int32 layer, float& atmosphereR, Vector4& dhdh)
|
||||
{
|
||||
float r = layer / Math::Max<float>(InscatterAltitudeSampleNum - 1.0f, 1.0f);
|
||||
r = r * r;
|
||||
r = Math::Sqrt(RadiusGround * RadiusGround + r * (RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround)) + (layer == 0 ? 0.01f : (layer == InscatterAltitudeSampleNum - 1 ? -0.001f : 0.0f));
|
||||
float dMin = RadiusAtmosphere - r;
|
||||
float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround);
|
||||
float dMinP = r - RadiusGround;
|
||||
float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround);
|
||||
const float dMin = RadiusAtmosphere - r;
|
||||
const float dMax = Math::Sqrt(r * r - RadiusGround * RadiusGround) + Math::Sqrt(RadiusAtmosphere * RadiusAtmosphere - RadiusGround * RadiusGround);
|
||||
const float dMinP = r - RadiusGround;
|
||||
const float dMaxP = Math::Sqrt(r * r - RadiusGround * RadiusGround);
|
||||
atmosphereR = r;
|
||||
DhdH = Vector4(dMin, dMax, dMinP, dMaxP);
|
||||
dhdh = Vector4(dMin, dMax, dMinP, dMaxP);
|
||||
}
|
||||
|
||||
void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
@@ -390,8 +390,8 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
}
|
||||
ASSERT(_isUpdatePending && _updateFrameNumber == 0);
|
||||
|
||||
auto shader = _shader->GetShader();
|
||||
auto cb = shader->GetCB(0);
|
||||
const auto shader = _shader->GetShader();
|
||||
const auto cb = shader->GetCB(0);
|
||||
Data data;
|
||||
|
||||
// Compute transmittance texture T (line 1 in algorithm 4.1)
|
||||
@@ -415,7 +415,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
context->SetState(_psInscatter1_A);
|
||||
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
{
|
||||
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
data.AtmosphereLayer = layer;
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
@@ -426,7 +426,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
context->SetState(_psInscatter1_B);
|
||||
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
{
|
||||
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
data.AtmosphereLayer = layer;
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
@@ -439,7 +439,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
//// old way to render Inscatter1 to DeltaSR and DeltaSM at once but didn't work well :/ (no time to find out why)
|
||||
//for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
//{
|
||||
// GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
// GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
// data.AtmosphereLayer = layer;
|
||||
// cb->SetData(&data);
|
||||
// context->Bind(0, cb);
|
||||
@@ -472,7 +472,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
context->BindSR(5, AtmosphereDeltaSM->ViewVolume());
|
||||
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
{
|
||||
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
data.AtmosphereLayer = layer;
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
@@ -489,14 +489,14 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
context->UnBindSR(6);
|
||||
context->SetViewportAndScissors((float)InscatterWidth, (float)InscatterHeight);
|
||||
context->SetState(_psInscatterS);
|
||||
data.FirstOrder = order == 2 ? 1.0f : 0.0f;
|
||||
data.First = order == 2 ? 1.0f : 0.0f;
|
||||
context->BindSR(0, AtmosphereTransmittance);
|
||||
context->BindSR(3, AtmosphereDeltaE);
|
||||
context->BindSR(4, AtmosphereDeltaSR->ViewVolume());
|
||||
context->BindSR(5, AtmosphereDeltaSM->ViewVolume());
|
||||
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
{
|
||||
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
data.AtmosphereLayer = layer;
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
@@ -523,7 +523,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
context->BindSR(6, AtmosphereDeltaJ->ViewVolume());
|
||||
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
{
|
||||
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
data.AtmosphereLayer = layer;
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
@@ -545,7 +545,7 @@ void AtmospherePreComputeImpl::onRender(RenderTask* task, GPUContext* context)
|
||||
context->BindSR(4, AtmosphereDeltaSR->ViewVolume());
|
||||
for (int32 layer = 0; layer < InscatterAltitudeSampleNum; layer++)
|
||||
{
|
||||
GetLayerValue(layer, data.AtmosphereR, data.DhdH);
|
||||
GetLayerValue(layer, data.AtmosphereR, data.dhdh);
|
||||
data.AtmosphereLayer = layer;
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/ContentImporters/CreateMaterial.h"
|
||||
#include "ThirdParty/meshoptimizer/meshoptimizer.h"
|
||||
#include <regex>
|
||||
|
||||
void RemoveNamespace(String& name)
|
||||
{
|
||||
@@ -1193,27 +1192,15 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt
|
||||
|
||||
int32 ModelTool::DetectLodIndex(const String& nodeName)
|
||||
{
|
||||
// Try detect mesh lod index
|
||||
int32 index;
|
||||
String result;
|
||||
std::match_results<const Char*> match;
|
||||
String reversed = nodeName;
|
||||
reversed.Reverse();
|
||||
|
||||
// Find '<name>LOD<index>' case
|
||||
const std::wregex regex2(TEXT("^(\\d+)DOL"));
|
||||
if (regex_search(*reversed, match, regex2) && match.size() == 2)
|
||||
const int32 index = nodeName.FindLast(TEXT("LOD"));
|
||||
if (index != -1)
|
||||
{
|
||||
// Get result
|
||||
String num(match[1].str().c_str());
|
||||
num.Reverse();
|
||||
|
||||
// Parse value
|
||||
if (!StringUtils::Parse(*num, num.Length(), &index))
|
||||
int32 num;
|
||||
if (!StringUtils::Parse(nodeName.Get() + index + 3, &num))
|
||||
{
|
||||
if (index >= 0 && index < MODEL_MAX_LODS)
|
||||
if (num >= 0 && num < MODEL_MAX_LODS)
|
||||
{
|
||||
return index;
|
||||
return num;
|
||||
}
|
||||
|
||||
LOG(Warning, "Invalid mesh level of detail index at node \'{0}\'. Maximum supported amount of LODs is {1}.", nodeName, MODEL_MAX_LODS);
|
||||
|
||||
@@ -635,7 +635,7 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path
|
||||
{
|
||||
sourceDxgiFormat = ToDxgiFormat(PixelFormatExtensions::ToNonsRGB(ToPixelFormat(sourceDxgiFormat)));
|
||||
((DirectX::TexMetadata&)currentImage->GetMetadata()).format = sourceDxgiFormat;
|
||||
for (int32 i = 0; i < currentImage->GetImageCount(); i++)
|
||||
for (size_t i = 0; i < currentImage->GetImageCount(); i++)
|
||||
((DirectX::Image*)currentImage->GetImages())[i].format = sourceDxgiFormat;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
// Auto hide if mouse leaves control area
|
||||
Vector2 mousePos = Input.MouseScreenPosition;
|
||||
Vector2 location = _showTarget.ScreenToClient(mousePos / Platform.DpiScale);
|
||||
Vector2 location = _showTarget.ScreenToClient(mousePos);
|
||||
if (!_showTarget.OnTestTooltipOverControl(ref location))
|
||||
{
|
||||
// Mouse left or sth
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace FlaxEngine.GUI
|
||||
/// <inheritdoc />
|
||||
public override Vector2 ScreenToClient(Vector2 location)
|
||||
{
|
||||
return _window.ScreenToClient(location);
|
||||
return _window.ScreenToClient(location) / Platform.DpiScale;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -13,5 +13,5 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("095aaaed-cc57-6182-57cc-8261bcf06644")]
|
||||
[assembly: AssemblyVersion("1.0.6213")]
|
||||
[assembly: AssemblyFileVersion("1.0.6213")]
|
||||
[assembly: AssemblyVersion("1.0.6214")]
|
||||
[assembly: AssemblyFileVersion("1.0.6214")]
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include "Engine/Core/Compiler.h"
|
||||
|
||||
#define FLAXENGINE_NAME "FlaxEngine"
|
||||
#define FLAXENGINE_VERSION Version(1, 0, 6213)
|
||||
#define FLAXENGINE_VERSION_TEXT "1.0.6213"
|
||||
#define FLAXENGINE_VERSION Version(1, 0, 6214)
|
||||
#define FLAXENGINE_VERSION_TEXT "1.0.6214"
|
||||
#define FLAXENGINE_VERSION_MAJOR 1
|
||||
#define FLAXENGINE_VERSION_MINOR 0
|
||||
#define FLAXENGINE_VERSION_BUILD 6213
|
||||
#define FLAXENGINE_VERSION_BUILD 6214
|
||||
#define FLAXENGINE_COMPANY "Flax"
|
||||
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2020 Wojciech Figat. All rights reserved."
|
||||
|
||||
|
||||
@@ -57,9 +57,8 @@ const static int InscatterNuNum = 8;
|
||||
const static int AtmosphericFogInscatterAltitudeSampleNum = 4;
|
||||
|
||||
// Configuration
|
||||
#define TRANSMITTANCE_NON_LINEAR 1
|
||||
#define INSCATTER_NON_LINEAR 1
|
||||
#define ATMOSPHERIC_TEXTURE_SAMPLE_FIX 1
|
||||
#define TRANSMITTANCE_NON_LINEAR 1
|
||||
#define INSCATTER_NON_LINEAR 1
|
||||
|
||||
#ifndef ATMOSPHERIC_NO_SUN_DISK
|
||||
#define ATMOSPHERIC_NO_SUN_DISK 0
|
||||
@@ -90,15 +89,15 @@ Texture3D AtmosphereInscatterTexture : register(t2);
|
||||
|
||||
float2 GetTransmittanceUV(float radius, float Mu)
|
||||
{
|
||||
float U, V;
|
||||
float u, v;
|
||||
#if TRANSMITTANCE_NON_LINEAR
|
||||
V = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround));
|
||||
U = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5;
|
||||
v = sqrt((radius - RadiusGround) / (RadiusAtmosphere - RadiusGround));
|
||||
u = atan((Mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5;
|
||||
#else
|
||||
V = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
|
||||
U = (Mu + 0.15) / (1.0 + 0.15);
|
||||
v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
|
||||
u = (Mu + 0.15) / (1.0 + 0.15);
|
||||
#endif
|
||||
return float2(U, V);
|
||||
return float2(u, v);
|
||||
}
|
||||
|
||||
void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
|
||||
@@ -116,9 +115,9 @@ void GetTransmittanceRMuS(float2 uv, out float radius, out float MuS)
|
||||
|
||||
float2 GetIrradianceUV(float radius, float MuS)
|
||||
{
|
||||
float V = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
|
||||
float U = (MuS + 0.2) / (1.0 + 0.2);
|
||||
return float2(U, V);
|
||||
float v = (radius - RadiusGround) / (RadiusAtmosphere - RadiusGround);
|
||||
float u = (MuS + 0.2) / (1.0 + 0.2);
|
||||
return float2(u, v);
|
||||
}
|
||||
|
||||
void GetIrradianceRMuS(float2 uv, out float radius, out float MuS)
|
||||
@@ -137,9 +136,6 @@ float4 Texture4DSample(Texture3D tex, float radius, float Mu, float MuS, float N
|
||||
float4 TexOffset = RMu < 0.0 && Delta > 0.0 ? float4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(InscatterMuNum)) : float4(-1.0, H * H, H, 0.5 + 0.5 / float(InscatterMuNum));
|
||||
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
|
||||
float MuMu = TexOffset.w + (RMu * TexOffset.x + sqrt(Delta + TexOffset.y)) / (Rho + TexOffset.z) * (0.5 - 1.0 / float(InscatterMuNum));
|
||||
// paper formula
|
||||
//float MuMuS = 0.5 / float(InscatterMuSNum) + max((1.0 - exp(-3.0 * MuS - 0.6)) / (1.0 - exp(-3.6)), 0.0) * (1.0 - 1.0 / float(InscatterMuSNum));
|
||||
// better formula
|
||||
float MuMuS = 0.5 / float(InscatterMuSNum) + (atan(max(MuS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(InscatterMuSNum));
|
||||
#else
|
||||
float MuR = 0.5 / float(AtmosphericFogInscatterAltitudeSampleNum) + Rho / H * (1.0 - 1.0 / float(AtmosphericFogInscatterAltitudeSampleNum));
|
||||
@@ -154,35 +150,32 @@ float4 Texture4DSample(Texture3D tex, float radius, float Mu, float MuS, float N
|
||||
+ tex.SampleLevel(SamplerLinearClamp, float3((MuNu + MuMuS + 1.0) / float(InscatterNuNum), MuMu, MuR), 0) * LerpValue;
|
||||
}
|
||||
|
||||
float Mod(float X, float Y)
|
||||
float Mod(float x, float y)
|
||||
{
|
||||
return X - Y * floor(X/Y);
|
||||
return x - y * floor(x / y);
|
||||
}
|
||||
|
||||
void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float MuS, out float Nu)
|
||||
{
|
||||
float X = uv.x * float(InscatterMuSNum * InscatterNuNum) - 0.5;
|
||||
float Y = uv.y * float(InscatterMuNum) - 0.5;
|
||||
float x = uv.x * float(InscatterMuSNum * InscatterNuNum) - 0.5;
|
||||
float y = uv.y * float(InscatterMuNum) - 0.5;
|
||||
#if INSCATTER_NON_LINEAR
|
||||
if (Y < float(InscatterMuNum) * 0.5f)
|
||||
if (y < float(InscatterMuNum) * 0.5f)
|
||||
{
|
||||
float D = 1.0 - Y / (float(InscatterMuNum) * 0.5f - 1.0);
|
||||
D = min(max(DhdH.z, D * DhdH.w), DhdH.w * 0.999);
|
||||
Mu = (RadiusGround * RadiusGround - radius * radius - D * D) / (2.0 * radius * D);
|
||||
float d = 1.0 - y / (float(InscatterMuNum) * 0.5f - 1.0);
|
||||
d = min(max(DhdH.z, d * DhdH.w), DhdH.w * 0.999);
|
||||
Mu = (RadiusGround * RadiusGround - radius * radius - d * d) / (2.0 * radius * d);
|
||||
Mu = min(Mu, -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) - 0.001);
|
||||
}
|
||||
else
|
||||
{
|
||||
float D = (Y - float(InscatterMuNum) * 0.5f) / (float(InscatterMuNum) * 0.5f - 1.0);
|
||||
D = min(max(DhdH.x, D * DhdH.y), DhdH.y * 0.999);
|
||||
Mu = (RadiusAtmosphere * RadiusAtmosphere - radius * radius - D * D) / (2.0 * radius * D);
|
||||
float d = (y - float(InscatterMuNum) * 0.5f) / (float(InscatterMuNum) * 0.5f - 1.0);
|
||||
d = min(max(DhdH.x, d * DhdH.y), DhdH.y * 0.999);
|
||||
Mu = (RadiusAtmosphere * RadiusAtmosphere - radius * radius - d * d) / (2.0 * radius * d);
|
||||
}
|
||||
MuS = Mod(X, float(InscatterMuSNum)) / (float(InscatterMuSNum) - 1.0);
|
||||
// paper formula
|
||||
//MuS = -(0.6 + log(1.0 - MuS * (1.0 - exp(-3.6)))) / 3.0;
|
||||
// better formula
|
||||
MuS = Mod(x, float(InscatterMuSNum)) / (float(InscatterMuSNum) - 1.0);
|
||||
MuS = tan((2.0 * MuS - 1.0 + 0.26) * 1.1) / tan(1.26 * 1.1);
|
||||
Nu = -1.0 + floor(X / float(InscatterMuSNum)) / (float(InscatterNuNum) - 1.0) * 2.0;
|
||||
Nu = -1.0 + floor(x / float(InscatterMuSNum)) / (float(InscatterNuNum) - 1.0) * 2.0;
|
||||
#else
|
||||
Mu = -1.0 + 2.0 * Y / (float(InscatterMuNum) - 1.0);
|
||||
MuS = Mod(X, float(InscatterMuSNum)) / (float(InscatterMuSNum) - 1.0);
|
||||
@@ -191,10 +184,7 @@ void GetMuMuSNu(float2 uv, float radius, float4 DhdH, out float Mu, out float Mu
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Nearest intersection of ray r,mu with ground or top atmosphere boundary
|
||||
* mu=cos(ray zenith angle at ray origin)
|
||||
*/
|
||||
// Nearest intersection of ray r,mu with ground or top atmosphere boundary, mu=cos(ray zenith angle at ray origin)
|
||||
float Limit(float radius, float Mu)
|
||||
{
|
||||
float Dout = -radius * Mu + sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusLimit * RadiusLimit);
|
||||
@@ -210,32 +200,20 @@ float Limit(float radius, float Mu)
|
||||
return Dout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmittance(=transparency) of atmosphere for infinite ray (r,mu)
|
||||
* (mu=cos(view zenith angle)), intersections with ground ignored
|
||||
*/
|
||||
// Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), intersections with ground ignored
|
||||
float3 Transmittance(float radius, float Mu)
|
||||
{
|
||||
float2 uv = GetTransmittanceUV(radius, Mu);
|
||||
return AtmosphereTransmittanceTexture.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmittance(=transparency) of atmosphere for infinite ray (r,mu)
|
||||
* (mu=cos(view zenith angle)), or zero if ray intersects ground
|
||||
*/
|
||||
// Transmittance(=transparency) of atmosphere for infinite ray (r,mu) (mu=cos(view zenith angle)), or zero if ray intersects ground
|
||||
float3 TransmittanceWithShadow(float radius, float Mu)
|
||||
{
|
||||
// Need to correct calculation based on shadow feature, currently don't consider
|
||||
//return Mu < -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) ? float3(0.f, 0.f, 0.f) : Transmittance(Radius, Mu);
|
||||
return Transmittance(radius, Mu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmittance(=transparency) of atmosphere between x and x0
|
||||
* Assume segment x,x0 not intersecting ground
|
||||
* D = Distance between x and x0, mu=cos(zenith angle of [x,x0) ray at x)
|
||||
*/
|
||||
//Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground. D = Distance between x and x0, mu=cos(zenith angle of [x,x0) ray at x)
|
||||
float3 TransmittanceWithDistance(float radius, float Mu, float D)
|
||||
{
|
||||
float3 result;
|
||||
@@ -252,42 +230,36 @@ float3 TransmittanceWithDistance(float radius, float Mu, float D)
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmittance(=transparency) of atmosphere between x and x0
|
||||
* Assume segment x,x0 not intersecting ground
|
||||
* radius=||x||, Mu=cos(zenith angle of [x,x0) ray at x), v=unit direction vector of [x,x0) ray
|
||||
*/
|
||||
// Transmittance(=transparency) of atmosphere between x and x0. Assume segment x,x0 not intersecting ground radius=||x||, Mu=cos(zenith angle of [x,x0) ray at x), v=unit direction vector of [x,x0) ray
|
||||
float3 TransmittanceWithDistance(float radius, float Mu, float3 V, float3 X0)
|
||||
{
|
||||
float3 result;
|
||||
float R1 = length(X0);
|
||||
float d1 = length(X0);
|
||||
float Mu1 = dot(X0, V) / radius;
|
||||
if (Mu > 0.0)
|
||||
{
|
||||
result = min(Transmittance(radius, Mu) / Transmittance(R1, Mu1), 1.0);
|
||||
}
|
||||
result = min(Transmittance(radius, Mu) / Transmittance(d1, Mu1), 1.0);
|
||||
else
|
||||
{
|
||||
result = min(Transmittance(R1, -Mu1) / Transmittance(radius, -Mu), 1.0);
|
||||
}
|
||||
result = min(Transmittance(d1, -Mu1) / Transmittance(radius, -Mu), 1.0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optical depth for ray (r,mu) of length d, using analytic formula
|
||||
* (mu=cos(view zenith angle)), intersections with ground ignored
|
||||
* H=height scale of exponential density function
|
||||
*/
|
||||
// Optical depth for ray (r,mu) of length d, using analytic formula (mu=cos(view zenith angle)), intersections with ground ignored H=height scale of exponential density function
|
||||
float OpticalDepthWithDistance(float H, float radius, float Mu, float D)
|
||||
{
|
||||
float particleDensity = 6.2831; // REK 04, Table 2
|
||||
float A = sqrt((0.5/H)*radius);
|
||||
float2 A01 = A * float2(Mu, Mu + D / radius);
|
||||
float a = sqrt(0.5 / H * radius);
|
||||
float2 A01 = a * float2(Mu, Mu + D / radius);
|
||||
float2 A01Sign = sign(A01);
|
||||
float2 A01Squared = A01*A01;
|
||||
float X = A01Sign.y > A01Sign.x ? exp(A01Squared.x) : 0.0;
|
||||
float2 Y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H*(D / (2.0 * radius) + Mu)));
|
||||
return sqrt((particleDensity * H)*radius) * exp((RadiusGround - radius) / H) * (X + dot(Y, float2(1.0, -1.0)));
|
||||
float x = A01Sign.y > A01Sign.x ? exp(A01Squared.x) : 0.0;
|
||||
float2 y = A01Sign / (2.3193 * abs(A01) + sqrt(1.52 * A01Squared + 4.0)) * float2(1.0, exp(-D / H*(D / (2.0 * radius) + Mu)));
|
||||
return sqrt((particleDensity * H)*radius) * exp((RadiusGround - radius) / H) * (x + dot(y, float2(1.0, -1.0)));
|
||||
}
|
||||
|
||||
// Transmittance(=transparency) of atmosphere for ray (r,mu) of length d (mu=cos(view zenith angle)), intersections with ground ignored uses analytic formula instead of transmittance texture, REK 04, Atmospheric Transparency
|
||||
float3 AnalyticTransmittance(float R, float Mu, float D)
|
||||
{
|
||||
return exp(- BetaRayleighScattering * OpticalDepthWithDistance(HeightScaleRayleigh, R, Mu, D) - BetaMieExtinction * OpticalDepthWithDistance(HeightScaleMie, R, Mu, D));
|
||||
}
|
||||
|
||||
float3 Irradiance(Texture2D tex, float r, float muS)
|
||||
@@ -296,32 +268,23 @@ float3 Irradiance(Texture2D tex, float r, float muS)
|
||||
return tex.SampleLevel(SamplerLinearClamp, uv, 0).rgb;
|
||||
}
|
||||
|
||||
/** Rayleigh phase function */
|
||||
// Rayleigh phase function
|
||||
float PhaseFunctionR(float Mu)
|
||||
{
|
||||
return (3.0 / (16.0 * PI)) * (1.0 + Mu * Mu);
|
||||
}
|
||||
|
||||
/** Mie phase function */
|
||||
// Mie phase function
|
||||
float PhaseFunctionM(float Mu)
|
||||
{
|
||||
return 1.5 * 1.0 / (4.0 * PI) * (1.0 - MieG * MieG) * pow(1.0 + (MieG * MieG) - 2.0 * MieG * Mu, -3.0/2.0) * (1.0 + Mu * Mu) / (2.0 + MieG * MieG);
|
||||
}
|
||||
|
||||
/** Approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision") */
|
||||
// Approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision")
|
||||
float3 GetMie(float4 RayMie)
|
||||
{
|
||||
// RayMie.rgb=C*, RayMie.w=Cm,r
|
||||
return RayMie.rgb * RayMie.w / max(RayMie.r, 1e-4) * (BetaRayleighScattering.rrr / BetaRayleighScattering.rgb);
|
||||
}
|
||||
|
||||
/** Transmittance(=transparency) of atmosphere for ray (r,mu) of length d
|
||||
* (mu=cos(view zenith angle)), intersections with ground ignored
|
||||
* uses analytic formula instead of transmittance texture, REK 04, Atmospheric Transparency
|
||||
*/
|
||||
float3 AnalyticTransmittance(float R, float Mu, float D)
|
||||
{
|
||||
return exp(- BetaRayleighScattering * OpticalDepthWithDistance(HeightScaleRayleigh, R, Mu, D) - BetaMieExtinction * OpticalDepthWithDistance(HeightScaleMie, R, Mu, D));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,39 +24,38 @@
|
||||
|
||||
static const float HeightOffset = 0.01f;
|
||||
|
||||
/** inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0) */
|
||||
float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry)
|
||||
// inscattered light along ray x+tv, when sun in direction s (=S[L]-T(x,x0)S[L]|x0)
|
||||
float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S, float radius, float Mu, out float3 attenuation, bool isSceneGeometry)
|
||||
{
|
||||
float3 result = float3(0.f, 0.f, 0.f); // X in space and ray looking in space, intialize
|
||||
attenuation = float3(1.f, 1.f, 1.f);
|
||||
float3 result = float3(0.0f, 0.0f, 0.0f);
|
||||
attenuation = float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
float D = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
|
||||
if (D > 0.0)
|
||||
{
|
||||
// if X in space and ray intersects atmosphere
|
||||
// move X to nearest intersection of ray with top atmosphere boundary
|
||||
X += D * V;
|
||||
T -= D;
|
||||
Mu = (radius * Mu + D) / RadiusAtmosphere;
|
||||
radius = RadiusAtmosphere;
|
||||
}
|
||||
|
||||
float Epsilon = 0.005f;//maybe 0.004?
|
||||
|
||||
if (radius < RadiusGround + HeightOffset + Epsilon)
|
||||
float d = -radius * Mu - sqrt(radius * radius * (Mu * Mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
|
||||
if (d > 0.0f)
|
||||
{
|
||||
float Diff = (RadiusGround + HeightOffset + Epsilon) - radius;
|
||||
X -= Diff * V;
|
||||
T -= Diff;
|
||||
radius = RadiusGround + HeightOffset + Epsilon;
|
||||
// if X in space and ray intersects atmosphere
|
||||
// move X to nearest intersection of ray with top atmosphere boundary
|
||||
X += d * V;
|
||||
T -= d;
|
||||
Mu = (radius * Mu + d) / RadiusAtmosphere;
|
||||
radius = RadiusAtmosphere;
|
||||
}
|
||||
|
||||
float epsilon = 0.005f;
|
||||
|
||||
if (radius < RadiusGround + HeightOffset + epsilon)
|
||||
{
|
||||
float diff = (RadiusGround + HeightOffset + epsilon) - radius;
|
||||
X -= diff * V;
|
||||
T -= diff;
|
||||
radius = RadiusGround + HeightOffset + epsilon;
|
||||
Mu = dot(X, V) / radius;
|
||||
}
|
||||
|
||||
if (radius <= RadiusAtmosphere && fogDepth > 0.f)
|
||||
{
|
||||
if (radius <= RadiusAtmosphere && fogDepth > 0.0f)
|
||||
{
|
||||
float3 X0 = X + T * V;
|
||||
float R0 = length(X0);
|
||||
// if ray intersects atmosphere
|
||||
float Nu = dot(V, S);
|
||||
float MuS = dot(X, S) / radius;
|
||||
|
||||
@@ -64,11 +63,11 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S,
|
||||
|
||||
if (isSceneGeometry)
|
||||
{
|
||||
Mu = max(Mu, MuHorizon + Epsilon + 0.15);
|
||||
Mu = max(Mu, MuHorizon + epsilon + 0.15f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mu = max(Mu, MuHorizon + Epsilon);
|
||||
{
|
||||
Mu = max(Mu, MuHorizon + epsilon);
|
||||
}
|
||||
|
||||
float MuOriginal = Mu;
|
||||
@@ -81,9 +80,8 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S,
|
||||
{
|
||||
V.z = max(V.z, 0.15);
|
||||
V = normalize(V);
|
||||
float3 X1 = X + T * V;
|
||||
float R1 = length(X1);
|
||||
Mu = dot(X1, V) / R1;
|
||||
float3 x1 = X + T * V;
|
||||
Mu = dot(x1, V) / length(x1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,264 +89,186 @@ float3 GetInscatterColor(float fogDepth, float3 X, float T, float3 V, float3 S,
|
||||
float phaseM = PhaseFunctionM(Nu);
|
||||
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu), 0.0);
|
||||
|
||||
if (T > 0.0)
|
||||
if (T > 0.0)
|
||||
{
|
||||
#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX
|
||||
// Avoids imprecision problems in transmittance computations based on textures
|
||||
attenuation = AnalyticTransmittance(radius, Mu, T);
|
||||
#else
|
||||
attenuation = TransmittanceWithDistance(radius, Mu, V, X0);
|
||||
#endif
|
||||
|
||||
float Mu0 = dot(X0, V) / R0;
|
||||
float MuS0 = dot(X0, S) / R0;
|
||||
|
||||
if (isSceneGeometry)
|
||||
{
|
||||
R0 = max(R0, radius);
|
||||
}
|
||||
|
||||
|
||||
if (R0 > RadiusGround + HeightOffset)
|
||||
{
|
||||
if (blendRatio < 1.0)
|
||||
{
|
||||
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu), 0.0);
|
||||
#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX
|
||||
// avoids imprecision problems near horizon by interpolating between two points above and below horizon
|
||||
if (!isSceneGeometry )
|
||||
if (!isSceneGeometry)
|
||||
{
|
||||
if (abs(Mu - MuHorizon) < Epsilon)
|
||||
if (abs(Mu - MuHorizon) < epsilon)
|
||||
{
|
||||
float Alpha = ((Mu - MuHorizon) + Epsilon) * 0.5f / Epsilon;
|
||||
|
||||
Mu = MuHorizon - Epsilon;
|
||||
Mu = MuHorizon - epsilon;
|
||||
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
|
||||
Mu0 = (radius * Mu + T) / R0;
|
||||
|
||||
Mu0 = max(MuHorizon + Epsilon, Mu0);
|
||||
float4 Inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
|
||||
float4 Inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
|
||||
float4 InscatterA = max(Inscatter0 - attenuation.rgbr * Inscatter1, 0.0);
|
||||
Mu0 = max(MuHorizon + epsilon, Mu0);
|
||||
float4 inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
|
||||
float4 inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
|
||||
float4 inscatterA = max(inscatter0 - attenuation.rgbr * inscatter1, 0.0);
|
||||
|
||||
Mu = MuHorizon + Epsilon;
|
||||
Mu = MuHorizon + epsilon;
|
||||
R0 = sqrt(radius * radius + T * T + 2.0 * radius * T * Mu);
|
||||
|
||||
Mu0 = (radius * Mu + T) / R0;
|
||||
Mu0 = max(MuHorizon + Epsilon, Mu0);
|
||||
Inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
|
||||
Inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
|
||||
float4 InscatterB = max(Inscatter0 - attenuation.rgbr * Inscatter1, 0.0);
|
||||
Mu0 = max(MuHorizon + epsilon, Mu0);
|
||||
inscatter0 = Texture4DSample(AtmosphereInscatterTexture, radius, Mu, MuS, Nu);
|
||||
inscatter1 = Texture4DSample(AtmosphereInscatterTexture, R0, Mu0, MuS0, Nu);
|
||||
float4 inscatterB = max(inscatter1 - attenuation.rgbr * inscatter1, 0.0);
|
||||
|
||||
inscatter = lerp(InscatterA, InscatterB, Alpha);
|
||||
float alpha = ((Mu - MuHorizon) + epsilon) * 0.5f / epsilon;
|
||||
inscatter = lerp(inscatterA, inscatterB, alpha);
|
||||
}
|
||||
}
|
||||
else if (blendRatio > 0.0)
|
||||
{
|
||||
inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio);
|
||||
inscatter = lerp(inscatter, (1.0 - attenuation.rgbr) * max(Texture4DSample(AtmosphereInscatterTexture, radius, MuOriginal, MuS, Nu), 0.0), blendRatio);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
inscatter = (1.0 - attenuation.rgbr) * inscatter;
|
||||
inscatter = (1.0 - attenuation.rgbr) * inscatter;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if ATMOSPHERIC_TEXTURE_SAMPLE_FIX
|
||||
// Avoids imprecision problems in Mie scattering when sun is below horizon
|
||||
inscatter.w *= smoothstep(0.00, 0.02, MuS);
|
||||
#endif
|
||||
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
|
||||
}
|
||||
|
||||
|
||||
inscatter.w *= smoothstep(0.00, 0.02, MuS);
|
||||
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Ground radiance at end of ray x+tv, when sun in direction s attenuated bewteen ground and viewer (=R[L0]+R[L*])
|
||||
// Ground radiance at end of ray x+tv, when sun in direction s attenuated between ground and viewer (=R[L0]+R[L*])
|
||||
float3 GetGroundColor(float4 sceneColor, float3 X, float T, float3 V, float3 S, float radius, float3 attenuation, bool isSceneGeometry)
|
||||
{
|
||||
float3 result = float3(0.f, 0.f, 0.f); // ray looking at the sky (for intial value)
|
||||
if (T > 0.0)
|
||||
{
|
||||
float3 result = float3(0.0f, 0.0f, 0.0f);
|
||||
if (T > 0.0f)
|
||||
{
|
||||
// if ray hits ground surface
|
||||
// ground Reflectance at end of ray, X0
|
||||
float3 X0 = X + T * V;
|
||||
float R0 = length(X0);
|
||||
float3 N = X0 / R0;
|
||||
N = X0 / R0;
|
||||
// ground Reflectance at end of ray, X0
|
||||
float3 X0 = X + T * V;
|
||||
float R0 = length(X0);
|
||||
float3 N = X0 / R0;
|
||||
sceneColor.xyz = saturate(sceneColor.xyz + 0.05);
|
||||
|
||||
float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0);
|
||||
float4 reflectance = sceneColor * float4(0.2, 0.2, 0.2, 1.0);
|
||||
|
||||
// direct sun light (radiance) reaching X0
|
||||
float MuS = dot(N, S);
|
||||
float3 SunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS);
|
||||
// direct sun light (radiance) reaching X0
|
||||
float MuS = dot(N, S);
|
||||
float3 sunLight = isSceneGeometry ? float3(0.f, 0.f, 0.f) : TransmittanceWithShadow(R0, MuS);
|
||||
|
||||
// precomputed sky light (irradiance) (=E[L*]) at X0
|
||||
float3 GroundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS);
|
||||
// precomputed sky light (irradiance) (=E[L*]) at X0
|
||||
float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, R0, MuS);
|
||||
|
||||
// light reflected at X0 (=(R[L0]+R[L*])/T(X,X0))
|
||||
float3 groundColor = (reflectance.rgb * (max(MuS, 0.0) * SunLight + GroundSkyLight)) / PI;
|
||||
// light reflected at X0 (=(R[L0]+R[L*])/T(X,X0))
|
||||
float3 groundColor = reflectance.rgb * ((max(MuS, 0.0) * sunLight + groundSkyLight) / PI);
|
||||
|
||||
// water specular color due to SunLight
|
||||
if (!isSceneGeometry && reflectance.w > 0.0)
|
||||
// water specular color due to SunLight
|
||||
if (!isSceneGeometry && reflectance.w > 0.0)
|
||||
{
|
||||
float3 H = normalize(S - V);
|
||||
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0);
|
||||
float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0);
|
||||
groundColor += reflectance.w * max(waterBrdf, 0.0) * SunLight;
|
||||
}
|
||||
float3 H = normalize(S - V);
|
||||
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-V, H), 5.0);
|
||||
float waterBrdf = fresnel * pow(max(dot(H, N), 0.0), 150.0);
|
||||
groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight;
|
||||
}
|
||||
|
||||
result = attenuation * groundColor; //=R[L0]+R[L*]
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Direct sun light for ray x+tv, when sun in direction s (=L0)
|
||||
float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu)
|
||||
float3 GetSunColor(AtmosphericFogData atmosphericFog, float3 X, float T, float3 V, float3 S, float radius, float Mu)
|
||||
{
|
||||
if (T > 0.0)
|
||||
{
|
||||
return float3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo)
|
||||
float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun
|
||||
return transmittance * sunIntensity; // Eq (9)
|
||||
}
|
||||
|
||||
float3 transmittance = radius <= RadiusAtmosphere ? TransmittanceWithShadow(radius, Mu) : float3(1.0, 1.0, 1.0); // T(X,xo)
|
||||
float sunIntensity = step(cos(PI * atmosphericFog.AtmosphericFogSunDiscScale / 180.0), dot(V, S)); // Lsun
|
||||
return transmittance * sunIntensity; // Eq (9)
|
||||
}
|
||||
|
||||
float3 inscatter(inout float3 x, inout float t, float3 v, float3 s, out float r, out float mu, out float3 attenuation)
|
||||
{
|
||||
float3 result = 0;
|
||||
r = length(x);
|
||||
mu = dot(x, v) / r;
|
||||
float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
|
||||
float3 result = 0;
|
||||
r = length(x);
|
||||
mu = dot(x, v) / r;
|
||||
float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + RadiusAtmosphere * RadiusAtmosphere);
|
||||
|
||||
// if x in space and ray intersects atmosphere
|
||||
if (d > 0.0)
|
||||
if (d > 0.0)
|
||||
{
|
||||
// move x to nearest intersection of ray with top atmosphere boundary
|
||||
x += d * v;
|
||||
t -= d;
|
||||
mu = (r * mu + d) / RadiusAtmosphere;
|
||||
r = RadiusAtmosphere;
|
||||
}
|
||||
// move x to nearest intersection of ray with top atmosphere boundary
|
||||
x += d * v;
|
||||
t -= d;
|
||||
mu = (r * mu + d) / RadiusAtmosphere;
|
||||
r = RadiusAtmosphere;
|
||||
}
|
||||
|
||||
float epsilon = 0.0045f;
|
||||
|
||||
// if ray intersects atmosphere
|
||||
if (r <= RadiusAtmosphere)
|
||||
if (r <= RadiusAtmosphere)
|
||||
{
|
||||
float nu = dot(v, s);
|
||||
float muS = dot(x, s) / r;
|
||||
float phaseR = PhaseFunctionR(nu);
|
||||
float phaseM = PhaseFunctionM(nu);
|
||||
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0);
|
||||
float nu = dot(v, s);
|
||||
float muS = dot(x, s) / r;
|
||||
float phaseR = PhaseFunctionR(nu);
|
||||
float phaseM = PhaseFunctionM(nu);
|
||||
float4 inscatter = max(Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu), 0.0);
|
||||
|
||||
if (t > 0.0)
|
||||
if (t > 0.0)
|
||||
{
|
||||
float3 x0 = x + t * v;
|
||||
float r0 = length(x0);
|
||||
float rMu0 = dot(x0, v);
|
||||
float mu0 = rMu0 / r0;
|
||||
float muS0 = dot(x0, s) / r0;
|
||||
#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX
|
||||
// avoids imprecision problems in transmittance computations based on textures
|
||||
float3 x0 = x + t * v;
|
||||
float r0 = length(x0);
|
||||
float rMu0 = dot(x0, v);
|
||||
float mu0 = rMu0 / r0;
|
||||
float muS0 = dot(x0, s) / r0;
|
||||
attenuation = AnalyticTransmittance(r, mu, t);
|
||||
#else
|
||||
attenuation = TransmittanceWithDistance(r, mu, v, x0);
|
||||
#endif
|
||||
if (r0 > RadiusGround + 0.01)
|
||||
if (r0 > RadiusGround + 0.01)
|
||||
{
|
||||
// computes S[L]-T(x,x0)S[L]|x0
|
||||
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0);
|
||||
#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX
|
||||
// avoids imprecision problems near horizon by interpolating between two points above and below horizon
|
||||
const float EPS = 0.004;
|
||||
// computes S[L]-T(x,x0)S[L]|x0
|
||||
inscatter = max(inscatter - attenuation.rgbr * Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu), 0.0);
|
||||
float muHoriz = -sqrt(1.0 - (RadiusGround / r) * (RadiusGround / r));
|
||||
if (abs(mu - muHoriz) < EPS)
|
||||
if (abs(mu - muHoriz) < epsilon)
|
||||
{
|
||||
float a = ((mu - muHoriz) + EPS) / (2.0 * EPS);
|
||||
|
||||
mu = muHoriz - EPS;
|
||||
mu = muHoriz - epsilon;
|
||||
r0 = sqrt(r * r + t * t + 2.0 * r * t * mu);
|
||||
mu0 = (r * mu + t) / r0;
|
||||
float4 inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu);
|
||||
float4 inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu);
|
||||
float4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
|
||||
|
||||
mu = muHoriz + EPS;
|
||||
mu = muHoriz + epsilon;
|
||||
r0 = sqrt(r * r + t * t + 2.0 * r * t * mu);
|
||||
mu0 = (r * mu + t) / r0;
|
||||
inScatter0 = Texture4DSample(AtmosphereInscatterTexture, r, mu, muS, nu);
|
||||
inScatter1 = Texture4DSample(AtmosphereInscatterTexture, r0, mu0, muS0, nu);
|
||||
float4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0);
|
||||
|
||||
inscatter = lerp(inScatterA, inScatterB, a);
|
||||
|
||||
float alpha = ((mu - muHoriz) + epsilon) / (2.0 * epsilon);
|
||||
inscatter = lerp(inScatterA, inScatterB, alpha);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef ATMOSPHERIC_TEXTURE_SAMPLE_FIX
|
||||
// avoids imprecision problems in Mie scattering when sun is below horizon
|
||||
}
|
||||
}
|
||||
|
||||
inscatter.w *= smoothstep(0.00, 0.02, muS);
|
||||
#endif
|
||||
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
|
||||
}
|
||||
result = max(inscatter.rgb * phaseR + GetMie(inscatter) * phaseM, 0.0);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
//ground radiance at end of ray x+tv, when sun in direction s
|
||||
//attenuated bewteen ground and viewer (=R[L0]+R[L*])
|
||||
float3 groundColor(float3 x, float t, float3 v, float3 s, float r, float mu, float3 attenuation)
|
||||
{
|
||||
float3 result;
|
||||
if (t > 0.0)
|
||||
{
|
||||
// if ray hits ground surface
|
||||
// ground reflectance at end of ray, x0
|
||||
float3 x0 = x + t * v;
|
||||
float r0 = length(x0);
|
||||
float3 n = x0 / r0;
|
||||
|
||||
float4 SceneColor = 0;
|
||||
|
||||
SceneColor.xyz = saturate(SceneColor.xyz + 0.05);
|
||||
|
||||
float4 Reflectance = SceneColor * float4(0.2, 0.2, 0.2, 1.0);
|
||||
|
||||
if (r0 > RadiusGround + 0.01)
|
||||
{
|
||||
reflectance = float4(0.4, 0.4, 0.4, 0.0);
|
||||
}
|
||||
|
||||
// direct sun light (radiance) reaching x0
|
||||
float muS = dot(n, s);
|
||||
float3 sunLight = transmittanceWithShadow(r0, muS);
|
||||
|
||||
// precomputed sky light (irradiance) (=E[L*]) at x0
|
||||
float3 groundSkyLight = Irradiance(AtmosphereIrradianceTexture, r0, muS);
|
||||
|
||||
// light reflected at x0 (=(R[L0]+R[L*])/T(x,x0))
|
||||
float3 groundColor = reflectance.rgb * (max(muS, 0.0) * sunLight + groundSkyLight) * ISun / M_PI;
|
||||
|
||||
// water specular color due to sunLight
|
||||
if (reflectance.w > 0.0)
|
||||
{
|
||||
float3 h = normalize(s - v);
|
||||
float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-v, h), 5.0);
|
||||
float waterBrdf = fresnel * pow(max(dot(h, n), 0.0), 150.0);
|
||||
groundColor += reflectance.w * max(waterBrdf, 0.0) * sunLight * ISun;
|
||||
}
|
||||
|
||||
result = attenuation * groundColor; //=R[L0]+R[L*]
|
||||
} else { // ray looking at the sky
|
||||
result = 0.0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
*/
|
||||
|
||||
static const float EPSILON_ATMOSPHERE = 0.002f;
|
||||
static const float EPSILON_INSCATTER = 0.004f;
|
||||
@@ -359,19 +279,19 @@ static const float EPSILON_INSCATTER = 0.004f;
|
||||
// output - maxPathLength: distance traversed within atmosphere
|
||||
// output - return value: intersection occurred true/false
|
||||
bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset, out float maxPathLength)
|
||||
{
|
||||
{
|
||||
offset = 0.0f;
|
||||
maxPathLength = 0.0f;
|
||||
|
||||
|
||||
// vector from ray origin to center of the sphere
|
||||
float3 l = -viewPosition;
|
||||
float l2 = dot(l,l);
|
||||
float s = dot(l,d);
|
||||
|
||||
float l2 = dot(l, l);
|
||||
float s = dot(l, d);
|
||||
|
||||
// adjust top atmosphere boundary by small epsilon to prevent artifacts
|
||||
float r = Rt - EPSILON_ATMOSPHERE;
|
||||
float r2 = r*r;
|
||||
if(l2 <= r2)
|
||||
float r2 = r * r;
|
||||
if (l2 <= r2)
|
||||
{
|
||||
// ray origin inside sphere, hit is ensured
|
||||
float m2 = l2 - (s * s);
|
||||
@@ -379,11 +299,11 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset,
|
||||
maxPathLength = s + q;
|
||||
return true;
|
||||
}
|
||||
else if(s >= 0)
|
||||
else if (s >= 0)
|
||||
{
|
||||
// ray starts outside in front of sphere, hit is possible
|
||||
float m2 = l2 - (s * s);
|
||||
if(m2 <= r2)
|
||||
if (m2 <= r2)
|
||||
{
|
||||
// ray hits atmosphere definitely
|
||||
float q = sqrt(r2 - m2);
|
||||
@@ -392,7 +312,7 @@ bool intersectAtmosphere(in float3 viewPosition, in float3 d, out float offset,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -409,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
|
||||
|
||||
if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength))
|
||||
{
|
||||
return float3(offset / 10,0,0);
|
||||
return float3(offset / 10, 0, 0);
|
||||
|
||||
float pathLength = distance(viewPosition, surfacePos);
|
||||
//return pathLength.xxx;
|
||||
@@ -418,10 +338,11 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi
|
||||
if (pathLength > offset)
|
||||
{
|
||||
//return float3(1,0,0);
|
||||
|
||||
|
||||
// offsetting camera
|
||||
float3 startPos = viewPosition + offset * viewDir;
|
||||
float startPosHeight = length(startPos); pathLength -= offset;
|
||||
float startPosHeight = length(startPos);
|
||||
pathLength -= offset;
|
||||
|
||||
// starting position of path is now ensured to be inside atmosphere
|
||||
// was either originally there or has been moved to top boundary
|
||||
@@ -508,9 +429,9 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float
|
||||
viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale;
|
||||
//viewPosition *= atmosphericFog.AtmosphericFogDistanceScale;
|
||||
//viewPosition *= scale;
|
||||
|
||||
|
||||
//viewPosition *= scale;
|
||||
|
||||
|
||||
//if(length(worldPosition) > Rg)
|
||||
// return float4(0, 0,0 ,0);
|
||||
|
||||
@@ -531,27 +452,27 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float
|
||||
return float4(0, 0, 0, 1);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
// TODO: scale viewPosition from cm to km !!!!!!!
|
||||
|
||||
// TODO: scale viewPosition from cm to km
|
||||
|
||||
float scale = 0.0001f * atmosphericFog.AtmosphericFogDistanceScale;
|
||||
viewPosition.y = (viewPosition.y - atmosphericFog.AtmosphericFogGroundOffset) * atmosphericFog.AtmosphericFogAltitudeScale;
|
||||
//viewPosition *= atmosphericFog.AtmosphericFogDistanceScale;
|
||||
viewPosition *= scale;
|
||||
//viewPosition.xz *= 0.00001f;
|
||||
|
||||
|
||||
//viewPosition *= scale;
|
||||
viewPosition.y += RadiusGround + HeightOffset;
|
||||
|
||||
|
||||
//viewPosition.y -= atmosphericFog.AtmosphericFogGroundOffset;
|
||||
//worldPosition
|
||||
float Radius = length(viewPosition);
|
||||
float3 V = normalize(viewVector);
|
||||
float Mu = dot(viewPosition, V) / Radius;
|
||||
float T = -Radius * Mu - sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround);
|
||||
|
||||
|
||||
//-Radius * Mu > sqrt(Radius * Radius * (Mu * Mu - 1.0) + RadiusGround * RadiusGround)
|
||||
/*
|
||||
float3 g = viewPosition - float3(0.0, 0.0, RadiusGround + 10.0);
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/Atmosphere.hlsl"
|
||||
|
||||
// Provides functions for atmospheric scattering and aerial perspective.
|
||||
//
|
||||
// Explanations:
|
||||
// Scale Height = the altitude (height above ground) at which the average
|
||||
// atmospheric density is found.
|
||||
// Optical Depth = also called optical length, airmass, etc.
|
||||
//
|
||||
// References:
|
||||
// [GPUGems2] GPU Gems 2: Accurate Atmospheric Scattering by Sean O'Neil.
|
||||
// [GPUPro3] An Approximation to the Chapman Grazing-Incidence Function for
|
||||
// Atmospheric Scattering, GPU Pro3, pp. 105.
|
||||
// Papers bei Bruneton, Nishita, etc.
|
||||
//
|
||||
// This code contains embedded portions of free sample source code from
|
||||
// http://www-evasion.imag.fr/Membres/Eric.Bruneton/PrecomputedAtmosphericScattering2.zip, Author: Eric Bruneton,
|
||||
// 08/16/2011, Copyright (c) 2008 INRIA, All Rights Reserved, which have been altered from their original version.
|
||||
|
||||
const static int TransmittanceIntegralSamples = 500;
|
||||
const static int InscatterIntegralSamples = 50;
|
||||
const static int IrradianceIntegralSamples = 32;
|
||||
@@ -14,11 +31,11 @@ const static float InscatterDeltaPhi = PI / float(InscatterSphericalIntegralSamp
|
||||
const static float InscatterDeltaTheta = PI / float(InscatterSphericalIntegralSamples);
|
||||
|
||||
META_CB_BEGIN(0, Data)
|
||||
float FirstOrder;
|
||||
float First;
|
||||
float AtmosphereR;
|
||||
int AtmosphereLayer;
|
||||
float Dummy0;
|
||||
float4 DhdH;
|
||||
float4 dhdh;
|
||||
META_CB_END
|
||||
|
||||
Texture2D AtmosphereDeltaETexture : register(t3);
|
||||
@@ -26,117 +43,72 @@ Texture3D AtmosphereDeltaSRTexture : register(t4);
|
||||
Texture3D AtmosphereDeltaSMTexture : register(t5);
|
||||
Texture3D AtmosphereDeltaJTexture : register(t6);
|
||||
|
||||
struct AtmosphereGSOutput
|
||||
float GetOpticalDepth(float h, float radius, float mu)
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 TexCoord : TEXCOORD0;
|
||||
//uint LayerIndex : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
/*
|
||||
META_VS(true, FEATURE_LEVEL_ES2)
|
||||
META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||
META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true)
|
||||
Quad_VS2PS VS(float2 Position : POSITION0, float2 TexCoord : TEXCOORD0)
|
||||
{
|
||||
Quad_VS2PS output;
|
||||
|
||||
output.Position = float4(Position, 0, 1);
|
||||
output.TexCoord = TexCoord;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
META_GS(true, FEATURE_LEVEL_SM4)
|
||||
[maxvertexcount(3)]
|
||||
void GS_Atmosphere(triangle Quad_VS2PS input[3], inout TriangleStream<AtmosphereGSOutput> output)
|
||||
{
|
||||
AtmosphereGSOutput vertex;
|
||||
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
vertex.Position = input[i].Position;
|
||||
vertex.TexCoord = input[i].TexCoord;
|
||||
vertex.LayerIndex = AtmosphereLayer;
|
||||
|
||||
output.Append(vertex);
|
||||
}
|
||||
}
|
||||
*/
|
||||
float OpticalDepth(float H, float radius, float Mu)
|
||||
{
|
||||
float result = 0.0;
|
||||
float Dx = Limit(radius, Mu) / float(TransmittanceIntegralSamples);
|
||||
float Xi = 0.0;
|
||||
float Yi = exp(-(radius - RadiusGround) / H);
|
||||
|
||||
float result = 0.0f;
|
||||
float ti = Limit(radius, mu) / float(TransmittanceIntegralSamples);
|
||||
float xi = 0.0f;
|
||||
float yi = exp(-(radius - RadiusGround) / h);
|
||||
LOOP
|
||||
for (int i = 1; i <= TransmittanceIntegralSamples; i++)
|
||||
{
|
||||
float Xj = float(i) * Dx;
|
||||
float Yj = exp(-(sqrt(radius * radius + Xj * Xj + 2.0 * Xj * radius * Mu) - RadiusGround) / H);
|
||||
result += (Yi + Yj) / 2.0 * Dx;
|
||||
Xi = Xj;
|
||||
Yi = Yj;
|
||||
float xj = float(i) * ti;
|
||||
float yj = exp(-(sqrt(radius * radius + xj * xj + 2.0f * xj * radius * mu) - RadiusGround) / h);
|
||||
result += (yi + yj) * 0.5f * ti;
|
||||
xi = xj;
|
||||
yi = yj;
|
||||
}
|
||||
|
||||
return Mu < -sqrt(1.0 - (RadiusGround / radius) * (RadiusGround / radius)) ? 1e9 : result;
|
||||
return mu < -sqrt(1.0f - (RadiusGround / radius) * (RadiusGround / radius)) ? 1e9 : result;
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Transmittance(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float radius, MuS;
|
||||
GetTransmittanceRMuS(input.TexCoord, radius, MuS);
|
||||
float3 depth = BetaRayleighScattering * OpticalDepth(HeightScaleRayleigh, radius, MuS) + BetaMieExtinction * OpticalDepth(HeightScaleMie, radius, MuS);
|
||||
return float4(exp(-depth), 0.0f); // Eq (5)
|
||||
float radius, mus;
|
||||
GetTransmittanceRMuS(input.TexCoord, radius, mus);
|
||||
float3 depth = BetaRayleighScattering * GetOpticalDepth(HeightScaleRayleigh, radius, mus) + BetaMieExtinction * GetOpticalDepth(HeightScaleMie, radius, mus);
|
||||
return float4(exp(-depth), 0.0f);
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Irradiance1(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float radius, MuS;
|
||||
GetIrradianceRMuS(input.TexCoord, radius, MuS);
|
||||
return float4(Transmittance(radius, MuS) * max(MuS, 0.0), 0.0);
|
||||
float radius, mus;
|
||||
GetIrradianceRMuS(input.TexCoord, radius, mus);
|
||||
return float4(Transmittance(radius, mus) * max(mus, 0.0f), 0.0f);
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_IrradianceN(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float radius, MuS;
|
||||
GetIrradianceRMuS(input.TexCoord, radius, MuS);
|
||||
float3 S = float3(sqrt(max(1.0 - MuS * MuS, 0.0)), 0.0, MuS);
|
||||
float3 result = float3(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Integral over 2.PI around x with two nested loops over W directions (theta, phi) -- Eq (15)
|
||||
float radius, mus;
|
||||
GetIrradianceRMuS(input.TexCoord, radius, mus);
|
||||
float3 s = float3(sqrt(max(1.0f - mus * mus, 0.0f)), 0.0f, mus);
|
||||
float3 result = float3(0, 0, 0);
|
||||
for (int iPhi = 0; iPhi < 4 * IrradianceIntegralSamplesHalf; iPhi++)
|
||||
{
|
||||
float phi = (float(iPhi) + 0.5) * IrradianceDeltaPhi;
|
||||
float phi = (float(iPhi) + 0.5f) * IrradianceDeltaPhi;
|
||||
for (int iTheta = 0; iTheta < IrradianceIntegralSamplesHalf; iTheta++)
|
||||
{
|
||||
float theta = (float(iTheta) + 0.5) * IrradianceDeltaTheta;
|
||||
float Dw = IrradianceDeltaTheta * IrradianceDeltaPhi * sin(theta);
|
||||
float3 W = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta));
|
||||
float Nu = dot(S, W);
|
||||
float theta = (float(iTheta) + 0.5f) * IrradianceDeltaTheta;
|
||||
float dw = IrradianceDeltaTheta * IrradianceDeltaPhi * sin(theta);
|
||||
float3 w = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta));
|
||||
float nu = dot(s, w);
|
||||
|
||||
if (FirstOrder == 1.0)
|
||||
if (First == 1.0f)
|
||||
{
|
||||
// First iteration is special because Rayleigh and Mie were stored separately,
|
||||
// without the phase functions factors; they must be reintroduced here
|
||||
float Pr1 = PhaseFunctionR(Nu);
|
||||
float Pm1 = PhaseFunctionM(Nu);
|
||||
float3 Ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb;
|
||||
float3 Mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, W.z, MuS, Nu).rgb;
|
||||
|
||||
result += (Ray1 * Pr1 + Mie1 * Pm1) * W.z * Dw;
|
||||
float pr1 = PhaseFunctionR(nu);
|
||||
float pm1 = PhaseFunctionM(nu);
|
||||
float3 ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu).xyz;
|
||||
float3 mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, w.z, mus, nu).xyz;
|
||||
result += (ray1 * pr1 + mie1 * pm1) * w.z * dw;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += Texture4DSample(AtmosphereDeltaSRTexture, radius, W.z, MuS, Nu).rgb * W.z * Dw;
|
||||
result += Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu).xyz * w.z * dw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return float4(result, 0.0);
|
||||
}
|
||||
|
||||
@@ -146,219 +118,185 @@ float4 PS_CopyIrradiance1(Quad_VS2PS input) : SV_Target0
|
||||
return AtmosphereDeltaETexture.Sample(SamplerLinearClamp, input.TexCoord);
|
||||
}
|
||||
|
||||
void Integrand(float radius, float Mu, float MuS, float Nu, float T, out float3 Ray, out float3 Mie)
|
||||
void Integrand(float radius, float mu, float mus, float nu, float t, out float3 ray, out float3 mie)
|
||||
{
|
||||
Ray = float3(0, 0, 0);
|
||||
Mie = float3(0, 0, 0);
|
||||
float Ri = sqrt(radius * radius + T * T + 2.0 * radius * Mu * T);
|
||||
float MuSi = (Nu * T + MuS * radius) / Ri;
|
||||
Ri = max(RadiusGround, Ri);
|
||||
if (MuSi >= -sqrt(1.0 - RadiusGround * RadiusGround / (Ri * Ri)) )
|
||||
ray = float3(0, 0, 0);
|
||||
mie = float3(0, 0, 0);
|
||||
float ri = sqrt(radius * radius + t * t + 2.0f * radius * mu * t);
|
||||
float musi = (nu * t + mus * radius) / ri;
|
||||
ri = max(RadiusGround, ri);
|
||||
if (musi >= -sqrt(1.0 - RadiusGround * RadiusGround / (ri * ri)) )
|
||||
{
|
||||
float3 Ti = TransmittanceWithDistance(radius, Mu, T) * Transmittance(Ri, MuSi);
|
||||
Ray = exp(-(Ri - RadiusGround) / HeightScaleRayleigh) * Ti;
|
||||
Mie = exp(-(Ri - RadiusGround) / HeightScaleMie) * Ti;
|
||||
float3 ti = TransmittanceWithDistance(radius, mu, t) * Transmittance(ri, musi);
|
||||
ray = exp(-(ri - RadiusGround) / HeightScaleRayleigh) * ti;
|
||||
mie = exp(-(ri - RadiusGround) / HeightScaleMie) * ti;
|
||||
}
|
||||
}
|
||||
|
||||
// For Inscatter 1
|
||||
void Inscatter(float radius, float Mu, float MuS, float Nu, out float3 Ray, out float3 Mie)
|
||||
void Inscatter(float radius, float mu, float mus, float nu, out float3 ray, out float3 mie)
|
||||
{
|
||||
Ray = float3(0, 0, 0);
|
||||
Mie = float3(0, 0, 0);
|
||||
float Dx = Limit(radius, Mu) / float(InscatterIntegralSamples);
|
||||
float Xi = 0.0;
|
||||
float3 Rayi;
|
||||
float3 Miei;
|
||||
Integrand(radius, Mu, MuS, Nu, 0.0, Rayi, Miei);
|
||||
ray = float3(0, 0, 0);
|
||||
mie = float3(0, 0, 0);
|
||||
float dx = Limit(radius, mu) / float(InscatterIntegralSamples);
|
||||
float xi = 0.0f;
|
||||
float3 rayi;
|
||||
float3 miei;
|
||||
Integrand(radius, mu, mus, nu, 0.0f, rayi, miei);
|
||||
for (int i = 1; i <= InscatterIntegralSamples; i++)
|
||||
{
|
||||
float Xj = float(i) * Dx;
|
||||
float xj = float(i) * dx;
|
||||
float3 Rayj;
|
||||
float3 Miej;
|
||||
Integrand(radius, Mu, MuS, Nu, Xj, Rayj, Miej);
|
||||
Ray += (Rayi + Rayj) / 2.0 * Dx;
|
||||
Mie += (Miei + Miej) / 2.0 * Dx;
|
||||
Xi = Xj;
|
||||
Rayi = Rayj;
|
||||
Miei = Miej;
|
||||
Integrand(radius, mu, mus, nu, xj, Rayj, Miej);
|
||||
ray += (rayi + Rayj) * 0.5f * dx;
|
||||
mie += (miei + Miej) * 0.5f * dx;
|
||||
xi = xj;
|
||||
rayi = Rayj;
|
||||
miei = Miej;
|
||||
}
|
||||
Ray *= BetaRayleighScattering;
|
||||
Mie *= BetaMieScattering;
|
||||
}
|
||||
|
||||
struct Inscatter1Output
|
||||
{
|
||||
float4 DeltaSR : SV_Target0;
|
||||
float4 DeltaSM : SV_Target1;
|
||||
};
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Inscatter1_A(AtmosphereGSOutput input) : SV_Target
|
||||
{
|
||||
float3 Ray;
|
||||
float3 Mie;
|
||||
float Mu, MuS, Nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
|
||||
Inscatter(AtmosphereR, Mu, MuS, Nu, Ray, Mie);
|
||||
|
||||
// Store separately Rayleigh and Mie contributions, WITHOUT the phase function factor (cf "Angular precision")
|
||||
return float4(Ray, 1);
|
||||
ray *= BetaRayleighScattering;
|
||||
mie *= BetaMieScattering;
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_CopyInscatter1(AtmosphereGSOutput input) : SV_Target0
|
||||
float4 PS_Inscatter1_A(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float3 UVW = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
|
||||
float4 Ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW);
|
||||
float4 Mie = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW);
|
||||
return float4(Ray.rgb, Mie.r);
|
||||
float3 ray;
|
||||
float3 mie;
|
||||
float mu, mus, nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
|
||||
Inscatter(AtmosphereR, mu, mus, nu, ray, mie);
|
||||
return float4(ray, 1);
|
||||
}
|
||||
|
||||
// For Inscatter S
|
||||
void Inscatter(float Radius, float Mu, float MuS, float Nu, out float3 RayMie)
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_CopyInscatter1(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
Radius = clamp(Radius, RadiusGround, RadiusAtmosphere);
|
||||
Mu = clamp(Mu, -1.0, 1.0);
|
||||
MuS = clamp(MuS, -1.0, 1.0);
|
||||
float Variation = sqrt(1.0 - Mu * Mu) * sqrt(1.0 - MuS * MuS);
|
||||
Nu = clamp(Nu, MuS * Mu - Variation, MuS * Mu + Variation);
|
||||
float3 uvw = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
|
||||
float4 ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, uvw);
|
||||
float4 mie = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, uvw);
|
||||
return float4(ray.xyz, mie.x);
|
||||
}
|
||||
|
||||
float cThetaMin = -sqrt(1.0 - (RadiusGround / Radius) * (RadiusGround / Radius));
|
||||
void Inscatter(float radius, float mu, float mus, float nu, out float3 rayMie)
|
||||
{
|
||||
radius = clamp(radius, RadiusGround, RadiusAtmosphere);
|
||||
mu = clamp(mu, -1.0f, 1.0f);
|
||||
mus = clamp(mus, -1.0f, 1.0f);
|
||||
float variation = sqrt(1.0f - mu * mu) * sqrt(1.0f - mus * mus);
|
||||
nu = clamp(nu, mus * mu - variation, mus * mu + variation);
|
||||
float cThetaMin = -sqrt(1.0f - (RadiusGround / radius) * (RadiusGround / radius));
|
||||
float3 v = float3(sqrt(1.0f - mu * mu), 0.0f, mu);
|
||||
float sx = v.x == 0.0f ? 0.0f : (nu - mus * mu) / v.x;
|
||||
float3 s = float3(sx, sqrt(max(0.0f, 1.0f - sx * sx - mus * mus)), mus);
|
||||
rayMie = float3(0, 0, 0);
|
||||
|
||||
float3 V = float3(sqrt(1.0 - Mu * Mu), 0.0, Mu);
|
||||
float Sx = V.x == 0.0 ? 0.0 : (Nu - MuS * Mu) / V.x;
|
||||
float3 S = float3(Sx, sqrt(max(0.0, 1.0 - Sx * Sx - MuS * MuS)), MuS);
|
||||
|
||||
RayMie = float3(0.f, 0.f, 0.f);
|
||||
|
||||
// Integral over 4.PI around x with two nested loops over W directions (theta, phi) - Eq (7)
|
||||
for (int iTheta = 0; iTheta < InscatterSphericalIntegralSamples; iTheta++)
|
||||
{
|
||||
float theta = (float(iTheta) + 0.5) * InscatterDeltaTheta;
|
||||
float theta = (float(iTheta) + 0.5f) * InscatterDeltaTheta;
|
||||
float cTheta = cos(theta);
|
||||
|
||||
float GReflectance = 0.0;
|
||||
float DGround = 0.0;
|
||||
float3 GTransmittance = float3(0.f, 0.f, 0.f);
|
||||
|
||||
float ground = 0.0f;
|
||||
float3 transmittance = float3(0, 0, 0);
|
||||
float reflectance = 0.0f;
|
||||
if (cTheta < cThetaMin)
|
||||
{
|
||||
// If ground visible in direction W, Compute transparency GTransmittance between x and ground
|
||||
GReflectance = AverageGroundRelectance / PI;
|
||||
DGround = -Radius * cTheta - sqrt(Radius * Radius * (cTheta * cTheta - 1.0) + RadiusGround * RadiusGround);
|
||||
GTransmittance = TransmittanceWithDistance(RadiusGround, -(Radius * cTheta + DGround) / RadiusGround, DGround);
|
||||
{
|
||||
ground = -radius * cTheta - sqrt(radius * radius * (cTheta * cTheta - 1.0f) + RadiusGround * RadiusGround);
|
||||
transmittance = TransmittanceWithDistance(RadiusGround, -(radius * cTheta + ground) / RadiusGround, ground);
|
||||
reflectance = AverageGroundRelectance / PI;
|
||||
}
|
||||
|
||||
for (int iPhi = 0; iPhi < 2 * InscatterSphericalIntegralSamples; iPhi++)
|
||||
{
|
||||
float phi = (float(iPhi) + 0.5) * InscatterDeltaPhi;
|
||||
float Dw = InscatterDeltaTheta * InscatterDeltaPhi * sin(theta);
|
||||
float3 W = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cTheta);
|
||||
float dw = InscatterDeltaTheta * InscatterDeltaPhi * sin(theta);
|
||||
float3 w = float3(cos(phi) * sin(theta), sin(phi) * sin(theta), cTheta);
|
||||
float nu1 = dot(s, w);
|
||||
float nu2 = dot(v, w);
|
||||
float pr2 = PhaseFunctionR(nu2);
|
||||
float pm2 = PhaseFunctionM(nu2);
|
||||
float3 normal = (float3(0.0f, 0.0f, radius) + ground * w) / RadiusGround;
|
||||
float3 irradiance = Irradiance(AtmosphereDeltaETexture, RadiusGround, dot(normal, s));
|
||||
float3 rayMie1 = reflectance * irradiance * transmittance;
|
||||
|
||||
float Nu1 = dot(S, W);
|
||||
float Nu2 = dot(V, W);
|
||||
float Pr2 = PhaseFunctionR(Nu2);
|
||||
float Pm2 = PhaseFunctionM(Nu2);
|
||||
|
||||
// Compute irradiance received at ground in direction W (if ground visible) =deltaE
|
||||
float3 GNormal = (float3(0.0, 0.0, Radius) + DGround * W) / RadiusGround;
|
||||
float3 GIrradiance = Irradiance(AtmosphereDeltaETexture, RadiusGround, dot(GNormal, S));
|
||||
|
||||
float3 RayMie1; // light arriving at x from direction W
|
||||
|
||||
// First term = light reflected from the ground and attenuated before reaching x, =T.alpha/PI.deltaE
|
||||
RayMie1 = GReflectance * GIrradiance * GTransmittance;
|
||||
|
||||
// Second term = inscattered light, =deltaS
|
||||
if (FirstOrder == 1.0)
|
||||
if (First == 1.0f)
|
||||
{
|
||||
// First iteration is special because Rayleigh and Mie were stored separately,
|
||||
// without the phase functions factors; they must be reintroduced here
|
||||
float Pr1 = PhaseFunctionR(Nu1);
|
||||
float Pm1 = PhaseFunctionM(Nu1);
|
||||
float3 Ray1 = Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb;
|
||||
float3 Mie1 = Texture4DSample(AtmosphereDeltaSMTexture, Radius, W.z, MuS, Nu1).rgb;
|
||||
RayMie1 += Ray1 * Pr1 + Mie1 * Pm1;
|
||||
}
|
||||
else
|
||||
float pr1 = PhaseFunctionR(nu1);
|
||||
float pm1 = PhaseFunctionM(nu1);
|
||||
float3 ray1 = Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu1).xyz;
|
||||
float3 mie1 = Texture4DSample(AtmosphereDeltaSMTexture, radius, w.z, mus, nu1).xyz;
|
||||
rayMie1 += ray1 * pr1 + mie1 * pm1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, Radius, W.z, MuS, Nu1).rgb;
|
||||
rayMie1 += Texture4DSample(AtmosphereDeltaSRTexture, radius, w.z, mus, nu1).xyz;
|
||||
}
|
||||
|
||||
// Light coming from direction W and scattered in direction V
|
||||
// = light arriving at x from direction W (RayMie1) * SUM(scattering coefficient * phaseFunction) - Eq (7)
|
||||
RayMie += RayMie1 * (BetaRayleighScattering * exp(-(Radius - RadiusGround) / HeightScaleRayleigh) * Pr2 + BetaMieScattering * exp(-(Radius - RadiusGround) / HeightScaleMie) * Pm2) * Dw;
|
||||
rayMie += rayMie1 * (BetaRayleighScattering * exp(-(radius - RadiusGround) / HeightScaleRayleigh) * pr2 + BetaMieScattering * exp(-(radius - RadiusGround) / HeightScaleMie) * pm2) * dw;
|
||||
}
|
||||
}
|
||||
|
||||
// output RayMie = J[T.alpha/PI.deltaE + deltaS] (line 7 in algorithm 4.1)
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_InscatterS(AtmosphereGSOutput input) : SV_Target0
|
||||
float4 PS_InscatterS(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float3 RayMie;
|
||||
float Mu, MuS, Nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
|
||||
Inscatter(AtmosphereR, Mu, MuS, Nu, RayMie);
|
||||
return float4(RayMie, 0);
|
||||
float3 rayMie;
|
||||
float mu, mus, nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
|
||||
Inscatter(AtmosphereR, mu, mus, nu, rayMie);
|
||||
return float4(rayMie, 0);
|
||||
}
|
||||
|
||||
float3 Integrand(float Radius, float Mu, float MuS, float Nu, float T)
|
||||
float3 Integrand(float radius, float mu, float mus, float nu, float t)
|
||||
{
|
||||
float Ri = sqrt(Radius * Radius + T * T + 2.0 * Radius * Mu * T);
|
||||
float Mui = (Radius * Mu + T) / Ri;
|
||||
float MuSi = (Nu * T + MuS * Radius) / Ri;
|
||||
return Texture4DSample(AtmosphereDeltaJTexture, Ri, Mui, MuSi, Nu).rgb * TransmittanceWithDistance(Radius, Mu, T);
|
||||
float ri = sqrt(radius * radius + t * t + 2.0 * radius * mu * t);
|
||||
float mui = (radius * mu + t) / ri;
|
||||
float musi = (nu * t + mus * radius) / ri;
|
||||
return Texture4DSample(AtmosphereDeltaJTexture, ri, mui, musi, nu).xyz * TransmittanceWithDistance(radius, mu, t);
|
||||
}
|
||||
|
||||
// InscatterN
|
||||
float3 Inscatter(float Radius, float Mu, float MuS, float Nu)
|
||||
float3 Inscatter(float radius, float mu, float mus, float nu)
|
||||
{
|
||||
float3 RayMie = float3(0.f, 0.f, 0.f);
|
||||
float Dx = Limit(Radius, Mu) / float(InscatterIntegralSamples);
|
||||
float Xi = 0.0;
|
||||
float3 RayMiei = Integrand(Radius, Mu, MuS, Nu, 0.0);
|
||||
|
||||
float3 rayMie = float3(0, 0, 0);
|
||||
float dx = Limit(radius, mu) / float(InscatterIntegralSamples);
|
||||
float xi = 0.0f;
|
||||
float3 raymiei = Integrand(radius, mu, mus, nu, 0.0f);
|
||||
for (int i = 1; i <= InscatterIntegralSamples; i++)
|
||||
{
|
||||
float Xj = float(i) * Dx;
|
||||
float3 RayMiej = Integrand(Radius, Mu, MuS, Nu, Xj);
|
||||
RayMie += (RayMiei + RayMiej) / 2.0 * Dx;
|
||||
Xi = Xj;
|
||||
RayMiei = RayMiej;
|
||||
float xj = float(i) * dx;
|
||||
float3 RayMiej = Integrand(radius, mu, mus, nu, xj);
|
||||
rayMie += (raymiei + RayMiej) * 0.5f * dx;
|
||||
xi = xj;
|
||||
raymiei = RayMiej;
|
||||
}
|
||||
|
||||
return RayMie;
|
||||
return rayMie;
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_InscatterN(AtmosphereGSOutput input) : SV_Target0
|
||||
float4 PS_InscatterN(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float Mu, MuS, Nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
|
||||
return float4(Inscatter(AtmosphereR, Mu, MuS, Nu), 0);
|
||||
float mu, mus, nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
|
||||
return float4(Inscatter(AtmosphereR, mu, mus, nu), 0);
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_CopyInscatterN(AtmosphereGSOutput input) : SV_Target0
|
||||
float4 PS_CopyInscatterN(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float Mu, MuS, Nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
|
||||
float3 UVW = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
|
||||
float4 Ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, UVW) / PhaseFunctionR(Nu);
|
||||
return float4(Ray.rgb, 0);
|
||||
float mu, mus, nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
|
||||
float3 uvw = float3(input.TexCoord, (float(AtmosphereLayer) + 0.5f) / float(AtmosphericFogInscatterAltitudeSampleNum));
|
||||
float4 ray = AtmosphereDeltaSRTexture.Sample(SamplerLinearClamp, uvw) / PhaseFunctionR(nu);
|
||||
return float4(ray.xyz, 0);
|
||||
}
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Inscatter1_B(AtmosphereGSOutput input) : SV_Target
|
||||
float4 PS_Inscatter1_B(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float3 Ray;
|
||||
float3 Mie;
|
||||
float Mu, MuS, Nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, DhdH, Mu, MuS, Nu);
|
||||
Inscatter(AtmosphereR, Mu, MuS, Nu, Ray, Mie);
|
||||
|
||||
// Store separately Rayleigh and Mie contributions, WITHOUT the phase function factor (cf "Angular precision")
|
||||
return float4(Mie, 1);
|
||||
float3 ray;
|
||||
float3 mie;
|
||||
float mu, mus, nu;
|
||||
GetMuMuSNu(input.TexCoord, AtmosphereR, dhdh, mu, mus, nu);
|
||||
Inscatter(AtmosphereR, mu, mus, nu, ray, mie);
|
||||
return float4(mie, 1);
|
||||
}
|
||||
|
||||
@@ -5,61 +5,11 @@
|
||||
|
||||
#include "./Flax/Math.hlsl"
|
||||
|
||||
// Bidirectional reflectance distribution functions
|
||||
// Physically based shading model:
|
||||
// Microfacet specular = D*G*F / (4*NoL*NoV) = D*Vis*F
|
||||
// Vis = G / (4*NoL*NoV)
|
||||
|
||||
float3 Diffuse_Lambert(float3 diffuseColor)
|
||||
{
|
||||
return diffuseColor * (1 / PI);
|
||||
}
|
||||
|
||||
// [Burley 2012, "Physically-Based Shading at Disney"]
|
||||
float3 Diffuse_Burley(float3 diffuseColor, float roughness, float NoV, float NoL, float VoH)
|
||||
{
|
||||
float FD90 = 0.5 + 2 * VoH * VoH * roughness;
|
||||
float FdV = 1 + (FD90 - 1) * Pow5(1 - NoV);
|
||||
float FdL = 1 + (FD90 - 1) * Pow5(1 - NoL);
|
||||
return diffuseColor * ((1 / PI) * FdV * FdL);
|
||||
}
|
||||
|
||||
// [Gotanda 2012, "Beyond a Simple Physically Based Blinn-Phong Model in Real-Time"]
|
||||
float3 Diffuse_OrenNayar(float3 diffuseColor, float roughness, float NoV, float NoL, float VoH)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
float s = a;
|
||||
float s2 = s * s;
|
||||
float VoL = 2 * VoH * VoH - 1;
|
||||
float Cosri = VoL - NoV * NoL;
|
||||
float C1 = 1 - 0.5 * s2 / (s2 + 0.33);
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * (Cosri >= 0 ? rcp( max(NoL, NoV)) : 1);
|
||||
return diffuseColor / PI * (C1 + C2) * (1 + roughness * 0.5);
|
||||
}
|
||||
|
||||
float PhongShadingPow(float x, float y)
|
||||
{
|
||||
return ClampedPow(x, y);
|
||||
}
|
||||
|
||||
// [Blinn 1977, "Models of light reflection for computer synthesized pictures"]
|
||||
float D_Blinn(float roughness, float NoH)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float n = 2 / a2 - 2;
|
||||
return (n + 2) / (2 * PI) * PhongShadingPow(NoH, n);
|
||||
}
|
||||
|
||||
// [Beckmann 1963, "The scattering of electromagnetic waves from rough surfaces"]
|
||||
float D_Beckmann(float roughness, float NoH)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float NoH2 = NoH * NoH;
|
||||
return exp((NoH2 - 1) / (a2 * NoH2)) / (PI * a2 * NoH2 * NoH2);
|
||||
}
|
||||
|
||||
// GGX / Trowbridge-Reitz
|
||||
// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"]
|
||||
float D_GGX(float roughness, float NoH)
|
||||
@@ -70,36 +20,6 @@ float D_GGX(float roughness, float NoH)
|
||||
return a2 / (PI * d * d);
|
||||
}
|
||||
|
||||
// Anisotropic GGX
|
||||
// [Burley 2012, "Physically-Based Shading at Disney"]
|
||||
float D_GGXaniso(float roughnessX, float roughnessY, float NoH, float3 H, float3 X, float3 Y)
|
||||
{
|
||||
float ax = roughnessX * roughnessX;
|
||||
float ay = roughnessY * roughnessY;
|
||||
float XoH = dot(X, H);
|
||||
float YoH = dot(Y, H);
|
||||
float d = XoH * XoH / (ax * ax) + YoH * YoH / (ay * ay) + NoH * NoH;
|
||||
return 1 / (PI * ax * ay * d * d);
|
||||
}
|
||||
|
||||
float Vis_Implicit()
|
||||
{
|
||||
return 0.25;
|
||||
}
|
||||
|
||||
// [Neumann et al. 1999, "Compact metallic reflectance models"]
|
||||
float Vis_Neumann(float NoV, float NoL)
|
||||
{
|
||||
return 1 / (4 * max(NoL, NoV));
|
||||
}
|
||||
|
||||
// [Kelemen 2001, "A microfacet based coupled specular-matte brdf model with importance sampling"]
|
||||
float Vis_Kelemen(float VoH)
|
||||
{
|
||||
// constant to prevent NaN
|
||||
return rcp(4 * VoH * VoH + 1e-5);
|
||||
}
|
||||
|
||||
// Tuned to match behavior of Vis_Smith
|
||||
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
|
||||
float Vis_Schlick(float roughness, float NoV, float NoL)
|
||||
@@ -116,8 +36,8 @@ float Vis_Smith(float roughness, float NoV, float NoL)
|
||||
{
|
||||
float a = Square(roughness);
|
||||
float a2 = a * a;
|
||||
float vis_SmithV = NoV + sqrt( NoV * (NoV - NoV * a2) + a2);
|
||||
float vis_SmithL = NoL + sqrt( NoL * (NoL - NoL * a2) + a2);
|
||||
float vis_SmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2);
|
||||
float vis_SmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2);
|
||||
return rcp(vis_SmithV * vis_SmithL);
|
||||
}
|
||||
|
||||
@@ -126,14 +46,9 @@ float Vis_Smith(float roughness, float NoV, float NoL)
|
||||
float Vis_SmithJointApprox(float roughness, float NoV, float NoL)
|
||||
{
|
||||
float a = Square(roughness);
|
||||
float vis_SmithV = NoL * (NoV * (1 - a) + a);
|
||||
float vis_SmithL = NoV * (NoL * (1 - a) + a);
|
||||
return 0.5 * rcp(vis_SmithV + vis_SmithL);
|
||||
}
|
||||
|
||||
float3 F_None(float3 specularColor)
|
||||
{
|
||||
return specularColor;
|
||||
float visSmithV = NoL * (NoV * (1 - a) + a);
|
||||
float visSmithL = NoV * (NoL * (1 - a) + a);
|
||||
return 0.5 * rcp(visSmithV + visSmithL);
|
||||
}
|
||||
|
||||
// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]
|
||||
@@ -143,86 +58,43 @@ float3 F_Schlick(float3 specularColor, float VoH)
|
||||
return saturate(50.0 * specularColor.g) * fc + (1 - fc) * specularColor;
|
||||
}
|
||||
|
||||
float3 F_Fresnel(float3 specularColor, float VoH)
|
||||
{
|
||||
float3 specularColorSqrt = sqrt(clamp(float3(0, 0, 0), float3(0.99, 0.99, 0.99), specularColor));
|
||||
float3 n = (1 + specularColorSqrt ) / (1 - specularColorSqrt);
|
||||
float3 g = sqrt(n * n + VoH * VoH - 1);
|
||||
return 0.5 * Square((g - VoH) / (g + VoH)) * (1 + Square(((g + VoH) * VoH - 1) / ((g - VoH) * VoH + 1)));
|
||||
}
|
||||
|
||||
float D_InvBlinn(float roughness, float NoH)
|
||||
{
|
||||
float m = roughness * roughness;
|
||||
float m2 = m * m;
|
||||
float A = 4;
|
||||
float cos2h = NoH * NoH;
|
||||
return rcp( PI * (1 + A * m2)) * (1 + A * exp(-cos2h / m2));
|
||||
}
|
||||
|
||||
float D_InvBeckmann(float roughness, float NoH)
|
||||
{
|
||||
float m = roughness * roughness;
|
||||
float m2 = m * m;
|
||||
float A = 4;
|
||||
float cos2h = NoH * NoH;
|
||||
float sin2h = 1 - cos2h;
|
||||
float sin4h = sin2h * sin2h;
|
||||
return rcp(PI * (1 + A * m2) * sin4h) * (sin4h + A * exp(-cos2h / (m2 * sin2h)));
|
||||
}
|
||||
|
||||
float D_InvGGX(float roughness, float NoH)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float A = 4;
|
||||
float d = (NoH - a2 * NoH) * NoH + a2;
|
||||
return rcp(PI * (1 + A * a2)) * (1 + 4 * a2 * a2 / (d * d));
|
||||
}
|
||||
|
||||
float Vis_Cloth(float NoV, float NoL)
|
||||
{
|
||||
return rcp(4 * (NoL + NoV - NoL * NoV));
|
||||
}
|
||||
|
||||
#define REFLECTION_CAPTURE_NUM_MIPS 7
|
||||
#define REFLECTION_CAPTURE_ROUGHEST_MIP 1
|
||||
#define REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE 1.2
|
||||
|
||||
half ProbeMipFromRoughness(half roughness)
|
||||
{
|
||||
half levelFrom1x1 = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
|
||||
return REFLECTION_CAPTURE_NUM_MIPS - 1 - levelFrom1x1;
|
||||
half mip1px = REFLECTION_CAPTURE_ROUGHEST_MIP - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
|
||||
return REFLECTION_CAPTURE_NUM_MIPS - 1 - mip1px;
|
||||
}
|
||||
|
||||
half SSRMipFromRoughness(half roughness)
|
||||
{
|
||||
half levelFrom1x1 = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
|
||||
return max(1, 10 - levelFrom1x1);
|
||||
half mip1px = 4 - REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE * log2(roughness);
|
||||
return max(1, 10 - mip1px);
|
||||
}
|
||||
|
||||
float ComputeReflectionCaptureRoughnessFromMip(float mip)
|
||||
{
|
||||
float levelFrom1x1 = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip;
|
||||
return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - levelFrom1x1) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE);
|
||||
float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip;
|
||||
return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE);
|
||||
}
|
||||
|
||||
// [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"]
|
||||
float3 EnvBRDFApprox(float3 specularColor, float roughness, float NoV)
|
||||
{
|
||||
// Approximate version, base for pre integrated version
|
||||
const half4 c0 = {-1, -0.0275, -0.572, 0.022};
|
||||
const half4 c1 = {1, 0.0425, 1.04, -0.04};
|
||||
const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
|
||||
const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
|
||||
half4 r = roughness * c0 + c1;
|
||||
half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
||||
half2 ab = half2(-1.04, 1.04) * a004 + r.zw;
|
||||
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
|
||||
}
|
||||
|
||||
// Pre integrated environment GF
|
||||
// Importance sampled preintegrated G * F
|
||||
float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness, float NoV)
|
||||
{
|
||||
// Importance sampled preintegrated G * F
|
||||
float2 ab = preIntegratedGF.SampleLevel(SamplerLinearClamp, float2(NoV, roughness), 0).rg;
|
||||
return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y;
|
||||
}
|
||||
@@ -231,29 +103,7 @@ float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness,
|
||||
|
||||
float RoughnessToSpecularPower(float roughness)
|
||||
{
|
||||
// TODO: use path tracer as a reference and calculate valid params for this conversion
|
||||
|
||||
return pow(2, 13 * (1 - roughness));
|
||||
|
||||
#if 0
|
||||
|
||||
float coeff = pow(4, roughness);
|
||||
coeff = max(coeff, 2.0f / (MAX_SPECULAR_POWER + 2.0f));
|
||||
return 2.0f / coeff - 2.0f;
|
||||
|
||||
//const float Log2Of1OnLn2_Plus1 = 1.52876637294; // log2(1 / ln(2)) + 1
|
||||
//return exp2(10 * roughness + Log2Of1OnLn2_Plus1);
|
||||
|
||||
//return pow(2, 2 * (1 - roughness));
|
||||
return pow(2, 13 * (1 - roughness));
|
||||
|
||||
//return exp2(10 * roughness + 1);
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
float SpecularPowerToRoughness(float specularPower)
|
||||
{
|
||||
return pow(-0.25f, specularPower * 0.5f + 1.0f);
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,67 +18,4 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3
|
||||
return inExtentX && inExtentY;
|
||||
}
|
||||
|
||||
// Computes where a ray hits a sphere (which is centered at the origin).
|
||||
// \param[in] rayOrigin The start position of the ray.
|
||||
// \param[in] rayDirection The normalized direction of the ray.
|
||||
// \param[in] radius The radius of the sphere.
|
||||
// \param[out] enter The ray parameter where the ray enters the sphere.
|
||||
// 0 if the ray is already in the sphere.
|
||||
// \param[out] exit The ray parameter where the ray exits the sphere.
|
||||
// \return 0 or a positive value if the ray hits the sphere. A negative value
|
||||
// if the ray does not touch the sphere.
|
||||
float HitSphere(float3 rayOrigin, float3 rayDirection, float radius, out float enter, out float exit)
|
||||
{
|
||||
// Solve the equation: ||rayOrigin + distance * rayDirection|| = r
|
||||
//
|
||||
// This is a straight-forward quadratic equation:
|
||||
// ||O + d * D|| = r
|
||||
// => (O + d * D)2 = r2 where V2 means V.V
|
||||
// => d2 * D2 + 2 * d * (O.D) + O2 - r2 = 0
|
||||
// D2 is 1 because the rayDirection is normalized.
|
||||
// => d = -O.D + sqrt((O.D)2 - O2 + r2)
|
||||
|
||||
float OD = dot(rayOrigin, rayDirection);
|
||||
float OO = dot(rayOrigin, rayOrigin);
|
||||
float radicand = OD * OD - OO + radius * radius;
|
||||
enter = max(0, -OD - sqrt(radicand));
|
||||
exit = -OD + sqrt(radicand);
|
||||
|
||||
// If radicand is negative then we do not have a result - no hit.
|
||||
return radicand;
|
||||
}
|
||||
|
||||
// Clips a ray to an AABB. Does not handle rays parallel to any of the planes.
|
||||
//
|
||||
// @param rayOrigin - The origin of the ray in world space.
|
||||
// @param rayEnd - The end of the ray in world space.
|
||||
// @param boxMin - The minimum extrema of the box.
|
||||
// @param boxMax - The maximum extrema of the box.
|
||||
// @return - Returns the closest intersection along the ray in x, and furthest in y.
|
||||
// If the ray did not intersect the box, then the furthest intersection <= the closest intersection.
|
||||
// The intersections will always be in the range [0,1], which corresponds to [rayOrigin, rayEnd] in worldspace.
|
||||
// To find the world space position of either intersection, simply plug it back into the ray equation:
|
||||
// WorldPos = RayOrigin + (rayEnd - RayOrigin) * Intersection;
|
||||
float2 LineBoxIntersect(float3 rayOrigin, float3 rayEnd, float3 boxMin, float3 boxMax)
|
||||
{
|
||||
float3 invRayDir = 1.0f / (rayEnd - rayOrigin);
|
||||
|
||||
//find the ray intersection with each of the 3 planes defined by the minimum extrema.
|
||||
float3 planeIntersections1 = (boxMin - rayOrigin) * invRayDir;
|
||||
//find the ray intersection with each of the 3 planes defined by the maximum extrema.
|
||||
float3 planeIntersections2 = (boxMax - rayOrigin) * invRayDir;
|
||||
//get the closest of these intersections along the ray
|
||||
float3 closestPlaneIntersections = min(planeIntersections1, planeIntersections2);
|
||||
//get the furthest of these intersections along the ray
|
||||
float3 furthestPlaneIntersections = max(planeIntersections1, planeIntersections2);
|
||||
|
||||
float2 boxIntersections;
|
||||
//find the furthest near intersection
|
||||
boxIntersections.x = max(closestPlaneIntersections.x, max(closestPlaneIntersections.y, closestPlaneIntersections.z));
|
||||
//find the closest far intersection
|
||||
boxIntersections.y = min(furthestPlaneIntersections.x, min(furthestPlaneIntersections.y, furthestPlaneIntersections.z));
|
||||
//clamp the intersections to be between rayOrigin and RayEnd on the ray
|
||||
return saturate(boxIntersections);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -45,31 +45,20 @@ Texture2D LutTexture : register(t0);
|
||||
// [Krystek 1985, "An algorithm to calculate correlated colour temperature"]
|
||||
float2 PlanckianLocusChromaticity(float temp)
|
||||
{
|
||||
float u = ( 0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp*temp ) / ( 1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp*temp );
|
||||
float v = ( 0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp*temp ) / ( 1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp*temp );
|
||||
|
||||
float x = 3*u / ( 2*u - 8*v + 4 );
|
||||
float y = 2*v / ( 2*u - 8*v + 4 );
|
||||
|
||||
return float2(x,y);
|
||||
float u = (0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp * temp) / (1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp * temp);
|
||||
float v = (0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp * temp) / (1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp * temp);
|
||||
float x = 3 * u / (2 * u - 8 * v + 4);
|
||||
float y = 2 * v / (2 * u - 8 * v + 4);
|
||||
return float2(x, y);
|
||||
}
|
||||
|
||||
// Accurate for 4000K < temp < 25000K
|
||||
// in: correlated color temperature
|
||||
// out: CIE 1931 chromaticity
|
||||
float2 D_IlluminantChromaticity(float temp)
|
||||
// Calculates chromaticity from temperature
|
||||
float2 IlluminantChromaticity(float temp)
|
||||
{
|
||||
// Correct for revision of Plank's law
|
||||
// This makes 6500 == D65
|
||||
temp *= 1.4388 / 1.438;
|
||||
|
||||
float x = temp <= 7000 ?
|
||||
0.244063 + ( 0.09911e3 + ( 2.9678e6 - 4.6070e9 / temp ) / temp ) / temp :
|
||||
0.237040 + ( 0.24748e3 + ( 1.9018e6 - 2.0064e9 / temp ) / temp ) / temp;
|
||||
|
||||
float y = -3 * x*x + 2.87 * x - 0.275;
|
||||
|
||||
return float2(x,y);
|
||||
float x = temp <= 7000 ? 0.244063 + (0.09911e3 + (2.9678e6 - 4.6070e9 / temp) / temp) / temp : 0.237040 + (0.24748e3 + (1.9018e6 - 2.0064e9 / temp) / temp) / temp;
|
||||
float y = -3 * x * x + 2.87 * x - 0.275;
|
||||
return float2(x, y);
|
||||
}
|
||||
|
||||
// Find closest color temperature to chromaticity
|
||||
@@ -77,38 +66,31 @@ float2 D_IlluminantChromaticity(float temp)
|
||||
float CorrelatedColortemperature(float x, float y)
|
||||
{
|
||||
float n = (x - 0.3320) / (0.1858 - y);
|
||||
return -449 * n*n*n + 3525 * n*n - 6823.3 * n + 5520.33;
|
||||
return -449 * n * n * n + 3525 * n * n - 6823.3 * n + 5520.33;
|
||||
}
|
||||
|
||||
// [McCamy 1992, "Correlated color temperature as an explicit function of chromaticity coordinates"]
|
||||
float2 PlanckianIsothermal(float temp, float tint)
|
||||
{
|
||||
float u = ( 0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp*temp ) / ( 1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp*temp );
|
||||
float v = ( 0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp*temp ) / ( 1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp*temp );
|
||||
|
||||
float ud = ( -1.13758118e9f - 1.91615621e6f * temp - 1.53177f * temp*temp ) / Square( 1.41213984e6f + 1189.62f * temp + temp*temp );
|
||||
float vd = ( 1.97471536e9f - 705674.0f * temp - 308.607f * temp*temp ) / Square( 6.19363586e6f - 179.456f * temp + temp*temp );
|
||||
|
||||
float2 uvd = normalize( float2( u, v ) );
|
||||
|
||||
// Correlated color temperature is meaningful within +/- 0.05
|
||||
float u = (0.860117757f + 1.54118254e-4f * temp + 1.28641212e-7f * temp * temp) / (1.0f + 8.42420235e-4f * temp + 7.08145163e-7f * temp * temp);
|
||||
float v = (0.317398726f + 4.22806245e-5f * temp + 4.20481691e-8f * temp * temp) / (1.0f - 2.89741816e-5f * temp + 1.61456053e-7f * temp * temp);
|
||||
float ud = (-1.13758118e9f - 1.91615621e6f * temp - 1.53177f * temp * temp) / Square(1.41213984e6f + 1189.62f * temp + temp * temp);
|
||||
float vd = (1.97471536e9f - 705674.0f * temp - 308.607f * temp * temp) / Square(6.19363586e6f - 179.456f * temp + temp * temp);
|
||||
float2 uvd = normalize(float2(u, v));
|
||||
u += -uvd.y * tint * 0.05;
|
||||
v += uvd.x * tint * 0.05;
|
||||
|
||||
float x = 3*u / ( 2*u - 8*v + 4 );
|
||||
float y = 2*v / ( 2*u - 8*v + 4 );
|
||||
|
||||
float x = 3 * u / (2 * u - 8 * v + 4);
|
||||
float y = 2 * v / (2 * u - 8 * v + 4);
|
||||
return float2(x,y);
|
||||
}
|
||||
|
||||
float3 WhiteBalance(float3 linearColor)
|
||||
{
|
||||
float2 srcWhiteDaylight = D_IlluminantChromaticity(WhiteTemp);
|
||||
float2 srcWhiteDaylight = IlluminantChromaticity(WhiteTemp);
|
||||
float2 srcWhitePlankian = PlanckianLocusChromaticity(WhiteTemp);
|
||||
|
||||
float2 srcWhite = WhiteTemp < 4000 ? srcWhitePlankian : srcWhiteDaylight;
|
||||
float2 d65White = float2(0.31270, 0.32900);
|
||||
|
||||
// Offset along isotherm
|
||||
float2 d65White = float2(0.31270f, 0.32900f);
|
||||
float2 isothermal = PlanckianIsothermal(WhiteTemp, WhiteTint) - srcWhitePlankian;
|
||||
srcWhite += isothermal;
|
||||
|
||||
@@ -121,8 +103,8 @@ float3 WhiteBalance(float3 linearColor)
|
||||
float3 ColorCorrect(float3 color, float luma, float4 saturation, float4 contrast, float4 gamma, float4 gain, float4 offset)
|
||||
{
|
||||
color = max(0, lerp(luma.xxx, color, saturation.xyz * saturation.w));
|
||||
color = pow(color * (1.0 / 0.18), contrast.xyz * contrast.w) * 0.18;
|
||||
color = pow(color, 1.0 / (gamma.xyz * gamma.w));
|
||||
color = pow(color * (1.0f / 0.18f), contrast.xyz * contrast.w) * 0.18f;
|
||||
color = pow(color, 1.0f / (gamma.xyz * gamma.w));
|
||||
color = color * (gain.xyz * gain.w) + (offset.xyz + offset.w);
|
||||
return color;
|
||||
}
|
||||
@@ -130,35 +112,12 @@ float3 ColorCorrect(float3 color, float luma, float4 saturation, float4 contrast
|
||||
float3 ColorCorrectAll(float3 color)
|
||||
{
|
||||
float luma = dot(color, AP1_RGB2Y);
|
||||
|
||||
// Shadow CC
|
||||
float3 ccColorShadows = ColorCorrect(color, luma,
|
||||
ColorSaturationShadows,
|
||||
ColorContrastShadows,
|
||||
ColorGammaShadows,
|
||||
ColorGainShadows,
|
||||
ColorOffsetShadows);
|
||||
float3 ccColorShadows = ColorCorrect(color, luma, ColorSaturationShadows, ColorContrastShadows, ColorGammaShadows, ColorGainShadows, ColorOffsetShadows);
|
||||
float ccWeightShadows = 1 - smoothstep(0, ColorCorrectionShadowsMax, luma);
|
||||
|
||||
// Highlight CC
|
||||
float3 ccColorHighlights = ColorCorrect(color, luma,
|
||||
ColorSaturationHighlights,
|
||||
ColorContrastHighlights,
|
||||
ColorGammaHighlights,
|
||||
ColorGainHighlights,
|
||||
ColorOffsetHighlights);
|
||||
float3 ccColorHighlights = ColorCorrect(color, luma,ColorSaturationHighlights, ColorContrastHighlights, ColorGammaHighlights, ColorGainHighlights, ColorOffsetHighlights);
|
||||
float ccWeightHighlights = smoothstep(ColorCorrectionHighlightsMin, 1, luma);
|
||||
|
||||
// Midtone CC
|
||||
float3 ccColorMidtones = ColorCorrect(color, luma,
|
||||
ColorSaturationMidtones,
|
||||
ColorContrastMidtones,
|
||||
ColorGammaMidtones,
|
||||
ColorGainMidtones,
|
||||
ColorOffsetMidtones);
|
||||
float3 ccColorMidtones = ColorCorrect(color, luma, ColorSaturationMidtones, ColorContrastMidtones, ColorGammaMidtones, ColorGainMidtones, ColorOffsetMidtones);
|
||||
float ccWeightMidtones = 1 - ccWeightShadows - ccWeightHighlights;
|
||||
|
||||
// Blend shadow, midtone and highlight CCs
|
||||
return ccColorShadows * ccWeightShadows + ccColorMidtones * ccWeightMidtones + ccColorHighlights * ccWeightHighlights;
|
||||
}
|
||||
|
||||
@@ -207,8 +166,7 @@ float3 TonemapNeutral(float3 linearColor)
|
||||
|
||||
float3 TonemapACES(float3 linearColor)
|
||||
{
|
||||
// The code in this file was originally written by Stephen Hill (@self_shadow), who deserves all
|
||||
// credit for coming up with this fit and implementing it. Buy him a beer next time you see him. :)
|
||||
// The code was originally written by Stephen Hill (@self_shadow).
|
||||
|
||||
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
|
||||
static const float3x3 ACESInputMat =
|
||||
@@ -280,7 +238,6 @@ float4 CombineLUTs(float2 uv, uint layerIndex)
|
||||
|
||||
// Move from encoded LUT color space to linear color
|
||||
//float3 linearColor = encodedColor.rgb; // Default
|
||||
//float3 linearColor = LogCToLinear(encodedColor.rgb); // LogC
|
||||
float3 linearColor = LogToLinear(encodedColor.rgb) - LogToLinear(0); // Log
|
||||
|
||||
// Apply white balance
|
||||
|
||||
@@ -196,25 +196,6 @@ struct Quad_GS2PS
|
||||
uint LayerIndex : SV_RenderTargetArrayIndex;
|
||||
};
|
||||
|
||||
float VisualizeTextureGrid(float2 texCoord, float2 textureSize, float gridSize)
|
||||
{
|
||||
if (gridSize > 0)
|
||||
{
|
||||
float2 t = frac(texCoord * textureSize);
|
||||
return (t.x < gridSize || t.x > 1 - gridSize) || (t.y < gridSize || t.y > 1 - gridSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
int2 t = int2(texCoord * textureSize);
|
||||
return (t.x % 2 == 0 && t.y % 2 == 0) || (t.x % 2 == 1 && t.y % 2 == 1);
|
||||
}
|
||||
}
|
||||
|
||||
float BiasedNDotL(float NoL)
|
||||
{
|
||||
return saturate(NoL * 1.08f - 0.08f);
|
||||
}
|
||||
|
||||
float Luminance(float3 color)
|
||||
{
|
||||
return dot(color, float3(0.299f, 0.587f, 0.114f));
|
||||
|
||||
@@ -38,22 +38,14 @@ float4 CalculateCombinedFog(float3 worldPosition, float sceneDepth, float3 volum
|
||||
float excludeDistance = 0;
|
||||
|
||||
#if VOLUMETRIC_FOG
|
||||
|
||||
// Volumetric fog covers up to MaxDistance along ViewZ, exclude analytical fog from this range
|
||||
excludeDistance = max(ExponentialHeightFog.VolumetricFogMaxDistance - 100, 0);
|
||||
|
||||
#endif
|
||||
|
||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, worldPosition, GBuffer.ViewPos, excludeDistance);
|
||||
|
||||
#if VOLUMETRIC_FOG
|
||||
|
||||
// Sample volumetric fog lookup table
|
||||
float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0);
|
||||
|
||||
// Mix volumetric and analytical fog
|
||||
fog = float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a);
|
||||
|
||||
#endif
|
||||
|
||||
return fog;
|
||||
|
||||
@@ -9,73 +9,50 @@
|
||||
// http://gpuopen.com/optimized-reversible-tonemapper-for-resolve/
|
||||
float3 FastTonemap(float3 c)
|
||||
{
|
||||
return c * rcp(max(max(c.r, c.g), c.b) + 1.0);
|
||||
return c * rcp(max(max(c.r, c.g), c.b) + 1.0);
|
||||
}
|
||||
|
||||
float4 FastTonemap(float4 c)
|
||||
{
|
||||
return float4(FastTonemap(c.rgb), c.a);
|
||||
return float4(FastTonemap(c.rgb), c.a);
|
||||
}
|
||||
|
||||
float3 FastTonemap(float3 c, float w)
|
||||
{
|
||||
return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0));
|
||||
return c * (w * rcp(max(max(c.r, c.g), c.b) + 1.0));
|
||||
}
|
||||
|
||||
float4 FastTonemap(float4 c, float w)
|
||||
{
|
||||
return float4(FastTonemap(c.rgb, w), c.a);
|
||||
return float4(FastTonemap(c.rgb, w), c.a);
|
||||
}
|
||||
|
||||
float3 FastTonemapInvert(float3 c)
|
||||
{
|
||||
return c * rcp(1.0 - max(max(c.r, c.g), c.b));
|
||||
return c * rcp(1.0 - max(max(c.r, c.g), c.b));
|
||||
}
|
||||
|
||||
float4 FastTonemapInvert(float4 c)
|
||||
{
|
||||
return float4(FastTonemapInvert(c.rgb), c.a);
|
||||
return float4(FastTonemapInvert(c.rgb), c.a);
|
||||
}
|
||||
|
||||
half3 LinearTo709Branchless(half3 linearColor)
|
||||
{
|
||||
linearColor = max(6.10352e-5, linearColor);
|
||||
return min(linearColor * 4.5, pow(max(linearColor, 0.018), 0.45) * 1.099 - 0.099);
|
||||
}
|
||||
|
||||
half3 LinearToSrgbBranchless(half3 linearColor)
|
||||
{
|
||||
linearColor = max(6.10352e-5, linearColor);
|
||||
return min(linearColor * 12.92, pow(max(linearColor, 0.00313067), 1.0/2.4) * 1.055 - 0.055);
|
||||
}
|
||||
|
||||
half LinearToSrgbBranchingChannel(half linearColor)
|
||||
half LinearToSrgbChannel(half linearColor)
|
||||
{
|
||||
if (linearColor < 0.00313067)
|
||||
return linearColor * 12.92;
|
||||
return pow(linearColor, (1.0/2.4)) * 1.055 - 0.055;
|
||||
return pow(linearColor, (1.0 / 2.4)) * 1.055 - 0.055;
|
||||
}
|
||||
|
||||
half3 LinearToSrgbBranching(half3 linearColor)
|
||||
half3 LinearToSrgb(half3 linearColor)
|
||||
{
|
||||
return half3(
|
||||
LinearToSrgbBranchingChannel(linearColor.r),
|
||||
LinearToSrgbBranchingChannel(linearColor.g),
|
||||
LinearToSrgbBranchingChannel(linearColor.b));
|
||||
LinearToSrgbChannel(linearColor.r),
|
||||
LinearToSrgbChannel(linearColor.g),
|
||||
LinearToSrgbChannel(linearColor.b));
|
||||
}
|
||||
|
||||
half3 LinearToSrgb(half3 linearColor)
|
||||
{
|
||||
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM4
|
||||
// Branching is faster than branchless on PC
|
||||
return LinearToSrgbBranching(linearColor);
|
||||
#else
|
||||
// Use branchless on mobile
|
||||
return LinearToSrgbBranchless(linearColor);
|
||||
#endif
|
||||
}
|
||||
|
||||
half3 sRGBToLinear(half3 color)
|
||||
half3 sRGBToLinear(half3 color)
|
||||
{
|
||||
color = max(6.10352e-5, color);
|
||||
return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92);
|
||||
@@ -103,7 +80,7 @@ float3 LinearToLog(float3 linearColor)
|
||||
const float exposureGrey = 444;
|
||||
|
||||
// Using stripped down, 'pure log', formula. Parameterized by grey points and dynamic range covered.
|
||||
float3 logColor = log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0; // scalar: 3log2 3mad
|
||||
float3 logColor = log2(linearColor) / linearRange - log2(linearGrey) / linearRange + exposureGrey / 1023.0; // scalar: 3log2 3mad
|
||||
//float3 logColor = (log2(linearColor) - log2(linearGrey)) / linearRange + exposureGrey / 1023.0;
|
||||
//float3 logColor = log2(linearColor / linearGrey) / linearRange + exposureGrey / 1023.0;
|
||||
//float3 logColor = (0.432699 * log10(0.5 * linearColor + 0.037584) + 0.616596) + 0.03; // SLog
|
||||
@@ -113,74 +90,4 @@ float3 LinearToLog(float3 linearColor)
|
||||
return logColor;
|
||||
}
|
||||
|
||||
// Alexa LogC converters (El 1000)
|
||||
// See http://www.vocas.nl/webfm_send/964
|
||||
// Max range is ~58.85666
|
||||
|
||||
struct ParamsLogC
|
||||
{
|
||||
float cut;
|
||||
float a, b, c, d, e, f;
|
||||
};
|
||||
|
||||
static const ParamsLogC LogC =
|
||||
{
|
||||
0.011361, // cut
|
||||
5.555556, // a
|
||||
0.047996, // b
|
||||
0.244161, // c
|
||||
0.386036, // d
|
||||
5.301883, // e
|
||||
0.092819 // f
|
||||
};
|
||||
|
||||
float3 LinearToLogC(float3 linearColor)
|
||||
{
|
||||
return LogC.c * log10(LogC.a * linearColor + LogC.b) + LogC.d;
|
||||
}
|
||||
|
||||
float3 LogCToLinear(float3 logcColor)
|
||||
{
|
||||
return (pow(10.0, (logcColor - LogC.d) / LogC.c) - LogC.b) / LogC.a;
|
||||
}
|
||||
|
||||
// Dolby PQ transforms
|
||||
float3 ST2084ToLinear(float3 pq)
|
||||
{
|
||||
const float m1 = 0.1593017578125; // = 2610. / 4096. * .25;
|
||||
const float m2 = 78.84375; // = 2523. / 4096. * 128;
|
||||
const float c1 = 0.8359375; // = 2392. / 4096. * 32 - 2413./4096.*32 + 1;
|
||||
const float c2 = 18.8515625; // = 2413. / 4096. * 32;
|
||||
const float c3 = 18.6875; // = 2392. / 4096. * 32;
|
||||
const float C = 10000.;
|
||||
|
||||
float3 Np = pow(pq, 1.0 / m2);
|
||||
float3 L = Np - c1;
|
||||
L = max(0.0, L);
|
||||
L = L / (c2 - c3 * Np);
|
||||
L = pow(L, 1.0 / m1);
|
||||
float3 P = L * C;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
float3 LinearToST2084(float3 lin)
|
||||
{
|
||||
const float m1 = 0.1593017578125; // = 2610. / 4096. * .25;
|
||||
const float m2 = 78.84375; // = 2523. / 4096. * 128;
|
||||
const float c1 = 0.8359375; // = 2392. / 4096. * 32 - 2413./4096.*32 + 1;
|
||||
const float c2 = 18.8515625; // = 2413. / 4096. * 32;
|
||||
const float c3 = 18.6875; // = 2392. / 4096. * 32;
|
||||
const float C = 10000.0;
|
||||
|
||||
float3 L = lin / C;
|
||||
float3 Lm = pow(L, m1);
|
||||
float3 N1 = (c1 + c2 * Lm);
|
||||
float3 N2 = (1.0 + c3 * Lm);
|
||||
float3 N = N1 * rcp(N2);
|
||||
float3 P = pow(N, m2);
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -60,18 +60,6 @@ void CS_GenerateHistogram(uint3 groupId : SV_GroupID, uint3 dispatchThreadId : S
|
||||
if (dispatchThreadId.x < InputSize.x && dispatchThreadId.y < InputSize.y)
|
||||
{
|
||||
uint weight = 1u;
|
||||
|
||||
// Vignette weighting to add more focus on the center of the screen
|
||||
#if 0
|
||||
{
|
||||
float2 uv = float2(dispatchThreadId.xy) / float2(InputSize.x, InputSize.y);
|
||||
float2 d = abs(uv - (0.5).xx);
|
||||
float factor = saturate(1.0 - dot(d, d));
|
||||
factor *= factor;
|
||||
weight = (uint)(64.0 * factor);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 color = Input[dispatchThreadId.xy].xyz;
|
||||
float luminance = Luminance(color);
|
||||
float logLuminance = ComputeHistogramPositionFromLuminance(luminance);
|
||||
|
||||
@@ -3,19 +3,13 @@
|
||||
#ifndef __IES_PROFILE__
|
||||
#define __IES_PROFILE__
|
||||
|
||||
// Apply 1D IES light profile texture
|
||||
// Calculate IES light profile from 1D texture
|
||||
float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection)
|
||||
{
|
||||
float3 negLightDirection = normalize(worldPosition - lightPosition);
|
||||
|
||||
// -1..1
|
||||
float dotProd = dot(negLightDirection, lightDirection);
|
||||
// -PI..PI (this distortion could be put into the texture but not without quality loss or more memory)
|
||||
float angle = asin(dotProd);
|
||||
// 0..1
|
||||
float normAngle = angle / PI + 0.5f;
|
||||
|
||||
return tex.SampleLevel(SamplerLinearClamp, float2(normAngle, 0), 0).r;
|
||||
float3 l = normalize(worldPosition - lightPosition);
|
||||
float d = dot(lightPosition, lightDirection);
|
||||
float angle = asin(d) / PI + 0.5f;
|
||||
return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,7 +28,6 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
|
||||
float NoH = saturate(dot(N, H));
|
||||
float VoH = saturate(dot(V, H));
|
||||
|
||||
// Generalized microfacet specular
|
||||
float D = D_GGX(gBuffer.Roughness, NoH) * energy;
|
||||
float Vis = Vis_SmithJointApprox(gBuffer.Roughness, NoV, NoL);
|
||||
float3 F = F_Schlick(specularColor, VoH);
|
||||
@@ -113,7 +112,6 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
{
|
||||
float4 result = 0;
|
||||
float3 V = normalize(viewPos - gBuffer.WorldPos);
|
||||
float3 ToLight = lightData.Direction;
|
||||
float3 N = gBuffer.Normal;
|
||||
float3 L = lightData.Direction; // no need to normalize
|
||||
float NoL = saturate(dot(N, L));
|
||||
@@ -124,7 +122,7 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
// Calculate attenuation
|
||||
if (isRadial)
|
||||
{
|
||||
GetRadialLightAttenuation(lightData, isSpotLight, gBuffer.WorldPos, N, 1, ToLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation);
|
||||
GetRadialLightAttenuation(lightData, isSpotLight, gBuffer.WorldPos, N, 1, lightData.Direction, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation);
|
||||
}
|
||||
float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation;
|
||||
|
||||
@@ -132,13 +130,13 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask);
|
||||
|
||||
// Reduce shadow mapping artifacts
|
||||
shadow.SurfaceShadow *= saturate(NoL * 6 - 0.2);
|
||||
shadow.SurfaceShadow *= saturate(NoL * 6.0f - 0.2f);
|
||||
|
||||
BRANCH
|
||||
if (shadow.SurfaceShadow + shadow.TransmissionShadow > 0)
|
||||
{
|
||||
gBuffer.Roughness = max(gBuffer.Roughness, lightData.MinRoughness);
|
||||
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, ToLight, L, V, N);
|
||||
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, lightData.Direction, L, V, N);
|
||||
|
||||
// Calculate direct lighting
|
||||
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
||||
|
||||
@@ -47,8 +47,8 @@ struct LightingData
|
||||
// WorldLightVector is the vector from the position being shaded to the light, divided by the radius of the light.
|
||||
float RadialAttenuation(float3 worldLightVector, half falloffExponent)
|
||||
{
|
||||
float normalizeDistanceSquared = dot(worldLightVector, worldLightVector);
|
||||
return pow(1.0f - saturate(normalizeDistanceSquared), falloffExponent);
|
||||
float t = dot(worldLightVector, worldLightVector);
|
||||
return pow(1.0f - saturate(t), falloffExponent);
|
||||
}
|
||||
|
||||
// Calculates attenuation for a spot light. Where L normalize vector to light.
|
||||
@@ -127,30 +127,21 @@ float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLig
|
||||
BRANCH
|
||||
if (lightData.SourceLength > 0)
|
||||
{
|
||||
// Energy conservation
|
||||
float lineAngle = saturate(lightData.SourceLength * invDistToLight);
|
||||
energy *= m / saturate(m + 0.5 * lineAngle);
|
||||
|
||||
// Closest point on line segment to ray
|
||||
float3 l01 = lightData.Direction * lightData.SourceLength;
|
||||
float3 l0 = toLight - 0.5 * l01;
|
||||
float3 l1 = toLight + 0.5 * l01;
|
||||
|
||||
float a = Square(lightData.SourceLength);
|
||||
float b = dot(r, l01);
|
||||
float t = saturate(dot(l0, b * r - l01) / (a - b * b));
|
||||
|
||||
toLight = l0 + t * l01;
|
||||
}
|
||||
|
||||
BRANCH
|
||||
if (lightData.SourceRadius > 0)
|
||||
{
|
||||
// Energy conservation
|
||||
float sphereAngle = saturate(lightData.SourceRadius * invDistToLight);
|
||||
energy *= Square(m / saturate(m + 0.5 * sphereAngle));
|
||||
|
||||
// Closest point on sphere to ray
|
||||
float3 closestPointOnRay = dot(toLight, r) * r;
|
||||
float3 centerToRay = closestPointOnRay - toLight;
|
||||
float3 closestPointOnSphere = toLight + centerToRay * saturate(lightData.SourceRadius * rsqrt(dot(centerToRay, centerToRay)));
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
uint NextPow2(uint value)
|
||||
{
|
||||
uint mask = (1 << firstbithigh(value)) - 1;
|
||||
return (value + mask) & ~mask;
|
||||
uint mask = (1 << firstbithigh(value)) - 1;
|
||||
return (value + mask) & ~mask;
|
||||
}
|
||||
|
||||
float3 SafeNormalize(float3 v)
|
||||
@@ -17,7 +17,6 @@ float3 SafeNormalize(float3 v)
|
||||
float3 ExtractLargestComponent(float3 v)
|
||||
{
|
||||
float3 a = abs(v);
|
||||
|
||||
if (a.x > a.y)
|
||||
{
|
||||
if (a.x > a.z)
|
||||
@@ -32,161 +31,160 @@ float3 ExtractLargestComponent(float3 v)
|
||||
return float3(0, v.y > 0 ? 1 : -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return float3(0, 0, v.z > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
float Square(float x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float2 Square(float2 x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float3 Square(float3 x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float4 Square(float4 x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float Pow2(float x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float2 Pow2(float2 x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float3 Pow2(float3 x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float4 Pow2(float4 x)
|
||||
{
|
||||
return x*x;
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float Pow3(float x)
|
||||
{
|
||||
return x*x*x;
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
float2 Pow3(float2 x)
|
||||
{
|
||||
return x*x*x;
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
float3 Pow3(float3 x)
|
||||
{
|
||||
return x*x*x;
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
float4 Pow3(float4 x)
|
||||
{
|
||||
return x*x*x;
|
||||
return x * x * x;
|
||||
}
|
||||
|
||||
float Pow4(float x)
|
||||
{
|
||||
float xx = x*x;
|
||||
float xx = x * x;
|
||||
return xx * xx;
|
||||
}
|
||||
|
||||
float2 Pow4(float2 x)
|
||||
{
|
||||
float2 xx = x*x;
|
||||
float2 xx = x * x;
|
||||
return xx * xx;
|
||||
}
|
||||
|
||||
float3 Pow4(float3 x)
|
||||
{
|
||||
float3 xx = x*x;
|
||||
float3 xx = x * x;
|
||||
return xx * xx;
|
||||
}
|
||||
|
||||
float4 Pow4(float4 x)
|
||||
{
|
||||
float4 xx = x*x;
|
||||
float4 xx = x * x;
|
||||
return xx * xx;
|
||||
}
|
||||
|
||||
float Pow5(float x)
|
||||
{
|
||||
float xx = x*x;
|
||||
float xx = x * x;
|
||||
return xx * xx * x;
|
||||
}
|
||||
|
||||
float2 Pow5(float2 x)
|
||||
{
|
||||
float2 xx = x*x;
|
||||
float2 xx = x * x;
|
||||
return xx * xx * x;
|
||||
}
|
||||
|
||||
float3 Pow5(float3 x)
|
||||
{
|
||||
float3 xx = x*x;
|
||||
float3 xx = x * x;
|
||||
return xx * xx * x;
|
||||
}
|
||||
|
||||
float4 Pow5(float4 x)
|
||||
{
|
||||
float4 xx = x*x;
|
||||
float4 xx = x * x;
|
||||
return xx * xx * x;
|
||||
}
|
||||
|
||||
float Pow6(float x)
|
||||
{
|
||||
float xx = x*x;
|
||||
float xx = x * x;
|
||||
return xx * xx * xx;
|
||||
}
|
||||
|
||||
float2 Pow6(float2 x)
|
||||
{
|
||||
float2 xx = x*x;
|
||||
float2 xx = x * x;
|
||||
return xx * xx * xx;
|
||||
}
|
||||
|
||||
float3 Pow6(float3 x)
|
||||
{
|
||||
float3 xx = x*x;
|
||||
float3 xx = x * x;
|
||||
return xx * xx * xx;
|
||||
}
|
||||
|
||||
float4 Pow6(float4 x)
|
||||
{
|
||||
float4 xx = x*x;
|
||||
float4 xx = x * x;
|
||||
return xx * xx * xx;
|
||||
}
|
||||
|
||||
float ClampedPow(float x,float y)
|
||||
float ClampedPow(float x, float y)
|
||||
{
|
||||
return pow(max(abs(x), 0.000001f),y);
|
||||
return pow(max(abs(x), 0.000001f), y);
|
||||
}
|
||||
|
||||
float2 ClampedPow(float2 x,float2 y)
|
||||
float2 ClampedPow(float2 x, float2 y)
|
||||
{
|
||||
return pow(max(abs(x), float2(0.000001f, 0.000001f)), y);
|
||||
}
|
||||
|
||||
float3 ClampedPow(float3 x,float3 y)
|
||||
float3 ClampedPow(float3 x, float3 y)
|
||||
{
|
||||
return pow(max(abs(x), float3(0.000001f, 0.000001f, 0.000001f)), y);
|
||||
}
|
||||
}
|
||||
|
||||
float4 ClampedPow(float4 x,float4 y)
|
||||
float4 ClampedPow(float4 x, float4 y)
|
||||
{
|
||||
return pow(max(abs(x), float4(0.000001f, 0.000001f, 0.000001f, 0.000001f)), y);
|
||||
}
|
||||
}
|
||||
|
||||
float4 FindQuatBetween(float3 from, float3 to)
|
||||
{
|
||||
@@ -208,8 +206,8 @@ float4 FindQuatBetween(float3 from, float3 to)
|
||||
{
|
||||
w = 0.f;
|
||||
result = abs(from.x) > abs(from.y)
|
||||
? float4(-from.z, 0.f, from.x, w)
|
||||
: float4(0.f, -from.z, from.y, w);
|
||||
? float4(-from.z, 0.f, from.x, w)
|
||||
: float4(0.f, -from.z, from.y, w);
|
||||
}
|
||||
|
||||
return normalize(result);
|
||||
@@ -218,23 +216,13 @@ float4 FindQuatBetween(float3 from, float3 to)
|
||||
// Rotates Position about the given axis by the given angle, in radians, and returns the offset to position
|
||||
float3 RotateAboutAxis(float4 normalizedRotationAxisAndAngle, float3 positionOnAxis, float3 position)
|
||||
{
|
||||
// Project position onto the rotation axis and find the closest point on the axis to Position
|
||||
float3 closestPointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis);
|
||||
|
||||
// Construct orthogonal axes in the plane of the rotation
|
||||
float3 axisU = position - closestPointOnAxis;
|
||||
float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU);
|
||||
float cosAngle, sinAngle;
|
||||
sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle);
|
||||
|
||||
// Rotate using the orthogonal axes
|
||||
float3 rotation = axisU * cosAngle + axisV * sinAngle;
|
||||
|
||||
// Reconstruct the rotated world space position
|
||||
float3 rotatedPosition = closestPointOnAxis + rotation;
|
||||
|
||||
// Convert from position to a position offset
|
||||
return rotatedPosition - position;
|
||||
float3 pointOnAxis = positionOnAxis + normalizedRotationAxisAndAngle.xyz * dot(normalizedRotationAxisAndAngle.xyz, position - positionOnAxis);
|
||||
float3 axisU = position - pointOnAxis;
|
||||
float3 axisV = cross(normalizedRotationAxisAndAngle.xyz, axisU);
|
||||
float cosAngle, sinAngle;
|
||||
sincos(normalizedRotationAxisAndAngle.w, sinAngle, cosAngle);
|
||||
float3 rotation = axisU * cosAngle + axisV * sinAngle;
|
||||
return pointOnAxis + rotation - position;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -133,10 +133,10 @@ float4 ImportanceSampleGGX(float2 e, float roughness)
|
||||
|
||||
// Multiple importance sampling power heuristic of two functions with a power of two.
|
||||
// [Veach 1997, "Robust Monte Carlo Methods for Light Transport Simulation"]
|
||||
float MISWeight(uint number, float pdf, uint otherNumber, float otherpdf)
|
||||
float MISWeight(uint number, float PDF, uint otherNumber, float otherPDF)
|
||||
{
|
||||
float weight = number * pdf;
|
||||
float otherWeight = otherNumber * otherpdf;
|
||||
float weight = number * PDF;
|
||||
float otherWeight = otherNumber * otherPDF;
|
||||
return weight * weight / (weight * weight + otherWeight * otherWeight);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,16 +43,16 @@ Texture2D Input0 : register(t0);
|
||||
Texture2D Input1 : register(t1);
|
||||
Texture2D Input2 : register(t2);
|
||||
|
||||
// Converts a motion vector into RGBA color.
|
||||
float4 VectorToColor(float2 mv)
|
||||
// Calculates the color for the a motion vector debugging
|
||||
float4 VectorToColor(float2 motionVector)
|
||||
{
|
||||
float phi = atan2(mv.x, mv.y);
|
||||
float phi = atan2(motionVector.x, motionVector.y);
|
||||
float hue = (phi / PI + 1) * 0.5;
|
||||
|
||||
float r = abs(hue * 6 - 3) - 1;
|
||||
float g = 2 - abs(hue * 6 - 2);
|
||||
float b = 2 - abs(hue * 6 - 4);
|
||||
float a = length(mv);
|
||||
float a = length(motionVector);
|
||||
|
||||
return saturate(float4(r, g, b, a));
|
||||
}
|
||||
@@ -61,18 +61,15 @@ float4 VectorToColor(float2 mv)
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_MotionVectorsDebug(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float4 src = SAMPLE_RT(Input0, input.TexCoord);
|
||||
float4 color = SAMPLE_RT(Input0, input.TexCoord);
|
||||
float2 motionVector = SAMPLE_RT(Input1, input.TexCoord).rg * (DebugAmplitude * 5.0f);
|
||||
float4 motionColor = VectorToColor(motionVector);
|
||||
|
||||
float2 mv = SAMPLE_RT(Input1, input.TexCoord).rg * (DebugAmplitude * 5.0f);
|
||||
float4 mc = VectorToColor(mv);
|
||||
float colorRation = saturate(2 - DebugBlend * 2);
|
||||
float motionColorRatio = saturate(DebugBlend * 2);
|
||||
color.rgb = lerp(color.rgb * colorRation, motionColor.rgb, motionColor.a * motionColorRatio);
|
||||
|
||||
float3 rgb = mc.rgb;
|
||||
|
||||
float src_ratio = saturate(2 - DebugBlend * 2);
|
||||
float mc_ratio = saturate(DebugBlend * 2);
|
||||
rgb = lerp(src.rgb * src_ratio, rgb, mc.a * mc_ratio);
|
||||
|
||||
return float4(rgb, src.a);
|
||||
return color;
|
||||
}
|
||||
|
||||
// Motion vector arrow data from VS to PS
|
||||
@@ -88,60 +85,51 @@ ArrowVaryings VS_DebugArrow(uint VertexId : SV_VertexID)
|
||||
{
|
||||
// Screen aspect ratio
|
||||
float aspect = GBuffer.ScreenSize.x * GBuffer.ScreenSize.w;
|
||||
float inv_aspect = GBuffer.ScreenSize.y * GBuffer.ScreenSize.z;
|
||||
float aspectInv = GBuffer.ScreenSize.y * GBuffer.ScreenSize.z;
|
||||
|
||||
// Vertex IDs
|
||||
uint arrow_id = VertexId / 6;
|
||||
uint point_id = VertexId - arrow_id * 6;
|
||||
uint arrowId = VertexId / 6;
|
||||
uint pointId = VertexId - arrowId * 6;
|
||||
|
||||
// Column/Row number of the arrow
|
||||
uint row = arrow_id / DebugColumnCount;
|
||||
uint col = arrow_id - row * DebugColumnCount;
|
||||
// Column and row number of the arrow
|
||||
uint row = arrowId / DebugColumnCount;
|
||||
uint col = arrowId - row * DebugColumnCount;
|
||||
|
||||
// Texture coordinate of the reference point
|
||||
// Get the motion vector
|
||||
float2 uv = float2((col + 0.5) / DebugColumnCount, (row + 0.5) / DebugRowCount);
|
||||
|
||||
// Retrieve the motion vector
|
||||
float2 mv = SAMPLE_RT(Input1, uv).rg * DebugAmplitude;
|
||||
float2 motionVector = SAMPLE_RT(Input1, uv).rg * DebugAmplitude;
|
||||
|
||||
// Arrow color
|
||||
float4 color = VectorToColor(mv);
|
||||
float4 color = VectorToColor(motionVector);
|
||||
|
||||
// Arrow vertex position parameter (0 = origin, 1 = head)
|
||||
float arrow_l = point_id > 0;
|
||||
|
||||
// Rotation matrix for the arrow head
|
||||
float2 head_dir = normalize(mv * float2(aspect, 1));
|
||||
float2x2 head_rot = float2x2(head_dir.y, head_dir.x, -head_dir.x, head_dir.y);
|
||||
|
||||
// Offset for arrow head vertices
|
||||
float head_x = point_id == 3 ? -1 : (point_id == 5 ? 1 : 0);
|
||||
head_x *= arrow_l * 0.3 * saturate(length(mv) * DebugRowCount);
|
||||
|
||||
float2 head_offs = float2(head_x, -abs(head_x));
|
||||
head_offs = mul(head_rot, head_offs) * float2(inv_aspect, 1);
|
||||
// Arrow transformation
|
||||
float isEnd = pointId > 0;
|
||||
float2 direction = normalize(motionVector * float2(aspect, 1));
|
||||
float2x2 rotation = float2x2(direction.y, direction.x, -direction.x, direction.y);
|
||||
float offsetStart = pointId == 3 ? -1 : (pointId == 5 ? 1 : 0);
|
||||
offsetStart *= isEnd * 0.3f * saturate(length(motionVector) * DebugRowCount);
|
||||
float2 offset = float2(offsetStart, -abs(offsetStart));
|
||||
offset = mul(rotation, offset) * float2(aspectInv, 1);
|
||||
|
||||
// Vertex position in the clip space
|
||||
float2 vp = mv * arrow_l + head_offs * 2 / DebugRowCount + uv * 2 - 1;
|
||||
float2 pos = motionVector * isEnd + offset * 2 / DebugRowCount + uv * 2.0f - 1.0f;
|
||||
|
||||
// Convert to the screen coordinates
|
||||
float2 scoord = (vp + 1) * 0.5 * GBuffer.ScreenSize.xy;
|
||||
|
||||
// Snap to a pixel-perfect position.
|
||||
scoord = round(scoord);
|
||||
float2 posSS = (pos + 1) * 0.5f * GBuffer.ScreenSize.xy;
|
||||
posSS = round(posSS);
|
||||
|
||||
// Bring back to the clip space
|
||||
vp = (scoord + 0.5) * GBuffer.ScreenSize.zw * 2 - 1;
|
||||
vp.y *= -1;
|
||||
pos = (posSS + 0.5f) * GBuffer.ScreenSize.zw * 2.0f - 1.0f;
|
||||
pos.y *= -1;
|
||||
|
||||
// Color tweaks
|
||||
color.rgb = lerp(color.rgb, 1, 0.5);
|
||||
color.rgb = lerp(color.rgb, 1, 0.5f);
|
||||
color.a = DebugBlend;
|
||||
|
||||
// Output
|
||||
ArrowVaryings output;
|
||||
output.Position = float4(vp, 0, 1);
|
||||
output.ScreenUV = scoord;
|
||||
output.Position = float4(pos, 0, 1);
|
||||
output.ScreenUV = posSS;
|
||||
output.Color = color;
|
||||
return output;
|
||||
}
|
||||
@@ -150,8 +138,8 @@ META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_DebugArrow(ArrowVaryings input) : SV_Target
|
||||
{
|
||||
// Pseudo anti-aliasing
|
||||
float aa = length(frac(input.ScreenUV) - 0.5) / 0.707;
|
||||
aa *= (aa * (aa * 0.305306011 + 0.682171111) + 0.012522878); // gamma
|
||||
float aa = length(frac(input.ScreenUV) - 0.5f) / 0.707f;
|
||||
aa *= (aa * (aa * 0.305306011f + 0.682171111f) + 0.012522878f);
|
||||
return float4(input.Color.rgb, input.Color.a * aa);
|
||||
}
|
||||
|
||||
@@ -190,10 +178,10 @@ float4 PS_VelocitySetup(Quad_VS2PS input) : SV_Target
|
||||
float2 v = SAMPLE_RT(Input0, input.TexCoord).rg;
|
||||
|
||||
// Apply the exposure time and convert to the pixel space
|
||||
v *= (VelocityScale * 0.5) * GBuffer.ScreenSize.xy;
|
||||
v *= (VelocityScale * 0.5f) * GBuffer.ScreenSize.xy;
|
||||
|
||||
// Clamp the vector with the maximum blur radius
|
||||
v /= max(1.0, length(v) * RcpMaxBlurRadius);
|
||||
v /= max(1.0f, length(v) * RcpMaxBlurRadius);
|
||||
|
||||
// Sample the depth of the pixel
|
||||
float depth = SAMPLE_RT(Input1, input.TexCoord).r;
|
||||
@@ -201,7 +189,7 @@ float4 PS_VelocitySetup(Quad_VS2PS input) : SV_Target
|
||||
depth = LinearizeZ(gBufferData, depth);
|
||||
|
||||
// Pack into 10/10/10/2 format
|
||||
return float4((v * RcpMaxBlurRadius + 1.0) * 0.5, depth, 0.0);
|
||||
return float4((v * RcpMaxBlurRadius + 1.0f) * 0.5f, depth, 0.0f);
|
||||
}
|
||||
|
||||
float2 MaxV(float2 v1, float2 v2)
|
||||
@@ -220,40 +208,40 @@ float4 PS_TileMax1(Quad_VS2PS input) : SV_Target
|
||||
float2 v3 = SAMPLE_RT(Input0, input.TexCoord + d.xw).rg;
|
||||
float2 v4 = SAMPLE_RT(Input0, input.TexCoord + d.zw).rg;
|
||||
|
||||
v1 = (v1 * 2.0 - 1.0) * MaxBlurRadius;
|
||||
v2 = (v2 * 2.0 - 1.0) * MaxBlurRadius;
|
||||
v3 = (v3 * 2.0 - 1.0) * MaxBlurRadius;
|
||||
v4 = (v4 * 2.0 - 1.0) * MaxBlurRadius;
|
||||
v1 = (v1 * 2.0f - 1.0f) * MaxBlurRadius;
|
||||
v2 = (v2 * 2.0f - 1.0f) * MaxBlurRadius;
|
||||
v3 = (v3 * 2.0f - 1.0f) * MaxBlurRadius;
|
||||
v4 = (v4 * 2.0f - 1.0f) * MaxBlurRadius;
|
||||
|
||||
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
|
||||
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// Pixel Shader for TileMax filter (2 pixel width)
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_TileMax2(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float4 d = TexelSize2.xyxy * float4(-0.5, -0.5, 0.5, 0.5);
|
||||
float4 d = TexelSize2.xyxy * float4(-0.5f, -0.5f, 0.5f, 0.5f);
|
||||
|
||||
float2 v1 = SAMPLE_RT(Input0, input.TexCoord + d.xy).rg;
|
||||
float2 v2 = SAMPLE_RT(Input0, input.TexCoord + d.zy).rg;
|
||||
float2 v3 = SAMPLE_RT(Input0, input.TexCoord + d.xw).rg;
|
||||
float2 v4 = SAMPLE_RT(Input0, input.TexCoord + d.zw).rg;
|
||||
|
||||
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
|
||||
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// Pixel Shader for TileMax filter (2 pixel width)
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_TileMax4(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float4 d = TexelSize4.xyxy * float4(-0.5, -0.5, 0.5, 0.5);
|
||||
float4 d = TexelSize4.xyxy * float4(-0.5f, -0.5f, 0.5f, 0.5f);
|
||||
|
||||
float2 v1 = SAMPLE_RT(Input0, input.TexCoord + d.xy).rg;
|
||||
float2 v2 = SAMPLE_RT(Input0, input.TexCoord + d.zy).rg;
|
||||
float2 v3 = SAMPLE_RT(Input0, input.TexCoord + d.xw).rg;
|
||||
float2 v4 = SAMPLE_RT(Input0, input.TexCoord + d.zw).rg;
|
||||
|
||||
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0, 0.0);
|
||||
return float4(MaxV(MaxV(MaxV(v1, v2), v3), v4), 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// Pixel Shader for TileMax filter (variable width)
|
||||
@@ -306,7 +294,7 @@ float4 PS_NeighborMax(Quad_VS2PS input) : SV_Target
|
||||
float2 vb = MaxV(v4, MaxV(v5, v6));
|
||||
float2 vc = MaxV(v7, MaxV(v8, v9));
|
||||
|
||||
return float4(MaxV(va, MaxV(vb, vc)) * (1.0 / cw), 0.0, 0.0);
|
||||
return float4(MaxV(va, MaxV(vb, vc)) * (1.0f / cw), 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
// Interleaved gradient function from Jimenez 2014
|
||||
@@ -314,8 +302,8 @@ float4 PS_NeighborMax(Quad_VS2PS input) : SV_Target
|
||||
float GradientNoise(float2 uv)
|
||||
{
|
||||
uv = floor(uv * GBuffer.ScreenSize.xy);
|
||||
float f = dot(float2(0.06711056, 0.00583715), uv);
|
||||
return frac(52.9829189 * frac(f));
|
||||
float f = dot(float2(0.06711056f, 0.00583715f), uv);
|
||||
return frac(52.9829189f * frac(f));
|
||||
}
|
||||
|
||||
// Returns true or false with a given interval
|
||||
@@ -328,103 +316,77 @@ bool Interval(float phase, float interval)
|
||||
float2 JitterTile(float2 uv)
|
||||
{
|
||||
float rx, ry;
|
||||
sincos(GradientNoise(uv + float2(2.0, 0.0)) * (2.0f * PI), ry, rx);
|
||||
return float2(rx, ry) * TexelSizeNM.xy * 0.25;
|
||||
sincos(GradientNoise(uv + float2(2.0f, 0.0f)) * (2.0f * PI), ry, rx);
|
||||
return float2(rx, ry) * TexelSizeNM.xy * 0.25f;
|
||||
}
|
||||
|
||||
// Velocity sampling function
|
||||
float3 SampleVelocity(float2 uv)
|
||||
{
|
||||
float3 v = SAMPLE_RT(Input1, uv).xyz;
|
||||
return float3((v.xy * 2.0 - 1.0) * MaxBlurRadius, v.z);
|
||||
return float3((v.xy * 2.0f - 1.0f) * MaxBlurRadius, v.z);
|
||||
}
|
||||
|
||||
// Pixel Shader for reconstruction filter (applies the motion blur to the frame)
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Reconstruction(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
// Color sample at the center point
|
||||
const float4 c_p = SAMPLE_RT(Input0, input.TexCoord);
|
||||
// Sample at the current location
|
||||
const float4 color = SAMPLE_RT(Input0, input.TexCoord);
|
||||
const float3 velocity = SampleVelocity(input.TexCoord);
|
||||
const float velocityLen = max(length(velocity.xy), 0.5);
|
||||
const float depthInv = 1.0 / velocity.z;
|
||||
|
||||
// Velocity/Depth sample at the center point
|
||||
const float3 vd_p = SampleVelocity(input.TexCoord);
|
||||
const float l_v_p = max(length(vd_p.xy), 0.5);
|
||||
const float rcp_d_p = 1.0 / vd_p.z;
|
||||
const float2 velocityMax = SAMPLE_RT(Input2, input.TexCoord + JitterTile(input.TexCoord)).xy;
|
||||
const float velocityMaxLength = length(velocityMax);
|
||||
if (velocityMaxLength < 2.0f)
|
||||
return color;
|
||||
const float2 velocityWeighted = (velocityLen * 2.0f > velocityMaxLength) ? velocity.xy * (velocityMaxLength / velocityLen) : velocityMax;
|
||||
|
||||
// NeighborMax vector sample at the center point
|
||||
const float2 v_max = SAMPLE_RT(Input2, input.TexCoord + JitterTile(input.TexCoord)).xy;
|
||||
const float l_v_max = length(v_max);
|
||||
const float rcp_l_v_max = 1.0 / l_v_max;
|
||||
// Calculate the amount of samples
|
||||
const float sc = floor(min(LoopCount, velocityMaxLength * 0.5f));
|
||||
|
||||
// Escape early if the NeighborMax vector is small enough
|
||||
if (l_v_max < 2.0)
|
||||
return c_p;
|
||||
|
||||
// Use V_p as a secondary sampling direction except when it's too small
|
||||
// compared to V_max. This vector is rescaled to be the length of V_max.
|
||||
const float2 v_alt = (l_v_p * 2.0 > l_v_max) ? vd_p.xy * (l_v_max / l_v_p) : v_max;
|
||||
|
||||
// Determine the sample count.
|
||||
const float sc = floor(min(LoopCount, l_v_max * 0.5));
|
||||
|
||||
// Loop variables (starts from the outermost sample)
|
||||
const float dt = 1.0 / sc;
|
||||
const float t_offs = (GradientNoise(input.TexCoord) - 0.5) * dt;
|
||||
float t = 1.0 - dt * 0.5;
|
||||
float count = 0.0;
|
||||
|
||||
// Background velocity
|
||||
// This is used for tracking the maximum velocity in the background layer
|
||||
float l_v_bg = max(l_v_p, 1.0);
|
||||
|
||||
// Color accumlation
|
||||
float4 acc = 0.0;
|
||||
// Accumlation loop
|
||||
float backgroudVelocity = max(velocityLen, 1.0f);
|
||||
const float dt = 1.0f / sc;
|
||||
const float offsetNoise = (GradientNoise(input.TexCoord) - 0.5f) * dt;
|
||||
float t = 1.0f - dt * 0.5f;
|
||||
float count = 0.0f;
|
||||
float4 sum = 0.0f;
|
||||
LOOP
|
||||
while (t > dt * 0.25)
|
||||
{
|
||||
// Sampling direction (switched per every two samples)
|
||||
const float2 v_s = Interval(count, 4.0) ? v_alt : v_max;
|
||||
const float2 sampleVelocity = Interval(count, 4.0) ? velocityWeighted : velocityMax;
|
||||
|
||||
// Sample position (inverted per every sample)
|
||||
const float t_s = (Interval(count, 2.0) ? -t : t) + t_offs;
|
||||
const float samplePosition = (Interval(count, 2.0) ? -t : t) + offsetNoise;
|
||||
|
||||
// Distance to the sample position
|
||||
const float l_t = l_v_max * abs(t_s);
|
||||
// Calculate UVs for the sample position
|
||||
const float2 sampleUV = input.TexCoord + sampleVelocity * samplePosition * GBuffer.ScreenSize.zw;
|
||||
|
||||
// UVs for the sample position
|
||||
const float2 uv0 = input.TexCoord + v_s * t_s * GBuffer.ScreenSize.zw;
|
||||
//const float2 uv1 = input.TexCoord + v_s * t_s * MotionVectorsTexelSize.xy;
|
||||
const float2 uv1 = uv0;
|
||||
|
||||
// Color sample
|
||||
const float3 c = SAMPLE_RT(Input0, uv0).rgb;
|
||||
|
||||
// Velocity/Depth sample
|
||||
const float3 vd = SampleVelocity(uv1);
|
||||
|
||||
// Background/Foreground separation
|
||||
const float fg = saturate((vd_p.z - vd.z) * 20.0 * rcp_d_p);
|
||||
// Sample color and velocity with depth
|
||||
const float3 c = SAMPLE_RT(Input0, sampleUV).rgb;
|
||||
const float3 velocityDepth = SampleVelocity(sampleUV);
|
||||
|
||||
// Length of the velocity vector
|
||||
const float l_v = lerp(l_v_bg, length(vd.xy), fg);
|
||||
const float foreground = saturate((velocity.z - velocityDepth.z) * 20.0f * depthInv);
|
||||
const float sampleVelocityLength = lerp(backgroudVelocity, length(velocityDepth.xy), foreground);
|
||||
|
||||
// Sample weight
|
||||
// (Distance test) * (Spreading out by motion) * (Triangular window)
|
||||
const float w = saturate(l_v - l_t) / l_v * (1.2 - t);
|
||||
// Apply color accumulation
|
||||
float weight = saturate(sampleVelocityLength - (velocityMaxLength * abs(samplePosition))) / sampleVelocityLength * (1.2f - t);
|
||||
sum += float4(c, 1.0) * weight;
|
||||
|
||||
// Color accumulation
|
||||
acc += float4(c, 1.0) * w;
|
||||
// Calculate the background velocity
|
||||
backgroudVelocity = max(backgroudVelocity, sampleVelocityLength);
|
||||
|
||||
// Update the background velocity.
|
||||
l_v_bg = max(l_v_bg, l_v);
|
||||
|
||||
// Advance to the next sample.
|
||||
t = Interval(count, 2.0) ? t - dt : t;
|
||||
count += 1.0;
|
||||
// Move to the next sample
|
||||
t = Interval(count, 2.0f) ? t - dt : t;
|
||||
count += 1.0f;
|
||||
}
|
||||
|
||||
// Add the center sample
|
||||
acc += float4(c_p.rgb, 1.0) * (1.2 / (l_v_bg * sc * 2.0));
|
||||
sum += float4(color.rgb, 1.0f) * (1.2f / (backgroudVelocity * sc * 2.0f));
|
||||
|
||||
return float4(acc.rgb / acc.a, c_p.a);
|
||||
return float4(sum.rgb / sum.a, color.a);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
//
|
||||
// Perlin noise shader by toneburst:
|
||||
// http://machinesdontcare.wordpress.com/2009/06/25/3d-perlin-noise-sphere-vertex-shader-sourcecode/
|
||||
//
|
||||
// Lens flares by John Chapman:
|
||||
//https://john-chapman.github.io/2017/11/05/pseudo-lens-flare.html
|
||||
//
|
||||
|
||||
#include "./Flax/Common.hlsl"
|
||||
#include "./Flax/Random.hlsl"
|
||||
@@ -107,7 +111,6 @@ half3 ColorLookupTable(half3 linearColor)
|
||||
{
|
||||
// Move from linear color to encoded LUT color space
|
||||
//float3 encodedColor = linearColor; // Default
|
||||
//float3 encodedColor = saturate(LinearToLogC(linearColor)); // LogC
|
||||
float3 encodedColor = LinearToLog(linearColor + LogToLinear(0)); // Log
|
||||
|
||||
float3 uvw = encodedColor * ((LUTSize - 1) / LUTSize) + (0.5f / LUTSize);
|
||||
|
||||
@@ -25,46 +25,44 @@ float4 SampleCubemap(float3 uv)
|
||||
return Cube.SampleLevel(SamplerLinearClamp, uv, SourceMipIndex);
|
||||
}
|
||||
|
||||
float3 GetCubemapVector(float2 scaledUVs)
|
||||
float3 UvToCubeMapUv(float2 uv)
|
||||
{
|
||||
float3 cubeCoordinates;
|
||||
|
||||
float3 coords;
|
||||
if (CubeFace == 0)
|
||||
{
|
||||
cubeCoordinates = float3(1, -scaledUVs.y, -scaledUVs.x);
|
||||
coords = float3(1, -uv.y, -uv.x);
|
||||
}
|
||||
else if (CubeFace == 1)
|
||||
{
|
||||
cubeCoordinates = float3(-1, -scaledUVs.y, scaledUVs.x);
|
||||
coords = float3(-1, -uv.y, uv.x);
|
||||
}
|
||||
else if (CubeFace == 2)
|
||||
{
|
||||
cubeCoordinates = float3(scaledUVs.x, 1, scaledUVs.y);
|
||||
coords = float3(uv.x, 1, uv.y);
|
||||
}
|
||||
else if (CubeFace == 3)
|
||||
{
|
||||
cubeCoordinates = float3(scaledUVs.x, -1, -scaledUVs.y);
|
||||
coords = float3(uv.x, -1, -uv.y);
|
||||
}
|
||||
else if (CubeFace == 4)
|
||||
{
|
||||
cubeCoordinates = float3(scaledUVs.x, -scaledUVs.y, 1);
|
||||
coords = float3(uv.x, -uv.y, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cubeCoordinates = float3(-scaledUVs.x, -scaledUVs.y, -1);
|
||||
coords = float3(-uv.x, -uv.y, -1);
|
||||
}
|
||||
|
||||
return cubeCoordinates;
|
||||
return coords;
|
||||
}
|
||||
|
||||
// Pixel Shader for filtring probe mip levels
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_FilterFace(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float2 scaledUVs = input.TexCoord * 2 - 1;
|
||||
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
|
||||
float2 uv = input.TexCoord * 2 - 1;
|
||||
float3 cubeCoordinates = UvToCubeMapUv(uv);
|
||||
|
||||
#define NUM_FILTER_SAMPLES 512
|
||||
#define NUM_FILTER_SAMPLES 512
|
||||
|
||||
float3 N = normalize(cubeCoordinates);
|
||||
float roughness = ComputeReflectionCaptureRoughnessFromMip(SourceMipIndex);
|
||||
@@ -102,19 +100,15 @@ float4 PS_CopyFace(Quad_VS2PS input) : SV_Target
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_CalcDiffuseIrradiance(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float2 scaledUVs = input.TexCoord * 2 - 1;
|
||||
float3 cubeCoordinates = normalize(GetCubemapVector(scaledUVs));
|
||||
|
||||
float squaredUVs = 1 + dot(scaledUVs, scaledUVs);
|
||||
|
||||
// Dividing by NumSamples here to keep the sum in the range of fp16, once we get down to the 1x1 mip
|
||||
float2 uv = input.TexCoord * 2 - 1;
|
||||
float3 cubeCoordinates = normalize(UvToCubeMapUv(uv));
|
||||
float squaredUVs = 1 + dot(uv, uv);
|
||||
float weight = 4 / (sqrt(squaredUVs) * squaredUVs);
|
||||
|
||||
ThreeBandSHVector shCoefficients = SHBasisFunction3(cubeCoordinates);
|
||||
float currentSHCoefficient = dot(shCoefficients.V0, CoefficientMask0) + dot(shCoefficients.V1, CoefficientMask1) + shCoefficients.V2 * CoefficientMask2;
|
||||
|
||||
float3 radiance = SampleCubemap(cubeCoordinates).rgb;
|
||||
|
||||
return float4(radiance * currentSHCoefficient * weight, weight);
|
||||
}
|
||||
|
||||
@@ -124,23 +118,23 @@ float4 PS_AccDiffuseIrradiance(Quad_VS2PS input) : SV_Target
|
||||
{
|
||||
float4 result = 0;
|
||||
{
|
||||
float2 scaledUVs = saturate(input.TexCoord + Sample01.xy) * 2 - 1;
|
||||
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
|
||||
float2 uv = saturate(input.TexCoord + Sample01.xy) * 2 - 1;
|
||||
float3 cubeCoordinates = UvToCubeMapUv(uv);
|
||||
result += SampleCubemap(cubeCoordinates);
|
||||
}
|
||||
{
|
||||
float2 scaledUVs = saturate(input.TexCoord + Sample01.zw) * 2 - 1;
|
||||
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
|
||||
float2 uv = saturate(input.TexCoord + Sample01.zw) * 2 - 1;
|
||||
float3 cubeCoordinates = UvToCubeMapUv(uv);
|
||||
result += SampleCubemap(cubeCoordinates);
|
||||
}
|
||||
{
|
||||
float2 scaledUVs = saturate(input.TexCoord + Sample23.xy) * 2 - 1;
|
||||
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
|
||||
float2 uv = saturate(input.TexCoord + Sample23.xy) * 2 - 1;
|
||||
float3 cubeCoordinates = UvToCubeMapUv(uv);
|
||||
result += SampleCubemap(cubeCoordinates);
|
||||
}
|
||||
{
|
||||
float2 scaledUVs = saturate(input.TexCoord + Sample23.zw) * 2 - 1;
|
||||
float3 cubeCoordinates = GetCubemapVector(scaledUVs);
|
||||
float2 uv = saturate(input.TexCoord + Sample23.zw) * 2 - 1;
|
||||
float3 cubeCoordinates = UvToCubeMapUv(uv);
|
||||
result += SampleCubemap(cubeCoordinates);
|
||||
}
|
||||
return result / 4.0f;
|
||||
|
||||
@@ -3,104 +3,38 @@
|
||||
#ifndef __RANDOM__
|
||||
#define __RANDOM__
|
||||
|
||||
// @param xy should be a integer position (e.g. pixel position on the screen), repeats each 128x128 pixels similar to a texture lookup but is only ALU
|
||||
float PseudoRandom(float2 xy)
|
||||
{
|
||||
float2 pos = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f);
|
||||
return frac(dot(pos.xyx * pos.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
|
||||
float2 p = frac(xy / 128.0f) * 128.0f + float2(-64.340622f, -72.465622f);
|
||||
return frac(dot(p.xyx * p.xyy, float3(20.390625f, 60.703125f, 2.4281209f)));
|
||||
}
|
||||
|
||||
// Find good arbitrary axis vectors to represent U and V axes of a plane, given just the normal
|
||||
void FindBestAxisVectors(float3 input, out float3 axis1, out float3 axis2)
|
||||
{
|
||||
const float3 N = abs(input);
|
||||
|
||||
// Find best basis vectors
|
||||
if( N.z > N.x && N.z > N.y )
|
||||
{
|
||||
const float3 a = abs(input);
|
||||
if (a.z > a.x && a.z > a.y)
|
||||
axis1 = float3(1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
axis1 = float3(0, 0, 1);
|
||||
}
|
||||
|
||||
axis1 = normalize(axis1 - input * dot(axis1, input));
|
||||
axis2 = cross(axis1, input);
|
||||
}
|
||||
|
||||
// References for noise:
|
||||
//
|
||||
// Improved Perlin noise
|
||||
// http://mrl.nyu.edu/~perlin/noise/
|
||||
// http://http.developer.nvidia.com/GPUGems/gpugems_ch05.html
|
||||
// Modified Noise for Evaluation on Graphics Hardware
|
||||
// http://www.csee.umbc.edu/~olano/papers/mNoise.pdf
|
||||
// Perlin Noise
|
||||
// http://mrl.nyu.edu/~perlin/doc/oscar.html
|
||||
// Fast Gradient Noise
|
||||
// http://prettyprocs.wordpress.com/2012/10/20/fast-perlin-noise
|
||||
|
||||
|
||||
// -------- ALU based method ---------
|
||||
|
||||
/*
|
||||
* Pseudo random number generator, based on "TEA, a tiny Encrytion Algorithm"
|
||||
* http://citeseer.ist.psu.edu/viewdoc/download?doi=10.1.1.45.281&rep=rep1&type=pdf
|
||||
* @param v - old seed (full 32bit range)
|
||||
* @param iterationCount - >=1, bigger numbers cost more performance but improve quality
|
||||
* @return new seed
|
||||
*/
|
||||
uint2 ScrambleTEA(uint2 v, uint iterationCount = 3)
|
||||
{
|
||||
// Start with some random data (numbers can be arbitrary but those have been used by others and seem to work well)
|
||||
uint k[4] ={ 0xA341316Cu , 0xC8013EA4u , 0xAD90777Du , 0x7E95761Eu };
|
||||
|
||||
uint y = v[0];
|
||||
uint z = v[1];
|
||||
uint sum = 0;
|
||||
|
||||
UNROLL
|
||||
for (uint i = 0; i < iterationCount; i++)
|
||||
{
|
||||
sum += 0x9e3779b9;
|
||||
y += (z << 4u) + k[0] ^ z + sum ^ (z >> 5u) + k[1];
|
||||
z += (y << 4u) + k[2] ^ y + sum ^ (y >> 5u) + k[3];
|
||||
}
|
||||
|
||||
return uint2(y, z);
|
||||
}
|
||||
|
||||
// Computes a pseudo random number for a given integer 2D position
|
||||
// @param v - old seed (full 32bit range)
|
||||
// @return random number in the range -1 .. 1
|
||||
float ComputeRandomFrom2DPosition(uint2 v)
|
||||
{
|
||||
return (ScrambleTEA(v).x & 0xffff ) / (float)(0xffff) * 2 - 1;
|
||||
}
|
||||
|
||||
// Computes a pseudo random number for a given integer 2D position
|
||||
// @param v - old seed (full 32bit range)
|
||||
// @return random number in the range -1 .. 1
|
||||
float ComputeRandomFrom3DPosition(int3 v)
|
||||
{
|
||||
// numbers found by experimentation
|
||||
return ComputeRandomFrom2DPosition(v.xy ^ (uint2(0x123456, 0x23446) * v.zx) );
|
||||
}
|
||||
|
||||
// Evaluate polynomial to get smooth transitions for Perlin noise (2 add, 5 mul)
|
||||
float PerlinRamp(in float t)
|
||||
{
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
float2 PerlinRamp(in float2 t)
|
||||
{
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
float3 PerlinRamp(in float3 t)
|
||||
{
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
float4 PerlinRamp(in float4 t)
|
||||
{
|
||||
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||
|
||||
@@ -11,21 +11,20 @@ struct ThreeBandSHVector
|
||||
half V2;
|
||||
};
|
||||
|
||||
ThreeBandSHVector SHBasisFunction3(half3 inputVector)
|
||||
ThreeBandSHVector SHBasisFunction3(half3 v)
|
||||
{
|
||||
ThreeBandSHVector result;
|
||||
|
||||
result.V0.x = 0.282095f;
|
||||
result.V0.y = -0.488603f * inputVector.y;
|
||||
result.V0.z = 0.488603f * inputVector.z;
|
||||
result.V0.w = -0.488603f * inputVector.x;
|
||||
result.V0.y = -0.488603f * v.y;
|
||||
result.V0.z = 0.488603f * v.z;
|
||||
result.V0.w = -0.488603f * v.x;
|
||||
|
||||
half3 vectorSquared = inputVector * inputVector;
|
||||
result.V1.x = 1.092548f * inputVector.x * inputVector.y;
|
||||
result.V1.y = -1.092548f * inputVector.y * inputVector.z;
|
||||
result.V1.z = 0.315392f * (3.0f * inputVector.z - 1.0f);
|
||||
result.V1.w = -1.092548f * inputVector.x * inputVector.z;
|
||||
result.V2 = 0.546274f * (inputVector.x - inputVector.y);
|
||||
result.V1.x = 1.092548f * v.x * v.y;
|
||||
result.V1.y = -1.092548f * v.y * v.z;
|
||||
result.V1.z = 0.315392f * (3.0f * v.z - 1.0f);
|
||||
result.V1.w = -1.092548f * v.x * v.z;
|
||||
result.V2 = 0.546274f * (v.x - v.y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -39,25 +39,8 @@ struct LightShadowData
|
||||
#define DECLARE_LIGHTSHADOWDATA_ACCESS(uniformName) LightShadowData Get##uniformName##Data() { return uniformName; }
|
||||
#endif
|
||||
|
||||
// Gets the cube texture face index to use for shadow map sampling for the given view-to-light direction vector
|
||||
// Where: direction = normalize(worldPosition - lightPosition)
|
||||
int GetCubeFaceIndex(float3 direction)
|
||||
{
|
||||
int cubeFaceIndex;
|
||||
float3 absDirection = abs(direction);
|
||||
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
|
||||
if (maxDirection == absDirection.x)
|
||||
cubeFaceIndex = absDirection.x == direction.x ? 0 : 1;
|
||||
else if (maxDirection == absDirection.y)
|
||||
cubeFaceIndex = absDirection.y == direction.y ? 2 : 3;
|
||||
else
|
||||
cubeFaceIndex = absDirection.z == direction.z ? 4 : 5;
|
||||
return cubeFaceIndex;
|
||||
}
|
||||
|
||||
float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
|
||||
{
|
||||
// Note: offsetScale should be multiplied by 2*ShadowMapTextureTexelSize on CPU
|
||||
float normalOffsetScale = saturate(1.0f - NoL);
|
||||
return normal * (offsetScale * normalOffsetScale);
|
||||
}
|
||||
@@ -65,8 +48,6 @@ float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
|
||||
float CalculateSubsurfaceOcclusion(float opacity, float sceneDepth, float shadowMapDepth)
|
||||
{
|
||||
float thickness = max(sceneDepth - shadowMapDepth, 0);
|
||||
//float density = -0.05f * log(1 - min(opacity, 0.999f));
|
||||
//float occlusion = saturate(exp(-thickness * density));
|
||||
float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity);
|
||||
return shadowMapDepth > 0.99f ? 1 : occlusion;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,22 @@
|
||||
#include "./Flax/PCFKernels.hlsl"
|
||||
#endif
|
||||
|
||||
// Gets the cube texture face index to use for shadow map sampling for the given view-to-light direction vector
|
||||
// Where: direction = normalize(worldPosition - lightPosition)
|
||||
int GetCubeFaceIndex(float3 direction)
|
||||
{
|
||||
int cubeFaceIndex;
|
||||
float3 absDirection = abs(direction);
|
||||
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
|
||||
if (maxDirection == absDirection.x)
|
||||
cubeFaceIndex = absDirection.x == direction.x ? 0 : 1;
|
||||
else if (maxDirection == absDirection.y)
|
||||
cubeFaceIndex = absDirection.y == direction.y ? 2 : 3;
|
||||
else
|
||||
cubeFaceIndex = absDirection.z == direction.z ? 4 : 5;
|
||||
return cubeFaceIndex;
|
||||
}
|
||||
|
||||
// Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmpRed.
|
||||
// Uses code from "Fast Conventional Shadow Filtering" by Holger Gruen, in GPU Pro.
|
||||
float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPos, uint cascadeIndex)
|
||||
|
||||
@@ -103,30 +103,12 @@ float3 ComputeVolumeUV(float3 worldPosition, float4x4 worldToClip)
|
||||
return float3(ndcPosition.xy * float2(0.5f, -0.5f) + 0.5f, ComputeNormalizedZSliceFromDepth(ndcPosition.w));
|
||||
}
|
||||
|
||||
float IsotropicPhase()
|
||||
{
|
||||
return 1 / (4 * PI);
|
||||
}
|
||||
|
||||
float HenyeyGreensteinPhase(float g, float cosTheta)
|
||||
{
|
||||
return (1 - g * g) / (4 * PI * pow(1 + g * g + 2 * g * cosTheta, 1.5f));
|
||||
}
|
||||
|
||||
float SchlickPhase(float k, float cosTheta)
|
||||
{
|
||||
float t = (1 + k * cosTheta);
|
||||
return (1 - k * k) / (4 * PI * t * t);
|
||||
}
|
||||
|
||||
float RaleighPhase(float cosTheta)
|
||||
{
|
||||
return 3.0f * (1.0f + cosTheta * cosTheta) / (16.0f * PI);
|
||||
}
|
||||
|
||||
// Positive g = forward scattering
|
||||
// Zero g = isotropic
|
||||
// Negative g = backward scattering
|
||||
// +g = forward scattering, 0=g = isotropic, -g = backward scattering
|
||||
float PhaseFunction(float g, float cosTheta)
|
||||
{
|
||||
return HenyeyGreensteinPhase(g, cosTheta);
|
||||
|
||||
@@ -476,6 +476,25 @@ namespace Flax.Build
|
||||
"System.Core",
|
||||
};
|
||||
SetupProjectConfigurations(project, rootProject);
|
||||
if (project.Configurations.Count == 0)
|
||||
{
|
||||
// Hardcoded dummy configuration even if platform tools are missing for this platform
|
||||
var platform = Platform.BuildPlatform;
|
||||
var architecture = TargetArchitecture.x64;
|
||||
var configuration = TargetConfiguration.Debug;
|
||||
project.Configurations.Add(new Project.ConfigurationData
|
||||
{
|
||||
Platform = platform.Target,
|
||||
PlatformName = platform.Target.ToString(),
|
||||
Architecture = architecture,
|
||||
ArchitectureName = architecture.ToString(),
|
||||
Configuration = configuration,
|
||||
ConfigurationName = configuration.ToString(),
|
||||
Target = target,
|
||||
TargetBuildOptions = GetBuildOptions(target, platform, null, architecture, configuration, project.WorkspaceRootPath),
|
||||
Modules = new Dictionary<Module, BuildOptions>(),
|
||||
});
|
||||
}
|
||||
var c = project.Configurations[0];
|
||||
c.Name = "Debug|AnyCPU";
|
||||
c.Text = "Debug";
|
||||
|
||||
@@ -946,7 +946,7 @@ namespace Flax.Build
|
||||
{
|
||||
using (new ProfileEventScope(reference.Project.Name))
|
||||
{
|
||||
if (Configuration.BuildBindingsOnly || reference.Project.IsCSharpOnlyProject)
|
||||
if (Configuration.BuildBindingsOnly || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled)
|
||||
{
|
||||
BuildTargetReferenceNativeCppBindingsOnly(buildContext, buildData, reference);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Flax.Build.Platforms
|
||||
if (string.IsNullOrEmpty(sdkPath))
|
||||
{
|
||||
// Look for ndk installed side-by-side with an sdk
|
||||
if (AndroidSdk.Instance.IsValid)
|
||||
if (AndroidSdk.Instance.IsValid && Directory.Exists(Path.Combine(AndroidSdk.Instance.RootPath, "ndk")))
|
||||
{
|
||||
var subdirs = Directory.GetDirectories(Path.Combine(AndroidSdk.Instance.RootPath, "ndk"));
|
||||
if (subdirs.Length != 0)
|
||||
|
||||
Reference in New Issue
Block a user