Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

당신의 첫 게임

높은 수준에서 Godot가 하는 일은 사용자에게 선택적으로 설정할 수 있는 여러 매개변수(AO, SSS_Strength, RIM 등)를 제공하는 것입니다. 이러한 매개변수는 다양한 복잡한 효과(주변 폐색, 하위 표면 산란, 테두리 조명 등)에 해당합니다. 작성되지 않은 경우 코드는 컴파일되기 전에 폐기되므로 셰이더에는 추가 기능 비용이 발생하지 않습니다. 이를 통해 사용자는 복잡한 셰이더를 작성하지 않고도 복잡한 PBR 올바른 셰이딩을 쉽게 가질 수 있습니다. 물론 Godot를 사용하면 이러한 모든 매개변수를 무시하고 완전히 사용자 정의된 셰이더를 작성할 수도 있습니다.

이러한 매개변수의 전체 목록은 spatial 셰이더 참조 문서를 참조하세요.

정점 함수와 조각 함수의 차이점은 정점 함수는 정점별로 실행되고 VERTEX``(위치) ``NORMAL``와 같은 속성을 설정하는 반면 조각 셰이더는 픽셀별로 실행되며 가장 중요하게는 :ref:`MeshInstance3D<class_MeshInstance3D>`의 ``ALBEDO 색상을 설정한다는 것입니다.

당신의 첫 공간 프래그먼트 함수

이 튜토리얼의 이전 부분에서 언급했듯이. Godot에서 조각 기능의 표준 사용은 다양한 물질 속성을 설정하고 나머지는 Godot가 처리하도록 하는 것입니다. 더 많은 유연성을 제공하기 위해 Godot는 렌더링 모드라는 기능도 제공합니다. 렌더링 모드는 shader_type 바로 아래의 셰이더 상단에 설정되며 셰이더의 내장 측면에 원하는 기능의 종류를 지정합니다.

예를 들어 조명이 객체에 영향을 주지 않도록 하려면 렌더링 모드를 ``unshaded``로 설정합니다.

render_mode unshaded;

여러 렌더링 모드를 함께 쌓을 수도 있습니다. 예를 들어 보다 사실적인 PBR 셰이딩 대신 툰 셰이딩을 사용하려면 확산 모드와 반사 모드를 toon으로 설정하세요.

render_mode diffuse_toon, specular_toon;

이 내장 기능 모델을 사용하면 몇 가지 매개변수만 변경하여 복잡한 사용자 정의 셰이더를 작성할 수 있습니다.

Spatial 셰이더

튜토리얼의 이 부분에서는 이전 부분의 울퉁불퉁한 지형을 가져와 바다로 바꾸는 방법을 살펴보겠습니다.

먼저 물의 색을 설정해 봅시다. ``ALBEDO``를 설정하여 이를 수행합니다.

``ALBEDO``는 객체의 색상을 포함하는 ``vec3``입니다.

멋진 파란색 음영으로 설정해 보겠습니다.

void fragment() {
  ALBEDO = vec3(0.1, 0.3, 0.5);
}
../../../_images/albedo.png

물의 푸른색 대부분은 하늘의 반사에서 나오기 때문에 우리는 그것을 매우 어두운 파란색 음영으로 설정했습니다.

Godot가 사용하는 PBR 모델은 METALLIC``ROUGHNESS``라는 두 가지 주요 매개변수에 의존합니다.

``ROUGHNESS``는 재료 표면의 매끄러움/거친 정도를 지정합니다. ``ROUGHNESS``가 낮으면 재질이 반짝이는 플라스틱처럼 보이고, 거칠기가 높으면 재질이 색상이 더 단단해 보입니다.

METALLIC``는 물체가 얼마나 금속과 유사한지를 지정합니다. ``0 또는 1``에 가깝게 설정하는 것이 좋습니다. ``METALLIC``를 반사와 ``ALBEDO 색상 간의 균형을 변경하는 것으로 생각하십시오. 높은 METALLIC``는 ``ALBEDO``를 거의 무시하고 하늘의 거울처럼 보입니다. 낮은 ``METALLIC``는 하늘색과 ``ALBEDO 색상을 더 동일하게 표현합니다.

``ROUGHNESS``는 왼쪽에서 오른쪽으로 ``0``에서 ``1``로 증가하고, ``METALLIC``는 위에서 아래로 ``0``에서 ``1``로 증가합니다.

../../../_images/PBR.png

참고

적절한 PBR 셰이딩을 위해서는 METALLIC``가 ``0 또는 ``1``에 가까워야 합니다. 재료 간 블렌딩을 위해 그 사이에만 설정하십시오.

물은 금속이 아니므로 METALLIC 속성을 0.0``로 설정합니다. 물은 반사율도 높으므로 ``ROUGHNESS 속성도 매우 낮게 설정하겠습니다.

void fragment() {
  METALLIC = 0.0;
  ROUGHNESS = 0.01;
  ALBEDO = vec3(0.1, 0.3, 0.5);
}
../../../_images/plastic.png

