Metal學習筆記七:片元函數

知道如何通過將頂點數據發送到 vertex 函數來渲染三角形、線條和點是一項非常巧妙的技能 — 尤其是因為您能夠使用簡單的單行片段函數為形狀著色。但是,片段著色器能夠執行更多操作。

? 打開網站 https://shadertoy.com,在那里您會發現大量令人眼花繚亂的社區創建的出色著色器。

這些示例可能看起來像復雜 3D 模型的渲染圖,但外觀具有欺騙性!您在此處看到的每個 “模型” 都是完全使用數學生成的,用 GLSL 片段著色器編寫。GLSL 是 OpenGL 的圖形庫著色語言 — 在本章中,您將開始了解所有著色高手使用的原理。

注意:每個圖形API都使用自己的著色器語言。原理是相同的,因此,如果您找到喜歡的GLSL著色器,則可以使用Metal MSL重新創建它。

起始項目

Starter 項目展示了一個示例,該示例將多個管線狀態與不同的頂點函數結合使用,具體取決于您渲染的是旋轉的火車還是全屏四邊形。
? 打開本章的入門項目。
? 構建并運行項目。(您可以選擇渲染火車或四邊形。您將先從四邊形開始。)
讓我們仔細看看代碼。
? 打開 Shaders 組中的 Vertex.metal,您將看到兩個頂點函數:
? vertex_main:此函數將呈現火車,就像在上一章中所做的那樣。
? vertex_quad:此函數使用著色器中定義的數組渲染全屏四邊形。

這兩個函數都輸出一個 VertexOut結構體,其中僅包含頂點的位置。

? 打開 Renderer.swift。
在 init(metalView:options:) 中,您將看到兩個管線狀態對象 (PSO)。兩個 PSO 之間的唯一區別是 GPU 在繪制時將調用的頂點函數。
根據 options.renderChoice 的值,draw(in:) 渲染火車模型或四邊形,并換入正確的管線狀態。SwiftUI 視圖處理 Options 的更新,而 MetalViewRepresentable 將當前選項傳遞給 Renderer。
? 在繼續之前,請確保您了解此項目的運作方式。

屏幕空間

片段函數可以執行的許多操作之一是創建復雜的模式,這些模式用來填充呈現的四邊形上的屏幕像素。目前,片段函數只有 vertex 函數的插值position輸出可供其使用。因此,首先,您將了解您可以利用此position做什么以及它的局限性是什么。
? 打開 Fragment.metal,將 fragment 函數內容改為:

float color;
in.position.x < 200 ? color = 0 : color = 1;
return float4(color, color, color, 1);

當光柵器處理頂點位置時,它會將它們從 NDC(標準化設備坐標)轉換為屏幕空間。您在 ContentView.swift 中將 Metal 視圖的寬度定義為 400點。使用新添加的代碼,您說如果 x 位置小于 200,則將顏色設為黑色。否則,將顏色設為白色。


注意:雖然您可以使用 if 語句,但編譯器可以更好地優化三元語句,因此使用它更有意義。

? 在您的 Mac 和 iPhone 15 Pro Max 模擬器上構建并運行該應用程序。


您是否預料到一半的屏幕是黑色的?視圖的寬是 400 點,所以這是合理的。但是您可能沒有考慮到一些事情:Apple Retina 顯示屏具有不同的像素分辨率或像素密度。例如,MacBook Pro 配備 2 倍 Retina 顯示屏,而 iPhone 15 Pro Max 配備 3 倍 Retina 顯示屏。這些不同的顯示屏意味著 MacBook Pro 上的 400 點, Metal 視圖可創建 800x800 像素的可繪制紋理,而 iPhone 視圖可創建 1200x1200 像素的可繪制紋理。

您的四邊形填滿了屏幕,您正在寫入視圖的可繪制渲染目標紋理(其大小與設備的顯示屏相匹配),但沒有簡單的方法可以在 fragment 函數中找出當前渲染目標紋理的大小。
? 打開 Common.h,并添加新的結構體:

typedef struct {uint width;uint height;
} Params;

此代碼包含可發送到 fragment 函數的參數。您可以根據需要向此結構體添加參數。
? 打開 Renderer.swift,并向 Renderer 添加一個新屬性:

var params = Params()

您將把當前渲染目標大小存儲在新屬性中。
? 將以下代碼添加到 mtkView(_:drawableSizeWillChange:) 的末尾:

 params.width = UInt32(size.width)
