纹理贴图

  • 简介
  • 简单案例
  • 结果
  • 分析
  • 完整代码


简介

  1. 纹理最基础的用法就是作为贴图被添加到材质上,当用这种方法创建网格时,网格的颜色就来源于纹理
  2. UV贴图实质上就是指定模型上的哪一部分需要被映射到纹理的相应位置
  3. 可以用如下方法加载纹理
var textureLoader = new THREE.TexturenLoader();
var texture = textureLoader.load("../../assets/textures/sss.jpg");

使用THREE.TextureLoader()从指定位置加载图片,图片格式可以是png,jpg或jpeg

  1. 纹理的加载是异步的:如果纹理加载较大,而程序在文件加载完成之前开始渲染场景,那么在最开始的瞬间会看到场景中一些物体没有贴图。如果希望等待纹理加载完成。可以为THREE.TextureLoader.load()添加回调函数。
var textureLoader = new THREE.TextureLoader();
var texture = textureLoder.load("../../assets/textures/sss.jpg",onloadFunction,onProgressFunction,onErrorFunction);

onloadFunction在纹理加载完成时被调用,onProgressFunction可以随时汇报加载进度,onError在纹理加载出故障时被调用

  1. 用图片来作为纹理使用时,最好使用长宽为2的次方的正方形图片
  2. 纹理的放大和缩小。可以设置magFilter属性来指定纹理如何放大,minFilter来指定纹理如何缩小
    这两个属性的属性值可以设置如下表所示的属性值

名称

描述

THREE.NearestFilter(最邻近过滤)

这个过滤器会将纹理上最近的像素颜色应用于面上。在放大时,会导致方块化,在缩小时,会丢失很多细节

THREE.LinearFilter(线性过滤)

这个过滤器最终的颜色是由周围四个像素值决定的。在缩小时仍会丢失一些细节,但是在放大时会平滑很多,方块化也比较少一些出现

也可以使用mipmap。mipmap是把纹理按照2的倍数进行缩小。mipmap纹理过滤模式如下表所示
关于mipmap,

💡 如果没有设置magFilter和minFilter属性的值。Three.js会将THREE.LinearFilter作为magFilter属性的默认值,将THREE.LinearMipMapLinearFilter作为minFilter属性的默认值。

简单案例

结果

threejs MeshStandardMaterial 贴图_javascript

分析

  1. 上述场景中有三个物体,分别运用了移位贴图,凹凸贴图、法向贴图(具体属性特征可以看之前的文章);
  2. 场景中有性能插件、光源、平面、GUI
  3. 先搭建HTML框架
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<meta name=viewport
		  content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no,minimal-ui">
	<script type="text/javascript" src="../libs/three/three.js"></script>
	<script type="text/javascript" src="../libs/three/controls/OrbitControls.js"></script>
	<script type="text/javascript" src="../libs/util/dat.gui.js"></script>
	<script type="text/javascript" src="../libs/util/Stats.js"></script>
	<script type="text/javascript" src="js/basicTexture.js"></script>
	<script type="text/javascript" src="../js/util.js"></script>
	<script type="text/javascript" src="js/utils.js"></script>
	<link rel="stylesheet" href="../css/default.css">
	<title>Title</title>
	<style></style>
	<script></script>
</head>
<body>
<div id="webgl-output">

</div>
<script type="text/javascript">
	(function() {
		draw()
	})()
</script>
</body>
</html>
  1. 进行场景的初步创建(定义场景、渲染器、摄像机),在定义渲染器时可以将渲染的结果添加到 html框架的div中
//初始化场景
    var scene = new THREE.Scene();
    //渲染器
    var renderer = new THREE.WebGLRenderer();
    renderer.setClearColor(new THREE.Color(0x000000));
    renderer.setSize(window.innerWidth, window.innerHeight);
    //告诉渲染器开启阴影投射
    renderer.shadowMap.enabled = true;
    document.getElementById("webgl-output").appendChild(renderer.domElement);
    //摄像机
    var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight,0.1,1000);
    camera.position.set(0,20,40);
    camera.lookAt(new THREE.Vector3(0,0,0));
    scene.add(camera);
  1. 设置光源和地面
//地面
    var groundPlane = addLargeGroundPlane(scene);
    groundPlane.position.y = -10;
    //聚光源
    var spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10,30,40);
    spotLight.shadow.mapSize.width = 20480;
    spotLight.shadow.mapSize.height = 20480;
    spotLight.shadow.camera.fov = 150;
    spotLight.castShadow = true;
    spotLight.decay = 2;
    spotLight.penumbra = 0.05;
    spotLight.name = "spotLight"
    scene.add(spotLight);
    scene.add(new THREE.AmbientLight(0x444444));
  1. 添加 物体与贴图
