LearnOpenGL - Android OpenGL ES 3.0 使用 FBO 进行离屏渲染

系列文章目录

  • LearnOpenGL 笔记 - 入门 01 OpenGL
  • LearnOpenGL 笔记 - 入门 02 创建窗口
  • LearnOpenGL 笔记 - 入门 03 你好,窗口
  • LearnOpenGL 笔记 - 入门 04 你好,三角形
  • OpenGL - 如何理解 VAO 与 VBO 之间的关系
  • LearnOpenGL - Android OpenGL ES 3.0 绘制三角形
  • LearnOpenGL - Android OpenGL ES 3.0 绘制纹理
  • LearnOpenGL - Android OpenGL ES 3.0 YUV 渲染

一、前言

利用 FBO(Framebuffer Object),我们可以实现离屏渲染。在前面的章节中,当我们调用 glDrawElements 后,手机屏幕上就会显示出绘制的图像。这意味着 OpenGL 将数据直接渲染到了手机屏幕上。通过使用 FBO,我们可以将数据渲染到纹理上,而不是直接渲染到屏幕,这个过程称为离屏渲染。

通过离屏渲染,我们可以在最终显示之前对图像进行复杂的处理。这种方法非常有用,比如在后期处理效果(如模糊、HDR、阴影等)中,或者在渲染多个场景以进行纹理贴图、环境映射等操作时。

假设你在开发一款图片处理软件,包含美颜、滤镜等功能。用户可以同时应用多种滤镜,如瘦脸、美白、长腿等,每种滤镜都通过 OpenGL Shader 进行处理和渲染。为实现这种功能,你可以设计一个图片渲染链。

一种直观的方法是为每种滤镜创建一个独立的模块,通过组合不同的模块实现多种滤镜的处理链。在处理链完成之前,我们无法将结果渲染到屏幕上。模块与模块之间的处理结果应该通过某种介质进行传递,这里使用的介质就是纹理。这也解释了我们为什么需要使用 FBO。

通过 FBO,我们可以在离屏状态下将渲染结果存储到纹理中,然后将该纹理作为输入传递给下一个滤镜模块。这样,整个处理链就可以逐步处理图像,直到应用所有滤镜后,将最终结果渲染到屏幕上。

在这里插入图片描述
本文所有代码在 FBODrawer.kt

二、FBO 简介

在这里插入图片描述
上图显示了帧缓冲区对象的结构,它提供了颜色缓冲区和深度缓冲区的替代品。如你所见,绘制操作并不是直接发生在帧缓冲区中的,而是发生在帧缓冲区所关联的对象(attachment)上。一个帧缓冲区有多个关联对象:颜色关联对象(color attachment)、深度关联对象(depth attachment)和模板关联对象(stencil attachment),分别用来替代颜色缓冲区、深度缓冲区和模板缓冲区。经过一些设置,OpenGL 就可以向帧缓冲区的关联对象中写入数据,就像写入颜色缓冲区或深度缓冲区一样。我们目前只关注颜色关联对象即可

每个关联对象又可以是两种类型的:纹理对象或渲染缓冲区对象(renderbuffer object)。当我们把纹理对象作为颜色关联对象关联到帧缓冲区对象后,OpenGL 就可以在纹理对象中绘图。渲染缓冲区对象表示一种更加通用的绘图区域,可以向其中写入多种类型的数据。

2.1 渲染缓冲对象

渲染缓冲区对象(Renderbuffer Object)是 OpenGL 和 OpenGL ES 中的一种缓冲区类型,用于离屏渲染。它提供了一种高效的方式来存储图像数据,特别适用于深度缓冲区和模板缓冲区。

渲染缓冲区对象的特点:

  1. 高效存储

    • 渲染缓冲区对象在实现上通常比纹理对象更高效,特别是用于深度和模板数据的存储。
    • 它不需要纹理过滤、MIP 贴图等特性,因此在某些场景下可以提供更好的性能。
  2. 不可直接采样

    • 与纹理对象不同,渲染缓冲区对象不能直接被着色器采样。
    • 这意味着你不能在着色器中直接访问渲染缓冲区对象中的数据,只能用于渲染过程。
  3. 用途广泛

    • 渲染缓冲区对象可以用作颜色、深度或模板缓冲区。
    • 在使用 FBO 进行离屏渲染时,渲染缓冲区对象可以作为这些附件类型附加到 FBO 上。

