Forum

Is really bemode used in FTE?!?

Discuss programming topics for the various GPL'd game engine sources.

Moderator: InsideQC Admins

Is really bemode used in FTE?!?

Postby toneddu2000 » Sat Mar 31, 2018 7:45 pm

Hy guys, I'm tinkering with GLSL (and it's incredible how much you can learn from GLSL) and I'm trying to understand how FTE handles various passes (diffuse, depth, shadowmap,rtlight,etc.)
Here Spike explained his terrain shader and how bemode blocks should encompass every pass, for example:

Spike wrote here
Spike wrote:bemode blocks (which will masively confuse DP) are actually separate shaders embedded within the parent shader. these embedded shaders are used for the different backend modes. depthonly is used for a few things, including shadows. depthdark is used as an optimisation when r_shadow_realtime_world_lightmaps (wow long cvar) is set to 0. while the rtlight backend mode is used when drawing rtlights (and should thus be additively blended).


Code: Select all
bemode rtlight
{
  diffusemap textures/foo.tga
  program defaultwall
}

This should define realtime lights behaviour

instead:
Code: Select all
bemode depthdark
{
  program depthonly
  {
    depthwrite
  }
}

This should be used for example for shadows.
Code: Select all
bemode depthonly
{
  program depthonly
  {
    depthwrite
    maskcolor
  }
}

This one should be similar to depthdark

The weird thing is the function that should parse bemode is static void Shader_BEMode(shader_t *shader, shaderpass_t *pass, char **ptr) in gl/gl_shader.c, but, it's only called (without arguments) in shaderkeys array at line
Code: Select all
{"bemode",            Shader_BEMode,            "fte"},

,but how is it possible that arguments are not included? I'm a completely C rookie, so it may be weird for me, but an usual habit for any real programmer.

Anyway, I still can't understand how to set bemode to, for example, write shadow colors blue.

I infact created a shader like this
Code: Select all
scene_old
{
   bemode rtlight
   {
      diffusemap textures/env/red.tga
      program defaultwall
   }
   bemode depthdark
   {
      program eg_depthonly2
      {
         depthwrite
      }
   }
   bemode depthonly
   {
      program eg_depthonly2
      {
         depthwrite
         maskcolor
      }
   }
   program defaultwall
}


eg_depthonly2 is just a modified version of eg_depthonly
Code: Select all
!!ver 100 150
!!permu FRAMEBLEND
!!permu SKELETAL
#include "sys/defs.h"
#ifdef VERTEX_SHADER
#include "sys/skeletal.h"
void main ()
{
gl_Position = skeletaltransform();
}
#endif
#ifdef FRAGMENT_SHADER
void main ()
{
gl_FragColor = vec4(0, 0, 1, 1);
}
#endif


But bemode never takes part in rendering process. If, for example, I write a simple rtlight shader
Code: Select all
scene
{
   bemode rtlight
   {
      diffusemap textures/env/red.tga
      program defaultwall
   }
}

Engine will complain that no pass or surfaceparm is inserted so it will be ignored and engine will add a pass (it doesn't say which one, maybe diffuse) for you. But, as you can see, a diffusemap call has been added and a program directive is inserted below. The warning will go off only if:
a) either you add a simple madeup name after rtlight, as
Code: Select all
bemode rtlight foo

b) or you add a program directive OUTSIDE bemode block, which, to me, kinda makes little sense, so..why should I use a block if the only shader taken in consideration it's OUTSIDE of that block?!?

about this argument, Spike wrote here
Spike wrote:internally the engine has a shader override system, this includes stuff like the rtlight shader. these shaders 'inherit' their named s_diffuse/$diffuse etc textures from the original shader that they are replacing, which is how fte uses one rtlight shader (with permutations based upon texture availability) for pretty much everything.
naturally you can use 'bemode rtlight othershader' to specify a different override for the rtlight passes. there's a few other backend modes that you can provide alternative shaders for like that. the terrain system does this too, of course (which uses the lightmap to mix 4 underlying textures, which means it can't use the regular rtlight logic as that assumes there is only a single diffusemap).


and infact, eg_terrain.glsl uses:
Code: Select all
uniform sampler2D s_t0;
uniform sampler2D s_t1;
uniform sampler2D s_t2;
uniform sampler2D s_t3;
uniform sampler2D s_t4;

uses 4 different diffuse textures, (s_t4 is the lightmap)

So.. could it be possible to create a mydiffusepass shader containing a glsl that manipulates only diffuse + rtlight pass, then another mydepthpass shader that control depth pass and then calling all of them inside a new shader like this?
Code: Select all
scene
{
   bemode rtlight mydiffusepass
   {
      diffusemap textures/env/red.tga
      program mydiffusepass
   }
   bemode depthdark mydepthpass
   {
      program mydepthpass
      {
         depthwrite
      }
   }
   bemode depthonly mydepthonlypass
   {
      program mydepthonlypass
      {
         depthwrite
         maskcolor
      }
   }
}


So, I ask help to Spike and any other FTE engine devs to shed some lights on this topic, because I couldn't find anywhere a detailed explanation how to setup complex shader passes in FTE.
This could lead, from my perspective ,to a very interesting discussion. :mrgreen:

