2016-09-09 11:51:44 PDUnderstand 阅读数 2621

做了一个关于立体图形3D动画和绘制图形的例子,效果如下:

这个是参照苹果官方文档和例子来写的,其中茶壶是根据点、颜色渲染、网格结构和灯光效果来绘制出来的。

再说实现步骤前我们需要了解一下概念:

GLKView:作为OpenGLES内容的呈现目标

GLKViewController: 内容呈现的控制和动画,视图管理和维护一个framebuffer,应用只需在framebuffer进行绘画即可

GLKView 和GLKViewController类提供一个标准的OpenGLES视图和相关联的呈现循环

EAGLContext:实现和提供一个呈现环境

GLuint、GLfloat:其实就是typedef int和float的别名,当我们编写代码时不要被这些苹果的别名给吓到。

GLKTextureLoader:提供从iOS支持的各种图像格式的源自动加载纹理图像到OpenGLES 图像环境的方式,并能够进行适当的转换,并支持同步和异步加载方式

GLKMatrix4:一个unit共用体,是一个4*4的矩阵。

GLKBaseEffect:OpenGL ES 1.1规范中的关键的灯光和材料模式

好了,还有其他的概念性的东西可以下方留言,我将进行回答,也可以自己谷歌。

1.将所需系统库导入工程


2.创建一个GLKViewController类。

重写一个继承类,然后将view改为GLKView。

@interface PDTeapotViewController : GLKViewController
{
    EAGLContext *context;    // 实现和提供一个呈现环境
    GLuint mode;
    // 茶壶
    GLfloat rot;
    // 正方体
    GLfloat cubePos[3];
    GLfloat cubeRot;
    GLuint cubeTexture;
}

@property (nonatomic, strong) PDTeapotBaseEffect *innerCircle;
@property (nonatomic, strong) PDTeapotBaseEffect *outerCircle;
@property (nonatomic, strong) PDTeapotBaseEffect *teapot;
@property (nonatomic, strong) NSMutableArray *cubeEffectArr;
@property (nonatomic, strong) PDMusicCube *musicCube;

@end
并且分别在类中创建效果路径、茶壶、正方体、和控制背景音乐。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    // 创建环境失败,或者将当前线程环境设置失败
    if (!context || ![EAGLContext setCurrentContext:context]) {
        return;
    }
    GLKView *glView = (GLKView *)self.view;
    glView.context = context;
    glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
    
    mode = 1;
    glEnable(GL_DEPTH_TEST);
    
    // 创建效果路径
    self.innerCircle = [PDTeapotBaseEffect makeCircleWithNumOfSegments:circleSegments radius:pathCircleRadius];
    self.outerCircle = [PDTeapotBaseEffect makeCircleWithNumOfSegments:circleSegments radius:pathOutCircleRadius];
    
    // 创建茶壶
    self.teapot = [PDTeapotBaseEffect makeTeapot];
    
    // 创建正方体
    self.cubeEffectArr = [PDTeapotBaseEffect makeCube];
    
    [self setUpCubeEffect];
    
    [self setUpMusicCube];
}
3.创建一个材料和效果类

#import "PDTeapotBaseEffect.h"

用来创建图形

<span style="font-size:12px;">#define kTeapotScale		1.8
#define kCubeScale			0.12
#define kButtonScale		0.1

#define kButtonLeftSpace	1.1

#define	DegreesToRadians(x) ((x) * M_PI / 180.0)

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

static const CGFloat pathCircleRadius = 1.0;  // 运动路径内环
static const CGFloat pathOutCircleRadius = 1.1;  // 运动路径外环
static const GLuint circleSegments = 36;

// 效果类
@interface PDTeapotBaseEffect : NSObject

@property (nonatomic, strong) GLKBaseEffect *effect; // 效果类,灯光和材料模式效果
@property (nonatomic, assign) GLuint vertexArray;    // GLuint基础类型
@property (nonatomic, assign) GLuint vertexBuffer;
@property (nonatomic, assign) GLuint normalBuffer;


