постоянная демонстрация обновляющихся данных на OpenGL

Вопрос или проблема

Я пытаюсь отобразить хронометр и постоянно обновляемые данные, такие как пройденное расстояние в OpenGL. Также есть кнопки для управления хронометром. Я планирую нарисовать текст на текстуре, а затем добавить текстуру к прямоугольной форме. (если есть лучший способ, пожалуйста, сообщите мне)

У меня уже есть код для моего хронометра и других данных, но как передать эти данные в OpenGL?

(Используя Android Studio, OpenGL ES 2.0)

мой код на java;

package com.example.code;

import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Chronometer;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.Locale;

public class Activity extends AppCompatActivity implements LocationListener {
    private static final String TAG = "SpeedMeasurementActivity";

    private LocationManager locationManager;
    //private TextView currentSpeedTextView;
    //private TextView distanceTextView;
    private Chronometer chronometer;
    private FloatingActionButton startStopButton;
    private boolean isRunning = false;
    private double totalDistance = 0;
    private Location lastLocation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_no_ar);

        // Инициализация представлений
        //currentSpeedTextView = findViewById(R.id.speed_text_view);
        //distanceTextView = findViewById(R.id.distance_text_view);
        chronometer = findViewById(R.id.time_text_view);
        startStopButton = findViewById(R.id.fab_startstop);
        FloatingActionButton back = findViewById(R.id.fab_back);

        // Инициализация менеджера местоположения
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        startStopButton.setOnClickListener(v -> {
            if (!isRunning) {
                startMeasurement();
            } else {
                stopMeasurement();
            }
        });

        // Настройка кнопки назад
        back.setOnClickListener(v -> {
            Intent intent1 = new Intent(getApplication(), ActivityMain.class);
            startActivity(intent1);
            finish();
        });
    }

    private void startMeasurement() {
        isRunning = true;
        startStopButton.setImageResource(R.drawable.ic_action_stop);
        startStopButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(code.this, R.color.red)));
        chronometer.setBase(SystemClock.elapsedRealtime());
        chronometer.start();

        try {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, this);
        } catch (SecurityException e) {
            Log.e(TAG, "request Location Updates error.", e);
        }
    }

    private void stopMeasurement() {
        isRunning = false;
        startStopButton.setImageResource(R.drawable.ic_action_play);
        startStopButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(code.this, R.color.green)));
        chronometer.stop();
        locationManager.removeUpdates(this);
    }

    @Override
    public void onLocationChanged(@NonNull Location location) {
        if (isRunning) {
            double speed = location.getSpeed() * 3.6; // Преобразование м/с в км/ч
            currentSpeedTextView.setText(String.format(Locale.getDefault(), "%.1f км/ч", speed));

            if (lastLocation != null) {
                float distance = lastLocation.distanceTo(location);
                totalDistance += distance;
                distanceTextView.setText(String.format(Locale.getDefault(), "%.2f м", totalDistance));
            }
            lastLocation = location;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        locationManager.removeUpdates(this);
    }

    // Реализуйте другие методы LocationListener (onStatusChanged, onProviderEnabled, onProviderDisabled)
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {}

    @Override
    public void onProviderEnabled(@NonNull String provider) {}

    @Override
    public void onProviderDisabled(@NonNull String provider) {}
}

MyGlSurfaceView.java;

package com.example.code;

import android.content.Context;
import android.opengl.GLSurfaceView;

public class MyGLSurfaceView extends GLSurfaceView {
    private final Context mContext;
    public GLRenderer renderer;
    private static final String TAG = "MyGLSurfaceView";

    public MyGLSurfaceView(Context context) {
        super(context);
        mContext=context;
        setEGLContextClientVersion(2);
        setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        renderer = new GLRenderer(context);
        setRenderer(renderer);
    }
}

TexRectangular.java;

package com.example.code;

import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

public class TexRectangular {
    private FloatBuffer vertexBuffer;
    private ByteBuffer indexBuffer;
    private FloatBuffer normalBuffer;
    private FloatBuffer texcoordBuffer;

    private float[] vertexs= new float[4*3];

    private byte[] indexs= {
            0,2,1,3
    };

    private float[] normals= {
            0f,0f,1f,
            0f,0f,1f,
            0f,0f,1f,
            0f,0f,1f
    };
    float textcoords[] = {
            0f, 0f, 
            1f, 0f, 
            0f, 1f, 
            1f, 1f  
    };

    TexRectangular() {
        setRectangular(1f, 1f);
    }
    TexRectangular(float width, float height) {
        setRectangular(width, height);
    }

    public void setRectangular(float width, float height) {
        float top=height*.5f;
        float bottom=-top;
        float right=width*.5f;
        float left=-right;

        float[] vertexs= {
                left, top, 0f,     
                right, top, 0f,    
                left, bottom, 0f,  
                right, bottom, 0f  
        };
        vertexBuffer=BufferUtil.makeFloatBuffer(vertexs);
        indexBuffer=BufferUtil.makeByteBuffer(indexs);
        normalBuffer=BufferUtil.makeFloatBuffer(normals);
        texcoordBuffer = BufferUtil.makeFloatBuffer(textcoords);
    }

    public void draw(float r,float g,float b,float a, float shininess) {
     
        GLES20.glVertexAttribPointer(GLES.texcoordHandle, 2,
                GLES20.GL_FLOAT, false, 0, texcoordBuffer);

     
        GLES20.glVertexAttribPointer(GLES.positionHandle, 3,
                GLES20.GL_FLOAT, false, 0, vertexBuffer);

       
        GLES20.glVertexAttribPointer(GLES.normalHandle, 3,
                GLES20.GL_FLOAT, false, 0, normalBuffer);

        GLES20.glUniform4f(GLES.materialAmbientHandle, r, g, b, a);

        GLES20.glUniform4f(GLES.materialDiffuseHandle, r, g, b, a);

        GLES20.glUniform4f(GLES.materialSpecularHandle, 1f, 1f, 1f, a);
        GLES20.glUniform1f(GLES.materialShininessHandle, shininess);

        GLES20.glUniform4f(GLES.objectColorHandle, r, g, b, a);

        //
        indexBuffer.position(0);
        GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP,
                4, GLES20.GL_UNSIGNED_BYTE, indexBuffer);
    }
}

StringTexture.java;

package com.example.code;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.opengl.GLES20;
import android.opengl.GLUtils;

import javax.microedition.khronos.opengles.GL10;

public class StringTexture {
    private int TextureId=-1;
    private int TextureUnitNumber=0;
    StringTexture(String text, float textSize, int txtcolor, int bkcolor, int textureidnumber) {
        TextureUnitNumber = textureidnumber;
        makeStringTexture(text, textSize, txtcolor, bkcolor);
    }
    StringTexture(String text, float textSize, int txtcolor, int bkcolor) {
        makeStringTexture(text, textSize, txtcolor, bkcolor);
    }
    public void makeStringTexture(String text, float textSize, int txtcolor, int bkcolor) {
        Paint paint = new Paint();
        paint.setTextSize(textSize);
        paint.setAntiAlias(true);
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        paint.getTextBounds(text, 0, text.length(), new Rect(0, 0, (int) textSize * text.length(), (int) textSize));

        int textWidth = (int) paint.measureText(text);
        int textHeight = (int) (Math.abs(fontMetrics.top) + fontMetrics.bottom);

        if (textWidth == 0) textWidth = 10;
        if (textHeight == 0) textHeight = 10;

        int bitmapsize = 2; 
        while (bitmapsize < textWidth) bitmapsize *= 2;
        while (bitmapsize < textHeight) bitmapsize *= 2;

        Bitmap bitmap = Bitmap.createBitmap(bitmapsize, bitmapsize, Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(bitmap);
        paint.setColor(bkcolor);
        canvas.drawRect(new Rect(0, 0, bitmapsize, bitmapsize), paint);
        paint.setColor(txtcolor);
        canvas.drawText(text, bitmapsize / 2 - textWidth / 2, bitmapsize / 2 - (fontMetrics.ascent + fontMetrics.descent) / 2, paint);

        int FIRST_INDEX = 0;
        final int DEFAULT_OFFSET = 0;
        final int[] textures = new int[1];
        if (TextureId!=-1) {
            textures[FIRST_INDEX]=TextureId;
            GLES20.glDeleteTextures(1, textures, DEFAULT_OFFSET);
        }
        GLES20.glGenTextures(1, textures, DEFAULT_OFFSET);
        TextureId = textures[FIRST_INDEX];
//        GLES20.glActiveTexture(GLES20.GL_TEXTURE0+TextureUnitNumber);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, TextureId);
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
    }
    public void setTexture() {
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0+TextureUnitNumber);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, TextureId);
        GLES20.glUniform1i(GLES.textureHandle, TextureUnitNumber); 
    }

}

MyGLRenderer.java;

package com.example.code;

import android.content.Context;
import android.graphics.Color;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyGLRenderer implements GLSurfaceView.Renderer {
    private final Context mContext;
    private boolean validProgram=false;

    private float aspect;
    private float viewingangle = 45f;

    private  float[] LightPos={0f,0f,3f,0f};//x,y,z,1

    private  float[] pMatrix=new float[16];
    private  float[] mMatrix=new float[16];
    private  float[] cMatrix=new float[16];

    private TexRectangular MyTexRectangular = new TexRectangular();
    private StringTexture Speed;
    private StringTexture Distance;
    private StringTexture Time;

    private static float[] DummyFloat= new float[1];
    private static final FloatBuffer DummyBuffer=BufferUtil.makeFloatBuffer(DummyFloat);

    MyGLRenderer(final Context context) {
        mContext = context;
    }

    private String speedText = "";
    private String distanceText = "";
    private String timeText = "";

    @Override
    public void onSurfaceCreated(GL10 gl10,EGLConfig eglConfig) {
        validProgram = GLES.makeProgram();

        GLES20.glEnableVertexAttribArray(GLES.positionHandle);
        GLES20.glEnableVertexAttribArray(GLES.normalHandle);
        GLES20.glEnableVertexAttribArray(GLES.texcoordHandle);

        GLES20.glEnable(GLES20.GL_DEPTH_TEST);

        GLES20.glEnable(GLES20.GL_CULL_FACE);

        GLES20.glFrontFace(GLES20.GL_CCW);
        GLES20.glCullFace(GLES20.GL_BACK);

        GLES20.glUniform4f(GLES.lightAmbientHandle, 0.5f, 0.5f, 0.5f, 1.0f);
        GLES20.glUniform4f(GLES.lightDiffuseHandle, 1f, 1f, 1f, 1.0f);
        GLES20.glUniform4f(GLES.lightSpecularHandle, 0f, 0f, 0f, 0f);
        GLES20.glClearColor(0f, 0f, 0f, 1.0f);

        GLES20.glEnable(GLES20.GL_TEXTURE_2D);

        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
    }

    //установить текст скорости
    public void setSpeedText(float f){
        //int speedInt;
        String str;
        //speed = f;
        str = String.valueOf(f);

        speedText = str + "км/ч";
    }

    // установить текст расстояния
    public void setDistanceText(float f){
        //int distanceInt;
        String str;
        //distance = f;
        str = String.valueOf(f);

        distanceText = str + "м";
    }

    // установить текст таймера
    public void setTimeText(float f){
        //int timeInt;
        String str;
        //time = f;
        str = String.valueOf(f);

        timeText = str;
    }
    
    @Override
    public void onSurfaceChanged(GL10 gl10,int w,int h) {
        GLES20.glViewport(0,0,w,h);
        aspect=(float)w/(float)h;
    }
    
    @Override
    public void onDrawFrame(GL10 glUnused) {
        if (!validProgram) return;

        GLES20.glVertexAttribPointer(GLES.positionHandle, 3, GLES20.GL_FLOAT, false, 0, DummyBuffer);
        GLES20.glVertexAttribPointer(GLES.normalHandle, 3, GLES20.GL_FLOAT, false, 0, DummyBuffer);
        GLES20.glVertexAttribPointer(GLES.texcoordHandle, 2, GLES20.GL_FLOAT, false, 0, DummyBuffer);

        Speed = new StringTexture(speedText,16, Color.WHITE, Color.BLACK);
        Distance = new StringTexture(distanceText,16, Color.WHITE, Color.BLACK);
        Time = new StringTexture(timeText,16, Color.WHITE, Color.BLACK);

        GLES.disableTexture();
        GLES.enableShading();

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT |
                GLES20.GL_DEPTH_BUFFER_BIT);

        GLES.gluPerspective(pMatrix,
                viewingangle,
                aspect,
                .01f,
                120.0f);
        GLES.setPMatrix(pMatrix);

        float[] c1Matrix=new float[16];
        float[] c2Matrix=new float[16];
        Matrix.setLookAtM(c1Matrix, 0,
                0f,0f,0f,
                0.0f, 0.0f, -1.0f,
                0.0f, 1.0f, 0.0f);

        Matrix.multiplyMM(cMatrix, 0, c1Matrix, 0, c2Matrix, 0); //cMatrix = c1Matrix * c2Matrix

        GLES.setCMatrix(cMatrix);

        GLES.setLightPosition(LightPos);

        GLES.disableShading();

        GLES.enableShading();

        GLES.enableTexture();

        // Нарисуйте текст скорости
        Matrix.setIdentityM(mMatrix, 0);
        Matrix.translateM(mMatrix, 0, 0f, 1.0f, 0f); // Позиция для текста скорости
        Matrix.scaleM(mMatrix, 0, 0.4f, 0.01f, 0.4f);
        GLES.updateMatrix(mMatrix);
        Speed.setTexture();

        MyTexRectangular.draw(1f, 1f, 1f, 1f, 20.f);

        // Нарисуйте текст расстояния
        Matrix.setIdentityM(mMatrix, 0);
        Matrix.translateM(mMatrix, 0, 0f, 2.0f, 0f); // Позиция для текста расстояния
        Matrix.scaleM(mMatrix, 0, 0.4f, 0.01f, 0.4f);
        GLES.updateMatrix(mMatrix);
        Distance.setTexture();

        MyTexRectangular.draw(1f, 1f, 1f, 1f, 20.f);


        // Нарисуйте текст времени
        Matrix.setIdentityM(mMatrix, 0);
        Matrix.translateM(mMatrix, 0, 0f, 3.0f, 0f); // Позиция для текста времени
        Matrix.scaleM(mMatrix, 0, 0.4f, 0.01f, 0.4f);
        GLES.updateMatrix(mMatrix);
        Time.setTexture();

        MyTexRectangular.draw(1f, 1f, 1f, 1f, 20.f);

        GLES.disableTexture();
    }
}

п/с: если что-то не так с моим кодом, пожалуйста, скажите мне! Я все еще столкнулся с проблемами, особенно с MyGLRenderer.java

Ответ или решение

Отображение непрерывно обновляющихся данных, таких как хронометр и информация о пройденном расстоянии на OpenGL ES 2.0 для Android, может быть достигнуто с помощью внедрения текстур, которые обновляются каждый раз при изменении данных. Ваш подход к отрисовке текста на текстуре и добавлению текстуры на прямоугольную поверхность является подходящим. Давайте детально разберем ваш вопрос и предложим некоторые улучшения.

Шаги для создания приложения

  1. Подготовка текстур для текста:
    Вы уже создали класс StringTexture, который генерирует текстуру из заданной строки. Убедитесь, что текстура обновляется каждый раз, когда изменяются данные о скорости, расстоянии или времени. В MyGLRenderer.java вы можете обновить текстуры после получения новых данных. Например:

    @Override
    public void onDrawFrame(GL10 glUnused) {
       if (!validProgram) return;
    
       // Обновляем текстуры с данными
       Speed = new StringTexture(speedText, 16, Color.WHITE, Color.BLACK);
       Distance = new StringTexture(distanceText, 16, Color.WHITE, Color.BLACK);
       Time = new StringTexture(timeText, 16, Color.WHITE, Color.BLACK);
    
       // Далее идет код отрисовки ...
    }
  2. Передача данных из Activity в MyGLRenderer:
    Ваша основная Activity должна вызывать методы установки текста в MyGLRenderer, когда новые данные получены. Для этого создайте метод, который будет принимать значения скорости, расстояния и времени, и обновлять соответствующие поля. Например:

    private void updateGLRenderer() {
       runOnUiThread(() -> {
           // Обновите текстовые данные в рендерере
           myGLSurfaceView.renderer.setSpeedText(speed);
           myGLSurfaceView.renderer.setDistanceText(totalDistance);
           myGLSurfaceView.renderer.setTimeText(elapsedTime);
       });
    }
  3. Использование OpenGL для отрисовки текстур с текстом:
    Убедитесь, что вы правильно вызываете функции OpenGL для установки текстуры и ее отрисовки как прямоугольника на экране, как вы это уже сделали в методе onDrawFrame.

  4. Оптимизация обновления текстур:
    Чтобы избежать создания текстуры каждый кадр и улучшить производительность, вы можете обновлять текстуры только при необходимости. Например, если скорость или расстояние изменились, создайте новые текстуры только в этом случае.

Примечания к коду

  1. Управление потоками:
    Помните, что обновление данных текущее и отрисовка должны выполняться в основном потоке. Ваша реализация, использующая runOnUiThread(), эффективна для этого.

  2. Обработка жизненного цикла:
    Убедитесь, что ваша Activity корректно управляет обновлением местоположения и сохраняет состояние, когда она становится невидимой (например, в onPause()).

  3. Выделение своих текстур:
    В StringTexture.java вы можете добавить метод для уничтожения текстур, чтобы избежать утечек памяти.

Заключение

Таким образом, вы можете ввести непрерывное обновление данных на OpenGL ES 2.0 в вашем проекте. Основной акцент следует сделать на правильной передаче данных из Activity в рендерер и эффективном обновлении текстур. Регулярные оптимизации и минимизация создания объектов во время отрисовки обеспечат вашему приложению хорошую производительность. Если у вас возникнут дополнительные вопросы или необходимо более детальное объяснение определенных аспектов, не стесняйтесь задавать их.

Оцените материал
Добавить комментарий

Капча загружается...