params.height = UInt32(size.height)

size 包含視圖的可繪制紋理大小。換句話說,也就是視圖的bounds按設備的比例因子進行縮放后的尺寸。
? 在 draw(in:)中調用渲染模型或四邊形的方法之前,將參數發送到 fragment 函數:

renderEncoder.setFragmentBytes(&params,length: MemoryLayout<Params>.stride,index: 12)

請注意,您使用 setFragmentBytes(_:length:index:)將數據發送到片段函數的方式與之前使用 setVertexBytes(_:length:index:)的方式相同。
? 打開 Fragment.metal,將 fragment_main 的簽名更改為:

 fragment float4 fragment_main(constant Params &params [[buffer(12)]],VertexOut in [[stage_in]])

具有目標繪圖紋理大小的參數現在可用于 fragment 函數。
? 將設置 color 值的代碼(基于 in.position.x 的值)更改為:

   in.position.x < params.width * 0.5 ? color = 0 : color = 1;

在這里,您將使用目標渲染大小進行計算。
? 在 macOS 和 iPhone 15 Pro Max 模擬器中運行該應用程序。

太棒了,現在兩種設備的渲染看起來都一樣。

Metal標準庫函數

除了標準的數學函數(如 sin、abs 和 length)之外,還有一些其他有用的函數。讓我們來看看:

step

如果 x 小于 edge,則 step(edge, x) 返回 0。否則,它將返回 1。此評估正是您對當前 fragment 函數執行的操作。

? 將 fragment 函數的內容替換為:

 float color = step(params.width * 0.5, in.position.x);
return float4(color, color, color, 1);

此代碼生成的結果與以前相同,但代碼略少。

? 構建并運行。


結果是,左側為黑色,因為左側 step 的結果為 0。而右側為白色,因為右側step 的結果為 1 。

讓我們用棋盤格模式更進一步。
? 將 fragment 函數的內容替換為:

uint checks = 8;
// 1
float2 uv = in.position.xy / params.width;
// 2
uv = fract(uv * checks * 0.5) - 0.5;
// 3
float3 color = step(uv.x * uv.y, 0.0);
return float4(color, 1.0);


以下是正在發生的事情:
1. UV 坐標形成一個值介于 0 和 1 之間的網格。因此,中點位于 [0.5, 0.5],左上角位于 [0.0, 0.0]。UV 坐標通常與將頂點映射到紋理相關聯,如第 8 章 “紋理”所示。
2. fract(x)返回 x 的小數部分。將 UV 的小數值乘以checks值的一半,得到一個介于 0 和 1 之間的值。然后減去 0.5,使一半的值小于零。
3. 如果 xy 乘法的結果小于零,則結果為 1 或白色。否則,它是 0 或黑色。
例如:

float2 uv = (550, 50) / 800;     // uv = (0.6875, 0.0625)
uv = fract(uv * checks * 0.5);   // uv = (0.75, 0.25)
uv -= 0.5; // uv = (0.25, -0.25)
float3 color = step(uv.x * uv.y, 0.0); // x > -0.0625, so color
is 1

? 構建并運行應用程序。

length

創建正方形很有趣,但讓我們使用 length 函數創建一些圓。

? 將 fragment 函數替換為:

float center = 0.5;
float radius = 0.2;
float2 uv = in.position.xy / params.width - center;
float3 color = step(length(uv), radius);
return float4(color, 1.0);


? 構建并運行應用程序。

要調整形狀大小并在屏幕上移動形狀,請更改圓的中心和半徑。

smoothstep

smoothstep(edge0, edge1, x)返回介于 0 和 1 之間的平滑艾米插值。
?注意:edge1 必須大于 edge0,x 應該是 edge0 <= x <= edge1。

? 將片段函數改為:

 float color = smoothstep(0, params.width, in.position.x);
return float4(color, color, color, 1);

color 包含介于 0 和 1 之間的值。當位置與屏幕寬度相同時,顏色為 0 或白色。當位置位于屏幕的最左側時,顏色為 0 或黑色。
? 構建并運行應用程序。



在兩種邊緣情況之間,顏色是在黑色和白色之間插值的漸變。在這里,您使用 smoothstep 來計算顏色,但您也可以使用它在任意兩個值之間進行插值。例如,您可以使用 smoothstep 為 vertex 函數中的位置設置動畫。


mix

mix(x, y, a)產生與 x + (y - x) * a 相同的結果。

? 將片段函數更改為:

float3 red = float3(1, 0, 0);
float3 blue = float3(0, 0, 1);
float3 color = mix(red, blue, 0.6);
return float4(color, 1);


混合 0 將產生全紅色。混合 1 產生全藍色。這些顏色共同產生 60% 的紅色和藍色混合。

? 構建并運行應用程序。
?
您可以將混合與 smoothstep 結合使用以產生顏色漸變。

? 將 fragment 函數替換為:

float3 red = float3(1, 0, 0);
float3 blue = float3(0, 0, 1);
float result = smoothstep(0, params.width, in.position.x);
float3 color = mix(red, blue, result);
return float4(color, 1);

此代碼使用result的插值,將其用作紅色和藍色的混合比例。

? 構建并運行應用程序。

normalize

規范化過程是指重新調整數據比例以使用標準范圍。例如,向量同時具有 direction 和 magnitude。在下圖中,向量 A 的長度為 2.12132,方向為 45 度。向量 B 的長度相同,但方向不同。向量 C 的長度不同,但方向相同。

如果兩個向量的大小相同,則更容易比較它們的方向,因此可以將向量標準化為單位長度。normalize(x)返回方向相同但長度為 1 的向量 x。
讓我們看看另一個規范化的例子。假設您希望使用顏色可視化頂點位置,以便更好地調試某些代碼。
? 將片段函數改為:

return in.position;

? 構建并運行應用程序。

片段函數應返回每個元素介于 0 和 1 之間的 RGBA 顏色。但是,由于位置位于屏幕空間中,因此每個位置在 [0, 0, 0] 和 [800, 800, 0] 之間變化,這就是四邊形呈現黃色的原因(它僅在左上角位于 0 和 1 之間)。
? 現在,將代碼更改為:

 float3 color = normalize(in.position.xyz);
return float4(color, 1);

在這里,您將向量 in.position.xyz 標準化為長度為 1。現在,所有顏色都保證介于 0 和 1 之間。歸一化后,最右上角的位置 (800, 0, 0) 包含紅色的 1, 0, 0。
? 構建并運行應用程序以查看結果。

法線

盡管可視化位置有助于調試,但通常對創建 3D 渲染沒有幫助。但是,找到三角形的朝向對于著色很有用,而著色器正是法線發揮作用的地方。法線是表示頂點或表面朝向的向量。在下一章中,您將學習如何為模型增加光照。但首先,您需要了解法線。

從 Blender 捕獲的以下圖像顯示了指向的頂點法線。球體的每個頂點都指向不同的方向。
?
球體的著色取決于這些法線。如果法線指向光源,則 Blender 將更亮。
四邊形對于著色目的不是很有趣,因此請將默認渲染切換到火車。
? 打開 Options.swift,并將 renderChoice 的初始化更改為:

var renderChoice = RenderChoice.train

? 運行應用程序以檢查您的火車渲染。
?
與全屏四邊形不同,只有火車覆蓋的片段才會顯示。但是,每個片段的顏色仍然取決于片元的屏幕位置,而不是火車頂點的位置。

加載帶法線的火車模型

3D模型文件通常包含表面法線值,您可以和模型一起加載這些值。如果您的文件不包含Surface Formals,則Model I/O可以使用MDLMesh的addNormals(withAttributeNamed:creaseThreshold:),在導入時生成它們。

為頂點描述器增加法線

? 打開 VertexDescriptor.swift。
目前,您只加載 position 屬性。是時候將 normal 添加到頂點描述符。
? 在設置 offset 的代碼之后,在設置 layouts[0] 的代碼之前,將以下代碼添加到 MDLVertexDescriptor 的 defaultLayout:

vertexDescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeNormal,format: .float3,offset: offset,bufferIndex: 0)
offset += MemoryLayout<float3>.stride

這里,法線類型是 float3,并在緩沖區 0 中和position交錯放置。float3 是在 MathLibrary.swift 中定義的 SIMD3<Float> 類型的別名。每個頂點在索引0緩沖區中占用兩個 float3,即 32 字節。layouts[0] 描述帶有 stride 的索引0緩沖區。

更新 Shader 函數

? 打開 Vertex.metal。
火車模型的管線狀態使用此頂點描述符,以便頂點函數可以處理屬性,并將這些屬性與 VertexIn中的屬性匹配。
? 構建并運行應用程序,您會發現一切仍然按預期工作。即使您向頂點緩沖區添加了新屬性,管線也會忽略它。
因為您尚未將其作為attribute(n)包含在 VertexIn 中。是時候解決這個問題了。

? 在 VertexIn 中添加以下代碼:

float3 normal [[attribute(1)]];

在這里,您將 attribute(1) 與頂點描述符的屬性 1 匹配。現在你將能夠訪問 vertex 函數中的 normal 屬性。
? 接下來,將以下代碼添加到 VertexOut 中:

float3 normal;

通過在此處包含 normal,您現在可以將數據傳遞給 fragment 函數。
? 在 vertex_main 中,將賦值更改為 out:

VertexOut out {.position = position,.normal = in.normal
};


完美!通過該更改,您現在可以從 vertex 函數返回位置和法線。
? 打開 Fragment.metal,將 fragment_main 的內容替換為:

return float4(in.normal, 1);

別擔心,編譯錯誤是意料之中的。即使您在 Vertex.metal 中更新了 VertexOut,該結構體的作用域也僅在該文件中。


添加頭文件

在多個著色器文件中需要結構體和函數是很常見的。因此,就像您對 Swift 和 Metal 之間的橋接頭文件 Common.h 所做的那樣,您可以添加其他頭文件并將它們導入到著色器文件中。
? 使用 macOS 頭文件模板在 Shaders 組中創建一個新文件,并將其命名為 ShaderDefs.h。
? 將代碼替換為:
?

#include <metal_stdlib>
using namespace metal;
struct VertexOut {float4 position [[position]];float3 normal;
};

在這里,您可以在 metal 命名空間中定義 VertexOut。

? 打開 Vertex.metal,并刪除 VertexOut 結構。

? 導入 Common.h 后,添加:

   #import "ShaderDefs.h"

? 打開 Fragment.metal,并刪除 VertexOut 結構。

? 同樣,在導入 Common.h 后,添加:

#import "ShaderDefs.h"

? 構建并運行應用程序。
哦,現在看起來有點奇怪!

您的法線看起來好像顯示正確 — 紅色法線位于火車的右側,綠色法線向上,藍色位于后面 — 但隨著火車旋轉,它的某些部分看起來幾乎是透明的。
這里的問題是光柵器會混淆頂點的深度順序。當你從前面看火車時,你不應該能看到火車的后面;它應該被遮擋。

深度

光柵器默認情況下不會處理深度順序,因此您需要以深度模板狀態為光柵器提供所需的信息。
您可能還記得第3章“渲染管道”,模板測試單元檢查渲染管道期間片段是否可見。如果確定片段在另一個片段后面,則將其丟棄。
讓我們給渲染編碼器一個MTLDepthStencilState屬性,以描述如何進行此測試。
?打開Renderer.swift。
?在init(metalView:options:)結束之前,設置metalView.clearColor之后,添加:

metalView.depthStencilPixelFormat = .depth32Float

該代碼告訴Metal View,您需要保留深度信息。默認的像素格式為.invalid,它告知視圖不需要創建深度和模板紋理。
渲染命令編碼器使用的管線狀態必須具有相同的深度像素格式。
?在init(metalView:options:)設置PipelinedEscriptor.colorattachments [0] .pixelformat之后,在do {之前添加:

   pipelineDescriptor.depthAttachmentPixelFormat = .depth32Float

如果您現在要構建并運行該應用程序,那么您將獲得與以前相同的結果。但是,在幕后,視圖創建了紋理,光柵器可以在該紋理上寫入深度值。
接下來,您需要設置希望光柵器計算深度值的方式。?

?向渲染器添加新屬性:

let depthStencilState: MTLDepthStencilState?

該屬性具有正確的渲染設置,使其具有深度模板狀態。

? 在 Renderer 中創建此方法以實例化深度模板狀態:

static func buildDepthStencilState() -> MTLDepthStencilState? {
// 1let descriptor = MTLDepthStencilDescriptor()
// 2descriptor.depthCompareFunction = .less
// 3descriptor.isDepthWriteEnabled = truereturn Renderer.device.makeDepthStencilState(descriptor: descriptor)
}

瀏覽這段代碼:
1. 創建一個描述符,用于初始化深度模板狀態,就像您對管道狀態對象所做的那樣。
2. 指定如何比較當前和已處理的片段。使用 compare 函數 less 時,如果當前片段深度小于幀緩沖區中前一個片段的深度,則當前片段將替換前一個片段。
3. 說明是否寫入深度值。如果您有多個通道,如第 12 章 “渲染通道”中所述,有時您需要讀取已繪制的片段。在這種情況下,請將 isDepthWriteEnabled 設置為 false。請注意,當您繪制需要深度的對象時,isDepthWriteEnabled 始終為 true。
? 在 super.init() 之前從 init(metalView:options:) 調用方法:

depthStencilState = Renderer.buildDepthStencilState()

? 在 draw(in:) 中,將以下內容添加到方法頂部的 guard { } 之后:

renderEncoder.setDepthStencilState(depthStencilState)

? 構建并運行應用程序,以光彩奪目的 3D 形式查看您的火車。

當火車旋轉時,它會以紅色、綠色、藍色和黑色的陰影出現。

考慮一下你在這個渲染中看到的內容。法線當前位于對象空間中。因此,即使火車在世界空間中旋轉,顏色/法線也不會隨著模型旋轉的改變而改變。

當法線沿模型的 x 軸指向右側時,值為 [1, 0, 0]。這與 RGB 值中的紅色相同,因此對于指向右側的法線,片段為紅色。
指向上方的法線在 y 軸上為 1,因此顏色為綠色。

指向攝像機的法線為負數。當顏色為 [0, 0, 0] 或更小時,它們為黑色。當你看到火車旋轉的后部時,你可以看出指向 z 方向的車輪后部是藍色的 [0, 0, 1]。
現在,您在 fragment 函數中擁有了法線,您可以根據顏色的朝向開始操作顏色。當您開始使用光照時,操縱顏色非常重要。

半球光照

半球照明使用環境光。使用這種類型的照明,場景的一半使用一種顏色照明,另一半使用另一種顏色照明。例如,下圖中的球體使用半球照明。

請注意球體如何呈現從天空反射的顏色(頂部)和從地面反射的顏色(底部)。要查看這種類型的光照效果,您需要更改 fragment 函數,以便:
? 朝上的法線為藍色。
? 朝下的法線為綠色。
? 過渡值為藍色和綠色混合。

? 打開 Fragment.metal,并將 fragment_main 的內容替換為:

float4 sky = float4(0.34, 0.9, 1.0, 1.0);
float4 earth = float4(0.29, 0.58, 0.2, 1.0);
float intensity = in.normal.y * 0.5 + 0.5;
return mix(earth, sky, intensity);


mix(x, y, z) 根據第三個值在前兩個值之間進行插值,第三個值必須介于 0 和 1 之間。您的正常值介于 -1 和 1 之間,因此您可以在 0 和 1 之間轉換強度。
? 構建并運行應用程序以查看您閃亮的火車。請注意,火車的頂部是藍色的,而它的底部是綠色的。

片段著色器非常強大,允許您精確地為對象著色。在第 10 章 “光照基礎知識”中,您將使用法線的力量為場景提供更逼真的光照著色。在第19章“鑲嵌與地形”中,你將創建一個與此類似的效果,學習如何根據坡度在地形上放置雪。

挑戰

目前,您正在對所有緩沖區索引和屬性使用硬編碼的魔數。隨著應用程序的增長,跟蹤這些數字將變得越來越困難。所以,你在本章中的挑戰是尋找所有這些神奇的數字,并為它們起一個令人難忘的名字。對于此挑戰,您將在 Common.h 中創建一個枚舉。
以下是一些可幫助您入門的代碼:
?

typedef enum {VertexBuffer = 0,UniformsBuffer = 11,ParamsBuffer = 12
} BufferIndices;

現在,您可以在 Swift 和 C++ 著色器函數中使用這些常量:?

//Swift
encoder.setVertexBytes(&uniforms,length: MemoryLayout<Uniforms>.stride,index: Int(UniformsBuffer.rawValue))
// Shader Function
vertex VertexOut vertex_main(const VertexIn in [[stage_in]],constant Uniforms &uniforms [[buffer(UniformsBuffer)]])

您甚至可以在 VertexDescriptor.swift 中添加擴展來美化代碼:

extension BufferIndices {var index: Int {return Int(self.rawValue)}
}

使用此代碼,您可以使用 UniformsBuffer.index 而不是 Int(UniformsBuffer.rawValue)。
您可以在本章的 challenge 文件夾中找到完整的解決方案。

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

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

相關文章

騰訊云 | 微搭低代碼快速開發數據表單應用

如上所示&#xff0c;登錄騰訊云微搭低代碼業務控制臺&#xff0c;開始新創建一個應用&#xff0c;創建應用的方式包括&#xff0c;根據實際的業務需求&#xff0c;從模版列表中選擇一個模板填入數據模型創建新應用&#xff0c;使用微搭組件自主設計數據模型創建新應用&#xf…

儲油自動化革命,網關PROFINET與MODBUS網橋的無縫融合,錦上添花

儲油行業作為能源供應鏈的關鍵環節&#xff0c;其自動化和監控系統的可靠性和效率至關重要。隨著工業4.0的推進&#xff0c;儲油設施越來越多地采用先進的自動化技術以提高安全性、降低成本并優化運營。本案例探討了如何通過使用穩聯技術PROFINET轉MODBUS模塊網關網橋&#xff…

【前端】JavaScript 備忘清單(超級詳細!)

文章目錄 入門介紹打印調試斷點調試數字let 關鍵字const 關鍵字變量字符串算術運算符注釋賦值運算符字符串插值字符串數字Math全局函數 JavaScript 條件操作符邏輯運算符 &&比較運算符邏輯運算符空值合并運算符 ?? if Statement (if 語句)Ternary Operator (三元運算…

Linux cat 命令

cat&#xff08;英文全拼&#xff1a;concatenate&#xff09;命令用于連接文件并打印到標準輸出設備上&#xff0c;它的主要作用是用于查看和連接文件。 使用權限 所有使用者 語法格式 cat [選項] [文件] 參數說明&#xff1a; -n&#xff1a;顯示行號&#xff0c;會在輸…

PARETO PROMPT OPTIMIZATION

題目 帕累托提示優化 論文地址&#xff1a;https://openreview.net/forum?idHGCk5aaSvE 摘要 自然語言迅速優化或及時工程已成為一種強大的技術&#xff0c;可以解鎖大型語言模型&#xff08;LLMS&#xff09;的各種任務的潛力。盡管現有方法主要集中于最大化LLM輸出的單一特…

前端面試題---.onChange() 事件與焦點機制解析

.onChange() 事件與焦點的總結 焦點&#xff08;focus&#xff09; 指的是 當前正在操作的元素&#xff08;如輸入框、按鈕&#xff09;。只有一個元素能同時擁有焦點。 原生 HTML 事件&#xff1a; onchange &#xff08;需要失去焦點才觸發&#xff09; 用戶輸入后&#x…

Nest系列:從環境變量到工程化實踐-2

文章目錄 [toc]一、環境搭建與基礎配置1.1 安裝驗證&#xff08;新增完整示例&#xff09;1.2 多環境配置示例 二、模塊化配置實戰2.1 根模塊高級配置2.2 數據庫模塊專用配置 三、配置獲取最佳實踐3.1 類型安全獲取示例3.2 枚舉工程化示例 四、高級場景示例4.1 動態配置熱更新4…

3.對象生活的地方—Java環境搭建

1、你要養魚&#xff0c;總得先有一個魚塘吧。挖一個魚塘來養魚&#xff0c;你需要去做下面這些事情&#xff1a; 規劃和設計&#xff1a;確定魚塘的位置、大小和形狀&#xff0c;繪制設計圖。標記和測量&#xff1a;使用測量工具標記魚塘的邊界和深度。挖掘&#xff1a;使用挖…

玩轉大模型——Trae AI IDE國內版使用教程

文章目錄 Trae AI IDE完備的 IDE 功能強大的 AI 助手 安裝 Trae 并完成初始設置管理項目什么是 “工作空間”&#xff1f;創建項目 管理插件安裝插件從 Trae 的插件市場安裝從 VS Code 的插件市場安裝 禁用插件卸載插件插件常見問題暫不支持安裝 VS Code 插件市場中某個版本的插…

數據結構1-4 隊列

一、隊列是什么&#xff1f; 先舉一個日常例子&#xff0c;排隊買飯。 排隊買飯 大家按先來后到的順序&#xff0c;在窗口前排隊買飯&#xff0c;先到先得&#xff0c;買完之后走開&#xff0c;輪到下一位買&#xff0c;新來的人排在隊尾&#xff0c;不能插隊。 可見&#x…

(十 四)趣學設計模式 之 策略模式!

目錄 一、 啥是策略模式&#xff1f;二、 為什么要用策略模式&#xff1f;三、 策略模式的實現方式四、 策略模式的優缺點五、 策略模式的應用場景六、 總結 &#x1f31f;我的其他文章也講解的比較有趣&#x1f601;&#xff0c;如果喜歡博主的講解方式&#xff0c;可以多多支…

探秘基帶算法:從原理到5G時代的通信變革【三】Turbo 編解碼

文章目錄 2.2 Turbo 編解碼2.2.1 基本概念與系統構成2.2.2 編碼過程分步解析交織器遞歸系統卷積編碼器復接器總結 2.2.3 譯碼算法分類與原理Turbo碼的強大主要來源于其解碼器理論基礎解碼過程詳解交織與解交織譯碼算法總結 2.2.4 Turbo碼的應用場景無線通信衛星通信深空通信 2.…

Yocto + 樹莓派攝像頭驅動完整指南

—— 從驅動配置、Yocto 構建&#xff0c;到 OpenCV 實戰 在樹莓派上運行攝像頭&#xff0c;在官方的 Raspberry Pi OS 可能很簡單&#xff0c;但在 Yocto 項目中&#xff0c;需要手動配置驅動、設備樹、軟件依賴 才能確保攝像頭正常工作。本篇文章從 BSP 驅動配置、Yocto 關鍵…

TCP協議(20250304)

1. TCP TCP: 傳輸控制協議&#xff08;Transmission Control Protocol&#xff09;&#xff0c;傳輸層協議之一&#xff08;TCP&#xff0c;UDP&#xff09; 2. TCP與UDP UDP&#xff08;用戶數據報協議&#xff09; 面向數據報無連接不安全不可靠&#xff08;盡最大努力交…

NModbus 連接到Modbus服務器(Modbus TCP)

1、在項目中通過NuGet添加NModbus&#xff0c;在界面中添加一個Button。 using NModbus.Device; using NModbus; using System.Net.Sockets; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Docu…

【零基礎到精通Java合集】第十八集:多線程與并發編程-線程池與Callable/Future應用

課程標題:線程池與Callable/Future應用(15分鐘) 目標:掌握線程池的創建與管理,理解Callable任務與Future異步結果處理機制 0-1分鐘:課程引入與線程池意義 以“銀行窗口服務”類比線程池:復用固定資源(柜員)處理多任務(客戶)。說明線程池的核心價值——避免頻繁創建…

【leetcode hot 100 238】除自身以外數組的乘積

解法一&#xff1a;&#xff08;左右乘積列表&#xff09;利用索引左側所有數字的乘積和右側所有數字的乘積&#xff08;即前綴與后綴&#xff09;相乘得到答案。 class Solution {public int[] productExceptSelf(int[] nums) {int len nums.length;int[] L new int[len]; …

BUU44 [BJDCTF2020]ZJCTF,不過如此1 [php://filter][正則表達式get輸入數據][捕獲組反向引用][php中單雙引號]

題目&#xff1a; 我仿佛見到了一位故人。。。也難怪&#xff0c;題目就是ZJCTF 按要求提交/?textdata://,I have a dream&filenext.php后&#xff1a; ......不太行&#xff0c;好像得用filephp://filter/convert.base64-encode/resourcenext.php 耶&#xff1f;那 f…

[Web 安全] PHP 反序列化漏洞 —— POP 鏈構造思路

關注這個專欄的其他相關筆記&#xff1a;[Web 安全] 反序列化漏洞 - 學習筆記-CSDN博客 0x01&#xff1a;什么是 POP 鏈&#xff1f; POP 鏈&#xff08;Payload On Purpose Chain&#xff09;是一種利用 PHP 中的魔法方法進行多次跳轉以獲取敏感數據的技術。它通常出現在 CTF…

擴散語言模型:從圖像生成到文本創造的范式躍遷

近年來,擴散模型(Diffusion Models)在人工智能領域異軍突起,尤其在圖像生成任務中取得了令人矚目的成就,如 Stable Diffusion 等模型已成為生成高質量圖像的標桿。這種成功激發了研究者們的好奇心:擴散模型的魔力能否從視覺領域延伸至自然語言處理(NLP),為文本生成帶來…