opengl 着色器 (四)最终章收尾

颜色属性

在前面的教程中,我们了解了如何填充VBO、配置顶点属性指针以及如何把它们都储存到一个VAO里。这次,我们同样打算把颜色数据加进顶点数据中。我们将把颜色数据添加3个float值到vertices数组。我们将把三角形的三个角分别指定为红色、绿色和蓝色:

float vertices[] = {// 位置              // 颜色0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
};

由于现在有更多的数据要发送到顶点着色器,我们有必要去调整一下顶点着色器,使它能够接收颜色值作为一个顶点属性输入。需要注意的是我们用layout标识符来把aColor属性的位置值设置为1:

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0 
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1out vec3 ourColor; // 向片段着色器输出一个颜色void main()
{gl_Position = vec4(aPos, 1.0);ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
}

layout关键字为顶点着色器的输入属性指定了location值。location值是一个整数,它指定了顶点数据在顶点缓冲区中的位置或索引,以及这些数据如何与顶点着色器的输入变量关联起来。

  • layout (location = 0) in vec3 aPos; 表示顶点数据中的第一个分量(通常是一个三维向量)被用作位置(aPos),并且这个分量在顶点缓冲区中的位置索引是0。
  • layout (location = 1) in vec3 aColor; 表示顶点数据中的第二个分量(也是一个三维向量)被用作颜色(aColor),并且这个分量在顶点缓冲区中的位置索引是1。

这种指定方式允许顶点数据按照特定的顺序和格式被发送到GPU,顶点着色器则根据这些location值来读取和处理这些数据。

通过这种方式,开发者可以确保无论顶点数据的实际存储顺序如何,着色器都能正确地访问到预期的数据。这对于在多个着色器之间传递数据、或者在使用不同的渲染管线时保持数据一致性非常有用。听不懂没关系,我们继续往下看。

由于我们不再使用uniform来传递片段的颜色了,现在使用ourColor输出变量,我们必须再修改一下片段着色器:

#version 330 core
out vec4 FragColor;  
in vec3 ourColor;void main()
{FragColor = vec4(ourColor, 1.0);
}

因为我们添加了另一个顶点属性,并且更新了VBO的内存,我们就必须重新配置顶点属性指针。更新后的VBO内存中的数据现在看起来像这样:

知道了现在使用的布局,我们就可以使用glVertexAttribPointer函数更新顶点格式,

// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);

在这里,你还记得上面讲的layout关键字 指定了location值么。

layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0 
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1

对,就是它,我们详细讲一下这些参数。

  • glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    • 第一个参数0是顶点属性的位置索引(location),它对应于顶点着色器中通过layout(location = 0)指定的输入变量,如果是1 那就是颜色索引,通过layout(location = 1)指定的输入变量。
    • 第二个参数3表示每个顶点属性的组件数量,这里是3个,对应于一个三维向量(x, y, z)。
    • 第三个参数GL_FLOAT指定了顶点属性数据的类型,这里是浮点数。
    • 第四个参数GL_FALSE表示顶点数据在传递给顶点着色器之前不需要被标准化(即,数据将以其原始格式传递)。
    • 第五个参数6 * sizeof(float)是步长(stride),它表示连续顶点属性组之间的字节偏移量。这里,每个顶点由6个浮点数组成(3个用于位置,3个用于颜色),所以步长是6个浮点数的大小。
    • 第六个参数(void*)0是偏移量(offset),它表示从当前绑定的VBO的起始位置到第一个顶点属性的字节偏移量。这里,位置属性是第一个,所以偏移量是0。
  • glEnableVertexAttribArray(0);
    • 这个函数启用了位置属性数组,使其在接下来的渲染调用中可用。

由于我们现在有了两个顶点属性,我们不得不重新计算步长值。为获得数据队列中下一个属性值(比如位置向量的下个x分量)我们必须向右移动6个float,其中3个是位置值,另外3个是颜色值。这使我们的步长值为6乘以float的字节数(=24字节)。
同样,这次我们必须指定一个偏移量。对于每个顶点来说,位置顶点属性在前,所以它的偏移量是0。颜色属性紧随位置数据之后,所以偏移量就是3 * sizeof(float),用字节来计算就是12字节。

