osg使用整理(12):SSAO屏幕空间环境光遮蔽

news/2024/7/7 7:32:40/文章来源:https://www.cnblogs.com/wangxydela/p/18284632

一、基础概念

1、SSAO:通过将褶皱、孔洞和非常靠近墙面变暗的方法,近似模拟间接光照。SSAO称为屏幕空间环境光遮蔽 ,使用屏幕空间场景的深度而不是真实的几何体数据来确定遮蔽量,速度快效果好。

2、实现原理:根据物体表面法线方向生成一个半球随机深度采样,主要看物体周围深度值大小,通过这个值来确定是否被遮蔽。

3、关键点:

​ a. 投影采样点到屏幕空间,然后获取深度纹理

​ b. 采样深度缓冲

​ c. 如果采样位置比深度纹理深度大,说明被遮挡,遮挡系数增加

​ 由此发现采样密度决定了最后遮蔽效果质量,但过多的采样点会导致渲染卡顿。可以通过随机旋转采样核,达到采样点少且遮蔽效果好的目的。

4、法向半球采样步骤:

​ a. 生成随机三维点坐标,分布类似于朝向z轴的半球

//随机数生成函数
int xorshift32()
{static usigned int x=1424447641;x^=x<<13;x^=x>>17;x^=x<<5;return x;
}float random(float min,float max)
{return min+static_cast<float>(xorshift32()/static_cast<float>(0xFFFFFFFF/(max-min)));
}
//线性插值
float lerp(float min,float max,float t)
{return min*(1.0-t)+max*t;
}
osg::Vec3f* generateHemisphereSamples(int kernelSize)
{osg::Vec3f* kernel=new osg::Vec3f[kernelSize];for (int i = 0; i < kernelSize; ++i) {kernel[i]=osg::Vec3f(random(-0.95,0.95),random(-0.95,0.95),random(0.0,1.0));kernel[i].normalize();kernel[i] *= random(0.0f, 1.0f);float scale = float(i) / float(kernelSize);scale = lerp(0.1f, 1.0f, scale * scale);kernel[i] *= scale;}return kernel;
}

​ b. 离散化分布点,并保证距离中心点越近,采样点越多

kernel[i] *= random(0.0f, 1.0f);
float scale = float(i) / float(kernelSize);
scale = lerp(0.1f, 1.0f, scale * scale);
kernel[i] *= scale;

​ c. 生成噪声纹理

osg::Vec3f* generateNoise(int noise)
{osg::Vec3f* noiseData=new osg::Vec3f[noiseSize];for(int i=0;i<noiseSize;++i){noiseData[i]=osg::Vec3f(random(-1.0,1.0),random(-1.0,1.0),0.0);noiseData[i].normalize();noiseData[i]=noiseData[i]+osg::Vec3f(1.0,1.0,1.0);noiseData[i]=noiseData[i]/2.f;}return noiseData[i];
}osg::ref_ptr<osg::Texture2D> createDataTexture(int width,int height)
{osg::ref_ptr<osg::Texture2D> texture=new osg::Texture2D;osg:Image* image=new osg:Image;auto data=generateNoise(width*height);image->setImge(width,height,1,GLRGB,GL_FLOAT,(unsigned char*)data);texture->setImage(image);texture->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT);texture->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT);texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);return texture;
}

5、创建延迟渲染shader

