在Cesium中加载模型时一个需要注意的地方就是模型的姿态问题,我们在本篇文章及下一篇与大家进行探讨。

一、背景概述

cesium-1.47,gltf 2.0
我们知道目前市面上有许多种3d格式,各大厂商纷纷开发自己的数据格式以争取话语权。
而cesium支持多种格式的三维模型,主要有dae,gltf,glb,czml以及3d-tiles,它们的文件组织格式和坐标系统不尽相同,所以我们需要进行探讨。顺便说一下踩过的坑。
在本篇文章中,我们主要探讨的是obj格式的模型,为什么是obj呢?因为之前的几篇中写到了osgb-->obj-->gltf的格式转换路线,obj正是其中的关键环节。在这个地方主要出现了两个问题,一是纹理贴错了,需要做一个镜像;二是模型的姿态在转换到gltf是总是错误的,我用的是obj2gltf工具。那么我们就这两个问题分别进行探讨,以姿态为主,纹理的问题我只提供一个简单的方法。

注:3d-tiles的事情以后再讨论

二、解决方法

1、对于纹理镜像的问题提供一个简单思路:
在进行dds-->png的转化中,我们使用了工具DDS4J。看他的源码发现其实是以横排的像素作为单位进行转换的,那么我们在此就可以做一点文章了。

ddsImageDecoder.java------>convertToPNG(....)

修改前:

DdsHeader header = dds.getHeader();FormatDecoder decoder = Decoders.getDecoder(dds);ImageInfo imageInfo = new ImageInfo(header.getDwWidth(), header.getDwHeight(), 8, true);PngWriter pngWriter = new PngWriter(outputStream, imageInfo);ImageLineInt imageLine = new ImageLineInt(imageInfo);for (int[] ints : decoder) {swizzle(ints, swizzle);ImageLineHelper.setPixelsRGBA8(imageLine, ints);pngWriter.writeRow(imageLine);}pngWriter.end();

修改后:

 DdsHeader header = dds.getHeader();FormatDecoder decoder = Decoders.getDecoder(dds);ImageInfo imageInfo = new ImageInfo(header.getDwWidth(), header.getDwHeight(), 8, true);PngWriter pngWriter = new PngWriter(outputStream, imageInfo);ImageLineInt imageLine = new ImageLineInt(imageInfo);// 镜像List<int[]> data=new ArrayList<>();for (int[] ints:decoder){data.add(ints);}for (int i=data.size();i>0;i--){int[] linedata=data.get(i-1);swizzle(linedata,swizzle);ImageLineHelper.setPixelsRGBA8(imageLine,linedata);pngWriter.writeRow(imageLine);}pngWriter.end();

2、解决姿态问题的方法:使用软件blender对模型进行旋转操作
[1]手动方式解决:
file-->import-->obj导入模型;在右上角的outliner操作框中可以看到scene中加了几个object,这就是我们的模型,请把自带的cube右键删除掉;选中我们模型里的任意一个object,按键盘R键(旋转的快捷键),然后输入xyz字母中的一个作为约束轴,最后输入角度值(可以为负),回车确定即完成旋转操作;以同上的操作对该模型的其他object进行旋转;file-->export-->obj导出模型。


[2]代码解决:
blender是可以进行二次开发的,可以使用python脚本(3.x)扩展blender功能,但是也有一些不能由python脚本实现,需要使用C/C++进行开发。
有两种方式来进行开发,一种是使用内置的python控制台来写代码,这种比较简单,但是不适合较大任务;所以另外blender提供文本编辑器(Text Editor)来进行开发,所以我们切换到文本编辑器进行开发,其中也提供了常见功能的代码例子以供参阅。

下面是我的一些代码,主要就是实现了旋转的功能。注意,在写代码前请先配置好python 3.x环境。

# 目标1:读取文件夹下所有obj文件
# 目标2:对obj模型中所有mesh进行旋转(x轴 -90度)
# 目标3:对旋转后obj模型进行保存(原文件名 + "-RotateX-90")import bpy,osInputFileDirectory="E:/obj/obj";
OutputFileDirectory="E:/obj/obj/RotateX-90Out";
Files=os.listdir(InputFileDirectory);
for file in Files:if file.endswith(".obj"):bpy.ops.import_scene.obj(filepath=(InputFileDirectory+"/"+file));     # 导入模型bpy.ops.transform.rotate(axis=(-90,0,0));   # 以x为轴旋转-90度str=file.split('.');OutPath=str[0]+'-RotateX-90.obj';bpy.ops.export_scene.obj(filepath=(OutputFileDirectory+"/"+OutPath));   # 输出模型# 删除模型override = bpy.context.copy();override['selected_bases'] = list(bpy.context.scene.object_bases);bpy.ops.object.delete(override);print(file+" is over");

注:输出在菜单栏 window-->Toggle System console 查看

如果想对gltf模型直接进行操作,请先导入gltf导入和导出模块,才能对其进行操作。不过我测试用的时候总是丢失纹理,所以我还是选择了对obj文件进行旋转操作。

对于blender中的任意按钮或菜单,鼠标悬停后可以看到其对应的python命令,右键单击可以进入API文档查看该命令的具体参数和使用方法。

三、理论依据和原因追究

以上呢都是关于问题的描述以及如何去解决这个问题,那么除此之外,我们还要去弄清楚这到底是怎么回事,那么我们一起来看一下。

在我们这里遇到的模型姿态问题实际上是坐标轴朝向问题。glTF使用满足右手准则的三维空间笛卡尔坐标系,其中Y轴表示物体的上方向、Z轴垂直屏幕指向屏幕外、X轴为Y轴与Z轴向量的叉积。而大部分建模软件(如blender,3ds Max)和人们平常认知中都是以Z轴向上的坐标系,所以就导致了转化出来的 glTF场景中三维物体的底层顶点数据是以Z轴正方向为上方向的。最重要的是Cesium也是以Z轴向上,X轴向外,Y轴为XZ叉积的右手坐标系,那么就会显示出我们这里说的姿态问题。就这个问题的解决还有其他的方法:

1、使用blender导入obj模型后,再导出模型,但是导出的模型选项应为 -Z Froward, Y Up

在bpy.ops.export_scene.obj(.....) 参数中修改即可,API在此

2、在gltf文件的节点树中增加一个根节点以实现坐标系统的旋转,该节点的matrix属性应为[0,0,1,0, 0,1,0,0, -1,0,0,0, 0,0,0,1],

起初我以为这个方法很简单,写个符合json格式的转换的对象填进去就行了,实际上好像并不是这么简单。根据我的询问和摸索,这种方法需要对原来的gltf文件做如下改动:

(1) 在源文件中的"scenes"对象中查看"nodes"节点,把这些节点记录一下一会还要用,比如说为[0,1,2,3,4,5,6],然后将内容改为[8];

(2) 在源文件中的"nodes"节点数组中加入两个对象。一是在开头处加一个子节点数组对象,如{ "children": [1,2,3,4,5,6,7] };之后在末尾处加一个转换矩阵的对象,如{ "name": "Y_UP_Transform","matrix": [0,0,1,0,0,1,0,0,-1,0,0,0,0,0,0,1],"children" : [0] }

我来大概解释一下为什么,因为scenes是我们的场景,也就是要渲染的东西。修改前后分别渲染的是[0,1,2,3,4,5,6]和[8],这里面的数字或数组就是node节点对应的位置。那么根据我们这里提供的数字,可以找到对应的对象。在修改后的[8]处,我们可以看到,这就是我们的转换矩阵,而它所对应的子节点就是[0]号节点,继而渲染出所有对象并且执行了旋转。

这个地方有一点需要注意,如果把这个矩阵直接加到[0]节点也是可以的

{"children": [1,2,3,4,5,6,7],"matrix": [0,0,1,0,0,1,0,0,-1,0,0,0,0,0,0,1]}

但是不建议这么做,因为会污染我们的场景。大家也可以看一个其他例子。

四、参考资料

Blender Manual中文版: https://docs.blender.org/manual/zh-hans/dev/getting_started/index.html

Blender python API Documents: https://docs.blender.org/api/current/contents.html

obj文件格式详述: http://paulbourke.net/dataformats/obj/

论文:《3D Tiles 定义解析与生产规范设计》

likangning93

在此一并感谢!

