Here's the shader with ambient light from Caelum and splatting. This worked very well with TSM, in fact it's based on the example shader from the Caelum demo.
I'm trying to use it with ETM too but with poor results because it doesn't splat the right textures so it uses the rock textures instead of the grass one.
Since it is based on the Caelum demo it can also easily support PSSM - at least there's some reference code to look at.
This is the default material:
Code: Select all
material Ainur/Materials/Base
{
technique Default
{
scheme Default
pass Main
{
ambient 1 1 1
diffuse 1 1 1
fog_override true none
vertex_program_ref Ainur/Programs/BasicVP
{
}
fragment_program_ref Ainur/Programs/BasicFP
{
}
texture_unit Main
{
tex_coord_set 0
}
}
}
}
This is the program definition:
Code: Select all
/*
* Based on Caelum code which is (c) 2008 Caelum team.
* See http://www.ogre3d.org/wiki/index.php/Caelum
*/
vertex_program Ainur/Programs/PSSM/ShadowCasterVP cg
{
source Lighting.cg
profiles vs_1_1 arbvp1
entry_point PssmShadowCasterVP
default_params
{
param_named_auto wvpMat worldviewproj_matrix
}
}
fragment_program Ainur/Programs/PSSM/ShadowCasterFP cg
{
source Lighting.cg
profiles ps_2_0 arbfp1
entry_point PssmShadowCasterFP
default_params
{
}
}
vertex_program Ainur/Programs/BasicVP cg
{
source Lighting.cg
entry_point MainVP
profiles vs_2_0 arbvp1
compile_arguments -posinv -DAMBIENT=1 -DTWO_LIGHTS=1
default_params
{
param_named_auto worldviewproj_matrix worldviewproj_matrix
param_named_auto inverse_transpose_worldview_matrix inverse_transpose_worldview_matrix
}
}
fragment_program Ainur/Programs/BasicFP cg
{
source Lighting.cg
entry_point MainFP
profiles ps_2_x arbfp1
compile_arguments -DAMBIENT=1 -DTWO_LIGHTS=1
default_params
{
param_named_auto derived_scene_colour derived_scene_colour
param_named_auto light_position_view_space_0 light_position_view_space 0
param_named_auto light_position_view_space_1 light_position_view_space 1
param_named_auto derived_light_diffuse_colour_0 derived_light_diffuse_colour 0
param_named_auto derived_light_diffuse_colour_1 derived_light_diffuse_colour 1
}
}
vertex_program Ainur/Programs/PSSM/AmbientVP cg
{
source Lighting.cg
profiles vs_1_1 arbvp1
entry_point MainVP
compile_arguments -DPSSM=0 -DAMBIENT=1
default_params
{
param_named_auto worldviewproj_matrix worldviewproj_matrix
}
}
fragment_program Ainur/Programs/PSSM/AmbientFP cg
{
source Lighting.cg
profiles ps_2_x arbfp1
entry_point MainFP
compile_arguments -DPSSM=0 -DAMBIENT=1
default_params
{
param_named_auto derived_scene_colour derived_scene_colour
}
}
vertex_program Ainur/Programs/PSSM/OneLightVP cg
{
source Lighting.cg
profiles vs_1_1 arbvp1
entry_point MainVP
compile_arguments -DPSSM=1 -DONE_LIGHT=1
default_params
{
param_named_auto worldviewproj_matrix worldviewproj_matrix
param_named_auto inverse_transpose_worldview_matrix inverse_transpose_worldview_matrix
param_named_auto texWorldViewProjMatrix0 texture_worldviewproj_matrix 0
param_named_auto texWorldViewProjMatrix1 texture_worldviewproj_matrix 1
param_named_auto texWorldViewProjMatrix2 texture_worldviewproj_matrix 2
}
}
fragment_program Ainur/Programs/PSSM/OneLightFP cg
{
source Lighting.cg
profiles ps_2_x arbfp1
entry_point MainFP
compile_arguments -DPSSM=1 -DONE_LIGHT=1
default_params
{
param_named_auto invShadowMapSize0 inverse_texture_size 0
param_named_auto invShadowMapSize1 inverse_texture_size 1
param_named_auto invShadowMapSize2 inverse_texture_size 2
param_named_auto light_position_view_space light_position_view_space 0
param_named_auto derived_light_diffuse_colour derived_light_diffuse_colour 0
}
}
This is the definition of terrain related programs:
Code: Select all
/*
* Based on Caelum code which is (c) 2008 Caelum team.
* See http://www.ogre3d.org/wiki/index.php/Caelum
*/
fragment_program Ainur/Programs/BasicFP/Terrain cg
{
source Lighting.cg
entry_point MainFP
profiles ps_2_x arbfp1
compile_arguments -DTERRAIN=1 -DAMBIENT=1 -DTWO_LIGHTS=1
default_params
{
param_named_auto derived_scene_colour derived_scene_colour
param_named_auto light_position_view_space_0 light_position_view_space 0
param_named_auto light_position_view_space_1 light_position_view_space 1
param_named_auto derived_light_diffuse_colour_0 derived_light_diffuse_colour 0
param_named_auto derived_light_diffuse_colour_1 derived_light_diffuse_colour 1
param_named splatScaleX float 192
param_named splatScaleZ float 192
}
}
fragment_program Ainur/Programs/PSSM/AmbientFP/Terrain cg
{
source Lighting.cg
profiles ps_2_x arbfp1
entry_point MainFP
compile_arguments -DPSSM=0 -DAMBIENT=1 -DTERRAIN=1
default_params
{
param_named_auto derived_scene_colour derived_scene_colour
param_named splatScaleX float 192
param_named splatScaleZ float 192
}
}
fragment_program Ainur/Programs/PSSM/OneLightFP/Terrain cg
{
source Lighting.cg
profiles ps_2_x arbfp1
entry_point MainFP
compile_arguments -DPSSM=1 -DONE_LIGHT=1 -DTERRAIN
default_params
{
param_named_auto invShadowMapSize0 inverse_texture_size 0
param_named_auto invShadowMapSize1 inverse_texture_size 1
param_named_auto invShadowMapSize2 inverse_texture_size 2
param_named_auto light_position_view_space light_position_view_space 0
param_named_auto derived_light_diffuse_colour derived_light_diffuse_colour 0
param_named splatScaleX float 192
param_named splatScaleZ float 192
}
}
This is the Cg source:
Code: Select all
/*
* Based on Caelum code which is (c) 2008 Caelum team.
* See http://www.ogre3d.org/wiki/index.php/Caelum
*/
void PssmShadowCasterVP
(
float4 position : POSITION,
out float4 oPosition : POSITION,
out float2 oDepth : TEXCOORD0,
uniform float4x4 wvpMat)
{
// This is the view space position
oPosition = mul(wvpMat, position);
// Depth info for the fragment.
oDepth.x = oPosition.z;
oDepth.y = oPosition.w;
// clamp z to zero. seem to do the trick. :-/
//oPosition.z = max(oPosition.z, 0);
}
void PssmShadowCasterFP
(
float2 depth : TEXCOORD0,
out float4 oColour : COLOR,
uniform float4 pssmSplitPoints)
{
float finalDepth = depth.x / depth.y;
oColour = float4(finalDepth, finalDepth, finalDepth, 1);
}
float shadowPCF
(
sampler2D shadowMap,
float4 shadowMapPos,
float2 offset
)
{
shadowMapPos = shadowMapPos / shadowMapPos.w;
float2 uv = shadowMapPos.xy;
float3 o = float3(offset, -offset.x) * 0.3f;
// Note: We using 2x2 PCF. Good enough and is alot faster.
float c = (shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.xy).r) ? 1 : 0; // top left
c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.xy).r) ? 1 : 0; // bottom right
c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy + o.zy).r) ? 1 : 0; // bottom left
c += (shadowMapPos.z <= tex2D(shadowMap, uv.xy - o.zy).r) ? 1 : 0; // top right
//float c = (shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy - o.xyyy).r) ? 1 : 0; // top left
//c += (shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy + o.xyyy).r) ? 1 : 0; // bottom right
//c += (shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy + o.zyyy).r) ? 1 : 0; // bottom left
//c += (shadowMapPos.z <= tex2Dlod(shadowMap, uv.xyyy - o.zyyy).r) ? 1 : 0; // top right
return c / 4;
}
// Vertex program entry point.
void MainVP
(
in float4 iPosition : POSITION,
in float2 iTexCoord : TEXCOORD0,
in float4 iNormal : NORMAL,
#if PSSM
uniform float4x4 texWorldViewProjMatrix0,
uniform float4x4 texWorldViewProjMatrix1,
uniform float4x4 texWorldViewProjMatrix2,
#endif
uniform float4x4 worldviewproj_matrix,
uniform float4x4 inverse_transpose_worldview_matrix,
#if PSSM
out float4 oLightPosition2 : TEXCOORD5,
out float4 oLightPosition1 : TEXCOORD4,
out float4 oLightPosition0 : TEXCOORD3,
out float oSplitPoint : TEXCOORD2,
#endif
out float2 oTexCoord : TEXCOORD0,
out float3 oNormal : TEXCOORD1,
out float4 oPosition : POSITION
)
{
oPosition = mul(worldviewproj_matrix, iPosition);
oTexCoord = iTexCoord;
oNormal = normalize(mul(inverse_transpose_worldview_matrix, iNormal).xyz);
#if PSSM
// Calculate the position of vertex in light space
oSplitPoint = oPosition.z;
oLightPosition0 = mul(texWorldViewProjMatrix0, iPosition);
oLightPosition1 = mul(texWorldViewProjMatrix1, iPosition);
oLightPosition2 = mul(texWorldViewProjMatrix2, iPosition);
#endif
}
// Fragment program entry point.
void MainFP
(
in float2 iTexcoord : TEXCOORD0,
in float3 iNormal : TEXCOORD1,
#if PSSM
in float4 iSplitPoint : TEXCOORD2,
in float4 iLightPosition0 : TEXCOORD3,
in float4 iLightPosition1 : TEXCOORD4,
in float4 iLightPosition2 : TEXCOORD5,
#endif
#if PSSM
uniform float4 invShadowMapSize0,
uniform float4 invShadowMapSize1,
uniform float4 invShadowMapSize2,
uniform float4 pssmSplitPoints,
uniform sampler2D shadowMap0 : register(s0),
uniform sampler2D shadowMap2 : register(s2),
uniform sampler2D shadowMap1 : register(s1),
uniform sampler mainTexture : register(s3),
#else
uniform sampler mainTexture : register(s0),
#endif
#if TERRAIN
uniform sampler2D covMap1 : register(s0),
uniform sampler2D covMap2 : register(s1),
uniform sampler2D covMap3 : register(s2),
uniform sampler2D splat1 : register(s3),
uniform sampler2D splat2 : register(s4),
uniform sampler2D splat3 : register(s5),
uniform sampler2D splat4 : register(s6),
uniform sampler2D splat5 : register(s7),
uniform sampler2D splat6 : register(s8),
uniform sampler2D splat7 : register(s9),
uniform sampler2D splat8 : register(s10),
uniform sampler2D splat9 : register(s11),
uniform float splatScaleX,
uniform float splatScaleZ,
#endif
#if AMBIENT
uniform float4 derived_scene_colour,
#endif
#if ONE_LIGHT
uniform float4 light_position_view_space,
uniform float4 derived_light_diffuse_colour,
#endif
#if TWO_LIGHTS
uniform float4 light_position_view_space_0,
uniform float4 derived_light_diffuse_colour_0,
uniform float4 light_position_view_space_1,
uniform float4 derived_light_diffuse_colour_1,
#endif
out float4 oColour : COLOR
)
{
// Calculate shadow
float shadowing = 1.0f;
#if PSSM
float4 splitColour;
if (iSplitPoint <= pssmSplitPoints.y)
{
splitColour = float4(0.1, 0, 0, 1);
shadowing = shadowPCF(shadowMap0, iLightPosition0, invShadowMapSize0.xy);
}
else if (iSplitPoint <= pssmSplitPoints.z)
{
splitColour = float4(0, 0.1, 0, 1);
shadowing = shadowPCF(shadowMap1, iLightPosition1, invShadowMapSize1.xy);
}
else
{
splitColour = float4(0.1, 0.1, 0, 1);
shadowing = shadowPCF(shadowMap2, iLightPosition2, invShadowMapSize2.xy);
}
#endif
// The colour we'll return
oColour = float4(0, 0, 0, 0);
// Base colour
float4 baseColour;
#if TERRAIN
// Modulate two scaled textures.
float3 cov1 = tex2D(covMap1, iTexcoord).rgb;
float3 cov2 = tex2D(covMap2, iTexcoord).rgb;
float3 cov3 = tex2D(covMap3, iTexcoord).rgb;
iTexcoord.x *= splatScaleX;
iTexcoord.y *= splatScaleZ;
baseColour = tex2D(splat1, iTexcoord) * cov1.x
+ tex2D(splat2, iTexcoord) * cov1.y
+ tex2D(splat3, iTexcoord) * cov1.z
+ tex2D(splat4, iTexcoord) * cov2.x
+ tex2D(splat5, iTexcoord) * cov2.y
+ tex2D(splat6, iTexcoord) * cov2.z
+ tex2D(splat7, iTexcoord) * cov3.x
+ tex2D(splat8, iTexcoord) * cov3.y
+ tex2D(splat9, iTexcoord) * cov3.z;
#else
baseColour = tex2D(mainTexture, iTexcoord);
#endif
#if AMBIENT
oColour += baseColour * derived_scene_colour;
#endif
#if ONE_LIGHT
float3 normal = normalize(iNormal);
float diffuse_factor = max(0, dot(float4(normal, 1), light_position_view_space));
float4 light_colour = diffuse_factor * derived_light_diffuse_colour * shadowing;
//if (derived_light_diffuse_colour.b > 2) {
//}
//light_colour = splitColour * 100;
oColour += baseColour * light_colour;
#endif
#if TWO_LIGHTS
float3 normal = normalize(iNormal);
// Accumulate two lights
float4 light_colour = float4(0, 0, 0, 0);
float diffuse_factor_0 = max(0, dot(float4(normal, 1), light_position_view_space_0));
light_colour += diffuse_factor_0 * derived_light_diffuse_colour_0 * shadowing;
float diffuse_factor_1 = max(0, dot(float4(normal, 1), light_position_view_space_1));
light_colour += diffuse_factor_1 * derived_light_diffuse_colour_1 * shadowing;
oColour += baseColour * light_colour;
#endif
}
This is the terrain material:
Code: Select all
/*
* Based on Caelum code which is (c) 2008 Caelum team.
* See http://www.ogre3d.org/wiki/index.php/Caelum
*/
import * from AinurDefault.material
// Terrain material
material Ainur/Materials/Terrain : Ainur/Materials/Base
{
technique Default
{
pass Main
{
fragment_program_ref Ainur/Programs/BasicFP/Terrain
{
}
}
}
}
Here's a material for a crate mesh:
Code: Select all
import * from "AinurDefault.material"
material Crate : Ainur/Materials/Base
{
set_texture_alias Main crate.png
}
As you can see splatting is very easy and it's actually the same thing as ETM.
I should probably do something for LoD though, but I'm still learning Cg programming.
Anyway, the terrain material should have texture_units for coverage and splatting textures. I am adding them with C++ code, here's my code (which is made with ETM in mind, when I was using TSM I did put the texture_units on the material file):
Code: Select all
// Create splatting manager
splattingManager = new ET::SplattingManager(COVERAGE_NAME,
stage, coverMapSize, coverMapSize, COVERAGE_MAPS);
splattingManager->setNumTextures(SPLATTING_TEXTURES);
// Load coverage maps
Ogre::LogManager::getSingleton().logMessage("[TerrainSystem] Loading coverage maps...");
for (unsigned int i = 0; i < splattingManager->getNumMaps(); i++)
{
image.load("coverage." + Ogre::StringConverter::toString(i) + "." + splatExt, stage);
splattingManager->loadMapFromImage(i, image);
}
// Get material and technique
Ogre::MaterialPtr matPtr = Ogre::MaterialManager::getSingleton().getByName("Ainur/Materials/Terrain");
Ogre::Technique* technique = matPtr->getTechnique("Default");
if (!technique)
{
OGRE_EXCEPT(Ogre::Exception::ERR_RT_ASSERTION_FAILED,
"Can't find terrain material technique.", "TerrainSystemPrivate::load");
}
Ogre::Pass* splattingPass = technique->getPass("Main");
if (!splattingPass)
{
OGRE_EXCEPT(Ogre::Exception::ERR_RT_ASSERTION_FAILED,
"Can't find Splatting pass from terrain material.", "TerrainSystemPrivate::load");
}
for (int i = 0; i < COVERAGE_MAPS; i++)
{
Ogre::TextureUnitState* coverage = splattingPass->createTextureUnitState();
coverage->setTextureName(COVERAGE_NAME + Ogre::StringConverter::toString(i));
coverage->setTextureCoordSet(i);
}
for (int i = 0; i < SPLATTING_TEXTURES; i++)
{
Ogre::TextureUnitState* splatting = splattingPass->createTextureUnitState();
Ogre::String keyName = "Splatting" + Ogre::StringConverter::toString(i);
Ogre::String fileName = file.getSetting(keyName, "Terrain", "splatting_rock0") + "." + splatExt;
splatting->setTextureName(fileName);
splatting->setTextureCoordSet(COVERAGE_MAPS + i);
}
// Set material
terrainManager->setMaterial(matPtr);