본문 바로가기
Shader CG

10. RayMarching - Cube map

by SimonLee 2024. 8. 13.

텍스처링을 바로 샘플링 하는 방법도 있고,

프레임 버퍼 생성을 할수있는데, 

 

지원되는 이미지 버퍼가 4개를 지원하고,

1개의 큐브맵 버퍼, 1개의 컴포지팅 할수 있는 버퍼 

총 6개를 지원한다.

버퍼를 생성하여 텍스처링 가져오는 방법은 아래 15번 챕터에 잘 나와있다.

아래 레퍼런스 사이트를 참고하자.

 

그러면

 

큐브맵 텍스처링을 해본다.

먼저 채널 0번 버퍼에 큐브맵을 세팅한다.

 

채널 0번 버퍼에 바인딩 되어있는 큐브맵 샘플링 값을 가져온다.

iChannel0은 큐브맵의 samplerCube 값이고, rd는 uv 값이다.

큐브맵 텍스처에 uv 좌표에 해당하는 컬러값을 가져온다.

vec3 col = texture(iChannel0, rd).xyz;

 

 

코드 >>

마우스 좌/우, 상/하 움직임에 따라서 카메라 시점을 이동한다.

#define PI 3.147592

mat2 rotate2d(float theta) {
  float s = sin(theta), c = cos(theta);
  return mat2(c, -s, s, c);
}

mat3 camera(vec3 cameraPos, vec3 lookat) {
  vec3 cd = normalize(lookat - cameraPos);
  vec3 cr = normalize(cross(vec3(0,1,0), cd));
  vec3 cu = normalize(cross(cd, cr));
  return mat3(-cr, cu, -cd);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = (fragCoord - 0.5 * iResolution.xy)/iResolution.xy;
  vec2 mouseUv = iMouse.xy / iResolution.xy;
  if (mouseUv == vec2(0,0)) mouseUv = vec2(0.5, 0.5);
  
  vec3 lookat = vec3(0, 0, 0);
  vec3 ro = vec3(0, 0, 3);
  vec3 rd = camera(ro, lookat) *  normalize(vec3(uv, -1));
  rd.yz *= rotate2d(mix(-PI/2., PI/2., mouseUv.y));
  rd.xz *= rotate2d(mix(-PI, PI, mouseUv.x)); 
 
  vec3 col = texture(iChannel0, rd).xyz;
  fragColor = vec4(col,1.0);
}

 

 

구를 하나 생성하여

구의 색상 값을 큐브맵의 reflection 컬러 값으로 설정해보자.

// reflesive
vec3 refl = texture(iChannel0, reflect(rd, normal)).rgb;
reflCol = 1.5 * vec3(0.0,0.5,0.8) * refl;
reflCol = refl;

view direction 과 노말값을 사용하여 반사 벡터를 구한뒤, 

반사 벡터를 uv로 사용하여 큐브맵 샘플링 된 색상을 refl 로 정의했다.

특정 색상 값을 곱해주면 다양한 효과를 줄 수 있다.

 

 

전체 코드 >

구에 퐁 라이팅 적용 (diffuse + specular) + 큐브맵 reflection + fresnel 컬러값을 적용했다.

const int MAX_MARCHING_STEPS = 255;
const float MIN_DIST = 0.0;
const float MAX_DIST = 100.0;
const float PRECISION = 0.001;
const float EPSILON = 0.0005;
const float PI = 3.14159265359;

#define PI 3.147592

mat2 rotate2d(float theta) {
  float s = sin(theta), c = cos(theta);
  return mat2(c, -s, s, c);
}

mat3 camera(vec3 cameraPos, vec3 lookat) {
  vec3 cd = normalize(lookat - cameraPos);
  vec3 cr = normalize(cross(vec3(0,1,0), cd));
  vec3 cu = normalize(cross(cd, cr));
  return mat3(-cr, cu, -cd);
}

vec3 phong(vec3 lightDir, float lightIntensity, vec3 rd, vec3 normal) {
  vec3 diffCol = vec3(0);
  vec3 reflCol = vec3(0);
  vec3 specCol = vec3(0);
  
  // reflesive
  vec3 refl = texture(iChannel0, reflect(rd, normal)).rgb;
  reflCol = 1.5 * vec3(0.0,0.5,0.8) * refl;
  reflCol = refl;
  
  // specular
  vec3 specR = 2. * normal * dot(normal, lightDir) - lightDir;
  specR = reflect(-lightDir, normal);
  float spec = clamp(dot(specR, -rd), 0., 1.);
  specCol = pow(spec, 5.) * vec3(1.);
  
  // diffuse
  diffCol = clamp(dot(lightDir, normal), 0., 1.) * vec3(1.);
  
  return (diffCol + reflCol + specCol) * lightIntensity;
}

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

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

vec3 calcNormal(vec3 p) {
  vec2 e = vec2(1, -1) * EPSILON;
  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)
  );
}

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

float fresnel(vec3 rd, vec3 normal) {
  return pow(1. - clamp(dot(-rd, normal), 0., 1.), 5.);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = (fragCoord - 0.5 * iResolution.xy)/iResolution.y;
   
  vec2 mouseUv = iMouse.xy / iResolution.xy;
  if (mouseUv == vec2(0,0)) mouseUv = vec2(0.5, 0.5);
  
  vec3 lookat = vec3(0, 0, 0);
  vec3 ro = vec3(0, 0, 5);
  vec3 rd = camera(ro, lookat) *  normalize(vec3(uv, -1));
  vec3 rdOrigin = rd;
  rd.yz *= rotate2d(mix(-PI/2., PI/2., mouseUv.y));
  rd.xz *= rotate2d(mix(-PI, PI, mouseUv.x)); 
 
  vec3 col = vec3(0);
  vec3 lightPosa = vec3(5, 10, 0);
  vec3 lightPosb = vec3(-215, 10, 0);
  
  float d = rayMarch(ro, rd);
  if (d < MAX_DIST) {
    vec3 p = ro + rd * d;
    vec3 normal = calcNormal(p);
    vec3 lightDi_a = normalize(lightPosa - p);
    vec3 lightDi_b = normalize(lightPosb - p);
    
    vec3 a = phong(lightDi_a, 0.5, rd, normal);
    vec3 b = phong(lightDi_b, 0.3, rd, normal);
    vec3 f = fresnel(rd, normal) * vec3(1.) * 0.4;
    col = a + b + f;
  }
  else {
    col = texture(iChannel0, rd).xyz;
  }
  
  
  fragColor = vec4(col,1.0);
}

 

 

Reference

https://inspirnathan.com/posts/63-shadertoy-tutorial-part-16

 

Shadertoy Tutorial Part 16 - Cubemaps and Reflections

Use cubemaps in Shadertoy and add reflections to 3D objects!

inspirnathan.com

 

https://inspirnathan.com/posts/62-shadertoy-tutorial-part-15

 

Shadertoy Tutorial Part 15 - Channels, Textures, and Buffers

Learn how to use textures and create multi-pass shaders!

inspirnathan.com

 

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

12. RayMarching - rain and lights  (0) 2024.08.18
11. RayMarching - Snow man  (0) 2024.08.14
09. RayMarching - SD Operation  (0) 2024.08.12
08. RayMarching - Shadow  (0) 2024.08.10
07. RayMarching - Frenel effect  (0) 2024.08.10