# [笔记]unity渲染类零碎代码记录（100多条）

1.获得renderTexture上的4个角的近裁面位置
cam = GetComponent<Camera>();
Matrix4x4 inverseViewProjectionMatrix = GL.GetGPUProjectionMatrix(cam.projectionMatrix, true);
inverseViewProjectionMatrix *= cam.worldToCameraMatrix;
inverseViewProjectionMatrix = inverseViewProjectionMatrix.inverse;
Vector3 leftBottom = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(-1, -1, 1));
Vector3 rightBottom = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(1, -1, 1));
Vector3 leftTop = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(-1, 1, 1));
Vector3 rightTop = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(1, 1, 1));

2.获得renderTexture上的4个角的远裁面位置[相机空间的位置]
cam = GetComponent<Camera>();
Matrix4x4 inverseViewProjectionMatrix = GL.GetGPUProjectionMatrix(cam.projectionMatrix, true);//根据相机的投影矩阵计算GPU投影矩阵
inverseViewProjectionMatrix *= cam.worldToCameraMatrix;//unity_MatrixVP
inverseViewProjectionMatrix = inverseViewProjectionMatrix.inverse;//unity_MatrixVP矩阵的逆矩阵
Vector3 leftBottom = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(-1, -1, 0));
Vector3 rightBottom = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(1, -1, 0));
Vector3 leftTop = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(-1, 1, 0));
Vector3 rightTop = inverseViewProjectionMatrix.MultiplyPoint(new Vector3(1, 1, 0));

3. GL.GetGPUProjectionMatrix
GL.GetGPUProjectionMatrix函数获得差异处理后的投影矩阵，并且远裁面深度为0，近裁面深度为1。
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(mCamera.projectionMatrix, true);
unity_MatrixVP = projectionMatrix * mCamera.worldToCameraMatrix;
DX左上角为(0,0)且值域为(0,1),GL左下角为(0,0）且值域为(-1,1)
GL.GetGPUProjectionMatrix:用于处理DX和GL的坐标差异性。

5.shdaer看属性数据，可以通过帧调试窗口看

6.矩阵
E00~E03
E10~E13
...
...

length(pos1 - pos2)

9.
[loop]
[unroll]
[NoScaleOffset]        隐藏贴图的Tilling和Offset选项
[Toggle]        模拟开关，0为假，1为真，同[MaterialToggle]

10.

11.
//当前激活的渲染目标
//获取当前状态的RenderTexture（对应于OnRenderImage的source）
UnityEngine.Rendering.BuiltinRenderTextureType.CurrentActive

public Light ml;
public RenderTexture rt;
CommandBuffer buf = null;
buf = new CommandBuffer();
RenderTargetIdentifier id = new RenderTargetIdentifier(rt);
buf.Blit(BuiltinRenderTextureType.CurrentActive,id );

private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(rt,destination);
}

14.
_material.EnableKeyword("HEIGHT_FOG");
_material.DisableKeyword("HEIGHT_FOG");

15 两向量之间夹角弧度计算，当direction1 和direction2 都是单位向量时

16.
/// <summary>
/// 相机是否在点光源范围内
private bool IsCameraInPointLightBounds()
{
float distanceSqr = (_light.transform.position - Camera.current.transform.position).sqrMagnitude;
float extendedRange = _light.range + 1;
if (distanceSqr < (extendedRange * extendedRange))
return true;
return false;
}

/// <summary>
/// 相机是否在Spot光源范围内
private bool IsCameraInSpotLightBounds()
{
// check range
float distance = Vector3.Dot(_light.transform.forward, (Camera.current.transform.position - _light.transform.position));
float extendedRange = _light.range + 1;
if (distance > (extendedRange))
return false;

// check angle
float cosAngle = Vector3.Dot(transform.forward, (Camera.current.transform.position - _light.transform.position).normalized);
if ((Mathf.Acos(cosAngle) * Mathf.Rad2Deg) > (_light.spotAngle + 3) * 0.5f)
return false;
return true;
}

17.cos 和acos

float angle=Mathf.Acos(Mathf.Dot(direction1,direction2)) * Mathf.Rad2Deg -> 得到角度

18.投影向量
Vector3.Project(toP_foDirect, toP_toDirect);
public static Vector3 Project(Vector3 vector, Vector3 onNormal)
{
//pow(onNormal, 2)
float num1 = Vector3.Dot(onNormal, onNormal);
if ((double) num1 < (double) Mathf.Epsilon)
return Vector3.zero;
float num2 = Vector3.Dot(vector, onNormal);
return new Vector3(
onNormal.x * num2 / num1,
onNormal.y * num2 / num1,
onNormal.z * num2 / num1
);
}

19.OnPreRender和OnPostRender代替OnRenderImage
private void OnPreRender()
{
if(QualitySettings.antiAliasing == 0)
cameraRenderTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default);
else
cameraRenderTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default, QualitySettings.antiAliasing);
mainCamera.targetTexture = cameraRenderTex;
}

private void OnPostRender()
{
Graphics.Blit(cameraRenderTex, null as RenderTexture);
RenderTexture.ReleaseTemporary(cameraRenderTex);
}

20.射线追踪
https://zhuanlan.zhihu.com/p/21425792

//ro视线起点，rd是视线方向
vec3 raymarch(vec3 ro, vec3 rd)
{
const int stepNum= 100;
//光源强度
const float lightIntense = 100.;
//推进步幅
float stepSize= 250./stepNum;
vec3 light= vec3(0.0, 0.0, 0.0);
//光源位置
vec3 lightPos = vec3(2.,2.,.5);
float t = 1.0;
vec3 p = vec3(0.0, 0.0, 0.0);
for(int i=0; i<stepNum;i++)
{
vec3 p = ro + t*rd;
//采样点光照亮度
float vLight = lightIntense /dot(p-lightPos,p-lightPos);
light + =vLight;
//继续推进
t+=stepSize;
}
return light;
}

float InScatter(vec3 start, vec3 rd, vec3 lightPos, float d)
{
vec3 q = start - lightPos;
float b = dot(rd, q);
float c = dot(q, q);
float iv = 1.0f / sqrt(c - b*b);
float l = iv * (atan( (d + b) * iv) - atan( b*iv ));

return l;
}

21.

in , out , inout,
uniform (被修饰的变量从外部传入), const

POSITION, NORMAL, BINORMAL, BLENDINDICES, BLENDWEIGHT, TANGENT, PSIZE,
TEXCOORD0 ~ TEXCOORD7, SV_VertexID

POSITION, PSIZE, FOG，COLOR0 ~ COLOR1, TEXCOORD1 ~ TEXCOORD7,SV_POSITION

22.SV_VertexID
struct VSInput
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
uint vertexId : SV_VertexID;
};

23.移位操作符只能作用在int上
int2 a = int2(0.0,0.0);
int2 b = a>>1;

float2 a = int2(0.0,0.0);
float2 b = a>>1;

24.三目运算符
if(a < 0){b = a}
else{c = a}

a < 0 ） ?(b = a) :( c = a); expr2 和 expr3 必须是与 expr1 长度相同的向量

25.in out inout
in : 修辞一个形参只是用于输入，进入函数体时被初始化，且该形参值的改变不会影响实参值，这是典型的值传递方式。
out : 修辞一个形参只是用于输出的，进入函数体时并没有被初始化，这种类型的形参一般是一个函数的运行结果；
inout : 修辞一个形参既用于输入也用于输出，这是典型的引用传递。

void myFunction(out float x);  // 形参 x ，只是用于输出
void myFunction(inout float x);  // 形参 x ，即用于输入时初始化，也用于输出数据
void myFunction(in float x);  // 形参 x ，只是用于输入
void myFunction(float x);  / 等价与 in float x ，这种用法和 C/C++ 完全一致

26.

#pragma target 3.0：派生指令，纹理LOD采样，10个插值器，允许使用更多的数学/纹理指令。

27.
sampler2D_half - 低精度采样器

28.
sampler2D _MainTex;
half4 color = tex2D(_MainTex, uv);

unity允许声明贴图和采样器时使用DX11风格的HLSL语法，用一个特殊的命名惯例来将他们匹配起来；拥有名字为“sampler”+贴图名字 的采样器会对这个纹理进行取样
Texture2D _MainTex;
SamplerState sampler_MainTex; // "sampler" + “_MainTex”
half4 color = _MainTex.Sample(sampler_MainTex, uv);

29.替换着色器

Tags { "RenderType"="Opaque" }
Pass {
...
}
}
Tags { "RenderType"="SomethingElse" }
Pass {
...
}
}
...
}

30.定点翻转
Direct3D的：顶部的坐标为0，向下则增加。这适用于Direct3D，Metal和控制台。
OpenGL：底部的坐标为0，向上增大。这适用于OpenGL和OpenGL ES。
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
uv.y = 1-uv.y;
#endif

31.

32.lod
Unity中的内置着色器通过以下方式设置其LOD
VertexLit着色器= 100    VertexLit kind of shaders = 100

33.纹理数组
_MyArr ("Tex", 2DArray) = "" {}
#pragma require 2darray

34.Visual Studio调试着色器

35.深度专题
https://www.jianshu.com/p/80a932d1f11e
35.1 开启
Camera.main.depthTextureMode = DepthTextureMode.Depth;

35.2 声明

35.3 访问
//1.如果是后处理，可以直接用uv访问
//vertex
//当有多个RenderTarget时，处理UV翻转问题
#if UNITY_UV_STARTS_AT_TOP //DirectX之类的
if(_MainTex_TexelSize.y < 0)
o.uv.y = 1 - o.uv.y; //满足上面两个条件时uv会翻转，因此需要转回来
#endif
//fragment

//2.其他：利用投影纹理采样
//vertex
o.screenPos = ComputeScreenPos(o.vertex);
//fragment

float linear01Depth = Linear01Depth(depth); //转换成[0,1]内的线性变化深度值
float linearEyeDepth = LinearEyeDepth(depth); //转换到视野空间

35.4 NDC坐标反推世界坐标，利用VP矩阵重建世界坐标
35.4.1
// _InverseViewProjectionMatrix UNITY_MATRIX_VP矩阵的逆矩阵
float worldPos = mul(_InverseViewProjectionMatrix, float3(i.uv * 2 - 1, depth));//通过NDC坐标反推世界坐标

35.4.2
unity中
Matrix4x4 currentVP = VPMatrix;
Matrix4x4 currentInverseVP = VPMatrix.inverse;
mat.SetMatrix("_CurrentInverseVP", currentInverseVP);
Graphics.Blit(source, destination, mat);

float4 NDC = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, depth * 2 - 1, 1); //NDC坐标
float4 WorldPosWithW = mul(_CurrentInverseVP, NDC);//VP逆矩阵乘以NDC坐标得到世界坐标
float4 WorldPos = WorldPosWithW / WorldPosWithW.w; //公式推导可知需除以w,参考：https://www.cnblogs.com/sword-magical-blog/p/10483459.html
//因为经过了 投影矩阵, 分量w 就不在为1, 所以要求得具体的世界坐标, 就需要 除以 分量w 的影响

35.4.3 利用视椎方向向量重建
unity中
float halfHeight = near * tan(fov/2);
float halfWidth = halfHeight * aspect;
Vector3 toTop = up * halfHeight;
Vector3 toRight = right * halfRight;
Vector3 toTopLeft = forward + toTop - toRight;
Vector3 toBottomLeft = forward - toTop - toRight;
Vector3 toTopRight = forward + toTop + toRight;
Vector3 toBottomRight = forward - toTop + toRight;
toTopLeft /= cam.nearClipPlane;//视锥体近平面的四个点
toBottomLeft /= cam.nearClipPlane;
toTopRight /= cam.nearClipPlane;
toBottomRight /= cam.nearClipPlane;
Matrix4x4 frustumDir = Matrix4x4.identity;
frustumDir.SetRow(0, toBottomLeft);
frustumDir.SetRow(1, toBottomRight);
frustumDir.SetRow(2, toTopLeft);
frustumDir.SetRow(3, toTopRight);
mat.SetMatrix("_FrustumDir", frustumDir);

//vertex
int ix = (int)o.uv.z;
int iy = (int)o.uv.w;
o.frustumDir = _FrustumDir[ix + 2 * iy];
//fragment
float linearEyeDepth = LinearEyeDepth(depth);
float3 worldPos = _WorldSpaceCameraPos + linearEyeDepth * i.frustumDir.xyz;

35.5 ComputeScreenPos和COMPUTE_EYEDEPTH
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.scrPos = ComputeScreenPos(o.pos);//将返回片段着色器的屏幕位置。 ComputeScreenPos 的参数是剪裁空间下的位置
COMPUTE_EYEDEPTH(o.scrPos.z);//计算顶点摄像机空间的深度：距离裁剪平面的距离，线性变化；

35.6 相交判断
//vertex
o.eyeZ = COMPUTE_EYEDEPTH(o.eyeZ);
o.screenPos = ComputeScreenPos(o.vertex);
//fragment
float halfWidth = _IntersectionWidth / 2;
float diff = saturate(abs(i.eyeZ - screenZ) / halfWidth); //除以halfWidth来控制相交宽度为_IntersectionWidth
fixed4 finalColor = lerp(_IntersectionColor, col, diff);//相交程度越高的越偏_IntersectionColor
return finalColor;

35.7

