Page 1 of 1

Is really bemode used in FTE?!?

Posted: Sat Mar 31, 2018 7:45 pm
by toneddu2000
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:

Re: Is really bemode used in FTE?!?

Posted: Sat Mar 31, 2018 11:53 pm
by toneddu2000
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:

Re: Is really bemode used in FTE?!?

Posted: Sun Apr 01, 2018 12:09 am
by Spike

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.

Re: Is really bemode used in FTE?!?

Posted: Sun Apr 01, 2018 9:17 pm
by toneddu2000
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:

Re: Is really bemode used in FTE?!?

Posted: Tue Apr 03, 2018 4:37 pm
by toneddu2000
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! :)

Re: Is really bemode used in FTE?!?

Posted: Tue Apr 03, 2018 9:06 pm
by toneddu2000
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

Re: Is really bemode used in FTE?!?

Posted: Wed Apr 04, 2018 11:22 pm
by Spike

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.

Re: Is really bemode used in FTE?!?

Posted: Thu Apr 05, 2018 11:59 am
by toneddu2000
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.