如何使用着色器在网格上映射字母?
How to map letters on mesh with shader?
我正在尝试创建一个允许在网格上映射文本的着色器。如何水平对齐每个字符?目前,它们都相互重叠。
目前看起来像这样:
使用的纹理:
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
}
}
}
解释在评论里
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# 中,您必须设置字符串数组并告诉它您要绘制的字符串的长度。没有 SetIntArray
但 SetFloatArray
有效:
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
中的内容,否则可能会产生意外结果。考虑在字体纹理中包含一个 "blank" 字符,这样您就可以初始化为一个只有空白字符的字符串。
此着色器产生如下效果(注意右侧的不同着色器属性):
请务必关闭字体纹理的 "Generate Mip Maps"(未选中),否则您会得到意想不到的伪像:
我正在尝试创建一个允许在网格上映射文本的着色器。如何水平对齐每个字符?目前,它们都相互重叠。
目前看起来像这样:
使用的纹理:
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
}
}
}
解释在评论里
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# 中,您必须设置字符串数组并告诉它您要绘制的字符串的长度。没有 SetIntArray
但 SetFloatArray
有效:
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
中的内容,否则可能会产生意外结果。考虑在字体纹理中包含一个 "blank" 字符,这样您就可以初始化为一个只有空白字符的字符串。
此着色器产生如下效果(注意右侧的不同着色器属性):
请务必关闭字体纹理的 "Generate Mip Maps"(未选中),否则您会得到意想不到的伪像: