Android OpenGL Live Wallpaper 创建两个渲染器

Android OpenGL Live Wallpaper creating two renderers

我有一个具有视差效果的动态壁纸。我的代码基于 the openGL Tutorials.

它大部分时间都在工作,除了在壁纸已设置为壁纸的情况下进行预览。在我这样做之后,我的 onOffsetsChanged() 方法不断被调用,但我的渲染器 class 中的相机偏移值没有改变。这意味着我的视差墙纸变成静态的。一旦我设置不同的墙纸然后切换回此墙纸,视差效果就会再次起作用。

更新:因此,看起来正在绘制的渲染器和接收 onOffsetsChanged 调用的渲染器是两个独立的渲染器。有谁知道为什么在绘制旧的时新的由 WallpaperService 控制,或者如何解决这个问题?

GLWallpaperService:

public abstract class GLWallpaperService extends WallpaperService{
public class GLEngine extends Engine{
    class WallpaperGLSurfaceView extends GLSurfaceView {
        private static final String TAG = "WallpaperGLSurfaceView";

        WallpaperGLSurfaceView(Context context)
        {
            super(context);
        }

        public SurfaceHolder getHolder()
        {
            return getSurfaceHolder();
        }

        public void onDestroy()
        {
            super.onDetachedFromWindow();
        }
    }

  private WallpaperGLSurfaceView glSurfaceView;
  private boolean rendererHasBeenSet;

  @Override
  public void onCreate(SurfaceHolder surfaceHolder)
  {
      super.onCreate(surfaceHolder);

      glSurfaceView = new WallpaperGLSurfaceView(GLWallpaperService.this);

      Log.d("onCreate", "was called");
  }

  @Override
  public void onVisibilityChanged(boolean visible)
  {
      super.onVisibilityChanged(visible);
      if(rendererHasBeenSet)
      {
          if (visible)
          {
              glSurfaceView.onResume();
              //glSurfaceView.requestRender();
              Log.d("onResume", "was called");
          } else
          {
              glSurfaceView.onPause();
              Log.d("onPause", "was called");
          }
      }
  }

  @Override
  public void onDestroy()
  {
      super.onDestroy();
      glSurfaceView.onDestroy();
      Log.d("onDestroy", "was called");
  }

  protected void setRenderer(GLSurfaceView.Renderer renderer)
  {
      glSurfaceView.setRenderer(renderer);
      rendererHasBeenSet = true;
      Log.d("setRenderer", "was called");
  }

  protected void setPreserveEGLContextOnPause(boolean preserve)
  {
      if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
      {
          glSurfaceView.setPreserveEGLContextOnPause(preserve);
      }
  }

  protected void setEGLContextClientVersion(int version)
  {
      glSurfaceView.setEGLContextClientVersion(version);
  }
    }
  }

OpenGLES2WallpaperService:

    public class OpenGLES2WallpaperService extends GLWallpaperService
{

    //set up our main_preferences
    SharedPreferences preferences;
    GLRenderer renderer;
    private SharedPreferences.OnSharedPreferenceChangeListener prefListener;

    @Override
    public Engine onCreateEngine()
    {
        Log.d("GLES2 onCreateEngine", "engine was created");
        return new OpenGLES2Engine();
    }

    class OpenGLES2Engine extends GLWallpaperService.GLEngine
    {
        @Override
        public void onCreate(SurfaceHolder surfaceHolder)
        {
            super.onCreate(surfaceHolder);
            Log.d("GLES2 onCreate", "surface was created");

            preferences = PreferenceManager.getDefaultSharedPreferences(OpenGLES2WallpaperService.this);

            final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
            final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;

            if (supportsEs2)
            {
                setEGLContextClientVersion(2);

                setPreserveEGLContextOnPause(true);

                renderer = new GLRenderer(OpenGLES2WallpaperService.this);

                setRenderer(renderer);

                //set up preference listener

                final SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(OpenGLES2WallpaperService.this);
                prefListener = new SharedPreferences.OnSharedPreferenceChangeListener()
                {
                    @Override
                    public void onSharedPreferenceChanged(SharedPreferences mprefs, String key) {
                        if(mPrefs.getBoolean("activate_sunset", true))
                        {
                            renderer.changeColor(1);
                        }
                        else
                        {
                            renderer.changeColor(0);
                        }

                    }
                };
                mPrefs.registerOnSharedPreferenceChangeListener(prefListener);
            }
        }

        @Override
        public void onOffsetsChanged(float xOffset, float yOffset, float xStep,
                                     float yStep, int xPixels, int yPixels)
        {
            renderer.setEyeX(xOffset);
//            Log.d("onOffsetsChanged", "was called");
        }

        //set up gesture detection
        private android.view.GestureDetector.OnGestureListener gestureListener = new android.view.GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }

            @Override
            public void onShowPress(MotionEvent e) {
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
//                if(preferences.getBoolean("pref_key_sim_scroll", true))
//                    renderer.setEyeX( e1.getX() - e2.getX());
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                return false;
            }
        };
        GestureDetector mGestureDetector = new GestureDetector(OpenGLES2WallpaperService.this, gestureListener);

        @Override
        public void onTouchEvent(MotionEvent event)
        {
            mGestureDetector.onTouchEvent(event);
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)
        {
            super.onSurfaceChanged( holder,  format,  width,  height);
            Log.d("GLES2 onSurfaceChanged", "the surface was changed");
        }

        @Override
        public void onSurfaceRedrawNeeded(SurfaceHolder holder)
        {
            super.onSurfaceRedrawNeeded(holder);
            Log.d("GLES2 RedrawNeeded", "the surface was redrawn");

        }

    }

    GLSurfaceView.Renderer getNewRenderer()
    {
        return renderer = new GLRenderer(OpenGLES2WallpaperService.this);
    }
}

渲染器class(现在有点乱):

public class GLRenderer implements Renderer {

    // Our matrices
    private final float[] mtrxProjection = new float[16];
    private final float[] mtrxView = new float[16];
    private final float[] mMVPMatrix = new float[16];
    private float[] mModelMatrix = new float[16];
    private int mMVPMatrixHandle;

    // Geometric variables
    public static float vertices[];
    public static short indices[];
    public static float uvs[];
    public FloatBuffer vertexBuffer;
    public ShortBuffer drawListBuffer;
    public FloatBuffer uvBuffer;

    // Our screenresolution
    float   mScreenWidth = 1280;
    float   mScreenHeight = 768;

    // Misc
    Context mContext;
    long mLastTime;
    int mProgram;

    //set up our main_preferences
    SharedPreferences preferences;

    //set up array database
    ArrayHolder arrayHolder = new ArrayHolder();

//  Square square, square1;
    Sprite background, mountains, forest, person;
    float offsetDifference = 1;
//  Background background;

    public void setEyeX(float offset)
    {
            eyeX = -offset * offsetDifference;
            lookX = eyeX;
//      Log.d("setEyeX", "eyeX: " + eyeX);
    }

