본문 바로가기
Shader CG

12. RayMarching - rain and lights

by SimonLee 2024. 8. 18.

 

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' 카테고리의 다른 글

11. RayMarching - Snow man  (0) 2024.08.14
10. RayMarching - Cube map  (0) 2024.08.13
09. RayMarching - SD Operation  (0) 2024.08.12
08. RayMarching - Shadow  (0) 2024.08.10
07. RayMarching - Frenel effect  (0) 2024.08.10