Three.js: UV偏移/重复应该是材料的一部分而不是纹理

创建于 2015-01-10  ·  73评论  ·  资料来源: mrdoob/three.js

UV 偏移和重复(可能还有一些其他纹理属性)是统一的,它们奇怪地从纹理传递到材质中,这会导致您必须克隆每个材质的纹理(浪费大量内存)的问题,如果您想要每个- 材质偏移/重复,或滚动您自己的自定义着色器。 强迫人们这样做是非常低效的,最好的情况是如果有人想根据纹理应用到的对象的大小在表面上平铺纹理,或者使用纹理作为动画的精灵表。 我只能将当前系统视为没有真正优势的障碍,因为在“共享”纹理上需要共享 UV 偏移/重复的可能性很低,并且通常通过共享材质会更好地服务。 无论如何,从纹理更新制服是很奇怪的,因为它真的没有成为纹理的一部分,最终会让最终用户感到困惑。 例如,当多个贴图应用于材质时更改偏移/重复,例如漫反射+法线贴图,仅使用漫反射贴图的偏移/重复,因此法线贴图偏移/重复的值在该上下文中完全无用,当它真的暗示它应该影响法线贴图偏移/重复时。 这一切都令人困惑。

我很确定“refreshUniformsCommon”函数中需要进行更改,但可能还有更多内容。 精灵插件也需要一些更改。 这可能会破坏很多人的项目,但它在纹理/材质 API 中存在很大的不一致。 将其设置为材质通常为 null 的覆盖可能是一个更好的主意,并且当 set 忽略纹理中的值时,这样就不会破坏每个人的东西。

Suggestion

最有用的评论

这个线程对我来说是 TL;DR。 但我刚刚在 r68 中发现了这段代码(旧项目,是的):

        // uv repeat and offset setting priorities
        //  1. color map
        //  2. specular map
        //  3. normal map
        //  4. bump map
        //  5. alpha map

        var uvScaleMap;

        if ( material.map ) {

            uvScaleMap = material.map;

        } else if ( material.specularMap ) {

            uvScaleMap = material.specularMap;

        } else if ( material.normalMap ) {

            uvScaleMap = material.normalMap;

        } else if ( material.bumpMap ) {

            uvScaleMap = material.bumpMap;

        } else if ( material.alphaMap ) {

            uvScaleMap = material.alphaMap;

        }

        if ( uvScaleMap !== undefined ) {

            var offset = uvScaleMap.offset;
            var repeat = uvScaleMap.repeat;

            uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );

        }

是的...

如果您打算对库提出根本性的更改,例如您在此处提出的更改,则必须为该更改提供清晰且令人信服的论据

我试图在漫反射和法线贴图上设置不同的重复,然后把头发拉出来,因为它不起作用。 由于 API 将此设置置于纹理中,我认为我可以做到这一点。 所以是的,如何保存我的头发以进行令人信服的论证? 这张票仍然是开放的,3js 在纹理上仍然有这个设置,并且只对其中一个纹理有效 = 这实际上已经是每个材料的设置。

如果你不打算把它移到它所属的地方,至少说它在文档中没有影响?

所有73条评论

相关帖子: https :

你必须克隆每个材质的纹理(浪费大量内存)

在什么意义上浪费了大量内存?

在“共享”纹理上需要共享 UV 偏移/重复的可能性很低

你是什​​么意思?

如果我们保持当前的方法,需要改变的一件事是,当克隆纹理时,共享的图像应该只传递给 GPU 一次。

如果您有一个包含大量帧/表的对象,则会创建许多无用的对象,并且正如您所说,图像被多次复制到 GPU 上,这是非常浪费的。 更浪费的是,如果您有多个对象需要位于不同动画的不同点,则必须为每个独特的材质/对象创建一组独特的纹理,因此处理起来很快就会变得烦人,而材质统一可以更容易操作,而无需这些额外的纹理偏移/重复数据结构围绕一个经过深思熟虑的 API 构建,只是为了让它工作。 在我的应用程序的某个用例中,我不得不在任何地方创建这些独特的纹理组,我的应用程序出现了重大放缓,并且感觉唯一的解决方案是用着色器材料替换我使用的所有库存材料来解决这个单一问题,或者 fork 我的 THREE.js 版本并进行必要的修改。

关于第二个说法:

我的意思是,当前的范例仅在您想要应用具有相同 UV 偏移/重复的相同纹理的情况下才能促进工作,但这通常是一种罕见的情况,因为您似乎更有可能想要像其他人一样定义它制服,每种材料,并共享一种材料而不仅仅是纹理。

当前系统的主要警告。 是偏移/重复仅作为影响着色器中所有纹理的单一统一真正存在,但它附加到 THREE.Texture 的事实意味着它不能正确用作统一并愚弄人们认为偏移/重复可以为可应用于库存材质的各种纹理选择不同的 .(即,您可以定义不同的漫反射偏移和法线贴图偏移,但这实际上是不可能的,即使它们可以在不同的纹理上进行唯一设置)。 我可以看到它可能是画布渲染器的残余,但它对 webGLrender / GLSL 材质系统毫无意义,它在很大程度上篡夺了它。

