New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Material: Feature request to use world coordinates of vertex as UV #18992
Comments
You can accomplish this with custom shaders, or with the node material system in the examples: const material = new THREE.StandardNodeMaterial();
const uv = new SwitchNode( new PositionNode(), 'xy' );
material.color = new TextureNode( texture, uv ); I don't think world coordinates as UVs is likely to be supported in a way other than the ideas above — the problem of selecting an existing UV set for specific textures has been stalled for a while, even though it is easier and more common. |
Thanks for the reply! The node material thing might solve the problem, but I don't see any documentation on that feature. Guessing its still in dev. Somethings I would like to know from the documentation is if it would support fog, which I use extensively, and whether it would support normal maps. I'm guessing the answer to both is yes. I know I could make a custom shader but that would lose the benefit of fog and normal maps. I was hoping the functionality I'm suggesting seems worth it to integrate into the engine itself. If not, that's ok. I'll have to find another way, or live with the seams. |
Yes, but there are already issues tracking the development and documentation: #7522 and #17971. And yes, fog and normal maps are supported. There is a list of examples that demonstrate the various features of this new material: https://threejs.org/examples/?q=nodes |
This issue is pretty important to me so I wrote a PR, my first for this repository. Please tell me how I can increase the chances of getting it merged 😃 |
TBH, I don't vote to add this logic to the renderer. It's too application specific. Consider to use the presented solution with node materials or modify the shaders on application level via I'm also not aware of any other engine that supports such a mode. |
As for other engines:
You might be right that there are no other engines that have such a "mode" (in the literal sense of the word) but that doesn't mean there is not demand for the feature. Others have clearly spent time working around the limits of their respective engines to use this feature. Edit: For what it's worth, one reason I like the |
The node material approach presented here #18992 (comment) is equivalent to what other engines/tools offer like e.g. Blender. I would say that is sufficient and does not justify a modification of the core materials. |
Here is how to implement this feature request in EDIT: Actually, this was a first-attempt -- without instancing. var hexMaterial = new Nodes.StandardNodeMaterial();
hexMaterial.side = THREE.DoubleSide;
hexMaterial.fog = true;
var scale = new Nodes.FloatNode( .1 );
var uv = new Nodes.SwitchNode( new Nodes.PositionNode( Nodes.PositionNode.WORLD ), 'xz' );
var uvPos = new Nodes.OperatorNode( uv, scale, Nodes.OperatorNode.MUL );
hexMaterial.color = new Nodes.TextureNode( worldUvTexture, uvPos ); |
In the end, it was trivial to add this feature and support instancing by using code-injection in the built-in material. In #include <worldpos_vertex>
vec4 wPos = instanceMatrix * vec4( transformed, 1.0 );
vUv = ( uvTransform * vec3( wPos.xz, 1 ) ).xy; |
@WestLangley could you elaborate on what you mean by 'code-injection'? It looks very promising. Is that something I could do programmatically? Do you mean defining a Also, 1) doesn't
The reason I would prefer modifying a normal material is that, having just tried the node approach, it has significant drawbacks (despite solving this one problem). Most notably, the resulting materials are not drop-in replacements that have familiar properties like |
Here's where I am now: material.onBeforeCompile = (shader) => {
shader.vertexShader = shader.vertexShader.replace('#include <uv_vertex>', 'vec4 worldPos = vec4(position, 1.0);\n#ifdef USE_INSTANCING\nworldPos = instanceMatrix * worldPos;\n#endif\nvUv = ( uvTransform * vec3( worldPos.xz, 1 ) ).xy;');
}; I guess my question is: Were you suggesting something different/better than this? |
In my proof-of-concept, I just hacked the shader by adding two lines after But, yes, in practice, you would use |
Thanks for the clarification. The issue I'm now dealing with is that onBeforeCompile() executes on the shader before three.js templates in everything. As a result, the #define USE_INSTANCING doesn't seem to be present. I would have liked to use the same material for instanced objects and non-instanced objects (world coordinates were supposed to make it universal) but maybe that's not an option. Edit: Nvm, just remembered that that information isn't needed in the JS runtime - its used by the shader compiler. Not sure why the math didn't work then...retrying. Edit: Turns out the matrix multiplication with instanceMatrix was occuring but that doesn't mean the shader works for some reason. Fixing this seam issue is slowly becoming a nightmare 😉 \ Edit: Looks like I was trying to insert code too early, and position hadn't been multiplied by the associated matrices to get world position. Thanks for the advice! (It might be ideal to have this logic in the engine but this solution works) For reference, my final solution was: material.onBeforeCompile = (shader) => {
shader.vertexShader = shader.vertexShader.replace('#include <uv_vertex>\n', '').replace('#include <worldpos_vertex>', 'vec4 worldPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\nworldPosition = instanceMatrix * worldPosition;\n#endif\nworldPosition = modelMatrix * worldPosition;\nvUv = (uvTransform * vec3(worldPosition.xz, 1)).xy;');
}; |
Description of the problem
There is an abundance of textures that can be repeated without showing seams. However, if they are applied to anything but the simplest of shapes (squares in a perfect grid), seams will re-emerge. One important feature of seamless textures is that the specific UVs are irrelevant. What matters is that the UVs are consistent between multiple shapes.
In the image, a "seamless" texture has been applied to a grid of hexagons (one of the 3 possible regular polygons that can form a grid). Circled are the intersecting hexagon vertices and arrows point to the edge. Seams have been reintroduced due to the fact that the texture was designed to be seamless over a square, not a hexagon. Some textures, especially ones with higher contrast, appear much worse with these seams.
Using debug texture to highlight that, while specific UVs work for grids of squares, they don't for grids of hexagons (or triangles for that matter):
Describe the bug or feature request in detail
One way to achieve this consistency is the use a global coordinate system to drive the UVs, such as scene/world coordinates. There is a slight complication that the axis that translates world to uv coordinates is relevant to the process. Therefore, I propose that the axis be configurable. The lack of such an axis would indicate that the geometry's specific, local UVs should be used.
Potential API:
material.uvsAxis = quaternion | plane | vector (normal to plane)
Alternate API:
material.uvsAxis = THREE.XZ | THREE.XY | THREE.YZ
(only support 3 axes)Alternate API:
geometry.uvsAxis = (see above); material.useUvsAxis = true
(allow each geometry to have its own UV axis.Both would solve my problem, with the first being highly flexible. To be clear, the implementation would project the world coordinate onto the axis to determine the resulting UV coordinate.
Not a solution: User having to manually set the UVs of each vertex of each mesh at all times. This approach doesn't support InstancedMesh, where each instance has the same UVs. It is also needlessly difficult.
Three.js version
As long as I can remember
Browser
OS
The text was updated successfully, but these errors were encountered: