├── 1.mlx_get_data_addr.md ├── 2.wall.md ├── 3.ceil.md ├── 4.transparency.md └── README.md /1.mlx_get_data_addr.md: -------------------------------------------------------------------------------- 1 | **mlx_get_data_addr 함수를 살펴보는 페이지.**

2 | 3 | 함수를 이해하는 데 필요했던 rgb, endian, bpp, 포인터 형변환 개념을 먼저 정리했습니다. 4 | 5 | mlx_get_data_addr에서 정리된 내용은 벽과 천장, rgb는 투명도 페이지에서 사용됩니다. 6 | 7 | [1. rgb](#rgb)
8 | [2. endian](#endian)
9 | [3. bpp](#bpp)
10 | [4. 포인터 형변환](#포인터-형변환)
11 | [5. mlx_get_data_addr](#mlx-get-data-addr) 12 | 13 | --- 14 | 15 | ## **rgb** 16 | 17 | Red, Green, Blue (빨강, 초록, 파랑) 세 종류의 기본 색을 이용하여 색을 표현한다. 18 | 19 | 각각의 요소는 0 ~ 255 의 값을 가질 수 있다. 이 값은 해당 색이 얼마나 섞였는지를 나타낸다. 20 | 21 | 완전 빨강: R = 255, G = 0, B = 0 22 | 완전 초록: R = 0, G = 255, B = 0 23 | 완전 파랑: R = 0, G = 0, B = 255 24 | 완전 검정: R = 0, G = 0, B = 0 25 | 완전 하양: R = 255, G = 255, B = 255 26 | 27 | 이런 방식으로 256 * 256 *256, 총 16,777,216 개의 색을 표현할 수 있다. 28 | 29 |
30 | 31 | #### **16진법** 32 | 33 | 색상을 표현할 때는 RGB를 16진법으로 변환해서 사용한다. 34 | 35 | (0 ~ 255)를 (00 ~ FF)로 변환해 0xRRGGBB 형식으로 나타낸다. 36 | 37 | 완전 빨강: 0xFF0000 38 | 완전 초록: 0x00FF00 39 | 완전 파랑: 0x0000FF 40 | 완전 검정: 0x000000 41 | 완전 하양: 0xFFFFFF 42 | 43 |
44 | 45 | #### **10진법** 46 | 47 | 특정 색상에서 R, G, B 값을 구해보자. 48 | 49 | 완전 빨강(0xFF0000): 255 * (256²) + 0 * (256) + 0 * (1) 50 | 51 | 여기서 R, G, B 값을 각각 구해보면
52 | 53 | B : 255 \* (256²) + 0 \* (256) + 0 \* (1) 를 256으로 나눈 나머지 = 0
54 | 55 | G : *255 \* (256²) + 0 \* (256) + 0 \* (1) 를 256으로 나눈 몫*을 다시 256으로 나눈 나머지
56 |   = *255 \* (256) + 0 \* (1)* 을 256으로 나눈 나머지 = 0
57 | 58 | R : *255 \* (256²) + 0 \* (256) + 0 \* (1)를 256²으로 나눈 몫*을 다시 256으로 나눈 나머지
59 |   = *255 \* (1)* 을 256으로 나눈 나머지 = 256 60 | 61 |
62 | 63 | #### **2진법, 비트연산** 64 | 65 | 이번엔 16진법 0xFF00000에서 R, G, B 값을 구해보자. 66 | 67 | 우리는 두 자리씩 딱 끊어서 R = FF, G = 00, B = 00 로 바로 파악할 수 있지만 컴퓨터는 못하니까.. 68 | 69 | 이 계산을 위해 비트 연산이 필요하고 비트 연산을 이해하기 위해 색을 2진법으로 표현해본다.

70 | 71 | 16진법 각 자리의 수를 2진법으로 변환한다. 72 | 73 | 0xFF0000 : 1111 1111 / 0000 0000 / 0000 0000

74 | 75 | B 값을 구할 땐 앞의 16자리는 필요 없고 마지막 8자리만 필요하기 때문에 0xFF(255)와 & 연산을 해준다 76 | 77 | (10진법에서 256으로 나눠 나머지를 구하는 과정과 유사하다)
78 | 79 | 0xFF0000 : 1111 1111 / 0000 0000 / 0000 0000 80 | & 81 | 0x0000FF : 0000 0000 / 0000 0000 / 1111 1111 82 | 83 | 결과: 0000 0000 / 0000 0000 / **0000 0000** 84 | 85 | 86 |

87 | 88 | G: (10진법) 256으로 나눈 몫에서 256으로 나눈 나머지
89 |  (2진법) 비트를 오른쪽으로 8칸 보내고 & 0xFF 90 | 91 | 0xFF0000 >> 8 = 0x00FF00 92 | 93 | 0x00FF00 : 0000 0000 / 1111 1111 / 0000 0000 94 | & 95 | 0x0000FF : 0000 0000 / 0000 0000 / 1111 1111 96 | 97 | 결과: 0000 0000 / 0000 0000 / **0000 0000** 98 | 99 |

100 | 101 | R : (10진법) 256²으로 나눈 몫에서 256으로 나눈 나머지
102 |  (2진법) 비트를 오른쪽으로 16칸 보내고 & 0xFF 103 | 104 | 0xFF0000 >> 16 = 0x0000FF 105 | 106 | 0x00FF00 : 0000 0000 / 0000 0000 / 1111 1111 107 | & 108 | 0x0000FF : 0000 0000 / 0000 0000 / 1111 1111 109 | 110 | 결과: 0000 0000 / 0000 0000 / **1111 1111** 111 | 112 |
113 | 114 | #### **비트연산 정리** 115 | 116 | RGB color에서 117 | 118 | ``` 119 | R = (color >> 16) & 0xFF 120 | G = (color >> 8 ) & 0xFF 121 | B = color & 0xFF 122 | ``` 123 | 124 | 같은 원리로 R, G, B 값이 주어졌을 때 color를 구하는 방법 125 | 126 | ``` 127 | color = R * 256² + G * 256 + B 128 | color = (R << 16) | ( G << 8) | B 129 | ``` 130 | 131 |
132 | 133 | #### **ARGB** 134 | 135 | RGB에 투명도(Alpha)값을 추가해 ARGB 형식(0xAARRGGBB)으로 나타내기도 한다. 136 | 137 | A 또한 0(완전 불투명) ~ FF(완전 투명)의 값을 갖는다. 138 | 139 | 0xFF0000은 0x00FF0000와 동일하기 때문에 완전 불투명한 빨강 140 | 0xCCFF0000: 50% 투명한 빨강 141 | 0xFFFF0000: 완전 투명한 빨강 142 | 143 | ARGB가 가질 수 있는 값은 0x00000000 ~ 0xFFFFFFFF 로 (unsigned int) 범위와 동일하다. 144 |

145 | 146 | --- 147 | 148 | ## **endian** 149 | 150 | unsigned int color = 0x12345678; 를 선언하면 메모리에 아래처럼 저장된다. 151 | 152 |

153 | endian1 154 |

155 |
156 | 우리가 눈으로 보는 것과 반대로 저장되는 이유는 리틀엔디언이라는 방식으로 값을 저장하기 때문이다.
157 | 158 | 엔디언은 숫자를 1바이트씩 쪼개서 저장할 때 저장순서를 나타낸다.
159 | 160 | 빅엔디언: 큰 자릿수가 앞에 저장됨 161 | 162 | 리틀엔디언: 작은 자릿수가 앞에 저장됨 163 | 164 |

165 | endian2
166 | 출처 코딩도장 167 |

168 | 169 |
170 | 리틀엔디언은 숫자를 읽는 방식이랑 반대라서 생소하게 보이지만, 우리가 보통 사용하는 x86(x86-64) 계열 CPU는 다 리틀 엔디언 방식이라고 한다.

171 | 172 | 리틀엔디언에서 color = 0x12345678 는 78 / 56 / 34 / 12 로 저장이 되고 173 | 174 | 순서대로 color = 0x78 + 0x5600 + 0x340000 + 0x12000000 = 0x12345678 로 계산이 된다.
175 | 176 | 우리는 0xF00FFF를 1111 0000 / 0000 1111 / 1111 1111 로 표현하지만 177 | 178 | 리틀엔디언에서는 실제로 1111 1111 / 0000 1111 / 1111 0000 으로 저장한다.

179 | 180 | 그래도 비트연산, 쉬프트연산 같은 계산은 엔디언의 영향을 받지 않기 때문에 그냥 사용하면 된다.

181 | 182 | 본 페이지에서는 183 | 184 | 0xRRGGBB 는 B / G / R 185 | 0xAARRGGBB는 B / G / R / A 순서로 저장되는 것을 알면 충분하다.

186 | cf) 엔디언이 계산에 영향을 미치는 예시 [확인하기](https://developer.ibm.com/articles/au-endianc/#list4) 187 |
188 |
189 | 190 | --- 191 | 192 | ## **bpp** 193 | 194 | **[mlx man]** 195 | 196 | > bits_per_pixel will be filled with the number of bits needed to represent a pixel color (also called the depth of the image). 197 | > 198 | > bpp = 픽셀 하나(색상 하나)를 표현하는 데 필요한 비트 수 (= depth of the image) 199 | 200 |
201 | RGB: 색상을 표현할때 BB / GG / RR 3바이트가 필요하기 때문에 bpp = 24비트
202 | ARGB : BB / RR / GG / AA 4바이트가 필요하기 때문에 bpp = 32비트 203 |

204 | 205 |

206 | bpp
207 | 24비트 비트맵의 픽셀 저장. 출처 코딩도장 208 |

209 |
210 | 211 | --- 212 | 213 | ### **포인터 형변환** 214 | 215 | 색상을 char \*에 저장해놓고 주소값으로 접근해보자. 216 | 217 | char \*data = 할당; 218 | 219 |

220 | heap1 221 |

222 | 223 | ```C 224 | unsigned int color_red = 0x00ff0000; 225 | *(unsigned int *)data = color_red; 226 | ``` 227 | 228 | color_red를 0번에 넣을 때 형변환 없이 \*data = 0x00ff0000; 로 대입하면 1바이트 자리에 4바이트 값을 넣고 있기 때문에 오류가 난다. 229 | 230 | 따라서 data가 가리키는 곳의 타입을 unsigned int로 바꾸고 값을 넣어주어야 한다. 231 |


232 | 233 | 저장 결과 234 | 235 |

236 | heap2 237 |

238 | 239 |

240 | 241 | 두 번째 색을 저장하려면 1번이 아니라 4번 주소에 저장한다. 첫 번째 색이 4바이트를 사용했기 때문에. 242 | 243 | ```C 244 | unsigned int color_2 = 0x12345678; 245 | *(unsigned int *)(data + 4) = color_2; 246 | ``` 247 | 248 |

249 | heap3 250 |

251 |

252 | 값을 가져올 때도 형 변환에 따라 결과가 달라진다. (data + 4)를 살펴보자. 253 | 254 |

255 | heap4 256 |

257 | 258 | ```C 259 | *(data + 4) : 0x78 260 | *(short *)(data + 4) : 0x5678 261 | *(int *)(data + 4) : 0x12345678 262 | *(unsigned int *)(data + 4) : 0x12345678 263 | ``` 264 | 265 |
266 | 색상과 색상 사이에 있는 중간 주소에 접근할 수도 있다. 267 | 268 | 다만 (data + 2)를 unsigned int로 접근하면 ff / 00 / 78 / 56 → 0x567800ff 가 뽑혀 저장했던 색상이 아닐 뿐. 269 |

270 | 271 | **정리:** 272 | 273 | char \* 포인터에 연속으로 색상을 저장할 때는 4칸 간격으로 저장하고, 저장한 값을 뽑아올 때도 4칸 간격으로 가져온다. 274 | 275 | (각각 unsigned int 캐스팅)

276 | 277 | --- 278 | 279 | ### **MiniLibX** 280 | 281 | **This library is a simple framework to help 42 students create simple graphical apps.** 282 |

283 | mlx에서는 색상을 ARGB, 0xAARRGGBB 형식으로 사용한다. 284 | 285 | 반투명한 색상을 넣고 띄워보면 투명도가 지원되는 것을 확인할 수 있다. 286 |
287 | 288 |

289 | mlx
290 | mlx함수로 뒷 배경이 투명한 X 그림 위에 투명도를 다르게 해서 0xAA00FF00(초록색)을 짝수 줄마다 입혀봄 291 |

292 | 293 | [투명도 참고](https://davidwalsh.name/hex-opacity) 294 | 295 |
296 | 투명도 50%일 때 코드는 다음과 같다. 297 | 298 | ```C 299 | int main(void) 300 | { 301 | void *mlx, *win, *img; 302 | unsigned int *data; 303 | int img_width, img_height; 304 | int bpp, size_l, endian; 305 | 306 | mlx = mlx_init(); 307 | win = mlx_new_window(mlx, 640, 480, "mlx_title"); 308 | img = mlx_xpm_file_to_image(mlx, "./textures/mark.xpm", &img_width, &img_height); 309 | data = (unsigned int *)mlx_get_data_addr(img, &bpp, &size_l, &endian); 310 | 311 | for(int i = 0; i < img_height; i++) 312 | for (int j = 0; j < img_width; j++) 313 | if (i % 2 == 0) 314 | data[size_l / 4 * i + j] = 0xCC00FF00; 315 | 316 | mlx_put_image_to_window(mlx, win, img, 0, 0); 317 | mlx_loop(mlx); 318 | 319 | } 320 | ``` 321 | 322 | 여기서 **[size_l / 4 * i + j]** 이 무엇인지 알아보자. 해당 식은 cub3d에서 텍스쳐에 접근할 때 계속 사용된다.

323 | 324 | --- 325 | 326 | ## **mlx get data addr** 327 | 328 |

329 | mlx 330 |

331 |
332 | 이 4pixel * 2pixel xpm 파일은 mlx에서 아래처럼 저장된다. 333 |

334 |

335 | mlx
336 |

337 |
338 | 339 | 가로로 4칸씩 저장되는 것은 앞의 포인터 형변환 파트에서 본 것과 동일하다. 340 | 341 | 이 과정은 라이브러리에서 확인할 수 있다. 342 |

343 | **[라이브러리 - mlx_xpm.c]** 344 | 345 | ```C 346 | void *mlx_int_parse_xpm(void *xvar, void *info, int info_size, char *(*f)(), int *width, int *height) 347 | { 348 | opp = 4; 349 | mlx_int_xpm_set_pixel(data, opp, col, x); 350 | 351 | } 352 | 353 | void *mlx_int_xpm_set_pixel(char *data, int opp, int col, int x) 354 | { 355 | *((unsigned int _)(data + 4 _ x)) = col; ← 4 대신 opp를 써야 했는데 하드코딩된 듯 356 | } 357 | ``` 358 | 359 |
360 | 361 | 세로를 살펴보자. 362 |
363 | 364 | 원본 이미지를 생각하면 15번 주소에 이어서 16번 주소에 흰색이 들어갈 것 같지만 365 | 366 | 16부터 255는 건너 뛰고 256부터 두 번째 라인이 저장된다. 이때 **첫 번째 줄과 두 번째 줄의 간격이 바로 size_line이다.** 367 |

368 | 369 | **[mlx man]** 370 | 371 | > size_line is the number of bytes used to store one line of the image in memory. 372 | > This information is needed to move from one line to another in the image. 373 | > 374 | > size_line : 이미지 한 줄을 저장하는 데 쓰는 바이트. 줄을 넘어갈 때 이 값이 필요하다. 375 | 376 |
377 | 378 | '줄을 넘어간다'는 부분도 라이브러리에서 찾을 수 있다. 379 | 380 | **[라이브러리 - mlx_xpm.c]** 381 | 382 | ```C 383 | void *mlx_int_parse_xpm(void *xvar,void _info,int info_size,char _(*f)(), int *width, int \*height) 384 | { 385 | opp = 4; 386 | 387 | i = *height; 388 | while (i--) 389 | { 390 | x = 0; 391 | while (x < *width) 392 | { 393 | mlx_int_xpm_set_pixel(data, opp, col, x); 394 | x++; 395 | } 396 | data += size_line; //img->width*4; 397 | } 398 | 399 | } 400 | ``` 401 | 402 | 안쪽 while문에서 width로 한 줄을 끝낸 후에 size_line 간격만큼 주소값을 건너 뛰고 있다. 403 | 404 |
405 | 406 | 그리고 이때 size_line의 값은 아래 함수에서 정해져서 오는 값이다. 407 | 408 | **[라이브러리 - mlx_img.swift]** 409 | 410 | ```Swift 411 | public class MlxImg 412 | { 413 | texture*sizeline = width * 4 414 | texture*sizeline = 256 * (texture_sizeline / 256 + (texture_sizeline % 256 >= 1 ? 1 : 0)) 415 | } 416 | ``` 417 | 418 |
419 | 420 | 0 ~ 64 : size_line = 256 * 1 = 256 421 | 65 ~ 128 : size_line = 256 * 2 = 512 422 | 129 ~ 192 : size_line = 256 * 3 = 768 423 | 193 ~ 256 : size_line = 256 * 4 = 1024 424 | 425 |
426 | 정리하자면 size_line은 이미지 한 줄이 저장된 후에 추가되는 간격, 이 값은 width 64마다 달라진다. 427 |

428 | 429 | --- 430 | 431 | 이를 바탕으로 데이터에서 [색상이 저장된 주소]를 계산하는 방법은 다음과 같다. 432 | 433 | 이미지는 첫 줄에서 다음 줄로 넘어갈 때 width를 더하겠지만, 데이터 접근은 size_line을 더해야 한다. 434 | 435 | 따라서 흰색의 주소는 436 | 437 | [width * 1 + 0] (x) 438 | [size_line * 1 + 0] (o) 439 | 440 |
441 | 그리고 가로로 접근할 때도 1이 아니라 4씩 더해야 한다. 442 | 443 | 노란색의 주소는 444 | 445 | [size_line * 0 + 2] (x) 446 | [size_line * 0 + 4 * 2] (o) 447 | 448 |
449 | 이 부분도 man에 나와있다. 450 | 451 | > From this adress, the first bits_per_pixel bits represent the color of the first pixel in the first line of the image. 452 | > The second group of bits_per_pixel bits represent the second pixel of the first line, and so on. 453 | > Add size_line to the adress to get the begining of the second line. You can reach any pixels of the image that way. 454 | > 455 | > 처음 32비트(4바이트)는 이미지 첫 줄의 첫 번째 픽셀을 나타낸다. 456 | > 그 다음 32비트는 이미지 첫 줄의 두 번째 픽셀을 나타낸다. 457 | > 처음 주소 값에 size_line을 더하면 이미지 둘째 줄로 갈 수 있다. 458 | 459 |
460 | 461 | 각 색상 데이터에 접근하는 방법은 아래처럼 정리된다. 462 | 463 | ```C 464 | int main(void) 465 | { 466 | char *data; 467 | 468 | data = mlx_get_data_addr(img, &bpp, &size_l, &endian); 469 | 470 | for(int i = 0; i < img_height; i++) 471 | for (int j = 0; j < img_width; j++) 472 | *(unsigned int *)(data + (size_l * i + 4 * j)) 473 | 474 | } 475 | ``` 476 | 477 |

478 | 479 | --- 480 | 481 | 여기까지 데이터에 접근하는 계산식은 찾았지만 482 | 483 | 바로 4라는 값을 어떻게 알지? 항상 라이브러리를 뜯어봐야 하나? 라는 의문이 생겼다. 484 |

485 | 486 | 사실 man에서는 4바이트라는 정확한 수치 대신 bits_per_pixel bits 단어를 사용한다. 487 | 488 | > From this adress, the first bits_per_pixel bits represent the color of the first pixel in the first line of the image. 489 | > The second group of bits_per_pixel bits represent the second pixel of the first line, and so on. 490 | > Add size_line to the adress to get the begining of the second line. You can reach any pixels of the image that way. 491 | > 492 | > ㅡㅡㅡㅡ아래는 bits_per_pixel bits를 곧바로 32비트로 해석한 것.ㅡㅡㅡㅡ 493 | > 494 | > 처음 32비트(4바이트)는 이미지 첫 줄의 첫 번째 픽셀을 나타낸다. 495 | > 그 다음 32비트는 이미지 첫 줄의 두 번째 픽셀을 나타낸다. 496 | > 처음 주소 값에 size_line을 더하면 이미지 둘째 줄로 갈 수 있다. 497 | 498 |
499 | bpp는 앞에서 정리했었다.
500 | 501 | 색을 RGB로 표현하면 3바이트, bpp = 24
502 | 색을 ARGB로 표현하면 4바이트, bpp = 32
503 | 504 | mlx에서는 색을 ARGB로 사용하기 때문에 bpp도 32고, 아래 문장은 전부 동일한 의미다. 505 | 506 | ARGB 형식을 사용한다 507 | = 색상을 4바이트로 표현한다 508 | = opp를 4로 둔다(4칸씩 띄워서 색상을 저장한다) 509 | = bits_per_pixel이 32다 510 | 511 |
512 | 그리고 get_data_addr함수에서 bpp에 32를 넣어서 우리한테 위 정보를 노출하고 있다. 513 | (cf) 엔디언을 0으로 주고 있는 것도 확인 가능) 514 | 515 | **[라이브러리 - interface.swift]** 516 | 517 | ```Swift 518 | @_cdecl("mlx_get_data_addr") 519 | public func mlx_get_data_addr_swift(_ imgptr:UnsafeRawPointer, _ bpp:UnsafeMutablePointer, _ sizeline:UnsafeMutablePointer, \_ endian:UnsafeMutablePointer) -> UnsafeMutablePointer 520 | { 521 | let img:MlxImg = \_mlx_bridge(ptr:imgptr) 522 | bpp.pointee = 32 523 | sizeline.pointee = Int32(img.texture_sizeline) 524 | endian.pointee = Int32(0) 525 | return img.texture_data 526 | } 527 | ``` 528 | 529 |

530 | 531 | 그래서 "뜯어보지 않고 어떻게 4라는 값을 바로 알지?" 라는 의문에 대해서는 532 | 533 | get_data_addr 함수에서 bpp값(32)을 주고 있고, **[size_line * y + (bpp / 8) * x]** 가 정확한 접근이다. 라고 답할 수 있다. 534 | 535 | ```C 536 | int main(void) 537 | { 538 | char *data; 539 | 540 | data = mlx_get_data_addr(img, &bpp, &size_l, &endian); 541 | 542 | for(int i = 0; i < img_height; i++) 543 | for (int j = 0; j < img_width; j++) 544 | *(unsigned int *)(data + (size_l * i + (bpp / 8) * j)) 545 | 546 | } 547 | ``` 548 | 549 |

550 | 551 | --- 552 | 553 | #### **(unsigned int \*) 캐스팅** 554 | 555 | 매 4번째마다 색상이 들어있기 때문에 mlx_get_data_addr 전체를 (unsigned int \*)로 형변환해도 문제없이 사용 가능하다. 556 | 557 | 전체적으로 1/4 사이즈로 줄어든다고 보면 된다. (4 = bpp / 8) 558 | 559 | (아래 그림은 이해를 돕기 위한 그림으로 실제 메모리와 다름) 560 | 561 |

562 | mlx3
563 |

564 |
565 | 566 | (unsigned int \*)로 캐스팅했을 때 데이터 접근방법 567 | 568 | ```C 569 | int main(void) 570 | { 571 | unsigned int *data; 572 | 573 | data = (unsigned int *)mlx_get_data_addr(img, &bpp, &size_l, &endian); 574 | 575 | for(int i = 0; i < img_height; i++) 576 | for (int j = 0; j < img_width; j++) 577 | data[size_l / (bpp / 8) * i + j] 578 | } 579 | 580 | ``` 581 |

582 | 583 | ---- 584 | **cub3d에서 이미지 띄우는 흐름** 585 | 586 | 587 | 1. 윈도우 사이즈의 *새 이미지* 를 만든다. 588 | 589 | 2. 각 xpm 파일을 불러와 ^각 이미지 데이터^를 저장해놓는다. (img_width, img_height, bpp, size_l도 각각 저장) 590 | 591 | 3. floor, wall, sprite 순서로 스캔하면서, **저장한 ^이미지 데이터^들에서 색상을 뽑아와** 윈도우 사이즈의 버퍼에 저장한다. 592 | 593 | 4. 스캔이 모두 끝난 버퍼를 처음에 만들었던 *이미지 데이터* 에 넣는다. 594 | 595 | 5. *이미지* 를 윈도우에 띄운다. 596 | 597 | 598 | 3번 단계에서 **데이터 접근, \[size_l / (bpp / 8) * y + x\]** 가 사용된다. 599 | -------------------------------------------------------------------------------- /2.wall.md: -------------------------------------------------------------------------------- 1 | **로데브 中 텍스쳐를 이해하는 페이지.**

2 | 3 | 이해했던 흐름 그대로의 정리라서 로데브 원본과 살짝 차이가 있습니다. 4 | 5 | **1 [벽 텍스쳐 (affine texture mapping)](https://github.com/p-eye/cub3d_texturing/blob/master/2.wall.md)**
6 | 2 [천장, 바닥 텍스쳐](https://github.com/p-eye/cub3d_texturing/blob/master/3.ceil.md)
7 | 3 [텍스쳐에 투명도 넣기](https://github.com/p-eye/cub3d_texturing/blob/master/4.transparency.md)
8 | 9 | --- 10 | 11 | ## **1. 벽 텍스쳐 (affine texture mapping)** 12 | 13 | 광선으로 벽의 높이를 구한 다음부터의 내용 14 | 15 | ```C 16 | int texNum = worldMap[mapX][mapY] - 1; 17 | 18 | double obj_x = (side == 0) ? posY + perpWallDist * rayDirY : posX + perpWallDist * rayDistX; 19 | 20 | int img_x = (int)(img_width * (obj_x - floor(obj_x))); 21 | if (wall->type == SOUTH || wall->type == WEST) 22 | img_x = img_width - img_x - 1; 23 | 24 | double obj_y = 0; 25 | if (draw_start == 0) 26 | obj_y = wall_height / 2 - sceenHeight / 2; 27 | 28 | y = draw_start; 29 | while (y < draw_end) 30 | { 31 | int img_y = (int)(img_height * obj_y / wall_height); 32 | int tex_point = size_l / (bpp / 8) * img_y + img_x; 33 | buf[y][x] = data[texNum][tex_point]; 34 | obj_y++; 35 | y++; 36 | } 37 | 38 | ``` 39 | 40 |
41 | 전체적인 흐름은 x좌표를 잡은 상태에서 수직으로 내려오면서 텍스쳐(이미지)를 가져오는 것이다. 42 |
43 | 44 | ``` 45 | obj_x: 벽에 부딪혔을 때 가로 좌표 46 | obj_y: 그 위치에서 수직으로 올린 벽 시작점 47 | 48 | (img_x, img_y): 그 때 텍스쳐에 해당하는 위치 49 | img_height, img_width: 텍스쳐 각각의 가로세로 (각 텍스쳐 크기가 다른 경우 위 코드에서 보완 필요) 50 | ``` 51 |

52 | 예시를 보면서 이해해보자. 53 | 54 | 왼쪽 벽에 오른쪽 4 \* 2 사이즈의 텍스쳐를 넣을 것이다. 55 | 56 | #### **가로 과정** 57 | 58 | 59 |

60 | wall1 61 |

62 |
63 | 64 | ``` 65 | 1. 광선이 벽에 부딪혔을 때 x 좌표를 구한다. (왼쪽 갈색별) (값은 레이캐스팅 과정에서 구해진다.) 66 | ex) obj_x = 44.23 67 | 68 | 2. 벽 가로에 대한 obj_x의 상대적 위치를 구한다. 가로가 1이기 때문에 정수부분을 빼서 소수부분으로 상대적 위치를 구할 수 있다. 69 | ex) obj_x - floor(obj_x) = 44.23 - 44 = 0.23 70 | 71 | 3. 상대적 위치를 원본 텍스쳐 width에 적용한다. 72 | ex) img_x = (int)(4 * 0.23) = (int) (0.92) = 0 73 | 74 | 4. [img_x = 0]이 실제 텍스쳐에서 가져올 가로 위치. (오른쪽 갈색별 위치를 int로 변환) 75 | ``` 76 | 77 |

78 | #### **세로 과정** 79 | 80 |

81 | wall2 82 |

83 |
84 | 85 | ``` 86 | 1. wall_height = 64라고 가정하자 (실제 구현에서는 texture 단계 이전에 wall_height를 구하는 과정이 있다.) 87 | 88 | 2. obj_x에서 수직으로 올라와 벽 시작점 obj_y를 잡는다. (왼쪽 파란별) 89 | (일단 스크린에 벽 전체가 보일 경우로 가정했다. 이때 obj_y는 0이다) 90 | ex) obj_y = 0 91 | 92 | 3. 벽 세로에 대한 obj_y의 상대적 위치를 구한다. 내 위치를 벽 높이로 나눠주면 상대적 위치를 구할 수 있다. 93 | ex) obj_y / 64 = 0 94 | 95 | 4. 상대적 위치를 원본 텍스쳐 height에 적용한다. 96 | ex) img_y = (int)(2 * 0) = 0 97 | 98 | 5. [img_y = 0]이 실제 텍스쳐에서 가져올 세로 위치. (오른쪽 흰색별 위치를 int로 변환) 99 | ``` 100 | 101 | 종합하면 \[img\_x = 0, img\_y = 0\]으로 파란색 픽셀을 가져오게 된다. 102 | 103 |
104 | 105 | while문을 계속 돌아서 obj\_y = 40이 되었다면 106 | 107 | ``` 108 | 1. obj_y = 40 109 | 110 | 2. obj_y의 상대적 위치는 40 / 64 = 5 / 8 111 | 112 | 3. img_y = (int) (2 * 5 / 8) = 1 113 | ``` 114 | 115 | \[img\_x = 0, img\_y = 1\]으로 흰색 픽셀을 가져온다. 116 | 117 |
118 | 119 | --- 120 | 121 | 스킵했던 부분 설명 122 | 123 | ```C 124 | 1. 125 | img_x = (int)(img_width * (obj_x - floor(obj_x)); 126 | if (wall->type == SOUTH || wall->type == WEST) 127 | img_x = img_width - img_x - 1; 128 | 129 | 130 | 2. 131 | obj_y = 0; 132 | if (draw_start == 0) 133 | obj_y = wall_height / 2 - sceenHeight / 2; 134 | ``` 135 | 136 | 1. 137 | 138 | 스크린을 띄워보면 벽면이 가로로 뒤집혀서 그려지는 방향이 있다. (대칭이 아닌 텍스쳐를 넣어보면 바로 눈에 보인다.)
139 | 140 | **\[img\_x = img\_width - img\_x - 1\]** 이 찍은 위치를 다시 뒤집어서 정상적으로 돌려주는 부분이다.
141 | 142 | (side == 0 && rayDirX > 0) || (side == 1 && rayDirY < 0) 조건문을 그대로 써도 되고, cub3d 과제 내용 중 방향을 정하는 부분이 있기 때문에 위 코드처럼 단순화할 수도 있다.
143 | 144 |
145 | 146 | 2. 147 | 148 | **\[obj\_y= 0]** 은 벽 높이가 스크린 높이보다 작아서 화면에 벽 전체가 그려지는 경우를 가정한 것이었다. (멀리서 벽을 바라볼 때)
149 | 150 | 하지만 아래 그림처럼 벽 높이가 노란색 스크린보다 커지는 경우가 있다. (벽 바로 앞에 섰을 때)
151 | 152 |

153 | wall3 154 |

155 |
156 | texture 이전 단계에서 이런 경우를 draw_start = 0, draw_end =sceenHeight로 보정해주었다.
157 | 158 | 마찬가지로 보정이 필요한데, 이때는 벽 제일 윗부분을 시작점(obj\_y = 0)으로 잡으면 안되고, 화면에 그려주는 부분부터 시작해야 한다.
159 | 160 | 그 시작점이 벽 절반 높이에서 화면 절반 높이를 뺀 위치다.
161 | 162 | 그래서 draw\_start == 0일 땐 **\[obj\_y = wall\_height / 2 - sceenHeight / 2]** 163 | 164 |
165 | 166 | --- 167 | 168 | 마지막으로, 구한 텍스쳐 위치(img\_x, img\_y)로 데이터에서 색상을 가져와서 버퍼에 넣어준다.
169 | 170 | 텍스쳐 위치로 색상을 가져오는 계산은 [mlx_get_data_addr 페이지](https://github.com/p-eye/cub3d_texturing/blob/master/1.mlx_get_data_addr.md)에 설명되어 있다. 171 |
172 | ```C 173 | tex_point = size_l / (bpp / 8) * img_y + img_x; 174 | buf[y][x] = data[tex_point]; 175 | ``` 176 |
177 | x 하나에 대해서 수직으로 전부 내려오면 아래처럼 저장되고
178 | 179 | 이 작업을 모든 x에 대해(스크린 widht만큼) 실행하면 버퍼에 화면 전체 벽이 저장된다. 180 |

181 | wall3 182 |

183 |
184 | -------------------------------------------------------------------------------- /3.ceil.md: -------------------------------------------------------------------------------- 1 | **로데브 中 텍스쳐를 이해하는 페이지.**

2 | 3 | 이해했던 흐름 그대로의 정리라서 로데브 원본과 살짝 차이가 있습니다. 4 | 5 | 1 [벽 텍스쳐](https://github.com/p-eye/cub3d_texturing/blob/master/2.wall.md)
6 | **2 [천장, 바닥 텍스쳐 (scanline by scanline)](https://github.com/p-eye/cub3d_texturing/blob/master/3.ceil.md)**
7 | 3 [텍스쳐에 투명도 넣기](https://github.com/p-eye/cub3d_texturing/blob/master/4.transparency.md)
8 | 9 | --- 10 | 11 | ## **2. 천장, 바닥 텍스쳐 (scanline by scanline)** 12 | 13 | 14 | 벽은 x좌표를 잡고 위→아래, 수직으로 텍스쳐를 저장하는 방식이었다면 15 | 16 | 천장과 바닥은 y좌표를 잡고 왼→오, 수평으로 텍스쳐를 저장하는 방식이다. 17 | 18 |
19 | 20 | **1. 천장까지 수평거리 구하기** 21 | 22 | **2. 스크린 왼쪽 끝에 보이는 천장 좌표 구하기** 23 | 24 | **3. 왼쪽 끝에서 오른쪽 끝으로 갈 때 step 구하기** 25 | 26 | **4. 좌표에 해당하는 텍스쳐 저장하기** 27 | 28 | **5. 반복문 돌기** 29 | 30 | 31 | 32 | 순으로 진행된다. 33 | 34 |
35 | 36 | 37 | ---- 38 | 39 | ### 1. 천장까지 수평거리 구하기 40 | 41 |

42 | ceil1
43 |

44 | 45 | ```C 46 | double posZ = 0.5 * screenHeight; 47 | int p = abs(y - screenHeight / 2); 48 | ``` 49 | 50 | 광선시작점 posZ가 주어지고 y는 0부터 screenHeight / 2까지 움직인다. 51 | 52 | posZ가 y를 지나는 광선을 쏘았을 때 y와 스크린 중앙의 차이가 p, 광선이 천장에 닿았을 때 수평거리가 dist다. 53 |

54 | 55 | ``` 56 | 1 : p = dist : posZ 57 | 따라서 58 | dist = posZ / p 59 | ``` 60 |
61 | 62 | 다만 cub3d에서는 abs가 allowed function이 아니기 때문에.. 아래처럼 선언해준다. 63 | ```C 64 | double posZ = 0.5 * screenHeight; 65 | int p = y - screenHeight / 2; 66 | double dist = fabs(posZ / p); 67 | ``` 68 |
69 | 70 | 직접 대입해보면 다음과 같다 71 | |screenHeihgt|480|480|480|480|480| 72 | |------|---|---|---|---|---| 73 | |y|0|60|120|180|239| 74 | |posZ|240|240|240|240|240| 75 | |p|-240|-180|-120|-6|-1| 76 | |dist|1|1.5|2|4|240| 77 | 78 |
79 | y = 0일 때 dist가 가장 짧고, y가 중앙(h /2)으로 갈수록 dist가 급격하게 커지는 관계를 확인하자. 80 |
81 | (y가 중앙으로 갈수록 삼각형이 눕고, dist는 더 커진다.) 82 | 83 |

84 | 85 | 86 |

87 | ceil2
88 |

89 | 90 | 정면에서 볼 땐
91 | 92 | dist = 1 : 내 1칸 앞에 벽이 있음 → 천장이 1칸 보임
93 | 94 | dist = 2 : 내 2칸 앞에 벽이 있음 → 천장이 2칸 보임
95 | 96 | dist = 8 : 내 8칸 앞에 벽이 있음 → 천장이 8칸 보임
97 | 98 | dist = 240 : 내 240칸 앞에 벽이 있음 → 천장이 240칸 보임
99 | 100 |
101 | 로 생각하자 102 |

103 | 104 | ---- 105 | 106 | ### 2. 스크린 왼쪽 끝에 보이는 천장 좌표 구하기 107 | 108 | 109 | ```C 110 | double rayDirX0 = dirX - planeX; 111 | double rayDirY0 = dirY - planeY; 112 | double rayDirX1 = dirX + planeX; 113 | double rayDirY1 = dirY + planeY; 114 | 115 | rayDir0 : 내 위치에서 스크린 왼쪽으로 끝으로 뻗어지는 광선 116 | rayDir1 : 내 위치에서 스크린 오른쪽 끝으로 뻗어지는 광선 117 | ``` 118 |
119 | 120 | ```C 121 | double obj_x = posX + rayDirX0 * dist; 122 | double obj_y = posY + rayDirY0 * dist; 123 | 124 | (obj_x, obj_y) : (스크린 왼쪽 끝으로 광선을 뻗어서 * 천장에 닿을 때) 좌표 125 | ``` 126 |
127 | 128 | 내 위치에서 스크린 왼쪽 끝으로 광선을 뻗었을 때 천장에 닿는 좌표를 (obj_x, obj_y)라고 하자. 129 | 130 |

131 | ceil3
132 |

133 | 134 |
135 | 내 앞에 있던 벽이 멀어질수록 좌우로 시야가 넓어진다
136 |
137 | = dist가 커질수록 좌우로 시야가 넓어진다

138 | 139 | = y가 중앙으로 갈수록 좌우로 시야가 넓어진다
140 | 141 | = y가 중앙으로 갈수록 내 위치에서 obj_x, obj_y 좌표가 멀어진다
142 | 143 | 144 |

145 | 146 | 이해를 위해 스크린 오른쪽 끝 좌표도 한 번 더 확인해보자. 147 | 148 | ``` 149 | double obj_x_end = posX + rayDirX1 * dist; 150 | double obj_y_end = posY + rayDirY1 * dist; 151 | 152 | (obj_x_end, obj_y_end) : (스크린 오른쪽 끝으로 광선을 뻗어서 * 천장에 닿을 때) 좌표 153 | ``` 154 |
155 |

156 | ceil4
157 |

158 | 159 | 160 | 내 앞에 있던 벽이 멀어질수록 좌우로 시야가 넓어진다.
161 | 162 | dist = 1 : 눈에 보이는 좌표가 10 ~12
163 | 164 | dist = 8 : 눈에 보이는 좌표가 6 ~ 16
165 | 166 | dist = 240 : 눈에 보이는 좌표가 -146 ~ 16 167 | 168 |
169 | 170 | ---- 171 | ### 3. 왼쪽 끝에서 오른쪽 끝으로 갈 때 step 구하기 172 | 173 |
174 | 이제 y위치를 잡아놓고 (obj_x, obj_y)에서 오른쪽으로 가면서 텍스쳐를 저장한다.
175 | 176 | 한 번에 얼만큼씩 움직여야 할까? 벽에서는 obj_y가 항상 동일하게 1씩 증가했지만, 천장은 조금 다르다.
177 | 178 | 179 | dist = 1 : 처음부터 끝까지 갔을 때 x 좌표 차이가 1.32
180 | 181 | dist = 8: 처음부터 끝까지 갔을 때 x 좌표 차이가 10.54
182 | 183 | dist = 240 : 처음부터 끝까지 갔을 때 x 좌표 차이가 326.3 184 | 185 |
186 | 187 | 이렇게 dist가 커질수록 좌표 차이가 크다는 것은 (거리가 멀어질수록 좌우로 시야가 넓어진다는 것은)
188 | 189 | dist = 1일 땐 obj에 더해주는 값이 작고 (가까운 곳을 볼 땐 시선을 움직일 때 시야의 변화가 작고)
190 | 191 | dist가 커질수록 더해주는 값이 커진다는 것을 의미한다. (멀리 볼수록 시선을 움직일 때 시야의 변화가 크다) 192 | 193 |
194 | 195 | 이때 더해주는 값이 step이다. step은 dist에 따라 달라지기 때문에 1로 둘 수 없고 값을 구해야 한다. 196 | 197 |
198 | 스크린 왼쪽 끝에서 오른쪽 끝으로 가는 과정은 왼쪽 끝 좌표인 (obj_x, obj_y)에서 199 | 200 | ```C 201 | double obj_x = posX + rayDirX0 * dist; 202 | double obj_y = posY + rayDirY0 * dist; 203 | ``` 204 |
205 | 206 | (스크린 width)번 움직여서 오른쪽 끝 좌표인 (obj_x_end, obj_y_end)에 도착하는 과정이다. 207 | 208 | ```C 209 | double obj_x_end = posX + rayDirX1 * dist; 210 | double obj_y_end = posY + rayDirY1 * dist; 211 | ``` 212 | 213 | 따라서 214 | ```C 215 | obj_x + (step_x * screenWidth) = obj_x_end 216 | obj_y + (step_y * screenWidth) = obj_y_end 217 | ``` 218 | 를 만족하는 step_x, step_y를 구하면 된다. 219 | 220 |

221 | 222 | **step 유도과정** 223 | ```C 224 | obj_x + (step_x * screenWidth) = obj_x_end 225 | step_x * screenWidth = obj_x_end - obj_x 226 | step_x * screenWidth = (posX + rayDirX1 * dist) - (posX + rayDirX0 * dist) 227 | step_x * screenWidth = (rayDir1X - rayDir0x) * dist 228 | 229 | step_x = (rayDir1X - rayDir0X) * dist / screenWidth 230 | 231 | 동일한 방법으로 232 | 233 | step_y = (rayDir1Y - rayDir0Y) * dist / screenWidth 234 | 235 | dist에 따라 step이 바뀌는 관계도 식으로 확인할 수 있다. 236 | ``` 237 |
238 | 239 | ---- 240 | 241 | ### 4. 좌표에 해당하는 텍스쳐 가져오기 242 | 243 | 텍스쳐를 가져오는 방법은 벽과 동일하다. 244 |
245 | 1. 실제 천장 좌표(obj_x, obj_y)에서 소수부분만 구해서 = (1*1) 안에서 상대적 위치를 구해서
246 | 247 | 실제 텍스쳐에 상대적 위치를 적용해준다. 248 | ```C 249 | img_x = (int)(img_width * (obj_x - floor(obj_x))); 250 | img_y = (int)(img_height * (obj_y - floor(obj_y))); 251 | ``` 252 |
253 | 254 | 2. 텍스쳐 위치(img_x, img_y)에 해당하는 색상을 가져온다. 255 | ```C 256 | tex_point = size_l / (bpp / 8) * img_y + img_x; 257 | buf[y][x] = data[tex_point]; 258 | ``` 259 |
260 | 261 | 상대적 위치를 구할 때 정수부분을 (int)obj_x 로 하지 않고 floor 함수로 구하는 것은 음수를 보정하기 위해서다.
262 | 263 | 천장좌표가 음수일 경우 상대적 위치가 정상범위(0 ~ 1)를 넘는 오류가 생긴다. (벽을 다 지우고 천장, 바닥만 그리면 오류를 확인할 수 있다.) 264 | ```C 265 | ex) obj_x = -1.4 266 | 267 | obj_x - (int)obj_x = -1.4 - (-1) = -0.4 268 | ``` 269 |
270 | 따라서 좌표가 음수일 경우에도 상대적 위치는 양수가 되도록 floor함수를 사용했다. 271 | 272 | ```C 273 | ex) obj_x = -1.4 274 | 275 | obj_x - floor(obj_x) = -1.4 - (-2) = 0.6 276 | ``` 277 |
278 | 279 | -0.4과 0.6의 상대적 위치가 동일하기 때문에 floor로 보정이 가능하다. 280 | 281 |

282 | ceil5
283 |

284 | 285 | 286 |

287 | 288 | ---- 289 | 290 | ### 5. 반복문 돌기 291 | 292 | 293 | 294 | 반복문은 두가지 케이스가 있다. 295 | 296 | ```C 297 | 로데브 ver. 298 | 299 | for (int y = screenHeight / 2 + 1; y < screenHeight; y++) 300 | { 301 | for (int x = 0; x < screenWidth; ++x) 302 | { 303 | buf[y][x] = floor_color; 304 | buf[screenHeight - y - 1][x] = ceil_color; 305 | } 306 | } 307 | ``` 308 | 309 | ```C 310 | for (int y = 0; y < sceenHeight / 2; y++) 311 | { 312 | for (int x = 0; x < screenWidth; ++x) 313 | { 314 | buf[y][x] = ceil_color; 315 | buf[screenHeight - y - 1][x] = floor_color; 316 | } 317 | } 318 | ``` 319 | 320 | 321 |
322 | 1. 세로 반복문을 screenHeight만큼 전부 돌리지 않고 범위를 반으로 줄일 수 있다. 왜냐하면 screenHeignt / 2 기준으로 천장과 바닥이 완전 대칭이기 때문이다.

323 | 324 | screenHeight = 480일 때 y = 0과 y = 439 계산이 동일하고, y = 120과 y = 359 계산이 동일하다. 325 | 326 | 따라서 while문 안에서 buf[y][x]와 buf[screenHeignt - 1 - y][x]로 위아래를 한번에 처리해줄 수 있기 때문에 반복문을 절반만 돌린다.
327 | 328 |
329 | 2. 절반을 돌릴 때 기준을 floor과 ceil 중 선택할 수 있다.

330 | 331 | 로데브는 반복문 범위를 (h / 2 + 1) ~ (h - 1)으로 y를 floor에서 돌리고 있고
332 | 333 | 범위를 (0) ~ (h / 2 - 1)로 바꿔 y를 ceil에서 돌릴 수도 있다.

334 | 335 | 전자의 경우 h / 2 근처에서 1픽셀씩 범위가 비기 때문에 나는 (0) ~ (h / 2 - 1) 범위를 선택했다. (사실 눈에 보이지 않는 차이다.) 336 | 337 | 처음에 dist를 양수로 보정해주었기 때문에 이렇게 범위를 바꿔도 정상적으로 구현된다. 338 | 339 | ```C 340 | double dist = fabs(posZ / p); 341 | ``` 342 | 343 | 344 |

345 | **전체적인 흐름** 346 | 347 | ```C 348 | y = 0; 349 | while (y < screenHeight / 2) 350 | { 351 | ... 352 | double dist = fabs(posZ / p); 353 | 354 | double obj_x = posX + rayDirX0 * dist; 355 | double obj_y = posY + rayDirY0 * dist; 356 | 357 | int step_x = (rayDir1X - rayDir0X) * dist / win_w; 358 | int step_y = (rayDir1Y - rayDir0Y) * dist / win_w; 359 | 360 | x = 0; 361 | while (x < screenWidth) 362 | { 363 | int img_x = (int)(img_width * (obj_x - floor(obj_x))); 364 | int img_y = (int)(img_height * (obj_y - floor(obj_y))); 365 | int tex_point = size_l / (bpp / 8) * img_y + img_x; 366 | buf[y][x] = data[tex_point]; // ceil 367 | buf[screenHeight - 1 - y][x] = data[tex_point]; // floor 368 | obj_x += step_x; 369 | obj_y += step_y; 370 | x++; 371 | } 372 | y++; 373 | } 374 | ``` 375 | 376 | ceil 텍스쳐, floor 텍스쳐가 각자의 img_width, img_height, size_l, data를 사용할 수 있도록 코드 보완이 필요하다. 377 | -------------------------------------------------------------------------------- /4.transparency.md: -------------------------------------------------------------------------------- 1 | **로데브 中 텍스쳐를 이해하는 페이지.**

2 | 3 | 이해했던 흐름 그대로의 정리라서 로데브 원본과 살짝 차이가 있습니다. 4 | 5 | 1 [벽 텍스쳐](https://github.com/p-eye/cub3d_texturing/blob/master/2.wall.md)
6 | 2 [천장, 바닥 텍스쳐](https://github.com/p-eye/cub3d_texturing/blob/master/3.ceil.md)
7 | **3 [텍스쳐에 투명도 넣기](https://github.com/p-eye/cub3d_texturing/blob/master/4.transparency.md)**
8 | 9 | --- 10 | 11 | ## **3. 텍스쳐에 투명도 넣기** 12 | 13 | 14 |

15 | mlx
16 |

17 | 18 | 앞에서 mlx에서는 0xAARRGGBB 색상 형식을 사용하고, 실제로 투명도를 지원하는 것도 확인해보았다. (위 움짤)
19 | 20 | alpha값이 있는 색상을 바로 mlx 함수로 띄웠던 경우다. 21 | 22 |
23 | 24 | 하지만 cub3d에서 buf[y][x]에 색을 저장하는 방식에서는 투명도를 다르게 구현해야 한다.
25 | 26 | '완전 투명'한 경우를 제외하고는, 아무리 반투명한 색을 넣어도 스크린에 띄워보면 '완전 불투명'한 이미지로 출력된다. 27 | 28 |
29 | 30 | 왜냐하면 buf[y][x] = color 과정에서 기존 색상을 남겨두지 않고 그냥 새로운 색상을 덮어 씌워버리기 때문이다.
31 | 32 | 지금은 파란색 위에 반투명한 노란색을 놓는다고 색이 섞이는 것이 아니라 그냥 노란색만 남겨진다.
33 | 34 | 따라서 투명도를 적용하기 위해서는 두 색을 섞어 새로운 색상을 만들어서 버퍼에 넣어야 한다.
35 | 36 |
37 | 38 | 이렇게 39 |

40 | trans
41 |

42 | 43 | [투명도 참고](https://davidwalsh.name/hex-opacity) 44 | 45 | 46 |
47 | 48 | 두 색을 섞을 땐 각 색상에 투명도 가중치를 주고 더해준다. 49 | ```C 50 | result = weight * new + (1 - weight) * old 51 | 52 | 53 | 여기서 가중치 weight는 54 | weight = (double) (255 - new_alpha) / 255; 55 | ``` 56 |
57 | 58 | ```C 59 | ex) 완전 불투명한 색을 추가하면 60 | 61 | new_alpha = 0 62 | weihgt = 1 63 | result = new 64 | 65 | 이기 때문에 새로운 색만 보이게 된다. 66 | ``` 67 |
68 | 69 | 70 | **새로 가져오는 색에 투명도가 적용된 경우** 71 | 72 | 1. new와 old를 각각 r, g, b로 분리한다.
73 | 74 | 2. new에서 alpha값을 뽑는다.
75 | 76 | 3. 가중치를 반영해서 r, g, b 각각 새로운 색을 만든다.
77 | 78 | 4. result color로 합쳐준다.
79 | 80 |
81 | 82 | **새로 가져오는 색은 불투명하지만, 투명도를 추가하고 싶은 경우** 83 | 84 | 1. new와 old를 각각 r, g, b로 분리한다.
85 | 86 | 2. new에 적용할 alpha값을 정한다.
87 | 88 | 3. 가중치를 반영해서 r, g, b 각각 새로운 색을 만든다.
89 | 90 | 4. result color로 합쳐준다.
91 | 92 | 93 |
94 | 과제에서는

95 | old: 기존 buf[y][x]에 저장된 색
96 | new: 새로 가져오는 data[tex_point] 색

97 | 이 될 것이다. 98 | 99 |

100 | 각각의 색상 요소를 분리하고 합치는 법은 [mlx_get_data_addr 페이지](https://github.com/p-eye/cub3d_texturing/blob/master/1.mlx_get_data_addr.md)에 설명되어 있다. 101 | 102 |
103 | 104 | 1) 벽 색이 0xCC00FF00 인 경우 105 |

106 | trans2
107 |

108 | 109 | 110 |
111 | 112 | 2) 완전 불투명한 벽에 0x80만큼의 투명도를 준 경우 113 |

114 | trans3
115 |

116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cub3d_texturing 2 | Description of cub3D texturing 3 | 4 | ---- 5 | 6 | cub3d 과제를 진행하면서

7 | * [taelee님의 mlx 예제](https://github.com/taelee42/mlx_example)
8 | * [로데브 레이캐스팅 튜토리얼](https://lodev.org/cgtutor/raycasting.html)
9 | * [mihykim님의 로데브 번역본](https://github.com/365kim/raycasting_tutorial)
10 | * [yohlee님의 로데브 C 버전](https://github.com/l-yohai/cub3d)

11 | 12 | 자료들로부터 많은 도움을 받았습니다. 13 |

14 | 이 글도 위 자료들을 바탕으로 작성했기 때문에 참고하시면 좋을 것 같습니다. 15 | 16 |

17 | 이 글엔 제가 cub3d 진행 중 '텍스쳐 처리'를 학습한 과정이 정리되어 있습니다.
18 | 19 | 따라서 로데브 튜토리얼 중 아래 부분만 담겨 있습니다.
20 | 21 | * 벽에 텍스쳐 넣기 - 로데브 said, "affine texture mapping" (subejct mandatory) 22 | * 천장, 바닥에 텍스쳐 넣기 - 로데브 said, "scanline by scanline" (subject bonus) 23 | * 텍스쳐에 투명도 넣기 (extra) 24 | 25 | 그리고 이해를 돕기 위해 첫 페이지에 mlx_get_data_addr 함수를 정리했습니다.
26 | 27 | cub3d 텍스쳐 부분을 공부하는 분들에게 도움이 되었으면 좋겠습니다.
28 | --------------------------------------------------------------------------------