Thanks A LOT in advance to anyone! :biggrin:
toneddu2000
 
Posts: 1318
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Is really bemode used in FTE?!?

Postby toneddu2000 » Sat Mar 31, 2018 11:53 pm

kinda working now. The issue was because I was using diffusemap and not map directive

This example uses custom rtlight shader that switches 4 different diffuse textures and custom shadow shader to change shadowcolor
Code: Select all
scene
{
   //rtlight
   bemode rtlight ton_rtlight
   {
      //change #TEX1 to #TEX4 to switch textures
      program ton_rtlight#TEX1
      map textures/env/red.tga
      map textures/env/green.tga
      map textures/env/yellow.tga
      map textures/env/grey.tga
   }
   //shadow color
   bemode depthonly ton_shdwcolor
   {
      program ton_shdwcolor
      {
         blendfunc add
         depthwrite
         colormask
      }
   }
}


GLSL ton_rtlight
Code: Select all
!!permu FOG
#include "sys/fog.h"
varying vec2 tc;
varying vec4 vc;
varying vec3 lightvector;
uniform vec3 l_lightposition;
#ifdef VERTEX_SHADER
attribute vec2 v_texcoord;
attribute vec2 v_lmcoord;
attribute vec4 v_colour;
void main (void)
{
   tc = v_texcoord.st;
   vc = v_colour;
   gl_Position = ftetransform();
   vec3 lightminusvertex = l_lightposition - v_position.xyz;
   lightvector = lightminusvertex;
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0;
uniform sampler2D s_t1;
uniform sampler2D s_t2;
uniform sampler2D s_t3;
uniform vec4 e_lmscale;
uniform float l_lightradius;
uniform vec3 l_lightcolour;
uniform vec3 l_lightcolourscale;
void main (void)
{
   vec4 tex1  = texture2D(s_t0, tc);
   vec4 tex2  = texture2D(s_t1, tc);
   vec4 tex3 = texture2D(s_t2, tc);
   vec4 tex4  = texture2D(s_t3, tc);
   vec3 nl = normalize(lightvector);
   float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);
   colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
   //choose image used from s_t0,s_t1,etc.
   #if defined(TEX1)
      vec4 finalimg = tex1;
   #elif defined(TEX2)
      vec4 finalimg = tex2;
   #elif defined(TEX3)
      vec4 finalimg = tex3;
   #elif defined(TEX4)
      vec4 finalimg = tex4;
   #else
      vec4 finalimg = vec4(0.5,0.3,0.6,1);//fallback default color
   #endif
   finalimg.rgb *= colorscale * l_lightcolour;
   gl_FragColor = fog4additive(finalimg);
}
#endif


