Object3D color not being read in shader

Started by zoalord12, November 19, 2015, 09:14:36 PM

Previous topic - Next topic

zoalord12

Hi all,

Reading an Object3D,
when i render it as it is , the "green" color shows up on the mesh since its coming from the material file, (I loaded from an .obj + .mtl)

When I add a shader, I cant get the correct vertex color in my attribute variable.
The normals come through fine though, using this method.
PLEASE HELP. thanks in advance



// VERTEX SHADER   -----------------------------------------------------------------------

attribute vec4 additionalColor;

attribute vec4 position;
attribute vec3 normal;
attribute vec4 color;

uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;

varying vec4 myColor;
void main(void)
{

  myColor = color;

  // I also tried this one but neither one works.
  myColor = additionalColor;

  gl_Position = modelViewProjectionMatrix * position;
}


// FRAGMENT SHADER  ------------------------------------------------------------------

precision mediump float;
varying vec4 myColor;

void main ()
{
   gl_FragColor = vec4(myColor.xyz, 1);
}






EgonOlsen

#1
additionalColor has to be a uniform, not an attribute. Attributes change for each vertex, uniforms don't.

zoalord12

Hmm ok,  but the material color coming from the .mtl file is not for the whole mesh
there can be "n" number of materials each being a solid color, one for the arm, one for the feet, one for the head etc.

so it should come through the color channel.
I now understand that I can set additional color (only 1 RGB ) for the mesh, so that was my mistake asking about that in the previous question.

but my question is why am I not getting the color channel for my mesh using
attribute vec3 color in the shader ?

zoalord12

#3
Here is the example : The color output on the mesh is BLACK.

When i don't use the shader, color of mesh = green which it should be
When i have something like this myObject3D.add(myShader) , i get color = black.

// --- THIS IS THE SUBSET OF MY MTL FILE ------------------------------------------------------

newmtl torsoColor
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.296990 0.451769 0.032732
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000

// ----------------------------------------------------------------------


// ----- VERTEX


attribute vec4 position;
attribute vec4 color;

varying vec4 myColor;
void main(void)
{
             myColor = color ;


   gl_Position = modelViewProjectionMatrix * position;
}

// --------------- FRAGMENT

varying vec4 myColor;
void main ()
{

gl_FragColor = vec4(myColor.xyz, 1);

}

EgonOlsen

Not sure what you mean. additionalColor has to be a uniform. If it's not, it won't be injected. additionalColor is the value set by http://www.jpct.net/jpct-ae/doc/com/threed/jpct/Object3D.html#setAdditionalColor(int, int, int). It has nothing to do with the material color. jPCT-AE doesn't really know the concept of material colors, so the loader mimics them by creating textures in that color. If you want to change these colors, you have to modify the textures. Have a look at the log output when loading the model. It will tell you if it's creates a unicolored texture for this purpose.

zoalord12

Oh i see,

found this : http://www.jpct.net/forum2/index.php?topic=4139.5;wap2

Where you explain it. I just used texture0 and it works now.
I was confused since I didn't attach any textures but you explan in there that a new texture is generated. 


Can you share those few lines of code where the color from float/double is read from the .mtl file and written to the texture. I want to write something like this


// ORIGINAL MTL FILE

newmtl torsoColor
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.296990 0.451769 0.032732

// ---------------------


//  IN THE SHADER I WANT

vec4 color == GET THE COLOR AT THE VERTEX.

if (color.x == 0.296990  && color.y == 0.451769 && color.z ==   0.032732)  {


}

// ------------------------------------

or any other way you recommend doing this since my model is color coded ... like hand = shade of red, face = shade of green etc..





zoalord12

can you also tell me what happens when there are multiple materials ? like for instance this case


//---------------------------------

Blender MTL File: 'free_car_1_b.blend'
# Material Count: 6

newmtl bodyMat
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.005056 0.000000 0.050252
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2

newmtl glassMat
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2

newmtl metalMat1
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2

newmtl metalMat2
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.000000 0.000000 0.000000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
... and so on



EgonOlsen

If there are multiple materials (which define a diffuse color and no texture), multiple colored textures will be created and the polygons in question will be mapped accordingly. If you then want to change the textures, you can't use setTexture() anymore but he PolygonManager instead.

Regarding reading in the color information: Just split the line and parse the floats. It's really simple stuff.

zoalord12

#8
Thanks for that info, it makes things a lot clearer.

One more thing, so if i have lets say 20 diffuse materials, i get 20 textures ,
but i cant access them directly in the shader , since I only have access to the 4 textures in the shader ( textureUnit0 -  textureUnit3), is that correct ?


AFTER CHECKING:
Texturemanager , does a replacement based on name, not ID, I see that the texturecount is 5, while my diffuse material count is 6
also they have no names or do they have the material name, i dont know ?
i tried a texture lookup using the material name but it didnt work.


Regarding replacing textures at runtime, can i just do

texture abc = tm.gettextureById(id)
abc = new texture .. ... or use a scaled bitmap etc.

How can i change the textures with just the id, and not knowing the name ?



EgonOlsen

They have a artifical name based on the color. You should see it the log output. Or, if that's not the case, you can get all names from the TextureManager. Just look at them and you'll notice the naming convention.

To make you better understand the texture/shader relation, here is what happens behind the scenes:

For each diffuse color in a material, a texture with that color will be created (if the material doesn't contain a texture anyway). It will create one Object3D with x different textures assigned to different polygons depending on the materials of those. However, that's not what's getting rendered. Because of the way in which graphics hardware works, you can't render one mesh with different textures on different polygons. So what actually happens is, that jPCT-AE internally splits the object into lumps of polygons, one for each texture/color. You should be able to spot that in the log output as well (some "compiled" message). Each of these lumps will be rendered in its own draw call with its own set of textures (in your case one on the first stage). So in your shader, you have one texture only, but your shader will be used in multiple render calls.

May I ask what exactly you want to create? Maybe there's another solution...

zoalord12


oh i see, i looked through the debugger its _obj-Color:R/G/B

my logger keeps flooding the console with "Binding texture 0" and "Binding texture 1", even with LL_ERRORS_ONLY
so I cant even see these messages.

I'm building a model viewer where you can change colors for certain parts on the fly, as in using user input.
The model is color coded in the sense that certain mesh parts are one specific color, so im just using crude logic to identify the color,
and use if statements inside the shader to change it to user color, if existing color is within a certain tolerance range.


I now see that the texture swapping solution will be way more elegant.

Also, im assuming if there is any material in the file that is not being used will not be TEXTURED, right ?
that would explain why my total texture count is less than the number of materials in the .mtl file



BTW, your engine is the best I've seen for android. I was able to get it up and running in 1 day. I use AndEngine but its only good for 2D stuff.
Thank you so much for writing this.



here is a sample


// Changing COLORS AT RUNTIME IN THE FRAG SHADER

// If "check" is within a certain tolerance, then we have the correct color that must be replaced.
// "check" is being read from the texture, and compared to the known RGB color "x"

// x is the known value of the color which must be replaced.

bool isWithin(float check, float x, float tol) {
   if(  (check >= (x - tol)) &&
        (check <=(x + tol)))
        return true;
    return false;
}


void main () {
    vec4 printCol;
    vec4 base = texture2D(textureUnit0, TEX0);

    if (isWithin(base.x, mod1.x, modTol) && isWithin(base.y, mod1.y, modTol) && isWithin(base.z, mod1.z, modTol) )
        printCol = vec4(mod1NewColor.xyz, 1);
    else
        printCol = vec4(base.xyz, 1);// myAddColor;// * diffuse;// vDiffuse;

   
    gl_FragColor = printCol;
}

EgonOlsen

Quote from: zoalord12 on November 20, 2015, 04:34:56 PM
my logger keeps flooding the console with "Binding texture 0" and "Binding texture 1", even with LL_ERRORS_ONLY
so I cant even see these messages.
That can't happen. This message is printed out only in one part of the code. That part is conditional. It only gets executed, if the log level is set to DEBUG. Check you log level settings...maybe you are setting it to DEBUG somewhere else.

Quote from: zoalord12 on November 20, 2015, 04:34:56 PM
I now see that the texture swapping solution will be way more elegant.
I think so. Either that, or you can use an ITextureEffect implementation to change the color of the existing texture. This causes a new texture upload for each change in color, but these textures are pretty small, so it might not be an issue.

Quote from: zoalord12 on November 20, 2015, 04:34:56 PM
Also, im assuming if there is any material in the file that is not being used will not be TEXTURED, right ?
that would explain why my total texture count is less than the number of materials in the .mtl file
Yes, it does it on demand only.

Another thing to keep in mind is that you can't split the model afterwards, i.e. if your color code for polygon 1 and 2 is x and for polygons 3,4 and 5 y, you can modify 1 and 2 and 3,4,5 but you can't set 3 and 4 to z and keep 5 at y...if you know what I mean... ;)

About the shader solution: That should work as well. I seems a little hacky to me though, but maybe that's just me.


EgonOlsen

Another idea would be an implementation of IRenderHook. The beforeRendering-method will be called before each draw call. It gets the current polygonID as parameter. If you know which id corresponds to which color, you can write a much simpler shader that takes a single uniform set inside this method that defines the final color.

zoalord12

#13
That shader is very very hacky and totally not scalable, but helped me prototype things i do after the color changes   ^_^

I haven't seen any samples with the IRenderHook, so i didn't take that route.

Option-1
From what i know now, i can use the setTexture and keep creating these 16x16 textures with a flat color.

Option-2
This IRenderHook thing will be way faster, but i dont know how to get started with this, can you point me to a sample because I want to use this if possible.
My polygons dont have IDs, I'm assuming by ID you mean HAND, FACE etc for the polygons representing hand/face , correct ?


BTW, is there any change I can get access to the MTL reader, or any way to access the material RGB and Titles ?
Currently, I'm using OBJImport alongside your api to read the materials separately. Would be nice to get this done in 1 place.

EgonOlsen

The polygonID is basically a polygon index. You just to figure out which one is which.

About the IRenderHook: Just implement it and do your magic in the beforeRendering()-method. To execute it, set it to all Object3D instances in question. That's it! The method will be called automatically before each draw call.