COMPUTE_EYEDEPTH 计算该片段在视野空间下的z
LinearEyeDepth，可以用在 物体a 需要与场景的 深度值 作比较, 进而判断出是否相交. 此时需要保证 物体a 不会渲染都深度图中, 不然不能做比较.
Linear01Depth，把 深度值 约束在 [0, 1] ,与材质暴露出来的控制值做比较

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
// 仅仅是把模型的深度信息写入深度缓冲中
// 从而剔除模型中被自身遮挡的片元
Pass {
// 开启深度写入
ZWrite On
// 当为0时意味着该Pass不写入任何颜色通道，也就不会输出任何颜色
}

35.8 颜色写入掩码

35.9 深度偏移
Offset 3000,0

36.后处理可直接用uv取深度，其他情况不可以

37.垂直雾
float fogDensity = (worldPos.y - _StartY) / (_EndY - _StartY);
fogDensity = saturate(fogDensity * _FogDensity);
fixed3 finalColor = lerp(_FogColor, col, fogDensity).xyz;

38.边缘检测
38.1 视角和法线点乘
38.2 取当前像素的附近4个角，分别计算出两个对角的深度值差异，将这两个差异值相乘就得到我们判断边缘的值。缺少法线效果不太好
//vertex
//Robers算子 取四个角的uv
o.uv[1] = uv + _MainTex_TexelSize.xy * float2(-1, -1);
o.uv[2] = uv + _MainTex_TexelSize.xy * float2(-1, 1);
o.uv[3] = uv + _MainTex_TexelSize.xy * float2(1, -1);
o.uv[4] = uv + _MainTex_TexelSize.xy * float2(1, 1);

float edge = 1.0;
//对角线的差异相乘
edge *= abs(sample1 - sample4) < _EdgeThreshold ? 1.0 : 0.0;
edge *= abs(sample2 - sample3) < _EdgeThreshold ? 1.0 : 0.0;
return lerp(0, col, edge); //描边

40.ZWrite Off和ZTest Always

ZTest为Off时，表示的是关闭深度测试，等价于取值为Always，而不是Never！
Always是直接将当前像素颜色(不是深度)写进颜色缓冲区中；而Never指的是不要将当前像素颜色写进颜色缓冲区中，相当于消失

41.在TtoW0,TtoW1,TtoW2中存放切线，副切线，法线，坐标点
struct appdata
{
fixed4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed3 normal:NORMAL;
float4 tangent : TANGENT;
};

struct v2f
{
fixed4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 TtoW0 : TEXCOORD1;
float4 TtoW1 : TEXCOORD2;
float4 TtoW2 : TEXCOORD3;
};
v2f vert (appdata v)
{
v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _Albedo);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
//转换矩阵本身也包含信息如：worldpos
//切线到世界坐标的 转换矩阵
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}

42.
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));//视角方向
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//获取指向光源的方向
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));//获取光源射向该物体的方向

43.获得切线坐标下的法线
fixed3 normal = UnpackNormal(tex2D(_Normal, i.uv));

44.前向渲染和延迟渲染的区别

45.Buffer 参考：http://blog.csdn.net/colorapp/article/details/36899341
1. buffer分为frame buffer和render buffer两大类，其中frame buffer相当于render buffer的管理者，frame buffer object即称为FBO，常用于做离屏渲染缓冲等。render buffer则又可分为三类，color buffer / depth buffer / stencil buffer。

c#侧
ComputeBuffer _drawArgsBuffer;
var kernel = _compute.FindKernel("Update");
_compute.SetVector("NoiseOffset", _noiseOffset);
_compute.SetBuffer(kernel, "NormalBuffer", _normalBuffer);

#pragma kernel Update
RWStructuredBuffer<float4> NormalBuffer;

CBUFFER_START(Params)
float3 NoiseOffset;
CBUFFER_END

{
int idx1 = id * 3;
int idx2 = id * 3 + 1;
int idx3 = id * 3 + 2;
//计算
}
RWStructureBuffer定义了可读写的buffer，可以用来把信息传到其它渲染阶段。
numthreds(64,1,1)]定义了线程是如何分配的，如果是(8,8,1)代表每个线程组8个线程，共有8个线程组所以一共也是64个线程。

AppendStructuredBuffer<int> appendBuffer;
ConsumeStructuredBuffer<int> consumeBuffer;
RWStructuredBuffer<int> counter;//计数器

47.判断是否在相机裁截体内
var planes = GeometryUtility.CalculateFrustumPlanes(Camera.main);
var isContain = GeometryUtility.TestPlanesAABB(planes, bounds);

if(isContain)
{
//in frustum..
}

bool IsCanCulling(Transform tran)
{
//必要时候，摄像机的视域体的计算 放置在裁剪判断之外，避免多次坐标变换开销，保证每帧只有一次
Vector3 viewVec = Camera.main.WorldToViewportPoint(tran.position);
var far = Camera.main.farClipPlane ;
var near = Camera.main.nearClipPlane;
if (viewVec.x > 0 && viewVec.x < 1 && viewVec.y > 0 && viewVec.y < 1 && viewVec.z > near && viewVec.z < far)
return false;
else
return true;
}

45.fwidth,ddx，ddy的理解

1.mipmap的核心在选择到底用那一块mipmap的level时，靠的就是偏导数。屏幕空间的贴图UV偏导数过大的时候代表贴图离我们过远，就会选择低等级的mipmap
2.根据顶点求法线
3.锐化

fwidth是相邻两像素的最大偏差值

fwidth是求的偏导数，但因为显卡不知道解析解，所以实际上是用一阶差分替代了偏导数

http://blog.sina.com.cn/s/blog_7cb69c550102xvog.html

46 kernel 滤波
kernel的用处不仅仅是抗锯齿，还可以进行边界检测等等

47 用顶点求出法线（未验证）
normalize( cross(ddy(pos), ddx(pos)))
Unity里面显示法线如下效果：
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = normalize(cross(ddy(IN.worldPos),ddx(IN.worldPos)));
}

48.贴图勾边锐化（对贴图颜色求偏导的例子）
void surf (Input IN, inout SurfaceOutput o)
{
half4 c = tex2D(_MainTex, IN.uv_MainTex);
//c += ddx(c)*2 + ddy(c)*2;这行代码开启和关闭的效果
o.Albedo = c.rgb;
o.Alpha = c.a;
}

49 对uv四舍五入
floor(uv+0.5)

50 抗锯齿
https://blog.csdn.net/candycat1992/article/details/44673819
50.1 Point Style Sampling

vec4 AntiAlias_None(vec2 uv, vec2 texsize) {
return texture2D(iChannel0, uv / texsize, -99999.0);
}

vec4 AntiAliasPointSampleTexture_None(vec2 uv, vec2 texsize) {
return texture2D(iChannel0, (floor(uv+0.5)+0.5) / texsize, -99999.0);
}

vec4 AntiAliasPointSampleTexture_Smoothstep(vec2 uv, vec2 texsize) {
vec2 w=fwidth(uv);
return texture2D(iChannel0, (floor(uv)+0.5+smoothstep(0.5-w,0.5+w,fract(uv))) / texsize, -99999.0);
}

vec4 AntiAliasPointSampleTexture_Linear(vec2 uv, vec2 texsize) {
vec2 w=fwidth(uv);
return texture2D(iChannel0, (floor(uv)+0.5+clamp((fract(uv)-0.5+w)/w,0.,1.)) / texsize, -99999.0);
}

vec4 AntiAliasPointSampleTexture_ModifiedFractal(vec2 uv, vec2 texsize) {
uv.xy -= 0.5;
vec2 w=fwidth(uv);
return texture2D(iChannel0, (floor(uv)+0.5+min(fract(uv)/min(w,1.0),1.0)) / texsize, -99999.0);
}

51.间隔划线，通常用于比较效果
if (floor(uv.x)==split || floor(uv.x)==split*2. || floor(uv.x)==split*3. || floor(uv.x)==split*4.) {
fragColor=vec4(1.); return ;
}

52.旋转uv
// rotate the uv with time
float c=cos(iTime*0.1),s=sin(iTime*0.1);
uv=uv*mat2(c,s,-s,c)*0.05;

53.gpu instance

#pragma multi_compile_instancing
struct appdata
{
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
UNITY_VERTEX_OUTPUT_STEREO//与VR有关，特别是VR的单程立体声渲染。如果未启用VR，则它们不执行任何操作
};
v2f vert(appdata v)
{
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_OUTPUT(v2f, o);//与VR有关，特别是VR的单程立体声渲染。如果未启用VR，则它们不执行任何操作
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);//与VR有关，特别是VR的单程立体声渲染。如果未启用VR，则它们不执行任何操作
}

54.

{
Tags { "RenderType"="Opaque" }

CGPROGRAM

#pragma vertex vert
#pragma fragment frag

struct appdata
{
float4 vertex:POSITION;
fixed2 uv:TEXCOORD0;
};

struct v2f
{
fixed4 pos : SV_POSITION;

};

v2f vert(appdata v)
{
v2f o;

return o;
}

fixed4 frag(v2f o)
{
}

ENDCG
}

56 (-1,1)->(0,1)
/w归一化后范围是(-1,1)
uv = uv*0.5 + 0.5; //(-1, 1)-->(0, 1)

depth = depth*0.5 + 0.5; //(-1, 1)-->(0, 1)
#elif defined (UNITY_REVERSED_Z)
depth = 1 - depth;       //(1, 0)-->(0, 1)
#endif

57.世界法线，反射使用
float3 wolrdN = UnityObjectToWorldNormal(v.normal);
o.RefDir = reflect(-WorldSpaceViewDir(v.vertex), wolrdN);

58.将法线从模型空间转换到观察空间
float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);//法线变换到视角空间

59
o.worldViewDir = _WorldSpaceCameraPos.xyz - worldPos;//得到世界空间的视线方向，顶点指向相机
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);//世界法线

60 albedo和diffuse的区别
albedo是无光贴图,diffuse是albedo加上光照形成的漫反射

61.暗部提亮
diff = (diff * 0.5 + 0.5) * atten;

62.frag中获取光照衰减
float4 frag(v2f i) : SV_Target
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);//阴影值计算

63.

MultiplyPoint，MultiplyVector 用于显示模型的切线和副切线时
Vector3 vertexData = vertexMatrix.MultiplyPoint (vertexs[i]);//MultiplyPoint 通过此矩阵（通用）转换位置。
Vector3 vectorData = vectorMatrix.MultiplyVector(vectors[i]);//MultiplyVector 通过此矩阵变换方向

{
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
base.OnGUI(materialEditor, properties); // 显示默认面板

Material targetMat = materialEditor.target as Material;

bool isEnable = false;

// emission
isEnable = emissionMap.textureValue != null;
if (isEnable)
{
targetMat.EnableKeyword("_EMISSION_ON");
}
else
{
targetMat.DisableKeyword("_EMISSION_ON");
}

EditorGUI.BeginChangeCheck();

if (EditorGUI.EndChangeCheck())
{
else
}
}
}

65.普通的阴影显示
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};

v2f vert (appdata v)
{
return o;
}

fixed4 frag (v2f i) : SV_Target
{

return col;
}

66.CommandBuffer的使用
void OnEnable(){
Append();
}

void OnPreRender(){
if(buffer == null)
Append();
BufferBilt();
}

void Append()
{
buffer = new CommandBuffer();
buffer.name = "Screen Space Subsurface Scattering";
}

void BufferBilt(){
buffer.GetTemporaryRT(maskRT, -1, -1, 24, FilterMode.Bilinear, RenderTextureFormat.ARGB32);
}

void OnDisable(){
GetComponent<Camera>().RemoveCommandBuffer(camEvent, buffer);
}

buffer.GetTemporaryRT的参数
方法第二和三个参数指定了分辨率，-1为默认值，-2为一半大小分辨率，-3为1/3以此类推

67.深度相关
float4 frag (v2f i, out float outDepth : SV_Depth) : SV_Target
{
return float4(0, 0, 0, 0);
}

68.ComputeScreenPos
https://www.jianshu.com/p/df878a386bec
ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,范围是[0, w]

inline float4 ComputeScreenPos (float4 pos)
{
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
o.zw = pos.zw;

return o;
}
o.pos = UnityObjectToClipPos(v.vertex);
o.screenPos = ComputeScreenPos(o.pos);

顶点在变换到齐次坐标后，其x和y分量的范围在[-w, w]。假设目前屏幕的宽度为width，高度为height，那么屏幕坐标的计算方法为：
screenPosX = ((x / w) * 0.5 + 0.5) * width
screenPosY = ((y / w) * 0.5 + 0.5) * height

