I'm trying to load a texture for my mesh using three.js, but instead of loaded texture I'm getting plain black object. I did it as it is in three.js docs (https://threejs.org/docs/?q=texture#api/en/loaders/TextureLoader - Example) and also tried different solutions but nothing worked. Here is the code
//Creating The Sun model
const sunGeometry = new THREE.SphereGeometry(16, 30, 30);
const texture = new THREE.TextureLoader().load('assets/images/sun.jpg');
const sunMaterial = new THREE.MeshBasicMaterial({ map: texture }); // not working
//const sunMaterial = new THREE.MeshBasicMaterial({ color: 'skyblue' }); //working
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
//Adding The Sun to the Scene
scene.add(sun);
renderer.render(scene, camera);
Setting a mesh color works perfectly fine tho. Also tried using urls, no difference.
I'm using Angular and that code is in a constructor
Edit: image path is correct
You're calling renderer.render(scene, camera); immediately after creating your Mesh, but at that time the texture hasn't loaded yet. Textures can take a fraction of a second to load, so by the time they're loaded the rendering has already taken place. If you're calling render() at 60 frames per second, you probably won't notice this delay, but if you're calling it only once, then you'll get a black material.
If you only want to call render() once after the texture has loaded, you should just use the onLoad callback in the method outlined in the code example of the docs page.
Related
When using textureloader from three.js in a blazor application. when trying to get a picture on a cube, it just gets displayed in black. I have tried a lot of things including:
new THREE.TextureLoader().load('https://images.pexels.com/photos/2422/sky-earth-galaxy-universe.jpg?auto=compress&cs=tinysrgb&dpr=1&w=500', function (texture) {
var objGeometry = new THREE.SphereBufferGeometry(11, 11, 11);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
moonMesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(moonMesh);
});
Local images that are preloaded don't work, tried with and without callback. Tried another browser.
The console is empty, if I log the texture, it's always define.
Still the sphere is always displayed in black. Maybe a light is missing, although they don't get used in any example. The js is loaded from a blazor page using the following code:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
object[] args = { canvasId };
await JSRuntime.InvokeVoidAsync("threeExample", args); // NOTE: call JavaScript function
}
And the scene and stuff load fine, I can use orbidcontrols, normal cubes with a color texture are visibe. Any ideas?
EDIT:
When using MeshBasicMaterial It works! So I guess I used the wrong type of material to use the texture.
Kind regards
I have a legacy code snippet that has long since been part of a web application. The goal is to render a cube from 6 image urls via three.js. The problem is, that the bottom image is flipped around on its y axis. All other sides look fine.
There is no way to flip the original image, since they are provided by an external service. Also when I open the bottom image directly, it is displayed correctly, only in the three.js cube its flipped.
Since I have no prior experience with three.js, I first looked at the documentation for this functionality and realized that the code used in our script is deprecated. unfortunately there is no detailed documentation of the old function (THREE.ImageUtils.loadTextureCube) - or I was simply unable to find it (if someone knows where it is, a link would be great!).
I know that for the long term, a reimplementation to replace all deprecated functions would in order, but since this would take a lot more time (for someone unfamiliar with three.js), I first wanted to check if the problem can be fixed using the existing code base.
I read in various other posts/issues that this is a known issue because three.js for some internal reason defaults to flipY=true on textures. The problem I have is that I don't know (due to lack of documentation) how to change this parameter on one side of the cube only. I expect a function can be applied to the loader that could update this parameter for one of the images or a transformation could be applied to the loaded image afterwards.
I really hope somebody knows (remembers) how this can be achieved with the old loader function.
The following partial code is from my script:
// passed list of urls (images.bottom is flipped around)
var urls = [ images.left, images.right, images.top, images.bottom, images.back, images.front ];
// SCENE
scene = new THREE.Scene();
// TEXTURES
textureCube = THREE.ImageUtils.loadTextureCube( urls );
textureCube.format = THREE.RGBFormat;
textureCube.mapping = THREE.CubeReflectionMapping;
// MATERIALS
var cubeShader = THREE.ShaderLib[ "cube" ];
var cubeMaterial = new THREE.ShaderMaterial( {
fragmentShader: cubeShader.fragmentShader,
vertexShader: cubeShader.vertexShader,
uniforms: cubeShader.uniforms,
depthWrite: false,
side: THREE.BackSide
} );
cubeMaterial.uniforms[ "tCube" ].value = textureCube;
var cubeGeometry = new THREE.BoxGeometry(50,50,50);
cube = new THREE.Mesh( cubeGeometry, cubeMaterial );
scene.add( cube );
// RENDER
renderer = new THREE.WebGLRenderer();
renderer.autoClear = false;
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( containerWidth, containerHeight );
renderer.setFaceCulling( THREE.CullFaceNone );
$container.append( renderer.domElement );
...
When applying a large texture image to a Mesh for a noticeable period of time Three.js locks-up browser's main thread. Let's consider the following example:
var texLoader = new THREE.TextureLoader();
texLoader.load('someLargeTexture.jpg', function(texture) {
var geometry = new THREE.SphereGeometry(10, 32, 32);
var material = new THREE.MeshBasicMaterial({map: texture});
var sphere = new THREE.Mesh(geometry, material);
// adding the object to the scene will lock up the browser's main thread
scene.add(sphere);
});
I also noticed the following:
no thread lock-up occurs if the new object is not added to the scene
changing the geometry of an object doesn't cause lock-up
if a new object is created with a material borrowed from an existing object (which already is in the scene) will will not cause lock-up
assigning a new material to an existing object (which already is in the scene) will causes lock-up
My conclusion is that Three.js does some work on a material at point at which it's added to the scene. The result is the cached and later re-used.
The question is if this work can somehow be offloaded to web worker, so that the main thread doesn't get locked-up?
The worker could look like this:
var texLoader = new THREE.TextureLoader();
texLoader.load('someLargeTexture.jpg', function(texture) {
var material = new THREE.MeshBasicMaterial({map: texture});
// ... long-running work goes here ...
// adding the object to the scene will lock up the browser's main thread
self.postMessage({
msg: 'objectCreated',
material: material.toJson(), // converting material to JSON
});
});
Then in the main thread we could do:
var worker = new Worker('worker.js');
worker.addEventListener('message', function(ev) {
var geometry = new THREE.SphereGeometry(10, 32, 32);
// converting JSON back to material object
var material = MaterialLoader.prototype.parse( ev.data.material );
// this should't lock-up the main thread
var sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
});
Can something like this be done?
Just run into this issue while trying to load textures in parallel with workers. Main roadblock seems to be THREE.Texture holds a reference to the <img> (DOM element) which in turn holds the actual image data. Don't know of a way to serialize an <img>, also webgl accepts <img> as texture data. So there is no need for THREE to touch it.
A solution might be to use a THREE.DataTexture, but that involves turning possibly compressed images into uncompressed RGB. Unfortunately a detour via a <canvas> drawImage() + getImageData() is not possible, because workers can't deal with DOM elements.
Eventually I've decided parallel texture loading has to wait for the-offscreen-canvas-interface.
I am upgrading some code that used to work with three.js r61 to three.js r74. I know, it's a big jump, but things are not always as they should be.
One of the remaining things that I still didn't get to work properly is a texture built using a canvas, used in a SpriteMaterial which is then used for a THREE.Sprite.
So far, I did the following:
Changed Texture to CanvasTexture
In SpriteMaterial, commented out the usage of useScreenCoordinates, alignment and sizeAttenuation
I tried various stuff, but it seems like this thing just doesn't display at all. Is there any such example anywhere? I just want to display an artifact that is built in a canvas.
Update: the code below is what I tried so far:
var loader = new THREE.TextureLoader();
loader.load(canvas.toDataURL(), function(texture) {
var material = new THREE.SpriteMaterial({
map: texture,
blending: THREE.AdditiveBlending,
color: me.color,
opacity: me.opacity,
transparent: me.transparent,
visible: me.visible
});
var sprite = new THREE.Sprite(material);
scene.add(sprite);
});
You're quite light on details but here's one good way to do it:
https://jsfiddle.net/_jered/xcej4ec6/
var loader = new THREE.TextureLoader();
loader.load(canvas.toDataURL(), function(d){
// 'd' is the loaded texture
// apply it to the object here
});
Basically, just use the built in Loader classes to load the assets you need, including from canvas. It's important to use Loaders and callbacks because assets will become ready asynchronously, and you need to wait until they're done before you make use of them. In my example, I create the mesh with a placeholder texture, and apply it in the Loader's callback.
I need to render a 'soft' cube with a normal map. I'm not getting any errors or warnings when running the code, but when I specify the normal map parameter in the function call and refresh the browser, I see a black object. If I remove the normal map, I can successfully render a cube with a Phong material. Interestingly, if I change the material to a Normal material, and attempt to texture the cube, the actual RGB normal map that I'm tring to use otherwise is applied. Also, I can get the normal map to work properly if I don't use THREE.SubdivisionModifier() on the cube, and simply render a sharp-edged cube. Any ideas? Thanks in advance. Here's my code:
var cube_geo = new THREE.BoxGeometry(.1, .1, .1, 5, 5, 5);
var smooth = cube_geo.clone();
var modifier = new THREE.SubdivisionModifier(5);
modifier.modify(smooth);
var cube_mat = new THREE.MeshPhongMaterial(
{
color: 0x000000,
specular: 0x222222,
normalMap: cube_normal_map,
}
);
cube = new THREE.Mesh(smooth, cube_mat);
scene.add(cube);
Three.SubdivisionModifier currently has a limitation in that is does not handle UVs.
See the note in the source code of SubdivisionModifier.js.
three.js r.70