How to compute the derivative of multi octave perlin noise - graphics

I have implemented Perlin Noise (3d) that will not just yield me a field value, but also an analytical derivative. For the analytical derivative, I followed the scratchapixel guide.
This is all fine, but I do not know how to treat the derivative if I have multiple octaves.
For the field value, the octave summing is very straightforward:
Assume I have 4 octaves of noise with persistence 0.5 and lacunarity 2, I would simply do:
float sample_4o( vec3 samplepos )
{
const float amp0 = 1;
const float amp1 = 0.5f;
const float amp2 = 0.25f;
const float amp3 = 0.125f;
const float freq0 = 1;
const float freq1 = 2;
const float freq2 = 4;
const float freq3 = 8;
const float v0 = perlin( freq0 * samplepos );
const float v1 = perlin( freq1 * samplepos );
const float v2 = perlin( freq2 * samplepos );
const float v3 = perlin( freq3 * samplepos );
const float maxval = (amp0+amp1+amp2+amp3);
return ( 1 / maxval ) * ( amp0 * v0 + amp1 * v1 + amp2 * v2 + amp3 * v3 );
}
It's nothing more than a weighted sum of the octaves. Easy peasy.
But now that I have not just four noise values, but also four derivatives (or gradients), how would I combine the four 3d gradients g0,g1,g2,g3 using the amplitudes amp0,amp1,amp2,amp3?
To visualize the example...
I have two signals, the red and the orange.
I know the derivatives for the red and orange signal.
I scale up the freq of orange, to get the dashed-orange signal.
I then scale down the amplitude of dashed-orange, to get the dotted-orange signal.
I add dotted-orange to red, to obtain the 2-octave signal in blue.
How is the derivative of the blue signal expressed in the derivatives of orange and red?

If your gradients are correct, then you just sum them with the same same weights that you use to sum the amplitudes.
Note, however, that gradients are proportional to frequency, so if you multiply the frequency by 2, then the gradients must also end up multiplied by 2.
In your example, the weights are inversely proportional to frequency, so if you have NOT corrected the gradients for frequency already, then just sum them without any weighting at all, because the weight*frequency correction comes out to 1 for all of them.

Related

Can I do random writes from a kernel without worrying about synchronization issues?

Consider a simple depth-of-field filter (my actual use case is similar). It loops over the image and scatters every pixel over a circular neighborhood of its. The radius of the neighborhood depends on the depth of the pixel - the closer the it is to the focal plane, the smaller the radius.
Note that I said "scatters" and not "gathers". In simpler image processing applications, you normally use the "gather" technique to perform an uniform Gaussian blur. IOW, you loop over the neighborhood of each pixel, and "gather" the nearby values into a weighted average. This works fine in that case, but if you make the blur kernel vary between pixels, while still using "gathering", you'll get a somewhat unrealistic effect. Such "space-variant filtering" scenarios are where "scattering" is different from "gathering".
To be clear: the scatter algo is something like this:
init resultImage to black
loop over sourceImage
var c = fetch current pixel from sourceImage
var toAdd = c * weight // weight < 1
loop over circular neighbourhood of current sourcepixel
add toAdd to current neighbor from resultImage
My question is: if I do a direct translation of this pseudocode to OpenCL, will there be synchronization issues due to different work-items simultaneously writing to the same output pixel?
Does the answer vary depending on whether I'm using Buffers or Images?
The course I'm reading suggests that there will be synchronization issues. But OTOH I read the source of Mandelbulber 1.21-2, which does a straightforward OpenCL DOF just like my above pseudocode, and it seems to work fine.
(the relevant code is in mandelbulber-opencl-1.21-2.orig/usr/share/cl/cl_DOF.cl and it's as follows)
//*********************************************************
// MANDELBULBER
// kernel for DOF effect
//
//
// author: Krzysztof Marczak
// contact: buddhi1980#gmail.com
// licence: GNU GPL v3.0
//
//*********************************************************
typedef struct
{
int width;
int height;
float focus;
float radius;
} sParamsDOF;
typedef struct
{
float z;
int i;
} sSortZ;
//------------------ MAIN RENDER FUNCTION --------------------
kernel void DOF(__global ushort4 *in_image, __global ushort4 *out_image, __global sSortZ *zBuffer, sParamsDOF p)
{
const unsigned int i = get_global_id(0);
uint index = p.height * p.width - i - 1;
int ii = zBuffer[index].i;
int2 scr = (int2){ii % p.width, ii / p.width};
float z = zBuffer[index].z;
float blur = fabs(z - p.focus) / z * p.radius;
blur = min(blur, 500.0f);
float4 center = convert_float4(in_image[scr.x + scr.y * p.width]);
float factor = blur * blur * sqrt(blur)* M_PI_F/3.0f;
int blurInt = (int)blur;
int2 scr2;
int2 start = (int2){scr.x - blurInt, scr.y - blurInt};
start = max(start, 0);
int2 end = (int2){scr.x + blurInt, scr.y + blurInt};
end = min(end, (int2){p.width - 1, p.height - 1});
for (scr2.y = start.y; scr2.y <= end.y; scr2.y++)
{
for(scr2.x = start.x; scr2.x <= end.x; scr2.x++)
{
float2 d = scr - scr2;
float r = length(d);
float op = (blur - r) / factor;
op = clamp(op, 0.0f, 1.0f);
float opN = 1.0f - op;
uint address = scr2.x + scr2.y * p.width;
float4 old = convert_float4(out_image[address]);
out_image[address] = convert_ushort4(opN * old + op * center);
}
}
}
No, you can't without worrying about synchronization. If two work items scatter to the same location without synchronization, you have a race condition and won't get the correct results. Same for both buffers and images. With buffers you could use atomics, but they can slow down your code, especially when there is contention (but even when not). AFAIK, read/write images don't have atomic operations.

Calculate signed distance between point and rectangle

I'm trying to write a function in GLSL that returns the signed distance to a rectangle. The rectangle is axis-aligned. I feel a bit stuck; I just can't wrap my head around what I need to do to make it work.
The best I came up with is this:
float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
{
// signed distances for x and y. these work fine.
float dx = max(tl.x - uv.x, uv.x - br.x);
float dy = max(tl.y - uv.y, uv.y - br.y);
dx = max(0.,dx);
dy = max(0.,dy);
return sqrt(dx*dx+dy*dy);
}
Which produces a rectangle that looks like:
The lines show distance from the rectangle. It works fine but ONLY for distances OUTSIDE the rectangle. Inside the rectangle the distance is a static 0..
How do I also get accurate distances inside the rectangle using a unified formula?
How about this...
float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
{
vec2 d = max(tl-uv, uv-br);
return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
}
Here's the result, where green marks a positive distance and red negative (code below):
Breakdown:
Get the signed distance from x and y borders. u - left and right - u are the two x axis distances. Taking the maximum of these values gives the signed distance to the closest border. Viewing d.x and d.y are shown individually in the images below.
Combine x and y:
If both values are negative, take the maximum (i.e. closest to a border). This is done with min(0.0, max(d.x, d.y)).
If only one value is positive, that's the distance we want.
If both values are positive, the closest point is a corner, in which case we want the length. This can be combined with the above case by taking the length anyway and making sure both values are positive: length(max(vec2(0.0), d)).
These two parts to the equation are mutually exclusive, i.e. only one will produce a non-zero value, and can be summed.
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
uv -= 0.5;
uv *= vec2(iResolution.x/iResolution.y,1.0);
uv += 0.5;
float d = sdAxisAlignedRect(uv, vec2(0.3), vec2(0.7));
float m = 1.0 - abs(d)/0.1;
float s = sin(d*400.0) * 0.5 + 0.5;
fragColor = vec4(s*m*(-sign(d)*0.5+0.5),s*m*(sign(d)*0.5+0.5),0,1);
}

Fastest formula to get Hue from RGB

If you are given red, green, and blue values that range from 0-255, what would be the fastest computation to get just the hue value? This formula will be used on every pixel of a 640x480 image at 30fps (9.2 million times a second) so every little bit of speed optimization helps.
I've seen other formulas but I'm not happy with how many steps they involve. I'm looking for an actual formula, not a built in library function.
Convert the RGB values to the range 0-1, this can be done by dividing the value by 255 for 8-bit color depth (r,g,b - are given values):
R = r / 255 = 0.09
G = g / 255 = 0.38
B = b / 255 = 0.46
Find the minimum and maximum values of R, G and B.
Depending on what RGB color channel is the max value. The three different formulas are:
If Red is max, then Hue = (G-B)/(max-min)
If Green is max, then Hue = 2.0 + (B-R)/(max-min)
If Blue is max, then Hue = 4.0 + (R-G)/(max-min)
The Hue value you get needs to be multiplied by 60 to convert it to degrees on the color circle. If Hue becomes negative you need to add 360 to, because a circle has 360 degrees.
Here is the full article.
In addition to Umriyaev's answer:
If only the hue is needed, it is not required to divide the 0-255 ranged colours with 255.
The result of e.x. (green - blue) / (max - min) will be the same for any range (as long as the colours are in the same range of course).
Here is the java example to get the Hue:
public int getHue(int red, int green, int blue) {
float min = Math.min(Math.min(red, green), blue);
float max = Math.max(Math.max(red, green), blue);
if (min == max) {
return 0;
}
float hue = 0f;
if (max == red) {
hue = (green - blue) / (max - min);
} else if (max == green) {
hue = 2f + (blue - red) / (max - min);
} else {
hue = 4f + (red - green) / (max - min);
}
hue = hue * 60;
if (hue < 0) hue = hue + 360;
return Math.round(hue);
}
Edit: added check if min and max are the same, since the rest of the calculation is not needed in this case, and to avoid division by 0 (see comments)
Edit: fixed java error
Probably not the fastest but this is a JavaScript function that you can try directly in the browser by clicking the "Run code snippet" button below
function rgbToHue(r, g, b) {
// convert rgb values to the range of 0-1
var h;
r /= 255, g /= 255, b /= 255;
// find min and max values out of r,g,b components
var max = Math.max(r, g, b), min = Math.min(r, g, b);
// all greyscale colors have hue of 0deg
if(max-min == 0){
return 0;
}
if(max == r){
// if red is the predominent color
h = (g-b)/(max-min);
}
else if(max == g){
// if green is the predominent color
h = 2 +(b-r)/(max-min);
}
else if(max == b){
// if blue is the predominent color
h = 4 + (r-g)/(max-min);
}
h = h*60; // find the sector of 60 degrees to which the color belongs
// https://www.pathofexile.com/forum/view-thread/1246208/page/45 - hsl color wheel
// make sure h is a positive angle on the color wheel between 0 and 360
h %= 360;
if(h < 0){
h += 360;
}
return Math.round(h);
}
let gethue = document.getElementById('gethue');
let r = document.getElementById('r');
let g = document.getElementById('g');
let b = document.getElementById('b');
r.value = Math.floor(Math.random() * 256);
g.value = Math.floor(Math.random() * 256);
b.value = Math.floor(Math.random() * 256);
gethue.addEventListener('click', function(event) {
let R = parseInt(r.value)
let G = parseInt(g.value)
let B = parseInt(b.value)
let hue = rgbToHue(R, G, B)
console.log(`Hue(${R}, ${G}, ${B}) = ${hue}`);
});
<table>
<tr><td>R = </td><td><input id="r"></td></tr>
<tr><td>G = </td><td><input id="g"></td></tr>
<tr><td>B = </td><td><input id="b"></td></tr>
<tr><td colspan="2"><input id="gethue" type="button" value="Get Hue"></td></tr>
</table>
The web page Math behind colorspace conversions, RGB-HSL covers this however it contains what I believe is an error. It states for hue calculation to divide by max-min however if you divide by this fractional amount the value increases and easily exceeds the full expected range of -1 to 5. I found multiplying by max-min to work as expected.
Instead of this:
If Red is max, then Hue = (G-B)/(max-min)
If Green is max, then Hue = 2.0 + (B-R)/(max-min)
If Blue is max, then Hue = 4.0 + (R-G)/(max-min)
I suggest this:
If Red is max, then Hue = (G-B)*(max-min)
If Green is max, then Hue = 2.0 + (B-R)*(max-min)
If Blue is max, then Hue = 4.0 + (R-G)*(max-min)
You must specify which language and platform you're using because C#, Java and C are very different languages and the performance also varies among them and among the platforms. The question is currently too broad!!!
640×480 is not very large compared to current common resolutions, but "fastest" is subjective and you need to do careful benchmarking to choose which is best for your usecase. An algorithm that looks longer with many more steps isn't necessarily slower than a shorter one because instruction cycles are not fixed and there are many other factors that affect performance such as cache coherency and branch (mis)predictions.
For the algorithm Umriyaev mentioned above, you can replace the division by 255 with a multiplication by 1.0/255, that'll improve performance with a tiny acceptable error.
But the best way will involve vectorization and parallelization in some way because modern CPUs have multiple cores and also SIMD units to accelerate math and multimedia operations like this. For example x86 has SSE/AVX/AVX-512... which can do things on 8/16/32 channels at once. Combining with multithreading, hardware acceleration, GPU compute.... it'll be far better than any answers in this question.
In C# and Java there weren't many vectorization options in the past so with older .NET and JVM versions you need to run unsafe code in C#. In Java you can run native code through JNI. But nowadays all of them also had vectorized math support. Java had a new Vector API in JEP-338. In Mono you can use the vector type in the Mono.Simd namespace. In RyuJIT there's Microsoft.Bcl.Simd. In .NET 1.6+ there's System.Numerics which includes Vector and other
... SIMD-enabled vector types, which include Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, and Quaternion.
How to use the Intel AVX in Java?
Parallelism on a Single Core - SIMD with C#
SIMD in Depth - Performance and Cost in C# and C++
Will .NET ever do intelligent SIMD?
Using System.Numerics.Vector for Graphics Programming
System.Numerics.Vectors 'Vector<T>': is it basically just System.UInt128?
Performance Gains with Data Parallelism: Using SIMD Instructions from C#
You could use one of the mathematical techniques suggested here, but instead of doing it on every pixel, do it on a random sample of ~10% of the pixels. This is still very likely to have high accuracy and will be 10x as fast.

