当前位置: 首页 > news >正文

OpenGL编译用户着色器shader

        shader相信很多朋友们都听说过,shader就是运行再GPU上的程序。虽然是这么说,但是我们发现,很多IDE开发工具比如说visual studio 没有办法直接去运行shader代码。这是因为,许多编译器不会自动将shader文件编译成可执行的代码然后发送给GPUshader代码的编译需要开发人员手动动用GPUshader编译接口,将对应的代码编译成可执行的程序。在这里,笔者就为大家介绍,如何使用OpenGL提供的API编译用户自己的shader程序。

Shader

OpenGL渲染管线

        这里先为大家介绍一下OpenGL渲染管线如下图所示

事实上在一众着色器当中,只有顶点着色器和片元着色器是必须的,其他都是可选的。具体想要了解更多,可以去看一些资料或者书籍,比如笔者手上的这本《OpenGL编程指南》

OpenGL shader

        OpenGL 支持的 shader 语法是 OpenGL shader language 简称是glsl。这个shader语法和C++基本上是大同小异,很快就可以轻松上手。

话说到这里,让我们回顾一下上一篇文章OpenGL渲染结果移至ImGui窗口上,有细心的本有不难发现,我并没有去编译用户着色器,而且必要的顶点着色器和片元着色器都没有进行编写,但是我们仍然得到我们想要的渲染结果。其原因是,OpenGL自带默认的着色器,所以对于初学者来说,可以尽可能使用少的代码去实现自己想要的结果,这就是为什么图形接口的学习一般都是从OpenGL开始学起。

OpenGL着色器编译

Shader 类

        这里笔者写了一个Shader类,整体的代码如下

Shader.h

#pragma once#include<unordered_map>
typedef unsigned int GLenum;class Shader {
public:Shader(const std::string& filePath);~Shader();void Bind();void UBind();void UploadUniformFloat4(const std::string& name, float* value);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_ShaderID;std::string m_Name;
};

Shader.cpp

#include<glad/glad.h>
#include<string>
#include<array>
#include<fstream>
#include<iostream>#include"Shader.h"static GLenum ShaderTypeFromString(const std::string& type) {if (type == "vertex")return GL_VERTEX_SHADER;else if (type == "fragment" || type == "pixel")return GL_FRAGMENT_SHADER;std::cout << "Unknown shader type" << std::endl;return 0;
}Shader::Shader(const std::string& filePath) :m_ShaderID(0) {std::string source = ReadFile(filePath);auto shaderSource = PreProcess(source);Compile(shaderSource);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);
}Shader::~Shader() {glDeleteProgram(m_ShaderID);
}void Shader::Bind(){glUseProgram(m_ShaderID);
}void Shader::UBind(){glUseProgram(0);
}void Shader::UploadUniformFloat4(const std::string& name, float* value) {int location = glGetUniformLocation(m_ShaderID, name.c_str());glUniform4f(location, value[0], value[1], value[2], value[3]);
}std::string Shader::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());in.seekg(0, std::ios::beg);in.read(&result[0], result.size());in.close();}else {std::cout << "着色器文件没有正常打开" << std::endl;__debugbreak();}return result;
}std::unordered_map<GLenum, std::string> Shader::PreProcess(const std::string& source) {std::unordered_map<GLenum, std::string> shaderSources;const char* typeToken = "#type";size_t typeTokenLength = strlen(typeToken);size_t pos = source.find(typeToken,0);while (pos != std::string::npos) {size_t eol = source.find_first_of("\r\n", pos);if (eol == std::string::npos) {std::cout << "着色器语法出错" << std::endl;__debugbreak();}size_t begin = pos + typeTokenLength + 1;std::string type = source.substr(begin, eol - begin);if (!ShaderTypeFromString(type)) {std::cout << "这是一个不合法的着色器类型" << std::endl;__debugbreak();}size_t nextLinePos = source.find_first_of("\r\n", eol);pos = source.find(typeToken, nextLinePos);shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));}return shaderSources;
}void Shader::Compile(const std::unordered_map<GLenum, std::string>& shaderSources) {unsigned int program = glCreateProgram();//一次性至多编译两种着色器if (shaderSources.size() < 2) {std::cout << "一次性至多编译两种着色器" << std::endl;__debugbreak();}std::array<GLenum, 2> glShaderIDs;int glShaderIDIndex = 0;for (auto& kv : shaderSources) {GLenum type = kv.first;const std::string& source = kv.second;unsigned int shader = glCreateShader(type);const char* sourceCStr = source.c_str();glShaderSource(shader, 1, &sourceCStr, 0);glCompileShader(shader);int isCompiled = 0;glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);if (isCompiled == GL_FALSE) {int maxLength = 0;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);std::vector<char> infoLog(maxLength);glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);glDeleteShader(shader);std::cout << "着色器编译出错:" << infoLog.data() << std::endl;__debugbreak();break;}glAttachShader(program, shader);glShaderIDs[glShaderIDIndex++] = shader;}m_ShaderID = program;// Link our programglLinkProgram(program);// Note the different functions here: glGetProgram* instead of glGetShader*.int isLinked = 0;glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);if (isLinked == GL_FALSE) {int maxLength = 0;glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);// The maxLength includes the NULL characterstd::vector<char> infoLog(maxLength);glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);// We don't need the program anymore.glDeleteProgram(program);for (auto id : glShaderIDs)glDeleteShader(id);std::cout << "用户着色器链接失败:" << infoLog.data() << std::endl;__debugbreak();return;}for (auto id : glShaderIDs)glDetachShader(program, id);
}

