iOS OpenGL ES3.0入门实践

一、效果图 

入门实践,做的东西比较简单,效果如下:

二、关于顶点坐标和纹理坐标

绘制图片需要设置顶点坐标和纹理坐标并加载像素数据,之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系,就是告诉OpenGL:把图像的某一区域绘制到屏幕的某一区域,3个点能确定一个三角形区域,我们把一张图分成4个三角形,分别是中心点和每条边点两个顶点组成的三角形,如下图,它图能够帮助你理解后面代码 GLImage.m 中的两组顶点为什么是那样子设置的

三、关于坐标轴方向、投影和坐标映射

具体参考我的另一篇文章Android OpenGL ES 2.0入门实践,主要也是参考的Android官方文档,好在iOS都有对应的函数,就没再仔细看iOS官方文档了。 

四、代码

OpenGL在iOS12 就已经不推荐使用了,官方推荐使用Metal,最近在学习OpenGL ES就在Android和iOS上分别实践了一下,iOS端最简单的方式就是使用GLKit,主要使用GLKViewController这个现成的绘制环境,有了这个环境,就可以直接使用OpenGL的接口进行编码了,平台有更简单易用的 GLKBaseEffect,我没有使用,还是沿用了最基本的流程,使用GL着色器,下面是主要代码:

GlUtil.h

#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GLUtil : NSObject

+(GLuint)createProgram:(NSString*)vertexShaderCode :(NSString*)fragmentShaderCode;

+(void)printActiveUniforms:(GLuint)program;

@end

NS_ASSUME_NONNULL_END

GLUtil.m

#import "GLUtil.h"

@implementation GLUtil

+ (NSString *)readShaderSource:(NSString *)bundleFileName {
    NSString* path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:bundleFileName];
    NSString* content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//    NSLog(@"%@", content);
    return content;
}

+ (GLuint)loadShader:(GLenum)type :(const char *)shaderCode {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &shaderCode, NULL);
    glCompileShader(shader);
    
    GLint commpiled;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &commpiled);
    if(!commpiled) {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 0) {
            char* infoLog = malloc(infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            NSLog(@"%s", infoLog);
            free(infoLog);
            glDeleteShader(shader);
            return 0;
        }
    }
    return shader;
}

+ (GLuint)createProgram:(NSString *)vertexShaderFileName :(NSString *)fragmentShaderFileName {
    GLuint program = glCreateProgram();
    
    const char* vertexShaderCode = [[GLUtil readShaderSource:vertexShaderFileName] UTF8String];
    const char* fragmentShaderCode = [[GLUtil readShaderSource:fragmentShaderFileName] UTF8String];
    
    GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER:vertexShaderCode];
    GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER:fragmentShaderCode];
    
    if (vertexShader == 0 || fragmentShader == 0) {
        return 0;
    }
    
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);
    GLint linked;
    glGetProgramiv(program, GL_LINK_STATUS, &linked);
    if(!linked) {
        GLint infoLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 0) {
            char* infoLog = malloc(infoLen);
            glGetProgramInfoLog(program, infoLen, NULL, infoLog);
            NSLog(@"%s", infoLog);
            free(infoLog);
            glDeleteProgram(program);
            return 0;
        }
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    return program;
}


+ (void)printActiveUniforms:(GLuint)program {
    GLint maxUniformLen;//变量名的最大长度
    GLint numUniforms;//变量个数
    char* uniformName;//变量名
    glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numUniforms);
    glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
    uniformName = malloc(maxUniformLen);
    for (GLint index = 0; index < numUniforms; index++) {
        GLint size;
        GLenum type;
        GLint location;
        glGetActiveUniform(program, index, maxUniformLen, NULL, &size, &type, uniformName);
        location = glGetUniformLocation(program, uniformName);
        NSString* typeStr;
        switch (type) {
            case GL_FLOAT:
                typeStr = (@"GL_FLOAT");
                break;
                
            case GL_FLOAT_VEC2:
                typeStr = (@"GL_FLOAT_VEC2");
                break;
                
            case GL_FLOAT_VEC3:
                typeStr = (@"GL_FLOAT_VEC3");
                break;
                
            case GL_FLOAT_VEC4:
                typeStr = (@"GL_FLOAT_VEC4");
                break;
                
            case GL_FLOAT_MAT4:
                typeStr = (@"GL_FLOAT_MAT4");
                break;
                
            case GL_BOOL:
                typeStr = (@"GL_BOOL");
                break;
                
            case GL_SAMPLER_2D:
                typeStr = (@"GL_SAMPLER_2D");
                break;
                
            default:
                typeStr = [NSString stringWithFormat:@"变量类型:0x%X", type];
                break;
        }
        
        NSLog(@"index=%d, name=%s, size=%d, type=%@, location=%d", index, uniformName, size, typeStr, location);
    }
    free(uniformName);
}

