A request for a simple example of using shaders

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

Moderators: winston, another_commander

User avatar
Shipbuilder
---- E L I T E ----
---- E L I T E ----
Posts: 877
Joined: Thu May 10, 2012 9:41 pm
Location: Derby

A request for a simple example of using shaders

Post by Shipbuilder »

I was wondering if anyone would be willing and able to post a `simple` example of how to specify shaders for models used in oxps.

What I would be looking for if possible would be as very basic example possibly with a few notes of explanation.

As a suggestion perhaps just a cube with the following -

1) One face flashing from say green to black.

2) Another face gradually changing colour from red, through orange and then to yellow.

3) An example of how code can be used in conjunction with shaders for example one face changing colour gradually as the speed increases.
The GalTech Industries Corporation - Building ships to populate the galaxies.

Increase the variety of ships within your Ooniverse by downloading my OXPs

Flying the [wiki]Serpent_Class_Cruiser[/wiki] "Thargoid's Bane"
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2483
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Re: A request for a simple example of using shaders

Post by Griff »

I can write up some of these examples for you if you like but probably not until the weekend if you can hang on that long, i'm not sure about how to do the first shader with the surface switching between 2 different colours, I imagine something some thing based on the timer code from Jens animated billboard shader could do this, hmm...
User avatar
Thargoid
Thargoid
Thargoid
Posts: 5528
Joined: Thu Jun 12, 2008 6:55 pm

Re: A request for a simple example of using shaders

Post by Thargoid »