/**
 *  创建运动轨迹
 */
+ (instancetype)makeCircleWithNumOfSegments:(GLuint)segments radius:(GLfloat)radius;

/**
 *  创建茶壶
 */
+ (instancetype)makeTeapot;

/**
 *  创建正方体
 */
+ (NSMutableArray *)makeCube;

/**
 *  背景音乐的播放
 */
+ (void)musicBack;</span>


例如创建一个茶壶代码:

PDTeapotBaseEffect *teapot = [[PDTeapotBaseEffect alloc] init];
    GLKBaseEffect *effect = [[GLKBaseEffect alloc] init];
    // 材料
    effect.material.ambientColor = GLKVector4Make(0.4, 0.8, 0.4, 1.0);
    effect.material.diffuseColor = GLKVector4Make(1.0, 1.0, 1.0, 1.0);
    effect.material.specularColor = GLKVector4Make(1.0, 1.0, 1.0, 1.0);
    effect.material.shininess = 100.0;
    // 光
    effect.light0.enabled = GL_TRUE;
    effect.light0.ambientColor = GLKVector4Make(0.2, 0.2, 0.2, 1.0);
    effect.light0.diffuseColor = GLKVector4Make(0.2, 0.7, 0.2, 1.0);
    effect.light0.position = GLKVector4Make(0.0, 0.0, 1.0, 0.0);
    
    GLuint vertexArray, vertexBuffer, normalBuffer;
    
    glGenVertexArraysOES(1, &vertexArray);
    glBindVertexArrayOES(vertexArray);
    
    // 位置
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(teapot_vertices), teapot_vertices, GL_STATIC_DRAW);
    
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
    
    glGenBuffers(1, &normalBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, normalBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(teapot_normals), teapot_normals, GL_STATIC_DRAW);
    
    glEnableVertexAttribArray(GLKVertexAttribNormal);
    glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
    
    glBindVertexArrayOES(0);
    
    teapot.effect = effect;
    teapot.vertexArray = vertexArray;
    teapot.vertexBuffer = vertexBuffer;
    teapot.normalBuffer = normalBuffer;
    return teapot;

4.利用glkView:drawInRect:函数做绘制和重绘

关于glkView:drawInRect:我要解释一下:

使用GLKit视图绘制OpenGL内容需要三个子步骤:准备OpenGLES基础;发布绘制命令;呈现显示内容到Core Animation。       GLKit类本身已经实现了第一个和第三个步骤,用户只需实现第二个步骤,在视图的方法drawRect或视图的代理对象的glkView:drawInRect:中调用适当的OpenGLES绘制命令进行内容绘制。

代码如下(由于代码比较多,我将部分粘贴,其余部分参照我的github例子 ps:下载时给颗星最好了。。。。):

<span style="font-size:12px;">#pragma mark - 重绘
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0.0, 0, 0, 1.0);
    glClearDepthf(1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    GLfloat aspectRatio = (GLfloat)(view.drawableWidth) / (GLfloat)(view.drawableHeight);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(-1.0f, 1.0f, -1.0f/aspectRatio, 1.0f/aspectRatio, -10.0f, 10.0f);
    projectionMatrix = GLKMatrix4Rotate(projectionMatrix, DegreesToRadians(-30.0f), 0.0f, 1.0f, 0.0f);
    
    // set the projection matrix
    self.innerCircle.effect.transform.projectionMatrix = projectionMatrix;
    self.outerCircle.effect.transform.projectionMatrix = projectionMatrix;
    self.teapot.effect.transform.projectionMatrix = projectionMatrix;
    for (int f=0; f<6; f++)
        ((PDTeapotBaseEffect *)self.cubeEffectArr[f]).effect.transform.projectionMatrix = projectionMatrix;
    
    glBindVertexArrayOES(self.innerCircle.vertexArray);
    [self.innerCircle.effect prepareToDraw];
    glDrawArrays (GL_LINE_LOOP, 0, circleSegments);
    
    glBindVertexArrayOES(self.outerCircle.vertexArray);
    [self.outerCircle.effect prepareToDraw];
    glDrawArrays (GL_LINE_LOOP, 0, circleSegments);
    
    [self drawTeapotAndUpdatePlayback];
    
    [self drawCube];
}</span>
好了,关于整个项目其他不动的地方可以参照, 图像编程总结
下面是大家最想要的   github源码



