Shader的基础知识

管线渲染

定义:图形数据在GPU上经过运算处理,最后输出到屏幕的过程

1. 顶点处理: 接收模型顶点数据、坐标系装换

2. 图元装配: 组装面、连接相邻的顶点,绘制为三角面

3. 光栅化 :计算三角面上的像素,并为后面着色阶段提供合理的插值参数(以及深度值)

4. 像素处理: 对每个像素区域进行着色、写入到缓存

5.  缓存:一个存储像素数据的内存块,最重要的缓存是帧缓存与深度缓存

 帧缓存:存储每个像素的色彩(缓冲)

 深度缓存Z-buffer:前后排序(深度信息,物体到摄像机的距离)

6. Draw Call  绘制调用:每帧调用显卡渲染物体的次数

什么是缓冲?

一个像素有如下缓冲

颜色缓冲Color buffer/prixel buffer: 储存该点即将显示的颜色,RGBA值

深度缓存 depth buffer/Z buffer: 储存该点的深度Z

模板缓存stencil buffer  : 通常作用限制渲染区域。更高级用法需结合深度缓存,例如某像素的模板缓冲值会随着其是否通过深度缓冲测试而改变

什么是图元装配(Primitive Assembly)

经过变换的顶点 被装配成几何(三角形等)图集

什么是光栅化(栅格化)(Rasterization)

栅格化这个术语:可以用于任何将矢量图形转化成栅格图像的过程

在3D渲染中主要是指,三角形等图元(矢量)转化成像素碎片的过程。或者说决定哪些像素几何图元覆盖的过程。栅格化的结果是像素位置的集合和片段的集合

什么是光栅操作(Raster Operation)

指在碎片fragment处理后,在更新帧缓存前最后执行的一系列操作。通过包括裁剪,深度测试,alpha测试,alpha混合等。

Shader常见术语

Alpha 透明的(黑透白不透、灰色半透半不透)

Bump凹凸 (法线贴图)

Specular (高光)

Shader主要看Game视窗

Shader显示名称与Shader名不冲突

什么是GPU?

GPU:Graphic Processing Unit,中文翻译为“图形处理器”。显卡包括(GPU,显存,显卡BLOS,显卡PCB板)

什么是Shader?

Shader程序:GPU执行的,针对3D对象进行操作的程序

Shader编程有那几种?

CG:与DirectX 9.0 以上以及OpenGL 完全兼容。运行时或事先编译成GPU汇编代码

HLSL: 主要用于Direct3D。平台:Windows

GLSL: 主要用于OPenGL。 平台:移动平台(IOS,Android),Mac(only use when you target Mac OS X or OpenGL ES 2.0)

为什么Shader中选择CG?

因为CG/HLSL 比GLSL支持更多的平台。

Unity3d 里CG输出什么?

Windows平台:Direct3D,GPU汇编代码

Mac: OpenGL GPU汇编代码

Flash: Flash GPU 汇编代码

IOS/Android : unity 会将CG转换成GLSL代码。

总结:也就是除了移动平台会把CG转换成GLSL代码,其余平台都是转化成汇编代码。

 

Unity 中的三种自定义Shade:

1.surface shaders, 表面着色器(最常用,比固定功能管线高级)(之前默认创建的shader类型)它是 Vertex and fragment shaders 的包装,让我们可以不用关心这些顶点和片段程序的细节,可以直接得到我们想要的着色器。

2.Vertex and fragment shaders 顶点和片元着色器(细节处理,偏底层)

3.fixed function shaders.  固定功能管线着色器(更简单),在可编程渲染管线硬件出现之前,很多光照都会放在硬件级处理(可以理解为对固定管线硬件的操作),一般放在项目前绝大多数硬件都可支持,应用就可以使用,比如光照、纹理采样

//Shader中没有注明是前两种shader,即为第三种shader

先从ShaderLab基本语法开始入手,再去阅读surface shader,或者vertex and fragment shaders

ShaderLab 基本结构

Unity中的Shader 都是要通过ShaderLab的基本语法进行编写,unity就是想通过Shaderlab的方案进行Shader的编写。将三种定义的Shader通过同一种格式进行编写,避免不同Shader使用不同的方法。

 

Shader "name"
{
    //[Properties] 属性 查看ShaderLab:Properties 
    //作用:在可视化面板提供美工可使用的属性
	Properties  
	{
		
	}
    //[Subshader] 算法用于执行给定的数据
	SubShader
	{
		
	}
    //[FallBack] 后退 一般会填写所有硬件都支持的渲染方式
    FallBack "Diffuse"
}

关于SubShaders (处理ShaderLab中的语言片段) 在ShaderLab中至少有一个SubShader,当然也可多个。但是,显卡每次渲染处理的时候只能选择一个SubShaders执行。那多个SubShader的作用是为了不同的硬件的渲染支持,为了Shader能在比较老的图形显卡中也能支持。一般比较往下的Subshader要简化,运算指令要简单。

 

Fixed function shader固定功能管线

所有硬件平台都可支持,针对硬件能够执行的基本命令的Shader,当然有,但是,速度最快

Properties 属性

Material 材质

Lighting 光照

Settexture  设置纹理

Pass通道(存储图像的色彩RGB)(只有Surface 可以不写)

Surface shaders

surfaceOutPut输出

Input 输入

Lighting 光照

常用公式

环境反射

Ambient= Ka*globalAmbient;

Ka是材质的环境反射系数

globalAmbient是入射环境光的颜色

 

漫反射

Diffuse=Kd*lightColor*max(N*L,0);

Kd 是材质的漫反射颜色

lightColor是入射漫反射光的颜色

N 是规范化的表面法向量

L 是规范化的纸箱光源对的向量;

镜面反射

Specular=Ks * lightColor *facing*max((N*H),0) shininess

Ks 是材质的镜面反射颜色;

lightColor是入射镜面反射光的颜色

N是规范化的表面法向量

V是指向视点的规范化的向量

L是指向光源的规范化的向量

H是V和L的中间向量的规范化向量

P是要被着色的点

Faceing是1如果N*L是大于0的,否者为0

写Shader不区分大小写

和灯光有呼应的  ------ 漫反射  高光   背光    阴影   环境光

固定着色器格式

hader "Unlit/Test2"//不区分大小写
{
    //声明属性
	Properties
	{
        //默认颜色颜色
        //变量名    在监视面板显示的名称    类型=值
		_Color("MyColor",Color)=(1,0,0,1)
        //环境光
        _Ambient("Ambient",Color)=(1,0,0,1)
        //高光
        _Specular("Specular",Color)=(1,0,0,1)
        _Shininess("Shininess",Range(0,1))=0.5 //range 0-8的值
        //自发光
        _Emission("Emission",Color)=(1,0,0,1)
        //
        _MainTexture("Main Texture",2D)="white"{}
        //默认颜色

	}
	SubShader
	{

    pass
    {

		Color[_Color]   //使用默认颜色进行颜色的写入
      
        Material{
            //漫反射
            Diffuse[_Color]
            //环境光
            Ambient[_Ambient]
            //亮度
            Shininess[_Shininess]
            //高光反射系数
            Specular[_Specular]
            //自发光
            Emission[_Emission]
        }
        //启动光照
        Lighting On 
        //启动高光反射
        SeparateSpecular On
        SetTexture[_MainTexture]
        {
          Combine Texture*primary double
        }
	}
    }
    FallBack "Diffuse"
}


表面着色器格式

Shader "Custom/Test1" {
    //属性 
    //属性变量
	Properties {
         //变量名  面板中的名字  类型
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0
	}
    //算法
	SubShader {
		Tags { "RenderType"="Opaque" } //描述渲染类型,当前是不透明的物体
        //Tags{"RenderType"="Opaque" "queue"="transparent"}//透明的物体
		LOD 200  //层级细节
		
		CGPROGRAM  //CG代码块 CG开始
		// Physically based Standard lighting model, and enable shadows on all light types
        //如果是Standard fullforwardshadows光照模型,则对应用SurfaceOutputStandard,
        //如果是Lambert光照模型(其他版本Unity默认的光照模型),则对应SurfaceOutput。
        //对应的SurfaceOutputStandard/SurfaceOutput结构体不用写出来
        #pragma surface surf Standard fullforwardshadows//编译指令
        //surface表面着色器 surf调用的方法 Standard基于物理的光照模型(原来是漫反射 Lambert)
        //fullforwardshadows 阴影表现(平行光、点光、聚光都是有阴影的)
        // Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 3.0  //GPU硬件支持3.0 (不写默认是2.0)

		sampler2D _MainTex; //CG中图片的类型

		struct Input {
			float2 uv_MainTex; //记录uv纹理坐标
		};

		half _Glossiness; //CG中的浮点型
		half _Metallic;   
		fixed4 _Color;    //CG中的四阶向量(0,0,0,0)rgb

		// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
		// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
		// #pragma instancing_options assumeuniformscaling
		UNITY_INSTANCING_CBUFFER_START(Props)
			// put more per-instance properties here
		UNITY_INSTANCING_CBUFFER_END
        //Input 传入值    inout 传出值(基于物理的要加Standard)
		void surf (Input IN, inout SurfaceOutputStandard o) {
			// Albedo comes from a texture tinted by color
			fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			// Metallic and smoothness come from slider variables
			o.Metallic = _Metallic;  //金属光泽表现
			o.Smoothness = _Glossiness; //高光光泽度
			o.Alpha = c.a;
		}
		ENDCG //CG结束
	}
	FallBack "Diffuse"
}

