unity Shader Lab(cg hlsl glsl)着色器入门教程 以及 vstudio 支持unity shader语法(更新中)

前言:

如果你对cg glsl hlsl 顶点着色器 片段着色器 表面着色器 固定渲染管线 等等有所疑惑,或是想学会unity的渲染,看这一篇就足够了。另外我博客的shader分类中还有很多shader教程和源码,每篇源码都有实现思路、语法功能注释,还在不断更新添加中。感兴趣的请自行查看

让vs支持shader

点击vs的工具>扩展和更新>联机>visual studio marketplace
搜素shader
点击下载
重启vs
弹出installer,点击修改 即可。

一,基础知识

关于着色器语言与Shader Lab

着色器语言有三种:
hlsl(DirectX)
glsl(OpenGL)
cg(支持以上两种)
Shader Lab 是unity封装了cg、hlsl、glsl的unity专有着色器语言。shaderlab具有跨平台、图形化编程、便于着色器与unity通信等优点。在unity2018.3.5f以后版本,可以使用图形化工具shader graph来大幅缩减shader编程成本,使这个令人头疼的语言走入寻常百姓家。。。。。但作为一个unityer最好具有基本的Shader Lab编程能力,至少了解基本效果的代码实现过程。

mesh、材质、着色器的与模型的关系

把模型比作一只兔子,那mesh就是兔子的骨架,顶点就是骨架的端点,片段就是由顶点组成的面,材质就是皮肉用来装饰片段的,shader就是用来控制如何显示材质的。

关于顶点、片段处理器

顶点着色器和片段着色器都有自己独立的硬件处理单元。该硬件处理单元拥有非常强大的并行运算能力,非常擅长矩阵计算,片段处理器还可以告诉查询纹理信息。

白话:顶点着色器负责定位像素位置!片段着色器负责修改像素颜色!!

顶点着色程序与片断着色程序通常是同时存在,相互配合,前者的输出作为后者的输入。不过,也可以只有顶点着色程序。如果只有顶点着色程序,那么只对输入的顶点进行操作,而顶点内部的点则按照硬件默认的方式自动插值。例如,输入一个三角面片,顶点着色程序对其进行phong光照计算,只计算三个顶点的光照颜色,而三角面片内部点的颜色按照硬件默认的算法(Gourand明暗处理或者快速phong明暗处理)进行插值,如果图形硬件比较先进,默认的处理算法较好(快速phong明暗处理),则效果也会较好;如果图形硬件使用Gourand明暗处理算法,则会出现马赫带效应(条带化)。
而片断着色程序是对每个片断进行独立的颜色计算,并且算法由自己编写,不但可控性好,而且可以达到更好的效果。
由于GPU对数据进行并行处理,所以每个数据都会执行一次shader程序程序。即,每个顶点数据都会执行一次顶点程序;每个片段都会执行一次片段程序。
片段就是所有三维顶点在光栅化之后的数据集合,这些数据没有经过深度值比较,而屏幕显示的像素是经过深度比较的。

顶点着色器、片段着色器与表面着色器的关系

作用

顶点着色器负责顶点坐标变换,片段着色器负责像素的颜色计算。顶点着色器计算好坐标信息后传入片段着色器计算颜色。所以顶点着色器和片段着色器是合作关系。
表面着色器是封装了顶点和片段着色器的新api。与他们属于上下层关系。
shader编译时会将表面渲染代码编译成多个pass代码块,再分解成顶点/片元着色器。

区别

顶点着色器用于处理顶点。片段着色器用于处理面。
表面着色器是对顶点着色器与片段着色器的进一步封装。
即是说,表面着色器有一套即成的处理办法,不用去搞那些细节。
而顶点着色器和片段着色器更接近底层,可以处理一些细节问题。

着色器的工作流程
请求
3D应用或游戏
3D API : OpenGL DirectX 接口
GPU Front End : GPU前端
Primitive Assembly : 图元装配
Programmable Vertex Proccessor : 可编程顶点处理器
Rasterization and interpolation : 光栅化与插值
Pixel Operation: 像素处理
Frame Buffer : 帧缓存
Programmable Fragment Proccessor: 可编程片段处理器

二,Shader Lab语法

基本结构
Shader "path/name" {
 	Properties{
 		//_CG变量名 ("unity可见的变量名", 属性类型) = 值
 		_Color ("My Color", Color) = (1, 1, 1, .5) 
 	 } 
 	 Subshader{
 	  }
 }
  • path/name :unity编辑器中检索的位置和shader名。shader名尽量与shader文件名一样。
  • Properties:shader属性,用于cg与unity通信。
  • Subshader:shader解决方案。每个shader程序包含至少一个subshader,用于解决硬件性能兼容问题。shader的主代码部分都写在这里
Properties

属性的结构:_CG变量名 (“unity可见的变量名”, 属性类型) = 值
例:_Color (“My Color”, Color) = (1, 1, 1, .5)

属性类型表

