ue4自定义Shader教程(一)
我们可以在ue4材质编辑器实现各种材质效果,不过这些操作最终都是转换成Shader代码运行的。那如果我们要想在ue4中使用自己的Shader要怎么做呢?国外有一个自定义Shader教程,不过那个是用第一人称射击工程来示范的,对于学习和参考来说太复杂, 我这里给出一个简化版的,仅使用完全新建的空白项目来实现,供大家参考。只使用了一个pixel shader, 最终效果如图:首先,我们这里使用到的一个shader文件,名为ShaderExample.usf,就是实现以上一个简单颜色不断变化的效果,这里大家需要先把这个文件放入引擎的Engine\Shaders文件夹下面。ShaderExample.usf文件内容:
/*
把这些内容拷贝出来,新建一个ShaderExample.usf文件拷进去即可。或者在这里使用你自定义的shader代码。
*/
//PIXEL SHADER
//////////////
void MainVertexShader(
float4 InPosition : ATTRIBUTE0,
float2 InUV : ATTRIBUTE1,
out float2 OutUV : TEXCOORD0,
out float4 OutPosition : SV_POSITION
)
{
OutPosition = InPosition;
OutUV = InUV;
}
Texture2D<uint> TextureParameter;
void MainPixelShader(
in float2 uv : TEXCOORD0,
out float4 OutColor : SV_Target0
)
{
//First we need to unpack the uint material and retrieve the underlying R8G8B8A8_UINT values.
float sizeX, sizeY;
TextureParameter.GetDimensions(sizeX, sizeY);
uint packedValue = TextureParameter.Load(int3(sizeX * uv.x, sizeY * uv.y, 0));
uint r = (packedValue & 0x000000FF);
uint g = (packedValue & 0x0000FF00) >> 8;
uint b = (packedValue & 0x00FF0000) >> 16;
uint a = (packedValue & 0xFF000000) >> 24;
//Here we will just blend using the TextureParameterBlendFactor between our simple color change shader and the input from the compute shader
float alpha = length(uv) / length(float2(1, 1));
OutColor = lerp(PSConstant.StartColor, PSVariable.EndColor, alpha) * (1.0 - PSVariable.TextureParameterBlendFactor)
+ float4(r, g, b, a) / 255.0 * PSVariable.TextureParameterBlendFactor;
}
然后大家新建一个空白的蓝图项目,假设名叫ShaderDemo,然后用ue4打开项目,在编辑器里新建一个C++类,选择父类为Actor,因为待会我们要让这个类能拖入场景中。名字随便起,我这里为默认的MyActor。以上步骤编辑器会自动在项目中创建一个名为MyActor的c++类。
然后,需要创建一个自定义Shader的插件模块,而这个插件里,就是封装shader功能的一些代码。 为什么叫"插件"呢,大家可以简单理解为ue4里的插件就是一个功能模块,我们这里使用插件是为了让我们的自定义shader在项目启动之前就加载到引擎里面。
这里我已经把插件的完整文件夹打包,
如何使用很简单,大家下载这个插件包,解压后是一个Plugins的文件夹,把它放在你刚才新建项目的根目录下面,然后来到你的项目目录右键项目文件(*.uproject),点击"Generate visual studio project files":
这样就为我们的插件模块生成了工程文件。
这时候再双击.sln解决方案文件打开项目(会启动vs),可以看到插件模块已经自动被加载到我们的项目文件夹下面,
大家也可以对比一下你的插件目录文件是否正确。
好了,现在名叫ShaderActor的插件加载好了。下面我们就动手写一下测试项目,这就很简单了,因为刚才我们添加了一个类,现在项目里应该有一个MyActor.h和一个MyActor.cpp文件,
这两个文件里面添加一些内容以实现我们的测试功能,这里给出MyActor.h的代码:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "ShaderActor.h"
#include "MyActor.generated.h"
UCLASS()
class DEMO_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ShaderDemo)
FColor PixelShaderTopLeftColor;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ShaderDemo)
UMaterialInterface* MaterialToApply; //我们要给场景中的Actor设置的材质
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ShaderDemo)
UTextureRenderTarget2D* RenderTarget; //用以显示的渲染目标
private:
FShaderActor* PixelShading;
float EndColorBuildup;
float EndColorBuildupDirection;
};
MyActor.cpp代码:
// Fill out your copyright notice in the Description page of Project Settings.
#include "demo.h"
#include "MyActor.h"
#include "ShaderActor.h"
using namespace EMaterialQualityLevel;
// Sets default values
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame.You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
EndColorBuildup = 0;
EndColorBuildupDirection = 1;
PixelShaderTopLeftColor = FColor::Green;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
PixelShading = new FShaderActor(PixelShaderTopLeftColor,
GetWorld()->Scene->GetFeatureLevel());
PixelShading->SetTextureRenderTarget(RenderTarget);
TArray<UStaticMeshComponent*> StaticMeshComponents =
TArray<UStaticMeshComponent*>();
GetComponents<UStaticMeshComponent>(StaticMeshComponents);
UStaticMeshComponent* CurrentStaticMeshPtr = StaticMeshComponents;
CurrentStaticMeshPtr->SetMaterial(0, MaterialToApply); //设置当前Actor的材质为我们给的材质
UMaterialInstanceDynamic* MID =
CurrentStaticMeshPtr->CreateAndSetMaterialInstanceDynamic(0); //根据mesh创建一个动态材质实例
UTexture* CastedRenderTarget = Cast<UTexture>(RenderTarget); //将我们的渲染目标
MID->SetTextureParameterValue("InputTexture", CastedRenderTarget); //设置给刚才创建的动态材质(以在场景中显示shader效果)
}
// Called every frame
void AMyActor::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
if (PixelShading) {
EndColorBuildup = FMath::Clamp(EndColorBuildup + DeltaTime *
EndColorBuildupDirection, 0.0f, 1.0f);
if (EndColorBuildup >= 1.0 || EndColorBuildup <= 0) {
EndColorBuildupDirection *= -1;
}
PixelShading->ExecutePixelShader(FColor(EndColorBuildup * 255, 0, 0, 255), DeltaTime);
}
}
仅有一些部分修改,大部分都是初始代码。然后生成一下项目(vs中右键工程,生成),生成成功之后回到ue4编辑器中,创建一个蓝图类testShaderActor继承刚才的c++类MyActor,再创建一个渲染目标newTextureRender,一个材质newMat,
打开材质newMat,给其添加一个纹理输入参数节点,改名为"InputTexture"(代码中用到);
双击蓝图类testShaderActor,会出现两个在c++代码中定义的属性,将这两个属性分别设置为我们刚才创建的材质和渲染目标,
OK,设置完成,现在把这个材质应用到刚才创建的蓝图类,注意要先给蓝图类添加一个staticmesh,你可以随便拉一个立方体或者球体进来,只要是mesh就可以,主要是应用我们的材质。我这里是给了一个平面网格,比较方便观察。
那么把这个蓝图类随便拖几个到场景中,
点击运行,就可以看到shader运行结果了!
本项目仅做一个示范,核心的功能,也就是ue4如何加载我们自己的shader文件这部分内容都在插件的代码中,大家需要仔细阅读一下代码。理解其流程(用源码版的同学可以直接调试跟踪一下流程)之后,可以抛开插件,直接在引擎里创建包装shader的类,完全仿照插件中的写法来实现相同的功能。下期教程我就讲一下如何在引擎中直接创建和运行我们的自定义shader。
当然,做了这么多,大家可能会问有什么用?用材质不行吗,当然,材质编辑器是ue4方便普通用户而苦心创造的一个庞大复杂的系统,它的思想是将编写shader这层操作改变为在材质编辑器中拖动各种节点来实现,其最终还是要经过引擎编译成shader代码,这其中又有一层模板编译机制,ue4引擎根据一些参数对每一份材质会生成多个shader,最终绑定到渲染管线进行渲染。如果你不懂这其中的流程,那么就会受限于材质编辑器提供的功能,若想实现一些其他的效果如材质编辑器做不到的功能就无能为力了。所以,理解其机制是我们的第一个目的,其次,便于我们脱离材质系统,来实现一些自己想要的功能。
时间关系,先到这里。给大家一个思考的空间。
材质编辑器唯一的弱点就是不支持函数了。。。受楼主指导准备写一个SSS的透射试试(滑稽) 学习学习 学习学习,感谢分享。 什么时候出续集滑稽 谢谢分享! 这就很叼了 初学,有点点不太明白,用函数做吗 多谢分享~~~ THK!!!!!!!!!