Learn QOpenGL 读取obj模型

news2024/11/6 0:26:52
/* 
**  File name:   OpenGLModelWidget.h
**  Author:      
**  Date:        2024-10-31
**  Brief:       读取模型文件并渲染的OpenGL控件
**  Copyright (C) 1392019713@qq.com All rights reserved.
*/

#ifndef OpenGLModelWidget_H
#define OpenGLModelWidget_H

#include <QOpenGLWidget>
#include <QOpenGlFunctions_3_3_Core>
#include <QMatrix4x4>
#include <QVector2D>
#include <QVector3D>
#include <QMouseEvent>
#include <array>
#include "glm/glm.hpp"
#include "glm/ext.hpp"

/*
*@brief 定义顶点、法线、纹理坐标、面数据结构
*/
struct Vertex {
	glm::vec3 position;
	glm::vec3 normal;
};

/*
*@brief 读取模型文件并渲染的OpenGL控件
*/
class COpenGLModelWidget : public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
	Q_OBJECT
public:
	COpenGLModelWidget(QWidget*parent = nullptr);

	virtual ~COpenGLModelWidget();

	/*
	* @brief 加载模型文件
	* @param strModelFile 模型文件路径
	* @return 无
	*/
	void LoadOBJModel(const std::string& strModelFile);

	void SetShaderFilePath(const std::string& strVertFilePath, const std::string& strfragFilePath); //设置着色器文件路径

protected:
	void initializeGL();

	void paintGL();

	void resizeGL(int width, int height);

	void mouseMoveEvent(QMouseEvent* event);

	void mousePressEvent(QMouseEvent* event);

	void wheelEvent(QWheelEvent* event);

	void mouseReleaseEvent(QMouseEvent* event);

	void keyPressEvent(QKeyEvent* event);

	void keyReleaseEvent(QKeyEvent* event);

private:
	void AutoNormal(); //计算法线

	std::string GetFileConTent(const std::string& strFilePath) const;

	glm::vec3 ComputeNormalBiased(glm::vec3 const& a, glm::vec3 const& b,
		glm::vec3 const& c); //计算平滑法线

	glm::mat4x4 GetViewMatrix() const; //视图矩阵

	glm::mat4x4 GetProjectionMatrix() const; //投影矩阵

	void NormalizeAngle(int &nAngle); //修正角度

	void Orbit(glm::vec2 delta, bool isDrift); //轨道旋转

	void Zoom(float delta, bool isHitchcock); //缩放

	void Pan(glm::vec2 delta);//平移

private:
	std::vector<Vertex> m_vecVertices; //顶点数据

	std::vector<glm::uvec3> m_vecFaces; //面数据

	unsigned int m_unVAO; //顶点数组对象
	unsigned int m_unVBO; //顶点缓冲对象
	unsigned int m_unEBO; //索引缓冲对象
	unsigned int m_unShaderProgram; //着色器程序

	std::string m_strFragFilePath; //片元着色器文件路径
	std::string m_strVertFilePath; //顶点着色器文件路径

	glm::vec2 lastpos;
	bool moving = false;
	glm::vec3 eye = { 0, 0, 5 };
	glm::vec3 lookat = { 0, 0, 0 };
	glm::vec3 up_vector = { 0, 1, 0 };
	glm::vec3 keep_up_axis = { 0, 1, 0 };
	float focal_len = 40.0f;
	float film_height = 24.0f;
	float film_width = 32.0f;
	float zoom_speed = 0.2f;
	float orbit_speed = 1.0f;
	float drift_speed = 1.0f;
	float pan_speed = 2.0f;
	int zoom_axis = 1;

	glm::vec2 mousePos;
};

#endif // !

#include "../Include/OpenGLModelWidget.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <QMatrix3x3>
    
COpenGLModelWidget::COpenGLModelWidget(QWidget* parent)
	: QOpenGLWidget(parent)
{

}

COpenGLModelWidget::~COpenGLModelWidget()
{
    makeCurrent(); 
    glDeleteBuffers(1, &m_unVBO);
    glDeleteVertexArrays(1, &m_unVAO);
    glDeleteProgram(m_unShaderProgram);
    doneCurrent();
}

