我们在ThreeJS-3D教学二:基础形状展示中有简单介绍过一些常用的材质,这次我们举例来具体看下效果:
代码是这样的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body {
width: 100%;
height: 100%;
}
* {
margin: 0;
padding: 0;
}
.label {
font-size: 20px;
color: #000;
font-weight: 700;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="importmap">
{
"imports": {
"three": "../three-155/build/three.module.js",
"three/addons/": "../three-155/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
let stats, labelRenderer, gpuPanel, spotLight;
let camera, scene, renderer, controls, earth;
const group = new THREE.Group();
let widthImg = 200;
let heightImg = 200;
let time = 0;
init();
initHelp();
initLight();
axesHelperWord();
animate();
// 添加平面
addPlane();
addBox();
function addBox() {
/**
* 凹凸贴图可以使纹理也有厚度,看起来更立体。凹凸贴图一般使用一张灰度图,设置成材料的bumpMap属性
* */
const loader = new THREE.TextureLoader();
loader.load("../materials/line_images/stone.jpg", texture => {
loader.load("../materials/line_images/stone-bump.jpg",bumpTexture => {
const boxGeo = new THREE.BoxGeometry(60, 60, 2);
const material = new THREE.MeshStandardMaterial({
map: texture,
bumpMap: bumpTexture,
bumpScale: 1
});
const box = new THREE.Mesh(boxGeo,material);
box.position.set(-45, 10, 0);
box.castShadow = true;
scene.add(box);
});
});
/**
* 法向贴图使用一张法向图来表示纹理图片某个点的法向量。即用一张图片保存另一张图片的法向量信息,
* 然后再在threejs中将这两个图片的信息合在一起,就形成了一个细节丰富的立体纹理
* 设置材质的 normalMap 属性
* */
loader.load("../materials/line_images/normal2.jpg", texture => {
loader.load("../materials/line_images/normal1.jpg", bumpTexture => {
const boxGeo = new THREE.BoxGeometry(60, 60, 2);
const material = new THREE.MeshStandardMaterial({
map: texture,
normalMap: bumpTexture
});
// material.normalScale.set(1, 1)
const box = new THREE.Mesh(boxGeo,material);
box.position.set(45, 10, 0);
box.castShadow = true;
scene.add(box);
});
});
// 高光贴图
loader.load("../materials/line_images/earth.jpg",textureNormal => {
loader.load("../materials/line_images/earthSpec.png", textureSpec => {
const meterial = new THREE.MeshPhongMaterial({
shininess: 5, //高光部分的亮度,默认30
map: textureNormal, // 普通纹理贴图
specularMap: textureSpec, // 高光贴图
specular: '#fff' // 高光部分的颜色
});
/**
SphereGeometry(radius:浮点,widthSegments:整数,heightSegments:整数)
radius------球体半径。默认值为1。
widthSegments---水平线段的数量。最小值为3,默认值为32。
heightSegments---垂直线段的数量。最小值为2,默认值为16。
*/
const earthGeo = new THREE.SphereGeometry(15, 50, 50);
earth = new THREE.Mesh(earthGeo, meterial);
earth.position.set(0, 15, 70);
earth.castShadow = true;
scene.add(earth);
});
});
}
function addPlane() {
// 创建一个平面 PlaneGeometry(width, height, widthSegments, heightSegments)
const planeGeometry = new THREE.PlaneGeometry(widthImg, heightImg, 1, 1);
// 创建 Lambert 材质:会对场景中的光源作出反应,但表现为暗淡,而不光亮。
const planeMaterial = new THREE.MeshPhongMaterial({
color: 0xb2d3e6,
side: THREE.DoubleSide
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 以自身中心为旋转轴,绕 x 轴顺时针旋转 45 度
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(0, -4, 0);
plane.castShadow = true;
plane.receiveShadow = true;
scene.add(plane);
}
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 1000 );
camera.up.set(0, 1, 0);
camera.position.set(60, 40, 60);
camera.lookAt(0, 0, 0);
scene = new THREE.Scene();
scene.background = new THREE.Color( '#ccc' );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
// 色调映射的曝光级别
renderer.toneMappingExposure = 1;
document.body.appendChild( renderer.domElement );
labelRenderer = new CSS2DRenderer();
labelRenderer.setSize( window.innerWidth, window.innerHeight );
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0px';
labelRenderer.domElement.style.pointerEvents = 'none';
document.getElementById( 'container' ).appendChild( labelRenderer.domElement );
controls = new OrbitControls( camera, renderer.domElement );
// 设置最大最小视距
controls.minDistance = 20;
controls.maxDistance = 1000;
window.addEventListener( 'resize', onWindowResize );
stats = new Stats();
stats.setMode(1); // 0: fps, 1: ms
document.body.appendChild( stats.dom );
gpuPanel = new GPUStatsPanel( renderer.getContext() );
stats.addPanel( gpuPanel );
stats.showPanel( 0 );
scene.add( group );
}
function initLight() {
const targetObject = new THREE.Object3D();
targetObject.position.set(0, 0, 0);
scene.add(targetObject);
spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(0, 80, 150);
spotLight.target = targetObject;
spotLight.angle = Math.PI / 4;
spotLight.intensity = 500;
spotLight.penumbra = 1;
spotLight.decay = 1;
spotLight.distance = 300;
// spotLight.map = textures['uv_grid_opengl.jpg'];
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 200;
spotLight.shadow.focus = 1;
scene.add(spotLight);
const lightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(lightHelper);
const AmbientLight = new THREE.AmbientLight(new THREE.Color('rgb(255, 255, 255)'));
scene.add( AmbientLight );
}
function initHelp() {
// const size = 100;
// const divisions = 5;
// const gridHelper = new THREE.GridHelper( size, divisions );
// scene.add( gridHelper );
// The X axis is red. The Y axis is green. The Z axis is blue.
const axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
}
function axesHelperWord() {
let xP = addWord('X轴');
let yP = addWord('Y轴');
let zP = addWord('Z轴');
xP.position.set(50, 0, 0);
yP.position.set(0, 50, 0);
zP.position.set(0, 0, 50);
}
function addWord(word) {
let name = `<span>${word}</span>`;
let moonDiv = document.createElement( 'div' );
moonDiv.className = 'label';
// moonDiv.textContent = 'Moon';
// moonDiv.style.marginTop = '-1em';
moonDiv.innerHTML = name;
const label = new CSS2DObject( moonDiv );
group.add( label );
return label;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
if (earth) {
time += 0.00005;
earth.rotateY(time);
}
stats.update();
controls.update();
labelRenderer.render( scene, camera );
renderer.render( scene, camera );
}
</script>
</body>
</html>
图片我依次放进来,方便大家本地看效果
stone.jpg
stone-bump.jpg
normal2.jpg
normal1.jpg
earth.jpg
earthSpec.png
具体的注释也都放代码里了!感觉不错的点个赞,光白嫖可还行!