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"
}
在一个基本模型里,一个物体表面的颜色是由放射(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