知乎上看到字符风格的shader,然后照着自己撸了一遍代码,微做修改,增加了控制材质的C#代码
https://zhuanlan.zhihu.com/p/30775000
先看一下我实现的效果(字符由大到小,最后字符过密产生摩尔纹),素材图片在文章末尾:
思路是这样的:
将原图分割为很多个小方块,然后取小方块左上角位置的颜色,计算灰度值。
灰度值和字符图上的字符一一对应。最终就实现了字符组成的图片。当然这种左上角取颜色的方式只适合大色块的原始图。
下面是稍作修改的shader代码,注释已经写好了。
Shader "Custom/CharacterStyleShader" {
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_CharTex ("Char texture", 2D) = "white" {}
//每个字符所占的uv比例,由C#传入
_WidthPerChar("Width per char", Range(0.0, 1.0)) = 0.001
//(字符图上)一共支持几个色阶
_CharCount("Char count", int) = 16
//(字符图上)每行几个色阶
_CharCountPerRow("char per row", int) = 4
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float _WidthPerChar;
sampler2D _MainTex;
int _CharCountPerRow;
int _CharCount;
sampler2D _CharTex;
fixed4 frag (v2f i) : SV_Target
{
//找到原图上网格的起点
float2 startUv = float2(floor(i.uv.x / _WidthPerChar) * _WidthPerChar, floor(i.uv.y / _WidthPerChar) * _WidthPerChar);
//找到字符图上相对网格起点的坐标
float ratio = 1.0 / _WidthPerChar / _CharCountPerRow;
float2 oppositeUv = float2((i.uv.x - startUv.x) * ratio, (i.uv.y - startUv.y) * ratio);
//计算灰度值
fixed3 color = tex2D(_MainTex, startUv).rgb;
fixed luminosity = dot(color.rgb,fixed3(0.299,0.587,0.114));
//计算灰度阶数
int greyStep = floor(luminosity * _CharCount);
//计算字符图上对应网格起点坐标
float _ReciprocalPerRow = 1.0 / _CharCountPerRow;
float2 greyStartUv = float2(floor(greyStep / _CharCountPerRow) * _ReciprocalPerRow, (greyStep % _CharCountPerRow) * _ReciprocalPerRow);
//获取
float2 greyUv = greyStartUv + oppositeUv;
return tex2D(_CharTex, greyUv);
}
ENDCG
}
}
}
控制的C#代码:
//Author: 邵志恒
//E-mail: [email protected]
//参考 破晓(知乎专栏) https://zhuanlan.zhihu.com/p/30775000
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class CharacterStyleAdjust : MonoBehaviour {
[Range(0.00001f,0.1f)]
public float withPerChar = 0.001f; //每个字符所占uv比例
public int charCount = 16; //色阶数
[Range(1, 16)]
public int charCountPerRow = 4; //每行色阶数
private Renderer m_Renderer;
private Material m_Mat;
private MaterialPropertyBlock m_PropBlock;
private int m_MainTexId = Shader.PropertyToID("_MainTex");
private int m_CharTexId = Shader.PropertyToID("_CharTex");
private int m_WidthPerCharId = Shader.PropertyToID("_WidthPerChar");
private int m_CharCountId = Shader.PropertyToID("_CharCount");
private int m_CharCountPerRowId = Shader.PropertyToID("_CharCountPerRow");
void Awake() {
m_PropBlock = new MaterialPropertyBlock();
m_Renderer = this.GetComponent<MeshRenderer>();
m_Mat = m_Renderer.sharedMaterial;
Debug.Log(m_Mat.name);
UpdatePropBlock();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
UpdatePropBlock();
}
void UpdatePropBlock()
{
m_Renderer.GetPropertyBlock(m_PropBlock);
m_PropBlock.SetFloat(m_WidthPerCharId, withPerChar);
m_PropBlock.SetFloat(m_CharCountId, charCount);
m_PropBlock.SetFloat(m_CharCountPerRowId, charCountPerRow);
m_Renderer.SetPropertyBlock(m_PropBlock);
}
}
附:
字符图片:
原始图片: