불꽃놀이 파티클
폭발하는 파티클 단계를 만들어 본다.
먼저 가운데 밝은점 만들어 보자.
uv 값의 범위는 x : -.5 ~ .5, y : -.5 ~ .5 로 설정되어 있다.
uv 값의 크기를 구하면 uv와 원점과의 거리가 나오며, 그 거리를 역수를 취해서 컬러값에 넣어주면
다음과 같이 나온다.
vec2 uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
col = length(uv) * vec3(1);
length(uv) 값을 d 값으로 설정하고, 소수를 d 값으로 나눈 값을 컬러값으로 사용하면
d 값 작으면 컬러값이 커지게 되고, d 값이 커지면 컬러값이 작아져 다음과 같이 나온다.
col = .1 / length(uv) * vec3(1);
이제 해쉬함수를 생성한다.
해쉬 함수는 랜덤함수는 아니고, 인풋 값에 따라 겹치지 않은 다른 값을 리턴해준다.
vec2 Hash12(float t) { // 1
float x = fract(sin(t * 674.4) * 453.2);
float y = fract(sin((t + x) * 714.2) * 263.3);
return vec2(x, y);
}
함수를 보면 t (iTime) 시간값에 따라서 sin을 하고 소수점을 반환한 값 x를 체인형태로 y 값에 input으로
들어가게 한다.
셰이더에서 이런식으로 해쉬함수를 구현함
이 해쉬함수는 파티클이 랜덤으로 퍼져나가는 방향을 만들때 사용할 것이다.
float t = fract(iTime);
vec2 dir = Hash12(.5)-.5;
float d = length(uv - dir * t);
hash 함수에 특정 값을 넣으면 항상 똑같은 0~1 범위의 float 값을 리턴한다.
우리는 화면이 -.5~.5 이기 때문에, hash 함수 리턴 값에 -.5를 하여 화면 범위와 맞춰준다.
dir * t 를 하여 dir 벡터 방향으로 움직이는 애니메이션을 구현했다.
그런데 해쉬함수에서 리턴받는 vec2 dir은 픽셀마다 항상 동일할 것이다
파티클이 확산하는 것이 아니라 동일한 방향으로 애니메이션 될것이다.
hash 함수 input 파라메터의 .5 값을 매 프레임마다 바꾸어 주어야 한다.
for loop를 묶고 여러개의 파티클을 생성하자.
hash 값에 index값을 넣어 주고 0인 경우 해쉬함수 0을 리턴하는 것을 막기 위해 .1을 더해준다.
#define NUM_PARTICLE 95.
vec2 Hash12(float t) { // 1
float x = fract(sin(t * 674.4) * 453.2);
float y = fract(sin((t + x) * 714.2) * 263.3);
return vec2(x, y);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec3 col = vec3(0.);
vec2 uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
for (float i=0.; i < NUM_PARTICLE; i++) { // 5
float t = fract(iTime);
vec2 dir = Hash12((i + .1))-.5;
float d = length(uv - dir * t);
float brightness = .001;
col += brightness / d;
}
fragColor = vec4(col,1.0);
}
결과는 다음과 같다.
그런데 보면 폭발이 사각형태이다.
그 이유는 파티클을 확산하는 방향을 계산하는 해쉬함수를 보면,
x, y 값이 -.5 ~ .5 이기 때문에 많은 파티클을 생성해 주게 되면 사각형태가 될 것이다.
그래서 극좌표 형태의 값을 리턴하는 해쉬함수를 생성하자.
theta에 -.5~.5 값에 6.28 값을 곱해주어서 360도를 의미하는 2PI 값을 만들어주고
r 값에는 우리의 화면 -.5~.5를 넘어가지 않게 -.3~.3 범위를 만들어 주기 위해 마지막에 .3을 곱해준다.
sin(theta), cos(theata)를 사용하여 원점을 기준으로 원의 반지름 r 만큼 해당하는 구역의 좌표를 리턴하게 된다.
vec2 HashPolar12(float t) {
float theta = fract(sin(t * 674.4) * 453.2) * PI * PI;
float r = fract(sin((t + theta) * 714.2) * 263.3) * .3;
return vec2(sin(theta), cos(theta)) * r;
}
> 파티클이 항상 똑같은 밝기를 가져서 부자연 스럽다.
불꽃놀이를 하면 파티클이 보이지 않아다가 터지면서 파티클이 확 밝아지기 때문이다.
파티클이 어둡다가 밝아지게 만들자.
smoothstep과 mix를 사용하면 된다.
smoothstep을 사용하면 리턴 값이 0~1을 리턴하게 되고,
이 값이 mix 값의 input으로 들어가서 remap 해주면 된다.
float brightness = mix(.0001, .0007, smoothstep(.1, .7, t)); // 7
t는 fract 시간값이니깐, 1초 주기이다.
0.1초 전에는 밝기가 0.0001이고, 서서히 0.7초까지 밝아지고 0.7초 부터는 0.0007로
밝기가 설정이 된다.
> 파티클이 반짝 반짝거리는 효과를 만들어본다.
float sparkle = sin(t * .5) * .5 + .5;
sin 값에 0.5를 곱하고 .5를 더해주게 되면 0 ~ 1의 범위를 가지게 된다.
그 값을 컬러값과 곱해지게 되면 0에 가까울때는 컬러값이 어둡게 나오고,
1에 가까울때는 컬러값이 밝게 나온다.
반짝 반짝 거리는 주기를 빠르게 하기 위하여 sin 파라메터 값을 적당하게 만져준다.
이 내용을 종합해서 explosion 함수를 만든다.
#define NUM_PARTICLE 95.
#define PI 3.141592
vec2 Hash12(float t) { // 1
float x = fract(sin(t * 674.4) * 453.2);
float y = fract(sin((t + x) * 714.2) * 263.3);
return vec2(x, y);
}
vec2 HashPolar12(float t) {
float theta = fract(sin(t * 674.4) * 453.2) * PI * PI;
float r = fract(sin((t + theta) * 714.2) * 263.3) * .3;
return vec2(sin(theta), cos(theta)) * r;
}
float Explosion(float i, vec2 uv) {
float explod = 0.;
float t = fract(iTime);
vec2 dir = HashPolar12((i + .1)); // 6
float d = length(uv - dir * t);
float brightness = mix(.0001, .0007, smoothstep(.1, .7, t)); // 7
float sparkle = sin(t * i * .5) * .5 + .5; // 8
explod += brightness * sparkle / d;
return explod;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec3 col = vec3(0.);
vec2 uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
float firewalks = 0.;
for (float i=0.; i < NUM_PARTICLE; i++) { // 5
firewalks += Explosion(i, uv);
}
col = vec3(firewalks);
fragColor = vec4(col,1.0);
}
자이제 컬러값을 입혀주고 빵빵 터지는 불꽃놀이 쑈를 만들어볼까나 ?
> 컬러 변수를 만들자
형형 색색 바뀌는 불꽃의 색상을 설정해보자
sin(..) 함수로 -1~1 레인지를 *.25 를 곱해서 -.25 ~ .25 가 되고, .75를 더해서
.5~.75 레인지로 변경하여 최소 0.5 밝은 계열의 색상이 랜덤으로 나오도록 한다.
vec3 color = sin(4. * vec3(.34, .54, .43) * ft) * .25 + .75; // 10
> 불꽃이 터지는 위치를 여러개로 만들자..
한번에 터지는 불꽃의 개수를 NUM_EXPLOSION로 설정하고 loop를 만든다.
uv 값을 변경하여 여러 지점에서 불꽃을 터지게 하자.
이전에 구했던 Hash 함수를 사용하여 offset 값을 구하고, offset 값에 적당한 값을 곱해서
화면에 전체 터지는 영역을 고르게 만들어 준다.
최초 불꽃이 터지는 지점이 화면에 고르게 나오는지 확인 하기 위하여 아래 코드를 사용하여 확인한다
col += .001/length(uv - offs)
for (float i=0.; i < NUM_EXPLOSION; i++) { // 13
....
vec2 offs = Hash12(iTime + i + 1) - .5; // 14
offs *= vec2(1.77, 1.);
col += Explosion(uv - offs, fract(t)) * color;
}
> 불꽃 터지는 타이밍도 랜덤하게 만들어 주기 위하여 시간축을 왜곡해야 한다.
float t = iTime + (i / NUM_EXPLOSION); // 15
float ft = floor(t); // 11
fract(iTime)을 쓰지만, for loop당 하나의 불꽃을 렌더링 하기에 index를 사용한다.
floor(iTime + i) 값을 offset 계산할때 사용하여, 불꽃마다 다른 위치에서 터지도록 했으며,
fract(iTime + index / 전체 루프 카운트) 를 사용하여, 불꽃 마다 터지는 시간이 다르도록 한다.
전체코드 >
#define NUM_PARTICLE 95.
#define NUM_EXPLOSION 5.
#define PI 3.141592
vec2 Hash12(float t) { // 1
float x = fract(sin(t * 674.4) * 453.2);
float y = fract(sin((t + x) * 714.2) * 263.3);
return vec2(x, y);
}
vec2 HashPolar12(float t) {
float theta = fract(sin(t * 674.4) * 453.2) * PI * PI;
float r = fract(sin((t + theta) * 714.2) * 263.3) * .3;
return vec2(sin(theta), cos(theta)) * r;
}
float Explosion(vec2 uv, float t) {
float explod = 0.;
for (float i=0.; i< NUM_PARTICLE; i++) {
vec2 dir = HashPolar12((i + .1)); // 6
float brightness = mix(.0001, .0007, smoothstep(.1, .7, t)); // 7
brightness *= smoothstep(1., .7, t); // 12
float sparkle = sin(t * i * .5) * .5 + .5; // 8
float d = length(uv - dir * t);
explod += brightness * sparkle / d;
}
return explod;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
vec3 col = vec3(0.);
float firewalks = 0.;
for (float i=0.; i < NUM_EXPLOSION; i++) { // 13
float t = iTime + (i / NUM_EXPLOSION); // 15
float ft = floor(t); // 11
vec3 color = sin(4. * vec3(.34, .54, .43) * ft) * .25 + .75; // 10
vec2 offs = Hash12(i + 1. + ft) - .5; // 14
offs *= vec2(1.77, 1.);
col += Explosion(uv - offs, fract(t)) * color;
}
fragColor = vec4(col,1.0);
}
'Shader CG' 카테고리의 다른 글
Star (0) | 2024.09.10 |
---|---|
12. RayMarching - rain and lights (0) | 2024.08.18 |
11. RayMarching - Snow man (0) | 2024.08.14 |
10. RayMarching - Cube map (0) | 2024.08.13 |
09. RayMarching - SD Operation (0) | 2024.08.12 |