Unity Shader 入门精要学习 (冯乐乐 著)
第1篇 基础篇
第2篇 初级篇
第3篇 中级篇
第4篇 高级篇
第5篇 扩展篇
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第1篇 基础篇
第1章 欢迎来到Shader的世界
第2章 渲染流水线
| 3D API | 最后支持固定管线的版本 | 第一个支持可编程管线的版本 |
| OpenGL | 1.5 | 2.0 |
| OpenGL ES | 1.1 | 2.0 |
| DirectX | 7.0 | 8.0 |
第3章 Unity Shader 基础
| 属性类型 | 默认值的定义语法 | 例子 |
| Int | number | _Int("Int",Int)=2 |
| Float | number | _Float("Float",Float)=1.5 |
| Range(min,max) | number | _Range("Range",Range(0.0,5.0))=3.0 |
| Color | (number,number,number,number) | _Color("Color",Color)=(1,1,1,1) |
| Vector | (number,number,number,number) | _Vector("Vector",Vector)=(2,3,6,1) |
| 2D | "defaulttexture" {} | _2D("2D",2D)="" {} |
| Cube | "defaulttexture" {} | _Cube("Cube",Cube)="white" {} |
| 3D | "defaulttexture" {} | _3D("3D",3D)="black" {} |
| 状态名称 | 设置指令 | 解释 |
| Cull | Cull Back|Front|Off | 设置剔除模式:剔除背面/正面/关闭剔除 |
| ZTest | ZTest Less Greater|LEqual|GEqual|Equal|NotEqual|Always | 设置深度测试时使用的函数 |
| ZWrite | ZWrite On|Off | 开启/关闭深度写入 |
| Blend | Blend SrcFactor DstFactor | 开启并设置混合模式 |
| 标签类型 | 说明 | 例子 |
| Queue | 控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染,我们也可以自定义使用的渲染队列来控制物体的渲染顺序 | Tags {"Queue" = "Transparent"} |
| RenderType | 对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等.这可以被用于着色器替换(Shader Replacement)功能 | Tags {"RenderType" = "Opaque"} |
| DisableBatching | 一些SubShader在使用Unity的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画.这时可以通过该标签来直接指明是否对该SubShader使用批处理 | Tags {"DisableBatching" = "True"} |
| ForceNoShadowCasting | 控制使用该SubShader的物体是否会投射阴影 | Tags {"ForceNoShadowCasting" = "True"} |
| IgnoreProjector | 如果该标签值为"True",那么使用该SubShader的物体将不会受Projector的影响.通常用于半透明物体 | Tags {"IgnoreProjector" = "True"} |
| CanUseSpriteAtlas | 当该SubShader是用于精灵(sprites)时,将该标签设为"False" | Tags {"CanUseSpriteAtlas" = "False"} |
| PreviewType | 指明材质面板将如何预览该材质.默认情况下,材质将显示为一个球形,我们可以通过把该标签的值设为"Plane""SkyBox"来改变预览类型 | Tags {"PreviewType" = "Plane"} |
| 标签类型 | 说明 | 例子 |
| LightMode | 定义该Pass在Unity的渲染流水线中的角色 | Tags {"LightMode" = "ForwardBase"} |
| RequireOptions | 用于指定当满足某些条件时才渲染该Pass,它的值是一个由空格分隔的字符串.目前,Unity支持的选项有:SoftVegetation.在后面的版本中,可能会增加更多的选项 | Tags {"RequireOptions" = "SoftVegetation"} |
第4章 学习Shader所需的数学基础
| 变换名称 | 是线性变换吗 | 是仿射变换吗 | 是可逆矩阵吗 | 是正交矩阵吗 |
| 平移矩阵 | N | Y | Y | N |
| 绕坐标轴旋转的旋转矩阵 | Y | Y | Y | Y |
| 绕任意轴旋转的旋转矩阵 | Y | Y | Y | Y |
| 按坐标轴缩放的缩放矩阵 | Y | Y | Y | N |
| 错切矩阵 | Y | Y | Y | N |
| 镜像矩阵 | Y | Y | Y | Y |
| 正交投影矩阵 | Y | Y | N | N |
| 透视投影矩阵 | N | N | N | N |
| 变量名 | 描述 |
| UNITY_MATRIX_MVP | 当前的模型观察投影矩阵,用于将顶点/方向矢量从模型空间变换到裁剪空间 |
| UNITY_MATRIX_MV | 当前的模型观察矩阵,用于将顶点/方向矢量从模型空间变换到观察空间 |
| UNITY_MATRIX_V | 当前的观察矩阵,用于将顶点/方向矢量从世界空间变换到观察空间 |
| UNITY_MATRIX_P | 当前的投影矩阵,用于将顶点/方向矢量从观察空间变换到裁剪空间 |
| UNITY_MATRIX_VP | 当前的观察投影矩阵,用于将顶点/方向矢量从世界空间变换到裁剪空间 |
| UNITY_MATRIX_T_MV | UNITY_MATRIX_MV的转置矩阵 |
| UNITY_MATRIX_IT_MV | UNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间,也可用于得到UNITY_MATRIX_MV的逆矩阵 |
| _Object2World | 当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间 |
| _World2Object | _Object2World的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间 |
| 变量名 | 类型 | 描述 |
| _WorldSpaceCameraPos | float3 | 该摄像机在世界空间中的位置 |
| _ProjectionParams | float4 | x=1.0(或-1.0,如果正在使用一个翻转的投影矩阵进行渲染),y=Near,z=Far,w=1.0+1.0/Far,其中Near和Far分别是近裁剪平面和远裁剪平面和摄像机的距离 |
| _ScreenParams | float4 | x=width,y=height,z=1.0+1.0/width,w=1.0+1.0/height,其中width和height分别是该摄像机 |
| _ZBufferParams | float4 | x=1-Far/Near,y=Far/Near,z=x/Far,w=y/Far,该变量用于线性化Z缓存中的深度值 |
| unity_OrthoParams | float4 | x=width,y=height,z没有定义,w=1.0(该摄像机是正交摄像机)或w=0.0(该摄像机是透视摄像机),其中width和height是正交投影摄像机的宽度和高度 |
| unity_CameraProjection | float4x4 | 该摄像机的投影矩阵 |
| unity_CameraInvProjection | float4x4 | 该摄像机的投影矩阵的逆矩阵 |
| unity_CameraWorldClipPlanes[6] | float4 | 该摄像机的6个裁剪平面在世界空间下的等式,按如下顺序:左,右,下,上,近,远裁剪平面 |
第2篇 初级篇
第5章 开始Unity Shader的学习之旅
| ShaderLab属性类型 | Cg变量类型 |
| Color,Vector | float4,half4,fixed4 |
| Range,Float | float,half,fixed |
| 2D | sampler2D |
| Cube | samplerCube |
| 3D | sampler3D |
| 文件名 | 描述 |
| UnityCG.cginc | 包含了最常使用的帮助函数,宏和结构体等 |
| UnityShaderVariables.cginc | 在编译UnityShader时,会被自动包含进来.包含了许多内置的全局变量,如UNITY_MATRIX_MVP等 |
| Lighting.cginc | 包含了各种内置的光照模型,如果编写的是SurfaceShader的话,会自动包含进来 |
| HLSLSupport.cginc | 在编译UnityShader时,会被自动包含进来.声明了很多用于跨平台编译的宏和定义 |
| 名称 | 描述 | 包含的变量 |
| appdata_base | 可用于顶点着色器的输入 | 顶点位置,顶点法线,第一组纹理坐标 |
| appdata_tan | 可用于顶点着色器的输入 | 顶点位置,顶点切线,顶点法线,第一组纹理坐标 |
| appdata_full | 可用于顶点着色器的输入 | 顶点位置,顶点切线,顶点法线,四组(或更多)纹理坐标 |
| appdata_img | 可用于顶点着色器的输入 | 顶点位置,第一组纹理坐标 |
| v2f_img | 可用于顶点着色器的输出 | 裁剪空间中的位置,纹理坐标 |
| 函数名 | 描述 |
| float3 WorldSpaceViewDir(float4 v) | 输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向 |
| float3 ObjSpaceViewDir(float4 v) | 输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向 |
| float3 WorldSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向.没有被归一化 |
| float3 ObjSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向.没有被归一化 |
| float3 UnityObjectToWorldNormal(float3 norm) | 把法线方向从模型空间转换到世界空间中 |
| float3 UnityObjectToWorldDir(float3 dir) | 把方向矢量从模型空间变换到世界空间中 |
| float3 UnityWorldToObjectDir(float3 dir) | 把方向矢量从世界空间变换到模型空间中 |
| 语义 | 描述 |
| POSITION | 模型空间中的顶点位置,通常是float4类型 |
| NORMAL | 顶点法线,通常是float3类型 |
| TANGENT | 顶点切线,通常是float4类型 |
| TEXCOORDn,如TEXCOORD0,TEXCOORD1 | 该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标,依次类推.通常是float2或float4类型 |
| COLOR | 顶点颜色,通常是fixed4或float4类型 |
| 语义 | 描述 |
| SV_POSITION | 裁剪空间的顶点坐标,结构体中必须包含一个用该语义修饰的变量.等同于DirectX9中的POSITION,但最好使用SV_POSITION |
| COLOR0 | 通常用于输出第一组顶点颜色,但不是必需的 |
| COLOR1 | 通常用于输出第二组顶点颜色,但不是必需的 |
| TEXCOORD0~TEXCOORD7 | 通常用于输出纹理坐标,但不是必需的 |
| 语义 | 描述 |
| SV_Target | 输出值将会存储到渲染目标(render target)中.等同于DirectX9中的COLOR语义,但最好使用SV_Target |
| 类型 | 精度 |
| float | 最高精度的浮点值.通常使用32位来存储 |
| half | 中等精度的浮点值.通常使用16位来存储,精度范围是-60000~+60000 |
| fixed | 最低精度的浮点值.通常使用11位来存储,精度范围是-2.0~+2.0 |
| 指令 | 描述 |
| #pragma target 2.0 | 默认的Shader Target等级.相当于Direct3D 9上的Shader Model2.0,不支持对顶点纹理的采样,不支持显式的LOD纹理采样等 |
| #pragma target 3.0 | 相当于Direct3D 9上的Shader Model 3.0,支持对顶点纹理的采样等 |
| #pragma target 4.0 | 相当于Direct3D 10上的Shader Model 4.0,支持几何着色器等 |
| #pragma target 5.0 | 相当于Direct3D 11上的Shader Model 5.0 |

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 5/Simple Shader"{
Properties{
_Color("Color",Color)=(1.0,1.0,1.0,1.0)
}
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// 在Cg代码中,我们需要定义一个与属性名称和类型都匹配的变量
fixed4 _Color;
// 使用一个结构体来定义顶点着色器的输入
struct a2v{
// POSITION语义告诉Unity,用模型空间的顶点坐标填充vertex变量
float4 vertex:POSITION;
// NORMAL语义告诉Unity,用模型空间的法线方向填充normal变量
float3 normal:NORMAL;
// TEXCOORD0语义告诉Unity,用模型的第一套纹理坐标填充texcoord变量
float4 texcoord:TEXCOORD0;
};
// 使用一个结构体来定义顶点着色器的输出
struct v2f{
// SV_POSITION语义告诉Unity,pos里包含了顶点在裁剪空间种的位置信息
float4 pos:SV_POSITION;
// COLOR0语义可以用于存储颜色信息
fixed3 color:COLOR0;
};
v2f vert(a2v v):POSITION{
// 声明输出结构
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
// v.normal包含了顶点的法线方向,其分量范围在[-1.0,1.0]
// 下面的代码把分量范围映射到了[0.0,1.0]
// 存储到f.color中传递给片元着色器
f.color = v.normal * 0.5 + fixed3(0.5,0.5,0.5);
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed3 c = f.color;
// 使用_Color属性来控制输出颜色
c *= _Color.rgb;
// 将插值后的f.color显示到屏幕上
return fixed4(c,1.0);
}
ENDCG
}
}
}
Simple Shader
第6章 Unity中的基础光照
| 函数名 | 描述 |
| float3 WorldSpaceViewDir(float4 v) | 输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向.内部实现使用了UnityWorldSpaceViewDir函数 |
| float3 UnityWorldSpaceViewDir(float4 v) | 输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向 |
| float3 ObjSpaceViewDir(float4 v) | 输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向 |
| float3 WorldSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向.内部实现使用了UnityWorldSpaceLightDir函数.没有被归一化 |
| float3 UnityWorldSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个世界空间中的顶点位置,返回世界空间中从该点到光源的光照方向.没有被归一化 |
| float3 ObjSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向.没有被归一化 |
| float3 UnityObjectToWorldNormal(float3 norm) | 把法线方向从模型空间转换到世界空间中 |
| float3 UnityObjectToWorldDir(float3 dir) | 把方向矢量从模型空间变换到世界空间中 |
| float3 UnityWorldToObjectDir(float3 dir) | 把方向矢量从世界空间变换到模型空间中 |

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shader Book/Chapter 6/Diffuse Vertex-Level" {
Properties{
_Diffuse("Diffuse",Color)=(1.0,1.0,1.0,1.0)
}
SubShader{
Pass{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v{
float4 pos:POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
fixed3 color:COLOR;
};
v2f vert(a2v v){
v2f f;
// Transform the vertex from object space to projection space
f.pos = UnityObjectToClipPos(v.pos);
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Transform the normal from object space to world sapce
fixed3 worldNormal = mul(UNITY_MATRIX_M,v.normal);
// Get the light direction in worldSpace
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
f.color = ambient + diffuse;
return f;
}
fixed4 frag(v2f f):SV_Target{
return fixed4(f.color,1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}
Diffuse Vertex-Level
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader"Unity Shader Book/Chapter 6/Diffuse Pixel-Level" {
Properties{
_Diffuse(,,,)
}
SubShader{
Pass{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
struct a2v{
float4 pos:POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
};
uniform fixed4 _Diffuse;
v2f vert(a2v v){
v2f f;
// Transform the vertex from object space to projection space
f.pos = UnityObjectToClipPos(v.pos);
// Transform the normal from object space to world space
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
return f;
}
fixed4 frag(v2f f):SV_Target{
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Get the normal in world space
fixed3 worldNormalDir = normalize(f.worldNormal);
// Get the light direction in world space
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormalDir,worldLightDir));
fixed3 color = ambient + diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}
Diffuse Pixel-Level
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader"Unity Shader Book/Chapter 6/Diffuse Half-Lambert" {
Properties{
_Diffuse(,,,)
}
SubShader{
Pass{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
struct a2v{
float4 pos:POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
};
uniform fixed4 _Diffuse;
v2f vert(a2v v){
v2f f;
// Transform the vertex from object space to projection space
f.pos = UnityObjectToClipPos(v.pos);
// Transform the normal from object space to world space
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
return f;
}
fixed4 frag(v2f f):SV_Target{
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Get the normal in world space
fixed3 worldNormalDir = normalize(f.worldNormal);
// Get the light direction in world space
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * (dot(worldNormalDir,worldLightDir) * 0.5 + 0.5);
fixed3 color = ambient + diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
Fallback "Diffuse"
}
Diffuse Half-Lambert

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shader Book/Chapter 6/Specular Vertex-Level" {
Properties {
_Diffuse("Diffuse",Color)=(1.0,1.0,1.0,1.0)
_Specular("Specular",Color)=(1.0,1.0,1.0,1.0)
_Gloss("Gloss",Range(8.0,256.0))=20.0
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
#pragma vertex vert
#pragma fragment frag
struct a2v{
float4 pos:POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
float3 color:COLOR;
};
v2f vert(a2v v){
v2f f;
// Transform the vertex from object space to projection space
f.pos = UnityObjectToClipPos(v.pos);
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Transform the normal from object space to world space
float3 worldNormalDir = normalize(mul(UNITY_MATRIX_M,v.normal));
// Get the light direction in world space
float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormalDir,worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormalDir));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(UNITY_MATRIX_M,v.pos).xyz);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
f.color = ambient + diffuse + specular;
return f;
}
fixed4 frag(v2f f):SV_Target{
);
}
ENDCG
}
}
Fallback "Specular"
}
Specular Vertex-Level
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shader Book/Chapter 6/Specular Pixel-Level" {
Properties {
_Diffuse("Diffuse",Color)=(1.0,1.0,1.0,1.0)
_Specular("Specular",Color)=(1.0,1.0,1.0,1.0)
_Gloss(,))=
}
SubShader{
Pass{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v{
float4 pos:POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(a2v v){
v2f f;
// Transform the vertex from object space to projection space
f.pos = UnityObjectToClipPos(v.pos);
// Transform the normal from object space to world space
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
// Transform the vertex from object space to world space
f.worldPos = mul(UNITY_MATRIX_M,v.pos).xyz;
return f;
}
fixed4 frag(v2f f):SV_Target{
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormalDir = normalize(f.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormalDir,worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormalDir));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos.xyz);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
return fixed4(ambient + diffuse + specular,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
Specular Pixel-Level
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shader Book/Chapter 6/Specular BlinnPhong" {
Properties {
_Diffuse("Diffuse",Color)=(1.0,1.0,1.0,1.0)
_Specular("Specular",Color)=(1.0,1.0,1.0,1.0)
_Gloss(,))=
}
SubShader{
Pass{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v{
float4 pos:POSITION;
float3 normal:NORMAL;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert(a2v v){
v2f f;
// Transform the vertex from object space to projection space
f.pos = UnityObjectToClipPos(v.pos);
// Transform the normal from object space to world space
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
// Transform the vertex from object space to world space
f.worldPos = mul(UNITY_MATRIX_M,v.pos).xyz;
return f;
}
fixed4 frag(v2f f):SV_Target{
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormalDir = normalize(f.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormalDir,worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormalDir));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos.xyz);
// Get the half direction in world space
fixed3 halfDir = normalize(worldLightDir + viewDir);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(,dot(worldNormalDir,halfDir)),_Gloss);
return fixed4(ambient + diffuse + specular,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
Specular BlinnPhong
第7章 基础纹理

Shader "Unity Shader Book/Chapter 7/Simplest Texture" {
Properties{
_MainTex("MainTex",2D) = "white"{}
}
SubShader{
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
struct a2v{
float4 vertex:POSITION;
float4 uv:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = v.uv;
return f;
}
fixed4 frag(v2f f):SV_Target{
float4 albedo = tex2D(_MainTex,f.uv);
return albedo;
}
ENDCG
}
}
Fallback "Specular"
}
Simplest Texture

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shader Book/Chapter 7/Single Texture" {
Properties {
_Color (, , , )
_MainTex ("Main Tex", 2D) = "white" {}
_Specular (, , , )
_Gloss ()) =
}
SubShader {
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Specular;
float _Gloss;
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD1;
float3 worldPos:TEXCOORD2;
float2 uv:TEXCOORD3;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
f.worldPos = mul(UNITY_MATRIX_M, v.vertex).xyz;
f.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// Or just call the built-in function
// Transform 2D UV by scale/bias property
// #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
// f.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed3 worldNormalDir = normalize(f.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(f.worldPos));
// Use the texture to sample the diffuse color
fixed3 albedo = tex2D(_MainTex,f.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(,dot(worldNormalDir,worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(worldNormalDir, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
SingleTexture

Shader "Unity Shader Book/Chapter 7/Simplest NormalMap" {
Properties{
_BumpMap("Bump Map",2D) = "bump"{}
_BumpScale("Bump Scale",Float) = 1.0
}
SubShader{
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _BumpMap;
float _BumpScale;
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;
float4 uv:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float3 lightDir:TEXCOORD1;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = v.uv;
TANGENT_SPACE_ROTATION;
f.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed3 tangentLightDir = normalize(f.lightDir);
fixed4 packedNormal = tex2D(_BumpMap, f.uv);
fixed3 tangentNormal;
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 diffuse = max(,dot(tangentNormal,tangentLightDir));
);
}
ENDCG
}
}
Fallback "Specular"
}
Simplest NormalMap

Shader "Unity Shader Book/Chapter 7/Normal Map In Tangent Space" {
Properties {
_Color (, , , )
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale ("Bump Scale", Float) = 1.0
_Specular (, , , )
_Gloss ()) =
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
// Compute the binormal
// float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
// // Construct a matrix which transform vectors from object space to tangent space
// float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
// Or just use the built-in macro
TANGENT_SPACE_ROTATION;
// Transform the light direction from object space to tangent space
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
// Transform the view direction from object space to tangent space
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
// Get the texel in the normal map
fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
fixed3 tangentNormal;
// If the texture is not marked as "Normal map"
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// Or mark the texture as "Normal map", and use the built-in funciton
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(tangentNormal, tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(tangentNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
Normal Map In Tangent Space
Shader "Unity Shader Book/Chapter 7/Normal Map In World Space" {
Properties {
_Color (, , , )
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale ("Bump Scale", Float) = 1.0
_Specular (, , , )
_Gloss ()) =
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
float4 TtoW0 : TEXCOORD1;
float4 TtoW1 : TEXCOORD2;
float4 TtoW2 : TEXCOORD3;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
// Compute the matrix that transform directions from tangent space to world space
// Put the world position in w component for optimization
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get the position in world space
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
// Compute the light and view dir in world space
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
// Get the normal in tangent space
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
bump.xy *= _BumpScale;
bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));
// Transform the narmal from tangent space to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(bump, lightDir));
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(bump, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
Normal Map In World Space



Shader "Unity Shader Book/Chapter 7/Ramp Texture" {
Properties {
_Color (, , , )
_RampTex ("Ramp Tex", 2D) = "white" {}
_Specular (, , , )
_Gloss ()) =
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
return o;
}
fixed4 frag(v2f f) : SV_Target {
fixed3 worldNormal = normalize(f.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(f.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Use the texture to sample the diffuse color
fixed halfLambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5;
fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
RampTexture
第8章 透明效果
| 名称 | 队列索引号 | 描述 |
| Background | 1000 | 这个渲染队列会在任何其他队列之前被渲染,我们通常使用该队列来渲染那些需要绘制在背景上的物体 |
| Geometry | 2000 | 默认的渲染队列,大多数物体都使用这个队列.不透明物体使用这个队列 |
| AlphaTest | 2450 | 需要透明度测试的物体使用这个队列.在Unity5中它从Geometry队列中被单独分出来,这是因为在所有不透明物体渲染之后再渲染它们会更加高效 |
| Transparent | 3000 | 这个队列中的物体会在所有Geometry和AlphaTest物体渲染后,再按从后往前的顺序进行渲染.任何使用了透明度混合(例如关闭了深度写入的Shader)的物体都应该使用该队列 |
| Overlay | 4000 | 该队列用于实现一些叠加效果.任何需要在最后渲染的物体都应该使用该队列 |

Shader "Unity Shader Book/Chapter 8/Alpha Test" {
Properties{
_Color(,,,)
_MainTex("Main Tex",2D) = "white"{}
_Cutoff(,)) = 0.5
}
SubShader{
Tags { "Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
Pass{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
struct a2v{
float4 vertex:POSITION;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD2;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = v.texcoord;
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed4 texColor = tex2D(_MainTex,f.uv);
// Alpha test
clip(texColor.a - _Cutoff);
// Equal to
//if((texColor.a - _Cutoff) < 0.0){
//discard;
//}
return texColor;
}
ENDCG
}
}
Fallback "Transparent/Cutout/VertexLit"
}
Alpha Test
| 语义 | 描述 |
| Blend Off | 关闭混合 |
| Blend SrcFactor DstFactor | 开启混合,并设置混合因子.源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓存的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲中 |
| Blend SrcFactor DstFactor,SrcFactorA DstFactorA | 和上面几乎一样,只是使用不同的因子来混合透明通道 |
| BlendOp BlendOperation | 并非是把源颜色和目标颜色简单相加后混合,而是使用BlendOperation对它们进行其他操作 |

Shader "Unity Shader Book/Chapter 8/Alpha Blend" {
Properties{
_Color(,,,)
_MainTex("Main Tex",2D)="white"{}
_AlphaScale(,))=
}
SubShader{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass{
Tags { "LightMode"="ForwardBase" }
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
//Blend Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
float4 _Color;
sampler2D _MainTex;
float _AlphaScale;
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
f.worldPos = mul(UNITY_MATRIX_M,v.vertex);
f.uv = v.texcoord;
return f;
}
fixed4 frag(v2f f):SV_Target{
//float3 worldNormalDir = normalize(f.worldNormal);
//float3 worldLightDir = normalize(UnityWorldSpaceLightDir(f.worldPos));
float4 texColor = tex2D(_MainTex,f.uv);
//float3 albedo = _LightColor0.xyz * _Color.xyz * texColor.xyz * max(0,dot(worldNormalDir,worldLightDir));
float3 albedo = texColor.xyz;
return fixed4(albedo,0.5);
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
Alpha Blend


Shader "Unity Shader Book/Chapter 8/Alpha Blending ZWrite" {
Properties{
_Color(,,,)
_MainTex("Main Tex",2D)="white"{}
_AlphaScale(,))=
}
SubShader{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass {
ZWrite On
ColorMask
}
Pass{
Tags { "LightMode"="ForwardBase" }
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
//Blend Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
float4 _Color;
sampler2D _MainTex;
float _AlphaScale;
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.worldNormal = mul(UNITY_MATRIX_M,v.normal);
f.worldPos = mul(UNITY_MATRIX_M,v.vertex);
f.uv = v.texcoord;
return f;
}
fixed4 frag(v2f f):SV_Target{
float3 worldNormalDir = normalize(f.worldNormal);
float3 worldLightDir = normalize(UnityWorldSpaceLightDir(f.worldPos));
float4 texColor = tex2D(_MainTex,f.uv);
//float3 albedo = _LightColor0.xyz * _Color.xyz * texColor.xyz * max(0,dot(worldNormalDir,worldLightDir));
float3 albedo = _Color.xyz * texColor.xyz * max(,dot(worldNormalDir,worldLightDir));
return fixed4(albedo,0.5);
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
Alpha Blending ZWrite
| 命令 | 描述 |
| Blend SrcFactor DstFactor | 开启混合,并设置混合因子.源颜色(该片元产生的颜色)会乘以SrcFactor,而目标颜色(已经存在于颜色缓存的颜色)会乘以DstFactor,然后把两者相加后再存入颜色缓冲中 |
| Blend SrcFactor DstFactor,SrcFactorA DstFactorA | 和上面几乎一样,只是使用不同的因子来混合透明通道 |
| 参数 | 描述 |
| One | 因子为1 |
| Zero | 因子为0 |
| SrcColor | 因子为源颜色值.当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于混合A的混合等式时,使用SrcColor的A分量作为混合因子 |
| SrcAlpha | 因子为源颜色的透明度值(A通道) |
| DstColor | 因子为目标颜色值.当用于混合RGB通道的混合等式时,使用DstColor的RGB分量作为混合因子;当用于混合A通道的混合等式时,使用DstColor的A分量作为混合因子 |
| DstAlpha | 因子为目标颜色的透明度值(A通道) |
| OneMinusSrcColor | 因子为(1-源颜色).当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;当用于混合A的混合等式时,使用结果的A分量作为混合因子 |
| OneMinusSrcAlpha | 因子为(1-源颜色的透明度值) |
| OneMinusDstColor | 因子为(1-目标颜色).当用于混合RGB的混合等式时,使用结果的RGB分量作为混合因子;当用于混合A的混合等式时,使用结果的A分量作为混合因子 |
| OneMinusDstAlpha | 因子为(1-目标颜色的透明度值) |
| 操作 | 描述 |
| Add |
将混合后的源颜色和目标颜色相加.默认的混合操作.使用的混合等式是: Orgb = SrcFactor x Srgb + DstFactor x Drgb Oa = SrcFactorA x Sa + DstFactorA x Da |
| Sub |
用混合后的源颜色减去混合后的目标颜色.使用的混合等式是: Orgb = SrcFactor x Srgb - DstFactor x Drgb Oa = SrcFactorA x Sa - DstFactorA x Da |
| RevSub |
用混合后的目标颜色减去混合后的源颜色.使用的混合等式是: Orgb = DstFactor x Drgb - SrcFactor x Srgb Oa = DstFactorA x Da - SrcFactorA x Sa |
| Min |
使用源颜色和目标颜色中较小的值,是逐分量比较的.使用的混合等式是: Orgba = (min(Sr,Dr),min(Sg,Dg),min(Sb,Db),min(Sa,Da)) |
| Max |
使用源颜色和目标颜色中较大的值,是逐分量比较的.使用的混合等式是: Orgba = (max(Sr,Dr),max(Sg,Dg),max(Sb,Db),max(Sa,Da)) |
| 其他逻辑操作 | 仅再DirectX 11.1中支持 |
Shader "Unity Shader Book/Chapter 8/Alpha Blend Operations" {
Properties{
_Color(,,,)
_MainTex("MainTex",2D)="white"{}
_AlphaScale(,))=
}
SubShader{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
// Normal
Blend SrcAlpha OneMinusSrcAlpha
// Soft Additive
//Blend OneMinusDstColor One
// Multiply
//Blend DstColor Zero
// 2x Multiply
//Blend DstColor SrcColor
// Darken
//BlendOp Min
//Blend One One // When using Min operation,these factors are ignored
// Lighten
//BlendOp Max
//Blend One One // When using Max operation,these factors are ignored
// Screen
//Blend OneMinusDstColor One
// Or
//Blend One OneMinusSrcColor
// Linear Dodge
//Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = v.texcoord;
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed4 texColor = tex2D(_MainTex,f.uv);
return fixed4(texColor.xyz * _Color.rgb,texColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
Alpha Blend Operations

Shader "Unity Shader Book/Chapter 8/Alpha Test With Both Side" {
Properties{
_Color(,,,)
_MainTex("MainTex",2D)="white"{}
}
SubShader{
Tags { "Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout" }
Pass{
Tags { "LightMode"="ForwardBase" }
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
struct a2v{
float4 vertex:POSITION;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = v.texcoord;
return f;
}
fixed4 frag(v2f f):SV_Target{
float4 texColor = tex2D(_MainTex,f.uv);
if(texColor.a <0.6){
discard;
}
return texColor;
}
ENDCG
}
}
Fallback "Transparent/Cutout/VertexLit"
}
Alpha Test With Both Side

Shader "Unity Shader Book/Chapter 8/Alpha Blend With Both Side" {
Properties {
_Color (, , , )
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale (, )) =
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
// First pass renders only back faces
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardBase" }
// Second pass renders only front faces
Cull Back
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
Alpha Blend With Both Side
第3篇 中级篇
第9章 更复杂的光照
| 标签名 | 描述 |
| Always | 不管使用那种渲染路径,该Pass总是会被渲染,但不会计算任何光照 |
| ForwardBase | 用于前向渲染.该Pass会计算环境光,最重要的平行光,逐顶点/SH光源和Lightmaps |
| ForwardAdd | 用于前向渲染.该Pass会计算额外的逐像素光源,每个Pass对应一个光源 |
| Deferred | 用于延迟渲染.该Pass会渲染G缓冲(G-buffer) |
| ShadowCaster | 把物体的深度信息渲染到阴影映射纹理(shadowmap)或一张深度纹理中 |
| PrepassBase | 用于遗留的延迟渲染.该Pass会渲染法线和高光反射的指数部分 |
| PrepassFinal | 用于遗留的延迟渲染.该Pass通过合并纹理,光照和自发光来渲染得到最后的颜色 |
| Vertex,VertexLMRGBM和VertexLM | 用于遗留的顶点照明渲染 |
| 名称 | 类型 | 描述 |
| _LightColor0 | float4 | 该Pass处理的逐像素光源的颜色 |
| _WorldSpaceLightPos0 | float4 | _WorldSpaceLightPos0.xyz 是该Pass处理的逐像素光源的位置.如果该光源是平行光,那么_WorldSpaceLightPos0.w是0,其他光源类型w值为1 |
| _LightMatrix0 | float4x4 | 从世界空间到光源空间的变换矩阵.可以用于采样cookie和光强衰减(attenuation)纹理 |
|
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0 |
float4 | 仅用于Base Pass.前4个非重要的点光源在世界空间中的位置 |
| unity_4LightAtten0 | float4 | 仅用于Base Pass.存储了前4个非重要的点光源的衰减因子 |
| unity_LightColor | half[4] | 仅用于Base Pass.存储了前4个非重要的点光源的颜色 |
| 函数名 | 描述 |
| float3 WorldSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向.内部实现使用了UnityWorldSpaceLightDir函数.没有被归一化 |
| float3 UnityWorldSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个世界空间中的顶点位置,返回世界空间中从该点到光源的光照方向.没有被归一化 |
| float3 ObjSpaceLightDir(float4 v) | 仅可用于前向渲染中.输入一个模型空间的顶点位置,返回模型空间中从该点到光源的光照方向.没有被归一化 |
| float3 Shade4PointLights(...) |
仅可用于前向渲染中.计算四个点光源的光照,它的参数是已经打包进矢量的光照数据,通常就是表9.2中的内置变量,如unity_4LightPosX0,unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor和unity_4LightAtten0等.前向渲染通常会使用这个函数来计算逐顶点光照 |
| 名称 | 类型 | 描述 |
| unity_LightColor | half4[8] | 光源颜色 |
| unity_LightPosition | float4[8] | xyz分量是视角空间中的光源位置.如果光源是平行光,那么z分量值为0,其他光源类型z分量值为1 |
| untiy_LightAtten | half4[8] |
光源衰减因子.如果光源是聚光灯,x分量是cos(spotAngle/2),y分量是1/cos(spotAngle/4);如果是其他类型的光源,x分量是-1,y分量是1.z分量是衰减的平方, w分量是光源范围开根号的结果 |
| untiy_SpotDirection | float4[8] | 如果光源是聚光灯的话,值为视角空间的聚光灯的位置;如果是其他类型的光源,值为(0,0,1,0) |
| 函数名 | 描述 |
| float3 ShadeVertexLights(float4 vertex,float3 normal) | 输入模型空间中的顶点位置和法线,计算四个逐顶点光源的光照以及环境光.内部实现实际上调用了ShadeVertexLightsFull函数 |
| float3 ShadeVertexLightsFull(float4 vertex,float3 normal,int lightCount,bool spotLight) | 输入模型空间中的顶点位置和法线,计算lightCount个光源的光照以及环境光.如果spotLight值为true,那么这些光源会被当成聚光灯来处理,虽然结果更精确,但计算更加耗时;否则,按点光源处理 |
| 名称 | 类型 | 描述 |
| _LightColor | float4 | 光源颜色 |
| _LightMatrix0 | float4x4 | 从世界空间到光源空间的变换矩阵.可以用于采样cookie和光强衰减纹理 |

Shader "Unity Shader Book/Chapter 9/Forward Rendering" {
Properties {
_Diffuse (, , , )
_Specular (, , , )
_Gloss ()) =
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
// Pass for ambient light & first pixel light (directional light)
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
// Apparently need to add this declaration
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(worldNormal, halfDir)), _Gloss);
fixed atten = 1.0;
return fixed4(ambient + (diffuse + specular) * atten, 1.0);
}
ENDCG
}
Pass {
// Pass for other pixel lights
Tags { "LightMode"="ForwardAdd" }
Blend One One
CGPROGRAM
// Apparently need to add this declaration
#pragma multi_compile_fwdadd
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
// 我们首先判断当前处理的逐像素光源的类型,这是通过使用#ifdef指令判断是否定义了
// USING_DIRECTIONAL_LIGHT来得到的.如果当前前向渲染Pass处理的光源类型是平行光,
// 那么Unity的底层渲染引擎就会定义USING_DIRECTIONAL_LIGHT.如果判断得知是平行光的话,
// 光源方向可以直接由_WorldSpaceLightPos0.xyz得到;如果是点光源或聚光灯,那么
// _WorldSpaceLightPos0.xyz表示的世界空间下的光源位置,而想要得到光源的方向的话,
// 我们就需要用这个位置减去世界空间下的顶点位置
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(worldNormal, halfDir)), _Gloss);
// 我们同样通过判断是否定义了USING_DIRECTIONAL_LIGHT来决定当前处理的光源类型.
// 如果是平行光的话,衰减值为1.0.如果是其他光源类型,那么处理更复杂一些.尽管我们
// 可以使用数学表达式来计算给定点相对于点光源和聚光灯的衰减,但这些计算往往涉及开根号
// 除法等计算量相对较大的操作,因此Unity选择了使用一张纹理作为查找表(Lookup Table,LUT)
// 以在片元着色器中得到光源的衰减.我们首先得到光源空间下的坐标,然后使用该坐标对衰减纹理进行采样得到衰减值
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
#if defined (POINT)
float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, )).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#elif defined (SPOT)
float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, ));
) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#else
fixed atten = 1.0;
#endif
#endif
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
ForwardRendering



Shader "Unity Shader Book/Chapter 9/SimplestShadow" {
SubShader{
Pass{
Tags { "LightMode"="ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f{
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v){
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i):SV_Target{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}
SimplestShadow
第10章 高级纹理



Shader "Unity Shader Book/Chapter 10/Reflection" {
Properties {
_Color (, , , )
_ReflectColor (, , , )
_ReflectAmount (, )) =
_Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _ReflectColor;
fixed _ReflectAmount;
samplerCUBE _Cubemap;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefl : TEXCOORD3;
SHADOW_COORDS()
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
// Compute the reflect dir in world space
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(, dot(worldNormal, worldLightDir));
// Use the reflect dir in world space to access the cubemap
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
// Mix the diffuse color with the reflected color
fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
Reflection

Shader "Unity Shader Book/Chapter 10/Refraction" {
Properties {
_Color (, , , )
_RefractColor (, , , )
_RefractAmount (, )) =
_RefractRatio ()) = 0.5
_Cubemap ("Refraction Cubemap", Cube) = "_Skybox" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _RefractColor;
float _RefractAmount;
fixed _RefractRatio;
samplerCUBE _Cubemap;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefr : TEXCOORD3;
SHADOW_COORDS()
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
// Compute the refract dir in world space
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(, dot(worldNormal, worldLightDir));
// Use the refract dir in world space to access the cubemap
fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
// Mix the diffuse color with the refract color
fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
Refraction

Shader "Unity Shader Book/Chapter 10/Fresnel" {
Properties {
_Color (, , , )
_FresnelScale (, )) = 0.5
_Cubemap ("Reflection Cubemap", Cube) = "_Skybox" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
fixed _FresnelScale;
samplerCUBE _Cubemap;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD0;
fixed3 worldNormal : TEXCOORD1;
fixed3 worldViewDir : TEXCOORD2;
fixed3 worldRefl : TEXCOORD3;
SHADOW_COORDS()
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(i.worldViewDir);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;
- _FresnelScale) * pow( - dot(worldViewDir, worldNormal), );
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(, dot(worldNormal, worldLightDir));
fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
Fresnel

Shader "Unity Shader Book/Chapter 10/Mirror"{
Properties{
_MainTex("Main Tex",2D)="white"{}
}
SubShader{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
struct a2v{
float4 vertex:POSITION;
float3 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
// Mirror needs to flip x
o.uv.x = - o.uv.x;
return o;
}
fixed4 frag(v2f i):SV_Target{
return tex2D(_MainTex,i.uv);
}
ENDCG
}
}
Fallback Off
}
Mirror

Shader "Unity Shader Book/Chapter 10/Glass Refraction" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
_Distortion (, )) =
_RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0
}
SubShader {
// We must be transparent, so other objects are drawn before this one.
Tags { "Queue"="Transparent" "RenderType"="Opaque" }
// This pass grabs the screen behind the object into a texture.
// We can access the result in the next pass as _RefractionTex
GrabPass { "_RefractionTex" }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
samplerCUBE _Cubemap;
float _Distortion;
fixed _RefractAmount;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord: TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
float4 uv : TEXCOORD1;
float4 TtoW0 : TEXCOORD2;
float4 TtoW1 : TEXCOORD3;
float4 TtoW2 : TEXCOORD4;
};
v2f vert (a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag (v2f i) : SV_Target {
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
// Get the normal in tangent space
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
// Compute the offset in tangent space
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
// Convert the normal to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed3 reflDir = reflect(-worldViewDir, bump);
fixed4 texColor = tex2D(_MainTex, i.uv.xy);
fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;
fixed3 finalColor = reflCol * ( - _RefractAmount) + refrCol * _RefractAmount;
);
}
ENDCG
}
}
FallBack "Diffuse"
}
Glass Refraction

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[ExecuteInEditMode]
public class ProceduralTextureGeneration : MonoBehaviour {
public Material material = null;
#region Material properties
[SerializeField, SetProperty("textureWidth")]
;
public int textureWidth {
get {
return m_textureWidth;
}
set {
m_textureWidth = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("backgroundColor")]
private Color m_backgroundColor = Color.white;
public Color backgroundColor {
get {
return m_backgroundColor;
}
set {
m_backgroundColor = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("circleColor")]
private Color m_circleColor = Color.yellow;
public Color circleColor {
get {
return m_circleColor;
}
set {
m_circleColor = value;
_UpdateMaterial();
}
}
[SerializeField, SetProperty("blurFactor")]
private float m_blurFactor = 2.0f;
public float blurFactor {
get {
return m_blurFactor;
}
set {
m_blurFactor = value;
_UpdateMaterial();
}
}
#endregion
private Texture2D m_generatedTexture = null;
// Use this for initialization
void Start () {
if (material == null) {
Renderer renderer = gameObject.GetComponent<Renderer>();
if (renderer == null) {
Debug.LogWarning("Cannot find a renderer.");
return;
}
material = renderer.sharedMaterial;
}
_UpdateMaterial();
}
private void _UpdateMaterial() {
if (material != null) {
m_generatedTexture = _GenerateProceduralTexture();
material.SetTexture("_MainTex", m_generatedTexture);
}
}
private Color _MixColor(Color color0, Color color1, float mixFactor) {
Color mixColor = Color.white;
mixColor.r = Mathf.Lerp(color0.r, color1.r, mixFactor);
mixColor.g = Mathf.Lerp(color0.g, color1.g, mixFactor);
mixColor.b = Mathf.Lerp(color0.b, color1.b, mixFactor);
mixColor.a = Mathf.Lerp(color0.a, color1.a, mixFactor);
return mixColor;
}
private Texture2D _GenerateProceduralTexture() {
Texture2D proceduralTexture = new Texture2D(textureWidth, textureWidth);
// The interval between circles
float circleInterval = textureWidth / 4.0f;
// The radius of circles
float radius = textureWidth / 10.0f;
// The blur factor
float edgeBlur = 1.0f / blurFactor;
; w < textureWidth; w++) {
; h < textureWidth; h++) {
// Initalize the pixel with background color
Color pixel = backgroundColor;
// Draw nine circles one by one
; i < ; i++) {
; j < ; j++) {
// Compute the center of current circle
Vector2 circleCenter = ), circleInterval * (j + ));
// Compute the distance between the pixel and the center
float dist = Vector2.Distance(new Vector2(w, h), circleCenter) - radius;
// Blur the edge of the circle
Color color = _MixColor(circleColor, new Color(pixel.r, pixel.g, pixel.b, 0.0f), Mathf.SmoothStep(0f, 1.0f, dist * edgeBlur));
// Mix the current color with the previous color
pixel = _MixColor(pixel, color, color.a);
}
}
proceduralTexture.SetPixel(w, h, pixel);
}
}
proceduralTexture.Apply();
return proceduralTexture;
}
}
ProceduralTextureGeneration
Shader "Unity Shader Book/Chapter 8/Single Texture" {
Properties {
_Color (, , , )
_MainTex ("Main Tex", 2D) = "white" {}
_Specular (, , , )
_Gloss ()) =
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// Or just call the built-in function
// o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
// Use the texture to sample the diffuse color
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
Single Texture
第11章 让画面动起来
| 名称 | 类型 | 描述 |
| _Time | float4 | t是自该场景加载开始所经过的时间,4个分量的值分别是(t/20,t,2t,3t) |
| _SinTime | float4 | t是时间的正弦值,4个分量的值分别是(t/8,t/4,t/2,t) |
| _CosTime | float4 | t是时间的余弦值,4个分量的值分别是(t/8,t/4,t/2,t) |
| unity_DeltaTime | float4 | dt是时间增量,4个分量的值分别是(dt,1/dt,smoothDt,1/smoothDt) |

Shader "Unity Shader Book/Chapter 11/ImageSequenceAnimation"{
Properties{
_Color(,,,)
_MainTex("Main Tex",2D)="white"{}
_HorizontalAmount(
_VerticalAmount(
_Speed(,))=
}
SubShader{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Pass{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _HorizontalAmount;
float _VerticalAmount;
float _Speed;
struct a2v{
float4 vertex:POSITION;
float2 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return f;
}
fixed4 frag(v2f f):SV_Target{
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorizontalAmount);
float column = time - row * _HorizontalAmount;
//half2 uv = float2(f.uv.x / _HorizontalAmount,f.uv.y / _VerticalAmount);
//uv.x += column/_HorizontalAmount;
//uv.y -= row/_VerticalAmount;
half2 uv = f.uv + half2(column,-row);
uv.x /= _HorizontalAmount;
uv.y /= _VerticalAmount;
fixed4 c = tex2D(_MainTex,uv);
c.xyz *= _Color;
return c;
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
ImageSequenceAnimation

Shader "Unity Shader Book/Chapter 11/Scrolling Background"{
Properties{
_MainTex("Base Layer(RGB)",2D)="white"{}
_DetailTex("2nd Layer(RGB)",2D)="white"{}
_ScrollX(
_Scroll2X( }
SubShader{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DetailTex;
float4 _DetailTex_ST;
float _ScrollX;
float _Scroll2X;
float _Multiplier;
struct a2v{
float4 vertex:POSITION;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv.xy = v.texcoord.xy + float2(_ScrollX,0.0) * _Time.y;
f.uv.zw = v.texcoord.xy + float2(_Scroll2X,0.0) * _Time.y;
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed4 firstLayer = tex2D(_MainTex,f.uv.xy);
fixed4 secondLayer = tex2D(_DetailTex,f.uv.zw);
// 混合
fixed4 c = lerp(firstLayer,secondLayer,secondLayer.a);
return c;
}
ENDCG
}
}
Fallback "VertexLit"
}
Scrolling Background

Shader "Unity Shader Book/Chapter 11/Water"{
Properties{
_MainTex("Main Tex",2D)="white"{}
_Color(,,,)
_Magnitude(
_Frequency(
_InvWaveLength(
_Speed("Speed",Float)=0.5
}
SubShader{
// Need to disable batching because of the vertex animation
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True" }
Pass{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
struct a2v{
float4 vertex:POSITION;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert(a2v v){
v2f f;
float4 offset;
offset.yzw = float3(0.0,0.0,0.0);
offset.x = sin(_Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
f.pos = UnityObjectToClipPos(v.vertex + offset);
f.uv = v.texcoord.xy;
//f.uv += float2(0.0,_Time.y * _Speed);
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed4 c = tex2D(_MainTex,f.uv);
//c.xyz *= _Color.xyz;
return c;
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
Water

Shader "Unity Shader Book/Chapter 11/Billboard" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_Color (, , , )
_VerticalBillboarding (, )) =
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _VerticalBillboarding;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v) {
v2f o;
// Suppose the center in object space is fixed
float3 center = float3(, , );
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, ));
float3 normalDir = viewer - center;
// If _VerticalBillboarding equals 1, we use the desired view dir as the normal dir
// Which means the normal dir is fixed
// Or if _VerticalBillboarding equals 0, the y of normal is 0
// Which means the up dir is fixed
normalDir.y =normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
// Get the approximate up dir
// If normal dir is already towards up, then the up dir is towards front
float3 upDir = abs(normalDir.y) > , , ) : float3(, , );
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
// Use the three vectors to rotate the quad
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
o.pos = UnityObjectToClipPos(float4(localPos, ));
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
Billboard

Shader "Unity Shaders Book/Chapter 11/Vertex Animation With Shadow" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_Color (, , , )
_Magnitude (
_Frequency (
_InvWaveLength (
_Speed ("Speed", Float) = 0.5
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"DisableBatching"="True"}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(a2v v) {
v2f o;
float4 offset;
offset.yzw = float3(0.0, 0.0, 0.0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
o.pos = UnityObjectToClipPos(v.vertex + offset);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv += float2(0.0, _Time.y * _Speed);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 c = tex2D(_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
// Pass to render object as a shadow caster
Pass {
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
struct v2f {
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v) {
v2f o;
float4 offset;
offset.yzw = float3(0.0, 0.0, 0.0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
v.vertex = v.vertex + offset;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
fixed4 frag(v2f i) : SV_Target {
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "VertexLit"
}
Vertex Animation With Shadow
第4篇 高级篇
第12章 屏幕后处理效果
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {
// Called when start
protected void CheckResources() {
bool isSupported = CheckSupport();
if (isSupported == false) {
NotSupported();
}
}
// Called in CheckResources to check support on this platform
protected bool CheckSupport() {
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) {
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
// Called when the platform doesn't support this effect
protected void NotSupported() {
enabled = false;
}
protected void Start() {
CheckResources();
}
// Called when need to create the material used by this effect
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) {
if (shader == null) {
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported) {
return null;
} else {
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
}
PostEffectsBase

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BrightnessSaturationAndContrast : PostEffectsBase {
public Shader m_bscShader;
private Material m_bscMaterial;
public Material Material {
get {
m_bscMaterial = CheckShaderAndCreateMaterial(m_bscShader, m_bscMaterial);
return m_bscMaterial;
}
}
[Range(0.0f,3.0f)]
public float Brightness = 1.0f;
[Range(0.0f,3.0f)]
public float Saturation = 1.0f;
[Range(0.0f,3.0f)]
public float Contrast = 1.0f;
private void OnRenderImage(RenderTexture source, RenderTexture destination) {
if(Material != null) {
Material.SetFloat("_Brightness", Brightness);
Material.SetFloat("_Saturation", Saturation);
Material.SetFloat("_Contrast", Contrast);
Graphics.Blit(source, destination, Material);
} else {
Graphics.Blit(source, destination);
}
}
}
BrightnessSaturationAndContrast
Shader "Unity Shader Book/Chapter 12/Brightness Saturation And Contrast"{
Properties{
_MainTex("Base (RGB)",2D)="white"{}
_Brightness(
_Saturation(
_Contrast(
}
SubShader{
Pass{
ZTest Always
Cull Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
half _Brightness;
half _Saturation;
half _Contrast;
struct v2f{
float4 pos:SV_POSITION;
half2 uv:TEXCOORD0;
};
v2f vert(appdata_img v){
v2f f;
f.pos = UnityObjectToClipPos(v.vertex);
f.uv = v.texcoord;
return f;
}
fixed4 frag(v2f f):SV_Target{
fixed4 renderTex = tex2D(_MainTex,f.uv);
// Apply birghtness
fixed3 finalColor = renderTex.xyz * _Brightness;
// Apply saturation
fixed luminance = 0.2125 * renderTex.x + 0.7154 * renderTex.y + 0.0721 * renderTex.z;
fixed3 luminanceColor = fixed3(luminance,luminance,luminance);
finalColor = lerp(luminanceColor,finalColor,_Saturation);
// Apply Contrast
fixed3 avgColor = fixed3(0.5,0.5,0.5);
finalColor = lerp(avgColor,finalColor,_Contrast);
return fixed4(finalColor,renderTex.w);
}
ENDCG
}
}
Fallback Off
}
BrightnessSaturationAndContrast

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EdgeDetection : PostEffectsBase {
public Shader edgeDetectShader;
private Material edgeDetectMaterial = null;
public Material Material {
get {
edgeDetectMaterial = CheckShaderAndCreateMaterial(edgeDetectShader, edgeDetectMaterial);
return edgeDetectMaterial;
}
}
[Range(0.0f,1.0f)]
public float edgesOnly = 0.0f;
public Color edgeColor = Color.black;
public Color backgroundColor = Color.white;
void OnRenderImage(RenderTexture src,RenderTexture dest) {
if(Material != null) {
Material.SetFloat("_EdgeOnly", edgesOnly);
Material.SetColor("_EdgeColor", edgeColor);
Material.SetColor("_BackgroundColor", backgroundColor);
Graphics.Blit(src, dest, Material);
} else {
Graphics.Blit(src, dest);
}
}
}
EdgeDetection
Shader "Unity Shader Book/Chapter 12/Edge Detection"{
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_EdgeOnly ("Edge Only", Float) = 1.0
_EdgeColor (, , , )
_BackgroundColor (, , , )
}
SubShader {
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment fragSobel
sampler2D _MainTex;
uniform half4 _MainTex_TexelSize;
fixed _EdgeOnly;
fixed4 _EdgeColor;
fixed4 _BackgroundColor;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[] : TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[] = uv + _MainTex_TexelSize.xy * half2(-, -);
o.uv[] = uv + _MainTex_TexelSize.xy * half2(, -);
o.uv[] = uv + _MainTex_TexelSize.xy * half2(, -);
o.uv[] = uv + _MainTex_TexelSize.xy * half2(-, );
o.uv[] = uv + _MainTex_TexelSize.xy * half2(, );
o.uv[] = uv + _MainTex_TexelSize.xy * half2(, );
o.uv[] = uv + _MainTex_TexelSize.xy * half2(-, );
o.uv[] = uv + _MainTex_TexelSize.xy * half2(, );
o.uv[] = uv + _MainTex_TexelSize.xy * half2(, );
return o;
}
fixed luminance(fixed4 color) {
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
half Sobel(v2f i) {
] = {-, , ,
-, , ,
-, , };
] = {-, -, -,
, , ,
, , };
half texColor;
half edgeX = ;
half edgeY = ;
; it < ; it++) {
texColor = luminance(tex2D(_MainTex, i.uv[it]));
edgeX += texColor * Gx[it];
edgeY += texColor * Gy[it];
}
half edge = - abs(edgeX) - abs(edgeY);
return edge;
}
fixed4 fragSobel(v2f i) : SV_Target {
half edge = Sobel(i);
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv[]), edge);
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
return lerp(withEdgeColor, onlyEdgeColor, _EdgeOnly);
}
ENDCG
}
}
FallBack Off
}
EdgeDetection

using UnityEngine;
using System.Collections;
public class GaussianBlur : PostEffectsBase {
public Shader gaussianBlurShader;
private Material gaussianBlurMaterial = null;
public Material material {
get {
gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
return gaussianBlurMaterial;
}
}
// Blur iterations - larger number means more blur.
[Range(, )]
;
// Blur spread for each iteration - larger value means more blur
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;
[Range(, )]
;
/// 1st edition: just apply blur
// void OnRenderImage(RenderTexture src, RenderTexture dest) {
// if (material != null) {
// int rtW = src.width;
// int rtH = src.height;
// RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
//
// // Render the vertical pass
// Graphics.Blit(src, buffer, material, 0);
// // Render the horizontal pass
// Graphics.Blit(buffer, dest, material, 1);
//
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }
/// 2nd edition: scale the render texture
// void OnRenderImage (RenderTexture src, RenderTexture dest) {
// if (material != null) {
// int rtW = src.width/downSample;
// int rtH = src.height/downSample;
// RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);
// buffer.filterMode = FilterMode.Bilinear;
//
// // Render the vertical pass
// Graphics.Blit(src, buffer, material, 0);
// // Render the horizontal pass
// Graphics.Blit(buffer, dest, material, 1);
//
// RenderTexture.ReleaseTemporary(buffer);
// } else {
// Graphics.Blit(src, dest);
// }
// }
/// 3rd edition: use iterations for larger blur
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
int rtW = src.width/downSample;
int rtH = src.height/downSample;
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, );
buffer0.filterMode = FilterMode.Bilinear;
Graphics.Blit(src, buffer0);
; i < iterations; i++) {
material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, );
// Render the vertical pass
Graphics.Blit(buffer0, buffer1, material, );
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, );
// Render the horizontal pass
Graphics.Blit(buffer0, buffer1, material, );
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
}
Graphics.Blit(buffer0, dest);
RenderTexture.ReleaseTemporary(buffer0);
} else {
Graphics.Blit(src, dest);
}
}
}
GaussianBlur
Shader "Unity Shader Book/Chapter 12/Gaussian Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
float _BlurSize;
struct v2f {
float4 pos : SV_POSITION;
half2 uv[]: TEXCOORD0;
};
v2f vertBlurVertical(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[] = uv;
o.uv[] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
v2f vertBlurHorizontal(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[] = uv;
o.uv[] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
fixed4 fragBlur(v2f i) : SV_Target {
] = {0.4026, 0.2442, 0.0545};
fixed3 sum = tex2D(_MainTex, i.uv[]).rgb * weight[];
; it < ; it++) {
sum += tex2D(_MainTex, i.uv[it*-]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[it*]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
NAME "GAUSSIAN_BLUR_VERTICAL"
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass {
NAME "GAUSSIAN_BLUR_HORIZONTAL"
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
}
FallBack "Diffuse"
}
GaussianBlur

using UnityEngine;
using System.Collections;
public class Bloom : PostEffectsBase {
public Shader bloomShader;
private Material bloomMaterial = null;
public Material material {
get {
bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);
return bloomMaterial;
}
}
// Blur iterations - larger number means more blur.
[Range(, )]
;
// Blur spread for each iteration - larger value means more blur
[Range(0.2f, 3.0f)]
public float blurSpread = 0.6f;
[Range(, )]
;
[Range(0.0f, 4.0f)]
public float luminanceThreshold = 0.6f;
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
material.SetFloat("_LuminanceThreshold", luminanceThreshold);
int rtW = src.width/downSample;
int rtH = src.height/downSample;
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, );
buffer0.filterMode = FilterMode.Bilinear;
Graphics.Blit(src, buffer0, material, );
; i < iterations; i++) {
material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, );
// Render the vertical pass
Graphics.Blit(buffer0, buffer1, material, );
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, );
// Render the horizontal pass
Graphics.Blit(buffer0, buffer1, material, );
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
}
material.SetTexture ("_Bloom", buffer0);
Graphics.Blit (src, dest, material, );
RenderTexture.ReleaseTemporary(buffer0);
} else {
Graphics.Blit(src, dest);
}
}
}
Bloom
Shader "Unity Shader Book/Chapter 12/Bloom" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Bloom ("Bloom (RGB)", 2D) = "black" {}
_LuminanceThreshold ("Luminance Threshold", Float) = 0.5
_BlurSize ("Blur Size", Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _Bloom;
float _LuminanceThreshold;
float _BlurSize;
struct v2f {
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
v2f vertExtractBright(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed luminance(fixed4 color) {
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
fixed4 fragExtractBright(v2f i) : SV_Target {
fixed4 c = tex2D(_MainTex, i.uv);
fixed val = clamp(luminance(c) - _LuminanceThreshold, 0.0, 1.0);
return c * val;
}
struct v2fBloom {
float4 pos : SV_POSITION;
half4 uv : TEXCOORD0;
};
v2fBloom vertBloom(appdata_img v) {
v2fBloom o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv.xy = v.texcoord;
o.uv.zw = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0.0)
o.uv.w = 1.0 - o.uv.w;
#endif
return o;
}
fixed4 fragBloom(v2fBloom i) : SV_Target {
return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
CGPROGRAM
#pragma vertex vertExtractBright
#pragma fragment fragExtractBright
ENDCG
}
UsePass "Unity Shader Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_VERTICAL"
UsePass "Unity Shader Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_HORIZONTAL"
Pass {
CGPROGRAM
#pragma vertex vertBloom
#pragma fragment fragBloom
ENDCG
}
}
FallBack Off
}
Bloom

using UnityEngine;
using System.Collections;
public class MotionBlur : PostEffectsBase {
public Shader motionBlurShader;
private Material motionBlurMaterial = null;
public Material material {
get {
motionBlurMaterial = CheckShaderAndCreateMaterial(motionBlurShader, motionBlurMaterial);
return motionBlurMaterial;
}
}
[Range(0.0f, 0.9f)]
public float blurAmount = 0.5f;
private RenderTexture accumulationTexture;
void OnDisable() {
DestroyImmediate(accumulationTexture);
}
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
// Create the accumulation texture
if (accumulationTexture == null || accumulationTexture.width != src.width || accumulationTexture.height != src.height) {
DestroyImmediate(accumulationTexture);
accumulationTexture = );
accumulationTexture.hideFlags = HideFlags.HideAndDontSave;
Graphics.Blit(src, accumulationTexture);
}
// We are accumulating motion over frames without clear/discard
// by design, so silence any performance warnings from Unity
accumulationTexture.MarkRestoreExpected();
material.SetFloat("_BlurAmount", 1.0f - blurAmount);
Graphics.Blit (src, accumulationTexture, material);
Graphics.Blit (accumulationTexture, dest);
} else {
Graphics.Blit(src, dest);
}
}
}
MotionBlur
Shader "Unity Shader Book/Chapter 12/Motion Blur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurAmount ("Blur Amount", Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed _BlurAmount;
struct v2f {
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 fragRGB (v2f i) : SV_Target {
return fixed4(tex2D(_MainTex, i.uv).rgb, _BlurAmount);
}
half4 fragA (v2f i) : SV_Target {
return tex2D(_MainTex, i.uv);
}
ENDCG
ZTest Always Cull Off ZWrite Off
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
CGPROGRAM
#pragma vertex vert
#pragma fragment fragRGB
ENDCG
}
Pass {
Blend One Zero
ColorMask A
CGPROGRAM
#pragma vertex vert
#pragma fragment fragA
ENDCG
}
}
FallBack Off
}
MotionBlur
第13章 使用深度和法线纹理
第14章 非真实感渲染

Shader "Unity Shader Book/Chapter 14/Toon Shading" {
Properties {
_Color (, , , )
_MainTex ("Main Tex", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Outline (, )) = 0.1
_OutlineColor (, , , )
_Specular (, , , )
_SpecularScale (, 0.1)) = 0.01
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
NAME "OUTLINE"
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float _Outline;
fixed4 _OutlineColor;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
};
v2f vert (a2v v) {
v2f o;
float4 pos = mul(UNITY_MATRIX_MV, v.vertex);
float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
normal.z = -0.5;
pos = pos + float4(normalize(normal), ) * _Outline;
o.pos = mul(UNITY_MATRIX_P, pos);
return o;
}
float4 frag(v2f i) : SV_Target {
);
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _Ramp;
fixed4 _Specular;
fixed _SpecularScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
};
struct v2f {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
SHADOW_COORDS()
};
v2f vert (a2v v) {
v2f o;
o.pos = UnityObjectToClipPos( v.vertex);
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
float4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir);
fixed4 c = tex2D (_MainTex, i.uv);
fixed3 albedo = c.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed diff = dot(worldNormal, worldLightDir);
diff = (diff * 0.5 + 0.5) * atten;
fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb;
fixed spec = dot(worldNormal, worldHalfDir);
fixed w = fwidth(spec) * 2.0;
fixed3 specular = _Specular.rgb * lerp(, , smoothstep(-w, w, spec + _SpecularScale - )) * step(0.0001, _SpecularScale);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
ToonShading

Shader "Unity Shader Book/Chapter 14/Hatching" {
Properties {
_Color (, , , )
_TileFactor (
_Outline (, )) = 0.1
_Hatch0 ("Hatch 0", 2D) = "white" {}
_Hatch1 ("Hatch 1", 2D) = "white" {}
_Hatch2 ("Hatch 2", 2D) = "white" {}
_Hatch3 ("Hatch 3", 2D) = "white" {}
_Hatch4 ("Hatch 4", 2D) = "white" {}
_Hatch5 ("Hatch 5", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
UsePass "Unity Shader Book/Chapter 14/Toon Shading/OUTLINE"
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc"
fixed4 _Color;
float _TileFactor;
sampler2D _Hatch0;
sampler2D _Hatch1;
sampler2D _Hatch2;
sampler2D _Hatch3;
sampler2D _Hatch4;
sampler2D _Hatch5;
struct a2v {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
fixed3 hatchWeights0 : TEXCOORD1;
fixed3 hatchWeights1 : TEXCOORD2;
float3 worldPos : TEXCOORD3;
SHADOW_COORDS()
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy * _TileFactor;
fixed3 worldLightDir = normalize(WorldSpaceLightDir(v.vertex));
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
, dot(worldLightDir, worldNormal));
o.hatchWeights0 = fixed3(, , );
o.hatchWeights1 = fixed3(, , );
float hatchFactor = diff * 7.0;
if (hatchFactor > 6.0) {
// Pure white, do nothing
} else if (hatchFactor > 5.0) {
o.hatchWeights0.x = hatchFactor - 5.0;
} else if (hatchFactor > 4.0) {
o.hatchWeights0.x = hatchFactor - 4.0;
o.hatchWeights0.y = 1.0 - o.hatchWeights0.x;
} else if (hatchFactor > 3.0) {
o.hatchWeights0.y = hatchFactor - 3.0;
o.hatchWeights0.z = 1.0 - o.hatchWeights0.y;
} else if (hatchFactor > 2.0) {
o.hatchWeights0.z = hatchFactor - 2.0;
o.hatchWeights1.x = 1.0 - o.hatchWeights0.z;
} else if (hatchFactor > 1.0) {
o.hatchWeights1.x = hatchFactor - 1.0;
o.hatchWeights1.y = 1.0 - o.hatchWeights1.x;
} else {
o.hatchWeights1.y = hatchFactor;
o.hatchWeights1.z = 1.0 - o.hatchWeights1.y;
}
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 hatchTex0 = tex2D(_Hatch0, i.uv) * i.hatchWeights0.x;
fixed4 hatchTex1 = tex2D(_Hatch1, i.uv) * i.hatchWeights0.y;
fixed4 hatchTex2 = tex2D(_Hatch2, i.uv) * i.hatchWeights0.z;
fixed4 hatchTex3 = tex2D(_Hatch3, i.uv) * i.hatchWeights1.x;
fixed4 hatchTex4 = tex2D(_Hatch4, i.uv) * i.hatchWeights1.y;
fixed4 hatchTex5 = tex2D(_Hatch5, i.uv) * i.hatchWeights1.z;
fixed4 whiteColor = fixed4(, , , ) * ( - i.hatchWeights0.x - i.hatchWeights0.y - i.hatchWeights0.z -
i.hatchWeights1.x - i.hatchWeights1.y - i.hatchWeights1.z);
fixed4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5 + whiteColor;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
return fixed4(hatchColor.rgb * _Color.rgb * atten, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
Hatching
第15章 使用噪声

Shader "Unity Shader Book/Chapter 15/Dissolve" {
Properties {
_BurnAmount ("Burn Amount", Range(0.0, 1.0)) = 0.0
_LineWidth("Burn Line Width", Range(0.0, 0.2)) = 0.1
_MainTex ("Base (RGB)", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BurnFirstColor(, , , )
_BurnSecondColor(, , , )
_BurnMap("Burn Map", 2D) = "white"{}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
Pass {
Tags { "LightMode"="ForwardBase" }
Cull Off
CGPROGRAM
#include "Lighting.cginc"
#include "AutoLight.cginc"
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
fixed _BurnAmount;
fixed _LineWidth;
sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _BurnFirstColor;
fixed4 _BurnSecondColor;
sampler2D _BurnMap;
float4 _MainTex_ST;
float4 _BumpMap_ST;
float4 _BurnMap_ST;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uvMainTex : TEXCOORD0;
float2 uvBumpMap : TEXCOORD1;
float2 uvBurnMap : TEXCOORD2;
float3 lightDir : TEXCOORD3;
float3 worldPos : TEXCOORD4;
SHADOW_COORDS()
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap);
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
TANGENT_SPACE_ROTATION;
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
float3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));
fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(, dot(tangentNormal, tangentLightDir));
- smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
burnColor = pow(burnColor, );
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 finalColor = lerp(ambient + diffuse * atten, burnColor, t * step(0.0001, _BurnAmount));
);
}
ENDCG
}
// Pass to render object as a shadow caster
Pass {
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
fixed _BurnAmount;
sampler2D _BurnMap;
float4 _BurnMap_ST;
struct v2f {
V2F_SHADOW_CASTER;
float2 uvBurnMap : TEXCOORD1;
};
v2f vert(appdata_base v) {
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
clip(burn.r - _BurnAmount);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
FallBack "Diffuse"
}
Dissolve

Shader "Unity Shader Book/Chapter 15/Water Wave" {
Properties {
_Color (, )
_MainTex ("Base (RGB)", 2D) = "white" {}
_WaveMap ("Wave Map", 2D) = "bump" {}
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
_WaveXSpeed ("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01
_WaveYSpeed ("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01
_Distortion (, )) =
}
SubShader {
// We must be transparent, so other objects are drawn before this one.
Tags { "Queue"="Transparent" "RenderType"="Opaque" }
// This pass grabs the screen behind the object into a texture.
// We can access the result in the next pass as _RefractionTex
GrabPass { "_RefractionTex" }
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _WaveMap;
float4 _WaveMap_ST;
samplerCUBE _Cubemap;
fixed _WaveXSpeed;
fixed _WaveYSpeed;
float _Distortion;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
float4 uv : TEXCOORD1;
float4 TtoW0 : TEXCOORD2;
float4 TtoW1 : TEXCOORD3;
float4 TtoW2 : TEXCOORD4;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _WaveMap);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag(v2f i) : SV_Target {
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
// Get the normal in tangent space
fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb;
fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb;
fixed3 bump = normalize(bump1 + bump2);
// Compute the offset in tangent space
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
fixed3 refrCol = tex2D( _RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
// Convert the normal to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
fixed3 reflDir = reflect(-viewDir, bump);
fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb * _Color.rgb;
- saturate(dot(viewDir, bump)), );
fixed3 finalColor = reflCol * fresnel + refrCol * ( - fresnel);
);
}
ENDCG
}
}
// Do not cast shadow
FallBack Off
}
WaterWave

using UnityEngine;
using System.Collections;
public class FogWithNoise : PostEffectsBase {
public Shader fogShader;
private Material fogMaterial = null;
public Material material {
get {
fogMaterial = CheckShaderAndCreateMaterial(fogShader, fogMaterial);
return fogMaterial;
}
}
private Camera myCamera;
public Camera camera {
get {
if (myCamera == null) {
myCamera = GetComponent<Camera>();
}
return myCamera;
}
}
private Transform myCameraTransform;
public Transform cameraTransform {
get {
if (myCameraTransform == null) {
myCameraTransform = camera.transform;
}
return myCameraTransform;
}
}
[Range(0.1f, 3.0f)]
public float fogDensity = 1.0f;
public Color fogColor = Color.white;
public float fogStart = 0.0f;
public float fogEnd = 2.0f;
public Texture noiseTexture;
[Range(-0.5f, 0.5f)]
public float fogXSpeed = 0.1f;
[Range(-0.5f, 0.5f)]
public float fogYSpeed = 0.1f;
[Range(0.0f, 3.0f)]
public float noiseAmount = 1.0f;
void OnEnable() {
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;
}
void OnRenderImage (RenderTexture src, RenderTexture dest) {
if (material != null) {
Matrix4x4 frustumCorners = Matrix4x4.identity;
float fov = camera.fieldOfView;
float near = camera.nearClipPlane;
float aspect = camera.aspect;
float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
Vector3 toRight = cameraTransform.right * halfHeight * aspect;
Vector3 toTop = cameraTransform.up * halfHeight;
Vector3 topLeft = cameraTransform.forward * near + toTop - toRight;
float scale = topLeft.magnitude / near;
topLeft.Normalize();
topLeft *= scale;
Vector3 topRight = cameraTransform.forward * near + toRight + toTop;
topRight.Normalize();
topRight *= scale;
Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight;
bottomLeft.Normalize();
bottomLeft *= scale;
Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop;
bottomRight.Normalize();
bottomRight *= scale;
frustumCorners.SetRow(, bottomLeft);
frustumCorners.SetRow(, bottomRight);
frustumCorners.SetRow(, topRight);
frustumCorners.SetRow(, topLeft);
material.SetMatrix("_FrustumCornersRay", frustumCorners);
material.SetFloat("_FogDensity", fogDensity);
material.SetColor("_FogColor", fogColor);
material.SetFloat("_FogStart", fogStart);
material.SetFloat("_FogEnd", fogEnd);
material.SetTexture("_NoiseTex", noiseTexture);
material.SetFloat("_FogXSpeed", fogXSpeed);
material.SetFloat("_FogYSpeed", fogYSpeed);
material.SetFloat("_NoiseAmount", noiseAmount);
Graphics.Blit (src, dest, material);
} else {
Graphics.Blit(src, dest);
}
}
}
FogWithNoise
Shader "Unity Shaders Book/Chapter 15/Fog With Noise" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_FogDensity ("Fog Density", Float) = 1.0
_FogColor (, , , )
_FogStart ("Fog Start", Float) = 0.0
_FogEnd ("Fog End", Float) = 1.0
_NoiseTex ("Noise Texture", 2D) = "white" {}
_FogXSpeed ("Fog Horizontal Speed", Float) = 0.1
_FogYSpeed ("Fog Vertical Speed", Float) = 0.1
_NoiseAmount (
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
float4x4 _FrustumCornersRay;
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
half _FogDensity;
fixed4 _FogColor;
float _FogStart;
float _FogEnd;
sampler2D _NoiseTex;
half _FogXSpeed;
half _FogYSpeed;
half _NoiseAmount;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uv_depth : TEXCOORD1;
float4 interpolatedRay : TEXCOORD2;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
)
o.uv_depth.y = - o.uv_depth.y;
#endif
;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
index = ;
} else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
index = ;
} else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
index = ;
} else {
index = ;
}
#if UNITY_UV_STARTS_AT_TOP
)
index = - index;
#endif
o.interpolatedRay = _FrustumCornersRay[index];
return o;
}
fixed4 frag(v2f i) : SV_Target {
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
float2 speed = _Time.y * float2(_FogXSpeed, _FogYSpeed);
float noise = (tex2D(_NoiseTex, i.uv + speed).r - 0.5) * _NoiseAmount;
float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
fogDensity = saturate(fogDensity * _FogDensity * ( + noise));
fixed4 finalColor = tex2D(_MainTex, i.uv);
finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
return finalColor;
}
ENDCG
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}
FogWithNoise
第16章 Unity中的渲染优化技术
| 信息名称 | 描述 |
| 每帧的时间和FPS | 在Graphic的右侧显示,给出了处理和渲染一帧所需的时间,以及FPS数目 |
| Batches | 一帧中需要进行的批处理数目 |
| Saved by batching | 合并的批处理数目,这个数字表明了批处理为我们节省了多少draw call |
| Tris 和 Verts | 需要绘制的三角面片和顶点数目 |
| Screen | 屏幕的大小,以及它占用的内存大小 |
| SetPass | 渲染使用的Pass的数目,每个Pass都需要Unity的runtime来绑定一个新的Shader,这可能造成CPU的瓶颈 |
| Visible Skinned Meshes | 渲染的蒙皮网格的数目 |
| Animations | 播放的动画数目 |
第5篇 扩展篇
第17章 Unity的表面着色器探秘
| 变量 | 描述 |
| float3 viewDir | 包含了视角方向,可用于计算边缘光照等 |
| 使用COLOR语义定义的float4变量 | 包含了插值后的逐顶点颜色 |
| float4 screenPos | 包含了屏幕空间的坐标,可以用于反射或屏幕特效 |
| float3 worldPos | 包含了世界空间下的位置 |
| float3 worldRefl | 包含了世界空间下的反射方向.前提是没有修改表面法线o.Normal |
| float3 worldRefl;INTERNAL_DATA |
如果修改了表面法线o.Normal,需要使用该变量告诉Unity要基于修改后的法线计算世界空间下的反射方向. 在表面函数中,我们需要使用WorldReflectionVector(IN,o.Normal)来得到世界空间下的反射方向 |
| float3 worldNormal | 包含了世界空间的法线方向.前提是没有修改表面法线o.Normal |
|
float3 worldNormal;INTERNAL_DATA |
如果修改了表面法线o.Normal,需要使用该变量告诉Unity要基于修改后的法线计算世界空间下的法线方向. 在表面函数中,我们需要使用WorldNormalVector(IN,o.Normal)来得到世界空间下的法线方向 |
第18章 基于物理的渲染
| 文件 | 描述 |
| UnityPBSLighting.cginc | 定义了表面着色器使用的标准光照函数和相关的结构体等,如LightingStandardSpecular函数和SurfaceOutputStandardSpecular结构体 |
| UnityStandardCore.cginc |
定义了Standard和Standard(Specular setup)Shader使用的顶点/片元着色器和相关的结构体,辅助函数等,如vertForwardBase,fragForwardBase,MetallicSetup,SpecularSetup 函数和VertexOutputForwardBase,FragmentCommonData结构体 |
| UnityStandardBRDF.cginc | 实现了Unity中基于物理的渲染技术,定义了BRDF1_Unity_PBS,BRDF2_Unity_PBS和BRDF3_Unity_PBS等函数,来实现不同平台下的BRDF |
| UnityStandardInput.cginc |
声明了Standard Shader使用的相关输入,包括Shader使用的属性和顶点着色器的输入结构体VertexInput,并定义了基于这些输入的辅助函数,如TexCoords,Albedo, Occlusion,SpecularGloss等函数 |
| UnityStandardUtils.cginc | Standard Shader 使用的一些辅助函数,将来可能会移到UnityCG.cginc文件中 |
| UnityStandardConfig.cginc |
对 Standard Shader 的相关配置,例如默认情况下关闭简化版的PBS实现(将UNITY_STANDARD_SIMPLE设为0),以及使用基于归一化的Blinn-Phong模型而非GGX模型来实现BRDF (将UNITY_BRDF_GGX设为0) |
| UnityStandardMeta.cginc | 定义了Standard Shader中"LightMode"为"Meta"的Pass(用于提取光照纹理和全局光照的相关信息)使用的顶点/片元着色器,以及它们使用的输入/输出结构体 |
| UnityStandardShadow.cginc | 定义了Standard Shader中"LightMode"为"ShadowCaster"的Pass(用于投射阴影)使用的顶点/片元着色器,以及它们使用的输入/输出结构体 |
| UnityGlobalIllumination.cginc | 定义了和全局光照相关的函数,如UnityGloballIllumination函数 |
第19章 Unity5更新了什么
第20章 还有更多内容吗
Unity Shader 入门精要学习 (冯乐乐 著)的更多相关文章
- Unity Shader入门精要学习笔记 - 第17章 Unity的表面着色器探秘
转自 冯乐乐的<Unity Shader 入门精要> 2010年的Unity 3 中,Surface Shader 出现了. 表面着色器的一个例子. 我们先做如下准备工作. 1)新建一个场 ...
- Unity Shader入门精要学习笔记 - 第16章 Unity中的渲染优化技术
转自冯乐乐的 <Unity Shader 入门精要> 移动平台的特点 为了尽可能一处那些隐藏的表面,减少overdraw(即一个像素被绘制多次),PowerVR芯片(通常用于ios设备和某 ...
- Unity Shader入门精要学习笔记 - 第15章 使用噪声
转载自 冯乐乐的 <Unity Shader 入门精要> 消融效果 消融效果常见于游戏中的角色死亡.地图烧毁等效果.这这些效果中,消融往往从不同的区域开始,并向看似随机的方向扩张,最后整个 ...
- Unity Shader入门精要学习笔记 - 第14章非真实感渲染
转载自 冯乐乐的 <Unity Shader 入门精要> 尽管游戏渲染一般都是以照相写实主义作为主要目标,但也有许多游戏使用了非真实感渲染(NPR)的方法来渲染游戏画面.非真实感渲染的一个 ...
- Unity Shader入门精要学习笔记 - 第11章 让画面动起来
转自 冯乐乐的 <Unity Shader入门精要> Unity Shader 中的内置变量 动画效果往往都是把时间添加到一些变量的计算中,以便在时间变化时画面也可以随之变化.Unity ...
- Unity Shader入门精要学习笔记 - 第10章 高级纹理
转载自 冯乐乐的 <Unity Shader入门精要> 立方体纹理 在图形学中,立方体纹理是环境映射的一种实现方法.环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层 ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- Unity Shader入门精要学习笔记 - 第8章 透明效果
转载自 冯乐乐的 <Unity Shader入门精要> 透明是游戏中经常要使用的一种效果.在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道.当开启透明混合后,当一个物体被渲染 ...
- Unity Shader入门精要学习笔记 - 第7章 基础纹理
转自 冯乐乐的 <Unity Shader 入门精要> 纹理最初的目的就是使用一张图片来控制模型的外观.使用纹理映射技术,我们可以把一张图“黏”在模型表面,逐纹素地控制模型的颜色. 在美术 ...
随机推荐
- tomcat的安装及配置
1.首先进tomcat官网下载zip压缩文件:http://tomcat.apache.org/download-90.cgi 2.解压缩到指定文件压(后面配置环境变量会用到) 3.配置环境变量 4. ...
- Spring接管JDBC
在Spring配置JDBC <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...
- Nio Bio Netty Tomcat的NIO
socket():新建一个文件 bind():绑定到端口,第一个参数就是socket()方法产生的文件描述符 listen():确定新建的这个socket是一个服务器,被动等待网络其他进程链接,参数有 ...
- EffecJava Method
坚持使用Overrider注解,可以预防我们并没有重载的情况出现. 除非使用者绝对安全,不然使用保护性拷贝,可以使程序安全. public class Period {//没有拷贝安全 private ...
- requery.js使用姿势
最近在看requerjs,现在来总结下自己的收获,有不对的地方,望大家指正! 1.首先介绍下requirejs,引用中文官网http://www.requirejs.cn的一句话,requirejs是 ...
- 关于orm 的基础3 day67
day67 ORM 特殊的语法 一个简单的语法 --翻译成--> SQL语句 语法: 1. 操作数据库表 创建表.删除表.修改表 2. 操作数据库行 增.删.改.查 怎么连数据库: 需要手动创建 ...
- ios scrollView代理的用法
// // ZQRViewController.m // 03-图片缩放 // // Created by apple on 17-08-25. // #import "ZQRViewCon ...
- 用户登陆代码py
实现用户输入用户名和密码,当用户名为 seven 且 密码为 123 时,显示登陆成功,否则登陆失败! 实现用户输入用户名和密码,当用户名为 seven 且 密码为 123 时,显示登陆成功,否则登陆 ...
- 如何给cbv的程序添加装饰器
引入method_decorator模块 1,直接在类上加装饰器 @method_decorator(test,name=‘dispatch’) class Loginview(view) 2,直接在 ...
- 2019-03-01-day002-基础编码
01 昨日内容回顾 编译型: 一次性编译成二进制. 优点:质型速度快. 确定:开发效率低,不能跨平台. 解释型: 逐行解释,逐行运行. 优点:开发效率高,可以跨平台. 缺点:回字形效率低. pytho ...