叶飘飘 發表於 2025-5-5 00:17:00

《Fundamentals of Computer Graphics》第二章 杂项数学

<h1 id="开篇">开篇</h1>
<p>  第二章“<strong>Miscellaneous Math</strong>”讲了许多高中、大学所学的一些基础数学知识:集合、映射、区间、三角函数、积分、密度函数等等。这里有一些要注意的地方。</p>
<h1 id="立体角与球面三角学solid-angles-and-spherical-trigonometry">立体角与球面三角学(Solid Angles and Spherical Trigonometry)</h1>
<p>  传统的三角学涉及平面上的三角形,三角形也能定义在非平面表面。这种三角学在许多领域例如天文学出现,研究这些三角形的领域叫<strong>球面三角学</strong>。它虽然不会经常在图形学中被使用,但是出现的时候一般都非常重要。<br>
  在<strong>球面三角学</strong>中对计算机图形学非常重要的一个概念叫<strong>立体角</strong>(<strong>Solid Angle</strong>),<strong>立体角</strong>让我们可以量化例如在天上的飞机占据视野的多少。整个测量过程其实很简单,把能“看到”飞机的所有方向投影到单位球,接着测量面积。测量出的面积大小就是<strong>立体角</strong>的大小,不过立体角的单位是<strong>Sr</strong>(<strong>Steradian</strong>),在单位球上的总立体角就是<span class="math inline">\(4\pi\,\mathrm{sr}\)</span>。<br>
  此外,我还稍微查了下资料。wiki百科给出了<strong>立体角</strong>积分公式,这里可以稍微了解一下,能让你对<strong>立体角</strong>有更好的理解。首先通过<span class="math inline">\(\Delta \theta\)</span>和<span class="math inline">\(\Delta \varphi\)</span>分割半径为<span class="math inline">\(r\)</span>的球面,如图所示。<br>
<img src="https://img2023.cnblogs.com/blog/2774734/202505/2774734-20250504200322064-1335335338.png"><br>
作者:Travailen</p>
<p>当<span class="math inline">\(\Delta \theta\)</span>和<span class="math inline">\(\Delta \varphi\)</span>在极小情况下,分割出来的每一极小份的曲面可以被当作一个矩形。因此极小份的面积为</p>
<p></p><div class="math display">\[dA = (r\,\sin\theta\,d\varphi)(rd\theta) = r^2(\sin\theta\,d\theta\,d\varphi)
\]</div><p></p><p>当<span class="math inline">\(r=1\)</span>时,极小<strong>立体角</strong>的大小就是极小面积的大小</p>
<p></p><div class="math display">\[d\Omega = \sin\theta\,d\theta\,d\varphi
\]</div><p></p><p>所以<strong>立体角</strong>的积分公式为</p>
<p></p><div class="math display">\[\Omega = \int \int_{S} \sin\theta\,d\theta\,d\varphi
\]</div><p></p><h1 id="规范正交基的构造">规范正交基的构造</h1>
<p>  有时候需要为给定的向量求一个规范正交基,比如在写全屏光线追踪着色器时,要为每个像素发出光线,这个时就需要一个规范正交基。对于给定的向量<span class="math inline">\(\mathbf{a}\)</span>,我们需要规范正交基<span class="math inline">\(\mathbf{u}\)</span>、<span class="math inline">\(\mathbf{v}\)</span>、<span class="math inline">\(\mathbf{w}\)</span>。其中<span class="math inline">\(\mathbf{w}\)</span>与<span class="math inline">\(\mathbf{a}\)</span>方向一致,<span class="math inline">\(\mathbf{u}\)</span>、<span class="math inline">\(\mathbf{v}\)</span>一般不关心是什么,书中给了以下两种方法。</p>
<h2 id="从一个辅助向量构造constructing-a-basis-from-a-single-vector">从一个辅助向量构造(Constructing a Basis from a Single Vector)</h2>
<p>  首先挑选一个不与<span class="math inline">\(\mathbf{w}\)</span>共线的一个辅助向量<span class="math inline">\(\mathbf{t}\)</span>,要得注意的是,辅助向量<span class="math inline">\(\mathbf{t}\)</span>不可与<span class="math inline">\(\mathbf{w}\)</span>过于接近,<span class="math inline">\(\mathbf{u}\)</span>、<span class="math inline">\(\mathbf{v}\)</span>、<span class="math inline">\(\mathbf{w}\)</span>可以按如下公式进行计算。</p>
<p></p><div class="math display">\[\mathbf{w}=\frac{\mathbf{a}}{||\mathbf{a}||}
\]</div><p></p><p></p><div class="math display">\[\mathbf{u}=\frac{\mathbf{t} \times \mathbf{w}}{||\mathbf{t} \times \mathbf{w}||}
\]</div><p></p><p></p><div class="math display">\[\mathbf{v}=\mathbf{w} \times \mathbf{u}
\]</div><p></p><h2 id="从两个辅助向量构造constructing-a-basis-from-two-vectors">从两个辅助向量构造(Constructing a Basis from Two Vectors)</h2>
<p>  要使用这个方法,最好挑选两个垂直的向量<span class="math inline">\(\mathbf{a}\)</span>、<span class="math inline">\(\mathbf{b}\)</span>,要注意的是向量<span class="math inline">\(\mathbf{a}\)</span>、<span class="math inline">\(\mathbf{b}\)</span>不可过于接近,<span class="math inline">\(\mathbf{u}\)</span>、<span class="math inline">\(\mathbf{v}\)</span>、<span class="math inline">\(\mathbf{w}\)</span>可以按如下公式进行计算。</p>
<p></p><div class="math display">\[\mathbf{w}=\frac{\mathbf{a}}{||\mathbf{a}||}
\]</div><p></p><p></p><div class="math display">\[\mathbf{u}=\frac{\mathbf{b} \times \mathbf{w}}{||\mathbf{b} \times \mathbf{w}||}
\]</div><p></p><p></p><div class="math display">\[\mathbf{v}=\mathbf{w} \times \mathbf{u}
\]</div><p></p><h1 id="重心坐标">重心坐标</h1>
<p> 对于三角形<span class="math inline">\(ABC\)</span>上的一点<span class="math inline">\(\mathrm{P}\)</span>,它的重心坐标<span class="math inline">\((\alpha,\beta,\gamma)\)</span>指代的是,三角形<span class="math inline">\(ABC\)</span>每个顶点的位置占点<span class="math inline">\(\mathrm{P}\)</span>位置的比重,即<span class="math inline">\(\vec{P} = \alpha \vec{A}+\beta\vec{B}+\gamma\vec{C}\)</span>,而且有如下关系<span class="math inline">\(\alpha+\beta+\gamma=1\)</span>。重心坐标对于顶点属性的插值来说非常重要,而且在后面的代码实现中需要计算它。书中为了证明恒等于<span class="math inline">\(1\)</span>这个关系,于是先引出了重心坐标的计算,反而导致证明过程非常复杂。我想了想有更简单的几何证明,方法如下。<br>
  在三角形<span class="math inline">\(\mathrm{ABC}\)</span>上做<span class="math inline">\(\mathrm{DE}\)</span>平行于<span class="math inline">\(\mathrm{AC}\)</span>,接着在线段<span class="math inline">\(\mathrm{DE}\)</span>上取一点<span class="math inline">\(\mathrm{P}\)</span>,如下图所示。<br>
