Deferred Shading Terrain Material Generator

A place to show off your latest screenshots and for people to comment on them. Only start a new thread here if you have some nice images to show off!
Post Reply
User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Deferred Shading Terrain Material Generator

Post by Xplodwild » Mon Aug 06, 2012 12:39 pm

Hi everyone!

I recently switched to Deferred Shading for my game, and I was also using terrains. Obviously, the default TerrainMaterialGenerator gave an ugly green lime and random black dots, well, garbage all the way :) . After spending a few days figuring out what needs to be done, and how to do it, I spend 3 days fixing the damn normal calculation, as it needs to be done in eye-space and not object space, and surprisingly there is almost nothing on the web that shows this... Luckily enough, I came across a small GLSL code that showed how to do it. After playing around a bit, I could finally have this working properly :)

Limitations:
-There are visible seams when shadows are enabled
- Lightmap isn't supported
- Only Cg shader (HLSL conversion would be easy)
- Composite map and low LOD shader not extensively tested

Other than that, it works quite fine:

Image

You can find the generator code and a few other details on the Wiki :
http://www.ogre3d.org/tikiwiki/tiki-ind ... +Generator


I'd be interested if someone have ideas on how to fix shadows seams (at least sub-page seams), as LOD seams can be avoided in most situations.

Have fun :)
Last edited by Xplodwild on Mon Aug 06, 2012 2:10 pm, edited 1 time in total.
0 x

User avatar
Mind Calamity
Ogre Magi
Posts: 1255
Joined: Sat Dec 25, 2010 2:55 pm
Location: Macedonia

Re: Deferred Shading Terrain Material Generator

Post by Mind Calamity » Mon Aug 06, 2012 1:36 pm

I'm not a wiki expert, but I created a new page under "Snippets/Materials".

And I fixed up some misspellings and made the code parts collapsible.

Thanks for posting this. :)
0 x
BitBucket username changed to iboshkov (from MindCalamity)
Do you need help? What have you tried?
- xavier
---------------------
HkOgre - a Havok Integration for OGRE | Simple SSAO | My Blog | My YouTube | My DeviantArt


nickG
Greenskin
Posts: 122
Joined: Fri Jan 20, 2012 6:44 pm
Location: Russia,Moscow

Re: Deferred Shading Terrain Material Generator

Post by nickG » Thu Aug 16, 2012 9:31 pm

Thanks for posting

Performance is very good,but shadows not working for me.

I'm using caelum on scene.

On standart shadow generator shadows working a fine.

What you can say?
0 x

User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Re: Deferred Shading Terrain Material Generator

Post by Xplodwild » Thu Aug 16, 2012 11:15 pm

Did you enable shadows in the TerrainGlobalOptions?
Make sure your LightExtrusionDistance are right also. I had a couple of issues with them mis-set by default. Check the sample.
0 x

nickG
Greenskin
Posts: 122
Joined: Fri Jan 20, 2012 6:44 pm
Location: Russia,Moscow

Re: Deferred Shading Terrain Material Generator

Post by nickG » Fri Aug 17, 2012 12:59 am

Xplodwild wrote:Did you enable shadows in the TerrainGlobalOptions?
of course enabled.

Make sure your LightExtrusionDistance are right also.
Ok,later i will say result

Thanks for reply
0 x

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Thu Oct 04, 2012 4:07 pm

Hi,

i cannot get your code to work. Its compiling, but the Terrain is always totaly lit up (as shown in the attachment). Maybe i use it the wrong way. Looks like full ambient light (but no ambient enabled at all).

To setup the textures i do this:

Code: Select all

		Ogre::Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
		defaultimp.terrainSize = mTerrainGroup->getTerrainSize();
		defaultimp.worldSize = mWorldSizePerTerrainSize * defaultimp.terrainSize;
		defaultimp.inputScale = DEFAULT_IMPORT_IMAGE_SCALING;
		defaultimp.minBatchSize = DEFAULT_IMPORT_MIN_BATCHSIZE;
		defaultimp.maxBatchSize = DEFAULT_IMPORT_MAX_BATCHSIZE;

		defaultimp.layerList.resize(3);
		defaultimp.layerList[0].worldSize = 100;
		defaultimp.layerList[0].textureNames.push_back("splatting1.png");
		defaultimp.layerList[0].textureNames.push_back("testnormalmap.png");
		defaultimp.layerList[1].worldSize = 30;
		defaultimp.layerList[1].textureNames.push_back("splatting2.png");
		defaultimp.layerList[1].textureNames.push_back("testnormalmap.png");
		defaultimp.layerList[2].worldSize = 200;
		defaultimp.layerList[2].textureNames.push_back("splatting3.png");
		defaultimp.layerList[2].textureNames.push_back("testnormalmap.png");
Is it the right way to setup the normal maps as second texture name?

Ogre-Log:

Code: Select all

17:05:36: Creating resource group General
17:05:36: Creating resource group Internal
17:05:36: Creating resource group Autodetect
17:05:36: SceneManagerFactory for type 'DefaultSceneManager' registered.
17:05:36: Registering ResourceManager for type Material
17:05:36: Registering ResourceManager for type Mesh
17:05:36: Registering ResourceManager for type Skeleton
17:05:36: MovableObjectFactory for type 'ParticleSystem' registered.
17:05:36: OverlayElementFactory for type Panel registered.
17:05:36: OverlayElementFactory for type BorderPanel registered.
17:05:36: OverlayElementFactory for type TextArea registered.
17:05:36: Registering ResourceManager for type Font
17:05:36: ArchiveFactory for archive type FileSystem registered.
17:05:36: ArchiveFactory for archive type Zip registered.
17:05:36: ArchiveFactory for archive type EmbeddedZip registered.
17:05:36: DDS codec registering
17:05:36: FreeImage version: 3.15.3
17:05:36: This program uses FreeImage, a free, open source image library supporting all common bitmap formats. See http://freeimage.sourceforge.net for details
17:05:36: Supported formats: bmp,ico,jpg,jif,jpeg,jpe,jng,koa,iff,lbm,mng,pbm,pbm,pcd,pcx,pgm,pgm,png,ppm,ppm,ras,tga,targa,tif,tiff,wap,wbmp,wbm,psd,cut,xbm,xpm,gif,hdr,g3,sgi,exr,j2k,j2c,jp2,pfm,pct,pict,pic,3fr,arw,bay,bmq,cap,cine,cr2,crw,cs1,dc2,dcr,drf,dsc,dng,erf,fff,ia,iiq,k25,kc2,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,ptx,pxn,qtk,raf,raw,rdc,rw2,rwl,rwz,sr2,srf,srw,sti
17:05:36: Registering ResourceManager for type HighLevelGpuProgram
17:05:36: Registering ResourceManager for type Compositor
17:05:36: MovableObjectFactory for type 'Entity' registered.
17:05:36: MovableObjectFactory for type 'Light' registered.
17:05:36: MovableObjectFactory for type 'BillboardSet' registered.
17:05:36: MovableObjectFactory for type 'ManualObject' registered.
17:05:36: MovableObjectFactory for type 'BillboardChain' registered.
17:05:36: MovableObjectFactory for type 'RibbonTrail' registered.
17:05:36: Loading library .\RenderSystem_Direct3D9_d
17:05:36: Installing plugin: D3D9 RenderSystem
17:05:36: D3D9 : Direct3D9 Rendering Subsystem created.
17:05:36: D3D9: Driver Detection Starts
17:05:36: D3D9: Driver Detection Ends
17:05:36: Plugin successfully installed
17:05:36: Loading library .\RenderSystem_GL_d
17:05:36: Installing plugin: GL RenderSystem
17:05:36: OpenGL Rendering Subsystem created.
17:05:36: Plugin successfully installed
17:05:36: Loading library .\Plugin_CgProgramManager_d
17:05:36: Installing plugin: Cg Program Manager
17:05:36: Plugin successfully installed
17:05:36: *-*-* OGRE Initialising
17:05:36: *-*-* Version 1.8.0 (Byatis)
17:05:36: Added resource location 'data/models' of type 'FileSystem' to resource group 'General'
17:05:36: Added resource location 'data/materials/textures' of type 'FileSystem' to resource group 'General'
17:05:36: Added resource location 'data/materials/scripts' of type 'FileSystem' to resource group 'General'
17:05:36: Added resource location 'data/materials/programs' of type 'FileSystem' to resource group 'General'
17:05:36: D3D9 : RenderSystem Option: Allow NVPerfHUD = No
17:05:36: D3D9 : RenderSystem Option: FSAA = 0
17:05:36: D3D9 : RenderSystem Option: Fixed Pipeline Enabled = Yes
17:05:36: D3D9 : RenderSystem Option: Floating-point mode = Fastest
17:05:36: D3D9 : RenderSystem Option: Full Screen = No
17:05:36: D3D9 : RenderSystem Option: Multi device memory hint = Use minimum system memory
17:05:36: D3D9 : RenderSystem Option: Rendering Device = Monitor-1-NVIDIA GeForce GTX 460
17:05:36: D3D9 : RenderSystem Option: Resource Creation Policy = Create on all devices
17:05:36: D3D9 : RenderSystem Option: Use Multihead = Auto
17:05:36: D3D9 : RenderSystem Option: VSync = No
17:05:36: D3D9 : RenderSystem Option: VSync Interval = 1
17:05:36: D3D9 : RenderSystem Option: Video Mode = 640 x 480 @ 16-bit colour
17:05:36: D3D9 : RenderSystem Option: sRGB Gamma Conversion = No
17:05:37: CPU Identifier & Features
17:05:37: -------------------------
17:05:37:  *   CPU ID: AuthenticAMD: AMD Phenom(tm) II X4 955 Processor
17:05:37:  *      SSE: yes
17:05:37:  *     SSE2: yes
17:05:37:  *     SSE3: yes
17:05:37:  *      MMX: yes
17:05:37:  *   MMXEXT: yes
17:05:37:  *    3DNOW: yes
17:05:37:  * 3DNOWEXT: yes
17:05:37:  *     CMOV: yes
17:05:37:  *      TSC: yes
17:05:37:  *      FPU: yes
17:05:37:  *      PRO: yes
17:05:37:  *       HT: no
17:05:37: -------------------------
17:05:37: D3D9 : Subsystem Initialising
17:05:37: Registering ResourceManager for type Texture
17:05:37: Registering ResourceManager for type GpuProgram
17:05:37: ***************************************
17:05:37: *** D3D9 : Subsystem Initialised OK ***
17:05:37: ***************************************
17:05:37: D3D9RenderSystem::_createRenderWindow "QOgreWidget_RenderWindow", 732x781 windowed  miscParams: externalWindowHandle=2496594 
17:05:37: D3D9 : Created D3D9 Rendering Window 'QOgreWidget_RenderWindow' : 732x781, 32bpp
17:05:37: D3D9 : WARNING - disabling VSync in windowed mode can cause timing issues at lower frame rates, turn VSync on if you observe this problem.
17:05:37: D3D9: Vertex texture format supported - PF_A8R8G8B8
17:05:37: D3D9: Vertex texture format supported - PF_B8G8R8A8
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT16_RGB
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT16_RGBA
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT32_RGB
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT32_RGBA
17:05:37: D3D9: Vertex texture format supported - PF_R8G8B8A8
17:05:37: D3D9: Vertex texture format supported - PF_DEPTH
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT16_R
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT32_R
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT16_GR
17:05:37: D3D9: Vertex texture format supported - PF_FLOAT32_GR
17:05:37: D3D9: Vertex texture format supported - PF_PVRTC_RGB2
17:05:37: D3D9: Vertex texture format supported - PF_PVRTC_RGBA2
17:05:37: D3D9: Vertex texture format supported - PF_PVRTC_RGB4
17:05:37: D3D9: Vertex texture format supported - PF_PVRTC_RGBA4
17:05:37: D3D9: Vertex texture format supported - PF_R8
17:05:37: D3D9: Vertex texture format supported - PF_RG8
17:05:37: RenderSystem capabilities
17:05:37: -------------------------
17:05:37: RenderSystem Name: Direct3D9 Rendering Subsystem
17:05:37: GPU Vendor: nvidia
17:05:37: Device Name: Monitor-1-NVIDIA GeForce GTX 460
17:05:37: Driver Version: 8.17.12.9610
17:05:37:  * Fixed function pipeline: yes
17:05:37:  * Hardware generation of mipmaps: yes
17:05:37:  * Texture blending: yes
17:05:37:  * Anisotropic texture filtering: yes
17:05:37:  * Dot product texture operation: yes
17:05:37:  * Cube mapping: yes
17:05:37:  * Hardware stencil buffer: yes
17:05:37:    - Stencil depth: 8
17:05:37:    - Two sided stencil support: yes
17:05:37:    - Wrap stencil values: yes
17:05:37:  * Hardware vertex / index buffers: yes
17:05:37:  * Vertex programs: yes
17:05:37:  * Number of floating-point constants for vertex programs: 256
17:05:37:  * Number of integer constants for vertex programs: 16
17:05:37:  * Number of boolean constants for vertex programs: 16
17:05:37:  * Fragment programs: yes
17:05:37:  * Number of floating-point constants for fragment programs: 224
17:05:37:  * Number of integer constants for fragment programs: 16
17:05:37:  * Number of boolean constants for fragment programs: 16
17:05:37:  * Geometry programs: no
17:05:37:  * Number of floating-point constants for geometry programs: 0
17:05:37:  * Number of integer constants for geometry programs: 0
17:05:37:  * Number of boolean constants for geometry programs: 0
17:05:37:  * Supported Shader Profiles: hlsl ps_1_1 ps_1_2 ps_1_3 ps_1_4 ps_2_0 ps_2_a ps_2_b ps_2_x ps_3_0 vs_1_1 vs_2_0 vs_2_a vs_2_x vs_3_0
17:05:37:  * Texture Compression: yes
17:05:37:    - DXT: yes
17:05:37:    - VTC: no
17:05:37:    - PVRTC: no
17:05:37:  * Scissor Rectangle: yes
17:05:37:  * Hardware Occlusion Query: yes
17:05:37:  * User clip planes: yes
17:05:37:  * VET_UBYTE4 vertex element type: yes
17:05:37:  * Infinite far plane projection: yes
17:05:37:  * Hardware render-to-texture: yes
17:05:37:  * Floating point textures: yes
17:05:37:  * Non-power-of-two textures: yes
17:05:37:  * Volume textures: yes
17:05:37:  * Multiple Render Targets: 4
17:05:37:    - With different bit depths: yes
17:05:37:  * Point Sprites: yes
17:05:37:  * Extended point parameters: yes
17:05:37:  * Max Point Size: 8192
17:05:37:  * Vertex texture fetch: yes
17:05:37:  * Number of world matrices: 0
17:05:37:  * Number of texture units: 8
17:05:37:  * Stencil buffer depth: 8
17:05:37:  * Number of vertex blend matrices: 0
17:05:37:    - Max vertex textures: 4
17:05:37:    - Vertex textures shared: no
17:05:37:  * Render to Vertex Buffer : no
17:05:37:  * DirectX per stage constants: yes
17:05:37: DefaultWorkQueue('Root') initialising on thread 03BE28D8.
17:05:37: DefaultWorkQueue('Root')::WorkerFunc - thread 03347ED8 starting.
17:05:37: DefaultWorkQueue('Root')::WorkerFunc - thread 03347F48 starting.
17:05:37: DefaultWorkQueue('Root')::WorkerFunc - thread 03348770 starting.
17:05:37: DefaultWorkQueue('Root')::WorkerFunc - thread 007A4858 starting.
17:05:37: Particle Renderer Type 'billboard' registered
17:05:37: Parsing scripts for resource group Autodetect
17:05:37: Finished parsing scripts for resource group Autodetect
17:05:37: Creating resources for group Autodetect
17:05:37: All done
17:05:37: Parsing scripts for resource group General
17:05:37: Parsing script TerrainShader.program
17:05:37: Parsing script Ogre.material
17:05:37: Parsing script Terrain.material
17:05:37: Finished parsing scripts for resource group General
17:05:37: Creating resources for group General
17:05:37: All done
17:05:37: Parsing scripts for resource group Internal
17:05:37: Finished parsing scripts for resource group Internal
17:05:37: Creating resources for group Internal
17:05:37: All done
17:05:37: Mesh: Loading ogrehead.mesh.
17:05:37: Texture: GreenSkin.jpg: Loading 1 faces(PF_R8G8B8,256x256x1) Internal format is PF_X8R8G8B8,256x256x1.
17:05:37: Texture: spheremap.png: Loading 1 faces(PF_R8G8B8,256x256x1) Internal format is PF_X8R8G8B8,256x256x1.
17:05:37: Texture: tusk.jpg: Loading 1 faces(PF_R8G8B8,128x128x1) Internal format is PF_X8R8G8B8,128x128x1.
17:05:37: Terrain created; size=513 minBatch=33 maxBatch=65 treeDepth=4 lodLevels=5 leafLods=2
17:05:38: *** Terrain Fragment Program: OgreTerrain/3163266815/sm2/fp/hlod ***
float4 expand(float4 v)
{ 
	return v * 2 - 1;
}


void main_fp(
float4 position : TEXCOORD0,
 out float4 oColor0 : COLOR0,
 out float4 oColor1 : COLOR1,
 out float4 oColor2 : COLOR2,
uniform float cFarDistance,
uniform float4x4 viewMatrix,
float4 uvMisc : TEXCOORD1,
float4 layerUV0 : TEXCOORD2, 
float4 layerUV1 : TEXCOORD3, 
uniform sampler2D globalNormal : register(s0)
, uniform sampler2D blendTex0 : register(s1)
, uniform sampler2D difftex0 : register(s2)
, uniform sampler2D normtex0 : register(s3)
, uniform sampler2D difftex1 : register(s4)
, uniform sampler2D normtex1 : register(s5)
, uniform sampler2D difftex2 : register(s6)
, uniform sampler2D normtex2 : register(s7)
) 
{
	float2 uv = uvMisc.xy;
	oColor0 = float4(0,0,0,0);
   oColor1 = oColor2 = float4(1,1,1,1);
	float3 normal = expand(tex2D(globalNormal, uv));
	float3 diffuse = float3(0,0,0);
	float specular = 0;
	float4 blendTexVal0 = tex2D(blendTex0, uv);
	float3 tangent = float3(1, 0, 0);
	normal = mul(viewMatrix, float4(normal,0)).xyz;
	tangent = mul(viewMatrix, float4(tangent,0)).xyz;
	float3 binormal = cross(normal, tangent);
	float3x3 TBN = float3x3(tangent, binormal, normal);
	float3 TSnormal;
	float2 uv0 = layerUV0.xy;
	TSnormal = expand(tex2D(normtex0, uv0)).rgb;
  oColor1.rgb = TSnormal;
	float4 diffuseSpecTex0 = tex2D(difftex0, uv0);
	diffuse = diffuseSpecTex0.rgb;
	specular = diffuseSpecTex0.a;
	float2 uv1 = layerUV0.zw;
	TSnormal = expand(tex2D(normtex1, uv1)).rgb;
  oColor1.rgb = lerp(oColor1.rgb, TSnormal, blendTexVal0.r);
	float4 diffuseSpecTex1 = tex2D(difftex1, uv1);
	diffuse = lerp(diffuse, diffuseSpecTex1.rgb, blendTexVal0.r);
	specular = lerp(specular, diffuseSpecTex1.a, blendTexVal0.r);
	float2 uv2 = layerUV1.xy;
	TSnormal = expand(tex2D(normtex2, uv2)).rgb;
  oColor1.rgb = lerp(oColor1.rgb, TSnormal, blendTexVal0.g);
	float4 diffuseSpecTex2 = tex2D(difftex2, uv2);
	diffuse = lerp(diffuse, diffuseSpecTex2.rgb, blendTexVal0.g);
	specular = lerp(specular, diffuseSpecTex2.a, blendTexVal0.g);
	oColor0.rgb += diffuse;
   oColor0.a += specular;
  oColor1 = float4(normalize(mul(TSnormal, TBN)), length(position) / cFarDistance);
}

***   ***
17:05:38: Texture: splatting1.png: Loading 1 faces(PF_R8G8B8,1024x1024x1) Internal format is PF_X8R8G8B8,1024x1024x1.
17:05:38: Texture: testnormalmap.png: Loading 1 faces(PF_R8G8B8,1024x1024x1) Internal format is PF_X8R8G8B8,1024x1024x1.
17:05:39: Texture: splatting2.png: Loading 1 faces(PF_R8G8B8,1024x1024x1) Internal format is PF_X8R8G8B8,1024x1024x1.
17:05:39: Texture: splatting3.png: Loading 1 faces(PF_R8G8B8,1024x1024x1) Internal format is PF_X8R8G8B8,1024x1024x1.
Attachments
terrain.jpg
terrain.jpg (142.29 KiB) Viewed 6006 times
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Re: Deferred Shading Terrain Material Generator

Post by Xplodwild » Thu Oct 04, 2012 5:47 pm

Do you have lights in your scene at least? How do you set your ambient light? No ambient light set = fully bright if I remember correctly.
0 x

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Fri Oct 05, 2012 4:32 pm

Have a look on the Ogre-Head. Its shaded -> light existing in scene. I set Ambient-Light to (0,0,0). Tested different Lightsources (Spot & Directional). The head is lit as expected but the terrain is always on full light. I m using DirectX as RenderSystem - in my App-Setup im not able to test OpenGL (cause of QT trouble..) - have u tested the shader on Dx?
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Sat Oct 06, 2012 12:55 pm

Really want to use and tweak your MaterialGenerator but i can't get it running ...

My setup for the terrain is based on the wiki tutorial. I may did anything wrong?

Code: Select all

	ko::koTerrain::instance()->setupTerrain("test", 513, Ogre::Vector3(0, 0, 0));
	ko::koTerrain::instance()->setDirectionalLight(lightd);
	ko::koTerrain::instance()->loadTerrain();
Defines:

Code: Select all

#define DEFAULT_WORLDSIZE_PER_TERRAINSIZE 10.0
#define DEFAULT_TERRAIN_FILENAME_EXTENSION ".terrain"
#define DEFAULT_MAX_PIXELERROR 8
#define DEFAULT_COMPOSITE_MAP_DISTANCE 3000
#define DEFAULT_IMPORT_IMAGE_SCALING 600
#define DEFAULT_IMPORT_MIN_BATCHSIZE 33
#define DEFAULT_IMPORT_MAX_BATCHSIZE 65
Implementation:

Code: Select all

#include "koTerrain.h"

using namespace ko;

koTerrain::koTerrain()
	: mWorldSizePerTerrainSize(DEFAULT_WORLDSIZE_PER_TERRAINSIZE),
	mTerrainFilenameExtension(DEFAULT_TERRAIN_FILENAME_EXTENSION),
	mTerrainGlobals(NULL),
	mTerrainGroup(NULL)
{

}

koTerrain::~koTerrain()
{
}

void koTerrain::setDirectionalLight(Ogre::Light* light)
{
	if(mTerrainGlobals)
	{
		mTerrainGlobals->setLightMapDirection(light->getDerivedDirection());
		mTerrainGlobals->setCompositeMapAmbient(koOgre::instance()->getScnMgr()->getAmbientLight());
		mTerrainGlobals->setCompositeMapDiffuse(light->getDiffuseColour());
	}
}

void koTerrain::setTerrainDefaults()
{
	if(mTerrainGroup)
	{
		// Configure default import settings for if we use imported image
		Ogre::Terrain::ImportData& defaultimp = mTerrainGroup->getDefaultImportSettings();
		defaultimp.terrainSize = mTerrainGroup->getTerrainSize();
		defaultimp.worldSize = mWorldSizePerTerrainSize * defaultimp.terrainSize;
		defaultimp.inputScale = DEFAULT_IMPORT_IMAGE_SCALING;
		defaultimp.minBatchSize = DEFAULT_IMPORT_MIN_BATCHSIZE;
		defaultimp.maxBatchSize = DEFAULT_IMPORT_MAX_BATCHSIZE;

		defaultimp.layerList.resize(3);
		defaultimp.layerList[0].worldSize = 100;
		defaultimp.layerList[0].textureNames.push_back("splatting1.png");
		defaultimp.layerList[0].textureNames.push_back("testnormalmap.png");
		defaultimp.layerList[1].worldSize = 30;
		defaultimp.layerList[1].textureNames.push_back("splatting2.png");
		defaultimp.layerList[1].textureNames.push_back("testnormalmap.png");
		defaultimp.layerList[2].worldSize = 200;
		defaultimp.layerList[2].textureNames.push_back("splatting3.png");
		defaultimp.layerList[2].textureNames.push_back("testnormalmap.png");
	}
}

void koTerrain::setupTerrain(Ogre::String terrainFilename, Ogre::uint16 terrainSize, Ogre::Vector3 origin)
{
	mTerrainFilename = terrainFilename;
    mTerrainGlobals = new Ogre::TerrainGlobalOptions();
	mTerrainGroup = new Ogre::TerrainGroup(koOgre::instance()->getScnMgr(), 
											Ogre::Terrain::ALIGN_X_Z, 
											terrainSize,
											mWorldSizePerTerrainSize * terrainSize);
    mTerrainGroup->setFilenameConvention(terrainFilename, mTerrainFilenameExtension);
    mTerrainGroup->setOrigin(origin);

    mTerrainGlobals->setMaxPixelError(DEFAULT_MAX_PIXELERROR);
    mTerrainGlobals->setCompositeMapDistance(DEFAULT_COMPOSITE_MAP_DISTANCE);

	// Setup custom material generator
	Ogre::koTerrainMaterialGenerator *terrainMaterialGenerator = new Ogre::koTerrainMaterialGenerator();
	mTerrainMaterialGenerator.bind( terrainMaterialGenerator );
	mTerrainGlobals->setDefaultMaterialGenerator(mTerrainMaterialGenerator);
	//mTerrainGlobals->setCastsDynamicShadows(true);
 
	setTerrainDefaults();

    mTerrainGroup->defineTerrain(0, 0, 1.0);
}

void koTerrain::loadTerrain()
{
    mTerrainGroup->loadAllTerrains(true);
    mTerrainGroup->freeTemporaryResources();
}
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Re: Deferred Shading Terrain Material Generator

Post by Xplodwild » Sat Oct 06, 2012 1:38 pm

You need to be running the deferred framework, and it works perfectly in Qt based applications (as my screen shots were taken from my Qt based editor running ogre direct x 9)
0 x

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Sat Oct 06, 2012 1:52 pm

yeah - thank you that wasnt clear to me... :? i ll try it again using the framework!
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

User avatar
vitefalcon
Orc
Posts: 438
Joined: Tue Sep 18, 2007 5:28 pm
Location: Seattle, USA

Re: Deferred Shading Terrain Material Generator

Post by vitefalcon » Sun Oct 07, 2012 12:57 pm

@Xplodwild: That's a cool piece for work. Congrats :)

Sorry if this is a dumb question. With deferred rendering isn't it hard to get alpha-blending correct? If so, have you solved this?
0 x
Image

Alexiss
Halfling
Posts: 74
Joined: Wed Aug 10, 2011 2:11 pm

Re: Deferred Shading Terrain Material Generator

Post by Alexiss » Sun Oct 07, 2012 1:38 pm

vitefalcon wrote:@Xplodwild: That's a cool piece for work. Congrats :)

Sorry if this is a dumb question. With deferred rendering isn't it hard to get alpha-blending correct? If so, have you solved this?
EDIT: Sorry, misunderstood the question
Last edited by Alexiss on Sun Oct 07, 2012 5:43 pm, edited 1 time in total.
0 x

User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Re: Deferred Shading Terrain Material Generator

Post by Xplodwild » Sun Oct 07, 2012 2:59 pm

vitefalcon wrote:@Xplodwild: That's a cool piece for work. Congrats :)

Sorry if this is a dumb question. With deferred rendering isn't it hard to get alpha-blending correct? If so, have you solved this?
Do you mean terrain splatting alpha blending? It's not really alpha blending as you're not rendering each piece in different batch. The material shader "paints" the terrain using lerps to do the alpha blending and transition, thus everything is still opaque and rendered during one pass by the shader using all the texture units needed.
0 x

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Sun Oct 07, 2012 3:08 pm

Now its working on the Deferred Shading System. But the lighting result isnt as expected. The following screenshot was taken with a single point light (and a small ambient light).

Code: Select all

	light = mSceneMgr->createLight();
	light->setAttenuation(3250, 1.0, 0.0014, 0.00007);
	light->setType(Ogre::Light::LT_POINT);
	light->setDiffuseColour(1, 1, 1);
	light->setSpecularColour(0.5, 0.5, 0.5);
	light->setPosition(0, 50, 0);
	node->attachObject(light);
its looking like a spotlight with an big open angle. and the strangest thing is a second lit area (as you can see on the screenshot) which disappears if the camera position changes!!! if i try to load other meshes they lit up as expected (so its something wrong with the materialgenerator thought)!
Attachments
wrong.jpg
wrong.jpg (39.24 KiB) Viewed 5777 times
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Mon Oct 08, 2012 10:06 am

[SOLVED] There was a problem with the splatting textures. Caused by a wrong LayerList Size (didnt match the numer of the textures + normaltextures). So the Generator is finally working as expected and i'm now something more familiar with the terrain system 8) .

Thank you for sharing your work!
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Wed Oct 24, 2012 1:55 am

Cause i want to use normal-textures aswell as simple diffuse ones i had to adapt the shader Xplodwild presented. If the normal maps are missing the original shader didnt compute correct normals.That results in a dark and unlighted terrain. My version of the shader is looking for an normal-mapping sampler inside the terrain layer and if there is no - it will take the vertex normal to compute the simple diffuse texture layer.

It was easy to integrate.

In "...SM2Profile::ShaderHelperCg::generateFpLayer() and ..SM2Profile::ShaderHelperCg::generateFpLayer()" i am now looking for the normal textures for each layer:

Code: Select all

		std::vector<bool> layerNormalMapping;
		layerNormalMapping.resize(terrain->getLayerCount());
		for(unsigned int i = 0; i < terrain->getLayerCount(); i++)
		{
			if(terrain->getLayerTextureName(i, 1).compare("") == 0)
				layerNormalMapping[i] = false;
			else
				layerNormalMapping[i] = true;
		}
and later on i m using those informations to compute the correct normals:

Code: Select all

		if(layerNormalMapping[layer])
		{
			outStream << "	TSnormal2 = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n";
			outStream << "	TSnormal2 = normalize(TSnormal2);\n";
		}
		else
		{
			outStream << " TSnormal2 = normalize(normal);\n";
		}

		if (layer)
			outStream << " TSnormal = lerp(TSnormal, TSnormal2, " << blendWeightStr << ");\n";  // THIS ONE MAY FIXES THE NORMAL-BLENDING-ISSUE
		else
			outStream << " TSnormal = TSnormal2;\n";
Coding this stuff i may found another bug in the shader. There is no TSnormal-blending at all. In the FPfooter the TSnormal of the last layer will be applied to the GBuffer-Normal-Texture. This is maybe not the expected result. I m not quite sure if i m right but i think the TSnormal has to be blended with each layer blending factor.

The original code did something like that:

Code: Select all

		outStream << "	TSnormal = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n";
		if (layer)
		{	
			outStream << "  oColor1.rgb = lerp(oColor1.rgb, TSnormal, " << blendWeightStr << ");\n";
		}
		else
		{
			outStream << "  oColor1.rgb = TSnormal;\n";
		}
The problem is assignment of the TSnormal. The if-statement after the assignment is unnecessary cause of the oColor1 assignment in the FPfooter:

Code: Select all

outStream << "  oColor1 = float4(normalize(mul(TSnormal, TBN)), length(position) / cFarDistance);\n" << "}\n";
So this way - the GBuffer-Normal-Texture get the last layer normal but not a right blended one. I fixed this using something like this:

Code: Select all

outStream << "	TSnormal2 = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n";
outStream << "	TSnormal2 = normalize(TSnormal2);\n";
outStream << " TSnormal = lerp(TSnormal, TSnormal2, " << blendWeightStr << ");\n";
My editor - using the shader - isn't able to change the alpha splatting so far so i could not test if the blending is working correct. But a single diffuse texture layer seems now to reflect all the light (directional light) more correct. Notice - my code is mostly untested.

Image

