[OpenGL]使用OpenGL绘制带纹理三角形

一、简介

本文介绍了如何使用使用OpenGL绘制带纹理三角形。
在绘制带纹理的三角形时,
首先使用.h读取准备好的.png格式的图片作为纹理,然后在fragment shader中使用

...
in vec2 textureCoord;
uniform sampler2D aTexture1;
void main()
{FragColor = texture(aTexture1, textureCoord);
}

确定三角形内各个点对应的纹理颜色。

按照本文代码实现后,理论上可以得到以下结果:

绘制结果

二、使用OpenGL绘制带纹理三角形

0. 环境需要

  • Linux ,或者 windos下使用wsl2。
  • 已经安装完成GLFW和GLAD。请参考[OpenGL] wsl2上安装使用cmake+OpenGL教程。
  • 需要准备一张图片做为纹理图,本文使用著名的Lenna_(test_image).png作为纹理图。将Lenna_(test_image).png下载后放到resources/images/目录下。
  • 需要下载 stb_image.h 作为加载.png图像的库。将 stb_image.h 下载后放入include/目录下。

1. 项目目录

项目目录

2. CMakeLists.txt代码

cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 14)project(OpenGL_Hello_Textured_Triangle)include_directories(include)find_package(glfw3 REQUIRED)
file(GLOB project_file main.cpp glad.c)
add_executable(${PROJECT_NAME} ${project_file})
target_link_libraries(${PROJECT_NAME} glfw)

3. main.cpp代码

1). 代码整体流程

  1. 初始化glfw,glad,窗口
  2. 构建,编译 vertex shader 和 fragment shader
  3. 准备数据
  4. 准备纹理
  5. 设置VBO,VAO,和EBO
  6. 开始绘制
  7. 释放资源

2). main.cpp代码

