当我们在ue4中制作了一个美术材质之后,引擎本身会为我们做很多事情,它会把结点翻译为hlsl,生成多个shader变体,并在多个mesh pass中去选择性的调用所需的shader,其中一个重要的过程就是获取shader绑定的数据。

本文将主要讨论ue4是如何处理来自材质的不同的输入,它们将以怎样的形式传递给shader,以怎样的频率更新,并在调用层做了怎样的优化处理。

输入类型

我们在材质中能控制输入的地方有两处,一个是材质直接输入,另一个是材质参数集合:

① 材质直接输入。

我们可以在母材质中开放给shader的参数,如美术纹理和参数。静态的参数我们使用静态材质,动态可运行时修改的参数我们使用动态材质。

这种输入的特点是每个材质独享一份参数输入,因此它在底层设计为每个Material实例独立的Uniform Buffer,在ue4中使用FUniformExpressionCache数据结构来描述。

② 材质参数集合(MPC, Material Parameter Collection)

我们还可以创建MPC资产,可以添加vector或scalar参数。

这种输入的特点是参数可以在多个材质共享,比较适合一些全场景的效果控制,但一个材质支持输入的MPC数量是比较有限的。

它在底层设计为场景中全局的Uniform Buffer集合,同时每个shader引用到的MPC索引由FUniformExpressionCache记录,可方便我们快速查找。

材质会生成多个shader变体,其中可能包括prepass, shadowpass, basepass的vs和ps,根据顶点类型的不同可能还包括了不同的顶点工厂(Vertex Factory),比如staticmesh, instance或skeletal。这些shader是在代码中定义的,每个特定的shader还可以指定一些输入。

③ 顶点工厂的输入

主要包括的是顶点属性(Vertex Attribute)的输入,包括位置、法线、顶点色、实例位置等。我们可以使用Vertex Buffer或Buffer传输这些数据。

④ 顶点或像素着色器的输入

主要包括的是更加底层模块的一些输入,比如光照/阴影/大气等的输入。

这部分的输入格式是由代码指定的,和材质中主要通过Uniform Buffer来绑定不一样,这里输入的格式更加灵活,一些共用的数据可能会放在Uniform Buffer中,而一些常量(Loose Data)可能直接绑定到shader中,我们还可以绑定一些资源类型的数据(SRV),比如Texture2D, Buffer, Structure Buffer等。

⑤ 全局输入

在多个shader中共享的数据或者一些必需的数据会设计在全局的Uniform Buffer中。

比如在basepass和defer pass中都可能会用到的各种LightUniformBuffer,存储灯光方向等信息;几乎在所有pass都会用到的ViewUniformBuffer,存储相机位置等信息。

还设计了一些高频数据作为独立的Unform Buffer,比如逐物件的PrimitivieUniformBuffer等;

输入上传

ue4在开始计算场景可见性(SceneVisibility)前,会先尽早地完成一些数据的上传,让GPU先开始忙碌起来,这样的话可以不阻碍后续的一些渲染工作,包括:

① 上传Primitive Uniform Buffer(静态物件初始化调用,动态物件多帧上传)

② 上传GPU Skin(蒙皮物件动画信息)

③ 上传Material Uniform Buffer(静态材质初始化调用,动态材质多帧上传)

④ 上传Material Parameter Collection(更新时上传)

⑤ 上传一些代码中定义的Uniform Buffer(如View等)

还有一部分数据比如LightmapUniformBuffer一般都是静态的,所以不会频繁更新,我们也较难捕获到这方面的数据。

单个ub数据上传的时间并不算太长,大概是us的量级。但如果场景中使用了大量的动态物件和动态材质,整体的上传时间还是比较可观的。

输入绑定

当我们向shader传入特定输入的时候,意味着shader中应该有对应的变量。材质中的变量是由ue4自动生成的,而代码中则是程序指定的变量。

在整个绘制工作流中,我们会首先完成shader的编译,并且去收集这些shader中存在的绑定信息。当我们在C++中收集绑定输入的实际值时,会先去校验shader中对应的绑定点和slot id。

绑定类型

ue4的Mesh Draw管线中,我们使用Shader Binding来完成这一点,它是一种延迟的设计,因为它会先去收集所有可用的绑定实参,提交前再调用实际的RHI层的绑定。

它支持的类型包括Uniform Buffer,Sampler,SRV(texture, buffer),Loose Data,顶点的输入则由Input Stream负责,不包含在Shader Binding负责的范畴中。所有的绑定信息我们可以认为是一个Input Layout,它可编码为缓冲区。

其中,Uniform Buffer, Sampler, SRV记录的是实际分配的引用,是一种分离式的设计;而Loose Data是我们直接绑定在shader上的参数,存储了实际的数据,可以理解为一个内联的常量数据。

API映射

我们在API中完成一次drawcall,通常会设置首先去各种状态量和绑定量,包括:

● SetPipelineState

● SetVertexBuffer/ SetIndexBuffer

● SetShaderBinding

对于CPU端来说,消耗主要体现在数据的准备和调用上;实际指令执行的过程中,GPU也会产生状态切换的消耗。

在API底层,如在Vulkan中,Shader Binding的调用会被映射为vkCmdBindDescriptorSets;dx12则相对复杂,它可能会映射到SetGraphicsRootConstantBufferView或SetGraphicsRootDescriptorTable等。

Vulkan的设计可能会产生更少的调用,而DX12的设计会更加适合输入排列的复用,但大多数的游戏引擎并不会优化到这么细致。

输入调用

