10.ThreeJs开发指南-第十章-加载和使用纹理

来源:互联网 发布:javascript 第7版 pdf 编辑:程序博客网 时间:2024/06/06 15:48

第十章 加载和使用纹理

在材质中使用纹理

function createMesh(geom,imageFile){    var texture = THREE.ImageUtils.loadTexture('../assets/textures/general/' + imageFile);    var mat = new THREE.MeshPhongMaterial();    mat.map = texture;    var mesh = new THREE.Mesh(geom,mat);    return mesh;}

这里的纹理图片可以是:png、gif、jpeg

纹理加载是异步的。这是没有问题的,因为我们有一个render循环,每秒大概渲染场景60次,所以一旦纹理加载完毕就会立即在场景中显示。

如果你想纹理加载前一致等待。

texture = THREE.ImageUtils.loadTexture('texture.png',{},function(){    renderer.render(scene);});

几乎所有的图片都可以作为纹理。

为了达到最好的效果,最好使用正方形的图片,其长宽都是2的次方。

由于纹理需要放大和缩小,所以通常纹理上的像素(纹理单元:texel)不会一对一地映射成面上的像素。因此WebGL和Three.js提供了几种选择。

magFilter属性:指定纹理如何放大。默认值:THREE.LinearFilter
minFilter属性:指定纹理如何缩小。默认值:THREE.LinearMipMapLinearFilter

属性值:
THREE.NearestFilter 最邻近过滤
THREE.LinearFilter 线性过滤

mipmap:一个mipmap是一组纹理图片,每个图片的尺寸都是前一张图片的一半。这些图片实在加载纹理时创建的,可以生成较平滑的过滤效果。

mipmap的属性值:

THREE.NearestMipMapNearestFilter:选择最贴近目标解析度的mipmap,然后使用最邻近过滤原则。
THREE.NearestMipMapLinarFilter:选择层次最近的两个mipmap,然后在这两层之间使用最邻近过滤原则获取两个中间值,然后这两个中间值传递给线性过滤器,以获得最终效果。
THREE.LinearMipMapNearestFilter:选择最贴近目标解析度的mipmap,然后使用线性过滤原则。
THREE.LinearMipMapLinearFilter:选择层次最近的两个mipmap,然后在这两层之间使用线性过滤原则获得两个中间值,然后这两个中间值传递给线性过滤器,以获得最终结果。

凹凸纹理贴图创建皱纹

凹凸纹理的目的是为材质增加厚度。

