3D模型人物换装系统(三 优化合批处理,提取合批配置可,封装)

news2024/12/23 5:56:49

3D模型人物换装系统三(优化合批处理,提取合批配置可,封装)

  • 介绍
  • 法线贴图问题
  • 规划以及封装
    • 缺陷
    • 修改整理
  • 整合
  • 总结

介绍

本文使用2018.4.4和2020.3.26进行的测试
这里先说一下我上一篇没有解决的问题,法线贴图不正确,如果按照我之前的文章创建其他贴图的方式去创建法线贴图,你会发现法线贴图不正确。
如果这里不太明白换装的流程可以参考我之前
3D模型人物换装系统一
3D模型人物换装系统二
请添加图片描述

法线贴图问题

之前创建贴图的方式如下所示
这里能看到我创建所有图片的时候的格式都是使用的RGBA32,但是后来我在网上查了以后才发现,其实法线贴图是不带A通道的图片,所以这里应该使用的是RGB24而不是使用的RGBA32,前后对比图我就不放了,因为我对这里的方法进行了封装,已经不是原来的方式创建和合并贴图了。

newAlbedoMapTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);
newNormalMapTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);
newMaskMapTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);

规划以及封装

这里我大概讲一下我之前脚本设计的缺陷,以及我想修改成的样子,在文章最后面我会把我修改完成的脚本和资源都放过来,如果大家在后面使用脚本的过程中遇到问题可以在询问我。

缺陷

  1. 原来合并设计的是太过于死板,材质球的属性是直接提取出来的并没有动态去做,也就是说我这里只能手动修改,如果换一个四个贴图的材质还需要修改下面的代码,这样太不方便,需要封装一下这里
    private const string COMBINE_ALBEDOMAP_TEXTURE = "_AlbedoMap";
    private const string COMBINE_NORMALMAP_TEXTURE = "_NormalMap";
    private const string COMBINE_MASKMAP_TEXTURE = "_MaskMap";
  1. 创建贴图的大小也是固定死的现在,这里也是有缺陷,没法动态的调整
   /// <summary>
    /// Only for merge materials.
    /// </summary>
	private const int COMBINE_TEXTURE_MAX = 256;
  1. 合并这里或多或少有点缺陷,但是不算是有问题,这里传入的如果是true则就是需要合并,但是如果合并不成功的话就直接报错
if (combine)
        {
            Shader tmpShader = Shader.Find("E3D/Actor/PBR-MaskRG-Normal");
            newMaterial = new Material(tmpShader);
            oldUV = new List<Vector2[]>();

            // merge the texture
            List<Texture2D> AlbedoTextures = new List<Texture2D>();
            List<Texture2D> NormalTextures = new List<Texture2D>();
            List<Texture2D> MaskTextures = new List<Texture2D>();

            for (int i = 0; i < materials.Count; i++)
            {
                AlbedoTextures.Add(materials[i].GetTexture(COMBINE_ALBEDOMAP_TEXTURE) as Texture2D);
                NormalTextures.Add(materials[i].GetTexture(COMBINE_NORMALMAP_TEXTURE) as Texture2D);
                MaskTextures.Add(materials[i].GetTexture(COMBINE_MASKMAP_TEXTURE) as Texture2D);
            }
            newAlbedoMapTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);
            newNormalMapTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);
            newMaskMapTex = new Texture2D(COMBINE_TEXTURE_MAX, COMBINE_TEXTURE_MAX, TextureFormat.RGBA32, true);

            Rect[] uvs = newAlbedoMapTex.PackTextures(AlbedoTextures.ToArray(), 0);
            //newNormalMapTex.PackTextures(NormalTextures.ToArray(), 0);
            newMaskMapTex.PackTextures(MaskTextures.ToArray(), 0);

            newMaterial.SetTexture(COMBINE_ALBEDOMAP_TEXTURE, newAlbedoMapTex);
            newMaterial.SetTexture(COMBINE_NORMALMAP_TEXTURE, newNormalMapTex);
            newMaterial.SetTexture(COMBINE_MASKMAP_TEXTURE, newMaskMapTex);

            // reset uv
            Vector2[] uva, uvb;
            for (int i = 0; i < combineInstances.Count; i++)
            {
                uva = combineInstances[i].mesh.uv;
                uvb = new Vector2[uva.Length];
                for (int k = 0; k < uva.Length; k++)
                {
                    uvb[k] = new Vector2((uva[k].x * uvs[i].width) + uvs[i].x, (uva[k].y * uvs[i].height) + uvs[i].y);
                }
                oldUV.Add(uva);
                combineInstances[i].mesh.uv = uvb;
            }
        }

		if (combine)
		{
    		r.material = newMaterial;
        	for (int i = 0; i < combineInstances.Count; i++)
        	{
            	combineInstances[i].mesh.uv = oldUV[i];
        	}
   		}
  1. Shader也是写死的,不是动态的
 Shader tmpShader = Shader.Find("E3D/Actor/PBR-MaskRG-Normal");

