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

【2】openGL shader着色器分析三角形填色

源代码在下面。文档查询 > docs.gl

结果展示:使用自己的shader和打印错误描述
在这里插入图片描述

该篇主要在上一部分代码的基础上添加了自己写的shader,即着色器。最常用的两个着色器 vertex shader 和 fragment shader,即顶点着色器和片段着色器。

大概了解一下:

  • shader只是一段程序
  • 假如你要画一个三角形,有三个顶点,那么vertex shader就被调用了三次
  • 你要对三角形填色,也就是光栅化,那么fragment shader就被调用了很多次,成千上万次,一个像素点调用一次
  • openGL是一个状态机,就跟有很多开关一样,不是说你一改变代码就立马执行并影响到后面的代码了

添加的shader主要通过函数 CreateShader 来实现,本次主要通过查文档来提高一下分析能力。

/*方便起见,写成一个函数*/
static unsigned int CompileShader(unsigned int type, const std::string& source) {unsigned int id = glCreateShader(type);const char* src = source.c_str(); /*或者写 &source[0]*/glShaderSource(id, 1, &src, nullptr);glCompileShader(id);int result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE) {int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);// char message[length]; /*这里会发现因为长度不定,无法栈分配,但你仍要这么做*/char* message = (char*)alloca(length * sizeof(char));glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex":"fragment" )<< "shader!请定位到此行" << std::endl;std::cout << message << std::endl;glDeleteShader(id);return 0;}return id;
}/*使用static是因为不想它泄露到其他翻译单元?
使用string不是最好的选择,但是相对安全, int类型-该着色器唯一标识符,一个ID*/
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {/*使用unsigned是因为它接受的参数就是这样,或者可以使用 GLuint,但是作者不喜欢这样,因为它要使用多个图像api*/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;
}

开始分析部分代码:

1.先看 CreateShader 和 CompileShader,都是 static

这里对static关键字的使用做以下解释:

  • C++函数默认是非static的,也就是可以从外部函数/文件调用。

  • 如果定义为static,则这个函数只在所在的编译单元(源文件内)可见并用,外部无法访问。

  • 在OpenGL中,通常一个文件会包含多个shader程序,我们需要一个函数返回一个特定程序的ID。

  • 如果不加static,这个函数就会被所有程序公用,可能会产生冲突。

  • 加static后,这个函数就只对本文件内的对象可见,不会对其他文件产生影响。

  • 换句话说,不加static可能会导致函数定义在其他翻译单元(文件)中重复,产生命名冲突。

  • 加static可以避免该函数暴露给其他文件,仅在本文件内起到程序创建的目的,不与其他程序产生影响。

所以总的来说,static用于将函数封装在本文件内,避免跨文件调用产生潜在 bugs 的意思。

您提出了一个好问题。我刚才解释中说:

“在OpenGL中,通常一个文件会包含多个shader程序,我们需要一个函数返回一个特定程序的ID。”

这里需要进一步解释下为什么:

  • 在OpenGL中,一个着色器程序可以包含多个shader(顶点着色器+片元着色器等)。

  • 如果一个文件定义了多个这样的程序,就需要区分返回每个程序的唯一ID。

  • 如果函数定义为非static,当多个程序同时调用此函数时:

    • 由于函数名重复定义,编译会报错。

    • 或者后定义的程序会覆盖前面程序保存的ID,导致ID错乱。

  • 使用static后,每个程序调用的函数实际上是不同函数的副本。

  • 这样每个程序得到的ID就是对应着色器程序本身的唯一标识符。

所以非static定义在这里可能会导致:

  • 编译错误
  • ID管理错误
  • 程序无法正确识别各自的着色器

而static可以避免此问题,保证函数本地性质的定义。

2.根据文档分析函数,如 glShaderSource

查看文档:https://docs.gl/gl4/glShaderSource

在这里插入图片描述
四个参数,分别是
在这里插入图片描述
函数描述:
glShaderSource sets the source code in shader to the source code in the array of strings specified by string. Any source code previously stored in the shader object is completely replaced. The number of strings in the array is specified by count. If length is NULL, each string is assumed to be null terminated. If length is a value other than NULL, it points to an array containing a string length for each of the corresponding elements of string. Each element in the length array may contain the length of the corresponding string (the null character is not counted as part of the string length) or a value less than 0 to indicate that the string is null terminated. The source code strings are not scanned or parsed at this time; they are simply copied into the specified shader object.

这个看完就比较清楚了。

第一个参数,GLuint类型,确定是哪个shader,
您提出了一个重要的问题。

OpenGL中的shader id(如glCreateShader返回的id),如果不加以管理,确实可能不是唯一的。

例如:

  • 程序运行期间创建多个shader对象,IDs可能重叠冲突。

  • 不同程序间shader ID也可能相同。

所以为了保证shader id的唯一性,需要做一些附加处理:

  1. 使用静态计数器维护shader id分配,每个id加1分配。

  2. 将id与shader对象绑定,使用对象指针作为唯一标识。

  3. 提取id生成代码到单独函数,控制唯一性。

  4. 为程序设计名称空间,每个程序独立定义id。

  5. 使用面向对象的shader类,id作为对象属性管理。

  6. 等等其它方法。

即使OpenGL ids本身不唯一,我们也可以通过程序设计的方式来管理ids,保证在程序运行过程中是唯一的。

这就需要考虑额外的id管理机制,而不是简单依赖OpenGL原生id。

第二个参数,算个数,shader的个数,shader是以string的形式存在的,vertexShader的内容就是vertexShader的源代码。

第三个参数传进来的指针就是

 std::string vertexShader ="#version 330 core\n""\n""layout(location = 0) in vec4 position;""\n""void main()""{\n""   gl_Position  = position;\n""}\n";std::string fragmentShader ="#version 330 core\n""\n""layout(location = 0) out vec4 color;""\n""void main()""{\n""   color = vec4(1.0, 0.0, 0.0, 1.0);\n""}\n";

第四个参数,给一个数组计算每个shader的长度

3.错误打印 直接看代码即可

glGetShaderInfoLog(id, length, &length, message);

openGL提供了接口。


源代码:

#inclu
de <iostream>
#include <string> #include <GL/glew.h> 
#include <GLFW/glfw3.h>/*方便起见,写成一个函数*/
static unsigned int CompileShader(unsigned int type, const std::string& source) {unsigned int id = glCreateShader(type);const char* src = source.c_str(); /*或者写 &source[0]*/glShaderSource(id, 1, &src, nullptr);glCompileShader(id);int result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE) {int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);// char message[length]; /*这里会发现因为长度不定,无法栈分配,但你仍要这么做*/char* message = (char*)alloca(length * sizeof(char));glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex":"fragment" )<< "shader!请定位到此行" << std::endl;std::cout << message << std::endl;glDeleteShader(id);return 0;}return id;
}/*使用static是因为不想它泄露到其他翻译单元?
使用string不是最好的选择,但是相对安全, int类型-该着色器唯一标识符,一个ID*/
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {/*使用unsigned是因为它接受的参数就是这样,或者可以使用 GLuint,但是作者不喜欢这样,因为它要使用多个图像api*/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;//if (glewInit() != GLEW_OK)/*glew文档,这里会报错,因为需要上下文,而上下文在后面*///    std::cout << "ERROR!-1" << std::endl;/* 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)/*这里就不会报错了*/std::cout << "ERROR!-2" << std::endl;std::cout << glGetString(GL_VERSION) << std::endl;float positions[6] = {-0.5f, 0.5f,0.0f, 0.0f,0.5f, 0.5f};/*这段代码是创建和初始化顶点缓冲对象(Vertex Buffer Object,简称VBO)。VBO是OpenGL中一个很重要的概念,用于高效渲染顶点数据。它这段代码的作用是:glGenBuffers生成一个新的VBO,ID保存到buffer变量中。glBindBuffer将这个VBO绑定到GL_ARRAY_BUFFER目标上。glBufferData向被绑定的这个VBO中填充实际的顶点数据。通过这三步:我们得到了一个可以存储顶点数据的VBO对象后续绘制调用只需要指定这个VBO就可以加载顶点数据教程强调VBO是因为:相对直接送入顶点更高效绘制调用不再需要每帧重复发送相同顶点提高渲染性能所以总结下VBO可以高效绘制复杂顶点数据至显卡,是OpenGL重要概念glGenBuffers(1, &buffer);
glGenBuffers作用是生成VBO对象的ID编号。第一个参数1表示要生成的VBO数量,这里只生成1个。第二个参数&buffer是用于返回生成的VBO ID编号。glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBindBuffer用于将VBO对象绑定到指定的目标上。第一个参数GL_ARRAY_BUFFER表示要绑定的目标是顶点属性数组缓冲。GL_ARRAY_BUFFER指定将要保存顶点属性数据如位置、颜色等。第二个参数buffer就是前面glGenBuffers生成的VBO ID。所以总结下:glGenBuffers生成1个VBO对象并获取ID编号glBindBuffer将这个VBO绑定到属性缓冲目标上,作为后续顶点数据的存储对象。glBufferData的作用是向之前绑定的VBO对象中填充实际的顶点数据。参数说明:GL_ARRAY_BUFFER:指定操作目标为顶点属性缓冲(与glBindBuffer一致)6 * sizeof(float):数据大小,这里 positions 数组有6个float数positions:数组指针,提供实际的数据源GL_STATIC_DRAW:数据使用模式GL_STATIC_DRAW:数据不会或很少改变
GL_DYNAMIC_DRAW:数据可能会被修改
GL_STREAM_DRAW:数据每次绘制都会改变
它的功能是:分配指定大小内存给当前绑定的VBO对象将positions数组内容拷贝到VBO对象内存中以GL_STATIC_DRAW模式,显卡知道如何优化分配内存这样一来,positions数组中的顶点数据就上传到GPU中VBO对象里了。OpenGL随后通过该VBO对象来读取顶点数据进行绘制。*/unsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), positions, GL_STATIC_DRAW);glEnableVertexAttribArray(0);/*index-只有一个属性,填0size-两个数表示一个点,填2stripe-顶点之间的字节数pointer-偏移量好的,我们来用一个例子来解释glVertexAttribPointer的参数含义:假设我们有一个VBO,里面存放3个三维顶点数据,每个顶点由(x,y,z)组成,每个元素类型为float。那么数据在VBO中排列如下:VBO地址 | 数据
0     |  x1
4     |  y1\
8     |  z1
12     |  x2
16     |  y2
20     |  z2
24     |  x3
28     |  y3
32     |  z3现在我们要告诉OpenGL如何解析这些数据:glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0);- 0:属性为位置数据
- 3:每个位置由3个float组成,(x,y,z)
- GL_FLOAT:数据类型是float
- 12:当前属性到下一个属性的间隔,即一个顶点需要12个字节
- 0:这个属性起始位置就是VBO的开头这样OpenGL就知道:- 从VBO开始地址读取3个float作为第一个顶点的位置
- 下一个顶点偏移12字节再读取3个float最后一个参数0就是告诉OpenGL属性的起始读取偏移是多少。好的,用一个例子来具体说明一下这种情况:假设我们有一个VBO来存储顶点数据,每个顶点包含位置和颜色两个属性。数据在VBO内部的排列方式为:位置x | 位置y | 位置z | 颜色r | 颜色g | 颜色b那么对于第一个顶点来说,它在VBO内的布局是:VBO地址 | 数据
0     |  位置x\
4     |  位置y
8     |  位置z
12    |  颜色r
16    |  颜色g
20    |  颜色b此时,我们设置位置属性和颜色属性的指针:glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, 12);可以看到:- 位置属性从0字节处开始读取
- 颜色属性从12字节处开始读取(让出位置数据占用的空间)这就是为什么位置属性的偏移不能写0,需要指定非0偏移量让出给颜色属性存储空间。这样才能正确解析这两个分开但共处一个VBO的数据。*/glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);/* (const void)*8//*这里开始使用着色器*/std::string vertexShader ="#version 330 core\n""\n""layout(location = 0) in vec4 position;""\n""void main()""{\n""   gl_Position  = position;\n""}\n";std::string fragmentShader ="#version 330 core\n""\n""layout(location = 0) out vec4 color;""\n""void main()""{\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);// glDrawElements(GL_TRIANGLES, )/*    glBegin(GL_TRIANGLES);glVertex2f(-0.5f, 0.5f);glVertex2f(0.0f, 0.0f);glVertex2f(0.5f, 0.5f);glEnd();*//* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glDeleteProgram(shader);glfwTerminate();return 0;
}

相关文章:

【2】openGL shader着色器分析三角形填色

源代码在下面。文档查询 > docs.gl 结果展示&#xff1a;使用自己的shader和打印错误描述 该篇主要在上一部分代码的基础上添加了自己写的shader&#xff0c;即着色器。最常用的两个着色器 vertex shader 和 fragment shader&#xff0c;即顶点着色器和片段着色器。 大概…...

mysql数据表Table is marked as crashed and should be repaired 的解决办法

错误原因 网上查了一下&#xff0c;错误的产生原因&#xff0c;有网友说是频繁查询和更新XXXX表造成的索引错误&#xff0c;还有说法是Mysql数据库因某种原因而受到了损坏。 【如&#xff1a;数据库服务器突发性断电&#xff0c;在数据表提供服务时对表的源文件进行某种操作都…...

【Unity基础】1.项目搭建与视图编辑

【Unity基础】1.项目搭建与视图编辑 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity基础系列博客&#xff0c;终于要开始写基础系列的博客了&#xff0c;前两篇的内容基本上与入门系列相同&#xff0c;如果有紧跟入门系列的同学可以直接从第三篇文章开始看 好了话不多说我们开…...

C语言每日一练---Day(14)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;统计每个月兔子的总数 数列的和 &#x1f493;博主csdn个人主页&#x…...

基于孔雀算法优化的BP神经网络(预测应用) - 附代码

基于孔雀算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于孔雀算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.孔雀优化BP神经网络2.1 BP神经网络参数设置2.2 孔雀算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…...

【小沐学Unity3d】3ds Max 骨骼动画制作(蒙皮修改器skin)

文章目录 1、简介2、蒙皮修改器3.1 骨骼对象测试3.2 Biped对象测试 3、动画制作4、FBX导出结语 1、简介 “蒙皮”修改器是一种骨骼变形工具&#xff0c;主要设计用于通过另一个对象对一个对象进行变形来创建角色动画。可使用骨骼、样条线和其他对象变形网格、面片和 NURBS 对象…...

【Latex】使用技能站:(三)使用 Vscode 配置 LaTeX

使用 Vscode 配置 LaTeX 引言1 安装texlive2 安装vscode2.1 插件安装2.2 配置 3 安装SumatraPdf3.1 vscode配置3.2 配置反向搜索 引言 安装texlive 安装vscode 安装SumatraPdf 1 安装texlive 在线LaTeX编辑器&#xff1a;https://www.overleaf.com TeX Live下载&#xff1a;h…...

诗诺克科技引领数字资产智能交易革命

在当今全球金融市场中&#xff0c;数字资产的崛起正引发着一场前所未有的变革。随着区块链技术不断演进和数字资产广泛获得认可&#xff0c;智能交易系统正在迅速成为投资者和交易者的首选工具。这一趋势不仅在全球范围内显著&#xff0c;而且为金融领域的未来带来了令人瞩目的…...

混合编程python与C++

上个版本: 只是用到ctypes进行传输, 这次将python服务端更改为C服务端,方便后续维护. 本文实现功能: python传输图片给C, C接受图片后对图片进行处理,并将结果返回给python客户端, pass image from python to C C 服务端 .h文件 注意文中的model // .h #pragma once #inclu…...

【单片机】单片机入门指南

