Всем привет! В этом сообщении расскажу как прикрутить шейдер к конкретному спрайту. Мои предыдущие сообщения по теме шейдеров могли ввести в заблуждение тем, что процесс отрисовки результатов шейдера производился через специальную текстуру и UncoloredSprite. Все эти изыски изрядно сбивали с толку и меня,но время разобраться с этим вопросом появилось только сейчас (Maksim respect!) =)
В этом примере мы напишем активити которая отображает спрайт с примененным к нему шейдером размытия (Blur-эффект). Все тривиально:
В этом примере мы напишем активити которая отображает спрайт с примененным к нему шейдером размытия (Blur-эффект). Все тривиально:
package com.expedition107.shader.test; import java.io.IOException; import java.io.InputStream; import org.andengine.engine.Engine; import org.andengine.engine.LimitedFPSEngine; import org.andengine.engine.camera.Camera; import org.andengine.engine.options.EngineOptions; import org.andengine.engine.options.ScreenOrientation; import org.andengine.engine.options.resolutionpolicy.*; import org.andengine.entity.scene.Scene; import org.andengine.entity.scene.background.Background; import org.andengine.entity.sprite.Sprite; import org.andengine.entity.sprite.UncoloredSprite; import org.andengine.entity.util.FPSLogger; import org.andengine.opengl.shader.PositionTextureCoordinatesShaderProgram; import org.andengine.opengl.shader.ShaderProgram; import org.andengine.opengl.shader.exception.ShaderProgramException; import org.andengine.opengl.shader.exception.ShaderProgramLinkException; import org.andengine.opengl.shader.constants.ShaderProgramConstants; import org.andengine.opengl.texture.ITexture; import org.andengine.opengl.texture.PixelFormat; import org.andengine.opengl.texture.bitmap.BitmapTexture; import org.andengine.opengl.texture.region.ITextureRegion; import org.andengine.opengl.texture.region.TextureRegionFactory; import org.andengine.opengl.texture.render.RenderTexture; import org.andengine.opengl.util.GLState; import org.andengine.opengl.vbo.attribute.VertexBufferObjectAttributes; import org.andengine.ui.activity.BaseGameActivity; import org.andengine.util.adt.io.in.IInputStreamOpener; import org.andengine.util.color.Color; import android.opengl.GLES20; public class ShaderTestActivitySprite extends BaseGameActivity { public static final int WIDTH = 720; public static final int HEIGHT = 480; private Camera mCamera; private Sprite mSprite; private ITexture mTexture; private ITextureRegion mTextureRegion; @Override public EngineOptions onCreateEngineOptions() { this.mCamera = new Camera(0, 0, WIDTH, HEIGHT); final EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new RatioResolutionPolicy(WIDTH, HEIGHT), this.mCamera); return engineOptions; } @Override public Engine onCreateEngine(final EngineOptions pEngineOptions) { return new LimitedFPSEngine(pEngineOptions, 60); } @Override public void onCreateResources(OnCreateResourcesCallback pOnCreateResourcesCallback) throws Exception { try { this.mTexture = new BitmapTexture(this.getTextureManager(), new IInputStreamOpener() { @Override public InputStream open() throws IOException { return getAssets().open("gfx/sw_12_23.jpg"); } }); this.mTextureRegion = TextureRegionFactory.extractFromTexture(mTexture); this.mTexture.load(); } catch (IOException e) { } this.getShaderProgramManager().loadShaderProgram(GaussianBlurPass1ShaderProgram.getInstance()); pOnCreateResourcesCallback.onCreateResourcesFinished(); } @Override public void onCreateScene(OnCreateSceneCallback pOnCreateSceneCallback) throws Exception { this.mEngine.registerUpdateHandler(new FPSLogger()); final Scene scene = new Scene(); scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f)); getEngine().registerUpdateHandler(new FPSLogger()); pOnCreateSceneCallback.onCreateSceneFinished(scene); } @Override public void onPopulateScene(Scene pScene, OnPopulateSceneCallback pOnPopulateSceneCallback) throws Exception { this.mSprite = new Sprite(234f, 40f, this.mTextureRegion, getVertexBufferObjectManager()) { @Override protected void onManagedUpdate(float pSecondsElapsed) { this.setRotation(this.getRotation()+1); super.onManagedUpdate(pSecondsElapsed); } }; Sprite sprite = new Sprite(0, 0, 200,200, this.mTextureRegion, getVertexBufferObjectManager()); mSprite.setShaderProgram(GaussianBlurPass1ShaderProgram.getInstance()); pScene.attachChild(mSprite); pScene.attachChild(sprite); pOnPopulateSceneCallback.onPopulateSceneFinished(); } public static class GaussianBlurPass1ShaderProgram extends ShaderProgram { private static GaussianBlurPass1ShaderProgram instance; public static GaussianBlurPass1ShaderProgram getInstance() { if (instance == null) instance = new GaussianBlurPass1ShaderProgram(); return instance; } public static final String FRAGMENTSHADER = "precision lowp float;\n" + "uniform sampler2D " + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ";\n" + "varying mediump vec2 " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ";\n" + "const float blurSize = 2.0/" + (WIDTH-1) + ".0; \n" + "void main() \n" + "{ \n" + " vec4 sum = vec4(0.0); \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x - 4.0*blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.05; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x - 3.0*blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.09; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x - 2.0*blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.12; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x - blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.15; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.16; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x + blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.15; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x + 2.0*blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.12; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x + 3.0*blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.09; \n" + " sum += texture2D(" + ShaderProgramConstants.UNIFORM_TEXTURE_0 + ", vec2(" + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".x + 4.0*blurSize, " + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ".y)) * 0.05; \n" + " gl_FragColor = sum; \n" + "} \n"; private GaussianBlurPass1ShaderProgram() { super(PositionTextureCoordinatesShaderProgram.VERTEXSHADER, FRAGMENTSHADER); } public static int sUniformModelViewPositionMatrixLocation = ShaderProgramConstants.LOCATION_INVALID; public static int sUniformTexture0Location = 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); GaussianBlurPass1ShaderProgram.sUniformModelViewPositionMatrixLocation = this.getUniformLocation(ShaderProgramConstants.UNIFORM_MODELVIEWPROJECTIONMATRIX); GaussianBlurPass1ShaderProgram.sUniformTexture0Location = this.getUniformLocation(ShaderProgramConstants.UNIFORM_TEXTURE_0); } @Override public void bind(final GLState pGLState, final VertexBufferObjectAttributes pVertexBufferObjectAttributes) { GLES20.glDisableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION); super.bind(pGLState, pVertexBufferObjectAttributes); GLES20.glUniformMatrix4fv(GaussianBlurPass1ShaderProgram.sUniformModelViewPositionMatrixLocation, 1, false, pGLState.getModelViewProjectionGLMatrix(), 0); GLES20.glUniform1i(GaussianBlurPass1ShaderProgram.sUniformTexture0Location, 0); } @Override public void unbind(final GLState pGLState) throws ShaderProgramException { GLES20.glEnableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION); super.unbind(pGLState); } } }
Ну вот, собственно и все. Подводя итог, можно сказать следующее: убрали лишнее и применили шейдер к конкретному спрайту методом: Sprite.setShaderProgram(...) в который и предаем нашу программу шейдера. Для наглядности, я добавил еще один спрайт на сцену с той же текстурой и задал вращение спрайту к которому применен шейдер. Надеюсь кому-то данная информация пригодится. Спасибо!
Продвинутый пример смотрите в статье "Немного о Render-to-Texture..."
Проект на GitHub