【问题标题】:color ramp shader (cg / shaderlab)颜色渐变着色器(cg / shaderlab)
【发布时间】:2018-12-28 09:20:04
【问题描述】:

我有一个着色器函数,其功能类似于 Blender3D 中的“色带”节点。它将一个介于 0 和 1 之间的浮点数作为输入,并输出一种颜色,该颜色是按指定顺序混合了任意数量的颜色:

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. 颜色数组在函数中声明,这意味着必须为每个片段创建一个新数组(我认为)。

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

【问题讨论】:

    标签: unity3d shader cg shaderlab


    【解决方案1】:

    布莱斯是对的。我可以直接告诉你——没有人将这种类型的渐变用于着色器,仅仅是因为它与仅使用 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 有一个特殊的 Gradient 类,当暴露时,它会在检查器中为您提供一个渐变编辑器。

    【讨论】:

      【解决方案2】:
      1. 这是不可能的。着色器将自动编译。并且编译器不支持循环。您的循环只是语法糖,在编译时将扩展为确定的迭代次数。
      2. If/else 的性能并不差。它只对测试不利,因为它会增加圈复杂度。
      3. 您可以在“属性”块中声明数组。

      抱歉,您没有在属性块中使用数组。您将在方法之外声明它。

      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;
      }
      

      但您必须在统一脚本外部初始化数组。

      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);
      

      【讨论】:

      • 谢谢!您能否提供一个在 Property 块中声明数组的示例?我遇到了从 Unity 编辑器传递数组以及从可以从 Unity 编辑器传递的属性创建数组的麻烦。
      【解决方案3】:

      简单的答案是:将渐变烘焙到纹理中

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-02
        • 1970-01-01
        • 2014-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-29
        • 1970-01-01
        相关资源
        最近更新 更多