[C++] 4.1 외부 이미지 사용하기

Date:     Updated:

카테고리:

태그:

인프런에 있는 홍정모 교수님의 홍정모의 게임 만들기 연습 문제 패키지 강의를 듣고 정리한 필기입니다.😀
🌜 공부에 사용된 홍정모 교수님의 코드들 보러가기
🌜 [홍정모의 게임 만들기 연습 문제 패키지] 강의 들으러 가기!


Chapter 4. 외부 이미지 사용하기

드디어 마지막 챕터!! 🙆‍♂️

🔔 stb 라이브러리 설치하기

  • 우선 외부 이미지를 사용하려면 stb 라이브러리를 설치해야한다
    • cmd에서 cd를 통해 vcpkg 폴더로 이동한 후 vcpkg - install -stb:x64-windows 명령어로 설치하기
      • 설치 좀 많이 오래걸린다.
  • 비주얼 스튜디오 - 프로젝트 속성 - C/C++ - 추가 포함 디렉터리 에서 stb 헤더 파일들이 있는 폴더를 환경변수로 지정해주기

코드

image

#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
          • 문구 이미지
  • 생성자
    • init 함수
      • 각 이미지 객체를 초기화 한다.
      • 각 이미지의 파일명(필수) 을 매개변수로 넘긴다.
        • house.init(“car.png”, 255, 255, 255);
          • 원하는 색을 투명하게 만들어주는 코드
          • 255, 255, 255 는 하얀색이므로 이미지 내의 하얀색 부분들을 투명하게 만듬
        • house.init(“car.png”, 0, 0, 0);
          • 이면 검정색 부분들을 투명하게 만듬
  • 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에 가까우면 투명
    • 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배열로부터 모든 해상도 픽셀의 색상을 가져오며 비교
  • ratio = static_cast<float>(width) / static_cast<float>(height);
    • width와 height의 비율 저장
      • 정사각형이면 1일 것.
      • 이미지를 저장하고 불러올때 컴퓨터는 정사각형으로 저장함.
        • 다시 불러올때 그 비율대로 덧씌우기 위해.
        • 정사각형으로 그리고 원래 비율대로 덧씌우는 방식이다.
  • stbi_image_free(data);
    • 맨끝에서 이렇게 data를 해제 해준다.
    • data는 동적할당 포인터
    • stb 라이브리러 함수 이용
  • void ImageObject::draw()
    • scale(ratio * 0.5f, 1.0f * 0.5f);
      • 기본적으로 2x2 정사각형으로 저장되기 때문에 1x1로 맞춰주기 위해 0.5를 곱했고
      • 원래 비율에 맞게 ratio를 x방향에 곱해주고 있다.
      • 나머지는 이미지를 그려주는 과정.


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

댓글남기기