anti-aliased pixel rendering - c++

I'm trying to implement anti-aliased pixel rendering. My basic idea is to render 4 pixels instead of 1, and give each "real" pixel a weight based on its distance to the "virtual" pixel:
void put_aa_pixel(double x, double y)
{
int x0 = int(x);
int x1 = x0 + 1;
int y0 = int(y);
int y1 = y0 + 1;
double weight_x1 = x - x0;
double weight_x0 = 1 - weight_x1;
double weight_y1 = y - y0;
double weight_y0 = 1 - weight_x1;
put_pixel(x0, y0, int((weight_x0 * weight_y0) * 255));
put_pixel(x1, y0, int((weight_x1 * weight_y0) * 255));
put_pixel(x0, y1, int((weight_x0 * weight_y1) * 255));
put_pixel(x1, y1, int((weight_x1 * weight_y1) * 255));
}
Multiplying the x and y weights gives me the overlapping area of the virtual pixel inside each real pixel. I naively assumed this would give me a perfect anti-aliasing effect, but the moving pixels inside my test program just display an aweful flicker. It looks much worse then simple pixels without any anti-aliasing.
However, when I switch from multiplication to addition, it looks much better:
put_pixel(x0, y0, int((weight_x0 + weight_y0) * 127.5));
put_pixel(x1, y0, int((weight_x1 + weight_y0) * 127.5));
put_pixel(x0, y1, int((weight_x0 + weight_y1) * 127.5));
put_pixel(x1, y1, int((weight_x1 + weight_y1) * 127.5));
Adding the weights doesn't seem to have any geometric significance. So why does this work better? What's wrong with the first version? And is there an even better approach?

As requested :)
Intuitively: your x and y weights express distance along axis from virtual to real pixel. So, the actual distance is sqrt(w_x^2 + w_y^2). Explains why sum works better - it's way closer to this form than multiplication.

There was a bug lurking in my code for half a year:
double weight_x1 = x - x0;
double weight_x0 = 1 - weight_x1;
double weight_y1 = y - y0;
double weight_y0 = 1 - weight_x1; // BUG
Can you see the bug? Yes, it's a classic copy and paste error:
double weight_y0 = 1 - weight_y1; // FIXED
After fixing the bug, the original multiplication approach looks very nice.

Related

Determine Next Point on a Line Between Point A and B From A

I have a 2D Coordinate system where The Origin Starts at the top Left
(Y Is higher as I move downward)
I am Given Two Points in Space, Lets Say Point A, and Point B.
How can I determine that next Point on the line From Point A to Point B?
For example, I have Point A(10, 10) and Point B (1,1)
I know the point I'm looking for is (9,9).
But how do I do this mathematically?
For say a more complicated Set of points
A(731, 911) and B(200, 1298)
I'm trying to move my mouse, one pixel at a time from its current location to a new one.
This doesn't work, but honestly I'm stumped where to begin.
int rise = x2 - 460; //(460 is Point A x)
int run = y2 - 360;//(360 is Point A Y)
float slope = rise / run;
int newx = x1 + ((slope / slope) * 1); //x1 Is my current mouse POS x
int newy = y1 + (slope * -1);//y1 is my current mouse Pos y
It almost works but seems inverted, and wrong.
You already have the slope, so to get the next point on the line (there are infinitely many), you have to choose a step value or just arbitrarily pick one of the points.
Given A(y1, x1), your goal in finding a new point, B(y2, x2) is that it must satisfy the equation: (y2 - y1) / (x2 - x1) = slope.
To simplify, (x2 - x1) * slope = y2 - y1
You already have x1, slope, y1, and you can choose any arbitrary x2, so when you plug all those into the equation, you can simplify it further to:
y2 = (x2 - x1) * slope + y1
To illustrate this with your other points (A(731, 911) and C(200, 1298)) and say you want to find a new point B, we can proceed as follows:
Find the slope first:
float slope = (1298 - 911) / (200 - 731); // -0.728813559322
Choose x and solve for y:
x1 = 731, slope = -0.728813559322, y1 = 911
Choose x2 = 500 and solving for y2, we get:
float y2 = (500 - 731) * -0.728813559322 + 911; // 1079.355932203382
So your new point is:
B(500, 1079.355932203382)
You can verify this new point still has the same slope to point C
With A = (x1,y1) and B = (x2,y2) the line is (expressed in two same equations):
(1) y = (x-x1)*(y2-y1)/(x2-x1) + y1
(2) x = (y-y1)*(x2-x1)/(y2-y1) + x1
To find next point, put x1+1 (or x1-1 you know) in equation (1) and find y and also put y1+1 or y1-1 in equation (2) and find x.
You can decide which one is better choice. Take care of vertical or horizontal lines, where one of the equations won't work.
NOTE: do not cast floating point result to int. Do round instead.

