scaling down and repeating a texture in threejs - javascript

I have quite a large plane with a set displacement map and scale which I do not want to be changed. I simply want the loaded texture to apply to that mesh without it having to scale up so largely.
Currently, a floor texture doesn't look like a floor as it has been upscaled to suit the large plane.
How would I be able to scale down the texture and multiply it across the plane so it looks more like actual terrain?
const tilesNormalMap = textureLoader.load(
"./textures/Stylized_Stone_Floor_005_normal.jpg"
);
function createGround() {
let disMap = new THREE.TextureLoader().load("./models/Heightmap.png");
disMap.wrapS = disMap.wrapT = THREE.RepeatWrapping;
disMap.repeat.set(4, 2);
const groundMat = new THREE.MeshStandardMaterial({
map: tilesBaseColor,
normalMap: tilesNormalMap,
displacementMap: disMap,
displacementScale: 2
});
const groundGeo = new THREE.PlaneGeometry(300, 300, 800, 800);
let groundMesh = new THREE.Mesh(groundGeo, groundMat);
scene.add(groundMesh);
groundMesh.rotation.x = -Math.PI / 2;
groundMesh.position.y -= 1.5;
I tried using the .repeat method as shown below but i can't figure out how this would be implemented
tilesBaseColor.repeat.set(0.9, 0.9);
tilesBaseColor.offset.set(0.001, 0.001);
a photo of the current ground texture
enter image description here

First of all what you want to achieve does currently not work with three.js since it's only possible to a have a single uv transform for all textures (except for light and ao map). And map has priority in your case so you can't have different repeat settings for the displacement map. Related issue at GitHub: https://github.com/mrdoob/three.js/issues/9457
Currently, a floor texture doesn't look like a floor as it has been upscaled to suit the large plane. How would I be able to scale down the texture and multiply it across the plane so it looks more like actual terrain?
In this case, you have to use repeat values great 1 otherwise you zoom into the texture. Do it like in the following live example:
let camera, scene, renderer;
init().then(render);
async function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 1;
scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
const texture = await loader.loadAsync('https://threejs.org/examples/textures/uv_grid_opengl.jpg');
// repeat settings
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
const geometry = new THREE.PlaneGeometry();
const material = new THREE.MeshBasicMaterial({map: texture});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function render() {
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.148/build/three.min.js"></script>

Related

Setting the position of a text geometry?

I have looked through stack overflow and google and I have found how to CENTER a text geometry but that is not what I want to do.
I have a scene that just has a block of text that says "Buy Here!". Using the documentation in the three.js website and examples here I was able to do that after some struggling. I had some trouble finding out how to refer to that mesh since I had created the geometry inside a function, and it took hours for me to know about setting a name for it as a string so it can be accessible from different parent/child levels.
What I am NOT able to do now is to offset the text by some arbitrary number of units. I tried shifting it down by 5 units. No matter how I try to do it it isn't working. I either manage to make the text geometry disappear OR my whole scene is black.
Here is my code...
I have the basic scene setup working properly and I'll include it here but feel free to skip since I'm pretty sure this has nothing to do with the issue...
import './style.css'
import * as THREE from 'three';
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three#0.117.0/examples/jsm/controls/OrbitControls.js';
import TWEEN from 'https://cdn.jsdelivr.net/npm/#tweenjs/tween.js#18.5.0/dist/tween.esm.js';
//BASIC SCENE SETUP
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
//LIGHTS (POINT AND AMBIENT)
const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(5, 5, 5);
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
scene.add(pointLight, ambientLight);
//RESIZE WINDOW
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
render();
}, false);
//ORBIT CONTROLS
const controls = new OrbitControls(camera, renderer.domElement);
controls.minDistance = 5;
controls.maxDistance = 70;
controls.enablePan = false;
controls.enableRotate = false;
controls.enableZoom = false;
controls.target.set(0,0,-1);
camera.position.setZ(25);
window.addEventListener("click", (event) => {
onClick(event);
})
window.addEventListener("mousemove", onMouseMove);
var animate = function() {
requestAnimationFrame(animate);
controls.update();
render();
TWEEN.update();
};
function render() {
renderer.render(scene, camera);
}
animate();
and here is my code for the text object....
var loaderF = new THREE.FontLoader();
loaderF.load( 'https://threejs.org/examples/fonts/optimer_regular.typeface.json', function ( font ) {
var geometry = new THREE.TextGeometry( 'Buy Here!', {
font: font,
size: 2.3,
height: 0.1,
curveSegments: 15,
bevelEnabled: true,
bevelThickness: 0.5,
bevelSize: 0.31,
bevelSegments: 7
} );
geometry.center();
var material = new THREE.MeshLambertMaterial({color: 0x686868});
var mesh = new THREE.Mesh( geometry, material );
mesh.name = "bhText"
scene.add( mesh );
mesh.userData = { URL: "http://google.com"};
} );
Here's what I have tried.....
under "var geometry ({...});" I typed....
geometry.position.setX(-5);
but the text object disappears completely so I tried
geometry.position.setX = -5;
but there was no difference so i tried taking out
geometry.center();
but it had the same results.
So then I tried using
mesh.position.x = -5;
with AND without
geometry.center();
but again, they all just make my text object disappear.
So now I tried to set the position from outside the function by typing the following code OUTSIDE of everything that is contained in
loaderF.load ('https.....', function (font){var geometry = .....})
using the reference I learned....
scene.getObjectByName("bhText").position.x(-5);
but this makes my entire scene go blank (black). So I tried variations of like
scene.getObjectByName("bhText").position.x = -5;
scene.getObjectByName("bhText").position.setX(-5);
scene.getObjectByName("bhText").position.setX = -5;
mesh.position.setX = -5;// I was pretty sure this wasn't going to work since I wasn't
//using the mesh name specifically for when it's inside something
//I can't reach because of parent-child relations
and again trying each of those with AND without
geometry.center();
but they all made my scene go black.
I just wanna move it down a couple of units. Sheesh.
Could anyone be kind enough to tell me WHERE in my code I can set the position of the text geometry? Thank you please.
I just wanna move it down a couple of units.
In this case use mesh.position.y = - 5;. Changing the x coordinate will move the mesh to the left or right. Here is a complete live example based on your code:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set( 0, 0, 10 );
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
const pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.set(5, 5, 5);
const ambientLight = new THREE.AmbientLight(0xFFFFFF);
scene.add(pointLight, ambientLight);
const loader = new THREE.FontLoader();
loader.load('https://threejs.org/examples/fonts/optimer_regular.typeface.json', function(font) {
const geometry = new THREE.TextGeometry('Buy Here!', {
font: font,
size: 2,
height: 0.5
});
geometry.center();
const material = new THREE.MeshLambertMaterial({
color: 0x686868
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.y = - 1; // FIX
mesh.name = "bhText"
scene.add(mesh);
renderer.render(scene, camera);
});
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.min.js"></script>

Three.js - Why is csg.js not working for boolean operations?

I'm new to both three.js and csg.js. I've created a basic scene like this:
// Create scene
const scene = new THREE.Scene();
// Create perspective camera - FOV, aspect ratio, near + far clipping plane
const camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 1, 10000);
// Create renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
And I'm trying to subtract a circle from a plane.
// Create plane geometry
const pgeometry = new THREE.PlaneGeometry();
const pmaterial = new THREE.MeshBasicMaterial({color: 0xE8ADAA, opacity: 0.7, transparent: true});
const plane = new THREE.Mesh(pgeometry, pmaterial);
plane.position.z = 3;
// Create circle BoxGeometry
const ogeometry = new THREE.CircleGeometry(0.5, 32);
const omaterial = new THREE.MeshBasicMaterial({color: 0xE8ADAA});
const circle = new THREE.Mesh(ogeometry, omaterial);
circle.position.z = 3;
// CSG test
const bspPlane = new ThreeBSP(plane);
const bspCircle = new ThreeBSP(circle);
const bspResult = bspPlane.subtract(bspCircle);
const result = bspResult.toMesh();
scene.add(result);
(Then I have this at the end, to render the scene from the camera.)
// Camera position on z-axis
camera.position.z = 5;
// Render scene from camera
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
The three.js portion (rendering the plane and the circle by using `scene.add()' works, and I've attached three-js-csg, csg, and three-2-csg in the HTML in hopes of getting something to work. Is there anything glaringly wrong?
Thanks.
CSG doesn’t work with two non-manifold geometries. Your plane and circle are infinitely thin so the math of subtracting one from the other doesn’t work.
You need to use geometry that has an “inside” and “outside”, so instead of a plane, use a cube with a small depth. Instead of a circle, use a cylinder.

three.js - How to set camera position?

I'm pretty beginner in three.js and webgl programming. so I have created a box in three.js and its working fine but the problem is when I set camera position in z axis(eg: camera.position.z = 2; ) the box just disappears. could anyone explain me why is it happening and how can I properly set the position of the camera?
try uncommenting the camera.position.z = 2; in the fiddle
function init() {
var scene = new THREE.Scene();
var box = getBox(1, 1, 1);
scene.add(box);
var camera = new THREE.Camera(45, window.innerWidth/window.innerHeight, 1, 1000 );
//camera.position.z = 2;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("webgl").appendChild(renderer.domElement);
renderer.render(scene, camera);
}
function getBox(w, h, d) {
var geometry = new THREE.BoxGeometry(w, h, d);
var material = new THREE.MeshBasicMaterial({
color : 0x00ff00
});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
init();
not sure if you're trying to create this scene with an orthographic camera or perspective camera, but you'll typically need to specify the camera by type (ie THREE.PerspectiveCamera(...)).
I also added a few extra lines to ensure the camera was configured correctly, namely, setting the "LookAt" point to (0,0,0) , as well as setting an actual position of the camera via the THREE.Vector3.set(...) method.
Here are my adjustments to your code:
function init() {
var scene = new THREE.Scene();
var box = getBox(1, 1, 1);
scene.add(box);
var camera = new THREE.PerspectiveCamera(70,
window.innerWidth/window.innerHeight, 0.1, 1000 ); // Specify camera type like this
camera.position.set(0,2.5,2.5); // Set position like this
camera.lookAt(new THREE.Vector3(0,0,0)); // Set look at coordinate like this
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("webgl").appendChild(renderer.domElement);
renderer.render(scene, camera);
}
function getBox(w, h, d) {
var geometry = new THREE.BoxGeometry(w, h, d);
var material = new THREE.MeshBasicMaterial({
color : 0x00ff00
});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
init();
Try
camera.position.set( <X> , <Y> , <Z> );
where <X> and <Z> are 2D coordinates and <Y> is height

Does three.js renderer clone the objects positions?

I created a small scene with 3 spheres and a triangle connecting the 3 centers of the spheres, i.e. the triangle vertex positions are the same variables as the sphere positions.
Now I expected that if i change the position of one of the spheres, the triangle vertex should be moved together with it (since it's the same position object) and therefore still connect the three spheres.
However, if I do this coordinate change AFTER the renderer was called, the triangle is NOT changed. (Though it does change if I move the sphere BEFORE the renderer is called.)
This seems to indicate that the renderer doesnt use the original position objects but a clone of them.
Q: Is there a way to avoid this cloning behaviour (or whatever is the reason for the independent positions) so I can still change two objects with one variable change? Or am I doing something wrong?
The code:
var width = window.innerWidth;
var height = window.innerHeight;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene;
var camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 10000);
camera.position=new THREE.Vector3(50,50,50);
camera.lookAt(new THREE.Vector3(0,0,0));
scene.add(camera);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position=camera.position;
scene.add(pointLight);
var sphere=[];
var sphereGeometry = new THREE.SphereGeometry(1,8,8);
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
var triGeom = new THREE.Geometry();
for (var i=0; i<3; i++) {
sphere[i] = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere[i].position=new THREE.Vector3(10*i,20+5*(i-1)^2,0);
scene.add(sphere[i]);
triGeom.vertices.push(sphere[i].position);
}
triGeom.faces.push( new THREE.Face3( 0, 1, 2 ) );
triGeom.computeFaceNormals();
var tri= new THREE.Mesh( triGeom, new THREE.MeshLambertMaterial({side:THREE.DoubleSide, color: 0x00ff00}) );
scene.add(tri);
sphere[0].position.x+=10; // this changes both sphere and triangle vertex
renderer.render(scene, camera);
sphere[1].position.x+=10; // this changes only the sphere
renderer.render(scene, camera);
This is probably because of geometry caching feature. You will have to set triGeom.verticesNeedUpdate = true every time you change vertex position.

Update height/radius of 3D Cylinder created with Three.js at runtime

Like we are changing height/width/depth of 3D cube at run time in this Fiddle:
http://jsfiddle.net/EtSf3/4/
How can we change the Radius and Length at runtime of Cylinder created using Three.js
Here is my code:
HTML:
<script src="http://www.html5canvastutorials.com/libraries/three.min.js"></script>
<div id="container"></div>
JS
//Script for 3D Cylinder
// revolutions per second
var angularSpeed = 0.2;
var lastTime = 0;
var cylinder = null;
// this function is executed on each animation frame
function animate() {
// update
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angleChange = angularSpeed * timeDiff * 2 * Math.PI / 1000;
cylinder.rotation.x += angleChange;
cylinder.rotation.z += angleChange;
lastTime = time;
// render
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function () {
animate();
});
}
// renderer
var container = document.getElementById("container");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(container.offsetWidth, container.offsetHeight);
container.appendChild(renderer.domElement);
// camera
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 700;
// scene
var scene = new THREE.Scene();
// cylinder
// API: THREE.CylinderGeometry(bottomRadius, topRadius, height, segmentsRadius, segmentsHeight)
cylinder = new THREE.Mesh(new THREE.CylinderGeometry(150, 150, 500, 100, 100, false), new THREE.MeshPhongMaterial({
// light
specular: '#cccccc',
// intermediate
color: '#666666',
// dark
emissive: '#444444',
shininess: 100
}));
cylinder.overdraw = true;
cylinder.rotation.x = Math.PI * 0.2;
//cylinder.rotation.y = Math.PI * 0.5;
scene.add(cylinder);
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x444444);
scene.add(ambientLight);
// directional lighting
var directionalLight = new THREE.DirectionalLight(0xcccccc);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
// start animation
animate();
Here is the Fiddle for the same: http://jsfiddle.net/dpPjD/
Let me know if you need any other information.
Please suggest.
Once the object geometry is added to the mesh, it is converted to face/vertex/UV/normals and stored as part of the mesh. For example, the cylinder shape you have specified is tessellated (divided) by Three.js into triangles with a vertex count of more than 10,000.
Hence while the global mesh properties like transforms can be updated, updating the individual geometries is as good as creating a new geometry every animation-frame. If you happen to know precisely the vertices you need to modify, you can update it directly using the geometry.vertices property. But if not, I do not think there is a way.
You can try overwriting the geometry of an object by doing something like this:
cylinder.geometry.dispose();
cylinder.geometry = new THREE.CylinderGeometry(botRad, topRad, height, 32);
where: botRad, topRad and height are new values from initial or a variable that is constantly changing if you are planning on oscillating cylinder height.
The dispose makes sure that it deletes the previous geometry of the object before overwriting a new geometry to it.

Categories

Resources