2017-07-31 11:06:12 ZCMUCZX 阅读数 313

饼图

 NSArray * array=@[@0.25,@0.35,@0.3,@0.1];
    CGFloat start=0.0;
    CGFloat end=0.0;
    for(int i=0;i<array.count;i++)
    {
        //结束的位置等于起始的位置加大小
        end=2*M_PI*[array[i] floatValue]+start;
        UIBezierPath * path=[UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:100 startAngle:start endAngle:end clockwise:YES];
        //画一个从终点到圆心的线
        [path addLineToPoint:CGPointMake(100, 100)];
        [[UIColor colorWithRed:((float)arc4random_uniform(256)/255.0)green:((float)arc4random_uniform(256)/255.0) blue:((float)arc4random_uniform(256)/255.0) alpha:1] set];
        [path fill];
        //下一次的起点等于上一次的终点
        start=end;
    }



柱状图

  NSArray * array=@[@0.7,@1,@0.3,@0.5,@0.6];
    for(int i=0;i<array.count;i++)
    {
    CGFloat width=rect.size.width/((2*array.count)-1);
    CGFloat height=[array[i] floatValue]*rect.size.height;
    CGFloat x=i*2*width;
    CGFloat y=rect.size.height-height;
        UIBezierPath * path=[UIBezierPath bezierPathWithRect:CGRectMake(x, y, width, height)];
        //设置随机的颜色
     [[UIColor colorWithRed:((float)arc4random_uniform(256)/255.0)green:((float)arc4random_uniform(256)/255.0) blue:((float)arc4random_uniform(256)/255.0) alpha:1] set];
       //渲染
        [path fill];

2012-11-13 09:57:15 newjerryj 阅读数 1364

 1. ios提供了两种高质量的图形绘制方法:   a. OpenGL ES进行渲染     b.Quartz,Core Animation, UIKit进行绘制


2. OpenGL ES框架主要适用于游戏或者高帧率的应用程序开发。基于C语言的接口,用于创建2D和3D内容。


UIKit的图形系统

3.  iOS中所有的描画,无论OpenGL ES, Quartz, UIKit, 或者Core Animation 都发生在UIView对象的区域内.


4. UIView调用drawRect触发视图更新的操作有以下几种情况:  

a.  对遮挡视图的其他视图进行移除或者删除操作

b.  将hidden属性设置为NO, 使其从隐藏变为显示状态

c.  将试图滚出屏幕,然后重新回到屏幕

d. 显示的调用setNeedsDisplay或者setNeedsDisplayInRect



5. 提高描画性能的方法:

  a.  使重绘的区域最小

b. 尽可能将视图区域标识为不透明,  opaque设置为YES

c. 删除不透明的PNG文件中的alpha通道,  将不透明PNG文件的alpha通道删除可以避免对包含该图像的图层进行融合操作,从而很大程度简化了图像的合成,提高了描画性能。

d. 滚动过程中复用已有的视图

e. 避免滚动过程中清除原先的内容,在默认情况下,调用drawRect方法对视图的某个区域进行更新之前,UIKit会清除该区域的上下文缓冲区。滚动过程中如果清除缓冲区,那么代价很大, 可以将clearsContextBeforeDrawing属性设置为NO. 





参考资料:  《iPhone应用编程指南》— Apple Inc

2018-07-13 15:40:04 qq_37772356 阅读数 483

前言:做相关demo动画已经有一段时间了、现在闲下来记录下相关动画制作。iOS动画主要就是路径动画、帧动画、缩放动画、以及这些动画的组合叫组合动画。另外还有一些3D动画和转场动画。不想看原理的直接下载demo使用方式很简单。

摇杆(手柄)制作

首先需要绘制摇杆的范围我这画了一个圆形、然后监听用户的手势、根据手势移动的前后位置判断移动的方向上、下、左、右。此外如果你规定用户只能上下或者左右移动、不能斜角移动、你可以根据移动的位置判断当前点和中心点这条直线的斜率偏移不能超过多少度。
效果如下:
这里写图片描述

1.绘制背景边界圆

 CGContextRef context = UIGraphicsGetCurrentContext();
//     CGContextSetRGBFillColor (context,  1, 0, 0, 1.0);//设置填充颜色
    CGContextSetRGBStrokeColor(context,143/255.0,190/255.0,81/255.0,1.0);//画笔线的颜色
    CGContextSetLineWidth(context, 2.0);//线的宽度
    bigRadius = LL_mmWidth(self)/2.0-4;
    CGContextAddArc(context, LL_mmWidth(self)/2.0, LL_mmHeight(self)/2.0, bigRadius, 0, 2*PI, 0); //添加一个圆
    CGContextDrawPath(context, kCGPathStroke); //绘制路径

2.绘制中间摇杆圆和四个方向标志以及添加摇杆手势监听。

-(void)createCenterCircle:(CGRect)selfFrame
{
    //中间摇杆圆
    _centerView = [[UIView alloc]init];
    _centerView.frame = CGRectMake((CGRectGetWidth(selfFrame)-CGRectGetWidth(selfFrame)/3.0)/2.0, (CGRectGetHeight(selfFrame)-CGRectGetHeight(selfFrame)/3.0)/2.0, CGRectGetWidth(selfFrame)/3.0, CGRectGetHeight(selfFrame)/3.0);
    _handShankOriginCenter = _centerView.center;
    smallRadius = (CGRectGetWidth(selfFrame)/3.0)/2.0;
    _centerView.layer.cornerRadius = smallRadius;
    _centerView.layer.masksToBounds = YES;
    _centerView.alpha = 0.8;

    _centerView.backgroundColor = [UIColor colorWithRed:143/255.0 green:190/255.0 blue:81/255.0 alpha:1.0];
    [self addSubview:_centerView];
    //四个方向标志
    float interval = (CGRectGetHeight(selfFrame)-LL_mmHeight(_centerView))/4.0;
    for (int i = 0; i<4; i++) {
        UIImageView *arrow = [self InsertImageView:self cgrect:CGRectMake(0, 0, 20*FIT_WIDTH, 12*FIT_WIDTH) image:[UIImage imageNamed:self.handShankArrows[i]]];
//        InsertImageView(self, CGRectMake(0, 0, 20*FIT_WIDTH, 12*FIT_WIDTH), [UIImage imageNamed:self.handShankArrows[i]]);
//        arrow.hidden = YES;//注意隐藏箭头了
        switch (i) {
            case 0:
            {
                //上
                 arrow.center = CGPointMake(CGRectGetWidth(selfFrame)/2.0, interval);

            }
                break;
            case 1:
                //下
                arrow.center = CGPointMake(CGRectGetWidth(selfFrame)/2.0, CGRectGetHeight(selfFrame)-interval);
                break;
            case 2:
                //左
                arrow.frame = CGRectMake(0, 0, 12*FIT_WIDTH, 20*FIT_WIDTH);
                arrow.center = CGPointMake(interval, CGRectGetHeight(selfFrame)/2.0);
                break;
            case 3:
                //右
                arrow.frame = CGRectMake(0, 0, 12*FIT_WIDTH, 20*FIT_WIDTH);
                arrow.center = CGPointMake(CGRectGetWidth(selfFrame)-interval, CGRectGetHeight(selfFrame)/2.0);
                break;

            default:
                break;
        }



    }

    //摇杆添加移动手势
    UIPanGestureRecognizer *pangesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pangestureAction:)];
    [_centerView addGestureRecognizer:pangesture];
}

