游戏人生
首页
(current)
GameDevTools
登陆
|
注册
个人中心
注销
游戏引擎 浅入浅出
Introduction
Introduction
前言
前言
1. 游戏引擎框架介绍
1. 游戏引擎框架介绍
1.1 Unity的组成
1.2 游戏引擎组成
2. Opengl开发环境搭建
2. Opengl开发环境搭建
2.1 Opengl到底是什么
2.2 搭建Opengl开发环境
2.3 使用VisualStudio开发
3. 绘制多边形
3. 绘制多边形
3.1 画个三角形
3.2 画个正方形
3.3 画个立方体
4. 着色器
4. 着色器
4.1 Unity Shader和OpenGL Shader
4.2 顶点着色器
4.3 片段着色器
5. 绘制贴图
5. 绘制贴图
5.1 颜色和贴图
5.2 贴图文件介绍
5.3 CPU与GPU的通信方式
5.4 使用stb_image解析图片
5.5 绘制带贴图的立方体盒子
5.6 压缩纹理
5.7 图片压缩工具
5.8 使用压缩纹理
5.9 DXT压缩纹理扩展
6. 索引与缓冲区对象
6. 索引与缓冲区对象
6.1 顶点索引
6.2 缓冲区对象
6.3 OpenGL Core Profile
6.4 顶点数组对象
7. 绘制Mesh和材质
7. 绘制Mesh和材质
7.1 导出Mesh文件
7.2 使用Mesh文件
7.3 Shader文件创建与使用
7.4 创建材质
7.5 使用材质
7.6 MeshRenderer
8. 绘制静态模型
8. 绘制静态模型
8.1 Blender安装与配置
8.2 Blender制作模型
8.3 Blender Python设置开发环境
8.4 Blender Python创建物体
8.5 Blender Python导出顶点数据
8.6 加载导出的Mesh
9. 基于组件开发
9. 基于组件开发
9.1 基于RTTR实现反射
9.2 实现GameObject-Component
10. 相机
10. 相机
10.1 最简单的相机
10.2 多相机渲染
10.3 相机排序
10.4 CullingMask
11. 控制系统
11. 控制系统
11.1 键盘控制
11.2 鼠标控制
12. 拆分引擎和项目
12. 拆分引擎和项目
13. 绘制文字
13. 绘制文字
13.1 TrueType简介
13.2 绘制单个字符
13.3 绘制多个文字
13.4 彩色字
14. GUI
14. GUI
14.1 正交相机
14.2 UIImage
14.3 UIMask
14.4 UIText
14.5 UIButton
15. 播放音效
15. 播放音效
15.1 播放2D音效
15.2 播放3D音效
15.3 使用FMOD Studio音频引擎
16. Profiler
16. Profiler
16.1 初识easy_profiler
16.2 集成easy_profiler
17. 嵌入Lua
17. 嵌入Lua
17.1 Sol2与C++交互
17.2 更加友好的Lua框架设计
17.3 引擎集成sol2
17.4 调试Lua
18. 骨骼动画
18. 骨骼动画
18.1 Blender制作骨骼动画
18.2 Blender导出骨骼动画
18.3 解析骨骼动画
18.4 矩阵的主序
19. 骨骼蒙皮动画
19. 骨骼蒙皮动画
19.1 骨骼蒙皮动画实现
19.2 骨骼权重
19.3 Blender蒙皮刷权重
19.4 Blender导出蒙皮权重
19.5 加载权重文件
20. 解析FBX文件
20. 解析FBX文件
20.1 导出Mesh
20.2 导出骨骼动画
20.3 导出权重
20.4 渲染骨骼蒙皮动画
21. 多线程渲染
21. 多线程渲染
21.1 GLFW多线程渲染
21.2 基于任务队列的多线程渲染
21.3 完全异步的多线程模型
21.4 引擎支持多线程渲染
22. Physx物理引擎
22. Physx物理引擎
22.1 Physx实例-小球掉落
22.2 物理材质
22.3 碰撞检测
22.4 连续碰撞检测
22.5 场景查询
22.6 引擎集成Physx
23. 经典光照
23. 经典光照
23.1 环境光
23.2 漫反射光照模型
23.3 镜面高光光照模型
23.4 高光贴图
23.5 Shader结构体
23.6 Uniform Buffer Object
23.7 方向光
23.8 点光源
23.9 多光源
24. 引擎编辑器的实现
24. 引擎编辑器的实现
24.1 分析Godot引擎编辑器
24.2 FBO RenderTexture GameTurbo DLSS
24.3 ImGui介绍与使用
24.4 分离引擎核心层和应用层
24.5 使用ImGui实现引擎编辑器
24.6 Hierarchy与Inspector面板
24.7 Geometry Buffer
25. Shadow Mapping
25. Shadow Mapping
25.1 深度图
25.2 简单阴影
88. VSCode扩展开发与定制
88. VSCode扩展开发与定制
88.1 第一个VSCode扩展程序
88.2 从源码编译VSCode
88.3 打包VSCode内置扩展
88.4 打包LuaHelper到Code-OSS
89. Doxygen生成API文档
89. Doxygen生成API文档
90. GPU分析工具
90. GPU分析工具
90.1 RenderDoc分析不显示bug
98. SubstancePainter插件开发
98. SubstancePainter插件开发
98.1 SP插件开发环境
98.2 开发SP功能性插件
98.3 开发SP渲染插件
99. Toolbag插件开发
99. Toolbag插件开发
99.1 插件开发环境
99.2 API介绍
99.3 命令行调用Toolbag
99.4 更多实现
99.5 代码参考
附录1. Wwise音频引擎
附录1. Wwise音频引擎
1.1 Wwise名词概念
1.2 Wwise制作音效导出SoundBank
1.3 集成Wwise
1.4 封装Wwise播放3D音效
1.5 Wwise性能分析器介绍
1.6 猎人开发后记
代码资源下载
点我下载
Github
点赞、收藏、关注
目录
<< 19.3 Blender蒙皮刷权重
19.5 加载权重文件 >>
## 19.4 Blender导出蒙皮权重 ```text 「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book ``` ```bash Blender文件位于 files\skinned_mesh_renderer\export_weight\export_weight.blend ``` 在Blender中做完蒙皮,并且刷好权重之后,就可以导出了。 前面已经介绍了如何导出Mesh,以及骨骼动画,那么现在只要导出顶点关联的骨骼序号以及对应权重即可。 其实就是导出 `19.2 骨骼权重` 手动创建的数据,如下: ```lua --file:example/login_scene.lua line:53 --顶点关联骨骼信息,按照 bone_index_[4] bone_weight_[4] 的顺序存储 local vertex_relate_bone_infos=sol2.convert_sequence_int({ 0, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第一个顶点:关联骨骼0,权重是1。注意-1表示无骨骼。 0, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第二个顶点同理 0, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第三个顶点同理 0, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第四个顶点同理 0, 1, -1, -1, --[[左边骨骼,右边权重]] 70, 30, -1, -1,--第一个顶点:关联骨骼0,权重0.7,关联骨骼1,权重0.3。 0, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第二个顶点:关联骨骼0,权重1. 1, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第三个顶点:关联骨骼1,权重1. 1, -1, -1, -1, --[[左边骨骼,右边权重]] 100, -1, -1, -1,--第四个顶点:关联骨骼1,权重1. }) mesh_filter:set_vertex_relate_bone_infos(vertex_relate_bone_infos) ``` 只不过上面是在Lua中手动创建的数据,受限于Lua的API,没法直接以内存的形式传递,而是从`table`转成了`vector`,再遍历拷贝。 在Blender中导出,可以直接导出更符合内存结构的数据到文件中,在引擎中就只要加载然后`memcpy`,速度更快。 来看一下之前的代码,单个顶点关联的骨骼及权重,用结构`VertexRelateBoneInfo`来存储。 ```c++ //file:source/renderer/mesh_filter.h line:84 /// 顶点关联骨骼及权重,每个顶点最多可以关联4个骨骼。 struct VertexRelateBoneInfo{ char bone_index_[4];//骨骼索引,一般骨骼少于128个,用char就行。 char bone_weight_[4];//骨骼权重,权重不会超过100,所以用char类型就可以。 }; ``` 所有顶点关联的骨骼权重,存储在`MeshFilter::vertex_relate_bone_infos_`中: ```c++ private: ...... VertexRelateBoneInfo* vertex_relate_bone_infos_= nullptr;//顶点关联骨骼信息(4个骨骼索引、权重),长度为顶点数 ``` 按照这个结构,只要将Lua中手动创建的数据格式,一个一个数字写入到文件就行了。 读取的时候再读取整段数据,`memcpy`即可。 下面就来看如何导出吧。 ### 1. 导出蒙皮权重数据 我这里还是直接贴出Blender的Python代码,注释写的比较详细。<a id="antiCollectorAdTxt" href="https://github.com/ThisisGame/cpp-game-engine-book">「游戏引擎 浅入浅出」是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book</a> ```python # file:files\skinned_mesh_renderer\export_weight\export_weight.py # 导出骨骼权重,需要在Blender合并Mesh,然后选择Mesh,然后执行脚本。 import bpy import os import struct # 引擎顶点结构(坐标、颜色、纹理坐标) class EngineVertex: x=y=z=r=g=b=a=u=v=0 def __init__(self,x,y,z,r,g,b,a,u,v): self.x=x self.y=y self.z=z self.r=r self.g=g self.b=b self.a=a self.u=u self.v=v def __eq__(self,other): equal= self.x==other.x and self.y==other.y and self.z==other.z and self.r==other.r and \ self.g==other.g and self.b==other.b and self.a==other.a and self.u==other.u and self.v==other.v return equal def __str__(self) -> str: return "x:{:.2f},y:{:.2f},z:{:.2f},r:{:.2f},g:{:.2f},b:{:.2f},a:{:.2f},u:{:.2f},v:{:.2f}".format(self.x,self.y,self.z,self.r,self.g,self.b,self.a,self.u,self.v) def __repr__(self) -> str:#显示属性 用来打印。 return str(self) def write(self,f): f.write(struct.pack('f',self.x)) f.write(struct.pack('f',self.y)) f.write(struct.pack('f',self.z)) f.write(struct.pack('f',self.r)) f.write(struct.pack('f',self.g)) f.write(struct.pack('f',self.b)) f.write(struct.pack('f',self.a)) f.write(struct.pack('f',self.u)) f.write(struct.pack('f',self.v)) # 顶点关联骨骼及权重,每个顶点最多可以关联4个骨骼。 class VertexRelateBoneInfo: bone_index=[-1,-1,-1,-1] #骨骼索引,一般骨骼少于128个,用char就行。注意-1表示无骨骼。 bone_weight=[-1,-1,-1,-1]#骨骼权重,权重不会超过100,所以用char类型就可以。 def __init__(self):# 一定要在 __init__ 里面初始化一次,不然每次实例化返回的对象实例都是相同指针。 self.bone_index=[-1,-1,-1,-1] self.bone_weight=[-1,-1,-1,-1] def __str__(self): return "bone_index:{},bone_weight:{}".format(self.bone_index,self.bone_weight) def __repr__(self): return str(self) def write(self,f): for i in range(4):# 写入4个骨骼索引 f.write(struct.pack('b',self.bone_index[i])) for i in range(4):# 写入4个骨骼权重,注意权重是乘以100的。 if self.bone_weight[i]>=0: f.write(struct.pack('b',int(self.bone_weight[i]*100))) else: f.write(struct.pack('b',-1)) # 显示信息框 def ShowMessageBox(message = "", title = "Message Box", icon = 'INFO'): def draw(self, context): self.layout.label(text=message) bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) print("----------- EXPORT MESH & WEIGHT ---------") # 当前选中的Mesh mesh_selected = bpy.context.object.data print("---------- COLLECT BONE ------------") # 收集骨骼名字 [bone_name , bone_name , ...] bone_names=[] armature_object = bpy.data.objects["Armature"] for i in range(0, len(armature_object.data.bones)): bone = armature_object.data.bones[i] bone_names.append(bone.name) print(bone_names) print("---------- COLLECT WEIGHT ------------") # 收集骨骼权重 armature_object = bpy.data.objects["Armature"]#场景中的骨架对象。 object_group_names = [g.name for g in bpy.context.object.vertex_groups]# 获取当前Mesh 关联的所有的顶点组名称。 print(object_group_names) print("----------- GENERATE MESH & WEIGHT ---------") # 生成引擎顶点数据,并设置权重。 uv_layer = mesh_selected.uv_layers.active.data engine_vertexes=[] engine_vertex_indexes=[] engine_vertex_relate_bone_infos=[]#顶点关联骨骼信息(4个骨骼索引、权重),长度为顶点数。 for poly in mesh_selected.polygons:#遍历多边形 print("Polygon index: %d, length: %d" % (poly.index, poly.loop_total)) if poly.loop_total==4: ShowMessageBox("Need Triangle","Polygon Error", 'ERROR') break # 遍历一个多边形的3个顶点,注意这里的loop_index是包含重复顶点的顶点数组的index,是以三角形为单位*3作为顶点总数,并不是实际顶点的index。 # 例如第一个三角形就是0 1 2,第二个三角形就是 3 4 5。是包含重复顶点的。 for loop_index in range(poly.loop_start + poly.loop_total-1,poly.loop_start-1,-1): vertex_index=mesh_selected.loops[loop_index].vertex_index#顶点在Blender中的索引 vertex=mesh_selected.vertices[vertex_index] uv=uv_layer[loop_index].uv engine_vertex=EngineVertex(vertex.co.x,vertex.co.z,vertex.co.y,1,1,1,1,uv.x,uv.y)#构造引擎顶点,注意 y z调换 #判断顶点是否存在 find_engine_vertex_index=-1 for engine_vertex_index in range(len(engine_vertexes)): if engine_vertexes[engine_vertex_index]==engine_vertex: find_engine_vertex_index=engine_vertex_index break if find_engine_vertex_index<0: find_engine_vertex_index=len(engine_vertexes) engine_vertexes.append(engine_vertex) # 这个顶点的权重数据 vertex_relate_bone_info=VertexRelateBoneInfo() for group_index in range(0, len(vertex.groups)):#遍历顶点关联的顶点组。 group = vertex.groups[group_index] group_name = object_group_names[group.group]#获取顶点组名字 # print("Vertex group index: %d, group name: %s, weight: %f" % (group_index, group_name, group.weight)) if group_name in bone_names:#判断顶点组名字是否在骨骼名字列表中。制作的时候,建模人员要严格按照骨骼名字来作为顶点组名字。 bone_index=bone_names.index(group_name)#获取骨骼索引。 vertex_relate_bone_info.bone_index[group_index]=bone_index vertex_relate_bone_info.bone_weight[group_index]=group.weight else: print("Error: group name not in bone names") # print(vertex_relate_bone_info) engine_vertex_relate_bone_infos.append(vertex_relate_bone_info) engine_vertex_indexes.append(find_engine_vertex_index)#把index放入顶点索引表 print(engine_vertexes) print(engine_vertex_indexes) print(*engine_vertex_relate_bone_infos,sep='\n') print("----------- CHECK EXPORT DIR ---------") # 检查目录 export 是否存在,不存在则创建 blender_project_dir = os.path.dirname(bpy.data.filepath) if os.path.exists(blender_project_dir+"/export")==False: os.mkdir(blender_project_dir+"/export") print("----------- WRITE MESH FILE ---------") # 将顶点数据写入 export.mesh 文件 with open(blender_project_dir+"/export/export.mesh", 'wb') as f: #写入文件头 f.write("mesh".encode()) f.write(struct.pack('H',len(engine_vertexes))) f.write(struct.pack('H',len(engine_vertex_indexes))) #写入顶点数据 for engine_vertex in engine_vertexes: engine_vertex.write(f) #写入索引数据 kVertexIndexVectorInverse=engine_vertex_indexes[::-1] for engine_vertex_index in kVertexIndexVectorInverse: f.write(struct.pack('H',engine_vertex_index)) f.close() print("----------- WRITE WEIGHT FILE ---------") # 将权重数据写入 export.weight 文件 with open(blender_project_dir+"/export/export.weight", 'wb') as f: #写入文件头 f.write("weight".encode()) #写入顶点关联骨骼信息(4个骨骼索引、权重),长度为顶点数。 for vertex_relate_bone_info in engine_vertex_relate_bone_infos: vertex_relate_bone_info.write(f) f.close() print("----------- SUCCESS --------------") ``` 注意先选中物体,然后再执行脚本。  会同时导出`.Mesh`(模型)和`.Weight`(权重)文件。  如果代码没有加载出来,可能是因为我电脑路径和你电脑路径不对,那点旁边的按钮,再打开代码文件。  ### 2. 验证 在`HxD`打开导出的`export.weight`文件,查看数据是否正确。 
<< 19.3 Blender蒙皮刷权重
19.5 加载权重文件 >>
12
代码资源下载
点我下载
Github
点赞、收藏、关注
目录
Introduction
Introduction
前言
前言
1. 游戏引擎框架介绍
1. 游戏引擎框架介绍
1.1 Unity的组成
1.2 游戏引擎组成
2. Opengl开发环境搭建
2. Opengl开发环境搭建
2.1 Opengl到底是什么
2.2 搭建Opengl开发环境
2.3 使用VisualStudio开发
3. 绘制多边形
3. 绘制多边形
3.1 画个三角形
3.2 画个正方形
3.3 画个立方体
4. 着色器
4. 着色器
4.1 Unity Shader和OpenGL Shader
4.2 顶点着色器
4.3 片段着色器
5. 绘制贴图
5. 绘制贴图
5.1 颜色和贴图
5.2 贴图文件介绍
5.3 CPU与GPU的通信方式
5.4 使用stb_image解析图片
5.5 绘制带贴图的立方体盒子
5.6 压缩纹理
5.7 图片压缩工具
5.8 使用压缩纹理
5.9 DXT压缩纹理扩展
6. 索引与缓冲区对象
6. 索引与缓冲区对象
6.1 顶点索引
6.2 缓冲区对象
6.3 OpenGL Core Profile
6.4 顶点数组对象
7. 绘制Mesh和材质
7. 绘制Mesh和材质
7.1 导出Mesh文件
7.2 使用Mesh文件
7.3 Shader文件创建与使用
7.4 创建材质
7.5 使用材质
7.6 MeshRenderer
8. 绘制静态模型
8. 绘制静态模型
8.1 Blender安装与配置
8.2 Blender制作模型
8.3 Blender Python设置开发环境
8.4 Blender Python创建物体
8.5 Blender Python导出顶点数据
8.6 加载导出的Mesh
9. 基于组件开发
9. 基于组件开发
9.1 基于RTTR实现反射
9.2 实现GameObject-Component
10. 相机
10. 相机
10.1 最简单的相机
10.2 多相机渲染
10.3 相机排序
10.4 CullingMask
11. 控制系统
11. 控制系统
11.1 键盘控制
11.2 鼠标控制
12. 拆分引擎和项目
12. 拆分引擎和项目
13. 绘制文字
13. 绘制文字
13.1 TrueType简介
13.2 绘制单个字符
13.3 绘制多个文字
13.4 彩色字
14. GUI
14. GUI
14.1 正交相机
14.2 UIImage
14.3 UIMask
14.4 UIText
14.5 UIButton
15. 播放音效
15. 播放音效
15.1 播放2D音效
15.2 播放3D音效
15.3 使用FMOD Studio音频引擎
16. Profiler
16. Profiler
16.1 初识easy_profiler
16.2 集成easy_profiler
17. 嵌入Lua
17. 嵌入Lua
17.1 Sol2与C++交互
17.2 更加友好的Lua框架设计
17.3 引擎集成sol2
17.4 调试Lua
18. 骨骼动画
18. 骨骼动画
18.1 Blender制作骨骼动画
18.2 Blender导出骨骼动画
18.3 解析骨骼动画
18.4 矩阵的主序
19. 骨骼蒙皮动画
19. 骨骼蒙皮动画
19.1 骨骼蒙皮动画实现
19.2 骨骼权重
19.3 Blender蒙皮刷权重
19.4 Blender导出蒙皮权重
19.5 加载权重文件
20. 解析FBX文件
20. 解析FBX文件
20.1 导出Mesh
20.2 导出骨骼动画
20.3 导出权重
20.4 渲染骨骼蒙皮动画
21. 多线程渲染
21. 多线程渲染
21.1 GLFW多线程渲染
21.2 基于任务队列的多线程渲染
21.3 完全异步的多线程模型
21.4 引擎支持多线程渲染
22. Physx物理引擎
22. Physx物理引擎
22.1 Physx实例-小球掉落
22.2 物理材质
22.3 碰撞检测
22.4 连续碰撞检测
22.5 场景查询
22.6 引擎集成Physx
23. 经典光照
23. 经典光照
23.1 环境光
23.2 漫反射光照模型
23.3 镜面高光光照模型
23.4 高光贴图
23.5 Shader结构体
23.6 Uniform Buffer Object
23.7 方向光
23.8 点光源
23.9 多光源
24. 引擎编辑器的实现
24. 引擎编辑器的实现
24.1 分析Godot引擎编辑器
24.2 FBO RenderTexture GameTurbo DLSS
24.3 ImGui介绍与使用
24.4 分离引擎核心层和应用层
24.5 使用ImGui实现引擎编辑器
24.6 Hierarchy与Inspector面板
24.7 Geometry Buffer
25. Shadow Mapping
25. Shadow Mapping
25.1 深度图
25.2 简单阴影
88. VSCode扩展开发与定制
88. VSCode扩展开发与定制
88.1 第一个VSCode扩展程序
88.2 从源码编译VSCode
88.3 打包VSCode内置扩展
88.4 打包LuaHelper到Code-OSS
89. Doxygen生成API文档
89. Doxygen生成API文档
90. GPU分析工具
90. GPU分析工具
90.1 RenderDoc分析不显示bug
98. SubstancePainter插件开发
98. SubstancePainter插件开发
98.1 SP插件开发环境
98.2 开发SP功能性插件
98.3 开发SP渲染插件
99. Toolbag插件开发
99. Toolbag插件开发
99.1 插件开发环境
99.2 API介绍
99.3 命令行调用Toolbag
99.4 更多实现
99.5 代码参考
附录1. Wwise音频引擎
附录1. Wwise音频引擎
1.1 Wwise名词概念
1.2 Wwise制作音效导出SoundBank
1.3 集成Wwise
1.4 封装Wwise播放3D音效
1.5 Wwise性能分析器介绍
1.6 猎人开发后记