美文网首页
Unity3D Shader教程五 Surface Shader

Unity3D Shader教程五 Surface Shader

作者: UnityAsk | 来源:发表于2019-07-31 20:28 被阅读0次

除了基础的vertex fragment Shader,Unity还给我们提供了另一种方式,只要我们定义一些参数,Unity将自动生成包含复杂光照计算的Shader,这种Shader被称为“Surface Shaders”.

在开始Surface Shaders 之前,最好参考我们之前的教程,先了解无光照Shader。

Result

Conversion to simple Surface Shader

下面我们基于上一节的 Shader 代码将它修改为 Surface Shader。删除我们的vertex 函数,删除掉 vertex 和 fragment 函数的 pragma定义,删除input 和 v2f,删除掉MainTex_ST 变量 ,以及UnityCG的引用文件。移除掉Pass 标记,因为Unity会为我们生成Pass。现在我们的Shader变成了下面这样:

Shader "Tutorial/005_surface" {
    Properties {
        _Color ("Tint", Color) = (0, 0, 0, 1)
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        CGPROGRAM

        sampler2D _MainTex;
        fixed4 _Color;

        fixed4 frag (v2f) : SV_TARGET {
            fixed4 col = tex2D(_MainTex, i.uv);
            col *= _Color;
            return col;
        }
        ENDCG
    }
    FallBack "Standard"
}

现在它还不能正常使用,让我们来添加一些东西,使其成为能正常工作的surface shader。

首先我们添加一个结构体Input,它将包含用来设置surface颜色的所有信息。在这里,它仅仅包含uv 坐标信息,声明一个 float2 类型的变量,将它命名为 uv_MainTex,注意这个名称非常重要,它自动包含我们tiling和offset信息。如果我们使用别的名称,需要用对应的 uvTextureName 以便获得贴图的uv信息。

struct Input {
    float2 uv_MainTex;
};

修改 fragment 函数为surface 函数,重命名为surf,修改返回类型为void。
两个参数,第一个为我们上面声明的Input结构体的实例,这样我们能获得每个顶点的信息。第二个参数SurfaceOutputStandard,用来输出surf函数结果,声明参数类型为inout。它包含了unity用来进行计算光照的所有信息。光照计算是基于物理的,后面我们将会解释。

删除函数的 sv_target 属性,因为unity也会自动帮我们处理。

最后需要删除 return 语句,因为上面返回值已经改为了void。设置输出部分的Albedo为贴图颜色乘上_Color。

void surf (Input i, inout SurfaceOutputStandard o) {
    fixed4 col = tex2D(_MainTex, i.uv_MainTex);
    col *= _Color;
    o.Albedo = col.rgb;
}

然后需要像之前的教程中讲的 vertex 和 fragment shader一样,进行pragma声明。
以 #pragma 开头,后面跟着的是shader类型(surface),surface 函数的名称(surf),以及光照模型(Standard)。

现在到Unity中查看,它已经能正确显示,并且处理了光照信息。

Shader "Tutorial/005_surface" {
    Properties {
        _Color ("Tint", Color) = (0, 0, 0, 1)
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        CGPROGRAM

        #pragma surface surf Standard fullforwardshadows

        sampler2D _MainTex;
        fixed4 _Color;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input i, inout SurfaceOutputStandard o) {
            fixed4 col = tex2D(_MainTex, i.uv_MainTex);
            col *= _Color;
            o.Albedo = col.rgb;
        }
        ENDCG
    }
}

Simple Albedo Material

我们来看下 SurfaceOutputStandard 的属性:

  • Albedo
    Albedo 漫反射颜色

  • Normal
    切线空间法线

  • Emission
    自发光颜色

  • Metallic
    金属度;取0为非金属, 取1为金属

  • Smoothness
    光泽度;取0为非常粗糙, 取1为非常光滑

  • Occlusion
    遮挡(默认值为1)

  • Alpha
    透明度

现在让往shader中设置一些上面的属性值,emission,metallic 和 smoothness。

首先添加 两个标量类型 ,这里为half。

half _Smoothness;
half _Metallic;

然后把它们添加到Properties里面,这样可以在Inspector中修改它们的值。Properties不支持half,所以声明为float类型。

Properties {
    _Color ("Tint", Color) = (0, 0, 0, 1)
    _MainTex ("Texture", 2D) = "white" {}
    _Smoothness ("Smoothness", float) = 0
    _Metallic ("Metalness", float) = 0
}

现在可以把 smoothness 和 metalness 赋值给 SurfaceOutputStandard 对应的属性。

void surf (Input i, inout SurfaceOutputStandard o) {
    fixed4 col = tex2D(_MainTex, i.uv_MainTex);
    col *= _Color;
    o.Albedo = col.rgb;
    o.Metallic = _Metallic;
    o.Smoothness = _Smoothness;
}

float 类型的属性,当改变值的时候,非常容易调整为大于1或者小于0,我们可以改为range,这样就可以把它的值限定在0和1之间。

Properties {
    _Color ("Tint", Color) = (0, 0, 0, 1)
    _MainTex ("Texture", 2D) = "white" {}
    _Smoothness ("Smoothness", Range(0, 1)) = 0
    _Metallic ("Metalness", Range(0, 1)) = 0
}

Inspector and Material with smoothness und metalness

最后我们添加 emissive 颜色。先在HLSL 代码中添加变量,然后添加对应的属性。

// ...

_Emission ("Emission", Color) = (0,0,0,1)

// ...

half3 _Emission;

// ...

o.Emission = _Emission;

Emissive Material

上面的颜色值最大值只能到1,如果想让它超过1,可以添加一个[HDR] 标记,这样亮度效果能更强。也可以把贴图作为emission color。

[HDR] _Emission ("Emission", Color) = (0,0,0,1)

HDR Inspector

Minor Improvements

最后,我们做两个小的操作,来让shader效果看起来更好一点。
首先,可以添加一个 fallback shader 在 subshader 后面,这样unity将会直接使用它,这样可以减少代码冗余。这里使用standard shader 作为fallback , unity 会使用里面的 shadow pass, 使material 向其他的物体上产生投影。
接着我们修改下pragma 命令,添加 fullforwardshadows ,这样能获得更好的影子效果。再添加一个#pragma target 3.0 ,这样untiy将使用更精确的数值,使效果更好一些。

Shader "Tutorial/005_surface" {
    Properties {
        _Color ("Tint", Color) = (0, 0, 0, 1)
        _MainTex ("Texture", 2D) = "white" {}
        _Smoothness ("Smoothness", Range(0, 1)) = 0
        _Metallic ("Metalness", Range(0, 1)) = 0
        [HDR] _Emission ("Emission", color) = (0,0,0)
    }
    SubShader {
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        CGPROGRAM

        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        sampler2D _MainTex;
        fixed4 _Color;

        half _Smoothness;
        half _Metallic;
        half3 _Emission;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input i, inout SurfaceOutputStandard o) {
            fixed4 col = tex2D(_MainTex, i.uv_MainTex);
            col *= _Color;
            o.Albedo = col.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Smoothness;
            o.Emission = _Emission;
        }
        ENDCG
    }
    FallBack "Standard"
}

Result

通过今天的教程,你应该已经明白了如何写一个具有看起来还不错的光照效果的shader了。

相关文章

网友评论

      本文标题:Unity3D Shader教程五 Surface Shader

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