I am creating a terrain with a help of heightmap data that i have in json file with 4104x1856 number of floating point height data between 0 and 1.
and I need to create a terrain using that heightmap but my problem is that my mesh is blocky. I feel that i am doing it wrong. Since i am new here, if you could suggest something to correct it or do something different so that instead of blocky it will be smooth surface, that would be so much helpful.
this is my code
const terrainDimension = new THREE.Vector2(4104, 1856);
const geometry = new THREE.PlaneGeometry(
terrainDimension.x,
terrainDimension.y,
terrainDimension.x - 1,
terrainDimension.y - 1
);
geometry.rotateX(-Math.PI / 2);
const vertices: Array<number> = (geometry as THREE.BufferGeometry).attributes
.position.array as Array<number>;
let i = 0,
j = 0,
l = 0;
for (i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
vertices[j + 1] = data[i] * 100;
}
// and i create mesh
terrain = new THREE.Mesh(geometry, _meshMaterial);
terrain.geometry.computeVertexNormals();
terrain.castShadow = true;
terrain.receiveShadow = true;
scene.add(terrain);
I am using rawshaderMaterial because i have to triplanar mapping. I have smooth shading not flat shading.
This is my output in fragment shader when i color it as normal value
out_FragColor = vec4(vNormal.xyz, 1.0);
PS: I also tried mergeVertices from BufferGeometryUtils but it had not effect on output.
Could you share the shader you’re using? The mesh looks alright, and the issue is most liekly caused by the mapping done in the shader, so a bit hard to help without seeing the shader
If you set the vertices height like this you’re basically transforming it with no interpolation (like “nearest neighbour”), you should at least linear interpolate the height data and ensure your geometry density isn’t higher than the resolution you provide or use a different interpolation then.
A 1:1 resolution without interpolation is possible as well, but the differences between each texel obviously need to be decent, the heavier the differences the more blocky it will become, which you seem to ramp up here with 100:
You can throw in your buffer as a float texture as well and let the GPU do the interpolation, what isn’t exactly the best practice way to do since you only have a complete static terrain but will do.
In any case you cannot use such heavy differences in the data height data, the only way would be a denser geometry with bicubic or similar interpolation, but the insufficient data would make it look unnatural as well, smooth but unnatural as there is no actual detail.
If removing the 100 multiplication and it looks perfectly fine then this is what the resolution density is made for, scaling up these differences needs smoothing then.
This is the output after multiplying by 20 instead of 100. The terrain now looks like a flat plane and the blocky-ness is not there expect couple of place which is troublesome thing ( when height*20)
Yes that’s what i already thought, also that it’s from satelite data, you need to consider the realscale distances while the resolution is relatively low to what details the surfaces has, while it looks flat, if you were down with the camera in height of a human it would be different.
To scale up these features you need interpolation, if you want to create more geometry to avoid grid edges then smoothing interpolation like bicubic, and even further more possibly blurring or denoising the data first if it’s too bumpy, by scaling it up you create more and more heavy differences between each texel the edges of the geometry will stand out more, a interpolation with denser geometry will smoothly bridge across those “pillars” of texels.
But ramping up geometry density is costly as well as you easily start getting millions of triangles for such a static terrain, unless you create sparse adaptive geometry based on slope changes.
It sure looks like you could fix it with smoothing interpolation right away, but you need to implement it and it takes away details as well as it always will smooth a patch of neighbouring texels to shape a smooth hill.
In short, there. You don’t take the directy values, instead you read the neighbours and interpolate the values. You just need to lookup how interpolation works i can only point you in the direction, you easily find examples googling for “pixel interpolation js” like javascript - Linear interpolation on canvas - Stack Overflow
Of course you only need to consider one channel, you need to lookup the pixels coordinates from your array when using the function examples from there. While you could use it as float texture to let the GPU do the work there won’t be normals automatically and there is no reason to let the GPU do the transformation every frame on such a static mesh.
Calculating the index in data by the xy coordinates like for the function at SO simple as
x = Math.max( Math.min( x, width - 1 ), 0 );
y = Math.max( Math.min( y, height - 1 ), 0 );
const i = y * width + x;
Thank you so much for giving me hope and sharing your wisdom.
I am sorry for being dumb but i am still confused, what to interpolate? height value right? i checked the interpolation techniques and all of them are suggesting for interpolating the value to the place where there is no value.
suppose there is a mountain and the height map data array is like [0.2, 0.221, 0.2413, 0.3114]
how do i interpolate? I mean i know interpolation and i am not able to understand how to i use interpolation and use the result when there is no empty place to put that interpolation result.
Suppose i am doing linear interpolation.
do you mean i interpolate between 0.2 and 0.2413 and replace value of second by interpolated value?
and like wise use second and fourth element for interpolation and replace the third element with result value?
Do you mean scaling a terrain and using interpolated value in the scaled empty pixels?
i am sorry for bugging but i am not able to understand and since i am getting lead, i want to learn it properly and believe me i checked interpolation and interpolation techniques as well but still confused.
I am sorry for being dumb but i am still confused, what to interpolate?
You need to interpolate between one block and the next.
In the image here, think of the bottom red point as a vertex on one block and the top red point is a vertex on the next block. The blue line is a linear interpolation between the two points.
Currently in your terrain, there’s no blue line, there’s a “step”. Essentially you will need to add some extra vertices along the blue line to get rid of the step.
You will get better results with a curved interpolation instead of linear but I would start with linear as it’s the simplest.
Hi Thank you so so much for response !!! does this mean that i need to make atleast double the number of vertices in the mesh and interpolate the value of two vertices to calculate the height of the vertex in between two known vertices?