    public GLRenderer(Context c)
    {
        mContext = c;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        //Load in Preferences
        preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        // Generate Textures, if more needed, alter these numbers.
        int[] textureNames = new int[4];
        GLES20.glGenTextures(4, textureNames, 0);

        //set the scene color from preferences
        float[] sceneColor;
        if(preferences.getBoolean("activate_sunset", false))
        {
            sceneColor = arrayHolder.sunsetColor;
        }
        else
        {
            sceneColor = arrayHolder.normalColor;
        }

        //create the sprites
        person = new Sprite(arrayHolder.vertices1, sceneColor);
        forest = new Sprite(arrayHolder.vertices2, sceneColor);
        mountains = new Sprite(arrayHolder.vertices3, sceneColor);
        background = new Sprite(arrayHolder.vertices4, sceneColor);

        // Set the clear color to white
        GLES20.glClearColor(0.9f, 0.9f, 0.9f, 0);

        // Create the shaders, solid color
        int vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_SolidColor);
        int fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_SolidColor);

        riGraphicTools.sp_SolidColor = GLES20.glCreateProgram();             // create empty OpenGL ES Program
        GLES20.glAttachShader(riGraphicTools.sp_SolidColor, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(riGraphicTools.sp_SolidColor, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(riGraphicTools.sp_SolidColor);                  // creates OpenGL ES program executables

        // Create the shaders, images
        vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_Image);
        fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_Image);

        riGraphicTools.sp_Image = GLES20.glCreateProgram();             // create empty OpenGL ES Program
        GLES20.glAttachShader(riGraphicTools.sp_Image, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(riGraphicTools.sp_Image, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(riGraphicTools.sp_Image);                  // creates OpenGL ES program executables

        // Set our shader program
        GLES20.glUseProgram(riGraphicTools.sp_Image);

        setupImages();
    }

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

        // We need to know the current width and height.
        mScreenWidth = width;
        mScreenHeight = height;
        GLES20.glViewport(0, 0, width, height);


        float ratio;
        if(height > width)
        {
            ratio = (float) width / height;
            Matrix.frustumM(mtrxProjection,0, -ratio, ratio, -1, 1, 3, 7);
            offsetDifference = 1;
        }
        else
        {
            ratio = (float) height / width;
            Matrix.frustumM(mtrxProjection,0, -1, 1, -ratio, ratio,  3, 7);
            offsetDifference = 0.5f;
        }

    }

    // Position the eye in front of the origin.
    float eyeX = 0.0f;
    float eyeY = 0.0f;
    float eyeZ = -4.0f;
    // We are looking toward the distance
    float lookX = 0.0f;
    float lookY = 0.0f;
    float lookZ = 0.0f;
    // Set our up vector. This is where our head would be pointing were we holding the camera.
    float upX = 0.0f;
    float upY = 1.0f;
    float upZ = 0.0f;

    boolean colorIsRed;

    @Override
    public void onDrawFrame(GL10 unused) {

        // Set the camera position (View matrix)
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_MVPMatrix");

//      Log.d("onDrawFrame", "eyeX: " + eyeX);
        Matrix.setLookAtM(mtrxView, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);


        // Calculate the projection and view transformation
//      Matrix.multiplyMM(mMVPMatrix, 0, mtrxProjection, 0, mtrxView, 0);
        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.translateM(mModelMatrix, 0, eyeX, 0.0f, 1.0f);
        Matrix.multiplyMM(mMVPMatrix, 0, mtrxView, 0, mModelMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mtrxProjection, 0, mMVPMatrix, 0);
        background.draw(mMVPMatrix, uvBuffer, 0);

        float[] scratch2 = new float[16];
////        Matrix.multiplyMM(scratch, 0, mtrxProjection, 0, mtrxView, 0);
        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.translateM(mModelMatrix, 0, eyeX * 0.9f, 0.0f, 1.0f);
        Matrix.multiplyMM(scratch2, 0, mtrxView, 0, mModelMatrix, 0);
        Matrix.multiplyMM(scratch2, 0, mtrxProjection, 0, scratch2, 0);
        mountains.draw(scratch2, uvBuffer, 3);


        float[] scratch1 = new float[16];
        Matrix.setIdentityM(mModelMatrix, 0);
        Matrix.translateM(mModelMatrix, 0, eyeX * 0.5f, 0.0f, 1.0f);
        Matrix.multiplyMM(scratch1, 0, mtrxView, 0, mModelMatrix, 0);
        Matrix.multiplyMM(scratch1, 0, mtrxProjection, 0, scratch1, 0);
        forest.draw(scratch1, uvBuffer, 1);


        if(!preferences.getBoolean("pref_key_remove_layer", true))
        {
            float[] scratch = new float[16];
////        Matrix.multiplyMM(scratch, 0, mtrxProjection, 0, mtrxView, 0);
            Matrix.setIdentityM(mModelMatrix, 0);
            Matrix.translateM(mModelMatrix, 0, -0.5f, 0.3f, 1.0f);
            Matrix.multiplyMM(scratch, 0, mtrxView, 0, mModelMatrix, 0);
            Matrix.multiplyMM(scratch, 0, mtrxProjection, 0, scratch, 0);
            person.draw(scratch, uvBuffer, 2);
        }
    }

    public void changeColor(int colorCode)
    {
        float[] newColor = arrayHolder.normalColor;
        switch(colorCode){
            case 0: newColor = arrayHolder.normalColor;
                break;
            case 1: newColor = arrayHolder.sunsetColor;
                break;
        }

        person.changeColor(newColor);
        forest.changeColor(newColor);
        mountains.changeColor(newColor);
        background.changeColor(newColor);
    }

    private void setupImages()
    {
        // Create our UV coordinates.
        uvs = new float[] {
                0.0f, 0.0f,
                0.0f, 1.0f,
                1.0f, 1.0f,
                1.0f, 0.0f
        };

        // The texture buffer
        ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
        bb.order(ByteOrder.nativeOrder());
        uvBuffer = bb.asFloatBuffer();
        uvBuffer.put(uvs);
        uvBuffer.position(0);

        // Generate Textures, if more needed, alter these numbers.
        int[] texturenames = new int[4];
        GLES20.glGenTextures(4, texturenames, 0);


        // Temporary create a bitmap
        Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.squaresky);
        // Bind texture to texturename
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texturenames[0]);
        // Set filtering
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        // Load the bitmap into the bound texture.
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);

        bmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.squareground);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texturenames[1]);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);

        bmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.squareperson);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texturenames[2]);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);

        bmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.squaremountains);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE3);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texturenames[3]);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);

        bmp.recycle();
    }
}

看来我需要为新的 Surface 视图设置正确的渲染器。我通过将 GLRenderer 渲染器移动到 GLWallpaperService 并将 setRenderer() 更改为此:

      protected void setRenderer(GLSurfaceView.Renderer renderer)
  {
      this.renderer = (GLRenderer)renderer; //this line was missing
      glSurfaceView.setRenderer(renderer);
      rendererHasBeenSet = true;
  }