Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OpenGL ES 1и2.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
4.8 Mб
Скачать

1.6 OpenGl es 1. Текстурирование на примере

В данном примере показано как создать текстурированный многогранник в Android OpenGL ES на примере пирамид. Применен объектно-ориентированный подход. Пирамида разбита на отдельные самостоятельные объекты (полигоны) - треугольники и прямоугольник, внутри которых содержатся собственные свойства полигонов - координаты вершин и текстур, а также определен метод позволяющий вращать полигоны в пространстве.

Используемые классы:

Texture - класс текстуры. При создании объекта данного класса в память загружется графический файл, соответствующий данной текстуре, а также присваивается уникальное имя для обращения к текстуре. Таким образом, текстура однозначно связывается с картинкой.

 Triangle - класс треугольника. Знает координаты своих вершин и координаты текстур, присвоенных вершинам. При помощи метода rotate можно поворачивать треугольник вокруг любой оси. При этом собственные координаты вершин будут меняться. К треугольнику можно привязать текстуру методом setTextureName, а также отвязать её если сделать имя текстуры равной нулю. Для правильной сортировки полигонов при рисовании полупрозрачной пирамиды  в треугольник встроены координаты центра, которые также изменяются  при вращении треугольника. Содержит метод draw, рисующий треугольник на экране.

 Quadr - прямоугольник для дна пирамиды. Описывается аналогично треугольнику. При создании прямоугольника задаются координаты трёх вершин, а четвертая рассчитывается автоматически как сумма двух векторов образованных тремя вершинами, т.к. все четыре вершины должны лежать в одной плоскости.

 Pyramid - пирамида, собранная из четырех треугольников и одного прямоугольника. Все пять полигонов жестко связаны по координатам вершин. Определены методы вращающие пирамиду относительно осей, соединяющих её центр и вершины. Каждая грань пирамиды может быть залита отдельной текстурой, а также текстура может быть общей для всех граней пирамиды. Для этого перегружен метод setTextureName.

------------------------------------------------------------------------------------------------------------ Перейдем от теории к практике. Создадим класс Texture, описывающий двумерную текстуру ------------------------------------------------------------------------------------------------------------ package com.blogspot.andmonahov.pyramid; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; public class Texture{             private int name; // поле для хранения имени текстуры             // конструктор                public Texture(GL10 gl, Context context, int idpicture){                         int[] names=new int[1];                         // генерирует массив свободных имен текстур, состоящий из одного элемента                         gl.glGenTextures(1, names, 0);                         // запомним имя текстуры                         name=names[0];                         // имя текстуры - это ее уникальный номер, по которому можно к ней обращаться                         // имя текстуры будем хранить в поле name для получения имени из других классов                          //позже напишем метод getName                         // установим выравнивание                         gl.glPixelStorei(GL10.GL_UNPACK_ALIGNMENT,1);

                        // команда glBindTexture устанавливает текстуру с номером name                         // в качестве текущей активной текстуры                         gl.glBindTexture(GL10.GL_TEXTURE_2D, name);                         // в дальнейшем все настройки действуют  на текущую активную текстуру                         // устанавливаем параметры текстуры                         // включаем автоматическую генерацию мип-мапов всех уровней                         gl.glTexParameterx(GL11.GL_TEXTURE_2D,                                     GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);                         // устанавливаем фильтры                         gl.glTexParameterf(GL10.GL_TEXTURE_2D,                                      GL10.GL_TEXTURE_MIN_FILTER,                                       GL10.GL_LINEAR_MIPMAP_LINEAR);                         gl.glTexParameterf(GL10.GL_TEXTURE_2D,                                     GL10.GL_TEXTURE_MAG_FILTER,                                     GL10.GL_LINEAR);                         // если текстура не полностью закрывает рисуемый полигон                          // устанавливаем режим повтора рисунка текстуры                         gl.glTexParameterx(GL10.GL_TEXTURE_2D,                                     GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);                         gl.glTexParameterx(GL10.GL_TEXTURE_2D,                                     GL10.GL_TEXTURE_WRAP_T,                                     GL10.GL_REPEAT);                         //загружаем картинку с идентификаторм idpicture                         // idpicture - это уникальный номер картинки в R.java                         Bitmap bitmap = BitmapFactory.decodeResource                                                 (context.getResources(), idpicture);                         GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);                         // удаляем картину из памяти                         bitmap.recycle();                         // т.о. текущая текстура с именем name связана с картинкой idpicture             }

             public int getName(){                         return name;              } }

------------------------------------------------------------------------------------------------------------ 

В нашем примере будем рисовать текстурированную пирамиду, поэтому нам понадобятся два класса треугольник для боковых граней пирамиды и прямоугольник для основания. Создадим класс Triangle, описывающий треугольник:

