[Ray Tracing: The Next Week] 笔记

前言

本篇博客参照自《Ray Tracing: The Next Week》教程,地址为:https://raytracing.github.io/books/RayTracingTheNextWeek.html

该教程在ray tracing in one weekend的基础上,增加了运动模糊、BVH树、Texture映射、柏林噪声、光照、体积渲染等内容。

渲染器的构建过程

与我的上一篇系列笔记类似,我会顺序罗列我认为重要的部分。

运动模糊

这一点和散焦模糊有些类似,也是模仿真实相机拍摄到的现象,即,当相机的快门按下时,运动的物体会发生模糊。

要实现这一点,方法是,在发射采样光线时,令光线在快门的这一段时间内随机发射,再混合采样结果。为光线增加了一个时间t属性。

于此同时,为场景种添加相应的运动物体,比如运动的球体,在1秒的时间内,球体会在某一个方向上移动。这时,因为光线包含时间属性,t时刻的光线r会打到在该时刻对应位置的物体表面上。

最后渲染时,则是将在快门这一时间段内所有的光线追踪结果混合。

以下是运动模糊的渲染结果:

在这里插入图片描述

BVH树

BVH树是一种空间划分的数据结构,可以有效提高光线与物体求交的效率。

在该教程中,BVH树的建立分为以下几步:

  1. 将所有图元包裹在一个大的包围盒中。

  2. 进行BVH树的划分。

  3. 一直分到BVH树的叶子节点只有一个图元为止。

作者在划分BVH树时,不希望留下空子树的问题,因此,在划分子树时,若当前只剩下一个图元,文中会令这个图元既属于左子树又属于右子树。另外,在划分过程中,对于[begin, end]范围内的图元,中点为mid,文中会将[begin, mid]划归左子树,[mid, end]划归右子树,相当于mid这个位置的图元既属于左子树又属于右子树。这种划分方式属于作者的喜好,有它的优势。

一个BVH树的例子如下:

在这里插入图片描述

包围盒

BVH树采用的包围盒应当便于计算相交,且紧密,AABB包围盒符合这个条件。

三维场景内的AABB包围盒为包含目标图元,且边平行于坐标轴的最小六面体。

如何计算射线与AABB包围盒的相交?拿二维场景举例,如下图所示,射线在与x的两个边界相交时,对应的相交位置为t1、t2,射线在与y的两个边界相交时,对应的相交位置为t3、t4,如果射线与这个2D AABB包围盒相交,则t1、t2与t3、t4 的范围重叠。

在这里插入图片描述

三维空间中与上述类似,当射线与x、y、z方向的对应平面相交时,如果范围有重合,则说明射线与包围盒相交。

如何求解射线与对应平面的交点?因为边界都是轴对齐的,将射线的方程带入对应边界的x、y或z值即可。

BVH树的划分

BVH树的划分主要分为三个步骤:

  1. 随机选择一个轴
  2. 对BVH树种的图元进行排序
  3. 对这些图元进行二分,构建子BVH树

文中给出的方法是每次随机选出一个轴,当然也可以轮流选择x、y、z轴。在排序时,依据的排序规则是每个图元对应轴的最小值。在C++中,可以构建box_x_compare、box_y_compare、box_z_compare,然后调用sort方法对数组进行排序。

最终,按照文中给出的BVH树构建方法,光线追踪的渲染过程有所加速。

纹理映射

在光线追踪中,纹理映射是一个反向寻找的过程,即通过射线与物体的交点,反向推算出交点在纹理中的颜色、凹凸等其他属性。这和光栅化不同,光栅化是通过纹理获得物体所有表面的颜色等属性,然后通过光栅化的过程投射给图像。

文中介绍了几种纹理,分别是纯色纹理、空间纹理、以及利用uv坐标的纹理。

纯色纹理。与uv坐标和空间坐标都无关,利用纯色纹理的材质,物体表面的所有颜色都一样。

空间纹理。采样和物体表面的空间坐标有关,和uv坐标无关,即物体表面某一点的颜色由该点的世界坐标(x, y, z)计算得出。

方格纹理就是一种空间纹理,对于任意一点,其颜色的计算是这样的:对于坐标(x, y, z),首先向下取整,得到三个整数结果(⌊x⌋,⌊y⌋,⌊z⌋),将这三个结果相加并进行模2运算,得到0或1。如果结果为0将映射到偶数颜色,如果结果为1将映射到奇数颜色。

