Repository: ufna/VaOcean Branch: archive_UE4.12 Commit: a01274e0aac0 Files: 30 Total size: 60.8 KB Directory structure: gitextract_xwtm81v2/ ├── .gitignore ├── Content/ │ ├── Blueprints/ │ │ ├── OceanSimulator.uasset │ │ └── OceanSimulator_Debug.uasset │ ├── Maps/ │ │ └── Level_Test.umap │ ├── Materials/ │ │ ├── MI_OceanCS_Example.uasset │ │ ├── M_FFT_Debug.uasset │ │ └── M_OceanCS.uasset │ ├── Meshes/ │ │ └── OceanSurface_Near.uasset │ └── Textures/ │ ├── RT_Displacement.uasset │ ├── RT_Gradient.uasset │ ├── RT_Spectrum_Height.uasset │ ├── RT_Spectrum_Normals.uasset │ └── T_Noise_Perline.uasset ├── LICENSE ├── README.md ├── Shaders/ │ ├── VaOcean_CS.usf │ ├── VaOcean_FFT.usf │ └── VaOcean_VS_PS.usf ├── Source/ │ └── VaOceanPlugin/ │ ├── Classes/ │ │ ├── VaOceanRadixFFT.h │ │ ├── VaOceanShaders.h │ │ ├── VaOceanSimulator.h │ │ └── VaOceanTypes.h │ ├── Private/ │ │ ├── VaOceanPlugin.cpp │ │ ├── VaOceanPluginPrivatePCH.h │ │ ├── VaOceanRadixFFT.cpp │ │ ├── VaOceanShaders.cpp │ │ └── VaOceanSimulator.cpp │ ├── Public/ │ │ └── IVaOceanPlugin.h │ └── VaOceanPlugin.Build.cs └── VaOceanPlugin.uplugin ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled Object files *.slo *.lo *.o *.obj # Compiled Dynamic libraries *.so *.dylib *.dll # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app /Binaries/ /Intermediate/ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Vladimir Alyamkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ Welcome to the VaOcean source code! =================================== **Attn.! This branch contains archived version of plugin for UE 4.12 based on [NVIDIA SDK sample](https://developer.nvidia.com/dx11-samples) "FFT Ocean". It's deprecated and lives here for history only.** ---------- VaOcean is the ocean surface simulation plugin for [Unreal Engine 4](https://www.unrealengine.com/). The plugin includes: * 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.4 Alpha 6 Hotfix 1** ![SCREENSHOT](SCREENSHOT.jpg) Legal info ---------- Unreal® is a trademark or registered trademark of Epic Games, Inc. in the United States of America and elsewhere. Unreal® Engine, Copyright 1998 – 2014, Epic Games, Inc. All rights reserved. References ---------- 1. Tessendorf, Jerry. Simulating Ocean Water. In SIGGRAPH 2002 Course Notes #9 (Simulating Nature: Realistic and Interactive Techniques), ACM Press. 1. Phillips, O.M. 1957. On the generation of waves by turbulent wind. Journal of Fluid Mechanics. 2 (5): 417–445. ================================================ FILE: Shaders/VaOcean_CS.usf ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "Common.usf" #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; } } ================================================ FILE: Shaders/VaOcean_FFT.usf ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "Common.usf" #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]; } ================================================ FILE: Shaders/VaOcean_VS_PS.usf ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "Common.usf" ////////////////////////////////////////////////////////////////////////// // 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); } ================================================ FILE: Source/VaOceanPlugin/Classes/VaOceanRadixFFT.h ================================================ // Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. #pragma once #include "VaOceanPluginPrivatePCH.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 #define FFT_PARAM_SETS 6 /** Per frame parameters for FRadix008A_CS shader */ USTRUCT() struct FRadix008A_CSPerFrame { GENERATED_USTRUCT_BODY() uint32 ThreadCount; uint32 ostride; uint32 istride; uint32 pstride; float PhaseBase; }; /** Radix FFT data (for 512x512 buffer size) */ USTRUCT() struct FRadixPlan512 { GENERATED_USTRUCT_BODY() // More than one array can be transformed at same time uint32 Slices; // For 512x512 config, we need 6 sets of parameters FRadix008A_CSPerFrame PerFrame[FFT_PARAM_SETS]; // Temporary buffers FStructuredBufferRHIRef pBuffer_Tmp; FUnorderedAccessViewRHIRef pUAV_Tmp; FShaderResourceViewRHIRef pSRV_Tmp; }; 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); ================================================ FILE: Source/VaOceanPlugin/Classes/VaOceanShaders.h ================================================ // 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); } 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); } 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; } 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); } 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); } 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; }; ================================================ FILE: Source/VaOceanPlugin/Classes/VaOceanSimulator.h ================================================ // Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. #pragma once #include "VaOceanTypes.h" #include "VaOceanPluginPrivatePCH.h" #include "VaOceanSimulator.generated.h" #define PAD16(n) (((n)+15)/16*16) /** * Renders normals and heightmap from Phillips spectrum */ UCLASS(Blueprintable, BlueprintType, ClassGroup=Environment) class VAOCEANPLUGIN_API 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); }; ================================================ FILE: Source/VaOceanPlugin/Classes/VaOceanTypes.h ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #pragma once #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; } }; ================================================ FILE: Source/VaOceanPlugin/Private/VaOceanPlugin.cpp ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #include "VaOceanPluginPrivatePCH.h" class FVaOceanPlugin : public IVaOceanPlugin { /** IModuleInterface implementation */ virtual void StartupModule() override { } virtual void ShutdownModule() override { } }; IMPLEMENT_MODULE( FVaOceanPlugin, VaOceanPlugin ) DEFINE_LOG_CATEGORY(LogVaOcean); ================================================ FILE: Source/VaOceanPlugin/Private/VaOceanPluginPrivatePCH.h ================================================ // Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. #pragma once #include "CoreUObject.h" #include "Engine.h" #include "UniformBuffer.h" #include "ShaderParameters.h" #include "ShaderParameterUtils.h" #include "GlobalShader.h" #include "RHIStaticStates.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 "VaOceanTypes.h" #include "VaOceanShaders.h" #include "VaOceanRadixFFT.h" #include "VaOceanSimulator.h" ================================================ FILE: Source/VaOceanPlugin/Private/VaOceanRadixFFT.cpp ================================================ // Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. #include "VaOceanPluginPrivatePCH.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); } ================================================ FILE: Source/VaOceanPlugin/Private/VaOceanShaders.cpp ================================================ // Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. #include "VaOceanPluginPrivatePCH.h" IMPLEMENT_SHADER_TYPE(, FUpdateSpectrumCS, TEXT("VaOcean_CS"), TEXT("UpdateSpectrumCS"), SF_Compute); IMPLEMENT_SHADER_TYPE(, FRadix008A_CS, TEXT("VaOcean_FFT"), TEXT("Radix008A_CS"), SF_Compute); IMPLEMENT_SHADER_TYPE(, FRadix008A_CS2, TEXT("VaOcean_FFT"), TEXT("Radix008A_CS2"), SF_Compute); IMPLEMENT_SHADER_TYPE(, FQuadVS, TEXT("VaOcean_VS_PS"), TEXT("QuadVS"), SF_Vertex); IMPLEMENT_SHADER_TYPE(, FUpdateDisplacementPS, TEXT("VaOcean_VS_PS"), TEXT("UpdateDisplacementPS"), SF_Pixel); IMPLEMENT_SHADER_TYPE(, FGenGradientFoldingPS, TEXT("VaOcean_VS_PS"), 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")); ================================================ FILE: Source/VaOceanPlugin/Private/VaOceanSimulator.cpp ================================================ // Copyright 2014-2016 Vladimir Alyamkin. All Rights Reserved. #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; // 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, { const auto FeatureLevel = GMaxRHIFeatureLevel; FUpdateDisplacementUniformParameters Parameters; Parameters.ChoppyScale = PerFrameParams.g_ChoppyScale; Parameters.GridLen = PerFrameParams.g_GridLen; FUpdateDisplacementUniformBufferRef UniformBuffer = FUpdateDisplacementUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleFrame); SetRenderTarget(RHICmdList, TextureRenderTarget->GetRenderTargetTexture(), NULL); RHICmdList.Clear(true, FLinearColor::Transparent, false, 0.f, false, 0, FIntRect()); // Be sure we're blending right without any alpha influence on Color blending RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); TShaderMapRef QuadVS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); TShaderMapRef UpdateDisplacementPS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); static FGlobalBoundShaderState UpdateDisplacementBoundShaderState; SetGlobalBoundShaderState(RHICmdList, GMaxRHIFeatureLevel, UpdateDisplacementBoundShaderState, GQuadVertexDeclaration.VertexDeclarationRHI, *QuadVS, *UpdateDisplacementPS); 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); }); // ----------------------------------- 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, { FUpdateDisplacementUniformParameters Parameters; const auto FeatureLevel = GMaxRHIFeatureLevel; Parameters.ChoppyScale = PerFrameParams.g_ChoppyScale; Parameters.GridLen = PerFrameParams.g_GridLen; FUpdateDisplacementUniformBufferRef UniformBuffer = FUpdateDisplacementUniformBufferRef::CreateUniformBufferImmediate(Parameters, UniformBuffer_SingleFrame); SetRenderTarget(RHICmdList, TextureRenderTarget->GetRenderTargetTexture(), FTextureRHIRef()); RHICmdList.Clear(true, FLinearColor::Transparent, false, 0.f, false, 0, FIntRect()); // Be sure we're blending right without any alpha influence on Color blending RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); TShaderMapRef QuadVS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); TShaderMapRef GenGradientFoldingPS(GetGlobalShaderMap(GMaxRHIFeatureLevel)); static FGlobalBoundShaderState UpdateDisplacementBoundShaderState; SetGlobalBoundShaderState(RHICmdList, GMaxRHIFeatureLevel, UpdateDisplacementBoundShaderState, GQuadVertexDeclaration.VertexDeclarationRHI, *QuadVS, *GenGradientFoldingPS); 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); }); } ////////////////////////////////////////////////////////////////////////// // 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); } ================================================ FILE: Source/VaOceanPlugin/Public/IVaOceanPlugin.h ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. #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" ); } }; ================================================ FILE: Source/VaOceanPlugin/VaOceanPlugin.Build.cs ================================================ // Copyright 2014 Vladimir Alyamkin. All Rights Reserved. namespace UnrealBuildTool.Rules { public class VaOceanPlugin : ModuleRules { public VaOceanPlugin(TargetInfo Target) { PublicIncludePaths.AddRange( new string[] { // ... add public include paths required here ... } ); PrivateIncludePaths.AddRange( new string[] { "VaOceanPlugin/Private", // ... add other private include paths required here ... } ); PublicDependencyModuleNames.AddRange( new string[] { "Core", "CoreUObject", "Engine", "RenderCore", "ShaderCore", "RHI" // ... add other public dependencies that you statically link with here ... } ); PrivateDependencyModuleNames.AddRange( new string[] { // ... add private dependencies that you statically link with here ... } ); DynamicallyLoadedModuleNames.AddRange( new string[] { // ... add any modules that your module loads dynamically here ... } ); } } } ================================================ FILE: VaOceanPlugin.uplugin ================================================ { "FileVersion" : 3, "FriendlyName" : "VaOcean", "Version" : 7, "VersionName" : "0.5-a1", "CreatedBy" : "Vladimir Alyamkin", "CreatedByURL" : "http://alyamkin.com", "EngineVersion" : "4.12.0", "Description" : "Ocean surface simulation plugin", "Category" : "Environment", "EnabledByDefault" : false, "CanContainContent" : true, "Modules" : [ { "Name" : "VaOceanPlugin", "Type" : "Runtime", "LoadingPhase" : "PostConfigInit" } ] }