GLSL, 무지개 색 표현

수평으로 7개의 무지개 색을 표현하는 프레그먼트 쉐어디 코드입니다. 배열 자료형을 사용하고 있는 예제 코드입니다.

uniform vec2 uResolution;
uniform float uTime;
uniform vec2 uMouse;

vec3 rainbow(float t) {
    vec3 colors[7];

    colors[0] = vec3(1.0, 0.0, 0.0); // 빨강
    colors[1] = vec3(1.0, 0.5, 0.0); // 주황
    colors[2] = vec3(1.0, 1.0, 0.0); // 노랑
    colors[3] = vec3(0.0, 1.0, 0.0); // 초록
    colors[4] = vec3(0.0, 0.0, 1.0); // 파랑
    colors[5] = vec3(0.29, 0.0, 0.51); // 남색
    colors[6] = vec3(0.56, 0.0, 1.0); // 보라
    
    int index = int(t * 7.0);
    return colors[index];
}

void main() {
    vec2 st = gl_FragCoord.xy/uResolution.xy;
    vec3 color = rainbow(st.x);
        
    gl_FragColor = vec4(color,1.0);
}

결과는 다음과 같습니다.

PostgreSQL JSON

원문출처 : http://www.postgresqltutorial.com/postgresql-json/

이 튜토리얼은 PostgreSQL의 JSON 데이터 타입을 어떻게 처리하는지 설명한다. 추가적으로 JSON 데이터를 처리하기 위한 가장 기본적인 연산자와 함수에 대해서도 설명한다.

JSON은 JavaScript Object Notation이다. JSON으로 다양한 데이터를 표현할 수 있지만 특히 키-값으로 구성된 쉽게 해석할 수 있는 텍스트 형식이다. JSON의 주요 사용처는 서버와 웹 어플리케이션 사이의 데이터 교환 및 전송이다. 바이너리 형식과 다르게 JSON은 사람이 이해하고 읽을 수 있는 텍스트 형식이다.

PostgreSQL은 9.2버전 이후부터 JSON 데이터 타입을 지원하며 JSON 데이터를 조작하기 위한 다양한 함수와 연산자를 제공한다.

JSON 데이터에 대한 이해를 위해 테이블을 생성하는 것부터 시작해 보자.

CREATE TABLE orders (
    ID serial NOT NULL PRIMARY KEY,
    info json NOT NULL
);

orders 테이블은 2개의 컬럼으로 구성 되어져 있는데, id는 순서를 나타내는 주 키이며 info는 JSON 형식으로 데이터가 저장될 컬럼이다.

JSON 데이터 INSERT

JSON 컬럼에 데이터를 입력하기 위해서 올바른 JSON 포맷의 데이터가 필요하다. 다음 INSERT 구문은 orders 테이블에 새로운 레코드를 추가한다.

INSERT INTO orders (info)
VALUES
(
    '{ "customer": "John Doe", "items": {"product": "Beer","qty": 6}}'
);

위의 데이터는 6병의 맥주를 가진 John Doe를 의미한다.

마찬가지로 여러 개의 레코드를 추가해 보자.

INSERT INTO orders (info)
VALUES
(
    '{ "customer": "Lily Bush", "items": {"product": "Diaper","qty": 24}}'
),
(
    '{ "customer": "Josh William", "items": {"product": "Toy Car","qty": 1}}'
),
(
    '{ "customer": "Mary Clark", "items": {"product": "Toy Train","qty": 2}}'
);

JSON 데이터 쿼리

JSON 데이터를 쿼리하기 위해서 일반적인 다른 데이터 타입을 쿼리 하듯이 SELECT 구문을 사용한다.

SELECT
 info
FROM
 orders;

PostgreSQL은 그 결과를 JSON 형태의 집합으로 반환한다.

