重庆网站制作公司哪家好,wordpress关联adsense,网站成功案例设计,怎么制作游戏地图本章将接着上一篇文章#xff0c;在初步实现一个渲染管线后来创建自定义的shader。上一篇文章的链接 https://blog.csdn.net/yinfourever/article/details/90516602。在本章中#xff0c;将完成以下内容#xff1a;
写一个HLSL Shader定义constant buffer#xff08;常量缓…本章将接着上一篇文章在初步实现一个渲染管线后来创建自定义的shader。上一篇文章的链接 https://blog.csdn.net/yinfourever/article/details/90516602。在本章中将完成以下内容
写一个HLSL Shader定义constant buffer常量缓冲区使用 Render Pipeline Core Library支持动态合批和GPU instancing
本章最后实现的效果如下图多个不同颜色球体只需要一个draw call 256个球体只需要一个draw call1.Custom Unlit Shader
1.1Creating a Shader
创建一个Shader删掉所有自带的默代码写入以下代码。 将上一章创建的Unlit Opaque material指定为使用我们创建的这个新shader。
1.2 HLSL
Unity 新的SRP渲染管线使用HLSL所以我们自定义的渲染管线也使用HLSL我们使用HLSLPROGRAM and ENDHLSL 一个Shader至少应该包含vertex函数顶点shader部分和fragment函数片元shader部分。我们命名UnlitPassVertex for the vertex function and UnlitPassFragment for the other。但具体代码我们不在这个文件中实现而是分离到一个单独的hlsl文件中在这里include进来。 在生成的Unlit.hlsl文件中我们使用#ifndef来避免多次引用。之后我们声明Vertex函数的输入参数结构体和输出参数结构体 在vertex shader中讲输入参数传递给输出参数在fragment shader中返回1这样就完成了一个最简单的shader架构虽然参数还是有错误的position参数没有进行坐标变换处理直接从vertex shader传入到了fragment shader 1.3 Transformation Matrices
从模型空间到裁剪空间需要两步转换我们先用unity_ObjectToWorld矩阵转换到世界坐标空间再用unity_MatrixVP矩阵转换到裁剪空间。 我们可以通过一个小技巧来提高运算的效率将input的position信息从三维向量补齐成四维可以大大加快运算效率。 1.4 Constant Buffers
Unity没有直接提供MVP矩阵二是拆开成提供两个矩阵M和VP是因为VP矩阵在一帧中不会改变可以重复利用。Unity将M矩阵和VP矩阵存入Constant Buffer中以提高运算效率M矩阵存入的buffer为UnityPerDraw buffer, 也就是针对每个物体的绘制不会改变。VP矩阵则存入的是UnityPerFrame buffer即每一帧VP矩阵并不会改变。Constant Buffer并不是所有平台都支持目前OpenGL就不支持。
使用cbuffer keyworld来引入Constant bufferconstant buffer中还有很多其他的数据暂时我们用不到只使用这两个矩阵 1.5 Core Library 因为constant buffer并不是支持所有平台所以我们使用宏来代替直接cbuffer keyword使用CBUFFER_START 和CBUFFER_END 这两个宏需要使用Core Library通过package manager可以安装。 安装成功后在hlsl代码中引入common.hlsl就可以使用这两个宏了。 1.6 Compilation Target Level
我们使用#pragma target 指定shader level为3.5 代替默认的2.5我们不支持OpenGL ES2那些老设备。 1.7 Folder Structure
我们调整下文件目录结构使引用的文件都放入ShaderLibrary文件夹 2 Dynamic Batching 现在使用我们新创建的这个shader去渲染大量球体时可以看到每个球体各自占用一个draw call。在fram debugger中我们可以看到提示没有合批的原因是我们没有开启动态合批。 我们根据提示即使打开了player setting中的Dynamic Batching option选项依然不会有效果这是因为player setting中设置的是unity默认的渲染管线而我们现在使用的是自定义的渲染管线所以需要代码手动控制我们自己的这个管线开启合批功能。 开启后我们发现合批依然没有成功根据提示是因为物体的定点数太多超过了动态合批的限制300。 把球体换成顶点数很少的方体动态合批就成功了可以看到只有一个draw call 2.2 Colors
当使用不同的material时是不能动态合批的为了证明这一点我们加入color属性。 将color属性存入constant buffer中 复制一份之前的material将复制的material中的color属性设置为其他颜色我们可以看到合批结果为4个draw call。 这是因为对于每个material至少会产生一个draw callunity中为了避免overdraw会根绝空间位置信息分组渲染对于每个material往往会多于一个draw call。 debugger中可以看到提示不能合批的信息为使用了不同的material。 2.3 Optional Batching
动态合批是一个提升渲染效率的好方法但是并不是所有时候都有效甚至会拖慢渲染效率。当场景中并不存在大量使用相同material的小mesh的时候动态合批就没什么用了反而每帧关于动态合批的运算会降低效率。我们在自定义的渲染管线中加入一个bool值来作为是否开启动态合批的开关。 在MyPipeline的构造函数中传入是否开启动态合批的bool值 在render函数中根据drawFlags设置动态合批是否开启 3 GPU Instancing
除了动态合批外GPU Instancing也可以用于减少draw call。通过GPU InstancingCPU告诉GPU在一个draw call中渲染特定的mesh和material的组合多次。这使得使用相同mesh和material的物体渲染时不再像动态合批需要重新组成一个新的后批后的大mesh这也就移除了对mesh大小的限制。
3.1 Optional Instancing
和之前的动态合批一样我们也引入一个bool值控制是否开启GPU Instancing 3.2 Material Support
渲染管线开启了GPU Instancing还不够还需要使得material支持GPU Instancing通过添加#pragma multi_compile_instancing来产生shader 变体一个支持GPU Instancing一个不支持。在Editor中material的设置中可以看到是否开启GPU Instancing选项。 3.3 Shader Support
当GPU Instancing开启时GPU会使用相同的constant data渲染同一个mesh多次。但是因为每个物体的位置不同所以M矩阵就不同。为了解决这个问题会在constant buffer中存入一个数组用于存储待渲染的物体的M矩阵。每一个instance根据自身的index从数组中取用数据。
我们使用unity提供的UNITY_MATRIX_M这个宏在core library中的这个宏可以支持instancing在使用矩阵数组的时候可以取出对应的矩阵 使用该宏需要引用UnityInstancing.hlsl这个文件。 当时用Instancing时物体的index或被gpu传入顶点数据中UNITY_MATRIX_M这个宏需要使用这个index数据我们将Index数据加入到Vertex shader函数的输入结构体中在Vertex Shader的处理函数中使用UNITY_SETUP_INSTANCE_ID 这个宏来使其生效。 除了 object-to-world matrices, 默认 world-to-object矩阵也存在了constant buffer中。但是目前我们没有需求使用他们所以可以通过#pragma instancing_options assumeuniformscaling去掉他们提高性能 3.4 Many Colors
之前我们想实现一个场景中多个颜色的物体只能使用多个material但是如果使用类似存储M矩阵的方式操作color属性就可以实现用一个matrial渲染多种颜色物体。
首先创建一个脚本包含要使用的color数据。 其次通过MaterialPropertyBlock函数创建一个MaterialPropertyBlock实体通过它来设置material中的颜色。这些操作写入了OnValidate函数中使得在editor中可以看到颜色变化效果。 通过去掉局部变量可以优化效率。 这时就可以用一个material渲染多种颜色了但是到目前为止每个物体都会产生draw call 3.5 Per-Instance Colors
像M矩阵处理的方式一样我们也将color属性以数组形式存入constant buffer中在使用时通过index从数组中取出。
不同于M矩阵Unity已经帮我们处理好了color属性需要我们自己手动创建constant buffer并存入其中 在vertex shader函数的输出中也要把index数据传递到fragment shader函数中。在fragment shdare的处理函数中根据index取用color数据 这时我们只需使用一个material就能实现多颜色物体的渲染了并且可以实现合并draw call。但是要注意的是constant buffer能存储多少数据是有限制的最大数量的Instance取决于每个instance需要多少数据除此之外不同平台最大限制也不同。同时instance draw call的合并依然也要求需要是相同的mesh和material比如下图中的方形和圆形虽然使用相同的material但因为mesh不同所以依然需要不同的draw call进行渲染。