screenPosX / width = ((x / w) * 0.5 + 0.5)
screenPosY / height = ((y / w) * 0.5 + 0.5)
两边同时乘w：
screenPosX / width * w = ((x * 0.5 + w * 0.5)
screenPosY / height * w = ((y * 0.5 + w * 0.5)

本意是该ComputeScreenPos返回的坐标值用作tex2Dproj指令的参数值，tex2Dproj会在对纹理采样前除以w分量
也可以自己除以w分量后进行采样，但是效率不如内置指令tex2Dproj

69.Camera.projectionMatrix

1. 用透视变换矩阵把顶点从视锥体中变换到裁剪空间的规则观察体（CVV）中
2. 使用CVV进行裁剪
3. 屏幕映射：将经过前述过程得到的坐标映射到屏幕坐标系上。

70.
unity_CameraProjection是相机的投影矩阵，里面的第2行第2个元素存储的就是相机FOV的一半的正切值（tan）

71.[-1,1] => [0,1]
/2.后再＋0.5

+1.后再/2.

scrPos.xy = float2(scrPos.x, scrPos.y) + scrPos.w;

scrPos.xy = float2(scrPos.x + scrPos.w, scrPos.y  + scrPos.w);

73.

UNITY_FOG_COORDS(1)
UNITY_TRANSFER_FOG(o,o.pos);
UNITY_APPLY_FOG(i.fogCoord, col);

74.
UsePass
Name "OUTLINE2PASS2"
UsePass "OUTLINE2PASS2"
Pass内CGPROGRAM外写别名,别名必须大写

76.2×2矩阵的行列式的绝对值是这两个向量组成的平行四边形的面积

struct Triangle {
int2 a, b, c;
int2 ab() { return b - a; }
int2 ac() { return c - a; }
int2 bc() { return c - b; }
//determinant返回矩阵的行列式
half area() { return abs(determinant(half2x2(ab().x, ab().y, ac().x, ac().y))) / 2.0; }
};

Triangle NewTriangle(int2 a, int2 b, int2 c) {
Triangle r; r.a = a; r.b = b, r.c = c;
return r;
}

//点p是否落在a，b，c组成的面内
float PointInTriangle(int2 a, int2 b, int2 c, int2 p) {
Triangle abc = NewTriangle(a, b, c);
half sabc = NewTriangle(a, b, c).area(),
spbc = NewTriangle(p, b, c).area(),
spba = NewTriangle(p, b, a).area(),
spac = NewTriangle(p, a, c).area();
return step(spbc + spba + spac - sabc, 0);
}

79.TRANSFER_VERTEX_TO_FRAGMENT 宏定义一个坑

LIGHTING_COORDS(4,5)  --  初始化光照和阴影
struct v2f

//half4 vertex : SV_POSITION; //这样会报错
half4 pos : SV_POSITION;//应该改为这样
half2 uv : TEXCOORD0;
half3 normal : TEXCOORD1;
half3 viewDir : TEXCOORD2;
half3 lightDir : TEXCOORD3;
LIGHTING_COORDS(4, 5)
｝;

unity的世界变化矩阵最后一列是存的Transform里的Position
float3 center = float3(unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w);
float3 center = float3(unity_ObjectToWorld._m03, unity_ObjectToWorld._m13, unity_ObjectToWorld._m23);
float3 center = mul(unity_ObjectToWorld , float(0,0,0,1)).xyz;
float3 center = unity_ObjectToWorld._14_24_34;

81.
Unity的矩阵转换顺序，缩放→旋转Z→旋转X→旋转Y→平移
My·Mx·Mz * 向量

float4x4( //横着填充
cy * cz + sy * sx * sz,
-cy * sz + sy * sx * cz,
sy * cx,
0, //第一排末端
cx * sz,
cx * cz,
-sx,
0, //第二排末端
-sy * cz + cy * sx * sz,
sy * sz + cy * sx * cz,
cy * cx,
0, //第三排末端
0, 0, 0, 1 //第四排
);

82.instancing相关
https://acgmart.com/unity/ugp1-3/
Instancing相关的宏可以参考：

#pragma instancing_options procedural:setup
有这个定义则之后可以用unity_InstanceID直接取id
否则需要
uint instanceID : SV_InstanceID

通过查看UnityInstancing.hlsl可以看到：
unity_InstanceID = inputInstanceID + unity_BaseInstanceID
使用了Unity封装的宏以后，我们只能通过unity_InstanceID来访问per-instance数据，inputInstanceID来自于SV_InstanceID，那么unity_BaseInstanceID是什么呢？

unity_BaseInstanceID
仅用于在(同material的)多个instanced draw call之间共享instancing array。
通常情况下unity_BaseInstanceID在每次draw call都会重置，因此它不会随帧或时间积累

83.
Properties中
[KeywordEnum(Off, On,Back)] _IsS3("是否开启SSS", Float) = 0
Pass中
#pragma multi_compile _ISS3_OFF _ISS3_ON _ISS3_BACK

[Toggle(S_DEVELOP)] S_DEVELOP("开发者模式", Float) = 0

Properties中
[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 2
Pass中
Cull [_Cull]

https://blog.csdn.net/RandomXM/article/details/88642534
https://blog.csdn.net/cgy56191948/article/details/103703254
#pragma multi_compile  支持定义多个关键字，其中第一个关键字为默认项

#pragma multi_compile __ FOO_ON

multi_compile_fwdbase
multi_compile_fog

将选中RED关键字的prefab打包，加载bundle和其中的prefab，显示了红色，此时改变此材质的keyword为GREEN或者BLUE，没有效果（打包时只会打包当前选中的变体）
#pragma multi_compile RED GREEN BLUE
将选中RED关键字的prefab打包，加载bundle和其中的prefab，显示了红色，此时改变此材质的keyword为GREEN或者BLUE，可以显示绿色和蓝色(打包时会打包所有变体)

Fog和LightMapping模式的变体,如果场景中没有引用不会被打进包里

项目中一般使用：SVC + multi_compile + 依赖打包
收集变体工具：

类似这样，这个代码还不够完整
public int callbackOrder {
get { return 0; } // 可以指定多个处理器之间回调的顺序
}
};
}
for ( int i = data.Count - 1; i >= 0; --i ) {
for ( int j = 0; j < s_uselessKeywords.Length; ++j ) {
if ( data[ i ].shaderKeywordSet.IsEnabled( s_uselessKeywords[ j ] ) ) {
data.RemoveAt( i );
break;
}
}
}
}
}

项目前期美术需定好宏定义使用规范，防止多个美术为了效果过度使用宏定义

总的来说都是求交集，以下链接对着几个情况进行了说明：
运行时修改material中的keywords之后会调用哪个变体
链接：https://blog.csdn.net/RandomXM/article/details/88642534

3.如果需要在代码中开关宏，请使用multi_compile来定义这个宏，以免变体丢失

85.
Material mat
mat.CopyPropertiesFromMaterial(targetMat);

86.Unity贴图的sRGB选线

会自动将输出颜色做一个伽马矫正，偏暗，相当于拍照对图片的自动处理。

Gamma矫正例子：
A和B一样为（127,0,0），C 为（187,0,0）
A 导入选项为 Linear，不勾选 sRGB，B 勾选 sRGB，C勾选 sRGB
unity选linear
最终结果A和C都是(127,0,0),B为(55，0，0)
计算过程
A 贴图为 Linear，采样后不会被硬件 Gamma 2.2 而维持原样，Shader 计算输出到 ColorBuffer 时 Gamma 0.45 提亮，最终输出到显示器再 Gamma 2.2，最终输出颜色（127,0,0）；
B 贴图为 sRGB，采样后被硬件 Gamma 2.2 为 ((127/255)^2.2)*255=55，然后 Shader 中计算输出 ColorBuffer 时 Gamma 0.45 变回到127，最终被显示器 Gamma 2.2，输出颜色（55,0,0）

86.UnityDynamicBatch 动态合批的规则及注意事项
可以简单概括为以下几条：
如果两个物体的scale刚好是呈镜像的，如scale分别为(-1,-1,-1)或(1,1,1)，他们不会被Dynamic Batching。
引用材质实例不同的物体不会被Dynamic Batching，即使两个物体的材质本质上没有任何不同。这句话的理解有点绕，简单举例就是说，相同的材质实例化了两份，分别被A和B引用了，那么A和B是不会被Dynamic Batching的，因为他们引用的是两个不同的实例。
拥用lightmaps的物体将不会被Dynamic Batching，除非他们指向了lightmap的同一部分。
合批之后的单个mesh的顶点数不能超过64K

86.ui相关

UI 合批规则说明

ui优化

87.
step(a,b) b>=a 返回1，否则返回0

88.获取灯光贴图的值
fixed3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv2));

89.没有定义的写法
#ifndef LIGHTMAP_OFF

90.切线，副切线计算
NTBYAttribute GetWorldNormalTangentBitangent(in float3 normal ,in float4 tangent)
{
NTBYAttribute o = (NTBYAttribute)0;
o.normal = UnityObjectToWorldNormal(normal);
o.tangent = normalize(mul(unity_ObjectToWorld, float4(tangent.xyz, 0.0)).xyz);

//v.tangent.w
//值为-1或者1,由DCC软件中的切线自动生成,和顶点的环绕顺序有关。
//unity_WorldTransformParams.w
//模型的Scale值是三维向量，即xyz，当这三个值中有奇数个值为负时（1个或者3个值全为负时），unity_WorldTransformParams.w = -1，否则为1.
//副切线有2条分为2个方向，通过tangentSign去区别是哪一条
half tangentSign = tangent.w * unity_WorldTransformParams.w;
o.bitangent = normalize(cross(o.normal, o.tangent) * tangentSign);
return o;
}

91. 在；号后面加个\

92.
LIGHTING_COORDS在AutoLight.cginc里定义

93.##idx中的##是占位符的意思，idx1,idx2,idx3传入的是数字，如果传入的是1，2，3，则以下代码
#define NORMAL_TANGENT_BITANGENT_COORDS(idx1,idx2,idx3) \
half3 normal : TEXCOORD##idx1;\
half3 tangent : TEXCOORD##idx2;\
half3 bitangent : TEXCOORD##idx3;

half3 normal : TEXCOORD1;\
half3 tangent : TEXCOORD2;\
half3 bitangent : TEXCOORD3;

94.TRANSFER_VERTEX_TO_FRAGMENT(o);

95.相机相关矩阵
Matrix4x4 depthProjectionMatrix = depthCamera.projectionMatrix;
Matrix4x4 depthViewMatrix = depthCamera.worldToCameraMatrix;
Matrix4x4 depthVP = depthProjectionMatrix * depthViewMatrix;
Matrix4x4 depthVPBias = biasMatrix * depthVP;//缩放vp矩阵

96.软阴影 PCF(Percentage Closer Filtering)
https://zhuanlan.zhihu.com/p/45653702

float PCFSample(float depth, float2 uv)
{
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float sampleDepth = DecodeFloatRGBA(col);
}
}
}

EncodeFloatRGBA用32位保存深度，提高了深度精度

97.世界坐标转为光源空间坐标的2种方法
97.1
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(dirLightCamera.projectionMatrix, false);
GL.GetGPUProjectionMatrix来处理不同平台投影矩阵的差异性

97.2

98
#pragma fragmentoption ARB_precision_hint_fastest

99.法线贴图的切线空间下存储发现的方式

inline fixed3 UnpackNormalRG(fixed4 packednormal)
{
fixed3 normal;
normal.xy = packednormal.xy * 2 - 1;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}

100._WorldSpaceLightPos0
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); 用来获取 指向光源的方向
_WorldSpaceLightPos0.w为0，则表示该光源为平行光，_WorldSpaceLightPos0.xyz为此平行光的方向向量。反之_WorldSpaceLightPos0.w不为0，则表示光源为点光源，_WorldSpaceLightPos0.xyz为此点光源

101.smoothstep和lerp,step，clamp
smoothstep(min,max,x):非线性插值，–2*(( x – min )/( max – min ))3 +3*(( x – min )/( max – min ))2
lerp(a,b,f):线性插值函数，返回值为（1-f）*a+b*f
step(a,x):如果x<a返回0；如果x>或=a返回1
clamp(x,a,a):如果x<a返回a；如果x>b返回b；如果在a和b之间就返回x

102.?:运算符，例如以下的test为float3，比较时是用test的xyz分别和0进行比较，得到三个1或0，再与起来作为(test > 0.0f)的结果
float3 test = float3(0,0,0.5);
fixed3 col1 = fixed3(1,1,1);
fixed3 col2 = fixed3(0,0,0);
color = (test > 0.0f) ? col1 : col2;

103.Camera.projectionMatrix

//用逆转置矩阵将平面从世界空间变换到反射相机空间
var viewSpacePlane = reflectionCamera.worldToCameraMatrix.inverse.transpose * plane;
//计算相机和一个平面得出的哦一个斜视锥体裁剪投影矩阵,也就是远裁剪面改为平面,被平面遮挡的部分会被裁减掉
var clipMatrix = reflectionCamera.CalculateObliqueMatrix(viewSpacePlane);
reflectionCamera.projectionMatrix = clipMatrix;

104.表示平面的方程

d = -Dot(N，P0); //原点到平面上任意点在法线上的投影长度

105.矩阵

Pc = Pv(M的逆)的转置
Pv = Pc((M的逆)的转置)的逆

106.推导
https://blog.csdn.net/puppet_master/article/details/54000951

https://blog.csdn.net/puppet_master/article/details/80317178

107.捕捉图像的
GrabPass，使用简单，但不是所有硬件都支持。
CommandBuffer：通过在渲染循环中插入自己渲染内容来实现，
buf.GetTemporaryRT (screenCopyID, -1, -1, 0, FilterMode.Bilinear);
buf.Blit (BuiltinRenderTextureType.CurrentActive, screenCopyID);
buf.Blit (screenCopyID, blurredID);
ReflectProbe：速度快、操作麻烦，要矫正指标需要使用射线和包围盒求交点，Unity中可以用boxProjectCubemapDirection。
后处理：类似扭曲这种不要求精准对像素，直接不拍摄了，放在后处理中来实现

108
#pragma fragmentoption ARB_precision_hint_fastest 用低精度，提升fragment着色器的运行速度
#pragma fragmentoption ARB_precision_hint_nicest 用高精度，可能会降低运行速度，增加时间
_MainTex_TexelSize表示_MainTex这张贴图一个像素/素纹的大小,是一个四元数，是 unity 内置的变量，它的值为 Vector4(1 / width, 1 / height, width, height)，
UNITY_UV_STARTS_AT_TOP
表示纹理的uv坐标原点是在左下还是在左上，Direct3D 和opengl等不同平台的定义不一样，在 Direct3D 中，坐标顶部为零，向下增加。 在 OpenGL 和 OpenGL ES 中，坐标底部为零，向上增加。UNITY_UV_STARTS_AT_TOP 总是1或0，Direct3D类似平台使用1；OpenGL类似平台使用0。
v2f_simple vertBloom (appdata_img v)
{
v2f_simple o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xyxy;
#if UNITY_UV_STARTS_AT_TOP
//如果开启了抗锯齿，则xxx_TexelSize.y 会变成负值
if (_MainTex_TexelSize.y < 0.0)
o.uv.w = 1.0 - o.uv.w;
#endif
return o;
}
OnRenderImage函数
3d渲染已完成，通过source传入
对source的处理，并把结果整合到destination中。这个函数所在的脚本一般绑定在Camera上。此函数只有在Unity Pro版本中才能够使用。
Graphics.Blit函数
static void Blit(Texture source, RenderTexture dest);
static void Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);
static void Blit(Texture source, Material mat, int pass = -1);
RenderTexture.GetTemporary函数 和RenderTexture.ReleaseTemporary函数
GetTemporary获取临时的RenderTexture。ReleaseTemporary用来释放指定的RenderTexture，unity内部对RenderTexture做了池化操作

109.

Dot(vec,vec)

1/(1+Dot(vec,vec))

110.
Unity中的几种内置的渲染队列，按照渲染顺序，从先到后进行排序，队列数越小的，越先渲染，队列数越大的，越后渲染。
Background（1000） 最早被渲染的物体的队列。
AlphaTest   （2450） 有透明通道，需要进行Alpha Test的物体的队列，比在Geomerty中更有效。
Transparent（3000） 半透物体的渲染队列。一般是不写深度的物体，Alpha Blend等的在该队列渲染。
Overlay      （4000） 最后被渲染的物体的队列，一般是覆盖效果，比如镜头光晕，屏幕贴片之类的。

RenderType
Opaque: 用于大多数着色器（法线着色器、自发光着色器、反射着色器以及地形的着色器）。
Transparent:用于半透明着色器（透明着色器、粒子着色器、字体着色器、地形额外通道的着色器）。
TransparentCutout: 蒙皮透明着色器（Transparent Cutout，两个通道的植被着色器）。
Background: 天空盒着色器。
Overlay: GUITexture，镜头光晕，屏幕闪光等效果使用的着色器。
TreeOpaque: 地形引擎中的树皮。
TreeTransparentCutout:  地形引擎中的树叶。
TreeBillboard: 地形引擎中的广告牌树。
Grass: 地形引擎中的草。
GrassBillboard: 地形引擎何中的广告牌草。

Tags { "RenderType"="Opaque" "Queue" = "Geometry+1"}

111.代码设置骨骼的Optimize Game Object

public static string[] NodeNames = new string[]
{
"node_arm",
"node_leg",
};

void OnPreprocessModel()
{
ModelImporter importer = assetImporter as ModelImporter;
importer.optimizeGameObjects = true;
//若实际骨骼中没有以下节点，可能会报错，但不影响使用
importer.extraExposedTransformPaths = EWEquipmentsBase.nodeNames;
}

private static readonly string[] s_BindPoints = new string[]
{
"node_arm",
"node_leg",
};

private Animator m_Animator = null;
private void Awake()
{
m_Animator = GetComponent<Animator>();
Assert.IsNotNull(m_Animator);
foreach (var bindPoint in s_BindPoints)
{
GameObject bindPointObj = new GameObject(bindPoint);
bindPointObj.layer = gameObject.layer;
bindPointObj.transform.SetParent(transform);
}
m_Animator.Rebind();    // important!
}

112

113.
LinearEyeDepth 负责把深度纹理的采样结果转换到视角空间下的深度值，也就是相对于相机的距离
Linear01Depth 则返回一个范围在[0,1]的线性深度值

115
A和B是矩阵，AB的逆，等于 B的逆乘以A的逆

116.[Delayed]特性

117.模版测试参数（没有通过测试的虽然不绘制，但会有overDraw）

Stencil
{
Ref referenceValue //参考值
Comp comparisonFunction  //比较函数，关键字有，Greater（>），GEqual（>=），Less（<），LEqual（<=），Equal（=），NotEqual（!=），Always（总是满足），Never（总是不满足）
Pass stencilOperation  //条件满足后的处理
Fail stencilOperation  //条件不满足后的处理
ZFail stencilOperation  //深度测试失败后的处理
}

Comp Always：模板测试始终通过
Pass Replace：将参考值赋值给缓冲值

119.AO
AO时由于表面凹下去的地方，在光照时受到面片的遮挡，导致颜色压暗
AO只对间接光产生影响，不影响直接光否则整体会被压暗

120.参数默认值
Zwirte：是否开启深度缓存，默认为On。Zwrite On时会开启深度缓存和深度测试
Ztest：深度测试的模式，默认为Lequal（小于等于）。
Queue：渲染队列，默认为Geometry
Cull：默认是On的，也就是只渲单面，要渲双面需要Cull Off

121.

122.植被优化
1是关闭双面渲染，Cull默认就是单面渲染，也就是不加Cull关键词

Name "EarlyZ"
Tags { "LightMode" = "ForwardBase" }
ZWrite On
ZTest LEqual

Name "FORWARD"
Tags { "LightMode" = "ForwardBase" }
ZWrite Off
ZTest Equal

123.将切线空间法线图采用得到的值 转为 世界空间法线
//切线，法线，副切线放入矩阵中
float3x3  GetNormalTranform(in float3 wNormalDir, in float3 wTangentDir,in float3 wBitangentDir)
{
return   float3x3(wTangentDir, wBitangentDir, wNormalDir);;
}

//传入的packednormal是从切线空间下的法线图中采用得到的值
inline fixed3 UnpackNormalRG(fixed4 packednormal)
{
fixed3 normal;
normal.xy = packednormal.xy * 2 - 1;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}

fixed3 worldNormal = o.normal;
fixed3 worldTangent = UnityObjectToWorldDir( v.tangent.xyz );
//这一句是为了解决建模时法线镜像的问题(也就是有时给模型加光照时，中间有条缝隙的问题)
//模型的左右手系存储在v.tangent.w里，unity的左右手系存储在unity_WorldTransformParams.w里,两者都需要考虑
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
fixed3 worldBinormal = cross( worldNormal, worldTangent ) * tangentSign;

o.T2WRow0 = float3( worldTangent.x, worldBinormal.x, worldNormal.x); // * tNormal.xyz
o.T2WRow1 = float3( worldTangent.y, worldBinormal.y, worldNormal.y);
o.T2WRow2 = float3( worldTangent.z, worldBinormal.z, worldNormal.z);

half3 _normal_val = UnpackNormalRG(e);
//如果有normal的缩放，也只能对xy缩放，只改变斜率
//half3 _normal_val = UnpackScaleNormal(e,_BumpScale);

