Blending/smooth-shading material - c#

I'm creating and drawing a triangle mesh in wpf c# using GeometryModel3D. I've been trying to figure out how to create a smooth shading over the triangles, like the classic openGL smooth shaded triangle.
I would like to define a colour for each vertex, and then having the colours interpolated over the face, like this, assuming the three colour where red, green and blue.
I assumed I would need to use a brush, but I haven't been able to figure out how.
So any help would be appreciated, or any pointer to a guide that shows me how to achieve this.
EDIT:
I've looked at Triangular Gradient in WPF3D, which seems to answer the question partly, just using xaml.
Unfortunatly it seems like it need equilateral triangles.
2nd EDIT
The answer above, uses the RadialGradientBrush. Is the RadiusXand RadiusY used to make it elliptic instead of circular?
3rd EDIT
Okay, I'm fairly sure I can use the RadialGradientBrush. What I think I can do is, find the center of the circumcircle of the triangles, and create a RadialGradientBrush with RadiusX and RadiusY equal to the radius if the circumcircle. I would then move the focal point of the RadialGradientBrush to the vertices with GradientOrigin.
GradientOrigin takes two doubles X,Y as the center, with both of them being in the interval [0,1]. From what I can read is X = 0.0 is the left side and X = 1.0 is the right side and Y = 0.0 is the top and Y = 1.0 is the bottom. What I can't figure out, is this mapping [0,1]x[0,1] to a circle, or is it a square? The mapping from the vertices of the triangle to [0,1]x[0,1], depends on what shape this interval represents.

Have you heard of Helix 3D Toolkit for WPF ?
I didn't go as far as you'd like but I guess it is possible by looking at the Surface Demo example :

There are surely libraries for that, but to give some simple way, searching through some google,http://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
computing the distance from corners, gives info about the smooth color. Checking if point is in triangle.
float area(int x1, int y1, int x2, int y2, int x3, int y3)
{
return (float)Math.Abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0);
}
bool isInside(int x1, int y1, int x2, int y2, int x3, int y3, int x, int y)
{
/* Calculate area of triangle ABC */
float A = area(x1, y1, x2, y2, x3, y3);
/* Calculate area of triangle PBC */
float A1 = area(x, y, x2, y2, x3, y3);
/* Calculate area of triangle PAC */
float A2 = area(x1, y1, x, y, x3, y3);
/* Calculate area of triangle PAB */
float A3 = area(x1, y1, x2, y2, x, y);
/* Check if sum of A1, A2 and A3 is same as A */
return (A == A1 + A2 + A3);
}
for (int ii = 5; ii < 100; ii++)
{
for (int jj = 5; jj < 100; jj++)
{
int distanceRed =0, distanceGreen =0,distanceBlue =0;
if (isInside(30, 50, 30, 90, 20, 70, ii, jj))
{
distanceRed = (int)Math.Sqrt(((ii - 30) * (ii - 30) + (jj - 50) * (jj - 50)));
distanceGreen = (int)Math.Sqrt(((ii - 30) * (ii - 30) + (jj - 90) * (jj - 90)));
distanceBlue = (int)Math.Sqrt(((ii - 20) * (ii - 20) + (jj - 70) * (jj - 70)));
}
else
{
distanceRed = 0; distanceGreen = 0; distanceBlue = 0;
}
ptr[(((int)jj) * 3) + ((int)ii) * stride] = (byte)(distanceRed % 256);
ptr[(((int)jj) * 3) + ((int)ii) * stride + 1] = (byte)(distanceGreen % 256);
ptr[(((int)jj) * 3) + ((int)ii) * stride + 2] = (byte)(distanceBlue % 256);
}
}
gives the result:
Couldnt fit the red. Maybe the modulo is wrong here.
Also the sqrt is inefficient.

Related

Plot/Draw Circle in WindowsForms Chart

Is there any possibility to plot a circle in a WindowsForm Chart?
A method-call as follows would be really nice!
Graph.Series["circle"].Circle.Add(centerX, centerY, radius);
Well, I created myself a work around.
Maybe it helps someone
public void DrawCircle(Chart Graph, double centerX, double centerY, double radius, int amountOfEdges)
{
string name = "circle_" + centerX + centerY + radius + amountOfEdges;
// Create new data series
if (Graph.Series.IndexOf(name) == -1)
Graph.Series.Add(name);
// preferences of the line
Graph.Series[name].ChartType = SeriesChartType.Spline;
Graph.Series[name].Color = Color.FromArgb(0, 0, 0);
Graph.Series[name].BorderWidth = 1;
Graph.Series[name].IsVisibleInLegend = false;
// add line segments (first one also as last one)
for (int k = 0; k <= amountOfEdges; k++)
{
double x = centerX + radius * Math.Cos(k * 2 * Math.PI / amountOfEdges);
double y = centerY + radius * Math.Sin(k * 2 * Math.PI / amountOfEdges);
Graph.Series[name].Points.AddXY(x, y);
}
}
You can call it for example via
DrawCircle(Graph, 5, 4, 3, 30);
Around 30 points should be enough to get a nice circle instead of a polygon, but depends on the size of your chart.

Generating Sampling points on a 3D Triangle with the help of Straightforward sampling or Bresenham type sampling [duplicate]

This question already has answers here:
Algorithm to fill triangle
(3 answers)
Closed 5 years ago.
I have a triangle with 3 vertices,namely: (x1,y1,z1); (x2,y2,z2) and (x3,y3,z3).
I am using Bresenhams 3D Line algorithm,currently for generating 3D Points between two vertices of the triangle,depending on the resolution size(resSize).
void Bresenham3Algo(float x0, float y0, float z0, float x1, float y1, float z1)
{
float dx = Math.Abs(x1 - x0);
float sx = x0 < x1 ? resSize : -resSize;
float dy = Math.Abs(y1 - y0);
float sy = y0 < y1 ? resSize : -resSize;
float dz = Math.Abs(z1 - z0);
float sz = z0 < z1 ? resSize : -resSize;
float dm = Math.Max(dx, Math.Max(dy, dz)), i = dm;
x1 = y1 = z1 = dm / 2;
for (; ; )
{
Console.WriteLine(x0,y0,z0); //Printing points here
if (i <= 0) break;
x1 -= dx; if (x1 < 0) { x1 += dm; x0 += sx; }
y1 -= dy; if (y1 < 0) { y1 += dm; y0 += sy; }
z1 -= dz; if (z1 < 0) { z1 += dm; z0 += sz; }
i -= resSize;
}
}
So, As of now,I am calling the above function three times to generate 3D Sampling points on the boundary of the three Triangular edges.
Bresenham3Algo(x1,y1,z1,x2,y2,z2);
Bresenham3Algo(x2,y2,z2,x3,y3,z3);
Bresenham3Algo(x3,y3,z3,x1,y1,z1);
I am finding it difficult to find the internal sampling points lying inside the triangle.
For example,If I have the vertices (0,0,0); (5,0,0) and (3,3,0), With the help of the above function, I find 3D Points on the three triangular edges i.e.
(0,0,0),(1,0,0),(2,0,0),(3,0,0),(4,0,0),(5,0,0) -> first Edge
(3,3,0),(4,1,0),(4,2,0),(5,0,0) ->Second Edge
(0,0,0),(1,1,0),(2,2,0),(3,3,0) -> Third Edge
Now,I need to find the internal 3D Sampling points,lying inside the triangle i.e. (2,1,0) , (3,1,0), (3,2,0)
I would be glad,if someone can help me with this algo.
Thanks in Advance!
Assuming you aren't constrained to a regular grid, you do the following:
Rotate the triangle onto the x-y plane.
Draw and fill the triangle using your favourite algorithm in 2D (e.g. Bresenham).
Add z-values (all zero) to the points drawn
Rotate back to the original orientation.

How to bound a circle inside an ellipse?