方格纹理的一个例子如下:

在这里插入图片描述

利用uv坐标的纹理。需要知道如何根据物体表面的三维坐标,计算uv纹理坐标,再利用纹理坐标做颜色运算。因为场景中目前提供的模型都是球体,且目前也不涉及旋转,因此这里的uv坐标直接沿着球面计算。

球面的uv坐标用经纬度表示,其中u = Φ / 2π,v = θ / π,Φ为对应点在单位球中的经度,θ为对应点在单位球中的纬度。球面上点的三维关系与经纬度的关系如下:
y = − c o s ( θ ) x = − c o s ( ϕ ) s i n ( θ ) z = s i n ( ϕ ) s i n ( θ ) y = -cos(\theta)\\ x = -cos(\phi)sin(\theta)\\ z = sin(\phi)sin(\theta) y=cos(θ)x=cos(ϕ)sin(θ)z=sin(ϕ)sin(θ)
从而可得
ϕ = a t a n 2 ( − z , x ) + π θ = a r c c o s ( − y ) \phi = atan2(-z, x) + \pi\\ \theta = arccos(-y) ϕ=atan2(z,x)+πθ=arccos(y)
注意,这里因为atan2的数值范围为-π到π,但是为了让u映射到[0, 1]范围,需要加上一个π。

利用uv坐标的纹理效果如下:

在这里插入图片描述

柏林噪声

柏林噪声是一种自然噪声生成算法。文中主要利用柏林噪声生成空间纹理。

教程中一步步地给出3D柏林噪声的迭代改进过程。

第零次迭代,按照上述方格纹理的思路,首先生成一个大小为256的double数组ranfloat,其中的每个值为从0到1的随机。对于物体表面的每一个点,根据其三维坐标(x, y, z),得出一个指向ranfloat的下标,并根据这个下标,计算出方格的灰阶。其计算代码如下:

int i = static_cast<int>(4 * x) % 256;
int j = static_cast<int>(4 * y) % 256;
int k = static_cast<int>(4 * z) % 256;

int index = i ^ j ^ k;
double grayscale = ranfloat[index];

这一步可以得出一种类似瓦片的效果,这种纹理是重复的。

在这里插入图片描述

第一次迭代,在上述瓦片贴图的基础上,加一些随机,具体到计算,主要是对上述代码中的i,j,k进行随机。这一部分的计算代码如下:

static int* perlin_generate_perm(){
    int p = new int[256];
    for(int i = 0;i<256;i++) p[i] = i;
    
  	for(int i = 255;i>0;i--){
        int target = random_int(0, i); // 返回一个[0, i]范围的整数
        swap(p[i], p[target]);
    }
    return p;
}
// noise函数内部
{
    ...
    int* perm_x = perlin_generate_perm();
    int* perm_y = perlin_generate_perm();
    int* perm_z = perlin_generate_perm();
    i = perm_x[i];
    j = perm_y[j];
    k = perm_z[k];
    ...
}

这一步的纹理效果:

在这里插入图片描述

第二次迭代,在生成噪声时,进行线性插值。这一步是将一个点周围8个位置的值做平均加权运算,得出当前位置的灰阶。其核心计算代码如下:

static double trilinear_interp(double c[2][2][2], double u, double v, double w){
    double accum = 0.0;
    for (int i=0; i < 2; i++)
            for (int j=0; j < 2; j++)
                for (int k=0; k < 2; k++)
                    accum += (i*u + (1-i)*(1-u))*
                            (j*v + (1-j)*(1-v))*
                            (k*w + (1-k)*(1-w))*c[i][j][k];
	return accum;
}

// noise函数内部
{
    ...
    int u = x - floor(x); // floor为向下取整运算
    int v = y - floor(y);
    int w = z - floor(z);

    int i = static_cast<int>(floor(x));
    int j = static_cast<int>(floor(y));
    int k = static_cast<int>(floor(z));
    double c[2][2][2];

    for(int di=0;di < 2;di++){
        for(int dj=0;dj<2;dj++){
            for(int dk=0;dk<2;dk++){
                c[di][dj][dk] = ranfloat[
                    perm_x[(i+di) % 256] ^
                    perm_y[(j+dj) % 256] ^
                    perm_z[(k+dk) % 256]
                ];
            }
        }
    }
    
    double grayscale = trilinear_interp(c, u, v, w);
	...
}

