GLSL 퀵 레퍼런스

0 ~ 1 사이의 값을 갖는 간단한 노이즈 함수

float random (in vec2 st) {
    return fract(sin(dot(st.xy,
        vec2(12.9898,78.233)))*
        43758.5453123);
}

// Based on Morgan McGuire @morgan3d
// https://www.shadertoy.com/view/4dS3Wd
float noise (in vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);

    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));

    vec2 u = f * f * (3.0 - 2.0 * f);

    return mix(a, b, u.x) +
            (c - a)* u.y * (1.0 - u.x) +
            (d - b) * u.x * u.y;
}

-1 ~ 1 사이의 값을 갖는 Simplex 노이즈 함수

// Some useful functions
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }

//
// Description : GLSL 2D simplex noise function
//      Author : Ian McEwan, Ashima Arts
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License :
//  Copyright (C) 2011 Ashima Arts. All rights reserved.
//  Distributed under the MIT License. See LICENSE file.
//  https://github.com/ashima/webgl-noise
//
float snoise(vec2 v) {

    // Precompute values for skewed triangular grid
    const vec4 C = vec4(0.211324865405187,
                        // (3.0-sqrt(3.0))/6.0
                        0.366025403784439,
                        // 0.5*(sqrt(3.0)-1.0)
                        -0.577350269189626,
                        // -1.0 + 2.0 * C.x
                        0.024390243902439);
                        // 1.0 / 41.0

    // First corner (x0)
    vec2 i  = floor(v + dot(v, C.yy));
    vec2 x0 = v - i + dot(i, C.xx);

    // Other two corners (x1, x2)
    vec2 i1 = vec2(0.0);
    i1 = (x0.x > x0.y)? vec2(1.0, 0.0):vec2(0.0, 1.0);
    vec2 x1 = x0.xy + C.xx - i1;
    vec2 x2 = x0.xy + C.zz;

    // Do some permutations to avoid
    // truncation effects in permutation
    i = mod289(i);
    vec3 p = permute(
            permute( i.y + vec3(0.0, i1.y, 1.0))
                + i.x + vec3(0.0, i1.x, 1.0 ));

    vec3 m = max(0.5 - vec3(
                        dot(x0,x0),
                        dot(x1,x1),
                        dot(x2,x2)
                        ), 0.0);

    m = m*m ;
    m = m*m ;

    // Gradients:
    //  41 pts uniformly over a line, mapped onto a diamond
    //  The ring size 17*17 = 289 is close to a multiple
    //      of 41 (41*7 = 287)

    vec3 x = 2.0 * fract(p * C.www) - 1.0;
    vec3 h = abs(x) - 0.5;
    vec3 ox = floor(x + 0.5);
    vec3 a0 = x - ox;

    // Normalise gradients implicitly by scaling m
    // Approximation of: m *= inversesqrt(a0*a0 + h*h);
    m *= 1.79284291400159 - 0.85373472095314 * (a0*a0+h*h);

    // Compute final noise value at P
    vec3 g = vec3(0.0);
    g.x  = a0.x  * x0.x  + h.x  * x0.y;
    g.yz = a0.yz * vec2(x1.x,x2.x) + h.yz * vec2(x1.y,x2.y);
    return 130.0 * dot(m, g);
}

좀더 고른 분포의 랜던 hash

uvec2 murmurHash21(uint src) {
  const uint M = 0x5bd1e995u;
  uvec2 h = uvec2(1190494759u, 2147483647u);
  src *= M;
  src ^= src>>24u;
  src *= M;
  h *= M;
  h ^= src;
  h ^= h>>13u;
  h *= M;
  h ^= h>>15u;
  return h;
}

// 2 outputs, 1 input
vec2 hash21(float src) {
  uvec2 h = murmurHash21(floatBitsToUint(src));
  return uintBitsToFloat(h & 0x007fffffu | 0x3f800000u) - 1.0;
}

smoothstep의 보간식

아래의 a와 b는 동일한 값이다.

float f = uv.x;
float a = smoothstep(0., 1., f);
float b = f * f * (3. - 2. * f);

위의 보간식을 cubic Hermite curve(큐빅 헤르미트 곡선)라고 한다. 0~1 사이의 영역에서 시작과 끝을 좀더 편평하게 만들어주는 quintic interpolation cuver(퀸틱 보간 곡선)에 대한 코드는 다음과 같다.

float f = uv.x;
float b = f * f * f * (6. * f * f - 15. * f + 10.);

modelMatrix로 변환된 normal 얻기

varying vec3 vNormal;

...

vNormal = mat3(transpose(inverse(modelMatrix))) * normal;

프레그먼트 쉐이더에서는 받은 vNormal을 반드시 정규화(normalize)해야 한다.

3초 주기로 0 ~ 1 사이의 연속된 값 얻기

uniform float u_time; // 0, 1, 2, ...의 값이며 각각 0초, 1초, 2초, ...를 나타냄

...

float period = 3.; // 3초 주기
float t = mod(u_time, period) / period; // 0 ~ 1 사이의 연속된 값

// sin 함수에 3.1415를 곱하는 이유는 sin 함수의 한 주기가 360도이기 때문임
vec3 color = mix(vec3(0), vec3(1), abs(sin(3.1415 * t)));
// or
vec3 color = mix(vec3(0), vec3(1), sin(3.1415 * t) * .5 + .5); 

원하는 각도를 이루는 선

아래의 이미지는 45도를 이루는 선인데, 이처럼 원하는 각도를 이루는 선을 만들기 위한 코드이다.

uniform vec3 uResolution;
uniform float uTime;
uniform vec4 uMouse;

#define PI (3.141592)

void main() {
  vec2 st = gl_FragCoord.xy / uResolution.xy;
  st = (gl_FragCoord.xy - .5 * uResolution.xy) / uResolution.y;

  float w = fwidth(st.y);
  float a = (45.) * PI / 180.0;
  float d = dot(st, vec2(cos(a), sin(a)));
  d = smoothstep(-w, w, abs(d));

  gl_FragColor = vec4(vec3(1. - d), 1.);
}

다각형 그리기

uniform vec3 uResolution;
uniform float uTime;
uniform vec4 uMouse;

#define PI 3.14159265359
#define TWO_PI 6.28318530718

void main(){
  vec2 st = gl_FragCoord.xy/uResolution.xy * 2. - 1.; //-1-1
  st.x *= uResolution.x/uResolution.y;
  vec3 color = vec3(0.0);
  float d = 0.0;

  // Number of sides of your shape
  int N = 6;
  // int N = int(floor(mod(uTime * 10., 20.))) + 3;

  // Angle and radius from the current pixel
  float a = atan(st.x,st.y)+PI;
  float r = TWO_PI/float(N);

  // Shaping function that modulate the distance
  d = cos(floor(.5+a/r)*r-a)*length(st);

  // color = vec3(d); // Distance Field
  color = vec3(1.0-step(.5, d)); // Fill
  // color = vec3(step(.5,d) * step(d,.51)); // Only outline

  gl_FragColor = vec4(color,1.0);
}

GLSL 코드

다음과 같은 결과를 프레그먼트 쉐이더로 작성한다면 … ? 즉, 화면의 반을 가르고 가른 영역의 중심을 원점으로 삼으며 원점을 기준으로 gl_FragCoord 지점에 대한 좌표(x,y)에 대한 각도(atan(y,x))에 대해 왼쪽 영역은 cos 값으로, 오른쪽 영역은 sin 값으로 채움.

방식은 여러가지겠지만 여기서는 2가지 구현 코드를 언급함. 첫번째는 제시된 문제를 그대로 해석해 풀이한 것.

uniform vec2 iResolution;

void main2() {
    vec2 uv = gl_FragCoord.xy / iResolution;
  
    vec2 origin = uv.x < 0.5 ? vec2(0.25, 0.5) : vec2(0.75, 0.5);
    vec2 v = uv - origin;
  
    float angle = atan(v.y, v.x);
    float c = cos(angle);
    float s = sin(angle);
    
    vec3 color = uv.x < 0.5 ? vec3(c) : vec3(s);
    
    gl_FragColor = vec4(color, 1.0);
}

두번째는 삼각함수의 원리를 이해하고 상황에 맞게 최적화해 구현한 것.

uniform vec2 iResolution;

void main() {
  vec2 uv = gl_FragCoord.xy / iResolution.xy;

  vec2 normal = uv.x > .5 ? 
    normalize(uv - vec2(0.75, 0.5)) : 
    normalize(uv - vec2(0.25, 0.5));
  
  float t = uv.x > .5 ?  normal.y : normal.x;

  gl_FragColor = vec4(vec3(t), 1.0);
}