着色器代码

TextureShader.glsl

#type vertex
#version 450 core
//标记为0的内存位置输入一个有两个分量的向量,这是顶点的位置
layout(location = 0) in vec2 v_Position;void main(){//顶点位置的数据进行赋值,需要转换为齐次向量gl_Position = vec4(v_Position,0.0f,1.0f);
}#type fragment
#version 450 core
//标记为0的内存位置输出一个有四个分量的向量,这是像素的颜色
layout(location = 0) out vec4 o_Color;void main(){o_Color = vec4(0.8f,0.2f,0.3f,1.0f);
}

着色器介绍

        上面虽然是一个着色器文件,其实这里面写了两个着色器,一个是顶点着色器,一个是片元着色器。#type vertex 下面的是顶点着色器,#type fragment 下面的是片元着色器。为什么这两个要一起写了?前面也介绍了,这两个着色器是必需要有的,所以笔者推荐这个着色器最好就是一起写。顶点着色器必须要有输入数据,片元着色器必须要有输出数据,不然屏幕上就看不到任何东西。

着色器编译

笔者将其分成了3个步骤进行

1、读取对应的文件内容

std::string Shader::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());in.seekg(0, std::ios::beg);in.read(&result[0], result.size());in.close();}else {std::cout << "着色器文件没有正常打开" << std::endl;__debugbreak();}return result;
}

TextureShader.glsl当中的文本信息全部转换成一个string类型当中进行存储。

2、确定着色器的类型,以及每个着色器的代码

std::unordered_map<GLenum, std::string> Shader::PreProcess(const std::string& source) {std::unordered_map<GLenum, std::string> shaderSources;const char* typeToken = "#type";size_t typeTokenLength = strlen(typeToken);size_t pos = source.find(typeToken,0);while (pos != std::string::npos) {size_t eol = source.find_first_of("\r\n", pos);if (eol == std::string::npos) {std::cout << "着色器语法出错" << std::endl;__debugbreak();}size_t begin = pos + typeTokenLength + 1;std::string type = source.substr(begin, eol - begin);if (!ShaderTypeFromString(type)) {std::cout << "这是一个不合法的着色器类型" << std::endl;__debugbreak();}size_t nextLinePos = source.find_first_of("\r\n", eol);pos = source.find(typeToken, nextLinePos);shaderSources[ShaderTypeFromString(type)] = source.substr(nextLinePos, pos - (nextLinePos == std::string::npos ? source.size() - 1 : nextLinePos));}return shaderSources;
}

对于OpenGL来说我们不光要告诉它需要编译的代码,还要告诉它编译的着色器代码是什么类型的着色器代码。在前面可以看到TextureShader.glsl当中有 #type vertex 这样的语句,这个并不是glsl语法,我们在进行文本处理的时候需要省略掉才行,不然的话编译会失败,这个只是用来告诉程序下面着色器代码是什么类型着色器的,所以这里选择返回了一个字典,用来存储着色器的类型和需要编译的程序。能够编译的是下面两段

#version 450 core
//标记为0的内存位置输入一个有两个分量的向量,这是顶点的位置
layout(location = 0) in vec2 v_Position;void main(){//顶点位置的数据进行赋值,需要转换为齐次向量gl_Position = vec4(v_Position,0.0f,1.0f);
}
#version 450 core
//标记为0的内存位置输出一个有四个分量的向量,这是像素的颜色
layout(location = 0) out vec4 o_Color;void main(){o_Color = vec4(0.8f,0.2f,0.3f,1.0f);
}

他们已经被分开存储了。 

3、编译链接着色器

void Shader::Compile(const std::unordered_map<GLenum, std::string>& shaderSources) {//注册使用下面两个着色器的程序号unsigned int program = glCreateProgram();//一次性至多编译两种着色器if (shaderSources.size() < 2) {std::cout << "一次性至多编译两种着色器" << std::endl;__debugbreak();}std::array<GLenum, 2> glShaderIDs;int glShaderIDIndex = 0;for (auto& kv : shaderSources) {GLenum type = kv.first;const std::string& source = kv.second;//注册对饮类型的着色器unsigned int shader = glCreateShader(type);const char* sourceCStr = source.c_str();glShaderSource(shader, 1, &sourceCStr, 0);//编译着色器源码glCompileShader(shader);int isCompiled = 0;glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);//检查着色器是否编译失败if (isCompiled == GL_FALSE) {int maxLength = 0;glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);std::vector<char> infoLog(maxLength);glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);glDeleteShader(shader);std::cout << "着色器编译出错:" << infoLog.data() << std::endl;__debugbreak();break;}//将着色器加入到这个程序当中glAttachShader(program, shader);glShaderIDs[glShaderIDIndex++] = shader;}m_ShaderID = program;// Link our programglLinkProgram(program);// Note the different functions here: glGetProgram* instead of glGetShader*.int isLinked = 0;glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);//检查程序是否能够链接成功if (isLinked == GL_FALSE) {int maxLength = 0;glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);// The maxLength includes the NULL characterstd::vector<char> infoLog(maxLength);glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);// We don't need the program anymore.glDeleteProgram(program);for (auto id : glShaderIDs)glDeleteShader(id);std::cout << "用户着色器链接失败:" << infoLog.data() << std::endl;__debugbreak();return;}for (auto id : glShaderIDs)glDetachShader(program, id);
}

上面大致流程就是,注册程序的编号,创建对应类型的着色器,根据下面的代码

static GLenum ShaderTypeFromString(const std::string& type) {if (type == "vertex")return GL_VERTEX_SHADER;else if (type == "fragment" || type == "pixel")return GL_FRAGMENT_SHADER;std::cout << "Unknown shader type" << std::endl;return 0;
}

可以知道 #type vertex 对应的着色器类型就是GL_VERTEX_SHADER#type fragment 对应的着色器类型就是GL_FRAGMENT_SHADER。创建了对应的着色器类型过后就是对源码进行编译,放入到程序当中,检查这个程序能否顺利接入管线当中,隔离开然后等待被调用。

使用用户自定义着色器

着色器使用,主函数代码如下

#include<glad/glad.h>
#include<GLFW/glfw3.h>#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"#include<iostream>#include"FrameBuffer.h"
#include"Shader.h"int main() {glfwInit();GLFWwindow* window = glfwCreateWindow(640, 480, "Triangles", NULL, NULL);glfwMakeContextCurrent(window);glfwSwapInterval(1); // Enable vsync// Setup Dear ImGui contextIMGUI_CHECKVERSION();ImGui::CreateContext();ImGuiIO& io = ImGui::GetIO(); (void)io;io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controlsio.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controlsio.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Dockingio.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows//io.ConfigViewportsNoAutoMerge = true;//io.ConfigViewportsNoTaskBarIcon = true;// Setup Dear ImGui styleImGui::StyleColorsDark();//ImGui::StyleColorsLight();// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.ImGuiStyle& style = ImGui::GetStyle();if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable){style.WindowRounding = 0.0f;style.Colors[ImGuiCol_WindowBg].w = 1.0f;}// Setup Platform/Renderer backendsImGui_ImplGlfw_InitForOpenGL(window, true);ImGui_ImplOpenGL3_Init("#version 130");//需要初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return -1;}float positions[6] = {-0.5f, -0.5,0.0f, 0.5f,0.5f, -0.5f};GLuint buffer = 0;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL);glEnableVertexAttribArray(0);bool show_demo_window = true;ImVec2 viewPortSize(640,480);float colorEditor[4] = {1.0f, 1.0f, 1.0f, 1.0f};FrameBuffer *pFrameBuffer = new FrameBuffer(640, 480);Shader* pShader = new Shader("assets/shaders/TextureShader.glsl");pShader->UBind();while (!glfwWindowShouldClose(window)) {pFrameBuffer->Bind();pShader->Bind();glClear(GL_COLOR_BUFFER_BIT);glDrawArrays(GL_TRIANGLES, 0, 3);pFrameBuffer->UBind();// Start the Dear ImGui frameImGui_ImplOpenGL3_NewFrame();ImGui_ImplGlfw_NewFrame();ImGui::NewFrame();ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());ImGui::Begin("ViewPort");viewPortSize = ImGui::GetContentRegionAvail();if (viewPortSize.x * viewPortSize.y > 0 && (viewPortSize.x != pFrameBuffer->GetWidth() || viewPortSize.y != pFrameBuffer->GetHeight())) {pFrameBuffer->Resize(viewPortSize.x, viewPortSize.y);glViewport(0, 0, viewPortSize.x, viewPortSize.y);}uint32_t textureID = pFrameBuffer->GetColorAttachment();ImGui::Image(reinterpret_cast<void*>(textureID), viewPortSize, { 0,1 }, { 1,0 });ImGui::End();ImGui::Begin("ColorEditor");ImGui::ColorEdit4("##colorEditor", colorEditor);ImGui::End();/*if(show_demo_window)ImGui::ShowDemoWindow(&show_demo_window);*/// RenderingImGui::Render();ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable){GLFWwindow* backup_current_context = glfwGetCurrentContext();ImGui::UpdatePlatformWindows();ImGui::RenderPlatformWindowsDefault();glfwMakeContextCurrent(backup_current_context);}glfwSwapBuffers(window);glfwPollEvents();}// CleanupImGui_ImplOpenGL3_Shutdown();ImGui_ImplGlfw_Shutdown();ImGui::DestroyContext();delete pFrameBuffer;delete pShader;glfwDestroyWindow(window);glfwTerminate();
}

