Shaders’ Outpost
Moderators: winston, another_commander
- JensAyton
- Grand Admiral Emeritus
- Posts: 6657
- Joined: Sat Apr 02, 2005 2:43 pm
- Location: Sweden
- Contact:
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.
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.
E-mail: [email protected]
- Simon B
- ---- E L I T E ----
- Posts: 836
- Joined: Thu Oct 23, 2008 5:54 am
- Location: Red Beach NZ
- Contact:
You can extract the color channels to greyscale layers in the gimp from menu > colors > components > decomposeGriff 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
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
[re2dux] [neolite]
"Everything is perfect down to every last flaw..."
HBT: The Book of Verse - Principia Discordia
- Simon B
- ---- E L I T E ----
- Posts: 836
- Joined: Thu Oct 23, 2008 5:54 am
- Location: Red Beach NZ
- Contact:
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.
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
[re2dux] [neolite]
"Everything is perfect down to every last flaw..."
HBT: The Book of Verse - Principia Discordia
- DaddyHoggy
- Intergalactic Spam Assassin
- Posts: 8515
- Joined: Tue Dec 05, 2006 9:43 pm
- Location: Newbury, UK
- Contact:
That's very clever!
Oolite Life is now revealed hereSelezen wrote:Apparently I was having a DaddyHoggy moment.
- Simon B
- ---- E L I T E ----
- Posts: 836
- Joined: Thu Oct 23, 2008 5:54 am
- Location: Red Beach NZ
- Contact:
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.
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
[re2dux] [neolite]
"Everything is perfect down to every last flaw..."
HBT: The Book of Verse - Principia Discordia
- Griff
- Oolite 2 Art Director
- Posts: 2483
- Joined: Fri Jul 14, 2006 12:29 pm
- Location: Probably hugging his Air Fryer
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
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'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?
I think there are some conversions (implicit cast)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?
Code: Select all
float newVar = float(intVar);
- Griff
- Oolite 2 Art Director
- Posts: 2483
- Joined: Fri Jul 14, 2006 12:29 pm
- Location: Probably hugging his Air Fryer
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 (the 4.0 is because there are 4 decals arranged in a row in the image) - it works perfectly but generates this warning
grr, stupid OpenGL, be more like C!! A further but of clueless messing about got me this
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
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;
Code: Select all
warning C7503: OpenGL does not allow C-style casts
Code: Select all
float DecalSelect = 16.0 / float(GroupID);
I think it's easier to mis ab ... errr use the sawtooth pattern, Ahruman has posted sometime ago.Still returns values between 0.0...1.0, but with the right pattern.
Code: Select all
float tempvar = float(GroupID);
float Decal = mod(tempvar, 4.0) / 4.0;
- Griff
- Oolite 2 Art Director
- Posts: 2483
- Joined: Fri Jul 14, 2006 12:29 pm
- Location: Probably hugging his Air Fryer
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)
fragment shader
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.
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;
}
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);
}
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.
- Griff
- Oolite 2 Art Director
- Posts: 2483
- Joined: Fri Jul 14, 2006 12:29 pm
- Location: Probably hugging his Air Fryer
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
Position scale & rotation settings for decals are written like this in the shipdata.plist
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);
}
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; };