修改整理

  1. 创建一个配置类,当创建模型的时候根据自己需要传入定制化配置文件,这样可以动态调整创建贴图的大小、动态调整合并贴图的数量、动态修改想要合并成的材质Shader。

/// <summary>
/// 贴图属性
/// </summary>
public class CombineClass
{
    /// <summary>
    /// 新Shader属性名
    /// </summary>
    public string NewShaderPropertiesName = "_AlbedoMap";

    /// <summary>
    /// 原始Shader属性名
    /// </summary>
    public string OriginalPropertiesName = "_AlbedoMap";

    /// <summary>
    /// 图片格式
    /// </summary>
    public TextureFormat Format = TextureFormat.RGBA32;

    /// <summary>
    /// 
    /// </summary>
    public bool MipChain = true;

    /// <summary>
    /// 实例化
    /// </summary>
    /// <param name="propertiesName"></param>
    /// <param name="format"></param>
    /// <param name="mipChain"></param>
    public CombineClass(string newPropertiesName, string orinalPropertiesName, TextureFormat format, bool mipChain)
    {
        this.NewShaderPropertiesName = newPropertiesName;
        this.OriginalPropertiesName = orinalPropertiesName;
        this.Format = format;
        this.MipChain = mipChain;
    }
}


public class CombineConfig
{
    /// <summary>
    /// Only for merge materials.
    /// 仅适用于合并材质.
    /// </summary>
    private int COMBINE_TEXTURE_MAX = 256;

    /// <summary>
    /// Shader名称
    /// </summary>
    private string ShaderName = "";

    /// <summary>
    /// 贴图设置集合
    /// </summary>
    private List<CombineClass> combineClasses = new List<CombineClass>();

    /// <summary>
    /// 实例化
    /// </summary>
    /// <param name="COMBINE_TEXTURE_MAX"></param>
    /// <param name="combineClasses"></param>
    public CombineConfig(int COMBINE_TEXTURE_MAX, string ShaderName, List<CombineClass> combineClasses)
    {
        this.COMBINE_TEXTURE_MAX = COMBINE_TEXTURE_MAX;
        this.ShaderName = ShaderName;
        this.combineClasses = combineClasses;
    }

    /// <summary>
    /// 获取合并贴图大小
    /// </summary>
    /// <returns></returns>
    public int CombineTextureMax() { return this.COMBINE_TEXTURE_MAX; }

    /// <summary>
    /// 获取Shader名
    /// </summary>
    /// <returns></returns>
    public string GetShaderName() { return this.ShaderName; }

    /// <summary>
    /// 获取贴图设置集合数量
    /// </summary>
    /// <returns></returns>
    public int GetCombineCount() { return this.combineClasses.Count; }