得到的结果是

三角形被顺利染成了红色,有人可能会说这也有点费了这么大的劲,就把颜色改成了红色,实在是有点无聊,那让我们来做一些比较Cool的事。

我们修改一下着色器

#type vertex
#version 450 corelayout(location = 0) in vec2 v_Position;void main(){gl_Position = vec4(v_Position,0.0f,1.0f);
}#type fragment
#version 450 corelayout(location = 0) out vec4 o_Color;
//增加的片段
uniform vec4 u_Color;void main(){o_Color = u_Color;
}

主函数也修改一下

pShader->UBind();while (!glfwWindowShouldClose(window)) {pFrameBuffer->Bind();pShader->Bind();//新增片段pShader->UploadUniformFloat4("u_Color", colorEditor);glClear(GL_COLOR_BUFFER_BIT);

展示一下结果

我们现在可以通过ImGui上面的控件对三角形的颜色进行实时修改了,不用去改动程序,是不是很棒了。下面还是把整个主函数放出来,如果对里面的FrameBuffer类不了解的可以看笔者的OpenGL渲染结果移至ImGui窗口上这篇文章,同样有源代码,希望对大家能有帮助。

#include<glad/glad.h>
#include<GLFW/glfw3.h>#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"#include<iostream>#include"FrameBuffer.h"
#include"Shader.h"int main() {glfwInit();GLFWwindow* window = glfwCreateWindow(640, 480, "Triangles", NULL, NULL);glfwMakeContextCurrent(window);glfwSwapInterval(1); // Enable vsync// Setup Dear ImGui contextIMGUI_CHECKVERSION();ImGui::CreateContext();ImGuiIO& io = ImGui::GetIO(); (void)io;io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controlsio.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controlsio.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Dockingio.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;       // Enable Multi-Viewport / Platform Windows//io.ConfigViewportsNoAutoMerge = true;//io.ConfigViewportsNoTaskBarIcon = true;// Setup Dear ImGui styleImGui::StyleColorsDark();//ImGui::StyleColorsLight();// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.ImGuiStyle& style = ImGui::GetStyle();if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable){style.WindowRounding = 0.0f;style.Colors[ImGuiCol_WindowBg].w = 1.0f;}// Setup Platform/Renderer backendsImGui_ImplGlfw_InitForOpenGL(window, true);ImGui_ImplOpenGL3_Init("#version 130");//需要初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return -1;}float positions[6] = {-0.5f, -0.5,0.0f, 0.5f,0.5f, -0.5f};GLuint buffer = 0;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), NULL);glEnableVertexAttribArray(0);bool show_demo_window = true;ImVec2 viewPortSize(640,480);float colorEditor[4] = {1.0f, 1.0f, 1.0f, 1.0f};FrameBuffer *pFrameBuffer = new FrameBuffer(640, 480);Shader* pShader = new Shader("assets/shaders/TextureShader.glsl");pShader->UBind();while (!glfwWindowShouldClose(window)) {pFrameBuffer->Bind();pShader->Bind();pShader->UploadUniformFloat4("u_Color", colorEditor);glClear(GL_COLOR_BUFFER_BIT);glDrawArrays(GL_TRIANGLES, 0, 3);pFrameBuffer->UBind();// Start the Dear ImGui frameImGui_ImplOpenGL3_NewFrame();ImGui_ImplGlfw_NewFrame();ImGui::NewFrame();ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());ImGui::Begin("ViewPort");viewPortSize = ImGui::GetContentRegionAvail();if (viewPortSize.x * viewPortSize.y > 0 && (viewPortSize.x != pFrameBuffer->GetWidth() || viewPortSize.y != pFrameBuffer->GetHeight())) {pFrameBuffer->Resize(viewPortSize.x, viewPortSize.y);glViewport(0, 0, viewPortSize.x, viewPortSize.y);}uint32_t textureID = pFrameBuffer->GetColorAttachment();ImGui::Image(reinterpret_cast<void*>(textureID), viewPortSize, { 0,1 }, { 1,0 });ImGui::End();ImGui::Begin("ColorEditor");ImGui::ColorEdit4("##colorEditor", colorEditor);ImGui::End();/*if(show_demo_window)ImGui::ShowDemoWindow(&show_demo_window);*/// RenderingImGui::Render();ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable){GLFWwindow* backup_current_context = glfwGetCurrentContext();ImGui::UpdatePlatformWindows();ImGui::RenderPlatformWindowsDefault();glfwMakeContextCurrent(backup_current_context);}glfwSwapBuffers(window);glfwPollEvents();}// CleanupImGui_ImplOpenGL3_Shutdown();ImGui_ImplGlfw_Shutdown();ImGui::DestroyContext();delete pFrameBuffer;delete pShader;glfwDestroyWindow(window);glfwTerminate();
}

相关文章:

OpenGL编译用户着色器shader

shader相信很多朋友们都听说过&#xff0c;shader就是运行再GPU上的程序。虽然是这么说&#xff0c;但是我们发现&#xff0c;很多IDE开发工具比如说visual studio 没有办法直接去运行shader代码。这是因为&#xff0c;许多编译器不会自动将shader文件编译成可执行的代码然后发…...

C++ 已经知道,中序和后序,推算前序的方法。

已经知道&#xff0c;中序和后序&#xff0c;推算前序的方法。 #include<iostream> using namespace std; string ldr_str,lrd_str;//中序遍历和后序遍历 void build(int l1,int r1,int l2,int r2){if(l1>r1) return ;//边界条件,说明已经没有元素了cout<<lrd_s…...

unity打包到安卓帧率降低

这个问题遇到过很多次了我的做法就是直接设置Application.targetFrameRate60 参考...

计算机网络复习——概念强化作业

物理层负责网络通信的二进制传输 用于将MAC地址解析为IP地址的协议为RARP。 一个交换机接收到一帧,其目的地址在它的MAC地址表中查不到,交换机应该向除了来的端口外的所有其它端口转发。 关于ICMP协议,下面的论述中正确的是ICMP可传送IP通信过程中出现的错误信息。 在B类网络…...

DO、DTO、VO都是干什么的?

DO、DTO、VO 是三个常见的Java 对象&#xff0c;它们都是用来承载数据的&#xff0c;但是在不同的场景下有着不同的用途. 1.DO(Domain Object):领域对象&#xff0c;也称为实体对象。D0 通常用于数据库表的映射&#xff0c;DO中包含了实体的属性以及对实体的操作方法。DO 对应…...

深入探索 Node.js:构建强大的后端应用

亲爱的小伙伴们&#x1f618;&#xff0c;在求知的漫漫旅途中&#xff0c;若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界&#xff0c;亦或是读研论文的撰写攻略有所探寻&#x1f9d0;&#xff0c;那不妨给我一个小小的关注吧&#x1f970;。我会精心筹备&#xff0c;在…...

【Agent】构建智能诗歌创作系统:基于多 Agent 的协同创作实现

在探索大语言模型的创意应用过程中&#xff0c;我们开发了一个基于多 Agent 的智能诗歌创作系统。本文将介绍如何通过多个专业化的 Agent 协同工作&#xff0c;实现根据地点和天气信息自动创作诗歌的功能。 GitHub Code 项目地址 核心架构设计 1. Agent 基类设计 from pydan…...

【Git】:远程操作

目录 新建远程仓库 克隆远程仓库 向远程仓库推送 拉取远程仓库 配置 Git 忽略特殊文件 给命令配置别名 我们可以自己搭建⼀台运行 Git 的服务器&#xff0c;不过现阶段&#xff0c;为了学 Git 先搭个服务器绝对是小题大作。好在这个世界上有个叫 GitHub 的神奇的网站&#xff0…...

服务器数据恢复—LINUX下各文件系统删除/格式化的数据恢复可行性分析

Linux操作系统是世界上流行的操作系统之一&#xff0c;被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统&#xff08;EXT2/EXT3/EXT4/Reiserfs/Xfs&#xff0…...

基于python django的药材数据可视化系统的设计与实现,可对各类药材数据做一个统计分析可视化

研究背景 随着中医药文化的不断传承与发展&#xff0c;传统中药材的市场需求逐渐增加。然而&#xff0c;随着药材种类繁多、来源复杂、品质参差不齐&#xff0c;如何高效地管理、分析与展示中药材的相关数据&#xff0c;成为现代中药产业面临的重要课题。传统的药材数据管理方…...

docker及docker exec命令学习笔记

docker exec 是一个常用的 Docker 命令&#xff0c;允许你在已经运行的容器中执行命令或启动新的进程。以下是详细介绍和常见用法&#xff1a; 基本语法 docker exec [OPTIONS] CONTAINER COMMAND [ARG...]参数详解 1. CONTAINER指定目标容器的名字或容器 ID。可以通过以下命…...

【pyspark学习从入门到精通24】机器学习库_7

目录 聚类 在出生数据集中寻找簇 主题挖掘 回归 聚类 聚类是机器学习中另一个重要的部分&#xff1a;在现实世界中&#xff0c;我们并不总是有目标特征的奢侈条件&#xff0c;因此我们需要回归到无监督学习的范式&#xff0c;在那里我们尝试在数据中发现模式。 在出生数据…...

Unity 策略游戏地图上的网格是如何实现的

在Unity中实现策略游戏地图上的网格&#xff0c;主要涉及到地图数据的处理、地图的加载与渲染、以及玩家在地图上的移动与碰撞检测等关键步骤。以下是对这些步骤的详细解释&#xff1a; 一、地图数据的处理 收集地图数据&#xff1a;这包括地形高度、地形纹理、建筑物、树木等…...

