Здесь я выложу код модифицированного шейдера под названием Metablob (автор Adrian Boeing). Исходник взят от сюда Shader Toy.
Как, что и почему я тут расписывать не буду. Более детальное прикручивание шейдера к Android можно посмотреть в моем сообщении "Пример шейдера на Android. Ripple Effect.". Здесь будет интересен уже результат шейдера.
Выглядит он *примерно* так, плюс оно шевелиться всячески =):
Изменению подверглись количество и радиусы сгустков (свой! уникальный! аррргххх....=( ).
А вот и код активити:
Копируйте, пользуйтесь, не забывайте про Adrian-а Boeing-а! :-)
Продвинутый пример смотрите в статье "Немного о Render-to-Texture..."
Скачать исходник с апк внутри.
Как, что и почему я тут расписывать не буду. Более детальное прикручивание шейдера к Android можно посмотреть в моем сообщении "Пример шейдера на Android. Ripple Effect.". Здесь будет интересен уже результат шейдера.
Выглядит он *примерно* так, плюс оно шевелиться всячески =):
Изменению подверглись количество и радиусы сгустков (свой! уникальный! аррргххх....=( ).
А вот и код активити:
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.handler.IUpdateHandler; import org.andengine.engine.options.EngineOptions; import org.andengine.engine.options.ScreenOrientation; import org.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy; import org.andengine.entity.scene.IOnAreaTouchListener; import org.andengine.entity.scene.IOnSceneTouchListener; import org.andengine.entity.scene.ITouchArea; 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.input.touch.TouchEvent; import org.andengine.input.touch.detector.ClickDetector; import org.andengine.input.touch.detector.ClickDetector.IClickDetectorListener; import org.andengine.opengl.shader.PositionTextureCoordinatesShaderProgram; import org.andengine.opengl.shader.ShaderProgram; import org.andengine.opengl.shader.constants.ShaderProgramConstants; import org.andengine.opengl.shader.exception.ShaderProgramException; import org.andengine.opengl.shader.exception.ShaderProgramLinkException; 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 com.expedition107.shader.test.ShockwaveTest.ShockwaveShaderProgram; import android.opengl.GLES20; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; public class MetablobTest extends BaseGameActivity implements IOnSceneTouchListener { public static int WIDTH = 800; public static int HEIGHT = 480; protected static MetablobTest Instance; private Camera mCamera; private Sprite mSprite; private ITexture mTexture; private ITextureRegion mTextureRegion; private boolean mRenderTextureInitialized = false; private RenderTexture mRenderTexture; private Sprite mRenderTextureSprite; private float mShockwaveTime = 0f; private float mCenterX = 0.5f; private float mCenterY= 0.5f; @Override public EngineOptions onCreateEngineOptions() { DisplayMetrics displayMetrics = new DisplayMetrics(); WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(displayMetrics); wm.getDefaultDisplay().getRotation(); WIDTH = displayMetrics.widthPixels; HEIGHT = displayMetrics.heightPixels; 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, 120) { @Override public void onDrawFrame(GLState pGLState) throws InterruptedException { if (!mRenderTextureInitialized) { initRenderTexture(pGLState); mRenderTextureInitialized = true; } mRenderTexture.begin(pGLState, false, true, Color.TRANSPARENT); { super.onDrawFrame(pGLState); } mRenderTexture.end(pGLState); pGLState.pushProjectionGLMatrix(); pGLState.orthoProjectionGLMatrixf(0, mCamera.getSurfaceWidth(), 0, mCamera.getSurfaceHeight(), -1, 1); { mRenderTextureSprite.onDraw(pGLState, mCamera); } pGLState.popProjectionGLMatrix(); } private void initRenderTexture(GLState pGLState) { mRenderTexture = new RenderTexture(this.getTextureManager(),mCamera.getSurfaceWidth(), mCamera.getSurfaceHeight(), PixelFormat.RGBA_8888); mRenderTexture.init(pGLState); mRenderTextureSprite = new UncoloredSprite(0f, 0f, TextureRegionFactory.extractFromTexture(mRenderTexture), getVertexBufferObjectManager()); mRenderTextureSprite.setShaderProgram(ShockwaveShaderProgram.getInstance()); } }; } @Override public void onCreateResources(OnCreateResourcesCallback pOnCreateResourcesCallback) throws Exception { Instance = this; try { this.mTexture = new BitmapTexture(this.getTextureManager(), new IInputStreamOpener() { @Override public InputStream open() throws IOException { // TODO Auto-generated method stub return getAssets().open("gfx/sw_12_23.jpg"); } }); this.mTextureRegion = TextureRegionFactory.extractFromTexture(mTexture); this.mTexture.load(); } catch (IOException e) { } this.getShaderProgramManager().loadShaderProgram(ShockwaveShaderProgram.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)); this.mSprite = new Sprite(0f, 0f,40,40, this.mTextureRegion, getVertexBufferObjectManager()); scene.attachChild(mSprite); getEngine().registerUpdateHandler(new IUpdateHandler() { @Override public void reset() { } @Override public void onUpdate(float pSecondsElapsed) { mShockwaveTime += pSecondsElapsed*0.5; } }); scene.setOnSceneTouchListener(this); pOnCreateSceneCallback.onCreateSceneFinished(scene); } @Override public void onPopulateScene(Scene pScene, OnPopulateSceneCallback pOnPopulateSceneCallback) throws Exception { pOnPopulateSceneCallback.onPopulateSceneFinished(); } 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 highp float;\n" + "uniform float time;\n" + "uniform vec2 resolution;\n" + "void main() \n" + "{\n" + //центры сгустков " vec2 move1;\n" + " move1.x = cos(time)*0.4;\n" + " move1.y = sin(time*1.5)*0.4;\n" + " vec2 move2;\n" + " move2.x = cos(time*2.0)*0.6;\n" + " move2.y = sin(time*3.0)*0.6;\n" + " vec2 move3;\n" + " move3.x = sin(time*2.0)*0.8;\n" + " move3.y = cos(time*3.0)*0.8;\n" + " vec2 move4;\n" + " move4.x = cos(time*1.8)*0.2;\n" + " move4.y = sin(time*3.0)*0.2;\n" + " vec2 move5;\n" + " move5.x = cos(time*4.8)*1.2;\n" + " move5.y = sin(time*1.0)*0.5;\n" + " vec2 move6;\n" + " move6.x = sin(time*1.8)*0.2;\n" + " move6.y = cos(time*1.1)*0.2;\n" + " vec2 move7;\n" + " move7.x = cos(time*1.8)*0.2;\n" + " move7.y = sin(time*3.0)*0.2;\n" + " vec2 move8;\n" + " move8.x = sin(time*0.8)*0.2;\n" + " move8.y = cos(time*0.3)*0.5;\n" + " vec2 move9;\n" + " move9.x = cos(time*1.8)*0.9;\n" + " move9.y = sin(time)*5.4;\n" + //координаты " vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;\n" + //радиусы " float r1 =(dot(p-move1,p-move1))*16.0;\n" + " float r2 =(dot(p+move2,p+move2))*24.0;\n" + " float r3 =(dot(p+move3,p+move3))*32.0;\n" + " float r4 =(dot(p+move4,p+move4))*40.0;\n" + " float r5 =(dot(p+move5,p+move5))*42.0;\n" + " float r6 =(dot(p+move6,p+move6))*50.0;\n" + " float r7 =(dot(p+move7,p+move7))*56.0;\n" + " float r8 =(dot(p+move8,p+move8))*65.0;\n" + " float r9 =(dot(p+move9,p+move9))*70.0;\n" + //сумма сгустков " float metaball =(1.0/r1-1.0/r2+1.0/r3-1.0/r4+1.0/r5-1.0/r6+1.0/r7-1.0/r8+1.0/r9);\n" + //места разрыва сгустков " float col = pow(metaball,8.0);\n" + //вывод конечного цвета фрагмента " gl_FragColor = vec4(col+move2.x,col+move1.x,col+move4.y,0.8);\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 sUniformTimeLocation = ShaderProgramConstants.LOCATION_INVALID; public static int sUniformResolutionLocation = 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.sUniformResolutionLocation = this.getUniformLocation("resolution"); 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.glUniform2f(ShockwaveShaderProgram.sUniformResolutionLocation, WIDTH, HEIGHT); GLES20.glUniform1f(ShockwaveShaderProgram.sUniformTimeLocation, MetablobTest.Instance.mShockwaveTime); } @Override public void unbind(final GLState pGLState) throws ShaderProgramException { GLES20.glEnableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION); super.unbind(pGLState); } } @Override public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) { if (pSceneTouchEvent.getAction() == TouchEvent.ACTION_DOWN){ mCenterX = pSceneTouchEvent.getMotionEvent().getX() / this.mCamera.getSurfaceWidth(); mCenterY = 1-pSceneTouchEvent.getMotionEvent().getY() / this.mCamera.getSurfaceHeight(); return true; } return false; } }
Копируйте, пользуйтесь, не забывайте про Adrian-а Boeing-а! :-)
Продвинутый пример смотрите в статье "Немного о Render-to-Texture..."
Скачать исходник с апк внутри.
Спасибо за пример, Алексей!
ОтветитьУдалитьтолько не совсем понятно чем управляют переменные mCenterX, mCenterY - они совершенно бесполезны здесь...
по идее их ведь куда-то в шейдере прибиндить надо чтобы управление по тачу происходило?
Да действительно, эти переменные здесь не используются, они остались от моих экспериментов. Чтобы их использовать, в шейдере нужно определить униформы для них и связать. Ну и конечно, в коде шейдера эти переменные должны как-то влиять на происходящее, иначе так в них смысла и не будет).
ОтветитьУдалитьРаз так то понятно. Я не мог определить на что бы они могли влиять в этом шейдере и что тут по тачу вообще меняться может. А вот в предыдущем шейдере им место найдётся.
УдалитьСпасибо еще раз!