01-网格合并
- 使用gameobject的static属性设置
注意的是,所谓静态就是静止的,你如果要改变它位置,就不是静态了,静态物体在脚本加载的时候就开始渲染,不随着update 每侦更新,你如果要移动物体,就必然要重新刷新,就是动态了 。
所有被勾选了“Static”的GameObject,其中的Mesh Filter中的mesh都会被合并到 "Combined Mesh (root: scene)" 中。
- 使用代码合并
合并网格有利于性能最优化。如果mergeSubMeshes为true,所有的网格会被结合成一个单个子网格。否则每一个网格都将变成单个不同的子网格。如果所有的网格共享同一种材质,设定它为真。如果useMatrices为false,在CombineInstance结构中的变换矩阵将被忽略。
using UnityEngine;
public class MashTest : MonoBehaviour {
void Start () {
MeshFilter[] meshfies = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshfies.Length];
int i = 0;
while (i < meshfies.Length)
{
combine[i].mesh = meshfies[i].sharedMesh;
combine[i].transform = meshfies[i].transform.localToWorldMatrix;
meshfies[i].gameObject.SetActive(false);
i++;
}
transform.GetComponent<MeshFilter>().mesh = new Mesh();
transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
transform.gameObject.SetActive(true);
}
}
using UnityEditor;
using UnityEngine;
public class MashTool : MonoBehaviour {
[MenuItem("Tools/Save Combine Mesh")]
public static void SaveMesh()
{
Mesh m = Selection.activeGameObject.GetComponent<MeshFilter>().sharedMesh;
AssetDatabase.CreateAsset(m, "Assets/Test/cmbMesh.asset");
AssetDatabase.SaveAssets();
}
}
使用Linear还是Gamma
概述
线性渲染就是渲染场景所有输入都是线性的。一般来说存在的纹理都是经过Gamma矫正了的,也就是说当纹理被采样到一个材质上时,颜色值已经不是线性的了。如果这些纹理用通常的计算方式去计算光照和图片效果,在非线性空间计算,这将导致轻微的偏差。(而这种偏差就是需要Gamma矫正的原因)
线性渲染保证了在shader中输入与输出都是在正确的颜色空间得出更正确的结果。
Gamma矫正
所谓伽玛校正就是对图像的伽玛曲线进行编辑,以对图像进行非线性色调编辑的方法,检出图像信号中的深色部分和浅色部分,并使两者比例增大,从而提高图像对比度效果。计算机绘图领域惯以此屏幕输出电压与对应亮度的转换关系曲线,称为伽玛曲线(Gamma Curve)。以传统CRT(Cathode Ray Tube)屏幕的特性而言,它的输入电压和显示出来的亮度关系不是线性的,而是一个类似幂律(pow-law)曲线的关系,而这个关系又恰好跟人眼对光的敏感度是相反的。这个巧合意味着,虽然CRT显示关系是非线性的,但对人类来说感知上很可能是一致的。
Gamma 校正补偿了不同输出设备存在的颜色显示差异,从而使图像在不同的监视器上呈现出相同的效果。
保存颜色信息本身矫正称为encoding gamma,显示器对颜色的矫正称为display gamma,所以一个完整的图形系统中需要两个Gamma值,两次矫正刚好在一定程度上抵消(但一般不是完全抵消)。
Gamma管线(Gamma Pipeline)
在Gamma渲染管线中,所有颜色和纹理在Gamma空间被采样,在shader应用了之后前,不会对shader输入做任何处理。即使这些值是在Gamma空间中的,所有shader计算对待他们输入都当做在线性空间,此外,当把shader输出写到内存中时,没有对最终像素应用Gamma矫正。很多时候是可以接受两次偏差一定程度上相互抵消。但是这是不正确的。
两种情况:
线性输入
输入颜色值在线性空间下,而在shader中按照线性空间下的计算,这些都是正确的,但最终输出的时候也没有做任何处理(主要Gamma矫正),所以在屏幕显示时,屏幕进行了一次display gamma,这样的转换后得到非预期的亮度,通常表现为整个场景比较昏暗。非线性输入
输入颜色值在非线性空间下(通常表现为纹理),而在shader中把该值当成是线性空间下计算的(产生了偏差),这是不正确的,在最终输出的时候也没有做任何处理,但在屏幕显示时,进行了display gamma,虽然这样产生两次偏差会在一定程度上进行抵消,但这样的抵消是不正确的。
为了直接显示时可以正确显示,大多数图像文件都进行了提前的校正,即已经使用了一个encoding gamma对像素值编码。
线性管线(Linear Pipeline)
如果开启了线性渲染(Linear Rendering),Unity会背地里把输入纹理设置为sRGB模式,这种模式下硬件在对纹理进行采样时会自动将其转换到线性空间中;并且,也会设置一个sRGB格式的buffer,此时GPU会在shader写入color buffer前自动进行伽马校正。如果此时开启了混合(像我们之前的那样),在每次混合是,之前buffer中存储的颜色值会先重新转换回线性空间中,然后再进行混合,完成后再进行伽马校正,最后把校正后的混合结果写入color buffer中。这里需要注意,Alpha通道是不会参与伽马校正的。
sRGB模式是在近代的GPU上才有的东西。如果不支持sRGB,我们就需要自己在shader中进行伽马校正。
对非线性输入纹理的校正通常代码如下:
float3 diffuseCol = pow(tex2D(diffTex, texCoord), 2.2);
在最后输出前,对输出像素值的校正代码通常长下面这样:
fragColor.rgb = pow(fragColor.rgb, 1.0/2.2);
return fragColor;
但是,手工对输出像素进行伽马校正在使用混合的时候会出现问题。这是因为,校正后导致写入color buffer的颜色是非线性的,这样混合就发生在非线性空间中。一种解决方法时,在中间计算时不要对输出进行伽马校正,在最后进行一个屏幕后处理操作对最后的输出进行伽马校正,但很显然这会造成性能问题。 还有一些细节问题,例如在进行屏幕后处理的时候,要小心我们目前正在处理的图像到底是不是已经伽马校正后的。
总之,一切工作都是为了“保证所有的输入都转换到线性空间,并在线性空间下做各种光照计算,最后的输出(最最最最后的输出)进行伽马校正后再显示”。
Linear Rendering 和 Gamma Rendering的区别
Linear Rendering就是在shader中所有计算会在线性空间下进行,Gamma Rendering就是在shader中不进行转换到线性空间下,直接计算。然就是计算方程式不同,也就意味例如光照表面会有不同的响应曲线和图片效果,表现不相同。
-
Light Falloff
光照表现一般受光源的距离和法线两个因素影响(在同等光强下)。首先当我们用Linear Rendering时,执行Gamma矫正将会使光照范围变大。第二种会使边缘模糊,分不清界限。这更准确的表现了表面光照强度下降。
- 表面响应强度
随着光强的增加,非线性方式计算的表面会更亮一些。这导致了光照在表面很多地方曝光过度,而且给场景模型一个褪色(变白色了)的感觉。当你用线性渲染时,表面颜色仍然随着光照强度线性增加的,这样就使表面材质和颜色更接近现实
- 混合
混合是在帧缓冲区发生的,当使用Gamma Rendering,这表示颜色之间混合是在非线性空间下计算的。然而这是不正确的。
下图在Linear Space中混合结果,颜色之间过度不是很明显。
下图在Gamma Space中混合结果,颜色交界处出现了明显的其它颜色,颜色更亮,出现褪色的现象。
- Mipmaps
计算纹理Mipmap是种线性计算,需要对某个方形区域内像素取平均值,如果纹理存储在非线性空间,那么计算时也是在非线性空间里计算,这样就会得到错误的结果。正确的做法是先转换到线性空间在计算mipmap。
- Lightmapping
切换linear 和gamma方式,需要重新烘焙相关的Lightmapping。