Why do I need to update uvs? - three.js

I'm working with the three.js editor where I parse an object from JSON format. As usual it first parses the materials and geometries, then I create meshes from it. While parsing materials I also load textures. The issue now is that I have to call...
object.geometry.uvsNeedUpdate = true;
object.geometry.buffersNeedUpdate = true;
... after the image for the texture completely loaded - but why?! The geometry never changed before, neither did its uvs or anything like it. It's still the plain old geometry, yet I always get a GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 2 when trying to render. It only works with this "hack" although the geometry is always the same.
In my opinion it should also work perfectly when I update the uvs after object creation (or not at all). I didn't find anything in the three.js editor code that would update the geometry or its faceVertexUvs.
I know it's a bit of an abstract problem, I'm mainly looking for some hints or insights why this hack might be necessary.
Thanks!

Three.js "guesses" whether uvs are needed according to your used textures in bufferGuessUVType. If you want to preallocate uv buffers you can either init a map attribute with an empty THREE.Texture, update the geometry after the map was assigned, etc.

Related

Separate materials in an instancedMesh react three fiber

I essentially have a bunch of geometries that I need to display unique and updating text.
The approach I've been using to display the text is using a canvas material which(along with the mesh position) is constantly updated in a useFrame.
However, the only way I've been able to get the texture to work is as follows, and all geometries are obviously sharing it.
<instancedMesh ref={meshRef} args={[null, null, intervalData.length]}>
<circleBufferGeometry args={[sizes.radius ?? 0.6, sizes.segments ?? 48]}>
<instancedBufferAttribute attachObject={['attributes', 'color']} args={[colorArray, 3]} />
</circleBufferGeometry>
<meshStandardMaterial vertexColors={THREE.VertexColors} map={texture}/>
</instancedMesh>
What would be the way to set the textures per instance? Is there somewhere I can store an array of textures and assign them to the mesh?
Probably pretty late for you, but maybe if someone else is stuck with same question. I have solved this like this https://codesandbox.io/s/instancedmesh-with-different-textures-forked-iy5xh?file=/src/App.js
Though I am passing each texture separately, it has down side that you can only pass 16 textures to single shader, so maybe you'll have to use texture atlas (basically single texture composed of multiple texture and you also pass couple of more attributes to crop the particular texture part from whole texture)
Probably any performance boost from InstancedMesh would be surpassed by using a Sprite, not to mention more useful.

Render scene onto custom mesh with three.js

After messing around with this demo of Three.js rendering a scene to a texture, I successfully replicated the essence of it in my project: amidst my main scene, there's a now sphere and a secondary scene is drawn on it via a THREE.WebGLRenderTarget buffer.
I don't really need a sphere, though, and that's where I've hit a huge brick wall. When trying to map the buffer onto my simple custom mesh, I get an infinite stream of the following errors:
three.js:23444 WebGL: INVALID_VALUE: pixelStorei: invalid parameter for alignment
three.js:23557 Uncaught TypeError: Cannot read property 'width' of undefined
My geometry, approximating an annular shape, is created using this code. I've successfully UV-mapped a canvas onto it by passing {map: new THREE.Texture(canvas)} into the material options, but if I use {map: myWebGLRenderTarget} I get the errors above.
A cursory look through the call stack makes it look like three.js is assuming the presence of the texture.image attribute on myWebGLRenderTarget and attempting to call clampToMaxSize on it.
Is this a bug in three.js or am I simply doing something wrong? Since I only need flat rendering (with MeshBasicMaterial), one of the first thing I did when adapting the render-to-texture demo above was remove all trace of the shaders, and it worked great with just the sphere. Do I need those shaders back in order to use UV mapping and a custom mesh?
For what its worth, I was needlessly setting needsUpdate = true on my texture. (The handling of needsUpdate apparently assumes the presence of a <canvas> that the texture is based on.)

SceneImporter not loading MeshFaceMaterial textures in r68

I have a scene which I am exporting from Blender using the Three.js exporter. After a bit of trial and error I got it to export fine. When I tried to import it into Three, I got a few WebGL errors like glDrawElements: range out of bounds for buffer, which seemed to be related to the size or positioning of the imported object. I mucked around with some stuff related to size and eventually got it to load fine. However, none of my materials with textures are loading, EXCEPT one, which was an object with a single material applied to it.
Here's my pipeline
Create base for the level in my three js editor tool
Export this and import it into Blender for texturing/uv mapping (works fine)
Objects can have a number of materials, one for each face really, each material can have a different texture mapped to it (all good, objects are uv unwrapped etc..,)
Join all objects together (except one object which is a tree with a single material/texture applied to it)
Export the Blender file as a JSON file via the Three.js exporter
Load the file into Three using the SceneImporter
When it comes into Three, there are two objects, one is the tree which has a single texture mapped to its material. The other is a large geometry with 20 materials and textures mapped. This geometry does not have any textures displaying and just shows as a MeshLambertMaterial.
Has anyone else experienced any issues like this? Any solutions?
Seems like the SceneImporter doesn't know what to do with these materials, but all the data is there, so you'll need to give it a little help.
Like so;
loader.load('sceneWithObjectOfManyFaceMaterials.json' , function(loaded)
{
for(var key in loaded.objects)
{
var mesh = loaded.objects[key];
if(mesh.material.materials)
{
mesh.material.materials.forEach(function(m , i)
{
m.map = loaded.materials[m.name].map;
});
}
scene.add(mesh);
}
render();
});

lightMap / specularMap / shading with meshBasicMaterial

I'm currently working on something along the lines of a plugin for another program to add 3D capability to it, so I'm trying to put all the functionality i can from three.js into it, with the added goal of this being a good way to learn all the functionality of three.js firsthand.
I'm running into an issue now as i implement textures and materials that with mesh basic material, setting some things which the documentation on the main threejs.org site shows are features, doesn't actually do anything.
when i set a texture for either specularmap or lightmap nothing is actually showing up. Im pretty sure its not a mistake im making because setting the texture of the map works, but trying to set this same texture for the specularMap or lightMap is doing nothing. Does a regular texture work for these, or do i have to do something different?
I'd also like to know what the shading property does for mesh basic, because as far as i can see setting it to smoothshading/flatshading/noshading is doing nothing aswell.
MeshBasicMaterial does not respond to lights. Change your material to MeshLamberMaterial or MeshPhongMaterial, for example.
For MeshBasicMaterial and MeshLambertMaterial, the specularMap is used only to modulate the reflection when an environment map is used.
For any material, lightmaps require a second set of UVs. geometry.faceVertexUvs[ 0 ] contains the usual set of UVs; for a lightmap, you need to add geometry.faceVertexUvs[ 1 ], a second set of UVs.
For MeshBasicMaterial, the shading property only applies when an environment map is used. SmoothShading will yield smooth reflections; FlatShading will yield faceted reflections.
three.js r.66

Erroneous bindTexture(TEXTURE_2D, null); call, or bad shader? Texture disapparing with three.ShaderMaterial

In two cases, I have a THREE.ShaderMaterial that doesn't doesn't correctly render an object, omitting its texture.
On both examples, the middle object is a basic THREE.MeshPhongMaterial
Example1: http://jsfiddle.net/sG9MP/4/ The object that's closest to the screen never shows.
On this one, it works with renderer.render(...) but not composer.render(...).
renderer.render( scene, camera );
//composer.render();
Example2: http://jsfiddle.net/sG9MP/5/ Here I'm trying to duplicate the MeshPhongMaterial shader as a base so I can modify it. I tried to replicate it perfectly. I copied the uniform, vert, frag, and replicated what's in the object. I can't see anything different, so I don't get why it's not working the same as the standard Three.js phong shader.
So it's two cases where I'm using THREE.ShaderMaterial and it's not rendering the shader correctly, and I can't figure out why. On the second example(which is the one where I really need fixed. The first was an old test), in the webGL inspector I see the scene often looks fine until there is a "bindTexture(TEXTURE_2D, null);" call that happens under the hood by three.js. Though sometimes it just draws without it. In the first example, it's always drawing without it.
I feel like I must be missing some sort of flag in the renderer, or composer, or something. Or in my second example, where I'm trying to copy the Three.js phong shader, maybe I didn't copy something perfectly.
The goal here is just to copy the phong shader, so I can modify the uniform, vert, and frag on it. Sadly, I can't simply .clone() it since the vert and frag can't be modified after it's compiled.
It looks like while ShaderMaterial.map was being set, ShaderMaterial.uniforms.map.value was not consistently set.
I really don't understand this, though. In some cases I had issues not setting things at the top level under ShaderMaterial. Other cases I have issues not setting uniforms.
In my material, I just went and added this:
for(var k in phongMat){
if( typeof phongMat.uniforms[k] != 'undefined' ){
phongMat.uniforms[k].value = phongMat[k];
}
}

Resources