cesium FBO(一)渲染到紋理(RTT)

????????一聽到三維的RTT(Render To Texture),似乎很神秘,但從底層實現一看,其實也就那樣,設計API的哪些頂級家伙已經幫你安排的明明白白了,咱們只需要學會怎么用就可以了。我認為得從WebGL入手,把整件事講明白就夠了。WebGL再下一層那是瀏覽器和硬件(顯卡)的事了,再再往下就到類似于“利用電器的物理特性”就到底了,這嚴重超出應用層該懂的范疇了。WebGL上層就是Cesium,我感覺Cesium就像是一只漂亮的大象,我就像是個盲人在摸。說的有些遠,接下來我主要闡述RTT的底層原理以及Cesium的實現,不想看原理的小伙伴可直接閱讀《三、實現》部分,等有時間了再回過頭來查看原理部分,我覺得我差不多講明白了。

一、效果

上面是渲染到Canvas,下面是渲染到紋理。

二、原理

要講原理,必須得提一嘴WebGL的常規渲染流程,Cesium是對WebGL的高級封裝,下圖是WebGL的渲染流程,同樣適用于Cesium。

我們看到畫面的渲染順序是源碼程序->著色器處理->顏色緩存區->瀏覽器這么一個過程,著色器經過數據加載、圖元裝配、柵格化、深度隱藏等處理后,將數據扔進顏色緩存區,瀏覽器自動從顏色緩存區讀取數據并顯示到Canvas上,最后我們就看到畫面,這是常規的渲染流程。如果將著色器處理好的數據不扔進顏色緩存區,而是輸出到別的地方如作為Geometry紋理圖像或Canvas的ImageData呢?

針對這個需求,WebGL引入了FBO(frame buffer object)幀緩存區對象,FBO可以用來代替顏色緩存區或深度緩存區,如下圖所示:

繪制到FBO中的數據不會直接被瀏覽器顯示出來,如何沒有后續的處理,它只是存在一塊內存中的數據而已,我們可以將這塊數據渲染到紋理(render to texture RTT),或者渲染到其他的Canvas上(離屏渲染)下一節講。如何將數據渲染到FBO里呢?我們先看看WebGL的實現:

1.新建FBO。

2.切換到FBO。

上面提到了應為WebGL默認會把數據渲染到顏色緩存區,所以得需要切換到FBO。

3.繪制

該繪制什么就繪制什么,比如想繪制一個矩形,那就去do it,這時繪制的數據就存放到FBO里面了。

4.切回顏色緩存區

如果我想繪制到瀏覽器上顯示(顏色緩存區),那就需要調用gl.bindFramebuffer(gl.FRAMEBUFFER, null);,第二個參數傳空就好了。

有了以上的基礎,那么就有了渲染到紋理的實現思路:

  1. 創建FBO。
  2. 切換到FBO。
  3. 繪制一個物體A。
  4. 切回到顏色緩存區。
  5. 繪制另一個物體B(以物體A作為紋理)。

上面的第2、3、4、5都發生在一次DC當中,沒錯,這就是WebGL指南中所給出的例子:

一次繪制的過程

WebGL運行例子效果

這個例子是將一個帶紋理的正方體作為紋理繪制到一個平面上,具體代碼實現可查《WebGL編程指南》中第10章的FramebufferObject.html例子,如沒有《WebGL編程指南》的電子書和例子代碼的小伙伴可留言,我看到了就會發給你。

這里大概介紹一下WebGL創建FBO的過程:

FBO包含了顏色關聯對象、深度關聯對象和模板關聯對象三個關聯對象,每個關聯對象又分為紋理對象和渲染緩存對象,如下圖所示他們的關系:

創建FBO的過程如下:

WebGL例子代碼

創建了一個紋理對象和一個渲染緩存區對象并指定給了FBO,此時一個FBO創建完畢并切換完成,渲染的數據就會流入的FBO中。

上面說了WebGL的實現,我們再看看Cesium的實現,大家知道Cesium是對WebGL的高級封裝,Cesium有單獨一個Framebuffer.js的文件封裝了FBO:

Framebuffer.js

