Join us at the Oolite Anniversary Party -- London, 7th July 2024, 1pm
More details in this thread.

Shaders’ Outpost

Discussion and information relevant to creating special missions, new ships, skins etc.

Moderators: winston, another_commander

User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2479
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Post by Griff »

Ha, love the ideas for the eastereggs oxp!

I posted a long rambling message in Simon B's "Development: Arachnid SB" thread https://bb.oolite.space/viewtopic.php?t=5179 about how i go about painting out effects maps without having to use the 'color channel' options in photoshop,
I'll repeat the post here and include the screenshot i mentioned that hopefully makes it clearer just what on earth i am on about :wink:


-------
Painting your own effects map can be a bit confusing especially if you try and split an image into seperate colour channels, you just end up with a series of greyscale images, it always gives me headaches, but there is a much simpler way of working, just create seperate layers for each colour 'channel' stack them above your usual texture map (diffuse map) and work by paining only red in teh red layer, blue in the blue layer and green in the green layer. When it comes time to save out your final effects map, just switch off the visibility of the diffuse map and a "save as.." so you can save out a copy of your effects map .png

Image
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2479
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Post by Griff »

Had another go at the wavy flag shader, attached it to a ship this time, couldn't get it to pose nicely in Oolite so i took a screengrab from rendermonkey
Image

also used the 'environment map' shader from a few pages back to fake a reflective look on the more shiny metal surfaces, this might look good on an icy asteroid or something

download the bugbot test oxp here (works best with shaders)
http://www.box.net/shared/ospuz7du8x#griff_bugbot
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Post by Svengali »

@Griff: You are the greatest - Rock'n'Roll!
User avatar
LittleBear
---- E L I T E ----
---- E L I T E ----
Posts: 2869
Joined: Tue Apr 04, 2006 7:02 pm
Location: On a survey mission for GalCop. Ship: Cobra Corvette: Hidden Dragon Rated: Deadly.

Post by LittleBear »

Griff you really have to gather these together for an OXP on the Wikki page, there just too good not to! Now that turrets work for players on 1.72 I want a player version of the Griff Boa! Can't find the link for that one, must have dropped off the forum. If you could post one I'll hack a player version and add a Wikki page. 8) 8)

EDIT: found it here! :D

https://bb.oolite.space/viewtopic.php?t= ... &start=135

Is that the final version?
OXPS : The Assassins Guild, Asteroid Storm, The Bank of the Black Monks, Random Hits, The Galactic Almanac, Renegade Pirates can be downloaded from the Elite Wiki here.
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2479
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Post by Griff »

thanks guys!
very kind offer there LB, i've never made a player ship before, so it'd would be really cool to see how it's done!
i've had a quick look at the boa oxp and it looks a bit out of date now, the shaders are missing the uniform bindings possible in the new 1.7x versions of oolite and there are a whole load of texture maps being used that i could probably thin down a bit now, i'll have a go at it this weekend!
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

In order to further motivate me to implement support for normal mapping, I’ve put together an OXP which… uses normal mapping. Here it is.

Obviously it can’t be that simple, and in fact it isn’t. The fatal flaw is that it hard-codes an assumption about the orientation of the surface, or more precisely its tangent, which would have to be provided by Oolite for this to be generally useful. (Not only that, but the assumption made appears to be wrong. I wasn’t paying much attention to that aspect.)

Image
(Click for bigger picture)
The board on the left uses “basic” normal mapping. The one on the right also uses parallax mapping, which provides a sort of per-pixel perspective effect; the sides of the rocks facing the camera are stretched and the ones facing away are foreshortened. This effect is really too subtle to be of much use on spaceships – it’s mostly useful for floors and walls seen at shallow angles – but if you have a spare effect channel lying around it’s three extra lines of code.

Credit: the texture is an example from the OGRE 3D engine. I found the matching normal and parallax maps somewhere on the tubes.
User avatar
ramon
---- E L I T E ----
---- E L I T E ----
Posts: 346
Joined: Thu Jun 01, 2006 2:38 pm
Location: Tavistock
Contact:

Post by ramon »

that looks great. I think it'll be really effective on space stations when you get in close.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Masonry space stations? Maybe something for low-tech systems. :-)
User avatar
ramon
---- E L I T E ----
---- E L I T E ----
Posts: 346
Joined: Thu Jun 01, 2006 2:38 pm
Location: Tavistock
Contact:

Post by ramon »

lol. yeah. i meant the normal mapping, not the brick work - excellent as it may be.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Well, I now have tangent generation almost working. The one slight flaw is that it generates the wrong tangents, but in apparently consistent ways. This should be an easy fix… tomorrow some part of today which is daytime. Overall, tangent generation was a simple enough change I can hardly believe it took me this long to get around to implementing it. I might even sneak it into 1.72.1. :-)
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2479
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Post by Griff »

Wow, that's really, really amazing!
I don't know much about creating normal maps, but it looks like nvidia have some sort of texture tool here http://developer.nvidia.com/object/nv_t ... tools.html that will create a normal map from a texture image - there looks like there's a plugin for Gimp too! cool, i thought you needed something like zbrush to make normal maps so having free tools available is great!
Can't wait to see oolite normal mapped asteroids!
User avatar
Ark
---- E L I T E ----
---- E L I T E ----
Posts: 664
Joined: Sun Dec 09, 2007 8:22 am
Location: Athens Greece

Post by Ark »

Griff wrote:
Can't wait to see oolite normal mapped asteroids!
and planets and moons and also ....... :roll:
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Starting in 1.73, there will be a new default vertex shader, oolite-tangent-space-vertex.vertex. oolite-standard-vertex.vertex will be kept around for compatibility, although I don’t think anyone except me used it in OXPs. The fragment shader default fragment shader has of course also been updated.

From a user perspective, the main new feature of these shaders is the normal and parallax mapping, but those only add up to four lines of code. From a shader-hacking perspective, the main change is that everything is done in tangent space instead of mixed world space and model space.

Warning: if you happen to know the formal mathematical definitions of “tangent space” and “binormal”, note that these terms are abused a bit in graphics. “Normal” is used sensibly and the “tangent vector” is a vector in the actual tangent space of a surface, though.

Tangent space is a coordinate basis using vectors based on the surface of an object and its texture map. In particular, the basis vectors are called tangent, binormal and normal. Normal is the, er, normal normal, that is, a vector pointing straight out from the object (and defining what “straight out” means). Tangent is defined in terms of the texture map. This is slightly tricky to explain without a diagram, but it’s the vector from texture coordinate (0, 0) to texture coordinate (1, 0), projected onto the plane perpendicular to the normal (i.e. the tangent plane) and normalized. The binormal is the cross product of the normal and tangent, i.e. a vector that’s perpendicular to both. The normal is specified in the DAT file, and the tangent is calculated by Oolite (at least in the trunk), and the binormal is calculated in the vertex shader:

Code: Select all

attribute vec3          tangent;  // Provided by Oolite
//...
void main(void)
{
    // 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);
The first line declares an attribute variable, which we haven’t seen before in Oolite-related shaders. This is a variable which has a value set for each vertex by the host (i.e. Oolite), unlike a uniform where the host sets a value that applies to the whole model.

The last line builds a transformation matrix from model space to tangent space. This is then used to convert the various vectors we’re interested in:

Code: Select all

    vec3 eyeVector = -vec3(gl_ModelViewMatrix * gl_Vertex);
    vEyeVector = eyeVector * TBN;
    
    vec3 light0Vector = gl_LightSource[0].position.xyz + eyeVector;
    vLight0Vector = light0Vector * TBN;
    
    vec3 light1Vector = gl_LightSource[1].position.xyz + eyeVector;
    vLight1Vector = light1Vector * TBN;
The new vertex shader also sticks the texture coordinate into a varying vector instead of extracting it in the fragment shader (I’m not sure whether this makes a difference either way for performance, but it simplifies the fragment shader slightly, which is good) and projects the vertex onto the screen:

Code: Select all

    vTexCoord = gl_MultiTexCoord0.st;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
Over in the fragment shader, most things actually work exactly as before, since we’ve applied the same transformation to both the eye vector and the light vectors. However, if we’re not doing normal mapping we can make some simplifications because we now know that the normal will allways be (0, 0, 1). For instance, the expression dot(normal, lightVector) in the diffuse lighting calculation simplifies to lightVector.z.
Proof wrote:
By definition, dot(u, v) is equivalent to u.x * v.x + u.y * v.y + u.z * v.z.
Given u = (0, 0, 1), we get v.x * 0 + v.y * 0 + v.z * 1 = v.z.
Additionally, the expression -reflect(lightVector, normal) simplifies to vec3(-lightVector.x, -lightVector.y, lightVector.z).
Proof wrote:
By definition, reflect(v, n) is eqivalent to v - 2.0 * dot(n, v) * n.
Given n = (0, 0, 1), we get:
v - 2.0 * v.z * (0, 0, 1) [see previous proof]
= v - (0, 0, 2.0 * v.z)
= (v.x, v.y, -v.z)

The negation is of course (-v.x, -v.y, v.z).
Since the surface normal is a constant, normal mapping is simply implemented by replacing it with a value from the normal map texture.

Code: Select all

#if OOSTD_NORMAL_MAP  // Defined to 1 by Oolite if a normal map is to be used
    vec3 normal = texture2D(uNormalMap, texCoord).rgb;
#else
    const vec3 normal = vec3(0.0, 0.0, 1.0);
#endif
The very simple, but very fast, parallax mapping method used in the default shader works like this:
Image
The parallax map is sampled where the eye vector hits the actual surface, and is used to project the eye vector onto the virtual surface defined by the parallax map. (The offset can be positive or negative.) The diagram accurately illustrates how imprecise this method is: the parallax value at the projected point is different from the one at the intersection pount, which is what’s actually being used. The shallower the viewing angle and the higher the parallax scale, the more wrong it gets, but this technique should be sufficient for stuff like hull plating. I’ll probably implement one of various more accurate (but slower) techniques that exist for high-quality mode. Google for “offset limiting” and “relief mapping” if you’re curious.

Code: Select all

#if OOSTD_NORMAL_AND_PARALLAX_MAP
    float parallax = texture2D(uNormalMap, vTexCoord).a;
    parallax = parallax * uParallaxScale + uParallaxBias;
    vec2 texCoord = vTexCoord - parallax * eyeVector.xy * vec2(-1.0, 1.0);
#else
    #define texCoord vTexCoord
#endif
The updated shader also does away with the macros used for lighting before and uses functions instead:

Code: Select all

vec4 CalcDiffuseLight(in vec3 lightVector, in vec3 normal, in vec4 lightColor)
{
#if OOSTD_NORMAL_MAP
    float intensity = dot(normal, lightVector);
#else
    float intensity = lightVector.z;
#endif
    intensity = max(intensity, 0.0);
    return lightColor * intensity;
}


vec4 CalcSpecularLight(in vec3 lightVector, in vec3 eyeVector, in float exponent, in vec3 normal, in vec4 lightColor)
{
#if OOSTD_NORMAL_MAP
    vec3 reflection = -reflect(lightVector, normal);
#else
    vec3 reflection = vec3(-lightVector.x, -lightVector.y, lightVector.z);
#endif
    float intensity = dot(reflection, eyeVector);
    intensity = pow(max(intensity, 0.0), exponent);
    return lightColor * intensity;
}
...
    diffuseLight += CalcDiffuseLight(light1Vector, normal, gl_LightSource[1].diffuse);
    specularLight += CalcSpecularLight(light1Vector, eyeVector, exponent, normal, gl_LightSource[1].specular);
There’s not much to say here, except that varying variables are now used for light vectors instead of gl_LightSource[idx].position.xyz because the light positions need to be transformed to tangent space in the vertex shader. The new shader also avoids normalizing the light vectors twice, which was a bit silly.
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2479
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Post by Griff »

I've been having a go at generating normal maps from greyscale images using nvidias photoshop plugin, it seems to be work ok for my 'blocky metal plate above another blocky metal plate' textures, i've got normal maps made for the ship currently known as the griff_boa :wink:
here's a screen grab of it in rendermonkey using ahrumans normal map enabled shaders:
Image
the lighting looks a bit odd though because (i think) of the weird way i've laid out the UV map
User avatar
DaddyHoggy
Intergalactic Spam Assassin
Intergalactic Spam Assassin
Posts: 8512
Joined: Tue Dec 05, 2006 9:43 pm
Location: Newbury, UK
Contact:

Post by DaddyHoggy »

:shock: :shock: :shock: :shock:

No longer can you avoid the title "God of Modelling" - even if you claim to do it all by accident and theft!

:lol:

More - More!
Selezen wrote:
Apparently I was having a DaddyHoggy moment.
Oolite Life is now revealed here
Post Reply