    /// <summary>
    /// 获取贴图设置集合
    /// </summary>
    /// <returns></returns>
    public List<CombineClass> GetCombineList() { return combineClasses; }
}
  1. 获取需要合并的模型的所有的所有Shader,如果是只有一个Shader则你combine为true时会进行合批处理,如果Shader数量多了哪怕你combine为true则也不进行合批。
  2. 加入了一个材质回调,这样在调用的时候可以对材质球进行设置

整合

CombineClass.cs


/// <summary>
/// 贴图属性
/// </summary>
public class CombineClass
{
    /// <summary>
    /// 新Shader属性名
    /// </summary>
    public string NewShaderPropertiesName = "_AlbedoMap";

    /// <summary>
    /// 原始Shader属性名
    /// </summary>
    public string OriginalPropertiesName = "_AlbedoMap";

    /// <summary>
    /// 图片格式
    /// </summary>
    public TextureFormat Format = TextureFormat.RGBA32;

    /// <summary>
    /// 
    /// </summary>
    public bool MipChain = true;

    /// <summary>
    /// 实例化
    /// </summary>
    /// <param name="propertiesName"></param>
    /// <param name="format"></param>
    /// <param name="mipChain"></param>
    public CombineClass(string newPropertiesName, string orinalPropertiesName, TextureFormat format, bool mipChain)
    {
        this.NewShaderPropertiesName = newPropertiesName;
        this.OriginalPropertiesName = orinalPropertiesName;
        this.Format = format;
        this.MipChain = mipChain;
    }
}

CombineConfig.cs


public class CombineConfig
{
    /// <summary>
    /// Only for merge materials.
    /// 仅适用于合并材质.
    /// </summary>
    private int COMBINE_TEXTURE_MAX = 256;

    /// <summary>
    /// Shader名称
    /// </summary>
    private string ShaderName = "";

    /// <summary>
    /// 贴图设置集合
    /// </summary>
    private List<CombineClass> combineClasses = new List<CombineClass>();

    /// <summary>
    /// 实例化
    /// </summary>
    /// <param name="COMBINE_TEXTURE_MAX"></param>
    /// <param name="combineClasses"></param>
    public CombineConfig(int COMBINE_TEXTURE_MAX, string ShaderName, List<CombineClass> combineClasses)
    {
        this.COMBINE_TEXTURE_MAX = COMBINE_TEXTURE_MAX;
        this.ShaderName = ShaderName;
        this.combineClasses = combineClasses;
    }

    /// <summary>
    /// 获取合并贴图大小
    /// </summary>
    /// <returns></returns>
    public int CombineTextureMax() { return this.COMBINE_TEXTURE_MAX; }

    /// <summary>
    /// 获取Shader名
    /// </summary>
    /// <returns></returns>
    public string GetShaderName() { return this.ShaderName; }

    /// <summary>
    /// 获取贴图设置集合数量
    /// </summary>
    /// <returns></returns>
    public int GetCombineCount() { return this.combineClasses.Count; }

    /// <summary>
    /// 获取贴图设置集合
    /// </summary>
    /// <returns></returns>
    public List<CombineClass> GetCombineList() { return combineClasses; }
}

UCombineSkinnedMgr.cs

using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System;

public class UCombineSkinnedMgr
{
    /// <summary>
    /// Combine SkinnedMeshRenderers together and share one skeleton.
    /// 将蒙皮网格渲染器组合在一起并共享一个骨架.(优化合批报错,如果无法合批但是combine为true,则进行不分批处理)
    /// Merge materials will reduce the drawcalls, but it will increase the size of memory.
    /// 合并材质会减少drawcalls,但会增加内存大小.
    /// </summary>
    /// <param name="skeleton">combine meshes to this skeleton(a gameobject)</param>
    /// <param name="meshes">meshes need to be merged</param>
    /// <param name="combine">merge materials or not</param>
    public void CombineObject(CombineConfig config, GameObject skeleton, SkinnedMeshRenderer[] meshes, bool combine = false, Action<Material> action = null)
    {
        //Fetch all bones of the skeleton
        //获取所有的骨骼
        List<Transform> transforms = new List<Transform>();
        transforms.AddRange(skeleton.GetComponentsInChildren<Transform>(true));

        //the list of materials
        List<Shader> shaders = new List<Shader>();

        //the list of materials
        //所有材质球
        List<Material> materials = new List<Material>();

        //the list of meshes
        //所有网格
        List<CombineInstance> combineInstances = new List<CombineInstance>();

        //the list of bones
        //所有骨骼节点
        List<Transform> bones = new List<Transform>();

        #region 合批使用

        // Below informations only are used for merge materilas(bool combine = true)
        //以下信息仅用于合并材料(bool-combine=true)原始UV坐标
        List<Vector2[]> oldUV = null;
        //合并之后得新材质球
        Material newMaterial = null;
        //创建新贴图集合
        List<Texture2D> MapTex = new List<Texture2D>();
        for (int i = 0; i < config.GetCombineCount(); i++)
        {
            MapTex.Add(null);
        }
        #endregion

        // Collect information from meshes and shader
        // 获取网格和shader信息
        for (int i = 0; i < meshes.Length; i++)
        {
            SkinnedMeshRenderer smr = meshes[i];
            materials.AddRange(smr.materials);
            for (int j = 0; j < smr.materials.Length; j++)
            {
                if (!shaders.Contains(smr.materials[j].shader))
                {
                    shaders.Add(smr.materials[j].shader);
                }
            }
            // Collect meshes
            for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
            {
                CombineInstance ci = new CombineInstance();
                ci.mesh = smr.sharedMesh;
                ci.subMeshIndex = sub;
                combineInstances.Add(ci);
            }
            // Collect bones
            for (int j = 0; j < smr.bones.Length; j++)
            {
                int tBase = 0;
                for (tBase = 0; tBase < transforms.Count; tBase++)
                {
                    if (smr.bones[j].name.Equals(transforms[tBase].name))
                    {
                        bones.Add(transforms[tBase]);
                        break;
                    }
                }
            }
        }
        // merge materials
        //合并材质
        if (combine && shaders.Count == 1)
        {
            if (config.GetShaderName() == "") newMaterial = new Material(shaders[0]);
            else newMaterial = new Material(Shader.Find(config.GetShaderName()));

            oldUV = new List<Vector2[]>();

            // merge the texture
            //合并贴图
            List<List<Texture2D>> texture2Ds = new List<List<Texture2D>>();
            for (int i = 0; i < config.GetCombineCount(); i++)
            {
                texture2Ds.Add(new List<Texture2D>());
            }

            for (int i = 0; i < materials.Count; i++)
            {
                Material mat = materials[i];
                for (int j = 0; j < config.GetCombineCount(); j++)
                {
                    texture2Ds[j].Add(mat.GetTexture(config.GetCombineList()[j].OriginalPropertiesName) as Texture2D);
                }
                //int ind = 0;
                //texture2Ds.ForEach((txtLst) =>
                //{
                //    txtLst.Add(mat.GetTexture(config.GetCombineList()[ind].OriginalPropertiesName) as Texture2D);
                //    ind++;
                //});
            }

            Rect[] uvs = new Rect[config.GetCombineCount()];
            for (int i = 0; i < config.GetCombineCount(); i++)
            {
                MapTex[i] = new Texture2D(config.CombineTextureMax(), config.CombineTextureMax(), config.GetCombineList()[i].Format, config.GetCombineList()[i].MipChain);
                uvs = MapTex[i].PackTextures(texture2Ds[i].ToArray(), 0);
                newMaterial.SetTexture(config.GetCombineList()[i].NewShaderPropertiesName, MapTex[i]);
            }

            action?.Invoke(newMaterial);

            #region 导出图片

            //WriteIntoPic(TextureToTexture2D(newMaterial.GetTexture(COMBINE_ALBEDOMAP_TEXTURE)), "albedo");
            //WriteIntoPic(TextureToTexture2D(newMaterial.GetTexture(COMBINE_NORMALMAP_TEXTURE)), "normal");
            //WriteIntoPic(TextureToTexture2D(newMaterial.GetTexture(COMBINE_MASKMAP_TEXTURE)), "mask");

            #endregion

            // reset uv
            Vector2[] uva, uvb;
            for (int i = 0; i < combineInstances.Count; i++)
            {
                uva = combineInstances[i].mesh.uv;
                uvb = new Vector2[uva.Length];
                for (int k = 0; k < uva.Length; k++)
                {
                    uvb[k] = new Vector2((uva[k].x * uvs[i].width) + uvs[i].x, (uva[k].y * uvs[i].height) + uvs[i].y);
                }
                oldUV.Add(uva);
                combineInstances[i].mesh.uv = uvb;
            }
        }

        // Create a new SkinnedMeshRenderer
        SkinnedMeshRenderer oldSKinned = skeleton.GetComponent<SkinnedMeshRenderer>();
        if (oldSKinned != null)
        {
            GameObject.DestroyImmediate(oldSKinned);
        }
        SkinnedMeshRenderer r = skeleton.AddComponent<SkinnedMeshRenderer>();
        r.sharedMesh = new Mesh();
        r.sharedMesh.CombineMeshes(combineInstances.ToArray(), combine && shaders.Count == 1, false);// Combine meshes
        r.bones = bones.ToArray();// Use new bones
        if (combine && shaders.Count == 1)
        {
            r.material = newMaterial;
            for (int i = 0; i < combineInstances.Count; i++)
            {
                combineInstances[i].mesh.uv = oldUV[i];
            }
        }
        else
        {
            r.materials = materials.ToArray();
        }
    }