此时得出的结果:

在这里插入图片描述

第三次迭代,采用Hermitian插值,而不是线性插值来得到uvw的值,让结果更平滑,消除上述明显的网格特征。添加代码:

u = u*u*(3-2*u);
v = v*v*(3-2*v);
w = w*w*(3-2*w);

这一步得出的结果:

在这里插入图片描述

第四次迭代,调整噪声贴图的频率,方法就是将一个比例系数scale乘以(x, y, z)坐标值,scale越大,频率就越大。我的理解是,这种噪声的生成有一个模式,每经过一定的间隔就会得出相似的噪声结果。把坐标值增大,再来采样,等效于将模式的间隔缩小,这样导致的结果便是噪声图看起来频率更高。

这一步得出的效果如下:

在这里插入图片描述

第五次迭代,上面的结果看起来仍然有些块状,或许是因为这种模式的最小值和最大值总是恰好落在(x, y, z)都为整数的坐标上。因为当(x, y, z)为整数时,u, v, w的值均为0,此时trilinear_interp的结果为c[0][0][0],等于这个点没有和周围做平均运算。

可以用随机vec3数组ranvec代替之前的随机double数组ranfloat进行采样。在加权平均时,用权重向量和周围点的随机向量做点积,累加获得当前点的采样值。这样可以避免最小和最大值总是恰好落在整数坐标上。由于乘出来的值有可能小于零,需要做0.5 * (grayscale + 1.0)的归一化操作。

这一步的核心代码如下:

static double perlin_interp(vec3 c[2][2][2], double u, double v, double w) {
	double uu = u*u*(3-2*u);
    double vv = v*v*(3-2*v);
    double ww = w*w*(3-2*w);
    double accum = 0.0;

    for (int i=0; i < 2; i++)
        for (int j=0; j < 2; j++)
            for (int k=0; k < 2; k++) {
                vec3 weight_v(u-i, v-j, w-k);
                accum += (i*uu + (1-i)*(1-uu))
                       * (j*vv + (1-j)*(1-vv))
                       * (k*ww + (1-k)*(1-ww))
                       * dot(c[i][j][k], weight_v);
            }

    return accum;
}
// noise函数内部
{
    ...
    double u = x - floor(x);
    double v = y - floor(y);
    double w = z - floor(z);
    int i = static_cast<int>(floor(x));
    int j = static_cast<int>(floor(y));
    int k = static_cast<int>(floor(z));
    vec3 c[2][2][2];
    
    for (int di=0; di < 2; di++)
            for (int dj=0; dj < 2; dj++)
                for (int dk=0; dk < 2; dk++)
                    c[di][dj][dk] = ranvec[
                        perm_x[(i+di) % 256] ^
                        perm_y[(j+dj) % 256] ^
                        perm_z[(k+dk) % 256]
                    ];
    double grayscale = perlin_interp(c, u, v, w);
    ...
}

这一步的结果如下图:

在这里插入图片描述

第六次迭代,制造湍流效果。将多个不同频率的噪声以不同的权重相加,这就是湍流。湍流部分的计算代码如下:

double turb(const point3& p, int depth=7) const {
    double accum = 0.0;
    double temp_p = p;
    double weight = 1.0;

    for (int i = 0; i < depth; i++) {
        accum += weight*noise(temp_p);
        weight *= 0.5;
        temp_p *= 2;
    }

    return fabs(accum);
}

这一步得出的效果如下:

注:这里的噪声图颜色很深,是因为这里的效果没有做归一化操作,而是直接把小于零的结果直接取绝对值了。

在这里插入图片描述

第七次,最后一次迭代,调整相位。将得出的灰度值送入正弦函数做计算,可以得到起伏的结果。利用相位迭代,可以实现类似大理石表面的纹理效果。这一步的核心代码如下:

double s = scale * p;
color = color(1,1,1) * 0.5 * (1 + sin(s.z() + 10*noise.turb(s)));

效果图如下:

在这里插入图片描述

平行四边形

除了球体之外,教程中终于添加了另外一种图元:平行四边形。

平行四边形用一个基点和两个向量表示:基点Q、两个边向量u和v。

