当前位置: 首页 > news >正文

UnityShader——初级篇之开始Unity Shader学习之旅

开始Unity Shader学习之旅

  • 一个最简单的顶点/片元着色器
    • 顶点/片元着色器的基本结构
    • 模型数据从哪里来
    • 顶点着色器和片元着色器之间如何通信
    • 如何使用属性
  • 强大的援手:Unity 提供的内置文件和变量
    • 内置的包含文件
    • 内置的变量
  • Unity 提供的 Cg/HLSL 语义
    • 什么是语义
    • Unity 支持的语义
    • 如何定义复杂的变量类型
  • Debug
    • 使用假彩色图像
    • 利用神器:Visual Studio
    • 最新利器:帧调试器
  • 小心渲染平台的差异
    • 渲染纹理的坐标差异
    • Shader的语法差异
    • Shader 的语义差异
  • Shader 整洁之道
    • float、half 还是 fixed
    • 规范语法
    • 避免不必要的计算
    • 慎用分支和循环语句
    • 不要除以 0

一个最简单的顶点/片元着色器

顶点/片元着色器的基本结构

  在之前已经看到了Unity Shader 的基本结构。它包含了 S h a d e r 、 P r o p e r t i e s 、 S u b S h a d e r 、 F a l l b a c k Shader、Properties、SubShader、Fallback ShaderPropertiesSubShaderFallback等语义块。顶点/片元着色器的结构与之大体相似,它的结构如下:

Shader "MyShaderName"{Properties{//属性}SubShader{//针对显卡A的SubShaderPass{//设置渲染状态和标签//开始Cg代码片段CGPROGRAM//该代码片段的编译指令,例如#pragma vertex vert#pragma fragment frag//Cg代码写在这里ENDCG//其他设置}//其他需要的Pass}SubShader{//针对显卡B的SubShader}//上述SubShader都失败后用于回调的Unity ShaderFallback "VertexLit"
}

  其中,最重要的部分是Pass语义块,绝大部分的代码都是写在这个语义块里面的。下面就来创建一个最简单的顶点/片元着色器。

  (1) 新建一个场景,把它命名为 Scene_5_2,咋爱 Unity 中可以得到下图中的效果

在这里插入图片描述
  可以看到,场景中包含了一个摄像机、一个平行光。而且场景的背景不是纯色,而是一个天空盒子。这是因为在Unity中,默认的天空盒子不为空,而是Unity内置的一个天空盒子。为了得到更加原始的效果可以去掉这个天空盒子,做法是,再Unity的菜单中,选择Window -> Rendering -> Lighting -> Environment -> Skybox Material,把该选项置空

  (2)新建一个 Unity Shader,把它命名为 Chapter5-SimpleShader

  (3)新建一个材质,把它命名为 SimpleShaderMat。把第 2 步中新建的 Unity Shader 赋给它

  (4)新建一个球体,拖曳它的位置可以在 Game 视图中可以合适的显示出来。把第 3 步中新建的材质赋给它

  (5)双击打开第 2 步中创建的 Unity Shader。删除里面所有的的代码,把下面的代码粘贴进去

Shader "Unity Shader Book/Chapter 5/Simple Shader"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragfloat4 vert(float4 v : POSITION) : SV_POSITION{return UnityObjectToClipPos(v);}fixed4 frag() : SV_Target{return fixed4(1.0,1.0,1.0,1.0);}ENDCG}}
}

  保存并返回 Unity 查看结果,最后得到的结果如下图所示

在这里插入图片描述
  首先,代码的第一行通过 Shader 语义定义了这个 Unity Shader的名字——“Unity SHaders Book/Chapter 5/Simple Shader”。保持良好的命名习惯有助于在为材质球选择 Shader 时快速找到自定义的 Unity Shader。需要注意的是,在上面的代码离并没有用到 Properties 语义块,Properties 语义并不是必需的,可以选择不生命任何材质属性

  齐次,声明了 SubShader 和 Pass 语义块。在本例中不需要进行任何渲染设置和标签设置,因此 SubShader 将使用默认的渲染设置和标签设置。在 SubShader 语义块中定义了一个 Pass,在这个 Pass 中同样没有进行任何自定义的渲染设置和标签设置

  接着,就是由 CGPROGRAM 和 ENDCG 所包围的 CG 代码片段,这就是重点。首先就是两行非常重要的编译指令:

#pragma vertex vert
#pragma fragment frag

  它们告诉 Unity,哪个函数包含了顶点着色器的代码,哪个函数包含了片元着色器的代码。更通用的编译指令如下:

#pragma vertex name
#pragma fragment name

  其中 name 就是指定的函数名,这两个函数的名字不一定是 vert 和 frag,它们可以是任意自定义的合法函数名,但一般使用 vert 和 frag 来定义这两个函数,因为它们很直观

  接下来具体看一下 vert 函数的定义:

float4 vert(float4 v : POSITION) : SV_POSITION{return UnityObjectToClipPos(v);
}

  这就是本例使用的顶点着色器代码,它是逐顶点执行的。vert 函数的输入 v 包含了这个顶点的位置,就是通过 POSITION 语义指定的。它的返回值是一个 float4 类型的变量,它是该顶点在裁剪空间中的位置,POSITION 和 SV_POSITION 都是 Cg/HLSL 中的语义,它们是不可省略的,这些语义将告诉系统用户需要哪些输入值,以及用户的输入是什么。例如这里,POSITION 将告诉 Unity,把模型的顶点坐标填充到输入参数 v 中,SV_POSITION 将告诉 Unity,顶点着色器的输入时裁剪空间中的顶点坐标。如果没有这些语义来限定输入和输出参数的话,渲染器就完全不知道用户的输入输出是什么,因此就会得到错误的效果。在本例中,顶点着色器只包含了一行代码,float4 UnityObjectToClipPos(float3 pos) 是 Unity 内置的函数,作用是将对象空间中的点变换到齐次坐标中的摄像机裁剪空间。

  然后,再来看一下 frag 函数:

fixed4 frag() : SV_Target{return fixed(1.0,1.0,1.0,1.0);
}

  在本例中,frag 函数没有任何输入。它的输出是一个 fixed4 类型的变量,并且使用了 SV_Target 语义进行限定。SV_Target 也是 HLSL 中的一个语义系统,他等同于告诉渲染器,把用户的输出颜色存储到一个渲染目标中,这里将输出到默认的帧缓存中。片元着色器中的代码很简单,返回了一个白色的 fixed4 类型的变量。片元着色器输出的颜色的每个分量范围在 [0,1],其中 (0,0,0) 表示黑色,而 (1,1,1) 表示白色

模型数据从哪里来

  想要得到更多的模型数据该怎么办

  想要得到模型上每个顶点的纹理坐标和法线方向。这个需求是很常见的,需要使用纹理坐标来访问纹理,而法线可用于计算光照,因此,需要为顶点着色器定义一个新的输入参数,这个参数不再是一个简单的数据类型,而是一个结构体。修改后的代码如下:

Shader "Unity Shader Book/Chapter 5/Simple Shader"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragstruct a2v{// POSITION 语义告诉 Unity,用模型空间的顶点坐标填充 vertex 变量float4 vertex : POSITION;// NORMAL 语义告诉 Unity,用模型空间的法线方向填充 normal 变量float3 normal : NORMAL;// TEXCOORD0 语义告诉 Unity,用模型空间的第一套纹理坐标填充 texcoord 变量float4 texcoord : TEXCOORD0;};float4 vert(a2v v) : SV_POSITION{//使用 v.vertex 来访问模型空间的顶点坐标return UnityObjectToClipPos(v.vertex);}fixed4 frag() : SV_Target{return fixed4(1.0,1.0,1.0,1.0);}ENDCG}}
}

  上面的代码中,声明了一个新的结构体 a2v,它包含了顶点着色器需要的模型数据。在 a2v 的定义中,用到了更多 Unity 支持的语义,如 NORMAL 和 TEXCOORD0,当它们作为顶点着色器的输入时都是有特定含义的,因为 Unity 会根据这些语义来填充这个结构体。对于顶点着色器的输入,Unity 支持的语义有:POSITION,TANGENT,NORMAL,TEXCOORD0,TEXCOORD1,TEXCOORD2,TEXCOORD3,COLOR 等。

  为了创建一个自定义的结构体,必须使用如下格式定义它:

struct StructName{Type Name : Semantic;Type Name : Semantic;......
};

  其中,语义是不可以省略的

  然后,修改了 vert 函数的输入参数类型,把它设置为新定义的结构体 a2v。通过这种自定义结构体的方式,就可以在顶点着色器中访问数据

  a2v 的名字是什么意思?

  a 表示应用,v 表示顶点着色器,a2v 的意思就是把数据从应用阶段传递到顶点着色器中。

  那么,填充到 POSITION,TANGENT,NORMAL 这些语义中的数据究竟时从哪里来的?在 Unity 中,它们是由使用该材质的 Mesh Render 组件提供的。在每帧调用 Draw Call 的时候,Mesh Render 组件会把它负责渲染的模型数据发送给 Unity Shader。由于一个模型通常包含了一组三角面片,每个三角面片由 3 个顶点构成,而每个顶点又包含了一些数据,例如顶点位置、法线、切线、纹理坐标、顶点颜色等。通过上面的方法就可以在顶点着色器中访问顶点的模型数据

顶点着色器和片元着色器之间如何通信

  在实践中往往希望从顶点着色器输出一些数据,例如把模型的法线、纹理坐标等传递给片元着色器。这就涉及顶点着色器和片元着色器之间的通信。

  为此,需要再定义一个新的结构体。修改后的代码如下

Shader "Unity Shader Book/Chapter 5/Simple Shader"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragstruct v2f{// SV_POSITION 语义告诉 Unity,pos 里包含了顶点在裁剪空间中的位置信息float4 pos : SV_POSITION;// COLOR0 语义可以用于存储颜色信息fixed3 color : COLOR0;};struct a2v{// POSITION 语义告诉 Unity,用模型空间的顶点坐标填充 vertex 变量float4 vertex : POSITION;// NORMAL 语义告诉 Unity,用模型空间的法线方向填充 normal 变量float3 normal : NORMAL;// TEXCOORD0 语义告诉 Unity,用模型空间的第一套纹理坐标填充 texcoord 变量float4 texcoord : TEXCOORD0;};v2f vert(a2v v){// 声明输出结构v2f o;o.pos = UnityObjectToClipPos(v.vertex);// v.normal 包含了顶点的法线方向,其分量范围在 [-1.0, 1.0]// 下面的代码把分量范围映射到了 [0.0, 1.0]// 存储到 o.color 中传递给偏远着色器o.color = v.normal * 0.5 + fixed3(0.5,0.5,0.5);return o;}fixed4 frag(v2f i) : SV_Target{//将插值后的 i.color 显示到屏幕上return fixed4(i.color,1.0);}ENDCG}}
}

  上面的代码中声明了一个新的结构体 v2f。v2f 用于在顶点着色器和片元着色器之间传递信息。同样的,v2f 中也需要指定每个变量的语义。在本例中使用了 SV_POSITION 和 COLOR0 语义。顶点着色器的输出结构中,必须包含一个变量,它的语义是 SV_POSITION。否则,渲染器将无法得到裁剪空间中的顶点坐标,也就无法把顶点渲染到屏幕上。COLOR0 语义中的数据则可以由用户自行定义,但一般都是存储颜色,例如逐顶点的漫反射颜色或逐顶点的高光反射颜色。类似的语义还有 COLOR1 等。

  至此就完成了顶点着色器和片元着色器之间的通信。需要注意的是,顶点着色器是逐顶点调用的,而片元着色器是逐片元调用的。片元着色器中的输入实际上是把顶点着色器的输出进行插值后得到的结果

如何使用属性

  材质提供一个可以方便地调节 Unity Shader 中参数的方式,通过这些参数可以随时调整材质的效果。而这些参数就需要写在 Properties 语义块中

  现在有了新的需求,需要在材质面板上显示一个颜色拾取器,从而可以直接控制模型在屏幕上显示的颜色。为此需要继续修改上面的代码

