游戏引擎学习第128天

[复制链接]
发表于 昨天 07:00 | 显示全部楼层 |阅读模式
开始

然而,我们仍然有一些工作要做,渲染部门并没有完全完成。固然如今已经可以大概运行游戏,而且帧率已经可以继承,但仍然有一些东西必要进一步美满。正在利用调试构建编译版本,固然调试版本性能不如优化版本,但仍然可以流通运行游戏。
接下来,必要做一些调解和改进。渲染器中有一些还没有完成的部门,大概会对团体体现产生影响,必要注意。为了做好预备,写了一些待服务项,重要是关于渲染器的一些优化,好比确保全部的瓦片都可以大概准确地对齐缓存行。固然如今的实现已经富足好,但仍然有一些细节上的优化空间,尤其是在缓存管理方面,确保可以大概最大化性能
这些改进并不必要立即完成,由于如今的体系已经相对稳固,可以继承处置惩罚这些题目而不会影响到其他部门的代码。因此,接下来的一段时间可以用来举行这些优化,进一步提拔渲染器的服从和性能
总的来说,当前渴望顺遂,渲染器的速率令人满足,但仍需举行一些细节上的优化和调解,确保终极的结果更好。
黑板:对齐到缓存行

在开发过程中,讨论到了一个缓存干系的题目,特别是怎样确保瓦片(tile)在内存中的对齐,以进步渲染性能。当前我们将屏幕分成瓦片并将其分配给差异的线程举行处置惩罚,目的是使得每个线程可以独立渲染差异的瓦片,从而加速渲染过程。比方,焦点0负责一个瓦片,焦点1负责另一个瓦片,依此类推。
然而,纵然我们已经包管每个瓦片的内存位置是对齐的,制止了线程间的直接竞争,仍然存在一个埋伏的题目,即“缓存线竞争”(Cache Line Contention)。所谓缓存线,就是处置惩罚器缓存的最小单位。如果两个相邻的瓦片凌驾了同一个缓存线,那么当一个焦点修改了缓存线的数据,另一个焦点的缓存中的雷同数据将被标志为无效,从而必要重新加载数据。这种数据的“争取”大概导致性能降落,由于焦点1必须重新从焦点0的缓存中读取数据。
为了防止这种情况发生,抱负的做法是确保瓦片的界限刚好落在缓存线的界限上,如许每个焦点就能独立处置惩罚本身的瓦片而不会相互干扰。如果瓦片的界限没有对齐缓存线,大概会导致缓存行在焦点之间往返转达,增长内存流量和缓存无效的次数,终极影响性能。
因此,为了制止大概的性能瓶颈,必要举行优化,确保瓦片的内存布局与缓存线对齐。这大概会带来显着的性能提拔,尤其是在多焦点处置惩罚时。固然如今还没有出现显着的性能题目,但这个题目仍然必要关注和测试,以确保不会影响终极的渲染服从。
渲染器待服务项

在开发过程中,讨论了几个优化渲染性能的方向,特别是怎样利用超线程和调解瓦片巨细来提拔渲染服从。
起首,思量到超线程(Hyper-Threading)技能,如今体系并没有充实利用超线程来提拔并行性。固然体系支持超线程,但并未专门针对其举行优化。超线程答应每个焦点同时实验两个线程,抱负情况下,可以将使命分配给这些线程,以便更高效地利用CPU资源。不外,当前的题目在于怎样同步这些线程的工作,使得超线程能有用地瓜代工作,确保每个线程处置惩罚差异的使命,而不会产生辩说或数据共享题目。由于如今没有显着的同步机制,临时还没有实现对超线程的优化。将来可以通过研究和与他人讨论,探索怎样充实利用超线程,以进步渲染服从,大概如果这个思绪不可,也可以放弃这个方案。
接下来,讨论了瓦片(Tile)巨细对性能的影响。瓦片的巨细决定了在渲染过程中每个瓦片能占据多少缓存。如果瓦片太大,大概会导致大量数据无法全部加载到缓存中,从而低沉服从。而如果瓦片太小,固然每个瓦片能更好地利用缓存,但每个瓦片的处置惩罚开销也会增长,尤其是在设置阶段,必要处置惩罚更多的块、裁剪等工作。因此,瓦片巨细的选择是一个必要权衡的点,既要包管性能,又要思量到每个瓦片的处置惩罚本钱。将来必要举行测试,找出一个符合的瓦片巨细,以优化渲染过程。
别的,提到必要对渲染流程的内存带宽举行评估,以相识当前的内存利用服从。必要通过实际测试来盘算内存带宽,以判定是否存在瓶颈,以及是否可以通过调解内存访问方式来进步性能。
末了,讨论了对指令选择的重新测试。如今体系已经支持多线程渲染,因此必要重新评估现有指令的选择,确保在多线程情况下,指令实验能最大限度地进步服从。通太过析和测试,进一步优化指令集,以提拔团体性能。
只管有这些优化方向,但如今还不计划立刻实验这些改进,而是渴望在稍后举行更深入的性能测试和调解,找出最符合的优化方案。
本日的工作:办理剩余的题目

接下来,讨论了渲染体系的整理工作。在渲染功能已经可以运行而且性能精良的根本上,目的是进一步美满渲染的功能,并确保渲染体系在终极优化之前具有完备的特性。重要目的是制止在不完全明白体系的情况下对渲染举行过早的优化。
起首,必要整理和修正坐标体系。固然之进步行了一些优化,而且渲染体系的运行速率已经非常快,但仍然必要举行一个坐标体系的整理,以确保各部门的功能和布局公道。之后,还必要继承完成一些关于光照的工作,进一步优化渲染性能,终极可以将渲染部门标志为完成。
除了光照,另一个必要思量的内容是粒子体系。粒子体系在渲染中通常具有差异于其他元素的特点,因此必要专门思量怎样将其与渲染体系集成。粒子体系的开发大概会在光照优化前或后举行,具体次序尚不确定,但粒子体系也是渲染优化的一部门。
完成上述工作后,操持举行一次终极的构造整理,然后可以将渲染部门视为根本完成,为游戏的开发和发布打下根本。渲染体系的优化和完成将意味着进入下一阶段的游戏开发,预备渐渐推进其他方面的工作。
别的,还必要思量游戏中的音频和动画体系的整合,并将这些工作安排在接下来的开发操持中。音频和动画干系的内容将渐渐融入到游戏中,而且这部门将进入更高条理的游戏功能开发。
总结来说,当前的工作重点是完成低层引擎部门的开发,并尽快完成渲染体系的终极整理和优化。这些低层工作完成后,将为接下来的游戏开发提供更结实的根本,使得开发团队可以大概顺遂向游戏的其他方面过渡,并终极推动游戏的发布
检察坐标体系

在渲染体系的开发过程中,渲染实体的处置惩罚和相机体系的管理显得有些杂乱。最初,我们将渲染使命简朴地推送到一个列表中,但如许做显得有些马虎,而且没有认真思量这些使命应该怎样有序地工作。具体来说,渲染实体的处置惩罚方式让人感觉有些繁琐,包罗了很多不须要的利用,这些利用大概并非渲染过程中必须的。
相机的处置惩罚也是云云,感觉我们在利用相机时过于随意,没有依照一个尺度的三维渲染管线做法,这让体系的举动显得不敷清楚和可控。因此,操持对这些部门举行重新审视和优化,目的是使其更加符合尺度的三维渲染管线布局,如许可以大概更好地明白和控制渲染过程中的每一个环节,确保体系的稳固性和性能。
重新整理和优化渲染实体的处置惩罚和相机体系的管理将资助体系更加高效和稳固,也能为后续的开发和调试打下更好的根本。

重新审视根本变动

如今渲染过程中存在一些题目,尤其是在三维变动的处置惩罚上。固然渲染大部门内容是在二维空间中举行的,但实际上,很多利用仍然依靠于三维变动。我们如今面临的一个题目是没有有用的机制来制止不须要的三维利用,尤其是在处置惩罚地面区块时。这导致我们无法确保在准确的像素空间举行利用,进而影响了地面区块的无缝拼接。
为了更好地办理这个题目,操持将渲染的变动过程举行改进。目的是确保可以在两种模式下机动处置惩罚变动:一种是尺度的正交变动(如最初的像素空间变动),另一种是透视变动。为了实现这一点,计划不再延长变动的实验,而是将其直接在必要的位置举行。这一改变必要调解当前的实体模仿方式,制止在实体模仿后才举行渲染处置惩罚。
具体来说,当前的流程是在实体模仿时实验其对应的利用,然后期待模仿竣事后再渲染图形。但这种做法存在题目,由于直到模仿完成后才气知道实体的位置,导致渲染的机会被延后。因此,为了改进这一流程,计划在实体移动时直接举行相应的渲染变动,从而制止不须要的延长和复杂性,使整个渲染过程更加高效和流通。
通过这些改进,渴望能使渲染体系更清楚、更具可控性,同时制止冗余的三维变动和延长渲染,从而进步团体的渲染性能和机动性。
拆分更新/渲染

渲染仇人的过程可以通过两种方式实现。为了简朴起见,当前选择了最直接的方式,但将来大概会做一些调解。如今的做法是复制现有的switch语句,并将其一部门用来举行渲染,另一部门用于实体模仿。如许,渲染和实体模仿分开处置惩罚,渲染部门负责绘制图像,实体模仿部门负责盘算实体的举动和状态。
操持在将来大概会将这两部门归并处置惩罚,尤其是在移动方式上有所改动时。也大概会重新定名这些利用函数,使得它们更符合实际的工作内容,好比将“模仿实体”的部门重定名为“自由物理模仿实体工作”之类,以更清楚地表达每个函数的作用。
如今,为了保持简朴,决定先按现有的方式将代码拆分,渲染部门和模仿部门各自独立实验。这一过程包罗移除一些不须要的代码,好比一些不必要调用的发送利用。删除这些后,剩下的代码只涉及到必要渲染或模仿的实体,镌汰了不须要的复杂性。
通过这种方式,渲染和实体处置惩罚将更清楚,实体将被渲染到它们实际的位置,而不再是之前那种依靠模仿后再举行渲染的方式。这使得渲染和实体处置惩罚更加直接和高效,能更准确地控制每个实体的状态和位置。

按需实验根本变动

为了制止延长实验的复杂性,如今决定将转换利用直接在线上举行处置惩罚,而不是事后再举行调解。具体来说,起首通过修改渲染组的默认根本设置,使得它可以直接被利用和覆盖。这意味着,全部渲染和变动的设置将直接嵌入到渲染组中,而不必要单独的延长步调。
起首,要提取出渲染所需的根本数据,好比屏幕坐标、摄像机的间隔和焦距等,全部这些都将被直接放入渲染组中举行处置惩罚。这一过程中,摄像机干系的设置(比方渲染相机)将移除,同一由渲染组来处置惩罚。这种调解将使得渲染和变动利用更加直接和清楚。
别的,调解后,渲染过程中的每个利用(如推送位图或矩形)都会先辈行根本变动,再查抄变动后的物体是否在视图范围内。如果物体超出视图,它将不会被推送到渲染队列中,如许制止了不须要的渲染盘算。
对于坐标体系,也同样调解为在利用时及时盘算,而不再是后续再举行增补。如答应以镌汰不须要的盘算和数据存储,提拔渲染服从和清楚度。
总之,通过将这些处置惩罚步调提前并简化为及时利用,制止了延长盘算和数据存储,进步了体系的团体服从。这些改动让渲染和变动过程更加直接,镌汰了复杂度,同时提拔了渲染的及时性和精度。



去除EntityBasis

在处置惩罚渲染实体时,决定简化和优化数据布局,去掉了一些冗余的字段。起首,原来存储的“scale”(缩放)字段实际上没有被利用,因此决定移除它,只保存了“p”以及与巨细干系的须要信息。对于渲染位图,本来存储的“size”和“scale”字段不再必要,由于它们对当前渲染流程没有实际作用。只有位置和巨细信息会被保存。
在思量是否必要存储更多的信息时,发现仅当涉及到照明盘算时,才有须要存储“像素到米”的转换比例,由于这一信息会影响到照明盘算。对于不涉及照明的对象(比方渲染实体位图),则不必要存储这个转换比例。如许,体系可以根据实际需求决定是否存储这些额外的转换数据。
为了处置惩罚照明题目,当渲染实体必要到场照明盘算时,会特别存储“像素到米”的转换比例,而其他对象则不必要。对于坐标体系,大概也必要存储这一比例,以便准确盘算光照结果。
在进一步分析和整理时,去除了冗余的字段和不须要的转换步调。比方,本来为矩形绘制设置的“pixels to meters”字段,现仅在必要照明的情况下保存。其他不涉及照明的情况下,这一字段被默认设置为1,表现没有举行任何转换。
终极,通过将不须要的数据移除和简化存储布局,渲染代码变得更加高效,镌汰了内存占用并简化了盘算过程,特别是在不涉及照明的情况下,进一步进步了渲染服从。


初始化变动

在这段处置惩罚中,重要是对渲染变动(transform)的初始化举行了一些调解。之前的默认变动(DefaultBasis)已经移除,但我们如今必要确保渲染变动对象准确初始化,而且包罗须要的字段,比方缩放(Scale)和偏移(OffsetP)。这些字段将成为渲染变动的焦点部门。
初始化渲染变动:
在新的变动布局中,起首必要将一些须要的参数参加进来,包罗:

  • 缩放(Scale)
  • 偏移(OffsetP)
  • 焦距(FocalLength)
  • 目的上方的间隔(DistanceAboveTarget)
  • 表现器的半宽度(MonitorHalfDimInMeters)
  • 像素到米的转换(MetersToPixels)
这些数据在渲染过程中是必须的,用于盘算和表现准确的渲染结果。
题目办理:

  • 渲染变动的初始化:将变动布局初始化为无效状态(大概公道的初始值),确保它在后续处置惩罚中可以大概准确工作。
  • 移除不再必要的字段:由于默认变动已被移除,某些冗余的代码也被去除。比方,渲染组直接包罗变动对象,不再必要通过间接方式处置惩罚。
关于表现器设置:

  • 表现器的半宽度(MonitorHalfDimInMeters)在变动布局中仍然必要处置惩罚,由于它直接影响到渲染时的视角设置,因此没有被移除。
  • **环球透明度(GlobalAlpha)**仍然是必要保存的,这与渲染中的淡入淡出结果有关。
总结:
颠末调解,全部渲染变动干系的内容都被会合到变动布局里。全部须要的参数都被初始化并准确设置,同时去除了一些不再必要的字段。为了确保盘算准确,变动初始化时必要根据表现器尺寸、焦距、目的间隔等信息来设置。这些更改使得渲染体系更加轻便高效,而且制止了冗余的盘算和存储。



翻译调用

在这段代码中,重要的目的是对渲染变动(transform)举行重新构造和优化,确保渲染的利用在内存中高效实验,而且简化了对一些参数的处置惩罚。团体思绪是通过将变动干系的数据集成在渲染变动布局中,制止了不须要的中心步调和冗余的数据存储。
变动布局的调解:

  • 屏幕中心(ScreenCenter)参加到变动中
    之前的代码中,屏幕中心信息是单独管理的,而如今它作为变动的一部门,直接包罗在变动布局中。如答应以简化处置惩罚过程,直接从变动中获取所需的信息。
  • P值和偏移(OffsetP)
    在渲染过程中,实体位置的盘算依靠于P值。原先的 EntityBasis 重要是用来获取P值,而如今直接通过转达渲染变动中的P值来简化利用。P值包罗了三维空间中的位置,而偏移则是通过变动中的OffsetP来处置惩罚的,因此原来的 EntityBasis 可以被移除。
  • 调解P值
    为了确保位置准确,P值必要根据变动中的偏移举行调解。每次盘算P值时,都必要思量变动的偏移,如答应以实现位置的准确调解。比方,在盘算渲染位置时,P值要加上偏移量,确保表现的准确性。
  • 去除不须要的字段
    之前的代码中存在一些不再必要的字段,好比表现器的半宽度(MonitorHalfDimInMeters)已经不再必要,因此被删除。我们如今只保存须要的字段,如缩放比例、偏移量和焦距等。
  • 像素与米的转换(PixelsToMeters)
    PixelsToMeters 仍然是一个必要保存的参数,由于它在渲染和光照盘算中利用。如果渲染项没有涉及光照,它可以不被利用,但是对于涉及光照的物体来说,PixelsToMeters 是必不可少的。
  • 渲染流程简化
    通过将全部变动盘算直接嵌入到渲染过程中,制止了期待大概延长的盘算。如许每次渲染时,都会直接盘算出终极的P值,制止了后续重新盘算带来的开销。渲染过程中的每一步(如矩形绘制、位图推送等)都会按照这种新机制举行。
  • 巨细(Size)和缩放(Scale)处置惩罚
    之前大概对每个渲染项都处置惩罚了缩放(Scale)和巨细(Size)分离的利用,如今直接将缩放参数内嵌到巨细值中,确保在渲染时可以直接利用准确的缩放后巨细,而不必要单独存储和盘算缩放。
  • 调解后的渲染逻辑
    渲染利用中,起首查抄渲染项是否有用,如果有用,再根据变动后的P值盘算实际的表现位置。然后通过新的渲染方式,直策应用变动并盘算出准确的P值,制止了过多的冗余存储和盘算。
总结:
团体优化过程中,重点在于简化渲染变动的盘算流程,移除不再必要的中心步调,制止冗余的存储和盘算。变动数据直接嵌入渲染流程中,使得每个渲染项都能在实验时直接盘算出准确的P值,而且通过符合的偏移和缩放举行调解。如许,渲染体系的性能得到了提拔,代码布局也变得更加清楚和高效。



完成代码修改

在这段代码的修改过程中,焦点目的是简化和优化变动和渲染根本布局的管理。重要的利用包罗调解怎样转达和利用变动数据,去除不再必要的冗余代码,并确保渲染组(RenderGroup)可以大概准确地处置惩罚变动。
重要修改内容总结:


  • 变动的优化

    • 如今不再必要利用“调试摄像机”(DebugCamera)大概其他不须要的调试工具。本来必要多次盘算的变动,如今通过直接转达和利用渲染组的变动来完成。全部的变动盘算被简化为直策应用当前的变动,并根据目的位置和偏移量调解。
    • 此中涉及到的变动(如目的间隔等)不再单独处置惩罚,而是通过调解变动数据布局来完成。如许做的目的是镌汰冗余盘算,进步代码服从。

  • 渲染根本(RenderBasis)移除

    • 本来的 RenderBasis 处置惩罚逻辑被简化,不再必要额外的渲染根本类。如今只必要将干系的巨细(size)直接转达给渲染组,制止了不须要的数据布局利用。
    • 渲染组的变动可以直接从变动数据中提取出来,不再依靠本来的渲染根本类。去除了之前的 RenderBasis 干系的代码,整理了冗余部门。

  • 移除不须要的初始化利用

    • 渲染组中不再必要重复初始化渲染根本数据(如位置、巨细等)。本来的代码中必要将数据推送到渲染队列,但如今直接转达所需的变动数据即可,不再涉及不须要的初始化和处置惩罚步调。
    • 在本来必要设置渲染根本数据的位置,如今只必要设置渲染组的变动参数,并通过调解位置和偏移量来准确定位渲染物体。

  • 整理冗余字段和利用

    • 删除了一些不再必要的字段和处置惩罚逻辑。比方,渲染根本位置的设置和偏移量的调解,如今由渲染组的变动和目的位置直接受理,而不再必要额外的盘算和设置。
    • 通已往除这些冗余代码,简化了渲染和变动流程,使代码更轻便易懂。

  • 错误处置惩罚和调试

    • 在举行代码修改时,碰到了一个关于渲染组变动(RenderGroup.Transform)的错误。错误提示指出,渲染组的变动必须是一个类实例,而不是一个指针。颠末查抄,发现必要准确设置渲染组中的变动范例和指针,办理了这个题目。

  • 将来操持和优化

    • 当前的修改已经根本完成,下一步是继承优化摄像机的设置和渲染组的利用,确保变动数据和渲染数据的高效连合。
    • 只管如今的代码已经简化,将来大概还必要调解渲染组的设置方式,确保其可以大概更机动地顺应差异的渲染需求。

总结:

团体上,修改的焦点是优化和简化渲染和变动的流程,通过直接利用变动数据来镌汰不须要的中心步调,去除冗余的渲染根本布局,提拔了代码的可维护性和实验服从。变动和渲染根本的调解使得每次渲染利用都更加直接和高效。同时,办理了干系的调试错误,确保了渲染组和变动的准确毗连。




什么都没有黑屏调试一下



PushBitmap 没调用






测试更改

在调试中使渲染变动收效

思量到调试过程中的变动利用,想要实验一种新的方法:在调试模式下,变动大概会以差异的方式工作,重要是渴望可以大概直接对变动举行修改,而且在调试过程中,变动的修改可以是后置的、事后才会应用的。具体来说,思量通过设置“DistanceAboveTarget”来调解调试相机的位置,而且在某些情况下,可以在调试过程中让这个间隔数值更高,或与通例利用下的间隔差异。
一种大概的实现方式是,在调试相机模式下,调解“DistanceAboveTarget”值,大概会使得相机间隔目的更远或更近,从而影响相机视角,大概乃至完全改变其视距。这种改变可以在调试过程中举行,而不影响正常模式下的体现。比方,大概通过代码控制将相机间隔的变革与调试状态挂钩,只有在调试状态下,这些变动才会收效,从而制止干扰到正式发布的功能。
不外,对于这种方法是否真的有用,还没有完全确定。思量到必要在调试中调试视角并得到精准的数据,大概这种调解可以资助开发过程,但在实际利用时大概还必要进一步思量其复杂性和可行性。



齐备第一次工作是由于你在高声思索。

偶尔间,固然我们在体系中参加了很多功能,但并不是每个功能都会在第一次运行时就能完善工作。通常,料想之外的地方大概会出现题目,而一些本身预推测会出题目的地方,反而偶尔却能顺遂运行。这种情况挺希奇的。大概是由于当我们以为某个功能很复杂时,我们会更加专注和警惕,制止堕落。而那些以为比力简朴大概没那么器重的地方,反而大概会出现不测的错误。
黄色部门代表加载的位图吗?

黄色部门表现的是加载的位图,但黄色本身只是为了方便调试,展示了如今哪些部门没有正常渲染出来。它重要是用来标志地面地区的添补情况。本来这些黄色标志只是调试用的,资助看到哪些地方没有被准确添补。如今可以思量把这些标志重新打开,乃至在来日诰日的工作中,大概会把它们永世保存,由于不再必要为调试目的而关闭它们。
如今,剩下的焦点工作就是处置惩罚一些与深度排序、照明和怎样处置惩罚对象的深度(z轴偏移)有关的题目。这部门内容是引擎中的大题目,尤其是在处置惩罚这些与深度和排序有关的功能时,还必要办理怎样与光照体系和谐工作的题目。具体来说,处置惩罚这些题目是当前待服务项中唯一的“大题目”。
别的,关于2D处置惩罚,当前出现了一个题目,就是在处置惩罚深度偏移时,把全部的偏移量都当作一样对待了,但显然不应该如许做。好比,如今可以看到脚色的头部在上下浮动时,它也会左右移动,这种征象原来是不想发生的,但时不时会有些夷由,不确定是否应该这么处置惩罚。总的来说,关于这个题目,必要进一步探究,来日诰日可以继承看看该怎样调解。
有些现阶段的占位符是为了将来的楼梯计划而放置的,如今还不确定终极会怎样处置惩罚这些占位符。抱负的结果是让这些对象能稳固地堆叠在一起,保持准确的层级关系,固然如今还不确定怎样做到这一点,但这确实是一个棘手的寻衅。
然而,最让人高兴的是缩放功能。对缩放结果的喜欢让各人刻意肯定要找到办理方案,确保游戏中的物体在走楼梯时会根据玩家的高度变革举行缩放,视觉结果非常酷,各人以为这个功能必须要有。以是,只管处置惩罚起来有些难度,但肯定要想办法办理,而且与美术资源和谐好。
黑板:缓存失效

在处置惩罚缓存时,起首必要相识 CPU 中的缓存是怎样工作的。每个焦点(Core)都有本身的缓存,这些缓存用于存储数据,以进步访问速率。缓存比主内存要小得多,但它们的作用是镌汰 CPU 访问主内存的时间。

  • 数据加载
    假设我们有一个位置 A 的数据存储在内存中。当 CPU 焦点(好比焦点 0)必要加载这个数据时,它会先查抄本身的缓存。如果缓存中没有这个数据,它就会向主内存哀求数据。当主内存将数据提供给 CPU 后,数据会被加载到焦点的缓存中,并大概标志为 “共享” 或 “独占”(具体取决于缓存的一些标志)。
  • 多个焦点共享数据
    如果另一个焦点(好比焦点 1)也必要访问位置 A 的数据,它会先查询本身的缓存。通常它会通过 “嗅探” 利用(snooping)查抄其他焦点是否已经有了这个数据。如果焦点 0 已经缓存了这个数据,焦点 1 会从焦点 0 的缓存中读取数据,而不是再次去主内存哀求。这时,两个焦点都共享这块数据。
  • 数据修改与缓存失效
    题目出如今当某个焦点(比方焦点 0)必要修改这块数据时。如果焦点 0 正在修改数据,它必要先关照其他焦点它将独占这块数据。为了确保数据同等性,焦点 0 会将这块数据的缓存标志为 “独占”(exclusive),同时关照其他焦点标志该数据为无效(invalid)。这时,其他焦点不能再继承利用这块数据,必须重新从内存或焦点 0 的缓存中获取最新的数据。
  • 写入利用与缓存同等性
    当焦点 0 完成写入利用后,它将数据标志为 “修改”(modified),而其他焦点必须清空它们的缓存中原来的数据,以确保全部焦点都能获取到修改后的数据。这是缓存同等性协议的一部门,确保全部焦点的缓存数据是同步的。
  • 缓存失效的过程
    所谓 “无效化” 缓存,就是在数据被修改后,某个焦点的缓存中的数据不再是最新的,必须标志为无效。这是由于其他焦点大概在背后修改了数据,从而导致该缓存行的数据已经不再准确。每当发生写利用时,缓存行会被标志为 “无效”,而且其他必要该数据的焦点必须重新获取。
总结来说,缓存失效是在多核 CPU 中保持数据同等性的一个关键步调。它确保了当多个焦点利用雷同的数据时,可以大概通过有用的缓存协议包管数据的准确性和同等性。
能否总结一下今晚做了什么?有些地方有点难以跟上。

本日做的重要工作实在比力简朴,涉及到一些渲染流程的改动。之前的做法是,我们有一个渲染缓冲区(render buffer),然后会推送一个叫做“实体基准”(entity basis)的变动。接着,利用这些变动来处置惩罚位图纪录(bitmap records),这些纪录界说了要绘制的位图以及干系的基准数据。
在渲染时,我们会连合这些基准数据和位图数据,来盘算出位图的实际位置和屏幕上表现的矩形地区。也就是说,变动和位图数据是分开处置惩罚的,变动数据仅在渲染时才会连合起来。
不外,我们对这个过程做了一些改动,目的是进步服从。改动后的做法是,在渲染组(render group)中直接存储当前的变动矩阵,而不再推送基准数据。然后,在渲染缓冲区(push buffer)中,我们不再推送基准数据,而是直接推送位图。当位图被推送时,我们会立即在推送时举行变动盘算,而不是比及渲染时再盘算。如许,我们直接存储变动后的结果,而不是存储全部必要重构数据的中心信息。
如许做的重要目的是进步服从,制止延长实验变动,而是在数据推送时就完成变动利用。这就是本日的重要工作,简朴来说,就是将变动从延长实验改为推送时立即实验。
你能表明一下什么叫做串行处置惩罚吗?

当说某个利用是按串行(serial)举行时,意味着这些利用必须按次序实验,一个接着一个,没有重叠。也就是说,每个利用都得等前一个利用完成后才气开始,不能并行实验。好比,假设有四个步调:A、B、C 和 D,按串行实验时,它们会依次实验:

  • 第一步:实验 A
  • 第二步:实验 B(A 完成后才气开始)
  • 第三步:实验 C(B 完成后才气开始)
  • 第四步:实验 D(C 完成后才气开始)
每个步调只能在前一步完成后才气开始,换句话说,全部的利用都得“列队”,因此这些利用在时间上是一连发生的。
并行(parallel)实验则是完全差异的。并行意味着这些利用可以同时举行,不必要期待其他利用完成。举个例子,假设我们仍然有 A、B、C 和 D 四个步调,在并行实验的情况下,全部的步调都可以同时开始:

  • 在同一时候,A、B、C 和 D 都开始实验,只管它们大概泯灭差异的时间,但它们不必要期待其他步调完成绩可以一起开始。
串行实验的时间是全部步调所需时间的总和。也就是说,若每个步调必要的时间分别是 tA、tB、tC 和 tD,那么总时间就是 tA + tB + tC + tD。
并行实验的时间则是全部步调中泯灭时间最长的谁人。也就是说,假设 A 必要 tA 时间,B 必要 tB 时间,C 必要 tC 时间,D 必要 tD 时间,那么并行实验的总时间是 max(tA, tB, tC, tD),即取最慢的步调的时间作为整个过程的实验时间。
总结来说,串行必要全部利用按次序逐个实验,团体耗时较长。而并行则能让利用同时举行,团体耗时则取决于实验时间最长的谁人步调。
在你移除缩放后,我错过了一部门内容。岂非你不必要它来做Z轴上的缩放吗?

在这个过程里,之前利用的缩放(scale)功能被移除的缘故原由是由于不再必要在渲染过程中存储它。原先的做法是为了根据 Z 轴来举行缩放,但如今的做法是通过直接盘算缩放值来替换存储缩放因子。
具体来说,渲染转换(render transform)中设置了缩放值,但这个值并不再被存储到渲染缓冲区中。每次举行推送(push)时,都会在推送时盘算出当前的缩放值,而且直接存储盘算后的尺寸,而不必要存储缩放因子本身。
比方,盘算缩放时,会在推送时直接处置惩罚这个缩放,而不在渲染数据中保存缩放因子。盘算完成后,全部干系的内容,包罗位置(p),都会被准确地缩放,而且只存储这些终极的值,而不再存储原始的缩放因子。如许做的目的是进步服从,制止不须要的存储,由于在推送后,缩放因子已经没有须要再被利用。
在位图之间的蹊径通过是什么情况?是基于Z轴的简朴缩放吗?

在这段过程中,涉及到的是如安在渲染和缩放时处置惩罚 Z 轴的偏移题目。最初,p 值被用于盘算偏移,这个值重要影响 X 和 Y 轴的偏移。然而,Z 轴的处置惩罚必要举行调解,p 值不应该影响 Z 轴的偏移。
具体来说,目的是消除 Z 轴的偏移(即,不再利用 p 的值来直接调解 Z 轴)。在举行转换时,起首必要应用 p 的变动来影响位置,然后再思量缩放。在这一过程中,缩放是基于 p 值和 Z 轴的偏移量举行盘算的。这个偏移量大概会影响 Z 轴的终极位置,但必要在渲染盘算后才加上。
如今碰到的题目是,固然已经调解了 Z 轴的偏移,但仍然发现 Z 值大概没有完全“归零”或扫除,以是在实际利用中照旧大概有一些偏移。临时的办理方法是将 Z 轴的偏移设置为零,并操持在将来继承调解和优化怎样准确地处置惩罚这个题目。
简而言之,目的是修正 Z 轴偏移的处置惩罚方式,确保缩放和变动仅影响 X 和 Y 轴,而且将 Z 轴的偏移在渲染时举行准确的盘算和调解。
这个的FPS是多少?

如今,运行的帧率是每秒 60 帧(FPS),而且是固定的。如果将编译器切换到优化模式,性能会更好,到达更高的服从。
不外,地面块的重修(ground chunk rebuilding)如今还没有在单独的线程中处置惩罚,固然理论上可以将其独立出来,如答应以制止一些性能题目。当前,仍然会在某些情况下出现轻微的卡顿,好比移动到新位置时会看到一些小的“卡顿”征象。这个题目必要办理,固然团体来说,性能体现还算不错,渴望比力顺遂。
为了创建蹊径的3D结果,是否必要创建一种渲染技能来绘制蹊径的墙壁,从底楼开始,到顶楼竣事?

为了实现楼梯的三维结果,目的是从底层开始绘制墙面,直到顶层,形成一个很酷的视差结果。这必要计划一个渲染技能来处置惩罚楼梯墙面的绘制,确保在差异的高度上出现出精良的结果。具体要怎么做,还没有完全确定,大概会在之后举行调解和改进,操持会根据实际情况举行优化和实现。


什么时间会思量咨询其他步调员?什么情况才到达这个门槛?

在思量是否向其他步调员讨教时,通常是在本身碰到某个本身不善于的范畴时,特别是当我知道某个步调员在某个范畴非常专业时。比方,涉及到多线程编程时,如果本身对这一部门不敷夺目,大概会向一些善于这个范畴的步调员讨教,相识他们的思绪和方法,获取一些有代价的看法。通常不太会单纯为了讨论编程题目而去咨询别人,由于大部门情况下本身能处置惩罚这些题目。但如果碰到本身不认识的范畴,大概对某些技能有疑问时,会主动讨教履历丰富的人。这不但是为了当下的项目,也是为了以后能积聚更多的履历和知识。
如果你想对场景的差异部门应用差异的变动或光照,会发生什么?

如果必要对场景中差异部门应用差异的变动,实在体系已经为此做好了预备。当前,在处置惩罚每个实体时,已经有差异的变动应用到了每个实体上。以是,如果必要对场景的差异部门举行差异的变动,这个利用好坏常简朴的。实际上,如今体系已经在每个实体上应用了变动,此中每个实体的变动都可以根据其位置来设置。如果必要为每个实体设置差异的焦距或其他任何参数,也是可以轻松做到的。总的来说,体系已经支持了这种机动的变动应用,可以根据需求举行差异的设置和调解。
你预期利用OpenGL/D3D举行Blitting会有多少性能提拔?

利用OpenGL举行处置惩罚时,重要的性能提拔来自于硬件方面的优化。OpenGL提供了更多的硬件资源来处置惩罚内存复制,这些硬件资源本身就为这种利用举行了优化,因此在处置惩罚纹理时,OpenGL应该能比当前的方式更快速。如果指的是将数据展示到屏幕上的过程,那么通过OpenGL举行渲染大概会更加流通,由于没有颠末一些复杂的路径。不外,对于具体提拔的幅度,如今还不太确定,大概提拔的水平并不会特别大。
渲染器中是否有雷同于OpenGL/D3D中对模子空间/摄像机空间/裁剪空间的翻译的概念?

在OpenGL中,模子空间(local space)、天下空间(model space)、相机空间(camera space)和裁剪空间(clip space)之间的转换好坏常关键的,但OpenGL本身并不直接处置惩罚或关心这些转换。它只关心裁剪空间,而且答应开发者本身在着色器中举行全部须要的变动。具体来说,OpenGL并不直接盘算从局部空间、模子空间到相机空间的转换,而是将这些变动组合起来,在着色器中盘算终极的裁剪空间坐标。
在这种方式下,OpenGL让开发者完全控制全部的变动过程,而无需显式地处置惩罚每一个空间转换。相反,其他图形API或渲染体系(好比DirectX)大概更关注这些转换步调,特别是怎样将对象从局部空间转换到裁剪空间,举行相应的盘算和处置惩罚。
总之,OpenGL并不关心局部空间、模子空间或相机空间等中心步调,而是通过在着色器中完玉成部须要的盘算,终极将对象转换为裁剪空间。
我找到一个你讲授四元数双重覆盖的视频,但没有表明四元数。你会思量在不久的将来做一个表明吗?

关于四元数双重覆盖(quaternion double cover)的题目,如今并不计划提供这方面的讲授,由于已经好久没有深入研究四元数了,尤其是自从最初打仗时,四元数在盘算机图形学中还比力新奇。固然四元数的数学配景本身并不新,早在哈密顿期间就已经有干系理论,但随着时间的推移,四元数的应用和理论也有了很多发展,尤其是如今的双四元数(dual quaternion)等技能,已经超出了从前的研究范围。
四元数的应用如今涉及到很多复杂的多少代数内容,这些内容并不认识,也没有举行过深入的学习,因此无法为各人提供一个完备的入门讲授。四元数的学习不但仅是明白其数学配景,还必要涉及一些当代多少代数的概念,而这些内容并没有深刻相识。因此,大概必要找到更符合的专家来举行具体的讲授。
总的来说,四元数固然是一个紧张的数学工具,特别在盘算机图形学中用于旋转等利用,但如今的技能发展已经将其应用范围拓展,明白和利用当代的双四元数等技能,也要求对更广泛的多少代数有所相识。
1. 什么是四元素(Quaternion)?

四元素是一种数学对象,用于表现三维空间中的旋转。它由威廉·哈密顿(William Hamilton)在1843年提出,情势为:
q=w+xi+yj+zkq = w + xi + yj + zkq=w+xi+yj+zk
此中:

  • w,x,y,zw, x, y, zw,x,y,z 是实数;
  • i,j,ki, j, ki,j,k 是虚数单位,满足 i2=j2=k2=ijk=−1i^2 = j^2 = k^2 = ijk = -1i2=j2=k2=ijk=−1。
四元素可以看作一个标量(www)加上一个三维向量(xi+yj+zkxi + yj + zkxi+yj+zk)。在盘算机图形学、呆板人学和航空航天中,四元素常用来表现旋转,由于它比旋转矩阵更紧凑,且制止了“万向锁”(gimbal lock)题目。
例子:

假设一个四元素 q=0.707+0.707i+0j+0kq = 0.707 + 0.707i + 0j + 0kq=0.707+0.707i+0j+0k,它表现绕 xxx 轴旋转 90 度的旋转。

  • w=cos⁡(θ/2)w = \cos(\theta/2)w=cos(θ/2),θ\thetaθ 是旋转角度,这里 θ=90∘\theta = 90^\circθ=90∘,以是 w=cos⁡(45∘)=0.707w = \cos(45^\circ) = 0.707w=cos(45∘)=0.707;
  • 向量部门 (x,y,z)=(1,0,0)(x, y, z) = (1, 0, 0)(x,y,z)=(1,0,0) 表现旋转轴,模长尺度化后乘以 sin⁡(θ/2)=sin⁡(45∘)=0.707\sin(\theta/2) = \sin(45^\circ) = 0.707sin(θ/2)=sin(45∘)=0.707。
