1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2014 Torus Knot Software Ltd
8 
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15 
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 THE SOFTWARE.
26 -----------------------------------------------------------------------------
27 */
28 #include "OgreTerrainMaterialShaderHelpers.h"
29 #include "OgreTerrain.h"
30 #include "OgreHighLevelGpuProgramManager.h"
31 #include "OgreShadowCameraSetupPSSM.h"
32 #include "OgreHighLevelGpuProgram.h"
33 
34 namespace Ogre
35 {
36     //---------------------------------------------------------------------
37     //---------------------------------------------------------------------
38     HighLevelGpuProgramPtr
createVertexProgram(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt)39     ShaderHelperCg::createVertexProgram(
40         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
41     {
42         HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
43         String progName = getVertexProgramName(prof, terrain, tt);
44 
45         String lang = mgr.isLanguageSupported("hlsl") ? "hlsl" : "cg";
46 
47         HighLevelGpuProgramPtr ret = mgr.getByName(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
48         if (!ret)
49         {
50             ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
51                                     lang, GPT_VERTEX_PROGRAM);
52         }
53         else
54         {
55             ret->unload();
56         }
57 
58         if(lang == "hlsl")
59         {
60             ret->setParameter("enable_backwards_compatibility", "true");
61             ret->setParameter("target", "vs_4_0 vs_3_0 vs_2_0");
62         }
63         else
64         {
65             ret->setParameter("profiles", "vs_4_0 vs_3_0 vs_2_0 arbvp1");
66         }
67 
68         ret->setParameter("entry_point", "main_vp");
69 
70         return ret;
71 
72     }
73     //---------------------------------------------------------------------
74     HighLevelGpuProgramPtr
createFragmentProgram(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt)75         ShaderHelperCg::createFragmentProgram(
76             const SM2Profile* prof, const Terrain* terrain, TechniqueType tt)
77     {
78         HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton();
79         String progName = getFragmentProgramName(prof, terrain, tt);
80 
81         String lang = mgr.isLanguageSupported("hlsl") ? "hlsl" : "cg";
82 
83         HighLevelGpuProgramPtr ret = mgr.getByName(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
84         if (!ret)
85         {
86             ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
87                                     lang, GPT_FRAGMENT_PROGRAM);
88         }
89         else
90         {
91             ret->unload();
92         }
93 
94 
95         if(lang == "hlsl")
96         {
97             ret->setParameter("enable_backwards_compatibility", "true");
98             ret->setParameter("target", "ps_4_0 ps_3_0 ps_2_x");
99         }
100         else
101         {
102             if(prof->isLayerNormalMappingEnabled() || prof->isLayerParallaxMappingEnabled())
103                 ret->setParameter("profiles", "ps_4_0 ps_3_0 ps_2_x fp40 arbfp1");
104             else
105                 ret->setParameter("profiles", "ps_4_0 ps_3_0 ps_2_0 fp30 arbfp1");
106         }
107         ret->setParameter("entry_point", "main_fp");
108 
109         return ret;
110 
111     }
112     //---------------------------------------------------------------------
generateVpHeader(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)113     void ShaderHelperCg::generateVpHeader(
114         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
115     {
116         outStream <<
117             "void main_vp(\n";
118         bool compression = terrain->_getUseVertexCompression() && tt != RENDER_COMPOSITE_MAP;
119         if (compression)
120         {
121             const char* idx2 = prof->_isSM4Available() ? "int2" : "float2";
122             outStream <<
123                 idx2 << " posIndex : POSITION,\n"
124                 "float height  : TEXCOORD0,\n";
125         }
126         else
127         {
128             outStream <<
129                 "float4 pos : POSITION,\n"
130                 "float2 uv  : TEXCOORD0,\n";
131 
132         }
133         if (tt != RENDER_COMPOSITE_MAP)
134             outStream << "float2 delta  : TEXCOORD1,\n"; // lodDelta, lodThreshold
135 
136         outStream <<
137             "uniform float4x4 worldMatrix,\n"
138             "uniform float4x4 viewProjMatrix,\n"
139             "uniform float2   lodMorph,\n"; // morph amount, morph LOD target
140 
141         if (compression)
142         {
143             outStream <<
144                 "uniform float4x4   posIndexToObjectSpace,\n"
145                 "uniform float    baseUVScale,\n";
146         }
147         // uv multipliers
148         uint maxLayers = prof->getMaxLayers(terrain);
149         uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
150         uint numUVMultipliers = (numLayers / 4);
151         if (numLayers % 4)
152             ++numUVMultipliers;
153         for (uint i = 0; i < numUVMultipliers; ++i)
154             outStream << "uniform float4 uvMul_" << i << ", \n";
155 
156         outStream <<
157             "out float4 oPos : POSITION,\n"
158             "out float4 oPosObj : TEXCOORD0 \n";
159 
160         uint texCoordSet = 1;
161         outStream <<
162             ", out float4 oUVMisc : TEXCOORD" << texCoordSet++ <<" // xy = uv, z = camDepth\n";
163 
164         // layer UV's premultiplied, packed as xy/zw
165         uint numUVSets = numLayers / 2;
166         if (numLayers % 2)
167             ++numUVSets;
168         if (tt != LOW_LOD)
169         {
170             for (uint i = 0; i < numUVSets; ++i)
171             {
172                 outStream <<
173                     ", out float4 oUV" << i << " : TEXCOORD" << texCoordSet++ << "\n";
174             }
175         }
176 
177         if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP)
178         {
179             outStream << ", out float2 lodInfo : TEXCOORD" << texCoordSet++ << "\n";
180         }
181 
182         bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
183         if (fog)
184         {
185             outStream <<
186                 ", uniform float4 fogParams\n"
187                 ", out float fogVal : COLOR\n";
188         }
189 
190         if (prof->isShadowingEnabled(tt, terrain))
191         {
192             texCoordSet = generateVpDynamicShadowsParams(texCoordSet, prof, terrain, tt, outStream);
193         }
194 
195         // check we haven't exceeded texture coordinates
196         if (texCoordSet > 8)
197         {
198             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
199                 "Requested options require too many texture coordinate sets! Try reducing the number of layers.");
200         }
201 
202         outStream <<
203             ")\n"
204             "{\n";
205         if (compression)
206         {
207             outStream <<
208                 "   float4 pos;\n"
209                 "   pos = mul(posIndexToObjectSpace, float4(posIndex, height, 1));\n"
210                 "   float2 uv = float2(posIndex.x * baseUVScale, 1.0 - (posIndex.y * baseUVScale));\n";
211         }
212         outStream <<
213             "   float4 worldPos = mul(worldMatrix, pos);\n"
214             "   oPosObj = pos;\n";
215 
216         if (tt != RENDER_COMPOSITE_MAP)
217         {
218             // determine whether to apply the LOD morph to this vertex
219             // we store the deltas against all vertices so we only want to apply
220             // the morph to the ones which would disappear. The target LOD which is
221             // being morphed to is stored in lodMorph.y, and the LOD at which
222             // the vertex should be morphed is stored in uv.w. If we subtract
223             // the former from the latter, and arrange to only morph if the
224             // result is negative (it will only be -1 in fact, since after that
225             // the vertex will never be indexed), we will achieve our aim.
226             // sign(vertexLOD - targetLOD) == -1 is to morph
227             outStream <<
228                 "   float toMorph = -min(0, sign(delta.y - lodMorph.y));\n";
229             // this will either be 1 (morph) or 0 (don't morph)
230             if (prof->getParent()->getDebugLevel())
231             {
232                 // x == LOD level (-1 since value is target level, we want to display actual)
233                 outStream << "lodInfo.x = (lodMorph.y - 1) / " << terrain->getNumLodLevels() << ";\n";
234                 // y == LOD morph
235                 outStream << "lodInfo.y = toMorph * lodMorph.x;\n";
236             }
237 
238             // morph
239             switch (terrain->getAlignment())
240             {
241             case Terrain::ALIGN_X_Y:
242                 outStream << "  worldPos.z += delta.x * toMorph * lodMorph.x;\n";
243                 break;
244             case Terrain::ALIGN_X_Z:
245                 outStream << "  worldPos.y += delta.x * toMorph * lodMorph.x;\n";
246                 break;
247             case Terrain::ALIGN_Y_Z:
248                 outStream << "  worldPos.x += delta.x * toMorph * lodMorph.x;\n";
249                 break;
250             };
251         }
252 
253 
254         // generate UVs
255         if (tt != LOW_LOD)
256         {
257             for (uint i = 0; i < numUVSets; ++i)
258             {
259                 uint layer  =  i * 2;
260                 uint uvMulIdx = layer / 4;
261 
262                 outStream <<
263                     "   oUV" << i << ".xy = " << " uv.xy * uvMul_" << uvMulIdx << "." << getChannel(layer) << ";\n";
264                 outStream <<
265                     "   oUV" << i << ".zw = " << " uv.xy * uvMul_" << uvMulIdx << "." << getChannel(layer+1) << ";\n";
266 
267             }
268 
269         }
270 
271 
272     }
273     //---------------------------------------------------------------------
generateFpHeader(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)274     void ShaderHelperCg::generateFpHeader(
275         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
276     {
277 
278         // Main header
279         outStream <<
280             // helpers
281             "float4 expand(float4 v)\n"
282             "{ \n"
283             "   return v * 2 - 1;\n"
284             "}\n\n\n";
285 
286         if (prof->isShadowingEnabled(tt, terrain))
287             generateFpDynamicShadowsHelpers(prof, terrain, tt, outStream);
288 
289 
290         outStream <<
291             "float4 main_fp(\n"
292             "float4 vertexPos : POSITION,\n"
293             "float4 position : TEXCOORD0,\n";
294 
295         uint texCoordSet = 1;
296         outStream <<
297             "float4 uvMisc : TEXCOORD" << texCoordSet++ << ",\n";
298 
299         // UV's premultiplied, packed as xy/zw
300         uint maxLayers = prof->getMaxLayers(terrain);
301         uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount());
302         uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount()));
303         uint numUVSets = numLayers / 2;
304         if (numLayers % 2)
305             ++numUVSets;
306         if (tt != LOW_LOD)
307         {
308             for (uint i = 0; i < numUVSets; ++i)
309             {
310                 outStream <<
311                     "float4 layerUV" << i << " : TEXCOORD" << texCoordSet++ << ", \n";
312             }
313 
314         }
315         if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP)
316         {
317             outStream << "float2 lodInfo : TEXCOORD" << texCoordSet++ << ", \n";
318         }
319 
320         bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
321         if (fog)
322         {
323             outStream <<
324                 "uniform float3 fogColour, \n"
325                 "float fogVal : COLOR,\n";
326         }
327 
328         uint currentSamplerIdx = 0;
329 
330         outStream <<
331             // Only 1 light supported in this version
332             // deferred shading profile / generator later, ok? :)
333             "uniform float3 ambient,\n"
334             "uniform float4 lightPosObjSpace,\n"
335             "uniform float3 lightDiffuseColour,\n"
336             "uniform float3 lightSpecularColour,\n"
337             "uniform float3 eyePosObjSpace,\n"
338             // pack scale, bias and specular
339             "uniform float4 scaleBiasSpecular,\n";
340 
341         if (tt == LOW_LOD)
342         {
343             // single composite map covers all the others below
344             outStream <<
345                 "uniform sampler2D compositeMap : register(s" << currentSamplerIdx++ << ")\n";
346         }
347         else
348         {
349             outStream <<
350                 "uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n";
351 
352 
353             if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled())
354             {
355                 outStream << ", uniform sampler2D globalColourMap : register(s"
356                     << currentSamplerIdx++ << ")\n";
357             }
358             if (prof->isLightmapEnabled())
359             {
360                 outStream << ", uniform sampler2D lightMap : register(s"
361                     << currentSamplerIdx++ << ")\n";
362             }
363             // Blend textures - sampler definitions
364             for (uint i = 0; i < numBlendTextures; ++i)
365             {
366                 outStream << ", uniform sampler2D blendTex" << i
367                     << " : register(s" << currentSamplerIdx++ << ")\n";
368             }
369 
370             // Layer textures - sampler definitions & UV multipliers
371             for (uint i = 0; i < numLayers; ++i)
372             {
373                 outStream << ", uniform sampler2D difftex" << i
374                     << " : register(s" << currentSamplerIdx++ << ")\n";
375                 outStream << ", uniform sampler2D normtex" << i
376                     << " : register(s" << currentSamplerIdx++ << ")\n";
377             }
378         }
379 
380         if (prof->isShadowingEnabled(tt, terrain))
381         {
382             generateFpDynamicShadowsParams(&texCoordSet, &currentSamplerIdx, prof, terrain, tt, outStream);
383         }
384 
385         // check we haven't exceeded samplers
386         if (currentSamplerIdx > 16)
387         {
388             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
389                 "Requested options require too many texture samplers! Try reducing the number of layers.");
390         }
391 
392         outStream <<
393             ") : COLOR\n"
394             "{\n"
395             "   float4 outputCol;\n"
396             "   float shadow = 1.0;\n"
397             "   float2 uv = uvMisc.xy;\n"
398             // base colour
399             "   outputCol = float4(0,0,0,1);\n";
400 
401         if (tt != LOW_LOD)
402         {
403             outStream <<
404                 // global normal
405                 "   float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n";
406 
407         }
408 
409         outStream <<
410             "   float3 lightDir = \n"
411             "       lightPosObjSpace.xyz -  (position.xyz * lightPosObjSpace.w);\n"
412             "   float3 eyeDir = eyePosObjSpace - position.xyz;\n"
413 
414             // set up accumulation areas
415             "   float3 diffuse = float3(0,0,0);\n"
416             "   float specular = 0;\n";
417 
418 
419         if (tt == LOW_LOD)
420         {
421             // we just do a single calculation from composite map
422             outStream <<
423                 "   float4 composite = tex2D(compositeMap, uv);\n"
424                 "   diffuse = composite.rgb;\n";
425             // TODO - specular; we'll need normals for this!
426         }
427         else
428         {
429             // set up the blend values
430             for (uint i = 0; i < numBlendTextures; ++i)
431             {
432                 outStream << "  float4 blendTexVal" << i << " = tex2D(blendTex" << i << ", uv);\n";
433             }
434 
435             if (prof->isLayerNormalMappingEnabled())
436             {
437                 // derive the tangent space basis
438                 // we do this in the pixel shader because we don't have per-vertex normals
439                 // because of the LOD, we use a normal map
440                 // tangent is always +x or -z in object space depending on alignment
441                 switch(terrain->getAlignment())
442                 {
443                 case Terrain::ALIGN_X_Y:
444                 case Terrain::ALIGN_X_Z:
445                     outStream << "  float3 tangent = float3(1, 0, 0);\n";
446                     break;
447                 case Terrain::ALIGN_Y_Z:
448                     outStream << "  float3 tangent = float3(0, 0, -1);\n";
449                     break;
450                 };
451 
452                 outStream << "  float3 binormal = normalize(cross(tangent, normal));\n";
453                 // note, now we need to re-cross to derive tangent again because it wasn't orthonormal
454                 outStream << "  tangent = normalize(cross(normal, binormal));\n";
455                 // derive final matrix
456                 outStream << "  float3x3 TBN = float3x3(tangent, binormal, normal);\n";
457 
458                 // set up lighting result placeholders for interpolation
459                 outStream <<  " float4 litRes, litResLayer;\n";
460                 outStream << "  float3 TSlightDir, TSeyeDir, TShalfAngle, TSnormal;\n";
461                 if (prof->isLayerParallaxMappingEnabled())
462                     outStream << "  float displacement;\n";
463                 // move
464                 outStream << "  TSlightDir = normalize(mul(TBN, lightDir));\n";
465                 outStream << "  TSeyeDir = normalize(mul(TBN, eyeDir));\n";
466 
467             }
468             else
469             {
470                 // simple per-pixel lighting with no normal mapping
471                 outStream << "  lightDir = normalize(lightDir);\n";
472                 outStream << "  eyeDir = normalize(eyeDir);\n";
473                 outStream << "  float3 halfAngle = normalize(lightDir + eyeDir);\n";
474                 outStream << "  float4 litRes = lit(dot(lightDir, normal), dot(halfAngle, normal), scaleBiasSpecular.z);\n";
475 
476             }
477         }
478 
479 
480     }
481     //---------------------------------------------------------------------
generateVpLayer(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,uint layer,StringStream & outStream)482     void ShaderHelperCg::generateVpLayer(
483         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringStream& outStream)
484     {
485         // nothing to do
486     }
487     //---------------------------------------------------------------------
generateFpLayer(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,uint layer,StringStream & outStream)488     void ShaderHelperCg::generateFpLayer(
489         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringStream& outStream)
490     {
491         uint uvIdx = layer / 2;
492         String uvChannels = (layer % 2) ? ".zw" : ".xy";
493         uint blendIdx = (layer-1) / 4;
494         String blendChannel = getChannel(layer-1);
495         String blendWeightStr = String("blendTexVal") + StringConverter::toString(blendIdx) +
496             "." + blendChannel;
497 
498         // generate early-out conditional
499         /* Disable - causing some issues even when trying to force the use of texldd
500         if (layer && prof->_isSM3Available())
501             outStream << "  if (" << blendWeightStr << " > 0.0003)\n  { \n";
502         */
503 
504         // generate UV
505         outStream << "  float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n";
506 
507         // calculate lighting here if normal mapping
508         if (prof->isLayerNormalMappingEnabled())
509         {
510             if (prof->isLayerParallaxMappingEnabled() && tt != RENDER_COMPOSITE_MAP)
511             {
512                 // modify UV - note we have to sample an extra time
513                 outStream << "  displacement = tex2D(normtex" << layer << ", uv" << layer << ").a\n"
514                     "       * scaleBiasSpecular.x + scaleBiasSpecular.y;\n";
515                 outStream << "  uv" << layer << " += TSeyeDir.xy * displacement;\n";
516             }
517 
518             // access TS normal map
519             outStream << "  TSnormal = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n";
520             outStream << "  TShalfAngle = normalize(TSlightDir + TSeyeDir);\n";
521             outStream << "  litResLayer = lit(dot(TSlightDir, TSnormal), dot(TShalfAngle, TSnormal), scaleBiasSpecular.z);\n";
522             if (!layer)
523                 outStream << "  litRes = litResLayer;\n";
524             else
525                 outStream << "  litRes = lerp(litRes, litResLayer, " << blendWeightStr << ");\n";
526 
527         }
528 
529         // sample diffuse texture
530         outStream << "  float4 diffuseSpecTex" << layer
531             << " = tex2D(difftex" << layer << ", uv" << layer << ");\n";
532 
533         // apply to common
534         if (!layer)
535         {
536             outStream << "  diffuse = diffuseSpecTex0.rgb;\n";
537             if (prof->isLayerSpecularMappingEnabled())
538                 outStream << "  specular = diffuseSpecTex0.a;\n";
539         }
540         else
541         {
542             outStream << "  diffuse = lerp(diffuse, diffuseSpecTex" << layer
543                 << ".rgb, " << blendWeightStr << ");\n";
544             if (prof->isLayerSpecularMappingEnabled())
545                 outStream << "  specular = lerp(specular, diffuseSpecTex" << layer
546                     << ".a, " << blendWeightStr << ");\n";
547 
548         }
549 
550         // End early-out
551         /* Disable - causing some issues even when trying to force the use of texldd
552         if (layer && prof->_isSM3Available())
553             outStream << "  } // early-out blend value\n";
554         */
555     }
556     //---------------------------------------------------------------------
generateVpFooter(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)557     void ShaderHelperCg::generateVpFooter(
558         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
559     {
560 
561         outStream <<
562             "   oPos = mul(viewProjMatrix, worldPos);\n"
563             "   oUVMisc.xy = uv.xy;\n";
564 
565         bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
566         if (fog)
567         {
568             if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR)
569             {
570                 outStream <<
571                     "   fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n";
572             }
573             else
574             {
575                 outStream <<
576                     "   fogVal = 1 - saturate(1 / (exp(oPos.z * fogParams.x)));\n";
577             }
578         }
579 
580         if (prof->isShadowingEnabled(tt, terrain))
581             generateVpDynamicShadows(prof, terrain, tt, outStream);
582 
583         outStream <<
584             "}\n";
585 
586 
587     }
588     //---------------------------------------------------------------------
generateFpFooter(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)589     void ShaderHelperCg::generateFpFooter(
590         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
591     {
592 
593         if (tt == LOW_LOD)
594         {
595             if (prof->isShadowingEnabled(tt, terrain))
596             {
597                 generateFpDynamicShadows(prof, terrain, tt, outStream);
598                 outStream <<
599                     "   outputCol.rgb = diffuse * rtshadow;\n";
600             }
601             else
602             {
603                 outStream <<
604                     "   outputCol.rgb = diffuse;\n";
605             }
606         }
607         else
608         {
609             if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled())
610             {
611                 // sample colour map and apply to diffuse
612                 outStream << "  diffuse *= tex2D(globalColourMap, uv).rgb;\n";
613             }
614             if (prof->isLightmapEnabled())
615             {
616                 // sample lightmap
617                 outStream << "  shadow = tex2D(lightMap, uv).r;\n";
618             }
619 
620             if (prof->isShadowingEnabled(tt, terrain))
621             {
622                 generateFpDynamicShadows(prof, terrain, tt, outStream);
623             }
624 
625             // diffuse lighting
626             outStream << "  outputCol.rgb += ambient.rgb * diffuse + litRes.y * lightDiffuseColour * diffuse * shadow;\n";
627 
628             // specular default
629             if (!prof->isLayerSpecularMappingEnabled())
630                 outStream << "  specular = 1.0;\n";
631 
632             if (tt == RENDER_COMPOSITE_MAP)
633             {
634                 // Lighting embedded in alpha
635                 outStream <<
636                     "   outputCol.a = shadow;\n";
637 
638             }
639             else
640             {
641                 // Apply specular
642                 outStream << "  outputCol.rgb += litRes.z * lightSpecularColour * specular * shadow;\n";
643 
644                 if (prof->getParent()->getDebugLevel())
645                 {
646                     outStream << "  outputCol.rg += lodInfo.xy;\n";
647                 }
648             }
649         }
650 
651         bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP;
652         if (fog)
653         {
654             outStream << "  outputCol.rgb = lerp(outputCol.rgb, fogColour, fogVal);\n";
655         }
656 
657         // Final return
658         outStream << "  return outputCol;\n"
659             << "}\n";
660 
661     }
662     //---------------------------------------------------------------------
generateFpDynamicShadowsHelpers(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)663     void ShaderHelperCg::generateFpDynamicShadowsHelpers(
664         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
665     {
666         // TODO make filtering configurable
667         outStream <<
668             "// Simple PCF \n"
669             "// Number of samples in one dimension (square for total samples) \n"
670             "#define NUM_SHADOW_SAMPLES_1D 2.0 \n"
671             "#define SHADOW_FILTER_SCALE 1 \n"
672 
673             "#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D \n"
674 
675             "float4 offsetSample(float4 uv, float2 offset, float invMapSize) \n"
676             "{ \n"
677             "   return float4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w); \n"
678             "} \n";
679 
680         if (prof->getReceiveDynamicShadowsDepth())
681         {
682             outStream <<
683                 "float calcDepthShadow(sampler2D shadowMap, float4 uv, float invShadowMapSize) \n"
684                 "{ \n"
685                 "   // 4-sample PCF \n"
686 
687                 "   float shadow = 0.0; \n"
688                 "   float offset = (NUM_SHADOW_SAMPLES_1D/2 - 0.5) * SHADOW_FILTER_SCALE; \n"
689                 "   for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE) \n"
690                 "       for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE) \n"
691                 "       { \n"
692                 "           float4 newUV = offsetSample(uv, float2(x, y), invShadowMapSize);\n"
693                 "           // manually project and assign derivatives \n"
694                 "           // to avoid gradient issues inside loops \n"
695                 "           newUV = newUV / newUV.w; \n"
696                 "           float depth = tex2D(shadowMap, newUV.xy, 1, 1).x; \n"
697                 "           if (depth >= 1 || depth >= uv.z)\n"
698                 "               shadow += 1.0;\n"
699                 "       } \n"
700 
701                 "   shadow /= SHADOW_SAMPLES; \n"
702 
703                 "   return shadow; \n"
704                 "} \n";
705         }
706         else
707         {
708             outStream <<
709                 "float calcSimpleShadow(sampler2D shadowMap, float4 shadowMapPos) \n"
710                 "{ \n"
711                 "   return tex2Dproj(shadowMap, shadowMapPos).x; \n"
712                 "} \n";
713 
714         }
715 
716         if (prof->getReceiveDynamicShadowsPSSM())
717         {
718             uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
719 
720 
721             if (prof->getReceiveDynamicShadowsDepth())
722             {
723                 outStream <<
724                     "float calcPSSMDepthShadow(";
725             }
726             else
727             {
728                 outStream <<
729                     "float calcPSSMSimpleShadow(";
730             }
731 
732             outStream << "\n    ";
733             for (uint i = 0; i < numTextures; ++i)
734                 outStream << "sampler2D shadowMap" << i << ", ";
735             outStream << "\n    ";
736             for (uint i = 0; i < numTextures; ++i)
737                 outStream << "float4 lsPos" << i << ", ";
738             if (prof->getReceiveDynamicShadowsDepth())
739             {
740                 outStream << "\n    ";
741                 for (uint i = 0; i < numTextures; ++i)
742                     outStream << "float invShadowmapSize" << i << ", ";
743             }
744             outStream << "\n"
745                 "   float4 pssmSplitPoints, float camDepth) \n"
746                 "{ \n"
747                 "   float shadow; \n"
748                 "   // calculate shadow \n";
749 
750             for (uint i = 0; i < numTextures; ++i)
751             {
752                 if (!i)
753                     outStream << "  if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n";
754                 else if (i < numTextures - 1)
755                     outStream << "  else if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n";
756                 else
757                     outStream << "  else \n";
758 
759                 outStream <<
760                     "   { \n";
761                 if (prof->getReceiveDynamicShadowsDepth())
762                 {
763                     outStream <<
764                         "       shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << "); \n";
765                 }
766                 else
767                 {
768                     outStream <<
769                         "       shadow = calcSimpleShadow(shadowMap" << i << ", lsPos" << i << "); \n";
770                 }
771                 outStream <<
772                     "   } \n";
773 
774             }
775 
776             outStream <<
777                 "   return shadow; \n"
778                 "} \n\n\n";
779         }
780 
781 
782     }
783     //---------------------------------------------------------------------
generateVpDynamicShadowsParams(uint texCoord,const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)784     uint ShaderHelperCg::generateVpDynamicShadowsParams(
785         uint texCoord, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
786     {
787         // out semantics & params
788         uint numTextures = 1;
789         if (prof->getReceiveDynamicShadowsPSSM())
790         {
791             numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
792         }
793         for (uint i = 0; i < numTextures; ++i)
794         {
795             outStream <<
796                 ", out float4 oLightSpacePos" << i << " : TEXCOORD" << texCoord++ << " \n" <<
797                 ", uniform float4x4 texViewProjMatrix" << i << " \n";
798             if (prof->getReceiveDynamicShadowsDepth())
799             {
800                 outStream <<
801                     ", uniform float4 depthRange" << i << " // x = min, y = max, z = range, w = 1/range \n";
802             }
803         }
804 
805         return texCoord;
806 
807     }
808     //---------------------------------------------------------------------
generateVpDynamicShadows(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)809     void ShaderHelperCg::generateVpDynamicShadows(
810         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
811     {
812         uint numTextures = 1;
813         if (prof->getReceiveDynamicShadowsPSSM())
814         {
815             numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
816         }
817 
818         // Calculate the position of vertex in light space
819         for (uint i = 0; i < numTextures; ++i)
820         {
821             outStream <<
822                 "   oLightSpacePos" << i << " = mul(texViewProjMatrix" << i << ", worldPos); \n";
823             if (prof->getReceiveDynamicShadowsDepth())
824             {
825                 // make linear
826                 outStream <<
827                     "oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n";
828 
829             }
830         }
831 
832 
833         if (prof->getReceiveDynamicShadowsPSSM())
834         {
835             outStream <<
836                 "   // pass cam depth\n"
837                 "   oUVMisc.z = oPos.z;\n";
838         }
839 
840     }
841     //---------------------------------------------------------------------
generateFpDynamicShadowsParams(uint * texCoord,uint * sampler,const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)842     void ShaderHelperCg::generateFpDynamicShadowsParams(
843         uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
844     {
845         if (tt == HIGH_LOD)
846             mShadowSamplerStartHi = *sampler;
847         else if (tt == LOW_LOD)
848             mShadowSamplerStartLo = *sampler;
849 
850         // in semantics & params
851         uint numTextures = 1;
852         if (prof->getReceiveDynamicShadowsPSSM())
853         {
854             numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
855             outStream <<
856                 ", uniform float4 pssmSplitPoints \n";
857         }
858         for (uint i = 0; i < numTextures; ++i)
859         {
860             outStream <<
861                 ", float4 lightSpacePos" << i << " : TEXCOORD" << *texCoord << " \n" <<
862                 ", uniform sampler2D shadowMap" << i << " : register(s" << *sampler << ") \n";
863             *sampler = *sampler + 1;
864             *texCoord = *texCoord + 1;
865             if (prof->getReceiveDynamicShadowsDepth())
866             {
867                 outStream <<
868                     ", uniform float inverseShadowmapSize" << i << " \n";
869             }
870         }
871 
872     }
873     //---------------------------------------------------------------------
generateFpDynamicShadows(const SM2Profile * prof,const Terrain * terrain,TechniqueType tt,StringStream & outStream)874     void ShaderHelperCg::generateFpDynamicShadows(
875         const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringStream& outStream)
876     {
877         if (prof->getReceiveDynamicShadowsPSSM())
878         {
879             uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount();
880             outStream <<
881                 "   float camDepth = uvMisc.z;\n";
882 
883             if (prof->getReceiveDynamicShadowsDepth())
884             {
885                 outStream <<
886                     "   float rtshadow = calcPSSMDepthShadow(";
887             }
888             else
889             {
890                 outStream <<
891                     "   float rtshadow = calcPSSMSimpleShadow(";
892             }
893             for (uint i = 0; i < numTextures; ++i)
894                 outStream << "shadowMap" << i << ", ";
895             outStream << "\n        ";
896 
897             for (uint i = 0; i < numTextures; ++i)
898                 outStream << "lightSpacePos" << i << ", ";
899             if (prof->getReceiveDynamicShadowsDepth())
900             {
901                 outStream << "\n        ";
902                 for (uint i = 0; i < numTextures; ++i)
903                     outStream << "inverseShadowmapSize" << i << ", ";
904             }
905             outStream << "\n" <<
906                 "       pssmSplitPoints, camDepth);\n";
907 
908         }
909         else
910         {
911             if (prof->getReceiveDynamicShadowsDepth())
912             {
913                 outStream <<
914                     "   float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0);";
915             }
916             else
917             {
918                 outStream <<
919                     "   float rtshadow = calcSimpleShadow(shadowMap0, lightSpacePos0);";
920             }
921         }
922 
923         outStream <<
924             "   shadow = min(shadow, rtshadow);\n";
925 
926     }
927 }
928