Unity URP 曲面细分学习笔记

学百人时遇到了曲面着色器的内容,有点糊里糊涂,于是上知乎找到了两篇大佬的文章 Unity URP 曲面细分 和 Unity曲面细分笔记,本文只是自己做学习记录使用

1.曲面细分与镶嵌

曲面细分或细分曲面(Subdivision surface)是指一种通过递归算法将一个粗糙的几何网格细化的技术。镶嵌(Tessellation)则是实现曲面细分的具体手段,它能将场景中的几何物体顶点集划分为合适的渲染结构。
曲面细分分为三个阶段:外壳着色器(Hull Shader)、镶嵌器(Tessellator)、域着色器(Domain Shader)。

1.1 外壳着色器 Hull Shader

Hull shader实际上是两个阶段(Phase)组成:常量外壳着色器(Constant Hull Shader)和 控制点外壳着色器(Control point hull shader),两个阶段并行运行。

  • Constant Hull Shader 会对每一个面片进行处理,主要任务是输出网格的曲面细分因子(Tessellation Factor),曲面细分因子用于指导面片细分数。
    • 假如要传入的面片是三角形,那么对于三角形就会有三个曲面细分因子,所以edgeFactor[3],对于内部曲面细分因子,因为三角形是最小的图元所以内部是一个因子(个人理解)insideFactor,如果是矩形的话就是四个边因子和两个内部因子
    • 常量外壳着色器会以面片的所有顶点(或控制点)为输入,所以有三个顶点InputPatch<VertexOut, 3> patch,数字为3
    • SV_PrimitiveID 提供传入面片的ID值,可以用来区分不同的Patch,这样你就可以根据Patch的ID来为每个Patch设置不同的细分因子,或者执行其他依赖于Patch ID的操作。它对于每个图元都是不同的
struct PatchTess{float edgeFactor[3];float insideFactor;
};PatchTess PatchConstant(InputPatch<VertexOut, 3> patch, uint patchID : SV_PrimitiveID)
{PatchTess o;o.edgeFactor[0] = 4;o.edgeFactor[1] = 4; o.edgeFactor[2] = 4;o.insideFactor  = 4;return o;
}
  • Control Point Hull Shader 用来改变每个输出顶点的位置信息,例如将一个三角形变为三次贝塞尔三角面片
    • domain:面片类型,参数有三角面tri、四角面片quad、等值线isoline
    • partitioning:曲面细分方式,参数有integer、fractional_even、fractional_odd
      • integer,指新顶点的增加指取决于细分的整数部分(等分),图形可能会出现图片
      • fractional_even,向上取最近的偶数n,将整段切割为n-2个相等长度的部分,和两端较短的部分
      • fractional_odd,向上取最近的奇数n,将整段切割为n-2个相等长度的部分,和两端较短的部分
    • outputtopology:细分创建的三角形面片的绕序,参数有顺时针triangle_cw、逆时针triangle_ccw
    • patchconstantfunc:常量外壳着色器的函数名
    • outputcontrolpoints:外壳着色器的执行次数,即生成的控制点个数
    • maxtessfactor:程序会使用到最大的细分因子
    • SV_OutputControlPointID:当前正在操作的控制点索引ID
[domain("tri")]    
[partitioning("integer")]    
[outputtopology("triangle_cw")]   
[patchconstantfunc("PatchConstant")]   
[outputcontrolpoints(3)]             
[maxtessfactor(64.0f)]        
HullOut ControlPoint (InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){  HullOut o;o.positionOS = patch[id].positionOS;o.texcoord = patch[id].texcoord; return o;
}

常量外壳着色器对每个片元执行一次,输出细分因子等信息;控制点外壳着色器对每个控制点执行一次,并输出对应或衍生的控制点。两个阶段并行运行。

1.2 镶嵌器阶段 Tessellator

这一阶段我们无法对其做出任何控制,全程由硬件控制。在这一阶段,硬件根据之前曲面细分因子对面片做出细分操作。

1.3 域着色器阶段 Domain Shader

就和普通的顶点着色器要做的差不多,我们需要计算每一个控制点的顶点位置等信息。

  • 功能
    • 生成细分顶点,这些顶点是由外壳着色器确定的细分因子和细分模式所生成的
    • 插值属性,根据控制点的属性进行插值,得到细分顶点的属性
    • 输出顶点,域着色器生成最终的顶点数据,并传递给后面的着色器
      在这里插入图片描述
struct HullOut{float3 positionOS : INTERNALTESSPOS; float2 texcoord : TEXCOORD0;
};struct DomainOut
{float4  positionCS      : SV_POSITION;float2  texcoord        : TEXCOORD0; 
};[domain("tri")]      
DomainOut FlatTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{  float3 positionOS = patch[0].positionOS * bary.x + patch[1].positionOS * bary.y + patch[2].positionOS * bary.z; float2 texcoord   = patch[0].texcoord * bary.x + patch[1].texcoord * bary.y + patch[2].texcoord * bary.z;DomainOut output;output.positionCS = TransformObjectToHClip(positionOS);output.texcoord = texcoord;return output; 
}
  • 域着色器输入:接受一个域(Domain)输入,代表了细分后顶点在原始补丁内的位置,这个位置通常用参数空间坐标表示,例如重心坐标(Barycentric Coordinates)(这里的重心坐标,是由硬件在细分过程的一个中间阶段 Tessellator 自动计算得出的)
  • 属性插值:域着色器使用这些参数空间坐标来插值原始控制点的属性。
  • 顶点输出:域着色器输出一个顶点,这个顶点包含计算后的位置和其他属性(如颜色、纹理坐标、法线等)

2.具体实现

2.1 不同的细分策略

2.1.1 平面镶嵌 Flat Tessellation

平面镶嵌只是线性插值位置信息,细分后的图案只比之前多了一些三角面片,单独使用并不能平滑模型。通常和置换贴图配合使用,创建凹凸不平的平面。

Shader "Tessellation/Flat Tessellation"
{Properties{[NoScaleOffset]_BaseMap ("Base Map", 2D) = "white" {}  [Header(Tess)][Space][KeywordEnum(integer, fractional_even, fractional_odd)]_Partitioning ("Partitioning Mode", Float) = 0[KeywordEnum(triangle_cw, triangle_ccw)]_Outputtopology ("Outputtopology Mode", Float) = 0_EdgeFactor ("EdgeFactor", Range(1,8)) = 4 _InsideFactor ("InsideFactor", Range(1,8)) = 4 }SubShader{Tags { "RenderType"="Opaque" }Pass{ HLSLPROGRAM#pragma target 4.6 #pragma vertex FlatTessVert#pragma fragment FlatTessFrag #pragma hull FlatTessControlPoint#pragma domain FlatTessDomain#pragma multi_compile _PARTITIONING_INTEGER _PARTITIONING_FRACTIONAL_EVEN _PARTITIONING_FRACTIONAL_ODD #pragma multi_compile _OUTPUTTOPOLOGY_TRIANGLE_CW _OUTPUTTOPOLOGY_TRIANGLE_CCW #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" CBUFFER_START(UnityPerMaterial) float _EdgeFactor; float _InsideFactor; CBUFFER_ENDTEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); struct Attributes{float3 positionOS   : POSITION; float2 texcoord     : TEXCOORD0;};struct VertexOut{float3 positionOS : INTERNALTESSPOS; float2 texcoord : TEXCOORD0;};struct PatchTess {  float edgeFactor[3] : SV_TESSFACTOR;float insideFactor  : SV_INSIDETESSFACTOR;};struct HullOut{float3 positionOS : INTERNALTESSPOS; float2 texcoord : TEXCOORD0;};struct DomainOut{float4  positionCS      : SV_POSITION;float2  texcoord        : TEXCOORD0; };VertexOut FlatTessVert(Attributes input){ VertexOut o;o.positionOS = input.positionOS; o.texcoord   = input.texcoord;return o;}PatchTess PatchConstant (InputPatch<VertexOut,3> patch, uint patchID : SV_PrimitiveID){ PatchTess o;o.edgeFactor[0] = _EdgeFactor;o.edgeFactor[1] = _EdgeFactor; o.edgeFactor[2] = _EdgeFactor;o.insideFactor  = _InsideFactor;return o;}[domain("tri")]   #if _PARTITIONING_INTEGER[partitioning("integer")] #elif _PARTITIONING_FRACTIONAL_EVEN[partitioning("fractional_even")] #elif _PARTITIONING_FRACTIONAL_ODD[partitioning("fractional_odd")]    #endif #if _OUTPUTTOPOLOGY_TRIANGLE_CW[outputtopology("triangle_cw")] #elif _OUTPUTTOPOLOGY_TRIANGLE_CCW[outputtopology("triangle_ccw")] #endif[patchconstantfunc("PatchConstant")] [outputcontrolpoints(3)]                 [maxtessfactor(64.0f)]                 HullOut FlatTessControlPoint (InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){  HullOut o;o.positionOS = patch[id].positionOS;o.texcoord = patch[id].texcoord; return o;}[domain("tri")]      DomainOut FlatTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION){  float3 positionOS = patch[0].positionOS * bary.x + patch[1].positionOS * bary.y + patch[2].positionOS * bary.z; float2 texcoord   = patch[0].texcoord * bary.x + patch[1].texcoord * bary.y + patch[2].texcoord * bary.z;DomainOut output;output.positionCS = TransformObjectToHClip(positionOS);output.texcoord = texcoord;return output; }half4 FlatTessFrag(DomainOut input) : SV_Target{   half3 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.texcoord).rgb;return half4(color, 1.0); }ENDHLSL}}
}

在这里插入图片描述

2.1.2 PN Tessellation(Per-Node Tessellation)

论文学习 Curved PN Triangles(Paper)
网站学习 OpenGL tutorial

在外壳着色器阶段,把一个三角面片(3个控制点)转换为一个3次贝塞尔三角面片(Cubic Bezier Triangle Patch,一种具有10个控制点的面片),这种策略称为 Curved Point-Normal Triangles(PN triangles),不同于Flat Tessellation,即使没有置换贴图,也能实现改变模型形状,平滑轮廓的作用。满足了资源有限以及环境限制的需求。

我们将使用一个Bezier曲面,Bezier三角形,形式如下
在这里插入图片描述

uvw代表的是质心坐标(u + v + w = 1),10个 b u v w b_{uvw} buvw是CPs,CPs形似如下图,类似于三角形顶部有一个点膨胀的表面在这里插入图片描述
在镶嵌流水线中,

  • 外壳着色器:我们将生成10个控制点并确定细分因子
    • 如何生成控制点?
      • 1.三角形原始顶点B003、B030和B300保持不变
      • 2.两个中点B012和B021在1/3和2/3的位置
      • 3.将中点投影在初始顶点的切平面上
      • 4.对于B111点,我们从原始的三角形中心(三个初始顶点取平均值)到6个中点的平均值(投影后的)取一个矢量
        在这里插入图片描述
  • Tesellation Primitive Generator:PG中再根据细分因子对三角形域进行细分,并对每个新点执行域着色器;
  • 域着色器:把来自PG的质心坐标和来自外壳着色器的10个控制点插入到贝塞尔三角形多项式中,得到的结果是膨胀表面上的坐标。

每个控制点的生成:
在这里插入图片描述在这里插入图片描述

float3 ComputeCP(float3 pA, float3 pB, float3 nA){return (2 * pA + pB - dot((pB - pA), nA) * nA) / 3.0f;
}

在这里插入图片描述

由于控制点的增多,在Hull Shader输出时每个顶点需要多携带两个顶点信息(中心控制点b111可以直接推算出来),例如:b030 可能需要携带b021和b012的顶点信息。修改一下控制点外壳着色器。

  • 用TEXCOORD1和TEXCOORD2来存储额外的两个顶点信息
  • 在PNTessControlPoint控制点着色器中,使用三元运算符来对nextID进行赋值,实现对每个新生成的控制点进行计算。
struct HullOut{float3 positionOS : INTERNALTESSPOS;float3 normalOS   : NORMAL;float2 texcoord   : TEXCOORD0;float3 positionOS1 : TEXCOORD1; // 三角片元每个顶点多携带两个顶点信息float3 positionOS2 : TEXCOORD2;
}; float3 ComputeCP(float3 pA, float3 pB, float3 nA){return (2 * pA + pB - dot((pB - pA), nA) * nA) / 3.0f;
}[domain("tri")]    
[partitioning("integer")]   
[outputtopology("triangle_cw")]   
[patchconstantfunc("PatchConstant")]     
[outputcontrolpoints(3)]                 
[maxtessfactor(64.0f)] 
HullOut PNTessControlPoint(InputPatch<VertexOut,3> patch,uint id : SV_OutputControlPointID){ HullOut output;const uint nextCPID = id < 2 ? id + 1 : 0;output.positionOS    = patch[id].positionOS;output.normalOS      = patch[id].normalOS;output.texcoord      = patch[id].texcoord;output.positionOS1 = ComputeCP(patch[id].positionOS, patch[nextCPID].positionOS, patch[id].normalOS);output.positionOS2 = ComputeCP(patch[nextCPID].positionOS, patch[id].positionOS, patch[nextCPID].normalOS);return output;
}

域着色器负责“Bezier三角形的实现”,根据上面Bezier三角形的表达式

  • 首先计算系数u,v,w(即质心坐标,上面已经介绍了,是PG阶段自动生成的)
  • 把外壳着色器中得到的控制点赋值给变量,并计算E和V,得到b111点
  • 此处对于法线的计算就简化为“最简单的三个控制点插值获取的方式”
[domain("tri")]      
DomainOut PNTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{ float u = bary.x;float v = bary.y;float w = bary.z;float uu = u * u;float vv = v * v;float ww = w * w;float uu3 = 3 * uu;float vv3 = 3 * vv;float ww3 = 3 * ww;float3 b300 = patch[0].positionOS;float3 b210 = patch[0].positionOS1;float3 b120 = patch[0].positionOS2;float3 b030 = patch[1].positionOS;float3 b021 = patch[1].positionOS1;float3 b012 = patch[1].positionOS2;float3 b003 = patch[2].positionOS;float3 b102 = patch[2].positionOS1;float3 b201 = patch[2].positionOS2;  float3 E = (b210 + b120 + b021 + b012 + b102 + b201) / 6.0;float3 V = (b003 + b030 + b300) / 3.0; float3 b111 = E + (E - V) / 2.0f;  // 插值获得细分后的顶点位置float3 positionOS = b300 * ww * w  + b030 * uu * u + b003 * vv * v+ b210 * ww3 * u + b120 * uu3 * w + b021 * uu3 * v + b012 * vv3 * u+ b102 * vv3 * w + b201 * ww3 * v+ b111 * 6.0 * w * u * v;// 此处简化了法线的计算float3 normalOS = patch[0].normalOS * u + patch[1].normalOS * v+ patch[2].normalOS * w;normalOS = normalize(normalOS);float2 texcoord = patch[0].texcoord * u+ patch[1].texcoord * v+ patch[2].texcoord * w;DomainOut output; output.positionCS = TransformObjectToHClip(positionOS);  output.normalWS = TransformObjectToWorldNormal(normalOS);output.uv = texcoord;return output; 
}

然而,目前的做法是有缺陷的,在面对一些相同位置有不同法线的模型时,细分后会造成模型边缘的不连续,形成裂缝(Crack)。

2.1.3 Phong Tessellation

Phong着色应该很熟悉,是一种利用法向量线性差值得到平滑的着色的技术。Phong细分的灵感来自Phong着色,将Phong着色这一概念扩展到空间域。

核心思想:是利用三角形每个角的顶点法线来影响细分过程中新顶点的位置,从而创造出曲面而非平面。
在这里插入图片描述
三角形原始顶点的切平面上, P ′ = P − ( ( P − V ) ⋅ N ) N P' = P - ((P-V)\cdot N)N P=P((PV)N)N

  • P P P 是最初插值的平面位置
  • V V V 是平面上的一个顶点位置
  • N N N 是顶点 V V V 处的法线
  • P ′ P' P P P P 在平面上的投影。
float3 ProjectPointOnPlane(float3 flatPositionWS, float3 cornerPositionWS, float3 normalWS) 
{return flatPositionWS - dot(flatPositionWS - cornerPositionWS, normalWS) * normalWS;
}

在这里插入图片描述
投影在三个切平面的三个点重新组成一个新的三角形,再用当前顶点的重心坐标插值计算出新的点。

 real3 PhongTessellation(real3 positionWS, real3 p0, real3 p1, real3 p2, real3 n0, real3 n1, real3 n2, real3 baryCoords, real shape){// 分别计算三个切平面的投影点real3 c0 = ProjectPointOnPlane(positionWS, p0, n0);real3 c1 = ProjectPointOnPlane(positionWS, p1, n1);real3 c2 = ProjectPointOnPlane(positionWS, p2, n2);// 利用质心坐标插值得到最终顶点位置real3 phongPositionWS = baryCoords.x * c0 + baryCoords.y * c1 + baryCoords.z * c2;// 通过shape 控制平滑程度return lerp(positionWS, phongPositionWS, shape);}

域着色器修改为

DomainOut PhongTriTessDomain (PatchTess tessFactors, const OutputPatch<HullOut,3> patch, float3 bary : SV_DOMAINLOCATION)
{  float3 positionWS = patch[0].positionWS * bary.x + patch[1].positionWS * bary.y + patch[2].positionWS * bary.z; positionWS =PhongTessellation(positionWS, patch[0].positionWS, patch[1].positionWS, patch[2].positionWS, patch[0].normalWS, patch[1].normalWS, patch[2].normalWS, bary, _PhongShape);float2 texcoord   = patch[0].texcoord * bary.x + patch[1].texcoord * bary.y + patch[2].texcoord * bary.z;DomainOut output;output.positionCS = TransformObjectToHClip(positionWS);output.texcoord = texcoord;return output; 
}

2.2 不同的细分因子

2.2.1 基于相机距离

为了让距离相机近的位置细分程度高一点,所以我们要先获取相机的位置,并且得到片元距相机的距离(三角形边缘中点到相机的距离),以此距离来调整细分因子

real3 GetDistanceBasedTessFactor(real3 p0, real3 p1, real3 p2, real3 cameraPosWS, real tessMinDist, real tessMaxDist)
{real3 edgePosition0 = 0.5 * (p1 + p2);real3 edgePosition1 = 0.5 * (p0 + p2);real3 edgePosition2 = 0.5 * (p0 + p1);// In case camera-relative rendering is enabled, 'cameraPosWS' is statically known to be 0,// so the compiler will be able to optimize distance() to length().real dist0 = distance(edgePosition0, cameraPosWS);real dist1 = distance(edgePosition1, cameraPosWS);real dist2 = distance(edgePosition2, cameraPosWS);// The saturate will handle the produced NaN in case min == maxreal fadeDist = tessMaxDist - tessMinDist;real3 tessFactor;tessFactor.x = saturate(1.0 - (dist0 - tessMinDist) / fadeDist);tessFactor.y = saturate(1.0 - (dist1 - tessMinDist) / fadeDist);tessFactor.z = saturate(1.0 - (dist2 - tessMinDist) / fadeDist);return tessFactor;
}
  • real可以根据编译时的设置或宏定义来决定它具体代表哪种浮点数类型
  • cameraPosWS变量不需要在Shader中声明,因为它是由Unity渲染管线自动提供的
  • fadeDist是最大距离tessMaxDist和最小距离tessMinDist之间的差值,这个值定义了距离影响细分因子的范围
  • 对于每个边缘,细分因子是通过将距离与tessMinDist相减,然后除以fadeDist(这个比例表示了当前距离相对于细分开始和结束的距离范围的位置),再用1减去该值(将比例翻转,即摄像机靠近相机时,细分因子接近1),最后使用saturate函数来限制结果在0到1之间(细分因子通常是介于0-1之间的值,0表示没有细分,1表示最大细分级别)。这样,当边缘中点距离相机较近时,细分因子接近1,表示细分程度较高;当边缘中点距离相机较远时,细分因子接近0,表示细分程度较低。

通过上述返回的细分因子,给三角形的边缘细分因子和内部细分因子赋值(内部细分因子设为三个边缘的平均)

real4 CalcTriTessFactorsFromEdgeTessFactors(real3 triVertexFactors)
{real4 tess;tess.x = triVertexFactors.x;tess.y = triVertexFactors.y;tess.z = triVertexFactors.z;tess.w = (triVertexFactors.x + triVertexFactors.y + triVertexFactors.z) / 3.0;return tess;
}

现将常量外壳着色器的代码调整如下

PatchTess PatchConstant (InputPatch<VertexOut,3> patch, uint patchID : SV_PrimitiveID){ PatchTess o;float3 cameraPosWS = GetCameraPositionWS();real3 triVectexFactors =  GetDistanceBasedTessFactor(patch[0].positionWS, patch[1].positionWS, patch[2].positionWS, cameraPosWS, _TessMinDist, _TessMinDist + _FadeDist);float4 tessFactors = _EdgeFactor * CalcTriTessFactorsFromEdgeTessFactors(triVectexFactors);o.edgeFactor[0] = max(1.0, tessFactors.x);o.edgeFactor[1] = max(1.0, tessFactors.y);o.edgeFactor[2] = max(1.0, tessFactors.z);o.insideFactor  = max(1.0, tessFactors.w);return o;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397459.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

字节跳动发Seed-TTS语音合成模型,可模仿任意人的声音,效果逼真

前期我们介绍过很多语音合成的模型&#xff0c;比如ChatTTS&#xff0c;微软语音合成大模型等&#xff0c;随着大模型的不断进步&#xff0c;其合成的声音基本跟真人没有多大的区别。本期介绍的是字节跳动自家发布的语音合成模型Seed-TTS。 Seed-TTS 推理包含四个功能模块&…

无人机之热成像篇

一、定义 无人机热成像技术是指将热成像相机安装在无人机云台上&#xff0c;通过无人机的高空飞行能力和云台的稳定性&#xff0c;结合红外热成像技术对目标区域进行非接触式的温度测量和图像采集。该技术利用物体发出的红外辐射来生成图像&#xff0c;通过测量物体表面温度分布…

Leetcode JAVA刷刷站(8)字符串转换整数

一、题目概述 二、思路方向 要实现这个功能&#xff0c;我们可以遵循以下步骤来编写 myAtoi 函数&#xff1a; 去除前导空格&#xff1a;使用循环或字符串的 trim() 方法&#xff08;虽然直接操作字符串更高效的方式是使用循环&#xff09;。检查符号&#xff1a;记录第一个非…

nodejs 生成随机邮箱

首先安装依赖&#xff1a; npm install faker 示例代码&#xff1a; const faker require(faker); const fs require(node:fs) function generateRandomEmail(num){let str for (let i 0; i < num; i) {str faker.internet.email() &:focus:&;}fs.writeFil…

魔众文库系统v7.0.0版本推荐店铺功能,管理菜单逻辑优化

推荐店铺功能&#xff0c;管理菜单逻辑优化 [新功能] RandomImageProvider 逻辑升级重构&#xff0c;支持更丰富的随机图片生成 [新功能] 资源篮订单参数字段 [新功能] 首页推荐店铺功能&#xff0c;需要在后台 文库系统 → 文库店铺 开启推荐 [系统优化] Grid 快捷编辑请求…

告别DockerHub 镜像下载难题:掌握高效下载策略,畅享无缝开发体验

告别DockerHub 镜像下载难题:掌握高效下载策略,畅享无缝开发体验 1. 介绍 1.1 DockerHub简介 Docker Hub 是 Docker 提供的一项服务,用于与您的团队查找和共享容器映像。 它是世界上最大的容器映像存储库,其中包含一系列内容源,包括容器社区开发人员,开源项目和独立软…

【Kubernetes】Service 类型

Service 类型 1.NodePort2.ClusterlP3.LoadBalance4.ExternalName 在《Service 概念与实战》一文中&#xff0c;Service 的发布使用的是 NodePort 类型。除此之外&#xff0c;Service 的发布还支持 ClusterlP、LoadBalancer 和 ExternalName 这 3 种类型。 1.NodePort 在把 Se…

基于微信小程序的小区业主服务系统(源码+论文+部署讲解等)

博主介绍&#xff1a;✌全网粉丝10W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术栈介绍&#xff1a;我是程序员阿龙&#xff…

SOMEIP_ETS_033:echoUINT8ArrayMinSize_too_short

测试目的&#xff1a; 验证DUT是否能够正确处理小于最小尺寸&#xff08;少于3个元素&#xff09;的UINT8数组参数&#xff0c;并返回相应的错误消息。 描述 本测试用例旨在检验DUT在接收到长度不足3个元素的UINT8数组参数时&#xff0c;是否能够返回错误消息MALFORMED_MESS…

【电路笔记】-L 型衰减器

L 型衰减器 文章目录 L 型衰减器1、概述2、等阻抗L型衰减器3、不等阻抗的 L型衰减器4、L型衰减器示例25、总结L型衰减器是一个简单的电阻分压器网络,可用作固定无源衰减器以降低信号幅度。 1、概述 就其基本形式而言,L 型衰减器只不过是一个非常简单的分压器网络,用于许多电…

数据结构实验:排序算法(附c++源码:冒泡、选择、希尔、快速、堆排序)

实验内容&#xff1a; 输入一组关键字序列&#xff0c;分别实现下列排序算法: 1.编写函数&#xff0c;实现简单选择排序、直接插入排序和冒泡排序算法。 2.编写函数&#xff0c;实现希尔排序算法。 3.编写函数&#xff0c;实现快速排序算法。 4.编写函数&#xff0c;实现堆…

入门 PyQt6 看过来(项目)26 在线购物-主页面

功能导航页面很简单&#xff0c;就几个按钮功能。效果如下图&#xff1a; 1 主界面 ​ 包含 “商品选购”、”下单结算“、”销售分析“四个按钮以及“功能导航”标题。 2 工程目录 首先先创建工程目录及子目录&#xff1a; ​ 3 代码 主窗口文件为Main.py&#xff0c;其…

字体识别验证码的介绍!

字体识别验证码 ​是一种安全机制&#xff0c;‌通过要求用户识别特定字体来验证用户的身份或防止自动化攻击。‌这种验证码通常包含一些经过特殊设计的字符&#xff0c;‌需要用户根据这些字符的特定样式&#xff08;‌如字体、‌字形等&#xff09;‌来进行识别和输入。‌字…

【日常开发】 java返回ECharts数据结构封装

java返回ECharts数据结构封装 一、前端页面示例图如下&#xff1a; 二、准备测试数据&#xff1a; 三、后端 格式封装代码&#xff1a; 四、最终结果&#xff1a; &#x1f388;边走、边悟&#x1f388;迟早会好 一、前端页面示例图如下&#xff1a; 二、准备测试数据&am…

LVS实战演练

目录 一.LVS简介 <1>.工作原理 <2>.相关术语 <3>.lvs集群的常用转发类型 二.部署NAT模式集群实验 <1>.实验环境 1.调度器 2.真实服务器 3.客户端 <2>.实验配置 1.VS中启用内核路由器功能 2.RS装上http服务 3.VS安装ipvsadm软件 4.…

删掉Elasticsearch6.x 的 .security-6索引会怎么样?

背景 玩了下 Elasticsearch 的认证&#xff0c;启动 ES 并添加认证后&#xff0c;看到索引列表额外多了一个 .security-6 。以为是没用的&#xff0c;手欠就给删掉了&#xff0c;然后 Elasticsearch 就访问不了了。 只好再重新部署&#xff0c;再看索引内容&#xff0c;发现这…

VMWare虚拟机磁盘扩容

文章目录 环境背景虚拟机磁盘扩容配置参考 环境 VMWare Workstation 17 ProRHEL 9.4 背景 一个RHEL虚拟机&#xff0c;其 /home 目录大小为30GB。 [ding192 ~]$ df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 4.0M 0…

Multisim 用LM358 运放模拟线性稳压器 - 运放输出饱和 - 前馈电容

就是拿运放搭一个可调的LDO 稳压器&#xff0c;类似下面这个功能框图里的感觉。本来应该非常简单&#xff0c;没什么好说的&#xff0c;没想到遇到了两个问题。 原理 - 理想运放 我用PNP 三极管Q2 作为输出&#xff0c;运放输出电压升高时&#xff0c;流过PNP 三极管BE 的电流变…

关于解决Qt配置clang format插件后打开Qt时报缺少pythonxxx.dll的问题

前言 原本安装过程中没有出现任何问题&#xff0c;但是当我退出Qt&#xff0c;再次打开Qt时报 虽然也不影响正常编程&#xff0c;但是架不住每次打开它都提示&#xff0c;于是准备探究下这个问题&#xff0c;并将其解决掉 第一步&#xff1a; 在官网下载:clang format&…

ubuntu20.04安装k8s和kubesphere

前提 已经安装docker 机器使用小写的hostname hostname建议你的机器把hostname改成小写&#xff0c;不然您会踩坑 官网的教程已经足够详实&#xff0c;推荐看官网教程 https://kubesphere.io/docs/v3.4/quick-start/all-in-one-on-linux/ 安装依赖 sudo apt install connt…