3.分析手势数据根据上一次移动的点和这次的做比较判断移动方向

 -(void)pangestureAction:(UIPanGestureRecognizer *)recognizer
{
    CGPoint translation = [recognizer translationInView:self];
    [recognizer setTranslation:CGPointZero inView:self];
    CGPoint movingPosition = CGPointMake(recognizer.view.center.x + translation.x,
                                    recognizer.view.center.y + translation.y);

    int rangeRadius = bigRadius - smallRadius;
    if ([self isInciclelPoint:movingPosition andR:rangeRadius centrePoing:_handShankOriginCenter]) {
        _lasrtPoint = movingPosition;
        _centerView.center = _lasrtPoint;

        if (_lasrtPoint.y < _startPoint.y && (fabsf((float)(_lasrtPoint.y - _startPoint.y)) > fabs(_lasrtPoint.x - _startPoint.x))) {
            //上
            if (_currentDirection == HandShankDirectionUnknow) {
                NSLog(@"开始上");
                _currentDirection = HandShankDirectionStartTop;
            }else{
                NSLog(@"上");
                _currentDirection = HandShankDirectionTop;
            }

        }else if (_lasrtPoint.y > _startPoint.y && (fabsf((float)(_lasrtPoint.y - _startPoint.y)) > fabs(_lasrtPoint.x - _startPoint.x)))
        {
            if (_currentDirection == HandShankDirectionUnknow) {
                NSLog(@"开始下");
                _currentDirection = HandShankDirectionStartDown;
            }else{
                NSLog(@"下");
                _currentDirection = HandShankDirectionDown;
            }

        }else if (_lasrtPoint.x >_startPoint.x && (fabsf((float)(_lasrtPoint.x -_startPoint.x)) > fabsf((float)(_lasrtPoint.y -_lasrtPoint.y))) )
        {
            if (_currentDirection == HandShankDirectionUnknow) {
                NSLog(@"开始右");
                 _currentDirection = HandShankDirectionStartRight;
            }else{
                NSLog(@"右");
                _currentDirection = HandShankDirectionRight;
            }

        }else if (_lasrtPoint.x <_startPoint.x && (fabsf((float)(_lasrtPoint.x -_startPoint.x)) > fabsf((float)(_lasrtPoint.y -_lasrtPoint.y))) )
        {
            if (_currentDirection == HandShankDirectionUnknow) {
                NSLog(@"开始左");
                _currentDirection = HandShankDirectionStartLeft;
            }else{
                NSLog(@"左");
                _currentDirection = HandShankDirectionLeft;
            }

        }

        if (self.delegete && [self.delegete respondsToSelector:@selector(handShankDirectionDidChange:)]) {
            [self.delegete handShankDirectionDidChange:_currentDirection];
        }
    }

    if(recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateFailed)
    {
        NSLog(@"结束");
        _startPoint = _handShankOriginCenter;
        [UIView animateWithDuration:0.5 animations:^{
            _centerView.center = _handShankOriginCenter;
        }];
        _currentDirection = HandShankDirectionUnknow;


        if (self.delegete && [self.delegete respondsToSelector:@selector(handShankDirectionDidEnd)]) {
            [self.delegete handShankDirectionDidEnd];
        }
    }



}

