Hello World(2) - - 加载纹理

本文介绍如何在OpenGL中添加带纹理的地面和辅助坐标轴。通过加载纹理,调整映射方式,设置纹理包裹模式,解决背面纹理显示问题,实现更真实的3D场景效果。
摘要由CSDN通过智能技术生成

0. 本文可以做什么

在前一节的基础上,添加了有纹理的地面 、辅助坐标。

1. 添加地面

init()中加入

// floor
{
    var mat = new THREE.MeshBasicMaterial({color:0xffffff});
    var geom = new THREE.PlaneGeometry(1000, 1000, 1, 1);

    var floor = new THREE.Mesh(geom, mat);
    scene.add(floor);
}

和前面创建cube一样,这里创建地面。
不过用的是PlaneGeometry创建一个平面;参数分别是宽、长、宽分段、长分段。

接着运行,你会看到一个白色的平面在方块后面。
让我们再改变一下它的角度,在scene.add()前加入

floor.rotation.x = Math.PI * -0.5;

OpenGL使用的是右手坐标系,旋转方向也是右手定义的。
你可以将右手伸出做个GOOD的手势,大拇指沿X轴正方向,那么你的其余四指就是旋转的正方向。
于是——上述代码将floor绕X轴顺时针旋转90度。

然后运行,你会看到方块被白色的地面埋了一半。
我们不打算改变地面高度,于是你得修改cube的坐标。
在创建cube的代码中加入

cube.position.set(0, 50, 0);

运行,你会看到方块冒出来了。

2. 添加辅助坐标轴

你可能很需要画一个坐标轴,特别是在复杂的场景中确定空间位置的时候。

init()中加入

// axes
{
    var axes = new THREE.AxesHelper(300);
    scene.add(axes);
}

这里用了THREE.AxesHelper()来创建辅助坐标轴,参数是轴的长度。

运行,你会看到一个坐标轴从方块中心冒出来 。
你可能发现地面上的轴不是那么清晰,因为轴线和地面重合了。我们将它往上挪一点点,在add前加入

axes.position.y = 1;

接着运行,你会发现轴线清晰了。
坐标轴

在OpenGL中,面重合往往会带来一些麻烦,造成面闪烁;因为浮点数精度是有限的,如果你用OpenGL画阴影贴图,你肯定会遇到这个问题。

坐标轴的颜色

XYZ轴上面并没有标上字母啊,我怎么知道哪根轴是X轴?

我们表达颜色时用的是RGB,而表达坐标时用的是XYZ,很自然地会用R对应X这样,于是红色就是X轴,绿Y轴,蓝Z轴。
而且在数据结构方面,RGB和XYZ都用的是同一个数据结构是THREE.Vector3,为一个三维向量。

3. 给地面添加纹理

材质可以表达物体的光泽属性,让我们分辨物体是金属还是塑料;纹理则表达物体表面纹路。
通常二者结合起来就可以渲染出很不错的画面了。

我们在floor中创建mat材质前,加入

var tex = new THREE.TextureLoader().load('src/img/colors.png');

这张图片的地址是three.js/examples/textures/colors.png
上述代码用纹理加载器加载了一个纹理。

然后删掉原来的mat,替换成下面的

var mat = new THREE.MeshBasicMaterial({map: tex});

map:tex 设置我们的纹理映射是tex,我们用这个纹理就不再需要color了。

运行,你会看到图片被贴到地面上了
有纹理的地面

调整纹理映射

拉近了看,是不是纹理有点粗糙?
因为纹理图片像素是有限的,放大了自然会这样。

既然这是一个地面,我们可以接受重复,于是在创建tex后加入

tex.repeat.set(10, 10);

上述代码设置纹理映射在水平和垂直方向重复10次。

运行,你会发现
错误的纹理

这是怎么了?

设置纹理包裹

英文原文是Texture Wrapping 可能别的书籍上用的不是这个词“纹理包裹”。

纹理坐标可以用ST或者UV表示,是一个意思。
原点(0,0)在纹理的左上角,S/U是右,而T/V是下。右下角是(1,1)。

于是我们可以推断纹理图片的坐标范围不会超过1。
我们这里重复了10次,纹理坐标是10。而超出1这个范围的纹理该怎么取坐标呢?

可以设置纹理的包裹模式,three.js中有以下3种:
1. THREE.ClampToEdgeWrapping
2. THREE.RepeatWrapping
3. THREE.MirroredRepeatWrapping

默认值为第1种,即超出的部分取纹理边缘的颜色。
第2种是超出后就重复纹理,等于丢弃坐标中整数的部分。
第3种是镜像,一般地面会用这个模式。当纹理的整数部分是奇数时是第2种,是偶数时则会对应翻转。

如果我们知道了为什么纹理会变成这样,我们接着添加一句

tex.wrapS = tex.wrapT = THREE.RepeatWrapping;

上述代码分别设置2个方向上的重复模式,由此可见不同方向上可以用不同的模式。

我们这里没有用第3种模式,因为我们的贴图有点像瓷砖,重复起来会感觉很和谐。
如果贴图是一张草地的话,会用第3种模式。因为全是一样的草看来很假,不需要重复感。

运行,你会看到好得多的纹理渲染。

背面的纹理

如果你把镜头拉到下方,你又可以看到奇怪的现象
地面不见了

这是因为三维世界中,物体是有正反面区分的。
而为了运行效率,OpenGL默认会只绘制正面(如果你在前面把地面给转反了,那么你只会在下方才看到地面)。

我们需要设置纹理也贴到背面去。
mat创建时的代码改为这样

var mat = new THREE.MeshBasicMaterial({map: tex, side:THREE.DoubleSide});

这样我们就贴在正反面都应用了同一个纹理。

运行,你可以发现地面的反面也会渲染了,但是cube那里有些闪烁。
前面已经提到了面重合带来的问题,我们修改cube那里的代码,将cube向上移一点点

cube.position.set(0, 51, 0);

运行,终于正确了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值