【猫猫的Unity Shader之旅】之使用顶点的世界坐标

  在顶点运算阶段,我们可以得到每个顶点的位置,利用这些位置可以实现非常不错的效果

访问顶点坐标

  为了使用顶点坐标,我们需要使用顶点函数,将顶点坐标传入Input结果,再传入surf中进行处理。当然,要记得在Input中添加相应分量。

void Myvert(inout appdata_full v, out Input IN)
{
    UNITY_INITIALIZE_OUTPUT(Input, IN);
    IN.worldSpacePos = mul(_Object2World, v.vertex);
}

  这里的_Object2World是Unity Shader内置的一个变量,表示从局部坐标到世界坐标的变换矩阵,我们用顶点的局部坐标与该矩阵相乘,得到的就是顶点的世界坐标。

  为了观察效果,我们可以根据世界坐标显示不同的颜色,surf函数如下:

void surf (Input IN, inout SurfaceOutput o) {
    half4 c = tex2D (_MainTex, IN.uv_MainTex);
    fixed4 col;
    if(IN.worldSpacePos.x < 0)
        col = _Color1;
    else
        col = _Color2;
    o.Albedo = col.rgb * c.rgb;
    o.Alpha = c.a;
}

  效果如图:

  这里写图片描述

变色动画

  为了实现一个变色效果,我们需要定义一个可以变化的边界。这个边界的一侧用Color1,另一个是Color2,当这个边界移动时,就可以看到模型的颜色从Color1逐渐变成Color2。为了效果更好,我们可以用球面来切割这个模型。要表示一个虚拟的球,需要一个球心坐标和一个半径。

  为表诚意先上主要代码:

Shader "Custom/ChangeColor" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _Color1 ("Color 1", Color) = (1, 1, 1, 1)
        _Color2 ("Color 2", Color) = (1, 1, 1, 1)
        _BorderColor ("边界颜色", Color) = (1, 1, 1, 1)
        _BorderThick ("边界厚度", Range(0.01, 3)) = 0.1
        _Bulge ("边界凸出程度", Range(0.01, 0.3)) = 0.08
        _CullPos ("裁剪球位置", Range(0, 3)) = 3
        _CullRadius ("裁剪球半径", Range(0.1, 5)) = 1
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert vertex:Myvert

        sampler2D _MainTex;
        fixed4 _Color1;
        fixed4 _Color2;
        fixed4 _BorderColor;
        float _BorderThick;
        float _Bulge;
        float _CullPos;
        float _CullRadius;

        struct Input {
            float2 uv_MainTex;
            float3 worldSpacePos;
        };

        inline float GetDis(float3 pos)
        {
            return distance(pos, float3(_CullPos, _CullPos, _CullPos));
        }
        void Myvert(inout appdata_full v, out Input IN)
        {
            UNITY_INITIALIZE_OUTPUT(Input, IN);
            IN.worldSpacePos = mul(_Object2World, v.vertex);
            if(abs(GetDis(IN.worldSpacePos.xyz) - _CullRadius) <= _BorderThick / 2)
            {
                v.vertex.xyz += v.normal * _Bulge;
            }
        }

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 tint;
            if(abs(GetDis(IN.worldSpacePos.xyz) - _CullRadius) <= _BorderThick / 2)
            {
                tint = _BorderColor;
            }
            else if(GetDis(IN.worldSpacePos.xyz) > _CullRadius)
            {
                tint = _Color1;
            }
            else
            {
                tint = _Color2;
            }
            half4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = tint.rgb * c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

  这里我们定义了球心和半径,当模型上的点与球心距离大于半径时,表示点在球外,用Color1,反之用Color2。我们还增加了一个过渡用的颜色,当距离和半径的差的绝对值比较小时,使用这个过渡颜色。注意到我们还为这个过渡区域增加了凸起效果,方法是将顶点位置沿着发现方向移动一点距离,非常好理解。

  下面是效果图:

  这里写图片描述

  本次用到的工程

结束语

  虽然只是简单的使用顶点位置信息,但是依然可以做出非常好玩的效果。Shader确实是一门艺术,需要创意和灵感才能激发它最大的潜能。其实我们人又何尝不是这样,1%的灵感往往比99%的汗水还要重要。当我们看不到前方的路的时候,不妨停下脚步整顿一下,去回忆最初的理想,去感受,去发现,换个角度思考问题,往往会有不一样的答案。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dbtxdxy/article/details/45679371
所属专栏: 猫猫的Unity Shader之旅
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