VBO - Strange black quad appears sometimes

Started by TheBoneJarmer, October 16, 2015, 18:20:34

Previous topic - Next topic

TheBoneJarmer

Hey guys

I got a very odd situation here. I have a set of primitive 3D models (cube, sphere and cylinder) which I made using AC3D. I export them as OBJ and load them in my game. The cube and sphere are displayed just fine, but when I try to load my cylinder, the effect you can see in the screenshot happens. I'll add my 3 models in case you'd like to study them. If you need any code just ask. I'm not certain what to add because I do not know what the problem's source is. Bytheway, it is black because I didn't add any lights or emission in case you'd wonder.

Thanks in advance!
TBJ

Kai

Looks fine in Blender and when loaded with LWJGL3's demo WavefrontMeshLoader. (see attached image).
Though the bottom side of the cylinder has flipped normals pointing into +Y and also inconsistent/clockwise winding order.
With backface culling enabled in OpenGL you will not see the bottom side of your cylinder.
Regarding the strange quad, I somehow suspect your VBO being bigger than the actual model data and containing garbage at the end and you issue more vertices to be rendered than you imported.

TheBoneJarmer

Quote from: Kai on October 16, 2015, 18:58:56
I somehow suspect your VBO being bigger than the actual model data and containing garbage at the end and you issue more vertices to be rendered than you imported.

I suspected the same thing so I've been searching all classes involved. But it makes no sense because it doesn't happen to the sphere or cube. Thanks for taking a look at it that way! Indeed, it does appear fine in Blender but perhaps it has something to do with a small bug inside the meshes itself? I know you tested it out but what you told me about the flipped normals and such may have something to do with it. Perhaps it is the data that somehow is different and because of a piece of code I wrote it may not display right.

That being said, I'm using shaders right now but I thinking about creating an example with the fixed function pipeline instead of shaders to see if it does make any differences or not. It could help me locating the source of the problem and making sure the shaders don't have anything to do with it.

Kai

I am very certain that the shaders are not the issue.
It's very unlikely that the uniform vertex transformation in your shader correctly transforms all vertices except that few.
After all a shader transforms what is given to it. Using the fixed-function pipeline will very likely not change anything.
What I would do is to suspect the problem in the wavefront obj loader.
Then to verify that assumption I would debug the positions that it imports/produces in the positions buffer by printing them out or by computing the bounding box or by asserting that they do not exceed a certain value.
If the loader is fine, then I would verify whether you issue the same number of vertices in the draw call that you imported from the obj file.

TheBoneJarmer

Quote from: Kai on October 16, 2015, 20:40:19
What I would do is to suspect the problem in the wavefront obj loader.
Then to verify that assumption I would debug the positions that it imports/produces in the positions buffer by printing them out or by computing the bounding box or by asserting that they do not exceed a certain value.
If the loader is fine, then I would verify whether you issue the same number of vertices in the draw call that you imported from the obj file.

Done all that, bounding box is correct, positions are correct. Nope, results are positive so it can't be the OBJ loader.

Quote from: Kai on October 16, 2015, 20:40:19
It's very unlikely that the uniform vertex transformation in your shader correctly transforms all vertices except that few.
After all a shader transforms what is given to it. Using the fixed-function pipeline will very likely not change anything.

It actually did. I copied my engine's source code to a temp folder, removed the entire shader thing and replaced it with FFP. The model is now rendered the way it should. That proves at least the VBO and the OBJ loader work so I can remove that from my list of possible sources. You are right about the shaders, they only render what they receive. So the shaders are not the source of the problem either. It seems something else is causing troubles.

TheBoneJarmer

Okay, I did some testing again and I finally found the source of the problem. It turns out it has to do with the fragment shader. And a really stupid thing too. Here is my shadercode:

#version 120

struct Light
{
	vec3 position;
	vec4 color;
	float range;
	int directional;
	int enabled;
};

uniform mat4 MVP;
uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 NormalMatrix;

//Material
uniform vec4 MaterialDiffuse = vec4(1.0, 1.0, 1.0, 1.0);
uniform vec4 MaterialAmbient = vec4(0, 0, 0, 0);
uniform vec4 MaterialSpecular = vec4(1.0, 1.0, 1.0, 1.0);
uniform vec4 MaterialEmission = vec4(0.0, 0.0, 0.0, 1.0);
uniform float MaterialShininess = 0;
uniform sampler2D MaterialTexture0;
uniform sampler2D MaterialTexture1;
uniform sampler2D MaterialTexture2;
uniform sampler2D MaterialTexture3;
uniform sampler2D MaterialTexture4;
uniform sampler2D MaterialTexture5;
uniform sampler2D MaterialTexture6;
uniform sampler2D MaterialTexture7;
uniform int UseTexture0;
uniform int UseTexture1;
uniform int UseTexture2;
uniform int UseTexture3;
uniform int UseTexture4;
uniform int UseTexture5;
uniform int UseTexture6;
uniform int UseTexture7;

//Light
uniform Light lights[100];
uniform int LightCount;

varying vec3 VertexPosition;
varying vec3 VertexNormal;
varying vec3 EyeDirection;

void main()
{
	//The final fragment color
	vec4 finalColor = vec4(0.0, 0.0, 0.0, 0.0);
	
	//Apply the material based on all the lights in the scene
	vec4 finalTexture = vec4(1.0, 1.0, 1.0, 1.0);
	
	if (UseTexture0 == 1) finalTexture = texture2D(MaterialTexture0, gl_TexCoord[0].st);
	if (UseTexture1 == 1) finalTexture += texture2D(MaterialTexture1, gl_TexCoord[0].st);
	if (UseTexture2 == 1) finalTexture += texture2D(MaterialTexture2, gl_TexCoord[0].st);
	if (UseTexture3 == 1) finalTexture += texture2D(MaterialTexture3, gl_TexCoord[0].st);
	if (UseTexture4 == 1) finalTexture += texture2D(MaterialTexture4, gl_TexCoord[0].st);
	if (UseTexture5 == 1) finalTexture += texture2D(MaterialTexture5, gl_TexCoord[0].st);
	if (UseTexture6 == 1) finalTexture += texture2D(MaterialTexture6, gl_TexCoord[0].st);
	if (UseTexture7 == 1) finalTexture += texture2D(MaterialTexture7, gl_TexCoord[0].st);
	
	for (int i=0; i<LightCount; i++) {
		if (lights[i].enabled == 1) {
			
			vec4 finalMaterial = vec4(0.0, 0.0, 0.0, 0.0);		
			vec3 lightDirection = (NormalMatrix * vec4(lights[i].position, 1)).xyz + EyeDirection;
			
			if (lights[i].directional == 0) {
				float lightDistance = length(lights[i].position - VertexPosition);
				float lightAttenuation = 1 / (1.0 + (0 * lights[i].range) + ((1 / (lights[i].range * lights[i].range)) * (lightDistance * lightDistance)));
				
				vec3 n = normalize(VertexNormal);
				vec3 l = normalize(lightDirection);

				float theta = clamp(dot(l, n), 0, 1);
				
				finalMaterial += MaterialAmbient * finalTexture * lights[i].color;
				finalMaterial += MaterialDiffuse * finalTexture * lights[i].color * theta;				
				finalMaterial += MaterialSpecular * finalTexture * pow(theta, 5);
				finalMaterial *= lightAttenuation;
				
				finalColor += finalMaterial / (lightDistance / lights[i].range);
			} else {				
				vec3 n = normalize(VertexNormal);
				vec3 l = normalize(lights[i].position);

				float theta = clamp(dot(n, -l), 0, 1);
			
				finalMaterial += MaterialAmbient * MaterialDiffuse * finalTexture;
				finalMaterial += MaterialDiffuse * finalTexture * lights[i].color * theta;
				finalMaterial += MaterialSpecular * finalTexture * pow(theta, 5);
				
				finalColor += finalMaterial;
			}
		}
	}
	
	finalColor.w = MaterialDiffuse.w * finalTexture.w;
	finalColor += vec4(MaterialEmission.xyz, 0.0);
	
	//Set the result
	gl_FragColor = finalColor;
}


The problem is rather odd and I have not been able to figure out how to fix it but at least I do know the source of the problem. It has to do with this line:

if (lights[i].enabled == 1) {


For some reason the condition is true, even when I set the uniform in my Java code to 0. So why is the condition still true? The reason I see that black quad is because none of the other lighting uniform variables have values, so it only would make sense strange results occur. Because if the condition is true, it would check if it is directional or not, since directional is 0, it assumes it is a point light and from that point on shit happens. When I put the entire code inside that if condition as a multiline comment that black quad won't render and things are the way I expected.

I'll search and try on, but also here any help would be appreciated! Kai, thanks for your help so far! Your advice helped me figuring this one out.

EDIT:

Is it just me or is the codeblock not appearing correct? I tried to edit and adjust it, but for some odd reason no scrollbars appear when they do in the previewmode when editing this message.

TheBoneJarmer

Sorry for the triple post but due to a bug in the codeblock my fragment shader is not entirely visible. I reported the bug to Spasi and since he asked me not to edit the post anymore, I'll make an exception for now and add a reply with the fragment shader as attachment. Sorry for the inconvience!

Kai

Did you ever think about throwing everything in your fragment shader over board and replace it with:

#version 110
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}


What does that show?

TheBoneJarmer

It works like a charm that way. I started out doing that and slowely adding more and more of my original shader I added as attachment to see whether or not it would make a difference. And it turns out the problem I described above is what is causing that quad.