Shader "Unity Shader Book/Chapter 5/Simple Shader"
{Properties{// 声明一个 Color 类型的属性_Color("Color Tint",Color) = (1.0,1.0,1.0,1.0)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// 在 Cg 代码中,需要定义一个与属性名称和类型都匹配的变量fixed4 _Color;struct v2f{// SV_POSITION 语义告诉 Unity,pos 里包含了顶点在裁剪空间中的位置信息float4 pos : SV_POSITION;// COLOR0 语义可以用于存储颜色信息fixed3 color : COLOR0;};struct a2v{// POSITION 语义告诉 Unity,用模型空间的顶点坐标填充 vertex 变量float4 vertex : POSITION;// NORMAL 语义告诉 Unity,用模型空间的法线方向填充 normal 变量float3 normal : NORMAL;// TEXCOORD0 语义告诉 Unity,用模型空间的第一套纹理坐标填充 texcoord 变量float4 texcoord : TEXCOORD0;};v2f vert(a2v v){// 声明输出结构v2f o;o.pos = UnityObjectToClipPos(v.vertex);// v.normal 包含了顶点的法线方向,其分量范围在 [-1.0, 1.0]// 下面的代码把分量范围映射到了 [0.0, 1.0]// 存储到 o.color 中传递给偏远着色器o.color = v.normal * 0.5 + fixed3(0.5,0.5,0.5);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 c = i.color;//使用 _Color 属性来控制输出颜色c *= _Color.rgb;//将插值后的 i.color 显示到屏幕上return fixed4(c,1.0);}ENDCG}}
}

  在上面的代码中首先添加了 Properties 语义块中,并在其中声明了一个属性 _Color,它的类型是 Color,初始值是 (1.0,1.0,1.0,1.0),对应白色。为了而在 Cg 代码中可以访问它,还需要再 Cg 代码片段中提前定义一个新的变量,这个变量的名称和类型必须与 Properties 语义块中的属性相匹配

  ShaderLab 中属性的类型和 Cg 中变量的类型之间的匹配关系如下表所示:

ShaderLab属性类型和Cg变量类型的匹配关系
ShaderLab 属性类型Cg变量类型
Color,Vectorfloat4,half4,fixed4
Range,Floatfloat,half,fixed
2Dsampler2D
CubesamplerCube
3Dsampler3D

  有时 Cg 变量前会有一个 uniform 关键字,例如:

uniform fixed _Color;

  uniform 关键词 Cg 中修饰变量和参数的一种修饰词,它仅仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息(这和其他一些图像编程接口中的 uniform 关键词的作用不太一样),在 Unity Shader 中,uniform 关键词是可以省略的

强大的援手:Unity 提供的内置文件和变量

内置的包含文件

  包含文件,是类似于 C++ 中头文件的一种文件。在 Unity 中,它们的文件后缀是 .cginc。在编写 Shader 时可以使用 #include 指令把这些文件文件包含起来,这样就可以使用 Unity 提供的一些非常有用的变量和帮助函数。例如:

CGPROGRAM
// ...
# include "UnityCG.cginc"
// ...
ENDCG

  可以从 Unity 的应用程序中直接找到 CGIncludes 文件夹。在 Mac 上,它们的位置是:/Applications/Unity/Unity.app/Contents/CGIncludes;在 Windows 上,它们的位置是:Unity 的安装路径/Data/CGIncludes

  下表给出了 CGIncludes 中主要的包含文件以及它们的主要用途:

Unity 中一些常用的包含文件
文件名描述
UnityCG.cginc包含了最常使用的帮助函数、宏和结构体等
UnityShaderVVariables.cginc在编译 Unity Shader 时,会被自动包含进来。包含了许多内置的全局变量
Lighting.cginc包含了各种内置的光照模型,如果编写的是 Surface Shader 的话,会被自动包含进来
HLSLSupport.cginc在编译 Unity Shader 时,会被自动包含进来。声明了很多用于跨平台编译的宏和定义

  可以看出,有一些文件是即使没有使用 #include 指令,它们也是会被自动包含进来的,例如 UnityShaderVariables.cginc。除了商标列出的包含文件外,还引入了许多新的重要的文件,如 UnityStandardBRDF.cginc、UnityStandardCore.cginc 等,这些包含文件用于实现基于物理的渲染

  UnityCG.cginc 是最常接触的一个包含文件,在后面将使用很多该文件提供的结构体和函数,为编写提供方便。下表中给出了一些结构体的名称和包含的变量

UnityCG.cginc 中一些常用的结构体
名称描述包含的变量
appdata_base可用于顶点着色器的输入顶点位置、顶点法线、第一组纹理坐标
appdata_tan可用于顶点着色器的输入顶点位置、顶点切线、顶点法线、第一组纹理坐标
appdata_full可用于顶点着色器的输入顶点位置、顶点切线、顶点法线、四组(或更多)纹理坐标
appdata_img可用于顶点着色器的输入顶点位置、第一组纹理坐标
v2f_img可用于顶点着色器的输出裁剪空间中的位置、纹理坐标

  除了结构体外,UnityCG.cginc 也提供了一些常用的帮助函数。下表给出了一些函数名和它们的描述

UnityCG.cginc 中一些常用的帮助函数
函数名描述
float3 WorldSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向
float3 WorldSpaceLightDir(float4 v)仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化
float3 ObjSpaceLightDir(float4 v)仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向。没有被归一化
float3 UnityObjectToWorldNormal(float3 nrom)把法线方向从模型空间转换到世界空间中
float3 UnityObjectToWorldDir(float3 dir)把方向矢量从模型空间变换到世界空间中
float3 UnityWorldToObjectDir(float3 dir)把方向矢量从世界空间变换到模型空间中

内置的变量

  除了之前提出的用于坐标变换和摄相机参数的内置变量。除此之外,Unity 还提供了用于访问时间、光照、雾效和环境光等目的的变量。这些内置变量大多位于 UnityShaderVariables.cginc 中,与光照有关的内置变量还会位于 Lighting.cginc、AutoLight.cginc 等文件中

Unity 提供的 Cg/HLSL 语义

什么是语义

  实际上,这些事 Cg/HLSL 提供的语义。语义实际上就是要给赋给 Shader 输入和输出的字符串,这个字符串表达了这个参数的含义。通俗的讲,这些语义可以让 Shader 知道从哪里读取数据,并把数据输出到哪里,它们在 Cg/HLSL 的 Shader 流水线中是不可或缺的。需要注意的是,Unity 并没有支持所有的语义

  通常情况下,这些输入输出变量并不需要有特别的意义,也就是说可以自行决定这些变量的用途。例如上面的代码,顶点着色器的输出结构体中用 COLOR0 语义去描述 color 变量。color 变量本身存储了什么,Shader 流水线并不关心

  Unity 为了方便对模型数据的传输,对一些语义进行了特别的含义规定。例如,在顶点着色器的输入结构体 a2v 用 TEXCOORD0 来描述 texcoord,Unity 会识别 TEXCOORD0 语义,以把模型的第一组纹理坐标填充到 texcoord 中。需要注意的是,即便语义的名称一样,如果出现的位置不同,含义也不同。例如,TEXCOORD0 既可以用于描述顶点着色器的输入结构体 a2v,也可以用于描述输出结构体 v2f。但在输入结构体 a2v 中,TEXCOORD0 由特别的含义,即把模型的第一组纹理坐标存储在该变量中,而在输出结构体 v2f 中,TEXCOORD0 修饰的变量含义就可以由自己来决定

  在 DirectX 10 以后,有了一种新的于一类型,就是系统数值语义。这类语义是 SV 开头的,SV 代表的含义就是系统数值。这些语义在渲染流水线中由特殊的含义。例如上面的代码中使用 SV_POSITION 语义去修饰顶点着色器的输出变量 pos,那么就表示 pos 包含了可用于光栅化的变换后的顶点坐标(即齐次裁剪空间中的坐标)。用这些语义描述的变量是不可以随便赋值的,因为流水线需要使用它们来完成特定的目的,例如渲染引擎会把用 SV_POSITION 修饰的变量经过光栅化后显示在屏幕上。有时也可能会看到同一个变量在不同的 Shader 里面使用了不同的语义修饰。例如,一些 Shader 会使用 POSITION 而非 SV_POSITION 来修饰顶点着色器的输出。SV_POSITION 是 DirectX 10 中引入的系统数值语义,在绝大多数平台上,它和 POSITION 语义是等价的,但在某些平台上必须使用 SV_POSITION 来修饰顶点着色器的输出,否则无法让 Shader 正常工作。同样的例子还有 COLOR 和 SV_Target。因此,为了让 Shader 有更好的跨平台性,对于这些有特殊含义的变量最好使用以 SV 开头的语义进行修饰。

Unity 支持的语义

  下表总结了从应用阶段传递模型数据给顶点着色器时 Unity 使用的常用语义。这些语义虽然没有使用 SV 开头,但 Unity 内部赋予了它们特殊的含义

从应用阶段传递模型数据给顶点着色器时 Unity 支持的常用语义
语义描述
POSITION模型空间中的顶点位置,通常是 float4 类型
NORMAL顶点法线,通常是 float3 类型
TANGENT顶点切线,通常是 float4 类型
TEXCOORDn,如 TEXCOORD0、TEXCOORD1该顶点的纹理坐标,TEXCOORD0 表示第一组纹理坐标,以此类推。通常是 float2 或 float4 类型
COLOR顶点颜色,通常是 fixed4 或 float4 类型

  其中,TEXCOORDn 中 n 的数目是和 Shader Model 有关的,例如一般在 Shader Model 2(即 Unity 默认编译到的 Shader Model 5 中,n 等于 16。通常情况下,一个模型的纹理坐标组数一般不超过2,即往往只使用 TEXCOORD0 和 TEXCOORD1。在 Unity 内置的数据结构体 appdata_full 中,它最多使用了 6 个坐标纹理组

  下表总结了从顶点着色器阶段到片元着色器阶段 Unity 支持的常用语义

从顶点着色器传递数据给片元着色器时 Unity 使用的常用语义
语义描述
SV_POSITION裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量。等同于 DirectX 9 中的 POSITION,但最好使用 SV_POSITION
COLOR0通常用于输出第一组顶点颜色,但不是必需的
COLOR1通常用于输出第二组顶点颜色,但不是必需的
TEXCOORD0 ~ TEXCOORD7通常用于输出纹理坐标,但不是必需的

  上面的于一中,除了 SV_POSITION 是有特别含义外,其他语义对变量的含义没有明确要求,也就是说,可以存储任意值到这些语义描述变量中。通常,如果需要把一些自定义的数据从顶点着色器传递给片元着色器中,一般选用 TEXCOORD0 等

  下表给出了 Unity 中支持的片元着色器的输出语义

片元着色器输出时 Unity 支持的常用语义
语义描述
SV_Target输出值将会存储到渲染目标(render target)中。等同于 DirectX 9 中的 COLOR 语义,但最好使用 SV_Target

如何定义复杂的变量类型

  上面提到的语义绝大部分用于描述标量或适量类型的变量,例如 fixed2, float, float4, fixed4 等。下面的代码给出了一个使用语义

struct v2f{float4 pos : SV_POSITION;fixed3 color0 : COLOR0;fixed4 color1 : COLOR1;half value0 : TEXCOORD0;float2 value1 : TEXCOORD1;
};

  注意,一个语义可以使用的寄存器只能处理 4 个浮点值(float)。因此,如果想要定义矩阵类型,如 float3 * 4, float4 * 4 等变量就需要使用更多的空间。一种方法是,把这些变量拆分成多个变量,例如对于 float4 * 4 的矩阵类型,可以拆分成 4 个 float4 类型的变量,每个变量存储了矩阵中的一行数据

Debug

  本节在于给出 Unity 中对 Unity Shader 的调试方法,这主要包含了两种方法

使用假彩色图像

  假彩色图像指的是用假彩色技术生成的一种图像。与假彩色图像对应的的是照片这种真彩色图像。一张假彩色图像可以用于可视化一些数据,那么如何用它对 Shader 进行调试?

  主要思想是,可以把需要调试的变量映射到[0,1]之间,把它们作为颜色输出到屏幕上,然后通过屏幕上显示的像素颜色来判断这个值是否正确。这种方法得到的调试信息很模糊,能够得到的信息很有限,但在很长一段时间内,这种方法的确是唯一的可选方法。

  需要注意的是,由于颜色的分量范围在[0,1],因此需要小心处理需要调试的变量的范围。如果已知它的至于范围,可以先把它映射到[0,1]之间再进行输出。如果不知道一个变量的范围(这往往说明对这个 Shader 中的运算并不了解),就只能不停的实验。一个提示是,颜色分量重任何大于 1 的数值将会被设置为1,而任何小于 0 的数值会被设置为 0.因此可以尝试使用不同的映射,知道发现颜色发生了变化(这意味着得到了 0 ~ 1的值)。

  如果要调试的数据是一个以为数据,那么可以选择一个单独的颜色分量(如 R 分量)进行输出,而把其他颜色分量置为 0。如果是多维数据,可以选择对它的每一个分量单独调试,或者选择多个颜色分量进行输出。

  作为实例,下面会使用假彩色图像的方式来可视化一些模型数据,如法线、切线、纹理坐标、顶点颜色,以及它们之间的运算结果等。使用的代码如下:

Shader "Unity Shaders Book/Chapter 5/False Color"
{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f{float4 pos : SV_POSITION;fixed4 color : COLOR0;};v2f vert(appdata_full v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);//可视化法线方向o.color = fixed4(v.normal * 0.5 + fixed3(0.5,0.5,0.5),1.0);//可视化切线方向o.color = fixed4(v.tangent.xyz * 0.5 + fixed3(0.5,0.5,0.5),1.0);//可视化副切线方向fixed3 binormal = cross(v.normal,v.tangent.xyz) * v.tangent.w;o.color = fixed4(binormal * 0.5 + fixed3(0.5,0.5,0.5),1.0);//可视化第一组纹理坐标o.color = fixed4(v.texcoord.xy,0.0,1.0);//可视化第二组纹理坐标o.color = fixed4(v.texcoord.xy,0.0,1.0);//可视化第一组纹理坐标的小数部分o.color = frac(v.texcoord);if(any(saturate(v.texcoord) - v.texcoord)){o.color.b = 0.5;}o.color.a = 1.0;//可视化第二组纹理坐标的小数部分o.color = frac(v.texcoord1);if(any(saturate(v.texcoord1) - v.texcoord1)){o.color.b = 0.5;}o.color.a = 1.0;//可视化顶点颜色//o.color = v.colorreturn o;}fixed4 frag(v2f i) : SV_Target{return i.color;}ENDCG}}FallBack "Diffuse"
}

  上面的代码中使用了 Unity 内置的一个结构体——appdata_full。可以在 UnityCG.cginc 里找到它的定义

struct appdata_full{float4 vertex : POSITION;float4 targent : TANGENT;float3 nromal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;
#if defined(SHADER_API_XBOX360)half4 texcoord4 : TEXCOORD4;half4 texcoord5 : TEXCOORD5;
#endiffixed4 color : COLOR;
};

  可以看出,appdata_full 几乎包含了所有的模型数据

  把计算得到的加色彩存储到了顶点着色器的输出结构体——v2f 中的 color 变量里,并且在片元着色器中输出了这个颜色。可以对其中的代码添加或取消注释,管擦和不同运算和数据得到的效果。下图给出了这些代码得到的显示效果。

在这里插入图片描述
  为了得到某点的颜色值,可以使用类似颜色拾取器的脚本得到屏幕上某点的RGBA值。从而推断出该点的调试信息,如下图所示

在这里插入图片描述

利用神器:Visual Studio

  在 Visual Studio 中也提供了对 Unity Shader 的调试功能——Graphics Debugger

  通过 Graphics Debugger,不仅可以查看每个像素的最终颜色、位置等信息,还可以对顶点着色器和片元着色器进行单步调试。

  当然,本方法也有一些限制。例如,需要保证 Unity 运行在 DirectX 11 平台上,而且 Graphics Debugger 本身存在一些 bug

最新利器:帧调试器

  Unity 5除了带来全新的UI系统外,还带来了一个新的针对渲染的调试器——帧调试器。与其他调试工具的复杂性相比,Unity原生的帧调试器非常简单快捷。可以使用它来看到游戏图像的某一帧是如何一步一步渲染出来的

  要使用帧调试器,首先需要在 Window -> Frame Debugger 中打开帧调试器,如下图所示

在这里插入图片描述
  帧调试器可以用于查看渲染该帧时进行的各种渲染时间,这些事件包含了 Draw Call 序列,也包括了类似清空帧缓存等操作。帧调试器窗口大致可分为 3 个部分:最上面的区域可以开启 / 关闭(单击 Enable 按钮)帧调试功能,当开启了帧调试时,通过移动窗口最上方的滑动条(或单击前进和后退按钮),可以重放这些渲染事件;左侧的区域显示了所有时间的树状图,在这个树状图中,每个叶子节点就是一个事件,而每个父节点的右侧显示了该节点下的时间数目。可以从事件的名字了解这个事件的操作,例如以 Draw 开头的事件通常就是一个 Draw Call;当单击了某个事件时,在右侧的窗口中就会显示出该事件的细节,例如几何图形的细节以及使用了哪个 Shader 等。同时在 Game 视图中也可以看到它的效果。如果该事件是一个 Draw Call 并且对应了场景中的一个 GameObject,那么这个 GameObject 也会在 Hierarchy 视图中被高亮显示出来,下图显示了单击渲染某个对象的深度图事件的结果

在这里插入图片描述
  如果被选中的 Draw Call 是对一个渲染纹理的渲染操作,那么这个渲染纹理就会显示在 Game 视图中。并且,此时右侧面板上方的工具栏也会出现更多的选项。例如在 Game 视图中单独显示 R、G、B、A 通道

  Unity 5 提供的帧调试器实际上并没有实现一个真正的帧拾取的功能,二十仅仅使用停止渲染的方法来查看渲染时间的结果。例如:如果想要查看第 4 个 Draw Call 的结果,那么帧调试器就会在第 4 个 Draw Call 调用完毕后停止渲染。这种方法虽然简单,但得到的信息也很有限。如果想要获取更多的信息,还是需要使用外部工具

小心渲染平台的差异

渲染纹理的坐标差异

  在前文提到过 OpenGL 和 DirectX 的屏幕空间坐标的差异。在水平方向上,两者的数值变化方向是相同的,但在竖直方向上,两者是相反的。在 OpenGL(OpenGLES 也是)中,(0,0) 点对应了屏幕的左下角,而在 DirectX(Metal 也是)中,(0,0) 点对应了左上角,如下图所示

在这里插入图片描述
  需要注意的是,不仅可以把渲染结果输出到屏幕上,还可以输出到不同的渲染目标中。这是需要使用渲染纹理来保存这些渲染结果。

  大多数情况下,这样的差异并不会造成任何影响,但当使用渲染到纹理技术,把屏幕图像渲染到一张渲染纹理中时,如果不采取任何措施的话,就会出现纹理翻转的情况。幸运的是,Unity处理了这种反转问题——当在 DirectX 平台上使用渲染到纹理技术时,Unity会翻转屏幕图像纹理,以便在不同平台上达到一致性。

  在一种特殊情况下 Unity 不会进行这个翻转操作,这种情况就是开启了抗锯齿(在 Edit -> Project Settings -> Quality -> Anti Aliasing 中开启)并在此时使用了渲染到纹理技术。在这种情况下,Unity 首先渲染得到屏幕图像,再由硬件进行抗锯齿处理后,得到一张渲染纹理来供后续处理。此时,在 DirectX 平台下,得到的输入屏幕图像并不会被 Unity 翻转,也就是说,此时对屏幕图像额采样坐标时需要符合 Direct X 平台规定的。如果屏幕特效只需要处理一张渲染图像,仍然不需要在意纹理的翻转问题,这是因为在调用 Graphics.Blit 函数时,Unity 已经对屏幕图像的采样坐标进行了处理,只需要按正常的采样过程处理屏幕图像即可。但如果需要同时处理多张渲染图象(前提是开启了抗锯齿),例如需要同时处理屏幕图像和发现处理,这些图像在竖直方向的朝向就可能是不同的(只有在 DirectX 这样的平台上才有这样的问题)。这种时候就需要在顶点着色器中翻转某些渲染纹理(例如深度纹理或其他由脚本传递过来的纹理)的纵坐标,使之都符合 DirectX 平台的规则。例如:

#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)uv.y = 1 - uv.y
#endif

  其中,UNITY_UV_STARTS_AT_TOP 用于判断当前平台是否是 DirectX 类型的平台,而当在这样的平台下开启了抗锯齿后,主纹理的纹素大小在竖直方向上会变成负值,以方便我们对主纹理进行正确的采样。因此,我们可以通过判断_MainTex_TexelSize.y 是否小于 0 来检验是否开启了抗锯齿。如果是,我们就需要对除主纹理外的其他纹理的采样进行数值方向上的翻转。

Shader的语法差异

  在 Windows 平台下编译某些在 Mac 平台下工作良好的 Shader 时,可能会看到类似下面的报错信息:

incorrect number of arguments to numeric-type constructor(compiling for d3dll)

  或者

output parameter 'o' not completely initialized (compling for d3dll)

 &emsp上面的报错都是因为 DirectX 9/11 对 Shader 的语义更加严格造成的。例如,造成第一个报错的原因是,Shader 中可能存在下面这样的代码:

// v 是 float4 类型,但在它的构造器中仅提供了一个参数
float4 v = float4(0.0);

  在 OpenGL 平台上,上面的代码是合法的,它将得到一个 4 个分量都是 0.0 的 float4 类型的变量。但在 DirectX 11 平台上,必须提供和变量类型相匹配的参数数目,也就是说,应该写成:

float4 v = float4(0.0, 0.0, 0.0, 0.0);

  对于第二个报错信息,往往是出现在表面着色器中。表面着色器的顶点函数(注意,不是顶点着色器)有一个使用了 out 修饰符的参数。如果出现这样的报错信息,可能是因为在顶点函数中没有对这个参数的所有成员变量都进行初始化。应该使用类似下面的代码来对这些参数进行初始化:

void vert(inout appdata)ull v, out Input o){// 使用 Unity 内置的 UNITY_INITIALIZE_OUTPUT 宏对输出结构体 o 进行初始化UNITY_INITIALIZE_OUTPUT(Input,o);// ...
}

  除了上述两点语法不同外,DirectX 9/11 也不支持在顶点着色器中使用 tex2D 函数。tex2D 是对一个对纹理进行采样的函数。之所以 DirectX 9/11 不支持顶点阶段中的 tex 2D 运算,是因为在顶点着色器阶段 Shader 无法得到 UV 偏导,而 tex2D 函数需要这样的偏导信息(这和纹理采样时使用的数学运算有关)。如果的却需要在顶点着色器中访问纹理,需要使用 tex2Dlod 函数来代替,如:

tex2Dlod(tex,float4(uv, 0, 0))

  而且还需要添加 #pragma target 3.0,因为 tex2Dlod 是 Shader Model 3.0 中的特性

Shader 的语义差异

  在之前讲到了 Shader 中的语义是什么,其中讲到了一些语义在某些平台是等价的,例如 SV_POSITION 和 POSITION。但在另一平台上,这些语义是不等价的。为了让 Shader 能够在所有平台正常工作,应该尽可能使用下面的语义来描述 Shader 的输入输出变量。

  • 使用 SV_POSITION 来描述顶点着色器输出的顶点位置。一些 Shader 使用了 POSITION 语义,但这些 Shader 无法在索尼 PS4 平台上或使用了细分着色器的情况下正常工作。
  • 使用 SV_Target 来描述片元着色器的输出颜色。一些 Shader 使用了 COLOR 或者 COLOR0 语义,同样的,这些 Shader 无法在索尼 PS4 上正常工作

Shader 整洁之道

float、half 还是 fixed

  在这里使用 Cg/HLSL 来编写 Unity Shader 中的代码。而在 Cg/HLSL 中,有 3 种精度的数值类型:float,half 和 fixed。这些精度将决定计算结果的数值范围。下表给出了这 3 种精度在通常情况下的数值范围

Cg/HLSL 中 3 种精度的数值类型
类型精度
flaot最高精度的浮点值。通常使用 32 位来存储
half中等精度的浮点值。通常使用 16 位来存储,精度范围是 -60000 ~ +60000
fixed最低精度的浮点值。通常使用 11 位来存储,精度范围是 -2.0 ~ +2.0

  上面的精度范围并不是绝对正确的,尤其是在不同平台和 GPU 上,他们实际的精度可能和上面给出的范围不一致。通常来讲:

  • 大多数现代的桌面 GPU 会把所有计算都按最高的浮点精度进行计算,也就是说,float、half、fixed 在这些平台上实际上是等价的。这意味着,在 PC 平台上很难看出因为 half 和 fixed 精度而带来的不同
  • 但在移动平台的 GPU 上,它们的确会有不同的精度范围,而且不同精度的浮点值的运算速度也会有所差异。因此应该确保在真正的移动平台上验证 Shader。
  • fixed 精度实际上只在一些较旧的移动平台上有用,在大多数现代的 GPU 上,它们内部把 fixed 和 half 当成同等精度来对待

  尽管有上面的不同,但一个基本建议是,尽可能使用精度较低的类型,因为这可以优化 Shader 的性能,这一点在移动平台上尤其重要。从它们大体的值域范围来看,可以使用 fixed 类型来存储颜色和单位矢量,如果要存储更大范围的数据可以选择 half 类型,最差情况下再选择使用 float。如果目标平台是移动平台,一定要确保在真实的手机上测试 Shader,这一点非常重要。

规范语法

  因为 DirectX 平台对 Shader 的语义有更加严格的要求。这意味着,如果要发布到 DirectX 平台上就需要使用更严格的语法。

避免不必要的计算

  如果毫无节制的在 Shader(尤其是片元着色器)中进行了大量计算,那么可能很快就会收到 Unity 的错误提示:

temporary register limit of 8 exceeded

  或

Arithmetic instruction limit of 64 exceeded; 65 arithmetic instructions needed to compile program

  出现这些错误信息大多是因为在 Shader 中进行了过多的运算,使得重要的临时寄存器数目或指令数目超过了当前可支持的数目。需要知道,不同的 Shader Target、不同的着色器阶段,可使用的临时寄存器和指令数目都是不同的

  通常可以通过更高等级的 Shader Target 来消除这些错误。下表给出了 Unity 目前支持的一些 Shader Target

Unity 支持的 Shder Target
指令描述
#pragma target 2.0默认的 Shader Target 等级。相当于 Direct3D 8 上的 Shader Model 2.0,不支持对顶点纹理的采样,不支持显示的 LOD 纹理采样等
#pragma target 3.0相当于 Direct3D 9 上的 Shader Model 3.0,支持对顶点纹理的采样等
#pragma target 4.0相当于 Direct3D 10 上的 Shader Model 4.0,支持几何着色器等
#pragma target 5.0相当于 Direct3D 11 上的 Shader Model 5.0

  需要注意的是,由于 Unity 版本的不同,Unity 支持的 Shader Target 种类也不同

  虽然更高等级的 Shader Target 可以使用更多的临时寄存器和运算指令,但一个更好的方法是尽可能减少 Shader 中的运算,或者通过预计算的方式来提供更多的数据

慎用分支和循环语句

  在最开始,GPU 是不支持在顶点着色器和片元着色器中使用流程控制语句的。随着 GPU 的发展,现在可以使用 if-else、for 和 while 这种流程控制指令了。但是,它们在 GPU 上的实现和在 CPU 上有很大的不同。大体来说,GPU 使用了不同于 CPU 的技术来实现分支语句,在最坏的情况下花在一个分支语句的时间相当于运行了所有分支语句的时间。因此不鼓励在 Shader 中使用流程控制语句,因为它们会降低 GPU 的并行处理操作(尽管在现代的 GPU 上已经有了改进)

  如果在 Shader 中使用了大量的流程控制语句,那么这个 Shader 的性能可能会成倍下降。一个解决方法是尽量把计算向流水线上端移动,例如把片元着色器中的计算放到顶点着色器中,或者直接在 CPU 中进行预计算,再把结果传递给 Shader。淡然,有时不可避免的要使用分支语句来进行运算,那么一些建议是:

  • 分支判断语句中使用的条件变量最好是常熟,即在 Shader 运行过程中不会发生变化;
  • 每个分支中包含的操作指令数尽可能少;
  • 分支的嵌套层数尽可能少

不要除以 0

  虽然在类似 C# 等高级语言进行编程的时候会谨记不要除以0这个基本常识(就算不这么做,编译器可能也会报错),但在编写 Shader 的时候有时会忽略这个问题

  例如,如下代码:

fixed4 frag(v2f i) : SV_Target{return fixed4(0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0, 1.0);
}

  这样代码的结果往往是不可预测的。在某些渲染平台上,上面的代码不会造成 Shader 的崩溃,但即便不会崩溃得到的结果也是不确定的,有时会得到白色(由无限大截取到1),有些会得到黑色,但在另一些平台上,Shader 可能会直接崩溃。因此,即便在开发游戏的平台上,看到的结果可能是符合预期的,但在目标平台上可能就会出现问题

  一个解决方法是,对那些除数可能为 0 的情况,强制截取到非 0 范围。一个方法是使用 if 语句来判断除数是否为 0 的例子。另一个方法是,使用一个很小的浮点值,例如 0.000001 来保证分母大于 0(前提是原始数值是非负数)。

相关文章:

UnityShader——初级篇之开始Unity Shader学习之旅

开始Unity Shader学习之旅 一个最简单的顶点/片元着色器顶点/片元着色器的基本结构模型数据从哪里来顶点着色器和片元着色器之间如何通信如何使用属性 强大的援手&#xff1a;Unity 提供的内置文件和变量内置的包含文件内置的变量 Unity 提供的 Cg/HLSL 语义什么是语义Unity 支…...

Burp入门(6)-自动化漏洞测试理论

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 声明&#xff1a;本文主要用作技术分享&#xff0c;所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险&#xff0c;并遵循相关法律法规。 感谢泷…...

【git】git 客户端设置本地缓冲区大小

文章目录 1. 报错2. 解决&#xff1a;增加本地缓冲区 1. 报错 git -c core.quotepathfalse -c log.showSignaturefalse push --progress --porcelain origin refs/heads/master:master Enumerating objects: 17, done. Counting objects: 5% (1/17) Counting objects: 11% …...

Xcode——LLDB Debugger 与断点调试学习

Xcode——LLDB Debugger 与断点调试学习 文章目录 Xcode——LLDB Debugger 与断点调试学习前言介绍打开LLDB命令helpprintexpression 断点调试异常断点——Exception Breakpoint标志断点——Symbolic Breakpointwatchpointset 断点行为condition条件判断 最后参考文章 前言 在…...

linux安全管理-系统环境安全

1 历史命令设置 1、检查内容 检查操作系统的历史命令设置。 2、配置要求 建议操作系统的历史命令设置。 3、配置方法 编辑/etc/profile 文件&#xff0c;配置保留历史命令的条数 HISTSIZE 和保留历史命令的记录文件大小 HISTFILESIZE&#xff0c;这两个都设置为 5。 配置方法如…...

【Maven】依赖冲突如何解决?

准备工作 1、创建一个空工程 maven_dependency_conflict_demo&#xff0c;在 maven_dependency_conflict_demo 创建不同的 Maven 工程模块&#xff0c;用于演示本文的一些点。 什么是依赖冲突&#xff1f; 当引入同一个依赖的多个不同版本时&#xff0c;就会发生依赖冲突。…...

学习视频超分辨率扩散模型中的空间适应和时间相干性(原文翻译)

文章目录 摘要1. Introduction2. Related Work3. Our Approach3.1. Video Upscaler3.2. Spatial Feature Adaptation Module3.3. Temporal Feature Alignment Module3.4. Video Refiner3.5. Training Strategy 4. Experiments4.1. Experimental Settings4.2. Comparisons with …...

MFC工控项目实例三十四模拟量实时监控数字显示效果

点击监控按钮&#xff0c;对选中模拟量用数字显示效果实时显示数值。 SenSet.cpp中相关代码 UINT m_nCounterID_1[6] { IDC_STATIC0,IDC_STATIC1,IDC_STATIC2,IDC_STATIC3,IDC_STATIC4,IDC_STATIC5,};UINT m_nCounterID_2[7] { IDC_STATIC7,IDC_STATIC8,IDC_STATIC9,IDC_S…...

Z2400032基于Java+Mysql+SSM的校园在线点餐系统的设计与实现 代码 论文

在线点餐系统 1.项目描述2. 技术栈3. 项目结构后端前端 4. 功能模块5. 项目实现步骤注意事项 6.界面展示7.源码获取 1.项目描述 本项目旨在开发一个校园在线点餐系统&#xff0c;通过前后端分离的方式&#xff0c;为在校学生提供便捷的餐厅点餐服务&#xff0c;同时方便餐厅和…...

Linux Deploy安装Debian桌面

下载安装Linux Deploy 下载地址 https://github.com/lateautumn233/Linuxdeploy-Pro/releases/download/3.1.0/app-debug.apk 配置 发行版本&#xff1a;Debian架构&#xff1a;arm64发行版版本&#xff1a;bookworm源地址&#xff1a;http://mirrors.aliyun.com/debian/安装…...

C语言数据相关知识:静态数据、越界与溢出

1、静态数组 在 C 语言中&#xff0c;数组一旦被定义后&#xff0c;占用的内存空间就是固定的&#xff0c;容量就是不可改变的&#xff0c;既不能在任何位置插入元素&#xff0c;也不能在任何位置删除元素&#xff0c;只能读取和修改元素&#xff0c;我们将这样的数组称为静态…...

纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架

前言​ 开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C 库&#xff0c;如 ​​OpenCV​​ 或 ​​​dlib​​​&#xff0c;但通过 ​​​cgo​​​ 调用 C 程序会引入巨大的延迟&#xff0c;并在性能方面产生显著的权衡。…...

华为ACL应用笔记

1、基本ACL 2000-2999 基本ACL&#xff08;Access Control List&#xff0c;访问控制列表&#xff09;是一种网络安全技术&#xff0c;它根据源IP地址、分片信息和生效时间段等信息来定义规则&#xff0c;对报文进行过滤。 规则&#xff1a; ACL由一系列规则组成&#xff0c;每…...

Axios:现代JavaScript HTTP客户端

在当今的Web开发中&#xff0c;与后端服务进行数据交换是必不可少的。Axios是一个基于Promise的HTTP客户端&#xff0c;用于浏览器和node.js&#xff0c;它提供了一个简单的API来执行HTTP请求。本文将介绍Axios的基本概念、优势、安装方法、基本用法以及如何使用Axios下载文件。…...

Qml-TabBar类使用

Qml-TabBar类使用 TabBar的概述 TabBar继承于Container 由TabButton进行填充&#xff0c;可以与提供currentIndex属性的任何容器或布局控件一起使用&#xff0c;如StackLayout 或 SwipeView&#xff1b;contentHeight : real:TabBar的内容高度&#xff0c;用于计算标签栏的隐…...

qt QGraphicsEllipseItem详解

1、概述 QGraphicsEllipseItem是Qt框架中QGraphicsItem的一个子类&#xff0c;它提供了一个可以添加到QGraphicsScene中的椭圆项。QGraphicsEllipseItem表示一个带有填充和轮廓的椭圆&#xff0c;也可以用于表示椭圆段&#xff08;通过startAngle()和spanAngle()方法&#xff…...

单链表---移除链表元素

对于无头单向不循环链表&#xff0c;给出头结点head与数值val&#xff0c;删除链表中数据值val的所有结点 #define ListNodeDataType val struct ListNode { struct ListNode* psll;ListNodeDataType val; } 方法一---遍历删除 移除所有数值为val的链表结点&#xff0c;…...

Kafka知识体系

一、认识Kafka 1. kafka适用场景 消息系统&#xff1a;kafka不仅具备传统的系统解耦、流量削峰、缓冲、异步通信、可扩展性、可恢复性等功能&#xff0c;还有其他消息系统难以实现的消息顺序消费及消息回溯功能。 存储系统&#xff1a;kafka把消息持久化到磁盘上&#xff0c…...

Micopython与旋转按钮(Encoder)

一、 encoder.py文件 CLK pin attached to GPIO12DT pin attached to GPIO13GND pin attached to GND 旋转编码器s1->CLK s2->DTimport time from rotary_irq_esp import RotaryIRQ r = RotaryIRQ(pin_num_clk=12, #clk引脚 pin_num_dt=13, #dat…...

联想Lenovo SR650服务器硬件监控指标解读

随着企业IT架构的复杂性和业务需求的增长&#xff0c;服务器的稳定运行变得至关重要。联想Lenovo SR650服务器以其高性能和稳定性&#xff0c;在各类应用场景中发挥着关键作用。为了保障服务器的稳定运行&#xff0c;监控易作为一款专业的IT基础设施监控软件&#xff0c;为联想…...

RAG数据拆分之PDF

引言RAG数据简介PDF解析方法及工具代码实现总结 二、正文内容 引言 本文将介绍如何将RAG数据拆分至PDF格式&#xff0c;并探讨PDF解析的方法和工具&#xff0c;最后提供代码示例。 RAG数据简介 RAG&#xff08;关系型属性图&#xff09;是一种用于表示实体及其关系的图数据…...

基于STM32的传感器数据采集系统设计:Qt、RS485、Modbus Rtu协议(代码示例)

一、项目概述 项目目标与用途 本项目旨在设计并实现一个基于STM32F103RCT6微控制器的传感器数据采集系统。该系统通过多个传感器实时监测环境参数&#xff0c;并将采集到的数据传输至上位机进行处理和分析。系统的主要应用领域包括环境监测、工业控制、智能家居等。通过该系统…...

【计网不挂科】计算机网络——<34道经典简述题>特训

前言 大家好吖&#xff0c;欢迎来到 YY 滴计算机网络 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 本博客主要内容&#xff0c;收纳了一部门基本的计算机网络题目&#xff0c;供yy应对期中考试复习。大家可以参考 本章为分章节的习题内容题库&#x…...

Spring Web开发(请求)获取JOSN对象| 获取数据(Header)

大家好&#xff0c;我叫小帅今天我们来继续Spring Boot的内容。 文章目录 1. 获取JSON对象2. 获取URL中参数PathVariable3.上传⽂件RequestPart3. 获取Cookie/Session3.1 获取和设置Cookie3.1.1传统获取Cookie3.1.2简洁获取Cookie 3. 2 获取和存储Session3.2.1获取Session&…...

算法训练营day22(二叉树08:二叉搜索树的最近公共祖先,插入,删除)

第六章 二叉树part08 今日内容&#xff1a; ● 235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点 详细布置 235. 二叉搜索树的最近公共祖先 相对于 二叉树的最近公共祖先 本题就简单一些了&#xff0c;因为 可以利用二叉搜索树的…...

【论文阅读】 Learning to Upsample by Learning to Sample

论文结构目录 一、之前的上采样器二、DySample概述三、不同上采样器比较四、整体架构五、设计过程&#xff08;1&#xff09;初步设计&#xff08;2&#xff09;第一次修改&#xff08;3&#xff09;第二次修改&#xff08;4&#xff09;第三次修改 六、DySample四种变体七、复…...

Android 图形系统之五:Gralloc

Gralloc (Graphics Allocator) 是 Android 系统中的关键组件之一&#xff0c;用于管理图形缓冲区的分配、映射以及处理。在 Android 的图形架构中&#xff0c;Gralloc 充当了 HAL (Hardware Abstraction Layer) 的一部分&#xff0c;为系统和硬件提供了通用的接口&#xff0c;使…...

【大数据学习 | Spark调优篇】Spark之内存调优

1. 内存的花费 1&#xff09;每个Java对象&#xff0c;都有一个对象头&#xff0c;会占用16个字节&#xff0c;主要是包括了一些对象的元信息&#xff0c;比如指向它的类的指针。如果一个对象本身很小&#xff0c;比如就包括了一个int类型的field&#xff0c;那么它的对象头实…...

Spring Data JPA(一) 基础入门

Spring Data JPA&#xff08;一&#xff09; 基础入门 JPA 的全称是 Java Persistence API , 即 Java 持久层 API。Spring Data JPA 是 Spring 生态中提出的一套数据库 ORM &#xff08;对象关系映射&#xff09;规范、抽象标准&#xff0c;或者说它是对ORM框架实现的顶层抽象…...

Flutter | 基于函数式编程的通用单选列表设计

背景 项目中多次用到如下图的通用单选列表页&#xff1a; 常规封装 此列表需要三样东西&#xff1a; 标题数组当前选中项的 index点击 cell 的回调 封装大体如下&#xff1a; import package:flutter/material.dart;class ListPage1 extends StatefulWidget {const ListPa…...

华三防火墙F1000-AK系列策略路由配置案例(WEB)

1 配置需求或说明 1.1 适用的产品系列 本案例适用于如F1000-AK180、F1000-AK170等F1000-AK系列的防火墙。 1.2 配置需求及实现的效果 防火墙作为网络出口设备,外网有移动和联通两条线路。内网有192.168.1.0和192.168.2.0两个网段,需要实现192.168.1.0网段走移动线路,192…...

Oracle 锁表的解决方法及避免锁表问题的最佳实践

背景介绍 在 Oracle 数据库中&#xff0c;锁表或锁超时相信大家都不陌生&#xff0c;是一个常见的问题&#xff0c;尤其是在执行 DML&#xff08;数据操作语言&#xff09;语句时。当一个会话对表或行进行锁定但未提交事务时&#xff0c;其他会话可能会因为等待锁资源而出现超…...

深度学习中的生成对抗网络(GAN)原理与应用

引言 生成对抗网络&#xff08;Generative Adversarial Network&#xff0c;简称GAN&#xff09;是由Ian Goodfellow等人在2014年提出的一种深度学习模型&#xff0c;它通过对抗训练的方式生成与真实数据分布相似的假数据。GAN的出现极大地推动了深度学习和生成模型的研究&…...

Swing中JScrollPane面板

一、介绍 在设置界面时&#xff0c;可能会遇到在一个较小的容器窗体中显示一个较大部分的内容的情况&#xff0c;这时可使用JScrollPane面板。JScrollPane面板是带滚动条的面板&#xff0c;是一种容器&#xff0c;但是JScrollPane只能放置一个组件&#xff0c;并且不可使用布局…...

【学习笔记】检测基于RTOS的设计中的堆栈溢出-第2部分

有许多技术可用于检测堆栈溢出。有些使用硬件,而有些则完全在软件中执行。正如我们很快将看到的那样,在硬件中具有这种能力到目前为止是更可取的,因为堆栈溢出可以在发生时立即检测到,事实上,可以避免,因为硬件实际上可以防止对无效访问的写入。 硬件堆栈溢出检测机制通…...

PHP 函数

在php中有非常多的函数&#xff0c;函数这种东西不需要记全&#xff0c;直到怎么使用就行了&#xff0c;如果想了解多点函数&#xff0c;可以查看php官方函数手册&#xff0c;或者参考菜鸟PHP 5 Array 函数 | 菜鸟教程。 创建 PHP 函数 通常函数创建完毕后是用来调用。 语法格…...

centos更换源文件,换源,替换源

期初怎么折腾就是不行&#xff0c;换了源也是不能使用的&#xff0c;最后发现不是换的源不行&#xff0c;而是之前的源文件不行&#xff0c;然后给所有的源文件在yum源统一放在了bak目录下&#xff0c;随后我们再去下载安装源文件。 您将yum源下载之后&#xff0c;先将您的其他…...

【深度学习】四大图像分类网络之VGGNet

2014年&#xff0c;牛津大学计算机视觉组&#xff08;Visual Geometry Group&#xff09;和Google DeepMind公司一起研发了新的卷积神经网络&#xff0c;并命名为VGGNet。VGGNet是比AlexNet更深的深度卷积神经网络&#xff0c;该模型获得了2014年ILSVRC竞赛的第二名&#xff0c…...

线性表-链式描述(C++)

链式实现的线性表&#xff1a; 链式实现的线性表&#xff0c;即链表&#xff08;Linked List&#xff09;&#xff0c;是一种通过节点&#xff08;Node&#xff09;的集合来存储数据的线性数据结构。在链表中&#xff0c;每个节点包含两部分&#xff1a;存储数据的域&#xff…...

C++高阶算法[汇总]

&#xff08;一&#xff09;高精度算法概述 高精度算法是指能够处理超出常规数据类型表示范围的数值的算法。在 C 中&#xff0c;标准数据类型通常有固定的位数和精度限制&#xff0c;而高精度算法可以解决大数运算、金融计算和科学计算等领域的问题。 &#xff08;二&#x…...

机器学习之DeepMind推出的DreamerV3

开放域任务强化学习(Open-Ended Task Reinforcement Learning)的目标是使智能体能够在多样化且未见过的任务中表现出色,同时能够实现任务间的迁移学习。这类研究的重点在于开发通用的学习算法,能够在没有明确任务定义的情况下,从环境中学习并推广到新任务。DeepMind的Drea…...

【Zookeeper】四,Zookeeper节点类型、通知、仲裁、会话

文章目录 Zookeeper的架构znode的版本Zookeeper的节点类型层级树状结构znode的不同类型 Zookeeper监视与通知通知的类型 Zookeeper的仲裁Zk的会话会话的生命周期 Zookeeper的架构 Zookeeper的服务器端运行两种模式&#xff1a;独立模式&#xff08;standalone&#xff09;和仲…...

Vue 集成和使用 SQLite 的完整指东

1. 引言 SQLite 是一种轻量级的关系型数据库管理系统&#xff0c;以其简单易用、无需服务器等特点广泛应用于嵌入式系统、移动应用和小型应用程序中。在 Web 开发中&#xff0c;尤其是前端应用开发中&#xff0c;SQLite 可以作为客户端本地存储的一种选择&#xff0c;为用户提…...

CMAKE常用命令详解

NDK List基本用法 Get–获取列表中指定索引的元素 list(Get list_name index output_var)解释 list_name: 要操作集合的名称index: 要取得的元素下标output_var: 保存从集合中取得元素的结果 栗子 list(GET mylist 0 first_element) # 获取第一个元素APPEND–在列表末尾…...

【嵌入式——QT】QT制作安装包

第一步 QT程序写好之后&#xff0c;编译release版本 第二步 拿到release生成的.exe文件 第三步 新建文件夹deploy 第四步 将.exe文件复制到deploy目录下 第五步 在该目录下输入cmd指令&#xff0c;回车 第六步 在打开的命令窗口下输入 windeployqt TegNetCom_1.0.…...

JavaScript 前端开发:从入门到精通的奇幻之旅

目录 一、引言 二、JavaScript 基础 &#xff08;一&#xff09;变量与数据类型 &#xff08;二&#xff09;运算符 &#xff08;三&#xff09;控制结构 三、函数 &#xff08;一&#xff09;函数定义与调用 &#xff08;二&#xff09;函数作用域 &#xff08;三&am…...

shell编程基础笔记

目录 echo改字体颜色和字体背景颜色 bash基本功能&#xff1a; 运行方式&#xff1a;推荐使用第二种方法 变量类型 字符串处理&#xff1a; 条件判断&#xff1a;&#xff08;使用echo $?来判断条件结果&#xff0c;0为true&#xff0c;1为false&#xff09; 条件语句&a…...

FPGA实现GTP光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案本博已有的FPGA驱动USB通信方案 3、工程详细设计方案工程设计原理框图输入Sensor之-->OV5640摄像头输入Sensor之-->芯片解码的…...

使用 PDF API 合并 PDF 文件

内容来源&#xff1a; 如何在 Mac 上合并 PDF 文件 1. 注册与认证 您可以注册一个免费的 ComPDFKit API 帐户&#xff0c;该帐户允许您在 30 天内免费无限制地处理 1,000 多个文档。 ComPDFKit API 使用 JSON Web Tokens 方法进行安全身份验证。从控制面板获取您的公钥和密钥&…...

Jenkins Nginx Vue项目自动化部署

目录 一、环境准备 1.1 Jenkins搭建 1.2 NVM和Nodejs安装 1.3 Nginx安装 二、Jenkins配置 2.1 相关插件安装 2.2 全局工具安装 2.3 环境变量配置 2.4 邮箱配置&#xff08;构建后发送邮件&#xff09; 2.5 任务配置 三、Nginx配置 3.1 配置路由转发 四、部署项目 …...