Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

3D模型动画分类及其使用

发表于 2019-12-28

3DMax、Blender之类的3D建模软件易学难精,其原因在于很多人不了解其背后的计算机图形学原理。因此,掌握相关的计算机图形学原理和知识,对于我们熟练运用3D建模软件是十分必要的。下面简单介绍3D模型的分类及其使用方法。

3D模型动画分类

3D模型动画的基本原理是让模型中各顶点的位置随时间变化。 主要种类有Morph(变形)动画,关节动画和骨骼蒙皮动画(SkinnedMesh)。从动画数据的角度来说,三者一般都采用关键帧技术,即只给出关键帧的数据,其他帧的数据使用插值得到。但由于这三种技术的不同,关键帧的数据是不一样的。

变形动画

Morph(渐变,变形)动画是直接指定动画每一帧的顶点位置,其动画关键中存储的是Mesh所有顶点在关键帧对应时刻的位置。

关节动画

关节动画的模型不是一个整体的Mesh,而是分成很多部分(Mesh),通过一个父子层次结构将这些分散的Mesh组织在一起,父Mesh带动其下子Mesh的运动,各Mesh中的顶点坐标定义在自己的坐标系中,这样各个Mesh是作为一个整体参与运动的。

动画帧中设置各子Mesh相对于其父Mesh的变换(主要是旋转,当然也可包括移动和缩放),通过子到父,一级级的变换累加(当然从技术上,如果是矩阵操作是累乘)得到该Mesh在整个动画模型所在的坐标空间中的变换(从本文的视角来说就是世界坐标系了,下同),从而确定每个Mesh在世界坐标系中的位置和方向,然后以Mesh为单位渲染即可。

关节动画的问题是,各部分Mesh中的顶点是固定在其Mesh坐标系中的,这样在两个Mesh结合处就可能产生裂缝。

骨骼蒙皮动画

骨骼蒙皮动画即SkinnedMesh了,骨骼蒙皮动画的出现解决了关节动画的裂缝问题。骨骼动画的基本原理可概括为:在骨骼控制下,通过顶点混合动态计算蒙皮网格的顶点,而骨骼的运动相对于其父骨骼,并由动画关键帧数据驱动。

一个骨骼动画通常包括骨骼层次结构数据,网格(Mesh)数据,网格蒙皮数据(skin info)和骨骼的动画(关键帧)数据。

SkinnedMesh原理

SkinnedMesh中文一般称作骨骼蒙皮动画,正如其名,这种动画中包含骨骼(Bone)和蒙皮(Skinned Mesh)两个部分,Bone的层次结构和关节动画类似,Mesh则和关节动画不同:

关节动画中是使用多个分散的Mesh,而Skinned Mesh中Mesh是一个整体,也就是说只有一个Mesh,实际上如果没有骨骼让Mesh运动变形,Mesh就和静态模型一样了。

Skinned Mesh技术的精华在于蒙皮,所谓的皮并不是模型的贴图(也许会有人这么想过吧),而是Mesh本身,蒙皮是指将Mesh中的顶点附着(绑定)在骨骼之上,而且每个顶点可以被多个骨骼所控制,这样在关节处的顶点由于同时受到父子骨骼的拉扯而改变位置就消除了裂缝。

Skinned Mesh这个词从字面上理解似乎是有皮的模型,哦,如果贴图是皮,那么普通静态模型不也都有吗?所以我觉得应该理解为具有蒙皮信息的Mesh或可当做皮肤用的Mesh,这个皮肤就是Mesh。而为了有皮肤功能,Mesh还需要蒙皮信息,即Skin数据,没有Skin数据就是一个普通的静态Mesh了。

Skin数据决定顶点如何绑定到骨骼上。顶点的Skin数据包括顶点受哪些骨骼影响以及这些骨骼影响该顶点时的权重(weight),另外对于每块骨骼还需要骨骼偏移矩阵(BoneOffsetMatrix)用来将顶点从Mesh空间变换到骨骼空间。

SkinnedMesh结构

  • 骨骼决定了模型整体在世界坐标系中的位置和朝向。

先看看静态模型吧,静态模型没有骨骼,我们在世界坐标系中放置静态模型时,只要指定模型自身坐标系在世界坐标系中的位置和朝向。在骨骼动画中,不是把Mesh直接放到世界坐标系中,Mesh只是作为Skin使用的,是依附于骨骼的,真正决定模型在世界坐标系中的位置和朝向的是骨骼。

