深入URP之Shader篇15: Shader关键字和变体

2024-02-22 15:20

本文主要是介绍深入URP之Shader篇15: Shader关键字和变体,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前说了很多shader关键字的事情,本篇好好说一下关键字和变体。

关键字是干什么的

我们写shader的时候,经常会遇到需要处理不同的情况,比如是否启用雾,光源是平行光还是点光源,是否使用法线贴图等等。如果为每一种情况都写一个单独的shader,那么这些不同的条件就会组合出非常多的可能性,要写太多的shader显然不现实。那么另外一种方法就是像写程序那样在shader里面进行逻辑判断,如果直接用if就属于动态分支了,对效率有一定的影响,另外就是像c语言预处理宏那样使用#if#ifdef这类,而此时的条件就是关键字。编译器会对关键字进行预处理,从而产生匹配当前所使用的关键字的shader版本,比如开启雾,使用平行光但不使用法线贴图的shader版本。

什么是变体

每种关键字的组合对应的shader版本就是一个shader变体。所谓变体,就是同一份shader源码,由于启用了不同的关键字组合,经过编译器预处理就得到了最终不一样的shader代码。

使用关键字还是使用动态分支

使用关键字可以避免动态分支,从单个shader的效率来说是最高的。但是使用关键字会造成变体增多,这就意味着GPU需要切换更多的变体来完成渲染。之前我们讨论的SRP Batcher的原理就是只要变体不切换,可以高效的重新绑定CBuffer完成draw call,如果变体切换了只能调用一次set pass call,这就打断了SRP Batcher。根据Unity的建议,尽量减少变体,让SRP Batcher包含的draw call数目尽量多是首选。当然了,按照我自己的经验,如果动态分支实在太费,比如会增加贴图的采样次数,或者非常复杂的计算,且这个分支在一个warp中是不可能一致的,那么就还是用关键字来代替动态分支吧。当然最靠谱的是需要经过profile来决定。

声明shader keywords

有两种声明关键字的指令

#pragma multi_compile

  • 声明一组关键字,比如
#pragma multi_compile QUALITY_LOW QUALITY_MEDIUM QUALITY_HIGH QUALITY_ULTRA
  • 默认情况下关键字是全局作用域的(即针对所有的shader)
  • 并且影响所有的shader stage(如VS, FS)
  • 构建系统会包含该组中所有关键字,例如#pragma multi_compile a b c,会分别编译出包含定义了a,b和c的shader变体。

#pragma shader_feature

multi_compile有两点不同:

  • 构建系统只会包含该组中被使用的关键字。比如我们可以将shader feature关键字在Properties中设置:
Properties
{[MaterialToggle(_USE_FOG)] _UseFog("Use Fog", int)=0 
}
#pragma shader_feature _USE_FOG

当材质启用_USE_FOG时,这个关键字就被使用。当然,如果是multi_compile也可以在材质属性里面设置,但是无论是否设置该组中的某个关键字,这些关键字都还是会被编译,但shader_feature就只有选择的那些关键字会被编译。

  • 虽然也是声明一组关键字,但是隐含了一个任何关键字都没启用的情况。
  • 但是如果将shader设置到图形设置窗口的Always Included Shaders中,那么所有的关键字都会被包含。

全局关键字和本地关键字

上面的声明方式都是全局关键字,所谓全局就是可以使用Shader.EnableKeyword针对所有使用该shader的材质统一开启或关闭的关键字。
而本地关键字声明的时候要加上_local,比如#pragma multi_compile_local,使用Material.EnableKeyword修改,只影响这个材质。

这篇关于深入URP之Shader篇15: Shader关键字和变体的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/735675

相关文章

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Python批量替换多个Word文档的多个关键字的方法

《Python批量替换多个Word文档的多个关键字的方法》有时,我们手头上有多个Excel或者Word文件,但是领导突然要求对某几个术语进行批量的修改,你是不是有要崩溃的感觉,所以本文给大家介绍了Py... 目录工具准备先梳理一下思路神奇代码来啦!代码详解激动人心的测试结语嘿,各位小伙伴们,大家好!有没有想

Java中的volatile关键字多方面解析

《Java中的volatile关键字多方面解析》volatile用于保证多线程变量可见性与禁止重排序,适用于状态标志、单例模式等场景,但不保证原子性,相较synchronized更轻量,但需谨慎使用以... 目录1. volatile的作用1.1 保证可见性1.2 禁止指令重排序2. volatile的使用

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

深入解析Java NIO在高并发场景下的性能优化实践指南

《深入解析JavaNIO在高并发场景下的性能优化实践指南》随着互联网业务不断演进,对高并发、低延时网络服务的需求日益增长,本文将深入解析JavaNIO在高并发场景下的性能优化方法,希望对大家有所帮助... 目录简介一、技术背景与应用场景二、核心原理深入分析2.1 Selector多路复用2.2 Buffer

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

PowerShell中15个提升运维效率关键命令实战指南

《PowerShell中15个提升运维效率关键命令实战指南》作为网络安全专业人员的必备技能,PowerShell在系统管理、日志分析、威胁检测和自动化响应方面展现出强大能力,下面我们就来看看15个提升... 目录一、PowerShell在网络安全中的战略价值二、网络安全关键场景命令实战1. 系统安全基线核查

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语