0927

20250927

카테고리: 게임엔진, GIF 렌더링


0927_0.gif


GIF 렌더링을 구현했다.

원래는 stb를 써서 했었는데,

stb가 완전하지 않다는 걸 알게 된 후 -

giflib 이라는 라이브러리를 가져와서 로딩하게 되었다.

stb로 gif를 로딩했을 때

내가 겪은 문제는 3개가 있다.

  1. disposal mode 기능을 지원하질 않아서, 일부 gif에 잔상(이전프레임) 이 남는다.
  2. 여백이어야 할 부분에 이상한 색이 채워져 있거나, 특정 부분이 너무 밝게 나온다(이건 압축 관련 문제일듯).
  3. duration 을 제대로 못 읽는다. 아예 읽지 못하거나, 지연시간을 너무 짧게 읽는다.

RenderDoc 으로 캡처해서 (1) 과 (2) 를 시각화해 보았다.

밑에 그림을 참고하길 바란다.

0927_1.png

왼쪽이 giflib, 오른쪽이 stb_image이다.

첫 번째 행은 disposal mode 오류,

두 번째 행은 아티팩트 오류이다.


아무튼 렌더링 과정은 다음과 같다.

        giftex_arr = renderer->create_texture_2d_array();
        giftex_arr->initialize_empty(seq.get_unit_size(), seq.get_image_count(), dp::bcknd::TextureDesc::make_for_rgba());
        for (uint32_t i = 0; i < seq.get_image_count(); i++) {
            giftex_arr->upload_sub_image(
                { 0, 0, i },
                seq.get_unit_size(),
                1,
                dp::bcknd::PixelFormat::RGBA,
                dp::bcknd::DataType::UnsignedByte,
                0,
                seq.get_image_data_at(i)
            );
        }
        for (int i = 0; i < seq.get_image_count(); i++) {
            giftex_durs.push_back(seq.get_delay_data_at(i));
        }

아주아주 옛날에는

Texture2D 를 여러개 만들었었는데,

이번에는 Texture2DArray 를 써보자 해서 그걸로 구현해놨다.

중요한 게 있다면

(처음에는 자세히 몰랐지만)

GIF 표준에 '프레임과 프레임 사이의 duration' 속성이 존재한다는 것이다.

update() 에서 해당 프레임 이후의 duration 만큼 쉬어주는 것도

구현을 해 주었다.


마지막에는 AnimatedSprite2D 라는 클래스로

특수화해주었다.

class AnimatedSprite2D : public IObject2D {
    DP_OBJECT_DEF(AnimatedSprite2D, IObject2D);
private:
    const bcknd::ITexture2DArray* m_texture_array = nullptr;
private:
    uint32_t m_frame = 0;
    uint32_t m_max_frame_count = 0;
private:
    std::vector<double> m_durations{};
    size_t m_duration_index = 0;
    double m_last_time = 0.0;
private:
    bool m_is_playing = false;
    bool m_animation_repeated = true /*false*/;
private:
    Vector2f m_size = { 64, 64 };
    Color4f m_modulate = White;
public:
    AnimatedSprite2D(IScene& owner, const std::string& name);
    virtual ~AnimatedSprite2D() override;