类型说明实例
Int整型(.1, 2)
Float浮点数.5
Vector四维向量(.5, 1 , 1, 0.5)
Range范围1-2.3的浮点数(1, 2.3)
ColorRGBA颜色(1,1,1,.5)
2D2d贴图, 2d纹理,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者”white”,”black”,”gray”,”bump”中的一个”white”{}
3D3d贴图
Cube6面立方贴图”white”{}
Rect矩形贴图”white”{}
SubShader
SubShader { 
	Tags { "RenderType" = "Opaque" "ForceNoShadowCasting" = "True" "IgnoreProjector" = "True"}   
	LOD 100
	 Pass{
		 	Fog{Mode OFF}
		 	
		 	//固定渲染器
			Material{
               	Diffuse[_Color]  //设置漫反射
       		}      
            Lighting On
	        SeparateSpecular On   //启用高光颜色
            //设置纹理
            SetTexture[_MainTex]{
		 	//表面渲染器
		 	#pragma surface surf Lambert
		 	//顶点渲染器
			#pragma vertexvert
			//片段渲染器
		 	#pragma fragment frag
	 }
	Pass{
	}
}
Tags

tags可以填写多个命令,可以控制渲染时机。可以通过在unity的asset窗口中点击shader查看tags生效情况。

标签说明
“RenderType”=“Opaque”系统在渲染不透明物体时调用该shader
“RenderType” = “Transparent”系统在渲染透明物体时调用该shader,绝大部分透明的物体、包括粒子特效都使用这个
“RenderType” = “Background”系统渲染背景时调用,天空盒都使用这个
“RenderType” = “Overlay”系统渲染gui镜头时调用,GUI、镜头光晕都使用这个
“IgnoreProjector”=“True”忽略Projectors
“ForceNoShadowCasting”=“True”不生成阴影
“Queue”=“xxx”指定渲染队列顺序,下面有详细说明

Queue的说明

关键字说明
Background最先调用的,用来渲染天空盒或背景
Geometry默认值,用来渲染非透明物体(一般情况下,场景中的绝大多数物体应该是非透明的)
AlphaTest用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
Transparent以从后往前的顺序渲染透明物体
Overlay用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)

这些预定义的值本质上是一组定义整数,Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000,最后Overlay = 4000。在我们实际设置Queue值时,不仅能使用上面的几个预定义值,我们也可以指定自己的Queue值,写成类似这样:“Queue”=“Transparent+100”,表示一个在Transparent之后100的Queue上进行调用。通过调整Queue值,我们可以确保某些物体一定在另一些物体之前或者之后渲染,这个技巧有时候很有用处。(比如遮挡描边效果,应该就是这么来的)

LOD

细节等级。大家玩吃鸡的时候,从飞机上跳下,这时看到地图上的建筑都是比较粗糙的块,当距离慢慢拉近,建筑模型变得越发精致,这就是LOD技术,根据不同的范围使用不同的模型。shader的LOD也是同样用法,不同细节等级,使用不同的LOD。在Unity的Quality Settings中可以设定最大LOD值,当当前LOD小于shader LOD时,那个sub shader就会失效.
VertexLit及其系列 = 100
Decal, Reflective VertexLit = 150
Diffuse = 200
Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
Bumped, Specular = 300
Bumped Specular = 400
Parallax = 500
Parallax Specular = 600### 三、实例

pass

pass是实现着色器具体代码的地方。一个subshader内可以有多个pass。但尽可能用较少的pass实现是对性能的考虑。

pass内的tags说明
pass内的tags有别与subshader中的tags

取值例子说明
Always“LightMode”=“Always”不管是用哪种渲染路径,该pass总是会被渲染。但不计算任何光照
Forwardbase“LightMode”=“ForwardBase”用于向前渲染,该pass会计算环境光,重要的平行光,逐顶点/SH光源和lightmaps
ForwardAdd“LightMode”=“ForwardAdd”用于向前渲染,该pass会计算额外的逐像素光源,每个pass对应一个光源
Deferred“LightMode”=“Deferred”用于向前渲染,该pass会渲染G缓冲,G-buffer
ShadowCaster“LightMode”=“ShadowCaster”把物体的深度信息渲染到盈盈映射纹理(shadowmap)或一张深度纹理中,用于渲染产生阴影的物体
ShadowCollector“LightMode”=“ShadowCollector”用于收集物体阴影到屏幕坐标Buff里
PrepassBase用于遗留的延迟渲染,该pass会渲染法线和高光反射的指数部分
PrepassFinal用于遗留的延迟渲染,该pass通过合并纹理、光照和自发光来渲染得到最后的颜色
Vertex、VertexLMRGBM和VertexLM用于遗留的顶点照明渲染

pass 内的代码分为:固定渲染管线、可编程顶点/片段渲染管线,可编程表面渲染管线。
下面是对三种渲染管线的详细介绍。

pass中的ragma

ragma用于对渲染器的控制。
ragma 参数表

命令参数实例说明
vertex#pragma vertex name将函数name的代码编译为顶点程序
fragment#pragma fragment name将函数name的代码编译为片元程序
geometry#pragma geometry name将函数name的代码编译为DX10的几何着色器
hull#pragma hull name将函数name 的代码编译为DX11hull着色器
domain#pragma domain name将函数name 的代码编译为DX11 domain着色器
fragmentoption option#pragma fragmentoption option添加选项到编译的OpenGL片段程序。对顶点程序或编译目标不是opengl的程序无效
targettarget 2.0、target 3.0、target 4.0、target 5.0#pragma target name设置着色器的编译目标,对应不同版本的着色器模型
only_renderers space separatedd3d9(direct3d 9)、d3d11、opengl、gles(opengl 2s 2.0)、xbox360、ps3、flash#pragma only_renderers space separated names仅编译到指定的渲染平台
exclude_renderers space separatedd3d9(direct3d 9)、d3d11、opengl、gles(opengl 2s 2.0)、xbox360、ps3、flash#pragma exclude_renderers space separated names不编译到指定的渲染平台
glsl#pragma glsl为桌面系统的opengl进行编译时,将cg/hlsl代码转为glsl代码
glsl_no_auto_normalization#pragma glsl_no_auto_normalization name编译到移动平台glsl时(ios/android), 关闭在定点着色器中对法线向量和切线向量自动进行规范化

着色器中的参数

从应用阶段传递模型数据给顶点着色器时 常用的语义
命令实例说明
POSITION模型空间中的顶点位置,一般是float4类型
NORMAL顶点法线,float3类型
TANGENT顶点切线 float4
TEXCOORD0~N该顶点纹理坐标,0是第一组,一般是flkoat2 或float4类型
COLOR定点颜色,通常是fixed4或float4类型
从顶点着色器传递给片元着色器时 常用的语义
命令实例说明
SV_POSITION裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量
COLOR0用于输出第一组顶点颜色
COLOR1通常用于输出第二组顶点颜色
TEXCOORD0~TEXCOORD7通常用于输出纹理坐标
片元着色器输出给unity时常用的语义
命令实例说明
SV_Target输出值将会存到渲染目标(render target)中
unity 内置的矩阵变换
命令实例说明
UNITY_MATRIX_MVP当前的模型观察投影矩阵,用于将顶点/方向矢量从模型空间变换到裁剪空间
UNITY_MATRIX_MV当前的模型观察矩阵,用于将顶点/方向矢量从模型空间变换到观察空间
UNITY_MATRIX_V当前的观察矩阵,用于将顶点/方向矢量从世界空间变换到观察空间
UNITY_MATRIX_P当前的投影矩阵,用于将顶点/方向矢量从观察空间变换到裁剪空间
UNITY_MATRIX_VP当前的观察投影矩阵,用于将顶点/方向矢量从世界空间变换到裁剪空间
UNITY_MATRIX_T_MVUNITY_MATRIX_MV 的转置矩阵
UNITY_MATRIX_IT_MVUNITY_MATRIX_MV的逆转置矩阵,用于将法线从模型空间变换到观察空间,也可以用于得到UNITY_MATRIX_MV的逆矩阵
_Object2World当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间
_World2Object_Object2World的逆转矩阵,用于将顶点/方向矢量从世界空间变换到模型空间
unity 顶点转换函数
命令说明实例
float4 UnityObjectToClipPos(float3 pos)将一个点从object空间转换成相机在均匀坐标下的剪辑空间。这就相当于 mul(UNITY_MATRIX_MVP, float4(pos, 1.0)), 应该在它的位置上使用。
float3 UnityObjectToViewPos(float3 pos)将一个点从object空间转换为view空间。这就相当于mul(UNITY_MATRIX_MV, float4(pos, 1.0)).xyz, 应该在它的位置上使用。
辅助函数
命令说明实例
unity 内置的摄像机和屏幕参数
命令实例说明
float3 _WorldSpaceCameraPos该摄像机在世界空间中的位置
float4 _ProjectionParamsx=1.0 或-1.0(使用反转的投影矩阵渲染时是负数),y=Near,z=Far,w= 1.0+1.0/Far, 其中near和far分别是近裁剪平面和远裁剪平面与摄像机的距离
float4 _ScreenParamsx=width,y=height,z=1.0+1.0/width,w=1.0+1.0/height, 其中width和height分别是该摄像机的渲染目标 (render target)的像素宽度和高度
float4 _ZBufferParamsx=1-Far/near,yFar/Near, 最x/Far,wy/Far,该变量用于线性化Z缓存中的深度值
floart4 unity_OrhoParamsx=width,y=height,z无意义,w=1.0(该相机是正交相机)或w=0.0(透视相机),其中width和height是正交投影相机的宽和高
float4x4 unity_CameraProjection该摄像机的投影矩阵
floart4x4 unity_CameraInvProjection该摄像机的投影矩阵的逆矩阵
float4 unity_CameraWorldClipPlanes该摄像机的6个裁剪屏幕在世界空间下的等式,按左右上下近远的顺序裁剪平面
时间变量
命令实例说明
float4 _Time_Time.x;_Time.y; _Time.z; _Time.w;t是自该场景加载开始所经过的时间,4个分量分别是t/20,t,2t,3t
float4 _SinTimet是时间的正限制,4个分量分别是t/8,t/4,t/2,t
float4 _Costimet是时间的余弦值,t/8,t/4.t/2,t
float4 unity_DeltaTimedt是时间增量,4个值分别是dt,1/dt,smoothDt,1/smoothDt
UnityCG.cginc 库

UnityCG.cginc 该文件中包含了很多即成的参数方法。使用十分方便

