패스트 캠퍼스에서 수강한 강의 내용 중 셰이더 함수에 대해 정리하였다.
우선 무작정 따라해볼 수 있도록 glsl에서 사용되는 함수들에 대해 알아볼 것이다.
개발 환경은 Vite+VanillaJS환경이다.
1. 그라데이션
precision mediump float;
varying vec2 vUv;
void main(){
// 1. 그라데이션
float x = vUv.x;
float y = vUv.y;
float col = x;
gl_FragColor=vec4(col, col, col, 1.0);
}

vUv는 vertex shader에서 가져온 값을 의미한다.
x좌표에 따라 color의 값이 0.0부터 1.0까지 변화한다.
2. 대각선 만들기
precision mediump float;
varying vec2 vUv;
void main(){
// 2. 대각선 그라데이션 만들기
float x = vUv.x;
float y = vUv.y;
float gradient = (x + y) / 2.0;
vec3 col = vec3(gradient);
gl_FragColor=vec4(col, 1.0);
}

만약 사선 모양의 그라데이션을 만들고 싶다면 위와 같이 코드를 수정해주면 된다.
3. step
precision mediump float;
varying vec2 vUv;
void main(){
// 3. step
float x = vUv.x;
float y = vUv.y;
float strength = step(0.5, x); // 기준점, 변수
vec3 col = vec3(strength);
gl_FragColor=vec4(col, 1.0);
}

step 함수는 첫번째 인자로 기준점을 받고 두번째 인자로 변수를 받는다. 변수가 기준점을 넘지 않으면 0.0을 리턴하고 기준점을 넘으면 1.0을 리턴한다.
4. min, max
precision mediump float;
varying vec2 vUv;
void main(){
// 4. min, max
float x = vUv.x;
float y = vUv.y;
float strength = min(0.5, x);
// float strength = max(0.5, x);
vec3 col = vec3(strength);
gl_FragColor=vec4(col, 1.0);
}
JavaScript의 Math.min, Math.max 함수와 같은 리턴값을 가진다.
4. min, max
precision mediump float;
varying vec2 vUv;
void main(){
// 4. min, max
float x = vUv.x;
float y = vUv.y;
float strength = min(0.5, x);
// float strength = max(0.5, x);
vec3 col = vec3(strength);
gl_FragColor=vec4(col, 1.0);
}


JavaScript의 Math.min, Math.max 함수와 같은 리턴값을 가진다.
5. clamp
precision mediump float;
varying vec2 vUv;
void main(){
// 5. clamp
float x = vUv.x;
float y = vUv.y;
// 0.3 이하에서는 0.3을 리턴, 0.7 이상에서는 0.7을 리턴, 0.3 ~ 0.7에서는 x를 리턴
float strength = clamp(x, 0.1, 0.8);
vec3 col = vec3(strength);
gl_FragColor=vec4(col, 1.0);
}

clamp 함수는 2개의 기준점을 가진다.x가 min값보다 작으면 min, min과 max 사이라면 x, max보다 크면 max를 리턴한다.
6. smoothstep
precision mediump float;
varying vec2 vUv;
void main(){
// 6. smoothstep
float x = vUv.x;
float y = vUv.y;
float strength = smoothstep(0.2, 0.8, x); // 내장 함수
vec3 col = vec3(strength);
gl_FragColor=vec4(col, 1.0);
}

step 함수에서 기준점을 그라데이션 처리하는 함수이다. 기준점의 간격이 늘어날수록 그라데이션 영역도 늘어난다.
7. mix
precision mediump float;
varying vec2 vUv;
void main(){
// 7. mix
float x = vUv.x;
float y = vUv.y;
vec3 green = vec3(0.0, 1.0, 0.0);
vec3 blue = vec3(0.0, 0.0, 1.0);
vec3 col = mix(green, blue, x);
gl_FragColor = vec4(col, 1.0);
}

mix 함수는 첫번째 인자와 두번째 인자를 세번째 인자의 비율로 보간한 값을 리턴해주는 함수이다.
8. pow, sqrt
precision mediump float;
varying vec2 vUv;
void main(){
// 8. pow, sqrt
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(x);
vec3 green=vec3(0.0, 1.0, 0.0);
if(pow(x, 2.0) - 0.005 <= y && y <= pow(x, 2.0) + 0.005){
col = green;
}
// if(sqrt(x) - 0.005 <= y && y <= sqrt(x) + 0.005){
// col = green;
// }
gl_FragColor=vec4(col, 1.0);
}


pow와 sqrt함수 역시 JavaScript의 Math.pow, Math.sqrt와 같다.
9. mod
precision mediump float;
varying vec2 vUv;
void main(){
// 9. mod: (a % b)
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(mod(x * 2.0, 1.0));
gl_FragColor=vec4(col, 1.0);
}

mod함수는 나머지를 반환해주는 함수이다. 첫번째 인자로 들어온 값을 두번째 인자로 나눈 나머지를 반환한다.
10. fract
- 소수점 이하의 값을 반환해주는 함수이다.
위 mod 함수는 아래 코드로도 대체할 수 있다.
precision mediump float;
varying vec2 vUv;
void main(){
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(fract(x * 2.0));
gl_FragColor=vec4(col, 1.0);
}