void COpenGLModelWidget::LoadOBJModel(const std::string& strModelFile)
{
    std::ifstream file(strModelFile);
    if (!file.is_open()) {
        std::cerr << "Failed to open file: " << strModelFile << std::endl;
        return;
    }

    std::string line;
    while (std::getline(file, line)) 
    {
        if (line.substr(0, 2) == "v ") //顶点
        {
            std::istringstream s(line.substr(2));
            glm::vec3 vertex;
            s >> vertex.x >> vertex.y >> vertex.z;
            m_vecVertices.push_back({ vertex, {} });
        }
        else if (line.substr(0, 3) == "vn ") //法线  可以不读法线数据自己计算
        {
            std::istringstream s(line.substr(2));
            //stVec3 vec3;
            //s >> vec3.x >> vec3.y >> vec3.z;
            //m_vecVNormals.push_back(vec3);
            //m_vecVertices.push_back(vec3);
        }
        else if (line.substr(0, 3) == "vt ") //纹理坐标
        {
            //std::istringstream s(line.substr(2));
            //stVec2 vec2;
            //s >> vec2.x >> vec2.y;
            //m_vecVTextures.push_back(vec2);
        }
        else if (line.substr(0, 2) == "f ") //面
        {
            std::istringstream s(line.substr(2));
            std::string splitted;
            std::vector<unsigned int> indices;
            while (std::getline(s, splitted, ' '))
            {
                unsigned int index = 1;
                std::istringstream(splitted) >> index;
                indices.push_back(index - 1);
            }
            for (size_t i = 2; i < indices.size(); i++) 
            {
                glm::uvec3 face =
                    glm::uvec3(indices[0], indices[i - 1], indices[i]);
                m_vecFaces.push_back(face);
            }
        }
    }

    file.close();
    std::cout << strModelFile << ": Loaded " << m_vecVertices.size() << " vertices, "
        << m_vecFaces.size() << " faces.\n";
   AutoNormal();
}

void COpenGLModelWidget::SetShaderFilePath(const std::string& strVertFilePath, const std::string& strFragFilePath)
{
    m_strVertFilePath = strVertFilePath;    
    m_strFragFilePath = strFragFilePath;
}

void COpenGLModelWidget::initializeGL()
{
	initializeOpenGLFunctions(); // 必须在初始化之前调用

    glEnable(GL_DEPTH_TEST); // 开启深度测试
    glEnable(GL_MULTISAMPLE); // 开启多重采样抗锯齿
    glEnable(GL_BLEND); // 开启混合
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置混合函数
    glEnable(GL_CULL_FACE); // 开启背面剔除
    glCullFace(GL_BACK); // 设置背面剔除策略
    glFrontFace(GL_CCW); // 设置正面朝向为CCW

    m_unShaderProgram = glCreateProgram(); // 创建着色器程序

    unsigned int unVertexShader = glCreateShader(GL_VERTEX_SHADER); // 创建顶点着色器
    std::string str = GetFileConTent(m_strVertFilePath);
    const char* srcList[1] = { str.c_str() };
    int srcLenList[1] = { (int)str.size() };
    glShaderSource(unVertexShader, 1, srcList, srcLenList);
    glCompileShader(unVertexShader); // 编译顶点着色器
    int nCompileStatus;
    glGetShaderiv(unVertexShader, GL_COMPILE_STATUS, &nCompileStatus);
    if (nCompileStatus == GL_FALSE) {
        int infoLen = 0;
        glGetShaderiv(unVertexShader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char* infoLog = new char[infoLen];
            glGetShaderInfoLog(unVertexShader, infoLen, NULL, infoLog);
            std::cerr << "Vertex shader compile error: " << infoLog << std::endl;
            delete[] infoLog;
        }
        glDeleteShader(unVertexShader);
        return;
    }
    glAttachShader(m_unShaderProgram, unVertexShader);  // 将顶点着色器附着到程序中

    unsigned int unFragmentShader = glCreateShader(GL_FRAGMENT_SHADER); // 创建片元着色器
    str = GetFileConTent(m_strFragFilePath);
    srcList[0] = str.c_str();
    srcLenList[0] = (int)str.size();
    glShaderSource(unFragmentShader, 1, srcList, srcLenList); // 设置片元着色器源代码
    glCompileShader(unFragmentShader); // 编译片元着色器
    glGetShaderiv(unFragmentShader, GL_COMPILE_STATUS, &nCompileStatus); // 获取编译状态
    if (nCompileStatus == GL_FALSE) {
        int nInfoLen = 0;
        glGetShaderiv(unFragmentShader, GL_INFO_LOG_LENGTH, &nInfoLen); // 获取信息长度
        if (nInfoLen > 1) {
            char* infoLog = new char[nInfoLen];
            glGetShaderInfoLog(unFragmentShader, nInfoLen, NULL, infoLog); // 获取信息日志
            std::cerr << "Fragment shader compile error: " << infoLog << std::endl;
            delete[] infoLog;
        }
        glDeleteShader(unFragmentShader);
        return;
    }
    glAttachShader(m_unShaderProgram, unFragmentShader); // 将片元着色器附着到程序中

    glLinkProgram(m_unShaderProgram); // 链接程序
    glGetProgramiv(m_unShaderProgram, GL_LINK_STATUS, &nCompileStatus);
    if (nCompileStatus == GL_FALSE)
    {
        int nInfoLen = 0;
        glGetShaderiv(unFragmentShader, GL_INFO_LOG_LENGTH, &nInfoLen); // 获取信息长度
        if (nInfoLen > 1) {
            char* infoLog = new char[nInfoLen];
            glGetShaderInfoLog(unFragmentShader, nInfoLen, NULL, infoLog); // 获取信息日志
            std::cerr << "glAttachShader fail " << infoLog << std::endl;
            delete[] infoLog;
        }
        glDeleteShader(unFragmentShader);
        return;
    }

    glDeleteShader(unVertexShader); // 删除顶点着色器
    glDeleteShader(unFragmentShader); // 删除片元着色器

    glGenVertexArrays(1, &m_unVAO); // 获取缓冲区
    glGenBuffers(1, &m_unVBO); // 创建顶点缓冲对象

    glBindVertexArray(m_unVAO); // 绑定VAO
    glBindBuffer(GL_ARRAY_BUFFER, m_unVBO); // 绑定顶点缓冲对象
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * m_vecVertices.size(), m_vecVertices.data(), GL_STATIC_DRAW); // 填充顶点缓冲对象

    glGenBuffers(1, &m_unEBO); // 创建索引缓冲对象
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_unEBO); // 绑定索引缓冲对象
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::array<unsigned int, 3>) * m_vecFaces.size(), m_vecFaces.data(), GL_STATIC_DRAW); // 填充索引缓冲对象

   
    
    glEnableVertexAttribArray(0); // 启用顶点属性数组
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position)); // 设置顶点属性指针

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); // 设置顶点属性指针

    glBindBuffer(GL_ARRAY_BUFFER, 0);  // 解绑缓冲对象
    glBindVertexArray(0); // 解绑VAO
}   

void COpenGLModelWidget::paintGL()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 设置背景颜色
    
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // 清除深度缓冲和颜色缓冲
    
    auto projection = GetProjectionMatrix();
    auto view = GetViewMatrix();
    glm::mat4x4 model(1.0f);
    glUseProgram(m_unShaderProgram); // 使用着色器程序
   

    glUniformMatrix4fv(glGetUniformLocation(m_unShaderProgram, "uniModel"), 1, GL_FALSE, glm::value_ptr(model)); // 设置模型矩阵
    glUniformMatrix4fv(glGetUniformLocation(m_unShaderProgram, "uniView"), 1, GL_FALSE, glm::value_ptr(view)); // 设置视图矩阵
    glUniformMatrix4fv(glGetUniformLocation(m_unShaderProgram, "uniProjection"), 1, GL_FALSE, glm::value_ptr(projection)); // 设置投影矩阵

    glm::vec3 lightDir = glm::normalize(glm::vec3(mousePos.x, mousePos.y, 1));
    int location = glGetUniformLocation(m_unShaderProgram, "uniLightDir");
    glUniform3fv(location, 1, glm::value_ptr(lightDir));
    
    glBindVertexArray(m_unVAO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_unEBO);
    glDrawElements(/*mode=*/GL_TRIANGLES, /*count=*/m_vecFaces.size() * 3,
        /*type=*/GL_UNSIGNED_INT, /*indices=*/(void*)0); // 绘制模型
}