Kai

Huh... then it's definitely got to be a driver bug.
I cannot explain how a fragment shader could possibly produce fragments where there were no primitives.
Can you get hold of another computer to try your app on that, preferably with a different graphics card/driver?
Maybe you feed some shader API functions with wrong data and somehow (due to a driver bug) cause other memory to be overwritten?
Are you using a geometry shader or framebuffer objects?
Could you probably make a standalone single-file example that reproduces your issue?

TheBoneJarmer

Quote from: Kai on October 17, 2015, 12:26:56
Huh... then it's definitely got to be a driver bug.
I cannot explain how a fragment shader could possibly produce fragments where there were no primitives.
Can you get hold of another computer to try your app on that, preferably with a different graphics card/driver?
Maybe you feed some shader API functions with wrong data and somehow (due to a driver bug) cause other memory to be overwritten?
Are you using a geometry shader or framebuffer objects?
Could you probably make a standalone single-file example that reproduces your issue?

I'll give all of this a try!. I just tried something with the if condition I was talking about. It turns out the condition works correct after all. Because when I replaced the code inside the if block with a "finalColor = vec4(1.0, 1.0, 1.0, 1.0);". According to my previous theory it should turn white, but it didn't. Which means the code never ran in the first place and the if conditon returned false.

TheBoneJarmer

Okay, I finally managed to find the source of the problem. I don't think it has anything to do with my driver (luckily *pfew*) but with this line in the fragment shader:

vec3 lightDirection = (NormalMatrix * vec4(lights[i].position.x, -lights[i].position.y, lights[i].position.z, 1)).xyz + EyeDirection;


This is what is causing all the trouble. And in particular, the uniform NormalMatrix. When I replaced NormalMatrix with ModelMatrix * ViewMatrix everything works fine. So it turns out something is wrong with the normal matrix. In my java code, the normal matrix is being uploaded like this:

mNormal.identity();
shader.setUniform("NormalMatrix", Camera.main.getMatrixView().normal(mNormal));


As for the Camera's getMatrixView method, here is what it looks like.

public Matrix4f getMatrixView() {
	mView.identity();
	mView.rotateX(MathHelper.toRadians(rotation.x));
	mView.rotateY(MathHelper.toRadians(rotation.y));
	mView.rotateZ(MathHelper.toRadians(rotation.z));
	mView.translate(position.x, position.y, position.z);
		
	return mView;
}


As for my thoughts, I think something goes wrong when the matrix is being uploaded to the shader. The method I'm using to set uniforms is working like it should, otherwise I would not have been able to see my model in the first place because the ModelViewProjection matrix is being uploaded the same way. So what is left is the JOML's Matrix4f's normal() method. Is it possible I'm using it the wrong way or could this be a bug?

Anyway, thanks for your help so far!
TBJ

EDIT

It turns out the uniform ViewMatrix has problems too. Because when I replace ModelMatrix * ViewMatrix with ViewMatrix that quad appears again. Since the normal matrix is generated by the view matrix I guess the origin has something to do with it. Although I do wonder why it makes such a difference. The code is not being executed but yet when I turn it into a comment that quad doesn't appear.

Kai

There was indeed a bug in JOML's Matrix4f.normal().
See this commit. In some cases the normal matrix was created incorrectly.
If you use Maven, then please switch to 1.6.6-SNAPSHOT. If not, then clone/pull the repository (again).

But I repeat myself: This cannot possibly be the "source" of your problem.
It simply CANNOT be. A wrong normal matrix cannot possibly result in some fragments being shown where there was no rasterized geometry. It CANNOT!
A wrong normal matrix can however result in wrong lighting calculations where some fragments (that are being produced by visible geometry) are being lit incorrectly.

But the "source" of your problem this is surely not. You just tend to always find "manifestations" of the problem when changing something in your shader. But I still suspect a driver bug.

TheBoneJarmer

Oh god I almost forgot. I tried my game at another computer. Not just one but an entire forest of triangles appeared. The computer I used (still) runs Windows 7. What both computers have in common is that they both use Intel graphic cards. Are you absolutely certain it is a driver bug? Because it never happened before. I have Unity3D installed and a few other 3D editors and they work like a charm. At least, I never experienced this before. Darn this is mindbreaking haha

I do not have another laptop in range with another graphic card unfortunately. If this is a driver bug, a better question would be how to fix it. I got no clue where it starts and why, and why changing the fragment shader makes a difference. And also, is there a connection between those uniforms and the bug. A hell lot of questions. I do know a way to test it out at another computer, but that is only possible later this day. That one has a NVIDEA graphic card.

Kai

Can you run some of the examples in the lwjgl3-demos repository?
Such as those in the fbo subpackage?
Do those work for you?