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