LearnOpenGL 学习(入门--三角形,着色器,纹理)
你好,三角形
下面,你会看到一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。
图形渲染管线的第一个部分是顶点着色器(Vertex Shader),它把一个单独的顶点作为输入。顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。
顶点着色器阶段的输出可以选择性地传递给几何着色器(Geometry Shader)。几何着色器将一组顶点作为输入,这些顶点形成图元,并且能够通过发出新的顶点来形成新的(或其他)图元来生成其他形状。在这个例子中,它从给定的形状中生成第二个三角形。
图元装配(Primitive Assembly)阶段将顶点着色器(或几何着色器)输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并将所有的点装配成指定图元的形状;本节例子中是两个三角形。
图元装配阶段的输出会被传入光栅化阶段(Rasterization Stage),这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
OpenGL中的一个片段是OpenGL渲染一个像素所需的所有数据。
片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。
在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶段,我们叫做Alpha测试和混合(Blending)阶段。这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend)。所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。
顶点输入
OpenGL仅当3D坐标在3个轴(x、y和z)上-1.0到1.0的范围内时才处理它。所有在这个范围内的坐标叫做标准化设备坐标(Normalized Device Coordinates),此范围内的坐标最终显示在屏幕上(在这个范围以外的坐标则不会显示)。
标准化设备坐标:
一个简单的着色器:
#include <GL/glew.h>
#include <GLFW/glfw3.h>#include <iostream>static unsigned int CompileShader(unsigned int type, const std::string& source) {unsigned int id = glCreateShader(type);const char* src = source.c_str();glShaderSource(id, 1, &src, nullptr);glCompileShader(id); //Syntax error handlingint result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE) { //编译失败 获取错误信息int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);char* message = (char*)alloca(length * sizeof(char));glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile" << (type == GL_VERTEX_SHADER ? "vertexShader" : "fragmentShader ") << " shader!" << std::endl;std::cout << message << std::endl;glDeleteShader(id);return 0;}return id;
}//我们向OpenGL提供我们实际的着色器源代码,我的着色器文本。我们想让OpenGL编译那个程序,将这两个链接到一个
//独立的着色器程序,然后给我们一些那个着色器返回的唯一标识符,我们就可以绑定着色器并使用
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {unsigned int program = glCreateProgram();unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);//将这两个着色器附加到我们的程序上glAttachShader(program, vs);glAttachShader(program, fs);//链接程序glLinkProgram(program);glValidateProgram(program);//删除一些无用的中间文件glDeleteShader(vs);glDeleteShader(fs);return program;
}int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);if (glewInit() != GLEW_OK) { //should after MakeContextCurrent std::cout << "Error" << std::endl;}std::cout << glGetString(GL_VERSION) << std::endl;float positions[6] = {-0.5f, -0.5f,0.0f, 0.5f,0.5f, -0.5f};unsigned int buffer; glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);//glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数。
//它的第一个参数是目标缓冲的类型:顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上。第二个参数指定传输
//数据的大小(以字节为单位);用一个简单的sizeof计算出顶点数据大小就行。第三个参数是我们希望发送的
//实际数据。glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float),positions,GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8, 0);std::string vertexShader ="#version 330 core\n""\n""layout(location = 0) in vec4 position;\n""\n""void main()\n""{\n""gl_Position = position;\n""}\n";std::string fragmentShader = "#version 330 core\n""\n""layout(location = 0) out vec4 color;\n""\n""void main()\n""{\n""color = vec4(1.0,0.0,0.0,1.0)\n;""}\n";unsigned int shader = CreateShader(vertexShader, fragmentShader);//绑定着色器glUseProgram(shader); /* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);glDrawArrays(GL_TRIANGLES, 0, 3);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}//删除着色器glDeleteProgram(shader);glfwTerminate();return 0;
}
链接顶点属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);
glVertexAttribPointer函数的参数非常多,所以我会逐一介绍它们:
- 第一个参数指定我们要配置的顶点属性。我们在顶点着色器中有这样的句子:layout(location = 0)定义了一个属性,希望把数据传入这个属性就在这里传入 0
- 第二个参数指定顶点属性的大小。顶点属性是一个
vec3
,它由3个值组成,所以大小是3。 - 第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中
vec*
都是由浮点数值组成的)。 - 下个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。
- 第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个
float
之后,我们把步长设置为3 * sizeof(float)
。(译注: 这个参数的意思简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节)。 - 最后一个参数的类型是
void*
,所以需要我们进行这个奇怪的强制类型转换。它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。我们会在后面详细解释这个参数。
顶点数组对象
#include "VertexArray.h"#include "Render.h"
#include "VertexBufferLayout.h"VertexArray::VertexArray() {GLCall(glGenVertexArrays(1, &m_RenderID));GLCall(glBindVertexArray(m_RenderID));
}VertexArray::~VertexArray() {GLCall(glDeleteVertexArrays(1, &m_RenderID));
}void VertexArray::AddBuffer(const VertexBuffer& vb, const VertexBufferLayout& layout) {Bind();vb.Bind();const std::vector<VertexBufferElement>& elements = layout.GetElements();unsigned int offset = 0;for (unsigned int i = 0; i < elements.size();i++) {const auto& element = elements[i];GLCall(glEnableVertexAttribArray(i));GLCall(glVertexAttribPointer(i, element.count, element.type, element.normalized, layout.GetStride(), (const void*)offset));offset += element.count * VertexBufferElement::GetSizeOfType(element.type);}
}void VertexArray::Bind() const {GLCall(glBindVertexArray(m_RenderID));
}
void VertexArray::UnBind() const {GLCall(glBindVertexArray(0));
}
这个顶点数组类可以绑定多个缓冲区,可以使用addbuffer函数添加缓冲区,函数会自动计算glVertexAttribPointer 函数的参数并调用
着色器
一个典型的着色器有下面的结构:
#version version_number
in type in_variable_name;
in type in_variable_name;out type out_variable_name;uniform type uniform_name;void main()
{// 处理输入并进行一些图形操作...// 输出处理过的结果到输出变量out_variable_name = weird_stuff_we_processed;
}
数据类型
向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
纹理
纹理环绕方式
当纹理坐标超出默认范围时,每个选项都有不同的视觉效果输出。我们来看看这些纹理图像的例子:
纹理图像是方形数组,纹理坐标通常可定义成一、二、三或四维形式,称为 s,t,r和q坐标
首先,其中的q是一个缩放因子,相当于顶点坐标中的 w 。实际在纹理读取中的坐标应该分别是 s/q、t/q、r/q。默认情况下,q 是1.0。通常情况下貌似没什么用,但是在一些产生纹理坐标的高级算法比如阴影贴图中,比较有用。
纹理过滤
纹理坐标不依赖于分辨率(Resolution),它可以是任意浮点值,所以OpenGL需要知道怎样将纹理像素(Texture Pixel,也叫Texel,译注1)映射到纹理坐标。当一个物体很大但是纹理的分辨率很低的时候就需要纹理过滤(Texture Filtering)
GL_NEAREST(也叫邻近过滤,Nearest Neighbor Filtering)是OpenGL默认的纹理过滤方式。当设置为GL_NEAREST的时候,OpenGL会选择中心点最接近纹理坐标的那个像素。下图中你可以看到四个像素,加号代表纹理坐标。左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色:
GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大。下图中你可以看到返回的颜色是邻近像素的混合色:
对比图:
//设置图片缩小放大,嵌入的采样方式GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
多级渐远纹理(mipmap)
手工为每个纹理图像创建一系列多级渐远纹理很麻烦,OpenGL有一个glGenerateMipmap函数,在创建完一个纹理后调用它OpenGL就会承担接下来的所有工作了。
为了指定不同多级渐远纹理级别之间的过滤方式,你可以使用下面四个选项中的一个代替原有的过滤方式:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
加载于创建纹理
cherno openGL 课程写的 texcure 代码:
#include "Texture.h"
#include "stb_image.h"Texture::Texture(const std::string& path) : m_RenderID(0), m_FilePath(path), m_LocationBuffer(nullptr),m_Width(0), m_Height(0), m_BPP(0) {stbi_set_flip_vertically_on_load(1); //将图片上下翻转m_LocationBuffer = stbi_load(path.c_str(), &m_Width,&m_Height,&m_BPP,4);GLCall(glGenTextures(1, &m_RenderID));GLCall(glBindTexture(GL_TEXTURE_2D, m_RenderID));//设置图片缩小放大,嵌入的采样方式GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA,GL_UNSIGNED_BYTE, m_LocationBuffer));GLCall(glBindTexture(GL_TEXTURE_2D, 0));//生成 mipmapglGenerateMipmap(GL_TEXTURE_2D);//释放图像内存stbi_image_free(m_LocationBuffer);if (m_LocationBuffer) {stbi_image_free(m_LocationBuffer);}
}Texture::~Texture() {GLCall(glDeleteTextures(1, &m_RenderID));
}//slot 是想要绑定纹理的插槽
//一般电脑支持最大32个,手机支持8个,但是具体的还得具体看
void Texture::Bind(unsigned int slot) const {GLCall(glActiveTexture(GL_TEXTURE0 + slot));GLCall(glBindTexture(GL_TEXTURE_2D, m_RenderID));
}void Texture::UnBind() const {GLCall(glBindTexture(GL_TEXTURE_2D, 0));
}
使用载入的图片数据生成一个纹理了。纹理可以通过glTexImage2D来生成:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
参数解释:
- 第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
- 第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
- 第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有
RGB
值,因此我们也把纹理储存为RGB
值。 - 第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
- 下个参数应该总是被设为
0
(历史遗留的问题)。 - 第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
- 最后一个参数是真正的图像数据。
将两个图片混合显示,然后有一个滑动条可以调节每个图片混合的权重:
#include "TestTextureBlend.h"#include "Render.h"
#include "imgui/imgui.h"#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"namespace test {TestTextureBlend::TestTextureBlend() : m_Proj(glm::ortho(0.0f,960.0f,0.0f,720.0f,-1.0f,1.0f)),m_View(glm::translate(glm::mat4(1.0f), glm::vec3(100, 0, 0))),m_TranslationA(0.5f){float positions[] = {100.0f, 100.0f, 0.0f, 0.0f, // 0200.0f, 100.0f, 1.0f, 0.0f, // 1200.0f, 200.0f, 1.0f, 1.0f, // 2100.0f, 200.0f, 0.0f, 1.0f // 3};unsigned int indices[] = {0, 1, 2,2, 3, 0};GLCall(glEnable(GL_BLEND));GLCall(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));m_VAO = std::make_unique<VertexArray>();m_VertexBuffer = std::make_unique<VertexBuffer>(positions, static_cast<unsigned long long>(4) * 4 * sizeof(float));VertexBufferLayout layout;layout.Push<float>(2);layout.Push<float>(2);m_VAO->AddBuffer(*m_VertexBuffer, layout);m_IndexBuffer = std::make_unique<IndexBuffer>(indices, 6);m_Shader = std::make_unique<Shader>("res/shaders/TestTextureBlend.shader"); m_Shader->Bind();m_Shader->SetUniform4f("u_Color", 0.2f, 0.3f, 0.8f, 1.0f);m_Shader->SetUniform1i("u_Texture", 0);m_Shader->SetUniform1i("u_Texture1", 1);m_Texture0 = std::make_unique<Texture>("res/textures/14fnm.png");m_Texture1 = std::make_unique<Texture>("res/textures/smileface.png");}TestTextureBlend::~TestTextureBlend() {}void TestTextureBlend::OnUpdate(float delteTime) {}void TestTextureBlend::OnRender() {GLCall(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));GLCall(glClear(GL_COLOR_BUFFER_BIT));constexpr Render renderer;m_Texture0->Bind(0);m_Texture1->Bind(1);{const glm::mat4 model = glm::mat4(1.0f);const glm::mat4 mvp = m_Proj * m_View * model;m_Shader->Bind();m_Shader->SetUniformMat4f("u_MVP", mvp);m_Shader->SetUniform1f("u_percent", m_TranslationA);GLCall(renderer.Draw(*m_VAO, *m_IndexBuffer, *m_Shader));}}void TestTextureBlend::OnImGuiRender() {ImGui::SliderFloat("m_TranslationA", &m_TranslationA, 0.0f, 1.0f);ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", static_cast<double>(1000.0f / ImGui::GetIO().Framerate),static_cast<double>(ImGui::GetIO().Framerate));}
}
#shader vertex
#version 330 corelayout(location = 0) in vec4 position;
layout(location = 1) in vec2 texCoord;out vec2 v_TexCoord;
uniform mat4 u_MVP;void main()
{gl_Position = u_MVP * position;v_TexCoord = texCoord;
};#shader fragment
#version 330 corelayout(location = 0) out vec4 color;in vec2 v_TexCoord;uniform vec4 u_Color;
uniform float u_percent;
uniform sampler2D u_Texture0;
uniform sampler2D u_Texture1;void main()
{vec4 texColor = mix(texture(u_Texture0, v_TexCoord), texture(u_Texture1, v_TexCoord), u_percent);color = texColor;
};
出错:忘记了需要把图片的坐标变换到 [ -1,1 ] 的屏幕坐标下,所以没有显示图片,代码中是 proj 矩阵
这个脸是我自己画的
参考:你好,三角形 - LearnOpenGL CN
纹理 - LearnOpenGL CN
【OpenGL学习笔记④】——纹理贴图【SOIL2的配置 + 绘制木板 + 纹理环绕】-CSDN博客
相关文章:
LearnOpenGL 学习(入门--三角形,着色器,纹理)
你好,三角形 下面,你会看到一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。 图形渲染管线的第一个部分是顶点着色器(Vertex Shader),它把一个单独的顶点作为输入。顶点着色器主要的目的是…...
前端开发入门指南Day12:ES6进阶:面向对象的编程(Class类、继承、模块化)
今天,让我们探索ES6中面向对象编程的革新。想象一下,如果传统的JavaScript是一个手工作坊,那么ES6的类和模块化就是一个现代化工厂,让代码的组织和复用变得更加规范和高效。 一、为什么需要Class类的革新 🎯 在ES6之…...
深入探索进程间通信:System V IPC的机制与应用
目录 1、System V概述 2.共享内存(shm) 2.1 shmget — 创建共享内存 2.1.2 ftok(为shmmat创建key值) 2.1.3 为什么一块共享内存的标志信息需要用户来传递 2.2 shmat — 进程挂接共享内存 2.3 shmdt — 断开共享内存连接 2.4…...
跨 CA 签发多个证书的 Nginx mTLS 配置
研究过用同一个 CA 签发的服务端和客户端证书的 Nginx mTLS 配置,本文要试验一番服务端和客户端证书由不同 CA 机构签发的情形。这是常有事,比如与客户间采用 mTLS 加密方式,需要文件交付可能是 客户端证书由甲方生成,发送客户端…...
CentOS7 虚拟机 双网卡绑定
一、网卡绑定模式 模式类型特点mode0round-robin(平衡轮询策略)基于per packet方式,轮询往每条链路发送报文。提供负载均衡和容错的能力,当有链路出问题,会把流量切换到正常的链路上。交换机端需要配置聚合口。mode1a…...
设计模式——方法链or流式接口
方法链或流式接口是一种编程模式或设计模式。核心思想是通过返回对象自身的应用,使得可以在一个表达式中连续调用多个方法。 c中实现这种模式 1.基本语法规则 (1)每个可链接的方法都返回对象自身的引用(通常是*this)…...
BioDeepAV:一个多模态基准数据集,包含超过1600个深度伪造视频,用于评估深度伪造检测器在面对未知生成器时的性能。
2024-11-29, 由罗马尼亚布加勒斯特大学创建BioDeepAV数据集,它专门设计来评估最先进的深度伪造检测器在面对未见过的深度伪造生成器时的泛化能力,这对于提高检测器的鲁棒性和适应性具有重要意义。 数据集地址:biodeep 一、研究背景࿱…...
winform 程序添加API接口
假定你有这么一个winform程序,现在有需求开放一个接口,供外部调用,则你可以这样实现 安装需要的依赖包 Microsoft.AspNetCore.App 2.1.34 Microsoft.Extensions.Hosting 8.0.1使用线程在主窗体loading时候启动webhost publi…...
vscode 怎么下载 vsix 文件?
参考:https://marketplace.visualstudio.com/items?itemNameMarsCode.marscode-extension 更好的办法:直接去相关插件的 github repo 下载老版本 https://github.com/VSCodeVim/Vim/releases?page5 或者,去 open-vsx.org 下载老版本 点击这…...
jupyter-lab 环境构建
我平时用来调试各种代码的。 创建环境,安装库 conda create --name jupyterlab python3.12 -y conda activate jupyterlab conda install -c conda-forge jupyterlab nodejs之前用的是3.10的,但是最近安装的时候,发现3.10的python里面的jup…...
Mysql事务常见面试题 -- 事务的特性 ,并发事务问题 , undo_log和redo_log , 分布式事务
一. 事务的特性 ACID 原子性 --> 事务操作被视为一个整体 , 要么全部成功 , 要么全部失败一致性 --> 事务操作前后数据的变化是一致的隔离性 --> 事务的执行不受其他事务的影响持久性 --> 事务执行完毕会对数据永久保存 比如我们在转账的过程中 , A给B转账1000元…...
C# 线程--Thread类
目录 什么是线程? Thread类的定义 创建和启动线程 使用 ThreadStart 委托 使用 ParameterizedThreadStart 委托 Lambda简写 使用线程池(ThreadPool) 使用线程池的优点 使用 ThreadPool 的一般步骤 常用方法 Start() Join() Slee…...
【RK3588 Linux 5.x 内核编程】-内核高分辨率定时器
内核高分辨率定时器 文章目录 内核高分辨率定时器1、高分辨率定时器介绍2、高分辨率定时器API2.1 初始化定时器2.2 启动定时器2.3 停止定时器2.4 改变定时器超时时间2.5 定时器状态检查3、驱动实现4、驱动验证在前面的文章中,我们知道了如果在Linux内核中使用定时器。本文将详…...
论文阅读与源码解析:MogaNet
论文阅读与源码解析:MogaNet: Multi-order Gated Aggregation Network 论文地址:https://arxiv.org/pdf/2211.03295 GitHub项目地址:https://github.com/Westlake-AI/MogaNet 源码:https://github.com/Westlake-AI/MogaNet/blob/…...
长、宽数据表格转换
excel数据读取 使用 readxl 包读取 Excel 文件 library(readxl) 读取 Excel 文件: 使用 read_excel() 函数读取 .xlsx 或 .xls 格式的文件。可以选择指定工作表名或工作表索引。 # 读取 Excel 文件的默认工作表 data <- read_excel("path_to_y…...
华为 Mate 70 系列智能手机将运行不兼容 Android 的鸿蒙
华为宣布其 Mate 70 系列新智能手机将搭载不兼容 Android 的 HarmonyOS Next 操作系统。虽然是基于安卓系统的,但是该分叉树将正式分离。以后将不再兼容安卓系统软件。 HarmonyOS Next 可运行的应用数量仍然远远逊于 Android。华为表示 HarmonyOS Next 获得了逾 1.…...
【Halcon】 derivate_gauss
1、derivate_gauss Halcon中的derivate_gauss算子是一个功能强大的图像处理工具,它通过将图像与高斯函数的导数进行卷积,来计算各种图像特征。这些特征在图像分析、物体识别、图像增强等领域具有广泛的应用。 参数解释 Sigma:高斯函数的标准差,用于控制平滑的程度。Sigma…...
原型模式的理解和实践
引言 在软件开发中,我们经常需要创建具有相同属性或状态的对象。如果采用传统的构造函数或工厂模式来创建对象,那么每次创建对象时都需要重新设置对象的属性,这无疑增加了代码的冗余和复杂性。为了解决这一问题,原型模式ÿ…...
封装loding加载动画的请求
图片 /*** Loading 状态管理类*/ export class Loading {constructor(timer300) {this.value falsethis.timer timer}/*** 执行异步操作并自动管理 loading 状态* param {Promise|Function|any} target - Promise、函数或其他值* returns {Promise} - 返回请求结果*/async r…...
特种设备相关管理A全国通用试题
1.下列()类型的起重机械的定期检验每年1次。 A.机械式停车设备 B.施工升降机 C.门式起重机 D.桅杆式起重机 答案:B 2.压力容器安全技术监察规程中所指的容积,应为( )。 A.应当扣除永久连接在压力容器内部的内件…...
ES语法(一)概括
一、语法 1、请求方式 Elasticsearch(ES)使用基于 JSON 的查询 DSL(领域特定语言)来与数据交互。 一个 ElasticSearch 请求和任何 HTTP 请求一样由若干相同的部件组成: curl -X<VERB> <PROTOCOL>://&l…...
反射的作用
只要不是写死在程序里的对象的创建和调用,都可以用反射来实现。 例如,依据注解或配置信息动态生成并注入对象的需求场景,生成动态代理的需求场景。...
KARE:知识图谱社区级检索,增强 LLM 推理能力的医疗预测框架
KARE:知识图谱社区级检索,增强 LLM 推理能力的医疗预测框架 论文大纲理解要点全流程分析核心模式解法拆解知识图谱社区检索和普通检索有什么本质区别?为什么要同时使用专家LLM和本地LLM?动态知识检索中的评分机制是如何平衡多个因…...
LabVIEW氢气纯化控制系统
基于LabVIEW的氢气纯化控制系统满足氢气纯化过程中对精确控制的需求,具备参数设置、过程监控、数据记录和报警功能,体现了LabVIEW在复杂工业控制系统中的应用效能。 项目背景 在众多行业中,尤其是石油化工和航天航空领域,氢气作为…...
数据分析案例-笔记本电脑价格数据可视化分析
🤵♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞Ǵ…...
CSS函数
目录 一、背景 二、函数的概念 1. var()函数 2、calc()函数 三、总结 一、背景 今天我们就来说一说,常用的两个css自定义属性,也称为css函数。本文中就成为css函数。先来看一下官方对其的定义。 自定义属性(有时候也被称作CSS 变量或者级…...
Linux网络——传输层
目录 1. 再谈端口号 2. UDP ① UDP 协议的报文 ② UDP 协议报头的本质 3. TCP ① TCP 缓冲区的理解 ② TCP 协议的报文 1. 16位源端口号 && 16位目的端口号 && 4位首部长度 2. 16位窗口大小 3. 32位序号 && 32位确认序号 4. 六个标记位 ③…...
2024年认证杯SPSSPRO杯数学建模A题(第一阶段)保暖纤维的保暖能力全过程文档及程序
2024年认证杯SPSSPRO杯数学建模 A题 保暖纤维的保暖能力 原题再现: 冬装最重要的作用是保暖,也就是阻挡温暖的人体与寒冷环境之间的热量传递。人们在不同款式的棉衣中会填充保暖材料,从古已有之的棉花、羽绒到近年来各种各样的人造纤维。不…...
采药 刷题笔记 (动态规划)0/1背包
P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 动态规划 0/1背包 的本质在于继承 一行一行更新 上一行是考虑前i个物品的最优情况 当前行是考虑第i1个物品的情况 当前行的最优解 来自上一行和前i个物品的最优解进行比较 如果当前装了当前物品ÿ…...
LabVIEW MathScript工具包对运行速度的影响及优化方法
LabVIEW 的 MathScript 工具包 在运行时可能会影响程序的运行速度,主要是由于以下几个原因: 1. 解释型语言执行方式 MathScript 使用的是类似于 MATLAB 的解释型语言,这意味着它不像编译型语言(如 C、C 或 LabVIEW 本身的 VI&…...
大数据新视界 -- Hive 数据湖架构中的角色与应用(上)(25 / 30)
💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...
电脑关机的趣味小游戏——system函数、strcmp函数、goto语句的使用
文章目录 前言一. system函数1.1 system函数清理屏幕1.2 system函数暂停运行1.3 system函数电脑关机、重启 二、strcmp函数三、goto语句四、电脑关机小游戏4.1. 程序要求4.2. 游戏代码 总结 前言 今天我们写一点稍微有趣的代码,比如写一个小程序使电脑关机…...
elasticsearch是如何进行搜索的?
请求与转发 协调节点确定参与搜索的目标索引,及其通过分片路由表确定分片对索引所在分片中选择任意节点并发请求多个分片的副本分片 副本选择策略 副本选择主要考虑 分片健康状态:选择状态为 green 或 yellow 的副本节点负载情况:避免查询…...
【Linux课程学习】:站在文件系统之上理解:软硬链接,软硬链接的区别
🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 Linux学习笔记: https://blog.csdn.net/d…...
如何做好一份技术文档
要做好一份技术文档,你需要遵循以下步骤: 1. 明确文档目标: 确定文档的目的和受众。 了解受众的技术水平和背景,以便调整内容的深度和语言风格。 2. 收集信息: 搜集所有与主题相关的技术细节、数据、图表和…...
优雅关闭进程
原文地址:优雅关闭进程 – 无敌牛 欢迎参观我的个人博客:无敌牛 – 技术/著作/典籍/分享等 介绍 本文涉及到进程对信号的响应,关于信号的一些基本知识,可以参考往期文章:linux系统信号简介 – 无敌牛 一个进程正常…...
【WEB开发.js】addEventListener事件监听器的绑定和执行次数的问题(小心踩坑)
假设我们有一个按钮,用户点击该按钮后,会选择一个文件,且我们希望每次点击按钮时只触发一次文件处理。下面我会给你一个简单的例子,展示放在函数内部和放在函数外部的区别。 1. 将事件监听器放在函数内部(问题的根源&…...
将 x 减到 0 的最小操作数 C++滑动窗口
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。 如果可以将 x 恰好 减到 0 ,返回 最小操作数 &#x…...
深入解析 C++ 中的 common_reference_with 和 common_with:设计原理与复杂性
在 C 标准库的设计中,类型之间的兼容性和安全性是至关重要的,尤其是在泛型编程中。为了实现高效且安全的类型推导和转换,C 提供了一些复杂的概念和工具来确保不同类型之间能够正确协同工作。common_reference_with 和 common_with 这两个概念…...
从CPU缓存与指令重排序探讨JMM
目录 问题背景 解决思路 hb关系的应用 原子性问题 问题背景 1. 编译器和CPU优化: 编译器和CPU为了提升单线程程序的性能,会对代码进行优化,如指令重排序、延迟计算等。这些优化在单线程环境下不会影响程序的最终结果,但在多线…...
ETSI EN 300328 标准的一些笔记
ETSI - European Telecommunications Standards Institute 欧洲电信标准化协会 ETSI EN 300328 是欧洲协调标准,此标准适用于工作在2.4G频段范围内运行的宽频传输系统和设备的无线电频谱。 例如 WIFI、Zigbee、蓝牙、 (国内的星闪)。不涵盖UWB。 符合了EN 300328标…...
各大浏览器(如Chrome、Firefox、Edge、Safari)的对比
浏览器如Chrome、Firefox、Edge等在功能、性能、隐私保护等方面各有特点。以下是对这些浏览器的详细对比,帮助你选择合适的浏览器。 1. Google Chrome 市场份额:Chrome是目前市场上最流行的浏览器,约占全球浏览器市场的65%以上。 性能&#…...
AD7606使用方法
AD7606是一款8通道最高16位200ksps的AD采样芯片。5V单模拟电源供电,真双极性模拟输入可以选择10 V,5 V两种量程。支持串口与并口两种读取方式。 硬件连接方式: 配置引脚 引脚功能 详细说明 OS2 OS1 OS2 过采样率配置 000 1倍过采样率 …...
双向长短期记忆(Bi-LSTM)神经网络介绍
长短期记忆(Long Short-Term Memory, LSTM)神经网络: 1.是Hochreiter和Schmidhuber设计的循环神经网络(Recurrent Neural Network, RNN)的改进版本。LSTM模型借鉴了人类大脑的选择性输入和选择性遗忘机制,获取序列中的关键信息,遗忘和当前预测…...
openGauss开源数据库实战十八
文章目录 任务十八 openGauss逻辑结构:构:用户和权眼管理任务目标实施步骤一、准备工作二、用户和角色管理1.使用CREATE USER语句创建用户2.使用CREATE ROLE语句创建用户3.删除用户和角色 三、权限管理1.系统权限清理工作 任务十八 openGauss逻辑结构:构:用户和权眼管理 任务目…...
JVM 性能调优 -- JVM 调优常用网站
前言: 上一篇分享了 JDK 自带的常用的 JVM 调优命令和图形化界面工具,本篇我们分享一下常用的第三方辅助 JVM 调优网站。 JVM 系列文章传送门 初识 JVM(Java 虚拟机) 深入理解 JVM(Java 虚拟机) 一文搞…...
现在的电商风口已经很明显了
随着电商行业的不断发展,直播带货的热潮似乎正逐渐降温,而货架电商正成为新的焦点。抖音等平台越来越重视货架电商,强调搜索功能的重要性,预示着未来的电商中心将转向货架和搜索。 在这一转型期,AI技术与电商的结合为…...
基于AT89C52单片机的电子时钟与温湿度检测系统
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
Wwise SoundBanks内存优化
1.更换音频格式为Vorbis 2.停用多余的音频,如Random Container的随机脚步声数量降为2个 3.背景音乐勾选“Stream”。这样就让音频从硬盘流送到Wwise,而不是保存在内存当中,也就节省了内存 4.设置最大发声数Max Voice Instances 5.设置音频…...
Next.js 独立开发教程(十三):错误处理(Error Handling)
系列文章目录 Next.js 开发教程(一):入门指南-CSDN博客 Next.js 开发教程(二):从零构建仪表盘应用-CSDN博客 Next.js 开发教程(三):CSS 样式的完整指南-CSDN博客 Next.js 独立开发教程&…...