History: Enhanced CelShading
Source of version: 1
- «
- »
Copy to clipboard
{maketoc}
!!Description
This is an enhanced version of Ogre's CelShading CG program and material. It adds the support of decal textures, dynamic ambient lighting, specular texture mapping, edge coloring, and light attenuation. Keep in mind that when using this shader, it will take more resources than the basic example that comes with Ogre.
The material and cg program below may look confusing, but do not worry, you do not have to understand the works completely in order to include it in your own materials.
!!CelShading.cg
{CODE(wrap="1", colors="c++")}
//
//Basic lighting functions
//
void lighting_vp(float4 position : POSITION,
float3 normal : NORMAL,
// outputs
out float4 oPosition : POSITION,
out float diffuse : TEXCOORD0,
// parameters
uniform float3 lightPosition, // object space
uniform float4 lightAttenuation,
uniform float4x4 worldViewProj)
{
// calculate output position
oPosition = mul(worldViewProj, position);
// calculate light vector
float3 N = normalize(normal);
float3 L = normalize(lightPosition - position.xyz);
//Calculate distance from light to object.
float lightDist = length( lightPosition - position.xyz );
float luminosity = 0;
if( lightAttenuation.x > lightDist)
luminosity = 1.0 / ( lightAttenuation.y + lightAttenuation.z*lightDist + lightAttenuation.w*(lightDist*lightDist) );
// Calculate diffuse component
diffuse = max(dot(N, L) , 0)*luminosity;
}
void lighting_fp(float diffuseIn : TEXCOORD0,
out float4 colour : COLOR,
uniform sampler1D diffuseRamp : register(s0))
{
// Step functions from textures
diffuseIn = tex1D(diffuseRamp, diffuseIn).x;
colour = diffuseIn;
}
//
//Specular
//
void specular_vp(float4 position : POSITION,
float3 normal : NORMAL,
// outputs
out float4 oPosition : POSITION,
out float specular : TEXCOORD0,
float2 texCoord : TEXCOORD0,
out float2 uv : TEXCOORD1,
// parameters
uniform float3 lightPosition, // object space
uniform float4 lightAttenuation,
uniform float3 eyePosition, // object space
uniform float4 shininess,
uniform float4x4 worldViewProj)
{
// calculate output position
oPosition = mul(worldViewProj, position);
// calculate light vector
float3 N = normalize(normal);
float3 L = normalize(lightPosition - position.xyz);
// Calculate specular component
float3 E = normalize(eyePosition - position.xyz);
float3 H = normalize(L + E);
//Calculate distance from light to object.
float lightDist = length( lightPosition - position.xyz );
float luminosity = 0;
if( lightAttenuation.x > lightDist)
luminosity = 1.0 / ( lightAttenuation.y + lightAttenuation.z*lightDist + lightAttenuation.w*(lightDist*lightDist) );
specular = pow(max(dot(N, H), 0), shininess)*luminosity;
uv = texCoord;
}
void specular_fp(float specularIn : TEXCOORD0,
float2 uv : TEXCOORD1,
out float4 colour : COLOR,
uniform float4 specular,
uniform sampler1D specularRamp : register(s0))
{
// Step functions from textures
specularIn = tex1D(specularRamp, specularIn).x;
colour = specular * specularIn;
}
//
//Specular with specular mapping image
//
void specularTex_fp(float specularIn : TEXCOORD0,
float2 uv : TEXCOORD1,
out float4 colour : COLOR,
uniform float4 specular,
uniform sampler1D specularRamp : register(s0),
uniform sampler2D specMap : register(s1))
{
// Step functions from textures
specularIn = tex1D(specularRamp, specularIn).x;
float4 specValue = tex2D(specMap, uv);
colour = specular * specValue * specularIn;
}
//
//Basic ambent lighting
//
void ambientOneTexture_vp(float4 position : POSITION,
float2 uv : TEXCOORD0,
out float4 oPosition : POSITION,
out float2 oUv : TEXCOORD0,
out float4 colour : COLOR,
uniform float4x4 worldViewProj,
uniform float4 ambient,
uniform float4 diffuse)
{
oPosition = mul(worldViewProj, position);
oUv = uv;
colour = diffuse*ambient;
}
//
//Edge shading
//
void celEdge_vp(float4 position : POSITION,
float2 uv : TEXCOORD0,
float3 normal : NORMAL,
out float4 oPosition : POSITION,
out float edge : TEXCOORD0,
uniform float4x4 worldViewProj,
uniform float3 eyePosition )
{
oPosition = mul(worldViewProj, position);
float3 N = normalize(normal);
float3 E = normalize(eyePosition - position.xyz);
edge = max(dot(N, E), 0);
}
void celEdge_fp(float edge : TEXCOORD0,
out float4 colour : COLOR,
uniform float4 edgeColor,
uniform sampler1D edgeRamp : register(s0))
{
edge = tex1D(edgeRamp, edge).x;
colour = (1-edge)*edgeColor;
}{CODE}
!!CelShading.material
{CODE(wrap="1", colors="c++")}
// A really basic ambient pass program, support for one texture coodinate set
vertex_program AmbientOneTexture cg
{
source CelShading.cg
entry_point ambientOneTexture_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto worldViewProj worldviewproj_matrix
param_named_auto ambient ambient_light_colour
param_named diffuse float4 1 1 1 1
}
}
vertex_program CelEdgeVP cg
{
source CelShading.cg
entry_point celEdge_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto worldViewProj worldviewproj_matrix
param_named_auto eyePosition camera_position_object_space
}
}
fragment_program CelEdgeFP cg
{
source CelShading.cg
entry_point celEdge_fp
profiles ps_1_1 arbfp1 fp20
default_params
{
param_named edgeColor float4 0 0 0 1
}
}
vertex_program CelShadingVP cg
{
source CelShading.cg
entry_point lighting_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto lightPosition light_position_object_space 0
param_named_auto lightAttenuation light_attenuation 0
param_named_auto worldViewProj worldviewproj_matrix
}
}
fragment_program CelShadingFP cg
{
source CelShading.cg
entry_point lighting_fp
profiles ps_1_1 arbfp1 fp20
}
vertex_program CelSpecularVP cg
{
source CelShading.cg
entry_point specular_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto lightPosition light_position_object_space 0
param_named_auto lightAttenuation light_attenuation 0
param_named_auto eyePosition camera_position_object_space
param_named_auto worldViewProj worldviewproj_matrix
param_named shininess float 10
}
}
fragment_program CelSpecularFP cg
{
source CelShading.cg
entry_point specular_fp
profiles ps_1_1 arbfp1 fp20
}
fragment_program CelSpecularTexFP cg
{
source CelShading.cg
entry_point specularTex_fp
profiles ps_1_1 arbfp1 fp20
}
material CelShading
{
technique
{
set $diffuse "1 1 1 1"
set $specular "1 1 1 1"
set $edge "0 0 0 1"
set $shininess "25"
// Base ambient pass
pass ambient
{
// base colours, not needed for rendering, but as information
// to lighting pass categorisation routine
ambient 1 1 1
diffuse 0 0 0
specular 0 0 0 0
// Really basic vertex program
// NB we don't use fixed function here because GL does not like
// mixing fixed function and vertex programs, depth fighting can
// be an issue
vertex_program_ref AmbientOneTexture
{
}
}
pass perlight
{
ambient 0 0 0
iteration once_per_light
scene_blend add
vertex_program_ref CelShadingVP
{
}
fragment_program_ref CelShadingFP
{
}
texture_unit
{
texture cel_shading_diffuse.png 1d
tex_address_mode clamp
filtering none
colour_op replace
}
}
pass decal
{
lighting off
vertex_program_ref AmbientOneTexture
{
param_named_auto worldViewProj worldviewproj_matrix
param_named ambient float4 1 1 1 1
param_named diffuse float4 $diffuse
}
scene_blend dest_colour zero
texture_unit decalmap
{
}
}
pass specular
{
ambient 0 0 0
iteration once_per_light
scene_blend add
vertex_program_ref CelSpecularVP
{
// map shininess from custom renderable param 1
param_named shininess float $shininess
}
fragment_program_ref CelSpecularFP
{
// map specular from custom renderable param 2
param_named specular float4 $specular
}
texture_unit
{
texture cel_shading_specular.png 1d
tex_address_mode clamp
filtering none
colour_op replace
}
}
pass edge
{
scene_blend alpha_blend
// base colours, not needed for rendering, but as information
// to lighting pass categorisation routine
ambient 1 1 1
diffuse 0 0 0
specular 0 0 0 0
// Really basic vertex program
// NB we don't use fixed function here because GL does not like
// mixing fixed function and vertex programs, depth fighting can
// be an issue
vertex_program_ref CelEdgeVP
{
}
fragment_program_ref CelEdgeFP
{
param_named edgeColor float4 $edge
}
texture_unit
{
texture cel_shading_edge.png 1d
tex_address_mode clamp
filtering none
}
}
}
}
material CelShadingSpecular
{
technique
{
set $diffuse "1 1 1 1"
set $specular "1 1 1 1"
set $edge "0 0 0 1"
set $shininess "25"
// Base ambient pass
pass ambient
{
// base colours, not needed for rendering, but as information
// to lighting pass categorisation routine
ambient 1 1 1
diffuse 0 0 0
specular 0 0 0 0
// Really basic vertex program
// NB we don't use fixed function here because GL does not like
// mixing fixed function and vertex programs, depth fighting can
// be an issue
vertex_program_ref AmbientOneTexture
{
}
}
pass perlight
{
ambient 0 0 0
iteration once_per_light
scene_blend add
vertex_program_ref CelShadingVP
{
}
fragment_program_ref CelShadingFP
{
}
texture_unit
{
texture cel_shading_diffuse.png 1d
tex_address_mode clamp
filtering none
colour_op replace
}
}
pass decal
{
lighting off
vertex_program_ref AmbientOneTexture
{
param_named_auto worldViewProj worldviewproj_matrix
param_named ambient float4 1 1 1 1
param_named diffuse float4 $diffuse
}
scene_blend dest_colour zero
texture_unit decalmap
{
}
}
pass specular
{
ambient 0 0 0
iteration once_per_light
scene_blend add
vertex_program_ref CelSpecularVP
{
// map shininess from custom renderable param 1
param_named shininess float $shininess
}
fragment_program_ref CelSpecularTexFP
{
// map specular from custom renderable param 2
param_named specular float4 $specular
}
texture_unit
{
texture cel_shading_specular.png 1d
tex_address_mode clamp
filtering none
colour_op replace
}
texture_unit specMap
{
texture white.png
}
}
pass edge
{
scene_blend alpha_blend
// base colours, not needed for rendering, but as information
// to lighting pass categorisation routine
ambient 1 1 1
diffuse 0 0 0
specular 0 0 0 0
// Really basic vertex program
// NB we don't use fixed function here because GL does not like
// mixing fixed function and vertex programs, depth fighting can
// be an issue
vertex_program_ref CelEdgeVP
{
}
fragment_program_ref CelEdgeFP
{
param_named edgeColor float4 $edge
}
texture_unit
{
texture cel_shading_edge.png 1d
tex_address_mode clamp
filtering none
}
}
}
}{CODE}
!!Usage
You can define two variables very much like in regular materials: diffuse and specular. There are two more variables: edge and shininess, edge is the color of the outline over the mesh. For texturing, you would define the pass: decal, then use the texture_unit: decalmap, and insert your texture.
This is an example material overriding the OgreHead Mesh's materials with the CelShading materials:
{CODE(wrap="1", colors="c++")}
import * from "CelShading.material"
material Ogre/Skin : CelShadingSpecular
{
technique
{
set $specular "0.3 0.3 0.3 1"
pass decal
{
texture_unit decalmap
{
texture GreenSkin.jpg
}
}
pass specular
{
texture_unit specMap
{
texture GreenSkin.jpg
}
}
}
}
material Ogre/Eyes : CelShading
{
technique
{
set $diffuse "1 0.7 0 1"
set $specular "1 0.8 0 1"
set $edge "0 0 0 0"
}
}
material Ogre/Tusks : CelShading
{
technique
{
set $diffuse "0.7 0.7 0.6 1"
set $specular "0 0 0 1"
set $edge "0.4 0.4 0.3 1"
pass decal
{
texture_unit decalmap
{
texture tusk.jpg
}
}
}
}
material Ogre/Earring : CelShading
{
technique
{
set $diffuse "0.7 0.7 0 1"
set $specular "1 1 1 1"
set $edge "0.6 0.6 0 1"
}
}{CODE}