Mugichoko's blog

Mugichoko’s blog

プログラミングを中心としたメモ書き.

GLSL #5: テクスチャマッピング

目標

前回のプログラムをベースに,今度はテクスチャマッピングを実装する.より具体的には,画面いっぱいに四角形を描き,そこに任意の画像をテクスチャマッピングする.

mugichoko.hatenablog.com

実装環境

DevILのダウンロード

  1. ここから「DevIL-Windows-SDK-1.8.0.zip」をダウンロード
  2. 「C:\Libraries\」に「DevIL Windows SDK」フォルダを解凍
    • フォルダ名が分かりにくいので「DevIL-1.8.0」とする
    • 以降,この「C:\Libraries\DevIL-1.8.0」を「Path」と呼ぶ

Visual Studioのプロジェクト作成

  1. Visual Studioを開いて「新しいプロジェクト」を作成
  2. 「インクルードディレクトリ」に以下を追加
    • Path\include
  3. 「ライブラリディレクトリ」に以下を追加
    • Debug: Path\lib\x64\Release
    • Release: 同上
  4. 「追加の依存ファイル」に必要なライブラリを追加
    • Debug: DevIL.lib
    • Release: 同上
  5. 3のフォルダに入っている「.dll」ファイルを「C:\Windows\System32」に追加

サンプルプログラム

前回までに作成した以下のプログラムは変更せずそのまま利用します.

  • window.h
  • window.cpp
  • shaderUtil.h
  • shaderUtil.cpp

model.h

モデルデータを扱うためのインタフェースを記述したヘッダファイル.

//
// model.h
//
#pragma once
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include "shaderUtil.h"

namespace gl
{
    class model
    {
    protected:
        GLuint prog;
        glm::mat4 projMat;
        glm::mat4 viewMat;
        glm::mat4 modelMat;

    public:
        virtual void render() = 0;
        void setProjMat(
            glm::mat4 &mat
        );
        void setViewMat(
            glm::mat4 &mat
        );
        void setModelMat(
            glm::mat4 &mat
        );
    };
}

model.cpp

モデルデータを扱うためのインタフェースを記述したソースファイル.

//
// model.cpp
//
#include "model.h"

namespace gl
{
    void model::setProjMat(
        glm::mat4 &mat
    )
    {
        projMat = mat;
    }

    void model::setViewMat(
        glm::mat4 &mat
    )
    {
        viewMat = mat;
    }

    void model::setModelMat(
        glm::mat4 &mat
    )
    {
        modelMat = mat;
    }
}

modelTex.h

画像データをDevILを用いて読み込んで,テクスチャとして描画するクラスを記述したヘッダファイル.

//
// modelTex.h
//
#pragma once
#include "model.h"
#include <IL/il.h>

namespace gl
{
    class modelTex : public model
    {
    private:
        static const GLfloat vertData[20];
        static const GLshort idxData[6];
        static const int numVert;
        static const int offsetTexUVData;

        GLuint texID;

        GLuint MVPMatLoc;
        GLuint vao; // Vertex array object
        GLuint vbo; // Vertex buffer object
        GLuint ibo; // Index buffer object

    public:
        modelTex(
            const string &texFileName,
            const string &vertShaderName,
            const string &fragShaderName
        );
        ~modelTex();

        void render();
    };
}

modelTex.cpp

画像データをDevILを用いて読み込んで,テクスチャとして描画するクラスを記述したソースファイル.

//
// modelTex.cpp
//
#include "modelTex.h"

namespace gl
{
    const GLfloat modelTex::vertData[20] =
    {
        // Vertex position
        +1.0f, +1.0f, 0.0f,      // #0: Upper right
        -1.0f, +1.0f, 0.0f,      // #1: Upper left
        -1.0f, -1.0f, 0.0f,      // #2: Lower left
        +1.0f, -1.0f, 0.0f,      // #3: Lower right
        // Texture uv position
        1.0f, 1.0f,               // #0: Upper right
        0.0f, 1.0f,               // #1: Upper left
        0.0f, 0.0f,               // #2: Lower left
        1.0f, 0.0f                // #3: Lower right
    };
    const GLshort modelTex::idxData[6] =
    {
        0, 1, 2,
        0, 2, 3
    };
    const int modelTex::numVert(4);
    const int modelTex::offsetTexUVData(sizeof(float) * 3 * numVert);

    modelTex::modelTex(
        const string &texFileName,
        const string &vertShaderName,
        const string &fragShaderName
    )
    {
        // ----- Initialize shader programs
        initializeProgram(prog, vertShaderName, fragShaderName);

        // ----- Generate buffers
        // VBO
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertData), vertData, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        // IBO
        glGenBuffers(1, &ibo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idxData), idxData, GL_STATIC_DRAW);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        // ----- Load an image and create a texture of the image
        ilInit();
        
        ILuint imgID;
        ilGenImages(1, &imgID);
        glGenTextures(1, &texID);

        ilBindImage(imgID);
        ilEnable(IL_ORIGIN_SET);
        ilOriginFunc(IL_ORIGIN_LOWER_LEFT);

        try {
            ILboolean isLoaded = ilLoadImage((ILstring)texFileName.c_str());
            if (isLoaded)
            {
                ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);

                glBindTexture(GL_TEXTURE_2D, texID);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ilGetInteger(IL_IMAGE_WIDTH),
                    ilGetInteger(IL_IMAGE_HEIGHT), 0, GL_RGBA, GL_UNSIGNED_BYTE,
                    ilGetData());
            }
            else
            {
                throw "Could not load image: " + texFileName;
            }
        }
        catch (string e)
        {
            cerr << "Error: " << e.c_str() << endl;
            exit(EXIT_FAILURE);
        }

        ilDeleteImages(1, &imgID);

        // ----- Get a uiform location
        MVPMatLoc = glGetUniformLocation(prog, "MVPMat");

        // ----- VAO
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);  // 3 elements per vertex
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)offsetTexUVData); // 2 elements per color
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);

        glBindVertexArray(0);
    }
    modelTex::~modelTex()
    {
        glDeleteVertexArrays(1, &vao);
        glDeleteTextures(1, &texID);
        glDeleteBuffers(1, &vbo);
        glDeleteBuffers(1, &ibo);
        glDeleteBuffers(1, &MVPMatLoc);
    }

    void modelTex::render()
    {
        glUseProgram(prog);
        {
            // Send matrix data to GPU
            glUniformMatrix4fv(MVPMatLoc, 1, GL_FALSE, glm::value_ptr(projMat * viewMat * modelMat));
            
            // Texture
            glBindTexture(GL_TEXTURE_2D, texID);

            // VAO
            glBindVertexArray(vao);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
            glBindVertexArray(0);
        }
        glUseProgram(0);
    }
}

main.cpp

//
// main.cpp
//
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include "OpenGLWrapper/window.h"
#include "OpenGLWrapper/shaderUtil.h"
#include "OpenGLWrapper/modelTex.h"

class MyWindow : gl::window
{
private:
    gl::modelTex *myTex;

public:
    MyWindow(
        int w,
        int h,
        const string &name,
        const string &texFileName,
        const string &vertShaderName,
        const string &fragShaderName
    ): window(w, h, name)
    {
        myTex = new gl::modelTex(texFileName, vertShaderName, fragShaderName);
    }
    ~MyWindow()
    {
        if (myTex) delete myTex;
    }

    void render()
    {
        // Rendering loop
        glClearColor(0.3f, 0.5f, 0.8f, 0.0f);
        while (!glfwWindowShouldClose(wnd))
        {
            // Clear color and depth buffers
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            // Draw
            myTex->render();

            // Swap front and back buffers
            glfwSwapBuffers(wnd);
            // Poll for and process events
            glfwPollEvents();
        }
    }
};

int main()
{
    MyWindow wnd(
        512, 512, "GLFW 3 Window",
        "../data/me.jpg",
        "../shader/modelTex.vert",
        "../shader/modelTex.frag"
    );
    wnd.render();

    return 0;
}

modelTex.vert

#version 330

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 uv;

uniform mat4 MVPMat;

out vec2 texCoord;

void main()
{
    gl_Position = MVPMat * vec4(position, 1.0);
    texCoord = uv;
}

modelTex.vert

#version 420

layout(binding = 0) uniform sampler2D texLoc;

in vec2 texCoord;

void main()
{
    gl_FragColor = texture(texLoc, texCoord);
}

結果

動いた!目標達成です.

f:id:Mugichoko:20170516190925j:plain