mrdoob 表示,它需要采用以下形式作为反对每个材料的理由,

材质贴图
material.mapOffset
material.map重复
材质.env
material.envOffset
material.env 重复
... 等等。

但同样,即使现在,偏移量/重复也不能按地图类型工作,所以这不是一个真正有效的论点。 无论如何,每个贴图类型都会浪费太多制服(更不用说你通常只使用一组 UV,所以你不会真的有多个偏移量用于法线贴图/凹凸贴图/漫反射贴图/镜面反射贴图)。 我认为如果你权衡所有的选择合理,它真的应该是一个材料属性而不是纹理属性。 我真的不觉得当前系统有任何优势。 我觉得通过 UV 偏移对材质进行动画处理是根本拥有该属性的主要原因,甚至无法正确使用该属性。 更不用说如果您不需要那些制服,它们可以从着色器编译中省略,而现在只要地图存在,它们就存在。

@QuaziKb 恕我直言,如果您打算对库提出根本性更改,例如您在此处提出的更改,则必须为该更改提供清晰且令人信服的论据。 从您的特定应用程序的参考框架进行争论并不会削减它。

话虽如此,我也希望看到偏移/重复从Texture移到材料上。

我相信假设以下地图具有相同的偏移/重复值是合理的:

specularMap
normalMap
bumpMap
alphaMap
aoMap (future)
glossMap (future)
displacementMap (future)

唯一的例外是


这是它现在的实施方式; 即使每个地图都有自己的偏移/重复值,它们也不受尊重。

所以,我们可以添加到MeshPhongMaterialMeshLambertMaterialMeshBasicMaterialSpriteMaterial ,我们会添加

mapOffset // or mapTranslate
mapRepeat // or mapScale

我们将从offset删除repeat Texture

通过这种方式,实现一个精灵表是直接的。 每个精灵都有一个材质,这些材质共享一个纹理。 纹理有图像。 不再需要克隆纹理。 在 GPU 方面,材质将共享一个着色器程序。

恕我直言,这是一个更自然的 API。

我试图回忆为什么 API 是这样实现的。 我希望有一个很好的理由。

抱歉,这并不是说它特别适合我的示例,只是当前的 api 在用于为每个唯一对象/材料制作精灵表/偏移量动画时会导致显着膨胀,而没有真正的解决方案。 自然地,将其作为统一而不是简单地将偏移/重复烘焙到模型的顶点 UV 中的主要目的是为偏移设置动画,我建议这不能在不绕过 API 的情况下完成,使统一贴在纹理上的用处比贴在材质上的用处要小得多。

我相信假设以下地图具有相同的偏移/重复值是合理的:
地图
...
阿尔法地图

FWIW,此时此刻,这种行为(当前的实现)让我感到非常沮丧。 我试图通过独立于其漫反射贴图(保持固定)对网格材质的 alphaMap 偏移进行补间来为网格的显示设置动画。 经过一番沮丧之后,我通过https://github.com/mrdoob/three.js/pull/4987和这个错误发现这是不可能的。

@jcarpenter您所指的“错误”是什么? 您有什么改进建议?

更正:“错误”是指这个问题。 由于在 Bugzilla 文化中时间过长而造成的混淆。 :p 我明白这不是错误,而是预期的行为。

WRT 改进,根据我使用传统 3D 内容创建应用程序(如 Cinema 4D)的经验,我想用户能够:

  • 为材质中的每个纹理定义偏移/缩放/重复,独立于其他纹理,_and_
  • 定义父材质的偏移/缩放/重复

或者,对于我正在处理的用例(“尝试为网格的显示设置动画”),如果 wrapS 和 wrapT 有一个完全不包裹的选项,那就太棒了。 根据来自 Cinema4D 的附加 GIF,它可以选择完全禁用平铺以用于 UVW 贴图。 基于对现有 wrapS 和 wrapT 方法的相当广泛的测试,这样的事情是不可能的。 这意味着我在three.js中为项目显示设置动画的选项似乎仅限于整个网格的补间位置和不透明度......除非我遗漏了什么。

c4d-tile-2

同意。 计划是简化所有这些(在着色器中使用单个矩阵3)并为每个纹理设置偏移/重复(或平移/缩放)。

任何想帮助解决这个问题的人都将不胜感激!

@jcarpenter

可悲的是,现在唯一的方法是使用具有不同材料的多个网格或自定义着色器材料。 对于刚开始使用 Three.js 工作流的用户来说,这两者都有些涉及。

