结果图
threejs比例不变自适应
首先是设置相机和render,要注意的就是相机要加上aspect不然如果页面不是正方形看到的样式就会失调。
// ---------相机设置-------------
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
const cameraPostion = [20, 20, 20];
camera.position.set(...cameraPostion);
camera.lookAt(mesh.position);//指向mesh对应的位置
// 更新相机的宽高比
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();// ----------渲染器设置------------
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
// 定义threejs输出画布的尺寸(单位:像素px)
const width = window.innerWidth; //宽度
const height = window.innerHeight; //高度
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
// renderer.setPixelRatio(window.devicePixelRatio)
renderer.render(scene, camera); //执行渲染操作
在onmounted中加入resize事件的监听,这样即使缩放也不会又问题
window.addEventListener('resize', () => {const width = window.innerWidth; //宽度const height = window.innerHeight; //高度// camera.position.set(20, 20, 20);camera.lookAt(mesh.position);//指向mesh对应的位置// 更新相机的宽高比camera.aspect = width / height;// 更新摄像机的投影矩阵camera.updateProjectionMatrix();renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)renderer.render(scene, camera); //执行渲染操作});
鼠标控制视角
禁止拖拽和缩放是为了以防超出天空盒或者进入地球模型中,也可以设置这两个的范围
const controls = new MapControls(camera, renderer.domElement);
controls.enablePan = false; //禁止右键拖拽
controls.enableZoom = false;//禁止缩放
controls.addEventListener('change', function () {// 鼠标右键旋转时候,查看.position变化// 鼠标左键拖动的时候,查看.position、.target的位置会变化console.log('camera.position', camera.position);renderer.render(scene, camera); //执行渲染操作// console.log('controls.target', controls.target);
});
天空盒
这边我采用了球体来制作天空盒,因为cube要6张图很难找,刚好我看到了一个长图,就直接用了
//----------天空盒-----------
// 加载天空球纹理
// 创建TextureLoader加载天空盒纹理
let texture = new THREE.TextureLoader().load([require('../assets/longbk.jpeg')
]);
// 创建立方体几何体并应用天空盒材质
createGeometry(scene, 'Sphere', [500], [0, 0, 0],{map: texture, //设置材质环境贴图side: THREE.DoubleSide // 双面展示}
)
记得天空盒是需要双面贴图的,因为视角在里面,不然是看不见贴图的
遇到的问题以及一些补充
【2024/1/3】------ 之前的代码没有添加动画会导致刚打开的瞬间是黑屏的,因为加载texture是异步的,这个问题我一开始没有找到解决办法加个setTimeout什么的都不行,放在里面做回调函数也不行,知道我发现了只要加上动画就可以及时添加texture,如下
function render() {// i+=1;// console.log('执行次数'+i);renderer.render(scene, camera); //执行渲染操作requestAnimationFrame(render);//请求再次执行函数render
}
render();
具体代码
<template><div><div ref="webgl"></div></div>
</template><script setup>
// 引入three.js
import * as THREE from 'three';
import { createGeometry } from '../utils/threeFun.js'
import { onMounted, ref } from 'vue';
// 引入相机控件`MapControls`
import { MapControls } from 'three/addons/controls/OrbitControls.js';
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';const webgl = ref(null);// 创建3D场景对象Scene
const scene = new THREE.Scene();// // ----------场景布置------------
let earthTexture = new THREE.TextureLoader().load([require('../assets/earth.jpg')
]);
let mesh = createGeometry(scene, 'Sphere', [5], [0, 0, 0], {map: earthTexture,// color: 0xff0000,//0xff0000设置材质颜色为红色
})//=========测试用===========
// AxesHelper:辅助观察的坐标系
// const axesHelper = new THREE.AxesHelper(200)
// scene.add(axesHelper)//----------天空盒-----------
// 加载天空球纹理
// 创建TextureLoader加载天空盒纹理
let texture = new THREE.TextureLoader().load([require('../assets/longbk.jpeg')
]);
// 创建立方体几何体并应用天空盒材质
createGeometry(scene, 'Sphere', [50], [0, 0, 0],{map: texture, //设置材质环境贴图side: THREE.DoubleSide // 双面展示}
)// ---------相机设置-------------
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
const cameraPostion = [20, 20, 20];
camera.position.set(...cameraPostion);
camera.lookAt(mesh.position);//指向mesh对应的位置
// 更新相机的宽高比
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();// ----------渲染器设置------------
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
// 定义threejs输出画布的尺寸(单位:像素px)
const width = window.innerWidth; //宽度
const height = window.innerHeight; //高度
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
// renderer.setPixelRatio(window.devicePixelRatio)
renderer.render(scene, camera); //执行渲染操作const controls = new MapControls(camera, renderer.domElement);
controls.enablePan = false; //禁止右键拖拽
controls.enableZoom = false;//禁止缩放
controls.addEventListener('change', function () {// 鼠标右键旋转时候,查看.position变化// 鼠标左键拖动的时候,查看.position、.target的位置会变化console.log('camera.position', camera.position);renderer.render(scene, camera); //执行渲染操作// console.log('controls.target', controls.target);
});// --------- UI界面 ---------------
// 实例化一个gui对象
const gui = new GUI();// gui增加交互界面,用来改变obj对应属性
gui.add(mesh.position, 'x', 0, 15);
gui.add(mesh.position, 'y', 0, 15);
gui.add(mesh.position, 'z', 0, 15);function render() {// i+=1;// console.log('执行次数'+i);renderer.render(scene, camera); //执行渲染操作requestAnimationFrame(render);//请求再次执行函数render
}
render();
// ----------渲染器添加到页面------------
// 一定要放在onmounted里面,因为在onmounted的时候才有webgl这个dom元素
onMounted(() => {console.log('mounted');webgl.value.appendChild(renderer.domElement);// 监听浏览器窗口大小的变化,变化时重新渲染window.addEventListener('resize', () => {const width = window.innerWidth; //宽度const height = window.innerHeight; //高度// camera.position.set(20, 20, 20);camera.lookAt(mesh.position);//指向mesh对应的位置// 更新相机的宽高比camera.aspect = width / height;// 更新摄像机的投影矩阵camera.updateProjectionMatrix();renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)renderer.render(scene, camera); //执行渲染操作});})</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#webgl {width: 100px;height: 100px;
}
</style>
threeFun.js
import * as THREE from "three";
/**** @param {*} scene* @param {*} type 几何体类型* @param {*} size 几何体参数* @param {*} pos 放置位置数组* @param {*} material 材质对象*/
export function createGeometry(scene, type, size, pos, material) {console.log(scene, type, size, pos, material);let mesh;switch (type) {case "Box":mesh = createBox(scene, size, pos, material);break;case "Sphere":mesh = createSphere(scene, size, pos, material);break;case "Cylinder":mesh = createCylinder(scene, size, pos, material);break;case "Plane":mesh = createPlane(scene, size, pos, material);break;case "Circle":mesh = createCircle(scene, size, pos, material);break;default:console.log("类型错误");}return mesh;
}// ----------示例代码---------
// //创建一个长方体几何对象Geometry
// const geometry = new THREE.BoxGeometry(100, 100, 100);
// //创建一个材质对象Material
// const material = new THREE.MeshBasicMaterial({
// color: 0xff0000,//0xff0000设置材质颜色为红色
// });
// // 两个参数分别为几何体geometry、材质material
// const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
// //设置网格模型在三维空间中的位置坐标,默认是坐标原点
// let pos = [0, 10, 0]
// mesh.position.set(...pos);
// scene.add(mesh); //网格模型添加到场景中function createBox(scene, size, pos, materialParams) {const geometry = new THREE.BoxGeometry(...size);// const geometry = new THREE.BoxGeometry(100, 100, 100);//创建一个材质对象Materialconst material = new THREE.MeshBasicMaterial(materialParams);const mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(...pos);scene.add(mesh);console.log(scene);return mesh;
}
function createSphere(scene, size, pos, materialParams) {const geometry = new THREE.SphereGeometry(...size);//创建一个材质对象Materialconst material = new THREE.MeshBasicMaterial(materialParams);const mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(...pos);scene.add(mesh);return mesh;
}
function createCylinder(scene, size, pos, materialParams) {const geometry = new THREE.CylinderGeometry(...size);//创建一个材质对象Materialconst material = new THREE.MeshBasicMaterial(materialParams);const mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(...pos);scene.add(mesh);return mesh;
}
function createPlane(scene, size, pos, materialParams) {const geometry = new THREE.PlaneGeometry(...size);//创建一个材质对象Materialconst material = new THREE.MeshBasicMaterial(materialParams);const mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(...pos);scene.add(mesh);return mesh;
}
function createCircle(scene, size, pos, materialParams) {const geometry = new THREE.CircleGeometry(...size);//创建一个材质对象Materialconst material = new THREE.MeshBasicMaterial(materialParams);const mesh = new THREE.Mesh(geometry, material); //网格模型对象Meshmesh.position.set(...pos);scene.add(mesh);return mesh;
}