@end

vertexShader.glsl

顶点着色器代码 vertexShader.glsl

#version 300 es
uniform mat4 uMVPMatrix;
uniform mat4 translateMatrix;
uniform bool isTranslate;
layout(location = 0) in vec4 vPosition;
layout(location = 1) in vec2 aTextureCoord;
out vec2 vTexCoord;
void main() {
    if (isTranslate) {
        gl_Position = uMVPMatrix * translateMatrix * vPosition;
    } else {
        gl_Position = uMVPMatrix * vPosition;
    }
    vTexCoord = aTextureCoord;
}

fragmentShader.glsl

片段着色器代码 fragmentShader.glsl

#version 300 es
precision mediump float;
uniform bool isTexture;
uniform vec4 vColor;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
   if(isTexture) {
       fragColor = texture(uTextureUnit,vTexCoord);
   } else {
       fragColor = vColor;
   }
}

绘制2D纹理的类 GLImage

GLImage.h

#import <GLKit/GLKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GLImage : NSObject

-(void)loadTexture:(NSString*)imageName;

-(void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo;

@end

NS_ASSUME_NONNULL_END

GLImage.m

#import "GLImage.h"
#import <UIKit/UIKit.h>

//原文链接:https://blog.csdn.net/gongxiaoou/article/details/89344561

//图片尺寸:2385 × 3623
static const float POSITION_VERTEX[]  = {
    0.0f, 0.0f,     //顶点坐标V0
    1.0f, 1.52f,   //顶点坐标V1
    -1.0f, 1.52f,   //顶点坐标V2
    -1.0f, -1.52f,  //顶点坐标V3
    1.0f, -1.52f     //顶点坐标V4
};

static const float TEX_VERTEX[]  = {
    0.5f, 0.5f, //纹理坐标V0
    1.0f, 1.0f,     //纹理坐标V1
    0.0f, 1.0f,     //纹理坐标V2
    0.0f, 0.0f,   //纹理坐标V3
    1.0f, 0.0f    //纹理坐标V4
};

static const short VERTEX_INDEX[]  = {
    0, 1, 2,  //V0,V1,V2 三个顶点组成一个三角形
    0, 2, 3,  //V0,V2,V3 三个顶点组成一个三角形
    0, 3, 4,  //V0,V3,V4 三个顶点组成一个三角形
    0, 4, 1   //V0,V4,V1 三个顶点组成一个三角形
};


@interface GLImage()
{
    GLuint textureID;
}

@end

@implementation GLImage


//原文链接:https://blog.csdn.net/qq_30513483/article/details/101538967
- (void)loadTexture:(NSString *)imageName {
    UIImage *image = [UIImage imageNamed:imageName];
    
    // 将 UIImage 转换为 CGImageRef
    CGImageRef cgImageRef = [image CGImage];
    GLuint width = (GLuint)CGImageGetWidth(cgImageRef);
    GLuint height = (GLuint)CGImageGetHeight(cgImageRef);
    CGRect rect = CGRectMake(0, 0, width, height);

    // 绘制图片
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    void *imageData = malloc(width * height * 4);
    CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0f, -1.0f);
    CGColorSpaceRelease(colorSpace);
    CGContextClearRect(context, rect);
    CGContextDrawImage(context, rect, cgImageRef);
     
    // 生成纹理
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    // 将图片数据写入纹理缓存
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
        
    // 设置如何把纹素映射成像素
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            
    // 解绑
    glBindTexture(GL_TEXTURE_2D, 0);
    
    // 释放内存
    CGContextRelease(context);
    free(imageData);
}

