threejs学习day4:材质

Posted 简数

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了threejs学习day4:材质相关的知识,希望对你有一定的参考价值。

材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体的颜色、纹理贴图、光照模式等。

本章将介绍基本材质、两种基于光照模型的材质,以及使用法向量作为材质。除此之外,本章还将介绍如何使用图像作为材质。

基本材质

使用基本材质(BasicMaterial)的物体,渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。如果没有指定材质的颜色,则颜色是随机的。其构造函数是:

THREE.MeshBasicMaterial(opt)

其中,opt可以缺省,或者为包含各属性的值。如新建一个不透明度为0.75的黄色材质:

new THREE.MeshBasicMaterial({
    color: 0xffff00,
    opacity: 0.75
});

将其应用于一个正方体(方法参见3.1节),效果为:

接下来,我们介绍几个较为常用的属性。

  • visible:是否可见,默认为true

  • side:渲染面片正面或是反面,默认为正面THREE.FrontSide,可设置为反面THREE.BackSide,或双面THREE.DoubleSide

  • wireframe:是否渲染线而非面,默认为false

  • color:十六进制RGB颜色,如红色表示为0xff0000

  • map:使用纹理贴图,详见4.5节

对于基本材质,即使改变场景中的光源,使用该材质的物体也始终为颜色处处相同的效果。当然,这不是很具有真实感,因此,接下来我们将介绍更为真实的光照模型:Lambert光照模型以及Phong光照模型。

Lambert材质

Lambert材质(MeshLambertMaterial)是符合Lambert光照模型的材质。Lambert光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。

其光照模型公式为:

Idiffuse = Kd * Id * cos(theta)

其中,Idiffuse是漫反射光强,Kd是物体表面的漫反射属性,Id是光强,theta是光的入射角弧度。

当然,对于使用Three.js的Lambert材质,不需要了解以上公式就可以直接使用。创建一个黄色的Lambert材质的方法为:

new THREE.MeshLambertMaterial({
    color: 0xffff00
})

在使用了光照之后(具体方法参见第8章),得到这样的效果:

threejs学习day4:材质

color是用来表现材质对散射光的反射能力,也是最常用来设置材质颜色的属性。除此之外,还可以用ambientemissive控制材质的颜色。

ambient表示对环境光的反射能力,只有当设置了AmbientLight后,该值才是有效的,材质对环境光的反射能力与环境光强相乘后得到材质实际表现的颜色。

emissive是材质的自发光颜色,可以用来表现光源的颜色。单独使用红色的自发光:

new THREE.MeshLambertMaterial({
    emissive: 0xff0000
})

效果为:

threejs学习day4:材质

如果同时使用红色的自发光与黄色的散射光:

new THREE.MeshLambertMaterial({
    color: 0xffff00,
    emissive: 0xff0000
})

效果为:

threejs学习day4:材质

如果将同样的材质用于球体,看起来像不像滚烫的太阳呢?

threejs学习day4:材质

Phong材质

Phong材质(MeshPhongMaterial)是符合Phong光照模型的材质。和Lambert不同的是,Phong模型考虑了镜面反射的效果,因此对于金属、镜面的表现尤为适合。

漫反射部分和Lambert光照模型是相同的,镜面反射部分的模型为:

 Ispecular = Ks * Is * (cos(alpha)) ^ n

其中,Ispecular是镜面反射的光强,Ks是材质表面镜面反射系数,Is是光源强度,alpha是反射光与视线的夹角,n是高光指数,越大则高光光斑越小。

由于漫反射部分与Lambert模型是一致的,因此,如果不指定镜面反射系数,而只设定漫反射,其效果与Lambert是相同的:

例4.3.1

new THREE.MeshPhongMaterial({
    color: 0xffff00
});

threejs学习day4:材质

同样地,可以指定emissiveambient值,这里不再说明。下面就specular值指定镜面反射系数作说明。首先,我们只使用镜面反射,将高光设为红色,应用于一个球体:

var material = new THREE.MeshPhongMaterial({
    specular: 0xff0000
});
var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 20, 8), material);

效果为:

threejs学习day4:材质

