And now for a Material question ... grrrr

I’m trying to get the shader being used in the material for a Post Process Filter…



mat.getActiveTechnique().getShader() obviously isn’t going to do it, due to there being no active technique during postQueue, preFrame and postFrame… or maybe there is and I am doing something wrong. But, I’m getting a NullPointerException for the shader.



Is there any way to update the shader directly without extending the material class?

Lol… ok… will do that instead

just create a j3md file for your post process and the associated shaders.



Then in your Filter the getMarterial method must return a material created with this material definition.

@nehon Unfortunately, I’m trying to use vec arrays, so the material def file needs to be circumvented.

Or, don’t use vec array use a texture



With vector array you will get pretty quick to the limit, trust me a texture is way faster.

1 Like

@nehon Ok… now for the stupid question. For the life of me, I can’t figure out how to create the JME Image and write the vectors into it. I hate to be a complete bother, but where can I find an example of this?

@nehon Is this the proper way of doing this? Or am II way off base?



[java]

Image img = new Image();

img.setWidth(1);

img.setHeight(particleCount);

ColorRGBA color = new ColorRGBA(0,0,0,0);

for (int i = 0; i < img.getData().size(); i++)

img.setData(i, ByteBuffer.wrap(color.asBytesRGBA()));

[/java]

here is a class i use to make it work

it’s vastly inspired from the ImageToAwt class of JME



the interesting method for you is

convert(ColorRGBA[] image, Format format, ByteBuffer buf)



[java]

import com.jme3.math.ColorRGBA;

import com.jme3.math.Vector2f;

import com.jme3.texture.Image.Format;

import java.nio.ByteBuffer;

import java.util.EnumMap;



public class ImageUtils {



private static final EnumMap<Format, DecodeParams> params = new EnumMap<Format, DecodeParams>(Format.class);



private static class DecodeParams {



final int bpp, am, rm, gm, bm, as, rs, gs, bs, im, is, ra, rg, rb, rr;



public DecodeParams(int bpp, int am, int rm, int gm, int bm, int as, int rs, int gs, int bs, int im, int is) {

this.bpp = bpp;

this.am = am;

this.rm = rm;

this.gm = gm;

this.bm = bm;

this.as = as;

this.rs = rs;

this.gs = gs;

this.bs = bs;

this.im = im;

this.is = is;

ra = 8 - Integer.bitCount(am);

rr = 8 - Integer.bitCount(rm);

rg = 8 - Integer.bitCount(gm);

rb = 8 - Integer.bitCount(bm);



}



public DecodeParams(int bpp, int rm, int rs, int im, int is, boolean alpha) {

this.bpp = bpp;

if (alpha) {

this.am = rm;

this.as = rs;

this.rm = 0;

this.rs = 0;

} else {

this.rm = rm;

this.rs = rs;

this.am = 0;

this.as = 0;

}



this.gm = 0;

this.bm = 0;

this.gs = 0;

this.bs = 0;

this.im = im;

this.is = is;

ra = 8 - Integer.bitCount(am);

rr = 8 - Integer.bitCount(rm);

rg = 8 - Integer.bitCount(gm);

rb = 8 - Integer.bitCount(bm);

}



public DecodeParams(int bpp, int rm, int rs, int im, int is) {

this(bpp, rm, rs, im, is, false);

}

}



static {

final int mx___ = 0xff000000;

final int m_x__ = 0x00ff0000;

final int m__x_ = 0x0000ff00;

final int m___x = 0x000000ff;

final int sx___ = 24;

final int s_x__ = 16;

final int s__x_ = 8;

final int s___x = 0;

final int mxxxx = 0xffffffff;

final int sxxxx = 0;



final int m4x___ = 0xf000;

final int m4_x__ = 0x0f00;

final int m4__x_ = 0x00f0;

final int m4___x = 0x000f;

final int s4x___ = 12;

final int s4_x__ = 8;

final int s4__x_ = 4;

final int s4___x = 0;



final int m5___ = 0xf800;

final int m_5__ = 0x07c0;

final int m__5_ = 0x003e;

final int m___1 = 0x0001;



final int s5___ = 11;

final int s_5__ = 6;

final int s__5_ = 1;

final int s___1 = 0;



final int m5__ = 0xf800;

final int m_6_ = 0x07e0;

final int m__5 = 0x001f;



final int s5__ = 11;

final int s_6_ = 5;

final int s__5 = 0;



final int mxx__ = 0xffff0000;

final int sxx__ = 32;

final int m__xx = 0x0000ffff;

final int s__xx = 0;



// note: compressed, depth, or floating point formats not included here…



params.put(Format.ABGR8, new DecodeParams(4, mx___, m___x, m__x_, m_x__,

sx___, s___x, s__x_, s_x__,

mxxxx, sxxxx));

params.put(Format.ARGB4444, new DecodeParams(2, m4x___, m4_x__, m4__x_, m4___x,

s4x___, s4_x__, s4__x_, s4___x,

mxxxx, sxxxx));

params.put(Format.Alpha16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, true));

params.put(Format.Alpha8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, true));

