【Unity3D】选中物体描边特效

news2025/1/12 22:54:54

1 前言

        描边的难点在于如何检测和识别边缘,当前实现描边特效的方法主要有以下几种:

        1)基于顶点膨胀的描边方法

        在 SubShader 中开 2 个 Pass 渲染通道,第一个 Pass 通道渲染膨胀的顶点,即将顶点坐标沿着法线方向向外扩展,并使用纯色给扩展后的顶点着色,第二个 Pass 通道渲染原顶点,并覆盖第一个 Pass 通道渲染的内部。

        该方案实现简单,算法效率高,但是对于拐角较大的两个面交界处,会出现描边断裂,并且描边的宽度会受到透视投影影响。

        2)基于法线的描边方法

        对于物体的任意一顶点,判断视线向量(该点指向相机的向量)与该点法线向量的夹角(记为 θ)是否近似 90 度,如果近似 90 度,就将该点识别为边缘,并进行边缘着色。实际应用中,通常采用模糊描边着色法,而不是阈值法,即将 sin(θ) 作为该点渲染描边色的强度。

        该方案属于内描边,实现简单,算法效率高,但是物体中间 θ 值近似 90 度的地方也会被描边。

        3)基于深度纹理的描边方法

        对渲染后的屏幕纹理进行二次渲染,根据像素点周围颜色的差异判断是否是物体边缘像素,如果是边缘,需要重新进行边缘着色。判断边缘的具体做法是:对该像素点周围的像素点进行卷积运算,得到该点的梯度(反映该点附件颜色突变的强度),根据梯度阈值判断该点是否是边缘。

        该方案属于内描边,实现有一定难度,算法效率一般,算法依赖梯度阈值,并且受颜色、光照、阴影等影响较大如:地面的影子可能会被描边。

        4)基于法线和深度纹理的描边方法

        通过引入法线纹理,对方案 3) 进行优化。该方案不仅考虑了深度纹理图中任意点的梯度,还考虑了法线纹理图中该点的梯度,通过综合两种梯度和梯度阈值,确定该点是否需要边缘着色。

        该方案属于内描边,效果较好,实现较难,算法依赖梯度阈值。

        5)基于模板纹理模糊膨胀的描边方法

        首先使用纯色对选中的物体进行渲染,得到模板纹理,接着对模板纹理进行模糊处理,使模板颜色往外扩,得到模糊纹理,再根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色。

        该方案属于外描边,效果较好,实现较难,但算法不依赖阈值。

        本文代码资源见→Unity3D选中物体描边特效。

2 基本原理

        本文采用基于模板纹理模糊膨胀的描边方法,本节将通过图文详细介绍该算法的原理。

        1)原图

        2)模板纹理

        说明:清屏颜色为 (0, 0, 0, 0),后面会用到 。通过 Graphics.ExecuteCommandBuffer(commandBuffer) 对选中的物体进行渲染,得到模板纹理。

        3)模糊纹理

        说明:通过对模板纹理进行模糊处理, 使模板颜色向外扩展,得到模糊纹理,外扩的部分就是需要描边的部分

        4)合成纹理

         说明:根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色,如下:

// 由于模糊纹理的外部清屏颜色是(0, 0, 0, 0), blur.a=0, 因此模糊纹理的外部也会被渲染为原色
color.rgb = lerp(source.rgb, blur.rgb, blur.a); // lerp(a,b,x)=(1-x)*a+x*b
color.a = source.a;

        5)描边颜色和宽度渐变

         描边颜色由模板颜色决定,通过设置模板颜色随时间变化,实现描边颜色渐变,通过设置模板透明度随时间变化,实现描边在出现和消失,视觉上感觉描边在扩大和缩小。