    #region 导出图片

    private Texture2D TextureToTexture2D(Texture texture)
    {
        Texture2D texture2D = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
        RenderTexture currentRT = RenderTexture.active;
        RenderTexture renderTexture = RenderTexture.GetTemporary(texture.width, texture.height, 32);
        Graphics.Blit(texture, renderTexture);

        RenderTexture.active = renderTexture;
        texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
        texture2D.Apply();

        RenderTexture.active = currentRT;
        RenderTexture.ReleaseTemporary(renderTexture);

        return texture2D;
    }

    public void WriteIntoPic(Texture2D tex, string name)
    {
        //编码纹理为PNG格式 
        var bytes = tex.EncodeToPNG();
        File.WriteAllBytes(Application.dataPath + "/" + name + ".png", bytes);
    }

    #endregion
}

UCharacterController.cs

using UnityEngine;

public class UCharacterController
{
    /// <summary>
    /// GameObject reference
    /// </summary>
	public GameObject Instance = null;

    /// <summary>
    /// 换装总组装数量
    /// </summary>
    public int m_MeshCount = 7;

    public string Role_Skeleton;
    public string Role_Body;
    public string Role_Clothes;
    public string Role_Hair;
    public string Role_Head;
    public string Role_Pants;
    public string Role_Shoes;
    public string Role_Socks;

