ue4自定义Shader教程(一)

我们可以在ue4材质编辑器实现各种材质效果,不过这些操作最终都是转换成Shader代码运行的。那如果我们要想在ue4中使用自己的Shader要怎么做呢?国外有一个自定义Shader教程,不过那个是用第一人称射击工程来示范的,对于学习和参考来说太复杂, 我这里给出一个简化版的,仅使用完全新建的空白项目来实现,供大家参考。只使用了一个pixel shader, 最终效果如图:
223731wgnga26a6agaa1g6.png

首先,我们这里使用到的一个shader文件,名为ShaderExample.usf,就是实现以上一个简单颜色不断变化的效果,这里大家需要先把这个文件放入引擎的Engine\Shaders文件夹下面。ShaderExample.usf文件内容:

[mw_shl_code=c,true]/*
把这些内容拷贝出来,新建一个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;
}[/mw_shl_code]

然后大家新建一个空白的蓝图项目,假设名叫ShaderDemo,然后用ue4打开项目,在编辑器里新建一个C++类,选择父类为Actor,因为待会我们要让这个类能拖入场景中。名字随便起,我这里为默认的MyActor。以上步骤编辑器会自动在项目中创建一个名为MyActor的c++类。

然后,需要创建一个自定义Shader的插件模块,而这个插件里,就是封装shader功能的一些代码。 为什么叫"插件"呢,大家可以简单理解为ue4里的插件就是一个功能模块,我们这里使用插件是为了让我们的自定义shader在项目启动之前就加载到引擎里面。

这里我已经把插件的完整文件夹打包,
Plugins.rar (6.6 KB)

( 2017-8-3 23:16上传, 下载次数: 159 )



如何使用很简单,大家下载这个插件包,解压后是一个Plugins的文件夹,把它放在你刚才新建项目的根目录下面,然后来到你的项目目录右键项目文件(*.uproject),点击"Generate visual studio project files":
QQ图片20170803231042.png
这样就为我们的插件模块生成了工程文件。

这时候再双击.sln解决方案文件打开项目(会启动vs),可以看到插件模块已经自动被加载到我们的项目文件夹下面,

224940s8xg21o3t103gzsg.png
大家也可以对比一下你的插件目录文件是否正确。

好了,现在名叫ShaderActor的插件加载好了。下面我们就动手写一下测试项目,这就很简单了,因为刚才我们添加了一个类,现在项目里应该有一个MyActor.h和一个MyActor.cpp文件,
225945yz5e6yocbugwzvl6.png
这两个文件里面添加一些内容以实现我们的测试功能,这里给出MyActor.h的代码:
[mw_shl_code=cpp,true]// 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;
};[/mw_shl_code]

MyActor.cpp代码:
[mw_shl_code=cpp,true]// 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[0];
        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);
        }

}[/mw_shl_code]

仅有一些部分修改,大部分都是初始代码。然后生成一下项目(vs中右键工程,生成),生成成功之后回到ue4编辑器中,创建一个蓝图类testShaderActor继承刚才的c++类MyActor,再创建一个渲染目标newTextureRender,一个材质newMat,
231104oo9zri9ocwlcrltu.png

打开材质newMat,给其添加一个纹理输入参数节点,改名为"InputTexture"(代码中用到);

双击蓝图类testShaderActor,会出现两个在c++代码中定义的属性,将这两个属性分别设置为我们刚才创建的材质和渲染目标,
232633mdaa1x7ga53xgzw7.png

OK,设置完成,现在把这个材质应用到刚才创建的蓝图类,注意要先给蓝图类添加一个staticmesh,你可以随便拉一个立方体或者球体进来,只要是mesh就可以,主要是应用我们的材质。我这里是给了一个平面网格,比较方便观察。

那么把这个蓝图类随便拖几个到场景中,
233146skvm5g2dsoods9w2.png

点击运行,就可以看到shader运行结果了!
233400kf05vjqv5gl8flgq.png

本项目仅做一个示范,核心的功能,也就是ue4如何加载我们自己的shader文件这部分内容都在插件的代码中,大家需要仔细阅读一下代码。理解其流程(用源码版的同学可以直接调试跟踪一下流程)之后,可以抛开插件,直接在引擎里创建包装shader的类,完全仿照插件中的写法来实现相同的功能。下期教程我就讲一下如何在引擎中直接创建和运行我们的自定义shader。



当然,做了这么多,大家可能会问有什么用?用材质不行吗,当然,材质编辑器是ue4方便普通用户而苦心创造的一个庞大复杂的系统,它的思想是将编写shader这层操作改变为在材质编辑器中拖动各种节点来实现,其最终还是要经过引擎编译成shader代码,这其中又有一层模板编译机制,ue4引擎根据一些参数对每一份材质会生成多个shader,最终绑定到渲染管线进行渲染。如果你不懂这其中的流程,那么就会受限于材质编辑器提供的功能,若想实现一些其他的效果如材质编辑器做不到的功能就无能为力了。所以,理解其机制是我们的第一个目的,其次,便于我们脱离材质系统,来实现一些自己想要的功能。


时间关系,先到这里。给大家一个思考的空间。
回复

全部回复

材质编辑器唯一的弱点就是不支持函数了。。。受楼主指导准备写一个SSS的透射试试(滑稽)
回复
学习学习
回复
学习学习,感谢分享。
回复
什么时候出续集滑稽
回复
我也说一句
1234.. 5下一页
数据加载中,请稍后...
导航 顶部
头像游客

您还未登录,立即登录?

确定 取消