params.put(Format.BGR8, new DecodeParams(3, 0, m___x, m__x_, m_x__,

0, s___x, s__x_, s_x__,

mxxxx, sxxxx));

params.put(Format.Luminance16, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));

params.put(Format.Luminance8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));

params.put(Format.Luminance16Alpha16, new DecodeParams(4, m__xx, mxx__, 0, 0,

s__xx, sxx__, 0, 0,

mxxxx, sxxxx));

params.put(Format.Luminance16F, new DecodeParams(2, mxxxx, sxxxx, mxxxx, sxxxx, false));

params.put(Format.Luminance16FAlpha16F, new DecodeParams(4, m__xx, mxx__, 0, 0,

s__xx, sxx__, 0, 0,

mxxxx, sxxxx));

params.put(Format.Luminance32F, new DecodeParams(4, mxxxx, sxxxx, mxxxx, sxxxx, false));

params.put(Format.Luminance8, new DecodeParams(1, mxxxx, sxxxx, mxxxx, sxxxx, false));

params.put(Format.RGB5A1, new DecodeParams(2, m___1, m5___, m_5__, m__5_,

s___1, s5___, s_5__, s__5_,

mxxxx, sxxxx));

params.put(Format.RGB565, new DecodeParams(2, 0, m5__, m_6_, m__5,

0, s5__, s_6_, s__5,

mxxxx, sxxxx));

params.put(Format.RGB8, new DecodeParams(3, 0, m_x__, m__x_, m___x,

0, s_x__, s__x_, s___x,

mxxxx, sxxxx));

params.put(Format.RGBA8, new DecodeParams(4, m___x, mx___, m_x__, m__x_,

s___x, sx___, s_x__, s__x_,

mxxxx, sxxxx));

}



private static int Ix(int x, int y, int w) {

return y * w + x;

}



private static int readPixel(ByteBuffer buf, int idx, int bpp) {

buf.position(idx);

int original = buf.get() & 0xff;

while ((–bpp) > 0) {

original = (original << 8 ) | (buf.get() & 0xff );

}

return original;

}



private static void writePixel(ByteBuffer buf, int idx, int pixel, int bpp) {

buf.position(idx);

while ((–bpp) >= 0) {

// pixel = pixel >> 8;

byte bt = (byte) ((pixel >> (bpp * 8 )) & 0xff );

// buf.put( (byte) (pixel & 0xff) );

buf.put(bt);

}

}



/**

  • Convert an ColorRBGA array image to jME image.

    */

    public static void convert(ColorRGBA[] image, Format format, ByteBuffer buf) {

    DecodeParams p = params.get(format);

    if (p == null) {

    throw new UnsupportedOperationException("Image format " + format + " is not supported");

    }



    for (int x = 0; x < image.length; x++) {



    int outputPixel = encodeColor(image[x], p);

    int i = (x * p.bpp);

    writePixel(buf, i, outputPixel, p.bpp);

    }



    }



    /**
  • Convert an ColorRBGA array image to jME image.

    */

    public static void clear(ColorRGBA color, Format format, ByteBuffer buf, int size) {

    DecodeParams p = params.get(format);

    if (p == null) {

    throw new UnsupportedOperationException("Image format " + format + " is not supported");

    }



    for (int x = 0; x < size * size; x++) {



    int outputPixel = encodeColor(color, p);

    int i = (x * p.bpp);

    writePixel(buf, i, outputPixel, p.bpp);

    }



    }





    private static int encodeColor(ColorRGBA color, DecodeParams p) {

    // Get ARGB

    int argb = color.asIntARGB();

    // Extract color components

    int a = (argb & 0xff000000) >> 24;

    int r = (argb & 0x00ff0000) >> 16;

    int g = (argb & 0x0000ff00) >> 8;

    int b = (argb & 0x000000ff);

    // Remove anything after 8 bits

    a = a & 0xff;

    r = r & 0xff;

    g = g & 0xff;

    b = b & 0xff;

    // Do bit reduction, assumes proper rounding has already been

    // done.

    a = a >> p.ra;

    r = r >> p.rr;

    g = g >> p.rg;

    b = b >> p.rb;

    // Put components into appropriate positions

    a = (a << p.as) & p.am;

    r = (r << p.rs) & p.rm;

    g = (g << p.gs) & p.gm;

    b = (b << p.bs) & p.bm;

    int outputPixel = ((a | r | g | b) << p.is) & p.im;

    return outputPixel;

    }

    }



    [/java]



    here is how i initialize the texture

    gridSize is my array dimension, the grid is a 2 dimensional ColorRGBA array, groundMat is the material, GridMap is may shader param

    [java]

    mainTexture = new Texture2D(gridSize, gridSize, 1, Format.RGBA8);

    mainTexture.setMagFilter(MagFilter.Nearest);

    ByteBuffer buf = BufferUtils.createByteBuffer(gridSize * gridSize * 4);

    mainTexture.getImage().getData().add(buf);

    groundMat.setTexture("GridMap", mainTexture);

    [/java]



    then when ever i want to dump my grid to the texture i do

    [java]

    ImageUtils.convert(grid, Format.RGBA8, mainTexture.getImage().getData(0));

    [/java]

