IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity SRP系列——DrawCalls -> 正文阅读

[游戏开发]Unity SRP系列——DrawCalls

实例原文

Unity通用渲染管线(URP)系列(二)——Draw Calls(Shaders&Batches) - 知乎 (zhihu.com)

Draw Calls (catlikecoding.com)

编写简单的HLSL Shader

创建Shader:?Custom RP/Unlit

声明其顶点着色器函数和片元着色器函数,声明颜色属性_BaseColor

Shader "Custom RP/Unlit"
{
    Properties
    {
        _BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)
    }
    
    SubShader
    {
        Pass
        {
            HLSLPROGRAM
            
            #pragma vertex UnlitPassVertex
            #pragma fragment UnlitPassFragment
            
            #include "UnlitPass.hlsl"
            
            ENDHLSL
        }
    }
}

在UnlitPass.hlsl中编写顶点着色器和片元着色器的具体定义

#ifndef CUSTOM_UNLIT_PASS_INCLUDE
#define CUSTOM_UNLIT_PASS_INCLUDE

#include "../ShaderLibrary/Common.hlsl"

float4 _BaseColor;

float4 UnlitPassVertex(float3 positionOS : POSITION) : SV_POSITION
{
    float3 positionWS = TransformObjectToWorld(positionOS.xyz);
    return TransformWorldToHClip(positionWS);
}

float4 UnlitPassFragment() : SV_TARGET
{
    return _BaseColor;
}

#endif

其中在顶点着色器中做的是顶点位置空间变换,从模型空间转换到世界空间,再从世界空间转换到裁剪空间。

片元着色器返回定义的片元颜色。

在做空间变换时用到了unity_ObjectToWorld,unity_MatrixVP等矩阵及其逆矩阵,在UnityInput.hlsl中定义:

#ifndef CUSTOM_UNITY_INPUT_INCLUDED
#define CUSTOM_UNITY_INPUT_INCLUDED

float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
real4 unity_WorldTransformParams;

float4x4 unity_MatrixVP;
float4x4 unity_MatrixV;
float4x4 glstate_matrix_projection;

#endif

在Common.hlsl中包含对实际调用转换方法的定义:

#ifndef CUSTOM_COMMON_INCLUDED
#define CUSTOM_COMMON_INCLUDED

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "UnityInput.hlsl"

#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"

#endif

可以定位到对应的Core文件中看到顶点空间变换调用的函数代码SpaceTransform.hlsl:

float3 TransformObjectToWorld(float3 positionOS)
{
    return mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)).xyz;
}

float4x4 GetObjectToWorldMatrix()
{
    return UNITY_MATRIX_M;
}

float4 TransformWorldToHClip(float3 positionWS)
{
    return mul(GetWorldToHClipMatrix(), float4(positionWS, 1.0));
}

float4x4 GetWorldToHClipMatrix()
{
    return UNITY_MATRIX_VP;
}

批处理

SRP批处理器

它在GPU上缓存了材质属性,所有材质属性都需要在具体的存储缓冲区内定义,而不是在全局级别上定义。它通过将指定属性放入特定的常量内存缓冲区中来隔离它,尽管它仍可在全局级别访问。

并非所有平台(例如OpenGL ES 2.0)都支持常量缓冲区,因此,我们可以使用核心RP库中包含的CBUFFER_START和CBUFFER_END宏,而不是直接使用cbuffer。

在这个示例中,如果我们使用特定的一组值,则需要全部定义它们。对于转换组,即使我们不使用它,我们也需要包括float4 unity_LODFade。顺序无关紧要,但是Unity会将其直接放在unity_WorldToObject之后,因此我们也要这样做。

在渲染管线中开启SRP批处理程序,调用:

GraphicsSettings.useScriptableRenderPipelineBatching设置为true

唯一的限制是每种材质的内存布局需要相同,这是因为我们对所有材质都使用相同的着色器,每个着色器仅包含一个颜色属性。Unity不会比较材质的确切内存布局,它只是仅批处理使用完全相同的着色器变体的绘制调用。

GPU Instancing

还有一种合并DrawCall的方法,该方法适用于逐对象的材质属性。这就是所谓的GPU实例化(GPUInstancing),其工作原理是一次对具有相同网格物体的多个对象发出一次绘图调用。CPU收集所有每个对象的变换和材质属性,并将它们放入数组中,然后发送给GPU。然后,GPU遍历所有条目,并按提供顺序对其进行渲染。

在Unlit.shader文件中添加预编译指令,生成具有GPU实例化支持和不具有GPU实例化支持的两个变体。

在Common.hlsl中包括对GPU实例化的支持:

UnityInstancing.hlsl的作用是重新定义这些宏来访问实例数据数组。将输入结构修改为结构用作函数的输入参数,并将UNITY_VERTEX_INPUT_INSTANCE_ID放在属性中来添加它,然后在输入时,添加UNITY_SETUP_INSTANCE_ID(input); 在UnlitPassVertex的开头。这将从输入中提取索引,并将其存储在其他实例化宏所依赖的全局静态变量中。

struct Attribute
{
    float3 positionOS : POSITION;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

float4 UnlitPassVertex(Attribute input) : SV_POSITION
{
    UNITY_SETUP_INSTANCE_ID(input);
    float3 positionWS = TransformObjectToWorld(input.positionOS);
    return TransformWorldToHClip(positionWS);
}

现在尚不支持逐实例的材质数据。如果要添加的话,通过用UNITY_INSTANCING_BUFFER_START替换CBUFFER_START以及用UNITY_INSTANCING_BUFFER_END替换CBUFFER_END来完成。

然后,将_BaseColor的定义替换为UNITY_DEFINE_INSTANCED_PROP(Float 4,_BaseColor)。

UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
	UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)

使用实例化时,我们现在还需要在UnlitPassFragment中提供实例索引。为了简单起见,我们将使用一个结构,通过UNITY_TRANSFER_INSTANCE_ID(input,output)使UnlitPassVertex输出位置和索引。复制索引(如果存在)。我们像Unity一样命名此结构Varying,因为它包含的数据在同一三角形的片段之间可能会有所不同。

将此结构作为参数添加到UnlitPassFragment。然后像以前一样使用UNITY_SETUP_INSTANCE_ID来使索引可用。现在需要通过UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial,_BaseColor)访问material属性。

struct Varyings {
	float4 positionCS : SV_POSITION;
	UNITY_VERTEX_INPUT_INSTANCE_ID
};

Varyings UnlitPassVertex(Attributes input)
{
    Varyings output;
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_TRANSFER_INSTANCE_ID(input, output);
    float3 positionWS = TransformObjectToWorld(input.positionOS);
    output.positionCS = TransformWorldToHClip(positionWS);
    return output;
}


float4 UnlitPassFragment(Varyings input) : SV_TARGET
{
    UNITY_SETUP_INSTANCE_ID(input);
    return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
}

请注意,基于目标平台以及每个实例需要提供的数据量,批处理大小是有限制的。如果超过此限制,那么最终将导致一批以上。此外,如果使用多种材质,分类仍可以拆分批次。

配置批处理

那种方式更好可能取决于很多因素,所以把它们处理成可配置的选项会更好。首先,添加布尔参数以控制是否将动态批处理和GUI实例化用于DrawVisibleGeometry,而不是对其进行硬编码。

相关代码如下:

创建透明和裁切的材质

Blend模式

不透明渲染和透明渲染之间的主要区别是,我们是替换之前绘制的任何内容还是与之前的结果结合以产生透视效果。可以通过设置源和目标混合模式来控制。这里的源是指现在绘制的内容,目标是先前绘制的内容,以及最终产生的结果。为此添加两个着色器属性:_SrcBlend和_DstBlend。它们是blend modes的枚举,我们可以使用的最佳类型是Float,默认情况下将源设置为1,将目标设置为零。

为了简化编辑,我们可以将Enum添加到properties中,并使用完全限定的UnityEngine.Rendering.BlendMode枚举类型作为参数。

可以在Pass块中使用Blend语句和两个模式来定义混合模式。想使用着色器属性,可以通过将其放在方括号内来访问它们。这是可编程着色器之前的远古语法。

不写入深度

纹理化

向着色器添加_BaseMap纹理属性

在UnlitPass.hlsl中的着色器属性之前执行:

使用名为TEXTURE2D的宏参数,将纹理传到GPU内存。

通过SAMPLER宏实现,在名称前添加了sampler。用来匹配Unity自动提供的采样器状态。

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

在UnityPerMaterial缓冲区中添加提供纹理的平铺和偏移属性,但附加了_ST,代表缩放和平移等。

?

定义纹理坐标:

在片元着色器函数中,添加baseUV的定义

修改顶点着色器中复制纹理坐标,

float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
output.baseUV = input.baseUV * baseST.xy + baseST.zw;

在片元着色器中,通过使用SAMPLE_TEXTURE2D宏对纹理,采样器状态和坐标作为参数,对纹理进行采样。最终颜色是通过乘法相结合的纹理和单一颜色。

Alpha裁剪

完成此操作的通常方法是定义一个截止阈值。alpha值低于此阈值的片段将被丢弃,而所有其他片段将保留。添加一个_Cutoff属性,默认情况下将其设置为0.5。由于alpha始终位于零和1之间,因此我们可以使用Range(0.0,1.0)作为其类型。

同样将其添加到UnlitPass.hlsl的材质属性中。

通过调用UnlitPassFragment中的clip函数来丢弃片段。如果我们传递的值为零或更小,它将中止并丢弃该片段。因此,将最终的alpha值(可通过a或w属性访问)减去截止阈值传递给它。

添加一个控制着色器裁剪的关键字的Toggle属性,我们将使用_CLIPPING。属性本身的名称无关紧要,因此只需使用_Clipping。

将#pragma shader_feature _CLIPPING添加到其Pass中的指令中。

它将生成一个或两个变体,可以使代码以定义为条件:

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-04 11:32:18  更:2021-08-04 11:33:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/6 18:21:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码