Three.js: GLTFLoader:正切测试模型结果不正确

创建于 2017-06-03  ·  30评论  ·  资料来源: mrdoob/three.js

问题描述

我试图显示正切测试模型
但是,显示的结果似乎与Khronos的示例不同。

Three.js +正切测试模型结果:
image

Khronos样本加载器+正切测试模型结果:
image

我认为此样本模型应具有相同的左右结果。

相关: https :

/ cc @emackey

Three.js版本
  • [x]开发
  • [] r85
  • [] ...
浏览器
  • [x]全部
  • [] Chrome
  • []火狐
  • [ ] IE浏览器
操作系统
  • [x]全部
  • [] Windows
  • [ ] 苹果系统
  • [] Linux
  • [] Android
  • [] iOS
硬件要求(图形卡,VR设备等)

ThinkPad X260 + Windows 10 + Intel HD Graphics 520

Bug Loaders

最有用的评论

我想特别从法线贴图

法线向量使用OpenGL约定,其中+ X正确,+ Y正确。 + Z指向观看者。

这句话使模型可以在没有切向量的情况下运输,从而节省了空间。

让我们测试一下。 在这里,我用绘制程序中的斑点绘制了一个糟糕的高度图(凹凸图):

TestHeightMap

让我们将此高度场定义为向外的凹凸,其中白色像素离观察者更近,黑色像素离观察者更远。

使用在线转换器(质量有问题,但是我们稍后会检查结果),我已经将其从高度图转换为法线图。 请记住,这里没有3D模型,没有UV坐标或mikktspace计算或任何几何图形。 只是将高度图转换为法线图。 我必须按照glTF的说明手动配置在线转换器,以使X正确,Y向上,Z面向查看者。 结果如下:

TestNormalMap

让我们将其带回绘画程序,并消除颜色通道以查看这些向量指向的位置。 在下面,每个颜色通道都被分成了自己的灰度图像。 请记住,这些将被解释为,黑色表示-1.0 ,中间灰色表示0.0 ,白色表示+1.0

TestNormalMap-Decomposed

因此,我认为在线转换器至少在正确配置后才执行glTF的要求。 在红色(X)图像中,我们可以看到右侧的斜率具有白色像素(+1.0),将X向量指向图像的右边缘。 在红色图像的左侧,黑色像素(-1.0)将X向量指向图像的左侧。 在绿色(Y)图像中,沿着凹凸顶部倾斜的白色像素将Y向量指向图像顶部。 Z值是最不直观的,但是请记住,凸块的尖端和背板本身都指向观察者,并且所有侧面的斜率都指向相反,因此都均匀地变暗。

如果将其加载到Blender Eevee中(与glTF一样)可以接受OpenGL样式的法线贴图,该怎么办? 如果UV贴图旋转甚至缩放以反转,会发生什么?

NormalSpinTest

事实证明,这很好。 确实,以这种方式定义切线空间的全部目的并不是要使软件对向量发疯,而是要确保纹理艺术家可以通过确保其法线贴图不受几何形状影响而保持理智。

但是,并非所有软件都使用OpenGL约定。 有些使用不同的约定(有时称为DirectX约定),其中Y向量指向图像的底部而不是顶部。 这是这种形式的图像的分解Y通道。 较亮的像素是面向图像底部的像素。

TestNormalMap_DirectX-Green

如果我将这些DirectX样式的法线贴图之一加载到Blender Eevee中,我还能期望它起作用吗?

NormalSpinTest_DirectX_v3

否。Blender期望+ Y增长。 数学是错误的,并且反射的视界线自转。
如果将OpenGL样式的法线贴图加载到期望+ Y向下的引擎中,则会发生相同的情况。

这就是NormalTangentTest模型正在尝试进行的测试。 每行将UV坐标旋转到不同的方向,以确保反射在这些不同的方向上保持右侧向上。

所有30条评论

感谢您编写此@ cx20。