渲染缓冲区对象的使用步骤:

  1. 创建渲染缓冲区对象

    GLuint rbo;
    glGenRenderbuffers(1, &rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
    
  2. 分配存储

    • 根据用途分配存储,比如深度缓冲区、颜色缓冲区等。
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
    // 或者为颜色缓冲区分配存储
    // glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
    
  3. 附加到 FBO

    • 将渲染缓冲区对象附加到 FBO 作为附件。
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
    // 如果是颜色缓冲区
    // glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
    

纹理对象与渲染缓冲区对象的对比:

  • 纹理对象

    • 可以在着色器中采样,用于更灵活的图像处理。
    • 适用于需要在多个渲染步骤中反复使用和处理的图像数据。
  • 渲染缓冲区对象

    • 高效的存储和写入,但不能在着色器中采样。
    • 适用于深度缓冲区和模板缓冲区,或者不需要在着色器中采样的颜色缓冲区。

结合使用:

在实际应用中,常常将纹理对象和渲染缓冲区对象结合使用。比如:

  • 使用渲染缓冲区对象存储深度和模板数据,以获得更高的性能。
  • 使用纹理对象存储颜色数据,以便在后续渲染步骤中进行采样和处理。

例子:

假设我们在开发一个图片处理软件,通过 FBO 进行多重滤镜处理。每个滤镜模块会产生一个中间结果,这些中间结果通常存储在纹理对象中,因为它们需要被后续的滤镜模块采样和处理。然而,为了提高性能,我们可以使用渲染缓冲区对象来存储深度数据,因为这些数据通常不需要在滤镜处理中直接访问。

// 创建并绑定 FBO
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);// 创建并附加颜色附件(纹理对象)
GLuint colorTex;
glGenTextures(1, &colorTex);
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);// 创建并附加深度附件(渲染缓冲区对象)
GLuint depthRbo;
glGenRenderbuffers(1, &depthRbo);
glBindRenderbuffer(GL_RENDERBUFFER, depthRbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRbo);// 检查 FBO 完整性
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {// 处理错误
}// 解绑 FBO 以恢复默认帧缓冲区
glBindFramebuffer(GL_FRAMEBUFFER, 0);

通过这种方式,我们可以高效地实现图像的离屏渲染和多重滤镜处理。

三、FBO 使用流程

GLES30.glGenTextures(1, fboTexIds)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0])
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR)
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)// generate fbo id and config fbo
// 创建 FBO
GLES30.glGenFramebuffers(1, fbo);
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0])
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0])
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, fboTexIds[0], 0)
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, imageWidth, imageHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null)
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)

这段代码用于在 OpenGL ES 3.0 中创建并配置一个帧缓冲区对象(FBO),并将一个纹理对象附加到这个帧缓冲区对象上作为颜色附件,以便进行离屏渲染。下面是对每行代码的详细解释:

创建和配置纹理对象

// 生成一个纹理对象,并将其ID存储在 fboTexIds 数组中
GLES30.glGenTextures(1, fboTexIds);// 绑定生成的纹理对象
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]);// 设置纹理过滤参数,线性过滤
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);// 解除纹理绑定
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);
  1. 生成纹理对象

    • GLES30.glGenTextures(1, fboTexIds);:生成一个纹理对象,并将其ID存储在 fboTexIds 数组中。
  2. 绑定纹理对象

    • GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]);:将生成的纹理对象绑定到目标 GL_TEXTURE_2D
  3. 设置纹理参数

    • GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);:设置纹理的缩小过滤为线性过滤。
    • GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);:设置纹理的放大过滤为线性过滤。
  4. 解除纹理绑定

    • GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);:解除当前绑定的纹理对象。

创建和配置帧缓冲区对象

// 生成一个帧缓冲区对象,并将其ID存储在 fbo 数组中
GLES30.glGenFramebuffers(1, fbo);// 绑定生成的帧缓冲区对象
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0]);// 重新绑定之前创建的纹理对象
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]);// 将纹理对象附加到帧缓冲区对象的颜色附件上
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, fboTexIds[0], 0);// 为纹理对象分配存储空间
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, imageWidth, imageHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);// 解除纹理绑定
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);// 解除帧缓冲区对象的绑定
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);
  1. 生成帧缓冲区对象

    • GLES30.glGenFramebuffers(1, fbo);:生成一个帧缓冲区对象,并将其ID存储在 fbo 数组中。
  2. 绑定帧缓冲区对象

    • GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0]);:将生成的帧缓冲区对象绑定到目标 GL_FRAMEBUFFER
  3. 重新绑定纹理对象

    • GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]);:将之前创建的纹理对象重新绑定到目标 GL_TEXTURE_2D
  4. 附加纹理对象到帧缓冲区对象

    • GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, fboTexIds[0], 0);:将纹理对象作为颜色附件附加到帧缓冲区对象上。
  5. 为纹理对象分配存储空间

    • GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, imageWidth, imageHeight, 0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);:为纹理对象分配存储空间,并指定其格式和尺寸。
  6. 解除纹理绑定

    • GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);:解除当前绑定的纹理对象。
  7. 解除帧缓冲区对象的绑定

    • GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);:解除当前绑定的帧缓冲区对象。

