Unity3D Shader教程九 Color Interpolation

通常情况下有多个颜色需要输出到屏幕上,通常情况下我们可以利用参数对它们进行插值来融合这些颜色。

下面的脚本基于我们前面的教程 simple textured shader。

颜色插值

作为开始,我们仅仅使用两种纯色,因为这样我们不需要考虑uv坐标信息或者贴图信息。我们添加一个 _SecondaryColor 属性 代表第二种颜色,添加一个 _Blend 参数来 控制输出第一个颜色还是第二个颜色,我们声明 _Blend 的属性为Range,这样可以在Inspector上按滑动条的形式显示它。

//...

//show values to edit in inspector
    Properties{
        _Color ("Color", Color) = (0, 0, 0, 1) //the base color
        _SecondaryColor ("Secondary Color", Color) = (1,1,1,1) //the color to blend to
        _Blend ("Blend Value", Range(0,1)) = 0 //0 is the first color, 1 the second
}

//...

//the value that's used to blend between the colors
float _Blend;

//the colors to blend between
fixed4 _Color;
fixed4 _SecondaryColor;

除了删除和uv信息相关的代码之外,我们保留vertex shader 部分,然后修改fragment shader ,作为第一个版本我们只是通过 blend 的值添加第二个颜色到第一个颜色上。

//the fragment shader
fixed4 frag(v2f i) : SV_TARGET{
    fixed4 col = _Color + _SecondaryColor * _Blend;
    return col;
}

Blend Between two colors in a wrong way

我们已经可以改变颜色了,但是还不能只显示第二种颜色。这是因为第二种颜第二种颜色是被加在第一种颜色之上的。(类似于将两个不同颜色的灯光照射到同一个区域)。

我们改进一下,可以随着 blend 的值增加将第一个颜色的值减弱。当blend 的值为0时,我们看不到任何第二种颜色,全部显示第一种颜色。当blend 的值为1时,全部显示第二种颜色,而不显示第一种颜色。我们通过第一种颜色乘上 1 - _Blend 来实现。

//the fragment shader
fixed4 frag(v2f i) : SV_TARGET{
    fixed4 col = _Color * (1 - _Blend) + _SecondaryColor * _Blend;
    return col;
}

Blend Between two colors correctly

这种方式也被称为 线性插值,unity也为我们提供了这样一个函数 lerp。

//the fragment shader
fixed4 frag(v2f i) : SV_TARGET{
    fixed4 col = lerp(_Color, _SecondaryColor, _Blend);
    return col;
}

完整的两种颜色插值融合 shader 代码如下:

Shader "Tutorial/009_Color_Blending/Plain"{
    //show values to edit in inspector
    Properties{
        _Color ("Color", Color) = (0, 0, 0, 1) //the base color
        _SecondaryColor ("Secondary Color", Color) = (1,1,1,1) //the color to blend to
        _Blend ("Blend Value", Range(0,1)) = 0 //0 is the first color, 1 the second
    }

    SubShader{
        //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        Pass{
            CGPROGRAM

            //include useful shader functions
            #include "UnityCG.cginc"

            //define vertex and fragment shader
            #pragma vertex vert
            #pragma fragment frag

            //the value that's used to blend between the colors
            float _Blend;

            //the colors to blend between
            fixed4 _Color;
            fixed4 _SecondaryColor;

            //the object data that's put into the vertex shader
            struct appdata{
                float4 vertex : POSITION;
            };

            //the data that's used to generate fragments and can be read by the fragment shader
            struct v2f{
                float4 position : SV_POSITION;
            };

            //the vertex shader
            v2f vert(appdata v){
                v2f o;
                //convert the vertex positions from object space to clip space so they can be rendered
                o.position = UnityObjectToClipPos(v.vertex);
                return o;
            }

            //the fragment shader
            fixed4 frag(v2f i) : SV_TARGET{
                fixed4 col = lerp(_Color, _SecondaryColor, _Blend);
                return col;
            }

            ENDCG
        }
    }
}

贴图插值

接下来 我们 将 插值 从贴图读取出来的颜色。我们首先删除颜色相关的属性和变量,同时添加贴图相关的属性和变量。我们同时添加 uv 坐标信息,但是我们不在vertex shader 部分添加 tiling 和 offset。因为我们的多个贴图使用了同样的uv坐标信息。

//...

//show values to edit in inspector
Properties{
    _MainTex ("Texture", 2D) = "white" {} //the base texture
    _SecondaryTex ("Secondary Texture", 2D) = "black" {} //the texture to blend to
    _Blend ("Blend Value", Range(0,1)) = 0 //0 is the first color, 1 the second
}

//...

//the object data that's put into the vertex shader
struct appdata{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
};

//the data that's used to generate fragments and can be read by the fragment shader
struct v2f{
    float4 position : SV_POSITION;
    float2 uv : TEXCOORD0;
};

