第6章 纹理,不一样的皮肤(一)

1、 你能学习到从图片到纹理的过程

2、 多重纹理的使用等

3、 为炫酷的中级教程做好准备

1 纹理概述

纹理对于我们来说是多么的重要,以至于大家已经忘记了它的重要性。闭上眼睛想一想,如果你心爱的女人,没有穿衣服,该是多么的令你心动啊。哦,说错了,是她不仅没有穿衣服,而且没有皮肤,就像画皮中的没有皮的周迅一样,你就不会喜欢它了,因为她奇丑无比。

纹理之于3D世界,就像皮肤之于动物世界一样。如果没有皮肤,那么人就会非常的丑陋,没有纹理,那么3D世界也就不会那么吸引人了。

2 纹理由图片组成

我们来想一想,3D世界的纹理,由什么组成呢?3D世界的纹理由图片组成。

是的,就这么简单,如果下次谁问你什么是纹理,那么你告诉它是图片,或者贴图就完了。将纹理以一定的规则映射到几何体上,一般是三角形上,那么这个几何体就有纹理皮肤了。

那么在threejs中,或者任何3D引擎中,纹理应该怎么来实现呢?首先应该有一个纹理类,其次是有一个加载图片的方法,将这张图片和这个纹理类捆绑起来。

在threejs中,纹理类由THREE.Texture表示,其构造函数如下所示:

THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )

各个参数的意义是:

Image:这是一个图片类型,基本上它有ImageUtils来加载,如下代码

var image = THREE.ImageUtils.loadTexture(url); // url 是一个http://xxxx/aaa.jpg 的类似地址,javascript没有从本地加载数据的能力,所以没有办法从您电脑的C盘加载数据。

Mapping:是一个THREE.UVMapping()类型,它表示的是纹理坐标。下一节,我们将说说纹理坐标。

wrapS:表示x轴的纹理的回环方式,就是当纹理的宽度小于需要贴图的平面的宽度的时候,平面剩下的部分应该p以何种方式贴图的问题。

wrapT:表示y轴的纹理回环方式。 magFilter和minFilter表示过滤的方式,这是OpenGL的基本概念,我将在下面讲一下,目前你不用担心它的使用。当您不设置的时候,它会取默认值,所以,我们这里暂时不理睬他。

format:表示加载的图片的格式,这个参数可以取值THREE.RGBAFormat,RGBFormat等。THREE.RGBAFormat表示每个像素点要使用四个分量表示,分别是红、绿、蓝、透明来表示。RGBFormat则不使用透明,也就是说纹理不会有透明的效果。

type:表示存储纹理的内存的每一个字节的格式,是有符号,还是没有符号,是整形,还是浮点型。不过这里默认是无符号型(THREE.UnsignedByteType)。暂时就解释到这里,有需要时,我们在仔细分析,或者给作者留言询问。

anisotropy:各向异性过滤。使用各向异性过滤能够使纹理的效果更好,但是会消耗更多的内存、CPU、GPU时间,暂时就了解到这里吧。

ok,各个参数介绍完了。我们接下来看纹理坐标。

3 纹理坐标

在正常的情况下,你在0.0到1.0的范围内指定纹理坐标。我们来简单看一下纹理坐标如下图:

当我们用一幅图来做纹理的时候,那么这幅图就隐示的被赋予了如图一样的纹理坐标,这个纹理坐标将被对应到一个形状上。

4 实例

ok,我们来看一下例子,这个例子很简单,就是在平面上贴上一张美女的图片。首先,来看看效果。

你可以在6下找到这份代码:




仔细阅读上面的代码,一共完成了4件事情:

a:画一个平面

b:为平面赋予纹理坐标

c:加载纹理

d:将纹理应用于材质

做了这四件事,我们的工作就可以大功告成了。还是让我们来详细解释一下吧。

4.1 画一个平面

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

var geometry = new THREE.PlaneGeometry( 500, 300, 1, 1 );

这个平面的宽度是500,高度是300.

4.2为平面赋予纹理坐标

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

geometry.vertices[0].uv = new THREE.Vector2(0,0);

geometry.vertices[1].uv = new THREE.Vector2(1,0);

geometry.vertices[2].uv = new THREE.Vector2(1,1);

geometry.vertices[3].uv = new THREE.Vector2(0,1);

注意,4个顶点分别对应了纹理的4个顶点。还要注意(0,0),(1,0),(1,1),(0,1)他们之间的顺序是逆时针方向。大家在给平面赋纹理坐标的时候也要注意方向,不然three.js是分不清楚的。

