티스토리 뷰

Unity/Shader Graph

Render Texture

연홍 2021. 1. 14. 15:18
728x90

렌더 텍스처(Render Textures) 는 특수한 타입의 텍스처 로 런타임 시점에 생성되고 업데이트됩니다. 렌더 텍스처를 사용하려면 먼저 새 렌더 텍스처를 생성하고 카메라 하나를 지정해서 렌더링합니다. 그러면 일반 텍스처와 마찬가지로 머티리얼 에서 렌더 텍스처를 사용할 수 있습니다.

(Assets 폴더에서 마우스오른쪽 클릭으로 Create > RenderTexture로 생성 혹은 스크립트로 생성 할 수 있음)

var rt =new RenderTexture(256, 256, 16, RenderTextureFormat.ARGB32);
Destroy(rt); // 사용하지 않을때 제거해야함.

생성된 렌더텍스처를 보면 크기/색상/형식/mipmap 활성화 등의 옵션이 있는데 이는 렌더텍스처에 필요한 메모리에 영향을 미치며 그 정보는 아래쪽 preview에서 볼수 있습니다.

카메라를 사용하여 텍스처를 렌더링하므로 각 오브젝트별 레이어를 두어서 특정 레이어만 렌더링할 수 있습니다.

쉐이더나 쉐이더그래프에서는 일반 텍스쳐와 마찬가지로 사용할 수 있습니다. (Texture2D)

사용예

  • 모니터에 표시되는 보안카메라
  • 거울, 포탈
  • 파티클을 렌더텍스처로 렌더링하여 눈이나 모래/진흑에 흔적을 남기거나 잔디와 상호작용을 하거나 물에 잔물결을 추가하는것과같은 다양한 상호작용효과

Color Format

렌더 텍스처에는 색상 형식에 대한 몇 가지 옵션이 있으며 일부는 '호환 가능한 색상 형식 활성화'가 유용한 대상 플랫폼에서 지원되지 않을 수 있습니다. 형식 이름이 R8G8B8A8_UNORM 인 경우 이는 렌더 텍스처에 Red , Green , Blue 및 Alpha에 대한 채널이 있으며 각각 8 비트 (총32비트)가 있음을 의미합니다. 모든 채널이 필요하지 않은 경우 메모리를 절약하기 위해 더 적은 형식을 사용하세요. 예를 들어 R8_UNORM 에는 빨간색 채널 만 있습니다. 채널당 8 비트를 사용하면 16 비트 보다 적은 메모리를 사용 하거나채널당 32 비트이지만 사용 가능한 색상 수가 줄어 듭니다.

 

형식의 뒷부분의 의미는 다음과 같습니다. (메뉴얼)

  • SRGB = sRGB 비선형 인코딩을 사용하는 부호없는 정규화 된 형식. (알파 채널도있는 경우 선형의 부호없는 정규화 된 값이됩니다.)
  • UNORM = 부호없는 정규화 된 형식. (출력은 0과 1 사이 입니다. )
  • SNORM = 부호있는 정규화 된 형식. (출력은 -1과 1 사이 입니다.)
  • UINT = 부호없는 정수 형식. (출력은 0과 2 ^ (n-1) 사이입니다. 여기서 n은 채널에있는 비트 수입니다.)
  • SINT = 부호있는 정수 형식. (출력은 -2^(n-1)과 2^(n-1) 사이입니다.)
  • UFLOAT = 부호없는 부동 소수점 형식.
  • SFLOAT = 부호있는 부동 소수점 형식.
  • PACKn = 형식이 n 비트의 기본 형식으로 압축됩니다.

HDR 색상 지원을 위해서는 16 비트 또는 32 비트 SFLOAT 형식 을 사용해야한다고 생각 합니다. 렌더 텍스처는 음수 값도 지원할 수 있으며 부호있는 형식 중 하나를 사용합니다.

CPU에서 렌더텍스처 읽기

플레이어가 RenderTexture를 기반으로하는 물과 상호 작용할 수 있고 플레이어가 물에 있을 때만 파티클을 스폰 할 수 있고 다른 효과와 함께 싶었습니다. 물이있는 곳을 찾으려면 CPU에서 렌더 텍스처 데이터를 얻는 방법이 필요합니다.

 

이를 위해 Texture2D를 만들고 Texture2D.ReadPixels 함수를 사용 하여 활성 렌더링 텍스처를 CPU에 복사 할 수 있습니다 . 그러나 이것은 매우 느린 방법이며 데이터 획득이 완료 될 때까지 CPU가 멈출 수 있으므로 신중히 사용해야 합니다. 더 나은 접근 방식은 대신 AsyncGPUReadback.Request 를 사용 하는 것입니다. 이렇게하면 성능 향상을 위해 CPU가 지연되지 않지만 몇 프레임의 지연 시간이 발생할 수 있습니다. 아래 스크립트는 사용하는 방법의 예를 보여줍니다.

using System.Collections.Generic;
using UnityEngine.Rendering;
using Unity.Collections;
using UnityEngine;
 
public class RenderTextureGPURequest : MonoBehaviour {
 
    public RenderTexture renderTexture;
     
    private Queue<AsyncGPUReadbackRequest> requests
        = new Queue<AsyncGPUReadbackRequest>();
 
    private float t;
    private float timeBetweenRequests = 0.2f;
     
    void Update() {
        // Handle Request Queue
        while (requests.Count > 0) {
            // Get the first Request in the Queue
            AsyncGPUReadbackRequest request = requests.Peek();
 
            if (request.hasError) {
                // Error!
                Debug.LogWarning("AsyncGPUReadbackRequest Error! :(");
                requests.Dequeue(); // Remove from Queue
            } else if (request.done) {
                // Request is done, Obtain data!
                NativeArray<Color32> data = request.GetData<Color32>();
                // RGBA32 -> use Color32
                // RGBAFloat -> use Color
                // else, you may have to use the raw byte array:
                // NativeArray<byte> data = request.GetData<byte>();
                 
                // Do something with the data
                if (data.Length <= 0) {
                    // No data?
                } else if (data.Length == 1) {
                    // Single Pixel
                    // Note, we don't know the coords of the pixel obtained
                    // If you want this information, consider wrapping the
                    // AsyncGPUReadbackRequest object in a custom class.
                } else {
                    // Full Image
                }
                requests.Dequeue(); // Remove from Queue
            } else {
                // Request is still processing.
                break;
            }
            // Note : We have to Dequeue items or break,
            // or we'll be caught in an infinite loop!
        }
 
        // Handle Request Timer
        t += Time.deltaTime;
        if (t > timeBetweenRequests) {
            t = 0;
            RequestScreen();
            //RequestPixel(0, 0); 
            // Note that 0,0 is in the bottom left corner
            // of the Render Texture
        }
    }
     
    private void RequestScreen() {
        AsyncGPUReadbackRequest rq = AsyncGPUReadback.Request(
            renderTexture
        );
        requests.Enqueue(rq);
    }
 
    private void RequestPixel(int x, int y) {
        if (x < 0 || x >= renderTexture.width || 
            y < 0 || y > renderTexture.height) {
            // Pixel out of the render texture bounds!
            return;
        }
 
        AsyncGPUReadbackRequest rq = AsyncGPUReadback.Request(
            renderTexture, // Render Texture
            0, // Mip Map level
            Mathf.RoundToInt(x), // x
            1, // Width (1 as we want a single pixel)
            Mathf.RoundToInt(y), // y
            1, // Height (1 as we want a single pixel)
            0, // z
            1, // Depth
            TextureFormat.RGBA32); // Format
        // I believe this should reflect the Color Format the render texture has,
        // 8 bits per channel = RGBA32 (or R8, RG16, RGB24)
        // 16 bits per channel = RGBAHalf (or RHalf, RGHalf, RGBHalf)
        // 32 bits per channel = RGBAFloat (or RFloat, RGFloat, RGBFloat)
        // Note that not all Color Formats are supported by AsyncGPUReadback
        // Some will return errors/warnings.
        // UNORM seems to be supported, but SNORM returns errors.
        // If you need negative values, use a Half or Float format
        // I recommend using RGBA32 or RGBAFloat, as you can retrieve
        // the data as a NativeArray of Color32 or Color objects respectively.
 
        requests.Enqueue(rq);
    }
}

위의 예에서 Queue 및 루프를 사용하는 대신 콜백매개변수와 함께 AsyncGPUReadback.Request를 사용하여 요청이 완료 될 때 발생하는 작업을 트리거 할 수도 있습니다. 예를 보려면 여기를 참조하세요.


RequestPixel과 유사한 방법을 사용하고 너비와 높이를 설정하여 화면에서 특정 사각형을 요청할 수 있습니다. request.width 및 request.height를 사용하여 요청이 완료된 후 다시 가져와 데이터를 올바르게 분할/처리 할 수 있습니다. (더 적은 픽셀을 사용한다고 더 리소스를 적게 먹는지는 모르겠음.)

 

request의 x및 y좌료를 다시 가져올 수는 없는것 같지만 사용자 정의클래스에서 request를 래핑하여 데이터를 보관할 변수를 추가하고 해당 클래스를 대신 뷰에 추가할 수 있습니다. (목적은 렌더텍스처가 plane에 있는 플레이어의 위치 픽셀 색상이 필요했음.)

float scale = 512f / 10f;
Vector3 position = player.transform.position;
float x = position.x * scale + 512f / 2f;
float y = position.z * scale + 512f / 2f;
RequestPixel(x, y);

렌더 텍스처의 너비와 높이가 512이고 렌더링되는 평면이 원점을 중심으로하는 10 world space units에 걸쳐 있다고 가정하므로 오프셋 256이 필요합니다.

 

twitter.com/Ed_dV/status/1072458319590240257?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1072458319590240257%7Ctwgr%5E%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fcyangamedev.wordpress.com%2F2019%2F07%2F08%2Frender-textures%2F

 

Edward del Villar on Twitter

“My new favourite way of doing visual environment interaction: Top-down camera writes a global render texture. Using particles here for water ripples and mud trails, but lots of other approaches (trail renderers, depth comparisons, shader replacement etc

twitter.com

 

 

'Unity > Shader Graph' 카테고리의 다른 글

Screen Position  (0) 2021.01.15
Voronoi  (0) 2021.01.15
Vertex Displacement  (0) 2021.01.14
Fresnel Effect  (0) 2021.01.13
Scene Color & Scene Depth  (0) 2021.01.13
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
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