运行程序你应该会看到如下结果:

 

如果你在哪卡住了,可以查看以下源码

#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <iostream>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;const char *vertexShaderSource ="#version 330 core\n""layout (location = 0) in vec3 aPos;\n""layout (location = 1) in vec3 aColor;\n""out vec3 ourColor;\n""void main()\n""{\n""   gl_Position = vec4(aPos, 1.0);\n""   ourColor = aColor;\n""}\0";const char *fragmentShaderSource = "#version 330 core\n""out vec4 FragColor;\n""in vec3 ourColor;\n""void main()\n""{\n""   FragColor = vec4(ourColor, 1.0f);\n""}\n\0";int main()
{// glfw: initialize and configure// ------------------------------glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif// glfw window creation// --------------------GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// glad: load all OpenGL function pointers// ---------------------------------------if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// build and compile our shader program// ------------------------------------// vertex shaderunsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// check for shader compile errorsint success;char infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// fragment shaderunsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);// check for shader compile errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// link shadersunsigned int shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// check for linking errorsglGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success) {glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);// set up vertex data (and buffer(s)) and configure vertex attributes// ------------------------------------------------------------------float vertices[] = {// positions         // colors0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // bottom right-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // bottom left0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // top };unsigned int VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// position attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// color attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.// glBindVertexArray(0);// as we only have a single shader, we could also just activate our shader once beforehand if we want to glUseProgram(shaderProgram);// render loop// -----------while (!glfwWindowShouldClose(window)){// input// -----processInput(window);// render// ------glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// render the triangleglBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)// -------------------------------------------------------------------------------glfwSwapBuffers(window);glfwPollEvents();}// optional: de-allocate all resources once they've outlived their purpose:// ------------------------------------------------------------------------glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);// glfw: terminate, clearing all previously allocated GLFW resources.// ------------------------------------------------------------------glfwTerminate();return 0;
}// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{// make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays.glViewport(0, 0, width, height);
}

我们自己的着色器类

前面看到我们编写自己的着色器类很麻烦,类似这样

const char *vertexShaderSource ="#version 330 core\n""layout (location = 0) in vec3 aPos;\n""layout (location = 1) in vec3 aColor;\n""out vec3 ourColor;\n""void main()\n""{\n""   gl_Position = vec4(aPos, 1.0);\n""   ourColor = aColor;\n""}\0";

但我们不可能每次编写到要这样写对吧,所以,我们自己写一个方法,把它写到像cpp的文件里一样,它可以从硬盘读取着色器,然后编译并链接它们,并对它们进行错误检测,这就变得很好用了。

我们会把着色器类全部放在在头文件里,主要是为了学习用途,当然也方便移植。我们先来添加必要的include,并定义类结构:

#ifndef SHADER_H
#define SHADER_H#include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件#include <string>
#include <fstream>
#include <sstream>
#include <iostream>class Shader
{
public:// 程序IDunsigned int ID;// 构造器读取并构建着色器Shader(const char* vertexPath, const char* fragmentPath);// 使用/激活程序void use();// uniform工具函数void setBool(const std::string &name, bool value) const;  void setInt(const std::string &name, int value) const;   void setFloat(const std::string &name, float value) const;
};#endif

在上面,我们在头文件顶部使用了几个预处理指令(Preprocessor Directives)。这些预处理指令会告知你的编译器只在它没被包含过的情况下才包含和编译这个头文件,即使多个文件都包含了这个着色器头文件。它是用来防止链接冲突的。

着色器类储存了着色器程序的ID。它的构造器需要顶点和片段着色器源代码的文件路径,这样我们就可以把源码的文本文件储存在硬盘上了。除此之外,为了让我们的生活更轻松一点,还加入了一些工具函数:use用来激活着色器程序,所有的set…函数能够查询一个unform的位置值并设置它的值。

从文件读取

我们使用C++文件流读取着色器内容,储存到几个string对象里:

Shader(const char* vertexPath, const char* fragmentPath)
{// 1. 从文件路径中获取顶点/片段着色器std::string vertexCode;std::string fragmentCode;std::ifstream vShaderFile;std::ifstream fShaderFile;// 保证ifstream对象可以抛出异常:vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);try {// 打开文件vShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// 读取文件的缓冲内容到数据流中vShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();       // 关闭文件处理器vShaderFile.close();fShaderFile.close();// 转换数据流到stringvertexCode   = vShaderStream.str();fragmentCode = fShaderStream.str();     }catch(std::ifstream::failure e){std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;}const char* vShaderCode = vertexCode.c_str();const char* fShaderCode = fragmentCode.c_str();[...]

下一步,我们需要编译和链接着色器。注意,我们也将检查编译/链接是否失败,如果失败则打印编译时错误,调试的时候这些错误输出会及其重要(你总会需要这些错误日志的):

// 2. 编译着色器
unsigned int vertex, fragment;
int success;
char infoLog[512];// 顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// 打印编译错误(如果有的话)
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if(!success)
{glGetShaderInfoLog(vertex, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
};// 片段着色器也类似
[...]// 着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
// 打印连接错误(如果有的话)
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if(!success)
{glGetProgramInfoLog(ID, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);

use函数非常简单:

void use() 
{ glUseProgram(ID);
}

uniform的setter函数也很类似:

void setBool(const std::string &name, bool value) const
{glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); 
}
void setInt(const std::string &name, int value) const
{ glUniform1i(glGetUniformLocation(ID, name.c_str()), value); 
}
void setFloat(const std::string &name, float value) const
{ glUniform1f(glGetUniformLocation(ID, name.c_str()), value); 
} 

现在我们就写完了一个完整的着色器类。使用这个着色器类很简单;只要创建一个着色器对象,从那一点开始我们就可以开始使用了:

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
...
while(...)
{ourShader.use();ourShader.setFloat("someUniform", 1.0f);DrawStuff();
}

我们把顶点和片段着色器储存为两个叫做shader.vsshader.fs的文件。你可以使用自己喜欢的名字命名着色器文件;我自己觉得用.vs.fs作为扩展名很直观。

以下是我的代码:

#ifndef SHADER_H
#define SHADER_H#include <glad/glad.h>#include <string>
#include <fstream>
#include <sstream>
#include <iostream>class Shader
{
public:unsigned int ID;// constructor generates the shader on the fly// ------------------------------------------------------------------------Shader(const char* vertexPath, const char* fragmentPath){// 1. retrieve the vertex/fragment source code from filePathstd::string vertexCode;std::string fragmentCode;std::ifstream vShaderFile;std::ifstream fShaderFile;// ensure ifstream objects can throw exceptions:vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);try {// open filesvShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// read file's buffer contents into streamsvShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();// close file handlersvShaderFile.close();fShaderFile.close();// convert stream into stringvertexCode   = vShaderStream.str();fragmentCode = fShaderStream.str();}catch (std::ifstream::failure& e){std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;}const char* vShaderCode = vertexCode.c_str();const char * fShaderCode = fragmentCode.c_str();// 2. compile shadersunsigned int vertex, fragment;// vertex shadervertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);checkCompileErrors(vertex, "VERTEX");// fragment Shaderfragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fShaderCode, NULL);glCompileShader(fragment);checkCompileErrors(fragment, "FRAGMENT");// shader ProgramID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);checkCompileErrors(ID, "PROGRAM");// delete the shaders as they're linked into our program now and no longer necessaryglDeleteShader(vertex);glDeleteShader(fragment);}// activate the shader// ------------------------------------------------------------------------void use() { glUseProgram(ID); }// utility uniform functions// ------------------------------------------------------------------------void setBool(const std::string &name, bool value) const{         glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); }// ------------------------------------------------------------------------void setInt(const std::string &name, int value) const{ glUniform1i(glGetUniformLocation(ID, name.c_str()), value); }// ------------------------------------------------------------------------void setFloat(const std::string &name, float value) const{ glUniform1f(glGetUniformLocation(ID, name.c_str()), value); }private:// utility function for checking shader compilation/linking errors.// ------------------------------------------------------------------------void checkCompileErrors(unsigned int shader, std::string type){int success;char infoLog[1024];if (type != "PROGRAM"){glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}else{glGetProgramiv(shader, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}}
};
#endif

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

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

相关文章

矩阵在资产收益(Asset Returns)中的应用:以资产回报矩阵为例(中英双语)

本文中的例子来源于&#xff1a; 这本书&#xff0c;网址为&#xff1a;https://web.stanford.edu/~boyd/vmls/ 矩阵在资产收益(Asset Returns)中的应用&#xff1a;以资产回报矩阵为例 在量化金融中&#xff0c;矩阵作为一种重要的数学工具&#xff0c;被广泛用于描述和分析…

arXiv-2024 | NavAgent:基于多尺度城市街道视图融合的无人机视觉语言导航

作者&#xff1a;Youzhi Liu, Fanglong Yao*, Yuanchang Yue, Guangluan Xu, Xian Sun, Kun Fu 单位&#xff1a;中国科学院大学电子电气与通信工程学院&#xff0c;中国科学院空天信息创新研究院网络信息系统技术重点实验室 原文链接&#xff1a;NavAgent: Multi-scale Urba…

校园点餐订餐外卖跑腿Java源码

简介&#xff1a; 一个非常实用的校园外卖系统&#xff0c;基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化&#xff0c;提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合&am…

[计算机网络]ARP协议的故事:小明找小红的奇妙旅程

1.ARP小故事 在一个繁忙的网络世界中&#xff0c;每个设备都有自己的身份标识——MAC地址&#xff0c;就像每个人的身份证号码一样。在这个故事里&#xff0c;我们的主角小明&#xff08;主机&#xff09;需要找到小红&#xff08;目标主机&#xff09;的MAC地址&#xff0c;才…

arcgisPro将面要素转成CAD多段线

1、说明&#xff1a;正常使用【导出为CAD】工具&#xff0c;则导出的是CAD三维多线段&#xff0c;无法进行编辑操作、读取面积等。这是因为要素面中包含Z值&#xff0c;导出则为三维多线段数据。需要利用【复制要素】工具禁用M值和Z值&#xff0c;再导出为CAD&#xff0c;则得到…

vue+springboot+cas配置及cookie传递问题

cookie的注意事项 前边的文章已经介绍过cookie的基本信息&#xff0c;这里再次说明一点&#xff1a;cookie是无法进行跨域传递的&#xff0c;很多时候cookie无法设置和传递都是因为跨域问题&#xff0c;ip/端口不一致。 主要就是&#xff1a;被设置cookie和要传递cookie的地址…

simulink离散传递函数得到差分方程并用C语言实现

一. 创建连续时间的传递函数 G ( s ) s 2 217 s s 2 384 s 8989 G(s) \frac{s^2217s}{s^2384s8989} G(s)s2384s8989s2217s​ 二. 离散连续时间的传递函数G(s) 2.1 在matlab中用c2d函数双线性变换法离散G(s)&#xff0c; 下面是matlab脚本代码 % 创建连续时间传递函数 …

搭建私有链

文章目录 1. 准备工作2. 创建创世区块配置文件2.1 创建数据目录2.2 创建创世区块配置文件1. “config”部分2. “alloc”部分3. “coinbase”4. “difficulty”5. “extraData”6. “gasLimit”7. “nonce”8. “mixhash”9. “parentHash”10. “timestamp” 3. 初始化&#x…

AI Alignment: A Comprehensive Survey---治理

治理 除了技术解决方案之外&#xff0c;治理&#xff08;规则的制定和执行&#xff09;对于确保人工智能系统的安全开发和部署也是必不可少的。在本节中&#xff0c;我们将通过探索人工智能治理的作用、利益相关者在治理人工智能方面的功能和关系以及有效人工智能治理面临的若干…

CNN、RNN、LSTM和Transformer之间的区别和联系

文章目录 CNN、RNN、LSTM和Transformer之间的区别和联系前言CNN&#xff08;卷积神经网络&#xff09;RNN&#xff08;循环神经网络&#xff09;LSTM&#xff08;长短期记忆网络&#xff09;Transformer四者之间的联系与区别Yolo算法简介Yolo和CNN的关系YOLO各版本 CNN、RNN、L…

深度学习之超分辨率算法——FRCNN

– 对之前SRCNN算法的改进 输出层采用转置卷积层放大尺寸&#xff0c;这样可以直接将低分辨率图片输入模型中&#xff0c;解决了输入尺度问题。改变特征维数&#xff0c;使用更小的卷积核和使用更多的映射层。卷积核更小&#xff0c;加入了更多的激活层。共享其中的映射层&…

小程序UI自动化测试实践:Minium+PageObject !

小程序架构上分为渲染层和逻辑层&#xff0c;尽管各平台的运行环境十分相似&#xff0c;但是还是有些许的区别&#xff08;如下图&#xff09;&#xff0c;比如说JavaScript 语法和 API 支持不一致&#xff0c;WXSS 渲染表现也有不同&#xff0c;所以不论是手工测试&#xff0c…

堆的深度剖析及使用

目录 1.堆的创建1.1初始化1.2销毁 2.堆的使用2.1数据插入2.2堆顶元素2.3数据删除 3.堆的难点3.1向上调整3.1.1视频分析向上调整3.1.2 代码分析 3.2向下调整3.2.1视频分析向下调整3.2.2代码分析 1.堆的创建 堆的物理储存方式其实是一个数组&#xff0c;而逻辑储存方式其实是一个…

Hu矩原理 | cv2中基于Hu矩计算图像轮廓相似度差异的函数cv2.matchShapes【小白记笔记】

Hu 矩&#xff08;Hu Moments&#xff09; 是一种用于描述轮廓形状的 不变特征。它基于图像的矩提取&#xff0c;经过数学变换得到 7 个不变矩&#xff0c;这些不变矩在图像 平移、旋转和缩放等几何变换下保持不变&#xff0c;适合用来衡量轮廓或形状的相似度差异。 1、图像矩…

计算无人机俯拍图像的地面采样距离(GSD)矩阵

引言 在无人机遥感、测绘和精细农业等领域&#xff0c;地面采样距离&#xff08;Ground Sampling Distance&#xff0c;简称 GSD&#xff09;是一个非常重要的指标。GSD 是指图像中每个像素在地面上实际代表的物理距离&#xff0c;通常以米或厘米为单位。GSD 决定了图像的空间…

浅谈怎样系统的准备前端面试

前言 创业梦碎&#xff0c;回归现实&#xff0c;7 月底毅然裸辞&#xff0c;苦战两个月&#xff0c;拿到了美团和字节跳动的 offer&#xff0c;这算是从业以来第一次真正意义的面试&#xff0c;遇到蛮多问题&#xff0c;比如一开始具体的面试过程我都不懂&#xff0c;基本一直是…

2009 ~ 2019 年 408【数据结构】大题解析

2009 年 讲解视频推荐&#xff1a;【BOK408真题讲解-2009年&#xff08;催更就退网版&#xff09;】 1. 图的应用&#xff08;10’&#xff09; 带权图&#xff08;权值非负&#xff0c; 表示边连接的两顶点间的距离&#xff09;的最短路径问题是找出从初始顶点到目标顶点之间…

时空AI赋能低空智能科技创新

随着人工智能技术的不断进步&#xff0c;时空人工智能&#xff08;Spatio-Temporal AI&#xff0c;简称时空AI&#xff09;正在逐渐成为推动低空经济发展的新引擎。时空AI结合了地理空间智能、城市空间智能和时空大数据智能&#xff0c;为低空智能科技创新提供了强大的数据支持…

Python读取Excel批量写入到PPT生成词卡

一、问题的提出 有网友想把Excel表中的三列数据&#xff0c;分别是&#xff1a;单词、音标和释义分别写入到PPT当中&#xff0c;每一张PPT写一个单词的内容。这种批量操作是python的强项&#xff0c;尤其是在办公领域&#xff0c;它能较好地解放双手&#xff0c;读取Excel表后…

Proteus(8.15)仿真下载安装过程(附详细安装过程图)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、Proteus是什么&#xff1f; 二、下载链接 三、下安装步骤 1.解压&#xff0c;有键管理员运行 2.点击Next&#xff0c;进行下一步 3.勾选I accept…&#…