Reputation: 259
I've been trying to figure out how to get this to work. I'm using OpenTK for C# to modify images. Right now I'm able to load an image successfully, but when I try to modify the image with shaders is where I begin to have problems. I have 3 basic methods:
So here are the 2 problems I'm having:
1) I'm trying to create 2 triangles that span the entire texture. This is because I want my fragment shader to do work over the entire texture. What I'm actually getting is a large triangle that covers the left side of the screen, and a rectangle on the right side of the screen. EDIT: Made some progress! But still looking weird...Shader scripts updated..EDIT2: Newer progress pic uploaded
2) The shapes are showing up green, when I want them to use the colors from the texture but red channel modified. What's important to know is that I need to be able to analyze each pixel. Changing the red channel of every pixel is JUST a proof of concept for what I want to actually do (using color distance formulas, hue shifting, etc. but I need to start simpler first)
This is the image I've successfully loaded as a texture:Loaded Texture
Here's the code for LoadTexture:
public int LoadTexture(string file)
{
Bitmap bitmap = new Bitmap(file);
int tex;
GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
GL.GenTextures(1, out tex);
GL.BindTexture(TextureTarget.Texture2D, tex);
BitmapData data = bitmap.LockBits(new System.Drawing.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);
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.Repeat);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
return tex;
}
Here's the code for DrawImage:
public static void DrawImage(int image)
{
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();
GL.Ortho(0, 1920, 0, 1080, -1, 1);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
GL.Disable(EnableCap.Lighting);
GL.Enable(EnableCap.Texture2D);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, image);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0,1);
GL.Vertex3(0, 0, 0);
GL.TexCoord2(1, 1);
GL.Vertex3(1920, 0, 0);
GL.TexCoord2(1, 0);
GL.Vertex3(1920, 1080, 0);
GL.TexCoord2(0, 0);
GL.Vertex3(0, 1080, 0);
GL.End();
AddShaders();
GL.Disable(EnableCap.Texture2D);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
ErrorCode ec = GL.GetError();
if (ec != 0)
System.Console.WriteLine(ec.ToString());
Console.Read();
}
And here's the code for AddShaders:
private static void AddShaders()
{
/***********Vert Shader********************/
var vertShader = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertShader, @"attribute vec3 a_position;
varying vec2 vTexCoord;
void main() {
vTexCoord = a_position.xy;
gl_Position = vec4(a_position, 1);
}");
GL.CompileShader(vertShader);
/***********Frag Shader ****************/
var fragShader = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragShader, @"uniform sampler2D sTexture;
varying vec2 vTexCoord;
void main ()
{
vec4 color = texture2D (sTexture, vTexCoord);
color.r = 0.5;
// Save the result
gl_FragColor = color;
}");
GL.CompileShader(fragShader);
var program = GL.CreateProgram();
GL.AttachShader(program, vertShader);
GL.AttachShader(program, fragShader);
GL.LinkProgram(program);
GL.ClearColor(Color.AliceBlue);
// OpenGL expects vertices to be defined counter clockwise by default
float[] vertices = {
// Left bottom triangle
-1f, 1f, 0f,
-1f, -1f, 0f,
1f, -1, 0f,
// Right top triangle
1f, -1f, 0f,
1f, 1f, 0f,
-1f, 1f, 0f
};
var buffer = GL.GenBuffer();
var positionLocation = GL.GetAttribLocation(program, "a_position");
GL.EnableVertexAttribArray(positionLocation);
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float,false,0,0);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(ushort)), vertices, BufferUsageHint.StaticDraw);
GL.DrawArrays(PrimitiveType.Triangles, 0, vertices.Length);
GL.UseProgram(program);
}
I've researched this for a few days and I'm just completely stuck. Thanks to anyone who can see what I'm doing wrong. It HAS to be something small and dumb on my part!
EDIT: When I remove ALL vertex related code in AddShaders, I get the output I want, except its 1/4 the size and flipped in the top right of the screen. So, somehow my shaders don't even care about the vertices. Why is it being scaled down to 1/4 size and flipped?
EDIT2: Ok so thanks to Robert Rouhani, I've ALMOST got this working! Progress It looks like the triangle vertices might be messed up??
Here's my new code. I refactored functionality into methods, stopped creating programs / buffers every frame, etc. Now I have class level variables to hold the GL specific data, methods to create the GL program for the app, create the shaders, create the buffers, etc. Also I know that the 1920x1080 hardcode is, well, hardcoded. That is on my plate to make dynamic.
string file = "lambo2.png";
int program;
int vertShader;
int fragShader;
int buffer;
int positionLocation;
int texture;
float[] vertices = {
// Left bottom triangle
-1f, -1f, 0f,
1f, -1f, 0f,
1f, 1f, 0f,
// Right top triangle
1f, 1f, 0f,
-1f, 1f, 0f,
-1f, -1f, 0f
};
private void CreateProgram()
{
program = GL.CreateProgram();
GL.AttachShader(program, vertShader);
GL.AttachShader(program, fragShader);
GL.LinkProgram(program);
}
private void CreateShaders()
{
/***********Vert Shader********************/
vertShader = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertShader, @"attribute vec3 a_position;
varying vec2 vTexCoord;
void main() {
vTexCoord = (a_position.xy + 1) / 2;
gl_Position = vec4(a_position, 1);
}");
GL.CompileShader(vertShader);
/***********Frag Shader ****************/
fragShader = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragShader, @"precision highp float;
uniform sampler2D sTexture;
varying vec2 vTexCoord;
void main ()
{
vec4 color = texture2D (sTexture, vTexCoord);
if(color.r < 0.3){color.r = 1.0;}
// Save the result
gl_FragColor = color;
}");
// GL.ShaderSource(fragShader, System.IO.File.ReadAllText(@"C:\Users\Matt\Desktop\hue-shader-backup.ps"));
GL.CompileShader(fragShader);
}
private void InitBuffers()
{
buffer = GL.GenBuffer();
positionLocation = GL.GetAttribLocation(program, "a_position");
GL.EnableVertexAttribArray(positionLocation);
GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(ushort)), vertices, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 0, 0);
}
private void Init()
{
texture = LoadTexture(file);
CreateShaders();
CreateProgram();
InitBuffers();
}
public int LoadTexture(string file)
{
Bitmap bitmap = new Bitmap(file);
int tex;
GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
GL.GenTextures(1, out tex);
GL.BindTexture(TextureTarget.Texture2D, tex);
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
BitmapData data = bitmap.LockBits(new System.Drawing.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);
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);
return tex;
}
public void DrawImage(int image)
{
GL.Viewport(new Rectangle(0, 0, 1920, 1080));
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();
GL.Ortho(0, 1920, 0, 1080, 0, 1);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
GL.Disable(EnableCap.Lighting);
GL.Enable(EnableCap.Texture2D);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, image);
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0, 1);
GL.Vertex3(0, 0, 0);
GL.TexCoord2(1, 1);
GL.Vertex3(1920, 0, 0);
GL.TexCoord2(1, 0);
GL.Vertex3(1920, 1080, 0);
GL.TexCoord2(0, 0);
GL.Vertex3(0, 1080, 0);
GL.End();
RunShaders();
GL.Disable(EnableCap.Texture2D);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
ErrorCode ec = GL.GetError();
if (ec != 0)
System.Console.WriteLine(ec.ToString());
Console.Read();
}
private void RunShaders()
{
GL.ClearColor(Color.AliceBlue);
GL.UseProgram(program);
GL.DrawArrays(PrimitiveType.Triangles, 0, vertices.Length / 3);
ErrorCode ec = GL.GetError();
if (ec != 0)
System.Console.WriteLine(ec.ToString());
Console.Read();
}
Upvotes: 0
Views: 1228
Reputation: 14678
Going to start an answer instead of continuing the comments. You've still got a few minor issues that are compounding. You should comment out everything between and including GL.Begin
and GL.End
as the RunShaders
function should do draw everything you need to the screen. Also comment out the GL.Ortho
line, you don't need it if you're working with the vertices in the [-1, 1] range.
Second, your issue is that you're only uploading half your vertex buffer to the GPU. In InitBuffers
on the GL.BufferData
line, change sizeof(ushort)
to sizeof(float)
, since your vertices are floats (4 bytes long) and not ushorts (2 bytes long).
GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(float)), vertices, BufferUsageHint.StaticDraw);
Together this should get your program working.
Upvotes: 1