fixed4 frag(v2f i) : SV_Target // 片段着色器
{
    float t1 = sin(_Time.z); // _Time = float4(t/20, t, t*2, t*3)
	float t2 = cos(_Time.z);
	// 描边颜色随时间变化, 描边透明度随时间变化, 视觉上感觉描边在膨胀和收缩
	return float4(t1 + 1, t2 + 1, 1 - t1, 1 - t2);
}

        渐变模板图如下:

         渐变描边效果如下:

       

        6)缺陷

         如果描边的物体存在重叠,由于所有物体共一个模板纹理,将存在描边消融现象。

        模板纹理如下:

         描边消融如下:

         可以看到,正方体、球体、胶囊体、圆柱体下方及人体都没有描边特效,因为它们在模板纹理的内部,被消融掉了。

3 代码实现

        OutlineEffect.cs

using System;
using UnityEngine;
using UnityEngine.Rendering;

public class OutlineEffect : MonoBehaviour {
    public static Action<CommandBuffer> renderEvent; // 渲染事件
    public float offsetScale = 2; // 模糊处理像素偏移
    public int iterate = 3; // 模糊处理迭代次数
    public float outlineStrength = 3; // 描边强度

    private Material blurMaterial; // 模糊材质
    private Material compositeMaterial; // 合成材质
    private CommandBuffer commandBuffer; // 用于渲染模板纹理
    private RenderTexture stencilTex; // 模板纹理
    private RenderTexture blurTex; // 模糊纹理

    private void Awake() {
        blurMaterial = new Material(Shader.Find("Custom/Outline/Blur"));
        compositeMaterial = new Material(Shader.Find("Custom/Outline/Composite"));
        commandBuffer = new CommandBuffer();
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination) {
        if (renderEvent != null) {
            RenderStencil(); // 渲染模板纹理
            RenderBlur(source.width, source.height); // 渲染模糊纹理
            RenderComposite(source, destination); // 渲染合成纹理
        } else {
            Graphics.Blit(source, destination); // 保持原图
        }
    }

    private void RenderStencil() { // 渲染模板纹理
        stencilTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
        commandBuffer.SetRenderTarget(stencilTex);
        commandBuffer.ClearRenderTarget(true, true, Color.clear); // 设置模板清屏颜色为(0,0,0,0)
        renderEvent.Invoke(commandBuffer);
        Graphics.ExecuteCommandBuffer(commandBuffer);
    }

    private void RenderBlur(int width, int height) { // 对模板纹理进行模糊化
        blurTex = RenderTexture.GetTemporary(width, height, 0);
        RenderTexture temp = RenderTexture.GetTemporary(width, height, 0);
        blurMaterial.SetFloat("_OffsetScale", offsetScale);
        Graphics.Blit(stencilTex, blurTex, blurMaterial);
        for (int i = 0; i < iterate; i ++) {
            Graphics.Blit(blurTex, temp, blurMaterial);
            Graphics.Blit(temp, blurTex, blurMaterial);
        }
        RenderTexture.ReleaseTemporary(temp);
    }

    private void RenderComposite(RenderTexture source, RenderTexture destination) { // 渲染合成纹理
        compositeMaterial.SetTexture("_MainTex", source);
        compositeMaterial.SetTexture("_StencilTex", stencilTex);
        compositeMaterial.SetTexture("_BlurTex", blurTex);
        compositeMaterial.SetFloat("_OutlineStrength", outlineStrength);
        Graphics.Blit(source, destination, compositeMaterial);
        RenderTexture.ReleaseTemporary(stencilTex);
        RenderTexture.ReleaseTemporary(blurTex);
        stencilTex = null;
        blurTex = null;
    }
}

        说明: OnRenderImage 方法是MonoBehaviour的生命周期方法,在所有的渲染完成后由 MonoBehavior 自动调用,该方法依赖相机组件,由于 OnRenderImage 在渲染后调用,因此被称为后处理操作,它是 Unity3D 特效的重要理论分支;Graphics.Blit(source, dest, material) 用于将 source 纹理按照 material 材质重新渲染到 dest;CommandBuffer 携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果;OutlineEffect 脚本组件必须挂在相机上

        OutlineObject.cs

