본문 바로가기

WebGL

[WebGL + Three] Textures (텍스쳐 다루기)

 

 

 

 

텍스쳐 사용하기

 

먼저 텍스쳐 정보는 glsl의 내장 타입인 'Sampler2D'로 인식할 수 있습니다.

 

Sampler2D의 경우, 텍스쳐 정보와 텍스쳐 좌표계 (이하, uv 좌표계, uv-coordinate)를 인자로 삼아 각 uv 좌표계에 대응하는 색상 채널값을 반환합니다. 따라서 vec4로 타입캐스팅이 가능합니다.

 

텍스쳐 정보는 Three.js의 Texture 로더를 통해 이미지를 텍스쳐화 시켜 uniform값으로써, 쉐이더에 넘겨주도록 하겠습니다.

 

    const loader = new THREE.TextureLoader();
    const texture = loader.load('./concentrated.jpeg');
    
    const material = new THREE.ShaderMaterial({
      uniforms: {
        diffuse: { value: texture }
      },
      vertexShader: await vsh.text(),
      fragmentShader: await fsh.text()
    });
    const geometry = new THREE.PlaneGeometry(1, 1);
    const plane = new THREE.Mesh(geometry, material);

 

텍스쳐 정보를 plane에 입혀야합니다. 즉, 텍스쳐 정보를 planeGeometry에 매핑해야함으로, Vertext shader에서 planeGeometry의 uv 좌표 정보를 Fragment Shader에 넘겨줄 필요가 있습니다.

 

varying vec2 vUvs;
void main(){
  vec4 localPosition=vec4(position,1.);
  
  gl_Position=projectionMatrix*modelViewMatrix*localPosition;
  vUvs=uv; 
}

 

 

이제 geometry의 정점 정보를 넘겨 받았으니, Fragment shader에서 텍스쳐 색상 정보를 매핑해주고 gl_FragColor에 등록해주기만 하면 끝입니다.

 

varying vec2 vUvs;
uniform sampler2D diffuse;
void main(){
    gl_FragColor=texture2D(diffuse,vUvs)
}

 

 

결과

 

 

텍스쳐 블렌딩

1.  색상 블렌딩

텍스쳐에서 컬러 채널을 뽑음으로써, 저희는 이제 텍스쳐의 색상에 관한 관리자 권한을 얻었다 봐도 무방합니다.

 

스마트폰 카메라 보면 기본 옵션으로 사진에 대해 여러 색상 변조 효과를 넣을 수 있습니다.

이를 이제 구현할 수 있습니다.

 

사실 별게 있는게 아니라, 텍스쳐 정보에서 뽑아낸 색상 정보에 원하는 값을 더하고  곱하는 등, 여러 조작을 하는 것입니다.

 

저는 빈티지 감성을 좋아하므로, 아래 블로그에 나온 포토샵으로 빈티지 필터 만드는법을 참고하여 빈티지 필터를 만들어 보겠습니다.

 

https://adstorepost.com/71

 

빈티지한 느낌으로 포토샵 보정하기. 포토 필터(Photo Filter)를 이용하여 연출하는 레트로 스타일

최근 많은 카메라 어플리케이션에서 가장 인기 있는 필터 중 하나가 빈티지한 느낌을 연출하는 것들입니다. 몇 번의 터치만으로 부드러운 느낌을 주는 이 효과를 연출 하는 만큼포토샵으로 수

adstorepost.com

 

위 블로그의 진행과정을 간단히 정리하면 아래와 같습니다.

  1. 파란 필터를 씌운다
  2. exclusion 알고리즘을 적용한다
  3. 파란 필터의 투명도를 50%로 한다. (그냥 1번 과정에 넣어도 될듯합니다.)

 

 

먼저 파란색상값 코드를 뽑아낸 후 정규화 시켰을 때, 색상값은 아래와 같습니다.

  vec4 blueChannel=vec4(.01176470588,.42745098039,1.,1.);

 

이제 퍼렁 필터를 씌워볼까요?

 

포토샵의 기본 필터 알고리즘은 modulation, 즉 벡터 컴포넌트 곱연산입니다.

 

void main(){
  vec4 diffuseSample=texture2D(diffuse,vUvs);
  vec4 blueChannel=vec4(.01176470588,.42745098039,1.,1.);
  gl_FragColor = diffuseSample * blueChannel;
  }

 

 

오?  참고한 블로그 진행과정에서 나온 이미지와 같습니다.

 

참고한 블로그에선, 이제 블렌드 모드를 exclusion 모드로 바꾸라고합니다.

 

exclusion 알고리즘의 관한 더 많은 정보는 이 글에서 참고하시면 될 듯합니다.

 

공식은 다음과 같습니다.

F(x,y) = x + y - (2*x*y)

 

 

이제 해당 알고리즘을 구현하고 적용시키고 , 블루 필터 투명도를 0.5로 낮추겠습니다.

 

vec3 blendExclusion(vec3 base,vec3 blend){
  return base+blend-2.*base*blend;
}
void main(){
  

  vec4 diffuseSample=texture2D(diffuse,vUvs);
  vec4 blueChannel=vec4(.01176470588,.42745098039,1.,1.);
  vec4 filtered=(diffuseSample+blueChannel)-(2.*diffuseSample*blueChannel);
  gl_FragColor=vec4(blendExclusion(diffuseSample.xyz,blueChannel.xyz*.5),1
 }

완성!

 

 

 

2.  텍스쳐 오버레이

지금까지 색상 블랜딩만 진행했는데, 텍스쳐 끼리 블렌딩도 가능할 것 같습니다.

 

왜냐면 결국 저희가 쓰는건, 텍스쳐에서 추출한 색상 정보이기 때문에, 색상 끼리 블렌딩 할 수 있는건 위에서 확인했기에 가능합니다.

 

직관적으로 생각했을때, 두 텍스쳐 컬러값을 곱해주면 될듯합니다.

 

varying vec2 vUvs;
uniform sampler2D diffuse;
uniform sampler2D overlay;

void main(){

  vec4 diff=texture2D(diffuse,vUvs);
  vec4 over=texture2D(overlay,vUvs);
  gl_FragColor=diff*over;
 }

 

 

오... 뭔가 css cliping / masking 과 비슷한 결과가 나왔습니다.

 

조금만 조작하면 구현도 가능할듯합니다만, 제가 원하는건 이미지 끼리 겹치는 것이기에 읽으시는 분들은 한번 도전해보세요.

 

팁을 드리자면, 마스킹의 원리는, 하얀색 혹은 검은색 마스크 영역에 대해 겹치는 부분만 출력하는 것입니다.

 

이미지 끼리 겹치게 하려면, 이미지의 투명도에 의존하여 두 텍스쳐를 섞으면 됩니다.

 

void main(){
  vec4 diff=texture2D(diffuse,vUvs);
  vec4 over=texture2D(overlay,vUvs);  
  gl_FragColor=mix(diff,over,over.w);
}

 

mix함수는 (x,y,t)에 대해, x,y를 t퍼센트 삼아 섞은 값을 리턴합니다.

mix = ty - x(1-t) 

 

(x= 배경이미지 , y = 타원이미지)

타원 영역의 이미지 외측 영역은 투명도가 0인 부분입니다.

 

따라서, 두 이미지를 섞을 때,  y외측은 투명도가 0 이므로, x이미지가 그대로 출력 됩니다.

 

 

 

텍스쳐 변형

 

css 에서 사용하는 rotate,  scale과 같은 속성값을 glsl을 통해 구현가능합니다.

 

색상값은 uv 좌표에 매핑 되기에, uv좌표를 조작하는 것은 곧 변형을 야기합니다.

 

*{
	transform : transformX(-180deg);
}

 

 

void main(){
  vec2 uv=vec2(vUvs.x,vUvs.y)*vec2(-1.,1.);// -1 = flip
  vec4 diff=texture2D(diffuse,uv);
  gl_FragColor=vec4(diff);
}

 

uv 좌표계에 x값에 -1을 곱했습니다.

 

good!

 

확대의 경우, uv좌표계의 범위를 축소시키면 됩니다.

축소는 반대겠지요?

 

  vec2 uv=vec2(vUvs.x,vUvs.y)*.2; // 5배 확대

 

 

*참고*

three.js에서는 확대, 축소 시, 픽셀에 대한 보간필터를 어떻게 정의할 것인가에 대한 옵션이 있습니다.

 

기본적으로 확대 시, linearFiltering이 적용 되고, 다른 옵션으로는 nearestFiltering이 존재합니다.

퍼포먼스적으로는 nearestFiltering이 더 좋으므로, 결과에 큰 영향을 미치지 않는다면 바꾸는 것을 고려해볼만 합니다.

 

축소 시, mipmapping 이 적용됩니다. 각각의 알고리즘에 대해선, 다른 글로 이야기하도록 하겠습니다.

 

축소 시, uv 범위 밖의 값에 대해선 기본적으로 clamping 되기에 이상한 결과가 나옵니다.

 

 

영화 인터스텔라에서 나올법한 결과가 나왔습니다.

 

이는, webgl이 텍스쳐 uv 범위 밖의 값에 대해 범위의 양측 극한 값으로 값들을 매핑해버리기 때문입니다.

 

THREE.js 옵션을 통해 이에 대한 로직을 바꿀 수 있습니다.

 

기본적으로 three.js에선 ClampToEdgeWrapping방식을 사용합니다만, repeatWrapping으로 바꿔보겠습니다.

 

    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;

 

 

css 속성 중 background-repeat와 관련있어보이네요!

 

옵션은 three docs의 Wrapping mode 항목을 참조하시면 됩니다.

 

 

 

'WebGL' 카테고리의 다른 글

[WebGL] Hemisphere Lighting  (0) 2023.07.09
[WebGL] Ambient light  (0) 2023.07.09
[WebGL + Three.js] GLSL - colors  (1) 2023.03.23
[Graphics] Transformation Pipeline (local ~ screen 좌표계)  (0) 2023.03.19
[WebGL] GLSL - 기초  (2) 2023.03.16