GLSL ton_shdwcolor
Code: Select all
!!ver 100 150
!!permu FRAMEBLEND
#include "sys/defs.h"
varying vec2 tc;
#ifdef VERTEX_SHADER
void main ()
{
   tc = v_texcoord.st;
   gl_Position = ftetransform();
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0;
void main ()
{
   vec4 imgshdw = texture2D(s_t0, tc);
   float zr = 1.0-texture( s_t0, tc ).x;
   gl_FragColor = vec4(0, 0.05, 0.1, 0.2);
}
#endif


Shadow color was just an example. Now I'd like to know how to extract depth buffer. Should I use
Code: Select all
bemode depthonly ton_shdwcolor
   {
      program ton_shdwcolor
      {
         map $sourcedepth
      }
   }

and in GLSL use float zr = 1.0-texture( s_t0, tc ).x; (I kinda remember that Spike told me once that depth is store in .r swizzle value)??
But still I didn't understand what is the real potential of shaders in FTE! :biggrin:
toneddu2000
 
Posts: 1318
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Is really bemode used in FTE?!?

Postby Spike » Sun Apr 01, 2018 12:09 am

Code: Select all
MyShader
{
   //used for forward-rendered rtlights. additively blends according to the surface, drawn per-light.
   bemode rtlight
   {
      {
         //woo, one glsl to rule them all
         program terrain#RTLIGHT
         blendfunc add
         //FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse
         map $diffuse
         map $upperoverlay
         map $loweroverlay
         map $fullbright
         map $lightmap
         map $shadowmap
         map $lightcubemap
      }
   }

   //used when r_shadow_realtime_world_lightmaps is 0
   bemode depthdark
   {
      program depthonly
      {
         depthwrite
      }
   }

   //used for shadowmaps. doesn't need to be complex as there's no blending so nothing fancy beyond soup.
   bemode depthonly
   {
      {
         program depthonly
         depthwrite
         maskcolor
      }
   }

   //Standard static-lit non-bemode version starts here.
   {
      program terrain
      if r_terraindebug
         program terraindebug
      endif
      rgbgen vertex
      alphagen vertex
      //FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse
      map $diffuse
      map $upperoverlay
      map $loweroverlay
      map $fullbright
      map $lightmap
   }
}

I've cleaned it up so that the maps are all in a single subpass instead of the older messier style.
its worth noting that you should also be able to just remove the map lines entirely, as any such textures would normally be implicitly selected by the glsl.
the glsl in question blends the diffuse+upper+lower+fullbright textures according to the lightmap's rgb channels, with shadows coming from the lightmap's alpha channel - fte's terrain system has fancy special-case lightmaps.

so...

bemode blocks define an entirely separate shader (but NOT material - ie: diffuse etc come from the outer shader, while subpasses/programs come from the inner/bemode shader). those alternative shaders provide logic that is used when the regular shader isn't specific enough - eg normally the rtlight shader is some lame hardcoded thing that usually works, but might not be right in your situation.

a program line outside of a subpass cause all subpasses in that shader to be merged (with only the first subpass's blendmode etc being used. programs inside of a subpass allows you to chain multiple passes - and multiple maps inside that subpass must be merged.
there's not a big difference in the long run, you may find that programs outside makes it a little easier to cope with drivers that don't support glsl, but otherwise its cleaner to use the form that has the program lines inside the relevant subpasses.

performance-wise, you want as few un-merged subpasses as you can. also avoid using tcgens etc that require the cpu to think about each vertex - its better to get the glsl to pull the data from the relevant vbos and apply whatever modifiers needed.
beware of using too many different bits of glsl with different permutations. drivers will generally give you terrible load times if you do it.

diffusemap etc controls which textures are used for 'map $diffuse', as part of the material rather than the shader. they must be used outside of any subpasses, and are utterly useless inside of sub-shaders too, so do NOT use them inside any bemode blocks.

bemode with 3 tokens says to use a shader named by that 3rd token. any blocks following a 3-token bemode line are NOT part of that bemode, and will be treated as subpasses of the main shader. which will just weird stuff out, because you've got too many indentation levels going on for that (note that this is consistent with 1 and 2-token program lines - the block following program lines is used ONLY for the 1-token form - and is quite a bad practice as it means that those programs will not be shared between dupes of the containing shader etc).

not documented:
bemode gbuffer - this pass says how to render the geometry into the initial gbuffers. the prelights will then be run on those gbuffers, and the 'standard' version of the shader is then run and expected to read the gbuffers+lights, which requires special checks (eg: 'if lpp'), which kinda sucks.


so to recap:
a 'shader' is both a material and a shader. the material part says which textures to use while the shader part says what to do with them. the shader can be remapped with r_remapshader, or bemodes, or forceshader, or whatever to shade it differently. shaders can still use their own textures too of course. q3-compatible shaders do not really support the material part - the difference is that of 'map $diffuse' or glsl's s_diffuse existing in fte. or something.
Spike
 
Posts: 2883
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Is really bemode used in FTE?!?

Postby toneddu2000 » Sun Apr 01, 2018 9:17 pm

I've cleaned it up so that the maps are all in a single subpass instead of the older messier style.
its worth noting that you should also be able to just remove the map lines entirely, as any such textures would normally be implicitly selected by the glsl.
the glsl in question blends the diffuse+upper+lower+fullbright textures according to the lightmap's rgb channels, with shadows coming from the lightmap's alpha channel - fte's terrain system has fancy special-case lightmaps.
Thanks a lot Spike, this is really a lot cleaner, and now I think I'm quite grasping the concept of material + shader + glsl in FTE.

The problem that it's not working! :)

Before going in depth with the shader structure, I'd like to ask you to explain the map $diffuse syntax
I noticed that, to use $diffuse syntax I needed to put it in curly braces, otherwise it won't work. So, for example

Code: Select all
mymat
{
  map $diffuse //this doesn't work
}

myothermat
{
  {
    map $diffuse // this will work
  }
}

But, again, to use this syntax your shadername needs to be like textures/foo/blah and for diffuse texture name you need to create a texture blah.tga in the texture/foo subfolder or otherwise your textures should be stored in the main folder..
This is not very elegant imo but it's not the real problem. The problem is that if you create 2 materials
Code: Select all
mymat
{
  diffusemap textures/foo/blah.tga
}

textures/foo/blah
{
  {
    map $diffuse
  }
}

The latter won't have dynamic light, instead the first will, even if it's not defined anyway "program rtlight"

Another problem is, with {map $diffuse block}, you need to name every texture with the diffuse texture prefix, for example blah_gloss.tga, blah_norm.tga, etc.
Instead, with diffusemap syntax you can share different textures even if they are not in the same folder and not using any particular convention. For example:
Code: Select all
mymat
{
  diffusemap textures/foo/blah.tga
  specularmap textures/shared/specular_reflex.tga
  normalmap textures/otherfolder/mynorm.tga
  //and so on
}


So, my question is: is it possible to use the modern syntax (diffusemap, specularmap,etc.) inside shaders?
And, most of all
diffusemap etc controls which textures are used for 'map $diffuse', as part of the material rather than the shader. they must be used outside of any subpasses, and are utterly useless inside of sub-shaders too, so do NOT use them inside any bemode blocks.
So why did you put all that map blocks inside the bemode rtlight? Are the map blocks different from diffusemap stuff?
When did you write " //FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse" do you mean that are they there only because the shader doesn't have a s_diffuse sample2D but only s_t0,s_t1,etc.? So, they're not there for modifing textures but just to tell the shader to DEFINE textures? right? In that case I could use only glsl files that have s_diffuse, s_specular,etc sampler2d

Anyway I also remapped your code putting extra curly braces
Code: Select all
textures/test/plane
{
   //used for forward-rendered rtlights. additively blends according to the surface, drawn per-light.
   bemode rtlight
   {
      {
         //woo, one glsl to rule them all
         program terrain#RTLIGHT
         blendfunc add
         //FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse
       {
         map $diffuse
       }
         {
         map $upperoverlay
       }
       {
         map $loweroverlay
       }
       {
         map $fullbright
       }
       {
         map $lightmap
       }
       {
         map $shadowmap
       }
       {
         map $lightcubemap
       }
      }
   }

   //used when r_shadow_realtime_world_lightmaps is 0
   bemode depthdark
   {
      program depthonly
      {
         depthwrite
      }
   }

   //used for shadowmaps. doesn't need to be complex as there's no blending so nothing fancy beyond soup.
   bemode depthonly
   {
      {
         program depthonly
         depthwrite
         maskcolor
      }
   }

   //Standard static-lit non-bemode version starts here.
   {
      program terrain
      if r_terraindebug
         program terraindebug
      endif
      rgbgen vertex
      alphagen vertex
      //FIXME: these maps are a legacy thing, and could be removed if third-party glsl properly contains s_diffuse
      {
         map $diffuse
       }
         {
         map $upperoverlay
       }
       {
         map $loweroverlay
       }
       {
         map $fullbright
       }
       {
         map $lightmap
       }
   }
}

But model is always black, no warnings, but it's not rendered. Does it need to be a terrain object to render? Anyway I also replaced program terrain with program eg_rtlight but it's always black. using program with my ton_rtlight.glsl
Code: Select all
!!permu FOG
#include "sys/fog.h"
varying vec2 tc;
varying vec4 vc;
varying vec3 lightvector;
uniform vec3 l_lightposition;
#ifdef VERTEX_SHADER
attribute vec2 v_texcoord;
attribute vec2 v_lmcoord;
attribute vec4 v_colour;
void main (void)
{
   tc = v_texcoord.st;
   vc = v_colour;
   gl_Position = ftetransform();
   vec3 lightminusvertex = l_lightposition - v_position.xyz;
   lightvector = lightminusvertex;
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_t0;
uniform sampler2D s_t1;
uniform sampler2D s_t2;
uniform sampler2D s_t3;
uniform sampler2D s_diffuse;
uniform vec4 e_lmscale;
uniform float l_lightradius;
uniform vec3 l_lightcolour;
uniform vec3 l_lightcolourscale;
void main (void)
{
   vec4 tex1  = texture2D(s_t0, tc);
   vec4 tex2  = texture2D(s_t1, tc);
   vec4 tex3 = texture2D(s_t2, tc);
   vec4 tex4  = texture2D(s_t3, tc);
   vec4 diff  = texture2D(s_diffuse,tc);
   vec3 nl = normalize(lightvector);
   float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);
   colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
   //choose image used from s_t0,s_t1,etc.
   #if defined(TEX1)
      vec4 finalimg = tex1;
   #elif defined(TEX2)
      vec4 finalimg = tex2;
   #elif defined(TEX3)
      vec4 finalimg = tex3;
   #elif defined(TEX4)
      vec4 finalimg = tex4;
   #else
      vec4 finalimg = diff;//use diffuse texture as fallback
   #endif
   finalimg.rgb *= colorscale * l_lightcolour;
   gl_FragColor = fog4additive(finalimg);
}
#endif

It renders object as rtlights were off, so it skips bemode rtlight block and it goes directly to the last static lit block

Another question: does it possible to access depth buffer in depthonly or depthdark bemode block? Because I really needed for ambient occlusion or SSAO if bemode could be used in post-processing shaders. I tried with {map $sourcedepth} but I don't know how to manage it

Thanks a lot for your clarifications and sorry for the avalanche of words!! :mrgreen:
toneddu2000
 
Posts: 1318
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Is really bemode used in FTE?!?

Postby toneddu2000 » Tue Apr 03, 2018 4:37 pm

ok, let's go baby steps.

BEWARE
map command needs curly braces. So:
Code: Select all
myshader
{
   map textures/foo.tga // this won't work
   {
      map textures/foo.tga // this will work
   }
}


NOMENCLATURE:
material - block of code in scripts folder, in .shader file that starts with a name enclosed by curly braces and including directive to textures
Code: Select all
myshader
{
diffusemap pathtotexture
specularmap pathtotexture
//and so on
}


shader logic code inside material block, after texture definitions. it should define the logic of how textures react with colors, lights, etc.
//above this line there's texture definitions written above
program myglslfile

GLSL file glsl code file inside glsl folder. It defines complex operations with textures and colors. It's splitted in 2 segments (vertex and fragment)
//above this line there's texture definitions written above
program myglslfile

bemode (BackEnd Mode) block logic block of code inside shader that defines every pass, or at least this is what I understood. There's one per pass, I guess. This is the struct containing all the bemodes:
Code: Select all
typedef enum backendmode_e
{
   BEM_STANDARD,         //regular mode to draw surfaces akin to q3 (aka: legacy mode). lightmaps+delux+ambient
   BEM_DEPTHONLY,         //just a quick depth pass. textures used only for alpha test (shadowmaps).
   BEM_WIREFRAME,         //for debugging or something
   BEM_STENCIL,         //used for drawing shadow volumes to the stencil buffer.
   BEM_DEPTHDARK,         //a quick depth pass. textures used only for alpha test. additive textures still shown as normal.
   BEM_CREPUSCULAR,      //sky is special, everything else completely black
   BEM_GBUFFER,         //
   BEM_FOG,            //drawing a fog volume
   BEM_LIGHT,            //we have a valid light
} backendmode_t;

gbuffer is probably the coolest, because it could be used to retrieve depth and normals, but I really no idea how to use it!:)
no wait I have! This is a wall shader using deferred light
Code: Select all
deferred_wall
{
   {
      {
         deferredlight
         fte_program ton_lppwall#OFFSETMAPPING
         map $gbuffer2
         map $gbuffer3
         map $diffuse
         map $normalmap
         map $specular
      }
      //this is drawn during the initial gbuffer pass to prepare it
      fte_bemode gbuffer
      {
         {
            fte_program lpp_depthnorm
            tcgen base
         }
      }
      
   }
}

Don't ask me how it works because Spike gave it to me, so it's probably better ask him

MATERIAL START
Let's say I want to create a simple rtlight material that receives and projects realtime diffuse lights + shadows.
By realtime lights I mean a dynamic light added by code in CSQC using
Code: Select all
ltest = dynamiclight_add([cvar("render_light_posx"),cvar("render_light_posy"),cvar("render_light_posz")],1000,[1,1,1],0,"",0);

I used cvars because I think it's a very quick and dirty way to tweak stuff

I created a simple glsl file
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/test/specular_tiles.tga
   normalmap textures/test/normal_tiles.tga
}

Now plane looks like this:
Image
everything seems ok, dynamic lights(received and projected), specular is controlled by texture and by cvar r_shadow_realtime_dlight_specular and normal maps work, even if I didn't specify any GLSL file using the program directive.
So it must be the engine that finds the material name and it says:ok, no "surfaceparm nodlight" found, so it must be a realtime light material.

Now it seems that works right out the box, so using bemode blocks seems unnecessary but, if someone wants to do complex stuff, you're kinda stranded.
Because if I add an rtlight block below textures declarations
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/test/specular_tiles.tga
   normalmap textures/test/normal_tiles.tga
   //second bemode for realtime lights
   bemode rtlight
   {
      program ton_rtlight#DEBUG
   }
}

Code: Select all
!!permu FOG
#include "sys/fog.h"
varying vec2 tc;
varying vec4 vc;
varying vec3 lightvector;
uniform vec3 l_lightposition;
#ifdef VERTEX_SHADER
attribute vec2 v_texcoord;
attribute vec2 v_lmcoord;
attribute vec4 v_colour;
void main (void)
{
   tc = v_texcoord.st;
   vc = v_colour;
   gl_Position = ftetransform();
   vec3 lightminusvertex = l_lightposition - v_position.xyz;
   lightvector = lightminusvertex;
}
#endif
#ifdef FRAGMENT_SHADER
uniform sampler2D s_diffuse;
uniform vec4 e_lmscale;
uniform float l_lightradius;
uniform vec3 l_lightcolour;
uniform vec3 l_lightcolourscale;
void main (void)
{
   vec4 diff  = texture2D(s_diffuse,tc);
   vec3 nl = normalize(lightvector);
   float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);
   colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
   diff.rgb *= colorscale * l_lightcolour;
   // adding a little tint just to be sure the glsl file has been loaded
   #ifdef DEBUG
   diff.rgb *= vec3(0.3,0,0);
   #endif
   gl_FragColor = fog4additive(diff);
}
#endif

