体积字
为了方便美术去修改场景中的有体积字,在TMP基础上,用光线步进加SDF去渲染体积字。
先贴参考
Mesh
直接改原版的TMP有点麻烦,退而求其次,在TMP构造好Mesh后,修改这个Mesh,以适应新的Shader。
原本TMP构造出的网格是一个个面片,每个字符由两个三角形拼成。
然后以这个mesh为基础,再添加4个点,5个面,纵向拉伸成一个个立方体.
新网格中除了保存之前的网格中的信息外,还要额外添加几个。分别是
- 每个字符左下角顶点的坐标
- 每个字符右上角顶点坐标
- 每个字符左下角顶点的UV
除了这些,整体的厚度也写入材质中去。
Shader
网格准备好就轮到Shader了。
顶点着色器
这里没什么特殊的,只要把相关的顶点属性传递到片元中就行了。
光线步进
然后就开始光线步进吧。
从片元的表面出发,沿视线方向,以步进到的位置采样SDF图。
如果超出了当前字符方块的范围,就Clip掉。否则就一直步进,直至进入字符SDF内部或者迭代次数耗尽。
第一次迭代中如果是前后表面就不需要步进了。
法线
前后面法线很简单,侧面法线就用SDF图的梯度来计算
深度偏移
要通过SV_Depth来设定偏移后的深度
这里有个大坑,在OpenGL下,深度要从-1,1映射到0,1
着色
这里就用LIT的PBR来着色
UV先简单用物体空间的XY。
三向映射
侧面也需要UV,所以用法线作为权重,每个轴的坐标作为UV采样三次后混合
阴影
增加一个ShadowCaster Pass来投射阴影。
这里的步进方向记得改为从主光源的方向来步进,同时不要忘记应用阴影偏移。
同样最后要用SV_Depth来偏移深度。
也别忘记在前向Pass中增加接收阴影的宏
屏幕空间AO
有了上面的代码,增加一个DepthNormals Pass就可以给SSAO提供法线和深度了。
这里参数调整的很大以看出区别。
抗锯齿
因为没有网格,所以MSAA肯定是不行。直接就用URP的TAA了,效果还可以。
或者就添加圆角。
倒角
有点麻烦
先从倒角开始,倒一个面。基本思路就是在计算SDF时,在需要倒角切除的部分,手动增加一个距离。
然后是法线,这就简单了,lerp一下就行了
圆角
然后是圆角。
参考这篇文章
关键是其中这个圆角矩形的SDF
改进
- 可以仿照TMP,增加对应的外轮廓,下划线,斜体等等
- UV贴图也可以改进,分成侧面和正反面两种UV,适配两种不同的材质。
- 法线的获取,目前是在Shader中采样四个点去计算,可以预先根据SDF图去生成对应的法线图,直接采样。
局限
因为没有常规的UV,所以没有办法烘焙光照,也不能给其他静态物体提供静态的光照。
但既然要用体积字,应该都是有运行时更改且动态的需求,所以一般不会用来烘焙。
如果需要那种字体侧面发光的效果,可以在后面放一个TMP,做成透明渐变来模拟泛光。
摄像机不能进入字符立方体内部,否则就要渲染反面且从近平面开始步进,得不偿失。