上一篇文章介绍了溶解特效和其基本的原理, 接下来的两篇文章在此基础上增加了优化的内容.
今天主要的内容如下:
我们先来看看最终的效果:
再来之前的效果做对比:
可以看到, 优化过后的效果, 在溶解边缘有一层像是火烧的感觉, 比起原来的效果显得更炫丽一点, 没有那么干巴巴.
而要做到这个效果, 我们需要借助RampTex来实现.
RampTex和噪声纹理一样, 也是Shader里面使用比较多的一种技术, 特别是在卡通渲染领域, 用来制作层次的大块颜色纹理, 效果很好.
那么我们就开始介绍RampTex吧.
Ramp有阶梯, 斜坡, 渐变的意思, 所以RampTex可以翻译为: 渐变纹理.
我们先看看渐变纹理长啥样:
可以看到, 这张纹理的特点是, u方向的颜色(使用(u, 0)取值)是一种渐变的效果(白->黄->红->灰->黑), 而v方向的颜色是一样的, 严格上说这个纹理只是一个一维的纹理, v方向的信息用不着. 下面的RampTex说明了这个问题:
也就是说我们完全可以只存储少量的信息, 这个也是后面一个优化点, 下章再介绍.
那么怎么来理解RampTex呢?
因为原来的模型是使用PBR渲染来制作的, 用来说明这个原理不是很直观, 所以这里我们换一个模型:
左边是从RampTex采样, 右边是正常采样, 中间是两张纹理的重叠效果展示.
可以看到, 蓝色方框框起来的部分, 在纹理上是从左到右为黄色到红色的渐变, 可以对应上.
上面的图示应该是比较直观了, 我们使用RampTex来采样, 就可以获得一种渐变的效果.
当然, 这里的RampTex是用来做溶解的, 不是给模型本身添加渐变效果, 所以看起来会有点怪, ^_^.
到这里为止, 我们只需要明白: RampTex是一种渐变纹理, 用于提供一种渐变信息. 注意这里的渐变信息不一定是颜色, 也可以是某种判断条件, 只是使用颜色来展示和存储, 好直观了解而已, 前面介绍的噪声纹理也是类似的, 理解这点很重要, 如果只是将其局限的看做颜色信息, 那么很多概念和原理都没法很好的理解.
理解了RampTex是用来提供渐变信息这一点后, 我们来分析我们想要的效果.
想象一下, 火焰燃烧纸片时的场景, 纸片最接近火焰的部分是不是从白色到白黄色再到黄红色, 最后到红色的变化过程, 描述起来有点抽象, 大家可以自行网上搜索一下进行观察.
而这种渐变过程和所用到的渐变信息就是通过RampTex来提供的.
看上面的图, 是不是从白到黄, 再从黄到红, 最后到黑? 这就是用来模拟火焰烧纸片的物理现象.
结合一下之前介绍的噪声纹理, 我们需要将其由黑变白的过程, 映射到RampTex的由白变黄, 再到红, 最后到黑的过程.
是不是有点没太明白?
下面我尝试说人话.
首先使用噪声纹理给模型贴图:
通过上一篇文章的内容, 我们知道, 越黑的部分, 代表越先进行溶解, 越白的部分代表越后进行溶解.
如果需要将RampTex的变化映射到这个过程, 我们就需要使用噪声纹理的值去RampTex采样:
也就是说, 在噪声纹理上黑的部分, 在RampTex上应该是白的部分, 在噪声纹理上灰的部分, 对应RampTex上黄红的部分.
这里我特意加深了效果映射效果, 本身的情况没有这么明显.
相信大家应该有点感觉了吧, 总之这里能理解: 使用噪声纹理的值(由像素的uv坐标采样得到), 去采样RampTex即可得到这种效果就好.
然后叠加上模型本身的纹理效果:
显然, 这不是我们想要的效果.
我们需要的是: 只在溶解边缘有渐变的效果, 其它的部分正常显示.
接下来我们分几个情况来分析, 如果这个像素:
其中"使用本身纹理采样的颜色"可以用"使用本身纹理采样的颜色+渐变采样的颜色(但是颜色值为0, 也就是相当于没加)"来替代.
所以我们得出结论, 如果想要达到目的, “只需要”, 对于未发生溶解的像素:
怎么样? 看起来很简单吧?
简单才怪…
这个算法必须要解决的问题就是: 我特么怎么知道那些像素是溶解像素周围的像素???
最直观的想法当然是做判断啦:
fixed4 frag (v2f i) : SV_Target
{
fixed4 dissolveCol = tex2D(_DissolveTex, i.uv.zw);
clip(dissolveCol.r - _DissolveThreshold);
// ---- 获取溶解边缘像素
fixed4 rampColor = 0; // 三层圈之外的颜色使用原色
fixed offset = dissolveCol.r - _DissolveThreshold;
if (offset >= 0)
{
if (offset < 0.04)
{
rampColor = tex2D(_RampTex, 0);
}
else if (offset < 0.08)
{
rampColor = tex2D(_RampTex, dissolveCol);
}
else if (offset < 0.12)
{
rampColor = fixed4(0, 0.2, 0, 0);
}
}
// ---- 结束
fixed4 col = tex2D(_MainTex, i.uv.xy);
col += rampColor;
return col;
}
因为dissolveCol.r - _DissolveThreshold < 0
的像素会发生溶解, 被抛弃, 所以offset = dissolveCol.r - _DissolveThreshold;
这个offset
就可以用来判断溶解边缘像素.
我们通过offset
将溶解边缘像素划分为三层:
0 ~ 0.04
我们给一个0作为采样坐标, 在RampTex上采样获得白色0.04 ~ 0.08
选择噪声采样值作为采样坐标, 在RampTex上采样随着噪声采样值的变化而变化0.08 ~ 0.12
我们固定给一个绿色也就是说, 溶解边缘会有三层颜色, 依次是: 白色, (从白色到黄色再到红色最后到原色的变化色), 绿色. 下图可以清晰的看出我们预期的结果:
去掉原色的影响后, 效果更清晰:
void frag(...)
{
//....
col += rampColor;
if (rampColor.r > 0)
{
col = rampColor;
}
return col;
}
然后我们看看真实的效果:
是不是有点那啥意思了? 哈哈.
但是这样直接指定颜色, 而且用了多个if, 感觉有点傻, 所以我们在下一篇文章里换一个思路来实现.
今天借溶解特效优化的例子来认识了RampTex, 主要展示结合实际使用来介绍RampTex, 是为了加深大家对于RampTex的认识和理解, 溶解特效本身并不是重点.
下一篇文章会使用另一个思路来实现最后的一部分轮廓颜色, 还是基于RampTex, 只不过更合理和自然. 主要的目的是介绍SmoothStep函数和其使用, 可能对于像我一样数学不太好的同学来说, 会有一定的理解难度, 但是这种实现却非常漂亮, 同时可以让大家再一次领略数学和Shader的魅力.
好了, 今天就是这样, 希望对大家有所帮助.