目的

  1. 从图片到纹理
  2. 多重纹理
  3. 用canvas作为纹理
  4. 纹理的更新

纹理

纹理对于我们多么重要。就如我们人类有皮肤一样,物体也有他们的 “皮肤” – 纹理。纹理之于 3D 世界,就如同皮肤于动物世界一样。3D 世界的物体有了纹理,才能那么的美丽。

1、纹理的组成

3D 世界的纹理是由图片组成的,或者说是贴图。将纹理以一定的规则映射到几何体上(一般是三角形上),那么这个几何体就有纹理皮肤了。

纹理是怎么实现的呢?之前学习过 OpenGL 的时候,纹理是通过着色器(shader)完成图片对物体的映射任务的。

在 Threejs 中, 首先有一个纹理类,调用加载图片的方法,将这张图片和这个纹理类捆绑起来,然后再进行下一步操作的。

Threejs 的纹理类由 THREE.Texture 表示,其构造函数如下:

参数非常多:

  1. image:这是一个图片类型,基本上它是由 ImageUtils 来加载的
  2. mapping:是一个 THREE.UVMapping() 类型,它表示的是纹理坐标
  3. wrapS:表示x轴的纹理回环方式,就是当纹理的宽度小于需要贴图的平面的宽度的时候,平面剩下的部分应该 P 以何种方式的贴图的问题
  4. wrapT:表示y轴的纹理回环方式,就是当纹理的高度小于需要贴图的平面的高度的时候,平面剩下的部分应该 P 以何种方式的贴图的问题
  5. magFilter & minFilter:表示过滤的方式,这是 OpenGL 的基本概念
  6. format:表示加载的图片的格式,这个参数可以取值 THREE.RGBAFormat,RGBFormat 等
  7. type:表示存储纹理的内存的每一个字节的格式,有符号还是没有符号?整形还是浮点型?默认是无符号型(THREE.UnsignedByteType)
  8. anisotropy:各向异性过滤。使用各向异性过滤能够使纹理效果更好,但是会消耗更多的资源(CPU、GPU、Memory…)

2、纹理的坐标

之前学习 OpenGL 曾接触过,一张图片作为纹理图片的时候,正常情况下,用 [0,1] 的区间来表示宽高的比例,而不是具体用像素值来标定。

当用一幅图来做纹理的时候,那么这幅图就已经约定俗成的被隐示的被赋予了上图的 纹理坐标 ,这个纹理坐标将被对应到一个形状上。

3、实例

效果:

 

 

 

 

 

 

 

 

代码如下:

代码解析:

  • 画一个平面
  • 为平面赋予纹理坐标
  • 加载纹理
  • 将纹理应用于材质

(1) 画一个平面

通过 PlaneGeomotry 可以画一个平面,代码如下:

这个平面是一个宽度为500,高度为300的平面。

(2) 为平面赋予纹理坐标

平面有 4 个顶点,所以只需要指定 4 个纹理坐标就可以了。纹理坐标由顶点的 uv 成员来表示, uv 被定义为一个二维向量 THREE.Vector2(),代码如下:

四个顶点对应了纹理的四个顶点。注意坐标之间的顺序是逆时针方向,左下→右下→右上→左上。给平面赋予纹理坐标的时候也要注意方向问题,不然 Threejs 是不清楚的(OpenGL也是如此)。

(3) 加载纹理

纹理作为一张图片,可以来源于互联网,或者是本地服务器,但是不能来源于本地,即类似 C:\texture\a.jpg 这样的路径。这是因为 javascript 没有加载本地路径文件的权限。如果尝试这么做,将会在浏览器的控制窗口 console.log 中显示错误:

 

 

 

 

 

所以必须在电脑上搭建一个 Tomcat 或者 Apache 之类的网页服务器,并把图片资源放到服务器上,通过相对路径地址或者绝对路径地址去访问才可以正确加载。

旧版本的 Threejs 会用如下代码:

但是新版本用上面的代码是会显示这个函数已经取消了,需要用 THREE.TextureLoader 代替,故这部分代码应为:

(4) 将纹理应用于材质

加载好纹理,就只需要将纹理映射到材质上就可以了。我们这里使用了一个普通的材质 THREE.MeshBasicMaterial (如上代码),材质中有一个 map 属性,可以直接接受纹理。所以实际上,纹理作用于 material 的。相关代码看上面的 texture.load() 其中蕴含的 function(…)。

4、时钟纹理实例

实现的步骤如下:

  1. 在 canvas 上画时钟
  2. 将 canvas 传递给 THREE.Texture 纹理
  3. 将纹理传递给 THREE.MeshBasicMaterial 材质
  4. 最后构造 THREE.Mesh

(1) 在 canvas 上画时钟

封装 在 js/clock.js 中,这是用 HTML5 代码绘制的 canvas 的基本函数。

(2) 将 canvas 传递给 THREE.Texture 纹理

canvas 可以作为纹理传递给 THREE.Texture 函数,其构造函数在前文已给出。

在 Image 参数上传递 canvas 变量,后面的参数不理会自然使用默认参数。

那么纹理是怎么知道其每一个像素怎么映射到形状的表面的,默认情况下,纹理被均匀分配到四边形的各个顶点上。

(3) 将纹理传递给 THREE.MeshBasicMaterial 材质

将 texture 传递给材质,材质本身可以接受一个 属性名为 map 的参数,故:

这样就将纹理赋给了材质了。

(4) 最后构造 THREE.Mesh

Mesh 就是个网格表面,它代表着我们渲染到 3D 世界中的各种模型。其构造模型为:

只有两个参数:geometry – 几何体;material – 材质,可以通过这样来构造它:

 

后记

A、纹理原理

本质上来说,纹理只是图片而已,是由像素点组成的。无论实在内存中还是在显存中,都是由 4 个分量组成,就是 R、G、B、A。唯一不同的是,在显存里,会比内存更快的渲染到显示器上。

参考资料

http://www.hewebgl.com/article/getarticle/68