I have my rendering engine more or less figured out. But I need to add functionality to render a 3d object that's pinned next to the camera (like holding a gun in an fps).
I tried first doing it by doing a lot of angle calculations and placing the object (in this case a cube) in the world at the coordinates where the camera would be looking at. This did not work out as I expected it to.
Then I searched on the web and people recommend drawing the object before applying the camera matrix. I've been trying to do this but with no success. The object doesn't appear at all.
The exact code segment is as follows:
camera_matrix = camera.Matrix;
GL.ClearColor(Color.White);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadMatrix(ref camera_matrix);
foreach (var obj in farobjects)
{
GL.PushMatrix();
GL.Translate(obj.Location);
obj.Render(camera);
GL.PopMatrix();
}
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
foreach (var obj in nearobjects)
{
GL.PushMatrix();
GL.LoadIdentity();
GL.Translate(obj.Location);
obj.Render(camera);
GL.PopMatrix();
}
Any ideas on what I'm doing wrong? The object I placed does not appear at all. I tried moving it to (1,0,1), (0,0,1) and (1,0,0) and the object is nowhere to be found.
I finally found the solution. I had to create a second camera matrix for this and apply it, as well as flush the z buffer.
camera_matrix = camera.Matrix;
GL.ClearColor(Color.White);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadMatrix(ref camera_matrix);
foreach (var obj in farobjects)
{
GL.PushMatrix();
GL.Translate(obj.Location);
obj.Render(camera);
GL.PopMatrix();
}
GL.PopMatrix();
GL.Clear(ClearBufferMask.DepthBufferBit);
GL.PushMatrix();
Matrix4 modelview = Matrix4.LookAt(Vector3.Zero, Vector3.UnitZ, Vector3.UnitY);
GL.LoadMatrix(ref modelview);
foreach (var obj in nearobjects)
{
GL.PushMatrix();
GL.Translate(obj.Location);
obj.Render(camera);
GL.PopMatrix();
}
GL.PopMatrix();
Related
I am working on a 2D menu for a game using OpenTK in C#.
At the moment, the menu is separated in 3 different texture quads, or 'layers', which looks like this.
Layer 1: The base appearance of the buttons.
Layer 2: The appearance of the 'continue' button when the mouse hovers over it.
Layer 3: A 'gear' which holds the buttons, as well as their text/name.
Each of these layers consists of a semi-transparent (32-bit .png) texture bound to a Quad.
When drawing only layers 1 & 3, the textures seem to work properly, but when I want to show Layer 2 as well, layer 3 disappears from my screen, as seen here. In this image, only my base buttons (layer 1) and highlighted 'continue' button (layer 2) are drawn.
I believe this is an issue with my blend function, and the fact that I am drawing more than 2 sprites. I am relatively new to OpenTK/OpenGL, and I was hoping someone here could help me fix this problem. I will add some of my code below:
When the game starts, I set up some GL properties:
GL.Enable(EnableCap.DepthTest);
GL.Enable(EnableCap.CullFace);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend); //I migh t be missing something here, like textEnv?
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
This part of the code draws my 2D elements. I took it from a tutorial on OpenTK.
public void DrawHUD()
{
// Clear only the depth buffer, so that everything
// we draw for the HUD will appear in front of the
// world objects.
GL.Clear(ClearBufferMask.DepthBufferBit);
// Reset the ModelView matrix so the following
// objects are not affected by the camera position
GL.LoadIdentity();
////draw 3d hud elements (weapon in the main game)
// Disable lighting for the HUD graphics
// GL.Disable(EnableCap.Lighting);
// Save the current perspective projection
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
// Switch to orthogonal view
GL.LoadIdentity();
GL.Ortho(0, Width, 0, Height, -1, 1); // Bottom-left corner pixel has coordinate (0, 0)
// Go back to working with the ModelView
GL.MatrixMode(MatrixMode.Modelview);
//// Draw the HUD elements
GameEngine.Draw2D();
// Switch back to perspective view
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
// Turn lighting back on
// GL.Enable(EnableCap.Lighting);
}
I add the menu 'layers' to an arraylist, which I then use to draw using Graphic, a class which holds the position/texture info of my 'layer'
public virtual void Draw2D()
{
Vector2 pos;
int Z = 0; //to fight z-fighting? this fixed an issue where drawing a 2nd sprite would also make the 1st one dissapear partially
foreach (Graphic G in _2DList)
{
if (G.visible())
{
pos = G.position();
Renderer.DrawHUDSprite(pos.X, pos.Y, Z, G.W(), G.H(), G.Texture());
Z++;
}
}
}
Finally, my draw function for hudsprites looks like this:
public static void DrawHUDSprite(float x, float y, float z, float width, float height, int textID)
{
// Save the ModelView matrix
GL.PushMatrix();
// Move to the correct location on screen
GL.Translate(x, y, z);
GL.BindTexture(TextureTarget.Texture2D, textID);
// Setup for drawing the texture
GL.Color3(Color.White);
// Draw a flat rectangle
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(0, 0, 0);
GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(width, 0, 0);
GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(width, height, 0);
GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(0, height, 0);
GL.End();
// Restore the ModelView matrix
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.PopMatrix();
}
If this has anything to do with the way I load my textures, I will add the code for this as well.
I managed to solve my issue on my own: The problem lies with my draw2D() function, where I use a Z coordinate to prevent clipping issues. The Z-coordinate when drawing in 2D could only be in the range of [0..1].
My earlier solution which increments Z by 1 (Z++) would cause issues with more than 2 textures/Graphics (Z>1, meaning the quad is not displayed). The fixed version looks like this:
public virtual void Draw2D()
{
if (_2DList.Count > 0) { //pervents dividing by 0, and skips memory allocation if we have no Graphics
Vector2 pos;
float Z = 0; //to fight z-fighting, the sprites ar drawn in the order they were added.
float step = 1.0f / _2DList.Count; //limiting the Z between [0..1]
foreach (Graphic G in _2DList)
{
if (G.visible())
{
pos = G.position();
Renderer.DrawHUDSprite(pos.X, pos.Y, Z, G.W(), G.H(), G.Texture());
Z += step; //with z starting at 0, it will never be 1.
}
}
}
}
I'm using OpenTK and C#, I have defined a plane in 3D space as follows:
GL.Begin(BeginMode.Quads);
GL.Color3(Color.Magenta);
GL.Vertex3(-100.0f, -25.0f, -150.0f);
GL.Vertex3(-100.0f, -25.0f, 150.0f);
GL.Vertex3( 200.0f, -25.0f, 100.0f);
GL.Vertex3( 200.0f, -25.0f, -100.0f);
GL.End();
Can anyone please help me to make the plane transparent?
So you want something like this?
There are a lot of things to take care of to get there.
It all starts with a Color object that contains an alpha value<255. For example Color.FromArgb(85, Color.Turquoise) for the sphere below.
The main render class, sets up the camera view and renders all the lights, and then renders all the objects in the scene:
public void RenderOnView(GLControl control)
{
control.MakeCurrent();
var camera=views[control];
GL.Clear(ClearBufferMask.ColorBufferBit|ClearBufferMask.DepthBufferBit);
GL.Disable(EnableCap.CullFace);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
camera.LookThrough();
if (EnableLights)
{
GL.LightModel(LightModelParameter.LightModelAmbient, new[] { 0.2f, 0.2f, 0.2f, 1f });
GL.LightModel(LightModelParameter.LightModelLocalViewer, 1);
GL.Enable(EnableCap.Lighting);
foreach (var light in lights)
{
light.Render();
}
}
else
{
GL.Disable(EnableCap.Lighting);
GL.ShadeModel(ShadingModel.Flat);
}
GL.Enable(EnableCap.LineSmooth); // This is Optional
GL.Enable(EnableCap.Normalize); // These is critical to have
GL.Enable(EnableCap.RescaleNormal);
for (int i=0; i<objects.Count; i++)
{
GL.PushMatrix();
objects[i].Render();
GL.PopMatrix();
}
control.SwapBuffers();
}
Then each object has a base rendering code Render(), which calls more specialized code Draw()
public void Render()
{
if (Shading==ShadingModel.Smooth)
{
GL.Enable(EnableCap.ColorMaterial);
GL.ColorMaterial(MaterialFace.FrontAndBack, ColorMaterialParameter.AmbientAndDiffuse);
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, SpecularColor);
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, EmissionColor);
GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, Shinyness);
GL.Enable(EnableCap.Lighting);
}
else
{
GL.Disable(EnableCap.ColorMaterial);
GL.Disable(EnableCap.Lighting);
}
GL.ShadeModel(Shading);
GL.Translate(Position);
GL.Scale(Scale, Scale, Scale);
Draw(); // Draws triangles and quads to make up a shape
}
and for example to draw a quad surface you have
protected void DrawQuad(Color color, params Vector3[] nodes)
{
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
GL.Enable(EnableCap.PolygonOffsetFill);
// special code when translucent
if (color.A<255)
{
GL.Enable(EnableCap.Blend);
GL.DepthMask(false);
}
GL.Begin(PrimitiveType.Quads);
GL.Color4(color); //this is where the color with alpha is used
for (int i=0; i<nodes.Length; i++)
{
GL.Vertex3(nodes[i]);
}
GL.End();
// special code when translucent
if (color.A<255)
{
GL.Disable(EnableCap.Blend);
GL.DepthMask(true);
}
}
also the code to draw the outline of a quad to be called after DrawQaud()
protected void DrawLineLoop(Color color, params Vector3[] nodes)
{
GL.Disable(EnableCap.PolygonOffsetFill);
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
if (color.A<255)
{
GL.Enable(EnableCap.Blend);
GL.DepthMask(false);
}
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(color);
for (int i=0; i<nodes.Length; i++)
{
GL.Vertex3(nodes[i]);
}
GL.End();
if (color.A<255)
{
GL.Disable(EnableCap.Blend);
GL.DepthMask(true);
}
}
Finally I found solution of my question:
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One);
GL.Enable(EnableCap.Blend);
//Definition of Plane
GL.Begin(BeginMode.Quads);
GL.Color4(0, 0.2, 1, 0.5);
GL.Vertex3(-100.0f, -25.0f, -150.0f);
GL.Vertex3(-100.0f, -25.0f, 150.0f);
GL.Vertex3( 200.0f, -25.0f, 100.0f);
GL.Vertex3( 200.0f, -25.0f, -100.0f);
GL.End();
GL.Disable(EnableCap.Blend);
In computing, Transparency effects are fained using color blending.For The special case of transprency, we talk about Àlpha Blending`
For transparency the blending factor is usualy stored in the 4th component of the colour (the A in RGBA) which stands for alpha. So you have to set all your colors with it.
example for half transparent blue (like glass):
GL.Color4(0,0,1,0.5f);
In OpenGL, blending have to be activated with the following command, which enable a supplementary stage on the rendering pipeline.
GL.Enable( EnableCap.Blend );
Then, because blending could be used to mix colors for other purpose than transparency, you have to specify the blending function to use. Here is the common function for transparency:
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
http://www.opentk.com/doc/chapter/2/opengl/fragment-ops/blending
Simply use GL.Color4 instead of GL.Color3. The 4th value will be the alpha
I have a sphere as FBX and a shader that enables me to have a texture inside of the sphere. Also inside of the sphere I have a camera. The goal is to have a simple panoramic viewer that is being driven by a headtracker.
The texture is being shown but the camera is reacting in a weird way:
When rotating the headtracker, the image is all over the place, jittery and not at all correlated to the actual movements. The headtracker input itself is correct, though.
See this video for further clarification. Each time I keep the tracker steady at first and then I move it. In the second half, the first three numbers are evaluated. The other three values are gyro.
I presume the problem to be in the following code, something about the Vectors(Vector3.whatever) could be off:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);
foreach (ModelMesh mesh in skyDome.Meshes)
{
foreach (BasicEffect ef in mesh.Effects)
{
float aspectRatio = (float)graphics.GraphicsDevice.PresentationParameters.BackBufferWidth / (float)graphics.GraphicsDevice.PresentationParameters.BackBufferHeight;
Vector3 camPosition = Vector3.Transform(Vector3.Up, Quaternion.CreateFromYawPitchRoll(drehung, neigung, rollen));
ef.View = Matrix.CreateLookAt(Vector3.Zero, camPosition,Vector3.Forward);
ef.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, 0.1f, 10000.0f);
ef.TextureEnabled = true; //Textur zulassen
ef.Texture = panoramaTextur; //Textur auf Model darstellen
}
mesh.Draw();
}
base.Draw(gameTime);
}
I have a project due very soon and I'm having lots of issues trying to load a model I made in 3D Studio Max. The model I made is what I want as my terrain in my XNA game. Here is what I've done so far:
Methods:
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Model terrainModel;
Vector3 terrainPosition = Vector3.Zero;
Vector3 cameraPosition = new Vector3(0.0f, 60.0f, 160.0f);
Vector3 cameraLookAt = new Vector3(0.0f, 60.0f, 160.0f);
Matrix cameraProjectionMatrix;
Matrix cameraViewMatrix;
LoadContent()
spriteBatch = new SpriteBatch(GraphicsDevice);
cameraViewMatrix = Matrix.CreateLookAt(
cameraPosition,
Vector3.Zero,
Vector3.Up);
cameraProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(80.0f),
graphics.GraphicsDevice.Viewport.AspectRatio,
1.0f,
1000.0f);
terrainModel = Content.Load<Model>("alphastreet");
Draw(GameTime gameTime)
GraphicsDevice.Clear(Color.CornflowerBlue);
DrawModel(terrainModel, terrainPosition);
// TODO: Add your drawing code here
base.Draw(gameTime);
And then I want to Draw:
void DrawModel(Model model, Vector3 modelPosition)
{
foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;
effect.World = Matrix.CreateTranslation(modelPosition);
effect.Projection = cameraProjectionMatrix;
effect.View = cameraViewMatrix;
}
mesh.Draw();
}
}
Everything else is just as an XNA file should look. The model it's self looks like a fairly straightforward street. However, upon XNA loading the model, it's just a big block in the window that loads.
I don't know what I'm doing that's making the model load like this, but it's making me pull my hair out. Any help would be appreciated.
Also, would anybody be able to direct me to walk-through/guide or a class that creates a First Person Shooter camera, since that is the game I'm going. I probably shouldn't have picked that, but it's way too late to change now, so if you could help there too you would really be saving my skin!
Thanks, Jake!
Is there more than one mesh in the model, and if so, have you tried rendering each one separately?
Have you tried using identity matrices for world and view to make sure these aren't the issue?
I assume you haven't made any other state changes to xna/directx, if so try disabling these.
For a FPS camera, you can just track the x and y rotation in float variables and update these each frame based upon how far the mouse has moved in x and y. From these you can generate a camera view matrix; something like the following:
var mx = Matrix.CreateRotationX(xangle);
var my = Matrix.CreateRotationY(yangle);
var pos = Matrix.CreateTranslation(position);
var view = mx * my * pos; // These might be in the wrong order, not sure off the top of my head
view.Invert();
The camera is always inverted because moving left moves the world right, turning left turns the world right etc.
I am new to XNA, and I am creating a simple game. Sorry that this is probably really simple, but I can't find any help on it. There is a ship in the game that I made with Blender, and I want to be able to control the ship by being able to rotate the ship's X, Y and Z axises. Here is the code I have:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
RotationMatrix = Matrix.CreateRotationY(MathHelper.PiOver2) * Matrix.CreateRotationY (rotationY) * Matrix.CreateRotationX(rotationX) * Matrix.CreateRotationZ(rotationZ);
Matrix shipTransformMatrix = RotationMatrix * Matrix.CreateTranslation(ship.Position);
DrawModel(ship.Model, shipTransformMatrix, ship.Transforms);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
public void DrawModel(Model model, Matrix modelTransform, Matrix[] absoluteBoneTransforms)
{
//Draw the model, a model can have multiple meshes, so loop
foreach (ModelMesh mesh in model.Meshes)
{
//This is where the mesh orientation is set
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = absoluteBoneTransforms[mesh.ParentBone.Index] * modelTransform;
effect.Projection = projectionMatrix;
effect.View = viewMatrix;
}
//Draw the mesh, will use the effects set above.
mesh.Draw();
}
}
This will rotate the ship, but not along the ship's axis's. If I rotate the Y axis (by changing the value of rotationY), the ship will rotate along its Y axis. But if I rotate the X or Z axis, the ship rotates according according to the world's X and Z axises, not its own. How do I make it so the ship rotates on its own axises? Do I need to do something different with the matrices?
Thanks
Using CreateRotationX, CreateRotationY, & CreateRotationZ all apply rotations around the world or global axes. Meaning it causes your object to rotate only around the world/global axes, not your object's local axes.
Using CreateFromAxisAngle allows you to input whatever rotation axis you want, including the ship's own local axes.
A shift in your overall rotation paradigm will be needed, however, since a rotation around an arbitrary axis like the ship's Up, for instance, can cause a change to any of the 3 angle values at once. Keeping track of all that is unnecessarily difficult. There is an easier way:
Simply store a rotation in matrix (or quaternion) form instead of the 3 angles.
EDIT: Giving some credit here to Steve below (great answer mate, been a while since I did alot of 3D math).
This tutorial here will show you how to setup what Steve suggested!
http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series1/Rotation_-_translation.php
Original Post:
I believe you have to create an effect.Rotation in your BasicEffect loop.
All of this is covered in the basic tutorials over at MSDN I believe. Your code even looks like it came from there.
http://msdn.microsoft.com/en-us/library/bb203897(v=xnagamestudio.31)
If not, check out this link, Reimer covers everything in 3D worth knowing to start:
http://www.riemers.net/eng/Tutorials/XNA/Csharp/series1.php
Here is what I ended up doing just in case anyone else gets stuck like I did:
Matrix RotationMatrix;
//every time I wanted to rotate around an axis, I would do something like this:
protected void rotateY()
{
RotationMatrix *= Matrix.CreateFromAxisAngle(RotationMatrix.Up, MathHelper.ToRadians(1.0f));
//For the X axis I used RotationMatrix.Right, and for the Z RotationMatrix.Forward
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
Matrix shipTransformMatrix = RotationMatrix * Matrix.CreateTranslation(ship.Position);
DrawModel(ship.Model, shipTransformMatrix, ship.Transforms);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
public void DrawModel(Model model, Matrix modelTransform, Matrix[] absoluteBoneTransforms)
{
//Draw the model, a model can have multiple meshes, so loop
foreach (ModelMesh mesh in model.Meshes)
{
//This is where the mesh orientation is set
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = absoluteBoneTransforms[mesh.ParentBone.Index] * modelTransform;
effect.Projection = projectionMatrix;
effect.View = viewMatrix;
}
//Draw the mesh, will use the effects set above.
mesh.Draw();
}
}