颜色渐变着色器 (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;
        }

有效。但它很糟糕,原因如下:

  1. 渐变中的颜色数量是硬编码的。在 Unity 编辑器中添加它们的列表会好得多。
  2. if/else 语句。我知道这些对性能来说很糟糕,应该避免。
  3. 函数中声明了颜色数组,这意味着必须为每个片段创建一个新数组(我认为)。

我的主要问题是第一个:如何在不编辑硬编码值的情况下更改渐变中的颜色数量?

  1. 这是不可能的。着色器将自动编译。并且编译器不支持循环。你的循环只是语法糖,在编译时它会扩展到确定的迭代次数。
  2. If/else 的性能并不差。它仅对测试不利,因为它增加了圈复杂度。
  3. 您可以在 "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,当暴露时它会在检查器中为您提供一个渐变编辑器。