How to set up correct perspective view in OpenTK? - c#

I made a full-screen size square to show on window.
But sadly, I am stuck at changing viewpoint (camera or perspective?) to make square look small at the center of window.
As many people suggested on web, I followed guid of setting up Matrix and perspective field of view which does not work...
I am wondering what I am missing on my code.
private void ImageControl_OnRender(TimeSpan delta)
{
//Create perspective camera matrix
//ImageControl is the name of window
GL.Viewport(0, 0, (int)ImageControl.Width, (int)ImageControl.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
Matrix4 perspectiveMatrix;
Matrix4.CreatePerspectiveFieldOfView(45.0f * (float)Math.PI / 180, (float)(ImageControl.Width / ImageControl.Height), 0.1f, 100.0f, out perspectiveMatrix);
//Set perspective camera
//GL.MatrixMode(MatrixMode.Projection);
//GL.LoadIdentity();
GL.LoadMatrix(ref perspectiveMatrix);
GL.LoadIdentity();
GL.MatrixMode(MatrixMode.Modelview);
//GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
//Now starting to draw objects
//Set the background colour
GL.ClearColor(Color4.SkyBlue);
//Clear the colour and depth buffer for next matrix.
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//Set the scale of object first hand
//GL.Scale(0.5f, 0.5f, 0.5f);
//GL.Translate() <<< Set the translation of object first hand
GL.Translate(0.0f, 0.0f, -2.0f);
//Set the colour of object first hand
GL.Color3(0.3f, 0.2f, 0.5f);
//Tells that we are going to draw a sqare consisting of vertices. Can be Triangle too!
GL.Begin(PrimitiveType.Quads);
GL.Vertex3(-1.0f, -1.0f, 0.0f);
GL.Vertex3(1.0f, -1.0f, 0.0f);
GL.Vertex3(1.0f, 1.0f, 0.0f);
GL.Vertex3(-1.0f, 1.0f, 0.0f);
//GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
//GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
//GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.End();
GL.Finish();
}

You load the identity matrix after the projection matrix. This overrides the projection matrix. Do the following:
// 1. Select projection matrix mode
GL.MatrixMode(MatrixMode.Projection); // <--- INSERT
// 2. Load projection matrix
GL.LoadMatrix(ref perspectiveMatrix);
// GL.LoadIdentity(); <--- DELETE
// 3. Select model view matrix mode
GL.MatrixMode(MatrixMode.Modelview);
// 4. Clear model view matrix (load the identity matrix)
GL.LoadIdentity();
// 5. Multiply model view matrix with the translation matrix
GL.Translate(0.0f, 0.0f, -2.0f);
Note that GL.MatrixMode selects the current matrix. All pending matrix operations affect the selected matrix. GL.LoadIdentity "clears" the matrix. It loads the Identity matrix.

Related

Mapping rectangular texture onto sphere

I want to map a rectangular globe texture onto a sphere. I can load the "globe.jpg" texture and display it onto the screen. I think I need to retrieve the color of the "globe.jpg" texture at specific texture coordinates and use that to colorize a specific point on the globe.
I want to map the globe map on the rightmiddle side onto one of the spheres on the left side (see picture)
Code for loading texture:
int texture;
public Texture() {
texture = LoadTexture("Content/globe.jpg");
}
public int LoadTexture(string file) {
Bitmap bitmap = new Bitmap(file);
int tex;
GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
GL.GenTextures(1, out tex);
GL.BindTexture(TextureTarget.Texture2D, tex);
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
bitmap.UnlockBits(data);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
//GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
//GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
return tex;
}
I also already created some code that maps a point on a sphere to a point on a point on the texture I think (used code from the texture mapping spheres section on https://www.cs.unc.edu/~rademach/xroads-RT/RTarticle.html).
point is a Vector3 of where a ray intersects the sphere:
vn = new Vector3(0f, 1f, 0f); //should be north pole of sphere, but it isn't based on sphere's position, so I think it's incorrect
ve = new Vector3(1f, 0f, 0f); // should be a point on the equator
float phi = (float) Math.Acos(-1 * Vector3.Dot(vn, point));
float theta = (float) (Math.Acos(Vector3.Dot(point, ve) / Math.Sin(phi))) / (2 * (float) Math.PI);
float v = phi / (float) Math.PI;
float u = Vector3.Dot(Vector3.Cross(vn, ve), point) > 0 ? theta : 1 - theta;
I think that I can now use this u and v coordinate on the texture I loaded to find the color of the texture there. But I don't know how. I also think the north pole and equator vectors are not correct.
I don't know if you still need an answer after 4 months, but:
If you have a proper sphere model (like an obj file created with blender) with the correct uv information, you just need to import that model (using assimp or any other importer) and apply the texture during render pass.
Your question is a bit vague because I do not know if you use shaders.
My approach would be:
1: Import model with assimp library or any other import library
2: Implement vertex and fragment shaders and include a sampler2D uniform for the texture in the fragment shader
3: During render pass select your shader program id [ GL.UseProgram(...) ] and then upload vertices and texture uv and texture pixel (as a uniform) information to the shaders.
4: Use a standard vertex shader like this:
#version 330
in vec3 aPosition;
in vec2 aTexture;
out vec2 vTexture;
uniform mat4 uModelViewProjectionMatrix;
void main()
{
vTexture = aTexture;
gl_Position = uModelViewProjectionMatrix * vec4(aPosition, 1.0);
}
5: Use a standard fragment shader like this:
#version 330
in vec2 vTexture;
uniform sampler2D uTexture;
out vec4 fragcolor;
void main()
{
fragcolor = texture(uTexture, vTexture);
}
If you need a valid obj file for a sphere with rectangular uv mapping, feel free to drop a line (or two).

How do I stop a texture from darkening after drawing a colored quad in C# OpenTK?

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.

Managed DirectX: Specifying the Depth (Z-Order) of Sprites containing Texture and Text

I am using managed DirectX to try and draw a texture and a piece of text to the screen using a Sprite. Unfortunately, if I place the text and the texture in the same sprite, the texture overwrites (overdraws?) the text regardless of the order I do the draw commands.
Since I will eventually want to intersperse textures and text, how do I specify a Z-order for these sprites. Does each layer have to be in a separate sprite?
Here is the current code:
m_device.BeginScene();
m_device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);
m_sprite.Begin(SpriteFlags.SortTexture | SpriteFlags.AlphaBlend);
// Switching the order of following two statements doesn't change the Z-Order!
m_sprite.Draw(m_texture, Vector3.Empty, new Vector3(0, 0, 0),
Color.White.ToArgb());
m_d3dFont.DrawText(m_sprite, m_text, x, y, color);
m_sprite.End();
m_device.EndScene();
m_device.Present();
Note: Using the SpriteFlags.SortDepthBackToFront or SpriteFlags.SortDepthBackToFront does not change the behaviour.
This is probably a conceptual misunderstanding on my part, but if code is useful, I'll gratefully accept samples in unmanaged DirectX using C++ or whatever language.
Many thanks in advance!
If you want to change the Z-Order of rendering then you have to set the Z value in the Draw command. If you set the all to 0 you will get all sorts of weirdness. Your bigger issue is that DrawText doesn't allow you to set a Z-Depth which is all kinds of rubbish.
Thus your only chance is to use ID3DXSprite::SetTransform. You need to shift only the Z-Coordinate back with it for the relevant z ordering position. So you can set your transforms (assuming you are using identity world matrices) as follows (C++ example)
D3DXMATRIX mat( 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, zOrder, 1.0f );
m_Sprite->SetTransform( &mat );
You then carry on passing a position of (0, 0, 0) for rendering and text will also gain the correct depth for z-ordering.
Hope that helps.
Here is the answer I (the poster) went with:
Goz provided most of the information needed to solve the problem. Essentially, for textures, you can specify the z-order using the third parameter of the second Verctor3. Experimentation has made it clear that (1) z order goes from 0.0 to 1.0, with 0.0 being closest and 1.0 being the the furthest away. Anything out of that range doesn't appear at all.
For text, because there's no opportunity to specify the z in the call, you need to use Goz's suggestion of a transform matrix.
In the end, here's roughly the code I used.
m_device.BeginScene();
m_device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);
m_sprite.Begin(SpriteFlags.SortDepthFrontToBack | SpriteFlags.SortTexture | SpriteFlags.AlphaBlend);
// The "1.0f" is the z-order of texture1. This places it at the very back.
m_sprite.Draw(m_texture1, Vector3.Empty, new Vector3(0, 0, 1.0f),
Color.White.ToArgb());
// The text1 is placed at z-order 0.8f, in order to place it in front of texture1
Microsoft.DirectX.Matrix t = Microsoft.DirectX.Matrix.Identity;
t.Translate(new Vector3(0, 0, 0.8f));
m_sprite.Transform = t;
m_d3dFont.DrawText(m_sprite, m_text1, 200, 200, color1);
m_sprite.Transform = Microsoft.DirectX.Matrix.Identity;
// The "0.6f" is the z-order of texture2. This places it at the very back.
m_sprite.Draw(m_texture2, Vector3.Empty, new Vector3(220, 220, 0.6f),
Color.White.ToArgb());
// The text2 is placed at z-order 0.4f, in order to place it in front of texture2
t = Microsoft.DirectX.Matrix.Identity;
t.Translate(new Vector3(0, 0, 0.4f));
m_sprite.Transform = t;
m_d3dFont.DrawText(m_sprite, m_text2, 240, 240, color2);
m_sprite.Transform = Microsoft.DirectX.Matrix.Identity;
m_sprite.End();
m_device.EndScene();
m_device.Present();

Negative scaling with spritebatch in XNA vs Primitives

I am using both primitives and sprites in an XNA project. I draw my primitives using this code (a summary, not verbatim from my project):
transmatrix = Matrix.CreateTranslation(v23(-pos)) * Matrix.CreateScale(scale, -scale, 1f) * Matrix.CreateTranslation(v23(offset));
basicEffect.World = transmatrix;
basicEffect.View = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
basicEffect.Projection = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
basicEffect.Begin();
//draw primitives blah blah
basicEffect.End();
spritebatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None, transmatrix);
//draw sprites blah blah
spritebatch.end()
if I have
transmatrix = Matrix.CreateTranslation(v23(-pos)) * Matrix.CreateScale(scale, scale, 1f) * Matrix.CreateTranslation(v23(offset));
both the primitives and the sprites draw (but y is the inverse of what I want).
if I have
transmatrix = Matrix.CreateTranslation(v23(-pos)) * Matrix.CreateScale(scale, -scale, 1f) * Matrix.CreateTranslation(v23(offset));
The primitives draw correctly, but the sprites don't draw at all. What am I missing? I have tried messing around with lots of different things but nothing seems to work.
It turns it was another problem that was causing the issue. I have to scale all the sprites with negative y to get them to draw correctly as mentioned in a msdn community content "Something that is not obvious when using this form of the method to map SpriteBatch to a desired coordinate system is the effect of negative scaling on the results. If you scale by a single negative number in the X or Y axes (if, for instance, you want to flip the Y-axis to match your game coordinates) the polygon that the sprite gets drawn on gets flipped in the process and gets backface-culled. It is necessary to also scale the texture by a negative in the same direction to get the results you expect."
http://msdn.microsoft.com/en-us/library/ff433701.aspx
SpriteBatch sets up its own "camera" using its own parameters. To scale your camera, do this:
basicEffect.View = Matrix.CreateScale(1.0f, -1.0, 1.0f) * Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, 1.0f), Vector3.Zero, Vector3.Up);
Or you could just create a different matrix for the spritebatch.
Alternatively, you could pass in your BasicEffect to SpriteBatch:
http://msdn.microsoft.com/en-us/library/ff433700.aspx

c# Non-Linear gradient brushes?

Is there a method I can use to generate non-linear Gradient brushes in c#?
To clarify: Im not looking for a gradient that can be applied to a non-linear path. Rather, I am looking for a gradient between 2 colours that fades between the two in a non-linear fashion i.e. the rate of changs is quicker at the start of the gradient and slows down as the brush nears the finishing point.
Is this even remotly possible in c# using gdi+?
Yep, set the blend for the Brush.
LinearGradientBrush blendBrush = new LinearGradientBrush(Rectangle, Color.Red, Color.White, 360f);
Blend blend = new Blend();
blend .Factors = new float[] { 1.0f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f };
blend .Positions = new float[] { 0, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f };
blendBrush.Blend = blend ;
This example will start fading out, then go back to the previous colour. So a kinda fill from centre effect.
Use the Blend property of LinearGradientBrush -
http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.blend(VS.80).aspx

Categories