I am trying to tile the texture from multiple images onto a plane geometry using MeshFaceMaterial. Every thing works fine, except for a blurry edge forming in between tiles.
.
var textureArray = [];
var tileColumns = 2;
var tileRows = 1;
textureArray[0] = THREE.ImageUtils.loadTexture('./test3.jpg');
textureArray[1] = THREE.ImageUtils.loadTexture('./test4.jpg');
var faceCountPerTileX = 2 * widthSegments/tileColumns;
var faceCountPerTileY = heightSegments/tileRows;
var faceCountX = 2 * widthSegments;
var faceCountY = heightSegments;
for(var tileIndexY = 0; tileIndexY < tileRows; tileIndexY++){
for(var tileIndexX = 0; tileIndexX < tileColumns; tileIndexX++){
var index = tileIndexY * tileColumns + tileIndexX;
textureArray[index].wrapS = THREE.RepeatWrapping;
textureArray[index].wrapT = THREE.RepeatWrapping;
textureArray[index].repeat.set(tileColumns,tileRows);
materialContainer[tileIndexY * tileColumns + tileIndexX] = new THREE.MeshBasicMaterial({
map: textureArray[tileIndexY * tileColumns + tileIndexX],
overdraw: true,
ambient: 0xffffff
});
for(var faceIndexY = tileIndexY * faceCountPerTileY; faceIndexY < (tileIndexY+1) * faceCountPerTileY; faceIndexY++){
for(var faceIndexX = tileIndexX * faceCountPerTileX; faceIndexX < (tileIndexX+1) * faceCountPerTileX; faceIndexX++){
g.faces[faceIndexY * faceCountX + faceIndexX].materialIndex = tileIndexY * tileColumns + tileIndexX;
}
}
}
}
var mat = new THREE.MeshFaceMaterial(materialContainer);
var obj = new THREE.Mesh(g, mat);
I have tried all known solutions, i have even tried writing a custom shader and using ShaderMaterial. But no luck, can some help me out to fix the issue?
By the looks of it, you set the texture mode of the invidual textures in your set to repeat.
This seems wrong, the individual textures do not repeat, they are displayed only once. Setting a texture to repeat causes the right side of the texture to "blend through" on the left (and vice versa), causing visible seams like the one on your screenshot.
Related
Working with Three.js r113, I'm creating walls from coordinates of a blueprint dynamically as custom geometries. I've set up the vertices, faces and faceVertexUvs already successfully. Now I'd like to wrap these geometries with a textured material, that repeats the texture and keeps the original aspect ratio.
Since the walls have different lengths, I was wondering which is the best approach to do this?
What I've tried so far is loading the texture once and then using different texture.repeat values, depending on the wall length:
let textures = function() {
let wall_brick = new THREE.TextureLoader().load('../textures/light_brick.jpg');
return {wall_brick};
}();
function makeTextureMaterial(texture, length, height) {
const scale = 2;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( length * scale, height * scale );
return new THREE.MeshStandardMaterial({map: texture});
}
I then call the above function, after creating the geometry and assign the returned materials to the material array to apply it to faces of front and back of each wall. Note: material.wall is an untextured MeshStandardMaterial for the other faces.
let scaledMaterial = [
makeTextureMaterial(textures.wall_brick, this.length.back, this.height),
makeTextureMaterial(textures.wall_brick, this.length.front, this.height),
material.wall
];
this.geometry.faces[0].materialIndex = 0; // back
this.geometry.faces[1].materialIndex = 0; // back
this.geometry.faces[2].materialIndex = 1; // front
this.geometry.faces[3].materialIndex = 1; // front
this.geometry.faces[4].materialIndex = 2;
this.geometry.faces[5].materialIndex = 2;
this.geometry.faces[6].materialIndex = 2;
this.geometry.faces[7].materialIndex = 2;
this.geometry.faces[8].materialIndex = 2;
this.geometry.faces[9].materialIndex = 2;
this.geometry.faces[10].materialIndex = 2;
this.geometry.faces[11].materialIndex = 2; // will do those with a loop later on :)
this.mesh = new THREE.Mesh(this.geometry, scaledMaterial);
What happens is that the texture is displayed on the desired faces, but it's not scaled individually by this.length.back and this.length.front
Any ideas how to do this? Thank you.
I have just found the proper approach to this. The individual scaling is done via faceVertexUvs, as West Langley answered here: https://stackoverflow.com/a/27098476/4355114
I have made a scene with three.js in which I make use of a PointLight.
var distance = 10000;
var intensity = 10;
var decay = 0;
var hex = 0xFFFFFF;
var light1 = new THREE.PointLight(hex, intensity, distance, decay);
I want to use shadows, so I added
light1.shadow.camera.near = 200;
light1.shadow.camera.far = 10000;
light1.shadow.camera.fov = 90;
light1.shadow.bias = -0.00022;
light1.shadow.mapSize.width = 1024;
light1.shadow.mapSize.height = 1024;
Until here everything worked perfect. However, as soon as I include in the next line:
light1.castShadow = true;
Then the distance visibly changes from 10000 to 1000.
If I call the distance from light1 to the console, it is still 10000. Even though it is not like that in the canvas.
(I have changed it to a ~50-digit number with no change in distance)
Any suggestions on how to solve this?
You can try to create a shadow via THREE.Texture(), in this way the shadow will be an independent element and shouldn't influence the position of the PointLight.
var shadowTexture = new THREE.Texture(canvas);
shadowTexture.needsUpdate = true;
var shadowMaterial = new THREE.MeshBasicMaterial({map: shadowTexture, opacity: 0.5});
var shadowGeo = new THREE.PlaneBufferGeometry(100, 100, 100, 1);
// Locate the position of the shadows
mesh = new THREE.Mesh(shadowGeo, shadowMaterial);
mesh.position.y = 0;
mesh.rotation.x = - Math.PI / 2;
scene.add( mesh );
I am applying the shadow to a PlaneGeometry since I assume that the shadow is casted on the scene. In any case, give also a look at this example concerning shadows in a scene.
Is it possible to add an external image or text on an already jsc3d created 3d object.For example if any canvas imagedata needs to be stored on the created 3d object,then is it possible?
Yes, its possible.
If you look at the jsc3d implementation of Texture, you will see that a texture has already an underlying canvas.
Let say you have a canvas called "myTexture" and a Mesh called "myMesh", and to make it simple, you just only need a texture with a fixed size of 128x128 px, this will paint your canvas onto your mesh:
var canvas = document.getElementById('myTexture');
var context = canvas.getContext('2d');
var dim = 128;
var imgData = context.getImageData(0,0,dim,dim);
var data = imgData.data;
var size = data.length / 4;
var texture = new JSC3D.Texture;
texture.data = new Array(size);
var alpha;
for(var i=0, j=0; i<size; i++, j+=4) {
alpha = data[j + 3];
texture.data[i] = alpha << 24 | data[j] << 16 | data[j+1] << 8 | data[j+2];
if(alpha < 255)
texture.hasTransparency = true;
}
texture.width = dim;
texture.height = dim;
myMesh.setTexture(texture);
viewer.update();
The .data loop is taken from JSC3D.Texture.prototype.createFromImage (credits humu2009, creator of jsc3d).
I'm generating a random plane that animates movement in the vertices to give a crystalline effect. When I use regular PlaneGeometry, shading is not a problem: http://codepen.io/shshaw/pen/GJppEX
However, I tried to switch to PlaneBufferGeometry to see if I could get better performance, but the shading disappeared.
http://codepen.io/shshaw/pen/oXjyJL?editors=001
var planeGeometry = new THREE.PlaneBufferGeometry(opts.planeSize, opts.planeSize, opts.planeDefinition, opts.planeDefinition),
planeMaterial = new THREE.MeshLambertMaterial({
color: 0x555555,
emissive: 0xdddddd,
shading: THREE.NoShading
}),
plane = new THREE.Mesh(planeGeometry, planeMaterial),
defaultVertices = planeGeometry.attributes.position.clone().array;
function randomVertices() {
var vertices = planeGeometry.attributes.position.clone().array;
for (var i = 0; i <= vertices.length; i += 3) {
// x
vertices[i] = defaultVertices[i] + (rand(-opts.variance.x, opts.variance.x));
// y
vertices[i + 1] = defaultVertices[i + 1] + (rand(-opts.variance.y, opts.variance.y));
// z
vertices[i + 2] = rand(-opts.variance.z, -opts.variance.z);
}
return vertices;
}
plane.geometry.attributes.position.array = randomVertices();
As I saw suggested in this answer to 'Shading on a plane', I tried:
plane.geometry.computeVertexNormals();
On render, I have tried all of the following attributes for the geometry to make sure it's updating the normals & vertices, like I've done on the working example with PlaneGeometry:
plane.geometry.verticesNeedUpdate = true;
plane.geometry.normalsNeedUpdate = true;
plane.geometry.computeVertexNormals();
plane.geometry.computeFaceNormals();
plane.geometry.normalizeNormals();
What has happened to the shading? Can I bring it back on a PlaneBufferGeometry mesh, or do I need to stick with PlaneGeometry?
Thanks!
I'm attempting to create a randomly distributed particle field that exhibits Brownian (random) motion.
Scroll to the bottom of the code to see where the error happens. I'm trying to set the position of a single vertex with position.x.
I'll omit the rest of the code not directly related to rendering the particles in effort to save your time.
//Using WebGL renderer...
var particle, particles = [], particle_system, material, p_x, p_y, p_z;
particles = new THREE.Geometry();
material = new THREE.ParticleBasicMaterial({color: 0xffffff, size: 1});
for(var count = 0; count < 1000; count++){
p_x = Math.random() * 1000 - 500;
p_y = Math.random() * 1000 - 500;
p_z = Math.random() * 1000 - 500;
particle = new THREE.Vector3(p_x, p_y, p_z);
particles.vertices.push(particle);
}
particle_system = new THREE.ParticleSystem(particles, material);
scene.add(particle_system);
particle_system.geometry.dynamic = true;
//All of the code bellow will go into the render loop.
var index = 0;
while(index < 1000){
index++;
particle_system.geometry.verticiesNeedUpdate = true;
//THESE 3 LINES BELLOW CAUSE THE ERROR
particles.vertices[index].position.x += Math.random() * 1000 - 500;
particles.vertices[index].position.y += Math.random() * 1000 - 500;
particles.vertices[index].position.z += Math.random() * 1000 - 500;
}
verticiesNeedUpdate should be spelled verticesNeedUpdate
your while loop is wrongly incrementing the index variable before it is used. So in the last iteration (when index == 999) you try to access particles.vertices[1000] which is not defined