We are using a heightmap to generate terrain, and now we want to strewn some vegetation on it. This needs to be on the same level as the terrain of course.
The way I’m envisioning it is to get a random position on the texture and reading the pixeldata to see how high it should be placed, but I haven’t seen a method to get a single pixel from the texture.
I’m also open for suggestions. I realize this solution has a few flaws (random position may be picked several times, there’s no vegetation clustering, etc.)
Hi @CaseyDeCoder,
To quickly get a range of valid positions based on a color channel from a texture, you can use an image parser like this for PNG:
You can iterate through the color buffers returned and get the values that have green > 0.5 for example. From there you can project those pixels on the terrain and find their world positions. So you will have mostly accurate positions to raycast and find the true height.
Most likely you will have to make the points denser so for each pixel point you find a number of random points around it. That will provide some basic clustering at the same time.
A more performant/elaborate approach would be to do all of this in a vertex shader, if you don’t require actual pc.Entities spawned in place. Basically you will have to feed your vertex shader with the heightmap and colormap of the terrain and use some form of hardware instancing to find grass positions and the associated height. I think this WebGL example does something similar:
2 Likes
I looked at terra and gave that up rather quickly.
Unfortunately, UPNG isn’t working for me. It is saying “file is not a png” when I am using a png.
var img = this.img.resource.getResource(); // this.img is a texture asset.
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
var bufferWidth = img.width;
var bufferHeight = img.height;
canvas.width = bufferWidth;
canvas.height = bufferHeight;
context.drawImage(img, 0, 0);
var buffer = context.getImageData(0, 0, bufferWidth, bufferHeight).data;
var myImg = UPNG.decode(buffer);
var rgba8 = UPNG.toRGBA8(myImg); // throws error
Also, using a png for a heightmap is unnecessarily expensive.
1 Like
That was near, but the code that really made if for me is this:
var img = this.texture.resource; // this.texture is a texture asset
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img.getSource(), 0, 0 );
var imgData = context.getImageData(0, 0, img.width, img.height);
// Get the X, Y pixel color for the image
var redIndex = y * (img.width * 4) + x * 4;
var pixelColor = [
imgData.data[redIndex], // Red channel for the pixel
imgData.data[redIndex+1], // Green channel
imgData.data[redIndex+2], // Blue channel
imgData.data[redIndex+3]]; // Alpha channel
Hope this helps someone!
2 Likes