------------------------------------------------------------------------------------------------------------ 

package com.blogspot.andmonahov.pyramid;

import java.nio.*;

import javax.microedition.khronos.opengles.GL10;

import android.opengl.Matrix; public class Triangle {             // определение полей класса   координаты первой точки треугольника

            private float x1,y1,z1;

            // координаты второй точки треугольника

            private float x2,y2,z2;

            // координаты третьей точки треугольника

            private float x3,y3,z3;             // координаты вектора нормали т.к. три точки треугольника всегда лежат

            // в одной плоскости достаточно задать координаты одного вектора нормали для всех трех точек             private float nx,ny,nz;             // координаты центра треугольника они будут нам нужны в дальнейшем

//для сортировки треугольников по расстоянию до камеры             private float xcenter,ycenter,zcenter;              // OpenGl не принимает координаты в виде чисел float             // Внешние данные должны быть переданы в OpenGL в байтовых буферах             // поэтому определим три буфера буфер координат вершин              private FloatBuffer vertexBuffer;             // буфер вектора нормали             private FloatBuffer normalBuffer;             // буфер координат текстур             private FloatBuffer texcoordBuffer;              // треугольник может быть заполнен какой либо текстурой             // поэтому создадим поле для хранения имени текстуры             //внутри класса треугольника             private int textureName;

// конструктор

public Triangle(float x1, float y1, float z1, float x2,              float y2, float z2, float x3, float y3, float z3){             // треугольник однозначно описывается тремя точками поэтому в момент его создания в конструктор должны             //быть переданы координаты точек порядок точек 1,2,3 должен соответствовать обходу треугольника 

            // против часовой стрелки присваиваем координаты точкам

            this.x1=x1;

            this.y1=y1;

            this.z1=z1;

            this.x2=x2;

            this.y2=y2;

            this.z2=z2;

            this.x3=x3;

            this.y3=y3;

            this.z3=z3;

            // подготавливаем буфер вершин каждая вершина треугольника содержит три коодинаты x,y,z типа float

            // каждая координата типа float занимает 4 байта, итого 12 байт на вершину три вершины используют 3*12=36 байт 

            ByteBuffer b1 = ByteBuffer.allocateDirect(36);

            b1.order(ByteOrder.nativeOrder());

            vertexBuffer = b1.asFloatBuffer();

            // подготавливаем буфер нормалей

            ByteBuffer b2 = ByteBuffer.allocateDirect(36);

            b2.order(ByteOrder.nativeOrder());             normalBuffer = b2.asFloatBuffer();

            // пересчет нормалей и центра, заполнение буферов координат и нормалей

            recount(); //этот метод описан ниже

            // подготавливаем буфер координат текстур каждой вершине треугольника присвоены две координаты текстур s и t

            // каждая координата занимает 4 байта как число типа float итого 8 байт на вершину, 24 байта не треугольник

            ByteBuffer b4 = ByteBuffer.allocateDirect(24);

            b4.order(ByteOrder.nativeOrder());

            texcoordBuffer = b4.asFloatBuffer();

            // заполняем буфер текстур выполняем обход вершин против часовой стрелки

            // текстурные координаты верхней точки

            texcoordBuffer.put(0.5f); // s 

            texcoordBuffer.put(1); // t

            // текстурные коодинаты левой нижней точки

            texcoordBuffer.put(0); // s

            texcoordBuffer.put(0); // t

            // текстурные координаты правой нижней точки

            texcoordBuffer.put(1); // s

            texcoordBuffer.put(0); // t

            // установим текущую позицию буфера на его начало

            texcoordBuffer.position(0);

            // при создании треугольника ему еще не присвоена текстура поэтому ставим

            textureName = 0;

            // присвоить имя текстуры можно через метод setTextureName

}   // описание методов класса при изменении координат вершин треугольника

// изменяются координаты вектора нормали и координаты центра

// поэтому создадим метод который будет их пересчитывать и заполнять буферы новыми координатами

private void recount(){  

            //заполняем буфер вершин значением координат точек

            vertexBuffer.position(0);

            // для вершины 1

            vertexBuffer.put(x1); 

            vertexBuffer.put(y1);

            vertexBuffer.put(z1); 

            // для вершины 2

            vertexBuffer.put(x2);

            vertexBuffer.put(y2);

            vertexBuffer.put(z2);

            // для вершины 3

            vertexBuffer.put(x3);

            vertexBuffer.put(y3);

            vertexBuffer.put(z3);

            vertexBuffer.position(0);

            // нормаль может быть получена путем векторного произведения двух векторов - из точки 1 в точку 2 и из точки 2 в точку 3 

            float dx1=x2-x1;

            float dy1=y2-y1;

            float dz1=z2-z1;

            float dx2=x3-x2;

            float dy2=y3-y2;

            float dz2=z3-z2;

            nx=dy1*dz2-dy2*dz1;

            ny=dx2*dz1-dx1*dz2;

            nz=dx1*dy2-dx2*dy1;

            // приведение вектора нормали к единичной длине выполнять не будем это выполнит OpenGL

              normalBuffer.position(0);

            // заполняем буфер нормалей, нормаль одинакова для всех трех вершин для вершин 1,2,3 одно и тоже три раза

            for (int i=1;i<4;i++){

                        normalBuffer.put(nx);

                        normalBuffer.put(ny);

                        normalBuffer.put(nz);

            }

            normalBuffer.position(0);

            // вычисляем центр треугольника

            xcenter=(x1+x2+x3)/3;

            ycenter=(y1+y2+y3)/3;

            zcenter=(z1+z2+z3)/3;

}

// метод, устанавливающий для использования имя текстуры 

public void setTextureName(int name){

            textureName=name;  

}

// нам могут понадобиться координаты вершин и центра треугольника

// поэтому опишем методы, возвращающие их

public float getx1(){ return x1;}

public float gety1(){ return y1;}

public float getz1(){ return z1;}

public float getx2(){ return x2;}

public float gety2(){ return y2;}

public float getz2(){ return z2;}

public float getx3(){ return x3;}

public float gety3(){ return y3;}

public float getz3(){ return z3;}

public float getxcenter(){ return xcenter;}

public float getycenter(){ return ycenter;}

public float getzcenter(){ return zcenter;}

// создадим метод, который поворачивает треугольник 

// относительно вектора заданного двумя точками xa,ya,za и xb,yb,zb на угол angle против часовой стрелки

public void rotate(float angle,

            float xa, float ya, float za,

            float xb, float yb, float zb){

            // создаем матрицу вращения

            float [] rotatematrix=new float[16];

            // передаем в метод setRotateM угол и три координаты вектора, образованного двумя точками a-b

            Matrix.setRotateM(rotatematrix, 0, angle, xb-xa, yb-ya, zb-za);

            // в результате получаем заполненную матрицу вращения rotatematrix

            // для нахождения новых координат точек после поворота будем использовать метод multiplyMV 

            // умножения матрицы на вектор размером в 4 элемента зададим вектор, соединяющий точку a и вершину 1 треугольника 

            float [] oldvector1={x1-xa,y1-ya,z1-za,1};

            // получаем новый вектор после поворота

            float [] newvector=new float [4];

            Matrix.multiplyMV(newvector,0,rotatematrix,0,oldvector1,0);

            // добавляем к полученному вектору координаты точки a в результате получаем новые координаты точки 1

            x1=newvector[0]+xa;

            y1=newvector[1]+ya;

            z1=newvector[2]+za;

            // аналогично получаем коодинаты точек 2 и 3 после поворота поворачиваем точку 2

            float [] oldvector2={x2-xa,y2-ya,z2-za,1}; 

            Matrix.multiplyMV(newvector, 0, rotatematrix, 0, oldvector2, 0);

            x2=newvector[0]+xa;

            y2=newvector[1]+ya;

            z2=newvector[2]+za;

            // поворачиваем точку 3

            float [] oldvector3={x3-xa,y3-ya,z3-za,1};

            Matrix.multiplyMV(newvector, 0, rotatematrix, 0, oldvector3, 0);

            x3=newvector[0]+xa;

            y3=newvector[1]+ya;

            z3=newvector[2]+za;

            // пересчитывам нормали, центр и заполняем буферы

            recount();

}

// метод, рисующий треугольник на экране

public void draw (GL10 gl){

  vertexBuffer.position(0);

  normalBuffer.position(0);

  texcoordBuffer.position(0);

  // включаем использование массивов вершин

  gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 

  // указываем, что буфер с именем vertexBuffer является буфером вершин

  gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);

  // включаем использование массивов нормалей

  gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); 

  // указываем, что буфер с именем normalBuffer является буфером нормалей

  gl.glNormalPointer(GL10.GL_FLOAT,0,normalBuffer);

  if (textureName!=0){

     // если имя текстуры не пустое включаем использование двумерных текстур

     gl.glEnable(GL10.GL_TEXTURE_2D);

     // устанавливаем активной текстуру с именем textureName

     gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);

     // для учета освещения при использовании текстур включаем режим модуляции

     // если вместо GL10.GL_MODULATE поставить GL10.GL_REPLACE эффект освещения исчезнет 

     gl.glTexEnvx(GL10.GL_TEXTURE_ENV,         GL10.GL_TEXTURE_ENV_MODE,         GL10.GL_MODULATE);

     // включаем использование массивов текстур 

      gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

     // указываем программе, что буфер с именем texcoordBuffer является буфером текстур

     // первый параметр - это количество координат (две- s и t) 

     gl.glTexCoordPointer(2,GL10.GL_FLOAT,0,texcoordBuffer);   }else{

     // если имя текстуры пустое отключаем использование двумерных текстур

     gl.glDisable(GL10.GL_TEXTURE_2D);

  }

  // рисуем треугольник, последний параметр - это количество точек треугольника (т.е. три)

  gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);

} } // конец класса

