Stable Diffusion으로 게임 그래픽 만들기

게임 그래픽 제작을 AI가 할 수 있다고?

  • 예전에 이 블로그에 clip-interrogator를 이용해서 게임그래픽도 만들 수 있겠다고 소개한 적이 있었죠.
  • Interrogator를 이용해서 게임그래픽을 만들 수 있다??
  • AI그린 게임그래픽
  • 그런데 이렇게 만든 이미지의 문제점이 타일단위로 잘라내기 어렵다는 점이었는데요.
  • 물론 잘 나온 이미지는 굳이 타일로 자르지 않고 그대로 써도 되겠지만
  • 생성형 AI 특성상 만들어지는 이미지를 통제하기가 쉬운것은 아닙니다.

그런데 그래픽 유닛을 만들수 있어요!

  • 그런데 이미 많은 사람들이 AI를 이용해서 게임그래픽을 만드는 시도를 하고 있었더라구요.
  • 아래 사이트에 많은 리소스들이 공개되어 있습니다.
  • OPENART AI 게임그래픽
  • 그래서 저도 한 번 시도해봤어요.
  • 모델은 3개를 썼는데요
    • 제가 직접 제작한 수묵화 스타일의 모델
    • 만화 스타일의 모델
    • 그리고 실사 스타일의 모델
  • 쿼터뷰 시점의 리소스와 탑뷰 시점의 리소스를 만들어 봤습니다.
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
위 광고를 클릭해주시면 코드가 나타납니다.

쿼터뷰 시점 리소스

  • 수묵화 모델
    • 성채이미지
    • 건축물이미지
  • 만화 모델
    • 성채이미지
    • 건축물이미지
  • 실사 모델
    • 성채이미지
    • 건축물이미지
  • 성채 이미지의 경우엔 조금씩 모델의 특징을 따라가는 것으로 보입니다.
    • 사용한 프롬프트는 다음과 같습니다.
    • set of isometric game tiles, containing a wizard's tower, enemies'hideouts and several resources, colored lineart from resource gathering game
    • 수묵화 모델은 색표현이 상당히 플랫한 반면, 만화 모델은 다소 화려하네요.
    • 실사의 경우엔 조금더 디테일이 살아 있습니다.
  • 하지만 건축물이미지의 경우엔 3가지 모델에서 큰 차이를 확인하기가 어렵습니다.
    • 사용한 프롬프트 입니다.
    • isometric view, detailed, medieval tavern and a castle, asset on grey background
    • 그나저나 건축물은 결과물이 엄청나네요. 프로가 그린거 뺨치는 품질입니다.

탑뷰 시점 리소스

  • 수묵화 모델
    • 타일팔레트
    • 나무스프라이트1
    • 나무스프라이트2
    • 나무스프라이트3
  • 만화 모델
    • 타일팔레트1
    • 타일팔레트2
    • 나무스프라이트1
    • 나무스프라이트2
  • 실사 모델
    • 타일팔레트1
    • 타일팔레트2
    • 나무스프라이트1
    • 나무스프라이트2
    • 나무스프라이트3
  • 타일의 경우엔 3가지 모델에서 조금씩 다르지만 쓸만한 결과물들을 얻었습니다.
    • 사용한 프롬프트들은 다음과 같습니다.
    • game tiles 16x16, grass, night and day
    • grass texture, pixel art game asset
    • 잘 응용하면 흙길, 돌길, 물과 같은 타일도 만들수 있을겁니다.
  • 나무의 경우가 참 흥미롭습니다.
    • 사용한 프롬프트들 입니다.
    • a video game sprite sheet of fantasy forest trees
    • game assets of plants and tree
    • 나무같은 경우가 참 표현하기 어렵겠단 생각이 들었었는데, 이렇게 멋진 리소스가 만들어지네요.

앞으로의 목표

  • 이왕 이렇게 된거 게임한번 만들어 봐야 겠죠??
  • 먼저 필요한 그래픽 리소스들을 다 확보해 보도록 하겠습니다.
  • NPC들의 대사의 경우에도 AI를 이용하면 재미있을것 같으니 그 쪽도 한 번 연구해 보겠습니다.

휘어지는 LED로 고양이 끈 장난감 만들기

고양이들 레이저 불빛 참 좋아합니다~
아주 환장하지요~
제가 그래서 예전에 선풍기 가지고
레이저 포인트 200% 활용하기
노하우도 공유한 적이 있는데요.

https://blog.naver.com/dolja21/222950006478

그런데 레이저 포인트는
고양이가 직접 움켜쥘 수 있는
물체가 아니기 때문에
허무함을 줄 수 있단 이야기가 있어요.

https://www.salgoonews.com/news/articleView.html?idxno=17690

그리고 무엇보다 밤이 아니면
불빛이 잘 보이지 않아서
냥이가 잘 반응을 하지 않더라구요.

BAEKOKO 배코코 반딧불 낚시대 광섬유 고양이 장난감 고양이용품 반려동물용품, PINK, 1개
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."



그래서 이렇게 반딧불 낚싯대
장난감도 나왔더라구요.
이 아이는 잡을 수 있어서
허무감이 덜할 것 같군요.
근데 불빛이 좀 작단 느낌이 있었어요.
그래서 만들어 봤습니다!
내용은 아래 영상에서 확인할 수 있으세요.


보시고 좋아요 꾹~
구독도 부탁드려요!!!!

재료는 아래 링크로 가시면 구입할 수 있어요.
만 원도 안 되는 가격으로 만들 수 있었습니다~

휘어지는 LED

가온 스위치라인 USB 전원 LED 조명 2m, 웜화이트(옐로우)


"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."



USB 연결형 건전지 소켓
https://www.devicemart.co.kr/goods/view?no=14908041&NaPm=ct%3Dlradjcvj%7Cci%3Dcheckout%7Ctr%3Dppc%7Ctrx%3Dnull%7Chk%3De67ae3608f4328024a878d881f80ab9c4b088abf
Alt text

Alt text

동영상 속의 자막으로부터 SBV 파일 추출하기

  • 저는 블로그 뿐만 아니라 유튜브 채널도 운영하고 있는데요.
  • 비록 미미한 구독자수와 형편없는 조회수를 자랑하지만, 그래도 1년 넘게 꾸준히 영상을 업로드하고 있습니다.
  • 제가 만드는 컨텐츠들은 주로 영상속에 포함되어 렌더링되는 자막에 크게 의존하고 있는데요. 내용을 이해하는데 이 자막들이 상당히 중요합니다.
  • 그런데 구독자들 중에 외국인들이 생기면서 몇 가지 고민거리가 생겼어요. 그 고민거리는 아래와 같습니다.
    • 적어도 영어 자막은 제공하고 싶다.
    • 사용하는 영상제작 프로그램에서 sbv와 같은 시퀀싱 자막 파일을 추출할 수 없다.
    • 매번 유튜브 자막제작툴을 이용하고 있는데, 타임 레인지 맞추는게 너무 불편하다.

자막편집의고통

  • 이런 고민을 해결해보려고 여러가지 프로그램을 찾아봤지만, 뾰족한 대안을 찾기어려웠어요.
  • 그래서! 직접 만들어 봤습니다. 전체코드는 아래 링크로 공유할게요.
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
위 광고를 클릭해주시면 코드가 나타납니다.
import easyocr
from difflib import SequenceMatcher
import cv2
import os
import numpy
import scipy.cluster.hierarchy as hcluster
import matplotlib.pyplot as plt
from datetime import timedelta

def get_text_from_frame(img_file):
    reader = easyocr.Reader(['ko','en']) # this needs to run only once to load the model into memory
    result = reader.readtext(img_file)
    if len(result) > 0:
        text = ''
        for item in result:
            if len(item) > 1:
                text = "%s %s" % (text, item[1])
        return text
    return ''

def text_diff(t1, t2):
    return SequenceMatcher(None, t1, t2).ratio()

def frame_to_time(fps, frame):
    as_msecond = (frame / fps) * 1000
    td = timedelta(milliseconds=as_msecond)
    if str(td).find('.') == -1:
        return "%s.000" % str(td)
    return str(td)[:-3]

filepath = '[추출할동영상].mp4'
video = cv2.VideoCapture(filepath)
if not video.isOpened():
    print("Could not Open :", filepath)
    exit(0)
length = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = video.get(cv2.CAP_PROP_FPS)
fps2 = int(fps/2)
before_text = ''
count = 0
before_diff = 0
start_frame = 0
end_frame = 0
data = []
text_count = {}
while(video.isOpened() and count < length):
    ret, image = video.read()
    if(int(video.get(1)) % fps2 == 0): #앞서 불러온 fps 값을 사용하여 0.5초마다 추출
        cv2.imwrite('frame.png', image)
        text = get_text_from_frame('frame.png')
        diff = text_diff(text, before_text)
        if diff < 0.75:
            diff = 0.0
        else:
            diff = 1.0
        if text == '':
            diff = 0.0
        else:
            if text in text_count:
                text_count[text] = text_count[text] + 1
            else:
                text_count[text] = 1
        print("%s, %s" % (diff, text))
        if before_diff == 0.0 and diff == 1.0:
            start_frame = count
        elif before_diff == 1.0 and diff == 0.0:
            end_frame = count
            sorted_text_count = sorted(text_count.items(), key = lambda item: item[1], reverse = True)
            print(sorted_text_count)
            if sorted_text_count[0][0] != '':
                data.append([start_frame, end_frame, sorted_text_count[0][0]])
            text_count = {}
        before_diff = diff
        before_text = text

    print("%d / %d" % (count, length))
    if count < length:
        count = count + 1

if start_frame > end_frame:
    end_frame = count
    sorted_text_count = sorted(text_count.items(), key = lambda item: item[1], reverse = True)
    print(sorted_text_count)
    if sorted_text_count[0][0] != '':
        data.append([start_frame, end_frame, sorted_text_count[0][0]])

video.release()

f = open('[자막파일명].sbv', 'w')
for cap in data:
    start_time = frame_to_time(fps, cap[0])
    end_time = frame_to_time(fps, cap[1])
    f.write("%s,%s\n" % (start_time, end_time))
    f.write("%s\n\n" % cap[2])
f.close()

전체코드 다운로드

  • 주의사항
    • CUDA를 지원하는 GPU가 없을 경우 CPU만 이용해서 OCR을 수행하기 때문에 매우 느립니다.
    • GPU가 없다면 구글 코랩에서 하드웨어 가속기를 이용해보세요.
  • 원래는 번역까지 자동으로 되게하는 코드를 작성하려고 했었습니다만…
  • OCR의 품질이 번역 가능할 수 있는 정도까지 결과를 만들어내지는 못합니다.
  • 일단 타임레인지를 잡아내는 정도에서 만족해 보려고 하구요.
  • 아래 영상은 자동으로 추출한 타임레인지의 SBV 파일에서 영어 번역만 수행해서 올린 것 입니다.