引入文件
CGPROGRAM
#include "UnityCG.cginc"
ENDCG
unitycg.cginc 常用结构
命令参数实例说明
appdata_base顶点位置、顶点法线、第一组纹理坐标float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord: TEXCOORD0;可用于顶点着色器的输入
appdata_tan顶点位置、顶点切线、顶点法线、第一组纹理坐标float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0;可用于顶点着色器的输入
appdata_full顶点位置、顶点切线、顶点法线、四组(或更多)纹理坐标icfloat4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3; #if defined(SHADER_API_XBOX360) half4 texcoord4 : TEXCOORD4; half4 texcoord5 : TEXCOORD5; #endif fixed4 color : COLOR;可用于顶点着色器的输入
appdata_img可用于顶点着色器的输入float4 vertex : POSITION; half2 texcoord : TEXCOORD0;可用于顶点着色器的输入
v2f_img裁剪空间中的位置、纹理坐标可用于顶点着色器的输出
unitycg.cginc 常用函数
命令参数实例说明
float4 WorldSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float4 UnityWorldSpaceViewDir(float4 v)输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float4 ObjSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回模型空间中从该店到摄像机的观察方向
float4 WorldSpace LightDir(flaot4 v)仅用于向前渲染。 输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化
float4 ObjectSpaceLightDir(float4 v)仅用于向前渲染中,输入一个模型空间中的顶点位置, 返回模型空间中从该点到光源的光照方向。没有被归一化
float4 UnityWorldSpaceLightDir(float4 v)仅用于向前渲染中,输入一个世界空间中的顶点位置, 返回世界空间中从该点到光源的光照方向。没有被归一化
float3 UnityObjectToWorldNormal(float3 norm)把法线方向从模型空间中转换到世界空间中
float3 UnityObjectToWorldDir(float3 dir)把方向矢量从模型空间中变换到世界空间中
float3 Unity WorldToObjectDir(float3 dir)把方向矢量从世界空间变换到模型空间中
shader数学函数:
函数说明实例
radians(degree)角度变弧度(一般默认都用弧度)
degrees(radian)弧度变角度
sin(angle), cos(angle), tan(angle)三角函数
asin(x)arc sine, 返回弧度 [-PI/2, PI/2];
acos(x)arc cosine,返回弧度 [0, PI]
atan(y, x)arc tangent, 返回弧度 [-PI, PI];
atan(y/x)arc tangent, 返回弧度 [-PI/2, PI/2];
pow(x, y)x的y次方
exp(x)指数, log(x)
exp2(x)2的x次方, log2(x)
sqrt(x)x的根号;
inversesqrt(x)x根号的倒数
abs(x)绝对值
sign(x)取当前数值的正负符号,返回 1, 0 或 -1(x>0;x=0;x<0)
floor(x)底部取整
ceil(x)顶部取整
fract(x)取小数部分
mod(x, y)取模, x - y*floor(x/y)
min(x, y)取最小值
max(x, y)取最大值
clamp(x, min, max)min(max(x, min), max);
mix(x, y, a)x, y的线性混叠, x(1-a) + y*a;
step(edge, x)如 x smoothstep(edge0, edge1, x): threshod smooth transition时使用。 edge0<=edge0时为0.0, x>=edge1时为1.0
length(x)向量长度
distance(p0, p1)两点距离, length(p0-p1);
dot(x, y)点积,各分量分别相乘 后 相加
cross(x, y)差积x[1]*y[2]-y[1]*x[2], x[2]*y[0] - y[2]*x[0], x[0]*y[1] - y[0]*x[1]
normalize(x)归一化length(x)=1;
faceforward(N, I, Nref)如 dot(Nref, I)< 0则N, 否则 -N
reflect(I, N)I的反射方向I -2*dot(N, I)*N, N必须先归一化
refract(I, N, eta)折射k=1.0-etaeta(1.0 - dot(N, I) * dot(N, I)); 如k<0.0 则0.0,否则 etaI - (etadot(N, I)+sqrt(k))*N
matrixCompMult(matX, matY)矩阵相乘, 每个分量 自行相乘r[j] = x[j]*y[j];
lessThan(vecX, vecY)向量 每个分量比较 x < y
lessThanEqual(vecX, vecY)向量 每个分量比较 x<=y
greaterThan(vecX, vecY)向量 每个分量比较 x>y
greaterThanEqual(vecX, vecY)向量 每个分量比较 x>=y
equal(vecX, vecY)向量 每个分量比较 x==y
notEqual(vecX, vexY)向量 每个分量比较 x!=y
any(bvecX)只要有一个分量是true, 则true
all(bvecX)所有分量是true, 则true
not(bvecX)所有分量取反

三 着色器实例

1. 固定渲染管线

固定功能管线着色器的关键代码都在Pass的材质设置Material{}和纹理设置SetTexture{}部分。
目前固定着色器已经逐渐退出市场,只在为兼容一些老旧硬件设备而存在。

Shader "Custom/VertexList" {
    Properties {
    	//设置与unity通信的变量,用来通过unity编辑器获取素材资源及参数
        _Color("Main Color",Color) = (0,1,1,0.5)
        _SpecColor("Spec Color",Color) = (1,1,1,1)
        _Emission("Emissive Color",Color) = (0,0,0,0)
        _Shininess("Shininess",Range(0.01,1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Pass{
                Material{
                       Diffuse[_Color]  //设置漫反射
                       Ambient[_Color]  //环境光
                       Shininess[_Shininess]  //光泽度
                       Specular[_SpecColor]  //高光色
                       Emission[_Emission]  //自发光
                }      
                Lighting On
                SeparateSpecular On   //启用高光颜色
                //设置纹理
                SetTexture[_MainTex]{
                		constantColor[_Color]   //设置颜色常量
                   	//混合命令
                   	combine texture * primary DOUBLE,
                   	texture *constant
                }
            }
    }
}
2 .顶点/片段渲染管线

功能强大,且用途最多的渲染器

顶点/片段渲染管线 卸载pass块中,用CGPROGRAM 标签包裹。

该shader 会实现根据观察方向而变色的效果

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/SurfaceShader"{   
	 SubShader    {        
 		Tags { "RenderType" = "Opaque" }       
		LOD 100
		Pass {           
        		 	//CG代码块   
        		 	CGPROGRAM                
        		 		//一个 CGPROGRAM 块中必须有且只能有一个vertex 和一个fragment
        		 		#pragma vertex vert                
        		 		#pragma fragment frag
                			//引入unityCG处理库 
                			#include "UnityCG.cginc"
                			//定义一个数据结构,该结构包含一个4维向量,一个三维向量      v2f的意思是顶点to片元          
                			struct v2f{ 
	                			//获取位置
	                			float4 pos : SV_POSITION;
	                			//获取颜色                    
	                			float2 color : COLOR0;                
	                		};
                			//顶点程序代码,用于计算位置和颜色。   v2f是刚才新建的数据结构,vert是在cgprogram注册的用于顶点计算的程序名  
                			//该方法输入值appdata_base v是与模型关联后自动输入的,他的输出值会自动传入到片段代码块。所以相关联的顶点和片段方法的输出和输入类型一定要一致        
                			v2f vert(appdata_base v){
	                			//创建v2f数据 o
	                			v2f o;                    
	                			//计算位置                    
	                			o.pos = UnityObjectToClipPos(v.vertex);                    
	                			//计算颜色                    
	                			o.color = v.normal* 0.5 + 0.5;                    
	                			//返回该v2f数据                    
	                			return o;                
	                		 }                
	                		 //片段程序代码   
	                		 //该代码块会自动接受顶点着色器的输出值,而他的输出值会交给像素着色器最终显示在屏幕上
	                		 half4 frag(v2f i) : COLOR{
	                		 	//返回输入的颜色,并设置透明度为1                    
	                		 	return half4(i.color,.5,1);                
	                		 }            
	                	ENDCG        
	        	}    
	}    
	//如果以上代码块失败,则用默认vertexlit渲染器
	Fallback "VertexLit"
}

总是按方向着色

3. 表面渲染管线

备受unity宠爱的渲染器

在Unity中,表面着色器的关键代码用Cg/HLSL语言编写,然后嵌在ShaderLab的结构代码中使用。使用表面着色器,用户仅需要编写最关键的表面函数,其余周边代码将由Unity自动生成,包括适配各种光源类型、渲染实时阴影以及集成到前向/延迟渲染管线中等。
光照模型可以是内置的Lambert和BlinnPhong,或者是自定义的光照模型。
表面函数的作用是接收输入的UV或者附加数据,然后进行处理,最后将结构填充到输出结构体SurfaceOutPut中。

表面着色器的输入参数表

数据类型参数说明
float3viewDir视角方向
float4COLOR每个顶点的插值颜色
float4screenPos屏幕坐标(使用.xy/.w来获得屏幕2D坐标)
float3worldPos世界坐标
float3worldRefl世界坐标系中的反射向量
float3worldNormal世界坐标系中的法线向量
INTERNAL_DATA当输入结构包含worldRefl或worldNormal且表面函数会写入输出结构的Normal字段是需包含此声明

表面着色器的输出参数表

struct SurfaceOutput{
	    half3 Albedo;//反射光
	    half3 Normal;//法线
	    half3 Emission;//自发光
	    half Specular;//高光
	    half Gloss;//光泽度
	    half Alpha;//透明度
};

实例说明

Shader "Custom/surfShader"
	Properties{}
	SubShader{
		//当系统渲染不透明物体时 调用该shader
		Tags{"RenderType" = "Opaque"}
		//表面着色器代码块  不放在pass中,编译后会分放至各个pass中
		CGPROGRAM
			//定义着色器类型为surface(表面着色器),并使用光照模型Lambert
			#progma surface surf Lambert
			//定义输入数据的结构体
			struct Input {
				float color :COLOR;
			}
			//定义输出数据的结构体
			struct SurfaceOutput{
   				half3 Albedo;//反射光
   				half3 Normal;//法线
   				half3 Emission;//自发光
   				half Specular;//高光
   				half Gloss;//光泽度
   				half Alpha;//透明度
  			};
			//表面函数
			void surf(Input IN, inoutSurfaceOutput o){
				o.Albedo = 1;//输出颜色
			}
		ENDCG
	}
	Fallback "Diffuse"//备选着色器
}

