上接:https://blog.csdn.net/weixin_44506615/article/details/150465200?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl

接下来我们通过加载模型文件的方式来导入我们要渲染的模型,取代之前的硬编码顶点的箱子

一、Assimp库

像图片、音视频等文件,模型文件也有各种各样的格式,像是 .obj、.fbx 等,要加载并解析它们的逻辑也各不相同,接下来我们就要用到Assimp库(Open Asset Import Library)
Assimp库在不同的模型格式上进行抽象,我们使用统一的接口就可以加载不同格式的模型
当Assimp导入一个模型的时候,它会将整个模型加载进一个场景对象(aiScene),其中还包含根节点和任意数量的子节点,下图是Assimp数据结构的简化模型 (图片来自于LearnOpenGL)
Assimp
接下来我们去github上拉取代码 https://github.com/assimp/assimp,这里我使用了和LearnOpenGL一样的3.1.1版本
拉取完毕后,使用cmake创建sln编译,并在我们的项目中配置include目录lib目录以及链接器输入即可

二、网格 (Mesh)

接下来我们来创建一个网格类,用于存储加载后的网格数据
首先创建顶点结构体
Vertex.h 新建

#pragma once#include <glm.hpp>/**
* 顶点
*/
struct Vertex
{/*** 位置*/glm::vec3 position;/*** 法向量*/glm::vec3 normal;/*** UV*/glm::vec2 texCoord;
};

接下来是网格类
Mesh.h 新建

#pragma once#include <vector>#include "Vertex.h"
#include "Texture.h"
#include "Shader.h"/**
* 网格
*/
class Mesh
{
public:/*** 顶点*/std::vector<Vertex> vertices;/*** 索引*/std::vector<unsigned int> indices;/*** 纹理*/std::vector<Texture> textures;Mesh(std::vector<Vertex> _vertices, std::vector<unsigned int> _indices, std::vector<Texture> _textures);/*** 绘制*/void Draw(const Shader& shader);private:/*** 缓冲*/unsigned int VAO, VBO, EBO;/*** 创建缓冲*/void CreateBuffer();
};

Mesh.cpp 新建

