StuEv
January 1, 2024, 4:24pm
1
Hi,
I have a problem with my code. I imported a mesh with obj and mtl and i implemented a function to make a gradient onto the mesh. the problem is because the material consists of many files so the gradient doesn’t function properly. How can I fix this?
function applyHeightBasedColor(object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh && child.geometry) {
const geometry = child.geometry;
geometry.computeBoundingBox();
const minZ = geometry.boundingBox.min.z;
const maxZ = geometry.boundingBox.max.z;
const heightRange = maxZ - minZ;
const color = new THREE.Color();
const colors = ;
const positionAttribute = geometry.attributes.position;
for (let i = 0; i < positionAttribute.count; i++) {
const z = positionAttribute.getZ(i);
const normalizedHeight = (z - minZ) / heightRange;
// Interpolate between 0 (red), 0.33 (green) and 0.66 (blue)
const hue = normalizedHeight * 0.66;
color.setHSL(hue, 1.0, 0.5);
colors.push(color.r, color.g, color.b);
}
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
child.material = new THREE.MeshPhongMaterial({
vertexColors: true,
shininess: 0
});
}
});
}
I’m not sure I understand the question, and there is no way to debug the code, so I can only guess what is happening.
Namely, you define gradient color depending on the Z coordinate of each vertex in the geometry. The problem is that this is not always the Z position in the scene, because the child object can be translated, scaled, rotated … and its geometry data will still be the same.
I could only suggest that you get the (X,Y,Z) of a vertex, convert it to global coordinates (aka world coordinates) and then use its Z value to calculate gradient. If this fails, then there might be some other issue.
Good luck with your project.
1 Like
A very rough example of how you can achieve it, having a hierarchical object
Pictures:
Demo: JSFiddle
2 Likes
StuEv
February 25, 2024, 7:04pm
4
just played a bit with the code and i decided to not use a texture but it seems like the gradient has problems with my mesh because i still have artifacts. The Mesh is an OBJ file with Texture
Do you know why I have this issue?:
function applyHeightBasedGradientMaterial(object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
const geometry = child.geometry;
geometry.computeVertexNormals();
geometry.computeBoundingBox();
const minZ = geometry.boundingBox.min.z;
const maxZ = geometry.boundingBox.max.z;
// Vertex Shader
const vertexShader = `
varying float vHeight;
varying vec3 vNormal;
void main() {
vHeight = position.z;
vNormal = normalize(normalMatrix * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
// Fragment Shader mit einfacher Beleuchtung
const fragmentShader = `
precision highp float;
varying vec3 vNormal;
varying float vHeight;
uniform float minZ;
uniform float maxZ;
vec3 hsl2rgb(vec3 hsl) {
vec3 rgb = clamp(abs(mod(hsl.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);
rgb = rgb*rgb*(3.0-2.0*rgb);
return hsl.z + hsl.y * (rgb-0.5)*(1.0-abs(2.0*hsl.z-1.0));
}
void main() {
float heightRatio = (vHeight - minZ) / (maxZ - minZ);
vec3 color = hsl2rgb(vec3(0.66 * heightRatio, 1.0, 0.5)); // HSL-Farbverlauf
float lighting = dot(normalize(vNormal), vec3(0, 0, 1));
lighting = (lighting + 1.0) / 2.0; // Normalisieren des Wertes auf das Intervall [0,1]
lighting = lighting * 0.5 + 0.5; // Lighting up Areas with Shadows
vec3 finalColor = color * lighting;
gl_FragColor = vec4(finalColor, 1.0);
}
`;
// ShaderMaterial mit Uniforms und Shadern
const material = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
uniforms: {
minZ: { value: minZ },
maxZ: { value: maxZ }
}
});
child.material = material;
}
});
}
StuEv:
vHeight = position.z;
Doing so, you operate with coordinates in local space of a mesh, whereas you need coordinates in world space.
Why Z-axis for height, by the way?
The main
function of vertex shader needs to be something like so:
void main(){
vNormal = normalize(normalMatrix * normal);
vec4 modelPos = modelMatrix * vec4(position, 1.); // to world space
vHeight = modelPos.z; // possibly, it has to be Y-axis now, in world space
gl_Position = projectionMatrix * viewMatrix * modelPos;
}
1 Like
StuEv
February 25, 2024, 9:04pm
6
Hey thanks for your help!
I Want to Use the Z Axis für ground models like digital terrain models so i can see the lowest areas and the highest :-)!
I implemented your code snippet to my code but it looks the same
function applyHeightBasedGradientMaterial(object) {
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
const geometry = child.geometry;
geometry.computeVertexNormals(); // Stellen Sie sicher, dass Vertex-Normalen berechnet werden
geometry.computeBoundingBox();
const minZ = geometry.boundingBox.min.z;
const maxZ = geometry.boundingBox.max.z;
// Verbesserter Vertex Shader
const vertexShader = `
varying float vHeight;
varying vec3 vNormal;
void main() {
vNormal = normalize(normalMatrix * normal);
vec4 modelPos = modelMatrix * vec4(position, 1.0); // Transformation to World Coordinatesystem
vHeight = modelPos.z; // Z-Coordinate in world Coordinates
gl_Position = projectionMatrix * viewMatrix * modelPos;
}
`;
// Fragment Shader mit einfacher Beleuchtung
const fragmentShader = `
precision highp float;
varying vec3 vNormal;
varying float vHeight;
uniform float minZ;
uniform float maxZ;
vec3 hsl2rgb(vec3 hsl) {
vec3 rgb = clamp(abs(mod(hsl.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);
rgb = rgb*rgb*(3.0-2.0*rgb);
return hsl.z + hsl.y * (rgb-0.5)*(1.0-abs(2.0*hsl.z-1.0));
}
void main() {
float heightRatio = (vHeight - minZ) / (maxZ - minZ);
vec3 color = hsl2rgb(vec3(0.66 * heightRatio, 1.0, 0.5)); // HSL-Gradient
float lighting = dot(normalize(vNormal), vec3(0, 0, 1));
lighting = (lighting + 1.0) / 2.0; // Normalisieren des Wertes auf das Intervall [0,1]
lighting = lighting * 0.5 + 0.5; // Birighten up Shadows
vec3 finalColor = color * lighting;
gl_FragColor = vec4(finalColor, 1.0);
}
`;
// ShaderMaterial mit Uniforms und Shadern
const material = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
uniforms: {
minZ: { value: minZ },
maxZ: { value: maxZ }
}
});
child.material = material;
}
});
}
Compute min and max z-values for the whole object, not for its separate children.
StuEv
February 25, 2024, 9:56pm
8
StuEv:
it is one obj File
It doesn’t mean, that it’s a single mesh. From what I see, I would say the model contains several parts (meshes, thus, children).
Here is an example (not the ultimate solution) of how you can modify materials of a model, that contains children.
Before:
After:
Demo: JSFiddle
2 Likes