ShaderForge学习笔记:RGB to HSV、HSV to RGB节点(色彩空间转换节点)

发表于2018-07-16
评论0 3.7k浏览
ShaderForge是一款为Unity所用的、基于节点操作的Shader插件。所以这个ShaderForge学习笔记系列希望可以帮到大家。这一篇就来介绍下色彩空间转换节点的使用。

ShaderForge色彩空间转换

一、官方说明

RGB to HSV
RGB to HSV节点根据节点输入的RGB颜色值输出HSV(色相、饱和度和明度)。色相和饱和度在0到1之间。对于曝光过度的颜色,明度可以超过1。

HSV to RGB
根据给定的HSV(色相、饱和度、明度)输入值。色相和饱和度在0到1之间。明度也一样,但是可以超过1来显示颜色。

二、简单介绍HSV色彩空间

艺术家有时偏好使用 HSV 颜色模型而不选择 RGB 或 CMYK 模型,因为它类似于人类感觉颜色的方式。RGB 和 CMYK 分别是加法原色和减法原色模型,以原色组合的方式定义颜色,而 HSV 以人类更熟悉的方式封装了关于颜色的信息:“这是什么颜色?深浅如何?明暗如何?”

HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。

HSV对用户来说是一种直观的颜色模型。我们可以从一种纯色彩开始,即指定色彩角H,并让V=S=1,然后我们可以通过向其中加入黑色和白色来得到我们需要的颜色。增加黑色可以减小V而S不变,同样增加白色可以减小S而V不变。例如,要得到深蓝色,V=0.4 S=1 H=240度。要得到淡蓝色,V=1 S=0.4 H=240度。 一般说来,人眼最大能区分128种不同的色彩,130种色饱和度,23种明暗度。如果我们用16Bit表示HSV的话,可以用7位存放H,4位存放S,5位存放V,即745或者655就可以满足我们的需要了。

由于HSV是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,如Photoshop(在Photoshop中叫HSB)等等,但这也决定了它不适合使用在光照模型中,许多光线混合运算、光强运算等都无法直接使用HSV来实现。

色相(Hue)
色相(Hue)是饱和度和亮度都为100%的颜色。我的理解是,红色、绿色、蓝色三种原色,两两以不同比例混合后产生的颜色(可以只有一种颜色,最多只能两种颜色混合,不能三种颜色混合)。所有的色相可以用色相环来展示。

从物理学的角度上来讲,光线不为物体所吸收而反射出来的颜色,称之为色相。如果蓝色不为物体所吸收,那么呈现出来的就是蓝色。黑色是物体吸收了所有光线,无反射。白色相反。

饱和度(Saturation)
饱和度是指色彩的鲜艳程度,也称色彩的纯度。饱和度取决于该色中含色成分和消色成分(灰色)的比例(色彩的鲜明程度与它最大可能的鲜明程度的比值)。含色成分越大,饱和度越大;消色成分越大,饱和度越小。

明度(Value)
明度就是色彩的明暗程度,从物理学的角度上来讲,就是光线的折射度。
当颜色中白色成分较多时则明度高,黑色成分较多时则明度低。

三、颜色空间的转换

设 (r, g, b) 分别是一个颜色的红、绿和蓝坐标,它们的值是在 0 到 1 之间的实数。设 max 等价于 r, g 和 b 中的最大者。设 min 等于这些值中的最小者。

这里的 h ∈ [0, 360)是角度的色相角,而 s, v∈ [0,1] 是饱和度和亮度。
- RGB到HSV的转换

HSV到RGB的转换

四、应用实例-调节图片的色相、饱和度、明度


五、自定义UnityShader实现色彩空间的转换

rgb to hsv & hsv to rgb
1.完整代码
Shader "Hidden/HSV2RGB"
{
    Properties
    {
        _MainTex("Rgb from Tex", 2D) = "" {}
        _HueOffset("Hue Offset", Range(0,1)) = 1
        _SaturationOffset("Hue Offset", Range(-1,0)) = 1
        _ValueOffset("Hue Offset", Range(-1,0)) = 1
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            sampler2D _MainTex;
            float _HueOffset;
            float _SaturationOffset;
            float _ValueOffset;
            fixed4 frag (v2f i) : SV_Target
            {
                float4 tex = tex2D(_MainTex, i.uv);
                float3 rgb = tex.rgb;
            //rgb to hsv
                //在HLSL中, step(a,b)既是当b>=a时返回1,否则返回0,换句话说既是当a<=b时返回1,否则返回0。因此可以把被比较数灵活的插入a或b的位置,完成小于或大于的比较。
                float4 k = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
                float4 p = lerp(float4(rgb.bg, k.wz), float4(rgb.gb, k.xy), step(rgb.b, rgb.g));
                // 比较r和max(b,g)
                float4 q = lerp(float4(p.xyw, rgb.r), float4(rgb.r, p.yzx), step(p.x, rgb.r));
                float d = q.x - min(q.w, q.y);
                float e = 1.0e-10;
                float3 hsv = float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
                // q.x 就是max(r,g,b)// 比较b和g
            //调整明度饱和度
                hsv.x = hsv.x + _HueOffset;
                hsv.y = hsv.y + _SaturationOffset;
                hsv.z = hsv.z + _ValueOffset;
            //hsv to rgb
                rgb = saturate(3.0*abs(1.0-2.0*frac(hsv.x+float3(0.0,-1.0/3.0,1.0/3.0)))-1); //明度和饱和度为1时的颜色
                rgb = (lerp(float3(1,1,1),rgb,hsv.y)*hsv.z); // hsv
                return fixed4(rgb, 1);
            }
            ENDCG
        }
    }
}

2.效果展示

3.分析hsv to rgb关键算法
首先将hue值转为相应的rgb颜色值,hue转rgb的公式来由可参考:【shaderforge学习笔记】 Hue节点(色相节点),接着将hue转来的rgb值与(1,1,1)以饱和度(saturation)为依据进行插值,最后乘以明度。
float saturation = 0.8; // 饱和度
float value = 0.9; //明度
float hue = 1; //色相
float3 rgb = saturate(3.0*abs(1.0-2.0*frac(hue+float3(0.0,-1.0/3.0,1.0/3.0)))-1; //明度和饱和度为1时的颜色
float3 hsv = (lerp(float3(1,1,1),rgb),saturation)*value); // hsv
来自:https://blog.csdn.net/v_xchen_v/article/details/79366040

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引