计算机图形学笔记2-Viewing 观测

观测主要解决的问题是如何把物体的三维“模型”变成我们在屏幕所看到的二维“图片”,我们在计算机看到实体模型可以分成这样几步:

物体空间如何经过一系列变换转化到屏幕上

  • 相机变换(camera transformation)或眼变换(eye transformation):想象把相机放在任意一个位置来观测物体,我们首先就要把物体的世界坐标转换为相机坐标,这一步称为相机变换或眼变换。
  • 投影变换(projection transformation):相机把物体拍成照片本质是从三维的相机坐标转化为二维的平面坐标,这一步称为投影变换。投影可以分为正射投影和透视投影。
  • 视口变换(viewport transformation)或窗口变换(windowing transformation):相机拍成的图片最后是要显示在屏幕上,我们需要把二维的图片坐标再转换为电脑屏幕的像素坐标,这一步称为视口变换或窗口变换。

下面这个照相的类比非常地生动形象。

观测的形象解释

2.1 Viewport Transformation-视口变换

一般来说,我们规定相机沿着 − z -\mathbf{z} z方向,在观测过程中为了简化会使用canonical view volume(CCV):它是一个正方体, x x x, y y y, z z z坐标都位于 − 1 -1 1 1 1 1之间,也即 ( x , y , z ) ∈ [ − 1 , 1 ] 3 (x,y,z)\in[-1,1]^3 (x,y,z)[1,1]3,我们将 x = − 1 x=-1 x=1投影到电脑屏幕的左侧,将 x = + 1 x=+1 x=+1投影到屏幕的右侧,将 y = − 1 y=-1 y=1投影到屏幕的底部,将 y = + 1 y=+1 y=+1投影到屏幕的顶部。

canonical view volume(CCV)

如果我们定义屏幕每个像素的长和宽为1,最小的像素中心坐标是 ( 0 , 0 ) (0,0) (0,0),则图像的中心到其边界为 0.5 0.5 0.5,如果屏幕上的像素总长度为 n x n_x nx,总宽度为 n y n_y ny,那么我们可以将canonical view volume(CCV)的 x o y \mathbf{xoy} xoy平面的方形 [ − 1 , 1 ] 2 [-1,1]^2 [1,1]2映射为长方形 [ − 0.5 , n x − 0.5 ] × [ − 0.5 , n y − 0.5 ] [-0.5,n_x-0.5]\times[-0.5,n_y-0.5] [0.5,nx0.5]×[0.5,ny0.5]

注意我们现在假设所有的线都在CCV正方体里,后面这个假设将在讲裁剪的时候放松这个条件。

对于视口变换,我们需要想把CCV正方体的 x o y \mathbf{xoy} xoy平面进行放缩然后将原点平移到屏幕的左下角(CCV的原点在 x o y \mathbf{xoy} xoy正方形的正中心),其可以写作一个二维的变换【这里相当于计算上面的线性映射: − 1 → − 0.5 , 1 → n x − 0.5 ( n y − 0.5 ) -1\rightarrow -0.5,1\rightarrow n_x-0.5(n_y-0.5) 10.5,1nx0.5(ny0.5)】:

[ x screen  y screen  1 ] = [ n x 2 0 n x − 1 2 0 n y 2 n y − 1 2 0 0 1 ] [ x canonical  y canonical  1 ] \begin{bmatrix} x_{\text {screen }} \\ y_{\text {screen }} \\ 1 \end{bmatrix}=\begin{bmatrix} \frac{n_{x}}{2} & 0 & \frac{n_{x}-1}{2} \\ 0 & \frac{n_{y}}{2} & \frac{n_{y}-1}{2} \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x_{\text {canonical }} \\ y_{\text {canonical }} \\ 1 \end{bmatrix} xscreen yscreen 1 = 2nx0002ny02nx12ny11 xcanonical ycanonical 1

这里忽略了 z \mathbf{z} z轴的坐标,因为投影最终和 z z z坐标无关,这里我们可以扩充矩阵(尽管在这里没有用):

