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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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
|
||||
)
|
||||
)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+144
-223
@@ -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);
|
||||
}
|
||||
|
||||
+15
-165
@@ -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)));
|
||||
|
||||
+42
-54
@@ -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);
|
||||
|
||||
+9
-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