本文分享基于兰伯特漫反射的色散(Dispersion)效果解析
我们知道, 兰伯特漫反射可以表现渐变的明暗效果, 而这种明暗效果可以结合一些手段来实现很多有意思的效果, 今天就给大家介绍其中一种.
先看看最终效果:
效果解析
可以看到, 颜色分布由暗到明, 分别是: 黑色->蓝色->天蓝->白色.
我们使用兰伯特漫反射表现明暗变化, 为了比较清晰的观察变化, 这里使用球的模型:
Shader "Custom/Dispersion" {
Properties {
_RangeColor ("RangeColor", Color) = (0.5,0.8,0.2,1)
_node_6044 ("node_6044", Color) = (0.5,0.5,0.5,1)
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
struct VertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct VertexOutput {
float4 pos : SV_POSITION;
float4 posWorld : TEXCOORD0;
float3 normalDir : TEXCOORD1;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.normalDir = UnityObjectToWorldNormal(v.normal);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = UnityObjectToClipPos( v.vertex );
return o;
}
float4 frag(VertexOutput i) : COLOR {
i.normalDir = normalize(i.normalDir);
float3 normalDirection = i.normalDir;
float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
// 着色代码--begin
float nDotl = saturate(dot(i.normalDir,lightDirection));
float3 finalColor = nDotl;
// 着色代码--end
return fixed4(finalColor,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
效果如下:
可以看到, 大概从一半的位置往右上角渐变, 越来越白.
一次分割
接下来使用step
函数来分散明暗色:
// 着色代码--begin
float nDotl = max(0,dot(i.normalDir,lightDirection));
float color = step(0.6,nDotl);
float3 finalColor = float3(color,color,color);
return fixed4(finalColor,1);
// 着色代码--end
step(a, b)
是一个内置函数, 其作用为a <= b
时返回1, a > b
时返回0.
如果其中的参数是向量, 则每个分量分别处理, 如:
step(float2(0.5, 0.8), 0.6)
// 则得到二维向量 => (1, 0)
// 0.5 <= 0.6 => 1
// 0.8 > 0.6 => 0
漫反射值超过0.6的部分将显示为纯白, 其它部分为纯黑, 当前阶段效果如下:
二次分割
使用二维向量来分散明暗色:
// 着色代码--begin
float nDotl = max(0,dot(i.normalDir,lightDirection));
float2 color = step(float2(0.6, 0.2),nDotl);
float3 finalColor = float3(color,0);
return fixed4(finalColor,1);
// 着色代码--end
漫反射值超过0.6的部分将显示为纯黄, 0.2到0.6之间的部分将显示为纯绿, 当前阶段效果如下:
这是最容易让人迷惑的部分, 为什么是黄色和绿色呢?
首先我们要明白一点: step
函数的第一个参数的维度数代表要对球进行"分割"的次数, 比如本阶段的第一个参数为二维向量, 表示要将球"分割"两次, 变成三个部分.
然后我们分别分析其颜色构成:
明暗值大于0.6的部分, 肯定也是大于0.2的, 所以大于0.6的部分, 经过step
后会输出(1, 1, 0)
也就是纯黄色.
也就是说:
step(float2(0.6, 0.2), 0.8)
// => float2(1, 1)
而0.2到0.6的部分, 经过step
后会输出(0, 1, 0)
也就是纯绿色.
同理可得:
step(float2(0.6, 0.2), 0.5)
// => float2(0, 1)
最后小于0.2的部分将输出(0, 0, 0)
, 即纯黑色:
step(float2(0.6, 0.2), 0.1)
// => float2(0, 0)
三次分割
然后使用三维向量进行分割:
// 着色代码--begin
float nDotl = max(0,dot(i.normalDir,lightDirection));
float3 color = step(float3(0.8, 0.5, 0.2),nDotl);
float3 finalColor = float3(color);
return fixed4(finalColor,1);
// 着色代码--end
效果如下:
超过0.8的部分显示为白色:
step(float2(0.8, 0.5, 0.2), 0.9)
// => float2(1, 1, 1)
0.5到0.8的部分显示为天蓝色:
step(float2(0.8, 0.5, 0.2), 0.6)
// => float2(0, 1, 1)
0.2到0.5的部分显示为纯蓝色:
step(float2(0.8, 0.5, 0.2), 0.6)
// => float2(0, 0, 1)
小于0.2的部分为纯黑色:
step(float2(0.8, 0.5, 0.2), 0.6)
// => float2(0, 0, 0)
切换模型后就可得到图1的效果.
总结
今天分享在兰伯特漫反射的基础上进行色散的效果, 利用漫反射的明暗过度, 使用step
来分区颜色达到想要的效果.
因为step
函数的输出只有0和1, 所以该效果的颜色是有限的, 当然, 我们也可以在这个基础上进一步做更多的效果.
step
的分割次数和最后输出颜色的关系:
- 一次分割: 只能得到纯黑(0, 0, 0)和纯白(1, 1, 1)
- 二次分割: 能得到纯黑(0, 0, 0), 纯红(1, 0, 0), 纯绿(0, 1, 0), 纯黄(1, 1, 0)
- 三次分割: 能得到纯蓝(0, 0, 1), 天蓝(0, 1, 1), 纯白(1, 1, 1), 粉红(1, 0, 1)
可以将step
的结果理解为分割颜色后得到某个颜色遮罩, 然后按照区间计算颜色.
好了, 今天分享就这么多, 希望对大家有所帮助.