饼状图绘制

前言:饼状图由三角弧组成、弧线有起始角度和末尾角度组成、所以可以用一个mode类记录相关信息。然后绘制三角弧。效果如下:
这里写图片描述

1.弧线贝塞尔路径写法

-(UIBezierPath *)bezierPathStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle
{
    UIBezierPath *path = [UIBezierPath new];
    [path moveToPoint:_centerPoint];
    [path addArcWithCenter:_centerPoint radius:_radius startAngle:DEGREES_TO_RADIANS(startAngle) endAngle:DEGREES_TO_RADIANS(endAngle) clockwise:YES];
    [path closePath];
    return path;
}

2.生成弧线和对应的文字

-(void)createPieChartView
{
    [self.models enumerateObjectsUsingBlock:^(CurveModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        UIBezierPath *path = [self bezierPathStartAngle:obj.startAngel endAngle:obj.endAngel];
        CAShapeLayer *trangleLayer = [CAShapeLayer layer];
        //        maskLayer.backgroundColor = [UIColor purpleColor].CGColor;
        trangleLayer.path = [path CGPath];
        trangleLayer.fillColor = obj.color.CGColor;
        trangleLayer.fillRule = kCAFillRuleNonZero;//kCAFillRuleEvenOdd画的区域 取反  解释为奇偶。
        [self.layer addSublayer:trangleLayer];
        obj.trangleLayer = trangleLayer;

         CGFloat angle = obj.endAngel-(obj.endAngel-obj.startAngel)/2.0;//扇形边缘中心位置的角度
        //扇形边缘的中心点
//        CGPoint aPoint = CGPointMake(_centerPoint.x+ _radius*cos(DEGREES_TO_RADIANS(angle)),_centerPoint.y+ _radius*sin(DEGREES_TO_RADIANS(angle)));
        //外面边缘的半径
        CGFloat outRadius = [self height]/2.0+100;//100是随意
        CGPoint outCenterPoint = CGPointMake(_centerPoint.x+ outRadius*cos(DEGREES_TO_RADIANS(angle)), _centerPoint.y+outRadius*sin(DEGREES_TO_RADIANS(angle)));
        obj.outCenterPoint = outCenterPoint;

        int width = 30;
        //内部边缘文字
        UILabel *label0 = [[UILabel alloc]init];
        label0.adjustsFontSizeToFitWidth = YES;
        label0.bounds = CGRectMake(0, 0, width, width);
        CGFloat newRadius0 = (_radius-((width/2.0)/cos(DEGREES_TO_RADIANS(45))));//扇形的半径+label的对角线长度
        //新半径扇形的边缘中心点
        CGPoint bPoint0 = CGPointMake(_centerPoint.x+ newRadius0*cos(DEGREES_TO_RADIANS(angle)), _centerPoint.y+newRadius0*sin(DEGREES_TO_RADIANS(angle)));
        obj.innerLabelCenterPoint = bPoint0;
        obj.innerLabe = label0;
        label0.center = outCenterPoint;
        label0.text = @"6";
        label0.textAlignment = NSTextAlignmentCenter;
        [self addSubview:label0];


        //外围边缘文字
        UILabel *label = [[UILabel alloc]init];
        label.text = @"%10";
        label.textAlignment = NSTextAlignmentCenter;
        label.adjustsFontSizeToFitWidth = YES;
        label.bounds = CGRectMake(0, 0, width, width);
        CGFloat newRadius = (_radius+((width/2.0)/cos(DEGREES_TO_RADIANS(45))));//扇形的半径+label的对角线长度
        //新半径扇形的边缘中心点
        CGPoint bPoint = CGPointMake(_centerPoint.x+ newRadius*cos(DEGREES_TO_RADIANS(angle)), _centerPoint.y+newRadius*sin(DEGREES_TO_RADIANS(angle)));
        obj.outLabelCenterPoint = bPoint;
        obj.outLabel=label;
        label.center = outCenterPoint; //CGPointMake( aPoint.x+width/2.0, aPoint.y+((width/2.0)*tan(DEGREES_TO_RADIANS(angle))));
        [self addSubview:label];

    }];

}

