futurity75 / ToonShader

cartoon plugins for unreal engine

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

卡通材质

最终效果

使用方式

在材质编辑器中添加Toon Shading节点,ShadingModel即切换为SHADINGMODELID_STYLIZED_SHADOW
注意:添加节点后不再受编辑器中的ShadingModel变量控制

How to use

Add Toon Shading node in material editor, and ShadingModel property will be replaced as toon shading model.

插件添加ShadingModel大致流程

注意该教程在引擎版本4.26时作成

  • 继承UMaterialExpressionCustomOutput
    使用虚幻中UMaterialExpressionCustomOutput类型可在代码编译时插入条件编译宏的特性将自定义的ShadingModelId添加进Shader编译流程
    插件中重写的GetFunctionName返回的GetToonShading在shader对应的宏为NUM_MATERIAL_OUTPUTS_GETTOONSHADING

  • 进入引擎Engine/Shader目录修改和添加Shader文件

  • 修改ShadingCommon.ush中添加新的ShadingModel宏定义

    • 添加宏SHADINGMODELID_STYLIZED_SHADOW,且修改SHADINGMODELID_NUM的数量(注意当前最多支持16个)

      #define SHADINGMODELID_STYLIZED_SHADOW        12
      #define SHADINGMODELID_NUM                    13
    • 修改GetShadingModelColor函数,新增SHADINGMODELID_STYLIZED_SHADOW的调试颜色

  • 修改BasePassCommon.ushDeferredShadingCommon.ush开启自定义GBuffer的CustomData写入

    • BasePassCommon.ush WRITES_CUSTOMDATA_TO_GBUFFER宏,支持SHADINGMODELID_STYLIZED_SHADOW写入

      #define WRITES_CUSTOMDATA_TO_GBUFFER        (USES_GBUFFER && (MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_CLEAR_COAT || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_HAIR || MATERIAL_SHADINGMODEL_CLOTH || MATERIAL_SHADINGMODEL_EYE || SHADINGMODELID_STYLIZED_SHADOW))
    • DeferredShadingCommon.ush 修改HasCustomGBufferData函数,支持SHADINGMODELID_STYLIZED_SHADOW写入

      bool HasCustomGBufferData(int ShadingModelID)
      {
        return ShadingModelID == SHADINGMODELID_SUBSURFACE
          || ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN
          || ShadingModelID == SHADINGMODELID_CLEAR_COAT
          || ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE
          || ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE
          || ShadingModelID == SHADINGMODELID_HAIR
          || ShadingModelID == SHADINGMODELID_CLOTH
          || ShadingModelID == SHADINGMODELID_EYE
          || ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW;
      }
  • 修改BasePassPixelShader.usf中ShadingModel的初始化

    #if NUM_MATERIAL_OUTPUTS_GETTOONSHADING
        uint ShadingModel = SHADINGMODELID_STYLIZED_SHADOW;
    #else
        uint ShadingModel = GetMaterialShadingModel(PixelMaterialInputs);
    #endif
  • 修改ShadingModelsMaterial.ush将额外的数据写入GBuffer的CustomData

    // 支持 MaterialExpressionCustomOutput 形式
    #if NUM_MATERIAL_OUTPUTS_GETTOONSHADING
        else if (ShadingModel == SHADINGMODELID_STYLIZED_SHADOW)
        {
            GBuffer.CustomData.x = saturate( GetToonShading0(MaterialParameters) );  // SpecularRange
            GBuffer.CustomData.y = saturate( GetToonShading1(MaterialParameters) );  // Offset
        }
    // 支持 ShadingModelId 形式
    #elif SHADINGMODELID_STYLIZED_SHADOW
        else if (ShadingModel == SHADINGMODELID_STYLIZED_SHADOW)
        {
            GBuffer.CustomData.x = saturate( GetMaterialCustomData0(MaterialParameters) );  // SpecularRange
            GBuffer.CustomData.y = saturate( GetMaterialCustomData1(MaterialParameters) );  // Offset
        }
    #endif
  • 修改ShadingModels.ush支持添加的ShadingModel的自定义BxDF函数
    IntegrateBxDF函数中添加SHADINGMODELID_STYLIZED_SHADOW的case

    case SHADINGMODELID_STYLIZED_SHADOW:
       return StylizedShadowBxDF(GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow);
  • 修改DeferredLightingCommon.ush自定义光照
    修改GetDynamicLightingSplit函数,通过GBuffer.ShadingModelID判断SHADINGMODELID_STYLIZED_SHADOW执行自定义光照逻辑

    // STYLIZEDSHADOW SHADING 
    if (GBuffer.ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW)
    {
      float3 Attenuation = 1;
    
      float offset = GBuffer.CustomData.y;
      float TerminatorRange = saturate(GBuffer.Roughness - 0.5);
      
      offset = offset * 2 - 1;
      
      BRANCH
      if (offset >= 1)
      {
        Attenuation = 1;
      }
      else
      {
        float NoL = (dot(N, L) + 1) / 2;
        float NoLOffset = saturate(NoL + offset);
        float LightAttenuationOffset = saturate( Shadow.SurfaceShadow + offset);
        float ToonSurfaceShadow = smoothstep(0.5 - TerminatorRange, 0.5 + TerminatorRange, LightAttenuationOffset);
    
        Attenuation = smoothstep(0.5 - TerminatorRange, 0.5 + TerminatorRange, NoLOffset) * ToonSurfaceShadow;
      }
    
      LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow * Attenuation * 0.25, > bNeedsSeparateSubsurfaceLightAccumulation);
    }
    else
    {
      LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
    }

无法支持From Material Expression的原因

FMaterialShadingModelField::AddShadingModel 存在check(InShadingModel < MSM_NUM)检查导致没法Hack,导致新增的ShadingModel数量肯定超过MSM_NUM,引发断言导致编辑器crash

插件支持ShadingModel的方式

  • ToonShader模块
    该模块实现了UMaterialExpressionCustomOutput的子类,为运行时模块

  • ToonShaderBootstrap模块
    该模块会在引擎启动Shader编译前加载,用以向虚幻引擎Shader文件夹中替换默认Shader文件
    ShadersOverride文件夹下存放了shader文件

    • Default文件用来恢复
    • Override用来做替换,新增的Shader文件都需要存放在Toon目录下

About

cartoon plugins for unreal engine


Languages

Language:C++ 82.5%Language:C# 17.5%