Unreal Engine 3自定义Post Process Effect

         本文成于学习独行剑侠的一篇文章《Unreal Engine Shader编程基础》的过程中遇到的问题以及自己尝试的结果。

          首先,要新建一个usf格式的文件,放到引擎根目录/Engine/Shaders目录中。这个是UE3中使用的Shader文件格式(大概就是unreal shader file的意思: ),其实也就是文本文件。我们写个最简单的Pixel Shader:
 1: // TestShader.usf
 2:  
 3: float4 MainPS(float2 InUV : TEXCOORD0) : COLOR0
 4: {
 5:     return float4(InUV, 0, 1.0);
 6: }
      
       Shader代码写好后就要求我们按照Unreal的规则来创建对应的.cpp和/或.uc源文件,我们一步步来。
        在Engine\Src项目目录下新建一个UnTestPixelShaderEffect.cpp文件,用来映射我们的TestPixelShader.usf。
 1: IMPLEMENT_CLASS(UTestEffect);
 2:  
 3: class FTestPixelShader : public FGlobalShader
 4: {
 5:     DECLARE_SHADER_TYPE(FTestPixelShader, Global);
 6: public:
 7:     static UBOOL ShouldCache(EShaderPlatform Platform)
 8:     {
 9:         return TRUE;
 10:     }
 11:  
 12:     static void ModifyCompilationEnvironment(EShaderPlatform Platform, 
 13:                                              FShaderCompilerEnvironment& OutEnvironment)
 14:     {
 15:     }
 16:  
 17:     FTestPixelShader()
 18:     {
 19:     }
 20:  
 21:     FTestPixelShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
 22:     : FGlobalShader(Initializer)
 23:     {
 24:     }
 25: };
 26:  
 27: IMPLEMENT_SHADER_TYPE(, FTestPixelShader, TEXT("TestPixelShader"), TEXT("MainPS"), SF_Pixel, 0, 0);

     

     DECLARE_SHADER_TYPE和IMPLEMENT_SHADER_TYPE宏分别用来声明和定义Shader。这是Shader不带参数的最简单的情况,如果Shader带参数还要在类中声明相应的FShaderParameter/FShaderResourceParameter参数。FGlobalShader还有一个重载函数virtual UBOOL Serialize(FArchive& Ar)用于序列化,我们这里没有用到所以不需要重写它。

      在原文这里就要添加相应的绘制代码到引擎源文件中,出于简单测试目的我不是很想改动底层文件,于是我想能不能利用UE3的PostProcess来达到同样的目的。经过一番探索有了理想的结果。

      要在UE3中使用Post Process首先要创建PostProcessChain,然后将相应的Post Process Effect连接到PostProcessChain。这里就要涉及到自定义Post Process Effect如何实现了。一个Post Process Effect的实现由三部分组成,.cpp文件, .uc文件以及.usf文件,其中usf和cpp文件我们在文章开头已经准备好。经过对UE3自带Post Process Effect的参考,需要创建TestEffect.uc文件以及修改UnTestPixelShaderEffect.cpp文件。

      在Engine/Src/Classes/PostProcess/项目目录创建TestEffect.uc。

 1: class TestEffect extends PostProcessEffect
 2:     native;
 3:  
 4: cpptext
 5: {
 6:     // UPostProcessEffect interface
 7:     /**
 8:  * Creates a proxy to represent the render info for a post process effect
 9:  * @param WorldSettings - The world's post process settings for the view.
 10:  * @return The proxy object.
 11:  */
 12:     virtual class FPostProcessSceneProxy* CreateSceneProxy(const FPostProcessSettings* WorldSettings);
 13:  
 14:     /**
 15:  * @param View - current view
 16:  * @return TRUE if the effect should be rendered
 17:  */
 18:     virtual UBOOL IsShown(const FSceneView* View) const;
 19:  
 20:     /**
 21:  * Called after this instance has been serialized. UberPostProcessEffect should only
 22:  * ever exists in the SDPG_PostProcess scene
 23:  */
 24:     virtual void PostLoad();
 25: }

   

      然后修改UnTestPixelShader.cpp,在开头的代码之后添加如下代码:

 1: class FTestEffectPostProcessSceneProxy : public FPostProcessSceneProxy
 2: {
 3: public:
 4:     /** 
 5:  * Initialization constructor. 
 6:  * @param InEffect - DOF post process effect to mirror in this proxy
 7:  */
 8:     FTestEffectPostProcessSceneProxy(const UTestEffect* InEffect,
 9:                                      const FPostProcessSettings* WorldSettings);
 10:  
 11:     /**
 12:  * Render the post process effect
 13:  * Called by the rendering thread during scene rendering
 14:  * @param InDepthPriorityGroup - scene DPG currently being rendered
 15:  * @param View - current view
 16:  * @param CanvasTransform - same canvas transform used to render the scene
 17:  * @param LDRInfo - helper information about SceneColorLDR
 18:  * @return TRUE if anything was rendered
 19:  */
 20:     UBOOL Render(const FScene* Scene, UINT InDepthPriorityGroup,FViewInfo& View,
 21:                  const FMatrix& CanvasTransform,FSceneColorLDRInfo& LDRInfo);
 22: };
 23:  
 24: FTestEffectPostProcessSceneProxy
 25: ::FTestEffectPostProcessSceneProxy(const UTestEffect* InEffect,const FPostProcessSettings* WorldSettings)
 26: : FPostProcessSceneProxy(InEffect)
 27: {
 28: }
 29:  
 30: UBOOL FTestEffectPostProcessSceneProxy::Render(const FScene* Scene, UINT InDepthPriorityGroup,FViewInfo& View,
 31:                                                const FMatrix& CanvasTransform,FSceneColorLDRInfo& LDRInfo)
 32: {
 33:     RHISetRenderTarget(GSceneRenderTargets.GetSceneColorSurface(), FSurfaceRHIRef());
 34:  
 35:     GSceneRenderTargets.BeginRenderingSceneColor();
 36:  
 37:     TShaderMapRef<FScreenVertexShader>  TestVertexShader( GetGlobalShaderMap() );
 38:     TShaderMapRef<FTestPixelShader>     TestPixelShader( GetGlobalShaderMap() );
 39:  
 40:     RHISetViewport( 0, 0, 0.0f, View.RenderTargetX + View.RenderTargetSizeX, 
 41:                                 View.RenderTargetY + View.RenderTargetSizeY, 1.0f );
 42:     RHISetViewParameters(View);
 43:  
 44:     static FGlobalBoundShaderState TestBoundState;
 45:     SetGlobalBoundShaderState( 
 46:         TestBoundState,
 47:         GFilterVertexDeclaration.VertexDeclarationRHI,
 48:         *TestVertexShader,
 49:         *TestPixelShader,
 50:         sizeof(sizeof(FFilterVertex))
 51:         );
 52:  
 53:     DrawDenormalizedQuad(
 54:         View.RenderTargetX, View.RenderTargetY,
 55:         View.RenderTargetSizeX, View.RenderTargetSizeY,
 56:         View.RenderTargetX, View.RenderTargetY,
 57:         View.RenderTargetSizeX, View.RenderTargetSizeY,
 58:         View.RenderTargetSizeX, View.RenderTargetSizeY,
 59:         GSceneRenderTargets.GetBufferSizeX(), GSceneRenderTargets.GetBufferSizeY()
 60:         );
 61:  
 62:     GSceneRenderTargets.FinishRenderingSceneColor();
 63:  
 64:     return TRUE;
 65: }
 66:  
 67: FPostProcessSceneProxy* UTestEffect::CreateSceneProxy(const FPostProcessSettings* WorldSettings)
 68: {
 69:     return new FTestEffectPostProcessSceneProxy(this, WorldSettings);
 70: }
 71:  
 72: UBOOL UTestEffect::IsShown(const FSceneView* View) const
 73: {
 74:     return Super::IsShown(View);
 75: }
 76:  
 77: void UTestEffect::PostLoad()
 78: {
 79:     Super::PostLoad();
 80: }

    

     完成之后先运行一遍Editor,引擎会解析TestEffect.uc然后在EngineClasses.h头文件中添加对应的native类定义。之后完整编译整个项目。重新打开Editor,新建一个PostProcessChain,命名为TestEffect,双击打开,在Post Process编辑器中右键可以看到菜单底部有我们自定义的TestEffect,将它连接到默认的SceneRenderTarget上,保存PostProcessChain,然后选中它。

Unreal Engine 3自定义Post Process Effect_第1张图片

 

     打开任意一张地图,进入View->World Properties菜单项,点击World Post Process Chain栏旁的箭头按钮赋予我们选中的TestEffect,如果一切正常,结果应该类似这样:

Unreal Engine 3自定义Post Process Effect_第2张图片

      至此一个简单的自定义Post Process Effect就完成了^_^。

 

     更新:带参数的Shader

     昨天试验过不带参数的简单Shader之后,今天尝试了添加Shader参数的支持。

     首先我们在TestPixelShader.usf文件中添加一个变量:

 1: // TestShader.usf
 2: float4 ShadingColor;
 3:  
 4: float4 MainPS(float2 InUV : TEXCOORD0) : COLOR0
 5: {
 6:     return float4(InUV, 0, 1.0) + ShadingColor;
 7: }

 

     简单的进行颜色叠加。

     然后我们在TestEffect.uc文件中添加一个编辑器变量以便我们在运行时更改Shader参数进行测试。

 1: var() Vector ShadingColor;

 

     在defaultproperties块中给一个默认值:

 1: defaultproperties
 2: {
 3:     ShadingColor = (X=0.0f, Y=0.0f, Z=0.0f)
 4: }

 

     保存文件,在这时运行一下Editor,让EngineClass中UTestShaderEffect的声明得到更新。现在来修改UnTestPixelShader.cpp文件。

     先在FTestPixelShader类中声明一个FShaderParameter变量:

 1: FShaderParameter ShadingColorParameters;

 

     因为这一次我们用到了Shader参数,所以要重写FGlobalShader基类的Serialize(FArchive& Ar)方法:

 1: virtual UBOOL Serialize(FArchive& Ar)
 2: {
 3:     UBOOL bShaderHasOutdatedParameters = FShader::Serialize(Ar);
 4:     Ar << ShadingColorParameters;
 5:  
 6:     return bShaderHasOutdatedParameters;
 7: }

 

     然后在构造函数中绑定我们的Shader参数:

 1: FTestPixelShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
 2: : FGlobalShader(Initializer)
 3: {
 4:     ShadingColorParameters.Bind(Initializer.ParameterMap, TEXT("ShadingColor"), TRUE);
 5: }

 

     之后,在FTestEffectPostProcessSceneProxy类中定义一个用来传入Shader的变量FVector ShadingColor,在FTestEffectPostProcessSceneProxy构造函数中接收来自Post Process编辑器中用户指定的ShadingColor值。

 1: FTestEffectPostProcessSceneProx
 2: y::FTestEffectPostProcessSceneProxy(const UTestEffect* InEffect,const FPostProcessSettings* WorldSettings)
 3: : FPostProcessSceneProxy(InEffect)
 4: {
 5:     ShadingColor = InEffect->ShadingColor;
 6: }

 

     最后,我们需要将接收到的Shader参数实际应用到Shader中,在FTestEffectPostProcessSceneProxy::Render函数的SetGlobalBoundShaderState后面添加以下代码:

 1: SetPixelShaderValues(
 2:     TestPixelShader->GetPixelShader(),
 3:     TestPixelShader->ShadingColorParameters,
 4:     &ShadingColor,
 5:     1);

 

     这样,编辑器中的ShadingColor参数就和Shader文件中的ShadingColor变量建立了联系,现在启动Editor来进行测试。

     加载任意一张地图,打开内容浏览器找到上次创建TestEffect PostProcessChain,双击打开,在Post Process编辑器中选中它,我们可以发现编辑中已经可以看到我们定义的Shading Color变量了。

Unreal Engine 3自定义Post Process Effect_第3张图片

 

     随便改一下试试,比如把X改成1.0,然后将将PostProcessChain赋予场景观察结果:

Unreal Engine 3自定义Post Process Effect_第4张图片B

     Bingo!运行正常~

 

你可能感兴趣的:(process)