PostgreSQL은 JSON 데이터에 대한 쿼리를 돕기 위해 ->과 ->> 연산자를 제공한다.


  • “->” 연산자는 키로 JSON 데이터 필드를 반환한다.

  • “->>” 연산자는 텍스트로 JSON 데이터 필드를 반환한다.

다음 쿼리는 JSON 형식으로 모든 고객 정보를 얻기 위해 -> 연산자를 사용한다.

SELECT
    info -> 'customer' AS customer
FROM
    orders;

그리고 ->> 연산자를 사용하는 다음의 쿼리문은 텍스트 형식으로 모든 고객 정보를 반환한다.

SELECT
    info ->> 'customer' AS customer
FROM
    orders;

-> 연산자가 JSON 객체를 반환하므로, 연관된 노드를 반환시키기 위해 ->> 연산자와 함께 연결 호출이 가능하다. 예를들어 아래의 구문은 판매된 모든 제품을 반환한다.

SELECT
    info -> 'items' ->> 'product' as product
FROM
    orders
ORDER BY
    product;

먼저 info->’items’는 JSON 객체로써 항목을 반환한다. 그런 뒤 info->’items’->>’product’는 텍스트 형식으로 모든 product를 반환한다.

WHERE 구문에서 JSON 연산자 사용

조건 검색을 지정하기 위해 WHERE 구문에서 JSON 연산자를 사용할 수 있다. 예를들어 Diaper를 가져온 사람을 얻고자 한다면 다음 쿼리를 사용한다.

SELECT
    info ->> 'customer' AS customer
FROM
    orders
WHERE
    info -> 'items' ->> 'product' = 'Diaper'

또 다른 예로써, 다음의 쿼리는 2개의 product를 가지고 있는 레코드를 조회한다.

SELECT
    info ->> 'customer' AS customer,
    info -> 'items' ->> 'product' AS product
FROM
    orders
WHERE
    CAST( info -> 'items' ->> 'qty' AS INTEGER ) = 2

위의 쿼리문에서 주목해야 할 부분은 qty 필드를 숫자 2와 비교하기 위해 INTEGER로 형변환(Casting)했다는 것이다.

JSON 데이터에 집계 함수 적용하기

MIN, MAX, AVERAGE, SUM 등과 같은 집계 함수를 JSON 데이터에 적용할 수 있다. 예를 들어 다음 구문은 최소 개수, 최대 개수, 평균 개수와 전체 판매된 상품의 개수에 대한 쿼리문이다.

SELECT
    MIN (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
    ),
    MAX (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
    ),
    SUM (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
    ),
    AVG (
        CAST (info -> 'items' ->> 'qty' AS INTEGER)
     )
FROM orders

PostgreSQL의 JSON 함수

PostgreSQL은 JSON 데이터를 처리하는데 도움이 되는 함수들을 제공한다.

json_each 함수

json_each 함수는 키-값의 집합으로 JSON 객체를 확장시킨다. 다음 구문을 보자.

SELECT
    json_each (info)
FROM
    orders;

만약 문자형으로 키-값의 집합을 얻고자 한다면, json_each_text 함수를 사용하면 된다.

json_object_keys 함수

JSON 객체의 키 집합을 얻고자 한다면, json_object_keys 함수를 사용한다. 다음의 쿼리는 info 컬럼 안의 items 객체의 모든 키를 반환한다.

SELECT
    json_object_keys (info->'items')
FROM
    orders;

json_typeof 함수

json_typeof 함수는 문자열로 JSON 값의 타입을 반환하며, 반환값은 number, boolean, null, object, array 그리고 string이다.

다음 쿼리문은 항목의 데이터 타입을 반환한다.

SELECT
    json_typeof (info->'items')
FROM
    orders;

다음의 쿼리문은 중첩되어진 JSON 객체의 qty 필드의 데이터 타입을 반환한다.

SELECT
    json_typeof (info->'items'->'qty')
FROM
    orders;

JSON 함수에 대해 좀더 깊이 알고자 한다면, PostgreSQL JSON functions을 참고하기 바랍니다.

이 튜토리얼에서는, PostgreSQL JSON 데이터 타입에 대해 살펴보았다. 또한 JSON 데이터를 좀더 효율적으로 처리하기 위해서 가장 많이 활용되는 JSON 연산자와 함수에 대해서도 살펴보았다.

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);
}

Javascript의 스프레드 연산자(spread operator)를 적용해 보자

스프레드 연산자를 적용할 수 있는 클래스 AA가 있다고 할때 활용 예는 다음과 같다.

const aa = new AA();

aa.addItem(1);
aa.addItem(2);
aa.addItem(3);

console.log(...aa)

콘솔에 1 2 3이 찍힌다. 바로 … 연산자가 스프레드 연산자이다. 이처럼 스프레드 연산자를 적용할 수 있도록 하기 위해서 AA 클래스는 다음처럼 작성되어야 한다.

class AA {
  constructor() {
    this.items = []; // 이터러블한 내용을 저장할 배열
  }

  // 아이템을 추가하는 메소드
  addItem(item) {
    this.items.push(item);
  }

  // Symbol.iterator를 구현하여 이터러블하게 만듭니다.
  [Symbol.iterator]() {
    let index = 0;
    let data  = this.items;
    return {
      next: () => {
        if (index < data.length) {
          return { value: data[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
}

참고로 스프레드 연산자는 매우 유용한 문법으로, 배열이나 이터러블 객체의 요소를 개별 요소로 확장하거나, 함수 호출 시 인수로 사용하거나, 객체 리터럴에서 속성을 복사할 때 사용된다. 예를들어 아래의 코드의 예시가 있다.

사례1

let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

사례2

function sum(x, y, z) {
  return x + y + z;
}

let numbers = [1, 2, 3];
console.log(sum(...numbers)); // 6

사례3

let obj1 = { foo: 'bar', x: 42 };
let obj2 = { ...obj1, y: 1337 }; // { foo: 'bar', x: 42, y: 1337 }

스프레드 연산자는 얕은 복사를 수행한다는 점을 유념하자. 위의 예제를 보면 알겠지만 스프레드 연산자는 코드를 더욱 간결하고 가독성을 높여주며 데이터 구조를 쉽게 조작할 수 있게 해준다. 하지만 얕은 복사라는 점을 다시 한번 더 유념하자.

GLSL의 mat4 타입에 대한 정리

mat4는 4×4 행렬입니다. 열을 우선(Column major) 순서로 해서 각 요소가 저장됩니다. 아래처럼요.

그래서 행렬의 첫번째 열과 세번째 열은 다음처럼 vec4 로 얻을 수 있습니다.

vec4 col1 = m[0];
vec4 col4 = m[3];

반면 첫번째 행과 세번째 행은 다음처럼 얻을 수 있습니다.

vec4 row1 = vec4(m[0][0], m[1][0], m[2][0], m[3][0]);
vec4 row4 = vec4(m[0][3], m[1][3], m[2][3], m[3][3]);

행렬의 특정 요소의 접근은, 예를들어 3번째 열의 4번째 행 요소는 다음과 같습니다.

float e = m[2][3];

행렬의 연산은 벡터의 수학적인 곱으로 많이 사용되는데 일반적으로는 다음과 같습니다.

vec4 v = ...;
mat4 m = ...;
vec4 t = m * v;

행렬과 벡터의 곱 순서를 다음처럼 바꿀 수 있는데, 그 결과는 위와 다릅니다.

vec4 t2 = v * m;

위처럼 벡터에 행렬을 곱하면 행렬을 전치시킨 것으로써, 위의 코드는 아래의 코드와 동일합니다.

vec4 t2 = traverse(m) * v;

행렬의 각 요소별 곱은 matrixCompMult이며 함수 이름에서 알 수 있듯이 거의 사용되지 않습니다.

마지막으로 역행렬을 구하는 함수는 inverse 입니다.