如何实现射线与平行四边形的求交?主要分三步。

  1. 找到包含这个平行四边形的平面。

  2. 判断射线与这个平面的相交情况。

  3. 判断交点是否在这个平行四边形内。

找到四边形的平面

可以用u和v的叉乘得出平面法线,再将基点Q带入,便可得出这个平面方程。

判断射线与这个平面的相交

射线由一个基点和一个方向向量表示,判断射线与这个平面的相交,可以将射线的方程带入平面的方程。如果射线与平面平行或共面,则认为不相交,如果不平面,则求出这个交点,判断是否在射线的有效范围内。我们可以得到以下的式子。
平面方程: A x + B y + C z = D  射线方程: R ( t ) = P + t d 代入可得: n ∗ ( P + t d ) = D 解方程: n ∗ P + n ∗ t d = D n ∗ P + t ( n ∗ d ) = D t = D − n ∗ P n ∗ d 平面方程:Ax + By + Cz = D \ 射线方程:R(t) = \mathbf P + t\mathbf d\\ 代入可得:\mathbf n * (\mathbf P + t\mathbf d) = D \\ 解方程:\mathbf n *\mathbf P +\mathbf n * t\mathbf d = D\\ \mathbf n *\mathbf P + t(\mathbf n *\mathbf d) = D\\ t = \frac{D - \mathbf n * \mathbf P}{\mathbf n * \mathbf d} 平面方程:Ax+By+Cz=D 射线方程:R(t)=P+td代入可得:n(P+td)=D解方程:nP+ntd=DnP+t(nd)=Dt=ndDnP
如果t属于射线范围[t_min, t_max]之内,则认为射线与这个平面相交。

判断交点是否在这个平行四边形内

将平面的点转换成以u、v为基底的二维坐标系中,对于任意一个点P,可以表示为P = Q + αu + βv。做以下运算:
令 p = P − Q = α u + β v , p 是从 Q 到 P 的向量 将 u 、 v 向量分别与 p 叉乘: v × p = α ( v × u ) + β ( v × v ) = α ( v × u ) u × p = α ( u × u ) + β ( u × v ) = β ( u × v ) 向量的除法不能直接进行,两边点乘 n n ∗ ( v × p ) = n ∗ α ( v × u ) n ∗ ( u × p ) = n ∗ β ( u × v ) 则 α = n ∗ ( v × p ) n ∗ ( v × u ) β = n ∗ ( u × p ) n ∗ ( u × v ) 令 w = n n ∗ ( u × v ) = n n ∗ n α = w ∗ ( p × v ) β = w ∗ ( u × p ) 令 p =\mathbf P -\mathbf Q = \alpha \mathbf u + \beta \mathbf v ,p是从Q到P的向量 \\ \\将u、v向量分别与p叉乘:\\ \mathbf v \times \mathbf p = \alpha (\mathbf v \times \mathbf u) + \beta (\mathbf v \times \mathbf v) = \alpha (\mathbf v \times \mathbf u)\\ \mathbf u \times \mathbf p = \alpha (\mathbf u \times \mathbf u) + \beta (\mathbf u \times \mathbf v) = \beta (\mathbf u \times \mathbf v)\\ \\ 向量的除法不能直接进行,两边点乘n\\ \mathbf n * (\mathbf v \times \mathbf p) = \mathbf n * \alpha (\mathbf v \times \mathbf u)\\ \mathbf n * (\mathbf u \times \mathbf p) = \mathbf n * \beta (\mathbf u \times \mathbf v)\\则 \\ \alpha = \frac{\mathbf n * (\mathbf v \times \mathbf p)}{\mathbf n * (\mathbf v \times \mathbf u)}\\ \beta = \frac{\mathbf n * (\mathbf u \times \mathbf p)}{\mathbf n * (\mathbf u \times \mathbf v)}\\ \\ 令 w = \frac{\mathbf n}{\mathbf n * (\mathbf u \times \mathbf v)} = \frac{\mathbf n}{\mathbf n * \mathbf n} \\ \alpha = \mathbf w * (\mathbf p \times \mathbf v)\\ \beta = \mathbf w * (\mathbf u \times \mathbf p) p=PQ=αu+βvp是从QP的向量uv向量分别与p叉乘:v×p=α(v×u)+β(v×v)=α(v×u)u×p=α(u×u)+β(u×v)=β(u×v)向量的除法不能直接进行,两边点乘nn(v×p)=nα(v×u)n(u×p)=nβ(u×v)α=n(v×u)n(v×p)β=n(u×v)n(u×p)w=n(u×v)n=nnnα=w(p×v)β=w(u×p)
要判断点是否在平行四边形中,判断α和β在[0, 1]范围内即可。

