일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Specular
- URP로 변경
- Private Bytes
- Toon Shader
- Cell Look
- OculusMotionVectorPass
- Virtual Byte
- 작업 집합
- AppSW
- 프로그래밍 기초
- ASW(Application SpaceWarp)
- 벡터
- 메모리 누수
- Three(Two) Tone Shading
- 개인 바이트
- 3d
- Windows Build
- Rim Light
- 가상 바이트
- working set
- C언어
- 게임 수학
- VR
- ColorGradingLutPass
- Cell Shader
- URP
- Cartoon Rendering
- Today
- Total
WinCNT
높이 안개(Height Fog)를 Global Shader Variable로 변경하기! 본문
높이 안개(Height Fog)를 Global Shader Variable로 변경하기!
WinCNT_SSS 2024. 9. 18. 22:36서론
높이 안개(Height Fog)를 구현한 건 좋은데 구조에 좀 불만이 있었다
이대로라면 여러 귀찮은 점이 발생하므로 Global Shader Variable를 사용해 수정하기로 했다
무엇이 문제인가?
그 때는 셰이더가 하나여서 우선 Height Fog에 관련된 변수들을 전부 CBUFFER에 넣고 구현했다
CBUFFER_START(UnityPerMaterial)
//...생략...
// Height Fog
sampler2D _HeightFogNoise;
float _HeightFogNoisePower;
float _HeightFogNoiseSpeedX;
float _HeightFogNoiseSpeedY;
half4 _HeightFogColor;
float _HeightFogDensity;
float _MaxFogHeight;
//...생략...
CBUFFER_END
문제는 이러면 각각의 머테리얼마다 Height Fog 관련 설정을 해야 한다
또한 사용하는 모든 셰이더에 Height Fog 관련 변수와 함수를 각각 구현해줘야 한다!!!
안 그러면 당연하게도 일부 오브젝트만 Height Fog의 영향을 안 받는 케이스가 보인다
우선은 Height Fog를 라이브러리로 만들자
상술한 문제를 해결하기 위해 Height Fog를 라이브러리로 만들기로 했다
실제 폴더에 텍스트 파일을 하나 만들고 적절한 이름을 설정한 뒤 확장자를 hlsl로 변경했다
거기에 Height Fog와 관련된 변수와 함수들을 옮겼다
// Height Fog
sampler2D _HeightFogNoise;
float _HeightFogNoisePower;
float _HeightFogNoiseSpeedX;
float _HeightFogNoiseSpeedY;
half4 _HeightFogColor;
float _HeightFogDensity;
float _MaxFogHeight;
float3 MixUniformHeightFog(
float3 color,
float3 heightFogcolor,
float3 objectPos,
float3 cameraPos,
float heightFogDensity,
float maxFogHeight,
sampler2D noiseTex,
float2 noiseUV,
float noiseUVScrollX,
float noiseUVScrollY,
float noisePower)
{
noiseUV.x += frac(_Time.y * noiseUVScrollX);
noiseUV.y += frac(_Time.y * noiseUVScrollY);
const float noise = tex2D(noiseTex, noiseUV).r * noisePower;
objectPos = objectPos + noise;
float3 camToObj = cameraPos - objectPos;
float t = 0.0;
const float a = objectPos.y < maxFogHeight ? 0 : cameraPos.y;
const float b = cameraPos.y > maxFogHeight ? maxFogHeight : -maxFogHeight;
const float c = cameraPos.y > maxFogHeight ? -objectPos.y : 0;
float d = a + b + c;
d = max(objectPos.y, cameraPos.y) < maxFogHeight ? camToObj.y : d;
d = min(objectPos.y, cameraPos.y) > maxFogHeight ? 0 : d;
t = saturate(d / camToObj.y);
const float distance = length(camToObj) * t;
const float heightFogFactor = exp2(-heightFogDensity * distance * LOG2_E);
color = lerp(heightFogcolor.rgb, color, heightFogFactor);
return color;
}
참고로 HeightFogLib.hlsl에는 Core.hlsl가 없기 때문에 몇몇 부분이 컴파일 에러가 난다
include 관리를 잘 하면 없앴을 수 있겠지만 매우 귀찮기 때문에 그냥 셰이더 파일에서 순서를 맞춰서 include하기로 했다
Height Fog 키워드는 _HEIGHT_FOG_ON에서 _HEIGHT_FOG로 변경했다(그 이유는 후술)
Height Fog 키워드의 위치도 Lib로 옮길까 했지만 샘플치고 경직되는 느낌이 있어서 그대로 뒀다
(제대로 된 설계로 정해진 셰이더만 딱 만들다면 넣는 게 나을듯?)
SRP Batcher가 not compatible 되는데요??
이걸로 완성!…이면 좋겠지만 아쉽게도 그렇지 않다
Height Fog 관련 변수들을 HeightFogLib.hlsl에 옮겼기 때문에 SRP Batcher가 not compatible이 되어 버린다!
Properties에 있는 Height Fog 관련 변수들이 HeightFogLib.hlsl에 있으니 다른 CBUFFER라고 인식해서 SRP Batcher가 작동하지 않게 되는 것으로 보인다
그렇다고 HeightFogLib.hlsl에 있는 Height Fog와 관련 변수들을 CBUFFER_START(UnityPerMaterial)로 감싸면 중복 선언이 되므로 그냥 컴파일 에러가 발생한다
이건 Properties에 있는 Height Fog 관련 변수들을 삭제하면 해결된다
물론 이렇게 되면 머티리얼에서 Height Fog 관련 프로퍼티들이 사라지므로 조정할 수 없게 된다
‘아니 그럼 소용 없는 거 아닌가?’라고 생각할 수 있는데 이걸 해결해 주는 것이 바로 Global Shader Variable!
Global Shader Variable 사용하기!
Global Shader Variable는 말 그대로 셰이더에 전역 변수를 의미한다
유니티에서는 스크립트를 통해서 모든 셰이더의 Global Shader Variable에 대해서 값을 일괄적으로 설정할 수 있다
아래가 샘플 코드이다
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteInEditMode]
public class HeightFogController : MonoBehaviour
{
// Properties
[Header("Height Fog")]
[SerializeField] private bool HeightFogOn = false;
[ColorUsage(true, true)] public Color HeightFogColor = Color.gray;
[Range(0.0f, 1.0f)]public float HeightFogDensity = 0.0f;
public float MaxFogHeight = 1.0f;
[Header("Height Fog Noise")]
public Texture HeightFogNoise = null;
[Range(0.0f, 5.0f)]public float NoisePower = 1.0f;
[Range(-1.0f, 1.0f)]public float NoiseScrollSpeedX = 0.0f;
[Range(-1.0f, 1.0f)]public float NoiseScrollSpeedY = 0.0f;
// Property ID
private int _HeightFogNoise = -1;
private int _HeightFogNoisePower = -1;
private int _HeightFogNoiseSpeedX = -1;
private int _HeightFogNoiseSpeedY = -1;
private int _HeightFogColor = -1;
private int _HeightFogDensity = -1;
private int _MaxFogHeight = -1;
// Global Keyword
private GlobalKeyword HeightFogKeyword;
void Awake()
{
_HeightFogNoise = Shader.PropertyToID("_HeightFogNoise");
_HeightFogNoisePower = Shader.PropertyToID("_HeightFogNoisePower");
_HeightFogNoiseSpeedX = Shader.PropertyToID("_HeightFogNoiseSpeedX");
_HeightFogNoiseSpeedY = Shader.PropertyToID("_HeightFogNoiseSpeedY");
_HeightFogColor = Shader.PropertyToID("_HeightFogColor");
_HeightFogDensity = Shader.PropertyToID("_HeightFogDensity");
_MaxFogHeight = Shader.PropertyToID("_MaxFogHeight");
HeightFogKeyword = GlobalKeyword.Create("_HEIGHT_FOG");
}
void Update()
{
if (HeightFogOn)
Shader.EnableKeyword(HeightFogKeyword);
else
Shader.DisableKeyword(HeightFogKeyword);
Shader.SetGlobalColor(_HeightFogColor, HeightFogColor);
Shader.SetGlobalFloat(_HeightFogDensity, (float)HeightFogDensity);
Shader.SetGlobalFloat(_MaxFogHeight, MaxFogHeight);
Shader.SetGlobalTexture(_HeightFogNoise, HeightFogNoise);
Shader.SetGlobalFloat(_HeightFogNoisePower, NoisePower);
Shader.SetGlobalFloat(_HeightFogNoiseSpeedX, NoiseScrollSpeedX);
Shader.SetGlobalFloat(_HeightFogNoiseSpeedY, NoiseScrollSpeedY);
}
void OnDisable()
{
Shader.DisableKeyword(HeightFogKeyword);
}
void OnDestroy()
{
Shader.DisableKeyword(HeightFogKeyword);
}
}
위의 스크립트를 적당한 오브젝트에 붙여두면 완성!
결과
이제 HeightFogController로 모든 오브젝트에 대한 Height Fog의 변수를 컨트롤할 수 있다!
물론 HeightFogLib.hlsl를 include해야 하지만
주의!) _HEIGHT_FOG_ON을 _HEIGHT_FOG로 변경한 이유
변경한 이유는 다름이 아니라 Shader.EnableKeyword와 Shader.DisableKeyword가 평범하게 동작하지 않았기 때문이다
셰이더에서 Toggle을 붙인 프로퍼티가 있으면 자동으로 _ON가 붙은 키워드를 생성하는 것과 연관이 있을까?
자세히 조사는 하지 않아서 명확한 이유는 모르겠다…
아무튼 Global Keyword 뒤에 On, Off를 붙이는 건 피하도록 하자!
마무리
이걸로 Height Fog가 더 나은 형태로 진화했다
덤으로 Global Shader Variable에 대해서도 알게 되었으니 만족스러운 태스크였다
참고 사이트
UnityにはShaderのグローバル変数が存在する - Qiita
'Unity > URP or Shader 관련' 카테고리의 다른 글
포션의 의사 액체(Liquid) 셰이더 만들기! (0) | 2024.12.08 |
---|---|
커스텀 셰이더에 높이 안개(Height Fog) 구현해보기! (0) | 2024.08.01 |
Visual Effect Graph(VFX Graph)의 도입 체험기! (0) | 2024.07.09 |
Alpha 값으로 Blue Noise를 이용한 Dither를 해보자! (0) | 2024.06.24 |
Fill Amount 기능이 있는 간단(?) Dial Gauge UI(가칭) 셰이더 만들어보기! (1) | 2024.06.16 |