GPUInstance合并人物烘培顶点动画

2023-11-07

将4个小人合成一个,使用gpu Instance批量绘制可以使用gpu Instance的特性降低批处理,但是gpu Instance只支持mesh filter所有需要将蒙皮动画烘培成顶点动画,通过MaterialPropertyBlock给shader传值区分显示的人物。

烘培人物和动画代码

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

[CustomEditor(typeof(BakingAnimMesh))]
public class BakingAnimMeshEditor : Editor
{
    public override void OnInspectorGUI()
    {
        DrawDefaultInspector();

        BakingAnimMesh myScript = (BakingAnimMesh)target;

        if (GUILayout.Button("提取"))
        {
            myScript.SaveAsset();
        }
    }
}

public class BakingAnimMesh : MonoBehaviour
{
    public AnimationClip clip;//指定要烘培的动画
    public Animator[] animator;//有动画组件的人物
    public SkinnedMeshRenderer[] sk;//动画人物的蒙皮网格渲染器
    int pnum;//总像素点
    Texture2D texture;
    int size = 0;//图片的宽高
    int vertexCount = 0;//总顶点数
    int frameCount;//总帧数
    public string path = "mini";//导出名称

    //导出动画图片,合并后的网格,合并后的贴图
    public void SaveAsset()
    {
        //总帧数 = 动画时长 * 帧率
        frameCount = Mathf.CeilToInt(clip.length * 30);
        Debug.Log("frameCount:" + frameCount);
        //顶点个数
        int[] vertexNum = new int[sk.Length + 1];
        vertexNum[0] = 0;
        for (int i = 0; i < sk.Length; i++)
        {
            vertexCount += sk[i].sharedMesh.vertexCount;
            vertexNum[i + 1] = vertexCount;
        }
        Debug.Log("vertexCount:" + vertexCount);
        //总像素点 = 总顶点数 * 总帧数
        pnum = vertexCount * frameCount;
        Debug.Log("pnum:" + pnum);
        //将像素点数转换成2的倍数的贴图宽高
        size = Mathf.NextPowerOfTwo(Mathf.CeilToInt(Mathf.Sqrt(pnum)));
        Debug.Log("size:" + size);
        texture = new Texture2D(size, size, TextureFormat.RGBAFloat, false, true);
        //计算顶点的最大最小值
        _CalculateVertexMinAndMax(out float min, out float max);
        Debug.Log("min:" + min);
        Debug.Log("max:" + max);
        //烘培图片
        _BakeAnimationClip(max, min);
        //合并导出网格
        Mesh mesh = new Mesh();
        //合并网格贴图
        List<Texture2D> textures = new List<Texture2D>();
        for (int i = 0; i < sk.Length; i++)
        {
            textures.Add(sk[i].sharedMaterial.mainTexture as Texture2D);
        }
        Texture2D meshtexture = new Texture2D(2048, 2048, TextureFormat.RGBAFloat, false, true);
        Rect[] rects = meshtexture.PackTextures(textures.ToArray(), 0);
        meshtexture.Apply();
        //合并网格
        List<CombineInstance> combines = new List<CombineInstance>();
        List<Vector2[]> olduvs = new List<Vector2[]>();
        for (int i = 0; i < sk.Length; i++)
        {
            CombineInstance combine = new CombineInstance();
            combine.mesh = sk[i].sharedMesh;
            combine.transform = sk[i].transform.localToWorldMatrix;

            Vector2[] olduv = sk[i].sharedMesh.uv;
            olduvs.Add(olduv);
            Vector2[] newuv = new Vector2[olduv.Length];
            for (int j = 0; j < olduv.Length; j++)
            {
                newuv[j] = new Vector2(rects[i].x + rects[i].width * olduv[j].x, rects[i].y + rects[i].height * olduv[j].y);
            }
            combine.mesh.uv = newuv;
            combines.Add(combine);
        }
        mesh.CombineMeshes(combines.ToArray(), true, true);
        for (int i = 0; i < sk.Length; i++)
        {
            sk[i].sharedMesh.uv = olduvs[i];
        }
        //导出网格
        AssetDatabase.CreateAsset(mesh, "Assets/" + path + ".asset");
        //导出贴图
        File.WriteAllBytes(Application.dataPath + "/" + path + ".png", meshtexture.EncodeToPNG());
    }

