Кубическая текстура представляет из себя шесть фотографий окружающего пространства: "Правая сторона", "Левая сторона", "Верх", "Низ", "Передняя сторона", "Задняя сторона", которые должны быть подготовлены заранее. Всем шести картинкам присваивается одно имя текстуры и обращение идет к текстуре производится по данному имени. Для создания эффекта зеркального отражения используется автоматическая генерация текстур при помощи команды glTexGeni с параметром GL_REFLECTION_MAP. При этом каждой вершине полигона присваивается координата текстуры, соответствующая зеркальному отражению от поверхности полигона.
Автоматическая генерация текстур несмотря на высокую скорость расчета текстурных координат имеет свои недостатки. В частности, при перемещении камеры зеркальное отражение перемещается в месте с ней и расплывается. Чтобы избавиться от таких нежелательных эффектов, нужно изменять матрицу текстуры. Матрица текстуры - это аналог матрицы модели-вида, применяемый не к координатам вершин X, Y, Z, а координатам текстур S, T, R. Все операции, которые можно применить к матрице модели-вида, можно применять и к матрице текстуры, но действовать они будут на текстурные координаты. По умолчанию матрица текстуры равна единичной матрице. Чтобы зеркальное отражение выглядело корректно нужно компенсировать поворот и перемещение камеры. Для этого записываем в массив текущую матрицу модели-вида с учетом все поворотов и перемещений камеры, получаем из нее обратную матрицу модели-вида, а затем зануляем у обратной матрицы модели-вида элементы, связанные с перемещением. Результат всех этих манипуляций записываем в матрицу текстуры. Перед рисованием следующего полигона матрицу текстуры нужно снова превратить в единичную.
Продемонстрирую это на примере. За основу возьмем код статьи, в которой мы рисовали полупрозрачные пирамиды. Изменим код класса Texture на следующий:
------------------------------------------------------------------------------------------------------------
package com.blogspot.andmonahov.pyramidmirror;import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
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 idpicture1,int idpicture2,int idpicture3,
int idpicture4,int idpicture5,int idpicture6){
// передаем в конструктор шесть фотографий с идентификаторами
// idpicture1, idpicture2, idpicture3, idpicture4, idpicture5, idpicture6
int[] names=new int[1];
// генерируем свободное имя текстуры
gl.glGenTextures(1, names, 0);
name=names[0];
gl.glPixelStorei(GL10.GL_UNPACK_ALIGNMENT,1);
// Вместо параметра состояния GL10.GL_TEXTURE_2D будем
// использовать GL11ExtensionPack.GL_TEXTURE_CUBE_MAP
gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, name);
// устанавливаем параметры текстуры
gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_LINEAR_MIPMAP_LINEAR);
gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
//загружаем картинки
//правая сторона (положительная координата X)
Bitmap bitmap1 = BitmapFactory.decodeResource
(context.getResources(),idpicture1);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
0, bitmap1, 0);
bitmap1.recycle();
//левая сторона (отрицательная координата X)
Bitmap bitmap2 = BitmapFactory.decodeResource
(context.getResources(),idpicture2);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
0, bitmap2, 0);
bitmap2.recycle();
//верх ( положительная координата Y)
Bitmap bitmap3 = BitmapFactory.decodeResource
(context.getResources(),idpicture3);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
0, bitmap3, 0);
bitmap3.recycle();
//низ (отрицательная координата Y)
Bitmap bitmap4 = BitmapFactory.decodeResource
(context.getResources(),idpicture4);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
0, bitmap4, 0);
bitmap4.recycle();
//задняя часть (положительная координата Z)
Bitmap bitmap5 = BitmapFactory.decodeResource
(context.getResources(),idpicture5);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
0, bitmap5, 0);
bitmap5.recycle();
//передняя часть (отрицательная координата Z)
Bitmap bitmap6 = BitmapFactory.decodeResource
(context.getResources(),idpicture6);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
0, bitmap6, 0);
bitmap6.recycle();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// метод возвращает имя текстуры
public int getName(){
return 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(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP);
// для учета освещения при использовании текстур
//включаем режим модуляции
gl.glTexEnvx(GL10.GL_TEXTURE_ENV,
GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
// устанавливаем активной текстуру с именем textureName
gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
textureName);
GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
GL11 gl11 = (GL11) gl;
// переходим в режим работы с текстурной матрицей
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
// записываем в массив modelViewMatrix
//текущую матрицу модели-вида
float[] modelViewMatrix = new float[16];
gl11.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, modelViewMatrix, 0);
//записываем в массив invertModelViewMatrix
//обратную матрицу модели-вида
float[] invertModelViewMatrix=new float [16];
Matrix.invertM(invertModelViewMatrix,0,modelViewMatrix,0);
// зануляем у обратной матрицы модели-вида компоненты,
// связанные с переносом
invertModelViewMatrix[12]=0;
invertModelViewMatrix[13]=0;
invertModelViewMatrix[14]=0;
// записываем invertModelViewMatrix в матрицу текстуры
gl.glLoadMatrixf(invertModelViewMatrix,0);
//генерируем координаты зеркального отражения
gl.glEnable(GL11ExtensionPack.GL_TEXTURE_GEN_STR);
gl11ep.glTexGeni(GL11ExtensionPack.GL_TEXTURE_GEN_STR,
GL11ExtensionPack.GL_TEXTURE_GEN_MODE,
GL11ExtensionPack.GL_REFLECTION_MAP);
//переходим в режим работы с матрицей модели-вида
gl.glMatrixMode(GL10.GL_MODELVIEW);
}else{
// если имя текстуры пустое
// отключаем использование кубических текстур
gl.glDisable(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP);
}
// рисуем треугольник
// последний параметр - это количество точек треугольника (т.е. три)
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
}
------------------------------------------------------------------------------------------------------------
Аналогично заменим метод draw в классе Quadr.
Отличие будет только в последней строке. Вместо glDrawArrays(GL10.GL_TRIANGLES,0,3) у прямоугольника будет glDrawArrays(GL10.GL_TRIANGLE_FAN,0,4);
Изменим код класса MyClassRenderer. Вместо объявления пяти текстур объявим только одну private Texture texcubemap; и выполним в методе onSurfaceCreated загрузку только одной кубической текстуры
private float [] diffusematerialArray={0.8f, 0.8f, 0.8f, 0.8f};
Привожу код класса MyClassRenderer полностью:
-----------------------------------------------------------------------------------------------------------
package com.blogspot.andmonahov.pyramidmirror;
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 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, 0.8f};
// цвета отраженного зеркального света
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 texcubemap;
//пирамиды
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);
// включаем режим освещения внешних и внутренних сторон
gl.glLightModelx(GL10.GL_LIGHT_MODEL_TWO_SIDE, GL10.GL_TRUE);
// отключаем отсечение невидимых граней
gl.glDisable(GL10.GL_CULL_FACE);
// включаем альфа-компонету цвета
gl.glEnable(GL10.GL_ALPHA);
// включаем смешивание цветов
gl.glEnable(GL10.GL_BLEND);
// устанавливаем режим смешивания цветов
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
//
//загружаем фотографии в кубическую текстуру
texcubemap=new Texture(gl,context, R.drawable.icon1,R.drawable.icon2,
R.drawable.icon3,R.drawable.icon4,
R.drawable.icon5,R.drawable.icon6);
//привязываем текстуры к пирамидам
p1.setTextureName(texcubemap.getName());
p2.setTextureName(texcubemap.getName());
}
}
Исполняемый файл для телефона можно скачать отсюда Ссылка
GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterx(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE);
//загружаем картинки
//правая сторона (положительная координата X)
Bitmap bitmap1 = BitmapFactory.decodeResource
(context.getResources(),idpicture1);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
0, bitmap1, 0);
bitmap1.recycle();
//левая сторона (отрицательная координата X)
Bitmap bitmap2 = BitmapFactory.decodeResource
(context.getResources(),idpicture2);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
0, bitmap2, 0);
bitmap2.recycle();
//верх ( положительная координата Y)
Bitmap bitmap3 = BitmapFactory.decodeResource
(context.getResources(),idpicture3);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
0, bitmap3, 0);
bitmap3.recycle();
//низ (отрицательная координата Y)
Bitmap bitmap4 = BitmapFactory.decodeResource
(context.getResources(),idpicture4);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
0, bitmap4, 0);
bitmap4.recycle();
//задняя часть (положительная координата Z)
Bitmap bitmap5 = BitmapFactory.decodeResource
(context.getResources(),idpicture5);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
0, bitmap5, 0);
bitmap5.recycle();
//передняя часть (отрицательная координата Z)
Bitmap bitmap6 = BitmapFactory.decodeResource
(context.getResources(),idpicture6);
GLUtils.texImage2D(
GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
0, bitmap6, 0);
bitmap6.recycle();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// метод возвращает имя текстуры
public int getName(){
return name;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
} //конец класса
------------------------------------------------------------------------------------------------------------
Далее нам не нужно задавать координаты текстуры для вершин полигонов, т.к. мы попросим OpenGL генерировать их автоматически с параметром GL_REFLECTION_MAP, создающим координаты зеркального отражения. В классе Triangle заменим метод draw, рисующий треугольник:
------------------------------------------------------------------------------------------------------------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(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP);
// для учета освещения при использовании текстур
//включаем режим модуляции
gl.glTexEnvx(GL10.GL_TEXTURE_ENV,
GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
// устанавливаем активной текстуру с именем textureName
gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
textureName);
GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
GL11 gl11 = (GL11) gl;
// переходим в режим работы с текстурной матрицей
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
// записываем в массив modelViewMatrix
//текущую матрицу модели-вида
float[] modelViewMatrix = new float[16];
gl11.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, modelViewMatrix, 0);
//записываем в массив invertModelViewMatrix
//обратную матрицу модели-вида
float[] invertModelViewMatrix=new float [16];
Matrix.invertM(invertModelViewMatrix,0,modelViewMatrix,0);
// зануляем у обратной матрицы модели-вида компоненты,
// связанные с переносом
invertModelViewMatrix[12]=0;
invertModelViewMatrix[13]=0;
invertModelViewMatrix[14]=0;
// записываем invertModelViewMatrix в матрицу текстуры
gl.glLoadMatrixf(invertModelViewMatrix,0);
//генерируем координаты зеркального отражения
gl.glEnable(GL11ExtensionPack.GL_TEXTURE_GEN_STR);
gl11ep.glTexGeni(GL11ExtensionPack.GL_TEXTURE_GEN_STR,
GL11ExtensionPack.GL_TEXTURE_GEN_MODE,
GL11ExtensionPack.GL_REFLECTION_MAP);
//переходим в режим работы с матрицей модели-вида
gl.glMatrixMode(GL10.GL_MODELVIEW);
}else{
// если имя текстуры пустое
// отключаем использование кубических текстур
gl.glDisable(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP);
}
// рисуем треугольник
// последний параметр - это количество точек треугольника (т.е. три)
gl.glDrawArrays(GL10.GL_TRIANGLES,0,3);
}
------------------------------------------------------------------------------------------------------------
Аналогично заменим метод draw в классе Quadr.
Отличие будет только в последней строке. Вместо glDrawArrays(GL10.GL_TRIANGLES,0,3) у прямоугольника будет glDrawArrays(GL10.GL_TRIANGLE_FAN,0,4);
Изменим код класса MyClassRenderer. Вместо объявления пяти текстур объявим только одну private Texture texcubemap; и выполним в методе onSurfaceCreated загрузку только одной кубической текстуры
texcubemap=new Texture(gl,context, R.drawable.icon1,R.drawable.icon2,
R.drawable.icon3,R.drawable.icon4,
R.drawable.icon5,R.drawable.icon6);
Для улучшения эффекта зеркальности увеличим Альфа-компоненту отраженного рассеянного света:private float [] diffusematerialArray={0.8f, 0.8f, 0.8f, 0.8f};
Привожу код класса MyClassRenderer полностью:
-----------------------------------------------------------------------------------------------------------
package com.blogspot.andmonahov.pyramidmirror;
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 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, 0.8f};
// цвета отраженного зеркального света
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 texcubemap;
//пирамиды
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);
// включаем режим освещения внешних и внутренних сторон
gl.glLightModelx(GL10.GL_LIGHT_MODEL_TWO_SIDE, GL10.GL_TRUE);
// отключаем отсечение невидимых граней
gl.glDisable(GL10.GL_CULL_FACE);
// включаем альфа-компонету цвета
gl.glEnable(GL10.GL_ALPHA);
// включаем смешивание цветов
gl.glEnable(GL10.GL_BLEND);
// устанавливаем режим смешивания цветов
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
//
//загружаем фотографии в кубическую текстуру
texcubemap=new Texture(gl,context, R.drawable.icon1,R.drawable.icon2,
R.drawable.icon3,R.drawable.icon4,
R.drawable.icon5,R.drawable.icon6);
//привязываем текстуры к пирамидам
p1.setTextureName(texcubemap.getName());
p2.setTextureName(texcubemap.getName());
}
}
-------------------------------------------------------------------------------------------------------
Получаем две красивые вращающиеся пирамиды
Исходный код можно скачать отсюда СсылкаПолучаем две красивые вращающиеся пирамиды
Исполняемый файл для телефона можно скачать отсюда Ссылка
Спасибо большое за статью, очень познавательно.
ОтветитьУдалитьЭтот комментарий был удален автором.
ОтветитьУдалитьна Samsung Galaxy Nexus i9250 не выводятся текстуры. С чем это может быть связано?
ОтветитьУдалитьВсе 6 частей кубической текстуры должны быть квадратными и иметь одинаковый размер. Кроме того, некоторые видеокарты требуют, чтобы ширина и высота текстур должны быть степенью двойки (32, 64, 128, 256, 512, 1024 и.т.д.)
УдалитьВ вашем примере текстуры как раз квадратные 512х512. И они не выводятся.
Удалитьразобрался!
УдалитьВместо:
Bitmap bitmap =
BitmapFactory.decodeResource(context.getResources(), idpicture);
заменил на:
InputStream is = context.getResources()
.openRawResource(idpicture);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch(IOException e) {
// Ignore.
}
}
Огромное спасибо за примеры!
Странно! Вопрос. В изначальном варианте
УдалитьBitmap bitmap =
BitmapFactory.decodeResource(context.getResources(), idpicture);
система выдавала какую-нибудь ошибку или просто был черный экран ?
просто черный экран. И это проявлялось только на I9250 с ICS 4.2.2! На таблетке Galaxy Note 10.1 с ICS 4.1 нормально работают оба варианта.
УдалитьА у меня еще интересней. Изначальный вариант работает, а с потоком на телефоне Alcatel OneTouch 992D текстуры белые!
ОтветитьУдалитьПопробуйте поместить файлы с текстурами в папку res\drawable-nodpi
Удалить