前言

  昨天做建模的美术给我提了个需求
  3ds Max 导入的模型一旦没有办法修改软硬化边。
  主要原因是导入的模型锁定了,一旦解锁,所有的法线就会重置成硬边。
  虽然对于制作人员来说建模工具都无所谓了,哪个顺手用哪个。
  不过目前还是朝着Maya的方向进行切换,如果可以更顺手那么 Maya 流程推进也能更顺利。

  而且这个法线问题也的确是个较为让人头疼的问题。
  我原本以为非常简单就可以解决,不仅是 API , Mel 也有相关获取顶点法线数据的命令。
  理论上只要解锁之前读取数据,解锁之后重新设置法线就可以了。
  然而我大意了,重新设置法线之后,法线就会处于锁定状态,这个和软硬化边的操作不完全一样。
  为此我花了一整天的时间探索怎么解决这个问题。

法线丢失

  在Maya里面黄色表示法线锁定,绿色表示没有锁定。

  关于软硬边可以参照这个教程
  3、4 集都有很详尽的描述,非常有用。

前期探索

  最初其实我怀疑是 FBX 导入导出的设置问题。
  后来结合 Obj 流程进行测试,我发现Maya导入的模型都是锁定状态的,除非设置了解锁之类的操作。
  因此导入进来的法线信息是正确的,但是一旦解锁就会重置。

  3ds Max 的导出就是如此。
  然后我去看 Maya 导出的FBX是什么情况。
  如果 Maya 导出 FBX 在不锁定法线的情况下导出,那么导回Maya是可以正确解锁的。
  但是如果导出的时候锁定了法线,导回来解锁就变成了硬边状态了。

  最让人无语的就是混合导出,也就是软硬边都有的常见状态,这种也是一解锁就丢失所有软硬边信息。
  这对美术的制作人员来说实在是太痛苦了。


  开始我还是坚信是 FBX 的设置有问题,毕竟导出的时候的确有 法线信息 导出相关的选项。
  可是经过测试并不管用。
  于是我网上搜索了相关的链接。
  我发现有不少的解决方案,但是没有一个是我想要的。

  • 通过 Mel 脚本修改顶点法线
  • 通过 transfer Attribute 传递模型的法线信息

  上述的两个方案我都测试过了,问题都是修改之后法线就被锁定了。
  即便是手动设置法线角度的各种操作都会锁定法线。
  因此照抄模型数据的方案是不可行的。

踩坑尝试

  最后发现只有软硬化边的节点是不锁定法线。
  因此只需要找到模型软化的边,然后解锁,然后对这些边进行软化操作就可以了。

  因此需要解决的问题就是如何从导入的模型上找到硬化的边和软化的边。

github脚本地址

  我最初是通过 pymel 来获取模型上的点,然后通过点的 getNormals 方法获取相邻面的法线信息。
  这里获取到的法线有很严重的缺点,就是不知道法线的向量对应的面是哪一个。
  也就无法判断哪些向量需要考虑的。
  唯一明确的是软化边所有向量都重合的,硬化边则相反。
  因此我认为可以通过比对每一个向量的是否相等来确定这个顶点是否含有硬化边。

  由于 pymel 的执行效率很慢,我还做了个测试。
  后续将脚本转为 OpenMaya 来实现,中间在获取顶点法线数据的时候还卡了好一会。
  后面是法线可以通过迭代器来获取法线数据

  这个思路应对大量平滑的四边面检测是可行的,但是一旦涉及单一的硬化变就无法判断了。
  特别是将模型三角化之后暴露了很多检测错误的问题。
  然而Maya没有向量对应面的信息,也是让我头疼不已。


  后面沿着这个思路走了很多弯路。
失败示范1
失败示范2

  当时还想到将向量数组转成字符 然后计算出 md5 的值
  后面解锁之后,法线的数据就被改变了,重新算一遍 md5 的值,就可以找到哪些顶点的法线发生了改变。
  那么顶点相关的法线就可以硬化了。

  然而这个方案就还是不行,因为顶点周围的边无法判断哪一个才是软化边。
  然而并不是所有顶点相邻的边需要软化的,结果就会出现瑕疵。


  后面还想到既然 软化边 这个节点是通过角度计算过滤的。
  那 Maya 能否过滤出特定角度法线的边呢?
  网上一查也的确有方案,可以通过 polySelectConstraint 命令来实现。
  然而真实情况是硬化边可能是人工加上去的,才不管他的这是角度是多少。
  因此这个方案也行不通了。

最终解决方案

最终解决方案

  后面我看到可以通过 MItMeshFaceVertex 来遍历面上的点,并且通过这个点来获取到对应的法线
  这样就可以构建出面对应顶点的法线数据字典。
  我遍历边的时候可以找到对应的顶点和面。
  接着通过上面获取的字典就可以找到对应面的法线信息,这样一比较就能顺利找出边是否为硬边了。

  结果还是因为之前嫌这个方法有点绕,也没有深入思考是否可以构建出数据对应的字典,而且这个遍历两次效率也有点低。
  结果就一直没有尝试,没想到最后还是通过这个方法解决选边的问题。


  这个脚本可以选择出模型软化的边,当然如果模型的法线是经过人工修改的,那我的脚本是直接当成硬边处理了。
  毕竟一般美术也不会手动修改顶点法线这种操作的,再说这种操作必然锁定法线了的。

  脚本的逻辑再捋一遍

  1. 导入模型
  2. 遍历模型获取面对应点法线的数据
  3. 遍历模型上的边,找到边上顶点对应相邻面的法线数据
  4. 对比数据找到软化的边
  5. 法线解锁
  6. 对软化的边重新进行软化边操作

总结

  虽然这个东西我折腾了一天,最后还是顺利弄好了,满满的成就感。
  而且貌似网上也没有这样操作的解决方案,能够解决制作人员的痛点,我还是很开心的。
  最后其实脚本还有一些可优化的地方,比如添加进度条以及选择部分的线进行单独的解锁。
  不过目前脚本其实已经够用了,以后再看需求吧。