one texture for many faces

Started by webster, September 28, 2015, 18:19:22

Previous topic - Next topic

webster

hello,

i try to render a icosphere in my program. For the first test, i use the idea from this webpagehttp://blog.coredumping.com/subdivision-of-icosahedrons/, and it works.
The problem is, the more i split my sphere, the size of the texture becomes very big. And the result looks not so good.
A picture with 2 splits of the icosahedron (icosphere01.png)
Now i try to use only one image with only one triangle as texture for every face. As the result the stairs are gone, but now there are other disadvantages (icosphere02.png).
But no wonder, my uv coordinates are all the same. My image is only one triangle with one side on the buttom of the picture and following coordinates
Vec2d[] textureCoordinates = new Vec2d[] { new Vec2d(.5, 1),
			new Vec2d(0, 0), new Vec2d(1, 0) };


For the rendering i use a vertex array object in the same way like thishttp://wiki.lwjgl.org/wiki/The_Quad_textured.

Can someone tell me how i calculate the correct uv coordinates for every face of the sphere.

many thanks
Roland

p. s. apologize my poor english and i don't know how i insert my pictures in the post.
finally
the idea behind all this, is a game (like pacman) where the pacman and the ghosts could only walk on the edges of the sphere and not all edges should be visible. In later versions my texture has some more "triangles" with only one or two visible edges.

Kai

It took me quite a while before I finally understood what you are trying to do. Very nice idea for a Pacman clone! :)
So, you essentially want to draw a wireframe model of a subdivided icosahedron-sphere, but as I see it you don't want all edges in that wireframe to be visible and your game mechanics then does not allow Pacman and the ghosts to travel along these lines. Right?
If so, then you basically need a way to render edges of your model as (thick), antialiased wireframe and other non-edge areas of your model in a solid color. There is a technique for this, which is described in - among others - this paper:
Two Methods for Antialiased Wireframe Drawing with Hidden Line Removal.
They basically compute barycentric coordinates for each fragment in the triangle mesh and then shade a point inside a triangle based on the distance to the closest edge of that triangle.
I remembered that paper because I implemented that some years ago. It's very easy. The code is even given as GLSL at the end of the paper.
Now, this would render "all" lines of your triangle mesh, which you don't want.
You want to "mark" certain lines as not being drawn.
This can be achieved easily with an additional per-vertex attribute. This then means you cannot use indexed rendering anymore, since a vertex may no longer belong to more than one edge/line.
So the general idea is, alongside the vertex coordinates of your mesh you store an additional vertex attribute ("visible") in a VBO and then use the geometry shader/fragment shader pair to evaluate whether to render that line. For example, the geometry shader could set the distance to an invisible line to (near) infinity so that your fragment shader will never shade that line. (see the paper about what I mean by that "distance" function).
In essence: I would not use texturing for this. Texturing is more complicated and also prone to aliasing. Also with textures your lines will become thinner the higher the triangulation factor of your sphere, provided you do not resize your texture accordingly. And with textures you also have to develop a method to render the visible lines on the texture first, also in a way that correctly UV-maps them to the sphere... lots of problems with textures this way. :)

webster

hello kai,

thanks for your fast reply, and yes you're right that's what I'm trying.
In the last few days i learning a little bit about the geometry shader.
Now the shaders works and they render a wireframe geosphere for me:-)

Like your suggestion i try to set the distance variable to (near) infinity
when the geometry shader becomes a "invisible vertex", but i do not really
understand the math behind this.
my vertex shader
// ================
// Vertex shader:
// ================
#version 330
layout (location = 0) in vec3 position;
layout (location = 1) in float flag;

uniform mat4 perspectiveMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

out float visible;

void main(void) {
    vec4 in_Position = vec4(position.x, position.y, position.z, 1.0f);
    gl_Position = perspectiveMatrix * viewMatrix * modelMatrix * in_Position;
    visible = flag;
}

my geometry shader
// ================
// Geometry shader:
// ================
#version 330
layout (triangles) in;
layout (triangle_strip) out;
layout (max_vertices = 3) out;

out noperspective vec3 dist;

uniform vec2 WIN_SCALE;

inout float visible;