- (void)draw:(int)positionHandle :(int)textureHandle :(GLuint*)vbo {
    //顶点坐标
    glEnableVertexAttribArray(positionHandle);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(POSITION_VERTEX), POSITION_VERTEX, GL_STATIC_DRAW);
    glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);
    
    //纹理坐标
    glEnableVertexAttribArray(textureHandle);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(TEX_VERTEX), TEX_VERTEX, GL_STATIC_DRAW);
    glVertexAttribPointer(textureHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);
    //激活纹理
    glActiveTexture(GL_TEXTURE0);
    //绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureID);
    //绘制
    glDrawElements(GL_TRIANGLES, sizeof(VERTEX_INDEX)/sizeof(short), GL_UNSIGNED_SHORT, VERTEX_INDEX);
}

@end

GLViewController.h

#import <GLKit/GLKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface GLViewController : GLKViewController {
}

@end

NS_ASSUME_NONNULL_END

GLViewController.m

#import "GLViewController.h"
#import "GLUtil.h"
#import "GLImage.h"


const int VBO_NUM = 2;

static const GLfloat vertices[] = {
    0.0f,  0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f
    
};


@interface GLViewController ()
{
    GLuint program;
    int muMVPMatrixHandle;
    int translateMatrixHandle;
    int isTranslateHandle;
    int isTextureHandle;
    int colorHandle;
    GLKMatrix4 vMatrix;
    GLKMatrix4 projMatrix;
    GLKMatrix4 vPMatrix;
    GLKMatrix4 translateMatrix;
    GLuint vbo[VBO_NUM];
    
    GLImage* glImage;
}

@end

@implementation GLViewController

