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

NDK OpenGL离屏渲染与工程代码整合

NDK​系列之OpenGL离屏渲染与工程代码整合,本节主要是对上一节OpenGL渲染画面效果代码进行封装设计,将各种特效代码进行分离解耦,便于后期增加其他特效。

实现效果:

实现逻辑:

1.封装BaseFilter过滤器基类,实现通用的顶点着色器、片元着色器和着色器程序的初始化,赋值和绘制操作;

2.编写CamreaFilter相机过滤器,实现FBO离屏渲染和变换矩阵等相机相关操作,绘制操作完成后,将相机过滤器实现的效果的纹理ID传递给下一个过滤器,在相机过滤器效果上进行叠加其他效果。

本节主要内容:

1.封装BaseFilter过滤器基类;

2.实现CamreaFilter相机过滤器;

3.实现ScreenFilter屏幕过滤器;

4.自定义渲染器MyGlRenderer;

源码:

NdkOpenGLPlay: NDK OpenGL渲染画面效果

一、封装BaseFilter过滤器基类

1)BaseFilter构造方法,保存顶点着色器和片元着色器代码资源文件id,保存坐标系到数据缓冲区;

public BaseFilter(Context context, int vertexSourceId, int fragmentSourceId) {this.mVertexSourceId = vertexSourceId; // 子类传递过来的顶点着色器代码IDthis.mFragmentSourceId = fragmentSourceId; // 子类传递过来的片元着色器代码ID// 顶点相关 坐标系float[] VERTEX = {-1.0f, -1.0f,1.0f, -1.0f,-1.0f, 1.0f,1.0f, 1.0f,};mVertexBuffer = BufferHelper.getFloatBuffer(VERTEX); // 保存到 顶点坐标数据缓冲区// 纹理相关 坐标系float[] TEXTURE = {0.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,};mTextureBuffer = BufferHelper.getFloatBuffer(TEXTURE); // 保存到 纹理坐标数据缓冲区init(context);
}

2)加载并编译着色器代码,链接色器ID,输出着色器程序,并获取着色器的索引值;

private void init(Context context) {String vertexSource = TextResourceReader.readTextFileFromResource(context, mVertexSourceId); // 顶点着色器代码字符串String fragmentSource = TextResourceReader.readTextFileFromResource(context, mFragmentSourceId); // 片元着色器代码字符串int vertexShaderId = ShaderHelper.compileVertexShader(vertexSource); // 编译顶点着色器代码字符串int fragmentShaderId = ShaderHelper.compileFragmentShader(fragmentSource); // 编译片元着色器代码字符串mProgramId = ShaderHelper.linkProgram(vertexShaderId, fragmentShaderId); // 链接 顶点着色器ID,片元着色器ID 最终输出着色器程序// 删除 顶点 片元 着色器IDglDeleteShader(vertexShaderId);glDeleteShader(fragmentShaderId);// 顶点着色器里面的如下:vPosition = glGetAttribLocation(mProgramId, "vPosition"); // 顶点着色器:的索引值vCoord = glGetAttribLocation(mProgramId, "vCoord"); // 顶点着色器:纹理坐标,采样器采样图片的坐标 的索引值vMatrix = glGetUniformLocation(mProgramId, "vMatrix"); // 顶点着色器:变换矩阵 的索引值// 片元着色器里面的如下:vTexture = glGetUniformLocation(mProgramId, "vTexture"); // 片元着色器:采样器
}

3)完成绘制操作,顶点坐标赋值,纹理坐标赋值,采样器赋值,通知opengl绘制,将过滤器实现的效果的纹理ID返回给下一个过滤器;