GLSL file ton_rtlight is the following. Very easy. It takes realtime light and blend it with diffuse texture.
You could also use change program ton_rtlight with program defaultwall, which it's an engine glsl embedded file, but, for this test, I don't reccomend it.
Because, this simple glsl modify diffuse texture color adding a little of red, so you'll understand when GLSL file has been loaded.
That's because sometimes you might not recognise if GLSL is loaded or not, since the engine does (apparently) the realtime lighting by default
rememember to call it using program ton_rtlight#DEBUG, the #DEBUG condition will tint the objects red so you'll know if material is applied

Image
If you test the model now, you'll see that dynamic shadows, specular and normal maps are gone!
Diffuse texture is loaded.
Light intensity is very low but it decrease from the center to the edges (so it's ok).
But, at least, it tints my plane and sphere red, so we are sure the glsl file has been loaded.

Ok, just use double indentation adding a pair of curly braces to surround program directive as Spike did in the example above
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/test/specular_tiles.tga
   normalmap textures/test/normal_tiles.tga
   //second bemode for realtime lights
   bemode rtlight
   {
      {
         program ton_rtlight#DEBUG
      }
   }
}

The effect is interesting(!)
Image
it seems that GLSL has not loaded at all, since red tint is gone, no shadows but textures are loaded and light is..weird

So I'll stick with no double curly braces in bemode block and let's see why there's no shadow projected.

