Hello! I’m new to OpenGL programming and graphics in general. Recently I faced with some problem: I wanted to draw images (as a fullscreen tilemap). Firstly I used System.Drawing (as C# is the only language I know decently), but drawing 32x32 tiles on 800x600 display resulted extremly low fps (1-2), so I switched to OpenTK library. As I said, im only a beginner, so my code (based on tutorials and first chapters of books) is obviously not effective.
Drawing events: http://pastebin.com/rduEMv2L
// GLControl Events
protected override void OnGLControlLoad(object sender, EventArgs e)
{
base.OnGLControlLoad(sender, e);
_sp = ShaderProgram.LoadShader("default");
_sp.Use();
_sprite = new Sprite("tile");
_sprite.Initialize();
}
protected override void OnGLControlPaint(object sender, EventArgs e)
{
//GL.ClearColor(Color.Black);
GL.Clear(ClearBufferMask.ColorBufferBit);
for (int y = 0; y < glControl.Height; y += _sprite.Height)
{
for (int x = 0; x < glControl.Width; x += _sprite.Width)
{
_sprite.Render(glControl, x, y);
}
}
glControl.SwapBuffers();
}
// Sprite#Initialize and Sprite#Render methods
public void Initialize()
{
GraphicsEngine.Imaging.InitializeTexture(_bmp, out _textureId);
Initialized = true;
}
public void Render(GLControl control, float x, float y)
{
if (Initialized)
{
GraphicsEngine.Imaging.DrawImage(control, x, y, Size.Width, Size.Height, _textureId, _color, out _vao);
}
}
// GraphicsEngine#InitializeTexture
public static void InitializeTexture(Bitmap bmp, out int id)
{
GL.GenTextures(1, out id);
GL.BindTexture(TextureTarget.Texture2D, id);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
BitmapData data = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0, GLPixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
bmp.Dispose();
}
// GraphicsEngine.Imaging#DrawImage
public static void DrawImage(GLControl control, float x, float y, float width, float height, int textureId, out VertexArray vao)
{
DrawImage(control, x, y, width, height, textureId, Utilities.WhiteColorArray4, out vao);
}
public static void DrawImage(GLControl control, float x, float y, float width, float height, int textureId, float[] colors, out VertexArray vao)
{
GL.BindTexture(TextureTarget.Texture2D, textureId);
Primitives.DrawRectangle(control, x, y, width, height, colors, true, out vao);
}
// GraphicsEngine.Primitives#DrawRectangle
public static void DrawRectangle(GLControl control, float x, float y, float width, float height, float[] colors, bool textured, out VertexArray vao)
{
// Transforms points (in pixels) to OpenGL coordinates system
// Left bottom point (-1; -1)
float x1 = -1 + (2 * x / control.Width);
float y1 = -1 + (2 * y / control.Height);
// Left upper point (in pixels) to (-1; 1)
float x2 = x1;
float y2 = y1 + (2 * height / control.Height);
// Right upper point (in pixels) to (1; 1)
float x3 = x1 + (2 * width / control.Width);
float y3 = y2;
// Right bottom point (in pixels) to (1; -1)
float x4 = x3;
float y4 = y1;
var vertices = new float[] { x1, y1, x2, y2, x3, y3, x4, y4 };
vao = CreateVertexArray(2);
vao.PutVertices(vertices);
vao.PutData(colors, 4);
if (textured)
{
vao.PutData(DefaultTexCoords, 2);
}
vao.PutIndices(VertexIndices4);
vao.Initialize();
vao.Render(BeginMode.Quads);
}
}
VertexArray class: http://pastebin.com/0ywnsHm8
using System;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL;
namespace SGE
{
public sealed class VertexArray : IDisposable
{
private int _dimensions;
private int _ibo;
private int _vbo;
private int _vao;
private int _indexCount;
private readonly List<VertexData> _data;
private bool _disposed;
public bool Initialized { get; private set; }
internal VertexArray(int dimensions)
{
_dimensions = dimensions;
_data = new List<VertexData>();
}
~VertexArray()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing && Initialized)
{
GL.DeleteBuffer(_ibo);
GL.DeleteVertexArray(_vao);
Initialized = false;
}
_disposed = true;
GC.SuppressFinalize(this);
}
public bool PutIndices(int[] indicies)
{
if (indicies.Length < 3)
return false;
_ibo = GL.GenBuffer();
if (_ibo == 0)
return false;
GL.BindBuffer(BufferTarget.ArrayBuffer, _ibo);
GL.BufferData(BufferTarget.ArrayBuffer, indicies.Length * sizeof(uint), indicies, BufferUsageHint.StaticDraw);
_indexCount = indicies.Length;
return true;
}
public bool PutVertices(float[] vertices)
{
if (_vbo != 0 || vertices.Length < 6)
return false;
_vbo = GL.GenBuffer();
if (_vbo == 0)
return false;
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);
return true;
}
public bool PutData(float[] data, int size)
{
int dbo = GL.GenBuffer();
if (dbo == 0)
return false;
GL.BindBuffer(BufferTarget.ArrayBuffer, dbo);
GL.BufferData(BufferTarget.ArrayBuffer, data.Length * sizeof(float), data, BufferUsageHint.StaticDraw);
_data.Add(new VertexData(dbo, size));
return true;
}
public bool Initialize()
{
_vao = GL.GenVertexArray();
if (_vao == 0)
return false;
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
GL.VertexAttribPointer(0, _dimensions, VertexAttribPointerType.Float, false, 0, 0);
GL.EnableVertexAttribArray(0);
for (int i = 0; i < _data.Count; i++)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, _data[i].Buffer);
GL.VertexAttribPointer(i + 1, _data[i].Size, VertexAttribPointerType.Float, false, 0, 0);
GL.EnableVertexAttribArray(i + 1);
}
_data.Clear();
Initialized = true;
return true;
}
public void Render(BeginMode beginMode)
{
if (!Initialized)
return;
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _ibo);
GL.DrawElements(beginMode, _indexCount, DrawElementsType.UnsignedInt, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
GL.BindVertexArray(0);
}
private struct VertexData
{
public int Buffer { get; private set; }
public int Size { get; private set; }
public VertexData(int buffer, int size)
{
Buffer = buffer;
Size = size;
}
}
}
}
I also used default vertex (http://pastebin.com/KEZ85GDb) and fragment (http://pastebin.com/7cnfFfb0) shaders:
Vertex shader:
#version 330 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
layout (location = 2) in vec2 texCoord;
out vec4 fColor;
out vec2 fTexCoord;
void main()
{
gl_Position = position;
fColor = color;
fTexCoord = texCoord;
}
Fragment shader:
#version 330 core
in vec4 fColor;
in vec2 fTexCoord;
out vec4 color;
uniform sampler2D _texture;
void main()
{
color = texture(_texture, fTexCoord) * fColor;
}
I get 40-60 fps in 800x600 resolution and 7-11 in FullHD.
How can I improve the drawing process?
Thanks in advance!
P.S. I can’t post links, so I removed dots and slashes. I hope, it’s not prohibited, I just want to save space in the topic.