void COpenGLModelWidget::resizeGL(int width, int height)
{
    glViewport(0, 0, width, height);    // 设置视口大小
}


void COpenGLModelWidget::mouseMoveEvent(QMouseEvent* event)
{
    if (event) 
    {
        double xpos = event->x();
        double ypos = event->y();
        float x = (float)(2 * xpos / width() - 1);
        float y = (float)(2 * (height() - ypos) / height() - 1);
        glm::vec2 pos(x, y);
        mousePos = pos;
        moving = true;
        auto delta = glm::fract((pos - lastpos) * 0.5f + 0.5f) * 2.0f - 1.0f;
        if (event->buttons() == Qt::LeftButton && event->modifiers() == Qt::NoModifier)
        {
            Orbit(delta, false);
        }
        else if (event->buttons() == Qt::LeftButton && event->modifiers() == Qt::ControlModifier)
        {
            Orbit(delta, true);
        }
        else if (event->buttons() == Qt::LeftButton && event->modifiers() == Qt::ShiftModifier)
        {
            Pan(delta);
        }
        else 
        {
            moving = false;
        }
        lastpos = pos;
        update();
    }
}

void COpenGLModelWidget::Orbit(glm::vec2 delta, bool isDrift)
{
    if (isDrift) {
        delta *= -drift_speed;
        delta *= std::atan(film_height / (2 * focal_len));
    }
    else {
        delta *= orbit_speed;
    }

    auto angle_X_inc = delta.x;
    auto angle_Y_inc = delta.y;

    // pivot choose: drift mode rotates around eye center, orbit mode rotates around target object
    auto rotation_pivot = isDrift ? eye : lookat;

    auto front_vector = glm::normalize(lookat - eye);

    // new right vector (orthogonal to front, up)
    auto right_vector = glm::normalize(glm::cross(front_vector, up_vector));

    // new up vector (orthogonal to right, front)
    up_vector = glm::normalize(glm::cross(right_vector, front_vector));

    // rotation 1: based on the mouse horizontal axis
    glm::mat4x4 rotation_matrixX = glm::rotate(glm::mat4x4(1), -angle_X_inc, up_vector);

    // rotation 2: based on the mouse vertical axis
    glm::mat4x4 rotation_matrixY = glm::rotate(glm::mat4x4(1), angle_Y_inc, right_vector);

    // translate back to the origin, rotate and translate back to the pivot location
    auto transformation = glm::translate(glm::mat4x4(1), rotation_pivot)
        * rotation_matrixY * rotation_matrixX
        * glm::translate(glm::mat4x4(1), -rotation_pivot);

    // update eye and lookat coordinates
    eye = glm::vec3(transformation * glm::vec4(eye, 1));
    lookat = glm::vec3(transformation * glm::vec4(lookat, 1));

    // try to keep the camera horizontal line correct (eval right axis error)
    float right_o_up = glm::dot(right_vector, keep_up_axis);
    float right_handness = glm::dot(glm::cross(keep_up_axis, right_vector), front_vector);
    float angle_Z_err = glm::asin(right_o_up);
    angle_Z_err *= glm::atan(right_handness);
    // rotation for up: cancel out the camera horizontal line drift
    glm::mat4x4 rotation_matrixZ = glm::rotate(glm::mat4x4(1), angle_Z_err, front_vector);
    up_vector = glm::mat3x3(rotation_matrixZ) * up_vector;
}

