GameEngine Blender-Material/GLSL Shader Document.


Blender materials for the game engine redirect Tex-face options over to blenders material panel. Which allows you to set multiple materials on an object and multiple textures to that material. Sixteen materials can be assigned to one mesh. Three textures are available for each material assigned to the mesh. Regular UV-mapped models will still use the old game engine method. If 1).the mesh has no material actively assigned to it. 2). the material on the object is set with the option Tex-face.

Materials.
Lighting.
Images.
Mapping.
Texture Blending.
Image Types.
World.
Assigning Materials.
GLSL Python.
Blendo Toon Shader.
Blender Player Info.

Blender-Materials.


Materials.

 

A.

Use mesh vertex colors over the (default) material color.

B.

Use the meshes face texture assigned in face select mode (f) . This will count as your first texture unit.

C. Switch on/off the use of OpenGL lighting for this material.(default on)
D.

Specify which color value (E) will apply to.
Col: diffuse color
Spe: If option (C) is not set. the color set with (E) will be the specular light on the model

E.

RGB: color sliders.
A: Set the alpha value of this material, for use in blending

F. Material index slider. 1 of 16 available.see(Assigning materials)
G. Draw this material in wire frame-mode.

Lighting.


Note: If the option ( (C) above (Shadeless) ) is set, the settings here are ignored

A.

Sets the amount of reflection, in calculating the lit material's diffuse color.
B. Sets the amount of specular, in calculating the lit material's specular color.
C. Sets the shininess if the lit material
D.

Adjusts the amount of global ambient light the material will receive

E. Sets the amount of light this material emits

Images.



A. First image unit
B. Second image unit
C. Third image unit. This might not be available if your graphics card only supports two texture units. Two is the minimum, requirement.

Mapping.


Note: All options here are per image unit.


A.

If any button in A is set the meshes uv coordinates will be used.(Nor is reserved for the surface normal to be used as coordinates)

B.

Use the game object, in the text field. This will take the objects position as texture coordinates. Use in conjunction with (D) to specify the X,Y or Z surface plane that mapping takes place (GL_EYE_PLANE)

C. Use GL sphere map calculations (same as Reflection mapping in the uv window)
D. See (B)  ;)
E. For all options except (C) apply an offset to the texture coordinates by offsX,offsY,offsZ
F.

For all options except (C) scale the texture coordinates by sizeX,sizeY,sizeZ


Texture Blending.



A. Use the textures alpha channel to blend with Mix, Add Subtract, Multiply or Screen
B. Invert blending for this texture.
C. Use Mix, Add Subtract, Multiply and Screen to blend texture.
D. Special case:
If Mix is the blend mode: use the color slider to adjust the amount 1 = the texture only, 0= the previous texture(if no previous texture, the base color)only.

Image types.



A.

Use a EnvMap (loaded as a GL cube map)
Ex: setup a scene to render a blender EnvMap with CubeRes at power of two (128, 256, 512) then use the Save EnvMap option.

 

A1. Load a previously calculated EnvMap. This will be the only option that works at runtime.
B, Use a normal image.
B1. Toggle MipMap for this image
B2. Load from disk ... :)
B3 Use the images alpha channel for blending same as (Tex-face Alpha)
B4 Use the image for additive blending same as (Tex-face Add )

World types.



A. Controls the RGB amount of global ambient light

Multiple Materials Per Mesh.

To have more than one material per mesh, you have a couple of options. The first is to apply materials to separate meshes then join them together. The second option is to use the panel in the Edit Buttons.


Create the number of materials you want for the mesh(New). Select the Mat number and the faces on the mesh you want for the material then hit Assign.


GLSL Shaders

Check with your graphics card manufacturer to see if GLSL is supported.

This GLSL document will outline the python shader interface, and guide you through the setup of a toon shader using the lovable model Blendo :)


ShaderTemplate.py
# -------------------------------------
import GameLogic 
object_list = GameLogic.getCurrentScene().getObjectList()

# -------------------------------------
# shaders are stored as python strings 
# --

VertexShader = """
// default vertex shader
void main() {
	gl_Position = ftransform();
}
"""

FragmentShader = """
// default fragment shader
void main() {
	gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0);
}
"""

# -------------------------------------
# applying the shader to one or more objects 
# can be done by::
# getting the objects you want to apply this shader to
#	for each object to apply:
#		get the objects mesh
#		for each material on the mesh:
#			get the material index see(Materials (F))
#			if the index is the one to apply:
#				get the shader for this material
#				if the shader is not already set:
#					set the shader strings
#					set number of passes
#					allow the material to use the shader
#				set samplers
#				set uniiforms 
#		go to the next mesh or object
# --

material_index = 0

def main():
	shader_objs = [ object_list["OBCube"] ] 
	for obj in shader_objs:
		mesh_index = 0
		mesh = obj.getMesh(mesh_index)
		while mesh != None:
			for mat in mesh.materials:
				# valid BlenderMaterial ?
				if not hasattr(mat, "getMaterialIndex"):
					break
					
				if mat.getMaterialIndex() == material_index:
					shader = mat.getShader()
					if not shader.isValid():
						apply = 1
						shader.setSource(VertexProgram. FragmentProgram, apply)
						shader.setNumberOfPasses(1)
					#--
			mesh_index += 1
			mesh = obj.getMesh(mesh_index)
	
# --		
# call it 

main()


Accessing a shader...
mesh = Obj.getMesh()
for mat in mesh.materials:
	shader= mat.getShader()

 

Tip:
have a look at
rendercore.c
to implement your own GLSL shaders based on blender shaders (Toon, Blinn, ....)

Shader Functions.

setSource(vert, frag, apply)

Sets the shaders strings. One vertex program source and one fragment programs source Value apply sets the material to use the shader, 1 or 0.
delSource() Deletes the shader, If the shader is to be used again you will need to call setSource again.
getVertexProg() Returns a python string of the vertex program.
getFragmentProg() Returns a python string of the fragment program.
setNumberOfPasses(num) Sets the number of times the material that owns this shader is rendered.
isValid() Will return true if the shader has been set and no error has occurred, false otherwise.
validate() A check to see if the shader is a valid GLSL shader.
setUniform1f(name, f) Sets one float value for name.
( uniform float example; )
setUniform2f(name, f0, f1) Sets two float values for name.
( uniform vec2 example; // (1.0,0.0))
setUniform3f(name, f0,f1,f2) Sets three float values for name.
( uniform vec3 example; // (1.0, 0.0, 1.0))
setUniform4f(name, f0,f1,f2, f3) Sets four float values for name.
( uniform vec4 example; // (1.0, 0.0, 1.0, 1.0))
setUniform1i(name, i) Sets one int value for name.
( uniform int test; )
setUniform2i(name, i0,i1) Sets two int values for name.
( uniform vec2 example; // (1,1)
setUniform3i(name, i0,i1,f2) Sets three int values for name.
( uniform vec3 example; // (1,1,1))
setUniform4i(name, i0,i1,f2, f3) Sets four int values for name.
( uniform vec4 example; // (1,1,1,1))
setUniformfv(name, list) Sets float lists of length 2, 3, or 4 (vec2, vec3, vec4).
setUniformiv(name, list) Sets int lists of length 2, 3, or 4 (vec2, vec3, vec4).
setUniformMatrix4(name, mat, transp=true) Sets a 4x4 matrix (mat4) transpose matrix for OpenGL style.
setUniformMatrix3(name, mat, transp=true) Sets a 3x3 matrix (mat3) transpose matrix for OpenGL style.
setSampler(name, index) Sets a sampler2D or samplerCube at index. index is the order of the texture in the material panel, otherwise index is 0.





Blendo Toon Shader

Start by opening base.blend in the tutorial directory. The blend file contains the Blendo model we will be working with. On layer one will be the base model and Blendo's eye lids. Layer two contains the eye. Select the base model and add a new material. Set the material color to an orange and press the Shadeless button.

Now select each eye lid, then select the base model. Join them with Ctrl+j. The base model's material should now be on the each eye lid.


Move to layer two. There are three parts to the eye. The pupil, iris, and the white. Select the pupil and enter vertex paint mode(v). Set it a black color. Do the same for the iris but make it a blue color. (It's not necessary in this example... Set the vertex color of the Blendo model to an orange... Just so it's not bright white :))
In potato mode it should look like this...


Join the three parts together and add a material to the object with, Shadeless and VCol-Paint pressed.

 

Move the eye to layer one. Select the eye then the Blendo mesh and join the two. You should now have one mesh with two materials assigned to it, the Blendo mesh is at index1 and the eye is at index2. In python the indexes will be 0,1.
Were looking for this...

 

Create two empties some where in layer one. One of them will be our light and the other will hold the shader script.

Start with the vertex shader. We are going to need a uniform variable holding our light position and, three varying variables to pass to the fragment shader. The first is the vector from vertex to light, the second will hold the surface normal, and the last will hold the material color.

VertexShader = """
// blendo

uniform vec3 light_position;
varying vec3 light_vec;
varying vec3 normal_vec;
varying vec4 material_color;

void main(){
	vec3 vert =(gl_ModelViewMatrix * gl_Vertex).xyz;
	light_vec = (gl_ModelViewMatrix* vec4(light_position,1.0)).xyz - vert;
	normal_vec = gl_NormalMatrix *gl_Normal;
	material_color = gl_Color;
	gl_Position = ftransform();
}
"""
Now for the fragment shader. Copy the varying variables from the vertex shader to the fragment shader. We need to normalize the light vector and the normal vector. A color variable is also needed that we can modify. Some temporary variables will do the trick.
vec3 l = normalize(light_vec);
vec3 n = normalize(normal_vec);
vec4 color = material_color;
Calculate the dot product between the light vector and the normal and clamp it in the range of 0,1.
ndotl = clamp(dot(n,l), 0.0, 1.0);
Now create some cells based on the ndotl value. The lower the value is, multiply the material color by a smaller value.

FragmentShader = """
// blendo

varying vec3 light_vec;
varying vec3 normal_vec;
varying vec4 material_color;

void main(){
	vec3 l = normalize(light_vec);
	vec3 n = normalize(normal_vec);
	float ndotl = clamp(dot(n,l), 0.0, 1.0);
	
	vec4 color = material_color;
	
	if(ndotl >0.9)
		color += 0.1; // add for brighter cell 
	else if(ndotl > 0.8)
		color *= 0.8;
	else if(ndotl > 0.5)
		color *= 0.5;
	else if(ndotl >0.4)
		color *= 0.4;
	else 
		color *= 0.3;

	gl_FragColor = color;
}
"""

We will apply the same shader to the eye. Except, because the VCol-Paint button is set, the material_color (gl_Color) will be the vertex color assigned to the mesh.

Blendo.py
import GameLogic
objects = GameLogic.getCurrentScene().getObjectList()
lamp = objects['OBLamp']

VertexShader = """
// blendo

uniform vec3 light_position;
varying vec3 light_vec;
varying vec3 normal_vec;
varying vec4 material_color;

void main(){
	vec3 vert =(gl_ModelViewMatrix * gl_Vertex).xyz;
	light_vec = (gl_ModelViewMatrix* vec4(light_position,1.0)).xyz - vert;
	normal_vec = gl_NormalMatrix *gl_Normal;
	material_color = gl_Color;
	gl_Position = ftransform();
}
"""

FragmentShader = """
// blendo

varying vec3 light_vec;
varying vec3 normal_vec;
varying vec4 material_color;

void main(){
	vec3 l = normalize(light_vec);
	vec3 n = normalize(normal_vec);
	float ndotl = clamp(dot(n,l), 0.0, 1.0);

	vec4 color = material_color;
	
	if(ndotl >0.9)
		color += 0.1; // add for brighter cell 
	else if(ndotl > 0.8)
		color *= 0.8;
	else if(ndotl > 0.5)
		color *= 0.5;
	else if(ndotl >0.4)
		color *= 0.4;
	else 
		color *= 0.3;

	gl_FragColor = color;
}
"""

blendo_index = 0
eye_index = 1


def main():
	obj = objects['OBBlendo']
	mesh_index = 0
	mesh = obj.getMesh(mesh_index)
	while mesh != None:
		for mat in mesh.materials:
			if not hasattr(mat, "getMaterialIndex"):
				break
					
			mat_index = mat.getMaterialIndex()
			if mat_index == blendo_index or mat_index == eye_index:
				shader = mat.getShader()
				if not shader.isValid():
					shader.setSource(VertexShader, FragmentShader,1)
					shader.setNumberOfPasses(1)

				shader.setUniformfv('light_position', lamp.getPosition())

		mesh_index += 1
		mesh = obj.getMesh(mesh_index)
		
main()

 

If you want a different color Blendo just change the material color.

In the tutorial directory is ToonShader.py which you can use in your own projects.
It uses the same method here except it works with OpenGL Lights see the finished blend :)


Blender Player Info.

To run these changes in the BlenderPlayer, they need to be enabled.

blenderplayer.exe -g blender_material = 1 game_blend_file.blend
If it is a runtime.
your_runtime.exe -g blender_material = 1








References.
Lighthouse GLSL Tutorials
Toon Shader(roughly based)