#define STB_IMAGE_IMPLEMENTATION
// 将 stb_image.h 包含到头文件中,在包含该头文件之前必须 #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <iostream>// 用于处理窗口大小改变的回调函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
// 用于处理用户输入的函数
void processInput(GLFWwindow *window);// 指定窗口默认width和height像素大小
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;/****** vertex shader 和 fragment shader 代码 ******/
// 若对glsl编写的shader程序不了解,可以忽略此处的代码
// vertex shader, 用于处理各顶点的位置信息,此处的vertex shader不会对顶点位置进行操作
// 只需要记住此处我们将顶点 position 数据的 location 设为 0,并将 color 数据的 location 设为 1
// layout (location = 0) in vec3 aPos; 这句代码用来设置用到的 顶点 position 数据的location值
// layout (location = 1) in vec2 aTexCoord;; 这句代码用来设置用到的 顶点 texture coordinate 数据的location值
// vertex shader 中的 textureCoord 用于接收传入的texture coordinate数据 aTexCoord 然后再传给 fragment shader
const char *vertexShaderSource = R"(
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 textureCoord;
void main()
{textureCoord = aTexCoord;gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
)";
// fragment shader,用于处理各顶点的颜色信息
// fragment shader 接收从vertex shader 中传入的表示texture coordinate 的变量 textureCoord,
// 并且声明一个纹理采样器 aTexture1
// 然后在 main 函数中 使用 texture() 函数,根据从 vertex shader 传入的 textureCoord 在 aTexture1
// 上采样确定各点的纹理颜色
const char *fragmentShaderSource = R"(
#version 330 core
out vec4 FragColor;
in vec2 textureCoord;
uniform sampler2D aTexture1;
void main()
{FragColor = texture(aTexture1, textureCoord);
}
)";/************************************/int main()
{/****** 1.初始化glfw, glad, 窗口 *******/// glfw 初始化 + 配置 glfw 参数glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// glfw 生成窗口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;}// 设置窗口window的上下文glfwMakeContextCurrent(window);// 配置window变化时的回调函数glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 使用 glad 加载 OpenGL 中的各种函数if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}/************************************//****** 2.构建,编译 vertex shader 和 fragment shader ******/// vertex shaderunsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// 检查是否成功编译 vertex shaderint 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);// 检查是否成功编译 fragment shaderglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// 链接 shadersunsigned int shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// 检查是否成功链接 vertex shader 和 fragment shaderglGetProgramiv(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);/************************************//****** 3.准备数据 *******/// 三角形顶点的 position, texture coordinate 数据,总共3个顶点数据,// 每个顶点使用5个浮点数,分别表示 position(x,y,z) 和 texture coordinate(u,v)float vertices[] = {0.0f,  0.5f,  0.0,  0.5f, 1.0f, // 上-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // 左下0.5f,  -0.5f, 0.0f, 1.0f, 0.0f  // 右下};// 三角形 index// 我们只需要绘制一个三角形,三角形的三个点的position分别使用 vertices 数组中的第0,第1和第2个点的position数据unsigned int indices[] = {0, 1, 2 // 第一个三角形};/****** 4.准备纹理 *******/GLuint textureID;glGenTextures(1, &textureID);            // 生成纹理 IDglBindTexture(GL_TEXTURE_2D, textureID); // 绑定纹理// 设置纹理参数// 设置纹理在 S 方向(水平方向)的包裹方式为 GL_REPEATglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);// 设置纹理在 T 方向(垂直方向)的包裹方式为 GL_REPEATglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);// 设置纹理的缩小过滤方式,当纹理变小时,使用 GL_LINEAR (线性过滤)方式glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// 设置纹理的放大过滤方式,当纹理变大时,使用 GL_LINEAR (线性过滤)方式glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// 加载纹理图像const char *texture_path = "../resources/images/Lenna_(test_image).png";int width, height, nrChannels;stbi_set_flip_vertically_on_load(true);//启用将图像上下翻转load模式,不然显示的纹理图像会时上下倒过来的unsigned char *data = stbi_load(texture_path, &width, &height, &nrChannels, 0);if (data){GLenum format;if (nrChannels == 1)format = GL_RED;else if (nrChannels == 3)format = GL_RGB;else if (nrChannels == 4)format = GL_RGBA;// 生成纹理glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D); // 生成 Mipmaps}else{std::cerr << "Failed to load texture: " << texture_path << std::endl;}stbi_image_free(data); // 释放图像内存// 告诉着色器使用 纹理单元0 对应 shader 中的纹理 aTexture1// OpenGL通常支持16个纹理单元,编号依次为0,1,2,..,15,此处我们只使用一个纹理// 即,fragment shader 中的 uniform sampler2D aTexture1; 因此可以将 aTexture1 设为 纹理单元0glUniform1i(glGetUniformLocation(shaderProgram, "aTexture1"), 0);glActiveTexture(GL_TEXTURE0);            // 激活 纹理单元0glBindTexture(GL_TEXTURE_2D, textureID); // 绑定 纹理单元0 使用 textureID/************************************//****** 5.设置 VBO, VAO 和 EBO ******/// 1). VBO 用来打包三角形各顶点的所有数据(比如position, normal, color等。本文中只用到了position数据,即// vertices数组),并将打包好的数据一起传给GPU。// 2). EBO 用来打包(存储)待绘制的三角形的索引数据,即 indices 数组,用于指定顶点的顺序。// 3). VAO 用于记录顶点属性的格式,即如何从 VBO中提取数据。并存储了EBO数据,从而管理顶点数据的输入配置。// 生成 VBO, VAO 和 EBOunsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO); // 生成一个VAO对象glGenBuffers(1, &VBO);      // 生成一个VBO对象(VBO对象本质上是一个buffer)glGenBuffers(1, &EBO);      // 生成一个EBO对象(EBO对象本质上也是一个buffer)// 使用 VBO, VAO 和 EBO 的顺序为:// 先绑定 VAO -> 绑定 VBO -> 设置如何读取VBO中数据 -> 解绑VBO ->绑定 EBO -> 解绑 VAOglBindVertexArray(VAO); // 绑定VAOglBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBOglBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,GL_STATIC_DRAW); // 将vertices中的数据复制到刚刚绑定的VBO buffer中去,VBO buffer是GPU内存上的一块区域// 使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据的position部分// glVertexAttribPointer() 需要6个参数,每个参数的含义如下:// 1). 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,对于position部分,此处填入 0// 2). 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 vec3 类型的变量 aPos,// vec3 是一个包含3个float变量的 vector,因此此处填入 3// 3). 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT// 4). 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE// 5). 指定VBO中顶点数据组之间的间隔,因为VBO中的position和texture coordinate交替出现,// 每组position数据间隔3*float+2*float大小,因此此处填入 5*sizeof(float)// 6). 指定读取VBO(vertices数组)的position数据起始位置偏移,单位为字节,// 由于position数据在数组的开头,所以这里填入 0*sizeof(float)glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(0 * sizeof(float)));glEnableVertexAttribArray(0); // 启用 location = 0 处的数据,因此该函数的参数为 0// 使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据的texture coordinate部分// glVertexAttribPointer() 需要6个参数,每个参数的含义如下:// 1). 指定要配置的顶点属性, 即 shader 中指定 数据 location 值,对于texture coordinate部分,此处填入 1// 2). 指定顶点属性的大小,shader中我们将VBO中的数据传给了一个 vec2 类型的变量 aTexCoord,// vec2 是一个包含2个float变量的 vector,因此此处填入 2// 3). 指定数据的类型,用于使用的是浮点型,因此此处填入 GL_FLOAT// 4). 指定是否自动对数据进行归一化,我们不需要自动诡异化,因此此处填入 GL_FALSE// 5). 指定VBO中顶点数据组之间的间隔,因为VBO中的position和texture coordinate交替出现,// 每组texture coordinate数据间隔3*float+2*float大小,因此此处填入 5*sizeof(float)// 6). 指定读取VBO(vertices数组)的texture coordinate数据起始位置偏移,单位为字节,// 由于texture coordinate数据从vertices数组中的第4个数据开始(偏移为4-1=3),// 所以这里填入 (void *)3*sizeof(float)glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(3 * sizeof(float)));glEnableVertexAttribArray(1); // 启用 location = 1 处的数据,因此该函数的参数为 1glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑VBO// 使用EBO// 绑定EBO -> 复制数据到EBO bufferglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); // 绑定EBOglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,GL_STATIC_DRAW); // 将indices中的数据复制到刚刚绑定的EBO buffer中去,EBO buffer是GPU内存上的一块区域// remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO;// keep the EBO bound.// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);// 注意!// 在解绑 VAO 之前不要解绑 EBO (但是可以解绑VBO)// 这是因为 VAO// 只是存储了如何读取VBO中数据的信息,当调用glVertexAttribPointer()函数之后VAO中就有了如何读取VBO数据的配置,而绑定EBO确是实实在在地将EBO数据存储到VAO中,因此在解绑VAO之前不能解绑EBOglBindVertexArray(0); // 解绑 VAO/************************************/// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // 使用线框模式,绘制时只绘制 三角形 的轮廓glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // 使用填充模式,绘制时对 三角形 内部进行填充/****** 6.开始绘制 ******/while (!glfwWindowShouldClose(window)){// input// -----processInput(window);// render// ------glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 使用我们之前编译连接好的 shader 程序glUseProgram(shaderProgram);glBindVertexArray(VAO); // 绑定VAO,指定当前绘制使用的VAO对象// glDrawArrays(GL_TRIANGLES, 0, 3);// 使用glDrawElements()函数指定使用基于EBO索引的绘制,函数参数如下:// 1). 指定绘制的模式,绘制模式选择绘制三角形,因此填入 GL_TRIANGLES// 2). 指定绘制的顶点个数,我们需要绘制一个三角形,一个三角形包含3个顶点,因此填入 3// 3). 指定绘制的索引类型,即EBO中的数据类型,类型为unsigned int,因此填入 GL_UNSIGNED_INT// 4). 指定绘制的索引起始偏移,此处填入 0glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);glfwSwapBuffers(window); // 在gfw中启用双缓冲,确保绘制的平滑和无缝切换glfwPollEvents(); // 用于处理所有挂起的事件,例如键盘输入、鼠标移动、窗口大小变化等事件}/************************************//****** 7.释放资源 ******/// 释放之前申请的 VBO, VAO, EBO资源和shader程序glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glDeleteProgram(shaderProgram);glDeleteTextures(1, &textureID);// glfw 释放 glfw使用的所有资源glfwTerminate();/************************************/return 0;
}// 用于处理用户输入的函数
void processInput(GLFWwindow *window)
{// 当按下 Esc 按键时调用 glfwSetWindowShouldClose() 函数,关闭窗口if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}// 在使用 OpenGL 和 GLFW 库时,处理窗口大小改变的回调函数
// 当窗口大小发生变化时,确保 OpenGL 渲染的内容能够适应新的窗口大小,避免图像被拉伸、压缩或出现其他比例失真的问题
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{glViewport(0, 0, width, height);
}

4. 编译运行及结果

编译运行:

cd ./build
cmake ..
make 
./OpenGL_Hello_Textured_Triangle

绘制结果:

绘制结果

三、参考引用

[1].LearnOpenGL-CN-入门-纹理

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

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

相关文章

Backend - Eclipse 软件写 java 项目

目录 一、下载并安装 1. 下载 2. 下载 java ee packages 3. 创建安装文件夹 二、创建java项目 1. 打开eclipse软件 2. 创建项目 3. 创建包与类 4. eclipse工作目录 三、eclipse基础配置 1. eclipse配置快捷提示 2. eclipse 查看源码配置 3. 浏览目录用树状显示 四…

VIVADO IP核之FIR抽取器多相滤波仿真

VIVADO IP核之FIR抽取器多相滤波仿真&#xff08;含有与MATLAB仿真数据的对比&#xff09; 目录 前言 一、滤波器系数生成 二、用MATLAB生成仿真数据 三、VIVADO FIR抽取多相滤波器使用 四、VIVADO FIR抽取多相滤波器仿真 五、VIVADO工程下载 总结 前言 关于FIR低通滤波…

OpenHarmony(鸿蒙南向)——平台驱动开发【Regulator】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 Regulator模块用于控制系统中各类设备的电压/电流…

有关shell指令练习2

写一个shell脚本&#xff0c;将以下内容放到脚本中 在家目录下创建目录文件&#xff0c;dir dir下创建dir1和dir2 把当前目录下的所有文件拷贝到dir1中&#xff0c; 把当前目录下的所有脚本文件拷贝到dir2中 把dir2打包并压缩为dir2.tar.xz 再把dir2.tar.xz移动到dir1中 …

excel快速入门(二)

Excel的概念说明 文章目录 Excel的概念说明常见术语说明单元格/单元格区域活动单元格/单元格区域行或列单元格引用相对引用绝对引用混合引用 Excel的常见格式说明单元格格式数字格式 Excel 工作表编辑鼠标指针介绍1.白色十字状2.单向黑色箭头状3.双向单竖线箭头状4.双向双竖线箭…

Linux下的git开篇第一文:git的意义

1.git版本控制器 git其实就是一个版本控制器&#xff0c;程序员在写代码的时候&#xff0c;利用git工具保存每一个版本&#xff0c;在程序员需要任何一个版本的时候&#xff0c;都可提供。 2.git gitee&&github github其实上就是国外的gitee&#xff0c;网址在国外&am…

C++——关联式容器(5):哈希表

7.哈希表 7.1 哈希表引入 哈希表的出现依旧是为了查找方便而设计的。在顺序结构中&#xff0c;查询一个值需要一一比较&#xff0c;复杂度为O(N)&#xff1b;在平衡树中&#xff0c;查询变为了二分查找&#xff0c;复杂度为O(logN)&#xff1b;而对于哈希表&#xff0c;我们可…

JavaWeb - 7 - SpringBootWeb入门

Spring 官网&#xff1a;Spring | Home Spring发展到今天已经形成了一种开发生态圈&#xff0c;Spring提供了若干个子项目&#xff0c;每个项目用于完成特定的功能 SpringBoot SpringBoot可以帮助我们非常快速的构建应用程序、简化开发、提高效率 一.SpringBootWeb入门 需求…

【React】样式控制

1.react组件样式控制方式 行内样式&#xff08;通过style属性&#xff09;&#xff1a;不推荐class类名控制 function App() {const style {color: skyblue,fontSize: 20px}return (<div className"App"><span style{{ color: "pink", fontSiz…

N诺计算机考研-错题

B A.LLC&#xff0c;逻辑链路控制子层。一个主机中可能有多个进程在运行&#xff0c;它们可能同时与其他的一些进程&#xff08;在同一主机或多个主机中&#xff09;进行通信。因此在一个主机的 LLC子层的一个服务访问点&#xff0c;以便向多个进程提供服务。B.MAC地址&#xf…

VSCode#include头文件时找不到头文件:我的解决方法

0.前言 1.在学习了Linux之后&#xff0c;我平常大部分都使用本地的XShell或者VSCode连接远程云服务器写代码&#xff0c;CentOS的包管理器为我省去了不少繁琐的事情&#xff0c;今天使用vscode打开本地目录想写点代码发现#include头文件后&#xff0c;下方出现了波浪线&#…

手机解压软件加密指南:让文件更安全

在数字化时代&#xff0c;文件加密对于保护个人隐私和敏感信息的重要性不言而喻。随着互联网的飞速发展&#xff0c;我们的生活和工作越来越依赖于数字设备和网络。 然而&#xff0c;这也带来了一系列的安全风险&#xff0c;如黑客攻击、数据泄露等。文件加密技术成为了保护我…

keil软件开发流程

1.先建一个文件 2.然后打开keil&#xff0c;打开keil软件新建keil工程 3.确定保存的位置以及工程的名字 4.确定开发工程所用的单片机芯片 5.复制启动文件到工程中选择否 6.创建新的C文件 7.保存C文件 8.写一个代码 9.编译代码 10.勾选设置&#xff0c;生成可执行文件 10.构建代…

无人机之可承受风速的影响因素

无人机可承受风速的影响因素是多方面的&#xff0c;这些因素共同决定了无人机在特定风速条件下的飞行稳定性和安全性。以下是一些主要的影响因素&#xff1a; 一、无人机设计与结构 无人机的大小、形状和重量都会直接影响其抗风能力。大型无人机由于具有更大的表面积和质量&am…

HAproxy-7层负载均衡集群根据不同服务请求分配服务器

搭建HAproxy----7层负载均衡集群的补充 https://blog.csdn.net/qq_73990369/article/details/142500451?spm1001.2014.3001.5501 一、再准备两台虚拟机进行测试 192.168.229.15/24 ----php1 192.168.229.16/24 ----php2 1、PHP1 & php2(192.168.229.15/24 ,192…

SVG之path详解,全面解析椭圆弧命令A

前言&#xff1a; 转载于b站深坑妙脆角&#xff0c;讲解清晰明了&#xff0c;对初使用path圆弧命令非常友好 作者&#xff1a;深坑妙脆角 https://www.bilibili.com/read/cv35872299/?jump_opus1 出处&#xff1a;bilibili 简述&#xff1a; SVG 中的 <path> 元素用于创…

大数据Flink(一百二十三):五分钟上手Flink MySQL连接器

文章目录 五分钟上手Flink MySQL连接器 一、创建数据库表 二、​​​​​​创建session集群 三、源表查询 四、​​​​​窗口计算 五、​​​​​​结果数据写回数据库 五分钟上手Flink MySQL连接器 MySQL Connector可以将本地或远程的MySQL数据库连接到Flink中&#x…

16【Protues51单片机仿真】智能洗衣机倒计时系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 用直流电机转动模拟洗衣机。要求 有弱洗、普通洗、强洗三种模式&#xff0c;可通过按键选择。可以设置洗衣时长&#xff0c;通关按键选择15、30、45、60、90分钟。时间到蜂鸣器报警提示。LCD 显示…

Koa (下一代web框架) 【Node.js进阶】

koa (中文网) 是基于 Node.js 平台的下一代 web 开发框架&#xff0c;致力于成为应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石&#xff1b; 利用 async 函数 丢弃回调函数&#xff0c;并增强错误处理&#xff0c;koa 没有任何预置的中间件&#xff0c;可快速…

大数据Hive组件安装

组件版本 组件版本Hadoop3.3.0JDK1.8.0_241Mysql5.7.25Hive3.1.2 Hadoop集群服务分布 Node1Node2Node3NameNode DataNode DataNodeDataNode NodeManager NodeManagerResourceManagerSecondaryNameNode 安装前请确定Hadoop集群服务全部启动&#xff0c;不然后续测试时会报…