Terrain material now uses multiple passes if required, which means it can support an arbitrary number of layers. Also re-enables PSSM.

actorid
scrawl 12 years ago
parent 40bb772e34
commit 0e7d555cdf

@ -236,9 +236,7 @@ namespace MWGui
mReflectTerrainButton->setCaptionWithReplacing(Settings::Manager::getBool("reflect terrain", "Water") ? "#{sOn}" : "#{sOff}");
mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows"));
//mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}");
mShadowsLargeDistance->setCaptionWithReplacing("#{sOff}");
mShadowsLargeDistance->setEnabled (false);
mShadowsLargeDistance->setCaptionWithReplacing(Settings::Manager::getBool("split", "Shadows") ? "#{sOn}" : "#{sOff}");
mShadowsEnabledButton->setCaptionWithReplacing(Settings::Manager::getBool("enabled", "Shadows") ? "#{sOn}" : "#{sOff}");
mActorShadows->setCaptionWithReplacing(Settings::Manager::getBool("actor shadows", "Shadows") ? "#{sOn}" : "#{sOff}");

@ -28,10 +28,7 @@ void Shadows::recreate()
{
bool enabled = Settings::Manager::getBool("enabled", "Shadows");
// Split shadow maps are currently disabled because the terrain cannot cope with them
// (Too many texture units) Solution would be a multi-pass terrain material
//bool split = Settings::Manager::getBool("split", "Shadows");
const bool split = false;
bool split = Settings::Manager::getBool("split", "Shadows");
sh::Factory::getInstance ().setGlobalSetting ("shadows", enabled && !split ? "true" : "false");
sh::Factory::getInstance ().setGlobalSetting ("shadows_pssm", enabled && split ? "true" : "false");

@ -68,60 +68,114 @@ namespace MWRender
Ogre::MaterialManager::getSingleton().remove(matName);
mMaterial = sh::Factory::getInstance().createMaterialInstance (matName);
mMaterial->setProperty ("allow_fixed_function", sh::makeProperty<sh::BooleanValue>(new sh::BooleanValue(false)));
sh::MaterialInstancePass* p = mMaterial->createPass ();
p->setProperty ("vertex_program", sh::makeProperty<sh::StringValue>(new sh::StringValue("terrain_vertex")));
p->setProperty ("fragment_program", sh::makeProperty<sh::StringValue>(new sh::StringValue("terrain_fragment")));
p->mShaderProperties.setProperty ("colour_map", sh::makeProperty<sh::BooleanValue>(new sh::BooleanValue(mGlobalColourMap)));
// global colour map
sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap");
colourMap->setProperty ("texture_alias", sh::makeProperty<sh::StringValue> (new sh::StringValue(mMaterial->getName() + "_colourMap")));
colourMap->setProperty ("tex_address_mode", sh::makeProperty<sh::StringValue> (new sh::StringValue("clamp")));
// global normal map
sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap");
normalMap->setProperty ("direct_texture", sh::makeProperty<sh::StringValue> (new sh::StringValue(terrain->getTerrainNormalMap ()->getName())));
normalMap->setProperty ("tex_address_mode", sh::makeProperty<sh::StringValue> (new sh::StringValue("clamp")));
Ogre::uint maxLayers = getMaxLayers(terrain);
Ogre::uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount());
Ogre::uint numLayers = std::min(maxLayers, static_cast<Ogre::uint>(terrain->getLayerCount()));
int numPasses = getRequiredPasses(terrain);
int maxLayersInOnePass = getMaxLayersPerPass(terrain);
p->mShaderProperties.setProperty ("num_layers", sh::makeProperty<sh::StringValue>(new sh::StringValue(Ogre::StringConverter::toString(numLayers))));
p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty<sh::StringValue>(new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures))));
// blend maps
for (Ogre::uint i = 0; i < numBlendTextures; ++i)
{
sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i));
blendTex->setProperty ("direct_texture", sh::makeProperty<sh::StringValue> (new sh::StringValue(terrain->getBlendTextureName(i))));
blendTex->setProperty ("tex_address_mode", sh::makeProperty<sh::StringValue> (new sh::StringValue("clamp")));
}
// layer maps
for (Ogre::uint i = 0; i < numLayers; ++i)
for (int pass=0; pass<numPasses; ++pass)
{
sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i));
diffuseTex->setProperty ("direct_texture", sh::makeProperty<sh::StringValue> (new sh::StringValue(terrain->getLayerTextureName(i, 0))));
p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i),
sh::makeProperty<sh::StringValue>(new sh::StringValue(Ogre::StringConverter::toString(int((i-1) / 4)) + "." + getComponent(int((i-1) % 4)))));
int layerOffset = maxLayersInOnePass * pass;
int blendmapOffset = (pass == 0) ? 1 : 0; // the first layer of the first pass is the base layer and does not need a blend map
sh::MaterialInstancePass* p = mMaterial->createPass ();
p->setProperty ("vertex_program", sh::makeProperty<sh::StringValue>(new sh::StringValue("terrain_vertex")));
p->setProperty ("fragment_program", sh::makeProperty<sh::StringValue>(new sh::StringValue("terrain_fragment")));
if (pass != 0)
{
p->setProperty ("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend")));
// Only write if depth is equal to the depth value written by the previous pass.
p->setProperty ("depth_func", sh::makeProperty(new sh::StringValue("equal")));
}
p->mShaderProperties.setProperty ("colour_map", sh::makeProperty(new sh::BooleanValue(mGlobalColourMap)));
p->mShaderProperties.setProperty ("is_first_pass", sh::makeProperty(new sh::BooleanValue(pass == 0)));
// global colour map
sh::MaterialInstanceTextureUnit* colourMap = p->createTextureUnit ("colourMap");
colourMap->setProperty ("texture_alias", sh::makeProperty<sh::StringValue> (new sh::StringValue(mMaterial->getName() + "_colourMap")));
colourMap->setProperty ("tex_address_mode", sh::makeProperty<sh::StringValue> (new sh::StringValue("clamp")));
// global normal map
sh::MaterialInstanceTextureUnit* normalMap = p->createTextureUnit ("normalMap");
normalMap->setProperty ("direct_texture", sh::makeProperty<sh::StringValue> (new sh::StringValue(terrain->getTerrainNormalMap ()->getName())));
normalMap->setProperty ("tex_address_mode", sh::makeProperty<sh::StringValue> (new sh::StringValue("clamp")));
Ogre::uint numLayersInThisPass = std::min(maxLayersInOnePass, terrain->getLayerCount()-layerOffset);
// HACK: Terrain::getLayerBlendTextureIndex should be const, but it is not.
// Remove this once ogre got fixed.
Ogre::Terrain* nonconstTerrain = const_cast<Ogre::Terrain*>(terrain);
// a blend map might be shared between two passes
// so we can't just use terrain->getBlendTextureCount()
Ogre::uint numBlendTextures=0;
std::vector<std::string> blendTextures;
for (unsigned int layer=blendmapOffset; layer<numLayersInThisPass; ++layer)
{
std::string blendTextureName = terrain->getBlendTextureName(nonconstTerrain->getLayerBlendTextureIndex(
static_cast<Ogre::uint8>(layerOffset+layer)).first);
if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end())
{
blendTextures.push_back(blendTextureName);
++numBlendTextures;
}
}
p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numLayersInThisPass))));
p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures))));
// blend maps
// the index of the first blend map used in this pass
int blendmapStart;
if (terrain->getLayerCount() == 1) // special case. if there's only one layer, we don't need blend maps at all
blendmapStart = 0;
else
blendmapStart = nonconstTerrain->getLayerBlendTextureIndex(static_cast<Ogre::uint8>(layerOffset+blendmapOffset)).first;
for (Ogre::uint i = 0; i < numBlendTextures; ++i)
{
sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i));
blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getBlendTextureName(blendmapStart+i))));
blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp")));
}
// layer maps
for (Ogre::uint i = 0; i < numLayersInThisPass; ++i)
{
sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i));
diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(terrain->getLayerTextureName(layerOffset+i, 0))));
if (i+layerOffset > 0)
{
int blendTextureIndex = nonconstTerrain->getLayerBlendTextureIndex(static_cast<Ogre::uint8>(layerOffset+i)).first;
int blendTextureComponent = nonconstTerrain->getLayerBlendTextureIndex(static_cast<Ogre::uint8>(layerOffset+i)).second;
p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i),
sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(blendTextureIndex-blendmapStart) + "." + getComponent(blendTextureComponent))));
}
else
{
// just to make it shut up about blendmap_component_0 not existing in the first pass.
// it might be retrieved, but will never survive the preprocessing step.
p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i),
sh::makeProperty (new sh::StringValue("")));
}
}
// shadow
for (Ogre::uint i = 0; i < 3; ++i)
{
sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i));
shadowTex->setProperty ("content_type", sh::makeProperty<sh::StringValue> (new sh::StringValue("shadow")));
}
p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty (new sh::StringValue(
Ogre::StringConverter::toString(numBlendTextures + numLayersInThisPass + 2))));
// make sure the pass index is fed to the permutation handler, because blendmap components may be different
p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(pass)));
}
// shadow
for (Ogre::uint i = 0; i < 3; ++i)
{
sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i));
shadowTex->setProperty ("content_type", sh::makeProperty<sh::StringValue> (new sh::StringValue("shadow")));
}
p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty<sh::StringValue>(new sh::StringValue(
Ogre::StringConverter::toString(numBlendTextures + numLayers + 2))));
return Ogre::MaterialManager::getSingleton().getByName(matName);
}
@ -142,6 +196,11 @@ namespace MWRender
}
Ogre::uint8 TerrainMaterial::Profile::getMaxLayers(const Ogre::Terrain* terrain) const
{
return 255;
}
int TerrainMaterial::Profile::getMaxLayersPerPass (const Ogre::Terrain* terrain)
{
// count the texture units free
Ogre::uint8 freeTextureUnits = 16;
@ -151,11 +210,21 @@ namespace MWRender
--freeTextureUnits;
// shadow
--freeTextureUnits;
--freeTextureUnits;
--freeTextureUnits;
// each layer needs 1.25 units (1xdiffusespec, 0.25xblend)
return static_cast<Ogre::uint8>(freeTextureUnits / (1.25f));
}
int TerrainMaterial::Profile::getRequiredPasses (const Ogre::Terrain* terrain)
{
int maxLayersPerPass = getMaxLayersPerPass(terrain);
assert(terrain->getLayerCount());
assert(maxLayersPerPass);
return std::ceil(static_cast<float>(terrain->getLayerCount()) / maxLayersPerPass);
}
void TerrainMaterial::Profile::updateParams(const Ogre::MaterialPtr& mat, const Ogre::Terrain* terrain)
{
}

