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.

화면 읽기 셰이더

소개

쓰고 있는 화면과 동일한 화면에서 읽는 셰이더를 만드는 것이 종종 바람직합니다. OpenGL이나 DirectX와 같은 3D API는 내부 하드웨어 제한으로 인해 이를 매우 어렵게 만듭니다. GPU는 극도로 병렬적이므로 읽기 및 쓰기로 인해 모든 종류의 캐시 및 일관성 문제가 발생합니다. 결과적으로 최신 하드웨어조차도 이를 제대로 지원하지 않습니다.

해결 방법은 화면의 복사본이나 화면의 일부를 백 버퍼에 만든 다음 그리는 동안 읽는 것입니다. Godot는 이 과정을 쉽게 만들어주는 몇 가지 도구를 제공합니다.

화면 텍스처

Godot doc_shading_language`에는 이미 렌더링된 화면 콘텐츠에 접근하기 위한 특별한 텍스처가 있습니다. ``sampler2D` 유니폼을 선언할 때 힌트를 지정하여 사용됩니다: hint_screen_texture. 특수 내장형 가변 ``SCREEN_UV``를 사용하여 현재 조각의 화면을 기준으로 UV를 얻을 수 있습니다. 결과적으로 이 canvas_item 조각 셰이더는 뒤에 있는 내용만 보여주기 때문에 보이지 않는 개체가 됩니다.

shader_type canvas_item;

uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;

void fragment() {
    COLOR = textureLod(screen_texture, SCREEN_UV, 0.0);
}

여기서는 하단 밉맵에서만 읽으려고 하기 때문에 ``textureLod``가 사용되었습니다. 대신 텍스처의 흐린 버전에서 읽으려면 세 번째 인수를 ``textureLod``로 늘리고 힌트 ``filter_nearest``를 ``filter_nearest_mipmap``(또는 밉맵이 활성화된 다른 필터)로 변경할 수 있습니다. 밉맵과 함께 필터를 사용하는 경우 Godot는 흐릿한 텍스처를 자동으로 계산합니다.

경고

필터 모드가 이름에 mipmap``를 포함하는 필터 모드로 변경되지 않으면 ``0.0``보다 LOD 매개변수를 가진 ``textureLod``는 ``0.0 LOD 매개변수와 동일한 모양을 갖게 됩니다.

몇 가지 예제:

화면 텍스처는 다양한 용도로 사용될 수 있습니다. *Screen Space 셰이더*에 대한 특별 데모를 다운로드하여 보고 배울 수 있습니다. 한 가지 예는 밝기, 대비 및 채도를 조정하는 간단한 셰이더입니다.

shader_type canvas_item;

uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;

uniform float brightness = 1.0;
uniform float contrast = 1.0;
uniform float saturation = 1.0;

void fragment() {
    vec3 c = textureLod(screen_texture, SCREEN_UV, 0.0).rgb;

    c.rgb = mix(vec3(0.0), c.rgb, brightness);
    c.rgb = mix(vec3(0.5), c.rgb, contrast);
    c.rgb = mix(vec3(dot(vec3(1.0), c.rgb) * 0.33333), c.rgb, saturation);

    COLOR.rgb = c;
}

비하인드 스토리

이것이 마술처럼 보이지만 그렇지 않습니다. 2D에서, 그려지려는 노드에서 ``hint_screen_texture``가 처음 발견되면 Godot는 백 버퍼에 전체 화면 복사를 합니다. 셰이더에서 이를 사용하는 후속 노드에는 화면이 복사되지 않습니다. 이는 결국 비효율적이기 때문입니다. 3D에서는 불투명 지오메트리 패스 이후에 화면이 복사되지만 투명 지오메트리 패스 전에는 투명 객체가 화면 텍스처에 캡처되지 않습니다.

결과적으로 2D에서 ``hint_screen_texture``를 사용하는 셰이더가 겹치면 두 번째는 첫 번째 결과를 사용하지 않아 예상치 못한 시각적 결과가 발생합니다.

../../_images/texscreen_demo1.png

위 이미지에서 두 번째 구(오른쪽 상단)는 아래 첫 번째 것과 동일한 화면 텍스처 소스를 사용하므로 첫 번째 구는 "사라지거나" 표시되지 않습니다.

2D에서는 BackBufferCopy 노드를 통해 이 문제를 수정할 수 있으며, 이는 두 구 간에 인스턴스화될 수 있습니다. BackBufferCopy는 화면 영역이나 전체 화면을 지정하여 작동할 수 있습니다.

../../_images/texscreen_bbc.png

올바른 백버퍼 복사를 사용하면 두 구가 올바르게 혼합됩니다.

../../_images/texscreen_demo2.png

경고

3D에서 ``hint_screen_texture``를 사용하는 재질은 그 자체로 투명한 것으로 간주되어 다른 재질의 결과 화면 텍스처에 나타나지 않습니다. ``hint_screen_texture``가 포함된 재질을 사용하는 씬을 인스턴스화하려는 경우 BackBufferCopy 노드를 사용해야 합니다.

3D에서는 화면 텍스처가 한 번만 캡처되므로 이 특정 문제를 해결하기 위한 유연성이 떨어집니다. 3D에서 화면 텍스처를 사용할 때는 투명한 개체를 캡처하지 않으며 화면 텍스처를 사용하여 개체 앞에 있는 일부 불투명 개체를 캡처할 수 있으므로 주의하세요.

개체와 동일한 위치에 카메라가 있는 뷰포트 텍스처를 사용하여 백 버퍼 로직을 3D로 재현할 수 있습니다.

백버퍼 로직

그래서 더 명확하게 하기 위해 Godot의 2D에서 백버퍼 복사 논리가 작동하는 방법은 다음과 같습니다:

  • 노드가 ``hint_screen_texture``를 사용하는 경우 해당 노드를 그리기 전에 전체 화면이 백 버퍼에 복사됩니다. 이는 처음에만 발생합니다. 후속 노드는 이를 트리거하지 않습니다.

  • 위 지점의 상황 이전에 BackBufferCopy 노드가 처리된 경우(``hint_screen_texture``를 사용하지 않은 경우에도) 위 지점에서 설명한 동작이 발생하지 않습니다. 즉, 전체 화면의 자동 복사는 ``hint_screen_texture``가 노드에서 처음으로 사용되고 트리 순서에서 이전에 BackBufferCopy 노드(비활성화되지 않음)가 발견되지 않은 경우에만 발생합니다.

  • BackBufferCopy는 전체 화면이나 영역을 복사할 수 있습니다. 전체 화면이 아닌 영역으로만 설정되고 셰이더가 복사된 영역에 없는 픽셀을 사용하는 경우 해당 읽기 결과는 정의되지 않습니다(이전 프레임의 가비지일 가능성이 높음). 즉, BackBufferCopy를 사용하여 화면 영역을 다시 복사한 다음 다른 영역에서 화면 텍스처를 읽을 수 있습니다. 이런 행동을 피하세요!

깊이 텍스처

3D 셰이더의 경우 화면 깊이 버퍼에 액세스하는 것도 가능합니다. 이를 위해 hint_depth_texture 힌트가 사용됩니다. 이 텍스처는 선형이 아닙니다. 역투영 행렬을 사용하여 변환해야 합니다.

다음 코드는 그려지는 픽셀 아래의 3D 위치를 검색합니다.

uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;

void fragment() {
    float depth = textureLod(depth_texture, SCREEN_UV, 0.0).r;
    vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth, 1.0);
    vec3 pixel_position = upos.xyz / upos.w;
}

보통 거칠기 텍스처

참고

Normal-roughness 텍스처는 Forward+ 렌더링 방법에서만 지원되며 모바일 또는 호환성에서는 지원되지 않습니다.

마찬가지로, 법선 거칠기 텍스처를 사용하여 깊이 프리패스에서 렌더링된 객체의 법선과 거칠기를 읽을 수 있습니다. 법선은 .xyz 채널(0-1 범위에 매핑됨)에 저장되고 거칠기는 .w 채널에 저장됩니다.

uniform sampler2D normal_roughness_texture : hint_normal_roughness_texture, repeat_disable, filter_nearest;

void fragment() {
    float screen_roughness = texture(normal_roughness_texture, SCREEN_UV).w;
    vec3 screen_normal = texture(normal_roughness_texture, SCREEN_UV).xyz;
    screen_normal = screen_normal * 2.0 - 1.0;

텍스처 가져오기

화면 텍스처 힌트(hint_screen_texture, hint_depth_texturehint_normal_roughness_texture)는 여러 유니폼에 사용할 수 있습니다. 예를 들어, 다른 반복 플래그나 필터 플래그를 사용하여 텍스처에서 여러 번 읽을 수 있습니다.

다음 예에서는 선형 필터링을 사용하여 화면 공간 법선을 읽지만 최근접 이웃 필터링을 사용하여 화면 공간 거칠기를 읽는 셰이더를 보여줍니다.

uniform sampler2D normal_roughness_texture : hint_normal_roughness_texture, repeat_disable, filter_nearest;
uniform sampler2D normal_roughness_texture2 : hint_normal_roughness_texture, repeat_enable, filter_linear;

void fragment() {
    float screen_roughness = texture(normal_roughness_texture, SCREEN_UV).w;
    vec3 screen_normal = texture(normal_roughness_texture2, SCREEN_UV).xyz;
    screen_normal = screen_normal * 2.0 - 1.0;