【OpenGL】LearnOpenGL學習筆記12 - 網格模型繪制

上接: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…