libgdx open gles 2.0 шаблон алфа маскиране

Търся решение за прилагане на алфа маскиране с шаблонен буфер в libgdx с отворен gles 2.0.

Успях да внедря просто алфа маскиране с шаблонен буфер и шейдъри, където, ако алфа каналът на фрагмента е по-голям, тогава определена стойност се отхвърля. Това работи добре.

Проблемът е, че когато искам да използвам някаква градиентна маска на изображението или фетерирана png маска, не получавам това, което исках (получавам „запълнена“ правоъгълна маска без алфа канал), вместо това искам маска за плавно избледняване.

Знам, че проблемът е, че в буфера на шаблона има само 0 и 1, но искам да запиша в шаблона някои други стойности, които представляват действителната алфа стойност на фрагмента, преминал във фрагментния шейдър, и да използвам тази стойност от шаблона по някакъв начин направете малко смесване. Надявам се, че съм обяснил какво искам да получа, всъщност дали е възможно.

Наскоро започнах да играя с OpenGL ES, така че все още имам някои недоразумения.

Въпросът ми е: Как да настроя и шаблонния буфер да съхранява стойности, различни от 0 и 1, и как да използвам тези стойности по-късно за алфа маскиране?

Tnx предварително.

Това в момента е моята настройка на шаблона:

    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_STENCIL_BUFFER_BIT |   GL20.GL_DEPTH_BUFFER_BIT);

    // setup drawing to stencil buffer
    Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
    Gdx.gl20.glStencilFunc(GL20.GL_ALWAYS, 0x1, 0xffffffff);
    Gdx.gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
    Gdx.gl20.glColorMask(false, false, false, false);
    Gdx.gl20.glDepthMask(false);

    spriteBatch.setShader(shaderStencilMask);
    spriteBatch.begin();

    // push to the batch
    spriteBatch.draw(Assets.instance.actor1, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, Assets.instance.actor1.getRegionWidth(), Assets.instance.actor1.getRegionHeight());

    spriteBatch.end();

    // fix stencil buffer, enable color buffer
    Gdx.gl20.glColorMask(true, true, true, true);
    Gdx.gl20.glDepthMask(true);
    Gdx.gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);

    // draw where pattern has NOT been drawn
    Gdx.gl20.glStencilFunc(GL20.GL_EQUAL, 0x1, 0xff);

    decalBatch.add(decal);
    decalBatch.flush();

    Gdx.gl20.glDisable(GL20.GL_STENCIL_TEST);

    decalBatch.add(decal2);
    decalBatch.flush();

person Veljko    schedule 18.07.2014    source източник
comment
Шаблонът не позволява частични алфа стойности. За да получите междинни стойности, мисля, че ще трябва да нарисувате вашите неща във FrameBuffer и след това да рисувате на екрана с мулти-текстуриране, като използвате цветната текстура на FrameBuffer и текстура, представляваща алфа. Но може да има по-просто решение в зависимост от вашите специфични нужди.   -  person Tenfour04    schedule 18.07.2014
comment
Опитвам се да маскирам 3d равнини, например една равнина представлява маска, а друга равнина е зад равнината на маската. Искам зад равнината да стане прозрачна, където текстурата на маската е прозрачна. В момента използвам Decals в Libgdx. Възможно ли е дори без спад на fps?   -  person Veljko    schedule 18.07.2014
comment
Под равнини имате предвид правоъгълници в 3D пространство? Ако двата правоъгълника са напълно еднакви по размер, ориентация и позиция, тогава можете просто да го направите с мулти-текстуриране, въпреки че това ще изисква малко работа с вашите ваденки.   -  person Tenfour04    schedule 18.07.2014
comment
Да, самолетите са основно правоъгълници в 3D пространството. Маската трябва да е статична, а самолетът зад маската може да се движи. И припокриващите се части трябва да бъдат маскирани.   -  person Veljko    schedule 18.07.2014
comment
Мога да мисля само за начини, които включват FrameBuffers. Дали влияе на FPS зависи от това какво е вашето тясно място.   -  person Tenfour04    schedule 18.07.2014
comment
Мисля, че изобразяването на буфера в реално време и актуализирането на текстурата на 3D равнина може да е тясното гърло. Какво мислиш?   -  person Veljko    schedule 18.07.2014
comment
Няма начин да предвидите, без да видите целия си проект. Например, ако имате 10 000 ваденки на екрана, тогава пречка ще бъде по-вероятно натоварването на процесора при прехвърляне на върховете или преначертаване на тези смесени декали, а не изчертаване на FBO.   -  person Tenfour04    schedule 18.07.2014
comment
Добре. Ще опитам по този начин... и ще видя какво ще стане. Tnx за съвет.   -  person Veljko    schedule 18.07.2014


Отговори (1)


Единствените начини, по които се сещам да направя това, са с FrameBuffer.

Вариант 1

Начертайте фона на вашата сцена (нещата, които няма да бъдат маскирани) във FrameBuffer. След това нарисувайте цялата си сцена без маски на екрана. След това нарисувайте стикери за маска на екрана, като използвате цветния прикачен файл на FrameBuffer. Недостатъкът на този метод е, че в OpenGL ES 2.0 на Android FrameBuffer може да има RGBA4444, а не RGBA8888, така че ще има видими шевове по краищата на маските, където битовата дълбочина на цвета се променя.

Вариант 2

Начертайте стикери за маска като черно-бели непрозрачни за вашия FrameBuffer. След това начертайте своя фон на екрана. Когато рисувате нещо, което може да бъде маскирано, нарисувайте го с мулти-текстуриране, умножавайки се по цветовата текстура на FrameBuffer. Потенциалният недостатък е, че абсолютно всичко, което може да бъде маскирано, трябва да бъде начертано с много текстури с персонализиран шейдър. Но ако използвате само ваденки, това всъщност не е по-сложно от Вариант 1.

Следното не е тествано... може да изисква малко отстраняване на грешки.

И в двата варианта бих подкласирал CameraGroupStrategy, който да се използва с DecalBatch при рисуване на ваденките на маската, и бих заменил beforeGroups, за да задам и втората текстура.

public class MaskingGroupStrategy extends CameraGroupStrategy{

    private Texture fboTexture;

    //call this before using the DecalBatch for drawing mask decals
    public void setFBOTexture(Texture fboTexture){
        this.fboTexture = fboTexture;
    }