/*延迟渲染相机*/
osg::ref_ptr<RttCamera> createDeferCamera(osg::Camera::BufferComponent buffer1,osg::Texture* tex1,osg::Camera::BufferComponent buffer2,osg::Texture* tex2,osg::Camera::BufferComponent buffer3,osg::Texture* tex3,int width,int height)
{osg::ref_ptr<RttCamera> camera=new RttCamera(width,height);camera->setRenderTargetImplementation(osg::Camera::RenderTargetImplementation::FRAME_BUFFER_OBJECT);camera->setClearMask(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);camera->setPostDrawCallBack(new FBOPostDrawCallback);camera->setRenderOrder(osg::Camera::PRE_RENDER,20);camera->setViewPort(0,0,width,height);if(tex1){tex1->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);tex1->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);camera->attach(buffer1,tex1);}if(tex2){tex2->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);tex2->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);camera->attach(buffer2,tex2);}if(tex3){tex3->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);tex3->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);camera->attach(buffer3,tex3);}///顶点着色器const char* vertCode=R"(#version 330layout(location = 0) in vec3 Position;layout(location = 2) in vec3 normal;layout(location = 3) in vec3 TexCoord;uniform mat4 osg_ModelViewProjectionMatrix;uniform mat4 osg_ModelViewMatrix;uniform mat4 osg_NormalMatrix;out vec3 vNormal;out vec2 texCoord;out vec4 fragPos;void main(){texCoord=TexCoord;fragPos=osg_ModelViewMatrix*vec4(Position,1.0);vec4 viewNorm=transpose(inverse(osg_ModelViewMatrix))*vec4(-normal,1.0);vNormal=normalize(viewNorm.xyz);gl_Position=osg_ModelViewProjectionMatrix*vec4(Position,1.0);})";const char* fragCode=R"(#version 330 coreuniform vec3 frontCol=vec3(1.0,0.0,0.2);layout (location = 0) out vec4 gColor;layout (location = 1) out vec4 gNormal;layout (location = 2) out vec4 gPosition;in vec2 texCoord;in vec4 fragPos;in vec3 vNormal;void main(){    // Store the fragment position vector in the first gbuffer texturegPosition.xyz = fragPos.xyz;// Also store the per-fragment normals into the gbuffergNormal = vec4(vNormal,1.0);          gColor=vec4(frontCol,1.0);})";osg::ref_ptr<osg::Shader> vertShader=new osg::Shader(osg::Shader::VERTEX,vertCode);osg::ref_ptr<osg::Shader> fragShader=new osg::Shader(osg::Shader::FRAGMENT,fragCode);osg::ref_ptr<osg::Program>  program=new osg::Program;program->addShader(vertShader);program->addShader(fragShader);camera->getOrCreateStateSet()->setAttributeAndModes(program,OVERRIDE_ON);return camera;
}

6、创建ssao的shader。读sampleDepth出深度缓冲区(uTexLinearDepth)。如果它在样本位置的前面,则样本位于几何图形的“内部”并有助于遮挡。如果sampleDepth在样本位置的后面,则样本不会对遮挡因子做出贡献。引入rangeCheck有助于防止较大的深度不连续性之间的错误遮挡。

osg::ref_ptr<RttCamera> createSSAOCamera(osg::Texture* postionTex,osg::Texture* normalTex,osg::Matrix& projMat,osg::Camera::BufferComponent buffer,osg::Texture* tex,int width,int height)
{osg::ref_ptr<RttCamera> camera=new RttCamera(width,height);camera->setRenderTargetImplementation(osg::Camera::RenderTargetImplementation::FRAME_BUFFER_OBJECT);camera->setClearMask(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);camera->setPostDrawCallBack(new FBOPostDrawCallback);camera->setRenderOrder(osg::Camera::PRE_RENDER,20);camera->setViewPort(0,0,width,height);if(tex){tex1->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);tex1->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINER_MIPMAP_NEAREST);camera->setViewPort(0,0,tex->getTextureWidth(),tex->getTextureHeight());camera->attach(buffer,tex);}int noise=4,kernelSize=8;float radius=5.f,power=3.f;///创建相机的statesetauto ss=camera->getOrcreateStateSet();ss->addUniform(new osg::Uniform("noiseTexture",0));ss->setTextureAttributeAndModes(0,createDatatexture(noiseSize,noiseSize));ss->addUniform(new osg::Uniform("postionTex",1));ss->setTextureAttributeAndModes(1,postionTex);ss->addUniform(new osg::Uniform("normalTex",2));ss->setTextureAttributeAndModes(2,normalTex);ss->addUniform(new osg::Uniform("ssaoRadius",radius));ss->addUniform(new osg::Uniform("ssaoPower",power));ss->addUniform(new osg::Uniform("kernelSize",kernelSize*kernelSize));ss->addUniform(new osg::Uniform("noiseTextureRcp",osg::Vec2(width/noiseSize,height/noiseSize)));ss->addUniform(new osg::Uniform("projMat",(osg::Matrixf)projMat));///创建采样半球随机数组auto kernelUniform=new osg::Uniform(osg::Uniform::FLOAT_VEC3,"ssaoKernel",kernelSize*kernelSize);auto kernelData=generateHemisphereSamples(kernelSize*kernelSize);for(int i=0;i<kernelSize*kernelSize;i++){kernelUniform->setElement(i,kernelData[i]);}///顶点着色器const char* vertCode=R"(#version 330layout(location = 0) in vec3 Position;layout(location = 3) in vec3 TexCoord;uniform mat4 osg_ModelViewProjectionMatrix;uniform mat4 osg_ModelViewMatrix;uniform mat4 osg_NormalMatrix;out vec2 texCoord;void main(){texCoord=TexCoord;gl_Position=osg_ModelViewProjectionMatrix*vec4(Position,1.0);})";///片段着色器const char* fragCode=R"(#version 330 coreuniform sampler2D positionTex;uniform sampler2D normalTex;uniform sampler2D noiseTex;const int MAX_KERNEL_SIZE=128;uniform vec3 ssaoKernel[MAX_KERNEL_SIZE];uniform mat4 projMatrix;uniform vec2 noiseTextureRep;uniform int kernelSize;uniform float ssaoRadius;uniform float ssaoPower;const float bias=0.0;void main(){    // 计算屏幕坐标系下像素点位置vec3 fragPos=texture2D(positionTex,texCoord).xyz;// 计算屏幕坐标系下的法线vec3 normal=normalize(texture2D(normalTex,texCoord).xyz);// 计算随机噪声向量vec3 rvec=texture2D(noiseTex,texCoord*noiseTextureRcp).xyz;//计算切线空间到屏幕空间转换矩阵vec3 tangent=normalize(rvec-dot(rvec,normal)*normal);vec3 bitangent=cross(tangent,normal);mat3 tbn=mat3(tangent,bitangent,normal);float occlusion=0.0;for(int i=0;i<kerSize;++i){//获取采样位置vec3 _sample=fragPos+(tbn*ssaoKernel[i])*ssaoRadius;//投影采样位置vec4 offset=projMatrix*vec4(_sample,1.0);offset.xyz/=offset.w;offset.xyz=offset.xyz*0.5+0.5;//获取采样深度float sampleDepth=texture2D(postionTex,offset.xy).z;float dist=abs(fragPos.z-sampleDepth);float rangeCheck=smoothstep(0.0,1.0,ssaoRadius/dist);occlusion+=rangeCheck*(sampleDepth>=_sample.z+bias?1.0:0.0);}occlusion=1.0-(occlusion/float(kernelSize));fragColor=vec4(vec3(occlusion),1.0);})";///创建四边形顶点osg::ref_ptr<osg:Vec3Array> vertices= new osg::Vec3Array;vertices->push_back(osg::Vec3(-width,-height,0.f));vertices->push_back(osg::Vec3(width,-height,0.f));vertices->push_back(osg::Vec3(width,height,0.f));vertices->push_back(osg::Vec3(width,-height,0.f));///创建四边形法线osg::ref_ptr<osg:Vec3Array> normals= new osg::Vec3Array;normals->push_back(osg::Vec3(0.0,0.0,2.f));///创建四边形纹理坐标osg::ref_ptr<osg:Vec2Array> texCoords= new osg::Vec2Array;texCoords->push_back(osg::Vec2(1.0,0.f));texCoords->push_back(osg::Vec2(0.0,0.f));texCoords->push_back(osg::Vec2(0.0,1.f));texCoords->push_back(osg::Vec2(1.0,1.f));///创建四边形几何osg::ref_ptr<osg:Geometry> quad= new osg::Geometry;quad->setVertexArray(vertices);quad->setNormalArray(normals);quad->setTexCoordArray(0,texCoords);quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS,0,4));///创建四边形节点osg::ref_ptr<osg::Geode> quadGeode=new osg::Geode;quadGeode->addDrawable(quad);osg::ref_ptr<osg::Shader> vertShader=new osg::Shader(osg::Shader::VERTEX,vertCode);osg::ref_ptr<osg::Shader> fragShader=new osg::Shader(osg::Shader::FRAGMENT,fragCode);osg::ref_ptr<osg::Program>  program=new osg::Program;program->addShader(vertShader);program->addShader(fragShader);quadGeode->getOrCreateStateSet()->setAttributeAndModes(program,OVERRIDE_ON);camera->addChild(quadGeode);camera-》setReferenceFrame(osg::Transform::ABSOLUTE_RF);camera->setProjectionMatrix(osg::Matrix::ortho2D(widht,-width,-height,height));    return camera;
}	

7、创建 blur shader 通过平均4X4周围的每个像素颜色值来避免噪声图样。

osg::ref_ptr<RttCamera> createBlurCamera(osg::Texture* colorTex,osg::Texture* ssaoTex,osg::Texture* normalTex,int width,int height)
{osg::ref_ptr<RttCamera> camera=new RttCamera(width,height);camera->setRenderTargetImplementation(osg::Camera::RenderTargetImplementation::FRAME_BUFFER_OBJECT);camera->setClearMask(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);camera->setPostDrawCallBack(new FBOPostDrawCallback);camera->setRenderOrder(osg::Camera::POST_RENDER,100);camera->setViewPort(0,0,width,height);///创建相机的statesetauto ss=camera->getOrcreateStateSet();ss->addUniform(new osg::Uniform("colorTex",0));ss->setTextureAttributeAndModes(0,colorTex);ss->addUniform(new osg::Uniform("ssaoTex",1));ss->setTextureAttributeAndModes(1,ssaoTex);ss->addUniform(new osg::Uniform("normalTex",2));ss->setTextureAttributeAndModes(2,normalTex);///顶点着色器const char* vertCode=R"(#version 330layout(location = 0) in vec3 Position;layout(location = 3) in vec3 TexCoord;uniform mat4 osg_ModelViewProjectionMatrix;uniform mat4 osg_ModelViewMatrix;uniform mat4 osg_NormalMatrix;out vec2 texCoord;void main(){texCoord=TexCoord;gl_Position=osg_ModelViewProjectionMatrix*vec4(Position,1.0);})";///片段着色器const char* fragCode=R"(#version 330 coreuniform sampler2D colorTex;uniform sampler2D ssaoTex;uniform sampler2D normalTex;const int blurSize=4;uniform mat4 osg_ModelViewMatrix;uniform float ambFactor=0.5;uniform float diffFactor=0.8;uniform float specFactor=0.15;uniform float shininess=128;vec3 calcDirLight(vec3 color,vec3 normal,float ambient,float diffuse,float specular,int strenth){float diff=max=(0,dot(normal,lightDir));float spec=pow(diff,strenth);return color*(ambient+diff*diffuse)+spec*specular;}in vec2 texCoord;out vec4 fragColor;void main(){    vec2 texelSize = 1.0 / vec2(textureSize(ssaoTex, 0));float result = 0.0;for (int i = 0; i < uBlurSize; ++i) {for (int j = 0; j < uBlurSize; ++j){vec2 offset = (vec2(-2.f) + vec2(float(x), float(y))) * texelSize;result += texture(ssaoTex, texCoord + offset).r;}}result = result / float(blurSize * blurSize);vec3 color=texture2D(colorTex,texCoord).rgb;vec3 normal=texture2D(normalTex,texCoord).rgb;vec4 viewNorm=osg_ModelViewMatrix*vec4(-normal,1.0);normal=normalize(viewNorm.xyz);vec3 lightCol=calcDirLight(color,normal,ambFactor,diffFactor,specFactor,shininess);lightCol=mix(lightCol,vec3(1.0),step(normal.z,0.0));fragCol=vec4(lightCol,1.0);})";///创建四边形顶点osg::ref_ptr<osg:Vec3Array> vertices= new osg::Vec3Array;vertices->push_back(osg::Vec3(-width,-height,0.f));vertices->push_back(osg::Vec3(width,-height,0.f));vertices->push_back(osg::Vec3(width,height,0.f));vertices->push_back(osg::Vec3(width,-height,0.f));///创建四边形法线osg::ref_ptr<osg:Vec3Array> normals= new osg::Vec3Array;normals->push_back(osg::Vec3(0.0,0.0,2.f));///创建四边形纹理坐标osg::ref_ptr<osg:Vec2Array> texCoords= new osg::Vec2Array;texCoords->push_back(osg::Vec2(1.0,0.f));texCoords->push_back(osg::Vec2(0.0,0.f));texCoords->push_back(osg::Vec2(0.0,1.f));texCoords->push_back(osg::Vec2(1.0,1.f));///创建四边形几何osg::ref_ptr<osg:Geometry> quad= new osg::Geometry;quad->setVertexArray(vertices);quad->setNormalArray(normals);quad->setTexCoordArray(0,texCoords);quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS,0,4));///创建四边形节点osg::ref_ptr<osg::Geode> quadGeode=new osg::Geode;quadGeode->addDrawable(quad);osg::ref_ptr<osg::Shader> vertShader=new osg::Shader(osg::Shader::VERTEX,vertCode);osg::ref_ptr<osg::Shader> fragShader=new osg::Shader(osg::Shader::FRAGMENT,fragCode);osg::ref_ptr<osg::Program>  program=new osg::Program;program->addShader(vertShader);program->addShader(fragShader);quadGeode->getOrCreateStateSet()->setAttributeAndModes(program,OVERRIDE_ON);camera->addChild(quadGeode);camera-》setReferenceFrame(osg::Transform::ABSOLUTE_RF);camera->setProjectionMatrix(osg::Matrix::ortho2D(widht,-width,-height,height));    return camera;
}	

8、将G_Buffer相机节点、ssao相机节点和模糊相机节点挂载到根节点

///首先创建延迟渲染pass,输出模型深度、颜色、法线纹理
auto positionTex=createColorTexture(width,height);
auto colorTex=createColorTexture(width,height);
auto normalTex=createColorTexture(width,height); 
auto pass1=createPhongcamera(osg::Camera::COLOR_BUFFER2,positionTex, osg::Camera::COLOR_BUFFER0,colorTex,osg::Camera::COLOR_BUFFER1,normalTex,width,height);
pass1->setRenderOrder(osg::Camera::PRE_RENDER,20);
pass1->setClearColor(osg::vec4(1.0,1.0,1.0,1.0));
pass1->addChild(model);
///然后创建ssao效果pass,输出ssao纹理
auto ssaoTex=createColorTexture(width,height);
auto pass2=createSSAOCamera(positionTex,normalTex,projMat,osg::Camera::COLOR_BUFFER,ssaoTex,width,height);
pass2->setRenderOrder(osg::Camera::PRE_RENDER,100);
pass2->setClearColor(osg::vec4(1.0,1.0,1.0,1.0));
///最后创建模糊效果pass
auto pass3 =createBlurCamera(colorTex,ssaoTex,normalTex,width,height);
pass3->setRenderOrder(osg::Camera::POST_RENDER,300);
pass3->setClearColor(osg::vec4(1.0,1.0,1.0,1.0));
root->addChild(pass1);
root->addChild(pass2);
root->addChild(pass3);  

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

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

相关文章

比赛获奖的武林秘籍:01 如何看待当代大学生竞赛中“卷”“祖传老项目”“找关系”的现象?

本文主要分析了大学生电子计算机类比赛中“卷”“祖传老项目”“找关系”的现象,结合自身实践经验,给出了相应的解决方案。比赛获奖的武林秘籍:01 如何看待当代大学生竞赛中“卷”“祖传老项目”“找关系”的现象? 正文 目前现状 对于大部分的比赛小白来说,对当前比赛的现…

2024.7.4 鲜花

今日推歌 natural Will you hold the line. 只有你还没有放弃。 When every one of them is giving up or giving in, tell me. 当其他所有人都停止了尝试,被挫折磨尽了希望。 In this house of mine,Nothing ever comes without a consequence or cost, tell me. 我所在之处,…

【python+selenium的web自动化】—— 控制浏览器

前言: 需本教程以Edge做测试,且谷歌、火狐等浏览器的逻辑都一样需要使用 selenium 模块操作 Edge 浏览器。 一、先通过pip install 模块 把selenium模块安装了,可以加一个中国源提升速度。pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple二、需要下载Edg…

Python自动化之控制浏览器

前言: 需本教程以Edge做测试,且谷歌、火狐等浏览器的逻辑都一样需要使用 selenium 模块操作 Edge 浏览器。 一、先通过pip install 模块 把selenium模块安装了,可以加一个中国源提升速度。pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple二、需要下载Edg…

设计模式-设计原则与设计模式总结

设计原则,是设计模式的基础。在实际开发中,并不是一定要求所有代码都遵循设计原则,我们需要综合考虑人力、时间、成本、质量,不是可以追求完美,要在设当的场景遵循合适的设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。 设计模式(Design Pattern)是前…

mirai Bot初始化配置

RT其实本来我的bot已经因为自己手贱登陆qq nt直接报废了,但是论坛里有佬提供了新的协议库,那这不赶紧复活bot都对不起这个新的协议库。 本文写于2024年7月4日19:20:21,可能随着时间久远而无法实现功能。由于存在下载障碍,所以这里也搞了个存档,本帖中的相关标星*资源无法下…

量化曲线的平滑程度

思路 1. 对原始数据一阶求导,得到一阶导数数组。 2. 对一阶导数数组求标准差。导数的标准差提供了导数值的波动性,标准差越小,曲线越平滑。 平滑曲线import numpy as np import matplotlib.pyplot as plt from matplotlib import font_manager fname="/usr/local/pytho…

Android常见错误

错误1 A problem occurred configuring root project ����ʶ��. > Could not resolve all files for configuration :classpath.> Could not resolve com.android.tools.build:gradle:8.4.0.Required by:project : > com.android.application:com.android.appli…

MyBatis中的Where标签:提升你的SQL查询效率

