Unity地面交互效果——3、曲面细分基础知识

  大家好,我是阿赵。
  之前介绍了使用动态法线贴图混合的方式模拟轨迹的凹凸感,这次来讲一下更真实的凹凸感制作。不过在说这个内容之前,这一篇先要介绍一下曲面细分着色器(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着色器上面实现的过程就更简单了。

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

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

相关文章

canal+es+kibana+springboot

1、环境准备 服务器&#xff1a;Centos7 Jdk版本&#xff1a;1.8 Mysql版本&#xff1a;5.7.44 Canal版本&#xff1a;1.17 Es版本&#xff1a;7.12.1 kibana版本&#xff1a;7.12.1 软件包下载地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1jRpCJP0-hr9aI…

【计算机网络】网络层:数据平面

一.网络层概述 每台路由器的数据平面的主要功能时从其输入链路向其输出链路转发数据报&#xff0c;控制平面的主要功能是协调这些本地的每路由转发动作&#xff0c;使得数据报沿着源和目的地主机之间的路由器路径最终进行端到端传送。 网络层不运行运输层和应用层协议。 转发是…

Pytorch网络模型训练

现有网络模型的使用与修改 vgg16_false torchvision.models.vgg16(pretrainedFalse) # 加载一个未预训练的模型 vgg16_true torchvision.models.vgg16(pretrainedTrue) # 把数据分为了1000个类别print(vgg16_true) 以下是vgg16预训练模型的输出 VGG((features): S…

FFmpeg直播能力更新计划与新版本发布

// 编者按&#xff1a;客户端作为直接面向用户大众的接口&#xff0c;随着技术的发展进化与时俱进&#xff0c;实现更好的服务是十分必要的。FFmpeg作为最受欢迎的视频和图像处理开源软件&#xff0c;被相关行业的大量用户青睐&#xff0c;而随着HEVC标准的发布到广泛使用&am…

【jvm】虚拟机栈

目录 一、背景二、栈与堆三、声明周期四、作用五、特点&#xff08;优点&#xff09;六、可能出现的异常七、设置栈内存大小八、栈的存储单位九、栈运行原理十、栈帧的内部结构10.1 说明10.2 局部变量表10.3 操作数栈10.4 动态链接10.5 方法返回地址10.6 一些附加信息 十一、代…

整理10个地推拉新app接单平台,免费一手推广渠道平台干货分享

1. 聚量推客&#xff1a; “聚量推客”汇聚了众多市场上有的和没有的地推网推拉新接单项目&#xff0c;目前比较火热&#xff0c;我们做地推和网推从业者如果长期在这行业去做推广可以使用这个平台&#xff0c;价格高数据也好&#xff0c;大部分拉新项目也都是官签一手资源 一…

关于Intel Press出版的《Bedyong BIOS》第2版的观后感

文章目录 此书的背景UEFI运行时DXE基础CPU架构协议PCI协议UEFI驱动的初始化串口DXE驱动示例 《Beyond BIOS》首先介绍一个简单的UEFI应用程序模块&#xff0c;用于展示UEFI应用程序的行为。作者为Waldo。该模块名为“InitializeHelloApplication”&#xff0c;它接受两个参数&a…

Leetcode—101.对称二叉树【简单】

2023每日刷题&#xff08;十九&#xff09; Leetcode—101.对称二叉树 利用Leetcode101.对称二叉树的思想的实现代码 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ bool isSa…

【深度学习基础】Pytorch框架CV开发(1)基础铺垫

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

3 — NLP 中的标记化:分解文本数据的艺术

一、说明 这是一个系列文章的第三篇文章&#xff0c; 文章前半部分分别是&#xff1a; 1 、NLP 的文本预处理技术 2、NLP文本预处理技术&#xff1a;词干提取和词形还原 在本文中&#xff0c;我们将介绍标记化主题。在开始之前&#xff0c;我建议您阅读我之前介绍的关…

Docker的简单安装

安装环境 CentOS Linux release 8.1.1911 (Core)内核4.18.0-147.el8.x86_64Mini Installation 安装前的准备工作 切换国内源 由于centos源已经过期&#xff0c;所以切换为阿里云的yum源&#xff0c;第二个是docker的仓库 wget -O /etc/yum.repos.d/CentOS-Base.repo https:…

云闪付app拉新推广一手渠道 附详细教程

云闪付推广拉新可以通过“聚量推客”申请 云闪付是什么呢&#xff1f;是中国银联出的支付平台&#xff0c;在地推和网推项目里也算是比较火热的app拉新产品&#xff0c;属于地推和网推的百搭项目&#xff0c;操作也简单 只需要动账就算一个数据&#xff0c;目前主要招收地推、…

重新思考边缘负载均衡

本文介绍了Netflix在基于轮询的负载均衡的基础上&#xff0c;集成了包括服务器使用率在内的多因素指标&#xff0c;并对冷启动服务器进行了特殊处理&#xff0c;从而优化了负载均衡逻辑&#xff0c;提升了整体业务性能。原文: Rethinking Netflix’s Edge Load Balancing[1] 我…

第十五章 EM期望极大算法及其推广

文章目录 导读符号说明混合模型伯努利混合模型(三硬币模型)问题描述三硬币模型的EM算法1.初值2.E步3.M步初值影响p,q 含义 EM算法另外视角Q 函数BMM的EM算法目标函数LEM算法导出 高斯混合模型GMM的EM算法1. 明确隐变量, 初值2. E步,确定Q函数3. M步4. 停止条件 如何应用GMM在聚…

软测推荐第二期:10本高质量测试书籍

在不断发展的软件开发领域&#xff0c;测试是质量的守护者&#xff0c;确保产品不仅满足功能要求&#xff0c;而且提供无缝的用户体验。随着软件复杂性的增加&#xff0c;对完善的测试方法和见解的需求也随之增加。 上次给大家推荐了五本书&#xff0c;获得了大家的积极反馈&a…

RT-Thread系统使用常见问题处理记录

1.使用telnet连接系统时发送help指令显示不全的问题。 原因&#xff1a;telnet发送缓存太小。 解决办法&#xff1a;更改agile_telnet软件包里Set agile_telnet tx buffer size的大小。 2.使用Paho MQTT软件包过一段时间报错hard fault on thread: mqtt0 解决办法&#xff1…

UE5加载websocket模块为空

今天测试UE 发现工程启动不了&#xff0c;后来看到原来是websocket模块无法加载。 解决的它的方法很简单&#xff0c;这种问题一般会出现在源码版本的引擎或者是停电了&#xff0c;导致UElaunch版本损坏&#xff0c;解决方法是来到源码版本的引擎 这个目录下&#xff1a; D:\…

稳定性测试—fastboot和monkey区别

一、什么是稳定性测试 稳定性测试是指检验程序在一定时间内能否稳定地运行&#xff0c;在不同的场景下能否正常地工作的过程。主要目的是检测崩溃、内存泄漏、堆栈错误等缺陷。 二、Monkey 1.什么是Monkey 是一个命令行工具&#xff0c;通常在adb安卓调试运行&#xff0c;模…

ABAP简单的队列设置QRFC

场景&#xff1a;用job的方式在接口里启用job&#xff0c;如果接口调用比较频繁&#xff0c;存在同一时间启动相同job的情况&#xff0c;会导致锁表锁程序这种情况。 查阅job函数&#xff0c;发现在JOB_CLOSE函数里自带了类似队列的参数&#xff0c;但是因为是接口&#xff0c…

如何卸载干净 IDEA(图文讲解)windows和Mac教程

大家好&#xff0c;我是sun~ 很多小伙伴会问 Windows / Mac 系统上要怎么彻底卸载 IDEA 呢&#xff1f; 本文通过图片文字&#xff0c;详细讲解具体步骤&#xff1a; 如何卸载干净 IDEA&#xff08;图文讲解&#xff09; Windows1、卸载 IDEA 程序2、注册表清理3、残留清理 M…