float3x3 tangentTransform = GetNormalTranform(i.worldNormal, i.worldTangent, i.worldBinormal);
half3 normal = normalize(mul(_normal_val, tangentTransform));

o.tangent = float3( worldTangent.x,  worldTangent.y,  worldTangent.z);
o.binormal = float3( worldBinormal.x, worldBinormal.y, worldBinormal.z);
normalVal = i.tangent * normalVal.x + i.binormal * normalVal.y + i.normal * normalVal.z ;
o.Normal = normalize(normalVal);

124.切线空间视野方向
float3 objBinormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
float3x3 objectToTangent = float3x3(v.tangent.xyz,objBinormal,v.normal);
o.tangentViewDir = mul(objectToTangent, ObjSpaceViewDir(v.vertex));
//float3x3的排布如下
// v.tangent.xyz,
// objBinormal,   * dir
// v.normal

125.a是邻边，b是斜边，此处为b投影到a上的投影向量长度
dot(a,b);

126.去色
float3 desaturateIceColor = dot( iceCol, float3( 0.299, 0.587, 0.114 ));

127.视差贴图的uv计算
void DoParallaxMap(inout v2f i){
#if defined (_PARALLAX_MAP)
//切线空间视野方向
i.tangentViewDir = normalize(i.tangentViewDir);
//除以z来矫正uv偏移的向量，+0.41是防止分母太小，值太大，导致图像不正确
//除以z得到的是视线方向和地平面的交点线段的xy
i.tangentViewDir.xy /=(i.tangentViewDir.z + 0.41);
//_Parallax是视差值的缩放
i.tangentViewDir.xy *= _Parallax;
i.uv.xy +=  (i.tangentViewDir.xy /(i.tangentViewDir.z + 0.42) ) * height;

//以上4句与unity内置的ParallaxOffset等价
//float2 offset = ParallaxOffset(height,_Parallax,i.tangentViewDir);
//i.uv.xy += offset;
#endif
}

128.

Tags {"LightMode"="ForwardBase"}

v2f
vert
frag

.杂
WorldSpaceViewDir(模型空間坐标)   返回世界坐标指向相机坐标的向量
UnityWorldSpaceViewDir(世界空間坐标)   返回世界坐标指向相机坐标的向量

//正确混合两个法线混合应该是斜率相加（凹凸感不变），而不是直接相加（凹凸感变弱）
float3 n1 = UnpackNormal( tex2D(_BumpMap,i.uv));
float3 n2 = UnpackNormal( tex2D(_BumpMap,i.uv * 64));
o.Normal = BlendNormals(n1,n2);//等价于BlendNormals normalize(half3(n1.xy + n2.xy.n1.z*n2.z));

float3 n1 = UnpackNormal( tex2D(_BumpMap,i.uv));

float visible = texture(VisibleTexture, index).x;
gl_Vertex.z = (visible > threshold) ? 9999 : gl_Vertex.z;

float visible = texture(VisibleTexture, index).x;
visible = sign(visible - threshold) * .5 + .5; // 1=visible, 0=invisible
gl_Vertex.z += 9999 * visible; // original value only for visible

float distance = length(_WorldSpaceLightPos0.xyz - i.worldPosition.xyz);
atten = 1.0/distance;

----------------

o.vertex = UnityObjectToClipPos(v.vertex);
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldNormal = UnityObjectToWorldDir(v.normal);
o.worldViewDir = UnityWorldSpaceViewDir(worldPos);
//齐次坐标系下的屏幕坐标值,范围是[0, w]
//ComputeScreenPos函数，得到归一化前的视口坐标xy
//z分量为裁剪空间的z值，范围[-Near,Far]
o.screenPos = ComputeScreenPos(o.vertex);
//COMPUTE_EYEDEPTH函数，将z分量范围[-Near,Far]转换为[Near,Far]
COMPUTE_EYEDEPTH(o.eyeZ);
o.uv = TRANSFORM_TEX(v.uv,_MainTex);
saturate(dot(worldNormal, worldViewDir))
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));

float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldViewDir = WorldSpaceViewDir(v.vertex);
o.reflectionDir = reflect(-worldViewDir, worldNormal);

struct v2f
{
float4 uv : TEXCOORD0;//xy为主纹理uv，zw为法线纹理uv
float4 TtoW0 : TEXCOORD1;
float4 TtoW1 : TEXCOORD2;
float4 TtoW2 : TEXCOORD3;
}
v2f vert (appdata_full v)
{
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
}

1.
float2 depth_uv = float2(i.uv_MainTex.x, 1-i.uv_MainTex.y);  或者  o.depth_uv = TRANSFORM_TEX(v.uv, _MainTex);

2.float zx = SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, float4(depth_uv, 0, 0));
zx = Linear01Depth(zx);

3.v2f vert(appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.screen = ComputeScreenPos(o.pos);
COMPUTE_EYEDEPTH(o.screen.z);
o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
d2 = LinearEyeDepth(d2);

4.取光源空间下的深度
float4 lightPos = mul(unity_WorldToShadow[0], float4(worldPos, 1));
float depth = lightPos.z ;

5.
eyeDepth = LinearEyeDepth(eyeDepth);

//指向光源
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos1));
//指向相机
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos1));

//从世界空间转到光源空间得矩阵
// GL.GetGPUProjectionMatrix是处理多平台得问题得，api描述是：从Camera的投影矩阵得到GPU（图形处理器）的投影矩阵（该函数在跨平台是有用）
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(dirLightCamera.projectionMatrix, false);

//uv.xy * scaleAndOffset.xy + scaleAndOffset.zw;

//uv y轴反向
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0.0)
o.uv.y = 1.0 - o.uv.y;
#endif

//UNITY_SAMPLE_DEPTH返回的值范围是[0,1]
float z = -lerp(near, far, depth);

//屏幕坐标和ndc坐标计算
o.vertex = UnityObjectToClipPos(v.vertex);
o.screenPos = ComputeScreenPos(o.vertex);
float4 ndcPos = (o.screenPos / o.screenPos.w) * 2 - 1;

// x = 1 or -1 (-1 if projection is flipped)
// y = near plane
// z = far plane
// w = 1/far plane
float4 _ProjectionParams;

// x = 1-far/near
// y = far/near
// z = x/far
// w = y/far
// or in case of a reversed depth buffer (UNITY_REVERSED_Z is 1)
// x = -1+far/near
// y = 1
// z = x/far
// w = 1/far
float4 _ZBufferParams;

[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 0
Cull [_Cull]

o.lightDir = WorldSpaceLightDir( v.vertex );
o.viewDir = WorldSpaceViewDir( v.vertex );
o.tangentWorld = UnityObjectToWorldDir(v.tangent.xyz);

04-01 1385
08-02 802
01-27 162
09-17 1029
01-20 686
01-30 73