C++/SDL2 -- Rendering A Circle [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Am I on the right track here, drawing a filled circle with SDL2? I figured using parametric equations and a radius tending to zero would work, but it seems really inefficient in terms of processor use. Any other ideas are much appreciated Thanks in advance.
//Circle test
int circle_x = WINDOW_WIDTH/2;
int circle_y = WINDOW_HEIGHT/2;
int circle_radius = 100;
SDL_SetRenderDrawColor(window.renderer, 100, 100, 255, 255);
int point_x;
int point_y;
while (circle_radius > 0)
{
for (int t = 0; t < 360; t++)
{
point_x = circle_x + circle_radius * cos(t);
point_y = circle_y + circle_radius * sin(t);
SDL_RenderDrawPoint(window.renderer, point_x, point_y);
}
circle_radius--;
}
Output Img
First of all, there's some mistake in your code, as the sin() and cos() functions are radian and not degree based functions. This will make your circle to appear at pseudorandomly selected points as each step draws a point about 57 degrees apart from the previous. This will have no impact as modern graphics work buffered and you'll see the final result, not the workings.
Once this said, nobody draws a circle today by using your exposed algorithm. Have a look at the Bresenham's middle point algorithm, that basically tries to draw a circle by octants, but several times faster.
The idea behind these algorithms is to consider the R^2 = x^2 + y^2 formula and to draw, pixel by pixel, on one of the axis, and considering when the other axis must be followed (you draw by octants, as this way you don't cope with greater than one derivatives and you have only to decide if you move up or not). The routine also takes into account the circle symmetry to calculate only one octant and then draw the eight points at each pass.
As I developed that algorithm from scratch when I was young (without having seen Bresenham's before) probably my reasoning up to the solution will be of help to you.
The first attempt is to take into account that the resolution (the granularity is not angle dependant) for small circles you have to paint less pixels than for big ones, and the one degree approach you have followed has to be redesigned to adapt to finer or coarse resolutions. The idea is to go, pixel by pixel, instead of degree by degree, until you draw the complete thing. We are going to paint only the first octant, and will draw the rest by the symmetry properties of the figure. We part from the (0, -R) point and will go, pixel by pixel until we get to the (sqrt(2)*R, R - sqrt(2)*R) point.
The first thing we are going to do is to try to save all operations we have to do. The first place where we can save operations is in calculating squares... We are going to use the R^2 = x^2 + y^2 equation and on it, R is only used as R^2 all the time, so, lets suppose we want to draw a ten pixels radius circle, we will square things up to 100 (which is the 10 pixels radius squared).
Next, we are going to see one property of squares, that is, they grow odd from one square to the next (0 -> 1(delta is 1) -> 4(delta is 3) -> 9(delta is 5) -> 16(delta is 7) ...) so if we can arrange to grow by 1 in the x, we can calculate x^2 easily, by just adding two to the odd variable, and then adding odd to the last square number, so we'll use two numbers: x and x2. We initialize both to 0, and the first grows by x += 1;, while the second grows by the relationship x2 += dx2; dx2 += 2; (we initialize dx2 = 1;) this makes us to allow x and x2 growing by only making sums, and no multiplications at all.
If someone thinks we are going to need y2 = 100 - x2 and then whe are forced to calculate y = sqrt(y2) you are almost right, but the trick here is to be able to manage the y and y2 sequences backwards the same as the x counterparts. Well, right, y and y2 can be managed in the reverse direction the same as x and x2 but this time we have to go backwards, decreasing odd numbers from (what?) to 1 where dy2 -= 2; y2 -= dy2; and finally reaches 0. For this to happen, check that the difference between two consecutive squares is precisely the two numbers added, so, for example, the difference between 13^2 = 169 and 14^2 = 196 is 13 + 14 = 27, and this is the odd number to begin with if we go back from R = 14 to 0 in y.
The reason of complicating things so much, is that this way we only make additions with integers and no need to make multiplications (well, multiplications are not so expensive, but there was a time that they were) Well, we do multiplications to square initially the radius R, but we do it only once at the beginning to calculate R^2.
The idea now is to set the origin at the departure point (0, -R) and go to the right, pixel by pixel, adding (and modifiying) x, x2, and sum (we substract to sum all the time) until we reach the next square in y, and then update all the y axis values (we do decrement the y, we have to move a pixel up in that moment) y, y2, dy2, and draw the pixel (or draw it before, as we do in the routine), until... what? (well, the point is until we meet at the 45 degrees point in which the octant is complete, x and y coordinates are equal) It is important to stop there, because from that point on, it is possible that one step makes the y coordinate to increment more than one pixel (the derivative is greater than one) and this should complicate the overal algorithm (and we are anyway painting the other symmetrical eight points, so we are drawing the other part of the graphic)
So, suppose we have 100 as radius, and begin with:
x=0, x2= 0, dx2= 1, y=10, y2=100, dy2=19, sum=100 *
x=1, x2= 1, dx2= 3, y= 9, y2= 81, dy2=17, sum= 99
x=2, x2= 4, dx2= 5, y= 9, y2= 81, dy2=17, sum= 96
x=3, x2= 9, dx2= 7, y= 9, y2= 81, dy2=17, sum= 91
x=4, x2=16, dx2= 9, y= 9, y2= 81, dy2=17, sum= 84 *
x=5, x2=25, dx2=11, y= 8, y2= 64, dy2=15, sum= 75 *
x=6, x2=36, dx2=13, y= 7, y2= 49, dy2=13, sum= 64 *
x=7, x2=49, dx2=15, y= 7, y2= 49, dy2=13, sum= 51
The point marked with asterisk are the ones where the sum value crosses the next y2 value, making the y value to be decrmented and having to shift the pixel we paint on. The final routine is:
int bh_onlyonepoint(int r, int cx, int cy)
{
int r2 = r*r;
int x = 0, x2 = 0, dx2 = 1;
int y = r, y2 = y*y, dy2 = 2*y - 1;
int sum = r2;
while(x <= y) {
draw(cx + x, cy + y); /* draw the point, see below */
sum -= dx2;
x2 += dx2;
x++;
dx2 += 2;
if (sum <= y2) {
y--; y2 -= dy2; dy2 -= 2;
}
} /* while */
return x; /* number of points drawn */
}
If you want, I have written a simple example to draw a circle in ascii on the screen, given radius as command parameter. It uses ANSI escape sequences to position the cursor in the screen before drawing a single asterisk. The scale is doubled in the X direction to compensate for the character height ("pixels" in ascii are not square) I have included a new drawing function pointer parameter to call back for point drawing and a main routine to get parameters from the command line:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void draw(int x, int y)
{
/* move to position (2*x, y) and plot an asterisk */
printf("\033[%d;%dH*", y, x<<1);
}
int bh(int r, int cx, int cy, void(*draw)(int, int))
{
/* the variables mentioned in the text */
int r2 = r*r;
int x = 0, x2 = 0, dx2 = 1;
int y = r, y2 = y*y, dy2 = 2*y - 1;
int sum = r2;
while(x <= y) {
/* draw the eight points */
draw(cx + x, cy + y);
draw(cx + x, cy - y);
draw(cx - x, cy + y);
draw(cx - x, cy - y);
draw(cx + y, cy + x);
draw(cx + y, cy - x);
draw(cx - y, cy + x);
draw(cx - y, cy - x);
sum -= dx2;
x2 += dx2;
x++;
dx2 += 2;
if (sum <= y2) {
y--; y2 -= dy2; dy2 -= 2;
}
} /* while */
return x; /* number of points drawn */
}
int main(int argc, char **argv)
{
int i;
char *cols = getenv("COLUMNS");
char *lines = getenv("LINES");
int cx, cy;
if (!cols) cols = "80";
if (!lines) lines = "24";
cx = atoi(cols)/4;
cy = atoi(lines)/2;
printf("\033[2J"); /* erase screen */
for (i = 1; i < argc; i++) {
bh(atoi(argv[i]), cx, cy, draw);
}
fflush(stdout);
sleep(10);
puts(""); /* force a new line */
}
And the final result is:
*
* * * * * * * * * *
* * * *
* *
* * * *
* * *
* * * * * * * * * *
* * * *
* * * * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * *
* * * * * *
* * * *
* * * * * * * * * *
* * *
* * * *
* *
* * * *
* * * * * * * * * *
*
Finally, if you want better results (not those peaks that arise from an exact value of radius that make them only touch the point when x or y are zero) you can pass the routine directly the square value of the radius (that allows to make integer calculations with fractional radii)
filling a circle
If you want to fill the circle, just paint all points between a pair of the calculated points, as in:
lineFromTo(cx - x, cy - y, cx + x, cy - y);
lineFromTo(cx - y, cy + x, cx + y, cy + x);
lineFromTo(cx - y, cy - x, cx + y, cy - x);
lineFromTo(cx - x, cy + y, cx + x, cy + y);
These are all horizontal lines, so perhaps you can get an improvement with something like:
/* X1 X2 Y */
HorizLineX1X2Y(cx - x, cx + x, cy - y);
HorizLineX1X2Y(cx - y, cx + y, cy + x);
HorizLineX1X2Y(cx - y, cx + y, cy - x);
HorizLineX1X2Y(cx - x, cx + x, cy + y);
A new git repository with the final program allowing to fill, draw or trace the run of the algorithm has been created in github

Perlin Noise algorithm does not seem to produce gradient noise

I am attempting to implement Perlin Noise in c++.
Firstly, the problem (I think) is that the output is not what I expect. Currently I simply use the generated Perlin Noise values in a greyscaled image, and this is the results I get:
However, from my understanding, it's supposed to look more along the lines of:
That is, the noise I am producing currently seems to be more along the lines of "standard" irregular noise.
This is the Perlin Noise Algorithm I have implemented so far:
float perlinNoise2D(float x, float y)
{
// Find grid cell coordinates
int x0 = (x > 0.0f ? static_cast<int>(x) : (static_cast<int>(x) - 1));
int x1 = x0 + 1;
int y0 = (y > 0.0f ? static_cast<int>(y) : (static_cast<int>(y) - 1));
int y1 = y0 + 1;
float s = calculateInfluence(x0, y0, x, y);
float t = calculateInfluence(x1, y0, x, y);
float u = calculateInfluence(x0, y1, x, y);
float v = calculateInfluence(x1, y1, x, y);
// Local position in the grid cell
float localPosX = 3 * ((x - (float)x0) * (x - (float)x0)) - 2 * ((x - (float)x0) * (x - (float)x0) * (x - (float)x0));
float localPosY = 3 * ((y - (float)y0) * (y - (float)y0)) - 2 * ((y - (float)y0) * (y - (float)y0) * (y - (float)y0));
float a = s + localPosX * (t - s);
float b = u + localPosX * (v - u);
return lerp(a, b, localPosY);
}
The function calculateInfluence has the job of generating the random gradient vector and distance vector for one of the corner points of the current grid cell and returning the dot product of these. It is implemented as:
float calculateInfluence(int xGrid, int yGrid, float x, float y)
{
// Calculate gradient vector
float gradientXComponent = dist(rdEngine);
float gradientYComponent = dist(rdEngine);
// Normalize gradient vector
float magnitude = sqrt( pow(gradientXComponent, 2) + pow(gradientYComponent, 2) );
gradientXComponent = gradientXComponent / magnitude;
gradientYComponent = gradientYComponent / magnitude;
magnitude = sqrt(pow(gradientXComponent, 2) + pow(gradientYComponent, 2));
// Calculate distance vectors
float dx = x - (float)xGrid;
float dy = y - (float)yGrid;
// Compute dot product
return (dx * gradientXComponent + dy * gradientYComponent);
}
Here, dist is a random number generator from C++11:
std::mt19937 rdEngine(1);
std::normal_distribution<float> dist(0.0f, 1.0f);
And lerp is simply implemented as:
float lerp(float v0, float v1, float t)
{
return ( 1.0f - t ) * v0 + t * v1;
}
To implement the algorithm, I primarily made use of the following two resources:
Perlin Noise FAQ
Perlin Noise Pseudo Code
It's difficult for me to pinpoint exactly where I seem to be messing up. It could be that I am generating the gradient vectors incorrectly, as I'm not quite sure what type of distribution they should have. I have tried with a uniform distribution, however this seemed to generate repeating patterns in the texture!
Likewise, it could be that I am averaging the influence values incorrectly. It has been a bit difficult to discern exactly how it should be done from from the Perlin Noise FAQ article.
Does anyone have any hints as to what might be wrong with the code? :)
It seems like you are only generating a single octave of Perlin Noise. To get a result like the one shown, you need to generate multiple octaves and add them together. In a series of octaves, each octave should have a grid cell size double that of the last.
To generate multi-octave noise, use something similar to this:
float multiOctavePerlinNoise2D(float x, float y, int octaves)
{
float v = 0.0f;
float scale = 1.0f;
float weight = 1.0f;
float weightTotal = 0.0f;
for(int i = 0; i < octaves; i++)
{
v += perlinNoise2D(x * scale, y * scale) * weight;
weightTotal += weight;
// "ever-increasing frequencies and ever-decreasing amplitudes"
// (or conversely decreasing freqs and increasing amplitudes)
scale *= 0.5f;
weight *= 2.0f;
}
return v / weightTotal;
}
For extra randomness you could use a differently seeded random generator for each octave. Also, the weights given to each octave can be varied to adjust the aesthetic quality of the noise. If the weight variable is not adjusted each iteration, then the example above is "pink noise" (each doubling of frequency carries the same weight).
Also, you need to use a random number generator that returns the same value each time for a given xGrid, yGrid pair.

Speed of quadratic curve

I'm writing a 2d game and I have birds in a camera-space. I want to make them fly. So, I generate 3 ~random points. First one is left-upper side, second: middle-bottom, third: right-upper.
As a result I have 180deg rotated triangle.
To move a bird through the curve's path I have a t-parameter which is increased in each frame (render loop) by some delta.
The problem is that in different curves birds have different speed. If the triangle is "wide" (1) they are more slowly, if it's stretched by Y-coordinate (2), the speed is very fast.
But I want to make speed equal at different curves. It's logically, that I have to change delta which is appended each frame for each curve.
I've tried to solve it like this:
Find the ~length of the curve by summing length of 2 vectors: P1P2 and P2P3.
Than I've defined the speed for 1 virtual meter per frame. A little pseudocode:
float pixelsInMeter = 92.f; // One virtual meter equals to this number of pixels
float length = len(P1P2) + len(P2P3)
float speed = 0.0003f; // m/frame
// (length * speed) / etalon_length
float speedForTheCurve = toPixels( (toMeters(length) * speed) / 1.f);
// ...
// Each frame code:
t += speedForTheCurve;
Vector2 newPos = BezierQuadratic(t, P1, P2, P3);
But birds anyway have different speed. What's wrong? Or maybe there is a better way.
The Bezier function you're using is a parametrized function with bounds [0...1]. You're mucking with the step-size, which is why you're getting crazy speeds. Generally speaking, the distance d is the dependent variable in the equation, which says to me that their speeds will be different based on the length of the curve.
Since speed is your dependent variable, we're going to vectorize your function by computing the step-size.
Check out this pseudocode:
P1 = (x1, y1)
P2 = (x2, y2)
P3 = (x3, y3)
int vec[100][2]
int getPoint(int p1, int p2, float stepSize) {
return p1 + (p2 - p1)*stepSize;
}
for (float i = 0.0; i < 1.0; i += 0.01 ) {
int newX = getPoint(getPoint(x1, x2, i), getPoint(x2, x3, i), i);
int newY = getPoint(getPoint(y1, y2, i), getPoint(y2, y3, i), i);
vec[iter++][0] = newX;
vec[iter][1] = newY;
}
You can get the delta values by performing a first difference but I don't think that's necessary. As long as you move all the birds the appropriate distance based on the step iteration they will all move different distances but they will start and end their trajectories identically.
From your equation, we can compute the pixel delta step size:
int pixelsToMove = toMeter(sqrt((x2 - x1)^2 + (y2 - y1)^2))/pixelsInMeter;
Which will give you the appropriate amount of pixels to move the bird. That way they'll all move different step sizes, but their speeds will be different. Does that make sense?
Or, try something like this (much harder):
Obtain the actual quadratic function of the three points you chose.
Integrate the quadratic between two xy rectangular coordinate
Convert computed length into pixels or whatever you're using
Obtain dependent variable speed so all curves finish at the same time.
Let's start with quadratic stuff:
y = Ax^2 + Bx + C where A != 0, so since you have three points, you will need three equations. Using algebra, you can solve for the contants:
A = (y3 - y2)/((x3 - x2)(x3 - x1)) - (y1 - y2)/((x1 - x2)(x3 - x1))
B = (y1 - y2 + A(x2^2 - x1^2))/(x1 - x2)
C = y1 - Ax1^2 - Bx1
Then you can use the formula above to obtain a closed-form arc length. Check this website out, wolfram will integrate it for you and you just have to type it:
Closed form solution for quadradic integration
Now that you've computed the arc length, convert actualArcLength to the speed or whatever unit you're using:
float speedForTheCurve = toPixels( (toMeters(actualArcLength) * speed) / 1.f);

Finding the distance between 2 3D points

I'm running into a problem where my square of X is always becoming infinite leading to the resulting distance also being infinite, however I can't see anything wrong with my own maths:
// Claculate distance
xSqr = (x1 - x2) * (x1 - x2);
ySqr = (y1 - y2) * (y1 - y2);
zSqr = (z1 - z2) * (z1 - z2);
double mySqr = xSqr + ySqr + zSqr;
double myDistance = sqrt(mySqr);
When I run my program I get user input for each of the co-ordinates and then display the distance after I have run the calulation.
If your inputs are single-precision float, then you should be fine if you force double-precision arithmetic:
xSqr = double(x1 - x2) * (x1 - x2);
// ^^^^^^
If the inputs are already double-precision, and you don't have a larger floating-point type available, then you'll need to rearrange the Euclidean distance calculation to avoid overflow:
r = sqrt(x^2 + y^2 + z^2)
= abs(x) * sqrt(1 + (y/x)^2 + (z/x)^2)
where x is the largest of the three coordinate distances.
In code, that might look something like:
double d[] = {abs(x1-x2), abs(y1-y2), abs(z1-z2)};
if (d[0] < d[1]) swap(d[0],d[1]);
if (d[0] < d[2]) swap(d[0],d[2]);
double distance = d[0] * sqrt(1.0 + d[1]/d[0] + d[2]/d[0]);
or alternatively, use hypot, which uses similar techniques to avoid overflow:
double distance = hypot(hypot(x1-x2,y1-y2),z1-z2);
although this may not be available in pre-2011 C++ libraries.
Try this:
long double myDistance=sqrt(pow(x1-x2,2.0)+pow(y1-y2,2.0)+pow(z1-z2,2.0));
I figured out what was going on, I had copied and pasted the code for setting x1, y1 and z1 and forgot to change it to x2 y2 and z2, It's always the silliest of things with me :P thanks for the help anyway guys