I have a following picture:
Window displays two textures, the left texture is generated by binding it to a FBO and rendering to FBO, 50 random triangles.
The right texture is generated by glTexImage2D with full-color image of Mona Lisa, but as you can see the image is lacking red colors, I think the problem is in OpenGL part, since I've investigated bytes that are passed to glTexImage2D and they do contain Red...
Here is C#/Mono/OpenTK source
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform;
using OpenTK.Platform.X11;
using OpenTK.Input;
using ManOCL;
namespace GlTest
{
public class MainClass: GameWindow
{
public MainClass()
: base(800, 600, new GraphicsMode(new ColorFormat(0, 0, 0, 0), 32, 0))
{
this.VSync = VSyncMode.Off;
}
public uint fbo;
public uint textureA;
public uint textureB;
public int textureAWidth = 1024;
public int textureAHeight = 1024;
public int textureBWidth;
public int textureBHeight;
Random rand = new Random();
protected override void OnLoad(EventArgs e)
{
GL.Enable(EnableCap.Blend);
GL.ShadeModel(ShadingModel.Smooth);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.Disable(EnableCap.DepthTest);
GL.Disable(EnableCap.CullFace);
// GL.PolygonMode(MaterialFace.Back, PolygonMode.Line);
// Create Color Tex
GL.GenTextures(1, out textureA);
GL.BindTexture(TextureTarget.Texture2D, textureA);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, textureAWidth, textureAHeight, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
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.ClampToBorder);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToBorder);
GL.GenTextures(1, out textureB);
GL.BindTexture(TextureTarget.Texture2D, textureB);
Bitmap bmp = new Bitmap("/Projects/MonaLisa/MonaLisa.bmp");
Console.WriteLine(textureBWidth = bmp.Width);
Console.WriteLine(textureBHeight = bmp.Height);
//get the data out of the bitmap
System.Drawing.Imaging.BitmapData bmpBits = bmp.LockBits
(
new System.Drawing.Rectangle(0,0,textureBWidth, textureBHeight),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppRgb
);
for (int row = 0; row < 1; row++)
{
for (int col = 0; col < 32; col++)
{
Console.WriteLine
(
"{0}, {1}, {2}, {3}",
Marshal.ReadByte(bmpBits.Scan0, (row * textureBWidth + col) * 4 + 0),
Marshal.ReadByte(bmpBits.Scan0, (row * textureBWidth + col) * 4 + 1),
Marshal.ReadByte(bmpBits.Scan0, (row * textureBWidth + col) * 4 + 2),
Marshal.ReadByte(bmpBits.Scan0, (row * textureBWidth + col) * 4 + 3)
);
}
}
Console.WriteLine(bmpBits.Width);
Console.WriteLine(bmpBits.Height);
GL.TexImage2D
(
TextureTarget.Texture2D,
0,
PixelInternalFormat.Rgba,
textureBWidth,
textureBHeight,
0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgba,
PixelType.UnsignedByte,
bmpBits.Scan0
);
// GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8, textureBWidth, textureBHeight, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
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.ClampToBorder);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToBorder);
//free the bitmap data (we dont need it anymore because it has been passed to the OpenGL driver
bmp.UnlockBits(bmpBits);
// Create a FBO and attach the textures
GL.Ext.GenFramebuffers(1, out fbo);
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fbo);
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, textureA, 0);
#region Test for Error
switch (GL.Ext.CheckFramebufferStatus(FramebufferTarget.FramebufferExt))
{
case FramebufferErrorCode.FramebufferCompleteExt:
{
Console.WriteLine("FBO: The framebuffer is complete and valid for rendering.");
break;
}
case FramebufferErrorCode.FramebufferIncompleteAttachmentExt:
{
Console.WriteLine("FBO: One or more attachment points are not framebuffer attachment complete. This could mean there’s no texture attached or the format isn’t renderable. For color textures this means the base format must be RGB or RGBA and for depth textures it must be a DEPTH_COMPONENT format. Other causes of this error are that the width or height is zero or the z-offset is out of range in case of render to volume.");
break;
}
case FramebufferErrorCode.FramebufferIncompleteMissingAttachmentExt:
{
Console.WriteLine("FBO: There are no attachments.");
break;
}
/* case FramebufferErrorCode.GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
{
Console.WriteLine("FBO: An object has been attached to more than one attachment point.");
break;
}*/
case FramebufferErrorCode.FramebufferIncompleteDimensionsExt:
{
Console.WriteLine("FBO: Attachments are of different size. All attachments must have the same width and height.");
break;
}
case FramebufferErrorCode.FramebufferIncompleteFormatsExt:
{
Console.WriteLine("FBO: The color attachments have different format. All color attachments must have the same format.");
break;
}
case FramebufferErrorCode.FramebufferIncompleteDrawBufferExt:
{
Console.WriteLine("FBO: An attachment point referenced by GL.DrawBuffers() doesn’t have an attachment.");
break;
}
case FramebufferErrorCode.FramebufferIncompleteReadBufferExt:
{
Console.WriteLine("FBO: The attachment point referenced by GL.ReadBuffers() doesn’t have an attachment.");
break;
}
case FramebufferErrorCode.FramebufferUnsupportedExt:
{
Console.WriteLine("FBO: This particular FBO configuration is not supported by the implementation.");
break;
}
default:
{
Console.WriteLine("FBO: Status unknown. (yes, this is really bad.)");
break;
}
}
// using FBO might have changed states, e.g. the FBO might not support stereoscopic views or double buffering
int[] queryinfo = new int[6];
GL.GetInteger(GetPName.MaxColorAttachmentsExt, out queryinfo[0]);
GL.GetInteger(GetPName.AuxBuffers, out queryinfo[1]);
GL.GetInteger(GetPName.MaxDrawBuffers, out queryinfo[2]);
GL.GetInteger(GetPName.Stereo, out queryinfo[3]);
GL.GetInteger(GetPName.Samples, out queryinfo[4]);
GL.GetInteger(GetPName.Doublebuffer, out queryinfo[5]);
Console.WriteLine("max. ColorBuffers: " + queryinfo[0] + " max. AuxBuffers: " + queryinfo[1] + " max. DrawBuffers: " + queryinfo[2] +
"\nStereo: " + queryinfo[3] + " Samples: " + queryinfo[4] + " DoubleBuffer: " + queryinfo[5]);
Console.WriteLine("Last GL Error: " + GL.GetError());
#endregion Test for Error
GL.PushAttrib(AttribMask.ViewportBit);
{
GL.Viewport(0, 0, textureAWidth, textureAHeight);
OpenTK.Matrix4 orthogonal = OpenTK.Matrix4.CreateOrthographicOffCenter(0, 1, 0, 1, -3, 3);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref orthogonal);
Matrix4 lookat = Matrix4.LookAt(0, 0, 1, 0, 0, 0, 0, 1, 0);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref lookat);
// clear the screen in red, to make it very obvious what the clear affected. only the FBO, not the real framebuffer
GL.ClearColor(0f, 0f, 0f, 0f);
GL.Clear(ClearBufferMask.ColorBufferBit);
// smack 50 random triangles into the FBO's textures
GL.Begin(BeginMode.Triangles);
{
for (int i = 0; i < 50; i++)
{
GL.Color4(((float)(rand.NextDouble())), ((float)(rand.NextDouble())), ((float)(rand.NextDouble())), ((float)(rand.NextDouble())));
GL.Vertex3(((float)(rand.NextDouble())), ((float)(rand.NextDouble())), 0);
GL.Color4(((float)(rand.NextDouble())), ((float)(rand.NextDouble())), ((float)(rand.NextDouble())), ((float)(rand.NextDouble())));
GL.Vertex3(((float)(rand.NextDouble())), ((float)(rand.NextDouble())), 0);
GL.Color4(((float)(rand.NextDouble())), ((float)(rand.NextDouble())), ((float)(rand.NextDouble())), ((float)(rand.NextDouble())));
GL.Vertex3(((float)(rand.NextDouble())), ((float)(rand.NextDouble())), 0);
}
}
GL.End();
}
GL.PopAttrib();
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0); // disable rendering into the FBO
GL.ClearColor(1f, 1f, 1f, 0.0f);
GL.Color3(1f, 1f, 1f);
GL.Enable(EnableCap.Texture2D); // enable Texture Mapping
GL.BindTexture(TextureTarget.Texture2D, 0); // bind default texture
// IntPtr cglContext = Monobjc.OpenGL.CGL.GetCurrentContext();
//
// IntPtr cglShareGroup = Monobjc.OpenGL.CGL.GetShareGroup(cglContext);
//
// ManOCL.Context.ShareWithCGL(cglShareGroup);
//
// Kernel kernel = ManOCL.Kernel.Create
// (
// "kernel1",
// #"__kernel void kernel1(__global int *srcimg, __global int * output, __global int *smp)
// {
// }",
// new Argument[]
// {
// DeviceGlobalMemory.CreateFromArray(new int[1]),
// DeviceGlobalMemory.CreateFromArray(new int[2]),
// DeviceGlobalMemory.CreateFromArray(new int[1])
// }
// );
// Console.WriteLine("Success!");
// Console.WriteLine(kernel);
}
protected override void OnUnload(EventArgs e)
{
// Clean up what we allocated before exiting
GL.DeleteTextures(1, ref textureA);
GL.DeleteTextures(1, ref textureB);
GL.Ext.DeleteFramebuffers(1, ref fbo);
base.OnUnload(e);
}
protected override void OnResize (EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
double aspect_ratio = Width / (double)Height;
OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4 * 3 / 2, (float)aspect_ratio, 1, 64);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref perspective);
Matrix4 lookat = Matrix4.LookAt(0, 0, 3, 0, 0, 0, 0, 1, 0);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref lookat);
base.OnResize(e);
}
protected override void OnUpdateFrame (FrameEventArgs e)
{
base.OnUpdateFrame(e);
if (Keyboard[Key.Escape])
{
this.Exit();
}
}
protected override void OnRenderFrame(FrameEventArgs e)
{
this.Title = "Frames per Second: " + (1.0 / e.Time);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.PushMatrix();
{
// Draw the Color Texture
GL.Translate(-1f, 0f, 0f);
GL.BindTexture(TextureTarget.Texture2D, textureA);
GL.Begin(BeginMode.Quads);
{
GL.TexCoord2(0f, 1f);
GL.Vertex2(-1.0f, 1.0f);
GL.TexCoord2(0.0f, 0.0f);
GL.Vertex2(-1.0f, -1.0f);
GL.TexCoord2(1.0f, 0.0f);
GL.Vertex2(1.0f, -1.0f);
GL.TexCoord2(1.0f, 1.0f);
GL.Vertex2(1.0f, 1.0f);
}
GL.End();
GL.Translate(2f, 0f, 0f);
GL.BindTexture(TextureTarget.Texture2D, textureB);
GL.Begin(BeginMode.Quads);
{
GL.TexCoord2(0f, 0f);
GL.Vertex2(-1.0f, 1.0f);
GL.TexCoord2(0.0f, 1.0f);
GL.Vertex2(-1.0f, -1.0f);
GL.TexCoord2(1.0f, 1.0f);
GL.Vertex2(1.0f, -1.0f);
GL.TexCoord2(1.0f, 0.0f);
GL.Vertex2(1.0f, 1.0f);
}
GL.End();
GL.Translate(0f, 0f, 0f);
}
GL.PopMatrix();
this.SwapBuffers();
}
#region public static void Main()
/// <summary>
/// Entry point of this example.
/// </summary>
[STAThread]
public static void Main()
{
using (MainClass example = new MainClass())
{
example.Title = "FrameBufferObjects";
example.Run(1.0, 0.0);
}
}
#endregion
}
}
In the bit of code where you set your texture data:
GL.TexImage2D
(
TextureTarget.Texture2D,
0,
PixelInternalFormat.Rgba,
textureBWidth,
textureBHeight,
0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgba,
PixelType.UnsignedByte,
bmpBits.Scan0
);
Change your pixel format to
OpenTK.Graphics.OpenGL.PixelFormat.Bgra;
Windows bitmaps and opengl have a different endianess on pixels i.e. the bytes are reversed.
Related
I want to draw multiple images in to framebuffer and then also draw framebuffer on screen.
I also want to ReadPixels from frame buffer to store image on disk.
Is there some tutorial or example how to use framebuffer in OpenTK.
I found one example but is not in C# openTK
https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/minimalfbo/minimalfbo.c
Currently my code crashes application, and the ReadPixels returns an empty image.
namespace test
{
class Class1
{
int FramebufferName = -1;
int depthrenderbuffer;
int fbo_width = 0;
int fbo_height = 0;
public bool createImage = false;
DirectBitmap masterBitmap = null;
object masterBitmapSync = new object();
public int renderedTexture = 0;
public override void OnRender()
{
//https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/minimalfbo/minimalfbo.c
calculateMasterBitmapSize();
#region init
if (FramebufferName == -1)
{
//do this only once
//Framebuffer
GL.GenFramebuffers(1, out FramebufferName);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FramebufferName);
checkGlError();
//Color renderbuffer.
lock (masterBitmapSync)
{
if (masterBitmap != null)
createTexture();
}
if (renderedTexture <= 0 || FramebufferName < 0)
{
Console.WriteLine("ERROR:");
}
checkGlError();
GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
checkGlError();
//GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderedTexture);
//checkGlError();
/* Storage must be one of: */
/* GL_RGBA4, GL_RGB565, GL_RGB5_A1, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8. */
//GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent16, fbo_width, fbo_height);
//checkGlError();
//GL.FramebufferRenderbuffer(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, RenderbufferTarget.Renderbuffer, renderedTexture);
//checkGlError();
/* Depth renderbuffer. */
GL.GenRenderbuffers(1, out depthrenderbuffer);
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthrenderbuffer);
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent16, fbo_width, fbo_height);
GL.FramebufferRenderbuffer(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, depthrenderbuffer);
checkGlError();
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
checkGlError();
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, fbo_width, fbo_height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.FramebufferTexture2D(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, renderedTexture, 0); //original texture 1280x720
checkGlError();
FramebufferErrorCode errorCode = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer);
if (errorCode != FramebufferErrorCode.FramebufferComplete)
{
if (errorCode == FramebufferErrorCode.FramebufferUnsupported)
Console.WriteLine("FramebufferUnsupported");
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
GL.DeleteFramebuffers(1, ref FramebufferName);
GL.DeleteFramebuffers(1, ref depthrenderbuffer);
return;
}
checkGlError();
}
#endregion
#region drawInFramebuffer
checkGlError();
lock (masterBitmapSync)
{
if (masterBitmap != null)
createTexture();
}
checkGlError();
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.Enable(EnableCap.Texture2D);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FramebufferName);
checkGlError();
//GL.ColorMask(true, true, true, true);
GL.ClearColor(0, 0, 0, 0);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
checkGlError();
GL.Enable(EnableCap.Texture2D);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, renderedTexture);
checkGlError();
GL.Begin(PrimitiveType.Quads);
{
GL.TexCoord2(0.0f, 0.0f); GL.Vertex2(LocalPoints[0].X, LocalPoints[0].Y);
GL.TexCoord2(1.0f, 0.0f); GL.Vertex2(LocalPoints[1].X, LocalPoints[1].Y);
GL.TexCoord2(1.0f, 1.0f); GL.Vertex2(LocalPoints[2].X, LocalPoints[2].Y);
GL.TexCoord2(0.0f, 1.0f); GL.Vertex2(LocalPoints[3].X, LocalPoints[3].Y);
}
GL.End();
GL.Disable(EnableCap.Texture2D);
//TODO draw multipla images
//loop over all images and calling OnRender2()
if (createImage)
{
createImage = false;
GPoint p = new GPoint(0, 0); //TODO get correct x,y
using (Bitmap bitmap = new Bitmap(fbo_width, fbo_height))
{
BitmapData bits = bitmap.LockBits(new Rectangle(0, 0, fbo_width, fbo_height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
checkGlError();
GL.ReadPixels((int)p.X, (int)p.Y, fbo_width, fbo_height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bits.Scan0);
checkGlError();
bitmap.UnlockBits(bits);
bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
bitmap.Save(#"c:\Downloads\aaa\ReadPixels_" + DateTime.Now.ToString("HHmmss_fff") + ".png", ImageFormat.Png);
}
}
checkGlError();
//does command in next line, draw framebuffer on the screen?
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); //would draw to the default framebuffer again, basically finishing the drawing to the other framebuffer(the backbuffer which will be brought to front by SwapBuffers)
checkGlError();
#endregion
}
private void calculateMasterBitmapSize()
{
//for test example
fbo_width = 256;
fbo_height = 256 * 2;
DirectBitmap newBitmap = new DirectBitmap(fbo_width, fbo_height);
newBitmap.Bitmap.MakeTransparent();
setBitmap(newBitmap);
}
public void setBitmap(DirectBitmap bmp)
{
lock (masterBitmapSync)
{
if (masterBitmap != null)
masterBitmap.Dispose();
masterBitmap = bmp;
if (masterBitmap == null)
deleteTexture();
}
}
void deleteTexture()
{
if (renderedTexture > 0)
{
GL.DeleteTexture(renderedTexture);
renderedTexture = 0;
}
}
public void createTexture()
{
if (masterBitmap == null)
return;
int t = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, t);
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.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, masterBitmap.Width, masterBitmap.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
Rectangle rect = new Rectangle(0, 0, masterBitmap.Width, masterBitmap.Height);
System.Drawing.Imaging.BitmapData data = masterBitmap.Bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.BindTexture(TextureTarget.Texture2D, t);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, rect.X, rect.Y, rect.Width, rect.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
masterBitmap.Bitmap.UnlockBits(data);
masterBitmap.Dispose();
masterBitmap = null;
if (renderedTexture > 0)
GL.DeleteTexture(renderedTexture);
renderedTexture = t;
}
private void checkGlError()
{
ErrorCode errorCode = GL.GetError();
if (errorCode != ErrorCode.NoError)
{
Console.WriteLine("ERROR: " + errorCode);
}
}
public void OnRender2()
{
/*
//this is example only
lock (bitmapSync)
{
if (bitmap != null)
createTexture();
}
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, texture);
GL.Begin(PrimitiveType.Quads);
{
GL.TexCoord4(texCoords[0]); GL.Vertex4(LocalPoints[0].X, LocalPoints[0].Y, 1, 1); //UL
GL.TexCoord4(texCoords[1]); GL.Vertex4(LocalPoints[1].X, LocalPoints[1].Y, 1, 1); //UR
GL.TexCoord4(texCoords[2]); GL.Vertex4(LocalPoints[2].X, LocalPoints[2].Y, 1, 1); //LR
GL.TexCoord4(texCoords[3]); GL.Vertex4(LocalPoints[3].X, LocalPoints[3].Y, 1, 1); //LL
}
GL.End();
GL.Disable(EnableCap.Texture2D);
*/
}
}
}
"framebuffer" usually means memory on the graphics device used for final output to the screen. This is typically tricky to access from c#.
If you simply want to combine multiple images, and do not have any strict performance requirements, it would be much simpler to use a regular buffer to do the combining and then draw this buffer to the screen. The oldschool way to do this is to use a GDI graphics object to do the drawing.
An example using GDI & winforms
public class BitmapPaint : Control
{
private Bitmap buffer;
public BitmapPaint()
{
// Find out how large you want the output image to be
// This example does not handle disposal or resizing etc
buffer = new Bitmap(this.Width, this.Height);
}
public void Compose(IEnumerable<Bitmap> images)
{
using (var g = Graphics.FromImage(buffer))
{
foreach (var img in images)
{
// Adjust position etc if you need to
g.DrawImageUnscaled(img, 0, 0);
}
}
buffer.Save(#"c:\myFilename.bmp");
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw the buffer to the screen
e.Graphics.DrawImageUnscaled(buffer, 0, 0);
}
}
If you want higher performance it might be more appropriate to look for something like a 2D game engine that can manage the blitting and much more internally. Doing low level buffer manipulation is not a great fit for .Net since it typically involves raw pointers and resource management that is often easier to manage in C/C++.
Im trying to follow this tut here http://neokabuto.blogspot.com/2014/07/opentk-tutorial-6-part-3-putting-it-all.html (backup http://web.archive.org/web/20190125230120/http://neokabuto.blogspot.com/2014/07/opentk-tutorial-6-part-3-putting-it-all.html) But I have been threw the code like a hundred times but I still keep getting black cubes :(.
Heres a download link for my hole project https://mega.nz/folder/t8NAxDRJ#MEIlwc96FZEPi_pUuB2xaA
heres what it looks like
Heres my main script, maby the errors in that?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using OpenTK;
//using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System.IO;
using OpenTK.Input;
using System.Windows.Media.Imaging;
using System.Drawing.Imaging;
//using System.Windows.Input;
namespace OpenTKTutorial1
{
class Game: GameWindow
{
public Game() : base(1000,500, new OpenTK.Graphics.GraphicsMode(32,24,0,4))
{
}
//Just like we had a Dictionary to allow us to use strings to
//organize our shaders, we'll use another to keep track of our
//texture IDs. Add this to the Game class:
Dictionary<string, int> textures = new Dictionary<string, int>();
Vector2[] texcoorddata;
Camera cam;
Vector2 lastMousePos = new Vector2();
float time = 0.0f;
int ibo_elements;
Dictionary<string, ShaderProgram> shaders = new Dictionary<string, ShaderProgram>();
string activeShader = "default";
Vector3[] vertdata;
Vector3[] coldata;
List<Volume> objects = new List<Volume>();
int[] indicedata;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
cam = new Camera();
initProgram();
Title = "Hello OpenTK!";
GL.ClearColor(Color.CornflowerBlue);
GL.PointSize(5f);
}
void initProgram()
{
lastMousePos = new Vector2(Mouse.X, Mouse.Y);
GL.GenBuffers(1, out ibo_elements);
shaders.Add("default", new ShaderProgram("Shaders/vs.glsl", "Shaders/fs.glsl", true));
shaders.Add("textured", new ShaderProgram("Shaders/vs_tex.glsl", "Shaders/fs_tex.glsl", true));
activeShader = "textured";
textures.Add("pictures/opentksquare.png", loadImage("pictures/opentksquare.png"));
textures.Add("pictures/opentksquare2.png", loadImage("pictures/opentksquare2.png"));
TexturedCube tc = new TexturedCube();
tc.TextureID = textures["pictures/opentksquare.png"];
objects.Add(tc);
TexturedCube tc2 = new TexturedCube();
tc2.Position += new Vector3(1f, 1f, 1f);
tc2.TextureID = textures["pictures/opentksquare2.png"];
objects.Add(tc2);
cam.Position += new Vector3(0f, 0f, 3f);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Viewport(0, 0, Width, Height);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
shaders[activeShader].EnableVertexAttribArrays();
int indiceat = 0;
foreach (Volume v in objects)
{
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, v.TextureID);
GL.UniformMatrix4(shaders[activeShader].GetUniform("modelview"), false, ref v.ModelViewProjectionMatrix);
if (shaders[activeShader].GetUniform("maintexture") != -1)
{
GL.Uniform1(shaders[activeShader].GetUniform("maintexture"), 0);
}
GL.DrawElements(BeginMode.Triangles, v.IndiceCount, DrawElementsType.UnsignedInt, indiceat * sizeof(uint));
indiceat += v.IndiceCount;
}
shaders[activeShader].DisableVertexAttribArrays();
GL.Flush();
SwapBuffers();
}
private void ProcessInput()
{
if(OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.Escape))
{
Exit();
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.W))
{
cam.Move(0f, 0.1f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.S))
{
cam.Move(0f, -0.1f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.A))
{
cam.Move(-0.1f, 0f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.D))
{
cam.Move(0.1f, 0f, 0f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.Q))
{
cam.Move(0f, 0f, 0.1f);
}
if (OpenTK.Input.Keyboard.GetState().IsKeyDown(Key.E))
{
cam.Move(0f, 0f, -0.1f);
}
if (Focused)
{
Vector2 delta = lastMousePos - new Vector2(OpenTK.Input.Mouse.GetState().X, OpenTK.Input.Mouse.GetState().Y);
lastMousePos += delta;
cam.AddRotation(delta.X, delta.Y);
lastMousePos = new Vector2(OpenTK.Input.Mouse.GetState().X, OpenTK.Input.Mouse.GetState().Y);
}
}
protected override void OnUpdateFrame (FrameEventArgs e){
time += (float)e.Time;
ProcessInput();
// base.OnUpdateFrame();
Vector2[] texcoorddata;
List<Vector3> verts = new List<Vector3>();
List<int> inds = new List<int>();
List<Vector3> colors = new List<Vector3>();
List<Vector2> texcoords = new List<Vector2>();
int vertcount = 0;
foreach (Volume v in objects)
{
verts.AddRange(v.GetVerts().ToList());
inds.AddRange(v.GetIndices(vertcount).ToList());
colors.AddRange(v.GetColorData().ToList());
texcoords.AddRange(v.GetTextureCoords());
texcoorddata = texcoords.ToArray();
// texcoords.AddRange(v.GetTextureCoords());
vertcount += v.VertCount;
}
vertdata = verts.ToArray();
indicedata = inds.ToArray();
coldata = colors.ToArray();
texcoorddata = texcoords.ToArray();
GL.BindBuffer(BufferTarget.ArrayBuffer, shaders[activeShader].GetBuffer("vPosition"));
GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(vertdata.Length * Vector3.SizeInBytes), vertdata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(shaders[activeShader].GetAttribute("vPosition"), 3, VertexAttribPointerType.Float, false, 0, 0);
if (shaders[activeShader].GetAttribute("vColor") != -1)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, shaders[activeShader].GetBuffer("vColor"));
GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(coldata.Length * Vector3.SizeInBytes), coldata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(shaders[activeShader].GetAttribute("vColor"), 3, VertexAttribPointerType.Float, true, 0, 0);
}
if (shaders[activeShader].GetAttribute("texcoord") != -1)
{
GL.BindBuffer(BufferTarget.ArrayBuffer, shaders[activeShader].GetBuffer("texcoord"));
GL.BufferData<Vector2>(BufferTarget.ArrayBuffer, (IntPtr)(texcoorddata.Length * Vector2.SizeInBytes), texcoorddata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(shaders[activeShader].GetAttribute("texcoord"), 2, VertexAttribPointerType.Float, true, 0, 0);
}
// objects[0].Position = new Vector3(0.3f, -0.5f + (float) Math.Sin(time), -3.0f);
objects[0].Rotation = new Vector3(0.55f * time, 0.25f * time, 0);
objects[0].Scale = new Vector3(1, 1, 1);
objects[1].Position = new Vector3(-5f, 0.5f + (float)Math.Cos(time), -2.0f);
objects[1].Rotation = new Vector3(-0.25f * time, -0.35f * time, 0);
objects[1].Scale = new Vector3(2, 1, 2);
foreach (Volume v in objects)
{
v.CalculateModelMatrix();
v.ViewProjectionMatrix = cam.GetViewMatrix() * Matrix4.CreatePerspectiveFieldOfView(1.3f, ClientSize.Width / (float)ClientSize.Height, 1.0f, 40.0f);
v.ModelViewProjectionMatrix = v.ModelMatrix * v.ViewProjectionMatrix;
}
//GL.UniformMatrix4(uniform_mview, false, ref mviewdata[0]);
GL.UseProgram(shaders[activeShader].ProgramID);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, ibo_elements);
GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(indicedata.Length * sizeof(int)), indicedata, BufferUsageHint.StaticDraw);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1.0f, 64.0f);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadMatrix(ref projection);
}
void loadShader(String filename,ShaderType type, int program, out int address)
{
address = GL.CreateShader(type);
try{
using (StreamReader sr = new StreamReader(filename))
{
GL.ShaderSource(address, sr.ReadToEnd());
}
}
catch (Exception ex){
Console.WriteLine("error loading shader : " + ex);
}
GL.CompileShader(address);
GL.AttachShader(program, address);
Console.WriteLine(GL.GetShaderInfoLog(address));
}
///
///This function will load an image as a bitmap,
/// and then store the data of it in the graphics
/// card's memory. It returns an int, the address
/// of the texture so that we can retrieve the data
/// when we want to draw the texture.
int loadImage(Bitmap image){
int texID = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, texID);
BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.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);
image.UnlockBits(data);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
return texID;
}
int loadImage(string filename)
{
try
{
Bitmap file = new Bitmap(filename);
return loadImage(file);
}
catch (Exception e)
{
Console.WriteLine(e);
return -1;
}
}
}
}
You can see all my referances for the project in the picture
The mistake is very simple. The attribute TextureID exists twice. Fist it is declared in the class Volume:
public abstract class Volume
{
// [...]
public int TextureID;
// [...]
}
Then an attribute with the same name is declared in the class TexturedCube, which is (indirectly) derived from Volume:
public class TexturedCube : Cube
{
// [...]
public int TextureID;
// [...]
}
Hence the 2nd attribute covers the 1st one. Remove the attribute from the class TexturedCube:
By the way, the same applies to the IsTextured and TextureCoordsCount attribute.
I'm trying to render an image with a compute shader. For testing purposes, I try to render the gl_GlobalInvocationID to the image, but that only gives me a black screen. It does, however, work when I give the imageStore a predefined color. For example vec4(1, 0, 0, 1);. After some testing, it appears that the gl_GlobalInvocationID.xy doesn't get any further than [0,0]. What am I doing wrong?
I tested the values of gl_GlobalInvocationID by rendering a different color if the x or y coordinates got higher than 0, which they didn't.
Compute shader:
#version 440
layout (local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform image2D frameBuffer;
void main()
{
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = ivec2(imageSize(frameBuffer));
vec4 pixel;
pixel = vec4(vec2(pixel_coords) / vec2(size.x, size.y),0, 1);
imageStore(frameBuffer, pixel_coords, pixel);
}
Framebuffer creation:
frameBuffer = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, frameBuffer);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32f, Width, Height, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMinFilter.Nearest);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba32f, Width, Height, 0, PixelFormat.Rgba, PixelType.Float, IntPtr.Zero);
GL.BindImageTexture(0, frameBuffer, 0, false, 0, TextureAccess.WriteOnly, SizedInternalFormat.Rgba32f);
Compute shader initialization
//Initialise compute shader in Onload()
cShader = new ComputeShader("../../RayTracer.glsl");
cShader.Use();
cShader.Dispatch(Width, Height, 1);
Compute shader class
class ComputeShader {
int handle;
//Initialize the compute shader given by the path
public ComputeShader(string path) {
handle = GL.CreateProgram();
int shader = GL.CreateShader(ShaderType.ComputeShader);
string source;
using (StreamReader sr = new StreamReader(path)) {
source = sr.ReadToEnd();
}
GL.ShaderSource(shader, source);
GL.CompileShader(shader);
string shaderError = GL.GetShaderInfoLog(shader);
if (shaderError.Length > 0) {
Console.WriteLine(shaderError);
}
GL.AttachShader(handle, shader);
GL.LinkProgram(handle);
GL.DetachShader(handle, shader);
GL.DeleteShader(shader);
}
//Sets a uniform 4x4 matrix in the compute shader
public void SetMatrix4x4(string name, Matrix4 matrix) {
int location = GL.GetUniformLocation(handle, name);
GL.UniformMatrix4(location, false, ref matrix);
}
//Sets a uniform float in the compute shader
public void SetUniformFloat(string name, float val) {
int location = GL.GetUniformLocation(handle, name);
GL.Uniform1(location, val);
}
//Use the compute shader
public void Use() {
GL.UseProgram(handle);
}
//Dispatch the compute shader
public void Dispatch(int xGroups, int yGroups, int zGroups) {
GL.DispatchCompute(xGroups, yGroups, zGroups);
}
}
OnRenderFrameMethod:
protected override void OnRenderFrame(FrameEventArgs e) {
//Launch computer shaders
cShader.Use();
cShader.Dispatch(Width, Height, 1);
//Make sure frame is finsihed before proceeding
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
//Clear previous frame
GL.Clear(ClearBufferMask.ColorBufferBit);
//Bind VAO
GL.BindVertexArray(VertexArrayObject);
//Bind texture
GL.BindTexture(TextureTarget.Texture2D, frameBuffer); //Renders GPU
//Draw primitives
GL.DrawElements(PrimitiveType.Triangles, canvasIndices.Length, DrawElementsType.UnsignedInt, 0);
//Swap buffers
Context.SwapBuffers();
base.OnRenderFrame(e);
}
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 two VBOs. One of them has a texture and the other one doesn't. I have no success drawing them to the screen simultaneously. DrawQuad draws the untextured one and DrawQuadTex draws the textured one, both with code similar to the below:
// Enable/Disable GL_TEXTURE_2D depending on which I'm drawing
// Bind/Unbind the texture
GL.EnableClientState(ArrayCap.VertexArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferId);
GL.VertexPointer(2, VertexPointerType.Float, Vector2.SizeInBytes, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBufferId);
GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedByte, 0);
Whichever of these two functions gets called first, that shape is then visible (i.e. if I call DrawQuad, then an untextured quad is visible).
I do set glDisable on GL_TEXTURE_2D and I tried turning off the TexCoordArray and I tried binding a texture to 0, nothing made any difference. Is there something I'm overlooking?
SSCCE
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using OpenTK;
using OpenTK.Graphics.OpenGL;
namespace E04
{
internal class Renderer
{
private readonly Int32 _vertexBufferId;
private readonly Int32 _textureBufferId;
private readonly Int32 _indexBufferId;
private readonly Int32 _colorBufferId;
private readonly Int32 _normalBufferId;
private readonly Vector2[] _vertices;
private readonly Vector2[] _textureCoordinates;
private readonly Byte[] _indices;
private readonly Vector4[] _colors;
private readonly Vector3[] _normals;
public Renderer()
{
// Create a vertex array
_vertices = new Vector2[4];
// Set the coordinate data
_textureCoordinates = new Vector2[4];
_textureCoordinates[0].X = 0.0f;
_textureCoordinates[0].Y = 0.0f;
_textureCoordinates[1].X = 1.0f;
_textureCoordinates[1].Y = 0.0f;
_textureCoordinates[2].X = 0.0f;
_textureCoordinates[2].Y = 1.0f;
_textureCoordinates[3].X = 1.0f;
_textureCoordinates[3].Y = 1.0f;
// Set the index data
_indices = new Byte[6];
_indices[0] = 0;
_indices[1] = 1;
_indices[2] = 3;
_indices[3] = 3;
_indices[4] = 2;
_indices[5] = 0;
// Set the color data
_colors = new Vector4[4];
_colors[0] = new Vector4(1.0f, 0.0f, 0.0f, 1.0f);
_colors[1] = new Vector4(1.0f, 1.0f, 0.0f, 1.0f);
_colors[2] = new Vector4(0.0f, 1.0f, 0.0f, 1.0f);
_colors[3] = new Vector4(0.0f, 0.0f, 1.0f, 1.0f);
// Set the normal data
_normals = new Vector3[4];
_normals[0] = new Vector3(0.0f, 0.0f, 1.0f);
_normals[1] = new Vector3(0.0f, 0.0f, 1.0f);
_normals[2] = new Vector3(0.0f, 0.0f, 1.0f);
_normals[3] = new Vector3(0.0f, 0.0f, 1.0f);
// Bind the texture array buffer
GL.GenBuffers(1, out _textureBufferId);
GL.BindBuffer(BufferTarget.ArrayBuffer, _textureBufferId);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr) (_textureCoordinates.Length*Vector2.SizeInBytes),
_textureCoordinates, BufferUsageHint.DynamicDraw);
GL.TexCoordPointer(2, TexCoordPointerType.Float, 8, IntPtr.Zero);
GL.EnableClientState(ArrayCap.TextureCoordArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
// Bind the normal array buffer
GL.GenBuffers(1, out _normalBufferId);
GL.BindBuffer(BufferTarget.ArrayBuffer, _normalBufferId);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr) (_normals.Length*Vector3.SizeInBytes), _normals,
BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
// Bind the color array buffer
GL.GenBuffers(1, out _colorBufferId);
GL.BindBuffer(BufferTarget.ArrayBuffer, _colorBufferId);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr) (_colors.Length*Vector4.SizeInBytes), _colors,
BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
// Bind vertex array buffer
GL.GenBuffers(1, out _vertexBufferId);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferId);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr) (_vertices.Length*Vector2.SizeInBytes), _vertices,
BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
// Bind index array buffer
GL.GenBuffers(1, out _indexBufferId);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBufferId);
GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr) (_indices.Length*sizeof (Byte)), _indices,
BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
}
public void DrawQuad(Int32 x, Int32 y, Int32 width, Int32 height)
{
UpdateQuadVertices(x, y, width, height);
GL.Disable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.ColorArray);
GL.EnableClientState(ArrayCap.NormalArray);
GL.EnableClientState(ArrayCap.TextureCoordArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferId);
GL.VertexPointer(2, VertexPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ArrayBuffer, _colorBufferId);
GL.ColorPointer(4, ColorPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ArrayBuffer, _normalBufferId);
GL.NormalPointer(NormalPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ArrayBuffer, _textureBufferId);
GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBufferId);
GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedByte, 0);
}
public void DrawQuadTex(Int32 x, Int32 y, Int32 width, Int32 height, Int32 textureId)
{
UpdateQuadVertices(x, y, width, height);
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, textureId);
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.ColorArray);
GL.EnableClientState(ArrayCap.NormalArray);
GL.EnableClientState(ArrayCap.TextureCoordArray);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferId);
GL.VertexPointer(2, VertexPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ArrayBuffer, _colorBufferId);
GL.ColorPointer(4, ColorPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ArrayBuffer, _normalBufferId);
GL.NormalPointer(NormalPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ArrayBuffer, _textureBufferId);
GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, IntPtr.Zero);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBufferId);
GL.DrawElements(BeginMode.Triangles, _indices.Length, DrawElementsType.UnsignedByte, 0);
// Removing this has no effect
GL.DisableClientState(ArrayCap.TextureCoordArray);
}
internal void UpdateQuadVertices(Int32 x, Int32 y, Int32 width, Int32 height)
{
_vertices[0].X = x;
_vertices[0].Y = y;
_vertices[1].X = x + width;
_vertices[1].Y = y;
_vertices[2].X = x;
_vertices[2].Y = y + height;
_vertices[3].X = x + width;
_vertices[3].Y = y + height;
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferId);
GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, (IntPtr) (_vertices.Length*Vector2.SizeInBytes),
_vertices);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
}
public Int32 LoadSimpleTexture()
{
Int32 textureObjectId;
GL.GenTextures(1, out textureObjectId);
GL.BindTexture(TextureTarget.Texture2D, textureObjectId);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
(Int32) TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter,
(Int32)TextureMagFilter.Linear);
var bitmap = new Bitmap("simple.png");
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);
return textureObjectId;
}
}
class Program
{
static void Main()
{
Int32 textureObjectId = 0;
using (var gameWindow = new GameWindow(800, 600))
{
var renderer = new Renderer();
gameWindow.Load += (s, e) =>
{
GL.ClearColor(Color.CornflowerBlue);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.AlphaFunc(AlphaFunction.Greater, 1.0f);
textureObjectId = renderer.LoadSimpleTexture();
};
gameWindow.Resize += (s, e) =>
{
GL.Viewport(0, 0, 800, 600);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(0, 800, 600, 0, -1, 1);
};
gameWindow.RenderFrame += (s, e) =>
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
renderer.DrawQuad(0, 0, 128, 128);
renderer.DrawQuadTex(128, 0, 128, 128, textureObjectId);
gameWindow.SwapBuffers();
};
gameWindow.Run();
}
}
}
}
Update
When I said that whichever function gets called first that is the one who's shape is being shown is inaccurate, actually only the untextured supersedes the textured (i.e. when DrawQuad is present, only untextured is visible)
The sample code you posted, works just fine. Therefore I am unable to reproduce the issue that you described. Evidently from the picture both textured and non-textured quads are present. However the textured quad is in addition blended by the color which causes the rainbow look. To fix this disable the color-pointer or fill the color buffer with a white color.
If the results on your machine do not match the pictures I have posted below, it likely suggests that other forces are at play. A likely related example of this could be choice of video card (-drivers).