void COpenGLModelWidget::Zoom(float delta, bool isHitchcock)
{
    float inv_zoom_factor = glm::exp(- zoom_speed * delta);
    eye = (eye - lookat) * inv_zoom_factor + lookat;
    if (isHitchcock) {
        focal_len *= inv_zoom_factor;
        /* printf("focalLen: %.1f mm, FoVx: %.1f deg, FoVy: %.1f deg, Distance: %.1f m\n", focal_len, */
        /*        glm::degrees(2 * glm::atan(film_width / (2 * focal_len))), */
        /*        glm::degrees(2 * glm::atan(film_height / (2 * focal_len))), */
        /*        glm::length(eye - lookat)); */
    }
}

void COpenGLModelWidget::Pan(glm::vec2 delta)
{
    delta *= -pan_speed;

    auto front_vector = glm::normalize(lookat - eye);
    auto right_vector = glm::normalize(glm::cross(front_vector, up_vector));
    auto fixed_up_vector = glm::normalize(glm::cross(right_vector, front_vector));

    auto delta3d = delta.x * right_vector + delta.y * fixed_up_vector;

    eye += delta3d;
    lookat += delta3d;
}

void COpenGLModelWidget::mousePressEvent(QMouseEvent* event)
{
    if (event) 
    {
        double xpos = event->x();
        double ypos = event->y();
        float x = (float)(2 * xpos / width() - 1);
        float y = (float)(2 * (height() - ypos) / height() - 1);
        mousePos.x = x;
        mousePos.y = y;
    }
}

void COpenGLModelWidget::wheelEvent(QWheelEvent* event)
{
    if(event)
    {
        double xoffset = event->angleDelta().x();
        double yoffset = event->angleDelta().y();
        float deltax = xoffset < 0 ? -1 : xoffset > 0 ? 1 : 0;
        float deltay = yoffset < 0 ? -1 : yoffset > 0 ? 1 : 0;
        glm::vec2 delta(deltax, deltay);
        if(event->modifiers() == Qt::ShiftModifier) 
        {
            Zoom(delta[zoom_axis], true);
        } 
        else if(event->modifiers() == Qt::NoModifier)
        {
            Zoom(delta[zoom_axis], false);
        }
        else 
        {
            moving = false;
        }
        update();
    }
}

void COpenGLModelWidget::mouseReleaseEvent(QMouseEvent* event)
{
    Q_UNUSED(event);
}

void COpenGLModelWidget::keyPressEvent(QKeyEvent* event)
{
 
}

void COpenGLModelWidget::keyReleaseEvent(QKeyEvent* event)
{
 
}

std::string COpenGLModelWidget::GetFileConTent(const std::string& strFilePath) const
{
    std::ifstream file(strFilePath);
    if (!file.is_open())
    {
        std::cerr << "Failed to open file: " << strFilePath << std::endl;
        return {};
    }
    return std::string{
        std::istreambuf_iterator<char>(file),
            std::istreambuf_iterator<char>()};
}

glm::vec3 COpenGLModelWidget::ComputeNormalBiased(glm::vec3 const& a, glm::vec3 const& b,
    glm::vec3 const& c)
{
    // 计算三角形法线,带asin项加权的版本
    glm::vec3 ab = b - a;
    glm::vec3 ac = c - a;
    glm::vec3 n = glm::cross(ab, ac); //返回向量 v1 和 v2 的叉积
    auto nlen = glm::length(n);
    if (nlen != 0) [[likely]]
    {
        auto labc = glm::length(ab) * glm::length(ac);
        if (labc >= nlen) [[likely]]
        {
            n *= glm::asin(nlen / labc) / nlen;
        }
        else
        {
            n *= 1.0f / nlen;
        }
    }
    return n;
}

glm::mat4x4 COpenGLModelWidget::GetViewMatrix() const
{
    return glm::lookAt(eye, lookat, up_vector);
}

glm::mat4x4 COpenGLModelWidget::GetProjectionMatrix() const
{
    auto fov = 2 * std::atan(film_height / (2 * focal_len));
    auto aspect = (float)width() / height();
    return glm::perspective(fov, aspect, 0.01f, 100.0f);
}

void COpenGLModelWidget::NormalizeAngle(int& nAngle)
{
    while (nAngle < 0)
    {
        nAngle += 360 * 16;
    }
    while (nAngle > 360 * 16)
    {
        nAngle -= 360 * 16;
    }
}


