Unity地面交互效果——4、制作地面凹陷轨迹

news2024/11/25 21:21:15

  大家好,我是阿赵。
  上一篇介绍了曲面细分着色器的基本用法和思路,这一篇在曲面细分的基础上,制作地面凹陷的轨迹效果。

一、思路分析

  这次需要达到的效果是这样的:
在这里插入图片描述

  从效果上看,这个凹陷在地面下的轨迹,里面有法线变化的效果,然后地表模型也是真实的发生了凹陷变化。所以其实就是之前说到的法线混合轨迹和曲面细分的综合应用。
在这里插入图片描述

  曲面细分的等级实际上是根据自己的需要去调整的
在这里插入图片描述

  做到这么高的细分也可以,但实际上也没太大必要。
  之前已经介绍过法线轨迹的混合,然后有了曲面细分之后,剩下的事情就是把两者结合起来,并且做一个顶点偏移效果了。顶点偏移,实际上也是通过绘制出来的轨迹做偏移,比如我现在有一张黑白的图片,轨迹就绘制在上面,然后对贴图进行局部采样,最后把某些色值范围内的顶点做一个高度的偏移,就做出了这个凹陷的效果了。

二、贴图通道的利用

  还记得最开始的一篇的内容吗?我是使用一个顶视正交摄像机去拍摄这个范围内的轨迹的。
在这里插入图片描述

  这里的这个法线贴图,实际上只用到了RGB三个通道而已,还有一个A通道可以用。于是,可以把需要实现高度偏移的黑白图片,记录在这张贴图的A通道。
在这里插入图片描述

  这样,只需要打一个摄像机,渲染一张RenderTexture,就同时实现了法线贴图和顶点偏移2种效果了。
  值得注意的是,我这里只把需要凹陷的地方刷成了白色,然后不需要凹陷的地方默认是黑色。这样做的好处是计算简单,只需要根据A通道的0-1去决定凹陷的深度就行了。不过有时候,做轨迹是不止凹陷的,不如一个球在沙地上移动,实际上轨迹的边缘还会凸起来的。如果要实现这种效果,那么就要把平地的Alpha值设置成0.5,然后大于0.5的部分是凸起,小于0.5的部分是凹陷。

三、Shader实现

1、顶点片段着色器里的实现:

Shader "azhao/VFGround"
{
    Properties
    {
		_MainTex("Texture", 2D) = "white" {}
		_Color("Color", Color) = (1,1,1,1)
		_centerPos("CenterPos", Vector) = (0,0,0,0)
		_minDis("minVal", Float) = 0
		_maxDis("maxVal", Float) = 20
		_factor("factor", Float) = 15
		_footstepRect("footstepRect",Vector) = (0,0,0,0)
		_footstepTex("footstepTex",2D) = "gray"{}
		_height("height" ,Float) = 0.3
		_NormalTex("Normal Tex", 2D) = "black"{}
		_normalScale("normalScale", Range(-1 , 1)) = 0
		_specColor("SpecColor",Color) = (1,1,1,1)
		_shininess("shininess", Range(1 , 100)) = 1
		_specIntensity("specIntensity",Range(0,1)) = 1
		_ambientIntensity("ambientIntensity",Range(0,1)) = 1


    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            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 _minDis;
			uniform float _maxDis;
			uniform float3 _centerPos;
			uniform float _factor;
			float4 _footstepRect;
			sampler2D _footstepTex;
			float _height;
			sampler2D _NormalTex;
			float4 _NormalTex_ST;
			float _normalScale;
			float4 _specColor;
			float _shininess;
			float _specIntensity;
			float _ambientIntensity;
			struct a2v
			{
				float4 pos	: POSITION;
				float2 uv  : TEXCOORD0;
				float3 normal:NORMAL;
				float3 tangent:TANGENT;
			};

			struct v2t
			{				
				float2 uv  : TEXCOORD0;
				float2 footstepUV : TEXCOORD1;
				float4 worldPos	: TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
				float3 worldTangent :TEXCOORD4;
				float3 worldBitangent : TEXCOORD5;
			};
			struct t2f
			{
				float4 clipPos:SV_POSITION;
				float2 uv: TEXCOORD0;				
				float2 footstepUV:TEXCOORD1;
				float4 worldPos:TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
				float3 worldTangent :TEXCOORD4;
				float3 worldBitangent : TEXCOORD5;
				
			};

			struct TessOut
			{
				float2 uv  : TEXCOORD0;
				float4 worldPos	: TEXCOORD1;
				float2 footstepUV:TEXCOORD2;
				float3 worldNormal : TEXCOORD3;
				float3 worldTangent :TEXCOORD4;
				float3 worldBitangent : TEXCOORD5;
				
			};
			struct TessParam
			{
				float EdgeTess[3]	: SV_TessFactor;//各边细分数
				float InsideTess : SV_InsideTessFactor;//内部点细分数
			};

			float RemapUV(float min, float max, float val)
			{
				return (val - min) / (max - min);
			}
			half3 UnpackScaleNormal(half4 packednormal, half bumpScale)
			{
				half3 normal;
				//由于法线贴图代表的颜色是0到1,而法线向量的范围是-1到1
				//所以通过*2-1,把色值范围转换到-1到1
				normal = packednormal * 2 - 1;
				//对法线进行缩放
				normal.xy *= bumpScale;
				//向量标准化
				normal = normalize(normal);
				return normal;
			}
			//获取Lambert漫反射值
			float GetLambertDiffuse(float3 worldPos, float3 worldNormal)
			{
				float3 lightDir = UnityWorldSpaceLightDir(worldPos);
				float NDotL = saturate(dot(worldNormal, lightDir));
				
				return NDotL;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
			{
				float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
				float specDir = max(dot(normalize(worldNormal), halfDir), 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}

			v2t vert(a2v i)
			{
				v2t o;
				o.worldPos = mul(unity_ObjectToWorld,i.pos);
				o.uv = i.uv;
				o.footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, o.worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, o.worldPos.z));
				o.worldNormal = UnityObjectToWorldNormal(i.normal);
				o.worldTangent = UnityObjectToWorldDir(i.tangent);
				o.worldBitangent = cross(o.worldNormal, o.worldTangent);
				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;
				o.footstepUV = i[idx].footstepUV;
				o.worldNormal = i[idx].worldNormal;
				o.worldTangent = i[idx].worldTangent;
				o.worldBitangent = i[idx].worldBitangent;
				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(_minDis, _maxDis, 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;				
				//线性转换

				float2 uv = i[0].uv * bary.x + i[1].uv * bary.y + i[2].uv * bary.z;
				o.uv = uv;
				o.footstepUV = i[0].footstepUV * bary.x + i[1].footstepUV * bary.y + i[2].footstepUV * bary.z;
				float4 footstepCol = tex2Dlod(_footstepTex, float4(o.footstepUV, 0, 0.0));
				float addVal = footstepCol.a*_height;

				float4 worldPos = i[0].worldPos * bary.x + i[1].worldPos * bary.y + i[2].worldPos * bary.z;
				worldPos.y = worldPos.y - addVal;
				o.worldPos = worldPos;
				o.clipPos = UnityWorldToClipPos(worldPos);
				o.worldNormal = i[0].worldNormal * bary.x + i[1].worldNormal * bary.y + i[2].worldNormal * bary.z;
				o.worldTangent = i[0].worldTangent * bary.x + i[1].worldTangent * bary.y + i[2].worldTangent * bary.z;
				o.worldBitangent = i[0].worldBitangent * bary.x + i[1].worldBitangent * bary.y + i[2].worldBitangent * bary.z;
				return o;
			}
            fixed4 frag (t2f i) : SV_Target
            {
    //            // sample the texture
				//float2 mainUV = i.uv*_MainTex_ST.xy + _MainTex_ST.zw;
    //            fixed4 col = tex2D(_MainTex, mainUV)*_Color;
				//fixed4 footstepCol = tex2D(_footstepTex, i.footstepUV);
				//fixed3 footstepRGB = fixed3(footstepCol.r - 0.5, footstepCol.g - 0.5, footstepCol.b - 0.5);
				//footstepRGB = footstepRGB * footstepCol.a;
				//fixed4 finalCol = col;// fixed4(saturate(col.rgb + footstepRGB), 1);
    //            return finalCol;
				//采样漫反射贴图的颜色
				half4 col = tex2D(_MainTex, i.uv*_MainTex_ST.xy + _MainTex_ST.zw);
				//计算法线贴图的UV
				half2 normalUV = i.uv * _NormalTex_ST.xy + _NormalTex_ST.zw;
				//采样法线贴图的颜色
				half4 normalCol = tex2D(_NormalTex, normalUV);

				fixed4 footstepCol = tex2D(_footstepTex, i.footstepUV);
				fixed3 footstepRGB = UnpackScaleNormal(footstepCol*footstepCol.a, _normalScale).rgb;

				//得到切线空间的法线方向
				half3 normalVal = UnpackScaleNormal(normalCol, _normalScale).rgb;
				//normalVal = footstepRGB;
				//normalVal = normalize(normalVal + footstepRGB);
				normalVal -= footstepRGB;
				//构建TBN矩阵
				float3 tanToWorld0 = float3(i.worldTangent.x, i.worldBitangent.x, i.worldNormal.x);
				float3 tanToWorld1 = float3(i.worldTangent.y, i.worldBitangent.y, i.worldNormal.y);
				float3 tanToWorld2 = float3(i.worldTangent.z, i.worldBitangent.z, i.worldNormal.z);

				//通过切线空间的法线方向和TBN矩阵,得出法线贴图代表的物体世界空间的法线方向
				float3 worldNormal = float3(dot(tanToWorld0, normalVal), dot(tanToWorld1, normalVal), dot(tanToWorld2, normalVal));

				//用法线贴图的世界空间法线,算漫反射
				half diffuseVal = GetLambertDiffuse(i.worldPos, worldNormal);
				diffuseVal = clamp(diffuseVal, 0.6, 1);
				diffuseVal = pow(diffuseVal, 1.5);

				//用法线贴图的世界空间法线,算高光角度
				half3 specCol = _specColor * GetBlinnPhongSpec(i.worldPos, worldNormal)*_specIntensity;

				//最终颜色 = 环境色+漫反射颜色+高光颜色
				half3 finalCol = UNITY_LIGHTMODEL_AMBIENT * _ambientIntensity + saturate(col.rgb*diffuseVal) + specCol;
				return half4(finalCol,1);
            }
            ENDCG
        }
    }
}