四,着色器效果集

该连接中包含很多着色器代码实例,具体的代码和详细的注释,不定时更新中
https://blog.csdn.net/lengyoumo/article/details/99676462

五,三大测试与剔除、透明混合

三大测试:深度测试、透明测试、模版测试
重点:三大测试与剔除都是决定是否显示像素条件!混合是指有透明物体的情况下像素该如何叠加显示
深度测试依据物体在镜头前的空间位置排序。
透明测试依据颜色透明度,也就是alpha值。
模版测试依据自定义的值,当同样带有模版值的元素叠加时触发

剔除与三种测试渲染顺序按先后排列。

5.0 Cull剔除

Cull 是剔除的意思。

命令说明实例
Off绘制所有的面Cull Off
Front不绘制面向相机部分的面Cull Front
Back不绘制背对相机的面Cull Back
5.1 透明测试 AlphaTest

当透明度到达指定值,就输出像素,否则抛弃
语法:

指令说明实例
Greater大于,只渲染大于该值的像素。alphatest greater [_alphaValue] //类似于抠图
Less小于,只渲染小于该值的像素。类似于反向抠图
GEqual大于等于
LEqual小于等于
Equal等于
NotEqual不等于
Always总是
Never永不
Off关闭alphatest Off
例1 表面着色器
实例,只要声明 alphatest greater [_alphaValue] 即可。
	Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _alphaValue("alphavalue",range(0,1))=0.3
    }
    SubShader {
        Pass{
	        alphatest greater [_alphaValue]
	        CGPROGRAM
				...............
				ENDCG
        	}
      }

例2 片段着色器

fixed4 frag(v2f i):SV_Target{
	fixed4 texColor = tex2D(_MainTex, i.uv);
	clip(texColor.a - _Cutoff);
	//等同于
	//if((texColor.a - _Cutoff)<0.0){
	//	discard;
	//}
}
5.1.2 透明度混合 Blend

混合命令

指令说明实例
Blend Off关闭混合
Blend SrcFactor DstFactor开启混合,并设置混合因子,片元颜色胡i成因SrcFactor,而已经存在颜色缓存中的颜色会诚意DstFactor,然后把两者相加后再存入颜色缓冲中
Blend SrcFactor DstFactor, SrcFactorA DstFactorA上同,使用不同因子来混合透明通道
BlendOp BlendOperation并非是把源颜色和目标颜色简单相加后混合,而是使用BlendOperation对他们进行其他操作

混合因子

指令说明实例
One因子值为1
Zero因子值为0
SrcColor因子为源颜色值(当前片元),当用于混合rgb时,使用SrcColor的RGb分量作为混合因子,当用于混合a的混合等式时,使用SrcColor的A分量作为混合因子
SrcAlpha因子为源颜色的透明度,A通道
DstColor因子为目标颜色(已经存在颜色缓存中的颜色),当用于混合rgb时,使用DstColor的RGb分量作为混合因子,当用于混合a的混合等式时,使用DstColor的A分量作为混合因子
DstAlpha因子为源颜色的透明度,A通道
OneMinusSrcColor因子为 1 - 源颜色,其余与SrcColor相同
OneMinusSrcAlpha因子为 1-源颜色的透明度值
OneMinusDstColor因子为 1- 目标颜色,其余与DstColor相同
OneMinusDstAlpha因子为 1- 目标颜色透明度

混合操作
BlendOp

指令说明实例
Add将混合后的源颜色和目标颜色相加
Sub将混合后的源颜色减去混合后的目标颜色
RevSub用混合后的目标颜色减去混合后的源颜色
Min使用源颜色和目标i颜色中较小的值
Max使用源颜色和目标颜色中较大的值
片段
正常:Blend SrcAlpha OneMunusSrcAlph
柔和相加 Blend OneMinusDstColor One
正片叠底:Blend DstColor Zero
两倍相乘:Blend DstColor SrcColor
变暗:BlendOp Min
Blend One One
变量:BlendOp Max
Blend One One
滤色:Blend OneMinusDstColor One 等偶同于Blend One OneMinusSrcColor
线性减淡:Blend One One
5.2 模板测试 StencilTest

啥是模板测试,每个像素都有一个stencil值,在同一个像素上,所有shader的stencil都共享这一个值,当有其他带有遮罩像素与其重合时就能获取到该值,并根据自身的stencil值处理或。典型的应用就是遮罩显示。你可以选择每次重合都增加1,然后再指定某个物体,当值达到某个数量级再显示。这样的场景,比如,有个隐身的怪物,你只有使用圣水喷雾才能让他现行,但必须喷3次才行,这样,空中就存在了3次叠加的雾,透过这个3层雾就能看到怪物了。但你偏一下角度,透过两层wu就看不到。
Stencil完整语法:

stencil{
	Ref referenceValue //每个像素都有一个stencil值,在同一个像素上,所有shader的stencil都共享这一个值,当有其他带有遮罩像素与其重合时就能获取到该值,并根据自身的stencil值处理触发小狗
	ReadMask  readMask  //读遮罩
	WriteMask writeMask  //写遮罩
	Comp comparisonFunction   //条件判断  大于小于等触发
	Pass stencilOperation    //满足条件后,相应的处理办法   是替换值还是增长值等
	Fail stencilOperation    //没有通过模板测试怎么办
	ZFail stencilOperation    //通过了模板测试怎么办
}

模板语法

参数说明实例
Refref用来设定参考值(范围0-255)。这个值用来与stencilbuffer比较
ReadMaskReadMask 从字面意思的理解就是读遮罩,readMask将和referenceValue以及stencilBufferValue进行按位与(&)操作,readMask取值范围也是0-255的整数,默认值为255,二进制位11111111,即读取的时候不对referenceValue和stencilBufferValue产生效果,读取的还是原始值
WriteMaskWriteMask是当写入模板缓冲时进行掩码操作(按位与【&】),writeMask取值范围是0-255的整数,默认值也是255,即当修改stencilBufferValue值时,写入的仍然是原始值。
CompComp是定义参考值(referenceValue)与缓冲值(stencilBufferValue)比较的操作函数,默认值:always
PassPass是定义当模板测试(和深度测试)通过时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
FailFail是定义当模板测试(和深度测试)失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
ZFailZFail是定义当模板测试通过而深度测试失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
模板对比
指令说明实例
Greater大于,只渲染大于该值的像素。alphatest greater [_alphaValue] //类似于抠图
Less小于,只渲染小于该值的像素。类似于反向抠图
GEqual大于等于
LEqual小于等于
Equal等于
NotEqual不等于
Always总是
Never永不
Off关闭alphatest Off
模板操作
指令说明实例
Keep保留当前缓冲中的内容,即stencilBufferValue不变
Zero将0写入缓冲,即stencilBufferValue值变为0。
Replace将参考值写入缓冲,即将referenceValue赋值给stencilBufferValue。
IncrSatstencilBufferValue加1,如果stencilBufferValue超过255了,那么保留为255,即不大于255。
DecrSatstencilBufferValue减1,如果stencilBufferValue超过为0,那么保留为0,即不小于0。
Invert将当前模板缓冲值(stencilBufferValue)按位取反
IncrWrap当前缓冲的值加1,如果缓冲值超过255了,那么变成0,(然后继续自增)
DecrWrap当前缓冲的值减1,如果缓冲值已经为0,那么变成255,(然后继续自减) 。

实例:遮罩
将此shader付给遮罩物体

Shader "Custom/st1" {
	SubShader{
		Tags { "RenderType" = "Opaque" "Queue" = "Geometry-1"}
			CGINCLUDE
				struct appdata {
					float4 vertex : POSITION;
				};
				struct v2f {
					float4 pos : SV_POSITION;
				};
				v2f vert(appdata v) {
					v2f o;
					o.pos = UnityObjectToClipPos(v.vertex);
					return o;
				}
				half4 frag(v2f i) : SV_Target {
					return half4(1,1,0,1);
				}
			ENDCG
			Pass {
				ColorMask 0
				ZWrite Off
				Stencil
				{
					Ref 1
					Comp Always
					Pass Replace
				}
				CGPROGRAM
					#pragma vertex vert
					#pragma fragment frag
				ENDCG
			}
	}
}

将此shader付给被遮罩物体

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'


Shader "Custom/st2" {
	Properties{
	_MainTex("Base (RGB)", 2D) = "white" {}
	}
		SubShader{
			Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }
			LOD 100
			Pass {
				Stencil
				{
					Ref 2
					Comp Equal
				}
				CGPROGRAM
					#pragma vertex vert
					#pragma fragment frag
					#pragma multi_compile_fog
					#include "UnityCG.cginc"
					struct appdata_t {
						float4 vertex : POSITION;
						float2 texcoord : TEXCOORD0;
					};
					struct v2f {
						float4 vertex : SV_POSITION;
						half2 texcoord : TEXCOORD0;
						UNITY_FOG_COORDS(1)
					};
					sampler2D _MainTex;
					float4 _MainTex_ST;
					v2f vert(appdata_t v)
					{
						v2f o;
						o.vertex = UnityObjectToClipPos(v.vertex);
						o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
						UNITY_TRANSFER_FOG(o,o.vertex);
						return o;
					}
					fixed4 frag(v2f i) : SV_Target
					{
						fixed4 col = tex2D(_MainTex, i.texcoord);
						UNITY_APPLY_FOG(i.fogCoord, col);
						UNITY_OPAQUE_ALPHA(col.a);
						return col;
					}
				ENDCG
			}
	}
}

即可完成

5.3 深度测试 DepthTest

根据深度值选择通过测试。
Cull Back | Front | Off

ZWrite On | Off:用于控制是否将对象的像素写入深度缓冲(默认开启),如果需要绘制纯色物体,便将此项打开。如需绘制半透明效果,则关闭深度缓冲。
开启深度写入:当两个像素重合时,根据深度缓冲中的值对比,剔除掉离相机较远的那个,留下最近的那个显示。
关闭深度写入:不剔除任何像素,按顺序覆盖像素。(半透明物体需要这个)

