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