值得注意的地方:
1.为了获得细分后的顶点做偏移,所以顶点偏移的过程是写在domainProgram
2.在顶点类程序里面如果需要采样贴图,并不是使用tex2D方法,而是使用tex2Dlod方法

2、Surface着色器里的实现:

Shader "azhao/SurfaceGround"
{
	Properties
	{
		_MainTex("MainTex", 2D) = "white" {}
		_NormalTex("NormalTex", 2D) = "white" {}
		_minDis("minVal", Float) = 0
		_maxDis("maxVal", Float) = 15
		_factor("factor", Float) = 1
		_height("height",float) = 0

		_centerPos("centerPos", Vector) = (0,0,0,0)		
		_footstepRect("footstepRect", Vector) = (0,0,0,0)		
		_footstepTex("footstepTex", 2D) = "black" {}		
		[HideInInspector] _texcoord("", 2D) = "white" {}
		[HideInInspector] __dirty("", Int) = 1
	}

	SubShader
	{
		Tags{ "RenderType" = "Opaque"  "Queue" = "Geometry+0" }
		Cull Back
		CGPROGRAM
		#include "UnityStandardUtils.cginc"
		#pragma target 4.6
		#pragma surface surf Standard keepalpha addshadow fullforwardshadows vertex:vertexDataFunc tessellate:tessFunction 
		struct Input
		{
			float2 uv_texcoord;
			float3 worldPos;
		};

		sampler2D _NormalTex;
		float4 _NormalTex_ST;
		sampler2D _footstepTex;
		float4 _footstepRect;
		sampler2D _MainTex;
		float4 _MainTex_ST;
		float _minDis;
		float _maxDis;
		float4 _centerPos;
		float _factor;
		float _height;



		float RemapUV(float min, float max, float val)
		{
			return (val - min) / (max - min);
		}


		float4 tessFunction( appdata_full v0, appdata_full v1, appdata_full v2 )
		{
			float4 vertex = (v0.vertex + v1.vertex + v2.vertex) / 3;
			float3 worldPos = mul( unity_ObjectToWorld, vertex );
			float smoothstepResult = smoothstep( _minDis , _maxDis , distance(_centerPos.xz, worldPos.xz));
			float tessVal = max( ( ( 1.0 - smoothstepResult ) * _factor ) , 0.1 );
			return tessVal;
		}

		void vertexDataFunc( inout appdata_full v )
		{
			float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
			float temp_output_1_0_g1 = _footstepRect.x;
			float temp_output_1_0_g2 = _footstepRect.y;
			float2 footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, worldPos.z));

			float4 tex2DNode = tex2Dlod(_footstepTex, float4(footstepUV, 0, 0.0));
			float offset = tex2DNode.a * _height*-0.01;
			v.vertex.y += offset;
		}

		void surf( Input i , inout SurfaceOutputStandard o )
		{
			float2 uv_NormalTex = i.uv_texcoord * _NormalTex_ST.xy + _NormalTex_ST.zw;
			float3 worldPos = i.worldPos;
			float temp_output_1_0_g1 = _footstepRect.x;
			float temp_output_1_0_g2 = _footstepRect.y;
			float2 footstepUV = float2(RemapUV(_footstepRect.x, _footstepRect.z, worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, worldPos.z));
			o.Normal = BlendNormals( UnpackNormal( tex2D( _NormalTex, uv_NormalTex ) ) , UnpackNormal( tex2D( _footstepTex, footstepUV) ) );
			float2 uv_MainTex = i.uv_texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
			o.Albedo = tex2D( _MainTex, uv_MainTex ).rgb;
			o.Alpha = 1;
		}

		ENDCG
	}
	Fallback "Diffuse"
}

  用Surface写,过程会简单很多,毕竟不需要自己去声明曲面细分的各个步骤,也不需要自己写光照模型和法线贴图的计算。不过我觉得如果能用顶点片段程序去实现一下,会对这个过程更了解一些。

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

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