【MySQL 进阶之路】锁详解

MySQL 锁详解 1. 锁的基本概念 锁在数据库中是用来保证数据一致性和防止并发冲突的一种机制。MySQL 中的锁可以分为不同的类型和粒度&#xff0c;每种锁都有特定的使用场景和特点。了解锁的类型、作用以及如何避免锁带来的问题是提升数据库性能和避免数据冲突的关键。 2. 锁…...

RK3588--解码H264(mpp-dec-h264-to-yuv-file)

1. 简介 源码下载:https://download.csdn.net/download/mao0514/90096131 本例完成H264格式文件解码,并保存为yuv格式文件。参考mpp_dec_test重写,进行了一部分精简。 瑞芯微提供的媒体处理软件平台(Media Process Platform,简称 MPP)是适用于瑞芯微芯片系列的 通用媒体…...

解决Conda虚拟环境中pip下载包总是到base环境的问题

conda本地创建的虚拟环境使用pip安装一些包总是安装到base环境中&#xff0c;导致无法正确进行环境隔离&#xff0c;下面是一些解决办法 方法一、使用python -m pip安装 1.1、验证虚拟环境的pip版本是哪个版本&#xff0c;如下所示&#xff0c;本人的demo虚拟环境直接使用pip…...

PyCharm 中设置虚拟环境

在 PyCharm 中设置虚拟环境的步骤如下&#xff1a; 1. 创建新项目时设置虚拟环境 1. 打开 PyCharm 并选择 New Project。 2. 在 Location 中指定项目路径。 3. 在右侧的 Python Interpreter 下&#xff0c;选择 New Environment。 Environment: 选择 Virtualenv。 Loca…...

M9484C VXG 矢量信号发生器- 110GHz-

M9484C VXG 矢量信号发生器 - 110GHz- M9484C VXG 是一款矢量信号发生器&#xff0c;在每个通道上提供 2.5 GHz 调制带宽&#xff0c;能够生成高达 54 GHz 的信号。 这款 VXG 矢量信号发生器可以组成经过校准和同步的全方位综合解决方案&#xff0c;帮助您更快测试下一代无线…...

机器学习详解(3):线性回归之代码详解

文章目录 1 数据预处理2 构建线性回归模型并绘制回归线初始化方法前向传播&#xff1a;forward_propagation代价函数&#xff1a;cost_function反向传播&#xff1a;backward_propagation参数更新&#xff1a;update_parameters训练方法&#xff1a;train代码运行结果 3 使用Py…...

工业检测基础-工业相机选型及应用场景

以下是一些常见的工业检测相机种类、检测原理、应用场景及选型依据&#xff1a; 2D相机 检测原理&#xff1a;基于二维图像捕获&#xff0c;通过分析图像的明暗、纹理、颜色等信息来检测物体的特征和缺陷.应用场景&#xff1a;广泛应用于平面工件的外观检测&#xff0c;如检测…...

标准状态下一个气体分子每秒平均碰撞次数的估算

要估算在标准状态&#xff08;0C, 1个大气压&#xff09;下&#xff0c;一个气体分子在1秒内与其他分子的碰撞次数&#xff0c;我们可以使用一些基本的物理和化学原理。这个过程涉及到气体动力学理论&#xff0c;特别是麦克斯韦-玻尔兹曼分布。 计算气体分子的平均速率&#xf…...

使用PHPUnit使用本地调试代替远程调试,快速提高开发效率

Laravel 是一个在 Linux 环境下表现非常出色的 PHP 框架&#xff0c;但它在 Windows 环境下可能会遇到一些兼容性和配置问题。为了调试或没试的方便可以在 Windows 环境下进行 Laravel PHPUnit进行本地调试和测试。 本地主要针对断点调试效果非常高效。 在 Laravel 中&#x…...

Android 镜像模式和扩展模式区别探讨-Android14

Android 镜像模式和扩展模式区别探讨 1、区分镜像模式和扩展模式1.1 扩展屏是否有显示内容1.2 镜像模式显示条件 2、镜像模式界面 同屏显示和异屏显示探讨DisplayManagerService启动及主屏添加-Android13 Android主副屏显示-Android14 1、区分镜像模式和扩展模式 LogicalDispla…...

链表头文件大更新!!!

引言 原文章:链表简介及自制链表操作头文件_自己写一个链表头文件-CSDN博客。 此次更新添加了更多功能&#xff0c;让改头文件更 人性化 。 安装教程见原文章。 介绍 linked_list.h 头文件 linked_list.h 是一个 C 头文件&#xff0c;定义了一个模板类 LinkedList&#xff…...

ROS2创建 base 包用于其他模块的参数配置和头文件依赖