using UnityEngine;
using UnityEngine.Rendering;

public class OutlineObject : MonoBehaviour {
    private Material stencilMaterial; // 模板材质

    private void Awake() {
        stencilMaterial = new Material(Shader.Find("Custom/Outline/Stencil"));
    }

    private void OnEnable() {
        OutlineEffect.renderEvent += OnRenderEvent;
        // _StartTime用于控制每个选中的对象颜色渐变不同步
        stencilMaterial.SetFloat("_StartTime", Time.timeSinceLevelLoad * 2);
    }

    private void OnDisable() {
        OutlineEffect.renderEvent -= OnRenderEvent;
    }

    private void OnRenderEvent(CommandBuffer commandBuffer) {
        Renderer[] renderers = gameObject.GetComponentsInChildren<Renderer>();
        foreach (Renderer r in renderers) {
            commandBuffer.DrawRenderer(r, stencilMaterial); // 将renderer和material提交到主camera的commandbuffer列表进行渲染
        }
    }
}

        说明:被选中的物体将会添加 OutlineObject 脚本组件,用于渲染选中对象的模板纹理,每个选中对象独立持有 stencilMaterial,互不干扰,描边的渐变相位(由_StartTime控制)可以由选中对象独立控制,这样每个模板的颜色就可以独立控制,从而实现每个选中对象描边各异的效果。

        SelectController.cs

using System.Collections.Generic;
using UnityEngine;

public class SelectController : MonoBehaviour {
    private List<GameObject> targets; // 选中的游戏对象
    private List<GameObject> loseFocus; // 失焦的游戏对象
    private RaycastHit hit; // 碰撞信息

    private void Start() {
        Camera.main.gameObject.AddComponent<OutlineEffect>();
        targets = new List<GameObject>();
        loseFocus = new List<GameObject>();
        hit = new RaycastHit();
    }

    private void Update() {
        if (Input.GetMouseButtonUp(0)) {
            GameObject hitObj = GetHitObj();
            if (hitObj == null) { // 未选中任何物体, 已描边的全部取消描边
                targets.ForEach(obj => loseFocus.Add(obj));
                targets.Clear();
            }
            else if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
                if (targets.Contains(hitObj)) { // Ctrl重复选中, 取消描边
                    loseFocus.Add(hitObj);
                    targets.Remove(hitObj);
                } else { // Ctrl追加描边
                    targets.Add(hitObj);
                }
            } else { // 单选描边
                targets.ForEach(obj => loseFocus.Add(obj));
                targets.Clear();
                targets.Add(hitObj);
                loseFocus.Remove(hitObj);
            }
            DrawOutline();
        }
    }

    private void DrawOutline() { // 描边
        targets.ForEach(obj => {
            if (obj.GetComponent<OutlineObject>() == null) {
                obj.AddComponent<OutlineObject>();
            } else {
                obj.GetComponent<OutlineObject>().enabled = true;
            }
        });
        loseFocus.ForEach(obj => {
            if (obj.GetComponent<OutlineObject>() != null) {
                obj.GetComponent<OutlineObject>().enabled = false;
            }
        });
        loseFocus.Clear();
    }

    private GameObject GetHitObj() { // 获取屏幕射线碰撞的物体
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit)) {
            return hit.collider.gameObject;
        }
        return null;
    }
}

        说明:通过单击物体,给选中的物体添加 OutlineObject 脚本组件,再由 OutlineEffect 控制描边。按住 Ctrl 键再单击物体会追加选中,如果重复选中,会取消描边。

        StencilShader.shader