模型姿态问题原因及解决——以obj格式为例相关推荐

  1. 模型过拟合原因及解决办法

    模型过拟合原因及解决办法 过拟合现象 导致过拟合原因 解决办法 过拟合现象 对于样本量有限.但需要使用强大模型的复杂任务,模型很容易出现过拟合的表现,即在训练集上的损失小,在验证集或测试集上的损失较大 ...

  2. 【BIM模型生成点云数据】revit转obj格式,全网最详细最简单的步骤了!

    最近,学习到了一种新方法,用于制作点云数据集,那就是----用BIM三维模型转obj格式之后导入到cloudcompare生成点云数据.该方式适合做仿真实验,也可以用于三维建模的精度对比. 关键性问题 ...

  3. osg导入模型时,模型全黑的原因及解决方法分析

    在导入飞机模型时,发现模型是黑咕隆咚一片,然后翻阅资料,找到三个可能性. (一)几何模型没有法线导致 利用osgUtil::SmoothingVisitor自动生成法线 #include <os ...

  4. zbrush导入obj模型不显示_zbrush软件怎么导入obj格式文件?Zbrush2018教程在哪可以看?...

    回答: 关于ZBrush的学习,我大致整理了一些经常会被问到的问题和我的见解,可以参考一下. Q1:次世代ZB到底难不难?因为技术要求的全面升级,次世代确实不简单,但是新手能不能学习次世代呢? A1: ...

  5. 【深度学习】模型过拟合的原因以及解决办法

    [深度学习]模型过拟合的原因以及解决办法 1.背景 2.模型拟合 3.简述原因 4.欠拟合解决办法 5.过拟合解决办法 1.背景 所谓模型过拟合现象: 在训练网络模型的时候,会发现模型在训练集上表现很 ...

  6. 解决导入obj模型时出现模型镂空的问题

    解决导入obj模型时出现模型镂空的问题 这实际上是因为导入的模型采用四边形而非三角形的面片,导致splish采样不全. 使用houdini的divide节点就能将任意面片转化为三角形面片.从而解决问题 ...

  7. IE6IE7Firefox浏览器不兼容原因及解决办法

    IE6IE7Firefox浏览器不兼容原因及解决办法 一.IE6IE7Firefox浏览器不兼容原因及解决办法 1.文字 本身的大小不兼容.同样是font-size:14px的宋体文字,在不同浏览器下 ...

  8. 欠拟合的原因以及解决办法(深度学习)

    之前这篇文章,我分析了一下深度学习中,模型过拟合的主要原因以及解决办法: 过拟合的原因以及解决办法(深度学习)_大黄的博客-CSDN博客 这篇文章中写一下深度学习中,模型欠拟合的原因以及一些常见的解决 ...

  9. python内核死亡的原因_Kernel Panic常见原因以及解决方法

    Technorati 标签: Kernel Panic 出现原因 1. Linux在中断处理程序中,它不处于任何一个进程上下文,如果使用可能睡眠的函数,则系统调度会被破坏,导致kernel panic ...

最新文章

  1. Hadoop集群搭建(二:集群主机间免密登录配置)
  2. ajax提交数据到后台php接收
  3. 串口ic读卡器源码-c#代码(2)续上
  4. xdebug影响php运行速度
  5. jzoj4051-序列统计【NTT】
  6. 2016_shengyang_onsite
  7. 滴水课后作业(1-5)
  8. 荔枝派 Nano 全志 F1C100s 编译运行 Linux ubuntu并升级gcc
  9. 【Siddhi】Syntax error in SiddhiQL, no viable alternative at input
  10. SpringBoot基础篇日志管理之logback配置文件
  11. AI 崛起?科技公司却偷偷用人类做机器人的工作!
  12. 【SQL】IN、EXISTS和表连接三者的效率比较
  13. 通过修改word文件,来屏蔽宏代码
  14. Android 动画 Kotlin 教程
  15. cd 命令行进入目标文件夹
  16. 苹果6s上市时间_为什么苹果能用5年,安卓1年得换?原因太真实了
  17. Eclipese快捷键
  18. HM编码器代码阅读(13)——帧间预测之AMVP模式(一)总体流程
  19. python之旅六【第六篇】模块
  20. 叶武滨老师时间管理学习感悟

热门文章

  1. Android 的三种定位方式
  2. OAI rfsimulator第一课
  3. JAVA个人通讯录的实现
  4. 苹果审核团队在线讲座最全的细节梳理
  5. 某医院楼体发光字方案
  6. TensorFlow 之基于Inception V3的多标签分类 retrain
  7. 【Leetcode_easy】748. Shortest Completing Word
  8. Android开发基础知识概览
  9. 假如时间还可以重来那你还愿意做过去一年的事吗?——年终总结
  10. 从咖啡商的非洲梦,看雨花区“中非商港”的未来