I probably do something really stupid so go ahead and correct me.
I'm trying to make vertex move so...
In OnLoad I'm setting the VBO like you see.
In OnFrameRender I'm trying to edit vertex then i do drawArrays.
But i can't get this changes be visible in OpenTk window. Doing ConsoleWrite(prymi.vertOne) shows me that struct is edited (i do get sin from 0 to 20) so I'm assuming that this values are not updated on GPU. I tryed every VBO BufferUsageHint (Dynamic/Static/Stream(Copy/Draw/Read)) and this is not the case. I have read that i should use BufferSubData (i don't know about this concept yet(so any help would be helpful) but editing the object which is passed as reference with VBO StreamDraw should also do it... or em i wrong?
Thanks all :)
struct TrianglePrymi
{
public Vector2 vertOne;
public Vector2 vertSec;
public Vector2 VertThird;
public TrianglePrymi(Vector2 fi, Vector2 sec, Vector2 thi )
{
vertOne = fi;
vertSec = sec;
VertThird = thi;
}
public void edit(Vector2 a)
{
vertOne = a;
}
public static int retSize()
{
return (3 * Vector2.SizeInBytes);
}
}
protected override void OnLoad(EventArgs e)
{
prymi = new TrianglePrymi(new Vector2(20,20),new Vector2(300,200), new Vector2(530,420));
ttttt = TrianglePrymi.retSize();
VBO = GL.GenBuffer();
GL.BindVertexArray(VAO);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)ttttt,ref prymi, BufferUsageHint.DynamicDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
base.OnLoad(e);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
Console.WriteLine(prymi.vertOne[0]);
//Console.WriteLine(prymi.vertSec);
//Console.WriteLine(prymi.VertThird);
magicNumber += 1.00f;
float x = Math.Abs(20 * (float)Math.Sin(magicNumber));
float y = Math.Abs(20 * (float)Math.Cos(magicNumber));
GL.EnableClientState(ArrayCap.VertexArray);
GL.Clear(ClearBufferMask.ColorBufferBit|ClearBufferMask.DepthBufferBit);
GL.ClearColor(Color.Gray);
prymi.edit(new Vector2(x, y));
Vector2 vec = new Vector2(magicNumber, 450);
Matrix4 world = Matrix4.CreateOrthographicOffCenter(0, widX, 0, heiY, 0, 2000);
GL.LoadMatrix(ref world);
GL.Color3(Color.Yellow);
prymi.vertOne = vec;
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
GL.VertexPointer(2, VertexPointerType.Float,Vector2.SizeInBytes, 0);
GL.DrawArrays(BeginMode.Triangles, 0, 3);
GL.End();
GL.Flush();
base.OnRenderFrame(e);
this.SwapBuffers();
}
Related
I'm learning OpenGL with the openTK librairy in c#.
I've managed to successefuly renderer triangles on my window,
and things are running properly.
However, the performance is terrible (running at half a fps with only 100k triangles).
My question is, is there anything that I am doing badly ?
how could I improve performances ?
for the most part, I've followed this tutorial translated from c++.
Here is the render method, called each frame :
public static void Render()
{
//render the mesh render queue to the screen and clear it
GL.UseProgram(shaderProgram.id);
//meshRenderQueue is a List<Mesh>
foreach(Mesh mesh in meshRenderQueue)
{
mesh.Draw();
}
meshRenderQueue.Clear();
}
Here is my Mesh class :
public class Mesh
{
private Vertex[] vertices;
private uint[] triangles;
private int VAO;
private int VBO;
private int EBO;
public Mesh()
{
//create lots of unoptimized triangles to test performances
int triCount = 100000;
vertices = new Vertex[3 * triCount];
for(int i = 0; i < triCount; i++)
{
vertices[3 * i + 0] = new Vertex(new Vector3(-0.5f, -0.5f, 0f));
vertices[3 * i + 1] = new Vertex(new Vector3(0.5f, -0.5f, 0f));
vertices[3 * i + 2] = new Vertex(new Vector3(0, 0.5f, 0f));
};
triangles = new uint[3 * triCount];
for (int i = 0; i < triCount; i++)
{
triangles[3 * i + 0] = 0;
triangles[3 * i + 1] = 1;
triangles[3 * i + 2] = 2;
};
SetUpMesh();
}
private void SetUpMesh()
{
//initialize the openGL buffers
VAO = GL.GenVertexArray();
VBO = GL.GenBuffer();
EBO = GL.GenBuffer();
GL.BindVertexArray(VAO);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * 8 * 4, vertices, BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, EBO);
GL.BufferData(BufferTarget.ElementArrayBuffer, triangles.Length * sizeof(int), triangles, BufferUsageHint.StaticDraw);
//vertex positions
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 8 * 4, 0);
//vertex normals
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 8 * 4, 3 * 4);
//vertex texture coords
GL.EnableVertexAttribArray(2);
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 8 * 4, 6 * 4);
//unbind vertex array (what for ?)
GL.BindVertexArray(0);
}
public void Draw()
{
//render the mesh with the given shader
GL.BindVertexArray(VAO);
GL.DrawElements(BeginMode.Triangles, triangles.Length, DrawElementsType.UnsignedInt, 0);
GL.BindVertexArray(0);
}
}
And finally the Vertex struct, if usefull :
public struct Vertex
{
public Vector3 position;
public Vector3 normal;
public Vector2 textureCoords;
public Vertex(Vector3 pos, Vector3 norm, Vector2 textCoords)
{
position = pos;
normal = norm;
textureCoords = textCoords;
}
public Vertex(Vector3 pos)
{
position = pos;
normal = Vector3.zero;
textureCoords = new Vector2(0, 0);
}
}
Thanks in advance for any help. cheers !
I am trying to understand and apply modern OpenGL matrix transformations. I already read a lot of different sources but I am not sure what I am actually doing wrong.
The issue I have is also commented in the code: If I set the eye coordinates of the Matrix4.LookAt to a z value that is greater or equal 0 or lower -2 the triangle is not visible anymore.
Can someone explain why? As far as I understood the method the triangle should just be visible just from the other side (explicitly disabling face culling does not change anything).
Another thing is strange: if i rotate the triangle it seems to get clipped if I use eye-z = -2; if I use -1 it looks "smoother". Any ideas?
Here is the complete program:
using System;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL4;
namespace OGL420_Matrices
{
// OpenTK version 3.1.0
internal class Program
{
public static void Main(string[] args)
{
var program = new Program();
program.Run();
}
private GameWindow _gameWindow;
private Matrix4 _projectionMatrix;
private Matrix4 _viewMatrix;
private Matrix4 _viewProjectionMatrix;
private Matrix4 _modelMatrix;
private int _vbaId, _programId, _viewProjectionUniformId, _modelMatrixUniformId;
private void Run()
{
// 4, 2 is OpenGL 4.2
using (_gameWindow = new GameWindow(800, 600, GraphicsMode.Default, "", GameWindowFlags.Default,
DisplayDevice.Default, 4, 2, GraphicsContextFlags.Default))
{
_gameWindow.Load += OnLoad;
_gameWindow.Resize += OnResize;
_gameWindow.RenderFrame += OnRenderFrame;
_gameWindow.Run();
}
}
private void OnResize(object sender, EventArgs e)
{
var clientArea = _gameWindow.ClientRectangle;
GL.Viewport(0, 0, clientArea.Width, clientArea.Height);
}
private void OnLoad(object sender, EventArgs e)
{
_projectionMatrix = Matrix4.CreateOrthographic(3, 3, 0.001f, 50);
// change -1 to -2.1f you dont see anything
// change -1 to -2f you still see the same
// change -1 to >= 0 you dont see anything; of course 0 doesn't make sense but 1 would
_viewMatrix = Matrix4.LookAt(
new Vector3(0, 0, -1f), // eye
new Vector3(0, 0, 0), // target
new Vector3(0, 1, 0)); // up
_modelMatrix = Matrix4.Identity;
var data = new float[]
{
0, 0, 0,
1, 0, 0,
0, 1, 0
};
var vboId = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, vboId);
GL.BufferData(BufferTarget.ArrayBuffer, data.Length * sizeof(float), data, BufferUsageHint.StaticDraw);
_vbaId = GL.GenVertexArray();
GL.BindVertexArray(_vbaId);
GL.BindBuffer(BufferTarget.ArrayBuffer, vboId);
GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 0, 0);
var vertexShaderId = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertexShaderId, #"#version 420
layout(location = 0) in vec3 position;
uniform mat4 viewProjection;
uniform mat4 model;
out vec3 outColor;
void main()
{
gl_Position = viewProjection * model * vec4(position, 1);
outColor = vec3(1,1,1);
}");
GL.CompileShader(vertexShaderId);
GL.GetShader(vertexShaderId, ShaderParameter.CompileStatus, out var result);
if (result != 1)
throw new Exception("compilation error: " + GL.GetShaderInfoLog(vertexShaderId));
var fragShaderId = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragShaderId, #"#version 420
in vec3 outColor;
out vec4 fragmentColor;
void main()
{
fragmentColor = vec4(outColor, 1);
}");
GL.CompileShader(fragShaderId);
GL.GetShader(fragShaderId, ShaderParameter.CompileStatus, out result);
if (result != 1)
throw new Exception("compilation error: " + GL.GetShaderInfoLog(fragShaderId));
_programId = GL.CreateProgram();
GL.AttachShader(_programId, vertexShaderId);
GL.AttachShader(_programId, fragShaderId);
GL.LinkProgram(_programId);
GL.GetProgram(_programId, GetProgramParameterName.LinkStatus, out var linkStatus);
if (linkStatus != 1) // 1 for true
throw new Exception("Shader program compilation error: " + GL.GetProgramInfoLog(_programId));
GL.DeleteShader(vertexShaderId);
GL.DeleteShader(fragShaderId);
_viewProjectionUniformId = GL.GetUniformLocation(_programId, "viewProjection");
_modelMatrixUniformId = GL.GetUniformLocation(_programId, "model");
}
private void OnRenderFrame(object sender, FrameEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit);
_viewProjectionMatrix = _projectionMatrix * _viewMatrix;
GL.UniformMatrix4(_viewProjectionUniformId, false, ref _viewProjectionMatrix);
GL.UniformMatrix4(_modelMatrixUniformId, false, ref _modelMatrix);
GL.UseProgram(_programId);
GL.BindVertexArray(_vbaId);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
_gameWindow.SwapBuffers();
}
}
}
First I'll quote a comment to the OpenTK issue: Problem with matrices #687:
Because of how matrices are treated in C# and OpenTK, multiplication order is inverted from what you might expect in C/C++ and GLSL. This is an old artefact in the library, and it's too late to change now, unfortunately.
In compare to glsl, where column major order matrices have to be multiplied form the right to the left, where the right matrix is the matrix which is applied "first", in OpenTK the matrices have to be multiplied from the left to the right.
This means, if you want to calculate the viewProjectionMatrix in glsl, which does the view transformation followed by the projection, then in glsl it is (for column major order matrices):
mat4 viewProjectionMatrix = projectionMatrix * viewMatrix;
If you want to do the same in in OpenTK, by the use of Matrix4, then you've to do:
_viewProjectionMatrix = _viewMatrix * _projectionMatrix;
I'm running a C# project with the Pencil.Gaming library, which is the C# binding of GLFW which uses OpenTK to wrap OpenGL functions. I am trying to draw an array of Vertices in a vbo, and once I get to GL.DrawArrays, it gives me a System.AccessViolationException and an Invalid Operation error when I call GL.GetError(). Below is my code for the Mesh class and a screencap of the error is Visual Studio. The object I am trying to draw and a copy of my Vertex class is also below.
static Mesh mesh;
mesh = new Mesh();
Vertex[] data = new Vertex[] {new Vertex(new Vector3(-1,-1,0)),
new Vertex(new Vector3(0,1,0)),
new Vertex(new Vector3(1,-1,0))};
mesh.addVertices(data);
mesh.draw();
The Vertex Class:
struct Vertex
{
public static readonly int SIZE = 3;
private Vector3 position;
public Vertex(Vector3 position)
{
this.position = position;
}
}
The Mesh class:
using System;
using Pencil.Gaming.Graphics;
using Pencil.Gaming.MathUtils;
namespace Practicalis.Rendering
{
class Mesh
{
private int size;
private int vbo;
public Mesh()
{
size = 0;
GL.GenBuffers(size, out vbo);
}
public void addVertices(Vertex[] vertices)
{
size = vertices.Length * sizeof(float);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)size, (IntPtr)vbo, BufferUsageHint.StaticDraw);
}
public void draw()
{
GL.EnableVertexAttribArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, Vertex.SIZE * 4, 0);
Console.WriteLine(GL.GetError());
GL.DrawArrays(BeginMode.Triangles, 0, size);
GL.DisableVertexAttribArray(0);
}
}
}
GL.GenBuffers(size, out vbo);
Before this line you have size 0. It should be at least one since thats the number of buffers to be generated.
In the add vertice function, I wasn't passing in my actual Vertex array, just the pointer to vbo. The proper function is below.
public void addVertices(Vertex[] vertices)
{
size = vertices.Length * Vector3.SizeInBytes;
Vector3[] vertsData = new Vector3[vertices.Length];
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
for (int i = 0; i < vertices.Length; i++)
vertsData[i] = vertices[i].Position;
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)size, vertsData, BufferUsageHint.StaticDraw);
}
I have a utility class for Framebuffer objects which i use to generate FBO's, bind them, render to them, and render them to screen as 2d textures.
When using this class, i can successfully make fbo textures, render to them, and render them to screen.
However, the colors seem messed up once i render them to screen.
What seems to be happening is that the color i set as drawing color ends up as the background color of the FBO (but only if i make an actual draw call after setting the color), and the drawing i made always ends up black.
What i am expecting based on my code, is a texture with a white background, and two small colored rectangles (one purple and one cyan) on them, drawn to the screen two times.
what i am getting is this :
a cyan background with two small black rectangles.
Source code follows :
FboRenderTexture.cs
public class FboRenderTexture : IDisposable
{
public int textureId = 0;
private int fboId = 0;
public int Width;
public int Height;
public FboRenderTexture(int width, int height)
{
Width = width;
Height = height;
Init();
}
//semi pseudocode
public void DrawToScreen(float xoffset = 0, float yoffset = 0)
{
if (textureId != -1)
{
GL.BindTexture(TextureTarget.Texture2D, textureId);
GL.Begin(BeginMode.Quads);
//todo : might also flip the texture since fbo's have right handed coordinate systems
GL.TexCoord2(0.0, 0.0);
GL.Vertex3(xoffset, yoffset, 0.0);
GL.TexCoord2(0.0, 1.0);
GL.Vertex3(xoffset, yoffset+Height, 0.0);
GL.TexCoord2(1.0, 1.0);
GL.Vertex3(xoffset+Width, yoffset+Height, 0.0);
GL.TexCoord2(1.0, 0.0);
GL.Vertex3(xoffset+Width, yoffset, 0.0);
GL.End();
}
}
private void Init()
{
// Generate the texture.
textureId = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, textureId);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
// Create a FBO and attach the texture.
GL.Ext.GenFramebuffers(1, out fboId);
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, textureId, 0);
// Disable rendering into the FBO
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
}
// Track whether Dispose has been called.
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
// Clean up what we allocated before exiting
if (textureId != 0)
GL.DeleteTextures(1, ref textureId);
if (fboId != 0)
GL.Ext.DeleteFramebuffers(1, ref fboId);
disposed = true;
}
}
}
public void BeginDrawing()
{
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, fboId);
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
GL.PushAttrib(AttribMask.ViewportBit);
GL.Viewport(0, 0, Width, Height);
}
public void EndDrawing()
{
GL.PopAttrib();
GL.Ext.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); // disable rendering into the FBO
}
}
usage
public class Buffers : CreativeSharp.core.CSInstance //inherits from OpenTK.GameWindow, adds some simple drawing stuff.
{
private FboRenderTexture buffer;
public Buffers() : base(800, 600, "buffers")
{
GL.Enable(EnableCap.Texture2D);
buffer = new FboRenderTexture(128, 128);
NoStroke();
this.Closed += Buffers_Closed;
}
private void SetupBuffer()
{
buffer.BeginDrawing();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit);
GL.ClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GL.Color4(1f, 0f, 1f, 0f);
DrawRectangle(10,10, 30, 30);
GL.Color4(0f, 1f, 1f, 1f);
DrawRectangle(50, 10, 30, 30);
GL.Color4(1f, 1f, 1f);
buffer.EndDrawing();
}
void Buffers_Closed(object sender, EventArgs e)
{
buffer.Dispose();
}
public override void DrawContent()
{
SetupBuffer();
GL.ClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GL.Color4(1f, 1f, 1f, 1f);
buffer.DrawToScreen(0,0);
buffer.DrawToScreen(512,0);
}
public override void Update()
{
}
private void DrawRectangle(float x, float y, float w, float h)
{
GL.Begin(PrimitiveType.Quads);
GL.Vertex3(x, y, 0);
GL.Vertex3(x + w, y, 0);
GL.Vertex3(x + w, y + h, 0);
GL.Vertex3(x, y + h, 0);
GL.End();
}
}
Somewhat embarrassingly, the cause of my problems was that i was not setting white as drawing color before drawing the fbo's texture.
This caused the colors to come out all wrong.
I have this code:
public class Area
{
Texture2D point;
Rectangle rect;
SpriteBatch _sB;
GameTimer _gt;
int xo, yo, xt, yt;
//List<Card> _cards;
public Area(Texture2D point, SpriteBatch sB)
{
this.point = point;
this._sB = sB;
xt = 660;
yt = 180;
xo = 260;
yo = 90;
}
public void Draw(SpriteBatch spriteBatch)
{
rect = new Rectangle(660, 180, 80, 120);
spriteBatch.Draw(point, rect, Color.White);
_gt = new GameTimer();
_gt.UpdateInterval = TimeSpan.FromSeconds(0.1);
_gt.Draw += OnDraw;
}
private void OnDraw(object sender, GameTimerEventArgs e)
{
this.pass(xo, yo);
if (xo != xt) xo += (xt > xo) ? 10 : -10;
if (yo != yt) yo += (yt > yo) ? 10 : -10;
}
public void pass(int x, int y)
{
rect = new Rectangle(x, y, 80, 120);
_sB.Draw(point, rect, Color.Black);
}
}
So, I can't understand what's wrong. And It's my first project with XNA, and because of it there can be stupid mistake :)
P.S. Sorry. There is a rectangle with coordinates (xt,yt), and I need the animation to move the rectangle to (xo,yo)
P.P.S. I added the full class with corrections, because I don't understand my mistake.
You are drawing the entire animation in one frame.. .you should call Pass with diferent x,y from OnDraw...
EDITED:
1) You don't need the timer, the draw method in game class is by default called 60 frames per second...
2) The Seconds parameter should be calculated as (float) gametime.ElapsedTime.TotalSeconds;
float time;
int xt=660, yt=180;
int xo=260, yo=90;
public void Draw(SpriteBatch spriteBatch, float Seconds)
{
rect = new Rectangle(660, 180, 80, 120);
spriteBatch.Draw(point, rect, Color.White);
this.pass(xo, yo, spriteBatch);
time+= Seconds;
if (time>0.3)
{
if (xo!=xt) xo+= (xt>xo) ? 10 : -10;
if (yo!=yt) yo+= (yt>yo) ? 10 : -10;
time = 0;
}
}
public void pass(int x, int y, spritebatch sb)
{
rect = new Rectangle(x, y, 80, 120);
sb.Draw(point, rect, Color.Red);
}
As you should know this animation will move in a rough mode... if you want to move your sprite smoothly... you can use a Vector2 for your positions and a float for your speed;
Vector2 Origin = new Vector2(260, 90);
Vector2 Target = new Vector2(660, 180);
Vector2 Forward = Vector2.Normalize(Target-Source);
float Speed = 100; // Pixels per second
float Duration = (Target - Origin).Length() / Speed;
float Time = 0;
public void Update(float ElapsedSecondsPerFrame)
{
if (Time<Duration)
{
Time+=Duration;
if (Time > Duration) {
Time = Duration;
Origin = Target;
}
else Origin += Forward * Speed * ElapsedSecondsPerFrame;
}
}
public void Draw()
{
rect = new Rectangle((int) Origin.X, (int) Origin.Y, 80, 120);
sb.Draw(point, rect, Color.Red);
}
If you are willing to use Sprite Vortex to make your animations (a specific version actually) you can use the following class. You have to use Sprite Vortex 1.2.2 because in the newer versions the XML format is changed. Make sure that the XML file you add the property is changed to "Do not compile".
If you need a working example I can send you a very simple one.
p.s. Sprite Vortex should do the same thing you use the other program for, however v 1.2.2 is pretty buggy but not too bad.
the class is here : http://pastebin.com/sNSa7xgQ
Use Sprite Vortex (make sure it's 1.2.2) to select a spritesheet and select the sub images you want to animate. export the XML code.
add the class to your project, it reads the XML and adds automatically creates the frames for the animation for you.