diff --git a/Content/Blueprints/OceanSimulator.uasset b/Content/Blueprints/OceanSimulator.uasset new file mode 100644 index 0000000..4acde9f Binary files /dev/null and b/Content/Blueprints/OceanSimulator.uasset differ diff --git a/Content/Blueprints/OceanSimulator_Debug.uasset b/Content/Blueprints/OceanSimulator_Debug.uasset new file mode 100644 index 0000000..c2a1bd9 Binary files /dev/null and b/Content/Blueprints/OceanSimulator_Debug.uasset differ diff --git a/Content/Maps/Level_Test.umap b/Content/Maps/Level_Test.umap new file mode 100644 index 0000000..324d203 Binary files /dev/null and b/Content/Maps/Level_Test.umap differ diff --git a/Content/Materials/HSL.uasset b/Content/Materials/HSL.uasset new file mode 100644 index 0000000..b742f2f Binary files /dev/null and b/Content/Materials/HSL.uasset differ diff --git a/Content/Materials/MI_OceanCS_Example.uasset b/Content/Materials/MI_OceanCS_Example.uasset new file mode 100644 index 0000000..0ac2029 Binary files /dev/null and b/Content/Materials/MI_OceanCS_Example.uasset differ diff --git a/Content/Materials/M_OceanCS.uasset b/Content/Materials/M_OceanCS.uasset index 42ff352..576e7e0 100644 Binary files a/Content/Materials/M_OceanCS.uasset and b/Content/Materials/M_OceanCS.uasset differ diff --git a/Content/Materials/Philips_Spec.uasset b/Content/Materials/Philips_Spec.uasset new file mode 100644 index 0000000..dc23022 Binary files /dev/null and b/Content/Materials/Philips_Spec.uasset differ diff --git a/Content/Materials/ShaderTesting.uasset b/Content/Materials/ShaderTesting.uasset new file mode 100644 index 0000000..b9ddcc2 Binary files /dev/null and b/Content/Materials/ShaderTesting.uasset differ diff --git a/Content/Materials/SimpleWave.uasset b/Content/Materials/SimpleWave.uasset new file mode 100644 index 0000000..0baeadc Binary files /dev/null and b/Content/Materials/SimpleWave.uasset differ diff --git a/Content/Materials/Testing_Inst.uasset b/Content/Materials/Testing_Inst.uasset new file mode 100644 index 0000000..61af1ba Binary files /dev/null and b/Content/Materials/Testing_Inst.uasset differ diff --git a/Content/Materials/temp.uasset b/Content/Materials/temp.uasset new file mode 100644 index 0000000..4d3739e Binary files /dev/null and b/Content/Materials/temp.uasset differ diff --git a/Content/Materials/waterTest.uasset b/Content/Materials/waterTest.uasset new file mode 100644 index 0000000..4b9cb73 Binary files /dev/null and b/Content/Materials/waterTest.uasset differ diff --git a/Content/Materials/waveGenTest.uasset b/Content/Materials/waveGenTest.uasset new file mode 100644 index 0000000..3f978bf Binary files /dev/null and b/Content/Materials/waveGenTest.uasset differ diff --git a/Content/Textures/Dynamic_Tex_Test.uasset b/Content/Textures/Dynamic_Tex_Test.uasset new file mode 100644 index 0000000..fb48532 Binary files /dev/null and b/Content/Textures/Dynamic_Tex_Test.uasset differ diff --git a/Content/Textures/Rand_Tex.uasset b/Content/Textures/Rand_Tex.uasset new file mode 100644 index 0000000..5d77b1d Binary files /dev/null and b/Content/Textures/Rand_Tex.uasset differ diff --git a/Content/Textures/SeaDistant_N.uasset b/Content/Textures/SeaDistant_N.uasset new file mode 100644 index 0000000..a2fb016 Binary files /dev/null and b/Content/Textures/SeaDistant_N.uasset differ diff --git a/Content/Textures/SeaWavesB_N.uasset b/Content/Textures/SeaWavesB_N.uasset new file mode 100644 index 0000000..88bf84d Binary files /dev/null and b/Content/Textures/SeaWavesB_N.uasset differ diff --git a/Content/Textures/Vatrobot_tile6_debug_texture.uasset b/Content/Textures/Vatrobot_tile6_debug_texture.uasset new file mode 100644 index 0000000..b28cb14 Binary files /dev/null and b/Content/Textures/Vatrobot_tile6_debug_texture.uasset differ diff --git a/Content/Textures/foo.uasset b/Content/Textures/foo.uasset new file mode 100644 index 0000000..a653976 Binary files /dev/null and b/Content/Textures/foo.uasset differ diff --git a/Content/Textures/uniformRan1.uasset b/Content/Textures/uniformRan1.uasset new file mode 100644 index 0000000..558d0ae Binary files /dev/null and b/Content/Textures/uniformRan1.uasset differ diff --git a/Content/Textures/uniformRan2.uasset b/Content/Textures/uniformRan2.uasset new file mode 100644 index 0000000..03974a4 Binary files /dev/null and b/Content/Textures/uniformRan2.uasset differ diff --git a/Content/Textures/uniformRan3.uasset b/Content/Textures/uniformRan3.uasset new file mode 100644 index 0000000..88160e3 Binary files /dev/null and b/Content/Textures/uniformRan3.uasset differ diff --git a/Content/Textures/uniformRan4.uasset b/Content/Textures/uniformRan4.uasset new file mode 100644 index 0000000..5ee8602 Binary files /dev/null and b/Content/Textures/uniformRan4.uasset differ diff --git a/README.md b/README.md index d044663..baf71bf 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,17 @@ Overview VaOcean is the ocean surface simulation plugin for [Unreal Engine 4](https://www.unrealengine.com/). Key features: - * **@TODO** Plugin in development mode!!! - * Old version based on [NVIDIA SDK sample](https://developer.nvidia.com/dx11-samples) "FFT Ocean" lives in [archive_UE4.12](https://github.com/ufna/VaOcean/tree/archive_UE4.12) branch +* Component that renders displacement and gradient (normal) maps in real time +* Sample content, including water shader and grid meshes to be used on scene +* Set of global shaders that perform FFT calculation and other tech stuff on GPU Check the **[Wiki](https://github.com/ufna/VaOcean/wiki)** tab to know more about the plugin. -Current version: **0.5 A 1** (UE 4.19) +Current version: **0.6 A 1** (UE 4.19) + +Right now the plugin is a state of development. More features should be added soon! + +To install this plugin simpy drop it into the plugin folder in your project root directory. The enable it from the plug in menu. An example map is included. ![SCREENSHOT](SCREENSHOT.jpg) diff --git a/Resources/Icon128.png b/Resources/Icon128.png index 0a75ffd..1231d4a 100644 Binary files a/Resources/Icon128.png and b/Resources/Icon128.png differ diff --git a/SCREENSHOT.jpg b/SCREENSHOT.jpg index 0ba9d37..425ec4a 100644 Binary files a/SCREENSHOT.jpg and b/SCREENSHOT.jpg differ diff --git a/Shaders/Private/VaOcean_CS.usf b/Shaders/Private/VaOcean_CS.usf new file mode 100644 index 0000000..6205ff7 --- /dev/null +++ b/Shaders/Private/VaOcean_CS.usf @@ -0,0 +1,65 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +#include "/Engine/Private/Common.ush" + + +#define PI 3.1415926536f +#define BLOCK_SIZE_X 16 +#define BLOCK_SIZE_Y 16 + +// Immutable +uint g_ActualDim; +uint g_InWidth; +uint g_OutWidth; +uint g_OutHeight; +uint g_DtxAddressOffset; +uint g_DtyAddressOffset; + +// Buffers +StructuredBuffer g_InputH0; +StructuredBuffer g_InputOmega; +RWStructuredBuffer g_OutputHt; + + +////////////////////////////////////////////////////////////////////////// +// Pre-FFT data preparation: H(0) -> H(t) + +[numthreads(BLOCK_SIZE_X, BLOCK_SIZE_Y, 1)] +void UpdateSpectrumCS(uint3 DTid : SV_DispatchThreadID) +{ + int in_index = DTid.y * g_InWidth + DTid.x; + int in_mindex = (g_ActualDim - DTid.y) * g_InWidth + (g_ActualDim - DTid.x); + int out_index = DTid.y * g_OutWidth + DTid.x; + + // H(0) -> H(t) + float2 h0_k = g_InputH0[in_index]; + float2 h0_mk = g_InputH0[in_mindex]; + float sin_v, cos_v; + sincos(g_InputOmega[in_index] * PerFrameSp.Time, sin_v, cos_v); + + float2 ht; + ht.x = (h0_k.x + h0_mk.x) * cos_v - (h0_k.y + h0_mk.y) * sin_v; + ht.y = (h0_k.x - h0_mk.x) * sin_v + (h0_k.y - h0_mk.y) * cos_v; + + // H(t) -> Dx(t), Dy(t) + float kx = DTid.x - g_ActualDim * 0.5f; + float ky = DTid.y - g_ActualDim * 0.5f; + float sqr_k = kx * kx + ky * ky; + float rsqr_k = 0; + if (sqr_k > 1e-12f) + { + rsqr_k = 1 / sqrt(sqr_k); + } + + kx *= rsqr_k; + ky *= rsqr_k; + float2 dt_x = float2(ht.y * kx, -ht.x * kx); + float2 dt_y = float2(ht.y * ky, -ht.x * ky); + + if ((DTid.x < g_OutWidth) && (DTid.y < g_OutHeight)) + { + g_OutputHt[out_index] = ht; + g_OutputHt[out_index + g_DtxAddressOffset] = dt_x; + g_OutputHt[out_index + g_DtyAddressOffset] = dt_y; + } +} diff --git a/Shaders/Private/VaOcean_FFT.usf b/Shaders/Private/VaOcean_FFT.usf new file mode 100644 index 0000000..d660823 --- /dev/null +++ b/Shaders/Private/VaOcean_FFT.usf @@ -0,0 +1,167 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +#include "/Engine/Private/Common.ush" + +#define COS_PI_4_16 0.70710678118654752440084436210485f +#define TWIDDLE_1_8 COS_PI_4_16, -COS_PI_4_16 +#define TWIDDLE_3_8 -COS_PI_4_16, -COS_PI_4_16 + +#define COHERENCY_GRANULARITY 128 + +// Source and destination buffers +StructuredBuffer g_SrcData; +RWStructuredBuffer g_DstData; + + +////////////////////////////////////////////////////////////////////////// +// FFT butterfly helper functions + +void FT2(inout float2 a, inout float2 b) +{ + float t; + + t = a.x; + a.x += b.x; + b.x = t - b.x; + + t = a.y; + a.y += b.y; + b.y = t - b.y; +} + +void CMUL_forward(inout float2 a, float bx, float by) +{ + float t = a.x; + a.x = t * bx - a.y * by; + a.y = t * by + a.y * bx; +} + +void UPD_forward(inout float2 a, inout float2 b) +{ + float A = a.x; + float B = b.y; + + a.x += b.y; + b.y = a.y + b.x; + a.y -= b.x; + b.x = A - B; +} + +void FFT_forward_4(inout float2 D[8]) +{ + FT2(D[0], D[2]); + FT2(D[1], D[3]); + FT2(D[0], D[1]); + + UPD_forward(D[2], D[3]); +} + +void FFT_forward_8(inout float2 D[8]) +{ + FT2(D[0], D[4]); + FT2(D[1], D[5]); + FT2(D[2], D[6]); + FT2(D[3], D[7]); + + UPD_forward(D[4], D[6]); + UPD_forward(D[5], D[7]); + + CMUL_forward(D[5], TWIDDLE_1_8); + CMUL_forward(D[7], TWIDDLE_3_8); + + FFT_forward_4(D); + FT2(D[4], D[5]); + FT2(D[6], D[7]); +} + +void TWIDDLE(inout float2 d, float phase) +{ + float tx, ty; + + sincos(phase, ty, tx); + float t = d.x; + d.x = t * tx - d.y * ty; + d.y = t * ty + d.y * tx; +} + +void TWIDDLE_8(inout float2 D[8], float phase) +{ + TWIDDLE(D[4], 1 * phase); + TWIDDLE(D[2], 2 * phase); + TWIDDLE(D[6], 3 * phase); + TWIDDLE(D[1], 4 * phase); + TWIDDLE(D[5], 5 * phase); + TWIDDLE(D[3], 6 * phase); + TWIDDLE(D[7], 7 * phase); +} + + +////////////////////////////////////////////////////////////////////////// +// Radix FFT compute shaders + +[numthreads(COHERENCY_GRANULARITY, 1, 1)] +void Radix008A_CS(uint3 thread_id : SV_DispatchThreadID) +{ + if (thread_id.x >= PerFrameFFT.ThreadCount) + return; + + // Fetch 8 complex numbers + float2 D[8]; + + uint i; + uint imod = thread_id & (PerFrameFFT.istride - 1); + uint iaddr = ((thread_id - imod) << 3) + imod; + for (i = 0; i < 8; i++) + { + D[i] = g_SrcData[iaddr + i * PerFrameFFT.istride]; + } + + // Math + FFT_forward_8(D); + uint p = thread_id & (PerFrameFFT.istride - PerFrameFFT.pstride); + float phase = PerFrameFFT.PhaseBase * (float)p; + TWIDDLE_8(D, phase); + + // Store the result + uint omod = thread_id & (PerFrameFFT.ostride - 1); + uint oaddr = ((thread_id - omod) << 3) + omod; + g_DstData[oaddr + 0 * PerFrameFFT.ostride] = D[0]; + g_DstData[oaddr + 1 * PerFrameFFT.ostride] = D[4]; + g_DstData[oaddr + 2 * PerFrameFFT.ostride] = D[2]; + g_DstData[oaddr + 3 * PerFrameFFT.ostride] = D[6]; + g_DstData[oaddr + 4 * PerFrameFFT.ostride] = D[1]; + g_DstData[oaddr + 5 * PerFrameFFT.ostride] = D[5]; + g_DstData[oaddr + 6 * PerFrameFFT.ostride] = D[3]; + g_DstData[oaddr + 7 * PerFrameFFT.ostride] = D[7]; +} + +[numthreads(COHERENCY_GRANULARITY, 1, 1)] +void Radix008A_CS2(uint3 thread_id : SV_DispatchThreadID) +{ + if(thread_id.x >= PerFrameFFT.ThreadCount) + return; + + // Fetch 8 complex numbers + uint i; + float2 D[8]; + uint iaddr = thread_id << 3; + for (i = 0; i < 8; i++) + { + D[i] = g_SrcData[iaddr + i]; + } + + // Math + FFT_forward_8(D); + + // Store the result + uint omod = thread_id & (PerFrameFFT.ostride - 1); + uint oaddr = ((thread_id - omod) << 3) + omod; + g_DstData[oaddr + 0 * PerFrameFFT.ostride] = D[0]; + g_DstData[oaddr + 1 * PerFrameFFT.ostride] = D[4]; + g_DstData[oaddr + 2 * PerFrameFFT.ostride] = D[2]; + g_DstData[oaddr + 3 * PerFrameFFT.ostride] = D[6]; + g_DstData[oaddr + 4 * PerFrameFFT.ostride] = D[1]; + g_DstData[oaddr + 5 * PerFrameFFT.ostride] = D[5]; + g_DstData[oaddr + 6 * PerFrameFFT.ostride] = D[3]; + g_DstData[oaddr + 7 * PerFrameFFT.ostride] = D[7]; +} diff --git a/Shaders/Private/VaOcean_VS_PS.usf b/Shaders/Private/VaOcean_VS_PS.usf new file mode 100644 index 0000000..ad6b53a --- /dev/null +++ b/Shaders/Private/VaOcean_VS_PS.usf @@ -0,0 +1,87 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license . + +#include "/Engine/Private/Common.ush" + + +////////////////////////////////////////////////////////////////////////// +// Vertex shaders + +void QuadVS( + in float4 InPosition : ATTRIBUTE0, + in float2 InTexCoord : ATTRIBUTE1, + out float2 OutTexCoord : TEXCOORD0, + out float4 OutPosition : SV_POSITION) +{ + OutPosition = InPosition; + OutTexCoord.x = 0.5f + InPosition.x * 0.5f; + OutTexCoord.y = 0.5f - InPosition.y * 0.5f; +} + + +////////////////////////////////////////////////////////////////////////// +// Pixel shaders + +// Immutable +uint g_ActualDim; +uint g_InWidth; +uint g_OutWidth; +uint g_OutHeight; +uint g_DtxAddressOffset; +uint g_DtyAddressOffset; + +// The following three should contains only real numbers. But we have only C2C FFT now. +StructuredBuffer g_InputDxyz; + +// Post-FFT data wrap up: Dx, Dy, Dz -> Displacement +void UpdateDisplacementPS(float2 UV : TEXCOORD0, out float4 OutColor : SV_Target0) +{ + uint index_x = (uint)(UV.x * (float)g_OutWidth); + uint index_y = (uint)(UV.y * (float)g_OutHeight); + uint addr = g_OutWidth * index_y + index_x; + + // cos(pi * (m1 + m2)) + int sign_correction = ((index_x + index_y) & 1) ? -1 : 1; + + float dx = g_InputDxyz[addr + g_DtxAddressOffset].x * sign_correction * PerFrameDisp.ChoppyScale; + float dy = g_InputDxyz[addr + g_DtyAddressOffset].x * sign_correction * PerFrameDisp.ChoppyScale; + float dz = g_InputDxyz[addr].x * sign_correction; + + OutColor = float4(dx, dy, dz, 1); +} + + +// Textures and sampling states +Texture2D DisplacementMap; +SamplerState DisplacementMapSampler; + +// Displacement -> Normal, Folding +void GenGradientFoldingPS(float2 UV : TEXCOORD0, out float4 OutColor : SV_Target0) +{ + // Sample neighbour texels + float2 one_texel = float2(1.0f / (float)g_OutWidth, 1.0f / (float)g_OutHeight); + + float2 tc_left = float2(UV.x - one_texel.x, UV.y); + float2 tc_right = float2(UV.x + one_texel.x, UV.y); + float2 tc_back = float2(UV.x, UV.y - one_texel.y); + float2 tc_front = float2(UV.x, UV.y + one_texel.y); + + float3 displace_left = DisplacementMap.Sample(DisplacementMapSampler, tc_left).xyz; + float3 displace_right = DisplacementMap.Sample(DisplacementMapSampler, tc_right).xyz; + float3 displace_back = DisplacementMap.Sample(DisplacementMapSampler, tc_back).xyz; + float3 displace_front = DisplacementMap.Sample(DisplacementMapSampler, tc_front).xyz; + + // Do not store the actual normal value. Using gradient instead, which preserves two differential values. + float2 gradient = {-(displace_right.z - displace_left.z), -(displace_front.z - displace_back.z)}; + + + // Calculate Jacobian corelation from the partial differential of height field + float2 Dx = (displace_right.xy - displace_left.xy) * PerFrameDisp.ChoppyScale * PerFrameDisp.GridLen; + float2 Dy = (displace_front.xy - displace_back.xy) * PerFrameDisp.ChoppyScale * PerFrameDisp.GridLen; + float J = (1.0f + Dx.x) * (1.0f + Dy.y) - Dx.y * Dy.x; + + // Practical subsurface scale calculation: max[0, (1 - J) + Amplitude * (2 * Coverage - 1)]. + float fold = max(1.0f - J, 0); + + // Output + OutColor = float4(gradient, 0, fold); +} diff --git a/Source/VaOcean/Private/VaOcean.cpp b/Source/VaOcean/Private/VaOcean.cpp deleted file mode 100644 index 81dd278..0000000 --- a/Source/VaOcean/Private/VaOcean.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014-2018 Vladimir Alyamkin. All Rights Reserved. - -#include "VaOcean.h" -#include "VaOceanPrivate.h" - -#define LOCTEXT_NAMESPACE "FVaOceanModule" - -void FVaOceanModule::StartupModule() -{ - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module -} - -void FVaOceanModule::ShutdownModule() -{ - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FVaOceanModule, VaOcean) - -DEFINE_LOG_CATEGORY(LogVaOcean); diff --git a/Source/VaOcean/Private/VaOceanPrivate.h b/Source/VaOcean/Private/VaOceanPrivate.h deleted file mode 100644 index d087cf7..0000000 --- a/Source/VaOcean/Private/VaOceanPrivate.h +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2014-2018 Vladimir Alyamkin. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" - -DECLARE_LOG_CATEGORY_EXTERN(LogVaOcean, Log, All); diff --git a/Source/VaOcean/Public/VaOcean.h b/Source/VaOcean/Public/VaOcean.h deleted file mode 100644 index 940513b..0000000 --- a/Source/VaOcean/Public/VaOcean.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2014-2018 Vladimir Alyamkin. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "ModuleManager.h" - -class FVaOceanModule : public IModuleInterface -{ -public: - - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; -}; \ No newline at end of file diff --git a/Source/VaOcean/VaOcean.Build.cs b/Source/VaOcean/VaOcean.Build.cs deleted file mode 100644 index 366289d..0000000 --- a/Source/VaOcean/VaOcean.Build.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2014-2018 Vladimir Alyamkin. All Rights Reserved. - -using UnrealBuildTool; - -public class VaOcean : ModuleRules -{ - public VaOcean(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicIncludePaths.AddRange( - new string[] { - "VaOcean/Public" - // ... add public include paths required here ... - } - ); - - - PrivateIncludePaths.AddRange( - new string[] { - "VaOcean/Private", - // ... add other private include paths required here ... - } - ); - - - PublicDependencyModuleNames.AddRange( - new string[] - { - "Core", - // ... add other public dependencies that you statically link with here ... - } - ); - - - PrivateDependencyModuleNames.AddRange( - new string[] - { - "CoreUObject", - "Engine", - "RenderCore", - "ShaderCore", - "RHI" - // ... add private dependencies that you statically link with here ... - } - ); - - - DynamicallyLoadedModuleNames.AddRange( - new string[] - { - // ... add any modules that your module loads dynamically here ... - } - ); - } -} diff --git a/Source/VaOceanPlugin/Classes/VaOceanRadixFFT.h b/Source/VaOceanPlugin/Classes/VaOceanRadixFFT.h new file mode 100644 index 0000000..c927836 --- /dev/null +++ b/Source/VaOceanPlugin/Classes/VaOceanRadixFFT.h @@ -0,0 +1,47 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +//#define LOCTEXT_NAMESPACE "team" +#pragma once + +//#include "Runtime/Engine/Classes/Engine/DataTable.h" +#include "VaOceanPluginPrivatePCH.h" +#include "VaOceanTypes.h" +#include "VaStructs.h" +//#include "VaOceanRadixFFT.h" +//#include "VaOceanSimulator.h" +#include "VaOceanRadixFFT.generated.h" + +/** Memory access coherency (in threads) */ +#define COHERENCY_GRANULARITY 128 + +/** Common constants */ +#define TWO_PI 6.283185307179586476925286766559 + +#define FFT_DIMENSIONS 3U +#define FFT_PLAN_SIZE_LIMIT (1U << 27) + +#define FFT_FORWARD -1 +#define FFT_INVERSE 1 + + + +USTRUCT() +struct FdummyStruct +{ + GENERATED_BODY() + + UPROPERTY() + uint32 dummyParm; + +}; + +void RadixCreatePlan(FRadixPlan512* Plan, uint32 Slices); +void RadixDestroyPlan(FRadixPlan512* Plan); + +void RadixCompute(FRHICommandListImmediate& RHICmdList, + FRadixPlan512* Plan, + FUnorderedAccessViewRHIRef pUAV_Dst, + FShaderResourceViewRHIRef pSRV_Dst, + FShaderResourceViewRHIRef pSRV_Src); + +//#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/VaOceanPlugin/Classes/VaOceanShaders.h b/Source/VaOceanPlugin/Classes/VaOceanShaders.h new file mode 100644 index 0000000..9504a11 --- /dev/null +++ b/Source/VaOceanPlugin/Classes/VaOceanShaders.h @@ -0,0 +1,574 @@ +// Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. + +#pragma once + +#include "VaOceanPluginPrivatePCH.h" +#include "VaOceanShaders.generated.h" + +#define HALF_SQRT_2 0.7071068f +#define GRAV_ACCEL 981.0f // The acceleration of gravity, cm/s^2 + +#define BLOCK_SIZE_X 16 +#define BLOCK_SIZE_Y 16 + + +////////////////////////////////////////////////////////////////////////// +// UpdateSpectrumCS compute shader + +BEGIN_UNIFORM_BUFFER_STRUCT(FUpdateSpectrumUniformParameters, ) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(float, Time) +END_UNIFORM_BUFFER_STRUCT(FUpdateSpectrumUniformParameters) + +typedef TUniformBufferRef FUpdateSpectrumUniformBufferRef; + +/** Immutable parameters for UpdateSpectrumCS shader*/ +USTRUCT() +struct FUpdateSpectrumCSImmutable +{ + GENERATED_USTRUCT_BODY() + + uint32 g_ActualDim; + uint32 g_InWidth; + uint32 g_OutWidth; + uint32 g_OutHeight; + uint32 g_DtxAddressOffset; + uint32 g_DtyAddressOffset; +}; + +/** Per frame parameters for UpdateSpectrumCS shader */ +USTRUCT() +struct FUpdateSpectrumCSPerFrame +{ + GENERATED_USTRUCT_BODY() + + FShaderResourceViewRHIRef m_pSRV_H0; + FShaderResourceViewRHIRef m_pSRV_Omega; + FUnorderedAccessViewRHIRef m_pUAV_Ht; + + // Used to pass params into render thread + float g_Time; + float g_ChoppyScale; +}; + +/** +* H(0) -> H(t), D(x,t), D(y,t) +*/ +class FUpdateSpectrumCS : public FGlobalShader +{ + DECLARE_SHADER_TYPE(FUpdateSpectrumCS, Global) + +public: + static bool ShouldCache(EShaderPlatform Platform) + { + return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); + } + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& PermutationParams) + { + // Useful when adding a permutation of a particular shader + return true; + } + + FUpdateSpectrumCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ActualDim.Bind(Initializer.ParameterMap, TEXT("g_ActualDim"), SPF_Mandatory); + InWidth.Bind(Initializer.ParameterMap, TEXT("g_InWidth"), SPF_Mandatory); + OutWidth.Bind(Initializer.ParameterMap, TEXT("g_OutWidth"), SPF_Mandatory); + OutHeight.Bind(Initializer.ParameterMap, TEXT("g_OutHeight"), SPF_Mandatory); + DtxAddressOffset.Bind(Initializer.ParameterMap, TEXT("g_DtxAddressOffset"), SPF_Mandatory); + DtyAddressOffset.Bind(Initializer.ParameterMap, TEXT("g_DtyAddressOffset"), SPF_Mandatory); + + InputH0.Bind(Initializer.ParameterMap, TEXT("g_InputH0"), SPF_Mandatory); + InputOmega.Bind(Initializer.ParameterMap, TEXT("g_InputOmega"), SPF_Mandatory); + + OutputHtRW.Bind(Initializer.ParameterMap, TEXT("g_OutputHt"), SPF_Mandatory); + } + + FUpdateSpectrumCS() + { + } + + void SetParameters( + FRHICommandList& RHICmdList, + uint32 ParamActualDim, + uint32 ParamInWidth, + uint32 ParamOutWidth, + uint32 ParamOutHeight, + uint32 ParamDtxAddressOffset, + uint32 ParamDtyAddressOffset + ) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + + SetShaderValue(RHICmdList, ComputeShaderRHI, ActualDim, ParamActualDim); + SetShaderValue(RHICmdList, ComputeShaderRHI, InWidth, ParamInWidth); + SetShaderValue(RHICmdList, ComputeShaderRHI, OutWidth, ParamOutWidth); + SetShaderValue(RHICmdList, ComputeShaderRHI, OutHeight, ParamOutHeight); + SetShaderValue(RHICmdList, ComputeShaderRHI, DtxAddressOffset, ParamDtxAddressOffset); + SetShaderValue(RHICmdList, ComputeShaderRHI, DtyAddressOffset, ParamDtyAddressOffset); + } + + void SetParameters( + FRHICommandList& RHICmdList, + const FUpdateSpectrumUniformBufferRef& UniformBuffer, + FShaderResourceViewRHIRef ParamInputH0, + FShaderResourceViewRHIRef ParamInputOmega + ) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + + SetUniformBufferParameter(RHICmdList, ComputeShaderRHI, GetUniformBufferParameter(), UniformBuffer); + + RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, InputH0.GetBaseIndex(), ParamInputH0); + RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, InputOmega.GetBaseIndex(), ParamInputOmega); + } + + void UnsetParameters(FRHICommandList &RHICmdList) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + FShaderResourceViewRHIParamRef NullSRV = FShaderResourceViewRHIParamRef(); + + RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, InputH0.GetBaseIndex(), NullSRV); + RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, InputOmega.GetBaseIndex(), NullSRV); + } + + void SetOutput(FRHICommandList& RHICmdList, FUnorderedAccessViewRHIParamRef ParamOutputHtRW) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + if (OutputHtRW.IsBound()) + { + RHICmdList.SetUAVParameter(ComputeShaderRHI, OutputHtRW.GetBaseIndex(), ParamOutputHtRW); + } + } + + void UnbindBuffers(FRHICommandList& RHICmdList) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + if (OutputHtRW.IsBound()) + { + RHICmdList.SetUAVParameter(ComputeShaderRHI, OutputHtRW.GetBaseIndex(), FUnorderedAccessViewRHIParamRef()); + } + } + + virtual bool Serialize(FArchive& Ar) + { + bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); + Ar << ActualDim << InWidth << OutWidth << OutHeight << DtxAddressOffset << DtyAddressOffset + << InputH0 << InputOmega << OutputHtRW; + + return bShaderHasOutdatedParameters; + } + +private: + // Immutable + FShaderParameter ActualDim; + FShaderParameter InWidth; + FShaderParameter OutWidth; + FShaderParameter OutHeight; + FShaderParameter DtxAddressOffset; + FShaderParameter DtyAddressOffset; + + // Buffers + FShaderResourceParameter InputH0; + FShaderResourceParameter InputOmega; + FShaderResourceParameter OutputHtRW; + +}; + + +////////////////////////////////////////////////////////////////////////// +// Radix008A_CS compute shader + +BEGIN_UNIFORM_BUFFER_STRUCT(FRadixFFTUniformParameters, ) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(uint32, ThreadCount) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(uint32, ostride) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(uint32, istride) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(uint32, pstride) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(float, PhaseBase) +END_UNIFORM_BUFFER_STRUCT(FRadixFFTUniformParameters) + +typedef TUniformBufferRef FRadixFFTUniformBufferRef; + +/** +* FFT calculations for istride > 1 +*/ +class FRadix008A_CS : public FGlobalShader +{ + DECLARE_SHADER_TYPE(FRadix008A_CS, Global) + +public: + static bool ShouldCache(EShaderPlatform Platform) + { + return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); + } + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& PermutationParams) + { + // Useful when adding a permutation of a particular shader + return true; + } + + FRadix008A_CS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + SrcData.Bind(Initializer.ParameterMap, TEXT("g_SrcData")); + DstData.Bind(Initializer.ParameterMap, TEXT("g_DstData")); + } + + FRadix008A_CS() + { + } + + void SetParameters(FRHICommandList& RHICmdList, const FRadixFFTUniformBufferRef& UniformBuffer) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + + SetUniformBufferParameter(RHICmdList, ComputeShaderRHI, GetUniformBufferParameter(), UniformBuffer); + } + + void SetParameters(FRHICommandList& RHICmdList, FShaderResourceViewRHIRef ParamSrcData, FUnorderedAccessViewRHIRef ParamDstData) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + + RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, SrcData.GetBaseIndex(), ParamSrcData); + RHICmdList.SetUAVParameter(ComputeShaderRHI, DstData.GetBaseIndex(), ParamDstData); + } + + void UnsetParameters(FRHICommandList& RHICmdList) + { + FComputeShaderRHIParamRef ComputeShaderRHI = GetComputeShader(); + + RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, SrcData.GetBaseIndex(), FShaderResourceViewRHIParamRef()); + RHICmdList.SetUAVParameter(ComputeShaderRHI, DstData.GetBaseIndex(), FUnorderedAccessViewRHIParamRef()); + } + + virtual bool Serialize(FArchive& Ar) + { + bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); + Ar << SrcData << DstData; + + return bShaderHasOutdatedParameters; + } + +private: + // Buffers + FShaderResourceParameter SrcData; + FShaderResourceParameter DstData; + +}; + +/** +* pstride and istride parameters are excess here, but we use inheritance as its easier for now +*/ +class FRadix008A_CS2 : public FRadix008A_CS +{ + DECLARE_SHADER_TYPE(FRadix008A_CS2, Global) + +public: + FRadix008A_CS2(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FRadix008A_CS(Initializer) + { + } + + FRadix008A_CS2() + { + } +}; + + +////////////////////////////////////////////////////////////////////////// +// Simple Quad vertex shader + +/** The vertex data used to render displacement */ +struct FQuadVertex +{ + FVector4 Position; + FVector2D UV; +}; + +/** The displacement vertex declaration resource type */ +class FQuadVertexDeclaration : public FRenderResource +{ +public: + FVertexDeclarationRHIRef VertexDeclarationRHI; + + /** Destructor */ + virtual ~FQuadVertexDeclaration() {} + + virtual void InitRHI() + { + FVertexDeclarationElementList Elements; + Elements.Add(FVertexElement(0, STRUCT_OFFSET(FQuadVertex, Position), VET_Float4, 0, sizeof(FQuadVertex))); + Elements.Add(FVertexElement(0, STRUCT_OFFSET(FQuadVertex, UV), VET_Float2, 1, sizeof(FQuadVertex))); + VertexDeclarationRHI = RHICreateVertexDeclaration(Elements); + } + + virtual void ReleaseRHI() + { + VertexDeclarationRHI.SafeRelease(); + } +}; + +class FQuadVS : public FGlobalShader +{ + DECLARE_SHADER_TYPE(FQuadVS, Global); + +public: + static bool ShouldCache(EShaderPlatform Platform) + { + return true; + } + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& PermutationParams) + { + // Useful when adding a permutation of a particular shader + return true; + } + + FQuadVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + } + + FQuadVS() {} +}; + + +////////////////////////////////////////////////////////////////////////// +// Post-FFT data wrap up: Dx, Dy, Dz -> Displacement + +BEGIN_UNIFORM_BUFFER_STRUCT(FUpdateDisplacementUniformParameters, ) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(float, ChoppyScale) +DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER(float, GridLen) +END_UNIFORM_BUFFER_STRUCT(FUpdateDisplacementUniformParameters) + +typedef TUniformBufferRef FUpdateDisplacementUniformBufferRef; + +/** Per frame parameters for UpdateDisplacementPS shader */ +USTRUCT() +struct FUpdateDisplacementPSPerFrame +{ + GENERATED_USTRUCT_BODY() + + FVector4 m_pQuadVB[4]; + + FShaderResourceViewRHIRef g_InputDxyz; + + // Used to pass params into render thread + float g_ChoppyScale; + float g_GridLen; +}; + +/** +* Post-FFT data wrap up: Dx, Dy, Dz -> Displacement +*/ +class FUpdateDisplacementPS : public FGlobalShader +{ + DECLARE_SHADER_TYPE(FUpdateDisplacementPS, Global) + +public: + static bool ShouldCache(EShaderPlatform Platform) + { + return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); + } + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& PermutationParams) + { + // Useful when adding a permutation of a particular shader + return true; + } + + FUpdateDisplacementPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ActualDim.Bind(Initializer.ParameterMap, TEXT("g_ActualDim")); + InWidth.Bind(Initializer.ParameterMap, TEXT("g_InWidth")); + OutWidth.Bind(Initializer.ParameterMap, TEXT("g_OutWidth")); + OutHeight.Bind(Initializer.ParameterMap, TEXT("g_OutHeight")); + DtxAddressOffset.Bind(Initializer.ParameterMap, TEXT("g_DtxAddressOffset")); + DtyAddressOffset.Bind(Initializer.ParameterMap, TEXT("g_DtyAddressOffset")); + + InputDxyz.Bind(Initializer.ParameterMap, TEXT("g_InputDxyz")); + } + + FUpdateDisplacementPS() + { + } + + void SetParameters( + FRHICommandList& RHICmdList, + uint32 ParamActualDim, + uint32 ParamInWidth, + uint32 ParamOutWidth, + uint32 ParamOutHeight, + uint32 ParamDtxAddressOffset, + uint32 ParamDtyAddressOffset + ) + { + FPixelShaderRHIParamRef PixelShaderRHI = GetPixelShader(); + + SetShaderValue(RHICmdList, PixelShaderRHI, ActualDim, ParamActualDim); + SetShaderValue(RHICmdList, PixelShaderRHI, InWidth, ParamInWidth); + SetShaderValue(RHICmdList, PixelShaderRHI, OutWidth, ParamOutWidth); + SetShaderValue(RHICmdList, PixelShaderRHI, OutHeight, ParamOutHeight); + SetShaderValue(RHICmdList, PixelShaderRHI, DtxAddressOffset, ParamDtxAddressOffset); + SetShaderValue(RHICmdList, PixelShaderRHI, DtyAddressOffset, ParamDtyAddressOffset); + } + + void SetParameters( + FRHICommandList& RHICmdList, + const FUpdateDisplacementUniformBufferRef& UniformBuffer, + FShaderResourceViewRHIRef ParamInputDxyz + ) + { + FPixelShaderRHIParamRef PixelShaderRHI = GetPixelShader(); + + SetUniformBufferParameter(RHICmdList, PixelShaderRHI, GetUniformBufferParameter(), UniformBuffer); + + RHICmdList.SetShaderResourceViewParameter(PixelShaderRHI, InputDxyz.GetBaseIndex(), ParamInputDxyz); + } + + void UnsetParameters(FRHICommandList& RHICmdList) + { + FPixelShaderRHIParamRef PixelShaderRHI = GetPixelShader(); + + RHICmdList.SetShaderResourceViewParameter(PixelShaderRHI, InputDxyz.GetBaseIndex(), FShaderResourceViewRHIParamRef()); + } + + virtual bool Serialize(FArchive& Ar) + { + bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); + Ar << ActualDim << InWidth << OutWidth << OutHeight << DtxAddressOffset << DtyAddressOffset << InputDxyz; + + return bShaderHasOutdatedParameters; + } + +private: + // Immutable + FShaderParameter ActualDim; + FShaderParameter InWidth; + FShaderParameter OutWidth; + FShaderParameter OutHeight; + FShaderParameter DtxAddressOffset; + FShaderParameter DtyAddressOffset; + + // Buffers + FShaderResourceParameter InputDxyz; + +}; + + +////////////////////////////////////////////////////////////////////////// +// Generate Normal + +/** Per frame parameters for UpdateDisplacementPS shader */ +USTRUCT() +struct FGenGradientFoldingPSPerFrame +{ + GENERATED_USTRUCT_BODY() + + FVector4 m_pQuadVB[4]; + + // Used to pass params into render thread + float g_ChoppyScale; + float g_GridLen; +}; + +/** +* Displacement -> Normal, Folding +*/ +class FGenGradientFoldingPS : public FGlobalShader +{ + DECLARE_SHADER_TYPE(FGenGradientFoldingPS, Global) + +public: + static bool ShouldCache(EShaderPlatform Platform) + { + return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); + } + + static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& PermutationParams) + { + // Useful when adding a permutation of a particular shader + return true; + } + + FGenGradientFoldingPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) + : FGlobalShader(Initializer) + { + ActualDim.Bind(Initializer.ParameterMap, TEXT("g_ActualDim")); + InWidth.Bind(Initializer.ParameterMap, TEXT("g_InWidth")); + OutWidth.Bind(Initializer.ParameterMap, TEXT("g_OutWidth")); + OutHeight.Bind(Initializer.ParameterMap, TEXT("g_OutHeight")); + DtxAddressOffset.Bind(Initializer.ParameterMap, TEXT("g_DtxAddressOffset")); + DtyAddressOffset.Bind(Initializer.ParameterMap, TEXT("g_DtyAddressOffset")); + + DisplacementMap.Bind(Initializer.ParameterMap, TEXT("DisplacementMap")); + DisplacementMapSampler.Bind(Initializer.ParameterMap, TEXT("DisplacementMapSampler")); + } + + FGenGradientFoldingPS() + { + } + + void SetParameters( + FRHICommandList& RHICmdList, + uint32 ParamActualDim, + uint32 ParamInWidth, + uint32 ParamOutWidth, + uint32 ParamOutHeight, + uint32 ParamDtxAddressOffset, + uint32 ParamDtyAddressOffset + ) + { + FPixelShaderRHIParamRef PixelShaderRHI = GetPixelShader(); + + SetShaderValue(RHICmdList, PixelShaderRHI, ActualDim, ParamActualDim); + SetShaderValue(RHICmdList, PixelShaderRHI, InWidth, ParamInWidth); + SetShaderValue(RHICmdList, PixelShaderRHI, OutWidth, ParamOutWidth); + SetShaderValue(RHICmdList, PixelShaderRHI, OutHeight, ParamOutHeight); + SetShaderValue(RHICmdList, PixelShaderRHI, DtxAddressOffset, ParamDtxAddressOffset); + SetShaderValue(RHICmdList, PixelShaderRHI, DtyAddressOffset, ParamDtyAddressOffset); + } + + void SetParameters( + FRHICommandList& RHICmdList, + const FUpdateDisplacementUniformBufferRef& UniformBuffer, + FTextureRHIParamRef DisplacementMapRHI + ) + { + FPixelShaderRHIParamRef PixelShaderRHI = GetPixelShader(); + + SetUniformBufferParameter(RHICmdList, PixelShaderRHI, GetUniformBufferParameter(), UniformBuffer); + + FSamplerStateRHIParamRef SamplerStateLinear = TStaticSamplerState::GetRHI(); + SetTextureParameter(RHICmdList, PixelShaderRHI, DisplacementMap, DisplacementMapSampler, SamplerStateLinear, DisplacementMapRHI); + } + + void UnsetParameters(FRHICommandList& RHICmdList) {} + + virtual bool Serialize(FArchive& Ar) + { + bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); + Ar << ActualDim << InWidth << OutWidth << OutHeight << DtxAddressOffset << DtyAddressOffset + << DisplacementMap << DisplacementMapSampler; + + return bShaderHasOutdatedParameters; + } + +private: + // Immutable + FShaderParameter ActualDim; + FShaderParameter InWidth; + FShaderParameter OutWidth; + FShaderParameter OutHeight; + FShaderParameter DtxAddressOffset; + FShaderParameter DtyAddressOffset; + + // Displacement map + FShaderResourceParameter DisplacementMap; + FShaderResourceParameter DisplacementMapSampler; + +}; + diff --git a/Source/VaOceanPlugin/Classes/VaOceanSimulator.h b/Source/VaOceanPlugin/Classes/VaOceanSimulator.h new file mode 100644 index 0000000..c069156 --- /dev/null +++ b/Source/VaOceanPlugin/Classes/VaOceanSimulator.h @@ -0,0 +1,166 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +#pragma once + + +#include "VaStructs.h" +#include "VaOceanPluginPrivatePCH.h" +#include "VaOceanSimulator.generated.h" + +#define PAD16(n) (((n)+15)/16*16) + +#define FFT_PARAM_SETS 6 + + +using namespace std; +/** +* Renders normals and heightmap from Phillips spectrum +*/ + +//class VAOCEANPLUGIN_API AVaOceanSimulator : public AActor + + +UCLASS(Blueprintable, BlueprintType, ClassGroup = Environment) +class AVaOceanSimulator : public AActor +{ + GENERATED_UCLASS_BODY() + + + //////////////////////////////////////////////////////////////////////// + // Initialization + +protected: + /** Initialize all buffers and prepare shaders */ + void InitializeInternalData(); + + /** Initialize the vector field */ + void InitHeightMap(const FSpectrumData& Params, TResourceArray& out_h0, TResourceArray& out_omega); + + /** Initialize buffers for shader */ + void CreateBufferAndUAV(FResourceArrayInterface* Data, uint32 byte_width, uint32 byte_stride, FStructuredBufferRHIRef* ppBuffer, FUnorderedAccessViewRHIRef* ppUAV, FShaderResourceViewRHIRef* ppSRV); + + /** Clear internal buffers and shader data */ + void ClearInternalData(); + + /** Clear buffers and re-initalize them */ + void ResetInternalData(); + + //Begin UObject Interface + virtual void BeginDestroy() override; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif // WITH_EDITOR + //End UObject Interface + + /** Allow tick in editor */ + virtual bool ShouldTickIfViewportsOnly() const override; + + + //////////////////////////////////////////////////////////////////////// + //Simulation + +public: + /** Update ocean simulation */ + virtual void Tick(float DeltaSeconds) override; + +protected: + /** Update normals and heightmap from spectrum */ + void UpdateDisplacementMap(float WorldTime); + + + //////////////////////////////////////////////////////////////////////// + // Spectrum configuration + +public: + /** Get spectrum config */ + UFUNCTION(BlueprintCallable, Category = "VaOcean|FFT") + const FSpectrumData& GetSpectrumConfig() const; + +protected: + /** Ocean spectrum data */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Config) + FSpectrumData SpectrumConfig; + + + //////////////////////////////////////////////////////////////////////// + // Shader output targets + +public: + /** Render target for normal map that can be used by the editor */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Config) + UTextureRenderTarget2D* DisplacementTexture; + + /** Render target for height map that can be used by the editor */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Config) + UTextureRenderTarget2D* GradientTexture; + + + //////////////////////////////////////////////////////////////////////// + //Parameters that will be send to rendering thread + +protected: + FUpdateSpectrumCSImmutable UpdateSpectrumCSImmutableParams; + + + //////////////////////////////////////////////////////////////////////// + // Spectrum simulation data + +protected: + /** Initial height field H(0) generated by Phillips spectrum & Gauss distribution. */ + FStructuredBufferRHIRef m_pBuffer_Float2_H0; + FUnorderedAccessViewRHIRef m_pUAV_H0; + FShaderResourceViewRHIRef m_pSRV_H0; + + /** Angular frequency */ + FStructuredBufferRHIRef m_pBuffer_Float_Omega; + FUnorderedAccessViewRHIRef m_pUAV_Omega; + FShaderResourceViewRHIRef m_pSRV_Omega; + + /** Height field H(t), choppy field Dx(t) and Dy(t) in frequency domain, updated each frame. */ + FStructuredBufferRHIRef m_pBuffer_Float2_Ht; + FUnorderedAccessViewRHIRef m_pUAV_Ht; + FShaderResourceViewRHIRef m_pSRV_Ht; + + /** Height & choppy buffer in the space domain, corresponding to H(t), Dx(t) and Dy(t) */ + FStructuredBufferRHIRef m_pBuffer_Float_Dxyz; + FUnorderedAccessViewRHIRef m_pUAV_Dxyz; + FShaderResourceViewRHIRef m_pSRV_Dxyz; + + FVector4 m_pQuadVB[4]; + + /** FFT wrap-up */ + FRadixPlan512 FFTPlan; + + /** Initialization flags */ + bool bSimulatorInitializated; + + /** Internal world simulation time */ + float SimulationWorldTime; + + + //////////////////////////////////////////////////////////////////////// + //Utilities + +public: + /** + * Creates a new RenderTarget with desired params + * + * @param bInForceLinearGamma Whether or not to force linear gamma + * @param InPixelFormat Pixel format of the render target + * @param InTargetSize Dimensions of the render target + * @return Created render target + */ + UTextureRenderTarget2D* CreateRenderTarget(bool bInForceLinearGamma, bool bNormalMap, EPixelFormat InPixelFormat, FIntPoint& InTargetSize); + + /** + * SetupsRenderTarget with desired params + * + * @param bInForceLinearGamma Whether or not to force linear gamma + * @param InPixelFormat Pixel format of the render target + * @param InTargetSize Dimensions of the render target + * @return Created render target + */ + static void SetupRenderTarget(UTextureRenderTarget2D* InRenderTarget, bool bInForceLinearGamma, bool bNormalMap, EPixelFormat InPixelFormat, FIntPoint& InTargetSize); + +}; diff --git a/Source/VaOceanPlugin/Classes/VaOceanTypes.h b/Source/VaOceanPlugin/Classes/VaOceanTypes.h new file mode 100644 index 0000000..2b9c61f --- /dev/null +++ b/Source/VaOceanPlugin/Classes/VaOceanTypes.h @@ -0,0 +1,62 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license +#pragma once + + +#include "VaOceanPluginPrivatePCH.h" +#include "VaOceanTypes.generated.h" + + + + +/** Phillips spectrum configuration */ +USTRUCT(BlueprintType) +struct FSpectrumData +{ + GENERATED_USTRUCT_BODY() + + /** The size of displacement map. Must be power of 2. + * Not editable because of FFT shader config */ + UPROPERTY(VisibleAnywhere, BlueprintReadOnly) + int32 DispMapDimension; + + /** The side length (world space) of square patch. Typical value is 1000 ~ 2000. */ + UPROPERTY(EditAnywhere) + float PatchLength; + + /** Adjust the time interval for simulation (controls the simulation speed) */ + UPROPERTY(EditAnywhere) + float TimeScale; + + /** Amplitude for transverse wave. Around 1.0 (not the world space height). */ + UPROPERTY(EditAnywhere) + float WaveAmplitude; + + /** Wind direction. Normalization not required */ + UPROPERTY(EditAnywhere) + FVector2D WindDirection; + + /** The bigger the wind speed, the larger scale of wave crest. But the wave scale can be no larger than PatchLength. Around 100 ~ 1000 */ + UPROPERTY(EditAnywhere) + float WindSpeed; + + /** This value damps out the waves against the wind direction. Smaller value means higher wind dependency. */ + UPROPERTY(EditAnywhere) + float WindDependency; + + /** The amplitude for longitudinal wave. Higher value creates pointy crests. Must be positive. */ + UPROPERTY(EditAnywhere) + float ChoppyScale; + + /** Defaults */ + FSpectrumData() + { + DispMapDimension = 512; + PatchLength = 2000.0f; + TimeScale = 0.8f; + WaveAmplitude = 0.35f; + WindDirection = FVector2D(0.8f, 0.6f); + WindSpeed = 600.0f; + WindDependency = 0.07f; + ChoppyScale = 1.3f; + } +}; diff --git a/Source/VaOceanPlugin/Classes/VaStructs.h b/Source/VaOceanPlugin/Classes/VaStructs.h new file mode 100644 index 0000000..feda146 --- /dev/null +++ b/Source/VaOceanPlugin/Classes/VaStructs.h @@ -0,0 +1,60 @@ +#pragma once + +#include "VaOceanPluginPrivatePCH.h" +#include "VaStructs.generated.h" + +#define FFT_PARAM_SETS 6 + +/** Per frame parameters for FRadix008A_CS shader */ +USTRUCT() +struct FRadix008A_CSPerFrame +{ + GENERATED_BODY() + + //GENERATED_BODY() + + //UPROPERTY(EditAnywhere, BlueprintReadWrite) + uint32 ThreadCount; + + //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FBonus Struct") + uint32 ostride; + //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FBonus Struct") + uint32 istride; + //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FBonus Struct") + uint32 pstride; + //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FBonus Struct") + float PhaseBase; + + //FRadix008A_CSPerFrame::FRadix008A_CSPerFrame() + //{ + // ThreadCount = 0; + // ostride = 0; + // istride = 0; + // pstride = 0; + // PhaseBase = 0; + //} +}; + +/** Radix FFT data (for 512x512 buffer size) */ +USTRUCT() +struct FRadixPlan512 +{ + GENERATED_BODY() + +public: + // More than one array can be transformed at same time + UPROPERTY() + uint32 Slices; + + // For 512x512 config, we need 6 sets of parameters + //UPROPERTY() + FRadix008A_CSPerFrame PerFrame[FFT_PARAM_SETS]; + + // Temporary buffers + //UPROPERTY() + FStructuredBufferRHIRef pBuffer_Tmp; + //UPROPERTY() + FUnorderedAccessViewRHIRef pUAV_Tmp; + //UPROPERTY() + FShaderResourceViewRHIRef pSRV_Tmp; +}; \ No newline at end of file diff --git a/Source/VaOceanPlugin/Private/VaOceanPlugin.cpp b/Source/VaOceanPlugin/Private/VaOceanPlugin.cpp new file mode 100644 index 0000000..443f1f7 --- /dev/null +++ b/Source/VaOceanPlugin/Private/VaOceanPlugin.cpp @@ -0,0 +1,37 @@ +// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. + +//#include "VaOceanPlugin.h" +#include "VaOceanPluginPrivatePCH.h" + +//#define LOCTEXT_NAMESPACE "FVaOceanPluginModule" + +//void FVaOceanPluginModule::StartupModule() +//{ +// // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +//} +// +//void FVaOceanPluginModule::ShutdownModule() +//{ +// // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, +// // we call this function before unloading the module. +//} +// +////#undef LOCTEXT_NAMESPACE +// +//IMPLEMENT_MODULE(FVaOceanPluginModule, VaOceanPlugin) + +class FVaOceanPlugin : public IVaOceanPlugin +{ + /** IModuleInterface implementation */ + virtual void StartupModule() override + { + + } + + virtual void ShutdownModule() override + { + + } +}; + +IMPLEMENT_MODULE(FVaOceanPlugin, VaOceanPlugin) \ No newline at end of file diff --git a/Source/VaOceanPlugin/Private/VaOceanPluginPrivatePCH.h b/Source/VaOceanPlugin/Private/VaOceanPluginPrivatePCH.h new file mode 100644 index 0000000..ace0cc8 --- /dev/null +++ b/Source/VaOceanPlugin/Private/VaOceanPluginPrivatePCH.h @@ -0,0 +1,27 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +#pragma once + +#include "CoreUObject.h" +#include "Engine.h" +#include "UniformBuffer.h" +#include "GlobalShader.h" +#include "Public/PipelineStateCache.h" +#include "ObjectMacros.h" + + +// You should place include statements to your module's private header files here. You only need to +// add includes for headers that are used in most of your module's source files though. +#include "ModuleManager.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogVaOcean, Log, All); + +#include "IVaOceanPlugin.h" + +#include "VaOceanRadixFFT.h" +#include "VaOceanTypes.h" +#include "VaOceanShaders.h" +#include "VaOceanSimulator.h" + + + diff --git a/Source/VaOceanPlugin/Private/VaOceanRadixFFT.cpp b/Source/VaOceanPlugin/Private/VaOceanRadixFFT.cpp new file mode 100644 index 0000000..b455686 --- /dev/null +++ b/Source/VaOceanPlugin/Private/VaOceanRadixFFT.cpp @@ -0,0 +1,152 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +#include "VaOceanRadixFFT.h" +#include "VaOceanPluginPrivatePCH.h" +//#include "VaOceanShaders.h" + + + +void Radix008A( + FRHICommandListImmediate & RHICmdList, + FRadixPlan512* Plan, + uint32 ParamSet, + FUnorderedAccessViewRHIRef pUAV_Dst, + FShaderResourceViewRHIRef pSRV_Src, + uint32 ThreadCount, + uint32 istride) +{ + check(ParamSet < FFT_PARAM_SETS); + const auto FeatureLevel = GMaxRHIFeatureLevel; + // Setup execution configuration + uint32 grid = ThreadCount / COHERENCY_GRANULARITY; + + FRadixFFTUniformParameters Parameters; + Parameters.ThreadCount = Plan->PerFrame[ParamSet].ThreadCount; + Parameters.ostride = Plan->PerFrame[ParamSet].ostride; + Parameters.istride = Plan->PerFrame[ParamSet].istride; + Parameters.pstride = Plan->PerFrame[ParamSet].pstride; + Parameters.PhaseBase = Plan->PerFrame[ParamSet].PhaseBase; + + FRadixFFTUniformBufferRef UniformBuffer = FRadixFFTUniformBufferRef::CreateUniformBufferImmediate(Parameters, EUniformBufferUsage::UniformBuffer_SingleFrame); + + if (istride > 1) + { + TShaderMapRef Radix008A_CS(GetGlobalShaderMap(FeatureLevel)); + RHICmdList.SetComputeShader(Radix008A_CS->GetComputeShader()); + + Radix008A_CS->SetParameters(RHICmdList, UniformBuffer); + Radix008A_CS->SetParameters(RHICmdList, pSRV_Src, pUAV_Dst); + + RHICmdList.DispatchComputeShader(grid, 1, 1); + + Radix008A_CS->UnsetParameters(RHICmdList); + } + else + { + TShaderMapRef Radix008A_CS2(GetGlobalShaderMap(FeatureLevel)); + RHICmdList.SetComputeShader(Radix008A_CS2->GetComputeShader()); + + Radix008A_CS2->SetParameters(RHICmdList, UniformBuffer); + Radix008A_CS2->SetParameters(RHICmdList, pSRV_Src, pUAV_Dst); + + RHICmdList.DispatchComputeShader(grid, 1, 1); + + Radix008A_CS2->UnsetParameters(RHICmdList); + } +} + +void RadixSetPerFrameParams(FRadixPlan512* Plan, + uint32 ParamSet, + uint32 ThreadCount, + uint32 ostride, + uint32 istride, + uint32 pstride, + float PhaseBase) +{ + check(ParamSet < FFT_PARAM_SETS); + + Plan->PerFrame[ParamSet].ThreadCount = ThreadCount; + Plan->PerFrame[ParamSet].ostride = ostride; + Plan->PerFrame[ParamSet].istride = istride; + Plan->PerFrame[ParamSet].pstride = pstride; + Plan->PerFrame[ParamSet].PhaseBase = PhaseBase; +} + +void RadixCreatePlan(FRadixPlan512* Plan, uint32 Slices) +{ + Plan->Slices = Slices; + + // Create 6 param sets for 512x512 transform + const uint32 thread_count = Plan->Slices * (512 * 512) / 8; + uint32 ostride = 512 * 512 / 8; + uint32 istride = ostride; + uint32 pstride = 512; + double phase_base = -TWO_PI / (512.0 * 512.0); + + RadixSetPerFrameParams(Plan, 0, thread_count, ostride, istride, pstride, (float)phase_base); + + for (int i = 1; i < FFT_PARAM_SETS; i++) + { + istride /= 8; + phase_base *= 8.0; + + if (i == 3) + { + ostride /= 512; + pstride = 1; + } + + RadixSetPerFrameParams(Plan, i, thread_count, ostride, istride, pstride, (float)phase_base); + } + + // Temp buffers + uint32 BytesPerElement = sizeof(float) * 2; + uint32 NumElements = (512 * Plan->Slices) * 512; + + FRHIResourceCreateInfo ResourceCreateInfo; + ResourceCreateInfo.BulkData = nullptr; + ResourceCreateInfo.ResourceArray = nullptr; + Plan->pBuffer_Tmp = RHICreateStructuredBuffer(BytesPerElement, BytesPerElement * NumElements, (BUF_UnorderedAccess | BUF_ShaderResource), ResourceCreateInfo); + + Plan->pUAV_Tmp = RHICreateUnorderedAccessView(Plan->pBuffer_Tmp, false, false); + Plan->pSRV_Tmp = RHICreateShaderResourceView(Plan->pBuffer_Tmp); +} + +void RadixDestroyPlan(FRadixPlan512* Plan) +{ + Plan->pBuffer_Tmp.SafeRelease(); + Plan->pUAV_Tmp.SafeRelease(); + Plan->pSRV_Tmp.SafeRelease(); +} + +void RadixCompute( + FRHICommandListImmediate& RHICmdList, + FRadixPlan512* Plan, + FUnorderedAccessViewRHIRef pUAV_Dst, + FShaderResourceViewRHIRef pSRV_Dst, + FShaderResourceViewRHIRef pSRV_Src) +{ + const uint32 thread_count = Plan->Slices * (512 * 512) / 8; + uint32 istride = 512 * 512; + + FUnorderedAccessViewRHIRef pUAV_Tmp = Plan->pUAV_Tmp; + FShaderResourceViewRHIRef pSRV_Tmp = Plan->pSRV_Tmp; + + istride /= 8; + Radix008A(RHICmdList, Plan, 0, pUAV_Tmp, pSRV_Src, thread_count, istride); + + istride /= 8; + Radix008A(RHICmdList, Plan, 1, pUAV_Dst, pSRV_Tmp, thread_count, istride); + + istride /= 8; + Radix008A(RHICmdList, Plan, 2, pUAV_Tmp, pSRV_Dst, thread_count, istride); + + istride /= 8; + Radix008A(RHICmdList, Plan, 3, pUAV_Dst, pSRV_Tmp, thread_count, istride); + + istride /= 8; + Radix008A(RHICmdList, Plan, 4, pUAV_Tmp, pSRV_Dst, thread_count, istride); + + istride /= 8; + Radix008A(RHICmdList, Plan, 5, pUAV_Dst, pSRV_Tmp, thread_count, istride); +} diff --git a/Source/VaOceanPlugin/Private/VaOceanShaders.cpp b/Source/VaOceanPlugin/Private/VaOceanShaders.cpp new file mode 100644 index 0000000..f734fda --- /dev/null +++ b/Source/VaOceanPlugin/Private/VaOceanShaders.cpp @@ -0,0 +1,18 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license +#include "VaOceanShaders.h" +#include "VaOceanPluginPrivatePCH.h" + +IMPLEMENT_SHADER_TYPE(, FUpdateSpectrumCS, TEXT("/Plugin/VaOceanPlugin/Private/VaOcean_CS.usf"), TEXT("UpdateSpectrumCS"), SF_Compute); +IMPLEMENT_SHADER_TYPE(, FRadix008A_CS, TEXT("/Plugin/VaOceanPlugin/Private/VaOcean_FFT.usf"), TEXT("Radix008A_CS"), SF_Compute); +IMPLEMENT_SHADER_TYPE(, FRadix008A_CS2, TEXT("/Plugin/VaOceanPlugin/Private/VaOcean_FFT.usf"), TEXT("Radix008A_CS2"), SF_Compute); + +IMPLEMENT_SHADER_TYPE(, FQuadVS, TEXT("/Plugin/VaOceanPlugin/Private/VaOcean_VS_PS.usf"), TEXT("QuadVS"), SF_Vertex); +IMPLEMENT_SHADER_TYPE(, FUpdateDisplacementPS, TEXT("/Plugin/VaOceanPlugin/Private/VaOcean_VS_PS.usf"), TEXT("UpdateDisplacementPS"), SF_Pixel); +IMPLEMENT_SHADER_TYPE(, FGenGradientFoldingPS, TEXT("/Plugin/VaOceanPlugin/Private/VaOcean_VS_PS.usf"), TEXT("GenGradientFoldingPS"), SF_Pixel); + +IMPLEMENT_UNIFORM_BUFFER_STRUCT(FUpdateSpectrumUniformParameters, TEXT("PerFrameSp")); +IMPLEMENT_UNIFORM_BUFFER_STRUCT(FUpdateDisplacementUniformParameters, TEXT("PerFrameDisp")); +IMPLEMENT_UNIFORM_BUFFER_STRUCT(FRadixFFTUniformParameters, TEXT("PerFrameFFT")); + + +//IMPLEMENT_SHADER_TYPE(, FLensDistortionUVGenerationPS, TEXT("/Plugin/ShaderTest/Private/MyShader.usf"), TEXT("MainPS"), SF_Pixel) \ No newline at end of file diff --git a/Source/VaOceanPlugin/Private/VaOceanSimulator.cpp b/Source/VaOceanPlugin/Private/VaOceanSimulator.cpp new file mode 100644 index 0000000..5ac7886 --- /dev/null +++ b/Source/VaOceanPlugin/Private/VaOceanSimulator.cpp @@ -0,0 +1,486 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + + + +#include "VaOceanSimulator.h" +#include "VaOceanPluginPrivatePCH.h" + +#define HALF_SQRT_2 0.7071068f +#define GRAV_ACCEL 981.0f // The acceleration of gravity, cm/s^2 + +#define BLOCK_SIZE_X 16 +#define BLOCK_SIZE_Y 16 + +/** Vertex declaration for the fullscreen 2D quad */ +TGlobalResource GQuadVertexDeclaration; + + +////////////////////////////////////////////////////////////////////////// +// Height map generation helpers + +/** Generating gaussian random number with mean 0 and standard deviation 1 */ +float Gauss() +{ + float u1 = rand() / (float)RAND_MAX; + float u2 = rand() / (float)RAND_MAX; + + if (u1 < 1e-6f) + { + u1 = 1e-6f; + } + + return sqrtf(-2 * logf(u1)) * cosf(2 * PI * u2); +} + +/** +* Phillips Spectrum +* K: normalized wave vector, W: wind direction, v: wind velocity, a: amplitude constant +*/ +float Phillips(FVector2D K, FVector2D W, float v, float a, float dir_depend) +{ + // Largest possible wave from constant wind of velocity v + float l = v * v / GRAV_ACCEL; + + // Damp out waves with very small length w << l + float w = l / 1000; + + float Ksqr = K.X * K.X + K.Y * K.Y; + float Kcos = K.X * W.X + K.Y * W.Y; + float phillips = a * expf(-1 / (l * l * Ksqr)) / (Ksqr * Ksqr * Ksqr) * (Kcos * Kcos); + + // Filter out waves moving opposite to wind + if (Kcos < 0) + { + phillips *= dir_depend; + } + + // Damp out waves with very small length w << l + return phillips * expf(-Ksqr * w * w); +} + + +////////////////////////////////////////////////////////////////////////// +// Phillips spectrum simulator + +AVaOceanSimulator::AVaOceanSimulator(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + PrimaryActorTick.bCanEverTick = true; + PrimaryActorTick.TickGroup = TG_DuringPhysics; + bReplicates = true; + NetUpdateFrequency = 10.f; + + SimulationWorldTime = 0.f; + + // Vertex to draw on render targets + m_pQuadVB[0].Set(-1.0f, -1.0f, 0.0f, 1.0f); + m_pQuadVB[1].Set(-1.0f, 1.0f, 0.0f, 1.0f); + m_pQuadVB[2].Set(1.0f, -1.0f, 0.0f, 1.0f); + m_pQuadVB[3].Set(1.0f, 1.0f, 0.0f, 1.0f); +} + +void AVaOceanSimulator::InitializeInternalData() +{ + // Cache shader immutable parameters (looks ugly, but nicely used then) + UpdateSpectrumCSImmutableParams.g_ActualDim = SpectrumConfig.DispMapDimension; + UpdateSpectrumCSImmutableParams.g_InWidth = UpdateSpectrumCSImmutableParams.g_ActualDim + 4; + UpdateSpectrumCSImmutableParams.g_OutWidth = UpdateSpectrumCSImmutableParams.g_ActualDim; + UpdateSpectrumCSImmutableParams.g_OutHeight = UpdateSpectrumCSImmutableParams.g_ActualDim; + UpdateSpectrumCSImmutableParams.g_DtxAddressOffset = FMath::Square(UpdateSpectrumCSImmutableParams.g_ActualDim); + UpdateSpectrumCSImmutableParams.g_DtyAddressOffset = FMath::Square(UpdateSpectrumCSImmutableParams.g_ActualDim) * 2; + + // Height map H(0) + int32 height_map_size = (SpectrumConfig.DispMapDimension + 4) * (SpectrumConfig.DispMapDimension + 1); + TResourceArray h0_data; + h0_data.Init(FVector2D::ZeroVector, height_map_size); + TResourceArray omega_data; + omega_data.Init(0.0f, height_map_size); + InitHeightMap(SpectrumConfig, h0_data, omega_data); + + int hmap_dim = SpectrumConfig.DispMapDimension; + int input_full_size = (hmap_dim + 4) * (hmap_dim + 1); + // This value should be (hmap_dim / 2 + 1) * hmap_dim, but we use full sized buffer here for simplicity. + int input_half_size = hmap_dim * hmap_dim; + int output_size = hmap_dim * hmap_dim; + + // For filling the buffer with zeroes + TResourceArray zero_data; + zero_data.Init(0.0f, 3 * output_size * 2); + + // RW buffer allocations + // H0 + uint32 float2_stride = 2 * sizeof(float); + CreateBufferAndUAV(&h0_data, input_full_size * float2_stride, float2_stride, &m_pBuffer_Float2_H0, &m_pUAV_H0, &m_pSRV_H0); + + // Notice: The following 3 buffers should be half sized buffer because of conjugate symmetric input. But + // we use full sized buffers due to the CS4.0 restriction. + + // Put H(t), Dx(t) and Dy(t) into one buffer because CS4.0 allows only 1 UAV at a time + CreateBufferAndUAV(&zero_data, 3 * input_half_size * float2_stride, float2_stride, &m_pBuffer_Float2_Ht, &m_pUAV_Ht, &m_pSRV_Ht); + + // omega + CreateBufferAndUAV(&omega_data, input_full_size * sizeof(float), sizeof(float), &m_pBuffer_Float_Omega, &m_pUAV_Omega, &m_pSRV_Omega); + + // Re-init the array because it was discarded by previous buffer creation + zero_data.Empty(); + zero_data.Init(0.0f, 3 * output_size * 2); + // Notice: The following 3 should be real number data. But here we use the complex numbers and C2C FFT + // due to the CS4.0 restriction. + // Put Dz, Dx and Dy into one buffer because CS4.0 allows only 1 UAV at a time + CreateBufferAndUAV(&zero_data, 3 * output_size * float2_stride, float2_stride, &m_pBuffer_Float_Dxyz, &m_pUAV_Dxyz, &m_pSRV_Dxyz); + + // FFT + RadixCreatePlan(&FFTPlan, 3); + + // Turn the flag on + bSimulatorInitializated = true; +} + +void AVaOceanSimulator::InitHeightMap(const FSpectrumData& Params, TResourceArray& out_h0, TResourceArray& out_omega) +{ + int32 i, j; + FVector2D K, Kn; + + FVector2D wind_dir = Params.WindDirection; + wind_dir.Normalize(); + + float a = Params.WaveAmplitude * 1e-7f; // It is too small. We must scale it for editing. + float v = Params.WindSpeed; + float dir_depend = Params.WindDependency; + + int height_map_dim = Params.DispMapDimension; + float patch_length = Params.PatchLength; + + // Initialize random generator. + srand(0); + + for (i = 0; i <= height_map_dim; i++) + { + // K is wave-vector, range [-|DX/W, |DX/W], [-|DY/H, |DY/H] + K.Y = (-height_map_dim / 2.0f + i) * (2 * PI / patch_length); + + for (j = 0; j <= height_map_dim; j++) + { + K.X = (-height_map_dim / 2.0f + j) * (2 * PI / patch_length); + + float phil = (K.X == 0 && K.Y == 0) ? 0 : sqrtf(Phillips(K, wind_dir, v, a, dir_depend)); + + out_h0[i * (height_map_dim + 4) + j].X = float(phil * Gauss() * HALF_SQRT_2); + out_h0[i * (height_map_dim + 4) + j].Y = float(phil * Gauss() * HALF_SQRT_2); + + // The angular frequency is following the dispersion relation: + // out_omega^2 = g*k + // The equation of Gerstner wave: + // x = x0 - K/k * A * sin(dot(K, x0) - sqrt(g * k) * t), x is a 2D vector. + // z = A * cos(dot(K, x0) - sqrt(g * k) * t) + // Gerstner wave shows that a point on a simple sinusoid wave is doing a uniform circular + // motion with the center (x0, y0, z0), radius A, and the circular plane is parallel to + // vector K. + out_omega[i * (height_map_dim + 4) + j] = sqrtf(GRAV_ACCEL * sqrtf(K.X * K.X + K.Y * K.Y)); + } + } +} + +void AVaOceanSimulator::CreateBufferAndUAV(FResourceArrayInterface* Data, uint32 byte_width, uint32 byte_stride, + FStructuredBufferRHIRef* ppBuffer, FUnorderedAccessViewRHIRef* ppUAV, FShaderResourceViewRHIRef* ppSRV) +{ + FRHIResourceCreateInfo ResourceCreateInfo; + ResourceCreateInfo.ResourceArray = Data; + *ppBuffer = RHICreateStructuredBuffer(byte_stride, Data->GetResourceDataSize(), (BUF_UnorderedAccess | BUF_ShaderResource), ResourceCreateInfo); + + *ppUAV = RHICreateUnorderedAccessView(*ppBuffer, false, false); + *ppSRV = RHICreateShaderResourceView(*ppBuffer); +} + +void AVaOceanSimulator::ClearInternalData() +{ + RadixDestroyPlan(&FFTPlan); + + m_pBuffer_Float2_H0.SafeRelease(); + m_pUAV_H0.SafeRelease(); + m_pSRV_H0.SafeRelease(); + + m_pBuffer_Float_Omega.SafeRelease(); + m_pUAV_Omega.SafeRelease(); + m_pSRV_Omega.SafeRelease(); + + m_pBuffer_Float2_Ht.SafeRelease(); + m_pUAV_Ht.SafeRelease(); + m_pSRV_Ht.SafeRelease(); + + m_pBuffer_Float_Dxyz.SafeRelease(); + m_pUAV_Dxyz; + m_pSRV_Dxyz; + + bSimulatorInitializated = false; +} + +void AVaOceanSimulator::ResetInternalData() +{ + ClearInternalData(); + InitializeInternalData(); +} + +void AVaOceanSimulator::BeginDestroy() +{ + //ClearInternalData(); + + Super::BeginDestroy(); +} + + +////////////////////////////////////////////////////////////////////////// +// Simulation + +#if WITH_EDITOR +void AVaOceanSimulator::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + // AActor::PostEditChange will ForceUpdateComponents() + Super::PostEditChangeProperty(PropertyChangedEvent); + + // @todo Update shader configuration +} +#endif // WITH_EDITOR + +bool AVaOceanSimulator::ShouldTickIfViewportsOnly() const +{ + return true; +} + +void AVaOceanSimulator::Tick(float DeltaSeconds) +{ + Super::Tick(DeltaSeconds); + + // Check that data is initializated + if (!bSimulatorInitializated) + { + InitializeInternalData(); + } + + //// Tick world time + SimulationWorldTime += DeltaSeconds; + + UE_LOG(LogTemp, Warning, TEXT("Your message")); + + // Process simulation shaders + UpdateDisplacementMap(SimulationWorldTime); +} + +void AVaOceanSimulator::UpdateDisplacementMap(float WorldTime) +{ + if (!DisplacementTexture || !GradientTexture) + return; + + // ---------------------------- H(0) -> H(t), D(x, t), D(y, t) -------------------------------- + FUpdateSpectrumCSPerFrame UpdateSpectrumCSPerFrameParams; + UpdateSpectrumCSPerFrameParams.g_Time = WorldTime * SpectrumConfig.TimeScale; + UpdateSpectrumCSPerFrameParams.g_ChoppyScale = SpectrumConfig.ChoppyScale; + UpdateSpectrumCSPerFrameParams.m_pSRV_H0 = m_pSRV_H0; + UpdateSpectrumCSPerFrameParams.m_pSRV_Omega = m_pSRV_Omega; + UpdateSpectrumCSPerFrameParams.m_pUAV_Ht = m_pUAV_Ht; + + ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER( + UpdateSpectrumCSCommand, + FUpdateSpectrumCSImmutable, ImmutableParams, UpdateSpectrumCSImmutableParams, + FUpdateSpectrumCSPerFrame, PerFrameParams, UpdateSpectrumCSPerFrameParams, + { + FUpdateSpectrumUniformParameters Parameters; + const auto FeatureLevel = GMaxRHIFeatureLevel; + Parameters.Time = PerFrameParams.g_Time; + + FUpdateSpectrumUniformBufferRef UniformBuffer = FUpdateSpectrumUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleFrame); + + TShaderMapRef UpdateSpectrumCS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + RHICmdList.SetComputeShader(UpdateSpectrumCS->GetComputeShader()); + + UpdateSpectrumCS->SetParameters(RHICmdList, ImmutableParams.g_ActualDim, + ImmutableParams.g_InWidth, ImmutableParams.g_OutWidth, ImmutableParams.g_OutHeight, + ImmutableParams.g_DtxAddressOffset, ImmutableParams.g_DtyAddressOffset); + + UpdateSpectrumCS->SetParameters(RHICmdList, UniformBuffer, PerFrameParams.m_pSRV_H0, PerFrameParams.m_pSRV_Omega); + UpdateSpectrumCS->SetOutput(RHICmdList, PerFrameParams.m_pUAV_Ht); + + uint32 group_count_x = (ImmutableParams.g_ActualDim + BLOCK_SIZE_X - 1) / BLOCK_SIZE_X; + uint32 group_count_y = (ImmutableParams.g_ActualDim + BLOCK_SIZE_Y - 1) / BLOCK_SIZE_Y; + RHICmdList.DispatchComputeShader(group_count_x, group_count_y, 1); + + UpdateSpectrumCS->UnsetParameters(RHICmdList); + UpdateSpectrumCS->UnbindBuffers(RHICmdList); + }); + + // ------------------------------------ Perform FFT ------------------------------------------- + ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER( + RadixFFTCommand, + FRadixPlan512*, pPlan, &FFTPlan, + FUnorderedAccessViewRHIRef, m_pUAV_Dxyz, m_pUAV_Dxyz, + FShaderResourceViewRHIRef, m_pSRV_Dxyz, m_pSRV_Dxyz, + FShaderResourceViewRHIRef, m_pSRV_Ht, m_pSRV_Ht, + { + RadixCompute(RHICmdList, pPlan, m_pUAV_Dxyz, m_pSRV_Dxyz, m_pSRV_Ht); + }); + + // --------------------------------- Wrap Dx, Dy and Dz --------------------------------------- + FUpdateDisplacementPSPerFrame UpdateDisplacementPSPerFrameParams; + UpdateDisplacementPSPerFrameParams.g_ChoppyScale = SpectrumConfig.ChoppyScale; + UpdateDisplacementPSPerFrameParams.g_GridLen = SpectrumConfig.DispMapDimension / SpectrumConfig.PatchLength; + UpdateDisplacementPSPerFrameParams.g_InputDxyz = m_pSRV_Dxyz; + FMemory::Memcpy(UpdateDisplacementPSPerFrameParams.m_pQuadVB, m_pQuadVB, sizeof(m_pQuadVB[0]) * 4); + + FTextureRenderTargetResource* DisplacementRenderTarget = DisplacementTexture->GameThread_GetRenderTargetResource(); + + ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER( + UpdateDisplacementPSCommand, + FTextureRenderTargetResource*, TextureRenderTarget, DisplacementRenderTarget, + FUpdateSpectrumCSImmutable, ImmutableParams, UpdateSpectrumCSImmutableParams, // We're using the same params as for CS + FUpdateDisplacementPSPerFrame, PerFrameParams, UpdateDisplacementPSPerFrameParams, + { + check(IsInRenderingThread()); + + ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; + + FIntPoint DisplacementMapResolution(TextureRenderTarget->GetSizeX(), TextureRenderTarget->GetSizeY()); + + FUpdateDisplacementUniformParameters Parameters; + Parameters.ChoppyScale = PerFrameParams.g_ChoppyScale; + Parameters.GridLen = PerFrameParams.g_GridLen; + + FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList(); + + TShaderMapRef QuadVS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + TShaderMapRef UpdateDisplacementPS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + + FUpdateDisplacementUniformBufferRef UniformBuffer = FUpdateDisplacementUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleFrame); + + SetRenderTarget(RHICmdList, TextureRenderTarget->GetRenderTargetTexture(), NULL); + + FGraphicsPipelineStateInitializer GraphicsPSOInit; + RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); + GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); + GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); + GraphicsPSOInit.PrimitiveType = PT_TriangleStrip; + GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GQuadVertexDeclaration.VertexDeclarationRHI; + GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*QuadVS); + GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*UpdateDisplacementPS); + GraphicsPSOInit.DepthStencilState = (TStaticDepthStencilState::GetRHI());//Black vodo magic shit. Why the fucking hell are the two parethisis needed? The example I found worked fine with out them. I spent wayyy to long stuck on this line + + SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit); + + UpdateDisplacementPS->SetParameters(RHICmdList, ImmutableParams.g_ActualDim, + ImmutableParams.g_InWidth, ImmutableParams.g_OutWidth, ImmutableParams.g_OutHeight, + ImmutableParams.g_DtxAddressOffset, ImmutableParams.g_DtyAddressOffset); + + UpdateDisplacementPS->SetParameters(RHICmdList, UniformBuffer, PerFrameParams.g_InputDxyz); + + DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, PerFrameParams.m_pQuadVB, sizeof(PerFrameParams.m_pQuadVB[0])); + + UpdateDisplacementPS->UnsetParameters(RHICmdList); + + // Resolve render target. + RHICmdList.CopyToResolveTarget( + TextureRenderTarget->GetRenderTargetTexture(), + TextureRenderTarget->TextureRHI, + false, FResolveParams()); + }); + + // ----------------------------------- Generate Normal ---------------------------------------- + FGenGradientFoldingPSPerFrame GenGradientFoldingPSPerFrameParams; + GenGradientFoldingPSPerFrameParams.g_ChoppyScale = SpectrumConfig.ChoppyScale; + GenGradientFoldingPSPerFrameParams.g_GridLen = SpectrumConfig.DispMapDimension / SpectrumConfig.PatchLength; + FMemory::Memcpy(GenGradientFoldingPSPerFrameParams.m_pQuadVB, m_pQuadVB, sizeof(m_pQuadVB[0]) * 4); + + FTextureRenderTargetResource* GradientRenderTarget = GradientTexture->GameThread_GetRenderTargetResource(); + + ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER( + GenGradientFoldingPSCommand, + FTextureRenderTargetResource*, TextureRenderTarget, GradientRenderTarget, + FUpdateSpectrumCSImmutable, ImmutableParams, UpdateSpectrumCSImmutableParams, // We're using the same params as for CS + FGenGradientFoldingPSPerFrame, PerFrameParams, GenGradientFoldingPSPerFrameParams, + FTextureRenderTargetResource*, DisplacementRenderTarget, DisplacementRenderTarget, + { + check(IsInRenderingThread()); + + FUpdateDisplacementUniformParameters Parameters; + ERHIFeatureLevel::Type FeatureLevel = GMaxRHIFeatureLevel; + Parameters.ChoppyScale = PerFrameParams.g_ChoppyScale; + Parameters.GridLen = PerFrameParams.g_GridLen; + + FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList(); + + FUpdateDisplacementUniformBufferRef UniformBuffer = FUpdateDisplacementUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleFrame); + + TShaderMapRef QuadVS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + TShaderMapRef GenGradientFoldingPS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); + + SetRenderTarget(RHICmdList, TextureRenderTarget->GetRenderTargetTexture(), FTextureRHIRef()); + + FGraphicsPipelineStateInitializer GraphicsPSOInit; + RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); + GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); + GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); + GraphicsPSOInit.PrimitiveType = PT_TriangleStrip; + GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GQuadVertexDeclaration.VertexDeclarationRHI; + GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*QuadVS); + GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*GenGradientFoldingPS); + GraphicsPSOInit.DepthStencilState = (TStaticDepthStencilState::GetRHI());//Black vodo magic shit. Why the fucking hell are the two parethisis needed? The example I found worked fine with out them. I spent wayyy to long stuck on this line + + SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit); + + GenGradientFoldingPS->SetParameters(RHICmdList, ImmutableParams.g_ActualDim, + ImmutableParams.g_InWidth, ImmutableParams.g_OutWidth, ImmutableParams.g_OutHeight, + ImmutableParams.g_DtxAddressOffset, ImmutableParams.g_DtyAddressOffset); + + GenGradientFoldingPS->SetParameters(RHICmdList, UniformBuffer, DisplacementRenderTarget->TextureRHI); + + DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, PerFrameParams.m_pQuadVB, sizeof(PerFrameParams.m_pQuadVB[0])); + + GenGradientFoldingPS->UnsetParameters(RHICmdList); + + // Generate new mipmaps now + RHICmdList.GenerateMips(TextureRenderTarget->TextureRHI); + }); + + + //Test code for potentialy creating collision data from displacement renderTarget + //TArray SurfData; + //FTextureRenderTargetResource* RenderTarget = DisplacementTexture->GameThread_GetRenderTargetResource(); + + //RenderTarget->ReadPixels(SurfData); + + //FColor* colorPtr = SurfData.GetData(); + + + //FString colorStr = *colorPtr->ToHex(); + + //UE_LOG(LogTemp, Warning, TEXT("%s"), *colorStr); +} + +////////////////////////////////////////////////////////////////////////// +// Spectrum configuration + +const FSpectrumData& AVaOceanSimulator::GetSpectrumConfig() const +{ + return SpectrumConfig; +} + + +////////////////////////////////////////////////////////////////////////// +// Utilities + +UTextureRenderTarget2D* AVaOceanSimulator::CreateRenderTarget(bool bInForceLinearGamma, bool bNormalMap, EPixelFormat InPixelFormat, FIntPoint& InTargetSize) +{ + UTextureRenderTarget2D* NewRenderTarget = NewObject(this); + check(NewRenderTarget); + + SetupRenderTarget(NewRenderTarget, bInForceLinearGamma, bNormalMap, InPixelFormat, InTargetSize); + + return NewRenderTarget; +} + +void AVaOceanSimulator::SetupRenderTarget(UTextureRenderTarget2D * InRenderTarget, bool bInForceLinearGamma, bool bNormalMap, EPixelFormat InPixelFormat, FIntPoint & InTargetSize) +{ + const FLinearColor ClearColor = bNormalMap ? FLinearColor(0.0f, 0.0f, 0.0f, 0.0f) : FLinearColor(1.0f, 0.0f, 1.0f, 0.0f); + + InRenderTarget->ClearColor = ClearColor; + InRenderTarget->TargetGamma = 0.0f; + InRenderTarget->InitCustomFormat(InTargetSize.X, InTargetSize.Y, InPixelFormat, bInForceLinearGamma); +} diff --git a/Source/VaOceanPlugin/Public/IVaOceanPlugin.h b/Source/VaOceanPlugin/Public/IVaOceanPlugin.h new file mode 100644 index 0000000..da82aa7 --- /dev/null +++ b/Source/VaOceanPlugin/Public/IVaOceanPlugin.h @@ -0,0 +1,37 @@ +// Created 2014-2016 Vladimir Alyamkin. MIT license + +#pragma once + +#include "ModuleManager.h" + + +/** +* The public interface to this module. In most cases, this interface is only public to sibling modules +* within this plugin. +*/ +class IVaOceanPlugin : public IModuleInterface +{ + +public: + + /** + * Singleton-like access to this module's interface. This is just for convenience! + * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. + * + * @return Returns singleton instance, loading the module on demand if needed + */ + static inline IVaOceanPlugin& Get() + { + return FModuleManager::LoadModuleChecked< IVaOceanPlugin >("VaOceanPlugin"); + } + + /** + * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. + * + * @return True if the module is loaded and ready to use + */ + static inline bool IsAvailable() + { + return FModuleManager::Get().IsModuleLoaded("VaOceanPlugin"); + } +}; \ No newline at end of file diff --git a/Source/VaOceanPlugin/VaOceanPlugin.Build.cs b/Source/VaOceanPlugin/VaOceanPlugin.Build.cs new file mode 100644 index 0000000..61abff7 --- /dev/null +++ b/Source/VaOceanPlugin/VaOceanPlugin.Build.cs @@ -0,0 +1,59 @@ +// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class VaOceanPlugin : ModuleRules +{ + public VaOceanPlugin(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + "VaOceanPlugin/Public" + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + "VaOceanPlugin/Private", + // ... add other private include paths required here ... + } + ); + + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "RenderCore", + "ShaderCore", + "RHI", + "CoreUObject", + "Engine", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/VaOcean.uplugin b/VaOceanPlugin.uplugin similarity index 70% rename from VaOcean.uplugin rename to VaOceanPlugin.uplugin index 277ec0a..d3eee44 100644 --- a/VaOcean.uplugin +++ b/VaOceanPlugin.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 7, - "VersionName": "0.5", + "VersionName": "0.6", "FriendlyName": "VaOcean", "Description": "Ocean surface simulation plugin", "Category": "Environment", @@ -13,11 +13,11 @@ "CanContainContent": true, "IsBetaVersion": false, "Installed": false, - "Modules": [ - { - "Name" : "VaOcean", - "Type" : "Runtime", - "LoadingPhase" : "PostConfigInit" - } + "Modules": [ + { + "Name": "VaOceanPlugin", + "Type": "Developer", + "LoadingPhase": "PostConfigInit" + } ] } \ No newline at end of file