Recently, I am interested in 3D visualization. I have learned that ThreeJS is a common method to achieve 3D visualization, so ON the basis of self-learning ThreeJS, I plan to write a computer room Demo to practice
Click here to preview the project GitHub
The directory structure
├ ─ ─ the font / / font file | ├ ─ ─ ─ ─ the font. / / the vera.ttf font source file | └ ─ ─ ─ ─ the font. The json / / font file after the conversion ├ ─ ─ img/images/materials | ├ ─ ─ ─ ─ xx. PNG | ├ ─ ─ ─ ─ XXX. JPG | └ ─ ─ ─ ─… ├ ─ ─ js / / write your own js file | ├ ─ ─ ─ ─ composer_fn. Js / / post processing | ├ ─ ─ ─ ─ create_fn. Js / / create various geometric | ├ ─ ─ ─ ─ init_fn. Js / / initialize the project | └ ─ ─ ─ ─ Util_fn. Js / / tools function ├ ─ ─ lib / / need to introduce the js file | ├ ─ ─ ─ ─ three. Js | ├ ─ ─ ─ ─ OrbitControls. Js | ├ ─ ─ ─ ─ RenderPass. Js | └ ─ ─ ─ ─… ├ ─ ─ model / / modeling tools export model | ├ ─ ─ ─ ─ computer. GLTF | └ ─ ─ ─ ─… └─ index.html // import file
Initialize Three: scene, camera, renderer
First we should initialize Three, get our camera and renderer ready, and set up the scene
- Initialization Scenario
const scene = new THREE.Scene();
// Set the scene background, three types:
// 1. Plain background, one plane
scene.background = new THREE.Color("rgb(25, 35, 39)");
scene.background = new THREE.TextureLoader().load('img/back.jpg');
// 2. Cube background
scene.background = new THREE.CubeTextureLoader().setPath('img/').load(new Array(6).fill('back.jpg'));
// 3. Sphere panorama (background) image is achieved by setting up sphere and reverse magnification 100 times, where x magnification is negative
const geometry = new THREE.SphereGeometry(5.32.32);
const material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load("img/back.jpg")});const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
geometry.scale(- 100.100.100);
Copy the code
- Initializing the camera
const camera = new THREE.PerspectiveCamera(
75.window.innerWidth / window.innerHeight,
0.1.1000
);
camera.position.set(- 20.40.90); // Set the initial position of the camera
Copy the code
- Initialize the renderer
const renderer = new THREE.WebGLRenderer({ alpha: true.antialias: true }); // Alpha: transparent background, antialias: anti-aliasing
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement); // Add to the body, or to any element
Copy the code
- This is finally merged into a single function initThree in init_fn.js
// init_fn.js
function initThree(selector) {
const scene = new THREE.Scene();
scene.background = new THREE.Color("rgb(25, 35, 39)");
const camera = new THREE.PerspectiveCamera(
75.window.innerWidth / window.innerHeight,
0.1.1000
);
camera.position.set(- 20.40.90);
const renderer = new THREE.WebGLRenderer({ alpha: true.antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.querySelector(selector).appendChild(renderer.domElement);
return { scene, camera, renderer };
}
Copy the code
Add track Controller
In order to increase user interaction, we need to add a controller that can control the size of the model through the zoom wheel, the left mouse button rotation, and the right mouse button pan
- Start by importing the required files in index.html
<! -- index.html -->
<script src="lib/OrbitControls.js"></script>
Copy the code
- Then create the track controller
// init_fn.js
function initControls() {
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener('change'.function () {... });// Add events
return controls;
}
Copy the code
Add the Stats
Stats monitors FPS changes in real time to monitor the performance of rendered scenes
- Start by importing the required files in index.html
<! -- index.html -->
<script src="lib/stats.min.js"></script>
Copy the code
- And then create Stats
// init_fn.js
function initStats() {
const stats = new Stats();
document.body.appendChild(stats.dom);
return stats;
}
Copy the code
Initialize light
Lights are used to color objects, objects without lights will be dark, we usually add a natural light first to make sure everything is represented, and then add any lights as needed
// init_fn.js
function initLight() {
const ambientLight = new THREE.AmbientLight(0xffffff); // Natural light, light on every surface of every geometry
const pointLight = new THREE.PointLight(0xff0000.10); / / the point light source
pointLight.position.set(0.50.0); // Adjust the position of point light source
scene.add(ambientLight);
scene.add(pointLight);
return [ambientLight, pointLight];
}
Copy the code
Writing entry files
Write index. HTML and introduce the init_fn.js function we wrote earlier to initialize Three
<html>
<head>
<meta charset="UTF-8" />
<title>Three-Demo</title>
<style type="text/css">
body {
margin: 0;
}
#canvas-frame {
border: none;
background-color: #eeeeee; /* Sets the background color */
}
</style>
</head>
<body>
<div id="canvas-frame"></div>
<! -- Introduce ThreeJS -->
<script src="lib/three.js"></script>
<! -- Add controller -->
<script src="lib/OrbitControls.js"></script>
<! -- introducing FPS -->
<script src="lib/stats.min.js"></script>
<! -- self-wrapped function -->
<script src="js/init_fn.js"></script>
<script>
/ / initialization
const { scene, camera, renderer } = initThree(
"#canvas-frame"
);
const lights = initLight();
const controls = initControls();
const stats = initStats();
// camera.lookAt(10, 10, 10);
function animate(time) {
stats.update(); // After initializing stats, you need to execute the update method here to implement real-time FPS monitoring
renderer.render(scene, camera); // The scene needs to be rendered, without which nothing will be displayed
requestAnimationFrame(animate); // Use the browser API -- requestAnimationFrame, each frame is rendered, renderer.render(...) methods
}
animate();
</script>
</body>
</html>
Copy the code
Create a Machine (I) – Create geometry
The above steps are just the initialization of “Three”, which is the preliminary preparation. At this time, the scene is still empty and empty. We need to add various geometers to the scene and render them
Effect:
// create_fn.js
// Create a machine (sphere)
function createEarth() {
const geometry = new THREE.SphereBufferGeometry(5.64.64); // Construct a spherical Geometry with BufferGeometry performance better than Geometry
const texture = new THREE.TextureLoader().load("./img/earth.png"); // Create a texture map and paste it onto a surface
const material = new THREE.MeshBasicMaterial({ map: texture }); // To create a material, the map property is passed in the newly created texture map
const mesh = new THREE.Mesh(geometry, material); // Use Mesh to connect geometry and material to form the final object
mesh.position.x = - 15;
mesh.position.y = - 1; // Change the position of the geometry
return mesh;
}
Copy the code
Add geometry to the scene and render
<html>
<head>.</head>
<body>
<div id="canvas-frame"></div>
<! -- Some JS introduced -->
<script src="lib/three.js"></script>
<script src="lib/OrbitControls.js"></script>
<script src="lib/stats.min.js"></script>
<! -- self-wrapped function -->
<script src="js/init_fn.js"></script>
<script src="js/create_fn.js"></script>
<script>
const { scene, camera, renderer } = initThree(
"#canvas-frame"
);
const lights = initLight();
const controls = initControls();
const stats = initStats();
// The newly added code
const mesh = createEarth();
scene.add(mesh); // Add the object to the scene
function animate(time) {
stats.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
Copy the code
Create a Machine (2) – Create multi-material geometry
Effect:
// create_fn.js
// Create a machine (cylinder) with path as the upper and lower map picture path
function createMachine(path, conf) {
const geometry = new THREE.CylinderBufferGeometry(5.5.2.64);
const texture = createTexture(path); // Since maps are often used, a function is removed to create texture maps
const bottomMaterial = new THREE.MeshBasicMaterial({ map: texture });
const sideMaterial = new THREE.MeshBasicMaterial({ color: "#1296DB" });
const materials = [sideMaterial, bottomMaterial, bottomMaterial]; /* Material can be a single value or an array. If an array is used, it means that a different material is applied to each face. In this case, array is used
const mesh = new THREE.Mesh(geometry, materials);
initConfig(mesh, conf); // Write a separate function because you often need to transform an object (change position, size, etc.)
return mesh;
}
// Create a texture map with path as the map image path
function createTexture(path, conf) {
const texture = new THREE.TextureLoader().load(path);
initConfig(texture, conf);
return texture;
}
// Process the incoming conf, since most geometry can be set to position, rotation, scale, etc
InitConfig (mesh, {position: {x: -15, y: -1}})
The first parameter doesn't need to be passed to the mesh, it can also be passed to the Texture
function initConfig(mesh, conf) {
if (conf) {
const { position, rotation, scale, repeat } = conf;
if (position) {
const { x, y, z } = position;
x ? (mesh.position.x = x) : null;
y ? (mesh.position.y = y) : null;
z ? (mesh.position.z = z) : null;
}
if (rotation) {
const { x, y, z } = rotation;
x ? (mesh.rotation.x = x) : null;
y ? (mesh.rotation.y = y) : null;
z ? (mesh.rotation.z = z) : null;
}
if (scale) {
const { x, y, z } = scale;
x ? (mesh.scale.x = x) : null;
y ? (mesh.scale.y = y) : null;
z ? (mesh.scale.z = z) : null;
}
if (repeat) {
const { x, y } = repeat;
// Handle the repeat of the Texture
if (x) {
// Set the number of repetitions in the x direction
mesh.wrapS = THREE.RepeatWrapping;
mesh.repeat.x = x;
}
if (y) {
// Set the number of repeats in the y directionmesh.wrapT = THREE.RepeatWrapping; mesh.repeat.y = y; }}}}Copy the code
Create a machine (3) – Import the model
Usually, in actual projects, objects created by ThreeJS alone cannot meet our needs. In this case, modeling software such as 3DS MAX and Blender should be used for modeling, and then introduced into Three
- Import the required loader files
<! -- index.html -->
<! -- Import GLTF model, need both loaders -->
<script src="lib/DRACOLoader.js"></script>
<script src="lib/GLTFLoader.js"></script>
Copy the code
- Into the model
// create_fn.js
// Create an imported model with path as the model path
function createImportModel(path, conf) {
// Since GLTFLoader can only fetch geometry in the form of callback functions, we add promises to make it easier to fetch geometry later
return new Promise((res) = > {
const dracoLoader = new THREE.DRACOLoader().setDecoderPath(".. /js/draco/"); // ThreeJS source code has an example folder, where js directory has a Draco directory, also want to import this directory
const loader = new THREE.GLTFLoader().setDRACOLoader(dracoLoader);
loader.load(path, function (gltf) {
// GLTF objects have many properties. Gltf.scene is the geometry we need
initConfig(gltf.scene, conf);
res(gltf.scene); // Pass the object out
});
});
}
Copy the code
Effect:
// create_fn.js
// Create an imported model
function createImportModel(path, conf) {
return new Promise((res) = > {
const dracoLoader = new THREE.DRACOLoader().setDecoderPath(".. /js/draco/");
const loader = new THREE.GLTFLoader().setDRACOLoader(dracoLoader);
loader.load(path, function (gltf) {
const colorArr = [
"# 999"."rgb(110, 105, 112)"."#7fffd4"."#ffe4c4"."#faebd7"."#a9a9a9"."#5f9ea0"."#6495ed",];// There is a traverse method on the scene to traverse its children and determine if the child belongs to the Mesh class. If it does, it means that the child is a geometry and you can do something about it: add a material
gltf.scene.traverse(function (child) {
if (child instanceof THREE.Mesh) {
// Add different colors for different parts of the model (i.e. different geometry).
child.material = new THREE.MeshBasicMaterial({
color: colorArr.pop(), }); }}); initConfig(gltf.scene, conf); res(gltf.scene);// Pass the object out
});
});
}
Copy the code
New model:
// create_fn.js
// Create an imported model
function createImportModel(path, conf) {
return new Promise((res) = > {
const dracoLoader = new THREE.DRACOLoader().setDecoderPath(".. /js/draco/");
const loader = new THREE.GLTFLoader().setDRACOLoader(dracoLoader);
loader.load(path, function (gltf) {
const colorArr = [
"# 999"."rgb(110, 105, 112)"."#7fffd4"."#ffe4c4"."#faebd7"."#a9a9a9"."#5f9ea0"."#6495ed",]; gltf.scene.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material = new THREE.MeshBasicMaterial({ color: colorArr.pop() });
// Add wire frames for different parts of the model (i.e. different geometry) to make each part angular and more realistic
const geometry = new THREE.EdgesGeometry(child.geometry); // Edge geometry
const material = new THREE.LineBasicMaterial({ color: "#dcdcdc" }); // Wireframes material
// material.depthTest = false; // Depth test, if enabled, border transparent effect
const mesh = new THREE.LineSegments(geometry, material);
child.add(mesh); // Must be added to the child, not to the scene, to ensure that the position relative to the geometry is always the same}}); initConfig(gltf.scene, conf); res(gltf.scene);// Pass the object out
});
});
}
Copy the code
At this point, all our work is done, and we’re done! Final effect:
The words in the back
This is my first time to study 3D visualization and learn from ThreeJS and WebGL. Please point out any mistakes or questions in the comments section
The articles referred to in this article:
- Codepen. IO/rachsmith/p… The official recommended introductory tutorial
- Blog.csdn.net/qq_37540004… ThreeJs does smart city projects