unity的色环渐变shader

2018-11-22  本文已影响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上执行大量%运算,逻辑判断,应该还是要去避免)

上一篇下一篇

猜你喜欢

热点阅读