void COpenGLModelWidget::AutoNormal()
{
    for (auto& v : m_vecVertices)
    {
        v.normal = glm::vec3();
    }

    for (auto const& face : m_vecFaces) {
        auto& a = m_vecVertices[face[0]];
        auto& b = m_vecVertices[face[1]];
        auto& c = m_vecVertices[face[2]];
        a.normal += ComputeNormalBiased(a.position, b.position, c.position);
        b.normal += ComputeNormalBiased(b.position, c.position, a.position);
        c.normal += ComputeNormalBiased(c.position, a.position, b.position);
    }
    for (auto& v : m_vecVertices) 
    {
        v.normal = glm::normalize(v.normal);
    }
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++入门基础知识133—【关于C 库函数 - asctime()】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 库函数 - asctime()的相关内容&#x…

Java版企电子招标采购系统源业码Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

沟通工具选错了,团队效率会下降多少?

沟通工具的选择对团队的工作效率和协作能力有着直接影响。选用不当的沟通工具可能导致信息传递不畅、工作效率低下、团队士气下降等严重后果。具体来说&#xff0c;沟通不畅会造成误解、信息延迟和团队合作困难。例如&#xff0c;使用不适合团队规模和工作流程的工具&#xff0…

ElasticSearch - Bucket Script 使用指南

文章目录 官方文档Bucket Script 官文1. 什么是 ElasticSearch 中的 Bucket Script&#xff1f;2. 适用场景3. Bucket Script 的基本结构4. 关键参数详解5. 示例官方示例&#xff1a;计算每月 T 恤销售额占总销售额的比率百分比示例计算&#xff1a;点击率 (CTR) 6. 注意事项与…

Java中IO的高级操作

目录 缓冲流 缓冲字节输入流&#xff1a; 缓冲字节输出流&#xff1a; 缓冲字符输入流&#xff1a; 缓冲字符输出流&#xff1a; 转换流 转换流字符输入&#xff1a; 转换流字符输出&#xff1a; 练习案例&#xff1a; 打印流 字节打印流&#xff1a; 字符打印流&a…

VsCode中搭建 Rt-Thread 开发环境(编译,调试,下载)

VsCode中搭建 Rt-Thread 开发环境&#xff08;编译&#xff0c;调试&#xff0c;下载&#xff09; 文章目录 VsCode中搭建 Rt-Thread 开发环境&#xff08;编译&#xff0c;调试&#xff0c;下载&#xff09;一 . 下载rt-thread源码二. 安装env脚本环境三 . 配置工具链3.1 使用…

Pinctrl子需要中client端使用pinctrl过程的驱动分析

往期内容 本专栏往期内容&#xff1a; Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析&#xff1a;imx_pinctrl_soc_info结构体 input子系统专栏…

初探Flink的序列化

Flink中的序列化应用场景 程序通常使用(至少)两种不同的数据表示形式[2]&#xff1a; 1. 在内存中&#xff0c;数据保存在对象、结构体、列表、数组、哈希表和树等结构中。 2. 将数据写入文件或通过网络发送时&#xff0c;必须将其序列化为字节序列。 从内存中的表示到字节序列…

项目一:使用 Spring + SpringMVC + Mybatis + lombok 实现网络五子棋

一&#xff1a;系统展示: 二&#xff1a;约定前后端接口 2.1 登陆 登陆请求&#xff1a; GET /login HTTP/1.1 Content-Type: application/x-www-form-urlencodedusernamezhangsan&password123登陆响应&#xff1a; 正常对象&#xff1a;正常对象会在数据库中存储&…

CentOS7配置静态IP(非解决方法指导,纯笨蛋记录)

一、这篇博客算是记录我终于解决我安装的虚拟机ping不通外网的问题&#xff0c;前前后后我尝试了很多次花了很多时间&#xff0c;最后弄完发现其实都是因为我之前根本不知道什么是虚拟机的网络配置。。。。。 这个链接介绍了vmware虚拟机三种网络模式及配置详解_vmware 特定虚…

opencv-day2-图像预处理1

图像预处理 在计算机视觉和图像处理领域&#xff0c;图像预处理能够提高后续处理&#xff08;如特征提取、目标检测等&#xff09;的准确性和效率。 常见的图像预处理操作&#xff1a; 图像色彩空间转换 图像大小调整 图像仿射变换 图像翻转 图像裁剪 图像二值化处理 图…

3DDFA-V3——基于人脸分割几何信息指导下的三维人脸重建

1. 研究背景 从二维图像中重建三维人脸是计算机视觉研究的一项关键任务。在虚拟现实、医疗美容、计算机生成图像等领域中&#xff0c;研究人员通常依赖三维可变形模型&#xff08;3DMM&#xff09;进行人脸重建&#xff0c;以定位面部特征和捕捉表情。然而&#xff0c;现有的方…

Ubuntu系统如何实现键盘按键映射到其他按键(以 Ctrl+c 映射到 F3,Ctrl+v 映射到 F4 为例)

文章目录 写在前面1. 功能描述2. 实现步骤2.1 安装AutoKey2.2 软件设置2.2.1 软件设置 2.3 测试是否安装成功 参考链接 写在前面 自己的测试环境&#xff1a; Ubuntu20.04 1. 功能描述 Ubuntu系统使用Ctrlc 、Ctrlv 进行复制粘贴操作的时候&#xff0c;时间长了就会出现小拇指…

【Clickhouse】客户端连接工具配置

ClickHouse 是什么 ClickHouse 是一个分布式实时分析型列式存储数据库。具备高性能&#xff0c;支撑PB级数据&#xff0c;提供实时分析&#xff0c;稳定可扩展等特性。适用于数据仓库、BI报表、监控系统、互联网用户行为分析、广告投放业务以及工业、物联网等分析和时序应用场…

postman的脚本设置接口关联

pm常用的对象 变量基础知识 postman获取响应结果的脚本的编写 下面是购物场景存在接口信息的关联 登录进入---搜索商品---进入商品详情---加入购物车 资源在附件中&#xff0c;可以私聊单独发送 postman的SHA256加密 var CryptoJS require(crypto-js);// 需要加密的字符串 …

Qt 文件目录操作

Qt 文件目录操作 QDir 类提供访问系统目录结构 QDir 类提供对目录结构及其内容的访问。QDir 用于操作路径名、访问有关路径和文件的信息以及操作底层文件系统。它还可以用于访问 Qt 的资源系统。 Qt 使用“/”作为通用目录分隔符&#xff0c;与“/”在 URL 中用作路径分隔符…

qt QCheckBox详解

QCheckBox 是 Qt 框架中的一个控件&#xff0c;用于创建复选框&#xff0c;允许用户进行选择和取消选择。它通常用于表单、设置界面和任何需要用户选择的场景。 QCheckBox继承自QAbstractButton类&#xff0c;因此继承了按钮的特性。它表示一个复选框&#xff0c;用户可以通过…

读数据工程之道:设计和构建健壮的数据系统26数据建模

1. 数据建模 1.1. 良好的数据架构必须反映出使用这些数据的组织的业务目标和业务逻辑 1.2. 数据湖1.0、NoSQL和大数据系统的兴起&#xff0c;使工程师们有时是为了合理的性能提升去忽略传统的数据建模 1.3. 数据在企业中的地位急剧上升&#xff0c;人们越来越认识到&#xf…

2025生物发酵展(济南)为生物制造产业注入新活力共谱行业新篇章

2025第十四届国际生物发酵展将于3月3-5日济南盛大举办&#xff01;产业链逐步完整&#xff0c;展会面积再创历史新高&#xff0c;展览面积较上届增涨至60000平方米&#xff0c;专业观众40000&#xff0c;品牌展商800&#xff0c;同期活动会议增加至50场&#xff0c;展会同期将举…

Windows版 nginx安装,启动,目录解析,常用命令

Windows版 nginx安装&#xff0c;启动&#xff0c;目录解析&#xff0c;常用命令 一级目录二级目录三级目录 1. 下载2. 启动方式一&#xff1a;方式二&#xff1a; 3. 验证是否启动4. 安装目录解析5. 常用命令 一级目录 二级目录 三级目录 1. 下载 官网下载&#xff1a;ngi…