이제 우리는 매끄러운 플라스틱처럼 보이는 표면을 갖게 되었습니다. 이제 우리가 모방하고 싶은 물의 특정 특성에 대해 생각해 볼 때입니다. 이것을 이상한 플라스틱 표면에서 멋진 양식화된 물로 바꾸는 두 가지 주요 방법이 있습니다. 첫 번째는 정반사입니다. 반사광은 태양이 눈에 직접 반사되는 곳에서 볼 수 있는 밝은 점입니다. 두 번째는 프레넬 반사율입니다. 프레넬 반사율은 얕은 각도에서 반사율이 높아지는 객체의 속성입니다. 이것이 바로 아래의 물을 볼 수 있지만, 더 멀리 떨어진 곳에는 하늘이 반사되는 이유입니다.

정반사를 증가시키기 위해 우리는 두 가지 일을 할 것입니다. 먼저 Toon 렌더링 모드에는 반사광 하이라이트가 더 크기 때문에 반사광에 대한 렌더링 모드를 Toon으로 변경합니다.

render_mode specular_toon;
../../../_images/specular-toon.png

두 번째로 림 조명을 추가하겠습니다. 림 조명은 바라보는 각도에서 빛의 효과를 증가시킵니다. 일반적으로 빛이 물체의 가장자리에 있는 직물을 통과하는 방식을 에뮬레이션하는 데 사용되지만 여기서는 멋진 물 효과를 얻기 위해 이를 사용합니다.

void fragment() {
  RIM = 0.2;
  METALLIC = 0.0;
  ROUGHNESS = 0.01;
  ALBEDO = vec3(0.1, 0.3, 0.5);
}
../../../_images/rim.png

프레넬 반사율을 추가하기 위해 조각 셰이더에서 프레넬 항을 계산합니다. 여기서는 성능상의 이유로 실제 프레넬 용어를 사용하지 않습니다. 대신 NORMALVIEW 벡터의 내적을 사용하여 근사화하겠습니다. NORMAL 벡터는 메시 표면에서 먼 쪽을 가리키는 반면, VIEW 벡터는 눈과 표면의 해당 지점 사이의 방향입니다. 이들 사이의 내적은 표면을 정면으로 보고 있는지 또는 비스듬히 바라보고 있는지를 알 수 있는 편리한 방법입니다.

float fresnel = sqrt(1.0 - dot(NORMAL, VIEW));

그리고 ``ROUGHNESS``와 ``ALBEDO``에 섞어보세요. 이는 StandardMaterial3D에 비해 ShaderMaterials의 이점입니다. StandardMaterial3D를 사용하면 이러한 속성을 텍스처나 단순 숫자로 설정할 수 있습니다. 그러나 셰이더를 사용하면 우리가 꿈꾸는 수학적 함수를 기반으로 설정할 수 있습니다.

void fragment() {
  float fresnel = sqrt(1.0 - dot(NORMAL, VIEW));
  RIM = 0.2;
  METALLIC = 0.0;
  ROUGHNESS = 0.01 * (1.0 - fresnel);
  ALBEDO = vec3(0.1, 0.3, 0.5) + (0.1 * fresnel);
}
../../../_images/fresnel.png

이제 단 5줄의 코드만으로 복잡해 보이는 물을 만들 수 있습니다. 이제 조명이 있으므로 이 물은 너무 밝아 보입니다. 어둡게 해보자. 이는 ``ALBEDO``에 전달하는 ``vec3``의 값을 줄임으로써 쉽게 수행됩니다. ``vec3(0.01, 0.03, 0.05)``로 설정해 보겠습니다.

../../../_images/dark-water.png

``TIME``로 애니메이션

정점 함수로 돌아가서 내장 변수 ``TIME``를 사용하여 파도에 애니메이션을 적용할 수 있습니다.

``TIME``는 정점 및 조각 함수에서 액세스할 수 있는 내장 변수입니다.

지난 튜토리얼에서는 하이트맵을 읽어 높이를 계산했습니다. 이 튜토리얼에서도 동일한 작업을 수행합니다. ``height()``라는 함수에 하이트맵 코드를 넣으세요.

float height(vec2 position) {
  return texture(noise, position / 10.0).x; // Scaling factor is based on mesh size (this PlaneMesh is 10×10).
}

height() 함수에서 ``TIME``를 사용하려면 이를 전달해야 합니다.

float height(vec2 position, float time) {
}

그리고 꼭지점 함수 내부에 올바르게 전달했는지 확인하세요.

void vertex() {
  vec2 pos = VERTEX.xz;
  float k = height(pos, TIME);
  VERTEX.y = k;
}

법선을 계산하기 위해 법선 맵을 사용하는 대신. vertex() 함수에서 수동으로 계산하겠습니다. 이렇게 하려면 다음 코드 줄을 사용하십시오.

NORMAL = normalize(vec3(k - height(pos + vec2(0.1, 0.0), TIME), 0.1, k - height(pos + vec2(0.0, 0.1), TIME)));

다음 섹션에서는 수학을 사용하여 복잡해 보이는 파동을 생성하므로 ``NORMAL``를 수동으로 계산해야 합니다.

이제 position``를 ``TIME``의 코사인으로 오프셋하여 ``height() 함수를 좀 더 복잡하게 만들어 보겠습니다.

float height(vec2 position, float time) {
  vec2 offset = 0.01 * cos(position + time);
  return texture(noise, (position / 10.0) - offset).x;
}

이로 인해 파도가 천천히 움직이지만 매우 자연스러운 방식은 아닙니다. 다음 섹션에서는 셰이더를 사용하여 몇 가지 수학적 함수를 더 추가하여 더 복잡한 효과(이 경우 사실적인 파도)를 만드는 방법에 대해 자세히 알아볼 것입니다.

고급 벡터 연산

셰이더가 강력한 이유는 수학을 사용하여 복잡한 효과를 얻을 수 있다는 것입니다. 이를 설명하기 위해 height() 기능을 수정하고 ``wave()``라는 새로운 기능을 도입하여 웨이브를 다음 단계로 끌어올릴 것입니다.

``wave()``에는 하나의 매개변수 ``position``가 있으며 이는 ``height()``와 동일합니다.

파도가 보이는 방식을 가짜로 만들기 위해 ``height()``에서 ``wave()``를 여러 번 호출할 것입니다.

float wave(vec2 position){
  position += texture(noise, position / 10.0).x * 2.0 - 1.0;
  vec2 wv = 1.0 - abs(sin(position));
  return pow(1.0 - pow(wv.x * wv.y, 0.65), 4.0);
}

처음에는 복잡해 보입니다. 그럼 한 줄씩 살펴보도록 하겠습니다.

position += texture(noise, position / 10.0).x * 2.0 - 1.0;

noise 텍스처로 위치를 오프셋합니다. 이렇게 하면 파도가 곡선이 되어 그리드와 완전히 정렬된 직선이 되지 않습니다.

vec2 wv = 1.0 - abs(sin(position));

sin()position``를 사용하여 파도와 같은 함수를 정의합니다. 일반적으로 ``sin() 파도는 매우 둥글다. ``abs()``를 절대값으로 사용하여 날카로운 능선을 제공하고 0-1 범위로 제한합니다. 그런 다음 ``1.0``에서 이를 빼서 피크를 맨 위에 놓습니다.

return pow(1.0 - pow(wv.x * wv.y, 0.65), 4.0);

x 방향 파동과 y 방향 파동을 곱하고 제곱하여 피크를 날카롭게 만듭니다. 그런 다음 능선이 정점이 되도록 ``1.0``에서 빼서 능선을 날카롭게 하는 거듭제곱으로 올립니다.

이제 height() 함수의 내용을 ``wave()``로 바꿀 수 있습니다.

float height(vec2 position, float time) {
  float h = wave(position);
  return h;
}

이 가이드는 다음을 배웁니다:

../../../_images/wave1.png

사인파의 모양이 너무 뚜렷합니다. 그럼 파도를 좀 더 널리 퍼뜨려 볼까요? ``position``를 확장하여 이를 수행합니다.

float height(vec2 position, float time) {
  float h = wave(position * 0.4);
  return h;
}

이제 훨씬 좋아 보입니다.

../../../_images/wave2.png

다양한 주파수와 진폭으로 여러 파동을 서로 겹쳐 놓으면 더 나은 결과를 얻을 수 있습니다. 이것이 의미하는 바는 파도를 더 얇게 또는 더 넓게(주파수) 만들기 위해 각각의 위치를 조정한다는 것입니다. 그리고 우리는 파동의 출력을 곱하여 파동을 더 짧게 또는 더 크게(진폭) 만들 것입니다.

다음은 더 멋진 파도를 얻기 위해 4개의 파도를 겹쳐서 만드는 방법에 대한 예입니다.

float height(vec2 position, float time) {
  float d = wave((position + time) * 0.4) * 0.3;
  d += wave((position - time) * 0.3) * 0.3;
  d += wave((position + time) * 0.5) * 0.2;
  d += wave((position - time) * 0.6) * 0.2;
  return d;
}

두 개에 시간을 더하고 다른 두 개에서 뺍니다. 이로 인해 파도가 다른 방향으로 이동하여 복잡한 효과가 생성됩니다. 또한 진폭(결과에 곱한 수)을 모두 더하면 ``1.0``가 됩니다. 이렇게 하면 파동이 0-1 범위로 유지됩니다.

이 코드를 사용하면 더 복잡해 보이는 파도가 나타나며 약간의 수학을 추가하기만 하면 됩니다!

../../../_images/wave3.png

Spatial 셰이더에 대한 자세한 내용은 Shading Language 문서와 Spatial 셰이더 문서를 읽어보세요. 또한 Shading 섹션3D 섹션에서 고급 튜토리얼을 살펴보세요.