import Check from "../Core/Check.js";
import defaultValue from "../Core/defaultValue.js";
import defined from "../Core/defined.js";
import destroyObject from "../Core/destroyObject.js";
import DeveloperError from "../Core/DeveloperError.js";
import PixelFormat from "../Core/PixelFormat.js";
import ContextLimits from "./ContextLimits.js";
import PixelDatatype from "./PixelDatatype.js";function attachTexture(framebuffer, attachment, texture) {const gl = framebuffer._gl;gl.framebufferTexture2D(gl.FRAMEBUFFER,attachment,texture._target,texture._texture,0);
}function attachRenderbuffer(framebuffer, attachment, renderbuffer) {const gl = framebuffer._gl;gl.framebufferRenderbuffer(gl.FRAMEBUFFER,attachment,gl.RENDERBUFFER,renderbuffer._getRenderbuffer());
}/*** Creates a framebuffer with optional initial color, depth, and stencil attachments.* Framebuffers are used for render-to-texture effects; they allow us to render to* textures in one pass, and read from it in a later pass.** @param {Object} options The initial framebuffer attachments as shown in the example below. <code>context</code> is required. The possible properties are <code>colorTextures</code>, <code>colorRenderbuffers</code>, <code>depthTexture</code>, <code>depthRenderbuffer</code>, <code>stencilRenderbuffer</code>, <code>depthStencilTexture</code>, <code>depthStencilRenderbuffer</code>, and <code>destroyAttachments</code>.** @exception {DeveloperError} Cannot have both color texture and color renderbuffer attachments.* @exception {DeveloperError} Cannot have both a depth texture and depth renderbuffer attachment.* @exception {DeveloperError} Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.* @exception {DeveloperError} Cannot have both a depth and depth-stencil renderbuffer.* @exception {DeveloperError} Cannot have both a stencil and depth-stencil renderbuffer.* @exception {DeveloperError} Cannot have both a depth and stencil renderbuffer.* @exception {DeveloperError} The color-texture pixel-format must be a color format.* @exception {DeveloperError} The depth-texture pixel-format must be DEPTH_COMPONENT.* @exception {DeveloperError} The depth-stencil-texture pixel-format must be DEPTH_STENCIL.* @exception {DeveloperError} The number of color attachments exceeds the number supported.* @exception {DeveloperError} The color-texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension.* @exception {DeveloperError} The color-texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions.** @example* // Create a framebuffer with color and depth texture attachments.* const width = context.canvas.clientWidth;* const height = context.canvas.clientHeight;* const framebuffer = new Framebuffer({*   context : context,*   colorTextures : [new Texture({*     context : context,*     width : width,*     height : height,*     pixelFormat : PixelFormat.RGBA*   })],*   depthTexture : new Texture({*     context : context,*     width : width,*     height : height,*     pixelFormat : PixelFormat.DEPTH_COMPONENT,*     pixelDatatype : PixelDatatype.UNSIGNED_SHORT*   })* });** @private* @constructor*/
function Framebuffer(options) {options = defaultValue(options, defaultValue.EMPTY_OBJECT);const context = options.context;//>>includeStart('debug', pragmas.debug);Check.defined("options.context", context);//>>includeEnd('debug');const gl = context._gl;const maximumColorAttachments = ContextLimits.maximumColorAttachments;this._gl = gl;this._framebuffer = gl.createFramebuffer();this._colorTextures = [];this._colorRenderbuffers = [];this._activeColorAttachments = [];this._depthTexture = undefined;this._depthRenderbuffer = undefined;this._stencilRenderbuffer = undefined;this._depthStencilTexture = undefined;this._depthStencilRenderbuffer = undefined;/*** When true, the framebuffer owns its attachments so they will be destroyed when* {@link Framebuffer#destroy} is called or when a new attachment is assigned* to an attachment point.** @type {Boolean}* @default true** @see Framebuffer#destroy*/this.destroyAttachments = defaultValue(options.destroyAttachments, true);// Throw if a texture and renderbuffer are attached to the same point.  This won't// cause a WebGL error (because only one will be attached), but is likely a developer error.//>>includeStart('debug', pragmas.debug);if (defined(options.colorTextures) && defined(options.colorRenderbuffers)) {throw new DeveloperError("Cannot have both color texture and color renderbuffer attachments.");}if (defined(options.depthTexture) && defined(options.depthRenderbuffer)) {throw new DeveloperError("Cannot have both a depth texture and depth renderbuffer attachment.");}if (defined(options.depthStencilTexture) &&defined(options.depthStencilRenderbuffer)) {throw new DeveloperError("Cannot have both a depth-stencil texture and depth-stencil renderbuffer attachment.");}//>>includeEnd('debug');// Avoid errors defined in Section 6.5 of the WebGL specconst depthAttachment =defined(options.depthTexture) || defined(options.depthRenderbuffer);const depthStencilAttachment =defined(options.depthStencilTexture) ||defined(options.depthStencilRenderbuffer);//>>includeStart('debug', pragmas.debug);if (depthAttachment && depthStencilAttachment) {throw new DeveloperError("Cannot have both a depth and depth-stencil attachment.");}if (defined(options.stencilRenderbuffer) && depthStencilAttachment) {throw new DeveloperError("Cannot have both a stencil and depth-stencil attachment.");}if (depthAttachment && defined(options.stencilRenderbuffer)) {throw new DeveloperError("Cannot have both a depth and stencil attachment.");}//>>includeEnd('debug');///////////////////////////////////////////////////////////////////this._bind();let texture;let renderbuffer;let i;let length;let attachmentEnum;if (defined(options.colorTextures)) {const textures = options.colorTextures;length = this._colorTextures.length = this._activeColorAttachments.length =textures.length;//>>includeStart('debug', pragmas.debug);if (length > maximumColorAttachments) {throw new DeveloperError("The number of color attachments exceeds the number supported.");}//>>includeEnd('debug');for (i = 0; i < length; ++i) {texture = textures[i];//>>includeStart('debug', pragmas.debug);if (!PixelFormat.isColorFormat(texture.pixelFormat)) {throw new DeveloperError("The color-texture pixel-format must be a color format.");}if (texture.pixelDatatype === PixelDatatype.FLOAT &&!context.colorBufferFloat) {throw new DeveloperError("The color texture pixel datatype is FLOAT and the WebGL implementation does not support the EXT_color_buffer_float or WEBGL_color_buffer_float extensions. See Context.colorBufferFloat.");}if (texture.pixelDatatype === PixelDatatype.HALF_FLOAT &&!context.colorBufferHalfFloat) {throw new DeveloperError("The color texture pixel datatype is HALF_FLOAT and the WebGL implementation does not support the EXT_color_buffer_half_float extension. See Context.colorBufferHalfFloat.");}//>>includeEnd('debug');attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;attachTexture(this, attachmentEnum, texture);this._activeColorAttachments[i] = attachmentEnum;this._colorTextures[i] = texture;}}if (defined(options.colorRenderbuffers)) {const renderbuffers = options.colorRenderbuffers;length = this._colorRenderbuffers.length = this._activeColorAttachments.length =renderbuffers.length;//>>includeStart('debug', pragmas.debug);if (length > maximumColorAttachments) {throw new DeveloperError("The number of color attachments exceeds the number supported.");}//>>includeEnd('debug');for (i = 0; i < length; ++i) {renderbuffer = renderbuffers[i];attachmentEnum = this._gl.COLOR_ATTACHMENT0 + i;attachRenderbuffer(this, attachmentEnum, renderbuffer);this._activeColorAttachments[i] = attachmentEnum;this._colorRenderbuffers[i] = renderbuffer;}}if (defined(options.depthTexture)) {texture = options.depthTexture;//>>includeStart('debug', pragmas.debug);if (texture.pixelFormat !== PixelFormat.DEPTH_COMPONENT) {throw new DeveloperError("The depth-texture pixel-format must be DEPTH_COMPONENT.");}//>>includeEnd('debug');attachTexture(this, this._gl.DEPTH_ATTACHMENT, texture);this._depthTexture = texture;}if (defined(options.depthRenderbuffer)) {renderbuffer = options.depthRenderbuffer;attachRenderbuffer(this, this._gl.DEPTH_ATTACHMENT, renderbuffer);this._depthRenderbuffer = renderbuffer;}if (defined(options.stencilRenderbuffer)) {renderbuffer = options.stencilRenderbuffer;attachRenderbuffer(this, this._gl.STENCIL_ATTACHMENT, renderbuffer);this._stencilRenderbuffer = renderbuffer;}if (defined(options.depthStencilTexture)) {texture = options.depthStencilTexture;//>>includeStart('debug', pragmas.debug);if (texture.pixelFormat !== PixelFormat.DEPTH_STENCIL) {throw new DeveloperError("The depth-stencil pixel-format must be DEPTH_STENCIL.");}//>>includeEnd('debug');attachTexture(this, this._gl.DEPTH_STENCIL_ATTACHMENT, texture);this._depthStencilTexture = texture;}if (defined(options.depthStencilRenderbuffer)) {renderbuffer = options.depthStencilRenderbuffer;attachRenderbuffer(this, this._gl.DEPTH_STENCIL_ATTACHMENT, renderbuffer);this._depthStencilRenderbuffer = renderbuffer;}this._unBind();
}Object.defineProperties(Framebuffer.prototype, {/*** The status of the framebuffer. If the status is not WebGLConstants.FRAMEBUFFER_COMPLETE,* a {@link DeveloperError} will be thrown when attempting to render to the framebuffer.* @memberof Framebuffer.prototype* @type {Number}*/status: {get: function () {this._bind();const status = this._gl.checkFramebufferStatus(this._gl.FRAMEBUFFER);this._unBind();return status;},},numberOfColorAttachments: {get: function () {return this._activeColorAttachments.length;},},depthTexture: {get: function () {return this._depthTexture;},},depthRenderbuffer: {get: function () {return this._depthRenderbuffer;},},stencilRenderbuffer: {get: function () {return this._stencilRenderbuffer;},},depthStencilTexture: {get: function () {return this._depthStencilTexture;},},depthStencilRenderbuffer: {get: function () {return this._depthStencilRenderbuffer;},},/*** True if the framebuffer has a depth attachment.  Depth attachments include* depth and depth-stencil textures, and depth and depth-stencil renderbuffers.  When* rendering to a framebuffer, a depth attachment is required for the depth test to have effect.* @memberof Framebuffer.prototype* @type {Boolean}*/hasDepthAttachment: {get: function () {return !!(this.depthTexture ||this.depthRenderbuffer ||this.depthStencilTexture ||this.depthStencilRenderbuffer);},},
});Framebuffer.prototype._bind = function () {const gl = this._gl;gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);
};Framebuffer.prototype._unBind = function () {const gl = this._gl;gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};Framebuffer.prototype.bindDraw = function () {const gl = this._gl;gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._framebuffer);
};Framebuffer.prototype.bindRead = function () {const gl = this._gl;gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._framebuffer);
};Framebuffer.prototype._getActiveColorAttachments = function () {return this._activeColorAttachments;
};Framebuffer.prototype.getColorTexture = function (index) {//>>includeStart('debug', pragmas.debug);if (!defined(index) || index < 0 || index >= this._colorTextures.length) {throw new DeveloperError("index is required, must be greater than or equal to zero and must be less than the number of color attachments.");}//>>includeEnd('debug');return this._colorTextures[index];
};Framebuffer.prototype.getColorRenderbuffer = function (index) {//>>includeStart('debug', pragmas.debug);if (!defined(index) ||index < 0 ||index >= this._colorRenderbuffers.length) {throw new DeveloperError("index is required, must be greater than or equal to zero and must be less than the number of color attachments.");}//>>includeEnd('debug');return this._colorRenderbuffers[index];
};Framebuffer.prototype.isDestroyed = function () {return false;
};Framebuffer.prototype.destroy = function () {if (this.destroyAttachments) {// If the color texture is a cube map face, it is owned by the cube map, and will not be destroyed.let i = 0;const textures = this._colorTextures;let length = textures.length;for (; i < length; ++i) {const texture = textures[i];if (defined(texture)) {texture.destroy();}}const renderbuffers = this._colorRenderbuffers;length = renderbuffers.length;for (i = 0; i < length; ++i) {const renderbuffer = renderbuffers[i];if (defined(renderbuffer)) {renderbuffer.destroy();}}this._depthTexture = this._depthTexture && this._depthTexture.destroy();this._depthRenderbuffer =this._depthRenderbuffer && this._depthRenderbuffer.destroy();this._stencilRenderbuffer =this._stencilRenderbuffer && this._stencilRenderbuffer.destroy();this._depthStencilTexture =this._depthStencilTexture && this._depthStencilTexture.destroy();this._depthStencilRenderbuffer =this._depthStencilRenderbuffer &&this._depthStencilRenderbuffer.destroy();}this._gl.deleteFramebuffer(this._framebuffer);return destroyObject(this);
};
export default Framebuffer;

這源碼有點長,我們只看構造函數,一開始定義了

顏色的紋理對象和渲染緩存區對象

深度的紋理對象和渲染緩存區對象

模板的紋理對象和渲染緩存區對象

看的出來這和WebGL的FBO的簡創建過程相呼應,繼續往下看發現在一堆數據的校驗過后切換到FBO:

可見FBO切換的時機在Framebuffer的構造中就完成了,再往下看發現后面多次調用下面這個兩個函數為FBO的顏色關聯對象和深度關聯對象的紋理對象和渲染緩存區對象指定對象:

當我們簡單的?new Cesium.Framebuffer({...})的時候,就完成了上文提交的WebGL中的FBO創建的過程。

到這里關于底層的原理部分就寫完了,讓我喘口氣,又是截圖又是配解說詞的,快冒煙了(-:

三、實現

上面的原理解說,我們再來簡單看看Cesium是如何來完成上面這個過程的,Cesium渲染到紋理分為以下四個步驟:

1、創建幾何體

這里我創建一個矩形primitive,用于展示FBO渲染到紋理的效果

      let geometryInstance = new Cesium.GeometryInstance({geometry: new Cesium.RectangleGeometry({rectangle: Cesium.Rectangle.fromDegrees(120.0,30.0,125.0,33.0),height: 1000,}),attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE),},});var appearance = new Cesium.MaterialAppearance({material: new Cesium.Material({fabric: {uniforms: {textureRTT: "/data/300.png",},source: `czm_material czm_getMaterial(czm_materialInput materialInput) {czm_material material = czm_getDefaultMaterial(materialInput);vec2 v_st = materialInput.st;vec4 textColor = texture2D(textureRTT, v_st);material.diffuse =textColor.rgb;material.alpha = 1.0;return material;}`,},translucent: true}),translucent: true,flat: true,faceForward: false});this.viewer.scene.primitives.add(new Cesium.Primitive({geometryInstances: geometryInstance,appearance: appearance,}));

2、創建FBO

    createFrameBuffer(context) {let framebuffer = new Cesium.Framebuffer({context: context,colorTextures: [new Cesium.Texture({context: context,width: context.drawingBufferWidth,height: context.drawingBufferHeight,pixelFormat: Cesium.PixelFormat.RGBA,}),]});return framebuffer;}

3、渲染到FBO

renderToFbo(fbo, scene) {const frameState = scene._frameState;const context = scene.context;const us = context.uniformState;const view = scene._defaultView;scene._view = view;scene.updateFrameState();frameState.passes.render = true;frameState.passes.postProcess = scene.postProcessStages.hasSelected;frameState.tilesetPassState = scene.renderTilesetPassState;let backgroundColor = Cesium.defaultValue(scene.backgroundColor, Cesium.Color.BLACK);if (scene._hdr) {backgroundColor = Cesium.Color.clone(backgroundColor, scene.scratchBackgroundColor);backgroundColor.red = Cesium.Math.pow(backgroundColor.red, scene.gamma);backgroundColor.green = Cesium.Math.pow(backgroundColor.green, scene.gamma);backgroundColor.blue = Cesium.Math.pow(backgroundColor.blue, scene.gamma);}frameState.backgroundColor = backgroundColor;frameState.atmosphere = scene.atmosphere;scene.fog.update(frameState);us.update(frameState);const shadowMap = scene.shadowMap;if (Cesium.defined(shadowMap) && shadowMap.enabled) {if (!Cesium.defined(scene.light) || scene.light instanceof Cesium.SunLight) {// Negate the sun direction so that it is from the Sun, not to the SunCesium.Cartesian3.negate(us.sunDirectionWC, scene._shadowMapCamera.direction);} else {Cesium.Cartesian3.clone(scene.light.direction, scene._shadowMapCamera.direction);}frameState.shadowMaps.push(shadowMap);}scene._computeCommandList.length = 0;scene._overlayCommandList.length = 0;const viewport = view.viewport;viewport.x = 0;viewport.y = 0;viewport.width = context.drawingBufferWidth;viewport.height = context.drawingBufferHeight;const passState = view.passState;passState.framebuffer = fbo;passState.blendingEnabled = undefined;passState.scissorTest = undefined;passState.viewport = Cesium.BoundingRectangle.clone(viewport, passState.viewport);if (Cesium.defined(scene.globe)) {scene.globe.beginFrame(frameState);}scene.updateEnvironment();scene.updateAndExecuteCommands(passState, backgroundColor);scene.resolveFramebuffers(passState);passState.framebuffer = undefined;//executeOverlayCommands(scene, passState);if (Cesium.defined(scene.globe)) {scene.globe.endFrame(frameState);if (!scene.globe.tilesLoaded) {scene._renderRequested = true;}}context.endFrame();scene.globe.show = true;}

4、將FBO作為紋理賦值給幾何體

        let colorTexture = Cesium.Texture.fromFramebuffer({context: this.viewer.scene.context,framebuffer: fbo})appearance.material.uniforms.textureRTT = colorTexture;

通過上面四個步驟,最終在矩形幾何體上顯示出了當前場景,我們看到任何東西,不管它如何的復雜、如何的高深莫測,它都是離不開核心的東西,所謂萬變不離其宗,Cesium是基于WebGL封裝的,所以它再怎么封裝、再怎么抽象都離不開底層原理的。Cesium作為一款出色的大象,我們得慢慢的摸它。

如沒有《WebGL編程指南》的電子書和例子代碼同學請留言,我看到了會第一時間發給你。

碼字不易,請各位看官點個贊+關注,我會持續分享一些實用的東西

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93997.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93997.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93997.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PNP機器人機器人學術年會展示靈巧手動作捕捉方案。

2025年8月1-3日&#xff0c;第六屆中國機器人學術年會&#xff08;CCRS2025&#xff09;在長沙國際會議中心舉行&#xff0c;主題“人機共融&#xff0c;智向未來”。PNP機器人與靈巧智能聯合展出最新靈巧手模仿學習方案&#xff1a;基于少量示教數據即可快速復現復雜抓取動作&…

【45】C#入門到精通——C#調用C/C++生成動態庫.dll及C++ 生成動態庫.dll ,DllImport()方式導入 C++動態庫.dll方法總結

文章目錄1 C 生成動態庫.dll2 C#調用C/C生成動態庫.dll2.1 [DllImport()] 方式導入 C動態庫.dll2.2 調用測試3 C/C 生成通用dll,改進3.1改進后.h3.2 .cpp3.2 C# 調用4 [DllImport()] 方式導入C生成的 .dll 總結4.1 指定路徑導入4.2 .dll放在 執行目錄下&#xff08;一定要放對&…

從協議棧到ath12k_mac_op_tx的完整調用路徑

文章目錄 從協議棧到ath12k_mac_op_tx的完整調用路徑 1. 整體架構概覽 2. 詳細調用路徑分析 2.1 應用層到Socket層 2.2 協議層處理 2.3 網絡設備層到mac80211 2.4 mac80211發送入口 2.5 mac80211核心發送處理 2.6 mac80211發送核心處理 2.7 mac80211發送調度 2.8 最終驅動調用 …

WPFC#超市管理系統(4)入庫管理

入庫管理7. 商品入庫管理7.2 入庫實現顯示名稱、圖片、單位7.3 界面設計7.3 功能實現7. 商品入庫管理 數據庫中StockRecord表需要增加商品出入庫Type類型為nvarchar(50)。C#中的數據庫重新同步StockRecord表在Entity→Model中新建枚舉類型StockType namespace 超市管理系統.E…

CSS 打字特效

效果圖.wxml <view class"tips"><text>{{ tipsText }}</text><text class"tips-line">|</text> </view>.wxss .tips{padding: 50rpx 100rpx;font-size: 28rpx; } .tips-line{color: #ccc;animation: tips-line .5s al…

直播小程序 app 系統架構分析

一、引言 直播行業近年來發展迅猛&#xff0c;直播小程序和 APP 成為眾多用戶獲取直播內容以及主播進行內容輸出的重要平臺。一個完善且高效的系統架構是支撐直播業務穩定運行、提供優質用戶體驗的關鍵。本文將詳細剖析直播小程序 / APP 的系統架構&#xff0c;包括整體架構設計…

Vue常見題目

1. 什么是 Vue.js&#xff1f;它的核心特點是什么&#xff1f; Vue.js 是一個漸進式 JavaScript 框架&#xff0c;用于構建用戶界面。它的核心特點包括&#xff1a; - 響應式數據綁定 - 組件化開發 - 虛擬 DOM - 指令系統 - 輕量級且易于集成 - 豐富的生態系統&#xff08;Vue…

ipynb文件直接發布csdn

第一步&#xff0c;下載markdown文件 file --> save and export notebook as --> markdown第二步&#xff0c;導入markdown文件 進入csdn發布文章界面&#xff0c;點擊導入&#xff0c;選擇第一步下載的markdown文件即可

廣東省省考備考(第六十四天8.2)——判斷推理(重點回顧)

判斷推理&#xff1a;數量規律 錯題解析解析解析解析解析解析解析標記題解析解析解析解析解析解析解析今日題目正確率&#xff1a;53% 判斷推理&#xff1a;屬性規律 錯題解析解析解析解析解析解析標記題解析解析今日題目正確率&#xff1a;60%

【C++/STL】vector的OJ,深度剖析和模擬實現

vector在OJ中的使用 1.只出現一次的數字 class Solution { public:int singleNumber(vector<int>& nums) {int value 0;for(auto e : v) {value ^ e; }return value;} };2.楊輝三角 class Solution { public:vector<vector<int>> generate(int numRow…

衡石湖倉一體架構深度解構:統一元數據層如何破除數據孤島?

一、數據融合的世紀難題典型困境二、衡石統一元數據層設計架構核心關鍵技術實現智能元數據發現自動構建跨源血緣關系動態查詢重寫 將標準SQL翻譯為最優執行計劃text Original: SELECT SUM(sales) FROM virtual_view Rewritten: [S3] SELECT SUM(amount) FROM crm_sales [My…

Windows 下 fping 指令使用指南

fping 作為一款強大的網絡工具&#xff0c;能夠同時向多個主機發送 ICMP 回聲請求&#xff0c;相較于傳統的 ping 命令&#xff0c;在處理大量主機時具有顯著優勢。 一、fping 簡介? fping 是 “fast pinger” 的縮寫&#xff0c;它可以向一系列 IP 地址發送 ICMP 回聲請求。…

代碼隨想錄day52圖論3

文章目錄101. 孤島的總面積102. 沉沒孤島103. 水流問題104.建造最大島嶼101. 孤島的總面積 題目鏈接 文章講解 #include<bits/stdc.h> using namespace std;int ans 0; // 記錄不與邊界相連的孤島數量 int sum 0; // 當前孤島的面積 bool flag false; /…

linux pip/conda 修改默認cache位置

1 pip pip cache默認在/home/{username}目錄下&#xff0c;容易導致系統盤寫滿報錯。查看pip cache位置pip cache dir假設移動pip cache目錄到 /data/.cache/pip/cache&#xff0c;命令如下pip config set global.cache-dir /data/.cache/pip/cache2 conda 查看conda緩存位置c…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘seaborn’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘seaborn’問題 一、摘要 在使用 PyCharm 終端進行模塊安裝時&#xff0c;常常會遇到如下異常&#xff1a; ModuleNotFoundError: No module named ‘seaborn’…

(思維)洛谷 P13551 ももいろの鍵 題解

題意 愛莉給了你一個非負整數 nnn&#xff0c;你需要把 0,1,2,…,n0, 1, 2, \dots, n0,1,2,…,n 劃分成若干組&#xff0c;滿足每一組的按位與為 000。 劃分的組不需要相鄰。 你需要最大化劃分組數并給出方案。 1≤T≤6001 \le T \le 6001≤T≤600&#xff0c;0≤n≤1050 \le n…

記錄一次ESP32報錯Guru Meditation Error: Core 1 panic‘ed (Double exception).

一、問題描述 需求&#xff1a; ESP32S3單片機&#xff0c;連接一個麥克風讀取5s后&#xff0c;編碼后發送到百度云進行語音識別。通過freertos框架&#xff0c;將任務放在核1中運行&#xff08;放在核0同樣報錯&#xff09; 問題&#xff1a; 在最后的發送語音數據中&#xff…

半導體物理復習

半導體物理導論第一章 半導體的電子狀態

vi/vim跳轉到指定行命令

在 vi/vim 中跳轉到指定行有多種高效方法&#xff0c;以下是最常用的操作方式&#xff1a; 一、基礎跳轉&#xff1a;行號 命令命令模式下直接輸入行號 按 Esc 切換到命令模式后&#xff0c;輸入 :行號 并回車。例如&#xff0c;輸入 :100 會直接跳轉到第 100 行。使用 G 快捷…

智能落地扇方案:青稞RISC-V電機 MCU一覽

在科技飛速發展的今天&#xff0c;智能家居已成為人們生活中不可或缺的一部分&#xff0c;而風扇作為夏日解暑的必備家電&#xff0c;其智能化升級也成為了行業發展的必然趨勢。傳統落地扇功能單一、操作不便&#xff0c;已難以滿足現代消費者對便捷、舒適、節能生活的追求。在…