利用SAH实现kD树快速分割模型实践_weixin_33884611的博客-程序员宝宝

技术标签: 数据结构与算法  c/c++  

前言:简单的数据结构的尝试,主要是为研究RTRT准备,折腾了不少时间。从我C++博客转过来的,希望大伙觉得有些价值。

    kD 树是二叉树结构的一个变种,当前主要用于加速光纤跟踪的遍历过程。最简单的排序二叉树以各个元素的大小关系作为分割点,而 kD 树简而言之就是从数据中选择一个“维度”构造一个超平面对数据集进行分割。比如要对学生数据进行分割,找出哪些学生的生日小于 2 18 日,那么就只要遍历整个集合,把所有的数据分成。如果又要在符合第一次条件的学生中找出哪些学生是来自南京的,那么就只要再次进行比较规划就可以办到了。 kD 树的带来的性能提升很高,让我们看一下常用加速结构的复杂度 [1] ,从 O(N) O(logN)

Bounding Volumn Hierarchy
Grid
Octree
(包括 Quadtree
kD Tree

  一般来说,构造 kD 树的方式有两种:排序和扫描。我们参考了一些加速构造 kD 树的方法,如采样,再结合实际的 3D 模型来为大家说明具体的操作方法。

基本概念:

  由于 kD 树主要用于光线跟踪等 3D 图形学领域,所以很有必要把光线跟踪的基本方法弄清楚。我们知道场景都是由三角形组成,整个空间的三角形组成了模型的拓扑结构。传统的光栅化过程就是把所有变换过的三角形投射到屏幕上,也就是写入帧缓冲。而光线跟踪的最初概念来自于绘画。 15 世纪意大利 Albrecht Dűrer 发明了网格作画的方法,也就是在待画的物体前放一张同画布一样大小的纸质半透明网格,然后依次透过每个小格看物体,绘制那个小格子的图画,当把所有的小格子都画满了后,一幅画也就画好了。

  我们由此可以想到暴力测试法,也就是从一个网格里发出光线后,检测场景中每个三角形,然后确定点亮那个网格(像素)的是什么颜色。 kD 树可以大幅度加速这个过程,类似于二叉树的遍历。

准备材料:

  在真实的实现过程中,我们总是要面对许多模型的格式,比如 D3D X 格式, QUAKE 系列所使用的 MD3/5 模型格式,以及诸多初学者所头疼的 3DS ASE 格式。需要补充的是,如果牵涉到在场景中使用骨骼动画,那么只有选择 MD5 或者 X 格式, 3DS/ASE 文件中没有记录骨骼动画所需要的权重节点等数据。

  对于静态模型来说,主要数据包括:顶点数据,三角形索引,纹理坐标,顶点向量(有可能没有)。绘制模型的时候,用三角形索引来寻址,依次抽取顶点数组中的数据进行绘制。为了缩短开发周期,我们使用了来自 sourceforge.net lib3ds[2] 开源库来处理 3DS 模型。如图:

Suiren.PNG

  这个 Hanana 模型由 10955 个顶点, 16221 个面(三角形)组成。我们用它来生成我们所需要的 kD 树加速结构。

思路优先:

  我们的 kD 树的节点模型究竟应该是什么样子的呢?让我们看一下定义:

struct  kDInner{
unsigned 
int  flagDimAndOffset;
//  bits 0..1 : 0 1 2代表x y z维
//  bits 2..30 : offset to left child
//  bit 31 : 是否是leaf
float  splitCoordinates;
};

struct  kDLeaf{
unsigned 
int  flagAndOffset;
//  bits 0..30 : 在三角形数组组中的偏移量
//  bit 31 : 是否是leaf
unsigned  int  objectCount; //  三角形树的数目
};

typedef union{
kDLeaf leaf;
kDInner inner;
}kDNode; 
// 共用体,统inner和leaf统称为为Node

 

    事实上,这样一上来就如此的精简并不方便构建kD树,我们先用普通的容易理解的定义表现一个Node

union offsetorsplitpos{
    
float  SplitPos;
    
ulong  AbsoluteOffset;
};

struct  KDNode
{
    KDNode
*  Left;
    KDNode
*  Right;
    offsetorsplitpos tag;
    
ulong  Count;//当这个节电作为Inner使用的时候,Count储存了Dimsion
};

 

  每个三角形的 AABB(Axis Aligned Bounding Box 即轴对称绑定盒 ) 也是必不可少的结构。 AABB 就很简单了,可以定义成这样:

struct AABB{
float min[3],max[3];
};

  我们之所以要把每个三角形都打散,是为了计算合适的 Split Plane 位置,你可以想象为从什么地方把场景分成两半,也就是把一堆三角形分成两部分,代码级的思路就是,计算合适的数组偏移量。那么什么叫做合适的呢?请看下面的这个公式 [3]

SAHequa.PNG

    看似好像很复杂,其实可以化简为这样的公式:
SAHequaez.PNG

在这里, Ct 指遍历整个节点包含的三角形遍历时间,也就是渲染这一簇三角形的时间; Cl Split Plane“ 左边”的三角形遍历时间,同理 Cr 是“右边”的遍历时间。引用的一些资料认为,

  事实上, Cost(x) 是两个单调函数 Cl(x) Cr(x) 的线性叠加。我们要尽量找到 Cost(x) 最小的某个空间位置,或者是某一个三角形边界,进而分割场景三角形。

  显然,这肯定由三步组成:

1、选择合适的 Split Plane ,可以有三个维度,并且可以容易推断总共有 2n

2、数数看左边有多少个 AABBs

3、数数看右边有多少个 AABBs

  下面我们开始讨论 2 种方法:排序与扫描。

Sort 排序

  排序很容易理解。沿着轴将所有三角形的 AABB 排序(也就是对三角形索引数组进行排序)。排序后就可以直接把想要得位置和三角形数目带入公式计算,如果绘制为函数图像可以获得最光滑连续的图形。总花费为 O(nlogn)

Scan 扫描

  扫描有些罗嗦,不过也很容易实现,最重要的是目前已经有成功的应用 [4] 。指定场景中的一个平面,遍历三角形比较大小,记录落在左边和右边的数目,再带入公式。如果要获得多个 Cost 数值,就必须要选择多个位置进行计算。每次都要遍历全部的三角形。经过处理后也可以达到 O(nlogn) 的复杂度,不过结合了采样技术后可以获得近似准确的结果,而且花费小的多。而且,这个过程是可以使用 SIMD 加速的。

  还有一些技巧和疑问 [3]

1、是否总是计算三个轴中最小的那个位置,还是总是从一个轴计算?

2、树需要细分到何种程度?每个 Leaf 需要有多少个三角形?

  现在我们可以做进一步的推测,如果希望用光线跟踪一个动态的场景,那么每当我们变换过矩阵后,都需要重新构造一次 kD 树,所以为了达到实时交互式的速度,必须要对关键的步骤进行优化。而且生成树的质量与遍历的性能关系十分密切。

  我们的思路有了,下面可以构思具体的实现过程

A. 准备三角形索引数组 Tris[]
B.
获得整个场景的 AABB V ,其中 V.min[0] 就是场景的左边界,以此类推。
C.
通过三角形索引数组读取 3 个顶点,构造每个三角形的 AABB ,储存到一个容器中 vector<AABB> AABBs ,同时记录 AABB 究竟属于哪个三角形。
D.
获得三角形 AABBs 的数目 N ,如果大于一个数值比如 1024 就用步骤 FG 计算 3 次,否则只计算一次。获得 AABBs 的开头和末尾指针(迭代器)。
E.
如果选择 Sort ,那么就要先对 AABBs 中的元素排序。这里我们采用 Scan
F.
(min[AXIS],max[AXIS]) 之间找几个固定平面,比如 8 个。 AXIS 指选择的坐标轴。
H.
带入公式计算,选择 Cost 最小的 Split Position ,生成 Node 。如果已经达到中止条件,那么就生成 Leaf Node ,否则生成 Inner Node 。把 v Split Position 分成两个 Voxel Vl Vr 。把 AABBs 分割为两个,从与 Split Position 最近的三角形开始。
G.
遍历 AABBs 容器,比较平面和每个元素的位置,记录 8 对数据。
I.
带入 Vl Vr 分别从 D 开始迭代。

    下面是分隔这个模型的输出信息:

TriNum :  16220
Left Voxel has : 
12377   Right Voxel has :  384
Left Voxel has : 
2912    Right Voxel has :  946
Left Voxel has : 
1025    Right Voxel has :  188
Left Voxel has : 
415     Right Voxel has :  610
Left Voxel has : 
128     Right Voxel has :  287
Left Voxel has : 
68      Right Voxel has :  60
Leaf LEVEL : 
5   Count :  128
Left Voxel has : 
145     Right Voxel has :  142
Left Voxel has : 
77      Right Voxel has :  68
Leaf LEVEL : 
6   Count :  145
Left Voxel has : 
61      Right Voxel has :  81
Leaf LEVEL : 
6   Count :  142
Left Voxel has : 
218     Right Voxel has :  392
Left Voxel has : 
97      Right Voxel has :  121
Leaf LEVEL : 
5   Count :  218
Left Voxel has : 
211     Right Voxel has :  181
Left Voxel has : 
114     Right Voxel has :  97
Leaf LEVEL : 
6   Count :  211
Left Voxel has : 
82      Right Voxel has :  99
Leaf LEVEL : 
6   Count :  181
Left Voxel has : 
772     Right Voxel has :  111
Left Voxel has : 
373     Right Voxel has :  399
Left Voxel has : 
194     Right Voxel has :  179
Left Voxel has : 
82      Right Voxel has :  112
Leaf LEVEL : 
6   Count :  194
Left Voxel has : 
111     Right Voxel has :  68
Leaf LEVEL : 
6   Count :  179
Left Voxel has : 
190     Right Voxel has :  209
Left Voxel has : 
99      Right Voxel has :  91
Leaf LEVEL : 
6   Count :  190
Left Voxel has : 
63      Right Voxel has :  146
Left Voxel has : 
516     Right Voxel has :  599
Left Voxel has : 
246     Right Voxel has :  270
Left Voxel has : 
124     Right Voxel has :  122
Leaf LEVEL : 
6   Count :  246
Left Voxel has : 
126     Right Voxel has :  144
Left Voxel has : 
285     Right Voxel has :  314
Left Voxel has : 
150     Right Voxel has :  135
Left Voxel has : 
152     Right Voxel has :  162
Left Voxel has : 
4180    Right Voxel has :  528
Left Voxel has : 
2220    Right Voxel has :  196
Left Voxel has : 
1211    Right Voxel has :  100
Left Voxel has : 
502     Right Voxel has :  709
Left Voxel has : 
235     Right Voxel has :  267
Left Voxel has : 
343     Right Voxel has :  366
Left Voxel has : 
471     Right Voxel has :  538
Left Voxel has : 
201     Right Voxel has :  270
Left Voxel has : 
263     Right Voxel has :  275
Left Voxel has : 
924     Right Voxel has :  103
Left Voxel has : 
428     Right Voxel has :  496
Left Voxel has : 
184     Right Voxel has :  244
Left Voxel has : 
286     Right Voxel has :  210
Left Voxel has : 
440     Right Voxel has :  596
Left Voxel has : 
211     Right Voxel has :  229
Left Voxel has : 
310     Right Voxel has :  286
Left Voxel has : 
2437    Right Voxel has :  284
Left Voxel has : 
1228    Right Voxel has :  120
Left Voxel has : 
558     Right Voxel has :  670
Left Voxel has : 
303     Right Voxel has :  255
Left Voxel has : 
298     Right Voxel has :  372
Left Voxel has : 
578     Right Voxel has :  631
Left Voxel has : 
295     Right Voxel has :  283
Left Voxel has : 
302     Right Voxel has :  329
Left Voxel has : 
1466    Right Voxel has :  138
Left Voxel has : 
805     Right Voxel has :  661
Left Voxel has : 
430     Right Voxel has :  375
Left Voxel has : 
346     Right Voxel has :  315
Left Voxel has : 
653     Right Voxel has :  729
Left Voxel has : 
347     Right Voxel has :  306
Left Voxel has : 
367     Right Voxel has :  362
Left Voxel has : 
2319    Right Voxel has :  152
Left Voxel has : 
1413    Right Voxel has :  906
Left Voxel has : 
825     Right Voxel has :  588
Left Voxel has : 
437     Right Voxel has :  388
Left Voxel has : 
180     Right Voxel has :  257
Left Voxel has : 
98      Right Voxel has :  82
Leaf LEVEL : 
6   Count :  180
Left Voxel has : 
150     Right Voxel has :  107
Left Voxel has : 
191     Right Voxel has :  197
Left Voxel has : 
90      Right Voxel has :  101
Leaf LEVEL : 
6   Count :  191
Left Voxel has : 
68      Right Voxel has :  129
Left Voxel has : 
294     Right Voxel has :  294
Left Voxel has : 
150     Right Voxel has :  144
Left Voxel has : 
91      Right Voxel has :  59
Leaf LEVEL : 
6   Count :  150
Left Voxel has : 
51      Right Voxel has :  93
Leaf LEVEL : 
6   Count :  144
Left Voxel has : 
153     Right Voxel has :  141
Left Voxel has : 
105     Right Voxel has :  48
Leaf LEVEL : 
6   Count :  153
Left Voxel has : 
49      Right Voxel has :  92
Leaf LEVEL : 
6   Count :  141
Left Voxel has : 
457     Right Voxel has :  449
Left Voxel has : 
214     Right Voxel has :  243
Left Voxel has : 
124     Right Voxel has :  90
Leaf LEVEL : 
5   Count :  214
Left Voxel has : 
102     Right Voxel has :  141
Left Voxel has : 
79      Right Voxel has :  62
Leaf LEVEL : 
6   Count :  141
Left Voxel has : 
196     Right Voxel has :  253
Left Voxel has : 
99      Right Voxel has :  97
Leaf LEVEL : 
5   Count :  196
Left Voxel has : 
133     Right Voxel has :  120
Left Voxel has : 
56      Right Voxel has :  77
Leaf LEVEL : 
6   Count :  133
Left Voxel has : 
791     Right Voxel has :  733
Left Voxel has : 
462     Right Voxel has :  329
Left Voxel has : 
228     Right Voxel has :  234
Left Voxel has : 
129     Right Voxel has :  99
Left Voxel has : 
64      Right Voxel has :  65
Leaf LEVEL : 
6   Count :  129
Left Voxel has : 
117     Right Voxel has :  117
Leaf LEVEL : 
5   Count :  234
Left Voxel has : 
171     Right Voxel has :  158
Left Voxel has : 
90      Right Voxel has :  81
Leaf LEVEL : 
5   Count :  171
Left Voxel has : 
88      Right Voxel has :  70
Leaf LEVEL : 
5   Count :  158
Left Voxel has : 
423     Right Voxel has :  310
Left Voxel has : 
205     Right Voxel has :  218
Left Voxel has : 
114     Right Voxel has :  91
Leaf LEVEL : 
5   Count :  205
Left Voxel has : 
112     Right Voxel has :  106
Leaf LEVEL : 
5   Count :  218
Left Voxel has : 
177     Right Voxel has :  133
Left Voxel has : 
86      Right Voxel has :  91
Leaf LEVEL : 
5   Count :  177
Left Voxel has : 
77      Right Voxel has :  56
Leaf LEVEL : 
5   Count :  133

 

  其中, C 可以用 foreach H 可以用 partition stable_partition )等 STL 的算法。

核心的代码如下:


void  Count(AABB *  BeginItr,AABB *  EndItr,AXIS Axis, float  SplitPos, ulong *  Cl, ulong *  Cr)
{
    
for ( AABB *  itr  =  BeginItr ; itr  !=  EndItr; itr ++  ){
        (
* Cl) += ( (itr -> max[Axis]  <  SplitPos )  ?   1  :  0  );
        (
* Cr) += ( (itr -> min[Axis]  >  SplitPos )  ?   1  :  0  );
    }
};

 

inline  bool  Terminate( ulong  TriCount, int  Level)
{
    
if  ( TriCount  <   128   ||  Level  >  MAX_LEVEL){
        
return   true ;
    }
    
return   false ;
};

 


inline  bool  IsUpboundSmaller(AABB &  AABB)
{
    
float  half_width  =  ( AABB.max[::gSplitAxis]  -  AABB.min[::gSplitAxis] ) * 0.5f ;
    
return  (AABB.max[::gSplitAxis]  -  ::gSplitPos)  <  half_width  ?   true  :  false ;
};

float  SAHCost(AXIS Axis, ulong *  Cl, ulong *  Cr,AABB &  V, float  SplitPos)
{
    
ulong  Nl  =   * Cl;
    
ulong  Nr  =   * Cr;
    
float  Length  =  V.max[Axis]  -  V.min[Axis];
    
float  L  =  SplitPos  -  V.min[Axis],R  =  SplitPos  -  V.max[Axis];
    
float  Pl  =  L  /  Length;
    
float  Pr  =   1.0f   -  Pl;
    
float  Tt  =  TRAVERSAL_TIME;
     float  Til  =  Nl  *  INTERSECTION_TIME,Tir  =  Nr  *  INTERSECTION_TIME;
    
return  Tt  +  Pl * Nl * Til  +  Pr * Nr * Tir;

};

 