M v p = [ n x 2 0 0 n x − 1 2 0 n y 2 0 n y − 1 2 0 0 1 0 0 0 0 1 ] \mathbf{M}_{\mathrm{vp}}=\begin{bmatrix} \frac{n_{x}}{2} & 0 & 0 & \frac{n_{x}-1}{2} \\ 0 & \frac{n_{y}}{2} & 0 & \frac{n_{y}-1}{2} \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Mvp= 2nx00002ny0000102nx12ny101

2.2 Orthographic Projection Transformation-正射变换

我们通常想把在渲染某个空间区域的几何元素而不是CCV,我们要调整我们的坐标轴的方向来实现正射变换,让坐标轴的 − z -\mathbf{z} z轴对着物体,让 y \mathbf{y} y轴朝上, x \mathbf{x} x轴按照右手定则定义。我们看到的view volume是一个 [ l , r ] × [ b , t ] × [ f , n ] [l,r]\times[b,t]\times[f,n] [l,r]×[b,t]×[f,n]的box。

正射变换

关于 l , r , b , y , f , n l,r,b,y,f,n l,r,b,y,f,n的物理含义可以看下面的表格:

{% tabs active:1 align:center %}

planemeaningplanemeaning
x = l x=l x=lleft plane x = r x=r x=rright plane
y = b y=b y=bbottom plane y = t y=t y=ttop plane
z = n z=n z=nnear plane z = f z=f z=ffar plane

{% endtabs %}

我们同样可以写出变换矩阵把这个box映射为CCV(参考原书公式的6.7,英文原版132页),变换的好处是简化数字在 − 1 -1 1 1 1 1之间,方便后续计算:

M orth  = [ 2 r − l 0 0 0 0 2 t − b 0 0 0 0 2 n − f 0 0 0 0 1 ] [ 1 0 0 − r + l 2 0 1 0 − t + b 2 0 0 1 − n + f 2 0 0 0 1 ] = [ 2 r − l 0 0 − r + l r − l 0 2 t − b 0 − t + b t − b 0 0 2 n − f − n + f n − f 0 0 0 1 ] \begin{aligned}\mathbf{M}_{\text {orth }}&=\left[\begin{array}{cccc} \frac{2}{r-l} & 0 & 0 & 0 \\ 0 & \frac{2}{t-b} & 0 & 0 \\ 0 & 0 & \frac{2}{n-f} & 0 \\ 0 & 0 & 0 & 1 \end{array}\right]\left[\begin{array}{cccc} 1 & 0 & 0 & -\frac{r+l}{2} \\ 0 & 1 & 0 & -\frac{t+b}{2} \\ 0 & 0 & 1 & -\frac{n+f}{2} \\ 0 & 0 & 0 & 1 \end{array}\right]\\&=\begin{bmatrix} \frac{2}{r-l} & 0 & 0 & -\frac{r+l}{r-l} \\ 0 & \frac{2}{t-b} & 0 & -\frac{t+b}{t-b} \\ 0 & 0 & \frac{2}{n-f} & -\frac{n+f}{n-f} \\ 0 & 0 & 0 & 1 \end{bmatrix}\end{aligned} Morth = rl20000tb20000nf200001 1000010000102r+l2t+b2n+f1 = rl20000tb20000nf20rlr+ltbt+bnfn+f1

现在我们可以转换视角中任意看到的点 ( x , y , z ) (x,y,z) (x,y,z)在像素上看到的位置 ( x p i x e l , y p i x e l , z c a n o n i c a l ) (x_{pixel},y_{pixel},z_{canonical}) (xpixel,ypixel,zcanonical)

[ x pixel  y pixel  z canonical  1 ] = ( M v p M orth  ) [ x y z 1 ] \left[\begin{array}{c} x_{\text {pixel }} \\ y_{\text {pixel }} \\ z_{\text {canonical }} \\ 1 \end{array}\right]=\left(\mathbf{M}_{\mathrm{vp}} \mathbf{M}_{\text {orth }}\right)\left[\begin{array}{c} x \\ y \\ z \\ 1 \end{array}\right] xpixel ypixel zcanonical 1 =(MvpMorth ) xyz1

