[C++] 4.1 외부 이미지 사용하기
카테고리: C++ games
태그: Cpp Graphics OpenGL Programming
인프런에 있는 홍정모 교수님의 홍정모의 게임 만들기 연습 문제 패키지 강의를 듣고 정리한 필기입니다.😀
🌜 공부에 사용된 홍정모 교수님의 코드들 보러가기
🌜 [홍정모의 게임 만들기 연습 문제 패키지] 강의 들으러 가기!
Chapter 4. 외부 이미지 사용하기
드디어 마지막 챕터!! 🙆♂️
🔔 stb 라이브러리 설치하기
- 우선 외부 이미지를 사용하려면
stb 라이브러리
를 설치해야한다- cmd에서 cd를 통해 vcpkg 폴더로 이동한 후 vcpkg - install -stb:x64-windows 명령어로 설치하기
- 설치 좀 많이 오래걸린다.
- cmd에서 cd를 통해 vcpkg 폴더로 이동한 후 vcpkg - install -stb:x64-windows 명령어로 설치하기
- 비주얼 스튜디오 - 프로젝트 속성 - C/C++ - 추가 포함 디렉터리 에서 stb 헤더 파일들이 있는 폴더를 환경변수로 지정해주기
코드
#include "Game2D.h"
#include "Examples/PrimitivesGallery.h"
#include "RandomNumberGenerator.h"
#include "ImageObject.h"
#include <vector>
#include <memory>
namespace jm
{
using namespace std;
class Example : public Game2D
{
public:
ImageObject house;
ImageObject background;
ImageObject sentence;
Example()
: Game2D("This is my digital canvas!", 1280, 960, false) // MUST initialize Game2D
{
house.init("car.png", 255, 255, 255); // white to transparent
background.init("Background_image.bmp");
sentence.init("sentence.png");
}
void update() override
{
beginTransformation();
scale(1.5f, 1.5f);
background.draw();
endTransformation();
beginTransformation();
translate(-0.5f, 0.0f);
drawFilledStar(Colors::gold, 0.2f, 0.1f); // 별그리기
endTransformation();
beginTransformation();
translate(0.0f, 0.5f);
scale(0.5f, 0.5f);
sentence.draw();
endTransformation();
// moving car
{
static float x = -1.0f;
beginTransformation();
translate(x, 0.0f);
scale(0.5f, 0.5f);
house.draw();
endTransformation();
x += 0.01f;
if (x > 1.0f) x = -1.0f;
}
beginTransformation();
translate(0.5f, 0.0f);
drawFilledStar(Colors::red, 0.2f, 0.1f); // 별그리기
endTransformation();
//background.draw(); //this hides everything 덮어씌워지니까
}
};
}
int main(void)
{
jm::Example().run();
return 0;
- #include “ImageObject.h”
ImageObject.h
클래스 헤더파일을 include 해준다.- ImageObject
- 이미지 객체의 역할을 할 것이다.
- ImageObject house
- 차 이미지
- ImageObject background
- 배경 이미지
- ImageObject sentence
- 문구 이미지
- ImageObject house
- 이미지 객체의 역할을 할 것이다.
- 생성자
- init 함수
- 각 이미지 객체를 초기화 한다.
- 각 이미지의
파일명(필수)
을 매개변수로 넘긴다.- house.init(“car.png”, 255, 255, 255);
- 원하는 색을 투명하게 만들어주는 코드
- 255, 255, 255 는 하얀색이므로 이미지 내의 하얀색 부분들을 투명하게 만듬
- house.init(“car.png”, 0, 0, 0);
- 이면 검정색 부분들을 투명하게 만듬
- house.init(“car.png”, 255, 255, 255);
- init 함수
- void update() override
- 배경 그리기
beginTransformation(); scale(1.5f, 1.5f); background.draw(); endTransformation();
- 문장 그리기
beginTransformation(); translate(0.0f, 0.5f); 이동 scale(0.5f, 0.5f); 크기를 키운 후 sentence.draw(); endTransformation();
- 움직이는 차 그리기
{ static float x = -1.0f; beginTransformation(); translate(x, 0.0f); scale(0.5f, 0.5f); house.draw(); endTransformation(); x += 0.01f; if (x > 1.0f) x = -1.0f; }
- 문장 그리기
- 배경 그리기
- 노란별 → 차 → 빨간별 순서로 그렸기 때문에 노란별은 차 뒤에있고 빨간별은 차 앞에 덮는 식으로 그려진다.
📜ImageObject.h
#pragma once
#include "Game2D.h"
#include <string>
namespace jm
{
using namespace std;
class ImageObject
{
public:
unsigned int texture;
float ratio = 1.0f;
ImageObject()
{}
ImageObject(const string & filename)
{
init(filename);
}
// Use tr_r, tr_g, tr_b set transparent color
void init(const string & filename, const int tr_r = -1, const int tr_g = -1, const int tr_b = -1);
void draw();
};
}
- unsigned int texture
- float ratio = 1.0f;
- 생성자
- 파일 이름을 매개변수로 받는다.
- init 함수를 호출한다.
📜ImageObject.cpp
#include "ImageObject.h"
// https://learnopengl.com/Getting-started/Textures
// vcpkg install stb:x64-windows
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
namespace jm
{
// Use tr_r, tr_g, tr_b set transparent color
void ImageObject::init(const string & filename, const int tr_r, const int tr_g, const int tr_b)
{
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
int width, height, n_ch;
unsigned char *data = stbi_load(filename.c_str(), &width, &height, &n_ch, 0);
if (data)
{
if (n_ch == 4)
{
int count = 0;
for (int j = 0; j < height; ++j)
for (int i = 0; i < width; ++i)
{
unsigned char & r = data[count];
unsigned char & g = data[count + 1];
unsigned char & b = data[count + 2];
if (r == tr_r && g == tr_g && b == tr_b)
{
data[count + 3] = 0; // alpha = 0 -> transparent
}
count += 4;
}
}
//cout << width << " " << height << " " << n_ch << endl;
ratio = static_cast<float>(width) / static_cast<float>(height);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (n_ch == 4)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else if (n_ch == 3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
else
{
printf("Use RGB or RGBA images. Your input image has %d channels.", n_ch);
}
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
}
void ImageObject::draw()
{
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
beginTransformation();
scale(ratio * 0.5f, 1.0f * 0.5f);
//glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
glColor3f(1.0f, 1.0f, 1.0f);
// upside down texture coordinates
glTexCoord2d(0.0f, 1.0f);
glVertex2d(-1.0f, -1.0f);
glTexCoord2d(1.0f, 1.0f);
glVertex2d(1.0f, -1.0f);
glTexCoord2d(1.0f, 0.0f);
glVertex2d(1.0f, 1.0f);
glTexCoord2d(0.0f, 0.0f);
glVertex2d(-1.0f, 1.0f);
/*glTexCoord2d(0.0f, 0.0f);
glVertex2d(-1.0f, -1.0f);
glTexCoord2d(1.0f, 0.0f);
glVertex2d(1.0f, -1.0f);
glTexCoord2d(1.0f, 1.0f);
glVertex2d(1.0f, 1.0f);
glTexCoord2d(0.0f, 1.0f);
glVertex2d(-1.0f, 1.0f);*/
glEnd();
endTransformation();
glDisable(GL_TEXTURE_2D);
}
}
- void init (const string & filename, const int tr_r = -1, const int tr_g = -1, const int tr_b = -1);
- (tr_r, tr_g, tr_b) 색상을 투명하게 만든다.
- 매개변수 기본 값이 -1로 설정되어 있어 색상 매개변수 굳이 넘기지 않고 파일이름만 넘겨도 된다.
- (-1,-1,-1)은 있을 수 없는 색상이라 그냥 넘어가게 됨.
- glEnable(GL_BLEND);
- alpha blending을 하겠다는 얘기
- alpha
- 불투명도
- alpha가 1에 가까우면 불투명, 0에 가까우면 투명
- alpha
- alpha blending을 하겠다는 얘기
- glEnable(GL_TEXTURE_2D);
- 이미지를 읽어와서 텍스처로 사용한다.
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- alpha blending을 어떻게 할지 설정
- int width, height, n_ch
- unsigned char *data = stbi_load(filename.c_str(), &width, &height, &n_ch, 0);
- data는 동적할당 array의 포인터가 된다
- stb 라이브러리의 함수 stbi_load
- 이미지 파일을 읽어온다
- width, height는 해상도
- n_ch
- 채널의 개수
- RGB 이렇게 3개의 채널인 경우 n_ch = 3
- RGB + alpha 이렇게 4개의 채널인 경우 n_ch = 4
- if (data) : 데이터가 제대로 읽어 졌다면
- if (n_ch == 4)
- 4채널인 경우. 즉, alpha를 사용하는 경우
- 특정한 색 ( tr_r, tr_g, tr_b ) 의 alpha를 0으로. (alpha는 0에 가까울수록 투명)
- 이중 for를 돌면서 data배열로부터 모든 해상도 픽셀의 색상을 가져오며 비교
- 4채널인 경우. 즉, alpha를 사용하는 경우
- if (n_ch == 4)
- ratio = static_cast<float>(width) / static_cast<float>(height);
- width와 height의 비율 저장
- 정사각형이면 1일 것.
- 이미지를 저장하고 불러올때 컴퓨터는 정사각형으로 저장함.
- 다시 불러올때 그 비율대로 덧씌우기 위해.
- 정사각형으로 그리고 원래 비율대로 덧씌우는 방식이다.
- width와 height의 비율 저장
- stbi_image_free(data);
- 맨끝에서 이렇게 data를 해제 해준다.
- data는 동적할당 포인터
- stb 라이브리러 함수 이용
- void ImageObject::draw()
- scale(ratio * 0.5f, 1.0f * 0.5f);
- 기본적으로 2x2 정사각형으로 저장되기 때문에 1x1로 맞춰주기 위해 0.5를 곱했고
- 원래 비율에 맞게 ratio를 x방향에 곱해주고 있다.
- 나머지는 이미지를 그려주는 과정.
- scale(ratio * 0.5f, 1.0f * 0.5f);
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기