public int onDrawFrame(int textureId) {// 设置视窗大小glViewport(0, 0, mWidth, mHeight);glUseProgram(mProgramId); // 必须要使用着色器程序一次// TODO 画画,绘制 等工作// TODO 1.顶点坐标赋值mVertexBuffer.position(0);// 传值glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, mVertexBuffer);// 激活glEnableVertexAttribArray(vPosition);// TODO 2.纹理坐标赋值mTextureBuffer.position(0);// 传值glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, mTextureBuffer);// 激活glEnableVertexAttribArray(vCoord);// 只需要把OpenGL的纹理ID,渲染到屏幕上就可以了,不需要矩阵数据传递给顶点着色器了// 变换矩阵 把mtx矩阵数据 传递到 vMatrix// glUniformMatrix4fv(vMatrix, 1, false, mtx, 0);// TODO 3.片元 vTextureglActiveTexture(GL_TEXTURE0); // 激活图层// 不需要关心摄像头 和 矩阵// 绑定图层,为什么不需要GL_TEXTURE_EXTERNAL_OES?答:目前拿到的textureId已经是纹理ID了,不是摄像头直接采集到的纹理IDglBindTexture(GL_TEXTURE_2D ,textureId);// 因为CameraFilter已经做过了,我就直接显示,我用OepnGL 2D GL_TEXTURE_2D 显示就行了// glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); // 由于这种方式并不是通用的,所以先去除glUniform1i(vTexture, 0); // 传递采样器glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 通知 opengl 绘制return textureId; // 返回纹理ID,可以告诉下一个过滤器
}

二、实现CamreaFilter相机过滤器

1)CameraFilter构造方法,将顶点着色器和片元着色器代码资源文件id传给父类,完成顶点着色器、片元着色器和着色器程序的初始化工作;

public CameraFilter(Context context) {super(context, R.raw.camera_vertex, R.raw.camera_fragment);
}

2)准备工作,创建FBO帧缓冲区和FBO的纹理ID,并绑定起来;

public void onReady(int width, int height) {super.onReady(width,height);// TODO 准备工作// TODO 第一步:创建 FBO (看不见的离屏的屏幕)mFrameBuffers = new int[1];// 参数1:int n, fbo 个数// 参数2:int[] framebuffers, 用来保存 fbo id 的数组// 参数3:int offset 从数组中第几个id来保存,从零下标开始glGenFramebuffers(mFrameBuffers.length, mFrameBuffers, 0); // 实例化创建帧缓冲区,FBO缓冲区// TODO 第二步:创建属于 fbo 纹理(第一节课是没有配置的,但是这个是FOB纹理,所以需要配置纹理)// 既然上面的 FBO(看不见的离屏的屏幕),下面的目的就是要把画面显示到FBO中mFrameBufferTextures = new int[1]; // 记录FBO纹理的IDTextureHelper.genTextures(mFrameBufferTextures); // 生成并配置纹理// TODO 第三步:上面的 FBO缓冲区 与 FBO纹理 还没有任何关系,现在要让他们绑定起来glBindTexture(GL_TEXTURE_2D, mFrameBufferTextures[0]);// 生产2D纹理图像/*int target,         要绑定的纹理目标int level,          level一般都是0int internalformat, 纹理图像内部处理的格式是什么,rgbaint width,          宽int height,         高int border,         边界int format,         纹理图像格式是什么,rgbaint type,           无符号字节的类型java.nio.Buffer pixels*/glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers[0]); // 发生关系/*int target,     fbo的纹理目标int attachment, 附属到哪里int textarget,  要绑定的纹理目标int texture,    纹理int level       level一般都是0*/glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFrameBufferTextures[0], 0);// TODO 第四步:解绑操作glBindTexture(GL_TEXTURE_2D, 0);glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

3)完成绘制操作,渲染到FBO离线缓存中,顶点坐标赋值,纹理坐标赋值,变换矩阵把mtx矩阵数据传递到vMatrix,相机过滤器处理过后,其他过滤器不需要再变换矩阵,采样器赋值,通知opengl绘制,将FBO的纹理ID返回给下一个过滤器;

