본문 바로가기
셰이더 (Shader)/The Book of Shaders (완)

[GLSL] 10 - Random

by Minkyu Lee 2023. 4. 25.

랜덤은 엔트로피의 최대 표현이다.

코드에서 어떻게 랜덤을 생성할 수 있을까?

다음 함수를 분석해보자.

y = fract(sin(x)*1.0);

sin 값은 -1.0과 1.0 사이에서 변동한다.

소수점 뒤를 잘라서 0.0과 1.0 사이의 모든 양수 값으로 만들었다.

이를 사용하면, 사인파를 잘게 쪼개서 랜덤 값을 얻을 수 있다.

sin(x)의 뒤에 큰 숫자를 곱하는 것이다.

100000.0만큼 곱하면 더 이상 사인파를 식별할 수 없게 된다.

 

Controlling chaos

랜덤 사용시 주의할 점이 있다.

충분히 무작위적이지 않을 수 있다.


자세히 살펴보면 -1.5707과 1.5707에서 sin() 형태가 보인다.

이 지점은 사인파의 최대값과 최소값이 발생하는 지점이기 때문이다.
또한 분포가 가장자리에 비해 가운데에 값이 집중되어 있다.

 

 

y = rand(x); // 상단 좌측 이미지
y = rand(x)*rand(x); // 상단 우측 이미지
y = sqrt(rand(x)); // 하단 좌측 이미지
y = pow(rand(x),5.); // 하단 우측 이미지

 

몇 가지 함수를 추가하면 분포를 변경할 수 있다.

 

우리의 rand() 함수는 deterministic random(결정론적 무작위)이다.

예를 들면, rand(1.0)은 항상 같은 값을 반환한다.

 

Pixelero가 무작위 분포에 관한 흥미로운 글을 읽어보라.

Pixelero는 non-deterministic(비결정론적인) 액션스크립트 함수 Math.random()을 참조하였다.

이는 호출할 때마다 매번 다른 값을 반환한다.

 

2D Random

이를 2차원인 x축과 y축 모두에 적용해보자.

이를 위해 1차원 값으로 변환하는 방법이 필요하다.

방법은 여러 가지가 있지만 dot() 함수가 유용하다.

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

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    float rnd = random( st );

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

Using the chaos

2차원의 랜덤 활용하는 방법을 알아보자.
첫 번째 단계는 그리드를 만드는 것이다.

floor() 함수를 사용하여 셀의 정수 테이블을 생성한다.

 

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

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;

    st *= 10.0; // Scale the coordinate system by 10
    vec2 ipos = floor(st);  // get the integer coords
    vec2 fpos = fract(st);  // get the fractional coords

    // Assign a random value based on the integer coord
    vec3 color = vec3(random( ipos ));

    // Uncomment to see the subdivided grid
    // color = vec3(fpos,0.0);

    gl_FragColor = vec4(color,1.0);
}

좌표에 10을 곱한다.

ipos를 보라. 정수만 분리하였다.

fpos를 보라. 소수점 이하 부분만 분리하였다.

 

정수를 사용하여 해당 영역에 대한 임의의 값을 얻었다. (vec3 color)

소수점 이하 부분을 이용해, 각 셀 내부에 사물을 그리는 좌표계로 계속 사용할 수 있다.

미로 생성기 예제를 살펴보자.

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

vec2 truchetPattern(in vec2 _st, in float _index){
    _index = fract(((_index-0.5)*2.0));
    if (_index > 0.75) {
        _st = vec2(1.0) - _st;
    } else if (_index > 0.5) {
        _st = vec2(1.0-_st.x,_st.y);
    } else if (_index > 0.25) {
        _st = 1.0-vec2(1.0-_st.x,_st.y);
    }
    return _st;
}

void main() {
    vec2 st = gl_FragCoord.xy/u_resolution.xy;
    st *= 10.0;
    // st = (st-vec2(5.0))*(abs(sin(u_time*0.2))*5.);
    // st.x += u_time*3.0;

    vec2 ipos = floor(st);  // integer
    vec2 fpos = fract(st);  // fraction

    vec2 tile = truchetPattern(fpos, random( ipos ));

    float color = 0.0;

    // Maze
    color = smoothstep(tile.x-0.3,tile.x,tile.y)-
            smoothstep(tile.x,tile.x+0.3,tile.y);

    // Circles
    // color = (step(length(tile),0.6) -
    //          step(length(tile),0.4) ) +
    //         (step(length(tile-vec2(1.)),0.6) -
    //          step(length(tile-vec2(1.)),0.4) );

    // Truchet (2 triangles)
    // color = step(tile.x,tile.y);

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

셀의 랜덤 값을 응용하였다.

truchetPattern() 함수를 사용하여 한 방향 또는 다른 방향으로 선을 그었다.

 

st 아래 부분의 주석을 해제해보자.

패턴에 애니메이션을 적용할 수 있다.


Circles 부분의 주석을 해제해보자.

또 다른 형태의 패턴을 볼 수 있다. 

 

Master Random

일본의 일렉트로닉 작곡가이자 비주얼 아티스트인 이케다 료지는 랜덤 사용 사례를 소개한다.

여러가지 응용법도 소개한다.

 

이는 본문을 참고하자.

https://thebookofshaders.com/10/

 

랜덤은 자연스러운 시뮬레이션을 만들고자 하는 경우 문제가 될 수 있다.

랜덤은 너무 혼란스럽다. 현실에서 이런 것은 거의 없다.

 

그 이유는 무엇일까?

랜덤은 서로 상관관계가 전혀 없다.

하지만 대부분의 자연 패턴은 이전 상태에 대한 연속성을 가진다.

다음 장에서는 부드럽고 자연스럽게 보이는 랜덤인, 노이즈에 대해 알아본다.

'셰이더 (Shader) > The Book of Shaders (완)' 카테고리의 다른 글

[GLSL] 12 - Cellular Noise  (0) 2023.05.03
[GLSL] 11 - Noise  (0) 2023.04.26
[GLSL] 9 - Patterns  (0) 2023.04.20
[GLSL] 8 - 2D Matrices  (1) 2023.04.20
[GLSL] 7 - Shapes  (0) 2023.04.19

댓글