2. 什么是四元素的双覆盖(Double Cover)?

四元素的“双覆盖”是指:对于同一个三维旋转,存在两个差异的四元素可以表现它。具体来说,如果 qqq 表现某个旋转,那么 −q-q−q(即每个分量取负)表现的也是同一个旋转。这是由于四元素通过单位四元素的乘法对应到旋转群 SO(3)SO(3)SO(3)(三维旋转群),而这种对应是 2:1 的映射。
数学上:

  • 四元素属于 SU(2)SU(2)SU(2) 群(单位四元素的聚集),而 SU(2)SU(2)SU(2) 到 SO(3)SO(3)SO(3) 的映射是一个双覆盖。
  • 对于旋转角度 θ\thetaθ 和轴 v⃗\vec{v}v,可以用 q=cos⁡(θ/2)+sin⁡(θ/2)v⃗q = \cos(\theta/2) + \sin(\theta/2)\vec{v}q=cos(θ/2)+sin(θ/2)v 表现,但 qqq 和 −q-q−q 对应同一个旋转。
例子:


  • 旋转 180 度绕 zzz 轴:

    • q1=cos⁡(90∘)+sin⁡(90∘)k=0+1kq_1 = \cos(90^\circ) + \sin(90^\circ)k = 0 + 1kq1​=cos(90∘)+sin(90∘)k=0+1k;
    • q2=cos⁡(270∘)+sin⁡(270∘)k=0−1k=−q1q_2 = \cos(270^\circ) + \sin(270^\circ)k = 0 - 1k = -q_1q2​=cos(270∘)+sin(270∘)k=0−1k=−q1​。

  • q1q_1q1​ 和 q2q_2q2​ 都表现绕 zzz 轴旋转 180 度。
3. 上风是什么?

四元素的上风:


  • 紧凑性:只需 4 个数字表现旋转,而旋转矩阵必要 9 个。
  • 无万向锁:制止了欧拉角在某些角度下的奇特性题目。
  • 平滑插值:四元素可以用球面线性插值(SLERP)平滑过渡两个旋转,得当动画和运动规划。
  • 盘算服从:四元素的乘法和逆运算比矩阵更高效。
双覆盖的上风(或特性):


  • 数学完备性:双覆盖反映了旋转群的拓扑性子(SO(3)SO(3)SO(3) 不是单连通的),在理论物理(如量子力学中的自旋)中有紧张应用。
  • 一连性:在路径规划中,双覆盖答应区分“旋转一圈”和“回到原位”,比方区分 360 度和 0 度的路径,这在某些应用(如呆板人手臂控制)中很有用。
总结:

四元素是一个表现旋转的强盛工具,双覆盖是它的一个数学特性,固然在实际应用中我们通常只关心单一表现,但在理论和某些复杂场景下,双覆盖提供了额外的机动性和准确性。好比在游戏引擎(如Unity或Unreal)中,四元素被广泛利用来确保旋转的平滑和稳固。
  1. import numpy as np
  2. import tkinter as tk
  3. from tkinter import ttk
  4. import matplotlib.pyplot as plt
  5. from mpl_toolkits.mplot3d import Axes3D
  6. from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
  7. # 设置支持中文的字体并放大
  8. plt.rcParams['font.sans-serif'] = ['SimHei']
  9. plt.rcParams['axes.unicode_minus'] = False
  10. plt.rcParams['font.size'] = 14
  11. # 四元素类
  12. class Quaternion:
  13.     def __init__(self, w, x, y, z):
  14.         self.w = w
  15.         self.x = x
  16.         self.y = y
  17.         self.z = z
  18.    
  19.     def __str__(self):
  20.         return f"{self.w:.2f} + {self.x:.2f}i + {self.y:.2f}j + {self.z:.2f}k"
  21.    
  22.     def multiply(self, other):
  23.         w1, x1, y1, z1 = self.w, self.x, self.y, self.z
  24.         w2, x2, y2, z2 = other.w, other.x, other.y, other.z
  25.         w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
  26.         x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
  27.         y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
  28.         z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
  29.         return Quaternion(w, x, y, z)
  30.    
  31.     def conjugate(self):
  32.         return Quaternion(self.w, -self.x, -self.y, -self.z)
  33.    
  34.     def rotate_vector(self, vector):
  35.         p = Quaternion(0, vector[0], vector[1], vector[2])
  36.         q_conj = self.conjugate()
  37.         result = self.multiply(p).multiply(q_conj)
  38.         return np.array([result.x, result.y, result.z])
  39. # 创建四元素
  40. def create_quaternion(axis, angle_deg):
  41.     angle_rad = np.radians(angle_deg)
  42.     w = np.cos(angle_rad / 2)
  43.     s = np.sin(angle_rad / 2)
  44.     norm = np.sqrt(sum(a**2 for a in axis))
  45.     if norm == 0:
  46.         return Quaternion(1, 0, 0, 0)
  47.     x, y, z = [a / norm * s for a in axis]
  48.     return Quaternion(w, x, y, z)
  49. # 可视化工具类
  50. class QuaternionVisualizer:
  51.     def __init__(self, root):
  52.         self.root = root
  53.         self.root.title("四元素可视化工具")
  54.         self.root.geometry("900x700")
  55.         
  56.         # 设置全局字体
  57.         self.font_large = ('SimHei', 14)
  58.         
  59.         # 初始向量
  60.         self.initial_vector = np.array([1, 0, 0])
  61.         self.rotated_vector = self.initial_vector.copy()
  62.         self.current_q = Quaternion(1, 0, 0, 0)
  63.         
  64.         # 主框架
  65.         self.frame = ttk.Frame(root, padding="20")
  66.         self.frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
  67.         
  68.         # 设置行列权重
  69.         self.root.grid_rowconfigure(0, weight=1)
  70.         self.root.grid_columnconfigure(0, weight=1)
  71.         self.frame.grid_rowconfigure(0, weight=1)
  72.         self.frame.grid_columnconfigure(2, weight=1)
  73.         # 输入区域(左侧)
  74.         self.input_frame = ttk.Frame(self.frame)
  75.         self.input_frame.grid(row=0, column=0, sticky=(tk.W, tk.N, tk.S), padx=10)
  76.         
  77.         ttk.Label(self.input_frame, text="旋转轴 (x y z):", font=self.font_large).grid(row=0, column=0, sticky=tk.W, pady=10)
  78.         self.axis_entry = ttk.Entry(self.input_frame, width=15, font=self.font_large)
  79.         self.axis_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=10)
  80.         self.axis_entry.insert(0, "0 0 1")
  81.         ttk.Label(self.input_frame, text="旋转角度 (度):", font=self.font_large).grid(row=1, column=0, sticky=tk.W, pady=10)
  82.         self.angle_entry = ttk.Entry(self.input_frame, width=15, font=self.font_large)
  83.         self.angle_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=10)
  84.         self.angle_entry.insert(0, "90")
  85.         ttk.Button(self.input_frame, text="应用旋转", command=self.apply_rotation, style='Large.TButton').grid(row=2, column=0, columnspan=2, pady=10)
  86.         ttk.Button(self.input_frame, text="重置", command=self.reset, style='Large.TButton').grid(row=3, column=0, columnspan=2, pady=10)
  87.         # 四元素信息(中部)
  88.         self.info_frame = ttk.Frame(self.frame)
  89.         self.info_frame.grid(row=3, column=0, sticky=(tk.W, tk.N, tk.S), padx=10)
  90.         
  91.         self.q_label = ttk.Label(self.info_frame, text="当前四元素: 1.00 + 0.00i + 0.00j + 0.00k", font=self.font_large)
  92.         self.q_label.grid(row=4, column=0, pady=10)
  93.         
  94.         self.double_cover_label = ttk.Label(self.info_frame, text="双覆盖版本: -1.00 + 0.00i + 0.00j + 0.00k", font=self.font_large)
  95.         self.double_cover_label.grid(row=5, column=0, pady=10)
  96.         
  97.         self.explain_label = ttk.Label(self.info_frame, text="解释: 未旋转状态", font=self.font_large, wraplength=400)
  98.         self.explain_label.grid(row=6, column=0, pady=10)
  99.         # Matplotlib 嵌入(右侧)
  100.         self.fig = plt.Figure(figsize=(7, 7))
  101.         self.ax = self.fig.add_subplot(111, projection='3d')
  102.         self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
  103.         self.canvas.get_tk_widget().grid(row=0, column=2, sticky=(tk.W, tk.E, tk.N, tk.S))
  104.         # 绑定窗口大小变化事件
  105.         self.root.bind("<Configure>", self.on_resize)
  106.         
  107.         # 自定义按钮样式
  108.         style = ttk.Style()
  109.         style.configure('Large.TButton', font=self.font_large)
  110.         
  111.         # 初次绘制
  112.         self.update_plot()
  113.         self.last_width = self.root.winfo_width()
  114.         self.last_height = self.root.winfo_height()
  115.     def update_plot(self):
  116.         self.ax.clear()
  117.         self.ax.quiver(0, 0, 0, self.initial_vector[0], self.initial_vector[1], self.initial_vector[2],
  118.                        color='r', label='初始向量', linewidth=2)
  119.         self.ax.quiver(0, 0, 0, self.rotated_vector[0], self.rotated_vector[1], self.rotated_vector[2],
  120.                        color='b', label='旋转后向量', linewidth=2)
  121.         self.ax.set_xlim([-1.5, 1.5])
  122.         self.ax.set_ylim([-1.5, 1.5])
  123.         self.ax.set_zlim([-1.5, 1.5])
  124.         self.ax.set_xlabel('X轴')
  125.         self.ax.set_ylabel('Y轴')
  126.         self.ax.set_zlabel('Z轴')
  127.         self.ax.legend()
  128.         self.canvas.draw()
  129.     def on_resize(self, event):
  130.         # 避免初始调整和重复绘制
  131.         if event.widget == self.root:
  132.             new_width = event.width
  133.             new_height = event.height
  134.             if new_width != self.last_width or new_height != self.last_height:
  135.                 # 只调整图形大小,不重绘内容
  136.                 dpi = self.fig.dpi
  137.                 self.fig.set_size_inches((new_width * 0.5) / dpi, (new_height * 0.8) / dpi)
  138.                 self.canvas.draw()  # 只更新画布,不重新生成视图
  139.                 self.last_width = new_width
  140.                 self.last_height = new_height
  141.     def apply_rotation(self):
  142.         try:
  143.             axis = [float(x) for x in self.axis_entry.get().split()]
  144.             if len(axis) != 3:
  145.                 raise ValueError("轴必须是3个数字")
  146.             angle = float(self.angle_entry.get())
  147.         except ValueError as e:
  148.             tk.messagebox.showerror("输入错误", f"无效输入: {e}")
  149.             return
  150.         
  151.         q = create_quaternion(axis, angle)
  152.         self.current_q = q.multiply(self.current_q)
  153.         self.rotated_vector = self.current_q.rotate_vector(self.initial_vector)
  154.         
  155.         self.q_label.config(text=f"当前四元素: {self.current_q}")
  156.         self.double_cover_label.config(text=f"双覆盖版本: {Quaternion(-self.current_q.w, -self.current_q.x, -self.current_q.y, -self.current_q.z)}")
  157.         
  158.         norm_axis = np.array(axis) / np.sqrt(sum(a**2 for a in axis))
  159.         explain_text = (
  160.             f"解释:\n"
  161.             f"1. 四元素 = w + xi + yj + zk\n"
  162.             f"   - w = cos(θ/2) = {self.current_q.w:.2f} (标量部分,表示旋转角度)\n"
  163.             f"   - (x, y, z) = ({self.current_q.x:.2f}, {self.current_q.y:.2f}, {self.current_q.z:.2f}) (向量部分,表示轴)\n"
  164.             f"2. 当前旋转轴: [{norm_axis[0]:.2f}, {norm_axis[1]:.2f}, {norm_axis[2]:.2f}]\n"
  165.             f"3. 双覆盖: q 和 -q 表示相同旋转"
  166.         )
  167.         self.explain_label.config(text=explain_text)
  168.         
  169.         self.update_plot()
  170.     def reset(self):
  171.         self.current_q = Quaternion(1, 0, 0, 0)
  172.         self.rotated_vector = self.initial_vector.copy()
  173.         
  174.         self.axis_entry.delete(0, tk.END)
  175.         self.axis_entry.insert(0, "0 0 1")
  176.         self.angle_entry.delete(0, tk.END)
  177.         self.angle_entry.insert(0, "90")
  178.         
  179.         self.q_label.config(text="当前四元素: 1.00 + 0.00i + 0.00j + 0.00k")
  180.         self.double_cover_label.config(text="双覆盖版本: -1.00 + 0.00i + 0.00j + 0.00k")
  181.         self.explain_label.config(text="解释: 未旋转状态")
  182.         
  183.         self.update_plot()
  184. # 启动工具
  185. if __name__ == "__main__":
  186.     root = tk.Tk()
  187.     app = QuaternionVisualizer(root)
  188.     root.mainloop()
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表