Demo 背景 ROS2项目开发中存在以下需求&#xff1a;有多个包需要读取一些共同的配置项(以txt或者yaml形式存在&#xff09;&#xff0c;且依赖于一些公用的utils工具代码(C)。Solution: 创建一个 base_config 包来“存放” 配置文件和公用的头文件。gitee address: Gitee/CDal…...

设计模式の软件设计原则

文章目录 前言一、聚合&组合&继承&依赖1.1、继承1.2、组合1.3、聚合1.4、依赖 二、单一职责原则2.1、单一职责原则反面案例2.2、单一职责原则反面案例的改进 三、接口隔离原则3.1、接口隔离原则反面案例3.2、接口隔离原则反面案例的改进 四、依赖倒转原则4.1、依赖…...

【python自动化四】日志打印

我们在进行自动化测试时&#xff0c;需要打印过程日志和结果日志等&#xff0c;这里记录下日志的相关配置。这里我们直接自己新建一个logger。 先贴上日志代码如下&#xff0c;可根据需要修改&#xff1a; import logging import os import timefrom logging.handlers import …...

E498 ThinkPHP+MYSQL+LW+纯洁婚纱网站系统的设计与实现 源码 配置 文档 全套资料

婚纱网站系统的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 在互联网和电子商务迅速发展的今天&#xff0c;网络已经是人们日常生活所不可缺少的信息获取渠道&#xff0c;人们日常生活基本已完全被网络所覆盖&#xff0c;互联网影响到各…...

【PostgreSQL系列】列类型从整数转换为 UUID

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

shell脚本实战案例

文章目录 实战第一坑功能说明脚本实现 实战第一坑 实战第一坑&#xff1a;在Windows系统写了一个脚本&#xff0c;比如上面&#xff0c;随后上传到服务&#xff0c;执行会报错 原因&#xff1a; 解决方案&#xff1a;在linux系统touch文件&#xff0c;并通过vim添加内容&…...

VAE为什么叫变分(variational),相对于AE有什么区别。

VAE为什么叫变分&#xff08;variational&#xff09;&#xff0c;相对于AE有什么区别。 VAE为什么叫变分&#xff08;variational&#xff09;&#xff1f;VAE相对于AE有什么区别&#xff1f; VAE为什么叫变分&#xff08;variational&#xff09;&#xff1f; 变分自编码器&…...

Codeforces Round 991 (Div. 3)

补题连接 A. Line Breaks 思路&#xff1a;从头开始累加单词个数&#xff0c;超过m就退出。 代码&#xff1a; #include <bits/stdc.h> using namespace std; #define int long longvoid solve() {int n, m, k;cin >> n >> m;vector<string> a(n);…...

红日靶场vulnstark 4靶机的测试报告[细节](一)

目录 一、测试环境 1、系统环境 2、注意事项 3、使用工具/软件 二、测试目的 三、操作过程 1、信息搜集 2、漏洞利用Getshell ①Struts 2 s2-045漏洞 手工利用s2-45漏洞 Msf综合利用 ②Tomcat框架(CVE-2017-12615) ③phpMyAdmin(CVE-2018-12613) 构造语句写入冰蝎木…...

Android上运行OpenCV(Android Studio)

用Android Studio的话&#xff0c;整体来说没什么难的&#xff0c;照着教程来做就好了。 【OpenCV】OpenCV库的安装 - Android与OpenCV系列教程_哔哩哔哩_bilibili 主要就是导入module&#xff0c;然后加入依赖。代码只有几行。 if(OpenCVLoader.initLocal()){Toast.makeText(…...

代码随想录算法训练营day50|动态规划12

不同的子序列 给定一个字符串 s 和一个字符串 t &#xff0c;计算在 s 的子序列中 t 出现的个数。、 编辑距离中的删除元素&#xff0c;其实就是直接变数字&#xff0c;其只删除原来的较长的数组里的元素 递推模拟&#xff0c;使用s的最后一个元素匹配&#xff0c;或者删除…...

图像生成-扩散模型的经典之作DDPM

论文&#xff1a;https://arxiv.org/pdf/2006.11239 项目&#xff1a;https://github.com/hojonathanho/diffusion Denoising Diffusion Probabilistic Models (DDPM) 是一种生成模型&#xff0c;它通过一系列逐步添加噪声的过程将数据点映射到一个简单的先验分布&#xff08;…...

知识拓展 ?. 连选链操作

?. 连选链操作 ?. 可选链操作符 ?. 是可选链操作符&#xff0c;常用于访问引用类型具有不确定性的内部数据时&#xff0c;比如要访问一个对象中的数组&#xff0c;不确定数组一定有数据就可以使用 ? 取读取它的 length 属性&#xff0c;如果对象没有这个属性也仅会返回 …...

API设计指南:详解HTTP状态码错误解析、HTTP方法及参数命名规则

目录 1、HTTP API规范1.1 原则1.2 协议1.3 版本1.4 路径1.5 HTTP 方法&#xff08;Method&#xff09;1.6 过滤信息1.7 参数命名1.8 HTTP 状态码&#xff08;Response Code&#xff09;1.9 鉴权 2、状态码2.1 API返回基础规范2.2 常见的 HTTP 状态码2.3 API错误信息应该放到响应…...