//the vertex shader
v2f vert(appdata v){
    v2f o;
    //convert the vertex positions from object space to clip space so they can be rendered
    o.position = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

//...

然后在 fragment shader 部分,我们可以分别对两张贴图添加 添加 tiling 和 offset。我们使用这些坐标信息从两个贴图读取颜色信息,并对他们进行插值操作输出最终颜色。

//the fragment shader
fixed4 frag(v2f i) : SV_TARGET{
    //calculate UV coordinates including tiling and offset
    float2 main_uv = TRANSFORM_TEX(i.uv, _MainTex);
    float2 secondary_uv = TRANSFORM_TEX(i.uv, _SecondaryTex);

    //read colors from textures
    fixed4 main_color = tex2D(_MainTex, main_uv);
    fixed4 secondary_color = tex2D(_SecondaryTex, secondary_uv);

    //interpolate between the colors
    fixed4 col = lerp(main_color, secondary_color, _Blend);
    return col;
}

Blend Between two Textures

对两张贴图进行插值的完整 shader 代码如下:

Shader "Tutorial/009_Color_Blending/Texture"{
    //show values to edit in inspector
    Properties{
        _MainTex ("Texture", 2D) = "white" {} //the base texture
        _SecondaryTex ("Secondary Texture", 2D) = "black" {} //the texture to blend to
        _Blend ("Blend Value", Range(0,1)) = 0 //0 is the first color, 1 the second
    }

    SubShader{
        //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        Pass{
            CGPROGRAM

            //include useful shader functions
            #include "UnityCG.cginc"

            //define vertex and fragment shader
            #pragma vertex vert
            #pragma fragment frag

            //the value that's used to blend between the colors
            float _Blend;

            //the colors to blend between
            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _SecondaryTex;
            float4 _SecondaryTex_ST;

            //the object data that's put into the vertex shader
            struct appdata{
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            //the data that's used to generate fragments and can be read by the fragment shader
            struct v2f{
                float4 position : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            //the vertex shader
            v2f vert(appdata v){
                v2f o;
                //convert the vertex positions from object space to clip space so they can be rendered
                o.position = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            //the fragment shader
            fixed4 frag(v2f i) : SV_TARGET{
                //calculate UV coordinates including tiling and offset
                float2 main_uv = TRANSFORM_TEX(i.uv, _MainTex);
                float2 secondary_uv = TRANSFORM_TEX(i.uv, _SecondaryTex);

                //read colors from textures
                fixed4 main_color = tex2D(_MainTex, main_uv);
                fixed4 secondary_color = tex2D(_SecondaryTex, secondary_uv);

                //interpolate between the colors
                fixed4 col = lerp(main_color, secondary_color, _Blend);
                return col;
            }

            ENDCG
        }
    }
}

基于贴图的插值

最后我们来看下如何通过一张贴图作为参数来插值,而不是像上面那样通过一个单一的数值 (_blend) 插值。

我们将 _blend 相关代码改为_BlendTex,它是一张贴图。

//...

//show values to edit in inspector
Properties{
    _MainTex ("Texture", 2D) = "white" {} //the base texture
    _SecondaryTex ("Secondary Texture", 2D) = "black" {} //the texture to blend to
    _BlendTex ("Blend Texture", 2D) = "grey" //black is the first color, white the second
}

//...

//the texture that's used to blend between the colors
sampler2D _BlendTex;
float4 _BlendTex_ST;

//the colors to blend between
sampler2D _MainTex;
float4 _MainTex_ST;

sampler2D _SecondaryTex;
float4 _SecondaryTex_ST;

//...

我们同样对这张贴图进行 TRANSFORM_TEX 操作,并从贴图中读取颜色信息。然后我们使用其中的颜色信息中的 r 通道 ,也就是 红色 部分,作为插值参数。

//the fragment shader
fixed4 frag(v2f i) : SV_TARGET{
    //calculate UV coordinates including tiling and offset
    float2 main_uv = TRANSFORM_TEX(i.uv, _MainTex);
    float2 secondary_uv = TRANSFORM_TEX(i.uv, _SecondaryTex);
    float2 blend_uv = TRANSFORM_TEX(i.uv, _BlendTex);

    //read colors from textures
    fixed4 main_color = tex2D(_MainTex, main_uv);
    fixed4 secondary_color = tex2D(_SecondaryTex, secondary_uv);
    fixed4 blend_color = tex2D(_BlendTex, blend_uv);

    //take the red value of the color from the blend texture
    fixed blend_value = blend_color.r;

    //interpolate between the colors
    fixed4 col = lerp(main_color, secondary_color, blend_value);
    return col;
}

Blend Between two textures based on a texture

完整的shader代码如下:

Shader "Tutorial/009_Color_Blending/TextureBasedBlending"{
    //show values to edit in inspector
    Properties{
        _MainTex ("Texture", 2D) = "white" {} //the base texture
        _SecondaryTex ("Secondary Texture", 2D) = "black" {} //the texture to blend to
        _BlendTex ("Blend Texture", 2D) = "grey" //black is the first color, white the second
    }

    SubShader{
        //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        Pass{
            CGPROGRAM

            //include useful shader functions
            #include "UnityCG.cginc"

            //define vertex and fragment shader
            #pragma vertex vert
            #pragma fragment frag

            //the texture that's used to blend between the colors
            sampler2D _BlendTex;
            float4 _BlendTex_ST;

            //the colors to blend between
            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _SecondaryTex;
            float4 _SecondaryTex_ST;

            //the object data that's put into the vertex shader
            struct appdata{
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            //the data that's used to generate fragments and can be read by the fragment shader
            struct v2f{
                float4 position : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            //the vertex shader
            v2f vert(appdata v){
                v2f o;
                //convert the vertex positions from object space to clip space so they can be rendered
                o.position = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            //the fragment shader
            fixed4 frag(v2f i) : SV_TARGET{
                //calculate UV coordinates including tiling and offset
                float2 main_uv = TRANSFORM_TEX(i.uv, _MainTex);
                float2 secondary_uv = TRANSFORM_TEX(i.uv, _SecondaryTex);
                float2 blend_uv = TRANSFORM_TEX(i.uv, _BlendTex);

                //read colors from textures
                fixed4 main_color = tex2D(_MainTex, main_uv);
                fixed4 secondary_color = tex2D(_SecondaryTex, secondary_uv);
                fixed4 blend_color = tex2D(_BlendTex, blend_uv);

                //take the red value of the color from the blend texture
                fixed blend_value = blend_color.r;

                //interpolate between the colors
                fixed4 col = lerp(main_color, secondary_color, blend_value);
                return col;
            }

            ENDCG
        }
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容