2d/3d随机算法
- 学习ThreeJS的捷径
- 坐标系简介
- 平面直角坐标系和极坐标系
- 空间直角坐标系
- 圆柱坐标系
- 球坐标系
- 球坐标系与直角坐标系的转换
- 基于坐标系系统的随机点位算法
- 平面直角坐标系随机
- 平面直角坐标系随机的变形
- 空间直角坐标系随机
- 二维极坐标系随机
- 圆柱坐标系随机
- 基于Cylindercal(圆柱坐标系)的圆柱坐标系随机
- 球坐标系随机
- 随机算法的应用
- 土星环(使用极坐标系)
- 粒子球(使用球坐标系)
学习ThreeJS的捷径
本段内容会写在0篇以外所有的,本人所编写的Threejs教程中
对,学习ThreeJS有捷径
当你有哪个函数不懂的时候,第一时间去翻一翻文档
当你有哪个效果不会做的时候,第一时间去翻一翻所有的案例,也许就能找到你想要的效果
最重要的一点,就是,绝对不要怕问问题,越怕找找别人问题,你的问题就会被拖的越久
如果你确定要走WebGL/ThreeJS的开发者路线的话,以下行为可以让你更快的学习ThreeJS
- 没事就把所有的文档翻一遍,哪怕看不懂,也要留个印象,至少要知道Threejs有什么
- 没事多看看案例效果,当你记忆的案例效果足够多时,下次再遇到相似问题时,你就有可能第一时间来找对应的案例,能更快解决你自己的问题
- 上述案例不只是官网的案例,郭隆邦技术博客,跃焱邵隼,暮志未晚等站点均有不少优质案例,记得一并收藏
http://www.yanhuangxueyuan.com/ 郭隆邦技术博客
https://www.wellyyss.cn/ 跃焱邵隼
http://www.wjceo.com/ 暮志未晚(暮老的站点暂时挂了,请查阅他之前的threejs相关文档)
暮老的csdn首页
这三个站点是我最常逛的站点,推荐各位有事没事逛一下,看看他们的案例和写法思路,绝对没坏处
坐标系简介
我们常见2D坐标系,一般有平面直角坐标系,极坐标系
我们常见的3D坐标系,一般有空间直角坐标系,圆柱坐标系,球坐标系
平面直角坐标系和极坐标系
平面直角坐标系,一般以 (x,y) 两个数值来表示点位坐标
极坐标系,一般以(radius,theta),半径和旋转角度来表示点位坐标
极坐标系和平面直角坐标系可以互相转换:
//平面直角坐标系转极坐标系
let radius = Math.sqrt( x * x + y * y );
let theta = Math.atan( y / x );
//极坐标系转平面直角坐标系
let x = radius * Math.cos(theta);
let y = radius * Math.sin(theta);
空间直角坐标系
空间直角坐标系,一般使用(x,y,z)三个值来表示点位坐标,也就是我们在threejs中最常用的position
圆柱坐标系
圆柱坐标系,一般使用(radius,theta,height)来表示点位坐标,三个值可以根据实际使用情况做互换,圆柱坐标系,官方已经提供了相关API,我们可以查阅threejs的官方文档来更详细的了解圆柱坐标系
由于官方文档的内容就这些,所以这里就不贴文档源地址了,具体的圆柱坐标系到平面直角坐标系的转换算法,其实和上面极坐标算法相似,xz平面使用极坐标系来计算,y轴依然是用高度轴来计算,本质上圆柱坐标系就是极坐标系增加了高度轴的一种空间坐标系,转换公式参考极坐标系,这里不再赘述
球坐标系
球坐标系最常用的应用就是计算地球的经纬度,球坐标系一般用( radius,phi,theta)来表示点位坐标,phi可以视为纬度,theta可以视为经度,我们也可以参考threejs官方文档对球坐标系的解释
球坐标系与直角坐标系的转换
球坐标系到直角坐标系的互转,写法有点多,这里我贴出《3D数学基础》190页的内容,如果你的手边有这本书,书本内的183页到192页对球坐标系做了详细的介绍,这里请根据自身需求做了解即可
因为考虑到threejs使用右手坐标系,所以我们的公式以Threejs提供的为准
//直角坐标系转球坐标系,这里我们以threejs官方开发包中
//src/math/Spherical.js 中为准
setFromCartesianCoords( x, y, z ) {
this.radius = Math.sqrt( x * x + y * y + z * z );
if ( this.radius === 0 ) {
this.theta = 0;
this.phi = 0;
} else {
this.theta = Math.atan2( x, z );
this.phi = Math.acos( MathUtils.clamp( y / this.radius, - 1, 1 ) );
}
return this;
}
//球坐标系转换平面直角坐标系,这里我们以thrjeejs官方开发包中
//src/math/Vector3.js 564行的算法为准
setFromSphericalCoords( radius, phi, theta ) {
const sinPhiRadius = Math.sin( phi ) * radius;
this.x = sinPhiRadius * Math.sin( theta );
this.y = Math.cos( phi ) * radius;
this.z = sinPhiRadius * Math.cos( theta );
return this;
}
基于坐标系系统的随机点位算法
平面直角坐标系随机
默认的情况下,场景使用的是空间直角坐标系
for(let i = 0;i< 100;i++){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
});
let mesh = new THREE.Mesh(geometry,material);
mesh.position.x = Math.random() * 10 - 5;
mesh.position.z = Math.random() * 10 - 5;
scene.add(mesh);
}
因为我们这里仅操作了x轴和z轴,高度轴均保持一致,这样的随机为平面直角坐标系的随机,当然,我们也可以选择随机x轴和y轴,随机y轴和z轴,根据你自身的需求来做即可
这样的以x轴和z轴为随机的方式,可以用于在某个地区生成随机数量的树木,我们在代码中,xz轴的随机范围均为 -5 ~ 5 ,所以我们实际上是在
Math.random()的取值范围为 0~1,Math.random() * 10的取值范围为0 ~ 10,Math.random() * 10 - 5的取值范围为 -5 ~ 5
平面直角坐标系随机的变形
变形其实非常简单,只需要控制position.x,position.y,position.z其中两个轴,就可以控制物体在哪个平面上随机
空间直角坐标系随机
基本上没啥说的,只是在上面的基础上,将三个轴都参与随机
二维极坐标系随机
for(let i = 0;i< 100;i++){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
});
let mesh = new THREE.Mesh(geometry,material);
let angle = Math.random() * Math.PI * 2;
let radius = Math.random() * 10;
let x = Math.cos(angle) * radius;
let z = Math.sin(angle) * radius;
mesh.position.x = x;
mesh.position.z = z;
scene.add(mesh);
}
这样其实我们看的不太明显,我们可以选择随机的方块数量大幅增加,或者让半径不再随机
替换随机的半径
// let radius = Math.random() * 10;
let radius = 10;
生成1000个盒子,只需要修改for循环的100为1000即可
圆柱坐标系随机
其实就是在极坐标系的基础上,增加对高度轴y轴的随机数即可
这里我们对半径不做随机,相对来说结果更显而易见,如果你需要在圆柱范围内随机,将固定的radius改为随机即可
for(let i = 0;i< 1000;i++){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
});
let mesh = new THREE.Mesh(geometry,material);
let angle = Math.random() * Math.PI * 2;
let radius = 10;
let x = Math.cos(angle) * radius;
let z = Math.sin(angle) * radius;
mesh.position.x = x;
mesh.position.z = z;
mesh.position.y = Math.random() * 10 - 5;
scene.add(mesh);
}
基于Cylindercal(圆柱坐标系)的圆柱坐标系随机
这里使用了上述的Cylindercal的概念,如果你实在绕不过来极坐标系,可以考虑使用这个
let cylindrical = new THREE.Cylindrical();
for(let i = 0;i< 1000;i++){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
});
let mesh = new THREE.Mesh(geometry,material);
cylindrical.radius = 10;
cylindrical.theta = Math.random() * Math.PI * 2;
cylindrical.y = Math.random() * 10 - 5;
//文档在Vector3中
mesh.position.setFromCylindrical(cylindrical);
scene.add(mesh);
}
效果与上面一致,这里不再截图
球坐标系随机
球坐标系随机,这里仅建议使用官方的Spherical来进行随机
let spherical = new THREE.Spherical()
for(let i = 0;i< 1000;i++){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
});
let mesh = new THREE.Mesh(geometry,material);
spherical.radius = 10;
spherical.phi = Math.random() * Math.PI * 2;
spherical.theta = Math.random() * Math.PI * 2;
mesh.position.setFromSpherical(spherical);
scene.add(mesh);
}
随机算法的应用
土星环(使用极坐标系)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
margin: 0;
padding: 0;
border: 0;
}
body{
width:100vw;
height: 100vh;
overflow: hidden;
}
</style>
</head>
<body>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../three/build/three.module.js",
"three/addons/": "../three/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "../three/build/three.module.js";
import {OrbitControls} from "../three/examples/jsm/controls/OrbitControls.js";
window.addEventListener('load',e=>{
init();
addMesh();
render();
})
let scene,renderer,camera;
let orbit;
function init(){
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
alpha:true,
antialias:true
});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,0.1,2000);
camera.add(new THREE.PointLight());
camera.position.set(10,10,10);
scene.add(camera);
orbit = new OrbitControls(camera,renderer.domElement);
orbit.enableDamping = true;
scene.add(new THREE.GridHelper(10,10));
}
let groups = [];
function addMesh() {
//创建中心的球体
let geometry = new THREE.SphereGeometry(10,32,32);
let material = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
});
let mesh = new THREE.Mesh(geometry,material);
scene.add(mesh);
//创建5层土星环
for(let i = 0;i< 5;i++){
let group = new THREE.Group();
let material2 = new THREE.MeshStandardMaterial({
color:0xffffff * Math.random()
})
//每层土星环创造100个图块
for(let j = 0;j < 100;j++){
let geometry2 = new THREE.BoxGeometry();
let mesh2 = new THREE.Mesh(geometry2,material2);
group.add(mesh2);
//在 15 ~ i*2的半径范围内,生成指定的星环
let radius = Math.random() * i * 2 + 15;
let theta = Math.random() * Math.PI * 2;
mesh2.position.x = Math.cos(theta) * radius;
mesh2.position.z = Math.sin(theta) * radius;
//随机初始角度
mesh2.rotation.set(
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2,
)
//随机大小
mesh2.scale.set(
Math.random() * 2,
Math.random() * 2,
Math.random() * 2,
)
//注意,onBeforeRender,只有具有材质属性的物体,才生效,对group不生效
mesh2.onBeforeRender = ()=>{
mesh2.rotation.x += 0.01;
mesh2.rotation.y += 0.01;
}
}
scene.add(group);
groups.push(group);
}
}
function render() {
renderer.render(scene,camera);
orbit.update();
requestAnimationFrame(render);
//让每一层的星环转起来
for(let i = 0;i< groups.length;i++){
groups[i].rotation.y += 0.001 * (i+1);
}
}
</script>
</body>
</html>
效果如上,我不是设计师,所以不用太纠结好看与不好看的问题
粒子球(使用球坐标系)
这里就不贴完整代码了
function addMesh() {
let spherical = new THREE.Spherical();
let vectors = [];
for(let i = 0;i< 1000;i++){
spherical.radius = Math.random() * 100
spherical.theta = Math.random() * Math.PI * 2;
spherical.phi = Math.random() * Math.PI * 2;
let vec = new THREE.Vector3().setFromSpherical(spherical);
vectors.push(vec);
}
let geometry = new THREE.BufferGeometry().setFromPoints(vectors);
let points = new THREE.Points(
geometry,
new THREE.PointsMaterial({
sizeAttenuation:true,
})
);
points.onBeforeRender = ()=>{
points.rotation.x += 0.001;
points.rotation.y += 0.001;
}
scene.add(points);
}
有的人也许已经看出来了,粒子球可以用作创建星空(也不一定非要用球坐标系来创建也可以创建星空),粒子球也是烟花效果的基础