一、概述 单片机&#xff08;Microcontroller&#xff0c;简称MCU&#xff09;是一种集成了微处理器、存储器、时钟、IO端口和外设接口等的集成电路芯片。它可以通过编程实现各种控制、运算等功能&#xff0c;常用于自动化控制、家电、智能仪表等领域。 二、单片机的种类和选…...

【PyQt】下载文件时弹出提示用户选择保存文件位置的对话框

1 需求 在界面软件中&#xff0c;用户点击下载某个文件&#xff0c;此时软件需要提示用户选择保存到电脑的某个位置&#xff0c;然后软件才能将文件保存到用户指定的电脑文件夹中。 2 代码 # 需引入的库 import os import sys from PyQt5.QtWidgets import QFileDialogsrc .…...

工具分享 | PDF文档解析工具PyMuPDF

1 需求描述 最近工作需要从PDF文档中按照章节解析出对应的文本和图片(后续可能还会有表格)&#xff0c;经过调研&#xff0c;找到了一个功能强大的解析工具MuPDF&#xff0c;对应的Python包是PyMuPDF。本篇博客记录使用它来实现具体功能。 官方文档&#xff1a;https://pymupd…...

QML Book 学习基础5(An Image Viewer)

目录 桌面版&#xff08;win端&#xff09; 移动端 下面我们用更有挑战性例子来使用Qt控件&#xff0c;将创建一个简单的图像查看器。 桌面版&#xff08;win端&#xff09; 程序主要由四个主要区域组成&#xff0c;如下所示。菜单栏、工具栏和状态栏&#xff0c;通常由控件…...

解决Jackson解析JSON时出现的Illegal Character错误

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

feign和openfeign的区别

1.OpenFeign介绍 是一个Web声明式的Http客户端远程远程调用工具&#xff0c;底层是封装HttpClient技术&#xff0c;提供接口和注解形式调用。 注意feign客户端调用的事项&#xff1a; a、如果请求参数没有加上注解的话&#xff0c;默认采用post请求发送。 b、服务的名称命名不能…...

Python飞机大战小游戏

游戏规则&#xff1a;键盘上下左右键控制飞机移动 游戏展示图片&#xff1a; 源码&#xff1a; 第一个py命名为&#xff1a;plane_main.py import pygamefrom plane_sprites import *class PlaneGame(object):# """飞机大战主游戏"""def __in…...

【python爬虫】7.爬到的数据存到哪里?

文章目录 前言存储数据的方式存储数据的基础知识基础知识&#xff1a;Excel写入与读取基础知识&#xff1a;csv写入与读取项目&#xff1a;存储周杰伦的歌曲信息 复习 前言 上一关我们以QQ音乐为例&#xff0c;主要学习了如何带参数地请求数据&#xff08;get请求&#xff09;…...

Docker 的快速使用

ubuntu安装 centos安装 安装完毕之后执行一下这条命令&#xff0c;可以避免每次使用docker命令都需要sudo权限 sudo usermod -aG docker $USER阿里云docker镜像加速 DockerHub 遇到不懂或者不会使用的命令可以使用docker --help查看文档 docker --help 如&#xff1a; dock…...

Docker consul容器服务自动发现和更新

目录 一、什么是服务注册与发现 二、Docker-consul集群 1.Docker-consul 2.registrator 3.Consul-template 三、Docker-consul实现过程 四、Docker-consul集群配置 1.下载consul服务 2.web服务器启动多例nginx容器&#xff0c;使用registrator自动发现 3.使用…...

MPI内置类型与自定义类型

内置类型 MPI_CHAR: 字符型 MPI_UNSIGNED_CHAR: 无符号字符型MPI_BYTE: 字节型MPI_SHORT: 短整型MPI_UNSIGNED_SHORT: 无符号短整型MPI_INT: 整型MPI_UNSIGNED: 无符号整型MPI_LONG: 长整型MPI_UNSIGNED_LONG: 无符号长整型MPI_FLOAT: 单精度浮点型MPI_DOUBLE: 双精度浮点型M…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...