I'm using OpenTK (.NET wrapper around OpenGL), and have a GLControl in a form.
I've used the Loading a texture from disk and Loading multiple textures and passing through to a shader tutorial to get texture mapping working. But when I try to to set GL.ActiveTexture(TextureUnit.Texture1), and do all of my texture binding and loading on that unit, I get a solid colour displaying (seemingly the colour at texel (0,0)).
I'm befuddled as to why simply switching the texture unit to use 1 instead of the default 0 would break the program.
I figured I'd move away from the fixed-function pipeline and instead use shaders to do the texture colouring. No luck there either.
I'm thinking it has to do with the way I feed vertices to the vertex shader using glDrawArrays. I use constant vertex data in the shader, picked using gl_VertexID, for simplicity instead of using attributes.
Vertex Shader:
#version 420
const mat4x3 vs = mat4x3(-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0);
const mat4x2 ts = mat4x2(0.0, 1.0,1.0, 1.0, 1.0, 0.0, 0.0, 0.0);
out vec2 tcoord;
void main() {
gl_Position = vec4(vs[gl_VertexID],1);
tcoord = ts[gl_VertexID];
}
Fragment Shader:
#version 420
uniform sampler2D MyTexture0;
uniform sampler2D MyTexture1;
uniform sampler2D MyTexture2;
uniform sampler2D MyTexture3;
in vec2 tcoord;
out vec4 color;
void main(void)
{
color = texture2D( MyTexture3, tcoord);
}
Draw command:
GL.DrawArrays(PrimitiveType.Quads, 0, 4);
Entire source:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System.Diagnostics;
using System.Drawing.Imaging;
namespace TextureShaderDemoGLControl
{
public partial class Form1 : Form
{
bool immediateMode = true;
public Form1()
{
InitializeComponent();
}
Stopwatch sw = Stopwatch.StartNew();
int fragmentShaderHandle, shaderProgramHandle, texture0, texture1, texture2, texture3;
Bitmap bitmap0 = new Bitmap(@"img.gif");
string vertShaderSource = @"
#version 420
const mat4x3 vs = mat4x3(-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0);
const mat4x2 ts = mat4x2(0.0, 1.0,1.0, 1.0, 1.0, 0.0, 0.0, 0.0);
out vec2 tcoord;
void main() {
gl_Position = vec4(vs[gl_VertexID],1);
tcoord = ts[gl_VertexID];
}
";
string fragmentShaderSource = @"
#version 420
uniform sampler2D MyTexture0;
uniform sampler2D MyTexture1;
uniform sampler2D MyTexture2;
uniform sampler2D MyTexture3;
in vec2 tcoord;
out vec4 color;
void main(void)
{
color = texture2D( MyTexture3, tcoord);
}"
;
void CreateShaders()
{
int vertShaderHandle = GL.CreateShader(ShaderType.VertexShader);
GL.ShaderSource(vertShaderHandle, vertShaderSource);
GL.CompileShader(vertShaderHandle);
Debug.WriteLine(GL.GetShaderInfoLog(vertShaderHandle));
fragmentShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragmentShaderHandle, fragmentShaderSource);
GL.CompileShader(fragmentShaderHandle);
Debug.WriteLine(GL.GetShaderInfoLog(fragmentShaderHandle));
// Create program
shaderProgramHandle = GL.CreateProgram();
GL.AttachShader(shaderProgramHandle, vertShaderHandle);
GL.AttachShader(shaderProgramHandle, fragmentShaderHandle);
GL.LinkProgram(shaderProgramHandle);
Debug.WriteLine(GL.GetProgramInfoLog(shaderProgramHandle));
GL.UseProgram(shaderProgramHandle);
}
private void CreateTexture(out int texture, Bitmap bitmap)
{
// load texture
GL.GenTextures(1, out texture);
// Still required else TexImage2D will be applied on the last bound texture
GL.BindTexture(TextureTarget.Texture2D, texture);
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);
}
private void DrawRect()
{
GL.Begin(PrimitiveType.Quads);
GL.Color3(Color.Silver);
GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(-1.0f, -1.0f, -1.0f);
GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(-1.0f, 1.0f, -1.0f);
GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(1.0f, 1.0f, -1.0f);
GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(1.0f, -1.0f, -1.0f);
GL.End();
}
private void glControl1_Load(object sender, EventArgs e)
{
CreateShaders();
//GL.Enable(EnableCap.Texture2D);
// Immediate mode drawing works with Texture Unit 0
TextureUnit tUnit = TextureUnit.Texture0;
// Immediate mode drawing fails with Texture Unit 1
// TextureUnit tUnit = TextureUnit.Texture1;
GL.ActiveTexture(tUnit);
CreateTexture(out texture0, bitmap0);
GL.Uniform1(GL.GetUniformLocation(shaderProgramHandle, "MyTexture0"), tUnit - TextureUnit.Texture0);
string a = GL.GetString(StringName.ShadingLanguageVersion);
string b = GL.GetString(StringName.Version);
int vao = GL.GenVertexArray();
GL.BindVertexArray(vao);
glControl1.Invalidate();
}
private void glControl1_Paint(object sender, PaintEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
if (immediateMode) {
// The immediate mode DrawRect which encapsulates GL.Begin(); GL.Vertex2(..) ... ; GL.End() works nicely
DrawRect();
} else {
// Something must be wrong with the way DrawArrays feeds vertices to the vertex shader.
//GL.DrawArrays(PrimitiveType.Quads, 0, 4);
}
sw.Stop();
label1.Text = (1000.0 / sw.Elapsed.TotalMilliseconds).ToString();
sw.Restart();
glControl1.SwapBuffers();
}
private void glControl1_Resize(object sender, EventArgs e)
{
GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
}
}
}