平行四边形的绘制结果:

在这里插入图片描述

灯光

文中将能发光的特性作为一个灯光材质。

灯光材质会有一个主动发光的函数,这样在光线打到灯光材质表面时,会增加一项主动发光所产生的颜色。对于不发光的材质而言,主动发光产生的颜色为零。

同时设置背景颜色,当射线最终没有打到物体时,赋予默认背景颜色,而不是根据画布位置得出的渐变色。

得到的灯光效果如下:

在这里插入图片描述

实例化

这一部分主要引入了模型的移动和旋转。

所谓实例是已放置到场景中的几何图元的副本,它完全独立于图元的其他副本,并且可以移动或旋转。

模型移动

在这个过程的实现中,文中没有直接移动模型的坐标,而是反向变换光线的原点位置,然后与模型做求交运算。具体而言,主要分为以下三步:

  1. 将射线的原点反向移动偏移量

  2. 判断添加偏移后的射线是否与物体存在交点(如果存在,判断在何处)

  3. 给交点的位置增加偏移量

这一过程也可以从坐标变化的角度来思考:

  1. 将射线从世界坐标系转换到物体局部坐标系
  2. 判断射线在物体坐标系内是否与物体存在交点(如果存在,判断在何处)
  3. 将交点从物体局部坐标系转换到世界坐标系

文中将移动封装成了一个可击中对象,相当于构建了一个实例,这部分的代码如下:

class translate : public hittable {
  public:
    bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
        // 将射线的原点反向移动偏移量
        ray offset_r(r.origin() - offset, r.direction(), r.time());

        // 判断添加偏移后的射线是否与物体存在交点(如果存在,判断在何处)
        if (!object->hit(offset_r, ray_t, rec))
            return false;

        // 给交点的位置增加偏移量
        rec.p += offset;

        return true;
    }

  private:
    shared_ptr<hittable> object;
    vec3 offset;
};

模型旋转

模型的旋转与上述的构建过程类似,不过稍微复杂。

文中给出的旋转表示方法是欧拉角表示法。当射线变换到有旋转的物体坐标系时,不仅原点位置改变,射线的方向也会改变。这一过程如下:

  1. 将光线的原点和方向从世界坐标系变换到物体坐标系
  2. 判断光线在物体坐标系内与物体是否相交以及交点在何处
  3. 将交点的位置以及交点处的法线从物体坐标系变换到世界坐标系

旋转同样被封装成了一个可击中对象,相当于一个实例,这部分的核心代码如下:

class rotate_y : public hittable {
  public:

    bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
        // 将光线从世界坐标系转换到物体局部坐标系
        auto origin = r.origin();
        auto direction = r.direction();

        origin[0] = cos_theta*r.origin()[0] - sin_theta*r.origin()[2];
        origin[2] = sin_theta*r.origin()[0] + cos_theta*r.origin()[2];

        direction[0] = cos_theta*r.direction()[0] - sin_theta*r.direction()[2];
        direction[2] = sin_theta*r.direction()[0] + cos_theta*r.direction()[2];

        ray rotated_r(origin, direction, r.time());

        // 判断是否有交点以及交点在何处
        if (!object->hit(rotated_r, ray_t, rec))
            return false;

        // 将交点从物体局部坐标系转换到世界坐标系
        auto p = rec.p;
        p[0] =  cos_theta*rec.p[0] + sin_theta*rec.p[2];
        p[2] = -sin_theta*rec.p[0] + cos_theta*rec.p[2];

        // 将交点处的法线从物体局部坐标系转换到世界坐标系
        auto normal = rec.normal
        normal[0] =  cos_theta*rec.normal[0] + sin_theta*rec.normal[2];
        normal[2] = -sin_theta*rec.normal[0] + cos_theta*rec.normal[2];

        rec.p = p;
        rec.normal = normal;

        return true;
    }
};

运用平移和旋转,渲染出的cornell场景如下:

在这里插入图片描述

体积物体

这一部分将的是对于类似雾这种对象的光线追踪渲染,不过为了简便,文中只举例了密度恒定,且边界不变的情况。

