Shaders’ Outpost

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

Moderators: winston, another_commander

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 »

Yes. This post explains how to select one of several decals from a texture. Selecting one of several skins from a texture would work in the same way, only without the code to scale and position it as a decal.

However, using a number of different ship variants would save texture memory, unless you expect all of the variants to be in the system at the same time.

Also, some of Griff’s ships already demonstrate randomizing the colours of a ship while using a single pattern.
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 »

ramon wrote:
I've gone back to the drawing board on this Asp now. I like the way Griff's ships appear really chunky and solid, so I'm going to mimic that look.
I stole this idea from your Anaconda! :)
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 »

:) except I think the boosters on the Anaconda look stuck on now (I'm going to go back and re-do that ship again - at some point).

@Ahruman, thanks for the link that looks very useful. And thanks for not telling me off for not checking the threads first :)
User avatar
Simon B
---- E L I T E ----
---- E L I T E ----
Posts: 836
Joined: Thu Oct 23, 2008 5:54 am
Location: Red Beach NZ
Contact:

Post by Simon B »

Griff wrote:
Alpha channels are a confusing nightmare though, i've never found a proper way of removing an alpha channel from a png after it's been saved into it, so if anyone knows how to do this i'd really like to know
You can extract the color channels to greyscale layers in the gimp from menu > colors > components > decompose

Under layers > transparency there is a "remove alpha channel", but it does not seem to do anything. However, you can set the threshold alpha to zero, effectively removing its effect. Remove the channel completely by flattening it... afterward.
Simon Bridge
[re2dux] [neolite]
"Everything is perfect down to every last flaw..."
HBT: The Book of Verse - Principia Discordia
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 »

A very cool tip, thanks Simon!
User avatar
Simon B
---- E L I T E ----
---- E L I T E ----
Posts: 836
Joined: Thu Oct 23, 2008 5:54 am
Location: Red Beach NZ
Contact:

Post by Simon B »

Oh - I forgot - you can extract the alpha channel to greyscale by setting the rgb channels to zero (say, with brightness/contrast tool) and flattening the image (making sure background color is white.)

You can also make a layer mask from the layer's alpha channel.

Hmmm... the threshold alpha method does not seem to be foolproof.
It didn't remove the alpha where it was 100%.

http://www.linuxtopia.org/online_books/ ... ialog.html

Try> menu > dialogs > channels
... this is where you manipulate the channels one at a time. Select the channel to manipulate, then paint a shade of grey on the image.

To remove alpha, select the channel, switch to the image, select the whole image, edit > cut. The image vanishes, all the channels turn black. Don't panic: use the bucket-paint tool with white. The colors come back. Now flatten the image.

Alpha channel gone, rgb not modified.

Note: all the gimp tutorials have this wrong - they all say to just flatten the image. But this will not just remove alpha, but mix the background color with the color channels in proportion with transparency.
Last edited by Simon B on Mon Jul 06, 2009 3:25 pm, edited 2 times in total.
Simon Bridge
[re2dux] [neolite]
"Everything is perfect down to every last flaw..."
HBT: The Book of Verse - Principia Discordia
User avatar
DaddyHoggy
Intergalactic Spam Assassin
Intergalactic Spam Assassin
Posts: 8515
Joined: Tue Dec 05, 2006 9:43 pm
Location: Newbury, UK
Contact:

Post by DaddyHoggy »

That's very clever! :wink:
Selezen wrote:
Apparently I was having a DaddyHoggy moment.
Oolite Life is now revealed here
User avatar
Simon B
---- E L I T E ----
---- E L I T E ----
Posts: 836
Joined: Thu Oct 23, 2008 5:54 am
Location: Red Beach NZ
Contact:

Post by Simon B »

That's nothing ;) GIMP is really a GUI front end for ImageMagick ... which is a cli utility for manipulating images.

http://www.imagemagick.org/Usage/compose/#channel
... this shows the commands for fiddling with channel data with image magick. If you do this a lot, it is useful to put the commands in a script and just run it.

IIRC: it is possible to give the script a menu item in GIMP for one-click processing.

For this purpose, "copy opacity" is the function you want.
Simon Bridge
[re2dux] [neolite]
"Everything is perfect down to every last flaw..."
HBT: The Book of Verse - Principia Discordia
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 was wondering about using the group ID uniform to drive Ahrumans decal shader code that i've been using on my normal mapped ships, basically i'm thinking it would be cool if the main 'mum' ship and all it's escorts get the same decal without having to hardcode it in shipdata.plist
I'm stuck on the maths though, the wiki page has this to say about the group ID

Code: Select all

groupID  int  Unique identifier for the escort group the ship belongs to. 


how can i convert the integer number from the group ID into the floating point number the shader uses to select the decals? take the integer and divide it by 100 or something in the shader code?
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Post by Svengali »

Griff wrote:
how can i convert the integer number from the group ID into the floating point number the shader uses to select the decals? take the integer and divide it by 100 or something in the shader code?
I think there are some conversions (implicit cast)

Code: Select all

float newVar = float(intVar);
should do it here, but I'm not sure if it limits to [0.0...1.0]. If so you'd have to use a multiplier then. But I've also read somewhere that the conversions can slow down the whole execution, so test it carefully.
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 Svengali, that bit of code runs great, but changing the value of the integer isn't updating the decals, it looks like it's always returning a float value in the range 0.0 - 0.24, maybe it's always returning 0.0?
I got off my lazy backside and did a bit of hunting about on the net, i found this method

Code: Select all

float DecalSelect = ((float)(GroupID))/4.0;
(the 4.0 is because there are 4 decals arranged in a row in the image) - it works perfectly but generates this warning

Code: Select all

warning C7503: OpenGL does not allow C-style casts 
grr, stupid OpenGL, be more like C!! A further but of clueless messing about got me this

Code: Select all

float DecalSelect = 16.0 / float(GroupID);
which is running without errors, but not every integer gets an unique decal, eg, 1 & 2 are the same decal, as are 4 & 5, and if the int goes to 0 it freaks out massively, maybe that 16.0 needs changing to a different number - i just plucked it out of the air
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Post by Svengali »

I think it's easier to mis ab ... errr use the sawtooth pattern, Ahruman has posted sometime ago.

Code: Select all

float tempvar = float(GroupID);
float Decal = mod(tempvar, 4.0) / 4.0;
Still returns values between 0.0...1.0, but with the right pattern.
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 »

Dude you're a star! Thanks Svengali, that's working perfectly!
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 »

Edit: Here's the shader in a demo oxp
http://www.box.net/shared/4h8aykmzjg

Here's the multiple decal shader from Ramirez's Feudal States oxp
I'm hoping it works OK, it's running fine on a pc with an Nvidia Geforce FX 5200 in it. I haven't tested it yet but a similar shader wouldn't run on a pc with an Nvidia 8800GTS in it, the error seems to be related to the line where the texture is rotated
decal_TexCoord *= mat2(decal_c, decal_s, -decal_s, decal_c);
it doesn't like the *=

I've tried to make the decal placing code more generic, it can now be called multiple times in the main shader code, and each time you call it you can pass it different decal placement info as a uniform vec4

