您的位置:首页 > 游戏 > 游戏 > Unity ShaderLab基础

Unity ShaderLab基础

2024/12/27 8:10:06 来源:https://blog.csdn.net/qq_32470423/article/details/140608735  浏览:    关键词:Unity ShaderLab基础

[原文1] [参考2]

一 基础知识

1. 1 着色器语言分类:

语言说明
HLSL基于 OpenGL 的 OpenGL Shading Language
GLSL基于 DirectX 的 High Level Shading Language
CGNVIDIA 公司的 C for Graphic
Shader LabUnity封装了CG,HLSL,GLSL的Unity专用着色器语言,具有跨平台,图形化编程,便于与Unity通信等优点

1.2 Mesh,材质,着色器与模型的关系

模型比作兔子,Mesh就是兔子的骨骼,顶点就是骨骼的各个节点,片元就是顶点组成的面,材质就是皮肉装饰片元的,Shader就是控制如何显示材质的

1.3 关于顶点,片元着色器

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

顶点着色程序与片元着色程序通常是同时存在,相互配合,前者的输出作为后者的输入。不过,也可以只有顶点着色程序。如果只有顶点着色程序,那么只对输入的顶点进行操作,而顶点内部的点则按照硬件默认的方式自动插值。

例如,输入一个三角面片,顶点着色程序对其进行phong光照(实图形学中提出的第一个有影响的光照明模型)计算,只计算三个顶点的光照颜色,而三角面片内部点的颜色按照硬件默认的算法(Gourand明暗处理或者快速phong明暗处理)进行插值,如果图形硬件比较先进,默认的处理算法较好(快速phong明暗处理),则效果也会较好;如果图形硬件使用Gourand明暗处理算法,则会出现马赫带效应(条带化)。

而片元着色程序是对每个片元进行独立的颜色计算,并且算法由自己编写,不但可控性好,而且可以达到更好的效果。

由于GPU对数据进行并行处理,所以每个数据都会执行一次shader程序程序。即,每个顶点数据都会执行一次顶点程序;每个片元都会执行一次片段程序。

片元就是所有三维顶点在光栅化之后的数据集合,这些数据没有经过深度值比较,而屏幕显示的像素是经过深度比较的

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

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

1.5 区别

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

1.6 着色器的工作流程

在这里插入图片描述

二 基础知识

2.1 基本结构

