본문으로 건너뛰기

텍스처 매핑 (贴图)_WebGL 노트 8

무료2016-01-01#JS#WebGL#webgl texture#webgl image map#webgl贴图#webgl多张图片纹理#webgl纹理映射

贴图란 이미지에서 색상을 추출하여 이를 프래그먼트에 대입하는 것이지만, 물론, 실제 조작은 매우 번거롭습니다

서론

[이전 노트](/articles/varying 变量与内插-webgl 笔记 7/) 에서, 우리는 varying 변수를 이용하여 색상을 프래그먼트 셰이더에 전달하여 그라데이션 효과를 구현했습니다. 더 유용한 색상 설정 방법이贴图 (텍스처 매핑) 입니다

一.원리

贴图란 이미지 중의 색상을 기하 도형에 매핑하는 것으로, 스텝은 다음과 같습니다:

  1. 정점 셰이더 중에서 각 정점에 텍스처 좌표를 지정

  2. 그 후 프래그먼트 셰이더 중에서 각 프래그먼트의 텍스처 좌표에 기반하여 텍스처 이미지에서 텍셀 색상을 추출

여기서도 varying 변수를 이용하여 텍스처 좌표를 전달합니다. moreover 내삽 프로세스가 있기 때문에, 이미지를 완벽하게 기하 도형에 씌울 수 있습니다

텍스처 좌표는 새로운 좌표계로, canvas 와 WebGL 좌표계와 모두 다릅니다. 원점은 canvas 왼쪽 아래 모서리에 있고, s 축은 오른쪽 방향이 양, t 축은 위쪽 방향이 양이며, canvas 오른쪽 위 모서리의 좌표는 (1.0, 1.0) 입니다

二.텍스처 매핑

특별히 주의: 이미지 포맷에는 제한이 없으며, 브라우저가 서포트하는 임의의 포맷 이미지를 사용할 수 있지만, 텍스처로서의이미지 사이즈는 2^mx2^n 이어야 합니다.否则 에러가 발생합니다:

WebGL: drawArrays: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'. Or the texture is Float or Half Float type with linear filtering while OES_float_linear or OES_half_float_linear extension is not enabled.

이것을NPOT 문제(Non Power Of Two) 라고 하며,贴图의 상식입니다. 비 2^mx2^n 사이즈의 도형도 붙일 수 있지만, 보통은 더 복잡하고 더 퍼포먼스를 소비합니다

##1.텍스처 좌표 설정

텍스처 좌표와 WebGL 좌표의联系를 확립하고, 정점에 대응하는 텍스처 좌표를 설정한 후, 프래그먼트 셰이더 실행 시 gl_FragColor = texture2D(u_Sampler, v_TexCoord); 로 텍셀을 추출하여 프래그먼트 색상을 설정하면, 도형이 붙여집니다

셰이더 소스 프로그램은 다음과 같습니다:

// 顶点着色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
    'attribute vec2 a_TexCoord;' +  // 接受纹理坐标
    'varying vec2 v_TexCoord;' +    // 传递纹理坐标
    'void main() {' +
    'gl_Position = a_Position;' +   // 设置坐标
    'v_TexCoord = a_TexCoord;' +    // 设置纹理坐标
'}';
// 片元着色器源程序
//!!! 需要声明浮点数精度,否则报错 No precision specified for (float) 
var fsSrc = 'precision mediump float;' +
    'uniform sampler2D u_Sampler;' +    // 取样器
    'varying vec2 v_TexCoord;' +        // 接受纹理坐标
    'void main() {' +
    'gl_FragColor = texture2D(u_Sampler, v_TexCoord);' + // 设置颜色
'}';

그 중에서 sampler2D 는 샘플러 타입으로, 이미지 텍스처는 최종적으로 이 타입의 오브젝트에 저장됩니다