在可用性、性能和可扩展性之间有一个不断的权衡。 我的建议是重写当前 api 中纹理和材质的工作方式,以便纹理是您插入的严格参数,并且着色器中实际统一的值(如偏移/重复/包裹模式)专门链接到材料。 在某些情况下,您不希望将统一浪费在控制 UV 以用于永远不需要更改的纹理上(如果只需要漫反射,那么在 phong 材质中拥有 4*5 个额外的统一将是巨大的浪费),所以如果在幕后有一些魔法检测是否需要特定的 UV 并重建纹理以满足这些需求,或者如果可以选择传入一些参数来指定所需的可调整 UV 的数量以及什么,那就太酷了地图他们抵消以及如何抵消,但这是一个难以解决的问题。

计划是简化所有这些(在着色器中使用单个矩阵3)并为每个纹理设置偏移/重复(或平移/缩放)。

@mrdoob

  1. 你的意思是 _per material.map_ 所以纹理变换是在材质中吗? 不幸的是,有很多材质贴图。 我们可以继续假设所有的纹理变换都是相同的,除了光照贴图。
  2. 你也想支持轮换吗? 如果是这样,您还必须添加旋转中心——除非您想将旋转中心硬连线到纹理的中心。

这将是一个非常受欢迎的变化。 必须克隆纹理只是为了将它们置于独特的重复值感觉非常麻烦。 这是其他图书馆的做法吗?

  1. 你的意思是 _per material.map_ 所以纹理变换是在材质中吗? 不幸的是,有很多材质贴图。 我们可以继续假设所有的纹理变换都是相同的,除了光照贴图。

我认为每个地图应该有一个mat3并且应该由Texture属性组成。

  1. 你也想支持轮换吗? 如果是这样,您还必须添加旋转中心——除非您想将旋转中心硬连线到纹理的中心。

旋转是的。 居中与否...我不确定,但据我所知,所有这些都可以编码在着色器的单个mat3中(每张贴图)。

是一个原型,展示了如何将Matrix3传递给着色器并表示由offsetXoffsetYrepeatXrepeatY定义的变换rotationrotationCenterXrotationCenterY

如果center不允许作为一个选项,那么它应该硬连线到( 0.5, 0.5 )

欢迎评论。

编辑:演示更新

rotateuvs

这很棒! :+1: :+1:

我想我会用translationscale (而不是offsetrepeat )。

新的材料特性究竟应该是什么? 有很多材质贴图。

应该从纹理属性中删除什么?

我假设wrapS/T应该保留在纹理上。

我认为texture.offsettexture.repeat应该被删除。

我认为新属性应该是... texture.translationtexture.rotationtexture.scaletexture.centertexture.matrix 。 我们可能还需要一个在渲染时调用的texture.updateMatrix()方法。 当然,即使纹理被重用,这也面临着确保我们只做一次的挑战。

参考这篇文章的标题,以及https://github.com/mrdoob/three.js/issues/5876#issuecomment -69483293 中的论点,我认为重点是将这些属性移动到材料中。

/ping @bhouston征求意见。

我的想法是纹理偏移/重复应该尽可能多地烘焙到 UV 中。 这更容易。 这就是 UE4/Unity 5 在大多数情况下的做法。

唯一的例外是 Unity 5 确实能够为所有纹理共享的每个材质指定一个全局偏移/重复 - 但它不会影响被认为是烘焙贴图的内容,例如 lightMap 或ambientOcclusion 贴图(那些调整没有意义。)

我之所以不提倡这里有很大的灵活性,是因为专业创建的模型具有适当的 UV,这使得这通常不是必需的——内容创建工具有办法解决这个问题。 另一个问题是 WebGL 对 Fragment Uniforms 的下限低得离谱——16 Vec4。 如果你必须在每张地图上有一个重复/偏移,并且我们很快就会得到很多地图,那么我们正在浪费很少价值的碎片制服。

实际上,我最近在 Clara.io 中添加了重复/偏移,然后每个纹理的亮度/增益控制,我将撤消这些更改,因为它会导致低端设备上的 Fragment Uniforms 溢出——例如每个 Apple iOS 设备。 尽管在 NVIDIA 980 台式机上具有重复/偏移和亮度/增益效果很好,但我们必须为每个人设计,而不是为高端机器设计。 ;)

我们必须尊重 Fragment Uniforms,并且只在必要时使用它们。 我相信这不是这些情况之一。

唯一的例外是 Unity 5 确实能够为每种材质指定一个全局偏移/重复

这基本上就是three.js现在正在做的事情——尽管它从漫反射贴图中获取偏移/重复,并且所有其他材质纹理都使用该贴图的设置。

建议是从纹理中删除偏移/重复,并将其添加到材质中——可能会更改名称。 同样,所有材质纹理都将共享相同的设置(即使有些用户不会对此感到满意)。 这将使统一使用率保持在较低水平。

不幸的是,我们在这方面没有达成很多共识。

做 Unity 5 正在做的事情是个好主意。 我也会把它放在材料上而不是纹理上。 你有我的支持。

每个贴图并不是很理想,但可以通过在构建材料时以某种方式指定标志/键来解决,以防万一。

通常,在着色器中修改 UV 的唯一原因是因为您希望它们具有动画效果。 如果您只想静态转换 UV,我们不应该修改着色器来添加此功能。

对着色器的唯一建议更改是替换

vUv = uv * offsetRepeat.zw + offsetRepeat.xy;

vUv = ( uvTransform * vec3( uv, 1 ) ).xy;

好的。 这个怎么样...我们添加texture.dynamic (默认false ),它产生:

vUV = uv;

如果用户将texture.dynamictrue那么我们从texture.translationtexture.rotationtexture.scaletexture.center计算texture.matrix texture.center ,我们将其传递给程序,然后生成:

vUv = ( uvTransform * vec3( uv, 1 ) ).xy;

当然,如果texture.dynamic发生变化,我们需要重新编译程序。

这样我们才能两全其美?

@mrdoob

  1. 在您看来, scale是纹理的属性还是texture.scale是材质的属性? 我希望是后者,因为这就是这个线程的全部内容。
  2. 我们不需要.matrix属性。 新的Matrix3是替换材料统一offsetRepeat统一。 它是根据渲染器中的其他参数计算得出的,并像任何其他制服一样传递给 GPU。
  1. 在您看来, scale是纹理的属性还是texture.scale是材质的属性? 我希望是后者,因为这就是这个线程的全部内容。

我不同意这个线程。 我认为我们不应该用mapMatrixenvMapMatrix 、 ... 也不应该用mapMatrixenvMapMatrix mapTranslationmapRotationmapScale污染材料。 我认为如果THREE.Texturetranslationrotationscale以及可能center更干净。

  1. 我们不需要.matrix属性。 新的Matrix3是替换材料统一offsetRepeat统一。 它是根据渲染器中的其他参数计算得出的,并像任何其他制服一样传递给 GPU。

我们有点需要这样的东西。 假设在不同的贴图中重复使用相同的纹理。 我们不想为每个实例计算Matrix3

我不同意这个线程。 我认为我们不应该使用 mapMatrix、envMapMatrix、...也不应该使用 mapTranslation、mapRotation、mapScale 来污染材料。 我认为如果 THREE.Texture 有平移、旋转、缩放和中心,它会更干净。

是否仍需要克隆纹理以具有不同的重复属性? 在小场景中,这可能没什么大不了的,但在已经有 40 多个纹理的大场景中,这是一场记忆噩梦。

@titansoftime image应该与THREE.Texture 。 理想情况下,一个单独的THREE.Image可以由不同的THREE.Texture每个translationrotation ,...配置。

@titansoftime图像应该与 THREE.Texture 分离。 理想情况下,一个单一的 THREE.Image 可以被不同的 THREE.Texture 使用,每一个都具有不同的平移、旋转、...配置。

那讲得通。 texture.clone() 是否已经以这种方式工作,还是必须手动设置?

texture.clone()就是这样工作的,但是WebGLRenderer无法知道图像是否相同,因此无需上传纹理...

我不同意这个线程。 我认为我们不应该使用 mapMatrix、envMapMatrix、...也不应该使用 mapTranslation、mapRotation、mapScale 来污染材料。 我认为如果 THREE.Texture 有平移、旋转、缩放和中心,它会更干净。

这基本上就是我们现在所做的。 每个纹理都有自己的offset属性,并且不支持单独的偏移量——渲染器对材质的每个贴图使用相同的偏移量。

FWIW,我认为我们应该按照我在https://github.com/mrdoob/three.js/issues/5876#issuecomment -69483293 中所说的去做。

我不明白为什么 API 不允许做@jcarpenter想做的事情(动画alphaMap的偏移量,而map保持不变)。 人们可以在画布中做到这一点,但我认为这是 GPU 的一项任务。

通过使用不同地图的偏移量可以做很多事情......仅偏移alphaMap ,缩放normalMap

每个材料只有一个uvTransform似乎非常有限。

等一下。 我想我明白为什么你们更喜欢每种材料。 这样,在顶点着色器中计算单个 vUv,而不是在片段着色器中计算每个像素的 uv。 对?

每个材质是理想的,因为偏移实际上只是使用统一的材质实现的,与纹理无关,所以如果我们需要更改必须制作的每个材质的统一,我们最终会得到一个无意义的解决方法大量的新对象/纹理(这在 JS 中非常糟糕/在 WebGL 中非常糟糕)只是为了控制某些东西,这实际上只是对用户隐藏的材料制服的一个特征。 将其作为纹理的一部分绝不是有利的、更有效的,甚至更清晰,这意味着指定在运行时发生变化的 UV 的一个实际应用程序,动画,由于 api 渲染速度缓慢且效率低下。

纹理应该指定一个图像,以及与该图像有关的任何内容。 UV偏移与图像/图像属性无关,而与显示纹理的材质有关,如果它不是材质的一部分,即使具有此功能也是无稽之谈,因为每个都需要进行大量克隆例如,如果想在许多情况下实际使用该功能,则可以同时更新许多纹理。

想象一个动画图块的每一帧都有不同的图像,并且还想控制该图块的偏移/重复。 他们必须用偏移量更新每个传入帧,并为使用该图块的每个独特材料实例复制每个帧,它会迅速滚雪球成数百个新对象和额外分配,实际上只是控制无缘无故地使用所有这些对象在幕后为每个材质制作一些浮动。

至于每个地图的转换,虽然这很好,但在大多数情况下,统一成本无缘无故地过高,除非我们有一个复杂的 API 来控制转换的时间和方式应该是唯一的、共享的或动态的,IMO拥有这种控制权将是一件好事,但必须仔细考虑。

等一下。 我想我明白为什么你们更喜欢每种材料。 这样,在顶点着色器中计算单个 vUv,而不是在片段着色器中计算每个像素的 uv。 对?

否。uv 一如既往地在顶点着色器中计算。

想象一个精灵表和 20 个精灵。 目前,我们需要 20 个克隆材质和 20 个克隆纹理——就像在http://threejs.org/examples/misc_ubiquity_test2.html 中一样。 (顺便说一句,请注意我们已经有了SpriteMaterial.rotation 。)

将偏移移至材质,我们需要 20 个克隆材质和一个纹理。

事实上,将偏移量移动到精灵,我们需要 1 个材质和 1 个纹理。

想象一个精灵表和 20 个精灵。 目前,我们需要 20 个克隆材质和 20 个克隆纹理

哦,我没有考虑这个用例。 谢谢!

我会考虑这个...

我主张不要在纹理中进行 UV 变换。

有两个原因:(1)令人困惑,(2)有更多的事件要传播,以及(2)浪费。

混淆:原因是我们对于主贴图只有一个 UV 变量,但是如果在其中一个纹理上有一个 UV 变换,那么这个 UV 变换可能会被应用到所有使用第一个 UV 的贴图上,这是令人困惑的通道,无论其他纹理是否有变换。 如果我们确实允许每个贴图在材质中具有自己的 UV 变换,我会更同意与纹理相关的 UV 变换——但由于其片段统一使用,这很难做到。

更多事件传播:我在 Clara.io 中遇到的另一个问题是尝试为纹理参数设置动画时的事件传播。 需要每个纹理来跟踪使用它的每种材质,然后告诉这些材质它们是脏的,需要重新计算。 这样做并非不可能,只是需要更多的工作。

浪费:另一个问题是,如果您有多个 3D 模型或恶意实例,并且它们都具有相同的动画纹理。 在这种情况下,您必须在内存中拥有不同的纹理副本,才能让它们以不同的方式动画——即使纹理数据本身是相同的。 与将 UV 变换数据放在材质上相比,从这个意义上说,这有点浪费。

因此,如果每个材质只有一个允许的 UV 变换,我会将它放在材质本身上。 我会遵循 Unity 5 模型,它们在材质中具有 UV 偏移和旋转。 游戏开发人员已经熟悉这种方法。

我认为材质上的 UV 变换也可以很好地处理精灵 - 它与上面的 3D 模型案例非常相似。

Having only one uvTransform per material seems very very limiting.

这里完全同意。 没有这个功能是非常有限的。 这里可以提供一些奇妙的效果,但并不是因为每个偏移量都锁定在一起。

但是如何在不阻塞克隆的情况下提供此功能?

我认为在材质上而不是纹理上设置值对于匹配所有纹理的常见用例是有意义的。

这里完全同意。 没有这个功能是非常有限的。 这里可以提供一些奇妙的效果,但并不是因为每个偏移量都锁定在一起。

但是如何在不阻塞克隆的情况下提供此功能?

THREE.ShaderMaterialTHREE.RawShaderMaterial会给你这种能力,并且鉴于它目前不适用于常规材料,这是你无论如何都必须使用的路线。

如果你正在做一些更时髦的事情,它可能会比单独调整贴图重复和偏移更时髦,所以无论如何你可能会倾向于这种方式。

+1 为此,伙计们。

当您有一个巨大的精灵表(又名图集)并希望在多个THREE.Sprite实例上重用它的纹理时,这将非常有用。

有这方面的消息吗? 这个问题仍然是 API 中一个非常明显的缺陷。 大多数地图类型都有未使用的偏移/重复,并且事件传播使平面上的动画精灵之类的东西比它需要的更慢/内存繁重。

当前系统中不存在动画地图的灵活性。 争论不断出现,我们将通过将设置绑定到材料来降低灵活性。 这个论点没有实际意义,因为这种灵活性在当前系统中不存在。 您只能为材质全局设置偏移/重复,并且它取自漫反射贴图(?)。 这会导致一个更糟糕的问题,即在大多数使用的地图上都有多余的“偏移”/“重复”设置,并且每当您想共享动画纹理时都无法共享,则需要进行克隆,因此灵活性非常大减少。 您希望每个纹理/贴图都具有唯一的偏移量,但就目前情况而言这是不可能的,并且在大多数情况下,您实际上需要一组 UV 偏移量,因为将偏移量设置为与正常/规格相同的偏移量会很烦人/diffuse(固定漫反射贴图上的滚动法线贴图是可以使用着色器材质的利基用途)。

