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

第十章 加载和使用纹理

在材质中使用纹理

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画布元素或者视频元素创建动态纹理。

目录(共14章) 前言 本书内容 阅读之前的准备 读者对象 致谢 第1章 用Three.js创建你的第一个三维场景 1.1 使用Three.js的前提条件 1.2 获取源代码 1.3 创建HTML页面框架 1.4 渲染并展示三维对象 1.5 添加材质、灯光和阴影 1.6 用动画扩展你的首个场景 1.7 使用dat.GUI库简化试验 1.8 使用ASCII效果 1.9 总结 第2章 使用构建Three.js场景的基本组件 2.1 创建场景 2.2 使用几何和网格对象 2.3 选择合适的相机 2.4 总结 第3章 使用Three.js里的各种光源 3.1 探索Three.js库提供的光源 3.2 学习基础光源 3.3 总结 第4章 使用Three.js的材质 4.1 理解共有属性 4.2 从简单的网格材质(基础、深度和面)开始 4.3 学习高级材质 4.4 线段几何体的材质 4.5 总结 第5章 学习使用几何体 5.1 Three.js提供的基础几何体 5.2 总结 第6章 使用高级几何体和二元操作 6.1 ConvexGeometry 6.2 LatheGeometry 6.3 通过拉伸创建几何体 6.4 创建三维文本 6.5 使用二元操作组合网格 6.6 总结 第7章 粒子和粒子系统 7.1 理解粒子 7.2 粒子、粒子系统和BasicParticleMaterial 7.3 使用HTML5画布格式化粒子 7.4 使用纹理格式化粒子 7.5 从高级几何体中创建粒子系统 7.6 总结 第8章 创建、加载高级网格和几何体 8.1 几何体组合和合并 8.2 从外部资源中加载几何体 8.3 以Three.jsJSON格式保存和加载 8.4 使用Blender 8.5 导入三维格式文件 8.6 总结 第9章 创建动画和移动相机 9.1 基础动画 9.2 使用相机 9.3 变形动画和骨骼动画 9.4 使用外部模型创建动画 9.5 总结 第10加载使用纹理 10.1 在材质中使用纹理 10.2 纹理的高级用途 10.3 总结 第11章 定制着色器和渲染后期处理 11.1 设置后期处理 11.2 后期处理通道 11.3 创建自定义的后期处理着色器 11.4 总结 第12章 用Physijs在场景中添加物理效果 12.1 创建可用Physijs的基本Three.js场景 12.2 材质属性 12.3 基础图形 12.4 使用约束限制对象移动 12.5 总结
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值