美文网首页
Unity ShaderLab整理

Unity ShaderLab整理

作者: 爱吃鱼的鸡米 | 来源:发表于2021-02-06 00:04 被阅读0次

    预备知识

    • 模型文件存储结构(包含了顶点位置以及面的组成信息)


      模型文件

    渲染过程

    渲染管线的知识,很有必要去冯乐乐的书里看一下,在第9页( 2.3 GPU 流水线 )


    渲染流水线

    不同的渲染管线会有不同的差异,但是大体还是类似,要经过以上的一些步骤。
    流水线中,我简化了重点去理解:

    • 首先CPU把顶点数据传递给Shader的顶点着色器(在Unity中就是通过Mesh Renderer组件),开始一次DrawCall。
    • GPU在几何阶段把三维的坐标通过自己定义的顶点函数,按照给定的规则映射到二维的屏幕坐标中。
    • 于是屏幕上就出现了很多个点,但是还没有连成闭合的区域,所以在三角形遍历的时候,就是通过插值的方式在各个点包围的三角形内部填充其他点的信息。
    • 片元着色器把三角形中的每个点给拿过来,通过自定义的规则,为每个像素填充颜色。
    • 填完颜色后,放入缓存,等待后续的测试,例如混合测试。(比如透明物体背后还有一个物体,那么两个物体的颜色怎么混合)。
    • 最后输出屏幕。

    可以用以下简化的图来表示,其中Vertex Processor和Pixel Processor中间还有重要的插值的一步,因为顶点的个数是远少于像素的个数的,那么这个输入输出该怎么对应?
    如果像素有多少个,至少顶点也要有多少个吧,所以多余的顶点的信息(包括位置,顶点法线,颜色等等)就是通过原本的顶点插值计算出来的。

    渲染过程简化
    • Shader是图形可编程方案的程序片段

    • 渲染管线是一种计算机从数据到最终图形程序的形象描述

    • 材质是商品,Shader是方法,贴图是材料

    • 图形语言包括:
      OpenGL的GLSL
      DirectX的HLSL
      NVIDIA的CG

    • GPU采用并行计算,因此在顶点处理程序中,所有顶点信息可以同时计算,并且耗费相同时间。

    • 渲染管线最终认识的还是Vertex ShaderFragment Shader,Unity发明的Surface Shader只是对这两种Shader的包装。

    • Vertex Shader中,包含顶点坐标转换顶点色顶点法线的计算等工作,例如把模型文件中的点的信息转换成屏幕上的坐标信息等。

    • 渲染的过程中,一些Unity的全局光照设置、主摄像机等等信息都会作为全局变量,供Shader的使用。

    • 每一个像素格都会根据顶点着色器Fragment Shader中的方法计算出结果


    ShaderLab 的基本结构

    • ShaderLab语言是对HLSL、CG等语言的封装。更准确的说,HLSL、CG等语言是以内嵌的方式存在于ShaderLab语言中的。所以当遇到内嵌的代码段的时候,其中的语法和其外面包裹着的ShaderLab的语法是不一样的。
    shader "name"{
        [Properties]
        SubShaders
        [Fallback]
    }
    
    ShaderLab

    以下的规则仅仅指ShaderLab的语法,不代表其中内嵌的CG、HLSL的语法。

    • ShaderLab中不区分大小写
    • ShaderLab中不需要分号
    • ShaderLab中必须至少要有一个SubShader
    • 由于硬件不同,因此可以有多个SubShader,如果第一个SubShader不支持该硬件,那么就会顺序执行第二个SubShader
    • 如果所有的SubShader都不能执行,那么在Fallback中就会执行最后处理,通常在这里的处理用的都是Unity内建的一些通用的基础Shader
    Unity内置基础Shader举例

    先从连连开ShaderForge开始

    • 这里的连连看插件用的是Shader Forge,但是现在已经不更新了,最后更新是在2018的版本中,但是也可以用。
    • Shader Forge最后生成的Shader代码没有Shader Graph那么多,稍微方便一点,所以先用这个。

    用ShaderForge实现兰伯特光照

    兰伯特光照是最基础的漫反射模型,其效果如下

    Lambert光照模型
    在兰伯特光照中,很明显,朝向光的点最亮(灰度为1),背向光的点最暗(灰度为0),中间则介于0~1之间。
    通过光的反方向lDir某个点的朝向(该点法向量)nDir的接近程度,就可以得知该点是否面朝光
    刚好,数学上的点乘Dot刚好可以用于表示两个向量方向的接近程度,数值越大,两者方向越接近。(两个向量前提都经过了归一化处理)
    点乘的定义是:a·b = |a||b|cosθ
    当某点面朝光时,θ = 0,那么cosθ = 1。最后点乘结果也是等于1。
    模型上的每个点都进行一次 灰度值 = a·b 的运算,就可以得到整个模型经过漫反射之后的外观。

    通过上面的描述,我们首先需要获取两个重要的量,一个是全局光照的方向IDir,一个是几何体上的某点的法向量nDir。打开ShaderForge,空白处右键:

    光向量数据 法向量数据

    这两个数据一个在Lighting下,是全局的,一个在Geometry Data下。


    ShaderForge节点

    对它们进行点乘,点乘节点位于向量运算Vector Operations里。


    点乘
    连接节点

    点击左上角Compile Shader就可以将ShaderForge中的连接编译成shader代码。

    选中创建的shader右键create->material,创建材质。


    创建材质

    将材质赋予游戏物体,得到最后效果。


    最终样貌

    Fixed Function Shader( 了解 )

    参考文章:
    https://blog.csdn.net/weixin_30699831/article/details/95653586

    固定命令的Shader,可以在所有硬件平台上运行,但是功能有限。

    固定渲染管线中,主要包含以下代码块:


    image.png
    Shader "Custom/s01"
    {
        Properties
        {
            _Color ("Color", Color) = (1,1,1,1)
            _Ambient("Ambient",Color) = (0.3,0.3,0.3,0.3)
            _Specular("Specular",Color) = (1,1,1,1)
            _Shininess("Shininess",Float) = 0.3
            _Emission("Emission",Color) = (1,1,1,1)
        }
        SubShader
        {
            Pass{
                Material{
                    Diffuse[_Color]
                    Ambient[_Ambient]
                    Specular[_Specular]
                    Shininess[_Shininess]
                    Emission[_Emission]
                }
                Lighting on
                SeparateSpecular on
            }
        }
    }
    

    CG语法

    • 写在CGPROGRAM和ENDCG包裹的片段中,但是这两个关键字是ShaderLab的语法,不是CG的语法。
    • CG语言区分大小写
    • 通过#pragma vertex 顶点函数名声明顶点着色器
    • 通过#pragma fragment 片元函数名声明片元(像素)着色器
    • 通过#include "库函数名.cginc"声明外部引用的库
    数据类型
    • float4:4维向量,分量分别是xyzw。每个分量的占32bit,精确到小数点后6位。
    • half4: 4维向量,每个分量占16bit,表示范围为[-60000,60000],精确到小数点后3位。
    • fixed4:4维向量,分量分别是rgba。每个分量占11bit。表示范围为[-2,2],精度为1/256
      使用分量的时候,可以通过.x/.y/.z/.w/.xy/.xz等等,甚至还可以.xxyy。
      例如以下的赋值方式都合法的:
    float4 pos;
    float3 temp = pos.xyz;
    temp = pos.yzz;
    temp = float3(pos.yz,1);
    

    不可以将低维向高维转换,因为缺失了高维的数据。
    例如: float3(1,0,0) --x--> float4(1,0,0,?)
    可以将高维向低维转换,会直接舍弃高维数据。
    例如: float4(1,0,0,1) --√-->float3(1,0,0)

    • 坐标位置通常会用float4表示,由于float4类型的位置可以方便矩阵变换。所以需要将原本float3类型的位置信息扩散到齐次坐标空间。并且分量w可以用于表示该坐标是一个点,还是一个向量。
      点,用float4表示,就是float4(x,y,z,1)。
      向量,用float4表示,就是float4(x,y,z,0)。
    • 颜色通常会用fixed4来表示,因为颜色的表示不需要那么大的精度
    • 齐次坐标空间:将一个原本是n维的向量用一个n+1维向量来表示
    各种语义的意义和范围
    1. Unity中Renderer组件传递给顶点着色器的关联语义
      POSITION : 模型空间(相对于模型自身)的顶点坐标,float4类型。分量xyz可用于表示的范围为[-0.5,0.5],分量w的取值为0或1,0代表点,1代表向量。
      NORMAL : 顶点法线,float3类型
      TANGENT :顶点切线,float4类型
      TEXCOORDn :第n套纹理坐标,float2或float4类型
      COLOR : 顶点颜色,fixed4或float4类型

    2. 顶点着色器输出数据的语义
      SV_POSITION :剪裁空间(屏幕空间)下的坐标。左下角为原点(0,0) 横轴的范围是(0,屏幕宽度) 纵轴的范围是(0,屏幕高度)。结构体中必须包含一个用该语义修饰的变量。
      COLOR0 : 通常用于输出第一组顶点颜色,但不是必需的
      COLOR1 : 通常用于输出第二组顶点颜色,但不是必需的
      TEXCOORD0~TEXCOORD7 : 通常用于输出纹理坐标,但不是必需的

    3. 片元着色器输出数据的语义
      SV_TARGET : 最终输出屏幕的颜色值,fixed4或float4类型。有时候这个语义可以写成COLOR,但是由于SV_TARGET在各个平台上都容易被识别,所以还是选用这个。

    相关文章

      网友评论

          本文标题:Unity ShaderLab整理

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