void  KDTreeBuild(KDNode *  Root,AABB V,AABB *  BeginItr,AABB *  EndItr,AXIS Axis, int  Level)
{
    
ulong  TotalTriCount  =  EndItr  -  BeginItr;
    
if ( Terminate(TotalTriCount,Level) )
        
return ;

    
float  SplitLoc[ 7 ];
    
float  Cost[ 7 ];

    
ulong  Cl  =   0 ,Cr  =   0 ;
    
float  MinLoc  =  V.min[Axis],MaxLoc  =  V.max[Axis];
    
float  step  =  (MaxLoc  -  MinLoc)  /   8.0f ;

    
for int  i  =   0 ;i < 7  ;i ++  ){
        SplitLoc[i] 
=  MinLoc  +  step  *   float (i + 1 );
        Count(BeginItr,EndItr,Axis,SplitLoc[i],
& Cl, & Cr);
        Cost[i] 
=  SAHCost(Axis, & Cl, & Cr,V,SplitLoc[i]);
        Cl 
=  Cr  =   0 ;
    }
    
float *  pGoodCostPtr  =  min_element(Cost,Cost + 7 );
    size_t _Pos 
=  pGoodCostPtr  -  Cost;

    ::gSplitPos 
=  SplitLoc[_Pos];
    ::gSplitAxis 