在渲染静态模型时,由于模型的顶点都是定义在模型坐标系中的,所以各顶点只要经过模型坐标系到世界坐标系的变换后就可进行渲染。而对于骨骼动画,我们设置模型的位置和朝向,实际是在设置根骨骼的位置和朝向,然后根据骨骼层次结构中父子骨骼之间的变换关系计算出各个骨骼的位置和朝向,然后根据骨骼对Mesh中顶点的绑定计算出顶点在世界坐标系中的坐标,从而对顶点进行渲染。要记住,在骨骼动画中,骨骼才是模型主体,Mesh不过是一层皮,一件衣服。

  • 骨骼可理解为一个坐标空间。

骨骼只是一个形象的说法,实际上骨骼可理解为一个坐标空间,关节可理解为骨骼坐标空间的原点。关节的位置由它在父骨骼坐标空间中的位置描述。上图中有三块骨骼,分别是上臂,前臂和两个手指。Clavicle(锁骨)是一个关节,它是上臂的原点,同样肘关节(elbow joint)是前臂的原点,腕关节(wrist)是手指骨骼的原点。关节既决定了骨骼空间的位置,又是骨骼空间的旋转和缩放中心。

骨骼就是坐标空间,骨骼层次就是嵌套的坐标空间。关节只是描述骨骼的位置即骨骼自己的坐标空间原点在其父空间中的位置,绕关节旋转是指骨骼坐标空间(包括所有子空间)自身的旋转。

但还有两个可能的疑问,一是骨骼的长度问题,由于骨骼是坐标空间,没有所谓的长度和宽度的限制,我们看到的长度一方面是蒙皮后的结果,另一方面子骨骼的原点(也就是关节)的位置往往决定了视觉上父骨骼的长度,比如这里upper arm线段的长度实际是由elbow joint的位置决定的。

第二个问题,手指的那个端点是啥啊?实际上在我们的例子中手指没有子骨骼,所以那个端点并不存在:)那是为了方便演示画上去的。实际问题中总有最下层的骨骼,他们不能决定其他骨骼了,他们的作用只剩下控制Mesh顶点。对了,那么手指的长度如何确定?我们看到的长度应该是由蒙皮决定的,也就是由Mesh中属于手指的那些点离腕关节的距离决定。

3D模型动画使用

下面给出一段在Unity3D中控制3D模型动画的代码,作为参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

using UnityEngine;
using System.Collections;

public class AnimationScript : MonoBehaviour
{
void Start()
{
Animation animation = this.animation;//动画控制器
animation.Play("idle");//上来直接播放idle动画
}
void OnGUI()
{
if (GUI.Button(new Rect(0, 0, 100, 30), "行走"))
{
animation.Play("run");
}
if (GUI.Button(new Rect(100, 0, 100, 30), "停止"))
{
animation.Play("idle");
}
if (GUI.Button(new Rect(200, 0, 100, 30), "攻击"))
{
animation.Play("attack");
animation.PlayQueued("idle");//播放完attack之后再播放idle
}
}
}

参考链接

  1. 骨骼蒙皮动画(SkinnedMesh)的原理解析,by feng.
  2. 【Unity3D】3D模型的使用——FBX的使用与Animation设置,by yongh701.

GitBook入门教程

发表于 2019-12-26 | 更新于 2020-01-28

GitBook是一种制作在线书籍的工具。它基于Git支持多人协作,支持将采用Markdown语法编辑的文档导出成 PDF,EPUB,HTML等多种格式。

GitBook安装

环境要求

  • NodeJS (v4.0.0 and above is recommended)
  • Windows, Linux, Unix, or Mac OS X

NPM安装GitBook

通过NPM工具安装GitBook是最佳的方法:

1
2
$ npm install gitbook-cli -g
$ gitbook init //下载稳定版的gitbook,同时创建在线书籍

gitbook-cli工具可安装多个GitBook版本到系统上。对于Windows平台,gitbook-cli工具安装的多个GitBook版本通常存储在“C:\Users\CurrentLoginUser\.gitbook”。

离线安装GitBook

内网机器上安装GitBook的方法如下:

  • 安装最新Nodejs长期支持版。
  • 使用npm-bundle命令在线打包gitbook-cli
1
2
npm install npm-bundle -g
npm-bundle gitbook-cli
  • 内网机器上安装gitbook-cli
1
npm install ./gitbook-cli.tgz
  • 将“C:\Users\CurrentLoginUser\.gitbook”目录打包拷贝至内网机器对应位置

创建书籍

