OpenGL學習筆記(五):Textures 紋理

文章目錄

  • 紋理坐標
  • 紋理環繞方式
  • 紋理過濾——處理紋理分辨率低的情況
  • 多級漸遠紋理Mipmap——處理紋理分辨率高的情況
  • 加載與創建紋理 ( <stb_image.h> )
  • 生成紋理
  • 應用紋理
  • 紋理單元
  • 練習1
  • 練習2
  • 練習3
  • 練習4


通過上一篇著色部分的學習,我們可以為每個頂點添加顏色來增加圖形的細節,從而創建出有趣的圖像。當需要給圖形賦予真實顏色的時候,不大可能為每一個頂點指定一個顏色,通常會采用紋理貼圖。

本篇開始學習使用紋理(Texture)來添加物體的細節。我們要做的工作是告訴OpenGL該怎樣對紋理采樣。

紋理相關原理參考:GAMES101學習筆記(五):Texture 紋理(紋理映射、重心坐標、紋理貼圖)

紋理坐標

為了能夠把紋理映射(Map)到三角形上,我們需要指定三角形的每個頂點各自對應紋理的哪個部分。

每個頂點就會關聯著一個紋理坐標(Texture Coordinate),用來標明該從紋理圖像的哪個部分采樣(采集片段顏色)。之后在圖形的其它片段上進行片段插值(Fragment Interpolation)。

紋理坐標在x和y軸上,范圍為0到1之間(2D紋理圖像)。使用紋理坐標獲取紋理顏色叫做采樣(Sampling)。
紋理坐標的原點(0, 0)在紋理圖片的左下角,終止于(1, 1),即紋理圖片的右上角。

float texCoords[] = {0.0f, 0.0f, // 左下角1.0f, 0.0f, // 右下角0.5f, 1.0f  // 上中
};

下面的圖片展示了如何把紋理坐標映射到三角形上的:
在這里插入圖片描述
頂點結構將更新為如下:

float vertices[] = {// positions        // colors			// texCoords0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  0.0f, 0.0f,		// bottom right-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  1.0f, 0.0f,		// bottom left0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f,  0.5f, 1.0f   	// top 
};

由于我們添加了一個額外的頂點屬性,我們必須告訴OpenGL我們新的頂點格式:
在這里插入圖片描述

紋理環繞方式

紋理坐標的范圍通常是從(0, 0)到(1, 1),那如果紋理坐標設置在范圍之外會發生什么?
OpenGL默認的行為是重復這個紋理圖像(我們基本上忽略浮點紋理坐標的整數部分)OpenGL也提供了更多的選擇

環繞方式描述
GL_REPEAT對紋理的默認行為。重復紋理圖像。
GL_MIRRORED_REPEATGL_REPEAT一樣,但每次重復圖片是鏡像放置的。
GL_CLAMP_TO_EDGE紋理坐標會被約束在0到1之間,超出的部分會重復紋理坐標的邊緣,產生一種邊緣被拉伸的效果。
GL_CLAMP_TO_BORDER超出的坐標為用戶指定的邊緣顏色。

當紋理坐標超出默認范圍時,每個選項都有不同的視覺效果輸出:
在這里插入圖片描述
可以使用glTexParameter函數對單獨的一個坐標軸設置(st(如果是使用3D紋理那么還有一個r)它們和xyz是等價的):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
  • 第一個參數指定了紋理目標;我們使用的是2D紋理,因此紋理目標是GL_TEXTURE_2D
  • 第二個參數需要我們指定設置的選項與應用的紋理軸。我們打算配置的是WRAP選項,并且指定S和T軸。
  • 最后一個參數需要我們傳遞一個環繞方式(Wrapping),即上面表格中的4種方式。

如果我們選擇GL_CLAMP_TO_BORDER選項,我們還需要指定一個邊緣的顏色。這需要使用glTexParameter函數的fv后綴形式:

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

紋理過濾——處理紋理分辨率低的情況

OpenGL需要知道怎樣將紋理像素(Texture Pixel,也叫Texel)映射到紋理坐標。但

  • 紋理坐標的精度是無限的,可以是任意浮點值。(紋理坐標不依賴于分辨率Resolution)
  • 紋理像素是有限的(圖片分辨率)

當物體很大但是紋理的分辨率很低的時候,就會出現鋸齒現象。
在GAMES101課程中我們了解了雙線性插值和雙三次插值的方式來做抗鋸齒。

OpenGL有對于紋理過濾(Texture Filtering)的選項。紋理過濾有很多個選項,但是現在我們只討論最重要的兩種:

  • GL_NEAREST(也叫鄰近過濾,Nearest Neighbor Filtering)
    OpenGL默認的紋理過濾方式。當設置為GL_NEAREST的時候,OpenGL會選擇中心點最接近紋理坐標的那個像素。

  • GL_LINEAR(也叫線性過濾,(Bi)linear Filtering)
    它會基于紋理坐標附近的紋理像素,計算出一個插值,近似出這些紋理像素之間的顏色。一個紋理像素的中心距離紋理坐標越近,那么這個紋理像素的顏色對最終的樣本顏色的貢獻越大。

  • GL_LINEAR可以產生更真實的輸出,但有些開發者更喜歡8-bit風格,所以他們會用GL_NEAREST選項

當進行放大(Magnify)和縮小(Minify)操作的時候可以設置紋理過濾的選項,比如你可以在紋理被縮小的時候使用鄰近過濾,被放大時使用線性過濾。我們需要使用glTexParameter函數為放大和縮小指定過濾方式。這段代碼看起來會和紋理環繞方式的設置很相似:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多級漸遠紋理Mipmap——處理紋理分辨率高的情況

想象一下,假設我們有一個包含著上千物體的大房間,每個物體上都有紋理。有些物體會很遠,但其紋理會擁有與近處物體同樣高的分辨率。由于遠處的物體可能只產生很少的片段,OpenGL從高分辨率紋理中為這些片段獲取正確的顏色值就很困難,因為它需要對一個跨過紋理很大部分的片段只拾取一個紋理顏色。在小物體上這會產生不真實的感覺,更不用說對它們使用高分辨率紋理浪費內存的問題了。

OpenGL使用一種叫做 多級漸遠紋理(Mipmap) 的概念來解決這個問題,它簡單來說就是一系列的紋理圖像,后一個紋理圖像是前一個的二分之一。多級漸遠紋理背后的理念很簡單:距觀察者的距離超過一定的閾值,OpenGL會使用不同的多級漸遠紋理,即最適合物體的距離的那個。由于距離遠,解析度不高也不會被用戶注意到。同時,多級漸遠紋理另一加分之處是它的性能非常好。讓我們看一下多級漸遠紋理是什么樣子的:

在這里插入圖片描述
使用glGenerateMipmap函數,在創建完一個紋理后調用它OpenGL就會生成Mipmap。后面的教程中會看到該如何使用它。

在渲染中切換多級漸遠紋理級別(Level)時,OpenGL在兩個不同級別的多級漸遠紋理層之間會產生不真實的生硬邊界。就像普通的紋理過濾一樣,切換多級漸遠紋理級別時你也可以在兩個不同多級漸遠紋理級別之間使用NEARESTLINEAR過濾。為了指定不同多級漸遠紋理級別之間的過濾方式,可以使用下面四個選項中的一個代替原有的過濾方式:

過濾方式描述
GL_NEAREST_MIPMAP_NEAREST使用最鄰近的多級漸遠紋理來匹配像素大小,并使用鄰近插值進行紋理采樣
GL_LINEAR_MIPMAP_NEAREST使用最鄰近的多級漸遠紋理級別,并使用線性插值進行采樣
GL_NEAREST_MIPMAP_LINEAR在兩個最匹配像素大小的多級漸遠紋理之間進行線性插值,使用鄰近插值進行采樣
GL_LINEAR_MIPMAP_LINEAR在兩個鄰近的多級漸遠紋理之間使用線性插值,并使用線性插值進行采樣

就像紋理過濾一樣,我們可以使用glTexParameteri將過濾方式設置為前面四種提到的方法之一:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

一個常見的錯誤是,將放大過濾的選項設置為多級漸遠紋理過濾選項之一。這樣沒有任何效果
因為多級漸遠紋理主要是使用在紋理被縮小的情況下的,紋理放大不會使用多級漸遠紋理
為放大過濾設置多級漸遠紋理的選項會產生一個GL_INVALID_ENUM錯誤代碼。

加載與創建紋理 ( <stb_image.h> )

接下來我們加載本地圖片在OpenGL中創建紋理,這里我們使用一個支持多種流行格式的圖像加載庫stb_image.h庫。

stb_image.h是Sean Barrett的一個非常流行的單頭文件圖像加載庫,它能夠加載大部分流行的文件格式,并且能夠很簡單得整合到你的工程之中。stb_image.h可以在這里下載。

在工程中包含該庫時,需要定義宏:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

使用stb_image.hstbi_load函數加載一張木箱的圖片:

int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);

stbi_load函數原型:

unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)

首先接受一個圖像文件的路徑作為輸入。接下來它需要三個int作為它的第二、第三和第四個參數,stb_image.h將會用圖像的寬度高度顏色通道的個數填充這三個變量。我們之后生成紋理的時候會用到的圖像的寬度和高度。

生成紋理

和之前生成的其他OpenGL對象一樣,紋理也是使用ID引用的。

使用glGenTextures創建紋理:

unsigned int texture;
glGenTextures(1, &texture);
  • 第一個參數:生成紋理的數量
  • 第二個參數:一個unsigned int數組,用于存儲指定數量的紋理

像其他對象一樣,我們需要綁定它,讓之后任何的紋理指令都可以配置當前綁定的紋理:

glBindTexture(GL_TEXTURE_2D, texture);

紋理綁定之后,我們可以使用前面載入的圖片數據生成一個紋理了。

通過glTexImage2D來生成:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
  • 第一個參數指定了紋理目標(Target)
    設置為GL_TEXTURE_2D意味著會生成與當前綁定的紋理對象在同一個目標上的紋理(任何綁定到GL_TEXTURE_1DGL_TEXTURE_3D的紋理不會受到影響)。
  • 第二個參數為紋理指定多級漸遠紋理的級別,如果你希望單獨手動設置每個多級漸遠紋理的級別的話。這里我們填0,也就是基本級別。
  • 第三個參數指定紋理儲存格式。我們的圖像只有RGB值,因此我們也把紋理儲存為RGB值。
  • 第四個參數指定最終的紋理的寬度。
  • 第五個參數指定最終的紋理的高度。
  • 第六個參數應該總是被設為0(歷史遺留的問題)
  • 第七個參數定義了源圖的格式。使用RGB值加載這個圖像
  • 第八個參數定義了源圖的數據類型。把它們儲存為char(byte)數組,我們將會傳入對應值。
  • 最后一個參數是真正的圖像數據。

當調用glTexImage2D時,當前綁定的紋理對象就會被附加上紋理圖像數據。目前只加載基本級別(Base-level)的紋理圖像。
如果要使用多級漸遠紋理,在生成紋理之后還要調用glGenerateMipmap,這會為當前綁定的紋理自動生成所有需要的多級漸遠紋理。
綁定好紋理之后,就可以使用stbi_image_free釋放圖像內存了。


生成一個紋理的完整過程:

unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 為當前綁定的紋理對象設置環繞、過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加載并生成紋理
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);
}
else
{std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);

應用紋理

接下來,我們會使用之前繪制的矩形來應用紋理,更新頂點數據:

float vertices[] = {
//    ---- 位置 ----       ---- 顏色 ----     - 紋理坐標 -0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下-0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
};

由于我們添加了一個額外的頂點屬性,我們必須告訴OpenGL我們新的頂點格式:

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);

在這里插入圖片描述

注意,我們同樣需要調整前面兩個頂點屬性的步長參數為8 * sizeof(float)


調整頂點著色器,使其能夠接受頂點坐標aTexCoord為一個頂點屬性,并把坐標傳給片段著色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;out vec3 ourColor;
out vec2 TexCoord;void main()
{gl_Position = vec4(aPos, 1.0);ourColor = aColor;TexCoord = aTexCoord;
}

調整片段著色器,把輸出變量TexCoord作為輸入變量。

片段著色器也應該能訪問紋理對象,但是我們怎樣能把紋理對象傳給片段著色器呢?

GLSL有一個供紋理對象使用的內建數據類型,叫做采樣器(Sampler),它以紋理類型作為后綴,比如sampler1D、sampler3D,或在我們的例子中的sampler2D。我們可以簡單聲明一個uniform sampler2D把一個紋理添加到片段著色器中,稍后我們會把紋理賦值給這個uniform。

#version 330 core
out vec4 FragColor;in vec3 ourColor;
in vec2 TexCoord;uniform sampler2D ourTexture;void main()
{FragColor = texture(ourTexture, TexCoord);
}

使用GLSL內建的texture函數來采樣紋理的顏色,它第一個參數是紋理采樣器,第二個參數是對應的紋理坐標texture函數會使用之前設置的紋理參數對相應的顏色值進行采樣。這個片段著色器的輸出就是紋理的(插值)紋理坐標上的(過濾后的)顏色。


現在只剩下在調用glDrawElements之前綁定紋理了,它會自動把紋理賦值給片段著色器的采樣器:

glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

效果如下圖: (完整源碼參考)

在這里插入圖片描述

紋理單元

sampler2D變量是個uniform,但我們沒有用glUniform給它賦值。
實際上可以使用glUniform1i給紋理采樣器分配一個位置值,這樣的話我們能夠在一個片段著色器中設置多個紋理。
一個紋理的這個位置值通常稱為一個紋理單元(Texture Unit)

紋理的默認紋理單元是0(默認的激活狀態),所以前面部分我們沒有分配一個位置值。但并非所有圖形驅動程序都指定了默認紋理單元。

紋理單元的主要目的是讓我們在著色器中可以使用多于一個的紋理。通過把紋理單元賦值給采樣器,我們可以一次綁定多個紋理,只要我們首先激活對應的紋理單元。就像glBindTexture一樣,我們可以使用glActiveTexture激活紋理單元,傳入我們需要使用的紋理單元:

glActiveTexture(GL_TEXTURE0); // 在綁定紋理之前先激活紋理單元
glBindTexture(GL_TEXTURE_2D, texture);

激活紋理單元之后,接下來的glBindTexture函數調用會綁定這個紋理到當前激活的紋理單元,紋理單元GL_TEXTURE0默認總是被激活,所以我們在前面的例子里當我們使用glBindTexture的時候,無需激活任何紋理單元。但當我們想要應用多個紋理時,就要先激活對應的紋理單元,

OpenGL至少保證有16個紋理單元供你使用,也就是說你可以激活從GL_TEXTURE0GL_TEXTRUE15。它們都是按順序定義的,所以我們也可以通過GL_TEXTURE0 + 8的方式獲得GL_TEXTURE8,這在當我們需要循環一些紋理單元的時候會很有用。


編輯片段著色器來接收另一個采樣器,最終輸出顏色是兩個紋理的結合:

#version 330 core
out vec4 FragColor;in vec3 ourColor;
in vec2 TexCoord;uniform sampler2D texture1;
uniform sampler2D texture2;void main()
{FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

GLSL內建的mix函數需要接受兩個值作為參數,并對它們根據第三個參數進行線性插值。如果第三個值是0.0,它會返回第一個輸入;如果是1.0,會返回第二個輸入值。0.2會返回80%的第一個輸入顏色和20%的第二個輸入顏色,即返回兩個紋理的混合色。


現在載入并創建另一個紋理,第二個紋理我們使用一張笑臉表情圖片:

unsigned char *data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
if (data)
{glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);
}

注意,我們現在要讀取一張包含alpha(透明度)通道的.png圖片,這意味著我們現在需要使用GL_RGBA參數,指定該圖片數據包含了alpha通道;否則OpenGL將無法正確解析圖片數據。

為了使用第二個紋理(以及第一個),我們必須改變一點渲染流程:

  • 先綁定兩個紋理到對應的紋理單元
  • 然后定義哪個uniform采樣器對應哪個紋理單元:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

我們還要通過使用glUniform1i設置每個采樣器的方式告訴OpenGL每個著色器采樣器屬于哪個紋理單元。我們只需要設置一次即可,所以這個會放在渲染循環的前面:

ourShader.use(); // 不要忘記在設置uniform變量之前激活著色器程序!
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手動設置
ourShader.setInt("texture2", 1); // 或者使用著色器類設置while(...) 
{[...]
}

通過使用glUniform1i設置采樣器,我們保證了每個uniform采樣器對應著正確的紋理單元。你應該能得到下面的結果:
在這里插入圖片描述
紋理上下顛倒了!這是因為OpenGL要求y軸0.0坐標是在圖片的底部的,但是圖片的y軸0.0坐標通常在頂部
stb_image.h能夠在圖像加載時幫助我們翻轉y軸,只需要在加載任何圖像前加入以下語句即可:

stbi_set_flip_vertically_on_load(true);

(完整代碼參考)

在這里插入圖片描述

練習1

修改片段著色器,僅讓笑臉圖案朝另一個方向看

//FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);// 修改片段著色器,僅讓笑臉圖案朝另一個方向看
FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(1.0 - TexCoord.x, TexCoord.y)), 0.2);
}

在這里插入圖片描述

練習2

嘗試用不同的紋理環繞方式,設定一個從0.0f到2.0f范圍內的(而不是原來的0.0f到1.0f)紋理坐標。
試試看能不能在箱子的角落放置4個笑臉:參考解答

在這里插入圖片描述

在這里插入圖片描述

練習3

嘗試在矩形上只顯示紋理圖像的中間一部分,修改紋理坐標,達到能看見單個的像素的效果。嘗試使用GL_NEAREST的紋理過濾方式讓像素顯示得更清晰:參考解答

在這里插入圖片描述

練習4

使用一個uniform變量作為mix函數的第三個參數來改變兩個紋理可見度,使用上和下鍵來改變箱子或笑臉的可見度:參考解答

處理鍵盤輸入:

void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS){mixValue += 0.001f; // change this value accordingly (might be too slow or too fast based on system hardware)if(mixValue >= 1.0f)mixValue = 1.0f;}if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS){mixValue -= 0.001f; // change this value accordingly (might be too slow or too fast based on system hardware)if (mixValue <= 0.0f)mixValue = 0.0f;}
}

修改片段著色器:

#version 330 core
out vec4 FragColor;in vec3 ourColor;
in vec2 TexCoord;uniform float mixValue;// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;void main()
{// linearly interpolate between both texturesFragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), mixValue);
}

在渲染循環中,實時修改mixValue的值

ourShader.setFloat("mixValue", mixValue);

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

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

相關文章

代理模式——C++實現

目錄 1. 代理模式簡介 2. 代碼示例 1. 代理模式簡介 代理模式是一種行為型模式。 代理模式的定義&#xff1a;由于某些原因需要給某對象提供一個代理以控制該對象的訪問。這時&#xff0c;訪問對象不適合或者不能直接訪問引用目標對象&#xff0c;代理對象作為訪問對象和目標…

Vue3 表單:全面解析與最佳實踐

Vue3 表單&#xff1a;全面解析與最佳實踐 引言 隨著前端技術的發展&#xff0c;Vue.js 已經成為最受歡迎的前端框架之一。Vue3 作為 Vue.js 的最新版本&#xff0c;帶來了許多改進和新的特性。其中&#xff0c;表單處理是 Vue 應用中不可或缺的一部分。本文將全面解析 Vue3 …

