小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

OpenGL學(xué)習(xí)筆記(4)

 QomoIT 2019-07-19

引言

上一次已經(jīng)通過著色器繪制了一個2D的三角形,這次筆記記錄了教程從著色器到變換的內(nèi)容,變成了一個有紋理,會移動的2D三角形。

著色器

這一節(jié)主要是將著色器中的數(shù)據(jù)類型和uniform屬性。

GLSL中的數(shù)據(jù)類型

GLSL中的基本數(shù)據(jù)類型與C語言中差不多,有int,float,double,uint,bool。除此之外還有2個容器類型,向量vec和矩陣mat。
GLSL中的vec使用十分靈活,可以用.x,.y,.z,.w來獲取一個vec的第1,2,3,4個分量,或者rgba(顏色),stpq(紋理)來獲取。GLSL允許你像下面一樣使用vec:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

著色器之間傳遞數(shù)據(jù)

OpenGL允許位于管程前面的著色器像后面的著色器傳遞數(shù)據(jù)。
例如在vertexShader中輸出一個vertexColor,使用out關(guān)鍵字

out vec3 VertexColor;

在fragmentShader中獲取vertexColor,使用關(guān)鍵字in

in vec3 VertexColor;

名字和類型一定要對,如果后面的in前面沒有對應(yīng)的out,編譯和鏈接都不會出錯,但是in的值會被設(shè)為0。

著色器中的Uniform屬性

這個比較重要,Uniform屬性是你的OpenGL程序向著色器程序輸入數(shù)據(jù)的另一個途徑。
第一個途徑是通過上一次筆記講的VertexAttrib(頂點屬性),一般每一個頂點獨有的固定的屬性會使用這個傳遞。
第二個途徑就是Uniform變量,它在每個著色器程序?qū)ο笾卸际仟氁粺o二的,可以被著色器程序的任意著色器在任意階段訪問,而且數(shù)據(jù)對于每一個頂點不能被改變。Uniform比較適合用來輸入根據(jù)當(dāng)前程序狀態(tài)才能被確定,或是多個頂點的一樣屬性。
在著色器中定義uniform變量

uniform vec4 ourColor; // 在OpenGL程序代碼中設(shè)定這個變量

在OpenGL程序中設(shè)定變量

// 首先獲取uniform變量在著色器程序中的位置
int uniformLocation = glGetUniformLocation(shaderProgram, "ourColor");
// 設(shè)置Uniform變量
glUniform4f(uniformLocation, 0.0f, greenValue, 0.0f, 1.0f);

glUniform后面可用的后綴及其含義:

后綴 含義
f 函數(shù)需要一個float作為它的值
i 函數(shù)需要一個int作為它的值
ui 函數(shù)需要一個unsigned int作為它的值
3f 函數(shù)需要3個float作為它的值
fv 函數(shù)需要一個float向量/數(shù)組作為它的值

著色器程序類

不能總是在主函數(shù)儲存著色器的代碼,需要將著色器的代碼存在單獨的文件里,用的時候從文件讀取。著色器文件讀取,編譯鏈接都是一個相對比較流程化的過程,可以統(tǒng)一到一個Shader類中(其實是ShaderProgram類)。跟著教程寫出的著色器類Shader.h,Shader.cpp

紋理

紋理的繪制要求每個頂點有紋理坐標(biāo)屬性,2D紋理坐標(biāo)左下角是(0,0),右上角是(1,1)如下:
在這里插入圖片描述

紋理的環(huán)繞方式

OpenGL里的紋理有四種環(huán)繞方式

環(huán)繞方式 描述
GL_REPEAT 對紋理的默認(rèn)行為。重復(fù)紋理圖像。
GL_MIRRORED_REPEAT 和GL_REPEAT一樣,但每次重復(fù)圖片是鏡像放置的。
GL_CLAMP_TO_EDGE 紋理坐標(biāo)會被約束在0到1之間,超出的部分會重復(fù)紋理坐標(biāo)的邊緣。
GL_CLAMP_TO_BORDER 超出的坐標(biāo)為用戶指定的邊緣顏色。

效果如下
在這里插入圖片描述
使用glTexParameter*函數(shù)對單獨的一個坐標(biāo)軸設(shè)置(s、t(如果是使用3D紋理那么還有一個r)它們和x、y、z是等價的):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

紋理過濾

紋理過濾方式就是如何由連續(xù)的紋理坐標(biāo)離散的紋理像素確定當(dāng)前頂點的顏色。
GL_NEAREST(也叫鄰近過濾,Nearest Neighbor Filtering)是OpenGL默認(rèn)的紋理過濾方式。當(dāng)設(shè)置為GL_NEAREST的時候,OpenGL會選擇中心點最接近紋理坐標(biāo)的那個像素。下圖中你可以看到四個像素,加號代表紋理坐標(biāo)。左上角那個紋理像素的中心距離紋理坐標(biāo)最近,所以它會被選擇為樣本顏色:
在這里插入圖片描述
GL_LINEAR(也叫線性過濾,(Bi)linear Filtering)它會基于紋理坐標(biāo)附近的紋理像素,計算出一個插值,近似出這些紋理像素之間的顏色。一個紋理像素的中心距離紋理坐標(biāo)越近,那么這個紋理像素的顏色對最終的樣本顏色的貢獻越大。下圖中你可以看到返回的顏色是鄰近像素的混合色:
在這里插入圖片描述
兩種效果的比較,Nearest顆粒感較重,Linear比較模糊。
在這里插入圖片描述

還有一種叫做多級漸遠紋理的技術(shù),是為了解決遠處物體使用高分辨率的紋理所造成的內(nèi)存浪費和不真實感。其實就是生成一系列不同大小、分辨率的原圖縮小版,生成多級漸遠紋理可以調(diào)用glGenerateMipmaps函數(shù)
在這里插入圖片描述
然后根據(jù)距離選擇不同分辨率的圖片作為紋理,這個部分是OpenGL幫你做的,你需要的只是選擇OpenGL在切換兩個不同級別的多級漸遠紋理層之間時的過濾方式

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

前面的值代表紋理過濾的方式,后面的值代表多級漸遠紋理的方式
下面是設(shè)置過濾方式的示例代碼:

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

注意只有在min的時候設(shè)置多級漸遠才有效果,因為多級漸遠時紋理圖片只會縮小,不會放大。

加載紋理

stb_image.h是Sean Barrett的一個非常流行的單頭文件圖像加載庫,它能夠加載大部分流行的文件格式,并且能夠很簡單得整合到你的工程之中。引入了stb_image庫之后,加載一個紋理過程應(yīng)該如下:

unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 為當(dāng)前綁定的紋理對象設(shè)置環(huán)繞、過濾方式
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);

在實際的應(yīng)用中,會把上述過程封裝成一個函數(shù)

unsigned int TextureFromFile(const char *path){
    unsigned int textureID;
    glGenTextures(1, &textureID);

    int width, height, nrComponents;
    unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
    if(data){
        GLenum format;
        if(nrComponents == 1)
            format = GL_RED;
        else if(nrComponents == 3)
            format = GL_RGB;
        else if(nrComponents == 4)
            format = GL_RGBA;
        
        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);

        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_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        stbi_image_free(data);
    }else{
        std::cout << "Texture failed to load at path: " << path << std::endl;
        stbi_image_free(data);
    }

    return textureID;
}

紋理的應(yīng)用

為了將紋理傳遞到著色器中,GLSL提供了一個紋理對象使用的內(nèi)建數(shù)據(jù)類型,叫做采樣器(Sampler),它以紋理類型作為后綴,比如sampler1D、sampler3D,sampler2D。可以在著色器中這樣聲明一個著色器。

uniform sampler2D ourTexture;

因為大小問題(應(yīng)該吧),紋理類型不能像vec一樣直接使用glGetUniformLocation+glUniform設(shè)置。需要首先設(shè)置著色器里的Uniform sampler的紋理單元(只需在生成著色器程序后設(shè)置一次)

ourShader.use();
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 將texture1的紋理單元設(shè)置為0
glUniform1i(glGetUniformLocation(ourShader.ID, "texture2"), 1); // 將texture2的紋理單元設(shè)置為1

緊接著在需要渲染的時候分別對紋理單元綁定紋理

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);

傳到著色器中之后,需要通過GLSL內(nèi)置的texture函數(shù)使用紋理。

FragColor = texture(ourTexture, TexCoord); // TexCoord是一個vec2,代表紋理坐標(biāo)

變換

OpenGL里坐標(biāo)的變換是通過坐標(biāo)向量和矩陣相乘實現(xiàn)的。目前常用的矩陣由三種操作:縮放,位移,旋轉(zhuǎn)

坐標(biāo)向量

目前用到的坐標(biāo)向量一般都是一個四元向量,前三個分別是x,y,z的坐標(biāo),最后一個值稱為齊次坐標(biāo)。
\begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix}

齊次坐標(biāo)(Homogeneous Coordinates)

向量的w分量也叫齊次坐標(biāo)。想要從齊次向量得到3D向量,我們可以把x、y和z坐標(biāo)分別除以w坐標(biāo)。我們通常不會注意這個問題,因為w分量通常是1.0。使用齊次坐標(biāo)有幾點好處:它允許我們在3D向量上進行位移(如果沒有w分量我們是不能位移向量的),而且下一章我們會用w值創(chuàng)建3D視覺效果。

如果一個向量的齊次坐標(biāo)是0,這個坐標(biāo)就是方向向量(Direction Vector),因為w坐標(biāo)是0,這個向量就不能位移(譯注:這也就是我們說的不能位移一個方向)。

縮放矩陣

\begin{bmatrix} \color{red}{S_1} & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}{S_2} & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}{S_3} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \color{red}{S_1} \cdot x \\ \color{green}{S_2} \cdot y \\ \color{blue}{S_3} \cdot z \\ 1 \end{pmatrix}

位移矩陣

\begin{bmatrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}{T_x} \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}{T_y} \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}{T_z} \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} x + \color{red}{T_x} \\ y + \color{green}{T_y} \\ z + \color{blue}{T_z} \\ 1 \end{pmatrix}

旋轉(zhuǎn)矩陣

沿x軸旋轉(zhuǎn):
\begin{bmatrix} \color{red}1 & \color{red}0 & \color{red}0 & \color{red}0 \\ \color{green}0 & \color{green}{\cos \theta} & - \color{green}{\sin \theta} & \color{green}0 \\ \color{blue}0 & \color{blue}{\sin \theta} & \color{blue}{\cos \theta} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} x \\ \color{green}{\cos \theta} \cdot y - \color{green}{\sin \theta} \cdot z \\ \color{blue}{\sin \theta} \cdot y + \color{blue}{\cos \theta} \cdot z \\ 1 \end{pmatrix}
沿y軸旋轉(zhuǎn):
\begin{bmatrix} \color{red}{\cos \theta} & \color{red}0 & \color{red}{\sin \theta} & \color{red}0 \\ \color{green}0 & \color{green}1 & \color{green}0 & \color{green}0 \\ - \color{blue}{\sin \theta} & \color{blue}0 & \color{blue}{\cos \theta} & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \color{red}{\cos \theta} \cdot x + \color{red}{\sin \theta} \cdot z \\ y \\ - \color{blue}{\sin \theta} \cdot x + \color{blue}{\cos \theta} \cdot z \\ 1 \end{pmatrix}
沿z軸旋轉(zhuǎn):
\begin{bmatrix} \color{red}{\cos \theta} & - \color{red}{\sin \theta} & \color{red}0 & \color{red}0 \\ \color{green}{\sin \theta} & \color{green}{\cos \theta} & \color{green}0 & \color{green}0 \\ \color{blue}0 & \color{blue}0 & \color{blue}1 & \color{blue}0 \\ \color{purple}0 & \color{purple}0 & \color{purple}0 & \color{purple}1 \end{bmatrix} \cdot \begin{pmatrix} x \\ y \\ z \\ 1 \end{pmatrix} = \begin{pmatrix} \color{red}{\cos \theta} \cdot x - \color{red}{\sin \theta} \cdot y \\ \color{green}{\sin \theta} \cdot x + \color{green}{\cos \theta} \cdot y \\ z \\ 1 \end{pmatrix}

矩陣組合的順序

作者建議先縮放,再旋轉(zhuǎn),最后位移。即矩陣的乘法最好是下面的順序
Trans . Rotat . Scale . Vec

GLM

GLM是OpenGL Mathematics的縮寫,它是一個只有頭文件的庫,是專門為OpenGL量身定做的數(shù)學(xué)庫。
我們需要的GLM的大多數(shù)功能都可以從下面這3個頭文件中找到:

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

GLM中有mat, vec類型,以及許多方便的操作,比如生成三種變換矩陣

glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5)); 

將矩陣通過uniform送入頂點著色器,并且在頂點著色器中將矩陣與頂點坐標(biāo)相乘就完成了位置的變換

unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

本文的思路和出現(xiàn)的圖來自于 learnopengl-cn.

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多