본문 바로가기
Shader CG

07. RayMarching - Frenel effect

by SimonLee 2024. 8. 10.

프레넬 효과는 ?


Fresnel Effect는 빛의 물체와 수직에 가까울수록 투과하는 비율이 높아지고,

수평과 가까울수록 반사되는 비율이 높아지는 현상이다.

수평에 가까우면 반사 비율이 커서 좀 밝게 보입니다.

 

파란 부분이 프레넬 효과

 

정반사 (스펙큘러)와 의미는 비슷하지만, 

정반사의 경우 들어오는 빛의양과 나가는 빛의 양이 일정하다고 가정하는 반면,

현실에서는 빛이 물체에 도달했을때, 같은양의 빛이라고 해도 시야각(View Angle)에 따라 일정량은 반사를 하고,

일정량은 굴절하는것을 의미한다.

 

즉, Fresnel법칙이란, 물체의 매질과 시야각에 따라 반사율과 굴절율이 정해진다라는 것입니다.

 

 

원 물체에 점 2개가 있습니다.

첫번째 점의 노말은 N1, 두번째 점의 노말을 N2 입니다.

N1 부분은 수평에 가깝고, 반사율이 커지고 굴절율이 작아집니다. --> 밝게 빛남.

N2 정면에서 볼수록 반사율이 적어지고 굴절율이 커지게 됩니다. --> 덜 밝음.

 

N1은 View와의 각도가 90도(세타1) 이고,

N2와 View와의 각도는 30도 (세타2) 정도 됩니다.

여기서 View와 N1의 내적을 구하면 

첫번째 점 처럼 외곽에 위치해 있어서 뷰와 노말의 각도가 90에 가까운 것들은 0이 나오고,

두번째 점 처럼 뷰와 가까운곳에 위치해 있으면 1 가까운 값을 얻을 것 입니다.

 

인버트 (1 - a )를 시키지 않고 그대로 사용하게 되면, 

인버트를 시키면 외곽 부분은 밝게 빛이 날 것 입니다.

 

이 원리로 fake specular를 사용하게 되어,

빛의 방향과 상관없이 카메라가 보는 방향은 밝게 빛나는 효과를 만들어 낼 수도 있습니다.

 

프레넬 공식 >>

// frenel
vec3 frenelCol = vec3(0.2, 0.2, 0.7);
float frenel = pow(clamp(1. - dot(-rd, normal), 0., 1.), 0.5);
col += frenelCol * frenel;

 

전체 코드 >>

const int MAX_MARCHING_STEPS = 255;
const float MIN_DIST = 0.0;
const float MAX_DIST = 100.0;
const float PRECISION = 0.001;

float sdSphere(vec3 p, float r) {
  return length(p) - r;
}

float scene(vec3 p) {
  return sdSphere(p, 1.);
}

float rayMarch(vec3 ro, vec3 rd) {
  float d = MIN_DIST;
  for (int i=0; i<MAX_MARCHING_STEPS; i++) {
    vec3 p = ro + rd * d;
    float ret = scene(p);
    d += ret;
    if (ret < PRECISION || ret > MAX_DIST) break;
  }
  return d;
}

vec3 calcNormal(vec3 p) {
  vec2 e = vec2(1, -1) * 0.001;
  return normalize(
    e.xyy * scene(p + e.xyy) +
    e.yxy * scene(p + e.yxy) +
    e.yyx * scene(p + e.yyx) +
    e.xxx * scene(p + e.xxx));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = (fragCoord - 0.5*iResolution.xy)/iResolution.y;
  vec3 background = vec3(0.3);
  vec3 col = background;
  vec3 rd = normalize(vec3(uv, -1));
  vec3 ro = vec3(0, 0, 5);
  vec3 lightPos = vec3(5, 10, 0);
  
  float d = rayMarch(ro, rd);
  if (d < MAX_DIST) {
    col = vec3(0.);
    vec3 p = ro + rd * d;
    vec3 normal = calcNormal(p);
    vec3 lightDi = normalize(lightPos - p);
    // diffuse
    vec3 diffCol = vec3(0.7, 0.7, 0.2);
    float diffuse = dot(normal, lightDi);
    col += (diffCol * diffuse) * 0.7 + background * 0.3;
    
    // specular
    vec3 specCol = vec3(0.6, 0.6, 0.1);
    vec3 refl = -reflect(lightDi, normal); // why minus ?
    //refl = 2. * normal * dot(normal, lightDi) - lightDi;
    float spec = pow(clamp(dot(refl, -rd), 0., 1.), 10.);
    col += spec * specCol;
    
    // frenel
    vec3 frenelCol = vec3(0.2, 0.2, 0.7);
    float frenel = pow(clamp(1. - dot(-rd, normal), 0., 1.), 0.5);
    col += frenelCol * frenel;
  }
  
  fragColor = vec4(col,1.0);
}

 

 

Reference

https://inspirnathan.com/posts/58-shadertoy-tutorial-part-12

 

Shadertoy Tutorial Part 12 - Fresnel and Rim Lighting

Learn how to add rim lighting around objects in your scene with fresnel!

inspirnathan.com

 

'Shader CG' 카테고리의 다른 글

09. RayMarching - SD Operation  (0) 2024.08.12
08. RayMarching - Shadow  (0) 2024.08.10
06. RayMarching - 퐁 라이팅 모델  (0) 2024.08.08
05. RayMarching - Camera move  (0) 2024.08.05
04. RayMarching - Rotation  (0) 2024.08.04