프래그먼트 셰이더 중에서는 texture2D(sampler2D sampler, vec2 coord) 로 텍셀 색상을 추출하며, 첫 번째 파라미터는 텍스처 유닛 번호, 두 번째 파라미터는 텍스처 좌표이며, 리턴값은 gl.texImage2D 에传入한 internalformat 파라미터에 의해 결정되며, 만약 텍스처 이미지가 이용 불가이면, vec4(0.0, 0.0, 0.0, 1.0) 를 리턴합니다

##2.텍스처 구성 및 로드

이미지를 로드하는 방식은 여전히 new Image 이지만, WebGL 은크로스도메인 텍스처 이미지 사용을 허가하지 않습니다. 구체적인 스텝은 다음과 같습니다:

  1. 텍스처 오브젝트 생성

    // 创建 texture
    var texture = gl.createTexture();   // 创建纹理对象
    
  2. image 생성

    var image = new Image();
    
  3. image 에 load 이벤트 핸들러를 추가하고, 이벤트 핸들러 중에서 텍스처 구성

    image.onload = function() {
        //---加载纹理
        // 1.对纹理图像进行 y 轴反转
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
        // 2.开开启 0 号纹理单元
        gl.activeTexture(gl.TEXTURE0);
        // 3.向 target 绑定纹理对象
        gl.bindTexture(gl.TEXTURE_2D, texture);
        // 4.配置纹理参数
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        // 用图片边缘颜色填充空白区域
        // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        // 镜像填充(轴对称)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);
        // 5.配置纹理图像
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
        // 6.将 0 号纹理传递给着色器
        gl.uniform1i(u_Sampler, 0);
    
        // 绘制矩形
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, arrVtx.length / 4);
    };
    

텍스처 구성 프로세스는 비교적 복잡하며, 나중에 자세히 전개합니다

  1. image.src 에 값을 대입하여 이미지 로드

    image.src = 'miao256x128.png';
    

이미지 사이즈에 주의, 에러 정보 중의 non-power-of-2 는 텍스처로서 2^mx2^n 사이즈의 이미지를 사용해야 함을 알려줍니다

##3.내부 상태

그림과 같습니다:

[caption id="attachment_917" align="alignnone" width="605"]webgl-texture webgl-texture[/caption]

三.텍스처 구성

###1.텍스처 이미지의 y 축 반전

// 1.对纹理图像进行 y 轴反转
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

반전하지 않으면 붙인 이미지가 거꾸로 되어 있는 것을 발견하게 됩니다. "복"자를 붙이는 것과 같으며, WebGL 텍스처 좌표계 중의 t 축과 이미지 좌표계의 y 축은 방향이 반대이기 때문입니다. 이미지 좌표계는 우리가 만난 제 4 의 좌표계로, 좌표축/원점은 canvas 좌표계와 같습니다

물론, 수동으로 y 좌표를 반전할 수도 있으며, 프래그먼트 셰이더에서 y = 1.0 - y 하면 됩니다. 또는, 거꾸로 붙일 수도 있습니다.. 그러나 왜 이렇게 편리한 함수를 사용하지 않을까요?

gl.pixelStorei(pname, param)
---
pname
    gl.UNPACK_FLIP_Y_WEBGL 对图像进行 y 轴反转
    gl.UNPACK_PERMULTIPLY_ALPHA_WEBGL 给图像 rgb 色值的每一个分量乘以 A
param
    0 또는 비 0 정수

주의: 파라미터 값은 정수여야 하며, true/false 가 아닙니다

###2.x 호 텍스처 유닛开启

// 2.开开启 0 号纹理单元
gl.activeTexture(gl.TEXTURE0);

WebGL 은 텍스처 유닛의 메커니즘을 통해 여러 텍스처를 동시에 사용합니다. 각 텍스처 유닛은 1 장의 텍스처 이미지를 관리하며, gl.TEXTURE0~7 로 총 8 개이며, 즉 동시에 관리할 수 있는 텍스처 이미지는 최대 8 장까지입니다

텍스처 유닛을 사용하기 전에, gl.activeTexture(gl.TEXTURE0); 로 그것을激活해야 합니다