But first let's add normal and specular. I understood that GLSL has to use sampler2D s_normalmap and s_specular and mix them with s_diffuse to produce final image, otherwise, and that's quite obvious, only diffuse pass will be rendered.
Ok so this is the modified ton_rtlight glsl file with normal and specular map support.
It's not fancy like defaultwall shader (from which I stole the specular vector part) but it should do the work.
Code: Select all
!!permu FOG
!!permu BUMP
!!permu SPECULAR
!!samps diffuse specular normalmap
!!cvarf gl_specular
!!cvarf gl_specular_power
#include "sys/defs.h"
#include "sys/fog.h"
varying vec2 tc;
varying vec4 vc,tf;
varying vec3 lightvector;
varying vec3 eyevector;
#ifdef VERTEX_SHADER
void main (void)
{
   //specular
   vec3 eyeminusvertex = e_eyepos - v_position.xyz;
   eyevector.x = dot(eyeminusvertex, v_svector.xyz);
   eyevector.y = dot(eyeminusvertex, v_tvector.xyz);
   eyevector.z = dot(eyeminusvertex, v_normal.xyz);
   //diffuse
   tc = v_texcoord.st;
   vc = v_colour;
   tf = ftetransform();
   gl_Position = tf;
   vec3 lightminusvertex = l_lightposition - v_position.xyz;
   lightvector = lightminusvertex;
}
#endif
#ifdef FRAGMENT_SHADER
uniform float cvar_gl_specular,cvar_gl_specular_power;
void main (void)
{
   vec4 mapdiff  = texture2D(s_diffuse,tc);
   vec3 mapnormal = normalize(texture2D(s_normalmap, tc).rgb);
   vec4 mapspec = texture2D(s_specular,tc);
   vec3 nl = normalize(lightvector);
   float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);
   colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
   mapdiff.rgb *= colorscale * l_lightcolour;
   #ifdef DEBUG
   // adding a little tint just to be sure the glsl file has been loaded
   mapdiff.rgb *= vec3(0.3,0,0);
   #endif
   gl_FragColor = mapdiff;
   #ifdef NORMAL
   gl_FragColor.rgb *= mapnormal.rgb;
   #endif
   #ifdef SPECULAR
   //stolen from engine/shaders/glsl/defaultwall.glsl
   vec3 halfdir = normalize(normalize(eyevector));
   float spec = pow(max(dot(halfdir, mapnormal), 0.0), cvar_gl_specular_power * mapspec.a);
   spec *= cvar_gl_specular;
   gl_FragColor.rgb += spec * mapspec.rgb;
   #endif
   gl_FragColor = fog4(gl_FragColor);
}
#endif

Ok now the material is like this(note the #NORMAL and #SPEC condition: if material doesn't have a specular or normal texture, just omit them):
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/test/specular_tiles.tga
   normalmap textures/test/normal_tiles.tga
   //second bemode for realtime lights
   bemode rtlight
   {
      program ton_rtlight#DEBUG#NORMAL#SPEC
   }
}

It's quite perfect.
Image
Ok, now for the shadow part. I tried to add a second bemode block with depthonly as Spike suggested
Code: Select all
//used for shadowmaps. doesn't need to be complex as there's no blending so nothing fancy beyond soup.
bemode depthonly
{
   {
      program depthonly
      depthwrite
      maskcolor
   }
}

But nothing changes.
So I copied from engine rtlight.glsl this code
Code: Select all
//vtexprojcoord needs to be placed as global varying vec4;
//VERTEX_SHADER at the bottom
vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));
//FRAGMENT_SHADER before multipling colorscale to diffuse
colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);


And..kaboom!
Image
Now material has diffuse,specular,normal and realtime shadows!

So, here's the final shader
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/test/specular_grid.tga
   normalmap textures/test/normal_tiles.tga
   {
      map $shadowmap
   }
   //second bemode for realtime lights
   bemode rtlight
   {
      {
         map $shadowmap
      }
      program ton_rtlight#DEBUG#NORMAL#SPEC
   }
}