또한 아래 코드를 작성하면 이러한 효과를 만들 수도 있다.
precision mediump float;
varying vec2 vUv;
void main(){
// . fract: 소수점 이하의 값을 반환
float x = vUv.x;
float y = vUv.y;
// vec3 col = vec3(fract(x * y));
// col = step(0.5, col);
vec3 col = vec3(fract((x - 0.11) * 7.0));
vec3 col2 = vec3(fract((y - 0.11) * 7.0));
col = step(0.5, col) * step(0.5, col2);
fract(0.4); // 0.4
fract(2.3); // 0.3
gl_FragColor=vec4(col, 1.0);
}

11. sin, cos
- sin 함수와 cos 함수는 그래픽스에서 자주 사용되는 함수이다.
아래와 같이 코드를 작성한다.
precision mediump float;
varying vec2 vUv;
void main(){
// 11. sin, cos
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(sin(x * 1.0));
// vec3 col = vec3(cos(x * 20.0));
gl_FragColor=vec4(col, 1.0);
}
desmos 사이트에 가면 수학 그래프들을 그려볼 수 있다. 0과 1 사이에서 그래프를 따라 위와 같은 분포를 가진다고 생각하면 된다.
만약 그라데이션이 검은색부터 흰색까지 진행되다가 다시 검은색으로 돌아오는 그림을 그리고 싶다면 아래와 같이 코드를 작성하면 된다.
precision mediump float;
varying vec2 vUv;
void main(){
// 11. sin, cos
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(sin(x * 3.0));
// vec3 col = vec3(cos(x * 20.0));
gl_FragColor=vec4(col, 1.0);
}
비슷한 원리를 사용해서 코드를 아래와 같이 작성하면 다음과 같은 그림을 만들 수 있다.
11. sin, cos
- sin 함수와 cos 함수는 그래픽스에서 자주 사용되는 함수이다.
아래와 같이 코드를 작성한다.
precision mediump float;
varying vec2 vUv;
void main(){
// 11. sin, cos
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(sin(x * 1.0));
// vec3 col = vec3(cos(x * 20.0));
gl_FragColor=vec4(col, 1.0);
}


desmos 사이트에 가면 수학 그래프들을 그려볼 수 있다. 0과 1 사이에서 그래프를 따라 위와 같은 분포를 가진다고 생각하면 된다.
만약 그라데이션이 검은색부터 흰색까지 진행되다가 다시 검은색으로 돌아오는 그림을 그리고 싶다면 아래와 같이 코드를 작성하면 된다.
precision mediump float;
varying vec2 vUv;
void main(){
// 11. sin, cos
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(sin(x * 3.0));
// vec3 col = vec3(cos(x * 20.0));
gl_FragColor=vec4(col, 1.0);
}


비슷한 원리를 사용해서 코드를 아래와 같이 작성하면 다음과 같은 그림을 만들 수 있다.
precision mediump float;
varying vec2 vUv;
void main(){
// 11. sin, cos
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(sin(x * 20.0));
// vec3 col = vec3(cos(x * 20.0));
gl_FragColor=vec4(col, 1.0);
}


12. abs
절대값을 반환해주는 함수이다.
precision mediump float;
varying vec2 vUv;
void main(){
// 12. abs
float x = vUv.x;
float y = vUv.y;
vec3 col = vec3(abs(cos(x * 20.0)));
gl_FragColor=vec4(col, 1.0);
}


13. distance, length
두 정점 사이의 거리(길이)를 반환해주는 함수이다.
아래와 같이 코드를 작성해보자
precision mediump float;
varying vec2 vUv;
void main(){
// 13. distance: 두 정점 사이의 거리
float x = vUv.x;
float y = vUv.y;
float dist = distance(x, 0.5);
vec3 col = vec3(dist);
gl_FragColor=vec4(col, 1.0);
}

0.5에서부터 x위치까지의 거리를 반환해주어서 0.5와 가까울수록 어두운 색이고 멀어질수록 0.5와 가까운 색을 나타낸다.
precision mediump float;
varying vec2 vUv;
void main(){
// 13. distance: 두 정점 사이의 거리
float x = vUv.x;
float y = vUv.y;
float dist = distance(x, 0.5);
dist = step(0.3, dist);
vec3 col = vec3(dist);
gl_FragColor=vec4(col, 1.0);
}

이 함수에 step 함수를 사용하면 0.3을 기준으로 0과 1을 반환하는 그림을 그릴 수 있다.
precision mediump float;
varying vec2 vUv;
void main(){
// 13. distance: 두 정점 사이의 거리
float x = vUv.x;
float y = vUv.y;
// float dist = distance(x, 0.5);
float dist = distance(vec2(x, y), vec2(0.5));
dist = step(0.3, dist);
vec3 col = vec3(dist);
gl_FragColor=vec4(col, 1.0);
}

강의에 나온 함수들을 설명한 것이고 더 자세히는 The Book of Shaders에 가면 더 많은 함수들을 확인할 수 있다.
이렇게 함수들만 쭉 정리해본 이유는 이 데모의 코드를 분석해보고 구현해보기 위해서이다. 다음 글에서는 해당 데모의 코드를 분석해보고 직접 구현해보겠다.