The first thing you need is a Graphic Card that supports shaders.. without shaders.. no normal mapping. and there is nothing you can do about it... You also need to know to make normal maps.. that is beyond the scope of this tutorial.. but basic a grey scale bump map can be converted using plugins in photoshop or other tools, to a normal map.. which is far superior to greyscale bump mapping. There are other more advanced methods.
The second thing you need is the shader files, these are known as
vertex & fragment file types
These are written in a code form format very similar to C
here are the two simple files from my Polaris Class destroyer... These are direct copies of Griffs Coriolis Station.. the undamaged one... They are without any effects other than the normal mapping...
First the Vertex File..
Code: Select all
#ifdef OO_TANGENT_ATTR
attribute vec3 tangent;
#else
const vec3 tangent = vec3(1.0, 0.0, 0.0);
#endif
varying vec2 vTexCoord;
varying vec3 vEyeVector; // These are all in tangent space
varying vec3 vLight0Vector;
varying vec3 vLight1Vector;
void main()
{
// Build tangent basis
vec3 n = normalize(gl_NormalMatrix * gl_Normal);
vec3 t = normalize(gl_NormalMatrix * tangent);
vec3 b = cross(n, t);
mat3 TBN = mat3(t, b, n);
vec3 eyeVector = -vec3(gl_ModelViewMatrix * gl_Vertex);
vEyeVector = eyeVector * TBN;
#ifdef OO_LIGHT_0_FIX
vec3 light0Vector = gl_LightSource[0].position.xyz + eyeVector;
vLight0Vector = light0Vector * TBN;
#endif
vec3 light1Vector = gl_LightSource[1].position.xyz + eyeVector;
vLight1Vector = light1Vector * TBN;
vTexCoord = gl_MultiTexCoord0.st;
gl_Position = ftransform();
}
Now before we enter into explanation of the fragment file you need to be aware of howto link these in the shipdata.plist file...
here is the releavent shipdata.plist file entries... for the Polaris Class Destroyer
Code: Select all
shaders =
{
"back_metal.png" =
{
vertex_shader = "Frame_station.vertex";
fragment_shader = "Frame_station.fragment";
textures =
(
{
name = "fx3_Panels_Shiny.png";
repeat_s = "yes";
repeat_t = "yes";
},
{
name="fx3_Panels_Bump2.png";
repeat_s ="yes";
repeat_t ="yes";
}
);
};
Code: Select all
vertex_shader = "Frame_station.vertex";
fragment_shader = "Frame_station.fragment";
Code: Select all
textures =
(
{
name = "fx3_Panels_Shiny.png";
Textures named back_metal.png on this particular model is replaced by the Texture named fx3_Panels_Shiny.png
Code: Select all
name="fx3_Panels_Bump2.png";
Note that there are other ways of doing this but it involves Uniforms.. something I will not address here.
Code: Select all
repeat_s ="yes";
repeat_t ="yes";
Now to the juicy part.. the fragment file..
Code: Select all
uniform sampler2D tex0;
uniform sampler2D tex1;
varying vec2 vTexCoord;
varying vec3 vEyeVector; // These are all in tangent space
varying vec3 vLight0Vector;
varying vec3 vLight1Vector;
const float kSpecExponent = 1.0;
const float kSpecular = 0.1;
void Light(in vec3 lightVector, in vec3 normal, in vec4 lightColor, in vec3 eyeVector,
in float specExponent, inout vec4 totalDiffuse, inout vec4 totalSpecular)
{
lightVector = normalize(lightVector);
vec3 reflection = normalize(-reflect(lightVector, normal));
totalDiffuse += gl_FrontMaterial.diffuse * lightColor * max(dot(normal, lightVector), 0.0);
totalSpecular += lightColor * pow(max(dot(reflection, eyeVector), 0.0), specExponent);
}
#define LIGHT(idx, vector) Light(vector, normal, gl_LightSource[idx].diffuse, eyeVector, kSpecExponent, diffuse, specular)
void main()
{
vec3 eyeVector = normalize(vEyeVector);
vec2 texCoord = vTexCoord;
vec3 normal = normalize( texture2D(tex1, texCoord).xyz - 0.5);
normal = normalize(normal);
vec4 colorMap = texture2D(tex0, texCoord);
vec4 diffuse = vec4(0.0), specular = vec4(0);
#ifdef OO_LIGHT_0_FIX
LIGHT(0, normalize(vLight0Vector));
#endif
LIGHT(1, normalize(vLight1Vector));
diffuse += gl_FrontMaterial.ambient * gl_LightModel.ambient;
vec4 color = diffuse * colorMap;
// calculate the specular, colour it using the diffuseMap
color += colorMap * 5.0 * specular * kSpecular;
color.a = 1.0;
gl_FragColor = color;
}
Code: Select all
uniform sampler2D tex0;
uniform sampler2D tex1;
tex0 isfx3_Panels_Shiny.png our diffuse map
tex1 isfx3_Panels_Bump2.png our normal map
these next declarations are must be in declarations..
Code: Select all
varying vec2 vTexCoord;
varying vec3 vEyeVector; // These are all in tangent space
varying vec3 vLight0Vector;
varying vec3 vLight1Vector;
VeyeVector is the vector at which the user is pointing in space, and therefore the way his eyes is pointing..
vLight0Vector
vLight10Vector
not sure about those. but ofcourse something with lighting in space and in the start screen and buy ship screen... They do got their values from the vertex shader since they are of type varying...
Finally these two declarations are used to control how much the normal and color map is lit up... You can alter their settings as you wish however you can never change them on the fly.. meaning you cannot alter them any other place than here becuase they are declared const meaning they are a constant number that never changes after being declared... the declaration is at compile time constant, and in our case the compile time is when we start up Oolite...
Code: Select all
const float kSpecExponent = 1.0;
const float kSpecular = 0.1;
Code: Select all
void Light(in vec3 lightVector, in vec3 normal, in vec4 lightColor, in vec3 eyeVector,
in float specExponent, inout vec4 totalDiffuse, inout vec4 totalSpecular)
{
lightVector = normalize(lightVector);
vec3 reflection = normalize(-reflect(lightVector, normal));
totalDiffuse += gl_FrontMaterial.diffuse * lightColor * max(dot(normal, lightVector), 0.0);
totalSpecular += lightColor * pow(max(dot(reflection, eyeVector), 0.0), specExponent);
}
Ship Buy Screen
Oolite start screen
Ingame...
unless you want to do some heavy modifying of the light result you should not alter this... however here is a link if you are interested
http://www.lighthouse3d.com/opengl/glsl ... php?lights
Now to the main part, where it all happens namely MAIN
Code: Select all
void main()
{
vec3 eyeVector = normalize(vEyeVector);
vec2 texCoord = vTexCoord;
vec3 normal = normalize( texture2D(tex1, texCoord).xyz - 0.5);
normal = normalize(normal);
vec4 colorMap = texture2D(tex0, texCoord);
vec4 diffuse = vec4(0.0), specular = vec4(0);
#ifdef OO_LIGHT_0_FIX
LIGHT(0, normalize(vLight0Vector));
#endif
LIGHT(1, normalize(vLight1Vector));
diffuse += gl_FrontMaterial.ambient * gl_LightModel.ambient;
vec4 color = diffuse * colorMap;
// calculate the specular, colour it using the diffuseMap
color += colorMap * 5.0 * specular * kSpecular;
// add in the glowing window lights
//color += LampColor * colorMap.a; // multiplier here to increase glow effect
color.a = 1.0;
gl_FragColor = color;
}
without it you will probably get an error...
but to try and explain what goes on here...
the first thing you see is all the Normalize functions, now what does that mean.. Without going into to much detail it means bringing our texture face into a correct state so we can manipulate it.
So at this stage you should simply accept that this is so... So copy and paste..
Now here is the stuff we are interested in...
Code: Select all
vec3 normal = normalize( texture2D(tex1, texCoord).xyz - 0.5);
But our Normal is set to be equal of our normal map RGB channels and from each are subtracted 0.5. I admit i have no idea why the subtract , but I assume it has to-do with the parallax method used to generate the normal maps..
this:
Code: Select all
texture2D(tex1, texCoord)
The texCoord is a vec2 meaning it contains two values
namely s and t and they are floats... and you can manipulate them individualy like this.
Code: Select all
texCoord.s = 0.5
But on With the Normal mapping Explanation...
next up we got
Code: Select all
vec4 colorMap = texture2D(tex0, texCoord);
Vec4 submembers are known as RGBA (red green blue alpha) channels
so we can manipulate the the different channels individually just like we did with the s and t member of texCoord by simply writing colorMap.a = 1.0 meaning the entire alphamap will be white.. which we are going to-do later
Next Up
we read
Code: Select all
vec4 diffuse = vec4(0.0), specular = vec4(0);
we are setting all of their values to 0.0 for the diffuse and 0 for the specular..
we are declaring it now because we are going to use it later..
next up we got two things... depending on where the player is...
Code: Select all
#ifdef OO_LIGHT_0_FIX
LIGHT(0, normalize(vLight0Vector));
#endif
LIGHT(1, normalize(vLight1Vector));
terrible boring so we move on to
Code: Select all
diffuse += gl_FrontMaterial.ambient * gl_LightModel.ambient;
Diffuse reflection is the reflection of light from an uneven or granular surface such that an incident ray is seemingly reflected at a number of angles.
also known as our normal map
We then grab our normal map with our color map in the next line..
here by mutiplying theire values, obeying mathematical rules..
Code: Select all
vec4 color = diffuse * colorMap;
Code: Select all
color += colorMap * 5.0 * specular * kSpecular;
If your texture has an alphamap, and you are using it for something, you should remove this line. (I think )
Code: Select all
color.a = 1.0
Code: Select all
gl_FragColor = color;
http://www.lighthouse3d.com/opengl/glsl/index.php?intro
Hope this was of help to you
PS. do not forget to vote...
man this was supposed to be 15 min write up.. it took 2 hours at least...
to bed to bed and let loose the pigeons of peace...