#include "shaderhelper.hpp" #include "renderingmanager.hpp" #include "shadows.hpp" #include <OgreHighLevelGpuProgramManager.h> #include <OgreHighLevelGpuProgram.h> #include <OgreGpuProgramParams.h> #include <components/settings/settings.hpp> using namespace Ogre; using namespace MWRender; ShaderHelper::ShaderHelper(RenderingManager* rend) { mRendering = rend; applyShaders(); } void ShaderHelper::applyShaders() { if (!Settings::Manager::getBool("shaders", "Objects")) return; bool mrt = RenderingManager::useMRT(); bool shadows = Settings::Manager::getBool("enabled", "Shadows"); bool split = Settings::Manager::getBool("split", "Shadows"); // shader for normal rendering createShader(mrt, shadows, split, "main"); // fallback shader without mrt and without shadows // (useful for reflection and for minimap) createShader(false, false, false, "main_fallback"); } void ShaderHelper::createShader(const bool mrt, const bool shadows, const bool split, const std::string& name) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); const int numsplits = 3; // the number of lights to support. // when rendering an object, OGRE automatically picks the lights that are // closest to the object being rendered. unfortunately this mechanism does // not work perfectly for objects batched together (they will all use the same // lights). to work around this, we are simply pushing the maximum number // of lights here in order to minimize disappearing lights. int num_lights = Settings::Manager::getInt("num lights", "Objects"); { // vertex HighLevelGpuProgramPtr vertex; if (!mgr.getByName(name+"_vp").isNull()) mgr.remove(name+"_vp"); vertex = mgr.createProgram(name+"_vp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); vertex->setParameter("profiles", "vs_4_0 vs_2_x vp40 arbvp1"); vertex->setParameter("entry_point", "main_vp"); StringUtil::StrStreamType outStream; outStream << "void main_vp( \n" " float4 position : POSITION, \n" " float4 normal : NORMAL, \n" " float4 colour : COLOR, \n" " in float2 uv : TEXCOORD0, \n" " out float2 oUV : TEXCOORD0, \n" " out float4 oPosition : POSITION, \n" " out float4 oPositionObjSpace : TEXCOORD1, \n" " out float4 oNormal : TEXCOORD2, \n" " out float oDepth : TEXCOORD3, \n" " out float4 oVertexColour : TEXCOORD4, \n"; if (shadows && !split) outStream << " out float4 oLightSpacePos0 : TEXCOORD5, \n" " uniform float4x4 worldMatrix, \n" " uniform float4x4 texViewProjMatrix0, \n"; else { for (int i=0; i<numsplits; ++i) { outStream << " out float4 oLightSpacePos"<<i<<" : TEXCOORD"<<i+5<<", \n" " uniform float4x4 texViewProjMatrix"<<i<<", \n"; } outStream << " uniform float4x4 worldMatrix, \n"; } outStream << " uniform float4x4 worldViewProj \n" ") \n" "{ \n" " oVertexColour = colour; \n" " oUV = uv; \n" " oNormal = normal; \n" " oPosition = mul( worldViewProj, position ); \n" " oDepth = oPosition.z; \n" " oPositionObjSpace = position; \n"; if (shadows && !split) outStream << " oLightSpacePos0 = mul(texViewProjMatrix0, mul(worldMatrix, position)); \n"; else { outStream << " float4 wPos = mul(worldMatrix, position); \n"; for (int i=0; i<numsplits; ++i) { outStream << " oLightSpacePos"<<i<<" = mul(texViewProjMatrix"<<i<<", wPos); \n"; } } outStream << "}"; vertex->setSource(outStream.str()); vertex->load(); vertex->getDefaultParameters()->setNamedAutoConstant("worldViewProj", GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX); if (shadows) { vertex->getDefaultParameters()->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX); if (!split) vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix0", GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, 0); else { for (int i=0; i<numsplits; ++i) { vertex->getDefaultParameters()->setNamedAutoConstant("texViewProjMatrix"+StringConverter::toString(i), GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); } } } } { // fragment HighLevelGpuProgramPtr fragment; if (!mgr.getByName(name+"_fp").isNull()) mgr.remove(name+"_fp"); fragment = mgr.createProgram(name+"_fp", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); fragment->setParameter("profiles", "ps_4_0 ps_2_x fp40 arbfp1"); fragment->setParameter("entry_point", "main_fp"); StringUtil::StrStreamType outStream; if (shadows) outStream << "float depthShadow(sampler2D shadowMap, float4 shadowMapPos, float2 offset) \n" "{ \n" " shadowMapPos /= shadowMapPos.w; \n" " float3 o = float3(offset.xy, -offset.x) * 0.3f; \n" " float c = (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.xy).r) ? 1 : 0; // top left \n" " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.xy).r) ? 1 : 0; // bottom right \n" " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy + o.zy).r) ? 1 : 0; // bottom left \n" " c += (shadowMapPos.z <= tex2D(shadowMap, shadowMapPos.xy - o.zy).r) ? 1 : 0; // top right \n" " return c / 4; \n" "} \n"; outStream << "void main_fp( \n" " in float2 uv : TEXCOORD0, \n" " out float4 oColor : COLOR, \n" " uniform sampler2D texture : register(s0), \n" " float4 positionObjSpace : TEXCOORD1, \n" " float4 normal : TEXCOORD2, \n" " float iDepth : TEXCOORD3, \n" " float4 vertexColour : TEXCOORD4, \n" " uniform float4 fogColour, \n" " uniform float4 fogParams, \n"; if (shadows) outStream << " uniform float4 shadowFar_fadeStart, \n"; if (shadows && !split) outStream << " uniform sampler2D shadowMap : register(s1), \n" " float4 lightSpacePos0 : TEXCOORD5, \n" " uniform float4 invShadowmapSize0, \n"; else { outStream << " uniform float4 pssmSplitPoints, \n"; for (int i=0; i<numsplits; ++i) { outStream << " uniform sampler2D shadowMap"<<i<<" : register(s"<<i+1<<"), \n" " float4 lightSpacePos"<<i<<" : TEXCOORD"<<i+5<<", \n" " uniform float4 invShadowmapSize"<<i<<", \n"; } } if (mrt) outStream << " out float4 oColor1 : COLOR1, \n" " uniform float far, \n"; for (int i=0; i<num_lights; ++i) { outStream << " uniform float4 lightDiffuse"<<i<<", \n" " uniform float4 lightPositionObjSpace"<<i<<", \n" " uniform float4 lightAttenuation"<<i<<", \n"; } outStream << " uniform float4 lightAmbient, \n" " uniform float4 ambient, \n" " uniform float4 diffuse, \n" " uniform float4 emissive \n" ") \n" "{ \n" " float4 tex = tex2D(texture, uv); \n" " float d; \n" " float attn; \n" " float3 lightDir; \n" " float3 lightColour = float3(0, 0, 0); \n"; for (int i=0; i<num_lights; ++i) { outStream << " lightDir = lightPositionObjSpace"<<i<<".xyz - (positionObjSpace.xyz * lightPositionObjSpace"<<i<<".w); \n" // pre-multiply light color with attenuation factor " d = length( lightDir ); \n" " attn = ( 1.0 / (( lightAttenuation"<<i<<".y ) + ( lightAttenuation"<<i<<".z * d ) + ( lightAttenuation"<<i<<".w * d * d ))); \n" " lightDiffuse"<<i<<" *= attn; \n"; if (i == 0 && shadows) { outStream << " float shadow; \n"; if (!split) outStream << " shadow = depthShadow(shadowMap, lightSpacePos0, invShadowmapSize0.xy); \n"; else { for (int j=0; j<numsplits; ++j) { std::string channel; if (j==0) channel = "x"; else if (j==1) channel = "y"; else if (j==2) channel = "z"; if (j==0) outStream << " if (iDepth <= pssmSplitPoints." << channel << ") \n"; else if (j < numsplits - 1) outStream << " else if (iDepth <= pssmSplitPoints." << channel << ") \n"; else outStream << " else \n"; outStream << " { \n" " shadow = depthShadow(shadowMap" << j << ", lightSpacePos" << j << ", invShadowmapSize" << j << ".xy); \n" " } \n"; } } outStream << " float fadeRange = shadowFar_fadeStart.x - shadowFar_fadeStart.y; \n" " float fade = 1-((iDepth - shadowFar_fadeStart.y) / fadeRange); \n" " shadow = (iDepth > shadowFar_fadeStart.x) ? 1 : ((iDepth > shadowFar_fadeStart.y) ? 1-((1-shadow)*fade) : shadow); \n" " lightColour.xyz += shadow * lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n"; } else outStream << " lightColour.xyz += lit(dot(normalize(lightDir), normalize(normal)), 0, 0).y * lightDiffuse"<<i<<".xyz;\n"; } outStream << " float3 lightingFinal = lightColour.xyz * diffuse.xyz + ambient.xyz * lightAmbient.xyz + emissive.xyz; \n" " float fogValue = saturate((iDepth - fogParams.y) * fogParams.w); \n" " oColor.xyz = saturate(lerp(lightingFinal * tex.xyz * vertexColour.xyz, fogColour.xyz, fogValue)); \n" // saturate output to prevent negative output colors - TODO: replace this once there is HDR-rendering " oColor.a = tex.a * diffuse.a * vertexColour.a; \n"; if (mrt) outStream << " oColor1 = float4(iDepth / far, 0, 0, (oColor.a == 1)); \n"; // only write to MRT if alpha is 1 outStream << "}"; fragment->setSource(outStream.str()); fragment->load(); for (int i=0; i<num_lights; ++i) { fragment->getDefaultParameters()->setNamedAutoConstant("lightPositionObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); fragment->getDefaultParameters()->setNamedAutoConstant("lightDiffuse"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); fragment->getDefaultParameters()->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); } fragment->getDefaultParameters()->setNamedAutoConstant("emissive", GpuProgramParameters::ACT_SURFACE_EMISSIVE_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("diffuse", GpuProgramParameters::ACT_SURFACE_DIFFUSE_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_SURFACE_AMBIENT_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("lightAmbient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); fragment->getDefaultParameters()->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); if (shadows) { fragment->getDefaultParameters()->setNamedConstant("shadowFar_fadeStart", Vector4(mRendering->getShadows()->getShadowFar(), mRendering->getShadows()->getFadeStart()*mRendering->getShadows()->getShadowFar(), 0, 0)); for (int i=0; i < (split ? numsplits : 1); ++i) { fragment->getDefaultParameters()->setNamedAutoConstant("invShadowmapSize" + StringConverter::toString(i), GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i+1); } if (split) { Vector4 splitPoints; const PSSMShadowCameraSetup::SplitPointList& splitPointList = mRendering->getShadows()->getPSSMSetup()->getSplitPoints(); // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) for (int i = 1; i < numsplits; ++i) { splitPoints[i-1] = splitPointList[i]; } fragment->getDefaultParameters()->setNamedConstant("pssmSplitPoints", splitPoints); } } if (mrt) fragment->getDefaultParameters()->setNamedAutoConstant("far", GpuProgramParameters::ACT_FAR_CLIP_DISTANCE); } }