哈喽,大家好,我是木头左!理解MyBatis的Where标签 MyBatis是一款优秀的持久层框架,它提供了许多强大的标签来帮助编写更优雅、高效的SQL语句。其中,<where>标签是使用频率极高的一个,它能够自动处理查询条件,使得的SQL语句更加简洁和高效。在这篇文章中,将深入探讨…

Java中的JSON神器,如何轻松玩转复杂数据结构

哈喽,大家好,我是木头左!一、揭秘JSON世界的基石 在Java的世界中,JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它基于文本,易于阅读和编写,同时也易于机器解析和生成。JSON在日常开发中的应用非常广泛,无论是前后端的数据交互,还是配置文件的读取,…

《Python 第三方模块包安装指南》

在 Python 编程中,第三方模块包极大地丰富了其功能,让我们能够更高效地完成各种任务。下面将为您详细介绍如何安装 Python 的第三方模块包。 一、使用 pip 命令安装 pip 是 Python 的包管理工具,大多数情况下,我们可以通过以下命令来安装第三方模块包:pip install 模块包名…

巴图自动化Modbus转PN网关模块连智能仪表与PLC通讯

通过巴图自动化Modbus转Profinet协议网关模块,实现PLC对仪表设备的远程监控和数据传输,提高生产效率和运行稳定性。巴图自动化Modbus转Profinet协议转换BT-MDPN100网关模块的主要功能是实现Modbus协议和Profinet协议之间的转换和通信。Modbus 转 Profinet协议网关模块集成了M…

一文搞懂到底什么是 AQS

日常开发中,我们经常使用锁或者其他同步器来控制并发,那么它们的基础框架是什么呢?如何实现的同步功能呢?本文将详细用白话讲解构建锁和同步器的基础框架--AQS,并根据源码分析其原理。前言 日常开发中,我们经常使用锁或者其他同步器来控制并发,那么它们的基础框架是什么…

flutter状态管理 provider使用

provider是flutter官方推荐的状态管理插件,是基于InheritedWidget实现的。 下面我们来讲一个provider的使用方法。 1.在pubspec.yaml文件中添加 provider: ^6.1.2 开发文档:https://pub-web.flutter-io.cn/packages/provider 可以查看使用方法和最新版本号。 添加完成后…

企业数字化转型:顶层规划方法

随着数字化时代的到来,发生了以数字化、智能化为典型特征的新一轮科技革命,各行各业利用互联网、大数据、云计算、人工智能、区块链技术对传统产业进行全方位、全链条改造,实施“上云用数赋智”行动,全面推进各行业数字化转型。数字经济的大门已然开启,数字经济顶层战略规…

Nuxt3 的生命周期和钩子函数(十)

摘要:本文详细介绍了Nuxt3框架中的五个webpack钩子函数:webpack:configResolved用于在webpack配置解析后读取和修改配置;webpack:compile在编译开始前调用,可修改编译选项;webpack:compiled在编译完成后调用,可处理编译结果;webpack:change在开发模式下文件变化时触发,…

Jenkins汉化

1、Jenkins版本:版本2.426.3) Manage Jenkins->选择Plugins->切换到Availabled plugin->搜索local,然后选中安装,如下图所示 2、安装完成后重启Jenkins,汉化完成。如下图所示 像个小学生一样努力学习

模拟集成电路设计系列博客——9.1 比较器

模拟集成电路设计 9.1 比较器 比较器可能是继放大器之后第二常用的电路元件,比较器用于判断一个信号是否大于或小于零,或者比较一个信号是否大于另一个。如我们之前的章节所见,比较器在ADC中非常常用。在其他的应用中也经常出现比较器,例如数据传输,开关电源稳压器等等。 …

prufer序列

prufer序列用途: 将带标号的树用唯一的整数序列表示出来,证明凯莱公式。构造方法:每次选择一个编号最小的叶结点并删掉它,然后在序列中记录下它连接到的那个结点。重复\(n-2\)次后就只剩下两个结点,算法结束。 举个栗子(本图来自baoziwu2,侵删)显然可以有一个用堆做的方法,…

【冷启动#2】实用的springboot tutorial入门demo

跟着官方文档熟悉一遍创建spring工程的步骤 https://spring.io/guides/gs/spring-boot https://juejin.cn/post/7077958723829760008 demo简介 整个demo的预期目标是: 管理一堆玩家的数据,数据库使用的是现成的我们虚拟机上安装的MySQL 项目结构参考 主要工作:创建并熟悉spr…