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