I'm getting errors with Three.js v68 when I load a OBJ file and use a Material with texture map in it,
Error: WebGL: drawElements: bound vertex attribute buffers do not have sufficient size for given indices from the bound element array
The mesh works fine until I add a texture map. The map is set on the material with this code:
var imgTexture = THREE.ImageUtils.loadTexture( "assets/tex01.png" );
imgTexture.wrapS = THREE.RepeatWrapping;
imgTexture.wrapT = THREE.RepeatWrapping;
imgTexture.needsUpdate = true;
...
mat.map = imgTexture;
mat.needsUpdate = true;
The obj (exported from 3dmax) has UV data inside, and that UV info is loaded in thee faceVertexUvs property.
vt 0.8586 0.4138 0.0000
vt 0.8719 0.3913 0.0000
vt 0.9145 0.2598 0.0000
....
The mesh is loaded with THREE.OBJLoader. The loaded Object has faces: Array[6812] elements and faceVertexUvs: Array[1].Array[6812] elements of real UVs values. So its appears to have loaded the right data. But its failing during rendering.
Related
In Three.js, how can I change the way in which a texture gets mapped onto a plane?
Let's assume we have a 1x1 plane and a 16:9 image. How can I control the way in which that image gets mapped onto the plane?
By default, the image gets "squished". I would like it to maintain its aspect ratio and have any overlap get "cut off". Is there a way to configure the material or texture to do this, or would I use a shader? If so, what would it need to look like?
const planeMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1),
new THREE.MeshBasicMaterial({
map: texture,
})
);
PS: In future, I would also like to be able to zoom into and out of the image on mouse hover without affecting the size of the plane, so would think a shader might be better?
A Texture already has several properties built-in that can do what you're looking for.
const texture = textureLoader.load("whatever.png");
const planeMesh = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1),
new THREE.MeshBasicMaterial({
map: texture,
})
);
// Sets the pivot point to the center of the texture
texture.center.set(0.5, 0.5);
// Make the texture repeat 0.5625 times in the x-axis to match 16:9 ratio
let ratio = 9 / 16;
texture.repeat.set(ratio, 1);
// Scale texture up to "zoom" into it
let zoom = 0.5;
texture.repeat.set(ratio * zoom, 1 * zoom);
You can read more about the .repeat .center and even .rotation properties in the Texture docs. Just keep in mind that repeating a texture is a bit counter-intuitive because you're doing the inverse of scaling a texture. So to scale a texture by 2, you have to tell it to repeat 1/2 times.
I have seams between horizontal faces of the cube when use texture atlas in three.js.
This is demo: http://jsfiddle.net/rnix/gtxcj3qh/7/ or http://jsfiddle.net/gtxcj3qh/8/ (from comments)
Screenshot of the problem:
Here I use repeat and offset:
var materials = [];
var t = [];
var imgData = document.getElementById("texture_atlas").src;
for ( var i = 0; i < 6; i ++ ) {
t[i] = THREE.ImageUtils.loadTexture( imgData ); //2048x256
t[i].repeat.x = 1 / 8;
t[i].offset.x = i / 8;
//t[i].magFilter = THREE.NearestFilter;
t[i].minFilter = THREE.NearestFilter;
t[i].generateMipmaps = false;
materials.push( new THREE.MeshBasicMaterial( { map: t[i], overdraw: 0.5 } ) );
}
var skyBox = new THREE.Mesh( new THREE.CubeGeometry( 1024, 1024, 1024), new THREE.MeshFaceMaterial(materials) );
skyBox.applyMatrix( new THREE.Matrix4().makeScale( 1, 1, -1 ) );
scene.add( skyBox );
The atlas has size 2048x256 (power of two). I also tried manual UV-mapping instead of repeat, but the result is the same. I use 8 tiles instead of 6 because I have thought precision of division 1/6 causes the problem, but not.
Pixels on this line are from next tile in atlas. I tried completly white atlas and there was not any artefacts. This explains why there are not seams on vertical borders of Z-faces. I have played with filters, wrapT, wrapS and mipmaps but it does not help. Increasing resolution does not help. There is 8192x1024 atlas http://s.getid.org/jsfiddle/atlas.png I tried another atlas, the result is the same.
I know that I can split atlas into separate files and it works perfectly but it is not convenient.
Whats wrong?
I think the issue is the filtering problem with texture sheets. On image borders in a texture sheet, the gpu may pick the texel from either the correct image or the neighbor image due to limited precision. Because the colors are usually very different, this results in the visible seams. In regular textures, this is solved with CLAMP_TO_EDGE.
If you must use texture alias, then you need to fake CLAMP_TO_EDGE behavior by padding the image borders. See this answer https://gamedev.stackexchange.com/questions/61796/sprite-sheet-textures-picking-up-edges-of-adjacent-texture. It should look something like this: (exaggerated borders for clarity)
Otherwise, the simpler solution is to use a different texture for each face. Webgl supports the cube texture and that is usually used the majority of the time to implement skyboxes.
Hack the uv, replace all value 1.0 with 0.999, replace all value 0 with 0.001 will fakely resolve part of this problem.
I'm using a ShaderMaterial to generate noise to a render texture.
Then I'm using the resulting texture as a displacementMap on a plane using phong shading material.
What happens is that the displacement map will go from [0-displacementScale].
Meaning that my noise texture will be interpreted per pixel as: 0x000000 pixels means 0 vertex offset, and 0xffffff pixels means the displacementScale value...
I thought the displacementBias would allow me to map the range, but it kind of just offsets the "average".
I would like to know how could I map the range from the displacementMap so 0x000000 could mean e.g: -100 and the 0xffffff could mean 100...
In the source file displacementmap_vertex.glsl, you will find the displacement formula:
transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );
So in your case, set displacementBias = -100 and displacementScale = 200.
three.js r.73
I have a mesh with normalMap loaded:
but the normal map did not cover all the surface.
UVs are correctly set to cover all the surface with texture.
Is there any configuration to force normal map repeating?
material.normalMap.repeat.set( x, x ); do not make any difference.
If i switch normalMap to BumpMap or diffuse map "map", it is loaded correctly.
Code:
child.material.normalMap = THREE.ImageUtils.loadTexture('./js/models/asteroids/big/10035-normal.jpg' );
child.material.normalMap.repeat.set( 1, 1 );
///child.material.needsUpdate = true;
child.material.normalMap.wrapS = child.material.normalMap.wrapT = THREE.RepeatWrapping;
I have a simple THREE.js app that renders a cube and applies textures on each of the faces like so:
var cubeGeometry = new THREE.CubeGeometry(5, 8, 1, 4, 4, 1);
var materials = [ new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture('front.jpg') }),
//.....Front, back, left, etc...
];
...
var cubeMesh = new THREE.Mesh(cubeGeometry, new THREE.MeshFaceMaterial(materials));
However, all I see is a black cube, i.e. the images dont appear ont the cube's faces.
Also, my code works fine in release 50 of the THREE.js library, so it seems like a change is a newer release has caused my code to break, and I cant seem to find any relavent documentation around it.
Any help is appreciated.
The following code should work as of release 97
// creates cubes geometry in front of camera (assuming your camera position and rotation has not changed)
var geometry = new THREE.CubeGeometry(1, 1, 1, -2, 0, 0);
// creates a texture loader for the cube
var texture = new THREE.TextureLoader();
// defines variables to make things easier
var counter, textures = [], materials = [];
// iterate through all 6 sides of the cube
for(counter = 0; counter < 6; counter ++) {
// loads and stores a texture (you might run into some problems with loading images directly from a source because of security protocols, so copying the image data is a for sure way to get the image to load)
textures[counter] = texture.load('data:image/restOfImageAddress');
// creates material from previously stored texture
materials.push(new THREE.MeshBasicMaterial({map: textures[counter]}));
}
// creates the cube by mixing the geometry and materials
var cubeMesh = new THREE.Mesh(geometry, materials);