4.3 加载纹理

纹理作为一张图片,可以来源于互联网,或者本地服务器,但是就是不能来源于类似C:\pic\a.jpg这样的本地路径。这是因为javascript没有加载本地路径文件的权限。如果你尝试这么做,会报如下的错误:

所以务必在你的电脑上搭建一个tomcat,apache,iis等服务器中的一种,并把图片资源放到服务器中,并通过http://localhost:8080/img/a.jpg这样的方式去访问它。这样就能解决上面的交叉域问题。我们接着往下讲。

这里加载纹理使用了上面介绍的loadTexture函数,代码如下:

var texture = THREE.ImageUtils.loadTexture("textures/a.jpg",null,function(t)

{

});

这个函数的第一个参数是一个相对路径,表示与您的网页之间的相对路径。相对路径对应了一个纹理图片textures/a.jpg。

第二个参数为null,表示时候要传入一个纹理坐标参数,来覆盖前面在geometry中的参数。

第三个表示一个回调函数,表示成功加载纹理后需要执行的函数,参数t是传入的texture。

最后,这个函数的返回值是加载的纹理。

4.4 将纹理应用于材质

加载好纹理,万事俱备了,只需要将纹理映射到材质就可以了。我们这里使用了一个普通的材质THREE.MeshBasicMaterial,材质中有一个map属性,可以直接接受纹理,我们可以这样定义一个带纹理的材质:

var material = new THREE.MeshBasicMaterial({map:texture});

ok,接下来的事情就简单了,直接将纹理甩给Mesh,同时也别忘了Mesh也需要geometry,他们暧昧的关系如下:

var mesh = new THREE.Mesh( geometry,material );

最后的最后,将这个mesh加入场景中:

scene.add( mesh );

行了,打开你的浏览器,输入你服务器的网址吧,结果就在你眼前。

5、总结

这只是纹理的一个简单入门,也许你还想知道怎么加载立体纹理、为非光滑的平面贴纹理、甚至为人脸贴纹理,那么就关注后面的课程吧。我们无法在一课把这些知识讲完,但后面的课程会陆续告诉你这些知识的,敬请期待,非常感谢您的阅读。

请疯狂转载基础教程吧,为我们宣传一下,这样我们给你提供高质量的文章,你给我们增加访问量,我们扯平了。玩笑,玩笑,谢谢大家的支持。

[1楼] fion** 2016-07-19 14:23

老师你好。。有没有办法可以加载gif的动图的。。。

[2楼] like** 2016-07-19 17:34

geometry.vertices[0].uv = new THREE.Vector2(0,0);

geometry.vertices[1].uv = new THREE.Vector2(1,0);

geometry.vertices[2].uv = new THREE.Vector2(1,1);

geometry.vertices[3].uv = new THREE.Vector2(0,1);

这四个坐标是象征层面的 显示出来的大小 是与图片真实大小决定的吗

[3楼] 1611** 2016-11-23 12:56

首先感谢作者的辛勤付出,写了这篇通俗易懂的3D教程。

但是,看到这一章我真的忍不住想说一句。

不知道这里有多少女读者,本人作为一个女的,看这篇教程的不少地方都觉得不太舒服。

很多地方有拿女性在开玩笑我也忍了,应该是作者团队全部是男性导致的趣味吧。

但这一章的纹理贴图,让我觉得是在浏览一个不正经的h色网站一样……

作者可以抱着欣赏的态度,放一些美女图片,很好。但是请不要让人感觉像是抱着一种猥琐的态度,放一些衣着暴露的女性图片,很不好。

忍不住吐槽一二,见谅。

[4楼] anjo** 2016-11-30 11:35

赞同楼上~

[5楼] woni** 2016-12-05 13:10

老师你好。我想问一下为什么src="js/three.js" 这个路径不需要部署就可以访问的到,而图片的相对路径却需要部署后才能加载?

[6楼] woni** 2016-12-05 13:38

geometry.vertices[0].uv = new THREE.Vector2(0,0);

geometry.vertices[1].uv = new THREE.Vector2(1,0);

geometry.vertices[2].uv = new THREE.Vector2(1,1);

geometry.vertices[3].uv = new THREE.Vector2(0,1); 

老师您好。请问这四行 “为平面赋予纹理坐标” 的代码具体是什么意思?为什么我把其中的1改成10000最后运行结果还是不变?

提问或评论

登陆后才可留言或提问哦:) 登陆 | 注册 登陆后请返回本课提问
用户名
密   码
验证码