通用材質系統介紹
材質系統是一個實時渲染引擎很是重要的部分,它使得開發者可以很是便捷地設計出具備真實感的場景和角色。一個好的材質系統能夠提升引擎的易用性,並能夠方便的擴展渲染效果,來提高渲染質量和效率。java
材質系統需求
圖形引擎一般須要支持不一樣的渲染效果,一個優秀的材質系統一般要支持多Pass渲染管線和自定義Shader模板,因爲渲染效果的複雜多樣性會致使Shader數量大幅增長,這樣會形成Shader文件冗餘,所以材質系統要提供一套Shader複用的機制。同時,市面上各硬件廠商對圖形API的支持程度不一樣,受限於硬件水平的差別,材質系統也要兼容中低高端硬件。綜上所述,通用材質系統須要知足如下需求:
多Technique:材質中包含多個實現方案,這樣在進行高中低端機適配或實現不一樣材質效果時,咱們能夠方便進行材質更替。
多Pass:對於複雜繪製效果,單次繪製沒法實現,經常包含多個Pass的渲染。
自定義Shader:減小Shader數量,提供Shader複用機制。
模板 + 實例:材質是一個模板,經過對某一個材質進行實例化,指定不一樣的數據和貼圖,就可讓物體表現出不一樣的顯示效果。git
材質數據
材質描述了場景中物體與光照進行交互的過程,本質上它是指可以描述一個物體顯示外觀的一系列數據,它包括幾個方面:github
- 着色模型(Shading Model):着色器的組合,決定了材質的參數與光照參數如何被處理,從而合成最終的顏色。好比最基本的着色模式爲:Surface Color = Diffuse + Specular + Emissive + Ambient。
- 渲染狀態:好比剔除模式(正面、背面、不剔除),混合模式(開啓,關閉),混合因子,深度測試,模板測試等。
- 混合模式(Blend Mode):決定了幾何對象渲染完成後如何與場景中的其餘物體進行疊加,混合模式會影響對象的繪製順序,混合模式的渲染次序從先到後是:不透明(Opaque) > 蒙版(Masked) > 半透明(Translucent) > 疊加(Additive) > 相乘(Modulate)。
- 參數: Shader中使用到的Uniform參數,好比紋理貼圖,採樣器,顏色因子,相機參數,光源參數,Pass間的混合參數等。
材質模板
材質文件就是將上述的材質數據進行合理的組織,方便應用開發者使用,一般材質文件被劃分爲三個重要的模塊:算法
- Defines:宏列表,定義Shader宏有什麼值。
- Properties:定義Shader中參數的值。
- Technique:Pass列表,定義渲染用的狀態和Shader文件。
總結下來,一個材質模板文件應該是相似這樣的一個結構:json
Material 「ForwardPbr」 { Properties { Color(「Color」,Color)=(1,1,1,1) SpecularColor(「SpecularColor」,Color)=(1,1,1,1) Gloss(「Gloss」,Range(8.0,256))=20 } Technique { Pass { Blend One One CullMode None SkinningEnable true Shader 「ForwardPbr.vert」 Shader 「ForwardPbr.frag」 } Pass{} } Technique { Pass{} Pass{} } }
CGKit的材質系統
圖形引擎中提到的材質貼圖和物體顏色,高光計算,ALPHA混合、紋理過濾、裁剪模式等,在Vulkan中大多數由渲染管線控制。緩存
Vulkan圖形渲染管線介紹
Vulkan中的圖形管線決定了頂點數據如何被程序加工與處理,以及幾何對象的渲染順序,提交到設備的狀態控制值,着色器模型,是圖形引擎的核心模塊,Vulkan的圖形管線包括如下幾個部分(其中Vulkan經過Pipeline State Object進行狀態管理):數據結構
在圖形引擎或遊戲引擎中,咱們一般用材質文件來描述上述PSO狀態數據,Shader數據和貼圖數據,經過各類變換操做,最終將網格使用到的頂點數據轉化爲屏幕空間像素。材質決定了應用最後的展現效果和圖像質量。架構
CGKit材質系統介紹
CGKit的材質系統一樣也要知足通用材質系統的需求,支持多Technique、多Pass和自定義Shader。數據結構和算法
CGKit材質系統架構圖和類ide
CGKit的材質系統主要由如下類組成,咱們簡短介紹下它們的功能:
Material:包含了建立一個材質須要的全部資源,包括屬性定義,Shader文件定義,紋理貼圖,渲染狀態設置,由多個Technique組成。
PipelineState:Vulkan的PSO的封裝,包括了管線中的全部狀態。
ShaderProgram:表示渲染一個模型用到的全部Shader,負責把glsl編譯成SPIRV,並反射出全部的ShaderResource。
ShaderResource:經過Shader反射系統獲取的Shader資源,能夠獲取到資源的名字,位置等信息。
DescriptorSetLayout:定義了Shader中的資源與DescriptorSet的映射。
PipelineLayout:管理一組描述符集合佈局。
DescriptorSet:管理一組Shader資源。
RenderPipeline:用於渲染的Vulkan管線。
MaterialInstance:根據Material文件建立材質實例,會根據Material文件建立DescriptorSetLayout,DescriptorSet和RenderPipeline。
它對應的架構圖以下所示:
CGKit材質模板
CGKit使用json文件格式定義材質模板,材質文件描述僞代碼以下:
「Material」 : { 「basePath」 : 「material/ ForwardPbr.cgmat」, 「Properties」 : [{ 「name」 : 「Color」, 「type」 : 「vec4」, 「value」 : 「1,1,1,1」 }, { 「name」 : 「albedo」, 「type」 : 「texture」, 「value」 : 「models/chip/chip_albedo.png」 }, { 「name」 : 「normal」, 「type」 : 「texture」, 「value」 : 「models/chip/chip_normal.png」 }] 「Techniques」 : [{ 「Pass」:[{ "rasterizationState": { "cullMode": " NONE" }, "depthStencilState": { "depthTestEnable": true, "depthWriteEnable": true }, 「SkinningEnable」 : true, "shader": [{ "type": "SHADER_STAGE_TYPE_FRAGMENT" "uri": "shaders/basic_pbr.frag", }], }] }] }
CGKit自定義Shader
材質系統中最重要的一塊就是Shader文件的配置,實現Shader的自定義須要完成如下功能:
- Shader編譯;
- Shader代碼複用;
- Shader拼接;
- Shader反射;
- Shader參數更新;
Shader編譯
Shader只是一段可執行的彙編代碼,咱們不管是使用GLSL、HLSL、CG,或者使用Unity的Unity Shader,最終提交給GPU時,都須要將這些高層實現語言編譯成二進制的彙編語言。
CGKit的圖形API是Vulkan,而Vulkan使用的是SPIRV格式的Shader,咱們經過KhronosGroup提供的Glslang能夠將GLSL、HLSL編寫的Shader代碼編譯成SPIRV中間代碼。CGKit使用Glslang將GLSL轉換稱爲SPIRV:
External/`uname -s`/bin/GlslangValidator -H -o Asset/Shaders/Vulkan/pbr_ps.spv Asset/Shaders/Vulkan/pbr.frag
Shader代碼複用
不一樣的渲染效果須要不一樣的Shader實現,每一個Shader徹底獨立輸入的方式會形成Shader文件大量的冗餘,CGKit提供了一套Shader代碼複用的機制,經過將Shader進行模塊劃分並增長預處理宏來減小Shader數量。
鑑於Shader存在大量通用的數據結構及函數,經過對Shader進行合理模塊劃分,能夠達到Shader代碼複用的功能。好比咱們對不一樣的材質效果進行整理,找出它們數據結構之間的共性,抽取通用部分放在獨立的glsl文件裏,而後將剩餘獨有的部分保留在各自的文件裏。
一般咱們會將一些常量數據,如燈光,MVP矩陣,相機參數,材質貼圖(如陰影圖,PBR材質模型貼圖)放在cbuffer.glsl文件中。一樣的會將一些通用算法,如求交函數,僞隨機函數,插值函數,光照陰影計算,PBR中的各類GGX計算函數放在一個functions.glsl文件中。
爲了複用Shader的數據結構和算法,CGKit在Shader中定義了預處理宏,經過在材質文件中開啓或關閉這些宏來動態啓用或關閉Shader代碼,達到了減小Shader文件數量的目的。例如咱們能夠動態開啓和關閉一些渲染效果,如光照,陰影,霧效等等。
由於要動態開啓和關閉宏,CGKit經過Glslang對Shader實時編譯,爲避免實時編譯增長Shader的加載時間,CGKit同時也提供了Shader緩存機制。
Shader拼接
CGKit使用GLSL Shader,因爲GLSL語言不支持#include預編譯命令,咱們須要用命令行工具把不一樣模塊的Shader文件從新組合起來,造成一個完整的GLSL Shader:
cat Asset/Shaders/cbuffer.glsl Asset/Shaders/functions.glsl pbr_ps.glsl > Asset/Shaders/Vulkan/pbr.frag
Shader反射
對於Shader裏面的符號變量,如uniform buffer,texture sampler,push constant,specialization constant,CGKit須要與這些符號變量進行交互,經過材質系統設置或更新它們的值,所以,咱們須要經過一套反射機制獲取到對應變量的name,set,bind,location等信息。
SPIRV-Cross提供了一套Shader的反射機制,CGKit首先經過Glslang將指定的GLSL格式的Shader代碼編譯成SPIRV,再經過SPIRV Reflection將SPIRV code裏面的符號變量所有反射出來。
Shader參數更新
Shader中的數據流主要包括兩部分:
- vertex、index buffer等mesh提供的數據:這部分屬於Shader固定輸入,在建立管線的時候指定好頂點格式聲明,在渲染的時候綁定相應的頂點,索引buffer便可。
- uniform buffer,texture sampler:這部分輸入須要CGKit經過Descriptor Set進行設置和更新。經過SPIRV-Cross的Shader反射,咱們能夠獲取到對應資源的名字,位置信息。由於咱們是經過材質文件來更新這些Shader資源的,因此咱們在材質文件裏面指定了這些參數,經過嚴格按名字匹配來更新Shader資源。所以咱們建議用戶儘可能統一Shader裏面的參數名字,並定義在公共頭文件中。
CGKit材質建立
CGKit根據材質模板生成材質實例,生成材質實例的過程實際上是自動化建立Vulkan紋理貼圖,描述符集合佈局,管線佈局,描述符集合,渲染管線的過程。
CGKit加載材質的時候根據Shader反射填充好描述符集合,在更新Shader的uniform buffer,texture sampler時,會相應地更新DescriptorSet,在提交繪製命令時,只須要綁定不一樣的DescriptorSet就能切換不一樣的資源。
建立DescriptorSetLayout
建立描述符集合佈局分兩步:
1. 經過Shader反射機制獲取ShaderResource:材質文件裏面定義了一個渲染對象須要用到的全部Shader,咱們經過Shader的反射機制將Shader文件裏面的符號變量資源反射出來,做爲一個Shader資源存放在ShaderProgram類,Shader資源包含了資源的名字以及所屬的描述符集合的索引和綁定槽,相似下面的結構體:
struct ShaderResource { String name = 「」; ShaderStageFlag stageFlag = SHADER_STAGE_VERT; ShaderResourceType type; // 資源類型 u32 set = 0; u32 binding = 0; // binding u32 arraySize = 0; // 對應VkDescriptorSetLayoutBinding的descriptorCount u32 offset = 0; // for push constants u32 size = 0; // for push constants u32 constantID = 0; // for specialization constants u32 location = 0; u32 inputAttachmentIndex = 0; u32 vecSize = 0; u32 columns = 0; };
其中Shader中資源的類型以下:
enum ShaderResourceType { SHADER_RESOURCE_TYPE_INPUT, SHADER_RESOURCE_TYPE_OUTPUT, SHADER_RESOURCE_TYPE_BUFFER_UNIFORM, SHADER_RESOURCE_TYPE_BUFFER_STORAGE, SHADER_RESOURCE_TYPE_INPUTATTACHMENT, SHADER_RESOURCE_TYPE_IMAGE, SHADER_RESOURCE_TYPE_IMAGE_SAMPLERR, SHADER_RESOURCE_TYPE_IMAGE_STORAGE, SHADER_RESOURCE_TYPE_SAMPLER, SHADER_RESOURCE_TYPE_PUSH_CONSTANT, // for pipeline layout creating SHADER_RESOURCE_TYPE_SPECIALIZATION, // for Shader stage creating SHADER_RESOURCE_TYPE_All };
2. 根據ShaderResource建立描述符集合佈局:經過Shader反射後ShaderProgram類最終擁有不一樣的描述符集編號,及其對應的ShaderResource。咱們根據ShaderResource生成DescriptorSetLayoutBinding,固然,要去掉四種沒有綁定槽的資源(Input,Output,PushConstant,SpecializationConstant)。而後根據DescriptorSetLayoutBinding信息生成DescriptorSetLayout。在DescriptorSetLayout類中,咱們能夠根據資源的名字獲得它的綁定槽,以及對應的描述符佈局綁定信息。
建立DescriptorSet
建立描述符集合分兩步。
1. 建立DescriptorPool:規定好每一個描述符池可以分配的最大描述符集合個數,假定爲16,從DescriptorSetLayout中獲取全部的Bindings,統計描述符的數量,用這個數與最大描述符集合個數相乘,獲得描述符池的大小,依據這個大小建立描述符池。描述符池會允許建立16個描述符集合,若是描述符集合的數量超過了16,則從新分配一個描述符池。
2.根據DescriptorSetLayout和DescriptorPool生成描述符集合:同類型的描述符集合會對應多個描述符池。
建立PipelineLayout
根據DescriptorSetLayout和Shader中的push constant資源建立管線佈局。
建立RenderPipeline
從PipelineState中獲取管線的狀態信息和Shader信息,從mesh中拿到頂點佈局信息,建立管線。
CGKit材質應用
材質資源一旦被建立,就能夠添加到各類渲染組件中進行渲染。若是要修改材質表現效果,咱們只須要在運行時動態修改材質參數,包括渲染狀態,紋理參數,Shader參數,Shader文件,就能夠達到目的,不須要關注材質系統底層作了什麼事情。
CGKit材質系統優化
材質排序
咱們都知道,像Vulkan這樣的圖形接口每設置一次GPU狀態的時候,都會有必定的開銷。爲保證渲染流暢,咱們要儘可能減小狀態切換來下降開銷。
在CGKit中,經過對幾何對象的材質進行分組排序,將類似的材質排在一塊兒能夠減小渲染流程中的狀態切換,從而達到提升渲染效率的目的,分組的順序以下:
- 先按混合模式分組,順序爲:不透明 > 蒙版 > 半透明 > 疊加 > 相乘;
- 混合模式分組後,每組中的對象再按着色模型分組;
- 着色模型分組後,每組對象再按紋理分組;
- 紋理分組後,再按其餘參數分組。
即分組的優先級爲:混合模式 > 着色模型 > 紋理對象 > 其餘參數。
調整資源更新頻率
Shader資源在渲染時須要不斷更新,並且每一個資源的更新頻率會不同。應用須要指定每一個資源的更新頻率,按照資源的更新頻率能夠把Shader資源分爲三種類型:
- Static:只要綁定了就不會改變的資源,例如相機屬性(包括相機位置,視圖矩陣,投影矩陣),光照屬性(光源類型,光源位置,光源方向,光源顏色,光照強度,光源衰減因子),屏幕寬高,陰影Shadowmap等全局常量。
- Mutable:至關於材質的更新頻率,例如漫反射貼圖、法線貼圖,自發光貼圖,切換一個材質就會更新一次。
- Dynamic:隨時均可能更新的資源,如模型的世界矩陣。
預先建立管線
Vulkan中的圖形渲染管線幾乎不可改變,若是須要更改Shader,混合,光柵化等狀態的設置,則必須從新建立管線。所以咱們能夠預先建立好全部的管線,這樣管線的操做都是提早知道的,則能夠經過驅動程序更好地優化它。
緩存機制
隨着場景複雜度的增長,材質文件數量會變多,與材質建立相關的資源會大量重複,咱們能夠將這些資源緩存起來,避免資源的重複建立並加快資源的加載和建立。與材質建立相關的資源主要有Texture,Shader,DescriptorSetLayout,PipilineLayout,DescriptorSet,enderPipiline,咱們能夠將這些資源都緩存起來,加載資源的時候,先從緩存裏面查找,找不到,再從磁盤中加載和建立。
>>訪問華爲圖形計算服務官網,瞭解更多相關內容
>>獲取華爲圖形計算服務開發指導文檔
>>華爲HMS Core官方論壇
>>華爲圖形計算服務開源倉庫地址:GitHub、Gitee
點擊右上角頭像右方的關注,第一時間瞭解華爲移動服務最新技術~