Всем привет! В этом сообщении расскажу как прикрутить шейдер к конкретному спрайту. Мои предыдущие сообщения по теме шейдеров могли ввести в заблуждение тем, что процесс отрисовки результатов шейдера производился через специальную текстуру и 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