【D3.js in Action 3 精译_043】5.1 饼图和环形图的创建(三):圆弧的绘制

当前内容所在位置&#xff1a; 第五章 饼图布局与堆叠布局 ✔️ 5.1 饼图和环形图的创建 ✔️ 5.1.1 准备阶段&#xff08;一&#xff09;5.1.2 饼图布局生成器&#xff08;二&#xff09;5.1.3 圆弧的绘制&#xff08;三&#xff09; ✔️5.1.4 数据标签的添加&#xff08;四&…...

7. 一分钟读懂“单例模式”

7.1 模式介绍 单例模式就像公司里的 打印机队列管理系统&#xff0c;无论有多少员工提交打印任务&#xff0c;大家的请求都汇总到唯一的打印管理中心&#xff0c;按顺序排队输出。这个中心必须全局唯一&#xff0c;避免多个队列出现资源冲突&#xff0c;保证打印任务井然有序。…...

如何让谷歌外链看起来更真实?

在SEO优化过程中&#xff0c;外链的自然性往往会被忽视&#xff0c;尤其是在一些急于见效的策略中&#xff0c;外链往往集中在高权重的少数几个网站上&#xff0c;导致外链结构单一且缺乏多样性。这样的外链网络容易让搜索引擎怀疑其真实性&#xff0c;进而影响网站排名。如何才…...

C标签和 EL表达式的在前端界面的应用

目录 前言 常用的c标签有&#xff1a; for循环 1 表示 普通的for循环的 2 常在集合中使用 表示 选择关系 1 简单的表示如果 2 表示如果。。否则。。 EL表达式 格式 &#xff1a; ${属性名/对象/ 集合} 前言 本篇博客介绍 c标签和el表达式的使用 使用C标签 要引入 …...

Luma 视频生成 API 对接说明

Luma 视频生成 API 对接说明 随着 AI 的应用变广&#xff0c;各类 AI 程序已逐渐普及。AI 已逐渐深入到人们的工作生活方方面面。而 AI 涉及的行业也越来越多&#xff0c;从最初的写作&#xff0c;到医疗教育&#xff0c;再到现在的视频。 Luma 是一个专业高质量的视频生成平…...

嵌入式基础:Linux C语言:Day7

重点&#xff1a; strlen()函数\strcpy()函数\strcat实现\strcmp()实现 数组的清空&#xff1a;bzero函数、memset函数 一、字符数组 <1> 概念 字符数组本质上就是一个数组&#xff0c;保存一个个字符&#xff0c;也一般用来保存字符串 字符串由多个字符组成的一个字符…...

阿里云盘permission denied

问题是执行 ./aliyunpan 时遇到了 Permission denied 的错误。这通常是因为文件没有执行权限。以下是解决问题的步骤&#xff1a; 检查文件权限 运行以下命令检查文件的权限&#xff1a; ls -l aliyunpan输出中会看到类似以下内容&#xff1a; -rw-r--r-- 1 user group 123…...

Flink学习连载文章12--FlinkSQL高级部分

eventTime 测试数据如下&#xff1a; {"username":"zs","price":20,"event_time":"2023-07-17 10:10:10"} {"username":"zs","price":15,"event_time":"2023-07-17 10:10:3…...

缓冲区溢出基础与实践

缓冲区溢出 缓冲区溢出是指当计算机向缓冲区内填充数据时超过了缓冲区本身的容量&#xff0c;溢出的数据覆盖在合法数据上。理想的情况是&#xff1a;程序检查数据长度并不允许输入超过缓冲区长度的字符&#xff0c;但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹…...

matlab figure函数 single 数据类型

1.matlab figure函数详细介绍 在MATLAB中&#xff0c;figure函数用于创建新的图形窗口或激活现有的图形窗口。以下是figure函数的详细介绍和用法&#xff1a; 基本用法 创建新图形窗口&#xff1a;不带任何参数调用figure会创建一个新的图形窗口&#xff0c;并将其设为当前活…...

量化交易系统开发-实时行情自动化交易-8.15.Ptrade/恒生平台

19年创业做过一年的量化交易但没有成功&#xff0c;作为交易系统的开发人员积累了一些经验&#xff0c;最近想重新研究交易系统&#xff0c;一边整理一边写出来一些思考供大家参考&#xff0c;也希望跟做量化的朋友有更多的交流和合作。 接下来会对于Ptrade/恒生平台介绍。 P…...

Vue03

目录 一、今日目标 1.生命周期 2.综合案例-小黑记账清单 3.工程化开发入门 4.综合案例-小兔仙首页 二、Vue生命周期 三、Vue生命周期钩子 四、生命周期钩子小案例 1.在created中发送数据 六、工程化开发模式和脚手架 1.开发Vue的两种方式 2.Vue CLI脚手架 基本介绍…...