Unity URP 曲面细分学习笔记

news2024/9/20 18:29:09

Unity URP 曲面细分学习笔记

  • 1.曲面细分与镶嵌
    • 1.1 外壳着色器 Hull Shader
    • 1.2 镶嵌器阶段 Tessellator
    • 1.3 域着色器阶段 Domain Shader
  • 2.具体实现
    • 2.1 不同的细分策略
      • 2.1.1 平面镶嵌 Flat Tessellation
      • 2.1.2 PN Tessellation(Per-Node Tessellation)
      • 2.1.3 Phong Tessellation
    • 2.2 不同的细分因子
      • 2.2.1 基于相机距离

学百人时遇到了曲面着色器的内容,有点糊里糊涂,于是上知乎找到了两篇大佬的文章 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_END

            TEXTURE2D(_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 == max
    real 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.coloradmin.cn/o/2042731.html

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

相关文章

NPM使用教程:从入门到精通

NPM使用教程&#xff1a;从入门到精通&#xff0c;掌握Node.js包管理神器 引言 随着Node.js的流行&#xff0c;JavaScript已经成为服务器端开发的主力军。NPM&#xff08;Node Package Manager&#xff09;作为Node.js的官方包管理工具&#xff0c;为开发者提供了一个庞大的代…

用R的界面来安装扩展包

下面内容摘录自《R 语言与数据科学的终极指南》专栏文章的部分内容&#xff0c;每篇文章都在 5000 字以上&#xff0c;质量平均分高达 94 分&#xff0c;看全文请点击下面链接&#xff1a; 2章4节&#xff1a;认识和安装R的扩展包&#xff0c;什么是模糊搜索安装&#xff0c;工…

Linux高级编程 8.12 标准IO

目录 一、标准io 二、文件 三、man手册 四、操作文件 1.fopen 2.fputc 3.fgetc 4.fgets 5.fputs 6.fread 7.fwrite 五、命令解读 1.显示目录下的文件信息命令 2.vimdiff小工具 3.stat命令 一、标准io IO&#xff1a; input output I&#xff1a; 键盘是标准输…

吃透张宇18讲+1000题,最后能考到110+吗?

张宇18讲太难了怎么办&#xff1f; 当然是换一本 不难的&#xff0c;张宇18讲的难度确实超过大多数同学能够接受的程度&#xff0c;张宇老师也说&#xff0c;默认大家基础已经很好&#xff0c;所以&#xff0c;如果你想很好的利用张宇18讲&#xff0c;必须要有一个一个很好的基…

ViT——探索自监督视觉Transformers在深度伪造检测中的应用

介绍 论文地址&#xff1a;https://arxiv.org/abs/2405.00355 在这篇评论文章中&#xff0c;我们研究了自监督、预训练Transformers与监督、预训练Transformers和传统神经网络&#xff08;ConvNets&#xff09;相比&#xff0c;在检测深度伪造方面的效果如何。 我们尤其关注…

开源的 P2P 跨平台传文件应用「GitHub 热点速览」

就在上周&#xff0c;发完那篇文章之后不久&#xff0c;我就有幸获得了 GitHub Models 服务公测的访问权限&#xff0c;所以就体验了一下 Playground 聊天功能。 起初&#xff0c;我以为这是“微软菩萨”降临&#xff0c;但玩了一圈下来&#xff0c;发现实际效果并没有那么惊艳…

❤️【接口测试面试】精选50接口测试面试题目,或许能帮到金九银十面试浪潮中的你❤️

基本理论知识 1、什么是(软件)接口测试? 接口测试&#xff1a;是测试系统组件间接口的一种测试方法 接口测试的重点&#xff1a;检查数据的交换&#xff0c;数据传递的正确性&#xff0c;以及接口间的逻辑依赖关系 接口测试的意义&#xff1a;在较早期开展&#xff0c;在软…

iPhone官方商店软件下载---免费看各种剧第⑤弹【iOS版包括iPad】

①点击iPhone自带软件App Store ②点击搜索&#xff0c;输入“周末趣味活动” &#xff0c;点击下载到手机 ③进入软件页面后&#xff0c;我们需要激活页面&#xff0c;点击“feedback” &#xff0c;输入“周六日”&#xff0c;点击“summit” ④等软件闪退后&#xff0c;再点…

智能化清理C盘的方法 小白也可以轻松清理C盘了 不再担心误删文件

智能化清理C盘的方法 小白用户也可以轻松清理C盘了 不再担心误删文件。对于电脑小白来说&#xff0c;C盘清理是一个大大的问题&#xff0c;因为大家都不知道C盘里有哪些文件可以删除&#xff0c;哪些不能删除&#xff0c;所以就直接的导致大家不可能去清理c盘垃圾。 就算是C盘…

一次sql请求,返回分页数据和总条数

日常搬砖&#xff0c;总少不了需要获取分页数据和总行数。 一直以来的实践是编码两次sql请求&#xff0c;分别拉分页数据和totalCount。 最近我在思考&#xff1a; 常规实践为什么不是 在一次sql请求中中执行多次sql查询或多次更新&#xff0c;显而易见的优势&#xff1a; ① 能…

opencv 控制鼠标键盘实现功能setMouseCallback

鼠标事件类型 OpenCV 支持多种鼠标事件类型&#xff0c;常见的包括&#xff1a; cv2.EVENT_LBUTTONDOWN&#xff1a;左键按下 cv2.EVENT_RBUTTONDOWN&#xff1a;右键按下 cv2.EVENT_MBUTTONDOWN&#xff1a;中键按下 cv2.EVENT_LBUTTONUP&#xff1a;左键释放 cv2.EVENT_RBUTT…

Vue3从零开始——带你轻松掌握组件的基本操作

文章目录 1. Vue 组件的基础概念1.1 什么是组件&#xff1f;1.2 组件的作用1.3 组件的分类&#xff08;全局组件 vs 局部组件&#xff09; 2. 创建和注册组件2.1 单文件组件&#xff08;SFC&#xff09;2.2 全局组件注册2.3 局部组件注册 3. 组件命名格式4. ref获取DOM元素4.1 …

CSC7225、CSC7224 双绕组24瓦芯片

CSC7225、CSC7224为高性能电流模式 PWM 开关电源控制器&#xff0c;满足绿色环保标准&#xff1b;CSC7225、CSC7224广泛适用于经济型开关电源&#xff0c;如 DVD、机顶盒、传真机、打印机、LCD 显示器等。CSC7225、CSC7224采用 DIP-8 封装。应用原理如下图&#xff1a; CSC7225…

目前最流行的前端构建工具,你知道几个?

现在的市面上有很多不同的前端构建工具&#xff0c;我们很难对它们一一进行关注。在本文中&#xff0c;我们将重点介绍最受欢迎的几种&#xff0c;并探讨开发人员喜欢或不喜欢它们的原因。 Webpack Webpack 是一个模块打包器&#xff0c;主要用于处理 Web 应用程序的资源的优化…

Kali 2024 逆向调试 GDB 13.2 安装插件 Peda 不兼容报错解决方案

发现问题 如果你尝试直接进行$ apt install gdb安装后应该是最新版的gdb 13.2。并且尝试安装peda后将会出现from six.moves import range报错 2024版的kali的python3是python3.11版本&#xff0c;而peda中的six库支持的是3.11之前的。而gdb13是支持python3.12的。 有趣的一点…

EDAS(企业级应用服务)

1 :介绍 1&#xff1a;edas 提供了应用&#xff0c;开发&#xff0c;部署&#xff0c;监控&#xff0c;运维。同时支持 spring cloud, dubbo ,HSF 2:Ali-Tomcat 基于tomcat改造的Servlet容器。支持原有功能&#xff0c;它在启动时会自动加载Pandora&#xff08;潘多拉&#x…

Java面试八股之简述消息队列P2P模型

简述消息队列P2P模型 P2P模型组件 生产者(Producer)&#xff1a;生产者是创建并发送消息的实体。它可以是一个应用程序、服务或任何产生数据的系统组件。 队列(Queue)&#xff1a;队列是存储消息的数据结构。在P2P模型中&#xff0c;队列扮演着中间存储的角色&#xff0c;负…

[二次元]个人主页搭建

文章目录 域名买一个免费的 框架HexoHexo-Theme-ParticleX Halo 参考 域名 买一个 有钱人玩这个 免费的 github.io 教程在github官方文档有&#xff1b; 框架 Hexo 静态的 Hexo-Theme-ParticleX Argvchsの小窝 Halo 动态的 halo 参考 基于Hexo框架的GitHub个人主页…

推荐一个优秀的 .NET MAUI 组件库

目录 前言 组件介绍 组件展示 布局 按钮 复选框 进度条 导航栏 组件地址 最后 前言 .NET MAUI 的发布&#xff0c;项目中可以使用这个新的跨平台 UI 框架来轻松搭建的移动和桌面应用。 为了帮助大家更快地构建美观且功能丰富的应用&#xff0c;本文将推荐一款优秀…

C语言典型例题39

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 例题3.7 求axbxc0方程的解。要求能处理任何情况下a、b、c的组合。 数学知识&#xff1a; ①当a0时&#xff0c;为一次方程bxc0&#xff1b;x-c/b&#xff1b; ②当a≠0时&#xff0c;为二次函数axbxc0。b-4c≥0时…