相关文章

BigDecimal使用的时候需要注意什么?

BigDecimal只要涉及到浮点数运算都会用到BigDecimal&#xff0c;并且面试的时候经常会问到&#xff0c;那么BigDecimal使用的时候需要注意什么&#xff1f; 目录 1.为什么不能用浮点数表示金额&#xff1f;2.十进制转换二进制3.科学记数法4.IEEE 7545.在线浮点数转换二进制6.原…

Go uuid库介绍

简介&#xff1a; 在现代软件开发中&#xff0c;全球唯一标识符&#xff08;UUID&#xff09;在许多场景中发挥着重要的作用。UUID是一种128位的唯一标识符&#xff0c;它能够保证在全球范围内不重复。在Go语言中&#xff0c;我们可以使用第三方库github.com/google/uuid来方便…

怎样在iOS手机上进行自动化测试

Airtest支持iOS自动化测试&#xff0c;在Mac上为iOS手机部署iOS-Tagent之后&#xff0c;就可以使用AirtestIDE连接设备&#xff0c;像连接安卓设备一样&#xff0c;实时投影、控制手机。iOS测试不仅限于真机测试&#xff0c;iOS模拟器也可以进行。Mac端上部署完成后还可以提供给…

python-re模块

python之正则表达式-基础匹配https://blog.csdn.net/Python_1981/article/details/133777795python之正则表达式-元字符匹配https://blog.csdn.net/Python_1981/article/details/133778805 一、查找 1、findall 2、search 如果没有匹配到&#xff0c;会返回None, 使用group会报…

大文件传输小知识 | UDP和TCP哪个传输速度快?

在网络世界中&#xff0c;好像有两位“传输巨头”常常被提起&#xff1a;UDP和TCP。它们分别代表着用户数据报协议和传输控制协议。那么它们是什么&#xff1f;它们有什么区别&#xff1f;它们在传输大文件时的速度又如何&#xff1f;本文将深度解析这些问题&#xff0c;帮助企…

基于若依的ruoyi-nbcio流程管理系统仿钉钉流程json转bpmn的flowable的xml格式(简单支持发起人与审批人的流程)续

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 之前生产的xml&#xff0c;在bpmn设计里编辑有些内容不正确&#xff0c;包括审批人&#xff0c;关联表单等…

利用python找出偏序集中极大元、极小元、最大元和最小元

1 问题 在离散数学“关系”这一章的学习过程中&#xff0c;学到偏序集中极大元、极小元、最大元和最小元的求解方法&#xff0c;于是提出能不能用python语言实现偏序集中极大元、极小元、最大元和最小元的求解&#xff1f; 2 方法 判断偏序集中的极大元、极小元、最大元和最小元…

Java TCP服务端多线程接收RFID网络读卡器上传数据

本示例使用设备介绍&#xff1a;WIFI/TCP/UDP/HTTP协议RFID液显网络读卡器可二次开发语音播报POE-淘宝网 (taobao.com) import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; impor…

Vue路由介绍及使用

一、单页应用程序介绍 1.概念 单页应用程序&#xff1a;SPA【Single Page Application】是指所有的功能都在一个html页面上实现&#xff0c;当切换不同的功能时&#xff0c;页面不会进行刷新&#xff0c;类似Ajax请求&#xff0c;但请求地址会发生部分变化。 2.具体示例 单…

微型计算机原理1