如果您查看正在构建的实际着色器,则纹理偏移/重复与材质相关联,但奇怪的是从一张地图上复制出来,这绝不应该受到控制。 材料需要加以控制才能快速而优雅,没有冗余。

使用大量额外标志可以实现两全其美,但我认为这不是一个更好的解决方案,而不仅仅是指导用户尝试使用着色器材料来代替这些特定的边缘情况。

对此的解决方案是@sunag的 NodeMaterial 顺便说一句。

还为此添加了我的 +1! 将使使用精灵表更好。

:+1:我发现这令人惊讶。 我不得不开始为游戏中的每个精灵复制我的纹理,因为我所有重复的精灵都在相互动画。 听起来这意味着我正在向 GPU 加载冗余精灵数据?

有人有解决此问题的方法吗? 是否可以通过直接在着色器上设置统一值来操纵 UV 偏移量,绕过纹理界面?

对此的解决方案是@sunag的 NodeMaterial 顺便说一句。

@bhouston ,你能提供一个链接吗? @sunag的帐户下没有具有该名称的公共存储库。

@rhys-vdw 它仅位于 dev 分支中:

https://github.com/mrdoob/three.js/tree/dev/examples/js/nodes

它是新的,所以我不确定它是否可以用于精灵,但它是一个完全基于图形的着色器系统,因此它可以让您灵活地任意修改纹理访问。

它是新的,所以我不确定它是否可以用于精灵,但它是一个完全基于图形的着色器系统,因此它可以让您灵活地任意修改纹理访问。

@bhouston我可以用 Node 创建一个例子,这看起来不错。

关于THREE.SpriteMaterial您可以访问偏移量/比例来创建spritesheet ,例如:

var offsetX = frameX / mapWidth;
var scaleX = mapWidth / frameWidth;

sprite.material.map.offset.x = offsetX;
sprite.material.map.repeat.x = scaleX;

https://github.com/mrdoob/three.js/blob/master/src/renderers/webgl/plugins/SpritePlugin.js#L53

基于节点的系统不是修复...它仍然没有解决 api 的问题。 将偏移/重复添加到材质原型需要 2 秒,并使渲染器读取它而不是纹理。 我已经为我的项目完成了它,但事实仍然是 api 中的一个明显缺陷,应该正式更改它以防止新用户在尝试做一些常见的事情时会遇到这个问题。

...因此它将为您提供随意修改纹理访问的灵活性。

tbh 我不知道那是什么意思。 在每个材质的基础上为“偏移”和“重复”设置单独的着色器制服动画。

关于 THREE.SpriteMaterial,您可以访问偏移/比例以使用此创建精灵表,例如...

@sunag ,也许问题不清楚。 您共享的代码会改变纹理,该纹理由该材质的所有实例共享。 这意味着不可能让两个材质共享一个纹理,但具有唯一的偏移量 - 例如,两个敌人精灵显示不同的动画帧。

我已经为我的项目完成了它,但事实仍然是 api 中的一个明显缺陷,应该正式更改它以防止新用户在尝试做一些常见的事情时会遇到这个问题。

@QuaziKb是否有可以针对我的项目的 PR?

尽管正如@WestLangley所说,如果以下情况属实,那么整个事情就不会成为问题:

如果我们保持当前的方法,需要改变的一件事是,当克隆纹理时,共享的图像应该只传递给 GPU 一次。

那是对的吗?

@sunag ,也许问题不清楚。 您共享的代码会改变纹理,该纹理由该材质的所有实例共享。 这意味着不可能让两个材质共享一个纹理,但具有唯一的偏移量 - 例如,两个敌人精灵显示不同的动画帧。

嗯,对于这个NodeMaterial肯定会解决的问题,您将能够使用不同的材料和独立的 uv 偏移共享相同的纹理以及诸如自定义过滤器和基于节点的材料可以提供的其他东西的优势。

https://github.com/mrdoob/three.js/issues/7522

但是目前有人试图像这样实例化 uuid:?

THREE.Texture.prototype.createInstance = function() {

    var inst = this.clone();

    inst.uuid = this.uuid;
    inst.version = this.version;

    return inst;

}

如果您使用needsUpdate更新所有实例version

例子:

var uniqueTextureOffset = map.createInstance();
var material = new THREE.SpriteMaterial( { map: uniqueTextureOffset } );

保持相同的uuidversion将在 GPU 上共享texture

https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L2832
https://github.com/mrdoob/three.js/blob/master/src/renderers/webgl/WebGLProperties.js#L11

虽然对我来说这是一个临时解决方案。 我相信未来的NodeMaterial

我们可以将 uv 变换移动到材质吗?

两全其美怎么样。 在材料上添加标志overrideOffsetRepeat在材料上添加新的uvOffsetuvRepeat 。 这样,如果标志为 false,则向后兼容,这是默认设置。 如果为真,则使用材质偏移/重复。 我会支持这一点,因为似乎需求是强烈和广泛的。 @WestLangley? @mrdoob?