@ -72,6 +72,9 @@ namespace MWRender
private:
sh::MaterialInstance* mMaterial;
int getRequiredPasses (const Ogre::Terrain* terrain);
int getMaxLayersPerPass (const Ogre::Terrain* terrain);
bool mGlobalColourMap;
};

@ -45,8 +45,7 @@
// use alpha channel of the first texture
float alpha = shSample(texture1, UV).a;
// discard if alpha is less than 0.5
if (alpha < 1.0)
if (alpha < 0.5)
discard;
#endif

@ -1,6 +1,6 @@
#include "core.h"
#define IS_FIRST_PASS 1
#define IS_FIRST_PASS (@shPropertyString(pass_index) == 0)
#define FOG @shGlobalSettingBool(fog)
@ -23,6 +23,9 @@
#define VIEWPROJ_FIX @shGlobalSettingBool(viewproj_fix)
#if !IS_FIRST_PASS
// This is not the first pass.
#endif
#if NEED_DEPTH
@shAllocatePassthrough(1, depth)
@ -222,7 +225,11 @@
float3 waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,0,1), waterLevel);
#endif
#if !IS_FIRST_PASS
float combinedAlpha = 0.f;
#endif
// Layer calculations
@shForeach(@shPropertyString(num_blendmaps))
float4 blendValues@shIterator = shSample(blendMap@shIterator, UV);
@ -232,12 +239,20 @@
@shForeach(@shPropertyString(num_layers))
#if IS_FIRST_PASS == 1 && @shIterator == 0
// first layer of first pass doesn't need a blend map
#if IS_FIRST_PASS
#if @shIterator == 0
// first layer of first pass is the base layer and doesn't need a blend map
albedo = shSample(diffuseMap0, UV * 10).rgb;
#else
albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator));
#endif
#else
#if @shIterator == 0
albedo = shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator);
#else
albedo = shLerp(albedo, shSample(diffuseMap@shIterator, UV * 10).rgb, blendValues@shPropertyString(blendmap_component_@shIterator));
#endif
combinedAlpha += blendValues@shPropertyString(blendmap_component_@shIterator);
#endif
@shEndForeach
@ -325,6 +340,12 @@
// prevent negative colour output (for example with negative lights)
shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0,0,0));
#if IS_FIRST_PASS
shOutputColour(0).a = 1;
#else
shOutputColour(0).a = min(combinedAlpha, 1.f);
#endif
}
#endif

Loading…
Cancel
Save