纹理单元和对象
纹理单元,也称纹理映射单元或纹理处理单元,是GPU进行采样的组件。纹理对象是包含图片颜色数据的数据结构和纹理相关数学信息。一个纹理单元必须访问一个纹理对象来完成工作。纹理单元是处理器,而纹理对象保存被处理的数据。
GLSL中的sampler类型包括sampler2D和samplerCube。 sampler2D用在标准纹理图像上,而samplerCube用在立方体纹理上。sampler类型的值索引到一个纹理单元上。值用来告诉采样过程中哪个纹理单元被用到。采样值必须是声明在全局的uniform类型中,不能在着色器程序中去分配值。纹理单元的值可以是0,1,2... 在GLSL中的应用
uniform sampler2D u_texture;在JS中:
u_texture_location = gl.getUniformLocation(prog, "u_texture");
gl.uniform1i(u_texture_location, 2);纹理的使用:
图像纹理的使用基本过程就是创建,激活,并与对应的着色器程序关联;
- 创建纹理对象,关联到对应的内存中
textureObj = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, textureObj);
// 用来加载图片到纹理对象中
gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);- 匹配纹理单元 在告知纹理单元处理纹理对象时需要先激活纹理单位“gl.activeTexture”。参数包括gl.TEXTURE0, gl.TEXTURE1... 初始情况下 TEXTURE0是激活的。
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, textureObj); texture 来源 http://math.hws.edu/graphicsbook/c6/s4.html#webgl.4.4
立方体纹理
Webgl支持立方体纹理。纹理对象可以保存立方体纹理。两个纹理对象可以同时绑定到同一个纹理单元,一个是普通纹理,另一个是立方体纹理。两个纹理对象绑定到不同的目标上gl.TEXTURE2D和gl.TEXTURE_CUBE_MAP。纹理对象texObj绑定的方式:
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texObj);纹理对象一旦绑定到一个对象上,就不能再次被绑定到其他地方。立方体纹理包含6个图片,每张图片对应立方体的一个面。绑定到立方体纹理上的纹理对象有6个常数进行指定:
gl.TEXTURE_CUBE_MAP_NEGATIVE_X
gl.TEXTURE_CUBE_MAP_POSITIVE_X
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y
gl.TEXTURE_CUBE_MAP_POSITIVE_Y
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
gl.TEXTURE_CUBE_MAP_POSITIVE_Z这些常数被用在gl.texImage2D和gl.copyTexImage2D的目标上。图片加载到cubemap纹理对象有6个目标,但用来将纹理对象绑定到纹理单元的只有gl.TEXTURE_CUBE_MAP。立方体纹理存储了一组6张图片,必须单独的加载到纹理对象上。例子鱼眼??
function loadCubemapTexture(){
var tex = gl.createTexture();
var imageCt = 0;
load("negx.jpg", gl.TEXTURE_CUBE_MAP_NEGATIVE_X);
load("posx.jpg", gl.TEXTURE_CUBE_MAP_POSITIVE_X);
load("negy.jpg", gl.TEXTURE_CUBE_MAP_NEGATIVE_Y);
load("posy.jpg", gl.TEXTURE_CUBE_MAP_POSITIVE_Y);
load("negz.jpg", gl.TEXTURE_CUBE_MAP_NEGATIVE_Z);
load("posz.jpg", gl.TEXTURE_CUBE_MAP_POSITIVE_Z);
function load(url, target){
var img = new Image();
img.onload = function(){
gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
imageCt++;
if(imageCt === 6){
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
textureObject = tex;
draw();
}
}
img.src = url;
}
}立方体纹理中的图像必须相同大小,并且是正方体的,大小也应该是2的幂次。另外对于纹理参数必须应用在所有的6个面上。如:
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);对于立方体纹理为了面与面之间没有明显的裂缝,建议将纹理wrap的模式改为CLAMP_TO_EDGE来避免。
在片段着色器上,uniform sapmlerCube
vec4 color = textureCube(u_texture, vector);纹理坐标
纹理坐标通常是根据将要渲染的物体坐标进行计算的。纹理坐标也是跟着物体坐标的变换而变化的。最简单生成方式是使用物体坐标的x,y坐标。如果一个点坐标是acoords,那么意味着会采用a_coords_xy作为纹理坐标。这种简单的映射对应朝向正z轴方向的多边形来说是可行的,但对于在xy面的多边形来说并没有好的结果。结果可能如下图:
来源:http://math.hws.edu/graphicsbook/c7/s3.html
可以看到的是正面的结果是正常的,而其他几个面的结果就比较奇怪。这个问题的原因在于我们没有对其他面纹理坐标进行投影变换。
对于平的材质,多边形上所有的法向量都是同一方向的,这个计算就可以在顶点着色器上完成;对于平滑的材质而言,每个点上的法向量都不太一样,如果在不同的顶点上投影不同方向的纹理坐标并进行插值,那么结果很可能是一团糟的。因此这种情况就适合在片段着色器中计算。
程序式的纹理
程序式纹理是通过指定一个函数来计算值而不是选取已知的。在Webgl中程序式纹理定义在片段着色器中。采用vec2代表纹理坐标而不是sampler2D。这也可以扩展到3D纹理,使用vec3。相比于二维纹理投影在平面点上,三维纹理是投影在空间中。
vec4 color;
float a = floor(v_texCoords.x * scale);
float b = floor(v_texCoords.y * scale);
if(mod(a+b, 2.0) > 0.5){
color = vec4(1.0, 0.5, .5, 1.0);
} else {
color = vec4(0.6, 0.6, 1.0, 1.0);
}参考: