Adding a Texture to ExtrudeGeometry - javascript

three.js v53
In three.js v 53 on JSFiddle, making a custom shape, extruding it and applying a texture and wireframe shows the extruded faces being whole, complete squares.
JSFiddle: custom shape extruded in v53 with complete, un-split extrusion faces
three.js v74
The exact same code but three.js v 74 splits the extrude faces into triangles.
JSFiddle: custom shape extruded in v74 with segmented (into triangles) extrusion faces
Question 1: how can I eliminate the segmentation into triangles of the extruded faces in v74?
Bonus Question 2: how can I eliminate the segmentation into triangles on the main face of my shape that I extrude (what I was originally trying to solve when I noticed the extrusion differences between three.js versions)
Both are making my texturing journey harder. Many thanks.
Code for both JSFiddles:
(Some code borrowed from another JS Fiddle but I can't find it again to credit)
/****
Create the texture as I can't use an image on my server
*****/
var canvas = document.getElementById("texture"),
context = canvas.getContext("2d");
canvas.width = 50;
canvas.height = 50;
context.strokeStyle = "#5588ff";
context.lineWidth = 2;
context.moveTo(0, 10);
context.lineTo(50, 10);
context.moveTo(0, 20);
context.lineTo(50, 20);
context.moveTo(0, 30);
context.lineTo(50, 30);
context.moveTo(0, 40);
context.lineTo(50, 40);
context.stroke();
/***********/
var scene, camera, renderer, shape;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, 2, 1, 10000);
camera.position.z = 200;
scene.add(this.camera);
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
renderer = new THREE.WebGLRenderer({
antialias: true, alpha:true
});
document.getElementById("scene").appendChild(renderer.domElement);
renderer.setSize(document.getElementById("scene").scrollWidth, document.getElementById("scene").scrollHeight);
var points = []
points.push(new THREE.Vector2(100, 0));
points.push(new THREE.Vector2(100, 60));
points.push(new THREE.Vector2(40, 90));
points.push(new THREE.Vector2(-40, 90));
points.push(new THREE.Vector2(-100, 60));
points.push(new THREE.Vector2(-100, 0));
// var path = new THREE.LineCurve3(new THREE.Vector3(45, 0, 0), new THREE.Vector3(-45, 0, 0));
var extrusionSettings = {
steps: 1,
bevelEnabled: false,
amount: 90
};
shape = new THREE.Shape(points);
var geometry = new THREE.ExtrudeGeometry(shape, extrusionSettings),
texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({
color: 0xFF00FF,
map: texture
});
// *** UVMapping stuff I copied off an example. I don't know what it's doing but the texture won't work without it.
geometry.faceUvs = [
[]
];
geometry.faceVertexUvs = [
[]
];
for (var f = 0; f < geometry.faces.length; f++) {
var faceuv = [
new THREE.Vector2(0, 1),
new THREE.Vector2(1, 1),
new THREE.Vector2(1, 0),
new THREE.Vector2(0, 0)
];
geometry.faceUvs[0].push(new THREE.Vector2(0, 1));
geometry.faceVertexUvs[0].push(faceuv);
}
// add a wireframe to highlight the segmentation (or lack of in this v53 demo)
mesh = THREE.SceneUtils.createMultiMaterialObject(geometry, [material, new THREE.MeshBasicMaterial({
color: 0x000000,
wireframe: true,
transparent: true
})]);
mesh.position.z = -50;
mesh.position.y = -40;
mesh.rotation.y = 0.8;
mesh.rotation.z = 0.4;
scene.add(mesh);
animate = function() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
mesh.rotation.y += 0.02;
mesh.rotation.x += 0.02;
};
animate();

THREE.ExtrudeGeometry creates default UVs for you based on the coordinates of the extruded shape. (Look at the geometry of the extruded shape and inspect the UVs that are computed for you.)
The auto-generated UVs will likely be outside the normal range of [ 0, 1 ]. You can accommodate that by using a pattern like so:
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.offset.set( 0, 0.5 );
texture.repeat.set( 0.01, 0.01 );
Updated fiddle: http://jsfiddle.net/vxr3Ljf3/4/
Alternatively, you can specify your own custom UV generator like so:
var extrusionSettings = {
steps: 1,
bevelEnabled: false,
amount: 90,
UVGenerator: myUVGenerator
};
Base your custom UV generator on THREE.ExtrudeGeometry.WorldUVGenerator.
three.js r.74

Related

How to rotate an object to face the camera? [duplicate]

guys I know this question has been asked several times, several different ways, but I just can get it to work. Basically I have 2d clouds, but I want the camera to rotate around an object floating above the clouds. The problem is, when im not looking a the face of the clouds u can tell that they are 2d. Soooo i want the the clouds to "look" at the camera where ever it is. I believe my problem stems from how the cloud geometry is called on to the planes, but here take a look. I put the a lookAt function with in my animate function. I hope you can point me in the right direction at least.
Three.js rev. 70...
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 0, 100);
scene.add(camera);
controls = new THREE.OrbitControls( camera );
controls.target.copy( new THREE.Vector3( 0, 0,475) );
controls.minDistance = 50;
controls.maxDistance = 200;
controls.autoRotate = true;
controls.autoRotateSpeed = .2; // 30 seconds per round when fps is 60
controls.minPolarAngle = Math.PI/4; // radians
controls.maxPolarAngle = Math.PI/2; // radians
controls.enableDamping = true;
controls.dampingFactor = 0.25;
clock = new THREE.Clock();
cloudGeometry = new THREE.Geometry();
var texture = THREE.ImageUtils.loadTexture('img/cloud10.png', null, animate);
texture.magFilter = THREE.LinearMipMapLinearFilter;
texture.minFilter = THREE.LinearMipMapLinearFilter;
var fog = new THREE.Fog(0x4584b4, -100, 3000);
cloudMaterial = new THREE.ShaderMaterial({
uniforms: {
"map": {
type: "t",
value: texture
},
"fogColor": {
type: "c",
value: fog.color
},
"fogNear": {
type: "f",
value: fog.near
},
"fogFar": {
type: "f",
value: fog.far
},
},
vertexShader: document.getElementById('vs').textContent,
fragmentShader: document.getElementById('fs').textContent,
depthWrite: false,
depthTest: false,
transparent: true
});
var plane = new THREE.Mesh(new THREE.PlaneGeometry(64, 64));
for (var i = 0; i < 8000; i++) {
plane.position.x = Math.random() * 1000 - 500;
plane.position.y = -Math.random() * Math.random() * 200 - 15;
plane.position.z = i;
plane.rotation.z = Math.random() * Math.PI;
plane.scale.x = plane.scale.y = Math.random() * Math.random() * 1.5 + 0.5;
plane.updateMatrix();
cloudGeometry.merge(plane.geometry, plane.matrix);
}
cloud = new THREE.Mesh(cloudGeometry, cloudMaterial);
scene.add(cloud);
cloud = new THREE.Mesh(cloudGeometry, cloudMaterial);
cloud.position.z = -8000;
scene.add(cloud);
var radius = 100;
var xSegments = 50;
var ySegments = 50;
var geo = new THREE.SphereGeometry(radius, xSegments, ySegments);
var mat = new THREE.ShaderMaterial({
uniforms: {
lightPosition: {
type: 'v3',
value: light.position
},
textureMap: {
type: 't',
value: THREE.ImageUtils.loadTexture("img/maps/moon.jpg")
},
normalMap: {
type: 't',
value: THREE.ImageUtils.loadTexture("img/maps/normal.jpg")
},
uvScale: {
type: 'v2',
value: new THREE.Vector2(1.0, 1.0)
}
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});
mesh = new THREE.Mesh(geo, mat);
mesh.geometry.computeTangents();
mesh.position.set(0, 50, 0);
mesh.rotation.set(0, 180, 0);
scene.add(mesh);
}
function onWindowResize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
function animate() {
requestAnimationFrame(animate);
light.orbit(mesh.position, clock.getElapsedTime());
cloud.lookAt( camera );
controls.update(camera);
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', onWindowResize, false);
just a first guess:
the lookAt function needs Vector3 as parameter. try to use camera.position in the animate function.
cloud.lookAt( camera.position );
first of all, to build 2D objects in a scene that always faces towards the camera, you should use Sprite object, so you don't have to do anything to get this effect. (and have better performance :))
Definition from THREE.org: Sprite - a sprite is a plane in an 3d scene which faces always towards the camera.
var map = THREE.ImageUtils.loadTexture( "sprite.png" );
var material = new THREE.SpriteMaterial( { map: map, color: 0xffffff, fog: true } );
var sprite = new THREE.Sprite( material );
scene.add( sprite );
Please check this example: http://threejs.org/examples/#webgl_points_sprites
I would absolutely agree, I would use Sprite, or even Points, but then, if assign a texture to it, it will render it square-sized. My sprites are animated, and frames cannot be packed in square tiles, cause it would take a lot of space. I might make a mesh and use this lookAt function.

WebGL: How can you draw a pentagon?

I am practicing WebGL and attempting to draw a pentagon, but I am not sure how to approach this. Any help would be much appreciated.
It's easy to do this with three.js. You can use CircleGeometry and reduce the number of segments to five to give you a pentagon.
var geometry = new THREE.CircleGeometry( 10, 5 )
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } )
var circle = new THREE.Mesh( geometry, material )
scene.add( circle )
More information here: https://threejs.org/docs/#api/en/geometries/CircleGeometry
Within a working example:
var width = window.innerWidth
var height = window.innerHeight
var renderer = new THREE.WebGLRenderer({antialias: true})
renderer.setClearColor(0x8e8ed7)
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
document.body.appendChild(renderer.domElement)
var scene = new THREE.Scene()
var camera = new THREE.PerspectiveCamera(35, width / height, 0.1, 3000)
camera.position.set(0, 0, 100)
var controls = new THREE.OrbitControls(camera, renderer.domElement)
controls.minDistance = 40
controls.maxDistance = 1300
var material = new THREE.MeshPhongMaterial({color: 0xFF0000, specular: 0x111111, shininess: 75})
scene.add(camera, new THREE.AmbientLight(0xffffff, 0.4))
var light = new THREE.PointLight(0xffffff, 0.8)
camera.add(light)
light.position.y += 60
light.position.x += 70
requestAnimationFrame(function animate(){
requestAnimationFrame(animate)
renderer.render(scene, camera)
})
var geometry = new THREE.CircleGeometry( 10, 5 )
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } )
var circle = new THREE.Mesh( geometry, material )
scene.add( circle )
html, body {
width: 100%;
height: 100%;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
if you want to use only webgl, first of all you need to create vertices, for this you can use a circle, select five points on a circle at equal distances from each other and lying on a circle, here is the formula for each coordinate
let vertices =[];
for(let i=0; i<5; i++){
let x = radius * Math.cos(360/5 * i);
let y = radius * Math.sin(360/5 * i);
vertices.push(x);vertices.push(y);
}
substituting your required radius instead of the radius, you will get a set of coordinates of your pentagon, and then drop them into the buffer of your program and draw, tried to show as briefly as possible

THREE.js raycaster not finding any intersects

I have a simple scene with a plane, and I'm trying to use a ray caster to find where the plane intersects with a vector from below. Whatever I try, there are never any intersections. Any ideas as to what is wrong with this?
Here is the relevant code and a plunker. The arrow below shoots through the plane, so there should be some intersection there...
var geom = new THREE.PlaneGeometry(SCREEN_WIDTH, SCREEN_HEIGHT, 200, 200 );
var material = new THREE.MeshPhongMaterial({color: 0x2194ce, side: THREE.DoubleSide});
var mesh = new THREE.Mesh(geom, material);
geom.computeFaceNormals();
mesh.rotation.x = -Math.PI / 2;
scene.add(mesh)
var p1 = new THREE.Vector3(100, -20, 100);
var p2 = new THREE.Vector3(100, 10000, 100);
var arrow = new THREE.ArrowHelper(p2.clone().normalize(), p1, 150, 0xFFFFFF);
scene.add(arrow)
var direction = new THREE.Vector3 (0, 1, 0);
raycaster = new THREE.Raycaster(p1, direction);
var intersects = raycaster.intersectObject(mesh);
console.log(intersects) //nada

three.js pointLight changes from r.67 to r.68

The interaction between pointlight and a plane seems to have changed from r.67 to r.68
I'm trying to learn three.js, going through a book that is a year old.
I've stripped down the tutorial example to just a plane, a cube, and a pointlight and The "Shinyness" effect of the light on the plane goes away when i use r.68, which is the point of the light effect tutorial.
I'm guessing it must have something to do with the material reflectivity of planes now?
I didn't get any clues going through three.js github revision notes or history of the function sourcecode or similar current three.js examples, but my three.js rookie status is probably holding me back from knowing what to look for.
If someone could explain what changed and why it's not working I would love to turn this broken tutorial into a learning experience.
EDITED TO ADD FIDDLE EXAMPLES INSTEAD OF SOURCE
Here is r.68:
http://jsfiddle.net/nnu3qnq8/5/
Here is r.67:
http://jsfiddle.net/nnu3qnq8/4/
Code:
$(function () {
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColorHex(0xEEEEEE, 1.0);
renderer.setSize(window.innerWidth, window.innerHeight);
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15
plane.position.y = 0
plane.position.z = 0
// add the plane to the scene
scene.add(plane);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff7777});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
// position the cube
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// add the cube to the scene
scene.add(cube);
// position and point the camera to the center of the scene
camera.position.x = -25;
camera.position.y = 30;
camera.position.z = 25;
camera.lookAt(new THREE.Vector3(10, 0, 0));
// add subtle ambient lighting
var ambiColor = "#0c0c0c";
var ambientLight = new THREE.AmbientLight(ambiColor);
scene.add(ambientLight);
// add spotlight for the shadows
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
// scene.add( spotLight );
var pointColor = "#ccffcc";
var pointLight = new THREE.PointLight(pointColor);
pointLight.distance = 100;
pointLight.position = new THREE.Vector3(3, 5, 3);
scene.add(pointLight);
// add the output of the renderer to the html element
$("#WebGL-output").append(renderer.domElement);
// call the render function
var step = 0;
// used to determine the switch point for the light animation
var invert = 1;
var phase = 0;
var controls = new function () {
this.rotationSpeed = 0.03;
this.ambientColor = ambiColor;
this.pointColor = pointColor;
this.intensity = 1;
this.distance = 100;
}
var gui = new dat.GUI();
gui.addColor(controls, 'ambientColor').onChange(function (e) {
ambientLight.color = new THREE.Color(e);
});
gui.addColor(controls, 'pointColor').onChange(function (e) {
pointLight.color = new THREE.Color(e);
});
gui.add(controls, 'intensity', 0, 3).onChange(function (e) {
pointLight.intensity = e;
});
gui.add(controls, 'distance', 0, 100).onChange(function (e) {
pointLight.distance = e;
});
render();
function render() {
stats.update();
// move the light simulation
if (phase > 2 * Math.PI) {
invert = invert * -1;
phase -= 2 * Math.PI;
} else {
phase += controls.rotationSpeed;
}
pointLight.position.z = +(7 * (Math.sin(phase)));
pointLight.position.x = +(14 * (Math.cos(phase)));
if (invert < 0) {
var pivot = 14;
pointLight.position.x = (invert * (pointLight.position.x - pivot)) + pivot;
}
// render using requestAnimationFrame
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
$("#Stats-output").append(stats.domElement);
return stats;
}
});
You are using a pattern that is no longer supported.
pointLight.position = new THREE.Vector3( 3, 5, 3 );
Do not create a new object. Instead do this:
pointLight.position.set( 3, 5, 3 );
three.js r.68

How can I print text along an arc using Three.js?

I'm trying to print text along an arc using Three.js. With help from this post, I was able to get the math for the rotation. But, as you'll see, the text is all jumbled on top of itself. How can I get it to correctly space itself out? Codepen is here.
var container, camera, scene, renderer;
function init() {
container = document.getElementById( 'canvas' );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 1, 100000);
camera.position.z = 500;
scene.add(camera);
var loader = new THREE.FontLoader();
loader.load( 'https://raw.githubusercontent.com/mrdoob/three.js/master/examples/fonts/helvetiker_bold.typeface.json', function ( font ) {
var theText = "This is some text."
var numRadsPerChar = 2*Math.PI/theText.length;
for (var i = 0; i < theText.length; i++){
var char = theText[i]
var geometry = new THREE.TextBufferGeometry( char, {
font: font,
size: 60,
height: 60,
curveSegments: 20
});
var materials = [
new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, overdraw: 0.5 } ),
new THREE.MeshBasicMaterial( { color: 0x000000, overdraw: 0.5 } )
];
var mesh = new THREE.Mesh( geometry, materials );
mesh.rotation.z = (i * numRadsPerChar);
//mesh.position.x = i * 20;
group = new THREE.Group();
group.add( mesh );
scene.add( group );
};
} );
renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
animate();
} init();
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
Had some help figuring this out, but here's the portion that needed to be edited to work:
mesh.rotation.z = (i * numRadsPerChar);
Needed to be:
mesh.position.x = 100 * Math.sin(i * numRadsPerChar);
mesh.position.y = 100 * Math.cos(i * numRadsPerChar);
mesh.rotation.z = Math.PI/2 -(i * numRadsPerChar);
Basically, I needed to add a constant (Math.PI/2) for the rotation. For the x and y positions, I had to take the sin and cosine, respectively, to get the letters to be placed properly around the arc. Here's a working codepen.
Another way to do this would be to draw your text on a canvas and use the canvas in a texture with texture.wrapS and .wrapT set to THREE.RepeatWrapping.. then put that texture on a material.. then make a path with the extruded path geometry and set it's material. The you can move the text by setting the texture.offset.x or y each frame in your render loop.

Categories

Resources