------------------------------------------------------------------------------------------------------------ 

Таким образом мы создали класс, описывающий треугольник в трехмерном пространстве, научили его поворачиваться на произвольный угол относительно любой оси и хранить внутри себя ссылку на имя текстуры. Аналогично создается класс прямоугольника Quadr.

------------------------------------------------------------------------------------------------------------ package com.blogspot.andmonahov.pyramid; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; import android.opengl.Matrix;

public class Quadr { private float x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4; private float nx,ny,nz; private float xcenter,ycenter,zcenter; private FloatBuffer vertexBuffer,normalBuffer,texcoordBuffer; private int textureName; public Quadr(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3){   // присваиваем координаты трем точкам, четвертую найдем т.к. все четыре точки должны лежать в одной плоскости   this.x1=x1;   this.y1=y1;   this.z1=z1;   this.x2=x2;   this.y2=y2;   this.z2=z2;   this.x3=x3;   this.y3=y3;   this.z3=z3;   // подготавливаем буфер вершин   ByteBuffer b1 = ByteBuffer.allocateDirect(4*12);   b1.order(ByteOrder.nativeOrder());   vertexBuffer = b1.asFloatBuffer();   // подготавливаем буфер нормалей   ByteBuffer b2 = ByteBuffer.allocateDirect(4*12);   b2.order(ByteOrder.nativeOrder());   normalBuffer = b2.asFloatBuffer();   recount(); // пересчет нормалей и центра, поиск 4-ой точки подготавливаем и заполняем буфер координат текстур   ByteBuffer b4 = ByteBuffer.allocateDirect(4*8);   b4.order(ByteOrder.nativeOrder());   texcoordBuffer = b4.asFloatBuffer();   texcoordBuffer.put(0);    texcoordBuffer.put(1);   texcoordBuffer.put(0);   texcoordBuffer.put(0);   texcoordBuffer.put(1);   texcoordBuffer.put(0);   texcoordBuffer.put(1);   texcoordBuffer.put(1);   texcoordBuffer.position(0); }  // метод перезаписывает буфер вершин и буфер нормалей,  вычисляет центр прямоугольника private void recount(){   // ищем четвертую точку   x4=x1-x2+x3;   y4=y1-y2+y3;   z4=z1-z2+z3;   // заполняем буфер вершин значением координат точек   vertexBuffer.position(0);   // для вершины 1   vertexBuffer.put(x1);    vertexBuffer.put(y1);   vertexBuffer.put(z1);    // для вершины 2   vertexBuffer.put(x2);   vertexBuffer.put(y2);   vertexBuffer.put(z2);   // для вершины 3   vertexBuffer.put(x3);   vertexBuffer.put(y3);   vertexBuffer.put(z3);   // для вершины 4   vertexBuffer.put(x4);   vertexBuffer.put(y4);   vertexBuffer.put(z4);   vertexBuffer.position(0);   // вычисляем нормаль   float dx1=x2-x1;   float dy1=y2-y1;   float dz1=z2-z1;   float dx2=x3-x2;   float dy2=y3-y2;   float dz2=z3-z2;   nx=dy1*dz2-dy2*dz1;   ny=dx2*dz1-dx1*dz2;   nz=dx1*dy2-dx2*dy1;   /заполняем буфер нормалей, нормаль одинакова для всех четырех вершин   normalBuffer.position(0);   // для вершин 1,2,3,4 одно и тоже 4 раза   for (int i=1;i<5;i++){      normalBuffer.put(nx);      normalBuffer.put(ny);   normalBuffer.put(nz);   }   normalBuffer.position(0);   // вычисляем центр   xcenter=(x1+x2+x3+x4)/4;   ycenter=(y1+y2+y3+y4)/4;   zcenter=(z1+z2+z3+z4)/4; } // методы возвращающие координаты вершин и центра public float getx1(){ return x1;} public float gety1(){ return y1;} public float getz1(){ return z1;} public float getx2(){ return x2;} public float gety2(){ return y2;} public float getz2(){ return z2;} public float getx3(){ return x3;} public float gety3(){ return y3;} public float getz3(){ return z3;} public float getx4(){ return x4;} public float gety4(){ return y4;} public float getz4(){ return z4;} public float getxcenter(){ return xcenter;} public float getycenter(){ return ycenter;} public float getzcenter(){ return zcenter;}  // метод, поворачивающий прямоугольник public void rotate(float angle,   float xa, float ya, float za,   float xb, float yb, float zb){   // создаем матрицу вращения   float [] rotatematrix=new float[16];   // передаем в метод setRotateM угол и три координаты вектора, образованного двумя точками a-b   Matrix.setRotateM(rotatematrix, 0, angle, xb-xa, yb-ya, zb-za);   // в результате получаем заполненную матрицу вращения rotatematrix для нахождения новых координат точек после поворота    // будем использовать метод multiplyMV  умножения матрицы на вектор размером в 4 элемента   // зададим вектор, соединяющий точку a и вершину 1     float [] oldvector1={x1-xa,y1-ya,z1-za,1};   // получаем новый вектор после поворота   float [] newvector=new float [4];   Matrix.multiplyMV(newvector,0,rotatematrix,0,oldvector1,0);   // добавляем к полученному вектору координаты точки a в результате получаем новые координаты точки 1   x1=newvector[0]+xa;   y1=newvector[1]+ya;   z1=newvector[2]+za;   // аналогично получаем коодинаты точек 2 и 3 после поворота поворачиваем точку 2   float [] oldvector2={x2-xa,y2-ya,z2-za,1};    Matrix.multiplyMV(newvector, 0, rotatematrix, 0, oldvector2, 0);   x2=newvector[0]+xa;   y2=newvector[1]+ya;   z2=newvector[2]+za;   // поворачиваем точку 3   float [] oldvector3={x3-xa,y3-ya,z3-za,1};   Matrix.multiplyMV(newvector, 0, rotatematrix, 0, oldvector3, 0);   x3=newvector[0]+xa;   y3=newvector[1]+ya;   z3=newvector[2]+za;   // новая точка 4 ищется методом recount пересчитываем нормали, центр и заполняем буферы   recount(); } // метод, устанавливающий для использования имя текстуры  public void setTextureName(int name){   textureName=name;   } //метод, рисующий прямоугольник public void draw (GL10 gl){   vertexBuffer.position(0);   normalBuffer.position(0);   texcoordBuffer.position(0);   // включаем использование массивов вершин   gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);    // указываем программе, что буфер с именем vertexBuffer является буфером вершин   gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexBuffer);   // включаем использование массивов нормалей   gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);    // указываем программе, что буфер с именем normalBuffer является буфером нормалей   gl.glNormalPointer(GL10.GL_FLOAT,0,normalBuffer);   if (textureName!=0){      gl.glEnable(GL10.GL_TEXTURE_2D);      // устанавливаем активной текстуру с именем textureName      gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);        // для учета освещения при использовании текстур включаем режим модуляции       gl.glTexEnvx(GL10.GL_TEXTURE_ENV,         GL10.GL_TEXTURE_ENV_MODE,         GL10.GL_MODULATE);      // включаем использование массивов текстур       gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);      // указываем программе, что буфер с именем texcoordBuffer является буфером текстур      gl.glTexCoordPointer(2,GL10.GL_FLOAT,0,texcoordBuffer);   } else{      gl.glDisable(GL10.GL_TEXTURE_2D);   }   //рисуем прямоугольник   gl.glDrawArrays(GL10.GL_TRIANGLE_FAN,0,4); } } // конец класса

------------------------------------------------------------------------------------------------------------ Соберем из треугольников и прямоугольника пирамиду ------------------------------------------------------------------------------------------------------------ package com.blogspot.andmonahov.pyramid; import javax.microedition.khronos.opengles.GL10; public class Pyramid  { private float xcenter; private float ycenter; private float zcenter; public Triangle [] triangle=new Triangle[4]; // массив боковых треугольников public Quadr quadr; // квадрат дна // конструктор  public Pyramid (float x, float y, float z, float l, float h ) {             //x,y,z координаты центра основания             //l пол-ширины основания             //h высота             //создаем боковые треугольники              triangle[0]=new Triangle(x,y+h,z,   x-l,y,z+l,  x+l,y,z+l);             triangle[1]=new Triangle(x,y+h,z,   x+l,y,z+l,  x+l,y,z-l);             triangle[2]=new Triangle(x,y+h,z,   x+l,y,z-l,  x-l,y,z-l);             triangle[3]=new Triangle(x,y+h,z,   x-l,y,z-l,  x-l,y,z+l);             //создаем основание пирамиды             quadr=new Quadr(x-l,y,z+l,  x-l,y,z-l,  x+l,y,z-l);             recount(); // пересчитаем центр } // пересчет координат центра public void recount(){             xcenter=(triangle[0].getx1()+triangle[0].getx2()+triangle[0].getx3()+                         triangle[2].getx2()+triangle[2].getx3())/5;             ycenter=(triangle[0].gety1()+triangle[0].gety2()+triangle[0].gety3()+                         triangle[2].gety2()+triangle[2].gety3())/5;             zcenter=(triangle[0].getz1()+triangle[0].getz2()+triangle[0].getz3()+                         triangle[2].getz2()+triangle[2].getz3())/5; } // для того, чтобы повернуть пирамиду относительно вектора a-b // на угол angle достаточно повернуть боковые треугольники и квадрат дна  public void rotate(float angle,             float xa, float ya, float za,             float xb, float yb, float zb){             // вызываем методы вращения треугольников четырех треугольников             for (int i=0; i<4; i++){                         triangle[i].rotate(angle, xa, ya, za, xb, yb, zb);             }             // вызываем метод вращения квадрата дна              quadr.rotate(angle, xa, ya, za, xb, yb, zb);             // пересчитываем центр пирамиды             recount(); } // частные случаи метода rotate поворачивает пирамиду относительно оси центр-вершина public void rotatecenter0 (float angle){             rotate(angle, xcenter, ycenter, zcenter,                          triangle[0].getx1(),triangle[0].gety1(), triangle[0].getz1()); } // аналогично получаем методы поворачивающие пирамиду относительно оси центр-нижний угол public void rotatecenter1 (float angle){             rotate(angle, xcenter, ycenter, zcenter,                         triangle[0].getx2(), triangle[0].gety2(), triangle[0].getz2()); } public void rotatecenter2 (float angle){             rotate(angle, xcenter, ycenter, zcenter,                         triangle[0].getx3(), triangle[0].gety3(), triangle[0].getz3()); } public void rotatecenter3 (float angle){             rotate(angle, xcenter, ycenter, zcenter,                         triangle[2].getx2(), triangle[2].gety2(), triangle[2].getz2()); } public void rotatecenter4 (float angle){             rotate(angle, xcenter, ycenter, zcenter,                         triangle[2].getx3(), triangle[2].gety3(), triangle[2].getz3()); } // если грани пирамиды надо закрасить разными текстурами применяем этот метод public void setTextureName(int name1, int name2, int name3, int name4, int name5){             // устанавливаем текстуру боковых треугольников             triangle[0].setTextureName(name1);             triangle[1].setTextureName(name2);             triangle[2].setTextureName(name3);             triangle[3].setTextureName(name4);             // устанавливаем текстуру дна             quadr.setTextureName(name5); } // если всю пирамиду надо закрасить одной текстурой применяем этот метод public void setTextureName(int name){             // устанавливаем текстуру боковых треугольников             triangle[0].setTextureName(name);             triangle[1].setTextureName(name);             triangle[2].setTextureName(name);             triangle[3].setTextureName(name);             // устанавливаем текстуру дна             quadr.setTextureName(name); }  // рисуем пирамиду public void draw (GL10 gl){             // рисуем боковые треугольники              for (int i=0; i<4; i++){                         triangle[i].draw(gl);             }             // рисуем дно пирамиды             quadr.draw(gl); } }//конец класса

------------------------------------------------------------------------------------------------------------ 

Рисование осуществляется в классе, реализующем интерфейс GLSurfaceView.Renderer. Создадим такой класс MyClassRenderer. Предварительно поместим в каталоги res\drawable-hdpi, res\drawable-ldpi, res\drawable-mdpi какие-нибудь картинки с именами icon1.jpg, icon2.jpg, icon3.jpg, icon4.jpg, icon5.jpg. Эти фалы будут загружаться в качестве текстур.

------------------------------------------------------------------------------------------------------------ 

package com.blogspot.andmonahov.pyramid;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;

import android.opengl.GLSurfaceView;

import android.opengl.GLU;

public class MyClassRenderer implements GLSurfaceView.Renderer{

            // интерфейс GLSurfaceView.Renderer содержит три метода onDrawFrame, onSurfaceChanged, onSurfaceCreated

            // которые должны быть переопределены текущий контекст

            private Context context;

            // позиция камеры (почему поля определены как public static поясню позже)

            public static float xposition,yposition,zposition;

            // направление взгляда камеры

             private float xlook,ylook,zlook;

            // координаты вектора, указывающего камере, где верх

            private float xtop,ytop,ztop;

            // массивы для хранения цветов материала цвета общего фонового освещения

            private float [] ambientmaterialArray={0.2f, 0.2f, 0.2f, 1f};

            // цвета отраженного рассеянного света

            private float [] diffusematerialArray={0.8f, 0.8f, 0.8f, 1f};

            // цвета отраженного зеркального света

            private float [] specularmaterialArray={0.5f, 0.5f, 0.5f,1f};

            // соответствующие им буферы цветов материала

            private FloatBuffer ambientmaterialBuffer,

                        diffusematerialBuffer,specularmaterialBuffer;

            // массив для хранения координат источника света

            private float [] positionlightArray={0.5f,0,0.2f,0};

            // массивы для хранения цветов источника света цвета общего фонового освещения

            private float [] ambientlightArray={0.5f, 0.5f, 0.5f, 1f};

            // цвета отраженного рассеянного света

            private float [] diffuselightArray={0.8f, 0.8f, 0.8f, 1f};

            // цвета отраженного зеркального света

            private float [] specularlightArray={0.8f, 0.8f, 0.8f,1f};

            // соответствующие им буферы источника света

            private FloatBuffer positionlightBuffer,ambientlightBuffer,

                        diffuselightBuffer,specularlightBuffer;

            //текстуры             private Texture tex1,tex2,tex3,tex4,tex5;

            //пирамиды

            private Pyramid p1, p2;

            //конструктор               public MyClassRenderer(Context context) {

            // запомним контекст,он нам понадобится для загрузки текстур

            this.context=context;

            // настройки камеры

            xposition=0.3f;

            yposition=0.3f;

            zposition=1.2f;

            xlook=0;

            ylook=0;

            zlook=0;

            xtop=0;

            ytop=1;

            ztop=0;

            // переписываем цвета материалов из массивов в буферы

            ByteBuffer b1 = ByteBuffer.allocateDirect(4 * 4);

            b1.order(ByteOrder.nativeOrder());

            ambientmaterialBuffer = b1.asFloatBuffer();

            ambientmaterialBuffer.put(ambientmaterialArray);

            ambientmaterialBuffer.position(0);

            //

            ByteBuffer b2 = ByteBuffer.allocateDirect(4 * 4);

            b2.order(ByteOrder.nativeOrder());

            diffusematerialBuffer = b2.asFloatBuffer();

            diffusematerialBuffer.put(diffusematerialArray);

            diffusematerialBuffer.position(0);

            //

            ByteBuffer b3 = ByteBuffer.allocateDirect(4 * 4);

            b3.order(ByteOrder.nativeOrder());

            specularmaterialBuffer = b3.asFloatBuffer();

            specularmaterialBuffer.put(specularmaterialArray);

            specularmaterialBuffer.position(0);

            //

            // переписываем координаты источника света в буфер

            ByteBuffer b4 = ByteBuffer.allocateDirect(4 * 4);

            b4.order(ByteOrder.nativeOrder());

            positionlightBuffer = b4.asFloatBuffer();

            positionlightBuffer.put(positionlightArray);

            positionlightBuffer.position(0);

            //

            // переписываем цвета источника света из массивов в буферы

            ByteBuffer b5 = ByteBuffer.allocateDirect(4 * 4);

            b5.order(ByteOrder.nativeOrder());

            ambientlightBuffer = b5.asFloatBuffer();

            ambientlightBuffer.put(ambientlightArray);

            ambientlightBuffer.position(0);

            //

            ByteBuffer b6 = ByteBuffer.allocateDirect(4 * 4);

            b6.order(ByteOrder.nativeOrder());

            diffuselightBuffer = b6.asFloatBuffer();

            diffuselightBuffer.put(diffuselightArray);

            diffuselightBuffer.position(0);

            //

            ByteBuffer b7 = ByteBuffer.allocateDirect(4 * 4);

            b7.order(ByteOrder.nativeOrder());

            specularlightBuffer = b7.asFloatBuffer();

            specularlightBuffer.put(specularlightArray);

            specularlightBuffer.position(0);

            // создаем пирамиды

            p1=new Pyramid(-0.15f, 0, 0, 0.3f, 0.45f);

            p2=new Pyramid( 0.3f, 0.2f,-1, 0.3f, 0.45f);

}

public void onDrawFrame(GL10 gl) {

            //этот метод вызывается циклически здесь мы будем выполнять рисование

            // очищаем буферы глубины и цвета

            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

            // перейдем в режим работы с матрицей модели-вида

            gl.glMatrixMode(GL10.GL_MODELVIEW);

            // сбросим матрицу модели-вида на единичную

            gl.glLoadIdentity();

            // поворачиваем пирамиды  вокруг осей относительно текущего положения пирамиды

            p1.rotatecenter0(0.5f);

            p1.rotatecenter1(0.2f);

            p2.rotatecenter1(-0.5f);

            p2.rotatecenter2(-0.2f);

            // устанавливаем камеру

            GLU.gluLookAt(gl, xposition,yposition,zposition, xlook,ylook,zlook, xtop,ytop,ztop);             // включаем источник света с номером 0

            gl.glEnable(GL10.GL_LIGHT0);

            // устанавливаем координаты источника света

            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, positionlightBuffer);

            // устанавливаем цвета источника света

            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, ambientlightBuffer);

            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, diffuselightBuffer);

            gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, specularlightBuffer);

            // устанавливаем цвета материала

            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,                          GL10.GL_AMBIENT, ambientmaterialBuffer);

            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,                          GL10.GL_DIFFUSE, diffusematerialBuffer);

            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,                         GL10.GL_SPECULAR, specularmaterialBuffer);

            //рисуем дальнюю пирамиду

            p2.draw(gl);

            //рисуем ближнюю пирамиду

            p1.draw(gl);

}

public void onSurfaceChanged(GL10 gl, int width, int height) {

            // вызывается при изменении размеров окна установим область просмотра равной размеру экрана

            gl.glViewport(0, 0, width, height);

            // подсчитаем отношение ширина/высота экрана

            float ratio = (float) width / height;

            // перейдем в режим работы с матрицей проекции

            gl.glMatrixMode(GL10.GL_PROJECTION);

            // сбросим матрицу проекции на единичную

            gl.glLoadIdentity();

            // устанавливаем перспективную проекцию угол обзора 60 градусов

            // передняя отсекающая плоскость 0.1 задняя отсекающая плоскость 100

            GLU.gluPerspective (gl, 60, ratio, 0.1f, 100f);

            // перейдем в режим работы с матрицей модели-вида

            gl.glMatrixMode(GL10.GL_MODELVIEW);

}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {

            // вызывается при создании окна включим пересчет нормалей на единичную длину

            gl.glEnable(GL10.GL_NORMALIZE);

            // включим сглаживание цветов

            gl.glShadeModel(GL10.GL_SMOOTH);

            // включим проверку глубины

            gl.glEnable(GL10.GL_DEPTH_TEST);

            gl.glDepthFunc(GL10.GL_LEQUAL);

            // разрешим использовать освещение

            gl.glEnable(GL10.GL_LIGHTING);

            //загружаем текстуры

            tex1=new Texture(gl,context, R.drawable.icon1);

            tex2=new Texture(gl,context, R.drawable.icon2);

            tex3=new Texture(gl,context, R.drawable.icon3);

            tex4=new Texture(gl,context, R.drawable.icon4);

            tex5=new Texture(gl,context, R.drawable.icon5);

            //привязываем текстуры к пирамидам грани ближней пирамиды закрасим разными текстурами

            p1.setTextureName(tex1.getName(),tex2.getName(),                         tex3.getName(),tex4.getName(),tex5.getName());

            // грани дальней пирамиды закрасим одинаковой текстурой

            p2.setTextureName(tex1.getName());

} }//конец класса