//加载器
    var textureLoader = new THREE.TextureLoader();
    //法向贴图
    var polyhedron = new THREE.BoxGeometry(4,4,4);
    var polyheMaterial = new THREE.MeshStandardMaterial({
        map: textureLoader.load("../assets/textures/general/plaster.jpg"),
        normalMap: textureLoader.load("../assets/textures/general/plaster-normal.jpg"),
        // bumpScale: 15,
        // normalScale: 1,
        metalness: 0.2,
        roughness: 0.07,
    });
    var polyhedronMesh = new THREE.Mesh(polyhedron,polyheMaterial);
    polyheMaterial.normalScale.set(-5,-5);
    polyhedronMesh.castShadow = true;
    polyhedronMesh.position.x = 10;
    polyhedronMesh.position.z =10;
    scene.add(polyhedronMesh);
    //凹凸贴图
    var polyhedron2 = new THREE.BoxGeometry(4,4,4);
    var polyheMaterial2 = new THREE.MeshStandardMaterial({
        map: textureLoader.load("../assets/textures/stone/stone.jpg"),
        bumpMap: textureLoader.load("../assets/textures/stone/stone-bump.jpg"),
        metalness: 0.2,
        roughness: 0.07,
    });
    var polyhedronMesh2 = new THREE.Mesh(polyhedron2,polyheMaterial2);
    polyhedronMesh2.castShadow = true;
    polyhedronMesh2.position.x = 0;
    polyhedronMesh2.position.z = 10;
    scene.add(polyhedronMesh2);
    //移动贴图
    var sphereGeometry = new THREE.SphereGeometry(4,20,20);
    var sphereMaterial = new THREE.MeshStandardMaterial({
        map: textureLoader.load("../assets/textures/w_c.jpg"),
        displacementMap: textureLoader.load("../assets/textures/w_d.png"),
        metalness: 0.2,
        roughness: 0.07,
        color: 0xffffff,
    });
    var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
    sphere.castShadow = true;
    sphere.position.x = -10;
    sphere.position.z = 10;
    scene.add(sphere);
  1. 添加性能插件、GUI控制界面
var stats = new Stats();
	document.getElementById("webgl-output").appendChild(stats.domElement);
 	var bump = new function (){
        this.rotationSpeed = 0.02;
        this.bumpScale = 1;
        this.normalScaleX = 1;
        this.normalScaleY = 1;
        this.displacementScale = sphereMaterial.displacementScale;
        this.displacementBias= sphereMaterial.displacementBias;
    }
    gui.add(bump,'bumpScale',-1,1,0.001).onChange(function (e){
        polyheMaterial2.bumpScale = e;
    });
    gui.add(bump,'normalScaleX',-3,3,0.001).onChange(function (e){
        polyheMaterial.normalScale.set(bump.normalScaleX,bump.normalScaleY);
    });
    gui.add(bump,'normalScaleY',-3,3,0.001).onChange(function (e){
        polyheMaterial.normalScale.set(bump.normalScaleX,bump.normalScaleY);
    })
    gui.add(bump,'displacementScale',-5,5,0.01).onChange(function(e){
       sphereMaterial.displacementScale = e;
    });
    gui.add(bump,'displacementBias',-5,5,0.01).onChange(function (e){
       sphereMaterial.displacementBias  = e;
    });
  1. 设置摄像机控制器
var controls = new THREE.OrbitControls( camera, renderer.domElement );
  1. 设置场景对浏览器的自适应
window.addEventListener("resize",OnResize,false);
	function OnResize(){
        camera.aspect = window.innerWidth/window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth,window.innerHeight);
    }
  1. 最后告诉渲染器使用指定的摄像机渲染
render()
    function render(){
        stats.update();
        requestAnimationFrame(render);
        renderer.render(scene, camera);

        // polyhedronMesh.rotation.y += 0.02;
    }

完整代码