The title for this post was quite hard to think of, so if you can think of a more descriptive title please tell me. Anyway, my problem is quite specific and requires some simple maths knowledge. I am writing a C# WinForms application which is a bit like the old 'xeyes' Linux application. It basically is a set of eyes which follow around your mouse cursor. This may sound easy at first, however can get rather complicated if you're a perfectionist like me :P. This is my code so far (only the paint method, that is called on an interval of 16).
int lx = 35;
int ly = 50;
int rx;
int ry;
int wx = Location.X + Width / 2;
int wy = Location.Y + Height / 2;
Rectangle bounds = Screen.FromControl(this).Bounds;
// Calculate X
float tempX = (mx - wx) / (float)(bounds.Width / 2);
// Calculate Y
float tempY = (my - wy) / (float)(bounds.Height / 2);
// Draw eyes
e.Graphics.FillEllipse(Brushes.LightGray, 10, 10, 70, 100);
e.Graphics.FillEllipse(Brushes.LightGray, 90, 10, 70, 100);
// Draw pupils (this only draws the left one)
e.Graphics.FillEllipse(Brushes.Black, lx += (int)(25 * tempX), ly += (int)(40 * tempY), 20, 20);
Now this does work at a basic level, however sometimes this can happen if the user puts the cursor at 0,0.
Now my question is how to fix this? What would the IF statement be to check where the mouse pointer is, and then reduce the pupil X depending on that?
Thanks.
Edit: This is where I get the mouse positions (my and mx):
private void timer_Tick(object sender, EventArgs e)
{
mx = Cursor.Position.X;
my = Cursor.Position.Y;
Invalidate();
}
The timer is started in the eyes_Load event and the interval is 16.
Edit 2: Final solution: http://pastebin.com/fT5HfiQR
Modelling the eyeball as the following ellipse:
Its equation is:
And that of the line joining its center and the cursor:
(don't worry about the singularity)
We can then solve to get the intersection point:
Where
Now you can calculate the distance to the eyeball's edge, by dividing the distance from the center to the cursor by sigma. What remains is just interpolating to cap the position of the pupil:
The if statement you want is then
(N.B. for math-mo's out there the above was a slight simplification, which assumes your ellipse is not too narrow; the exact solution is non-analytical)
EDIT: my tests in VB.NET:
EDIT 2: C# port
PointF Bound(double xc, double yc, double w, double h, double xm, double ym, double r)
{
double dx = xm - xc, dy = ym - yc;
if (Math.Abs(dx) > 0.001 && Math.Abs(dy) > 0.001)
{
double dx2 = dx * dx, dy2 = dy * dy;
double sig = 1.0 / Math.Sqrt(dx2 / (w * w * 0.25) + dy2 / (h * h * 0.25));
double d = Math.Sqrt(dx2 + dy2), e = d * sig;
if (d > e - r)
{
double ratio = (e - r) / d;
return new PointF((float)(xc + dx * ratio),
(float)(yc + dy * ratio));
}
}
return new PointF((float)xm, (float)ym);
}
xc, yc: Center coordinates of the ellipse
w, h: Width and height of the ellipse
xm, ym: Mouse coordinates
r: Radius of the circle you wanna constrain (the pupil)
Returns: The point where you wanna place the center of the circle
EDIT 3: Many thanks to Quinchilion for the following optimization (gawd damn this smacked me hard in the face)
PointF Bound(double xc, double yc, double w, double h, double xm, double ym, double r)
{
double x = (xm - xc) / (w - r);
double y = (ym - yc) / (h - r);
double dot = x*x + y*y;
if (dot > 1) {
double mag = 1.0 / Math.Sqrt(dot);
x *= mag; y *= mag;
}
return new PointF((float)(x * (w - r) + xc), (float)(y * (h - r) + yc));
}

Zooming in on quadratic curve line in c#

I am relatively new to c# and i am trying to draw the quadratic curve with an X and Y graph to scale with. The i drew curve although appears at the upper left corner of the screen which is very small and barely noticeable. Is there possible way to enlarge my curve line and align it to the middle so it can be shown properly?
protected override void OnPaint(PaintEventArgs e)
{
float a = 1, b = -3, c = -4;
double x1, x2, x3, y1, y2, y3, delta;
delta = (b * b) - (4 * a * c);
x1 = ((b * (-1)) + Math.Sqrt(delta)) / (2 * a);
y1 = a * (x1 * x1) + b * (x1) + c;
x2 = x1 + 1;
y2 = a * (x2 * x2) + b * (x2) + c;
x3 = x1 - 3;
y3 = a * (x3 * x3) + b * (x3) + c;
int cx1 = Convert.ToInt32(x1);
int cx2 = Convert.ToInt32(x2);
int cx3 = Convert.ToInt32(x3);
int cy1 = Convert.ToInt32(y1);
int cy2 = Convert.ToInt32(y2);
int cy3 = Convert.ToInt32(y3);
Graphics g = e.Graphics;
Pen aPen = new Pen(Color.Blue, 1);
Point point1 = new Point(cx1, cy1);
Point point2 = new Point(cx2, cy2);
Point point3 = new Point(cx3, cy3);
Point[] Points = { point1, point2, point3 };
g.DrawCurve(aPen, Points);
Yes it is possible and even rather simple to both move (Translate) and enlarge (Scale) the Graphics results by using Graphics.TranslateTransform and Matrix and Graphics.MultiplyTransform:
using System.Drawing.Drawing2D;
//..
int deltaX = 100;
int deltaY = 100;
g.TranslateTransform(deltaX, deltaY);
float factor = 2.5f;
Matrix m = new Matrix();
m.Scale(factor, factor);
g.MultiplyTransform(m);
Note that the scaling works like a lens and will enlarge the pixels. So you may want to scale down the Pen.Width when you scale up the Graphics..
Using one before..
g.DrawEllipse(Pens.Blue, 11, 11, 55, 55);
..and two after the transformations..
g.DrawEllipse(Pens.Red, 11, 11, 55, 55);
using (Pen pen = new Pen(Color.Green, 1/factor))
g.DrawEllipse(pen, 11, 11, 44, 44);
..these calls result in this image:
(I have changed the green circle's radius to avoid complete overlaying..)
It will be up to you to find the desired numbers for the moving and scaling; this will probably involve finding the minimum and maximum values for points involved..
I would suggest that you look into Microsoft Chart controls, it has a lots of interesting features regarding how to do this kind of curves with the ability to parameterize them.
A link to a more recent version of it: here

Line rasterisation: Cover all pixels, regardless of line gradient?

Basically, I want to use a line algo to determine which cells to check for collisions for my raycaster.
Bresenham isn't great for this as it uses a unified-thickness approach, meaning that it ignores cells that aren't at least half-covering the line. Not great at all, because it means that some segments of my line aren't being checked for intersections with the cells, leading to errors.
I can't seem to find any "thick-line" algorithms, can anyone help me find one?
Green: What I would like.
Red: What I currently have and don't want.
I had exactly the same problem as you and found an very simple solution. Usually, Bresenham has two consecutive if's to determine whether it should increase the coordinate for the two dimensions:
public void drawLine(int x0, int y0, int x1, int y1, char ch) {
int dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy, e2; // error value e_xy
for (;;) {
put(x0, y0, ch);
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
// horizontal step?
if (e2 > dy) {
err += dy;
x0 += sx;
}
// vertical step?
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
Now all you have to do is to insert an else before the second if:
public void drawLineNoDiagonalSteps(int x0, int y0, int x1, int y1, char ch) {
int dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy, e2;
for (;;) {
put(x0, y0, ch);
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
// EITHER horizontal OR vertical step (but not both!)
if (e2 > dy) {
err += dy;
x0 += sx;
} else if (e2 < dx) { // <--- this "else" makes the difference
err += dx;
y0 += sy;
}
}
}
Now the algorithm doesn't change both coordinates at once anymore.
I haven't thoroughly tested this but it seems to work pretty well.
This thread old, but I thought it'd be worth putting this on the Internet:
// This prints the pixels from (x, y), increasing by dx and dy.
// Based on the DDA algorithm (uses floating point calculations).
void pixelsAfter(int x, int y, int dx, int dy)
{
// Do not count pixels |dx|==|dy| diagonals twice:
int steps = Math.abs(dx) == Math.abs(dy)
? Math.abs(dx) : Math.abs(dx) + Math.abs(dy);
double xPos = x;
double yPos = y;
double incX = (dx + 0.0d) / steps;
double incY = (dy + 0.0d) / steps;
System.out.println(String.format("The pixels after (%d,%d) are:", x, y));
for(int k = 0; k < steps; k++)
{
xPos += incX;
yPos += incY;
System.out.println(String.format("A pixel (%d) after is (%d, %d)",
k + 1, (int)Math.floor(xPos), (int)Math.floor(yPos)));
}
}
Without loss of generality, assume x2 >= x1, then
int x = floor(x1);
int y = floor(y1);
double slope = (x2 - x1) / (y2 - y1);
if (y2 >= y1) {
while (y < y2) {
int r = floor(slope * (y - y1) + x1);
do {
usepixel(x, y);
++x;
} while (x < r);
usepixel(x, y);
++y;
}
}
else {
while (y > y2) {
int r = floor(slope * (y - y1) + x1);
do {
usepixel(x, y);
++x;
} while (x < r);
usepixel(x, y);
--y;
}
}
The floor calls can probably be written just as a cast-to-integer.
There is an interesting article available in GPU Gems, maybe it can help you: Chapter 22. Fast Prefiltered Lines
What about Bresenham with an additional constraint that no diagonal moves are allowed: Generate the points with the traditional algorithm, then as a post-processing step insert extra steps needed to make only orthogonal movements.
You could find all the intersections your ray has with the horizontal grid lines, and then mark all the cells on a row that either have an intersection point on one side, or are between the two cells with the intersections on the row.
Finding the intersections can be done by starting from the origin, advancing the point to the first intersection (and marking the cells in the process), finding out the vector that takes you from an intersection to the next (both these operations are basic similar triangles (or trig)) and then advancing column by column until you've gone far enough. Advancing column by column involves one vector addition per column, and a small loop to fill in the cells between the ones with intersections. Replace "mark" with "process" if you're processing the cells on the fly - this algorithm is guaranteed to mark each cell only once.
The same could be done with the vertical lines, but grids are generally stored in horizontal slices so I chose that. If you're using trig, you'll need to handle straight horizontal lines with a special case.
By the way, as far as I know, this is how old grid-based raycaster "3D" games (like Wolfenstein 3D) were done. I first read about this algorithm from this book, eons ago.

Categories