笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

以前有朋友问我,关于变形动画是如何实现的,实现方式主要有两种,一种是通过美术人员利用Max工具自己调出动画,这种调出的动画太僵硬而且不能根据用户的要求随意变形,只能按照预先调好的动画变形,这种做法可以排除了。第二种做法是通过程序实现自由的动画变换,在这里给读者介绍利用OpenGL实现的变形动画,目的是把动画的变换原理给读者介绍清楚。

在介绍3D变形的之前,读者首先要掌握的是3D的固定流水线, 现在做游戏的开发者对于什么是3D固定流水线一无所知,市面上使用的大部分引擎都封装的非常好,开发者也沦为了只会使用工具,对于一些常识一概不知。在游戏公司做的主要事情就是写写逻辑,调用调用接口,如果需求超出接口的范畴基本上就是一筹莫展,或者说引擎的功能满足不了。面对这种情况,作为开发者必须把3D的基本知识掌握了,这样遇到3D的任何问题都不会感觉难下手,至少知道解决问题的思路。如果读者对于3D固定流水线不清楚可以自己去查阅,我也写过关于3D固定流水线的博客,在这里就不做介绍了,简单的一句话表示3D固定流水线就是将3D模型在2D屏幕上显示的过程,这个过程就是对于矩阵的换算,对应3D固定流水线是3D可编程流水线,不清楚的读者自行查阅一下,这个都必须要掌握的。

接下来介绍实现变形的基本思路:

第一步,准备使用的模型,模型格式很多种,这个可以使用网上的,也可以自己编写插件实现自定义的模型。本项目对应的模型是后缀名为m的自定义的模型格式,模型格式内容如下所示:

上图显示的只是一部分,为了能让读者看清楚,并没有对模型进行加密,为了计算方便,这里只给出了Vertex顶点和Face面的数据。

第二步,定义模型的数据结构体

#ifndef halfedge_h
#define halfedge_h#include <cstdlib>using namespace std;// Halfedge structures
struct HE_edge;
struct HE_vert;
struct HE_face;
struct HE_line;struct HE_edge
{int edgeID;HE_vert* vertS; // Vertex at the start of halfedgeHE_vert* vertE;   // Vertex at the end of halfedgeHE_edge* pair;  // Oppositely oriented halfedgeHE_face* face;   // Incident faceHE_edge* next;  // Next halfedge around the face
};struct HE_vert
{int vertID;            // Vertex IDfloat x, y, z;      // Vertex coordinatesfloat nx, ny, nz;  // Vertex normalsbool selected;     // Selected for FFDHE_edge* edge;       // Halfedge emanating from vertex
};struct HE_face
{int faceID;            // Face IDint v1, v2, v3;       // Vertex IDsfloat nx, ny, nz;  // Face normalsHE_edge* edge;       // Halfedge bordering the face
};struct HE_line
{unsigned int startVert, endVert;bool operator < (const HE_line &other) const{if(startVert < other.startVert)return true;else if(startVert == other.startVert)return endVert < other.endVert;elsereturn false;}HE_line(){};HE_line(const HE_line &vert) {startVert = vert.startVert;endVert = vert.endVert;}HE_line(unsigned int v1, unsigned int v2) {startVert = v1;endVert = v2;}
};#endif

第三步,需要加载模型,并计算顶点和面的法线以及设置包围盒,头文件的内容给读者展示如下:

#ifndef mesh_h
#define mesh_h#include <cstdlib>
#include <GL\glui.h>
//#include <GL\freeglut.h>
#include <map>
#include <Windows.h>
#include <WTypes.h>using namespace std;

// Mesh functions
void loadMesh(std::string path);
void findMinMax(float tempX, float tempY, float tempZ);
void calcFaceNorm(void);
void calcVertNorm(void);
void setToOrigin(void);
void scaleToOrigin(void);// Draw functions
double editLineWidth(int width);
void drawMeshWire(void);
void drawMeshSolid(void);
void drawMeshPoint(void);
void drawBbox(void);
void drawGrid(void);
void drawAxes(void);// Dialog functions
void createFileDialog(void);
int createMsgbox(int msgboxID);
enum
{ERR_no_mesh = 0,ERR_no_pt
};#endif

具体实现会在博客的末尾给读者展示。

第四步,开始对模型的变形处理,在这里并没有使用什么高深的算法,其实就是对它们的顶点进行矩阵变换。首先进行的是世界变换,投影变换,最后是视口变换。

变形的函数如下所示:

void deformMesh()
{copyVertices = new Vertex[numOfVert]();for(int count = 0; count < numOfVert; count++) {/* Create copy of vertex position */copyVertices[count].x = v0[count].x;copyVertices[count].y = v0[count].y;copyVertices[count].z = v0[count].z;/* Recalculate new minimum/maximum vertices */if(vertices[count].selected) {findSelectedMinMax(vertices[count].x, vertices[count].y, vertices[count].z);}}for(int count = 0; count < numOfVert; count++) {/* Only calculate new position of vertex if selected */if(vertices[count].selected) {/* Highlights selected vertex */glColor3f(0.5, 0.5, 1);glPushMatrix();glTranslatef(copyVertices[count].x, copyVertices[count].y, copyVertices[count].z);glutSolidSphere(0.1, 20, 20);glPopMatrix();glColor3f(1, 1, 1);vertices[count].x = 0;vertices[count].y = 0;//vertices[count].z = 0;/* Calculate the FFD position and update vertex position */Vertex tempResult = trivariateBernstein(copyVertices[count]);vertices[count].x = tempResult.x;vertices[count].y = tempResult.y;vertices[count].z = tempResult.z;//cout << "Vertex (old): " << vertices[count].x << ", " << vertices[count].y << ", " << vertices[count].z << "\n";//cout << "Vertex (new): " << vertices[count].x << ", " << vertices[count].y << ", " << vertices[count].z << "\n";}}
}

当你看到代码后,第一印象就是太简单了,确实是这样,变形其实就是将需要改变的顶点进行一系列矩阵变换,在这里使用了OpenGL的库函数接口

         glPushMatrix();glTranslatef(copyVertices[count].x, copyVertices[count].y, copyVertices[count].z);glutSolidSphere(0.1, 20, 20);glPopMatrix();

进行顶点的移动,同时调用了函数

trivariateBernstein

进行顶点的变换以及权值计算,代码如下所示:

Vertex trivariateBernstein(Vertex vert)
{Vertex stuVert;stuVert.x = 0;stuVert.y = 0;stuVert.z = 0;stuVert = convertToSTU(vert);/*cout << "Vertex (XYZ): " << vert.x << ", " << vert.y << ", " << vert.z << "\n";cout << "Vertex (STU): " << stuVert.x << ", " << stuVert.y << ", " << stuVert.z << "\n";*/double weight = 0.0;Vertex convertedVert;convertedVert.x = 0;convertedVert.y = 0;convertedVert.z = 0;/* Performing summations using for loops */for(int i = 0; i <= l; i++) {for(int j = 0; j <= m; j++) {for(int k = 0; k <= n; k++) {weight = 0;weight = bernsteinPoly(n, k, stuVert.z) * bernsteinPoly(m, j, stuVert.y) * bernsteinPoly(l, i, stuVert.x);convertedVert.x += weight * lattice[i][j][k].x;convertedVert.y += weight * lattice[i][j][k].y;convertedVert.z += weight * lattice[i][j][k].z;}}}return convertedVert;
}

没有使用任何算法,实现的就是模型的矩阵变换。

最后一步就是实现,首先要做的事情是选择需要变形的顶点或者区域,使用了函数如下所示:

void moveSelectedCP(int endX, int endY)
{/* Initialise movement variables */float moveX = 0;float moveY = 0;moveX = (endX-startX);moveY = (endY-startY);/* Set end point to start point to find next round of moved distance */startX = endX;startY = endY;if(!(moveX == 0) || !(moveY == 0)) {//lattice2d[sel_i][sel_j][sel_k].x = endX;//lattice2d[sel_i][sel_j][sel_k].y = endY;lattice2d[sel_i][sel_j][sel_k].x += moveX;lattice2d[sel_i][sel_j][sel_k].y += moveY;/* Convert to 3D to change position of 3D CP */gluUnProject(lattice2d[sel_i][sel_j][sel_k].x, lattice2d[sel_i][sel_j][sel_k].y, lattice2d[sel_i][sel_j][sel_k].z,modelView, projection, viewport,&lattice[sel_i][sel_j][sel_k].x, &lattice[sel_i][sel_j][sel_k].y, &lattice[sel_i][sel_j][sel_k].z);cout << "Move CP: [" << sel_i << "][" << sel_j << "][" << sel_k << "]\n";//drawLattice();//deformMesh();}
}

在再main函数中调用封装的函数即可。其实做起来还是比较简单的,就是对变形的部分做了矩阵的运算。大家关注上面加粗的部分。

代码下载地址:链接:http://pan.baidu.com/s/1i5klXXV 密码:m1f5

OpenGL实现3D自由变形相关推荐

  1. 3D Dirichlet Free-Form Deformation(三维Dirichlet自由变形)

    自由变形(Free-Form Deformation, 简称FFD)方法是常用的一种与物体表示无关的变形方法,被广泛地应用于计算机动画和几何建模领域中.FFD方法不对物体直接变形,而是对物体所嵌入的空 ...

  2. 基于HTML5+CSS+JavaScript+ Three.js实现的Free-Form-Deformation(FFD三维自由变形)

    资源下载地址:https://download.csdn.net/download/sheziqiong/85610855 一.实验目的和要求 掌握 FFD 变形算法的思想与实现原理. 能够实现交互控 ...

  3. ai怎么让图片任意变形_Illustrator自由变形工具 AI里面把一些图形拼合到一起 快速定义渐变颜色 输出文件...

    Illustrator自由变形工具 AI里面把一些图形拼合到一起 快速定义渐变颜色 输出文件 请教Illustrator/AI中自由变形工具的问题 在ai中如何用自由变形工具把一个矩形变成一个等腰梯形 ...

  4. C++ Opengl绘制3D源码

    C++ Opengl绘制3D源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8.1,三方库 ...

  5. 移动端利用OpenGL展示3D模型文件STL

    移动端利用OpenGL展示3D模型文件STL 突然发现上次写博客都是一年前了,没养成分享的习惯挺郁闷的,所以分享下个人感觉好玩的东西吧.纯理工科生笔杆子不硬,写的不好,哪里有看不懂的或者写的不好的希望 ...

  6. OpenGL与3D图形世界

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  7. Photoshop“自由变形”工具(转)

    Photoshop"自由变形"工具(转)[@more@] 在Photoshop(以下简称PS)中,我们常常碰到很多对象变形的需求,"自由变换"就是完成这一功能的 ...

  8. FFD(Free-Form Deformation)自由变形

    FFD(Free-Form Deformation)自由变形 图1 一.简介 FFD首先是由Brigham Young University的Sederberg和Parry提出来.FFD的变形操作不是 ...

  9. Spine之八——网格和自由变形(FFD)

    Spine 自由变形(FFD)        实现乳摇 1.  切到装配模式. 2.  选中图片. 3.  勾上 Mesh.勾上后就可以编辑网格点了. 1.  点 Edit Mesh. 2.  创建网 ...

最新文章

  1. webview加载本地html_安卓webview html5 自动播放本地视频,网上视频,可以循环播放...
  2. sqlserver 根据数组排序_看动画学算法之:排序-count排序
  3. linux-压缩和解压类
  4. Python实现HTTP服务器(三)线程、进程、协程实现多任务
  5. web加载本地html,WKWebview加载本地html问题汇总
  6. iOS开发证件要点详解
  7. 【C语言】Simple Sorting(结构)
  8. 从还珠格格到街头霸王!80后的怀旧神器 三星GalaxyFold另类体验
  9. 20200208(补):翻转游戏Ⅰ Ⅱ(leetcode)
  10. (转)如何在Windows下使用OpenGL 2.0的API(包括GLSL)
  11. C++读xml文件, C#解析对应的文件
  12. 清华大学李雅哲计算机,研究生教育动态 - 清华大学.pdf
  13. 经典合成器插件 – LennarDigital Sylenth1 2.2.1 WiN-MAC
  14. 2020软考数据库系统工程师-下午案例分析真题解析视频-任铄-专题视频课程
  15. Google SketchUp Cookbook: (Chapter 5) Roofs: Constraints and Inferences
  16. love2d教程28--血条
  17. 算法题解01——对分搜索求立方根
  18. 【C语言】函数的理解——关于函数的声明
  19. 全球及中国智能家居市场十四五竞争形势及营销模式咨询报告2021-2027年
  20. 常见的动画效果(二)

热门文章

  1. #ROM类(EPROM、PROM、EEPROM、MROM、Flash)汇总
  2. 非线性方程求根——牛顿迭代法
  3. win10/win11 控制面板-网络和 Internet-网络连接“本地连接2“老是自动开启,影响wifi网速
  4. java异常类中属于非检测异常的是_下列java语言的常用异常类中,属于检测异常的是()_学小易找答案...
  5. 传说中让理科生沉默,让文科生落泪的文史综合题
  6. tuned-adm性能优化
  7. Java Maven项目文件红叉叉(The compiler compliance specified is 1.6 but a JRE 1.8 is used) java编译报错
  8. 产品经理懂点技术之:常见的网络传输方式
  9. OIM API Usage
  10. 如何树立正确的人生观、价值观、世界观?