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, ¤tSamplerIdx, 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