1 Getting started 5 Shaders - JJhuk/LearnOpenGL GitHub Wiki

LearnOpenGL - Shaders

url: https://learnopengl.com/Getting-started/Shaders 생성일: 2020λ…„ 12μ›” 25일 μ˜€ν›„ 2:25

μ•ž Hello Triangle μ±•ν„°μ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄, μ…°μ΄λ”λŠ” GPU에 μžˆλŠ” μž‘μ€ ν”„λ‘œκ·Έλž¨μž…λ‹ˆλ‹€. μ΄λŸ¬ν•œ ν”„λ‘œκ·Έλž¨μ€ κ·Έλž˜ν”½ νŒŒμ΄ν”„ 라인의 각 νŠΉμ • μ„Ήμ…˜μ— λŒ€ν•΄ μ‹€ν–‰λ©λ‹ˆλ‹€. 기본적으둜 μ…°μ΄λ”λŠ” μž…λ ₯을 좜λ ₯으둜 λ³€ν™˜ν•˜λŠ” ν”„λ‘œκ·Έλž¨μž…λ‹ˆλ‹€. μ…°μ΄λ”λŠ” λ˜ν•œ μ„œλ‘œ 톡신할 수 μ—†λ‹€λŠ” μ μ—μ„œ 맀우 고립 된 ν”„λ‘œκ·Έλž¨μž…λ‹ˆλ‹€. 그듀이 가지고 μžˆλŠ” μœ μΌν•œ μ˜μ‚¬ μ†Œν†΅μ€ μž…λ ₯κ³Ό 좜λ ₯을 ν†΅ν•΄μ„œμž…λ‹ˆλ‹€.

이전 μž₯μ—μ„œ μš°λ¦¬λŠ” μ…°μ΄λ”μ˜ ν‘œλ©΄κ³Ό 그것듀을 μ μ ˆν•˜κ²Œ μ‚¬μš©ν•˜λŠ” 방법에 λŒ€ν•΄ κ°„λ‹¨νžˆ λ‹€λ£¨μ—ˆμŠ΅λ‹ˆλ‹€. 이제 셰이더, 특히 OpenGL 셰이딩 언어에 λŒ€ν•΄ μ’€ 더 일반적인 λ°©μ‹μœΌλ‘œ μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.

GLSL

μ…°μ΄λ”λŠ” C와 μœ μ‚¬ν•œ μ–Έμ–΄ GLSL둜 μž‘μ„±λ©λ‹ˆλ‹€. GLSL은 κ·Έλž˜ν”½κ³Ό ν•¨κ»˜ μ‚¬μš©ν•˜λ„λ‘ λ§žμΆ€ν™”λ˜μ—ˆμœΌλ©° νŠΉλ³„νžˆ 벑터 및 ν–‰λ ¬ 연산을 λŒ€μƒμœΌλ‘œν•˜λŠ” μœ μš©ν•œ κΈ°λŠ₯을 ν¬ν•¨ν•©λ‹ˆλ‹€.

μ…°μ΄λ”λŠ” 항상 버전 μ„ μ–ΈμœΌλ‘œ μ‹œμž‘ν•˜κ³  μž…λ ₯ 및 좜λ ₯ λ³€μˆ˜, μœ λ‹ˆνΌ 및 메인 ν•¨μˆ˜λ‘œ μ΄μ–΄μ§‘λ‹ˆλ‹€. 각 μ…°μ΄λ”μ˜ μ§„μž…μ μ€ μž…λ ₯ λ³€μˆ˜λ₯Ό μ²˜λ¦¬ν•˜κ³  κ²°κ³Όλ₯Ό 좜λ ₯ λ³€μˆ˜μ— 좜λ ₯ν•˜λŠ” 메인 ν•¨μˆ˜μ— μžˆμŠ΅λ‹ˆλ‹€. μœ λ‹ˆνΌμ΄ 뭔지 λͺ¨λ₯΄λ”라도 κ±±μ •ν•˜μ§€ λ§ˆμ„Έμš”. 곧 μ•Œλ €λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.

μ…°μ΄λ”λŠ” 일반적으둜 λ‹€μŒ ꡬ쑰λ₯Ό κ°–μŠ΅λ‹ˆλ‹€.

#version version_number
in type in_variable_name;
in type in_variable_name;

out type out_variable_name;
  
uniform type uniform_name;
  
void main()
{
  // μž…λ ₯(λ“€)을 μ²˜λ¦¬ν•˜κ³  μ΄μƒν•œ κ·Έλž˜ν”½ μž‘μ—…μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  ...
  // 처리된 것듀을 좜λ ₯ λ³€μˆ˜λ‘œ 좜λ ₯ν•©λ‹ˆλ‹€.
  out_variable_name = weird_stuff_we_processed;
}

특히 λ²„ν…μŠ€ 셰이더에 λŒ€ν•΄ 이야기 ν•  λ•Œ 각 μž…λ ₯ λ³€μˆ˜λŠ” λ²„ν…μŠ€ 속성이라고도 ν•©λ‹ˆλ‹€. ν•˜λ“œμ›¨μ–΄μ— μ˜ν•΄ μ œν•œμ μœΌλ‘œ μ„ μ–Έ ν•  수 μžˆλŠ” λ²„ν…μŠ€ μ†μ„±μ˜ μ΅œλŒ€ κ°œμˆ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€. OpenGL은 항상 μ΅œμ†Œ 16개 μ΄μƒμ˜ 4개 ꡬ성 μš”μ†Œ(vec4 * 16개) λ²„ν…μŠ€ 속성을 μ‚¬μš©ν•  수 μžˆμŒμ„ 보μž₯ν•˜μ§€λ§Œ 일뢀 ν•˜λ“œμ›¨μ–΄μ—μ„œλŠ” GL_MAX_VERTEX_ATTRIBS λ₯Ό μΏΌλ¦¬ν•˜μ—¬ 검색 ν•  수 μžˆλŠ” 더 λ§Žμ€ 속성을 ν—ˆμš©ν•˜κΈ°λ„ ν•©λ‹ˆλ‹€.

int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

이것은 λŒ€λΆ€λΆ„μ˜ λͺ©μ μ— μΆ©λΆ„ν•œ 수인 μ΅œμ†Œ 16 을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

Types

GLSL has, like any other programming language, data types for specifying what kind of variable we want to work with. GLSL has most of the default basic types we know from languages like C: int, float, double, uint and bool. GLSL also features two container types that we'll be using a lot, namely vectors and matrices. We'll discuss matrices in a later chapter.

GLSL은 λ‹€λ₯Έ ν”„λ‘œκ·Έλž˜λ° 언어와 λ§ˆμ°¬κ°€μ§€λ‘œ μ–΄λ–€ λ³€μˆ˜λ₯Ό μ‚¬μš©ν• μ§€ μ§€μ •ν•˜κΈ° μœ„ν•œ 데이터 μœ ν˜•μ„ 가지고 μžˆμŠ΅λ‹ˆλ‹€. GLSL은 μš°λ¦¬κ°€ C와 같은 μ–Έμ–΄μ—μ„œ μ•Œκ³  μžˆλŠ” λŒ€λΆ€λΆ„μ˜ 기본적인 νƒ€μž…λ“€μ„ 가지고 μžˆμŠ΅λ‹ˆλ‹€ : int, float, double , uint, bool. GLSL은 λ˜ν•œ μš°λ¦¬κ°€ 많이 μ‚¬μš©ν•  두 가지 μ»¨ν…Œμ΄λ„ˆ μœ ν˜•μΈ 벑터와 행렬을 μ œκ³΅ν•©λ‹ˆλ‹€. 이후 μž₯μ—μ„œ 행렬에 λŒ€ν•΄ λ…Όμ˜ν•  κ²ƒμž…λ‹ˆλ‹€.

Vectors

GLSL의 λ²‘ν„°λŠ” 방금 μ–ΈκΈ‰ ν•œ κΈ°λ³Έ νƒ€μž…μ— λŒ€ν•œ 1,2,3 λ˜λŠ” 4개의 ꡬ성 μš”μ†Œ μ»¨ν…Œμ΄λ„ˆμž…λ‹ˆλ‹€. λ‹€μŒκ³Ό 같은 ν˜•μ‹μ„ μ·¨ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (n 은 ꡬ성 μš”μ†Œμ˜ 수λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.)

  • vecn : n 개의 floatλ₯Ό κ°€μ§€λŠ” κΈ°λ³Έ 벑터
  • bvecn : n 개의 boolean을 κ°€μ§€λŠ” 벑터
  • ivecn : n 개의 μ •μˆ˜λ₯Ό κ°€μ§€λŠ” 벑터
  • uvecn : n 개의 λΆ€ν˜Έμ—†λŠ” μ •μˆ˜λ₯Ό κ°€μ§€λŠ” 벑터
  • dvecn : n 개의 double을 κ°€μ§€λŠ” 벑터

λŒ€λΆ€λΆ„μ˜ λͺ©μ μ—λŠ” floatκ°€ μΆ©λΆ„ν•˜λ―€λ‘œ λŒ€λΆ€λΆ„μ˜ 경우 κΈ°λ³Έ vecn 을 μ‚¬μš©ν•©λ‹ˆλ‹€.

λ²‘ν„°μ˜ ꡬ성 μš”μ†ŒλŠ” vec.x λ₯Ό 톡해 μ•‘μ„ΈμŠ€ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ x λŠ” λ²‘ν„°μ˜ 첫 번째 ꡬ성 μš”μ†Œμž…λ‹ˆλ‹€. x, y, z 및 w λ₯Ό μ‚¬μš©ν•˜μ—¬ 각각 첫 번째, 두 번째, μ„Έ 번째 및 λ„€ 번째 ꡬ성 μš”μ†Œμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€. GLSL을 μ‚¬μš©ν•˜λ©΄ 색상에 rgba λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ ν…μŠ€μ³ μ’Œν‘œμ— stpq λ₯Ό μ‚¬μš©ν•˜μ—¬ λ™μΌν•œ ꡬ성 μš”μ†Œμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

벑터 데이터 μœ ν˜•μ„ μ‚¬μš©ν•˜λ©΄ μŠ€μœ„μ¦λ§μ΄λΌκ³  ν•˜λŠ” ν₯λ―Έλ‘­κ³  μœ μ—°ν•œ ꡬ성 μš”μ†Œλ₯Ό 선택할 수 μžˆμŠ΅λ‹ˆλ‹€. μŠ€μœ„μ¦λ§μ„ μ‚¬μš©ν•˜λ©΄ λ‹€μŒκ³Ό 같은 ꡬ문을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

μ›λž˜ 벑터에 ν•΄λ‹Ή ꡬ성 μš”μ†Œκ°€ μžˆλŠ” ν•œ μ΅œλŒ€ 4개의 문자 쑰합을 μ‚¬μš©ν•˜μ—¬ λ™μΌν•œ μœ ν˜•μ˜ μƒˆ 벑터λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ vec2 의 .z ꡬ성 μš”μ†Œμ— μ ‘κ·Όν•  수 μ—†μŠ΅λ‹ˆλ‹€. λ˜ν•œ 벑터λ₯Ό λ‹€λ₯Έ 벑터 μƒμ„±μž ν˜ΈμΆœμ— 인수둜 μ „λ‹¬ν•˜μ—¬ ν•„μš”ν•œ 인수 수λ₯Ό 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

λ”°λΌμ„œ λ²‘ν„°λŠ” λͺ¨λ“  μ’…λ₯˜μ˜ μž…λ ₯ 및 좜λ ₯에 μ‚¬μš©ν•  수 μžˆλŠ” μœ μ—°ν•œ 데이터 μœ ν˜•μž…λ‹ˆλ‹€. 이 μ±… μ „μ²΄μ—μ„œ 벑터λ₯Ό 창의적으둜 관리 ν•  수 μžˆλŠ” 방법에 λŒ€ν•œ λ§Žμ€ 예λ₯Ό λ³Ό 수 μžˆμ„ κ²ƒμž…λ‹ˆλ‹€.

Ins and outs

μ…°μ΄λ”λŠ” κ·Έ 자체둜 멋진 μž‘μ€ ν”„λ‘œκ·Έλž¨μ΄μ§€λ§Œ μ „μ²΄μ˜ μΌλΆ€μ΄λ―€λ‘œ κ°œλ³„ 셰이더에 μž…λ ₯ 및 좜λ ₯을 κ°€μ Έ μ™€μ„œ 재료λ₯Ό 이동할 수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€.

GLSL은 κ·ΈλŸ¬ν•œ λͺ©μ μ„ μœ„ν•΄ in κ³Ό out ν‚€μ›Œλ“œλ₯Ό μ •μ˜ν–ˆμŠ΅λ‹ˆλ‹€. 각 μ…°μ΄λ”λŠ” ν•΄λ‹Ή ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μž…λ ₯ 및 좜λ ₯을 지정할 수 있으며 좜λ ₯ λ³€μˆ˜κ°€ μ „λ‹¬λ˜λŠ” λ‹€μŒ 셰이더 λ‹¨κ³„μ˜ μž…λ ₯ λ³€μˆ˜μ™€ μΌμΉ˜ν•˜λŠ” μœ„μΉ˜λ₯Ό 지정할 수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ λ²„ν…μŠ€ 및 ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”λŠ” μ•½κ°„ λ‹€λ¦…λ‹ˆλ‹€.

λ²„ν…μŠ€ μ…°μ΄λ”λŠ” μ–΄λ– ν•œ ν˜•νƒœμ˜ μž…λ ₯을 λ°˜λ“œμ‹œ λ°›μ•„μ•Ό ν•©λ‹ˆλ‹€. 그렇지 μ•ŠμœΌλ©΄ λΉ„νš¨μœ¨μ μž…λ‹ˆλ‹€. λ²„ν…μŠ€ μ…°μ΄λ”λŠ” λ²„ν…μŠ€ λ°μ΄ν„°μ—μ„œ 직접 μž…λ ₯을 λ°›λŠ”λ‹€λŠ” μ μ—μ„œ μž…λ ₯이 λ‹€λ¦…λ‹ˆλ‹€. λ²„ν…μŠ€ 데이터가 κ΅¬μ„±λ˜λŠ” 방식을 μ •μ˜ν•˜κΈ° μœ„ν•΄ μœ„μ§€ 메타 λ°μ΄ν„°λ‘œ μž…λ ₯ λ³€μˆ˜λ₯Ό μ§€μ •ν•˜μ—¬ CPUμ—μ„œ λ²„ν…μŠ€ 속성을 ꡬ성할 수 μžˆμŠ΅λ‹ˆλ‹€. 이전 μž₯μ—μ„œ 이것을 layout (location = 0) 으둜 λ³΄μ•˜μŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ λ²„ν…μŠ€ μ…°μ΄λ”λŠ” μž…λ ₯에 λŒ€ν•œ μΆ”κ°€ λ ˆμ΄μ•„μ›ƒ 사양이 ν•„μš”ν•˜λ―€λ‘œ λ²„ν…μŠ€ 데이터와 μ—°κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

layout (location = 0) μ§€μ •μžλ₯Ό μƒλž΅ν•˜κ³  glGetAttribLocation을 톡해 OpenGL μ½”λ“œμ˜ 속성 μœ„μΉ˜λ₯Ό 쿼리 ν•  μˆ˜λ„ μžˆμ§€λ§Œ λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ μ„€μ •ν•˜λŠ” 것을 μ„ ν˜Έν•©λ‹ˆλ‹€. 그것이 더 μ΄ν•΄ν•˜κΈ° 쉽고 λ„ˆμ˜(그리고 OpenGL도) μž‘μ—…μ„ μ ˆμ•½μ‹œμΌœ μ£ΌκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

λ‹€λ₯Έ μ˜ˆμ™ΈλŠ” ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더가 μ΅œμ’… 좜λ ₯ 색상을 생성해야 ν•˜λ―€λ‘œ ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더에 vec4 색상 좜λ ₯ λ³€μˆ˜κ°€ ν•„μš”ν•˜λ‹€λŠ” μ μž…λ‹ˆλ‹€. ν”„λž˜κ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ—μ„œ 좜λ ₯ 색상을 μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ ν•΄λ‹Ή ν”„λž˜κ·Έλ¨ΌνŠΈμ— λŒ€ν•œ 색상 버퍼 좜λ ₯이 μ •μ˜λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. (일반적으둜 OpenGL이 검은색 λ˜λŠ” ν°μƒ‰μœΌλ‘œ λžœλ”λ§ ν•˜λŠ”κ²ƒμ„ λœ»ν•¨)

λ”°λΌμ„œ ν•œ μ…°μ΄λ”μ—μ„œ λ‹€λ₯Έ μ…°μ΄λ”λ‘œ 데이터λ₯Ό 보내렀면 λ³΄λ‚΄λŠ” μ…°μ΄λ”μ—μ„œ 좜λ ₯을 μ„ μ–Έν•˜κ³  μˆ˜μ‹  μ…°μ΄λ”μ—μ„œ μœ μ‚¬ν•œ μž…λ ₯을 μ„ μ–Έν•΄μ•Ό ν•©λ‹ˆλ‹€. μœ ν˜•κ³Ό 이름이 μ–‘μͺ½μ—μ„œ λ™μΌν•˜λ©΄ OpenGL은 ν•΄λ‹Ή λ³€μˆ˜λ₯Ό ν•¨κ»˜ μ—°κ²° ν•œ λ‹€μŒ 셰이더 간에 데이터λ₯Ό 보낼 수 μžˆμŠ΅λ‹ˆλ‹€. (ν”„λ‘œκ·Έλž¨ 개체λ₯Ό linkingν•  λ•Œ μˆ˜ν–‰λ©λ‹ˆλ‹€.) 이것이 μ‹€μ œλ‘œ μ–΄λ–»κ²Œ μž‘λ™ν•˜λŠ”μ§€ 보여주기 μœ„ν•΄ 이전 μž₯의 셰이더λ₯Ό λ³€κ²½ν•˜μ—¬ λ²„ν…μŠ€ 셰이더가 ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더λ₯Ό κ²°μ •ν•˜λ„λ‘ ν•  κ²ƒμž…λ‹ˆλ‹€.

Vertex shader

#version 330 core
layout (location = 0) in vec3 aPos; // μœ„μΉ˜ λ³€μˆ˜λŠ” 속성 μœ„μΉ˜ 0을 κ°€μ§‘λ‹ˆλ‹€.
  
out vec4 vertexColor; // ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더에 색상 좜λ ₯ 지정

void main()
{
    gl_Position = vec4(aPos, 1.0); // vec3을 vec4의 μƒμ„±μžμ— 직접 μ œκ³΅ν•˜λŠ” 방법
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 좜λ ₯ λ³€μˆ˜λ₯Ό μ§„ν•œ λΉ¨κ°•μœΌλ‘œ 지정
}

Fragment shader

#version 330 core
out vec4 FragColor;
  
in vec4 vertexColor; // λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ 온 μž…λ ₯ λ³€μˆ˜ (같은 이름과 νƒ€μž…)  

void main()
{
    FragColor = vertexColor;
} 

vertexColor λ³€μˆ˜λ₯Ό λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ μ„€μ • ν•œ vec4 좜λ ₯으둜 μ„ μ–Έν–ˆκ³ , ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ—μ„œ λΉ„μŠ·ν•œ vertexColor μž…λ ₯을 μ„ μ–Έ ν•œ 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. λ‘˜ λ‹€ λ™μΌν•œ μœ ν˜•κ³Ό 이름을 κ°–κΈ° λ•Œλ¬Έμ— ν”„λž˜κ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ˜ vertexColorλŠ” λ²„ν…μŠ€ μ…°μ΄λ”μ˜ vertexColor와 μ—°κ²°λ©λ‹ˆλ‹€. λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ 색상을 μ§„ν•œ λΉ¨κ°• μƒ‰μœΌλ‘œ μ„€μ • ν–ˆμœΌλ―€λ‘œ ν”„λ ˆκ·Έλ¨ΌνŠΈμ˜ 결과도 μ§„ν•œ 빨간색이어야 ν•©λ‹ˆλ‹€. λ‹€μŒ μ΄λ―Έμ§€λŠ” κ²°κ³Όλ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€

shaders

자 κ°‘λ‹ˆλ‹€! λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”λ‘œ 값을 보낼 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 쑰금 더 λ©‹μ§€κ²Œ κΎΈλ―Έκ³  μ‘μš© ν”„λ‘œκ·Έλž¨μ—μ„œ ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”λ‘œ 색상을 보낼 수 μžˆλŠ”μ§€ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€

Uniforms

Uniform은 CPU의 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ GPU의 μ…°μ΄λ”λ‘œ 데이터λ₯Ό μ „λ‹¬ν•˜λŠ” 또 λ‹€λ₯Έ λ°©λ²•μž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μœ λ‹ˆνΌμ€ λ²„ν…μŠ€ 속성에 λΉ„ν•΄ μ•½κ°„ λ‹€λ¦…λ‹ˆλ‹€. μš°μ„  μœ λ‹ˆνΌμ€ μ „μ—­μž…λ‹ˆλ‹€. 무슨 λœ»μ΄λ‚˜λ©΄ μœ λ‹ˆνΌ λ³€μˆ˜λŠ” 셰이더 ν”„λ‘œκ·Έλž¨ κ°œμ²΄λ§ˆλ‹€ κ³ μœ ν•˜λ©° 셰이더 ν”„λ‘œκ·Έλž¨μ˜ λͺ¨λ“  λ‹¨κ³„μ—μ„œ λͺ¨λ“  셰이더가 μ•‘μ„ΈμŠ€ ν•  수 μžˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€. λ‘λ²ˆμ§Έλ‘œ, μœ λ‹ˆνΌμ€ 값을 μ„€μ •ν•˜λŠ” 것과 상관없이 μœ λ‹ˆνΌμ€ μž¬μ„€μ •λ˜κ±°λ‚˜ μ—…λ°μ΄νŠΈ λ λ•ŒκΉŒμ§€ 값을 μœ μ§€ν•©λ‹ˆλ‹€.

GLSLμ—μ„œ μœ λ‹ˆνΌμ„ μ„ μ–Έν•˜λ €λ©΄ 이름이 μžˆλŠ” 셰이더에 uniform μ΄λΌλŠ” ν‚€μ›Œλ“œλ₯Ό μΆ”κ°€ ν•˜κΈ°λ§Œν•˜λ©΄ λ©λ‹ˆλ‹€. κ·Έ μ‹œμ λΆ€ν„° μ…°μ΄λ”μ—μ„œ μƒˆλ‘œ μ„ μ–Έλœ μœ λ‹ˆνΌμ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ²ˆμ—λŠ” μœ λ‹ˆνΌμ„ 톡해 μ‚Όκ°ν˜•μ˜ 색상을 μ„€μ •ν•  수 μžˆλŠ”μ§€ μ‚΄νŽ΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

#version 330 core
out vec4 FragColor;
  
uniform vec4 ourColor; // OpenGL μ½”λ“œμ—μ„œ 이 λ³€μˆ˜λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€.

void main()
{
    FragColor = ourColor;
}   

ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ—μ„œ μœ λ‹ˆνΌ vec4 OurColorλ₯Ό μ„ μ–Έν•˜κ³  ν”„λž˜κ·Έλ¨ΌνŠΈμ˜ 좜λ ₯ 색상을 μœ λ‹ˆνΌκ°’μœΌλ‘œ μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€. μœ λ‹ˆνΌμ€ μ „μ—­ λ³€μˆ˜μ΄λ―€λ‘œ μ›ν•˜λŠ” 셰이더 λ‹¨κ³„μ—μ„œ μ •μ˜ν•  수 μžˆμœΌλ―€λ‘œ ν”„λž˜κ·Έλ¨Όν”„ μŽΌμ΄λ”μ— λ­”κ°€λ₯Ό κ°€μ Έ 였기 μœ„ν•΄ λ‹€μ‹œ λ²„ν…μŠ€ 셰이더λ₯Ό κ±°μΉ  ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€. λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ 이 μœ λ‹ˆνΌμ„ μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ κ±°κΈ°μ—μ„œ μ •μ˜ ν•  ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

GLSL μ½”λ“œμ—μ„œλŠ” μ‚¬μš©λ˜μ§€ μ•ŠλŠ” μœ λ‹ˆνΌμ„ μ„ μ–Έν•˜λ©΄ μ»΄νŒŒμΌλŸ¬λŠ” 컴파일 된 λ²„μ „μ—μ„œ λ³€μˆ˜λ₯Ό μžλ™μœΌλ‘œ μ œκ±°ν•©λ‹ˆλ‹€. μ΄λŠ” 였λ₯˜μ˜ 원인이 될 수 μžˆμŠ΅λ‹ˆλ‹€.

μœ λ‹ˆνΌμ΄ ν˜„μž¬ λΉ„μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 아직 μœ λ‹ˆνΌμ— 데이터λ₯Ό μΆ”κ°€ν•˜μ§€ μ•Šμ•˜μœΌλ‹ˆ 데이터λ₯Ό μΆ”κ°€ ν•΄ λ΄…μ‹œλ‹€. λ¨Όμ € μ…°μ΄λ”μ—μ„œ μœ λ‹ˆνΌ μ†μ„±μ˜ 인덱슀 / μœ„μΉ˜λ₯Ό μ°Ύμ•„μ•Ό ν•©λ‹ˆλ‹€. μœ λ‹ˆνΌμ˜ 인덱슀 / μœ„μΉ˜κ°€ 있으면 값을 μ—…λ°μ΄νŠΈ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더에 단일 색상을 μ „λ‹¬ν•˜λŠ” λŒ€μ‹  μ‹œκ°„μ΄ 지남에 따라 점차적으둜 색상을 λ³€κ²½ν•˜μ—¬ κΎΈλ©°λ΄…μ‹œλ‹€.

float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

λ¨Όμ € glfwGetTime()을 톡해 μ‹€ν–‰ μ‹œκ°„μ„ 초 λ‹¨μœ„λ‘œ κ²€μƒ‰ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ sin ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ 0.0 - 1.0 λ²”μœ„μ—μ„œ 색상을 λ³€κ²½ν•˜κ³  κ²°κ³Όλ₯Ό greenValue에 μ €μž₯ν•©λ‹ˆλ‹€

그런 λ‹€μŒ glGetUniformLocation을 μ‚¬μš©ν•˜μ—¬ ourColor μœ λ‹ˆνΌμ˜ μœ„μΉ˜λ₯Ό μΏΌλ¦¬ν•©λ‹ˆλ‹€. 셰이더 ν”„λ‘œκ·Έλž¨κ³Ό μœ λ‹ˆνΌ (μœ„μΉ˜λ₯Ό κ²€μƒ‰ν•˜λ €λŠ”)의 이름을 쿼리 ν•¨μˆ˜μ— μ œκ³΅ν•©λ‹ˆλ‹€. glGetUniformLocation이 -1 을 λ°˜ν™˜ν•˜λ©΄ μœ„μΉ˜λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ glUniform4f ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μœ λ‹ˆνΌμ˜ 값을 μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μœ λ‹ˆνΌμ˜ μœ„μΉ˜λ₯Ό μ°ΎλŠ” 데 셰이더 ν”„λ‘œκ·Έλž¨μ„ λ¨Όμ € μ‚¬μš©ν•  ν•„μš”λŠ” μ—†μ§€λ§Œ μœ λ‹ˆνΌ μ—…λ°μ΄νŠΈ λŠ” ν˜„μž¬ ν™œμ„±ν™” 된 셰이더 ν”„λ‘œκ·Έλž¨μ—μ„œ uniform을 μ„€μ •ν•˜λ―€λ‘œ λ¨Όμ € ν”„λ‘œκ·Έλž¨μ„ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€ (glUseProgram호좜)

OpenGL의 핡심은 CλΌμ΄λΈŒλŸ¬λ¦¬μ— 있기 떄문에 ν•¨μˆ˜ μ˜€λ²„λ‘œλ”©μ— λŒ€ν•œ κΈ°λ³Έ 지원이 μ—†μœΌλ―€λ‘œ λ‹€λ₯Έ μœ ν˜•μœΌλ‘œ ν•¨μˆ˜λ₯Ό 호좜 ν•  수 μžˆλŠ” 곳이면 μ–΄λ””μ—μ„œλ‚˜ OpenGL은 ν•„μš”ν•œ 각 μœ ν˜•μ— λŒ€ν•΄ μƒˆ ν•¨μˆ˜λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.

glUniform이 이에 λŒ€ν•œ μ™„λ²½ν•œ μ˜ˆμž…λ‹ˆλ‹€. 이 κΈ°λŠ₯은 μ„€μ •ν•˜λ €λŠ” μœ λ‹ˆνΌμœ ν˜•μ— λŒ€ν•œ νŠΉμ • 접미사가 ν•„μš”ν•©λ‹ˆλ‹€. κ°€λŠ₯ν•œ 접미사 쀑 λͺ‡ κ°€μ§€λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • f : ν•¨μˆ˜κ°€ 값이 float 라고 μ˜ˆμƒν•©λ‹ˆλ‹€.
  • i : ν•¨μˆ˜κ°€ 값이 int 라고 μ˜ˆμƒν•©λ‹ˆλ‹€.
  • ui: ν•¨μˆ˜κ°€ 값이 unsigned int 라고 μ˜ˆμƒν•©λ‹ˆλ‹€.
  • 3f : ν•¨μˆ˜λŠ” 값이 3개의 float 라고 μƒκ°ν•©λ‹ˆλ‹€.
  • fv : ν•¨μˆ˜λŠ” 값이 float λ°±ν„°/배열이라고 μƒκ°ν•©λ‹ˆλ‹€.

OpenGL μ˜΅μ…˜μ„ ꡬ성 ν•  λ•Œλ§ˆλ‹€ μœ ν˜•μ— ν•΄λ‹Ήν•˜λŠ” μ˜€λ²„λ‘œλ“œ 된 κΈ°λŠ₯을 μ„ νƒν•˜κΈ°λ§Œ ν•˜λ©΄ λ©λ‹ˆλ‹€. 우리의 경우 glUniform4fλ₯Ό 톡해 데이터λ₯Ό μ „ μ „λ‹¬ν•˜κΈ° μœ„ν•΄ uniform의 4개의 floatλ₯Ό κ°œλ³„μ μœΌλ‘œ μ„€μ •ν•˜λ €κ³  ν•©λ‹ˆλ‹€. (fv 버전도 μ‚¬μš©ν•  수 있음).

이제 균일 λ³€μˆ˜μ˜ 값을 μ„€μ •ν•˜λŠ” 방법을 μ•Œμ•˜μœΌλ―€λ‘œ λ Œλ”λ§μ— μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 색상을 μ μ§„μ μœΌλ‘œ λ³€κ²½ν•˜λ €λ©΄ 맀 ν”„λ ˆμž„λ§ˆλ‹€ 이 μœ λ‹ˆνΌμ„ μ—…λ°μ΄νŠΈ ν•΄μ•Ό ν•©λ‹ˆλ‹€. 그렇지 μ•ŠμœΌλ©΄ μ‚Όκ°ν˜•μ€ ν•œ 번만 μ„€μ •ν•˜λ©΄ 단색을 μœ μ§€ν•©λ‹ˆλ‹€. λ”°λΌμ„œ μš°λ¦¬λŠ” greenValueλ₯Ό κ³„μ‚°ν•˜κ³  각 λ Œλ”λ§ λ°˜λ³΅λ§ˆλ‹€ uniform을 μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

while(!glfwWindowShouldClose(window))
{
    // μž…λ ₯
    processInput(window);

    // λ Œλ”
    // 컬러 버퍼 클리어
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

		// 셰이더λ₯Ό ν™œμ„±ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€.
    glUseProgram(shaderProgram);
  
    // uniform color μ—…λ°μ΄νŠΈ
    float timeValue = glfwGetTime();
    float greenValue = sin(timeValue) / 2.0f + 0.5f;
    int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
    glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

    // μ‚Όκ°ν˜•μ„ λ Œλ”λ§ν•©λ‹ˆλ‹€.
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);
  
		// 버퍼 μŠ€μ™‘ 및 IO 이벀트 폴링
    glfwSwapBuffers(window);
    glfwPollEvents();
}

이 μ½”λ“œλŠ” 이전 μ½”λ“œλ₯Ό 비ꡐ적 κ°„λ‹¨ν•˜κ²Œ μˆ˜μ • ν•œ κ²ƒμž…λ‹ˆλ‹€. μ΄λ²ˆμ—λŠ” μ‚Όκ°ν˜•μ„ 그리기 전에 각 ν”„λ ˆμž„λ§ˆλ‹€ κ· μΌν•œ 값을 μ—…λ°μ΄νŠΈ ν•©λ‹ˆλ‹€. μœ λ‹ˆνΌμ„ μ˜¬λ°”λ₯΄κ²Œ μ—…λ°μ΄νŠΈ ν•˜λ©΄ μ‚Όκ°ν˜•μ˜ 색상이 점차 λ…Ήμƒ‰μ—μ„œ κ²€μ€μƒ‰μœΌλ‘œ, λ‹€μ‹œ λ…Ήμƒ‰μœΌλ‘œ λ³€ν•˜λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

shaders.mp4

λ¬Έμ œκ°€ μžˆλŠ” 경우 μ—¬κΈ°μ—μ„œ μ†ŒμŠ€μ½”λ“œλ₯Ό ν™•μΈν•˜μ„Έμš”.

λ³΄μ‹œλ‹€μ‹œν”Ό μœ λ‹ˆνΌμ€ λͺ¨λ“  ν”„λ ˆμž„μ„ λ³€κ²½ν•  수 μžˆλŠ” 속성을 μ„€μ •ν•˜κ±°λ‚˜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό 셰이더 간에 데이터λ₯Ό κ΅ν™˜ν•˜λŠ” 데 μœ μš©ν•œ λ„κ΅¬μž…λ‹ˆλ‹€.ν•˜μ§€λ§Œ 각 λ²„ν…μŠ€μ— λŒ€ν•œ 색상을 μ„€μ •ν•˜λ €λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒμš”? 이 κ²½μš°μ—λŠ” μš°λ¦¬κ°€ 가진 λ²„ν…μŠ€λ§ŒνΌμ˜ uniform을 μ„ μ–Έν•΄μ•Ό ν•©λ‹ˆλ‹€. 더 λ‚˜μ€ 해결책은 λ²„ν…μŠ€ 속성에 더 λ§Žμ€ 데이터λ₯Ό ν¬ν•¨ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

더 λ§Žμ€ 속성듀

이전 μž₯μ—μ„œ VBOλ₯Ό μ±„μš°κ³ , λ²„ν…μŠ€ 속성 포인터λ₯Ό κ΅¬μ„±ν•˜κ³ , λͺ¨λ“  것을 VAO에 μ €μž₯ν•˜λŠ” 방법을 λ³΄μ•˜μŠ΅λ‹ˆλ‹€. μ΄λ²ˆμ—λŠ” λ²„ν…μŠ€ 데이터에 색상 데이터도 μΆ”κ°€ν•˜λ €κ³  ν•©λ‹ˆλ‹€. 색상 데이터λ₯Ό vertices 배열에 3개의 float λ₯Ό μΆ”κ°€ν•  κ²ƒμž…λ‹ˆλ‹€. μ‚Όκ°ν˜•μ˜ 각 λͺ¨μ„œλ¦¬μ— 빨간색, μ΄ˆλ‘μƒ‰ 및 νŒŒλž€μƒ‰μ„ 각각 ν• λ‹Ήν•©λ‹ˆλ‹€.

float vertices[] = {
    // μœ„μΉ˜              // 색상
     0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 였λ₯Έμͺ½ ν•˜λ‹¨
    -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // μ™Όμͺ½ ν•˜λ‹¨
     0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 상단
};    

이제 λ²„ν…μŠ€ μ…°μ΄λ”λ‘œ 보낼 더 λ§Žμ€ 데이터가 μžˆμœΌλ―€λ‘œ λ²„ν…μŠ€ 속성 μž…λ ₯으둜 색상 값도 μˆ˜μ‹ ν•˜λ„λ‘ λ²„ν…μŠ€ 셰이더도 μ‘°μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. aColor 속성을 λ ˆμ΄μ•„μ›ƒ μ§€μ •μžλ₯Ό μ‚¬μš©ν•˜μ—¬ μœ„μΉ˜λ₯Ό 1둜 μ„€μ •ν–ˆμŠ΅λ‹ˆλ‹€.

#version 330 core
layout (location = 0) in vec3 aPos;   // μœ„μΉ˜ λ³€μˆ˜λŠ” 속성 μœ„μΉ˜ 0을 κ°€μ§‘λ‹ˆλ‹€.
layout (location = 1) in vec3 aColor; // 색상 λ³€μˆ˜λŠ” 속성 μœ„μΉ˜ 1을 κ°€μ§‘λ‹ˆλ‹€.
  
out vec3 ourColor; // 좜λ ₯ 색상은 ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”λ‘œ κ°‘λ‹ˆλ‹€.

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor; // ourColorλ₯Ό λ²„ν…μŠ€ λ°μ΄ν„°μ—μ„œ 얻은 μž…λ ₯ μƒ‰μƒμœΌλ‘œ μ„€μ •ν•©λ‹ˆλ‹€
}       

ν”„λ ˆκ·Έλ¨ΌνŠΈ 색상에 더 이상 uniform을 μ‚¬μš©ν•˜μ§€λŠ” μ•Šμ§€λ§Œ, 이제 ourColor 좜λ ₯ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더도 λ³€κ²½ν•΄μ•Ό ν•©λ‹ˆλ‹€.

#version 330 core
out vec4 FragColor;  
in vec3 ourColor;
  
void main()
{
    FragColor = vec4(ourColor, 1.0);
}

λ‹€λ₯Έ λ²„ν…μŠ€ 속성을 μΆ”κ°€ν•˜κ³  VBO의 λ©”λͺ¨λ¦¬λ₯Ό μ—…λ°μ΄νŠΈ ν–ˆκΈ° λ•Œλ¬Έμ— λ²„ν…μŠ€ 속성 포인터λ₯Ό λ‹€μ‹œ ꡬ성해야 ν•©λ‹ˆλ‹€. VBO λ©”λͺ¨λ¦¬μ˜ μ—…λ°μ΄νŠΈ 된 λ°μ΄ν„°λŠ” 이제 λ‹€μŒκ³Ό 같이 λ³΄μž…λ‹ˆλ‹€.

vertex_attribute_pointer_interleaved

ν˜„μž¬ λ ˆμ΄μ•„μ›ƒμ„ μ•Œλ©΄ glVertexAttribPointer둜 λ²„ν…μŠ€ ν˜•μ‹μ„ μ—…λ°μ΄νŠΈ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

// μœ„μΉ˜ 속성
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 색상 속성
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);

glVertexAttribPointer의 μ•žμ— μžˆλŠ” λͺ‡κ°œμ˜ μΈμˆ˜λŠ” 비ꡐ적 κ°„λ‹¨ν•©λ‹ˆλ‹€. μ΄λ²ˆμ—λŠ” 속성 μœ„μΉ˜ 1 에 vertex 속성을 κ΅¬μ„±ν•©λ‹ˆλ‹€. 색상 κ°’μ˜ ν¬κΈ°λŠ” float 3 이며 값을 μ •κ·œν™” ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€

이제 두 개의 λ²„ν…μŠ€ 속성이 μžˆμœΌλ―€λ‘œ stride 값을 λ‹€μ‹œ 계산해야 ν•©λ‹ˆλ‹€. 데이터 λ°°μ—΄μ—μ„œ λ‹€μŒ 속성 κ°’ (예 : μœ„μΉ˜ λ²‘ν„°μ˜ λ‹€μŒ x ꡬ성 μš”μ†Œ)을 μ–»μœΌλ €λ©΄ 6 float 을 였λ₯Έμͺ½μœΌλ‘œ, 3κ°œλŠ” μœ„μΉ˜ κ°’μœΌλ‘œ, 3κ°œλŠ” 색상 κ°’μœΌλ‘œ 이동해야 ν•©λ‹ˆλ‹€. 이것은 μš°λ¦¬μ—κ²Œ float 크기 (= 24 λ°”μ΄νŠΈ)의 6배의 stride값을 μ œκ³΅ν•©λ‹ˆλ‹€.

λ˜ν•œ μ΄λ²ˆμ—λŠ” μ˜€ν”„μ…‹μ„ 지정해야 ν•©λ‹ˆλ‹€. 각 λ²„ν…μŠ€μ˜ μœ„μΉ˜ λ²„ν…μŠ€ 속성이 λ¨Όμ €μ΄λ―€λ‘œ μ˜€ν”„μ…‹ 0을 μ„ μ–Έν•©λ‹ˆλ‹€. color 속성은 μœ„μΉ˜ 데이터 λ‹€μŒμ— μ‹œμž‘ν•˜λ―€λ‘œ μ˜€ν”„μ…‹μ€ λ°”μ΄νŠΈ λ‹¨μœ„μ˜ 3 * sizeof (float) (= 12 λ°”μ΄νŠΈ) μž…λ‹ˆλ‹€.

μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ‹€μ‹œ μ‹œμž‘ν•˜λ©΄ λ‹€μŒ 이미지가 ν‘œμ‹œλ©λ‹ˆλ‹€.

shaders3

λ¬Έμ œκ°€ μžˆλŠ” 경우 μ—¬κΈ°μ—μ„œ μ†ŒμŠ€ μ½”λ“œλ₯Ό ν™•μΈν•˜μ„Έμš”.

ν˜„μž¬ 보고 μžˆλŠ” μ΄λ―Έμ§€λŠ” κ±°λŒ€ν•œ 색상 νŒ”λ ˆνŠΈκ°€ μ•„λ‹Œ 3가지 μƒ‰μƒλ§Œ μ œκ³΅ν–ˆκΈ° λ•Œλ¬Έμ— 이미지가 μ˜ˆμƒν•œ 것과 μ •ν™•νžˆ μΌμΉ˜ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€. 이것은 λͺ¨λ‘ ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ—μ„œ ν”„λ ˆκ·Έλ¨ΌνŠΈ 보간(interpolation)이라고 ν•˜λŠ” κ²ƒμ˜ κ²°κ³Όμž…λ‹ˆλ‹€. μ‚Όκ°ν˜•μ„ λ Œλ”λ§ ν•  λ•Œ λ ˆμŠ€ν„°ν™” λ‹¨κ³„λŠ” 일반적으둜 μ›λž˜ μ§€μ •λœ λ²„ν…μŠ€λ³΄λ‹€ 훨씬 λ§Žμ€ ν”„λ ˆκ·Έλ¨ΌνŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ λ ˆμŠ€ν„°λΌμ΄μ €λŠ” μ‚Όκ°ν˜• λͺ¨μ–‘μ˜ μœ„μΉ˜μ— 따라 각 ν”„λ ˆκ·Έλ¨ΌνŠΈ μœ„μΉ˜λ₯Ό κ²°μ •ν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ μœ„μΉ˜λ₯Ό 기반으둜 λͺ¨λ“  ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ˜ μž…λ ₯ λ³€μˆ˜λ₯Ό λ³΄κ°„ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ μœ„μͺ½ 점이 녹색이고 μ•„λž˜μͺ½ 점이 νŒŒλž€μƒ‰μΈ 선이 μžˆλ‹€κ³  κ°€μ •ν•©λ‹ˆλ‹€. ν”„λž˜κ·Έλ¨ΌνŠΈ 셰이더가 라인의 70% μœ„μΉ˜ 주변에 μžˆλŠ” ν”„λž˜κ·Έλ¨ΌνŠΈμ—£ γ…“μ‹€ν–‰λ˜λŠ” 경우 κ²°κ³Ό μƒ‰μƒμ˜ μž…λ ₯ 속성은 녹색과 νŒŒλž€μƒ‰μ˜ μ„ ν˜• 쑰합이 λ©λ‹ˆλ‹€. 더 μ •ν™•ν•˜κ²Œ λ§ν•˜λ©΄ 30% 의 νŒŒλž€μƒ‰κ³Ό 70% 의 λ…Ήμƒ‰μž…λ‹ˆλ‹€.

이것이 λ°”λ‘œ μ‚Όκ°ν˜•μ—μ„œ μΌμ–΄λ‚˜λŠ” μΌμž…λ‹ˆλ‹€. μš°λ¦¬λŠ” 3개의 꼭지점과 3개의 색상을 가지고 있으며 μ‚Όκ°ν˜•μ˜ ν”½μ…€λ‘œ λ³Ό λ•Œ λŒ€λž΅ 50000개의 ν”„λ ˆκ·Έλ¨ΌνŠΈλ₯Ό ν¬ν•¨ν•˜κ³  μžˆμ„ κ²ƒμž…λ‹ˆλ‹€ .색상을 잘 μ‚΄νŽ΄λ³΄λ©΄ λͺ¨λ“  것이 μ˜λ―Έκ°€ μžˆμŒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. λΉ¨κ°„μƒ‰μ—μ„œ νŒŒλž€μƒ‰μœΌλ‘œ λ¨Όμ € 보라색이 되고 νŒŒλž€μƒ‰μœΌλ‘œ λ°”λ€λ‹ˆλ‹€. ν”„λž˜κ·Έλ¨ΌνŠΈ 보간은 λͺ¨λ“  ν”„λž˜κ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ˜ μž…λ ₯ 속성에 μ μš©λ©λ‹ˆλ‹€.

우리의 셰이더 클래슀

셰이더 μž‘μ„±, 컴파일 및 κ΄€λ¦¬λŠ” 맀우 번거둜울 수 μžˆμŠ΅λ‹ˆλ‹€. 셰이더 μ£Όμ œμ— λŒ€ν•œ λ§ˆμ§€λ§‰ μž‘μ—…μœΌλ‘œμ„œ, μš°λ¦¬λŠ” 셰이더λ₯Ό λ””μŠ€ν¬μ—μ„œ 읽고, 컴파일 ν•˜κ³ , μ—°κ²°ν•˜κ³ , 였λ₯˜λ₯Ό ν™•μΈν•˜κ³ , μ‚¬μš©ν•˜κΈ° μ‰¬μš΄ 셰이더 클래슀λ₯Ό λ§Œλ“€μ–΄ 우리의 삢을 μ’€ 더 μ‰½κ²Œ λ§Œλ“€ κ²ƒμž…λ‹ˆλ‹€. 이것은 λ˜ν•œ μš°λ¦¬κ°€ μ§€κΈˆκΉŒμ§€ 배운 μ§€μ‹μ˜ 일뢀λ₯Ό μœ μš©ν•œ 좔상적인 물체에 μ–΄λ–»κ²Œ μΊ‘μŠν™”ν•  수 μžˆλŠ”μ§€μ— λŒ€ν•œ 아이디어λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

셰이더 ν΄λž˜μŠ€λŠ” 주둜 ν•™μŠ΅ λͺ©μ κ³Ό νœ΄λŒ€μ„±μ„ μœ„ν•΄ 헀더 파일둜 μž‘μ„±ν•˜κ² μŠ΅λ‹ˆλ‹€. λ¨Όμ € ν•„μš”ν•œ μΈν΄λ£¨λ“œ ν•­λͺ©μ„ μΆ”κ°€ν•˜κ³  클래슀 ꡬ쑰λ₯Ό μ •μ˜ν•˜μ„Έμš”.

#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h> // ν•„μš”ν•œ OpenGL 헀더λ₯Ό λͺ¨λ‘ κ°€μ Έμ˜¬ 수 μžˆλŠ” gladλ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.
  
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
  

class Shader
{
public:
    // ν”„λ‘œκ·Έλž¨ ID
    unsigned int ID;
  
		// μƒμ„±μžκ°€ 셰이더λ₯Ό 읽고 μƒμ„±ν•©λ‹ˆλ‹€.
    Shader(const char* vertexPath, const char* fragmentPath);
    // 셰이더λ₯Ό μ‚¬μš©/ν™œμ„±ν™”ν•©λ‹ˆλ‹€.
    void use();
		// μœ λ‹ˆνΌ ν•¨μˆ˜ μœ ν‹Έλ¦¬ν‹°
    void setBool(const std::string &name, bool value) const;  
    void setInt(const std::string &name, int value) const;   
    void setFloat(const std::string &name, float value) const;
};
  
#endif

헀더 파일 맨 μœ„μ— λͺ‡ 가지 μ „μ²˜λ¦¬κΈ° μ§€μ‹œλ¬Έμ„ μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. 이런 짧은 μ½”λ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ μ—¬λŸ¬ νŒŒμΌμ— 셰이더 헀더가 ν¬ν•¨λ˜μ–΄ μžˆλ”λΌλ„ 아직 ν¬ν•¨λ˜μ§€ μ•Šμ€ κ²½μš°μ—λ§Œ 이 헀더 νŒŒμΌμ„ ν¬ν•¨ν•˜κ³  μ»΄νŒŒμΌν•˜λ„λ‘ μ»΄νŒŒμΌλŸ¬μ— μ•Œλ¦½λ‹ˆλ‹€. 이것은 μ—°κ²° μΆ©λŒμ„ λ°©μ§€ν•©λ‹ˆλ‹€.

셰이더 ν΄λž˜μŠ€λŠ” 셰이더 ν”„λ‘œκ·Έλž¨μ˜ IDλ₯Ό λ³΄μœ ν•©λ‹ˆλ‹€. μƒμ„±μžλŠ” λ””μŠ€ν¬μ— κ°„λ‹¨ν•œ ν…μŠ€νŠΈ 파일둜 μ €μž₯ν•  수 μžˆλŠ” λ²„ν…μŠ€ 및 ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”μ˜ μ†ŒμŠ€ μ½”λ“œ 파일 경둜λ₯Ό 각각 ν•„μš”λ‘œ ν•©λ‹ˆλ‹€. μ•½κ°„μ˜ μΆ”κ°€ κΈ°λŠ₯을 μΆ”κ°€ν•˜κΈ° μœ„ν•΄ λͺ‡ 가지 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ₯Ό μΆ”κ°€ν•˜μ—¬ 쑰금 더 νŽΈλ¦¬ν•˜κ²Œ ν•©λ‹ˆλ‹€. useλŠ” 셰이더 ν”„λ‘œκ·Έλž¨μ„ ν™œμ„±ν™” ν•˜κ³  λͺ¨λ“  set 으둜 μ‹œμž‘ν•˜λŠ” ν•¨μˆ˜λŠ” κ· μΌν•œ μœ„μΉ˜λ₯Ό μΏΌλ¦¬ν•˜κ³  값을 μ„€μ •ν•©λ‹ˆλ‹€.

νŒŒμΌλ‘œλΆ€ν„° μ½μ–΄μ˜€κΈ°

C++ 파일 μŠ€νŠΈλ¦Όμ„ μ‚¬μš©ν•˜μ—¬ 파일의 λ‚΄μš©μ„ μ—¬λŸ¬ string개체둜 μ½μŠ΅λ‹ˆλ‹€.

Shader(const char* vertexPath, const char* fragmentPath)
{
    // 1. filePathμ—μ„œ λ²„ν…μŠ€/ν”„λ ˆκ·Έλ¨ΌνŠΈ μ†ŒμŠ€μ½”λ“œ 검색
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    // ifstream κ°œμ²΄κ°€ μ˜ˆμ™Έλ₯Ό throwν•  수 μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.
    vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
    try 
    {
        // 파일 μ—΄κΈ°
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
				// 파일의 버퍼 λ‚΄μš©μ„ 슀트림으둜 μ½μŠ΅λ‹ˆλ‹€
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();		
        // 파일 ν—¨λ“€λŸ¬λ₯Ό λ‹«μŠ΅λ‹ˆλ‹€
        vShaderFile.close();
        fShaderFile.close();
        // μŠ€νŠΈλ¦Όμ„ 슀트링으둜 λ³€κ²½ν•©λ‹ˆλ‹€
        vertexCode   = vShaderStream.str();
        fragmentCode = fShaderStream.str();		
    }
    catch(std::ifstream::failure e)
    {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char* fShaderCode = fragmentCode.c_str();
    [...]

λ‹€μŒμœΌλ‘œ 셰이더λ₯Ό μ»΄νŒŒμΌν•˜κ³  μ—°κ²°ν•΄μ•Όν•©λ‹ˆλ‹€. λ˜ν•œ 컴파일/ 링크 μ‹€νŒ¨ μ—¬λΆ€λ₯Ό κ²€ν† ν•˜κ³  μžˆλŠ” κ²½μš°μ—λŠ” 컴파일 νƒ€μž„ 였λ₯˜λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€. 이것은 λ””λ²„κΉ…ν• λ•Œ μœ μš©ν•©λ‹ˆλ‹€ (μ΄λŸ¬ν•œ 였λ₯˜ λ‘œκ·Έκ°€ ν•„μš”ν•©λ‹ˆλ‹€.

// 2. 셰이더 컴파일
unsigned int vertex, fragment;
int success;
char infoLog[512];
   
// λ²„ν…μŠ€ 셰이더
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// μ–΄λ– ν•œ 컴파일 μ—λŸ¬κ°€ 있으면 좜λ ₯ν•©λ‹ˆλ‹€.
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if(!success)
{
    glGetShaderInfoLog(vertex, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
};
  
// ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더도 λΉ„μŠ·ν•©λ‹ˆλ‹€.
[...]
  
// 셰이더 ν”„λ‘œκ·Έλž¨
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
// 링킹 μ—λŸ¬κ°€ μžˆμ„ 경우 좜λ ₯ν•©λ‹ˆλ‹€.
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if(!success)
{
    glGetProgramInfoLog(ID, 512, NULL, infoLog);
    std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
  
// 셰이더가 μ§€κΈˆ ν”„λ‘œκ·Έλž¨μ— μ—°κ²°λ˜μ–΄ 있고 더이상 ν•„μš”ν•˜μ§€ μ•ŠμœΌλ‹ˆ μ‚­μ œ
glDeleteShader(vertex);
glDeleteShader(fragment);

use ν•¨μˆ˜λŠ” κ°„λ‹¨ν•©λ‹ˆλ‹€.

void use() 
{ 
    glUseProgram(ID);
}  

Uniform setter ν•¨μˆ˜λ“€μ€ λ‹€ λΉ„μŠ·ν•©λ‹ˆλ‹€.

void setBool(const std::string &name, bool value) const
{         
    glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); 
}
void setInt(const std::string &name, int value) const
{ 
    glUniform1i(glGetUniformLocation(ID, name.c_str()), value); 
}
void setFloat(const std::string &name, float value) const
{ 
    glUniform1f(glGetUniformLocation(ID, name.c_str()), value); 
} 

그럼 μ™„μ„± 된 셰이더 ν΄λž˜μŠ€κ°€ μžˆμŠ΅λ‹ˆλ‹€. 셰이더 클래슀λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 맀우 μ‰½μŠ΅λ‹ˆλ‹€. 셰이더 개체λ₯Ό ν•œ 번 λ§Œλ“€κ³  κ·Έ μ‹œμ λΆ€ν„° μ‚¬μš©μ„ μ‹œμž‘ν•˜λ©΄ λ©λ‹ˆλ‹€.

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
[...]
while(...)
{
    ourShader.use();
    ourShader.setFloat("someUniform", 1.0f);
    DrawStuff();
}

μ—¬κΈ°μ„œλŠ” shader.vs 와 shader.fs λΌλŠ” 두 νŒŒμΌμ— λ²„ν…μŠ€ 및 ν”„λ ˆκ·Έλ¨ΌνŠΈ 셰이더 μ†ŒμŠ€ μ½”λ“œλ₯Ό μ €μž₯ν–ˆμŠ΅λ‹ˆλ‹€. μ›ν•˜λŠ”λŒ€λ‘œ 셰이더 파일의 이름을 자유둭게 지정할 수 μžˆμŠ΅λ‹ˆλ‹€. 개인적으둜 .vs 와 .fs ν™•μž₯μžκ°€ 맀우 직관적이라고 μƒκ°ν•©λ‹ˆλ‹€.

μƒˆλ‘œ μƒμ„±λœ 셰이더 클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ μ—¬κΈ°μ—μ„œ μ†ŒμŠ€ μ½”λ“œλ₯Ό 찾을 수 μžˆμŠ΅λ‹ˆλ‹€. 셰이더 파일 경둜λ₯Ό ν΄λ¦­ν•˜μ—¬ μ…°μ΄λ”μ˜ μ†ŒμŠ€ μ½”λ“œλ₯Ό 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

Exercises

  1. μ‚Όκ°ν˜•μ΄ 거꾸둜 λ˜λ„λ‘ λ²„ν…μŠ€ 셰이더λ₯Ό μ‘°μ •ν•©λ‹ˆλ‹€: μ •λ‹΅
  2. uniform을 톡해 μˆ˜ν‰ μ˜€ν”„μ…‹μ„ μ €μž₯ν•˜κ³ , 이 μ˜€ν”„μ…‹ 값을 μ‚¬μš©ν•˜μ—¬ λ²„ν…μŠ€ μ…°μ΄λ”μ—μ„œ ν™”λ©΄ 였λ₯Έμͺ½μœΌλ‘œ μ‚Όκ°ν˜•μ„ μ΄λ™ν•©λ‹ˆλ‹€.: μ •λ‹΅
  3. out ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 꼭지점 μœ„μΉ˜λ₯Ό ν”„λ ˆκ·Έλ¨ΌνŠΈ μ…°μ΄λ”λ‘œ λ„˜κ²¨μ£Όκ³  ν”„λ ˆκ·Έλ¨ΌνŠΈμ˜ 색상을 꼭지점 μœ„μΉ˜μ™€ λ™μΌν•˜κ²Œ μ„€μ •ν•©λ‹ˆλ‹€ (λ²„ν…μŠ€ μœ„μΉ˜ 값이 μ‚Όκ°ν˜•μ„ κ°€λ‘œμ§ˆλŸ¬ λ³΄κ°„λ˜λŠ” 방식 μ°Έκ³ ). μ΄λ ‡κ²Œν•˜λ©΄ μ™œ μ‚Όκ°ν˜•μ˜ μ™Όμͺ½ μ•„λž˜ 뢀뢄이 κ²€μ€μƒ‰μΌκΉŒμš”? : μ •λ‹΅
⚠️ **GitHub.com Fallback** ⚠️