【问题标题】:How to map letters on mesh with shader?如何使用着色器在网格上映射字母?
【发布时间】:2019-09-22 21:32:42
【问题描述】:

我正在尝试创建一个允许在网格上映射文本的着色器。如何水平对齐每个字符?目前,它们都相互重叠。

目前看起来像这样:

使用的纹理:

Shader "Unlit/MapText"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _FontTex("FontTexture", 2D) = "white" {}
        _Color("Color", Color) = (1,1,1,1)
    }
        SubShader
        {
            Tags { "RenderType" = "Opaque" }

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

                uniform int _CharacterCount;
                uniform float4 _Characters[3];

                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                    float2 uv1 : TEXCOORD1;
                };

                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float2 uv1 : TEXCOORD1;
                    float4 vertex : SV_POSITION;
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;

                sampler2D _FontTex;
                float4 _FontTex_ST;

                float4 _Color;

                v2f vert(appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    o.uv1 = TRANSFORM_TEX(v.uv1, _FontTex);
                    return o;
                }

                fixed4 frag(v2f i) : SV_Target
                {
                    fixed4 col;

                    _Characters[0] = 0;
                    _Characters[1] = 1;
                    _Characters[2] = 2;

                    for (uint k = 0; k < _Characters.Length; k++)
                    {
                        float row = (k % 1024);
                        float column = (k / 1024);
                        float2 character = (i.uv1 + float2(row, column)) * 0.33;
                        _Characters[k] = tex2D(_FontTex, character);
                    }

                    col = (_Characters[0] + _Characters[1] + _Characters[2]) * _Color;
                    return col;
            }
            ENDCG
        }
    }
}

【问题讨论】:

  • 您不使用 TextMeshPro 的任何原因?
  • 是的,我稍后需要在蒙皮网格上使用它,而普通网格上的 afaik TMPro 不会弯曲。会吗?
  • this 之类的内容对您有帮助吗?
  • @derHugo 这个方法很有趣,但问题是我需要在场景中的大约 10-15 个对象上有动态文本,因此我需要很多相机、渲染纹理和图层,这需要在移动设备上运行。
  • @derHugo 这会是性能问题吗?

标签: unity3d shader fragment-shader vertex-shader shaderlab


【解决方案1】:

解释在 cmets

Shader "Unlit/MapText"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _FontTex("FontTexture", 2D) = "white" {}
        _FontTextColumns("FontTexture Columns", Int) = 3
        _FontTextRows("FontTexture Rows", Int) = 3
        _StringCharacterCount("Length of String", Int) = 3
        _StringOffset("String offset", Vector) = (0.5,0.5,0,0)
        _StringScale("String scale", Vector) = (0.25,0.25,0,0)
        _CharWidth("Character width", Float) = 1.0
        _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _FontTex;
            float4 _FontTex_ST;

            float4 _Color;

            // font texture information
            int _FontTextColumns;
            int _FontTextRows;

            // string length
            int _StringCharacterCount;

            // float array because there's no SetIntArray in c#
            float _String_Chars[512];

            // string placement & scaling
            float4 _StringOffset;
            float4 _StringScale;

            // Character width - combine with StringScale to change character spacing
            float _CharWidth;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // discard pixel if _StringCharacterCount < 1
                clip(_StringCharacterCount - 1);

                fixed4 col;

                // Determine what character in the string this pixel is in
                // And what UV of that character we are in
                float charIndex = 0;
                float2 inCharUV = float2(0,0);

                // Avoid i.uv.x = 1 and indexing charIndex[_StringCharacterCount] 
                i.uv.x = clamp(i.uv.x, 0.0, 0.99999);

                // Scale and offset uv
                i.uv = clamp((i.uv - _StringOffset.xy) / _StringScale.xy + 0.5, 0, 1);

                // Find where in the char to sample
                inCharUV = float2(
                    modf(i.uv.x * _StringCharacterCount, charIndex),
                    i.uv.y);

                // Scale inCharUV.x based on charWidth factor
                inCharUV.x = (inCharUV.x-0.5f)/charWidth + 0.5f;

                // Clamp char uv
                // alternatively you could clip if outside (0,0)-(1,1) rect
                inCharUV = clamp(inCharUV, 0, 1);

                // Get char uv in font texture space
                float fontIndex = _String_Chars[charIndex];
                float fontRow = floor(fontIndex / _FontTextColumns);
                float fontColumn = floor(fontIndex % _FontTextColumns);

                float2 fontUV = float2(
                        (fontColumn + inCharUV.x) / _FontTextColumns,
                        1.0 - (fontRow + 1.0 - inCharUV.y) / _FontTextRows);

                // Sample the font texture at that uv
                col = tex2D(_FontTex, fontUV);

                // Modify by color:
                col = col * _Color;

                return col;
            }
           ENDCG
        }
    }
}

然后在 C# 中,您必须设置字符串数组并告诉它您要绘制的字符串的长度。没有SetIntArraySetFloatArray 有效:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class test : MonoBehaviour
{
    Material mapTextMaterial;

    void Awake()
    {
        Renderer renderer = GetComponent<Renderer>();
        mapTextMaterial = renderer.material;

        float[] stringArray = new float[] { 2f, 0f, 6f, 4f, 3f }; // "CAGED"
        mapTextMaterial.SetFloatArray("_String_Chars", stringArray);
        mapTextMaterial.SetInt("_StringCharacterCount", stringArray.Length);
    }

}

请务必将 _String_Chars_StringCharacterCount 初始化为 Awake 中的某些内容,否则可能会产生意外结果。考虑在字体纹理中包含一个“空白”字符,这样您就可以初始化为一个只有空白字符的字符串。

这个着色器产生这样的效果(注意右边不同的着色器属性):

请务必关闭(未选中)字体纹理的“生成 Mip 贴图”,否则您会得到意外的伪影:

【讨论】:

  • 暂时垂直居中...但我想用滑块或其他东西动态设置它
  • 它有效..但字符之间有垂直线imgur.com/ZjJgx9Zimgur.com/K7MamLj
  • 可以缩小字母间距吗?
  • @Mazhar 我添加了一个字符宽度属性,因此您可以通过减小字符串 x 比例并增加字符宽度来减小间距。它仍然是等宽字体。如果您需要成比例的间距,那值得另一个问题
猜你喜欢
  • 2019-10-11
  • 1970-01-01
  • 2013-08-23
  • 2019-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多