how to do Raytracing with multiple lightsources? - c#

I am doing A raytraycer with c# and I am trying to add lights. My scene has one Spherical light in front and one behind the Spheres(I haven't implemented shadows yet). However, it doesn't seem to look right when multiple light sources are turned on. Are there any mistakes in my implementation ?
Scene with two light sources turned on
Scene with one light source
The code for computing the light
protected Color Shading(Vector3 position, List<Lightsource> lightSources, Color
color, Vector3 normal, float albedo)
{
var finalColor = Color.Black;
foreach (var lightSource in lightSources)
{
var posToLightVector = lightSource.Position - position;
var lightDir = Vector3.Normalize(posToLightVector);
var lightDot = Math.Max(Vector3.Dot(lightDir,normal), 0);
var lightReflected = albedo / Math.PI;
var lightPower = lightDot * lightSource.Intensity;
var newColor = calculateColorValue(color, lightPower, lightReflected);
finalColor = AddColors(finalColor, newColor);
}
return finalColor;
}
private Color calculateColorValue(Color colorValue, float lightPower, double lightReflected)
{
var r = ((float)colorValue.R / 255) * lightPower * lightReflected;
var g = ((float)colorValue.G / 255) * lightPower * lightReflected;
var b = ((float)colorValue.B / 255) * lightPower * lightReflected;
return Color.FromArgb(Math.Min((int)(r * 255), 255), Math.Min((int)(g * 255), 255), Math.Min((int)(b * 255), 255));
}
private static Color AddColors(Color color1, Color color2)
{
return Color.FromArgb(Math.Min(255, color1.R + color2.R), Math.Min(255, color1.G + color2.G), Math.Min(255, color1.B + color2.B));
}

Related

Why isn't my perspective transform working

I am building a test 3D renderer in WinForms using the objects in System.Numerics such as Vector3 and Matrix4x4.
The object drawn is a point cloud, centered around (0,0,0), and rotated about the origin. Each node renders as dots on the screen. Here is what the 3D shape should look like
Fake Perspective
and more specifically when viewed from the front the perspective should be obvious with the blue dots that are further away from the eye to be at a smaller distance from the center
Fake Perspective
The pipeline is roughly as follows:
Rotation transformation
Matrix4x4 RY = Matrix4x4.CreateRotationY(ry);
Perspective transformation (fov=90, aspect=1.0f, near=1f, far=100f)
Matrix4x4 P = Matrix4x4.CreatePerspectiveFieldOfView(fov.Radians(), 1.0f, 1f, 100f);
Camera transformation
Matrix4x4 C = RY * P;
var node = Vector3.Transform(face.Nodes[i], C);
Project to 2D
Vector2 point = new Vector2(node.X, node.Y);
View transformation
Matrix3x2 S = Matrix3x2.CreateScale(height / scale, -height / scale);
Matrix3x2 T = Matrix3x2.CreateTranslation(width / 2f, height / 2f);
Matrix3x2 V = S*T
point = Vector2.Transform(point, V);
Pixel Coordinates & Render
PointF pixel = new PointF(point.X, point.Y);
e.Graphics.FillEllipse(brush,pixel.X - 2, pixel.Y - 2, 4, 4);
So what I am seeing is an orthographic projection.
Program Output
The blue nodes further away are not smaller as expected. Somehow the perspective transformation is being ignored.
So my question is my usage of Matrix4x4.CreatePerspectiveFieldOfView() correct in step #2? And is the projection from 3D to 2D in step #4 correct?
Steps #1, #5 and #6 seem to be working exactly as intended, my issue is with steps #2-#4 somewhere.
Example code to reproduce the issue
Form1.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Shape Object { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Object = Shape.DemoShape1();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
float width = ClientSize.Width, height = ClientSize.Height;
float scale = 40f, fov = 90f;
Matrix4x4 RY = Matrix4x4.CreateRotationY(ry);
Matrix4x4 RX = Matrix4x4.CreateRotationX(rx);
Matrix4x4 P = Matrix4x4.CreatePerspectiveFieldOfView(fov.Radians(), 1.0f, 1f, 100f);
Matrix4x4 C = RY * RX * P;
Matrix3x2 S = Matrix3x2.CreateScale(
height / scale, -height / scale);
Matrix3x2 T = Matrix3x2.CreateTranslation(
width / 2f, height / 2f);
Matrix3x2 V = S * T;
using (var pen = new Pen(Color.Black, 0))
{
var arrow = new AdjustableArrowCap(4f, 9.0f);
pen.CustomEndCap = arrow;
using (var brush = new SolidBrush(Color.Black))
{
// Draw coordinate triad (omited)
// Each face has multiple nodes with the same color
foreach (var face in Object.Faces)
{
brush.Color = face.Color;
PointF[] points = new PointF[face.Nodes.Count];
for (int i = 0; i < points.Length; i++)
{
// transform nodes into draw points
var item = Vector4.Transform(face.Nodes[i], C);
var point = Vector2.Transform(item.Project(), V);
points[i] = point.ToPoint();
}
// Draw points as dots
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
for (int i = 0; i < points.Length; i++)
{
e.Graphics.FillEllipse(brush,
points[i].X - 2, points[i].Y - 2,
4, 4);
}
}
}
}
}
}
GraphicsExtensions.cs
public static class GraphicsExtensions
{
public static PointF ToPoint(this Vector2 vector)
=> new PointF(vector.X, vector.Y);
public static Vector2 Project(this Vector3 vector)
=> new Vector2(vector.X, vector.Y);
public static Vector2 Project(this Vector4 vector)
=> new Vector2(vector.X, vector.Y);
public static float Radians(this float degrees) => (float)(Math.PI/180) * degrees;
public static float Degrees(this float radians) => (float)(180/Math.PI) * radians;
}

How can i fill part of image with color?

I have image which i add in my form.How can i fill part of image?
I have this
What I'm trying to achieve:
To floodfill an area you need a foodfill routine and very little else.
See this example:
It uses two pictureboxes, also a label to display the chosen color.
And two mouse click events, one to pick the color:
private void pictureBoxPalette_MouseClick(object sender, MouseEventArgs e)
{
Point sPt = scaledPoint(pictureBoxPalette, e.Location);
lbl_color.BackColor = ((Bitmap)pictureBoxPalette.Image).GetPixel(sPt.X, sPt.Y);
}
..and one to call the fill:
private void pictureBoxTgt_MouseClick(object sender, MouseEventArgs e)
{
Point sPt = scaledPoint(pictureBoxTgt, e.Location);
Bitmap bmp = (Bitmap)pictureBoxTgt.Image;
Color c0 = bmp.GetPixel(sPt.X, sPt.Y);
Fill4(bmp, sPt, c0, lbl_color.BackColor);
pictureBoxTgt.Image = bmp;
}
The Floodfill routine is taken from this post; it is basically a direct implementation of a wikipedia algorithm..:
static void Fill4(Bitmap bmp, Point pt, Color c0, Color c1)
{
Color cx = bmp.GetPixel(pt.X, pt.Y);
if (cx.GetBrightness() < 0.01f) return; // optional, to prevent filling a black grid
Rectangle bmpRect = new Rectangle(Point.Empty, bmp.Size);
Stack<Point> stack = new Stack<Point>();
int x0 = pt.X;
int y0 = pt.Y;
stack.Push(new Point(x0, y0) );
while (stack.Any() )
{
Point p = stack.Pop();
if (!bmpRect.Contains(p)) continue;
cx = bmp.GetPixel(p.X, p.Y);
if (cx.ToArgb() == c0.ToArgb()) //*
{
bmp.SetPixel(p.X, p.Y, c1);
stack.Push(new Point(p.X, p.Y + 1));
stack.Push(new Point(p.X, p.Y - 1));
stack.Push(new Point(p.X + 1, p.Y));
stack.Push(new Point(p.X - 1, p.Y));
}
}
}
Note: (*) Color equality will fail if one of the colors is a known or named color. So we need to convert to a common format..
Update
I have updated the code to include a function that will scale a mouse click location to an image pixel point; now it will work with SizeMode=StretchImage as well, so you can work on the whole image..
static Point scaledPoint(PictureBox pb, Point pt)
{
float scaleX = 1f * pb.Image.Width / pb.ClientSize.Width;
float scaleY = 1f * pb.Image.Height / pb.ClientSize.Height;
return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY));
}
Of course you can then save the Image.
Note that your original image is 4bpp and must be converted to 24bpp or better before coloring..
Also note that for SizeMode=Zoom the calculations are a little more involved. Here is an example that should work with any SizeMode.:
static Point scaledPoint(PictureBox pbox, Point pt)
{
Size si = pbox.Image.Size;
Size sp = pbox.ClientSize;
int left = 0;
int top = 0;
if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
pbox.SizeMode == PictureBoxSizeMode.AutoSize) return pt;
if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
{
left = (sp.Width - si.Width) / 2;
top = (sp.Height - si.Height) / 2;
return new Point(pt.X - left, pt.Y - top);
}
if (pbox.SizeMode == PictureBoxSizeMode.Zoom)
{
if (1f * si.Width / si.Height < 1f * sp.Width / sp.Height)
left = (sp.Width - si.Width * sp.Height / si.Height) / 2;
else
top = (sp.Height - si.Height * sp.Width / si.Width) / 2;
}
pt = new Point(pt.X - left, pt.Y - top);
float scaleX = 1f * pbox.Image.Width / (pbox.ClientSize.Width - 2 * left) ;
float scaleY = 1f * pbox.Image.Height / (pbox.ClientSize.Height - 2 * top);
return new Point((int)(pt.X * scaleX), (int)(pt.Y * scaleY));
}