Shader "Custom/Outline/Stencil"
{
	Properties
	{
		_StartTime ("startTime", Float) = 0 // _StartTime用于控制每个选中的对象颜色渐变不同步
	}

	SubShader
	{
		Pass
		{	
			CGPROGRAM // CG语言的开始
			// 编译指令 着色器名称 函数名称
			#pragma vertex vert // 顶点着色器, 每个顶点执行一次
			#pragma fragment frag // 片段着色器, 每个像素执行一次
			#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度

			// 导入头文件
			#include "UnityCG.cginc"

			float _StartTime;

			struct appdata // 顶点函数输入结构体
			{
				half4 vertex: POSITION; // 顶点坐标
			};

			struct v2f // 顶点函数输出结构体
			{
				float4 pos : SV_POSITION;
			};
			
			v2f vert(appdata v) // 顶点着色器
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target // 片段着色器
			{
				float t1 = sin(_Time.z - _StartTime); // _Time = float4(t/20, t, t*2, t*3)
				float t2 = cos(_Time.z - _StartTime);
				// 描边颜色随时间变化, 描边透明度随时间变化, 视觉上感觉描边在膨胀和收缩
				return float4(t1 + 1, t2 + 1, 1 - t1, 1 - t2);
			}

			ENDCG // CG语言的结束
		}
	}

	FallBack off
}

        说明: StencilShader 用于渲染模板纹理,并且其颜色和透明度随时间变化,实现描边颜色渐变、宽度在膨胀和收缩效果。_StartTime 用于控制时间偏移,由物体被选中的时间决定,每个物体被选中的时间不一样,因此选中物体模板颜色各异,描边颜色也各异。

        BlurShader.shader

Shader "Custom/Outline/Blur"
{
	Properties
	{
		_MainTex ("stencil", 2D) = "" {}
		_OffsetScale ("offsetScale", Range (0.1, 3)) = 2 // 模糊采样偏移
	}
	
	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Lighting Off
			Fog { Mode Off }
			
			CGPROGRAM // CG语言的开始
			#pragma vertex vert // 顶点着色器, 每个顶点执行一次
			#pragma fragment frag // 片段着色器, 每个像素执行一次
			#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
			
			#include "UnityCG.cginc"

			sampler2D _MainTex;
			half _OffsetScale;
			half4 _MainTex_TexelSize; //_MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)

			struct appdata // 顶点函数输入结构体
			{
				half4 vertex: POSITION;
				half2 texcoord: TEXCOORD0;
			};

			struct v2f // 顶点函数输出结构体
			{
				float4 pos : POSITION;
				half2 uv[4] : TEXCOORD0;
			};
			
			v2f vert (appdata v) // 顶点着色器
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				half2 offs = _MainTex_TexelSize.xy * _OffsetScale;
				// uv坐标向四周扩散
				o.uv[0].x = v.texcoord.x - offs.x;
				o.uv[0].y = v.texcoord.y - offs.y;
				o.uv[1].x = v.texcoord.x + offs.x;
				o.uv[1].y = v.texcoord.y - offs.y;
				o.uv[2].x = v.texcoord.x + offs.x;
				o.uv[2].y = v.texcoord.y + offs.y;
				o.uv[3].x = v.texcoord.x - offs.x;
				o.uv[3].y = v.texcoord.y + offs.y;
				return o;
			}

			fixed4 frag(v2f i) : COLOR // 片段着色器
			{
				fixed4 color1 = tex2D(_MainTex, i.uv[0]);
				fixed4 color2 = tex2D(_MainTex, i.uv[1]);
				fixed4 color3 = tex2D(_MainTex, i.uv[2]);
				fixed4 color4 = tex2D(_MainTex, i.uv[3]);
				fixed4 color;
				// max: 2个向量中每个分量都取较大者, 这里通过max函数将模板的边缘向外扩, rgb=stencil.rgb
				color.rgb = max(color1.rgb, color2.rgb);
				color.rgb = max(color.rgb, color3.rgb);
				color.rgb = max(color.rgb, color4.rgb);
				color.a = (color1.a + color2.a + color3.a + color4.a) / 4; // 透明度向外逐渐减小
				return color;
			}
			
			ENDCG // CG语言的结束
		}
	}
	
	Fallback off
}

        说明: BlurShader 用于渲染模糊纹理,通过对模板纹理模糊化处理,实现模板颜色外扩,外扩的部分就是需要描边的部分。 

        CompositeShader.shader

Shader "Custom/Outline/Composite"
{
	Properties
	{
		_MainTex ("source", 2D) = "" {}
		_StencilTex ("stencil", 2D) = "" {}
		_BlurTex ("blur", 2D) = "" {}
		_OutlineStrength ("OutlineStrength", Range(1, 5)) = 3
	}
	
	SubShader
	{
		Pass
		{
			ZTest Always
			Cull Off
			ZWrite Off
			Lighting Off
			Fog { Mode off }
			
			CGPROGRAM // CG语言的开始
			#pragma vertex vert // 顶点着色器, 每个顶点执行一次
			#pragma fragment frag // 片段着色器, 每个像素执行一次
			#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
			
			#include "UnityCG.cginc"
		
			sampler2D _MainTex;
			sampler2D _StencilTex;
			sampler2D _BlurTex;
			float _OutlineStrength;
			float4 _MainTex_TexelSize; //_MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)

			struct appdata // 顶点函数输入结构体
			{
				half4 vertex: POSITION;
				half2 texcoord: TEXCOORD0;
			};

			struct v2f // 顶点函数输出结构体
			{
				float4 pos : POSITION;
				half2 uv : TEXCOORD0;
			};
			
			v2f vert (appdata v) // 顶点函数输出结构体
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				if (_MainTex_TexelSize.y < 0)
					o.uv.y = 1 - o.uv.y; // 在Direct3D平台下, 如果我们开启了抗锯齿, 则_MainTex_TexelSize.y 会变成负值
				return o;
			}
			
			fixed4 frag(v2f i) : COLOR // 顶点着色器
			{
				fixed4 source = tex2D(_MainTex, i.uv);
				fixed4 stencil = tex2D(_StencilTex, i.uv);
				if (any(stencil.rgb))
				{ // 绘制选中物体
					return source;
				}
				else
				{ // 绘制选中物体以外的图像
					fixed4 blur = tex2D(_BlurTex, i.uv);
					fixed4 color;
					color.rgb = lerp(source.rgb, blur.rgb * _OutlineStrength, saturate(blur.a - stencil.a));
					color.a = source.a;
					return color;
				}
			}

			ENDCG // CG语言的结束
		}
	}
	
	Fallback Off
}

        说明: CompositeShader 用于渲染合成纹理,根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色。

4 运行效果

        单击物体选中描边,按住 Ctrl 键单击物体追加选中描边,单击地面或空白地方所有已描边物体取消描边(删除了地面的碰撞体组件)。

5 拓展

        HighlightingSystem 插件也实现了基于模板纹理模糊膨胀的描边方法,插件资源在Unity3D选中物体描边特效的【Assets\Plugins\HighlightingSystem】目录下。

        该插件与本文的区别在于模板纹理的渲染方式不同,本文通过 CommandBuffer 渲染模板纹理,HighlightingSystem 通过第二个相机渲染模板纹理,具体操作如下:在 HighlightingEffect 脚本组件的 OnPreRender 方法中,通过 Copy 主相机新生成一个相机,并将其 cullingMask 设置为 highlightingLayer(值为7,表示新相机只渲染图层为 7 的物体),用于渲染图层为 7  的物体的模板纹理,被添加 HighlightableObject 脚本组件的物体在渲染模板纹理时其图层将会被临时更改为 7,渲染结束后又恢复原图层。

        该插件存在一个缺陷,7 号图层需要预留出来,否则 7 号图层的物体周围将存在一个模糊的灰色边缘。

        HighlightingSystem 插件的使用方法如下:将 HighlightingEffect 脚本组件挂在相机下,给需要描边的物体添加 SpectrumController 脚本组件。运行时,已添加 SpectrumController 脚本组件的物体将会被自动添加 HighlightableObject 脚本组件。

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

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

相关文章

Android BLE HIDS Data ,从问询DB 到写入Android 节点的flow 之一

