基于 OpenGLES 的 3D 老婆

引言

本文是大二暑校课程《移动图形概论》的大作业报告。

OpenGLES3DWaifu实现了一个3D人物在3D场景中舞蹈的动画展示,并且加入了传统光照技术以及shadowmapping使得画面更加逼真。

项目地址:OpenGLES3DWaifu

实现效果:

本作业主要分为两个大部分:整合生成模型文件以及编写代码。

一、整合生成模型文件

PowerVR SDK提供了加载模型的api:pvr::assets::loadModel(),方便我们解析模型中包含的meshes数据(包括顶点坐标、法向切向、骨骼数据等)、蒙皮动画、灯光、相机、相机动画、材质等信息。因此我们需要将收集到的3D人物、背景、动画文件整合生成PowerVR SDK方便读取的模型文件。这一部分我选择使用Blender来整合资源,主要有以下几个要点:

1. 模型文件格式

loadModel()事实上仅支持POD和GLTF两种格式(截至目前最新版本v5.7),前者是PowerVR推荐格式,而后者则更应用相对广泛一些。但囿于时间,我没能成功调试出程序使其正常读取GLTF文件——通过Blender整合导出的GLTF文件在程序载入时总会在texture加载时出问题,并且也无法读出灯光数据。因此我最终选择了POD格式。

然而市面上常见的3D动画软件多不支持POD格式的导出,POD文件只能由PowerVR提供的工具PVRGeoPOD导出(Blender可以通过安装插件以增加pod文件的导出选项,但其实也是调用的PVRGeoPOD)。所以我们需要先导出到一个中间文件,再转换为POD文件。中间文件我使用了应用较广的fbx格式。

事实上,收集得到的3D人物、背景、动画数据本身也是异于fbx的其它格式(pmx、vmd),导入Blender的过程中就很容易造成数据错误出bug,而且还有两位数的导入选项供选择。再进行一次导出与格式转换出错概率成倍叠加。因此这一部分需要更多的钻研与调试。

2. 纹理数据格式

loadModel()支持模型导入6种格式的纹理文件。然而在调用

void ModelGles::init(pvr::IAssetProvider& assetProvider, pvr::assets::Model& inModel, Flags flags)

来初始化场景时,函数会调用pvr::utils::textureUpload(),其中的gl::TexStorage2D()对于纹理文件的编码方式有限定,它几乎不支持BGR(A)顺序的编码方式,比如BGRA8888(GL_BGRA_EXT)。而tga、bmp等多数格式文件都是以BGRA顺序编码的。因此要使用PowerVR SDK提供的api,真正能用的纹理格式只有能以RGBA方式编码的pvr和ktx,并需要用PVRTexTool进转换。我选择了ktx格式。

3. 资源导入与整合

动画数据必须和3D人物的骨架数据相契合才能实现较好的效果。MMD圈有许多优秀资源,并且可以通过在Blender上安装插件Cats Blender Plugin与blender mmd tool来导入,主要有以下流程与细节:

  1. 如果使用Cats Blender Plugin来导入人物模型并fix Model会造成部分骨骼数据的简化与约舍,再用mmd tool导入动画数据后人物动作会很僵硬。因此可以先用mmd tool导入人物模型,注意导入时需要进行缩放以免尺寸过大不便操作。之后再有选择地利用Cats Blender Plugin的fix Model功能。
  2. fix Model时主要是用以去除Rigidbodies和Jionts的数据,因为这两者是用于MMD软件的物理引擎计算,在我们的实例中用不到。另外尽量多地保留骨骼数据不进行归并。
  3. 将所有材质的Mmd Toon Tex节点删除。
  4. 翻译骨骼标签为Blender的格式,否则动画文件导入无法正常工作。
  5. 利用mmd tool导入动画文件,缩放比例与导入人物模型时要相同。
  6. 合并所有textures与meshes,这是最为关键的一步。如果仅仅是将meshes合并,那么在将fbx文件转换为pod文件时,PVRGeoPOD会因为一个mesh使用了多个texture而重新拆解成多个mesh,这个过程会造成严重的bug并且难以发现。因此利用Material Combiner插件将人物模型使用的所有纹理合并为一个纹理文件,并合并所有meshes。
  7. 利用PVRTexTool将合并后的纹理转为ktx格式,并作垂直翻转。手动修改纹理路径为ktx文件以便我们的程序正常运行。因为PowerVR没有提供修改pod文件中材质纹理路径的工具,因此只能在这一阶段固定好纹理路径。
  8. 导入背景模型文件,流程与上述类似。
  9. 修改相机与灯光至合适位置,此后也没有机会再对pod文件中的这些数据进行修改。
  10. 整理场景集合的数据为如下结构,这样导出并转换得到的pod文件程序才能正确读取:
  • 11. 导出为fbx时选择所有物体类型、使用空间变换、应用修改器、导出切向空间、烘培动画。如果在cats插件中fix Model时保留了末端骨头就不再添加叶骨。

4. 转为POD文件

导出选项在默认配置的基础上有以下不同和要点:

  • Indexed triangle list。
  • Export skinning data打勾,取消Bone batching,后者是在OpenGLES3.0版本后就不必使用了。
  • 如果需要可以导出切向和法向向量。

二、编写代码

代码主要是在PowerVR SDK v5.12中的例程Skinning基础上修改的,并且参照了PowerVR SDK v3.4中的例程CubeShadowMapping以及OpenGL教程:阴影贴图LearnOpenGL-阴影映射ARMMALI-Shadow Mapping中的教程与代码。主要做了以下三方面工作:

修改原程序

对原程序的一个主要的修改是针对骨骼数据的处理的。因为原程序适配的演示蒙皮动画文件Robot.pod在数据构成上很特殊,因此并不适用于我们生成的pod文件。

首先,Robot.pod中包含骨骼的mesh只有一个,即robot。而原程序利用ssbo向shader传送BoneMatrix和BoneMatrixIT,在每次渲染节点时都会调用一次gl::MapBufferRange()。

// line 457     
void* bones = gl::MapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, static_cast<GLsizeiptr>(_deviceResources->ssboView.getSize()), GL_MAP_WRITE_BIT);

其中的ssboView的大小在原程序275行由最后一个有骨头的mesh所设置:

// line 275
ssboView.setLastElementArraySize(static_cast<uint32_t>(skeleton.bones.size()));

这个大小=ssbo结构大小*影响单个vertex的骨头个数。因为在这个实例中ssbo结构大小为34字节,并且与单个vertex相关联的骨头个数最多为4,因此我直接修改为了:

void *bones = gl::MapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 112 * numBones, GL_MAP_WRITE_BIT);

其次,Robot.pod中的Robot Mesh,影响单个vertex的骨头个数为4。因此对于相关联骨头个数小于4的pod文件,使用原程序可能会导致传送BoneMatrix时数据错位,同时Vertex Shader也不适用。因此我添加了

uniform int BoneCount;  // 每vertex受几个bone影响

并对原代码和shader作了修改。

加入传统光照技术

套用了老师的例程。但是因为我没有法线纹理贴图,因此人物没有纵深感。并且因为没有阴影人物像是悬在空中舞蹈。效果不够好。因此我又加入了ShadowMap。

加入ShadowMap

流程大概为:

  1. 创建深度纹理与帧缓存
  2. 将创建的纹理对象绑定到帧缓冲区
  3. 把光源作为摄像机进行一次渲染(即导入的是光源的PV矩阵)
  4. 解除绑定的缓冲区对象,重新绑定绘制到系统离屏缓冲。设置刚刚获得深度纹理,以回到相机视角进行一次渲染(导入相机的PV矩阵)。

为简化程序我没有为渲染shadowmap单独创建shader和program,而是又改写了SkinnedVertShader和SkinnedFragShader,用新的uniform isPassLight来区分之前的逻辑,以和人物、背景共用一个渲染程序。

为更好的效果,采用了泊松分布。

三、总结

OpenGLES的程序Debug非常难,一方面可以利用PowerVR提供的工具PVRCarbon,另一方面可以查询kronos和教材的文档。但是实现出来也非常有意思。未来可以进一步改进shader以实现更好的效果。

文章作者:哈猪猪
文章链接:https://hazhuzhu.com/embedded/opengles3dwaifu.html
许可协议: CC BY-NC-SA 4.0
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