四、FBO 离屏渲染

为了演示 FBO 离屏渲染,我在 FBODrawer.kt 构建了两个 shader,第一个 shader 将 RGB 图片转换为灰度图,第二个 shader 则将纹理渲染到屏幕上。

companion object {val vertexShaderSource ="""#version 300 eslayout(location = 0) in vec3 a_positlayout(location = 1) in vec2 a_texcoout vec2 v_texcoord;void main(){gl_Position = vec4(a_position, 1v_texcoord = a_texcoord;}""".trimIndent()val fragmentShaderSource ="""#version 300 esprecision mediump float;uniform sampler2D texture0;in vec2 v_texcoord;out vec4 fragColor;void main(void){fragColor = texture(texture0, v_}""".trimIndent()val fboFragmentShaderSource ="""#version 300 esprecision mediump float;uniform sampler2D texture0;in vec2 v_texcoord;out vec4 fragColor;void main(void)void main(void){vec4 tempColor = texture(texture0, v_texcoord);float gray = 0.299*tempColor.a + 0.587*tempColor.g + 0.114*tempColor.b;fragColor = vec4(vec3(gray), 1.0);}""".trimIndent()
}
private val shader = Shader(vertexShaderSource,fragmentShaderSource
)
private val fboShader = Shader(vertexShaderSource,fboFragmentShaderSource
)

因此我们需要调用两次 draw 方法:

  1. 第一次,我们的 shader 输入是 rgb 图片的纹理,输出是灰度图纹理
  2. 第二次,我们的 shader 输入是灰度图纹理,然后直接绘制到纹理上
override fun draw() {// first, fbo off screen renderingGLES30.glViewport(0, 0, imageWidth, imageHeight)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0])fboShader.use()fboShader.setInt("texture0", 0)GLES30.glBindVertexArray(vaos[0])GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, imageTexIds[0])GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)GLES30.glBindVertexArray(0)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)// second, draw texture to screenGLES30.glViewport(0, 0, screenWidth, screenHeight)shader.use()shader.setInt("texture0", 0)GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)GLES30.glBindVertexArray(vaos[0])GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]) // 用 fbo 渲染的结果作为纹理的输入GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0)GLES30.glBindVertexArray(0)
}

这段代码展示了如何使用帧缓冲区对象(FBO)进行离屏渲染,然后将离屏渲染的结果绘制到屏幕上。具体分为两个步骤:第一步是将场景渲染到 FBO,第二步是将 FBO 的内容作为纹理绘制到屏幕上。

第一步:离屏渲染到 FBO

// 设置视口为 FBO 的尺寸
GLES30.glViewport(0, 0, imageWidth, imageHeight);// 绑定 FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0]);// 使用离屏渲染的着色器程序
fboShader.use();// 设置着色器程序中纹理单元的位置
fboShader.setInt("texture0", 0);// 绑定 VAO
GLES30.glBindVertexArray(vaos[0]);// 激活纹理单元并绑定需要渲染的纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, imageTexIds[0]);// 绘制元素
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0);// 解除纹理绑定
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);// 解除 VAO 绑定
GLES30.glBindVertexArray(0);// 解除 FBO 绑定,恢复默认帧缓冲区
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
  1. 设置视口GLES30.glViewport(0, 0, imageWidth, imageHeight) 设置渲染区域为 FBO 的尺寸。
  2. 绑定 FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fbo[0]) 绑定帧缓冲区对象。
  3. 使用着色器程序fboShader.use() 使用用于离屏渲染的着色器程序。
  4. 设置纹理单元fboShader.setInt("texture0", 0) 设置着色器程序中的纹理单元。
  5. 绑定 VAOGLES30.glBindVertexArray(vaos[0]) 绑定顶点数组对象(VAO)。
  6. 激活并绑定纹理GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, imageTexIds[0]) 激活并绑定需要渲染的纹理。
  7. 绘制元素GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0) 使用索引数组绘制三角形。
  8. 解除绑定:解除纹理和 VAO 的绑定,以及 FBO 的绑定,恢复默认帧缓冲区。

