关于三维图形语义的思考

我设计软件的中心问题总是

这是什么意思?

对于函数式编程,这个问题特别明确。对于我定义的每个数据类型,我都希望有一个精确而简单的数学模型。(例如,我的行为模型是时间函数,我的图像模型是2D空间函数。)类型上的每一个操作也都被赋予了该语义模型的含义。

这个规范过程是应用于数据类型的指称语义,它为

  • 实施的正确性,
  • 无实施细节的用户文档,
  • 生成和证明属性,然后可用于自动测试,以及
  • 评估和比较设计决策的优雅性和表现力。

有关示例(2D图像)、此过程的一些动机和讨论,请参阅Luke Palmer的帖子语义设计另请参阅我关于类型类态射为指称设计提供了附加结构。

2008年春天,我开始开发一个功能强大的3D库,现场跳闸我以前设计过功能性3D库,作为TBAG公司活动VRML、和弗兰出于上述所有原因,这次我想要一个基于语义的设计。一如既往,我想要一个

  • 简单,
  • 优雅,并且
  • 概述。

对于3D,我还希望该模型对GPU友好,即在(现代)GPU上执行良好,并允许使用其功能。

我没有想到或听到过一个我喜欢的模型,因此我在FieldTrip的工作中没有那种坚定的立场。去年二月,我想到了这样一个模型。从那时起,我就有了这篇博客文章。最近,我再次专注于基于GPU的功能3D渲染,然后是Sean McDirmid提出了类似的问题,这让我再次思考。

几何图形

3D图形涉及多种概念。让我们从3D几何开始,使用表面(而不是固体)模型。

3D(曲面)几何图形的示例包括

  • 实体长方体、球体或圆环体的边界(表面),
  • 实心三角形、矩形或圆形,
  • 几何图形集合,以及
  • 几何的空间变换。

第一个模型:几何图元集

一个几何模型是一组几何图元。在这个模型中,联盟表示集合并集,空间变换表示变换集合中所有图元中的所有3D点。基本体包含无穷多(甚至不可数)点,因此需要进行大量变换。幸运的是,我们谈论的是什么(语义),而不是如何(实现)。

什么是几何基本体?

我们可以说它是一个三角形,由三个坐标指定。毕竟,计算机图形学将一切简化为一组三角形。哦,我们混淆了语义和实现。细分(Tessellation)近似值曲面由一组三角形组成,但在此过程中会丢失信息。我想要一个故事,包括这个近似过程,但要保持它与语义上理想的曲面有明显区别。然后,用户可以使用理想的、简单的语义,并依靠实现来执行智能的、动态的、与视图相关的细分,以适应可用的硬件资源。

几何图元的另一个模型是从二维空间到三维空间的函数,即曲面的“参数化”表示。除了函数之外,我们可能还需要一些方法来描述定义曲面的2D子集,以便修剪曲面。简单的形式化是

类型Surf=R2->可能是R3

哪里

类型R—实数类型R2=(R,R)R3型=(R、R、R)

对于着色,我们还需要法线,可能还需要切线和双切线,我们可以通过包含导数来获得这些特性以及更多特性,无论是一阶导数还是所有导数。查看我的衍生品帖子和纸张美丽的差异化

除了位置和导数之外,基本体上的每个点还具有材质属性,这些属性决定了光在该点处如何被曲面反射和透射。

类型Surf=R2->可能(R2:>R3,材质)

哪里a:>b包含类型为的函数的某一点的所有导数(包括零)a->b请参见高维、高阶导数,函数我们也许还可以包括材料性质的衍生物:

类型Surf=R2:~>可能(R3,材质)

哪里甲:~>乙是无穷可微函数的类型。

组合几何图形值

这个联盟函数提供了一种组合两个几何体值的方法。另一个是位置和材质属性的变形(插值)。变形的语义是什么?

两人之间的变形曲面更容易定义。曲面是一个函数,所以我们可以插值指向(point-wise):给定曲面,对于每个点第页在参数空间中,在(a)之间插值第页和(b)第页,这就是提升A2(关于功能)会建议。

这个定义有效如果我们有一种方法可以在也许 吧值。如果我们使用提升A2再一次,现在开始也许 吧值,然后只是/没有什么(和没有什么/只是)案例将产生没有什么这种语义是可取的吗?例如,考虑一个中间有孔的方形平面。一个正方形有一个小洞,另一个有一个大洞。如果孔的大小对应于映射到的参数空间部分的大小没有什么则点式插值将始终生成较大的孔,而不是在孔大小之间进行插值。另一方面,有孔的两个表面可能只是在完全相同的一组参数上,函数确定只是空间被拉伸。

描述这种变形笨拙的一种方法是,这两个函数(曲面)可能具有不同的域这种解释来自于观察a->也许b作为对来自子集属于(即a部分上的函数).

即使我们有一种令人满意的方法来组合曲面(点式),我们如何将其扩展到组合可以包含任意数量曲面的完整几何体值?一个想法是将几何图形建模为结构化的表面的集合,例如列表。然后我们可以将这些集合元素组合在一起。同样,我们必须处理集合不匹配的可能性。

曲面元组

让我们简单地回到一个更简单的曲面模型:

类型Surf=R2->R3

我们可以将此类曲面的集合表示为结构化集合,例如列表:

type Geometry=[冲浪]

但是,该类型不会捕获曲面的数量,从而导致在逐点组合几何体值时出现不匹配。

或者,我们可以通过元组(可能是嵌套的)在类型中显式显示曲面的数量。例如,两个曲面的类型(冲浪,冲浪)

该模型中的插值变得非常简单。通用插值器适用于向量空间:

lerp::VectorSpace v=>v->v->标量v->vlerp a b t=a^+^t*^(b^-^a)

或在仿射空间上:

alerp::(AffineSpace p,VectorSpace(Diff p))=>p->p->标量(Diff p)->palerp p p’s=p.+^s*^(p'.-.p)

这两个定义都在向量空间包裹。该套餐还包括矢量空间仿射空间函数和元组的实例。这些实例以及实值的实例足以使(可能嵌套的)曲面元组成为向量空间和仿射空间。

从产品到总额

函数对允许一些有用的同构。用产品替换产品:

(a→b)×(a→c)≅a→(b×c)

使用这个乘积/乘积同构,我们可以用来自R(右)2到的元组R(右)

在函数的上下文中,还有一个方便的同构,它将乘积与和联系起来:

(b→a)×(c→a)≅(b+c)→a

如果我们将曲面的概念推广到包含比R(右)2

事实上,这两个同构是一般的和有用的Haskell函数的未组合形式(&&&)(|||),在箭头上定义:

(&&&)::箭头(~>)=>(a~>b)->(a~>c)->(a ~>(b,c))(|||)::ArrowChoice(~>)=>(a~>c)->(b>c)->(b~>c之一)

仅限于功能箭头,(|||)==其中之一

第二种同构,未结婚(|||)还有另一个好处。放宽域类型以允许求和也为其他域变量打开了道路。例如,我们可以有三角域的类型、带孔的形状以及其他类型的有界和无界参数空间。所有这些域都是二维的,尽管它们可能是由几个补丁造成的。

我们的几何图形类型现在变为参数化:

类型几何形状a=a->(R3,材料)

第一个同构,未修剪(&&&),在几何设置中也很有用。考虑范围类型的每个组件(此处R3级材料)作为曲面“属性”。然后(&&&)合并两个兼容的几何体,包括每个几何体的属性。属性可以包括位置(和导数)和与着色相关的材质,以及温度、弹性、粘性等非视觉属性。

考虑到这种灵活性,几何图形获取第二个类型参数,即范围类型。现在什么都没有了几何图形类型但通用功能:

类型Geometry=(->)

回想一下,我们正在寻找语义学用于3D几何图形。这个类型对于几何图形可能是抽象的(->)作为其语义模型。在这种情况下,模型表明几何图形具有所有相同类型的类实例(->)(及其全部或部分应用)具有,包括单体Functor(仿真器)适用莫纳德、和箭头这些实例的语义将由相应的实例给出(->)(请参阅上的帖子类型类态射和报纸具有类型类形态的指称设计.)

或者放弃几何图形并直接使用函数。

我很满意几何函数的简单性。函数适合可编程GPU的灵活性,并提供简单、强大和熟悉的属性合并概念((&&&))和工会((|||)/任何一个).

我剩下的主要问题是:域是什么?

一个简单的域是一维区间,比如[-1,1]。

两个有用的领域构建块是总和和乘积。我在上面提到了和,关于几何并集((|||)/任何一个)产品将域组合为高维域。例如,两个1D间隔的乘积是一个2D间隔(轴对齐的填充矩形),这对于某些参数化曲面很方便。

其他域如何,例如三角形域或多了一个孔?还是多路分支曲面?还是无界?

一个想法是使用求和将简单的域缝合在一起。我们不必构建任何特定的空间形状或大小,因为“几何”函数本身可以生成形状和大小。例如,方形区域可以映射为三角形甚至圆形区域。无限域可以从无限多个有限域缝合在一起。或者它可以从单个有限域映射到。例如,函数x->x/abs(1-x)映射[-1,1]到[-∞,∞]。

或者,我们可以将域表示为类型化谓词(特征函数)。例如,闭合区间[-1,1]将为x->abs x≤1更换防抱死制动系统具有量级(用于内积空间),将此公式推广到包含[-1,1](1D)、单位圆盘(2D)和单位球(3D)。

我喜欢谓词方法的简单通用性,而我喜欢纯类型方法如何支持插值和其他逐点操作(通过提升A2等)。

细分(Tessellation)

我特意在连续空间上制定了图形语义,这使得它独立于解析,并且易于编写。(此公式是3D几何和2D矢量图形的典型公式。连续性的好处通常适用于图像和至动画/行为.)

图形硬件专门用于有限的三角形集合。对于渲染,曲面必须镶嵌的即近似为三角形集合。理想的细分选择取决于曲面和视图的特性,以及场景复杂性和可用的CPU和GPU资源。以理想的曲线形式表示几何体可以进行自动分析和选择细分。例如,由于三角形是线性的,三角形相对于其近似曲面的误差取决于非线性曲面位于三角形对应的域子集之上。使用区间分析衍生产品,非线性可以作为二阶导数或一阶导数范围上的大小界限来测量。误差也可以根据结果图像而不是表面进行分析。

对于基于GPU的实现,可以在“几何着色器”或(我猜想)更通用的框架(如CUDA或OpenCL)中动态细分。

抽象

当指称模型等同于观测等价项时,它是“完全抽象的”。曲面的参数化模型并不是完全抽象的,因为重新参数化曲面会产生一个看起来与曲面相同的不同函数。(表面重成像改变了域和范围之间的关系,同时从几何角度覆盖了完全相同的表面。)独立于特定参数化的属性称为“几何”,我认为它对应于完全抽象(将这些属性视为语义函数)。

几何的完全抽象(几何)模型可能是什么?

18条评论

  1. 卢克·帕尔默:

    有趣的讨论。以下是一些注意事项:

    在依赖类型的设置中,您讨论的两个域(纯类型和特征函数)是相同的,排除了可计算性问题。

    到本文末尾,您似乎已经忘记了导数。我的印象是,连续性和可微性对3D几何非常重要。连续性在可计算函数领域有一个通用的解释:即,从[0,1]到Bool的函数可以是连续的,只要它在为True的区间和为False的区间之间包含一个_|_。我已经被悄悄地唠叨了一段时间,关于衍生品的相应概念。可微函数与[0,1][0,1]中的任意一个看起来像什么?

    但我觉得如果你能捕捉到衍生品,多态域方法是相当好的。数学家(在微分几何中)使用任何最方便的域来处理参数化曲面……他们对这件事考虑了很多:-)

  2. 帕泰·格格利:

    你考虑过从造型艺术家的角度来看这个问题吗?如何以人性化的方式创建复杂的形状,比如真实物体和生物的模型?在我看来,最大的挑战不是提出一个独立的一致且优雅的语义,而是提供一个能够为实际问题提供优雅解决方案的结构。当然,这也取决于观众“真正的问题”意味着什么,因为例如程序几何与人类雕塑家的故事完全不同。

  3. 圆锥:

    在我看来,最大的挑战不是提出一个独立的一致且优雅的语义,而是提供一个能够为实际问题提供优雅解决方案的结构。

    我认为这些挑战是和谐的,而不是紧张的。我的信念(源于个人直觉和经验)是,当我优雅地孤立地触及问题的核心时,我能清楚地看到务实的优点和缺点。我不希望这种观点受到欢迎,我也不介意不受欢迎。这是一条对我来说真实的个人道路,这对我来说已经足够了。

    所以,改写你的话来代表我自己:在我看来,最大的挑战是想出一个独立的、一致的、优雅的语义,从而提供一个能够为实际问题提供优雅解决方案的结构,包括我们还没有想到的

    “真正的问题”的概念是一个狡猾的概念。因此,我们的困难往往在于我们习惯于提出的问题和我们所依附的旧模式。3D艺术家可能喜欢使用优雅的指称设计,或者他们可能需要重新连接大脑,或者只是等待年轻一代。

    总之,我将重复以下两个原则Luke Palmer分享了去年6月的一篇帖子:

    如果极端理想主义不适合你,也许是因为你没有全心全意地追随自己的理想。

    你必须成为你希望在世界上看到的改变(——圣雄甘地)。应用于软件:将软件设计成您希望它成为的美丽天堂,然后将脚手架的各个部分构建回现状。

  4. 杰克·麦克阿瑟:

    我很好奇你为什么选择3D的表面模型而不是2D的图像模型。它主要用于GPU友好性吗?

  5. 圆锥形的:

    你好,杰克。

    我对这个问题不清楚。表面模型图像模型(2D功能)。

    或者你的意思是,我为什么不使用3D图像,即R(右)(无限连续体素贴图)。如果是,那么是的,主要是对GPU友好。

  6. 杰克·麦克阿瑟:

    我确实是指后者,为什么你不使用3D图像。引用卢克·帕尔默(Luke Palmer)的话,“如果极端理想主义对你来说行不通,可能是因为你没有全心全意地遵循自己的理想。”除了易于实现之外,你是否出于任何原因看不到3D图像(可能对实际暴露在GPU面前的操作有一些限制)作为一种理想?(你可以删除我之前的评论。我不是有意点击提交的。)

  7. 圆锥:

    杰克:是的,我在这里选择了一个实现友好的、非自然的模型来制作表面(超过2D),而不是3D模型。和往常一样,作弊者必须付出代价。例如,我的熄火意味着我不能自然地进行CSG,也不能受光线通过的介质(空气、烟、热、水、玻璃等)的影响。

    嗯。说实话感觉真好。难怪天主教徒喜欢忏悔。

  8. 迈克尔·罗宾逊:

    谢谢你那篇鼓舞人心的文章!作为一名应用数学家,我对微分(和其他)拓扑有很强的亲和力,我想知道这其中有多少可能在维度上抽象出来。(我也对卢克的评论做出了恰如其分的回应。)

    也就是说,你能很容易地将你的讨论提升到关于任意维流形之间的平滑映射的讨论中吗?

    当然,为了提供一些模糊的答案(尽管毫无疑问,你比我更清楚如何将其形式化为Haskell),似乎有两个主要障碍:1.在维度上抽象似乎是写的。例如,列表没有编译时长度保证,当您必须说(R、R、R…R)时,元组会变得很尴尬。再次,我真的很感激你的向量空间库!2.记账多方面的图表肯定会有点麻烦。一方面,您可能需要使用指定的方式组合图表,使其看起来相同(简单复合体是一种非常严格的管理方式,Cw复合体更灵活,但仍具有限制性,当然两者都不符合通常的定义)。基本问题是以一种好的方式定义R^n的子集,也许o-最小结构在这里也有帮助。

    无论如何,这是无谓的投机;但对于Haskell中的流形计算来说,这将是一个有用的语义!

  9. 圆锥:

    谢谢你的建议,迈克尔。马克·拉夫特(Mark Rafter)在三月份就这些问题提出了一些详细的建议,但我已经忘记了。值得一提!

  10. 杰克·麦克阿瑟:

    “嗯。坦白的感觉很好。难怪天主教徒喜欢忏悔。”

    暴露模型的局限性也让我对它有了更清晰的了解,这使我更容易推理,尽管在这个过程中它失去了一种简单的表面外观。也许一个很好的类比是我对Arch Linux胜过Ubuntu的品味。他们都有相同的基本架构,但Ubuntu通过简单的界面隐藏了自己的缺点,其优点是最初不会让用户感到困惑,但缺点是掩盖了出现问题的真正原因,而Arch故意暴露了其架构的固有复杂性,可能会使学习曲线变得更陡峭,但由于其充分披露,也更容易推理。虽然Ubuntu有Arch所没有的抽象,但它并不是一个好的抽象,因为它会泄漏,因此用户不仅要学习抽象,还要学习它是如何工作的。我认为,掩盖或未能澄清模型的固有缺陷有点像一个漏洞百出的抽象。

  11. 杰克·麦克阿瑟:

    我想也许我把这个“漏洞百出的抽象”类比推得太远了。我真正的意思是,我更喜欢有明显缺陷的模型,而不是有隐藏缺陷的模型。

  12. 帕泰·格格利:

    我的信念(源于个人直觉和经验)是,当我优雅地孤立地触及问题的核心时,我能清楚地看到务实的优点和缺点。

    那么,你对你正在寻找的这个精华可能采取什么样的形状有任何直觉吗?你是在寻找“3D几何的SK演算”,还是更高层次的视图?

    “真正的问题”的概念是一个狡猾的概念。因此,我们的困难往往在于我们习惯于提出的问题和我们所依附的旧模式。

    当然,这很难说,因为有几种方法可以看待同一件事,我也提到了这一点。但我基本上是想在这里说,最终的“大统一理论”应该能够涵盖所有这些方面,所以你必须考虑这些联系。至于对未知问题的推断,我的感觉是,如果统一成功,它实际上将是免费的。

  13. 圆锥:

    那么,你对你正在寻找的这个精华可能采取什么样的形状有任何直觉吗?

    是的,我喜欢。这对数学/类型/FP人员来说将是简单、熟悉和通用的。总和/产品/功能——诸如此类。它将把已知的特定领域操作解释为更一般/简单操作的特殊情况。会有很多TCM(列车控制模块)在API中,没有太多其他内容。像Reactive的FRP模型一样,它只是根据产品(未来)和功能(行为)构建的。

    至于对未知问题的推断,我的感觉是,如果统一成功,它实际上将是免费的。

    哦,那我想我们走的是同一条路。

  14. 弗拉基米尔·斯列普涅夫:

    这条评论大约迟了5年,但我必须写下来,因为我知道你的问题的正确答案!查看Inigo Quilez关于使用距离场渲染的工作。

    基本思想是将3D对象表示为“距离场”,即从R^3到R的连续函数,该函数返回到对象的距离。该函数的值在对象内部为零,并且随着距离的增加而增加。该技术非常适合于在GPU上进行光线行进,因为当您沿着光线行进时,计算当前点的距离场会给出下一步的大小,这样,当光线从远离对象的地方经过时,可以迈出更大的步伐。球体等简单对象具有易于定义的距离场,对对象的简单操作通常对应于对距离场的简单操作。还可以很容易地近似法线方向、环境光遮挡甚至半影(向光源发送光线,并注意它与遮挡器的距离)。

    在一个完美的世界里,我们可以在Haskell中有一个用于渲染距离场的组合函数库,它可以编译为单个GPU着色器程序。令人担忧的是,这个想法是如此契合。

  15. 道格·莫恩:

    我实现了Vladimir Slepnev的想法(不是用Haskell实现的,而是用另一种纯函数语言实现的)。使用带符号的距离场作为形状表示可以为三维实体提供连续的函数表示,包括并集、变形、混合等。您可以使用传统方法(如矢量图形、离散表示或体素或三角形网格)表示许多无法表示的形状。例如,您可以为分形建模,理论上,分形具有无限的细节,您可以放大这些细节。按照Inigo Quilez的说法,我将我的函数代码编译成一个GPU着色器程序。我对结果感到非常兴奋。但就像所有3D实体的表示一样,这是一个漏洞百出的抽象。你最终必须意识到性能问题,有些形状无法表示,因为它们的距离函数太昂贵,无法评估。球体跟踪要求距离函数为Lipschitz(1)。如果您有一组丰富的形状操作符,如扭曲、弯曲和变形,那么在某些情况下,输出可能会违反Lipschitz(1)要求,最终用户必须意识到这一点(泄漏抽象)并解决问题。我正在扩展我的程序以克服这些问题,这样我就可以表示一组更大的形状,方法是让用户对内部形状表示有更多的控制权,并提供多种表示和渲染方法,但通过这种方式使程序功能更强大,泄漏抽象问题变得更糟。实数没有无泄漏的抽象,因此同样的问题再次出现在几何形状上也就不足为奇了。

  16. 圆锥:

    道格,谢谢你的评论!我很感谢更多的参考资料,包括Lipschitz条件如何与球体跟踪相关,以及如何违反该要求。还有你正在做的项目(如果是公开的)和/或相关项目。还有你正在进行的项目(如果是公开的)和/或相关项目,以及你提到的弗拉基米尔·斯莱普涅夫和伊尼戈·奎雷斯的工作。

  17. 道格·莫恩:

    你好,Conal。我的项目是https://github.com/curv3d/curv.Curv是一种curried的纯函数式语言,我将其创建为用于制作2D和3D形状的DSL。你已经做了很多与我想要做的事情相匹配的研究,所以我现在正在阅读你的论文:FRP、有形价值观等(我的形状已经是时间的连续函数;下一步是使它们更具普遍的交互性,超越我目前使用滑块更改参数的做法。)

    约翰·哈特(John C.Hart)的《球体追踪》(Sphere Tracing)是一本优秀的参考书:https://github.com/curv3d/curv/blob/master/docs/papers/sphere_tracing.pdf

    这是我写的一篇关于曲线形状表示背后的数学的论文。它说了一些哈特没有说的事情,这与曲线有关:https://github.com/curv3d/curv/blob/master/docs/Theory.rst

    我的形状组合子库记录在这里:https://github.com/curv3d/curv/tree/master/docs/shapes

    该文档解释了形状具有距离场,并将距离场分为精确、斜接、近似、差和不连续。您需要知道这一点,因为形状组合符对作为输入接收的距离字段的类别很敏感,并且它们返回的距离类别可能比作为输入接收到的距离类别更差。https://github.com/curv3d/curv/blob/master/docs/shapes/shapes.rst

    每个形状组合子的文档描述了它在接受作为输入并产生作为输出的距离场类方面的限制。

    其他人通过限制表达能力、限制可以定义或使用的形状集和形状组合符来解决这个问题(用户需要担心距离字段属性)。下面列出的所有相关项目都限制了表达能力,从而减少了抽象的泄漏。

    相关项目:

    隐式CAD是用Haskell编写的,使用相同的有符号距离表示。没有GPU编译,并且有一组固定的距离场操作硬编码到库中,您必须破解这些操作才能定义新的原语。它不像Curv那样可扩展。没有颜色或动画。https://github.com/colah/ImplicitCAD

    libfive使用Scheme作为其扩展语言。它有一些与Curv相关的限制:距离函数必须是封闭形式的表达式(没有条件,没有递归),因此许多Curv基元无法被移植。但是,对于距离函数没有Lipshitz连续性限制,因此该限制为用户减轻了负担。图形在显示之前会转换为三角形网格,因此没有精细的细节,也没有无限的图形。也没有颜色或动画。作者马特·基特(Matt Keeter)在今年的SigGraph上发表了一篇论文,介绍如何在不使用球体跟踪的情况下,将其封闭形式的距离函数编译为GPU着色器代码。这是一种新的算法:论文和源代码在编写时尚不可用。

    Media Molecule在Playstation上的视频游戏“梦想”使用有符号的距离字段来表示游戏中的所有几何图形。您可以使用编辑器创建自己的内容,方法是合并、差异和混合一组固定的小几何图元。几何图元和形状操作符的集合受到了严格限制,无法使所有内容都表现良好。GPU的实现非常有趣,令人印象深刻;我正在研究它,希望有一天能复制他们在《曲线》中的最佳技巧。https://www.mediamolecule.com/blog/article/siggraph_2015

  18. 圆锥:

    极好的。再次感谢,道格!我错过了图形方面的工作,现在我可以利用一些灵感。

    我敢打赌,对你的作品进行一次可爱的重新包装,将是通过以下方式重新解释Haskell程序编译为类别

留下评论