Skip to content

There is some example program for teaching how to use texture in OpenGL

Notifications You must be signed in to change notification settings

ThIsJaCk23657689/TextureExamples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenGL 材質讀取外部圖片教學

圖片讀取函式庫

如果想要特別讀取 BMP、JPEG 或 PNG 檔案,通常都會需要撰寫 Reder,但除了 BMP 之外其他的格式讀取或寫入都較為複雜,所以說一般建議使用已經開發好的函式庫即可。

stb_image

安裝方法 1: 直接下載 (推薦方法)

  1. 首先下載 stb_image.h 到專案根目錄,Github 載點
  2. 之後創建一個新的 .cpp 檔案,名稱隨意,裡面內容如下:
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
  1. 在要使用的檔案中(例如main.cpp)引入標頭檔,即可開始使用 stb 圖片函式庫了!
#include "stb_image.h"

安裝方法 2: 透過 vcpkg

  1. 首先輸入指令來安裝 stb 函式庫。
$ vcpkg install stb --triplet=x64-windows
  1. 安裝好後,只需要在專案根目錄下的 CMakeLists.txt 加上相關設定
find_path(STB_INCLUDE_DIRS "stb.h")
target_include_directories(目標名稱 PRIVATE ${STB_INCLUDE_DIRS})
  1. 之後創建一個新的 .cpp 檔案,名稱隨意,裡面內容如下:
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
  1. 在要使用的檔案中(例如main.cpp)引入標頭檔,即可開始使用 stb 圖片函式庫了!
#include <stb_image.h>

使用方法

讀取圖片的方式,就是這麼如此簡單:

int width, height, nrChannels;
unsigned char *image = stbi_load("圖片位置.png", &width, &height, &nrChannels, 0);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);

由於 OpenGL 的 Texture Coordinate 中,原點 (0, 0) 是在左下角,而一般來說圖片索引的 (0, 0) 會是左上角(想想陣列的概念),所以讀取出來的圖片會是上下顛倒的,這個時候可以透過下列函式來矯正它:

stbi_set_flip_vertically_on_load(true);

記得要在呼叫 stbi_load() 之前。

SDL Image

除了使用 stb_image.h 之外,還可以使用 SDL2 自己的衍生函式庫 SDL2-Image 來實現讀取圖片的功能。

安裝方法

  1. 首先輸入指令來安裝 sdl2-image 函式庫。
$ vcpkg install sdl2-image[core,libjpeg-turbo] --triplet=x64-windows
  1. 安裝好後,記得要去專案根目錄下的 CMakeLists.txt 做相關設定:
find_package(sdl2-image CONFIG REQUIRED)
target_link_libraries(目標名稱 PRIVATE SDL2::SDL2_image)
  1. 接著引入標頭檔並且初始化,這樣就可以開始使用 SDL2-Image 了。
#include <SDL_image.h>

int main(int argc, char **argv) {
    // ... SDL 初始化
    
    // ... SDL Image 初始化
    int sdl_img_init_flags = IMG_INIT_JPG | IMG_INIT_PNG;
    int img_init = IMG_Init(sdl_img_init_flags);
    if ((img_init & sdl_img_init_flags) != sdl_img_init_flags) {
        std::cout << "Oops! Failed to initialize SDL Image extension library. :(\n" << SDL_GetError() << std::endl;
        return -1;
    }
    
    // 其他程式 ....
}

使用方法

讀取圖片的方式,就是這麼如此簡單:

SDL_Surface* image = IMG_Load("圖片位置.png");

// 抓取圖片寬、高與通道
int width = image->w;
int height = image->h;
int nrChannels = image->format->BitsPerPixel / 8;

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);

很可惜的是,SDL2-Image 沒有提供垂直翻轉的函式可以呼叫,所以這邊只能自己寫函式去翻轉圖片,或者是另一個方法..,把 Texture Coordinate 的 Y 顛倒過來(雖然是最簡單但是不建議)。

void flip_surface(SDL_Surface* surface) {
    SDL_LockSurface(surface);

    int pitch = surface->pitch; // row size
    char* temp = new char[pitch]; // intermediate buffer
    char* pixels = (char*) surface->pixels;

    for(int i = 0; i < surface->h / 2; ++i) {
        // get pointers to the two rows to swap
        char* row1 = pixels + i * pitch;
        char* row2 = pixels + (surface->h - i - 1) * pitch;

        // swap rows
        memcpy(temp, row1, pitch);
        memcpy(row1, row2, pitch);
        memcpy(row2, temp, pitch);
    }

    delete[] temp;

    SDL_UnlockSurface(surface);
}

// 上下翻轉圖片
SDL_Surface* image = IMG_Load("assets/textures/rickroll.png");
flip_surface(image);

利用 CMake 建立 Symbolic Link

當你有開始有讀取外部圖片的需求時,就會發現圖片路徑是一個很大的問題,比如說你在主程式寫說要讀取 assets\textures\doge.png(相對路徑寫法),但編譯後執行程式卻說一直找不到該檔案,除非你改成絕對路徑寫法,比如 C:\uers\user\Desktop\CGHomework05\assets\textures\doge.png 才可以讀取,但問題是這樣的寫法很不明智,因為代表就已經寫死你的專案必須要放在桌面,一旦改變了路徑或是換了地方又整個要重改。 之所以會有這樣的原因,就是因為你認為的【相對】路徑中的相對是基於 main.cpp,而事實上不是,是【相對於你最終編譯出來的執行檔】,舉例來說使用 Visual Studio 編譯的話,最終的程式(.exe)會產生在路徑 CGHomework05\out\build\x64-Debug 當中,所以說在 main.cpp 所寫的圖片路徑(假設是 assets\textures\doge.png),程式的路徑反而是會抓取 CGHomework05\out\build\x64-Debug\assets\textures\doge.png,而非我們預期的 CGHomework05\assets\textures\doge.png。 或許會有人就乾脆把在專案根目錄(CGHomework05)下的外部資源檔案全部手動複製到建置目錄下(CGHomework05\out\build\x64-Debug\),雖然可以解決方法但一樣換湯不換藥,畢竟建置目錄內的東西可是會隨著不同電腦、不同環境、不同 IDE、不同建置器和不同編譯器就會有所不一樣,所以說強烈不建議這樣做。 最好的解法就是透過 CMake 來自動幫我們處理,只要輸入以下語法即可:

add_custom_command(TARGET 目標名稱 POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E create_symlink
        "${CMAKE_CURRENT_SOURCE_DIR}/assets"
        "$<TARGET_FILE_DIR:目標名稱>/assets"
    DEPENDS
        "${CMAKE_CURRENT_SOURCE_DIR}/assets" # Make sure the directory exists
    COMMENT "Creating symlink from build tree to project resources..."
    VERBATIM
)

指令解釋

add_custom_command(TARGET <target>
                   PRE_BUILD | PRE_LINK | POST_BUILD
                   COMMAND command1 [ARGS] [args1...]
                   [COMMAND command2 [ARGS] [args2...] ...]
                   [BYPRODUCTS [files...]]
                   [WORKING_DIRECTORY dir]
                   [COMMENT comment]
                   [VERBATIM] [USES_TERMINAL]
                   [COMMAND_EXPAND_LISTS])
  • add_custom_command() 代表是我們可以透過該函式來執行一些自定義的指令,來達成一些我們可能會需要的事情,大概就式輸入指令到終端機的概念。
  • POST_BUILD 意思是【建置後事件】,也就說當程式建置好後才會觸發指令,除此之外還有 PRE_BUILDPRE_LINK
  • COMMAND 就是用來執行命令的意思。
  • DEPENDS 就是依賴條件,當某檔案不存在時將會出錯。
  • VERBATIM 大概就是命令的所有參數都將為構建工具正確轉義,總之 CMake 官方文件建議使用。

而上述指令大致上的意思就是說,在程式建置好後會輸入以下指令:

$ cmake -E create_symlink "${CMAKE_CURRENT_SOURCE_DIR}/assets" "$<TARGET_FILE_DIR:${你的目標名稱}>/assets"

而這個指令的意思就是建立符號連結(Symbolic Link),如果是熟悉類 Unix 系統的使用者想必很熟悉,但 Windows 使用者可能就未必了,基本上這個東西跟 Windows 上的捷徑(.lnk)是截然不同的東西,功能雖然很像但基本上是完全不一樣,根據微軟官方的解釋,【符號連結是指向另一個檔案系統物件的檔案系統物件】,符號連結在檔案總管中會顯示為一般檔案或者是目錄,使用者或應用程式可以用完全相同的方式處理或是存取它們。 現在知道這個指令是在創建符號連結,但是路徑到底是哪裡呢?我們可以看出這邊有 CMake 的變數:${CMAKE_CURRENT_SOURCE_DIR}$<TARGET_FILE_DIR:目標名稱>,前者為你當前 CMakeLists.txt 的所在根目錄,後者則是 CMake 生產器表達式,它的語法其實是 $<TARGET_FILE_DIR:tgt>,這其中的 tgt 就是你專案的目標名稱,比如說 add_executable(my-target),那 my-target 就是你的目標名稱,但如果你是有設定變數的話,例如 set(MY_EXECUTABLE "my-target"),那你的目標名稱反而要變成 ${MY_EXECUTABLE}。 回到主題,$<TARGET_FILE_DIR:目標名稱> 回傳的就是你程式建置好的所在目錄(可以說是建置根目錄),所以我們只要把符號連結建立在該目錄下,你的外部檔案就可以被程式讀取到了。

CMake 可以透過參數 -E 來實現例如建立符號連結 create_symlink、複製資料夾 copy_directory、刪除檔案 removecattouch 等行為。

錯誤排除

  1. 如果你會發生以下錯誤的話: 其解決方案很簡單,就是到【設定】->【更新與安全性】->【開發人員專用】,然後將【開發人員模式】給它開啟起來就解決了:

  2. 如果你會發生以下錯誤的話: 其解決方案很簡單,把整個建置資料夾刪除,並且也清除掉 CMake 快取,重新建立就可以解決了。

tags: OpenGL

About

There is some example program for teaching how to use texture in OpenGL

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published