我的专栏目录:
小IVan:专题概述及目录
简介:
海洋这个要素在游戏里占比越来越多,开放世界的游戏要求角色能上天入地下水。目前游戏里做海洋的方法就那几种。(1)预烘焙法(2)Gerstner wave(3)FFT海洋。预烘焙法可以是实现烘焙好DisplacementMap或者是FFT的运算结果。Gerstner wave可以在GPU或者CPU上算。FFT的话就是拿海洋频率模型算出Displacement。
【1】基础环境搭建
在开始研究之前我们需要先搭建起我们的环境。我选择在ComputeShader种完成各种计算,然后在顶点着色器种直接Sample前面的ComputeShader的波形计算结果。不要把波形的计算塞到VertexShader里。把波形计算独立出来还有个好处就是我能把波形的结果储存起来拿给其它效果使用,比如制作浮力部分的时候我们就需要知道海面波形的信息,如果塞VertexShader里就拿不到这些信息了。
搭建ComputeShader的方法前面我的文章有提到,这里我就直接贴代码了。使用的引擎版本是4.21.0,如果引擎更新了新版本可能代码有一点区别。FFT部分我会给出4.22的代码。
如果不是在Unreal中实现或者不想做这么复杂,可以直接跳过这部分。


SDHOcean.build.cs
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class SDHOcean : ModuleRules
{
public SDHOcean(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"RHI",
"Engine",
"RenderCore",
"ShaderCore",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"UnrealEd",
"Projects",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
}
}
SDHOcean.h
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/IPluginManager.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
class FSDHOceanModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
SDHOcean.cpp
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "SDHOcean.h"
#define LOCTEXT_NAMESPACE "FSDHOceanModule"
void FSDHOceanModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("SDHOcean"))->GetBaseDir(), TEXT("Shaders"));
AddShaderSourceDirectoryMapping(TEXT("/Plugin/SDHOcean"), PluginShaderDir);
}
void FSDHOceanModule::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(FSDHOceanModule, SDHOcean)
Ocean.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Runtime/Engine/Classes/Components/ActorComponent.h"
#include "Engine/Classes/Engine/TextureRenderTarget2D.h"
#include "Ocean.generated.h"
typedef TRefCountPtr<class FRHITexture2D> FTexture2DRHIRef;
typedef TRefCountPtr<class FRHIUnorderedAccessView> FUnorderedAccessViewRHIRef;
typedef TRefCountPtr<class FRHIStructuredBuffer> FStructuredBufferRHIRef;
class FRHITexture;
class FRHIUnorderedAccessView;
class FRHICommandListImmediate;
USTRUCT(BlueprintType)
struct FOceanBasicStructData_GameThread
{
GENERATED_USTRUCT_BODY()
FOceanBasicStructData_GameThread(){}
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FVector4 OceanTime_GameThread;
};
UCLASS(hidecategories = (Object, LOD, Physics, Collision), editinlinenew, meta = (BlueprintSpawnableComponent), ClassGroup = Rendering, DisplayName = "OceanRenderComp")
class SDHOCEAN_API UOceanRenderComponent : public UActorComponent
{
GENERATED_BODY()
public:
UOceanRenderComponent(const FObjectInitializer& ObjectInitializer);
//~ Begin UActorComponent Interface.
virtual void OnRegister() override;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OceanComponent")
UTextureRenderTarget2D* OutputRenderTarget2D;
//UniformData for Ocean render
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OceanComponent")
FOceanBasicStructData_GameThread OceanUniformDataBuffer;
int32 TargetSize;
ETextureRenderTargetFormat RenderTargetFormat;
private:
//Render ocean render thread
void OceanCalculating_GameThread();
void OceanCalculating_RenderThread
(
FRHICommandListImmediate& RHICmdList,
ERHIFeatureLevel::Type FeatureLevel,
FRHITexture* OutputRenderTarget,
int32 SurfaceSize,
const FOceanBasicStructData_GameThread& OceanUniformData
);
FTexture2DRHIRef OutputTexture;
FUnorderedAccessViewRHIRef OutputTextureUAV;
};
Ocean.cpp
#include "SDHOcean/Public/Ocean.h"
#include "ShaderCore/Public/GlobalShader.h"
#include "Classes/Engine/World.h"
#incl