跟着cherno手搓游戏引擎【21】shaderLibrary(shader管理类)

前置:

ytpch.h:

#pragma once
#include<iostream>
#include<memory>
#include<utility>
#include<algorithm>
#include<functional>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<sstream>
#include<array>
#ifdef YT_PLATFORM_WINDOWS
#include<Windows.h>
#endif // YT_PLATFORM_WINDOWS

创建glsl文件:

 

Texture.glsl:

		#type vertex#version 330 corelayout(location = 0) in vec3 a_Position;layout(location = 1) in vec2 a_TexCoord;uniform mat4 u_ViewProjection;uniform mat4 u_Transform;out vec2 v_TexCoord;out vec3 v_Position;void main(){v_TexCoord=a_TexCoord;v_Position=a_Position;gl_Position =u_ViewProjection*u_Transform*vec4( a_Position,1.0);}#type fragment#version 330 corelayout(location = 0) out vec4 color;in vec3 v_Position;in vec2 v_TexCoord;uniform sampler2D u_Texture ;void main(){color = texture(u_Texture, v_TexCoord);	}

抽象:

Shader.h:

#pragma once
#include <string>
#include"YOTO/Core.h"
namespace YOTO {class Shader {public:virtual~Shader()=default;virtual void Bind()const=0;virtual void UnBind()const=0;virtual const std::string& GetName()const = 0;static Ref<Shader> Create(const std::string& filepath);static Ref<Shader> Create(const std::string&name, const std::string& vertexSrc, const std::string& fragmentSrc);};class ShaderLibrary {public:void Add(const Ref<Shader>& shader);void Add(const std::string &name,const Ref<Shader>& shader);Ref<Shader> Load(const std::string filepath);Ref<Shader> Load(const std::string &name,const std::string filepath);Ref<Shader> Get(const std::string& name);bool Exists(const std::string& name);private:std::unordered_map<std::string,Ref<Shader>> m_Shaders;};
}

Shader.cpp:

#include "ytpch.h"
#include "Shader.h"
#include"Renderer.h"
#include "Platform/OpenGL/OpenGLShader.h"
namespace YOTO {Ref<Shader> Shader::Create(const std::string& filepath){switch (Renderer::GetAPI()){case RendererAPI::API::None:YT_CORE_ASSERT(false, "Shader:API为None不支持");return nullptr;case RendererAPI::API::OpenGL:return std::make_shared <OpenGLShader>(filepath);}YT_CORE_ASSERT(false, "Buffer:未知API");return nullptr;}Ref<Shader> Shader::Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc){switch (Renderer::GetAPI()){case RendererAPI::API::None:YT_CORE_ASSERT(false, "Shader:API为None不支持");return nullptr;case RendererAPI::API::OpenGL:return std::make_shared <OpenGLShader>(name,vertexSrc, fragmentSrc);}YT_CORE_ASSERT(false, "Buffer:未知API");return nullptr;}void ShaderLibrary::Add(const Ref<Shader>& shader){auto& name = shader->GetName();Add(name, shader);}void ShaderLibrary::Add(const std::string& name, const Ref<Shader>& shader){YT_CORE_ASSERT(!Exists(name), "ShaderLibrary:shader已经存在了")m_Shaders[name] = shader;}Ref<Shader> ShaderLibrary::Load(const std::string filepath){auto shader = Shader::Create(filepath);Add(shader);return shader;}Ref<Shader> ShaderLibrary::Load(const std::string& name, const std::string filepath){auto shader = Shader::Create(filepath);Add(shader);return shader;}Ref<Shader> ShaderLibrary::Get(const std::string& name){YT_CORE_ASSERT(Exists(name),"ShaderLibrary:未找到shader")return m_Shaders[name];}bool ShaderLibrary::Exists(const std::string& name){return m_Shaders.find(name)!=m_Shaders.end();}
}

实现:

OpenGLShader.h:

