在图形API中有:OpenGL
,DX
,Vulkan
等。Unity为了处理不同平台使用的Shader语言的差异,就用ShaderLab封装起来,最后根据不同平台编译成对应的着色器语言。
UnityShader 结构图:
官网介绍
在上面的结构图最底下的Variant就是变体部分,如下所示frag中有三个判断分支,Unity就会产生多个Shader变体。如果变体数量太多就会增加内存消耗,一般ShaderLab占用内存在30-70M左右,如果超出太多就要考虑优化变体数量。
fixed4 frag (v2f i) : SV_Target
{
#if _OVERLAY_1
col += secCol;
#elif _OVERLAY_2
col *= secCol;
#endif
col *= _OtherCol;
return col;
}
主要是在Shader中使用了:#pragma shader_feature
和#pragma multi_compile
起到宏的作用。不一样的是 shader_feature
没有用到的不会被包含进去, 而 multi_compile
全部版本都会被包含,因此 shader_feature
适用于那些会在材质面板中直接设置的情况,但在脚本里通过DisableKeyword 和 EnableKeyword 来开启或关闭 keyword 的话就要使用 multi_compile
。
Case1
#pragma multi_compile A B C
#pragma multi_compile D E
如果shader中使用了以上两个multi_compile那么不管后面是否使用到宏(A,B,C,D,E)
它都会产生变体,这样计算的方法是(A,B,C)(D,E) => 32=>6。
Case2
#pragma multi_compile A B C
#pragma shader_feature D E
这里有两个shader_feature,当他们启用才会产生变体因此有以下几种组合情况:
(A+D,B+D,C+D) ,(A+E,B+E,C+E),(A+D,B+D,C+D,A+E,B+E,C+E)
Case3
#pragma multi_compile A B C
#pragma shader_feature _ D E
这里的(’_’)表示空,当D或E启用时会产生:(A,B,C,A+D,B+D,C+D) 或 (A,B,C,A+E,B+E,C+E)
Unity在编辑器里提供了变体查看的方法,我们在shader中添加#pragma shader_feature_local CS_BOOL
为例演示如何查看:
1.选中shader,在inspector面板中有个Keywords
,它里面包含了全局和本地的所有keword。
2.点击inspector上的Compiled code
右边的倒三角,再点击弹出框内的Show
按钮即可看到当前shader用到的Keyword。
1.shader内剔除
我们可以在shader中使用#pragma skip_variants
来剔除不想要的Keyword变体。如下所示在shader中添加#pragma skip_variants CS_BOOL
后就可以屏蔽该Keyword。
2.Build时剔除(只能用于Global )
使用以下方法剔除时发现无法把Local类型的keyword剔除掉,如果有哪位晓得方法麻烦留言指导下哈,非常感谢。
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Build;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
namespace ZYF
{
///
/// 在Build时剔除shader keyword
///
public class ZYF_Test_ShaderKeywordStripping : IPreprocessShaders
{
public int callbackOrder => 0;
//要剔除的keyword
ShaderKeyword cs_bool_keyword;
public ZYF_Test_ShaderKeywordStripping()
{
cs_bool_keyword = new ShaderKeyword("CS_BOOL");
}
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
List<ShaderCompilerData> strippingList = new List<ShaderCompilerData>();
//收集要剔除的data
for (int i = 0, length = data.Count; i < length; i++)
{
if (data[i].shaderKeywordSet.IsEnabled(cs_bool_keyword))
{
strippingList.Add(data[i]);
}
}
//剔除收集到的data
strippingList.ForEach(d =>
{
data.Remove(d);
});
}
}
}
使用ShaderVariantCollection预加载指定Shader
官方说明
ShaderVariantCollection里记录着每个shader里真正使用到的变体,它可以用来预加载shader。
1.首先进入运行模式并让摄像机看到整个场景,然后到ProjectSettings/Graphics
的最下方点击Save to asset
保存。
2.把保存的SVC文件放到Project Settings/Graphics 中的Preloaded Shaders中,这样就能预加载shader了,当然你可以按需求修改SVC内容。
3.还可以在代码中进行预加载(因为预加载比较耗时最好放在加载场景中执行):Shader.WarmupAllShaders
(预加载所有shader) 和ShaderVariantCollection.Warmup
(加载指定SVC中的shader)。
书籍github
每个阶段也是一个流水线系统。
应用阶段:输出渲染所需的几何信息即渲染图元(点线面)
;
几何阶段:把顶点坐标变换到屏幕空间中,输出屏幕空间二维顶点坐标、每个顶点对应的深度值、着色等相关信息;
光栅化阶段:在GPU上运行,决定哪些像素被绘制,对逐顶点数据(纹理坐标、顶点颜色等)进行插值,然后逐像素处理。
概念阶段的几何、光栅化两个阶段开发者无法拥有绝对的控制权,其实现的载体是GPU.
顶点着色器:每个顶点都会调用一次顶点着色器,顶点与顶点之间相互独立。主要工作是坐标变换
、逐顶点光照
。坐标变换把顶点坐标从模型空间转换到齐次裁剪空间(通过MVP变换坐标到-1~1)