第二步:将 FBO 的内容绘制到屏幕上

// 设置视口为屏幕尺寸
GLES30.glViewport(0, 0, screenWidth, screenHeight);// 使用屏幕渲染的着色器程序
shader.use();// 设置着色器程序中纹理单元的位置
shader.setInt("texture0", 0);// 清除颜色缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);// 绑定 VAO
GLES30.glBindVertexArray(vaos[0]);// 激活纹理单元并绑定 FBO 的纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]);// 绘制元素
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0);// 解除 VAO 绑定
GLES30.glBindVertexArray(0);
  1. 设置视口GLES30.glViewport(0, 0, screenWidth, screenHeight) 设置渲染区域为屏幕的尺寸。
  2. 使用着色器程序shader.use() 使用用于屏幕渲染的着色器程序。
  3. 设置纹理单元shader.setInt("texture0", 0) 设置着色器程序中的纹理单元。
  4. 清除颜色缓冲区GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT) 清除颜色缓冲区。
  5. 绑定 VAOGLES30.glBindVertexArray(vaos[0]) 绑定顶点数组对象(VAO)。
  6. 激活并绑定纹理GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, fboTexIds[0]) 激活并绑定 FBO 的纹理(即离屏渲染的结果)。
  7. 绘制元素GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_INT, 0) 使用索引数组绘制三角形。
  8. 解除绑定:解除 VAO 的绑定。

总结

  • 第一步:在 FBO 中进行离屏渲染,将结果存储在一个纹理对象中。
  • 第二步:将 FBO 中的纹理对象作为输入,绘制到屏幕上。

这种方法在图形应用程序中非常常见,特别是在实现多重渲染效果(如后期处理、反射、阴影映射等)时。

参考

  • FBODrawer.kt
  • NDK OpenGLES3.0 开发(五):FBO 离屏渲染

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

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

相关文章

《Windows API每日一练》6.4 程序测试

前面我们讨论了鼠标的一些基础知识,本节我们将通过一些实例来讲解鼠标消息的不同处理方式。 本节必须掌握的知识点: 第36练:鼠标击中测试1 第37练:鼠标击中测试2—增加键盘接口 第38练:鼠标击中测试3—子窗口 第39练&…

vite-ts-cesium项目集成mars3d修改相关的包和配置参考

