问题
在开启今天的学习之前,我们先解决一个问题:当我们使用右击让物体远离中心时,会看起来扭曲或拉伸
解决办法:
//透视投影(透视投影会使得远离中心的物体看起来扭曲或拉伸,这是一个不可避免的数学事实)
//const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//解决办法是使用正交投影:
const camera = new THREE.OrthographicCamera(window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1, 1000);
//设置相机的缩放比例(正交投影会使得物体变得非常小,所以你需要设置一下缩放比例,比如放大一百倍)
camera.zoom = 100;
// 更新相机的投影矩阵(这句话好像不用加)
camera.updateProjectionMatrix();
官方:
https://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene
在官方搜索:AxesHelper
// 添加坐标轴辅助器(相当于一个线段,看成一个物件)
const axesHelper = new THREE.AxesHelper(5);//线段的长度为5
scene.add(axesHelper);//添加到场景中
// 补充
// 设置相机位置
//camera.position.set(0, 0, 10);
//camera.position.set(0, 15, 70);
camera.position.z = 6;
camera.position.y = 6;
camera.position.x = 6;
scene.add(camera);
有了这个,就可以用右键进行拖动了
测试了一下,不仅仅是右键,总结起来:
如果我不想使用three.js轨道控制器的默认配置,我想让:中间移动,右键缩放,应该怎么办?(请看问题集中处理部分)
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 创建轨道控制器(此时你可以使用右键让模型动起来)
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.enableDamping = true;
//设置阻尼的系数
controls.dampingFactor = 0.05;
//让其主动的旋转
controls.autoRotate = true;
// 动画循环渲染
function animate() {
// 如果没有鼠标交互,立方体会自动旋转
if (!isDragging) {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
}
controls.update();//更新轨道控制器/
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate(); // 开始动画循环
//cube.position.x = 2;
//等价于
cube.position.set(2, 0, 0);
父组件的坐标是多少就是多少,
子组件的真实坐标=父组件坐标+子组件坐标
默认是"XYZ",
先旋转X轴,然后是Y轴,最后是Z轴
// 监听窗口大小变化事件
window.addEventListener('resize', function () {
var width = window.innerWidth;
var height = window.innerHeight;
//1. 更新渲染器大小
renderer.setSize(width, height);
//2.更新相机的宽高比
camera.aspect = width / height;
// 设置渲染器的像素比(这个好像可有可无,不过加上确实更好一点,拉伸更自然了)
renderer.setPixelRatio(window.devicePixelRatio);
// 3.更新相机的投影矩阵
camera.updateProjectionMatrix();
});
window.addEventListener("dblclick", () => {
const fullScreenElement = document.fullscreenElement;
if (!fullScreenElement) {
// 双击控制屏幕进入全屏,退出全屏
// 让画布对象全屏
renderer.domElement.requestFullscreen();///真实功能实现
} else {
// 退出全屏,使用document对象
document.exitFullscreen();
}
// console.log(fullScreenElement);
});
//导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
//lil-gui
let eventObj = {
Fullscreen: function () {
renderer.domElement.requestFullscreen();
gui.show();
},
ExitFullscreen: function () {
document.exitFullscreen();
gui.show();
}
};
//创建GUI
const gui = new GUI();
//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
gui.close();
// 创建一个折叠的文件夹
const folder = gui.addFolder('全屏设置');
// 添加按钮到折叠文件夹
folder.add(eventObj, 'Fullscreen').name('全屏');
folder.add(eventObj, 'ExitFullscreen').name('退出全屏');
// 关闭折叠面板
folder.close();
const folder1 = gui.addFolder('立方体位置设置');
folder1.add(cube.position, "x", -5, 5).name("立方体x轴位置").onChange((val) => {
console.log("cube's x position:", val);
});
folder1.add(cube.position, "y", -5, 5).name("立方体y轴位置");
folder1.add(cube.position, "z", -5, 5).name("立方体z轴位置");
//.min(-10).max(10).step(1)
folder1.close();
const folder2 = gui.addFolder('线框模式设置');
folder2.add(material, "wireframe").name("线框模式");
folder2.close();
const folder3 = gui.addFolder('颜色设置');
let colorParams = {
cubeColor: "#ff0000",
};
folder3.addColor(colorParams, "cubeColor")
.name("颜色变化")
.onChange((val) => {
cube.material.color.set(val);
});
folder3.close();
有没有大神知道下面这个报错是什么原因?因为我是半路搞前端,对前端知之甚少,只是暂时学习一下three.js,后续可能更加倾向于cpp+opengl
//一共需要三步:1.创建加载器2.加载图片3.将纹理放入材质
//1.创建纹理加载器
let textureLoader = new THREE.TextureLoader();
//2.加载纹理
let texture = textureLoader.load(
'./src/picture/2.png',
// onLoad回调
function (texture) {
// in this example we create the material when the texture is loaded
console.log('Texture loaded successfully.');
},
// 目前暂不支持 onProgress 的回调
undefined,
// onError回调
function (err) {
console.log('An error happened while loading the texture.');
console.log('Error details:{{{[', err, ']}}}');
}
);
/********************************************/
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
//材质
const material = new THREE.MeshBasicMaterial({
color: 0xffffff,
map: texture,///3.将贴图添加到立方体的材质中
});
'./src/picture/2.png'
我困在这里一下午,后来被大神同事一下子就搞定了!!!太牛了!!!
package.json的位置才是根目录!!!
你需要在index-r5u9b8gU.js
中,重新修改png图片的位置
three自带的几何体模型是自带顶点数据的,所以当你使用纹理贴图时,会自动匹配这个顶点去渲染,
而你自己渲染的stl文件,不会有自带的uv顶点数据,所以就会导致贴图不会按照正确的模型位置去渲染纹理的效果.
so,你需要手动去计算他大的uv顶点坐标!!!
//你在使用three.js内置的模型时,直接向下面这样写就行
stlLoader.load('./src/stl/Down/m_CutGumPd0_12.stl', geometry => {
material12 = new THREE.MeshPhongMaterial({
color: 0xDDDADA,
map: texture
});
mesh12 = new THREE.Mesh(geometry, material12);
scene.add(mesh12);
});
const positions = geometry.getAttribute('position').array;
// 找到模型顶点坐标的最小值和最大值
let minX = Number.POSITIVE_INFINITY;
let maxX = Number.NEGATIVE_INFINITY;
let minY = Number.POSITIVE_INFINITY;
let maxY = Number.NEGATIVE_INFINITY;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
// 计算顶点坐标范围
const rangeX = maxX - minX;
const rangeY = maxY - minY;
// 计算 UV 坐标并归一化到 0 到 1 之间
const uvs = [];
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
const u = (x - minX) / rangeX;
const v = (y - minY) / rangeY;
uvs.push(u, v);
}
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
//这个是牙龈
stlLoader.load('./src/stl/Down/m_CutGumPd0_14.stl', (geometry) => {
/***************计算uv坐标*******************/
const positions = geometry.getAttribute('position').array;
// 找到模型顶点坐标的最小值和最大值
let minX = Number.POSITIVE_INFINITY;
let maxX = Number.NEGATIVE_INFINITY;
let minY = Number.POSITIVE_INFINITY;
let maxY = Number.NEGATIVE_INFINITY;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
minX = Math.min(minX, x);
maxX = Math.max(maxX, x);
minY = Math.min(minY, y);
maxY = Math.max(maxY, y);
}
// 计算顶点坐标范围
const rangeX = maxX - minX;
const rangeY = maxY - minY;
// 计算 UV 坐标并归一化到 0 到 1 之间
const uvs = [];
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
const u = (x - minX) / rangeX;
const v = (y - minY) / rangeY;
uvs.push(u, v);
}
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
/**********************************/
material14 = new THREE.MeshPhongMaterial({
color: 0xDDDADA,
map: texture2
});
mesh14 = new THREE.Mesh(geometry, material14);
scene.add(mesh14);
});
我这里有一张环境贴图,他是一个hdr文件
,如果你没有的话,可以去下面的网站中下载一下:
环境贴图示例文件
//导入环境贴图的加载器
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader"
import { useState, useEffect, Component } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import * as THREE from 'three';
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 引入指针锁定控制器扩展库PointerLockControls.js
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'
// 导入动画库
import gsap from "gsap";
//导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
class App extends Component {
render() {
return <div></div>
}
componentDidMount() {
// 设置场景、相机
const scene = new THREE.Scene();
// 设置场景颜色为蓝色
scene.background = new THREE.Color(0xC0C0C0);
//透视投影(透视投影会使得远离中心的物体看起来扭曲或拉伸,这是一个不可避免的数学事实)
//const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//解决办法是使用正交投影:
const camera = new THREE.OrthographicCamera(window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
1, 1000);
//设置相机的缩放比例(正交投影会使得物体变得非常小,所以你需要设置一下缩放比例,比如放大一百倍)
camera.zoom = 100;
// 更新相机的投影矩阵(这句话好像不用加)
camera.updateProjectionMatrix();
// 设置相机位置
//camera.position.set(0, 0, 10);
camera.position.set(0, 15, 70);
//camera.position.z = -6;
// camera.position.y = 6;
// camera.position.x = -6;
scene.add(camera);
//渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 监听窗口大小变化事件
window.addEventListener('resize', function () {
var width = window.innerWidth;
var height = window.innerHeight;
//1. 更新渲染器大小
renderer.setSize(width, height);
//2.更新相机的宽高比
camera.aspect = width / height;
// 设置渲染器的像素比(这个好像可有可无,不过加上确实更好一点,拉伸更自然了)
renderer.setPixelRatio(window.devicePixelRatio);
// 3.更新相机的投影矩阵
camera.updateProjectionMatrix();
});
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
//材质
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
//设置材质为线框模式
material.wireframe = false;
// const cube = new THREE.Mesh(geometry, material);
// //cube.position.x = 2;
// //等价于
// cube.position.set(0, 0, 0);
// scene.add(cube);
/**************/
// 创建轨道控制器(此时你可以使用右键让模型动起来)
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE, // 左键旋转
MIDDLE: THREE.MOUSE.PAN, // 中键平移
RIGHT: THREE.MOUSE.DOLLY // 右键缩放
};
controls.enableDamping = true;
//设置阻尼的系数
controls.dampingFactor = 0.05;
//让其主动的旋转
//controls.autoRotate = true;
// 添加坐标轴辅助器(相当于一个线段,看成一个物件)
const axesHelper = new THREE.AxesHelper(5);//线段的长度为5
scene.add(axesHelper);//添加到场景中
// 设置时钟
const clock = new THREE.Clock();
// window.addEventListener("dblclick", () => {
// const fullScreenElement = document.fullscreenElement;
// if (!fullScreenElement) {
// // 双击控制屏幕进入全屏,退出全屏
// // 让画布对象全屏
// renderer.domElement.requestFullscreen();
// } else {
// // 退出全屏,使用document对象
// document.exitFullscreen();
// }
// // console.log(fullScreenElement);
// });
/**************/
// 动画循环渲染
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate(); // 开始动画循环
// 辅助函数:将角度转换为弧度
function toRadians(angle) {
return angle * (Math.PI / 180);
}
let textureLoader = new THREE.TextureLoader();
//2.加载纹理
let texture1 = textureLoader.load('./src/picture/test.png');
let texture2 = textureLoader.load('./src/picture/test1.png');
let texture3 = textureLoader.load('./src/picture/test2.png');
let texture4 = textureLoader.load('./src/picture/test3.png');
let texture5 = textureLoader.load('./src/picture/2.png');
/********************bengin*****************
*光线投影+模型的位置+点击交互
**************/
//创建三个球
//1
const sphere1 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0x00FF00,
map: texture1,
})
);
sphere1.position.x = -4;
scene.add(sphere1);
//2
const sphere2 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0x0000FF,
map: texture5,
})
);
sphere2.position.x = 0;
scene.add(sphere2);
//3
const sphere3 = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshBasicMaterial({
color: 0xFF00FF,
map: texture4,
})
);
sphere3.position.x = 4;
scene.add(sphere3);
console.log(scene.children);
//创建射线
const raycaster = new THREE.Raycaster();
//创建鼠标向量
const mouse = new THREE.Vector2();
window.addEventListener("click", (event) => {
//console.log(event.clientX, event.clientY);
//设置鼠标向量的x,y值
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
//console.log(mouse.x, mouse.y);
//通过摄像机和鼠标为位置,更新射线
raycaster.setFromCamera(mouse, camera);
//计算物体和射线的焦点
const intersects = raycaster.intersectObjects([sphere1, sphere2, sphere3]);
if (intersects.length > 0) {
if (intersects[0].object._isSelect) {
//恢复原色
intersects[0].object.material.color.set(
intersects[0].object._originColor
);
intersects[0].object._isSelect = false;
// console.log("直接跳出来");
return;
}
intersects[0].object._isSelect = true;
intersects[0].object._originColor =
intersects[0].object.material.color.getHex();
intersects[0].object.material.color.set(0xff0000);
}
});
/********************end*******************************/
}
}
export default App
这个大佬写的非常好,大家可以去看一下:【Three.js使用STLLoader加载stl模型 - CSDN App】
//STL
import { Mesh } from "three";
import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
// 创建STL加载器
var loader = new STLLoader();
var material;
var mesh;
// 创建STL加载器
var stlLoader = new STLLoader();
stlLoader.load('./src/stl/lower.stl', geometry => {
material = new THREE.MeshPhongMaterial({ color: 0xDDDADA });
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
})
import { useState, useEffect, Component } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import * as THREE from 'three';
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 引入指针锁定控制器扩展库PointerLockControls.js
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'
// 导入动画库
import gsap from "gsap";
//导入lil.gui
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
//STL
import { Mesh } from "three";
import { STLLoader } from "three/examples/jsm/loaders/STLLoader";
class App extends Component {
render() {
return <div></div>
}
componentDidMount() {
//场景
const scene = new THREE.Scene();
// // 一个网格模型
// const geometry = new THREE.SphereGeometry(50, 25, 25);
// const material = new THREE.MeshPhongMaterial({
// color: 0x00ffff,
// specular: 0x111111,
// });
// const mesh = new THREE.Mesh(geometry, material);
// scene.add(mesh); //模型对象添加到场景中
// 创建STL加载器
var loader = new STLLoader();
var material;
var mesh;
// 创建STL加载器
var stlLoader = new STLLoader();
stlLoader.load('./src/stl/lower.stl', geometry => {
material = new THREE.MeshPhongMaterial({ color: 0xDDDADA });
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
})
//光源设置
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(400, 200, 300);
scene.add(directionalLight);
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
//辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);
const gui = new GUI(); //创建GUI对象
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
color: 0x00ffff,// 材质颜色
specular: 0x111111,// 材质高光颜色
};
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
// 材质颜色color
matFolder.addColor(obj, 'color').onChange(function (value) {
material.color.set(value);
});
// 材质高光颜色specular
matFolder.addColor(obj, 'specular').onChange(function (value) {
material.specular.set(value);
});
// 环境光子菜单
const ambientFolder = gui.addFolder('环境光');
// 环境光强度
ambientFolder.add(ambient, 'intensity', 0, 2);
// 平行光子菜单
const dirFolder = gui.addFolder('平行光');
// 平行光强度
dirFolder.add(directionalLight, 'intensity', 0, 2);
// 平行光位置
dirFolder.add(directionalLight.position, 'x', -400, 400);
dirFolder.add(directionalLight.position, 'y', -400, 400);
dirFolder.add(directionalLight.position, 'z', -400, 400);
//渲染器和相机
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
camera.position.set(292, 223, 185);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
// 渲染循环
function render() {
// 当gui界面设置obj.bool为true,mesh执行旋转动画
if (obj.bool) mesh.rotateY(0.01);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
const controls = new OrbitControls(camera, renderer.domElement);
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE, // 左键旋转
MIDDLE: THREE.MOUSE.PAN, // 中键平移
RIGHT: THREE.MOUSE.DOLLY // 右键缩放
};
// 画布跟随窗口变化
window.onresize = function () {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
}
}
export default App
//打入补间动画tween
import * as TWEEN from "three/examples/jsm/libs/tween.module.js"
/********************补间动画begin**********************/
const tween = new TWEEN.Tween(sphere1.position);
tween.to({ x: 7 }, 3000);//3秒
//这个动画重复无数次
tween.repeat(Infinity);
tween.start();
//记得在渲染函数中对每一帧进行更新
/********************end*******************************/
// 动画循环渲染
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
//更新tween///补间动画///
TWEEN.update();/
}
animate(); // 开始动画循环
你可以通过修改轨道控制器的mouseButtons属性来实现这个功能。以下是回答:
轨道控制器(OrbitControls)是一种用于控制相机在场景中旋转、缩放和平移的控制器。它有一个mouseButtons属性,用于定义不同的鼠标按钮对应的不同的操作。默认情况下,mouseButtons属性的值是:
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE, // 左键旋转
MIDDLE: THREE.MOUSE.DOLLY, // 中键缩放
RIGHT: THREE.MOUSE.PAN // 右键平移
};
如果你想设置中键移动,你可以将mouseButtons属性的值修改为:
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE, // 左键旋转
MIDDLE: THREE.MOUSE.PAN, // 中键平移
RIGHT: THREE.MOUSE.DOLLY // 右键缩放
};
这样,你就可以使用中键来移动场景,而不是右键。你也可以根据你的喜好,自由地调整其他鼠标按钮的功能。
答:当然可以.
//导入字体
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js"
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'
// 添加坐标轴辅助器(相当于一个线段,看成一个物件)
const axesHelper = new THREE.AxesHelper(5);//线段的长度为5
scene.add(axesHelper);//添加到场景中
// 创建字体加载器
var fontLoader = new FontLoader();
// 加载字体文件
fontLoader.load('https://raw.githubusercontent.com/mrdoob/three.js/master/examples/fonts/helvetiker_regular.typeface.json', function (font) {
// 创建文本几何体
var textGeoX = new TextGeometry('X', {
font: font,
size: 0.5,
height: 0.1,
curveSegments: 6
});
var textGeoY = new TextGeometry('Y', {
font: font,
size: 0.5,
height: 0.1,
curveSegments: 6
});
var textGeoZ = new TextGeometry('Z', {
font: font,
size: 0.5,
height: 0.1,
curveSegments: 6
});
// 创建文本材质
var textMaterialX = new THREE.MeshBasicMaterial({
color: 0xff0000
});
var textMaterialY = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var textMaterialZ = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
// 创建文本网格对象
var textX = new THREE.Mesh(textGeoX, textMaterialX);
var textY = new THREE.Mesh(textGeoY, textMaterialY);
var textZ = new THREE.Mesh(textGeoZ, textMaterialZ);
// 将文本网格对象添加到坐标轴辅助对象中
axesHelper.add(textX);
axesHelper.add(textY);
axesHelper.add(textZ);
// 调整文本网格对象的位置和旋转
textX.position.x = 5.2;
textX.rotation.y = -Math.PI / 2;
textY.position.y = 5.2;
textZ.position.z = 5.2;
textZ.rotation.x = Math.PI / 2;
});
假如说,没有报错,你也没看到坐标轴,那你可以试一试改改参数,
eg:
/*******创建字体begin*********/
// 添加坐标轴辅助器(相当于一个线段,看成一个物件)
const axesHelper = new THREE.AxesHelper(90);//线段的长度为5
scene.add(axesHelper);//添加到场景中
// 创建字体加载器
var fontLoader = new FontLoader();
// 加载字体文件
fontLoader.load('https://raw.githubusercontent.com/mrdoob/three.js/master/examples/fonts/helvetiker_regular.typeface.json', function (font) {
// 创建文本几何体
var textGeoX = new TextGeometry('X', {
font: font,
size: 5,
height: 1,
curveSegments: 6
});
var textGeoY = new TextGeometry('Y', {
font: font,
size: 5,
height: 1,
curveSegments: 6
});
var textGeoZ = new TextGeometry('Z', {
font: font,
size: 5,
height: 1,
curveSegments: 6
});
// 创建文本材质
var textMaterialX = new THREE.MeshBasicMaterial({
color: 0xff0000
});
var textMaterialY = new THREE.MeshBasicMaterial({
color: 0x00ff00
});
var textMaterialZ = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
// 创建文本网格对象
var textX = new THREE.Mesh(textGeoX, textMaterialX);
var textY = new THREE.Mesh(textGeoY, textMaterialY);
var textZ = new THREE.Mesh(textGeoZ, textMaterialZ);
// 将文本网格对象添加到坐标轴辅助对象中
axesHelper.add(textX);
axesHelper.add(textY);
axesHelper.add(textZ);
// 调整文本网格对象的位置和旋转
textX.position.x = 95.2;
textX.rotation.y = -Math.PI / 2;
textY.position.y = 95.2;
textZ.position.z = 95.2;
textZ.rotation.x = Math.PI / 2;
});
/*******创建字体end*********/
科学上网
如果你不想每次都要科学上网,才能找到这个json文件,
你可以将该网站的内容进行copy,放入到json文件中,如下:
//fontLoader.load('https://raw.githubusercontent.com/mrdoob/three.js/master/examples/fonts/helvetiker_regular.typeface.json', function (font) {
//改成
fontLoader.load('./src/json/font.json', function (font) {
03-几何体顶点转化_顶点位移_旋转_缩放
在使用three.js编写代码是,使用它的lil.gui,在本地使用npm run dev运行后,出现bug:折叠后仍然显示,然后你去修改折叠后上面的选项是无法生效的,那就说明,折叠成功了,但是页面没有刷新
但是:当你使用npm run build将相关代码部署到cpp服务器上时,就没有了这个bug!!!(这个就是:解决的办法,部署上就好了)
(这个你需要知道两个材质:物理材质->MeshBasicMaterial 和 着色器材质->ShaderMaterial)
1.CPU的运行速率比gpu的运行速率快,但是cpU 1次只能执行一次计算,而CPU可以一次执行多次计算
2. 着色器依赖与glsl语言
//eg:
const click_fragmentShader = `
uniform sampler2D texturetooth;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vViewPosition;
void main(){
vec4 textureColor = texture2D(texturetooth, vUv + 0.1);
// 光照参数
vec3 lightColor = vec3(1.0, 1.0, 1.0); // 光源颜色
vec3 lightDirection = normalize(vec3(0.0, 0.0, 1.0)); // 光线方向
float ambientIntensity = 0.3;
float diffuseIntensity = max(dot(vNormal, lightDirection), 0.3);
vec3 diffuseReflection = lightColor * diffuseIntensity;
vec3 viewDirection = normalize(vViewPosition);
vec3 reflectDirection = reflect(-lightDirection, vNormal);
float specularIntensity = pow(max(dot(viewDirection, reflectDirection), 0.0), 32.0);
vec3 specularReflection = lightColor * specularIntensity;
vec3 finalColor = (ambientIntensity * textureColor.rgb) + (diffuseReflection * textureColor.rgb) + (specularReflection * textureColor.rgb);
gl_FragColor = vec4(finalColor, textureColor.a);
}`;
顶点着色器
和片段着色器
对象以及贴图与法线
(贴图和法线也是两个对象,直接加载文件[贴图是必须的,法线就看你自己的需要了])(到这里没着色器的事了)//你在点击了一颗牙齿或者牙龈后,我就换成这个这个片段着色器,哈哈哈//标记2
//eg:material_tooth_0.fragmentShader = click_fragmentShader;
const click_fragmentShader = `
uniform sampler2D texturetooth;
varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vViewPosition;
void main(){
vec4 textureColor = texture2D(texturetooth, vUv + 0.1);
// 光照参数
vec3 lightColor = vec3(1.0, 1.0, 1.0); // 光源颜色
vec3 lightDirection = normalize(vec3(0.0, 0.0, 1.0)); // 光线方向
float ambientIntensity = 0.3;
float diffuseIntensity = max(dot(vNormal, lightDirection), 0.3);
vec3 diffuseReflection = lightColor * diffuseIntensity;
vec3 viewDirection = normalize(vViewPosition);
vec3 reflectDirection = reflect(-lightDirection, vNormal);
float specularIntensity = pow(max(dot(viewDirection, reflectDirection), 0.0), 32.0);
vec3 specularReflection = lightColor * specularIntensity;
vec3 finalColor = (ambientIntensity * textureColor.rgb) + (diffuseReflection * textureColor.rgb) + (specularReflection * textureColor.rgb);
gl_FragColor = vec4(finalColor, textureColor.a);
}`;
//改完之后,你不能直接修改着色器demo,你需要在重新加载一个material,然后再把这个新的复制给stl网格对象才可以!!!终于解决了!@王珂珑理论正确!!!
var change_material = new THREE.ShaderMaterial({
uniforms: {
texturetooth: { value: texture },
},
//下面两个属性是专门属于牙齿的着色器
vertexShader: vertexShader,
fragmentShader: click_fragmentShader,
});
window.addEventListener(
"click", event => {
//console.log(event.clientX, event.clientY);
//设置鼠标向量的x,y值
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
console.log(mouse.x, mouse.y);
//通过摄像机和鼠标为位置,更新射线
raycaster.setFromCamera(mouse, camera);
//计算物体和射线的焦点
intersects = raycaster.intersectObjects([
mesh0,
mesh1,
mesh2,
mesh3,
mesh4,
mesh5,
mesh6,
mesh7,
mesh8,
mesh9,
mesh10,
mesh11,
mesh12,
mesh13,
//mesh14,
]);
if (intersects.length > 0) {//有牙齿也要进行交互
console.log('有人点击了一颗牙齿');
if (hasTeethSelected == false)//表示还没有任何一颗牙齿被选择
{
hasTeethSelected = true;
//解释一下:因为是第一次选,所以当前选中和上一次选中都是他自己(后来想了一下,这个没什么意义)
selectedObject = intersects[0].object;
lastObject = intersects[0].object;
//你可以使用'lastObject.lastmaterial'保存当前牙齿上一次的材质
//你可以使用'selectedObject.material.'对当前选中模型的材质进行更改
lastObject.lastmaterial = selectedObject.material;
selectedObject.material = change_material;
} else {
//说明已经有一颗牙齿被选中了
//恢复上一颗牙齿的材质'lastObject.material = lastObject.material'
lastObject.material = lastObject.lastmaterial;
//此时,就和上一颗牙齿无关了.
selectedObject = intersects[0].object;
lastObject = intersects[0].object;
//报存当前牙齿的材质
lastObject.lastmaterial = selectedObject.material;
//改变当前牙齿的材质
selectedObject.material = change_material;
}
renderer.render(scene, camera);
console.log('重新渲染了!');
}
});
如果你还不懂,再通俗易懂一点就是:
你需要将一些新的相关参数放在一个新的material材质中(即,参数需要以这个材质为载体),将新的材质赋值给模型,从而完成对模型的修改.
与物理材质的区别是: 物理材质你直接改他的属性就可以,不需要用一个新的材质作为中间载体
本地坐标是对象在其自身的坐标系中的坐标,每个对象都会有一个本地坐标也就相对于其父对象的坐标系,比如,应该有层级关系的场景,它包含了一个父对象和一个子对象,父对象它的原点就是全局坐标系中的一个点,子对象相对于父对象来说它的原点是具有一组本地坐标。
// mesh的世界坐标就是mesh.position与group.position的累加
const mesh = new THREE .Mesh(geometry, materia1);
mesh.position.set(50,0,0) ;
const group = new THREE .Group();
group .add(mesh) :
group.position.set(50,0,0);
改变子对象的.position,子对象在3D空间中的坐标会发生改变
改变父对象的,position,子对象在3D空间中的位置也会跟着变化,也就是说父对象.position和子对象,position看加才是才是子对象的.position。
它的作用是对象相对于场景的坐标。它是考虑了对象的层级关系和父对象的位置、旋转和缩放等属性的坐标。
世界坐标系是整个场景或空间的坐标系。它是所有对象的共同参考坐标系。
在这个坐标系中,每个对象的位置和旋转是相对于整个场景的全局坐标原点和方向的。
当一个对象位于世界坐标系中时,它的位置和旋转是相对于整个场景的,不受其他对象的影响
学习这个知识点的目的:
我想在一个场景中,给某一个单独的模型,添加箭头,这应该如何去做?
let axesHelper1 = new THREE.ArrowHelper(//x轴
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, 0, 0),
30,
0xFF0000
);
//模型中心的对象
const modelCenter = new THREE.Vector3();
//计算网格对象的几何体的BoundingBox边界框
selectedObject.geometry.computeBoundingBox();
//获取模型geometry的边界框的getCenter(中心点)
selectedObject.geometry.boundingBox.getCenter(modelCenter);
//从局部坐标系转换到世界坐标系中,以便在世界空间中准确地定位边界框的中心点。
modelCenter.applyMatrix4(selectedObject.matrixWorld);
axesHelper1.position.copy(modelCenter);// 设置坐标轴位置与当前选中几何体的位置相同
scene.add(axesHelper1);//将箭头加入到场景中