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++.
This question already has an answer here:
C# OpenTK - Textured Quad
(1 answer)
Closed 5 years ago.
So im trying to render a texture to a quad using OpenGL and im getting a really weird artifact and I think it being caused by my loading method but im not truly sure.
When you get close to the quad you can see that it is tring to render the red green and blue next to each other. kinda looks like what a pixel on a tv looks like
Rendered Quad
Close up of quad
Texture
LoadTexture
public static uint LoadTexture(string filePath)
{
Bitmap bitmap = new Bitmap("Res/" + filePath);
Gl.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
uint textureID = Gl.GenTexture();
Gl.BindTexture(TextureTarget.Texture2d, textureID);
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Gl.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgb8, data.Width, data.Height, 0, OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
bitmap.UnlockBits(data);
Gl.TexParameter(TextureTarget.Texture2d, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
Gl.TexParameter(TextureTarget.Texture2d, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
return textureID;
}
Rendering
public void Render(TexturedModel texturedModel, StaticShader shader)
{
RawModel model = texturedModel.model;
using (MemoryLock vertexPositions = new MemoryLock(model.vertexPositions))
using (MemoryLock texturePositions = new MemoryLock(texturedModel.textureCords))
{
Gl.VertexAttribPointer((uint)shader.locationPosition, model.dimension, VertexAttribType.Float, false, 0, vertexPositions.Address);
Gl.EnableVertexAttribArray((uint)shader.locationPosition);
Gl.VertexAttribPointer((uint)shader.locationTexturedCoords, 2, VertexAttribType.Float, false, 0, texturePositions.Address);
Gl.EnableVertexAttribArray((uint)shader.locationTexturedCoords);
Gl.PixelStore(PixelStoreParameter.UnpackAlignment, 0);
Gl.ActiveTexture(TextureUnit.Texture0);
Gl.BindTexture(TextureTarget.Texture2d, texturedModel.texture.TextureID);
Gl.Uniform1(shader.locationTextureSampler, 0);
Gl.UniformMatrix4(shader.locationProjectionMatrix, false, projectionMatrix.ToArray());
viewMatrix = Maths.CreateViewMatrix(camera);
Gl.UniformMatrix4(shader.locationViewMatrix, false, viewMatrix.ToArray());
Gl.UniformMatrix4(shader.locationTransformationMatrix, false, model.modelMatrix.ToArray());
//Gl.DrawArrays(PrimitiveType.Triangles, 0, model.vertexPositions.Length / model.dimension);
Gl.DrawElements(PrimitiveType.Triangles, model.indices.Length, DrawElementsType.UnsignedInt, model.indices);
}
}
Shader program
public class StaticShader : ShaderProgram
{
private static string VERTEX_FILE = #"Shader/VertexShader.txt";
private static string FRAGMENTSHADER = #"Shader/FragmentShader.txt";
public int locationPosition;
public int locationTexturedCoords;
public int locationTransformationMatrix;
public int locationProjectionMatrix;
public int locationViewMatrix;
public int locationTextureSampler;
public StaticShader() : base(VERTEX_FILE, FRAGMENTSHADER)
{
locationTransformationMatrix = this.GetUniformLocation("transformationMatrix");
locationProjectionMatrix = this.GetUniformLocation("projectionMatrix");
locationViewMatrix = this.GetUniformLocation("viewMatrix");
locationTexturedCoords = this.GetAttribLocation("textureCords");
locationPosition = this.GetAttribLocation("position");
locationTextureSampler = this.GetUniformLocation("textureSampler");
}
}
Fragment shader
varying vec2 outTextureCords;
uniform sampler2D textureSampler;
void main() {
gl_FragColor = texture(textureSampler, outTextureCords);
}
vertices
public float[] vertexPositions = new float[] {
-0.5f,0.5f,0, //V0
-0.5f,-0.5f,0, //V1
0.5f,-0.5f,0, //V2
0.5f,0.5f,0 //V3
};
public int[] indices = new int[] {
0,1,3, //Top left triangle (V0,V1,V3)
3,1,2 //Bottom right triangle (V3,V1,V2)
};
public float[] textureCords = {
0,0,
0,1,
1,1,
1,0
};
Change the opengl format in the TexImage2d to B and switch internalFormat to rgba
Gl.TexImage2D(TextureTarget.Texture2d, 0, InternalFormat.Rgba, data.Width, data.Height, 0, OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
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 working with C# and OpenTK.
Currently I only want to map a texture on a triangle.
It seems to be working but on nearest texture filter, the whole triangle is only colored with the upper left pixel color of the bmp image and if I set the texture filter to linear the triangle shows still only one color, but it seems whether it is now mixed with the other pixels.
Can someone find the error in the code ?
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
GL.Enable(EnableCap.Texture2D);
GL.ClearColor(0.5F, 0.5F, 0.5F, 1.0F);
int vertexShaderHandle = GL.CreateShader(ShaderType.VertexShader);
int fragmentShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
string vertexShaderSource = #"#version 400
layout(location = 0) in vec3 position;
layout(location = 1) in vec2 uv;
out vec2 texture_uv;
void main()
{
gl_Position = vec4(inPosition.xyz, 1);
texture_uv = uv;
}";
string fragmentShaderSource = #"#version 400
in vec2 texture_uv;
out vec3 outColor;
uniform sampler2D uniSampler;
void main()
{
outColor = texture( uniSampler, texture_uv ).rgb;
}";
GL.ShaderSource(vertexShaderHandle, vertexShaderSource);
GL.ShaderSource(fragmentShaderHandle, fragmentShaderSource);
GL.CompileShader(vertexShaderHandle);
GL.CompileShader(fragmentShaderHandle);
prgHandle = GL.CreateProgram();
GL.AttachShader(prgHandle, vertexShaderHandle);
GL.AttachShader(prgHandle, fragmentShaderHandle);
GL.LinkProgram(prgHandle);
GL.DetachShader(prgHandle, vertexShaderHandle);
GL.DetachShader(prgHandle, fragmentShaderHandle);
GL.DeleteShader(vertexShaderHandle);
GL.DeleteShader(fragmentShaderHandle);
uniSamplerLoc = GL.GetUniformLocation(prgHandle, "uniSampler");
texHandle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, texHandle);
Bitmap bmp = new Bitmap("C:/Users/Michael/Desktop/Test.bmp");
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmpData.Width, bmpData.Height, 0,
OpenTK.Graphics.OpenGL4.PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0);
bmp.UnlockBits(bmpData);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
vaoHandle = GL.GenVertexArray();
GL.BindVertexArray(vaoHandle);
vboHandle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.ArrayBuffer, vboHandle);
float[] bufferData = { 0.5F, 1, 0, 1, 1,
0, 0, 0, 0, 0,
1, 0, 0, 1, 0 };
GL.BufferData<float>(BufferTarget.ArrayBuffer, (IntPtr) (15 * sizeof(float)), bufferData, BufferUsageHint.StaticDraw);
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 5 * sizeof(float), 0);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float));
}
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
GL.DeleteTexture(texHandle);
GL.DeleteProgram(prgHandle);
GL.DeleteBuffer(vboHandle);
GL.DeleteVertexArray(vaoHandle);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.UseProgram(prgHandle);
GL.Uniform1(uniSamplerLoc, texHandle);
GL.BindVertexArray(vaoHandle);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
SwapBuffers();
}
EDIT:
I tried this:
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.UseProgram(prgHandle);
GL.BindVertexArray(vaoHandle);
GL.ActiveTexture(TextureUnit.Texture3);
GL.BindTexture(TextureTarget.Texture2D, texHandle);
GL.Uniform1(uniSamplerLoc, 3);
GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
SwapBuffers();
}
But nothing changed :(
The value of a sampler uniform variable needs to be the texture unit it should sample from. In your code, it is set to the texture name (aka texture id, aka texture handle) instead:
GL.Uniform1(uniSamplerLoc, texHandle);
The texture unit can be set with ActiveTexture(). When glBindTexture() is called, the value of the currently active texture unit determines which unit the texture is bound to. The default for the active texture unit is 0. So if you never called ActiveTexture(), the uniform should be set as:
GL.Uniform1(uniSamplerLoc, 0);
Just as a heads-up, another related source of errors is that the value of the uniform is a 0-based index of the texture unit, while the glActiveTexture() call takes an enum starting with GL_TEXTURE0. For example with the C bindings (not sure how exactly this looks with C# and OpenTK, but it should be similar enough), this would bind a texture to texture unit 3, and set a uniform sampler variable to use it:
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texId);
glUniform1i(texUniformLoc, 3);
Note how GL_TEXTURE3 is used in the argument for glActiveTexture(), but a plain 3 in glUniform1i().
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.