vertex shader (it's the same same as before)

Code: Select all

varying vec3         v_normal;      // Surface normal
varying vec3         v_pos;      // Vertex/fragment position in eye space

void main()
{
   v_normal = normalize(gl_NormalMatrix * gl_Normal);
   v_pos = vec3(gl_ModelViewMatrix * gl_Vertex);
   
   gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
fragment shader

Code: Select all

// Texture Information from Oolite. 
   uniform sampler2D      tex0; // Difuse and Illumination map 
   uniform sampler2D      tex1; // Decal Texture 
    
// Uniforms from Oolite 
   uniform float uDecalSelect; // used to decide which decal to select 
   uniform vec4  Decal1SettingsUniform; // position settings for decal 1
   uniform vec4  Decal2SettingsUniform; // position settings for decal 2
/*
the decal settings vec4's should be made up of the following data  - in this order!
    vec4(Decal_S_pos, Decal_T_pos, Decal Size, Decal Orientation) eg
    in the shader/uniforms setion of shipdata.plist you'd write something like
    Decal1SettingsUniform = { type = vector; value = "0.38 0.9 9.0 0.78540"; };
    Decal2SettingsUniform = { type = vector; value = "0.98 0.9 9.0 -0.78540"; };   
    
    Decal_S_pos & Decal_T_pos controll where your decal will be placed on your ships
    unfolded UV map and thus where it will appear on your ships hull in the game 
    Decal Size controls how big to draw the decal (smaller number = bigger decal)
    Decal Orientation controls the amount of rotation to apply to the decal 
*/  
      
 // Information from vertex shader. 
   varying vec3         v_normal;   // Surface normal 
   varying vec3         v_pos;      // Vertex/fragment position in eye space 

// Constants 
   const float specExponent = 2.0; 
   const float kDecalCount = 4.0; // Number of decals in your idecal image texture 
/*
   "the_decaliser" - this function scales & positions the decal image using data passed 
   to it from the main shader (see lines 105 & 106 below)
*/ 
vec4 the_decaliser(vec4 Your_Decal_Settings) 
{ 
    vec2 baseTexCoord = gl_TexCoord[0].st; 
    
// Setup the basic texture co-ords for the decals 
    vec2 decal_TexCoord = baseTexCoord; 
    
// Position the decal     
    decal_TexCoord -= vec2(Your_Decal_Settings.s, Your_Decal_Settings.t - (0.5 / Your_Decal_Settings.p)); 

// Orientate & scale the decal  
    float decal_s = sin(Your_Decal_Settings.q); 
    float decal_c = cos(Your_Decal_Settings.q); 
    decal_TexCoord *= mat2(decal_c, decal_s, -decal_s, decal_c); 

    decal_TexCoord += vec2(0.5 / Your_Decal_Settings.p); 
    decal_TexCoord *= vec2(Your_Decal_Settings.p / kDecalCount, Your_Decal_Settings.p); 
    
// Select the desired decal from the set. 
    float offset = floor(uDecalSelect * kDecalCount) / kDecalCount; 
    decal_TexCoord.s += offset; 
    
// Get texture values. 
    vec4 decal_Tex = texture2D(tex1, decal_TexCoord); 

// Modify the Decals texture co-oords 
    decal_Tex *= step(offset, decal_TexCoord.s) * 
                step(0.0, decal_TexCoord.t) * 
                step(-1.0 / kDecalCount - offset, -decal_TexCoord.s) * 
                step(-1.0, -decal_TexCoord.t); 

// Use the Alpha in the decal as a transparency mask so you can 'cutout' your decal from it's background
 float alpha = decal_Tex.a; 
 
    
// Return the scaled, position & rotated decal, it's mixed into the colour texture further on in the shader . 
return alpha * decal_Tex; // decal_Tex;
}      
    
// Calculate the contribution of a single light. Ought to be a function, but OS X's GLSlang implementation isn't sufficiently clever. 
   #define LIGHT(idx) \
        { \
           vec3 lightVector   = normalize(gl_LightSource[idx].position.xyz); \
           vec3 reflection   = normalize(-reflect(lightVector, v_normal)); \
           diffuse += gl_FrontMaterial.diffuse * gl_LightSource[idx].diffuse * max(dot(v_normal, lightVector), 0.0); \
           specular += gl_LightSource[idx].diffuse * pow(max(dot(reflection, eyeVector), 0.0), specExponent); \
        } 
    
   void main(void) 
   { 
   vec4 diffuse = vec4(0.0), specular = vec4(0.0); 
   vec3 eyeVector = normalize(-v_pos); 
  
// Load texture data 
   vec2 texCoord = gl_TexCoord[0].st; 
   vec4 colorMap = texture2D(tex0, texCoord); 
    
// calculate specular effects 
   float specIntensity = 0.1; 
    
/*   Light 0 is the "showroom" light, used in the demo screen and shipyard. 
     Light 1 is the sun. 
*/ 
   #ifdef OO_LIGHT_0_FIX 
   LIGHT(0); 
   #endif 
  
   LIGHT(1); 
   diffuse += gl_FrontMaterial.ambient * gl_LightModel.ambient; 
    
// Calculate the lighting for full shader mode, (adds in the decals)
   #ifndef OO_REDUCED_COMPLEXITY 
    vec4 color =  diffuse * colorMap; // set up the basic colour texture...
   
    /* .. then apply the decals on top of it, if you need more decals , set up extra DecalXSettingsUniforms
    // and duplicate the line below modifying the 'Decal2SettingsUniform' so it's the same as
    // your new uniform */ 
    color += diffuse * the_decaliser(Decal1SettingsUniform); // apply decal 2
   color +=  diffuse * the_decaliser(Decal2SettingsUniform); // apply decal 2  
   color += specular * specIntensity; // add in the lighting
   #endif 

// Calculate the lighting for simple shader mode 
   #ifdef OO_REDUCED_COMPLEXITY    
   vec4 color = diffuse * colorMap + specular * specIntensity; 
   #endif 
  
// Output final color 
   gl_FragColor = vec4(color.rgb, 1.0); 
   } 

This example places 2 decals, if you need more, then simply add more uniforms to place the decal and edit the fragment shader so it runs the 'the_decaliser' function for each new decal (see lines 106 & 107 for examples, add your new lines below these.)
When copy & pasting this code, check there's no spaces after the \ at the end of every line in the LIGHT macro, ther must be nothing after the \ or the shader won't work and you'll get a flood of syntax errors reported in the log.

The shader will use the alpha channel in the decalimage to cut out any unwanted background image, shades of grey in the alpha channel will control various levels of transparency when the decal texture is mided into the diffuse texture, with black being fully transparent and white being fully opaque.
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 »

Here's the same shader but the rotation is now in a seperate Uniform float
http://www.box.net/shared/xlld4l09ai
It's a work around for Oolite setting the 4th component in a uniform vec4 to 1.0 when binding it to a shader

Code: Select all

// Texture Information from Oolite. 
   uniform sampler2D      tex0; // Difuse and Illumination map 
   uniform sampler2D      tex1; // Decal Texture 
    
// Uniforms from Oolite 
   uniform float uDecalSelect; // used to decide which decal to select 
   uniform vec4  Decal1_Scale_and_Position; // position & scale settings for decal 1
   uniform float Decal1_Rotation;  // rotation settings for decal 1
   uniform vec4  Decal2_Scale_and_Position; // position & scale settings for decal 2
   uniform float Decal2_Rotation; //  rotation settings for decal 2
/*
the decal settings vec4's should be made up of the following data  - in this order!
    vec4(Decal_S_pos, Decal_T_pos, Decal Size, 1.0). The 4th component in any vec4 is not used,
    so give it the value 1.0.
    eg. in the shader/uniforms setion of shipdata.plist you'd write something like
    Decal1SettingsUniform = { type = vector; value = "0.38 0.9 9.0 1.0"; };
    Decal2SettingsUniform = { type = vector; value = "0.98 0.9 9.0 1.0"; };
 
    Decal_S_pos & Decal_T_pos control where your decal will be placed on your ships
    unfolded UV map and thus where it will appear on your ships hull in the game 
    Decal Size controls how big to draw the decal (smaller number = bigger decal)
           
    Decal rotation (in radians) is handled using a float value, this is written in shipdata.plist as
    Decal1Rotation = { type = "float"; value = 0.3536; };
*/  
      
 // Information from vertex shader. 
   varying vec3         v_normal;   // Surface normal 
   varying vec3         v_pos;      // Vertex/fragment position in eye space 

// Constants 
   const float specExponent = 2.0; 
   const float kDecalCount = 4.0; // Number of decals in your idecal image texture 
/*
   "the_decaliser" - this function scales & positions the decal image using data passed 
   to it from the main shader (see lines 105 & 106 below)
*/ 
vec4 the_decaliser(vec4 Your_Decal_Settings, float Your_decal_Rotation) 
{ 
    vec2 baseTexCoord = gl_TexCoord[0].st; 
    
// Setup the basic texture co-ords for the decals 
    vec2 decal_TexCoord = baseTexCoord; 
    
// Position the decal     
    decal_TexCoord -= vec2(Your_Decal_Settings.s, Your_Decal_Settings.t - (0.5 / Your_Decal_Settings.p)); 

// Orientate & scale the decal  
    float decal_s = sin(Your_decal_Rotation); 
    float decal_c = cos(Your_decal_Rotation); 
    decal_TexCoord *= mat2(decal_c, decal_s, -decal_s, decal_c); 

    decal_TexCoord += vec2(0.5 / Your_Decal_Settings.p); 
    decal_TexCoord *= vec2(Your_Decal_Settings.p / kDecalCount, Your_Decal_Settings.p); 
    
// Select the desired decal from the set. 
    float offset = floor(uDecalSelect * kDecalCount) / kDecalCount; 
    decal_TexCoord.s += offset; 
    
// Get texture values. 
    vec4 decal_Tex = texture2D(tex1, decal_TexCoord); 

// Modify the Decals texture co-oords 
    decal_Tex *= step(offset, decal_TexCoord.s) * 
                step(0.0, decal_TexCoord.t) * 
                step(-1.0 / kDecalCount - offset, -decal_TexCoord.s) * 
                step(-1.0, -decal_TexCoord.t); 

// Use the Alpha in the decal as a transparency mask so you can 'cutout' your decal from it's background
 float alpha = decal_Tex.a; 
 
    
// Return the scaled, position & rotated decal, it's mixed into the colour texture further on in the shader . 
return alpha * decal_Tex;
}      
    
// Calculate the contribution of a single light. Ought to be a function, but OS X's GLSlang implementation isn't sufficiently clever. 
   #define LIGHT(idx) \
        { \
           vec3 lightVector   = normalize(gl_LightSource[idx].position.xyz); \
           vec3 reflection   = normalize(-reflect(lightVector, v_normal)); \
           diffuse += gl_FrontMaterial.diffuse * gl_LightSource[idx].diffuse * max(dot(v_normal, lightVector), 0.0); \
           specular += gl_LightSource[idx].diffuse * pow(max(dot(reflection, eyeVector), 0.0), specExponent); \
        } 
    
   void main(void) 
   { 
   vec4 diffuse = vec4(0.0), specular = vec4(0.0); 
   vec3 eyeVector = normalize(-v_pos); 
  
// Load texture data 
   vec2 texCoord = gl_TexCoord[0].st; 
   vec4 colorMap = texture2D(tex0, texCoord); 
    
// calculate specular effects 
   float specIntensity = 0.4; 
    
/*   Light 0 is the "showroom" light, used in the demo screen and shipyard. 
     Light 1 is the sun. 
*/ 
   #ifdef OO_LIGHT_0_FIX 
   LIGHT(0); 
   #endif 
  
   LIGHT(1); 
   diffuse += gl_FrontMaterial.ambient * gl_LightModel.ambient; 
    
// Calculate the lighting for full shader mode, (adds in the decals)
   #ifndef OO_REDUCED_COMPLEXITY 
    vec4 color =  diffuse * colorMap; // set up the basic colour texture...
   
    /* .. then apply the decals on top of it, if you need more decals , set up extra DecalXSettingsUniforms
    // and duplicate the line below modifying the 'Decal2SettingsUniform' so it's the same as
    // your new uniform */ 
    color += diffuse * the_decaliser(Decal1_Scale_and_Position, Decal1_Rotation); // apply decal 2
   color +=  diffuse * the_decaliser(Decal2_Scale_and_Position, Decal2_Rotation); // apply decal 2  
   color += specular * specIntensity; // add in the lighting
   #endif 

// Calculate the lighting for simple shader mode 
   #ifdef OO_REDUCED_COMPLEXITY    
   vec4 color = diffuse * colorMap + specular * specIntensity; 
   #endif 

// Output final color 
   gl_FragColor = vec4(color.rgb, 1.0); 
   } 

Position scale & rotation settings for decals are written like this in the shipdata.plist

Code: Select all

							Decal1_Scale_and_Position = { type = vector; value = "0.83 0.67 9.0 1.0"; };
							Decal1_Rotation = { type = "float"; value = 0.7854; };							
							Decal2_Scale_and_Position = { type = vector; value = "0.83 0.32 6.1812 1.0"; };
							Decal2_Rotation = { type = "float"; value = 2.44; };
Post Reply