Qt实现简单的漫游器
文章目录
- Qt的OpenGL窗口
- GLSL的实现
- 摄像机类的实现
- 简单的漫游器
Qt的OpenGL窗口
Qt主要是使用QOpenGLWidget来实现opengl的功能。
QOpenGLWidget 提供了三个便捷的虚函数,可以重载,用来重新实现典型的OpenGL任务:
- paintGL:渲染OpenGL场景。widget 需要更新时调用。
- resizeGL:设置OpenGL视口、投影等。widget 调整大小(或首次显示)时调用。
- initializeGL:设置OpenGL资源和状态。第一次调用 resizeGL() / paintGL() 之前调用一次。
- 如果需要从paintGL()以外的位置触发重新绘制(典型示例是使用计时器设置场景动画),则应调用widget的update()函数来安排更新。
- 调用paintGL()、resizeGL()或initializeGL()时,widget 的OpenGL呈现上下文将变为当前。如果需要从其他位置(例如,在 widget 的构造函数或自己的绘制函数中)调用标准OpenGL API函数,则必须首先调用makeCurrent()。
- QOpenGLFunctions_X_X_Core 提供OpenGL X.X版本核心模式的所有功能。是对OpenGL函数的封装:
- initializeOpenGLFunctions:初始化OpenGL函数,将Qt里的函数指针指向显卡的函数。
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core> class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{
Q_OBJECT
public:
explicit MyOpenGLWidget (QWidget *parent = nullptr);
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
};
void MyOpenGLWidget::initializeGL()
{initializeOpenGLFunctions();
}
void MyOpenGLWidget::resizeGL(int w, int h)
{Q_UNUSED(w);Q_UNUSED(h);
}
void MyOpenGLWidget::paintGL()
{glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

以上是最简单的实现版本。
GLSL的实现
由于是最简单的漫游器,所以我们实现的版本只需要一个顶点着色器和一个片段着色器即可。
shapes.vert
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoord;out vec2 texCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main(){gl_Position = projection * view * model * vec4(aPos, 1.0f);texCoord = aTexCoord;
}
shapes.frag
#version 330 core
out vec4 FragColor;
in vec2 texCoord;uniform sampler2D texturewall;
uniform sampler2D texturesmile;
uniform sampler2D textureSmall;uniform float ratio;
void main(){FragColor = mix(texture(texturewall,texCoord),texture(texturesmile,texCoord),ratio);
}
摄像机类的实现
这边实现了一个可以对模型进行上下左右移动,移动视角,放大缩小的操作。
摄像机类初始化了几个变量
// 默认值
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;
偏航角默认为90度,灵敏度主要用于控制鼠标移动时视角的变化量。
#ifndef CAMERA_H
#define CAMERA_H#include<QMatrix4x4>#include <vector>// 移动方向枚举量. 是一种抽象,以避开特定于窗口系统的输入方法
// 我们这里是WSAD
enum Camera_Movement {FORWARD,BACKWARD,LEFT,RIGHT
};// 默认值
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;// 一个抽象的camera类,用于处理输入并计算相应的Euler角度、向量和矩阵,以便在OpenGL中使用
class Camera
{
public:// camera AttributesQVector3D Position;QVector3D Front;QVector3D Up;QVector3D Right;QVector3D WorldUp;// euler Anglesfloat Yaw;float Pitch;// camera optionsfloat MovementSpeed;float MouseSensitivity;float Zoom;// constructor with vectorsCamera(QVector3D position = QVector3D(0.0f, 0.0f, 0.0f), QVector3D up = QVector3D(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(QVector3D(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM){Position = position;WorldUp = up;Yaw = yaw;Pitch = pitch;updateCameraVectors();}// constructor with scalar valuesCamera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(QVector3D(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM){Position = QVector3D(posX, posY, posZ);WorldUp = QVector3D(upX, upY, upZ);Yaw = yaw;Pitch = pitch;updateCameraVectors();}// returns the view matrix calculated using Euler Angles and the LookAt MatrixQMatrix4x4 GetViewMatrix(){QMatrix4x4 theMatrix;theMatrix.lookAt(Position, Position + Front, Up);return theMatrix;}// 处理从任何类似键盘的输入系统接收的输入。接受摄像机定义枚举形式的输入参数(从窗口系统中提取)void ProcessKeyboard(Camera_Movement direction, float deltaTime){float velocity = MovementSpeed * deltaTime;if (direction == FORWARD)Position += Front * velocity;if (direction == BACKWARD)Position -= Front * velocity;if (direction == LEFT)Position -= Right * velocity;if (direction == RIGHT)Position += Right * velocity;}// 处理从鼠标输入系统接收的输入。需要x和y方向上的偏移值。void ProcessMouseMovement(float xoffset, float yoffset, bool constrainPitch = true){xoffset *= MouseSensitivity;yoffset *= MouseSensitivity;Yaw += xoffset;Pitch += yoffset;// 确保当投球超出边界时,屏幕不会翻转if (constrainPitch){if (Pitch > 89.0f)Pitch = 89.0f;if (Pitch < -89.0f)Pitch = -89.0f;}// 使用更新的Euler角度更新前、右和上矢量updateCameraVectors();}// 处理从鼠标滚轮事件接收的输入。仅需要在垂直车轮轴上输入void ProcessMouseScroll(float yoffset){Zoom -= (float)yoffset;if (Zoom < 1.0f)Zoom = 1.0f;if (Zoom > 75.0f)Zoom = 75.0f;}private:// 根据相机的(更新的)Euler角度计算前矢量void updateCameraVectors(){// calculate the new Front vectorfloat PI=3.1415926;QVector3D front;front.setX(cos(Yaw*PI/180.0) * cos(Pitch*PI/180.0));front.setY( sin(Pitch*PI/180.0));front.setZ(sin(Yaw*PI/180.0) * cos(Pitch*PI/180.0));front.normalize();Front = front;// also re-calculate the Right and Up vectorRight = QVector3D::crossProduct(Front, WorldUp);// 标准化向量,因为向上或向下看得越多,向量的长度就越接近0,这会导致移动速度变慢。Right.normalize();Up = QVector3D::crossProduct(Right, Front);Up.normalize();}
};
#endif
简单的漫游器
漫游器的实现主要是通过Qt的窗口事件触发后将触发产生的位置偏量给摄像机类进行计算,从摄像机类中得到视图矩阵将模型的位置进行改变。
myopenglwidget.h
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QTimer>
#include <QElapsedTimer>
#include "camera.h"class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{Q_OBJECT
public:enum Shape{None,Circle,Rect,Triangle};explicit MyOpenGLWidget(QWidget *parent = nullptr);~MyOpenGLWidget();void DrawShape(Shape shape);void setWireFrameMode(bool enterWireframe);void onTimeout();
protected:virtual void initializeGL();virtual void resizeGL(int w, int h);virtual void paintGL();
signals:
private:Shape m_shape;QOpenGLShaderProgram shaderProgram;QOpenGLTexture *textureWall;QOpenGLTexture *textureSmile;QOpenGLTexture *textureSmall;QTimer *m_timer;QElapsedTimer m_time;Camera m_camera;// QWidget interface
protected:void keyPressEvent(QKeyEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;void wheelEvent(QWheelEvent *event) override;
};#endif // MYOPENGLWIDGET_H
myopenglwidget.cpp
#include "myopenglwidget.h"
#include <QImage>
#include <QKeyEvent>float vertices[] = {-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};QVector<QVector3D> cubePositions= {QVector3D( 0.0f, 0.0f, 0.0f),QVector3D( 2.0f, 5.0f, -15.0f),QVector3D(-1.5f, -2.2f, -2.5f),QVector3D(-3.8f, -2.0f, -12.3f),QVector3D( 2.4f, -0.4f, -3.5f),QVector3D(-1.7f, 3.0f, -7.5f),QVector3D( 1.3f, -2.0f, -2.5f),QVector3D( 1.5f, 2.0f, -2.5f),QVector3D( 1.5f, 0.2f, -1.5f),QVector3D(-1.3f, 1.0f, -1.5f)
};#define TIMEOUT 100unsigned int indices[] = { // note that we start from 0!0, 1, 3, // first triangle1, 2, 3 // second triangle
};unsigned int VBO, VAO ,EBO;float ratio = 0.5;
QPoint deltaPos;MyOpenGLWidget::MyOpenGLWidget(QWidget *parent): QOpenGLWidget{parent}
{setFocusPolicy(Qt::StrongFocus);setMouseTracking(true);m_timer = new QTimer(this);connect(m_timer,&QTimer::timeout,this,&MyOpenGLWidget::onTimeout);m_timer->start(TIMEOUT);m_time.start();m_camera.Position = QVector3D(0.0,0.0,3.0);
}MyOpenGLWidget::~MyOpenGLWidget()
{if(!isValid()) return;makeCurrent();glDeleteBuffers(1,&VBO);glDeleteBuffers(1,&EBO);glDeleteVertexArrays(1,&VAO);doneCurrent();
}void MyOpenGLWidget::DrawShape(Shape shape)
{m_shape=shape;update();
}void MyOpenGLWidget::setWireFrameMode(bool enterWireframe)
{makeCurrent();if(enterWireframe)glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);elseglPolygonMode(GL_FRONT_AND_BACK,GL_FILL);update();doneCurrent();
}void MyOpenGLWidget::onTimeout()
{update();
}void MyOpenGLWidget::initializeGL()
{initializeOpenGLFunctions();//创建VBO和VAO对象,并赋予IDglGenVertexArrays(1,&VAO);glGenBuffers(1,&VBO);//绑定VAO和VBO对象glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER,VBO);//为当前绑定到target的缓冲区对象创建一个新的数据存储。//如果data不是NULL,则使用来自此指针的数据初始化数据存储glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);//告知显卡如何解析缓冲里的属性值glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,5*sizeof(float),(void *)0);//开启VAO管理的第一个属性值glEnableVertexAttribArray(0);//告知显卡如何解析缓冲里的属性值glVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,5*sizeof(float),(void *)(3*sizeof(float)));//开启VAO管理的第三个属性值glEnableVertexAttribArray(1);glBindBuffer(GL_ARRAY_BUFFER,0);bool success;shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":Shaders/shapes.vert");shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":Shaders/shapes.frag");success = shaderProgram.link();if(!success){qDebug()<<"Error:"<<shaderProgram.log();}shaderProgram.bind();shaderProgram.setUniformValue("ratio",ratio);shaderProgram.setUniformValue("texturewall",0);shaderProgram.setUniformValue("texturesmile",1);shaderProgram.setUniformValue("textureSmall",2);unsigned int EBO;glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);textureWall = new QOpenGLTexture(QImage(":Images/images/wall.jpg").mirrored());textureSmile = new QOpenGLTexture(QImage(":Images/images/awesomeface.png").mirrored());textureSmall = new QOpenGLTexture(QImage(":Images/images/small.png").mirrored());glBindVertexArray(0);}void MyOpenGLWidget::resizeGL(int w, int h)
{Q_UNUSED(w);Q_UNUSED(h);
}void MyOpenGLWidget::paintGL()
{QMatrix4x4 model;QMatrix4x4 view;QMatrix4x4 projection;view=m_camera.GetViewMatrix();glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);shaderProgram.bind();projection.perspective(m_camera.Zoom,(float)width()/(height()),0.1,100.0);shaderProgram.setUniformValue("projection", projection);shaderProgram.setUniformValue("view", view);glBindVertexArray(VAO);//glDrawArrays(GL_TRIANGLES, 0, 3);switch(m_shape){case Rect:textureWall->bind(0);textureSmile->bind(1);textureSmall->bind(2);//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);foreach (auto item, cubePositions){model.setToIdentity();model.translate(item);shaderProgram.setUniformValue("model", model);glDrawArrays(GL_TRIANGLES,0,36);}break;case None:break;case Triangle:break;case Circle:break;}}void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{float deltatime=TIMEOUT / 1000.0;switch(event->key()){case Qt::Key_Up:ratio += 0.1;break;case Qt::Key_Down:ratio -= 0.1;break;case Qt::Key_W: m_camera.ProcessKeyboard(FORWARD,deltatime);break;case Qt::Key_S: m_camera.ProcessKeyboard(BACKWARD,deltatime);break;case Qt::Key_D: m_camera.ProcessKeyboard(RIGHT,deltatime);break;case Qt::Key_A: m_camera.ProcessKeyboard(LEFT,deltatime);break;}if(ratio > 1) ratio = 1;if(ratio < 0) ratio = 0;makeCurrent();shaderProgram.bind();shaderProgram.setUniformValue("ratio",ratio);update();doneCurrent();
}void MyOpenGLWidget::mouseMoveEvent(QMouseEvent *event)
{static QPoint lastPos(width()/2,height()/2);auto currentPos=event->pos();deltaPos=currentPos-lastPos;lastPos=currentPos;m_camera.ProcessMouseMovement(deltaPos.x(),-deltaPos.y());update();
}void MyOpenGLWidget::wheelEvent(QWheelEvent *event)
{ m_camera.ProcessMouseScroll(event->angleDelta().y()/120);update();
}
完整代码链接
相关文章:
Qt实现简单的漫游器
文章目录 Qt的OpenGL窗口GLSL的实现摄像机类的实现简单的漫游器 Qt的OpenGL窗口 Qt主要是使用QOpenGLWidget来实现opengl的功能。 QOpenGLWidget 提供了三个便捷的虚函数,可以重载,用来重新实现典型的OpenGL任务: paintGL:渲染…...
【c语言】文件操作
朋友们,大家好,今天分享给大家的是文件操作的相关知识,跟着我一起学习吧!! 🎈什么是文件 磁盘上的文件是文件。 但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件 程序文…...
【Unity】坐标转换经纬度方法(应用篇)
【Unity】坐标转换经纬度方法(应用篇) 解决地图中经纬度坐标转换与unity坐标互转的问题。使用线性变换的方法,理论上可以解决小范围内所以坐标转换的问题。 之前有写过[Unity]坐标转换经纬度方法(原理篇),在实际使用中,…...
element时间选择器el-date-picter使用disabledDate指定禁用的日期
需要的效果 <el-date-pickerclass"selectstyle"v-model"year"value-format"yyyy"type"year":picker-options"disabledCli"placeholder"选择年"> </el-date-picker>data() {return {disabledCli: {/…...
出学校干了 5 年外包,已经废了
如果不是女朋友和我提分手,我估计现在还没醒悟 本科大专,17年通过校招进入某软件公司做测试,干了接近5年的功能。 今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经…...
day-23 代码随想录算法训练营(19)part09
669.修剪二叉搜索树 思路一:根据二叉搜索树的特性进行中间值与去区间值判断,有三种情况:1.在区间中,所以左右子树都可能在区间中; 2.在区间外面的左侧,必然只有右子树可能存在区间中;3.在区间外…...
JVM编译优化
即时编译器 HotSpot虚拟机中内置了两个即时编译器,分别称为Client Compiler和Server Compiler,或者简称为C1编译器和C2编译器。Java8默认开启Server模式。用户可以使用“-client”或“-server”参数去指定编译模式。 C1编译器启动速度快,关注局部简单可靠的优化,比如方法…...
vue浏览器插件安装-各种问题
方法1:vue.js devtolls插件下载 https://blog.csdn.net/qq_55640378/article/details/131553642 下载地址: Tags vuejs/devtools GitHub npm install 或是 cnpm install 遇到的报错 设置淘宝镜像源(推荐使用nrm,这一步是为…...
maven工具-maven的使用-镜像仓库、本地仓、IDEA使用maven
Maven 一、为什么使用maven 添加第三方jar包jar包之间的依赖关系处理jar包之间的冲突获取第三方jar包将项目拆分成多个工程模块实现项目的分布式部署 二、maven简介 Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的…...
Mac鼠标增强工具Smooze Pro
Smooze Pro是一款Mac上的鼠标手势增强工具,可以让用户使用鼠标手势来控制应用程序和系统功能。 它支持多种手势操作,包括单指、双指、三指和四指手势,并且可以自定义每种手势的功能。例如,您可以使用单指向下滑动手势来启动Expos视…...
数据结构-单链表(C语言简单实现)
简介 以顺序结构进行数据存储时,它的特点就是可以用一组任意的存储单元存储数据元素,这组存储单元可以是连续的,也可以是不连续的,这些数据可以存在内存未被占用的任意位置。它也是有缺点的,就是在插入和删除时需要移…...
.netcore grpc身份验证和授权
一、鉴权和授权(grpc专栏结束后会开启鉴权授权专栏欢迎大家关注) 权限认证这里使用IdentityServer4配合JWT进行认证通过AddAuthentication和AddAuthorization方法进行鉴权授权注入;通过UseAuthentication和UseAuthorization启用鉴权授权增加…...
分布式 - 服务器Nginx:一小时入门系列之负载均衡
文章目录 1. 负载均衡2. 负载均衡策略1. 轮询策略2. 最小连接策略3. IP 哈希策略4. 哈希策略5. 加权轮询策略 1. 负载均衡 跨多个应用程序实例的负载平衡是一种常用技术,用于优化资源利用率、最大化吞吐量、减少延迟和确保容错配置。使用 nginx 作为非常有效的HT…...
Linux学习之基本指令二
-----紧接上文 在了解cat指令之前,我们首先要了解到Linux下一切皆文件,在学习c语言时我们就已经了解到了 对文件输入以及读入的操作(向显示器打印,从键盘读取数据),对于Linux下文件的操作,也是…...
神经网络基础-神经网络补充概念-41-梯度的数值逼近
概念 梯度的数值逼近是一种用于验证梯度计算正确性的方法,它通过近似计算梯度来与解析计算的梯度进行比较。虽然数值逼近在实际训练中不常用,但它可以用来检查手动或自动求导的实现是否正确。 代码实现 import numpy as np# 定义函数 f(x) x^2 def f…...
tornado在模板中遍历二维数组
要在Tornado模板中遍历一个二维数组,你可以使用Tornado的模板语法来实现迭代和显示数组中的每个元素。 以下是一个示例,演示如何在Tornado模板中遍历和显示二维数组的内容: template.html: <!DOCTYPE html> <html> <head&g…...
前端-初始化Vue3+TypeScript
如果使用如下命令初始化项目,项目很干净,很适合了解项目的各个结构。 npm init vitelatest如果使用如下命令初始化项目,是可以选择你需要的组件 npm init vuelatest...
龙蜥社区安全联盟(OASA)正式成立,启明星辰、绿盟、360 等 23 家厂商重磅加入
7 月 28 日,由启明星辰、绿盟、360、阿里云、统信软件、浪潮信息、中兴通讯|中兴新支点、Intel、中科院软件所等 23 家单位共同发起的龙蜥社区安全联盟(OASA,OpenAnolisSecurityAlliance)(以下简称“安全联…...
Flask-SQLAlchemy
认识Flask-SQLAlchemy Flask-SQLAlchemy 是一个为 Flask 应用增加 SQLAlchemy 支持的扩展。它致力于简化在 Flask 中 SQLAlchemy 的使用。SQLAlchemy 是目前python中最强大的 ORM框架, 功能全面, 使用简单。 ORM优缺点 优点 有语法提示, 省去自己拼写SQL,保证SQL…...
大数据bug-sqoop(二:sqoop同步mysql数据到hive进行字段限制。)
一:sqoop脚本解析。 #!/bin/sh mysqlHost$1 mysqlUserName$2 mysqlUserPass$3 mysqlDbName$4 sql$5 split$6 target$7 hiveDbName$8 hiveTbName$9 partFieldName${10} inputDate${11}echo ${mysqlHost} echo ${mysqlUserName} echo ${mysqlUserPass} ec…...
家庭实验室方案:树莓派控制OpenClaw调用远程Qwen3-32B服务
家庭实验室方案:树莓派控制OpenClaw调用远程Qwen3-32B服务 1. 为什么选择树莓派OpenClaw组合 去年冬天,当我试图用语音控制家里的智能设备时,发现市面上的解决方案要么需要持续联网(隐私堪忧),要么响应延…...
基于图像的深度学习与MVS三维重建全流程服务 支持远程部署定制 含pcl/c++/matlab...
基于图像的深度学习MVS三维重建全流程 可远程部署,可定制 点云pcl,c,matlab开发,基于图像三维重建,点云算法开发 只需要提供摄的图像,即可生成完整的三维模型(大小场景均可)上周去爬了个浙西的小众山&#…...
Java开发必备:高德、百度、WGS84坐标互转实战(附完整代码)
Java开发实战:高德、百度与WGS84坐标系互转解决方案 当你需要在不同地图服务之间切换时,坐标系的差异往往会成为开发中的痛点。想象一下这样的场景:你的应用同时接入了高德地图和百度地图,用户上传的GPS数据却无法在两个平台上准确…...
IP5306电源芯片的‘怪脾气’:实测开机半分钟就休眠?手把手教你两个硬件调试技巧
IP5306电源芯片实战调试:破解自动休眠难题的硬件级方案 实验室里,示波器屏幕上那条本该稳定的电压线突然跌落至零,系统再次陷入休眠——这已经是今天第七次重现IP5306芯片的"怪脾气"。作为一款广泛应用于移动电源的高集成度SOC&…...
RTC成语音AI基础设施:AWS和ElevenLabs相继跟进,ZEGO已跑三年
2026 年 3 月,语音 AI 领域迎来一个值得关注的技术信号:AWS(亚马逊云科技)与 ElevenLabs 在同一个月内相继宣布支持 WebRTC 协议。这一时间上的高度吻合,折射出行业对实时语音交互底层架构的共同判断:传统 …...
Kimi,Minimax教你的客服怎么做客服
Kimi,教你怎么做客服。下面是Kimi根据我提供的图片写的文章。不是说minimax全面领先kimi,至少我在不断的提高自己的kimi会员等级。但是有时候,这是被迫的消耗积分和额度。199的套餐也快消耗完了。消耗积分是应该的,关键是要用在刀…...
保姆级避坑指南:Ubuntu系统下Hadoop HA集群搭建,我踩过的那些SSH和配置文件的“坑”
Ubuntu下Hadoop HA集群搭建:那些教科书不会告诉你的实战陷阱 第一次在Ubuntu上搭建Hadoop HA集群时,我天真地以为照着官方文档就能顺利跑起来。直到SSH连接莫名其妙失败、JournalNode权限报错刷屏、ZKFC死活不启动时,才明白为什么有人说大数据…...
AceCommon:Arduino嵌入式零堆分配轻量C++工具库
1. AceCommon 库概述:面向嵌入式 Arduino 的轻量级底层工具集AceCommon 是一个专为资源受限的微控制器平台(尤其是 Arduino 生态)设计的零依赖、低开销 C 工具库。其核心设计哲学是“小而精、无侵入、可复用”。与常见的功能臃肿、依赖繁杂的…...
OWASP靶场实战指南:从环境搭建到第一个SQL注入漏洞挖掘(含DVWA通关思路)
OWASP靶场实战指南:从环境搭建到第一个SQL注入漏洞挖掘 网络安全的世界就像一片未知的海洋,而靶场就是我们练习游泳的安全泳池。对于刚入门的新手来说,最大的困扰往往不是缺乏理论知识,而是不知道如何将所学付诸实践。OWASP靶场正…...
告别调参玄学:手把手教你用‘黎卡提方程’为自动驾驶LQR控制器选择Q和R矩阵
自动驾驶轨迹跟踪实战:从黎卡提方程到LQR调参的工程化思考 当你在仿真环境中第一次看到自己设计的LQR控制器让车辆完美跟踪参考轨迹时,那种成就感难以言喻。但更多时候,我们面对的是震荡的超调曲线、缓慢的收敛速度,以及令人抓狂的…...
