Metal 學習筆記四:頂點函數

到目前為止,您已經完成了 3D 模型和圖形管道。現在,是時候看看 Metal 中兩個可編程階段中的第一個階段,即頂點階段,更具體地說,是頂點函數。

著色器函數

定義著色器函數時,可以為其指定一個屬性。您將在本書中學到這些屬性:
? vertex:頂點函數:計算頂點的位置。
? fragment: 片段函數: 計算片段的顏色。
? kernel:內核功能:用于通用的并行計算,例如圖像處理。

在本章中,您將只關注 vertex 函數。在第 7 章 “片段函數”中,您將探索如何控制每個片段的顏色。在第 16 章 “GPU 計算編程” 中,您將了解如何使用具有多個線程的并行編程來寫入緩沖區和紋理。

到目前為止,您應該已經熟悉頂點描述符,以及如何使用它們來描述如何從加載的 3D 模型中排列頂點屬性。回顧一下:

? MDLVertexDescriptor:使用Model I/O 頂點描述符讀取 USD 文件。Model I/O 會創建緩存區,緩存區會按我們所需的布局,存放屬性值,例如位置、法線和紋理坐標。

? MTLVertexDescriptor:在創建管線狀態時使用 Metal 頂點描述符。GPU 頂點函數使用 [[stage_in]] 屬性將傳入數據與管線狀態中的頂點描述符進行匹配。

在學習本章時,您將在不使用頂點描述符情況下,構建自己的頂點網格并將頂點發送到 GPU。您將學習如何在頂點函數中控制這些頂點,然后升級到使用頂點描述符。在此過程中,您將看到如何使用 Model I/O 導入網格,從而為您完成許多繁重的工作。

開始項目

? 打開本章的初始項目。
此 SwiftUI 項目包含一個簡化的 Renderer,以便您可以添加自己的網格,并且著色器函數是缺失的,因此您可以構建它們。您尚未進行任何繪圖,因此在運行應用程序時看不到任何內容。

渲染一個四邊形

您可以使用兩個三角形創建一個四邊形。每個三角形有 3 個頂點,總共有 6 個頂點。

? 創建一個名為 Quad.swift 的新 Swift 文件。

? 將現有代碼替換為:

import MetalKit
struct Vertex {var x: Floatvar y: Floatvar z: Float
}
struct Quad {var vertices: [Vertex] = [Vertex(x: -1, y:  1, z: 0),Vertex(x:  1, y: -1, z: 0),Vertex(x: -1, y: -1, z: 0),Vertex(x: -1, y:  1, z: 0),Vertex(x:  1, y:  1, z: 0),Vertex(x:  1, y: -1, z: 0)
] }
// triangle 1
// triangle 2

您可以創建一個結構體來組成一個具有 x、y 和 z 值的頂點。在這里,頂點的環繞順序 (頂點順序) 是順時針方向的,這很重要。

? 向 Quad 添加新的頂點緩存區property并初始化它:

let vertexBuffer: MTLBuffer
init(device: MTLDevice, scale: Float = 1) {vertices = vertices.map {Vertex(x: $0.x * scale, y: $0.y * scale, z: $0.z * scale)}guard let vertexBuffer = device.makeBuffer(bytes: &vertices,length: MemoryLayout<Vertex>.stride * vertices.count,options: []) else {fatalError("Unable to create quad vertex buffer")
}self.vertexBuffer = vertexBuffer
}

?使用此代碼,您可以使用頂點數組初始化 Metal 緩存區。將每個頂點乘以 scale,這樣就可以在初始化期間設置四邊形的大小。

? 打開 Renderer.swift,并為 quad 網格添加一個新property:

 lazy var quad: Quad = {Quad(device: Self.device, scale: 0.8)
}()

在這里,您將使用 Renderer 的設備初始化 quad。您必須延遲初始化 quad,因為在運行 init(metalView:) 之前,device 不會初始化。您還可以調整四邊形的大小,以便可以清楚地看到它。

注意:如果您將比例保持在默認的1.0下,四邊形將覆蓋整個屏幕。覆蓋屏幕對于全屏繪圖很有用,因為您只能在渲染幾何圖形的區域繪制片段。

在 draw(in:) 中,在 // do drawing here 之后,添加:

renderEncoder.setVertexBuffer(quad.vertexBuffer,offset: 0,index: 0)

您在渲染命令編碼器上創建一個命令,將頂點緩沖區在緩沖區參數表中的索引設置為 0。

? 添加 draw 調用:

renderEncoder.drawPrimitives(type: .triangle,vertexStart: 0,vertexCount: quad.vertices.count)

?在這里,您將繪制四邊形的六個頂點。

? 打開 Shaders.metal。
? 將 vertex 函數替換為:

vertex float4 vertex_main(constant float3 *vertices [[buffer(0)]],uint vertexID [[vertex_id]])
{float4 position = float4(vertices[vertexID], 1);return position;
}

此代碼存在錯誤,您將很快觀察并修復該錯誤。

GPU 為每個頂點執行頂點函數。在繪制調用中,您指定了有6個頂點。因此,頂點函數將執行六次。

將指針傳遞到 vertex 函數時,必須指定地址空間,constant 或 device。constant 經過優化,可在多個頂點函數上并行訪問同一變量。device 最適合通過并行函數訪問緩沖區的不同部分,例如使用交錯頂點和顏色數據的緩沖區時。

[[vertex_id]] 是一個屬性限定符,它為您提供當前頂點。您可以將其用作訪問vertices 持有數組的入口。

您可能會注意到,您正在向 GPU 發送一個緩沖區,其中填充了一個 Vertexs 數組,該數組由 3 個 Float 組成。在頂點函數中,您讀取的緩沖區與 float3 數組相同,從而導致顯示錯誤。

盡管您可能會獲得不同的渲染,但頂點位于錯誤的位置,因為 float3 類型比具有三個 Float 類型成員的 Vertex 占用更多的內存。Float 長 4 字節,Vertex 長 12 字節。SIMD float3 類型是帶填充的,占用與 float4 類型相同的內存,即 16 字節。將此參數更改為 packed_float3 將修復錯誤,因為 packed_float3占用 12 個字節。

注意:您可以在https://apple.co/2UT993x查看Metal著色語言規范中類型的大小。

在 vertex 函數中,將第一個參數中的 float3 改為 packed_float3。

編譯并運行。

四邊形現在顯示正確了。或者,您可以將 Float 數組頂點定義為 simd_float3 數組。在這種情況下,您將在頂點函數中使用 float3,因為這兩種類型都需要 16 個字節。但是,每個頂點發送 16 個字節的效率略低于每個頂點發送 12 個字節的效率。

計算位置

Metal不但支持絢麗的色彩,也支持快速平滑的動畫。在下一步,我們會讓我們的四邊形上下移動。為了做到這個,我們需要一個計時器,每幀都更新四邊形的位置。頂點shader函數就是我們更新頂點位置的地方,我們會發送計時器數據到GPU。

在Renderer的頭部,添加如下屬性:

var timer: Float = 0

然后在draw(in:), 在這一行前面

renderEncoder.setRenderPipelineState(pipelineState)

添加如下代碼:

// 1
timer += 0.05
var currentTime = sin(timer)
// 2
renderEncoder.setVertexBytes(&currentTime,length: MemoryLayout<Float>.stride,index: 11)

1,每幀都更新計時器,如果你希望你的四邊形上下移動,你需要使用一個在-1和1之間的值,使用sin()函數是一個很好的限制值在-1到1之間的方法。你可以通過更改每幀中給timer增加的值,來更改動畫的速度。

2,如果你發送少量的數據(小于4kb)給GPU,setVertexBytes(_:length:index:)是一個創建MTLBuffer的較好選擇。這里你將currentTime設置給緩存參數表中索引為11的緩存。為頂點屬性(例如頂點位置)保留緩沖區 1 到 10 有助于記住哪些緩沖區保存哪些數據。

在Shader.metal,把vertex_main函數改成這樣:

vertex float4 vertex_main(constant packed_float3 *vertices [[buffer(0)]],constant float &timer [[buffer(11)]],uint vertexID [[vertex_id]])
{float4 position = float4(vertices[vertexID], 1);position.y += timer;return position;
}

您在緩沖區 11 中以浮點數的形式接收單值timer。您將 timer 值添加到 y 位置,并從函數返回新位置。

在下一章中,您將開始學習如何使用矩陣乘法將頂點投影到 3D 空間中。但是,您并不總是需要矩陣乘法來移動頂點;在這里,您可以使用簡單的加法來實現沿著Y 軸平移。

? 構建并運行應用程序,您將看到一個可愛的動畫四邊形。

?

更高效的渲染

目前,您正在使用 6 個頂點來渲染兩個三角形。

在這些頂點中,0 和 3 位于同一位置,1 和 5 也是如此。如果您渲染具有數千個甚至數百萬個頂點的網格,則盡可能減少重復是非常重要的。您可以使用索引渲染來實現。

僅為不同的頂點位置創建結構體,然后使用 indices 獲取頂點的正確位置。

? 打開 Quad.swift,并將頂點重命名為 oldVertices。

? 將以下結構添加到 Quad:

var vertices: [Vertex] = [Vertex(x: -1, y:  1, z: 0),Vertex(x:  1, y:  1, z: 0),Vertex(x: -1, y: -1, z: 0),Vertex(x:  1, y: -1, z: 0)
]
var indices: [UInt16] = [0, 3, 2,
0, 1, 3 ]

vertices 現在以任意順序保存四邊形的唯一四個點。indices 以正確的頂點順序保存每個頂點的索引。請參閱 oldVertices 以確保您的索引正確無誤。?

? 添加新的 Metal 緩沖區來保存索引:

let indexBuffer: MTLBuffer

在 init(device:scale:) 的末尾,添加:

guard let indexBuffer = device.makeBuffer(bytes: &indices,length: MemoryLayout<UInt16>.stride * indices.count,options: []) else {fatalError("Unable to create quad index buffer")
}
self.indexBuffer = indexBuffer

?創建索引緩沖區的方式與創建頂點緩沖區的方式相同。

? 打開 Renderer.swift,在 draw(in:) 中,在 draw 調用之前,添加:

renderEncoder.setVertexBuffer(quad.indexBuffer,offset: 0,index: 1)

在這里,您將索引緩沖區發送到 GPU。

? 將 draw 調用更改為:?

renderEncoder.drawPrimitives(type: .triangle,vertexStart: 0,vertexCount: quad.indices.count)

使用索引計數來表示要渲染的頂點數;而不是頂點計數。

? 打開 Shaders.metal,并將頂點函數更改為:?

vertex float4 vertex_main(constant packed_float3 *vertices [[buffer(0)]],constant ushort *indices [[buffer(1)]],constant float &timer [[buffer(11)]],uint vertexID [[vertex_id]])
{ushort index = indices[vertexID];float4 position = float4(vertices[index], 1);return position;
}

此處,vertexID 是緩存區中的索引,該緩存區保存了四邊形的索引。使用索引緩存區中的值,在頂點緩存區中正確索引頂點。?

? 構建并運行。

當然,你的四邊形的位置與以前相同,但現在你向 GPU 發送的數據更少。

從數組中的條目數量來看,您實際上似乎在發送更多數據 — 但事實并非如此!oldVertices 的內存占用為 72 字節,而 vertices + indices 的內存占用為 60 字節。

頂點描述器

使用索引渲染頂點時,可以使用更高效的繪制調用。但是,您首先需要在管道中設置頂點描述符。
使用頂點描述符始終是一個好主意,因為大多數情況下,您不僅會向 GPU 發送位置。您還將發送法線、紋理坐標和顏色等屬性。當您可以布置自己的頂點數據時,您可以更好地控制引擎處理模型網格的方式。

? 創建一個名為 VertexDescriptor.swift 的新 Swift 文件。

? 將代碼替換為:

import MetalKit
extension MTLVertexDescriptor {static var defaultLayout: MTLVertexDescriptor {let vertexDescriptor = MTLVertexDescriptor()vertexDescriptor.attributes[0].format = .float3vertexDescriptor.attributes[0].offset = 0vertexDescriptor.attributes[0].bufferIndex = 0let stride = MemoryLayout<Vertex>.stridevertexDescriptor.layouts[0].stride = stridereturn vertexDescriptor} 
}

在這里,您將設置一個只有一個屬性的頂點布局。此屬性描述每個頂點的位置。

頂點描述符包含屬性和緩存區布局的數組。

? attributes:對于每個屬性,指定從緩沖區開頭開始的第一項的類型格式和偏移量(以字節為單位)。您還可以指定保存該屬性的緩沖區的索引。
? buffer layout:指定每個緩沖區中組合的所有屬性的步幅長度。這里可能會讓人感到困惑,因為你正在使用索引 0 來索引布局和屬性,但布局索引 0 對應于屬性使用到的 bufferIndex 0。?