为了获得更多背景信息,以下是一些相关信息和问题:

  • 我在@donmccurdy的ThreeJS glTF查看器上报告了类似的问题,donmccurdy / three-gltf-viewer#10。 但是我现在认为这是ThreeJS的glTF加载程序中的错误,而不是查看器中的错误。 因此,此新期刊比我的旧期刊更好。

  • 在以上较早的版本中,模型曾经面向天空,但是后来我旋转它使其面向地平线。 如上面所示,这使地平线更容易旋转,因此更容易查看反射不正确的时间。

  • 通过KhronosGroup / glTF#952中的讨论,确认模型自身对法线贴图的使用是正确的。

  • 在#11315中对ThreeJS的法线贴图处理提出了质疑。

  • BabylonJS最近有一个类似的问题,已在此处报告,并已在此处修复。 这是应用了该修复程序的BabylonJS glTF 2.0加载程序

写得好,谢谢!

添加以下这一行,模型似乎可以正确渲染:

materialParams.normalScale = new THREE.Vector2(-1, 1);

但是我不确定我是否很好地理解了这个问题。 理解#11315可能会有所帮助。

@donmccurdy感谢您的建议。
我了解可以通过调整normalScale进行改进。
但是,如果glTF模型正确,我认为glTF Loader最好处理它。

@ cx20同意,此修复程序现在合并到THREE.GLTF2Loader中。

我已经确认它已修复。

Three.js +正切测试模型结果:
image

在r101和r104之间的某个时间,这种情况已经消退:
Screenshot from 2019-06-11 16-38-27

参见https://github.com/mrdoob/three.js/pull/15749-回归是有意的,可以通过在模型中包含切线来避免。

理想情况下,我们将有一个用于生成mikktspace切线的JS实现,以完全解决这个问题,但这相当复杂。

直到现在我还没有意识到#15749。 我对此感到措手不及,我以为我们在定义glTF中的切线方面做得很好,因此它们至少可以在运行时近似。

请注意,默认情况下,Blender导出器不会导出任何glTF切线,因为它有助于减小文件大小,并且glTF的主要实现都通过了该测试而没有切线。 我怀疑此更改可能破坏了ThreeJS中大多数glTF模型的法线映射。

我需要一些时间来阅读所有链接的问题,以更深入地了解发生了什么以及为什么发生。 但我认为glTF社区应该考虑将切线再次正确呈现的模型置于较高的优先级,因为我认为默认情况下大多数模型都属于该类别。

重新开放😅

我怀疑此更改可能破坏了ThreeJS中大多数glTF模型的法线映射。

我不认为这很严重–我们一直在使用导数在着色器中实时生成切线,而现在我们仍然这样做。 之前,我们包含了一个黑客( normalScale.y *= -1 ),它可以修复此特定的测试模型,但也碰巧破坏了其他一些示例。 我没有任何解释何时可以提供帮助或没有帮助的,所以一旦我们支持存储的切线,我们便删除了该hack –在这种情况下,肯定是错误的。 现在,依赖于hack(不包括切线)的模型已损坏,并且修复了被hack破坏(且不包含切线)的模型。

但我认为glTF社区应该考虑将切线再次正确呈现的模型置于较高的优先级,因为我认为默认情况下大多数模型都属于该类别。

往上看。 总的来说,我相信我们会在没有切线的情况下渲染模型。 但是,我们不会按照glTF规范的要求生成mikktspace切线。 据我所知,尚无JS实现,而我们基于导数的着色器实现只是“几乎足够好”的近似值。 该样本模型是故意的极端情况,它证明了这种近似的极限。

我们很高兴拥有JS mikktspace切线生成实现; 这将是THREE.BufferGeometryUtils的很好的补充。 但是正式的(本机)mikktspace代码很长,我还没有深入研究,想知道生成切线需要多少代码。

之前,我们包含了一个黑客( normalScale.y *= -1 ),它专门用于修复此特定测试模型,但也碰巧破坏了其他一些示例

它是否专门破坏了其他glTF模型,或者只是打破了一般示例?

野外有两种不同类型的切线法线贴图。 Substance Painter称它们为“ DirectX法线”和“ OpenGL法线”,这并不是它的最大称呼。 区别特别是y通道是反转的,这意味着纹理中的所有绿色通道值都将反转。 乘以y *= -1是将一个转换为另一个的正确方法。 所谓的“ DirectX法线”使用左手坐标系,而glTF为法线/切线/切线定义了右手坐标系。

我怀疑发生的事情是,当ThreeJS自动计算切线时,它期望法线贴图使用翻转的Y(DirectX)样式编写,并向后获取glTF的通道,因此需要翻转。 但是,提供切线时,无需进行此类翻转。

我认为mikktspace的问题与此分开。 不幸的是,该规范要求mikktspace,并且大多数实现都使用屏幕空间派生词来近似。 我不知道两者有多么相似,但是,只要正确地完成了地图的左/右手操作,当用近似值显示时,在mikktspace中生成的法线贴图似乎就可以很好地工作。

(去年KhronosGroup / glTF-Sample-Models#174也对此进行了讨论)

我想特别从法线贴图

法线向量使用OpenGL约定,其中+ X正确,+ Y正确。 + Z指向观看者。

这句话使模型可以在没有切向量的情况下运输,从而节省了空间。

让我们测试一下。 在这里,我用绘制程序中的斑点绘制了一个糟糕的高度图(凹凸图):

TestHeightMap

让我们将此高度场定义为向外的凹凸,其中白色像素离观察者更近,黑色像素离观察者更远。

使用在线转换器(质量有问题,但是我们稍后会检查结果),我已经将其从高度图转换为法线图。 请记住,这里没有3D模型,没有UV坐标或mikktspace计算或任何几何图形。 只是将高度图转换为法线图。 我必须按照glTF的说明手动配置在线转换器,以使X正确,Y向上,Z面向查看者。 结果如下:

TestNormalMap

让我们将其带回绘画程序,并消除颜色通道以查看这些向量指向的位置。 在下面,每个颜色通道都被分成了自己的灰度图像。 请记住,这些将被解释为,黑色表示-1.0 ,中间灰色表示0.0 ,白色表示+1.0

TestNormalMap-Decomposed

因此,我认为在线转换器至少在正确配置后才执行glTF的要求。 在红色(X)图像中,我们可以看到右侧的斜率具有白色像素(+1.0),将X向量指向图像的右边缘。 在红色图像的左侧,黑色像素(-1.0)将X向量指向图像的左侧。 在绿色(Y)图像中,沿着凹凸顶部倾斜的白色像素将Y向量指向图像顶部。 Z值是最不直观的,但是请记住,凸块的尖端和背板本身都指向观察者,并且所有侧面的斜率都指向相反,因此都均匀地变暗。

如果将其加载到Blender Eevee中(与glTF一样)可以接受OpenGL样式的法线贴图,该怎么办? 如果UV贴图旋转甚至缩放以反转,会发生什么?

NormalSpinTest

事实证明,这很好。 确实,以这种方式定义切线空间的全部目的并不是要使软件对向量发疯,而是要确保纹理艺术家可以通过确保其法线贴图不受几何形状影响而保持理智。

但是,并非所有软件都使用OpenGL约定。 有些使用不同的约定(有时称为DirectX约定),其中Y向量指向图像的底部而不是顶部。 这是这种形式的图像的分解Y通道。 较亮的像素是面向图像底部的像素。

TestNormalMap_DirectX-Green

如果我将这些DirectX样式的法线贴图之一加载到Blender Eevee中,我还能期望它起作用吗?

NormalSpinTest_DirectX_v3

否。Blender期望+ Y增长。 数学是错误的,并且反射的视界线自转。
如果将OpenGL样式的法线贴图加载到期望+ Y向下的引擎中,则会发生相同的情况。

这就是NormalTangentTest模型正在尝试进行的测试。 每行将UV坐标旋转到不同的方向,以确保反射在这些不同的方向上保持右侧向上。

规范中仍然需要一个具体的公式来计算给定图元及其UV坐标,以及如何使用W符号进行双切线计算孤根图元的切线。 “ OpenGL法线”和“ DX法线”不够精确,无法得出公式。 他们可能会引用约定,但是作为实现者,我不知道该怎么做。

我目前正在做的是从MikkTSpace发出翻转的TangentW,以匹配此特定样本,但这恰好起作用。

它是否专门破坏了其他glTF模型,或者只是打破了一般示例?

特别是,报告的错误与glTF模型有关。 就是说,我怀疑是否有足够的信心通过FBX或COLLADA导出材料,以至于这些法线贴图约定是否曾被完全理解和测试过。

区别在于,y通道已反转,这意味着纹理中的所有绿色通道值都已反转。 乘以y * = -1是将一个转换为另一个的正确方法。

谢谢,这是我们实施“ hack”的充分理由。 😇

不幸的是,该规范要求mikktspace,并且大多数实现都使用屏幕空间派生词来近似。

我认为MikkTSpace是生成切线的最可靠方法是正确的规范,我认为,这并不是普遍地在运行时自动执行此操作的正确选择。 如果更便宜的替代品对特定型号而言是正确的,则没有理由做一些看起来没有更好的更昂贵的东西。 可以放宽规格语言以允许近似值,但是我对此并不感到强烈。

规范中仍然需要一个具体的公式来计算给定图元及其UV坐标,以及如何使用W符号进行双切线计算孤根图元的切线。

我不确定MikkTSpace算法是否容易表示为离散公式...您是否在寻求MikkTSpace规范代码的替代方法? 还是除使用MikkTSpace的指令之外的其他信息? @Themaister


对于原始问题,听起来我们应该在某个地方恢复normal.y *= -1乘数。 有三个可能的位置可以执行此操作:

  • (a)在GLTFLoader中,用于没有切线的网格
  • (b)在GLTFLoader中,用于所有网格
  • (c)在WebGLRenderer中,用于所有网格

如果threejs确实在使用DirectX约定,例如Blender不是,则可以看到(c)的情况。 但是,为了一种快速,安全的解决方案,我倾向于选择(a)。

我怀疑正在发生的事情是,当ThreeJS自动计算切线时,它期望法线贴图是使用翻转的Y(DirectX)样式创作的。

不,three.js不假定...

three.js假定正切空间的+ Y为“ up”,而uv空间的增加-V为“ up”。

也就是说,three.js假定uv(0,0)在纹理的左下角,而glTF规范假定在左上角。 这就是为什么GLTFLoadertexture.flipYfalse 。 (默认情况下,three.js将texture.flipYtrue 。)

当切线不存在时,three.js使用屏幕空间导数来估计切线。 它使用链式规则进行操作。 该计算中的一个假设是切线空间和uv空间具有相同的方向。

对于正确编写的glTF模型,该假设是不正确的。 但是,您应该能够通过为_properly遵守glTF spec_的任何模型设置normalScale.y = - 1来进行补偿。

在我看来,我们也可以通过兑现着色器中的flipY标志来自动修复此问题。

如果设置normalScale.y不起作用,则说明正在进行其他操作。

感谢您的澄清。 我认为我们在这里有一条前进的道路。

在我看来,我们也可以通过兑现着色器中的flipY标志来自动修复此问题。

是的,除了glTF提供自己的切向量的情况之外,对吗? 我希望只有自动计算的切线需要flipY测试和y否定。

如果不是这种情况,那意味着我的NormalTangentMirrorTest模型已将不正确的切线编码到其中,这意味着Blender glTF导出器本身将错误的切线放入了glTF模型中。

编辑:我相信我已经确认了导出的切向量的正确性,并且它们不需要任何Y翻转。 如果需要,我可以发布更多细节。

但是似乎正确的操作是仅在将自动生成的切线(未提供的切线)与flipY标志一起使用时才翻转normalScale.y 。 有什么想法吗?

在我以前的文章中,我解释了当属性切线不存在时如何手动补偿反向法线。 当切线存在时,应该没有任何补偿,因为没有使用屏幕空间导数。

我还建议我们可以通过兑现着色器中的flipY标志来自动修复此问题。 我并不是说我们会通过自动翻转normalScale.y来解决此问题。 我认为我们不应该更改用户的设置。

无论如何,在我们走这条路之前,我认为必须验证这个假设:

对于适当遵守glTF规范的任何模型,[Y] ou应该能够通过设置normalScale.y =-1进行补偿。

对于每个未正确渲染的模型,我们必须有一个解释。

我认为我们不应该更改用户的设置。

是的,很抱歉,我无意规定那种实施细节。 我只是想确保对于屏幕空间切线生成的目的_honouring flipY flag_在数学上的含义没有造成误解。

对于每个未正确渲染的模型,我们必须有一个解释。

听起来可能很大。 如果确实存在与官方测试模型完全不同的模型,并且仍然可以认为它们是glTF中法线贴图的有效使用,那么发现这一点很重要。 测试模型旨在涵盖在静态(非变换)几何上正常使用法线贴图的情况。 特别是在依靠查看器生成的切向量的情况下,应该没有办法在其他坐标系中构造模型并声称它是有效的glTF。

我认为这与这个问题密切相关吗? https://github.com/KhronosGroup/glTF-Sample-Models/issues/174

@WestLangley这是到目前为止我发现的东西。 默认情况下,khronos-NormalTangentTest(不包含切线)看起来是错误的:
NormalTangentTest
如果我将normalScale.y设置为-1,那仍然是错误的:
NormalTangentTestNegY
如果我改为将normalScale.x = -1设置为正确:
NormalTangentTestNegX
可能的例外是渲染看起来有点像素化,尤其是在金质反射镜上。 屏幕空间近似值是预期的效果,还是其他错误的指示?

如果有人有其他没有切线的好的测试模型,请发送给我。 我想确保我有一个代表性的样本,并确定是否有不符合glTF规范的样本。

@elalish那个测试平台对我来说没有意义,对我而言从来没有任何意义,也许永远也不会。 所以...尝试解释时,我不会帮您。

那个例子太模棱两可了恕我直言。 我们需要一个连贯的测试用例,以提供足够的视觉效果来了解正在发生的事情。

顺便说一句,我们曾经有一个VertexTangentHelper (#3511),可以将其恢复以支持缓冲区几何。

@WestLangley我希望我知道如何使NormalTangentTest更加清晰。 它不会做任何奇怪的事情,它只是旋转每个样本的方向(在UV空间中)。 这三根色谱柱仅用于测试不同的材料,金柱本身就足够了。 除此之外,我认为测试模型不会变得更简单,仍然可以测试这些不同的UV方向。

尽管UV旋转,当将法线图视为2D图像时,法线图的图像在向量指向的方向上还是非常一致的。 在图像的2D视图中,所有半球的右侧具有较高的红色通道值,而所有半球的顶部边缘均具有较高的绿色通道值,而与UV坐标无关。 这遵循glTF规范,红色(+ X)指向UV空间中纹理的右侧,绿色(+ Y)指向UV空间中纹理的顶部边缘。 不需要提供切线向量就可以完成这项工作:通常,对于给定三角形,+ U轴通常在哪个方向上进行简单的屏幕空间估计就可以计算出切线和双切线向量,就像BabylonJS和CesiumJS那样,当然ThreeJS也可以,但是带有一些无法解释的normalScale轴翻转。

glTF使用flipY标志存在一个已知的混淆点,即在UV空间中反转V坐标,我相信您已经很清楚了。 长期以来,我一直怀疑这在无法解释的normalScale翻转中起作用。

图像中还有一个很小的烘烤错误(我的错,几年前我当时是Substance Painter的新用户)。 特别是在金半球的平坦部分,可以看到轻微的对角线接缝。 但这不应该损害整体效果。 烘烤的主要圆形部分效果很好,并且很好地展示了“正常”纹理在整个2D图像上“向上”和“向右”的效果恒定,而与3D或UV坐标沿所有不同方向旋转以及切线无关被省略。

这对于美术师在创建3D几何图形之前创作重复法线贴图纹理具有重要作用,因为美术师无需在制作纹理图像之前访问UV或切线坐标。

@WestLangley我喜欢这个测试,只是因为它是我见过的最好的,又是唯一的。 我很乐意使用我指向的任何其他测试模型。

此外,该图还会变厚:原始测试是双面材料。 由于#17804,我决定尝试单面使用。 默认情况下还是错误的,但是现在当我设置normalScale.y = -1时,我得到了正确的答案:
NormalTangentTestFrontNegY
至少我希望我们可以同意,更改doubleSided应该不会影响正面的渲染吗?

@emackey我认为另一个有用的测试模型将是带有焊接法线的闪亮立方体,以及一个相应的法线贴图,旨在使其变为立方体形而不是圆形。 可能有两个版本,一个带有和不带有切线。 不幸的是,我缺乏自己编写技术的能力。 如果您能为我们提供帮助,我所能提供的非常感谢:祈祷:

@emackey感谢您为此所做的所有工作。 我非常感谢。 我确实了解艺术家可能会遇到的所有各种问题,以及必须克服的问题。

也许您愿意使用@elalish并提供他要求的任何glb模型。 我希望这将有助于我们理解。

@elalish我们之前已经解决了这些问题。 在某一时刻,我给人的印象是一切正常。 如果有一个简单的测试用例,我可以使用bisect来确定发生问题的地方。

带有焊接法线的闪亮立方体,并设计有相应的法线贴图,以使其变为立方体形而不是圆形。

完成: normal_map_flat.zip 。 我以Draco团队编写的切线为例,并创建了一个没有切线属性的附加版本。

在某一时刻,我给人的印象是一切正常。

我认为我们已经达到了一切正常的状态-如果模型包含切线。 如果不是这样,通常情况就可以了,但是可能会有一些边缘情况,尤其是在UV接缝周围。 在这种情况下,我们建议用户添加切线。 或者,就像https://github.com/mrdoob/three.js/issues/11438#issuecomment -507027586中提到的@emackey一样,也许只有在网格物体覆盖的情况下,才应该恢复GLTFLoader中的normalScale.y *= -1翻转省略顶点切线。

@donmccurdy谢谢。 我今天没有时间处理这个问题,但是我仍然在为扁平的立方体侧面挣扎。 如果将模型编辑为具有光亮的表面( metallicFactor: 1, roughnessFactor: 0.1 ),您会看到立方体侧面的反射会四处游动。

我在自己的测试模型中看到了相同的效果。 更糟糕的是,如果我在Substance Painter中进行正常烘焙与在Blender Cycles中进行烘焙,则效果不同。 每个程序都对其正确的视口进行烘焙,但是即使在没有混合glTF的情况下,将其加载到另一个程序中时也不完美。 它非常接近,但是在如此平坦的表面上,最小的差异将其变成了游乐园镜子,而且效果不佳。

我认为在SP,Blender和各种WebGL引擎中,法线向量(或TBN空间)在三角形的顶点之间插值的方式之间存在细微的差异。 我使用Gimp进行SP烘焙与Blender烘焙的“差异”,结果在每个顶点都显示黑色(相同的RGB值),但是顶点之间的空间显示出非常微弱的强度的旋涡差异。

我希望可以得到微妙的游乐效果,但不是我目前所能获得的效果(无切线版本):
image
with tangents版本实际上并没有更好,这使我想知道这是否真的是有效的glTF:
image
@emackey您是否有资格告诉我们提供的这些模型@donmccurdy中的一个或两个是否实际上是有效的glTF? 如果是的话,我认为我们有一个大问题。

这个特殊的立方体是一个极端的例子,我真的不知道该说些什么,或者我不知道物理上真实的反射是否值得期待。 法线贴图用于添加诸如凹凸和凹痕之类的细节,而不用于使网格的整个表面变形。 恰好是测试切线是否正确读取或生成的非常有效的方法。 我确实知道normalScale.y *= -1更改永远不足以解决此问题; 这是促使我们首先支持存储切线的示例之一。

在BabylonJS中,反射也看起来很疯狂:
Screen Shot 2019-10-23 at 4 00 02 PM

如果唯一识别出的问题是NormalTangentTest,并且没有用户提出在实践中错误渲染的模型,也许我们应该在这里推迟进行更改吗?

我仍然可以恢复normalScale.y *= -1乘数,但是缺少问题的例子似乎表明问题很小,而不是表明我们需要创建这样的极端例子。

如果唯一识别出的问题是NormalTangentTest,并且没有用户提出在实践中错误渲染的模型,也许我们应该在这里推迟进行更改吗?

更正: https :

此页面是否有帮助?
0 / 5 - 0 等级