var camera,renderer,scene
function draw(){
    //初始化性能插件
    var stats = new Stats();
	document.getElementById("webgl-output").appendChild(stats.domElement);
     //渲染器
    var renderer = new THREE.WebGLRenderer();
    renderer.setClearColor(new THREE.Color(0x000000));
    renderer.setSize(window.innerWidth, window.innerHeight);
    //告诉渲染器开启阴影投射
    renderer.shadowMap.enabled = true;
    document.getElementById("webgl-output").appendChild(renderer.domElement);
    //摄像机
    var camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight,0.1,1000);
    camera.position.set(0,20,40);
    camera.lookAt(new THREE.Vector3(0,0,0));
    scene.add(camera);
    //交互鼠标控制场景移动旋转
    var controls = new THREE.OrbitControls( camera, renderer.domElement );
    //是否可以缩放
    controls.enableZoom = true;
    //是否自动旋转
    controls.autoRotate = true;
    //设置相机距离原点的最远距离
    controls.minDistance  = 1;
    //设置相机距离原点的最远距离
    controls.maxDistance  = 200;
    //是否开启右键拖拽
    controls.enablePan = true;
    //初始化场景
    scene = new THREE.Scene();
    //地面
    var groundPlane = addLargeGroundPlane(scene);
    groundPlane.position.y = -10;
    //聚光源
    var spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-10,30,40);
    spotLight.shadow.mapSize.width = 20480;
    spotLight.shadow.mapSize.height = 20480;
    spotLight.shadow.camera.fov = 150;
    spotLight.castShadow = true;
    spotLight.decay = 2;
    spotLight.penumbra = 0.05;
    spotLight.name = "spotLight"
    scene.add(spotLight);
    // initDefaultLighting(scene);
    scene.add(new THREE.AmbientLight(0x444444));
    //加载器
    var textureLoader = new THREE.TextureLoader();
    //gui
    var gui = new dat.GUI();
    //坐标轴
    var axes = new THREE.AxesHelper(50);
    // scene.add(axes);
    //法向贴图
    var polyhedron = new THREE.BoxGeometry(4,4,4);
    var polyheMaterial = new THREE.MeshStandardMaterial({
        map: textureLoader.load("../assets/textures/general/plaster.jpg"),
        normalMap: textureLoader.load("../assets/textures/general/plaster-normal.jpg"),
        // bumpScale: 15,
        // normalScale: 1,
        metalness: 0.2,
        roughness: 0.07,
    });
    var polyhedronMesh = new THREE.Mesh(polyhedron,polyheMaterial);
    polyheMaterial.normalScale.set(-5,-5);
    polyhedronMesh.castShadow = true;
    polyhedronMesh.position.x = 10;
    polyhedronMesh.position.z =10;
    scene.add(polyhedronMesh);
    //凹凸贴图
    var polyhedron2 = new THREE.BoxGeometry(4,4,4);
    var polyheMaterial2 = new THREE.MeshStandardMaterial({
        map: textureLoader.load("../assets/textures/stone/stone.jpg"),
        bumpMap: textureLoader.load("../assets/textures/stone/stone-bump.jpg"),
        metalness: 0.2,
        roughness: 0.07,
    });
    var polyhedronMesh2 = new THREE.Mesh(polyhedron2,polyheMaterial2);
    polyhedronMesh2.castShadow = true;
    polyhedronMesh2.position.x = 0;
    polyhedronMesh2.position.z = 10;
    scene.add(polyhedronMesh2);
    //移动贴图
    var sphereGeometry = new THREE.SphereGeometry(4,20,20);
    var sphereMaterial = new THREE.MeshStandardMaterial({
        map: textureLoader.load("../assets/textures/w_c.jpg"),
        displacementMap: textureLoader.load("../assets/textures/w_d.png"),
        metalness: 0.2,
        roughness: 0.07,
        color: 0xffffff,
    });
    var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
    sphere.castShadow = true;
    sphere.position.x = -10;
    sphere.position.z = 10;
    scene.add(sphere);

    var bump = new function (){
        this.rotationSpeed = 0.02;
        this.bumpScale = 1;
        this.normalScaleX = 1;
        this.normalScaleY = 1;
        this.displacementScale = sphereMaterial.displacementScale;
        this.displacementBias= sphereMaterial.displacementBias;
    }
    gui.add(bump,'bumpScale',-1,1,0.001).onChange(function (e){
        polyheMaterial2.bumpScale = e;
    });
    gui.add(bump,'normalScaleX',-3,3,0.001).onChange(function (e){
        polyheMaterial.normalScale.set(bump.normalScaleX,bump.normalScaleY);
    });
    gui.add(bump,'normalScaleY',-3,3,0.001).onChange(function (e){
        polyheMaterial.normalScale.set(bump.normalScaleX,bump.normalScaleY);
    })
    gui.add(bump,'displacementScale',-5,5,0.01).onChange(function(e){
       sphereMaterial.displacementScale = e;
    });
    gui.add(bump,'displacementBias',-5,5,0.01).onChange(function (e){
       sphereMaterial.displacementBias  = e;
    });

    window.addEventListener("resize",OnResize,false);

    render()
    function render(){
        stats.update();
        requestAnimationFrame(render);
        renderer.render(scene, camera);
    }

    function OnResize(){
        camera.aspect = window.innerWidth/window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth,window.innerHeight);
    }
}