How to make water effect in Andengine?

349 views Asked by At

Salaam
I want to design live wallpaper for android with AndEngine,
I want to have water ripple effect. How can I do this? Like this.

1

There are 1 answers

1
Biswajit Karmakar On BEST ANSWER

This is how it works on GLES2. The shader extends through whole screen. You need to tweak it for custom radius.

This code goes into main class/activity:

private float mShockwaveTime = 0f;


@Override
public Engine onCreateEngine(final EngineOptions pEngineOptions) {
    return new Engine(pEngineOptions) {
        private boolean mRenderTextureInitialized;

        private RenderTexture mRenderTexture;
        private UncoloredSprite mRenderTextureSprite;

        @Override
        public void onDrawFrame(GLState pGLState)
                throws InterruptedException {

            if (mShockwaveTime > 0f && mShockwaveTime < 10f) {

                if (!mRenderTextureInitialized) {
                    initRenderTexture(pGLState);
                    mRenderTextureInitialized = true;
                }

                mRenderTexture.begin(pGLState, false, true, Color.TRANSPARENT);
                {       
                    super.onDrawFrame(pGLState);
                }
                mRenderTexture.end(pGLState);

                pGLState.pushProjectionGLMatrix();
                pGLState.orthoProjectionGLMatrixf(0, CAMERA_WIDTH, 0, CAMERA_HEIGHT, -1, 1);
                {
                    mRenderTextureSprite.onDraw(pGLState, mCamera);
                }
                pGLState.popProjectionGLMatrix();   
            } else {
                super.onDrawFrame(pGLState);
            }
        }

        private void initRenderTexture(GLState pGLState) {
            mRenderTexture = new RenderTexture(getTextureManager(), mCamera.getSurfaceWidth(), mCamera.getSurfaceHeight(), PixelFormat.RGBA_4444);
            mRenderTexture.init(pGLState);
            mRenderTextureSprite = new UncoloredSprite(0f, 0f, TextureRegionFactory.extractFromTexture(mRenderTexture), getVertexBufferObjectManager()) {
                @Override
                protected void preDraw(GLState pGLState, Camera pCamera) {
                    super.preDraw(pGLState, pCamera);
                    if (mShockwaveTime > 0f && mShockwaveTime < 10f) GLES20.glUniform1f(ShockwaveShaderProgram.sUniformTimeLocation, mShockwaveTime);
                }
            };
            mRenderTextureSprite.setShaderProgram(ShockwaveShaderProgram.getInstance());                
        }
    };
}

This line goes inside onLoadResources:

    this.getShaderProgramManager().loadShaderProgram(ShockwaveShaderProgram.getInstance());

I put shader class code to the end of my main class:

public static class ShockwaveShaderProgram extends ShaderProgram {

    private static ShockwaveShaderProgram instance;

    public static ShockwaveShaderProgram getInstance() {
        if (instance == null) instance = new ShockwaveShaderProgram();
        return instance;
    }

    public static final String FRAGMENTSHADER = 
    "precision lowp float;\n" +

    "uniform lowp sampler2D " + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ";\n" +
    "varying mediump vec2 " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ";\n" +

    "uniform vec2 center;\n" +
    "uniform float time;\n" +
    "const vec3 params = vec3(10.0, 0.8, 0.02);\n" +

    "void main()    \n" +
    "{              \n" +
    "   mediump vec2 texCoord = " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ";\n" +
    "   float distance = distance(texCoord, center);\n" +
    "   if ( (distance <= (time + params.z)) && (distance >= (time - params.z)) )\n" +
    "   {\n" +      
    "       float diff = (distance - time);\n" +
    "       float powDiff = 1.0 - pow(abs(diff*params.x), params.y);\n" +
    "       float diffTime = diff  * powDiff;\n" +
    "       vec2 diffUV = normalize(texCoord - center);\n" +
    "       texCoord = texCoord + (diffUV * diffTime);\n" +
    "   }\n" +
    "   gl_FragColor = texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", texCoord);\n" +
    "}      \n";


    private ShockwaveShaderProgram() {
        super(PositionTextureCoordinatesShaderProgram.VERTEXSHADER, FRAGMENTSHADER);
    }

    public static int sUniformModelViewPositionMatrixLocation = ShaderProgramConstants.LOCATION_INVALID;
    public static int sUniformTexture0Location = ShaderProgramConstants.LOCATION_INVALID;
    public static int sUniformCenterLocation = ShaderProgramConstants.LOCATION_INVALID;
    public static int sUniformTimeLocation = ShaderProgramConstants.LOCATION_INVALID;

    @Override
    protected void link(final GLState pGLState) throws ShaderProgramLinkException {
        GLES20.glBindAttribLocation(this.mProgramID, ShaderProgramConstants.ATTRIBUTE_POSITION_LOCATION, ShaderProgramConstants.ATTRIBUTE_POSITION);
        GLES20.glBindAttribLocation(this.mProgramID, ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES_LOCATION, ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES);

        super.link(pGLState);

        ShockwaveShaderProgram.sUniformModelViewPositionMatrixLocation = this.getUniformLocation(ShaderProgramConstants.UNIFORM_MODELVIEWPROJECTIONMATRIX);
        ShockwaveShaderProgram.sUniformTexture0Location = this.getUniformLocation(ShaderProgramConstants.UNIFORM_TEXTURE_0);
        ShockwaveShaderProgram.sUniformCenterLocation = this.getUniformLocation("center");
        ShockwaveShaderProgram.sUniformTimeLocation = this.getUniformLocation("time");
    }

    @Override
    public void bind(final GLState pGLState, final VertexBufferObjectAttributes pVertexBufferObjectAttributes) {
        GLES20.glDisableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION);
        super.bind(pGLState, pVertexBufferObjectAttributes);
        GLES20.glUniformMatrix4fv(ShockwaveShaderProgram.sUniformModelViewPositionMatrixLocation, 1, false, pGLState.getModelViewProjectionGLMatrix(), 0);
        GLES20.glUniform1i(ShockwaveShaderProgram.sUniformTexture0Location, 0);
        GLES20.glUniform2f(ShockwaveShaderProgram.sUniformCenterLocation, 0.5f, 0.5f);
    }


    @Override
    public void unbind(final GLState pGLState) throws ShaderProgramException {
        GLES20.glEnableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION);
        super.unbind(pGLState);
    }
}

Put this code where you want to trigger it:

mScene.registerUpdateHandler(new TimerHandler(0.08f, true, new ITimerCallback() {
    @Override
    public void onTimePassed(TimerHandler pTimerHandler) {
        mShockwaveTime += 0.02f;
        if (mShockwaveTime > 1.2f) {
            mScene.unregisterUpdateHandler(pTimerHandler);
            mShockwaveTime = 0.0f;
        }
    }
}));