[OpenGL] HOW TO map-texture a subdivided icosahedron with VBOs?
in
Contributed Library Questions
•
11 months ago
Hello there,
I'm starting with opengl and, after playing with GLGraphics, I became interested on learning a bit more about opengl.
What I'm trying to do sounds simple, but I'm getting some trouble with textures.
I'm basically computing a subdivided icosahedron, which after 3 o 4 subdivisions looks like a sphere. Then, I'm drawing this model using VBOs. This part works OK... but when i try to get it textured, the results are terrible. I'm not sure if the problem is on how I'm calculating the tex coords or the opengl calls.
This is what i got so far. Any ideas?
- import processing.opengl.*; //OPENGL
- import javax.media.opengl.*; // USE OF OPENGL DIRECTLY
- import java.nio.FloatBuffer; //FLOATBUFFERS
- import java.nio.IntBuffer; //INTBUFFERS
- import com.sun.opengl.util.BufferUtil; //BUFFERS
- //import com.sun.opengl.util.texture.*; //TEXTURES
- import codeanticode.glgraphics.*;
- PGraphicsOpenGL pgl;
- GL gl;
- //icosahedron
- Ico ico;
- //just a var for rotation testing
- int rot = 0;
- //texture
- GLTexture tex;
- void setup() {
- size(800, 600, OPENGL);
- frameRate(60);
- pgl = (PGraphicsOpenGL) g; // g may change
- gl = pgl.gl;
- //buffers
- int[] bufferID = new int[3];
- gl.glGenBuffers(3, bufferID, 0);
- //new icosahedron (lvl of subdivisions <0= regular icosahedron>), verts buffer, indices buffer, tex buffer)
- ico = new Ico(2, bufferID[0], bufferID[1], bufferID[2]);
- //load an img
- tex = new GLTexture(this, "nebula.jpg");
- }
- void draw() {
- frame.setTitle(str(frameRate));
- background(100);
- //rotation
- rot++;
- if (rot == 360) {
- rot = 0;
- }
- //OpenGL calls
- pgl = (PGraphicsOpenGL) g; // g may change
- gl = pgl.gl;
- pgl.beginGL();
- setLghts(gl); //just some lights, nothing relevant
- gl.glPushMatrix();
- gl.glTranslatef(width/2, height/2, 0);
- gl.glRotatef(rot, 1, 1, 0);
- gl.glEnable(gl.GL_NORMALIZE);
- gl.glScalef(50, 50, 50);
- //VBO's
- //set current VBO (vertices)
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, ico.vertBufferID);
- //supply data to the current VBO --- glBufferData(int target, int size(in bytes), Buffer data, int usage)
- //size*4, since 1 float = 4 bytes.
- gl.glBufferData(GL.GL_ARRAY_BUFFER, ico.vertCont*4, ico.vertBuffer, GL.GL_STATIC_DRAW);
- gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
- gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
- //tex -----
- gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY);
- gl.glEnable(GL.GL_TEXTURE_2D);
- gl.glBindTexture(gl.GL_TEXTURE_2D, tex.getTextureID());
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, ico.texBufferID);
- gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);
- //---------
- //leave no buffer ID bound.
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
- //set current VBO (indices)
- gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ico.indBufferID);
- gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, ico.indCont*4, ico.indBuffer, GL.GL_STATIC_DRAW);
- //leave no buffer ID bound.
- gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
- gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
- gl.glEnableClientState(GL.GL_NORMAL_ARRAY);
- // Draw the triangles using the indices VBO
- gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ico.indBufferID);
- gl.glDrawElements(GL.GL_TRIANGLES, ico.indCont, GL.GL_UNSIGNED_INT, 0);
- //leave no buffer ID bound.
- gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
- gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
- gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
- gl.glDisableClientState(GL.GL_TEXTURE_COORD_ARRAY);
- gl.glPopMatrix();
- pgl.endGL();
- }
The Icosahedron class (I wrote it following the "red book" and different approaches I found around)
- class Ico{
- float[][] vertices; //vertices
- int[][] indices; //indices
- ArrayList<Float> vertAL; //AL vertices
- ArrayList<Integer> indAL; //AL indices
- int vertCont, indCont, lvl; //vertices/indices count, subdivide level
- int vertBufferID; //Buffer ID vertices
- int indBufferID; //Buffer ID indices
- int texBufferID; //Buffer ID texturas
- FloatBuffer vertBuffer; //Buffer vertices
- IntBuffer indBuffer; //Buffer indices
- FloatBuffer texBuffer; //Buffer texturas
- //CONSTRUCTOR
- //computes the icosahedron data (normals and vertices are the same)
- Ico(int lvl, int vertBufferID, int indBufferID, int texBufferID){
- this.lvl = lvl;
- this.vertBufferID = vertBufferID;
- this.indBufferID = indBufferID;
- this.texBufferID = texBufferID;
- vertAL = new ArrayList<Float>();
- indAL = new ArrayList<Integer>();
- float x = .525731112119133606;
- float z = .850650808352039932;
- //vertices
- vertices = new float[][] {
- {-x, 0.0, z}, {x, 0.0, z}, {-x, 0.0, -z}, {x, 0.0, -z},
- {0.0, z, x}, {0.0, z, -x}, {0.0, -z, x}, {0.0, -z, -x},
- {z, x, 0.0}, {-z, x, 0.0}, {z, -x, 0.0}, {-z, -x, 0.0}
- };
- //indices
- indices = new int[][] {
- {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1},
- {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3},
- {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6},
- {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11}
- };
- //add vertices to ArrayList
- for (float[] v : vertices) {
- vertAL.add(v[0]); vertAL.add(v[1]); vertAL.add(v[2]);
- }
- //subdivide each face as many times lvl
- for (int[] f: indices) {
- subdivide(f[0], f[1], f[2], vertAL, indAL, lvl);
- }
- //vertices and indices count
- vertCont = vertAL.size();
- indCont = indAL.size();
- //vertices Buffer
- vertBuffer = BufferUtil.newFloatBuffer(vertCont);
- for (float v : vertAL){ vertBuffer.put(v); }
- vertBuffer.rewind();
- //indices Buffer
- indBuffer = BufferUtil.newIntBuffer(indCont);
- for (int i : indAL){ indBuffer.put(i); }
- indBuffer.rewind();
- //texture coords Buffer
- texBuffer = BufferUtil.newFloatBuffer(vertCont*2); //nro. de caras * 3 vert * 2 uv cords
- for (int i=0; i<vertCont; i+=3){
- //get point
- float[]punto = new float[]{vertAL.get(i), vertAL.get(i+1), vertAL.get(i+2)};
- //calculate u and v
- float[] uv = calcTexCoord(punto);
- texBuffer.put(uv[0]);
- texBuffer.put(uv[1]);
- }
- texBuffer.rewind();
- }
- // Given a point P on a unit sphere, calculate texture coords [u, v]
- float[] calcTexCoord(float[] P){
- // using spherical coords
- // a is the angle from z axis [0, pi]
- // t is the angle from x-axis [0, 2pi]
- float a, t;
- a = acos(P[2]);
- float tmp = sin(a);
- // check for poles
- if(Math.abs(tmp) < 0.001) {
- t = 0.0;
- }
- else {
- float tmp2 = P[0]/tmp;
- // clamp to [-1, 1] - due to small numerical errors
- tmp2 = Math.min(Math.max(tmp2, -1.0), 1.0);
- t = acos(tmp2);
- if(P[1] < 0.0) {
- t = 2.0*PI - t;
- }
- }
- // convert to [0, 1]
- float v = 1.0 - a/PI;
- float u = t/(2.0*PI);
- return new float[]{u, v};
- }
- //faces subdivision
- void subdivide(int v1, int v2, int v3, ArrayList<Float> vertices, ArrayList<Integer> faces, int level) {
- if (level == 0) {
- faces.add(v1);
- faces.add(v2);
- faces.add(v3);
- }
- else {
- float a1 = (vertices.get(3*v1) + vertices.get(3*v2));
- float a2 = (vertices.get(3*v1+1) + vertices.get(3*v2+1));
- float a3 = (vertices.get(3*v1+2) + vertices.get(3*v2+2));
- float length = (float)Math.sqrt(a1*a1+a2*a2+a3*a3);
- a1 /= length;
- a2 /= length;
- a3 /= length;
- int indexA = vertices.size()/3;
- vertices.add(a1);
- vertices.add(a2);
- vertices.add(a3);
- float b1 = (vertices.get(3*v3) + vertices.get(3*v2));
- float b2 = (vertices.get(3*v3+1) + vertices.get(3*v2+1));
- float b3 = (vertices.get(3*v3+2) + vertices.get(3*v2+2));
- length = (float)Math.sqrt(b1*b1+b2*b2+b3*b3);
- b1 /= length;
- b2 /= length;
- b3 /= length;
- int indexB = vertices.size()/3;
- vertices.add(b1);
- vertices.add(b2);
- vertices.add(b3);
- float c1 = (vertices.get(3*v1) + vertices.get(3*v3));
- float c2 = (vertices.get(3*v1+1) + vertices.get(3*v3+1));
- float c3 = (vertices.get(3*v1+2) + vertices.get(3*v3+2));
- length = (float)Math.sqrt(c1*c1+c2*c2+c3*c3);
- c1 /= length;
- c2 /= length;
- c3 /= length;
- int indexC = vertices.size()/3;
- vertices.add(c1);
- vertices.add(c2);
- vertices.add(c3);
- subdivide(v1,indexA,indexC,vertices,faces,level-1);
- subdivide(indexA,v2,indexB,vertices,faces,level-1);
- subdivide(indexC,indexB,v3,vertices,faces,level-1);
- subdivide(indexA,indexB,indexC,vertices,faces,level-1);
- }
- }
- }
Without textures, the model displays perfect.
It'd be great to have some input from you guys, any correction/suggestion/tip/advice would be gladly welcome.
Thanks in advance!
1