Need Algorithm for Tie Dye Pattern

I am looking for an algorithm or help developing one for creating a tie-dye pattern in a 2-dimensional canvas. I will be using HTML Canvas (via fabric.js) or SVG and JavaScript, but I'm open to examples in any 2D graphics package, like Processing.
I would draw concentric rings of different colors, and then go around radially and offset them. Here's some pseudo-code for drawing concentric rings:
const kRingWidth = 10;
const centerX = maxX / 2;
const centerY = maxY / 2;
for (y = 0; y < maxY; y++)
{
for (x = 0; x < maxX; x++)
{
// Get the color of a concentric ring - assume rings are 10 pixels wide
deltaX = x - centerX;
deltaY = y - centerY;
distance = sqrt (deltaX * deltaX + deltaY * deltaY);
whichRing = int(distance / kRingWidth);
setPixel(x, y, myColorTable [ whichRing ]); // set the pixel based on a color look-up table
}
}
Now, to get the offsets, you can perturb the distance based on the angle of (x, y) to the x axis. I'd generate a random noise table with, say 360 entries (one per degree - you could try more or fewer to see how it looks). So after calculating the distance, try something like this:
angle = atan2(y, x); // This is arctangent of y/x - be careful when x == 0
if (angle < 0) angle += 2.0 * PI; // Make it always positive
angle = int(angle * 180 / PI); // This converts from radians to degrees and to an integer
distance += noiseTable [ angle ]; // Every pixel at this angle will get offset by the same amount.

Average color (X11 colors)

I want to fill the intersection of two(or more filled) rectangles with the average color. I have the colors of each rectangle stored as unsigned ints. How can I get the average color?
Thank you for you help!
Technically, you might be running on a color-map device, which means you need to go through X11 color management for all of this. You need to query the XColor for your two input colors, compute the average, then look up the closest representable color:
// Query XColor for both input colors
XColor xcol1, xcol2, outcol;
xcol1.pixel = color1;
xcol2.pixel = color2;
XQueryColor(display, colormap, &xcol1);
XQueryColor(display, colormap, &xcol2);
// Average red/green/blue and look up nearest representable color
outcol.red = (xcol1.red + xcol2.red) / 2;
outcol.green = (xcol1.green + xcol2.green) / 2;
outcol.blue = (xcol1.blue + xcol2.blue) / 2;
XAllocColor(display, colormap, &outcol);
// outcol.pixel is now the color to use
On a paletted device, you also need to free the color afterwards etc. - it's a mess, basically.
But in all likelihood you're on a 32-bit truecolor device, which means the integer is just a bitfield of r, g, b and a (not necessarily in that order). You can compute their average like this:
UInt out_color = 0;
for (int i=0; i < 4; i++) {
// Extract channel i from both input colors
UInt in1 = (color1 >> (i*8)) & 0xff;
UInt in2 = (color2 >> (i*8)) & 0xff;
// Compute the average and or it into the output color
out_color |= ((in1 + in2) / 2) << (i*8);
}
Color color1 = Color.FromArgb(UInt1);
Color color2 = Color.FromArgb(UInt2);
Color averageColor = Color.FromArgb(255,(color1.R + color2.R)/2,(color1.G + color2.G)/2,(color1.B + color2.B)/2);
This is assuming that you need a fully opaque average color.

Resources