<!--B"H-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Random Terrain Heightmap Generator</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="module">
console.log("B\"H")
import * as THREE from 'https://awtsmoos.com/games/scripts/build/three.module.js';
import { OrbitControls } from "https://awtsmoos.com/games/scripts/jsm/controls/OrbitControls.js"
import HeightMapGenerator from "https://awtsmoos.com/games/mitzvahWorld/editor/lib/HeightmapGenerator.js"
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create a basic plane geometry with randomized heights
const geometry = new THREE.PlaneGeometry(20, 20, 64, 64);
for (let i = 0; i < geometry.attributes.position.array.length; i += 2) {
geometry.attributes.position.array[i] += Math.random() * 2 - 1;
}
geometry.computeVertexNormals();
// Lambert material with lighting
const material = new THREE.MeshLambertMaterial({
color: 0xffff00,
side: THREE.DoubleSide
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
plane.rotation.x = Math.PI/2;
// Ambient light
const ambientLight = new THREE.AmbientLight(0x404040, 1); // adjust color and intensity
scene.add(ambientLight);
// Directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // adjust color and intensity
directionalLight.position.set(2, 5, 3);
scene.add(directionalLight);
camera.position.set(5, 10, 10); // Adjusted camera position
camera.lookAt(plane.position);
const controls = new OrbitControls(camera, renderer.domElement);
const heightmapGenerator = new HeightMapGenerator(plane, scene);
async function generateHeightmap() {
heightmapGenerator.generateHeightMap();
var png = await heightmapGenerator
.getPNGfromHeightmap()
console.log(window.h=png)
var img = document.createElement("img");
img.src=png;
document.body.innerHTML = ""
document.body.appendChild(img)
console.log("Heightmap generated!");
}
var clicked = false
onclick =async () => {
if(clicked) return;
clicked = true;
await generateHeightmap();
}
renderer.render(scene, camera);
animate();
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
</script>
</body>
</html>
我正在尝试制作一个高度图生成器,以网格作为输入并下载高度图。我应该采取不同的措施才能正确获得官方高度图吗?我计算网格的深度并将其渲染为纹理,但我不确定是否达到了所需的结果。
/**
* B"H
*/
import * as THREE from 'https://awtsmoos.com/games/scripts/build/three.module.js';
export default class HeightMapGenerator {
constructor(mesh, scene, mapWidth = 512, mapHeight = 512) {
this.mesh = mesh;
this.scene = scene;
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
this.orthoCamera = null;
this.renderTarget = null;
this.heightMap = null;
this.renderer = new THREE.WebGLRenderer();
this.init();
}
init() {
this.setupCamera();
this.setupRenderTarget();
this.generateHeightMap();
}
setupCamera() {
const boundingBox = new THREE.Box3().setFromObject(this.mesh);
const size = boundingBox.getSize(new THREE.Vector3());
this.boundingBox = boundingBox;
console.log(window.g=size)
this.orthoCamera = new THREE.OrthographicCamera(
boundingBox.min.x, boundingBox.max.x,
boundingBox.max.z, boundingBox.min.z,
0.01, 123456789
);
this.orthoCamera.position.set(0, boundingBox.max.y + 1, 0);
this.orthoCamera.lookAt(0, boundingBox.min.y, 0);
// Add the orthographic camera to the scene
this.scene.add(this.orthoCamera);
}
setupRenderTarget() {
this.renderTarget = new THREE.WebGLRenderTarget(this.mapWidth, this.mapHeight, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat
});
}
generateHeightMap() {
const depthMaterial = new THREE.ShaderMaterial({
uniforms: {
boundingBoxMin: { value: this.boundingBox.min },
boundingBoxMax: { value: this.boundingBox.max }
},
vertexShader: /*glsl*/`
varying vec4 vWorldPosition;
void main() {
vWorldPosition = modelMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: /*glsl*/`
uniform vec3 boundingBoxMin;
uniform vec3 boundingBoxMax;
varying vec4 vWorldPosition;
void main() {
// Calculate normalized height within bounding box
float normalizedHeight = (vWorldPosition.y - boundingBoxMin.y) / (boundingBoxMax.y - boundingBoxMin.y);
gl_FragColor = vec4(vec3(normalizedHeight), 1.0);
}
`,
side: THREE.DoubleSide
});
this.renderer.setSize(this.mapWidth, this.mapHeight);
this.renderer.setRenderTarget(this.renderTarget);
this.scene.overrideMaterial = depthMaterial;
// Set the mesh to a specific layer
this.mesh.layers.enable(14);
this.orthoCamera.layers.set(14)
this.renderer.render(this.scene, this.orthoCamera);
this.mesh.layers.disable(14);
this.scene.overrideMaterial = null;
this.renderer.setRenderTarget(null);
const readPixels = new Uint8Array(this.mapWidth * this.mapHeight * 4);
this.renderer.readRenderTargetPixels(this.renderTarget, 0, 0, this.mapWidth, this.mapHeight, readPixels);
this.heightMap = new Uint8Array(this.mapWidth * this.mapHeight);
for (let i = 0; i < this.mapWidth * this.mapHeight; i++) {
const r = readPixels[i * 4];
const g = readPixels[i * 4 + 1];
const b = readPixels[i * 4 + 2];
const depth = (r + g + b) / 3;
this.heightMap[i] = depth;
}
}
getHeightAt(x, z, boundingBox) {
const worldWidth = boundingBox.max.x - boundingBox.min.x;
const worldHeight = boundingBox.max.z - boundingBox.min.z;
const cellWidth = worldWidth / this.mapWidth;
const cellHeight = worldHeight / this.mapHeight;
const gridX = Math.floor((x - boundingBox.min.x) / cellWidth);
const gridZ = Math.floor((z - boundingBox.min.z) / cellHeight);
if (gridX < 0 || gridX >= this.mapWidth || gridZ < 0 || gridZ >= this.mapHeight) {
return boundingBox.min.y;
}
const heightIndex = gridZ * this.mapWidth + gridX;
const heightValue = this.heightMap[heightIndex];
const normalizedHeight = heightValue / 255;
const worldHeightValue = normalizedHeight * (boundingBox.max.y - boundingBox.min.y) + boundingBox.min.y;
return worldHeightValue;
}
updateObjectPosition(object, boundingBox) {
const position = object.position;
const height = this.getHeightAt(position.x, position.z, boundingBox);
if (!object.isJumping) {
position.y = height;
}
}
downloadHeightmapAsPNG(nm = "BH.png") {
const heightMap = this.heightMap;
const mapWidth = this.mapWidth;
const mapHeight = this.mapHeight;
// Create a canvas element
const canvas = document.createElement('canvas');
canvas.width = mapWidth;
canvas.height = mapHeight;
const ctx = canvas.getContext('2d');
// Create an ImageData object to store the heightmap as image data
const imageData = ctx.createImageData(mapWidth, mapHeight);
// Fill the ImageData with heightmap data
for (let i = 0; i < heightMap.length; i++) {
const value = heightMap[i];
// Set R, G, B channels to the heightmap value (greyscale)
imageData.data[i * 4] = value;
imageData.data[i * 4 + 1] = value;
imageData.data[i * 4 + 2] = value;
// Set the alpha channel to fully opaque
imageData.data[i * 4 + 3] = 255;
}
// Put the ImageData onto the canvas
ctx.putImageData(imageData, 0, 0);
// Convert the canvas to a Blob
canvas.toBlob(function(blob) {
// Create a download link for the Blob
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = nm;
// Trigger the download
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}, 'image/png');
}
}
IMO,你的方法可能是一个错误的结局。
您想要的是一个高度图,它包含在一个图像中,可以使用程序方法将其作为单个着色器完成,例如名为“Perlin Noise”的方法。
一个简单的例子:这里开始阅读他关于程序地形生成的文章。 对于具体实现,您从面向相机的平面开始,很可能会更容易使用正交相机控制生成的尺寸,然后有 2 个选项:
“无代码”方式,从此示例