第3章 Unity 3D着色器系统
3.1 从一个外观着色器程序谈起
新建名为basic_diffuse.shader的文件,被一个名为basic_diffuse.mat的材质文件所引用,而basic_diffuse.mat文件则被场景中名为Sphere的game object的MeshRenderer组件所使用。
basic_diffuse.shader代码文件的内容如下所示。
Shader "Custom/BasicDiffuse"{Properties{_EmissiveColor ("Emissive Color", Color) = (1,1,1,1)_MainTex ("Main Texture", 2D) = "white"{}}SubShader{Tags { "RenderType"="Opaque" "RenderType"="Opaque" }LOD 200CGPROGRAM#pragma surface surf BasicDiffuse vertex:vert finalcolor:final noforwardadd#pragma debug// 指定自身的自发光颜色float4_EmissiveColor;// 指定第一层纹理的映射坐标sampler2D_MainTex;// 由顶点着色器主入口函数在调用vert函数时获取到// 由片元着色器主入口函数在调用surf函数时作为参数传入structInput{float2uv_MainTex;};// 本函数在顶点着色器主入口函数中被调用voidvert(inoutappdata_full v,// appdata_full类型的参数是Unity 3D预定义的数据类型outInput o) // 由本函数返回,并在顶点着色器主入口函数中用来初始//化一个v2f_surf变量{o.uv_MainTex = v.texcoord.xy;}// 本函数在片元着色器主入口函数中被调用voidsurf (Input IN, // 根据传递给片元着色器主入口函数的v2f_surf类型的参数变量初始化而成inoutSurfaceOutput o) // 本函数内赋值完成后返回{o.Albedo = (_EmissiveColor.rgb + tex2D(_MainTex, IN.uv_MainTex).rgb);o.Alpha = _EmissiveColor.a;}// 本函数在片元着色器主入口函数中被调用inline float4LightingBasicDiffuse (SurfaceOutput s, // 此变量在片元着色器入口主函数内,调用surf获取到fixed3lightDir, // 光源到本片元的位置连线向量,由着色器编译器展开计算生成// 计算得到此变量的代码在片元着// 色器主入口函数中fixedatten) // 光线的衰减值,由着色器编译器展开计算生成// 计算得到此变量的代码在片元着色器主入口函数中{floatangle_cos = max(0, dot(s.Normal, lightDir));float4col;col.rgb = s.Albedo.rgb * _LightColor0.rgb * angle_cos * atten;col.a = s.Alpha;returncol;}voidfinal(Input IN, SurfaceOutput o, inoutfixed4 color){}ENDCG}FallBack "Diffuse"}
观察效果。
3.1.1 BasicDiffuse着色器展开后的代码分析
1. BasicDiffuse着色器的生成代码:原始着色器中定义的函数和变量
在每个编译完成的着色器的Inspector面板上,都有一个Show generated code按钮,单击Show generated code按钮后,原始着色器中定义的变量和函数也会被植入生成的代码中。(代码略,可自行生成查看)
2. BasicDiffuse着色器的生成代码:顶点着色器传递给片元着色器的数据
引擎会针对在原始的外观着色器中定义的Input内容,依据各种编译指示符指定的光照模型和其他参数,对应生成一个v2f_surf结构体。该结构体由顶点着色器返回,传递给片元着色器。
如下述代码所示:
// 从顶点着色器传递给片元着色器的数据结构体
#ifndef LIGHTMAP_ON
// 不使用烘焙光照贴图时的顶点着色器传递给片元着色器的数据结构体structv2f_surf{UNITY_POSITION(pos);float2pack0 : TEXCOORD0; // _MainTexhalf3worldNormal : TEXCOORD1;float3worldPos : TEXCOORD2;fixed3vlight : TEXCOORD3; // ambient/SH/vertexlightsUNITY_SHADOW_COORDS(4)#if SHADER_TARGET >= 30float4lmap : TEXCOORD5;#endifUNITY_VERTEX_INPUT_INSTANCE_IDUNITY_VERTEX_OUTPUT_STEREO};
#endif
#ifdef LIGHTMAP_ON
// 使用烘焙光照贴图时的顶点着色器传递给片元着色器的数据结构体structv2f_surf{UNITY_POSITION(pos);float2pack0 : TEXCOORD0; // _MainTexhalf3worldNormal : TEXCOORD1;float3worldPos : TEXCOORD2;float4lmap : TEXCOORD3;UNITY_SHADOW_COORDS(4)#ifdef DIRLIGHTMAP_COMBINEDfixed3tSpace0 : TEXCOORD5;fixed3tSpace1 : TEXCOORD6;fixed3tSpace2 : TEXCOORD7;#endifUNITY_VERTEX_INPUT_INSTANCE_IDUNITY_VERTEX_OUTPUT_STEREO};
#endif
3. BasicDiffuse着色器的生成代码:顶点着色器入口函数
引擎会对应生成一个顶点着色器入口主函数vert_surf,该函数使用一个内置的顶点格式描述结构体appdata_full作为传入参数。在把顶点变换到裁剪空间之前,会用用户定义的vert函数对顶点进行操作。顶点着色器根据原始的外观着色器中各种编译指示符指定的光照模型和其他的参数,在里面进行顶点变换和光照处理,然后生成一个v2f_surf结构体返回。
4. BasicDiffuse着色器的生成代码:片元着色器入口函数
引擎会对应生成一个片元着色器入口主函数frag_surf,该函数使用顶点着色器返回的v2f_surf结构体作为传入参数。根据原始的外观着色器中各种编译指示符指定的光照模型和其他参数,在里面依次调用用户定义的surf函数、LightingBasicDiffuse函数和final函数进行光照和阴影处理。
Unity 3D着色器代码的编译指示符(compile directive)。编译指示符的一般格式如下。
#pragma surface surfaceFunction lightModel [optionalparams]
5. SurfaceOutput结构体
SurfaceOutput结构体的定义如以下代码所示。
// 所在文件:lighting.cginc代码// 所在目录:CGIncludes// 从原文件第10行开始,至第17行结束structSurfaceOutput{fixed3Albedo; // 物体表面的漫反射颜色fixed3Normal; // 物体表面的法线fixed3Emission;// 物体的自发光颜色halfSpecular; // 物体的镜面反射系数,在[0,1]范围fixedGloss; // 物体的镜面高光亮度值fixedAlpha; // 物体的透明值};
lightModel即光照模型,此模型指明了经过surfaceFunction处理过的表面片元信息之后,如何利用这些片元信息进行光照计算。光照模型可以使用Unity 3D自带的光照函数,如Lambert模型,也可以使用自定义的光照函数。这些光照函数的命名规则是Lighting××××,即如果在编译指令中指定的光照模型名为××××,则定义此光照模型的光照函数名为Lighting××××
以引擎自带的Lambert模型为例,此光照模型函数则是在Lighting.cginc文件中定义的Lighting Lambert,如下所示。
inline fixed4LightingLambert (SurfaceOutput s, UnityGI gi){fixed4c;c = UnityLambertLight(s, gi.light);#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECTc.rgb += s.Albedo * gi.indirect.diffuse;#endifreturnc;}
6. UnityLambertLight函数
UnityLambertLight函数的定义如下。
// 所在文件:lighting.cginc代码// 所在目录:CGIncludes// 从原文件第29行开始,至第37行结束inline fixed4UnityLambertLight (SurfaceOutput s, UnityLight light){fixeddiff = max(0, dot(s.Normal, light.dir));fixed4c;c.rgb = s.Albedo * light.color * diff;c.a = s.Alpha;returnc;}
7. UnityLight、UnityIndirect和UnityGI结构体的定义
UnityLight、UnityIndirect和UnityGI结构体的定义如下。
// 所在文件:UnityLightingCommon.cginc代码// 所在目录:CGIncludes// 从原文件第9行开始,至第26行结束structUnityLight{half3color; // 直接光照光的颜色half3dir; // 直接光照的方向half ndotl; // 法线方向向量和光照方向向量的点积值,此值已经不使用了,故在开发中不要再用// 在开发中不要再用};structUnityIndirect{half3diffuse; // 间接光照的漫反射贡献量half3specular;// 间接光照的镜面反射贡献量};structUnityGI{UnityLight light;UnityIndirect indirect;};
除了上述的surfaceFuntion和lightModel,还可以自定义两种函数:vertex:VertexFunction和finalcolor:ColorFunction。
一个外观着色器在整个渲染流水线中的执行流程如图3-3所示。
▲图3-3 外观着色器在整个渲染流水线中的执行流程
3.1.2 外观着色器的编译指示符
外观着色器的功能性代码需要放在CGPROGRAM和ENDCG两个指令之间。
着色器代码段必须放在SubShader块中间,而不能放在Pass块内,编译器会将它编译到多个渲染通路(render pass)内。
必须使用#pragma surface指示符去指明本着色器是一个外观着色器。格式如下:
#pragma surface surfaceFunction lightModel [optionalparams]
▲表3-1 外观着色器必需的编译指示符与参数
▲ 表3-2 和Alpha混合及Alpha测试相关的编译指示符与参数
▲ 表3-3 和自定义操作顶点与片元着色器相关的指示符与参数
▲表3-4 和阴影操作和顶点镶嵌操作相关的指示符与参数
▲表3-5 精简着色器代码的指示符与参数
▲表3-6 其他杂项相关的指示符与参数
3.1.3 传给外观着色器函数的参数
传给外观着色器的输入结构通常可以包含任意着色器所需的纹理映射坐标。定义一个结构体分量去描述纹理映射坐标时,该分量务必以uv开头,然后后面加上分量名字,如uv_diffuse、uv_bumpmap等;如果是第二层纹理映射坐标,则使用uv2开头,其他层依此类推。
▲表3-7 引擎内建支持的外观着色器光照函数的输入结构体的各属性分量
3.2 直接编写顶点着色器和片元着色器
通过使用Cg语言,在ShaderLab的Pass代码段中,在CGPROGRAM语句之后,ENDCG语句之前,嵌入一些着色器代码片段(snippets),便可以采用直接编写顶点着色器和片元着色器的实现代码这种传统的着色器编写方式,去实现着色器。这种编写方式的代码结构通常如下所示。
Pass
{
// 此处的代码和普通的Pass的设置一样
CGPROGRAM
// 本代码片段所需要的编译指示符
#pragma vertex vert // 指定顶点着色器的主入口函数,主入口函数名为vert
#pragma fragment frag // 指定片元着色器的主入口函数,主入口函数名为frag
// 具体的着色器实现代码在这里
ENDCG
// 此处的代码和普通的Pass的设置一样
}
3.2.1 用Cg语言编写的包含着色器功能的代码片段
如果使用CGPROGRAM和ENDCG关键字,编译器在编译代码时会自动包含HLSLSupport文件和UnityShaderVariables文件;如果启用了HLSLPROGRAM和ENDHLSL语句,就不会自动包含。
▲表3-8 控制Cg代码片段的编译和优化指示符
3.2.2 声明目标渲染器
默认情况下所有着色器源代码都被编译到所有引擎能支持的渲染器中。使用#pragma only_renderers或#pragma exclude_renderers编译指示符,可以明确地告诉引擎把着色器源代码编译成只能给指定渲染器执行的版本。其他没有指定的渲染器,即使特性都支持,也都不编译。
▲Unity 3D支持的渲染器名称和对应的渲染接口如表3-9所示。
3.2.3 着色器的语义
着色器语言中的语义(semantics)用来说明在输入顶点的结构体中,以及顶点着色器传递给片元着色器中的数据(称为varying数据)结构体中,各数据成员的预期用途,如说明某数据是位置信息还是法向量信息,是纹理映射坐标还是雾化因子等。语义也表明这些图元数据存放的硬件资源是什么,如是寄存器还是纹理缓冲区等。如代码中的POSITION、NORMAL、TAGENT、TEXCOORD0便是语义词。
1. 顶点着色器的输入语义
▲表3-10是常见的顶点着色器中支持的输入语义,其中,n是一个从0到系统所支持的最大个数
语义词的使用示例说明如下所示:
Pass{// 此处的代码和普通的Pass的设置一样CGPROGRAM// 本代码片段所需要的编译指示符#pragma vertex vert // 指定顶点着色器的主入口函数,主入口函数名为vert#pragma fragment frag // 指定片元着色器的主入口函数,主入口函数名为frag// 具体的着色器实现代码在这里ENDCG// 此处的代码和普通的Pass的设置一样}in float4modelPos: POSITION
2. 顶点着色器的输出语义
▲表3-11 常用的顶点着色器中支持的输出语义
顶点着色器的输出数据被传入片元着色器中,所以顶点着色器的输出语义通常也是片元着色器的输入语义,但是语义POSITION除外。顶点着色器必须声明一个输出变量,并绑定POSITION语义。
3. 片元着色器的输入语义
▲常用的片元着色器的输入语义
POSITION语义用于顶点着色器,用来指定这些位置坐标值,是变换前的顶点在模型空间中的坐标。SV_Position语义则用于片元着色器,用来标识经过顶点着色器变换之后的顶点坐标。
在SV_Position的情况下,如果它绑定在一个从顶点着色器输出的结构体上,意味着该输出的结构体包含了最终转换过的并将用于光栅器的顶点坐标。或者,如果将这个标志绑定到一个输入给片元着色器的结构体,它会包含一个基于屏幕空间的像素坐标。
4. 在着色器中同时使用SV Position语义和VPOS语义
下面的代码展示了如何同时使用SV_Position语义和VPOS语义。
Shader "Unlit/Screen Position"{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma target 3.0// 只定义了使用第0层纹理坐标的语义,没有定义SV_Position语义到分量中structv2f {float2uv : TEXCOORD0;};v2f vert (// 输入给顶点着色器的顶点描述结构体float4vertex : POSITION, // 顶点坐标float2uv : TEXCOORD0, // 顶点使用的第0层纹理映射坐标// 不能在顶点到片元的结构体v2f中描述,只能在顶点着色器的// 主入口函数中声明为out返回out float4outpos : SV_Position// 顶点在裁剪空间的位置坐标){v2f o;o.uv = uv;// 调用Unity提供的工具函数,把顶点从模型空间变换到裁剪空间outpos = UnityObjectToClipPos(vertex);returno;}sampler2D_MainTex;fixed4frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target{//SV_Position语义所指明的裁剪空间坐标的范围是[-1,1],而VPOS//语义所指明的坐标值就是像素坐标值,假如视口的高宽分别是//1024像素和768像素,则VPOS坐标的取值范围就是[0,1024]、[0,768],并且是整数值screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;floatchecker = -frac(screenPos.r + screenPos.g);//若不能通过检测,就直接丢弃clip(checker);fixed4c = tex2D(_MainTex, i.uv);returnc;}ENDCG}}}
5. 在着色器中使用VFACE语义
当多边形正向摄像机时,VFACE绑定的变量是一个正值;背向摄像机时,该变量是一个负值。因为VFACE语义是在shader model 3.0时引入的,所以要使用VFACE语义,必须使用#pragma target 3.0编译指示符。
下面的代码展示了如何在着色器中使用VFACE语义。
Shader "Unlit/Face Orientation"{Properties{_ColorFront ("Front Color", Color) = (1,0.7,0.7,1)_ColorBack ("Back Color", Color) = (0.7,1,0.7,1)}SubShader{Pass{Cull Off // turn off backface cullingCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma target 3.0float4vert (float4vertex : POSITION) : SV_Position{returnUnityObjectToClipPos(vertex);}fixed4_ColorFront;fixed4_ColorBack;fixed4frag (fixedfacing : VFACE) : SV_Target{// 依据VFACE语义变量facing的取值,得到当前是正向还是背向摄像机,显示不同颜色returnfacing > 0 ? _ColorFront : _ColorBack;}ENDCG}}}
6. 片元着色器的输出语义
▲表3-13是常见的片元着色器的输出语义。
在大多数情况下,片元着色器将会输出一个颜色值,这个颜色值通常指定为SV_Target语义。
除了以单个数值的形式返回之外,片元着色器还支持以结构体的形式返回数据,如下代码所示。
structfragOutput
{fixed4color : SV_Target;
};
fragOutput frag (v2f i)
{fragOutput o;o.color = fixed4(i.uv, 0, 0);returno;
}
其他的语义项有SV_Target1、SV_Target2。SV_Target等同于SV_Target0,大多数情况下对应于默认的帧缓冲区。
当使用多渲染目标(multiple render targets,MRT)技术一次性地向不止一个渲染目标(render target)中写入颜色数据时,就需要利用SV_Target1、SV_Target2等一一对应去注明往哪个渲染目标去写入。多渲染目标在延迟渲染技术中会被普遍使用到。
3.3 在Cg代码中访问着色器属性块
Unity 3D着色器代码通过使用属性块(properties block)的方式声明着色器中要用到的材质属性。声明完材质属性后,还需要在着色器的Cg/HLSL代码体内一一对应声明一次材质属性对应的着色器变量。
3.4 使用着色器多样体处理多种情况
每一段由编译条件控制编译与否的代码段称为着色器多样体(shader variants),#pragma multi_compile或者#pragma shader_feature指示符后面跟着的名字字符串称为多样体关键字(variants keyword)。
3.4.1 编译指示符multi_compile和shader_feature的使用方式与区别
如下所示是一个使用编译指示符定义多样体的语句。
#pragma multi_compile FANCY_STUFF_OFF FANCY_STUFF_ON
该语句将会生成两个着色器多样体,一种就是启用了FANCY_STUFF_OFF,另一种就是启用了FANCY_STUFF_ON。在运行时,在C#语言层面,调用Material类的成员函数EnableKeyword可以显式地激活其中一个着色器多样体,如果没有一个多样体关键字被显式地启用,那么将默认启用第一个多样体。#pragma multi_Compile指示符还可以定义多于两个的多样体关键字。
当使用完全由下画线“_”组成的多样体关键字时,对应的多样体依然被编译,但没有与之对应的预处理宏被定义,比如以下代码。
#pragma multi_compile __ FOO_ON
使用这种匿名的方式定义多样体关键字,其好处是可以节省多样体关键字的个数,因为着色器编译器对多样体关键字的定义个数是有限制的。
#pragma shader_feature类似于#pragma multi_compile,唯一的区别就是shader_feature指示符中声明的着色器多样体如果未被使用,在构建游戏运行包(game build)时将不会被打包进去。
如果要在物体的材质中设置,即调用Material类成员函数EnableKeyword去设置的多样体关键字,用shader_feature指示符声明最好。如果要在全局范围中设置,即调用Shader类静态成员函数EnableKeyword去设置的多样体关键字,用multi_compile指示符声明最好。
3.4.2 多样体关键字的使用限制
当使用着色器多样体时,要时刻切记Unity 3D有着只能使用256个多样体关键字的限制,并且大约有60个关键字已经被内置的代码所使用了。所以在编写自定义的着色器代码时,不能超出个数的限制。
3.4.3 内置的multi_compile指示符快捷使用方式
Unity 3D提供了若干快捷(shortcut)编译指示符,可以用一个语句的方式代替需要多个编译指示符的声明,引擎后台在编译着色器代码时会将其自动展开。以下是若干快捷编译指示符的作用。
· multi_compile_fwdbase编译指示符一次性开启所有在ForwardBase类型的渲染通路中所需的多样体,这些多样体定义了不同的烘焙光照图的类型;以及主要的有向平行光参与的光照计算中是否开启阴影计算。
· multi_compile_fwdadd编译指示符一次性开启所有在ForwardAdd类型的渲染通路中所需的多样体,这些多样体将在渲染时控制操作场景中的有向光源、点光源和聚光灯光源的光照计算。
· multi_compile_fwdadd_fullshadows编译指示符除了处理控制操作场景中的有向光源、点光源和聚光灯光源的光照计算之外,还会控制光源生成对应的实时阴影。
· multi_compile_fwdadd编译指示符将会根据当前选定的雾化因子对应去展开成各个不同的多样体定义。
在使用快捷编译指示符时,如果想在快捷定义中同时生成的多样体中去除若干多样体,可以使用skip_variants编译指示符,如以下代码所示。
#pragma multi_compile_fwdadd
// multi_compile_fwdadd指示符把"POINT"和"POINT_COOKIE"都开启了
// 现在不想启用这两个
#pragma skip_variants POINT POINT_COOKIE
3.5 多平台着色器代码的支持
▲表3-14 编译指示符指定的目标渲染器和对应的宏
使用#pragma only_renderers或#pragma exclude_renderers编译指示符,定义了如表3-9所示的目标渲染器后,着色器编译器会根据所启用的目标渲染器,定义上对应的宏。
3.6 确定着色器编译器的版本
3.6.1 和着色器编译器版本相关的宏
确定着色器使用版本的代码段如下:
// 所在文件:HLSLSupport.cginc代码// 所在目录:CGIncludes// 从原文件第3行开始,至第23行结束#ifndef HLSL_SUPPORT_INCLUDED#define HLSL_SUPPORT_INCLUDED// 根据各个宏的预定义情况,确定底层使用哪个着色器编译器#if !defined(UNITY_COMPILER_CG) && // 如果Cg编译器没有被指定启用!defined(UNITY_COMPILER_HLSL) && // 如果HLSL编译器没有被指定启用!defined(UNITY_COMPILER_HLSL2GLSL)&& //HLSL转GLSL编译器没使用!defined(UNITY_COMPILER_HLSLCC)//如果HLSLCC编译器没被启用#if defined(SHADER_TARGET_SURFACE_ANALYSIS)//#define UNITY_COMPILER_CG#elif defined(SHADER_API_GLCORE) || defined(SHADER_API_GLES3)|| defined(SHADER_API_VULKAN)#define UNITY_COMPILER_HLSL#define UNITY_COMPILER_HLSLCC#elif defined(SHADER_API_D3D11) || defined(SHADER_API_D3D11_9X)|| defined(SHADER_API_D3D9) || defined(SHADER_API_XBOXONE)#define UNITY_COMPILER_HLSL#elif defined(SHADER_TARGET_GLSL) || defined(SHADER_API_WIIU)#define UNITY_COMPILER_HLSL2GLSL#else#define UNITY_COMPILER_CG#endif#endif
3.6.2 消除着色器代码中各平台的语义差异性
1.利用宏消除各平台的语义差异性
// 所在文件:HLSLSupport.cginc代码// 所在目录:CGIncludes// 从原文件第43行开始,至第73行结束#if defined(UNITY_FRAMEBUFFER_FETCH_AVAILABLE) &&defined(UNITY_FRAMEBUFFER_FETCH_ENABLED) &&defined(UNITY_COMPILER_HLSLCC)#define SV_Target CoLoR#define SV_Target0 CoLoR0#define SV_Target1 CoLoR1#define SV_Target2 CoLoR2#define SV_Target3 CoLoR3#define COLOR VCOLOR#define COLOR0 VCOLOR0#define COLOR1 VCOLOR1#define COLOR2 VCOLOR2#define COLOR3 VCOLOR3#endif
UNITY_FRAMEBUFFER_FETCH_AVAILABLE宏用来表征目标硬件平台是否实现了“帧缓冲区撷取”(frame buffer fetch)这一功能;UNITY_FRAMEBUFFER_FETCH_ENABLED宏则表示假如功能实现,是否启用它。
2. 消除SV Target和SV Depth语义在各平台的差异性
// 所在文件:HLSLSupport.cginc代码// 所在目录:CGIncludes// 从原文件第76行开始,至第125行结束#if !defined(SV_Target)# if !defined(SHADER_API_XBOXONE)# define SV_Target COLOR# endif#endif
3.6.3 关闭可忽视的编译警告
可以使用#pragma warning(disable警告编号)语句关闭一些特定的编译警告,使在编译时不提示这些警告。
//屏蔽把数据类型从大范围值转为小值时发生的警告
#pragma warning (disable : 3205)
3.6.4 Unity 3D Shader的基本数据类型
1. 浮点数类型
Cg/HLSL着色器语言中有几种浮点数的实现类型:float、half和fixed,以及基于它们所实现的向量和矩阵类型,如half3和float4x4。
▲表3-15 Cg语言中的浮点数类型
2.整数类型
着色器语言中的整数类型通常用于循环次数计数,或者用于数组索引,所以在大多数平台上同一种整数类型大都能工作良好。但在不同平台中,对整数类型的实现有所不同,如在Direct3D 9和OpenGL ES 2.0平台下,整数类型在GPU内部是用浮点数去模拟的。因此,一些在C语言中常见的对整数进行移位、按位与、按位或、按位异或等位操作是不能在着色器代码中使用的。而在Direct3D 11、OpenGL ES3.0、Metal等平台上,则真正地拥有整数类型,可以对整型变量进行位操作。
相关文章:

第3章 Unity 3D着色器系统
3.1 从一个外观着色器程序谈起 新建名为basic_diffuse.shader的文件,被一个名为basic_diffuse.mat的材质文件所引用,而basic_diffuse.mat文件则被场景中名为Sphere的game object的MeshRenderer组件所使用。 basic_diffuse.shader代码文件的内容如下所示…...

Qt项目天气预报(1) - ui界面搭建
ui中部 效果演示 ui效果 显示效果 控件列表 配合右图查看 居中对齐-label 设置label居中对齐(别傻傻的空格对齐了) 间距配置 widget03 外围的widget对象: 包含label 和 widget0301,如下图 widget0301 内围的widget对象,如下图 样式表 widget03 …...
一、从C语言到C++(一)
一、从C语言到C(一) C介绍C语言和C的联系C介绍 头文件命名空间定义命名空间使用命名空间中的名称使用using声明或指令命名空间与C语言的对比给命名空间起别名注意事项std 标准输入输出std::endl使用std::cout进行输出使用std::cin进行输入格式化输出 C介…...

MySQL(5)
聚合函数 GROUP BY 的使用 需求:查询各个部门的平均工资,最高工资SELECT department_id,AVG(salary),SUM(salary)FROM employeesGROUP BY department_id;需求:查询各个job_id的平均工资SELECT job_id,AVG(salary)FROM employeesGROUP BY jo…...
区块链之快照
定义 区块链快照是区块链技术中一个非常重要的概念,它可以帮助区块链系统提高性能和数据管理效率。 什么是区块链快照 区块链快照是指在某个时间点对整个区块链的状态进行保存和备份的过程。 快照会记录区块链上所有账户的余额、合约状态等信息,并将其序列化存储起来。 这样…...
自学前端第一天
HTML标签 ’HTML‘全程是‘hypertext Markup langage(超文本标记语言) HTML通过一系列的’标签(也称为元素)‘来定义文本、图像、链接。HTML标签是由尖括号包围的关键字。 标签通常成对存在,包括开始标签和结束标签(也称为双标签…...
SQL Server几种琐
SQL Server 中的锁类型主要包括以下几种,它们用于控制并发访问和数据一致性: 1. 共享锁(Shared Lock,S 锁): - 用于读取操作(如 SELECT 语句)。 - 允许多个事务同时读取同一资…...

redis 一些笔记1
redis 一、redis事务二、管道2.1 事务与管道的区别 三、主从复制3.13.2 权限细节3.3 基本操作命令3.4 常用3.4.1 一主几从3.4.2 薪火相传3.4.3 反客为主 3.5 步骤3.6 缺点 一、redis事务 放在一个队列里,依次执行,并不保证一致性。与mysql事务不同。 命…...

【计网复习】应用层总结(不含HTTP和错题重点解析)
应用层总结(不含HTTP和错题重点解析) 应用层简介 应用层的主要功能常见的应用层协议小林对于应用层通常的解释 网络应用模型 客户端-服务器模型(Client-Server Model, C/S) 特点优点缺点应用场景 对等网络模型(Peer-to…...
carbondata连接数优化
一,背景 carbondata的入库采用arbonData Thrift Server方式提供,由于存在异常的入库segments但是显示状态是success,所以每天运行另一个博客中的脚本,出现连接超时,运行不正常,排查是每天连接数太多&#x…...

云和运维(SRE)的半生缘-深读实证02
这个标题不算太夸张,云计算和很多IT岗位都有缘,但是和运维(SRE)岗位的缘分最深。 “深读实证”系列文章都会结合一些外部事件,点明分析《云计算行业进阶指南》书中的内容。本次分享介绍了下列内容: 我以运维…...

java基础操作5——java自定义获取任意年、月、日的起始和结束时间
在实际项目开发过程中,获取任意时间的起始和结束时间是常用操作,尤其对于统计业务来说,更是必要操作,理解了时间自定义的规律,对于开发人员的效率提升是大有裨益的。 一.获取任意年的起始和结束时间 1.获取任意年的起…...

【Java04】引用变量数组初始化的内存机制
引用类型数组指向的元素也是引用。其本质是: 由一个在栈上的引用数组变量指向一块堆内存;这块堆内存里存储的元素是引用,又分别指向其他堆内存。 class Person // Person是一个自定义的类 {public int age;puiblic double height;public vo…...

基于JSP的足球赛会管理系统
你好呀,我是计算机学长猫哥!如果有相关需求,文末可以找到我的联系方式。 开发语言:Java 数据库:MySQL 技术:JSP技术 工具:IDEA/Eclipse、Navicat、Maven 系统展示 首页 个人中心 球队介绍…...

博客摘录「 AXI三种接口及DMA DDR XDMA介绍(应用于vivado中的ip调用)」2024年6月10日
关键要点: 1.AXI Stream经过协议转换可使用AXI_FULL(PS与PL间的接口,如GP、HP和ACP)。 2.传输数据类里就涉及一个握手协议,即在主从双方数据通信前,有一个握手的过程。基本内容:数据的传输源会…...

Bigtable: A Distributed Storage System for Structured Data
2003年USENIX,出自谷歌,开启分布式大数据时代的三篇论文之一,底层依赖 GFS 存储,上层供 MapReduce 查询使用 Abstract 是一种分布式结构化数据存储管理系统,存储量级是PB级别。存储的数据类型和延时要求差异都很大。…...
RAG下的prompt编写探索
针对特定领域的回答,编写抽象的prompt需要在细节和灵活性之间找到平衡。我们需要一个既能涵盖普遍步骤又能适应不同问题的框架。以下是如何在这种情况下编写抽象prompt的方法,以及适用于各种技术领域的通用策略。 一、编写抽象Prompt的通用策略 定义用户问题和背景信息: 明…...

【计算机组成原理】指令系统考研真题详解之拓展操作码!
计算机组成原理:指令系统概述与深入解析 1. 指令系统概述 计算机软硬件界面的概念 在计算机组成原理中,指令系统扮演着至关重要的角色,它是计算机软硬件界面的核心。软件通过指令与硬件进行通信,硬件根据指令执行相应的操作。指…...

北航第六次数据结构与程序设计作业(查找与排序)选填题
一、 顺序查找的平均查找长度ASL(1 2 …… n)/ n (n 1)/ 2 二、 这半查找法的平均查找次数和判定树的深度有关系。若查找一个不存在的元素,说明进行了深度次比较。 注意,判定树不是满二叉树,因此深…...
Optional详解和常用API
目录 一、Optional简介 二、构建Optional对象三种方式 2.1 Optional.of(value) 2.1.1 使用案例 2.2 Optional.ofNullable(value) 2.2.1 使用案例 2.3 Optional.empty() 2.3.1 使用案例 三、Optional常用的api解析和使用案例 3.1 isPresent 3.1.1 使用案例 3.2 ifPrese…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
6.9本日总结
一、英语 复习默写list11list18,订正07年第3篇阅读 二、数学 学习线代第一讲,写15讲课后题 三、408 学习计组第二章,写计组习题 四、总结 明天结束线代第一章和计组第二章 五、明日计划 英语:复习l默写sit12list17&#…...

DL00871-基于深度学习YOLOv11的盲人障碍物目标检测含完整数据集
基于深度学习YOLOv11的盲人障碍物目标检测:开启盲人出行新纪元 在全球范围内,盲人及视觉障碍者的出行问题一直是社会关注的重点。尽管技术不断进步,许多城市的无障碍设施依然未能满足盲人出行的实际需求。尤其是在复杂的城市环境中ÿ…...