Pages

OpenTK Textures

So you want to know how to use textures? Well i don't know what to say other than read on :]

First off is just the prefix, Loading a texture in its simplest form.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private Bitmap texture_source;
private int texture;

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    //Load texture from file
    texture_source = new Bitmap("texture.png");

    //Generate empty texture
    texture = GL.GenTexture();

    //Link empty texture to texture2d
    GL.BindTexture(TextureTarget.Texture2D, texture);

    //Must be set else the texture will show glColor
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);

    //Describe to gl what we want the bound texture to look like
    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture_source.Width, texture_source.Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
    
    //Lock pixel data to memory and prepare for pass through
    System.Drawing.Imaging.BitmapData bitmap_data = texture_source.LockBits(new Rectangle(0, 0, texture_source.Width, texture_source.Height),
        System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    //Tell gl to write the data from are bitmap image/data to the bound texture
    GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture_source.Width, texture_source.Height, PixelFormat.Bgra, PixelType.UnsignedByte, bitmap_data.Scan0);
    
    //Release from memory
    texture_source.UnlockBits(bitmap_data);

    //Release texture
    GL.BindTexture(TextureTarget.Texture2D, 0);

    //Enable textures from texture2d target
    GL.Enable(EnableCap.Texture2D);
}

Method
For convenience

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
private int LoadTexture(string path, int quality = 0, bool repeat = true, bool flip_y = false)
{
    Bitmap bitmap = new Bitmap(path);

    //Flip the image
    if (flip_y)
        bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);

    //Generate a new texture target in gl
    int texture = GL.GenTexture();

    //Will bind the texture newly/empty created with GL.GenTexture
    //All gl texture methods targeting Texture2D will relate to this texture
    GL.BindTexture(TextureTarget.Texture2D, texture);

    //The reason why your texture will show up glColor without setting these parameters is actually
    //TextureMinFilters fault as its default is NearestMipmapLinear but we have not established mipmapping
    //We are only using one texture at the moment since mipmapping is a collection of textures pre filtered
    //I'm assuming it stops after not having a collection to check.
    switch (quality)
    {
        case 0:
        default://Low quality
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Linear);
            break;
        case 1://High quality
            //This is in my opinion the best since it doesnt average the result and not blurred to shit
            //but most consider this low quality...
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.Nearest);
            break;
    }

    if (repeat)
    {
        //This will repeat the texture past its bounds set by TexImage2D
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.Repeat);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.Repeat);
    }
    else
    {
        //This will clamp the texture to the edge, so manipulation will result in skewing
        //It can also be useful for getting rid of repeating texture bits at the borders
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)All.ClampToEdge);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)All.ClampToEdge);
    }

    //Creates a definition of a texture object in opengl
    /* Parameters
     * Target - Since we are using a 2D image we specify the target Texture2D
     * MipMap Count / LOD - 0 as we are not using mipmapping at the moment
     * InternalFormat - The format of the gl texture, Rgba is a base format it works all around
     * Width;
     * Height;
     * Border - must be 0;
     * 
     * Format - this is the images format not gl's the format Bgra i believe is only language specific
     *          C# uses little-endian so you have ARGB on the image A 24 R 16 G 8 B, B is the lowest
     *          So it gets counted first, as with a language like Java it would be PixelFormat.Rgba
     *          since Java is big-endian default meaning A is counted first.
     *          but i could be wrong here it could be cpu specific :P
     *          
     * PixelType - The type we are using, eh in short UnsignedByte will just fill each 8 bit till the pixelformat is full
     *             (don't quote me on that...)
     *             you can be more specific and say for are RGBA to little-endian BGRA -> PixelType.UnsignedInt8888Reversed
     *             this will mimic are 32bit uint in little-endian.
     *             
     * Data - No data at the moment it will be written with TexSubImage2D
     */
    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bitmap.Width, bitmap.Height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);

    //Load the data from are loaded image into virtual memory so it can be read at runtime
    System.Drawing.Imaging.BitmapData bitmap_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
        System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

    //Writes data to are texture target
    /* Target;
     * MipMap;
     * X Offset - Offset of the data on the x axis
     * Y Offset - Offset of the data on the y axis
     * Width;
     * Height;
     * Format;
     * Type;
     * Data - Now we have data from the loaded bitmap image we can load it into are texture data
     */
    GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, bitmap.Width, bitmap.Height, PixelFormat.Bgra, PixelType.UnsignedByte, bitmap_data.Scan0);

    //Release from memory
    bitmap.UnlockBits(bitmap_data);

    //get rid of bitmap object its no longer needed in this method
    bitmap.Dispose();

    /*Binding to 0 is telling gl to use the default or null texture target
    *This is useful to remember as you may forget that a texture is targeted
    *And may overflow to functions that you dont necessarily want to
    *Say you bind a texture
    *
    * Bind(Texture);
    * DrawObject1();
    *                <-- Insert Bind(NewTexture) or Bind(0)
    * DrawObject2();
    * 
    * Object2 will use Texture if not set to 0 or another.
    */
    GL.BindTexture(TextureTarget.Texture2D, 0);

    return texture;
}

Usage Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
private int current_texture;

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    current_texture = LoadTexture("texture.png", 1);

    GL.Enable(EnableCap.Texture2D);
    //Basically enables the alpha channel to be used in the color buffer
    GL.Enable(EnableCap.Blend);
    //The operation/order to blend
    GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
    //Use for pixel depth comparing before storing in the depth buffer
    GL.Enable(EnableCap.DepthTest);
}

protected override void OnRenderFrame(FrameEventArgs e)
{
    base.OnRenderFrame(e);

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    GL.MatrixMode(MatrixMode.Modelview);
    GL.LoadIdentity();

    GL.PushMatrix();

    GL.Translate(0, 0, -5);

    GL.Color4(Color.White);

    GL.BindTexture(TextureTarget.Texture2D, current_texture);

    GL.Begin(BeginMode.Quads);

    //Bind texture coordinates to vertices in ccw order

    //Top-Right
    GL.TexCoord2(1.0f, 0.0f);
    GL.Vertex2(1.0f, 1.0f);

    //Top-Left
    GL.TexCoord2(0f, 0f);
    GL.Vertex2(-1.0f, 1.0f);

    //Bottom-Left
    GL.TexCoord2(0f, 1f);
    GL.Vertex2(-1.0f, -1.0f);

    //Bottom-Right
    GL.TexCoord2(1f, 1f);
    GL.Vertex2(1.0f, -1.0f);

    GL.End();

    GL.BindTexture(TextureTarget.Texture2D, 0);

    GL.PopMatrix();

    SwapBuffers();
}

2 comments :

  1. Your LoadTexture method has memory leaks, checkout Memory profiling in MS Studio.

    ReplyDelete
  2. Please please use Shader and Program! Do not use Begin and End! Because my display card is supports only Shader and Program - Thanks!

    ReplyDelete