<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>课程笔记 on wanfeng</title>
        <link>https://sdpyy1.github.io/tags/%E8%AF%BE%E7%A8%8B%E7%AC%94%E8%AE%B0/</link>
        <description>Recent content in 课程笔记 on wanfeng</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <copyright>爱喝东方树叶</copyright>
        <lastBuildDate>Tue, 04 Nov 2025 11:04:19 +0800</lastBuildDate><atom:link href="https://sdpyy1.github.io/tags/%E8%AF%BE%E7%A8%8B%E7%AC%94%E8%AE%B0/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Games104：物理引擎的基础理论</title>
        <link>https://sdpyy1.github.io/p/games104%E7%89%A9%E7%90%86%E5%BC%95%E6%93%8E%E7%9A%84%E5%9F%BA%E7%A1%80%E7%90%86%E8%AE%BA/</link>
        <pubDate>Tue, 04 Nov 2025 11:04:19 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games104%E7%89%A9%E7%90%86%E5%BC%95%E6%93%8E%E7%9A%84%E5%9F%BA%E7%A1%80%E7%90%86%E8%AE%BA/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/%E6%88%AA%E5%B1%8F2025-11-04-11.15.42.png" alt="Featured image of post Games104：物理引擎的基础理论" /&gt;&lt;blockquote&gt;
&lt;p&gt;主要讲了欧拉积分、碰撞检测方法、碰撞结算方法
物理世界的对象:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Static&lt;/li&gt;
&lt;li&gt;Dynamic&lt;/li&gt;
&lt;li&gt;Trigger(参与GamePlay但是与物理世界无关)&lt;/li&gt;
&lt;li&gt;Kinematic（动力学Actor）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.09.08.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.09.08&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;actor-shapes&#34;&gt;Actor Shapes
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.11.53.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.11.53&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.15.42.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.15.42&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;欧拉积分&#34;&gt;欧拉积分
&lt;/h1&gt;&lt;p&gt;黎曼积分定义为：
&lt;/p&gt;
$$
\int_a^b f(t) \, dt = \lim_{n\to\infty} \sum_{i=0}^{n-1} f(t_i^*) \, \Delta t
$$&lt;p&gt;
其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;区间 $[a,b]$ 被划分为很多小区间；&lt;/li&gt;
&lt;li&gt;每个小区间长度为 $\Delta t = \frac{b-a}{n}$；&lt;/li&gt;
&lt;li&gt;$f(t_i^*)$ 是函数在区间上的一个采样值；&lt;/li&gt;
&lt;li&gt;积分就是“采样 × 宽度”的极限&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;显式欧拉法 = 左端点采样（Left Riemann Sum）&lt;/strong&gt;
&lt;strong&gt;隐式欧拉法 = 右端点采样（Right Riemann Sum）&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;显式欧拉积分&#34;&gt;显式欧拉积分
&lt;/h2&gt;&lt;p&gt;用当前状态的参数来近似 $\Delta T$ ，比如$t_0时速度为5，那就假设$$\Delta T$内都是5来模拟积分。  显式欧拉法 = 用左端点采样的黎曼积分近似。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.29.09.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.29.09&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;这样求积分的问题是：力不守恒，用因为时间段内力不是恒定的。 如下图所示，如果总是以某一点的力的方向来求解，那物体总是朝着切线方向位移，来不及改变力的方向（因为在$\Delta T$时间内力是恒定的）&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.40.01.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.30.18&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.42.33.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.42.33&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;隐式欧拉方法&#34;&gt;隐式欧拉方法
&lt;/h2&gt;&lt;p&gt;用未来的状态来计算$\Delta T$时间内的数据 。隐式欧拉法 = 用右侧端点采样的黎曼积分近似。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.43.00.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.43.00&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;显然能量衰减了，我在$\Delta T$时间内又不是一直这么小&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.44.31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.44.31&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;半隐式欧拉法&#34;&gt;半隐式欧拉法
&lt;/h2&gt;&lt;p&gt;有点像Hack&amp;hellip;..&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.48.52.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.48.52&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;这种方法数学上很稳定 $\Delta T$取0.05就能很好模拟圆周运动了&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.49.32.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.49.32&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.49.23.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.49.23&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;刚体运动学&#34;&gt;刚体运动学
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;这块课程很杂，应该需要用到什么学什么，这里先听课&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-11.57.07.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 11.57.07&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;碰撞检测&#34;&gt;碰撞检测
&lt;/h1&gt;&lt;p&gt;一般分两步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AABB检测&lt;/li&gt;
&lt;li&gt;计算碰撞点&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.06.19.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.06.19&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;broad-phase&#34;&gt;Broad Phase
&lt;/h2&gt;&lt;p&gt;BVH检测&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.09.12.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.09.12&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;排序检测&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.09.19.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.09.19&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;narrow-phase&#34;&gt;Narrow Phase
&lt;/h2&gt;&lt;p&gt;用球的半径来检测&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.12.58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.12.58&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.13.36.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.13.36&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;闵可夫斯基和minkowski-sum&#34;&gt;闵可夫斯基和（Minkowski Sum）
&lt;/h3&gt;&lt;p&gt;两个点集内全部互相加&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.16.07.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.16.07&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;红色三角形表示Minkowski Sum的结果&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.18.12.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.17.12&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.18.34.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.18.34&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;再定义减法&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.20.18.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.20.18&#34;
	
	
&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;最妙的来了，如果红色图像包了原点，那就碰撞了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.20.35.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.20.35&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;如何判断这个多边形过原点呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.22.52.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.22.52&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;分离轴原理sat&#34;&gt;分离轴原理(SAT)
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.24.33.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.24.33&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.26.21.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.26.21&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;碰撞处理&#34;&gt;碰撞处理
&lt;/h1&gt;&lt;p&gt;处理碰撞后分离&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.28.30.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.28.30&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;hack&#34;&gt;Hack
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.29.01.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.29.01&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;约束求解&#34;&gt;约束求解
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;需要再看&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.31.35.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.31.35&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.32.14.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.32.14&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;scene-query&#34;&gt;Scene Query
&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;查询碰撞点、查询Sweep、查询Overlap&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;raycast查询碰撞点：比如子弹&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.37.50.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.37.50&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Sweep&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.38.00.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.38.00&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Overlap：比如爆炸范围overlap了那些Actor&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.38.45.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.38.45&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;优化&#34;&gt;优化
&lt;/h1&gt;&lt;p&gt;模拟优化&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.41.05.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.41.05&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;CCD：连续碰撞检测，离散检测可能会穿过薄面&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.42.22.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.42.22&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;确定性模拟：不同的机器计算结果应该一致，比如显卡、移动端等差别&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-11-04-12.45.00.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-11-04 12.45.00&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Games104：引擎工具链</title>
        <link>https://sdpyy1.github.io/p/games104%E5%BC%95%E6%93%8E%E5%B7%A5%E5%85%B7%E9%93%BE/</link>
        <pubDate>Wed, 29 Oct 2025 14:42:31 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games104%E5%BC%95%E6%93%8E%E5%B7%A5%E5%85%B7%E9%93%BE/</guid>
        <description>&lt;blockquote&gt;
&lt;p&gt;讲了 GUI架构、Asset的序列化和反序列化、Redo undo实现、反射等技术&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;反射&#34;&gt;反射
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-29-15.18.45.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-29 15.18.45&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;为什么需要反射&#34;&gt;为什么需要反射
&lt;/h2&gt;&lt;p&gt;我定义一个定向光组件，刚开始有方向和强度，我想在GUI实时修改，就需要在imgui添加两个按钮来调整，但是定向光的属性越来越多，我并不希望每次都要修改imgui界面来修改，这种就可以让imgui拿到定向光的反射来自动生成&lt;/p&gt;
&lt;h2 id=&#34;c实现&#34;&gt;C++实现
&lt;/h2&gt;&lt;p&gt;Piccolo 借助 Clang AST，相当于直接复用了编译器的语法分析，通过在需要反射的类和属性上做标记，让clang AST分析后就会带上这个标记，从而知道了这些信息。 再下一步就是生成对应的反射类（就是自动生成的一些cpp文件，这些文件就包含了GetSet方法来设置原本被反射的类的信息&lt;/p&gt;
&lt;p&gt;这节内容主要是反射了解一下&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Games104：高级动画技术：动画树、IK和表情动画</title>
        <link>https://sdpyy1.github.io/p/games104%E9%AB%98%E7%BA%A7%E5%8A%A8%E7%94%BB%E6%8A%80%E6%9C%AF%E5%8A%A8%E7%94%BB%E6%A0%91ik%E5%92%8C%E8%A1%A8%E6%83%85%E5%8A%A8%E7%94%BB/</link>
        <pubDate>Sat, 25 Oct 2025 15:45:35 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games104%E9%AB%98%E7%BA%A7%E5%8A%A8%E7%94%BB%E6%8A%80%E6%9C%AF%E5%8A%A8%E7%94%BB%E6%A0%91ik%E5%92%8C%E8%A1%A8%E6%83%85%E5%8A%A8%E7%94%BB/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/%E6%88%AA%E5%B1%8F2025-10-20-15.21.05.png" alt="Featured image of post Games104：高级动画技术：动画树、IK和表情动画" /&gt;&lt;blockquote&gt;
&lt;p&gt;这节主要讲了动画在实践时需要的一些技术&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&#34;动画混合&#34;&gt;动画混合
&lt;/h1&gt;&lt;p&gt;在一个Clip内部，使用KeyFrame之间进行插值&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-15.47.17.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 15.47.17&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Clip之间进行插值，就涉及到如何设置两个Clip的权重，这里静态到走路可以用速度进行插值&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-15.48.58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 15.48.58&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Clip之间插值取哪一的KeyFrame进行插值呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-15.53.47.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 15.53.47&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;混合空间&#34;&gt;混合空间
&lt;/h1&gt;&lt;h2 id=&#34;1d2d混合空间&#34;&gt;1D/2D混合空间
&lt;/h2&gt;&lt;p&gt;一维混合就是类似从走到跑的混合，只需要一个速度变量就可以进行权重插值&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-15.56.04.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 15.56.04&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;二维混合空间就需要x,y两个参数来确定很多的动画的插值&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-16.03.49.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 16.03.49&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Delaunay Triangulation（德劳内三角剖分）：解决众多动画中那些动画参与插值（并不是所有动画都需要参与）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二维插值的问题&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设你有 2D Blend Space，点 &lt;code&gt;(x, y)&lt;/code&gt; 表示某个动作（如走、跑、斜向走）。&lt;/li&gt;
&lt;li&gt;当角色的当前参数 &lt;code&gt;(px, py)&lt;/code&gt; 落在这些点之间时，需要根据它周围的动画点计算混合权重。&lt;/li&gt;
&lt;li&gt;问题是：怎么快速找到 &lt;code&gt;(px, py)&lt;/code&gt; 所在的区域，并确定参与混合的动画点？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;三角剖分的思路&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把二维平面上的所有动画点用三角形连起来（Delaunay Triangulation），满足：
&lt;ul&gt;
&lt;li&gt;没有点在任意三角形的外接圆内（Delaunay性质）。&lt;/li&gt;
&lt;li&gt;三角形形状尽量接近等边三角形，避免“瘦长三角形”，这样插值更稳定。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;这样每个三角形的顶点就是 &lt;strong&gt;参与插值的动画点&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;skeleton-masked-blending&#34;&gt;Skeleton Masked Blending
&lt;/h2&gt;&lt;p&gt;用来解决两个动画分别作用于不同的骨骼的情况（一个动画控制上半身，一个动画控制下半身）&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-16.07.00.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 16.07.00&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;additive-blending&#34;&gt;Additive Blending
&lt;/h2&gt;&lt;p&gt;Blending做完后还可以再加一个修饰&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-16.09.21.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 16.09.21&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;动作状态机asm&#34;&gt;动作状态机（ASM）
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-16.12.59.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 16.12.59&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-16.14.59.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 16.14.59&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;动画混合树&#34;&gt;动画混合树
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-25-16.25.08.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-25 16.25.08&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;ik&#34;&gt;IK
&lt;/h1&gt;&lt;p&gt;这块东西在Games105详细记录过了&lt;/p&gt;
&lt;h1 id=&#34;面部动画&#34;&gt;面部动画
&lt;/h1&gt;&lt;p&gt;就是用很多的表情单元进行混合&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.48.12.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.48.12&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.50.24.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.50.24&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;直接混合也不行&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.50.42.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.50.42&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;真实存AU存的是一个表情相对于正常面部的便宜&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.51.26.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.51.26&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.52.53.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.52.53&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;动画重定向&#34;&gt;动画重定向
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.55.59.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.55.59&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-27-13.56.41.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-27 13.56.41&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Games104：游戏引擎的动画技术基础</title>
        <link>https://sdpyy1.github.io/p/games104%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E7%9A%84%E5%8A%A8%E7%94%BB%E6%8A%80%E6%9C%AF%E5%9F%BA%E7%A1%80/</link>
        <pubDate>Mon, 20 Oct 2025 15:00:44 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games104%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E7%9A%84%E5%8A%A8%E7%94%BB%E6%8A%80%E6%9C%AF%E5%9F%BA%E7%A1%80/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/%E6%88%AA%E5%B1%8F2025-10-20-15.21.05.png" alt="Featured image of post Games104：游戏引擎的动画技术基础" /&gt;&lt;h1 id=&#34;蒙皮动画&#34;&gt;蒙皮动画
&lt;/h1&gt;&lt;p&gt;这里讲的就是科普了一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.05.52.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.05.52&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;坐标系&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.09.11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.09.11&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;实际存储的数据是关节数据&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.16.00.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.16.00&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Root关节一般存在脚底，为了方便计算移速、跳跃高度。髋关节是第一个子关节&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.17.54.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.17.54&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;T-pose和A-pose：&lt;/p&gt;
&lt;p&gt;A-pose 下，肩关节旋转角度为 0°，肘部微弯 15°，手掌自然朝向大腿关节，旋转轴处于中性位置，绑定师可以更精确地绘制顶点权重，控制手臂摆动和肌肉变形，提高工作效率和质量。T-pose 中，肩部会受到挤压，当手臂放下时，可能会出现肌肉穿模等问题。A-pose 则能避免这种情况，其腋下、胯部等容易穿帮的区域自然展开，为后续的动画制作提供了更好的基础，减少了调整和修正的工作量。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.21.05.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.21.05&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;3d空间的旋转&#34;&gt;3D空间的旋转
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;这块东西在Games105数学原理博客部分已总结&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;欧拉角&#34;&gt;欧拉角
&lt;/h2&gt;&lt;p&gt;分别沿着三个轴旋转，旋转矩阵可以合并成一个&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.25.36.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.25.36&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;缺点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;顺序依赖&lt;/li&gt;
&lt;li&gt;万向锁，直接用公式也能解释，把y轴旋转带入后，整个旋转公式只剩下x轴旋转部分了，z轴的都没了&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.30.46.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.30.46&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;欧拉角的插值有问题&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.32.44.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.32.44&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;四元数&#34;&gt;四元数
&lt;/h2&gt;&lt;p&gt;在二维平面上可以用复数代表旋转。另外旋转的叠加可以直接通过Product运算算出&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.35.39.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.35.39&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;拓展到3D旋转&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.37.18.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.37.18&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;欧拉角转四元数&#34;&gt;欧拉角转四元数
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.41.55.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.40.44&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;用四元数旋转一个顶点&#34;&gt;用四元数旋转一个顶点
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.41.55.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.41.55&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;总结后：直接表达成旋转矩阵如下&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.46.58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.46.58&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;用四元数表达从u旋转到v&#34;&gt;用四元数表达从U旋转到V
&lt;/h3&gt;&lt;p&gt;可以通过下面的公式计算出对应的旋转四元数是多少&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.49.08.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.49.08&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;四元数表达给定轴的旋转&#34;&gt;四元数表达给定轴的旋转
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-20-15.50.10.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-20 15.50.10&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;蒙皮动画-1&#34;&gt;蒙皮动画
&lt;/h1&gt;&lt;h2 id=&#34;骨骼信息&#34;&gt;骨骼信息
&lt;/h2&gt;&lt;p&gt;旋转，大部分都是以旋转为主&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.26.58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.26.58&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;平移&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.27.31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.27.31&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;平移还是有用的，比如站立和蹲下是时Root和髋关节就发生了平移&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.28.10.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.28.10&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.28.42.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.28.42&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;放缩&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.29.24.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.29.24&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;所以Joint Pose就是，其中旋转矩阵可以通过四元数转化而来（注意这个变换不需要透视除法）&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.33.36.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.33.36&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;另外骨骼信息存储的都在局部坐标系，也就是说骨骼信息存储的实际是它相对于父骨骼的变换信息，而不是相对于整个模型。&lt;/p&gt;
&lt;p&gt;如果使用局部坐标系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只需更新父骨骼的变换，就能自动影响子骨骼。&lt;/li&gt;
&lt;li&gt;方便做&lt;strong&gt;层级动画&lt;/strong&gt;，比如抬手动作只需要改手臂局部变换，身体会自动跟随。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果存储在模型坐标系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个骨骼的世界变换必须单独存储或重复计算。&lt;/li&gt;
&lt;li&gt;当父骨骼旋转时，需要手动更新所有子骨骼的世界位置，&lt;strong&gt;计算复杂且容易出错&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;动画混合（比如走路 + 挥手）通常对&lt;strong&gt;局部骨骼变换&lt;/strong&gt;进行插值或加权，而不是对世界位置插值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;局部空间插值保持层次一致性，不会出现骨骼分离或错位。&lt;/li&gt;
&lt;li&gt;世界空间插值容易导致骨骼错位或姿势破坏，因为子骨骼的位置依赖父骨骼。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.37.41.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.37.41&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;蒙皮矩阵&#34;&gt;蒙皮矩阵
&lt;/h2&gt;&lt;p&gt;现在已经理解了骨骼信息如何存储，下面介绍骨骼信息如何作用到顶点上&lt;/p&gt;
&lt;p&gt;下图说明即使骨骼移动，顶点与骨骼的相对位置并不会变化&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.51.20.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.51.20&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;Bind Pose：默认姿态用于绑定骨骼和顶点&lt;/p&gt;
&lt;p&gt;下面三个参数分别是某个顶点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;绑定姿态下的模型空间位置&lt;/li&gt;
&lt;li&gt;绑定姿态下的局部空间位置&lt;/li&gt;
&lt;li&gt;绑定姿态下的骨骼在模型空间的位置&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;顶点相对于骨骼的位置在任何时候都不会变，并且等于骨骼在模型空间位置的逆矩阵 x 顶点在模型空间中的位置&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.52.37.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.52.37&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;顶点的位置 = BindPose下顶点位置先转到骨骼的局部坐标系再跟着骨骼运动后的位置&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-14.58.41.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 14.58.41&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;在存储骨骼信息时，需要存储Inverse BindPose Matrix，它是把变换矩阵一路从父节点传递下来，得到当前骨骼的模型坐标系下的变换信息，然后求逆。&lt;/p&gt;
&lt;p&gt;Inverse BindPose Matrix的作用是把一个在模型坐标系下的顶点坐标（bindpose下的）转移到这根骨骼上，所以不管任何时候，转移后得到的结果都应该是一样的&lt;/p&gt;
&lt;p&gt;把顶点信息（bindpose下）转移到骨骼后，再乘以现在的骨骼变换矩阵，就得到了顶点的现在的位置（实际位置），这样也就得到了一帧动画下这个顶点的位置&lt;/p&gt;
&lt;h2 id=&#34;蒙皮变换&#34;&gt;蒙皮变换
&lt;/h2&gt;&lt;p&gt;总结下：在骨骼蒙皮动画中，每个顶点在 &lt;strong&gt;Bind Pose（绑定姿态）&lt;/strong&gt; 下的模型空间位置，通过 &lt;strong&gt;Skinning Matrix&lt;/strong&gt;（即 $M_i \cdot B_i^{-1}$ 的加权和）进行变换后，就得到了该顶点在当前动画帧下的最终渲染位置。
&lt;/p&gt;
$$
v_\text{final} = \sum_i w_i \, (M_i \cdot B_i^{-1}) \, v_\text{bind}
$$&lt;ul&gt;
&lt;li&gt;$v_\text{bind}$ —— 顶点在绑定姿态下的模型空间坐标&lt;/li&gt;
&lt;li&gt;$B_i^{-1}$ —— 骨骼绑定姿态的逆矩阵（Inverse Bind Pose）&lt;/li&gt;
&lt;li&gt;$M_i$ —— 当前帧骨骼的变换矩阵&lt;/li&gt;
&lt;li&gt;$w_i$ —— 顶点对骨骼的权重&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外再乘上Model变换矩阵，就转移到世界坐标系，所以传递给GPU的变换信息应该是下面这个整体的变换公式&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.16.04.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.16.04&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多个骨骼影响的加权平均应该放在模型坐标系&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&#34;动画的插值&#34;&gt;动画的插值
&lt;/h1&gt;&lt;p&gt;Games105（2）已经详细记录&lt;/p&gt;
&lt;p&gt;四元数的NLearp插值的角速度不同&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.27.46.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.27.46&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;解决上述问题的办法&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.28.07.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.28.07&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;动画的runtime-pipeline&#34;&gt;动画的Runtime Pipeline
&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;计算运行时间&lt;/li&gt;
&lt;li&gt;计算插值&lt;/li&gt;
&lt;li&gt;计算骨骼信息&lt;/li&gt;
&lt;li&gt;计算蒙皮矩阵&lt;/li&gt;
&lt;li&gt;更新顶点信息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.29.42.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.29.42&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;动画压缩&#34;&gt;动画压缩
&lt;/h1&gt;&lt;p&gt;5S的动画数据要求2GB存储空间&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.31.58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.31.58&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;骨骼信息大量变化的都是旋转&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.35.11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.35.11&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/%e6%88%aa%e5%b1%8f2025-10-21-15.40.49.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;截屏2025-10-21 15.40.49&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES105 计算机角色动画基础（前向运动学、逆向运动学、关键帧动画与插值）</title>
        <link>https://sdpyy1.github.io/p/games105-%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%92%E8%89%B2%E5%8A%A8%E7%94%BB%E5%9F%BA%E7%A1%80%E5%89%8D%E5%90%91%E8%BF%90%E5%8A%A8%E5%AD%A6%E9%80%86%E5%90%91%E8%BF%90%E5%8A%A8%E5%AD%A6%E5%85%B3%E9%94%AE%E5%B8%A7%E5%8A%A8%E7%94%BB%E4%B8%8E%E6%8F%92%E5%80%BC/</link>
        <pubDate>Sun, 19 Oct 2025 14:25:28 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games105-%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%92%E8%89%B2%E5%8A%A8%E7%94%BB%E5%9F%BA%E7%A1%80%E5%89%8D%E5%90%91%E8%BF%90%E5%8A%A8%E5%AD%A6%E9%80%86%E5%90%91%E8%BF%90%E5%8A%A8%E5%AD%A6%E5%85%B3%E9%94%AE%E5%B8%A7%E5%8A%A8%E7%94%BB%E4%B8%8E%E6%8F%92%E5%80%BC/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/9cd6da17016c4d8bb0a71ba5e8f44d0c.png" alt="Featured image of post GAMES105 计算机角色动画基础（前向运动学、逆向运动学、关键帧动画与插值）" /&gt;&lt;h1 id=&#34;前向运动学&#34;&gt;前向运动学
&lt;/h1&gt;&lt;h2 id=&#34;单链&#34;&gt;单链
&lt;/h2&gt;&lt;p&gt;每个关节的局部坐标系与全局坐标系重合&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/30420a057d4b48d1ac427d38c5392200.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;父关节的旋转会让子关节也跟着旋转
&lt;img src=&#34;https://sdpyy1.github.io/487d233707f2467eb57598a1f1b105e9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
子关节的朝向就是夫关节朝向乘以自身的旋转
&lt;img src=&#34;https://sdpyy1.github.io/c5ef766273b74f7798dcde2d2431193f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
只旋转R0时,Q1在Q0的局部坐标系下的坐标并不会变一直是l0,如果想求Q1的世界坐标,就是父关节的世界坐标加朝向*l0
&lt;img src=&#34;https://sdpyy1.github.io/26526c4801494c039050d004bac0e624.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
这一套东西就是用传播来更新,比如Q4的局部坐标系的某一个点,可以算出他在Q3的局部坐标系的位置,Q3的位置又能计算出他在Q2的位置,最终得到这个点在Q0的位置,最终再换算到世界坐标系,就得到了Q4局部坐标系下某一点在全局坐标系下的位置
这块比较好理解
&lt;img src=&#34;https://sdpyy1.github.io/a3dd2947738740b8a3ed38487aace030.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
BVH文件,用欧拉角表示旋转
&lt;img src=&#34;https://sdpyy1.github.io/68e64229085948d2ac4ae0eae284d160.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;HIERARCHY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;ROOT&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RootJoint&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;主结点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;OFFSET&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;CHANNELS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Xposition&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Yposition&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Zposition&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Xrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Yrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Zrotation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;主节点的位置和旋转信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;JOINT&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lHip&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;主关节内的子关节&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;OFFSET&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.100000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.051395&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;子关节相对于主关节局部坐标系的偏移量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;CHANNELS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Xrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Yrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Zrotation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;欧拉角表示的旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;JOINT&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lKnee&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;子关节的子关节&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;一层一层嵌套&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;OFFSET&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.410000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;CHANNELS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Xrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Yrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Zrotation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;JOINT&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lAnkle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;OFFSET&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.390000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;CHANNELS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Xrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Yrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Zrotation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;JOINT&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lToeJoint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;OFFSET&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;  &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.050000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.130000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;CHANNELS&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Xrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Yrotation&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Zrotation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;End&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Site&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;OFFSET&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.010000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.002000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.060000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;一行MOTION信息按照上边给的关节顺序和通道数给出一帧的旋转信息&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;mf&#34;&gt;1.471305&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.917570&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;2.607916&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;  &lt;span class=&#34;mf&#34;&gt;45.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;45.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;   &lt;span class=&#34;mf&#34;&gt;0.000000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;前向运动学作业&#34;&gt;前向运动学作业
&lt;/h2&gt;&lt;h3 id=&#34;1-解析bvh文件&#34;&gt;1. 解析BVH文件
&lt;/h3&gt;&lt;p&gt;BVH存储的是每个关节相对于父关节的旋转(欧拉角)和位置偏移&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;67
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;68
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;69
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;70
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;71
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;72
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;73
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;74
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;part1_calculate_T_pose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bvh_file_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输入： bvh 文件路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输出:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_name: List[str]，字符串列表，包含着所有关节的名字
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_parent: List[int]，整数列表，包含着所有关节的父关节的索引,根节点的父关节索引为-1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_offset: np.ndarray，形状为(M, 3)的numpy数组，包含着所有关节的偏移量
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# 用于跟踪当前关节的父级层次&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# 根节点的父节点索引为-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 读取BVH文件内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bvh_file_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;strip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 定位层次结构部分（HIERARCHY到MOTION之间的内容）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;hierarchy_start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;hierarchy_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;enumerate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;HIERARCHY&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;hierarchy_start&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;MOTION&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hierarchy_start&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;hierarchy_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hierarchy_start&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;or&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;hierarchy_end&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;raise&lt;/span&gt; &lt;span class=&#34;ne&#34;&gt;ValueError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;BVH文件格式错误，未找到层次结构或运动数据部分&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 解析层次结构&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hierarchy_start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hierarchy_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;not&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ROOT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 处理根节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;JOINT&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 处理子关节&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;OFFSET&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 处理偏移量（T姿态下的位置偏移）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;END&amp;#39;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;upper&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;SITE&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 处理末端节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;end_joint_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;_end&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end_joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;}&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 层级结束，返回上一级父节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;pop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;current_parent&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent_stack&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 转换为numpy数组&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/b4c4695c429a408d894e4cac371cbc93.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;2-前向运动学计算&#34;&gt;2. 前向运动学计算
&lt;/h3&gt;&lt;p&gt;读入BVH文件中每一帧的动作数据, 计算每个关节在世界坐标系下的旋转和位置。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;part2_forward_kinematics&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;frame_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;请填写以下内容
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输入: part1 获得的关节名字，父节点列表，偏移量列表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        motion_data: np.ndarray，形状为(N,X)的numpy数组，其中N为帧数，X为Channel数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        frame_id: int，需要返回的帧的索引
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输出:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_positions: np.ndarray，形状为(M, 3)的numpy数组，包含着所有关节的全局位置
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_orientations: np.ndarray，形状为(M, 4)的numpy数组，包含着所有关节的全局旋转(四元数)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    Tips:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        1. joint_orientations的四元数顺序为(x, y, z, w)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        2. from_euler时注意使用大写的XYZ
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;bone_num&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 记录所有关节的全局位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bone_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 记录所有关节的全局旋转(四元数)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bone_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 一帧的所有旋转数据和主关节的位置信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;motion_data_oneFrame&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;frame_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 主关节的位置信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data_oneFrame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 主关节的旋转(从欧拉角转为四元数)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data_oneFrame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;frame_count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bone_num&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 当前关节的父关节id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;cur_rotate&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;_end&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# _end表示一条关节链的终点位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;cur_rotate&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;cur_rotate&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data_oneFrame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;frame_count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;frame_count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                      &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;frame_count&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 父关节的旋转信息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;p_orient&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 当前位置的旋转 = 父关节的旋转 * 当前关节的旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p_orient&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cur_rotate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 当前位置 = 父关节的全局位置 + 父关节的旋转 * 当前关节在父关节旋转方向上的偏移&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                         &lt;span class=&#34;n&#34;&gt;joint_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;3-运动重定向&#34;&gt;3. 运动重定向
&lt;/h3&gt;&lt;p&gt;读入一个A-Pose的文件, 将A-pose的bvh重定向到T-pose上
意思是 骨骼信息来自T-pose,但是一帧的旋转信息来自A-pose的文件.
&lt;code&gt;   retarget_motion_data = part3_retarget_func(T_pose_bvh_path, A_pose_bvh_path)&lt;/code&gt;用来把A-pose给出的旋转信息重定向到T-pose的骨骼上. 处理办法就是遍历到肩膀肘手腕时要额外处理旋转信息&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;67
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;68
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;69
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;70
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;part3_retarget_func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T_pose_bvh_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;A_pose_bvh_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    将 A-pose的bvh重定向到T-pose上
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输入: 两个bvh文件的路径
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输出: 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        motion_data: np.ndarray，形状为(N,X)的numpy数组，其中N为帧数，X为Channel数。retarget后的运动数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    Tips:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        两个bvh的joint name顺序可能不一致哦(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        as_euler时也需要大写的XYZ
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;index_bone_to_channel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;flag&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;t&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;end_bone_index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end_bone_index_t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;end_bone_index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end_bone_index_a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end_bone_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;end_bone_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end_bone_index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;get_t2a_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bone_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;l_bone&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;lShoulder&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lElbow&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lWrist&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;r_bone&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;rShoulder&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rElbow&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rWrist&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bone_name&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;l_bone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;45.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;bone_name&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r_bone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;45.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;load_motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A_pose_bvh_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;part1_calculate_T_pose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T_pose_bvh_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a_offset&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;part1_calculate_T_pose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;A_pose_bvh_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end_bone_index_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;_end&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;end_bone_index_t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;end_bone_index_a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;_end&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;end_bone_index_a&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;m_i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;frame&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;m_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;cur_frame&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;frame&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shape&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;cur_frame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;frame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t_i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;cur_bone&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;a_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a_name&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;index&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cur_bone&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;_end&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;channel_t_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index_bone_to_channel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;t&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;channel_a_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;index_bone_to_channel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# retarget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;frame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;channel_a_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;channel_a_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cur_bone&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;lShoulder&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lElbow&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lWrist&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rShoulder&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rElbow&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;rWrist&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;p_bone_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_parent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;Q_pi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_t2a_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;p_bone_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;Q_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_t2a_offset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cur_bone&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Q_pi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Q_i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_euler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;XYZ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                                                                                                 &lt;span class=&#34;n&#34;&gt;degrees&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;cur_frame&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;channel_t_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;channel_t_i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;m_i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cur_frame&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/9cd6da17016c4d8bb0a71ba5e8f44d0c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;逆向运动学&#34;&gt;逆向运动学
&lt;/h1&gt;&lt;p&gt;已知运动结果求每一个关节的旋转
&lt;img src=&#34;https://sdpyy1.github.io/523c2740ff8a489bbf301e897f189a11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
IK问题可以有解、无解、多解
&lt;img src=&#34;https://sdpyy1.github.io/5e31c5f9ee3d4b41bcbe530f51958de1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;双关节问题&#34;&gt;双关节问题
&lt;/h2&gt;&lt;p&gt;下图两个虚线圆的交点就是关节1应该去的位置，到达后旋转关节1，肯定能让关节2到达指定位置
&lt;img src=&#34;https://sdpyy1.github.io/09191a1412b7413cb8b4b24f031951c3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;另外一种思路：
&lt;img src=&#34;https://sdpyy1.github.io/9ee2f77744404313b353f6b4e0be5ba9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;移动关节1，让0-2的距离= 0到x的距离
&lt;img src=&#34;https://sdpyy1.github.io/dff6e1244c5e4a85a1e318edc2404993.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
再旋转关节0即可
&lt;img src=&#34;https://sdpyy1.github.io/e3984243737a40ee8295c55bd1c75fc2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在三维下绕着0x为轴旋转即可
&lt;img src=&#34;https://sdpyy1.github.io/46d4bc76f25f46fa9198e4e6408ad15a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;多关节最优化问题&#34;&gt;多关节最优化问题
&lt;/h2&gt;&lt;p&gt;只考虑末端点到达指定位置的问题，建模成 &lt;strong&gt;通过各个关节的旋转，得到末端点位置的这个函数&lt;/strong&gt;
&lt;img src=&#34;https://sdpyy1.github.io/f42d20f408024c66b807662f62c181c2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
求解问题就是：就是找到各关节的旋转使得目标点位置-公式求解的末端点位置=0
这个方程有没有根 &lt;img src=&#34;https://sdpyy1.github.io/a83a39c75e6d44b28505bc38fe51b669.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
等价的可以转化为一个优化问题，优化问题取极值
&lt;img src=&#34;https://sdpyy1.github.io/8d0ddd126ff34be3ad0ba4905bf04f05.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/daaebb3f845246a19d2ff1c00482f0d6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
求解问题到这里就变成了函数求极值的问题
&lt;img src=&#34;https://sdpyy1.github.io/59f12c68be1f4f8ca5caf06bc19e5b46.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;优化问题下降方法&#34;&gt;优化问题下降方法
&lt;/h3&gt;&lt;h4 id=&#34;1-循环坐标下降法-ccd&#34;&gt;1. 循环坐标下降法 CCD
&lt;/h4&gt;&lt;p&gt;认为参数的坐标轴方向为下降方向，不断交换坐标轴。 其实就是每次只更新一个参数（一个旋转角度）
&lt;img src=&#34;https://sdpyy1.github.io/7bab171e461a44c3bdc96608ae6d92cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
CCD放在IK来解释就是每次只旋转一个关节
下图就是只旋转3，让3-4朝向正确（也就是寻找了x离目标位置最近的位置）
&lt;img src=&#34;https://sdpyy1.github.io/b8103f4217204a0eb70cc6f78da8302d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;下一步旋转关节2
&lt;img src=&#34;https://sdpyy1.github.io/aaf51c0f1d8e4c7a88014c5f9cecb5c1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
依此类推
&lt;img src=&#34;https://sdpyy1.github.io/b31cd73e4a4b4100b70025bdf97d8cf0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
之后再回到4关节进行循环
&lt;img src=&#34;https://sdpyy1.github.io/8d495bf0c26e49e3b095a8899fe78f78.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h4 id=&#34;2--梯度下降雅可比转置法&#34;&gt;2.  梯度下降（雅可比转置法）
&lt;/h4&gt;&lt;p&gt;CCD没有考虑到目标函数的性质，直接找负梯度方向就可以更快的下降
&lt;img src=&#34;https://sdpyy1.github.io/a1bb1fda63424be8a95f5ee62979d73b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
也就是说每个参数的更新方向都是下降最快的方向&lt;img src=&#34;https://sdpyy1.github.io/e4ebf4f5fe21425f82803dda0393847b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
参数的更新公式
&lt;img src=&#34;https://sdpyy1.github.io/3c958e0be7a347f098c0f43a1badccab.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
红色的这一项是控制梯度更新是往正方向还是负方向（因为下降过头还得回来）
&lt;img src=&#34;https://sdpyy1.github.io/d5cbec56d7f04707b1036b396aef142f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
前边梯度的倒数构成的矩阵也可以叫做雅可比矩阵（f对每个参数求导的结果组成的矩阵）
&lt;img src=&#34;https://sdpyy1.github.io/086df6c95162428c814ec88e36cf060a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
下降梯度法也可以就雅可比转置法。
对于IK问题，他输出是一个点，是三维的输出（x,y,z）所以雅可比矩阵应该是三维的
&lt;img src=&#34;https://sdpyy1.github.io/b45b38c520104f5ba5bd8bcd6365baff.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/0d7028d05b824fc08e40b90d524b2c45.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h5 id=&#34;雅可比矩阵计算方法&#34;&gt;雅可比矩阵计算方法
&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;调库！！！&lt;/li&gt;
&lt;li&gt;有限差分（用前向运动学运动依次来获得数据填充矩阵，看不懂。。。。）
&lt;img src=&#34;https://sdpyy1.github.io/0bdd5647b2a84cc19081b7e3a4e65443.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;几何方法
（针对单自由度关节）这里应该是当一个关节旋转一个很小的角度时，把罗德里格斯旋转公式求出目标点，取极限求出来的就是雅可比矩阵的一项
&lt;img src=&#34;https://sdpyy1.github.io/9ca61d9917d745799f1546d4cd652f48.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
（针对ball 3自由度的关节）很难理解的，选择不理解，知道是可以的就行
&lt;img src=&#34;https://sdpyy1.github.io/346ecc4e5d2545a58626414074e7df36.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/b04f38a3641c4be99b353d7ae8b9e9fc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;3--高斯-牛顿法&#34;&gt;3.  高斯-牛顿法
&lt;/h4&gt;&lt;p&gt;最优化学过。。忘了，这里选择不回忆
&lt;img src=&#34;https://sdpyy1.github.io/6a1ebe08306a44dba0156749759741cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/aff67403181642ca985180be00ce10e7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h4 id=&#34;4--雅可比逆方法&#34;&gt;4.  雅可比逆方法
&lt;/h4&gt;&lt;p&gt;。。。听个响&lt;img src=&#34;https://sdpyy1.github.io/33850882bb744ab6a7088f7923a5b08e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
	
&gt;
听个。。
&lt;img src=&#34;https://sdpyy1.github.io/21260fddf90a4ea385c6696d49e9d117.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;角色的ik&#34;&gt;角色的IK
&lt;/h2&gt;&lt;p&gt;更复杂的优化问题。 每个目标点都对应上一节一个优化目标，最后相加。还需要加一个正则项，约束各个关节的旋转
&lt;img src=&#34;https://sdpyy1.github.io/3ac34f45d8f5453385d3d1406f6c5851.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;逆向运动学作业&#34;&gt;逆向运动学作业
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;  1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;  9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 67
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 68
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 69
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 70
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 71
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 72
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 73
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 74
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 75
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 76
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 77
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 78
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 79
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 80
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 81
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 82
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 83
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 84
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 85
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 86
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 87
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 88
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 89
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 90
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 91
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 92
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 93
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 94
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 95
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 96
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 97
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 98
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 99
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;100
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;101
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;102
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;103
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;104
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;105
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;106
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;107
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;108
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;109
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;110
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;111
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;112
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;113
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;114
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;115
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;116
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;117
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;118
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;119
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;120
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;121
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;122
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;123
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;124
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;125
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;126
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;127
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;128
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;129
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;130
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;131
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;132
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;133
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;134
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;135
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;136
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;137
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;138
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;139
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;140
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;141
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;142
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;143
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;144
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;145
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;146
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;147
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;148
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;149
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;150
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;151
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;152
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;153
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;154
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;155
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;156
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;157
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;158
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;159
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;160
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;161
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;162
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;163
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;164
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;165
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;166
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;167
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;168
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;169
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;170
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;171
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;172
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;173
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;174
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;175
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;176
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;177
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;178
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;179
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;180
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;181
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;182
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;183
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;184
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;185
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;186
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;187
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;188
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;189
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;190
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;191
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;192
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;193
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;194
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;195
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;196
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;197
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;198
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;199
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;200
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;201
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;202
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;203
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;204
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;205
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;206
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;207
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;208
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;209
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;210
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;211
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;212
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;213
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;214
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;215
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;216
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;217
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;218
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;219
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;220
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;221
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;222
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;223
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;224
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;225
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;226
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;227
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;228
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;229
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;230
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;load_motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bvh_file_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;part2 辅助函数，读取bvh文件&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;bvh_file_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;readlines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;startswith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Frame Time&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;float&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;line&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;reshape&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;concatenate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;axis&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;motion_data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;rotation_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cross&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 旋转矩阵是正交矩阵，矩阵的每一行每一列的模，都为1；并且任意两个列向量或者任意两个行向量都是正交的。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# n=n/np.linalg.norm(n)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 计算夹角&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;cos_theta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sin_theta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;theta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arctan2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sin_theta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cos_theta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 构造旋转矩阵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;theta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;theta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;rotation_matrix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;array&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                                &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rotation_matrix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;inv_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# return R.from_quat(data).inv()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;allclose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eye&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# return R.from_quat(data)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;allclose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;eye&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;part1_inverse_kinematics&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;meta_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_pose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    完成函数，计算逆运动学
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输入:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        meta_data: 为了方便，将一些固定信息进行了打包，见上面的meta_data类
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_positions: 当前的关节位置，是一个numpy数组，shape为(M, 3)，M为关节数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_orientations: 当前的关节朝向，是一个numpy数组，shape为(M, 4)，M为关节数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        target_pose: 目标位置，是一个numpy数组，shape为(3,)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    输出:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        经过IK后的姿态
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_positions: 计算得到的关节位置，是一个numpy数组，shape为(M, 3)，M为关节数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;        joint_orientations: 计算得到的关节朝向，是一个numpy数组，shape为(M, 4)，M为关节数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;    &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;meta_data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_path_from_root_to_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;meta_data&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_parent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# local_rotation是用于最后计算不在链上的节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;no_caled_orientation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;copy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;deepcopy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inv_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]])&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;local_position&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                      &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;local_position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;path_end_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;## lWrist_end 就是手掌 只是加了end不叫hand而已&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# k：循环次数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 正向的，path1是从手到root之前&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# idx：路径上的第几个节点了，第0个是手，最后一个是root&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;vec_to_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_end_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;vec_to_target&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_pose&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 获取end-&amp;gt;target的旋转矩阵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# debug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# rot_matrix=rotation_matrix(np.array([1,0,0]),np.array([1,1,0]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rotation_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vec_to_target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 计算前的朝向。这个朝向实际上是累乘到父节点的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;initial_orientation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 旋转矩阵，格式换算&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 计算后的朝向&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;calculated_orientation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initial_orientation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 写回结果列表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;calculated_orientation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 子节点的朝向也会有所变化&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# idx-1 就是当前节点的下一个更接近尾端的节点，一直向前迭代到1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 遍历路径后的节点,都乘上旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 修改子节点的位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# path_joint_id=path1[i]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 节点id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;next_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 指向下个节点的向量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;next_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 左乘，改变向量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 防止长度不对&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 还原回去&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;next_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# path2是从脚到root，所以要倒着&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# debug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# for idx in range(len(path2)-1,len(path2)-3,-1): # len(path2)-1 --&amp;gt; 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;# len(path2)-1 --&amp;gt; 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;parient_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;vec_to_end&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_end_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;vec_to_target&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_pose&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 获取end-&amp;gt;target的旋转矩阵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# debug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# rot_matrix=rotation_matrix(np.array([0.72,0.35,0]),np.array([0.5,0.35,0]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# rot_matrix=np.linalg.inv(rot_matrix)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rotation_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;vec_to_target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 计算前的朝向。注意path2是反方向的，要改父节点才行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;initial_orientation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 旋转矩阵，格式换算&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 计算后的朝向&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;calculated_orientation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initial_orientation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 写回结果列表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;calculated_orientation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 其他节点的朝向也会有所变化&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# idx-1 就是当前节点的下一个更接近尾端的节点，一直向前迭代到1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 遍历路径后的节点,都乘上旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;rot_matrix_R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])))&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;max&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 修改父节点，或者说更靠近手的那些节点的位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# path2上的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# path_joint_id=path1[i]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 节点id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;prev_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 指向上一个节点的向量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 左乘，改变向量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 防止长度不对&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 还原回去&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# path1上的&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# path_joint_id=path1[i]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 节点id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;prev_joint_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 指向上一个节点的向量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 左乘，改变向量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;rot_matrix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 防止长度不对&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next_dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vec_to_next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;# 还原回去&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;prev_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;calculated_vec_to_next&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_joint_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# debug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# rot_matrix=rotation_matrix(np.array([1,0,0]),np.array([1,0,1]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# joint_orientations[0]=R.from_matrix(rot_matrix).as_quat()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# joint_orientations[1]=R.from_matrix(rot_matrix).as_quat()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_end_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;cur_dis&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;norm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;path_end_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target_pose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cur_dis&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.01&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;距离&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cur_dis&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;迭代了&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;次&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 更新不在链上的节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;range&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 要单独处理，不然跟节点的-1就会变成从最后一个节点开始算&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 先获取局部旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 这里如果直接存的就是矩阵就会有问题？&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;local_rot_matrix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_rotation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 再获取我们已经计算了的父节点的旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;parent_rot_matrix&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 乘起来&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# re=local_rot_matrix.dot(parent_rot_matrix)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;re&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;parent_rot_matrix&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_rot_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;R&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;as_quat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 父节点没旋转的时候是：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;initial_o&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;from_quat_safe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;no_caled_orientation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 父节点的旋转*delta_orientation=子节点旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 反求delta_orientation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;delta_orientation&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;re&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linalg&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;inv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;initial_o&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;c1&#34;&gt;# 父节点的位置加原本基础上的旋转&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parent_idx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;delta_orientation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;local_position&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_positions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;joint_orientations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id=&#34;关键帧插值&#34;&gt;关键帧插值
&lt;/h1&gt;&lt;p&gt;给出一系列离散的值后，寻找一个f(x),首先满足已有的点都在f(x)上，另外要能计算其他的未给出的x的f(x)
&lt;img src=&#34;https://sdpyy1.github.io/4ab8167d086641ae88099a898c09df2e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;梯度函数&#34;&gt;梯度函数
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/c009bf351cde4c5fb7bb4a1e77f171a2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
也可以是离当前点最近的点的值作为f(x)
&lt;img src=&#34;https://sdpyy1.github.io/f5df7d93340e407fb4ed42fb0e09eebc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;线性插值函数&#34;&gt;线性插值函数
&lt;/h2&gt;&lt;p&gt;直接用直线连接已知点
&lt;img src=&#34;https://sdpyy1.github.io/406f498e5b85496cac6c4e98ba6e49dc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/15117e860a2142ac9245283827ace5cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;线性插值的平滑性&#34;&gt;线性插值的平滑性
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/d51108c9114540598f2f95a026d9388f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/deb1999fb8824fef9509505dba7418bb.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;多项式插值-polynomial-interpolation&#34;&gt;多项式插值 Polynomial Interpolation
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/5ddbccd8457e4d459972ecffc786d58a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
计算参数就是把已知点带进去
&lt;img src=&#34;https://sdpyy1.github.io/d372c045005b4bdbb0bb6d60f171b9c4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
然后用矩阵求解。 这里就能看出需要多少个采样点
&lt;img src=&#34;https://sdpyy1.github.io/18e43c512d2346d9a17e8ae3bdafa8e7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
把矩阵求逆就可以求出参数a
&lt;img src=&#34;https://sdpyy1.github.io/b7ad98fd79554f44a457f438aac0a76b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runge&amp;rsquo;s phenomenon&lt;/strong&gt;：当使用等距节点对某些光滑函数进行高次多项式插值时，随着插值多项式次数的增加，多项式在区间端点附近会出现剧烈的振荡，导致插值误差不仅不减小，反而会急剧增大，最终完全偏离原函数。
下图靠近两边的点中间插值出现了非常巨大的震荡（原因是接近1时，x的高次方会变得很大，如果还是使用&lt;strong&gt;等距插值&lt;/strong&gt;就会出问题）
&lt;img src=&#34;https://sdpyy1.github.io/01cf9890b6474a47a01f866482512ae6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;样条插值-spline-interpolation&#34;&gt;样条插值 Spline Interpolation
&lt;/h2&gt;&lt;p&gt;只在单独几个采样点上用低阶多项式插值
&lt;img src=&#34;https://sdpyy1.github.io/fcd34f02bada4671bacb8287547a5cd1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
常用的是Cubic Splines （三次样条插值）
&lt;img src=&#34;https://sdpyy1.github.io/3fab4c877b7b47a7bbb8057296033dad.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
计算时只需要相邻的两个采样点计算依次多项式参数。
如果总采样点是N+1，那一共有N段，每段都是一个三次多项式。每个多项式4个未知参数。所以一共有4N个未知参数。
&lt;img src=&#34;https://sdpyy1.github.io/4790b9f7979c487f803d56a18b773c99.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
2个采样点信息没办法求出4个未知数。样条采样引入了别的规则。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;两段之间的采样点的在两个多项式的导数相同&lt;/li&gt;
&lt;li&gt;二阶导也相同&lt;/li&gt;
&lt;li&gt;对整条曲线的边缘点的一阶二阶导数一些额外限制&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;三次埃尔米特样条-cubic-hermite-splines&#34;&gt;三次埃尔米特样条 Cubic Hermite Splines
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Cubic Splines问题是只要移动一个点，都可能发生整条曲线的变化，求解也比较昂贵&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cubic Hermite Splines的做法是除了给出两个采样点外，还需要额外告诉两个点的导数&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/ad7da6c9a3b54b8d8e10b14875a936cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
总结就是 4个方程4个未知数
&lt;img src=&#34;https://sdpyy1.github.io/431e38372bf44abaa65f519f88fad3d0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/f243ca6de7ea4264840c08951b771522.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
矩阵的逆都一步到位了，一次矩阵乘法就直接求解4个位置系数
&lt;img src=&#34;https://sdpyy1.github.io/9f814d58223a4ccfb2b3fa816ffdd56c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
还可以把多项式直接写出矩阵形式
&lt;img src=&#34;https://sdpyy1.github.io/91e86cc0e00d461d9a3ba6f0041721c5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;旋转的插值&#34;&gt;旋转的插值
&lt;/h2&gt;&lt;p&gt;旋转的四种表示方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;旋转矩阵&lt;/li&gt;
&lt;li&gt;欧拉角&lt;/li&gt;
&lt;li&gt;轴角&lt;/li&gt;
&lt;li&gt;四元数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面两种旋转表达旋转速度插值不是恒定的
&lt;img src=&#34;https://sdpyy1.github.io/72fbd1c6d1c74e8ca344732c2c35fe8e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
用四元数的slerp来恒定速度（但线性插值并单位化四元数来插值不恒定）&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES105 计算机角色动画基础（数学基础）</title>
        <link>https://sdpyy1.github.io/p/games105-%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%92%E8%89%B2%E5%8A%A8%E7%94%BB%E5%9F%BA%E7%A1%80%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80/</link>
        <pubDate>Sun, 19 Oct 2025 14:24:24 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games105-%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%92%E8%89%B2%E5%8A%A8%E7%94%BB%E5%9F%BA%E7%A1%80%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/697bf32a43404daeadb64e6c91c121f0.png" alt="Featured image of post GAMES105 计算机角色动画基础（数学基础）" /&gt;&lt;h1 id=&#34;线性代数&#34;&gt;线性代数
&lt;/h1&gt;&lt;h2 id=&#34;向量&#34;&gt;向量
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/fb83138d7956486cb1748166b3de3f4d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;点乘-dot-product&#34;&gt;点乘 Dot Product
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/32ad6a4c37154960a5ba407341b77c52.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;叉乘-cross-product&#34;&gt;叉乘 Cross Product
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/d7c4056ffe324e3c9318ad447a4040e0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
叉乘作用:寻找同时与AB都垂直的方向(AB平面的法线方向)&lt;/p&gt;
&lt;h3 id=&#34;向量旋转叉乘应用&#34;&gt;向量旋转(叉乘应用)
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/12c919dcfe1842aaa671849798f8e6ea.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
理论上在ab两个向量的角平分面上任何一个方向都可以作为旋转轴来进行旋转
&lt;img src=&#34;https://sdpyy1.github.io/0205672ffc514736a7e7efd51f4fdfba.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;strong&gt;这时候找ab叉乘方向作为轴进行旋转,旋转最少&lt;/strong&gt;
&lt;img src=&#34;https://sdpyy1.github.io/9cea876219ce4047878cd1f38e87cd71.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h4 id=&#34;如何旋转一个向量&#34;&gt;如何旋转一个向量
&lt;/h4&gt;&lt;p&gt;例如绕着u为轴进行旋转
&lt;img src=&#34;https://sdpyy1.github.io/ad08bc4c5cfb4c21b40cfdba0d369c43.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
可以把旋转拆分成两部分
&lt;img src=&#34;https://sdpyy1.github.io/61fae3dcd0204da4aad549c3ee2d6771.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
v方向是 UcrossA的方向(其实就是圆盘的切线方向,也就是说叉乘可以求切线方向),t方向是UcrossV(与切线垂直)
从俯视图来看
&lt;img src=&#34;https://sdpyy1.github.io/636ae78154a24416b670d568f3954615.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
这个推导结果就是&lt;strong&gt;Rodrigues&amp;rsquo;s rotation formula(罗德里格斯旋转公式)&lt;/strong&gt;
&lt;img src=&#34;https://sdpyy1.github.io/6315cc28c88c4d3cac6f2b52fd071225.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;正交基&#34;&gt;正交基
&lt;/h3&gt;&lt;p&gt;正交基的要求
&lt;img src=&#34;https://sdpyy1.github.io/3dc83de22bf848a48592fe057885e85c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
一个向量可以用正交基的线性组合来表示&lt;img src=&#34;https://sdpyy1.github.io/f77dfa63b358474db12660fe64cf322d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
两个向量的点乘可以展开
&lt;img src=&#34;https://sdpyy1.github.io/c8930d9c53d64b06bef16e4b40d23e33.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
最后一项为0了
&lt;img src=&#34;https://sdpyy1.github.io/73a8917905cf4d8dbcc3c13d751aaf52.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
叉乘也可以,去掉叉乘自己=0的项,化简后
&lt;img src=&#34;https://sdpyy1.github.io/a00107d9a5ba4065907eb65159564bf9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;矩阵&#34;&gt;矩阵
&lt;/h2&gt;&lt;p&gt;一些会用到的特殊矩阵(单位/对角/对称/反对称)
&lt;img src=&#34;https://sdpyy1.github.io/d71d9c449b6d40078da65ed98d47cb96.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;转置&#34;&gt;转置
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/b6c0daee7ce940de8906ff8e6067385c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;运算&#34;&gt;运算
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/c6f8d89202994454b60c0d757010c953.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
一些运算工具
&lt;img src=&#34;https://sdpyy1.github.io/67e7006b26a24d7e86e62f846ee11776.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
叉乘计算可以用矩阵来表示,这里的a用一个反对称阵来表示了
&lt;img src=&#34;https://sdpyy1.github.io/4e01ede975794ecb872e79f4f946ea22.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
把叉乘计算转为矩阵运算后,旋转一个向量的罗德里格斯旋转公式可以表示为Ra,这里的R就是空间中沿着某个单位轴u,旋转的旋转矩阵 &lt;img src=&#34;https://sdpyy1.github.io/3b312865659644aa80a6e31f39653bf4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;正交矩阵&#34;&gt;正交矩阵
&lt;/h2&gt;&lt;p&gt;定义:矩阵的每一列都是互相正交的向量
&lt;img src=&#34;https://sdpyy1.github.io/87508c370a934d67972cb1f99d33f23b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
正交阵的逆=正交阵的转置    正交阵转置*正交阵 = 单位阵
&lt;img src=&#34;https://sdpyy1.github.io/9de404ba5a344656a10f05f3f3ebde73.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;矩阵的行列式&#34;&gt;矩阵的行列式
&lt;/h2&gt;&lt;p&gt;三×四列矩阵的行列式怎么算😄
&lt;img src=&#34;https://sdpyy1.github.io/a13710f87d9343f9ad498d92259a9697.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
行列式的性质
&lt;img src=&#34;https://sdpyy1.github.io/a2b16e828939415ba96c6e436e7e125e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
叉乘同样可以用行列式来计算&lt;img src=&#34;https://sdpyy1.github.io/4c3d7311239c4a70b46459e48f0b0aec.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;特征值&#34;&gt;特征值
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/4077c06db4414891bb73f2c6f59db989.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
比较重要的结论是,3x3的正交阵肯定有一个特征值是1或-1
&lt;img src=&#34;https://sdpyy1.github.io/78b74e7b6c284a2aa2b600a95942dbc7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;刚体变换-rigid-transformation&#34;&gt;刚体变换 Rigid Transformation
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/8d8f84e5267f4e509620f721791476ab.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;缩放&#34;&gt;缩放
&lt;/h2&gt;&lt;p&gt;相当于把向量乘以一个对角阵,每一行表示缩放比例
&lt;img src=&#34;https://sdpyy1.github.io/78a50fdf420d48208511eb5192a72864.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;平移&#34;&gt;平移
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/697bf32a43404daeadb64e6c91c121f0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
平移可以直接加
&lt;img src=&#34;https://sdpyy1.github.io/4fc2cdc1cce24246ac7cc3dffb31b9d8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;旋转&#34;&gt;旋转
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/abebfc1d75b74793a98906dfdab30440.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
旋转矩阵有一些性质.旋转矩阵是正交阵,行列式一定是+1,刚性变化,不会改变向量的模长
&lt;img src=&#34;https://sdpyy1.github.io/8b695c3c0f064d3abb46dc1a7b27a7c5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
旋转的组合,毕竟是左乘肯定得反着写
&lt;img src=&#34;https://sdpyy1.github.io/44b7f04c70cd443f8d55b1ce01e4ba26.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
沿着坐标轴旋转的旋转矩阵
&lt;img src=&#34;https://sdpyy1.github.io/2ad8acd7690045ebb63a747aced47200.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
分别沿着3个轴旋转结果,与沿着某个计算出来的轴旋转可以得到一样的结果&lt;img src=&#34;https://sdpyy1.github.io/358390ab9bf24cbda1b31726c4bc040c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
旋转矩阵肯定有一个特征值为1的特征值. RU=1*U(特征值的概念),这里的特征向量U在左乘R后保持不变,可以理解为U方向就是旋转轴
&lt;img src=&#34;https://sdpyy1.github.io/b3f6cff754de4c81a2800035b4ad3cf1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
旋转轴和旋转角度可以通过各种线性代数公式推导出来.R-1=RT所以左右同乘以RT.得到某个反对称阵 * 对称轴U = 0.
&lt;img src=&#34;https://sdpyy1.github.io/408c478f2fc34766b3b28c616f45d9b0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
反对称阵可以写成叉乘
&lt;img src=&#34;https://sdpyy1.github.io/2c2ce7416ff7446e98228ca09f358316.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
叉乘=0表示两个向量共线
&lt;img src=&#34;https://sdpyy1.github.io/cae05423d252482688692d89db769df9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以说这个反对称阵对应的叉乘的u&amp;rsquo;单位化后就是对称轴.谁想出来的😄&lt;/p&gt;
&lt;h2 id=&#34;坐标转换&#34;&gt;坐标转换
&lt;/h2&gt;&lt;p&gt;全局和模型坐标系的转换&lt;img src=&#34;https://sdpyy1.github.io/eec68540dcce403bb344330c63b97df5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;3d旋转&#34;&gt;3D旋转
&lt;/h1&gt;&lt;p&gt;旋转的插值不能线性插值.下面这种插值是错的
&lt;img src=&#34;https://sdpyy1.github.io/83709e62216641c38aadf97153876a57.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/df027292243b4907bf8f1a5cde6a427d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
得出结论,用旋转矩阵来表达旋转时的缺点
&lt;img src=&#34;https://sdpyy1.github.io/8a48d5d5d9e340079c2d88d082dc4e7d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;欧拉角euler-angles来表示旋转&#34;&gt;欧拉角（Euler Angles）来表示旋转
&lt;/h2&gt;&lt;p&gt;欧拉角（Euler Angles）是 3D 旋转中最直观的表示方式（通过绕三个正交轴的依次旋转角度定义）
&lt;img src=&#34;https://sdpyy1.github.io/3cf4aed0958c4a3e8f014421b0deabb1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
但它存在一个致命缺陷 ——万向锁（Gimbal Lock，也称 Gimbal Lock）
&lt;img src=&#34;https://sdpyy1.github.io/d09c49a161cc4bc18748bb109f3596e1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
万向锁把中间变换设置为90°时,另外两个旋转变换都只会在同一个方向上进行旋转,这就是万向锁. &lt;br&gt;
&lt;code&gt;https://www.bilibili.com/video/BV1Nr4y1j7kn/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=9df9034e2f1978b1018f5b387ec3eacd&lt;/code&gt;解释的很好,其实调整第一个轴,结果却在最后一个轴上旋转的原因是: 调整第一个轴本来就是在第一个轴上旋转,但是调整后又经过第二个轴的旋转,把他带到了第三个轴的旋转上看上去就像是在第三个轴旋转.&lt;/p&gt;
&lt;p&gt;欧拉角的优缺点:
&lt;img src=&#34;https://sdpyy1.github.io/2a6497721ea64aedbbdbfc0366445358.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;旋转轴和旋转角度来表示旋转&#34;&gt;旋转轴和旋转角度来表示旋转
&lt;/h2&gt;&lt;p&gt;前边说过了
优缺点:
&lt;img src=&#34;https://sdpyy1.github.io/cbf41520915546318cfd98eef13bd375.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;四元数-quaternions&#34;&gt;四元数 Quaternions
&lt;/h2&gt;&lt;h3 id=&#34;复数与二维旋转&#34;&gt;复数与二维旋转
&lt;/h3&gt;&lt;p&gt;复数表示为&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/2aafe47dcd0e4baab5ea329024b45de8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
复数用向量来表示就是
&lt;img src=&#34;https://sdpyy1.github.io/eb3517f19a1b49499da65e40fd251811.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
复平面:x轴为实轴,y轴为虚轴
&lt;img src=&#34;https://sdpyy1.github.io/8f9dd344a8ad462b9a194f42ef381962.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
复数的乘法:
&lt;img src=&#34;https://sdpyy1.github.io/1fd4859fd1104d219c6d542df7e9d523.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
可以看出复数的可以写出一个矩阵和一个向量的结果.矩阵代表z1,向量代表z2.
也就是说&lt;strong&gt;z1乘以z2等价于给z2左乘一个变换矩阵&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把表示z1的这个变换矩阵做一个变形
&lt;img src=&#34;https://sdpyy1.github.io/3a9e36afe811439fb6d93e76c641df19.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
从下图可以看出根号下平方和表示这个复数的模长
&lt;img src=&#34;https://sdpyy1.github.io/13d1909759a349e9a67981bc6464aff8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
并且&lt;img src=&#34;https://sdpyy1.github.io/0e8719c89d114dcb82d768acc6fe434e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/2858b395fd4e4b3faa9129e27d27b72e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
带入刚才的矩阵
&lt;img src=&#34;https://sdpyy1.github.io/f8c8f4263ab942be87be2d794f8df2b5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
左边部分是一个缩放矩阵,右边刚好是2D旋转公式.  &lt;strong&gt;所以说一个复数可以看作是先旋转角度再缩放的变换矩阵&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;所以 我们要对一个向量旋转时,先把他看成一个复数&lt;img src=&#34;https://sdpyy1.github.io/292e796c0f664e8ca93bdb1f4fd80d7d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
给他左乘一个复数就能达到旋转效果
&lt;img src=&#34;https://sdpyy1.github.io/cf8aac76a1334806b9da7367624f5271.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
或者可以理解为2D旋转公式&lt;img src=&#34;https://sdpyy1.github.io/d19452b13b7c49c982e251f247137fff.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
中的这个旋转矩阵是可以写出复数形式的&lt;img src=&#34;https://sdpyy1.github.io/f76b61c59291499badafd9f1c93a4dd1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;三维旋转&#34;&gt;三维旋转
&lt;/h3&gt;&lt;p&gt;轴角式:绕着某个旋转轴进行旋转
欧拉角:就是上边讲的那个,用它的缺点就是有万向锁,四元数来解决它
&lt;img src=&#34;https://sdpyy1.github.io/7d41248d5b344218b3106f94c9ae9e58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在轴角表示法中,确定一个旋转需要一个旋转轴(3个自由度)旋转角度(1个自由度),四个自由度来表示&lt;/p&gt;
&lt;p&gt;旋转分解:把向量v分解为平行于旋转轴和垂直于旋转轴的两个分量&lt;img src=&#34;https://sdpyy1.github.io/40848779533b495284577555a2ac9d1f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/c43adcf985b44bf4b555b3bbff85468a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
分解过程有具体公式实现
&lt;img src=&#34;https://sdpyy1.github.io/b500c3c7c083418bb651ca1e1ea2ca67.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/84dd75694c1f49a0a0260ac8f72f1931.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
下面就只需要分别讨论两个分量的旋转
对于垂直分量,就是在底面投影形成的一个圆形上的旋转,w可以通过叉乘获得
&lt;img src=&#34;https://sdpyy1.github.io/c678650860c74c29bcebfa5b23261f65.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在底盘上可以把旋转结果分解到垂直的两个方向上&lt;img src=&#34;https://sdpyy1.github.io/c4896fabece7444d884392261d1233c7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
对于平行分量来说,它并没有旋转,所以&lt;img src=&#34;https://sdpyy1.github.io/8a80cc17db4f4210acca4b1a4d29191c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
最终组合两个分量并化简
&lt;img src=&#34;https://sdpyy1.github.io/08991e86251d49f38b642ab3c232b297.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/58c4c7e15c9245a6bf8edb74657f97d0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
额,其实就是上边讲的Rodrigues旋转公式(⊙﹏⊙)&lt;/p&gt;
&lt;h3 id=&#34;四元数定义&#34;&gt;四元数定义
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/4d7104b9b0b14cf298086f0163f3b769.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
四元数的性质:
&lt;img src=&#34;https://sdpyy1.github.io/6b1362d9806142e4bdcb64e2aee6049c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/2d01895bcc874673b5176b496e83b551.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
四元数可以写出一个向量
&lt;img src=&#34;https://sdpyy1.github.io/7730b7c705234a4092328c578943ade0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
可以用四元数来表示向量和标量
&lt;img src=&#34;https://sdpyy1.github.io/a262db4aa5274a9b970df4e22d8ad539.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
用向量表示后,乘法就表示为
&lt;img src=&#34;https://sdpyy1.github.io/882e517993314792b980ecd83ad2190e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
四元数没有交换律,有结合律
&lt;img src=&#34;https://sdpyy1.github.io/2506fdbdcfff47c6a06ed8068265fbbf.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
单位四元数的逆=共轭
&lt;img src=&#34;https://sdpyy1.github.io/24cd2d7708904ac2b3c41bee9b07560f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
类似于单位复数可以组成复平面的一个圆,单位四元数也可以组成4D空间的球壳
&lt;img src=&#34;https://sdpyy1.github.io/3b196a4e3b4442cbbde5dba693f8accc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
对于任何一个单位四元数都可以写出下面这个形式
&lt;img src=&#34;https://sdpyy1.github.io/c6d971f9da1942398c80ae8567d82db8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;strong&gt;它和轴角表示的u和斯塔有相同的信息量.这样的对应可得 一个轴角表示可以转化为一个四元数表示&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果把一个需要被旋转的向量表示为纯四元数(标量=0),那么旋转q作用到它的计算方式为
&lt;img src=&#34;https://sdpyy1.github.io/f59990a1db6b4fc4ba9c74d75d308437.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
四元数可以旋转叠加
&lt;img src=&#34;https://sdpyy1.github.io/fa71d31b0db54b6faf6973bba1f9bfd2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;四元数的插值&#34;&gt;四元数的插值
&lt;/h3&gt;&lt;p&gt;在球壳上才是合法的单位四元数,所以线性插值的qt是不合法的
&lt;img src=&#34;https://sdpyy1.github.io/827f7dd64379433e9c106bad81570ca3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以每次插值都需要进行单位化,但是插值速度是不恒定的&lt;img src=&#34;https://sdpyy1.github.io/bed468d5fbb04da9a6ba3ea78d7fd821.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
为了实现常数速度的插值,需要用slerp
&lt;img src=&#34;https://sdpyy1.github.io/601526dbe5484eb9a29458b2f28c6167.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
其中a和b可以推导出来
&lt;img src=&#34;https://sdpyy1.github.io/79632e1c113944f6b48114f3cfeb9df0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
四元数的优点
&lt;img src=&#34;https://sdpyy1.github.io/86c8f18c7fe64417b8062bb2b2f3ec18.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES202 GAMES202 高质量实时渲染（Real-Time Ray-Tracing）</title>
        <link>https://sdpyy1.github.io/p/games202-games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-ray-tracing/</link>
        <pubDate>Sun, 19 Oct 2025 14:02:08 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games202-games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-ray-tracing/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/39f6ae2e6aa04864b9165387c73282ae.png" alt="Featured image of post GAMES202 GAMES202 高质量实时渲染（Real-Time Ray-Tracing）" /&gt;&lt;h1 id=&#34;rtx&#34;&gt;RTX
&lt;/h1&gt;&lt;p&gt;RTX实际做的事：
一个像素用一个样本来采样SPP&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/a76c532017ff4b1a99a8e89540ff0a1d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
考虑了一次弹射，第一次primary Ray其实没有必要（从摄像机到着色点），只需要做一遍光栅化就可以实现一整个屏幕的primaryRay。所以一个SPP只需要考虑3条光线
&lt;img src=&#34;https://sdpyy1.github.io/4ce1bb095aad48f196e68d8c98b20e33.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;降噪&#34;&gt;降噪
&lt;/h1&gt;&lt;h2 id=&#34;时间滤波&#34;&gt;时间滤波
&lt;/h2&gt;&lt;p&gt;1SPP噪声非常大。实时光线追踪在算法上并没有区别，主要原因是硬件的突破，让光线追踪达到了实时水平。所以&lt;strong&gt;实时光线追踪&lt;/strong&gt;最关键的技术是&lt;strong&gt;降噪&lt;/strong&gt;！&lt;img src=&#34;https://sdpyy1.github.io/39f6ae2e6aa04864b9165387c73282ae.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
从这幅图可以看出，降噪的效果非常好。降噪的方法有很多，但是应用到实时的降噪技术很少
&lt;img src=&#34;https://sdpyy1.github.io/f982631b992146b097864633b0a53ae3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
实时降噪使用的是时间上的滤波。当前帧需要前一帧已经滤波。假设运动是连续的。有一个vector叫做motion vector，记录每个点上一帧的位置
&lt;img src=&#34;https://sdpyy1.github.io/2d793638b6b84714a60774e35e9124e8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
也就是当前帧用的spp，使用了上一帧的spp，上一帧的spp也用了上上帧的spp，所以作用到当前帧的spp，远大于1。
下面首先介绍一个概念，G-buffer，这东西就是延迟渲染用的
&lt;img src=&#34;https://sdpyy1.github.io/7a4368e284a44c3382c58232585bfa11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;back-projection&#34;&gt;Back projection
&lt;/h2&gt;&lt;p&gt;求一个像素的内容，在上一帧中的位置
&lt;img src=&#34;https://sdpyy1.github.io/f3910dd985d84359b1377ce7f496129e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
首先求一个点的世界坐标：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以提前存储在G-buffer&lt;/li&gt;
&lt;li&gt;通过MVP+viewport的逆变换变回世界坐标&lt;/li&gt;
&lt;li&gt;我们是知道每个世界坐标是如何变换的，所以通过逆变换来找到这个坐标上一帧的坐标，再把世界坐标转为屏幕坐标即可
&lt;img src=&#34;https://sdpyy1.github.io/b0532bcdccf24008b291f482d0dac78d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
得到上一帧对应像素的像素值，进行线性插值，通常a取0.1-0.2，也就是80%以上都是上一帧得到的
&lt;img src=&#34;https://sdpyy1.github.io/312bd52061794f478d59867c74c47197.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下面对比一下groundtruth和降噪结果&lt;img src=&#34;https://sdpyy1.github.io/5a0a29863d87499ea0a2d9c70128d317.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/1a8470c295ea41ddb3a3ab9342f15e9b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
有一些该暗的地方仍然是亮的。
基于时间的滤波是有问题的，有一下几点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;第一帧/切换场景
&lt;img src=&#34;https://sdpyy1.github.io/34f34ea233b94fe7b59789340e54eb03.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当前帧的物体在上一帧不存在&lt;img src=&#34;https://sdpyy1.github.io/b6c4a8ff9d2c43de8bdcc3e4e75c5cf8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;当前帧的物体在上一帧被遮挡了（G-buffer中没有存储）&lt;img src=&#34;https://sdpyy1.github.io/32e4a578f958445e9a7da52e98fad6dd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
会出现脱尾&lt;img src=&#34;https://sdpyy1.github.io/d8478f9beef441be9e6d241da1ef74a6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解决思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;混合当前帧和上一帧数据之前，把上一帧的结果拉到当前帧周围&lt;/li&gt;
&lt;li&gt;检测要不要用上一帧的数据
&lt;ol&gt;
&lt;li&gt;检测上一帧位置是否为同一个物体&lt;/li&gt;
&lt;li&gt;优化混合系数&lt;/li&gt;
&lt;li&gt;增大空间滤波
但是这样处理噪声又出现了
&lt;img src=&#34;https://sdpyy1.github.io/eaed0634083748f4ba7b4fef2031dbb3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;滤波实现&#34;&gt;滤波实现
&lt;/h1&gt;&lt;h2 id=&#34;空间滤波&#34;&gt;空间滤波
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/2fe0dce053d6484e87ca84dea99e5032.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
滤波操作为低通滤波，保留低频信息，移除高频信息&lt;/p&gt;
&lt;h3 id=&#34;高斯滤波&#34;&gt;高斯滤波
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/226d14370a9843d3a3990a43137bd398.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;双边滤波考虑边界&#34;&gt;双边滤波（考虑边界）
&lt;/h3&gt;&lt;p&gt;高斯滤波会把图像均匀地糊掉，边界也会模糊 ，公式的第二项表示如果两边颜色差值很大，贡献就会变小
&lt;img src=&#34;https://sdpyy1.github.io/18f01a6f9ec3434083c55e212ecc4a96.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;联合双边滤波&#34;&gt;联合双边滤波
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/bc05c831d26444b1b7f2be94bd0ac6c7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
使用G-buffer来更好地指导滤波&lt;img src=&#34;https://sdpyy1.github.io/34a42118bb1343dc86076ffb5307f9b7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/df173782d37d40e4b5a69b112cc64e7a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
用深度、颜色、法向量来指导滤波
&lt;img src=&#34;https://sdpyy1.github.io/7fc20e6cb0294e45ae804b081003ac66.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如何实现大的滤波核呢？先水平再垂直，效率从&lt;img src=&#34;https://sdpyy1.github.io/d32d0c15a5d6426abed34653ee5cc8e9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/2fc21a8857784f5992c7f7fb25f697b3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Progressively Growing Sizes&lt;img src=&#34;https://sdpyy1.github.io/05056902573f4822b28b48d11e3b2979.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;rtrt滤波解决方案&#34;&gt;RTRT滤波解决方案
&lt;/h1&gt;&lt;h2 id=&#34;svgf&#34;&gt;SVGF
&lt;/h2&gt;&lt;p&gt;3个factors指导的滤波&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;深度
&lt;img src=&#34;https://sdpyy1.github.io/470cbbaec95148b7896ec4f297fddd3a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;- 法线
&lt;img src=&#34;https://sdpyy1.github.io/e1cc43f114e2421f920259a9a661fefc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;颜色差异
&lt;img src=&#34;https://sdpyy1.github.io/3cd3d3f53d6044eea50c50b365e89ac1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;rae&#34;&gt;RAE
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/94d5daff4fca4be4bfdc1c6831e344d9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/f3dfd86f3ee04bfdb949699ae8009b3f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/dd1e1b9eeb6449a0b8a54f2d458dc156.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;工业界问题及解法&#34;&gt;工业界问题及解法
&lt;/h1&gt;&lt;h2 id=&#34;taa-时间上的抗锯齿&#34;&gt;TAA 时间上的抗锯齿
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/9dd2e2f5474243ad8b4858bc5b8c3fcc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
每一帧采样一个像素的四个角之一，剩余三个角用之前的数据。这样就相当于MSAA了（静止场景）&lt;/p&gt;
&lt;h2 id=&#34;msaa-vs-ssaa&#34;&gt;MSAA vs SSAA
&lt;/h2&gt;&lt;p&gt;SSAA就是高分辨率渲染再变小
MSAA分辨率不变，在一个像素中采样多次，甚至可以在临近像素复用&lt;img src=&#34;https://sdpyy1.github.io/2d90294b634340a7a10298bef0ee3dd3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;smaa&#34;&gt;SMAA
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/0625fb122b9b40ab885a85f5dfe911ea.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;超级分辨率&#34;&gt;超级分辨率
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/68edb7f8725542d1b77cedcdb0958bb9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;延迟渲染&#34;&gt;延迟渲染
&lt;/h2&gt;&lt;p&gt;节省shading的时间&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES202 高质量实时渲染（Real-Time Physically Based Materials）</title>
        <link>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-physically-based-materials/</link>
        <pubDate>Sun, 19 Oct 2025 14:01:31 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-physically-based-materials/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/a798b632f6bc41f9b3e68453c8445fb7.png" alt="Featured image of post GAMES202 高质量实时渲染（Real-Time Physically Based Materials）" /&gt;&lt;h1 id=&#34;micorfacet-brdf&#34;&gt;Micorfacet BRDF
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/c1dccc9561914ba7b95432dc6463973e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;f项fresnel-term&#34;&gt;F项：Fresnel term
&lt;/h2&gt;&lt;p&gt;Fresnel项（Fresnel term）用于描述光在界面上反射的强度随入射角变化的现象。&lt;img src=&#34;https://sdpyy1.github.io/a942291c496f4051afcc6bc9c1bfee1a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
对于绝缘体材质
&lt;img src=&#34;https://sdpyy1.github.io/24c084765fcf425cb261b58e31062919.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
对于导电材质
&lt;img src=&#34;https://sdpyy1.github.io/84d396e30c41458ba4c209bd5fbd15f7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
Snell&amp;rsquo;s Law（斯涅尔定律）描述的是光在两种介质交界面上传播时的折射行为
&lt;img src=&#34;https://sdpyy1.github.io/bd8922dfc342400795f5ce0b9923ca1e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
如果分为RGB三个通道，三个通道有自己的折射率。
Rs：垂直偏振（s-polarized）反射率
Rp：平行偏振（p-polarized）反射率
因为自然光通常是非偏振光（即含有相等的 s 和 p 偏振分量），所以实际使用时我们取两者的平均来得到总反射率：两者通过平均得到Fresnel平均反射率。
R0是垂直入射下的反射率，可以通过两种介质的折射率获得，他是一个三通道。进一步可以用Schlick&amp;rsquo;s Approximation来近似出θ角度下的反射率
&lt;img src=&#34;https://sdpyy1.github.io/716e700e92d840d19efab4135f0672b6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;g项shadowing-masking-termgeometry-term&#34;&gt;G项：shadowing-masking term（geometry term）
&lt;/h2&gt;&lt;p&gt;为了解决微表面的自遮挡问题
下图左边表示光线被遮挡（shadowing），右图是视线被遮挡（masking）
&lt;img src=&#34;https://sdpyy1.github.io/a1cab8a818d549fdaf0e23ae01c4dd91.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
如果没有G项，当视角和法线接近90°时，BRDF分母会接近0，所以BRDF会变得巨大，导致球体一圈变成白色
&lt;img src=&#34;https://sdpyy1.github.io/96df9e6eed734ba786be60a79543dcbd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
常用的G项为The Smith shadowing-masking term
把shadowing和masking两种情况分开考虑
&lt;img src=&#34;https://sdpyy1.github.io/63f9c6a0ac564809ae096ac1b4169215.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
他是基于NDF的值来考虑G项的值的
&lt;img src=&#34;https://sdpyy1.github.io/81a83552b89741ff82bf684bf4d805ba.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;d项normal-distribution-functionndf&#34;&gt;D项：Normal Distribution Function（NDF）
&lt;/h2&gt;&lt;p&gt;微表面上看法线方向并不是统一的
当法线分布更多在半程向量时，反射光强度更大&lt;/p&gt;
&lt;p&gt;glossy和diffuse的表面法线分布情况，glossy的表面分布比较集中
&lt;img src=&#34;https://sdpyy1.github.io/484fea8176da418d95c866902301b3b9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
有不同的模型来描述法线分布&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Benckmann、GGX
&lt;img src=&#34;https://sdpyy1.github.io/48d1b5f20a9e4b3e9e5fae3e9d03deb1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;beckmann-ndf&#34;&gt;Beckmann NDF
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/668659b7e67a41d3ae097f312d6c5840.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
两个系数来控制分布。阿尔法控制表面的粗糙程度。sita控制半程向量和法向量的夹角
&lt;img src=&#34;https://sdpyy1.github.io/fcaaf1365ae04480898058b459efd763.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
圆心是着色点，红色是微观法线。黑线是宏观着色点法线。他们的夹角就是sita，把红线延长到上面的一条线上。用tansita来定义分布
&lt;img src=&#34;https://sdpyy1.github.io/0c66b599e9e84979ae8a1bf8cd62f768.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以说Beckmann NDF是定义在tanθ上的高斯分布&lt;/p&gt;
&lt;h3 id=&#34;ggx-ndf&#34;&gt;GGX NDF
&lt;/h3&gt;&lt;p&gt;它的尾巴比较长，如下图橙色尾巴很长。而beckmannNDF在90°时已经衰减为非常接近0。如果把高出定义为高光，那backmannNDF高光周围会迅速衰减。而GGX会缓慢衰减，形成光晕的效果
&lt;img src=&#34;https://sdpyy1.github.io/05e5eaf5b5b14d468a527a63ac1b7930.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
下图可以看出backmann高光周围比较尖锐。GGX比较平滑
&lt;img src=&#34;https://sdpyy1.github.io/e7030edf588b48b7bed08c1a9bdc0fa7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以说GGX的长尾可以带来自然过渡的效果。&lt;/p&gt;
&lt;p&gt;GGX的拓展
定义参数gamma,调整尾巴衰减速度
&lt;img src=&#34;https://sdpyy1.github.io/d115f8cfb59e4f2aaa06ec4b94d7bcc0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;multiple-bounces&#34;&gt;Multiple Bounces
&lt;/h2&gt;&lt;p&gt;现在已经学习了BRDF的FGD三项的构成。但是这样渲染出来还有问题。随着roughness的增加，模型逐渐变暗了。下面一行是在做能量损失实验，说明roughness越大，能量损失越多了
&lt;img src=&#34;https://sdpyy1.github.io/1b0727ccc208402092dfea9f7cd16812.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
这种能量损失体现在，越粗糙的表面法线分布更容易被挡住
&lt;img src=&#34;https://sdpyy1.github.io/9ecc627afdcb47c7baf71af68be1b10c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以要考虑多次bounces，有相关的论文，但是对于实时渲染太慢了
&lt;img src=&#34;https://sdpyy1.github.io/52d226e2c892483ea9f0a9faa370ea1e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以现在有了基于经验补全的方式，kulla-Conty Approximation
首先根据渲染方程，假设输入的L=1，计算输出的能量是多少
&lt;img src=&#34;https://sdpyy1.github.io/2c8114e2323f4356849da2c38615a71c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
所以1-E(u0)就是损失的能量（E与观察方向有关系，所以不同的视角下损失能量是不一样的），最后补上这部分能量。
具体怎么算出来就不看了。最终损失能量依赖于观察方向和粗糙度两个变量。所以直接预&lt;strong&gt;计算打表&lt;/strong&gt;即可。
&lt;img src=&#34;https://sdpyy1.github.io/dfcfed8a53014f6fadc7a74794284d1e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
补充前后对比
&lt;img src=&#34;https://sdpyy1.github.io/a798b632f6bc41f9b3e68453c8445fb7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;ltcshading-microfacet-models-using-linearly-transformed-cosines&#34;&gt;LTC(Shading Microfacet Models using Linearly Transformed Cosines)
&lt;/h2&gt;&lt;p&gt;LTC 最初是由 Tobias Ritschel 等人在论文《LTC - A Fast and Efficient Approximation of Fresnel Terms and Distribution of Normal Vectors for Real-Time Rendering》提出的，它被设计为一种方法来 近似反射分布函数（BRDF）
Fresnel项 和 法线分布函数（例如 GGX）是微面模型的关键组成部分，这些函数非常复杂，计算成本较高。LTC 使用线性时间编码（Linear Time Coding）来近似这些复杂的分布，使得实时渲染时可以快速计算这些项。
综上LTC是一种对BRDF项的预计算&lt;/p&gt;
&lt;p&gt;首先，它是有限制的。主要是在GGX的法线分布下、没有阴影、多边形光源
&lt;img src=&#34;https://sdpyy1.github.io/66354821416b4c43bf12995a188ac243.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
主要思想是任何BRDF lobe 都可以通过一个变换转化为一个余弦函数
&lt;img src=&#34;https://sdpyy1.github.io/10747d8cb7744e25922901c727879a8c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/7084778496db4a5082c09cb782a3528e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
实际实现比较复杂，课程没提到&lt;/p&gt;
&lt;h1 id=&#34;disneys-principled-brdf&#34;&gt;Disney’s Principled BRDF
&lt;/h1&gt;&lt;p&gt;首先介绍了为什么有了微表面BRDF还需要Disney的BRDF
&lt;img src=&#34;https://sdpyy1.github.io/568ccf63308b491c899cc246bec4e1b7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
它被称为principled的BRDF，可以通过调整参数来达到想要的效果，所以他是艺术家友好的
&lt;img src=&#34;https://sdpyy1.github.io/fce4ae67e9cb4e629e3ec23e8c0043c4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
介绍一下里边的名词&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;subsurface（次表面反射）：光线进入表面后并不会直接反射回去，而是穿透表面并在内部多次散射，最终可能以不同的方向从表面射出。&lt;/li&gt;
&lt;li&gt;metallic（金属性）&lt;/li&gt;
&lt;li&gt;specular（镜面）&lt;/li&gt;
&lt;li&gt;specularTint（镜面颜色）：specularTint 是一个与 镜面反射（Specular Reflection）相关的参数，用来控制反射光的颜色如何与 材质本身的颜色混合。也就是说镜面反射光不是单纯的白色，而且受表面材质颜色的影响&lt;/li&gt;
&lt;li&gt;anisotropic（各向异性）：述材料或表面在不同方向上具有不同物理特性的术语&lt;/li&gt;
&lt;li&gt;sheen（沿着球的表面有绒毛雾化效果）&lt;/li&gt;
&lt;li&gt;clearcoat（添加一层镀膜）&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;non-photorealistic-renderingnpr&#34;&gt;Non-Photorealistic Rendering(NPR)
&lt;/h1&gt;&lt;h2 id=&#34;outline-rendering-描边&#34;&gt;Outline Rendering 描边
&lt;/h2&gt;&lt;p&gt;先来看看有哪些Outline，B表示自己的边界，S表示两个面交界处形成的边界，C是折痕，M是材质边界
&lt;img src=&#34;https://sdpyy1.github.io/ab26d2bd43da4b81a3a89d6a512edf31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
知道了边界，下来就需要描边，可以通过Shading或者图像处理来完成。首先来介绍Shading方法，哪些像素点是S边上呢，根据观察，就是法线与观察方向=之间夹角接近垂直的像素点。可以设置夹角的阈值，来改变边的粗细
&lt;img src=&#34;https://sdpyy1.github.io/f792388c1b6c438d9f8348ee62c59d04.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
另外就是外扩法，之前learnOpenGL里有
&lt;img src=&#34;https://sdpyy1.github.io/69be7e2417394169b1600be2f94750d3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
另外看一下从图像上来处理
&lt;img src=&#34;https://sdpyy1.github.io/f184962b02234ab88cd5988e16292580.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;color-blocks-色块效果&#34;&gt;Color blocks 色块效果
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/5ad865728c1345a0a4c184eba96509d0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
直接把渲染结果量化，就是连续值变成离散
&lt;img src=&#34;https://sdpyy1.github.io/c66c737ffa0046cdb39b5652d8204af2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;strokes-surface-stylization-素描效果&#34;&gt;Strokes Surface Stylization 素描效果
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/6fcfdf86c2a347179a0ac8abe7ad9866.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/7402580507ca48f0a7b9d3b126ec466d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES202 高质量实时渲染（Real-Time Global Illumination）</title>
        <link>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-global-illumination/</link>
        <pubDate>Sun, 19 Oct 2025 14:00:35 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-global-illumination/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/aa2c09da489f4288808aa9af1f4fc213.png" alt="Featured image of post GAMES202 高质量实时渲染（Real-Time Global Illumination）" /&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/78996c31c1b04f5eae5269c1d6756086.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
简单理解就是已经被照亮的点作为光源再去照亮别的点
&lt;img src=&#34;https://sdpyy1.github.io/aa2c09da489f4288808aa9af1f4fc213.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;在3d空间的全局光照&#34;&gt;在3D空间的全局光照
&lt;/h1&gt;&lt;h2 id=&#34;reflective-shadow-mapsrsm&#34;&gt;Reflective Shadow Maps（RSM）
&lt;/h2&gt;&lt;p&gt;用非直接光照照亮点p需要什么：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;那些点被光源直接照亮（从shadowMap中得到）&lt;/li&gt;
&lt;li&gt;其他点对点p的贡献是什么
&lt;img src=&#34;https://sdpyy1.github.io/789badb3ea1447e7be3f08235557ba93.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
shadowMap中每个纹素就是一个面光源（因为shadowMap描述的就是那些地方被直接照到），紧接着要求p点的间接光照其实就是求shadowMap每个纹素代表的面光源对p点的贡献&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把纹素作为面光源来求渲染方程&lt;img src=&#34;https://sdpyy1.github.io/b1aba486555a4511bb14e750381231f9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;img src=&#34;https://sdpyy1.github.io/474b13e5a0ca4d9da12c7c9abc5387b7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
现在问题就是从纹素反射出来的Radiance如何计算&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;BRDF项在当前场景中认为是diffuse的
&lt;img src=&#34;https://sdpyy1.github.io/370d725f018f4338ac513d7cc5e0874c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;BRDF可以认为是出射的Radiance与入射的irradiance的比例，所以L可以算出
&lt;img src=&#34;https://sdpyy1.github.io/d48ebf44454f4d858a7734014dc2d2ee.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这样写的好处是带入渲染方程后把dA项消掉了，这样就不需要patch的面积大小了，公式就变成了
&lt;img src=&#34;https://sdpyy1.github.io/9bd48f54de9048638db79731b15805c5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
有几个问题：&lt;/li&gt;
&lt;li&gt;V项没了，因为计算量太大，要计算对于每个着色点从任何一个纹素看向着色点是否被遮挡&lt;/li&gt;
&lt;li&gt;方程下边变成4次方，是论文作者对距离衰减做了平方衰减处理，课程中说这是错的，应该是平方，如果说错了直播吃键盘😄（看到第9节课，老师真吃键盘了）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有一些纹素一定不会对着色点有贡献 1. 可见性 2. 方向 3. 距离
&lt;img src=&#34;https://sdpyy1.github.io/c4456044a9cc4795be040b04487755c4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这个问题就引出了这篇论文的假设，如何知道那些纹素离着色点比较近，论文中认为在shaowMap上比较近，那么世界坐标下就离的比较近（大胆的假设）&lt;img src=&#34;https://sdpyy1.github.io/3647b13dc46d496ba7d5aa63f555a220.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
RSM需要存储的东西
&lt;img src=&#34;https://sdpyy1.github.io/4691c26e05fe46c195c34d823642dd78.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
RSM在工业界通常用在手电筒&lt;img src=&#34;https://sdpyy1.github.io/1c7c92698d714a6cac0ca5d004b64cc3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
RSM的优点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;易于实现
缺点：&lt;/li&gt;
&lt;li&gt;直接光源有多少就得有多少张shadowMap&lt;/li&gt;
&lt;li&gt;可见行没法做&lt;/li&gt;
&lt;li&gt;很多的假设&lt;/li&gt;
&lt;li&gt;采样率和质量的tradeoff
&lt;img src=&#34;https://sdpyy1.github.io/f6fbb270566c424daf7e3a0cbdde3f9a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;light-propagation-volumes-lpv&#34;&gt;Light Propagation Volumes (LPV)
&lt;/h2&gt;&lt;p&gt;关键点：Radiance沿直线传播时不会发生变化z
关键做法：把整个场景体积进行切割，成为一个个体素（类比纹素）
下图红色箭头就是间接光照的来源，就是求黄色点接收到的红色radiance的计算
&lt;img src=&#34;https://sdpyy1.github.io/2fd97894c5514c76b66407796bd15ce2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;那些点接收到了直接光照&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把这些点放在场景到的一个网格中&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在网格中传播radiance
&lt;img src=&#34;https://sdpyy1.github.io/966136cd35304efc8a6af1bf0507518f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
通过RSM得到一系列虚拟光源
&lt;img src=&#34;https://sdpyy1.github.io/37bb32b97d4a4264a1daa2f40c5ec2e1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
对场景划分3D网格（可以使用3D纹理，定义每个UV是3D空间的哪个网格）
计算一个格子中向任何方向上的radiance是多少，并用SH压缩，LPV需要存储场景中每个体素（voxel）的辐射度分布，直接存储全方向的辐射度会导致内存爆炸。通过SH投影（通常用2阶或3阶），可将6D的光照函数压缩为少量系数（如9个或16个），极大降低内存需求。
这一步我理解就是把每个次级光源都归为了每个网格的属性，这里记录了每个网格向各个方向的Radiance
&lt;img src=&#34;https://sdpyy1.github.io/ae5fdbf29cf74f25b2ef695cf1ed74a8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
一个格子的radiance向上下左右方向进行传播，这一步结束后每个格子的radiance就都记录好了
&lt;img src=&#34;https://sdpyy1.github.io/a7ff006c9d0f4b04b2490c9dde74c8f2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
对于一个着色点来说，就可以直接使用当前格子接受到的Radiance来计算间接光照了
&lt;img src=&#34;https://sdpyy1.github.io/4065c3b52123461c86c47f818868fcdb.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
但是有问题，墙的一边不可能照亮墙的另一边，那把一堵墙放在同一个网格中，那计算着色时墙两边的Radiance都会被考虑，就会出现漏光
&lt;img src=&#34;https://sdpyy1.github.io/ce86dd9217cc4335a6be27fabf77d8ca.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这样就会出现漏光现象，这就要考虑网格划分粒度了
&lt;img src=&#34;https://sdpyy1.github.io/00005e4e479d4e18ad8e06298b36210e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;voxel-global-illumination-vxgi&#34;&gt;Voxel Global Illumination (VXGI)
&lt;/h2&gt;&lt;p&gt;把整个场景网格化，想象成MC用方块搭起来的场景，并做了层级处理，比如上一层的一个格子在下一次划分为8个格子，最终建立起一颗树
&lt;img src=&#34;https://sdpyy1.github.io/833a236299854a6095942f118b28b821.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
第一步：用RSM的方法找到直接光照的网格，并记录每个网格接收到的光源的入射方向和法线，并更新到各个层级，高层级整合低层级的光源入射方向和法线
&lt;img src=&#34;https://sdpyy1.github.io/c16217c988254f6483fa9f2bb6e19bfe.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
开始着色：
对于glossy的着色点，光源到达后会反射为一个圆锥的范围
从着色点发射锥体，沿锥体轴线步进采样。看场景中那些体素在椎体范围内，那它就对该着色点有贡献（这是利用光路可逆的思想，反过来这些碰到的体素就会通过椎体射到着色点）
&lt;img src=&#34;https://sdpyy1.github.io/c9b988666964493c9a7de9f92622f192.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
对于diffuse的着色点
&lt;img src=&#34;https://sdpyy1.github.io/61906060646c451ea4507355c81d049c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/8b3469f01c7546d088ef523d2d9597d7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;在屏幕空间的全局光照&#34;&gt;在屏幕空间的全局光照
&lt;/h1&gt;&lt;p&gt;屏幕空间：只使用屏幕信息，对图像进行后期处理&lt;/p&gt;
&lt;h2 id=&#34;screen-space-ambient-occlusionssao&#34;&gt;Screen Space Ambient Occlusion（SSAO）
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/56bc741125b449aeb02dcb7691457023.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
一种全局光照的近似
key diea 1：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不知道间接光照，假设为一个常数&lt;/li&gt;
&lt;li&gt;但不是所有方向都能接收到，会被别的物体挡住&lt;img src=&#34;https://sdpyy1.github.io/91ab1bf16ed347ce8f6a49dec439081f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;显然AO的环境光更好
&lt;img src=&#34;https://sdpyy1.github.io/4aa8c4784dea406687ddf64cf70cfdc9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
经典从渲染方程中解释
&lt;img src=&#34;https://sdpyy1.github.io/f97c2f56c99243c0928e57f99cb7133e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
把V项拆出去了，拆出去的（蓝色）项就像当于把四面八方的可见行进行了平均，剩下的（橙色）项中L项在SSAO中已经假设为常数，另外还假设BRDF是diffuse的，所以全是常数了
&lt;img src=&#34;https://sdpyy1.github.io/cbdf6a3b5ade4bfa91f88e43bdef746c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
其中cossita还没解释
&lt;img src=&#34;https://sdpyy1.github.io/fe152b585bab4a6cb848b69c823abc63.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
用cos项把球面上的积分转为圆面上的积分（把立体角的面积投影到底面圆上进行积分）
&lt;img src=&#34;https://sdpyy1.github.io/4ea39dbe9d82466bbb29d4b4d6c62f8e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
数学结束，SSAO就是把间接光当常数、BRDF也是diffuse的
&lt;img src=&#34;https://sdpyy1.github.io/e2559823e91b41d498df2568ba8fef70.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
理论分析结束，现在问题就是如何在屏幕空间求一个着色点四面八方看哪些地方被挡住了
SSAO假设任何一个着色点在周围半径为R的球中进行采样，每个点与对应像素的zbuffer进行比较，如果大，说明这个点被挡住了就是红色
&lt;img src=&#34;https://sdpyy1.github.io/a2ae51e1e3074681afe946734ca315e7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
但是上图有一个点判断错了
&lt;img src=&#34;https://sdpyy1.github.io/8b7b0125d751451c84405fdce82bfe1e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
用一整个球来采样还有问题，墙体内部的点不需要考虑，只需要半球采样即可&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;采样越多越准确
AO做法是先低采样得到AO
&lt;img src=&#34;https://sdpyy1.github.io/bb3cb2f09f44419aa22db16b12ba2773.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
然后进行模糊
&lt;img src=&#34;https://sdpyy1.github.io/7311644636be470bacf2a14f92c2465f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
SSAO的一个问题是会出现假遮挡现象，因为在摄像机视角下，看着被遮挡了，但实际上两者距离是很远的 &lt;img src=&#34;https://sdpyy1.github.io/892c0a689792446bb1b561513c12f82e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
进一步有技术叫HBAO，只考虑一定范围内的遮挡&lt;/p&gt;
&lt;h2 id=&#34;screen-space-directional-occlusionssdo&#34;&gt;Screen Space Directional Occlusion(SSDO)
&lt;/h2&gt;&lt;p&gt;是对SSAO的提升，SSAO考虑间接光照是是一样的，但是通过RSM，我们是可以求出间接光照的&lt;/p&gt;
&lt;p&gt;下图看出AO只是简单的变暗，而DO考虑的反射物的颜色，让阴影变蓝了&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/b41c74d2bb4d41c18c1ab0f4f4b06ab8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/e9e995b1cba644f6b023dbb2c8684d44.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
与SSAO想法相反，DO认为如果打出去的光线没有物体挡住，那它才应该没有间接光照的贡献，只有直接光照的贡献，反而打到的物体才会反射回来光
&lt;img src=&#34;https://sdpyy1.github.io/336fbc7a6a6349a693aa486c7d80ff5f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
实际做法也和SSAO一样，半球面采样，使用Zbuffer来判断采样点是否被挡住，下图ABD点都得到是被遮挡了
&lt;img src=&#34;https://sdpyy1.github.io/a382ce63d49e4a8c8a0232fe4adea01d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
考虑ABD的间接光照对C点的贡献
&lt;img src=&#34;https://sdpyy1.github.io/b08750c3b5dd4533a955928233ee4b81.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
DO也有假遮挡问题，A点没有被挡住，但是从视角上看是被挡住了，所以被认为对C点有间接光照贡献
&lt;img src=&#34;https://sdpyy1.github.io/0f27d6026c204318a9e54c6c9068a38a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/eea45304538e4028b024dc72f2de4f51.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
只能做小范围的全局光照
&lt;img src=&#34;https://sdpyy1.github.io/1fa9977788964ba2978dec28287d5817.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;screen-space-reflectionssr&#34;&gt;Screen Space Reflection（SSR）
&lt;/h2&gt;&lt;p&gt;在屏幕空间中做光线追踪
现在在屏幕空间已经有了红框的东西，现在SSR就只需要加上白框的东西
&lt;img src=&#34;https://sdpyy1.github.io/31ca9120b8ef4113b05d7b14191249e7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
SSR的思路
&lt;img src=&#34;https://sdpyy1.github.io/0f08d604d2fa441faf9b66cf8e4d6026.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Linear Raymarch
目标是找到光线与场景的交点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选定步长，每走一步检查深度
&lt;img src=&#34;https://sdpyy1.github.io/0a60e020e18f413786e47b675285d3db.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
还有加速方法，首先对zbuffer生成mipmap
但与常规mipmap不相同，上一层级的一个像素是下一层级4个像素的最小值，而不是平均值
&lt;img src=&#34;https://sdpyy1.github.io/20ee35ebb76147ce955c078cd4d13adb.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
存最小值的目的就是，如果一个光线与上层不相交，那下层更不用考虑了，因为下层离得更远
&lt;img src=&#34;https://sdpyy1.github.io/7231499200974ea9a07c253f3694367d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
算法为首先走一步，每交点，就胆子大一点就在上一层一口气走两步，还没交点走4步，有交点了再慢慢走
&lt;img src=&#34;https://sdpyy1.github.io/8126d41b85f94e1086ceffc2d4c2279e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
SSR也有问题，因为屏幕空间只有最前面的数据，所以藏在背后的但是反射出来应该能看见的物体就渲染不出来了
&lt;img src=&#34;https://sdpyy1.github.io/2b4fcc662dc14ea3aa48ea820c3b0a31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;img src=&#34;https://sdpyy1.github.io/38327ccdb1a14357b7aa1a52ebcc9e86.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>GAMES202 高质量实时渲染（Real-Time Environment Mapping）</title>
        <link>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-environment-mapping/</link>
        <pubDate>Sun, 19 Oct 2025 13:59:44 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-environment-mapping/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/787b8e5bfbd6453bad3b4faab67d12d9.png" alt="Featured image of post GAMES202 高质量实时渲染（Real-Time Environment Mapping）" /&gt;&lt;h1 id=&#34;shading-from-environment-lighting&#34;&gt;Shading from Environment Lighting
&lt;/h1&gt;&lt;p&gt;使用环境光计算shading的操作叫做IBL(Image based lighting)  ，光照不是来自光源，而是环境贴图
下面在不考虑阴影的情况下来看渲染方程，接受来自整个半球面的光来进行渲染
&lt;img src=&#34;https://sdpyy1.github.io/3ec446e25c5a4678a6fb753cbbc6d6f9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
之前的做法是使用蒙特卡洛积分来估计积分，但是太慢了放在实时渲染中（采样表现并不好，（但是随着发展形势逐渐变化了）
，这里就讲避免采样的方法）
&lt;img src=&#34;https://sdpyy1.github.io/39c5875688c94a2d8d3fc3df667a1ef9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
根据上图，联系上节课如何对visibility拆分的情况，这里的光照项也可以提出去
&lt;img src=&#34;https://sdpyy1.github.io/73cecc8f034c4e4eaade9ce2ebedb2d9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;这种操作反应到贴图上就是对贴图进行了过滤，在渲染之前先得到滤波核处理过的贴图&lt;img src=&#34;https://sdpyy1.github.io/2aa3e22aacb44a39b7f4bb43027ce3b3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这种操作相当于把多次采样近似为先滤波后的镜面反射方向的一次采样
&lt;img src=&#34;https://sdpyy1.github.io/5539519c2d25465982cbf7bf6bbbdf08.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
到这里对于光照项避免了采样，下来来看BRDF项避免采样的方法，如今已经有更好的方法，这里只是学思想，主要思想就是我们想直接打表来预存每个变量下的积分值，但是维度太高了，这里在分析如何降维
&lt;img src=&#34;https://sdpyy1.github.io/5250dbc3f66a4e47a2290d29bb5ba9b6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
微表面模型的BRDF的方程
&lt;img src=&#34;https://sdpyy1.github.io/9fff82c9ae2a4d7bbff1cfe250f279c2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这里主要考虑菲涅耳项和法线分布项的近似
F项
&lt;img src=&#34;https://sdpyy1.github.io/b8cf2f62fcd24797a0b8f197c8efb930.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
G项
&lt;img src=&#34;https://sdpyy1.github.io/f9ee80ce04064ec5a03545db4639901a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
看这两幅图，说明如果我们想预计算BRDF函数，实际上只需要3个变量，一张三维的表，但是三维还是太麻烦了
F项还可以进一步简化，这样写之后把R0拆出来了，因为它基本是一个常数
&lt;img src=&#34;https://sdpyy1.github.io/687b7aea7cb64f3b83567eceba64e94c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这样等式右边的连个积分都可写成一个二维表来进行预计算
&lt;img src=&#34;https://sdpyy1.github.io/3540dc410fa8452d8d65c347f5f27662.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
在这张2d纹理任何一点的值，就是BRDF积分出来的结果，直接渲染方程都不用积分了
下图可以看见这种简化方法得到的结果与真实采样得到的结果十分相似，但计算量小了很多
&lt;img src=&#34;https://sdpyy1.github.io/5933360aa82d4f6a9c6804cb11cfeda6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这种方法叫做split sum，它是unreal引擎PBR🐮b的基础～（没有采样就没有噪点）
&lt;img src=&#34;https://sdpyy1.github.io/fa29c88b35f64132b0993545a666f19e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
我感觉实时渲染目前了解的很多都是如何把计算进行简化的同时偏差并不大！&lt;/p&gt;
&lt;h1 id=&#34;shadow-from-environment-lighting&#34;&gt;shadow from environment lighting
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/26c1b3405781438180a4b810824b9dd0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
可以把环境光贴图当作很多光源，每个光源都需要shadowMap，这是很困难的
&lt;img src=&#34;https://sdpyy1.github.io/6cef31e5c0a84c2d8169249a8e360a0e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;spherical-harmonics-sh&#34;&gt;Spherical Harmonics (SH)
&lt;/h1&gt;&lt;p&gt;Spherical Harmonics (SH)（球面谐函数）是一类在球面坐标系中定义的特殊函数
&lt;img src=&#34;https://sdpyy1.github.io/787b8e5bfbd6453bad3b4faab67d12d9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l决定了波动的“频率”或阶数&lt;/li&gt;
&lt;li&gt;m 控制了波动的方向性，特别是如何沿着经度
这个东西就是用来把一个二维函数展开成SH的线性组合（类似一维的傅里叶变换）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每一项前边的系数用下式来计算（两个函数乘起来求积分），这种求系数叫做投影，同样通过这些系数也可以恢复原来的函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/2e4caf69b2934fc49c30ba96dc633a0a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
说了这么多东西，想表达的就是计算时BRDF是低频时，就可以把它比做低通滤波器，所以不管光照项算出来多高频，最终都会被抵消，所以并不需要高频。所以可以用球谐函数前三阶来近似光照项
&lt;img src=&#34;https://sdpyy1.github.io/7b22a2af73bb4329820c215937e56650.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;总结一下上边这些东西。球谐函数的作用是把一个函数展开成从低频到高频的基函数组的线性组合，渲染方程中如果BRDF是diffuse的，那它就是个低通滤波，即使光照是高频的，它的高频信号计算后也会消失，所以直接用球谐函数前几阶来代替它即可。
&lt;img src=&#34;https://sdpyy1.github.io/41f9c5ac28cb4442b772436446722b58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
上面这些东西还只是shading，还没有解决shadow&lt;/p&gt;
&lt;h1 id=&#34;precomputed-radiance-transfer-prt&#34;&gt;Precomputed Radiance Transfer (PRT)
&lt;/h1&gt;&lt;p&gt;在实时渲染下，人们更愿意把渲染方程写成下面，PRT假设光照会变，别的不会变的情况
&lt;img src=&#34;https://sdpyy1.github.io/652f7a58ed6940a698dcc8b3fba57d21.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
上图是考虑下图这一点的数据
&lt;img src=&#34;https://sdpyy1.github.io/961727f1808d42a7989c991a899e2548.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这三张图就代表了在这一点任何方向的lighting、visibility、brdf。直接对应相乘再相加就是这一点的shading，但是每个像素都这么大的计算了，显然效率太低了
&lt;img src=&#34;https://sdpyy1.github.io/d48d235d7966422b805ba06d1503e41c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;br&gt;
这三项都可以表示为球面函数，那直接把上边三张图对应像素值相乘就可以了
PRT的基本思想就是把渲染方程看成下面的两部分 lighting和light transport
&lt;img src=&#34;https://sdpyy1.github.io/56f69f29c630443496301553c0fb02ec.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
PRT的做法就是：（它假设场景所有东西都不变，就光照可以改变）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把lighting进行球谐函数展开，在预计算时就可以算好，因为每个像素的lighting部分都是来自同一张环境光贴图所有点位的值&lt;/li&gt;
&lt;li&gt;transport部分也可以预计算球谐函数展开
&lt;img src=&#34;https://sdpyy1.github.io/7f9dcb3f501a44a39240b48fab246876.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如果BRDF是diffuse的，那可以把它当作一个常数（即任何方向都均匀反射），下图先提出BRDF的常数，然后把光照项换成求和式，之后交换了求和与积分的顺序（在CG中可以认为任何时候求和和积分都可以交换顺序）
&lt;img src=&#34;https://sdpyy1.github.io/28b19afbc5fa4c39ab92fde81736f665.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
到这里发现积分内的含义就和之前求球谐函数展开后各项系数的方程是长一样的，所以可以把后边剩下的项先变成球谐函数，然后i不同就是不同项的系数，这样这个积分项就变成了一个离散的一维数组
&lt;img src=&#34;https://sdpyy1.github.io/04f092d214b945cea246ccf7477481f4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;img src=&#34;https://sdpyy1.github.io/2e4caf69b2934fc49c30ba96dc633a0a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终渲染方程变成了两个东西的点乘
&lt;img src=&#34;https://sdpyy1.github.io/cc0ac8c6867345e19c251bb7399e1836.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
但是预计算后边这一部分就说明整个场景的物体是不能动的。另外球谐函数的一个优点是旋转后可以很快算出系数的变化，所以支持光照的旋转。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最终渲染方程只需要一个light的数组和一个transport的数组来计算
&lt;img src=&#34;https://sdpyy1.github.io/a7dc415487bf45afb4eac0d4f4c84a06.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
另外还有glossy的物体没有处理。 diffuse的BRDF可以当作一个常数，而glossy的BRDF与摄像机的位置o也有关系，不同的摄像机位置o得出的T数组是不一样的 &lt;img src=&#34;https://sdpyy1.github.io/ccac4f66b271437bbee6790d1889fb50.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
因为现在最终的结果与摄像机视角o有关了，进一步把T(o)也进行球谐函数展开
&lt;img src=&#34;https://sdpyy1.github.io/a65f458c84994241b27033321eb82309.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终light项仍然是一个数组，而transport项变成了二维的&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/cc2ac22356cd4629abb290871a69cc0e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
SH选择不同的阶的时间复杂度
&lt;img src=&#34;https://sdpyy1.github.io/b5d52282691c49fcbeb70e40c4ed5391.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES202 高质量实时渲染（Real Time Shadows）</title>
        <link>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-shadows/</link>
        <pubDate>Sun, 19 Oct 2025 13:58:35 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games202-%E9%AB%98%E8%B4%A8%E9%87%8F%E5%AE%9E%E6%97%B6%E6%B8%B2%E6%9F%93real-time-shadows/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/7d064c8a32bd4ca48f6dc45c082cf7c4.png" alt="Featured image of post GAMES202 高质量实时渲染（Real Time Shadows）" /&gt;&lt;h1 id=&#34;shadow-mapping&#34;&gt;Shadow Mapping
&lt;/h1&gt;&lt;p&gt;这块东西在我OpenGL学习笔记中有详细介绍&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.csdn.net/lzh804121985/article/details/147256140?spm=1001.2014.3001.5502&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://blog.csdn.net/lzh804121985/article/details/147256140?spm=1001.2014.3001.5502&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;shadowmapping的问题&#34;&gt;shadowMapping的问题
&lt;/h2&gt;&lt;p&gt;阴影痤疮(Shadow Acne)、或者叫自阴影
&lt;img src=&#34;https://sdpyy1.github.io/a4e85793e2aa4f54a130ad1b28572f94.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
出现该问题的原因可以看下图，光源视角下的一个像素内高度认为是同一个，下一个像素的位置，会被认为比前一个Z值更远，所以平坦的地板会交替出现高低不平的地方，这样就会出现阴影
&lt;img src=&#34;https://sdpyy1.github.io/4844c9f5ebb04b3abf5ec382b4b4a06a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
简单解决就是认为z插值在一个范围内就不算在阴影中，虽然能解决这个问题，但是引入了新的问题，阴影发生了偏移。如果bias设置的过大，在靠近脚的地方，就不会出现阴影。在工业界称为“彼得潘效应&amp;quot;(Peter Panning)
&lt;img src=&#34;https://sdpyy1.github.io/fe24e805998f4e88b24fd2906686da31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
对于这种不接触的阴影，解决方法是shadowmapping中不仅存最小深度的表面，还存储第二小深度的表面
&lt;img src=&#34;https://sdpyy1.github.io/5f5663c92fcd49c2ba4b560d1dd36054.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
另外在learnOpenGL提到，在渲染深度贴图时，使用正面剔除，让深度贴图存储的是物体的内面来解决彼得潘问题。
因为我们只需要深度贴图的深度值，对于实体物体无论我们用它们的正面还是背面都没问题。使用背面深度不会有错误，因为阴影在物体内部有错误我们也看不见。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/17af5ba33265405da751453ee1908cd0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;另外阴影还有走样问题，毕竟阴影计算就是一个像素一个像素来做的
&lt;img src=&#34;https://sdpyy1.github.io/ec576e1cfcf343c5818928afd9fbbbca.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;shadow-mapping背后的数学&#34;&gt;shadow mapping背后的数学
&lt;/h2&gt;&lt;p&gt;在实时渲染中，经常会用到一些不等式，但人们更多关注的是不等式相等时成立的条件，也就是说，不等式在什么条件下近似相等。实时渲染中，经常用到的一个约等如下：
&lt;img src=&#34;https://sdpyy1.github.io/57bf987cf801430aa8186bca896c9806.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
满足下列两个条件之一时，实时渲染领域认为他们近似相等：
当g(x)满足：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;g(x)积分域足够小&lt;/li&gt;
&lt;li&gt;g(x)足够smooth，g(x)值变化不大，比较平坦&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面来到渲染方程
&lt;img src=&#34;https://sdpyy1.github.io/1e7d325e87c946f4bee344e8a444f67a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
根据上边的不等式，就可以把最后一项（V）提出来
&lt;img src=&#34;https://sdpyy1.github.io/0a9420c5315942a9a51bcef35adf03ef.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
这样变换后，渲染方程就变成了正常做shading之后，再乘以一个表示可见不可见的项
这时的G(x)就是渲染方程的被积函数&lt;/p&gt;
&lt;h2 id=&#34;pcfpercentage-closer-filtering&#34;&gt;PCF（Percentage Closer Filtering）
&lt;/h2&gt;&lt;p&gt;以点P为例，并不只找shadowmap中对应的一个像素，而是找一圈的像素，与这些像素都进行比较，最后将比较结果进行平均
&lt;img src=&#34;https://sdpyy1.github.io/d9deb341cfcd4d268b64267a42fc9d4a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
比较结果就是一个全是1，0的矩阵，最终得到的数就不是非0即1的数了
&lt;img src=&#34;https://sdpyy1.github.io/c74e9d602c71492580041d0a067681c6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/e38b95bd0bcb4710b7df647d2ec33f0c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;pcsspercentage-closer-soft-shadows&#34;&gt;PCSS（Percentage closer soft shadows）
&lt;/h2&gt;&lt;p&gt;PCF它原本是用来解决阴影的走样问题的，后来人们又发现它可以用来生成软阴影，就是PCSS
首先来回顾一下软阴影和硬阴影
&lt;img src=&#34;https://sdpyy1.github.io/9e65479f005e4d1d8d8d5e9a2ec26560.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
到这里就可以理解软阴影在阴影不同位置有不同的软硬程度就可以通过PCF的滤波核的大小来控制，越大越模糊，越小越硬
&lt;img src=&#34;https://sdpyy1.github.io/7a944c13c04246a4ba67e519e0857896.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
w(半影)与光源大小、物体位置之间呈现相似三角形的关系，w就可以用来表示软硬程度。半影举例越大，滤波核尺寸就应该设计的更大，让阴影更软&lt;/p&gt;
&lt;p&gt;总结PCSS的算法流程：1. 在一个范围搜索遮挡物，并计算平均深度。2. 用平均深度求出半影距离3. 在对应的滤波核尺寸下进行PCF
&lt;img src=&#34;https://sdpyy1.github.io/15f8ad64ccca453c8d4d78adede3ebe6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
上边的步骤1需要选取一个合适的范围，下图就展示了如何选取一个范围来进行遮挡物搜素，假设了ShadowMap在近平面，用光源的大小来决定搜素范围
&lt;img src=&#34;https://sdpyy1.github.io/b926533d9f8147a082953d4d4832eb61.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;img src=&#34;https://sdpyy1.github.io/f36ec8d9e8cd425c9006784e630e6b08.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在PCSS第一步和第三步中，为了计算一个像素点的着色，需要考虑多个纹素，从而导致速度慢。如果采用稀疏采样，又会有噪点产生
&lt;img src=&#34;https://sdpyy1.github.io/7d58a8364ceb4b02ba11c9420e67ea67.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;vssmvariance-soft-shadow-mapping&#34;&gt;VSSM（Variance Soft Shadow Mapping）
&lt;/h2&gt;&lt;p&gt;针对PCSS效率问题，提出VSSM&lt;/p&gt;
&lt;h3 id=&#34;优化步骤3&#34;&gt;优化步骤3
&lt;/h3&gt;&lt;p&gt;PCF和PCSS的 PC（percentage closer）的理解就是在深度纹理的一片区域内，有多少比例的纹素比着色点深度小
&lt;img src=&#34;https://sdpyy1.github.io/c5016ff53ff445b0803802017092554d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这就相当于看多少人比我考试分数高，就需要遍历所有同学的成绩
VSSM的解决方案就是用成绩分布（正态分布）来估计（⚠️VSSM并没有用正态分布，这里这样说只是方便理解，后边提到切比雪夫不等式就好理解了）
&lt;img src=&#34;https://sdpyy1.github.io/4c15595d7a7b4bafbeaf151712f92592.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
定义一个正态分布需要均值和方差，这个可以快速计算&lt;img src=&#34;https://sdpyy1.github.io/f59a96bfa0404bafa5dbd1e679d1a3e0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
均值可以通过mipmap（因为上一级map到下一级map就是把相邻的正方形进行平均，到最后就整合为了一个平均值）、SAT（是一种用于高效计算矩形区域内元素总和的数据结构）来求，方差则是用一个概率论公式来求
&lt;img src=&#34;https://sdpyy1.github.io/a8edfd50921a428e88f0d33eb0a37738.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
得到正态分布分布后，求小于着色点深度的纹素百分比，其实就是阴影部分的线下面积
&lt;img src=&#34;https://sdpyy1.github.io/851b3d8206ed41f1beb15718639d3d8d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下来VASS还对这种计算进行了简化。直接使用不等式来近似
只要给定均值、方差、和要比较的位置t，就有下面的不等式成立 ，在渲染中就直接用方差、期望、t来估计红色部分的面积，然后马上就能得出剩余部分的面积（t必须大于中间线才好使）
&lt;img src=&#34;https://sdpyy1.github.io/bcf078962ce443468d42dbc6a0f6c4ad.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
至此，对PCSS步骤三的优化结束，在生成shadowMap时，为了计算方差，还需要额外生成一张平方shadowMap，总体来说甚至都不需要进行loop就可以知道一片区域有多少纹素深度小于着色点深度
&lt;img src=&#34;https://sdpyy1.github.io/b74b9a83b7724e0eba57119b474890c1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;优化步骤1&#34;&gt;优化步骤1
&lt;/h3&gt;&lt;p&gt;为了设置合理大小的过滤核，对一个区域内遮挡物的深度进行平均，需要遍历纹素，效率并不高&lt;/p&gt;
&lt;p&gt;例如下图，如果着色点的深度为7，那么这些纹素的深度只有蓝色的是遮挡物，就需要平均他们，而红色不参与计算
&lt;img src=&#34;https://sdpyy1.github.io/ca091f3d07d14ea8a74954809b7591bd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
对蓝色部分和红色部分求均值后，满足下式
&lt;img src=&#34;https://sdpyy1.github.io/fdc6f0946e014ef697caad775ea35e6a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这里求N1/N就可以直接用刚才步骤三的不等式（用方差、均值、t来估计大于t的比例），之后1减去它就是N2/N。&lt;/p&gt;
&lt;p&gt;但我们还不知道红色部分的均值，在VSSM中直接假设红色部分均值就是t（大胆的假设，假设的依据是阴影位置大概率是个平面）
&lt;img src=&#34;https://sdpyy1.github.io/be8ceb956e1d494d8554b489ecee84a7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
VSSM的效果&lt;img src=&#34;https://sdpyy1.github.io/74d05f7b9d7c414e86419b2132fe7ff5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
另外课程还提到目前PCSS是压过VSSM的，因为人们对噪声的容忍度越来越高，在图像层面进行降噪手段变强了&lt;/p&gt;
&lt;h3 id=&#34;satsummed-area-tables&#34;&gt;SAT（Summed Area Tables）
&lt;/h3&gt;&lt;p&gt;SAT是给一片区域立刻得到它的均值的一种数据结构&lt;/p&gt;
&lt;p&gt;SAT是对原数据的预处理得到的数据，在一维场景下，每个index记录都是前缀和（提前记录好），现在要求中间某一段的和，就直接拿出SAT在范围查询边界（左边是边界-1）的数据相减就行了
&lt;img src=&#34;https://sdpyy1.github.io/07f3c4580f624d6aafe41be6826e8e77.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
在二维情况下蓝色的范围查询，可以转化为左上角都是(0,0)的框加减操作
&lt;img src=&#34;https://sdpyy1.github.io/8cb7f27cf97f4adf9e6ea1fbeb4cac1f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;vssm的问题&#34;&gt;VSSM的问题
&lt;/h3&gt;&lt;p&gt;VSSM中作了很多假设，有一个就是假设一片区域纹素是符合正态分布的
例如右图深度只有3个峰值，不符合正态分布
&lt;img src=&#34;https://sdpyy1.github.io/aa6e951fd68249239b5e19793e93c89c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下图就是说本来只有一小部分没遮住，但是假设为正态分布变成了很大一部分，就会让阴影边白
&lt;img src=&#34;https://sdpyy1.github.io/13a872953e014f068ddf98e464e2db5a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
有一些地方变白了
&lt;img src=&#34;https://sdpyy1.github.io/54cefbba0016469d89dbc251c675249c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;moment-shadow-mapping&#34;&gt;Moment Shadow Mapping
&lt;/h2&gt;&lt;p&gt;为了解决VSSM分布描述不准确的问题，提出MSM，使用更高阶矩来描述分布
&lt;img src=&#34;https://sdpyy1.github.io/cea1241bde6b496d99b4891771f2ccf9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/9b05e04602c64889a6c3b4a5f0c266b8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
大概意思就是用数据的高阶来更好的拟合PCF的效果，下图表明用4阶矩下的分布就很好的达到了PCF的效果
&lt;img src=&#34;https://sdpyy1.github.io/694f857749eb4faf8383307c4cdbd3f8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
缺点很明显空间很大
&lt;img src=&#34;https://sdpyy1.github.io/7e05d414ae314233be94eef20d425425.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;distance-field-soft-shadows&#34;&gt;Distance Field Soft Shadows
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/7d064c8a32bd4ca48f6dc45c082cf7c4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
首先看一下Distance functions是什么,它描述了空间中任意一点到物体表面的最小距离，离得越远值越小，表示到图上就是变白
&lt;img src=&#34;https://sdpyy1.github.io/3fd17bb7bbc544e7a3ec382a89ff1a47.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
在任何一个点上，以它记录的值为半径画圆，都不可能碰到物体，如果做光线追踪，那可以直接移动到圆的表面上去，这就是SFD的一个应用
&lt;img src=&#34;https://sdpyy1.github.io/a7125637980b4220a54757ea99f6e3b6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下来用它来生成软阴影是它的另外一个阴影
用SDF可以大概描述多大的范围被物体挡住了，圆圈内为安全距离，不会有物体挡住，用这个圈的大小来定义visbility的大小，从而实现软阴影
&lt;img src=&#34;https://sdpyy1.github.io/d9ed1e511264484789edc877159fa42b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
在不同的位置找圆做切线，最小的切线就是需要的安全距离
&lt;img src=&#34;https://sdpyy1.github.io/263eeee958a84834acc700f7eee4cf11.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
求角度是一个acrsin的计算，比较慢，人们用一个替代来简化
&lt;img src=&#34;https://sdpyy1.github.io/2f5e03e72efa4c3797ca5374ecb73de6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
其中根据不同的k值来达到不同的效果
&lt;img src=&#34;https://sdpyy1.github.io/30c654e66f5e4dbeb49e36a7db26c7fc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
显而易见的优缺点 空间换时间，但这是不考虑生成距离场的时间
&lt;img src=&#34;https://sdpyy1.github.io/f3282cf582e24978bfcbda13be27e15b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES101 现代计算机图形学入门 Camera Lenses Light Fields Color and Perception</title>
        <link>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-camera-lenses-light-fields-color-and-perception/</link>
        <pubDate>Sun, 19 Oct 2025 12:49:13 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-camera-lenses-light-fields-color-and-perception/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/05414f77c7904ed5851265148aa16f58.png" alt="Featured image of post GAMES101 现代计算机图形学入门 Camera Lenses Light Fields Color and Perception" /&gt;&lt;h1 id=&#34;cameras&#34;&gt;Cameras
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/0212feac63754038823e0e1ee55c74c7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
小孔成像
&lt;img src=&#34;https://sdpyy1.github.io/277a3299df2c4a7a840c225aa53392a6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/481a70553db447f9adc33467f40f0247.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/05414f77c7904ed5851265148aa16f58.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
小孔成像是可以拍照的，但是没有景深效果&lt;/p&gt;
&lt;h2 id=&#34;视场-field-of-view-fov&#34;&gt;视场 Field of View (FOV)
&lt;/h2&gt;&lt;p&gt;传感器的高度与传感器和小孔之间的距离决定FOV的大小
&lt;img src=&#34;https://sdpyy1.github.io/46be80e1be924712a035c98cad524c71.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
通常都是固定传感器尺寸，控制焦距来控制FOV
&lt;img src=&#34;https://sdpyy1.github.io/57c59165023649119dd1e9678e725133.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
不同焦距下的视场
&lt;img src=&#34;https://sdpyy1.github.io/644ab57a421f444cbad7e74b50e6861a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;曝光-exposure&#34;&gt;曝光 Exposure
&lt;/h2&gt;&lt;p&gt;决定曝光的变量
&lt;img src=&#34;https://sdpyy1.github.io/09cbb038e7bf4d7bb9065eb0629adb82.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在摄像机中决定曝光的变量
&lt;img src=&#34;https://sdpyy1.github.io/12951eeafae74efe92248e404fb4aa0f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
第一行是光圈、第二行是快门速度、第三行是IOS设置
&lt;img src=&#34;https://sdpyy1.github.io/25ffc05ea69c460b93b5082c98567983.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
IOS的介绍
&lt;img src=&#34;https://sdpyy1.github.io/30db89edd702486d8aa0803a229109ac.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
光圈，N越大直径越小
&lt;img src=&#34;https://sdpyy1.github.io/688f2ee9555c48eb921b90412c3248c3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
快门
&lt;img src=&#34;https://sdpyy1.github.io/4646427f788448058de608465730cfb4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
因为快门打开需要时间，那高速运动物体就会出问题&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/d198f47b40a64f8d8b6cb4d9146b8d3c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;镜头&#34;&gt;镜头
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/8660f05786d74a46b24339783ded405b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
理想情况
&lt;img src=&#34;https://sdpyy1.github.io/5548fd95da8d4ac1ba2a22de2ddc5e59.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
焦距物距像距的关系
&lt;img src=&#34;https://sdpyy1.github.io/8384f6f89d914a039d371693feec8818.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/2f335c402e9049ca98c2262b67c140aa.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
景深就是像距没有落到感光元件上
&lt;img src=&#34;https://sdpyy1.github.io/9743de7b29944420b98b0f4530aab3f3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
这节主要是科普摄像机的东西&lt;/p&gt;
&lt;h1 id=&#34;光场-light-field&#34;&gt;光场 Light Field
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/c3e7349ff74e44b6951d6426226d813a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
人眼前放一块幕布，投影这个图形，对人来说看到的是一样的
&lt;img src=&#34;https://sdpyy1.github.io/594d74a32b7842b3aa08a5277edf00fa.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;全光函数&#34;&gt;全光函数
&lt;/h2&gt;&lt;p&gt;用全光函数表示人能看到的东西
&lt;img src=&#34;https://sdpyy1.github.io/e1a3a5871dd942d49af14e46f4fd0ea0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;加粗样式&#34;
	
	
&gt;
从简单来看
&lt;img src=&#34;https://sdpyy1.github.io/d628c3573ceb47e4959ac0204624a5e4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
引入颜色
&lt;img src=&#34;https://sdpyy1.github.io/e1b7c163732848649b19280a2787a3de.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
引入时间，变为电影
&lt;img src=&#34;https://sdpyy1.github.io/eae45b0a46324553adca0b8bf95a1252.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
引入位置。全息电影
&lt;img src=&#34;https://sdpyy1.github.io/d7a3e97a98b44d0fa496f694e0927d49.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
用七个维度衡量看到的所有东西，光场从这里开始&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/e4d5f2f6fff4402cb2bd86865a278a62.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
从任意一个位置看向任何一个位置，都可以从光场中提取光的信息
&lt;img src=&#34;https://sdpyy1.github.io/8750ede794b14384bb225606075f4a3a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
并不需要知道光场内是什么物体，只需要光场的信息，在盒子表面任何一点任何一个位置的信息
&lt;img src=&#34;https://sdpyy1.github.io/4c9c6bc46c3440a4ba0a250c6aa883c4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;颜色-color&#34;&gt;颜色 Color
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/626e79e259a946daa88de4a32306b39f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/f5d27f54201d4d869024a66c55c45c33.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/88fc3df638544ab9a2303199d48f7b1c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES101 现代计算机图形学入门 Animation/Simulation</title>
        <link>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-animation/simulation/</link>
        <pubDate>Sun, 19 Oct 2025 12:48:02 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-animation/simulation/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/49ba0bbdd1d24a848de86f532c675a7e.png" alt="Featured image of post GAMES101 现代计算机图形学入门 Animation/Simulation" /&gt;&lt;h1 id=&#34;一些科普&#34;&gt;一些科普
&lt;/h1&gt;&lt;h2 id=&#34;keyframe-animator&#34;&gt;Keyframe Animator
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/254f33ee74a74430a5be6baf38e5d1f5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;physical-simulation&#34;&gt;Physical Simulation
&lt;/h2&gt;&lt;p&gt;利用物理公式来模拟
&lt;img src=&#34;https://sdpyy1.github.io/91f335168e244e1ea749d0d67d302687.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;质点弹簧系统-mass-spring-rope&#34;&gt;质点弹簧系统 Mass Spring Rope
&lt;/h2&gt;&lt;p&gt;下面开始描述这个系统
胡克定律描述弹簧或弹性体所受的回复力 F 与形变量 x 成正比， $k_s$是劲度系数，但是弹簧本身也有长度
&lt;img src=&#34;https://sdpyy1.github.io/25049f6423a34d6a898ad8f34f17e20d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;考虑弹簧原始长度 ，$\frac{b - a}{|b - a|}$这个向量用来表示方向，但问题是这样写，弹簧一旦拉开就不会不停的运动下去（动能和势能相互转化），所以还需要引入摩擦力
&lt;img src=&#34;https://sdpyy1.github.io/110547693de240849dbc157c4afa95ce.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
引入摩擦力之前先引入一些符号表示位置、速度、加速度
&lt;img src=&#34;https://sdpyy1.github.io/1a0fdeee467b47318d4eccd8e33ae216.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
给物体一个相反与速度方向的力，但还有问题，这样会导致所有的运动都停下来，假设AB两个物体同步向右走，他俩都有速度，但相对之间没有速度，所以应该没摩擦力，但是用下面的公式，只要物体有速度，就会受到摩擦力而停止
&lt;img src=&#34;https://sdpyy1.github.io/14a447de54d34ed391bbf45e9f856c14.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
我们在逐渐完善弹簧系统，摩擦力应该跟着物体AB的相对运动而产生，下图用两个物体速度差来表示相对速度，另外还需要考虑这个速度差是沿着弹簧方向才行，所以点乘$\frac{b - a}{|b - a|}$来投影到ba方向
&lt;img src=&#34;https://sdpyy1.github.io/a1438c4208f446f6b6af25aefb047a62.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下面看一些弹簧结构&lt;img src=&#34;https://sdpyy1.github.io/bf8f492757154e20ae44b810420dfcb7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
模型的行为是由结构连接来决定的，下面看如果一块布这样设计，很明显是不合理的，图中展示了2种原因（布可以抵抗切变力但这个结构不行，布会对抗对折）
&lt;img src=&#34;https://sdpyy1.github.io/6b48a3875c18466e8ebc2c0aed92d4fd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
先来改进切变，加入蓝色弹簧来抵抗&lt;img src=&#34;https://sdpyy1.github.io/fc36d6c0ec0b45e9b18956c7bad91719.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
再加另外一个方向，但不能解决第二个问题
&lt;img src=&#34;https://sdpyy1.github.io/b0a5fe3bed954fa284c3e6d218d2bab8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终版
&lt;img src=&#34;https://sdpyy1.github.io/39275121565e476a9ff80c4325673c09.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;粒子系统&#34;&gt;粒子系统
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/48203561654e46c1b304d23a609f9e96.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/2e6cc29304194f129916e28fdba8bae2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;运动学-forward-kinematics&#34;&gt;运动学 Forward Kinematics
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/800e646fb8dc48c7b31d322eded58823.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
已知角度求P的位置
&lt;img src=&#34;https://sdpyy1.github.io/703cbcdc115340778d35738f211ec57e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;逆运动学inverse-kinematics&#34;&gt;逆运动学Inverse Kinematics
&lt;/h2&gt;&lt;p&gt;已知P的位置求角度
&lt;img src=&#34;https://sdpyy1.github.io/b62327776513418c87a405914d33ddba.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;rigging&#34;&gt;Rigging
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/1f420e4a8bde4f1db3da9f9895c7b114.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/49ba0bbdd1d24a848de86f532c675a7e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;motion-capture&#34;&gt;Motion Capture
&lt;/h2&gt;&lt;p&gt;![请添&lt;img src=&#34;https://sdpyy1.github.io/bf2d01f5eb5f4bcd8eae491584418063.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;第二次课-cont&#34;&gt;第二次课 cont.
&lt;/h1&gt;&lt;h2 id=&#34;single-particle-simulation&#34;&gt;Single Particle Simulation
&lt;/h2&gt;&lt;p&gt;首先学习单粒子运动，假设粒子的运动由一个速度向量场决定，速度向量场的速度由位置和时间决定
&lt;img src=&#34;https://sdpyy1.github.io/f02f0ad1b7cd45e98ac0935b806f85b6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
要计算粒子在某个时间的位置，需要解下图所示的常微分方程
&lt;img src=&#34;https://sdpyy1.github.io/de4f27953eb44c24917d53b78be5046f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
用欧拉公式来解微分方程，用上一步的数据来计算下一步的位置
&lt;img src=&#34;https://sdpyy1.github.io/a107c18e085447c09e0242d062cd108d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
欧拉方程的误差与步长大小有关
&lt;img src=&#34;https://sdpyy1.github.io/16f272e6dc224dba89340ffb8d8a7eff.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
欧拉方程的不稳定性，在一个螺旋速度场中，本来应该旋转，但是不管步长选的多小都一定会飞出去。第二幅图的运动模拟也不合理。
&lt;img src=&#34;https://sdpyy1.github.io/5830c8a8200546aeac8aa4f58bce0b4f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
如何解决呢，有人提出了中点法，先用欧拉算出a点，但不用a点，取中点b，考虑b点的速度，应用b点的速度回去重新算欧拉
&lt;img src=&#34;https://sdpyy1.github.io/e32beae8a0994f9d83cecac488b87535.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
进一步，有人用中点思想，算两次欧拉方法，不是回原点重新算，而是去中点算，如果算出来和第一次算出来差不多就不算了，说明步长够小了
&lt;img src=&#34;https://sdpyy1.github.io/d577bca230be4e05aba92260bee39e42.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
还有隐式的欧拉方法
&lt;img src=&#34;https://sdpyy1.github.io/a3bde1586cb94a169354526f9e18eaa1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
Runge-Kutta Families
&lt;img src=&#34;https://sdpyy1.github.io/6505e56ffe404bcd8a17749f9b9b7f9f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
Position-Based / Verlet Integration
&lt;img src=&#34;https://sdpyy1.github.io/30ea8e236c8b4792a83b6a0cf536de57.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
刚体（不会发生形变）模拟
&lt;img src=&#34;https://sdpyy1.github.io/32fd87b928d640f8ad20b95caa79d2d8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;流体模拟-fluid-simulation&#34;&gt;流体模拟 Fluid Simulation
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/f0dc6630895540ebbe11b9880dd7c8dc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/050ad9b02d4449e6a2605cbe04ca9999.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/35a97b81986e4258bb49b15dedacf91f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
这门课的动画模拟更多都是在科普。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES101 现代计算机图形学入门 Ray Tracing</title>
        <link>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-ray-tracing/</link>
        <pubDate>Sun, 19 Oct 2025 12:43:12 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-ray-tracing/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/a6d366b54c3a45b38beb9e550a1ea53b.png" alt="Featured image of post GAMES101 现代计算机图形学入门 Ray Tracing" /&gt;&lt;h1 id=&#34;shadow-mapping&#34;&gt;Shadow Mapping
&lt;/h1&gt;&lt;p&gt;如果使用光栅化，对一个像素shading时考虑光源，考虑摄影机位置，但是没有考虑模型其他位置对该位置的影响，因为在光源与该像素点之间有别的东西挡住，那光源就会被挡住，形成阴影。shading解决不了阴影问题&lt;img src=&#34;https://sdpyy1.github.io/070b07cef83249b58bc9e15c0ecad44f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
该算法为图像空间算法。生成阴影并不需要模型的几何位置信息，当前该算法还是存在走样现象
该算法的关键思想是：一个点不在阴影的前提是摄像机能看见这个点，并且光源能看见这个点
算法步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Render from Light：把光源当作摄像机，做一遍光栅化，从而得到光源会看到什么东西，只需要记录看到点的深度&lt;img src=&#34;https://sdpyy1.github.io/9f10d04ef11e4a32b576c3a930e46306.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;从设定好的摄像机位置去真正的渲染场景得到摄像机视角的深度Buffer，如下图橙线下的点两次都看见了，而红线上的点只有光源嫩看见，摄像机是看不见的&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/06a0886a48024e748254c75b69294dde.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/6c4150bdd8654ce98bad8f50e399a8a1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
举一个实际的例子&lt;img src=&#34;https://sdpyy1.github.io/b354e3a4f7d24f11ab9c769a08a3798e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
上图左上角有一个点光源，下图则展示了从点光源看向模型的样子
&lt;img src=&#34;https://sdpyy1.github.io/95dc6014690643119b5184237977dbaa.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
当然这一步只需要记录每个点的深度
&lt;img src=&#34;https://sdpyy1.github.io/14bd8c040528438980b8204b68355808.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终再回到摄像机的视角，绿色就是摄像机和光源都能看见的位置，不是绿色的位置就是阴影的位置
&lt;img src=&#34;https://sdpyy1.github.io/b2a879aef32d4a5fa398e658c97d364e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
shadow maps的问题
首先解释一下硬软阴影区别，看两幅图就能理解&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;硬阴影 ：&lt;img src=&#34;https://sdpyy1.github.io/9cee7031eb6c4771bd2917356df07717.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;软阴影： 产生软阴影是因为光源具有体积，导致，有的地方完全看不到光源（本影, Umbra）， 有的地方能看到一部分光源（半影，Penumbra）。所以阴影的边缘会有过渡的情况，从而产生软阴影，就像上图中太阳与地球的示意一样（全日食与半日食）。&lt;img src=&#34;https://sdpyy1.github.io/80801ad9fed748809c1f8746872b87de.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/ef8690c0e727412da5f47f3f2614948d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;ray-tracing&#34;&gt;Ray Tracing
&lt;/h1&gt;&lt;h2 id=&#34;why-ray-tracing&#34;&gt;Why Ray Tracing
&lt;/h2&gt;&lt;p&gt;为什么需要光线追踪？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;光栅化不能很好地处理全局效果&lt;/strong&gt;，如软阴影（光栅化可通过Shadow Mapping实现硬阴影（老师说目前也有技术实现软），由于缺乏全局信息，需要多次计算，如上一节提到的把光源当作摄像机先计算一遍深度缓存）。&lt;strong&gt;另外是当光多次弹射时，光栅化不好处理&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;光栅化很块，但是质量低&lt;/li&gt;
&lt;li&gt;光线追踪很准确，光栅化主要用于实时，光线追踪用于离线&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;图形学对光线的假设&#34;&gt;图形学对光线的假设
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;光线沿直线传播&lt;/li&gt;
&lt;li&gt;光线和光线不会碰撞&lt;/li&gt;
&lt;li&gt;光线从光源出发经过各种反射到达人眼（光路可逆，光线追踪利用的就是光路的可逆性）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;ray-casting-光线投射&#34;&gt;Ray Casting 光线投射
&lt;/h2&gt;&lt;p&gt;如下图所示，摄像机连出一根线到屏幕的一个像素点，点到达物体表面时，该点也能连接到光源，就可以计算出这一点的光的强度来进行着色&lt;img src=&#34;https://sdpyy1.github.io/778ae350cfad46c895f14ee6896469c8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
具体例子如下图
从眼睛发出线打到一个像素，紧接着达到场景中的某个位置上（eye ray），只考虑与场景最近的一个交点，所以后边的虚线就不考虑了（&lt;strong&gt;在作业5中可知，生成eve ray的流程：屏幕是Raster Space（光栅空间），为了找到摄像机到每个栅格真正代表的空间坐标，需要将Raster Space还原成NDC space再进一步还原成&lt;/strong&gt;）
&lt;img src=&#34;https://sdpyy1.github.io/7695f1443239413d928c0d6902098c6a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
紧接着连接一条线到光源位置（shadow ray），有了法线、入射方向、观察方向，就可以利用光照模型来计算着色了（例如 Blinn Phong model）
&lt;img src=&#34;https://sdpyy1.github.io/ba3a58d920ad4b41bf423b128a3c8700.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;recursive-ray-tracingwhitted风格&#34;&gt;Recursive Ray Tracing（Whitted风格）
&lt;/h2&gt;&lt;p&gt;上一节的模型仍然只考虑了光线只反射一次的情况，还是从上一节的图开始，假设eye ray打到的是一个玻璃球，可能会发生折射和反射，每一个弹射点都进行着色计算（都连接到光源），每个着色都会被加到这个像素点的着色上去。
&lt;img src=&#34;https://sdpyy1.github.io/3f16764a19d34cc0a22858445d980b95.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
里边具体的技术点会在后边讲到&lt;/p&gt;
&lt;h3 id=&#34;ray-surface-intersection-隐式几何与光线求交&#34;&gt;Ray-Surface Intersection （隐式几何与光线求交）
&lt;/h3&gt;&lt;p&gt;首先用数学定义一下光线：光线被定义为由起点和方向的向量，点光源的描述如下，这样定义的目的是将光线与物体求交点的问题转化为求t值的问题&lt;img src=&#34;https://sdpyy1.github.io/21d57cd70e7a418988bcfbaa6e80b9a2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
从简单的开始，光线如何与球求交点，其实就是点P在球面上，也在光线上，联立即可
&lt;img src=&#34;https://sdpyy1.github.io/4fc1a32d8fc740099ff75ec4aa6da3e6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终可以解出来t的大小，t取小的一个，表示第一个交点
&lt;img src=&#34;https://sdpyy1.github.io/d3f943f286b64f469b3218b89894d12b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;ray-intersection-with-triangle-mesh显示几何与光线求交&#34;&gt;Ray Intersection With Triangle Mesh（显示几何与光线求交）
&lt;/h3&gt;&lt;p&gt;由上边内容可知，给出一个隐式的几何，求光线与几何的交点，就是联立后求t的值，那显式的表达呢？如下图，如何判断光线与mesh有交点呢？一个一个三角形求光线与三角形的交点么？太慢了&lt;img src=&#34;https://sdpyy1.github.io/a6d366b54c3a45b38beb9e550a1ea53b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
虽然太慢了，但是还是要先学习一下怎么求一个光线与三角形的交点。 基本思路就是先找出光线与三角形所在平面的交点，之后判断这个交点是不是在三角形内部。
&lt;img src=&#34;https://sdpyy1.github.io/f856d53bbdd740dc8b7b47b179dd3665.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
首先来看一下平面如何定义，一个向量和一个点就可以定义一个平面
&lt;img src=&#34;https://sdpyy1.github.io/c663ec0e99194ff682d187e5e7f71c7e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如何判断一个点P是否在平面上呢？只要下图两点形成的向量与平面法向量垂直即可
&lt;img src=&#34;https://sdpyy1.github.io/42537bef31f14ce1be0be6bccdb11bfd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
所以又回到了上一节的联立，点P在平面上，也在光线上，进行联立求解，最后得到光线与平面的交点，再判断点是否在三角形内，就可以得到光线与三角形的交点。
&lt;img src=&#34;https://sdpyy1.github.io/0f382f822e5043c8af6653a2966f4db2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;strong&gt;Möller Trumbore Algorithm&lt;/strong&gt;
那有没有方法直接判断呢？如果光线与三角形有交点，那这个交点就可以用重心坐标表示，直接建立如下等式，用克莱默法则求解方程组就可以直接求出来（三个未知数，三个方程，所以是可以解出来的）&lt;img src=&#34;https://sdpyy1.github.io/0d9b97e41094499aa8462e282d491ec1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;accelerating-ray-surface--intersection加速求交方法&#34;&gt;Accelerating Ray-Surface  Intersection（加速求交方法）
&lt;/h3&gt;&lt;p&gt;现在已经知道了光线与三角形求交，那如何判断光线与mesh的交点呢。一个一个三角形遍历太慢了。如下图三角形实在太多了。这就引入了Accelerating Ray-Surface  Intersection&lt;img src=&#34;https://sdpyy1.github.io/5d5da1bf07f1420bac89e3a912684d22.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
先介绍一个概念 Bounding Volumes，如果光线都打不到包围盒，更打不到mesh了&lt;img src=&#34;https://sdpyy1.github.io/3c6503a6f4e84e15aa70c4acd8204fa5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
想象一下一个box相对的两个面是一个无限大的平面（称为对面），box就是三个对面的交集，通常使用的是Axis-Aligned Bounding Box (AABB) (轴对⻬包围盒)，这个东西就是这个盒子的边都是和坐标轴对齐的，简化计算。
&lt;img src=&#34;https://sdpyy1.github.io/1dd9a009f90a44b8a804b987a130f349.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下面就来看下光线与包围盒的求交问题（与包围盒有交点，才去考虑与包围盒内部的Mesh求交）
先从二维平面情况下看, $x_0$和$x_1$是一个对面，$y_0$和$y_1$是一个对面
从$x_0$和$x_1$对面上看可以求出两个交点
&lt;img src=&#34;https://sdpyy1.github.io/d0d9fd7184004d178696c26dbe881598.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
从$y_0$和$y_1$对面上看也可以求出两个交点
&lt;img src=&#34;https://sdpyy1.github.io/9a17787f7af8484684a375329e8db255.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
求交集后，就求出光线进入和出去盒子的时间&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/0d37d16853c54e308d2e801265ca5bc2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
从三维情况下开，3组对面的时间t进行求交集，当进入时间小于离开时间，就是有交点&lt;img src=&#34;https://sdpyy1.github.io/f106c53781c14b0081a51dd2c4a1d23b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
另外，用上诉算法算出来的t可能是负数，用下图进行处理，总结来说，进入小于退出，退出大于0即可
&lt;img src=&#34;https://sdpyy1.github.io/d60850637708462a994fe1641df721a0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最后解释一下为什么要用AABB盒，光线与平面求交点是有公式的，如下图General。对准坐标轴后计算更容易，下图为计算量对比。
&lt;img src=&#34;https://sdpyy1.github.io/fe49a66e725e40439ea07f5759905032.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;uniform-spatial-partitions-grids-均匀网格&#34;&gt;Uniform Spatial Partitions (Grids) 均匀网格
&lt;/h3&gt;&lt;p&gt;通过上节的加速算法，我们已经知道如果把物体包在盒子里，先判断与盒子求交，再考虑与盒子内物体求交，但采用包围盒并不一定会提升性能，例如下面两种情况&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;整个场景只有一个极其复杂的单一人物模型，那么只对这一个物体做包围盒的话，相当于对效率没有任何提升&lt;/li&gt;
&lt;li&gt;整个场景充斥着大量的细小模型，如草，花之类的，每个模型可能只有很少的面，如果此时对每个物体求包围盒，得到的包围盒数量会相当之多，对于光线追踪效率来说效率提升有限。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以还要对包围盒进行进一步的处理
首先来进行预处理
4. 找到包围盒
5. 画出格子（盒子内部再分成很多的格子）
6. 标记有物体的格子
&lt;img src=&#34;https://sdpyy1.github.io/ae7b2fd3892542ff9be22f0ca1bca9ee.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
预处理完成后，光线从一侧打进来，只会处理被标记的格子，如果有标记，就算一下是否有交点，这就避免了和包围盒中所有的物体求交，这种方法主要就是解决了，包围盒中有多个物体，只要光线打到了具体一个范围时，才判断是否有交点。
&lt;img src=&#34;https://sdpyy1.github.io/fe124cfa32d343688877915adbcae356.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Uniform Grids表现比较好的场景如下
&lt;img src=&#34;https://sdpyy1.github.io/187554f07ea44471a4ecd2fb4e2dc1e9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;但分布特别不均匀的场景，就不适合用上述方法&lt;/p&gt;
&lt;h3 id=&#34;spatial-partitions-不均匀的网格&#34;&gt;Spatial Partitions 不均匀的网格
&lt;/h3&gt;&lt;p&gt;在物体比较少地方没必要用统一大小的格子，那如何进行场景划分呢，有如下3中方式
第一种Oct-Tree，也就是八叉树，每次将空间分为8个相等的部分，再递归的对子空间进行划分。因为图中是2维例子，所以只划分了4部分。当划分的子空间足够小或是空间中三角形面的数量很少的时候会停止划分。这种方法的显著缺点是，随着维度的上升划分的空间数量会呈指数级增长。&lt;/p&gt;
&lt;p&gt;第二种KD-Tree，其每次将空间划分为两部分，且划分依次沿着 x − a x i s ， y − a x i s ， z − a x i s x-axis，y-axis，z-axisx−axis，y−axis，z−axis （保持最规整的空间区域），如图中所示，第一次横着将2维空间分为上下，第二次再竖着将上下两个子空间分别划分为左右部分，依次递归划分，终止条件与八叉树类似。&lt;/p&gt;
&lt;p&gt;第三种BSP-Tree，其与KD-Tree类似，唯一不同的是划分不再沿着固定一轴，可以任意方向划分，缺点自然是划分的空间没有规则性，求交困难。在与轴对齐更好计算的场景下不适用&lt;/p&gt;
&lt;p&gt;本节知识主要以KD-Tree来建立
&lt;img src=&#34;https://sdpyy1.github.io/60c2a05bc417414ea85e271a5010a697.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
给定一个场景，先建立KD-Tree，做好加速结构，再进行光线求交。
下面从预处理开始介绍，竖直先来一刀
&lt;img src=&#34;https://sdpyy1.github.io/14997e1e389d435ea294c327edfad63a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
水平再来一刀，蓝绿都需要做，这里演示只砍绿色
&lt;img src=&#34;https://sdpyy1.github.io/2cd1b75040684c07b44060b058bdf346.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
再来两刀
&lt;img src=&#34;https://sdpyy1.github.io/a4280856cb9d420580159f3b76694775.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
明白了KD-Tree的流程之后，介绍一下KD-Tree的数据结构
实际的三角形object只存在叶子结点上
&lt;img src=&#34;https://sdpyy1.github.io/201a0395e12f4d60b250a10209e88883.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下一步介绍光线来了之后如何与KD-Tree交互，如下图的这条光线
&lt;img src=&#34;https://sdpyy1.github.io/9d2835ca8d6b40ad85b408bf31515780.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
首先判断与最大的盒子是否有交点，如下图所示是有的&lt;img src=&#34;https://sdpyy1.github.io/bff09c35aad543ef8e35474acf094d32.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
紧接着判断A结点的子结点1是否有交点，此时发现确实有交点，1是叶子结点，那光线就必须和这个盒子里的物体进行计算求交
&lt;img src=&#34;https://sdpyy1.github.io/bd664ac5779647f6a3327d2bdaf66aed.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
之后又发现与右边也有交点，那么就需要继续看子结点
&lt;img src=&#34;https://sdpyy1.github.io/7ff651b4921b47748c84ae5e56375149.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
子结点2也有交点，是叶子结点，那就得和2中所有物体求交，后续流程一样
&lt;img src=&#34;https://sdpyy1.github.io/9a31bdf5fd8d46ba9a4bdec01908c4b5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;bounding-volume-hierarchybvh&#34;&gt;Bounding Volume Hierarchy（BVH）
&lt;/h3&gt;&lt;p&gt;KD-Tree并不完美，缺点是判断包围盒与三角面的是否相交较难，因此划分的过程不是那么想象的简单，其次同一个三角面可能被不同的包围盒同时占有，这两个不同包围盒内的叶节点会同时存储这一个三角形面。
BVH不再通过场景进行划分，而是通过物体来划分，得到了广泛应用。
首先把一个包围盒内的三角形组织成两部分，并对两部分三角形重新求包围盒，如下图
&lt;img src=&#34;https://sdpyy1.github.io/bd18e938e6bf45e9b3ec6f8b2c482ce4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
之后重复操作，划分到一个叶子结点只有比如5个三角形就停止，这样一个三角形只会出现在一个包围盒里。避免了KD-Tree一个三角形可能出现在不同的叶子结点里的问题
&lt;img src=&#34;https://sdpyy1.github.io/ad6eb2fd3d394d83b014dd2c8fcae7e2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
那如何进行结点划分呢？首先向KD-Tree学习，每次选一个x、y、z其中一个轴进行划分。技巧：每次都找最长的轴进行划分，例如场景在x轴上是一个长条，就利用x轴划分。如何分成两半呢（如何保证划分后两部分三角形数量差不多呢）？通过三角形的重心坐标，在x轴上排序（其实不用排序，有更好的找中位数算法叫快速旋转算法，可以在O(n)时间内找到中位数），就知道中间那个三角形是哪个了，这样就进行了划分。
&lt;img src=&#34;https://sdpyy1.github.io/eea3ae716eef4e8c9bdc0d33b222e28b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下来介绍光线与BVH求交的过程，用代码演示
&lt;img src=&#34;https://sdpyy1.github.io/9620275ad19f414a959695cb3775b424.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
划分空间vs划分物体
&lt;img src=&#34;https://sdpyy1.github.io/a1ead06dbdb74b018ca9a1db54034269.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
到这里，求交的内容就结束了&lt;/p&gt;
&lt;h2 id=&#34;basic-radiometry辐射度量学&#34;&gt;Basic Radiometry（辐射度量学）
&lt;/h2&gt;&lt;p&gt;在之前实现 Blinn-Phong模型时，光照强度I设置为10，但是10是什么呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;whited-style光线追踪只考虑了光滑面的镜面反射与折射，并没有对漫反射的光线进行追踪，而是直接返回当前着色点颜色&lt;/li&gt;
&lt;li&gt;在计算光源直接照射的贡献时，使用了Blinn-Phong模型，而Blinn-Phong模型本身就是一个不准确的经验模型，使用的这种模型的whited-style光线追踪自身自然也是不正确的&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为此，更好的渲染模型路径追踪出现了，而在这之前，我们必须掌握一些辐射度量学的知识，它是对光照的一套测量系统和单位，能够准确的描述光线的物理性质。辐射度量学是物理上准确定义光照的方法。首先来引入一些概念&lt;/p&gt;
&lt;h3 id=&#34;引入定义&#34;&gt;引入定义
&lt;/h3&gt;&lt;h4 id=&#34;radiant-energy-and-flux-power-辐射能量和辐射通量功率&#34;&gt;Radiant Energy and Flux (Power) 辐射能量和辐射通量/功率
&lt;/h4&gt;&lt;p&gt;辐射能量就是光源射出来的电磁能量，单位为焦耳   （功）
&lt;img src=&#34;https://sdpyy1.github.io/730c220ea2044eb0a383d685b9c37c75.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Radiant flux(power) 辐射能量的基础之上除以时间，也就是单位时间的能量（功率、流明）
&lt;img src=&#34;https://sdpyy1.github.io/40b80f5686794616ac812da10f358407.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
在计算机图形学来看，主要关注一下三个东西
&lt;img src=&#34;https://sdpyy1.github.io/c0c1c954d7f241468741251dd1e2ee0f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h4 id=&#34;radiant-intensity-辐射强度&#34;&gt;Radiant Intensity 辐射强度
&lt;/h4&gt;&lt;p&gt;从一个点光源发射的每单位立体角下的功率，光源向四面八方辐射能量，在单位立体角上的功率就是辐射强度
&lt;img src=&#34;https://sdpyy1.github.io/598f799b2a644a65b3e75335454d9952.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
那什么是立体角呢？
首先2维下的角度就是弧度/半径，3维空间中立体角就是一块面积/半径的平方，立体角就是用来描述空间中角有多大，一个球体总的立体角就是4pi
&lt;img src=&#34;https://sdpyy1.github.io/4c371678d35e4824a436e76db36c52a4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下图描述了单位立体角。在微分下，形成的一个很小的面积的立体角，是一个微分立体角dw，计算公式如下
&lt;img src=&#34;https://sdpyy1.github.io/dc876a9c2730410f9dfa5f1d5a26b16d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终可以得到 辐射强度 = 辐射功率/4pi
&lt;img src=&#34;https://sdpyy1.github.io/d8e6ed8ccd8f44978d0205d5f1d2571a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h4 id=&#34;irradiance&#34;&gt;Irradiance
&lt;/h4&gt;&lt;p&gt;每单位照射面积dA所接收到的power/flux
&lt;img src=&#34;https://sdpyy1.github.io/45cc11dddbed48aea7bd0ffdcbf832e8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
光源能量在距离上有r平方的衰减，可以用irradiance来解释，远处单位面积更大，按r平方衰减，Intensity并没有变化，单位角也没有变化，变化的是单位面积，进而影响了Irradiance&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/677134988a10429aa5c3f843f85ea424.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h4 id=&#34;radiance&#34;&gt;Radiance
&lt;/h4&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/29b0c3b5fed74a8a8cb26b7339d1d91b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Radiance是 单位立体角、单位面积上的power
&lt;img src=&#34;https://sdpyy1.github.io/c787e8ad444046758171c11a07588335.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/1c8db947cdc94c51ac7c1baa934a9dc3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Incident Radiance是到达表面的irradiance 在 per 立体角下
&lt;img src=&#34;https://sdpyy1.github.io/e492bf0a506f485eb17163e667af97cd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
Exiting Radiance 是 开表面辐射强度在per单位面积上&lt;img src=&#34;https://sdpyy1.github.io/81a491efec804d158be94079f61a6c31.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最后看一下Irradiance和Radiance的关系&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Irradiance是单位面积上的总能量&lt;/li&gt;
&lt;li&gt;Radiance是总能量在单位立体角上的大小，就是在Irradiance上增加了方向性（radiance在方向上积分就是Irradiance）
&lt;img src=&#34;https://sdpyy1.github.io/06d90287db5e41248b4a39ba59975bbb.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;brdf&#34;&gt;BRDF
&lt;/h3&gt;&lt;p&gt;Bidirectional Reflectance Distribute Function 双向反射分布函数，在介绍完上边概念的定义后，可以这样理解光线的反射，一个微分面积元在接受到一定方向上的亮度后，再向不同方向把能量辐射出去，所谓BRDF就是描述一个从不同方向入射之后，反射光线分布情况的函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/33fd1bfcb40d466f821b852a5a03ad34.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下图为BRDF的方程，只需要两个参数入射光方向 ωi，反射光方向 ωr，函数值为反射光的radiance与入射光的irradiance的比值
&lt;img src=&#34;https://sdpyy1.github.io/ece69c53fd0b48c4b63640326280bd7a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
摄像机所在方向上的反射光，是由来自不同方向入射光线的irradiance经过反射得到的，不同方向上的入射光线的irradiance的贡献由BRDF函数决定
&lt;img src=&#34;https://sdpyy1.github.io/3b0a4643e1fd45a1be6bbfc31accd825.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
但是入射光的radiance不仅仅来自光源，也可能是其他物体的反射光恰好反射到了该点，这是一个递归的过程
&lt;img src=&#34;https://sdpyy1.github.io/474c15de3c4c4180954115688e5c2bb7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;进一步，如果这个点本身就会发光，需要额外加上这部分，最终的渲染方程为下图
&lt;img src=&#34;https://sdpyy1.github.io/4360789f62274e29b3aabdfd7a0a1f6a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如何理解渲染方程呢？
从反射方程来看，如果有多个光源，就把这些radiance进行累加
&lt;img src=&#34;https://sdpyy1.github.io/182c0b02b941422dae4c9a73b617b018.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如果有一个面光源，对这个面对应的立体角进行积分
&lt;img src=&#34;https://sdpyy1.github.io/e03e5ea0150043dda842e9ec47bdb131.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
那么更进一步再在场景当中加入其它物体，使得物体之间发生光线交互之后是什么情况呢。如下图所示，可以把其它物体同样考虑成面光源，对其所占立体角进行积分即可，只不过对其它物体的立体角积分不像是面光源所有入射方向都有radiance，物体的立体角可能只有个别几个方向有入射的radiance（即多次物体间光线反射之后才恰好照射到着色点），其它方向没有，但本质上都可以视作是面光源。
&lt;img src=&#34;https://sdpyy1.github.io/c8505b098d7f4eea96af9b1e7518cded.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终整理方程，只有入射和反射的radiance不知道，其他项都是知道的
&lt;img src=&#34;https://sdpyy1.github.io/7cf912c3e771476d82d478af5325f43b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
简化形式
&lt;img src=&#34;https://sdpyy1.github.io/299297279df044b082c1cc6b51e45660.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
经过一系列变化后得到，L是所要求的反射光，E是自己的发光，K是一个算子
&lt;img src=&#34;https://sdpyy1.github.io/f2882cfc2b75493f8262e0abc17386bc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终可以得到
&lt;img src=&#34;https://sdpyy1.github.io/03581f587bcd4f0ea2102d0e5ee77bef.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终最终整理成如下形式，E是自身发光，KE是光源反射一次结果（光源直接照射），Blinn-phong模型就只考虑到这一层，K2E是弹射一次的光照&amp;hellip;..
&lt;img src=&#34;https://sdpyy1.github.io/50da0733963b4e259c18f0646f22038d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;下图展示了处理弹射次数变多时的变化，场景越来越亮并且逐渐收敛
&lt;img src=&#34;https://sdpyy1.github.io/af40e90ef0f94acf8b26241897aadb07.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;img src=&#34;https://sdpyy1.github.io/b4fe2f603f504a51842d83e722f53384.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/fdbadc2e48894517aa4e52729c38fdbd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/64173e8c85444a19bbd65438af052815.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/5f23c3cf279f4e8687d3fc40dddbb1aa.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
这一部分就结束了，主要就是从辐射度量学角度来计算光照。直接光照和弹射后的光照混合就形成了全局光照&lt;/p&gt;
&lt;h1 id=&#34;monte-carlo-path-tracing-蒙特卡洛路径追踪&#34;&gt;Monte carlo Path Tracing 蒙特卡洛路径追踪
&lt;/h1&gt;&lt;h2 id=&#34;monte-carlo-integration-蒙特卡洛积分&#34;&gt;Monte Carlo Integration 蒙特卡洛积分
&lt;/h2&gt;&lt;p&gt;蒙特卡洛是用来解一个原函数不好解析的定积分计算
&lt;img src=&#34;https://sdpyy1.github.io/0a45824c28574d719b2530e9d819d801.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
它的基本思想就是通过随机采样获得很多f(x)来进行估计
&lt;img src=&#34;https://sdpyy1.github.io/62b60924d1c14380a4a226b8e0ca72c7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;更一般情况下，概率函数取任意一个，用下边公式就可以求一个定积分
&lt;img src=&#34;https://sdpyy1.github.io/e2eff3901d754a2b8e597a6b07ca2a69.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;path-tracing-路径追踪&#34;&gt;Path Tracing 路径追踪
&lt;/h2&gt;&lt;p&gt;之前已经学习了Whitted-Style Ray Tracing，从摄像机射出光线遇到物体进行镜面反射或折射，每次弹射都与光源连线，但这里面有一些不符合物理的情况
问题一： Whitted-Style假设反射时完美的，没有任何模糊或扩散，但是对于Glossy表面，现实中光线会在一个范围内散射，而不是单一方向的镜面反射
&lt;img src=&#34;https://sdpyy1.github.io/4338829c85734a12ae7d2bb2d9a083f6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
问题二：光的漫反射应该真正的向四面八方反射开，例如下边右图柱子被墙壁的红色光反射到而呈现了红色&lt;img src=&#34;https://sdpyy1.github.io/9c960ae7b18945b3a616d60d27f93e6d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
所以最终得到Whitted-Style Ray Tracing是错的
&lt;img src=&#34;https://sdpyy1.github.io/70c9dc705b6747a8ac797c04d6c92e75.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;用蒙特卡洛积分解渲染方程&#34;&gt;用蒙特卡洛积分解渲染方程
&lt;/h2&gt;&lt;p&gt;考虑下图场景
&lt;img src=&#34;https://sdpyy1.github.io/bdd4d8060b5f4fe88ad58f880baf4985.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
解渲染方程，就是求在半球上的积分，所以可以用蒙特卡洛来解
&lt;img src=&#34;https://sdpyy1.github.io/ea6589a50c1346bf965ab494279bb2da.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
被积函数如下，在立体角上积分，整体为半球
&lt;img src=&#34;https://sdpyy1.github.io/606ec211c32d4747a659e980c61c5050.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
选择概率密度函数如下
&lt;img src=&#34;https://sdpyy1.github.io/311ae8c88724440892f91b6238116be3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终要求的就是 ，在不同的立体角上进行采样，最终利用蒙特卡洛公式来估计积分值&lt;img src=&#34;https://sdpyy1.github.io/a37a4598d6734cd593b5030de93b891f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
用算法描述就是要求p点向wo方向的辐射，首先通过概率密度函数随机选择N个样本，对于每个选中的方向wi，从p点连接到wi形成一条光线，如果这个方向打到了光源，就用公式进行累加 （这里的描述只考虑直接光照），如果没打到就是采样结果为0
&lt;img src=&#34;https://sdpyy1.github.io/629ef596da0843eebbf849b79a4aa0c0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
上边已经解决了直接光照问题，下来再解决全局光照，p点在wi方向上并没有看到光源，但是看到了Q点，这时候把P点当作摄像机，Q点当作要处理的点，对Q点进行上边介绍的处理，就能算出Q点对P点的贡献
&lt;img src=&#34;https://sdpyy1.github.io/3c62fa5dca7e47e9a154f6e100afac5b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
全局光照的算法描述如下，注意红色位置
&lt;img src=&#34;https://sdpyy1.github.io/0a490a506c644b6b9180318d5f1e3aa5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
到这里还是有问题
问题一： 点越打越多，比如采样10次，如果10次采样都打到了物体上，那每个采样点又会采样10次
&lt;img src=&#34;https://sdpyy1.github.io/ab7d37b6111140b3b2d5eca3c9700613.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;问题一的解决就是只采样一次&lt;img src=&#34;https://sdpyy1.github.io/37b205c4ff294d4d89535e0c55863a69.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
用N=1来进行积分就叫做Path Tracing，虽然在一个点位只会采样一次，但是整体路径可以进行多次，也就是从像素出发的线不只一条，但接触到物体时只采样一次
&lt;img src=&#34;https://sdpyy1.github.io/787e797befa74737b629822e7fae947c.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/fd3cdb70f7c8475f91b554f0296ee673.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
问题二：没有设置递归出口，可是真实情况光就是会弹射无数次，引入俄罗斯轮盘赌来解决，以一定的概率停止递归&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/397ae528cc02439f831a6b1ab18d2a65.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
根据期望，这种概率性做法最终的期望还是Lo
&lt;img src=&#34;https://sdpyy1.github.io/678ab7367b744b90be189b6d6066bccf.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终最终的算法步骤如下：
&lt;img src=&#34;https://sdpyy1.github.io/d0bed5e1f2f743d896fd66b4661c427b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下面看一下用这个算法生成的图，算法是正确的，但是像素采样点设置很少时（low SPP）效果并不好，在High SPP下速度又太慢
&lt;img src=&#34;https://sdpyy1.github.io/203edee284d74b27bbb7a2c2be464dc9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;sampling-the-light-光源采样&#34;&gt;Sampling the Light 光源采样
&lt;/h2&gt;&lt;p&gt;需要的采样数与光源大小有关系，如果射出的采样光没有打到光源，贡献就是0
&lt;img src=&#34;https://sdpyy1.github.io/ee91f491b6d54b738037b4f8bb621022.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
解决效率问题思想就是在点P不再四面八方采样，而是在光源上进行采样，现在存在的问题蒙特卡洛积分的采样概率密度函数是在P点半球面的采样，需要把公式转换成在光源上采样的形式
&lt;img src=&#34;https://sdpyy1.github.io/c6a2858a8a334778a183207fbaf98afa.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
从数学上说就是dw的积分改为dA积分，需要先研究dw和dA的关系，就是用两者的关系进行换元
&lt;img src=&#34;https://sdpyy1.github.io/1b22925b0d0b4691bc634834c658cba4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终重写渲染方程，就是把在立体角上的采样换成了在光源面积上采样
&lt;img src=&#34;https://sdpyy1.github.io/e4d819262b9b489aabe66a0d969e20d2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/27d10f5d879a441199c1cd8582ccca65.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/eb44d23a5084430eaa717bf58848e2fd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
到这还有一个小小问题
如果光源被挡住呢？
&lt;img src=&#34;https://sdpyy1.github.io/8f17ee61a00e4d4facb344a444ff403d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
点光源路径追踪不好处理，建议改成小面积的面光源&lt;/p&gt;
&lt;h1 id=&#34;最后来感受一下path-tracing的强大&#34;&gt;最后来感受一下Path Tracing的强大
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/0e44c9ef6ebe4770914347ddd318ee85.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如何渲染一张图：要么光栅化要么光线追踪&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES101 现代计算机图形学入门 Geometry</title>
        <link>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-geometry/</link>
        <pubDate>Sun, 19 Oct 2025 12:37:17 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-geometry/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/3b0a2bb693ef4b20a6ca0414d87ae566.png" alt="Featured image of post GAMES101 现代计算机图形学入门 Geometry" /&gt;&lt;h1 id=&#34;implicit-and-explicit&#34;&gt;Implicit and explicit
&lt;/h1&gt;&lt;h2 id=&#34;implicit&#34;&gt;Implicit
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;是并不会告诉任何点的信息，只会告诉该曲面上所有点满足的关系&lt;/strong&gt;
&lt;/p&gt;
$$
f(x,y,z)=0
$$&lt;ul&gt;
&lt;li&gt;判断所给出的隐式方程描述的是一个怎样的形状十分困难&lt;/li&gt;
&lt;li&gt;可以十分容易的判断出一点与曲面的关系&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;example-for-implicit&#34;&gt;example for Implicit
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;代数方法描述
&lt;img src=&#34;https://sdpyy1.github.io/3b0a2bb693ef4b20a6ca0414d87ae566.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;Constructive Solid Geometry（CSG 通过基本几何的运算定义新的几何）&lt;img src=&#34;https://sdpyy1.github.io/915046f3adaf4541acd90f58d2e10e72.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;Signed Distance Functions（空间任何点到物体表面的最短距离来描述，用正负表示在物体外还是内）
&lt;img src=&#34;https://sdpyy1.github.io/fed751784b27497d83ac9cf0c4effeb9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
在2D情况，看下图就很好理解了&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;图片来自https://blog.csdn.net/CODE_RabbitV/article/details/140288664&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/1b337361748244289c895e5ffcc7e0eb.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
当两个距离函数进行融合后后，找到所有值为0的位置，就是融合后的物体表面&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分形
&lt;img src=&#34;https://sdpyy1.github.io/f3f9e209b7f14b4a928a81d254e4e45b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;explicit&#34;&gt;explicit
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;所有曲面的点被直接给出，或者可以通过映射关系直接得到&lt;/strong&gt;
例如：
&lt;/p&gt;
$$
f(u,v) = ((2+cosu)cosv,(2+cosu)sinv,sinu)
$$&lt;p&gt;
把uv都遍历一遍就可以拿到所有的(x,y,z)&lt;/p&gt;
&lt;p&gt;因根据需要选择一种表达方式&lt;/p&gt;
&lt;h2 id=&#34;exampl-for-explicit&#34;&gt;Exampl For Explicit
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Point Cloud（点云）全是点组成的模型
&lt;img src=&#34;https://sdpyy1.github.io/94b7d310b92b4e40913f3077ea4bc24b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;Polygon Mesh（多边形面）
&lt;img src=&#34;https://sdpyy1.github.io/e57ef4ac7f744642a46910ebb8565b9a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
计算机中用三角形面表示的文件  .obj文件
&lt;img src=&#34;https://sdpyy1.github.io/21899e3590134bc6bdf71158675cec07.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/4137942384e84a358dc39d80de8d06a7.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;curves-曲线&#34;&gt;Curves 曲线
&lt;/h1&gt;&lt;h2 id=&#34;贝塞尔曲线bézier-curves&#34;&gt;贝塞尔曲线（Bézier Curves）
&lt;/h2&gt;&lt;p&gt;给定一系列控制点，画出曲线，下图是实际例子&lt;img src=&#34;https://sdpyy1.github.io/234520fa01e7457881cec852c42a5e3f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
de Casteljau实现
考虑3个控制点（quadratic Bezier）
&lt;img src=&#34;https://sdpyy1.github.io/aafe3981bd3a467b9357ff241a7797cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
给定一个比例t，把两个线段都根据t截开&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/468fe6effeaa46669390545ce276a9cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
得到的两个点连起来再执行相同操作&lt;img src=&#34;https://sdpyy1.github.io/f393f2c7ef3446bf8a3fe6d05b21a921.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最后只有一个点了，就确定了曲线一个点的位置
&lt;img src=&#34;https://sdpyy1.github.io/2fc428c252f34015b1347933ea9f08de.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
把所有的t都找一遍，就画出了曲线，见下图，想象一下绿色和蓝色的线跟着t的变化一直变化，就会画出蓝色的线
&lt;img src=&#34;https://sdpyy1.github.io/ff2990a596f0475db8ed0914572b83f2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
可以用表达式表达出每个点，用t去截线段，其实就是做一个插值计算
&lt;img src=&#34;https://sdpyy1.github.io/f2d6c928cc9a4084b3da9e78b6d28c5e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终总结，给定n+1个控制点，比例t的情况下，可以得到n阶的贝塞尔曲线
&lt;img src=&#34;https://sdpyy1.github.io/e9179f932eef40e9b943caa95ef7b554.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
贝塞尔曲线的一些性质：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必定经过起始与终止控制点&lt;/li&gt;
&lt;li&gt;必定经与起始与终止线段相切&lt;/li&gt;
&lt;li&gt;具有仿射变换性质，可以通过移动控制点移动整条曲线&lt;/li&gt;
&lt;li&gt;凸包性质，曲线一定不会超出所有控制点构成的多边形范围
什么是凸包（convex hull）？想象墙上有许多图钉，我们需要用一根橡皮筋把这些图钉围住，于是我们开始会让橡皮筋尽可能的撑大，然后松手放开橡皮筋，它所形成的多边形即为凸包，如图中蓝色曲线所示&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/a3e4cb077a784263869be2e1dc4d9100.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
贝塞尔曲线在控制点过多时，不好控制，如下图所示
&lt;img src=&#34;https://sdpyy1.github.io/2b86fc74c1c347708ed070ecc1a64be5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
于是就有了Piecewise Bézier Curves，取一部分点做，然后结合起来，如下图
&lt;img src=&#34;https://sdpyy1.github.io/96f0bf8b2087482dbee33323963f68cc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/a9815cef4bb04294aff41b81bc115ead.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
贝塞尔曲线的连续性
C0连续，就是控制点重合
&lt;img src=&#34;https://sdpyy1.github.io/4879f6270b3c41d884707108e63c4e48.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
C1连续就是达到了曲面光滑，实现方法看下图红线
&lt;img src=&#34;https://sdpyy1.github.io/77526950457d450a802ba7a1e2ba4663.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;surfaces-曲面&#34;&gt;Surfaces 曲面
&lt;/h1&gt;&lt;h2 id=&#34;贝塞尔曲面&#34;&gt;贝塞尔曲面
&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/368fc069555e4e199be6927666bc7f8a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
通过贝塞尔曲线获得贝塞尔曲面的方法：
如下图已经画出了四条贝塞尔曲线，在t取某一值下生成的4个点，&lt;strong&gt;把这4个点作为控制点再画一条曲线&lt;/strong&gt;，这样t变化时，这条线就绘制成了一个面
&lt;img src=&#34;https://sdpyy1.github.io/255480476961421ab0879d50907a6d78.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;mesh-operation&#34;&gt;Mesh Operation
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/d528394fa4364aafa05f82a8c729deb1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;mesh-subdivision-曲面细分&#34;&gt;Mesh subdivision 曲面细分
&lt;/h2&gt;&lt;p&gt;引入更多的三角形，
&lt;img src=&#34;https://sdpyy1.github.io/394499c9b19e4ebaa468f216e4682a7b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;loop-subdivision&#34;&gt;Loop Subdivision
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;增多三角形数量，如下如
&lt;img src=&#34;https://sdpyy1.github.io/c401784eedfb4804b0e2cc41b1d88b43.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;调整细分后新的三角形顶点处理，更新一条边的中心点，如下图的白点，对这个点的处理，通过上下左右四个顶点加权平均（这个权重是具体的算法，这里不做解释）
&lt;img src=&#34;https://sdpyy1.github.io/78b60ed6955642369eb2b5034aa67ad2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;旧的顶点呢，如下图白点就是一个旧顶点，更新算法包括自己本来的位置，以及周围顶点进行加权平均
&lt;img src=&#34;https://sdpyy1.github.io/88179a67c70b45b394f0108254151717.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;catmull-clark-subdivision&#34;&gt;Catmull-Clark Subdivision
&lt;/h3&gt;&lt;p&gt;一般的模型不一定全是三角形，就没法用loop subdivision，Catmull-Clark可用于一般情况，如下图有正方形也有三角形
&lt;img src=&#34;https://sdpyy1.github.io/d2a36872c41244c5946af77bd3427c06.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
先定义两个表达，非四边形面和奇异点（度不等于 4的点）
&lt;img src=&#34;https://sdpyy1.github.io/f8b7071330a44f75ba1733991aac29ab.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如上图框所示，每条边和每个面都取钟中点，然后把这些点连起来，如下图所示
&lt;img src=&#34;https://sdpyy1.github.io/02bb2646bc284ffb8d04ffd8831f6d2b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
连接后，奇异点变多了，非四边形都消失了，此时奇异点是4个，但是如果继续细分，奇异点数目并不会继续增加，如下图还是4个
&lt;img src=&#34;https://sdpyy1.github.io/3c2c267ffd014712ae9c1a86e7983b1e.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
与loop一样，调整完后需要对顶点位置进行处理，处理方法如下
&lt;img src=&#34;https://sdpyy1.github.io/0a5d2d7623f34e598f546e0caac7f418.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
下图为两种方法对比
&lt;img src=&#34;https://sdpyy1.github.io/8774493823fb49108527661615ff1f5a.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;mesh-simplification-曲面简化&#34;&gt;Mesh Simplification 曲面简化
&lt;/h2&gt;&lt;p&gt;当模型距离很远时，模型的细节就没必要表现出来&lt;/p&gt;
&lt;h3 id=&#34;collapsing-an-edge-边坍缩&#34;&gt;Collapsing An Edge 边坍缩
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/f7c0115a0d124a3b98dae744161a1177.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
那哪些边可以进行坍缩，如果度量呢？下图进行了度量，首先5个顶点进行平均效果并不好， 用2次误差方法来实现：即坍缩之后蓝色新顶点所在的位置与原来各个平面的垂直距离之和。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;为模型每条边赋值，其值为坍缩这条边之后，代替两个老顶点的新顶点所能得到的最小二次误差度量&lt;/li&gt;
&lt;li&gt;选取权值最小的边做坍缩，新顶点位置为原来计算得出使得二次误差最小的位置&lt;/li&gt;
&lt;li&gt;坍缩完之后，与之相连其他的边的位置会改动，更新这些边的权值&lt;/li&gt;
&lt;li&gt;重复上述步骤，直到到达终止条件
&lt;img src=&#34;https://sdpyy1.github.io/ad8ca52871de45ff98abfb35d33ee169.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
一些坍缩的实例
&lt;img src=&#34;https://sdpyy1.github.io/69166ee4014448b3b2543619933435f9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
几何部分就这些东西了～&lt;/li&gt;
&lt;/ol&gt;
</description>
        </item>
        <item>
        <title>GAMES101 现代计算机图形学入门 Shading</title>
        <link>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-shading/</link>
        <pubDate>Sun, 19 Oct 2025 00:57:01 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-shading/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/8794c0d329d24aca97c68998d2945da5.png" alt="Featured image of post GAMES101 现代计算机图形学入门 Shading" /&gt;&lt;h1 id=&#34;什么是shading&#34;&gt;什么是shading？
&lt;/h1&gt;&lt;p&gt;shading 负责计算物体表面每个采样点的颜色，具体考虑光照、材质属性、观察角度等因素，生成具有真实感的视觉效果（如漫反射、镜面高光、环境光等）‌，之前的光栅化将是几何图元（如三角形）转换为屏幕上的像素，确定哪些像素被图元覆盖，是几何层面的处理&lt;/p&gt;
&lt;h1 id=&#34;blinn-phong-reflectance-modela-simple-shading-model&#34;&gt;Blinn-Phong Reflectance Model(A Simple Shading Model)
&lt;/h1&gt;&lt;p&gt;Blinn-Phong 反射模型（Blinn-Phong Reflectance Model）是一种用于计算机图形学中模拟物体表面光照效果的着色模型.该模型主要由**环境光(Ambient)、漫反射(Diffuse)和高光反射(Specular)**三部分组成。&lt;img src=&#34;https://sdpyy1.github.io/92975763af4c45deacbe6332c3f2d16b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
定义一些基本向量，默认为单位向量：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Viewer Direction&lt;/code&gt; ：观察方向,用&lt;code&gt;v&lt;/code&gt;	表示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Surface normal&lt;/code&gt;：法线方向，使用&lt;code&gt;n&lt;/code&gt;表示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Light direction&lt;/code&gt;：光线方向，用&lt;code&gt;l&lt;/code&gt;表示
&lt;img src=&#34;https://sdpyy1.github.io/7b8e79b519294907b6730677c6933da2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;diffuse-reflection漫反射&#34;&gt;Diffuse Reflection（漫反射）
&lt;/h2&gt;&lt;p&gt;是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时，表面会把光线向着四面八方反射，所以入射线虽然互相平行，由于各点的法线方向不一致，造成反射光线向不同的方向无规则地反射，这种反射称之为“漫反射”或“漫射”。正是因为反射是完全随机的，因此可以认为漫反射光在任何反射方向上的分布都是一样的。
&lt;img src=&#34;https://sdpyy1.github.io/536d9f34abe444b6864d43a98691793b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
漫反射强度影响因素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;入射光线与法线的夹角
&lt;code&gt;lambert&#39;s cosine law&lt;/code&gt;: 当夹角为θ时，能量要乘&lt;code&gt;cosθ = l.dot(v)&lt;/code&gt;(单位向量)
&lt;img src=&#34;https://sdpyy1.github.io/ab18d5ca508a49a0b207f2a4755b3942.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/li&gt;
&lt;li&gt;入射光线自身的强度
入射光自身的强度与距离有关 &lt;img src=&#34;https://sdpyy1.github.io/e0e28606ee0f45e7b31cbbdea8a1ee12.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
结合两个影响因素，得到漫反射能量公式
$$L_d = k_d (I / r^2) \max(0, \mathbf{n} \cdot \mathbf{l})$$&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/59bc96822aeb4792901933319aa199b0.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
其中$k_d$表示反射系数指光（入射光）投向物体时，其表面反射光的强度与入射光的强度之比值，受入射光的投射角度、强度、波长、物体表面材料的性质以及反射光的测量角度等因素影响。一般来讲，在颜色系列中，黑色的反射系数较小，为0.03，白色的反射系数较大，为0.8
&lt;img src=&#34;https://sdpyy1.github.io/72ad4013378a455397a40b15611554e8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最后，漫反射与观察方向&lt;code&gt;v&lt;/code&gt;没有关系，漫反射能量会均匀反射到各个方向上去。&lt;/p&gt;
&lt;h2 id=&#34;specular-highlights高光镜面反射&#34;&gt;Specular highlights(高光/镜面反射)
&lt;/h2&gt;&lt;p&gt;若物体表面很光滑，光线会被反射，如图所示&lt;code&gt;R&lt;/code&gt;为镜面反射方向，观察方向&lt;code&gt;v&lt;/code&gt;与反射方向&lt;code&gt;R&lt;/code&gt;接近时，能看见反射光，反射光强度与&lt;code&gt;R&lt;/code&gt;和&lt;code&gt;v&lt;/code&gt;的夹角有关
&lt;img src=&#34;https://sdpyy1.github.io/7a8efb9ae9544cc6b84f38e78a19849f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;code&gt;R&lt;/code&gt;和&lt;code&gt;v&lt;/code&gt;的夹角 &lt;strong&gt;等价于&lt;/strong&gt;&lt;code&gt;l&lt;/code&gt;和&lt;code&gt;v&lt;/code&gt;的half vector与法线方向&lt;code&gt;n&lt;/code&gt;之间的夹角，通过向量相加的平行四边形法则获取到half vector，再单位化
&lt;img src=&#34;https://sdpyy1.github.io/9d8198ee92e340ff8c5b953692887771.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;img src=&#34;https://sdpyy1.github.io/5b4efb68c51d4f6792da743a7c374394.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最终得到镜面反射能量公式
&lt;/p&gt;
$$
\begin{align*}
L_s &amp;= k_s (I / r^2) \max(0, \cos\alpha)^p \\
&amp;= k_s (I / r^2) \max(0, \mathbf{n} \cdot \mathbf{h})^p
\end{align*}
$$&lt;p&gt;
其中$k_s$为镜面反射系数，通常取白色的系数&lt;/p&gt;
&lt;p&gt;因为镜面反射对角度很敏感，所以设置参数p来控制夹角的影响
&lt;img src=&#34;https://sdpyy1.github.io/382d5aa80fa342ddbb2fb7f7e2a08cf2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/cd3c5483732b4f65bb2a6199c51b69c3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;ambient-term环境光&#34;&gt;Ambient Term（环境光）
&lt;/h2&gt;&lt;p&gt;环境光不依赖任何项，任何方向射入，任何方向观察都不影响环境光的强度（该模型的简化，精确计算需要用到全局光照技术）
&lt;img src=&#34;https://sdpyy1.github.io/beb39f55be6248edad0bf167089248dd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
最后，展示结合效果
&lt;img src=&#34;https://sdpyy1.github.io/6218a369afe94437bb81dfb0420e1b39.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;shading-frequencies着色频率&#34;&gt;Shading Frequencies（着色频率）
&lt;/h1&gt;&lt;p&gt;3个几何形状完全相同的球，为什么着色结果各不相同？计算作色的频率不同，球1一个平面取一个法线，做一次着色，球3每个像素都求法线后都做一次着色
&lt;img src=&#34;https://sdpyy1.github.io/67eb9331c2f04237ba3ae48c01e76f40.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;shade each triangle(flat shading)：每个三角形求一个法线（边向量叉积），对整个三角形进行相同的shading，当三角形面很多，效果也不一定差，反而计算量少&lt;/li&gt;
&lt;li&gt;shade each vertex（Gouraud shading）：三角形的三个顶点都求法线，三个顶点进行shading，三角形内部用插值填充&lt;/li&gt;
&lt;li&gt;shade each pixel(Phong shading)：每个像素都求法线，每个像素都进行shading&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如何求顶点的法线？利用顶点关联的面的法线进行加权平均&lt;img src=&#34;https://sdpyy1.github.io/8f2e024b74cd409ba1b6d43604a60d15.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;graphicsreal-time-renderingpipeline&#34;&gt;Graphics（Real time Rendering）Pipeline
&lt;/h1&gt;&lt;p&gt;&lt;code&gt;Pipeline&lt;/code&gt;：从场景到图的流程&lt;img src=&#34;https://sdpyy1.github.io/2fb691f7ae274fa88e39ed8201439dfb.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
⚠️：Shading根据Shading Frequencies（着色频率）的不同发生在不同阶段，如果是每个顶点进行着色，就发生在顶点处理阶段，如果是每个像素shading么就在光栅化之后，下面图也解释的shader编程中的vertexShader和fragmentShader
&lt;img src=&#34;https://sdpyy1.github.io/aba7ce94e1894472ae4b1f697106576d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
GPU模型
&lt;img src=&#34;https://sdpyy1.github.io/e5b95abc7b0b48eb9a8cccce0aab5c97.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;texture-mapping&#34;&gt;Texture Mapping
&lt;/h1&gt;&lt;p&gt;考虑球和地板，不同的位置有不同的颜色，每个位置都有自己的漫反射系数，引入纹理映射对每一个点定义不同的属性
&lt;img src=&#34;https://sdpyy1.github.io/8794c0d329d24aca97c68998d2945da5.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
模型上任意一个三角形的顶点，都能在纹理图找到对应的顶点。 至于如何一一对应，是美工的任务。
纹理图通过UV坐标来进行定位（UV取值范围都是0-1之间），这样每个三角形的顶点都对应一个UV坐标
&lt;img src=&#34;https://sdpyy1.github.io/e1b8430b6af44f74bea4eeefb3af4d96.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
如果我已经知道每个三角形顶点对于的UV坐标，那如何知道三角形内部每个点的UV坐标呢？ （插值解决）&lt;/p&gt;
&lt;h1 id=&#34;如何插值&#34;&gt;如何插值？
&lt;/h1&gt;&lt;p&gt;为什么要用插值？因为我们希望处理完顶点后，三角形内部进行平滑的过度
首先引入&lt;strong&gt;重心坐标 Barycentric Coordinates&lt;/strong&gt; 重心坐标可以表示为顶点的线性组合（如下图所示），三角形内任意一点的坐标(x,y)，找到满足$\alpha+\beta+\gamma=1$ 和 (x, y) = $\alpha A + \beta B + \gamma C$的（$\alpha$,$\beta$,$\gamma$）, 就可以用（$\alpha$,$\beta$,$\gamma$）表示该点的重心坐标
&lt;img src=&#34;https://sdpyy1.github.io/517b089099a44139ae749739315b7713.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
重心坐标（$\alpha$,$\beta$,$\gamma$）可以通过面积比求出，如下图
&lt;img src=&#34;https://sdpyy1.github.io/9f827fde268a47c4bc84ea6b54573ca1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
由此可得三角形的重心的重心坐标为(1/3,1/3,1/3), 除此之外，也有直接的公式求出任意一点的重心坐标，如下图&lt;img src=&#34;https://sdpyy1.github.io/32f3c44f1aaf4457ab19517910e03bac.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
其实重心坐标显示出的内容就是该点对于三角形三个顶点的权重应该是多少。（$\alpha$,$\beta$,$\gamma$）就分别是A、B、C顶点的权重。
⚠️：投影后重心坐标会变化&lt;/p&gt;
&lt;h1 id=&#34;texture-magnification纹理图太小&#34;&gt;Texture Magnification（纹理图太小）
&lt;/h1&gt;&lt;p&gt;A pixel on a texture - a texel(纹理元素、纹素)
场景：纹理图片2&lt;em&gt;2，但需要把它渲染到4&lt;/em&gt;4的屏幕，那在渲染中间像素时，他的UV坐标无法对应texel，所以需要处理，下图为三种处理方式
&lt;img src=&#34;https://sdpyy1.github.io/e9aa4e162f6442d39accc2a3b712be12.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;nearest&#34;&gt;Nearest
&lt;/h2&gt;&lt;p&gt;落在哪个texel附近，就选择哪个texel
&lt;img src=&#34;https://sdpyy1.github.io/09ba75ab4b774ad68d34aa2ca946fac8.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;bilinear-interpolation&#34;&gt;Bilinear Interpolation
&lt;/h2&gt;&lt;p&gt;综合考虑周围4个texel的值，水平方向得到两个插值，再垂直方向再做一次插值，所以叫双线性插值，插值函数就是 &lt;/p&gt;
$$lerp(x,v_0,v_1) = v_0 + x(v_1 - v_0)$$&lt;p&gt;
&lt;img src=&#34;https://sdpyy1.github.io/717b972334db44758f07c7e0fe803292.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;bicubic&#34;&gt;Bicubic
&lt;/h3&gt;&lt;p&gt;更复杂的插值，取周围16的texel&lt;/p&gt;
&lt;h2 id=&#34;texture-magnification纹理图太大&#34;&gt;Texture Magnification（纹理图太大）
&lt;/h2&gt;&lt;p&gt;例如纹理图被重复使用用来帖地板
&lt;img src=&#34;https://sdpyy1.github.io/b07a70e13acf492ea9426eb388d797bc.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;如果只进行简单操作，得到结果为&lt;img src=&#34;https://sdpyy1.github.io/d008434c555a4cfab877a258e29584be.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
问题的原因：在远处一个Pixel就覆盖了多个texel，下图大框是Pixel，在远处，一个像素就覆盖了多个纹理，很大范围只采样一次，就会导致信息丢失，导致走样。最简单解决思路就是Supersampling，采样点增多，获得的信息就多，但计算量太大
&lt;img src=&#34;https://sdpyy1.github.io/1b1ea995cd5f4eefa26ca339f6c4b7f4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;mipmap&#34;&gt;Mipmap
&lt;/h3&gt;&lt;p&gt;为一张纹理图生成多个低分辨率纹理图，每次分辨率减半，将4个相邻像素点求均值合为一个像素点。从存储容量上看，根据等比数列求和，存储只增加了$1/3$
&lt;img src=&#34;https://sdpyy1.github.io/bf9cf2d5e45e47e6960504a48231cbf4.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
那在映射时应该选择哪一Level 作为纹理呢？利用屏幕相邻的像素点的距离估计纹理的大小，如图红色像素距离上和右两个像素的距离反应到纹理图上的距离变大的程度来选择，取最大&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/15e4c9a36001452196577fd5d56451b1.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
但通过$D = log_2L$计算的是一个连续值，并不一定直接表面使用第几层，如何处理？
使用三线性插值，例如D=1.8，就在第1层和第2层分别做一次双线性插值，最后根据D的大小，对两次插值结果再插值一次&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/85612818317b45fb9f6573afd11e35af.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
使用该技术得到结果，如下图所示，远处仍然比较模糊&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/c983fcf64bcb45629909889ac856342f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;各向异性过滤&#34;&gt;各向异性过滤
&lt;/h3&gt;&lt;p&gt;Mipmap所规定的区域查询
必须是正方形，而纹理映射中可不仅仅只有正方形，如下图所示，某些像素对应的纹理图位置并不是正方形，针对这种情况，有时候需要水平方向的高Level，有时候需要竖直方向上的高Level，因此也启发了各向异性过滤，生成各种方向上压缩的材质，最终存储容量收敛到原来的3倍
&lt;img src=&#34;https://sdpyy1.github.io/a159d9343186494fba48b180ddc7b9a6.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
看下图一个像素点在纹理上对应的并不是一个正方形区域，使用Mipmap就不合适了
&lt;img src=&#34;https://sdpyy1.github.io/a2694a17e4324c34b7b7844a9fcd9959.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;纹理的应用&#34;&gt;纹理的应用
&lt;/h2&gt;&lt;h3 id=&#34;environment-map&#34;&gt;Environment Map
&lt;/h3&gt;&lt;p&gt;纹理不一定非得一副图片，可以用纹理描述环境光效果
&lt;img src=&#34;https://sdpyy1.github.io/8c14a0abbde44a91baaef5af594efec3.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/215575e087ee43d39e809f0a70a6f259.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;bump-mapping-凹凸贴图&#34;&gt;Bump Mapping 凹凸贴图
&lt;/h3&gt;&lt;p&gt;欺骗人眼，看着像是用模型做出来的凹凸效果，其实只是材质的原因。在贴图上定义每个点的高度进而改变法向量，这就回影响shading，因为法线参与了color生成过程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/f152f87a28e243db85b3c98c4a11d9ec.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;displacement-mapping-位移贴图&#34;&gt;Displacement Mapping 位移贴图
&lt;/h3&gt;&lt;p&gt;真的去移动了顶点，Bump会露馅，因为最外圈显然是一个完整的球。Bump Maps是逻辑上的高度改变，它实则并没有改变内部的模型，是假的改变；而Displacement Map则是物理上的高度改变。它真正改变了模型。二者的区别就在此处，可以通过物体阴影的边缘发现这点
&lt;img src=&#34;https://sdpyy1.github.io/d3136e29f86748cfb3269debba2f2f33.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;3维纹理&#34;&gt;3维纹理
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/a0540dee9994499282928a5f85dd36fa.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>GAMES101 现代计算机图形学入门 Transformation &amp; Rasterization</title>
        <link>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-transformation-rasterization/</link>
        <pubDate>Sun, 19 Oct 2025 00:47:26 +0800</pubDate>
        
        <guid>https://sdpyy1.github.io/p/games101-%E7%8E%B0%E4%BB%A3%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6%E5%85%A5%E9%97%A8-transformation-rasterization/</guid>
        <description>&lt;img src="https://sdpyy1.github.io/3d23bd7f8fdc4adfa5ef11d68f2d98fd.png" alt="Featured image of post GAMES101 现代计算机图形学入门 Transformation &amp; Rasterization" /&gt;&lt;h1 id=&#34;线性代数复习&#34;&gt;线性代数复习
&lt;/h1&gt;&lt;p&gt;&lt;img src=&#34;https://sdpyy1.github.io/image-20251019121720334.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;image-20251019121720334&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;向量&#34;&gt;向量
&lt;/h2&gt;&lt;p&gt;首先介绍一下向量，有长度有方向，起始位置不固定
&lt;img src=&#34;https://sdpyy1.github.io/0cf789e58553444d93cff71c08defe69.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
向量归一化，就是获得向量方向上的单位向量，后续课程各种操作都是在单位向量上进行
&lt;img src=&#34;https://sdpyy1.github.io/3a1dbb53ecf24ac5a3a6924380cd0fce.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
向量加法
&lt;img src=&#34;https://sdpyy1.github.io/16979f8f8e314502a50499e210cb1241.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;点乘和叉乘&#34;&gt;点乘和叉乘
&lt;/h2&gt;&lt;p&gt;向量点乘，在后续课程中用来计算cosθ，如果两个单位向量点乘结果就是cosθ
&lt;img src=&#34;https://sdpyy1.github.io/ba36e5f8d6774f52815e0b203a9eb3e2.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在坐标系中的使用，就是对应坐标相乘再相加
&lt;img src=&#34;https://sdpyy1.github.io/a2583128ce874f96b266b4b1920dbb3d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
点乘可以用来投影
&lt;img src=&#34;https://sdpyy1.github.io/3eb2f75a0424479d842655c9586ae10b.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
向量叉乘
&lt;img src=&#34;https://sdpyy1.github.io/8de7a030c8074905a17feb911c59825d.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在坐标系中的使用，有公式
&lt;img src=&#34;https://sdpyy1.github.io/3b45ea1aaf4f48c6b375f8a867929baf.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
在图形中的使用是可以判断一个点是否在三角形内部，在光栅化时用来判断一个像素是否需要被渲染
&lt;img src=&#34;https://sdpyy1.github.io/416df626f00642e2917dc882af81999f.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;矩阵&#34;&gt;矩阵
&lt;/h2&gt;&lt;p&gt;矩阵就不说了，比较容易&lt;/p&gt;
&lt;h2 id=&#34;transformation变换&#34;&gt;Transformation（变换）
&lt;/h2&gt;&lt;h3 id=&#34;model-transformation&#34;&gt;Model Transformation
&lt;/h3&gt;&lt;p&gt;模型变换可以做缩放、旋转、平移
通过矩阵计算实现的缩放
&lt;img src=&#34;https://sdpyy1.github.io/b6b20ba2d3684b0e8f80e27ea37c2de9.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;
旋转
&lt;img src=&#34;https://sdpyy1.github.io/3d23bd7f8fdc4adfa5ef11d68f2d98fd.png&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;在这里插入图片描述&#34;
	
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;笔记保存&#34;&gt;笔记保存
&lt;/h1&gt;&lt;p&gt;&lt;code&gt;games101课程光栅化之前的笔记，发现还是直接写博客方便，后续用CSDN完成笔记，前边部分先贴在这里，以后再补充&lt;/code&gt;
这是之前遗留的笔记，放在这里保存
&lt;img src=&#34;https://sdpyy1.github.io/8750d51737f0475babe2875df6ffcd6d.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/84752f1b120f4e058fda182c7b03b50f.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/31c09e5916fa49e98190eb1d3f547191.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/74710fbd6e7146d298a020c0eba472e1.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/096ef71415ff47c3b1ddcc6f0220ec08.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/d343e7ad83204472a6e61f581d4e1c0f.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/20402b23e5de445e82fadb8c816c75e9.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/9a4c3613a85a4ba5bca6d1dd701fea48.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/2dd52198035446238adaa55e904b2353.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/cf449ec17b0845d8a68354f761c39d3d.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/8c9f224ae52a4119a0b391d6e5e24787.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/0d0154d6b0be4c9bb4bfaf91bc6d74ab.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;
&lt;img src=&#34;https://sdpyy1.github.io/1ab84e1c2ff0403b855e32c56748baea.jpeg&#34;
	
	
	
	loading=&#34;lazy&#34;
	
		alt=&#34;请添加图片描述&#34;
	
	
&gt;&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
