【OpenGL】LearnOpenGL學習筆記02 - 繪制三角形、矩形

上接: https://blog.csdn.net/weixin_44506615/article/details/149861824
完整代碼:https://gitee.com/Duo1J/learn-open-gl

一、渲染管線

在開始之前,我們先簡單了解一下圖形渲染管線
在渲染3D物體時,我們常用到的一種幾何結構為網格模型 (Mesh),其為一系列點的集合,各點相連組成三角面
首先CPU會進行一些前置操作(數據處理、緩沖綁定等)后將產生數據提交 (DrawCall) 到GPU,GPU在接收到數據后會執行一系列操作來將3D場景數據轉換為2D圖像,這個過程就稱作渲染管線
一個最簡單的渲染管線通常有以下步驟:
圖片來源LearnOpenGL
圖片來源LearnOpenGL
1. 頂點著色器 (Vertex Shader)
對傳入的頂點數據進行操作,如位移等,此外還會進行一系列的坐標變換(MVP變換),返回操作后的頂點坐標及其他屬性
2. 光柵化
將三維幾何圖元轉換為屏幕上的二維像素片元,通常會在這個環節進行視口變換,片元生成,裁剪等
3. 片元(片段、像素)著色器 (Fragment Shader/Pixel Shader)
處理光柵化生成的每個片元,進行光照模型計算、紋理采樣等操作,返回片元的顏色值及其他屬性
最后進行深度測試、模板測試、混合后寫入幀緩沖區
如今大多數顯卡都有成千上萬個小處理核心,它們運行著各自的小程序,從而在渲染管線中快速并行的處理渲染數據,這些小程序就是上述的著色器(Shader)

二、繪制三角形

我們先定義一下輸入的頂點數據
這段數據表示三個點,為三角形的三個頂點
這些點的坐標值范圍在[-1,1]之間,稱為標準化設備坐標(NDC)

float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f,  0.5f, 0.0f
};

1. 頂點緩沖對象(Vertex Buffer Objects - VBO)
VBO為顯存中的一塊緩沖區,其用來存儲大量的頂點數據,在渲染前由CPU將數據發送到VBO中
我們可以通過以下代碼來對VBO進行創建、綁定、設置數據

// 創建緩沖
unsigned int VBO;
glGenBuffers(1, &VBO);
// 綁定創建的緩沖到VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 寫入數據到VBO
// 這里第四個參數表示我們希望顯卡如何管理給定的數據
// GL_STATIC_DRAW 數據不會或幾乎不會改變
// GL_DYNAMIC_DRAW 數據會被改變很多
// GL_STREAM_DRAW 數據每次繪制時都會改變
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

2. 頂點屬性
我們已經將頂點數據傳入到了VBO中,但是GPU還不知道要怎么去解析這些頂點數據,所以接下來我們要配置頂點解析的方式

// 設置頂點屬性
// 參數1: 設置位置值為0,對于頂點著色器中的layout(location = 0)
// 參數2: 頂點屬性的大小,vec3,所以大小是3
// 參數3: 數據類型, float
// 參數4: 是否需要將數據標準化,會將數據映射到[-1,1]或是[0,1](取決于是否unsigned)
// 參數5: 步長
// 參數6: 數據偏移量
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

3. 頂點數組對象(Vertex Array Object - VAO)
我們可以將頂點數據VBO以及頂點屬性都存儲在一塊特定的緩沖區中,在之后的繪制中我們就只需要切換這塊特定的緩沖區即可,這塊緩沖區就叫做VAO
接下來我們來創建、綁定VAO,請注意,綁定VAO需要在VBO之前進行

// 創建VAO
unsigned int VAO;
glGenVertexArrays(1, &VAO);
// 綁定VAO
glBindVertexArray(VAO);// ...VBO和頂點屬性相關// 解綁VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解綁VAO
glBindVertexArray(0);

4. 頂點著色器(Vertex Shader)
接下來我們需要編寫頂點著色器的代碼
在OpenGL中使用名為GLSL(OpenGL Shading Language)語言來編寫
我們會先使用硬編碼的方式進行編寫,之后會拆分到一個獨立的文件中

// 版本號
#version 330 core
// 聲明輸入坐標,從位置0
layout (location = 0) in vec3 aPos;void main()
{// 設置頂點著色器的輸出gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}

接下來我們需要創建著色器對象,傳入我們編寫的代碼并且編譯它以供GPU使用

// 頂點著色器代碼
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";// 創建頂點著色器
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 設置著色器代碼,第二個參數為傳入的源碼字符串數量
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
// 編譯
glCompileShader(vertexShader);// 可選,判斷著色器是否編譯成功
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;glfwTerminate();return -1;
}

5. 片元(片段、像素)著色器(Fragment Shader/Pixel Shader)
接下來我們通過片元著色器計算像素最后的顏色輸出

#version 330 core
// 輸出顏色 vec4(r,g,b,a)
out vec4 FragColor;void main()
{// 固定輸出橘黃色FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
} 

同樣,我們需要創建并編譯片元著色器

// 片元著色器代碼
const char* fragmentShaderSource = "#version 330 core\n"
"layout (location = 0) out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0";// 創建片元著色器
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;glfwTerminate();return -1;
}

6. 著色器程序
著色器程序對象是多個著色器鏈接后的結果

// 創建著色器程序
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
// 添加著色器
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
// 鏈接
glLinkProgram(shaderProgram);glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "[Error] Shader program link failed!\n" << infoLog << std::endl;glfwTerminate();return -1;
}// 鏈接完成后即可刪除著色器對象
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

7. 繪制三角形
準備就緒! 接下來開始繪制三角形,在我們的主循環中,輸入處理之后

// 使用著色器程序
glUseProgram(shaderProgram);
// 綁定接下來要使用的VAO
glBindVertexArray(VAO);
// 繪制
glDrawArrays(GL_TRIANGLES, 0, 3);

編譯執行,順利的話可以看到
繪制三角形

三、繪制矩形

1. 元素(索引)緩沖對象(Element(Index) Buffer Object - EBO)
接下來我們來了解第三種緩沖對象,EBO
EBO中存儲的實際上是一系列成組的索引,這些索引表示我們應該用VBO中的那些頂點來繪制一個三角形
現在我們修改一下我們的vertices數據,并增加indices數據

// 頂點數據,基于標準化設備坐標(NDC)
float vertices[] = {0.5f, 0.5f, 0.0f,   // 右上角0.5f, -0.5f, 0.0f,  // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f   // 左上角
};// 索引數據
unsigned int indices[] = {0, 1, 3, // 第一個三角形1, 2, 3  // 第二個三角形
};

現在我們頂點數據中有四個點,來表示矩形的四個角的坐標
而indices中有六個索引數據,每三個一組,共兩組
第一組:0, 1, 3 表示GPU應該使用VBO中的第0個,第1個和第3個頂點來組成一個三角形
第二組:1, 2, 3 則表示GPU應該使用VBO中的第1個,第2個和第3個頂點來組成一個三角形
EBO的創建綁定與VBO類似

// VAO、VBO創建、綁定、設置// 創建EBO
unsigned int EBO;
glGenBuffers(1, &EBO);
// 綁定EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// 設置EBO數據
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//解綁VAO、VBO
//請注意這里不解綁EBO,在VAO中依然會使用到

然后修改繪制調用的指令

	// 繪制// glDrawArrays(GL_TRIANGLES, 0, 3);// 通過EBO繪制// 參數2: 繪制頂點的個數// 參數3: 索引的數據類型// 偏移量glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

編譯運行,順利的話可以看到
繪制矩形
我們還可以啟用線框模式來查看繪制的兩個三角形

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

線框模式繪制矩形

全部代碼

#include <iostream>#include <glad/glad.h>
#include <GLFW/glfw3.h>// 頂點數據,基于標準化設備坐標(NDC)
float vertices[] = {0.5f, 0.5f, 0.0f,   // 右上角0.5f, -0.5f, 0.0f,  // 右下角-0.5f, -0.5f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f   // 左上角
};// 索引數據
unsigned int indices[] = {0, 1, 3, // 第一個三角形1, 2, 3  // 第二個三角形
};// 頂點著色器代碼
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";// 片元著色器代碼
const char* fragmentShaderSource = "#version 330 core\n"
"layout (location = 0) out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\0";void ProcessInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE)){glfwSetWindowShouldClose(window, true);}
}void OnSetFrameBufferSize(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);GLFWwindow* window = glfwCreateWindow(1280, 720, "OpenGLRenderer", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}glfwSetFramebufferSizeCallback(window, OnSetFrameBufferSize);// 創建VAOunsigned int VAO;glGenVertexArrays(1, &VAO);// 綁定VAOglBindVertexArray(VAO);// 創建緩沖unsigned int VBO;glGenBuffers(1, &VBO);// 綁定創建的緩沖到VBOglBindBuffer(GL_ARRAY_BUFFER, VBO);// 寫入數據到VBO// 這里第四個參數表示我們希望顯卡如何管理給定的數據// GL_STATIC_DRAW 數據不會或幾乎不會改變// GL_DYNAMIC_DRAW 數據會被改變很多// GL_STREAM_DRAW 數據每次繪制時都會改變glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 設置頂點屬性// 參數1: 設置位置值為0,對于頂點著色器中的layout(location = 0)// 參數2: 頂點屬性的大小,vec3,所以大小是3// 參數3: 數據類型, float// 參數4: 是否需要將數據標準化,會將數據映射到[-1,1]或是[0,1](取決于是否unsigned)// 參數5: 步長// 參數6: 數據偏移量glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 創建EBOunsigned int EBO;glGenBuffers(1, &EBO);// 綁定EBOglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);// 設置EBO數據glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// 解綁VBOglBindBuffer(GL_ARRAY_BUFFER, 0);// 解綁VAOglBindVertexArray(0);// 創建頂點著色器unsigned int vertexShader;vertexShader = glCreateShader(GL_VERTEX_SHADER);// 設置著色器代碼,第二個參數為傳入的源碼字符串數量glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);// 編譯glCompileShader(vertexShader);// 可選,判斷著色器是否編譯成功int success;char infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "[Error] Vertex shader compile failed!\n" << infoLog << std::endl;glfwTerminate();return -1;}// 創建片元著色器unsigned int fragmentShader;fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "[Error] Fragment shader compile failed!\n" << infoLog << std::endl;glfwTerminate();return -1;}// 創建著色器程序unsigned int shaderProgram;shaderProgram = glCreateProgram();// 添加著色器glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);// 鏈接glLinkProgram(shaderProgram);glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "[Error] Shader program link failed!\n" << infoLog << std::endl;glfwTerminate();return -1;}// 鏈接完成后即可刪除著色器對象glDeleteShader(vertexShader);glDeleteShader(fragmentShader);while (!glfwWindowShouldClose(window)){glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);ProcessInput(window);// 使用著色器程序glUseProgram(shaderProgram);// 綁定接下來要使用的VAOglBindVertexArray(VAO);// 線框模式//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);// 繪制// glDrawArrays(GL_TRIANGLES, 0, 3);// 通過EBO繪制// 參數2: 繪制頂點的個數// 參數3: 索引的數據類型// 偏移量glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glfwSwapBuffers(window);glfwPollEvents();}glfwTerminate();return 0;
}

下接:https://blog.csdn.net/weixin_44506615/article/details/149894322?spm=1001.2014.3001.5502

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

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

相關文章

Mysql的事務是什么?

簡單來說&#xff0c;MySQL 實現事務的核心就像是給你的數據庫操作加了一套“保險和存檔”機制。它確保了你的操作要么全部成功&#xff0c;要么全部失敗&#xff0c;并且在面對多人同時操作、系統突然崩潰等情況時&#xff0c;數據依然可靠、準確。 為什么需要事務呢&#xff…

測試開發:Python+Django實現接口測試工具

【測試開發天花板】DjangoVuePyTest打造企業級自動化平臺&#xff5c;能寫進簡歷的硬核項目最近被幾個公司實習生整自閉了&#xff0c;沒有基礎&#xff0c;想學自動化又不知道怎么去學&#xff0c;沒有方向沒有頭緒&#xff0c;說白了其實就是學習過程中沒有成就感&#xff0c…

TFS-2022《A Novel Data-Driven Approach to Autonomous Fuzzy Clustering》

核心思想 這篇論文的核心思想是提出一種全新的、數據驅動的自主模糊聚類&#xff08;Autonomous Fuzzy Clustering, AFC&#xff09;算法。其核心創新在于&#xff0c;它巧妙地結合了模糊聚類的靈活性和基于中位數&#xff08;medoids&#xff09;聚類的可解釋性&#xff0c;并…

ELK是什么

ELK 是一個廣受歡迎的開源技術棧&#xff0c;用于實時采集、處理、存儲、搜索、分析和可視化海量的日志數據&#xff08;log&#xff09;和機器生成的數據&#xff08;machine data&#xff09;&#xff0c;尤其是在 IT 系統監控、應用故障排查、安全分析和業務智能等領域應用廣…

[硬件電路-123]:模擬電路 - 信號處理電路 - 常見的高速運放芯片、典型電路、電路實施注意事項

一、高速運放常見芯片型號及特性高速運放&#xff08;高速運算放大器&#xff09;通常指帶寬&#xff08;GBW&#xff09;超過10MHz、壓擺率&#xff08;SR&#xff09;高于10V/μs的器件&#xff0c;適用于視頻處理、通信系統、高速數據采集等場景。以下是典型芯片及其特性&am…

關于解決WinRiver項目動態XmlElement的序列化與反序列化的問題

關于解決WinRiver項目動態XmlElement的序列化與反序列化的問題 一、WinRiver項目流量匯總XML內容 1.1、索引可變,索引下 XmlElement 元素內容固定 1.2、如何將對象 BottomTrack 的動態內容序列化為 XML ? 1.3、如何將 XML 動態內容反序列化為對象 BottomTrack ? 二、XML 動態…

【力扣 Hot100】 刷題日記

D3 128.最長連續序列 錯解 class Solution {public int longestConsecutive(int[] nums) {Arrays.sort(nums);int maxCnt 0;int cnt 0;for (int i 0; i < nums.length - 1; i) {if(nums[i] ! nums[i 1] - 1){//如果不連續&#xff0c;取cnt與maxCnt較大值&#xff0c…

飛算JavaAI編程插件:以AI之力賦能Java開發,讓編碼效率再升級

你是否希望自己敲代碼的時候總有一位大佬在你背后幫你保駕護航。想象一下&#xff0c;當你對著Java編輯器敲代碼時&#xff0c;身后站了位“隱形大神”——你剛敲出for&#xff0c;它就預判到你要遍歷集合&#xff0c;自動補全帶泛型的循環邏輯&#xff1b;你手滑把equals寫成&…

機器學習通關秘籍|Day 03:決策樹、隨機森林與線性回歸

目錄 一、決策樹 1、概念 2、基于信息增益的決策樹的建立 &#xff08;1&#xff09;信息熵 &#xff08;2&#xff09;信息增益 &#xff08;3&#xff09;信息增益決策樹建立步驟 3、基于基尼指數的決策樹的建立 4、API 二、隨機森林 1、算法原理 2、API 三、線性…

C++進階—C++的類型轉換

第一章&#xff1a;C語言中的類型轉換在C語言中&#xff0c;如果賦值運算符左右兩側類型不同&#xff0c;或者形參與實參類型不匹配&#xff0c;或者返回值類型與接收返回值類型不一致時&#xff0c;就需要發生類型轉化&#xff0c;C語言中總共有兩種形式的類型轉換&#xff1a…

基于Flask的微博話題多標簽情感分析系統設計

基于Flask的微博話題情感分析系統設計與實現 一、項目概述 本項目是一個輕量化的微博話題情感分析系統&#xff0c;通過Flask框架整合情感分析模型&#xff0c;實現對微博話題及評論的情感標簽識別與結果展示。系統面向普通用戶和研究者&#xff0c;提供簡單易用的操作界面&…

TDengine 中 TDgpt 的模型評估工具

模型評估工具 TDgpt 在企業版中提供預測分析模型和異常檢測模型有效性評估工具 analytics_compare&#xff0c;該工具能夠使用 TDengine 中的時序數據作為 回測依據&#xff0c;評估不同預測模型或訓練模型的有效性。該工具在開源版本中不可用使用評估工具&#xff0c;需要在其…

【DL學習筆記】DataLoader類功能和參數說明

文章目錄一、Dataset 與 DataLoader 功能介紹抽象類Dataset的作用DataLoader 作用兩者關系二、torch.utils.data.DataLoader代碼示例常用參數圖示num_workers設置多少合適數據加載子進程如何并行的pin_memorysampler兩種sampler順序采樣 SequentialSampler隨機采樣 RandomSampl…

JVM中年輕代、老年代、永久代(或元空間)、Eden區和Survivor區概念介紹

在Java虛擬機&#xff08;JVM&#xff09;中&#xff0c;內存管理是自動化的&#xff0c;這主要通過垃圾回收機制實現。JVM將堆內存劃分為不同的區域&#xff0c;以便更高效地管理和回收對象。以下是關于年輕代、老年代、永久代&#xff08;或元空間&#xff09;、Eden區和Surv…

譯 | BBC Studios團隊:貝葉斯合成控制方法SCM的應用案例

來自上傳文件中的文章《Using Causal Inference for Measuring Marketing Impact: How BBC Studios Utilises Geo Holdouts and CausalPy》 本篇介紹了在傳統A/B測試不適用時&#xff0c;如何利用貝葉斯合成控制方法和地理區域保留來評估營銷活動效果。其亮點在于通過構建“反事…

Web開發-PHP應用TP框架MVC模型路由訪問模版渲染安全寫法版本漏洞

我們先使用/index.php/index/index/test&#xff0c;就是圖中的test()方法 /index.php/index/index/index&#xff0c;這個回顯就是111 http://127.0.0.1:83/index.php/index/index/test2?x123456 public function test2() {$x$_GET[x];return $x; } 這里再做一個案例更詳細一…

FreeRTOS列表系統深度解析

FreeRTOS列表系統深度解析 一、核心數據結構 1. 列表控制塊 (List_t) typedef struct xLIST {volatile UBaseType_t uxNumberOfItems; // 當前列表項數量ListItem_t * pxIndex; // 遍歷指針&#xff08;用于輪詢調度&#xff09;MiniListItem_t xListEnd; …

《Linux編譯器:gcc/g++食用指南》

堅持用 清晰易懂的圖解 代碼語言&#xff0c;讓每個知識點變得簡單&#xff01; &#x1f680;呆頭個人主頁詳情 &#x1f331; 呆頭個人Gitee代碼倉庫 &#x1f4cc; 呆頭詳細專欄系列 座右銘&#xff1a; “不患無位&#xff0c;患所以立。” 《Linux編譯器&#xff1a;GCC…

SparkKV轉換算子實戰解析

目錄 KV算子 parallelizePairs mapToPair mapValues groupByKey reduceByKey sortByKey 算子應用理解 reduceByKey和groupByKey的區別 groupByKeymapValues實現KV數據的V的操作 改進用reduceByKey groupby通過K和通過V分組的模板代碼 問題集錦 寶貴的經驗 這里會…

深度解析 TCP 三次握手與四次揮手:從原理到 HTTP/HTTPS 的應用

TCP 的三次握手和四次揮手是網絡通信的基石&#xff0c;無論是 HTTP 還是 HTTPS&#xff0c;它們都依賴 TCP 提供可靠的傳輸層服務。本文將用萬字篇幅&#xff0c;結合 Mermaid 圖表和代碼示例&#xff0c;深入講解 TCP 三次握手、四次揮手的原理、過程、狀態變化&#xff0c;以…