光线在射向这类物体时,有概率直接穿透,也有概率散射开来。

在这里插入图片描述

对于这样的物体,文中主要设置了两种参数,一个是密度,另一个是边界。

在做射线求交运算时,会设置计算两个碰撞点,分别代表射入和射出的点,并根据密度参数和随机值,计算得出一个新的碰撞点,用来做计算。如果这个点不在体积内,则判定这个射线没有和物体相交,如果在体积内,则判定为相交,进行计算。

判定相交时,射入的光线会在碰撞点处随机散射。

这一步的渲染效果:在这里插入图片描述

最终效果

和上一部教程一样,作者给出了一个大场景,渲染出一个最终效果。

这里我为了能够尽快得出渲染结果,将采样次数降低,牺牲了质量,此时的渲染结果如下:

在这里插入图片描述

完整代码

这一次同样上传在网盘上:

链接:https://pan.baidu.com/s/1hspYR2VGNlxynRJYa9jHug?pwd=qyfj
提取码:qyfj
–来自百度网盘超级会员V6的分享

ps: 只分享了源码,没有什么依赖库,应该可以直接跑出ppm格式的图片。

参考

https://www.cnblogs.com/wickedpriest/p/12269564.html

https://baike.baidu.com/item/%E5%8C%85%E5%9B%B4%E7%9B%92/4562345

https://blog.csdn.net/weixin_44176696/article/details/118655688

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

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

相关文章

SOA架构介绍与简单代码示例

1.SOA架构介绍 SOA (Service Oriented Architecture)“面向服务的架构":是一种设计方法&#xff0c;其中包含多个服务&#xff0c;服务之间通过相互依赖最终提供一系列的功能。一个服务通常以独立的形式存在与操作系统进程中。各个服务之间通过网络调用。 微服务架构80%…

开集目标检测-标签提示目标检测大模型(吊打YOLO系列-自动化检测标注)

背景 大多数现有的对象检测模型都经过训练来识别一组有限的预先确定的类别。将新类添加到可识别对象列表中需要收集和标记新数据&#xff0c;并从头开始重新训练模型&#xff0c;这是一个耗时且昂贵的过程。该大模型的目标是开发一个强大的系统来检测由人类语言输入指定的任意…

深入了解Python中文件IO的使用技巧,提高代码处理效率!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Python提供了强大而灵活的文件I/O&#xff08;输入/输出&#xff09;工具&#xff0c;能够读取、写入和处理各种文件类型。本文将深入介绍Python文件I/O的技巧和示例代码&#xff0c;帮助大家更好地理解如何在Py…

Swift 周报 第四十二期

文章目录 前言新闻和社区苹果 CEO 库克透露接班计划&#xff0c;希望继任者来自公司内部消息称苹果自研 5G 调制解调器开发再“难产”&#xff0c;将推迟到 2026 年 提案正在审查的提案 Swift论坛推荐博文话题讨论关于我们 前言 本期是 Swift 编辑组整理周报的第四十二期&…

【Tensor张量】AI模型的信息流通货币

官方解释https://www.tensorflow.org/guide/tensor?hl=zh-cn 1.Tensor的概念理解 如果大模型是一个会运行的城市工厂,那么Tensor就是 运输车! 如果大模型是计算机,那么Tensor就是硬盘。 负责深度学习数据的运输和存储!把数据送给AI模型进行训练,AI模型推理后的数据也…

机器学习中的强学习器:AdaBoost算法详解

目录 1. 引言 2. AdaBoost的基本概念 2.1 弱学习器 2.2 错误率与权重更新 3. AdaBoost的工作流程 3.1 初始化权重 3.2 训练弱学习器 3.3 更新样本权重 3.4 构建强学习器 4. AdaBoost的优缺点 4.1 优点 4.2 缺点 5. 应用场景 5.1 图像识别 5.2 语音处理 5.3 生物…

Hive安装笔记——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

将下发的ds_db01.sql数据库文件放置mysql中 12、编写Scala代码&#xff0c;使用Spark将MySQL的ds_db01库中表user_info的全量数据抽取到Hive的ods库中表user_info。字段名称、类型不变&#xff0c;同时添加静态分区&#xff0c;分区字段为etl_date&#xff0c;类型为String&am…

Python实现张万森下雪了的效果