CCV坐标变换至屏幕坐标直线算法流程:

CCV变换至屏幕坐标直线

z \mathbf{z} z坐标的范围是 [ − 1 , 1 ] [-1,1] [1,1],现在我们还没有用到,这将在z-buffer算法时很有用。

2.3 Camera Transformation-相机变换

当我们需要改变3D视角和观测的方向时,我们需要重新定义观测者的位置和方向(改变相机的放置位置)。可以定义相机坐标系,我们期望的相机的朝向可以由两个向量 g \mathbf{g} g和向量 t \mathbf{t} t来定义,以及一个点 e \mathbf{e} e来表示。

  • e \mathbf{e} e:相机位置
  • g \mathbf{g} g:观测方向
  • t \mathbf{t} t:上视方向

于是我们可以根据上面所说的向量和电定义我们的相机坐标系 u v w \mathbf{uvw} uvw(世界坐标系是 x y z \mathbf{xyz} xyz),其中坐标系的原点就是 e \mathbf{e} e v \mathbf{v} v轴和 t \mathbf{t} t矢量方向相同, w \mathbf{w} w轴和 − g -\mathbf{g} g矢量方向相同, u \mathbf{u} u轴根据右手定则确定。

w = − g ∥ g ∥ u = t × w ∥ t × w ∥ v = w × u \begin{aligned} \mathbf{w} & = -\frac{\mathbf{g}}{\|\mathbf{g}\|} \\ \mathbf{u} & = \frac{\mathbf{t} \times \mathbf{w}}{\|\mathbf{t} \times \mathbf{w}\|} \\ \mathbf{v} & = \mathbf{w} \times \mathbf{u} \end{aligned} wuv=gg=t×wt×w=w×u

相机坐标系

接下来我们会把世界坐标系的点坐标转换到相机坐标系中。我们可以把变换矩阵分解为两步,先平移再旋转。

由于将坐标系 u v w \mathbf{uvw} uvw转换为坐标系 x y z \mathbf{xyz} xyz的变换矩阵可以看做是

M c a m − 1 = [ u v w e 0 0 0 1 ] \mathbf{M}_{\mathrm{cam}}^{-1}=\left[\begin{array}{cccc} \mathbf{u} & \mathbf{v} & \mathbf{w} & \mathbf{e} \\ 0 & 0 & 0 & 1 \end{array}\right] Mcam1=[u0v0w0e1]

相机坐标系是要将坐标系 x y z \mathbf{xyz} xyz转换到坐标系 u v w \mathbf{uvw} uvw,于是这等价于对矩阵求一个逆。

M c a m = [ u v w e 0 0 0 1 ] − 1 = [ x u y u z u 0 x v y v z v 0 x w y w z w 0 0 0 0 1 ] [ 1 0 0 − x e 0 1 0 − y e 0 0 1 − z e 0 0 0 1 ] \mathbf{M}_{\mathrm{cam}}=\left[\begin{array}{cccc} \mathbf{u} & \mathbf{v} & \mathbf{w} & \mathbf{e} \\ 0 & 0 & 0 & 1 \end{array}\right]^{-1}=\left[\begin{array}{cccc} x_{u} & y_{u} & z_{u} & 0 \\ x_{v} & y_{v} & z_{v} & 0 \\ x_{w} & y_{w} & z_{w} & 0 \\ 0 & 0 & 0 & 1 \end{array}\right]\left[\begin{array}{cccc} 1 & 0 & 0 & -x_{e} \\ 0 & 1 & 0 & -y_{e} \\ 0 & 0 & 1 & -z_{e} \\ 0 & 0 & 0 & 1 \end{array}\right] Mcam=[u0v0w0e1]1= xuxvxw0yuyvyw0zuzvzw00001 100001000010xeyeze1

世界坐标正式投影变换至屏幕坐标画直线算法流程:

世界坐标正式投影变换至屏幕坐标画直线

2.4 Perspective Projective Transformations-透视变换

投影变换符合我们的视觉直观,主要体现在有近大远小的特性,平行线相交于一点。在实际的计算机图形中用得更多。

2D投影变换

如上图所示,我们从视点 e \mathbf{e} e沿着 g \mathbf{g} g方向看,看到的实际的点的高度为 y y y,反映在观察平面上高度为 y s y_s ys,观察平面距离视点为 d d d,实际的点距离视点为 z z z,根据简单的相似三角形的关系,我们有(这里我们认为 z z z是距离,为正数,而不是坐标意义下的负数):

y s = d z y y_{s}=\frac{d}{z} y ys=zdy

虎书里还提到了线性有理变换,这里把变换矩阵简单写一下(感兴趣可以直接看虎书)
[ x ~ y ~ z ~ w ~ ] = [ a 1 b 1 c 1 d 1 a 2 b 2 c 2 d 2 a 3 b 3 c 3 d 3 e f g h ] [ x y z 1 ] \left[\begin{array}{c} \tilde{x} \\ \tilde{y} \\ \tilde{z} \\ \tilde{w} \end{array}\right]=\left[\begin{array}{cccc} a_{1} & b_{1} & c_{1} & d_{1} \\ a_{2} & b_{2} & c_{2} & d_{2} \\ a_{3} & b_{3} & c_{3} & d_{3} \\ e & f & g & h \end{array}\right]\left[\begin{array}{c} x \\ y \\ z \\ 1 \end{array}\right] x~y~z~w~ = a1a2a3eb1b2b3fc1c2c3gd1d2d3h xyz1

( x ′ , y ′ , z ′ ) = ( x ~ / w ~ , y ~ / w ~ , z ~ / w ~ ) \left(x^{\prime}, y^{\prime}, z^{\prime}\right)=(\tilde{x} / \tilde{w}, \tilde{y} / \tilde{w}, \tilde{z} / \tilde{w}) (x,y,z)=(x~/w~,y~/w~,z~/w~)

上面的第二个公式刚好使用了计算机图形学笔记1-Transformation Matrix-变换*中注意的第二点:齐次坐标的等价性。使用上述的变换可以进行下面的操作:

前面我们已经讲了二维的情况。但是我们实际要处理的是三维的情况。经过投影变换就好像把一个棱台给变成了一个轴平行的box。这将方便我们使用正交投影变换矩阵变成CCV。

3D投影变换

这里我们就搬出之前在相机变换所建立的坐标系,我们依然使用 z = n z=n z=n近端平面和 z = f z=f z=f远端平面,并使用 z = n z=n z=n近端平面作为观察平面。需要注意的是,上面的 n n n z z z在坐标系定义下都是小于 0 0 0的。对于投影变换后的 x s x_s xs y s y_s ys,类似前面二维情况:

y s = n z y x s = n z x y_{s}=\frac{n}{z} y\quad x_{s}=\frac{n}{z} x ys=znyxs=znx

我们可以整理成矩阵的形式:

( x y z 1 ) ⇒ ( n x / z n y / z u n k n o w n 1 ) ≜ ( n x n y u n k n o w n z ) \begin{pmatrix} x\\y\\z\\1 \end{pmatrix}\Rightarrow\begin{pmatrix} nx/z\\ny/z\\\mathrm{unknown}\\1 \end{pmatrix}\triangleq\begin{pmatrix} nx\\ny\\\mathrm{unknown}\\z \end{pmatrix} xyz1 nx/zny/zunknown1 nxnyunknownz

于是我们所要求的投影变换矩阵满足:

P ( x y z 1 ) = ( n x n y u n k n o w n z ) ⇒ P = ( n 0 0 0 0 n 0 0 ? ? ? ? 0 0 1 0 ) \mathbf{P}\begin{pmatrix} x\\y\\z\\1 \end{pmatrix}=\begin{pmatrix} nx\\ny\\\mathrm{unknown}\\z \end{pmatrix}\Rightarrow \mathbf{P} =\left(\begin{array}{cccc} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ ? & ? & ? & ? \\ 0 & 0 & 1 & 0 \end{array}\right) P xyz1 = nxnyunknownz P= n0?00n?000?100?0

那么 z s z_{s} zs是多少呢?对投影变换我们注意到:

  1. 近端平面 z = n z=n z=n的点映射以后点不发生变化。

  2. 远端平面 z = f z=f z=f的点映射以后z坐标不变。

根据第(1)点:近端平面 z = n z=n z=n的点映射以后点不发生变化。我们有:

P ( x y n 1 ) = ( x y n 1 ) ≜ ( n x n y n 2 1 ) \mathbf{P}\begin{pmatrix} x\\y\\n\\1 \end{pmatrix}=\begin{pmatrix} x\\y\\n\\1 \end{pmatrix} \triangleq\begin{pmatrix} nx\\ny\\n^2\\1 \end{pmatrix} P xyn1 = xyn1 nxnyn21

所以 P \mathbf{P} P的第三行形式为 ( 0   0   A   B ) \begin{pmatrix} 0\,0\,A\,B \end{pmatrix} (00AB),前两个元素为0是因为该齐次坐标的 z z z坐标为 n 2 n^2 n2,和 x , y x,y x,y的取值无关。我们单独拿出第三行和齐次坐标相乘有:

( 0 0 A B ) ( x y n 1 ) = n 2 ⇒ A n + B = n 2 \begin{pmatrix} 0 & 0 & A & B \end{pmatrix}\begin{pmatrix} x \\ y \\ n \\ 1 \end{pmatrix}=n^{2}\Rightarrow An+B=n^2 (00AB) xyn1 =n2An+B=n2

根据第(2)点:远端平面 z = f z=f z=f的点映射以后z坐标不变。我们有:

( 0 0 f 1 ) ⇒ ( 0 0 f 1 ) ≜ ( 0 0 f 2 f ) ⇒ A f + B = f 2 \left(\begin{array}{l} 0 \\ 0 \\ f \\ 1 \end{array}\right) \Rightarrow\left(\begin{array}{l} 0 \\ 0 \\ f \\ 1 \end{array}\right)\triangleq\left(\begin{array}{c} 0 \\ 0 \\ f^{2} \\ f \end{array}\right)\Rightarrow Af+B=f^2 00f1 00f1 00f2f Af+B=f2

于是我们有:

{ A n + B = n 2 A f + B = f 2 ⇒ { A = n + f B = − n f \left\{\begin{aligned} &An+B=n^2\\ &Af+B=f^2 \end{aligned}\right.\quad\Rightarrow\quad\left\{\begin{aligned} &A=n+f\\ &B=-nf \end{aligned}\right. {An+B=n2Af+B=f2{A=n+fB=nf

这样我们就确定了投影变换的变换矩阵:

P = [ n 0 0 0 0 n 0 0 0 0 n + f − f n 0 0 1 0 ] \mathbf{P}=\begin{bmatrix} n & 0 & 0 & 0 \\ 0 & n & 0 & 0 \\ 0 & 0 & n+f & -f n \\ 0 & 0 & 1 & 0 \end{bmatrix} P= n0000n0000n+f100fn0

经过投影变换最终我们看到坐标发生了如下的变化:

P [ x y z 1 ] = [ n x n y ( n + f ) z − f n z ] ≜ [ n x z n y z n + f − f n z 1 ] \mathbf{P}\left[\begin{array}{l} x \\ y \\ z \\ 1 \end{array}\right]=\left[\begin{array}{c} n x \\ n y \\ (n+f) z-f n \\ z \end{array}\right] \triangleq\left[\begin{array}{c} \frac{n x}{z} \\ \frac{n y}{z} \\ n+f-\frac{f n}{z} \\ 1 \end{array}\right] P xyz1 = nxny(n+f)zfnz znxznyn+fzfn1

有时候我们想把屏幕坐标变换回世界坐标,这时候我们就需要用到 P \mathbf{P} P的逆矩阵:

P − 1 = [ 1 n 0 0 0 0 1 n 0 0 0 0 0 1 0 0 − 1 f n n + f f n ] ≜ [ f 0 0 0 0 f 0 0 0 0 0 f n 0 0 − 1 n + f ] \mathbf{P}^{-1}=\left[\begin{array}{cccc} \frac{1}{n} & 0 & 0 & 0 \\ 0 & \frac{1}{n} & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & -\frac{1}{f n} & \frac{n+f}{f n} \end{array}\right]\triangleq\left[\begin{array}{cccc} f & 0 & 0 & 0 \\ 0 & f & 0 & 0 \\ 0 & 0 & 0 & f n \\ 0 & 0 & -1 & n+f \end{array}\right] P1= n10000n100000fn1001fnn+f f0000f00000100fnn+f

完整的透视变换包括了正视变换和投影变换的组合,先处理近大远小的投影变换为box,然后把box变回一个CCV。

M p e r = M o r t h P = [ 2 n r − l 0 l + r l − r 0 0 2 n t − b b + t b − t 0 0 0 f + n n − f 2 f n f − n 0 0 1 0 ] \mathbf{M}_{\mathrm{per}}=\mathbf{M}_{\mathrm{orth}} \mathbf{P}=\left[\begin{array}{cccc} \frac{2 n}{r-l} & 0 & \frac{l+r}{l-r} & 0 \\ 0 & \frac{2 n}{t-b} & \frac{b+t}{b-t} & 0 \\ 0 & 0 & \frac{f+n}{n-f} & \frac{2 f n}{f-n} \\ 0 & 0 & 1 & 0 \end{array}\right] Mper=MorthP= rl2n0000tb2n00lrl+rbtb+tnff+n100fn2fn0

在OpenGL中,这一矩阵的定义可能不一样:

M OpenGL  = [ 2 ∣ n ∣ r − l 0 r + l r − l 0 0 2 ∣ n ∣ t − b t + b t − b 0 0 0 ∣ n ∣ + ∣ f ∣ ∣ n ∣ − ∣ f ∣ 2 ∣ f ∣ ∣ n ∣ ∣ n ∣ − ∣ f ∣ 0 0 − 1 0 ] \mathbf{M}_{\text {OpenGL }}=\left[\begin{array}{cccc} \frac{2|n|}{r-l} & 0 & \frac{r+l}{r-l} & 0 \\ 0 & \frac{2|n|}{t-b} & \frac{t+b}{t-b} & 0 \\ 0 & 0 & \frac{|n|+|f|}{|n|-|f|} & \frac{2|f||n|}{|n|-|f|} \\ 0 & 0 & -1 & 0 \end{array}\right] MOpenGL = rl2∣n0000tb2∣n00rlr+ltbt+bnfn+f100nf2∣f∣∣n0

这里我们使用了 M o r t h \mathbf{M}_{\mathrm{orth}} Morth,随之而来的问题是: M o r t h \mathbf{M}_{\mathrm{orth}} Morth中的 l , r , t , b l,r,t,b l,r,t,b这些值怎么定义呢,它们定义了我们的窗口看到的物体,由于近端平面 z = n z=n z=n x x x y y y不变,我们这里选择了近端平面 z = n z=n z=n来定义 l , r , t , b l,r,t,b l,r,t,b

我们最后来看一下从相机坐标经过透视变换到屏幕坐标的算法流程:

相机坐标经过透视变换到屏幕坐标的算法流程

2.5 Field-of-View-视场

我们通过 ( l , r , t , b ) (l,r,t,b) (l,r,t,b) n n n定义我们的窗口,我们可以进一步简化,如果我们屏幕的中心是原点,那么有:

l = − r , b = − t . \begin{aligned} l & =-r, \\ b & =-t . \end{aligned} lb=r,=t.

另外我们可以添加每个像素是正方形的约束,使得图形没有形状的畸变,我们使用的 r r r t t t一定要和水平像素数 n x n_x nx和竖直像素数 n y n_y ny成比例:

n x n y = r t \frac{n_{x}}{n_{y}}=\frac{r}{t} nynx=tr

n x n_x nx n y n_y ny被确定以后,只剩下一个自由度,我们经常使用 θ \theta θ作为视场,下图为垂直视场,它满足关系:

tan ⁡ θ 2 = t ∣ n ∣ \tan\frac{\theta}{2}=\frac{t}{|n|} tan2θ=nt

垂直视场

通过确定 n n n θ \theta θ,我们就可以计算 t t t来得到更一般的观测系统。

参考资料

  1. GAMES101-现代计算机图形学入门-闫令琪
  2. Shirley & Marschner, Fundamentals of Computer Graphics. forth edition

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/53829.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OSPF协议RIP协议+OSPF实验(eNSP)

本篇博客主要讲解单区域的ospf,多区域的仅作了解。 目录 一、OSPF路由协议概述 1.内部网关协议和外部网关协议 二、OSPF的应用环境 1.从以下几方面考虑OSPF的使用 2.OSPF的特点 三、OSPF重要基本概念 3.1,辨析邻居和邻接关系以及七种邻居状态 3…

MySQL中的函数

系列文章目录 MySQL常见的几种约束 文章目录 系列文章目录前言一、单行函数1.字符串函数 (String StringBuilder)2.数值函数 (Math)3.日期与时间函数4.流程函数( IF SWITCH)5.JSON函数6.其他函数 二、多行…

shopee,lazada,etsy店群如何高效安全的管理

对于电商卖家来说,要经营多个店铺,管理多个账号是非常常见的操作。为了避免账号关联被平台识别出来,需要使用防关联的浏览器来进行操作 ​1、支持多平台 支持同时管理多个电商平台店铺,Shopee、Lazada、etsy、poshmark、vinted等&…

Pytorch 最全入门介绍,Pytorch入门看这一篇就够了

本文通过详细且实践性的方式介绍了 PyTorch 的使用,包括环境安装、基础知识、张量操作、自动求导机制、神经网络创建、数据处理、模型训练、测试以及模型的保存和加载。 1. Pytorch简介 在这一部分,我们将会对Pytorch做一个简单的介绍,包括它…

MFC第二十四天 使用GDI对象画笔和画刷来开发控件(分页控件选择态的算法分析、使用CToolTipCtrl开发动静态提示)

文章目录 GDI对象画笔和画刷来开发控件梯形边框的按钮控件CMainDlg.hCMainDlg.cppCLadderCtrl.hCLadderCtrl.cpp 矩形边框的三态按钮控件 CToolTipCtrl开发动静态提示CMainDlg.hCMainDlg.cppCLadderCtrl.hCLadderCtrl.cpp: 实现文件 矩形边框的三态按钮控件 CToolTipCtrl开发动…

华为、阿里巴巴、字节跳动 100+ Python 面试问题总结(六)

系列文章目录 个人简介:机电专业在读研究生,CSDN内容合伙人,博主个人首页 Python面试专栏:《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读,一起进步!🌟🌟🌟 …

echart折线图,调节折线点和y轴的间距(亲测可用)

options代码: options {tooltip: {trigger: axis, //坐标轴触发,主要在柱状图,折线图等会使用类目轴的图表中使用。},xAxis: {type: category,//类目轴,适用于离散的类目数据,为该类型时必须通过 data 设置类目数据。…

抓紧收藏,Selenium无法定位元素的几种解决方案

01、frame/iframe表单嵌套 WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌的页面元素无法直接定位。 解决方法: driver.switch_to.frame(id/name/obj) switch_to.frame()默认可以直接取表单的id或name属性。如果没有可用的id和…

自适应大屏

可视化大屏适配/自适应现状 可视化大屏的适配是一个老生常谈的话题了,现在其实不乏一些大佬开源的自适应插件、工具但是我为什么还要重复造轮子呢?因为目前市面上适配工具每一个都无法做到完美的效果,做出来的东西都差不多,最终实…

ETHERNET/IP 转ETHERCAT连接ethercat总线伺服如何控制

捷米JM-EIP-ECAT网关连接到ETHERNET/IP总线中做为从站使用,连接到ETHERCAT总线中做为从站使用,可以同时满足多种工业生产的需求。支持广泛的设备类型,可以和多种不同的设备进行通讯。 技术参数 ETHERNET/IP 技术参数 网关做为 ETHERNET/IP …

CentOS下 Docker、Docker Compose 的安装教程

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。 Docker Compose是用于定义…

一些类型推导相关的功能(C++)

目录 auto关键的新用法(C11起) 背景介绍 用法示例 注意事项 typeid运算符 type_info类 typeid的用法 decltype运算符 用法阐述 用法示例 用法建议 function类模板 用法阐述 用法示例 function较函数指针的优势 std::function和decltype的…

DID以及社交网络中的ZKP

1. 引言 本文关键术语为: Decentralized Identity (DID,去中心化身份) or self-sovereign identity (SSI,自治身份) :是一个基于开放标准的框架,使用自主、独立的标识符和可验证证书,实现可信的数据交换。…

Vue2 第六节 key的作用与原理

(1)虚拟DOM (2)v-for中的key的作用 一.虚拟DOM 1.虚拟DOM就是内存中的数据 2.原生的JS没有虚拟DOM: 如果新的数据和原来的数据有重复数据,不会在原来的基础上新加数据,而是重新生成一份 3. Vue会有虚拟…

驶向专业:嵌入式开发在自动驾驶中的学习之道

导语: 自动驾驶技术在汽车行业中的快速发展为嵌入式开发领域带来了巨大的机遇。作为自动驾驶的核心组成部分,嵌入式开发在驱动汽车的智能化和自主性方面发挥着至关重要的作用。本文将探讨嵌入式开发的学习方向、途径以及未来在自动驾驶领域中的展望。 一、学习方向:…

3ds MAX绘制茶壶

综合一下之前的内容画个茶壶 长方形,然后转化为可编辑多边形,添加节点并设置圆角,如下图 车削生成一个圆环,其实这一步也可以用一个圆柱体和两个圆角圆柱体解决 效果如下: 茶壶的底座绘制好了 接下来是茶壶的上半边 …

python测试开发面试常考题:装饰器

目录 简介 应用 第一类对象 装饰器 描述器descriptor 资料获取方法 简介 Python 装饰器是一个可调用的(函数、方法或类),它获得一个函数对象 func_in 作为输入,并返回另一函数对象 func_out。它用于扩展函数、方法或类的行为。 装饰器模式通常用…

Hudi Flink SQL源码调试学习(1)

前言 本着学习hudi-flink源码的目的,利用之前总结的文章Hudi Flink SQL代码示例及本地调试中的代码进行调试,记录调试学习过程中主要的步骤及对应源码片段。 版本 Flink 1.15.4Hudi 0.13.0 目标 在文章Hudi Flink SQL代码示例及本地调试中提到:我们…

【LLM系列之指令微调】长话短说大模型指令微调的“Prompt”

1 指令微调数据集形式“花样”太多 大家有没有分析过 prompt对模型训练或者推理的影响?之前推理的时候,发现不加训练的时候prompt,直接输入模型性能会变差的,这个倒是可以理解。假如不加prompt直接训练,是不是测试的时…

基于高通QCC5171的对讲机音频数据传输系统设计

一 研发资料准备 二 设计方法 蓝牙连接与配对:使用QCC5171的蓝牙功能,实现设备之间的蓝牙连接和配对。确保设备能够相互识别并建立起稳定的蓝牙连接。 音频采集与处理:将麦克风采集到的音频数据通过QCC5171的ADC(模数转换器&…