Shader Cheat Sheet

CG 函数速查手册

Link

UNITY_MATRIX_MVP

使用方法:

1
2
mul(UNITY_MATRIX_MVP,*) //旧版
UnityObjectToClipPos(*) //新版

作用:

将模型空间下的 顶点/方向矢量 变换到 剪裁空间

空间:

  • 模型空间: 模型内部的坐标系
  • 世界空间: 世界坐标系 (变换使用 _Object2World(*) )
  • 观察空间: 以相机为原点的世界空间 (范围没有缩减,只是世界空间的旋转/缩放/平移)
  • 剪裁空间: 两个剪裁平面(近剪裁面/远剪裁面) 构成的菱形(透视)或方形(正交). 原点可以认为是该区域中的某一点(近剪裁面的中心点). 其内部坐标满足与 -w <= xyz <= w, 不满足条件的顶点均不在剪裁空间内
  • 屏幕空间: 最终的2D平面

[NoScaleOffset] 标签

Properties的纹理加入该标签后则不可以缩放和平移

1
2
3
4
5
Properties {
_MainTex ("Splat Map", 2D) = "white" {}
[NoScaleOffset] _Texture1 ("Texture 1", 2D) = "white" {}
[NoScaleOffset] _Texture2 ("Texture 2", 2D) = "white" {}
}

a2v v2f

  • a: Application 应用层
  • v: Vertex Shader 顶点着色起
  • f: Fragment Shader 片元着色起

_MainTex_ST

使用纹理名称后面加_ST 访问纹理的Scale(S),和Translation(T)属性

1
2
3
4
5
6
7
float4 _MainTex_ST; //声明部分

v2f vert(a2v v) {
...
o.uv = v.texcoord.xy * _MainTex_ST.xy+ _MainTex_ST.zw; //xy是Scale,zw是平移
...
}

tex2D

用于纹理采样

1
2
3
4
fixed4 frag(v2f i) : SV_Target {
fixed4 c = tex2D(_MainTex, i.uv); //首参数未纹理,第二参数为uv坐标
return fixed4(c.rgb, 1.0);
}

手动取MipMappingLevel

Link

1
2
3
4
5
6
7
8
9
10
11
12

// Manually set mip level and return fragment
float mipLevel = GetMipLevel(i.texcoord0, _AtlasSize);
return tex2Dlod(_MainTex, float4(uv.xy, 0, mipLevel));

float GetMipLevel(float2 iUV, float2 iTextureSize)
{
float2 dx = ddx(iUV * iTextureSize.x);
float2 dy = ddy(iUV * iTextureSize.y);
float d = max(dot(dx, dx), dot(dy,dy));
return 0.5 * log2(d);
}

Link

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define SUB_TEXTURE_SIZE 512.0
#define SUB_TEXTURE_MIPCOUNT 10

float MipLevel( float2 uv )
{
float2 dx = ddx( uv * SUB_TEXTURE_SIZE );
float2 dy = ddy( uv * SUB_TEXTURE_SIZE );
float d = max( dot( dx, dx ), dot( dy, dy ) );

// Clamp the value to the max mip level counts
const float rangeClamp = pow(2, (SUB_TEXTURE_MIPCOUNT - 1) * 2);
d = clamp(d, 1.0, rangeClamp);

float mipLevel = 0.5 * log2(d);
mipLevel = floor(mipLevel);

return mipLevel;
}

Calculating half texel offset for selected mip level also

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define SUB_TEXTURE_SIZE 512.0
#define SUB_TEXTURE_MIPCOUNT 10

float MipLevel( float2 uv, out float a_halfOffset )
{
float2 dx = ddx( uv * SUB_TEXTURE_SIZE );
float2 dy = ddy( uv * SUB_TEXTURE_SIZE );
float d = max( dot( dx, dx ), dot( dy, dy ) );

// Clamp the value to the max mip level counts
const float rangeClamp = pow(2, SUB_TEXTURE_MIPCOUNT - 1);
d = clamp(sqrt(d), 1.0, rangeClamp);

float mipLevel = log2(d);
mipLevel = floor(mipLevel);

a_halfOffset = d * (1.0 / pow(2.0, SUB_TEXTURE_MIPCOUNT));

return mipLevel;
}

标准光照模型

最终点颜色 = 自发光结果(emissive) + 高光反射结果(specular) + 漫反射(diffuse) + 环境光(ambient)

环境光(ambient)

通过 UNITY_LIGHTMODEL_AMBIENT.xyz访问

漫反射(diffuse)

C_diffuse = (C_light M_diffuse) Max(0,n*l)

比如:

1
2
fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb
fixed3 diffuse _LightColor0.rgb * albedo * max(0,dot(tangentNormal,tangentLightDir))
  • C_light : 就是当前点的光源颜色
  • M_diffuse : 材质的漫反射系数 也就是当前纹理的采样rgb值 * 叠加的颜色值
  • n : 当前坐标系下的单位法线
  • l : 当前坐标系下的光线方向

效果:

根据 光线和法线夹角不同 表现为物体有明暗变化
和相机的角度无关,只和光线有关

C_diffuse = (C_light M_diffuse) (A*(n*l)+B)
C_diffuse = (C_light M_diffuse) (0.5*(n*l)+0.5)

Half Lambert 光照模型. 对漫反射加入偏移修正. 防止背光区域过暗

高光反射(Specular)

C_specular = (C_light M_specular) Max(0,v*r)^M_Gloss

比如

1
2
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-mul(_Ojbect2World,v.vertext).xyz);
flxed3 specular = _LightColor.rgb * _Specular.rgb * pow(setuarte(dot(reflectDir,viewDir)),_Gloss)
  • viewDir : 视角方向,对应公式中的v 是用相机pos-当前Vertext在世界坐标系的Pos得到
  • reflectDir : 反射角度,对应公式中的r.使用函数reflect(i,n)求得. 手动求反射角度公式及求解方法

Blinn-Phone模型

C_specular = (C_light M_specular) Max(0,n*h)^M_Gloss

  • n : 法线
  • h : normalize(入射Dir+光照Dir)

效果:

高光反射用来处理因为光线而产生的亮斑
高光反射不对纹理进行采样.所以只是往原有颜色上叠加亮斑(_LightColor.rgb * _Specular.rgb 这两个控制叠加的颜色)
_Gloss控制光泽度.也就是值越大则光斑越大

坚持原创技术分享,您的支持将鼓励我继续创作!