1、 你能学习到从图片到纹理的过程
2、 多重纹理的使用等
3、 为炫酷的中级教程做好准备
纹理对于我们来说是多么的重要,以至于大家已经忘记了它的重要性。闭上眼睛想一想,如果你心爱的女人,没有穿衣服,该是多么的令你心动啊。哦,说错了,是她不仅没有穿衣服,而且没有皮肤,就像画皮中的没有皮的周迅一样,你就不会喜欢它了,因为她奇丑无比。
纹理之于3D世界,就像皮肤之于动物世界一样。如果没有皮肤,那么人就会非常的丑陋,没有纹理,那么3D世界也就不会那么吸引人了。
我们来想一想,3D世界的纹理,由什么组成呢?3D世界的纹理由图片组成。
是的,就这么简单,如果下次谁问你什么是纹理,那么你告诉它是图片,或者贴图就完了。将纹理以一定的规则映射到几何体上,一般是三角形上,那么这个几何体就有纹理皮肤了。
那么在threejs中,或者任何3D引擎中,纹理应该怎么来实现呢?首先应该有一个纹理类,其次是有一个加载图片的方法,将这张图片和这个纹理类捆绑起来。
在threejs中,纹理类由THREE.Texture表示,其构造函数如下所示:
THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )
各个参数的意义是:
Image:这是一个图片类型,基本上它有ImageUtils来加载,如下代码
// url 是一个http://xxxx/aaa.jpg 的类似地址,javascript没有从本地加载数据的能力, // 所以没有办法从您电脑的C盘加载数据。 var image = THREE.ImageUtils.loadTexture(url);
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,各个参数介绍完了。我们接下来看纹理坐标。
在正常的情况下,你在0.0到1.0的范围内指定纹理坐标。我们来简单看一下纹理坐标如下图:
当我们用一幅图来做纹理的时候,那么这幅图就隐示的被赋予了如图一样的纹理坐标,这个纹理坐标将被对应到一个形状上。
ok,我们来看一下例子,这个例子很简单,就是在平面上贴上一张美女的图片。首先,来看看效果。
你可以在6下找到这份代码:
<!DOCTYPE html> <html lang="en"> <head> <title></title> <meta charset="utf-8"> <style> body { margin: 0px; background-color: #000000; overflow: hidden; } </style> </head> <body> <script src="../js/three.js"></script> <script> var camera, scene, renderer; var mesh; init(); animate(); function init() { renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); // camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.z = 400; scene = new THREE.Scene(); var geometry = new THREE.PlaneGeometry( 500, 300, 1, 1 ); geometry.vertices[0].uv = new THREE.Vector2(0,0); geometry.vertices[1].uv = new THREE.Vector2(2,0); geometry.vertices[2].uv = new THREE.Vector2(2,2); geometry.vertices[3].uv = new THREE.Vector2(0,2); // 纹理坐标怎么弄 var texture = THREE.ImageUtils.loadTexture("textures/a.jpg",null,function(t) { }); var material = new THREE.MeshBasicMaterial({map:texture}); var mesh = new THREE.Mesh( geometry,material ); scene.add( mesh ); // window.addEventListener( 'resize', onWindowResize, false ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); renderer.render( scene, camera ); } </script> </body> </html>
仔细阅读上面的代码,一共完成了4件事情:
a:画一个平面
b:为平面赋予纹理坐标
c:加载纹理
d:将纹理应用于材质
做了这四件事,我们的工作就可以大功告成了。还是让我们来详细解释一下吧。
通过PlaneGemotry可以画一个平面,代码如下:
var geometry = new THREE.PlaneGeometry( 500, 300, 1, 1 );
这个平面的宽度是500,高度是300.
平面有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是分不清楚的。
纹理作为一张图片,可以来源于互联网,或者本地服务器,但是就是不能来源于类似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。
最后,这个函数的返回值是加载的纹理。
加载好纹理,万事俱备了,只需要将纹理映射到材质就可以了。我们这里使用了一个普通的材质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 );
行了,打开你的浏览器,输入你服务器的网址吧,结果就在你眼前。
这只是纹理的一个简单入门,也许你还想知道怎么加载立体纹理、为非光滑的平面贴纹理、甚至为人脸贴纹理,那么就关注后面的课程吧。我们无法在一课把这些知识讲完,但后面的课程会陆续告诉你这些知识的,敬请期待,非常感谢您的阅读。
请疯狂转载基础教程吧,为我们宣传一下,这样我们给你提供高质量的文章,你给我们增加访问量,我们扯平了。玩笑,玩笑,谢谢大家的支持。
给WebGL中文网团队的女程序员"小果妹妹"发一个鸡腿吧,微信扫一扫赞赏,感谢。
亲爱的读者,如果你觉得WebGL中文网的课程不错,您可以购买《WebGL中文网视频课程》 课程支持我们哦,购买后记得给我们好评哦!我们强烈建议您不要在iphone上的网易云课堂软件中购买,这样苹果会收取31%左右的服务费,虽然这是明码标价,我们也表示认可和理解,具体选择权在您自己了。
感谢大家的支持,下面是课程的截图之一
老师你好。。有没有办法可以加载gif的动图的。。。
当然可以,当做纹理,视频都是可以加载的哦。
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);
这四个坐标是象征层面的 显示出来的大小 是与图片真实大小决定的吗
首先感谢作者的辛勤付出,写了这篇通俗易懂的3D教程。
但是,看到这一章我真的忍不住想说一句。
不知道这里有多少女读者,本人作为一个女的,看这篇教程的不少地方都觉得不太舒服。
很多地方有拿女性在开玩笑我也忍了,应该是作者团队全部是男性导致的趣味吧。
但这一章的纹理贴图,让我觉得是在浏览一个不正经的h色网站一样……
作者可以抱着欣赏的态度,放一些美女图片,很好。但是请不要让人感觉像是抱着一种猥琐的态度,放一些衣着暴露的女性图片,很不好。
忍不住吐槽一二,见谅。
赞同楼上~
老师你好。我想问一下为什么src="js/three.js" 这个路径不需要部署就可以访问的到,而图片的相对路径却需要部署后才能加载?
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最后运行结果还是不变?
赞同3楼
这里animate有什么用?
另外这里将物体加入场景应该放在纹理加载的回调函数里吧
特意注册了一个账号,就是为了说本节的示例图用得太不好了,用这么暴露的女性照片,看起来特别刺眼!考虑过女性用户的感受吗??有种进错网站的感觉!!赞同3楼!!
回答3楼和10楼,你们是玩开发的吗,会F12吗。不想看,把响应的代码删除不就可以了。还打那么多字。。。。。。
回答5楼,最终代码都是放到服务器上的。纠结于本地程序为什么不能加载本地图片有什么意义吗
教程非常好,我先进一步学习,请问老师 :透明的贴图我怎么样才能加上呢?
回复11楼,你没听懂人家意思。为什么要手动隐藏下?换个张爱玲或者温美玲或者林志玲或者熊黛林,不是挺好的吗?何必弄个貌似岛国的AV明星?三维材质要用uv,不用av。
赞同13楼。。。同回复11楼。。。。 身为女性。。看到这样的图片真的会感觉不舒服。就算手动隐藏,隐藏前也看到了图片不是吗?。。虽然我非常非常感谢能提供那么棒的文章内容。。。。
讨论图片的同学,有必要吗?穿比基尼的图片,连国家都没有说这违法了。有必要上纲上线吗?难道出现比基尼的导演,都没有考虑过女性用户的感受,难道比基尼T台就赞助商,组织者,观看者,都应该拉出去突突了?老师免费分享,提供学习机会,还上纲上线,哎
使用新版three.js按照示例代码运行后不能正常出现图片,要把下面这句做下修改
var texture = THREE.ImageUtils.loadTexture("textures/a.jpg",null,function(t)
{
});
改为:
var texture = new THREE.TextureLoader().load("textures/a.jpg");
即可
看到这张贴图我就笑了, 直接转到发贴区抢沙发了.
还好, 大家都是来学习的, 所以这里才只到了十几楼. 也说明女性码农不多.
我是男性码农, 对于作者的娱乐精神不对反. 但作者明显是政治不正确啊.
可以躲着看H片, 但大庭广众下谈论还是道德禁忌啊. 哈哈哈
感谢作者的辛苦劳动, 也赞赏女性的主动反对态度!
我们非常尊重女性,非常尊重女性,女性。
库改了
var texture = new THREE.TextureLoader();
texture.load("img/cq.jpg",function(texture){
var material = new THREE.MeshBasicMaterial({map:texture});
var mesh = new THREE.Mesh( geometry,material );
scene.add( mesh );
});
这一块
var texture = THREE.ImageUtils.loadTexture("http://www.hewebgl.com/attached/image/20130601/20130601103132_996.png");
自己替换一下。上面是异步加载(emmmmmmm)
你好,请问在给一个球体加载纹理图片的时候,如何添加UV映射?
现在想把一张正方形的地图贴到一个球体上形成一个地球。
@[6楼]你的问题搞清楚了吗?分享一下
作为女码农,3楼因为个图片上纲上线也真是够了。。。。
纹理图一直没加载出来
//3.加载纹理
var texture = THREE.TextureLoader("textures/a.jpg", null, function (t) {
});
//4.将纹理应用于材质
var material = new THREE.MeshBasicMaterial({ map: texture });
var mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
scene.add(mesh);
window.addEventListener('resize', onWindowResize, false);
}
23楼,翻聊天记录:
19楼的
var texture = THREE.ImageUtils.loadTexture("http://www.hewebgl.com/attached/image/20130601/20130601103132_996.png");
17楼的:
var texture = new THREE.TextureLoader().load("textures/a.jpg");
3楼一看就是没男人的。
女码农一枚,个人感觉啊,不要喷我啊,感觉贴上这种图也没啥,讲课之余增加一些乐趣嘛,可能是之前上学的时候老师就是这种污污的讲课方式然后习惯了吧~~
哈哈,年龄,阅历不一样就会感觉不一样,只要心是正的,什么都不怕。一些表面的老实人,反而可能是好色之徒呢。
虽然我是男同胞,视频中和文字中很多比喻总用男女或者两性来比方,我觉得不妥。美女我也很喜欢啊,但是出现在这种学习资料中,有点倒胃口,还是分一下场合吧!
好的,改正。
18楼正解,感谢18楼。另外我也想说说图片的事,两个字“挺好”,比基尼不犯法。
那个我想给正方体每个面贴上不同的图片,一定要搭建一个服务器么,上面的没太看懂
我用的是WAMPSERVER(集成PHP,MYSQL,APACHE),在localhost中放了一个图片,结果还是提示JS没有权限,这可怎么办啊。
同样,引用网络图片,也是没有权限(估计是被网站方封锁了)。
var texture = THREE.TextureLoader("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=4250112166,2387168490&fm=26&gp=0.jpg",null,function(t)
{
});
var material = new THREE.MeshBasicMaterial({map:texture});
var mesh = new THREE.Mesh( geometry,material );
scene.add( mesh );
报错 :
three.js:12848 THREE.Material: 'map' parameter is undefined.
用谷歌浏览器的时候引用的图片会有跨域的问题,临时先用ie或者edge就行了。
但是这个例子就像教程里的效果一样。只是看到了一张图片,感受不到什么效果。继续看后面的教程吧。
但是教程里的那张美女图片真的很提神啊,赞一个!(对这张图片的态度完全取决于看这张图片的人自己的想法)
按18楼的修改,是对的
老师你好,关于4.2为平面赋予纹理坐标这块不太理解,我把这四行代码注释掉似乎也没啥问题,threejs版本是83
我们尊重写这篇教程的老师,也希望你们尊重下我们
在看到评论之前就已经对这个图片很不爽,没想到下面也有人指出来,如果老师们也觉得这样不对,并表示你们尊重女性的话,可不可以把这个图片改了呢?
这是一点我为贵网站能更进一步提出的小建议
顺便 24楼那位 我还真难以想象这是2018年发的评论 莫非是清朝穿越过来的?
同女码农,觉得两性举例并没有增加教材的趣味性,都是成年人什么意思心里都有逼数,所以别扯什么心正则正。并且这种举例某种意义上更加固化了码农的某种形象。
另外,教材知识内容写的很清楚,感谢大佬的贡献。
大佬4.1,PlaneGemotry,写错了
这不就是一张正常的泳装照片吗?没有露点也没有性暗示动作,怎么有些人就看出色情和不尊重女性的含义了?(想想高中生物书上的那张金刚芭比...)
说到金刚芭比 。怎么一下子就有画面了呢。。。。。
为什么我的按照18楼改了 没有报错了 但还是加载不出图片呢
老师你好,关于纹理坐标的不是很理解,
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);
改变这几个坐标,调换顺序,注释掉都没有用,请问这几个函数到底怎么起作用的
老师好 感谢教程! 我想请问一下将视频作为纹理应该怎样操作呢?
是在代码上直接替换成视频的url吗
我想在立方体的不同面上播放不同的视频,谢谢老师
Date: 2020.02.29
THREE: 113, from: https://threejs.org/build/three.js
Problem: could not load image
Tricks:
THREE.ImageUtils.crossOrigin = '';
var texture = THREE.ImageUtils.loadTexture('http://i.imgur.com/3tU4Vig.jpg');
learn from: https://stackoverflow.com/a/37325781
Date: 2020.02.29
THREE: 113, from: https://threejs.org/build/three.js
Problem: could not load image
Tricks:
THREE.ImageUtils.crossOrigin = '';
var texture = THREE.ImageUtils.loadTexture('http://i.imgur.com/3tU4Vig.jpg');
learn from: https://stackoverflow.com/a/37325781
抱歉,可否不要再用女人来举例,观感很不好,也是有很多女学员的