Jenocn
1906 字
10 分钟
Unity表面着色器笔记(一)
2019-06-17

对Shader是又喜欢又苦恼,几年来,一直断断续续的接触又放弃,因为对于Shader,要学的东西太多太多了,它不像写逻辑,再复杂的功能也能一点点攻克,它是真的让我感觉到脑袋不够用了,是硬伤.但是,掌握Shader很有必要,所以我又双叒叕来看Shader了,而这次,得利于Unity的表面着色器,让Shader入门简单了一些,嗯,看了一周,做点笔记.

本文:

  • Shader
  • UnityShader
  • UnitySurfaceShader
  • 一个改变颜色的小例子

Shader#

简单来说Shader就是一段程序,可以操作顶点像素.我们知道计算机中图形是由三角形组成的,一个三角形有三个点,顶点指的就是这三个点,而像素不用多说,都懂,Shader就是可以操作这些东西的位置颜色,从而实现很多效果.

目前有三大Shader语言 GLSL是OpenGL支持的Shader语言 HLSL是微软DirectX支持的Shader语言,而DX只支持Windows设备,所以不具备跨平台性 CG是微软和Nvidia合作开发的Shader语言,它支持跨平台,非常类似HLSL

UnityShader#

Unity中的ShaderLab主要基于CG语言.

Unity中的Shader分为三类: 固定功能着色器对应旧式的固定渲染管线,这里不考虑了,基本上用不到 顶点/片元着色器就是通常的Shader 表面着色器是Unity中特有的,可以理解为是对顶点/片元着色器的再一次抽象,Unity最终会将其编译为顶点/片元着色器代码,它简单一些,也更适合作为入门来学习Shader

UnitySurfaceShader#

我还是比较推荐从表面着色器开始学习的,一方面简单一些,另一方面可以更快的写出一些不错的效果,并且使用在自己的项目上.我也正是在学习这个,当然,最终目标还是要掌握顶点/片元着色器的.

下面就开始详细讲解表面着色器了:

新建一个表面着色器代码文件

在Unity的Project视窗下新建一个Shader文件.

新建

打开文件,可以看到Unity生成的一堆模板代码,有一堆,莫慌,下面就来拆解它.

Shader结构#

忽略中间的代码,关注到第一行的结构,如下:

Shader "Custom/NewSurfaceShader" {
    ...
}

在UnityShader中,所有的Shader代码都被包括在这里面,跟在Shader关键词后面双引号中的内容表示这个Shader在Unity视窗里选择Shader时的路径,如下:

2 3

  • Properties
Properties {
    ...
}

Properties中用来定义一些输入属性,即一些参数,这些参数可以在后面的代码中使用,作为外部调节具体细节的变量,如果没有变量,也可以不写,内部属性的定义后面再说

  • SubShader
SubShader {
    ...
}

SubShader就是具体实现效果的部分,可以有多个SubShader,当存在多个时,会按顺序从上到下检测条件,直到找到一个符合条件可以执行的SubShader然后执行

  • FallBack
FallBack "Diffuse"

如果所有的SubShader都不满足条件执行,那么会执行后面的FallBack中指定的内置Shader, 这里的"Diffuse"表示一个内置Shader的名称,Diffuse即漫反射

SubShader结构#

  • Tags
Tags {
    "RenderType"="Opaque"
    /*
    "Name1"="Value1"
    "Name2"="Value2"
    ...
    */
}

此项可以不写,Tags就相当于一个标签,给这个SubShader指定的标签,这个暂时不关心,忽略就好

对于"RenderType"="Opaque"的含义,可以简单理解为不透明的渲染方式,但实际上这个生效的意义需要配合额外的代码控制

  • LOD 此项可以不写,LOD用来表示这个SubShader的细节层次,这里使用默认的200就好,或者可以不写,暂不关心.

实际上这个值也要配合额外的代码才会生效,例如Shader.globalMaximumLOD = 100;,则表示设置当前全局Shader的LOD小于等于100的才会被执行,这个值若不设置默认为int.MaxValue

  • CGPROGRAM 和 ENDCG

重点部分来了,CGPROGRAMENDCG是一组关键词,就像C#中的#region#endregion一样,被包括的部分正是我们需要实现各种效果的CG代码

CGPROGRAM
    ...
ENDCG

对于这里的内容,后面结合一个小例子来讲.

到这里,整个Unity的Shader结构就搞清楚了,下面贴一个结构部分的整体代码:

Shader "Custom/ShaderDemo1" {
    Properties {
    }
    SubShader {
        // Tags { }
        // LOD 200
        CGPROGRAM
        ENDCG
    }
    FallBack "Diffuse"
}

一个改变颜色的小例子#

到这里,已经迫不及待想要自己写一个试试了,那么开始吧.我们自己新建一个空白文本来写Shader,这样利于学习,思路清晰.下面就一步一步来写一个最最最基础的改变颜色的着色器,以此来开启大门

完整代码,下面将在注释上讲解:

Shader "Custom/ShaderDemo1" {
    Properties {
        /**
        这里是定义了一个属性,它的定义方式是 `变量名("说明", 类型) = 默认值`
        这里表示定义了一个Color类型的变量_Color, 默认RGBA是(1, 1, 1, 1)
        */
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader {
    // =============================================================
        CGPROGRAM
        /**
        这是一个预编译命令,用于指明这是一个`表面着色器`
        `surface`表示这是一个`表面着色器`
        `surf`用于声明调用的函数
        `Lambert`表示采用内置默认的Lambert光照模型,详情可以自行搜索`Lambert光照模型`
        */
        #pragma surface surf Lambert

        /**
        这是一个结构体,上面声明的`surf`函数需要一个输入的参数正是这个结构体,里面的成员变量是可变的
        `float2`表示两个float组成的结构体,可以通过下标访问,也可以通过`.x`,`.y`,`.z`或者`.r`,`.g`,`.b`这种形式来访问
        `uv_MainTex`这个成员变量表示纹理坐标,`uv`作为打头是必须的,表示它是一个纹理坐标
        这个结构体在这个例子中并没有用到,我们只要定义它就好了,可以暂时不关心
        */
        struct Input {
            float2 uv_MainTex;
        };
        
        /**
        这里需要再定义一下这个变量,变量名与上面`Properties`中一样,这样就会获得到属性中设置的值
        这里是必须再定义一下的,否则是获取不到上面`Properties`中的值的
        */
        fixed4 _Color;

        /**
        这个正是上面我们指定的函数`surf`
        参数1:是输入的数据,它会将当前使用这个Shader的物体上的纹理坐标传入过来
        参数2:`SurfaceOutput`是一个输出的数据结构,是Unity内置的,描述了最终显示需要的颜色和数据
        所以,我们根据`Input`数据,计算出想要的结果给`SurfaceOutput`
        */
        void surf(Input IN, inout SurfaceOutput o) {
            /**
            Albedo是`SurfaceOutput`中的成员变量,表示反射的颜色,这里简单理解为物体的颜色
            `_Color.rgb`表示将`rgb`的值作为最终显示的颜色,`Albedo`不包含透明
            */
            o.Albedo = _Color.rgb;
            // Alpha 表示透明值
            o.Alpha = _Color.a;
        }
        ENDCG
    // =============================================================
    }
    FallBack "Diffuse"
}

到这里,一个Shader就写完了,Shader需要结合材质一起才能看到效果,在Unity中创建一个Cube,然后在Project下新建一个Material材质文件,将Shader拖到Material上,将Material拖到Cube上,就可以看到效果了,会有一个颜色选择框,可以调节颜色.

Shader的路还很长,耐心并坚持,加油.


下一篇:Unity表面着色器笔记(二)

Unity表面着色器笔记(一)
https://jenocn.github.io/posts/code/unity/surfaceshadernote1/
作者
Jenocn
发布于
2019-06-17
许可协议
CC BY-NC-SA 4.0