3.添加简单翻转动画和路径动画

-(void)startAnimation
{
    //翻转 oglFlip
   [CommonUse addAnimationLayer:self.layer type:@"oglFlip"];

    [self.models enumerateObjectsUsingBlock:^(CurveModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self startLabelAnimation:obj];
    }];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self stopAnimation];
    });
}

传送门:

源码
有疑问的小伙伴欢迎加交流讨论QQ:206931384
愿意鼓励的小伙伴们请看:
这里写图片描述

2019-12-30 21:10:53 wq1psa78 阅读数 112

 
这段代码主要功能:
  - 读取保存有pose的文件,文件格式为:timestamp tx ty tz qw qx qy qz;
  - 第3个参数dataset_i表示测试的第i个数据集的轨迹,代码中,直接将第i个数据集的groundtruth写死了.
  - PCL绘制轨迹,用红色表示第一个文件中的轨迹,绿色表示第二个文件中的轨迹.
 
**局部效果图: **
pcl绘制SLAM轨迹
 
代码如下:

#include <iostream>
#include <fstream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <Eigen/Geometry> 
#include <boost/format.hpp>  // for formating strings
#include <pcl/point_types.h> 
#include <pcl/io/pcd_io.h> 
#include <pcl/visualization/pcl_visualizer.h>
#include <string>

