美文网首页
unity的色环渐变shader

unity的色环渐变shader

作者: 相对豆 | 来源:发表于2018-11-22 23:08 被阅读0次
    unity的渐变材质没有现成的,百度了一下基本上都是两色渐变,使用lerp()做线性插值。但是我要实现色轮,红-黄-绿-蓝这种光谱的渐变,想了一下使用HSV颜色模型最适合了。
    image.png
    这里要先介绍一下HSV的颜色模型, 我们最熟悉的是RGB(red,green,blue)颜色空间,各个分量的数值越小,亮度越低。数值越大,亮度越高;如:(0,0,0)表示黑色,(255,255,255)表示白色,计算机中最基本的控制也是基于RGB原理,我们的显示器每一个像素都由三个颜色的物理亮点组成,只需要控制三个分量的明暗程度,就能呈现不同的颜色。
    image.png
    和RGB不同的HSV三个分量分别是色相饱和度明度(英语:Hue, Saturation, Value),我要的结果是仅仅改变H的值,一般H值得范围是0-359,也就是园的一周。所有实现的思路,是从任何一个初始颜色开始,H值做偏移,经过360度以后,再次回到初始值颜色也就是可见光谱从蓝到红。unity里面Material输入的值是RGB的,所以需要转换成HSV以后做偏移,再换回RGB。
    Shader "Tornado/ColorGradationByUV_HSV" {
        Properties{
            _MainColor("MainColor", color) = (1,0,0,1)     //第一种颜色:绿
            _SecondColor("SecondColor", color) = (1,0,0,1) //第二种颜色:红
            //贴图
            _MainTex("MainTex (RGB)", 2D) = "white" {}
            //Hue的值范围为0-359. 其他两个为0-1 ,这里我们设置到3,因为乘以3后 都不一定能到超过.
            _Hue("Hue", Range(0,359)) = 0
            _Saturation("Saturation", Range(0,3.0)) = 1.0
            _Value("Value", Range(0,3.0)) = 1.0
    
            _YMinimum("Y-Minimum", range(-180, 180.0)) = 0.0
            _YMaximum("Y-Maximum", range(-180, 180.0)) = 0.0
        }
    
        SubShader{
            Pass {
                Tags { "RenderType" = "Opaque" }
                LOD 200
    
                Lighting Off
    
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
    
                half _Hue;
                half _Saturation;
                half _Value;
    
                float _YMinimum;
                float _YMaximum;
                fixed4 _MainColor;
    
                struct v2f {
                    float4 pos:POSITION;
                    float3 uv : TEXCOORD0;
                };
    
                //RGB to HSV
                float3 RGBConvertToHSV(float3 rgb)
                {
                    float R = rgb.x,G = rgb.y,B = rgb.z;
                    float3 hsv;
                    float max1 = max(R,max(G,B));
                    float min1 = min(R,min(G,B));
                    if (R == max1)
                    {
                        hsv.x = (G - B) / (max1 - min1);
                    }
                    if (G == max1)
                    {
                        hsv.x = 2 + (B - R) / (max1 - min1);
                        }
                    if (B == max1)
                    {
                        hsv.x = 4 + (R - G) / (max1 - min1);
                        }
                    hsv.x = hsv.x * 60.0;
                    if (hsv.x < 0)
                        hsv.x = hsv.x + 360;
                    hsv.z = max1;
                    hsv.y = (max1 - min1) / max1;
                    return hsv;
                }
    
                //HSV to RGB
                float3 HSVConvertToRGB(float3 hsv)
                {
                    float R,G,B;
                    //float3 rgb;
                    if (hsv.y == 0)
                    {
                        R = G = B = hsv.z;
                    }
                    else
                    {
                        hsv.x = hsv.x / 60.0;
                        int i = (int)hsv.x;
                        float f = hsv.x - (float)i;
                        float a = hsv.z * (1 - hsv.y);
                        float b = hsv.z * (1 - hsv.y * f);
                        float c = hsv.z * (1 - hsv.y * (1 - f));
                        switch (i)
                        {
                            case 0: R = hsv.z; G = c; B = a;
                                break;
                            case 1: R = b; G = hsv.z; B = a;
                                break;
                            case 2: R = a; G = hsv.z; B = c;
                                break;
                            case 3: R = a; G = b; B = hsv.z;
                                break;
                            case 4: R = c; G = a; B = hsv.z;
                                break;
                            default: R = hsv.z; G = a; B = b;
                                break;
                        }
                    }
                    return float3(R,G,B);
                }
    
                v2f vert(appdata_base v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = v.texcoord;
                    return o;
                }
    
                fixed4 frag(v2f IN) : COLOR//SV_Target
                {
    
                    float y = IN.uv.y;
                    float g = lerp(_YMaximum, _YMinimum, y);
                    float3 colorHSV;
                    float4 original;
                    original = _MainColor;
                    colorHSV.xyz = RGBConvertToHSV(original.xyz);           //转换为HSV
                    colorHSV.x = (g + _Hue) % 360;                          //调整偏移Hue值
                                                                                                //超过360的值从0开始
    
                    colorHSV.y *= _Saturation;                              //调整饱和度
                    colorHSV.z = _Value;
                    original.xyz = HSVConvertToRGB(colorHSV.xyz);           //将调整后的HSV,转换为RGB颜色
                    
                    return original;
                }
                ENDCG
        }
    }
        FallBack "Diffuse"
    }
    
    
    shader在vertex部分读取了v.texcoord的uv信息,然后输入给fragment 去着色,要是是使用uv的v分量(IN.uv.y)插值得到0-360,把输入颜色转换成HSV,去做偏移,就形成了uv的v方向上的颜色变化。开始我想用物体顶点的Y值直接做驱动,但是发现模型有时候很大,有时候很小,值范围需要去做标准化,也很不方便,还是一键给个UV拉倒了。

    (总结:现在的GPU都很厉害,GPU方面的一些禁忌在很多情况下貌似也不突出了,但是像在GPU上执行大量%运算,逻辑判断,应该还是要去避免)

    相关文章

      网友评论

          本文标题:unity的色环渐变shader

          本文链接:https://www.haomeiwen.com/subject/nborqqtx.html