Давайте поговорим о том как использовать 3D текстуры в мидлетах, использующих JSR-184. Использование текстур позволяет значительно повысить качество получаемого изображения. Кроме того умелое использование текстур позволяет повысить производительность вашего приложения. Например достаточно распространенный прием - рендеринг 3D мира в Image2D объект и дальнейшее использование его в качестве текстуры. При этом при последующих перерисовках экрана не нужно опять рендерить весь мир, ограничившись лишь активными(изменяющимися) объектами. При необходимости такую текстуру-задний план можно анимировать, изменяя 3D сцену.
В рассматриваемом примере при создании сцены будем использовать два мира (world).
Первый мир используется для рендеринга пирамиды в Image2D. Второй мир использует полученный Image2D объект в качестве текстуры Mesh сетки. Пирамиду будем задавать координатами ее вершин.
texg3d.bindTarget(sceneTexture);// связываемся с Image2D объектом texg3d.render(texWorld);// Выполняем рендеринг пирамиды в Image2D ... g3d.bindTarget(g);// Указываем, что Image2D будем использовать в качестве цели Graphics3D g3d.render(world);// Выполняем рендеринг мира
Размер грани создаваемой текстуры должен соответствовать положительной степени двойки (2, 4, 8, 16, 32, 64, 128, 256).
sceneTexture=new Image2D(Image2D.RGB,64,64);
Создаем на основании Image2D объекта текстуру и используем ее в качестве текстуры плоскости.
Поскольку sceneTexture всего лишь ссылается на texture1 объект, вид sceneTexture можно менять в реальном времени. Делая это, мы получим анимированную текстуру.
Скачать архив с исходником (46 kB)
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.m3g.*; import javax.microedition.m3g.Camera; import javax.microedition.lcdui.game.*; import java.util.*; publicclass map3DTextureextends MIDlet{ private Display d; private mapTextureCanvas mapTextureCanvas; public map3DTexture(){ mapTextureCanvas=new mapTextureCanvas(this); d= Display.getDisplay(this); d.setCurrent(mapTextureCanvas); } public void startApp(){ } public void pauseApp(){ } public void destroyApp(boolean unconditional){ } } class mapTextureCanvasextends GameCanvas implements Runnable{ private MIDlet midlet; private Graphics3D g3d;// Графический объект для рендеринга мира. private World world;// Это главный мир в сцене. private Camera camera;// Камера для сцены private Mesh plane;// Плоскость private Graphics3D texg3d; private Mesh texMesh; private World texWorld;// Этот мир будем использовать для рендеринга в Image2D private Camera texCamera; private Image2D sceneTexture=null;// 3D текстура для плоскости private Texture2D texture;// Текстура для пирамиды private void doTexture(){ sceneTexture=new Image2D(Image2D.RGB,64,64); texg3d= Graphics3D.getInstance(); texWorld=new World(); texCamera=new Camera(); texWorld.addChild(texCamera);// Помещаем камеру в мир float w= getWidth(); float h= getHeight(); texCamera.setPerspective(60.0f, w/ h, 0.1f, 150f); texWorld.setActiveCamera(texCamera); Background bg=new Background(); try{ Image texImg= Image.createImage("/bgImage.png"); bg.setImage(new Image2D(Image2D.RGB, texImg)); }catch(Exception e){} texWorld.setBackground(bg); texMesh= createpyramid(); texMesh.postRotate(45.0f, 0.5f, 1.0f, 0.0f); texMesh.translate(0.0f, 0.0f,-5.0f); texWorld.addChild(texMesh); } public mapTextureCanvas(MIDlet m){ super(false); midlet= m; setFullScreenMode(true); doTexture(); g3d= Graphics3D.getInstance(); world=new World(); camera=new Camera(); world.addChild(camera);// Помещаем камеру в мир. float w= getWidth(); float h= getHeight(); // Создаем изометрическую матрицу проекций и устанавливаем ее в качестве активной. camera.setPerspective(60.0f, w/ h, 0.1f, 150f); camera.translate(0.0f, 0.0f, 0.0f); world.setActiveCamera(camera); plane= createPlane(); world.addChild(plane); plane.translate(0.0f, 0.0f,-5.0f); Background bg=new Background(); bg.setColor(0x000000); world.setBackground(bg); Thread t=new Thread(this); t.start(); } public void draw3D(Graphics g){ try{ texg3d.bindTarget(sceneTexture);// Связываем Image2D с графическим объектом texg3d.render(texWorld);// выполняем рендеринг пирамиды в Image2D }finally{ texg3d.releaseTarget(); } try{ g3d.bindTarget(g);// Указываем, что Image2D будем использовать // в качестве цели Graphics3D g3d.render(world);// Выполняем рендеринг мира }finally{ g3d.releaseTarget(); } } private void draw2D(Graphics g){ g.setColor(0xFFFFFF); g.drawString("Exit",3, getHeight(), Graphics.LEFT| Graphics.BOTTOM); } protected void keyPressed(intkey){ switch(key){ case-3: break; case-4: break; case-6: midlet.notifyDestroyed(); break; case-7: break; } } public void run(){ Graphics g= getGraphics(); while(true){ texMesh.postRotate(3.0f, 0.0f, 1.0f, 0.0f);// rotate the pyramid plane.postRotate(3.0f, 0.0f, 1.0f, 0.0f);// rotate the plane draw3D(g); draw2D(g); flushGraphics(); try{ Thread.sleep(10); }catch(Exception e){} } } public Mesh createPlane(){ int WIDTH=21, HEIGHT=2; short POINTS[]=new short[]{-1,-1,0, 1,-1,0, 1,1,0, -1,1,0}; short TEXTURES[]=new short[]{0,255, 255,255, 255,0, 0,0}; int INDICES[]=new int[]{0,1,3,2}; int[] LENGTHS=new int[]{4}; VertexArray POSITIONS_ARRAY, TEXTURE_ARRAY, COLOR_ARRAY; IndexBuffer INDEX_BUFFER; // инициализируем общие массивы POSITIONS_ARRAY=new VertexArray(POINTS.length/3,3,2); POSITIONS_ARRAY.set(0, POINTS.length/3, POINTS); TEXTURE_ARRAY=new VertexArray(TEXTURES.length/2,2,2); TEXTURE_ARRAY.set(0, TEXTURES.length/2, TEXTURES); INDEX_BUFFER=new TriangleStripArray(INDICES, LENGTHS); VertexBuffer vertexBuffer=new VertexBuffer(); vertexBuffer.setPositions(POSITIONS_ARRAY, 1.0f,null); vertexBuffer.setTexCoords(0, TEXTURE_ARRAY, 1.0f/255.0f,null); Mesh mesh=new Mesh(vertexBuffer, INDEX_BUFFER,null); Appearance appearance=new Appearance(); PolygonMode pm=new PolygonMode(); pm.setCulling(PolygonMode.CULL_NONE); appearance.setPolygonMode(pm); Image texImg; try{ Texture2D texture1=new Texture2D(sceneTexture); texture1.setBlending(Texture2D.FUNC_REPLACE); texture1.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP); texture1.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST); appearance.setTexture(0, texture1); }catch(Exception e){ System.out.println("Failed to create texture"); } mesh.setAppearance(0, appearance); return mesh; } private Mesh createpyramid(){ // Задаем координаты вершины пирамиды. x, y, z short[]POINTS=new short[]{-1,-1,1,1,-1,1,0,1,0,// фронтальная грань 1,-1,1,1,-1,-1,0,1,0,// правая 1,-1,-1,-1,-1,-1,0,1,0,// задняя -1,-1,-1,-1,-1,1,0,1,0,// левая -1,-1,1,1,-1,1,1,-1,-1,// нижняя правая -1,-1,1,1,-1,-1,-1,-1,-1};// нижняя левая // Текстурные координаты масштабируются в методе setTextCoords так, //что принимают значения от 0 до 1 short[]TEXTURES=new short[]{0,255,255,255,127,0, 0,255,255,255,127,0, 0,255,255,255,127,0, 0,255,255,255,127,0, 0,0,255,0,255,255, 0,0,255,255,0,255}; // Задаем последовательность точек, образующих грани. int[]INDICES=new int[]{0,1,2,// фронтальная грань 3,4,5,// правая 6,7,8,// задняя 9,10,11,// левая 12,13,14,// нижняя правая 15,16,17};// нижняя левая // Длина каждой последовательности в массиве индексов. int[]LENGTH=new int[]{3,3,3,3,3,3};// пирамида сформирована шестью треугольниками VertexArray POSITION_ARRAY, TEXTURE_ARRAY; IndexBuffer INDEX_BUFFER; // Создаеv VertexArray, который используется VertexBuffer POSITION_ARRAY=new VertexArray(POINTS.length/3,3,2); POSITION_ARRAY.set(0, POINTS.length/3, POINTS); TEXTURE_ARRAY=new VertexArray(TEXTURES.length/2,2,2); TEXTURE_ARRAY.set(0, TEXTURES.length/2, TEXTURES); INDEX_BUFFER=new TriangleStripArray(INDICES, LENGTH); // VertexBuffer содержит ссылки на массивы VertexArrays, в которых находятся координаты // точек, цвета, нормали, и текстурные координаты для набора вершин. VertexBuffer vertexBuffer=new VertexBuffer(); vertexBuffer.setPositions(POSITION_ARRAY, 1.0f,null); vertexBuffer.setTexCoords(0, TEXTURE_ARRAY,(1.0f/255.0f),null); vertexBuffer.setTexCoords(1, TEXTURE_ARRAY,(1.0f/255.0f),null); // Создайте трехмерный объект Mesh mesh=new Mesh(vertexBuffer, INDEX_BUFFER,null); // Набор компонент объекта, определяющих атрибуты рендеринга сетки Appearance appearance=new Appearance(); PolygonMode polygonMode=new PolygonMode(); polygonMode.setPerspectiveCorrectionEnable(true); // Используем CULL_BACK (отсечение задних поверхностей) для повышения производительности polygonMode.setCulling(PolygonMode.CULL_BACK); appearance.setPolygonMode(polygonMode); Image texImg; try{ texImg= Image.createImage("/texture2.png"); texture=new Texture2D(new Image2D(Image2D.RGB, texImg)); texture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP); texture.setBlending(Texture2D.FUNC_MODULATE); texture.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST); appearance.setTexture(0, texture); }catch(Exception e){ System.out.println("Failed to create texture"); } mesh.setAppearance(0, appearance);// Присваиваем установленные атрибуты 3D объекту return mesh; } }
Автор:Oscar Vivall
Перевод:aRix.