티스토리 뷰
렌더 텍스처(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이 필요합니다.
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