Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat
understanding normal map shader - Gideros Forum

understanding normal map shader

piepie Member
edited April 2016 in General questions
Hi
I was trying again to understand shaders: :(|)
my goal would be to apply a normal map shader to my tilebased game.

I have a tilemap which is composed by rgb tiles, with "objects (Sprites - but could be meshes)" on top of it.
I am currently using a basic fog of war made with rendertarget and various "holes" in a black shape to simulate lights.

I'd like to "bump" these objects according to my existing lights. I can provide normal map textures for each object.

My textures come from a texturepack, so I am using atilim's trick to get texture regions
http://giderosmobile.com/forum/discussion/comment/21594#Comment_21594

I understood that in normal map example there is a mesh with the "first texture" set here
local mesh = Mesh.new()
mesh:setVertexArray(0, 0, 512, 0, 512, 512, 0, 512)
mesh:setTextureCoordinateArray(0, 0, 512, 0, 512, 512, 0, 512)
mesh:setIndexArray(1, 2, 3, 1, 3, 4)
mesh:setTexture(texture)
mesh:setShader(effect)
but I am missing a lot of things, starting from the basics:
1) "where" are the coordinates to the "normal map texture"?
2) Is it possible to add more than one light source? How?


Thank you
«1

Comments

  • ar2rsawseenar2rsawseen Maintainer
    basically it means you need to generate normal map for each texture tile.
    I remember I tried to generate a normal map applying some photoshop effects, and it was not great, it was ok, but not great.
    like this:


    as I'm not a visual guy, it was hard for me to find proper values, etc.
    usually as I see, some assets already include normal maps created by the asset creator.
    but as I said, should be possible to create with some image editors, depending on your skill level.

    also am really interested to see a video demo of what/when you achieve something :)
  • piepie Member
    edited April 2016
    Thanks @ar2rsawseen I will keep you posted IF I achieve something :D .

    There are many other tools to draw normal maps: I was thinking about https://www.codeandweb.com/spriteilluminator or exporting them directly from blender or sketchup

    however my problem is not (yet) drawing normal maps textures, but how to "mix them up" using gideros shaders :)

    From what I understood it should work like this:
    Texture is the bitmap I need to show
    TextureMap is the normal map that should "change" the values of lighting in Texture pixels, according to light position.

    Cross.png - which is the demo texture used in the example is a single texture 1024x512 which contains both (Texture and TextureMap) - my texture size is different and I am taking my textures from a texturepack, so it should be a slightly different approach.. however, this is the main reason for my questions :)

    1)"where" are the coordinates to the "normal map texture"?
    Reading the example I only see a Mesh constructor, with a texture 512x512px (starting from 0,0) attached.. now.. how does the shader know that it needs to get the TextureMap (and "where" should it take it from? from which coordinates? )

    2)Is it possible to add more light sources using the same shader? and how to do it?

    thank you :)
  • tkhnomantkhnoman Member
    edited April 2016
    1. From Gideros Example :
    this is where you get the normal map:

    mediump vec3 normal = texture2D(g_Texture, texCoord + vec2(0.5, 0.0)).rgb * 2.0 - 1.0;

    Notice that texCoord + vec2(0.5, 0.0), that means normal is taken from main coordinate + half of total width.

    I'm not sure on how you can load other texture to your main texture with Gideros code.
    For my case, i just use renderTarget for it.

    2. Yes, by just simply adding it with FOR function.
    This probably would help:
    https://github.com/mattdesl/lwjgl-basics/wiki/ShaderLesson6

    Likes: pie, oleg

    +1 -1 (+2 / -0 ) Share on Facebook
  • tkhnomantkhnoman Member
    edited April 2016
    Probably it's better to understand about pixel shader in easy way first.

    Pixel shader is actually function to process color.
    And it applied on every pixel in screen.

    From normal map example(ps.glsl) This:
    lowp vec3 color0 = texture2D(g_Texture, texCoord).rgb;
    Would take a color in a current pixel.

    Also,
    gl_FragColor = g_Color * vec4(color0 * diff + color1 * spec, 1);
    This at the end, actually a return for the color with value : vec4(r,g,b,a)
    [notice that alpha in here is 1, if you want variable alpha, you might want to do something like this:
    lowp vec4 colorF = texture2D(g_Texture, texCoord);
    lowp vec3 color0 = colorF.rgb;
    .
    .
    .
    gl_FragColor = g_Color * vec4(color0 * diff + color1 * spec, colorF.a);

    ]

    To increase the lighting, you can just add r/g/b.


    On how normal mapping works.
    Basically it check (something like) distance between the current light position and the normalmap value in the pixel. You can see this from this function here:
    lowp float diff = max(0.0, dot(normal, lightDir));
    that "dot" function.

    This is also why in normal mapping x = red, y = green, z = blue, because it compare x with red and so on.
    By comparing, you get a value of distance between light position and normalmap value of that pixel, and this value then work as multiplier of the color : ( color0 * diff ).

    color1 at there is just added thing to add something like ilumination, you actually can do simpler function other than that formula.

    I hope this help to understand more.

    Likes: pie

    +1 -1 (+1 / -0 ) Share on Facebook
  • hgy29hgy29 Maintainer
    In addition to what @tkhnoman said, I commented the normal shader code:
    uniform lowp sampler2D g_Texture;
    uniform lowp vec4 g_Color;
     
    uniform mediump vec4 lightPos;
     
    varying highp vec2 texCoord;
    varying mediump vec2 position;
     
    void main()
    {
    	//Grab the main pixel color from the classic texture
    	lowp vec3 color0 = texture2D(g_Texture, texCoord).rgb;
     
    	//Set the specular (i.e. "shine") color to 30% gray
    	lowp vec3 color1 = vec3(0.3, 0.3, 0.3);
     
    	//Grab the normal vector from the right part of the texture:
    	//-Displace by 0.5 to the right in normalized coordinates (1=full texture width)
    	//-Convert RGB (range 0->1) to XYZ (range -1 -> 1)
    	mediump vec3 normal = texture2D(g_Texture, texCoord + vec2(0.5, 0.0)).rgb * 2.0 - 1.0;
     
    	//Compute light direction, assuming light distance of 150 units
    	mediump vec3 lightDir = normalize(vec3(lightPos.xy, 150) - vec3(position.xy, 0));
     
    	//Compute the light direction as if light was two times further (for specular)
    	mediump vec3 halfdir = normalize(normalize(lightDir) + vec3(0, 0, 1));
     
    	//Compute diffuse factor: normal and lightDir colinear -> 1, perpendicular -> 0
    	lowp float diff = max(0.0, dot(normal, lightDir));
     
    	//Compute specular factor the same way, but with exponential scale
    	mediump float nh = max(0.0, dot(normal, halfdir));
    	mediump float spec = pow(nh, 10.0);
     
    	//Combine g_Color (light) with Diffuse and Specular colors into final pixel value
    	gl_FragColor = g_Color * vec4(color0 * diff + color1 * spec, 1);
    }
    The way specular is computed doesn't make completely sense to me (specifically the halfdir part), however...

    Likes: pie

    +1 -1 (+1 / -0 ) Share on Facebook
  • hgy29hgy29 Maintainer
    About texture atlas, the given shader should work out of the box as long as your put normal map textures in the same atlas as normal textures, and you make it so that classic textures are on the left part and normal maps are on the right part, and you ensure your texture atlas has power of two sizes.
  • piepie Member
    edited April 2016
    Thank you guys, I am slowly grasping it. :)

    I made some progress, here's a screenshot of my test if you're interested.

    @hgy29 I hope you find some time to comment out all the shaders examples, this is gold :)
    I tried your suggestion but I wasn't able to make it work out of the box (unless I misunderstood something).
    Maybe this is due to how isometricTilemap is composed, if I apply the effect to each single tile mesh it "breaks the image" showing only an offsetted portion of each tile (black corners on top, and missing half bottom tile).

    Is there a tech reason why this shader only works with meshes or is it a "bug"?

    In the end I made it work, but I feel like I've cheated :D

    I made an additional normalmap-texture tileset in tiled and drew my normal map inside a tiled level.

    When I load the tilemap I draw the "normalmap level" aside of it, then rendertarget both (as these were a unique big texture) and then create a mesh to "host" this texture.

    However I am not sure that this is the best option, I now have a "bonus texture" loaded, as big as my tilemap x 2, and a new mesh on stage.
    What do you think?
    @tkhnoman do you render each tile-texture or you're doing something similar?

    By the way I believe that this approach has a benefit, normalmaps are independent from the classic-tileset: it's possible to achieve some vfx and there should be no issue when flipping tiles (bumps need to be inverted).

    A curious sideeffect (between the other things I ignore, I don't know math very well...) is that I needed to change vector grabbing position to 0.313 instead of 0.5.
    mediump vec3 normal = texture2D(g_Texture, texCoord + vec2(0.313, 0.0)).rgb * 2.0 - 1.0; //0.313 to overlay the tilemap with normalmap.

    Thank you

    Likes: antix

    Normal MapTest - Gideros Player.png
    798 x 480 - 444K
    +1 -1 (+1 / -0 ) Share on Facebook
  • hgy29hgy29 Maintainer
    Good to hear that you somehow managed to get it working. A wild guess for your coefficient issue (0.313 instead of 0.5): your texture width isn't a power of two maybe ?

    I started playing with normals for my 3D first person game demo and faced a few issues with normal map example, which is too simplistic. Among other things:
    - Coordinates used for computations are in Sprite local space, which makes it hard to use if several Sprite share the shader
    - It assumes that the shape drawn is in 2D (maybe not an issue for you)
    - I am still fighting correctly computing the light source position to give to the shader.
  • piepie Member
    edited April 2016
    Actually my starting textures and the derived atlas are power of two: tiles are 256x128 (or half) and atlas is 2048x2048 (or half).
    But, now that I think about it, the width of a drawn isometricTilemap (which could also be "irregular") is not always a power of 2.. this could be the problem ;)

    I need to find the best way to get the next power of 2 in gideros, given my tilemap width*2 as a starting number.
    Is there some math-savy trick that you can think of? :)

    Thank you
  • hgy29hgy29 Maintainer
    math.pow(2,math.ceil(math.log(width*2)/math.log(2)))

    Likes: antix, pie

    +1 -1 (+2 / -0 ) Share on Facebook
  • I'm using the same method, i need to create render target.
    But when it is for non moving object, i draw them together in one render target, and draw also normal map on that single rendertarget. There is a weakness though, that is render target can't exceed 2048 x 2048, so i need to maintain the group mesh to be 1024 x 1024 (considering the render target to be 2048 x 1024). So if it exceed, i need to create another group.

    Also, sometime when working on rendertarget / mesh, you need to prepare them when the application start, not before you're using them. If you're using them directly after creating, sometime it can show a bug, for example the image is gone, or it become black.
    So in my case, i create bunch of them, and using it like pooling.

  • piepie Member
    edited April 2016
    thank you,
    I didn't notice the renderTarget limit using 128x64 tiles, my texture is 4096 x 964 right now. It fails with tiles@2x on a phone (s2) but it's still working on tablet and laptop. however it's just a simple tilemap, I don't know what will happen when I'll bring it into my game with animations and stuff :)

    Now that my system works with coefficient 0.5 and any shaped tilemap ;) I tried to hardcode some more lights into the normal map shader example to know how many can be handled, but I am getting a bunch of errors:

    Here's my ps shader code, I left vs as it is in the example:
    I still don't get how to initialize and gather the totalLighting, I suppose I need to sum the result of each light and set gl_FragColor.
    uniform lowp sampler2D g_Texture;
    uniform lowp vec4 g_Color;
     
    uniform mediump vec4 lightPos;
    uniform mediump vec4 lightPos2;
     
     
    varying highp vec2 texCoord;
    varying mediump vec2 position;
     
    //how many lights:
    const int numberOfLights = 2;
    //light positions array
    vec4 ligPos[numberOfLights] = vec4[](lightPos, lightPos2);
    //lights - gl_FragColors to sum at the end
    vec4 lights[numberOfLights];
    // initialize total lighting 
    //vec4 totalLighting //should be the sum of all the lights gl_fragColors
     
     
    void main()
    {
     
     
     
    for (int index = 0; index <= numberOfLights; index++) // for all light sources 
    	{
    		//Grab the main pixel color from the classic texture
    		lowp vec3 color0 = texture2D(g_Texture, texCoord).rgb;
     
    		//Set the specular (i.e. "shine") color to 30% gray
    		lowp vec3 color1 = vec3(0.3, 0.3, 0.3);
     
    		//Grab the normal vector from the right part of the texture:
    		//-Displace by 0.5 to the right in normalized coordinates (1=full texture width)
    		//-Convert RGB (range 0->1) to XYZ (range -1 -> 1)
    		mediump vec3 normal = texture2D(g_Texture, texCoord + vec2(0.5, 0.0)).rgb * 2.0 - 1.0; 
     
    		//Compute light direction, assuming light distance of 150 units
    		mediump vec3 lightDir = normalize(vec3(ligPos[index].xy, 150) - vec3(position.xy, 0)); 
     
    		//Compute the light direction as if light was two times further (for specular)
    		mediump vec3 halfdir = normalize(normalize(lightDir) + vec3(0, 0, 1));
     
    		//Compute diffuse factor: normal and lightDir colinear -> 1, perpendicular -> 0
    		lowp float diff = max(0.0, dot(normal, lightDir));
     
    		//Compute specular factor the same way, but with exponential scale
    		mediump float nh = max(0.0, dot(normal, halfdir));
    		mediump float spec = pow(nh, 10.0); 
     
    		//vec4 lights[index] = g_Color * vec4(color0 * diff + color1 * spec, 1);
    	} 
    		//Combine g_Color (light) with Diffuse and Specular colors into final pixel value
    		gl_FragColor = g_Color * vec4(color0 * diff + color1 * spec, 1);
    		//it should be something like 
    		//totalLighting = lights[1]+lights[2]
    		//gl_FragColor = vec4(totalLighting, 1.0);
     
    }


    Here's my lua constructor:
    local effect = Shader.new("vsmulti","psmulti",0,
    {
    {name="g_MVPMatrix",type=Shader.CMATRIX,sys=Shader.SYS_WVP, vertex=true},
    {name="g_Color",type=Shader.CFLOAT4,mult=1,sys=Shader.SYS_COLOR},
    {name="lightPos",type=Shader.CFLOAT4,mult=1,vertex=false},
    {name="lightPos2",type=Shader.CFLOAT4,mult=1,vertex=false},
    {name="g_Texture",type=Shader.CTEXTURE,mult=1,vertex=false}
    },
    {
    {name="POSITION0",type=Shader.DFLOAT,mult=3,slot=0,offset=0},
    {name="vColor",type=Shader.DUBYTE,mult=0,slot=1,offset=0},
    {name="TEXCOORD0",type=Shader.DFLOAT,mult=2,slot=2,offset=0}
    })
    And this is the error output: :D
    FragmentShader:
    ERROR: 0:54: 'color0' : undeclared identifier 
    ERROR: 0:54: 'diff' : undeclared identifier 
    ERROR: 0:54: 'color1' : undeclared identifier 
    ERROR: 0:54: 'spec' : undeclared identifier 
    ERROR: 0:54: 'constructor' : not enough data provided for construction 
    ERROR: 0:67: 'position' : redefinition 
    ERROR: 0:81: 'numberOfLights' : redefinition 
    ERROR: 0:82: 'lights' : redeclaration of array with size 
    ERROR: 0:115: 'main' : function already has a body 
    ERROR: 0:117: 'assign' :  cannot convert from 'structure' to '4-component vector of float'
    ERROR: 0:118: 'assign' :  cannot convert from 'structure' to '4-component vector of float'
    ERROR: 0:130: 'position' : illegal vector field selection 
    ERROR: 0:130: 'w' :  field selection requires structure, vector, or matrix on left hand side 
    ERROR: 0:133: 'position' : illegal vector field selection 
    ERROR: 0:137: 'position' : illegal vector field selection 
    ERROR: 0:137: 'constructor' : not enough data provided for construction 
    ERROR: 0:140: 'constantAttenua
    Any help is appreciated, I feel like I am doing rocket science here @-) I am too far away from the friendly lua environment...
    Thanks
  • hgy29hgy29 Maintainer
    Hi @pie, below is how I would have wrote the ps shader (tested on my computer);
    uniform lowp sampler2D g_Texture;
    uniform lowp vec4 g_Color;
     
    #define NUM_LIGHTS	2 
    uniform mediump vec4 lightPos[NUM_LIGHTS];
     
    varying highp vec2 texCoord;
    varying mediump vec2 position; 
     
    void main()
    {
    	//Common data
    	//Grab the main pixel color from the classic texture
    	lowp vec3 color0 = texture2D(g_Texture, texCoord).rgb;
     
    	//Set the specular (i.e. "shine") color to 30% gray
    	lowp vec3 color1 = vec3(0.3, 0.3, 0.3);
     
    	//Grab the normal vector from the right part of the texture:
    	//-Displace by 0.5 to the right in normalized coordinates (1=full texture width)
    	//-Convert RGB (range 0->1) to XYZ (range -1 -> 1)
    	mediump vec3 normal = texture2D(g_Texture, texCoord + vec2(0.5, 0.0)).rgb * 2.0 - 1.0; 
     
    	lowp vec3 lightContributions=vec3(0,0,0);	
    	int index;
    	for (index = 0; index <= NUM_LIGHTS; index++) // for all light sources 
    	{ 
    		//Compute light direction, assuming light distance of 150 units
    		mediump vec3 lightDir = normalize(vec3(lightPos[index].xy, 150) - vec3(position.xy, 0)); 
     
    		//Compute the light direction as if light was two times further (for specular)
    		mediump vec3 halfdir = normalize(normalize(lightDir) + vec3(0, 0, 1));
     
    		//Compute diffuse factor: normal and lightDir colinear -> 1, perpendicular -> 0
    		lowp float diff = max(0.0, dot(normal, lightDir));
     
    		//Compute specular factor the same way, but with exponential scale
    		mediump float nh = max(0.0, dot(normal, halfdir));
    		mediump float spec = pow(nh, 10.0); 
     
    		lightContributions=lightContributions+ (color0 * diff + color1 * spec);
    	} 
    	gl_FragColor = g_Color * vec4(lightContributions.rgb, 1); 
    }
    And lua init/setup:
    local effect = Shader.new("vs","ps",0,
    {
    {name="g_MVPMatrix",type=Shader.CMATRIX,sys=Shader.SYS_WVP, vertex=true},
    {name="g_Color",type=Shader.CFLOAT4,mult=1,sys=Shader.SYS_COLOR},
    {name="lightPos",type=Shader.CFLOAT4,mult=2,vertex=false}, --Two lights
    {name="g_Texture",type=Shader.CTEXTURE,mult=1,vertex=false}
    },
    {
    {name="POSITION0",type=Shader.DFLOAT,mult=3,slot=0,offset=0},
    {name="vColor",type=Shader.DUBYTE,mult=0,slot=1,offset=0},
    {name="TEXCOORD0",type=Shader.DFLOAT,mult=2,slot=2,offset=0}
    })
     
    effect:setConstant("lightPos", Shader.CFLOAT4, 2, x, y,0,0, x2,y2,0,0) //Two lightPos

    Likes: pie, oleg

    +1 -1 (+2 / -0 ) Share on Facebook
  • @pie is the end result to get light areas on the map that follow the players around?

    Maybe the easiest and fastest way of doing this is to draw everything normally, then have a low resolution (but with filtering turned on) rendertarget that is a fraction of the screen display size. First clear the render target as black, then punched out of it the light positions in white depending on where the light sources in your game will appear on screen (as a fraction). Then draw the render target on the screen scaled up to the full size of the screen (don't forget it was created as a fraction of the screen) - but use the multiply option.

    The multiply option will multiply areas that are black in the rendertarget by 0, but let light through on bits that are not black in the rendertarget. You can add hundreds of light sources on screen at once (I use 16 in Dungeons). The filter and fractional screen make the multiply vary so it's nicely shaded.

    If you want thousands of light sources, then you can do a variation by using two more rendertargets, make them a fraction of 4 screens (or less) square. Build them up alternately over a number of frames depending on where the scroll position of your map is. Now draw that on your original rendertarget first rather than clearing with black. Swap the new rendertargets as they have been built.
    Coder, video game industry veteran (since the '80s, ❤'s assembler), arrested - never convicted hacker (in the '90s), dad of five, he/him (if that even matters!).
    https://deluxepixel.com
  • SinisterSoftSinisterSoft Maintainer
    edited April 2016
    Nico: I will try out this shader, see if it's faster in my case.

    Do you have the directx (hlsl?) variation on your code - so we can see what changes would have to be made for compatibility with everything?
    Coder, video game industry veteran (since the '80s, ❤'s assembler), arrested - never convicted hacker (in the '90s), dad of five, he/him (if that even matters!).
    https://deluxepixel.com
  • hgy29hgy29 Maintainer
    Anthony: Beware the this shader is intended for normal mapping, i.e. giving the impression of volume by changing the way light is rendered depending on the surface theoretical orientation. Think of it as 'emboss' effect. If you only need lighting, then it could be hugely simplified.

    And no, I don't have the HLSL counterpart, though it shouldn't be hard to translate.
  • Ahh - I see. :)
    Coder, video game industry veteran (since the '80s, ❤'s assembler), arrested - never convicted hacker (in the '90s), dad of five, he/him (if that even matters!).
    https://deluxepixel.com
  • piepie Member
    edited April 2016
    @Sinistersoft I already did the black rendertarget with your help some months ago :) but I am fascinated by bump effects and fake 3d: it really is a game changer with the correct normal maps (look at the water tiles in my previous screenshot - or in attached video when it works :D ).

    @hgy29 thanks a lot for the new shader, I am going to do some tests, however at first glance it seems much more "unstable" or "heavy" than the single light source (sometimes the map flicker or disappear for some ms) [edit: rebooting windows made it work flawlessly] and when the sources overlap there is too much light :)



    It is still a bit obscure to me how to change light vector direction: now it comes from down and it's pointing up-left (from a screen pov) changing the 150 changes the light "altitude", but which parameters control its "tilt" and "rotation" factor"? (my maths ignorance comes out again... ;) )
    //Compute light direction, assuming light distance of 150 units
    		mediump vec3 lightDir = normalize(vec3(lightPos[index].xy, 150) - vec3(position.xy, 0));

    However, you did a great job with shaders:
    The biggest improvement I can think about this is the possibility to set a normalmap for each loaded texture (with fallback if none), and let gideros do the thinking about normals if the effect is applied to a sprite containing textured objects with normalmaps. If you think that it's possible to do it, I will open a formal request on github :D

    Thank you again
  • tkhnomantkhnoman Member
    edited April 2016
    @pie : I think, about possibility to set a normalmap by Gideros is a bad idea. It's better if it's just an added code, like GTween or such. It's not good to have non-changeable normalmap code.

    Mine is modified much, i didn't use dot function, but distance instead.
    I also change blue parameter not as z, but as how strong object reflect light, that way i can differentiate between metal / stone.

    Just wondering, which device that flicker appear at?
    I don't have any flicker (at low spec PC) at 16 source of light, with much more calculation.
    It should be become lag if it to heavy, i did heavy calculation before at another shader function and it drop the frame rate, not flicker like this.

    Did you create mesh/render and use it right away (not preparing it at the first run)?
    If yes, probably its better to try create them first at first run.


    Also, if you want "not to much light if light overlap", you need to modify the code yourself (like giving the maximum IF value).
    Changing direction of the light would be advanced one, you need to do much more calculation for that, and not just changing a variable. The lights should have more parameter, not just how strong it is, but also something like light rotation and light reaching distance.

    But creating shadow, i think, would be heavy calculation if done in shader. Probably better to seperate the shadow calculation like how SinisterSoft said.
  • piepie Member
    edited April 2016
    Thank you, I am sorry to ask so many things but it's the first time that I approach this matter.

    The flickering is gone after rebooting my laptop, I suppose something went wrong playing with shaders on my system. Sorry for the false alarm :)

    I agree with you, I like a lot shader customization (though I am still not being able to do much on my own).
    What I was speaking about was the possibility to use shaders directly on a Sprite (as I would do with gtween) without using rendertarget and a mesh any time the sprite changes. I realize that this probably means that gideros has to manage the same things by its own.. However if this means to "make normal map unchangeable" of course it's not worth it. :)

    Back to my experiments, I managed to set light sources on/off sending a parameter in a Shader.CINT uniform, but I am unable to set parameters sending in an array. I think I am missing something about data types:


    this is the relevant code:

    lua call
    effect:setConstant("lightList", Shader.CINT, 2, 0, 1)
    ps
    //if I write it "plain" it works
    uniform mediump int lightList[NUM_LIGHTS]; 
     
    if (lightList[index] == 1 ) 
    			{
    ...
     
    }
    output:
    working.


    lua call
    effect:setConstant("lightList", Shader.CINT, 2, 0,30, 1,170)
    ps
    //if I send a vec2 array it won't work
    //here I'd like to store {onoff, parameter }
    uniform mediump vec2 lightList[NUM_LIGHTS]; 
     
    if (lightList[index].x == 1 ) 
    			{
    ...
     
    }
    output:
    no errors in output window but black screen


    lua call
    effect:setConstant("lightList", Shader.CINT, 2, 0,30, 1,170)
    ps
    uniform mediump int lightList[NUM_LIGHTS];
     
    if (lightList[index][0] == 1 ) 
    			{
    ...
    }
    output:
    ERROR: 0:38: 'expression' : left of '[' is not of type array, matrix, or vector
    ERROR: 0:41: 'expression' : left of '[' is not of type array, matrix, or vector

    What is the correct syntax to do it?
    Is vec2 wrong since my xy are "unrelated"?



    Thank you
  • hgy29hgy29 Maintainer
    Hi @pie,

    You're mixing data types and array sizes.
    If you use "mediump vec2 xxx[n]", in your second example,then your data type is FLOAT, with 2*n values, while you passed int values to the shader from lua, and only two values (not 4).

    If you use "mediump int xxx[n]", in your thirs example, then your data type is INT, with n values, not a double dimension array.

    Sorry I don't have much time to elaborate tonight, hope this shed some light.

    Likes: pie

    +1 -1 (+1 / -0 ) Share on Facebook
  • piepie Member
    Thanks, at least confirmed my feelings: I need to understand better data types and vec values :)

    I would like to send 3 parameters to shader in realtime, in addition to coordinates, and I saw that I can use method 1 from above (creating a uniform variable for each parameter I want to set).
    Is there a more elegant (and maybe performing) way to do the same thing? if I could change everything in one call I think it would be better than calling 3 times setConstant.

    Thank you
  • tkhnomantkhnoman Member
    edited April 2016
    not sure why you like to use "CINT", why not CFLOAT?
    {name="lightPos",type=Shader.CFLOAT4,mult=_lightCount,vertex=false}
    .
    .
     
    local tableForLight = {0,0,0,0,0,0,0,0,	0,0,0,0,	0,0,0,0,	
    0,0,0,0,0,0,0,0,	0,0,0,0,	0,0,0,0,
    0,0,0,0,0,0,0,0,	0,0,0,0,	0,0,0,0,
    0,0,0,0,0,0,0,0,	0,0,0,0,	0,0,0,0,}
    .
    . setting each param
    .
     
    effect:setConstant("lightPos", Shader.CFLOAT4, _lightCount, tableForLight)
    .
    .
    uniform lowp vec4 lightPos[_lightCount];
    Of course, it is limited to 4 parameter, but doing setConstant multiple times is no different from doing this.
  • piepie Member
    I don't know why I use CINT.. should I use CFLOAT? :-S I just thought "integer" -> CINT

    Thanks for the snippet, however if there is no difference from doing setConstant multiple times, it seems better to stick with the old method. At least it is easier to read :)

  • keszeghkeszegh Member
    edited February 2017
    @hgy29 and others, i hope my question fits this topic.

    basically i need an example of how to have a lighted 3d scene (with one light for now).

    optimally, i'd like to see a new gideros example project which does the following:
    having a 3d object (preferably a random polygonal terrain we see from above)
    and a light source (above the terrain) which follows the cursor movement.

    of course it would be silly to request such a thing, and if you give enough help i may try to do this on my own (which you can add to gideros examples if it works out well), but so far i have no knowledge about shaders at all, so i don't even know where to start.

    of course generating the terrain i can probably do, so i just need to know how to handle a light source.

    thanks a lot
  • hgy29hgy29 Maintainer
    Hi is this something like this that you need ?
    zip
    zip
    LitTerrain.zip
    6K
  • something like, indeed. amazing, thanks for your efforts. i will ask about some details in pm.
  • thanks, getting rid of lines 215-233 made it look exactly like i wanted.
    can you tell me what those lines do?

    next i will try to cut out all the code i don't need, given that my mesh is already generated, so i only need to add normals. btw it's interesting that although the mesh face is already in 3d you can set its normal different from orthogonal to the face? what's the rationaly behind this?

    all in all that's enough for me to work out the rest, if i manage to put it into my app Fragmenter so that it performs and looks well etc. then i will let you know. probably it will come later as there are many other more basic things that have to be done with it.

  • hgy29hgy29 Maintainer
    those lines do the smoothing: for each vertice, a normal is computed as the mean value of all face's normals this vertice is included in.
    Then those per vertex normals are interpolated for each pixel of the face, which the impression that the surface is curved when lit.

    I suppose a 3D guy would explain this better than me :)

    Likes: pie

    +1 -1 (+1 / -0 ) Share on Facebook
  • i see, so the vertices (or rather the indices) have normals defined on them and not the faces. and they together fake curvature. makes sense.
Sign In or Register to comment.