永远相信美好的事情即将发生 😊!

Unity Shader 入门精要(冯乐乐著)学习笔记(2)——Unity Shader基础

学习 Mavis 107℃ 0评论

Unity Shader 概述

材质是Unity Shader的载体,再将材质赋给相应模型即可查看最终的效果。

Unity中的材质

材质需要结合GameObject的Mesh/Partical Systems组件来工作,新建材质默认使用内置的Standard Shader,可以在材质面板调整Shader中使用的各类参数。

Unity Shader的导入设置面板,可以查看到很多关于该shader的信息:

材质面板中可以修改所选择的Shader的参数:

ShaderLab

Unity提供的专门为Unity Shader服务的一种说明性语言

示例(标准光照模型BumpedDiffuse):

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
//定义Shader的显示路径及名字
Shader "Unity Shaders Book/Common/Bumped Diffuse" {
//属性,用来展示在使用该shader的材质面板,以调节各参数
//写法:_属性名("面板显示属性名",属性类型)=默认值
Properties {
//类型:
//数值类型int、float、Range
//Int("Int",Int)=1
//Float("Float",Float)=1.5
//Range("Range",Range(0.0,5.0))=3.0
//四维向量:vector、color
//Vector("Vector",Vector)=(2,3,6,1)
//Color2("Color",Color)=(1,1,1,1)
//纹理类型(二维存储纹理坐标,变量名_ST自动存储Tiling.xy和offset.xy值):cube、2D、3D
//Cube("Cube",Cube)="white"{}
//2D("2D",2D)=""{}
//3D("3D",3D)="black"{}
_Color ("Color Tint", Color) = (1, 1, 1, 1)//颜色
_MainTex ("Main Tex", 2D) = "white" {}//主纹理  white&bump为内置纹理
_BumpMap ("Normal Map", 2D) = "bump" {}//法线纹理
}
SubShader {
//标签设置(如何渲染对象)
//SubShader中的标签类型:
//Queue //控制渲染顺序
//Background //最先渲染,通常渲染背景
//Geometry //默认的渲染队列,大多数物体以及不透明物体使用该队列
//AlphaTest //需要透明度测试的物体
//Transparent //使用了透明度混合的物体
//Overlay //最后渲染的物体,实现叠加效果
//RenderType //着色器分类
//DisableBatching  //是否批处理
//ForceNoShadowCasting  //是否投射阴影
//IgnoreProjector  //是否受Projector的影响
//CanUseSpriteAtlas  //该SubShader用于精灵时要设置为false
//PreviewType //材质面板如何预览该材质
Tags { "RenderType"="Opaque" "Queue"="Geometry"}
//状态设置(可用于SubShader中应用所有Pass,也可以单独设置在某Pass中)
//常见渲染状态设置选项:
//Cull Back|Front|Off //剔除背面/正面/关闭剔除
//ZTest Less Greater|LEqual|GEqual|Equal|NotEqual|Always //设置深度测试使用函数
//ZWrite On|Off //开启/关闭深度写入
//Blend  //开启并设置混合模式
//Off //关闭混合
//SrcFactor DstFactor  //开启混合并设置混合因子,源颜色乘以SrcFactor+目标颜色乘以DstFactor
//SrcFactor DstFactor SrcFactorA DstFactorA ,使用不同的混合因子混合rgb通道和透明通道
//混合因子:
//Zero //0
//One //1
//SrcColor //源颜色
//SrcAlpha //源颜色的透明值
//DstColor //目标颜色
//DstAlpha //目标颜色的透明值
//OneMinusSrcColor //1-源颜色
//OneMinusSrcAlpha //1-源颜色的透明值
//OneMinusDstColor //1-目标颜色
//OneMinusDstAlpha //1-目标颜色的透明值
//BlendOp BlendOperation //使用除加法外的其他操作
//Add 源+目标
//Sub 源-目标
//RevSub 反转相减 目标-源
//Min 对每一个分量值取最小或最大值,混合因子是没有使用的
//Max
Pass { 
//Name定义pass名字,以供复用(UsePass复用时全部大写,GrabPass抓取屏幕并存储到一张纹理中,用于后续pass处理)
Name "myPass" 
//标签设置
//Pass中的标签类型:
//LightMode //定义该pass在渲染流水线中的角色
//Always //该pass总是被渲染但是不计算任何光照
//ForwardBase //用于前向渲染 计算环境光、一个最重要的平行光(逐像素)、所有的逐顶点/SH光源和Lightmaps
//ForwardAdd //用于前向渲染 计算额外的逐像素光源,每个pass对应一个光源
//根据光源类型和渲染模式来判断选择逐像素、逐顶点、SH这三种中的哪一种处理方式
//最亮的平行光逐像素
//在light面板中Render type 被设置成not important的按逐顶点或者SH
//在light面板中Render type 被设置成important的按逐像素
//若根据上述规则得到的逐像素的光源数量小于Quality Setting 中的Pixel Light Count(逐像素光源数量),则会有更多的光源采用逐像素的方式进行渲染
//Deferred //用于延迟渲染 会渲染G缓冲
//ShadowCaster //把物体的深度信息渲染到阴影映射纹理或一张深度纹理中
//PrepassBase  //用于遗留的延迟渲染
//PrepassFinal //用于遗留的延迟渲染
//Vertex \	VertexLMRGBM \ VertexLM  //用于遗留的顶点照明渲染
//RequireOptions  用于指定当满足某些条件时才渲染该Pass
Tags { "LightMode"="ForwardBase" }
//Cg/HLSL代码开始标签
CGPROGRAM
//#pragma代表编译指令
//表明使用前向渲染路径,且可以得到正确的光照变量
#pragma multi_compile_fwdbase 
//表明顶点着色器和片元着色器的函数名字
#pragma vertex vert
#pragma fragment frag
//#include类似头文件,包含一些预置的函数和宏和一些常用的结构体
//注意使用函数的时候,若传入的是结构体类型就需要结构体内的变量名和函数内使用的变量名相同
#include "Lighting.cginc" 
#include "AutoLight.cginc"
//定义CGPROGRAM中的变量,变量名要与ShaderLab的Properties中定义的一样,变量类型由ShaderLab的Properties中定义的来确定
//对应关系:
//Color Vector : float4 half4 fixed4(精度区别)
//Range Float  : float half fixed
//2D           : sampler2D
//Cube         : samplerCUBE
//3D           : sampler3D
fixed4 _Color;//颜色
sampler2D _MainTex;//主纹理
float4 _MainTex_ST;//主纹理中的tiling(xy)和offset(zw)值
sampler2D _BumpMap;//法线纹理
float4 _BumpMap_ST;//法线纹理中的tiling和offset值
//结构体 a2v代表application to Vertex,定义顶点着色器的输入
struct a2v {
float4 vertex : POSITION;//模型空间中的顶点坐标
float3 normal : NORMAL;//法线
float4 tangent : TANGENT;//切线
float4 texcoord : TEXCOORD0;//第一组纹理坐标
};
//结构体 v2f代表Vertex to fragment,定义顶点着色器的输出和片元着色器的输入
struct v2f {
float4 pos : SV_POSITION;//系统数值:裁剪空间中的顶点坐标
float4 uv : TEXCOORD0; //纹理坐标用于在片元着色器中进行纹理采样
float4 TtoW0 : TEXCOORD1;  //在世界空间下计算光照模型(除此以外还可以在切线空间下计算)
float4 TtoW1 : TEXCOORD2;  //TtoW0 TtoW1 TtoW2组成了从切线空间到世界空间的变换矩阵
float4 TtoW2 : TEXCOORD3;  //TtoW0切线(x轴)-TtoW1副切线/副法线(y轴)-TtoW2法线(z轴)
SHADOW_COORDS(4) //阴影纹理坐标,括号内的4代表下一个可用的插值寄存器的索引号
};
//顶点着色器代码编写
v2f vert(a2v v) {
v2f o;//定义输出变量
o.pos = UnityObjectToClipPos(v.vertex);//将模型空间中的顶点坐标变换到裁剪空间
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;//用纹理坐标的xy存储主纹理坐标
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;//用纹理坐标的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; //计算世界空间中的副切线。v.tangent.w存储方向
//构造从切线空间转换到世界空间的变换矩阵的三个行向量,用于左乘坐标时利用dot计算
//并用三个行向量的最后一位存储世界空间中的顶点坐标
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);  
TRANSFER_SHADOW(o);//会把顶点坐标从模型空间变换到光源空间
return o;
}
//片元着色器代码编写
fixed4 frag(v2f i) : SV_Target {//系统数值:代表片元着色器的输出会存储到渲染目标中
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);//取出在顶点着色器中存储的世界空间中的顶点坐标,用于在世界空间下计算光照
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));//计算世界空间下的light方向(不同类型的光照会有不同的计算方式)
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));//计算世界空间下的view方向
//法线纹理中存储的是切线空间下,法线经过映射后得到的像素值。(-1,1)映射到(0,1) 即(normal+1)/2=pixel
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));//取出在切线空间下存储的法线纹理坐标,tex2D采样,UnpackNormal反映射,即pixel*2-1=normal
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));//将法线坐标从切线空间变换到世界空间(等同于变换矩阵左乘坐标)
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;//计算漫反射系数
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;//计算环境光
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir));//计算漫反射(兰伯特漫反射模型)
UNITY_LIGHT_ATTENUATION(atten, i, worldPos);//同时计算光照衰减和阴影,atten为两者乘积
return fixed4(ambient + diffuse * atten, 1.0);//1.0为透明度
}
//CGPROGRAM代码结束标签
ENDCG
}
Pass { 
Tags { "LightMode"="ForwardAdd" }
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
// Use the line below to add shadows for point and spot lights
//			#pragma multi_compile_fwdadd_fullshadows
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
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;
SHADOW_COORDS(4)
};
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; 
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);  
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_Target {
//与上一个pass区别在于没有环境光和自发光的计算,因为这些部分只需要计算一次即可
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir));
UNITY_LIGHT_ATTENUATION(atten, i, worldPos);
return fixed4(diffuse * atten, 1.0);
}
//Cg/HLSL代码结束标签
ENDCG
}
} 
//上述SubShader都失败后用于回调的Unity Shader
FallBack "Diffuse"
}

表面着色器以及真正意义上的Shader代码都写在SubShader中,顶点/片元着色器以及固定函数着色器写在Pass里。


Mavis , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Unity Shader 入门精要(冯乐乐著)学习笔记(2)——Unity Shader基础
喜欢 (1)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址