###3.텍스처 오브젝트 바인드

// 3.向 target 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);

WebGL 시스템에 텍스처 오브젝트가 어느 타입의 텍스처를 사용하는지 알립니다. 2 종류 텍스처 타입을 서포트: gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP. 버퍼 조작과 유사하며, 마찬가지로 target 을 지정하고, 파라미터 수 및 의미도 유사합니다

마찬가지로, WebGL 중에서는 텍스처 오브젝트를 직접 조작할 수 없으며, 텍스처 오브젝트를 텍스처 유닛에 바인드하고, 그 후 텍스처 유닛을 조작하여 텍스처 오브젝트를 조작해야 합니다

###4.텍스처 파라미터 구성

// 4.配置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 用图片边缘颜色填充空白区域
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// 镜像填充(轴对称)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT);

텍스처 이미지가 이미지에 매핑되는 구체적인 방식을 설정하고, 어떻게 텍스처 좌표에 기반하여 텍셀 색상을 획득하는가? 어느 방식으로 텍스처를 반복填充하는가? 즉 어떻게 내삽하여 프래그먼트를 생성하는가

texParameteri(target, pname, param)
---
target
    텍스처 타입, 값은 gl.TEXTURE_2D 또는 gl.TEXTURE_CUBE_MAP
pname
    텍스처 파라미터, 값은 gl.TEXTURE_MAG_FILTER 로 텍스처 사이즈가 도형 사이즈보다 작을 때 어떻게 텍스처를 확대하는가, 디폴트 값은 gl.LINEAR
        또는 gl.TEXTURE_MIN_FILTER 로 텍스처 사이즈가 도형 사이즈보다 클 때 어떻게 텍스처를 축소하는가, 디폴트 값은 gl.NEAREST_MIPMAP_LINEAR
        또는 gl.TEXTURE_WRAP_S 로 어떻게 텍스처 이미지의 왼쪽 또는 오른쪽 영역을填充하는가, 디폴트 값은 gl.REPEAT
        또는 gl.TEXTURE_WRAP_T 로 어떻게 텍스처 이미지의 위쪽과 아래쪽 영역을填充하는가, 디폴트 값은 gl.REPEAT
param
    파라미터 값, gl.TEXTURE_MAG_FILTER 와 gl.TEXTURE_MIN_FILTER 의 선택 가능 값은 gl.NEAREST 와 gl.LINEAR, 전자는 텍스처 상에서 매핑 후 픽셀 중심에 가장 가까운 픽셀의 색상 값을 사용하고, 후자는 새 픽셀 중심에 가장 가까운 4 개의 픽셀의 색상 값의 가중 평균을 사용하며, 효과는 더 좋지만, 오버헤드가 비교적 큽니다. 이 2 종은 비피라미드 텍스처이며, 피라미드 텍스처 (자주 사용하지 않음, 소개하지 않음) 도 있음
    gl.TEXTURE_WRAP_S 와 gl.TEXTURE_WRAP_T 의 선택 가능 값은 gl.REPEAT 平铺、gl.MIRRORED_REPEAT 镜像平铺、gl.CLAMP_TO_EDGE 텍스처 이미지 연변 값 사용

gl.TEXTURE_MIN_FILTER 의 디폴트 값은 특수한 피라미드 텍스처이므로, 수정해야 합니다 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);. 수정하지 않으면에러가 발생하고, 텍스처가 표시되지 않습니다

###5.텍스처 이미지 ���성

// 5.配置纹理图像
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);

텍스처 이미지를 텍스처 오브젝트에 할당하고, 동시에 WebGL 시스템에 텍스처 이미지에 대한 몇 가지 특성을 알립니다

gl.texImage2D(target, level, internalformat, format, type, image)
---
target
    텍스처 타입, 값은 gl.TEXTURE_2D 또는 gl.TEXTURE_CUBE_MAP
level
    0 을传入, 해당 파라미터는 피라미드 텍스처를 위해 준비되어 있으며, 일반적으로 이 파라미터는 사용하지 않음
internalformat
    이미지의 내부 포맷, format 값과 같아야 함
format
    이미지의 외부 포맷, 이미지 포맷에 기반하여 결정 jpg/bmp 는 gl.RGB, png 는 gl.RGBA, 회색조 도형은 gl.LUMINANCE 또는 gl.LUMINANCE_ALPHA, 이외에 gl.ALPHA 도 있음
type
    텍스처 데이터의 타입, 보통은 gl.UNSIGNED_BYTE 를 사용, 선택 가능 값에는 gl.UNSIGNED_SHORT_5_6_5, gl.UNSIGNED_SHORT_4_4_4_4, gl.UNSIGNED_SHORT_5_5_5_1 도 있음
image
    텍스처 이미지를 포함하는 image 오브젝트

###6.x 호 텍스처 유닛을 프래그먼트 셰이더 중의 샘플러 변수에 전달

// 6.将 0 号纹理传递给着色器
gl.uniform1i(u_Sampler, 0);

본예 중 샘플러는 uniform sampler2D 타입으로 선언되며, uniform 은 텍스처 이미지가 프래그먼트에 따라 변화하지 않기 때문이며, sampler2Dgl.TEXTURE_2D 에 대응하며, samplerCubegl.TEXTURE_CUBE_MAP 타입 텍스처에 사용됩니다

gl.uniform1i(u_Sampler, 0); 로 sampler2D 변수에 값을 대입하며, 1 은传入하는 성분 수를 나타내고, i 는 성분이 int 타입임을 나타냅니다. 이는 WebGL 의 관용적인 명명 방식으로, 예를 들어 gl.uniform4fv(name, value) 는 value 의 값은 4 차원 벡터 (4 개의 요소를 가진 형식화 배열) 임을 나타냅니다

四.DEMO

위 코드를 포함한 완전한 예에 대해서는, 다음을 참조하십시오:

다중 이미지 텍스처란 여러 장의 이미지를 동일한 영역에 붙이는 것으로, 예를 들어:

[caption id="attachment_918" align="alignnone" width="218"]webgl-texture-multi-image-example webgl-texture-multi-image-example[/caption]

중첩 효과는 2 개의 텍셀 색상 값 성분을 곱한 결과이며, vec4(r1, g1, b1, a1) * vec4(r2, g2, b2, a2), 결과 벡터는 vec4(r1 * r2, g1 * g2, b1 * b2, a1 * a2) 이므로, 색상 값의 왼쪽 곱하기 오른쪽 곱하기 효과는 같습니다

다중 이미지 텍스처의 내부 상태는 그림과 같습니다:

[caption id="attachment_919" align="alignnone" width="604"]webgl-texture-multi-image webgl-texture-multi-image[/caption]

五.정리

텍스처 매핑의 원리는 매우 간단: 텍스처 이미지에서 색상을 읽어와서, 이를 프래그먼트에 대입하며, varying 변수의 내삽이 착색의 균일을 보증합니다

그러나 조작이 비교적 복잡하므로, 우리는 다시 util 을 캡슐화했습니다. 인터페이스 설명은 다음과 같습니다:

/**
 * load texture image
 * by default, configure gl.TEXTURE_MIN_FILTER as gl.LINEAR
 *!!! can be overrode at callback
 * @param  {String}   imgPath  texture image path
 * @param  {Function} callback(image, unit) args: image object and texture unit number
 * @return
 * @throws {Error} If all texture unit was active now
 */
function loadTexture(imgPath, callback) {
    //...
}

callback 을 이용하여 유연성을 보증하며, 구체적인 구현 상세는 gl-util.js 를 참조하십시오

이로써, WebGL 의 가장 기초적인 것이 종료되었습니다. 다음편 노트는 GLSL ES 에 대한 상세한 설명이며, 그後は 끝없는 행렬 소용돌이입니다..

참고 자료

  • WebGL 프로그래밍 가이드》

댓글

아직 댓글이 없습니다

댓글 작성