在 three.js 中生成程序世界的最佳方法是什么?
What is the best approach to make procedural world generation in three.js?
我有函数 CreateChunk(x,z)
在指定的坐标 x 和 z 中创建一个地形“块”,它是一个平面,其顶点高度用 Perlin 噪声修改,然后根据它们的高度绘制(也添加了一层水)如下所示:
A single chunk
一切正常,直到我尝试制作更多块:
Many chunks
我知道它应该是这样工作的,没有任何问题,但是,我可以做些什么来“同步”它们,以便一个结束,另一个开始?同时保持程序生成。
如果您需要代码,请告诉我,但我只是在寻求一个可以遵循的想法。
您需要知道要构建什么瓦片以及瓦片上的噪声密度。
如需一些想法,请查看此论坛 post:https://discourse.threejs.org/t/help-getting-the-actual-position-of-a-vertices-in-a-buffer-geometry/29649/4
我将在此处保留该片段。也许它会对其他寻求者有所帮助:)
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.135.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.135.0/examples/jsm/controls/OrbitControls";
import {ImprovedNoise} from "https://cdn.skypack.dev/three@0.135.0/examples/jsm/math/ImprovedNoise";
THREE.BufferGeometry.prototype.toQuads = ToQuads;
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 8, 13);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
const perlin = new ImprovedNoise();
let controls = new OrbitControls(camera, renderer.domElement);
let step = 20;
for(let z = -4; z <= 4; z ++){
for(let x = -4; x <= 4; x++){
let p = createPlane(step, Math.random() * 0x7f7f7f + 0x7f7f7f);
setNoise(p.geometry, new THREE.Vector2(x, z), 2, 5);
p.geometry.rotateX(Math.PI * 0.5);
p.position.set(x, 0, z).multiplyScalar(step);
scene.add(p);
}
}
renderer.setAnimationLoop( _ => {
renderer.render(scene, camera);
})
function createPlane( step, color){
let g = new THREE.PlaneGeometry(step, step, 25, 25).toQuads();
let m = new THREE.LineBasicMaterial({color: color});
let l = new THREE.LineSegments(g, m);
return l;
}
function setNoise(g, uvShift, multiplier, amplitude){
let pos = g.attributes.position;
let uv = g.attributes.uv;
let vec2 = new THREE.Vector2();
for(let i = 0; i < pos.count; i++){
vec2.fromBufferAttribute(uv, i).add(uvShift).multiplyScalar(multiplier);
pos.setZ(i, perlin.noise(vec2.x, vec2.y, 0) * amplitude );
}
}
function ToQuads() {
let g = this;
let p = g.parameters;
let segmentsX = (g.type == "TorusBufferGeometry" ? p.tubularSegments : p.radialSegments) || p.widthSegments || p.thetaSegments || (p.points.length - 1) || 1;
let segmentsY = (g.type == "TorusBufferGeometry" ? p.radialSegments : p.tubularSegments) || p.heightSegments || p.phiSegments || p.segments || 1;
let indices = [];
for (let i = 0; i < segmentsY + 1; i++) {
let index11 = 0;
let index12 = 0;
for (let j = 0; j < segmentsX; j++) {
index11 = (segmentsX + 1) * i + j;
index12 = index11 + 1;
let index21 = index11;
let index22 = index11 + (segmentsX + 1);
indices.push(index11, index12);
if (index22 < ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index21, index22);
}
}
if ((index12 + segmentsX + 1) <= ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index12, index12 + segmentsX + 1);
}
}
g.setIndex(indices);
return g;
}
</script>
我有函数 CreateChunk(x,z)
在指定的坐标 x 和 z 中创建一个地形“块”,它是一个平面,其顶点高度用 Perlin 噪声修改,然后根据它们的高度绘制(也添加了一层水)如下所示:
A single chunk
一切正常,直到我尝试制作更多块:
Many chunks
我知道它应该是这样工作的,没有任何问题,但是,我可以做些什么来“同步”它们,以便一个结束,另一个开始?同时保持程序生成。
如果您需要代码,请告诉我,但我只是在寻求一个可以遵循的想法。
您需要知道要构建什么瓦片以及瓦片上的噪声密度。
如需一些想法,请查看此论坛 post:https://discourse.threejs.org/t/help-getting-the-actual-position-of-a-vertices-in-a-buffer-geometry/29649/4
我将在此处保留该片段。也许它会对其他寻求者有所帮助:)
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.135.0";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.135.0/examples/jsm/controls/OrbitControls";
import {ImprovedNoise} from "https://cdn.skypack.dev/three@0.135.0/examples/jsm/math/ImprovedNoise";
THREE.BufferGeometry.prototype.toQuads = ToQuads;
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 8, 13);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
renderer.setClearColor(0x404040);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
const perlin = new ImprovedNoise();
let controls = new OrbitControls(camera, renderer.domElement);
let step = 20;
for(let z = -4; z <= 4; z ++){
for(let x = -4; x <= 4; x++){
let p = createPlane(step, Math.random() * 0x7f7f7f + 0x7f7f7f);
setNoise(p.geometry, new THREE.Vector2(x, z), 2, 5);
p.geometry.rotateX(Math.PI * 0.5);
p.position.set(x, 0, z).multiplyScalar(step);
scene.add(p);
}
}
renderer.setAnimationLoop( _ => {
renderer.render(scene, camera);
})
function createPlane( step, color){
let g = new THREE.PlaneGeometry(step, step, 25, 25).toQuads();
let m = new THREE.LineBasicMaterial({color: color});
let l = new THREE.LineSegments(g, m);
return l;
}
function setNoise(g, uvShift, multiplier, amplitude){
let pos = g.attributes.position;
let uv = g.attributes.uv;
let vec2 = new THREE.Vector2();
for(let i = 0; i < pos.count; i++){
vec2.fromBufferAttribute(uv, i).add(uvShift).multiplyScalar(multiplier);
pos.setZ(i, perlin.noise(vec2.x, vec2.y, 0) * amplitude );
}
}
function ToQuads() {
let g = this;
let p = g.parameters;
let segmentsX = (g.type == "TorusBufferGeometry" ? p.tubularSegments : p.radialSegments) || p.widthSegments || p.thetaSegments || (p.points.length - 1) || 1;
let segmentsY = (g.type == "TorusBufferGeometry" ? p.radialSegments : p.tubularSegments) || p.heightSegments || p.phiSegments || p.segments || 1;
let indices = [];
for (let i = 0; i < segmentsY + 1; i++) {
let index11 = 0;
let index12 = 0;
for (let j = 0; j < segmentsX; j++) {
index11 = (segmentsX + 1) * i + j;
index12 = index11 + 1;
let index21 = index11;
let index22 = index11 + (segmentsX + 1);
indices.push(index11, index12);
if (index22 < ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index21, index22);
}
}
if ((index12 + segmentsX + 1) <= ((segmentsX + 1) * (segmentsY + 1) - 1)) {
indices.push(index12, index12 + segmentsX + 1);
}
}
g.setIndex(indices);
return g;
}
</script>