=  Axis;

    AABB
*  MidItr  =  stable_partition(BeginItr,EndItr,IsUpboundSmaller);

    Cl 
=  MidItr  -  BeginItr;
    Cr 
=  EndItr  -  MidItr;

    cout
<< " Left Voxel has :  " << Cl << " \tRight Voxel has :  " << Cr << endl;

    AABB Vl,Vr;

    
if  ( Cr  >   128   ||  Cl  >   128  ){
        Root 
=   new  KDNode;  // Root

        Root
-> Left  =   new  KDNode;
        Root
-> Right  =   new  KDNode;

        Root
-> tag.SplitPos  =  ::gSplitPos;
        Root
-> Count  =  Axis; // Ulong.MaxSize - Axis 代表坐标 因为Left Right肯定不是0
         switch (Axis){
            
case  X :
                Vl.min[
0 =  V.min[ 0 ];
                Vl.min[
1 =  V.min[ 1 ];
                Vl.min[
2 =  V.min[ 2 ];
                Vl.max[
0 =  ::gSplitPos;
                Vl.max[
1 =  V.max[ 1 ];
                Vl.max[
2 =  V.max[ 2 ];

                Vr.min[
0 =  ::gSplitPos;
                Vr.min[
1 =  V.min[ 1 ];
                Vr.min[
2 =  V.min[ 2 ];
                Vr.max[
0 =  V.max[ 0 ];
                Vr.max[
1 =  V.max[ 1 ];
                Vr.max[
2 =  V.max[ 2 ];
                
break ;
            
case  Y :
                Vl.min[
0 =  V.min[ 0 ];
                Vl.min[
1 =  V.min[ 1 ];
                Vl.min[
2 =  V.min[ 2 ];
                Vl.max[
0 =  V.max[ 0 ];
                Vl.max[
1 =  ::gSplitPos;
                Vl.max[
2 =  V.max[ 2 ];

                Vr.min[
0 =  V.min[ 0 ];
                Vr.min[
1 =  ::gSplitPos;
                Vr.min[
2 =  V.min[ 2 ];
                Vr.max[
0 =  V.max[ 0 ];
                Vr.max[
1 =  V.max[ 1 ];
                Vr.max[
2 =  V.max[ 2 ];
                
break ;
            
case  Z :
                Vl.min[
0 =  V.min[ 0 ];
                Vl.min[
1 =  V.min[ 1 ];
                Vl.min[
2 =  V.min[ 2 ];
                Vl.max[
0 =  V.max[ 0 ];
                Vl.max[
1 =  V.max[ 1 ];
                Vl.max[
2 =  ::gSplitPos;

                Vr.min[
0 =  V.min[ 0 ];
                Vr.min[
1 =  V.min[ 1 ];
                Vr.min[
2 =  ::gSplitPos;
                Vr.max[
0 =  V.max[ 0 ];
                Vr.max[
1 =  V.max[ 1 ];
                Vr.max[
2 =  V.max[ 2 ];
                
break ;
        }
    }
else {
        Root 
=   new  KDNode;  // Leaf
        Root -> Left  =   0 ;Root -> Right  =   0 ;
        Root
-> tag.AbsoluteOffset  =  BeginItr  -  ::gScenePtr;
        Root
-> Count  =  EndItr  -  BeginItr;
        //cout
<< " Leaf LEVEL :  " << Level << " \tCount :  " << Root -> Count << endl;
        
return ;
    }
    
++ Level;
    KDTreeBuild(Root
-> Left,Vl,BeginItr,MidItr,Axis,Level);
    KDTreeBuild(Root
-> Right,Vr,MidItr,EndItr,Axis,Level);
};

 

    这样,我们就得到了一颗完整的基于SAH的KD树。下面就是遍历。

  由于不同渲染要求的不同,遍历代码也大不相同。不过我们可以知道,kD树及适用于光线跟踪程序。目前在多款开源光线跟踪器,比如blender就采用了kD树作为加速结构。而且必须要提到的是,如果希望在Realtime Rendering的程序中使用光线跟踪技术 —— 虽然说在目前的情况下还不是很现实,不过这毕竟是个趋势,因为只有光线跟踪才能够精确的模拟物理全局光照,光栅化系统先天限制无法达到,虽然说Voxel渲染是一个折中的办法。我引用下面的这张表格[3]作为性能参照。

kdresult.PNG

  可是这里又会出现一个问题,就是在实时渲染程序中如何处理多纹理贴图?我们必须要模拟管线的处理过程。

A、如果场景中有运动的模型,标记出来

B、在世界坐标系统中对运动模型作矩阵变换,类似于OpenGL的MATRIX操作

C、把所有,各就各位的模型顶点、向量、纹理坐标、索引整合在一起。每当在这个链表中加入新的模型的时候,都要把现在模型的所有索引加上链表中已经加入的所有顶点的数目,构建一个好像是整体连续的单个模型。同时标记纹理,应该储存一个顶点偏移量以及三角形数目,用来标示这个纹理属于哪个模型。

D、根据视点,遍历kD树,把符合条件的三角形数目放入一个全局队列

E、渲染队列中的所有三角形,作为一个Frame。重复A。

  目前由德国萨尔大学计算机科学系所开发的实时光线跟踪硬件与软件已经成功的问世,最令人振奋的莫过于表现了一个全部由光线跟踪引擎实现的Quake3游戏。有兴趣的朋友可以去[4]看看。

[1]Computer Graphics WS05/06 – kD-Tree and Optimization for Ray Tracing
[2]Lib3ds http://lib3ds.sf.net/
[3]Fast kd-tree Construction with an Adaptive Error-Bounded Heuristic

[4]OpenRT http://www.openrt.de/
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33884611/article/details/86033547

智能推荐

Matlab日记#_matlab日志_秋海鱼的博客-程序员宝宝

这里写自定义目录标题一、入门概述matlab通用命令二、基础知识数据类型基本矩阵操作功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入一、入门概述matlab通用命令1.常用命令:clc、clear、disp(显示变量或文字内容

hibernate-SessionFactory详解_hibernate sessionfactory_张晓鉴Oliver的博客-程序员宝宝

坚持每天写博文,积累下开发中的点点滴滴 Configuration:用于拿到SessionFactory; 重要方法:addClass(domain.class):添加映射,一般不用 configure()用于实例化Configuration;SessionFactory:主要用于创建session.SessionFactory对象中保存了当前数据库配置信息和所有映射关系及预定义的sql语

合泰单片机 | HT66F3195 | 个人库开发过程 | 【1】前言_合泰单片机烧录教程_买糖给姐姐吃的博客-程序员宝宝

前言先把最重要的几个点说一下:这个不是什么新手教程,只是个人笔记,很多地方不可能交待得很清楚的。有问题可以私信,及时回复的话那是不可能的哈,太难的内容最好不要问,因为我也不会

php解决锚点定位不准确,解决网页中使用Bootstrap固定导航时锚点偏移问题_皮肤科老徐医生的博客-程序员宝宝

用两种方法解决网页中使用Bootstrap固定导航时锚点偏移问题,这个问是在响应式布局比较常见,希望本文能够帮助您解决固定民航锚点偏移。使用Bootstrap布局了一个网页使其支持响应式布局,并且将导航菜单固定住,这样做 Categories 和 Tags 页面点击某一个分类或者标签链接时, 锚点定位必然定位于页面顶部,这样一来就会被固定住的导航遮挡 ,例如,我在Categories页面,点击 h...

SpringBoot整合JDBC连接数据库错误常见问题_springboot jdbc连接失败_Gopher陈的博客-程序员宝宝

1、连接数据库1)首选配置application.yaml文件2)连接数据库2)有设置时区和没设置的区别所以在连接数据库的时候,一定要在配置文件中加上serverTimezone=UTC,还有别忘记开启mysql服务!新手发稿,有错误的地方望大佬指正。...

TestNG+Maven+IDEA环境搭建_testng+idea_LangSand的博客-程序员宝宝

TestNG+Maven+IDEA环境搭建前言:主要进行TestNG测试环境的搭建

随便推点

confluence使用宏时乱码_confluence 宏 乱码_R.oya的博客-程序员宝宝

confluence在使用宏时,出现中文乱码。解决办法1、在linux上安装中文字体使用命令fc-list :lang=zh查看是否已安装中文字体fc-list :lang=zh将windows上的字体直接拷贝至linux的/usr/share/fonts,在该路径下新建目录,自定义名称即可,我创建的为msttfonts,将新增字体拷贝到该目录下。建立字体索引信息,更新字体缓存。m...

Zabbix通过SNMP监控OpenSIS状态_云梦谭的博客-程序员宝宝

线上Server的状态监控至关重要。OpenSIPS/Kamailio提供了很多接口可以利用。这里介绍怎么配置SNMP监控。1. 依赖关系 OpenSIPS实现了一个叫snmpstats的模块。它可以通过SNMP汇报OpenSIPS的运行状态。使用snmpstats模块,必须先加载usrloc模块和dialog模块。 此外,必须安装NETSNMP 5.3从上版本。CentOS/Redhat安装方法yum installnet-snmp-deve...

jmeter计数器与循环实现参数的遍历-优化1_jmeter计数器和循环_Iris_3的博客-程序员宝宝

问题背景:此接口两个日期只能间隔一天,如果间隔多天有性能问题,研发还没处理需要补前半年的数据,如果手动填参数,会相当慢优化1:用计数器加循环实现遍历,但是只能每月执行优化2:日期先输入到csv中,从csv中读取日期参数,进行执行1.线程组设置循环次数,每循环一次,计数器那边加一设置计数器,初始值20220201,每次递增1使用计数器${c_fromDate}引用运行结果:fromDate=20220201&amp;toDate=20220202

桌面计算机图标不能重命名,桌面上快捷图标重命名对程序有影响吗_weixin_40009393的博客-程序员宝宝

2014-07-19如何将打开方式中的windows的图标改为原来的图?用户曾试过错误地把LNK文件的打开方式更改其他文件,导致系统所有的快捷方式都失效,对于Windows vista\Windows 7 操作系统解决方法如下:1、首先 win+r2、打开运行程序3、输入: regedit4、找到: 计算机\HKEY_CURRENT_USER\SOFTWARE\MICROSOFT\WINDOWS\...

yarnui 和 sparkui的总结 和配置_yarn ui spark_文武书生的博客-程序员宝宝

https://developer.aliyun.com/article/706215https://www.cnblogs.com/30go/p/9911492.html

问题 1437: [蓝桥杯][历届试题]城市建设_温姑娘的博客-程序员宝宝

解题思路:该题让求图的最小生成树是无疑的。不过对最小生成树进行了变形。首先两点间可以通过修路,也可通过建码头来建立联系。就好像航电的引水工程一样,一个点要想喝水,可以自己打井,也可以从别处饮水。当然和这个题目有所不同,引水工程是自己打井可满足自己也可满足别人,而该题,只有建港的人,才可相互到达,并不是一地建港就可到达他人的。根据引水工程的思路,同样可以可虑把河看成节点0.则可建港口的点都要像节点0...

推荐文章

热门文章

相关标签