TSL Shader - using positionLocal

Hello everyone, hopefully someone can enlighten me as to why this doesn’t work.

I want to color a surface based on it’s local, varying height.

To simplify - half way up the height at any point, I want to switch from the base color to the next color. Seems so simple!

So in the code below, how to I get the height of the surface so I can provide it to the HEIGHT_OF_THE_SURFACE?

const firstMix = step(HEIGHT_OF_THE_SURFACE_HERE.mul(0.5), positionLocal.y);

Tried passing it as an attribute of the surface geometry.
float(heightAttr).mul(0.5) Nope

Tried getting it from the position array. Nope.

But if I just use the actual number (18, for example) Bingo!

Some tiny clue would possibly save my sanity at this point.

Thanks

B


redMat.colorNode = Fn(() => {

  const colorOut  = bandColor(0).toVar();

// change at height 3 This works!!!

// const firstMix = step(float(6.0).mul(0.5), positionLocal.y); 

// This in no way works. Changes the whole surface to bandColor(1)
const firstMix = step(positionLocal.y.mul(0.5), positionLocal.y);

colorOut.assign( firstMix.mix(colorOut, bandColor(1).toVar() ));


return colorOut;

})();

Could you make a drawing or illustration of the desired effect? It is somewhat hard for me to imagine the coloring.

For example, if the image below shows the cross-section of the surface, what parts will be color 0 and what parts will be color 1? Note, that positionLocal is short-sighted, it does not see other positionLocal values, because each “pixel” (called fragment) is processed independently on others.

Thankyou Pavel for taking the time to look at this.

In my head I’m imagining something like a geological strata diagram. Eventually, something like that. But for now just ‘half way up’ to each of the vertices height.

Not sure what it will actually end up looking like. Possibly terrible. But I would like something approaching camouflage.

All the best
B

When you calculate the color of point B, the shader knows the elevation of point B, but has absolutely no idea of point A and its elevation. So it does not know whether point B should be the first or the second color.

Maybe you have to reconsider your approach or to look how to mimic the effect?

Hi Pavel, believe me when I say I’ve been trying lots of different ways.

But, I think I may be onto something. Maybe.

I’m generating an array of random numbers in JS, which I’m saving as a bufferAttribute:


let dummy = new Float32Array(vertexCount);

  for(let x=0; x<vertexCount; x++){

dummy[x] = Math.random()*62;

  }

geo.setAttribute( 'dummy', new THREE.Float32BufferAttribute(dummy, 1));

62 being just the maxHeight from a previous attempt. Other numbers work too.

Then in the shader:

const colorOut  = bandColor(0).toVar();

  // change at height/2

const nMix = step(float(dummyAttr).mul(0.5), positionLocal.y);

Perfect!

BUT if I use the array which contains the actual values of the vertices Y height. The same array used to deform the surface. Then, nothing:

Doesn’t matter if I do the value/2 in JS or if I do it in the shader. No difference.

And the arrays don’t look all that different.
Works:

array: Float32Array(1024) [17.36878204345703, 7.1190009117126465, 3.0476324558258057, 15.622990608215332, 13.9679536819458, 11.421907424926758, 2.049186944961548,

Does not work (this is actual heights. I’ve tried x 0.5 etc..):

array: Float32Array(1024) [30, 21, 24, 21, 37, 33, 23, 13, 16, 10, 26, 18, 21, 20, 19, 28, 22, 27, 30, 16, 25, 18,

So I can have a random pattern but not a meaningful one?

Gah!

B

Consider this expression: step(y/2, y).

If y>0 the result will always be constant, because y/2<y for every positive y. That’s why random numbers manage to cut through the surface, while actual half-values refuse to do so. I’m not sure whether I could explain it better, just manually evaluate with different values for y and y/2 and see that the result is always the same.

It is also possible that I misunderstand the issue. Maybe somebody else could come up with a better explanation or even a solution. For the time being, I still think the algorithm must be revised, or as alternative, some pseudo solution may also be good enough. Later on I may draft a pseudo solution.

1 Like

Thanks Pavel, I see the issue when you put it like that.

My goal in all of this is to produce a model of a geological strata. I have the relative weights for each strata at each point.

I imagine this as a solid consisting of a number of layers, each varying in thickness from point to point. But this doesn’t describe the surface, does it?

I confess to being a bit lost. But I shall continue to badger at the problem anyway!

All the best
BN

1 Like

Do you have data about the actual layers of geological strata that you must use, or you have only the surface and want to show some synthetic strata, more like imitation for illustrative purposes?

Horizontal layers are easy, but strata are not always horizontal, and often they are not exactly parallel to the surface.

1 Like

Hi Pavel, yes, I have the data. Example attached.

So, as you would imagine, the sum of each item [1,2,3,4,5,6] gives you an elevation.

I want eventually for the user to see it looking somewhat like the image attached though the layers will vary in thickness. The issue with this version being that is shows all layers as the same depth so does not provide a guide to what lies beneath…

Some other version:

The best result I’ve had is using the maxHeight as a yard stick. But then, you loose a lot of the data because the shader is working on the assumption everything is that tall…

strata.json (78.0 KB)

1 Like

Oh, I already made a pseudo solution with positionLocal, focusing on the geological aspect. It looks it is completely not what you want. Anyway, here it is in case someone finds it useful:

https://codepen.io/boytchev/full/GgjopGb

image

3 Likes

Lovely looking but ultimately not solving the problem I have.

I’m going to try ‘stacking blocks’ version. See how terrible that looks. Might give me some ideas.

Thanks Pavel, for your insight.

All the best

BN

1 Like

I’m looking at this and im not sure if i understand the problem. Why not use a texture so that you can find these neighboring values, or min/max within a certain area? To me this sounds like it doesn’t have anything to do with TSL specifically, or local positions derived from attributes alone? What are “stacking blocks”?

1 Like

Hello dubois, the goal is to describe a shader in tsl which will produce a geological stata which has x bands, which vary in relative thickness from point to point.

The problem currently seems to be about finding a local maxima for any given point and using this to determine which level of the strata is visible at any point/pixel.

As is, the height added to an attribute of the geometry ( the vertex.y used to displace the geometry initially) seems to be varied along with each iteration of the shader call when it’s accessed in the colorNode. So, the value at which the shader should change color bands is never obtained.

Your texture idea sounds like it could be useful. How could this be used to determine a local maxima? This is what’s needed perhaps.

Stacking blocks refers to the idea that you can think of the objective as being constructed by stacking blocks. At each x,z we have an array, [1,2,3,4,5,6] for example. Stack 1 red, 2 green, 3 yellow and so on. The problem restated is, how do we know which sides of these blocks are visible and which hidden by other blocks.

Does that make sense? Possibly not. I’ve been staring at this problem for a while and it’s softened my brain, I think.

Thanks
BN

You can define some radius, or whatever the shape, and sample it using something like QMC. I’m trying to google “geological strata” but not getting anything that looks like what you are describing. Is this a common representation or something that you came up with?

Hi Dubois,

It’s pretty common:

None of that requires a local min/max, it’s all global. You can see it being aligned to the iso surfaces and the fact that they are all straight lines on the walls.

Hello dubois,

In these images, yes, but in reality, few layers are flat like that. There’s variation, as here:

So the objective is to determine what layers are visible at surface. Pavel’s solution above works for a situation where all layers are unvarying in thickness.

It’s a head scratcher for sure.

BN

I don’t think that this is possible, you might want to create a 3d texture and then just sample it from your topography or some slice/isosurface. But keep in mind this last image is vastly different than the ones above.

1 Like

Well noted, dubois. Indeed, this is the classic difference between theory and practice!

BN

Here is a different approach, but you would need more data (this is what I meant by revised algorithm). Imagine stacked layers with variable thickness. And then the actual ground surface cuts through these layers. So for every pixels that you draw, you have a “column” of corresponding intersections with the layers, so it is easy to find in which layer the pixel is, and thus set its color.

https://codepen.io/boytchev/full/yyaewxa

image

4 Likes