I've got drawing sprites to work with OpenTK in my 2d game engine now. Only problem I'm having is that custom drawn objects with opengl (anything but sprites really) show up as the background color. Example:
I'm Drawing a 2.4f width black line here. There's also a quad and a point in the example, but they do not overlap anything that's actually visible. The line overlaps the magenta sprite, but the color is just wrong. My question is: Am I missing an OpenGL feature, or doing something horrible wrong?
These are the samples of my project concerning drawing: (you can also find the project on https://github.com/Villermen/HatlessEngine if there's questions about the code)
Initialization:
Window = new GameWindow(windowSize.Width, windowSize.Height);
//OpenGL initialization
GL.Enable(EnableCap.PointSmooth);
GL.Hint(HintTarget.PointSmoothHint, HintMode.Nicest);
GL.Enable(EnableCap.LineSmooth);
GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.ClearColor(Color.Gray);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.DepthTest);
GL.DepthFunc(DepthFunction.Lequal);
GL.ClearDepth(1d);
GL.DepthRange(1d, 0d); //does not seem right, but it works (see it as duct-tape)
Every draw cycle:
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//reset depth and color to be consistent over multiple frames
DrawX.Depth = 0;
DrawX.DefaultColor = Color.Black;
foreach(View view in Resources.Views)
{
CurrentDrawArea = view.Area;
GL.Viewport((int)view.Viewport.Left * Window.Width, (int)view.Viewport.Top * Window.Height, (int)view.Viewport.Right * Window.Width, (int)view.Viewport.Bottom * Window.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(view.Area.Left, view.Area.Right, view.Area.Bottom, view.Area.Top, -1f, 1f);
GL.MatrixMode(MatrixMode.Modelview);
//drawing
foreach (LogicalObject obj in Resources.Objects)
{
//set view's coords for clipping?
obj.Draw();
}
}
GL.Flush();
Window.Context.SwapBuffers();
DrawX.Line:
public static void Line(PointF pos1, PointF pos2, Color color, float width = 1)
{
RectangleF lineRectangle = new RectangleF(pos1.X, pos1.Y, pos2.X - pos1.X, pos2.Y - pos1.Y);
if (lineRectangle.IntersectsWith(Game.CurrentDrawArea))
{
GL.LineWidth(width);
GL.Color3(color);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(pos1.X, pos1.Y, GLDepth);
GL.Vertex3(pos2.X, pos2.Y, GLDepth);
GL.End();
}
}
Edit: If I disable the blendcap before and enable it after drawing the line it does show up with the right color, but I must have it blended.
I forgot to unbind the texture in the texture-drawing method...
GL.BindTexture(TextureTarget.Texture2D, 0);
Related
I am creating my own game graphics engine. I have looked into using others like Unity, but they don't fit my needs. Anyway, I am using OpenTK (This is a 2D game), and the issue is that when i draw a texture to the screen, then draw a quad to the screen, the color darkens on the texture. Here is the method I am using to draw a texture:
public void Texture(int ID, Vector2 size, Vector2 pos, Vector2 texSize, Vector2 texPos)
{
pos.Y = -pos.Y;
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, ID);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(texPos.X, texPos.Y);
GL.Vertex2(pos.X, pos.Y);
GL.TexCoord2(texPos.X + texSize.X, texPos.Y);
GL.Vertex2(pos.X + size.X, pos.Y);
GL.TexCoord2(texPos.X + texSize.X, texPos.Y + texSize.Y);
GL.Vertex2(pos.X + size.X, pos.Y - size.Y);
GL.TexCoord2(texPos.X, texPos.Y + texSize.Y);
GL.Vertex2(pos.X, pos.Y - size.Y);
GL.End();
}
I am inverting the Y because I am used to the Windows Forms coordinate system, where going down is y++. I am calling it like this:
Texture(backdropTextureID, new Vector2(1f, 1f), new Vector2(-0.5f, -0.5f), new Vector2(1f, 1f), new Vector2(0f, 0f));
As expected, if there is nothing else being drawn, it draws a texture with the GL id of backdropTextureID in the center of the screen. When I draw a colored quad also though, the texture is darkened. Here is the method I am using for drawing a quad:
public void Quad(Vector2 pos1, Vector2 pos2, Vector2 pos3, Vector2 pos4, Color color1, Color color2, Color color3, Color color4)
{
GL.Disable(EnableCap.Texture2D);
pos1.Y = -pos1.Y;
pos2.Y = -pos2.Y;
pos3.Y = -pos3.Y;
pos4.Y = -pos4.Y;
GL.Begin(PrimitiveType.Quads);
GL.Color3(color1);
GL.Vertex2(pos1);
GL.Color3(color2);
GL.Vertex2(pos2);
GL.Color3(color3);
GL.Vertex2(pos3);
GL.Color3(color4);
GL.Vertex2(pos4);
GL.End();
}
Again, inverting the Y, for the reason stated above. Also, notice, I am enabling EnableCap.Texture2D in the method for drawing a texture, and disabling it when I draw a colored quad. I am calling the quad method like this:
Quad(new Vector2(0.0f, 0.0f), new Vector2(0.5f, 0.0f), new Vector2(0.5f, 0.5f), new Vector2(0.0f, 0.5f), Color.Gray, Color.Gray, Color.Gray, Color.Gray);
If anyone could help me, thank you in advance. Basically: How do I stop a texture from darkening after drawing a colored quad in C# OpenTK?
For anyone whose having this problem, I figured it out. The same color I was giving to the colored quad i gave to the texture. You just need to add
GL.Color3(Color.Transparent);
to the start of the texture drawing method.
I'm new to OpenTK and I'm using the following code to draw large number of polygons using OpenTK
public static void DrawPolygon(Point[] points)
{
GL.Begin(BeginMode.Polygon); //IF I Change this to LineStrip every things will be OK
int numberOfPoints = points.Length;
for (int i = 0; i < numberOfPoints; i++)
{
GL.Vertex2(points[i].X, points[i].Y);
}
GL.End();
}
And this is the configuration code which executes before calling the DrawPolygon
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(0, width, 0, height, -1, 1); // Bottom-left corner pixel has coordinate (0, 0)
GL.Viewport(0, 0, (int)width, (int)height);
GL.ClearColor(drawing.Color.Transparent);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
GL.Color3(pen.Color);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
GL.PointSize(5f);
GL.LineWidth(2f);
Using this code when I save the rendered image to disk as png, the result will be like this
result for:GL.Begin(BeginMode.Polygon);
However If I change the first line of the DrawPolygon to GL.Begin(BeginMode.LineStrip); the polygon would be rendered as expected like this:
result for : GL.Begin(BeginMode.LineStrip);
Anyone knows why those two extra lines appears when using BeginMode.Polygon?
I believe it's because GL_POLYGON/BeginMode.Polygon only renders convex polygons. I think that if you attempt render a concave polygon, as you're doing, the driver will try split up the geometry that you give it to render it as convex polygons, hence the unexpected lines in your example.
It's inadvisable to use the polygon rendering mode in OpenGL. The rendering of line strips and triangles is much more highly optimized, and therefore much, much quicker. I'd advise staying with line strips.
I want to draw a simple line. So I have this code as shown below. But when I run the code I can't see anything on the form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
drawLine();
}
private void drawLine() {
GL.glClear(GL.GL_COLOR_BUFFER_BIT);
GL.glBegin(GL.GL_LINES);
GL.glVertex3f(100.0f, 100.0f, 0.0f); // origin of the line
GL.glVertex3f(200.0f, 140.0f, 5.0f); // ending point of the line
GL.glEnd();
}
}
What should I do?
The default state of OpenGL assumes a viewport size that's equal to the size of the window the context was first attached to. All matrices are identity. So you're effectively drawing in so called normalized device coordinates (NDC) space. In that space the visible viewport has a coordinate range of [-1; 1] in either direction.
Your naive code assumes vertex positions to be in units of pixels. However everything outside the range [-1; 1] lies outside the screen and your "pixel coordinates" are way outside the visible viewport. What you must do is set viewport and projection so that they match your window size, if you want to use pixels as coordinate units.
private void drawLine() {
GL.glViewport(0, 0, window_width, window_height);
GL.glMatrixMode(GL_PROJECTION);
GL.glLoadIdentity();
GL.glOrtho(0, window_width, 0, window_height, -1, 1);
GL.glMatrixMode(GL_MODELVIEW);
GL.glLoadIdentity();
GL.glClear(GL.GL_COLOR_BUFFER_BIT);
GL.glBegin(GL.GL_LINES);
GL.glVertex3f(100.0f, 100.0f, 0.0f); // origin of the line
GL.glVertex3f(200.0f, 140.0f, 5.0f); // ending point of the line
GL.glEnd();
GL.glFlush();
this.SwapBuffer(); // if the form doesn't automatically swap
}
I drew some circles on the Form1 using GDI+, and the center of the circle is a small red rectangle of Custom Control which is derived from User Control, the BackgroundImage property ofForm1's a bitmap which is also drawn by GDI+ with several colors.
What I want is that when I move the red rectangle(the center of circle) with a mouse, the circle will also move following the red rectangle. Using the MouseDown, MouseMove event I could move the red rectangle smoothly with the mouse.
My problem is how to move the circle corresponding to the red rectangle(the center of circle).
I enabled the double buffering to solve the flicker problem. CircleCenter is an object of Custom Control class(e.g. the red rectangle). GObject is a Grahpics object.
Here is some key codes:
public Form1()
{
InitializeComponent();
this.SetStyle(ControlStyles.DoubleBuffer | //enables double-buffering
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
}
Point CCenterPoint = new Point();
private int Diameter = 250;
private void CircleCenterMouseDown(object sender, MouseEventArgs e)
{
CCenterPoint = new Point(-e.X, -e.Y);
}
private void CircleCenterMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Point MousePos = CircleCenter.MousePosition;
MousePos.Offset(CCenterPoint.X, CCenterPoint.Y);
CircleCenter.Location = CircleCenter.Parent.PointToClient(MousePos);
CircleCenter.BringToFront();
CirclePen.Color = Color.Black;
GObject.DrawEllipse(CirclePen, CircleCenter.Left- Diameter/2, CircleCenter.Top - Diameter/2, Diameter, Diameter);
this.Invalidate();
}
}
How to remove the black circle drawn by GDI+ produced in the MouseMove proceed?
I googled several websites and didn't get a satisfied answer. Hope you could give me some hints,Thx!
Well, As I understand from your question, You just need to draw a circle around the red rectangle, This is quite easy.
In the Paint event of the Form, Add the following (assuming that your red rectangle control has the name "CircleCenter" and your Form was named "Form1"):
private void Form1_Paint(object sender, PaintEventArgs e)
{
// get the Graphics object of the form.
Graphics g = e.Graphics;
// create a think red pen for the circle drawing
Pen redPen = new Pen(Brushes.Red, 4.0f);
// drawing the circle
PointF ctrlCenter = GetCenterOfControl(CircleCenter);
g.DrawEllipse(redPen,
ctrlCenter.X - (Diameter / 2),
ctrlCenter.Y - (Diameter / 2),
Diameter, Diameter);
}
//The following little method to calculate the center point
PointF GetCenterOfControl(Control ctrl)
{
return new PointF(ctrl.Left + (ctrl.Width / 2),
ctrl.Top + (ctrl.Height / 2));
}
Any way, I know it looks long for such a simple task like Circle drawing! here is the ugly one line version of the above code:
e.Graphics.DrawEllipse(new Pen(Brushes.Red, 4.0f), (centerCircle.Left + (centerCircle.Width / 2)) - (Diameter / 2), (centerCircle.Top + (centerCircle.Height / 2)) - (Diameter / 2), Diameter, Diameter);
You will need to always reset the entire GObject (erase the images you drawn on it) and then redraw them all again.
This can be done by simply drawing a rectangle with the color of the object from which you obtained the Graphics object (although you didn't mentioned it, I think GObject is a Graphics object, obtained of some win control?).
So something like:
Control control = CircleCenter.Parent; // Parent control where you get Graphics object from.
System.Drawing.SolidBrush sBrush = new System.Drawing.SolidBrush(control.BackColor); // Brush to be used with the same color like the one of the parent control.
GObject.FilledRectangle(sBrush, new Rectangle(0, 0, control.Width, control.Height); // Erase background.
GObject.DrawEllipse(CirclePen, CircleCenter.Left- Diameter/2, CircleCenter.Top - Diameter/2, Diameter, Diameter); // Do your stuff.
should hide the old drawings and re-draw the circle on the new location.
Trying to make a glow effect in xna but it doesn't show the glow or any change at all. Also my back color is purple instead of black and I can't change that either :
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.SetRenderTarget(bulletRenderTarget);
spriteBatch.Begin();
foreach (Bullet bullet in bulletList)
{
Texture2D bulletTexture = textures[bullet.bulletType];
spriteBatch.Draw(
bulletTexture,
new Rectangle(
(int)bullet.position.X,
(int)bullet.position.Y,
bulletTexture.Width,
bulletTexture.Height
),
null,
Color.White,
MathHelper.ToRadians(bullet.angle),
new Vector2(
bulletTexture.Width / 2,
bulletTexture.Height / 2
),
SpriteEffects.None,
0
);
}
spriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.Black);
postProcessEffect.CurrentTechnique = postProcessEffect.Techniques["Blur"];
spriteBatch.Begin();
spriteBatch.Draw(
bulletRenderTarget,
new Vector2(0, 0),
Color.White
);
GraphicsDevice.BlendState = BlendState.Additive;
foreach (EffectPass pass in postProcessEffect.CurrentTechnique.Passes)
{
pass.Apply();
spriteBatch.Draw(
bulletRenderTarget,
new Vector2(0,0),
Color.White
);
}
DrawHud();
foreach (BaseEntity entity in entityList)
{
entity.Draw(gameTime);
}
spriteBatch.End();
I'm only trying to get the bullets to glow.
shader :
float BlurDistance = 0.002f;
sampler ColorMapSampler : register(s1);
float4 PixelShaderFunction(float2 Tex: TEXCOORD0) : COLOR
{
float4 Color;
// Get the texel from ColorMapSampler using a modified texture coordinate. This
// gets the texels at the neighbour texels and adds it to Color.
Color = tex2D( ColorMapSampler, float2(Tex.x+BlurDistance, Tex.y+BlurDistance));
Color += tex2D( ColorMapSampler, float2(Tex.x-BlurDistance, Tex.y-BlurDistance));
Color += tex2D( ColorMapSampler, float2(Tex.x+BlurDistance, Tex.y-BlurDistance));
Color += tex2D( ColorMapSampler, float2(Tex.x-BlurDistance, Tex.y+BlurDistance));
// We need to devide the color with the amount of times we added
// a color to it, in this case 4, to get the avg. color
Color = Color / 4;
// returned the blurred color
return Color;
}
technique Blur
{
pass Pass1
{
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
The reason it's purple is because you have
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.SetRenderTarget(bulletRenderTarget);
which should be the other way around, so changing that into
GraphicsDevice.SetRenderTarget(bulletRenderTarget);
GraphicsDevice.Clear(Color.Black);
fixes the purple problem, to fix the shader change the following in the fx file
sampler ColorMapSampler : register(s0);
And change your spriteBatch.Begin() into
spriteBatch.Begin(SpriteSortMode.Immediate, null);
Some extra info:
the s0 points to the first texture on the graphics device, which is the one that supplied by spriteBatch.Draw, if you want to use s1 you'd have to set it on the GraphicsDevice first by using:
GraphicsDevice.Textures[1] = bulletRenderTarget;
the SpriteSortMode.Immediate just forces the spriteBatch.Draw to draw the sprite immediately, if you don't set it it'll create a batch and draw them all at once, but this will be to late because it needs to be drawn when the EffectPass is being applied.
As for the blur you could lower the value of BlurDistance, but you'd have to try, you can also look up how to do a bloom shader, usually gives a nice effect too.