three.js创建简单的法向贴图
在上一节,我们用了three.js创建了简单的凹凸贴图效果:点击查看凹凸贴图,凹凸贴图可以呈现出比普通贴图更多细节,也更具层次感,如果我们对场景要求更为细致,我们可以使用法向贴图对材质进行贴图,本文以THREE.MeshPhongMaterial为例,演示一个法向贴图的示例,法向贴图主要是利用材质的normalMap来贴图,利用normalScale来控制其纹理的凹凸程度,代码如下:
function createTextureMesh(geometry, image, otherImage) {
let map = new THREE.TextureLoader().load(image);
let normalMap = new THREE.TextureLoader().load(otherImage);
let material = new THREE.MeshPhongMaterial();
material.map = map;//打底贴图
material.normalMap = normalMap;//法向贴图
material.normalScale = new THREE.Vector2(0.3, 0.3);//凹凸程度
return new THREE.Mesh(geometry, material);
}
其中image和otherImage都是图片所在的路径;为了贴图看起来更加细致和逼真,这里用了普通贴图和法向贴图相叠加。接下来实现一个法向贴图的场景(PS:一般用于法向贴图的图片文件名字都会含有normal字眼,用于凹凸贴图的会含有bump字眼,例如:XX-normal.jpg、XX-bump.jpg等等,所以在贴图时,读者可以根据需求再去找相应的文件),这里笔者实现了一个交互菜单栏,用于实现法向贴图凹凸程度的调节,效果如下:
demo代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>法向贴图</title>
<script type="text/javascript" src="build/three.js"></script>
<script type="text/javascript" src="js/controls/OrbitControls.js"></script>
<script type="text/javascript" src="js/libs/stats.js"></script>
<script type="text/javascript" src="js/libs/dat.gui.min.js"></script>
<style>
body {
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script type="text/javascript" >
let container1 = document.createElement('div');
document.body.appendChild(container1);
let container2 = document.createElement('div');
document.body.appendChild(container2);
let scene = undefined;
let renderer = undefined;
let camera = undefined;
let pointLight = ambientLight = undefined;
let controls = undefined;
let stats = undefined;
let sphere = undefined, cube = undefined;
let guiFields = {
"normalScale": 0.3
};
main();
render();
function main() {
initScene();
initRenderer(container1);
initCamera();
initLight();
initControls();
normalMapMesh();
initGUI();
initStats(container2);
window.addEventListener('resize', onWindowResize, false);
}
function initScene() {
scene = new THREE.Scene();
}
function initCamera() {
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 35);
camera.lookAt(scene.position);
}
function initRenderer(container1) {
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(0xeeeeee, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMapEnabled = true;
container1.appendChild(renderer.domElement);
}
function initLight() {
ambientLight = new THREE.AmbientLight(0xffffff, 0.35);
scene.add(ambientLight);
pointLight= new THREE.PointLight(0xff5808);
pointLight.position.set(10, 10, 10);
scene.add(pointLight);
}
function initControls() {
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.autoRotate = false;
controls.enableKeys = true;
}
function initGUI() {
let gui = new dat.GUI();
gui.add(guiFields, 'normalScale', -2, 2).onChange(function (e) {
sphere.material.normalScale.set(e, e);
cube.material.normalScale.set(e, e);
});
}
function normalMapMesh() {
let image1 = "textures/general/plaster.jpg";
let otherImage1 = "textures/general/plaster-normal.jpg";
let image2 = "textures/general/metal-floor.jpg";
let otherImage2 = "textures/general/metal-floor-normal.jpg";
sphere = createTextureMesh(new THREE.SphereGeometry(8, 20, 20), image1, otherImage1);
sphere.position.set(-10, 0, 0);
scene.add(sphere);
cube = createTextureMesh(new THREE.BoxGeometry(10, 10, 10, 20, 20, 20), image2, otherImage2);
cube.position.set(10, 0, 0);
scene.add(cube);
}
function createTextureMesh(geometry, image, otherImage) {
let map = new THREE.TextureLoader().load(image);
let normalMap = new THREE.TextureLoader().load(otherImage);
let material = new THREE.MeshPhongMaterial();
material.map = map;
material.normalMap = normalMap;
material.normalScale = new THREE.Vector2(0.3, 0.3);
return new THREE.Mesh(geometry, material);
}
function initStats(container2) {
stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
container2.appendChild(stats.domElement);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight);
}
function render() {
controls.update();
stats.update();
requestAnimationFrame(render);
renderer.render(scene, camera);
}
</script>
</body>
</html>