Short Version:
Code: Select all
This sample shows how to use your old fashioned Ogre material with Ogre::Terrain.
More or less typical ArtifexTerra / ETM terrain data. I tried to find how to get a multipass material to work, but I didn't manage or find a solution to avoid the skirts flashing through in a very disturbing way. So if anyone knows how to do that it would be great if you could share your knowledge . The shader works with common JPG and PNG textures, so no DDS required. And of course you don't need the provided shaders, any single pass material works.
There is still a small glitch with the dynamic lighting, in certain light angles the tiles seem to have slightly different lighting, if anyone has a clue how to fix it... would be great
Edit: *apparently there is something wrong with the tangent calculation, working on a fix.
You can download the whole sample with textures etc as zip here: https://sourceforge.net/projects/artifexterra3d/files/
Usage:
Code: Select all
// Init custom materialgenerator
TerrainMaterialGeneratorPtr terrainMaterialGenerator;
// Set Ogre Material with the name "TerrainMaterial" in constructor
TerrainMaterial *terrainMaterial = OGRE_NEW TerrainMaterial("TerrainMaterial");
terrainMaterialGenerator.bind( terrainMaterial );
terrainGlobals->setDefaultMaterialGenerator( terrainMaterialGenerator );
Change Terrain material:
Code: Select all
terrainMaterial->setMaterialByName("MyOtherMaterialName");
Code: Select all
#ifndef TERRAINMATERIAL_H
#define TERRAINMATERIAL_H
// V1.0
#include "Ogre.h"
#include "OgreTerrain.h"
#include "OgreTerrainMaterialGenerator.h"
class TerrainMaterial : public Ogre::TerrainMaterialGenerator
{
public:
TerrainMaterial(Ogre::String materialName, bool addNormalmap=true, bool cloneMaterial=true);
void setMaterialByName(const Ogre::String materialName);
void addNormalMapOnGenerate(bool set) { mAddNormalMap=set; };
void cloneMaterialOnGenerate(bool set) { mCloneMaterial=set; };
Ogre::String getMaterialName() { return mMaterialName; };
class Profile : public Ogre::TerrainMaterialGenerator::Profile
{
public:
Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc);
~Profile();
bool isVertexCompressionSupported() const { return false; }
Ogre::MaterialPtr generate(const Ogre::Terrain* terrain);
Ogre::MaterialPtr generateForCompositeMap(const Ogre::Terrain* terrain);
Ogre::uint8 getMaxLayers(const Ogre::Terrain* terrain) const;
void updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain);
void updateParamsForCompositeMap(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain);
void requestOptions(Ogre::Terrain* terrain);
};
protected:
Ogre::String mMaterialName;
bool mCloneMaterial;
bool mAddNormalMap;
};
#endif
Code: Select all
#include "TerrainMaterial.h"
TerrainMaterial::TerrainMaterial(Ogre::String materialName, bool addNormalmap, bool cloneMaterial)
: mMaterialName(materialName), mAddNormalMap(addNormalmap), mCloneMaterial(cloneMaterial)
{
mProfiles.push_back(OGRE_NEW Profile(this, "OgreMaterial", "Profile for rendering Ogre standard material"));
setActiveProfile("OgreMaterial");
}
void TerrainMaterial::setMaterialByName(const Ogre::String materialName) {
mMaterialName = materialName;
_markChanged();
};
// -----------------------------------------------------------------------------------------------------------------------
TerrainMaterial::Profile::Profile(Ogre::TerrainMaterialGenerator* parent, const Ogre::String& name, const Ogre::String& desc)
: Ogre::TerrainMaterialGenerator::Profile(parent, name, desc)
{
};
TerrainMaterial::Profile::~Profile()
{
};
Ogre::MaterialPtr TerrainMaterial::Profile::generate(const Ogre::Terrain* terrain)
{
const Ogre::String& matName = terrain->getMaterialName();
Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(matName);
if (!mat.isNull())
Ogre::MaterialManager::getSingleton().remove(matName);
TerrainMaterial* parent = (TerrainMaterial*)getParent();
// Set Ogre material
mat = Ogre::MaterialManager::getSingleton().getByName(parent->mMaterialName);
// Clone material
if(parent->mCloneMaterial) {
mat = mat->clone(matName);
parent->mMaterialName = matName;
}
// Add normalmap
if(parent->mAddNormalMap) {
// Get default pass
Ogre::Pass *p = mat->getTechnique(0)->getPass(0);
// Add terrain's global normalmap to renderpass so the fragment program can find it.
Ogre::TextureUnitState *tu = p->createTextureUnitState(matName+"/nm");
Ogre::TexturePtr nmtx = terrain->getTerrainNormalMap();
tu->_setTexturePtr(nmtx);
}
return mat;
};
Ogre::MaterialPtr TerrainMaterial::Profile::generateForCompositeMap(const Ogre::Terrain* terrain)
{
return terrain->_getCompositeMapMaterial();
};
Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const
{
return 0;
};
void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain)
{
};
void TerrainMaterial::Profile::updateParamsForCompositeMap(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain)
{
};
void TerrainMaterial::Profile::requestOptions(Ogre::Terrain* terrain)
{
terrain->_setMorphRequired(false);
terrain->_setNormalMapRequired(true); // enable global normal map
terrain->_setLightMapRequired(false);
terrain->_setCompositeMapRequired(false);
};
CG 2_x Vertex Shader terrain_vp.cg:
Code: Select all
void terrain_vp(
float4 position : POSITION,
float2 uv : TEXCOORD0,
float delta : BLENDWEIGHT,
out float4 oPosition : POSITION,
out float2 oUv : TEXCOORD0,
out float4 oColor : COLOR,
uniform float4 ambient,
uniform float4x4 worldViewProj,
uniform float morphFactor
)
{
position.y = position.y + (delta.x * morphFactor);
oPosition = mul(worldViewProj, position);
oUv = uv;
oColor = ambient;
}
Code: Select all
float4 expand(float4 v)
{
return v * 2 - 1;
}
void terrain_fp
(
float2 iTexCoord0 : TEXCOORD0,
float4 iPosition : TEXCOORD1,
float iAmbient : COLOR,
uniform float4 lightDiffuse,
uniform float4 lightSpecular,
uniform float exponent,
uniform float4 lightPosition,
uniform float3 eyePosition,
uniform float4 attenuation,
out float4 oColor : COLOR,
uniform sampler2D covMap1,
uniform sampler2D covMap2,
uniform sampler2D covMap3,
uniform sampler2D splat1,
uniform sampler2D splat2,
uniform sampler2D splat3,
uniform sampler2D splat4,
uniform sampler2D splat5,
uniform sampler2D splat6,
uniform sampler2D splat7,
uniform sampler2D splat8,
uniform sampler2D splat9,
uniform sampler2D colourMap,
uniform sampler2D lightMap,
/* global normalmap is added by the TerrainMaterialGenerator to TerrainMaterial
since Ogre::Terrain doesn't have vertex normals */
uniform sampler2D normalMap,
uniform float splatScaleX,
uniform float splatScaleZ
)
{
float3 cov1 = tex2D(covMap1, iTexCoord0).rgb;
float3 cov2 = tex2D(covMap2, iTexCoord0).rgb;
float3 cov3 = tex2D(covMap3, iTexCoord0).rgb;
// save TexCoord for Global Textures
float2 globalTexCoord = iTexCoord0;
iTexCoord0.x *= splatScaleX;
iTexCoord0.y *= splatScaleZ;
// calculate Splatting
oColor = tex2D(splat1, iTexCoord0) * cov1.x
+ tex2D(splat2, iTexCoord0) * cov1.y
+ tex2D(splat3, iTexCoord0) * cov1.z
+ tex2D(splat4, iTexCoord0) * cov2.x
+ tex2D(splat5, iTexCoord0) * cov2.y
+ tex2D(splat6, iTexCoord0) * cov2.z
+ tex2D(splat7, iTexCoord0) * cov3.x
+ tex2D(splat8, iTexCoord0) * cov3.y
+ tex2D(splat9, iTexCoord0) * cov3.z;
// add global Colourmap
oColor *= tex2D(colourMap, globalTexCoord);
// add global Lightmap
oColor *= tex2D(lightMap, globalTexCoord); //*tex2D(lightMap, globalTexCoord);
// lighting
float3 normal = expand(tex2D(normalMap, globalTexCoord)).rgb;
normal = normalize(normal);
float3 lightDir = lightPosition.xyz - (iPosition.xyz * lightPosition.w);
lightDir = normalize(lightDir);
float distance = length(lightDir);
float lumination = 1 / (attenuation.y + attenuation.z * distance + attenuation.w * distance * distance);
lumination = min(lumination, 1.0);
float3 eyeDir = normalize(eyePosition - iPosition.xyz);
eyeDir = normalize(eyeDir);
float3 halfAngle = normalize(lightDir + eyeDir);
float NdotL = dot(lightDir, normal);
float NdotH = dot(halfAngle, normal);
float4 Lit = lit(NdotL, NdotH, exponent);
// add lighting to color
oColor *= iAmbient + lumination * (lightDiffuse * Lit.y + lightSpecular * Lit.z * Lit.y);
}
Code: Select all
vertex_program Terrain/Programs/TerrainVP cg
{
source TerrainVP.cg
entry_point terrain_vp
profiles vs_2_x arbvp1
default_params
{
param_named_auto morphFactor custom 77
param_named_auto worldViewProj worldviewproj_matrix
param_named_auto ambient ambient_light_colour
}
}
fragment_program Terrain/Programs/TerrainFP cg
{
source TerrainFP.cg
entry_point terrain_fp
profiles ps_2_x arbfp1
default_params
{
param_named_auto lightDiffuse light_diffuse_colour 0
param_named_auto lightSpecular light_specular_colour 0
param_named_auto lightPosition light_position_object_space 0
param_named_auto eyePosition camera_position_object_space
param_named_auto attenuation light_attenuation 0
}
}
Code: Select all
material TerrainMaterial
{
technique
{
// requires PS 2.x
pass
{
vertex_program_ref Terrain/Programs/TerrainVP
{
}
fragment_program_ref Terrain/Programs/TerrainFP
{
param_named exponent float 63
param_named splatScaleX float 96
param_named splatScaleZ float 96
}
texture_unit 0
{
// first coverage map
texture ETcoverage.0.png
}
texture_unit 1
{
// second coverage map
texture ETcoverage.1.png
}
texture_unit 2
{
// third coverage map
texture ETcoverage.2.png
}
// splatting textures
texture_unit 3
{
texture splatting0.png
}
texture_unit 4
{
texture splatting1.png
}
texture_unit
{
texture splatting2.png
}
texture_unit
{
texture splatting3.png
}
texture_unit
{
texture splatting4.png
}
texture_unit
{
texture splatting5.png
}
texture_unit
{
texture splatting6.png
}
texture_unit
{
texture splatting7.png
}
texture_unit
{
texture splatting8.png
}
texture_unit
{
texture artifexterra3d_colourmap.png
}
texture_unit
{
texture ETlightmap.png
}
} // pass
} // technique
}
Going to set a wiki page up with it when I find the time. Hope this is of use for some of you. Feedback, critics and improvment are most welcome.
//Nauk