I feel like a Noob C++ GL 4.1 and GLSL 3.3 help please!

Started by
12 comments, last by mynameisnafe 10 years, 1 month ago

Please help! I am trying to render a textured quad.
For the life of me, it retains black with a textured phong shader. In fact, when I use a phong shader, it stays black, even though its the same phong shader that renders all the models around said dastardly quad.

I'm hoping the code is quite explanatory so I'm just gonna walk through it and someone with a sharper eye than I can hunt for glory. Beware, there is a LOT of code - just looked and I feel bad.. however I do have to explain a couple of classes, FreeImage, and how it all works. I have quite a big project now.. it just doesn't yet do a lot because I don't have any textures. Just some 30-second -to-load .objs that are blue, and almost shiny.

I digress, the 'textured' quad gets made with the line below, which calls CreateQuad inline. It's gonna have some picture of some wood or something on it. It sits at 5,5,0 world space, and is 5x5 units in area.


m_HUD->Create( *ShapeFactory::CreateQuad( 5, 5, 5, 5, true),
	         "../Shaders/basic_phong.vs", "../Shaders/basic_phong.fs" );

Heres CreateQuad. (I post this so you can spot an error in it, but it ~should~ be pretty standard. It creates a quad. I hope.)


GeometryData* // Draw with GL_QUADS, centred on x, y
	ShapeFactory::CreateQuad(int x, int y, int width, int height, bool textured)
{
	GeometryData* shape = new GeometryData();
	shape->SetTopology(7); // ..->SetTopology(GL_QUADS);
	
	float x0, x1, y0, y1;
	
	x0 = x + (width/2);	x1 = x - (width/2);
	y0 = y + (height/2);	y1 = y - (height/2);

	shape->numVertices = 4;
	shape->m_Vertices = (Cream::Vertex3D*) calloc (4, sizeof(Cream::Vertex3D) );
	shape->m_Vertices[0] = Vertex3D( x0, y1 , 0.0f );
	shape->m_Vertices[1] = Vertex3D( x1, y1 , 0.0f );
	shape->m_Vertices[2] = Vertex3D( x1, y0 , 0.0f );
	shape->m_Vertices[3] = Vertex3D( x0, y0 , 0.0f );

	shape->numNormals = 4;	
	shape->m_Normals = (Cream::Vertex3D*) calloc (4, sizeof(Cream::Vertex3D) );
	shape->m_Normals[0] = Vertex3D( 0.0f, 0.0f , 1.0f );
	shape->m_Normals[1] = Vertex3D( 0.0f, 0.0f , 1.0f );
	shape->m_Normals[2] = Vertex3D( 0.0f, 0.0f , 1.0f );
	shape->m_Normals[3] = Vertex3D( 0.0f, 0.0f , 1.0f );

	if( textured )
	{
		shape->numUVs = 4;
		shape->m_TexCoords = (Cream::Vertex2D*) calloc (4, sizeof(Cream::Vertex2D) );
		shape->m_TexCoords[0] = Vertex2D(0.0f, 1.0f);
		shape->m_TexCoords[1] = Vertex2D(1.0f, 1.0f);
		shape->m_TexCoords[2] = Vertex2D(1.0f, 0.0f);
		shape->m_TexCoords[3] = Vertex2D(0.0f, 0.0f);
	}

	shape->numIndices = 0;

	return shape;	
}

m_HUD is of type GLMesh. GLMesh::Create in short creates a VAO from that GeometryData, a Shader from those filenames, and holds a material - which contains uniforms I may want to set for the model; colour, gl texture id, texture name, etc. It has the Render method.

So, GLMesh::PreRender sets uniforms by talking to its GLShader, and it works like this: If the material says I'm textured, if it contains a texture ID for a diffuse map -- then I have a KdID, I set it. It also sets camera matrices. bOk is bOk, whether I use a texture or no, so the "1" is getting sent to map_kd in the shader.


if(material->isTextured)
{
	if( material->KdID != 0 )
	{	// both barrels and still nothing!
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, material->KdID );

		bOk = bOk && m_Shader->SetUniformInt( 1, "map_kd" );
	}
	if( material->KaID != 0 )
	{
		//glActiveTexture(GL_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, material->KaID );
	
        	bOk = bOk && m_Shader->SetUniformInt( 2, "map_ka" );		
	}
        ......
        else
        {       // always fires for non-textured shader, always works*
                bOk = bOk && m_Shader->SetUniformVec4(glm::vec4(material->Kd.GLM(), 1.0), "m_Kd");
        }

So that explains how the quad gets made and how it gets rendered, using texture unit or colour, the other data that it requires etc to exist in GL land. I'm pretty sure everything's good except setting the texture / sampler (I've said little of textures, I know).

I got FreeImage. It gives me bits, and I copy them, and keep them locally, then I let GL copy them and GL gives me a texture unit.

It gets convoluted though - as I said, I keep the texture locally and don't free them bits after I give them to OpenGL, because I want to play later - so I have a class for a texture, that lets me access those bits from FreeImage with GetPixel, SetPixel, Fill, etc, as if it were a 2D array. This I feel would be a handy thing to make heightmaps with, among other things.

So FreeImage gives me bits, FreeImage is disguised as ImageFactory.

The bits go to a GLBitmap Constructor ( .. bmp->GetPixel(x, y) ), along with width, height, and whether the bits need shuffling or not when they get copied.

The GLBitmap goes to a GLTexture, which takes the width and height and the bits and does the OpenGL stuff in GLTexture::Load.


Cream::GLTexture Cream::LoadTexture(std::string path)
{
ImageFactory* factory = ImageFactory::Create();
unsigned int width(0), height(0);
bool bShuffle(false);
BYTE* bits = factory->Load( path, width, height, bShuffle );
GLBitmap* bitmap = new GLBitmap( width, height, bShuffle, bits );
GLTexture texture(GL_TEXTURE_2D, GL_RGBA);
texture.SetParam(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
texture.SetParam(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
texture.SetParam(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
texture.SetParam(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
texture.Load( bitmap );
return texture;
}

Okay, so here's the constructor for GLBitmap - a Pixel is 4 bytes, a BYTE is an unsigned char. You knew that. So we cast the bits to Pixels and we know we have w * h of them. This could be a place where things have gone wrong though.


GLBitmap(int w, int h, bool bShuffle, BYTE* bits)
{
	width = w;
	height = h;
	Pixel* src = (Pixel*)bits;

	pixels = new Pixel[w*h];

	for(int i = 0; i < w; ++i)
	{
		for(int j = 0 ; j < h; ++j)
		{			
			const Pixel& p = src[(i * h) + j];
			pixels[(i * h) + j] = (bShuffle) ? Pixel(p.B, p.G, p.R, p.A) : p ;
		}
	}
}

And finally our wrapper for an int, GLTexture, here's the Load method, that makes the actual OpenGL calls to generate a texture ID and set the bits to it.


bool GLTexture::Load( GLBitmap* bitmap )
{
	glGenTextures( 1, &m_GLID );
	glBindTexture( target, m_GLID );

	glTexImage2D( target, 0, GL_RGBA, bitmap->Width(), bitmap->Height(), 
                              0, GL_BGRA, GL_UNSIGNED_BYTE, bitmap->Data() );

	for( auto i = params.begin(); i != params.end(); ++i )
	        glTexParameteri( target, i->first, i->second );
 
	return (m_GLID!=0);
}

And so, that explains, in SetMaterial, the workings behind Cream::LoadTexture()


if(mat->isTextured)
{
	material->isTextured = true;

	if(mat->mapKd.size() != 0)	// if there is a filename
	{
		if(mat->KdID != 0)		//if there is a gl/sl tex unit id
			material->KdID = mat->KdID;	//grab it
		else
		{
			m_Textures[0] = Cream::LoadTexture( mat->mapKd ); // else load it
			material->KdID = m_Textures[0].ID();
		}
		material->mapKd = mat->mapKd;
	}
....

So where's my error.. Well I suspect a couple of things. I have a debug callback set, would it inform me of anything obvious?

- the glActiveTexture(GLTEXTURE0+n) calls - they're wrong - the wrong place, too often, wrong texture unit ?
- not sending the right value through to the sampler in the shader*, and no map is being sampled.. linked to above?
- the bits were mangled or the pointer lost by the time OpenGL gets to them in GLTexture::Load

As I say, the coloured phong shader and the textured phong shader are identical except for swapping a vec4 uniform for a sampler and a vec2 attribute, and getting the colour by sampling the texture.

Everything renders fine except the textured-not-coloured stuff.

Please find my error(s), I'll be around to let you know how much you helped! It was a long post.. apologies! A lot of code..

Thank you so, so much in advance! There's a lot I want to do with textures that I can't yet!

In fact, here's a screenshot of a mildly shiny blue cube, a white floor-grid, and a square black hole. I kid, it's that quad.

Advertisement

Could you explain what "Everything renders fine except the textured-not-coloured stuff." means? maybe a screenshot?

Is your quad rendered with the shader on? Are you able to output a debug red color to verify the shader is on?

Are you binding the texture and sending the uniform to the shader?

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

Screenshots up. See the cube and the black quad? I can render that quad using that cube's shader and get a nice yellow on it. I simply set material->isTextured to false and give the other shader names in the GLMesh constructor way at the top. And set a colour on the material.

Okay.. the generic colured shader gives me yellow, the phong shader on the quad makes it black, same phong shader as the cube.. curiouser and curiouser..!

Could you explain what "Everything renders fine except the textured-not-coloured stuff." means? maybe a screenshot?


If I use texture shader, black. If coloured shader, fine. I say 'stuff' because I think the issue with the texturing resides in the texture stuff.. GLTexture, GLBitmap, or setting the texture / binding it. glGenTextures gives me a texture id, even when I was giving it nothing cos I had been calling FreeImage_Unload too early.. 80% sure that those are the right bytes going on the card. Lol.

So do any of your models/shaders have a textured model? Are you sending the uniform to the shader? What does your shader look like?

Does the quad have normal? Can you at least strip your shader down to texture2D(diffuse, texCoord))...... don't know if its a lighting problem/attribute problem, uniform problem, bad texture problem..

Why don't you verify glUseProgram(0) and binding the texture shows up on your quad wihtout using a shader.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

So do any of your models/shaders have a textured model? Are you sending the uniform to the shader? What does your shader look like?
Does the quad have normal? Can you at least strip your shader down to texture2D(diffuse, texCoord))...... don't know if its a lighting problem/attribute problem, uniform problem, bad texture problem..
Why don't you verify glUseProgram(0) and binding the texture shows up on your quad wihtout using a shader.


1. What?
2. Yes. All the lighting colours vectors, a bunch of matrices, and the texture unit all go to the shader no problem. The GLMesh looks at the material in SetMaterial(), checking for a gl tex id or texture path, and loads texture via a factory (Freeimage) if it needs to. In GLMesh::Render, GLMesh::PreRenderlooks at the material it holds and sets a colour if there is no texture ids - PreRender sets uniforms. See the code in the original post.

3. Look like? There is a basic coloured pass thru shader, a phong coloured shader, and a phong textured shader. In the screenshot in the original post, the phong coloured is the cube on the right, the coloured pass thru is the XYZ and the grid, and the textured quad shader is the black quad in the middle.

I rendered the quad with the pass thru shader, uniform colour as yellow, and it was yellow. I use the textured phong shader or the coloured phong shader, and the quad is black. Verr strange.

As shown in the code in the original post, the quad has four vertices and four normals, each normal is z positive.

The phong colour and phong texture shader are identical except the change from a uniform vec4 colour to a in vec2 uv and a uniform sampler2D tex.

What happens if you use these tex coords

shape->m_TexCoords[0] = Vertex2D(1.0f, 0.0f);
shape->m_TexCoords[1] = Vertex2D(0.0f, 0.0f);
shape->m_TexCoords[2] = Vertex2D(0.0f, 1.0f);
shape->m_TexCoords[3] = Vertex2D(1.0f, 1.0f);

Sorry I disappeared - work/sleep.

What happens if you use these tex coords

shape->m_TexCoords[0] = Vertex2D(1.0f, 0.0f);
shape->m_TexCoords[1] = Vertex2D(0.0f, 0.0f);
shape->m_TexCoords[2] = Vertex2D(0.0f, 1.0f);
shape->m_TexCoords[3] = Vertex2D(1.0f, 1.0f);

Trying them now..

Legen.. You are SO AWESOME, THANKYOU!

Seriously for chewing through that first post and it was just the texture coordinates.. I'm glad though, that it was a noob error and not down to the whole mechanism of materials, geometry, vaos n shaders, and those texture classes.. there's some saving face in that, I think.

I'll let you in on a secret: I changed my phong shader fragment shader to just set fragColour as what it sampled, no lighting calcs.. something may be wrong with my normals or the phong code.. another thread, coming soon *facepalm*.

Thank you again! So was my texture' backwards' or 'inside out' or what?

..-dary!

From the original post you seem to have specified the quad verts as botRight -> botLeft -> topLeft -> topRight, but the quad texcoords were

topLeft -> topRight -> botRight -> botLeft so the order didnt match.

This topic is closed to new replies.

Advertisement