void main(void) {
    
    	vec2 p0 = WIN_SCALE * gl_in[0].gl_Position.xy / gl_in[0].gl_Position.w;
	vec2 p1 = WIN_SCALE * gl_in[1].gl_Position.xy / gl_in[1].gl_Position.w;
	vec2 p2 = WIN_SCALE * gl_in[2].gl_Position.xy / gl_in[2].gl_Position.w;
	
	vec2 v0 = p2-p1;
	vec2 v1 = p2-p0;
	vec2 v2 = p1-p0;
	float fArea = abs(v1.x*v2.y - v1.y * v2.x);
	
	if(visible < 0) {
		dist = vec3(0,0,0);
	} else {
		dist = vec3(fArea/length(v0),0,0);
	}
	gl_Position = gl_in[0].gl_Position;
	EmitVertex();
	
	if(visible < 0) {
		dist = vec3(0,0,0);
	} else {
		dist = vec3(0,fArea/length(v1),0);
	}
	gl_Position = gl_in[1].gl_Position;
	EmitVertex();
	
	if(visible < 0) {
		dist = vec3(0,0,0);
	} else {
		dist = vec3(0,0,fArea/length(v2));
	}
	gl_Position = gl_in[2].gl_Position;
	EmitVertex();
	
	EndPrimitive();
}

my fragment shader
// ================
// Fragment shader:
// ================
#version 330

in noperspective vec3 dist;

const vec4 WIRE_COL = vec4(1.0f, 0.0f, 0.0f, 1.0f);
const vec4 FILL_COL = vec4(.0f, .0f, .0f, .0f);

void main(void) {
	float fNearest =min(min(dist[0], dist[1]), dist[2]);
	float I = exp2(-0.005 * fNearest * fNearest);
	gl_FragColor = mix(FILL_COL, WIRE_COL, I);
}


I am also not shure with my additional vertex attribute.
This is my first opengl project without pure "copy and paste coding".
First i think about a boolean or a byte type, but this is probably not possible in one
VBO object with float vertices.

private void initGL() {

		GeosphereModel model = new GeosphereModel(1);
		float[] vertices = model.getSphereTriangles();
		// 3 vertices with each 3 float for x, y, z
		faceCount = vertices.length / 9;
		int flags = (vertices.length * Float.SIZE / Byte.SIZE);
		float[] flagedVertices = new float[vertices.length * 4 / 3];
		System.arraycopy(vertices, 0, flagedVertices, 0, vertices.length);
		for (int i = vertices.length - 1; i < flagedVertices.length; i++) {
			if (i % 5 == 0)
				flagedVertices[i] = -1f;
			else
				flagedVertices[i] = 1f;
		}

		int vbo = glGenBuffers();
		vao = glGenVertexArrays();
		glBindVertexArray(vao);
		FloatBuffer vertexBuffer = BufferUtils
				.createFloatBuffer(flagedVertices.length);
		vertexBuffer.put(flagedVertices);
		vertexBuffer.flip();

		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);

		glEnableVertexAttribArray(0);
		glEnableVertexAttribArray(1);
		glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
		glVertexAttribPointer(1, 1, GL_FLOAT, false, 0, flags);
		glBindVertexArray(vao);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}


After my vbo modification, there is one vertex which is not there where it should be.
I think there is something wrong with my flags variable ???
There are more things which are very strange for me. For example if i change the declaration of the  variable visible in the geometry shader to "in float visible;" the shader don't want compile.

I hope you can help me once more.

Kai

Okay, I did not know that this is your first OpenGL project.
Then it may be shooting a bit too far.
But just one thing with your "visible" variable and how geometry shaders work:
A geometry shader is invoked for a set of all the three vertices that make up a triangle.
There, you have access to all vertices of that triangle, which is the reason why the geometry shader can compute the distances to the edges of that triangle.
Your "visible" variable is also per vertex, so you need to specify a different vertex attribute layout in your geometry shader, since it works both on the vertex "position" as well as on the "visible" flag.

Regarding the interpretation of that "visible" flag: Since that flag should be per edge but is in fact per vertex, we can use the "first" vertex of a triangle to mean the edge which would lead to the second vertex (in winding order). I admit that this is advanced. Sorry for proposing it.

So, your vertex attribute layout in the geometry shader would be:

in myVertex { // <- this comes from the vertex shader
  vec4 gl_Position;
  bool visible;
} vertices[];


Your vertex shader (situated before the geometry shader) would then declare the following interface:

in int visible; // <- this comes from vertex stream
in vec3 position; // <- this also comes from your vertex stream

out myVertex { // <- this goes to the geometry shader
  vec4 gl_Position;
  bool visible;
} vertex;


Logically, the 'in int visible' should have type 'bool', but that is not supported as an vertex attribute. So we just use 0 for false and 1 for true. You can also use float with 0.0 and 1.0.

QuoteFirst i think about a boolean or a byte type, but this is probably not possible in one
VBO object with float vertices.
You can pack any information in a VBO in an interleaved way, or use different VBOs for different vertex attributes.
You can in theory use the following interleaved format: XYZV|XYZV|XYZV|XYZV|...
where XYZ is the vertex position as three floats and V is a single 32-bit integer being either 0 or 1.
You just have to declare the vertex attributes correctly with glVertexAttribPointer.