BLE 的HIDS服务&#xff0c;可以理解为BR/EDR HID在BLE 上的简化版&#xff0c;因BLE的特殊性&#xff0c;BR/EDR HID的部分feature在HIDS 中被移除。 本文将讨论Android 蓝牙BLE 中HIDS 服务如何接收 Data 并写入到Android 节点"/dev/uhid"的flow。----关于Android…

openpowerlink 01

源码地址&#xff1a; https://sourceforge.net/projects/openpowerlink/ 源码目录 qt 源码目录 qt MN 源码 MN是主站的意思 用cmake 构建 qt cmakelists.txt分析 文件 mnobd.cdc&#xff1a; 此文件用于配置 MN 堆栈。它包括MN和所有CN的所有配置数据&#xff0c;包括网…

云安全解决方案安全保障体系框架

安全需求和挑战 从风险管理 的角度讲&#xff0c;主要就是管理资产、威胁、脆弱性和防护措施及其相关关系&#xff0c;最终保障云计算平台的持续安全&#xff0c;以及 其所支撑的业务的安全。 云计算 平台是在传统 IT技术的基础上&#xff0c;增加了一个虚拟化层&#xff0c;并…

[Spring MVC7] 解决Redis乱码前缀问题

最近在做Redis缓存的时候&#xff0c;遇到了一个棘手的问题&#xff0c;简单来说就是项目使用Spring的RedisTemplate进行Redis数据存取操作&#xff0c;实际应用中发现Redis中key和value会出现“无意义”乱码前缀。如果是普通的java程序是没有这个问题。 本文解决Redis乱码问题…

基于Matlab-gui信号系统设计

目 录 1绪论 - 1 - 2系统分析和开发软件概述 - 6 - 2.1 需求分析 - 6 - 2.2系统开发环境和运行环境 - 6 - 2.3 Matlab图形用户界面(GUI) - 6 - 2.4 系统主要目标及功能 - 7 - 2.4.1 系统目标 - 7 - 2.4.2 系统主要功能 - 8 - 3系统软件平台的设计 10 3.1系统整体框图 10 3.2系统…

阿里的三个「价值支点」

历史总是轮回的。 2015年5月&#xff0c;身处低谷的阿里换了掌舵人&#xff0c;张勇由COO升任CEO&#xff0c;随后进行了一轮组织架构调整&#xff0c;并通过新零售、淘宝直播、阿里云等业态为阿里安上未来发展引擎。 当时的阿里市值涨至2000亿美元左右&#xff0c;华尔街投资…

终端天线—11.NFC线圈仿真

NFC线圈单体仿真 NFC线圈主要根据终端设备预留的空间大小&#xff0c;去设计走线宽度和圈数以及面积&#xff0c;NFC单体主要关注其13.56MHz处的电感量大小&#xff0c;以及阻抗的虚部和实部&#xff0c;可以根据ST和NXP芯片的要求去设计。 一、Original model 1.Simulation …

C语言文件操作【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;为什么使用文件&#xff0c;什么是文件&#xff0c;文件的打开和关闭的操作方法&#xff0c;文件的顺序读写于随机读写&#xff0c;文件读取结束的判定&#x1f440;。 文章目录一、为什么使用文件&#x1f356;二、什么是文件&…

React 入门:使用脚手架写一个Hello组件

文章目录本文目标开发前的准备编写主页面 index.html编写外壳组件 App.js编写入口文件 index.js代码组件化开发 Hello 组件开发 Welcome 组件引用组件组件化实现效果样式的模块化提升编码效率本文目标 通过使用脚手架确实让我们很方便的创建一个 React 项目基础代码结构&#…

力扣(LeetCode)11. 盛最多水的容器(C++)

双指针贪心 盛水的面积 长度 \times 左右柱子最低高度 area(r−l)min(height[l],height[r])area (r-l)\times min(height[l],height[r])area(r−l)min(height[l],height[r]) 初始时&#xff0c;我们不知道每个柱子的高度&#xff0c;但是我们可以选取最左侧柱子和最右侧柱子…