一、选择题 1.8086CPU的字长是&#xff08;&#xff09;位。 A. 32 B. 128 C. 64 D. 16 2 间接寻址方式中&#xff0c;操作数在(&#xff09;中。 A. 通用寄存器 B. 内存单元 C. 程序计数器 D.堆栈 3.在循环指令LOOP和串操作指令中,用作计数器的寄存器是() A. AX B. BX C. C…

深度学习(CNN+RNN)笔记1

文章目录 视频课 第四课&#xff1a;卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;第一周&#xff1a;Convolutional Neural Networks 卷积神经网络第二周&#xff1a;Case Studies 案例学习第三周&#xff1a;Object Detection 目标检测第四周&#xff…

Jupyter Notebook快速上手

Jupyter Notebook快速上手 文章目录 Jupyter Notebook快速上手1 运行Jupyter Notebook1.1 通过图形化界面打开1.2 通过命令行打开1.3 在指定项目目录下打开 2 Jupyter Notebook运行后无法自动打开网页3 Jupyter Notebook运行代码没反应4 退出4.1 终端退出4.2 命令行退出 此教程…

[PHP]ShopXO企业级B2C免费开源商城系统 v2.3.1

ShopXO 企业级B2C免费开源电商系统&#xff01; 求实进取、创新专注、自主研发、国内领先企业级B2C电商系统解决方案。 遵循Apache2开源协议发布&#xff0c;无需授权、可商用、可二次开发、满足99%的电商运营需求。 PCH5、支付宝小程序、微信小程序、百度小程序、头条&抖音…

Chrome插件精选 — 屏幕录像插件

Chrome实现同一功能的插件往往有多款产品&#xff0c;逐一去安装试用耗时又费力&#xff0c;在此为某一类型插件挑选出比较好用的一款或几款&#xff0c;尽量满足界面精致、功能齐全、设置选项丰富的使用要求&#xff0c;便于节省一个个去尝试的时间和精力。 1. 屏幕录像机 - S…

Java GC机制 —— 个人笔记

文章目录 JVM内存区对象是否需要回收&#xff1f;1. 引用计数法2. 可达性分析法&#xff08;根搜索算法&#xff09;Java的引用 对象何时被回收&#xff1f;回收策略回收策略1&#xff1a;引用计数算法回收策略2&#xff1a;标记清除算法&#xff08;Mark-Sweep&#xff09;回收…

MySQL 8.0 Clone Plugin 详解

文章目录 前言1. 克隆插件安装2. 克隆插件的使用2.1 本地克隆2.2 远程克隆 3. 克隆任务监控4. 克隆插件实现4.1 Init 阶段4.2 File Copy4.3 Page Copy4.4 Redo Copy4.5 Done 5. 克隆插件的限制6. 克隆插件与 Xtrabackup 的异同7. 克隆插件相关参数后记 前言 克隆插件&#xff…

武汉某母婴用品公司 - 集简云连接ERP和营销系统,实现库存管理的自动化

品牌介绍与关怀理念 武汉某母婴用品公司是一家专注于高端孕婴童护理用品的企业&#xff0c;积极响应和关怀孕产人群&#xff0c;全方位提供从待产用品到产后护理用品&#xff0c;再到婴童洗护用品和初生婴儿用品等一系列全面的母婴产品。我们的使命是满足客户的需求&#xff0…

光学仿真 | 仿真推动以人类视觉感知为本的汽车显示设计

如果产品设计无法使终端用户产生共鸣&#xff0c;就不会存在卓越的工程设计。您可以设计一种结构坚固的方向盘&#xff0c;但如果它被放在错误的位置&#xff0c;就无法实现其用于转向的主要目的。 同样&#xff0c;在围绕人类视觉进行设计时&#xff0c;显示器其实无需具备尽…

分布式任务调度(01)--分布式锁

Quartz集群模式可水平扩展&#xff0c;也可分布式调度&#xff0c;但需业务方在数据库中添加对应表&#xff0c;有强侵入性。于是探索分布式锁模式。 1 超时关单 通常做定时任务每2min检查前半小时的订单&#xff0c;将待支付订单列表查出&#xff0c;然后对订单中的商品进行…

数据结构(c语言版) 队列

链队列 要求&#xff1a;实现链队列的创建、初始化、入队、出队 &#xff08;先进先出&#xff09; 代码 // // Created by My.cy on 2023/10/19. // //链队列 创建、初始化、入队、出队 先进先出#include <stdio.h> #include <malloc.h>//定义结构体 struct…