LearnOpenGL 紋理

紋理 :

紋理也是頂點數據的一種. 紋理的添加也是處理VAO

配置紋理的操作方式

使用glTexParamete* 函數對單獨的一個坐標軸設置 (s, t (如果使用3D紋理, 那么還有一個r), 它們和x,y,z是等價的)

glTexParameteri(GLenum target, GLenum pname, GLint param)
第一個參數target : 指定了紋理目標, 我們使用的是2D的內容, 所以這里使用的是GL_TEXTURE_2D
第二個參數pname  : 需要我們指定設置的選項與應用的紋理軸. 我們打算配置的是WRAP,并且指定s和t.
第三個參數param  : 選擇紋理環(huán)繞方式.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_MIRRORED_REPEAT);
紋理坐標

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

環(huán)繞方式
GL_REPEAT 默認行為,重復紋理圖像
GL_MIRRORED_REPEAT 和GL_REPEAT一樣, 但每次重復都是鏡像放置
GL_CLAMP_TO_EDGE 紋理坐標會被約束在0到1之間, 超出的部分會重復紋理坐標的邊緣, 產生一種邊緣被拉伸的效果
GL_CLAMP_TO_BOARD 超出的坐標為用戶指定的邊緣顏色
環(huán)繞方式
如果我們選擇GL_CLAMP_TO_BOARD, 我們需要指定一個邊緣顏色
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, {1.0f,1.0f, 0.0f, 1.0f} );

紋理過濾

紋理過濾很多選項,這里討論重要的兩種 : GL_NEAREST 和 GL_LINEAR

鄰近過濾

GL_NEAREST 是OpenGL默認的紋理過濾方式.當設置為GL_NEAREST的時候 : OpenGL會選擇中心點最接近紋理坐標的那個像素.

GL_NEAREST
線性過濾

GL_LINEAR 它會基于紋理坐標的紋理像素, 計算出一個插值, 近似出這些紋理像素之間的顏色. 一個紋理像素的中心距離紋理坐標越近, 那么這個紋理像素的顏色對最終的樣本顏色的貢獻越大.

GL_LINEAR

GL_NEAREST 會產生顆粒感.
GL_LINEAR 會羽化.產生更平滑圖案.

紋理過濾

當進行放大和縮小操作的時候, 可以設置紋理過濾的選項, 比如
放大時 , 選擇GL_LINEAR
縮小時 , 選擇GL_NEAREST

通過glTexParameter*函數來設置具體的方式.

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); //縮小
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);  //放大

多級漸遠紋理.

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

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

它簡單來說就是一系列的紋理圖像,后一個紋理圖像是前一個的二分之一。
多級漸遠紋理

手工為每個紋理圖像創(chuàng)建一系列多級漸遠紋理很麻煩,幸好OpenGL有一個glGenerateMipmap函數,
在創(chuàng)建完一個紋理后調用它OpenGL就會承擔接下來的所有工作了。后面的教程中你會看到該如何使用它。

glGenerateMipmap(GLenum target) 只有一個參數. 過濾方式
GL_NEAREST_MIPMAP_NEAREST 使用最鄰近的多級漸遠紋理來匹配像素大小,并使用鄰近插值進行紋理采樣
GL_LINEAR_MIPMAP_NEAREST 使用最鄰近的多級漸遠紋理級別,并使用線性插值進行采樣
GL_NEAREST_MIPMAP_LINEAR 在兩個最匹配像素大小的多級漸遠紋理之間進行線性插值,使用鄰近插值進行采樣
GL_LINEAR_MIPMAP_LINEAR 在兩個鄰近的多級漸遠紋理之間使用線性插值,并使用線性插值進行采樣
glGenerateMipmap(GL_NEAREST_MIPMAP_NEAREST);

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





紋理過濾加載與創(chuàng)建紋理

創(chuàng)建紋理之前解決一個問題, 就是怎么把圖片資源轉成紋理數據傳給OpenGL

STB

先添加一個庫 stb ,
把對應的源文件加載到項目中

STB

這里使用這個stbi_load 加載在項目中的紋理資源

    int width, height, nrChannels;
    unsigned char *data = stbi_load("本地的紋理圖片資源地址", &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);

stbi_load 第一個參數是獲取文件地址, 這里直接去一個本地的地址就好, 也可以根據自己的方式把圖片資源路徑設置成在項目里面的.

值得注意 :

stb_image.h 使用前需要 #define STB_IMAGE_IMPLEMENTATION

   Do this:
      #define STB_IMAGE_IMPLEMENTATION
   before you include this file in *one* C or C++ file to create the implementation.

在#include "stb_image.h"中的函數都是STBIDEF開頭. 這里定義了兩種方式, static或extern.

#ifndef STBIDEF
#ifdef STB_IMAGE_STATIC
#define STBIDEF static
#else
#define STBIDEF extern
#endif
#endif

如果在多個文件中導入了stb_image.h的頭文件. 需要STB_IMAGE_STATIC, 不然可能有編譯不通過的情況.

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#include "stb_image.h"

關于SOIL

SOIL是簡易OpenGL圖像庫(Simple OpenGL Image Library)的縮寫,它支持大多數流行的圖像格式,使用起來也很簡單,你可以從他們的主頁下載。像其它庫一樣,你必須自己生成.lib。你可以使用/projects文件夾內的任意一個解決方案(Solution)文件(不用擔心他們的Visual Studio版本太老,你可以把它們轉變?yōu)樾碌陌姹?,這一般是沒問題的。譯注:用VS2010的時候,你要用VC8而不是VC9的解決方案,想必更高版本的情況亦是如此)來生成你自己的.lib文件。你還要添加src文件夾里面的文件到你的includes文件夾;對了,不要忘記添加SOIL.lib到你的鏈接器選項,并在你代碼文件的開頭加上#include <SOIL.h>。

PS : 在LearnOpenGL里面介紹添加一個SOIL的庫, 并沒有很好的介紹怎么配置在項目中 , 這里配置的方式好像不是特別的方便, 所以我這里加載紋理的數據使用上面的stb文件內容. 相當于源文件吧.

下面的教程中,我們會使用一張木箱的圖片。要使用SOIL加載圖片,我們需要使用它的SOIL_load_image函數:

int width, height;
unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);

函數首先需要輸入圖片文件的路徑。然后需要兩個int指針作為第二個和第三個參數,SOIL會分別返回圖片的寬度高度到其中。后面我們在生成紋理的時候會用圖像的寬度和高度。第四個參數指定圖片的通道(Channel)數量,但是這里我們只需留為0。最后一個參數告訴SOIL如何來加載圖片:我們只關注圖片的RGB值。結果會儲存為一個很大的char/byte數組。

注意 : 雖然代碼中用的是stbi_load函數, 但是實現的效果是一樣的




實踐


頂點數據

MyTexturesVertices.hpp

#ifndef MyTexturesVertices_h
#define MyTexturesVertices_h

//紋理坐標 : 以左下角 (0.0, 0.0) 為原點
static float MyTextureVertices[] = {
//     ---- 位置 ----       ---- 顏色 ----     - 紋理坐標 -
     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    // 左上
};

//索引
static unsigned int  MyTextureVerticesIndices[] = { // 注意索引從0開始!
    0, 1, 2, // 第一個三角形
    0, 3, 2  // 第二個三角形
};
#endif /* MyTexturesVertices_h */

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


頂點數據

頂點數據和之前的有點不一樣, 所以這里需要調整一下Shader程序

Shader程序

MyTexturesShader.hpp

#ifndef MyTexturesShader_h
#define MyTexturesShader_h

#define STRINGIZE(x) #x
#define SHADER(shader) STRINGIZE(shader)

/// 著色器程序之間的數據傳遞
static char *MyTextureVertexShaderStr = SHADER(
    \#version 330 core\n
                               
    layout (location = 0) in vec3 position; //頂點數據源輸入
    layout (location = 1) in vec3 color; //顏色數據源輸入
    layout (location = 2) in vec2 texCoords; //紋理數據源輸入(2D)

    out vec2 vertexTexCoords;//紋理輸出
    out vec4 vertexColor;//顏色輸出

    void main()
    {
        gl_Position = vec4(position, 1.0f);//坐標位置
        vertexColor = vec4(color, 1.0f); //輸出給片元著色器
        vertexTexCoords = texCoords;
    }
);

//片元著色器程序
static char *MyTextureFragmentShaderSrc = SHADER(
    \#version 330 core\n
                                        
    in vec2 vertexTexCoords;//紋理輸入(從頂點)
    in vec4 vertexColor;//顏色輸入(從頂點)
                        
    uniform sampler2D myTexture;//全局myTexture

    out vec4 color;//顏色輸出
    out vec4 FragColor;//紋理輸出
                                                 
    void main()
    {
        color = vertexColor;
        FragColor = texture(myTexture, vertexTexCoords);
    }
);

#endif /* MyTexturesShader_h */

因為需要加載紋理, 在頂點著色器Shader程序中添加紋理的輸入輸出

    layout (location = 2) in vec2 texCoords; //紋理數據源輸入(2D)
    out vec2 vertexTexCoords;//紋理輸出
    main {   
      vertexTexCoords = texCoords; // 
    }

在紋理著色器SHader程序中也需要添加處理

    in vec2 vertexTexCoords;//紋理輸入(從頂點)
    uniform sampler2D myTexture;//全局myTexture
    out vec4 FragColor;//紋理輸出
    main {   
      FragColor = texture(myTexture, vertexTexCoords);
    }

注意 : uniform sampler2D myTexture; 這里創(chuàng)建了一個uniform修飾的myTexture , 暫時先忽略. 窗口程序中暫時沒有用到

窗口程序

MyTextures.cpp

#include <iostream>
#include "MyTextures.hpp"
#include "MyProgram.hpp"
#include "MyTexturesVertices.hpp"
#include "MyTexturesShader.hpp"

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

int runMyTextureOpenGlWindow() {
    int result = glfwInit();
    if (result == GL_FALSE) {
        printf("glfwInit 初始化失敗");
        return -1;
    }

    //這里的宏不好提示出來, 根據LearnOpenGL的文檔提示, 用這三個
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);

    //創(chuàng)建一個Window
    GLFWwindow *window = glfwCreateWindow(600, 400, "My Opengl Window", NULL, NULL);
    if(!window) {
        printf("window 創(chuàng)建失敗");

    }

    glfwMakeContextCurrent(window);
    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);

    //----------------------------------------------------------------------
    MyProgram myProgram = MyProgram(MyTextureVertexShaderStr, MyTextureFragmentShaderSrc);



    GLuint VBO , VAO , EBO;
    unsigned int squareIndicesCount = 0;

    //創(chuàng)建VBO
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(MyTextureVertices), MyTextureVertices, GL_STATIC_DRAW);

    //創(chuàng)建VAO
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    //配置紋理的VAO
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);

    //創(chuàng)建EBO, 這里的EBO相當于索引的作用
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(MyTextureVerticesIndices), MyTextureVerticesIndices, GL_STATIC_DRAW);

    //解綁VAO
    glBindVertexArray(0);
    squareIndicesCount = sizeof(MyTextureVerticesIndices)/sizeof(MyTextureVerticesIndices[0]);
    
    
 


    //生成紋理
    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    // 為當前綁定的紋理對象設置環(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("/Users/lumi/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Common/Sources/dizhuan.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);
    

    
    //進行繪制
    while(!glfwWindowShouldClose(window)){
       //檢查事件
        glfwPollEvents();

        //渲染指令
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
       
        glUseProgram(myProgram.program);
        glBindVertexArray(VAO);
        glBindTexture(GL_TEXTURE_2D, texture);//加載紋理
        glDrawElements(GL_TRIANGLES, squareIndicesCount, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        //交換緩沖
        glfwSwapBuffers(window);
    }

    //程序銷毀
    glfwTerminate();
    return 1;
}

最終效果 :


添加紋理

更多效果

因為著色器程序本身有顏色. 顏色跟紋理做一個混合 , 修改片元著色器程序

FragColor = texture(myTexture, vertexTexCoords) * color;
混合

PS : 留個思考怎么做一些前面介紹的效果的處理.

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



紋理單元

你可能會奇怪為什么sampler2D變量是個uniform,我們卻不用glUniform給它賦值。使用glUniform1i,我們可以給紋理采樣器分配一個位置值,這樣的話我們能夠在一個片段著色器中設置多個紋理。一個紋理的位置值通常稱為一個紋理單元(Texture Unit)。一個紋理的默認紋理單元是0,它是默認的激活紋理單元,所以教程前面部分我們沒有分配一個位置值。

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

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

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

修改著色器程序
//片元著色器程序
static char *MyTextureFragmentShaderSrc = SHADER(
    \#version 330 core\n
                                        
    in vec2 vertexTexCoords;//從頂點著色器中拿紋理的值
    in vec4 vertexColor;//從頂點著色器中拿color的值
                        
    uniform sampler2D myTexture0;//全局myTexture
    uniform sampler2D myTexture1;//全局myTexture

    out vec4 color;
    out vec4 FragColor;
                                                 
    void main()
    {
        FragColor = mix(texture(myTexture0, vertexTexCoords), texture(myTexture1, vertexTexCoords), 0.5);
    }
);

修改窗口程序
//生成紋理0
unsigned int texture0, texture1;
unsigned char *data0 ;
int width0, height0, nrChannels0;

glGenTextures(1, &texture0);
glBindTexture(GL_TEXTURE_2D, texture0);
// 為當前綁定的紋理對象設置環(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);
data0 = stbi_load("/Users/lumi/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Common/Sources/dizhuan.jpg", &width0, &height0, &nrChannels0, 0);
if (data0)
{
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width0, height0, 0, GL_RGB, GL_UNSIGNED_BYTE, data0);
    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

}
else
{
    std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data0);

//生成紋理1
unsigned char *data1 ;
int width1, height1, nrChannels1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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);
data1 = stbi_load("/Users/lumi/Desktop/LearnOpengl/LearnOpenGl/LearnOpenGl/Common/Sources/smile.png", &width1, &height1, &nrChannels1, 0);
if (data1)
{
    //這里用了PNG, 有透明度, 使用GL_RGBA.
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width1, height1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data1);
    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

}
else
{
    std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data1);

//進行繪制
while(!glfwWindowShouldClose(window)){
  //檢查事件
  glfwPollEvents();

  //渲染指令
  glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT);

  //激活紋理0
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texture0);
  glUniform1i(glGetUniformLocation(myProgram.program, "myTexture0"), 0);
        
  //激活紋理1
  glActiveTexture(GL_TEXTURE1);
  glBindTexture(GL_TEXTURE_2D, texture1);
  glUniform1i(glGetUniformLocation(myProgram.program, "myTexture1"), 1);
       
  glUseProgram(myProgram.program);
  glBindVertexArray(VAO);
  glDrawElements(GL_TRIANGLES, squareIndicesCount, GL_UNSIGNED_INT, 0);
  glBindVertexArray(0);

  //交換緩沖
  glfwSwapBuffers(window);
}

紋理重疊



最后, 重疊的效果有點奇怪. 先這樣吧, 先理解這個紋理單元的概念.

注意找的素材的格式是PNG還是JPG, PNG有Alpha通道

這里還是有很多需要弄明白的細節(jié), 比如著色器程序的texture() 和mix()

體會這里的Uniform和之前的Uniform的區(qū)別

PS : 思考, 怎么改變這個紋理貼圖的大小.
PS : 思考, 實現紋理題圖大小的改變之后, 怎么設置紋理過濾和紋理環(huán)繞方式

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 版本記錄 前言 OpenGL 圖形庫項目中一直也沒用過,最近也想學著使用這個圖形庫,感覺還是很有意思,也就自然想著...
    刀客傳奇閱讀 9,250評論 0 8
  • 紋理是一個2D圖片(甚至也有1D和3D的紋理),它可以用來添加物體的細節(jié);為了能夠把紋理映射(Map)到三角形上,...
    涼夜lrs閱讀 786評論 0 1
  • 1. 紋理 紋理(Texture)是一個2D圖片(甚至也有1D和3D的紋理),它可以用來添加物體的細節(jié)。你可以想象...
    木小易Ying閱讀 708評論 0 1
  • 紋理(Textures) 我們已經了解到,我們可以為每個頂點使用顏色來增加圖形的細節(jié),從而創(chuàng)建出有趣的圖像。但是通...
    IceMJ閱讀 5,840評論 2 13
  • 紋理是一個2D圖片(甚至也有1D和3D的紋理),它可以用來添加物體的細節(jié)。 為了能夠把紋理映射(Map)到三角形上...
    八雲苗閱讀 1,483評論 0 0

友情鏈接更多精彩內容