------------------------------------------------------------------------------------------------------------

Рендерер будет создан и запущен в классе, расширяющем GLSurfaceView. Создадим такой класс MyClassSurfaceView.

------------------------------------------------------------------------------------------------------------ package com.blogspot.andmonahov.pyramid; import android.content.Context; import android.opengl.GLSurfaceView; public class MyClassSurfaceView extends GLSurfaceView{             //создадим ссылку для хранения экземпляра нашего класса рендерера             private MyClassRenderer renderer; // конструктор public MyClassSurfaceView(Context context) {             // вызовем конструктор родительского класса GLSurfaceView             super(context);             // создадим экземпляр нашего класса MyClassRenderer             renderer = new MyClassRenderer(context);             // запускаем рендерер             setRenderer(renderer);             // установим режим циклического запуска метода onDrawFrame              setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);             // при этом запускается отдельный поток             // в котором циклически вызывается метод onDrawFrame             // т.е. бесконечно происходит перерисовка кадров } } // конец класса

------------------------------------------------------------------------------------------------------------ 

Нам осталось только создать экземпляр класса MyClassSurfaceView в нашем Activity и установить вызов его через метод setContentView.

------------------------------------------------------------------------------------------------------------ package com.blogspot.andmonahov.pyramid; import android.app.Activity; import android.os.Bundle; public class PyramidActivity extends Activity {             // создадим ссылку на экземпляр нашего класса MyClassSurfaceView             private MyClassSurfaceView mGLSurfaceView; // переопределим метод onCreate @Override public void onCreate(Bundle savedInstanceState){ s uper.onCreate(savedInstanceState);             //создадим экземпляр нашего класса MyClassSurfaceView             mGLSurfaceView = new MyClassSurfaceView(this);             //вместо вызова стандартного контента  setContentView(R.layout.main);             //вызовем экземпляр нашего класса MyClassSurfaceView               setContentView(mGLSurfaceView);             // на экране появится поверхность для рисования в OpenGL ES } @Override protected void onPause() {             super.onPause();             mGLSurfaceView.onPause(); } @Override protected void onResume() {             super.onResume();             mGLSurfaceView.onResume(); } } // конец класса

------------------------------------------------------------------------------------------------------------ 

В результате получим две вращающиеся пирамиды

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]