注意: stride 描述了每個實例之間的字節數。由于內部填充和字節對齊,此值可能與 size 不同。有關大小、步幅和對齊的精彩解釋,請查看 Greg Heo 的文章,網址為https://bit.ly/2V3gBJl.

對于 GPU,vertexBuffer 現在如下所示:

? 打開 Renderer.swift,并在 init(metalView:) 中找到創建管道狀態的位置。

? 在 do {} 中創建管道狀態之前,將以下代碼添加到管道狀態描述符中:

 pipelineDescriptor.vertexDescriptor =MTLVertexDescriptor.defaultLayout

GPU 現在期望頂點按描述符描述的格式存放。

? 在 draw(in:) 中,刪除:?

renderEncoder.setVertexBuffer(quad.indexBuffer,offset: 0,index: 1)

您將在 draw 調用中包含索引緩沖區。

? 將 draw 調用更改為:

renderEncoder.drawIndexedPrimitives(type: .triangle,indexCount: quad.indices.count,indexType: .uint16,indexBuffer: quad.indexBuffer,indexBufferOffset: 0)

此繪圖調用期望索引緩沖區使用 UInt16,這就是你在 Quad 中描述 indices 數組的方式。你沒有顯式地將 quad.indexBuffer 發送到 GPU,因為這個 draw 調用會為你做這件事。
? 打開 Shaders.metal。
? 將 vertex 函數替換為:

vertex float4 vertex_main(float4 position [[attribute(0)]] [[stage_in]],constant float &timer [[buffer(11)]])
{return position;
}

?你為 Swift 端的布局做了所有繁重的工作,所以頂點函數的大小大大減小了。

您可以使用 [[stage_in]] 屬性描述每個逐頂點的輸入。GPU 現在查看管道狀態的頂點描述符。
[[attribute(0)]] 是頂點描述符中描述位置的屬性。即使您將原始頂點數據定義為包含三個浮點數的頂點類型,也可以在此處將位置定義為 float4。GPU 可以進行轉換。
值得注意的是,當 GPU 將 w 信息添加到 xyz 位置時,它會添加為1.0。正如您將在以下章節中看到的那樣,這個 w 值在柵格化過程中非常重要。
GPU 現在擁有計算每個頂點位置所需的所有信息。
? 構建并運行應用程序以確保一切仍然有效。生成的渲染將與以前相同。

?

添加另一個頂點屬性

您可能永遠不會只有一個屬性,因此讓我們為每個頂點添加一個 color 屬性。
您可以選擇是使用兩個緩沖區還是在每個頂點位置之間交錯顏色。如果您選擇交錯,您將設置一個結構來保存位置和顏色。但是,在此示例中,添加新的顏色緩沖區以匹配每個頂點會更容易。

? 打開 Quad.swift,并添加新數組:

var colors: [simd_float3] = [[1, 0, 0], // red[0, 1, 0], // green[0, 0, 1], // blue[1, 1, 0]  // yellow
]

現在,您有四種 RGB 顏色來匹配這四個頂點。

? 創建一個新的緩沖區屬性:?

let colorBuffer: MTLBuffer

? 在 init(device:scale:) 的末尾添加:?

guard let colorBuffer = device.makeBuffer(bytes: &colors,length: MemoryLayout<simd_float3>.stride * colors.count,options: []) else {fatalError("Unable to create quad color buffer")}
self.colorBuffer = colorBuffer

初始化 colorBuffer 的方式與前兩個緩沖區相同。
? 打開 Renderer.swift,然后在 draw(in:) 中,在 draw 調用之前添加:?

renderEncoder.setVertexBuffer(quad.colorBuffer,offset: 0,index: 1)

使用緩沖區索引 1 將顏色緩沖區發送到 GPU,該索引必須與頂點描述符布局中的索引匹配。
? 打開 VertexDescriptor.swift,并在返回之前將以下代碼添加到 defaultLayout:

vertexDescriptor.attributes[1].format = .float3
vertexDescriptor.attributes[1].offset = 0
vertexDescriptor.attributes[1].bufferIndex = 1
vertexDescriptor.layouts[1].stride =MemoryLayout<simd_float3>.stride

在這里,您將描述緩沖區索引 1 中顏色緩沖區的布局。

?? 打開 Shaders.metal。
? 您只能在一個參數上使用 [[stage_in]],因此請創建一個新結構體
在 Vertex 函數之前:

struct VertexIn {float4 position [[attribute(0)]];float4 color [[attribute(1)]];
};

?? 將 vertex 函數更改為:

vertex float4 vertex_main(VertexIn in [[stage_in]],constant float &timer [[buffer(11)]])
{return in.position;
}

這段代碼仍然簡短明了。GPU 知道如何從緩沖區中檢索位置和顏色,因為結構中的 [[attribute(n)]] 限定符查看管道狀態的頂點描述符。
? 構建并運行以確保您的藍色象限仍然渲染。

fragment 函數確定每個渲染片段的顏色。您需要將頂點的顏色傳遞給 fragment 函數。您將在第 7 章 “片段函數” 中了解有關 fragment 函數的更多信息。
? 仍在 Shaders.metal 中,在 vertex 函數之前添加以下結構:?

 struct VertexOut {float4 position [[position]];float4 color;
};

現在,您不僅可以從 vertex 函數返回 position,還可以返回 position 和 color。您可以指定 position 屬性,讓 GPU 知道此結構中的哪個屬性是 position。
? 將 vertex 函數替換為:?

vertex VertexOut vertex_main(VertexIn in [[stage_in]],constant float &timer [[buffer(11)]]) {VertexOut out {.position = in.position,.color = in.color};
return out; }

?現在,您返回 VertexOut 而不是 float4。

? 將片段功能改為:

 fragment float4 fragment_main(VertexOut in [[stage_in]]) {return in.color;
}

[[stage_in]] 屬性指示 GPU 應從頂點函數獲取 VertexOut 輸出,并將其與柵格化片段匹配。在這里,您將返回頂點顏色。請記住第 3 章 “渲染管道” 中,每個片段的輸入都會進行插值。

? 構建并運行應用程序,您將看到以美麗的顏色渲染的四邊形。?

?

渲染成點狀

您可以渲染點和線,而不是渲染三角形。

? 打開 Renderer.swift,然后在 draw(in:) 中更改

 renderEncoder.drawIndexedPrimitives(type: .triangle,

為:

 renderEncoder.drawIndexedPrimitives(type: .point,

如果您現在構建并運行,GPU 將渲染點,但它不知道要使用什么大小的點,因此它會在各種點大小上閃爍。要解決此問題,您還將在從 vertex 函數返回數據時,返回點尺寸。
? 打開 Shaders.metal,并將此屬性添加到 VertexOut:?

float pointSize [[point_size]];

[[point_size]] 屬性將告訴 GPU 要使用什么尺寸的點。

? 在 vertex_main 中,將 out 的初始化替換為:?

VertexOut out {.position = in.position,.color = in.color,.pointSize = 30
};

?在這里,您將分配點尺寸為30。
? 構建并運行以查看使用頂點顏色渲染的點:

?

?

挑戰

到目前為止,您已將頂點位置發送到數組緩沖區中的 GPU。但這并不完全必要。GPU 需要知道的是要繪制多少個頂點。您的挑戰是刪除頂點和索引緩沖區,并在一個圓圈中繪制 50 個點。以下是您需要采取的步驟的概述,以及一些幫助您入門的代碼:
1. 在 Renderer 中,從管道中刪除頂點描述符。
2. 替換 Renderer 中的 draw 調用,使其不使用索引,但繪制 50個頂點。
3. 在 draw(in:) 中,刪除所有 setVertexBuffer 命令。
4. GPU 需要知道總點數,因此請以與緩沖區 0 中的 timer 相同的方式發送此值。
5. 將 vertex 函數替換為:

vertex VertexOut vertex_main(constant uint &count [[buffer(0)]],constant float &timer [[buffer(11)]],uint vertexID [[vertex_id]])
{float radius = 0.8;float pi = 3.14159;float current = float(vertexID) / float(count);float2 position;position.x = radius * cos(2 * pi * current);position.y = radius * sin(2 * pi * current);VertexOut out {.position = float4(position, 0, 1),.color = float4(1, 0, 0, 1),.pointSize = 20
};
return out; }

請記住,這是一個練習,可幫助您了解如何在 GPU 上定位點,而無需在 Swift 端保存任何等效數據。所以,不要太擔心數學。您可以使用當前頂點 ID 的正弦和余弦來繪制圓周圍的點。
請注意,GPU 上沒有 pi 的內置值。您將看到 50 個點被繪制成一個圓圈。


嘗試通過將 timer 添加到 current 來為點添加動畫。
如果你有任何困難,你可以在本章的項目挑戰目錄中找到解決方案。?

參考

https://zhuanlan.zhihu.com/p/385638027

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

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

相關文章

什么是零拷貝?

零拷貝是一種優化技術&#xff0c;用于減少數據在計算機系統中的拷貝次數&#xff0c;從而提高性能和效率。在傳統的數據傳輸中&#xff0c;數據通常會在多個緩沖區之間進行多次拷貝&#xff0c;而零拷貝技術通過減少這些不必要的拷貝操作&#xff0c;顯著降低了CPU和內存的開銷…

【LeetCode20】有效的括號

題目描述 給定一個只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘]’ 的字符串 s &#xff0c;判斷字符串是否有效。 有效字符串需滿足&#xff1a; 左括號必須用相同類型的右括號閉合。 左括號必須以正確的順序閉合。 每…

算法訓練(leetcode)二刷第三十七天 | *300. 最長遞增子序列、674. 最長連續遞增序列、*718. 最長重復子數組

刷題記錄 *300. 最長遞增子序列674. 最長連續遞增序列基礎解法&#xff08;非動規&#xff09;動態規劃 718. 最長重復子數組滾動數組 *300. 最長遞增子序列 leetcode題目地址 dp數組含義&#xff1a; dp[i]表示以nums[i]結尾的最長遞增子序列長度&#xff0c;即以nums[i]結尾…

Elasticsearch 相關面試題

1. Elasticsearch基礎 Elasticsearch是什么&#xff1f; Elasticsearch是一個分布式搜索引擎&#xff0c;基于Lucene實現。 Mapping是什么&#xff1f;ES中有哪些數據類型&#xff1f; Mapping&#xff1a;定義字段的類型和屬性。 數據類型&#xff1a;text、keyword、integer、…

TCP/IP的分層結構、各層的典型協議,以及與ISO七層模型的差別

1. TCP/IP的分層結構 TCP/IP模型是一個四層模型&#xff0c;主要用于網絡通信的設計和實現。它的分層結構如下&#xff1a; (1) 應用層&#xff08;Application Layer&#xff09; 功能&#xff1a;提供應用程序之間的通信服務&#xff0c;處理特定的應用細節。 典型協議&am…

pycharm技巧--鼠標滾輪放大或縮小 Pycharm 字體大小

1、鼠標滾輪調整字體 設置 Ctrl 鼠標滾輪調整字體大小 備注&#xff1a; 第一個是活動窗口&#xff0c;即縮放當前窗口 第二個是所有編輯器窗口&#xff0c;即縮放所有窗口的字體 2、插件 漢化包&#xff1a; Chinese Simplified 包

硬件工程師入門教程

1.歐姆定律 測電壓并聯使用萬用表測電流串聯使用萬用表&#xff0c;紅入黑出 2.電阻的阻值識別 直插電阻 貼片電阻 3.電阻的功率 4.電阻的限流作用 限流電阻阻值的計算 單位換算關系 5.電阻的分流功能 6.電阻的分壓功能 7.電容 電容簡單來說是兩塊不連通的導體加上中間的絕…

edge瀏覽器將書簽欄頂部顯示

追求效果&#xff0c;感覺有點丑&#xff0c;但總歸方便多了 操作路徑&#xff1a;設置-外觀-顯示收藏夾欄-始終

【SPIE出版,見刊快速,EI檢索穩定,浙江水利水電學院主辦】2025年物理學與量子計算國際學術會議(ICPQC 2025)

2025年物理學與量子計算國際學術會議&#xff08;ICPQC 2025&#xff09;將于2025年4月18-20日在中國杭州舉行。本次會議旨在匯聚全球的研究人員、學者和業界專家&#xff0c;共同探討物理學與量子計算領域的最新進展與前沿挑戰。隨著量子技術的快速發展&#xff0c;其在信息處…

谷歌瀏覽器更新后導致的刷新數據無法顯示

這幾天突然出現的問題&#xff0c;就是我做了一個網站&#xff0c;一直用Google展示&#xff0c;前兩天突然就是刷新會丟失數據&#xff0c;然后再刷新幾次吧又有了&#xff0c;之前一直好好的&#xff0c;后端也做了一些配置添加了CrossOrigin注解&#xff0c;然而換了edge瀏覽…

UE5從入門到精通之多人游戲編程常用函數

文章目錄 前言一、權限與身份判斷函數1. 服務器/客戶端判斷2. 網絡角色判斷二、網絡同步與復制函數1. 變量同步2. RPC調用三、連接與會話管理函數1. 玩家連接控制2. 網絡模式判斷四、實用工具函數前言 UE5給我們提供了非常強大的多人網路系統,讓我們可以很方便的開發多人游戲…

軟件需求管理辦法,軟件開發管理指南(Word原件)

1. 目的 2. 適用范圍 3. 參考文件 4. 術語和縮寫 5. 需求獲取的方式 5.1. 與用戶交談向用戶提問題 5.1.1. 訪談重點注意事項 5.1.2. 訪談指南 5.2. 參觀用戶的工作流程 5.3. 向用戶群體發調查問卷 5.4. 已有軟件系統調研 5.5. 資料收集 5.6. 原型系統調研 5.6.1. …

利用python和gpt寫一個conda環境可視化管理工具

最近在學習python&#xff0c;由于不同的版本之間的差距較大&#xff0c;如果是用環境變量來配置python的話&#xff0c;會需要來回改&#xff0c;于是請教得知可以用conda來管理&#xff0c;但是conda在管理的時候老是要輸入命令&#xff0c;感覺也很煩&#xff0c;于是讓gpt幫…

【復習】計算機網絡

網絡模型 OSI 應用層&#xff1a;給應用程序提供統一的接口表示層&#xff1a;把數據轉換成兼容另一個系統能識別的格式會話層&#xff1a;負責建立、管理、終止表示層實體之間的通信會話傳輸層&#xff1a;負責端到端的數據傳輸網絡層&#xff1a;負責數據的路由、轉發、分片…

圖書館系統源碼詳解

本項目是一個基于Scala語言開發的圖書館管理系統。系統主要由以下幾個部分組成&#xff1a;數據訪問層&#xff08;DAO&#xff09;、數據模型層&#xff08;Models&#xff09;、服務層&#xff08;Service&#xff09;以及用戶界面層&#xff08;UI&#xff09;。以下是對項目…

Redis——用戶簽到BitMap,UV統計

目錄 BitMap 使用場景 1. 用戶簽到系統 2. 用戶行為標記 3. 布隆過濾器&#xff08;Bloom Filter&#xff09; BitMap介紹 Redis中的使用 Redis功能示例 添加&#xff1a; 獲取&#xff1a; 批量獲取&#xff1a; java中實現 統計本月連續簽到次數 UV統計 UV 統計…

【數據庫】【MySQL】索引

MySQL中索引的概念 索引&#xff08;MySQL中也叫做"鍵&#xff08;key&#xff09;"&#xff09;是一種數據結構&#xff0c;用于存儲引擎快速定找到記錄。 簡單來說&#xff0c;它類似于書籍的目錄&#xff0c;通過索引可以快速找到對應的數據行&#xff0c;而無需…

【SpringBoot AI 集成DeepSeek 大模型API調用】

當DeepSeek開始盛行&#xff0c;提供強大的大語言模型&#xff0c;界面調用不能滿足我們的需要&#xff0c;同時提供API接口供我們在服務中調用&#xff0c;來實現各種AI場景。 我們通過將DeepSeek的AI能力與SpringBoot AI相結合&#xff0c;實現智能聊天、問答機器人&#xf…

Linux 性能更好的ftp客戶端 lftp 使用詳解

簡介 LFTP 是一個命令行 FTP 客戶端&#xff0c;支持多種文件傳輸協議&#xff0c;包括 FTP、FTPS、HTTP、HTTPS和SFTP 。它以其通過鏡像、后臺操作和腳本支持等特性有效管理復雜傳輸的能力而聞名。 安裝 Ubuntu/Debian sudo apt update sudo apt install lftpCentOS/RHEL/…

汽車智能制造企業數字化轉型SAP解決方案總結

一、項目實施概述 項目階段劃分&#xff1a; 藍圖設計階段主數據管理方案各模塊藍圖設計方案下一階段工作計劃 關鍵里程碑&#xff1a; 2022年6月6日&#xff1a;項目啟動會2022年12月1日&#xff1a;系統上線 二、總體目標 通過SAP實施&#xff0c;構建研產供銷協同、業財一…