如果vite技术栈下使用原生cesium,请参考下面文件的包和配置修改,想用原生创建的viewer结合我们mars3d的功能的话。 1. package.json文件 "dependencies": {"cesium": "^1.103.0","mars3d": "^3.7.18&quo…

深度学习 - Transformer 组成详解

整体结构 1. 嵌入层(Embedding Layer) 生活中的例子:字典查找 想象你在读一本书,你不认识某个单词,于是你查阅字典。字典为每个单词提供了一个解释,帮助你理解这个单词的意思。嵌入层就像这个字典&#xf…

Micrometer+ZipKin分布式链路追踪

目录 背景MicrometerMicrometer与ZipKin之间的关系专业术语分布式链路追踪原理 ZipKin安装下载 MicrometerZipKin 案例演示相关文献 背景 一个系统页面上的按钮点击到结果反馈,在微服务框架里,是由N个服务组成返回结果,中间可能经过a->b-…

【Electron】Electron入门实现

Electron 学习笔记 Electron 是一个开源框架,允许开发者使用网页技术(HTML、CSS 和 JavaScript)来构建跨平台的桌面应用程序。它由 GitHub 开发并维护,最初是为了支持开发 Atom 编辑器。Electron 结合了 Chromium(用于…

密码学及其应用 —— 对称加密技术

1. 对称加密、流加密和块加密 1.1 对称加密 对称加密(也称为密钥加密)是一种加密方式,其中加密和解密使用相同的密钥。这种加密方法基于二进制层面的操作,如XOR(异或)、SHIFT(位移)…

Redis Stream Redisson Stream

目录 一、Redis Stream1.1 场景1:多个客户端可以同时接收到消息1.1.1 XADD - 向stream添加Entry(发消息 )1.1.2 XREAD - 从stream中读取Entry(收消息)1.1.3 XRANGE - 从stream指定区间读取Entry(收消息&…

【DevExpress】WPF DevExpressMVVM 24.1版本开发指南

DevExpressMVVM WPF 环境安装 前言重要Bug(必看)环境安装控件目录Theme 主题LoginWindow 登陆窗口INavigationService 导航服务DockLayout Dock类型的画面布局TreeView 树状列表注意引用类型的时候ImageSource是PresentationCore程序集的博主找了好久&am…

Navicat 外网连接 mysql (1、通过SSH方式内网访问 2、对外开放3306端口)

1、通过SSH方式内网访问 直接常规方式使用IP、账号密码连接,失败 SSH方式: 常规 选项卡中:localhost录入数据库账号密码 SSH 选项卡中:勾选使用SSH,输入服务器IP、账号、密码 如果出现该错误,可能是服务器…

Windows下activemq开启jmx

1.activemq版本信息 activemq&#xff1a;apache-activemq-5.18.4 2.Windows下activemq开启jmx 1.进入activemq conf目录&#xff0c;备份activemq.xml文件 2.编辑activemq.xml文件&#xff0c;在broker节点增加useJmx"true" <broker xmlns"http://active…

无线通讯几种常规天线类别简介

天线对于无线模块来说至关重要&#xff0c;合适的天线可以优化通信网络&#xff0c;增加其通信的范围和可靠性。天线的选型对最后的模块通信影响很大&#xff0c;不合适的天线会导致通信质量下降。针对不同的市场应用&#xff0c;天线的材质、安置方式、性能也大不一样。下面简…

基于Vue 3.x与TypeScript的PPTIST本地部署与无公网IP远程演示文稿

文章目录 前言1. 本地安装PPTist2. PPTist 使用介绍3. 安装Cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统环境本地部署开源在线演示文稿应用PPTist&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问与使用该项目。 PPTist …

基于STM32的智能水质监测系统

目录 引言环境准备智能水质监测系统基础代码实现&#xff1a;实现智能水质监测系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景&#xff1a;水质管理与优化问题解决方案与优化收尾与总结 1. 引言 智能水质监测系统通过使用STM32嵌…

RISC-V知识总结 —— 向量(扩展)指令集

资源1:晏明 - RISC-V向量扩展指令架构及LLVM自动向量化支持 - 202112118 - 第13届开源开发工具大会&#xff08;OSDTConf2021&#xff09;_哔哩哔哩_bilibili资源2:张先轶 - 基于RISC-V向量指令集优化基础计算软件生态【第12届开源开发工具大会&#xff08;OSDT2020&#xff09…

研导智能科技——AI辅助科研产品开发

人工智能&#xff08;AI&#xff09;技术的飞速发展为科研领域带来了革命性的变化。本公司致力于开发基于人工智能的科研辅助产品&#xff0c;旨在通过智能化手段提高科研人员的工作效率和研究质量。目前&#xff0c;我们成功开发了研导学术平台&#xff08;www.zhiyanxueshu.c…

Linux运维:MySQL数据库(1)

1.信息与数据&#xff1a; 数据是信息的载体&#xff0c;信息是数据的内涵。数据库就是存储数据的仓库&#xff0c;并长期存储在计算机磁盘中&#xff0c;可由多个用户和应用程序共享的数据集合&#xff0c;就是数据库。 2.数据库中的数据的特点&#xff1a; 2.1.数据是按照某…

RuleApp1.4.6文章社区客户端 广告联盟支持Docx导入

支持编译为安卓&#xff0c;苹果&#xff0c;小程序&#xff0c;H5网页的社区客户端代码&#xff0c;包括文章模块&#xff0c;用户模块&#xff0c;动态模块&#xff0c;支付模块&#xff0c;聊天模块&#xff0c;广告模块&#xff0c;商城模块等基础功能&#xff0c;包含VIP会…

10位时间戳、13位时间戳、17位时间戳,以及在JavaScript中的格式转换

一、介绍 1、10位时间戳 2、13位时间戳 3、17位时间戳 4、时间戳转换工具 二、13位时间戳的转换 1、转标准日期 2、转格式化日期 三、10位时间戳的转换 1、转标准日期 2、转格式化日期 四、17位时间戳的转换 1、解析思路 2、解析过程 &#xff08;1&#xff09;统…

C++系统编程篇——Linux第一个小程序--进度条

&#xff08;1&#xff09;先引入一个概念&#xff1a;行缓冲区 \r和\n \r表示回车 \n表示回车并换行 ①代码一 #include<stdio.h> #include<unistd.h> int main()…

django学习入门系列之第三点《伪类简单了解》

文章目录 hover&#xff08;伪类&#xff09;after&#xff08;伪类&#xff09;往期回顾 hover&#xff08;伪类&#xff09; 伪类指的是用冒号加的 hover样式指的是&#xff0c;当用户光标移动到设定区域后&#xff0c;所执行的用法 如&#xff1a; <!DOCTYPE html>…