(虽然我确实支持在以后的所有事情中使用 NodeMaterial,但切换到它是一种痛苦。)

我仍然认为解决方案是创建THREE.Imagehttps://github.com/mrdoob/three.js/issues/5876#issuecomment -81189892

THREE.Image

@mrdoob ,这是否意味着每个分配的Texture将与Material一起克隆?

@mrdoob写道:

我仍然认为解决方案是创建 THREE.Image

是的,这行得通,虽然它的向后兼容性会稍差一些(如果我们现在要求每个人在创建纹理之前创建图像),但整体设计更简洁。 如果没有单独指定它,也许可以为每个纹理自动创建一个图像,然后实现向后兼容性? 如果你想做一些棘手的事情,你只需要直接操作图像。

我猜这个解决方案需要为一组精灵增加两倍的新对象——每个精灵一个纹理和一个材质? 另一种方法在哪里只需要每个精灵的新材质?

我知道 Unity 支持每个材质的重复/偏移,但是像 3DS Max、Maya、Softimage 这样的高端 VFX 工具不支持——它们只有一个位图节点和一个纹理节点(其中包含位图节点和 UV 映射)点头),这类似于@mrdoob的设计。

UE4 也类似于@mrdoob所提议的,他们有一个“Texture”节点,它是一个位图加载器,然后他们有一系列 UV Mapping 节点来执行各种类型的 UV 映射。

高级工具确实从纹理中拆分了图像/位图,并且它们还以这种形式拆分了一个单独的 UV 映射节点:

 -> Bitmap
 -> UVMapping

我们现在不允许在主 StandardMaterial 中使用太多 UVMapping 选项。 但是在 UE4、Maya、Softimage、3DS Max 中使用的各种类型的 UVMappings 将是使用哪个通道、应用到它的变换、使用世界坐标作为源,或者是否应该进行球面、立方体、平面投影基于这些预测所需的参数。

UE4 有一个精灵纹理,允许在材质中重复/偏移:

https://docs.unrealengine.com/latest/INT/Engine/Paper2D/Sprites/index.html

正如@mrdoob所建议的那样,Maya、Softimage、3DS Max 和 UE4 将位图/图像与纹理(以及 UV 生成)

如果没有单独指定它,也许可以为每个纹理自动创建一个图像,然后实现向后兼容性? 如果你想做一些棘手的事情,你只需要直接操作图像。

没错😊

可能有点不合适,但我想建议尽快合并 PTEX。
http://ptex.us/PtexFile.html
如果有一种方法可以将典型的投影等投射/转换为 NodeTexture (?) ......那么也许这是提前考虑的事情。
[进一步说到同一点:]
ptex 的概念并不是真正的 2d 图像,而是 uv 关系,因此您可以在复杂的表面周围绘画/标记/投影,而无需考虑如何将 2d 转换为 3d 的概念/挑战,相比之下,这在数学上充其量是一个黑客(总是在技术上与破坏作斗争)。
我只是指出 ptex 应该更有意义,并且在 20 多年前成为优先事项,不应被视为扩展或二流表演者,但对我来说,这恰恰相反。 它应该是声明性地说明如何将 2d 图像投影/标记到一个真正始终运行的 ptex 基础级别系统中的旧原始方式。 无论如何,只是执行这个想法,如果不是最好采取更集中的滚动,它应该被整合。
感谢有机会提出崇高的建议。 我很高兴 ptex 是按照规范制作的。 我自己在 10 多年前就想到了这一点,但作为一个孩子,我没有能力定义新的规格等。事后看来,我应该看到,也许我可以尝试做出改变,而不是我仍然坚持的观察者名单。 因此,这里试图消除长期存在的错误。

如果有人对 Flux 中潜在的当前方法有更深入的了解,可以提出一个更适用的建议,说明它在 THREEjs 中的工作方式,那么也许有人可以开始一个新线程。
再次感谢有机会听到。

@MasterJames有点跑题了……请创建一个新线程?

即使我们有图像以便“纹理”仍然可以使用,所有材料都需要重写,因为它们实际上不支持超过一个 uv 偏移/重复。 显然这可能会改变,但最终可能会增加复杂性,因为那时将需要冗余的制服(你多久需要一组以上的偏移/重复,以便例如法线贴图从漫反射中偏移)我认为最后对于性能非常重要的网络,最常见的用例是有一个全局偏移/重复会影响每个贴图,并且将其放在材质上才有意义,因为它最终成为材质架构的一部分。 自定义着色器材料可以很好地处理边缘情况。

@QuaziKb 是的。 这就是NodeMaterial解决的问题。

是不是Texture为每个.clone()使用相同的OpenGL 纹理实例,或者我错过了什么。 它实际上是否为每个克隆重新上传它? 如果是这样,那么这是一个_非常_严重的问题。

@evrimoztamur ,我相信它每次都会复制纹理。 您可以使用WebGL Inspector查看发生了什么。

