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:
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
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(!)
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.
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!
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!