View Full Version : Lscript mapRange()

09-01-2011, 09:52 PM
Can someone tell me what the output of the mapRange function should be given the following params...

mapRange(([Camera.Position.Z]<0?0:[Camera.Position.Z]), 0, 2000, 1, 0)

Shouldn't I be getting a value of 1 whenever the camera is in -Z territory and after crossing the Z axis into positive territory a value decreasing to 0 when it reaches 2000 m +Z, and after which remain at 0 as the camera moves farther on +Z?

Such is NOT being displayed in the graph editor (dissolve env) as a dotted line (modified channels enabled). The graph shows the conditional expression is working, but with a value being "clamped" at something like 150% instead of 100. Then as the camera moves past 2000m on +Z, the graph dips to around -50% (or even further if I move the camera farther past 2000m), instead of being clamped at 0%.

If I remove mapRange and just use the raw Camera.Position.Z, then the graph goes WAY neg and WAY pos, into the 10's of thousands of percent, so the function is remapping, just not as I expect. So how does mapRange work? What do the min/max output params do?

I'm basically trying (unsuccessfully) to make a reverse distance dissolve effect (opposite that in the object properties), so that an object dissolves IN the further away the camera is, but ONLY in the positive Z direction.

I've also tried unsuccessfully to somehow MIX Camera.Position.X in to the expression so that if the camera is too close to the object on +Z to be fully dissolved in, that if the camera is within range of X=0, then fully dissolve it in anyway, overriding the Z position effect, BUT only when the camera is in +Z territory.

Yeah, yeah... I know... I could so very easily just keyframe this object's dissolve manually, but I want to learn Lscript, too, so any assistance you can offer in crafting this one liner expression would be helpful in that education.


09-02-2011, 02:15 AM
So you're trying to mix a conditional less than with a map range... Ok no problem... should work fine.

I think your notation (specifically the precise placement of your brackets) may be the problem... but I cant be checking now before the morning coff' has worked.

Otherwise, you could use 2 expressions... one that does a map range function, and a second that refernces the first...

If <0, then 0 else maprange expression... sorta thing.

If the clamping doesnt seem to be right, you can of wrap the whole thing in a clamp() too to enforce specific clamp values.

09-02-2011, 08:00 AM
The right and left brackets around the camera Z channel was what Lscript builder wrote for me. The parenths around them were left over from a division calculation to reduce the slope, which didn't do that, it shifted the graph in time. They shouldn't affect anything.

To me it looks like mapRange() is a scaling function with no clamping ability, thus I have no idea what the last two output terms mean in relation to the two preceding range terms. Obviously it's not a direct mapping of input values to output values, because if input = 2000m, second range term = 2000, and second output term = 0 then the value returned is not 0m or 0% when applied to a percent graph, it goes way negative. So, what do the output term values represent and what do they do to remap the input?

Clamping the output of mapRange() will just clip the peaks and valleys, ignoring the slope. The slope of the graph is of vital importance. What I want to do is preserve the peaks/valleys in time (slopes thus proportional to high/low range).

I've tweaked the output range terms to various decimal values and can get a mapping that sort of fits between 1 and 0 (100%, 0%) in the graph editor, but again, I have no idea what those numbers mean or why they are changing the graph the way they do.


09-02-2011, 10:15 AM
This a graphical depiction of what I want to happen. Possible with a single line expression? If yes, HOW?

09-02-2011, 06:24 PM
Stumped the best of ya, hmmm...? Well, not to worry, I'm pretty sure I solved the "problem" and have pretty much an automated "inverse distance dissolve" that is working great to reveal the "air glow" effect around back lit planets.

First the scene setup.

a 1km diameter gas giant planet modeled and positioned at 0,0,0 with an incident angle gradient "feathered" edge.
a 1.02km diameter atmosphere mapped with the same image map and gradient edge plus a Z Distant from object (itself) gradient to make large feathered holes on the equator "pointed" at the sun. This so that the full arc of the luminous edge is not seen going around the entire dark side of the planet, but fades out at the equator. Not until you are directly behind the planet will you see the full circular arc.
a point light -10km or more on Z.

Here's the expression applied to the atmosphere's dissolve envelope and it's break down. First in simple terms, where Camera.Position.Z and .X are shortened to C.P.Z and C.P.X:

if C.P.Z < 0 then dissolve = 100%
/* you don't want the luminous atmosphere object to be visible when viewing the front-lit side of the planet */


if C.P.Z < 4000m then dissolve = (value derived from C.P.Z) * (value derived from absolute value of C.P.X)
/*you want the atmosphere to gradually appear as you swing around to the back side, influenced by your X side-to-side distance. That is, when X = 0 you want the atmosphere to be fully visible, 0% dissolved, because the sun is shining thru the full circular rim of the atmosphere. The further away from X=0, the more dissolved it is until at some point on either +X or -X it will be 100% dissolved, but maybe not depending on how far way you are on +Z */


dissolve = value derived from C.P.X.
/* beyond Z = 4000 the atmosphere dissolve is based only on X position. */

The full expression:

[Camera.Position.Z]<0?1:[Camera.Position.Z]<4000?clamp(mapRange((clamp(mapRange([Camera.Position.Z],0,5000,1,0), 0,1))*(clamp(mapRange(abs([Camera.Position.X]),0,2100,0,1),0,1)), 0,.9,0,1), 0,1):clamp(mapRange(abs([Camera.Position.X]), 0,2000,0,0.15), 0,1)

/* the If C.P.Z < 0 part */

/* the 'else if' part to test against... */

clamp(mapRange((clamp(mapRange([Camera.Position.Z],0,5000,1,0), 0,1))
/* the Z position dissolve amount, derived by first remapping (scaling) down Z to a 1 to 0 output based on a 0 to 5000 meter range, then clamped to 0 min and 1 max. The result is then multiplied by... */

(clamp(mapRange(abs([Camera.Position.X]),0,2100,0,1),0,1)), 0,.9,0,1), 0,1)
/* ... the X position dissolve amount, derived by first remapping (scaling) down absolute value of X to a 1 to 0 output based on a 0 to 2000 meter range, then clamped to 0 min and 1 max. */

Because the multiplication of the two terms above should result in a maximum of 1 (1 * 1 = 1, 1 * 0 = 0, etc) the range terms of the "else if clamp(mapRange(...)...)" was set to 0 and 1. Thinking about it more, the clamp() and mapRange() probably aren't needed as the range should already be 0 to 1.

And lastly, the second 'else if' where when Z is greater than 5000 the dissolve will be the X distance derived value of...

:clamp(mapRange(abs([Camera.Position.X]), 0,2000,0,0.15), 0,1)

Notice the tweaks for X mapRange() to get a smoother transition between the > 4000 cross-over point and to shape the X curve a bit.

Anyway, a challenging experiment in lscript expressions, for what it's worth.