기본 콘텐츠로 건너뛰기

[Unity] Texture, Shader

    목차

Shader

2019.4 LTS Document : Shader

구문

공통 프로퍼티

https://docs.unity3d.com/kr/2019.4/Manual/SL-Properties.html

Shader Property Id 값 예시 (이 값은 기기마다 다르다)

  • MainTex : 5
  • EnvironmentDepth : 208
  • CurrentDepthTexture : 312
  • UnityDisplayTransform : 372
  • DisplayTransform : 373
  • UnityCameraForwardScale : 376
  • OcclusionBlendingScale : 377

키워드

Rendering Mode

  • Opaque : Default, (불투명) 투명한 영역이 없는 일반 솔리드 오브젝트
  • Cutout :
  • Transparent : 투명한
  • Fade :

Forward rendering
Deferred rendering

  • Albedo : 반사계수

    • 일반적인 색상.
    • 그림자 연산을 하기 때문에 그림자가 생긴다. (Emission과 반대)
    • base (diffuse or specular) color
  • Normal

    • tangent space normal, if written
  • HeightMap
    +

  • Emission, Emissive : 방사, 방출

    • 자체 발광 (조명)
    • 그림자 연산을 하지 않는다. (Albedo와 반대)
  • Smoothness : 부드러움, 평탄, 평활도

    • 0 : rough (거칠다, 빛 난반사, Diffuse)
    • 1 : smooth (부드럽다, 빛 정반사, Specular)
    • Metallic, Specular 모두 사용가능 (텍스처 맵이 할당되지 않았을 경우)
  • Ambient Occulusion

    • 빛의 차폐로 인한 감쇠 근사치를 구하는 이펙트
    • 볼록한 입체는 밝게, 오목하거나 골 파인 곳은 어둡게
      (현실에서도 방 구석이 제일 어둡듯이 구석이나 틈 같은 곳에 효과를 줌)
    • Ambient : 대기, 환경, 주위에, 둘러쌓인
      • Metallic, Specular에도 영향
      • 어두운 부분의 색을 결정
    • Occlusion : 차폐, 폐색
      • default 1
  • Alpha : 투명도

    • 0 : 투명 / 1 : 불투명
  • Metallic : 금속의

    • 0 : 비금속성 / 1 : 금속성
  • Specular : 반사하는, 거울의

    • 광원 직접 반사 (Reflection : 광원 간접 반사)
    • Highlight와 비슷한 의미
  • Diffuse : 확산하다, 퍼지다

    • 물체의 고유색상, 전체적 색상 결정
  • Unlit : 빛이 없는, (전등, 불 등) 켜지지 않은

참고

Shader

Shader Smoothness
Shader Occlusion



Shader type

SurfaceShader

https://docs.unity3d.com/kr/2019.4/Manual/SL-SurfaceShaders.html

광원, 그림자에 큰 영향. Unity에만 있는 녀석(?)이며, Vertex & Fragment Shader를 간략화한 Shader

Vertex & Fragment Shader

광원, 그림자에 영향은 거의 없음

Fixed function Shader

Lagacy shader

Compute Shader

Geometry Shader



Shader code 예시

  1. 가운데 사각형 그리기
Shader "너님이 원하는 경로/이름"
{
    Properties
    {
        _MainTex ("Mask Texture", 2D) = "black" {}
        _TexWidthHalfRatio ("texture width half ratio", Float) = 0.10
        _TexHeightHalfRatio ("texture height half ratio", Float) = 0.15
    }

    // SubShader는 여러개 정의 가능
    // 단, 지원되는 SubShader 중 가장 위의 것이 선택된다.
    SubShader
    {
//        Tags
//        {
//            "RenderType"="Opaque"
//        }


        Pass
        {
            Cull Off
            ZWrite Off                      
            
            CGPROGRAM
            #pragma target 3.5
            #pragma vertex vert // Vertex shader
            #pragma fragment frag // Fragment shader

            #include "UnityCG.cginc"

			//UnityCG.cginc 내부에 선언된 것을 사용해도 된다.
            //중요한 것은 뒷부분에 있는 콜른 뒤에 있는 키워드
            struct appdata
            {
                float2 uv : TEXCOORD0;
                float4 vertex : POSITION;
            };
			
            //Vertex to Fragment
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            //유니티의 머터리얼은 "float4 {텍스쳐이름}_ST" 라는 프로퍼티명으로 Tiling 및 Offset 값을 쉐이더에 전달해준다. 사용하지 않아도 된다.
            //float4 _MainTex_ST;

			//Vertex shader 함수
            v2f vert(appdata input)
            {
                v2f output;
                output.vertex = UnityObjectToClipPos(input.vertex);
                //output.uv = TRANSFORM_TEX(input.uv, _MainTex);
                output.uv = input.uv;

				//Fragment 함수로 간다.
                return output; 
            }

            float _TexWidthHalfRatio;
            float _TexHeightHalfRatio;

            float4 rectangle(v2f IN, float x, float y)
            {
                //float _RectangleSize = 0.02;
              
                // actual rectangle
                float minXOuter = x - _TexWidthHalfRatio;
                float maxXOuter = x + _TexWidthHalfRatio;
                float minYOuter = y - _TexHeightHalfRatio;
                float maxYOuter = y + _TexHeightHalfRatio;
                
                float uvx = IN.uv.x;
                float uvy = IN.uv.y;
                
                float4 col = (minXOuter <= uvx && uvx <= maxXOuter && minYOuter <= uvy && uvy <= maxYOuter)
                    ? float4(1, 1, 1, 1) // 흰색 
                    : float4(0, 0, 0, 1);// 검은색
              
                return col;
            }

			// Fragment shader 함수
            float4 frag(v2f i) : SV_Target
            {
                //fixed4 col = tex2D(_MainTex, i.uv);

                float4 col = rectangle(i, 0.5, 0.5);
                
                // 최종 출력(?)
                return col; 
            }
            ENDCG
        }
    }
    
    FallBack Off
}

참고

Unity documentation

Cg/HLSL에서 셰이더 프로퍼티 액세스


RenderTexture

CCTV나 게임 미니맵을 생각하면 된다.

https://funfunhanblog.tistory.com/209

Camera.targetTexture

null인 경우, 카메라가 화면에 렌더링.
null이 아닌 경우, 카메라는 항상 전체 texture로 렌더링을 한다… (사실상 rect, pixelRect는 무시된다.)

https://docs.unity3d.com/ScriptReference/Camera-targetTexture.html


Texture

  1. Texture2D를 생성한다.
  2. 캡쳐할 영역 지정한 후, ReadPixel 함수를 호출하여 픽셀을 저장한다.
  3. 사용한 텍스처를 메모리에서 해제한다.

//
Texture2D screenTex = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
Rect area = new Rect(0f, 0f, Screen.width, Screen.height);
screenTex.ReadPixels(area, 0, 0);
Destroy(screenTex);

// //////////////////////////////////////
// 카메라 화면을 텍스쳐에 렌더링

//렌더 텍스쳐 생성 
var renderTextureWidth = Screen.width;
var renderTextureHeight = Screen.height;
RenderTexture cameraRenderTexture = new RenderTexture(renderTextureWidth, renderTextureHeight, 0, RenderTextureFormat.RFloat);

// RenderTexture active는 하나만 가능하다고 하니까 이전 것을 백업
var oldActiveRenderTexture = RenderTexture.active;
RenderTexture.active = cameraRenderTexture;
targetCamera.Render();

// 텍스쳐 생성
var outputTexture2D = new Texture2D(renderTextureWidth, renderTextureHeight, TextureFormat.RFloat, false);
outputTexture2D.ReadPixels(new Rect(0, 0, renderTextureWidth, renderTextureHeight), 0, 0);
outputTexture2D.Apply();

//렌더 텍스쳐 활성화 상태 복원
RenderTexture.active = oldActiveRenderTexture;

  • 안드로이드에서 텍스처 포맷 중 R16은 지원하지 않음. 따라서 Android에 빌드를 해야되는 경우 R16은 제외해야됨.

Error Unity Texture creation failed. ‘R16’ is not supported on this platform. Use ‘SystemInfo.SupportsTextureFormat’ C# API to check format support.

반전 (상하)

  • 방법 (1) : 배열 (검증 필요…)

/// <summary>
/// 이미지의 위, 아래를 Swap하는 구조
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="data">이미지 byte 배열, Input 및 Output</param>
/// <param name="bytePixel">GrayScale = 1, RGBA32 = 4</param>
public static void ImageFlipY(int width, int height, byte[] data, int bytePixel = 4)
{
    int rowSize = width * bytePixel;
    byte[] buf = new byte[rowSize];
    int heightHalf = height / 2;

    for (int y = 0; y < heightHalf; y++)
    {
        int y2 = height - y - 1;
        int p1 = y * rowSize;
        int p2 = y2 * rowSize;
        Buffer.BlockCopy(data, p1, buf, 0, rowSize);
        Buffer.BlockCopy(data, p2, data, p1, rowSize);
        Buffer.BlockCopy(buf, 0, data, p2, rowSize);
    }
}
  • 방법 (2)
    카메라의 Projection Matrix에 Matrix4x4.Scale(new Vector3(1, -1, 1))을 곱하면, 카메라는 위아래 반전되어 렌더링된다. 이 상태에서 텍스쳐에 렌더링하면, 위아래 반전된 화면이 텍스쳐로 나온다.

// Projection matrix 변경
camera.projectionMatrix = camera.projectionMatrix * Matrix4x4.Scale(new Vector3(1, -1, 1)) 



반전 (좌우)

검증 필요…

/// <summary>
/// 이미지의 좌,우 반전
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="src"></param>
/// <returns></returns>
public static Color32[] RawImageReverseFlip(byte[] src)
{    
    var srcLength = src.Length;
    var resultArrayLength = srcLength / 4;
    var resultArray = new Color32[resultArrayLength];

    for (int i = 0, k = resultArrayLength - 1; i < resultArrayLength; i++, k--)
    {
        var j = i * 4;
        resultArray[k] = new Color32(src[j], src[j + 1], src[j + 2], src[j + 3]);
    }

    return resultArray;
}

변환 (RGBA32 → RGB24)

// byte array는 texture에서 GetRawTextureData로 가져오면 된다.

// 방법 1 : 알파에 해당되는 부분만 제외하고 다른 배열에 저장 
int frameRgba32ByteArrayLength = frameRgba32ByteArray.Length;
var rgb24ByteArray = new byte[frameRgba32ByteArrayLength * 3 / 4];

for (int i = 0, j = 0; i < frameRgba32ByteArrayLength; i += 4, j += 3)
{
    rgb24ByteArray[j] = frameRgba32ByteArray[i];
    rgb24ByteArray[j + 1] = frameRgba32ByteArray[i + 1];
    rgb24ByteArray[j + 2] = frameRgba32ByteArray[i + 2];
}

// 방법 2 : Parallel (필요 없는 작업도 해서 속도는 그닥...)
// 4배수 인덱스를 찾아서 알파에 해당되는 부분만 제외하고 다른 배열에 저장
Parallel.ForEach(Frame_RGBA32_ByteArray, (element, state, i) =>
{
    if (i % 4 != 0)
        return;

    var j = i * 3 / 4;
    rgb24ByteArray[j] = Frame_RGBA32_ByteArray[i]; // 0
    rgb24ByteArray[j + 1] = Frame_RGBA32_ByteArray[i + 1]; // 1
    rgb24ByteArray[j + 2] = Frame_RGBA32_ByteArray[i + 2]; // 2
});

//작업이 끝난후 LoadRawTextureData 함수를 부르면 된다.

// 방법 3 : Pixel 데이터를 가져와 복사
Texture2D resultRGB24Texture = new Texture2D(width, height, TextureFormat.RGB24, false);
var colorArray = RGBA32Texture.GetPixels();
resultRGB24Texture.SetPixels(colorArray);
resultRGB24Texture.Apply();

참고

댓글