1
2
3
$ gitbook init    //在当前目录创建书籍
$ gitbook build //构建在线书籍网站
$ gitbook serve //构建在线书籍网站并启动

参考链接

  1. GitBook 从懵逼到入门,by 阿基米东.
  2. 使用 Gitbook 打造你的电子书,by 文艺小青年.
  3. 世上最佳离线markdown编辑工具(gitbook和gitbook editor),by icharm.
  4. 移除GitBook目录下方的“本书使用GitBook发布”字样,by tedxiong.
  5. EbookError: Error during ebook generation: ‘ebook-convert,by 狼爷.
  6. 书籍配置文件(book.json),by wiliam.

glTF2.0格式解析

发表于 2019-12-25 | 更新于 2019-12-27

glTF(GL传输格式的衍生简称)是一种使用JSON标准的3D场景和模型的文件格式。 它是Khronos Group 3D格式工作组开发的一种与API无关的运行时资产交付格式。 它在HTML5DevConf 2016上宣布。此格式旨在成为一种高效,可互操作的格式,具有最小的文件大小和应用程序对运行时的处理。 因此,其创建者将其描述为“3D JPEG”。 glTF还为3D内容工具和服务定义了一种通用的发布格式。本文旨通过对glTF2.0格式的解析,进一步加深对3D建模的理解。

基本概念

在对glTF2.0格式解析之前,应先了解一些3D建模或glTF独有的基本概念:

  • scenes, nodes:场景的基本结构
  • cameras:场景的可视配置
  • meshes:构成3D对象的几何
  • buffers, bufferViews, accessors:数据参考和布局描述
  • materials:定义数据如何被渲染
  • textures, images, samplers:对象表面显示
  • skins:顶点蒙皮信息
  • animations:随时间改变的属性

glTF概念之间的关系

图1 glTF概念之间的关系

参考链接

  1. glTF,by KhronosGroup.
  2. glTF,by wikipedia.
  3. glTF Overview,by KhronosGroup.
  4. 骨骼蒙皮动画(SkinnedMesh)的原理解析,by feng.
  5. 【Unity3D】3D模型的使用——FBX的使用与Animation设置,by yongh701.

jszip使用方法简介

发表于 2019-12-24

当大文件需要在网络中传输时,最好进行压缩传输,然后在终点进行解压。以ZIP压缩为例,压缩后文件大小极具减小,可节约带宽,提高系统并发能力。下面介绍使用jszip在浏览器端的解压方法。

JSZip简介

JSZip是一个用于创建、读取和编辑.zip文件的javascript库,有一个可爱而简单的API。JSZip支持Nodejs和浏览器端的安装使用。具体方法如下:

1
2
3
4
5
6
7
With npm : npm install jszip

With bower : bower install Stuk/jszip

With component : component install Stuk/jszip

Manually : download JSZip and include the file dist/jszip.js or dist/jszip.min.js

浏览器端解压zip文件

后端Nodejs将zip文件以二进制形式存储到数据库中。当前端需要该zip文件时,后端将zip文件以二进制形式传输到前端,前端再解压还原。

Nodejs使用JSZip压缩文件

1
2
3
4
5
6
7
8
9
10
11
12
var JSZip = require("jszip");
var zip = new JSZip();

// create a file
zip.file("hello.txt", "Hello[p my)6cxsw2q");
// oops, cat on keyboard. Fixing !
zip.file("hello.txt", "Hello World\n");

// create a file and a folder
zip.file("nested/hello.txt", "Hello World\n");
// same as
zip.folder("nested").file("hello.txt", "Hello World\n");

浏览器端解压Zip文件

1
2
3
4
5
6
7
8
9
10
import JSZip from 'jszip'

let new_zip = new JSZip();

// Read zip package
new_zip.loadAsync(content)
.then(function(zip) {
// you now have every files contained in the loaded zip
new_zip.file("hello.txt").async("string");
});

参考链接

  1. ZIP格式,by wikipedia.
  2. gzip,bzip2,zip三种格式压缩率对比,by CatDeacon.
  3. JSZip,by stuk.

Python第三方包离线安装

发表于 2019-12-19 | 更新于 2024-01-09

需要在离线的情况下,配置电脑的Python环境,具体方法如下:

  1. 寻找一台与离线电脑架构一致且能上网的干净电脑或虚拟机,在线配置Python环境。
  2. 在线电脑上打包Python环境,拷贝到离线电脑上。
  3. 在离线电脑上安装Python环境。

注意:Python第三方包离线安装可以与python的虚拟环境结合起来,即:

1
python -m venv env