期望E与高斯分布的期望

目录 1. 期望定义 2. 期望性质 2.1 用期望定义方差 / 标准差 方差定义 标准差定义 方差的表示——离散型&#xff1a; 方差的表示——连续型&#xff1a; 方差的性质 3. (一元)高斯分布定义 4. (一元)高斯分布的性质 5. 二维随机向量的数学期望E与方差σ 参考 1. …

PyCharm安装部署(一) 百篇文章学PyQT

本文章是百篇文章学PyQT的第一篇&#xff0c;本文讲述如何安装PyCharm IDEA工具&#xff0c;其它工具也可以但是PyCharm 相对来说用的人多大家都认可(方案成熟)&#xff0c;pycharm是一款功能强大的python编辑器&#xff0c;具有跨平台性&#xff0c;本文介绍一下pycharm在wind…

SSM 医院在线挂号系统

SSM 医院在线挂号系统 SSM 医院在线挂号系统 功能介绍 首页 登录注册 图片轮播展示 系统简介 系统公告 医院介绍 医生展示 医院资讯 预约挂号 收藏 评论 在线留言 查看留言 后台管理 登录 管理员管理 修改密码 医院信息管理 医生信息管理 用户权限管理 科室信息管理 预约挂号…

微信小程序如何转云开发

微信小程序云开发&#xff0c;为前端全栈开发提供了很大的便利。本文主要介绍普通的微信小程序如何让转为云开发。 一、建cloudfunction文件夹 cloudfunction文件夹建立在小程序的根目录下。 二、修改 project.config.json配置 在 project.config.json文件中&#xff0c;添加&…

ip子网的划分方法

目录 1 子网划分的定义&#xff1a; 2 掩码介绍 3、子网划分要解决的问题&#xff1a; 4 子网划分步骤 5 范例1根据ip和掩码求子网和网络广播地址&#xff1a; 6 范例2根据ip和掩码求同网段地址 7 合并子网的例子根据ip范围合并&#xff0c;并添加回程路由&#xff1a; …

还不进来看吗?c趁你不注意偷偷将你的数据类型转换啦

前言 如果不了解 整形提升 的小伙伴可就要注意了,c偷偷将你的数据类型改变了你都不知道.快点和牛牛一起学习一下c语言中 整形提升的知识吧 ! 一、整形提升是什么&#xff0c;又是怎样提升的? 不知道小伙伴们有没有听过整形提升这个词? 整形提升是什么呢? C语言中,在进行…

[附源码]java毕业设计石林县石漠化信息查询分析系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【GlobalMapper精品教程】020:Lidar点云数据分类(自动分类、手动分类)案例详解

航测点云通常跟DSM一致,即包含植被、房屋等信息,必须进行点云分类、过滤,才能生成准确的高程点、等高线和DEM等地形数据。本文以案例的形式详细讲解globalmapper23中点云工具及使用方法。 文章目录 1. 点云分类2. 创建地面高程格网3. 地形绘制4. 格网转点云5. 点云抽稀6. 点…

面试:java中的各种锁

共享锁 共享锁有CountDownLatch, CyclicBarrier, Semaphore, ReentrantReadWriteLock等 ReadWriteLock&#xff0c;顾名思义&#xff0c;是读写锁。它维护了一对相关的锁 — — “读取锁”和“写入锁”&#xff0c;一个用于读取操作&#xff0c;另一个用于写入操作。“读取锁…

java计算机毕业设计ssm建设路小学芙童币和芙童印章管理系统

项目介绍 随着移动互联网技术的迅速发展,时代对人们的知识水平和综合素质要求也越来越高了,各种教育管理系统层出不穷。其中以建设路小学吉祥物“芙童”为卡通原型设计的芙童印章、芙童币深受学生和老师们的喜爱。这是学校结合德育教学、少先队活动和社会实践活动为他们量身定…