跟着cherno手搓游戏引擎【18】抽象Shader、项目小修改
抽象:
Shader.h:
#pragma once
#include <string>namespace YOTO {class Shader {public:virtual~Shader()=default;virtual void Bind()const=0;virtual void UnBind()const=0;static Shader* Create(const std::string& vertexSrc, const std::string& fragmentSrc);};
}
Shader.cpp:
#include "ytpch.h"
#include "Shader.h"
#include"Renderer.h"
#include "Platform/OpenGL/OpenGLShader.h"
namespace YOTO {Shader* Shader::Create(const std::string& vertexSrc, const std::string& fragmentSrc){switch (Renderer::GetAPI()){case RendererAPI::API::None:YT_CORE_ASSERT(false, "Buffer:API为None不支持");return nullptr;case RendererAPI::API::OpenGL:return new OpenGLShader(vertexSrc, fragmentSrc);}YT_CORE_ASSERT(false, "Buffer:未知API");return nullptr;}
}
实现:
新建OpenGLShader类:

OpenGLShader.h
#pragma once
#include <string>
#include "YOTO/Renderer/Shader.h"
#include <glm/glm.hpp>
namespace YOTO {class OpenGLShader:public Shader {public:OpenGLShader(const std::string& vertexSrc, const std::string& fragmentSrc);~OpenGLShader();void Bind()const override;void UnBind()const override;void UploadUniformMat4(const std::string& name, const glm::mat4& matrix);void UploadUniformMat3(const std::string& name, const glm::mat3& matrix);void UploadUniformFloat4(const std::string& name, const glm::vec4& values);void UploadUniformFloat3(const std::string& name, const glm::vec3& values);void UploadUniformFloat2(const std::string& name, const glm::vec2& values);void UploadUniformFloat(const std::string& name, float values);void UploadUniformInt(const std::string& name, int values);private:uint32_t m_RendererID;};
}
OpenGLShader.cpp
#include "ytpch.h"
#include "OpenGLShader.h"#include <glad/glad.h>
#include <YOTO/Log.h>
#include<glm/gtc/type_ptr.hpp>
namespace YOTO {OpenGLShader::OpenGLShader(const std::string& vertexSrc, const std::string& fragmentSrc){// 1.1.创建顶点着色器对象GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);// Send the vertex shader source code to GL// Note that std::string's .c_str is NULL character terminated.// 1.2.附加顶点着色器源码到顶点着色器对象中const GLchar* source = vertexSrc.c_str();glShaderSource(vertexShader, 1, &source, 0);// 1.3.编译顶点着色器对象glCompileShader(vertexShader);// 1.4.检查是否编译成功GLint isCompiled = 0;glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isCompiled);if (isCompiled == GL_FALSE) {// 1.4.2编译失败可以打印报错信息GLint maxLength = 0;glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);// The maxLength includes the NULL characterstd::vector<GLchar> infoLog(maxLength);glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0]);// We don't need the shader anymore.glDeleteShader(vertexShader);YT_CORE_ERROR("{0}", infoLog.data());YT_CORE_ASSERT(false, "Vertex shader compilation failure!");return;}// 片段着色器一样// 2.1.创建片段着色器对象GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);// Send the fragment shader source code to GL// Note that std::string's .c_str is NULL character terminated.// 2.2.附加片段着色器源码到片段着色器对象中source = fragmentSrc.c_str();glShaderSource(fragmentShader, 1, &source, 0);// 2.3.编译片段着色器对象glCompileShader(fragmentShader);// 2.4.检查是否编译成功glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isCompiled);if (isCompiled == GL_FALSE) {// 2.4.2编译失败可以打印报错信息GLint maxLength = 0;glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);// The maxLength includes the NULL characterstd::vector<GLchar> infoLog(maxLength);glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0]);// We don't need the shader anymore.glDeleteShader(fragmentShader);// Either of them. Don't leak shaders.glDeleteShader(vertexShader);YT_CORE_ERROR("{0}", infoLog.data());YT_CORE_ASSERT(false, "Fragment shader compilation failure!");return;}// Vertex and fragment shaders are successfully compiled.// Now time to link them together into a program.// Get a program object.// 3.1创建着色器程序对象m_RendererID = glCreateProgram();GLuint program = m_RendererID;// 3.2附加着色器对象给着色器程序对象glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);// 3.3链接着色器程序对象glLinkProgram(program);// 3.4可以检查链接是否成功// Note the different functions here: glGetProgram* instead of glGetShader*.GLint isLinked = 0;glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);if (isLinked == GL_FALSE) {GLint maxLength = 0;glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);// The maxLength includes the NULL characterstd::vector<GLchar> infoLog(maxLength);glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);// We don't need the program anymore.glDeleteProgram(program);// Don't leak shaders either.glDeleteShader(vertexShader);glDeleteShader(fragmentShader);YT_CORE_ERROR("{0}", infoLog.data());YT_CORE_ASSERT(false, "Shader link failure!");return;}// 4.删除着色器对象// Always detach shaders after a successful link.glDetachShader(program, vertexShader);glDetachShader(program, fragmentShader);}OpenGLShader::~OpenGLShader(){glDeleteProgram(m_RendererID);}void OpenGLShader::Bind() const{glUseProgram(m_RendererID);}void OpenGLShader::UnBind() const{glUseProgram(0);}void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniformMatrix4fv(loacation, 1, GL_FALSE, glm::value_ptr(matrix));}void OpenGLShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniformMatrix3fv(loacation, 1, GL_FALSE, glm::value_ptr(matrix)); }void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& values){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniform4f(loacation, values.x, values.y, values.z, values.w);}void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& values){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniform3f(loacation, values.x, values.y, values.z);}void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& values){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniform2f(loacation, values.x, values.y);}void OpenGLShader::UploadUniformFloat(const std::string& name, float values){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniform1f(loacation, values);}void OpenGLShader::UploadUniformInt(const std::string& name, int values){GLint loacation = glGetUniformLocation(m_RendererID, name.c_str());glUniform1i(loacation, values);}
}
测试:
#include<YOTO.h>
#include "imgui/imgui.h"
#include<stdio.h>
#include <glm/gtc/matrix_transform.hpp>
#include <Platform/OpenGL/OpenGLShader.h>
#include <glm/gtc/type_ptr.hpp>
class ExampleLayer:public YOTO::Layer
{
public:ExampleLayer():Layer("Example"), m_Camera(-2.0f, 2.0f, -2.0f, 2.0f), m_CameraPosition(0){uint32_t indices[3] = { 0,1,2 };float vertices[3 * 7] = {-0.5f,-0.5f,0.0f, 0.8f,0.2f,0.8f,1.0f,0.5f,-0.5f,0.0f, 0.2f,0.3f,0.8f,1.0f,0.0f,0.5f,0.0f, 0.8f,0.8f,0.2f,1.0f,};m_VertexArray.reset(YOTO::VertexArray::Create());std::shared_ptr<YOTO::VertexBuffer> m_VertexBuffer;m_VertexBuffer.reset(YOTO::VertexBuffer::Create(vertices, sizeof(vertices)));{YOTO::BufferLayout setlayout = {{YOTO::ShaderDataType::Float3,"a_Position"},{YOTO::ShaderDataType::Float4,"a_Color"}};m_VertexBuffer->SetLayout(setlayout);}m_VertexArray->AddVertexBuffer(m_VertexBuffer);std::shared_ptr<YOTO::IndexBuffer>m_IndexBuffer;m_IndexBuffer.reset(YOTO::IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t)));m_VertexArray->AddIndexBuffer(m_IndexBuffer);std::string vertexSource = R"(#version 330 corelayout(location = 0) in vec3 a_Position;layout(location = 1) in vec4 a_Color;uniform mat4 u_ViewProjection;uniform mat4 u_Transform;out vec3 v_Position;out vec4 v_Color;void main(){v_Position=a_Position;v_Color=a_Color;gl_Position =u_ViewProjection *u_Transform* vec4( a_Position,1.0);})";//绘制颜色std::string fragmentSource = R"(#version 330 corelayout(location = 0) out vec4 color;in vec3 v_Position;in vec4 v_Color;void main(){color=vec4(v_Color);})";m_Shader.reset(YOTO::Shader::Create(vertexSource, fragmentSource));///测试/m_SquareVA.reset(YOTO::VertexArray::Create());float squareVertices[3 * 4] = {-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f,0.5f,0.5f,0.0f,-0.5f,0.5f,0.0f};std::shared_ptr<YOTO::VertexBuffer> squareVB;squareVB.reset(YOTO::VertexBuffer::Create(squareVertices, sizeof(squareVertices)));squareVB->SetLayout({{YOTO::ShaderDataType::Float3,"a_Position"}});m_SquareVA->AddVertexBuffer(squareVB);uint32_t squareIndices[6] = { 0,1,2,2,3,0 };std::shared_ptr<YOTO::IndexBuffer> squareIB;squareIB.reset((YOTO::IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t))));m_SquareVA->AddIndexBuffer(squareIB);//测试:std::string BlueShaderVertexSource = R"(#version 330 corelayout(location = 0) in vec3 a_Position;uniform mat4 u_ViewProjection;uniform mat4 u_Transform;out vec3 v_Position;void main(){v_Position=a_Position;gl_Position =u_ViewProjection*u_Transform*vec4( a_Position,1.0);})";//绘制颜色std::string BlueShaderFragmentSource = R"(#version 330 corelayout(location = 0) out vec4 color;in vec3 v_Position;uniform vec3 u_Color;void main(){color=vec4(u_Color,1.0);})";m_BlueShader.reset(YOTO::Shader::Create(BlueShaderVertexSource, BlueShaderFragmentSource));}void OnImGuiRender() override {ImGui::Begin("设置");ImGui::ColorEdit3("正方形颜色", glm::value_ptr(m_SquareColor));ImGui::End();}void OnUpdate(YOTO::Timestep ts)override {YT_CLIENT_TRACE("delta time {0}s ({1}ms)", ts.GetSeconds(), ts.GetMilliseconds());if (YOTO::Input::IsKeyPressed(YT_KEY_LEFT)) {m_CameraPosition.x -= m_CameraMoveSpeed* ts;}else if (YOTO::Input::IsKeyPressed(YT_KEY_RIGHT)) {m_CameraPosition.x += m_CameraMoveSpeed * ts;}if (YOTO::Input::IsKeyPressed(YT_KEY_DOWN)) {m_CameraPosition.y -= m_CameraMoveSpeed * ts;}else if (YOTO::Input::IsKeyPressed(YT_KEY_UP)) {m_CameraPosition.y += m_CameraMoveSpeed * ts;}if (YOTO::Input::IsKeyPressed(YT_KEY_A)) {m_CameraRotation += m_CameraRotationSpeed * ts;}else if (YOTO::Input::IsKeyPressed(YT_KEY_D)) {m_CameraRotation -= m_CameraRotationSpeed * ts;}YOTO::RenderCommand::SetClearColor({ 0.2f, 0.2f, 0.2f, 1.0f });YOTO::RenderCommand::Clear();m_Camera.SetPosition(m_CameraPosition);m_Camera.SetRotation(m_CameraRotation);YOTO::Renderer::BeginScene(m_Camera);{static glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(0.1f)); glm::vec4 redColor(0.8f, 0.3f, 0.3f, 1.0f);glm::vec4 blueColor(0.2f, 0.3f, 0.8f, 1.0f);/* YOTO::MaterialRef material = new YOTO::MaterialRef(m_FlatColorShader);YOTO::MaterialInstaceRef mi = new YOTO::MaterialInstaceRef(material);mi.setValue("u_Color",redColor);mi.setTexture("u_AlbedoMap", texture);squreMesh->SetMaterial(mi);*/std::dynamic_pointer_cast<YOTO::OpenGLShader>(m_BlueShader)->Bind();std::dynamic_pointer_cast<YOTO::OpenGLShader>(m_BlueShader)->UploadUniformFloat3("u_Color",m_SquareColor);for (int y = 0; y < 20; y++) {for (int x = 0; x <20; x++){glm::vec3 pos(x * 0.11f,y* 0.11f, 0.0);glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) * scale;/*if (x % 2 == 0) {m_BlueShader->UploadUniformFloat4("u_Color", redColor);}else {m_BlueShader->UploadUniformFloat4("u_Color", blueColor);}*/YOTO::Renderer::Submit(m_BlueShader, m_SquareVA, transform);}}YOTO::Renderer::Submit(m_Shader, m_VertexArray);YOTO::Renderer::EndScene();}}void OnEvent(YOTO::Event& event)override {/*if (event.GetEventType() == YOTO::EventType::KeyPressed) {YOTO:: KeyPressedEvent& e = (YOTO::KeyPressedEvent&)event;YT_CLIENT_TRACE("ExampleLayer:{0}",(char)e.GetKeyCode());if (e.GetKeyCode()==YT_KEY_TAB) {YT_CLIENT_INFO("ExampleLayerOnEvent:TAB按下了");}}*///YT_CLIENT_TRACE("SandBoxApp:测试event{0}", event);}private:std::shared_ptr<YOTO::Shader> m_Shader;std::shared_ptr<YOTO::VertexArray> m_VertexArray;std::shared_ptr<YOTO::Shader> m_BlueShader;std::shared_ptr<YOTO::VertexArray> m_SquareVA;YOTO::OrthographicCamera m_Camera;glm::vec3 m_CameraPosition;float m_CameraMoveSpeed = 5.0f;float m_CameraRotation = 0;float m_CameraRotationSpeed = 180.0f;glm::vec3 m_SquareColor = { 0.2f,0.3f,0.7f };};class Sandbox:public YOTO::Application
{
public:Sandbox(){PushLayer(new ExampleLayer());//PushLayer(new YOTO::ImGuiLayer());}~Sandbox() {}private:};YOTO::Application* YOTO::CreateApplication() {printf("helloworld");return new Sandbox();
}

cool!
小修改:
Core.h:
#pragma once
#include<memory>
//用于dll的宏
#ifdef YT_PLATFORM_WINDOWS
#if YT_DYNAMIC_LINK#ifdef YT_BUILD_DLL#define YOTO_API __declspec(dllexport) #else#define YOTO_API __declspec(dllimport) #endif // DEBUG
#else#define YOTO_API
#endif
#else
#error YOTO_ONLY_SUPPORT_WINDOWS
#endif // YOTO_PLATFORM_WINDOWS#ifdef YT_DEBUG
#define YT_ENABLE_ASSERTS
#endif#ifdef YT_ENABLE_ASSERTS
#define YT_CLIENT_ASSERT(x,...) {if(!(x)){YT_CLIENT_ERROR("断言错误:{0}",__VA_ARGS__);__debugbreak();}}
#define YT_CORE_ASSERT(x,...) {if(!(x)){YT_CORE_ERROR("断言错误:{0}",__VA_ARGS__);__debugbreak();}}
#else
#define YT_CLIENT_ASSERT(x,...)
#define YT_CORE_ASSERT(x,...)#endif // YT_ENABLE_ASSERTS#define BIT(x)(1<<x)
//绑定事件定义
#define YT_BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1)namespace YOTO {template<typename T>using Scope = std::unique_ptr<T>;template<typename T>using Ref = std::shared_ptr<T>;
}
之后把所有share_ptr的地方改成Ref就好了。
相关文章:
跟着cherno手搓游戏引擎【18】抽象Shader、项目小修改
抽象: Shader.h: #pragma once #include <string>namespace YOTO {class Shader {public:virtual~Shader()default;virtual void Bind()const0;virtual void UnBind()const0;static Shader* Create(const std::string& vertexSrc, const std::string&am…...
每日OJ题_算法_模拟②_力扣495. 提莫攻击
目录 力扣495. 提莫攻击 解析代码 力扣495. 提莫攻击 495. 提莫攻击 难度 简单 在《英雄联盟》的世界中,有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希(编者注:寒冰射手)进入中毒状态。 当提莫攻击艾希,…...
freertos 源码分析二 list链表源码
list.c 一、链表初始化 void vListInitialise( List_t * const pxList ) { pxList->pxIndex ( ListItem_t * ) &…...
Peter算法小课堂—Dijkstra最短路算法
大家好,我们人见人爱、花见花开、车见车爆胎的Peter Pan来啦,hia~hia~hia。今天,我们今天来学习毒瘤的最短路算法啦。啊这……什么是Dijkstra算法?长文警告⚠ 正经点啊 手算样例 大家思考一下,你在手算样例的时候&am…...
Python 读取和写入包含中文的csv、xlsx、json文件
背景 最近在做数据的训练,经常需要读取写入csv、xlsx、json文件来获取数据,在这里做简单总结记录。 ps: 读取和写入中文文件时,需要确保文件的编码格式是正确的。通常情况使用UTF-8编码格式。如果使用其他编码格式可能会导致读取或写入时出…...
【算法】利用递归dfs解决二叉树算法题(C++)
文章目录 1. 前言2. 算法题2331.计算布尔二叉树的值129.求根节点到叶节点数字之和LCR047.二叉树剪枝98.验证二叉搜索树230.二叉搜索树中第K小的元素257.二叉树的所有路径 1. 前言 有关 递归 的相关解释与解题 请看下文: 以汉诺塔理解递归、并用递归解决算法题 对于…...
计算机网络_1.6.1 常见的三种计算机网络体系结构
1.6.1 常见的三种计算机网络体系结构 1、OSI(七层协议)标准失败的原因2、TCP/IP参考模型3、三种网络体系结构对比 笔记来源: B站 《深入浅出计算机网络》课程 1、OSI(七层协议)标准失败的原因 (1…...
XML传参方式
export function groupLoginAPI(xmlData) {return http.post(/tis/group/1.0/login, xmlData, {headers: {Content-Type: application/xml,X-Requested-With: AAServer/4.0,}}) }import {groupLoginAPI} from "../api/user"; function (e) { //xml格式传参let groupX…...
Pyecharts炫酷散点图构建指南【第50篇—python:炫酷散点图】
文章目录 Pyecharts炫酷散点图构建指南引言安装Pyecharts基础散点图自定义散点图样式渐变散点图动态散点图高级标注散点图多系列散点图3D散点图时间轴散点图笛卡尔坐标系下的极坐标系散点图 总结: Pyecharts炫酷散点图构建指南 引言 在数据可视化领域,…...
关于爬取所有哔哩哔哩、任意图片、所有音乐、的python脚本语言-Edge浏览器插件 全是干货!
这些都是现成的并且实时更新的!从次解放双手! 首先有自己的edge浏览器基本上都有并且找到插件选项 1.哔哩哔哩视频下载助手(爬取哔哩哔哩视频) bilibili哔哩哔哩视频下载助手 - Microsoft Edge Addons 下面是效果: 2.图…...
压力测试工具-Jmeter使用总结
目录 一.前言 二.线程组 三.线程组的组件 四.线程组-HTTP请求 1、JSON提取器 2、XPATH提取器 3、正则表达式提取器 五.线程组-断言 1、响应断言 2、JSON断言 六.创建测试 1.创建线程组 2.配置元件 3.构造HTTP请求 4.添加HTTP请求头 5.添加断言 6.添加查看结果树…...
[cmake]CMake Error: Could not create named generator Visual Studio 16 2019解决方法
配置flycv时,cmake以下代码会报错第二行的错误,网上解决方法为第三行代码 cmake .. -G "Visual Studio 16 2019 Win64" CMake Error: Could not create named generator Visual Studio 16 2019 cmake .. -G "Visual Studio 16 2019"…...
2024美赛数学建模D题思路分析 - 大湖区水资源问题
1 赛题 问题D:大湖区水资源问题 背景 美国和加拿大的五大湖是世界上最大的淡水湖群。这五个湖泊和连接的水道构成了一个巨大的流域,其中包含了这两个国家的许多大城市地区,气候和局部天气条件不同。 这些湖泊的水被用于许多用途࿰…...
2024 高级前端面试题之 HTTP模块 「精选篇」
该内容主要整理关于 HTTP模块 的相关面试题,其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 HTTP模块精选篇 1. HTTP 报文的组成部分2. 常见状态码3. 从输入URL到呈现页面过程3.1 简洁3.2 详细 4. TCP、UDP相关5. HTTP2相关6. https相关7. WebSocket的…...
【Linux C | 网络编程】netstat 命令图文详解 | 查看网络连接、查看路由表、查看统计数据
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
Python爬虫存储库安装
如果你还没有安装好MySQL、MongoDB、Redis 数据库,请参考这篇文章进行安装: Windows、Linux、Mac数据库的安装(mysql、MongoDB、Redis)-CSDN博客 存储库的安装 上节中,我们介绍了几个数据库的安装方式,但…...
用函数求最小公倍数和最大公约数(c++题解)
题目描述 输入两个正整数m和n,求其最大公约数和最小公倍数。 提示,求最大公约数用一个函数实现。本题求最大公约数必须用高效算法,如辗转相除法,朴素算法要超时。 输入格式 第1行:两个非整数,值在0&…...
鲜花销售|鲜花销售小程序|基于微信小程序的鲜花销售系统设计与实现(源码+数据库+文档)
鲜花销售小程序目录 目录 基于微信小程序的鲜花销售系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、前台功能模块 2、后台功能模块 (1) 后台登录 (2) 管理员功能模块 用户管理 商家管理 鲜花信息管理 鲜花分类管理 管理员管理 系统管理 (3) 商家功…...
三.Linux权限管控 1-5.Linux的root用户用户和用户组查看权限控制信息chmod命令chown命令
目录 三.Linux权限管控 1.Linux的root用户 root用户(超级管理员) su和exit命令 sudo命令 为普通用户配置sudo认证 三.Linux权限管控 2.用户和用户组 用户,用户组 用户组管理 用户管理 getent---查看系统中的用户 三.Linux权限管控…...
Jmeter学习系列之四:测试计划元素介绍
测试计划元素 JMeter包含各种相互关联但为不同目的而设计的元素。在开始使用JMeter之前,最好先了解一下JMeter的一些主要元素。 注意:测试计划包含至少一个线程组。 以下是JMeter的一些主要组件: 测试计划(Plan)线程组(Thread Group)控制器…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
