5 апреля 2012 г.

OpenGL ES 1. Как сделать текстуры прозрачными

В этой статье, основанной на предыдущем примере показано как сделать грани пирамиды полупрозрачными, рассмотрен вопрос о сортировке полигонов для правильного отображения эффекта прозрачности. Для этих целей будем использовать режим смешивания цветов при помощи альфа-компоненты цвета материла. Всего у цвета есть четыре компонента: красный, зеленый, синий и альфа. Яркость каждой компоненты изменяется в диапазоне от 0 до 1. Альфа компонент-это четвертый компонент цвета. Он определяет степень непрозрачности объекта. 
Альфа=0 - полностью прозрачный объект. Альфа=1 - полностью непрозрачный объект.
В заголовке класса MyClassRenderer изменим строку:
private float [] diffusematerialArray={0.8f, 0.8f, 0.8f, 1};
на float [] diffusematerialArray={0.8f, 0.8f, 0.8f, 0.5f};
т.е. установим альфа-компоненту рассеянного цвета материала равной 0.5.

Кроме того нужно в метод onSurfaceCreated класса MyClassRenderer добавить следующие настройки:
// включаем модель освещения внешних и внутренних сторон 
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);
------------------------------------------------------------------------------------------------------------
Запустив программу на исполнение, убедимся что эффект прозрачности появляется и исчезает при вращении пирамиды. Это происходит потому, что мы рисуем полигоны, из которых состоит пирамида, в определенном порядке без учета их удаленности от глаза наблюдателя.
Поэтому нам нужно рисовать полигоны от дальнего к ближнему, чтобы эффект прозрачности не пропадал при повороте пирамиды. Займемся сортировкой полигонов в пирамиде. Поскольку полигоны представляют объекты разных классов Triangle и Quadr нам нужен метод, который сортирует объекты разных типов по определенному индексу. В Java таким методом является метод compareTo интерфейса Comparable. Создадим абстрактный класс Polygon, реализующий интерфейс Comparable.
------------------------------------------------------------------------------------------------------------ 

package com.blogspot.andmonahov.pyramid;
import javax.microedition.khronos.opengles.GL10;
public abstract class Polygon implements Comparable<Object> {
            // поле полигона, по которому производится сортировка
            public float indexsort;
            // абстрактный метод рисования
            // у каждого полигона он свой
            public abstract void draw(GL10 gl);
            // метод-сортировщик по полю indexsort
            // сортировка выполняется по убыванию значения indexsort
            public int compareTo(Object obj) {
                        Polygon tmp = (Polygon)obj;
                        if(this.indexsort < tmp.indexsort){
                                    return 1;
                        }   
                        else if(this.indexsort > tmp.indexsort){
                                    return -1;
                        }
                         return 0;  
            }
}
------------------------------------------------------------------------------------------------------------ 
Наследуем классы Triangle и Quadr от общего предка Polygon следующим образом:
public class Triangle extends Polygon {............
public class Quadr extends Polygon{............
При этом полигоны наследуют поле indexsort, по которому будет производиться сортировка в методе compareTo. Теперь нужно определить значение indexsort для полигонов внутри пирамиды. Таким полем будет расстояние от камеры до центра полигона. Поскольку поля для координат камеры xposition, yposition, position объявлены в классе MyClassRenderer как public static мы можем получить доступ к ним из любого класса, в т.ч. из класса пирамиды. Перепишем метод draw класса Pyramid:
------------------------------------------------------------------------------------------------------------ 

public void draw (GL10 gl){
            // координаты вектора от камеры до центра полигона
            float dx; 
            float dy;
            float dz;
            //задаем сортировку по расстоянию от камеры до центра полигона
            for (int i=0; i<4; i++){
                        dx=MyClassRenderer.xposition-triangle[i].getxcenter();
                        dy=MyClassRenderer.yposition-triangle[i].getycenter();
                        dz=MyClassRenderer.zposition-triangle[i].getzcenter();
                        triangle[i].indexsort=dx*dx+dy*dy+dz*dz;
            }
            dx=MyClassRenderer.xposition-quadr.getxcenter();
            dy=MyClassRenderer.yposition-quadr.getycenter();
            dz=MyClassRenderer.zposition-quadr.getzcenter();
            quadr.indexsort=dx*dx+dy*dy+dz*dz;
            //создаем массив полигонов
            Polygon [] polygon =new Polygon[5];
            for (int i=0; i<4; i++){
                        polygon[i]=triangle[i]; //записываем в массив полигонов треугольники
            }
            polygon[4]=quadr; // последний элемент массива-квадрат дна
            // сортируем массив полигонов по полю indexsort
            Arrays.sort(polygon);
            // рисуем массив полигонов
            for (int i=0; i<5; i++){
                        polygon[i].draw(gl);
            }
}
------------------------------------------------------------------------------------------------------------ 
Теперь эффект полупрозрачности не зависит от вращения пирамиды. Получаем такую картинку:

-----------------------------------------------------------------------------------------------
Исходник можно скачать отсюда Ссылка
Исполняемый файл для телефона (apk) можно скачать отсюда Ссылка

10 комментариев:

  1. Здравствуйте, возник такой вопрос, написал приложение, на эмуляторе оно работает, на телефоне работает, но пирамиды без текстур, хотя прозрачные.
    Решил попробывать запустить ваше приложение по ссылке, но и оно так же плохо работает на телефоне. Подскажите, пожалуйста, в чем проблема, телефон jiayu g3 версия android 4.0.4

    ОтветитьУдалить
    Ответы
    1. Переделайте размер текстур на степень двойки, т.е. 128х128, 256х256, 512х512, и.т.п.
      Скорее всего ваша видеокарта не поддерживает текстуры произвольного размера.

      Удалить
    2. Попробывал как вы сказали, заработало на другом телефоне, на котором не работало до этого, но на моем все еще ни одна текстура не загружается, пробывал 64х64,128х128,256х256,512х512, 1024х 1024

      Удалить
    3. А другие программы с OpenGL ES на Вашем телефоне работают ?

      Удалить
    4. Я правильно понимаю что большинство игр пишутся с ним? или нет? если да, то многие идут, пока не встречал не идущих, если нет, подскажите с помощью чего они пишутся. Или как можно по другому отобразить текстуры на экране

      Удалить
    5. Попробуйте изменить способ загрузки Bitmap в классе Texture
      Вместо:
      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) {

      }
      }

      Удалить
    6. Спасибо большое, подействовало, уроки действительно полезны, благодарен вам за ваш труд

      Удалить
  2. спасибо за уроки. все понятно и доходчиво.

    Подскажите пожалуйста как сделать чтобы прозрачной была не вся текстура, а только там где у самой текстуры альфа канал??

    ОтветитьУдалить
    Ответы
    1. О, получилось сделать что бы альфа канал с текстуры работал в программе, если интересно скину код.

      Удалить
  3. для загрузки альфа канала из текстуры применялся один из параметров texImage2D, но работает ли он на OpenGLES10 не знаю

    ОтветитьУдалить