Search Unity

TextMesh Pro How to make a Custom TMP Shader and Inspector UI?

Discussion in 'UGUI & TextMesh Pro' started by april_4_short, Jul 21, 2021.

  1. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489
    Am struggling to make a custom TMP Shader from the default TMP_SDF.shader with a correspondingly custom TMP_SDFShaderGUI

    TMP_SDFShaderGUI inherits from TMP_BaseShaderGUI, which inherits from ShaderGUI

    I thought it would be as simple as making a new TMP_SDFShaderGUI_mine, and making a few modifications.

    This doesn't work, all the formatting in the Unity Material Inspector is lost.

    So then I made a copy of TMP_BaseShaderGUI, and inherited from that... not at all modded, just renamed to TMP_BaseShader_mine

    This doesn't work, either - same problem, all the Unity Material Inspector formatting is lost.

    There's something about global packages and the npm and a cache that I don't understand, too.

    What is the exact process, please, for making a custom TMP Shader with an easily customisable TMP_SDFShaderGUI of its own?
     
  2. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Copy code:

    Code (CSharp):
    1. Shader "CustomFont" {
    2.  
    3. Properties {
    4.     _FaceTex            ("Face Texture", 2D) = "white" {}
    5.     _FaceUVSpeedX        ("Face UV Speed X", Range(-5, 5)) = 0.0
    6.     _FaceUVSpeedY        ("Face UV Speed Y", Range(-5, 5)) = 0.0
    7.     _FaceColor            ("Face Color", Color) = (1,1,1,1)
    8.     _FaceDilate            ("Face Dilate", Range(-1,1)) = 0
    9.  
    10.     _OutlineColor        ("Outline Color", Color) = (0,0,0,1)
    11.     _OutlineTex            ("Outline Texture", 2D) = "white" {}
    12.     _OutlineUVSpeedX    ("Outline UV Speed X", Range(-5, 5)) = 0.0
    13.     _OutlineUVSpeedY    ("Outline UV Speed Y", Range(-5, 5)) = 0.0
    14.     _OutlineWidth        ("Outline Thickness", Range(0, 1)) = 0
    15.     _OutlineSoftness    ("Outline Softness", Range(0,1)) = 0
    16.  
    17.     _Bevel                ("Bevel", Range(0,1)) = 0.5
    18.     _BevelOffset        ("Bevel Offset", Range(-0.5,0.5)) = 0
    19.     _BevelWidth            ("Bevel Width", Range(-.5,0.5)) = 0
    20.     _BevelClamp            ("Bevel Clamp", Range(0,1)) = 0
    21.     _BevelRoundness        ("Bevel Roundness", Range(0,1)) = 0
    22.  
    23.     _LightAngle            ("Light Angle", Range(0.0, 6.2831853)) = 3.1416
    24.     _SpecularColor        ("Specular", Color) = (1,1,1,1)
    25.     _SpecularPower        ("Specular", Range(0,4)) = 2.0
    26.     _Reflectivity        ("Reflectivity", Range(5.0,15.0)) = 10
    27.     _Diffuse            ("Diffuse", Range(0,1)) = 0.5
    28.     _Ambient            ("Ambient", Range(1,0)) = 0.5
    29.  
    30.     _BumpMap             ("Normal map", 2D) = "bump" {}
    31.     _BumpOutline        ("Bump Outline", Range(0,1)) = 0
    32.     _BumpFace            ("Bump Face", Range(0,1)) = 0
    33.  
    34.     _ReflectFaceColor    ("Reflection Color", Color) = (0,0,0,1)
    35.     _ReflectOutlineColor("Reflection Color", Color) = (0,0,0,1)
    36.     _Cube                 ("Reflection Cubemap", Cube) = "black" { /* TexGen CubeReflect */ }
    37.     _EnvMatrixRotation    ("Texture Rotation", vector) = (0, 0, 0, 0)
    38.        
    39.  
    40.     _UnderlayColor        ("Border Color", Color) = (0,0,0, 0.5)
    41.     _UnderlayOffsetX    ("Border OffsetX", Range(-1,1)) = 0
    42.     _UnderlayOffsetY    ("Border OffsetY", Range(-1,1)) = 0
    43.     _UnderlayDilate        ("Border Dilate", Range(-1,1)) = 0
    44.     _UnderlaySoftness    ("Border Softness", Range(0,1)) = 0
    45.  
    46.     _GlowColor            ("Color", Color) = (0, 1, 0, 0.5)
    47.     _GlowOffset            ("Offset", Range(-1,1)) = 0
    48.     _GlowInner            ("Inner", Range(0,1)) = 0.05
    49.     _GlowOuter            ("Outer", Range(0,1)) = 0.05
    50.     _GlowPower            ("Falloff", Range(1, 0)) = 0.75
    51.  
    52.     _WeightNormal        ("Weight Normal", float) = 0
    53.     _WeightBold            ("Weight Bold", float) = 0.5
    54.  
    55.     _ShaderFlags        ("Flags", float) = 0
    56.     _ScaleRatioA        ("Scale RatioA", float) = 1
    57.     _ScaleRatioB        ("Scale RatioB", float) = 1
    58.     _ScaleRatioC        ("Scale RatioC", float) = 1
    59.  
    60.     _MainTex            ("Font Atlas", 2D) = "white" {}
    61.     _TextureWidth        ("Texture Width", float) = 512
    62.     _TextureHeight        ("Texture Height", float) = 512
    63.     _GradientScale        ("Gradient Scale", float) = 5.0
    64.     _ScaleX                ("Scale X", float) = 1.0
    65.     _ScaleY                ("Scale Y", float) = 1.0
    66.     _PerspectiveFilter    ("Perspective Correction", Range(0, 1)) = 0.875
    67.     _Sharpness            ("Sharpness", Range(-1,1)) = 0
    68.  
    69.     _VertexOffsetX        ("Vertex OffsetX", float) = 0
    70.     _VertexOffsetY        ("Vertex OffsetY", float) = 0
    71.    
    72.     _MaskCoord            ("Mask Coordinates", vector) = (0, 0, 32767, 32767)
    73.     _ClipRect            ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
    74.     _MaskSoftnessX        ("Mask SoftnessX", float) = 0
    75.     _MaskSoftnessY        ("Mask SoftnessY", float) = 0
    76.  
    77.     _StencilComp        ("Stencil Comparison", Float) = 8
    78.     _Stencil            ("Stencil ID", Float) = 0
    79.     _StencilOp            ("Stencil Operation", Float) = 0
    80.     _StencilWriteMask    ("Stencil Write Mask", Float) = 255
    81.     _StencilReadMask    ("Stencil Read Mask", Float) = 255
    82.  
    83.     _ColorMask            ("Color Mask", Float) = 15
    84. }
    85.  
    86. SubShader {
    87.  
    88.     Tags
    89.     {
    90.         "Queue"="Transparent"
    91.         "IgnoreProjector"="True"
    92.         "RenderType"="Transparent"
    93.     }
    94.  
    95.     Stencil
    96.     {
    97.         Ref [_Stencil]
    98.         Comp [_StencilComp]
    99.         Pass [_StencilOp]
    100.         ReadMask [_StencilReadMask]
    101.         WriteMask [_StencilWriteMask]
    102.     }
    103.  
    104.     Cull [_CullMode]
    105.     ZWrite Off
    106.     Lighting Off
    107.     Fog { Mode Off }
    108.     ZTest [unity_GUIZTestMode]
    109.     Blend One OneMinusSrcAlpha
    110.     ColorMask [_ColorMask]
    111.  
    112.     Pass {
    113.         CGPROGRAM
    114.         #pragma target 3.0
    115.         #pragma vertex VertShader
    116.         #pragma fragment PixShader
    117.         #pragma shader_feature __ BEVEL_ON
    118.         #pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
    119.         #pragma shader_feature __ GLOW_ON
    120.  
    121.         #pragma multi_compile __ UNITY_UI_CLIP_RECT
    122.         #pragma multi_compile __ UNITY_UI_ALPHACLIP
    123.  
    124.         #include "UnityCG.cginc"
    125.         #include "UnityUI.cginc"
    126.         #include "TMPro_Properties.cginc"
    127.         #include "TMPro.cginc"
    128.  
    129.         struct vertex_t {
    130.             UNITY_VERTEX_INPUT_INSTANCE_ID
    131.             float4    position        : POSITION;
    132.             float3    normal            : NORMAL;
    133.             fixed4    color            : COLOR;
    134.             float2    texcoord0        : TEXCOORD0;
    135.             float2    texcoord1        : TEXCOORD1;
    136.         };
    137.  
    138.  
    139.         struct pixel_t {
    140.             UNITY_VERTEX_INPUT_INSTANCE_ID
    141.             UNITY_VERTEX_OUTPUT_STEREO
    142.             float4    position        : SV_POSITION;
    143.             fixed4    color            : COLOR;
    144.             float2    atlas            : TEXCOORD0;        // Atlas
    145.             float4    param            : TEXCOORD1;        // alphaClip, scale, bias, weight
    146.             float4    mask            : TEXCOORD2;        // Position in object space(xy), pixel Size(zw)
    147.             float3    viewDir            : TEXCOORD3;
    148.            
    149.         #if (UNDERLAY_ON || UNDERLAY_INNER)
    150.             float4    texcoord2        : TEXCOORD4;        // u,v, scale, bias
    151.             fixed4    underlayColor    : COLOR1;
    152.         #endif
    153.             float4 textures            : TEXCOORD5;
    154.         };
    155.  
    156.         // Used by Unity internally to handle Texture Tiling and Offset.
    157.         float4 _FaceTex_ST;
    158.         float4 _OutlineTex_ST;
    159.  
    160.         pixel_t VertShader(vertex_t input)
    161.         {
    162.             pixel_t output;
    163.  
    164.             UNITY_INITIALIZE_OUTPUT(pixel_t, output);
    165.             UNITY_SETUP_INSTANCE_ID(input);
    166.             UNITY_TRANSFER_INSTANCE_ID(input,output);
    167.             UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    168.  
    169.             float bold = step(input.texcoord1.y, 0);
    170.  
    171.             float4 vert = input.position;
    172.             vert.x += _VertexOffsetX;
    173.             vert.y += _VertexOffsetY;
    174.  
    175.             float4 vPosition = UnityObjectToClipPos(vert);
    176.  
    177.             float2 pixelSize = vPosition.w;
    178.             pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
    179.             float scale = rsqrt(dot(pixelSize, pixelSize));
    180.             scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
    181.             if (UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
    182.  
    183.             float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
    184.             weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
    185.  
    186.             float bias =(.5 - weight) + (.5 / scale);
    187.  
    188.             float alphaClip = (1.0 - _OutlineWidth * _ScaleRatioA - _OutlineSoftness * _ScaleRatioA);
    189.        
    190.         #if GLOW_ON
    191.             alphaClip = min(alphaClip, 1.0 - _GlowOffset * _ScaleRatioB - _GlowOuter * _ScaleRatioB);
    192.         #endif
    193.  
    194.             alphaClip = alphaClip / 2.0 - ( .5 / scale) - weight;
    195.  
    196.         #if (UNDERLAY_ON || UNDERLAY_INNER)
    197.             float4 underlayColor = _UnderlayColor;
    198.             underlayColor.rgb *= underlayColor.a;
    199.  
    200.             float bScale = scale;
    201.             bScale /= 1 + ((_UnderlaySoftness*_ScaleRatioC) * bScale);
    202.             float bBias = (0.5 - weight) * bScale - 0.5 - ((_UnderlayDilate * _ScaleRatioC) * 0.5 * bScale);
    203.  
    204.             float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
    205.             float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
    206.             float2 bOffset = float2(x, y);
    207.         #endif
    208.  
    209.             // Generate UV for the Masking Texture
    210.             float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
    211.             float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
    212.  
    213.             // Support for texture tiling and offset
    214.             float2 textureUV = UnpackUV(input.texcoord1.x);
    215.             float2 faceUV = TRANSFORM_TEX(textureUV, _FaceTex);
    216.             float2 outlineUV = TRANSFORM_TEX(textureUV, _OutlineTex);
    217.  
    218.            
    219.             output.position = vPosition;
    220.             output.color = input.color;
    221.             output.atlas =    input.texcoord0;
    222.             output.param =    float4(alphaClip, scale, bias, weight);
    223.             output.mask = half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
    224.             output.viewDir =    mul((float3x3)_EnvMatrix, _WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, vert).xyz);
    225.             #if (UNDERLAY_ON || UNDERLAY_INNER)
    226.             output.texcoord2 = float4(input.texcoord0 + bOffset, bScale, bBias);
    227.             output.underlayColor =    underlayColor;
    228.             #endif
    229.             output.textures = float4(faceUV, outlineUV);
    230.    
    231.             return output;
    232.         }
    233.  
    234.         fixed4 PixShader(pixel_t input) : SV_Target
    235.         {
    236.             UNITY_SETUP_INSTANCE_ID(input);
    237.  
    238.             float c = tex2D(_MainTex, input.atlas).a;
    239.        
    240.             #ifndef UNDERLAY_ON
    241.                 clip(c - input.param.x);
    242.             #endif
    243.  
    244.             float    scale    = input.param.y;
    245.             float    bias    = input.param.z;
    246.             float    weight    = input.param.w;
    247.             float    sd = (bias - c) * scale;
    248.  
    249.             float4 rgba = float4(1.0, 0.0, 0.0, 1.0);
    250.            
    251.             return rgba;
    252.         }
    253.  
    254.         ENDCG
    255.     }
    256. }
    257.  
    258. Fallback "TextMeshPro/Mobile/Distance Field"
    259. CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
    260. }
    261.  
    and save as CustomFont.shader, in directory Assets\TextMesh Pro\Resources\Shaders. Then select shader CustomFont in material. You will see hardcoded red color (line 249), to change color, edit variable "rgba".

    upload_2021-7-21_21-41-20.png
     
    april_4_short likes this.
  3. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489
    @Stephan_B Please, good sir... can you help me get through the second part?

    Mr Zaworski has very kindly gotten me over hump number one (the custom shader), but I'm still stuck trying to make a custom editor for the material out of the TMP_SDFShaderGUI.
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Lost as in when you close and re-open Unity? If so, this is simply due to your changed being local where they get overwritten since packages are supposed to be immutable. The solution is to make the changes in the Global Package Cache in ".../packages/packages.unity.com/com.unity.textmeshpro@..." where ... is the version of the TMP package you are using.

    Then you should be able to simply create your own TMP_SDFShaderGUI_mine as you described above. You will also need to make sure that your shader includes
    CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI_mine"
     
  5. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489

    This is exactly what I've done, multiple times.

    And I've tried variations of this, across different folders and possible caches and different names and everything I can imagine to try.

    However, every single time, it either doesn't work, or all the Inspector formatting is lost (most things are in the Inspector, but everything is right justified, there's none of the lovely layout to the inspector (material) that you have created.

    And some of the information from the parent class seems to not be coming down into the inspector.

    Also, when changing the name of the class in Rider, to TMP_SDFShaderGUI_mine, Rider loses all code formatting, seemingly unable to believe that the file is C#.

    I've tried multiple restarts, of everything, too.
     
  6. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489
    There is also an issue, sometimes (not always) in the console, saying this:

    Code (CSharp):
    1. Read only asset Packages/com.unity.textmeshpro/Scripts/Editor/TMP_SDFShaderGUI_mine.cs has no meta file.
    2.  
    This is not consistent. None of the folders its nested in, nor the file itself are flagged as read only by the operating system - full read/write for all folders and the file itself.

    This is a Mac, Unity 2019LTS.
     
  7. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Another alternative is for you to embed the current version of the TMP package in your project. To do so, copy the "com.unity.textmeshpro@2.1.6" from the global cache to the "Project Root/packages/..." folder. Although not necessary, I would rename the package to remove the @2.1.6.

    The above will then allow you to modify the package from within Unity where the changes will be persistent. Note that running with an embedded package will prevent you from upgrading to other versions of the TMP package. That is until you remove the embedded package.
     
  8. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489
    Before attacking the above approach, thought I'd try in 2018.LTS, where I'm learning a bit about shaders, due to its vastly faster and more stable handling of... everything ... // insert rant about everything added by Unity since... etc

    2018, when I do the process of trying to mod the Global cache, gives a slightly more verbose console insight into what might be going wrong:

    Could not create a custom UI for the shader 'TextMeshPro/Distance Field_mine'. The shader has the following: 'CustomEditor = TMPro.EditorUtilities.TMP_SDFShaderGUI_mine'. Does the custom editor specified include its namespace? And does the class either derive from ShaderGUI or MaterialEditor?
    UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr) (at /Users/bokken/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:179)


    In answer to those questions... the files are EXACTLY the same as the originals shipped with the package, they're duplicates, with the only things changed being the file name and class name.

    Does this make any sense? It's all above me. No idea what's going on.
     
  9. april_4_short

    april_4_short

    Joined:
    Jul 19, 2021
    Posts:
    489
    This approach seems to work, somewhat. I imagine it's a pain to update after having gone down this route, and probably almost impossible to easily share shaders and GUI editors for them with others, having done this?

    It also massively slows the editor update after making any script changes.