可以通过shininess属性控制光照模型中的n值,当shininess值越大时,高光的光斑越小,默认值为30。我们将其设置为1000时:

new THREE.MeshPhongMaterial({
    specular: 0xff0000,
    shininess: 1000
});

效果为:

threejs学习day4:材质

使用黄色的镜面光,红色的散射光:

material = new THREE.MeshPhongMaterial({
    color: 0xff0000,
    specular: 0xffff00,
    shininess: 100
});

threejs学习day4:材质

看起来是不是像个桌球了呢?

法向材质

法向材质可以将材质的颜色设置为其法向量的方向,有时候对于调试很有帮助。

法向材质的设定很简单,甚至不用设置任何参数:

new THREE.MeshNormalMaterial()

材质的颜色与照相机与该物体的角度相关,下面我们只改变照相机位置,观察两个角度的颜色变化:

camera.position.set(5, 25, 25);的效果:

threejs学习day4:材质

camera.position.set(25, 25, 25);的效果:

threejs学习day4:材质

我们观察的是同样的三个面,但是由于观察的角度不同,物体的颜色就不同了。因此,在调试时,要知道物体的法向量,使用法向材质就很有效。

材质的纹理贴图

在此之前,我们使用的材质都是单一颜色的,有时候,我们却希望使用图像作为材质。这时候,就需要导入图像作为纹理贴图,并添加到相应的材质中。下面,我们介绍具体的做法。

单张图像应用于长方体

首先,我们选择一张长宽均为128像素的图像:

threejs学习day4:材质

将其导入纹理中:

var texture = THREE.ImageUtils.loadTexture('../img/0.png');

然后,将材质的map属性设置为texture

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

这样就完成了将图片应用于材质的基本步骤。但是由于现在我们还没使用动画,画面只被渲染了一次,而在导入纹理之前,已经完成了这次渲染,因此看到的只是一片黑。所以,如果没有重绘函数(将在下一章介绍),就需要在完成导入纹理的步骤后,重新绘制画面,这是在回调函数中实现的:

var texture = THREE.ImageUtils.loadTexture('../img/0.png', {}, function() {
    renderer.render(scene, camera);
});
var material = new THREE.MeshLambertMaterial({
    map: texture
});

现在,就能看到这样的效果了:

threejs学习day4:材质

类似地,如果将其应用于球体,将会把整个球体应用该图像:

threejs学习day4:材质

六张图像应用于长方体

有时候,我们希望长方体的六面各种的贴图都不同。因此,我们首先准备了六张颜色各异的图像,分别写了数字0到5。然后,分别导入图像到六个纹理,并设置到六个材质中:

var materials = [];
for (var i = 0; i < 6; ++i) {
    materials.push(new THREE.MeshBasicMaterial({
        map: THREE.ImageUtils.loadTexture('../img/' + i + '.png',
                {}, function() {
                    renderer.render(scene, camera);
                }),
        overdraw: true
    }));
}

var cube = new THREE.Mesh(new THREE.CubeGeometry(5, 5, 5),
        new THREE.MeshFaceMaterial(materials));
scene.add(cube);

效果为:

threejs学习day4:材质

棋盘格


现在,我们有一个黑白相间的图像:

threejs学习day4:材质

我们希望用它填满一个屏幕。按照之前的做法依法炮制:

var texture = THREE.ImageUtils.loadTexture('../img/chess.png', {}, function() {
    renderer.render(scene, camera);
});

效果是:

可是,棋盘格是8横8纵64个小方格组成的,那应该怎么办呢?

首先,我们需要指定重复方式为两个方向(wrapSwrapT)都重复:

texture.wrapS = texture.wrapT = THREE.RepeatWrapping;

然后,设置两个方向上都重复4次,由于我们的图像本来是有2行2列,所以重复4次即为8行8列:

texture.repeat.set(4, 4);

最终就得到了棋盘格:


以上是关于threejs学习day4:材质的主要内容,如果未能解决你的问题,请参考以下文章

threejs学习笔记-04

threejs学习笔记-04

threejs学习笔记-04

threejs学习day5:网格

ThreeJS功能解读—材质

three.js(4)-网格Lambert材质