here's the final GLSL
Code: Select all
!!permu FOG
!!permu BUMP
!!permu SPECULAR
!!samps diffuse specular normalmap
!!cvarf gl_specular
!!cvarf gl_specular_power
#include "sys/defs.h"
#include "sys/pcf.h"
#include "sys/fog.h"
varying vec2 tc;
varying vec4 vtexprojcoord;
varying vec4 vc,tf;
varying vec3 lightvector;
varying vec3 eyevector;
#ifdef VERTEX_SHADER
void main (void)
{
   
   //specular
   vec3 eyeminusvertex = e_eyepos - v_position.xyz;
   eyevector.x = dot(eyeminusvertex, v_svector.xyz);
   eyevector.y = dot(eyeminusvertex, v_tvector.xyz);
   eyevector.z = dot(eyeminusvertex, v_normal.xyz);
   //diffuse
   tc = v_texcoord.st;
   vc = v_colour;
   tf = ftetransform();
   gl_Position = tf;
   vec3 lightminusvertex = l_lightposition - v_position.xyz;
   lightvector = lightminusvertex;
   //used for project realtime shadows
   vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));
}
#endif
#ifdef FRAGMENT_SHADER
uniform float cvar_gl_specular,cvar_gl_specular_power;
vec3 calcLightWorldPos(vec2 screenPos, float depth)
{
   vec4 pos = m_invviewprojection * vec4(screenPos.xy, (depth*2.0)-1.0, 1.0);
   return pos.xyz / pos.w;
}
void main (void)
{
   //maps
   vec4 mapdiff  = texture2D(s_diffuse,tc);
   vec3 mapnormal = normalize(texture2D(s_normalmap, tc).rgb);
   vec4 mapspec = texture2D(s_specular,tc);
   //light
   vec3 nl = normalize(lightvector);
   float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);
   colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
   //IMPORTANT adding realtime shadows
   colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);
   //adding lights to diffuse part
   mapdiff.rgb *= colorscale * l_lightcolour;
   #ifdef SPECULAR
   // adding a little tint just to be sure the glsl file has been loaded
   mapdiff.rgb *= vec3(0.3,0,0);
   #endif
   //adding diffuse
   gl_FragColor = mapdiff;
   #ifdef NORMAL
   //adding normals
   gl_FragColor.rgb *= mapnormal.rgb;
   #endif
   #ifdef SPECULAR
   //specular - stolen from engine/shaders/glsl/defaultwall.glsl
   vec3 halfdir = normalize(normalize(eyevector));
   float spec = pow(max(dot(halfdir, mapnormal), 0.0), cvar_gl_specular_power * mapspec.a);
   spec *= cvar_gl_specular;
   //adding specular
   gl_FragColor.rgb += spec * mapspec.rgb;
   #endif
   gl_FragColor = fog4(gl_FragColor);
}
#endif

Really no idea what's the purpose of bemode depthonly..

Last thing I miss is depth because I tried to use many times {map $sourcedepth} but same as always.
If anyone has an advice, it will be welcome! :)
toneddu2000
 
Posts: 1318
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Is really bemode used in FTE?!?

Postby toneddu2000 » Tue Apr 03, 2018 9:06 pm

I completely remade the normal + specular part and I trashed all the useless junk.
Here the new shader with only diffuse map + rt lights
Image
and here with normals and specular + rt lights. Looks niiiice! :biggrin:
Image
I really can't see the difference with the default rtlight shader, so yay me! :mrgreen:
Material
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/env/green.tga
   normalmap textures/test/normal_tiles.tga
   //second bemode for realtime lights
   bemode rtlight
   {
      program ton_rtlight#NORMAL#SPEC
   }
}

glsl
Code: Select all
!!permu FOG
!!permu BUMP
!!permu SPECULAR
!!samps diffuse specular normalmap
!!cvarf gl_specular
!!cvarf gl_specular_power
#include "sys/defs.h"
#include "sys/pcf.h"
#include "sys/fog.h"
varying vec2 tc;
varying vec4 vtexprojcoord;
varying vec4 vc,tf;
varying vec3 lightvector;
varying vec3 eyevector;
#ifdef VERTEX_SHADER
void main (void)
{
   //specular
   vec3 eyeminusvertex = e_eyepos - v_position.xyz;
   eyevector.x = dot(eyeminusvertex, v_svector.xyz);
   eyevector.y = dot(eyeminusvertex, v_tvector.xyz);
   eyevector.z = dot(eyeminusvertex, v_normal.xyz);
   //diffuse
   tc = v_texcoord.st;
   vc = v_colour;
   tf = ftetransform();
   gl_Position = tf;
   vec3 lightminusvertex = l_lightposition - v_position.xyz;
   lightvector = lightminusvertex;
   //used for project realtime shadows
   vtexprojcoord = (l_cubematrix*vec4(v_position.xyz, 1.0));
}
#endif
#ifdef FRAGMENT_SHADER
void main (void)
{
   //maps
   vec4 mapdiff  = texture2D(s_diffuse,tc);
   vec4 mapspec = texture2D(s_specular,tc);
   vec3 mapnormals = normalize(vec3(texture2D(s_normalmap, tc)) - 0.5);
   vec3 composite = mapdiff.rgb * (l_lightcolourscale.x+l_lightcolourscale.y);
   //light
   vec3 nl = normalize(lightvector);
   #ifdef NORMAL
   composite = mapdiff.rgb * (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(mapnormals, nl), 0.0));
   #endif
   float colorscale = max(1.0 - (dot(lightvector, lightvector)/(l_lightradius*l_lightradius)), 0.0);
   colorscale *= (l_lightcolourscale.x + l_lightcolourscale.y * max(dot(vec3(0.0, 0.0, 1.0), nl), 0.0));
   //IMPORTANT adding realtime shadows
   colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);
   //adding lights to diffuse part
   composite.rgb *= colorscale * l_lightcolour;
   #ifdef SPECULAR
   vec3 halfdir = normalize(normalize(eyevector) + nl);
   float spec = pow(max(dot(halfdir, mapnormals), 0.0), FTE_SPECULAR_EXPONENT * mapspec.a)*float(SPECMUL);
   composite += l_lightcolourscale.z * spec * mapspec.rgb;
   #endif
   #ifdef DEBUG
   // adding a little tint just to be sure the glsl file has been loaded
   composite.rgb *= vec3(0.3,0,0);
   #endif
   //final composite image
   gl_FragColor.rgb = composite*colorscale*l_lightcolour;
   gl_FragColor = fog4(gl_FragColor);
}
#endif
toneddu2000
 
Posts: 1318
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Is really bemode used in FTE?!?

Postby Spike » Wed Apr 04, 2018 11:22 pm

Code: Select all
materialname
{
    materialproperties
    {
        subpassproperties_1stpass
    }
    {
        subpassproperties_2ndpass
    }
}


the 'program' property is weird in that the original version of it was placed at the material level (and merged all subpasses, with the extras being merely there for textures).
the new version favours the program lines inside the subpasses, with multiple map lines being listed as necessary, which is more consistent and cleaner.
the ONLY reason you'd have more indents than two is if you're using the one-token form of the *program lines, or the two-token form of bemode.
the three-token form of bemode defers to an entirely separate material. the two-token form of *program defers to an external file (which is the only real sane way to deal with glsl/hlsl/spir-v/etc, and also the only way to reuse the various pipelines).
the two-token form of bemode has a block that immediately follows the bemode line. this block represents the 'outer' level of a new material, but as most of those fields are irrelevant the only real purpose for the extra block is as a way to group the multiple subpasses of the alternative shader.
so if you're using embedded bemode then you should reach three levels of blocks ONLY WITHIN THE BEMODE'S BLOCK, otherwise you should reach only two levels.
you might have only one level with a sky shader, or if you're using surfaceparm nodraw, or if you've got an (outdated) program line at material scope.

this isn't like C, where you can just add extra blocks whereever the heck you want. each thing has its private place, and indents MUST BE EXACT.
if you want conditionals, use if/elif/else/endif commands, NOT C-style braces (although they should be balanced with regard to conditional statements if only for the sake of sanity).

'bemode depthonly' is used for shadowmaps. its use allows for alpha-tested shadows.
Spike
 
Posts: 2883
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Re: Is really bemode used in FTE?!?

Postby toneddu2000 » Thu Apr 05, 2018 11:59 am

the 'program' property is weird in that the original version of it was placed at the material level (and merged all subpasses, with the extras being merely there for textures).
the new version favours the program lines inside the subpasses, with multiple map lines being listed as necessary, which is more consistent and cleaner.
well, I use that and it works, but only if use {map foo} and not diffusemap, specularmap,etc. (and in glsl file I use s_diffuse, s_specular,etc.)

the ONLY reason you'd have more indents than two is if you're using the one-token form of the *program lines, or the two-token form of bemode.
the three-token form of bemode defers to an entirely separate material.

I'm sorry but it's getting every time more confused
Code: Select all
//one-token form of the *program lines:
material
{
program myglslfile
}

//one-token form of the *program lines:
material
{
program myglslfile
}

//two-token form of bemode:
material
{
  bemode rtlight myglslfile
  {
    program myglslfile
  }
}

//three-token form of bemode:
really no idea how it looks


are they correct?
Because I use the one-token form for bemode
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/blah/grid.tga
   specularmap textures/blah/green.tga
   normalmap textures/blah/normal_tiles.tga
   //second bemode for realtime lights - note the only one token for bemode
   bemode rtlight
   {
      //just one token even for the program directive
      program ton_rtlight#NORMAL#SPEC
   }
}

Just to be clear: for "token", do you mean argument? Because, if not, the two-token bemode would be the one I use(bemode rtlight), and the three-form bemode would be the one you told me not to use (bemode rtlight myglslfile) because it compresses all passes into one. I really no idea how could it be the one-token form of the *program lines!?! Just
Code: Select all
//really? no argument at all?
program



the two-token form of bemode has a block that immediately follows the bemode line. this block represents the 'outer' level of a new material, but as most of those fields are irrelevant the only real purpose for the extra block is as a way to group the multiple subpasses of the alternative shader.

it doesn't work if I put extra curly braces. A material like this
Code: Select all
simplemat
{
   //first set textures
   diffusemap textures/test/grid.tga
   specularmap textures/env/green.tga
   normalmap textures/test/normal_tiles.tga
   //second bemode for realtime lights
   bemode rtlight
   {
      {
         //all logic inside this new block
         program ton_rtlight#NORMAL#SPEC
      }
   }
}

will just not work. Every logic will be overwritten by engine.
Code: Select all
so if you're using embedded bemode then you should reach three levels of blocks ONLY WITHIN THE BEMODE'S BLOCK, otherwise you should reach only two levels.
you might have only one level with a sky shader, or if you're using surfaceparm nodraw, or if you've got an (outdated) program line at material scope.
I am using embedded bemode (if you take a look at my previous post) but I can only reach code inside bemode block, any other extra block will make FTE discard logic inside

don't want to be a pain in the XXX, but could you please write down the samples for every "category" of material. Just like this
Code: Select all
//old fashioned one-token program form
blah
//new updated two-token program form
blah2
//new updated three-token program form
blah3

Or tell me if the one I wrote in the previous form was correct, at least. Because it seems it works pretty well.
toneddu2000
 
Posts: 1318
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy


Return to Engine Programming

Who is online

Users browsing this forum: No registered users and 1 guest