一.创建第一个Shader
组织架构:
MeshFilter决定了是什么样的模型,MeshRenderer能够将模型绘制出来,并设置材质,每一个材质对shader的封装,在此基础上还包括变量以及属性的定义,材质就类似我们不同表面的效果。每一个材质都会封装一个具体的shader来执行具体的着色效果。
unity提供的默认shader类型
- Standard Surface Shader:标准的表面着色器(一般都使用标准的表面着色器)
- Unlit Shader:无光照着色器
- Image Effect Shader: 图像特效着色器
- Compute Shader:计算着色器(通过计算机GPU数值计算用到的着色器)
- Shader Variant Collection:
unity在我们新建shader的时候,为我们的shader填充了基本的框架代码,默认是接收纹理贴图的漫反射的shader,我们通过对它进行修改,来学习快速开发自定义shader。
1.安装shader插件
unity创建好的shader文件,使用VS打开的话会出现杂项文件的问题,这时候我们本身安装环境是安装好的,只需要下载好shader的扩展(代码提示)即可。
扩展-管理扩展中找到以下shader扩展工具,下载即可。
2.shader代码模板介绍
shader文件的路径:
Shader "Custom/StandardDiffuse"
这个shader路径对应的是材质materials的shader选项,根据这个shader文件路径,可以在materials里面的shader选项中找到,同时改变了这个路径之后,对应的目录路径也会随之改变。比如我改成如下路径规范:
Shader "ShaderCaseStudy/Chapter01/StandardDiffuse"
对应的shader选项会变成这样:
Properties属性块:
属性块中会声明好我们需要用到的属性,比如颜色,纹理,都是在里面进行定义。同时在属性块中里面定义的属性,可以在unity面板查看器里面可以进行相应的指定和初始化。
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader具体的着色执行:
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
CGPROGRAM以及ENDCG包裹起来的代码称之为CG语言,进行具体的着色操作,一些计算都在里面进行。
3.创建自定义属性
创建自定义属性,默认是下划线开头的。自定义属性结构如下:
(下划线)+属性名(“GUI名称,也就是unity面板上的名称”,“属性的类型”)= 默认值
注意点:当我们有多个对象使用同一个材质的时候,我们改变了材质的属性值后,会影响所有使用这个材质的对象。所有我们需要改变材质的时候,都会习惯的新建一个材质,然后设置不同的值,最后在赋给对象。
常见的属性值类型有:
//在这里,我们使用shader代码创建自定义自己的属性
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
//自定义属性
//添加环境光的颜色
_AmbientColor("Ambient Color",Color) = (1,1,1,1)
//添加自定义滑块的取值
_MySliderValue("This is a slider",Range(0,10)) = 2.5
}
另一边unity面板编辑器也可以接收并使用对应我们创建好的属性:
实际上在GUI上创建的是Ambient Color和This is a slider这两个元素,在shader文件里面,将他们分别保存在_AmbientColor和_MySliderValue变量里面,这样GUI查看器的属性值和shader属性值相关联,使得它们指向同一个数据。
4.变量关联
自定义好shader属性的同时,我们也需要在SubShader声明一个同名称的变量,使得在SubShader声明的变量和shader里面的属性值相关联。只有通过这种方式才能让CG语言里面的变量和属性里面的变量关联起来。
GUI查看器---》属性块---》CG语言
常用的对应关系有以下几种:
// Color,Vector float4,half4,fixed4(数字4表示4个元素)
//Rnage,Float float,half,fixed
//2D sample2D
//3D sample3D
//Cube sampleCube
CG语言在脚本中也不允许有代码错误出现,如果有错误,材质会被渲染成无着色的效果。在unity2017之后,无着色效果是用一种纯白色来表示。代码错误提示不像C#出现在编辑器上面,shader的代码错误提示是出现在unity面板编辑器上,双击错误会跳转到VS错误那一行。并且,shader错误不会停止unity的运行,如何判断是否有错误,只能判断是否有着色效果来进行判断。
注意点:对材质的属性值修改是一种永恒的修改,也就是你修改更新值了,就会发生修改值的保存。
二、表面着色器和纹理映射
表面着色器的工作流程:
获取3D模型数据--》传入到修改器里面--》将input作为我们表面函数的输入结构体--》在表面函数中进行计算--》将计算结果填写到表面输出结构体中+从场景中获取的光源信息结合起来进行物理计算(光照模型的计算内容)--》得到屏幕上每一个像素点最终的颜色信息。
1.漫反射着色
漫反射每一个物体都会有的,是比较简单的一种反射效果,在着色器中比较容易实现,并且在低模游戏中比较常用。
光照模型、以及光照模型对应的参数:
unity5之后,引入了物理渲染,需要提供更多的渲染相关的信息(平滑度、光滑度),Specular:镜面反射
常用的结构体属性介绍:
去掉光照,会发现立方体每一个面都是一样的,没有着色效果,如果有了光照之后,漫反射效果就会很明显,每一个方向不一样对应的着色也是不一样的。
有光照之后的漫反射效果明显:
漫反射代码详解:
Shader "ShaderCaseStudy/Chapter02/CustomDiffuse"
{
Properties
{
//漫反射只需要物体表面无光照的颜色
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
//设置着色器常用标签
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// surface函数告诉我们当前表面处理函数是surf,Standard表示使用的是标准的光照模型,fullforwardshadows表示完整的阴影效果
#pragma surface surf Standard fullforwardshadows
// 使用的shader模型是3.0的版本,此版本可以获得更好的光照效果
#pragma target 3.0
//表面函数的输入结构体
struct Input
{
float2 uv_MainTex;
};
fixed4 _Color;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
//表面处理函数,参数分别是输入结构体,输出结构体
//注意点:不同的光照模型所需要的参数是不一样的,所返回的类型也随之变化
void surf (Input IN, inout SurfaceOutputStandard o)
{
// 将输入体获取到的结构信息计算后填充到输出结构体,表面输出结构体在传递到光照模型进行光照计算
//所以如何更好的计算决定了最后的效果是怎么样的
fixed4 c = _Color;
o.Albedo = c.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
2.压缩数组
shader里面的代码都是针对于模型的顶点以及像素进行,所以GPU采用并行计算运行的原因。在CG里面有两种类型的变量,一种是单值,一种是压缩数组。
压缩数组变量,例如:float3(3个float类型的变量),int4(4个int类型的变量)
针对于压缩数组,有几种特点:
- 混写(允许在一行代码中多个元素进行重新排列)
- 涂抹(单值赋值实际上是给多个变量赋同一个值)
- 遮蔽
_m表示获取的是矩阵里面的成员,必须得加上
3.给着色器添加纹理
新建shader文件,将对应的模型贴图指定到_MainTex属性值当中,然后模型使用对应的这个材质球,即可给着色器添加纹理效果。
4.通过UV值滚动纹理
Shader "ShaderCaseStudy/Chapter02/ScrollingShader"
{
Properties
{
_MainTint ("MainTint", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
//创建自定义属性值
_ScrollingXSpeed("X Scrolling Speed",Range(0,10)) = 2
_ScrollingYSpeed("Y Scrolling Speed",Range(0,10)) = 2
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
//输入结构体中传入主纹理的UV值
struct Input
{
float2 uv_MainTex;
};
fixed4 _MainTint;
fixed _ScrollingXSpeed;
fixed _ScrollingYSpeed;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
//获取主纹理的UV值
fixed2 scrolledUV = IN.uv_MainTex;
//获取UV的滚动偏移量
fixed xScrollValue = _ScrollingXSpeed * _Time; //__Time为float类型,
fixed yScrollValue = _ScrollingYSpeed * _Time;
//将偏移量应用到UV上面
scrolledUV+=fixed2(xScrollValue,yScrollValue);
//获取纹理上面的信息
half4 c = tex2D(_MainTex,scrolledUV);
//赋值到最后的输出结构体里面
o.Albedo = c.rgb * _MainTint;
//设置rgb通道
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
四个shader会使用到的内置时间:
5.法线映射
在光线与材质表面交互过程中,朝向起到了非常重要的作用,如果我们面对不同的方向,反射光线的角度也是不同的。即使多个面的颜色是相同的,但是着色效果却是不同的。对于曲面对象来说,就会出现渲染的效果很不理想。为了解决这个问题,光线和材质表面交互我们不考虑朝向,而是使用顶点的法向,也就是三角形三个顶点包含法向信息(顶点可以保存信息),实际上三角形每个顶点上都有自己的法向,这个法向就是三角形顶点线性的差值,这样就实现高模效果的实例。
法线贴图也称为凹凸贴图,法线贴图就是控制模型的凹凸变化的。
Shader "ShaderCaseStudy/Chapter02/NormalMapShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
//创建法线贴图属性,法线贴图默认值是bump,告诉unity这是一个法线贴图
_NormalTex("Normal map",2D) = "bump"{}
//创建法线贴图强度
_NormalMapIntensify("Normal Intensify",Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
struct Input
{
//提供法线映射的UV值
float2 uv_NormalTex;
};
fixed4 _Color;
sampler2D _NormalTex;
fixed _NormalMapIntensify;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
//对纹理进行采样,并解析,最后得出正确的法线信息
//UnpackNormal是unity内置提供的辅助函数
float3 normalMap = UnpackNormal(tex2D(_NormalTex,IN.uv_NormalTex));
//根据强度大小来调节法线贴图的强度
normalMap.x *=_NormalMapIntensify;
normalMap.y *=_NormalMapIntensify;
//将法线映射填充到标准的输出结构体中
o.Normal = normalize(normalMap);//normalize作用:标准化为单位矢量
o.Albedo = _Color.rgb;
o.Alpha = _Color.a;
}
ENDCG
}
FallBack "Diffuse"
}
最终效果:
6.创建透明材质
Shader "ShaderCaseStudy/Chapter02/TransparentShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader
{
//Tags用于添加渲染信息的,这些渲染信息描述了如何渲染
//渲染类型指定为透明类型
//忽视其他的投影物体
//设置渲染队列
Tags {
"RenderType"="Transparent"
"IgnoreProjector"="True"
"Quene" = "Transparent"
}
LOD 200
Cull Back //剔除背面物体
CGPROGRAM
// alpha:fade设置alpha通道的融合(当前材质的所有像素必须跟alpha通道的值与屏幕上现在绘有的像素进行融合,这样才会实现透明效果)
#pragma surface surf Standard alpha:fade
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
fixed4 _Color;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
最终效果:
渲染队列参数详解:
7.创建全息投影
Shader "ShaderCaseStudy/Chapter02/HolographicShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
//定义自定义属性,控制全息投影的强度(边缘效果的强弱)
_RimEffect("Rim Effect",Range(-1,1)) = 0.25
}
SubShader
{
Tags {
"RenderType"="Transparent"
"IgnoreProject" = "True"
"Quene"="Transparent"
}
LOD 200
//不剔除背景
Cull off
//不需要光照效果
Lighting off
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Lambert alpha:fade
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
//世界的法线
float3 worldNormal;
//视图的方向
float3 viewDir;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
float _RimEffect;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
//对主纹理进行采样
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
//计算边缘效果的强弱,也就是光线射向模型与模型法线形成的夹角
//求点积的时候,点积值为0,垂直了,点积值为1,表示垂直了(相同方向),点积值为-1,表示垂直(相反方向)
float border = 1-abs(dot(IN.viewDir,IN.worldNormal));
//差值运算,越接近中心,越透明,相反越远中心,就越实体
float alpha = (border * (1-_RimEffect) + _RimEffect);
o.Alpha = c.a * alpha;//赋值到表面输出结构体中
}
ENDCG
}
FallBack "Diffuse"
}
最终效果:
8.纹理的压缩和融合
lerp插值的运算介绍:
详细介绍:如果a,b是两个纹理,这时f是一个系数的话,这时候a,b里面每一个像素都通过f同一个系数进行差值。如果f也是一个纹理的话,a,b里面每一个像素的话都会对应f里面每一个相同的位置比例的像素进行不同的差值。
Shader "ShaderCaseStudy/Chapter02/PackedTextureShader"
{
Properties
{
//默认漫反射的主色调
_MainTint("Diffuse Tint",Color) = (1,1,1,1)
//添加颜色
_ColorA("Terrain A",Color) = (1,1,1,1)
_ColorB("Terrain B",Color) = (1,1,1,1)
//添加不同纹理
_RTexture("Red Channel Texture",2D) = ""{}
_GTexture("Green Channel Texture",2D) = ""{}
_BTexture("Blue Channel Texture",2D) = ""{}
_ATexture("Alpha Channel Texture",2D) = ""{}
//设置控制融合的纹理
_BlendTex("Blend Texture",2D) = ""{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_BlendTex;
};
float4 _MainTint;
float4 _ColorA;
float4 _ColorB;
sampler2D _RTexture;
sampler2D _GTexture;
sampler2D _BTexture;
sampler2D _ATexture;
sampler2D _BlendTex;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
//对融合纹理进行采样
float4 blendData = tex2D(_BlendTex,IN.uv_BlendTex);
float4 rTexData = tex2D(_RTexture,IN.uv_BlendTex);
float4 gTexData = tex2D(_GTexture,IN.uv_BlendTex);
float4 bTexData = tex2D(_BTexture,IN.uv_BlendTex);
float4 aTexData = tex2D(_ATexture,IN.uv_BlendTex);
//计算出最后融合的结果
float4 finalColor;
finalColor = lerp(rTexData,gTexData,blendData.g);
finalColor = lerp(finalColor,bTexData,blendData.b);
finalColor = lerp(finalColor,aTexData,blendData.a);
//设置finalColor的alpha值
finalColor.a = 1.0;
//差值
float terrainLayers = lerp(_ColorA,_ColorB,blendData.r);
finalColor*=terrainLayers;
//约束值的范围
finalColor = saturate(finalColor); //约束在有效颜色范围之内
o.Albedo = finalColor * _MainTint.rgb; // 设置主色调
o.Alpha = finalColor.a;
}
ENDCG
}
FallBack "Diffuse"
}
指定纹理贴图:
最终效果:
9.创建贴合地图的圆环
如果地形凹凸不平,我们需要使用圆环自适应凹凸不平的地形.效果如下:
我们需要创建地形,以及对应的材质。
给自定义地形添加纹理:
给自定义地形添加自定义材质:
RediusShader
Shader "ShaderCaseStudy/Chapter02/RediusShader"
{
Properties
{
//圆环的中心位置
_Center("Center",Vector) = (0,0,0,0)
//定义半径的大小
_Radius("Radius",Float) = 0.5
//定义圆环的颜色
_RadiusColor("Radius Color",Color) = (1,0,0,1)
//定义圆环的宽度
_RadiusWidth("Radius Width",Float) = 2
//主纹理
_MainTex("Terrian Texture",2D) = ""{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
struct Input
{
float2 uv_MainTex;
float3 worldPos;//以世界坐标为中心来绘制圆环
};
sampler2D _MainTex;
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
//当前绘制的位置与中心位置的距离
float d = distance(_Center,IN.worldPos);
//判断是否处于圆环绘制范围
if(d>_Radius&&d<_Radius+_RadiusWidth)
{
o.Albedo = _RadiusColor;
}else
{
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
}
}
ENDCG
}
FallBack "Diffuse"
}
RediusController.cs
该脚本实现的作用是移动的时候,圆环跟着移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RediusController : MonoBehaviour
{
public Material radiusMaterial;
public float radius = 1;
public Color color = Color.white;
void Update()
{
//圆环的设置
if(radiusMaterial != null)
{
radiusMaterial.SetVector("_Center", transform.position);
radiusMaterial.SetFloat("_Radius", radius);
radiusMaterial.SetColor("_RadiusColor", color);
}
}
}
并且指定现有地形所使用的材质:
三、光照模型(LightingModel)
光照模型负责接收属性,计算每一个像素的着色效果。unity将这些实现的光照效果隐藏起来,大部分情况下写一个光照模型都需要理解光照反射,折射的物理原理,实现起来复杂。大部分情况下只需要调用unity内置提供的光照模型即可。但是理解光照模型的实现原理对于后面的学习是非常有必要的
1.创建一个自定义的漫反射光照
Lambert经常用于低模游戏中,在手游中非常受欢迎。一个表面反射光的数量取决于入射光线和表面法线的夹角
点积为0,表示两个矢量是垂直的,也就是夹角为90,如果点积对于1,-1,表示两者是互相平行的,为1表示 平行的方向是一致的,为-1表示平行的方向是相反的。
复杂的平面光线反射:
注意点:unity版本的不同其光照强度是不同的,在unity4的lambert需要*2,光照强度比较弱,在unity5就不需要乘2
Shader "ShaderCaseStudy/Chapter03/LambertShader"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Lambert是unity内置lambert的实现,如果需要手动的只需要加上Simple
#pragma surface surf SimpleLambert
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
//SimpleLambert和LightingSimpleLambert自动关联起来
//SurfaceOutput表面输出结构体,lightDir:光线的方向 atten:光线的衰减率
half4 LightingSimpleLambert(SurfaceOutput s,half3 lightDir,half3 atten){
//一个表面反射光的数量取决于入射光线和表面法线的夹角
//顶点的法线和光线的方向,计算两个之间的夹角
half NdotL = dot(s.Normal,lightDir);
//根据夹角大小的不同,计算发射光线的强弱
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten); //_LightColor0场景中光线的颜色
c.a = s.Alpha;
return c;
}
ENDCG
}
FallBack "Diffuse"
}
2.创建卡通着色器
Shader "ShaderCaseStudy/Chapter03/TooShader卡通着色器"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_RamTex("Ramp",2D) = "white"{}
_ToonShaderLayer("Toon Shading Layers",Range(2,10)) = 10
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Toon2
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _RamTex;
struct Input
{
float2 uv_MainTex;
};
float _ToonShaderLayer;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
fixed4 LightingToon(SurfaceOutput s,fixed3 lightDir,fixed atten){
//法线和光线方向的点积
half NdotL = dot(s.Normal,lightDir);
//对坡度映射进行采样
NdotL = tex2D(_RamTex,fixed2(NdotL,0.5));
//新建返回的值
fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
c.a = s.Alpha;
return c;
}
//通过值来控制卡通着色器
half4 LightingToon2(SurfaceOutput s,half3 lightDir,half3 viewDir,half atten){
half NdotL = dot(s.Normal,lightDir);
half toon = floor(NdotL*_ToonShaderLayer) / (_ToonShaderLayer - 0.5);
half4 c;
c.rgb = s.Albedo * _LightColor0 * toon * atten;
c.a = s.Alpha;
return c;
}
ENDCG
}
FallBack "Diffuse"
}
指定纹理:
最终效果:
3.创建方式反射模型
一般的物体是由镜面反射和漫反射构成:
常用的光照函数:
计算光照的公式:
L:光照方向,R:反射光线,N:法线方向,V:视图方向
I(方式模型的光照强度) = D(漫反射)+S(镜面反射)
D = N.L(法线与光照方向的点积)
S = (R.V)的p次方 (p表示镜面反射的系数,R反射光线的矢量,V是视图方向的矢量)
R(反射光线的矢量) = 2N.(N.L) - L
对应的方式反射的shader代码:
Shader "ShaderCaseStudy/Chapter03/PhongSpecShader"
{
Properties
{
//创建漫反射主色调
_MainTint ("Diffuse Tint", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
//添加镜面反射的颜色
_SpecularColor("Specular Color",Color) = (1,1,1,1)
//设置镜面反射的强度--控制系数
_SpecPower("Specular Power",Range(0.1,30)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf CustomPhong
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
float4 _SpecularColor;
float4 _MainTint;
float _SpecPower;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
fixed4 LightingCustomPhong(SurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed3 atten){
//求反射部分,求法线和光线方向的点积
float NdotL = dot(s.Normal,lightDir);
//求反射的向量--需要公式,标准化为单位矢量
float3 reflectionVector = normalize(2.0 * s.Normal * NdotL - lightDir);
//计算镜面反射的部分--与观察的方向和光线的方向是有关系的
//先求镜面反射的系数
float spec = pow(max(0,dot(reflectionVector,viewDir)),_SpecPower);//max忽略负值
//镜面发射的颜色信息
float3 finanSpec = _SpecularColor.rgb * spec;
//最后的效果
fixed4 c;
//返回漫反射的颜色+镜面反射的颜色
c.rgb = (s.Albedo * _LightColor0.rgb * max(0,NdotL) * atten) + (_LightColor0.rgb * finanSpec);
c.a = s.Alpha;
return c;
}
ENDCG
}
FallBack "Diffuse"
}
最终效果:
4.创建宾氏反射模型
宾氏反射模型与方式反射模型相比,宾氏反射模型表面更光滑一些,方式反射模型不会那么规整。
L:光照方向,R:反射光线,N:法线方向,V:视图方向 H:半向量(L和V的中间向量)
相关宾式反射模型shader代码:
Shader "ShaderCaseStudy/Chapter03/BlinnPhongSpecShader"
{
Properties
{
_MainTint ("Main Tint", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
//添加镜面反射的颜色
_SpecularColor("Specular Color",Color) = (1,1,1,1)
//设置镜面反射的强度--控制系数
_SpecPower("Specular Power",Range(0.1,60)) = 3
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf CustomBlinnPhong
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
float4 _SpecularColor;
float4 _MainTint;
float _SpecPower;
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
fixed4 LightingCustomBlinnPhong(SurfaceOutput s,fixed3 lightDir,half3 viewDir,fixed atten){
//法向和光线方向的点积
float NdotL = max(0,dot(s.Normal,lightDir));
//半矢量(光线方向和视图方向的中间位置)
float3 halfVector = normalize(lightDir + viewDir);
//半矢量和法向量的点积
float NdotH = max(0,dot(s.Normal,halfVector));
//镜面反射的系数
float spec = pow(NdotH,_SpecPower);
float4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * NdotL) + (_LightColor0.rgb * _SpecularColor.rgb * spec) * atten;
c.a = s.Alpha;
return c;
}
ENDCG
}
FallBack "Diffuse"
}
最终效果:
5.创建各项异性反射模型
各项异性反射模型是一种镜像类型,它模拟表面凹槽的定向性,并且在垂直方向上并延伸的镜面反射,这种模型特别适用于模拟拉丝金属效果。
Shader "ShaderCaseStudy/Chapter03/AnisotropicSpecShader"
{
Properties
{
_MainTint("Diffuse Tint",Color) = (1,1,1,1)
_MainTex("Base (RGB)",2D) = "white"{}
_SpecularColor("Specular Color",Color) = (1,1,1,1)
_SpecularAmount("Specular Amount",Range(0,1)) = 0.5
//镜面反射的强弱
_SpecularPower("Specular Power",Range(0,1)) = 0.5
//各项异性的方向
_AnisoDir("Anisotropic Direction",2D) = ""{}
//纹理的偏移控制
_AnisoOffset("Anisotropic Offset",Range(-1,1)) = -0.2
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf AnisotropicSpec
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
struct Input
{
float2 uv_MainTex;
float2 uv_AnisoDir;
};
sampler2D _MainTex;
sampler2D _AnisoDir;
float4 _MainTint;
float4 _SpecularColor;
float _AnisoOffset;
float _SpecularAmount;
float _SpecularPower;
//创建自定义的输出结构体
struct SurfaceAnisoOutput{
fixed3 Albedo;
fixed3 Normal; //法向量
fixed3 Emission; //自发光
fixed3 AnisoDirection; //各项异性方向
half Specular;//镜面发射系数
fixed Gloss;//高光系数
fixed Alpha; //Alpha的值
};
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceAnisoOutput o)
{
//获取每一个像素的信息
half4 c=tex2D(_MainTex,IN.uv_MainTex) * _MainTint;
//各项异性的法线信息
float3 anisoTex = UnpackNormal(tex2D(_AnisoDir,IN.uv_AnisoDir));
//填充表面输出结构体
o.AnisoDirection = anisoTex;
o.Specular = _SpecularAmount;
o.Gloss = _SpecularPower;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
fixed4 LightingAnisotropicSpec(SurfaceAnisoOutput s,fixed3 lightDir,half3 viewDir,fixed atten){
//求半矢量
fixed3 halfVector = normalize(normalize(lightDir)+normalize(viewDir));
//光线的方向与法线的夹角
float NdotL = saturate(dot(s.Normal,lightDir));
//半矢量和法向的点积
fixed HdotA = dot(normalize(s.Normal+s.AnisoDirection),halfVector);
//各项异性的属性
float aniso = max(0,sin(radians((HdotA+_AnisoOffset) *180)));
//镜面反射系数
float spec = saturate(pow(aniso,s.Gloss * 128) * s.Specular);
fixed4 c;
c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL) +(_LightColor0.rgb * _SpecularColor.rgb * spec)) * atten;
c.a = s.Alpha;
return c;
}
ENDCG
}
FallBack "Diffuse"
}
最终效果:
四、物理渲染
unity5版本之后引入了物理渲染,PBR工作原理的学习
unity5引入了两个重要概念改变了PBR,unity并没有对它们驰加真实的物理限制,PBR通过使用光照模型强制实行一些物理原则来弥补这些缺陷
1.标准光照模型(standard)
- 能量守恒(发射光线不能比入射光线多)
- 维表面散射(粗糙表面反射光线比光滑反射光线更加具有不确定性)
- 菲涅尔反射(镜面反射出现在滤色角)
- 表面遮挡(角落或者其他难以照亮的几何体会变得比较暗) 2.全局光照(Global Illumination GI)
1.理解金属度的设置
在传统的着色器里面,创建材质的时候,很容易引入一些非真实的光照条件,从而打破这种真实感,这是因为着色器中的材质里面所有的属性都是无关联的。通过引入金属工作流,unity对物理的外观表现驰加了更多的约束,使得shader的使用者很难创造出一些不符合物理逻辑的材质。在标准的着色器里面,纯粹的金属材质漫反射部分很暗,镜面反射的颜色取决于LB的纹理映射。相反纯粹的非金属材质漫反射部分取决于Albedo纹理映射,镜面反射的颜色取决于入射光的颜色。
2.给PBR添加透明度
2.1 Transparent
当材质的shader的渲染类型为Transparent的时候,它的透明度强度变化是根据Albedo里面的颜色alpha的通道值来决定的。值越小,透明度也就越明显。
2.2 fade
Transparent与fade的区别在于,在alpha通道值为0的时候,Transparent类型下,因为PBR的作用下它是有轮廓的,但是在fade的模式下,alpha值为0的时候,它是完全看不见,并且边缘没有任何的折射效果。
fade渲染模式适用于非真实的对象,比如说一些科幻题材里面的全息图,激光射线,人造光源,幽灵或者是粒子特效经常会用到这种。
2.3 Cutout
材质会按照像素指定它的alpha通道值,所以有些部分会显示出来,有些却显示不出来,根据像素里面的alpha控制每一部分的显示隐藏。
3.创建镜面反射表面
反射弹针
反射弹针的设置参数: