Notice
Recent Posts
Recent Comments
Link
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Longseabear DevLog

나만의 Texture2D class 만들기 본문

Graphics Project

나만의 Texture2D class 만들기

longseabear 2023. 2. 1. 21:47

더러운 Opengl texture 설정코드를 단순화 해 볼 것이다. 목표는 다음과 같다.

    auto texture = LEapsGL::Texture2D("resources/textures/container.jpg");
    texture.Apply();

    // Draw
    while(true)..
        shaderProgramObject.use();
        shaderProgramObject.setFloat("deltaTime", timeValue);

        texture.use();
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

Unity와 비슷하게 구조화했다. texture class를 통해 이미지를 불러오고, 처리가 모두 끝나면 Apply 함수를 통해 cpu에서 gpu로 전달한다. 그 후, use를 사용하면 자동으로 바인딩 되도록 만들었다. 추후에는 Unity의 material이 multiple shader를 가질 수 있도록 수정할 예정이므로, Texture unit 개념은 material 구현까지 미뤄둔다.

Rule of five & Swap idiom

새롭게 만든 texture2D의 멤버변수는 다음과 같다.

    private:
        GLuint ID;
        TextureFormat format;
        GLuint mipmapCount;
        std::shared_ptr<Pixel> data;
        int width, height, nrChannels;

        std::map<GLuint, GLuint> textureParams;

ID는 opengl에 할당된 texture의 id를 가지고 있다. format은 GL_RGB, GL_RGBA 등 저장된 이미지의 포맷을 의미한다. mipmapCount는 아직 사용하지 않지만, 추후에 mipmap을 구조화 할 때 사용할 예정(유니티 script API document를 참고했다). data는 shared pointer를 통해 이미지를 들고 있을 것이다. Pixel은 그냥 unsigned char.

        friend void swap(Texture2D& lhs, Texture2D& rhs) {
            using std::swap;
            swap(lhs.data, rhs.data);
            swap(lhs.format, rhs.format);
            swap(lhs.height, rhs.height);
            swap(lhs.width, rhs.height);
            swap(lhs.ID, rhs.ID);
            swap(lhs.mipmapCount, rhs.mipmapCount);
            swap(lhs.nrChannels, rhs.nrChannels);
            swap(lhs.textureParams, rhs.textureParams);
        }

swap idiom을 위하여 swap 함수를 만든다. 이렇게 하게 되면 복사할당자와 이동할당자를 쉽게 구현할 수 있다.

        Texture2D& operator=(Texture2D other) {
            swap(*this, other);
            return *this;
        }
        Texture2D& operator=(Texture2D&& other) noexcept {
            swap(*this, other);
            return *this;
        }

이제 여기서 아낀 힘으로 복사 생성자를 구현하면 된다.

        Texture2D(const Texture2D& rhs): Object(){
            this->ID = rhs.ID;
            format = rhs.format;
            mipmapCount = mipmapCount;
            data = rhs.data;

            int size = rhs.width * rhs.height * rhs.nrChannels;
            Pixel* copiedData = new Pixel[size];
            std::memcpy(copiedData, rhs.data.get(), size);
            data = std::shared_ptr<Pixel>(copiedData, stbi_image_free);

            width = rhs.width;
            height = rhs.height;
            nrChannels = rhs.nrChannels;
            textureParams = rhs.textureParams;
        }

이제 이미지를 읽어오는 Load 함수를 만든다

void LEapsGL::Texture2D::Load(const char* path)
{
    glGenTextures(1, &ID);

    // set the texture wrapping parameters
    SetTextureParam(GL_TEXTURE_WRAP_S, GL_REPEAT);
    SetTextureParam(GL_TEXTURE_WRAP_T, GL_REPEAT);

    // set texture filtering parameters
    SetTextureParam(GL_TEXTURE_MIN_FILTER, GL_REPEAT);
    SetTextureParam(GL_TEXTURE_MAG_FILTER, GL_REPEAT);

    stbi_set_flip_vertically_on_load(true);
    data = std::shared_ptr<Pixel>(stbi_load(path, &width, &height, &nrChannels, 0),
        stbi_image_free);
    format = GetTextureImageFormatFromPath(path);

    if (data == nullptr) {
        std::cout << "failed to load texture\n";
    }
}
void LEapsGL::Texture2D::SetTextureParam(GLuint key, GLuint value)
{
    textureParams[key] = value;
}

Texture id를 생성하고 저장한다. Texture param은 opengl에 바로 적용하지 않고 map 자료구조에 저장한다. 나는 영상처리 위주의 opengl 프로젝트를 만들거라 cpu -> gpu로의 이미지 이동이 빈번하게 발생할 수 있고, 옵션도 바꿀 수 있도록 만들것이기 때문에 나중에 Apply 함수가 호출되면 옵션이 적용되도록 구현했다. (현재는 integer만 가능)

void LEapsGL::Texture2D::Apply()
{
    use();
    for (auto iter : textureParams) {
        glTexParameteri(GL_TEXTURE_2D, iter.first, iter.second);
    }

    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data.get());
    glGenerateMipmap(GL_TEXTURE_2D);
}

Apply를 호출하면 모든 파라미터를 현재 바인딩된 텍스쳐에 적용하고 현재 저장된 이미지를 cput -> gpu로 이동시켜준다.
use는 texture를 바인딩해준다.

 

멋진 결과

 

Textutre2D class가 잘 동작하는 것을 확인할 수 있다.

 

'Graphics Project' 카테고리의 다른 글

Voronoi noise 파헤치기  (0) 2024.10.27
Color struct 만들기  (0) 2023.01.31
Shader program wrapper class  (0) 2023.01.28
Opengl 3.3 시작  (0) 2023.01.28