在输入调用上,Shader Binding之所以要设计为延迟调用,是为了尽可能缓存一些绑定命令,减少CPU端渲染指令调用的次数。缓存的可复用性依赖于绘制对象的排序,我们应该尽可能把共享相同状态的对象合并到一起。

在缓存机制上,我们可以去缓存的内容包括PipelineState,它包含的最重要内容就是Shader,如果Shader Code完全一致仅仅是输入不同我们是可以缓存的。其次是一些输入,比如Uniform Buffer, SRV等,这些数据的缓存会对一些图形API产生收益。

实际实现中,Shader Binding会去维护一个缓存的状态,只有在绑定发生变化的时候,才去实际调用RHI层的设置接口,当我们把具有相同状态的对象排列在一起时,尤其是使用相同Shader的物体,缓存优化会得到较好的收益。

[ue4] 着色器绑定(Shader Binding)相关推荐

  1. 为新手准备的 Codea 着色器(Shader)教程

    为新手准备的 Codea 着色器(Shader) 教程 原文标题:<Shaders for dummies>  作者:Ignatz  译者:FreeBlues  译文链接:http://m ...

  2. Directx 计算着色器(compute shader)

    原文 :http://www.cnblogs.com/Ninputer/archive/2009/12/11/1622190.html 博者注:计算着色器调试(http://msdn.microsof ...

  3. OpenGL之计算着色器(Compute Shader)注解

    一.前言 关于计算着色器,我也是刚试验成功,所以接下来我也讲不出什么长篇大论,概念什么的百度一下到处都是,我这边只讲讲百度没有的填坑经历吧. 二.计算着色器的语法解释 先附上一个计算着色器的代码段: ...

  4. 片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但

    片元着色器(Fragment Shader)被称为像素着色器(Pixel Shader),但片元着色器是一个更合适的名字, 因为此时的片元并不是一个真正意义上的像素.

  5. OpenGL 几何着色器Geometry Shader

    OpenGL几何着色器Geometry Shader 几何着色器Geometry Shader简介 使用几何着色器 造几个房子 爆破物体 法向量可视化 几何着色器Geometry Shader简介 在 ...

  6. Unity Shader:细分着色器(Tessellation Shader)在Unity顶点着色器中的写法以及各参数变量解释

    图1:在Unity内将sphere细分后 图2:在Unity内将sphere细分后 Unity官网关于细分着色器的资料比较少,只有在Surface Shader中使用的例子.我看了下Surface S ...

  7. Learn OpenGL(四)——片段着色器(Fragment Shader)

    片段着色器(Fragment Shader) 片段着色器是第二个也是最终我们打算创建的用于渲染三角形的着色器. 片段着色器的全部, 都是用来计算你的像素的最后颜色输出. 为了让事情比较简单, 我们的片 ...

  8. 3D河豚鱼—OpenGL着色器(Shader)和GLSL程序

    3D河豚鱼-OpenGL着色器(Shader)和GLSL程序 效果图 程序代码 #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #el ...

  9. php代码着色器,使用Shader Graph实现《塞尔达传说:旷野之息》风格的着色器(转)...

    马上注册,加入CGJOY,让你轻松玩转CGJOY. 您需要 登录 才可以下载或查看,没有帐号?立即注册 x Unity的技术经理Ciro Continisio在Connect上分享创作模仿任天堂游戏& ...

最新文章

  1. python使用matplotlib可视化线图(line plot)、使用arrow函数在matplotlib可视化图像中添加箭头(drawing arrows in matplotlib)
  2. WinServer-FTP搭建
  3. 【数字信号处理】相关系数 ( 相关系数特点 | 完全相关 | 完全无关 | 部分相关 | 取值范围 | 相关信号产生 | 相干信号产生 )
  4. redmine常见问题
  5. visio 形状_分享我Visio经验(问题从来都不是问题)
  6. IBM虚拟化全接触 实现从虚拟化到云端的数据中心
  7. 自制操作系统:引导扇区的实现
  8. 如何安装数据库和数据库安装不了如何解决
  9. python做erp系统的可行性_ERP可行性分析
  10. Ti c64x 优化基本策略
  11. 做嵌入式编程,为什么用的是C语言而不是C++呢?
  12. java matlab 遗传算法_简单遗传算法MATLAB实现
  13. 用stlstack实现深搜_同心筑共未来,深信服是认真的
  14. OC作业- 图书馆管理系统
  15. coalesce函数的用法
  16. 马斯克疯狂理念在中国落地?国产高速飞行列车即将登场
  17. 自动驾驶与python_Python对自动驾驶技术的重要作用
  18. 一次尴尬的笔试。。。
  19. 如何在Joomla中创建一个漂亮的单页网站
  20. python 中的while true是什么意思_解析Python中while true的使用

热门文章

  1. 将瞰景smart3d空三结果导入contextcapture(CC)进行建模
  2. 电脑中出现共享打印机连接错误问题(错误0x0000000a)--解决方法
  3. Mysql之常见可视化管理工具
  4. 安卓手机Android文件夹下obb文件是什么,obb是什么文件?怎么使用obb文件夹
  5. 中国网建提供的SMS短信发送
  6. 诺基亚智能手机的sis和jar格式游戏文件安装方法图解
  7. Linux改完ip没有inet,linux中eth0中没有inet addr
  8. 公务员想辞职转行做程序员?
  9. Wiznote为知笔记私有部署(docker)删除多余用户账号.md
  10. 笨方法学 python3进阶篇_笨办法学Python 3 进阶篇