- (void)initGL{
    program = [GLUtil createProgram:@"vertexShader.glsl" :@"fragmentShader.glsl"];
    glUseProgram(program);
    muMVPMatrixHandle = glGetUniformLocation(program, "uMVPMatrix");
    translateMatrixHandle = glGetUniformLocation(program, "translateMatrix");
    isTranslateHandle = glGetUniformLocation(program, "isTranslate");
    isTextureHandle = glGetUniformLocation(program, "isTexture");
    colorHandle = glGetUniformLocation(program, "vColor");
    
    glGenBuffers(VBO_NUM, vbo);
    [GLUtil printActiveUniforms:program];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    GLKView* glView = (GLKView*)self.view;
    glView.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
    [EAGLContext setCurrentContext:glView.context];//这句必须得有,不然画不出来图形
    
    [self initGL];
    
    glImage = [GLImage new];
    [glImage loadTexture:@"dog.jpg"];
    
    // Combine the projection and camera view matrices
    vMatrix = GLKMatrix4MakeLookAt(0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
}

- (void) onSurfaceChange:(float)width :(float)height {
    NSLog(@"%fx%f", width, height);
    glViewport(0, 0, width, height);
    //投影矩阵
    if (width > height) {
        float ratio = width / height;
        projMatrix = GLKMatrix4MakeFrustum(-ratio, ratio, -1.0f, 1.0f, 3.0f, 7.0f);
    } else {
        float ratio = height / width;
        projMatrix = GLKMatrix4MakeFrustum(-1.0f, 1.0f, -ratio, ratio, 3.0f, 7.0f);
    }
    
    // Combine the projection and camera view matrices
    vPMatrix = GLKMatrix4Multiply(projMatrix, vMatrix);
}


- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    float width = self.view.frame.size.width;
    float height = self.view.frame.size.height;
    NSLog(@"%s %fx%f", __func__, width, height);
    [self onSurfaceChange:width :height];
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
//    NSLog(@"%s %f", __func__, [[NSDate new] timeIntervalSince1970]);
    // Apply the combined projection and camera view transformations
    glUniformMatrix4fv(muMVPMatrixHandle, 1, false, (float*)&vPMatrix);
    
    glClearColor(0.9f, 0.9f, 0.9f, 1.0f);//背景色
    glClear(GL_COLOR_BUFFER_BIT);
    
    //画图片
    glUniform1i(isTranslateHandle, 0);//禁用矩阵偏移
    glUniform1i(isTextureHandle, 1);//启用纹理
    [glImage draw:0 :1 :vbo];
    
    double degrees = (long)([[NSDate new] timeIntervalSince1970] * 50) % 360;
    double radians = degrees * M_PI / 180.0;
//    NSLog(@"degrees=%f", degrees);
    translateMatrix = GLKMatrix4Translate(GLKMatrix4Identity, cos(radians), sin(radians), 0.0f);
    glUniformMatrix4fv(translateMatrixHandle, 1, GL_FALSE, (float*)&translateMatrix);
    
    glUniform1i(isTranslateHandle, 1);//启用矩阵偏移
    glUniform1i(isTextureHandle, 0);//禁用纹理
    glUniform4f(colorHandle, 0.0f, 0.5f, 1.0f, 1.0f);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

@end

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

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

相关文章

9 个可以免费检索意外删除或丢失的文件的专业数据恢复软件

今天&#xff0c;我们将探索一些最佳数据恢复软件&#xff0c;它们可以帮助您从 Windows PC 或存储设备中检索意外删除或丢失的文件&#xff01; 丢失数据或意外删除数据是一种令人不安的经历。值得庆幸的是&#xff0c;存在有效的解决方案来解决这种情况。今天&#xff0c;我…

CSS 滚动捕获 scroll-snap-type

scroll-snap-type 语法实例 捕获轴 y 捕获严格程度 mandatory捕获轴 y 捕获严格程度 proximity同理看下捕获轴 x 一些注意事项兼容性 scroll-snap-type 用来指定一个滚动容器(scroll container)是否是滚动捕获容器(scroll snap container)、捕获的严格程度以及在什么方向上执行…

2.8 CE修改器:寻找共享代码

本关我们将学习共享代码&#xff0c;在C语言中角色属性都是以结构体的方式进行存储的&#xff0c;而结构体所存储的信息都是连续性的&#xff0c;这一关我们将会解释如何处理游戏中的共用代码&#xff0c;这种代码是通用在除了自己以外的其他同类型对像上的常常你在修改游戏的时…

在Vue.js中,什么是Vuex?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

Spring Cloud OpenFeign:基于Ribbon和Hystrix的声明式服务调用

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Spring Cloud OpenFeign&#xff1a;基于Ribbon和Hystrix的声明式服务调用 Spring Cloud OpenFeign是一个声明式的服务调用框架&#xff0c;基于Feign并整合了Ribbon和…

Crypto | Affine password 第二届“奇安信”杯网络安全技能竞赛

题目描述&#xff1a; 明文经过仿射函数y3x9加密之后变为JYYHWVPIDCOZ&#xff0c;请对其进行解密&#xff0c;flag的格式为flag{明文的大写形式}。 密文&#xff1a; JYYHWVPIDCOZ解题思路&#xff1a; 1、使用在线网站直接破解或手工计算破解&#xff0c;获得flag。&#xf…

中国专利转让数据集(1985-2021年)

专利转让数据追踪和记录专利从一个实体转移到另一个实体的过程。这些数据不仅包括参与转让的申请人和受让人的身份信息&#xff0c;如名字和地址&#xff0c;还涵盖了转让的具体法律细节&#xff0c;包括转让执行日、转让次数、法律状态变更&#xff0c;以及转让登记的相关信息…

软件开发项目文档系列之十六如何撰写系统运维方案

前言 项目运维方案是为了确保项目的稳定运行和可持续发展而制定的指导性文档。本文将详细介绍项目运维方案的各个方面&#xff0c;包括硬件和软件基础设施、监控和警报、备份和恢复、安全性、团队组织和沟通等方面。本博客将提供示例和最佳实践&#xff0c;以帮助您更好地理解…

前端前沿技术

文章目录 网站静态化PWA - Progressive Web APP, 渐进式 Web 应用PWA 简介解决了哪些问题?PWA 的优势浏览器支持情况参考文档 Weex 是一个可以使用现代化的 Web 技术开发高性能原生应用的框架。高性能跨平台贴近前端生态被大规模的使用 GraphQL[一种用于 API 的查询语言](http…

通信原理板块——线性分组码之循环码

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、循环码原理 循环码(cycle code)…

Linux操作系统使用及C高级编程-D3Linux shell命令(权限、输入输出)

Shell 是一种应用程序&#xff0c;用以完成用户与内核之间的交互 一个功能强大的编程语言&#xff08;C语言&#xff09; 一个解释执行的脚本语言&#xff0c;不需要编译&#xff0c;写完直接执行 目前Linux 乌班图的Shell默认是bash 查看当前提供的Shell&#xff1a;cat /…

Milvus Cloud——LangChain 分块简介

LangChain 分块简介 LangChain 是一个 LLM 协调框架,内置了一些用于分块以及加载文档的工具。本次分块教程主要围绕设置分块参数,并最小限度地使用 LLM。简而言之,通过编写一个函数并设置其参数来加载文档并对文档进行分块,该函数打印结果为分块后的文本块。在下述实验中,…

Linux服务器从零开始训练 RT-DETR 改进项目 (Ultralytics) 教程,改进RTDETR算法(包括使用训练、验证、推理教程)

手把手从零开始训练 RT-DETR 改进项目 (Ultralytics版本) 教程,改进RTDETR算法 本文以Linux服务器为例:从零开始使用Linux训练 RT-DETR 算法项目 《芒果剑指 RT-DETR 目标检测算法 改进》 适用于芒果专栏改进RT-DETR算法 文章目录 百度 RT-DETR 算法介绍改进网络代码汇总第…

轻量封装WebGPU渲染系统示例<26>- PingpongBlur模糊效果(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/rendering/src/voxgpu/sample/PingpongBlur.ts 当前示例运行效果: WGSL片段shader group(0) binding(0) var<uniform> param: vec4f; group(0) binding(1) var sampler0: sampler; group(…

(免费领源码)Springboot宠物医院管理系统的设计与实现84724-计算机毕业设计项目选题推荐

摘 要 现如今生活质量提高&#xff0c;人们追求精神健康&#xff0c;与家中宠物朝夕相处&#xff0c;感情深厚&#xff0c;宠物渐渐成了我们身边的朋友。因而宠物生病了&#xff0c;需要去看病&#xff0c;自古医院救死扶伤&#xff0c;生命无贵贱&#xff0c;无论人类还是动物…

C# Onnx LSTR 基于Transformer的端到端实时车道线检测

目录 效果 模型信息 项目 代码 下载 效果 模型信息 lstr_360x640.onnx Inputs ------------------------- name&#xff1a;input_rgb tensor&#xff1a;Float[1, 3, 360, 640] name&#xff1a;input_mask tensor&#xff1a;Float[1, 1, 360, 640] -----------------…

计算机毕业设计项目选题推荐(免费领源码)Springboot+MySQL智能购房推荐系统09040

摘 要 近年来随着我国经济的高速发展&#xff0c;房地产业也随之蓬勃发展&#xff0c;尤其是最近国家新出台的房改政策。鼓励居民购房&#xff0c;这对房产公司无疑是一个极好的发展势头。尤为重要的是&#xff0c;近几年随着信息技术和电子商务的快速发展&#xff0c;许多企业…

倾斜摄影三维模型的根节点合并的点云抽稀关键技术分析

倾斜摄影三维模型的根节点合并的点云抽稀关键技术分析 倾斜摄影三维模型的根节点合并是指将多个倾斜摄影拍摄得到的点云数据进行抽稀操作&#xff0c;以减少点云数据量和提高数据处理效率。在处理大规模的倾斜摄影点云数据时&#xff0c;点云抽稀是一个关键的技术&#xff0c;它…

Linux基础环境开发工具的使用(三):gdb调试器

Linux基础环境开发工具的使用[三]:gdb调试器 一.调试命令的应用场景1.为什么要介绍调试命令的应用场景呢?2.调试命令的应用场景1.找到问题1.不借助调试2.调试 2.解决问题1.不借助调试2.借助调试 二.调试命令1.gdb使用的前置说明2.基础指令3.断点相关指令4.范围查找相关操作5.局…

番外 1 : Java 环境下的 selenium 搭建

Java 环境下的 selenium 搭建 一 . 下载谷歌浏览器二 . 下载谷歌浏览器驱动2.1 查看谷歌浏览器版本2.2 下载对应版本的谷歌驱动2.3 解压下载好的驱动压缩包 , 将下载好的 chromedriver.exe 放到java 系统环境变量下 三 . 下载 Edge 浏览器的驱动3.1 查看 Edge 浏览器的版本3.2 …