public int onDrawFrame(int textureId) {// 不能调用super,因为父类做的事情,和子类是很大区别的// super.onDrawFrame(textureId);glViewport(0, 0, mWidth, mHeight);  // 设置视窗大小// TODO 渲染到 FBO离线缓存中// 绑定FBO缓存(否则会绘制到屏幕上) 我们最终的效果是 离屏渲染glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers[0]);glUseProgram(mProgramId); // 必须要使用着色器程序一次// 画画 绘制操作mVertexBuffer.position(0); // 顶点坐标赋值glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, mVertexBuffer); // 传值glEnableVertexAttribArray(vPosition); // 激活mTextureBuffer.position(0); // 纹理坐标赋值glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, mTextureBuffer); // 传值glEnableVertexAttribArray(vCoord); // 激活// TODO 变换矩阵,在我们CameraFilter这里就需要处理了,后面的BaseFilter就不需要了glUniformMatrix4fv(vMatrix, 1, false, matrix, 0);// 片元 vTextureglActiveTexture(GL_TEXTURE0); // 激活图层// glBindTexture(GL_TEXTURE_2D ,textureId); // 公用的那个  1glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); // 摄像头打交道采样器:使用额外拓展的,不能使用公用的那个  2glUniform1i(vTexture, 0);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 通知 opengl 绘制// 解绑 fboglBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);glBindFramebuffer(GL_FRAMEBUFFER, 0);// FBO的纹理ID,返回了return mFrameBufferTextures[0]; // 你没有学过C,你就不明白,如果你学过,无需多言
}

4)相机过滤器顶点着色器代码:

attribute vec4 vPosition; // 顶点坐标,相当于:相机的四个点位置排版attribute vec4 vCoord; // 纹理坐标,用来图形上色的uniform mat4 vMatrix; // 变换矩阵,4*4的格式的varying vec2 aCoord; // 把这个最终的计算成果,给片元着色器 【不需要Java传递,他是计算出来的】void main() {gl_Position = vPosition; // 确定好位置排版aCoord = (vMatrix * vCoord).xy; // 兼容所有设备
}

5)相机过滤器片元着色器代码:

// 导入 samplerExternalOES 
#extension GL_OES_EGL_image_external : require// float 数据的精度 (precision lowp = 低精度) (precision mediump = 中精度) (precision highp = 高精度)
precision mediump float;// 根据上面的数据的精度,写下面的 采样器 相机的数据
// uniform sampler2D vTexture;  由于我们用的是 安卓的相机,就不能用他
uniform samplerExternalOES vTexture; // samplerExternalOES才能采样相机的数据varying vec2 aCoord; // 把这个最终的计算成果,给片元着色器,拿到最终的成果,我才能上色void main() {// 底片效果vec4 rgba = texture2D(vTexture,aCoord);  // rgbagl_FragColor = vec4(1.-rgba.r, 1.-rgba.g, 1.-rgba.b, rgba.a);
}

三、实现ScreenFilter屏幕过滤器

1)ScreenFilter构造方法,将顶点着色器和片元着色器代码资源文件id传给父类,完成顶点着色器、片元着色器和着色器程序的初始化工作;

public ScreenFilter(Context context) {super(context, R.raw.base_vertex, R.raw.base_fragment); // base_vertex(没有矩阵) base_fragment(没有OES 是sampler2D)
}

2)准备工作和绘制操作复用父类完成;

3)屏幕过滤器顶点着色器代码:

attribute vec4 vPosition; // 顶点坐标,相当于:相机的四个点位置排版attribute vec2 vCoord; // 纹理坐标,用来图形上色的varying vec2 aCoord; // 把这个最终的计算成果,给片元着色器 【不需要Java传递,他是计算出来的】void main() {gl_Position = vPosition; // 确定好位置排版   gl_Position OpenGL着色器语言内置的变量// 着色器语言基础语法// aCoord = vCoord.xy;// aCoord是2个分量的    vCoord是四个分量的.xy取出两个分量aCoord = vCoord;
}