So my whole shader cpp is now looking like this. I added a few more lines of code beside thouse above. If there is a need i would integrate my fix into the original Shader (similar to this one - simply replace the classname).
If iam completly wrong - be patient im new to that stuff and i didnt know it better! :(

Code: Select all

#include "koTerrainMaterialGenerator.h"
#include "Terrain/OgreTerrain.h"
#include "OgreMaterialManager.h"
#include "OgreTechnique.h"
#include "OgrePass.h"
#include "OgreTextureUnitState.h"
#include "OgreGpuProgramManager.h"
#include "OgreHighLevelGpuProgramManager.h"
#include "OgreHardwarePixelBuffer.h"

#include "koTerrain.h"
 
namespace Ogre
{
	//---------------------------------------------------------------------
	koTerrainMaterialGenerator::koTerrainMaterialGenerator()
	{
		// define the layers
		// We expect terrain textures to have no alpha, so we use the alpha channel
		// in the albedo texture to store specular reflection
		// similarly we double-up the normal and height (for parallax)
		mLayerDecl.samplers.push_back(TerrainLayerSampler("albedo_specular", PF_BYTE_RGBA));
		mLayerDecl.samplers.push_back(TerrainLayerSampler("normal_height", PF_BYTE_RGBA));
 
		mLayerDecl.elements.push_back(
			TerrainLayerSamplerElement(0, TLSS_ALBEDO, 0, 3));
		mLayerDecl.elements.push_back(
			TerrainLayerSamplerElement(0, TLSS_SPECULAR, 3, 1));
		mLayerDecl.elements.push_back(
			TerrainLayerSamplerElement(1, TLSS_NORMAL, 0, 3));
		mLayerDecl.elements.push_back(
			TerrainLayerSamplerElement(1, TLSS_HEIGHT, 3, 1));
 
 
		mProfiles.push_back(OGRE_NEW SM2Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards"));
		// TODO - check hardware capabilities & use fallbacks if required (more profiles needed)
		setActiveProfile("SM2");
 
	}
	//---------------------------------------------------------------------
	koTerrainMaterialGenerator::~koTerrainMaterialGenerator()
	{
 
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	koTerrainMaterialGenerator::SM2Profile::SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc)
		: Profile(parent, name, desc)
		, mShaderGen(0)
		, mGlobalColourMapEnabled(false)
		, mLightmapEnabled(false)
		, mCompositeMapEnabled(false)
	{
 
	}
	//---------------------------------------------------------------------
	koTerrainMaterialGenerator::SM2Profile::~SM2Profile()
	{
		OGRE_DELETE mShaderGen;
	}	
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::requestOptions(Terrain* terrain)
	{
		terrain->_setMorphRequired(true);
		terrain->_setNormalMapRequired(true);
		terrain->_setLightMapRequired(mLightmapEnabled, true);
		terrain->_setCompositeMapRequired(mCompositeMapEnabled);
	}
	//---------------------------------------------------------------------
	bool koTerrainMaterialGenerator::SM2Profile::isVertexCompressionSupported() const
	{
		return true;
	}
	//---------------------------------------------------------------------
	void  koTerrainMaterialGenerator::SM2Profile::setGlobalColourMapEnabled(bool enabled)
	{
		if (enabled != mGlobalColourMapEnabled)
		{
			mGlobalColourMapEnabled = enabled;
			mParent->_markChanged();
		}
	}
	//---------------------------------------------------------------------
	void  koTerrainMaterialGenerator::SM2Profile::setLightmapEnabled(bool enabled)
	{
		if (enabled != mLightmapEnabled)
		{
			mLightmapEnabled = enabled;
			mParent->_markChanged();
		}
	}
	//---------------------------------------------------------------------
	void  koTerrainMaterialGenerator::SM2Profile::setCompositeMapEnabled(bool enabled)
	{
		if (enabled != mCompositeMapEnabled)
		{
			mCompositeMapEnabled = enabled;
			mParent->_markChanged();
		}
	}
	//---------------------------------------------------------------------
	uint8 koTerrainMaterialGenerator::SM2Profile::getMaxLayers(const Terrain* terrain) const
	{
		// count the texture units free
		uint8 freeTextureUnits = 16;
		// lightmap
		--freeTextureUnits;
		// normalmap
		--freeTextureUnits;
		// colourmap
		if (terrain->getGlobalColourMapEnabled())
			--freeTextureUnits;
 
		// each layer needs 2.25 units (1xdiffusespec, 1xnormalheight, 0.25xblend)
		return static_cast<uint8>(freeTextureUnits / 2.25f);
 
 
	}
	//---------------------------------------------------------------------
	MaterialPtr koTerrainMaterialGenerator::SM2Profile::generate(const Terrain* terrain)
	{
		// re-use old material if exists
		MaterialPtr mat = terrain->_getMaterial();
		if (mat.isNull())
		{
			MaterialManager& matMgr = MaterialManager::getSingleton();
 
			// it's important that the names are deterministic for a given terrain, so
			// use the terrain pointer as an ID
			const String& matName = terrain->getMaterialName();
			mat = matMgr.getByName(matName);
			if (mat.isNull())
			{
				mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
			}
		}
		// clear everything
		mat->removeAllTechniques();
 
		addTechnique(mat, terrain, HIGH_LOD);
		
		// LOD
		if(mCompositeMapEnabled)
		{
			addTechnique(mat, terrain, LOW_LOD);
			Material::LodValueList lodValues;
			lodValues.push_back(TerrainGlobalOptions::getSingleton().getCompositeMapDistance());
			mat->setLodLevels(lodValues);
			Technique* lowLodTechnique = mat->getTechnique(1);
			lowLodTechnique->setLodIndex(1);
		}
 
		updateParams(mat, terrain);
 
		return mat;
 
	}
	//---------------------------------------------------------------------
	MaterialPtr koTerrainMaterialGenerator::SM2Profile::generateForCompositeMap(const Terrain* terrain)
	{
		// re-use old material if exists
		MaterialPtr mat = terrain->_getCompositeMapMaterial();
		if (mat.isNull())
		{
			MaterialManager& matMgr = MaterialManager::getSingleton();
 
			// it's important that the names are deterministic for a given terrain, so
			// use the terrain pointer as an ID
			const String& matName = terrain->getMaterialName() + "/comp";
			mat = matMgr.getByName(matName);
			if (mat.isNull())
			{
				mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
			}
		}
		// clear everything
		mat->removeAllTechniques();
 
		addTechnique(mat, terrain, RENDER_COMPOSITE_MAP);
 
		updateParamsForCompositeMap(mat, terrain);
 
		return mat;
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::addTechnique(
		const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt)
	{
		Technique* tech = mat->createTechnique();
		tech->setSchemeName("GBuffer");
 
		// Only supporting one pass
		Pass* pass = tech->createPass();
		//pass->setName("NO_DEFERRED");
 
		GpuProgramManager& gmgr = GpuProgramManager::getSingleton();
		HighLevelGpuProgramManager& hmgr = HighLevelGpuProgramManager::getSingleton();
		if (!mShaderGen)
		{
			if (hmgr.isLanguageSupported("cg"))
				mShaderGen = OGRE_NEW ShaderHelperCg();
			else
			{
				// todo
			}
 
			// check SM3 features
			mSM3Available = GpuProgramManager::getSingleton().isSyntaxSupported("ps_3_0");
			mSM4Available = GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_0");
 
		}
		HighLevelGpuProgramPtr vprog = mShaderGen->generateVertexProgram(this, terrain, tt);
		HighLevelGpuProgramPtr fprog = mShaderGen->generateFragmentProgram(this, terrain, tt);
 
		pass->setVertexProgram(vprog->getName());
		pass->setFragmentProgram(fprog->getName());
 
		if (tt == HIGH_LOD || tt == RENDER_COMPOSITE_MAP)
		{
			// global normal map
			TextureUnitState* tu = pass->createTextureUnitState();
			tu->setTextureName(terrain->getTerrainNormalMap()->getName());
			tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
 
			// global colour map
			if (terrain->getGlobalColourMapEnabled() && isGlobalColourMapEnabled())
			{
				tu = pass->createTextureUnitState(terrain->getGlobalColourMap()->getName());
				tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
			}
 
			// light map
			if (isLightmapEnabled())
			{
				tu = pass->createTextureUnitState(terrain->getLightmap()->getName());
				tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
			}
 
			// blend maps
			uint maxLayers = getMaxLayers(terrain);
			uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount());
			uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
			for (uint i = 0; i < numBlendTextures; ++i)
			{
				tu = pass->createTextureUnitState(terrain->getBlendTextureName(i));
				tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
			}
 
			// layer textures
			for (uint i = 0; i < numLayers; ++i)
			{
				// diffuse / specular
				pass->createTextureUnitState(terrain->getLayerTextureName(i, 0));
 
				// normal / height
				pass->createTextureUnitState(terrain->getLayerTextureName(i, 1));
			}
 
		}
		else
		{
			// LOW_LOD textures
			// composite map
			TextureUnitState* tu = pass->createTextureUnitState();
			tu->setTextureName(terrain->getCompositeMap()->getName());
			tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
 
			// That's it!
 
		}
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::updateParams(const MaterialPtr& mat, const Terrain* terrain)
	{
		mShaderGen->updateParams(this, mat, terrain, false);
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain)
	{
		mShaderGen->updateParams(this, mat, terrain, true);
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	HighLevelGpuProgramPtr 
		koTerrainMaterialGenerator::SM2Profile::ShaderHelper::generateVertexProgram(
			const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
	{
		HighLevelGpuProgramPtr ret = createVertexProgram(prof, terrain, tt);
 
		StringUtil::StrStreamType sourceStr;
		generateVertexProgramSource(prof, terrain, tt, sourceStr);
		ret->setSource(sourceStr.str());
		ret->load();
		defaultVpParams(prof, terrain, tt, ret);
#if OGRE_DEBUG_MODE
		LogManager::getSingleton().stream(LML_CRITICAL) << "*** Terrain Vertex Program: " 
			<< ret->getName() << " ***\n" << ret->getSource() << "\n***   ***";
#endif
 
		return ret;
 
	}
	//---------------------------------------------------------------------
	HighLevelGpuProgramPtr 
	koTerrainMaterialGenerator::SM2Profile::ShaderHelper::generateFragmentProgram(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
	{
		HighLevelGpuProgramPtr ret = createFragmentProgram(prof, terrain, tt);
 
		StringUtil::StrStreamType sourceStr;
		generateFragmentProgramSource(prof, terrain, tt, sourceStr);
		ret->setSource(sourceStr.str());
		ret->load();
		defaultFpParams(prof, terrain, tt, ret);
 
#if 1
		LogManager::getSingleton().stream(LML_CRITICAL) << "*** Terrain Fragment Program: " 
			<< ret->getName() << " ***\n" << ret->getSource() << "\n***   ***";
#endif
 
		return ret;
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::generateVertexProgramSource(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
	{
		generateVpHeader(prof, terrain, tt, outStream);
 
		if (tt != LOW_LOD)
		{
			uint maxLayers = prof->getMaxLayers(terrain);
			uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
 
			for (uint i = 0; i < numLayers; ++i)
				generateVpLayer(prof, terrain, tt, i, outStream);
		}
 
		generateVpFooter(prof, terrain, tt, outStream);
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::generateFragmentProgramSource(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
	{
		generateFpHeader(prof, terrain, tt, outStream);
 
		if (tt != LOW_LOD)
		{
			uint maxLayers = prof->getMaxLayers(terrain);
			uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
 
			for (uint i = 0; i < numLayers; ++i)
				generateFpLayer(prof, terrain, tt, i, outStream);
		}
 
		generateFpFooter(prof, terrain, tt, outStream);
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::defaultVpParams(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog)
	{
		GpuProgramParametersSharedPtr params = prog->getDefaultParameters();
		params->setIgnoreMissingParams(true);
		params->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX);
		params->setNamedAutoConstant("viewMatrix", GpuProgramParameters::ACT_WORLDVIEW_MATRIX);
		params->setNamedAutoConstant("viewProjMatrix", GpuProgramParameters::ACT_VIEWPROJ_MATRIX);
		params->setNamedAutoConstant("lodMorph", GpuProgramParameters::ACT_CUSTOM, 
			Terrain::LOD_MORPH_CUSTOM_PARAM);
		params->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS);
 
		if (terrain->_getUseVertexCompression() && tt != RENDER_COMPOSITE_MAP)
		{
			Matrix4 posIndexToObjectSpace;
			terrain->getPointTransform(&posIndexToObjectSpace);
			params->setNamedConstant("posIndexToObjectSpace", posIndexToObjectSpace);
		}
 
 
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::defaultFpParams(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog)
	{
		GpuProgramParametersSharedPtr params = prog->getDefaultParameters();
		params->setIgnoreMissingParams(true);
		params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR);
		params->setNamedAutoConstant("cFarDistance", Ogre::GpuProgramParameters::ACT_FAR_CLIP_DISTANCE);
		params->setNamedAutoConstant("viewMatrix", GpuProgramParameters::ACT_WORLDVIEW_MATRIX); // tout sauf Z : VIEW_MATRIX
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::updateParams(
		const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap)
	{
		Pass* p = mat->getTechnique(0)->getPass(0);
		if (compositeMap)
		{
			updateVpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getVertexProgramParameters());
			updateFpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getFragmentProgramParameters());
		}
		else
		{
			// high lod
			updateVpParams(prof, terrain, HIGH_LOD, p->getVertexProgramParameters());
			updateFpParams(prof, terrain, HIGH_LOD, p->getFragmentProgramParameters());
 
			if(prof->isCompositeMapEnabled())
			{
				// low lod
				p = mat->getTechnique(1)->getPass(0);
				updateVpParams(prof, terrain, LOW_LOD, p->getVertexProgramParameters());
				updateFpParams(prof, terrain, LOW_LOD, p->getFragmentProgramParameters());
			}
		}
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::updateVpParams(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params)
	{
		params->setIgnoreMissingParams(true);
		uint maxLayers = prof->getMaxLayers(terrain);
		uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
		uint numUVMul = numLayers / 4;
		if (numLayers % 4)
			++numUVMul;
		for (uint i = 0; i < numUVMul; ++i)
		{
			Vector4 uvMul(
				terrain->getLayerUVMultiplier(i * 4), 
				terrain->getLayerUVMultiplier(i * 4 + 1), 
				terrain->getLayerUVMultiplier(i * 4 + 2), 
				terrain->getLayerUVMultiplier(i * 4 + 3) 
				);
			params->setNamedConstant("uvMul_" + StringConverter::toString(i), uvMul);
		}
 
		if (terrain->_getUseVertexCompression() && tt != RENDER_COMPOSITE_MAP)
		{
			Real baseUVScale = 1.0f / (terrain->getSize() - 1);
			params->setNamedConstant("baseUVScale", baseUVScale);
		}
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelper::updateFpParams(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params)
	{
		params->setIgnoreMissingParams(true);
		// TODO - parameterise this?
		/*Vector4 scaleBiasSpecular(0.03, -0.04, 32, 1);
		params->setNamedConstant("scaleBiasSpecular", scaleBiasSpecular);*/
 
	}
	//---------------------------------------------------------------------
	String koTerrainMaterialGenerator::SM2Profile::ShaderHelper::getChannel(uint idx)
	{
		uint rem = idx % 4;
		switch(rem)
		{
		case 0:
		default:
			return "r";
		case 1:
			return "g";
		case 2:
			return "b";
		case 3:
			return "a";
		};
	}
	//---------------------------------------------------------------------
	String koTerrainMaterialGenerator::SM2Profile::ShaderHelper::getVertexProgramName(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
	{
		String progName = terrain->getMaterialName() + "/sm2/vp";
 
		switch(tt)
		{
		case HIGH_LOD:
			progName += "/hlod";
			break;
		case LOW_LOD:
			progName += "/llod";
			break;
		case RENDER_COMPOSITE_MAP:
			progName += "/comp";
			break;
		}
 
		return progName;
 
	}
	//---------------------------------------------------------------------
	String koTerrainMaterialGenerator::SM2Profile::ShaderHelper::getFragmentProgramName(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
	{
 
		String progName = terrain->getMaterialName() + "/sm2/fp";
 
		switch(tt)
		{
		case HIGH_LOD:
			progName += "/hlod";
			break;
		case LOW_LOD:
			progName += "/llod";
			break;
		case RENDER_COMPOSITE_MAP:
			progName += "/comp";
			break;
		}
 
		return progName;
	}
	//---------------------------------------------------------------------
	//---------------------------------------------------------------------
	HighLevelGpuProgramPtr
	koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::createVertexProgram(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
	{
		HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
		String progName = getVertexProgramName(prof, terrain, tt);
		HighLevelGpuProgramPtr ret = mgr.getByName(progName);
		if (ret.isNull())
		{
			ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
				"cg", GPT_VERTEX_PROGRAM);
		}
		else
		{
			ret->unload();
		}
 
		ret->setParameter("profiles", "vs_4_0 vs_3_0 vs_2_0 arbvp1");
		ret->setParameter("entry_point", "main_vp");
 
		return ret;
 
	}
	//---------------------------------------------------------------------
	HighLevelGpuProgramPtr
		koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::createFragmentProgram(
			const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
	{
		HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
		String progName = getFragmentProgramName(prof, terrain, tt);
 
		HighLevelGpuProgramPtr ret = mgr.getByName(progName);
		if (ret.isNull())
		{
			ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
				"cg", GPT_FRAGMENT_PROGRAM);
		}
		else
		{
			ret->unload();
		}
 
		ret->setParameter("profiles", "ps_4_0 ps_3_0 ps_2_x fp40 arbfp1");
		ret->setParameter("entry_point", "main_fp");
 
		return ret;
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::generateVpHeader(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
	{
		outStream << 
			"void main_vp(\n";
		bool compression = terrain->_getUseVertexCompression() && tt != RENDER_COMPOSITE_MAP;
		if (compression)
		{
			outStream << 
				"float2 posIndex : POSITION,\n"
				"float height  : TEXCOORD0,\n";
		}
		else
		{
			outStream <<
				"float4 pos : POSITION,\n"
				"float2 uv  : TEXCOORD0,\n";
 
		}
		if (tt != RENDER_COMPOSITE_MAP)
			outStream << "float2 delta  : TEXCOORD1,\n"; // lodDelta, lodThreshold
 
		outStream << 
			"uniform float4x4 worldMatrix,\n"
			"uniform float4x4 viewMatrix,\n"
			"uniform float4x4 viewProjMatrix,\n"
			"uniform float2   lodMorph,\n"; // morph amount, morph LOD target
 
		if (compression)
		{
			outStream << 
				"uniform float4x4   posIndexToObjectSpace,\n"
				"uniform float    baseUVScale,\n";
		}
		// uv multipliers
		uint maxLayers = prof->getMaxLayers(terrain);
		uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
		uint numUVMultipliers = (numLayers / 4);
		if (numLayers % 4)
			++numUVMultipliers;
		for (uint i = 0; i < numUVMultipliers; ++i)
			outStream << "uniform float4 uvMul_" << i << ", \n";
 
		outStream <<
			"out float4 oPos : POSITION,\n"
			"out float4 oPosObj : TEXCOORD0\n";
 
		uint texCoordSet = 1;
		outStream <<
			", out float4 oUVMisc : TEXCOORD" << texCoordSet++ <<" // xy = uv, z = camDepth\n";
 
		// layer UV's premultiplied, packed as xy/zw
		uint numUVSets = numLayers / 2;
		if (numLayers % 2)
			++numUVSets;
		if (tt != LOW_LOD)
		{
			for (uint i = 0; i < numUVSets; ++i)
			{
				outStream <<
					", out float4 oUV" << i << " : TEXCOORD" << texCoordSet++ << "\n";
			}
		}
 
		if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP)
		{
			outStream << ", out float2 lodInfo : TEXCOORD" << texCoordSet++ << "\n";
		}
 
		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
		if (fog)
		{
			outStream <<
				", uniform float4 fogParams\n"
				", out float fogVal : COLOR\n";
		}
 
		// check we haven't exceeded texture coordinates
		if (texCoordSet > 8)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
				"Requested options require too many texture coordinate sets! Try reducing the number of layers.",
				__FUNCTION__);
		}
 
		outStream <<
			")\n"
			"{\n";
		if (compression)
		{
			outStream <<
				"	float4 pos;\n"
				"	pos = mul(posIndexToObjectSpace, float4(posIndex, height, 1));\n"
				"   float2 uv = float2(posIndex.x * baseUVScale, 1.0 - (posIndex.y * baseUVScale));\n";
		}
		outStream <<
			"	float4 worldPos = mul(worldMatrix, pos);\n"
			"	oPosObj = mul(viewMatrix, pos);\n";
 
		if (tt != RENDER_COMPOSITE_MAP)
		{
			// determine whether to apply the LOD morph to this vertex
			// we store the deltas against all vertices so we only want to apply 
			// the morph to the ones which would disappear. The target LOD which is
			// being morphed to is stored in lodMorph.y, and the LOD at which 
			// the vertex should be morphed is stored in uv.w. If we subtract
			// the former from the latter, and arrange to only morph if the
			// result is negative (it will only be -1 in fact, since after that
			// the vertex will never be indexed), we will achieve our aim.
			// sign(vertexLOD - targetLOD) == -1 is to morph
			outStream << 
				"	float toMorph = -min(0, sign(delta.y - lodMorph.y));\n";
			// this will either be 1 (morph) or 0 (don't morph)
			if (prof->getParent()->getDebugLevel())
			{
				// x == LOD level (-1 since value is target level, we want to display actual)
				outStream << "lodInfo.x = (lodMorph.y - 1) / " << terrain->getNumLodLevels() << ";\n";
				// y == LOD morph
				outStream << "lodInfo.y = toMorph * lodMorph.x;\n";
			}
 
			// morph
			switch (terrain->getAlignment())
			{
			case Terrain::ALIGN_X_Y:
				outStream << "	worldPos.z += delta.x * toMorph * lodMorph.x;\n";
				break;
			case Terrain::ALIGN_X_Z:
				outStream << "	worldPos.y += delta.x * toMorph * lodMorph.x;\n";
				break;
			case Terrain::ALIGN_Y_Z:
				outStream << "	worldPos.x += delta.x * toMorph * lodMorph.x;\n";
				break;
			};
		}
 
 
		// generate UVs
		if (tt != LOW_LOD)
		{
			for (uint i = 0; i < numUVSets; ++i)
			{
				uint layer  =  i * 2;
				uint uvMulIdx = layer / 4;
 
				outStream <<
					"	oUV" << i << ".xy = " << " uv.xy * uvMul_" << uvMulIdx << "." << getChannel(layer) << ";\n";
				outStream <<
					"	oUV" << i << ".zw = " << " uv.xy * uvMul_" << uvMulIdx << "." << getChannel(layer+1) << ";\n";
 
			}
 
		}	
 
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::generateFpHeader(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
	{

		// Main header
		outStream << 
			// helpers
			"float4 expand(float4 v)\n"
			"{ \n"
			"	return v * 2 - 1;\n"
			"}\n\n\n";
 
		outStream << 
			"void main_fp(\n"
			"float4 position : TEXCOORD0,\n"
			" out float4 oColor0 : COLOR0,\n"
			" out float4 oColor1 : COLOR1,\n"
			//" out float4 oColor2 : COLOR2,\n"
			"uniform float cFarDistance,\n"
			"uniform float4x4 viewMatrix,\n";
 
 
		uint texCoordSet = 1;
		outStream <<
			"float4 uvMisc : TEXCOORD" << texCoordSet++ << ",\n";

		std::vector<bool> layerNormalMapping;
		layerNormalMapping.resize(terrain->getLayerCount());
		for(unsigned int i = 0; i < terrain->getLayerCount(); i++)
		{
			if(terrain->getLayerTextureName(i, 1).compare("") == 0)
				layerNormalMapping[i] = false;
			else
				layerNormalMapping[i] = true;
		}
 
		// UV's premultiplied, packed as xy/zw
		uint maxLayers = prof->getMaxLayers(terrain);
		uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount());
		uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
		uint numUVSets = numLayers / 2;
		if (numLayers % 2)
			++numUVSets;
		if (tt != LOW_LOD)
		{
			for (uint i = 0; i < numUVSets; ++i)
			{
				outStream <<
					"float4 layerUV" << i << " : TEXCOORD" << texCoordSet++ << ", \n";
			}
 
		}
		if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP)
		{
			outStream << "float2 lodInfo : TEXCOORD" << texCoordSet++ << ", \n";
		}
 
		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
		if (fog)
		{
			outStream <<
				"uniform float3 fogColour, \n"
				"float fogVal : COLOR,\n";
		}
 
		uint currentSamplerIdx = 0;
 
		if (tt == LOW_LOD)
		{
			// single composite map covers all the others below
			outStream << 
				"uniform sampler2D compositeMap : register(s" << currentSamplerIdx++ << ")\n";
		}
		else
		{
			outStream << 
				"uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n";
 
 
			if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled())
			{
				outStream << ", uniform sampler2D globalColourMap : register(s" 
					<< currentSamplerIdx++ << ")\n";
			}
 
			// Blend textures - sampler definitions
			for (uint i = 0; i < numBlendTextures; ++i)
			{
				outStream << ", uniform sampler2D blendTex" << i 
					<< " : register(s" << currentSamplerIdx++ << ")\n";
			}
 
			// Layer textures - sampler definitions & UV multipliers
			for (uint i = 0; i < numLayers; ++i)
			{
				outStream << ", uniform sampler2D difftex" << i 
					<< " : register(s" << currentSamplerIdx++ << ")\n";
				if(layerNormalMapping[i])
					outStream << ", uniform sampler2D normtex" << i 
						<< " : register(s" << currentSamplerIdx++ << ")\n";
			}
		}
 
		// check we haven't exceeded samplers
		if (currentSamplerIdx > 16)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
				"Requested options require too many texture samplers! Try reducing the number of layers.",
				__FUNCTION__);
		}
 
		outStream << 
			") \n"
			"{\n"
			"	float2 uv = uvMisc.xy;\n"
			// base colour
			"	oColor0 = float4(0,0,0,0);\n"
			"   oColor1 = float4(1,1,1,1);\n";
 
		if (tt != LOW_LOD)
		{
			outStream << 
				"	float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n";
		}
 
 
 
			// set up accumulation areas
		outStream << "	float3 diffuse = float3(0,0,0);\n"
			"	float specular = 0;\n";
 
 
		if (tt == LOW_LOD)
		{
			// we just do a single calculation from composite map
			outStream <<
				"	float4 composite = tex2D(compositeMap, uv);\n"
				"	diffuse = composite.rgb;\n";
			// TODO - specular; we'll need normals for this!
		}
		else
		{
			// set up the blend values
			for (uint i = 0; i < numBlendTextures; ++i)
			{
				outStream << "	float4 blendTexVal" << i << " = tex2D(blendTex" << i << ", uv);\n";
			}
 
			// derive the tangent space basis
			// we do this in the pixel shader because we don't have per-vertex normals
			// because of the LOD, we use a normal map
			// tangent is always +x or -z in object space depending on alignment
			switch(terrain->getAlignment())
			{
			case Terrain::ALIGN_X_Y:
			case Terrain::ALIGN_X_Z:
				outStream << "	float3 tangent = float3(1, 0, 0);\n";
				break;
			case Terrain::ALIGN_Y_Z:
				outStream << "	float3 tangent = float3(0, 0, -1);\n";
				break;
			};
 
			// We must do normal calculations here instead of the vertex program because terrain normals
			// are provided by the "normal" texture, and not in vertex info. We multiply it by the viewMatrix
			// so the normals are in eye-space instead of object-space, as required by deferred shading.
			outStream << "	normal = mul(viewMatrix, float4(normal,0)).xyz;" << std::endl;
			outStream << "	tangent = mul(viewMatrix, float4(tangent,0)).xyz;" << std::endl;
			outStream << "	float3 binormal = cross(normal, tangent);" << std::endl;
 
			// derive final matrix
			outStream << "	float3x3 TBN = float3x3(tangent, binormal, normal);\n";
 
			// set up lighting result placeholders for interpolation
			outStream << "	float3 TSnormal = normal;\n";
			outStream << "	float3 TSnormal2;\n";
		}
 
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::generateVpLayer(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream)
	{
		// nothing to do
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::generateFpLayer(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream)
	{
		std::vector<bool> layerNormalMapping;
		layerNormalMapping.resize(terrain->getLayerCount());
		for(unsigned int i = 0; i < terrain->getLayerCount(); i++)
		{
			if(terrain->getLayerTextureName(i, 1).compare("") == 0)
				layerNormalMapping[i] = false;
			else
				layerNormalMapping[i] = true;
		}

		uint uvIdx = layer / 2;
		String uvChannels = (layer % 2) ? ".zw" : ".xy";
		uint blendIdx = (layer-1) / 4;
		String blendChannel = getChannel(layer-1);
		String blendWeightStr = String("blendTexVal") + StringConverter::toString(blendIdx) + 
			"." + blendChannel;
 
		// generate early-out conditional
		/* Disable - causing some issues even when trying to force the use of texldd
		if (layer && prof->_isSM3Available())
			outStream << "  if (" << blendWeightStr << " > 0.0003)\n  { \n";
		*/
 
		// generate UV
		outStream << "	float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n";
 
		// access TS normal map
		if(layerNormalMapping[layer])
		{
			outStream << "	TSnormal2 = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n";
			outStream << "	TSnormal2 = normalize(TSnormal2);\n";
		}
		else
		{
			outStream << " TSnormal2 = normalize(normal);\n";
		}

		if (layer)
			outStream << " TSnormal = lerp(TSnormal, TSnormal2, " << blendWeightStr << ");\n";
		else
			outStream << " TSnormal = TSnormal2;\n";

		if (layer)
		{	
			outStream << "  oColor1.rgb = lerp(oColor1.rgb, TSnormal2, " << blendWeightStr << ");\n";

		}
		else
		{
			outStream << "  oColor1.rgb = TSnormal2;\n";
		}
 
		// sample diffuse texture
		outStream << "	float4 diffuseSpecTex" << layer 
			<< " = tex2D(difftex" << layer << ", uv" << layer << ");\n";
 
		// apply to common
		if (!layer)
		{
			outStream << "	diffuse = diffuseSpecTex0.rgb;\n";
			outStream << "	specular = diffuseSpecTex0.a;\n";
		}
		else
		{
			outStream << "	diffuse = lerp(diffuse, diffuseSpecTex" << layer 
				<< ".rgb, " << blendWeightStr << ");\n";
			outStream << "	specular = lerp(specular, diffuseSpecTex" << layer 
				<< ".a, " << blendWeightStr << ");\n";
 
		}
 
		// End early-out
		/* Disable - causing some issues even when trying to force the use of texldd
		if (layer && prof->_isSM3Available())
			outStream << "  } // early-out blend value\n";
		*/
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::generateVpFooter(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
	{
 
		outStream << 
			"	oPos = mul(viewProjMatrix, worldPos);\n" 
			"	oUVMisc.xy = uv.xy;\n";
 
		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
		if (fog)
		{
			if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR)
			{
				outStream <<
					"	fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n";
			}
			else
			{
				outStream <<
					"	fogVal = 1 - saturate(1 / (exp(oPos.z * fogParams.x)));\n";
			}
		}
 
 
		outStream << 
			"}\n";
 
 
	}
	//---------------------------------------------------------------------
	void koTerrainMaterialGenerator::SM2Profile::ShaderHelperCg::generateFpFooter(
		const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream)
	{
 		std::vector<bool> layerNormalMapping;
		layerNormalMapping.resize(terrain->getLayerCount());
		for(unsigned int i = 0; i < terrain->getLayerCount(); i++)
		{
			if(terrain->getLayerTextureName(i, 1).compare("") == 0)
				layerNormalMapping[i] = false;
			else
				layerNormalMapping[i] = true;
		}

		if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled())
		{
			// sample colour map and apply to diffuse
			outStream << "	diffuse *= tex2D(globalColourMap, uv).rgb;\n";
		}
 
		// diffuse lighting
		outStream << "	oColor0.rgb += diffuse;\n"
				"   oColor0.a += specular;\n";
 
		bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
		if (fog)
		{
			outStream << "	oColor0.rgb = lerp(oColor0.rgb, fogColour, fogVal);\n";
		}
 
		// Final return
		outStream << "  oColor1 = float4(normalize(mul(TSnormal, TBN)), length(position) / cFarDistance);\n" << "}\n";
 
	}
	//---------------------------------------------------------------------
}


0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Fri Oct 26, 2012 12:34 am

I finally tested the fixed blending and it seems to working very well. Now here comes a screenshot showing a simple diffuse/specular texture (gras) beside a normal mapping one (rocks). Actually the layersize is restricted to 5 (but i think its still cool to use diffuse/specular textures beside normal textures too).

Image
(using the shaderfix provided above - and fixed my fix :) - i had to comment out something in line 840, now its looking like that "if(true/*layerNormalMapping*/)")

I would like to combine these different kinds of mappings to maximize the layer limit of 5 to a limit which depend of the dynamic combination. Lets say your level designer needs only 3 normal mapping textures - in this case he can use totally up to 7 layers (3 normal mapping, 4 diffuse/specular) - without rewrite something. Technically thats not a problem but the implementation of the terrain system doesnt alllow different sampler declaration of each layer:

The definition of the information each layer will contain in this terrain.
All layers must contain the same structure of information, although the input textures can be different per layer instance.


I may use a litte "hack" to use a layer declaration of one diffuse and one normal-texture but internally - in the shader - reuse the "normal" texture for the next layer (if this one isnt a normal texture). the problem is maybe the texture filtering (could be different from diffuse and normal texture). i do not know - but i'll try!
Last edited by technique on Fri Oct 26, 2012 12:45 pm, edited 1 time in total.
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Re: Deferred Shading Terrain Material Generator

Post by Xplodwild » Fri Oct 26, 2012 11:25 am

For your first fix, I'm not sure normal blending should be applied twice, as TBN already takes into account the terrain "world normal map".

However, there is an issue I fixed in my local copy but forgot to push to wiki, the final return line of generateFpFooter should read:

Code: Select all

outStream << "  oColor1 = float4(normalize(mul(oColor1.rgb, TBN)), length(position) / cFarDistance);\n"
Instead of mul(TSnormal, TBN), as it would only take the last normal map sampler. I'm updating the wiki to reflect this fix.

For your TSnormal2, I'm not sure it's right, for me you'd be applying twice the world normal map.
0 x

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Fri Oct 26, 2012 1:13 pm

Xplodwild wrote: For your TSnormal2, I'm not sure it's right, for me you'd be applying twice the world normal map.
Its because of the Simple Texture Layer (without normal-mapping). My problem was the assignment/blending of the TSnormal for none normal mapping layer. Basically to get the terrain normal out of the tbn its just a multiplication like this (0,0,1)^T * TBN. So for each Simple Texture Layer i should blend with the vector v= (0,0,1)? may this be the right way?
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

User avatar
Xplodwild
Goblin
Posts: 231
Joined: Thu Feb 12, 2009 3:49 pm
Location: France
Contact:

Re: Deferred Shading Terrain Material Generator

Post by Xplodwild » Fri Oct 26, 2012 6:53 pm

No normal mapping layer should point Z, yes.
But there's no point having a deferred shading if you don't use normalmaps :p
0 x

technique
Halfling
Posts: 91
Joined: Fri Oct 22, 2010 10:46 pm

Re: Deferred Shading Terrain Material Generator

Post by technique » Sun Oct 28, 2012 6:15 pm

Meh...for terrain - it may be usefull to pass on normal mapping for a few layer to get a higher layer maximum (as i wrote before). Especially for textures which dont need an extra "bumpy-look".
For DF - I really want to have many lights affecting the terrain so for me this is the most important advantage in deferred shading.
0 x
Image
Kingdoms Defender offers Tower Defense action with breathtaking 3-D graphics for your mobile Android device.

Give it a try:
Free-Version:
http://play.google.com/store/apps/detai ... ender_free

Full-Version:
http://play.google.com/store/apps/detai ... msdefender

Post Reply