struct ray
{
vec3 o;
vec3 d;
};
// 직선과 점이 수직으로 만났을때 지점
vec3 clossestPoint(ray r, vec3 p) {
return r.o + max(0., dot(p - r.o, r.d)) * r.d;
}
// 직선과 점과 최단 거리
float DistRay(ray r, vec3 p) {
return length(p - clossestPoint(r, p));
}
// 카메라 좌표계 구하기.
ray GetRay(vec2 uv, vec3 camPos, vec3 lookat, float zoom) {
ray a;
a.o = camPos;
vec3 f = normalize(lookat - camPos);
vec3 r = cross(vec3(0,1,0), f);
vec3 u = cross(f, r);
vec3 c = a.o + f * zoom;
vec3 i = c + r * uv.x + u * uv.y;
a.d = normalize(i - a.o);
return a;
}
// p 지점을 기준으로 구형 모양의 렌더링
// p지점의 원점으로 부터 length를 size에 곱함으로써 원근의 효과를 없앰..
float Bokeh(ray r, vec3 p, float size, float blur) {
float d = DistRay(r, p);
size *= length(p);
float c = smoothstep(size, size * (1. - blur), d);
c *= mix(.6, 1., smoothstep(size * .8, size, d));
return c;
}
// 가로등 렌더링
vec3 streetLight(ray r, float t) {
// 좌측 우측 뻗어나가는 bukeh의 offset을 다르게 할때 사용.
float side = step(r.d.x, 0.);
// x축 대칭을 통해 양쪽으로 뻗어나가는 라이팅 렌더링
r.d.x = abs(r.d.x);
float s = 1. / 10.;
float m = 0.;
// 한프레임에 여러개의 bokeh를 렌더링
// bokeh의 z값을 크게 주어도, 원근감이 없도록 구현해 놔서 2d처럼 보임.
// bokeh x,y 좌표만 주고 중앙에서 좌측 우측 사이드로 퍼져나감.
for (float i=0.; i<1.; i+=s) {
float ti = fract(t + i + side * s * .5);
vec3 p = vec3(2., 2., 100.-ti*100.);
m += Bokeh(r, p, 0.03, .1) * ti * ti;
}
return vec3(1., .7, .3) * m;
}
// 랜덤 함수
float random(float t) {
return fract(sin(t * 4342.) * 3425.);
}
// 차량 헤드 라이트
vec3 headLight(ray r, float t) {
float side = step(r.d.x, 0.);
float s = 1. / 30.;
float m = 0.;
float w1 = 0.25;
float w2 = w1 * 1.25;
for (float i=0.; i<1.; i+=s) {
if (random(i) > .3) continue;
float ti = fract(t + i);
float z = 100.-ti*100.;
float fade = ti * ti * ti * ti * ti * ti;
float focus = smoothstep(0.7 , 1., ti);
float size = mix(.03, .015, focus);
m += Bokeh(r, vec3(-1.+w1, 0.15, z), size, .1) * fade;
m += Bokeh(r, vec3(-1.-w1, 0.15, z), size, .1) * fade;
m += Bokeh(r, vec3(-1.+w2, 0.15, z), size, .1) * fade;
m += Bokeh(r, vec3(-1.-w2, 0.15, z), size, .1) * fade;
float ref = 0.;
ref += Bokeh(r, vec3(-1.+w1, -0.15, z), size*3., 1.) * fade;
ref += Bokeh(r, vec3(-1.-w1, -0.15, z), size*3., 1.) * fade;
m += ref * focus;
}
return vec3(.9, .9, 1.) * m;
}
// 차량 후미등 렌더링
vec3 tailLight(ray r, float t) {
float side = step(r.d.x, 0.);
float s = 1. / 5.;
float m = 0.;
float w1 = 0.25;
float w2 = w1 * 1.25;
for (float i=0.; i<1.; i+=s) {
float n = random(i);
if (n > .5) continue;
float lane = step(.25, n); // 0 ~ 1
float ti = fract(t + i);
float blink = step(sin(200. * ti), 0.)* 5. * lane * step(0.9, ti);
float z = 100.-ti*100.;
float fade = ti * ti * ti * ti * ti;
float focus = smoothstep(0.8 , 1., ti);
float size = mix(.03, .015, focus);
float laneshift = smoothstep(1., .96, ti);
float x = 1.5 - lane * laneshift;
m += Bokeh(r, vec3(x+w1, 0.15, z), size, .1) * fade;
m += Bokeh(r, vec3(x-w1, 0.15, z), size, .1) * fade;
m += Bokeh(r, vec3(x+w2, 0.15, z), size, .1) * fade * (1. + blink);
m += Bokeh(r, vec3(x-w2, 0.15, z), size, .1) * fade;
float ref = 0.;
ref += Bokeh(r, vec3(x+w1, -0.15, z), size*3., 1.) * fade *(1. + blink * .1);
ref += Bokeh(r, vec3(x-w1, -0.15, z), size*3., 1.) * fade;
m += ref * focus;
}
return vec3(1.,.1,.03) * m;
}
// 랜덤함수
vec4 random2(float t) {
return fract(sin(
t * vec4(4342., 1014., 3456., 9564.)) * vec4(3425., 3245., 5324., 2123.));
}
vec4 N14(float t) {
return fract(sin(
t * vec4(123., 1024.,3435, 9564.)) * vec4(6547., 345., 8799., 1441.));
}
// 여러 빌딩 라이트 렌더링
vec3 environtLight(ray r, float t) {
float side = step(r.d.x, 0.);
r.d.x = abs(r.d.x);
float s = 1. / 10.;
vec3 m = vec3(0.);
for (float i=0.; i<1.; i+=s) {
float ti = fract(t + i + side * s * .5);
vec4 ran = N14(i + side * 100.);
float fade = .8;
float occlusion = sin(ti * 6.28 * 5.) * .5 + .5;
fade *= occlusion;
float x = mix(2.5, 10., ran.x);
float y = mix(.1, 1.5, ran.y);
vec3 p = vec3(x, y, 50.-ti*50.);
m += Bokeh(r, p, 0.03, .1) * ran.xzy * fade;
}
return m;
}
// 물방울 렌더링
vec2 Rain(vec2 uv) {
vec2 a = vec2(23, 5);
vec2 b = vec2(23, 25);
float t = iTime;
vec2 st = uv * a; // 그리드 만들기
vec2 id = floor(st); // 랜덤에 필요한 id 구하기
// random
// y축에 시간값을 더해서 비내리는 것을 어긋나게 만듬.
// 물방울과 바운딩을 함께 아래로 내려가는 효과
st.y += t;
float n = fract(sin(id.x * 3242.) * 3524.); // 랜덤
st.y += n;
uv.y += n;
id = floor(st);
// 랜덤으로 구해진 좌표값을 다시 id 값으로 만들어서 랜덤
// 바운딩 그리드 어긋날 뿐아니라 그리드 내부 offset도 어긋나게 만듬.
t += fract(sin(id.x * 21. + id.y * 324.) * 42.) * 6.28; // random
// rain
// 떨어지는 물방울 렌더링
// 떨어지는 속도를 sin의 중복을 통해 초반에 빠르고 떨어지고 ,
// 후반에 위로 느리게 올라가는 효과 구현.
// 위에서 전체적으로 떨어지고, 위로 느리게 올라가는 y offset이 상쇄되어 물방울이 멈춰보이는 효과
st = fract(st) - .5;
float y = -sin(t + sin(t + sin(t) * .5)) * .4;
vec2 p1 = vec2(0, y);
vec2 o1 = (st - p1) / a;
float d = length(o1);
float m1 = smoothstep(0.015, 0.01, d);
// rain2
// 떨어지는 물방울에 발자취 물방울 렌더링
// st.y - p1.y 의미는 떨어지는 물방울 위에있는 발자취 물방울만
// 렌더링 하겠다는 의미
vec2 st2 = uv * b;
st2 = fract(st2) - .5;
vec2 o2 = st2 / b;
float d1 = length(o2);
float exist = smoothstep(-.1, .1, st.y - p1.y);
float size = 0.02 * (0.5 - st.y);
float m2 = smoothstep(size, 0., d1) * exist;
vec2 c1 = vec2(m1 * o1 * 50.);
vec2 c2 = vec2(m2 * o2 * 50.);
//if (st.x > 0.44 || st.y > 0.48) m1 = 1.;
//return vec2(m1 + m2);
return c1 + c2;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (fragCoord.xy - 0.5*iResolution.xy)/iResolution.y;
vec2 mouseUv = iMouse.xy / iResolution.xy;
vec3 camPos = vec3(.5, .2, 0);
vec3 lookat = vec3(.5, .2, 1.);
vec2 rain = Rain(uv * 2.) * .5;
uv.x += sin(uv.y * 40.) * .005; // 지글지글
uv.y += sin(uv.x * 170.) * .003;
ray r = GetRay(uv - rain *.5, camPos, lookat, 2.);
float t = iTime * 0.1 + mouseUv.x;
vec3 col = streetLight(r, t);
col += headLight(r, t);
col += tailLight(r, t * .5);
col += environtLight(r, t);
col += uv.y * vec3(0.2, 0.3, 0.7);
//col = vec3(rain, 0);
fragColor = vec4(col,1.0);
}
'Shader CG' 카테고리의 다른 글
FireWorks (0) | 2024.09.29 |
---|---|
Star (0) | 2024.09.10 |
11. RayMarching - Snow man (0) | 2024.08.14 |
10. RayMarching - Cube map (0) | 2024.08.13 |
09. RayMarching - SD Operation (0) | 2024.08.12 |