    @Override
    public void beforeGroups () {
        super.beforeGroups();
        fboTexture.bind(1);
        shader.setUniformi("u_fboTexture", 1);
        shader.setUniformf("u_screenDimensions", Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
    }
}

И във вашия шейдър можете да получите цвета на текстурата FBO по следния начин:

vec4 fboColor = texture2D(u_fboTexture, gl_FragCoord.xy/u_screenDimensions.xy);

След това за вариант 1:

gl_FragColor = vec4(fboColor.rgb, 1.0-texture2D(u_texture, v_texCoords).a);

или за вариант 2:

gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
gl_FragColor.a *= fboColor.r;
person Tenfour04    schedule 18.07.2014
comment
Хей, работи перфектно на десктоп! Но на android нищо не се рисува. Имате ли идея защо? Опитах с формат на кадровия буфер 8888 и 4444, но е същото. - person Veljko; 19.07.2014
comment
Отстраних грешки... рисуването в буфер на рамка работи, но стъпка, която използва персонализирана групова стратегия, не рисува нищо на екрана. Това е моят beforeGroupsMethod: Gdx.gl.glEnable(GL20.GL_DEPTH_TEST); shader.begin(); shader.setUniformMatrix(u_projTrans, camera.combined); Gdx.graphics.getGL20().glActiveTexture(GL20.GL_TEXTURE1); fboTexture.bind(1); shader.setUniformi(u_fboTexture, 1); shader.setUniformf(u_screenDimensions, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); Gdx.graphics.getGL20().glActiveTexture(GL20.GL_TEXTURE0); shader.setUniformi(u_texture, 0); - person Veljko; 19.07.2014
comment
Когато нещо работи на настолен компютър, но не и на Android, за мен това обикновено се дължи на нещо в шейдъра, което кара шейдъра да не се компилира. След като компилирате своя шейдър, поставете това за целите на отстраняване на грешки: if (!shader.isCompiled())Gdx.app.log("shader compile failed", shader.getLog());. Това ще ви каже всички предупреждения или грешки, възникнали при компилирането. Той всъщност ви дава номера на реда и номера на колоната, където възникват грешките, но не и дали се случват във върховия или фрагментния шейдър, така че понякога трябва да проверите и двете. - person Tenfour04; 19.07.2014
comment
Хей, пробвах кода си на телефона, работи... също така го пробвах на STB устройство и нищо не се изчертава. Няма регистър на грешки или предупреждения в шейдъра. Дори се опитах да го заменя, за да нарисува проста червена кутия, но няма разлика. Знаете ли някои настройки, които могат да помогнат? STB устройството е моята целева платформа, така че трябва да работи на нея. Някакви предположения? Това е Jelly Bean 4.2.2. - person Veljko; 21.07.2014
comment
Намерих решение: Единственото нещо, което трябваше да добавя, е v_color в calulus за цвят на фрагмент във фрагментен шейдър. Ако е безполезно, предполагам, че има нещо нередно с версията на компилатора на STB или нещо подобно. - person Veljko; 21.07.2014
comment
Обикновено не е нещо нередно в компилатора, а по-скоро, че другите устройства, на които сте тествали, са били по-снизходителни, отколкото стандартът OpenGL ES изисква относно подробностите за синтаксиса. Например, повечето графични процесори автоматично ще прехвърлят цели числа към числа с плаваща единица, ако може да се заключи, че трябва да бъде плаваща единица, но това не се изисква от OpenGL ES, така че има няколко устройства, при които използването на цяло число на грешното място ще доведе до не да работиш. - person Tenfour04; 21.07.2014
comment
Имах идея за подобряване на производителността...в момента fboColor се изчислява със зависимо четене на текстура. Можете да изчислите еквивалента на gl_FragCoord.xy/u_screenDimensions.xy в върховия шейдър и да го подадете като променлив. Мисля, че бихте направили това, като използвате gl_Position, след като сте го изчислили. И така v_fboTexCoords = gl_Position.xy * 0.5 + 0.5;. - person Tenfour04; 21.07.2014
comment
Вашият трик за оптимизация работи, но изходът не е същият... всичко става по-малко в маската. Наистина не разбирам какви са тези постоянни фактори, които умножавате и добавяте. Бихте ли обяснили идеята и целта му? - person Veljko; 21.07.2014
comment
Когато умножите позицията на върха по вашата проекционна матрица, за да получите gl_Position, това трансформира позицията в екранно пространство, където екранът е картографиран от {-1,-1}(долно ляво) до {1,1}(горе-дясно). Така че разделих на две и добавих 0,5, за да го съпоставя от {0,0} до {1,1}. Но може би съм сгрешил някъде. - person Tenfour04; 21.07.2014
comment
Разбирам идеята. Както казах, работи, но изглежда, че маскираната текстура е 2 пъти по-малка от текстурата с предишния метод. Не мога да го поправя, като преместя маскираната си текстура по-близо до маската. Но добре, ако имах проблеми с производителността, ще погледна по-близо до това. Засега ще се придържам към метод 1. Много благодаря за вашата помощ!!! - person Veljko; 21.07.2014
comment
Може би просто добавяне на 0,5, без да се умножава по 0,5? - person Tenfour04; 21.07.2014