ZTest Less | Greater | LEqual | GEqual | Equal | NotEqual | Always
用于控制深度测试如何执行, 缺省值是LEqual。如果要绘制的像素的Z值 小余等于深度缓冲区中的值,那么就用新的像素颜色值替换。

Offset Factor,Units
利用Factor和Units来定义深度偏移
Factor参数表示Z缩放的最大斜率的值
Units参数表示可分辨的最小深度缓冲区的值
利用该句法,我们就可以强制使位于同一位置上的两个集合体中的一个几何体绘制在另一个的上层
以上几个值可以行内写。

懒人片段

	//顶点着色器正文
	v2f vert(a2v v) {
		v2f o;
		//获取顶点的裁剪坐标
		o.pos = UnityObjectToClipPos(v.vertex);//将模型顶点坐标转换为裁剪坐标
		//获取纹理坐标
		o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);//贴图与顶点对应的uv,必须配合下面的TRANSFER_SHADOW(o)
		//顶点法线 * 世界转模型法线  获取世界法线?
		o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
		//获取世界顶点
		o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
		TRANSFER_SHADOW(o);
		return o;
	}		
	ColorMask 0   //屏蔽所有颜色
	fiexed4 frag(v2f i):color{
		//纹理寻址
		fixed4 colo = tex2D(_MainTex, i.texcoord)//给贴图上色
		colo = _Color * col;
	}		
语法解释:

SetTexture[_MainTex]{
Combine Primary * Texture
}

SeparateSpecular On

Blend SrcAlpha OneMinusSrcAlpha

该文章参考了下列文章:
https://www.jianshu.com/p/5be2801e226c
https://docs.unity3d.com/Manual/SL-Pass.html
https://blog.csdn.net/weixin_33973609/article/details/85751777
https://blog.csdn.net/pizi0475/article/details/6574700
https://blog.csdn.net/zx1091515459/article/details/79262053
https://blog.csdn.net/zyq20130118/article/details/52874639
https://blog.csdn.net/qq_38572472/article/details/79020122
https://www.cnblogs.com/Jason-c/p/8385946.html
https://blog.csdn.net/qq826364410/article/details/81744032 关于测试
喜欢看书的同学,重点推荐冯乐乐的shader入门教程,虽然说是入门教程,但写的由浅入深阅读曲线十分不错。重点推荐。csdn还有她的博客请自搜。

新地址
https://github.com/QianMo/Awesome-Unity-Shader

https://blog.csdn.net/lyh916/category_6208767.html

https://blog.csdn.net/ynnmnm/article/details/69791337

### 回答1: Unity 2019.4.x文文档是指为Unity 2019.4.x版本提供的文版文档。这份文档是Unity官方提供的开发指南,旨在帮助开发人员使用Unity游戏引擎来创建游戏和应用程序。 Unity 2019.4.x文文档覆盖了各个方面的内容,包括Unity界面的介绍,项目管理,场景编辑,游戏对象的创建与编辑,材质和纹理的应用,光照和阴影的处理,物理引擎的使用,动画的创建与控制,音频的处理,以及用户界面的设计等等。 这份文档提供了详细的说明和示例,以帮助开发人员了解和掌握各个功能和工具的使用方法。通过阅读文档,开发人员可以学习如何使用Unity的各种组件和系统来实现自己的游戏或应用程序的需求。 同时,Unity 2019.4.x文文档也提供了大量的编程接口文档,涵盖了Unity的各种类和函数的用法和说明。这些接口文档使开发人员能够更好地理解Unity引擎的内部结构和工作原理,以便更加高效和灵活地进行开发工作。 总之,Unity 2019.4.x文文档是Unity官方提供的重要参考资料,对于想要使用Unity引擎进行游戏和应用程序开发的开发人员来说,是一份不可或缺的指南和学习资料。 ### 回答2: Unity 2019.4.x提供了全面的文文档支持,为用户提供了更方便、直观的学习和使用体验。 首先,Unity文文档涵盖了各个方面的内容,包括引擎的各个模块、功能的使用、编辑器的操作指南等。无论是初学者还是有一定经验的开发者,都能够在文文档找到自己需要的信息,帮助他们更好地了解和使用Unity。 其次,Unity文文档以详细的说明和示例来解释每个功能和概念。无论是脚本编程、场景编辑、粒子系统还是动画制作,文文档都会提供清晰的步骤和例子,帮助用户理解和掌握各种功能。 另外,Unity文文档还会根据官方版本进行及时的更新,保持与最新版本的Unity保持同步。这意味着用户可以始终获得最新的特性和改进的详细解释,帮助他们更好地利用Unity的最新功能进行开发。 最后,Unity文文档还提供了丰富的教程和案例,使用户可以通过实际操作来学习。这些教程和案例涵盖了不同类型的游戏和应用开发,供用户参考和借鉴。用户可以通过这些实例来加深对Unity的理解,并且可以根据自己的实际需求进行修改和扩展。 总的来说,Unity 2019.4.x文文档为用户提供了全面、详细的学习和使用指南,帮助他们更好地掌握Unity的各种功能和技术。这些文档的存在使得Unity成为了一个广受欢迎的开发工具,为用户创造了更好的开发环境。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千年奇葩

从来没受过打赏,这玩意好吃吗?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值