1.点积/Dot:
结果为标量 几何意义:几何向量在另一向量上的投影长度 结果的图形学表现为:两向量 方向相同时 结果=1(白色)方向相反 结果=-1(黑色),垂直 结果=0(黑色)
2.Lambert:
法线方向点乘光方向的反向 Max(0, nDIr * lDir)
halfLambert:(Max(0, nDIr * lDir)) * 0.5 + 0.5
3.映射:
4.Helloworld
Shader "Shader/FlatCol_SF" {
Properties {
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
struct VertexInput {
float4 vertex : POSITION;
};
struct VertexOutput {
float4 pos : SV_POSITION;
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex );
return o;
}
float4 frag(VertexOutput i) : COLOR {
return fixed4(finalColor,1);
}
ENDCG
}
}
FallBack "Diffuse"
}
5.Lambert_VS
Shader "Shader/Lambert_VS" {
Properties {
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
//输入结构
struct VertexInput {
float4 vertex : POSITION; //将模型顶点信息输入进来
float3 normal : NORMAL; //将模型法线信息输入进来
};
//输出结构
struct VertexOutput {
float4 pos : SV_POSITION; //由模型顶点信息换算而来的顶点屏幕位置
float3 nDirWS : TEXCOORD0; //由模型法线信息换算来的世界空间法线信息
};
//顶点shader
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; //新建一个输出结构
o.pos = UnityObjectToClipPos( v.vertex ); //变换顶点信息 并将其塞给输出结构
o.nDirWS = UnityObjectToWorldNormal(v.normal);//变换法线信息 并将其塞给输出结构
return o; //将输出结构 输出
}
//像素shader
float4 frag(VertexOutput i) : COLOR {
float3 nDir = i.nDirWS; //获取nDir
float3 lDir = normalize(_WorldSpaceLightPos0.xyz);//获取lDir
float nDotl = dot(nDir, lDir); //nDir点积lDir
float lambert = max(0.0, nDotl); //截断负值
return float4(lambert, lambert, lambert, 1.0); //输出最终颜色
}
ENDCG
}
}
FallBack "Diffuse"
}
6.漫反射和镜面反射
漫反射(Diffuse):漫无目的,四面八方,均匀散射
实现方式:Lambert(n dot l)
镜面反射(Specular):有目的的,沿镜面方向,不均匀的散射
实现方式:Phong(r dot v)Blinn-Phong(n dot h)
7.黑话
8.Power:一般叫高光次幂,正片叠底
9.面板参数定义
10.计算vDir需要世界顶点空间位置posWS
float4 posCS : SV_POSITION; //裁剪空间顶点位置
11.vDIr
12.四段法写像素shader
13.vDir求法:
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);
14.lDir求法:
float3 lDir = normalize(_WorldSpaceLightPos0.xyz);
15.hDir求法
float3 hDir = normalize(vDir + lDir);
16.AO图:
环境光遮罩(Occlusion)
17.环境光:
Ambient
18.采样贴图方法:
tex2D(_Texture, uv)
19.投影代码调用方法 Shadow:
#include "AutoLight.cginc"
#include "Lighting.cginc"
LIGHTING_COORDS(0,1)
TRANSFER_VERTEX_TO_FRAGMENT(o)
float shadow = LIGHT_ATTENUATION(i)
20.简化理解光照构成:
20.法线
Shader "Shader/NormalMap_VS" {
Properties {
_NormalMap ("法线贴图", 2D) = "bump"{}
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
//输入参数
uniform sampler2D _NormalMap;
//输入结构
struct VertexInput {
float4 vertex : POSITION; //将模型顶点信息输入进来
float2 uv0 : TEXCOORD0; //需要uv坐标,采样法线贴图
float3 normal : NORMAL; //将模型法线信息输入进来
float4 tangent: TANGENT; //构建法线空间需要模型切线信息
};
//输出结构
struct VertexOutput {
float4 pos : SV_POSITION; //由模型顶点信息换算而来的顶点屏幕位置
float2 uv0 : TEXCOORD0; //uv信息
float3 nDirWS : TEXCOORD1; //由模型法线信息换算来的世界空间法线信息
float3 tDirWS : TEXCOORD2; //世界空间切线信息
float3 bDirWS : TEXCOORD3; //世界空间副切线信息
};
//顶点shader
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; //新建一个输出结构
o.pos = UnityObjectToClipPos( v.vertex ); //变换顶点信息 并将其塞给输出结构
o.uv0 = v.uv0; //传递uv信息
o.nDirWS = UnityObjectToWorldNormal(v.normal);//世界空间下法线信息
o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz);//世界空间下切线信息
o.bDirWS = normalize(cross(o.nDirWS, o.tDirWS) * v.tangent.w); //世界空间下副切线信息
return o; //将输出结构 输出
}
//像素shader
float4 frag(VertexOutput i) : COLOR {
//获取nDir
float3 var_NormalMap = UnpackNormal(tex2D(_NormalMap, i.uv0)).rgb;//采样法线纹理并解码 切线空间nDir
float3x3 TBN = float3x3(i.tDirWS, i.bDirWS, i.nDirWS); //构建TBN矩阵
float3 nDir = normalize(mul(var_NormalMap, TBN)); //世界空间nDirWS
//获取lDir
float3 lDir = _WorldSpaceLightPos0.xyz;
//一般Lambert
float nDotl = dot(nDir, lDir);
float Lambert = max(0.0, nDotl);
return float4 (Lambert, Lambert, Lambert, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
21.菲涅尔
菲涅尔现象:真实世界中,除了金属之外其它物质,视线垂直于表面时, 反射较弱,而当视线非垂直表面时,夹角越小,反射越明显
算法:Fresnel = pow(1-ndotv,powVal)
• ldotv:理解为光从眼睛发出时的Lambert;
中间亮,边缘暗;
• 1-ndotv:黑白反相,中间暗,边缘亮;
• power:套一个power控制边缘亮的范围;
Shader "Shader/Fresnel_VS" {
Properties {
_PowVal ("边缘光强度", range(1, 5)) = 1
}
SubShader {
Tags {
"RenderType"="Opaque"
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
//输入参数
uniform float _PowVal; //边缘光强度
//输入结构
struct VertexInput {
float4 vertex : POSITION; //模型顶点信息
float3 normal : NORMAL; //模型法线信息
};
//输出结构
struct VertexOutput {
float4 posCS : SV_POSITION; //裁剪空间顶点信息
float4 posWS : TEXCOORD0; //世界空间顶点信息
float3 nDirWS : TEXCOORD1; //世界空间法线信息
};
//顶点shader
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0; //新建一个输出结构
o.posCS = UnityObjectToClipPos( v.vertex ); //把模型空间顶点信息 转换为 裁剪空间顶点信息
o.posWS = mul(unity_ObjectToWorld, v.vertex); //把模型空间顶点信息 转换为 世界空间顶点信息
o.nDirWS = UnityObjectToWorldNormal(v.normal);//把模型空间法线信息 转换为 世界空间法线信息
return o; //将输出结构 输出
}
//像素shader
float4 frag(VertexOutput i) : COLOR {
float3 nDir = i.nDirWS; //获取nDir
float3 vDir = normalize(_WorldSpaceCameraPos.xyz - i.posWS.xyz);//获取vDir
float nDotv = dot(nDir, vDir); //nDir点积lDir
float Fresnel = pow(1.0 - nDotv, _PowVal); //菲涅尔光照模型
return float4(Fresnel, Fresnel, Fresnel, 1.0); //输出最终颜色
}
ENDCG
}
}
FallBack "Diffuse"
}
22.Matcap
• 一种无视BRDF,将BRDF渲染结果,用View空间法线朝向,直接映射到模型表面的流氓算法;
• 常用来模拟环境反射;
算法:
1. 将nDir从切线空间转到观察空间;
2. 取RG通道Remap到(0~1),作为UV对Matcap图采样;
3. 叠加菲涅尔效果,以模拟金属和非金属不同质感;
23.CubeMap
简单理解就是:全景图
24.定义面板参数
[Header(XXX)] 排版
_MainTex ("RGB:基础颜色 A:环境遮罩", 2D) = "while"{} //默认参数要给对 参考默认值
_NormTex ("RGB:法线贴图", 2D) = "bump"{}
_SpecTex ("RGB:高光颜色 A:高光次幂", 2D) = "gray"{}
_EmitTex ("RGB:自发光贴图", 2d) = "black"{}
_Cubemap ("RGB:环境贴图", cube) = "_Skybox"{}
25.从法线贴图中采样切线空间的法线方向
float3 nDirTS = UnpackNormal(tex2D(_NormTex, i.uv0)).rgb;
解码:UnpackNormal()采样tex2D(贴图,i.uv)
26.TBN矩阵构
//顶点shadeer
o.nDirWS = UnityObjectToWorldNormal(v.normal); // 法线方向 OS>WS
o.tDirWS = normalize(mul(unity_ObjectToWorld, float4(v.tangent.xyz, 0.0)).xyz); // 切线方向 OS>WS
o.bDirWS = normalize(cross(o.nDirWS, o.tDirWS) * v.tangent.w); // 副切线方向
//像素shader
float3x3 TBN = float3x3(i.tDirWS, i.bDirWS, i.nDirWS);
//切线空间的法线方向和TBN乘可以得到世界空间的法线方向 必须归一化
float3 nDirWS = normalize(mul(nDirTS, TBN));
27.mul
向量与矩阵相乘mul
28.Cubemap采样
float3 var_Cubemap = texCUBElod(_Cubemap, float4(vrDirWS, lerp(_CubemapMip, 0.0, var_SpecTex.a))).rgb;
29.参数面板声明格式
30.ShaderLab中的参数类型
31.可访问的顶点Input数据
32.常用的顶点Otput数据
33.常用顶点Shader操作
34.库
35.彩色贴图:
创建彩色贴图时,格外注意物品的明暗度、调色板、渐变及纹理细节在游戏中对英雄的辨识度有何影响
36.透明贴图:
透明贴图决定纹理哪里不透明。白色为不透明,黑色为不可见。
透明贴图只能是完全不透明或完全不可见,并无灰阶。
37.法线贴图:
法线贴图能造成一种当光照在光滑网格上时,表面有凸起和凹陷的错觉。
红色通道为左、绿色为下,蓝色为上
38.金属度遮罩:
金属度遮罩能使颜色和边缘光减弱与变暗,以模拟真实世界的金属外观。
此遮罩能与基础色遮罩并用,后者通过镜面反射高光传回颜色。
使用此遮罩时结合其他遮罩可以让表面显得更具金属感。
通常这代表着上了基础颜色的、明亮且宽幅的高光。
39.自发光:
“自发光”决定表面何处与环境照明独立分开,自行发光。
这个遮罩可以使表面的像素全亮,并显示颜色纹理中的颜色。
40. 镜面反射遮罩
镜面反射遮罩决定高光的亮度,乘以来自材质的镜面反射强度。
环境光遮蔽贴图是此贴图的良好基础。此遮罩与镜面反射指数遮罩共同作用。
遮罩的镜面反射强度值会乘以材质的镜面反射比例值。 自定义物品会使用默认物品材质中的值。
大多数情况下,镜面反射比例是大于 1 的数字,如此可产生夸大的镜面反射效果。
41.边缘光:
边缘光是模型边缘的高光,赋予模型深度,让其从环境凸显出来。
也称为菲涅耳强度或边缘光强度。
遮罩的边缘光强度值会乘以材质的边缘光比例值。
自定义物品会使用默认物品之材质中的值。
大多数情况下, 边缘光比例是大于 1 的数字,如此可产生夸大的边缘光效果。
42.基础色调遮罩
基础色调遮罩决定了镜面反射高光从颜色纹理中获得多少颜色。 它适用于有色金属(如黄金),或模拟光线穿透表面并反射回带着颜色的光,使表面显得更加真实。
亮的明暗度可使镜面反射高光保留影响表面的光线颜色
43.镜面反射指数
镜面反射指数值决定表面上镜面反射高光的大小。
明暗度低会形成表面粗糙的印象。
亮的明暗度会形成表面磨光的印象。
镜面反射遮罩与镜面反射指数共同作用,可创造不同类型的表面。 范例包括:
-
- 金属 - 高镜面反射,中指数
- 皮革 - 中镜面反射,高指数
- 木料 -低镜面反射,极低的指数
此遮罩中的明暗度有为材质中镜面反射指数值充当比例尺的作用。 若整个通道为白,则整个表面会承袭来自材质的镜面反射指数值。
44.Cubemap采样方法
half3 var_Cubemap = texCUBElod(_Cubemap, float4(vrDirWS, lerp(8.0, 0.0, var_MaskTex.a))).rgb;//采样坐标是一个四维的
45.透明剪切
[HideInInspector]
_Color ("Main Color", Color) = (1.0, 1.0, 1.0, 1.0)
uniform half3 _Color;
clip(opacity - _Cutoff);
46.投影不正常
FallBack "Legacy Shaders/Transparent/Cutout/VertexLit"
47.双面问题
Cull Off
48.特效
透:AB、AD、AC、自定义混合方式
动:
参数动画
UV动画:UV流动、UV扰动、序列帧动画
顶点动画:顶点位置动画、顶点颜色动画
映:极坐标、屏幕坐标UV、透明扭曲
49.透明剪切 AlphaCutout AC
用途:常用于复杂轮廓,明确边缘的物体表现,如:镂空金属,裙摆边缘,特定风格下的头发,树叶等;
卡通渲染的特效表现
优点:没有排序问题
缺点:边缘效果太实
移动端性能较差
Shader "Shader/AC" {
Properties {
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
_Cutoff ("透贴阈值", range(0.0, 1.0)) = 0.5
}
SubShader {
Tags {
"RenderType"="TransparentCutout" //对应改为Cutout
"ForceNoShadowCasting"="True" //关闭阴影投射
"IgnoreProjector"="True" //不响应投射器
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
//输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform half _Cutoff;
struct VertexInput {
float4 vertex : POSITION; //顶点位置,总是必要的
float2 uv : TEXCOORD0; //UV信息 采样贴图用
};
struct VertexOutput {
float4 pos : SV_POSITION; //顶点位置,总是必要的
float2 uv : TEXCOORD0; //UV信息 采样贴图用
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex ); //顶点位置OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); //UV信息 支持TillingOffset
return o;
}
float4 frag(VertexOutput i) : COLOR {
half4 var_MainTex = tex2D(_MainTex, i.uv);//采样贴图 RGB颜色 Atoutie
clip(var_MainTex.a - _Cutoff); //透明剪切
return half4 (var_MainTex.rgb, 1.0); //返回值
}
ENDCG
}
}
FallBack "Diffuse"
}
50.透明混合 AlphaBlend AB
用途:常用于复杂轮廓,无明确边缘的物体表现;
常用于半透明的物体表现;
一般的特效表现,打底用
优点:移动端性能较好
边缘效果好
缺点:有排序问题
Shader "Shader/AB" {
Properties {
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
}
SubShader {
Tags {
"Queue"="Transparent" //调整渲染序列
"RenderType"="Transparent" //对应改为Transparent
"ForceNoShadowCasting"="True" //关闭阴影投射
"IgnoreProjector"="True" //不响应投射器
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
Blend One OneMinusSrcAlpha //修改混合方式One/SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
//输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
struct VertexInput {
float4 vertex : POSITION; //顶点位置,总是必要的
float2 uv : TEXCOORD0; //UV信息 采样贴图用
};
struct VertexOutput {
float4 pos : SV_POSITION; //顶点位置,总是必要的
float2 uv : TEXCOORD0; //UV信息 采样贴图用
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex ); //顶点位置OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); //UV信息 支持Tilling Offset
return o;
}
float4 frag(VertexOutput i) : COLOR {
half4 var_MainTex = tex2D(_MainTex, i.uv);//采样贴图 RGB颜色 Atoutie
return var_MainTex; //返回值
}
ENDCG
}
}
FallBack "Diffuse"
}
51.透明叠加 Addtive AD
用途:常用于发光体,辉光的表现;
一般的特效表现,提亮用;
问题:
有排序问题
多层叠加容易爆性能(OverDraw)
作为辉光效果,通常可用于后处理代替
Shader "Shader/AD" {
Properties {
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
}
SubShader {
Tags {
"Queue"="Transparent" //调整渲染序列
"RenderType"="Transparent" //对应改为Transparent
"ForceNoShadowCasting"="True" //关闭阴影投射
"IgnoreProjector"="True" //不响应投射器
}
Pass {
Name "FORWARD"
Tags {
"LightMode"="ForwardBase"
}
Blend One One //修改混合方式
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
//输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
struct VertexInput {
float4 vertex : POSITION; //顶点位置,总是必要的
float2 uv : TEXCOORD0; //UV信息 采样贴图用
};
struct VertexOutput {
float4 pos : SV_POSITION; //顶点位置,总是必要的
float2 uv : TEXCOORD0; //UV信息 采样贴图用
};
VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
o.pos = UnityObjectToClipPos( v.vertex ); //顶点位置OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); //UV信息 支持Tilling Offset
return o;
}
float4 frag(VertexOutput i) : COLOR {
half3 var_MainTex = tex2D(_MainTex, i.uv).rgb;//采样贴图 RGB颜色 Atoutie
return half4(var_MainTex, 1.0); //返回值
}
ENDCG
}
}
FallBack "Diffuse"
}
52.为什么模型在世界空间下的法线方向的绿通道能代表物体在世界空间下的上下关系?
nDir开辟内存空间用float3类型的TEXCOORD去存储,那么rgb就代表xyz,那么上下就是g(y)了。
53.混合模式
54.排序问题
55.Alpha通道预乘问题
56.屏幕纹理计算方法
输出结构:
float2 screenUV : TEXCOORD1; //屏幕UV
顶点shader:
float3 posVS = UnityObjectToViewPos(v.vertex).xzy; //顶点位置OS>VS
float originDist = UnityObjectToViewPos(float3(0.0, 0.0, 0.0)).z;//原点位置OS>VS
o.screenUV = posVS.xy/posVS.z; //VS空间畸变校正
o.screenUV *= originDist; //纹理大小按距离锁定
o.screenUV = o.screenUV * _ScreenTex_ST.xy - frac(_Time.x * _ScreenTex_ST.zw);//启用屏幕纹理ST
57.取余
frac()
58.背景纹理
59.声明常量
#define TWO_PI 6.283185
60.顶点动画
void Translation (inout float3 vertex) {
vertex.y += _MoveRange * sin(frac(_Time.z * _MoveSpeed) * TWO_PI);