zoukankan      html  css  js  c++  java
  • 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,然后选中它。

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

          至此一个简单的自定义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变量了。

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

    B

         Bingo!运行正常~

  • 相关阅读:
    使用SpringBoot校验客户端传来的数据
    在RestController中获取各种信息的方法
    SpringBoot的文件上传&下载
    Maximum upload size exceede上传文件大小超出解决
    在SpringBoot程序中记录日志
    使用IDEA配置SpringBoot热部署无效解决
    RESTful API是什么?
    IDEA如何配置jdk路径
    使用IDEA集成Spring框架时右下角警戒
    Spring框架各模块功能介绍
  • 原文地址:https://www.cnblogs.com/eros/p/2469077.html
Copyright © 2011-2022 走看看