4)屏幕过滤器片元着色器代码

// float 数据的精度 (precision lowp = 低精度) (precision mediump = 中精度) (precision highp = 高精度)
precision mediump float;// 根据上面的数据的精度,写下面的 采样器 相机的数据
uniform sampler2D vTexture;// 由于我们用的是 安卓的相机,就不能用他(用OpenGL 2D显示就行了,不需要摄像头了,sampler2D)varying vec2 aCoord;// 把这个最终的计算成果,给片元着色器,拿到最终的成果,我才能上色void main() {// texture2D (采样器, 坐标)  gl_FragColor OpenGL着色器语言内置的变量gl_FragColor = texture2D(vTexture, aCoord);// 直接上色,你直接上色,是上camera_fragment.glsl的底片效果
}

四、自定义渲染器MyGlRenderer

从上一节中我们知道,MyGlRenderer实现GLSurfaceView.Renderer接口,实现接口的onSurfaceCreated()、onSurfaceChanged()和onDrawFrame()方法;

1)当Surface创建时,回调onSurfaceCreated()函数,创建ScreenFilter屏幕过滤器,同样在创建ScreenFilter屏幕过滤器之前,我们要先创建CamreaFilter相机过滤器;

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {mCameraFilter = new CameraFilter(myGLSurfaceView.getContext()); // 先 FBOmScreenFilter = new ScreenFilter(myGLSurfaceView.getContext()); // 后 渲染屏幕
}

2)当Surface改变时,回调onSurfaceChanged()函数,完成过滤器准备工作;

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {mCameraFilter.onReady(width, height);mScreenFilter.onReady(width, height);
}

3)绘制一帧图像时,回调onDrawFrame()函数,获取纹理对象的图像数据,先通过CamreaFilter相机过滤器,实现相关效果,再将其FBO的纹理ID传递给下一个过滤器,这里由于暂无实现其他特效,就传递给了ScreenFilter屏幕过滤器,将最终成果的纹理ID通过OpenGL渲染到屏幕;

@Override
public void onDrawFrame(GL10 gl) {// 相机过滤器,绘制一帧图像,不可见mCameraFilter.setMatrix(mtx);int textureId = mCameraFilter.onDrawFrame(mTextureID[0]); // 摄像头,矩阵,都已经做了// 增加其他特效/*textureId = 美白.onDrawFrame(textureId);textureId = 大眼.onDrawFrame(textureId);textureId = xxx.onDrawFrame(textureId);*/// 屏幕过滤器,绘制一帧图像,屏幕显示mScreenFilter.onDrawFrame(textureId); // textureId == 最终成果的纹理ID
}

注:后续将会在CamreaFilter相机过滤器和ScreenFilter屏幕过滤器中间增加各种效果过滤器,实现其他特效,如:美白,大眼,兔耳朵等。

至此,OpenGL离屏渲染与工程代码整合已完成。

源码:

NdkOpenGLPlay: NDK OpenGL渲染画面效果

相关文章:

NDK OpenGL离屏渲染与工程代码整合

NDK​系列之OpenGL离屏渲染与工程代码整合,本节主要是对上一节OpenGL渲染画面效果代码进行封装设计,将各种特效代码进行分离解耦,便于后期增加其他特效。 实现效果: 实现逻辑: 1.封装BaseFilter过滤器基类&#xff0c…...

Python基础入门编程代码练习(二)

一、求1~100之间不能被3整除的数之和 循环条件&#xff1a;i<100循环操作 实现代码如下&#xff1a; def sums():sum 0for num in range(1, 101):if num % 3 ! 0:sum numprint("1~100之间不能被3整除的数之和为&#xff1a;%s" % (sum))sums() print("1~…...

C# | 对象池

对象池 文章目录 对象池前言什么是对象池对象池的优点对象池的缺点 实现思路示例代码 结束语 前言 当我们开发一个系统或者应用程序时&#xff0c;我们通常需要创建很多的对象&#xff0c;这些对象可能是线程、内存、数据库连接、文件句柄等等。在某些情况下&#xff0c;我们需…...

CSS小技巧之圆形虚线边框

虚线相信大家日常都用的比较多&#xff0c;常见的用法就是使用 border-style 控制不同的样式&#xff0c;比如设置如下边框代码&#xff1a; border-style: dotted dashed solid double;这将设置顶部的边框样式为点状&#xff0c;右边的边框样式为虚线&#xff0c;底部的边框样…...

QString与QByteArray互相转换的方法

QString与QByteArray互相转换的方法 [1] QString与QByteArray互相转换的方法QString转QByteArray方法QByteArray转QString方法QByteArray类同样不以’\0’为结尾QByteArray转QString&#xff0c;主要用buf.toHex()即可 [2] Qt开发串口通讯软件中的数据转换问题1.读取串口命令-Q…...

Springboot +Flowable,设置流程变量的方式(一)

一.简介 为什么需要流程变量。 举个例子&#xff0c;假设有如下一个流程&#xff0c;截图如下&#xff1a; 这是一个请假流程&#xff0c;那么谁请假、请几天、起始时间、请假理由等等&#xff0c;这些都需要说明&#xff0c;不然领导审批的依据是啥&#xff1f;那么如何传递…...

机器学习13(正则化)

文章目录 简介正则化经验风险和结构风险过拟合正则化建模策略 逻辑回归逻辑回归评估器 练习评估器训练与过拟合实验评估器的手动调参 简介 这一节详细探讨关于正则化的相关内容&#xff0c;并就 sklearn 中逻辑回归&#xff08;评估器&#xff09;的参数进行详细解释由于 skle…...

并发编程学习(十一):原子数组、

1、数组类型的原子类 原子数组类型&#xff0c;这个其实和AtomicInteger等类似&#xff0c;只不过在修改时需要指明数组下标。 CAS是按照来根据地址进行比较。数组比较地址&#xff0c;肯定是不行的&#xff0c;只能比较下标元素。而比较下标元素&#xff0c;就和元素的…...

递归到动态规划:省去枚举行为

如果在动态规划的过程中没有枚举行为&#xff0c;那严格位置依赖和傻缓存的方式并没有太大区别&#xff0c;但是当有枚举行为的时候&#xff08;一个位置依赖于多个位置&#xff09;&#xff0c;那严格位置依赖是有优化空间的&#xff0c;枚举行为也许可以省去&#xff0c;题目…...

服务(第二十一篇)mysql高级查询语句(二)

①视图表&#xff1a; 视图表是虚拟表&#xff0c;用来存储SQL语句的定义 如果视图表和原表的字段相同&#xff0c;是可以进行数据修改的&#xff1b; 如果两者的字段不通&#xff0c;不可以修改数据。 语法&#xff1a; 创建&#xff1a;create view 试图表名 as ... 查…...

MYSQL高可用配置(MHA)

1、什么是MHA MHA&#xff08;Master High Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故障切换的过程中最大…...

单精度浮点数与十进制数据相互转换

一、float基础&#xff1a; Float类型占4个字节,也就是32bit,其中最高位是符号位,2~9位是指数位,后边的23bit是数值位.如下所示 大部分数据的二进制形式都可以用科学计数法表示,即1.m*2^n这种形式,只要知道m和n,就能确定一个数值 二、小数位如何转变为二进制&#xff1a; 下面…...

PMP敏捷-4大价值观、12原则

宣言及4大价值观 个体及互动 胜于 流程和工具 以人为本 工作的软件 胜于 完整的文档 以价值为导向 客户合作 胜于 合同谈判 合作共赢 应对变更 胜于 遵循计划 拥抱变化 12原则 工作原则&#xff1a;精益、至简&#xff0c;实现这种原则的方式是“定期反省”。9、10、12 …...

K8S—Helm