    public  void _BakeAnimationClip(float max, float min)
    {
        var mesh = new Mesh();
        //计算差值
        float vertexDiff = max - min;
        //通过差值返回0到1的一个数(简易函数,传入float返回float)
        Func<float, float> cal = (v) => (v - min) / vertexDiff;
        //顶点计数
        int currentPixelIndex = 0;
        //循环动画的所有帧
        for (int i = 0; i < frameCount; i++)
        {
            //计算每帧的时间
            float t = i * 1f / 30;
            for (int k = 0; k < animator.Length; k++)
            {
                //播放指定时间的动画
                clip.SampleAnimation(animator[k].gameObject, t);
                mesh.Clear(false);
                sk[k].BakeMesh(mesh);

                var vertices = mesh.vertices;
                //Debug.Log("vertices:" + vertices.Length);
                //循环网格的所有顶点
                for (int v = 0; v < vertices.Length; v++)
                {
                    var vertex = vertices[v];
                    //把顶点坐标转换成0到1的颜色值
                    Color c = new Color(cal(vertex.x), cal(vertex.y), cal(vertex.z));
                    //一维转二维
                    //通过顶点id计算该颜色在图片中的位置
                    int x = currentPixelIndex % size;
                    int y = currentPixelIndex / size;
                    //把颜色写入图片
                    texture.SetPixel(x, y, c);
                    currentPixelIndex++;
                }
            }
        }
        //保存图片
        texture.Apply();
        Debug.Log("currentPixelIndex:" + currentPixelIndex);
        File.WriteAllBytes(Application.dataPath + "/" + path + "Anim.png", texture.EncodeToPNG());
    }

    public void _CalculateVertexMinAndMax(out float vertexMin, out float vertexMax)
    {
        //默认为float的最大最小值
        float min = float.MaxValue;
        float max = float.MinValue;
        Mesh preCalMesh = new Mesh();
        for (int f = 0; f < frameCount; f++)
        {
            float t = f * 1f / 30;
            for (int k = 0; k < animator.Length; k++)
            {
                //播放指定时间的动画
                clip.SampleAnimation(animator[k].gameObject, t);
                //取出当前人物蒙皮的网格
                sk[k].BakeMesh(preCalMesh);
                //循环网格的所有顶点
                for (int v = 0; v < preCalMesh.vertexCount; v++)
                {
                    var vertex = preCalMesh.vertices[v];
                    //取x,y,z的最小值
                    //min = Mathf.Floor(Mathf.Min(min, vertex.x, vertex.y, vertex.z));
                    min = Mathf.Min(min, vertex.x, vertex.y, vertex.z);
                    //取x,y,z的最大值
                    //max = Mathf.Ceil(Mathf.Max(max, vertex.x, vertex.y, vertex.z));
                    max = Mathf.Max(max, vertex.x, vertex.y, vertex.z);
                }
            }
        }

        vertexMin = min;
        vertexMax = max;
    }
}

显示动画的shader

