网站开发视频播放好做吗/百度seo价格查询系统
大家好,我是阿赵。
之前介绍了使用动态法线贴图混合的方式模拟轨迹的凹凸感,这次来讲一下更真实的凹凸感制作。不过在说这个内容之前,这一篇先要介绍一下曲面细分着色器(Tessellation Shader)的用法。
一、为什么要做曲面细分
之前通过法线贴图模拟了凹凸的感觉:
法线贴图不会真的产生凹凸,它只是改变了这个平面上面的法线方向。所以,只有通过光照模型,通过法线方向和灯光方向进行点乘,才会计算出不同的光照角度,让我们产生一定的凹凸感觉。
但如果想做到这样的效果,法线贴图是不行的:
这种效果,球是真的陷进去地面了。很明显,这些都是需要偏移顶点让网格产生真实的变形,才能做到。
不过这里有一个问题,如果地面的网格面数并不是很高,那么就算我们有能力去偏移顶点,也产生不了这样好的效果。
比如一般的地面网格的面数都很低,只有这样的水平:
这个时候,球所在的地方,根本就没有顶点,所以也偏移不了。就算再稍微多一点面,这样的地面网格面数算比较高了,仍然产生不了很好的凹凸效果:
所以这里有一个很严重的问题,我们难道需要用几十万甚至几百万面,去做一个地面的模型,才能产生真实的凹凸感吗?
这是不可能的,实际的情况是:
在需要到很精确的顶点控制的一个小局部,才需要把面数变高,其他的地方,面数很是很低的。具体可以看看这个视频:
Unity引擎动态曲面细分
而这里用到的局部增加面数的技术,就是曲面细分(Tessellation)了。
二、曲面细分的过程
在Unity里面写顶点片段着色器的Shader,我们一般只会注意到需要些Vertex顶点程序,和fragment片段程序,因为在大多数情况下,其他的渲染管线流程都不是我们可以控制的,而我们能控制顶点程序改变模型的形状,控制片段程序来改变模型的颜色。
但在顶点程序和片段程序中间,其实还有一个曲面细分(tessellate)的过程,这个过程有2个程序是我们可以控制的
1、hullProgram
这个程序会接受每个多边形各个顶点的信息,记录下来,然后通过指定一个Patch Constant Function,去设置细分的数量,这个过程是针对多边形的每一条边,还有多边形的内部,分别设置拆分的数量的。
2、domainProgram
在前面的hullProgram里面,其实只是设置了顶点信息和拆分数量,并没有真正的生成新的网格。而在这个domainProgram里面,拆分后的顶点信息已经产生了,所以可以对拆分后的顶线进行操作,可以计算他们的位置、法线、uv等。
为了避免难以理解,也不说太多,只要知道,需要做曲面细分的时候,需要添加2个程序过程,一个过程设置了拆分的数量和其他参数,另外一个过程就得到了顶点,可以进行实际操作,这样就行了。
三、曲面细分在Unity引擎的实现
1、Surface类型着色器
Surface类型的Shader提供了很多Unity封装好的方法,也包括提供了对应曲面细分着色器的方法。
使用很简单:
1.#include “Tessellation.cginc”
2.指定曲面细分的方法:tessellate:tessFunction
3.指定target 4.6
看到这里有target 4.6的声明了,没错Unity官方的说明也是这样的:
When you use tessellation, the shader is automatically compiled into
the Shader Model 4.6 target, which prevents support for running on
older graphics targets.
这里着重说一下曲面细分方法。
由于Surface的曲面细分方法是Unity封装好的,所以我们不需要走正常的渲染流程,不需要指定hullProgram、Patch Constant Function和domainProgram,只需要指定一个tessellate处理方法。这个方法实际是返回一个曲面细分的值,来决定某个面具体要细分成多少个网格。
而在Unity提供的方法里面,对于怎样细分曲面,提供了3种选择:
1.Fixed固定数量细分
这种方式细分,在tessFunction里面直接返回一个数值,然后全部面就按照统一的数值去细分。
unity官方文档里面的例子是这样
Shader "Tessellation Sample" {Properties {_Tess ("Tessellation", Range(1,32)) = 4_MainTex ("Base (RGB)", 2D) = "white" {}_DispTex ("Disp Texture", 2D) = "gray" {}_NormalMap ("Normalmap", 2D) = "bump" {}_Displacement ("Displacement", Range(0, 1.0)) = 0.3_Color ("Color", color) = (1,1,1,0)_SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)}SubShader {Tags { "RenderType"="Opaque" }LOD 300CGPROGRAM#pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessFixed nolightmap#pragma target 4.6struct appdata {float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};float _Tess;float4 tessFixed(){return _Tess;}sampler2D _DispTex;float _Displacement;void disp (inout appdata v){float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;v.vertex.xyz += v.normal * d;}struct Input {float2 uv_MainTex;};sampler2D _MainTex;sampler2D _NormalMap;fixed4 _Color;void surf (Input IN, inout SurfaceOutput o) {half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;o.Specular = 0.2;o.Gloss = 1.0;o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));}ENDCG}FallBack "Diffuse"}
其中曲面细分方法是直接返回了一个指定的值
float4 tessFixed()
{return _Tess;
}
2.根据距离细分
这里的距离,指的是和摄像机的距离。根据离摄像机不同的距离,设置一个范围来细分
unity官方文档里面的例子是这样:
Shader "Tessellation Sample" {Properties {_Tess ("Tessellation", Range(1,32)) = 4_MainTex ("Base (RGB)", 2D) = "white" {}_DispTex ("Disp Texture", 2D) = "gray" {}_NormalMap ("Normalmap", 2D) = "bump" {}_Displacement ("Displacement", Range(0, 1.0)) = 0.3_Color ("Color", color) = (1,1,1,0)_SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)}SubShader {Tags { "RenderType"="Opaque" }LOD 300CGPROGRAM#pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessDistance nolightmap#pragma target 4.6#include "Tessellation.cginc"struct appdata {float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};float _Tess;float4 tessDistance (appdata v0, appdata v1, appdata v2) {float minDist = 10.0;float maxDist = 25.0;return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);}sampler2D _DispTex;float _Displacement;void disp (inout appdata v){float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;v.vertex.xyz += v.normal * d;}struct Input {float2 uv_MainTex;};sampler2D _MainTex;sampler2D _NormalMap;fixed4 _Color;void surf (Input IN, inout SurfaceOutput o) {half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;o.Specular = 0.2;o.Gloss = 1.0;o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));}ENDCG}FallBack "Diffuse"}
其中曲面细分方法是传入了最小距离、最大距离和一个控制值
float4 tessDistance (appdata v0, appdata v1, appdata v2) {float minDist = 10.0;float maxDist = 25.0;return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);
}
UnityDistanceBasedTess就是Unity提供的根据距离计算细分值的方法。
3.根据边的长度细分
这个根据边的长度,指的是多边形的边,在屏幕里面渲染的大小。
所以从左图可以看出,越近屏幕的边,渲染的长度越大,所以细分得越多,而离屏幕越远的边,渲染的长度越小,细分得也越少。
从右图可以看出,同一个模型,如果通过缩放把边拉长,它的细分程度也会随着模型拉长而变大,最后保持着一个比较固定的细分密度。
unity官方文档里面的例子是这样的:
Shader "Tessellation Sample" {Properties {_EdgeLength ("Edge length", Range(2,50)) = 15_MainTex ("Base (RGB)", 2D) = "white" {}_DispTex ("Disp Texture", 2D) = "gray" {}_NormalMap ("Normalmap", 2D) = "bump" {}_Displacement ("Displacement", Range(0, 1.0)) = 0.3_Color ("Color", color) = (1,1,1,0)_SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)}SubShader {Tags { "RenderType"="Opaque" }LOD 300CGPROGRAM#pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessEdge nolightmap#pragma target 4.6#include "Tessellation.cginc"struct appdata {float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};float _EdgeLength;float4 tessEdge (appdata v0, appdata v1, appdata v2){return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);}sampler2D _DispTex;float _Displacement;void disp (inout appdata v){float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;v.vertex.xyz += v.normal * d;}struct Input {float2 uv_MainTex;};sampler2D _MainTex;sampler2D _NormalMap;fixed4 _Color;void surf (Input IN, inout SurfaceOutput o) {half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;o.Specular = 0.2;o.Gloss = 1.0;o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));}ENDCG}FallBack "Diffuse"
}
其中曲面细分程序传入一个指定的值,需要注意的是,这个值越小,细分得越多
float4 tessEdge (appdata v0, appdata v1, appdata v2)
{return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
}
UnityEdgeLengthBasedTess 是Unity提供的根据边长细分的方法
2、顶点片段程序实现曲面细分
如果不使用Surface类型的Shader,而用传统的顶点片段程序着色器,实现曲面细分就只有一种方式,就是正常的添加hullProgram、Patch Constant Function和domainProgram,然后逐条边和多边形内部指定细分的数量。我这里提供一个最简单的Shader来说明一下写法:
Shader "azhao/TessVF"
{Properties{_MainTex("Texture", 2D) = "white" {}_Color("Color", Color) = (1,1,1,1)_EditFactor("edgeFactor", Float) = 15_InsideFactor("insideFactor",FLoat) =15}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert//在正常的vertex和fragment之间还需要hull和domain,所以在这里加上声明#pragma hull hullProgram#pragma domain domainProgram#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;uniform float _EditFactor;uniform float _InsideFactor;struct a2v{float4 pos : POSITION;float2 uv : TEXCOORD0;};struct v2t{float4 worldPos : TEXCOORD0;float2 uv : TEXCOORD1;};struct t2f{float4 clipPos:SV_POSITION;float2 uv: TEXCOORD0;float4 worldPos:TEXCOORD1;};struct TessOut{float2 uv : TEXCOORD0;float4 worldPos : TEXCOORD1;};struct TessParam{float EdgeTess[3] : SV_TessFactor;//各边细分数float InsideTess : SV_InsideTessFactor;//内部点细分数};v2t vert(a2v i){v2t o;o.worldPos = mul(unity_ObjectToWorld,i.pos);o.uv = i.uv;return o;}//在hullProgram之前必须设置这些参数,不然会报错[domain("tri")]//图元类型,可选类型有 "tri", "quad", "isoline"[partitioning("integer")]//曲面细分的过渡方式是整数还是小数[outputtopology("triangle_cw")]//三角面正方向是顺时针还是逆时针[outputcontrolpoints(3)]//输出的控制点数[patchconstantfunc("ConstantHS")]//对应之前的细分因子配置阶段的方法名[maxtessfactor(64.0)]//最大可能的细分段数//vert顶点程序之后调用,计算细分前的三角形顶点信息TessOut hullProgram(InputPatch<v2t, 3> i, uint idx : SV_OutputControlPointID){TessOut o;o.worldPos = i[idx].worldPos;o.uv = i[idx].uv;return o;}//指定每个边的细分段数和内部细分段数TessParam ConstantHS(InputPatch<v2t, 3> i, uint id : SV_PrimitiveID){TessParam o;o.EdgeTess[0] = _EditFactor;o.EdgeTess[1] = _EditFactor;o.EdgeTess[2] = _EditFactor;o.InsideTess = _InsideFactor;return o;}//在domainProgram前必须设置domain参数,不然会报错[domain("tri")]//细分之后,把信息传到frag片段程序t2f domainProgram(TessParam tessParam, float3 bary : SV_DomainLocation, const OutputPatch<TessOut, 3> i){t2f o; //线性转换float2 uv = i[0].uv * bary.x + i[1].uv * bary.y + i[2].uv * bary.z;o.uv = uv;float4 worldPos = i[0].worldPos * bary.x + i[1].worldPos * bary.y + i[2].worldPos * bary.z;o.worldPos = worldPos;o.clipPos = UnityWorldToClipPos(worldPos);return o;}fixed4 frag (t2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv)*_Color;return col;}ENDCG}}
}
需要注意的地方是:
1.声明处理程序:
#pragma hull hullProgram
#pragma domain domainProgram
2.在hullProgram之前必须设置这些参数,不然会报错
[domain("tri")]//图元类型,可选类型有 "tri", "quad", "isoline"
[partitioning("integer")]//曲面细分的过渡方式是整数还是小数
[outputtopology("triangle_cw")]//三角面正方向是顺时针还是逆时针
[outputcontrolpoints(3)]//输出的控制点数
[patchconstantfunc("ConstantHS")]//对应之前的细分因子配置阶段的方法名
[maxtessfactor(64.0)]//最大可能的细分段数
3.domainProgram前必须设置domain参数,不然会报错
[domain("tri")]
四、根据范围做局部曲面细分
已经介绍完怎样使用曲面细分了,接下来就是要实现文章一开始说的,根据指定的中心点和范围,做局部的曲面细分。
1、在顶点片段着色器实现局部细分
由于使用顶点片段着色器做曲面细分,是可以直接设置每个多边形的边和内部的细分数量,所以要实现局部细分也就非常简单了,思路是:
1.获得中心点坐标和范围半径
2.在着色器取得当前顶点的世界坐标,然后判断是否在中心点的半径范围内
3.用一个smoothStep做一个边缘范围过渡,作为细分强度
4.根据计算出的细分强度,设置最终的细分值。
写成代码大概就是这样:
Shader "azhao/GroundTessVF"
{Properties{_MainTex("Texture", 2D) = "white" {}_Color("Color", Color) = (1,1,1,1)_centerPos("CenterPos", Vector) = (0,0,0,0)_minVal("minVal", Float) = 0_maxVal("maxVal", Float) = 10_factor("factor", Float) = 15}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert//在正常的vertex和fragment之间还需要hull和domain,所以在这里加上声明#pragma hull hullProgram#pragma domain domainProgram#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;uniform float _minVal;uniform float _maxVal;uniform float3 _centerPos;uniform float _factor;struct a2v{float4 pos : POSITION;float2 uv : TEXCOORD0;};struct v2t{float4 worldPos : TEXCOORD0;float2 uv : TEXCOORD1;};struct t2f{float4 clipPos : SV_POSITION;float2 uv : TEXCOORD0;float4 worldPos : TEXCOORD1;};struct TessOut{float2 uv : TEXCOORD0;float4 worldPos : TEXCOORD1;};struct TessParam{float EdgeTess[3] : SV_TessFactor;//各边细分数float InsideTess : SV_InsideTessFactor;//内部点细分数};v2t vert(a2v i){v2t o;o.worldPos = mul(unity_ObjectToWorld,i.pos);o.uv = i.uv;return o;}//在hullProgram之前必须设置这些参数,不然会报错[domain("tri")]//图元类型,可选类型有 "tri", "quad", "isoline"[partitioning("integer")]//曲面细分的过渡方式是整数还是小数[outputtopology("triangle_cw")]//三角面正方向是顺时针还是逆时针[outputcontrolpoints(3)]//输出的控制点数[patchconstantfunc("ConstantHS")]//对应之前的细分因子配置阶段的方法名[maxtessfactor(64.0)]//最大可能的细分段数//vert顶点程序之后调用,计算细分前的三角形顶点信息TessOut hullProgram(InputPatch<v2t, 3> i, uint idx : SV_OutputControlPointID){TessOut o;o.worldPos = i[idx].worldPos;o.uv = i[idx].uv;return o;}//指定每个边的细分段数和内部细分段数TessParam ConstantHS(InputPatch<v2t, 3> i, uint id : SV_PrimitiveID){TessParam o;float4 worldPos = (i[0].worldPos + i[1].worldPos + i[2].worldPos) / 3;float smoothstepResult = smoothstep(_minVal, _maxVal, distance(worldPos.xz, _centerPos.xz));float fac = max((1.0 - smoothstepResult)*_factor, 1);//由于我这里是根据指定的中心点和半径范围来动态算细分段数,所以才有这个计算,不然可以直接指定变量来设置。o.EdgeTess[0] = fac;o.EdgeTess[1] = fac;o.EdgeTess[2] = fac;o.InsideTess = fac;return o;}//在domainProgram前必须设置domain参数,不然会报错[domain("tri")]//细分之后,把信息传到frag片段程序t2f domainProgram(TessParam tessParam, float3 bary : SV_DomainLocation, const OutputPatch<TessOut, 3> i){t2f o; //线性转换o.worldPos = i[0].worldPos * bary.x + i[1].worldPos * bary.y + i[2].worldPos * bary.z;o.clipPos = UnityWorldToClipPos(o.worldPos);float2 uv = i[0].uv * bary.x + i[1].uv * bary.y + i[2].uv * bary.z;o.uv = uv;return o;}fixed4 frag (t2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv)*_Color;return col;}ENDCG}}
}
使用的时候,在C#端中心点改变的时候,传入centerPos,通过调整_maxVal和_minVal,可以控制半径和边缘强度渐变的效果
2、在Surface着色器实现局部细分
在Surface着色器里面实现曲面细分,需要写的代码很少,我们就使用上面介绍的Fixed类型然后同样的通过传入中心点,还有_maxVal和_minVal,来确定需要细分的范围,实现思路和上面的顶点片段着色器是一样的。
代码会是这样的:
Shader "azhao/FootStepMeshSurface"
{Properties{_MainTex("Texture", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)_centerPos("centerPos", Vector) = (0,0,0,0)_minVal("minVal", Float) = 0_maxVal("maxVal", Float) = 10_factor("factor", Float) = 15_footstepRect("footstepRect",Vector) = (0,0,0,0)_footstepTex("footstepTex",2D) = "gray"{}_height("height" ,Float) = 0.3_Glossiness("Glossiness",Float) = 0_Metallic("Metallic",Float) = 0}SubShader{Tags { "RenderType"="Opaque" }LOD 200CGPROGRAM#include "Tessellation.cginc"#pragma surface surf Standard fullforwardshadows vertex:vertexDataFunc tessellate:tessFunction #pragma target 4.6struct Input{float2 uv_texcoord;};half _Glossiness;half _Metallic;fixed4 _Color;uniform sampler2D _mainTex;SamplerState sampler_mainTex;uniform float4 _mainTex_ST;uniform float _minVal;uniform float _maxVal;uniform float3 _centerPos;uniform float _factor;float4 _footstepRect;sampler2D _footstepTex;float _height;UNITY_INSTANCING_BUFFER_START(Props)UNITY_INSTANCING_BUFFER_END(Props)float RemapUV(float min, float max, float val){return (val - min) / (max - min);}//这里处理细分相关逻辑float4 tessFunction(appdata_full v0, appdata_full v1, appdata_full v2){float3 worldPos = mul(unity_ObjectToWorld, (v0.vertex + v1.vertex + v2.vertex) / 3);float smoothstepResult = smoothstep(_minVal, _maxVal, distance(worldPos.xz, _centerPos.xz));float fac = max((1.0 - smoothstepResult)*_factor, 0.1);return fac;}void vertexDataFunc(inout appdata_full v){float4 worldPos = mul(unity_ObjectToWorld, v.vertex);float2 footUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, worldPos.z));float4 footstepCol = tex2Dlod(_footstepTex, float4(footUV, 0, 0.0));float addVal = (footstepCol.r * 2 - 1)*footstepCol.a*_height;v.vertex.y += addVal/100;}void surf (Input IN, inout SurfaceOutputStandard o){fixed4 c = _Color;float2 uv_mainTex = IN.uv_texcoord * _mainTex_ST.xy + _mainTex_ST.zw;float4 mainTex = tex2D(_mainTex, uv_mainTex);o.Albedo = mainTex.rgb*c.rgb;o.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}FallBack "Diffuse"
}
3、选择哪种方式的Shader实现会比较好?
这个问题是没有直接答案的,需要根据自己的实际情况来选择。
顶点片段着色器的优点是可控性强,自己可以随意的定义各种光照模型、修改细节的效果,缺点是写法麻烦。
Surface着色器的优点是写法简单,缺点是可控性比较弱一点。
我个人是习惯用顶点片段着色器的,因为我比较的喜欢自己控制各个环节的细节。所以在接下来的例子里面,我还是会用顶点片段着色器的写法来继续做这个地面交互效果的demo。不过其实如果顶点片段着色器上知道了怎样实现,在Surface着色器上面实现的过程就更简单了。
相关文章:

Unity地面交互效果——3、曲面细分基础知识
大家好,我是阿赵。 之前介绍了使用动态法线贴图混合的方式模拟轨迹的凹凸感,这次来讲一下更真实的凹凸感制作。不过在说这个内容之前,这一篇先要介绍一下曲面细分着色器(Tessellation Shader)的用法。 一、为什么要做曲面细分 之前通过法…...

NOIP 赛前模拟总结(第一周)
10.24 虽然今天我过了一道题,成为了少数过了题的人,但是排名没有想象中那么高,充分说明了打暴力的重要性,有时候你拼正解可能还没有你打满暴力考得好。 一定要打满暴力!一定要打满暴力!一定要打满暴力&am…...

stm32 DMA
目录 简介 框图 DMA请求 DMA通道 DMA优先级 DMA 数据 外设到存储器 存储器到外设 存储器到存储器 传多少,单位是什么 传输完成 hal库代码 标准库代码 简介 CPU根据代码内容执行指令,这些众多指令中,有的用于计算、有的用于控制程…...

厦门万宾科技智能井盖监测仪器的作用如何?
越来越多的人们希望改善生活,走出农村走出大山,前往城市之中居住。由此城市的人口和车辆在不断增加,与之而来的是城市的交通压力越来越大,时常会出现道路安全隐患,这给城市未来发展和智慧城市建设都带来一定的难题&…...

【带头学C++】----- 三、指针章 ---- 3.5 字符串与指针
在 C 中,字符串可以通过指针来表示和操作。C 的字符串是由字符组成的字符数组,而指针则用于引用和操作内存中的数据。 1. 字符数组 1. 字符数组: 字符数组是最基本的字符串表示方式。可以使用字符数组来存储字符串,并使用指针来引用它。字符…...

二十三种设计模式全面解析-深入解析桥接模式:解锁软件设计的灵活性
在软件开发中,我们经常面临需要处理多个不同维度变化的情况。这些变化可能涉及多个维度的组合,导致类的爆炸性增长和难以维护的代码。在这种情况下,桥接模式(Bridge Pattern)是一种强大的设计模式,能够帮助…...

Ansible中的角色使用
Ansible中的角色使用: 目录 一、ansible角色简介 二、roles目录结构 三、roles的创建 四、roles的使用 1、书写task主任务 2、触发器模块 3、变量模块 4、j2模块 5、files模块 6、启用模块 7、执行playbook 五、控制任务执行顺序 六、多重角色的使用 一…...

C通过指针访问数组元素
在C语言中,数组除了通过数组索引访问,也可以通过指针来访问数组中的元素。下面是一个简单的例子: #include <stdio.h>int main() {int array[5] {1, 2, 3, 4, 5};int *ptr array; // 指向数组的第一个元素的指针printf("数组元…...

程序员成长树
- 10年以后我在做什么? 成为项目负责人(管理事、管理人) - 如何处理同事的关系: 平时生活中最简单的一句问候,闲暇时间的聊天了解,互帮互助 - miss yang: - 1、软件UI设计 - 2、需求分析 - 3、协调推进任务的安排 …...

数字化时代,数据仓库是什么?有什么用?
在激烈的市场竞争和全新的数字经济共同作用下,数字化转型成为了大多数企业的共识,也是获取数字经济的最佳方式。在整个数据价值生产链路中,数据仓库的主要作用就是中心化分发,将原始数据与数据价值挖掘活动隔离。 所有的原始数据…...

android NetworkMonitor和ConnectivityService记录
一、NetworkMonitor packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java 网络上都叫网络可用性校验,在WIFI和数据(以太网切换中使用) 有多个状态机 方法:isCaptivePortal 11-03 12:01:17…...

Technology strategy Pattern 学习笔记1-Context: Architecture and Strategy
Context: Architecture and Strategy 1 Architect and Strategist 1.1 three primary concerns of the architect 1.1.1 Contain entropy(熵-混乱程度,不确定性,惊奇程度,不可预测性,信息量等等) The architect wh…...

数据结构-顺序表
1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条直线…...

数据结构与算法 | 第三章:栈与队列
本文参考网课为 数据结构与算法 1 第三章栈,主讲人 张铭 、王腾蛟 、赵海燕 、宋国杰 、邹磊 、黄群。 本文使用IDE为 Clion,开发环境 C14。 更新:2023 / 11 / 5 数据结构与算法 | 第三章:栈与队列 栈概念示例 实现顺序栈类定义…...

oracle查询数据库内全部的表名、列明、注释、数据类型、长度、精度等
Oracle查询数据库内全部的表名、列明、注释、数据类型、长度、精度 SELECT a.TABLE_NAME 表名, row_number() over(partition by a.TABLE_NAME order by a.COLUMN_NAME desc) 字段顺序,a.COLUMN_NAME 列名, b.COMMENTS 注释,a.DATA_TYPE 数据类型, a.DATA_LENGTH 长度,DATA_SC…...

数据可视化:折线图
1.初看效果 (1)效果一 (2)数据来源 2.JSON数据格式 其实JSON数据在JAVA后期的学习过程中我已经是很了解了,基本上后端服务器和前端交互数据大多是采用JSON字符串的形式 (1)JSON的作用 &#…...

Python语言_matplotlib包_共80种--全平台可用
Python语言_matplotlib包_共80种–全平台可用 往期推荐: Python语言_single_color_共140种–全平台可用 R语言_RColorBrewer包–全平台可用 R语言gplots包的颜色索引表–全平台可用 R语言中的自带的调色板–五种–全平台可用 R语言657中单色colors颜色索引表—全平台…...

OpenFeign 的超时重试机制以及底层实现原理
目录 1. 什么是 OpenFeign? 2. OpenFeign 的功能升级 3. OpenFeign 内置的超时重试机制 3.1 配置超时重试 3.2 覆盖 Retryer 对象 4. 自定义超时重试机制 4.1 为什么需要自定义超时重试机制 4.2 如何自定义超时重试机制 5. OpenFeign 超时重试的底层原理 5…...

redis安装
redis安装 mac下直接安装 mac下安装redis还是很简单的(其实mac下安装什么软件都挺简单的,brew啥都有) brew install redis 之后就是漫长的等待,下了好久,终于下载完了 修改redis.conf中的配置 # 后台启动daemonize yes 启动服务端 redis-serv…...

VM虚拟机逆向 --- [NCTF 2018]wcyvm 复现
文章目录 前言题目分析 前言 第四题了,搞定,算是独立完成比较多的一题,虽然在还原汇编的时候还是很多问题。 题目分析 代码很简单,就是指令很多。 opcode在unk_6021C0处,解密的数据在dword_6020A0处 opcode [0x08, …...

2024天津理工大学中环信息学院专升本机械设计制造自动化专业考纲
2024年天津理工大学中环信息学院高职升本科《机械设计制造及其自动化》专业课考试大纲《机械设计》《机械制图》 《机械设计》考试大纲 教 材:《机械设计》(第十版),高等教育出版社,濮良贵、陈国定、吴立言主编&#…...

华为OD机试 - 服务失效判断 - 逻辑分析(Java 2023 B卷 200分)
目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题&a…...

刚入职因为粗心大意,把事情办砸了,十分后悔
刚入职,就踩大坑,相信有很多朋友有我类似的经历。 5年前,我入职一家在线教育公司,新的公司福利非常好,各种零食随便吃,据说还能正点下班,一切都超出我的期望,“可算让我找着神仙公司…...

Docker学习——③
文章目录 1、Docker Registry(镜像仓库)1.1 什么是 Docker Registry?1.2 镜像仓库分类1.3 镜像仓库工作机制1.4 常用的镜像仓库 2、镜像仓库命令3、镜像命令[部分]4、容器命令[部分]4.1 docker run4.2 docker ps 5、CentOS 搭建一个 nginx 服…...

EMC Unity存储系统如何查看SSD的使用寿命
为什么要写这个博客? 客户对老的EMC unity的存储系统要扩容,如何确定SSD磁盘是全新的还是拆机二手的?很多时候客户还有一个奇葩的要求,就是要和5年前的磁盘PN一致,甚至要求固件版本一致,最关键的还要求是全…...

python创建一个简单的flask应用
下面用python在本地和服务器上分别创建一个简单的flask应用: 1.在pc本地 1)pip flask后创建一个简单的脚本flask_demo.py from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return Hello, World!winR进入命令行,…...

阿里云域名实战
一、准备阿里云服务器,实现网站功能 (1)百度搜索阿里云 (2)登录阿里云 可以使用支付宝,淘宝账号登录 (3)点击控制台 (4)创建实例,购买云服务器 (5&#x…...

git关联远程仓库自己分支自用
初始化仓库 cassielDESKTOP-KPKFOEU MINGW64 /d/code/api_test_202310232022 (tong) $ git init Reinitialized existing Git repository in D:/code/api_test_202310232022/.git/关联远程仓库并创建本地分支 cassielDESKTOP-KPKFOEU MINGW64 /d/code/api_test_202310232022 …...

eBPF BCC开源工具简介
目录 官方链接 编译安装 ubuntu版本 安装 examples tools hello_world.py demo 运行报错 网上目前的解决办法 错误分析过程 python版本检测 libbcc库检查 python3 bcc库检查 正常输出 监控进程切换 运行输出 监控CPU直方图 缓存命中率监控:caches…...

Linux上后台运行进程(nohub、screen和tmux )
文章目录 Linux上后台运行进程(nohub、screen和tmux )nohupscreen虚拟终端安装screen使用 tmux终端复用器[个人推荐]安装tmux使用 Linux上后台运行进程(nohub、screen和tmux ) 命令行的典型使用方式是,打开一个终端窗…...