一、Helm介绍 helm通过打包的方式&#xff0c;支持发布的版本管理和控制&#xff0c;很大程度上简化了Kubernetes应用的部署和管理。 Helm本质就是让k8s的应用管理&#xff08;Deployment、Service等&#xff09;可配置&#xff0c;能动态生成。通过动态生成K8S资源清单文件&a…...

ALSA内部函数调用流程

ALSA内部函数调用流程 一直都有这样的一个疑问 就是在linux系统中我们调用snd_pcm_open后&#xff0c;就不知道alsa内部是怎么运行的了 用户的pcm_open()相当于先对ASoC各个驱动模块startup()&#xff0c;再做hw_params()。 pcm_open()pcm->fd open("/dev/snd/pcm…...

Python正则表达式详解,保姆式教学,0基础也能掌握正则

正则作为处理字符串的一个实用工具&#xff0c;在Python中经常会用到&#xff0c;比如爬虫爬取数据时常用正则来检索字符串等等。正则表达式已经内嵌在Python中&#xff0c;通过导入re模块就可以使用&#xff0c;作为刚学Python的新手大多数都听说”正则“这个术语。 今天来给…...

ChatGPT 接入飞书教程,创建自己的聊天机器人

ChatGPT 接入飞书教程,创建自己的聊天机器人 一、飞书进入开发者平台。点击创建应用。二、打开Aircode,点击创建应用,上面输入名字,下面选择Node.js v16三、配置环境,点击Environments,创建四个变量,全部要大写本教程收集于: AIGC从入门到精通教程 首先,准备三个账号…...

JS生成随机数(多种解决方案)

JS生成随机数 概述 随机数是编程语言中的重要组成部分。在JavaScript中&#xff0c;生成随机数是一项简单的任务。本文将介绍生成随机数的各种方法。 Math.random() Math.random()是JavaScript中生成随机数最常见的方法。该方法返回介于0和1之间的随机数。例如&#xff0c;…...

文件IO 函数 静态库和动态库的创建 5.11

5.11 文件IO函数 1.数据读写 ssize_t read(int fd,void *buf,size_t count); 功能&#xff1a; ​ 从fd对应的文件中 读取前count个字节的数据到buf缓冲区中 头文件&#xff1a; ​ #include <unistd.h> 参数&#xff1a; ​ fd &#xff1a;文件描述符 ​ buf…...

考研日语-详解ている、てある、ていく、てくる用法

目录 一、ている用法 1. 表示现在状态 2. 表示持续动作 3. 表示经验或习惯 4. 表示结果或效果 二、てある用法 1. 表示已经完成的动作 2. 表示现在状态 3. 表示被动 三、ていく用法 1. 表示未来的动作 2. 表示逐渐变化的过程 四、てくる用法 1. 表示过去到现在的…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

windows系统MySQL安装文档

概览&#xff1a;本文讨论了MySQL的安装、使用过程中涉及的解压、配置、初始化、注册服务、启动、修改密码、登录、退出以及卸载等相关内容&#xff0c;为学习者提供全面的操作指导。关键要点包括&#xff1a; 解压 &#xff1a;下载完成后解压压缩包&#xff0c;得到MySQL 8.…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》

&#x1f9e0; LangChain 中 TextSplitter 的使用详解&#xff1a;从基础到进阶&#xff08;附代码&#xff09; 一、前言 在处理大规模文本数据时&#xff0c;特别是在构建知识库或进行大模型训练与推理时&#xff0c;文本切分&#xff08;Text Splitting&#xff09; 是一个…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...

针对药品仓库的效期管理问题,如何利用WMS系统“破局”

案例&#xff1a; 某医药分销企业&#xff0c;主要经营各类药品的批发与零售。由于药品的特殊性&#xff0c;效期管理至关重要&#xff0c;但该企业一直面临效期问题的困扰。在未使用WMS系统之前&#xff0c;其药品入库、存储、出库等环节的效期管理主要依赖人工记录与检查。库…...