流水线:
1.应用阶段:(CPU)输出渲染图元,粗粒度剔除等 比如完全不在相机范围内的需要剔除,文件系统的粒子系统实现就用到粗粒度剔除。
2.几何阶段:(GPU)把顶点坐标转换到屏幕空间,包含了模型空间 到世界空间 到观察空间(相机视角view) 到齐次裁剪空间(投影project2维空间,四维矩阵,通过-w<x<w判断是否在裁剪空间)
到归一化设备坐标NDC(四维矩阵通过齐次除法,齐次坐标的w除以xyz实现归一化) 到屏幕空间(通过屏幕宽高和归一化坐标计算)。
a.顶点着色器:坐标变换和逐顶点光照,将顶点空间转换到齐次裁剪空间。
b.曲面细分着色器:可选
c.几何着色器:可选
d.裁剪:通过齐次裁剪坐标的-w<x<w判断不在视野范围内的部分或者全部裁剪,归一化。
e.屏幕映射:把NDC坐标转换为屏幕坐标
3.光栅化阶段:(GPU)把几何阶段传来的数据来产生屏幕上的像素,计算每个图元覆盖了哪些像素,计算他们的颜色、
a.三角形设置:计算网格的三角形表达式
b.三角形遍历:检查每个像素是否被网格覆盖,被覆盖就生成一个片元。
c.片元着色器:对片元进行渲染操作
d.逐片元操作:模板测试,深度测试 混合等
e.屏幕图像
-------------------------------------------------------
矩阵:
M*A=A*M的转置(M是矩阵,A是向量,该公式不适合矩阵与矩阵)
坐标转换:
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);顶点位置模型空间到齐次空间
o.worldNormal = mul((float3x3)_Object2World,v.normal);//游戏中正常的法向量转换,转换后法向量可能不与原切线垂直,但是不影响游戏显示,而且大部分显示也是差不多的。一般用这个就行了。
o.worldNormal = mul(v.normal, (float3x3)_World2Object);顶点法向量从模型空间转换到世界空间的精确算法,公式是用_Object2World该矩阵的逆转置矩阵去转换法线。然后通过换算得到该行。
-------------------------------------------------------
API:
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的逆转置矩阵,用于将法线从模型空间转换到观察空间
_Object2World将顶点方向矢量从模型空间变换到世界空间,矩阵。
_World2Object将顶点方向矢量从世界空间变换到模型空间,矩阵。
模型空间到世界空间的矩阵简称M矩阵,世界空间到View空间的矩阵简称V矩阵,View到Project空间的矩阵简称P矩阵。
_WorldSpaceCameraPos该摄像机在世界空间中的坐标
_ProjectionParams
_ScreenParams
_ZBufferParams
unity_OrthoParams
unity_Cameraprojection
unity_CameraInvProjection
unity_CameraWorldClipPlanes[6]摄像机在世界坐标下的6个裁剪面,分别是左右上下近远、
-------------------------------------------------------
表面着色器:
void surf (Input IN, inout SurfaceOutput o) {}表面着色器,unity特殊封装的着色器
Input IN:可以引用外部定义输入参数
inout SurfaceOutput o:输出参数
struct SurfaceOutput//普通光照
{
half3 Albedo;//纹理,反射率,是漫反射的颜色值
half3 Normal;//法线坐标
half3 Emission;//自发光颜色
half Specular;//高光,镜面反射系数
half Gloss;//光泽度
half Alpha;//alpha通道
}
-------------------------------------------------------
基于物理的光照模型:
金属工作流SurfaceOutputStandard 高光工作流SurfaceOutputStandardSpecular
half3,half4代表rgba或者xyz,可以分开用 Albedo.xy=1.或Albedo.ga=1
#pragma surface surfname lightModel op - 指出函数surfname 表面着色器。
lightModel的光照模型和可选op操作,还可以添加顶点修改函数vertex和颜色修改函数finalcolor。
#pragma surface surf CustomLambert vertex:myvert finalcolor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
#pragma vertex name - 指出函数name 是顶点着色器。
#pragma fragment name - 指出函数name 是片段着色器。
#pragma fragmentoption option - 添加option 到编辑的OpenGL片段程序。参看ARB fragment program说明书了解被允许的选项列表。这个指示在顶点程序或者编辑到非OpenGL targets的程序没有影响。
#pragma multi_compile_builtin - 为了pixel-lit shaders;;这个将告知Unity去编辑大量的这个着色器程序数列以支持所有的照明种类,和所有的阴影选项。
#pragma multi_compile_builtin_noshadows - 对于pixel-lit 着色器,不接受阴影。这将告知Unity去编辑几个该着色器程序的数列来支持所有的照明种类。这比multi_compile_builtin pragma可以更快的编辑,而且结果着色器也更小。
#pragma target name - 那个着色器target 去编辑。细节参看shader targets。
#pragma only_renderers space separated names - 只为给定的渲染器编辑着色器。默认情况下,着色器为所有的渲染器被编辑。细节参看 renderers。
#pragma exclude_renderers space separated names - 不为给定的渲染器编辑着色器。默认情况下,着色器为所有的渲染器被编辑。细节参看 renderers。
-------------------------------------------------------
顶点着色器:
struct appdata_full {//vertex输入
float4 vertex : POSITION;//must
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;//该顶点的纹理坐标,第一组纹理坐标uv 也就是第一张贴图的坐标、为了实现多重纹理贴图,比如子弹打在墙上的弹痕等
float4 texcoord1 : TEXCOORD1;//n的数量和shadermodel版本有关
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
fixed4 color : COLOR;//顶点颜色
};
-------------------------------------------------------
片段着色器:
struct v2f{//vertec的输出和frag的输入
float4 vertex :SV_POSITION;//must
float3 color0:COLOR0;
float3 color1:COLOR1;
float4:texcoord:TEXCOORD0;//TEXCOORD0-TEXCOORD7自定义纹理坐标
}
SV_Tatget //frag的输出,输出float4的颜色
-------------------------------------------------------
光照:
1.逐顶点光照:在顶点着色器阶段计算光照,效率高但是效果不好,在边缘像素映射的时候插值可能会产生锯齿。
2.逐像素光照:在片元着色器阶段计算光照,计算量大,但是边缘表现效果好。
3.半兰伯特模型:处理无光照的地方,也让其有光,不然可能是全黑。经验模型。
#include "Lighting.cginc"
Tags { "LightMode"="ForwardBase" }
WorldSpaceViewDir(float4 v) 输入模型空间中的顶点坐标,返回世界空间中从该点到摄像机的观察方向
UnityWorldSpaceViewDir(float4 v) 输入世界空间中的顶点坐标,返回世界空间中从该点到摄像机的观察方向
ObjSpaceViewDir(float4 v)输入模型空间中的顶点坐标,返回模型空间中从该点到摄像机的观察方向
WorldSpaceLightDir()仅用于前向渲染,输入模型空间中的顶点坐标,返回世界空间中从该点到光源光照方向,没有归一化。
UnityWorldSpaceLightDir()仅用于前向渲染,输入世界空间中的顶点坐标,返回世界空间中从该点到光源光照方向,没有归一化。
ObjSpaceLightDir()仅用于前向渲染,输入模型空间中的顶点坐标,返回模型空间中从该点到光源光照方向,没有归一化。
UnityObjectToWorldNormal(float3 v)把法线从模型空间转换到世界空间
UnityObjectToWorldDir(float3 v)把方向矢量从模型空间转换到世界空间
UnityWorldToObjectDir(float3 v)把方向矢量从世界空间转换到模型空间
_WorldSpaceLightPos0.xyz获取平行光光源方向,或者点光源的光源位置
_LightColor0.rgb获取当前pass的光源颜色和强度
UNITY_LIGHTMODEL_AMBIENT.xyz; 环境光
normalize(_WorldSpaceCameraPos.xyz - worldPos.xyz); 视觉方向 UnityWorldSpaceViewDir
-------------------------------------------------------
a.漫反射公式:
diff=C*max(0,cos<L,N>);//C是颜色和强度_LightColor0.rgb
代码: diff=max(0,dot(i.normal,i.lightDir))//i的单位向量and单位法向量
c=tex2D(tex,i.uv)*_LightColor0*diff//_LightColor0表示的是场景中平行光的颜色和强度
-------------------------------------------------------
b.高光反射公式:
Spec=pow(max(0,cos(R,V),gloss))//R 单位反射向量reflect(ray,normal)函数获取,V视线单位方向向量 ,gloss光色度
代码: Spec=pow(max(0),dot(reflect(-i.lightDir,i.normal),32))
c=c**_LightColor0*(diff+Spec)
-----------------------------------------------
纹理 uv坐标是顶点存储了图片上的坐标
_MainTex ("Main Tex", 2D) = "white" {}
sampler2D _MainTex;
float4 _MainTex_ST;//Unity中 纹理_ST来默认声明该纹理的属性_MainTex_ST.xy表示Scale, Till缩放,_MainTex_ST.zw表示Transform 偏移
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);//vs输入纹理坐标和纹理值输出UV,ps对uv进行纹理采样和计算。UV通常在0-1范围,等于o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;//反射率
-------------------------------------------------------
法线贴图:
xyz映射存成rgb值。一般存在切线空间,z轴法线方向,x轴切线方向,y轴副(法)切线方向
TANGENT_SPACE_ROTATION;//Unity来获取rotation矩阵,从模型空间到切线空间变换的矩阵。仅存在旋转和平移时,一个矩阵的转置矩阵等于他的逆矩阵。
自己实现:
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; //切线空间的w分量用来存储负法线向内还是向外
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);//float3x3是按行存储
float3 tangentNormal = UnpackNormal(packedNormal);Unity将法线贴图纹理坐标0,1映射到正常法线坐标-1,1,返回切线空间下的法线方向。法线贴图要设置成Normal格式。该设置unity有优化 rgb值不再是法线xyz的映射了,如果不设置的话要自己算 该公式不能用。
自己实现:
tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;//坐标反映射,自己计算的方法
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));//通过xy计算z
-------------------------------------------------------
透明度测试AlphaTest:
只要有一个片元的透明度不满足条件就被裁剪,用来优化显示。
AlphaTest Greater AlphaValue//仅渲染 alpha 值大于 AlphaValue 的像素。AlphaValue :0-1
AlphaTest GEqual AlphaValue//仅渲染 alpha 值大于或等于 AlphaValue 的像素。
AlphaTest LessAlphaValue//仅渲染 alpha 值小于 AlphaValue 的像素。
AlphaTest LEqual AlphaValue//仅渲染 alpha 值小于或等于 AlphaValue 的像素。
AlphaTest Equal AlphaValue//仅渲染 alpha 值等于 AlphaValue 的像素。
AlphaTest NotEqual AlphaValue//仅渲染 alpha 值不等于 AlphaValue 的像素。
AlphaTest Always //渲染所有像素。这在功能上相当于 Alpha 测试关 (AlphaTest Off)。
AlphaTest Never//不渲染任何像素。
-------------------------------------------------------
模板测试:
Stencil如果开启了模板测试,GPU会首先会读取模板缓冲区的值,然后把该值和读取的参考值ref进行比较,比较方式由Comp指定,比如大于Greater就表示通过模板测试,
然后由Pass Fail ZFail去指定通过和不通过模板和深度测试后对缓冲区的值进行的Operation处理。
Stencil
{Ref 2 //设置模板参考值为2
Comp equal//比较方式,有8种比较方式。
Pass Operation //这个是当stencil测试和深度测试都通过的时候,进行的stencilOperation操作方法
Fail Operation //这个是在stencil测试通过的时候执行的stencilOperation方法
ZFail Operation//这个是在stencil测试通过,但是深度测试没有通过的时候执行的stencilOperation方法。
ReadMask readMask//readMask默认是255,一般不用该功能,设置隐码后 读取ref和buff值都需要与该码进行与操作。(0-255)
WriteMask writeMask//写操作进行与操作
}
Comp 的参数包括Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never
Operation的参数包括:
Keep保持
Zero归零
Replace拿比较的参考值替代原来buffer的值
IncrSat值增加1,但是不溢出,如果是255,就不再加
DecrSat值减少1,不溢出,到0就不再减
Invert翻转所有的位,所以1会变成254
IncrWrap值增加1,会溢出,所以255会变成0
DecrWrap值减少1,会溢出,所以0会变成255
clip(x) //x的任何分量小于0 被裁剪掉
discard//舍弃当前片元
ZWrite Off//关闭深入写入
ColorMask RGB|A|0 //设置颜色通道的写掩码,为0表示该pass不进行颜色输出。
-------------------------------------------------------
深度测试ZTEST:
一个片元离摄像机的远近,渲染后会进行深度写入,通常会判断缓存深度和当前片元深度 可知前后关系。
ZTest Always //指的是直接将当前像素颜色(不是深度)写进颜色缓冲区中 相当于ZTest Off
ZTest Never//而Never指的是不要将当前像素颜色写进颜色缓冲区中,相当于消失。
ZTest Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never/Off,默认值为LEqual 即当物体深度小于或等于缓存深度值时(越远深度越大),该物体渲染,就是默认的先后顺序渲染。
透明度混合AlphaBlending:
该片元需要关闭深度写入,不关闭深度测试。会导致片元之间深度穿插。可以采用2个pass,第一个pass只用来做深度写入ZWrite On,第二个pass只用来输出颜色ZWrite Off,这样深度和颜色效果才会正确
Blend Off//关闭混合,只有blend打开后ps输出a通道才有意义
Blend SrcFactor DstFactor//用同样的因子对rgba进行混合(默认都开启混合)第一个参数对源颜色(当前片元颜色,ps输出的颜色)*SrcFactor混合,
第二个参数对目标颜色(当前读到的缓冲区颜色)*DstFactor混合,混合后默认相加后会重新写入缓冲区(相加后超过1的自动截断到1)。混合包括RABG值。结果都是源颜色和目标颜色与各自因子相乘后再加起来作为输出颜色。
shader里边的向量相乘不同于点乘叉乘,相当于各项分别相乘。
Blend SrcFactor DstFactor,SrcFactorA DstFactorA//把rgb和a的混合因子分开。
混合因子
One //因子是1
Zero //因子是0
SrcColor//因子为源颜色值,当前片元颜色,对应rgba分量分别与SrcColor分量相乘
SrcCAlpha//因子为源颜色透明值值,对应rgba分别与SrcCAlpha相乘。
DstColor//因子为目标颜色值,当前读到的缓冲区颜色
DstAlpha//因子为目标颜色透明值值
OneMinusSrcColor//因子为1-源颜色
OneMinusSrcAlpha//因子为1-源alpha
OneMinusDstColor//因子为1-目标颜色
OneMinusDstAlpha//因子为1-目标alpha
例子:
Blend SrcAlpha OneMinusSrcAlpha// Alpha混合,正常的透明度混合
Blend OneMinusDstColor One //柔和相加Soft Additive
Blend One One // Additive相加 线性减淡
Blend One OneMinusDstColor // Soft Additive比较柔和的相加
Blend DstColor Zero // Multiplicative乘法
Blend DstColor SrcColor // 2x Multiplicative2倍乘法
BlendOp OP//对源和目标颜色进行其他操作,而不是默认的相加,op操作包括:
Add //相加
Sub//源颜色减目标颜色
RevSub//目标颜色减源颜色
Min //使用2者较小的值
Min //使用2者较大的值
chen
BlendOp Min
Blend One One //组合变暗
-------------------------------------------------------
双面渲染:
一般采用多个pass分别渲染正面和背面
Cull Back|Front|Off
Cull Back默认的背对相机图元不渲染
Cull Front朝向相机图元不渲染,只显示背面
Cull Off关闭剔除功能 全部渲染 性能低,但是可以实现比如看见物体内部结构。
不透明物体有深度测试,先渲前后没有关系,但是先渲染近的效率会更高,因为远的会被深度测试自动剔除不用渲染。
透明物体一般要先渲远的,再渲近的才能保证视觉顺序正确。
------------------------------------------
SubShader的Tag{}标签类型:
Queue:渲染顺序,保证渲染顺序小的先渲 大的后渲
RenderType:Unity可以运行时替换符合特定RenderType的所有Shader,着色器分类
ForceNoShadowCasting:值为”true”时,表示不接受阴影。
IgnoreProjector:值为”true”时,表示不接受Projector组件的投影。常用语半透明物体
DisableBatching:是否对subshader进行批处理,当shader中需要对顶点进行偏移的时候,该项设置为true
CanUseSpriteAtlas:当该subshader用于sprite时 该标签设为false
PreviewType:指明材质面包怎么预览材质 比如 "PreviewType"="Plane"
LightMode : 渲染路径 ,pass的标签类型
-------------------------------------------------------
渲染队列:
"Queue"="Transparent"
Background:1000 //该声明的物体最先被渲染
Geometry:2000//默认的不透明物体使用的渲染队列
AlphaTest:2450//透明度测试,默认不透明物体渲染完后就渲染该物体
Transparent:3000//透明物体,在Geometry和AlphaTest后渲染,保证不透明物体渲染完了再渲染透明的。
Overlay:4000//该队列用来实现叠加效果,该物体会在最后被渲染。
------
RenderType:
Opaque:绝大部分不透明的物体都使用这个;
Transparent:绝大部分透明的物体、包括粒子特效都使用这个;
Background:天空盒都使用这个;
Overlay:GUI、镜头光晕都使用这个
-------------------------------------------------------
渲染路径:
Tag{ "LightMode" = "ForwardBase"}//为每个pass指定渲染路径
LightMode包括:
Always:所有渲染路径该pass都会渲染,但不计算光照
ForwardBase:前向渲染,该pass会计算环境光,最重要的平行光,逐顶点光和 Lightmaps
ForwardAdd:前向渲染,该pass会计算额外的逐像素光源,每个pass对应一个光源。光源多该pass会被多次调用 效率变低。
Deferred:延时渲染,该Pass会渲染G-buffer
ShadowCaster:把物体的深度信息渲染到阴影映射纹理或深度纹理中
PrepassBase:遗留的延迟渲染,该pass会渲染法线和高光反射的指数部分、
PrepassFinal:遗留的延迟渲染,该pass通过合并纹理 光照 自发光来渲染得到最后的颜色
Vertex:遗留的顶点照明渲染
-------------------------------------------------------
前向渲染:
包括ForwardBase类型渲染常用光照和ForwardAdd额外光照
#pragma multicompile_fwdbase //ForwardBase中用来保证光照衰减等参数正确赋值。
#pragma multicompile_fwdadd //ForwardAdd中用来保证可以访问到正确的光照变量.
#pragma multicompile_fwdadd_fullshadows //ForwardAdd中用来计算阴影效果
USING_DIRECTIONAL_LIGHT//平行光的宏定义
_LightColor0//该pass的桌像素光照颜色
_WorldSpaceLightPos0//获取平行光光源方向,或者点光源的光源位置
_LightMatrix0//世界空间到光源空间(光源位置为坐标原点的坐标系)的变换矩阵
_LightTexture0//光照衰减纹理
...........
tips:光源的RendeMode参数设置为Important unity会自动采用像素光源,如果不重要就是顶点光源。还有qualitysetting里边的PixelLIghtCount,超过这个数也会用顶点光照
-------------------------------------------------------
光照衰减:
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;//UNITY_ATTEN_CHANNEL获得衰减值所在的分量
float shadow=SHADOW_ATTENUATION(i);//负值使用SHADOW_COORDS对相关纹理进行采样,返回值为阴影。关闭阴影的状态是等于1
return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);//atten为衰减,shadow为阴影
------计算别人投到自己身上的阴影和衰减
SHADOW_COORDS(n)//声明一个_ShadowCoord的阴影纹理坐标 ps输入坐标,n的值是声明TEXCOORD0-7坐标使用的个数
TRANSFER_SHADOW(o);//用于在顶点着色器中计算上一步声明中的阴影纹理坐标 并传向ps阶段。
float shadow=SHADOW_ATTENUATION(i);//负值使用SHADOW_COORDS对相关纹理进行采样,返回值为阴影。关闭阴影的状态是等于1
UNITY_LIGHT_ATTENUATION(atten,v2f i, i.worldPos);//计算别人投影到身上的阴影#include "AutoLight.cginc" Unity会将光照衰减和阴影相乘后存在第一个参数中,并自动声明atten变量。第二个参数结构体包含SHADOW_COORDS,第三个参数世界空间坐标
return fixed4((diffuse + specular) * atten, 1.0);//UNITY_LIGHT_ATTENUATION出的atten为衰减和阴影
-----计算阴影投影到别人身上,自己的阴影
V2F_SHADOW_CASTER//unity里边定义阴影投射需要定义的变量
TRANSFER_SHADOW_CASTER_NORMALOFFSET(0)//unity对顶点进行自动处理
SHADOW_CASTER_FRAGMENT(i)//unity自动完成阴影投射部分,把结果输出到深度图和阴影映射纹理中
--ds2的阴影采用的是屏幕后处理的方式去计算阴影,延迟渲染
2.顶点照明渲染:过时的渲染方式。效果差。
3.延迟渲染:通常2个pass,第一个pass计算哪些片元可见,第二个pass计算真实光照。
-------------------------------------------------------
1.反射o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);//入射光线,表面法线
2.折射o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);//入射光线,表面法线,介质比
3.镜子效果:使用相机的RenderTexture来设置渲染纹理。o.uv.x = 1 - o.uv.x;坐标需要翻转一下。
4.玻璃效果:反射和折射使用cubemap进行采样 是天空盒的cubemap,然后反射需要采样的是周围环境的光照和纹理。
GrabPass { "_RefractionTex" }//会把屏幕输出到_RefractionTex的texture中, _RefractionTex_TexelSize 可以得到该纹理纹素大小,例如255*255的纹素是(1/255,1/255)
GrabPass{} //然后用_GrabTexture直接访问屏幕图像,但是这样效率比较低,推荐要上面需要声明的方法。
o.scrPos = ComputeGrabScreenPos(o.pos);//得到对应被抓取的屏幕图像的采样坐标
反射和折射需要显示环境的效果,所以需要对环境的cubemap进行采样。先用反射和折射的公式计算出光线,然后对环境贴图进行采样texCUBE(_Cubemap, i.worldRefl).rgb就可以得到具体效果了。
反射skybox 3d采样,折射屏幕抓取图像2d采样。
-------------------------------------------------------
时间变量:
_Time:float4 //t是自该场景加载开始所经过的时间,4个分量是(t/20,t,2t,3t)
_SinTime:float4//t是时间的正玄弦值,四个分量的值分别是(t/8,t/4,t/2,t)
_CosTime:float4//t是时间的余玄弦值,四个分量的值分别是(t/8,t/4,t/2,t)
unity_DeltaTime:float4// dt是时间增量,4个分量分别是(dt,1/dt,smoothDt,1/smoothDt)
序列帧动画:时间去控制uv坐标映射转换。uv坐标的xy是顶点坐标,映射到小格子里边,和UItexture的xy和宽高不一样。
背景偏移动画:时间控制uv坐标偏移。
水流动画:通过时间和正弦函数去控制顶点偏移,通过时间控制uv移动。设置DisableBatching=true
广告牌BillBoarding:根据视觉方向来旋转被纹理着色的多边形。顶点动画
-------------------------------------------------------------------------
屏幕后处理:
void OnRenderImage(RenderTexture src, RenderTexture dest){}//全部渲染完后将调用,屏幕纹理存在src上,用来计算后通过dest返回,添加[ImageEffectOpaque]属性,可使不透明物体(AlphaTest)被渲染完后立即调用.
Graphics.Blit(src, dest);//直接copy纹理。src是屏幕当前或上一步渲染的纹理,dest是目标纹理
Graphics.Blit(src, dest, material,pass=-1);//将把src传到shader的material的_MainTex纹理。经过material(shader)的处理后输出到dest渲染到屏幕.pass默认是-1会调用所有pass,否则只调用给定顺序的pass。指定pass渲染很重要。
基于颜色变化的边缘检测:Sobel卷积算法,对边缘点进行采样计算 和特定矩阵卷积相乘。
高斯模糊:多次采样纹理混合 消耗较大
Bloom效果:把较亮区域提取出来进行高斯模糊 模拟扩散效果,然后再与原纹理混合。
运动模糊:将上一帧的屏幕图像存到renderTexture中,然后执行Graphics.Blit(src, renderTexture, material),shader将开启混合Blend SrcAlpha OneMinusSrcAlpha把src纹理和目标缓冲纹理renderTexture进行混合,然后再Blit输出到dst进行渲染。就得到了运动模糊效果。
----------------------------------------------------------------------------
深度和法线纹理:
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
_CameraDepthNormalsTexture //unity中调用camera.depthTextureMode=DepthTextureMode.Depth/DepthNormal;这句话后可以通过该变量访问深度纹理或者深度和法线纹理,project空间
float depth=SAMPLE_DEPTH_TEXTURE(tex,uv)//对深度纹理进行采样,返回project空间下非线性深度值。和tex2D类似 只是封装了平台。自动将NDC坐标下的深度映射(0,1)需要转换到(-1,1)veiw空间去计算
LinearEyeDepth(depth)负责把深度纹理的采样结果转换到视角view空间下的线性深度值
Linear01Depth(depth)则会返回一个范围在0,1的线性深度值
half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv);
half2 centerNormal = DecodeViewNormalStereo(sample1);//center.xy存的法线的映射值
float centerDepth = DecodeFloatRG(center.zw);//zw深度
DecodeDepthNormal(sample1,out centerNormal,out centerDepth)//获取采样的法线和深度
Camera.worldToCameraMatrix //世界转相机矩阵 world2view
Camera.cameraToWorldMatrix //相机转世界矩阵
Camera.projectionMatrix //get投影矩阵viewToproject 视角空间到2维投影空间矩阵,set自定义的投影矩阵。如果你改变这个矩阵,相机的渲染不再基于它的fieldOfView更新,直到调用ResetProjectionMatrix
默认把view2project矩阵叫成project矩阵,默认把World2view矩阵叫做view矩阵。比如ViewProject就是world 2 project矩阵
全局雾效
深度雾效:通过每个顶点的深度值计算出该点到摄像机的距离d,然后把距离d进行参与公式计算得到雾效图(远的雾浓 rgb值大,近的雾淡 rgb值小),再把原图和雾效图进行混合。一般用线性,指数,指数平方公式,ds采用指数平方。
地面雾效:通过深度值和摄像机的方向向量计算该点到摄像机的偏移量,再加上摄像机的位置得到该顶点在世界空间中的坐标,然后把该坐标的y值参与雾效计算。如果用坐标z参与计算和深度雾类似。
#pragma multi_compile_fog
基于法线的边缘检测:防止阴影等信息干扰检测,判断临近的4个点的法线和深度值是否是近似,如果差距过大则是边缘roberts算法。(屏幕后处理)
---
渲染轮廓线:第一个pass对顶点进行法线方向扩散渲染,第二个pass用真实渲染实际光照,覆盖第一次,对扩散的顶点未被覆盖的像素就产生了轮廓效果。(模型轮廓)
---------------------------------------------------------------------------------
噪声:
消融效果:怪物消失渐散的效果,把某个像素值小于阈值的裁剪掉,阈值附近的值用burncolor进行混合。阴影的pass里边算阴影时也把该项给clip掉,这样阴影就动态变化了//clip(burn.r - _BurnAmount);
水面扰动效果:用时间去控制偏移距离,然后对该顶点的uv偏移两点的法线平均值来代替该点的法线值。水面=反射+折射+绕动
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);
全局(动态)雾效:通过时间控制噪声纹理的偏移距离,然后根据噪声颜色值来参与计算雾效浓度,然后计算雾效,就有了流动和淡浓的效果。
伽马空间 线性空间(伽马矫正) HDR
一、Cg顶点程序必须在结构中传递顶点数据。几种常用的顶点结构定义在文件UnityCG.cginc中。在大部分情况下仅仅使用它们就够了。结构如下:
1、appdata_base: 包含顶点位置,法线和一个纹理坐标。
2、appdata_tan:包含顶点位置,切线,法线和一个纹理坐标。
3、appdata_full:包含位置、法线、切线、顶点色和两个纹理坐标。
4、appdata_img:包含位置和一个纹理坐标。
二、如果你想访问个别的顶点数据,你必须自己声明顶点结构。结构中的成员必须是属于以下列表中的:
1、float4 vertex:POSITION 顶点位置
2、float3 normal:NORMAL 顶点法线
3、float4 texcoord:TEXCOORD0 第一UV坐标
4、float4 texcoord1:TEXCOORD1 第二UV坐标
5、float4 tangent:TANGENT 切线向量(用在法线贴图中)
6、float4 color:COLOR 每个顶点(per-vertex)颜色
三、内置矩阵
1、UNITY_MATRIX_MVP:当前模型*视*投影矩阵。(注:模型矩阵为 本地->世界)
2、UNITY_MATRIX_MV:当前模型*视图矩阵
3、UNITY_MATRIX_V:当前视图矩阵
4、UNITY_MATRIX_P:当前投影矩阵
5、UNITY_MATRIX_VP:当前视图*投影矩阵
6、UNITY_MATRIX_T_MV:转置模型*视图矩阵
7、UNITY_MATRIX_IT_MV:逆转置模型*视矩阵
8、UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3:纹理变换矩阵
四、内置向量
1、UNITY_LIGHTMODEL_AMBIENT:当前环境色
不同光源的结果都是相加的,而光源作用在材质上就是相乘。
黑白rgb 分量
float luminosity = 0.299 * r + 0.587 * g + 0.114 * b;
light=光
ambient=环境反射(光/色/强度/系数)=阴影色
diffuse=漫反射(光/色/强度/系数)=固有色
specular=镜面反射(光/色/强度/系数)=高光色
emissive=自发光(光/色/强度/系数)=辐射色
shininess=rough=光泽度=镜面反射加权系数n
ka=环境反射系数
kd=漫反射系数
ks=镜面反射系数
N=法线方向单位向量
L=入射光方向单位向量
R=反射光方向单位向量
V=视点方向单位向量
H=半方向单位向量
Input中的可选变量
float3 viewDir - 视图方向 (view direction)。为了计算视差效果(Parallax
effects),边缘光照等
float4 with COLOR semantic -每个顶点插值后的颜色
float4 screenPos - 屏幕空间中的位置。 为了反射效果,需要包含屏幕空间中的位置信息。
float3 worldPos - 世界空间中的位置。
float3 worldRefl - 世界空间中的反射向量。 如果surface
shader没有赋值o.Normal,将会包含世界反射向量。参见例子:Reflect-Diffuse shader。
float3 worldRefl; INTERNAL_DATA - 世界空间中的反射向量。如果surface
shader没有赋值o.Normal,将会包含这个参数。为了获得逐像素法线贴图的反射向量,请使用WorldReflectionVector
(IN, o.Normal)。参见例子: Reflect-Bumped shader。
float3 worldNormal; INTERNAL_DATA -世界空间中的法线向量。如果surface
shader没有赋值o.Normal,将会包含世界法向量。为了获得逐像素法线贴图的法向量,请使用WorldNormalVector
(IN, o.Normal)。
The SurfaceOutput struct has the following properties:
fixed3 Albedo;: This is the diffuse color of the material
fixed3 Normal;: This is the tangent space, normal, if written
fixed3 Emission;: This is the color of the light emitted by the material (this property is declared as half3 in the Standard Shaders)
fixed Alpha;: This is the transparency of the material
half Specular;: This is the specular power from 0 to 1
fixed Gloss;: This is the specular intensity
The SurfaceOutputStandard struct has the following properties:
fixed3 Albedo;: This is the base color of the material (whether it's diffuse or specular)
fixed3 Normal;
half3 Emission;: This property is declared as half3, while it was defined as fixed3 in SurfaceOutput
fixed Alpha;
half Occlusion;: This is the occlusion (default 1)
half Smoothness;: This is the smoothness (0 = rough, 1 = smooth)
half Metallic;: 0 = non-metal, 1= metal
The SurfaceOutputStandardSpecular struct has the following properties:
fixed3 Albedo;
fixed3 Normal;
half3 Emission;
fixed Alpha;
half Occlusion;
half Smoothness;
fixed3 Specular; This is the specular color. This is very different from the Specular property in SurfaceOutput as it allows you to specify a color rather
than a single value.
Render Queue: Background, Geometry, AlphaTest,Transparent,Overlay
一、基本数据类型:Cg支持7种基本的数据类型
1、float,32位浮点数据,一个符号位。浮点数据类型被所有的图形接口支持;
2、half,16位浮点数据;
3、int,32位整形数据
4,fixed,12位定点数,
5、bool,布尔数据,被所有的图形接口支持;
6、sampler*,纹理对象的句柄,分为sampler、sampler1D、sampler2D、sampler3D、samplerCUBE和samplerRECT。
二、内置的数据类型
基于基础数据类型,如float3,表示float类型的三维向量;同理,bool2表示布尔类型的二维向量。
注:向量最长不能超过四元,如float5 vector;//编译错误
向量的赋值:
float2 a=float(1.0,1.0); //编译通过
float2 a=float(1.0f,1.0f); //编译错误
float3 b=float(a,0.0); //编译通过
矩阵数据类型:
float1X1 m1; //即float m1,一维矩阵
float3X4 m34 //3*4阶矩阵
注:X是字符,不是乘号,最大的维数为4*4阶,矩阵的初始化 float 2*2 m22={1.0,2.0,3.0,2.3};
float3 x和floatx[3]是不同的,前者为向量是内置的数据类型,而数组则是一种数据结构,不是内置的数据类型。
三、类型转换
Cg中的类型转换有强制转换和隐式转换;如果是隐式转换则数据类型从低精度向高精度转换。如:
float a=1.0; half b=2.0; loat c=a+b; //等价于float c=a+(float)b;
Cg语言中可以对常量数据加上类型后缀表示该数据类型的数据,如:
float a=1.0h; //1.0h为half类型常量数据
这样的后缀类型有三种:
f:表示float;
h:表示half;
x:表示fixed;
四、Swizzle操作符
Cg语言中的其他操作符和高级CPU语言C++类似,包括关系操作符、逻辑操作符和位移操作符以及条件操作符。而Swizzle操作符是Cg语言中特有的,它可以将一个向量的成员取出组成一个新的向量。对于坐标或者角度等其他多维向量,Swizzle操作符(.)后接x、y、z、w分别表示原始向量的第一个、第二个、第三个和第四个元素;同样,对于颜色可以后接r、g、b和a来表示同样的索引。
例如:
float4(a,b,c,d).xwz 等价于 float(a,d,c)
float4(a,b,c,d).xxy 等价于 float(a,a,b)
注:Swizzle操作符只对结构体和向量使用,不能对数组使用。
五、输入数据关键字:Cg中输入数据流一般分为两类
1、varying 参数:在Cg程序中通过语义进行绑定变量, Cg语言提供了一组语义词,用以表示参数是由顶点的那些数据初始化的,一旦这个变量使用了语义词进行绑定,那么这个变量值被初始化的同时也意味着它有了特殊的含义,如表示位置、法线等含义。语义提供了一种使用随顶点变化或随片段变化而变化的值来初始化Cg程序参数的方法,这些数据都是从应用程序输入到GPU的数据,如顶点位置、法向量、纹理坐标数据等。
2、Uniform 参数:Uniform是用来限制一个变量的初始值的来源,当声明一个变量为Uniform类型的时候,表示这个变量的初始值来自于外部的其他环境。除了获取初始值的这点之外,Uniform关键字声明的变量和其他变量是完全一样的。通常用Uniform来定义一些与三维渲染有关的离散信息数据,并通常不会随着图元信息的变化而变化,如材质对光的反射信息。Uniform表示一个参数,通常使用 uniform表示函数的形参,不能定义一个uniform表示的局部变量。
Unity的内置Uniform输入参数如下:
uniform float4 _Time, _SinTime, _CosTime; // 时间量
uniform float4 _ProjectionParams; // x = 1 or -1 (如果投影翻转就是-1)
// y = 近平面; z = 远平面; w = 1/远平面
uniform float4 _ScreenParams; // x = width; y = height; z = 1 +1/width; w = 1 + 1/height
uniform float3_WorldSpaceCameraPos;
uniform float4x4 _Object2World; //模型矩阵
uniform float4x4 _World2Object; // 模型矩阵的逆
uniform float4 _LightPositionRange; // xyz = pos, w = 1/range
uniform float4 _WorldSpaceLightPos0; // 光源的位置和方向
uniform float4x4 UNITY_MATRIX_MVP; // 模型视图投影矩阵
uniform float4x4 UNITY_MATRIX_MV; // 模型视图矩阵
uniform float4x4 UNITY_MATRIX_V; // 视图矩阵
uniform float4x4 UNITY_MATRIX_P; // 投影矩阵
uniform float4x4 UNITY_MATRIX_VP; // 视图投影矩阵
uniform float4x4 UNITY_MATRIX_T_MV; // 模型视图矩阵的转置矩阵
uniform float4x4 UNITY_MATRIX_IT_MV; // 模型视图矩阵的逆矩阵的转置矩阵
uniform float4x4 UNITY_MATRIX_TEXTURE0; // 贴图纹理矩阵
uniform float4x4 UNITY_MATRIX_TEXTURE1; //贴图纹理矩阵
uniform float4x4 UNITY_MATRIX_TEXTURE2; //贴图纹理矩阵
uniform float4x4 UNITY_MATRIX_TEXTURE3; //贴图纹理矩阵
uniform float4 UNITY_LIGHTMODEL_AMBIENT; // 环境色
六、输入输出
对于程图形渲染管线,可编程控制的部分只有两个,顶点着色器和片段着色器。对于编程控制这两个部分,首要的任务就是要怎么给它们传参数。Cg语言的参数传递同样也有“值传递”和“引用传递”之分。因为GPU不支持指针,所以Cg语言采用了如下的方式来修辞参数传递:
1、in:修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值的改变不会影响实参值,传递方式为值传递。
2、out:修辞一个形参只是用于输出,进入函数体时没有被初始化,一般为函数的返回值。
3、inout:修辞一个形参即用于输入也用于输出,这是典型的引用传递。
一些常用 Shader 变量的Attribute设置
1.1 枚举, 使用系统自带的枚举
[Enum(UnityEngine.Rendering.BlendMode)] _Blend ("Blend mode", Float) = 1
1.2 枚举, Shader中设置的枚举并指定枚举值
[Enum(One,1,SrcAlpha,5)] _Blend2 ("Blend mode subset", Float) = 1
1.3 枚举, 控制宏关键字, 自动添加变量
[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
会自动生成变量_OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
这个关键字需要在CGPROGRAM ... ENDCG 中定义
2.1. 普通开关
[Toggle] _Invert("Invert color?", Float) = 0
2.2 关键字开关
[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
需要手动添加 #pragma multi_compile _ ENABLE_FANCY
或 #pragma shader_feature ENABLE_FANCY
3.1 数值变量拖动条
_Blend("Blend", Range(0,1)) = 0.5 // 普通拖动条
[PowerSlider(3.0)] _Shininess ("Shininess", Range (0.01, 1)) = 0.08 // 二次方拖动条
4.1 一般扩展
// Default small amount of space. [Space] _Prop1 ("Prop1", Float) = 0
// Large amount of space. [Space(50)] _Prop2 ("Prop2", Float) = 0
[Header(A group of things)] _Prop1 ("Prop1", Float) = 0 // 提示信息
顶点和片段着色器
input结构体构成
POSITION, SV_POSITION
|
The position of a vertex in world coordinates (object space)
|
NORMAL
|
The normal of a vertex, relative to the world (not to the camera)
|
COLOR, COLOR0, DIFFUSE, SV_TARGET
|
The color information stored in the vertex
|
COLOR1, SPECULAR
|
The secondary color information stored in the vertex (usually the pecular)
|
TEXCOORD0, TEXCOORD1,
…, TEXCOORDi
|
The i-th UV data stored in the vertex
|
POSITION, SV_POSITION, HPOS
|
The position of a vertex in camera coordinates (clip space, from zero to one for each dimension)
|
COLOR, COLOR0, COL0,
COL, SV_TARGET
|
The front primary color
|
COLOR1, COL1
|
The front secondary color
|
TEXCOORD0, TEXCOORD1,
…, TEXCOORDi, TEXi
|
The i-th UV data stored in the vertex
|
WPOS
|
The position, in pixels, in the window (origin in the lower left corner)
|
编写shader时的一些建议:
1、只计算需要计算的东西;
2、通常,需要渲染的像素比顶点数多,而顶点数又比物体数多很多。所以如果可以,尽量将运算从PS移到VS,或直接通过script来设置某些固定值;
3、在使用Surface Shader时,可以通过一些指令让shader优化很多。
通常情况下,Surface shader的很多默认选项都是开启的,以适应大多数情况,但是很多时候,你可以关闭其中的一些选项,从而让你的shader运行的更快:
(1) approxview 对于使用了view direction的shader,该选项会让view dir的normalize操作per-vertex进行,而不是per-pixel。这个优化通常效果明显。
(2) halfasview 可以让Specular shader变得快一些,使用一个介于光照方向和观察方向之间的half vector来代替真正的观察方向viewDir来计算光照函数。
(3) noforwardadd Forward Render时,完全只支持一盏方向光的per-pixel渲染,其余的光照全部按照per-vertex或SH渲染。这样可以确保shader在一个pass里渲染完成。
(4) noambient 禁掉ambient lighting和SH lighting,可以让shader快一点儿。
4、浮点数精度相关:
float:最高精度,通常32位
half:中等精度,通常16位,-60000到60000,
fixed:最低精度,通常11位,-2.0到2.0,1/256的精度。
尽量使用低精度。对于color和unit length vectors,使用fixed,其他情况,根据取值范围尽量使用half,实在不够则使用float。
在移动平台,关键是在fragment shader中尽可能多的使用低精度数据。另外,对于多数移动GPU,在低精度和高精度之间转换是非常耗的,在fixed上做swizzle操作也是很费事的。
5、Alpha Test
Alpha test和clip()函数,在不同平台有不同的性能开销。
通常使用它来cull那些完全透明的像素。
但是,在ios和一些android上使用的PowerVR GPUs上面,alpha test非常的昂贵。
6、Color Mask
在移动设备上,Color Mask也是非常昂贵的,所以尽量别使用它,除非真的是需要。
shader多版本编译:
shader variants(shader 变种)使用方式:
(1)#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON
(2)#pragma shader_feature FANCY_STUFF_OFF FANCY_STUFF_ON
#pragma shader_feature FANCY_STUFF是#pragma shader_feature _ FANCY_STUFF的快捷方式,同样会生成两个变种
shader_feature和multi_compile的区别:
shader_feature:未使用的变种不会build进游戏;
multi_compile:会编译生成所有变种。
用法:shader_feature更适用于材质的关键字,而multi_compile更适用于代码设置的全局关键字。
关键字个数限制:
unity最多支持256个关键字,并且unity内部已经使用了60个左右。另外,可以在unity的工程设置中定义一些全局有效的关键字,这样也会消耗一些数量,所以在编写shader时要注意数量不要超过上限。
内置的multi_compile快捷组合:
#pragma multi_compile_fwdbase 编译ForwardBase需要的所有关键字变种,包括不同的lightmap类型,主要的方向光是否开启阴影等。
#pragma multi_compile_fwdadd 编译ForwardAdd Pass包含的关键字变种。
#pragma multi_compile_fwdadd_fullshadows 和上一个类似,另外还包含了光照实施阴影的能力。
#pragma ulti_compile_fog 雾效。
上面内置的快捷方式包含了很多的变种,但可以通过skip_variants来屏蔽某些关键字:
#pragma skip_variants POINT POINT_COOKIE
硬件级别shader变种:
提供针对不同硬件(比如gles和gles 3.0)的shader变种。(针对硬件能力级别的优化)
#pragma hardware_tier_variants renderer
d3d11 - Direct3D 11/12
glcore - OpenGL 3.x/4.x
gles - OpenGL ES 2.0
gles3 - OpenGL ES 3.x
metal - iOS
/Mac Metal
vulkan - Vulkan
d3d11_9x - Direct3D 11 9.x feature level, as commonly used on WSA platforms
xboxone - Xbox One
ps4
- PlayStation 4
psp2 - PlayStation Vita
n3ds - Nintendo 3DS
wiiu - Nintendo Wii U
添加了上述#pragma的shader会自动生成三个关键字变种:
UNITY_HARDWARE_TIER1
UNITY_HARDWARE_TIER2
UNITY_HARDWARE_TIER3
编辑器模式下,可以在Graphics Emulation中来手动设置使用哪个变体。
上述三中变体,同时只会加载其中一种,
指定方式:
(1)自动检测:在加载时,Unity检测GPU并进行设置;如果检测不到,默认选择最高级。
(2)手动设置来制定使用哪一个tier,代码如下:(一定是在shader加载之前制定,在某个shader加载之后再指定是不会影响该shader的)
Graphics.activeTier = UnityEngine.Rendering.GraphicsTier.Tier1;
平台shader设置:
可以通过代码手动设置[平台, tier, 设置]
UnityEditor.Rendering.EditorGraphicsSettings.SetTierSettings(BuildTargetGroup target, GraphicsTier tier, TierSettings settings);
Shader预处理宏、内置状态变量、多版本编译等
预定义shader预处理宏:
Target platform:
SHADER_API_OPENGL - desktop OpenGL
SHADER_API_D3D9 - Direct3D 9
SHADER_API_XBOX360 - Xbox 360
SHADER_API_PS3 - PlayStation 3
SHADER_API_D3D11 - desktop Direct3D 11
SHADER_API_GLES - OpenGL ES 2.0 (desktop or mobile), use presence of SHADER_API_MOBILE to determine.
SHADER_API_FLASH - Flash Stage3D
SHADER_API_D3D11_9X - Direct3D 11 target for Windows RT
Surface shader pass indicators:
UNITY_PASS_FORWARDBASE - 前向渲染的base pass(主方向光、lightmaps、SH)
UNITY_PASS_FORWARDADD - 前向渲染的add pass(没盏灯一个pass)
UNITY_PASS_PREPASSBASE - 延迟渲染base pass(renders normals & specular exponent).
UNITY_PASS_PREPASSFINAL - 延迟渲染final pass (applies lighting & textures).
UNITY_PASS_SHADOWCASTER - 阴影投射渲染pass.
UNITY_PASS_SHADOWCOLLECTOR - 阴影手机pass for directional light shadows.
内置全局状态变量:
内置的矩阵:
UNITY_MATRIX_MVP float4x4 Current model * view * projection matrix.
UNITY_MATRIX_MV float4x4 Current model * view matrix.
UNITY_MATRIX_V float4x4 Current view matrix.
UNITY_MATRIX_P float4x4 Current projection matrix.
UNITY_MATRIX_VP float4x4 Current view * projection matrix.
UNITY_MATRIX_T_MV float4x4 Transpose of model * view matrix.
UNITY_MATRIX_IT_MV float4x4 Inverse transpose of model * view matrix.
UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3 float4x4 Texture transformation matrices.
_Object2World float4x4 Current model matrix.
_World2Object float4x4 Inverse of current world matrix.
_WorldSpaceCameraPos float3 World space position of the camera.
unity_Scale float4 xyz components unused; w contains scale for uniformly scaled objects.
内置的向量:
UNITY_LIGHTMODEL_AMBIENT: Current ambient color.
光照相关的:
_ModelLightColor float4 Material’s Main * Light color
_SpecularLightColor float4 Material’s Specular * Light color
_ObjectSpaceLightPos float4 Light’s position in object space. w component is 0 for directional lights, 1 for other lights
_Light2World float4x4 Light to World space matrix
_World2Light float4x4 World to Light space matrix
_Object2Light float4x4 Object to Light space matrix
变量:
_Time float4 Time (t/20, t, t*2, t*3), use to animate things inside the shaders.
_SinTime float4 Sine of time: (t/8, t/4, t/2, t).
_CosTime float4 Cosine of time: (t/8, t/4, t/2, t).
unity_DeltaTime float4 Delta time: (dt, 1/dt, smoothDt, 1/smoothDt).
_ProjectionParams float4 x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix), y is the camera’s near plane, z is the camera’s far plane and w is 1/FarPlane.
_ScreenParams float4 x is the current render target width in pixels, y is the current render target height in pixels, z is 1.0 + 1.0/width and w is 1.0 + 1.0/height.
内置光照参数:
对于不同的Rendering Path和Pass Tag,可以使用的光照参数是不一样的。(当然所有参数你都可以在shader中使用,但并不能保证其一定会存储有效的值)。
Forward rendering(ForwardBase和PorwardAdd标记的pass)支持的变量:
LightColor0 (declared in Lighting.cginc) | fixed4 |Light color. | |WorldSpaceLightPos0 float4 Directional lights: (world space direction, 0). Other lights: (world space position, 1).
_LightMatrix0 (declared in AutoLight.cginc) float4x4 World-to-light matrix. Used to sample cookie & attenuation textures.
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0 float4 (ForwardBase pass only) world space positions of first four non-important point lights.
unity_4LightAtten0 float4 (ForwardBase pass only) attenuation factors of first four non-important point lights.
unity_LightColor half4[4] (ForwardBase pass only) colors of of first four non-important point lights.
说明:
(1)_LightColor0在ForwardBass里面表示:主光,一定是Pixel(Important)、Directional光源,没有的话则改值不可用;
(2)_LightColor0在ForwardAdd里面表示:当前执行的ForwardAdd的pixel光;
_WorldSpaceLightPos0的意义和_LightColor类似,也是在Base里表示主光,Add里表示像素光。
(3)unity_LightColor是距离物体最近的4个point光,也就是说同一个场景中每个物体的这4个光不一样,注意这4个point光的选择非常有意思;
(a)当shader中没有ForwardAdd Pass的时候,unity_LightColor记录了4盏vertex point光源,无视pixel光;
(b)当shader中包含ForwardAdd Pass的时候,unity_LightColor记录了4盏point光源,pixel比vertex优先。
(4)如果场景中有多个pixel光,则对每一个物体,所有的非主光的pixel光都会执行一遍ForwardAdd Pass,按照强度大小、平行光优先的顺序来执行;
(5)不要在ForwardAdd Pass里面使用unity4LightPosX/Y/Z等数据,因为它们就是给ForwardBase Pass使用的,而且如果其中有pixel光,它们本身就会对ForwardAdd执行一遍;
(6)可以使用Shade4PointLights函数在ForwardBase中计算Vertex光源的照明。
Deferred shading:
LightColor | float4 | Light color. | |LightMatrix0 float4x4 World-to-light matrix. Used to sample cookie & attenuation textures.
Vertex-lit rendering(Vertex标记的pass):
顶点光照可以使用最多8盏光源,这些光源按照亮度排序,按顺序存储在一下这些数据结构中,如果光源数量少于8,那么多出来的都将黑色:
unity_LightColor half4[8] Light colors.
unity_LightPosition float4[8] View-space light positions. (-direction,0) for directional lights; (position,1) for point/spot lights.
unity_LightAtten half4[8] Light attenuation factors. x is cos(spotAngle/2) or –1 for non-spot lights; y is 1/cos(spotAngle/4) or 1 for non-spot lights; z is quadratic attenuation; w is squared light range.
unity_SpotDirection float4[8] View-space spot light positions; (0,0,1,0) for non-spot lights.
说明:
(1)_LightColor0表示unity_LightColor[0]
(2)unity_LightPosition是MV空间中的位置
(3)逐顶点光照函数float4 ShadeVertexLights(float4 vertex, float3 normal)
注意:
(1)_LightColor0变量不代表某个具体含义,它在不同的Render Path和Pass里面是有不同的意义的
(2)ShaderLab里面的内置变量是不会及时清理的,这是Unity处于性能方面的考虑,因为在CPU和GPU之间频繁传递数据是很耗资源的
Fog和Ambient环境光信息:
UNITY_LIGHTMODEL_AMBIENT fixed4 Ambient lighting color (Equator ambient in three-color lighting case).
unity_AmbientSky fixed4 Sky ambient lighting color in three-color lighting case.
unity_AmbientGround fixed4 Ground ambient lighting color in three-color lighting case.
unity_FogColor fixed4 Fog color.
unity_FogParams float4 Parameters for fog calculation: (density / sqrt(ln(2)), density / ln(2), –1/(end-start), end/(end-start)). x is useful for Exp2 fog mode, y for Exp mode, z and w for Linear mode.
使用multi_compile编译Shader的多个版本:
有时候我们希望保留一个shader的大多数实现,而只是做一些细节的修改,此时我们就可以通过该方式来生成shader的多个版本,然后在代码中控制使用哪个版本。
命令:
#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON
控制:
Material.EnableKeyword(keyword: string)/DisableKeyword(keyword: string)
Shader.EnableKeyword(keyword: string)/DisableKeyword(keyword: string)
在shader中根据定义好的关键字处理具体显示方案:
float4 frag(vertOut i):COLOR
{
float4 c = float4(0, 0, 0, 0);
#ifdef MY_multi_1
c = float4(0, 1, 0, 0);
#endif
#ifdef MY_multi_2
c = float4(0, 0, 1, 0);
#endif
return c;
}
注意不要在shader中大量使用此命令,因为关键字的个数是有上限的,而多条multi_complie会交叉匹配产生很多种结果。
GLSL shader编程
在Shader中,除了Cg/HLSL以外,GLSL(OpenGL Shading Languate)也可以直接使用。
一般不建议使用GLSL,除非你确定你的目标平台只有Max OS X或兼容OpenGL ES 2.0的移动设备。
Unity默认都会将Cg/HLSL交叉编译出优化过得GLSL,以此来支持多平台。在desktop平台下可以通过打开#pragma glsl选项来支持。
平台特性相关的渲染差异:
Unity封装了大多数的平台问题,但有些时候还是需要自己处理一下的。
纹理坐标:
Direct3D里面是左上角为原点;
OpenGL和OpenGL ES是左下角为原点。
多数时候都ok,除了在Render To Texture时。但是在RTT时,Unity对于D3D平台会自动将渲染的贴图上下翻转一下,以此隐藏了各平台的差异。
D3D平台下,有一种情况不需要Unity自动翻转渲染贴图,那就是使用Image Effects和Anti-Aliasing时,因为直接渲染到Screen了,
需要手动处理的情况:但同时使用screen texture和RenderTexture时,有可能会得到不一致的坐标朝向,此时就需要在shader里面通过代码处理了:
// On D3D when AA is used, the main texture and scene depth texture
// will come out in different vertical orientations.
// So flip sampling of the texture when that is the case (main texture
// texel size will have negative Y).
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
uv.y = 1-uv.y;
#endif
AlphaTest:
在OpenGL ES 2.0和Direct3D 11中,没有alpha testing的固定函数。所以在编写programmable shader时,建议在pixel shader中使用CG/HLSL clip()函数来替代。
D3D 11 shader编译器比较挑剔:
D3D 9和OpenGL使用NVIDIA的Cg来编译shader,但D3D 11使用微软的HLSL来编译,此时就会产生一些差异化。
OpenGL ES 2.0只支持部分GLSL原生的东西,所以Unity实现了一些内置的参数,让其以OpenGL的方式工作,但是也有一些参数是没有的。
_WorldSpaceLightPos0:
在UnityCG.cginc里,提供了获取世界空间下的光照方向的函数
// Computes world space light direction
inline float3 WorldSpaceLightDir( in float4 v )
{
float3 worldPos = mul(_Object2World, v).xyz;
#ifndef USING_LIGHT_MULTI_COMPILE
return _WorldSpaceLightPos0.xyz - worldPos * _WorldSpaceLightPos0.w;
#else
#ifndef USING_DIRECTIONAL_LIGHT
return _WorldSpaceLightPos0.xyz - worldPos;
#else
return _WorldSpaceLightPos0.xyz;
#endif
#endif
}
其中,由于平行光的方向不随顶点位置发生变化,因此直接使用_WorldSpaceLightPos0.xyz即可,此时里面存储的其实就是平行光的方向,而非位置。同时,
_WorldSpaceLightPos0.w可以表明该光源的类型,如果为0表示是平行光,为1表示是点光源或者聚光灯光源。
TANGENT_SPACE_ROTATION:
创建一个正切空间的旋转矩阵,TANGENT_SPACE_ROTATION由下面两行组成
等同于如下两行代码:
float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w;
float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal );
一、
#pragma surface surfaceFunction lightModel [optionalparams]
surfaceFunction(指明使用的表面函数):
void surf(Input IN, inout SurfaceOutput o)
void surf(Input IN, inout SurfaceOutputStandard o) 基于PBR
void surf(Inout IN, inout SurfaceOutputStandardSpecular o) 基于PBR
lightModel(指明使用的光照模型):
内置不基于物理的:
Lambert,BlinnPhong
内置基于物理的:
Standard,StandardSpecular
自定义光照模型
[optionalparams](可选参数):
透明度混合与透明度测试
alpha or alpha:auto 为简单光照选择褪色透明度(等同于alpha:fade) ,以及基于物理照明的预乘透明度(等同于alpha:premul)
alpha:blend 开启透明度混合
alpha:fade 开启传统渐变透明
alpha:premul 开启预乘a透明度
alphatest:VariableName 根据VariableName的变量来控制透明度混合和透明度测试,VariableName是一个float型的变量,剔除不满足条件的片元,此时往往需要用到addshadow来生成正确阴影投射的Pass
keepalpha 默认不透明表面着色器将1写入A通道,不管alpha输出值以及光照函数的返回值
decal:add 对其他表面上的物体使用additive blending
decal:blend 对其他表面上的物体使用alpha blending
自定义修改函数
vertex:VertexFunction 顶点修改函数,用于修改计算顶点位置、信息等
finalcolor:ColorFunction 最终颜色修改函数
finalgbuffer:ColorFunction 自定义延迟路径,用于更改gbuffer
finalprepass:ColorFunction 自定义预处理路径
阴影
addshadow 生成一个阴影投射的Pass,为一些使用了顶点动画、透明度测试的物体产生正确的阴影
fullforwardshadows 支持前向渲染路径中所有光源类型的阴影,shader默认只支持最重要平行光的阴影,添加该参数可以支持点光源或聚光灯的阴影效果
tessellate:TessFunction 使用DX11 GPU曲面细分
控制代码生成(表面着色器默认处理所有坑能的光照、阴影、光照烘培,可手动调整跳过一些不必要的加载提升性能)
exclude_path:deferred, exclude_path:forward, exclude_path:prepass 不为某个渲染路径生成代码
noshadow 禁用阴影
noambient 不应用任何环境光以及光照探针
novertexlights 在前向渲染路径中不应用任何逐顶点光照及光照探针
nolightmap 不应用任何光照烘培
nodynlightmap 不应用实时GI
nodirlightmap 不应用directional lightmaps
nofog 不应用任何雾效
nometa 生成meta这个Pass(that’s used by lightmapping & dynamic global illumination to extract surface information)
noforwardadd 不应用前向渲染中所有的additive pass,使得shader只支持一个重要平行光,其他光用逐顶点/SH光源计算光照影响,使shader更精简
nolppv 不应用光照探针代理Light Probe Proxy Volume(LPPV)
noshadowmask 不应用Shadowmask
其他
softvegetation 只有当Soft Vegetation(软植被)开启时该shader才被渲染
interpolateview 在顶点而不是片元着色器中计算 view direction并插值,需多使用一张纹理插值器,提升渲染速度
halfasview Pass half-direction vector into the lighting function instead of view-direction. Half-direction will be computed and normalized per vertex. This is faster, but not entirely correct.
dualforward 在前向渲染中使用dual lightmaps
dithercrossfade 使表面着色器支持 dithering effects
二、Input
包含表面属性数据来源,作为表面函数的输入结构体,顶点修改函数的输出结构体
其中的采样坐标必须以uv为前缀,如uv_MainTex(uv2也可,表明使用次级纹理坐标集合)
各个变量往往由Unity自动准备好,直接在表面函数中使用即可,但如自定义了顶点修改函数用Input作为输出时需在里面自定义相应变量
float3 viewDir 包含视角方向
float4 with COLOR semantic 包含插值后的逐顶点颜色
float4 screenPos 包含屏幕空间坐标,用于反射、屏幕特效等,不支持 GrabPass ,需自己用ComputeGrabScreenPos计算UV
float3 worldPos 包含世界空间位置
float3 worldRefl 包含世界空间下反射方向,前提是没有修改表面法线o.Normal
float3 worldNormal 包含世界空间下法线方向,前提是没有修改表面法线o.Normal
float3 worldRefl; INTERNAL_DATA 如果修改了表面法线o.Normal,需要使用该变量告诉Unity要基于修改后的法线计算世界空间下的反射方向。用WorldReflectionVector (IN, o.Normal)得到世界空间下的反射方向。
float3 worldNormal; INTERNAL_DATA 如果修改了表面法线o.Normal,需要使用该变量告诉Unity要基于修改后的法线计算世界空间下的法线方向。用WorldReflectionVector (IN, o.Normal)得到世界空间下的法线方向。
三、SurfaceOutput
作为表面函数的输出,作为光照函数的输入进行各种光照计算
结构体中的变量是提前声明好的,不可增加或减少,若没有赋值则使用默认值
struct SurfaceOutput(非物理的光照模型)
{
fixed3 Albedo; // diffuse color
fixed3 Normal; // tangent space normal, if written
fixed3 Emission;
half Specular; // specular power in 0..1 range
fixed Gloss; // specular intensity
fixed Alpha; // alpha for transparencies
};
struct SurfaceOutputStandard(默认金属工作流程)
{
fixed3 Albedo; // base (diffuse or specular) color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Metallic; // 0=non-metal, 1=metal
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
struct SurfaceOutputStandardSpecular(高光工作流程)
{
fixed3 Albedo; // diffuse color
fixed3 Specular; // specular color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
其中,Specular为高光反射中指数部分的系数,Gloss为高光反射中强度系数
四、自定义光照模式
half4 LightingName (SurfaceOutput s, half3 lightDir, half atten);
用于表示前向渲染路径中的光照模式,不取决于view direction
half4 LightingName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
用于表示前向渲染路径中的光照模式,包含view direction
half4 LightingName_PrePass (SurfaceOutput s, half4 light);
用于延迟光照路径
half4 LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, bool surfFuncWritesNormal);
half4 LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, half3 viewDir, bool surfFuncWritesNormal,out half3 specColor);
前者不包含,后者包含view direction,这两个函数会自动处理前向渲染路径和延迟渲染路径
half4 Lighting<Name> (SurfaceOutput s, UnityGI gi); Use this in forward rendering paths for light models that are not dependent on the view direction.
half4 Lighting<Name> (SurfaceOutput s, half3 viewDir, UnityGI gi); Use this in forward rendering paths for light models that are dependent on the view direction.
half4 Lighting<Name>_Deferred (SurfaceOutput s, UnityGI gi, out half4 outDiffuseOcclusion, out half4 outSpecSmoothness, out half4 outNormal); Use this in deferred lighting paths.
half4 Lighting<Name>_PrePass (SurfaceOutput s, half4 light); Use this in light prepass (legacy deferred) lighting paths.
half4 Lighting<Name>_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi);