Manual material definition with diffuse, normal and specular maps
  • Hi,
    1. Can you give me a hint on how to define normal, specular, emission and opacity textures to the imported collada object.
    I started with "astro man" tutorial and changed collada object with my own exported from 3dMax. I defined all textures in collada but they are ommited after parsing in Minko.
    Optionally i could create material manually and put it on the collada model in as3 code but I need a short tutorial for this.
    Could you put a bit of code about how to create custom material with diffuse, normal, specular maps together and how to put it on mesh?

    2. Can you import skinned and animated models with 3ds extension?

    Regards, Piotr
  • 11 Comments sorted by
  • Hello Heath,

    nice work you're trying to do here!

    normalBitmapTexture.styleProperty = BasicStyle.NORMAL_MULTIPLIER;
    specularBitmapTexture.styleProperty = BasicStyle.DIFFUSE_MULTIPLIER;

    I believe this is because BasicStyle.DIFFUSE_MULTIPLIER is not the correct way to mark my specular map, but I do not know.


    Indeed. Multipliers are scalar value that should be used as multiplier for other sampled/computed values.

    What you have to understand is how we sample/use a texture added to the scene in a shader. It is done by calling the sampleTexture and passing the style id set for the ITexture.styleProperty of the texture we want to sample.

    If you want to define custom normal/alpha/specular/bump/whatever-you-need maps, here is how to do it with a simple use case for bump mapping:


    public final class BumpMappingStyle
    {
    // the style that will be used for the bump map texture
    public static const BUMP_MAP : int = Style.getStyleId("bump map");
    // an optional value to scale the bump effect
    public static const BUMP_MULTIPLIER : int = Style.getStyleId("bump multiplier");
    }

    public class Main extends Sprite
    {
    private var _viewport : Viewport = new Viewport();
    private var _camera : ArcBallCamera = new ArcBallCamera();
    private var _scene : Group = new Group(_camera);

    public function Main()
    {
    var bumpMap : BitmapTexture = LoaderGroup.loadClass(EMBED_BUMP)[0];
    var diffuseMap : BitmapTexture = LoaderGroup.loadClass(EMBED_DIFFUSE)[0];

    bumpMap.styleProperty = BumpMappingStyle.BUMP_MAP;
    // useless because "ITexture.styleProperty" is set to BasicStyle.DIFFUSE by default :
    // diffuseMap.styleProperty = BasicStyle.DIFFUSE;

    var materialScene : MaterialGroup = new MaterialGroup(new SinglePassRenderingEffect(new BumpMappingShader()));

    materialScene.textures.addChild(bumpMap).addChild(diffuseMap);
    materialScene.addChild(CubeMesh.cubeMesh);

    _scene.addChild(materialScene);

    stage.addChild(_viewport);
    stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }

    private function enterFrameHandler(event : Event) : void
    {
    _viewport.render(_scene);
    }
    }

    public final class BumpMappingShader
    {
    override protected function getOutputPosition() : SValue
    {
    return vertexClipspacePosition;
    }

    override protected function getOutputColor() : SValue
    {
    var uv : SValue = interpolate(vertexUV);
    // IMPORTANT: note that we sample BumpMappingStyle.BUMP_MAP
    // sampling BumpMappingStyle.BUMP_MULTIPLIER does not make sense: it is supposed
    // to be a scalar value
    var bump : SValue = sampleTexture(BumpMappingStyle.BUMP_MAP, uv);
    var diffuse : SValue = sampleTexture(BasicStyle.DIFFUSE, uv);

    // do your bump mapping stuff here...

    return bumpMappedDiffuse;
    }
    }


    We understand that the fact that textures are referenced in the shader using the value set for ITexture.styleProperty is very disturbing. People are tempted to set this very style id with the texture resource but it is not how it should be done.

    Minko v2 will make a clear difference between textures - that can be sampled using sampleTexture - and styles - that are just (Vectors of) Number/Point/Vector4/Matrix4x4 values that can be used in the shader.


    This is the one confusing thing we will eliminate in the next release. Sorry for the inconvenience, we are actively working on it as we speak :)

    - Specular Maps do not work. When I run I get the error...
    Error #3645: AGAL validation failed: Attribute registers can only be read in vertex programs for source operand 2 at token 2 of fragment program.


    Passing values from the vertex shader to the fragment shader is a special case. When considering AGAL assembly code for shaders and how the hardware works, it is done using what we call "varying registers". Varying registers are written by the vertex shaders (VS) and read by the fragment shader (FS). But when read in the fragment shader, those registers are first linearily interpolated. Remember: vertex shaders work on vertices (or triangles), but fragment shaders work on pixels. So you have to interpolate the values computed per vertices to makes them relevant for per pixel computations.

    To do this using ActionScript shaders, you juste have to use the interpolate() method on the value computed in the VS to get a (interpolated) value usable in the FS. Here is how to read the vertex normal in the FS for example:


    override protected function getOutputColor() : SValue
    {
    // vertexXXX values have to be interpolated because they are vertex attributes read in the VS
    var vertexNormal : SValue = normalize(interpolate(vertexNormal));

    // ...
    }


    Passing values from the VS to the FS is then entirely handled by Minko's JIT shader compiler, but you have to help it understanding what values have to be passed from the VS to the FS using interpolate. You might think that the compiler could detect all by itself when this is required, and it is true.

    The textures need to be added to a MaterialGroup, with an Effect, and this MaterialGroup is added to the Scene.


    Well... yes and not. Textures can be added anywhere in the scene. You just need to make sure they are placed "before" the meshes that will use sample them during rendering.

    But MaterialGroup nodes are indeed the best way to define all the components of a material at once. A material is made of an effect (basically, a shader), textures (bitmap data to sample in the shader) and a style (value used to tune the shader output). MaterialGroup nodes provide all of those in one place, and textures can be placed in the MaterialGroup.textures node. This is recommanded because this way, you can switch all the textures used by a material by simply changing the value of the MaterialGroup.textures property :)


    var matGroup : MaterialGroup = new MaterialGroup();

    matGroup.textures.addChild(texture1).addChild(texture2);
    matGroup.effect = new MyCoolEffect();
    matGroup.style.set(MyCoolStyle.SOME_STYLE, someStyleValue);


    To put it in a nutshell:
    - if you want to use specific textures, you must them their styleProperty accordingly and use to call the sampleTexture method
    - textures can be used anywhere in the scene
    - if you want to set an effect for a sub-scene, use an EffectGroup
    - if you want to tune style values for a sub-scene, use a StyleGroup
    - if you want to define all of the above (textures + style + effect), use a MaterialGroup and we recommend to put all the textures in the MaterialGroup.textures group

    The important thing to remember is that you can use the group that best fits your needs :)

    For example, when you will load an effect built using the shader lab, it will load a MaterialGroup ready to be used.

    Thanks a ton for all your hard work, Jean-Marc!


    Thank you! But a lot of people are working on Minko now :)
    We're all very happy that you like it.

    Regards,
  • 2. 3ds file format supports only static models, no animations.
  • I was going to create my own topic about how to implement Specular Maps, but I think hijacking this thread will be helpful.

    I am attempting to load a .3ds model with diffuse, normal and specular maps.
    The tutorials I've used are the Globe, Lighting and DisplayLitSpinningCubes. My shader is cannabalized from the Globe and Gravity demos.

    Here's the snippet of code I'm using to embed my materials...

    [Embed("../assets/Track_DF.png")]
    private static const ASSET_DIFFUSE_MAP : Class;
    private static const DIFFUSE_MAP_TEXTURE: ITexture = new LoaderGroup().loadClass(ASSET_DIFFUSE_MAP)[0] as ITexture;

    [Embed("../assets/Track_NM.png")]
    private static const ASSET_NORMAL_MAP : Class;
    private var normalBitmapTexture: BitmapTexture = new LoaderGroup().loadClass(ASSET_NORMAL_MAP)[0];
    private var NORMAL_MAP_TEXTURE: ITexture;

    [Embed("../assets/Track_SP.png")]
    private static const ASSET_SPECULAR_MAP : Class;
    private var specularBitmapTexture : BitmapTexture = new LoaderGroup().loadClass(ASSET_SPECULAR_MAP)[0];
    private var SPECULAR_MAP_TEXTURE : ITexture;


    Here is my Scene setup...

    private var _camera : FirstPersonCamera = new FirstPersonCamera();
    private var _spotlight : SpotLight = new SpotLight(0xffffff, 1., .8, 64, new Vector4(900., 500., 0.), 900., new Vector4(0., -1., 0.), .4, .4, 1024);
    private var _scene : StyleGroup = new StyleGroup(_camera, _spotlight);


    In an initializeShaders() function, I do...

    normalBitmapTexture.styleProperty = BasicStyle.NORMAL_MULTIPLIER;
    NORMAL_MAP_TEXTURE = normalBitmapTexture as ITexture;

    specularBitmapTexture.styleProperty = BasicStyle.DIFFUSE_MULTIPLIER;
    SPECULAR_MAP_TEXTURE = specularBitmapTexture as ITexture;

    _trackGroup = new MaterialGroup(new TrackEffect(), DIFFUSE_MAP_TEXTURE, NORMAL_MAP_TEXTURE ,SPECULAR_MAP_TEXTURE);

    _scene.addChild(_trackGroup);


    Setting the BasicStyle.NORMAL_MULTIPLIER marks the NORMAL_MAP_TEXTURE as the texture sampled when my shader calls sampleTexture(BasicStyle.NORMAL_MULTIPLIER, uv)
    QUESTION: Is the BasicSTyle.DIFFUSE_MULTIPLIER the correct StyleProperty to use to flag my Specular Map?

    TrackEffect() is a SinglePassEffect that simply instances a TrackShader()

    Now, the hard work. Here's my shader code. Most of it stolen from LightingEffect and Globe...

    public class TrackShader extends ActionScriptShader
    {
    private var _lightVec : SValue = null;
    private var _eyeVec : SValue = null;
    private var _halfVec : SValue = null;

    override protected function getOutputPosition() : SValue
    {
    var vertexBitangent : SValue = cross(vertexNormal, vertexTangent);
    var lightPosition : SValue = cameraLocalPosition;
    var lightDirection : SValue = normalize(subtract(lightPosition, vertexPosition));

    _lightVec = float3(
    dotProduct3(lightDirection, vertexTangent),
    dotProduct3(lightDirection, vertexBitangent),
    dotProduct3(lightDirection, vertexNormal)
    );

    _eyeVec = normalize(subtract(vertexPosition, cameraLocalPosition));
    _eyeVec = float3(
    dotProduct3(_eyeVec, vertexTangent),
    dotProduct3(_eyeVec, vertexBitangent),
    dotProduct3(_eyeVec, vertexNormal)
    );

    var vertexPos : SValue = normalize(vertexPosition);
    _halfVec = normalize(add(vertexPos, lightDirection));
    _halfVec = float3(
    dotProduct3(_halfVec, vertexTangent),
    dotProduct3(_halfVec, vertexBitangent),
    dotProduct3(_halfVec, vertexNormal)
    );

    return vertexClipspacePosition;
    }

    override protected function getOutputColor() : SValue
    {
    var pointLights:WorldDataList = getWorldDataList(LightData);
    var numLights:int = pointLights.length;

    var uv : SValue = interpolate(vertexUV);
    var pos : SValue = interpolate(vertexPosition);
    var normal : SValue = interpolate(vertexNormal);

    var diffuseMapValue : SValue = sampleTexture(BasicStyle.DIFFUSE, uv);
    var normalMapValue : SValue = sampleTexture(BasicStyle.NORMAL_MULTIPLIER, uv);
    var normalVec : SValue = subtract(normalMapValue.multiply(2.),1.);
    normalVec.normalize();
    var specularMapValue : SValue = sampleTexture(BasicStyle.DIFFUSE_MULTIPLIER, uv);
    var specularMapVectorHack : Vector4 = new Vector4(.5,.5,.5);

    var illumination:SValue = float3(0.,0.,0.);

    for (var lightIndex : int = 0; lightIndex < numLights; ++lightIndex)
    {
    var lightPosition : SValue = getWorldParameter(3, LightData, LightData.LOCAL_POSITION, lightIndex);
    var lightDiffuse : SValue = getWorldParameter(3, LightData, LightData.PREMULTIPLIED_DIFFUSE_COLOR, lightIndex);
    var squareLocalDist : SValue = getWorldParameter(1, LightData, LightData.SQUARE_LOCAL_DISTANCE, lightIndex);
    var lightToPoint : SValue = subtract(pos, lightPosition);

    //Specular test
    // do I need these numbers? they are set in the light but not used in the specular illumination calculation
    //var lightSpecular : SValue = getWorldParameter(1, LightData, LightData.PREMULTIPLIED_SPECULAR, lightIndex);
    //var lightShininess : SValue = getWorldParameter(1, LightData, LightData.PREMULTIPLIED_SHININESS, lightIndex);
    var lightPower : Number = 2.0; // power hard coded. can I get/set it in the pointLight objects?

    var ref : SValue = getReflectedVector(_lightVec, normalVec);
    var halfVector : SValue = interpolate(_halfVec);
    var shininess : SValue = power(max(dotProduct3(ref, halfVector), 0.0), lightPower);
    //illumination.increment(shininess.multiply(specularMapValue)); // what can I do to get this to work?
    illumination.incrementBy(shininess.multiply(specularMapVectorHack));

    // hacky hacky...
    var attenuation : SValue = saturate(multiply(squareLocalDist,reciprocal(dotProduct3(lightToPoint, lightToPoint))));

    // here goes the hack again...
    lightDiffuse.scaleBy(attenuation).scaleBy(.8);
    illumination.incrementBy(lightDiffuse);
    }

    return float4(multiply(diffuseMapValue.rgb, illumination), diffuseMapValue.a);
    }
    }


    Issues with the shader...
    - Normal Maps work (yay!).

    - I cannot use specularMapValue in my illumination calculation, specifically the lines...

    //illumination.increment(shininess.multiply(specularMapValue)); // what can I do to get this to work?
    illumination.incrementBy(shininess.multiply(specularMapVectorHack));

    I believe this is because BasicStyle.DIFFUSE_MULTIPLIER is not the correct way to mark my specular map, but I do not know.

    - Specular Maps do not work. When I run I get the error...
    Error #3645: AGAL validation failed: Attribute registers can only be read in vertex programs for source operand 2 at token 2 of fragment program.

    Commenting out the line...

    illumination.incrementBy(shininess.multiply(specularMapVectorHack));

    ...prevents this error. I do not know why.



    So in Summary (and to help out Piotr)
    Your Textures must be loaded and have the correct StyleProperty tags set on them.
    The textures need to be added to a MaterialGroup, with an Effect, and this MaterialGroup is added to the Scene.
    The Effect needs a Shader, and the Shader does the work of interpolating the vertexPosition, vertexNormal, and vertexUV, and sampling the textures flagged with the StyleProperty

    I would like to know if I am using my Specular Map properly, and what I am doing incorrectly in my shader code.

    Thanks a ton for all your hard work, Jean-Marc!
    -Heath
  • Issues with the shader...
    - Normal Maps work (yay!).

    Can you, please, make a screenshot of normalmapped mesh and post here?!
    Im trying your code, but it seems not work for me. (i didnt see any normal map effect on my mesh). =(

    im using your shader code and material group.
  • Yes sorry for the confusion. The shader I posted above is list using the 'hacky hacky' attenuation copied from the LightCubeShader example.
    Here is a simple pixel shader function that uses a normal and diffuse map.
    NOTE: It DOES NOT take in to account light color/distance/power, just an example of sampling normal map.

    override protected function getOutputColor() : SValue
    {
    var pointLights:WorldDataList = getWorldDataList(LightData);
    var numLights:int = pointLights.length;

    var uv : SValue = interpolate(vertexUV);
    var pos : SValue = interpolate(vertexPosition);

    var diffuseMapValue : SValue = sampleTexture(BasicStyle.DIFFUSE, uv);
    var normalMapValue : SValue = sampleTexture(BasicStyle.NORMAL_MULTIPLIER, uv);
    var normalVec : SValue = subtract(normalMapValue.multiply(2.0),1.0);

    var illumination:SValue = float3(0.,0.,0.);

    for (var lightIndex : int = 0; lightIndex < numLights; ++lightIndex)
    {
    var lightPosition : SValue = getWorldParameter(3, LightData, LightData.LOCAL_POSITION, lightIndex);
    var lightToPoint : SValue = subtract(pos, lightPosition);
    lightToPoint.normalize();

    illumination.incrementBy( max(dotProduct3(lightToPoint,normalVec), 0.0));
    }

    return float4(multiply(diffuseMapValue.rgb, illumination), diffuseMapValue.a);
    }


  • var normalMapValue : SValue = sampleTexture(BasicStyle.NORMAL_MULTIPLIER, uv);



    Please :) Again, multiplier = scalar value, it is not a texture.
    The BasicStyle.NORMAL_MULTIPLIER will never be used as a texture style id.

    If you need a normal map for your effect, you can do something like this:


    public final class MyCoolEffectStyle
    {
    public static const NORMAL_MAP : int = Style.getStyle("my cool effect normal map");
    }

    public class MyCoolShader
    {
    override protected getOutputPosition() : SValue
    {
    return vertexClipspacePosition;
    }

    override protected getOutputColor() : SValue
    {
    var uv : SValue = interpolate(vertexUV);
    var normalMapValue : SValue = sampleTexture(MyCoolStyle.NORMAL_MAP, uv);

    // do your stuff...
    }
    }

    Regards,
  • Ok I guess I'm still confused. I created new Style for my specular map, if I need to create another style for my normal map, that's fine.

    I'm confused about the usage of BasicStyle.NORMAL_MULTIPLIER above. Did you make a typo? If I create a new StyleId (NORMAL_MAP), would I call sampleTexture(MyCoolEffectStyle.NORMAL_MAP, uv)?
  • I'm confused about the usage of BasicStyle.NORMAL_MULTIPLIER above. Did you make a typo? If I create a new StyleId (NORMAL_MAP), would I call sampleTexture(MyCoolEffectStyle.NORMAL_MAP, uv)?


    Ahah my bad :) I'm so frustrated about this whol style/texture thing. I edited my previous post.
    Hopefully this confusion will be gone for good in a few weeks:
    - style properties will be hard typed
    - textures will be separated from the style, so there will be no confusion possible

    I created new Style for my specular map, if I need to create another style for my normal map, that's fine.


    Yes: if there is no style id available in minko for texture semantics you are using, just create it.

    Regards,
  • Im realised own normal(bump) mapper shader with various lighting models, if you interested in it i can post some code there.
  • Im realised own normal(bump) mapper shader with various lighting models, if you interested in it i can post some code there.


    Of course :)
    Did you use light world objects like in the LightCubeShader?

    Regards,
  • if you interested in it i can post some code


    I'm interested, could you post what you've done?
    Thanks!
    -Heath

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion

Tagged