应用于 Text 实例的自定义着色器可以实现一些非常好的动画效果。 对于其中许多,您希望独立处理每个字符/字形,为此您需要在着色器中提供一些信息来告诉您当前正在渲染哪个字符。
理论上gl_InstanceID
可以用于此,因为我们对字形四边形使用实例化。 而且这种有点有效: https ://codesandbox.io/s/zealous-water-m8lzq?file=/src/index.js - 但gl_InstanceID
仅在 WebGL2 中可用,而且似乎在void main
以外的函数中使用时在 ANGLE 实现中被破坏。 所以它现在并不实际可用。
相反,我们可以添加我们自己的实例属性,例如attribute float charIndex;
,它只包含一个递增的字符索引。 然后自定义着色器可以利用它。
我可能想让它成为一个可选功能,比如textmesh.includeCharIndexInShader = true
,只是为了避免在不需要时创建额外的属性数组。
也可能: wordIndex
, lineIndex
...?
word 和 line 将启用每行和每字动画,就像https://greensock.com/splittext/ (我们可以考虑一个很好的例子来说明这种变化将启用)
我是否正确假设这也将允许片段着色器中的每个标记(字符、单词、行)更改?
我是否正确假设这也将允许片段着色器中的每个标记(字符、单词、行)更改?
您必须将它作为变量从顶点传递到片段,但是是的。 :)
我们去兔子洞……
我可以想到以下所有索引的用途,以及每个索引的总数:
我们想让所有这些选择加入并做一些智能打包以尽量减少我们引入的新 glsl 属性的数量。 我将不得不为此考虑更多的API。
我们可以让用户选择所有带有标志的,所以
{
拆分:{ 单词:true,字符:true,行:true }
}
做一些聪明的包装
介意扩展这个吗? 我很好奇👐
我喜欢让用户选择拆分的想法。
通过打包我只是意味着我们不想添加 12 个新的attribute float foo
声明,否则我们将达到限制(我认为 webgl 中总共有 16 个属性),但我们最多可以将它们打包成 3 个新的attribute vec4 foo
声明,然后将它们解压缩为着色器中看起来更好看的float
变量。
哦,是的,很有道理,谢谢!
charIndex
是否应该在其递增中包含空格? 或者只是增加可见字形? 我倾向于只看到可见的字形。
在按索引制作动画时,计算空格会产生奇怪的交错,所以我也倾向于可见
我有很多清理工作要做,但我有上述计数的初始 POC。 以下是使用各种索引/总数对来更改片段着色器中颜色的示例:
charIndex / totalChars:
charInWordIndex / totalCharsInWord:
charInLineIndex / totalCharsInLine:
wordIndex / totalWords:(在此示例中看起来与 charIndex/totalChars 非常相似,但有细微差别)
wordInLineIndex / totalWordsInLine:
lineIndex / totalLines:
我设法将所有数据放入最多 3 个制服 + 2 个属性中,这非常好。 我仍然想让它们成为可选的。
惊人的! 当你准备好测试版本时让我知道😄
我正在寻找一个可以解决的功能。 我有一堆 3 个字母的标签要放在一个球体周围。 我让它们始终面向观察者并进行调整,以使它们在屏幕上始终具有相同的大小,无论在视图矩阵或建模矩阵中应用的缩放比例如何。
现在,我为每个标签创建一个Text
对象并应用转换。
我在想我可以通过将所有标签打包在一个文本中来获得更好的性能,并有一个属性来让每个标签的球体上的位置在顶点着色器中进行正确的转换。
@lojjic你能记录下如何创建自定义着色器吗? 我认为添加属性应该不难,因为 BufferGeometry 的现有方法setAttribute
BufferGeometry
应该这样做。
我正在考虑的另一个选项是实例渲染,因为我所有的标签都有 3 个字母。 但这肯定需要 WebGL 2 并且可能太复杂以至于不值得,至少在开始时是这样。
@FunMiles我认为你是对的,这可能会促进这种事情。 如果我理解正确,优化可能涉及两个部分:
对于 1,请查看此评论,这实际上可能已经足以满足您的需求。 这也是如何应用自定义着色器的一个很好的演示,基本上只是将其分配为material
。 我在那里使用了 Troika 的createDerivedMaterial实用程序,但您可以类似地传递任何ShaderMaterial
或具有自己的onBeforeCompile
修改的材料。
对于 2,我认为您是对的,您可以渲染单个文本,然后使用 _some_ 属性来置换球体周围的单个字符。 本期中描述的新字符/单词索引属性可能会有所帮助,但我不确定它们是否必要。 您可能只需将这些位移编码到一个新的 InstancedBufferAttribute 中,其中每个向量都保存一个字符的位移(文本的几何图形是一个简单的四边形,为每个字形实例化,因此将为每个字形逐步执行额外的 InstancedBufferAttributes。)
我有兴趣看看你是否有运气这样做!
@lojjic您理解正确。 您链接的代码完成了我需要 (1) 的 90%。 你的演示出奇地符合我的用例。
如果我可以要求您纠正我的理解,以下是我如何看待您在做什么:
mvPosition
使用模型视图空间中的参考点位置进行初始化。 我假设该参考点是锚点。 那正确吗?position
是从该参考点的偏移量,我认为它只有 x 和 y 非零坐标。 (这让我觉得你可以跳过计算缩放的 z 分量?)(PS:查看实例渲染顶点着色器代码,似乎可以旋转文本,在这种情况下 z 分量不会为零)position
,如果 z 分量为零,则此贡献将位置保持在与投影平面平行的平面中。我对这段代码所做的更改(1)主要是缩放和轻微的坐标偏移,以便文本保持在我拥有的球体之外。
你也明白了2。 但是在阅读代码时我有点困惑。 我没有意识到文本渲染已经是实例渲染。 让我感到困惑的是,您确实有GlyphsGeometry
派生自InstancedBufferAttribute
,但Text
是Mesh
的子类,而不是InstancedMesh
的子类。 我想我必须更深入地研究 Three.js。
否则,假设每个字符都被实例化,我只需要一个带有向量的属性来为每个字形实例偏移position
。 那正确吗?
我不得不暂时离开这个,但这里有一个快速的状态更新:
PR #109 在收集和暴露各种计数方面感觉非常可靠。 我想让他们选择加入,但除此之外我对它的位置感到满意。
但是,我有一种强烈的预感,基于着色器的动画不仅需要这些新计数,还可能需要访问其他一些数据,例如:
其中一些在技术上已经存在于着色器中,但如果用户要依赖它们,那么它们需要以友好的名称公开并记录为可靠的合同。
如果有人有时间玩那个 PR 分支并尝试实现一些着色器动画,并让我知道缺少哪些信息,那将是一个很大的帮助。
@lojjic只是想表达我对这个功能的兴趣😺
我在这里解决了与最新大师的一些合并冲突: https ://github.com/canadaduane/troika/tree/char-indices
我还没有机会测试它,但打算在接下来的几天内测试它。
我已经离开这个讨论很长时间了。 然而,我只是被提醒我想回去看看它。
在这篇文章的末尾是我使用createDerivedMaterial
完成的一个示例。
然而,现在,每个标签只有一个网格,当标签数量变得非常大时,低端手机上的渲染会变得非常不稳定。 我假设这是由于每个标签的 CPU 调用。 为了解决这个问题,我想用一个带有所有标签的网格替换多个网格,并使用改进的顶点着色器来完成剩下的工作。
我认为,正如@lojjic 所提到的,我需要对这些词进行一些限制。 或者至少,就我而言,每个单词的中心。
关于如何以其他方式进行操作或如何获得该中心的任何其他建议?
最有用的评论
我有很多清理工作要做,但我有上述计数的初始 POC。 以下是使用各种索引/总数对来更改片段着色器中颜色的示例:
charIndex / totalChars:
charInWordIndex / totalCharsInWord:
charInLineIndex / totalCharsInLine:
wordIndex / totalWords:(在此示例中看起来与 charIndex/totalChars 非常相似,但有细微差别)
wordInLineIndex / totalWordsInLine:
lineIndex / totalLines:
我设法将所有数据放入最多 3 个制服 + 2 个属性中,这非常好。 我仍然想让它们成为可选的。