纹理单元和对象
纹理单元,也称纹理映射单元或纹理处理单元,是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_Xgl.TEXTURE_CUBE_MAP_POSITIVE_Xgl.TEXTURE_CUBE_MAP_NEGATIVE_Ygl.TEXTURE_CUBE_MAP_POSITIVE_Ygl.TEXTURE_CUBE_MAP_NEGATIVE_Zgl.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);}复制代码
参考: