如何在Andengine中制作流水效果?

How to make water effect in Andengine?

萨拉姆
我想用 AndEngine 为 android 设计动态壁纸,
我想要水波纹效果。我怎样才能做到这一点? 喜欢 this.

这就是它在 GLES2 上的工作方式。 着色器延伸到整个屏幕。您需要针对自定义半径对其进行调整。

这段代码进入主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());                
        }
    };
}

这一行在 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);
    }
}

把这段代码放在你想触发它的地方:

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;
        }
    }
}));