도형들의 여러가지 연산 방법들의 대해서 정의 한다.
필요할때 마다 찾아서 사용하면 좋을 듯 하다
1) 도형 1, 도형 2를 합쳐서 출력.
float opUnion(float d1, float d2) {
return min(d1, d2);
}
2) 도형 1, 도형 2를 부드럽게 합쳐서 출력.
float opSmoothUnion(float d1, float d2, float k) {
float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
return mix( d2, d1, h ) - k*h*(1.0-h);
}
3) 도형 1, 도형 2의 공통 부분만 출력
float opIntersection(float d1, float d2) {
return max(d1, d2);
}
4) 도형 1, 도형 2의 공통 부분을 부드럽게 출력
float opSmoothIntersection(float d1, float d2, float k) {
float h = clamp( 0.5 - 0.5*(d2-d1)/k, 0.0, 1.0 );
return mix( d2, d1, h ) + k*h*(1.0-h);
}
5) 도형 2 에서 도형 1 부분을 뺀 영역을 출력
float opSubtraction(float d1, float d2) {
return max(-d1, d2);
}
6) 도형 2에서 도형 1부분을 뺀 영역을 부드럽게 출력
float opSmoothSubtraction(float d1, float d2, float k) {
float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
return mix( d2, -d1, h ) + k*h*(1.0-h);
}
7) 도형 1에서 도형 2을 뺀 영역을 출력
float opSubtraction2(float d1, float d2) {
return max(d1, -d2);
}
8) 도형 1에서 도형 2를 뺀 영역을 부드럽게 출력
float opSmoothSubtraction2(float d1, float d2, float k) {
float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
return mix( d1, -d2, h ) + k*h*(1.0-h);
}
9) +x, -x 값으로 대칭한 도형을 렌더링
float opSymX(vec3 p, float r, vec3 o)
{
p.x = abs(p.x);
return sdSphere(p, r, o);
}
10) +x, -x, +z, -z 값으로 대칭한 도형 여러개 렌더링
float opSymXZ(vec3 p, float r, vec3 o)
{
p.xz = abs(p.xz);
return sdSphere(p, r, o);
}
11) 도형을 c 간격으로 반복해서 출력
float opRep(vec3 p, float r, vec3 o, vec3 c)
{
vec3 q = mod(p+0.5*c,c)-0.5*c;
return sdSphere(q, r, o);
}
12) 도형을 반복해서 출력, c 는 도형 간격, l 은 반복하는 횟수
float opRepLim(vec3 p, float r, vec3 o, float c, vec3 l)
{
vec3 q = p - c * clamp(round(p/c),-l,l);
return sdSphere(q, r, o);
}
13) 도형의 외형을 변화
float opDisplace(vec3 p, float r, vec3 o)
{
float d1 = sdSphere(p, r, o);
float d2 = sin(p.x)*sin(p.y)*sin(p.z) * cos(iTime);
return d1 + d2;
}
전체 코드 >
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;
const vec3 COLOR_BACKGROUND = vec3(.741, .675, .82);
const vec3 COLOR_AMBIENT = vec3(0.42, 0.20, 0.1);
float scene(vec3 p);
mat3 cameraTransform(vec3 cameraPos, vec3 lookat) {
vec3 cd = normalize(lookat - cameraPos);
vec3 cr = cross(vec3(0, 1, 0), cd);
vec3 cu = cross(cd, cr);
return mat3(-cr, cu, -cd);
}
mat2 rotate2d(float theta) {
float s = sin(theta), c = cos(theta);
return mat2(c, -s, s, c);
}
float sdSphere(vec3 p, float r, vec3 offset)
{
return length(p - offset) - r;
}
float opUnion(float d1, float d2) {
return min(d1, d2);
}
float opSmoothUnion(float d1, float d2, float k) {
float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
return mix( d2, d1, h ) - k*h*(1.0-h);
}
float opIntersection(float d1, float d2) {
return max(d1, d2);
}
float opSmoothIntersection(float d1, float d2, float k) {
float h = clamp( 0.5 - 0.5*(d2-d1)/k, 0.0, 1.0 );
return mix( d2, d1, h ) + k*h*(1.0-h);
}
float opSubtraction(float d1, float d2) {
return max(-d1, d2);
}
float opSmoothSubtraction(float d1, float d2, float k) {
float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
return mix( d2, -d1, h ) + k*h*(1.0-h);
}
float opSubtraction2(float d1, float d2) {
return max(d1, -d2);
}
float opSmoothSubtraction2(float d1, float d2, float k) {
float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
return mix( d1, -d2, h ) + k*h*(1.0-h);
}
float opSymX(vec3 p, float r, vec3 o)
{
p.x = abs(p.x);
return sdSphere(p, r, o);
}
float opSymXZ(vec3 p, float r, vec3 o)
{
p.xz = abs(p.xz);
return sdSphere(p, r, o);
}
float opRep(vec3 p, float r, vec3 o, vec3 c)
{
vec3 q = mod(p+0.5*c,c)-0.5*c;
return sdSphere(q, r, o);
}
float opRepLim(vec3 p, float r, vec3 o, float c, vec3 l)
{
vec3 q = p - c * clamp(round(p/c),-l,l);
return sdSphere(q, r, o);
}
float opDisplace(vec3 p, float r, vec3 o)
{
float d1 = sdSphere(p, r, o);
float d2 = sin(p.x)*sin(p.y)*sin(p.z) * cos(iTime);
return d1 + d2;
}
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 depth = scene(p);
d += depth;
if (d < PRECISION || d > MAX_DIST) break;
}
return d;
}
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 scene(vec3 p) {
float d1 = sdSphere(p, 1., vec3(0, -1, 0));
float d2 = sdSphere(p, 0.75, vec3(0, 1., 0));
//return d1;
//return d2;
//return opUnion(d1, d2);
//return opSmoothUnion(d1, d2, 0.2);
//return opIntersection(d1, d2);
//return opSmoothIntersection(d1, d2, 0.2);
//return opSubtraction(d1, d2);
//return opSmoothSubtraction(d1, d2, 0.2);
//return opSubtraction2(d1, d2);
//return opSmoothSubtraction2(d1, d2, 0.2);
//return opSymX(p, 1., vec3(1, 0, 0));
//return opSymXZ(p, 1., vec3(1, 0, 1));
//return opRep(p, .6, vec3(0.), vec3(10));
//return opRepLim(p, 0.5, vec3(0), 2., vec3(1., 0, 0));
return opDisplace(p, 1., vec3(0));
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (fragCoord - 0.5*iResolution.xy)/iResolution.y;
vec3 col = vec3(0);
vec3 ro = vec3(0, 0.4, 5);
vec3 lp = vec3(0);
vec3 rd = normalize(vec3(uv, -1)) * cameraTransform(ro, lp);
vec3 lightPos = vec3(0, 10., 0);
float depth = rayMarch(ro, rd);
if (depth < MAX_DIST) {
vec3 p = ro + rd * depth;
vec3 lightDirection = normalize(lightPos - p);
vec3 normal = calcNormal(p);
float diffuse = clamp(dot(normal, lightDirection), 0.3, 1.);
float dif = clamp(dot(normal, lightDirection), 0., 1.) * 0.5 + 0.5;
col = vec3(dif) + COLOR_AMBIENT;
}
else {
col = COLOR_BACKGROUND;
}
fragColor = vec4(col,1.0);
}
Reference
https://inspirnathan.com/posts/60-shadertoy-tutorial-part-14
Shadertoy Tutorial Part 14 - SDF Operations
Learn how to draw complex shapes and scenes in Shadertoy!
inspirnathan.com
'Shader CG' 카테고리의 다른 글
11. RayMarching - Snow man (0) | 2024.08.14 |
---|---|
10. RayMarching - Cube map (0) | 2024.08.13 |
08. RayMarching - Shadow (0) | 2024.08.10 |
07. RayMarching - Frenel effect (0) | 2024.08.10 |
06. RayMarching - 퐁 라이팅 모델 (0) | 2024.08.08 |