C++11新特性之范圍for循環

1.介紹 C11標準之前&#xff0c;使用for循環遍歷數組或容器&#xff0c;只能使用以下結構&#xff1a; for&#xff08;表達式1&#xff1b;表達式2&#xff1b;表達式3&#xff09;{ 循環體 } 那么在C11標準中&#xff0c;除了上面的方法外&#xff0c;又引入了一種全新的語…

攻防世界 fileclude

代碼審計 WRONG WAY! <?php include("flag.php"); highlight_file(__FILE__);//高亮顯示文件的源代碼 if(isset($_GET["file1"]) && isset($_GET["file2"]))//檢查file1和file2參數是否存在 {$file1 $_GET["file1"];$fi…

圖書管理系統 Axios 源碼__獲取圖書列表

目錄 核心功能 源碼介紹 1. 獲取圖書列表 技術要點 適用人群 本項目是一個基于 HTML Bootstrap JavaScript Axios 開發的圖書管理系統&#xff0c;可用于 添加、編輯、刪除和管理圖書信息&#xff0c;適合前端開發者學習 前端交互設計、Axios 數據請求 以及 Bootstrap 樣…

Vue 響應式渲染 - 列表布局和v-html

Vue 漸進式JavaScript 框架 基于Vue2的學習筆記 - Vue 響應式渲染 - 列表布局和v-html 目錄 列表布局 簡單渲染列表 顯示索引值 點擊變色 V-html 作用 注意 采用策略 應用 總結 列表布局 簡單渲染列表 Data中設置狀態&#xff0c;是一個數組格式的默認信息。 然后…

如何實現一個CLI命令行功能 | python 小知識

如何實現一個CLI命令行功能 | python 小知識 在現代軟件開發中&#xff0c;命令行界面&#xff08;CLI&#xff09;的設計與交互至關重要。Click是一個強大的Python庫&#xff0c;專門用于快速創建命令行界面&#xff0c;以其簡單易用性和豐富的功能贏得了開發者的青睞。本文將…

[SAP ABAP] Debug Skill

SAP ABAP Debug相關資料 [SAP ABAP] DEBUG ABAP程序中的循環語句 [SAP ABAP] 靜態斷點的使用 [SAP ABAP] 在ABAP Debugger調試器中設置斷點 [SAP ABAP] SE11 / SE16N 修改標準表(慎用)

kamailio-Core 說明書 版本:Kamailio SIP Server v6.0.x(穩定版)

Core 說明書 版本&#xff1a;Kamailio SIP Server v6.0.x&#xff08;穩定版&#xff09; 概述 本教程收集了 Kamailio 導出的函數和參數 core 添加到配置文件中。 注意&#xff1a;此頁面上的參數不按字母順序排列。 結構 kamailio.cfg 的結構可以看作是三個部分&#xff…

.Net / C# 繁體中文 與 簡體中文 互相轉換, 支持地方特色詞匯

版本號 Nuget 搜索 “OpenCCNET”, 注意別找錯, 好多庫的名字都差不多 支持 “繁,簡” 的互相轉換, 支持多個地區常用詞匯的轉換, 還支持 日文的新舊轉換. OpenCC 在 .Net 中的實現 https://github.com/CosineG/OpenCC.NET <PackageReference Include"OpenCCNET"…

Redis腦裂問題詳解及解決方案

Redis是一種高性能的內存數據庫&#xff0c;廣泛應用于緩存、消息隊列等場景。然而&#xff0c;在分布式Redis集群中&#xff0c;腦裂問題&#xff08;Split-Brain&#xff09;是一個需要特別關注的復雜問題。本文將詳細介紹Redis腦裂問題的成因、影響及解決方案。 一、什么是…

LLMs之OpenAI o系列:OpenAI o3-mini的簡介、安裝和使用方法、案例應用之詳細攻略

LLMs之OpenAI o系列&#xff1a;OpenAI o3-mini的簡介、安裝和使用方法、案例應用之詳細攻略 目錄 相關文章 LLMs之o3&#xff1a;《Deliberative Alignment: Reasoning Enables Safer Language Models》翻譯與解讀 LLMs之OpenAI o系列&#xff1a;OpenAI o3-mini的簡介、安…

女生年薪12萬,算不算屬于高收入人群

在繁華喧囂的都市中&#xff0c;我們時常會聽到關于收入、高薪與生活質量等話題的討論。尤其是對于年輕女性而言&#xff0c;薪資水平不僅關乎個人價值的體現&#xff0c;更直接影響到生活質量與未來的規劃。那么&#xff0c;女生年薪12萬&#xff0c;是否可以被劃入高收入人群…

AI開發學習之——PyTorch框架

PyTorch 簡介 PyTorch &#xff08;Python torch&#xff09;是由 Facebook AI 研究團隊開發的開源機器學習庫&#xff0c;廣泛應用于深度學習研究和生產。它以動態計算圖和易用性著稱&#xff0c;支持 GPU 加速計算&#xff0c;并提供豐富的工具和模塊。 PyTorch的主要特點 …

Python安居客二手小區數據爬取(2025年)

目錄 2025年安居客二手小區數據爬取觀察目標網頁觀察詳情頁數據準備工作&#xff1a;安裝裝備就像打游戲代碼詳解&#xff1a;每行代碼都是你的小兵完整代碼大放送爬取結果 2025年安居客二手小區數據爬取 這段時間需要爬取安居客二手小區數據&#xff0c;看了一下相關教程基本…

OpenCV:開運算

目錄 1. 簡述 2. 用腐蝕和膨脹實現開運算 2.1 代碼示例 2.2 運行結果 3. 開運算接口 3.1 參數詳解 3.2 代碼示例 3.3 運行結果 4. 開運算應用場景 5. 注意事項 6. 總結 相關閱讀 OpenCV&#xff1a;圖像的腐蝕與膨脹-CSDN博客 OpenCV&#xff1a;閉運算-CSDN博客 …

JavaWeb入門-請求響應(Day3)

(一)請求響應概述 請求(HttpServletRequest):獲取請求數據 響應(HttpServletResponse):設置響應數據 BS架構:Browser/Server,瀏覽器/服務器架構模式。客戶端只需要瀏覽器就可訪問,應用程序的邏輯和數據都存儲在服務端(維護方便,響應速度一般) CS架構:Client/ser…

【SLAM】于AutoDL云上GPU運行GCNv2_SLAM的記錄

配置GCNv2_SLAM所需環境并實現AutoDL云端運行項目的全過程記錄。 本文首發于?慕雪的寒舍 1. 引子 前幾天寫了一篇在本地虛擬機里面CPU運行GCNv2_SLAM項目的博客&#xff1a;鏈接&#xff0c;關于GCNv2_SLAM項目相關的介紹請移步此文章&#xff0c;本文不再重復說明。 GCNv2:…

羅格斯大學:通過輸入嵌入對齊選擇agent

&#x1f4d6;標題&#xff1a;AgentRec: Agent Recommendation Using Sentence Embeddings Aligned to Human Feedback &#x1f310;來源&#xff1a;arXiv, 2501.13333 &#x1f31f;摘要 &#x1f538;多代理系統必須決定哪個代理最適合給定的任務。我們提出了一種新的架…

【實戰篇】Android安卓本地離線實現視頻檢測人臉

實戰篇Android安卓本地離線實現視頻檢測人臉 引言項目概述核心代碼類介紹人臉檢測流程項目地址總結 引言 在當今數字化時代&#xff0c;人臉識別技術已經廣泛應用于各個領域&#xff0c;如安防監控、門禁系統、移動支付等。本文將以第三視角詳細講解如何基于bifan-wei-Face/De…