Shader "path/name" {Properties{//_CG变量名 ("unity可见的变量名", 属性类型) = 值_Color ("My Color", Color) = (1, 1, 1, .5) } Subshader{}}

结构说明

  • path/name: Unity编辑器检索位置和Shader名,Shader名和文件名尽量保持一致
  • Properties: Shader属性,用于cg变量与Unity通信
  • Subshader:shader解决方案。每个Shader程序包含至少一个Subshader,用于解决硬件性能兼容问题。Shader的主代码部分都写在这里

2.2 Properties

  • 结构: _CG变量名(“Unity可见描述”, 属性类型) = 值
  • 例: _LineColor(“Line Color”, Color) = (1,1,1,0.5)
  • 属性列表
    项目 | Value
类型实例说明
Int-整型
Float-浮点型
Vector-四维向量
Range_Range(“Range”, x,y)范围x-y的浮点数,x<y
Color-RGBA颜色
2D”white”{}2d贴图, 2d纹理,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者”white”,”black”,”gray”,”bump”中的一个
3D-3d贴图
Cube-6面立方贴图
Rect-矩形贴图

2.3 SubShader

SubShader { Tags { "RenderType" = "Opaque" "ForceNoShadowCasting" = "True" "IgnoreProjector" = "True"}LOD 100Pass{Fog{Mode OFF}//固定渲染器Material{Diffuse[_Color]  //设置漫反射}Lighting OnSeparateSpecular On   //启用高光颜色//设置纹理SetTexture[_MainTex]//表面渲染器#pragma surface surf Lambert//顶点渲染器#pragma vertexvert//片段渲染器#pragma fragment frag}Pass{}
}
2.3.1 Tags
类型说明
“RenderType”=“Opaque”系统在渲染不透明物体时调用该shader
“RenderType” = “Transparent”系统在渲染透明物体时调用该shader,绝大部分透明的物体、包括粒子特效都使用这个
“RenderType” = “Background”系统渲染背景时调用,天空盒都使用这个
“RenderType” = “Overlay”系统渲染gui镜头时调用,GUI、镜头光晕都使用这个
“IgnoreProjector”=“True”忽略Projectors
“ForceNoShadowCasting”=“True”不生成阴影
“Queue”=“xxx”指定渲染队列顺序,下面有详细说明
2.3.2 Queue的说明
关键字说明
Background最先调用的,用来渲染天空盒或背景1000
Geometry默认值,用来渲染非透明物体(一般情况下,场景中的绝大多数物体应该是非透明的)2000
AlphaTest用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑2450
Transparent以从后往前的顺序渲染透明物体3000
Overlay用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)4000

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

2.3.3 LOD

细节等级。大家玩吃鸡的时候,从飞机上跳下,这时看到地图上的建筑都是比较粗糙的块,当距离慢慢拉近,建筑模型变得越发精致,这就是LOD技术,根据不同的范围使用不同的模型。shader的LOD也是同样用法,不同细节等级,使用不同的LOD。在Unity的Quality Settings中可以设定最大LOD值,当当前LOD小于shader LOD时,那个sub shader就会失效.

关键字
VertexLit及其系列100
Decal, Reflective VertexLit150
Diffuse200
Diffuse Detail250
Reflective Bumped Unlit250
Reflective Bumped VertexLit250
Bumped, Specular300
Bumped Specular400
Parallax500
Parallax Specular600
2.3.4 pass

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

2.3.4.1 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-用于遗留的顶点照明渲染
2.3.4.2 pass内的代码
管线说明
固定渲染管线图形API提供了一个对硬件进行操作的标准接口;从内部实现上来说,API对程序员提出的各种绘制图元或属性的请求都采用固定的方式来处理
可编程顶点/片元渲染管线-
可编程表面渲染管线(Scriptable Render Pipeline, SRP) 是 Unity 内置渲染管线的替代方案。 使用 SRP 可以通过 C# 脚本控制和定制渲染方式。稍微修改或完全构建并自定义渲染管线可满足需求
2.3.4.3 pass内的pragma

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

命令参数实例说明
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), 关闭在定点着色器中对法线向量和切线向量自动进行规范化-
2.3.5 着色器中的参数

从应用阶段传递模型数据给顶点着色器时 常用的语义

命令说明
POSITION模型空间中的顶点位置,一般是float4类型
NORMAL顶点法线,float3类型
TANGENT顶点切线 float4
TEXCOORD0~N该顶点纹理坐标,0是第一组,一般是flkoat2 或float4类型
COLOR定点颜色,通常是fixed4或float4类型
2.3.6 从顶点着色器传递给片元着色器时 常用的语义
命令说明
SV_POSITION裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量
COLOR0用于输出第一组顶点颜色
COLOR1通常用于输出第二组顶点颜色
TEXCOORD0~TEXCOORD7通常用于输出纹理坐标
2.3.7 片元着色器输出给unity时常用的语义
命令说明
SV_Target输出值将会存到渲染目标(render target)中
2.3.8 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的逆转矩阵,用于将顶点/方向矢量从世界空间变换到模型空间

2.4 Shader函数及变量

2.4.1 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 等价
2.4.2 辅助函数
函数实例
float2 ParallaxOffset (half h, half height, half3 viewDir)为视差法线贴图计算UV偏移
fixed Luminance (fixed3 c)将颜色转换为亮度(灰度)
fixed3 DecodeLightmap (fixed4 color)从Unity光照贴图解码颜色(基于平台为RGBM 或dLDR)
float4 EncodeFloatRGBA (float v)为储存低精度的渲染目标,编码[0…1)范围的浮点数到RGBA颜色。
float DecodeFloatRGBA (float4 enc)解码RGBA颜色到float。
float2 EncodeFloatRG (float v)为储存低精度的渲染目标,编码[0…1)范围的浮点数到RGBA颜色,使用的是两个颜色通道。
float DecodeFloatRG (float2 enc)解码RGBA颜色到float。使用的是两个颜色通道。
float2 EncodeViewNormalStereo (float3 n)编码视图空间法线到在0到1范围的两个数。
float3 DecodeViewNormalStereo (float4 enc4)从enc4.xy解码视图空间法线
2.4.3 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个裁剪屏幕在世界空间下的等式,按左右上下近远的顺序裁剪平面
2.4.4 时间变量
命令说明
float4 _Time自该场景加载开始所经过的时间,(x,y,z,w)分别是t/20,t,2t,3t
float4 _SinTime时间的正弦制,(x,y,z,w)分别是t/8,t/4,t/2,t
float4 _Costime时间的余弦值,(x,y,z,w)分别是t/8,t/4.t/2,t
float4 unity_DeltaTime时间增量,(x,y,z,w)分别是t,1/t,smoothDt,1/smoothDt
2.4.5 光照相关参数

前向渲染(ForwardBase 和 ForwardAdd 通道类型):

参数名描述
fixed4 _LightColor0 (在 Lighting.cginc 中声明)光源颜色。
float4 _WorldSpaceLightPos0方向光:(世界空间方向,0)。其他光源:(世界空间位置,1)。
float4x4 _LightMatrix0(在 AutoLight.cginc 中声明) 世界/光源矩阵。用于对剪影和衰减纹理进行采样。
float4 unity_4LightPosX0、unity_4LightPosY0、unity_4LightPosZ0(仅限 ForwardBase 通道)前四个非重要点光源的世界空间位置。
float4 unity_4LightAtten0(仅限 ForwardBase 通道)前四个非重要点光源的衰减因子。
half4[4] unity_LightColor(仅限 ForwardBase 通道)前四个非重要点光源的颜色。
float4x4[4] unity_WorldToShadow世界/阴影矩阵。聚光灯的一个矩阵,方向光级联最多有四个矩阵。

延迟着色和延迟光照,在光照通道着色器中使用(全部在 UnityDeferredLibrary.cginc 中声明):

参数名描述
fixed4 unity_AmbientSky梯度环境光照情况下的天空环境光照颜色。
fixed4 unity_AmbientEquato梯度环境光照情况下的赤道环境光照颜色。
fixed4 unity_AmbientGround梯度环境光照情况下的地面环境光照颜色。
fixed4 UNITY_LIGHTMODEL_AMBIENT环境光照颜色(梯度环境情况下的天空颜色)。旧版变量。
fixed4 unity_FogColor雾效颜色。
float4 unity_FogParams用于雾效计算的参数:(density / sqrt(ln(2))、density / ln(2)、–1/(end-start) 和 end/(end-start))。x 对于 Exp2 雾模式很有用;y 对于 Exp 模式很有用,z 和 w 对于 Linear 模式很有用。
2.4.6 与颜色空间相关
函数名描述
bool IsGammaSpace()根据宏UNITY_COLORSPACE_GAMMA是否被启用了,判断当前是否启用了伽马颜色空间。
float GammaToLinearSpaceExact (float value)把一个颜色值精确地从伽马颜色空间(sRGB颜色空间)变化到线性空间(CIE-XYZ颜色空间)。
half3 GammaToLinearSpace (half3 sRGB)用一个近似模拟的函数把颜色值近似地从伽马空间变换到线性空间。
float LinearToGammaSpaceExact (float value)把一个颜色值精确地从线性空间变换到伽马颜色空间。
half3 LinearToGammaSpace (half3 linRGB)用一个近似模拟的函数把颜色值近似地从线性空间变换到伽马颜色空间。
2.4.7 数学常数
#ifndef UNITY_CG_INCLUDED#define UNITY_CG_INCLUDED#define UNITY_PI 3.14159265359f //圆周率#define UNITY_TWO_PI 6.28318530718f //2倍圆周率#define UNITY_FOUR_PI 12.56637061436f //4倍圆周率#define UNITY_INV_PI 0.31830988618f //圆周率的倒数#define UNITY_INV_TWO_PI 0.15915494309f //2倍圆周率的倒数#define UNITY_INV_FOUR_PI 0.07957747155f //4倍圆周率的倒数#define UNITY_HALF_PI 1.57079632679f //半圆周率#define UNITY_INV_HALF_PI 0.636619772367f //半圆周率的倒数

2.5 UnityCG.cginc 库

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

CGPROGRAM
#include "UnityCG.cginc"
ENDCG
2.5.1 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顶点位置
顶点切线
顶点法线
多组纹理坐标
cfloat4 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裁剪空间中的位置
纹理坐标
-可用于顶点着色器的输出
2.5.2 unitycg.cginc 常用函数
函数说明
float4 UnityWorldToClipPos(float3 pos)世界坐标空间中的点pos变换到齐次裁剪空间
float4 UnityViewToClipPos(float3 pos)观察坐标空间中点pos变换到齐次裁剪空间
float3 UnityObjectToViewPos(float3 pos或float4 pos)模型局部空间坐标系中某一个点pos变换到观察空间坐标系
float3 UnityWorldToViewPos(float3 pos)把世界坐标系下的点pos变换到观察空间坐标系
float3 UnityObjectToWorldDir(float3 dir)把方向矢量从模型空间转换到世界空间(方向已单位化)
float3 UnityWorldToObjectDir(float3 dir)把方向矢量从世界空间转换到模型空间(方向已单位化)
float3 UnityObjectToWorldNormal(float3 norm )将法线从模型空间转换到世界空间(方向已单位化)
float3 UnityWorldSpaceLightDir(float3 worldPos)输入参数worldPos是一个世界坐标系下的坐标,得到世界空间中从该点到光源(_WorldSpaceLightPos0)的光照方向。(方向没单位化)
float3 WorldSpaceLightDir(float4 localPos)输入一个模型顶点坐标,得到世界空间中从该点到光源(_WorldSpaceLightPos0)的光照方向。(方向没单位化)
float3 ObjSpaceLightDir(float4 v)输入一个模型顶点坐标,得到模型空间中从该点到光源(_WorldSpaceLightPos0)的光照方向。(方向没单位化)
float3 UnityWorldSpaceViewDir(float3 worldPos)输入参数worldPos是一个世界坐标系下的坐标,得到世界空间中从该点到摄像机的观察方向。(方向没单位化)
float3 WorldSpaceViewDir(float4 localPos)输入一个模型顶点坐标,得到世界空间中从该点到摄像机的观察方向。(方向没单位化)
float3 ObjSpaceViewDir(float4 v)输入一个模型顶点坐标,得到模型空间中从该点到摄像机的观察方向。(方向没单位化)
2.5.3 屏幕空间相关函数
函数说明
float4 ComputeScreenPos (float4 clipPos)计算用于执行屏幕空间贴图纹理采样的纹理坐标。输入是裁剪空间位置。
float4 ComputeGrabScreenPos (float4 clipPos)计算用于 GrabPass 纹理采样的纹理坐标。输入是裁剪空间位置。
2.5.4 shader数学函数:
函数说明实例
abs(x)绝对值-
acos(x)arc cosine,返回弧度 [0, PI]-
all(bvecX)所有分量是true(非0), 则true-
any(bvecX)只要有一个分量是true(非0), 则true-
asin(x)arc sine, 返回弧度 [-PI/2, PI/2];-
atan(y, x)arc tangent, 返回弧度 [-PI, PI];-
atan(y/x)arc tangent, 返回弧度 [-PI/2, PI/2];-
ceil(x)顶部取整-
clamp(x, min, max)x<=a,返回a;x>=b,返回b;否则,返回x-
cosh(x)双曲余弦(hyperbolic cosine)函数,计算x的双曲余弦值。-
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])
degrees(radian)弧度变角度-
determinant(m)计算矩阵的行列式因子-
distance(p0, p1)两点距离, length(p0-p1);-
dot(x, y)点积,各分量分别相乘 后 相加-
equal(vecX, vecY)向量 每个分量比较 x==y-
exp(x)指数, log(x)-
exp2(x)2的x次方, log2(x)-
frac(x)返回标量或矢量的小数-
faceforward(N, I, Nref)如 dot(Nref, I)< 0则N, 否则 -N
floor(x)底部取整-
fmod(x,y)取模-
fract(x)取小数部分-
frexp(x, out i)将浮点数 x 分解为尾数和指数,即 x = m* 2^exp,返回 m,并将指数存入 exp 中;如果 x 为 0,则尾数和指数都返回 0-
greaterThan(vecX, vecY)向量 每个分量比较 x>y-
greaterThanEqual(vecX, vecY)向量 每个分量比较 x>=y-
ldexp(x, n)计算x∗2n的值-
length(x)向量长度-
lerp(a, b, f)计算或者的值。即在下限a和上限b之间进行插值,f表示权值。注意,如果a和b是向量,则权值f必须是标量或者等长的向量。-
lessThan(vecX, vecY)向量 每个分量比较 x < y-
lessThanEqual(vecX, vecY)向量 每个分量比较 x<=y-
inversesqrt(x)x根号的倒数-
isfinite(x)判断标量或者向量中的每个数据是否是有限数,如果是返回true;否则返回false;-
isinf(x)判断标量或者向量中的每个数据是否是无限,如果是返回true;否则返回false;-
isnan(x)判断标量或者向量中的每个数据是否是非数据(not-a-number NaN),如果是返回true;否则返回false;-
lit(NdotL, NdotH, m)函数计算环境光、散射光、镜面光的贡献,返回的4元向量。
N表示法向量;
L表示入射光向量;
H表示半角向量;
m表示高光系数。
X位表示环境光的贡献,总是1.0;
Y位代表散射光的贡献,如果 N∙L<0,则为0;否则为N∙L
Z位代表镜面光的贡献,如果N∙L<0 或者N∙H<0,则位0;否则为(N∙L)m;
W位始终位1.0
-
log(x)计算2x的值-
log2(x)计算log2​(x)的值,x必须大于0-
log10(x)计算lg2​(x)的值,x必须大于0-
max(x, y)取最大值-
matrixCompMult(matX, matY)矩阵相乘, 每个分量 自行相乘r[j] = x[j]*y[j];
min(x, y)取最小值-
mix(x, y, a)x, y的线性混叠, x(1-a) + y*a;-
mod(x, y)取模, x - y*floor(x/y)-
modf(x, out ip)将浮点数 x 分解为尾数和指数,即 x = m* 2^exp,返回 m,并将指数存入 exp 中;如果 x 为 0,则尾数和指数都返回 0-
mul(M, N)矩阵M和矩阵N的积-
mul(M, v)矩阵M和列向量v的积-
mul(v, M)行向量v和矩阵M的积-
noise(x)根据它的参数类型,这个函数可以是一元、二元或三元噪音函数。返回的值在0和1之间,并且通常与给定的输入值一样-
normalize(x)归一化length(x)=1;
not(bvecX)所有分量取反-
notEqual(vecX, vexY)向量 每个分量比较 x!=y-
pow(x, y)x的y次方-
radians(degree)角度变弧度(一般默认都用弧度)-
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
round(x)四舍五入-
rsqrt(x)x的平方根的倒数,x必须大于0-
saturate(x)把x限制到[0,1]之间-
sign(x)取当前数值的正负符号,返回 1, 0 或 -1(x>0;x=0;x<0)
sin(angle), cos(angle), tan(angle)三角函数(正弦,余弦,正切)-
sincos(float x, out s, out c)该函数是同时计算x的sin值和cos值,其中s=sin(x),c=cos(x)。
该函数用于“同时需要计算sin值和cos值的情况”,比分别运算要快很多!
-
sinh(x)计算x的双曲正弦-
smoothstep(min, max, x)值x位于min、max区间中。
如果x=min,返回0;
如果x=max,返回1;
如果x在两者之间,按照下列公式返回数据:
–2∗((x–min)/(max–min))3+3∗((x–min)/(max–min))/2
float smoothstep(float a, float b, float x)
{
float t = saturate((x - a)/(b - a));
return tt(3.0 - (2.0*t));
}
-
sqrt(x)x的根号;-
step(edge, x)如果x<a, 返回0;否则返回1-
tanh(x)计算x的双曲线切线-
transpose(M)返回M(AxB)的转置矩阵m(BxA)矩阵-