#include "Mesh.h"
#include <glad/glad.h>
#include <assert.h>Mesh::Mesh(std::vector<Vertex> _vertices, std::vector<unsigned int> _indices, std::vector<Texture> _textures)
{vertices = _vertices;indices = _indices;textures = _textures;// 创建三缓冲CreateBuffer();
}void Mesh::Draw(const Shader& shader)
{// 纹理索引计数unsigned int diffuseNum = 0;unsigned int specularNum = 0;for (int i = 0; i < textures.size(); ++i){glActiveTexture(GL_TEXTURE0 + i);// 这里计算应该用到第几张纹理了std::string number;Texture texture = textures[i];if (texture.type == TextureType::DIFFUSE){number = std::to_string(++diffuseNum);}else if (texture.type == TextureType::SPECULAR){number = std::to_string(++specularNum);}else{assert(false);}// 着色器中的纹理名std::string name = "material." + texture.GetTypeName() + number;shader.SetInt(name, i);glBindTexture(GL_TEXTURE_2D, texture.GetTextureID());}// 绑定缓冲glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBindVertexArray(VAO);//绘制glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);// 解绑glBindVertexArray(0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}void Mesh::CreateBuffer()
{// 创建缓冲glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);// 绑定VAOglBindVertexArray(VAO);// 绑定顶点数据glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);// 绑定索引数据glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);// 设置顶点属性glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, false, sizeof(Vertex), (void*)offsetof(Vertex, normal));glEnableVertexAttribArray(2);glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (void*)offsetof(Vertex, texCoord));// 解绑glBindVertexArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

这里我们需要对着色器中纹理采样器命名进行修改,由于在渲染前我们并不知道到底需要多少个sampler2D,所以我们会进行预定义,如下

struct Material {sampler2D tex_diffuse1;sampler2D tex_diffuse2;// ...sampler2D tex_specular1;sampler2D tex_specular2;// ...float shininess;
};

然后在绑定纹理的时候统计diffuseNum和specularNum来设置到对应的槽位上
对此,Texture类也增加了一个方法来根据类型返回不同的前缀名
Texture.h

public:/*** 获取纹理类型的字段名称*/std::string GetTypeName();

Texture.cpp

std::string Texture::GetTypeName()
{if (type == TextureType::DIFFUSE){return "tex_diffuse";}else if (type == TextureType::SPECULAR){return "tex_specular";}else{assert(false);return "";}
}

网格类创建好之后,接下来我们就该创建模型类并加载模型来渲染了

三、模型 (Model)

首先我们需要下载一个模型资源供后续使用,可以在这里下载到一个来自于Berk Gedik设计的吉他生存背包模型,如果以上链接点开没有反应的话,可以右键复制到迅雷中下载,或是在顶部的git仓库中的Resource目录中找到

解压后我们可以得到一个obj文件和几张纹理
obj

接下来我们确定一下模型类的结构
Model.h 新建

#pragma once#include <vector>#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>#include "Shader.h"
#include "Mesh.h"
#include "Texture.h"/**
* 模型类
*/
class Model
{
public:Model(const char* path);/*** 绘制*/void Draw(Shader shader);private:/*** 网格列表*/std::vector<Mesh> meshes;/*** 文件目录*/std::string directory;/*** 已加载的纹理列表*/std::vector<Texture> textures_loaded;/*** 加载模型*/void LoadModel(std::string path);/*** 处理节点*/void ProcessNode(aiNode* node, const aiScene* scene);/*** 处理网格*/Mesh ProcessMesh(aiMesh* mesh, const aiScene* scene);/*** 加载纹理*/std::vector<Texture> LoadMaterialTextures(aiMaterial* mat, aiTextureType aiTexType, TextureType texType);
};

在实现之前,我们先捋一下加载的流程
首先我们使用Assimp::ImporterReadFile方法加载我们的obj文件得到场景aiScene,接着获取到了 aiNode根节点(mRootNode)

接下来使用 ProcessNode 方法来递归解析aiNode,遍历其中的模型aiMesh

使用 ProcessMesh 方法来解析aiMesh,获取其中的顶点mVertices法向量mNormalsUV坐标 mTextureCoords索引数组 mIndices
最后,获取aiMesh中的 aiMaterial 并进行纹理的加载

#include "Model.h"Model::Model(const char* path)
{LoadModel(path);
}void Model::Draw(Shader shader)
{for (unsigned int i = 0; i < meshes.size(); ++i){meshes[i].Draw(shader);}
}void Model::LoadModel(std::string path)
{Assimp::Importer importer;// aiProcess_Triangulate 如果模型不完全是三角面组成,需要转化为三角面// aiProcess_FlipUVs OpenGL中需要翻转UVconst aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);// AI_SCENE_FLAGS_INCOMPLETE 是否未加载完全if (!scene || scene->mFlags * AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode){std::cout << "[Error] Assimp: " << importer.GetErrorString() << std::endl;return;}// 获取纹理文件的根路径directory = path.substr(0, path.find_last_of('/'));// 处理根节点ProcessNode(scene->mRootNode, scene);
}void Model::ProcessNode(aiNode* node, const aiScene* scene)
{// 遍历Meshfor (unsigned int i = 0; i < node->mNumMeshes; ++i){aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];meshes.push_back(ProcessMesh(mesh, scene));}// 遍历子节点for (unsigned int i = 0; i < node->mNumChildren; ++i){ProcessNode(node->mChildren[i], scene);}
}Mesh Model::ProcessMesh(aiMesh* mesh, const aiScene* scene)
{std::vector<Vertex> vertices;std::vector<unsigned int> indices;std::vector<Texture> textures;for (unsigned int i = 0; i < mesh->mNumVertices; ++i){Vertex vertex;// 顶点位置vertex.position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);// 法向量if (mesh->HasNormals()){vertex.normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);}// UVif (mesh->mTextureCoords[0]){vertex.texCoord = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);}else{vertex.texCoord = glm::vec2(0.0f, 0.0f);}vertices.push_back(vertex);}for (unsigned int i = 0; i < mesh->mNumFaces; ++i){// Face即为图元PrimitiveaiFace face = mesh->mFaces[i];for (unsigned int j = 0; j < face.mNumIndices; ++j){indices.push_back(face.mIndices[j]);}}// 加载材质和纹理if (mesh->mMaterialIndex >= 0){aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];std::vector<Texture> diffuseMaps = LoadMaterialTextures(material, aiTextureType_DIFFUSE, TextureType::DIFFUSE);textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());std::vector<Texture> specularMaps = LoadMaterialTextures(material, aiTextureType_SPECULAR, TextureType::SPECULAR);textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());}return Mesh(vertices, indices, textures);
}std::vector<Texture> Model::LoadMaterialTextures(aiMaterial* mat, aiTextureType aiTexType, TextureType texType)
{std::vector<Texture> texturesOutput;for (unsigned int i = 0; i < mat->GetTextureCount(aiTexType); ++i){// 获取纹理名称aiString str;mat->GetTexture(aiTexType, i, &str);std::string path = directory + '/' + std::string(str.C_Str());// 判断是否已加载,避免重复加载bool skip = false;for (unsigned int j = 0; j < textures_loaded.size(); ++j){if (std::strcmp(textures_loaded[j].path.data(), path.data()) == 0){skip = true;texturesOutput.push_back(textures_loaded[j]);break;}}// 加载纹理if (!skip){Texture texture(path.data(), texType);texturesOutput.push_back(texture);textures_loaded.push_back(texture);}}return texturesOutput;
}

加载后处理指令 const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); 常用的还有

指令描述
aiProcess_GenNormals如果模型不包含法向量的话,就为每个顶点创建法线
aiProcess_SplitLargeMeshes将比较大的网格分割成更小的子网格
aiProcess_OptimizeMeshes将多个小网格拼接为一个大的网格

模型类写好了,接下来调整一下以前的main和着色器代码

fragmentshader.glsl (我修改了后缀以供GLSL插件识别)
我们现在只使用平行光,去掉了点光源和聚光灯

#version 330 corestruct Material {sampler2D tex_diffuse1;sampler2D tex_diffuse2;// ...sampler2D tex_specular1;sampler2D tex_specular2;// ...float shininess;
};struct DirLight {vec3 ambient;vec3 diffuse;vec3 specular;vec3 direction;
};in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoords;out vec4 FragColor;uniform vec3 viewPos;uniform Material material;uniform DirLight dirLight;// 计算平行光
vec3 CalcDirectionalLight(DirLight light, vec3 normal, vec3 viewDir)
{vec3 lightDir = normalize(-light.direction);// 环境光vec3 ambient = light.ambient * vec3(texture(material.tex_diffuse1, TexCoords));// 漫反射float diff = max(dot(normal, lightDir), 0);vec3 diffuse = light.diffuse * diff * vec3(texture(material.tex_diffuse1, TexCoords));// 镜面反射vec3 reflectDir = reflect(-lightDir, normal);float spec = pow(max(dot(reflectDir, viewDir), 0), material.shininess);vec3 specular = light.specular * spec * vec3(texture(material.tex_specular1, TexCoords));return ambient + diffuse + specular;
}void main()
{vec3 normal = normalize(Normal);vec3 viewDir = normalize(viewPos - FragPos);// 平行光vec3 result = CalcDirectionalLight(dirLight, normal, viewDir);FragColor = vec4(result, 1.0f);
}

main.cpp

// ...int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "OpenGLRenderer", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;EXIT}glfwMakeContextCurrent(window);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;EXIT}glfwSetFramebufferSizeCallback(window, OnSetFrameBufferSize);glfwSetCursorPosCallback(window, ProcessMouseInput);glfwSetScrollCallback(window, ProcessMouseWheelInput);// 别忘了翻转stbi_set_flip_vertically_on_load(true);Transform cameraTransform;cameraTransform.position = glm::vec3(0, 0, 3);cameraTransform.front = glm::vec3(0, 0, -1);cameraTransform.up = glm::vec3(0, 1, 0);cameraTransform.rotate.yaw = -90;camera = Camera(cameraTransform);glEnable(GL_DEPTH_TEST);// 修改了文件后缀,便于GLSL插件识别Shader shader("VertexShader.glsl", "FragmentShader.glsl");// 创建模型,加载backpack.objModel model("F:/Scripts/Cpp/LearnOpenGL/learn-open-gl/Resource/backpack/backpack.obj");while (!glfwWindowShouldClose(window)){float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);ProcessKeyboardInput(window);glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::mat4(1);projection = glm::perspective(glm::radians(camera.fov), screenWidth / screenHeight, 0.1f, 100.0f);shader.Use();shader.SetMat4("view", view);shader.SetMat4("projection", projection);shader.SetVec3("lightColor", glm::vec3(1.0f, 1.0f, 1.0f));shader.SetVec3("viewPos", camera.transform.position);// 平行光参数shader.SetVec3("dirLight.ambient", glm::vec3(0.1f));shader.SetVec3("dirLight.diffuse", glm::vec3(0.9f));shader.SetVec3("dirLight.specular", glm::vec3(0.6f));shader.SetVec3("dirLight.direction", glm::vec3(-0.2f, -1.0f, -0.3f));shader.SetFloat("material.shininess", 32.0f);glm::mat4 modelMatrix = glm::mat4(1.0f);modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0f, 0.0f, 0.0f));shader.SetMat4("model", modelMatrix);// 绘制model.Draw(shader);glfwSwapBuffers(window);glfwPollEvents();}shader.Delete();glfwTerminate();return 0;
}

编译运行,顺利的话可以看见以下图像
模型绘制

直接将漫反射纹理输出会是这样

FragColor = vec4(vec3(texture(material.tex_diffuse1, TexCoords)), 1.0f);

模型绘制
我们还可以加上之前的点光源和聚光灯来获得更好的效果
加上聚光灯
模型绘制
加上点光源
模型绘制
完整代码可在顶部git仓库找到
下接:https://blog.csdn.net/weixin_44506615/article/details/150584832?spm=1001.2014.3001.5502

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode_238 除自身以外的数组乘积

1. 题意 除了自身外的乘积&#xff0c;题目要求不能用除法做。 2. 题解 不用除法做&#xff0c;那就用前后缀分解的方法做。 时间复杂度O(n)O(n)O(n) 两个数组记录前后缀乘积 class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int …

从0开始玩转soarm101 下篇

上篇我们从0开始构建了基本的环境&#xff0c;这篇我们继续后续的标定&#xff0c;遥操作&#xff0c;录制数据&#xff0c;上传&#xff0c;训练。 环境&#xff1a;显卡技嘉的5060&#xff0c;cpui5-13490f&#xff0c;主板技嘉b760m gaming&#xff0c;双系统ubuntu2204&am…

学习设计模式《二十三》——桥接模式

一、基础概念 桥接模式的本质是【分离抽象和实现】。 桥接模式的定义&#xff1a;将抽象部分与它的实现部分分离&#xff0c;使它们都可以独立地变化。 认识桥接模式序号认识桥接模式说明1什么是桥接通俗点说就是在不同的东西之间搭一个桥&#xff0c;让它们能够连接起来&a…

使用Python 创建虚拟环境的两种方式

使用Python 创建虚拟环境的两种方式&#xff1a; 方式一&#xff1a;使用官方标准库 venv (Python 3.3 推荐) 创建&#xff1a; # 语法&#xff1a;python -m venv <虚拟环境名称> python -m venv my_project_env指定Python解释器版本&#xff08;如果你的系统有多个Pyth…

Android 开发问题:android:marginTop=“20px“ 属性不生效

android:marginTop"20px"在 Android 开发中&#xff0c;XML 布局文件中&#xff0c;上述属性不生效 问题原因 margin 系列的属性需要加上 layout_ 前缀layout_marginTop&#xff1a;顶部边距layout_marginBottom&#xff1a;底部边距layout_marginLeft&#xff1a;左…

【P18 3-10】OpenCV Python—— 鼠标控制,鼠标回调函数(鼠标移动、按下、。。。),鼠标绘制基本图形(直线、圆、矩形)

P18 3-10 1 鼠标回调函数2 鼠标绘制基本图形&#xff08;直线、圆、矩形&#xff09;2.1 图形绘制教程2.2 鼠标绘制基本图形&#xff08;直线、圆、矩形&#xff09;代码实现1 鼠标回调函数 import cv2 import numpy as npdef mouse_callback(event,x,y,flage,userdata):print(…

微服务如何集成swagger3

文章目录引言一、项目结构二、顶级pom依赖准备三、common-swagger模块四、gateway模块配置五、结果演示引言 我们在用springboot开发应用时&#xff0c;经常使用swagger来作为我们的接口文档可视化工具&#xff0c;方便前端同事调用&#xff0c;集成也是比较简单的&#xff0c…

特种行业许可证识别技术:通过图像处理、OCR和结构化提取,实现高效、准确的许可证核验与管理

在酒店、娱乐场所、典当行、危化品经营等特种行业管理中&#xff0c;许可证是合法经营的“生命线”。传统人工核验方式效率低下、易出错&#xff0c;且难以应对海量数据和复杂伪造手段。特种行业许可证识别技术应运而生&#xff0c;成为智慧监管和优化服务的关键工具。特种行业…

零售行业新店网络零接触部署场景下,如何选择SDWAN

一家连锁超市在新疆偏远地区的新店开业申请网络专线&#xff0c;市政审批和架设电线杆的流程花了半个月&#xff0c;成本高企——而它的竞争对手在隔壁新店部署SD-WAN&#xff0c;从开箱到业务上线仅用了10分钟。近年来&#xff0c;零售企业疯狂扩张与下沉市场的趋势愈演愈烈。…

python发布文章和同步文章到社区的工具小脚本

在开发过程中&#xff0c;开发者们往往需要频繁地在社区中分享文章、解决方案以及技术文章来交流与成长。为了简化这一过程&#xff0c;我将为你们介绍两个基于Python脚本的自动化工具&#xff0c;可以帮助你发布文章到开发者社区&#xff0c;提高效率。一、从Markdown文件批量…

23.CNN系列网络思考

为什么是卷积、池化的交替进行? 卷积做的是特征提取,池化做的是一种降采样。 早期学习的主要是:低级特征(边缘、角点、纹理、颜色)。这些特征分布相对局部且空间位置信息很重要。 卷积将这些特征学习出来,然后通过池化降采样,突出其位置信息。然后再卷积进行学习池化后…

MySQL 8.x的性能优化文档整理

一、内存与缓冲优化 # InnoDB缓冲池&#xff08;内存的60%-80%&#xff09; innodb_buffer_pool_size 12G # 核心参数 innodb_buffer_pool_instances 8 # 8核CPU建议分8个实例# 日志缓冲区与Redo日志 innodb_log_buffer_size 256M # 事务日志缓冲区 innodb_log_…

个人使用AI开发的《PlSqlRewrite4GaussDB(PLSQL自动转换工具)1.0.1 BETA》发布

个人使用AI开发的《PlSqlRewrite4GaussDB(PLSQL自动转换工具)1.0.1 BETA》发布 前言 基于语法树的SQL自动改写工具开发系列&#xff08;1&#xff09;-离线安装语法树解析工具antlr4 基于语法树的SQL自动改写工具开发系列&#xff08;2&#xff09;-使用PYTHON进行简单SQL改写…

python的校园研招网系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 摘要&…

如何高效撰写AI领域学术论文——学习笔记

最开始写的时候最好仿照着顶会来写1. 标题(Title)​标题是论文的"门面"&#xff0c;需要同时具备简洁性和信息量&#xff1a;采用"XX方法 for XXX任务"的标准格式&#xff0c;包含核心创新点和应用领域&#xff0c;避免使用模糊词汇&#xff0c;力求精准&a…

elasticsearch8.12.0安装分词

上篇说到&#xff0c;安装了es后正常运行es分词下载地址从 GitHub Release 下载&#xff08;推荐&#xff09; &#x1f449; https://github.com/medcl/elasticsearch-analysis-ik/releases或https://release.infinilabs.com/analysis-ik/stable/安装&#xff1a;选择与你 ES …

强化学习算法分类与介绍(含权重更新公式)

强化学习算法种类丰富&#xff0c;可按学习目标&#xff08;基于价值 / 基于策略 / 演员 - 评论家&#xff09;、数据使用方式&#xff08;在线 / 离线&#xff09;、是否依赖环境模型&#xff08;无模型 / 有模型&#xff09;等维度分类。以下按核心逻辑梳理常见算法&#xff…

基于STM32F103单片机智能门禁热释人体感应报警设计

1 系统功能介绍 本设计基于 STM32F103C8T6 单片机&#xff0c;通过多种传感器、执行器以及通信模块实现智能门禁和安防报警功能。其主要功能是检测门外人员情况&#xff0c;结合环境光照强度判断是否需要照明&#xff0c;同时结合 GSM 模块在异常情况下发送报警信息&#xff0c…

imx6ull-驱动开发篇33——platform 平台驱动模型

目录 Linux 驱动的分离与分层 驱动的分隔与分离 驱动的分层 platform 平台驱动模型 platform 总线 bus_type 结构体 platform 总线 platform_match函数 platform 驱动 platform_driver 结构体 device_driver 结构体 platform_driver_register 函数 platform_drive…

Win/Linux笔记本合盖不睡眠设置指南

在 笔记本电脑上&#xff0c;当你合上屏幕时&#xff0c;默认系统可能会进入“睡眠”或“休眠”状态。如果你希望合上屏幕时系统继续正常运行&#xff08;例如后台下载、运行程序、远程访问等&#xff09;&#xff0c;需要修改系统的电源设置。 一、以下是 Windows 10 / Windo…