webster

servus kai :)

I have no idea how I get different types in the vbo. So far I have always done it in that way.
FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertices.length);
		vertexBuffer.put(vertices);
		vertexBuffer.flip();

		glBindBuffer(GL_ARRAY_BUFFER, vbo);
		glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);

Then i declare the vertex attributes (interleaved)
glVertexAttribPointer(0, 3, GL_FLOAT, false, 16, 0);
glVertexAttribPointer(1, 1, GL_FLOAT, false, 16, 12);

I thought the first parameter is what in the vertex shader the location is, for example
layout (location = 0) in vec3 position;
layout (location = 1) in float flag;

but your code snippet without the location work also very well, very strange.
Another question for me, is the gl_Position which you use the buildin variable, or point it to a new
vec4.
in int visible; // <- this comes from vertex stream
in vec3 position; // <- this also comes from your vertex stream
out myVertex { // <- this goes to the geometry shader
  vec4 gl_Position;
  bool visible;
} vertex;

Unfortunately, my shaders does not work and it is difficult to find the error, no way to see into the shader stages.
Glsl looks like c, but i have read that a if-question do not accept an integer. In my shader i write this.
if(visible == 0.0f)
   vertex.visible = false;
else
   vertex.visible = true;

i hope this is correct.

In the stage from geometry shader to the fragment shader, must i declare another interface for the distance varaible with an array as output, like the interface from vertex to geometry shader.
I use the distance varibiale in that way at least i habe no compiling or linking error.
float fNearest =min(min(distance[0], distance[1]), distance[2]);

Sorry for my ignorance.

Only one good news, that a vertex is not a point (visible or not) does not matter. I have in my model class a graph and knows every five triangle which a point target. The graph is for the (future) path finding algorithms (perhaps ::)).

Kai

Quote
I have no idea how I get different types in the vbo.
You have to create a ByteBuffer with BufferUtils.createByteBuffer.
Then you create a FloatBuffer "view" on it as well as an IntBuffer view, via ByteBuffer.asFloatBuffer and .asIntBuffer(), respectively.
Then you can write floats and ints to your ByteBuffer.
Alternatively, without using buffer views, you can also just use putInt() and putFloat() on your ByteBuffer.

Quote
Then i declare the vertex attributes (interleaved)
Yes. But instead of GL_FLOAT you use GL_UNSIGNED_INT or GL_SIGNED_INT.
And instead of glVertexAttribPointer you use glVertexAttribIPointer, to tell the shader that you really want to use integer values. With glVertexAttribPointer the vertex attribute values will always be converted to floating point types before reaching the shader.

Quote
i hope this is correct.
Better would be
if (visible == 0)
  vertex.visible = false;
else
  vertex.visible = true;

or even better:
vertex.visible = visible == 1;


Quote
Sorry for my ignorance.
No need to feel sorry. You are not ignorant at all. You are just curious and ask questions. That's good!
However, I myself feel a bit more sorry for proposing this solution. That whole topic can very quickly explode by having to explain a lot of things. :)

Quote
Only one good news, that a vertex is not a point (visible or not) does not matter. I have in my model class a graph and knows every five triangle which a point target.
What? I don't understand what you are trying to say. My head throws all sorts of BadEnglishGrammarExceptions. Sorry! :D
A vertex is a point in our case. It's a point with more attributes to it than just its position.

webster

hi kai,

Quotethrows all sorts of BadEnglishGrammarExceptions
that sounds really funny, i hope you don't need a reboot ;). But you must told this google, because of my poor English writing skills.