在线配置Python环境

安装Python3之后,使用如下命令安装第三方包:

1
2
# 临时使用清华镜像,加快Python第三方包下载速度
pip3 install -i https://mirrors.aliyun.com/pypi/simple/ matplotlib

打包安装的Python第三方包:

1
2
3
pip3 list #查看安装的包
pip3 freeze >requirements.txt
pip3 download -d C:\Python37\packages -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt

离线配置Python环境

在离线电脑上安装与在线电脑同样版本和架构的Python。注意:3.9版本以上python已不支持Windows7。

将打包好的Python环境拷贝到离线电脑后,使用如下命令安装第三方包:

1
pip install --no-index --find-links=C:\Python37\packages -r requirements.txt

参考链接

  1. pypi 镜像使用帮助,by 清华大学开源软件镜像站.
  2. Python pip离线安装package方法总结(以TensorFlow为例),by 毛帅.
  3. 离线环境安装python第三方库,by 接纳自己.

C++中Cout输出到文件

发表于 2019-12-12

最近在调试一个C++程序时,没有报任何错误就直接退出了,Cout输出到控制台的信息没法查看,无法找到错误原因。于是想到将Cout输出重定向到文件,以便分析错误原因。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <fstream>

using namespace std;

int main(){
streambuf *psbuf,*backup;
ofstream file;
file.open("test.txt");
backup=cout.rdbuf();
psbuf=file.rdbuf();

cout.rdbuf(psbuf);//将cout输出重定向到文件
cout<< "This will write to test.txt!";

cout.rdbuf(backup);//恢复cout输出重定向到终端
file.close();
return 0;
}

参考链接

  1. NULL指针、零指针、野指针,by fly1988happy.
  2. C++ Null 指针,by w3cschool.
  3. How to redirect cin and cout to files?,by stackoverflow.

红外点源目标探测仿真

发表于 2019-12-11

红外点源目标探测仿真过程中遇到两个问题:一是哪些目标重合到一块,二是如何计算重合目标的中心和半径。这两个问题可分别通过计算图连通分量的算法和最小圆覆盖算法解决。

参考链接

  1. 最小圆覆盖(经典算法【三点定圆),by Coco_T_.
  2. 利用邻接矩阵求解无向图的连通分支的个数,by EsonJohn.
  3. 使用向量叉乘判断线段是否相交并求交点,by leto.
  4. 图的那点事儿(1)-无向图,by SylvanasSun.

C++矩阵的存储方法接出存储方法-行主序与列主序

发表于 2019-12-11

最近要设计一个C++矩阵类,涉及到矩阵元素的存储。根据矩阵元素的存储,通常分为行主序和列主序。

  • 行主序是指以行为优先单位,在内存中逐行存储;

  • 列主序是指以列为优先单位,在内存中逐列存储。

行主序与列主序的代码实现有一定的惯例,表现如下:

  • 行主序以二维数组存储,列主序以一维数组存储;
  • 行主序以二维数组方式命名初始化参数,列主序以一维数组方式命名初始化参数;
  • 行主序以行为单位初始化,列主序以列为单位初始化;

参考链接

  1. OpenGL中矩阵的行主序与列主序,by 天律界中子.

GSL插值函数编译链接出错解决方法

发表于 2019-12-11 | 更新于 2022-07-16

在Windows平台使用VS2017社区版将开源数学库GSL编译链接成DLL后,使用官方示例测试其插值函数。在编译链接该示例过程中报

LNK2001: unresolved external symbol _gsl_interp_cspline

的错误。

解决方法如下,在示例开始处定义如下宏即可。

#define GSL_DLL

完整可运行示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#define GSL_DLL

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>

int
main (void)
{
int i;
double xi, yi, x[10], y[10];

printf ("#m=0,S=17\n");

for (i = 0; i < 10; i++)
{
x[i] = i + 0.5 * sin (i);
y[i] = i + cos (i * i);
printf ("%g %g\n", x[i], y[i]);
}

printf ("#m=1,S=0\n");

{
gsl_interp_accel *acc
= gsl_interp_accel_alloc ();
gsl_spline *spline
= gsl_spline_alloc (gsl_interp_cspline, 10);

gsl_spline_init (spline, x, y, 10);

for (xi = x[0]; xi < x[9]; xi += 0.01)
{
yi = gsl_spline_eval (spline, xi, acc);
printf ("%g %g\n", xi, yi);
}
gsl_spline_free (spline);
gsl_interp_accel_free (acc);
}
return 0;
}