<img src="https://img2023.cnblogs.com/blog/2774734/202505/2774734-20250504234913790-1088407119.png"><br>
如图所示,现在可由两个系数<span class="math inline">\(k\)</span>与<span class="math inline">\(t\)</span>确定三角形<span class="math inline">\(\mathrm{ABC}\)</span>上一点</p>
<p></p><div class="math display">\[\vec{P} = \vec{B}+(1-t)(1-k)(\vec{A}-\vec{B})+t(1-k)(\vec{C}-\vec{B})
\]</div><p></p><p>化简可得</p>
<p></p><div class="math display">\[\vec{P}=(1-t-k+tk)\vec{A}+k\vec{B}+(t-tk)\vec{C}=\alpha\vec{A}+\beta\vec{B}+\gamma\vec{C}
\]</div><p></p><p>易得</p>
<p></p><div class="math display">\[\alpha=1-t-k+tk
\]</div><p></p><p></p><div class="math display">\[\beta=k
\]</div><p></p><p></p><div class="math display">\[\gamma=t-tk
\]</div><p></p><p>因此可证得<span class="math inline">\(\alpha+\beta+\gamma=1\)</span>。此外,由几何证明我们能得到显而易见的一点,当<span class="math inline">\(k\)</span>线性变化时,点<span class="math inline">\(\mathrm{P}\)</span>离线段<span class="math inline">\(\mathrm{AC}\)</span>的距离也会线性变化,令点<span class="math inline">\(\mathrm{B}\)</span>和点<span class="math inline">\(\mathrm{P}\)</span>离线段<span class="math inline">\(\mathrm{AC}\)</span>的距离分别为<span class="math inline">\(D_\mathrm{B}\)</span>和<span class="math inline">\(D_\mathrm{P}\)</span>,再次利用相似关系,于是有</p>
<p></p><div class="math display">\[\frac{D_\mathrm{P}}{D_\mathrm{B}-D_\mathrm{P}} = \frac{k}{1-k}
\]</div><p></p><p>整理可得</p>
<p></p><div class="math display">\[k = \frac{D_\mathrm{P}}{D_\mathrm{B}}
\]</div><p></p><p>某点<span class="math inline">\((x,y)\)</span>距离直线<span class="math inline">\(f(x,y)=Ax+By+C=0\)</span>的距离公式为</p>
<p></p><div class="math display">\[D = \frac{|Ax+By+C|}{\sqrt{A^2+B^2}} = \frac{|f(x,y)|}{\sqrt{A^2+B^2}}
\]</div><p></p><p>令直线<span class="math inline">\(\mathrm{AC}\)</span>的函数为<span class="math inline">\(f_\mathrm{AC}\)</span>,那么有</p>
<p></p><div class="math display">\[\beta = k = \frac{|f_\mathrm{AC}(\mathrm{P})|}{|f_\mathrm{AC}(\mathrm{B})|}
\]</div><p></p><p>又因为点<span class="math inline">\(\mathrm{B}\)</span>和点<span class="math inline">\(\mathrm{P}\)</span>在直线<span class="math inline">\(\mathrm{AC}\)</span>同侧,所以可以去掉取绝对值操作,因此最终的求解公式为</p>
<p></p><div class="math display">\[\beta = \frac{f_\mathrm{AC}(\mathrm{P})}{f_\mathrm{AC}(\mathrm{B})}
\]</div><p></p><p>对于其它两个重心坐标来说,我们只需要取其它两个直线函数以及与其它两个边对应的顶点计算即可。</p>
<h1 id="蒙特卡洛积分monte-carlo-integration">蒙特卡洛积分(Monte Carlo Integration)</h1>
<p>  蒙特卡洛积分就是通过随机样本来对函数的某区域进行积分。假如现在要求表面法线方向半球内的入射光线所贡献的辐照度</p>
<p></p><div class="math display">\[H(\mathbf{n})=\int_{\Omega} L_f(\omega_i) \, (\mathbf{n} \cdot \omega_i) \, d\omega_i
\]</div><p></p><p>我们可以通过数量为<span class="math inline">\(\mathrm{N}\)</span>的随机采样点来获得辐亮度的积分值。由于采样点是随机分布的,所以每个采样点的权重是<span class="math inline">\(\frac{2\pi}{\mathrm{N}}\)</span>,由此可写如下代码</p>
<pre><code>const float3 dir = octDecode(coor.x, coor.y);

float3 result = float3(0.0, 0.0, 0.0);


for (uint i = 0; i &lt; SAMPLECOUNT; i++)
{
    const float3 randDir = randomDirection * sign(dot(dir, randomDirection));

    const float3 radiance = envCube.SampleLevel(linearClampSampler, randDir, 0.0).rgb;

    result += radiance * saturate(dot(dir, randDir));
}
      
result = 2.0 * PI / float(SAMPLECOUNT) * result;
</code></pre>
<h2 id="重要性采样importance-sampling">重要性采样(Importance Sampling)</h2>
<p>  当被积函数在积分区域内的值有很大的变化时,我们可以把采样点集中分布在一些区域。在这个时候每个采样点都有特定的非均匀权重。如果我们知道概率密度函数<span class="math inline">\(\mathrm{PDF}\)</span>就能求出积分值,代码如下</p>
<pre><code>const float3 dir = float3(0.0, 0.0, 1.0);

float3 result = float3(0.0, 0.0, 0.0);


for (uint i = 0; i &lt; SAMPLECOUNT; i++)
{
    const float3 randDir = randomDirection;

    const float3 radiance = envCube.SampleLevel(linearClampSampler, randDir, 0.0).rgb;

    result += radiance * saturate(dot(dir, randDir)) / directionImportance;
}
      
result = 1.0 / float(SAMPLECOUNT) * result;
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:TiredInkRaven,转载请注明原文链接:https://www.cnblogs.com/TiredInkRaven/p/18859403</p><br><br>
来源:https://www.cnblogs.com/TiredInkRaven/p/18859403
頁: [1]
查看完整版本: 《Fundamentals of Computer Graphics》第二章 杂项数学