颜色渐变着色器 (cg / shaderlab)
color ramp shader (cg / shaderlab)
我有一个着色器函数,其功能类似于 Blender3D 中的 "color ramp" 节点。它以零和一之间的浮点数作为输入,并输出一种颜色,该颜色是按指定顺序混合任意数量的颜色:
blender3D 色带
这是我的代码:
fixed4 ColorRamp(float factor){
float positions[5] = {_Pos0, _Pos1, _Pos2, _Pos3, _Pos4};
fixed4 colors[5] = {_Color0, _Color1, _Color2, _Color3, _Color4};
fixed4 outColor = _Color0;
for(int i = 0; i <4; i++){
if(factor >= positions[i] && factor <positions[i+1]){
fixed localFactor = (factor - positions[i])/(positions[i+1] - positions[i]);
outColor = lerp(colors[i], colors[i+1], localFactor);
}else if (factor > positions [_NumColors-1]){
outColor = colors[_NumColors -1];
}
}
return outColor;
}
有效。但它很糟糕,原因如下:
- 渐变中的颜色数量是硬编码的。在 Unity 编辑器中添加它们的列表会好得多。
- if/else 语句。我知道这些对性能来说很糟糕,应该避免。
- 函数中声明了颜色数组,这意味着必须为每个片段创建一个新数组(我认为)。
我的主要问题是第一个:如何在不编辑硬编码值的情况下更改渐变中的颜色数量?
- 这是不可能的。着色器将自动编译。并且编译器不支持循环。你的循环只是语法糖,在编译时它会扩展到确定的迭代次数。
- If/else 的性能并不差。它仅对测试不利,因为它增加了圈复杂度。
- 您可以在 "Property" 块中声明数组。
抱歉,您不能在 属性 块中使用数组。您将在方法外声明它。
float positions[5];
fixed4 colors[5];
fixed4 ColorRamp(float factor){
positions[0] = _Pos0;
positions[0] = _Pos2;
positions[0] = _Pos3;
positions[0] = _Pos4;
positions[0] = _Pos5;
colors[0] = _Color0;
colors[1] = _Color1;
colors[2] = _Color2;
colors[3] = _Color3;
colors[4] = _Color4;
fixed4 outColor = _Color0;
for(int i = 0; i <4; i++)
{
if(factor >= positions[i] && factor <positions[i+1])
{
fixed localFactor = (factor - positions[i])/(positions[i+1] - positions[i]);
outColor = lerp(colors[i], colors[i+1], localFactor);
}
else if (factor > positions [_NumColors-1])
{
outColor = colors[_NumColors -1];
}
}
return outColor;
}
但是您必须从 unity 脚本外部初始化数组。
float[] array = new float[] {1, 2, 3, 4, 5}
material.SetFloatArray("positions", array );
Vector4[] colors = new Vector4[] { new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4() };
material.SetVectorArray("colors", colors);
简单的答案是:将渐变烘焙到纹理中
布莱斯是对的。我可以直接告诉你 - 没有人将这种类型的渐变用于着色器,仅仅是因为它与仅使用 1x128px 颜色渐变纹理相比几乎没有任何优势。使用这样的查找纹理:
fixed4 col = tex2D(_MainTex, i.uv); // Sample the input texture
half lum = dot(col.rgb, fixed3(0.3, 0.59, 0.11)); // Color to luminosity
col = tex2D(_LookUpTex, float2(lum, 0)); // Get value from color ramp
如果您想为用户创建一个方便的检查器,只需从渐变生成一个查找纹理,然后通过 OnValidate() 处的脚本将其分配给着色器。只需为 0 到 127 之间的每个 x 评估 x/127.0 处的渐变即可生成一个。Unity 有一个特殊的渐变 class,当暴露时它会在检查器中为您提供一个渐变编辑器。
我有一个着色器函数,其功能类似于 Blender3D 中的 "color ramp" 节点。它以零和一之间的浮点数作为输入,并输出一种颜色,该颜色是按指定顺序混合任意数量的颜色:
blender3D 色带
这是我的代码:
fixed4 ColorRamp(float factor){
float positions[5] = {_Pos0, _Pos1, _Pos2, _Pos3, _Pos4};
fixed4 colors[5] = {_Color0, _Color1, _Color2, _Color3, _Color4};
fixed4 outColor = _Color0;
for(int i = 0; i <4; i++){
if(factor >= positions[i] && factor <positions[i+1]){
fixed localFactor = (factor - positions[i])/(positions[i+1] - positions[i]);
outColor = lerp(colors[i], colors[i+1], localFactor);
}else if (factor > positions [_NumColors-1]){
outColor = colors[_NumColors -1];
}
}
return outColor;
}
有效。但它很糟糕,原因如下:
- 渐变中的颜色数量是硬编码的。在 Unity 编辑器中添加它们的列表会好得多。
- if/else 语句。我知道这些对性能来说很糟糕,应该避免。
- 函数中声明了颜色数组,这意味着必须为每个片段创建一个新数组(我认为)。
我的主要问题是第一个:如何在不编辑硬编码值的情况下更改渐变中的颜色数量?
- 这是不可能的。着色器将自动编译。并且编译器不支持循环。你的循环只是语法糖,在编译时它会扩展到确定的迭代次数。
- If/else 的性能并不差。它仅对测试不利,因为它增加了圈复杂度。
- 您可以在 "Property" 块中声明数组。
抱歉,您不能在 属性 块中使用数组。您将在方法外声明它。
float positions[5];
fixed4 colors[5];
fixed4 ColorRamp(float factor){
positions[0] = _Pos0;
positions[0] = _Pos2;
positions[0] = _Pos3;
positions[0] = _Pos4;
positions[0] = _Pos5;
colors[0] = _Color0;
colors[1] = _Color1;
colors[2] = _Color2;
colors[3] = _Color3;
colors[4] = _Color4;
fixed4 outColor = _Color0;
for(int i = 0; i <4; i++)
{
if(factor >= positions[i] && factor <positions[i+1])
{
fixed localFactor = (factor - positions[i])/(positions[i+1] - positions[i]);
outColor = lerp(colors[i], colors[i+1], localFactor);
}
else if (factor > positions [_NumColors-1])
{
outColor = colors[_NumColors -1];
}
}
return outColor;
}
但是您必须从 unity 脚本外部初始化数组。
float[] array = new float[] {1, 2, 3, 4, 5}
material.SetFloatArray("positions", array );
Vector4[] colors = new Vector4[] { new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4() };
material.SetVectorArray("colors", colors);
简单的答案是:将渐变烘焙到纹理中
布莱斯是对的。我可以直接告诉你 - 没有人将这种类型的渐变用于着色器,仅仅是因为它与仅使用 1x128px 颜色渐变纹理相比几乎没有任何优势。使用这样的查找纹理:
fixed4 col = tex2D(_MainTex, i.uv); // Sample the input texture
half lum = dot(col.rgb, fixed3(0.3, 0.59, 0.11)); // Color to luminosity
col = tex2D(_LookUpTex, float2(lum, 0)); // Get value from color ramp
如果您想为用户创建一个方便的检查器,只需从渐变生成一个查找纹理,然后通过 OnValidate() 处的脚本将其分配给着色器。只需为 0 到 127 之间的每个 x 评估 x/127.0 处的渐变即可生成一个。Unity 有一个特殊的渐变 class,当暴露时它会在检查器中为您提供一个渐变编辑器。