Toggle materials/textures in an instance in react-three-fiber - three.js

I have an instance using Drei library with a plane geometry.
According to a state, I would like to display different materials for each instance:
state is false: display a standard material with a simple color
state is true: apply a different texture (an image)
Each instance has its own state (for example 20% can be true and 80% can be false, or another ratio)
What would be the best way to do that?
Retained solution: Instanced Attributes
As mentionned by #Bhushan Wagh, the idea is to store the texture state inside a geometry attribute. Then, you can access this data in the shaders in order to apply a custom logic. You can follow his codesandbox for the R3F solution, or this one for the drei instance solution.
Investigated solutions
Having 2 distinct instances (one for the colored material, one for the image texture):
it's ok when we have only 2 states, but what if we have like 10 possible states? (What would be the best solution in this case?)
Represent a plane with a boxGeometry and display the proper face according to the state
It's also only working when we only have 2 states and are in 2D

I don't think you need to change the material if you just wanna toggle between plain color and texture. You can do this doing some changes in shader of material where you pass the vec4 to gl_fragColor in fragment shader based on the state, you can pass the state as attribute to the mesh, since you using instanced mes you can use the instancedAttribute. I had the codesandbox for solution for using different textures on each object of instanced mesh so I made a changes in that code to solve your problem ( or at least what I think is solution) https://codesandbox.io/s/toggle-texture-of-instanced-mesh-ptcvbb You can toggle state of each plane by clicking on it. and I think this should work even with more than 2 states, you can represent the states with number and based on number you can select particular texture from texture array passed as uniform.

Related

How is a single texture with different colors being applied to the entity?

How is it that the texture for a beat is applied different objects in this moonrider project. First, I see that a beat material is defined using this.beatsTexture:
https://github.com/supermedium/moonrider/blob/b24431261dca9c8f5df717a5518983f69eeb58e4/src/components/materials.js#L241
... and when I go to the texture, I see a single texture being generated for all of the various colors.
https://github.com/supermedium/moonrider/blob/b24431261dca9c8f5df717a5518983f69eeb58e4/src/components/materials.js#L474
What I can’t trace down is:
How is this texture being used to identify different colors with the different beats (e.g. red vs blue)?
How can view the results of this texture easily to be able to debug?
Where is the logic where this texture’s coords are translated for the different beats to show the correct colors?
Thank you for the help!!!
To try to help with #1:
In the first file you linked, after the texture is define, a few materials are also defined which use that texture. Each of the materials specifies a different color (scheme.primary and scheme.secondary) which can be used to tint the texture being applied to the object. When an object collides with the mesh that uses the material, one could determine the color by checking something like mesh.material.uniforms.color.value and seeing if it were equal to the primary or secondary color, for example.

How can I light emission per vertex and per vertex lighting in ThreeJS?

I want to see a chart with color specified per vertex and to get little bit of shading too.
But if I use MeshBasicMaterial I only get VertexColor with no dynamic shading.
On the other hand, if I use MeshPhongMaterial I just get shading but without emissiveness from my vertex colors.
As the THREE.JS PhongMaterial supports vertexColors, giving you a nice combination of dynamic lighting and vertex colors, I'm not quite sure I understand your question. Perhaps that is something you should investigate more?
However, as an alternative to writing a custom shader you could try rendering your model in multiple passes.
This will not give you as much control over the way the vertex colors and phong lighting are combined as a shader would, but often a simple add/multiply blend can give pretty decent results.
Algorithm:
- create two meshes for the BufferGeometry, one with the BasicMaterial and one with the PhongMaterial
- for the PhongMaterial, set
depthFunc = THREE.EqualDepth
transparent = true;
blending = THREE.AdditiveBlending(or MultiplyBlending)
- render the first mesh
- render the second mesh at the exact same spot

Fixed texture size in Three.js

I am building quite a complex 3D environment in Three.js (FPS-a-like). For this purpose I wanted to structure the loading of textures and materials in an object oriënted way. For example; materials.wood.brownplank is a reusable material with a certain texture and other properties. Below is a simplified visualisation of the process where models uses materials and materials uses textures.
loadTextures();
loadMaterials();
loadModels();
//start doing stuff in the scene
I want to use that material on differently sized objects. However, in Three.js you can't (AFAIK) set a certain texture scale. You will have to set the repeat to scale it appropiate to your object. But I don't want to do that for every plane of every object I use.
Here is how it looks now
As you can see, the textures are not uniform in size.
Is there an easy way achieve this? So cloning the texture and/or material every time and setting the repeat according to the geometry won't do :)
I hope someone can help me.
Conclusion:
There is no real easy way to do this. I ended up changing my loading methods, where things like materials.wood.brownplank are now for example getMaterial('wood', 'brownplank') In the function new objects are instantiated
You should be able to do this by modifying your geometry UV coordinates according to the "real" dimensions of each face.
In Three.js, UV coordinates are relative to the face and texture (as in, 0.0 = one edge, 1.0 = other edge), no matter what the actual size of texture or face is. But by modifying the UVs in geometry (multiply them by some factor based on face physical size), you can use the same material and texture in different sizes (and orientations) per face.
You just need to figure out the mapping between UVs, geometry scale and your desired working units (eg. mm or m). Sorry I don't have, or know a ready algorithm to do it, but that's the approach you probably need to take. Should be quite doable with a bit of experimentation and google-fu.

Many meshes with the same geometry and material, can I change their colors?

I have a large number (~1000) of THREE.Mesh objects that have been constructed from the same THREE.Geometry and THREE.MeshPhongMaterial (which has a map).
I would like to tint (color) these objects individually.
Naïvely, I tried changing the mesh.material.color property, but changing this property on any of the objects changes the color of all the objects at once. This makes sense, since there is only one material that is shared among all the objects.
My next idea was to create a separate THREE.MeshPhongMaterial for each object. So, now I have a large number of THREE.Mesh objects that been constructed from the same THREE.Geometry, but have individual THREE.MeshPhongMaterials (that share the same texture). This allows me to change the colors individually, but the performance is worse. The chrome profilier shows that the app is spending significant time doing material-things like switching textures.
The material color is just a uniform in the shader. So, updating that uniform should be quite quick.
question: Is there a way to override a material color from the mesh level?
If there was, I believe I could share the material among all my objects and get my performance back, while still changing the colors individually.
[I have tested on v49 and v54, they have identical performance and degredation]
update: I have built a test case, and the performance drop due to this is smaller than I thought it was, but is still measurable.
Here are two links:
http://danceliquid.com/docs/threejs/material-test/index.html?many-materials=false
http://danceliquid.com/docs/threejs/material-test/index.html?many-materials=true
In the first case, there are only two materials, in the second case each cube has it's own material. I measure the framerate of the first case to be 53fps on this machine, and the framerate of the second is 46fps. This is about a 15% drop.
In both cases, the color of the material of every cube is changed every frame. In the case with many materials, we actually see each cube getting it's own color, in the case with only two materials, we see them all having the same color (as expected).
Yes. Per object, clone your material using material.clone(), modify its emissive and color, and set the object's material to this clone. Shaders and attributes are copied by reference, so do not worry that you are cloning the entire material each time; in fact the only things that are copied by value are the uniforms (such as emissive and color). So you can change these per individual object.
Personally I store the original material on a separate, custom property of the object so that I can easily switch back to it later; depends what your needs are.
If you're writing your own shaders, you could use a uniform variable for a general tint (not vertex specific) and pass that in to the shader for factoring into the overall color. vec4f_t and vec4f() are not standard in the C portion, but your code probably already has equivalents.
C:
vec4f_t hue = vec4f(....); // fill in as desired
// load the shader so that GLuint shader_id is available.
// "hue" is a uniform var in the vertexshader
GLUint hue_id = glGetUniformLocation(shader_id, "hue");
// later, before rendering the object:
glUniform4fv(hue_id, 1, &hue);
the.vertexshader:
uniform vec4 hue; // add this and use it in the texture's color computation

In GLGE, Is it possible specify the face of a mesh that a texture should be mapped to? (WEBGL as well)

I'm trying to make an environment map which is in the form of a cube that has images mapped onto particular faces to give the illusion of being in the area (sorta like google's street view)
I'm trying to do It in glgehowever, with my limited experience, I only know how to map one texture to a whole mesh (Which is what I'm doing at the moment). If I were to create 6 different textures, would it possible for me to specify the faces that those textures should be loaded to?
You could generate the six faces of the cube as separate objects and use a different texture for each. Alternative is to set different texture coordinates for the different faces of the cube.
If you want ready-to-run code, three.js has a couple of skybox examples. E.g. http://mrdoob.github.com/three.js/examples/webgl_panorama_equirectangular.html
You should look at "UV Mapping". Check this example. Roughly, UVs describe how the polygons are mapped (in x,y) on the texture.
Sounds like you want a cube map texture — it takes six separate images, and you lookup in it with a direction vector rather than (u,v) coordinates. They are the usual way to do environments. Cube map textures are available in WebGL.

Resources