mhh wait…this encode the color as a 8 bit per channel color, you may have to use a RGBA32F format

ok forget about all this use this instead :



[java]

ByteBuffer data= BufferUtils.createByteBuffer(width * height * 4);

for (int i = 0; i < width; ++i) {

for (int j = 0; j < height; ++j) {

data.putFloat(pos.x);

data.putFloat(pos.y);

data.putFloat(pos.z);

data.putFloat(1.0f); //this may not be necessary

}

}

Texture2D texture = new Texture2D(new Image(Image.Format.RGBA32F, width, height, data));

[/java]

1 Like

@nehon This is absolute great! Although, when I try and use it, I get a NullPointerException where the <<<<<< is:



[java]

tex_img = new Texture2D(1, particleCount, 1, Image.Format.RGBA8 );

tex_img.setMagFilter(Texture.MagFilter.Nearest);

ByteBuffer buf = BufferUtils.createByteBuffer(particleCount * 4);

tex_img.getImage().getData().add(buf);

colors = new ColorRGBA[particleCount];

for (int i = 0; i < particleCount; i++)

colors = ColorRGBA.BlackNoAlpha;

ImageUtils.convert(colors, Image.Format.RGBA8 , tex_img.getImage().getData(0));

mat.setTexture(“VectorMap”, tex_img); //<<<<<<

[/java]

@nehon One last question… is it necissary to reset the material’s texture each time the image is updated? Or does the shader load it each pass anyways?

@nehon Ok… I’m about ready to pull my hair out. I thought I understood how to get the data back out of the image once in the shader… but every attempt has failed. I would have thought this would be a common thing, but I can’t find squat about it on the web. At least nothing geared towards the version of OpenGL being used here. Any examples you know of?

let’s say you have a 32 x 10 array and you want the (i,j) element



you have to pass the i,j indices to the shader.



then you have to be aware of the size of the array (here 32 x 10)



to get the value from the texture you have to fetch the textel using



vec4 value = texture2D(m_yourTexture, vec2(i/32,j/10));

1 Like

@nehon Ugh… can you look at this and see what the problem is? I decided to try creating an image with visual queues for the shader. I know the buffer size is right. I know all the vectors are being positioned correctly. I know that the translation (though upside-down atm) to screen space is correct. I THOUGHT I was writing into the correct buffer indexes… but it isn’t showing up that way. It’s displaying as if I am writing to the correct buffer index - some number, which of course gets worse each pass… so everything shifts each frame and appears to be going diagonally (vary drastically).



[java]

float depth = 4;

ByteBuffer data = BufferUtils.createByteBuffer(this.width * this.height * depth);



for (int p = 0; p < this.particles.length; p++) {

// For testing

this.particles[p].coords.setY(this.particles[p].coords.getY()-(tpf/3));



// Get screen location

Vector3f screenCoords = activeCamera.getScreenCoordinates(this.particles[p].coords);



// Split these up so I could debug (have tried converting to int & using floor, ceil, etc to see if that was the issue, no luck

float sX = screenCoords.getX();

float sY = screenCoords.getY();



// Make sure the location is actually visible

if (sX >= 0 && sX <= width && sY >= 0 && sY <= height) {

// Get the buffer index

float index = sXsYdepth;



// Write to the buffer (don’t care about alpha atm)

data.putFloat((int)index,1f);

data.putFloat((int)index+1,1f);

data.putFloat((int)index+2,1f);

}

}

Texture2D texture = new Texture2D(new Image(Image.Format.RGBA8, this.width, this.height, data));

mat.setTexture(“VectorMap”, texture);

data.clear();

data = null;

[/java]

Your way to compute the index may be wrong,

It should be index = (sX+sY*this.width) * depth

1 Like

Its very easy to pass arrays (or anything supported by uniform) using Material.setParam()

well…i tried once, and found myself with very poor perf with and array bigger than 16 * 1 vector3f, never dif the issue though.

Recall that an array has to be sent for every model every frame, whereas a texture is sent only once.