表面着色器示例练习

Shader "Custom/Test5" {
	Properties {
        //2D ---图片    white----默认格式  “”内也可以不写
        //大括号也可以不写(大括号内可以给值)
		_MainTex("Texture",2D)="white"{}
        _BumpMap("Bumpmap",2D)="bump"{}
        //cube 立方体贴图
        _Cube("Cubemap",CUBE)=""{}
	}
	SubShader {
        //不透明
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
        //如果是Standard fullforwardshadows光照模型,则对应用SurfaceOutputStandard,
        //如果是Lambert光照模型(其他版本Unity默认的光照模型),则对应SurfaceOutput。
        //对应的SurfaceOutputStandard/SurfaceOutput结构体不用写出来
        #pragma surface surf Lambert

		// Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 3.0

		struct Input {
            //只要是纹理相关都需要UV
			float2 uv_MainTex;
            float2 uv_BumpMap;
            float3 worldRef1;
            INTERNAL_DATA  //反射与法线效果配合
		};

        //再次声明变量
        sampler2D _MainTex;
        sampler2D _BumpMap;
        samplerCUBE _Cube;


		// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
		// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
		// #pragma instancing_options assumeuniformscaling
		UNITY_INSTANCING_CBUFFER_START(Props)
			// put more per-instance properties here
		UNITY_INSTANCING_CBUFFER_END

		void surf (Input IN, inout SurfaceOutput o) {
			o.Albedo=tex2D(_MainTex,IN.uv_MainTex).rgb;
            //不需要法线的反射效果
            //o.Emission=texCUBE(_Cube,IN.worldRefl).rgb;
            //法线与反射效果配合
            //需要将INTERNAL_DATA添加到Input结构中
            o.Normal=UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
            //反射
            o.Emission=texCUBE(_Cube,WorldReflectionVector(IN,o.Normal)).rgb;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

CG之基本光照模型计算公式

在一个基本模型里,一个物体表面的颜色是由放射(emissive)、环境反射(ambient)、漫反射(diffuse)和镜面反射(specular)等光照作用的总和。每种光照作用取决于表面材质的性质(如亮度和材质颜色)和光源的性质(如光的颜色和位置)的共同作用。

从数学上描述基本模型的高级公式如下所示:

surfaceColor = emissive +ambient + diffuse + specular

一、放射项

emissive = Ke

其中:

Ke代表材质的放射光颜色。

二、环境反射项

ambient = Ka * globalAmbient

其中:

Ka是材质的环境反射系数。

globalAmbient是入射环境光的颜色。

三、漫反射项

diffuse = Kd * lightColor * max(dot(N, L), 0)

其中:

Kd是材质的漫反射颜色。

lightColor是入射漫反射光的颜色。

N是规范化的表面法向量。

L是规范化的从顶点到光源的向量。

四、镜面反射项

specular = Ks * lightColor * facing * pow(max(dot(N, H), 0), shininess)

其中:

Ks 是材质的镜面反射颜色。

lightColor是入射镜面反射光的颜色。

N是规范化的表面法向量。

H是规范化的,顶点到光源的向量与顶点到眼睛的向量的中间向量。

facing是,如果dot(N,L)大于0则为1,否则为0。其中L是顶点到光源位置的向量。

shinniess是表面光泽度。

1 Shader "Custom/Test"
 2 {
 3     Properties
 4     {
 5         _Ke("Ke", Color) = (1,1,1,1)
 6         _Ka("Ka", Color) = (1,1,1,1)
 7         _GlobalAmbient("Global ambient", Color) = (1,1,1,1)
 8         _Kd("Kd", Color) = (1,1,1,1)
 9         _Ks("Ks", Color) = (1,1,1,1)
10         _Shininess("", float) = 1
11     }
12
13     SubShader
14     {
15         Pass
16         {
17             Tags
18             {
19                 "RenderType" = "Opaque"
20             }
21
22             CGPROGRAM
23             #pragma vertex Vert
24             #pragma fragment Frag
25
26             #include "UnityCG.cginc"
27             #include "Lighting.cginc"
28
29             uniform float4 _Ke;
30             uniform float4 _Ka;
31             uniform float4 _GlobalAmbient;
32             uniform float4 _Kd;
33             uniform float4 _Ks;
34             uniform float _Shininess;
35             
36             struct VertexInput
37             {
38                 float4 pos : POSITION;
39                 float2 uv : TEXCOORD0;
40                 float3 nor : NORMAL;
41                 float4 col : COLOR;
42             };
43
44             struct FragmentInput
45             {
46                 float4 pos : SV_POSITION;
47                 float2 uv : TEXCOORD0;
48                 float4 col : COLOR;
49             };
50
51             FragmentInput Vert(VertexInput vi)
52             {
53                 FragmentInput fi;
54                 fi.pos = mul(UNITY_MATRIX_MVP, vi.pos);
55                 fi.uv = vi.uv;
56
57                 // compute emissive
58                 float3 emissiveC = _Ke.rgb;
59
60                 // compute ambient
61                 float3 ambientC = _Ka.rgb * _GlobalAmbient.rgb;
62
63                 // compute diffuse
64                 float3 nor = mul(vi.nor, (float3x3)_World2Object);
65                 float3 dir2Light = normalize(WorldSpaceLightDir(vi.pos));
66                 float nl = max(0, dot(nor, dir2Light));
67                 float3 diffuseC = _Kd.rgb * _LightColor0.rgb * nl;
68
69                 // compute specular
70                 float3 dir2Cam = normalize(WorldSpaceViewDir(vi.pos));
71                 float nh = max(0, dot(nor, dir2Cam + dir2Light));
72                 float specLight = nl > 0 ? pow(nh, _Shininess) : 0;
73                 float3 specC = _Ks * _LightColor0.rgb * specLight;
74
75                 fi.col.rgb = emissiveC + ambientC + diffuseC + specC;
76                 fi.col.a = 1;
77
78                 return fi;
79             }
80
81             float4 Frag(FragmentInput fi) : Color
82             {
83                 return fi.col;
84             }
85
86             ENDCG
87         }
88     }
89 }

 

【Unity5.x Shaders】最基本的Surface Shader-Diffuse shader以及Surface中的三种输出结构

某些物体可能具有均匀的颜色和光滑的表面,但光滑程度不足以照射反射光。
这些哑光材料最好用Diffuse Shader。 在现实世界中,不存在纯diffuse materials
Diffuse shader经常出现在游戏中,下面就是一个简单的Diffuse shader代码

Shader "Custom/DiffuseShader" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)                     
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        struct Input {
            float2 uv_MainTex;
        };

        fixed4 _Color;

        void surf (Input IN, inout SurfaceOutputStandard o)
        {           
            o.Albedo = _Color.rgb;          
        }
        ENDCG
    }
    FallBack "Diffuse"
}