三 着色器实例

3.1 固定渲染管线

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

Shader "Custom/VertexList"{Properties{//设置与Unity通信的变量,用来通过Unity编辑器获取素材资源和参数_Color("Main Color", Color) = (0,1,1,0.5)_SpecColor("Space Color", Color) = (1,1,1,1)_Emission("Emission Color", Color) = (0,0,0,0)_Shininess("Shininess", Range(0.01, 1)) = 0.7_MainTex("Base (RGB)", 2D) = "white"{}}//PropertiesSubShader{Pass{Material{Diffuse[_Color] //设置漫反射Ambient[_Color] //环境光Shininess[_Shininess]  //设置光泽度Specular[_SpecColor]  //设置高光Emission[_Emission]   //自发光}//MaterialLighting OnSeparateSpecular On //启用高光SetTexture[_MainTex]{constantColor[_Color]  //设置颜色常量//混合命令combine texture * primary DOUBLE,texture *constant}//SetTexture[_MainTex]}//Pass}//SubShader
}//

3.2 定点/片元着色器

功能强大,用途广
顶点/片段渲染管线 卸载pass块中,用CGPROGRAM 标签包裹。

Shader "Custom/ChestVertex"
{Properties {_MainTex("Texture", 2D) = "white"{}_ScreenFix("ScreenFix", Range(0.01, 0.5)) = 0.25}SubShader {pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma target 3.0//该结构没有SV_POSITIONstruct v2f{float2 uv : TEXCOORD;};sampler2D _MainTex;float _ScreenFix;v2f vert(float4 vertex : POSITION,//顶点位置输入float2 uv : TEXCOORD,//纹理坐标输入out float4 outpos: SV_POSITION)//裁切空间位置输出{v2f o;o.uv = uv;outpos = UnityObjectToClipPos(vertex);return o;}//vert// UNITY_VPOS_TYPE:在不同平台,屏幕空间位置输入的基础类型会有所不同 // 获取可移植性(多数平台为float4, Direct3D 9上float2)// VPOS是在着色器模型 3.0 开始存在,指定版本: #pragma target 3.0fixed4 frag(v2f i, UNITY_VPOS_TYPE screenPos : VPOS): SV_Target{// screenPos.xy 包含像素实现渲染 8*8 的像素块棋盘图案// 棋盘图案中 8*8 像素块的 checker 值为负screenPos.xy = floor(screenPos.xy * _ScreenFix) * 0.5;float checher = -frac(screenPos.r + screenPos.g);// 如果为负数,则使用Clip HLSL指令停止渲染像素clip(checher);// 对于保留的像素,读取纹理并将其输出fixed4 c = tex2D(_MainTex, i.uv);return c;} //fragENDCG}//pass}//SubShader
}

3.3 表面渲染管线

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

3.3.1 表面着色器的输入参数表
数据类型参数说明
float3viewDir视角方向
float4COLOR每个顶点的插值颜色
float4screenPos屏幕坐标(使用.xy/.w来获得屏幕2D坐标)
float3worldPos世界坐标
float3worldRefl世界坐标系中的反射向量
float3worldNormal世界坐标系中的法线向量
-INTERNAL_DATA当输入结构包含worldRefl或worldNormal且表面函数会写入输出结构的Normal字段是需包含此声明
3.3.2 表面着色器的输出参数表
struct SurfaceOutput{half3 Albedo;//反射光half3 Normal;//法线half3 Emission;//自发光half Specular;//高光half Gloss;//光泽度half Alpha;//透明度
};
3.3.2 实例
Shader "Custom/surfShader"
{Properties{_Color("Main Color",Color) = (1,1,1,1)_MainTex("Base",2D) = "white"{}_BumpMap("Normalmap",2D) = "bump"{}}SubShader{//当系统渲染不透明物体时 调用该shaderTags{"RenderType" = "Opaque"}LOD 300//表面着色器代码块  不放在pass中,编译后会分放至各个pass中CGPROGRAM//定义着色器类型为surface(表面着色器),并使用光照模型Lambert#pragma surface surf Lambert //编译指令#pragma target 3.0 //制定着色器版本sampler2D _MainTex;sampler2D _BumpMap;fixed4 _Color;//定义输入数据的结构体struct Input{float2 uv_MainTex;float2 uv_BumpMap;};//定义输出数据的结构体//struct SurfaceOutput{//		    half3 Albedo;//反射光//		    half3 Normal;//法线//		    half3 Emission;//自发光//		    half Specular;//高光//		    half Gloss;//光泽度//		    half Alpha;//透明度//    };void surf(Input IN,inout SurfaceOutput o){fixed4 tex = tex2D(_MainTex,IN.uv_MainTex);o.Albedo = tex.rgb * _Color.rgb;o.Alpha = tex.a * _Color.a;o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));}ENDCG}Fallback "Diffuse"//备选着色器
}

3.4 着色器效果集

3.4.1 描边着色器

Shader "Custom//UnlitShader"
{Properties{//定义与unity通信的变量名_MainColor("Main Color", Color) = (0.5,0.5,0.5,1)_OutlineColor("Outline Color",Color) = (0,0,0,1)   //轮廓颜色_Outline("Outline width",Range(0.0,10)) = 0.005    //轮廓线宽度}SubShader{Tags { "RenderType"="Transparent" }LOD 100//固定管线渲染器。背面剔除,并在深度缓冲区中留下背面剔除的深度值Pass{Name "BASE"//剔除背面Cull Back//保存缓冲区Blend Zero OneSetTexture[_OutlineColor]{ConstantColor(1,0,0,0)Combine constant}}//顶点/片段着色器。正面剔除,那些经过法线伸展后的点会因为与深度剔除的缓存值比较后无法通过而被剔除//也就是说仅仅剩下正面与背面的边缘处因为伸展的原因保留了下来,  于是轮廓就出现了,这就是大体过程了Pass{Name"OUTLINE"Tags{"LightMode" = "Always"}Cull FrontBlend One OneMinusDstColor//顶点片元着色器代码CGPROGRAM#pragma vertex vert #pragma fragment frag //引入unitycg库#include "UnityCG.cginc"//定义从应用到顶点着色器的数据,此处用于获取顶点位置信息和法线信息struct appdata{float4 vertex : POSITION;float3 normal : NORMAL;};//定义顶点着色器输出给片元着色器的数据,此处输出片元位置,及颜色struct v2f{float4 pos :POSITION;float4 color : COLOR;};uniform float _Outline;uniform float4 _OutlineColor;//顶点着色器程序块v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);float2 offset = TransformViewToProjection(norm.xy);o.pos.xy += offset * o.pos.z* _Outline;o.color = _OutlineColor;return o;}//片段着色器代码块half4 frag(v2f i) :COLOR{return i.color;}ENDCG}}
}

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

  1. 深度测试
  2. 透明度测试
  3. 模板测试
    重点:三大测试与剔除都是决定是否显示像素条件!混合是指有透明物体的情况下像素该如何叠加显示
    深度测试依据物体在镜头前的空间位置排序。
    透明测试依据颜色透明度,也就是alpha值。
    模版测试依据自定义的值,当同样带有模版值的元素叠加时触发
    剔除与三种测试渲染顺序按先后排列。
3.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;//}
}
3.5.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变暗
BlendOp Max变亮
Blend One One线性减淡
Blend OneMinusDstColor One滤色,等偶同于Blend One OneMinusSrcColor
3.5.3 StencilTest 模板测试

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

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

模板语法:

参数说明
Ref用来设定参考值(范围0-255)。这个值用来与stencilbuffer比较
ReadMask读遮罩,与referenceValue以及stencilBufferValue进行按位与运算,取值范围: [0,255],默认值: 255,即读取时不对referenceValue和stencilBufferValue产生效果,读取的还是原始值
WriteMask当写入模板缓冲时进行掩码操作(按位与运算),取值范围: [0-255],默认值: 255,即当修改stencilBufferValue值时,写入的仍然是原始值。
Comp定义参考值(referenceValue)与缓冲值(stencilBufferValue)比较的操作函数,默认值: always
Pass定义当模板测试(和深度测试)通过时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
Fail定义当模板测试(和深度测试)失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
ZFail定义当模板测试通过而深度测试失败时,则根据(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,(然后继续自减)

实例1: 遮罩物体使用

Shader "Custom/MaskShader" {SubShader{Tags { "RenderType" = "Opaque" "Queue" = "Geometry-1"}CGINCLUDEstruct 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);}ENDCGPass {ColorMask 0ZWrite OffStencil{Ref 1Comp AlwaysPass Replace}CGPROGRAM#pragma vertex vert#pragma fragment fragENDCG}}
}

实例2: 被遮罩物体使用

Shader "Custom/MaskedShader" {Properties{_MainTex("Base (RGB)", 2D) = "white" {}}SubShader{Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }LOD 100Pass {Stencil{Ref 2Comp 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}}
}
3.5.4 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);//获取纹理坐标,贴图与顶点对应的uv,必须配合下面的TRANSFER_SHADOW(o)o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);//顶点法线 * 世界转模型法线  获取世界法线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;}		

3.6 Cull剔除

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

四 参考

  1. 原文
  2. 着色器(Shader)
  3. Shader开发之三大着色器
  4. HLSL GLSL CG着色语言比较
  5. Unity ShaderLab学习
  6. ShaderLab基础(Pass定义)
  7. 神临的 Unity shader 学习之多Pass渲染 (八)
  8. Unity Shader (一)ShaderLab 语法
  9. Unity3D–Stencil Test模板测试
  10. https://github.com/QianMo/Awesome-Unity-Shader
  11. UnityShader3 效果
  12. Shader2D: 一些2D效果的Shader实现
  13. Unity Shader入门精讲

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com