粒子系统
粒子特效 打包成 AB 包后 Mesh 无法正常显示
Meshes must be read/write enabled to work on the Particle System.
If you assign them in the Editor, Unity handles this for you.
But if you want to assign different meshes at run time, you need to check the Read/Write Enabled setting in the Import Settings.
当需要重新指派 Mesh 时,需要打开 Read/Write 的设置,但是 UnityEditor 会自动执行这个操作.
****注意:****1.如果是 AB 包的话,UnityEditor 并不会执行这个操作.需要在打包前设置 FBX.2.打应用包时,也会出现相同的情况.

粒子的 GPUInstancing 只合并粒子系统本身的,不会合并所有粒子系统的,即使是同一个材质球,并且粒子系统需要走 pragma instancing_options procedural:ParticleInstancingSetup
https://docs.unity3d.com/cn/2019.4/Manual/PartSysInstancing.html
在粒子系统的 renderer上要 Enable Mesh GPU Instancing 。

总之粒子系统的 GPUInstancing 和其他的不相同,切记
动画与骨骼
- 动画
transform和SkinnedMeshRenderer分开时,如果动画不动,一般是骨骼没绑对 。需要绑定bones和rootbone -
rootbone一般表示模型的float3(0,0,0)位置,更改rootbone一般不会影响动画、顶点位置法线这些。 -
SkinnedMeshRenderer一般留意bones,rootbone,bindPose以及boneWeights
Shader
编码注意事项
-
Shader中属性非必要不要写float,只有一些涉及坐标计算\深度的时候才需要的 - 单纯一个
if,≈ 4 个指令消耗。但是如果分支里有大量逻辑代码,执行数量就 == 双倍。如果单条件分支,就没啥消耗了 -
shader中ifelse,带有采样的时候,虽然某个分支可能没有采样,但是申请的寄存器会多一些(GPU中可能不知道哪里用到,就都申请了) -
UNITY_UNROLL只可用在常量上,非常量消耗更大 -
Shader中使用了UsePass在真机上SRP Batcher可能会出问题,Batcher失败。留意下 -
Shader中少用矩阵、SinCos这些函数 -
shader中CBuffer这种传float数组,最好也按float4/Vector来传,用float/int数组有问题
矩阵与坐标变换
-
向量矩阵这些相乘的时候,位移需不需要也要留意,想法线方向这些,直接拿
float3来乘就好,不要带位移 -
TransformObjectToWorldNormal的原理是什么?查看其源代码,它根据是否定义了
UNITY_ASSUME_UNIFORM_SCALING宏而使用两种方法之一。当定义时,它调用TransformObjectToWorldDir,忽略平移部分(因为处理的是方向向量),但向量会均匀缩放,需要归一化。不假定均匀缩放时,法线向量必须反向缩放,需要乘以模型转置矩阵UNITY_MATRIX_I_M再归一化。可通过#pragma instancing_options assumeuniformscaling启用优化。 -
unity_WorldTransformParams.w在三个坐标轴存在奇数个负缩放时为 -1 否则为 +1,因为在存在奇数个负缩放时,左右手系会发生转换,需要进行修正。 -
UNITY_MATRIX_M比ObjectToWorld要好些,在GPU Instancing中,可能赋给UNITY_MATRIX_M的值是对的,而ObjectToWorld可能只针对单个物体 -
UNITY_MATRIX_V[2].xyz : 表示相机的朝向,即 C# 代码里的transform.forward
渲染状态与混合
-
Cull Back剔除背面的几何形状,判断标准是三角形的反面,跟摄像机正对的是没关系的。反之Cull Front也一样,Unity 中一般是顺时针的 -
Shader中Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }里面的Queue只是CPU为了渲染顺序进行区分的。至于混合Blend在那个队列都可以使用,一般在Transparent里面使用。这个是GPU操作的,是和颜色缓冲区挂钩。 -
多个像素深度相同的时候,使用
Blend混合的时候,同一个点多个像素值混合会出现问题。这个时候可以使用Stencil模板,让一个像素通过,其他像素不通过,保证blend不出问题 -
shader中Clip不可以借用ifelse报错,得使用keyword来//错误示例 half4 frag_outline(v2f i):SV_Target { if(_MainTexAlphaCutOff) { half4 MainTexture = tex2D(_MainTex, i.uv0.xy); // 不要在if中采样贴图 clip(CutOff - MainTexture.a); // 不可以在if中执行Clip } } -
Shader中模板缓冲以及深度缓冲区的值都是在模板测试和深度测试都结束之后才赋值的 -
Shader中渲染顺序很重要,有些情况不对,注意下渲染的顺序 -
Shader中渲染时,两个对象分别有 A、B pass 时,是先画对象 1 的 A pass,再画对象 1 的 B pass,然后再画对象 2 的 -
OpenGL 没有采样器的概念,完全读图的配置
DX 和 Vulkan 有采样器,数量限制 16,可以根据采样器点采样或者双线性采样
-
不用看 Mesh 什么样的,多大,其实看的还是转到裁剪空间下的占多少,这个才是最终画的大小。采样贴图消耗,实际也是看采样 UV 的范围。
深度与阴影
-
Wclip = -Zview裁剪空间下W == -视图空间下Z -
DDX和DDY可以重建法线,但是是非平滑的。float3 normal = normalize(cross(ddy(i.objPos), ddx(i.objPos))); - 正交相机下视图空间
z就是线性深度,w是 1;透视相机下视图空间z就是非线性深度,w就是线性深度,z/w就是去采样深度图的那个 - Unity 建议正常不透 2000,
AlphaTest 2450,半透 3000,半透的时候可以关闭ZWrite。但是 3000 队列的会从后往前画,有可能会人物重叠产生OverDraw - 解决阴影渗漏(痤疮
Acne)使用深度偏差
纹理采样与颜色空间
-
在顶点着色器中采样贴图
_NoiseMap.SampleLevel(sampler_NoiseMap, noiseuv, 0)。不能使用tex2D,原因是GPU计算贴图时需要使用适当的mipmap,mip级别的选择需要经过片段着色器阶段才能获取,旧着色器需要使用tex2Dlod才能在顶点着色器中采样 -
_Color("Color", Color) = (1, 1, 1, 1)颜色空间说明:空间 属性 行为 Gamma _Color输入啥就是啥 Linear _Color会 Pow(2.2)Linear [Gamma]_Color会 Pow(2.2)Linear [Linear]_RimCol没用,仍会 Pow(2.2)Linear [HDR]_Color是线性的,输入啥就是啥 Linear [HDR][Gamma]_Color会 Pow(2.2)

-
贴图里面的颜色和 Unity 里面取出来的值不一定是一样的,可能你数组定义颜色
0.5f存到png里面有可能会转的,就GetPixel、SetPixel根据颜色空间或者啥的给转了 -
正常美术出图单独的只存在法线信息的法线贴图,使用 Type 为
Default且sRGB和使用Normal Map是一样。
UI 渲染
-
UI中的color会存在Vertex Color中,包括Text、Image这些,这么做是为了合批。在 Unity 2022 高版本中 Canvas 上存在选项Vertex Color Always In Gamma Color Space - UI 中画模型的情况,
sortingOrder的优先级最高(越小的越先画),同等SortingOrder情况下看RenderQueue的大小(越小的越先画),然后再看SortingCriteria criteria = SortingCriteria.CanvasOrder | SortingCriteria.CommonTransparent;
CommandBuffer 与渲染管线
depthDesc.graphicsFormat = GraphicsFormat.None 只写深度,省一半消耗,因为颜色缓冲清除了。具体在 PC 还是安卓忘记了
// 绘制全屏mesh
cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
cmd.DrawMesh(CoreUtils.fullscreenMesh, Matrix4x4.identity, material, 0, pass);
cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix); // 恢复相机矩阵
CommitCommandBuffer(cmd);
cmd.SetRenderTarget的 RenderBufferLoadAction表明加载进这个 RT 之后,需不需要留意里面的内容,在这之后才是 clearDepth、clearColor 也就是清除里面的内容。所以不能瞎填
cmd.SetRenderTarget(_LowResTransparentHandleTexture.Identifier(), RenderBufferLoadAction.Load, RenderBufferStoreAction.Store);
cmd.ClearRenderTarget(false, true, Color.clear);
renderer.CommitCommandBuffer(cmd);
cmd.Blit相当于切了一次 RT
深度缓冲从一个 RT 切换到另一个 RT 的时候,RT 大小不等时,即 cmd.SetRenderTarget使用了 Load数据也是不对的,需要在 shader Blit 的时候设置 SV_Depth 啥的去设置,这还不如直接使用深度图
同一个 CommandBuffer内连续 DrawMesh/Blit/SetGlobal/SetRenderTarget等操作不需要中途调用 context.ExecuteCommandBuffer(cmd);但如果后续要调用 ScriptableRenderContext.DrawXXX系列 API,则需要先 context.ExecuteCommandBuffer(cmd)且 cmd.Clear()
cmd.SetRenderTarget使用 RenderBufferLoadAction.DontCare之后,cmd.ClearRenderTarget是否需要清除 color,可以根据 shader 里面混合模式以及有无 Clip 等操作判断。一般全屏后效,像素都能覆盖到,默认 One,Zero 混合可以考虑不清除
Y 翻转相关:
① 写入阶段:只要使用 Unity 构造的投影矩阵(GL.GetGPUProjectionMatrix / SRP 内置矩阵),Unity 会在生成矩阵时自动加入平台所需的 Y 翻转,因此渲染到 RT 或屏幕时方向统一;但如果你绕开它(手写投影矩阵、直接用 Matrix4x4.Perspective、手动做 NDC→clip→screen、或自定义 SRP 不用 Unity 的矩阵),就必须自己处理翻转。
② 上屏阶段:当 RT → 最终 CameraTarget时,Unity 会根据平台原点差异自动在 UV 层面做 Y 翻转(_ProjectionParams、UNITY_UV_STARTS_AT_TOP等),确保最终显示方向一致。注意 SceneView是上屏到 RT,是不会处理的;GameView是 CameraTarget 会处理的。