공부용으로 작성되는 페이지입니다. 틀린 부분이나 환경에 따라 오류가 발생할 수 있습니다.
최근 공부를 위해 참고하고 있는 교재는 Surface Shader 중점적으로 다루고 있다. URP를 사용하기 위해서는 HLSL 기반으로 Shader를 작성할 필요가 있기 때문에 현재 공부는 교재 내용을 따라 학습 후 HLSL로 고치는 방식으로 진행하고 있다.
HLSL의 구조
즉 Properties -> SubShader -> Pass -> struct Attributes, struct Varyinge -> Vertex Shader - > Fragment Shader
아래는 교재를 참고하면서 작성한 Cubemap 기반 Reflection 코드이다.
위에서 언급했듯이 HLSL이 아닌 Surface Shader로 작성되었다. URP에서는 당연히 작동되지 않는다.
이 경우 마테리얼이 깨질 때처럼 분홍색으로 나온다.
Shader "Custom/Reflection"
{
//Make Reflection with Cubemap
//Reflection worrk by reflact bright or dark image, so it needs Emission
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Cube("Cubemap", Cube) ="" //cubemap
}
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Lambert noambient
sampler2D _MainTex;
samplerCUBE _Cube;
struct Input
{
float2 uv_MainTex;
float3 worldRefl; //get reflaction vector for uv
};
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float4 re = texCUBE (_Cube, IN.worldRefl); //calculate cubemap texture
o.Albedo = 0; //Reflaction doesn't need to have Albedo(think aboout mirror. It doesn't have own color)
o.Emission = re.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
HLSL 구조를 떠올리며 단계별로 고쳐서 작성해보았다.
가장 헷갈리는 부분은 Varyings vert() 부분. Surface Shader가 자동으로 처리해주는 부분을 HLSL Shader에서는 내가 직접 계산하도록 지시를 내려야해서 어려울 수 밖에 없었다. 특히 이 예제의 경우 반사를 구현할 때 엔진내에서 미리 정의되어있는 worldRefl를 사용하였는데 HLSL에서는 수동 계산해야한다. 이 부분을 몰랐기 때문에 고전했다.
Surface Shader | HLSL | |
World Position | 자동 | TransformObjectToWorld |
World Normal | 자동 | TransformObjectToWorldNormal |
Reflection | worldRefl (엔진 내부 정의) | reflect(viewDir,normalws) |
Clip Position | 자동 처리 | TransformWorldToHClip |
Shader "Unlit/ReflectionHLSL"
//Lets covert to HLSL
{
Properties //Properties No change
{
_MainTex ("Texture", 2D) = "white" {}
_Cube("Cubemap", Cube) = ""
}
SubShader //Write RenderPipeline
{
Tags { "RenderType" = "Opaque" "Opaque" = "Geometry" "RenderPipeline" = "UniversalPipeline" }
//I'll use HLSLPROGRAM and HLSL Function
//naming vertex, fragment and include core.hlsl
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
//Write Global variable & CBUFFER
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
TEXTURECUBE(_Cube);
SAMPLER(sampler_Cube);
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
CBUFFER_END
struct Attributes //VERTEX INPUT
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};
struct Varyings //VERTEX -> FRAGMENT
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldRefl : TEXCOORD1;
};
Varyings vert(Attributes IN) // //calculate reflect vector
{
Varyings OUT;
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(IN.normalOS);
float3 viewDir = normalize(_WorldSpaceCameraPos - positionWS); //camera vector
OUT.worldRefl = reflect(-viewDir, normalWS); //Surface reflect vector
OUT.uv = TRANSFORM_TEX(IN.uv,_MainTex);
OUT.positionHCS = TransformObjectToHClip(positionWS);
return OUT; //Give data to fragment shader(frag)
}
half4 frag(Varyings IN) : SV_Target //decide final pixel color (=fragment shader)
{
half4 baseColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex,IN.uv); //color
half3 reflColor = SAMPLE_TEXTURECUBE(_Cube,sampler_Cube,IN.worldRefl).rgb; //reflect
return half4(reflColor, baseColor.a);
}
ENDHLSL
}
}
반사와는 별개의 이야기지로 CBUFFER란 Batcher를 만족시키기 위해 따로 작성해주는 것임.
잘 작동하는지 테스트를 해보자.
우선 URP 프로젝트에서도 Material이 깨지지 않는 것을 확인할 수 있다. 보기에는 정상적으로 돌아가는 거 같지만...
다만 내가 기대한 반사 효과가 만들어지지는 않는다. 당연하게도 조명 계산(Directional Light)이 작성되지 않은 코드이기 때문
애초에 반사 자체가 빛을 반사해야 가능한거니 어색하게 느낄 수 밖에 없겠다. 이걸 왜 몰랐지?
아무튼 하는 김에 MaskMap을 추가해서 빛을 받는 부분을 조절해본다.
Light의 경우 ShaderLibrary의 Lighintg.hlsl을 include하여서 적용하였다.
그리고 처음에 코드를 작성할 때는 Mask용 uv inpt을 따로 받아와줬는데, 오류를 왕창 받은 뒤에야 그럴 필요가 없단걸 알았다 .
대신 struct Varyings에서 명시적으로 MainTexture / UVTexture로 분리해주었다.
쓰다 보니 복잡해보이는데 그냥 같은 uv를 사용하고 텍스쳐만 다르게 받았다는 뜻이다.
정상적으로 작동하는 반면 ...
[Worker4] Shader error in 'Unlit/ReflectionHLSL': redefinition of 'Varyings::uv' at line 56 (on d3d11)
오류가 반복적으로 발생하는데. 아무래도 uv를 중복 사용한다고 판명한 모양이다. (실제로는 아님) 크게 신경쓸 필욘 없지만 거슬려서 추후에 바꿔주었다.
당연하게도 조명을 받는 부분, 반사 벡터를 받는 부분이 어려웠다. 벡터에 언제쯤 익숙해질지?
//
내가 유니티 6.0을 쓰지 않고 있어서 몰랐는데 거기서는 또 기본 작성 방법이 살짝 달라졌다고한다.... 오....
그건 또 추후에 공부하면서 알아봐야겠다.
참고자료
URP unlit basic shader | Universal RP | 12.0.0
URP unlit basic shader | Universal RP | 12.0.0
URP unlit basic shader This example shows a basic URP-compatible shader. This shader fills the mesh shape with a color predefined in the shader code. To see the shader in action, copy and paste the following ShaderLab code into the Shader asset. // This sh
docs.unity3d.com
Graphics/Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl at master · Unity-Technologies/Graphics
Unity Graphics - Including Scriptable Render Pipeline - Unity-Technologies/Graphics
github.com
'Shader' 카테고리의 다른 글
[Shader] Dithering Shader (0) | 2025.05.09 |
---|---|
[Shader] Fog with SkyBox (0) | 2025.05.08 |
[Shader] OPaque Error with Unlit Shader (불투명 쉐이더 문제) (0) | 2025.04.25 |
[Shader] Hologram shader 제작 연습 with HLSL (0) | 2025.04.09 |