using namespace std;

int main( int argc, char** argv )
{
    if(argc != 4){
        cerr << "Error!  ./joinMap poses1.txt poses2.txt index_of_dataset" << endl;
        return 0;
    }
    string path1 = argv[1] , path2 = argv[2];
    int dataset_i = atoi(argv[3]);
    ifstream fin1, fin2;
    fin1.open(path1.c_str());
    fin2.open(path2.c_str());
    if (!fin1 || !fin2)
    {
        cerr<<"请在有pose.txt的目录下运行此程序"<<endl;
        return 1;
    }

    typedef pcl::PointXYZRGB PointT; 
    typedef pcl::PointCloud<PointT> PointCloud;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
    
    // 新建一个点云
    PointCloud::Ptr pointCloud( new PointCloud ); 

    double t_wcn_gt1[3] = {-0.232988  , 0.178259 , 0.00207278};
    double t_wcn_gt2[3] = {0.0099497 , 0.153633 , 0.0190607};
    double t_wcn_lastRow1[3] = {0}; //record the lastest row of file
    double t_wcn_lastRow2[3] = {0};

    string str;
    getline(fin1,str);
    while(!fin1.eof()){
        double data[8] = {0};
        for(auto &d : data)
            fin1 >> d;
        if(fin1.eof())
            break;
        Eigen::Vector3d t{data[1] , data[2] , data[3]};
        PointT p ;
        p.x = data[1];
        p.y = data[2];
        p.z = data[3];
        p.b = 0;
        p.g = 0;
        p.r = 255;
        pointCloud->points.push_back( p );
        t_wcn_lastRow1[0] = data[1];
        t_wcn_lastRow1[1] = data[2];
        t_wcn_lastRow1[2] = data[3];
    }
    getline(fin2,str);
    while(!fin2.eof()){
        double data[8] = {0};
        for(auto &d : data)
            fin2 >> d;
        if(fin2.eof())
            break;
        Eigen::Vector3d t{data[1] , data[2] , data[3]};
        PointT p ;
        p.x = data[1];
        p.y = data[2];
        p.z = data[3];
        p.b = 0;
        p.g = 255;
        p.r = 0;
        pointCloud->points.push_back( p );
        t_wcn_lastRow2[0] = data[1];
        t_wcn_lastRow2[1] = data[2];
        t_wcn_lastRow2[2] = data[3];
    }
    cout << t_wcn_lastRow1[0] << "  "  << t_wcn_lastRow1[1] << " " << t_wcn_lastRow1[2] << endl;
    cout << t_wcn_lastRow2[0] << "  " << t_wcn_lastRow2[1] << "  " << t_wcn_lastRow2[2] << endl;

    //add the groundtruth of t_wcn;
    double gt_center[3];
    switch(dataset_i){
        case 1:
        {
            gt_center[0] = t_wcn_gt1[0];
            gt_center[1] = t_wcn_gt1[1];
            gt_center[2] = t_wcn_gt1[2];
            break;
        }
        case 2:
        {
            gt_center[0] = t_wcn_gt2[0];
            gt_center[1] = t_wcn_gt2[1];
            gt_center[2] = t_wcn_gt2[2];
            break;
        }
        default:
            break;
    }
    for(double i = 0;i < 0.3;i += 0.01){
        PointT p;
        //groundtruth
        p.b = 255;   p.g = 255;      p.r = 255;
        p.x = gt_center[0] + i;
        p.y = gt_center[1];
        p.z = gt_center[2];
        pointCloud->points.push_back(p);

        //the lastest pose of vio
        p.b = 255;   p.g = 0;      p.r = 255;
        p.x = t_wcn_lastRow1[0] + i;
        p.y = t_wcn_lastRow1[1];
        p.z = t_wcn_lastRow1[2];
        pointCloud->points.push_back(p);

        //the lastest pose of viwo
        p.b = 3;   p.g = 168;      p.r = 158;
        p.x = t_wcn_lastRow2[0] + i;
        p.y = t_wcn_lastRow2[1];
        p.z = t_wcn_lastRow2[2];
        pointCloud->points.push_back(p);
    }
    for(double i = 0;i < 0.3;i += 0.01){
        PointT p;
        p.b = 0;  p.g = 128;   p.r = 255;
        p.x = gt_center[0];
        p.y = gt_center[1] + i;
        p.z = gt_center[2] ;
        pointCloud->points.push_back(p);

        //the lastest pose of vio
        p.x = t_wcn_lastRow1[0];
        p.y = t_wcn_lastRow1[1] + i;
        p.z = t_wcn_lastRow1[2];
        pointCloud->points.push_back(p);

        //the lastest pose of viwo
        p.x = t_wcn_lastRow2[0];
        p.y = t_wcn_lastRow2[1] + i;
        p.z = t_wcn_lastRow2[2];
        pointCloud->points.push_back(p);
    }
    for(double i = 0;i < 0.5;i += 0.01){
        PointT p;
        p.b = 255;   p.g = 0;      p.r = 0;
        p.x = gt_center[0];
        p.y = gt_center[1] ;
        p.z = gt_center[2] + i;
        pointCloud->points.push_back(p);

         //the lastest pose of vio
        p.x = t_wcn_lastRow1[0];
        p.y = t_wcn_lastRow1[1];
        p.z = t_wcn_lastRow1[2] + i;
        pointCloud->points.push_back(p);

        //the lastest pose of viwo
        p.x = t_wcn_lastRow2[0];
        p.y = t_wcn_lastRow2[1];
        p.z = t_wcn_lastRow2[2] + i;
        pointCloud->points.push_back(p);
    }
    
    cout<<"正在将图像转换为点云..."<<endl;

    
    pointCloud->is_dense = false;
    cout<<"点云共有"<<pointCloud->size()<<"个点."<<endl;
    pcl::io::savePCDFileBinary("map.pcd", *pointCloud );
    return 0;
}

 
下面是CMakeLists.txt的内容:

cmake_minimum_required( VERSION 2.8 )
project( point_cloud )

set( CMAKE_BUILD_TYPE Release )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )

# opencv 
find_package( OpenCV 3.0 REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )

# eigen 
include_directories( "/usr/include/eigen3/" )

# pcl 
find_package( PCL REQUIRED COMPONENT common io )
include_directories( ${PCL_INCLUDE_DIRS} )
add_definitions( ${PCL_DEFINITIONS} )

add_executable( joinMap joinMap.cpp )
target_link_libraries( joinMap ${OpenCV_LIBS} ${PCL_LIBRARIES} )

 
运行指令:

./joinMap '/home/xtl/output/f1_poses_vins_fusion.txt' '/home/xtl/output/f1_poses_viwo_fusion.txt'  1

 


没有更多推荐了,返回首页