参考链接

  1. Interpolation,by gsl homepage.
  2. [Help-gsl] LNK2001: unresolved external symbol _gsl_interp_cspline,by lists.gnu.org.
  3. [Help-gsl] Re: LNK2001: unresolved external symbol _gsl_interp_cspline,by lists.gnu.org.
  4. C++求积分代码,by Firekisser.
  5. matlab代码—插值,by 从小练武功.

企事业单位IT架构的涅槃与重生之中台

发表于 2019-12-08 | 更新于 2019-12-11

近年来,企事业单位IT建设如火如荼,取得了很多的成果,极大地提高了企事业单位的工作效率。但同时企事业IT建设中存在的数据管理问题、业务管理问题不容忽视。在企事业单位IT建设的早期,项目之间没有整体规划,缺乏统一管理,造成数据孤岛、数据标准化缺失、数据存储杂乱、数据使用泛滥等数据管理问题,同时业务方面也存在重复建设、人亡政息、部门分割、多头管理等业务管理问题。中台架构即是针对上述问题,对企事业IT建设的涅槃和重生。

中台由来

在传统企业IT架构中,通常分成前台和后台。前台即包括各种和用户直接交互的界面,比如web页面、手机app;也包括服务端各种实时响应用户请求的业务逻辑,比如商品查询、订单系统等等。而后台并不直接面向用户,而是面向运营人员的配置管理系统,比如商品管理、物流管理、结算管理。后台为前台提供了一些简单的配置。

在企业竞争不够激烈时,这种IT架构能够满足用户需求,虽然易造成数据冗余、数据孤岛、数据杂乱等问题。而今天随着互联网的蓬勃发展,企业竞争越来越激烈,用户也越来越挑剔。只有以用户为中心,快速影响用户的需求,不断迭代和试错,才能让企事业在竞争当中立于不败,才能更加满足用户的需求。于是中台架构应运而生。

中台架构本质上是对传统前后台IT架构的重构,尤其是后台的重构。在传统的IT架构中,通常会遇到如下两类问题:

  • 一类是,许多业务需求或功能需求高度类似、通用化程度很高,但是由于没有专门的团队负责规划和开发,大量的系统重复开发、重复建设,导致复用性低、效率低、产研资源浪费、用户体验不统一。
  • 另一类是,早期业务发展过程中,为了解决一些当下的业务问题,垂直的、个性化的业务逻辑与基础系统耦合太深,由于没有平台性质的规划,横向系统之间、上下游系统之间的交叉逻辑也非常多,这样导致在新业务、新市场的拓展过程中,系统没法直接复用,甚至没法快速迭代。

这两类问题,在软件开发领域,有专门的名称,叫做“重复造轮子”和“烟囱式架构”。中台的诞生即为了避免“重复造轮子”的尴尬和“烟囱式架构”的无序发展。

中台是真正为前台而生的平台(可以是技术平台,业务能力甚至是组织机构),它存在的唯一目的就是更好的服务前台规模化创新,进而更好的响应服务引领用户,使企业真正做到自身能力与用户需求的持续对接。

中台就像是在前台与后台之间添加的⼀组“变速⻮轮”,将前台与后台的速率进行匹配,是前台与后台的桥梁。它为前台而生,易于前台使用,将后台资源顺滑流向用户,响应用户。

中台价值

中台的存在价值是为它的客户服务,比如业务中台和数据中台要快速响应前台应用的需求。中台建设的价值,在于帮助企业搭建更加适应企业数字化转型的全新IT架构。或者说,中台架构本身就是企业全新IT架构的核心内容和骨干系统,让企业IT运营更加顺畅,更能帮助企业尝试新的商业模式,完成战略转型的目的。

中台构建

根据功能和角色的不同,中台可分为:

  • 业务中台:通过各个项目的共通业务进行下沉,整合成通用的服务平台。
  • 技术中台:为了避免研发人员重复发明轮子,向各个项目提供通用的底层框架、引擎、中间件。
  • 数据中台:为各个项目进行各种数据采集和分析。
  • 算法中台:为各个项目提供算法能力,比如推荐算法、搜索算法、图像识别、语音识别等等。

参考链接

  1. 漫画:什么是中台?,by 程序员小灰.
  2. 互联网公司中所谓中台是怎么定义的?,by zhihu.
  3. 白话中台战略:中台是个什么鬼?,by 王健.
  4. 中台战略全解读(一):中台的发展与进化,by 陈新宇等.
上一页1…323334…54下一页

Jack Huang

535 日志
69 标签
© 2026 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse