유니티 6가 나온것을 기점으로 다시 리마인드를 위해 다른사람들의 강좌를 따라하면서 기초 기법등을 다시 연습해볼 생각이다.
필요 프로그램 유니티 6 (60000.0.30f1)
참조 강좌 (레트로, 유니티 공식 블로그) - 블로그들은 URP에 대하여, 레트로님의 강좌는 SRP에 대하여 설명하고 있음
아래 비교글은 ChatGPT로 작성
1. SRP (Scriptable Render Pipeline)
- 정의: SRP는 Unity에서 사용자 정의 가능한 렌더링 파이프라인의 기본 프레임워크입니다. Unity 개발자가 특정 렌더링 요구 사항에 맞는 파이프라인을 스크립트로 직접 설계하고 구현할 수 있도록 설계되었습니다.
- 특징:
- 완전한 커스터마이징 가능: 렌더링 순서, 셰이더, 후처리 효과 등을 자유롭게 구성 가능.
- Unity에서 제공하는 HDRP와 URP도 SRP를 기반으로 만들어졌음.
- 고급 사용자와 그래픽 엔지니어를 대상으로 설계됨.
- 직접 SRP를 활용하려면 C# 스크립팅과 그래픽 API(OpenGL, Vulkan, DirectX 등)에 대한 지식이 필요.
2. URP (Universal Render Pipeline)
- 정의: URP는 SRP를 기반으로 Unity에서 사전 구성된 렌더링 파이프라인입니다. 다양한 플랫폼에서 효율적으로 작동하도록 설계되었으며, 성능과 품질 간의 균형을 제공합니다.
- 특징:
- 범용성: 모바일, PC, 콘솔 등 여러 플랫폼에서 사용할 수 있도록 최적화.
- 사용 용이성: SRP의 복잡한 구현 없이, Unity 사용자들이 쉽게 사용할 수 있도록 사전 정의된 기능 제공.
- 성능 최적화: 저사양 기기에서도 효율적으로 작동하도록 경량화된 렌더링.
- 커스터마이징 가능하지만, SRP만큼 세밀하지는 않음.
SRP와 URP의 차이
항목SRP (Scriptable Render Pipeline)URP (Universal Render Pipeline)
정의 | 사용자 정의 가능한 렌더링 파이프라인 프레임워크 | SRP 기반의 사전 정의된 렌더링 파이프라인 |
대상 | 그래픽 엔지니어, 고급 사용자 | 일반 Unity 사용자 |
유연성 | 렌더링 단계부터 효과까지 완전 커스터마이징 가능 | 제한적이지만 적절한 커스터마이징 제공 |
난이도 | 고급 기술 요구 | 사용이 쉬움 |
플랫폼 지원 | 구현 방식에 따라 다름 | 다양한 플랫폼 지원 (모바일, PC, 콘솔 등) |
성능 최적화 | 사용자의 구현에 따라 달라짐 | 저사양 기기에서도 안정적인 성능 제공 |
https://unity.com/kr/srp/universal-render-pipeline
https://unity.com/kr/resources/introduction-to-urp-advanced-creators-unity-6
아래는 바로 실습용이고
전체 강좌는 여기이다.
https://www.youtube.com/watch?v=Q448UVXT-8M
Create Shader > Unlit Shader > HelloShader 이 파일내용을 일단 모두 삭제후 빈 파일 상태에서 새로 작성
기본개념 몇개
- 제일 상단에는 Shader의 이름을 작성한다
- 외부에서 셰이더 내부로 값을 전달하려면 머티리얼 프로퍼티(Properties)라는게 필요
머티리얼 프로퍼티에 쓰여지는 변수의 형식은 아래와 같다.
_변수명 ("인스펙트에 보여질 이름", 타입) = 기본값 이런식으로 구성된다.
Shader "retr0/HelloShader"
{
// 머티리얼의 프로퍼티 목록이 온다
// 머티리얼 프로퍼티의 값은 셰이더 내부의 변수로 전달된다.
Properties
{
_BaseColor ("Color", Color) = (1,1,1,1)
}
}
- SubShader가 실제 작동할 몸체가 되겠다. 하나 이상을 배치할수가 있는데 유니티는 위에서 아래로 진행하는데 최초로 성공하는 서브셰이더를 작동시키는 방식으로 동작한다. 이 서브셰이더를 사용하는 대표적인 예로써는 하드웨어 사양별로 동작하는 코드를 배치해두고 작동시키는 것이 있다.
Shader "retr0/HelloShader"
{
// 머티리얼의 프로퍼티 목록이 온다
// 머티리얼 프로퍼티의 값은 셰이더 내부의 변수로 전달된다.
Properties
{
_BaseColor ("Color", Color) = (1,1,1,1)
_Scale ("Scale", Float) = 1
}
// 풍부한 그래픽 효과를 사용 --> 고사양 하드웨어
SubShader
{
}
// 중급 그래픽 효과를 사용 --> 중급 하드웨어
SubShader
{
}
}
- Tags라는 블럭은 셰이더에 추가적인 정보를 제공하는 블럭이다. 아래 코드는 유니버설 렌더 파이프라이에서 동작하는 셰이더라는 의미이다.
Tag에 대한 자세한 설명은 여기 참조
- PreviewType이라는 태그는 머티리얼을 인스펙터에 어떻게 표시할지를 알린다.
Shader "retr0/HelloShader"
{
// 머티리얼의 프로퍼티 목록이 온다
// 머티리얼 프로퍼티의 값은 셰이더 내부의 변수로 전달된다.
Properties
{
_BaseColor ("Color", Color) = (1,1,1,1)
_Scale ("Scale", Float) = 1
}
SubShader
{
Tags { "RenderPipeline" = "UniversalRenderPipeline" "PreviewType" = "Sphere"}
}
}
- Pass 블럭은 게임오브젝트 하나를 완전히 한번에 그리는것에 대응 하는것임. 그래서 여러개의 Pass 블럭이 있다면 하나의 오브젝트를 여러번 그리는 효과를 내게 됨.
- 셰이더 프로그램 추가는 HLSLPROGRAM - ENDHLSL이라는 블럭안에서 작성하게 된다.
자세한건 여기 참조
Shader "retr0/HelloShader"
{
// 머티리얼의 프로퍼티 목록이 온다
// 머티리얼 프로퍼티의 값은 셰이더 내부의 변수로 전달된다.
Properties
{
_BaseColor ("Color", Color) = (1,1,1,1)
_Scale ("Scale", Float) = 1
}
SubShader
{
Tags { "RenderPipeline" = "UniversalRenderPipeline" "PreviewType" = "Sphere"}
// 게임 오브젝트 그리기 한번에 대응
Pass
{
HLSLPROGRAM
ENDHLSL
}
}
}
- 유니티는 작업의 편의성을 위해 작업자에게 제공하는 라이브러리 함수가 있는데 그걸 사용하려면 #include "" 구문으로 사용할수 있다.
- 기본 코어 라이브러리는 Core.hlsl 인데 아래처럼 참조가 가능하다.
...
SubShader
{
Tags { "RenderPipeline" = "UniversalRenderPipeline" "PreviewType" = "Sphere"}
// 게임 오브젝트 그리기 한번에 대응
Pass
{
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
ENDHLSL
}
}
}
- #Pragma 명령어는 컴파일러에게 옵션을 알려줄때 사용하는 것이다.
자세한 옵션이나 사용법은 여기를 참조하면 된다.
- vertex 이름뒤에 나오는 함수로 vertex함수를 정하는 것이다.
- fragment 이름뒤에 나오는 함수 fragment함수를 정하는 것이다.
...
SubShader
{
Tags { "RenderPipeline" = "UniversalRenderPipeline" "PreviewType" = "Sphere"}
// 게임 오브젝트 그리기 한번에 대응
Pass
{
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// 우리가 작성할 버텍스 셰이더와 프래그먼트 셰이더 함수의 이름을 결정
#pragma vertex vert
#pragma fragment frag
ENDHLSL
}
}
}
- 변수 선언은 맨 위에 선언한 머터리얼 프로퍼티 이름으로 선언할수 있다.
- RGBA 컬러값을 저장할수 있는 변수 타입인 half4 형식을 지정후에 _BaseColor라는 변수명으로 변수를 선언했다.
- pragma 밑에 선언되는 변수들은 유니폼변수라고 해서 버텍스, 프라그먼트 모두에 사용할수 있는 변수들이다.
- 참고로 half는 float의 메모리사용량의 절반만 쓰는 변수형이다.
- 그리고 각 함수의 입력으로 쓰이는 구조체를 선언해주어야 한다.
- 이 구조체의 자세한 설명은 여기를 참조한다.
...
SubShader
{
Tags { "RenderPipeline" = "UniversalRenderPipeline" "PreviewType" = "Sphere"}
// 게임 오브젝트 그리기 한번에 대응
Pass
{
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// 우리가 작성할 버텍스 셰이더와 프래그먼트 셰이더 함수의 이름을 결정
#pragma vertex vert
#pragma fragment frag
half4 _BaseColor;
half _Scale;
// 버텍스 함수에게 전달할 입력
struct VertexInput
{
// 3D 모델의 각 정점의 위치를 가진다.
// 시멘틱은 변수에 대한 추가 정보를 제공하는 문법/키워드
// POSITION - 버텍스 함수의 입력으로 사용할 오브젝트 정점을 표시
float3 objectSpacePosition : POSITION;
};
// 프래그먼트 함수에게 전달할 입력
// 버텍스 함수의 출력이기도 하다
struct FragmentInput
{
// 화면상의 위치
// x, y - 화면 위치, z - 깊이, w - 동차 좌표계
// SV_POSITION - 프래그먼트 함수의 입력으로 사용할 화면상의 정점을 표시하는데 사용
float4 screenPosition : SV_POSITION;
};
ENDHLSL
}
}
}
입력에 사용할 구조체들을 선언했으면 실제 함수들을 작성하면 된다.
// 우리가 작성할 버텍스 셰이더와 프래그먼트 셰이더 함수의 이름을 결정
#pragma vertex vert
#pragma fragment frag
...
half4 _BaseColor;
half _Scale;
// 버텍스 함수에게 전달할 입력
struct VertexInput
{
// 3D 모델의 각 정점의 위치를 가진다.
// 시멘틱은 변수에 대한 추가 정보를 제공하는 문법/키워드
// POSITION - 버텍스 함수의 입력으로 사용할 오브젝트 정점을 표시
float3 objectSpacePosition : POSITION;
};
// 프래그먼트 함수에게 전달할 입력
// 버텍스 함수의 출력이기도 하다
struct FragmentInput
{
// 화면상의 위치
// x, y - 화면 위치, z - 깊이, w - 동차 좌표계
// SV_POSITION - 프래그먼트 함수의 입력으로 사용할 화면상의 정점을 표시하는데 사용
float4 screenPosition : SV_POSITION;
};
FragmentInput vert(VertexInput input)
{
// 오브젝트 버텍스들의 좌표값을 얻어와서 스케일값을 곱해서 원점에서의 거리가 변경되서 스케일값이 변경되게 됨
// input.objectSpacePosition 이 값은 그래픽스 드라이버에 의해서 자동으로 채워져서 들어오게 된다.
half3 objectSpacePosition = input.objectSpacePosition;
objectSpacePosition *= _Scale;
// 오브젝트 공간의 정점을 월드 공간으로 변환 (Core.hlsl에 포함되어 있는함수)
half3 worldPosition = TransformObjectToWorld(objectSpacePosition);
// 월드 공간에 있는 점점을 뷰 공간으로 변환
half3 viewPosition = TransformWorldToView(worldPosition);
// 뷰 공간에 있는 점점을 클립 공간으로 변환(동차 클립 좌표계 Homobeneous Clip Space)
half4 clipPosition = TransformWViewToHClip(viewPosition);
FragmentInput output;
// vert -> screenPosition - HClip 위치 클립 공간위치
// --> 래스터라이저 --> frag : screenPosition 화면상의 좌표 위치로 변환 된 것을 받게된다.
output.screenPosition = clipPosition;
return output;
}
half4 frag(FragmentInput input) : SV_Target
{
return _BaseColor;
}
ENDHLSL
}
}
}
이렇게 셰이더를 다 작성했으면 유니티에서 셰이더 이름위에서 오른쪽 클릭으로 메뉴를 호출해 바로 매터리얼을 생성한다.
그리고 3D 객체를 생성해서 적용하면 바로 어떤식으로 동작하는지 알수 있다.
'유니티3D > 셰이더' 카테고리의 다른 글
매우 넓은 윤곽선 그리기 연구 (0) | 2024.12.09 |
---|---|
유니티로 게임 제작 기법 연습 - UI용 셰이더 만들기 (0) | 2024.11.18 |