这是一个从Standard Shader改装的的Shader,它将使用基于物理的渲染去模仿灯光在模型上的行为。
如果你想实现一个非真实感(non-photorealistic)的外观,可以改变第一个预处理指令使用Lambert光照模型。
#pragma surface surf Lambert
如果使用了这个模型,surf函数中的输出结构SurfaceOutputStandard需要改成SurfaceOutput

Surface输出结构

Shader允许你通过Surface的输出结构将材质的渲染属性传递给光照模型。
不同的Surface光照模型需要不同的Surface输出结构

在Unity5中,有三种主要的输出结构:

SurfaceOutput

一般的光照模型例如Lambert光照模型

struct SurfaceOutput {  
    fixed3 Albedo;   //对光源的反射率
    fixed3 Normal;   //对应切线空间法线方向
    fixed3 Emission; //自发光
    half Specular;   //高光反射中的指数部分的系数。
    fixed Gloss;     //高光反射中的强度系数
    half Alpha;      //透明通道

SurfaceOutputStandard

标准光照模型 使用此结构输出

struct SurfaceOutputStandard {  
    fixed3 Albedo;  //材质的基础颜色(无论是漫反射还是高光)
    fixed3 Normal;   
    half3 Emission; //类型被声明为half3,注意在SurfaceOutput中被定义为fixed3
    half Alpha;      
    half Occlusion; //遮挡(默认为1)  
    half Smoothness;//光滑程度(0=粗糙, 1=光滑)
    half Metallic;  //0=非金属, 1=金属
  •  

SurfaceOutputStandardSpecular

标准镜面光照模型 使用此结构输出

struct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;      
    fixed3 Specular;    // 镜面反射颜色
    fixed3 Normal;     
    half3 Emission;
    half Smoothness;    
    half Occlusion;     
    fixed Alpha;      
};

注意:这里的Specular完全不同于SurfaceOutput中的Specular
它允许指定一个颜色而不是一个单值。

正确使用Surface Shader,是使Surface输出结构输出正确的值

参考资料 https://blog.csdn.net/leonardo_davinci/article/details/78869587