function createMesh(geom,imageFile,bump){    var texture = THREE.ImageUtils.loadTexture('../assets/textures/general/'+imageFile);    var mat = new THREE.MeshPhongMaterial();    mat.map = texture;    var bump = THREE.ImageUtils.loadTexture('../assets/textures/general/'+bump);    mat.bumpMap = bump;    mat.bumpScale = 0.2;//凹凸的高度,负数表示凹下去的深度。    var mesh = new THREE.Mesh(geom,mat);    return mesh;}

凹凸贴图中只有像素的相对高度,没有任何坡度的方向性信息。所以凹凸贴图所能达到的厚度和细节程度是有限的。更多的细节可以使用法向贴图。

使用法向贴图创建更加细致的凹凸和皱纹

法向贴图中保存的不是每个像素的高度,而是像素的法向向量。使用法向贴图,只需使用很少的顶点和面,就可以创建出细节非常丰富的模型。

function createMesh(geom,imageFile,normal){    var t = THREE.ImageUtils.loadTexture('../assets/textures/general/'+imageFile);    var m = THREE.ImageUtils.loadTexture('../assets/textures/general/'+normal);    var mat2 = new THREE.MeshPhongMaterial({        mat:t,        normalMap:m    });    var mesh = new THREE.Mesh(geom,mat2);    return mesh;}

我们还可以指定凹凸的程度,方法是设置:mat.normalScale.set(1,1);通过这个属性,可以沿着x轴和y轴缩放。不过最好将他们设置成一样的。

法向贴图的问题是不容易创建。需要使用特殊的工具,例如:Blender和Photoshop,它们可以将高解析度的渲染结果或图片作为输入,从中创建法向贴图。

使用光照贴图创建假阴影

光照贴图是预先渲染好的阴影,可以用它来模拟真实的阴影。

只对静态场景起效果。

var lm = THREE.ImageUtils.loadTexture('../assets/textures/lightmap/lm-1.png');var wood = THREE.ImageUtils.loadTexture('../assets/textures/general/floor-wood.jpg');var groundMaterial = new THREE.MeshBasicMaterial({    lightMap:lm,    map:wood});groundGeom.faceVertexUvs[1] = groundGeom.faceVertexUvs[0];

要想光照贴图显示出来,我们需要为光照贴图明确指定UV映射(将纹理的哪一部分应用到表面)。只有这样,我们才能将光照贴图和其他的纹理独立开来。

用环境贴图创建虚假的反光效果

计算反光非常消耗CPU,而且通常会使用法线追踪算法。

所以,我们可以创建一个对象所处环境的纹理来伪装反光,并将它应用到特定的对象上。

步骤:

1、创建一个CubeMap对象
2、创建一个带有这个CubeMap对象的方块(天空盒)
3、将CubeMap作为纹理

天空盒照片:
http://www.humus.name/index.php?page=Textures

function createCubeMap(){    var path = "../assets/textures/cubemap/parliament/";    var format = '.jpg';    var urls= [        path + 'posx' + format , path + 'negx' + format,        path + 'posy' + format , path + 'negy' + format,        path + 'posz' + format , path + 'negz' + format,    ];    var textureCube = THREE.ImageUtils.loadTextureCube(urls);    return textureCube;}

创建一个方块:

var textureCube = createCubeMap();var shader = THREE.ShaderLib['cube'];shader.uniforms['tCube'].value = textureCube;var material = new THREE.ShaderMaterial({    fragmentShader:shader.fragmentShader,    vertexShader:shader.vertexShader,    uniforms:shader.uniforms,    depthWrite:false,    side:THREE.BackSide});cubeMesh = new THREE.Mesh(new THREE.CubeGeometry(100,100,100),material);

同一个Cube对象可以应用到某个网格上,用来创建虚假的反光。

var sphere1 = createMesh(new THREE.SphereGeometry(10,15,15),'plaster.jpg');sphere1.material.envMap = textureCube;sphere1.rotation.y = -0.5;sphere1.position.x = 12;sphere1.position.y = 5;scene.add(sphere1);var sphere2 = createMesh(new THREE.CubeGeometry(10,15,15),'plaste.jpg','plaster-normal.jpg');sphere2.material.envMap = textureCube;sphere2.rotation.y = 0.5;sphere2.position.x = -12;sphere2.position.y = 5;scene.add(sphere2);

折射:

var textureCube = THREE.ImageUtils.loadTextureCube(urls,new THREE.CubeRefractionMapping);

通过材质的refraction属性可以控制折射率。

在这个示例中,我们使用的是静态环境贴图。即我们只能看到环境的反光,而无法看到其他网格。

高光贴图

可以为材质指定一个闪亮的、色彩明快的贴图。

一般高光贴图会同specular属性一起使用。

var specularTexture = THREE.ImageUtils.loadTexture('../assets/textures/planets/EarthSpec.png');var normalTexture = THREE.ImageUtils.loadTexture('../assets/textures/planets/EarthNormal.png');var planetMaterial = new THREE.MeshPhongMaterial();planetMaterial.specularMap = specularTexture;planetMaterial.specular = new THREE.Color(0xff0000);planetMaterial.shininess = 1;planetMaterial.normalMap = normalTexture;

最好的效果往往是使用低光亮度实现的,但高光贴图还会受到光照的影响。

纹理的高级用途

一、定制UV映射

通过UV映射,可以指定纹理的哪一部分显示在物体表面。当你在Three.js中创建几何体时,根据几何体的类型,这些映射也一并自动创建。

UV定制一般是在诸如Blender这样的软件中完成的,特别是在模型变得复杂的时候。

UV映射有两个维度,U和V,分别取值范围是0-1.

需要为构成面的每一个顶点指定u和v。

重复映射

cube.material.map.wrapS = THREE.RepeatWrapping;//沿x方向的行为cube.material.map.wrapT = THREE.RepeatWrapping;//沿y方向的行为

THREE.RepeatWrapping 允许纹理重复自己
THREE.ClampToEdgeWrapping 默认设置,纹理边缘的像素会被拉伸,以填满剩下的空间。

如果使用了THREE.RepeatWrapping,我们可以使用下面的代码代替repeat属性:

cube.material.map.repeat.set(repeatX,repeatY);

参数:
repeatX:指定在x轴方向多久重复一次。
repeatY:指定在y轴方向多久重复一次。

如果设置为1,都不会重复。
如果设置<1,纹理就会被放大。
如果设置为负数,就会产生纹理镜像。

当修改repeat属性时,Three.js会自动更新纹理,并用新的设置进行渲染。

当然,你从 THREE.RepeatWrapping 切换到 THREE.ClampToEdgeWrapping 时,你就要明确更新纹理:
cube.material.map.needsUpdate = true;

在画布上绘制图案并作为纹理

交互式画布:literally库

http://literallycanvas.com

<div class="fs-container">    <div id="canvas-output" style='float:left'></div><div>...var canvas = document.createElement('canvas');$('#canvas-output')[0].appendChild(canvas);//创建绘图工具$('#canvas-output').literallycanvas(    {imageURLPrefix:'../libs/literally/img'});//将在画布上的绘图结果作为输入创建一个纹理function createMesh(geom){    var canvasMap = new THREE.Texture(canvas);    var mat = new THREE.MeshPhongMaterial();    mat.map = canvasMap;    var mesh = new THREE.Mesh(geom,mat);    return mesh;}

更新材质:

function render(){    stats.update();    cube.rotation.y += 0.01;    cube.rotation.x += 0.01;    cube.material.map.needsUpdate = true;    requeatAnimationFrame(render);    webGLRenderer.render(scene,camera);}

用画布作凹凸纹理

凹凸图只是简单的黑白图片。

贴图中的像素的密集程度越高,贴图看上去越皱。

在画布上随机产生一副灰度图。

Perlin噪声,可以产生看上去非常自然的随机纹理。

var ctx = canvas.getContext('2d');function fillWithPerlin(perlin,ctx){    for(var x = 0 ;x < 512; x++){        for(var y = 0 ;y <512;y++){            var base = new THREE.Color(0xffffff);            var value = perlin.noise(x/10,y/10,0);//在画布x坐标和y坐标的基础上生成一个0到1之间的值。该值可以在画布上画一个像素点。            base.multiplyScalar(value);            ctx.fillStyle = '#' + base.getHexString();            ctx.fillRect(x,y,1,1);        }    }}function createMesh(geom){    var bumpMap = new THREE.Texture(canvas);    var mat = new THREE.MeshPhongMaterial();    mat.color = new THREE.Color(0x77ff77);    mat.bumpMap = bumpMap;    bumpMap.needsUpdate = true;    var mesh = new THREE.Mesh(geom,mat);    return mesh;}

用视频输出作为纹理

webgl已经直接支持HTML视频元素。

<video id = 'video'     style='display:none;position: absolute;left:15px;top:75px;'    src='../assets/movies/Big_Buck_Bunny_small.ogv'    controls='true'    autoplay='true'</video>var video = document.getElementById('video');texture = new THREE.Texture(video);texture.minFilter = Three.LinearFilter;texture.magFilter = THREE.LinearFilter;texture.generateMipmaps = false;

注意:
1.由于我们的视频不是正方形,所以要保证材质不会生成mipmap。
2.由于材质变化得很频繁,所以我们需要设置简单高效的过滤器。

var materialArray =[];materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));var faceMaterial = new THREE.MeshFaceMaterial(materialArray);var mesh = new THREE.Mesh(geom,faceMaterial);

在帧循环中更新材质

if(video.readyState === video.HAVE_ENOUGH_DATA){    if(texture)        texture.needsUpdate = true;}

小结:

使用纹理时的注意事项:
1.纹理图片的类型可以是png、jpg或gif格式的。图片加载是异步的,所以要么使用渲染循环,要么在加载纹理时提供一个回调函数。
2.使用正方形纹理。这样就可以使用mipmap来达到更好的效果。
3.标准情况下,Three.js并不支持反光,可以使用环境贴图创建出虚假的反光。
4.要想直接控制物体表面的光亮贴图,可以使用高光贴图。,
5.纹理的repeat属性,可以让纹理自我复制。还要记住将材质的包裹属性从 ClampToEdgeWrapping 改成 RepeatWrapping
6.Thee.js中可以用HTML5画布元素或者视频元素创建动态纹理。

0 0
原创粉丝点击