서론
[이전 노트](/articles/varying 变量与内插-webgl 笔记 7/) 에서, 우리는 varying 변수를 이용하여 색상을 프래그먼트 셰이더에 전달하여 그라데이션 효과를 구현했습니다. 더 유용한 색상 설정 방법이贴图 (텍스처 매핑) 입니다
一.원리
贴图란 이미지 중의 색상을 기하 도형에 매핑하는 것으로, 스텝은 다음과 같습니다:
-
정점 셰이더 중에서 각 정점에 텍스처 좌표를 지정
-
그 후 프래그먼트 셰이더 중에서 각 프래그먼트의 텍스처 좌표에 기반하여 텍스처 이미지에서 텍셀 색상을 추출
여기서도 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 은크로스도메인 텍스처 이미지 사용을 허가하지 않습니다. 구체적인 스텝은 다음과 같습니다:
-
텍스처 오브젝트 생성
// 创建 texture var texture = gl.createTexture(); // 创建纹理对象 -
image 생성
var image = new Image(); -
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); };
텍스처 구성 프로세스는 비교적 복잡하며, 나중에 자세히 전개합니다
-
image.src 에 값을 대입하여 이미지 로드
image.src = 'miao256x128.png';
이미지 사이즈에 주의, 에러 정보 중의 non-power-of-2 는 텍스처로서 2^mx2^n 사이즈의 이미지를 사용해야 함을 알려줍니다
##3.내부 상태
그림과 같습니다:
[caption id="attachment_917" align="alignnone" width="605"]
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 은 텍스처 이미지가 프래그먼트에 따라 변화하지 않기 때문이며, sampler2D 는 gl.TEXTURE_2D 에 대응하며, samplerCube 는 gl.TEXTURE_CUBE_MAP 타입 텍스처에 사용됩니다
gl.uniform1i(u_Sampler, 0); 로 sampler2D 변수에 값을 대입하며, 1 은传入하는 성분 수를 나타내고, i 는 성분이 int 타입임을 나타냅니다. 이는 WebGL 의 관용적인 명명 방식으로, 예를 들어 gl.uniform4fv(name, value) 는 value 의 값은 4 차원 벡터 (4 개의 요소를 가진 형식화 배열) 임을 나타냅니다
四.DEMO
위 코드를 포함한 완전한 예에 대해서는, 다음을 참조하십시오:
-
단일 이미지 텍스처: http://www.ayqy.net/temp/webgl/texture/index.html
-
다중 이미지 텍스처: http://www.ayqy.net/temp/webgl/texture-multi-image/index.html
다중 이미지 텍스처란 여러 장의 이미지를 동일한 영역에 붙이는 것으로, 예를 들어:
[caption id="attachment_918" align="alignnone" width="218"]
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[/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 프로그래밍 가이드》
아직 댓글이 없습니다