#pragma once
#include <string>
#include "YOTO/Renderer/Shader.h"
#include <glm/glm.hpp>
typedef unsigned int GLenum;
namespace YOTO {class OpenGLShader:public Shader {public:OpenGLShader(const std::string& filepath);OpenGLShader(const std::string &name,const std::string& vertexSrc, const std::string& fragmentSrc);~OpenGLShader();void Bind()const override;void UnBind()const override;virtual const std::string& GetName()const override { return m_Name; }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:std::string ReadFile(const std::string filepath);std::unordered_map<GLenum,std::string> PreProcess(const std::string& source);void Compile(const std::unordered_map<GLenum, std::string>& shaderSources);private:uint32_t m_RendererID;std::string m_Name;};
} 

OpenGLShader.cpp:

#include "ytpch.h"
#include "OpenGLShader.h"#include <glad/glad.h>
#include <YOTO/Log.h>
#include<glm/gtc/type_ptr.hpp>
namespace YOTO {static GLenum ShaderTypeFromString(const std::string& type) {if (type == "vertex") {return GL_VERTEX_SHADER;}if (type == "fragment" || type == "pixel") {return GL_FRAGMENT_SHADER;}YT_CORE_ASSERT(false, "不知道的shader类型");return 0;}OpenGLShader::OpenGLShader(const std::string& filepath){std::string source = ReadFile(filepath);YT_CORE_ASSERT(source.size(), "GLSL读取的字符串为空");auto shaderSources = PreProcess(source);Compile(shaderSources);auto lastSlash = filepath.find_last_of("/\\");lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1;auto lastDot = filepath.rfind('.');auto count = lastDot == std::string::npos ? filepath.size() - lastSlash : lastDot - lastSlash;m_Name=filepath.substr(lastSlash, count);}OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc):m_Name(name){std::unordered_map<GLenum, std::string >sources;sources[GL_VERTEX_SHADER] = vertexSrc;sources[GL_FRAGMENT_SHADER] = fragmentSrc;Compile(sources);}OpenGLShader::~OpenGLShader(){glDeleteProgram(m_RendererID);}std::string OpenGLShader::ReadFile(const std::string filepath){std::string result;std::ifstream in(filepath, std::ios::in | std::ios::binary);if (in) {in.seekg(0, std::ios::end);			// 将指针放在最后面result.resize(in.tellg());			// 初始化string的大小, in.tellg()返回位置in.seekg(0, std::ios::beg);			// in指回头部in.read(&result[0], result.size());	// in读入放在result指向的内存中}else {YT_CORE_ERROR("不能打开文件:{0}", filepath);}return result;}std::unordered_map<GLenum, std::string> OpenGLShader::PreProcess(const std::string& source){std::unordered_map<GLenum, std::string> shaderSources;std::string typeToken = "#type";size_t typeTokenLen = typeToken.size();size_t findCurPos = source.find(typeToken, 0);size_t findNextPos = findCurPos;while (findNextPos != std::string::npos) {size_t curlineEndPos = source.find_first_of("\r\n", findCurPos);///r/n写错为/r/nYT_CORE_ASSERT(curlineEndPos != std::string::npos, "解析shader失败");size_t begin = findCurPos + typeTokenLen + 1;std::string type = source.substr(begin, curlineEndPos - begin);// 获取到是vertex还是fragmentYT_CORE_ASSERT(ShaderTypeFromString(type), "无效的shader的类型	");size_t nextLinePos = source.find_first_not_of("\r\n", curlineEndPos);findNextPos = source.find(typeToken, nextLinePos);// 获取到具体的shader代码shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, findNextPos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));findCurPos = findNextPos;}return shaderSources;/*用find,而不是find_firtst_of,因为find返回完全匹配的字符串的的位置;find_first_of返回被查匹配字符串中某个字符的第一次出现位置。std::string::npos是一个非常大的数source.substr(0, source.size() + 10000)截取到从头到末尾,不会报错*/}void OpenGLShader::Compile(const std::unordered_map<GLenum, std::string>& shaderSources){GLuint program = glCreateProgram();YT_CORE_ASSERT(shaderSources.size()<=2,"OpenGLShader:shader只支持两种!")std::array<GLenum,2>glShaderIDs;int glShaderIDIndex=0;for (auto& kv : shaderSources) {GLenum type = kv.first;const std::string& source = kv.second;// Create an empty vertex shader handleGLuint shader = glCreateShader(type);// Send the vertex shader source code to GL// Note that std::string's .c_str is NULL character terminated.const GLchar* sourceCStr = source.c_str();glShaderSource(shader, 1, &sourceCStr, 0);// Compile the vertex shaderglCompileShader(shader);GLint isCompiled = 0;glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);if (isCompiled == GL_FALSE){GLint maxLength = 0;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);// The maxLength includes the NULL characterstd::vector<GLchar> infoLog(maxLength);glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);// We don't need the shader anymore.glDeleteShader(shader);// Use the infoLog as you see fit.// In this simple program, we'll just leaveYT_CORE_ERROR("{0} ", infoLog.data());YT_CORE_ASSERT(false, "shader 编译失败!");break;}// Attach our shaders to our programglAttachShader(program, shader);glShaderIDs[glShaderIDIndex++]=shader;}// Link our programglLinkProgram(program);// 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.for (auto id : glShaderIDs) {glDeleteShader(id);}// Use the infoLog as you see fit.// In this simple program, we'll just leaveYT_CORE_ERROR("{0} ", infoLog.data());YT_CORE_ASSERT(false, "shader link failure!");return;}// Always detach shaders after a successful link.for (auto id : glShaderIDs) {glDetachShader(program, id);}m_RendererID = program;}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);}}

调用:

SandboxApp.cpp:

#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());YOTO::Ref<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);YOTO::Ref<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=(YOTO::Shader::Create("VertexPosColor", vertexSource, fragmentSource));///测试/m_SquareVA.reset(YOTO::VertexArray::Create());float squareVertices[5 * 4] = {-0.5f,-0.5f,0.0f, 0.0f,0.0f,0.5f,-0.5f,0.0f,  1.0f,0.0f,0.5f,0.5f,0.0f,   1.0f,1.0f,-0.5f,0.5f,0.0f,  0.0f,1.0f,};YOTO::Ref<YOTO::VertexBuffer> squareVB;squareVB.reset(YOTO::VertexBuffer::Create(squareVertices, sizeof(squareVertices)));squareVB->SetLayout({{YOTO::ShaderDataType::Float3,"a_Position"},{YOTO::ShaderDataType::Float2,"a_TexCoord"}});m_SquareVA->AddVertexBuffer(squareVB);uint32_t squareIndices[6] = { 0,1,2,2,3,0 };YOTO::Ref<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=(YOTO::Shader::Create("FlatColor", BlueShaderVertexSource, BlueShaderFragmentSource));auto textureShader=	m_ShaderLibrary.Load("assets/shaders/Texture.glsl");m_Texture=YOTO::Texture2D::Create("assets/textures/Checkerboard.png");m_ChernoLogo= YOTO::Texture2D::Create("assets/textures/ChernoLogo.png");std::dynamic_pointer_cast<YOTO::OpenGLShader>(textureShader)->Bind();std::dynamic_pointer_cast<YOTO::OpenGLShader>(textureShader)->UploadUniformInt("u_Texture", 0);}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.105f,y* 0.105f, 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);}}auto textureShader = m_ShaderLibrary.Get("Texture");m_Texture->Bind();YOTO::Renderer::Submit(textureShader, m_SquareVA, glm::scale(glm::mat4(1.0f), glm::vec3(1.5f)));m_ChernoLogo->Bind();YOTO::Renderer::Submit(textureShader, m_SquareVA, glm::scale(glm::mat4(1.0f), glm::vec3(1.5f)));//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:YOTO::ShaderLibrary m_ShaderLibrary;YOTO::Ref<YOTO::Shader> m_Shader;YOTO::Ref<YOTO::VertexArray> m_VertexArray;YOTO::Ref<YOTO::Shader> m_BlueShader;YOTO::Ref<YOTO::VertexArray> m_SquareVA;YOTO::Ref<YOTO::Texture2D> m_Texture,m_ChernoLogo;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! 

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

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

相关文章

ROS笔记一:工作空间和功能包

目录 工作空间 如何创建工作空间&#xff1a; 编译工作空间 设置环境变量 功能包 创建功能包 CMakeLists.txt package.xml 工作空间 ROS的工作空间是用来存放工程文件代码的文件夹 ROS的开发依赖于工作空间&#xff0c;包括编写代码、编译等都是在工作空间下进行的 工作空…

如何在Termux中使用Hexo结合内网穿透工具实现远程访问本地博客站点

文章目录 前言 1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并结合…

GO语言集成开发 JetBrains GoLand 2023 中文

JetBrains GoLand 2023是一款专为Go语言开发者打造的集成开发环境&#xff08;IDE&#xff09;。它基于IntelliJ IDEA平台&#xff0c;提供了丰富的功能和工具&#xff0c;旨在提高开发效率和质量。GoLand 2023具备强大的Go语言支持&#xff0c;包括语法高亮、自动补全、代码提…

RPA财务机器人之UiPath实战 - 自动化操作Excel进行财务数据汇总与分析之流程建立与数据读取、处理、汇总、分析

一、案例介绍&#xff1a; A公司共有13个开在不同银行的帐户&#xff0c;分别用于不同的业务分部或地区分部收付款。公司总部为了核算每月的收支情况&#xff0c;查看银行在哪个月交易量频繁&#xff0c;需要每月汇总各个银行的帐户借方和贷方金额&#xff0c;并将其净收支&am…

unity 增加系统时间显示、FPS帧率、ms延迟

代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;using UnityEngine;public class Frame : MonoBehaviour {// 记录帧数private int _frame;// 上一次计算帧率的时间private float _lastTime;// 平…

MATLAB Fundamentals>>>Centering and Scaling

MATLAB Fundamentals>Common Data Analysis Techniques>Polynomial Fitting>Centering and Scaling 数据导入 This code sets up the activity. yr 2000:2007 penguins [5.49 7.03 7.73 7.70 9.29 9.21 11.89 10.85] 附加练习 How does the model look?…

Node.js JSON Schema Ajv依赖库逐步介绍验证类型和中文错误提示

在构建应用程序时&#xff0c;数据的有效性是至关重要的。为了确保传入的数据符合预期的格式和规范&#xff0c;我们可以使用 Ajv&#xff08;Another JSON Schema Validator&#xff09;进行验证。在这篇博文中&#xff0c;我们将从头开始学习 Ajv&#xff0c;逐步介绍验证类型…

Unity3D判断屏幕中某个坐标点的位置是否在指定UI区域内

系列文章目录 unity工具 文章目录 系列文章目录前言一、使用rect.Contains()判断1-1、转换坐标1-2、代码如下&#xff1a;1-3、注意事项1-3、测试效果如下 二、使用坐标计算在不在区域内2-1、方法如下&#xff1a;2-2、注意事项 三、使用RectTransformUtility.ScreenPointToLo…

MongoDB从入门到实战之Docker快速安装MongoDB

前言 在上一篇文章中带领带同学们快速入门MongoDB这个文档型的NoSQL数据库&#xff0c;让大家快速的了解了MongoDB的基本概念。这一章开始我们就开始实战篇教程&#xff0c;为了快速把MongoDB使用起来我将会把MongoDB在Docker容器中安装起来作为开发环境使用。然后我这边MongoD…

SQL Server数据库日志查看若已满需要清理的三种解决方案

首先查看获取实例中每个数据库日志文件大小及使用情况&#xff0c;根据数据库日志占用百分比来清理 DBCC SQLPERF(LOGSPACE) 第一种解决方案&#xff1a; 在数据库上点击右键 → 选择 属性 → 选择 文件&#xff0c;然后增加数据库日志文件的文件大小。 第二种解决方案 手动…

宝塔+php+ssh+vscode+虚拟机 远程调试

远程(虚拟机)宝塔 安装扩展 配置文件添加&#xff0c;zend_extension看你虚拟机的具体位置 [Xdebug] zend_extension/www/server/php/74/lib/php/extensions/no-debug-non-zts-20190902/xdebug.so xdebug.modedebug xdebug.start_with_requesttrigger xdebug.client_host&quo…

C/C++ - 容器list

目录 容器特性 list 容器特性 使用场景 构造函数 默认构造函数 填充构造函数 范围构造函数 复制构造函数 大小函数 函数&#xff1a;size 函数&#xff1a;empty​ 函数&#xff1a;max_size​ 增加函数 函数&#xff1a;​push_back​ 函数&#xff1a;push_f…

车位检测,YOLOV8,OPENCV调用

车位检测YOLOV8NANO,opencv调用 车位检测&#xff0c;YOLOV8NANO&#xff0c;训练得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV的DNN调用&#xff0c;支持C,PYTHON,ANDROID

C#用Array类的FindAll方法和List<T>类的Add方法按关键词在数组中检索元素并输出

目录 一、使用的方法 1. Array.FindAll(T[], Predicate) 方法 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;示例 2.List类的常用方法 &#xff08;1&#xff09;List.Add(T) 方法 &#xff08;2&#xff09;List.RemoveAt(Int32) 方法 &#xff08;3&…

Bytebase 签约 Vianova,助力欧洲城市交通智能平台中 Snowflake 和 PG 的变更自动化及版本控制

在数字化发展的浪潮中&#xff0c;自动化数据库变更管理成为提升产品上线效率、降低人为失误风险的关键工具&#xff0c;同时促进流程的一致性与标准化&#xff0c;确保合规性和变更的可追溯性。近日&#xff0c;数据库 DevOps 团队协同管理工具 Bytebase 签约欧洲交通数据管理…

免费在线绘图工具有哪些好用的?线画图工具是比较好的选择?

据说一张图胜过千言万语。一张好的图片可以帮助你快速表达自己的想法&#xff0c;让自己的想法更直观清晰&#xff0c;无论是产品分析、方案选项还是技术交流。市面上有很多绘图工具。这里有一些好用又免费的工具&#xff0c;绝对会让你在几分钟内坠入爱河。 即时设计 - 可实时…

YUM | 起源 | 发展 | 运行逻辑

介绍 YUM&#xff08;Yellowdog Updater, Modified&#xff09;起源于 Red Hat Linux 发行版 up2date 工具。 最初&#xff0c;up2date 是由 Red Hat 公司提供的用于管理系统更新的工具。然而&#xff0c;社区逐渐对 up2date 出现一些不满&#xff0c;主要是由于其使用体验和…

10英寸安卓车载平板电脑丨ONERugged车载工业平板:解决农业工作效率

农业是人类社会的基石之一&#xff0c;而农业工作效率的提升一直是农民和农业专业人士关注的重要议题。随着技术的不断进步&#xff0c;车载工业平板成为了解决农业工作效率的创新解决方案。本文将探讨车载工业平板如何为农业带来巨大的改变&#xff0c;提高农民的工作效率和农…

Android Studio 安装Flutter插件但是没法创建项目

Android Studio 安装Flutter插件但是没法创建项目 如果你在Android Studio已经安装了Dart、Flutter插件&#xff0c;但是不能创建Flutter项目。 原因是因为Android Studio的版本更新&#xff0c;Android APK Support这个插件没被选中。 一旦勾选这个插件之后&#xff0c;就能…

python创建pdf文件

目录 一&#xff1a;使用reportlab库 二&#xff1a;使用使pdf库 在Python中生成PDF文件可以使用多种库&#xff0c;其中最常用的是reportlab和fpdf。以下是使用这两个库生成PDF文件的示例代码&#xff1a; 一&#xff1a;使用reportlab库 1&#xff1a;写入文字信息 from r…