    /// <summary>
    /// 创建对象
    /// </summary>
    /// <param name="job"></param>
    /// <param name="skeleton"></param>
    /// <param name="body"></param>
    /// <param name="cloak"></param>
    /// <param name="face"></param>
    /// <param name="hair"></param>
    /// <param name="hand"></param>
    /// <param name="leg"></param>
    /// <param name="mainweapon"></param>
    /// <param name="retina"></param>
    /// <param name="subweapon"></param>
    /// <param name="combine"></param>
    public UCharacterController(CombineConfig config, string job, string skeleton, string body, string clothes, string hair, string head, string pants, string shoes, string socks, bool combine = false, System.Action<Material> action = null)
    {
        Object res = Resources.Load("RoleMesh/" + job + "/" + job + "/" + skeleton);
        this.Instance = GameObject.Instantiate(res) as GameObject;
        this.Role_Skeleton = skeleton;
        this.Role_Body = body;
        this.Role_Clothes = clothes;
        this.Role_Hair = hair;
        this.Role_Head = head;
        this.Role_Pants = pants;
        this.Role_Shoes = shoes;
        this.Role_Socks = socks;

        string[] equipments = new string[m_MeshCount];
        equipments[0] = "Body/" + Role_Body;
        equipments[1] = "Clothes/" + Role_Clothes;
        equipments[2] = "Hair/" + Role_Hair;
        equipments[3] = "Head/" + Role_Head;
        equipments[4] = "Pants/" + Role_Pants;
        equipments[5] = "Shoes/" + Role_Shoes;
        equipments[6] = "Socks/" + Role_Socks;

        SkinnedMeshRenderer[] meshes = new SkinnedMeshRenderer[m_MeshCount];
        GameObject[] objects = new GameObject[m_MeshCount];
        for (int i = 0; i < equipments.Length; i++)
        {
            res = Resources.Load("RoleMesh/" + job + "/" + equipments[i]);
            objects[i] = GameObject.Instantiate(res) as GameObject;
            meshes[i] = objects[i].GetComponentInChildren<SkinnedMeshRenderer>();
        }

        UCharacterManager.Instance.CombineSkinnedMgr.CombineObject(config, Instance, meshes, combine, action);

        for (int i = 0; i < objects.Length; i++)
        {
            GameObject.DestroyImmediate(objects[i].gameObject);
        }
    }

    public void Delete()
    {
        GameObject.Destroy(Instance);
    }
}

测试代码
UCharacterManager.cs

using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 换装管理器
/// </summary>
public class UCharacterManager : MonoBehaviour
{
    public static UCharacterManager Instance;

    private UCombineSkinnedMgr skinnedMgr = null;
    public UCombineSkinnedMgr CombineSkinnedMgr { get { return skinnedMgr; } }

    private int characterIndex = 0;
    private Dictionary<int, UCharacterController> characterDic = new Dictionary<int, UCharacterController>();
    public UCharacterManager()
    {

        skinnedMgr = new UCombineSkinnedMgr();
    }

    private void Awake()
    {
        Instance = this;
    }

    public UCharacterController mine;

    private void Start()
    {
        //mine = Generatecharacter(new CombineConfig(256, "", new List<CombineClass> {
        //    new CombineClass("_AlbedoMap","_AlbedoMap",TextureFormat.RGBA32,true),
        //    new CombineClass("_NormalMap","_NormalMap",TextureFormat.RGB24,true),
        //    new CombineClass("_MaskMap","_MaskMap", TextureFormat.RGBA32,true),
        //}), "MaTa", "MaTa", "Body1", "Clothes1", "Hair1", "Head1", "Pants1", "Shoes1", "Socks1", true, (mat) =>
        //{
        //    mat.SetFloat("_SideLightScale", 0);
        //});
        mine = Generatecharacter(new CombineConfig(256, "Standard", new List<CombineClass> {
            new CombineClass("_MainTex","_AlbedoMap",TextureFormat.RGBA32,true),
            new CombineClass("_BumpMap","_NormalMap",TextureFormat.RGB24,true),
            new CombineClass("_DetailMask","_MaskMap", TextureFormat.RGBA32,true),
        }), "MaTa", "MaTa", "Body1", "Clothes1", "Hair1", "Head1", "Pants1", "Shoes1", "Socks1", true, (mat) =>
        {
            mat.SetFloat("_SideLightScale", 0);
        });
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ChangeRole();
        }
    }

    public void ChangeRole()
    {
        //if (mine != null)
        //{
        //    mine.Delete();
        //}

        //int a = Random.Range(1, 4);

        //mine = Generatecharacter("MaTa", "MaTa", "Body" + a, "Clothes" + a, "Hair" + a, "Head" + a, "Pants" + a, "Shoes" + a, "Socks" + a, true);
    }

    #region 创建人物模型骨骼

    public UCharacterController Generatecharacter(CombineConfig config, string job, string skeleton, string body, string clothes, string hair, string hand, string pants, string shoes, string socks, bool combine = false, System.Action<Material> action = null)
    {

        UCharacterController instance = new UCharacterController(config, job, skeleton, body, clothes, hair, hand, pants, shoes, socks, combine, action);
        characterDic.Add(characterIndex, instance);
        characterIndex++;

        return instance;
    }

    #endregion
}

完整资源

总结

人物换装的优化这篇文章后面应该不会在出了,预计得优化大概就是这样,如果有别的好点子可以私信我交流一下。希望这篇文章能给大家带来帮助,感谢大家的支持和关注,感谢点赞。

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

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

相关文章

24岁,拿到18K,我真的很卷?

前言 前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff0c…

MySQL,使用Union组合查询

1、基本使用 Union可将多条select语句组合成一个结果集&#xff0c;常见的使用场景有2种&#xff1a; 在单个查询中&#xff0c;从不同的表返回类似结构的数据&#xff1b;对单个表执行多个查询&#xff0c;按单个查询返回数据。 例&#xff1a;检索出所有价格<50的产品&…

java多线程创建的三种方式

第一种 第二种 第三种&#xff08;想获得线程的执行结果&#xff0c;建议使用这种&#xff09;

2023 英特尔On技术创新大会直播 |探索视觉AI的无限可能

2023 英特尔On技术创新大会直播 | 探索视觉AI的无限可能 前言一未来的 AI&#xff1a;释放视觉 AI 真正潜力二AI技术突破、视觉Al挑战及前沿研究创新三全尺度视觉学习全尺度视觉学习示例1.GridConv 实现三维人体姿态估计更高准确率2.KW 预训练及迁移模型性能3.无数据增强稠密对…

8.基于Cortex-M4内核的STM32F40x中断分析

通用中断知识铺垫1&#xff1a; 完整的CM4有256个可编程中断&#xff08;16个内核中断和240个外部中断&#xff09;&#xff0c;而stm32f40x共有92个中断&#xff08;10内82可编程&#xff09;&#xff0c;意思是说STM32F40X这个单片机没有完全释放CM4内核的资源。 CM4内核的中…

赴日IT培训课程 程序员新思路!

先说好&#xff0c;跟国内相比&#xff0c;日本IT并不发达。日本IT是依托着日本传统强势的制造业和政府机关发展的&#xff0c;所以开发的大多数软件也是面向这些的&#xff0c;由于日本人的严谨态度&#xff0c;各种文档的编写层出不穷&#xff0c;不像国内程序员每天没日没夜…

全方位的账号安全管理

如今&#xff0c;特权账户范围广、数量大且极不稳定是当前各行业面临黑客等攻击行为的最大安全隐患。而且&#xff0c;由于特权账户的权限极大&#xff0c;一旦其被攻击者破解&#xff0c;就能完全掌控组织的IT基础设施&#xff0c;从而引发防护控制失效、机密数据泄露、商业诈…

[网络安全]密码字典快速生成——在线网站

目录 1.密码字典在线生成器one点击链接 ​编辑2.密码字典在线生成器two点击链接 3.密码字典在线生成器three点击链接 个人推荐生成器1&#xff0c;因为复制黏贴好用。 1.密码字典在线生成器one点击链接 2.密码字典在线生成器two点击链接 3.密码字典在线生成器three点击链接 看…

Spring Boot学习随笔- 文件上传和下载(在线打开、附件下载、MultipartFile)

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十二章、文件上传、下载 文件上传 文件上传是指将文件从客户端计算机传输到服务器的过程。 上传思路 前端的上传页面&#xff1a;提交方式必须为post&#xff0c;enctype属性必须为multipart/form-data开发…

Java:获取线程的名字

代码示例&#xff1a; package com.thb;public class Test6 {public static void main(String[] args) {System.out.println(Thread.currentThread().getName());}}运行输出&#xff1a;

【单调栈】LeetCode2030:含特定字母的最小子序列

作者推荐 map|动态规划|单调栈|LeetCode975:奇偶跳 涉及知识点 单调栈 题目 给你一个字符串 s &#xff0c;一个整数 k &#xff0c;一个字母 letter 以及另一个整数 repetition 。 返回 s 中长度为 k 且 字典序最小 的子序列&#xff0c;该子序列同时应满足字母 letter 出…

常见分布的分布列/概率密度、分布函数、期望、方差、特征函数

0-1分布 二项分布 泊松分布 几何分布 均匀分布 正态分布 指数分布 伽马分布 卡方分布

【C语言】自定义类型之联合和枚举

目录 1. 前言2. 联合体2.1 联合体类型的声明2.2 联合体的特点2.3 相同成员的结构体和联合体对比2.4 联合体大小的计算2.4 判断当前机器的大小端 3. 枚举3.1 枚举类型的声明3.2 枚举类型的优点3.3 枚举类型的使用 1. 前言 在之前的博客中介绍了自定义类型中的结构体&#xff0c;…

【SQLite】SQLite数据库简单使用与Navicat安装-加密

Sqlite为免安装数据库&#xff0c;安装步骤总结&#xff1a; 官网下载Sqlit数据库&#xff0c;官网下载地址:https://www.sqlite.org/download.html 下载: sqlite-dll-win64-x64-3390400.zip或者32位sqlite-dll-win32 sqlite-tools-win-x64-3440200.zip或者32位sqlite-tools-wi…

【✅Java中有了基本类型为什么还需要包装类?】

✅Java中有了基本类型为什么还需要包装类&#xff1f; ✅Java中有了基本类型为什么还需要包装类✅Java的8中基本数据类型 ✅知识拓展✅基本类型和包装类型的区别✅如何理解自动拆装箱&#xff1f;✅拆箱与装箱✅自动拆装箱✅自动拆装箱原理 ✅哪些地方会自动拆装箱&#xff1f;…

10-高并发-应用级缓存

缓存简介 缓存&#xff0c;是让数据更接近于使用者&#xff0c;目的是让访问速度更快。 工作机制是先从缓存中读取数据&#xff0c;如果没有&#xff0c;再从慢速设备上读取实际数据并同步到缓存。 那些经常读取的数据、频繁访问的数据、热点数据、I/O瓶颈数据、计算昂贵的数…

算法基础之约数个数

约数个数 核心思想&#xff1a; 用哈希表存每个质因数的指数 然后套公式 #include <iostream>#include <algorithm>#include <unordered_map>#include <vector>using namespace std;const int N 110 , mod 1e9 7;typedef long long LL; //long l…

移动安全APP--Frida+模拟器,模拟器+burp联动

最近测APP被通报了&#xff0c;问题点测得比较深&#xff0c;涉及到frida和burp抓包&#xff0c;一般在公司可能会有网络的限制&#xff0c;手机没办法抓包&#xff0c;我就直接在模拟器上试了&#xff0c;就在这记录一下安装过程。 目录 一、Frida安装 二、burp与逍遥模拟器…

MySQL是如何保证数据不丢失的?

文章目录 前言Buffer Pool 和 DML 的关系DML操作流程加载数据页更新记录 数据持久化方案合适的时机刷盘双写机制日志先行机制日志刷盘机制Redo Log 恢复数据 总结 前言 上篇文章《InnoDB在SQL查询中的关键功能和优化策略》对InnoDB的查询操作和优化事项进行了说明。但是&#…

STM32F4的DHT11初始化与实例分析

STM32—— DHT11 本文主要涉及STM32F4 的DHT11的使用以及相关时序的介绍&#xff0c;最后有工程下载地址。 文章目录 STM32—— DHT11一、 DHT11的介绍1.1 DHT11的经典电路 二、DHT11的通信2.1 DHT11的传输数据格式2.2 DHT11 通信分步解析 三、 DHT11 代码3.1 引脚图3.2 电路图…