写在前面 即将步入婚宴殿堂的女主林北星&#xff0c;遭遇了男友展宇的毁约&#xff0c;生活和工作也变得一团糟。与此同时&#xff0c;她被时光老人带回了十八岁的高三时光&#xff0c;重新开启了自己的人生。林北星摆脱了展宇的束缚&#xff0c;认真准备高考&#xff0c;想要…

【深度学习-目标检测】05 - YOLOv1 论文学习与总结

论文地址&#xff1a;You Only Look Once:Unified, Real-Time Object Detection 论文学习 1. 摘要 YOLO的提出&#xff1a;作者提出了YOLO&#xff0c;这是一种新的目标检测方法。与传统的目标检测方法不同&#xff0c;YOLO将目标检测视为一个回归问题&#xff0c;直接从图像…

【数据结构】六、树和二叉树

目录 一、树的基本概念 二、二叉树 2.1二叉树的性质 2.2二叉树的存储结构 2.3遍历二叉树 先序遍历 中序遍历 后序遍历 层次遍历 2.4二叉树的应用 计算叶子数 前序遍历建树 根据序列恢复二叉树 计算树的深度 判断完全二叉树 三、线索二叉树 3.1线索化 四、树和森林…

redis的搭建及应用(三)-Redis主从配置

Redis主从配置 为提升Redis的高可用性&#xff0c;需要搭建多个Redis集群以保证高可用性。常见搭建方式有&#xff1a;主从&#xff0c;哨兵集群等&#xff0c;本节我们搭建一主二从的多Redis架构。 redis主从安装1主2从的方式配置&#xff0c;以端口号为redis的主从文件夹。 主…

前缀和——OJ题(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、和为 k 的子数组1、题目讲解2、思路讲解3、代码实现 二、和可被 K 整除的⼦数组1、题目讲…

office bookmarks

Word2007Util.java-CSDN博客

Linux 安装 Tomcat

1、找到安装包 2、解压缩 tar -xzvf xxx.tar.gz Tomcat 很好安装&#xff0c;已经安装好了 3、启动、关闭Tomcat 进入解压后文件夹之后&#xff0c;两个文件分别是启动和关闭 sh startup.sh // 启动 sh shutdown.sh // 关闭 然后就行了 4、主机连接虚拟机Tomcat 注意这里填写…

使用 SSH 方式实现 Git 远程连接GitHub

git是目前世界上最先进的分布式版本控制系统&#xff0c;相比于SVN&#xff0c;分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在&#xff0c;也就是有没有联网都可以正常工作&#xff01;当有网络的时候&#xff0c;再把本地提交推送一下就完成了同步&…

HackTheBox - Medium - Linux - OnlyForYou

OnlyForYou OnlyForYou 是一台中等难度的 Linux 计算机&#xff0c;其特点是 Web 应用程序容易受到本地文件包含 &#xff08;LFI&#xff09; 的影响&#xff0c;该应用程序用于访问源代码&#xff0c;从而揭示盲目命令注入漏洞&#xff0c;从而导致目标系统上的 shell。该计…

智能优化算法应用:基于蜣螂算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜣螂算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜣螂算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜣螂算法4.实验参数设定5.算法结果6.参考文献7.MA…

数据结构和算法:二分法

文章目录 一&#xff1a;最基本的二分法使用else if收缩空间&#xff0c;思路更清晰防止数据太大溢出int的范围什么情况使用while(left < right)&#xff1f;为什么这个时候right nums.length - 1&#xff1f;什么情况使用while(left < right)&#xff1f;为什么这个时候…

Docker 概念介绍

1、Docker 简介 Docker一个快速交付应用、运行应用的技术: 可以将程序及其依赖、运行环境一起打包为一个镜像&#xff0c;可以迁移到任意Linux操作系统运行时利用沙箱机制形成隔离容器&#xff0c;各个应用互不干扰启动、移除都可以通过一行命令完成&#xff0c;方便快捷 Doc…

RustDesk连接客户端提示key不匹配 Key Mismatch无法连接(已解决)

环境: RustDesk1.1.9 服务端docker部署 问题描述: RustDesk连接客户端提示key不匹配 Key Mismatch无法连接 解决方案: 1.docker部署RustDesk服务检查配置 networks:rustdesk-net:external: falsevolumes:hbbr:hbbs:services:hbbs:container_name: rustdesk-hbbsport…