1.纹理坐标:st坐标系统,纹理左边的原点位于图像左下角,图像右上角的坐标始终是(1.0,1.0)
2.纹理坐标与WebGL坐标的映射:
可以赋值给gl.TEXTURE_MAG_FILTER和gl.TEXTURE_MIN_FILTER的非金字塔纹理类型常量:
gl.NEAREST:使用原纹理上距离映射后像素(新像素)中心最近的哪个像素的颜色值,作为新像素的值(使用曼哈顿距离,即x1,x2到y1,y2的值是|x1-x2|+|y1-y2|)
gl.LINEAR:使用距离新像素中心最近的四个像素的加权平均作为新像素的值(该方法开销较大,图像质量更好)
可以赋值给gl.TEXTURE_WRAP_S和gl.TEXTURE_WRAP_T的常量:
gl.REPEAT:平铺式重复纹理
gl.MIRRORED_PEREAT:镜像对称式重复纹理
gl.CLAMP_TO_EDGE:使用纹理图像边缘值
(4)将纹理图像分配给纹理对象:
gl.texImage2D(gl.TEXTURE_2D,0,gl.RGB,gl.RGB,gl.UNSIGNED_BYTE,image)
API:gl.textImage2D(target,level,internalformat,format,type,image)
target:纹理类型,gl.TEXTURE_2D二维纹理,gl.TEXTURE_CUBE_MAP立方体纹理
level:传入0(该参数为金字塔准备)
internalformat:图像内部格式,和图片格式有关(jpg,png,jpeg,bmp等)。流明表示我们感知到的物体表面亮度,使用RGB分量值的加权平均值来计算
format:纹理数据格式,必须和internalformat一致
type:纹理数据类型,后面几种数据格式通常用来压缩数据,减少浏览器加载时间
image:包含纹理图像的Image图像
(5)将纹理单元传递给片元着色器:通过指定纹理单元编号将纹理对象传给u_Sampler()取样器使用多个纹理
gl.uniform1i(u_Sampler,0);
7.通过上述步骤,纹理图像已经全部设置完成,顶点着色器接收到顶点的纹理坐标之后会光栅化内插出所有片元的纹理坐标,接下来由片元着色器进行处理,texture2D是GLSL ES的内置方法,只需传入纹理单元编号和纹理坐标,就可以得到纹理上的像素颜色。通过varying变量可以将片元的纹理坐标由顶点着色器传入片元着色器。
简单将一幅纹理图映射到一个矩形上:
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec2 a_TexCoord;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){' +
'gl_Position = a_Position;\n' +
'v_TexCoord = a_TexCoord;\n' +
'}\n';
var FSHADER_SOURCE =
'precision mediump float;\n'+
'uniform sampler2D u_Sampler;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n' +
'gl_FragColor = texture2D(u_Sampler,v_TexCoord);\n' +
'}';
function main()
{
var canvas = document.getElementById('webgl');
var gl = canvas.getContext('webgl');
initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE);
var n = initVertexBuffers(gl);
initTextures(gl,n);
}
function initVertexBuffers(gl)
{
var verticesTexCoords = new Float32Array([
//顶点坐标,纹理坐标
-0.5,0.5,0.0,1.0,
-0.5,-0.5,0.0,0.0,
0.5,0.5,1.0,1.0,
0.5,-0.5,1.0,0.0
]);
var n = 4;
var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
var vertexTexCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexTexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER,verticesTexCoords,gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program,'a_Position');
gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,FSIZE*4,0);
gl.enableVertexAttribArray(a_Position);
//将纹理坐标分配给a_TexCoord
var a_TexCoord = gl.getAttribLocation(gl.program,'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord,2,gl.FLOAT,false,FSIZE*4,FSIZE*2);
gl.enableVertexAttribArray(a_TexCoord);
return n;
}
function initTextures(gl,n)
{
//创建纹理对象
var texture = gl.createTexture();
//获取u_Sampler的存储位置
var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
var image = new Image();
//注册图像加载事件的响应函数
image.onload = function (){
//为WebGL配置纹理
loadTexture(gl,n,texture,u_Sampler,image);
};
//浏览器开始加载图形
image.src = 'res/3.png';
return true;
}
function loadTexture(gl,n,texture,u_Sampler,image)
{
//alert("图片加载事件已触发");
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);//对纹理图形进行y轴反转
//开启0号纹理单元8
gl.activeTexture(gl.TEXTURE0);
//向target绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D,texture);
//配置纹理参数
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);
//注意:如果图片的分辨率不是2的次幂,那么需要配置以下两个参数
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
//配置纹理图像
gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image);
//将0号纹理传给着色器
gl.uniform1i(u_Sampler,0);
gl.drawArrays(gl.TRIANGLE_STRIP,0,n);
}
使用多个纹理:
var VSHADER_SOURCE=
'attribute vec4 a_Position;\n' +
'attribute vec2 a_TexCoord;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n' +
'gl_Position = a_Position;\n' +
'v_TexCoord = a_TexCoord;\n' +
'}\n';
var FSHADER_SOURCE =
'precision mediump float;\n'+
'uniform sampler2D u_Sampler0;\n' +
'uniform sampler2D u_Sampler1;\n' +
'varying vec2 v_TexCoord;\n' +
'void main(){\n' +
'vec4 color0 = texture2D(u_Sampler0,v_TexCoord);\n' +
'vec4 color1 = texture2D(u_Sampler1,v_TexCoord);\n' +
'gl_FragColor = color0 * color1;\n' +
'}\n';
function main()
{
var canvas = document.getElementById('webgl');
var gl = canvas.getContext('webgl');
initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE);
var n = initVertex(gl);
initTextures(gl,n);
}
function initVertex(gl)
{
var vertices = new Float32Array([
-0.5,0.5,0.0,1.0,
-0.5,-0.5,0.0,0.0,
0.5,0.5,1.0,1.0,
0.5,-0.5,1.0,0.0
]);
var FSIZE = vertices.BYTES_PER_ELEMENT;
console.log(FSIZE);
var n = 4;
var a_Position = gl.getAttribLocation(gl.program,'a_Position');
var a_TexCoord = gl.getAttribLocation(gl.program,'a_TexCoord');
var vertexCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);
gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,FSIZE*4,0);
gl.enableVertexAttribArray(a_Position);
gl.vertexAttribPointer(a_TexCoord,2,gl.FLOAT,false,FSIZE*4,FSIZE*2);
gl.enableVertexAttribArray(a_TexCoord);
return n;
}
function initTextures(gl,n)
{
var texture0 = gl.createTexture();
var texture1 = gl.createTexture();
var u_Sampler0 = gl.getUniformLocation(gl.program,'u_Sampler0');
var u_Sampler1 = gl.getUniformLocation(gl.program,'u_Sampler1');
var image0 = new Image();
var image1 = new Image();
image0.onload = function(){
loadTexture(gl,n,texture0,u_Sampler0,image0,0);
};
image1.onload = function(){
loadTexture(gl,n,texture1,u_Sampler1,image1,1);
};
image0.src = "res/1.png";
image1.src = "res/3.png";
return true;
}
//标记纹理单元是否就绪
var g_texUnit0 = false,g_texUnit1 = false;
function loadTexture(gl,n,texture,u_Sampler,image,texUnit)
{
//反转纹理图像y轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);
//激活纹理
if(texUnit == 0)
{
gl.activeTexture(gl.TEXTURE0);
g_texUnit0 = true;
console.log("激活单元0");
}else {
gl.activeTexture(gl.TEXTURE1);
g_texUnit1 = true;
console.log("激活单元1");
}
//绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D,texture);
//配置纹理参数
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);
//注意:如果图片的分辨率不是2的次幂,那么需要配置以下两个参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,image)
gl.uniform1i(u_Sampler,texUnit);
if(g_texUnit0 && g_texUnit1)
{
console.log(n);
gl.drawArrays(gl.TRIANGLE_STRIP,0,n);
}
}
重点:(1)因为矩形顶点在两幅纹理上的纹理坐标是完全相同的,所以只用处理一次
(2)使用两个纹素来计算最终的片元颜色。
(3)由于图像的加载过程是异步的,我们无法知道哪副纹理图像先被加载完成,所以需要添加判断当两幅图像都加载完成时再绘制图像。