OpenGL Shadow mapping. Performing calculations on the GPU?

I'm using a simple attenuation algorithm to darken walls based on their distance from light sources.
The end goal is to develop a light-mapping system in which the brightness for each wall is calculated in a pre-pass (including shadowing from other walls), and then that light-map image is blended with the wall texture.
+ =
Besides shadowing, I have the light-maps working, and the result replicate the shader code exactly. The problem is it is slow, and adding raycasted shadow checking is only going to make it worse.
My question is this, how can I perform these calculations on the GPU? Is a third party library/module required, or can it be done natively through OpenGL (OpenTK in my case)?
Alternatively, I'd be happy to switch to deferred rendering/lighting with cube shadow mapping but I'm yet to come across any information I can get my head around.
c# lightmap (run once for each wall)
public void createLightMap()
{
// Determine Light Map dimensions
int LightMapSize = 300;
int w = (int)(this.Width * LightMapSize);
int h = (int)(this.Height * LightMapSize);
// Create Bitmap
Bitmap bitmap = new Bitmap(w, h);
// Fragment testing
Vector3 fragmentPosition = new Vector3(this.x2, this.Height, this.z2);
float xIncement = (1f / LightMapSize) * ((x2 - x) / this.Width);
float zIncement = (1f / LightMapSize) * ((z2 - z) / this.Width);
float yIncement = (1f / LightMapSize);
// Calculate Light value for each pixel
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++)
{
// Update fragment position
fragmentPosition.X = this.x2 - xIncement -(xIncement * x);
fragmentPosition.Z = this.z2 - (zIncement * x);
fragmentPosition.Y = this.Height - (yIncement * y);
Vector3 totalDiffuse = Vector3.Zero;
// Iterate through the lights
for (int n = 0; n < 2; n++)
{
Light light = Game.lights[n];
Vector3 LightPosition = new Vector3(light.Position);
Vector3 Attenuation = new Vector3(light.Attenuation);
Vector3 Colour = new Vector3(light.Colour);
Vector3 toLightVector = LightPosition - fragmentPosition;
// Return early if wall is facing away from light
if (Vector3.Dot(this.normalVector, toLightVector.Normalized()) < 0)
continue;
// Calculate vector length (aka, distance from lightsource)
float distance = (float)Math.Sqrt(toLightVector.X * toLightVector.X + toLightVector.Y * toLightVector.Y + toLightVector.Z * toLightVector.Z);
// Attenuation
float attFactor = Attenuation.X + (Attenuation.Y * distance) + (Attenuation.Z * distance * distance);
Vector3 diffuse = Colour / attFactor;
totalDiffuse += diffuse;
}
// Create bitmap
var r = (int)(totalDiffuse.X * 256);
var g = (int)(totalDiffuse.Y * 256);
var b = (int)(totalDiffuse.Z * 256);
r = Math.Min(r, 255);
g = Math.Min(g, 255);
b = Math.Min(b, 255);
// Set Pixel
bitmap.SetPixel(x, y, Color.FromArgb(r, g, b));
}
}
this.LightMapTextureID = Texture.loadImage(bitmap);
}
Fragment shader (an alternative to above light-mapping, creating the same effect)
#version 330
precision highp float;
in vec2 frag_texcoord;
in vec3 toLightVector[8];
uniform sampler2D MyTexture0;
uniform vec3 LightColour[8];
uniform vec3 LightAttenuation[8];
uniform float NumberOfLights;
out vec4 finalColor;
void main(void)
{
vec3 totalDiffuse;
for (int i=0; i<NumberOfLights; i++) {
{
float distance = length(toLightVector[i]);
float attFactor = LightAttenuation[i].x + (LightAttenuation[i].y * distance) + (LightAttenuation[i].z * distance * distance);
vec3 diffuse = (LightColour[i]) / attFactor;
totalDiffuse += diffuse;
}
finalColor = vec4(totalDiffuse, 1.0) * texture(MyTexture0, frag_texcoord)
}
}

C# GDI+ clip draw and more

I am writing an application that has a panel in which I display:
Picture background
Lots of drawn objects (using GDI+ lines, paths, etc)
Said objects have some hit detection in mouse move event.
The panel pans and zooms. The panel is double buffered.
So everything is working pretty well - things look good, no flicker, but I am at the point where I am trying to focus on performance. I take less than 1 ms to draw my objects (per object) BUT when zoomed out I can have upwards of 500 objects to draw, which starts to make everything from the drawing to the hit detection sluggish.
I have already done a few things to try to improve performance like making a list of only the on screen (drawable) objects - but as previously mentioned, when zoomed out, it can still be LOTS. I got the grand idea that maybe instead of updating EVERY object EVERY time, I could implement a simple Invalidate() which would tell the object its made a visual change worthy of drawing. In the object paint code (which is passed e.graphics), I attempted to set the clipping region to the size of the object and only update that portion of the panel BG (also when done, I reset the e.Grpahics.clip). The result was only the invalidated object paints - everything else is blank.
I am confident this is the C#/GDI noob coming out in me and you all will tell me what stupid mistake I've probably overlooked.
At the time of writing this, I realize that I can save some time by make the path of my object constant instead of making it fresh every time in the draw function (since all objects paths are identical). I'll update with time I shave off shortly.
Here is the code that paints the BG image (and determines which OBJS to paint) and the OBJ paint code.
private void PaintImage(PaintEventArgs e)
{
int scale = 10; //TARGET ICON BASE SCALE....
var watch2 = System.Diagnostics.Stopwatch.StartNew();
if (bitmap != null && redrawBG)
{
float widthZoomed = TgtPanel.Width / Zoom;
float heigthZoomed = TgtPanel.Height / Zoom;
//Do checks the reason 30,000 is used is because
//much over this will cause DrawImage to crash
if (widthZoomed > 30000.0f)
{
Zoom = TgtPanel.Width / 30000.0f;
widthZoomed = 30000.0f;
}
if (heigthZoomed > 30000.0f)
{
Zoom = TgtPanel.Height / 30000.0f;
heigthZoomed = 30000.0f;
}
//we stop at 2 because at this point you have almost zoomed into a single pixel
if (widthZoomed < 2.0f)
{
Zoom = TgtPanel.Width / 2.0f;
widthZoomed = 2.0f;
}
if (heigthZoomed < 2.0f)
{
Zoom = TgtPanel.Height / 2.0f;
heigthZoomed = 2.0f;
}
float wz2 = widthZoomed / 2.0f;
float hz2 = heigthZoomed / 2.0f;
Rectangle drawRect = new Rectangle(
(int)(viewPortCenter.X - wz2),
(int)(viewPortCenter.Y - hz2),
(int)(widthZoomed),
(int)(heigthZoomed));
e.Graphics.Clear(Color.White); //Clear the Back buffer
//Draw the image, Write image to back buffer, and [render back buffer - no longer using my own backbuffer]
e.Graphics.DrawImage(bitmap,
this.TgtPanel.DisplayRectangle, drawRect, GraphicsUnit.Pixel);
// e.Graphics.ScaleTransform(Zoom, Zoom);
// e.Graphics.DrawImage(bitmap,
// 0,0);
if (draging)
{ //line to visualize the drag
e.Graphics.DrawLine(new Pen(Color.Yellow, 10), StartDrag.X, StartDrag.Y, lastMouse.X, lastMouse.Y);
}
//this checks for offscreen - works
if (drawRect.X > iconbitmap.Width || drawRect.X < -(drawRect.Width) ||
drawRect.Y > 0 + iconbitmap.Height || drawRect.Y < -(drawRect.Height))
{
label1.Text = "OFF";
}
} //if bitmap != null & redraw
// Font and Brush for the text graphics
Point mypoint = WorldToScreen(0.75f * scale, 7.0f * scale);
RectangleF bgrect = new RectangleF();
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = (3.5f * scale * Zoom);
bgrect.Height = (2.0f * scale * Zoom);
int aFontSizeDefault = 40;
int aFontSizeMinimum = 2;
String adrawString = "AAA"; //test this length
Font aDefaultFont = new Font("MS Sans Serif", aFontSizeDefault, FontStyle.Regular);
FontAdjustment afa = new FontAdjustment();
Font AAdjustedFont = afa.GetAdjustedFont(e.Graphics, adrawString,
aDefaultFont, Convert.ToInt32(bgrect.Width), aFontSizeDefault, aFontSizeMinimum, true);
//DRAW TGT BG
var Point1 = ScreenToWorld(0, 0);
var Point2 = ScreenToWorld(TgtPanel.Width, TgtPanel.Height);
//getVisible Screen == on;y draw visible targets
if (redrawBG)
{
VISIBLETARGETS.Clear(); //erase visible tgts array - we're going to update it
foreach (Target TGT in THETARGETS)
if (TGT.PosX >= Point1.X - 40 && TGT.PosX <= Point2.X - 9)
if (TGT.PosY >= Point1.Y - 83 && TGT.PosY <= Point2.Y - 5)
{
TGT.OnScreen = true;
//drawTarget(TGT, AAdjustedFont, e);
VISIBLETARGETS.Add(TGT); //update as visible
}
else TGT.OnScreen = false;
//redrawBG = false;
}
var watch = System.Diagnostics.Stopwatch.StartNew();
foreach(Target TGT in VISIBLETARGETS)
{
if(TGT.Invalidated || redrawBG) // THIS IS DRAWING ONLY TGT -- NOT OTHERS, OR BG - FIX THIS WITH CLIPPING?
drawTarget(TGT, AAdjustedFont, e);
}
watch.Stop();
watch2.Stop();
var elapsedMs = watch.ElapsedMilliseconds;//ElapsedTicks;
label6.Text = "TotalDrawTime = " + watch2.ElapsedMilliseconds.ToString();
label4.Text = "AvgDrawTime = " + elapsedMs.ToString();
label5.Text = "VisibleTgts = " + VISIBLETARGETS.Count.ToString();
AAdjustedFont.Dispose();
aDefaultFont.Dispose();
//------------- DRAWING TGT WITH GDI - WITH ORIGINAL BACKBUFFER
/// myBuffer.Render(this.TgtPanel.CreateGraphics());
redrawBG = false;
}
public void drawTarget(Target Tgt, Font AAdjustedFont, PaintEventArgs e)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
const float scale = 10; //10 is at 1 zoom
var bgrect = new RectangleF();
Point mypoint = WorldToScreen(Tgt.PosX + 0.75f * scale, Tgt.PosY + 1.0f * scale);
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = 3.5f * scale * Zoom;
bgrect.Height = 7.5f * scale * Zoom;
//PLAY WITH CLIP
e.Graphics.Clip = new Region(bgrect);
//var hbrush = new HatchBrush(HatchStyle.DarkDownwardDiagonal, Color.White);
var hbrush = new SolidBrush(Color.White);
//if(WantToDrawIconBG() ....
//e.Graphics.FillRectangle(hbrush, bgrect); //ICON BACKGROUND
//ADDR RECT
// mypoint = WorldToScreen(0, Tgt.PosY + 7.0f * scale);
mypoint = WorldToScreen(0, Tgt.PosY + 6.90f * scale); //moved Y up a bit from above
bgrect.Y = mypoint.Y;
bgrect.Height = 1.5f * scale * Zoom;
/////brush.Color = (Color.GhostWhite);
e.Graphics.FillRectangle(hbrush, bgrect);
hbrush.Dispose();
string adrawString = Tgt.Address;
System.Drawing.Font adrawFont = new System.Drawing.Font("Arial", 16);
System.Drawing.SolidBrush adrawBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Red);
System.Drawing.StringFormat adrawFormat = new System.Drawing.StringFormat();
adrawFormat.Alignment = StringAlignment.Center;
e.Graphics.DrawString(adrawString, AAdjustedFont, adrawBrush, bgrect, adrawFormat); //draw addr
//======= LETS MAKE THE TGT ICON SHAPE =======
GraphicsPath path = new GraphicsPath(FillMode.Alternate);
//TARGET POINTS (w = 3, h = 6)
var h1 = WorldToScreen(Tgt.PosX + 2 * scale, Tgt.PosY + 1.1f * scale);
var h2 = WorldToScreen(Tgt.PosX + 3 * scale, Tgt.PosY + 1.1f * scale);
var n1 = WorldToScreen(Tgt.PosX + 2 * scale, Tgt.PosY + 2 * scale);
var n2 = WorldToScreen(Tgt.PosX + 3 * scale, Tgt.PosY + 2 * scale);
var s1 = WorldToScreen(Tgt.PosX + 1 * scale, Tgt.PosY + 3 * scale);
var s2 = WorldToScreen(Tgt.PosX + 4 * scale, Tgt.PosY + 3 * scale);
var b1 = WorldToScreen(Tgt.PosX + 1 * scale, Tgt.PosY + 7 * scale);
var b2 = WorldToScreen(Tgt.PosX + 4 * scale, Tgt.PosY + 7 * scale);
var controln2 = WorldToScreen(Tgt.PosX + 1 * scale, (Convert.ToInt32(Tgt.PosY + 0.5 * scale)));
var controls2 = WorldToScreen(Tgt.PosX + 1 * scale, Tgt.PosY + 1 * scale);
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 255));
PointF[] npoints = { n2, s2, b2 };
PointF[] npoints2 = { n1, s1, b1 };
e.Graphics.DrawCurve(pen, npoints, 0.5f);
path.AddCurve(npoints, 0.5f); /////
e.Graphics.DrawLine(pen, b2, b1);
path.AddLine(b2, b1); /////
e.Graphics.DrawCurve(pen, npoints2, 0.5f);
path.AddCurve(npoints2, 0.5f); /////
PointF[] hpoints = { n1, h1, h2, n2 };
e.Graphics.DrawCurve(pen, hpoints, 0.1f);
path.AddCurve(hpoints, 0.1f); /////
path.CloseAllFigures();
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (Zoom > 0.9) //only draw stroke if big enough to see (and there arent a million targets to draw
{
pen.Color = Tgt.Selected ? Color.Chartreuse : Color.FromName(comboBox1.Text); //black default
pen.Width = Tgt.Selected ? 2 * Zoom : 1 * Zoom; //draw thicker border if selected
e.Graphics.DrawPath(pen, path); ///------------------------------------------STROKE PATH.....
//e.Graphics.FillPath(new SolidBrush(Color.ForestGreen), path);
}
// how much time is wasted making 2 gradient brushes? One we wont even use.
LinearGradientBrush linGrBrush = new LinearGradientBrush(
WorldToScreen(Tgt.PosX + 0 * scale, Tgt.PosY + 5 * scale),
WorldToScreen(Tgt.PosX + 5.5f * scale, Tgt.PosY + 5 * scale),
Color.ForestGreen, // Opaque red
Color.FromArgb(255, 0, 255, 0)); // Opaque blue
LinearGradientBrush linRedBrush = new LinearGradientBrush(
WorldToScreen(Tgt.PosX + 0 * scale, Tgt.PosY + 5 * scale),
WorldToScreen(Tgt.PosX + 5.5f * scale, Tgt.PosY + 5 * scale),
Color.FromArgb(255, 255, 0, 0), // Opaque red
Color.Firebrick); // Opaque blue
//FILL TARGET ICON WITH COLOR - UP or DOWN
if (Tgt.IsUp) e.Graphics.FillPath(linGrBrush, path);
else e.Graphics.FillPath(linRedBrush, path);
//------------
//tgt lines (cosmetic only)
if (Zoom > 0.9) //only draw if big enough to see (and there arent a million targets to draw
{
var transPen = new Pen(Color.FromArgb(150, 200, 200, 200));
var l1a = WorldToScreen(Tgt.PosX + 2.5f * scale, Tgt.PosY + 1.5f * scale);
var l1b = WorldToScreen(Tgt.PosX + 2.5f * scale, Tgt.PosY + 6 * scale);
e.Graphics.DrawLine(transPen, l1a, l1b);
var l2a = WorldToScreen(Tgt.PosX + 1.5f * scale, Tgt.PosY + 2.5f * scale);
var l2b = WorldToScreen(Tgt.PosX + 1.5f * scale, Tgt.PosY + 6.5f * scale);
e.Graphics.DrawLine(transPen, l2a, l2b);
var l3a = WorldToScreen(Tgt.PosX + 3.5f * scale, Tgt.PosY + 2.5f * scale);
var l3b = WorldToScreen(Tgt.PosX + 3.5f * scale, Tgt.PosY + 6.5f * scale);
e.Graphics.DrawLine(transPen, l3a, l3b);
}
//Draw Hits....
mypoint = WorldToScreen(Tgt.PosX + 1.0f * scale, Tgt.PosY + 3.0f * scale);
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = 3.0f * scale * Zoom;
bgrect.Height = 1.5f * scale * Zoom;
adrawString = Tgt.Hits.ToString();
adrawFormat.Alignment = StringAlignment.Center;
if (Zoom > 0.9) //only draw if big enough to see (and there arent a million targets to draw
{
adrawBrush.Color = Color.FromArgb(100, 100, 100, 100);
e.Graphics.FillRectangle(adrawBrush, bgrect);
}
adrawBrush.Color = Color.White;
e.Graphics.DrawString(adrawString, AAdjustedFont, adrawBrush, bgrect, adrawFormat); //draw hits
//Draw Score....
mypoint = WorldToScreen(Tgt.PosX + 1.0f * scale, Tgt.PosY + 5.0f * scale);
bgrect.X = mypoint.X;
bgrect.Y = mypoint.Y;
bgrect.Width = 3.0f * scale * Zoom;
bgrect.Height = 1.5f * scale * Zoom;
adrawString = Tgt.Score.ToString();
adrawFormat.Alignment = StringAlignment.Center;
if (Zoom > 0.9) //only draw if big enough to see (and there arent a million targets to draw
{
adrawBrush.Color = Color.FromArgb(100, 100, 100, 100);
e.Graphics.FillRectangle(adrawBrush, bgrect);
}
adrawBrush.Color = Color.White;
e.Graphics.DrawString(adrawString, AAdjustedFont, adrawBrush, bgrect, adrawFormat); //draw hits
adrawFont.Dispose();
adrawBrush.Dispose();
adrawFormat.Dispose();
path.Dispose();
watch.Stop();
var elapsedMs = watch.ElapsedTicks;
//14279 original ticks
//12764 removing label and reducing font size calcs...
// 1695 ! removed font size to external calc so it happens only once
e.Graphics.ResetClip();
e.Graphics.Clip.Dispose();
Tgt.Invalidated = false; //dont draw again until change
}

directx transformedcolored color field alpha value

I am using C #. I need to draw points using DirectX and they should be semitransparent. I declared vertices to be transformedColored and set the color value to be:
vertices[i].Color = Color.FromArgb(20,0,0,255).ToArgb();
This should be quite transparent blue but what I got was opaque blue. Whatever value I use for the alpha value, it always gets fully opaque.
Any ideas why? I hope the transformedcolored color field supports alpha value.
Thanks in advance.
The drawing code is:
device.Clear(ClearFlags.Target, System.Drawing.Color.White, 1.0f, 0);
device.RenderState.AlphaBlendEnable = true;
device.RenderState.AlphaSourceBlend = Blend.SourceAlpha;
device.RenderState.AlphaDestinationBlend = Blend.InvSourceAlpha;
device.RenderState.BlendOperation = BlendOperation.Add;
CustomVertex.TransformedColored[] vertices = new CustomVertex.TransformedColored[N];
for (int i = 0; i < N; i++)
{
vertices[i].Position = new Vector4(2.5f + (g_embed[i, 0] - minx) * (width - 5.0f) / maxx, 2.5f + (g_embed[i, 1] - miny) * (height - 5.0f) / maxy, 0f, 1f);//g_embed, minx, width,maxx, miny,height, maxy are all predifined
vertices[i].Color = Color.FromArgb(20, 0, 0, 255).ToArgb();
}
device.BeginScene();
device.VertexFormat = CustomVertex.TransformedColored.Format;
device.DrawUserPrimitives(PrimitiveType.PointList, N, vertices);
device.EndScene();
device.Present();
this.Invalidate();
Your renderstates are used for premultiplied alpha. Using a real alpha-channel needs the following settings (Blendenumerationdoc):
graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;
graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
graphics.GraphicsDevice.RenderState.BlendFunction = BlendFunction.Add;
It creates following the formular, where a is your alpha of the color: a * SourceColor + (1-a) * DestColor

Categories