The third one is fairly standard engine heat shader code - there are several examples of that around (my Vortex for example, plus most of Griff's ships I think).

I have an example around somewhere of a cube whose face image changes as its energy decreases (ie when you shoot it). It's not quite what you wanted, but you're welcome to it if it'd be useful (the code for it is in my Swarm OXP as well).
User avatar
Shipbuilder
---- E L I T E ----
---- E L I T E ----
Posts: 877
Joined: Thu May 10, 2012 9:41 pm
Location: Derby

Re: A request for a simple example of using shaders

Post by Shipbuilder »

Wow that would be great thanks Griff much appreciated.

I think that once I get my head around a couple of simple examples I should hopefully get to understand how these things work.
The GalTech Industries Corporation - Building ships to populate the galaxies.

Increase the variety of ships within your Ooniverse by downloading my OXPs

Flying the [wiki]Serpent_Class_Cruiser[/wiki] "Thargoid's Bane"
User avatar
Shipbuilder
---- E L I T E ----
---- E L I T E ----
Posts: 877
Joined: Thu May 10, 2012 9:41 pm
Location: Derby

Re: A request for a simple example of using shaders

Post by Shipbuilder »

That would be fantastic thanks Thargoid.
The GalTech Industries Corporation - Building ships to populate the galaxies.

Increase the variety of ships within your Ooniverse by downloading my OXPs

Flying the [wiki]Serpent_Class_Cruiser[/wiki] "Thargoid's Bane"
User avatar
Shipbuilder
---- E L I T E ----
---- E L I T E ----
Posts: 877
Joined: Thu May 10, 2012 9:41 pm
Location: Derby

Re: A request for a simple example of using shaders

Post by Shipbuilder »

Is there a list of events/conditions that can be used in conjunction with shaders ?

For example I know that shader effects can be linked to a ship's energy levels, speed and I think laser temperature.
The GalTech Industries Corporation - Building ships to populate the galaxies.

Increase the variety of ships within your Ooniverse by downloading my OXPs

Flying the [wiki]Serpent_Class_Cruiser[/wiki] "Thargoid's Bane"
User avatar
Commander McLane
---- E L I T E ----
---- E L I T E ----
Posts: 9520
Joined: Thu Dec 14, 2006 9:08 am
Location: a Hacker Outpost in a moderately remote area
Contact:

Re: A request for a simple example of using shaders

Post by Commander McLane »

Have you seen the relevant Wiki pages? [wiki]Shaders in Oolite[/wiki] and Image Shaders in Oolite: uniforms? I guess they should answer most of your questions. There is for instance a list on the latter page. I don't know, however, how up to date they are.

By the way: I found these pages simply on the Image scripting category index page, where you find everything related to creating OXPs.
User avatar
submersible
Commodore
Commodore
Posts: 264
Joined: Thu Nov 10, 2011 7:49 am

Re: A request for a simple example of using shaders

Post by submersible »

And now that you know how to fish.. since I have just been reading up on those effects , the interesting uniforms you're looking for are

Code: Select all

hullHeatLevel
,

Code: Select all

laserHeatLevel
,

Code: Select all

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

Re: A request for a simple example of using shaders

Post by Griff »

Here's Example one - Cube with one side switching colour between red & green
I've not included any lighting calculations in this example so the shader code is as short as possible, here's the vertex shader, it's just a text file saved in this case with the file extension .vertex and can be opened in any text editor

Code: Select all

varying vec2         vTexCoord;

void main(void)
{
   vTexCoord = gl_MultiTexCoord0.st;
   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
This simple vertex shader just calculates and passes the texture cooordinates for the current polygon fragment to the fragment shader as a vec2 called 'vTexCoord'
A 'vec' is just a collection of floating point numbers grouped together under a single variable name and since a texture is just a flat 2d image with an X axis (width) and a Y axis (height) then we only need 2 numbers to describe any pixels position on it so we use a vec2 to hold this data.
When we get to the fragment shader we're going to store colours using vec4's. Since a colour needs 4 components to describe it (the Red colour Value, the Green colour value, the blue Colour Value and the Alpha (Transparency) value we'll need four numbers to store those (and XYZ vertex positions could be stored in a vec3 if this vertex shader was going to be doing any transforms on the actual objects geometry.

To pass a vec to the fragment shader write 'varying' before its definition, see the line varying vec2 vTexCoord; above, this exact same line needs to be duplicated in the fragment shader to complete the 'link'.
You can pass data from the vertex shader to the fragment shader but not the other way around.
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; is dealing with the objects geometry, i must confess i don't really know what this is doing :lol: but it's a line that needs to be in the shader otherwise the whole thing fails to compile, 'gl_position' 'gl_ModelViewProjectionMatrix' and 'gl_Vertex' are all reserved openGl keywords

The shader needs to be saved to your OXP's Shaders folder (file and folder names are case sensitive) and listed in your ships Shader definitions in shipdata.plist
eg see lines 15 & 16 in shipdata.plist in Example_one_no_lighting.oxp

Code: Select all

                vertex_shader = "example_one.vertex"; 
                fragment_shader = "example_one.fragment"; 
Here's the fragment shader, notice the 'varying vec2 vTexCoord;' line linking this vec2 back to the same named one from the vertex shader.
To work in an useful way shaders often need links back to Oolite's game engine, these links are called 'Uniforms' and are defined in your shader and also in your shipdata.plist. Textures need to be sent from Oolite to your shader as uniforms, see the lines
uniform sampler2D uDiffuseMap; and uniform sampler2D uMaskMap; that start off our fragment shader and lines 24 & 25 in the shipdata.plist:-

Code: Select all

	uDiffuseMap = { type = texture; value = 0; };
	uMaskMap = { type = texture; value = 1; };
						
Make sure the uniform names in shipdata.plist(here they are uDiffuseMap & uMaskMap) match in the shader, the way you list your textures in shipdata.plist is important too

Code: Select all

    textures = 
		(
			"Example_one_DiffuseTexture.png", 
			"Example_one_MaskTexture.png"
		);
keep this order when you list them as uniforms (the shader snippet below also adds an extra uniform linking a variable called 'UTime' to Oolite's universalTime (oolites own 'gameclock')

Code: Select all

	uniforms =
		{
			uDiffuseMap = { type = texture; value = 0; };
			uMaskMap = { type = texture; value = 1; };
			uTime = "universalTime";
		};
I hope that's not too confusing, let's take a look at the fragment shader, i've added comments to the code that should hopefully explain stuff, just post on the forum for more detail on particular things, a lot of the code i can't follow myself especially the lighting code, i just know it has to be there and not fiddled with too much :)

OK, so to do the red/green texture colour switch we'll need to code up a timer, we've already got our shader uniform binding 'UTime' which is linking our shader to Oolite's Gameclock, since the gameclock will start at 0 seconds and just keep counting up for as long as the game is running it's not much use to us as a timer to switch between two settings, so what we'll do in this line in shader...

Code: Select all

float SwitchTime = step(0.0, sin(uTime)); 
...is to make a sine wave from current time value, then use a function called step to turn the sinewave into a square wave and then use the value of the squarewave (always either 0.0 or 1.0) to decide if either Red or Green gets added into the colour mix, here's a graph showing the sinewave generated from the game clock
Image
when the value of the sine wave is above 0.0 the step function overwrites the value to 1.0 and when the sine wave falls below 0.0 the step function overwrites the value to 0.0 (shown as a blue line on the graph)
right, so now whe have a floating point number called 'SwitchTime' that is alternating between 0.0 and 1.0 every few seconds or so, we can use this along with a command called 'mix' to create our red/green switching colour and add it into the final colour result, we'll do this in the following line

Code: Select all

SwitchingColour = mix(Green, Red, SwitchTime);
as this is turning into a bit of an epic post i'll let the comments in the shader code posted below explain how this is working but think of it as two pots of paint with a mix percentage of paint from the second pot determining the final colour we get, as our mix percentage is either 0% and 100% because of our square wave then we either just get paint from the first pot (green) or paint from the second pot (red)

Code: Select all

// Uniforms from Oolite
   uniform sampler2D    uDiffuseMap;
   uniform sampler2D    uMaskMap;
   uniform float        uTime;

// Information from Vertex Shader
   varying vec2 vTexCoord;

const vec4 Red = vec4(1.0, 0.0, 0.0, 1.0);    // store the Red Colour in a vec4 called 'Red', the 4 numbers are the Red, Green, Blue and Alpha values, range is 0.000000 - 1.000000
const vec4 Green = vec4(0.0, 1.0, 0.0, 1.0);  // store the Green Colour in a vec4 called 'Green'

void main(void)
{
 /*  - this symbol marks the start of a /comments section' which allow us to annotate the code
       this symbol marks the end of our comments  ->   */ 
// two slashes like this // can also be used to add comments on a per line basis

/*
 Calculate a sin wave powered by a 'link' back to Oolite's game clock, these links are knows as 'Uniforms'
 the results from the sin wave calculation are being stored as a floating point number in a variable called 
 'SwitchTime', the word float that starts the line is telling the shader this.
  Around our sin wave result is an openGL function called step, the definition of the step function is: 
  
      Step(edge, x) Returns 0.0 if x < edge, otherwise it returns 1.0
  
  Basically doing this changes our sin wave to a square wave 
  where the output is either 0.0 (when the sin wave result is below 0.0) or 1.0 when the sin wave result is above 0.0
*/

 float SwitchTime = step(0.0, sin(uTime)); 
 
/*
we could use a different function to step called smoothstep  if we'd like the colour 
to gradually change between red and green rather than instantly switch, to test this
comment out the 'float SwitchTime' line above and uncomment the float SwitchTime line below, remove the //'s
*/
  
// float  SwitchTime  = smoothstep(-1.0, 1.0, sin(uTime));

/*
 OK, so now we have a floating point number that switches between 0.0 and 1.0 every few seconds, we'll use this
 value in a 'mix' function and store the colour in a new vec4 called SwitchingColour

     'mix' definition:  mix(x,y,a) Returns x * (1-a) + y * a i.e. a linear blend of x and y

 So basically this means mix together colour choice 1 'Green', colour choice 2 'Red', and how much percentage of 
 each colour goes into the final result ('SwitchTime') and since SwitchTime is only ever 0.0 or 1.0 the mix 
 amount is either going to 100% colour choice 1 'Green' (when SwitchTime = 0.0) or 100% colour choice two 'Red' (When SwitchTime = 1.0)
*/
vec4  SwitchingColour = mix(Green, Red, SwitchTime);

// initialise the vec4 that will contain the final colour result from this shader, fill it with zero's (the colour value for Black)
   vec4 finalColor = vec4(0.0);

// Get the Texture Coordinates from the vertex shader, the UV map coordinates basically
   vec2 texCoord = vTexCoord;

// Read in the texture map using the texture co-ordinates to locate the correct pixels for the polygon fragment we're currently working on, store them in a vec4 called diffuseMapColor 
   vec4 diffuseMapColor = texture2D(uDiffuseMap, texCoord);

// Read in the effects texture that is used as a mask texture to control the visibility of our colour changer effect
   vec4 EffectMask = texture2D(uMaskMap, texCoord);
/*
   Add in either the results of our Red/Green mix calculation above, multiply this by the mask texture so only areas in this texture
   that have a colour value greater than 0.0 get the effect since as multiplying anything by 0.0 results in 0.0 
   (or no change to the originalcolour value we're adding 0.0 to it)
*/ 
   diffuseMapColor += SwitchingColour * EffectMask;

// output the final colour result to the frame buffer for displaying on screen  
   gl_FragColor = diffuseMapColor;
}
OXP download and rendermonkey file ( http://developer.amd.com/resources/arch ... toolsuite/ note: Rendermonkey is Windows Only) is available here

https://www.box.com/s/druvgqm7fe6tnqntkx2s
There are 2 versions of the oxp, one with lighting code and one without, just install one or the other not both at once as they'll clash
The oxp has a script.js that will spawn 6 of the example cubes nearby when you launch from a station, they're like_shipped to asteroids so will appear on the scanner as grey lollypops and will also appear in the demoships parade on the 'load commander y/n' title screen
User avatar
Smivs
Retired Assassin
Retired Assassin
Posts: 8408
Joined: Tue Feb 09, 2010 11:31 am
Location: Lost in space
Contact:

Re: A request for a simple example of using shaders

Post by Smivs »

Ha, you lost me after 'Here's Example one' :?
Commander Smivs, the friendliest Gourd this side of Riedquat.
User avatar
Star Gazer
---- E L I T E ----
---- E L I T E ----
Posts: 633
Joined: Sat Aug 14, 2004 4:55 pm
Location: North Norfolk, UK, (Average Agricultural, Feudal States,Tech Level 8)

Re: A request for a simple example of using shaders

Post by Star Gazer »

Yep. Fascinating, the different perspective knowledgeable people have on the word 'simple', as in:
This simple vertex shader just...
Very funny, Scotty, now beam down my clothes...
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2483
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Re: A request for a simple example of using shaders

Post by Griff »

Star Gazer wrote:
Yep. Fascinating, the different perspective knowledgeable people have on the word 'simple', as in:
This simple vertex shader just...
heh, I don't really know what i'm on about to be honest, :lol: the main workings of that vertex shader is really only two lines, and one of which I don't know what it does other than something to do with the objects geometry and if the line isn't there oolite generates a shader error
User avatar
Cody
Sharp Shooter Spam Assassin
Sharp Shooter Spam Assassin
Posts: 16081
Joined: Sat Jul 04, 2009 9:31 pm
Location: The Lizard's Claw
Contact:

Re: A request for a simple example of using shaders

Post by Cody »

Griff wrote:
I don't really know what i'm on about to be honest...
<chortles> I resemble that remark!
I would advise stilts for the quagmires, and camels for the snowy hills
And any survivors, their debts I will certainly pay. There's always a way!
User avatar
Thargoid
Thargoid
Thargoid
Posts: 5528
Joined: Thu Jun 12, 2008 6:55 pm

Re: A request for a simple example of using shaders

Post by Thargoid »

I think it's the line that works out the position of the point (pixel) that the shader is actually working on, from the model information and the vertex that you're dealing with (ie which fragment of the model's information).

Hence without it the fragment shader doesn't know what to apply its code to.

From a bit of research on t'net, the ModelViewProjectionMatrix is explained as:

The model, view and projection matrices are three separate matrices. Model maps from an object's local coordinate space into world space, view from world space to camera space, projection from camera to screen.

If you compose all three, you can use the one result to map all the way from object space to screen space, making you able to work out what you need to pass on to the next stage of a programmable pipeline from the incoming vertex positions.

In the fixed functionality pipelines of old, you'd apply model and view together, then work out lighting using another result derived from them (with some fixes so that e.g. normals are still unit length even if you've applied some scaling to the object), then apply projection. You can see that reflected in OpenGL, which never separates the model and view matrices — keeping them as a single modelview matrix stack. You therefore also sometimes see that reflected in shaders.

So: the composed model view projection matrix is often used by shaders to map from the vertices you loaded for each model to the screen. It's not required, there are lots of ways of achieving the same thing, it's just usual because it allows all possible linear transforms. Because of that, a lesser composed version of it was also the norm in ye olde fixed pipeline world.
User avatar
Shipbuilder
---- E L I T E ----
---- E L I T E ----
Posts: 877
Joined: Thu May 10, 2012 9:41 pm
Location: Derby

Re: A request for a simple example of using shaders

Post by Shipbuilder »

Wow so much information to read and digest thanks very much guys.

Using information received from Thargoid and Griff plus suggestions in this thread including Captain McLane's referral to the Wiki pages I have managed to get a shader to work which changes a ships texture as it's energy levels change.

I have also managed to get shaders to make engine glow change to suit a ship's speed.

I still need to fill in gaps in my knowledge but thanks to the help offered I have finally started to understand and be able to use shaders. :D

What I may do, if there is a call for it, once I have studied further is prepare a shaders for dummies style document to cover the basics along with some worked examples.

I'll have to make myself a coffee shortly and read through Griff's detailed post above.

Thanks again for all your help.
Last edited by Shipbuilder on Sat Mar 09, 2013 5:34 pm, edited 1 time in total.
The GalTech Industries Corporation - Building ships to populate the galaxies.

Increase the variety of ships within your Ooniverse by downloading my OXPs

Flying the [wiki]Serpent_Class_Cruiser[/wiki] "Thargoid's Bane"
Post Reply