link : http://www.opengl-tutorial.org/kr/beginners-tutorials/tutorial-5-a-textured-cube/
이번 튜토리얼부터는 영어다!
앞으로는 해석과 함께 중요한 개념들을 정리해서 올리도록 하겠다.
[배울 것]
1) UV좌표
2) 직접 텍스처를 입히는 방법
3) OpenGL에서 사용하는 방법
4) 필터링과 맵핑이 무엇이고, 어떻게 사용하는지
5) GLFW로 견고하게 텍스처를 입히는 방법
1) UV Coordinate
- mesh를 Texturing할 때 각 삼각형에 대해 이미지의 어느 부분을 사용해야하는지 OpenGL에 알리는 방법이 필요하다.
이 것은 UV 좌표로 수행된다.
각 정점은, 그 위치의 최상부에 2개의 부동 소수점 U와 V를 가질 수 있다.
이러한 좌표는 다음의 방법으로 텍스처에 액세스하기 위해서 사용된다.
삼각형에 텍스처가 왜곡되어 있는지 확인해야한다.
2) BMP images
- 이 튜토리얼에서는 BMP 파일 형식을 아는 것이 중요하지는 않으나, 매우 간단하고 이해하면 도움이 되므로 한 번 보고 간다고 한다.
이 부분은 딱히 메모할 부분이 없으므로 통과
3) Using the texture in OpenGL
- 먼저 조각 쉐이더를 살펴본다. 대부분이 간단하다.
#version 330 core
// Interpolated values from the vertex shaders
in vec2 UV;
// Ouput data
out vec3 color;
// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;
void main(){
// Output color = color of the texture at the specified UV
color = texture( myTextureSampler, UV ).rgb;
}
Three things :
1) 조각 쉐이더에는 UV 좌표가 필요하다
2) 또한, 액세스 할 텍스쳐를 알기 위해 "sampler2D"가 필요하다. (동일한 쉐이더에서 여러 택스처에 액세스 가능)
3) 마지막으로 Texture에 접근하면 (R,G,B,A) vec4를 돌려준다.
- 버텍스 쉐이더도 살표보자. UV를 프래그먼트 쉐이더에 전달하면 된다.
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
// Output data ; will be interpolated for each fragment.
out vec2 UV;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
// UV of the vertex. No special space for this one.
UV = vertexUV;
}
튜토리얼 4의 "layout(location = 1) in vec2 vertexUV"를 기억하고 있습니까? 우리는 여기서 똑같은 작업을 할 것이다.
버퍼(R, G, B)를 세 쌍으로 제공하는 대신 (U, V)쌍의 버퍼를 제공할 것이다.
// Two UV coordinatesfor each vertex. They were created with Blender. You'll learn shortly how to do this yourself.
static const GLfloat g_uv_buffer_data[] = {
0.000059f, 1.0f-0.000004f,
0.000103f, 1.0f-0.336048f,
0.335973f, 1.0f-0.335903f,
1.000023f, 1.0f-0.000013f,
0.667979f, 1.0f-0.335851f,
0.999958f, 1.0f-0.336064f,
0.667979f, 1.0f-0.335851f,
0.336024f, 1.0f-0.671877f,
0.667969f, 1.0f-0.671889f,
1.000023f, 1.0f-0.000013f,
0.668104f, 1.0f-0.000013f,
0.667979f, 1.0f-0.335851f,
0.000059f, 1.0f-0.000004f,
0.335973f, 1.0f-0.335903f,
0.336098f, 1.0f-0.000071f,
0.667979f, 1.0f-0.335851f,
0.335973f, 1.0f-0.335903f,
0.336024f, 1.0f-0.671877f,
1.000004f, 1.0f-0.671847f,
0.999958f, 1.0f-0.336064f,
0.667979f, 1.0f-0.335851f,
0.668104f, 1.0f-0.000013f,
0.335973f, 1.0f-0.335903f,
0.667979f, 1.0f-0.335851f,
0.335973f, 1.0f-0.335903f,
0.668104f, 1.0f-0.000013f,
0.336098f, 1.0f-0.000071f,
0.000103f, 1.0f-0.336048f,
0.000004f, 1.0f-0.671870f,
0.336024f, 1.0f-0.671877f,
0.000103f, 1.0f-0.336048f,
0.336024f, 1.0f-0.671877f,
0.335973f, 1.0f-0.335903f,
0.667969f, 1.0f-0.671889f,
1.000004f, 1.0f-0.671847f,
0.667979f, 1.0f-0.335851f
};
위의 UV 좌표는 다음 모델에 해당한다.
나머지는 명백하다. 버퍼를 생성하고, 바인딩하고, 채우고, 구성하고, 평소와 같인 Vertex Buffer를 그린다.
3 대신 glVertexAttribPointer의 두 번째 매개 변수로 2를 사용하도록 주의해라.
- 결과
- Zoomed-in version
4) What is filtering and mipmapping, and how to use them
- 위 스크린샷에서 볼 수 있듯이 텍스처 품질이 그다지 좋지 않다. 이는 loadBMP_custom에서 다음과 같이 작성했기 때문이다.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
이 것은 조각 쉐이더에서 texture()가 (U,V) 좌표에 있는 texel을 가져와서 진행하는 것을 의미한다.
이를 향상시키기 위해 할 수 있는 일이 몇가지 있다.
1) Linear filtering
- 선형 필터링을 사용하면 texture()는 주변의 다른 texel을 보고 각 중심까지의 거리에 따라 색상을 혼합한다.
이렇게 하면 위의 딱딱한 가장자리를 피할 수 있다.
이 것은 훨씬 더 좋고 많이 사용된다. 하지만 매우 높은 품질을 원한다면 약간 더 느린 aniostropic 필터링을 사용할 수 있다.
2) Anisotropic filtering
- 이 것은 조각을 통해 실제로 보인 이미지 부분을 근사한다.
예를 들어, 다음 텍스쳐가 측면에서 보이고 조금 회전하면 anisotropic 필터링은 주 방향을 따라 고정 된 수의 샘플을
취해 파란색 직사각형에 포함 된 색상을 계산한다.
3) Mipmaps
- 선형 필터링과 anisotropic 필터링 모두 텍스처가 멀리서 보일 경우 4가지 texel로는 충분하지 않다는 문제가 존재한다.
사실, 3D 모델이 화면에서 단 하나의 단편을 차지하는 것보다 멀리 떨어져 있다면, 이미지의 모든 texel을 평균해 색상을 만들어야 한다.
이 것은 성능 상의 이유로 분명히 행해지지는 않는다. 대신에 Mipmaps을 소개한다.
[1] 초기화 시간에 1x1 이미지 (사실상 이미지의 모든 texel의 평균)가 될 때까지 이미지를 2씩 축소시킨다.
[2] 메쉬를 그릴 때 texel이 얼마나 커야 하는지를 고려해 어느 밉맵을 사용하는 것이 더 적합한지 선택한다.
[3] 가장 가까운 선형 또는 anisotropic 필터링을 사용해 이 밉맵을 샘플링한다.
[4] 추가 품질을 위해 두 개의 밉맵을 샘플링하고, 결과를 혼합 할 수도 있다.
운이 좋게도 이 모든 작업은 OpenGL이 우리에게 제공한다.
// When MAGnifying the image (no bigger mipmap available), use LINEAR filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// When MINifying the image, use a LINEAR blend of two mipmaps, each filtered LINEARLY too
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// Generate mipmaps, by the way.
glGenerateMipmap(GL_TEXTURE_2D);
5) How to load texture with GLFW
- loadBMP_custom 함수는 우리가 만들었기 때문에 훌륭하지만 전용 라이브러리를 사용하는 것이 좋다.
GLuint loadTGA_glfw(const char * imagepath){
// Create one OpenGL texture
GLuint textureID;
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
// Read the file, call glTexImage2D with the right parameters
glfwLoadTexture2D(imagepath, 0);
// Nice trilinear filtering.
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
// Return the ID of the texture we just created
return textureID;
}
이미지를 로드하는 방법을 살펴 보겠다. 헤더가 다르게 구성된다는 점을 제외하면 BMP 코드와 매우 유사하다.
GLuint loadDDS(const char * imagepath){
unsigned char header[124];
FILE *fp;
/* try to open the file */
fp = fopen(imagepath, "rb");
if (fp == NULL)
return 0;
/* verify the type of file */
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return 0;
}
/* get the surface desc */
fread(&header, 124, 1, fp);
unsigned int height = *(unsigned int*)&(header[8 ]);
unsigned int width = *(unsigned int*)&(header[12]);
unsigned int linearSize = *(unsigned int*)&(header[16]);
unsigned int mipMapCount = *(unsigned int*)&(header[24]);
unsigned int fourCC = *(unsigned int*)&(header[80]);
After the header is the actual data : all the mipmap levels, successively. We can read them all in one batch : (해석불가)
unsigned char * buffer;
unsigned int bufsize;
/* how big is it going to be including all mipmaps? */
bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
fread(buffer, 1, bufsize, fp);
/* close the file pointer */
fclose(fp);
여기서는 DXT1, DXT3 및 DXT5의 3가지 형식을 다루겠다. 우리는 "fourCC"플래그를 OpenGL이 이해할 수 있는 값으로 변환해야 한다.
unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
unsigned int format;
switch(fourCC)
{
case FOURCC_DXT1:
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT3:
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case FOURCC_DXT5:
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
free(buffer);
return 0;
}
텍스쳐를 생성하는 것은 평소와 같이 처리된다.
// Create one OpenGL texture
GLuint textureID;
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
이제 각 밉맵을 하나씩 채워 넣으면 된다.
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
/* load the mipmaps */
for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
{
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
0, size, buffer + offset);
offset += size;
width /= 2;
height /= 2;
}
free(buffer);
return textureID;
[결론]
- OpenGL에서 텍스처를 만들고 입히는 방법을 배웠다.
일반적으로 압축 된 텍스처는 저장하기에 더 작고 로드하는데 거의 즉각적이며 사용하기가 더 빠르기 때문에 사용해한다.
Compressonator(또는 유사한 도구)의 주된 단점은 이미지를 변환해야 한다는 점이다.
위 내용을 공부 후에 코드를 따라 작성하면서 튜토리얼을 진행했다.
아무리해도 까만 정육면체만 나오길래 튜토리얼 코드와 하나씩 비교하면서 확인해봤더니
위 코드에서 filecode가 "DDS"가 아닌 "DDS " 띄어쓰기를 하나 더 포함한 문자열이었다. (금방 찾아서 다행이지 오래 걸렸으면 허탈했을듯)
이 문제를 해결하고 나니
이번에는 숫자들이 뒤엉키는 문제가 발생했다. 실수로 이전에 사용했던 color를 그대로 두고 uv로 바꾸지 않아서 생긴 문제였다.
uv로 변경해서 성공!
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 | #include <stdio.h> #include <stdlib.h> #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <GL/glew.h> #include <glfw3.h> GLFWwindow* window; #include <glm/glm.hpp> #include <glm/gtx/transform.hpp> using namespace glm; #define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII #define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII #define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII GLuint LoadShaders(const char *, const char *); GLuint loadBMP_custom(const char *); GLuint loadDDS(const char *); int main() { // Initialise GLFW if (!glfwInit()) { fprintf(stderr, "Failed to initialize GLFW\n"); getchar(); return -1; } glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Open a window and create its OpenGL context window = glfwCreateWindow(1024, 768, "QBOT_opengl", NULL, NULL); if (window == NULL) { fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n"); getchar(); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // Initialize GLEW glewExperimental = true; if (glewInit() != GLEW_OK) { fprintf(stderr, "Failed to initialize GLEW\n"); getchar(); glfwTerminate(); return -1; } // Ensure we can capture the escape key being pressed below glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); // Dark blue background glClearColor(0.0f, 0.0f, 0.4f, 0.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); GLuint VertexArrayID; glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); //Shader를 불러온다. GLuint programID = LoadShaders("TransformVertexShader.vertexshader", "TextureFragmentShader.fragmentshader"); //매트릭스ID 추가 GLuint MatrixID = glGetUniformLocation(programID, "MVP"); glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f); //카메라 매트릭스 glm::mat4 View = glm::lookAt( glm::vec3(-4, -3, 3), //카메라 위치 glm::vec3(0, 0, 0), //보는 Look glm::vec3(0, 1, 0) //head-up ); //모델 glm::mat4 Model = glm::mat4(1.0f); //우리의 모델뷰로젝션 : 3개의 행렬의 곱 glm::mat4 MVP = Projection * View * Model; //어떠한 두 가지의 함수를 사용해서 텍스처를 불러온다 //GLuint Texture = loadBMP_custom("uvtemplate.bmp"); GLuint Texture = loadDDS("uvtemplate.DDS"); GLuint TextureID = glGetUniformLocation(programID, "myTextureSampler"); //vertex 데이터 static const GLfloat g_vertex_buffer_data[] = { -1.0f,-1.0f,-1.0f, -1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f, -1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f, -1.0f,-1.0f, 1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f }; // color 데이터 static const GLfloat g_uv_buffer_data[] = { 0.000059f, 1.0f - 0.000004f, 0.000103f, 1.0f - 0.336048f, 0.335973f, 1.0f - 0.335903f, 1.000023f, 1.0f - 0.000013f, 0.667979f, 1.0f - 0.335851f, 0.999958f, 1.0f - 0.336064f, 0.667979f, 1.0f - 0.335851f, 0.336024f, 1.0f - 0.671877f, 0.667969f, 1.0f - 0.671889f, 1.000023f, 1.0f - 0.000013f, 0.668104f, 1.0f - 0.000013f, 0.667979f, 1.0f - 0.335851f, 0.000059f, 1.0f - 0.000004f, 0.335973f, 1.0f - 0.335903f, 0.336098f, 1.0f - 0.000071f, 0.667979f, 1.0f - 0.335851f, 0.335973f, 1.0f - 0.335903f, 0.336024f, 1.0f - 0.671877f, 1.000004f, 1.0f - 0.671847f, 0.999958f, 1.0f - 0.336064f, 0.667979f, 1.0f - 0.335851f, 0.668104f, 1.0f - 0.000013f, 0.335973f, 1.0f - 0.335903f, 0.667979f, 1.0f - 0.335851f, 0.335973f, 1.0f - 0.335903f, 0.668104f, 1.0f - 0.000013f, 0.336098f, 1.0f - 0.000071f, 0.000103f, 1.0f - 0.336048f, 0.000004f, 1.0f - 0.671870f, 0.336024f, 1.0f - 0.671877f, 0.000103f, 1.0f - 0.336048f, 0.336024f, 1.0f - 0.671877f, 0.335973f, 1.0f - 0.335903f, 0.667969f, 1.0f - 0.671889f, 1.000004f, 1.0f - 0.671847f, 0.667979f, 1.0f - 0.335851f }; GLuint vertexbuffer; glGenBuffers(1, &vertexbuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); GLuint uvbuffer; glGenBuffers(1, &uvbuffer); glBindBuffer(GL_ARRAY_BUFFER, uvbuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW); do { // Clear the screen. It's not mentioned before Tutorial 02, but it can cause flickering, so it's there nonetheless. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(programID); //transformation을 현재 쉐이더에 보냄 glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]); //텍스처 유닛0에 있는 텍스처를 바인딩한다. glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, Texture); //"myTextureSampler" 셈플러를 유저 텍스처 유닛 0에 세팅한다. glUniform1i(TextureID, 0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glVertexAttribPointer( 0, //0번째 속성. 0이 될 특별한 이유는 없지만 쉐이더의 레이아웃과 반드시 맞춰야함 3, //크기(size) GL_FLOAT, //타입(type) GL_FALSE, //정규화(normalized)? 0, //다음 요소까지의 간격(stride) (void*)0 //배열 버퍼의 오프셋(offset) ); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, uvbuffer); glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0 ); glDrawArrays(GL_TRIANGLES, 0, 12 * 3); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); // Swap buffers glfwSwapBuffers(window); glfwPollEvents(); } // Check if the ESC key was pressed or the window was closed while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0); // Cleanup VBO glDeleteBuffers(1, &vertexbuffer); glDeleteVertexArrays(1, &VertexArrayID); glDeleteProgram(programID); // Close OpenGL window and terminate GLFW glfwTerminate(); return 0; } GLuint LoadShaders(const char * vertex_file_path, const char * fragment_file_path) { //쉐이더 생성 GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); //버텍스 쉐이더 코드를 파일에서 읽기 std::string VertexShaderCode; std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); if (VertexShaderStream.is_open()) { std::stringstream sstr; sstr << VertexShaderStream.rdbuf(); VertexShaderCode = sstr.str(); VertexShaderStream.close(); } else { printf("파일 %s를 읽을 수 없음. 정확한 디렉토리를 사용 중입니까?\n", vertex_file_path); getchar(); return 0; } //프래그먼트 쉐이더 코드를 파일에서 읽기 std::string FragmentShaderCode; std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); if (FragmentShaderStream.is_open()) { std::stringstream sstr; sstr << FragmentShaderStream.rdbuf(); FragmentShaderCode = sstr.str(); FragmentShaderStream.close(); } GLint Result = GL_FALSE; int InfoLogLength; //버텍스 쉐이더를 컴파일 printf("Compiling shader : %s\n", vertex_file_path); char const * VertexSourcePointer = VertexShaderCode.c_str(); glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL); glCompileShader(VertexShaderID); //버텍스 쉐이더를 검사 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0) { std::vector<char> VertexShaderErrorMessage(InfoLogLength + 1); glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); printf("%s\n", &VertexShaderErrorMessage[0]); } //프래그먼트 쉐이더를 컴파일 printf("Compiling shader : %s", fragment_file_path); char const * FragmentSourcePointer = FragmentShaderCode.c_str(); glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL); glCompileShader(FragmentShaderID); //프래그먼트 쉐이더를 검사 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0) { std::vector<char> FragmentShaderErrorMessage(InfoLogLength + 1); glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); printf("%s\n", &FragmentShaderErrorMessage[0]); } //프로그램에 링크 printf("Linking program\n"); GLuint ProgramID = glCreateProgram(); glAttachShader(ProgramID, VertexShaderID); glAttachShader(ProgramID, FragmentShaderID); glLinkProgram(ProgramID); //프로그램 검사 glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0) { std::vector<char> ProgramErrorMessage(InfoLogLength + 1); glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); printf("%s\n", &ProgramErrorMessage[0]); } glDetachShader(ProgramID, VertexShaderID); glDetachShader(ProgramID, FragmentShaderID); glDeleteShader(VertexShaderID); glDeleteShader(FragmentShaderID); return ProgramID; } GLuint loadBMP_custom(const char * imagepath) { printf("Reading image %s\n", imagepath); //BMP파일의 헤더에서 데이터를 읽는다 unsigned char header[54]; unsigned int dataPos; unsigned int imageSize; unsigned int width, height; //실제 RGB 데이터 unsigned char * data; //파일을 연다 FILE * file = fopen(imagepath, "rb"); if (!file) { printf("%s는 열수 없다. 경로가 맞는지 확인해라.\n", imagepath); getchar(); return 0; } //헤더를 읽는다, i.e. the 54 first bytes //만약 54 bytes보다 적게 읽혔으면 문제 발생 if (fread(header, 1, 54, file) != 54) { printf("BMP 파일이 아니다\n"); return 0; } //A BMP 파일은 항상 "BM"으로 시작한다. if (header[0] != 'B' || header[1] != 'M') { printf("BMP 파일이 아니다\n"); return 0; } //24pp file임을 확인한다. if (*(int*)&(header[0x1e]) != 0 || *(int*)&(header[0x1C]) != 24) { printf("BMP 파일이 아니다\n"); return 0; } //이미지에 대한 정보를 읽는다. dataPos = *(int*)&(header[0x0A]); imageSize = *(int*)&(header[0x22]); width = *(int*)&(header[0x12]); height = *(int*)&(header[0x16]); //몇몇 BMP 파일들은 포맷이 놓쳐졌다, 놓쳐진 정보를 추측해라 if (imageSize == 0) imageSize = width*height * 3; // 3 : one byte for each Red-Green-Blue component if (dataPos == 0) dataPos = 54; //BMP 헤더는 항상 이 형식 //버퍼를 생성한다 data = new unsigned char[imageSize]; //파일의 버퍼에 있는 실제 데이터를 읽는다 fread(data, 1, imageSize, file); //모든 것은 현재 메모리에 있다, 파일을 닫는다 fclose(file); //openGL 텍스처를 만든다 GLuint textureID; glGenTextures(1, &textureID); //새로이 만들어진 텍스처를 바인딩한다. glBindTexture(GL_TEXTURE_2D, textureID); //이미지를 OpenGL에게 넘긴다 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data); delete[] data; // trilinear(삼선형) 필터링 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_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmap(GL_TEXTURE_2D); return textureID; } GLuint loadDDS(const char * imagepath) { unsigned char header[124]; FILE *fp; //파일을 연다 fp = fopen(imagepath, "rb"); if (fp == NULL) { printf("%s는 열 수 없다. 경로를 확인해라\n", imagepath); getchar(); return 0; } //파일의 타입을 확인한다 char filecode[4]; fread(filecode, 1, 4, fp); if (strncmp(filecode, "DDS ", 4) != 0) { fclose(fp); return 0; } //surface desc를 얻는다 fread(&header, 124, 1, fp); unsigned int height = *(unsigned int*)&(header[8]); unsigned int width = *(unsigned int*)&(header[12]); unsigned int linearSize = *(unsigned int*)&(header[16]); unsigned int mipMapCount = *(unsigned int*)&(header[24]); unsigned int fourCC = *(unsigned int*)&(header[80]); unsigned char * buffer; unsigned int bufsize; bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize; buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char)); fread(buffer, 1, bufsize, fp); fclose(fp); unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4; unsigned int format; switch (fourCC) { case FOURCC_DXT1: format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break; case FOURCC_DXT3: format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; case FOURCC_DXT5: format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; default: free(buffer); return 0; } //하나의 OpenGL 텍스처를 생성한다 GLuint textureID; glGenTextures(1, &textureID); //새로이 만들어진 텍스처를 바인딩한다 glBindTexture(GL_TEXTURE_2D, textureID); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; unsigned int offset = 0; //밉맵을 불러온다 for (unsigned int level = 0; level < mipMapCount && (width || height); ++level) { unsigned int size = ((width + 3) / 4)*((height + 3) / 4)*blockSize; glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height, 0, size, buffer + offset); offset += size; width /= 2; height /= 2; //Non-Power-Of-Two 텍스처를 사용합니다. //이 코드는 혼란을 줄이기 위해 웹 페이지에는 포함되어 있지 않습니다. if (width < 1)width = 1; if (height < 1) height = 1; } free(buffer); return textureID; } | cs |
'Game > Graphics' 카테고리의 다른 글
OpenGL-Tutorial 7 : Model loading (0) | 2018.06.22 |
---|---|
OpenGL-Tutorial 6 : Keyboard and Mouse (0) | 2018.06.21 |
OpenGL-Tutorial 4 : 색깔이 입혀진 육면체 (0) | 2018.06.20 |
OpenGL-Tutorial 3 : 행렬(매트릭스) (0) | 2018.06.20 |
OpenGL-Tutorial 2 : 첫 삼각형 (0) | 2018.06.20 |