我尝试了我能想到的所有方法,包括@sunag解决方法,但没有任何效果。 基于我没有产生结果的实验​​以及上面的讨论,我查看了其他库如何处理精灵动画。 我发现 Babylon.js 的SpriteSpriteManager API 是满足我特定需求的解决方案。 它处理精灵表、偏移和重复以及动画。 也许这比 THREE.js 旨在提供的抽象级别更高,但作为参考可能值得一看。

@rhys-vdw:对于当前的项目,我最终得到了 MeshBasicMaterial 的混蛋版本:
https://gist.github.com/karimbeyrouti/790d2e1a8c0137b16bae

当您设置地图时,这会自动分配偏移/重复制服(隐藏在材料中)。 您可以轻松地单独设置它们 - 这将阻止您现在需要克隆纹理。 (使用 r73)

我刚刚提交了一个应该解决这个问题的 PR,PR #8278

@WestLangley写道:

我相信假设以下地图具有相同的偏移/重复值是合理的:

地图
高光贴图
法线贴图
凹凸贴图
阿尔法地图
aoMap(未来)
光泽图(未来)
置换贴图(未来)

不总是。 我目前正在对同一材料(沥青)上的贴图和凹凸贴图使用不同的重复值来隐藏两者都用相当小的瓷砖平铺的事实。 这样,我不需要生成/拥有大纹理。 非常方便。 :-)

编辑:嗯,这至少是我认为我所做的。 这个技巧可能被忽略了。 我可以通过在着色器中添加噪声或其他东西来获得类似的结果。 WestLangley 的 Matrix3 演示很酷。

我认为这解决了 Sprite 中具有不同 UV 的实例的问题。 可以修改vertexpixel shader保留相同的纹理。

https://threejs.org/examples/#webgl_sprites_nodes

它使用SpriteNodeMaterialMesh以及共享的PlaneBufferGeometry 。 该接口不适用于Sprite但可以工作。 也许它可以演变为SpriteMesh使界面更友好

这个线程对我来说是 TL;DR。 但我刚刚在 r68 中发现了这段代码(旧项目,是的):

        // uv repeat and offset setting priorities
        //  1. color map
        //  2. specular map
        //  3. normal map
        //  4. bump map
        //  5. alpha map

        var uvScaleMap;

        if ( material.map ) {

            uvScaleMap = material.map;

        } else if ( material.specularMap ) {

            uvScaleMap = material.specularMap;

        } else if ( material.normalMap ) {

            uvScaleMap = material.normalMap;

        } else if ( material.bumpMap ) {

            uvScaleMap = material.bumpMap;

        } else if ( material.alphaMap ) {

            uvScaleMap = material.alphaMap;

        }

        if ( uvScaleMap !== undefined ) {

            var offset = uvScaleMap.offset;
            var repeat = uvScaleMap.repeat;

            uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );

        }

是的...

如果您打算对库提出根本性的更改,例如您在此处提出的更改,则必须为该更改提供清晰且令人信服的论据

我试图在漫反射和法线贴图上设置不同的重复,然后把头发拉出来,因为它不起作用。 由于 API 将此设置置于纹理中,我认为我可以做到这一点。 所以是的,如何保存我的头发以进行令人信服的论证? 这张票仍然是开放的,3js 在纹理上仍然有这个设置,并且只对其中一个纹理有效 = 这实际上已经是每个材料的设置。

如果你不打算把它移到它所属的地方,至少说它在文档中没有影响?

@sunag公关:
https://github.com/mrdoob/three.js/pull/11531

我遇到的一个问题:
https://jsfiddle.net/f0j2v3s8/

似乎SpriteNodeMaterial透明度丢失了,我无法使用混合组合来使此示例正常工作。

有任何想法吗?

@karimbeyrouti写道:

当您设置地图时,这会自动分配偏移/重复制服(隐藏在材料中)。 您可以轻松地单独设置它们 - 这将阻止您现在需要克隆纹理。 (使用 r73)

我相信这已经过时了,因为uniforms.offsetRepeat已更改为uniforms.uvTransform (r88)。

关于“重用具有多个 Object3D 实例的纹理图集”用例,我建议简单走一圈:

  1. 将 UV 数据(偏移量、重复)存储在 atlas json 对象中;
  2. 钩住Object3D的onBeforeRender\onAfterRender函数对;
  3. 在渲染前回调中,从 atlas json 对象加载 UVs 数据并设置为 material.map;
  4. 在渲染后回调中,将其重置;

这将导致:

  1. 只有一个 Texture & 一个 Material 被多个对象共享,不需要 Clone 并且 info.memory.textures 计数器不会增加;
  2. 但仍然所有其他贴图(法线、AO、位移...)必须符合相同的 UV 转换;
此页面是否有帮助?
0 / 5 - 0 等级

相关问题

yqrashawn picture yqrashawn  ·  3评论

Horray picture Horray  ·  3评论

danieljack picture danieljack  ·  3评论

clawconduce picture clawconduce  ·  3评论

boyravikumar picture boyravikumar  ·  3评论