Because of the shaders I'm really desperate soon. They don't want compile. This is what i try:
// ================
// Vertex shader:
// ================
#version 330
in vec3 position;
in int flag;
uniform mat4 perspectiveMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
out FlagedVertex {
	vec4 gl_Position;
	bool Visible;
} vertex;
void main() {
    vec4 in_Position = vec4(position.x, position.y, position.z, 1.0f);
    vertex.gl_Position = perspectiveMatrix * viewMatrix * modelMatrix * in_Position;
    gl_Position = vertex.gl_Position;
    vertex.Visible = flag == 1;
}
// ================
// Geometry shader:
// ================
#version 330
layout (triangles) in;
layout (triangle_strip) out;
layout (max_vertices = 3) out;
out Distance {
	noperspective vec3 distance;
} distOut;
uniform vec2 WIN_SCALE;
in FlagedVertex {
	vec4 gl_Position;
	bool Visible;
} vertices[];
void main() {
    	vec2 p0 = WIN_SCALE * vertices[0].gl_Position.xy / vertices[0].gl_Position.w;
	vec2 p1 = WIN_SCALE * vertices[1].gl_Position.xy / vertices[1].gl_Position.w;
	vec2 p2 = WIN_SCALE * vertices[2].gl_Position.xy / vertices[2].gl_Position.w;
	vec2 v0 = p2-p1;
	vec2 v1 = p2-p0;
	vec2 v2 = p1-p0;
	float fArea = abs(v1.x * v2.y - v1.y * v2.x);	
	distOut.distance = vertices[0].Visible ? vec3(fArea/length(v0), .0f, .0f) : vec3(.0f, .0f, .0f);
	gl_Position = vertices[0].gl_Position;
	EmitVertex();
	distOut.distance = vertices[1].Visible ?  vec3(.0f, fArea/length(v1), .0f) : vec3(.0f, .0f, .0f);
	gl_Position = vertices[1].gl_Position;
	EmitVertex();
	distOut.distance = vertices[2].Visible ?  vec3(.0f, .0f, fArea/length(v2)) : vec3(.0f, .0f, .0f);
	gl_Position = vertices[2].gl_Position;
	EmitVertex();
	EndPrimitive();
}
// ================
// Fragment shader:
// ================
#version 330
const vec4 WIRE_COL = vec4(0.0f, 0.9f, 0.7f, 1.0f);
const vec4 FILL_COL = vec4(.0f, .0f, .0f, .0f);
in Distance {
	noperspective vec3 distance;
} distIn;
void main() {
	float nearest =min(min(distIn.distance[0], distIn.distance[1]), distIn.distance[2]);
	float intensity = exp2(-0.05f * nearest * nearest);
	gl_FragColor = mix(FILL_COL, WIRE_COL, intensity);
}

Especially this line confuses me (in vertex and geometry shader):
gl_Position = vertex.gl_Position; // vertex shader

gl_Position = vertices[0].gl_Position; // geometry shader

I use this documentation http://wiki.delphigl.com/index.php/Tutorial_glsl#Objekte (which is easier for Germans). There I read that gl_Position in the vertex shader has to be written.
And what goes wrong when the linking stage comes off.
For the transfer of the distance, I have chosen this variant https://gist.github.com/paulhoux/9583212. No idea whether that was correct or how it works easier.

Not that it bothers me , but I think you should change the headline of this thread, which has nothing to do with textured faces (This sentence was one hundred percent by google ;)).

best regards roland

Kai

Quote from: webster on October 02, 2015, 13:50:43
For the transfer of the distance, I have chosen this variant https://gist.github.com/paulhoux/9583212. No idea whether that was correct or how it works easier.
This is actually a great resource. Thanks for linking! It just had a minor error (a missing semicolon).
Because I was curious how to do a simple demonstration of all of this, there is now this demo in the lwjgl3-demos repository: https://github.com/LWJGL/lwjgl3-demos/blob/master/src/org/lwjgl/demo/opengl/geometry/GeometryShaderTest.java
Corresponding shaders are here: https://github.com/LWJGL/lwjgl3-demos/tree/master/res/org/lwjgl/demo/opengl/geometry
It shows a spinning cube with that wireframe rendering and certain edges invisible. I also modified the distance function a bit to have thicker lines.
And this demo also uses two separate VBOs for the vertex positions and the visibility info. It's just easier to setup the buffers in Java this way, though using interleaved buffers is just as possible.

Have fun with it! :)

webster

opps there is a new visible flag in the vertex shader. :)
[LWJGL] OpenGL debug message
	ID: 0x0
	Source: SHADER COMPILER
	Type: ERROR
	Severity: HIGH
	Message: GLSL compile failed for shader 2, "": ERROR: 1:10: 'varying' : cannot be bool 
ERROR: 1:14: 'vertex' : undeclared identifier 
ERROR: 1:14: 'visible' :  field selection requires structure, vector, or matrix on left hand side 
ERROR: 1:14: 'assign' :  cannot convert from 'bool' to 'float'

Are you still working on the GeometryShaderTest?

Kai

It's again baffling how drivers behave differently. :)
Can you try it again?
Now it just uses floats for a logical boolean, with 0.0 and 1.0 as false and true.

webster

what should i say, very nice i'm really happy

twelve points from germany ;)

Kai

Deutschland darf sich selber keine Punkte vergeben. :D

- I just translated that sentence to English, and now I can see what you mean. Google Translate really produces nonesense most of the time.

webster

QuoteDeutschland darf sich selber keine Punkte vergeben
>:(
QuoteI just translated that sentence to English, and now I can see what you mean. Google Translate really produces nonesense most of the time
;D
:D