报名小程序怎么制作/seo推广是什么工作
shader相信很多朋友们都听说过,shader就是运行再GPU上的程序。虽然是这么说,但是我们发现,很多IDE开发工具比如说visual studio 没有办法直接去运行shader代码。这是因为,许多编译器不会自动将shader文件编译成可执行的代码然后发送给GPU,shader代码的编译需要开发人员手动动用GPU的shader编译接口,将对应的代码编译成可执行的程序。在这里,笔者就为大家介绍,如何使用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相信很多朋友们都听说过,shader就是运行再GPU上的程序。虽然是这么说,但是我们发现,很多IDE开发工具比如说visual studio 没有办法直接去运行shader代码。这是因为,许多编译器不会自动将shader文件编译成可执行的代码然后发…...

过期策略、内存淘汰机制
1.过期策略:请求时删除 定期删除 请求时删除:使用key之前,检查是否过期,属于一种被动的处理方式。 因此,过期时间到了不表示这个key真的被删除了 定期删除:Redis默认每隔100ms检查,有过期ke…...

Scala的正则表达式
package hfdobject Test35_3 {def main(args: Array[String]): Unit {println("a\tb")//定义一个规则 正则表达式//1. .表示除了换行之外的其他的任意单个字符//2. \d等于[0-9] 匹配一个数字//3. \D除了\d之外的其他的任意字符,表示非数字//4. \w等价于[…...

关于睡懒觉
我们经常听到一个词:睡懒觉。 我认为,睡懒觉这个词,是错误的。 人,是需要睡眠的,睡不够,就不会醒。睡够了,自然会醒,也不想继续睡。不信你试试,睡够了,你…...

【算法day10】栈与队列:拓展与应用
题目引用 逆波兰表达式求值滑动窗口最大值前k个高频元素 1.逆波兰表达式求值 给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意: 有效的算符为 ‘’、‘-’、‘*’ 和…...

爆肝Android JNI - 延展Android蓝牙JNI学习
零. 前言 由于Bluedroid的介绍文档有限,以及对Android的一些基本的知识需要了(Android 四大组件/AIDL/Framework/Binder机制/JNI/HIDL等),加上需要掌握的语言包括Java/C/C++等,加上网络上其实没有一个完整的介绍Bluedroid系列的文档,所以不管是蓝牙初学者还是蓝牙从业人员…...

总篇:Python3+Request+Pytest+Allure+Jenkins接口自动化框架设计思路
1、技术选型 Python3 Python 是一种广泛使用的高级编程语言,具有简洁、易读、易维护的特点。 Python 拥有丰富的第三方库,可以方便地进行接口测试的开发。 Request Request 是一个强大的 HTTP 库,用于发送 HTTP 请求和处理响应。 Request 支持多种 HTTP 方法,如 GET、P…...

Java的Map介绍以及常见方法和三种遍历方式
Java的Map介绍以及常见方法和三种遍历方式 1 Java 中的 Map 介绍 在 Java 中,Map 是一个接口,它提供了一种存储键值对(key-value pairs)的方式。每个键(key)都关联着一个值(value)…...

C/C++基础知识复习(39)
1) 什么是封装性?C中如何实现封装? 封装性(Encapsulation)是面向对象编程中的一个重要概念,它指的是将对象的状态(数据)和行为(方法)绑定在一起,并且通过访问…...

自建服务器,数据安全有保障
在远程桌面工具的选择上,向日葵和TeamViewer功能强大,但都存在收费昂贵、依赖第三方服务器、数据隐私难以完全掌控等问题。相比之下,RustDesk 凭借开源免费、自建服务的特性脱颖而出!用户可以在自己的服务器上部署RustDesk服务端&…...

CCF-GESP 编程能力认证 C++ 七级 2024年9月份判断题详细解析
链接:CCF-GESP 编程能力认证 C 七级 2024年9月份选择题详细解析-CSDN博客 目录 第 1 题 第 2 题 第 3 题 第 4 题 第 5 题 第 6 题 第 7 题 第 8 题 第 9 题 第 10 题 第 1 题 表达式 a << 1 的结果为 a(错误) 【a是字符常…...

使用Vue3+Echarts实现加载中国地图,点击省份地图下钻(完整教程)
一. 前言 在众多 ECharts 图表类型中,开发者始终绕不开的有各种各样的地图开发,关于地图开发,可能比其他图表相对繁琐一些,其实说简单也简单,说复杂也复杂,其中不乏有层级地图、3D 地图等,感觉…...

NUMA-非统一内存访问架构
NUMA(Non-Uniform Memory Access) 是一种计算机内存架构,主要用于多处理器系统。NUMA架构中的每个处理器都连接到自己的本地内存,并且可以访问其他处理器的内存,但访问其他处理器的内存速度较慢。 内核通过调度优化进…...

初识交换机和路由器
目录 初识交换机和路由器交换机路由器主要区别工作流程如果是交换机:如果是路由器 初识交换机和路由器 左为路由器,右为交换机 交换机 交换机的前身是集线器,集线器是物理层的设备,有很多接口,当一台计算机A想发消息…...

SQL面试题——滴滴SQL面试题 取出累计值与1000差值最小的记录
滴滴SQL面试题 取出累计值与1000差值最小的记录 今天的题目来自滴滴出行 已知有表cost_detail包含id和money两列,id为自增,请累加计算money值,并求出累加值与1000差值最小的记录。 +-----+--------+ | id | money | +-----+--------+ | 1 | 200 | | 2 | 300 …...

openEuler 22.03 使用cephadm安装部署ceph集群
目录 目的步骤规格步骤ceph部署前准备工作安装部署ceph集群ceph集群添加node与osdceph集群一些操作组件服务操作集群进程操作 目的 使用ceph官网的cephadm无法正常安装,会报错ERROR: Distro openeuler version 22.03 not supported 在openEuler上实现以cephadm安装部…...

C++哈希(一)
1.底层结构 顺序结构以及平衡中,元素关键码与其存储位置之间没有相对应的关系,因此在查找一个元素时,要经过关键码的多次比较。顺序查找的时间复杂度为O(N)。 理想的搜索方法:可以不经过比较,依次直接从表中直接搜索…...

阿拉丁论文助手:一键点亮学术之路
在学术研究的海洋中,每一位学者都渴望拥有一盏能够照亮前行道路的神灯。阿拉丁论文助手,正是这样一盏神奇的灯,它以其先进的人工智能技术和丰富的学术资源,为学者们的学术写作提供了全方位的支持。 一、阿拉丁论文助手简介 阿拉丁…...

视频码率到底是什么?详细说明
视频码率(Video Bitrate)是指在单位时间内(通常是每秒)传输或处理的视频数据量,用比特(bit)表示。它通常用来衡量视频文件的压缩程度和质量,码率越高,视频质量越好&#…...

嵌入式学习(17)-stm32F407串口使用注意事项
一、概述 配置串口时串口的接收一直不好使,对比例程发现了问题: 在网上也找了一些资料供参考“STM32F4的串口RX引脚不能被设置为输入是因为串口的接收(RX)功能是由硬件电路实现的,无法通过软件配置来控制。串口接收功…...

汽车48V电气系统
汽车48V电气系统 汽车48V电气系统汽车48V电气系统设计汽车48V电气系统测试汽车48V系统是48V供电和12V供电共存的么?48V供电系统是如何与12V供电系统共存的?48V电气系统测试的难点有哪些?在汽车48V电气系统通信测试中,如何向12V的控制器和48V的控制器供电?汽车48V电气系统通…...

【人工智能基础05】决策树模型习题
文章目录 1. 归一化对决策树的影响2. 选择决策树模型3. 决策树计算4. 基尼系数的优势5. 在叶子上使用线性模型的优缺点 1. 归一化对决策树的影响 题目:对于一些机器学习模型(例如,神经网络),对特征进行归一化(normaliz…...

rockit 学习、开发笔记(六)(VENC)
前言 上节我们讲到了VDEC解码模块,那当然少不了VENC编码模块了,一般有编解码的需求都是为了压缩视频的大小,方便减少传输所占用的带宽。 概述 VENC 模块,即视频编码模块。本模块支持多路实时编码,且每路编码独立&am…...

spring技术点
引入对象 Autowired 和 Resource的区别 Autowired 和 Resource的区别 valid 参数校验 jarkata进行SpringMVC校验 常规当前进行校验的配置操作,参考文档如下进行操作。 SpringMVC校验注解不生效 List类型参数校验 由于list类型默认不能进行标注校验实现&#x…...

R语言使用“纽约市数据集中的优步皮卡”数据创建不同年度时间范围的可视化
一、项目背景 为了分析纽约市优步(https://baike.baidu.com/item/Uber/14900884)皮卡在不同年度的使用情况,需要利用R语言进行数据可视化。通过对比不同年度的数据,可以揭示出优步皮卡使用的趋势和变化。 二、数据准备 数据集&a…...

电阻计RM3544、RM3545的使用
目录: 一、电阻计与PC通讯 1、硬件连接 2、RmLogger.exe的使用 二、RM3545测量35uΩ电阻 一、电阻计与PC通讯 1、硬件连接 可以设置USB或COM口(串口)连接PC,也可以设置为“打印”输出。 1)使用USB连接PC 2)使用串口连接PC …...

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

《鸟哥的Linux私房菜基础篇》---4 Linux档案的压缩与打包
目录 一、常见的压缩包的扩展名 二、常见的压缩和解压指令 1、tar 2、tar gzip(.tar.gz) (或 .tgz) 3、tar bzip2(.tar.bz2) 4、zip 5、gzip 6、bzip2 7、xz 8、rar 9、7z 三、安装解压工具 一、常见的压缩包的扩展…...

Springboot 2.7+解决跨域问题,到底是在SpringBoot中添加拦截器还是修改Nginx配置
文章目录 1摘要2 核心代码2.1 SpringBoot 全局跨域拦截器2.2 Nginx 配置跨域处理2.3 Nginx 和 SpringBoot 同时添加允许跨域处理会怎么样? 3 推荐参考资料 1摘要 跨域问题报错信息: Referrer Policy:strict-origin-when-cross-origin跨域问题是在前后端分离的情况…...

Spring中Bean的作用域深入剖析与技术实践
前言 Spring框架作为Java企业级应用开发中的中流砥柱,提供了强大的依赖注入(DI)和面向切面编程(AOP)等功能。在Spring框架中,Bean的作用域(Scope)是一个非常重要的概念,…...