How to plot various textures on geometry in Three.js - three.js

The map that I am attempting to create is isometric and is plotted in 44x44 pixel tiles. Each tile material is a loaded image.
Material with ID 1 might be reused at various x/y positions within the view. Material with ID 2 might only be used once. The same goes for any remaining tiles that need to be plotted.
I haven’t found anything in the docs that would be helpful and suggestions found on the web to use createMultiMaterialObject seem to be outdated.

If you have only a single geometry and want to apply multiple materials on it, you have to define so called groups data. These data allow you to render different parts of the geometry with different materials. After defining groups, you can create your (multi-material) mesh like so:
const mesh = new THREE.Mesh( geometry, [ material1, material2, material3 ] );
three.js R111

Related

simulate separate alpha map in glTF

I have an existing 3D application that was developed back in the days of the ThreeJS r92 that made use of a separate alpha map to simulate holes cut in wooden panels. This allowed re-use of a high quality woodgrain texture across all models, while using easily compressible black and white images to create the 'holes' in the wooden panels.
Now that I have begun migration of the project to the current glTF format, I find that the base color and alpha map now have to be combined. The result for my project is that now each of 130+ wooden panels will need their own "wood grain + alpha" texture, rather than being able to share a single wood grain texture.
From all my research, it seems like there are no obvious options to this situation using glTF.. my question now is - does anyone know of ANY workaround using glTF that allows me to separate the base color texture from the alpha map texture?
At this point the best way ahead sadly seems to be to avoid glTF and go back to using ObjectLoader, which is a pain as the binary objects of glTF are a huge plus.
You could export the GLTF with only the base color. Once imported into Three.js, you can manually assign the black/white texture as the material's .alphaMap property.
// Fetch texture
const texLoader = new THREE.TextureLoader();
const alphaTexture = texLoader.load("path/to/alphaTexture");
gltfLoader.load("path/to/model", function(gltf){
// Find the mesh you want to assign an alphaMap to
const myMesh = gltf.getObjectByName("meshName");
// Now just bind your texture to the alphaMap property
myMesh.material.alphaMap = alphaTexture;
myMesh.material.transparent = true;
});
The docs state that only the green channel is used so it doesn't have to be black and white.

ThreeJS Points (Point Cloud) with Lighting using custom Shader Material

Coded using:
Using ThreeJS v0.130.1
Framework: Angular 12, but that's not relevant to the issue.
Testing on Chrome browser.
I am building an application that gets more than 100K points. I use these points to render a THREE.Points object on the screen.
I found that default THREE.PointsMaterial does not support lighting (the points are visible with or without adding lights to the scene).
So I tried to implement a custom ShaderMaterial. But I could not find a way to add lighting to the rendered object.
Here is a sample of what my code is doing:
Sample App on StackBlitz showing my current attempt
In this code, I am using sample values for point cloud data, normals and color but everything else is similar to my actual application. I can see the 3D object, but need more proper lighting using normals.
I need help or guidance to implement the following:
Add lighting to custom shader material. I have Googled and tried many things, no success so far.
Using normals, show the effects of lighting (In this sample code, the normals are fixed to Y-axis direction, but I am calculating them based on some vector logic in actual application). So calculating normals is already done, but I want to use them to show light shine/shading effect in the custom shader material.
And in this sample, color attribute is set to fixed red color, but in actual application I am able to apply colors using UV range from a texture to color attribute.
Please advise how/if I can get lighting based on normals for Point Cloud. Thanks.
Note: I looked at this Stackoveflow question but it only deals with changing the alpha/transparency of points and not lighting.
Adding lighting to a custom material is a very complex process. Especially since you could use Phong, Lambert, or Physical lighting methods, and there's a lot of calculations that need to pass from the vertex to the fragment shader. For instance, this segment of shader code is just a small part of what you'd need.
Instead of trying to re-create lighting from scratch, I recommend you create a PlaneGeometry with the material you'd like (Phong, Lambert, Physical, etc...) and use an InstancedMesh to create thousands of instances, just like in this example.
Based on that example, the pseudo-code of how you could achieve a similar effect is something like this:
const count = 100000;
const geometry = new PlaneGeometry();
const material = new THREE.MeshPhongMaterial();
mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
scene.add( mesh );
const dummy = new THREE.Object3D();
update() {
// Sets the rotation so it's always perpendicular to camera
dummy.lookAt(camera);
// Updates positions of each plane
for (let i = 0; i < count; i++){
dummy.position.set( x, y, z );
dummy.updateMatrix();
mesh.setMatrixAt( i ++, dummy.matrix );
}
}
The for() loop would be the most expensive part of each frame, so if you need to update it on each frame, you might want to calculate this in the vertex shader, but that's another question altogether.

The problem of ray selection of mesh corresponding material in threejs

I have a model, only one mesh, and material is an array in mesh with 4 ShaderMaterial.The data structure of the model object,Position of material mark point
I want to click the mesh to get the material in the corresponding position, and use Raycaster to get the intersect object. I want to find the corresponding material in the object through the face.materialIndex.How do I get the right material? intersect data
If you have a specific materialIndex, you can get the corresponding material object like so:
const material = mesh.material[ materialIndex ];
As stated in the documentation when using multiple materials per mesh, Mesh.material becomes an array of materials.

Three.js - How to update UV mapping when using morph targets?

I've been struggling with this one for hours, and found nothing either in the docs or here on SO that would point me to the right direction to achieve what I aim at.
I'm loading a scene containing several meshes. The first one is used as an actual mesh, rendered on the scene, the other ones are just used as morph targets (their geometries, properly speaking).
loader.load("scene.json", function (loadedScene) {
camera.lookAt( scene.position );
var basis = loadedScene.getObjectByName( "Main" ).geometry;
var firstTarget = loadedScene.getObjectByName( "Target1" ).geometry;
// and so on for the rest of the "target" meshes
basis.morphTargets[0] = {name: 'fstTarget', vertices: firstTarget.vertices};
var MAIN = new THREE.Mesh(basis);
This works very well, and I can morph the MAIN mesh with no hassle by playing with the influence values. The differences between the basis mesh and the target are not huge, basically they're just XY adjustments (2D shape variations).
Now, I'm using a textures material: UVs are properly projected (Blender export) and the result is nice with the MAIN mesh as is.
The problem comes when the basis shape is morphed towards a target geometry: as expected, the texture (UVs) adapts automatically, but this is not what I need to achieve => I'd need to have the UVs "morph" towards the morph target's UVs for the texture to look the same.
Here is an example of what I have now (left: basis mesh, right: morphTargetInfluences = 1 for the first morph target)
morph target and texture
What I'd like to have is the exact same texture projection on the final, morphed mesh...
I can't figure out how to do that the right way. Should I reassign the target UVs to the MAIN mesh (and how would I do that)?
The result would be like having a cloth below which a shape is morphed, and the cloth being "shrinked-wrapped" all the time against that underlying shape => you can actually see the shape changes, but the cloth itself is not deformed, just wrapping itself properly and consistently around the shape...
Any help would be much appreciated! Thanks in advance :)

Specular highlights with a model imported from Blender?

I made in Dice with 2 different colored materials in Blender and exported it with the Blender exporter. In my three.js code, I use JSONLoader to get the mesh and use new THREE.MeshFaceMaterial(); as material.
This loads the model just fine with the two different colored materials showing correctly. But my problem is, that I want the dice to have specular highlights on the surface. Is this even possible with exported Blender models?
I managed to get a Sphere with specular highlights when I used THREE.MeshPhongMaterial() as material for the sphere, but the sphere wasn't imported from Blender and as far as I know, I can't apply a Phong Material when I load the Mesh with JSONLoader, or is there a trick? Please remember: I got two differently colored materials on the dice, which I import from Blender in the JSON file. The dice by itself is red, but the dots should be black. So I am not talking about different colors on each side of a cube or so, but two different colors on every side of the cube. Can you help me?
Here's the code snippet:
loader.load("models/dice.js", function(geometry){
var material = new THREE.MeshFaceMaterial();
material.specular = 0xffffff;
material.shininess = 10000000000;
dice=new THREE.Mesh(geometry, material);
dice.position.set(0,-400,5);
dice.scale.set(75, 75, 75);
dice.overdraw = true;
dice.name="dice";
navscene.add(dice);
});
If it helps, I am able to create a texture out of the two materials and apply them in Blender, so that the JSONLoader loads the texture instead of the materials, but I wasn't able to achieve a specular highlight either with that.
Thx West, you pointed me into the right direction. The problem was, that Blender's standard material type is Lambert. In Blender, this Lambert material produces the desired specular highlight, but once imported to three, the effect is gone. I don't know if this is a bug, or a desired behaviour.
The solution is to set the material type in Blender to Phong material. This works even after import. My guess is, that THREE.MeshFaceMaterial is an array which contains all the loaded materials, whether they are lambert, phong or whatever. It is even possible to mix those material types. So it appears to me, that THREE.MeshFaceMaterial doesn't seem to be a "stand alone" material type at all. Correct me if I'm wrong please.
If your mesh geometry has a materials array, you can always change the array elements after you load the geometry:
geometry.materials[ i ] = new THREE.MeshPhongMaterial( ... );
You then leave the mesh's material as MeshFaceMaterial:
mesh = new THREE.mesh( geometry, new THREE.MeshFaceMaterial() );
The exporter script you are using may set geometry.materials to Lambert material by default, based on your settings. Have a look at the script source code and see if you can figure it out.

Resources