Shader "Unlit/MyGPUInstance"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _FaceTex("Face", 2D) = "white" {}
        _AnimTex("Anim", 2D) = "white" {}
        _Fmax("fmax",Float) = 87
        _VCount("vCount",Float) = 3466
        _Size("size",Float) = 1024
        _VertexMax("VertexMax",Float) = 2
        _VertexMin("VertexMin",Float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            //第一步: sharder 增加变体使用shader可以支持instance  
            #pragma multi_compile_instancing
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            #include "UnityCG.cginc"

            UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(int, _VMax0)//顶点最大数
                UNITY_DEFINE_INSTANCED_PROP(int, _VMin0)//顶点最小数
                UNITY_DEFINE_INSTANCED_PROP(float4, _FaceVec)//脸的贴图坐标
            UNITY_INSTANCING_BUFFER_END(Props)

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                //返回顶点id
                uint vid : SV_VertexID;
                //第二步:instancID 加入顶点着色器输入结构 
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float4 faceVec : VECTOR;
                int clip : INT;
                //第三步:instancID 加入顶点着色器输出结构 
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            sampler2D _MainTex;
            sampler2D _FaceTex;
            sampler2D _AnimTex;
            uint _Fmax;
            uint _VCount;
            float _Size;
            float _VertexMax;
            float _VertexMin;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                //第四步:instanceid在顶点的相关设置  
                UNITY_SETUP_INSTANCE_ID(v);
                //第五步:传递 instanceid 顶点到片元
                UNITY_TRANSFER_INSTANCE_ID(v, o);

                int vmax = UNITY_ACCESS_INSTANCED_PROP(Props, _VMax0);
                int vmin = UNITY_ACCESS_INSTANCED_PROP(Props, _VMin0);
                o.faceVec = UNITY_ACCESS_INSTANCED_PROP(Props, _FaceVec);
                //当前帧数
                uint f = fmod(ceil(_Time.y * 30), _Fmax);
                if (v.vid < vmax && v.vid > vmin)
                {
                    //当前顶点
                    uint index = v.vid + f * _VCount;
                    //计算当前顶点在图片中的xy坐标
                    uint x = index % (uint)_Size;
                    uint y = index / _Size;
                    //把xy坐标转换为0到1的uv坐标
                    float uvx = x / _Size;
                    float uvy = y / _Size;
                    //获取图片中uv坐标的颜色,赋值给顶点坐标
                    v.vertex = tex2Dlod(_AnimTex, float4(uvx, uvy, 0, 0)) * (_VertexMax - _VertexMin) + _VertexMin;
                    o.clip = 1;
                }
                else {
                    o.clip = -1;
                }

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            { 
                //第六步:instanceid在片元的相关设置
                UNITY_SETUP_INSTANCE_ID(i);
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                fixed4 face = tex2D(_FaceTex, float2(i.uv.x * i.faceVec.x + i.faceVec.z,i.uv.y * i.faceVec.y + i.faceVec.w));
                col = lerp(col, face, face.a);
                clip(i.clip);
                //得到由CPU设置的颜色
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

多次创建代码

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

public class CreatCube : MonoBehaviour
{
    public GameObject cube;
    public int n = 10;
    // Start is called before the first frame update
    void Start()
    {
        Init();
    }
    public void Init()
    {
        //创建预制体
        for (int x = -n; x < n; x++)
        {
            for (int z = -n; z < n; z++)
            {
                if (Random.Range(0,10) < 1)
                {
                    GameObject c = Instantiate(cube, transform);
                    c.transform.position = new Vector3(x, 0, z);
                    MaterialPropertyBlock props = new MaterialPropertyBlock();
                    switch (Random.Range(0, 4))
                    {
                        case 1:
                            props.SetInt("_VMax0", 6859);
                            props.SetInt("_VMin0", 3466);
                            props.SetVector("_FaceVec", new Vector4(8, 8, -4.2f, 0));
                            c.transform.eulerAngles = new Vector3(0, Random.Range(0, 360), 0);
                            break;
                        case 2:
                            props.SetInt("_VMax0", 6859 + 3564);
                            props.SetInt("_VMin0", 6859);
                            props.SetVector("_FaceVec", new Vector4(8, 8, -0.2f, -4));
                            c.transform.eulerAngles = new Vector3(-90, Random.Range(0, 360), 0);
                            break;
                        case 3:
                            props.SetInt("_VMax0", 13581);
                            props.SetInt("_VMin0", 6859 + 3564);
                            props.SetVector("_FaceVec", new Vector4(8, 8, -4.2f, -4));
                            c.transform.eulerAngles = new Vector3(-90, Random.Range(0, 360), 0);
                            break;
                        default:
                            props.SetInt("_VMax0", 3466);
                            props.SetInt("_VMin0", 0);
                            props.SetVector("_FaceVec", new Vector4(8, 8, -0.2f, 0));
                            c.transform.eulerAngles = new Vector3(0, Random.Range(0, 360), 0);
                            break;
                    }
                    c.GetComponent<MeshRenderer>().SetPropertyBlock(props);
                }
            }
        }
    }
}

代码中的部分数据只适用我的模型

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GPUInstance合并人物烘培顶点动画 的相关文章

  • Unity用Vuforia做AR实现脱卡效果

    有时在识别目标丢失后我们仍希望虚拟物体能够出现在摄像机前 或者到一个特定的位置 我们能对其进行操作 这就是脱卡功能 自带的脱卡功能应该是ExtendedTracking 允许模型在识别图丢失的时候还存在 位置不变 在丢失的时候的位置 这样也
  • 【Unity Shader】屏幕后处理1.0:调整亮度/饱和度/对比度

    1 Unity中实现屏幕特效的基本步骤 什么叫屏幕后处理 Screen post processing effects 渲染完整个场景得到屏幕图像后对图像进行一系列操作 实现各种屏幕特效 这一步我们可以添加很多例如景深 Depth of F
  • unity制作一个可以自由滑动收缩的历史记录功能。

    公司在做一款模拟经营类的卖车游戏 需要一个简单的历史记录功能 放在左上角 记录最近20条的收入 支出记录 超过2秒不动则收起 收起时展示最近的一个消息记录 用到的组件是ScrollView 使用方法可以参考我写过的一篇博客 ScrollVi
  • Unity中级客户端开发工程师的进阶之路

    上期UWA技能成长系统之 Unity高级客户端开发工程师的进阶之路 得到了很多Unity开发者的肯定 通过系统的学习 可以掌握游戏性能瓶颈定位的方法和常见的CPU GPU 内存相关的性能优化方法 UWA技能成长系统是UWA根据学员的职业发展
  • Unity-AR 简介

    Unity AR 简介 现有Unity AR Sdk ARKit 苹果推出的AR开发平台 ARCore Google 推出的增强现实 SDK ARFoundation ARFoundation是ARKit XR插件和ARCore XR插件
  • FBX导入Unity中模型没有材质的处理

    一 3dMax导出FBX时的注意事项 导出时 确保maps文件存在 里面放着fbx用到的image 二 在Unity中的设置 1 文件拖入Unity的Assets文件夹中 2 查看模型的材质是否存在 如下所示 材质为None 此时拖入sce
  • Unity动画控制器animator.CrossFade

    需要特别注意 1 CrossFade虽然可以不用任何逻辑来链接而直接跳转 但是CrossFade只能覆盖其他动画 当当前动画播放完毕而没有跳出这个动画时再次调用CrossFade将会失败 造成动画依旧停在原位 参数animator Cros
  • Unity Shader入门精要第七章 基础纹理之遮罩纹理

    Unity系列文章目录 文章目录 Unity系列文章目录 前言 一 实践 参考 前言 遮罩纹理 mask texture 是本章要介绍的最后一种纹理 它非常有用 在很多商业游戏中 都可以见到它的身影 那么什么是遮罩呢 简单来讲 遮罩允许我们
  • unity中创建询问弹出窗口

    在开发过程中进程会遇到需要弹出一个窗口询问用户是否进行的操作 今天就来制作一个这样弹出窗口 然后根据弹出窗口的选择内容不同进行不同的操作 本例中主要是为了删除一个数据 而在删除数据操作前需要得到用户的一个确认操作 这里面主要用到了Notif
  • VLC for unity 插件如何使用

    VLC for unity 插件如何使用 先去下载一个VLC播放器 安装完成后 然后导入插件链接https download csdn net my 这个插件我的另一个上传资源里有 或者到商店去下载 这个插件链接下载完是一个txt文档 里面
  • unity 性能查看工具Profiler

    文章目录 前言 profiler工具介绍 菜单栏 帧视图 模块视图 模块详细信息 通过profiler分析优化游戏性能 最后 前言 每次进行游戏优化的时候都用这个工具查看内存泄漏啊 代码优化啊之类的东西 真的好用 但是之前也就是自己摸索一下
  • Unity万向节死锁解决方案(2023/12/4)

    1 万向节死锁无法解决 这是因为它的特性就是如此 就像玻璃杯就是玻璃 这不可否认 别钻牛角尖昂 2 大多数情况下欧拉角足够用 例如 CF 摄像机不可能绕z轴旋转 x轴旋转也不会超过九十度 因为那样人物的腰子会被扭断 塔防游戏 保卫萝卜 吃鸡
  • 【原神游戏开发日志1】缘起

    原神游戏开发日志1 缘起 版权声明 本文为 优梦创客 原创文章 您可以自由转载 但必须加入完整的版权声明 文章内容不得删减 修改 演绎 相关学习资源见文末 大家好 最近看到原神在TGA上频频获奖 作为一个14年经验的游戏开发行业的老兵 我就
  • 【Unity】如何让Unity程序一打开就运行命令行命令

    背景 Unity程序有时依赖于某些服务去实现一些功能 此时可能需要类似打开程序就自动运行Windows命令行命令的功能 方法 using UnityEngine using System Diagnostics using System T
  • 【Unity】运行时创建曲线(贝塞尔的运用)

    Unity 运行时创建线 贝塞尔的运用 1 实现的目标 在运行状态下创建一条可以使用贝塞尔方法实时编辑的网格曲线 2 原理介绍 2 1 曲线的创建 unity建立网格曲线可以参考 Unity程序化网格体 的实现方法 主要分为顶点 三角面 U
  • Unity学习笔记

    一 旋转欧拉角 四元数 Vector3 rotate new Vector3 0 30 0 Quaternion quaternion Quaternion identity quaternion Quaternion Euler rota
  • Unity中URP下的指数雾

    文章目录 前言 一 指数雾 雾效因子 1 FOG EXP 2 FOG EXP2 二 MixFog 1 ComputeFogIntensity 雾效强度计算 2 lerp fogColor fragColor fogIntensity 雾效颜
  • 游戏开发常见操作梳理之NPC任务系统

    多数游戏存在任务系统 接下来介绍通过NPC触发任务的游戏制作代码 using System Collections using System Collections Generic using UnityEngine
  • 游戏开发常见操作梳理系列之——玩家信息的显示系统

    在游戏中 有不少游戏在左上角会出现玩家的头像和等级以及血量 这就是玩家的信息显示系统 那么这些是如何制作的呢 接下来我将讲讲代码的操作 其它操作我会在其它笔记中一一说明 敬请期待 信息的显示相当简单就是控制一些UI 然后在其它系统里面填写相
  • 游戏开发常见操作系列之敌人系统的开发一(U3D)

    在开发游戏的过程中 我们常常会出现一些敌人攻击我们玩家 并且实现掉血以及死亡的现象 敌人还会源源不断地生成 这是怎么制作的呢 接下来为大家提供方法 其中使用了NGUI 后续会更新其它方法 敬请期待 使用HUDText实现扣血时显示文本 直接

随机推荐

  • 理解Vue插槽

    引言 在Vue开发中 我们多采用组件化开发 组件化开发最大特点就是对页面结构化划分 组件的复用 而在实际中 页面对组件的需求或许也稍有不同 那么就需要一种需求 为页面定制组件 解决的方式便是通过插槽 实例
  • Java动态执行计算表达式利器 -- ScriptEngine

    在通过配置文件对系统进行参数配置时 有时需要更好的理解参数值的具体意义 往往采用计算表达式的方式设置 例如1天换成秒数为86400 如果写成24 60 60就很清晰的表达是一天的秒数 但是这个表达式通过properties的方式获取为字符串
  • Celery ValueError: not enough values to unpack (expected 3, got 0)

    目录 1 Celery ValueError not enough values to unpack expected 3 got 0 2 AttributeError async 1 Celery ValueError not enoug
  • 使用CUDA实现零拷贝技术

    使用CUDA实现零拷贝技术 零拷贝技术是指在内存和设备之间传输数据时 不需要显式地将数据从内存复制到设备的过程 从而提高数据传输效率 本文将介绍如何使用CUDA实现零拷贝技术 并提供示例代码 在使用CUDA进行图像处理时 通常需要将数据从主
  • 【计算机视觉

    文章目录 一 SqueezeNet 二 Inception v3 三 Visual Geometry Group 19 Layer CNN 四 MobileNetV1 五 Data efficient Image Transformer 六
  • 【CentOS7】开机自启动三种方法

    有个需求 比如说我想要执行开机杀毒程序 就需要去做开机自启动相关操作 准备工作 在 usr local目录下建立killviruses sh 前提 安装病毒库 vi killviruses sh 键入以下内容 前提 已安装ClamAV cl
  • js纯ajax,纯js 的Ajax 请求

    纯js 的Ajax 请求 2018 02 24 126 var XMLHttpReq function createXMLHttpRequest try XMLHttpReq new ActiveXObject Msxml2 XMLHTTP
  • 深度学习优化算法大全系列3:NAG(Nesterov Acceleration Gradient)

    1 NAG与SGD M的区别 NAG全称为Nesterov Accelerated Gradient 是在SGD Momentum基础进一步优化所得 前面的文章我们提到过 SGD M主要是利用历史累积动量来代替当前梯度从而达到减小震荡 加速
  • python自定义assert抛出的异常

    方法一 常用于assert失败后的提示用语 assert 1 gt 4 what 异常为 AssertionError what 方法二 常用于assert失败后推断导致的报错 try assert 1 gt 4 except Except
  • 前端实现导出Excel

    一 准备文件 1 创建excel文件夹 excel Blob js Export2Excel js 2 Blob js文件夹内容 eslint disable Blob js global self unescape jslint bitw
  • python pygame 游戏实战:Maze 迷宫生成,显示和游戏(附全部代码)

    生成迷宫 maze 的算法有很多种 论坛上有很多这方面的资料可以参考 这里使用回溯法 backtracking 主要参考Build a 2 player maze game with Python Part 4 Coding TidBits
  • 深入理解nandflash之基本特性

    nandflash作为嵌入式中的 磁盘 被广泛的应用 以 K9F2G08U0B 为例 其他型号都差不多 nandflash的结构 nandflash的结构有页 page block 块 的概念 其中页是真实概念 而块儿是虚拟概念 目的是为了
  • Graphviz 可视化图形软件(python)

    目录 1 简介 2 Graphviz 工具安装 3 检查是否安装成功 4 Graphviz 库安装 5 验证安装的graphviz是否可用 6 绘制红酒数据集得到的决策树 7 问题 pycharm正常画决策树 但jupyter显示 Modu
  • 在线java编译器_五个免费在线Java编译器,轻松编译代码

    原标题 五个免费在线Java编译器 轻松编译代码 Java编译器网络版成为有用的在许多情况下 例如 假设你正在编写一个Java代码 但不在自己的计算机上 减少时间的浪费 可以无需下载和安装任何软件 使用免费的在线工具运行代码 也就很有帮助
  • 爬虫日常-cookies登录12306

    文章目录 前言 页面分析 代码设计 前言 hello兄弟们 今天没事干也不晓得更什么内容 就把上次和大家说的可以采用cookies登录12306的方法告诉大家 这个功能熟练了的话还是比较简单的 毕竟可以直接通过pickle 建议大家可以自行
  • 简单年龄换算

    function calculatePetAge birthday var userBirthday new Date birthday var now new Date var petAgeNew now getTime userBirt
  • 机器学习算法代码

    BPNN import math import random random seed 0 def rand a b return b a random random a def make matrix m n fill 0 0 mat fo
  • python将列表数据写入已有的excel文件的指定单元格

    本人结合网上资料 总结出以下两种可以将列表数据写入已有excel并不改变原文件其它内容的方法 第一种方法要用到xlwt xlutils xlrd三个第三方库 import xlwt as xw from xlutils copy impor
  • R语言数据挖掘开源工具rattle的安装

    1 R语言的环境配置 去R语言官网下载最新的R语言环境 2 安装Rstudio 去Rstudio官网下载最新的Rstudio版本安装 3 打开Rstudio安装在rattle install packages RGtk2 install p
  • GPUInstance合并人物烘培顶点动画

    将4个小人合成一个 使用gpu Instance批量绘制可以使用gpu Instance的特性降低批处理 但是gpu Instance只支持mesh filter所有需要将蒙皮动画烘培成顶点动画 通过MaterialPropertyBloc