Add terrain shaders and normal map support

Textures with _n filename suffix are automatically recognized as terrain normal maps.
move
scrawl 8 years ago
parent 17c4dfdb62
commit 6000e48bba

@ -228,6 +228,11 @@ namespace Resource
mForceShaders = force;
}
bool SceneManager::getForceShaders() const
{
return mForceShaders;
}
void SceneManager::recreateShaders(osg::ref_ptr<osg::Node> node)
{
Shader::ShaderVisitor shaderVisitor(*mShaderManager.get(), "objects_vertex.glsl", "objects_fragment.glsl");
@ -243,16 +248,31 @@ namespace Resource
mClampLighting = clamp;
}
bool SceneManager::getClampLighting() const
{
return mClampLighting;
}
void SceneManager::setForcePerPixelLighting(bool force)
{
mForcePerPixelLighting = force;
}
bool SceneManager::getForcePerPixelLighting() const
{
return mForcePerPixelLighting;
}
SceneManager::~SceneManager()
{
// this has to be defined in the .cpp file as we can't delete incomplete types
}
Shader::ShaderManager &SceneManager::getShaderManager()
{
return *mShaderManager.get();
}
void SceneManager::setShaderPath(const std::string &path)
{
mShaderManager->setShaderPath(path);

@ -40,17 +40,22 @@ namespace Resource
SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager);
~SceneManager();
Shader::ShaderManager& getShaderManager();
/// Re-create shaders for this node, need to call this if texture stages or vertex color mode have changed.
void recreateShaders(osg::ref_ptr<osg::Node> node);
/// @see ShaderVisitor::setForceShaders
void setForceShaders(bool force);
bool getForceShaders() const;
/// @see ShaderVisitor::setClampLighting
void setClampLighting(bool clamp);
bool getClampLighting() const;
/// @see ShaderVisitor::setForcePerPixelLighting
void setForcePerPixelLighting(bool force);
bool getForcePerPixelLighting() const;
void setShaderPath(const std::string& path);

@ -1,21 +1,26 @@
#include "material.hpp"
#include <iostream>
#include <stdexcept>
#include <osg/Depth>
#include <osg/TexEnvCombine>
#include <osg/Texture2D>
#include <osg/TexMat>
#include <osg/Material>
#include <osg/TexEnvCombine>
#include <components/shader/shadermanager.hpp>
namespace Terrain
{
FixedFunctionTechnique::FixedFunctionTechnique(const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
FixedFunctionTechnique::FixedFunctionTechnique(const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize)
{
bool firstLayer = true;
int i=0;
for (std::vector<osg::ref_ptr<osg::Texture2D> >::const_iterator it = layers.begin(); it != layers.end(); ++it)
for (std::vector<TextureLayer>::const_iterator it = layers.begin(); it != layers.end(); ++it)
{
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
@ -53,7 +58,7 @@ namespace Terrain
}
// Add the actual layer texture multiplied by the alpha map.
osg::ref_ptr<osg::Texture2D> tex = *it;
osg::ref_ptr<osg::Texture2D> tex = it->mDiffuseMap;
stateset->setTextureAttributeAndModes(texunit, tex.get());
osg::ref_ptr<osg::TexMat> texMat (new osg::TexMat);
@ -66,9 +71,85 @@ namespace Terrain
}
}
Effect::Effect(const std::vector<osg::ref_ptr<osg::Texture2D> > &layers, const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps,
ShaderTechnique::ShaderTechnique(Shader::ShaderManager& shaderManager, bool forcePerPixelLighting, bool clampLighting, const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize)
{
bool firstLayer = true;
int i=0;
for (std::vector<TextureLayer>::const_iterator it = layers.begin(); it != layers.end(); ++it)
{
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
if (!firstLayer)
{
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
osg::ref_ptr<osg::Depth> depth (new osg::Depth);
depth->setFunction(osg::Depth::EQUAL);
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
}
int texunit = 0;
stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap);
osg::ref_ptr<osg::TexMat> texMat (new osg::TexMat);
texMat->setMatrix(osg::Matrix::scale(osg::Vec3f(layerTileSize,layerTileSize,1.f)));
stateset->setTextureAttributeAndModes(texunit, texMat, osg::StateAttribute::ON);
stateset->addUniform(new osg::Uniform("diffuseMap", texunit));
if(!firstLayer)
{
++texunit;
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(i++);
stateset->setTextureAttributeAndModes(texunit, blendmap.get());
// This is to map corner vertices directly to the center of a blendmap texel.
osg::Matrixf texMat;
float scale = (blendmapScale/(static_cast<float>(blendmapScale)+1.f));
texMat.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f));
texMat.preMultScale(osg::Vec3f(scale, scale, 1.f));
texMat.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f));
stateset->setTextureAttributeAndModes(texunit, new osg::TexMat(texMat));
stateset->addUniform(new osg::Uniform("blendMap", texunit));
}
if (it->mNormalMap)
{
++texunit;
stateset->setTextureAttributeAndModes(texunit, it->mNormalMap);
stateset->addUniform(new osg::Uniform("normalMap", texunit));
}
Shader::ShaderManager::DefineMap defineMap;
defineMap["forcePPL"] = forcePerPixelLighting ? "1" : "0";
defineMap["clamp"] = clampLighting ? "1" : "0";
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
defineMap["blendMap"] = !firstLayer ? "1" : "0";
defineMap["colorMode"] = "2";
osg::ref_ptr<osg::Shader> vertexShader = shaderManager.getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
if (!vertexShader || !fragmentShader)
throw std::runtime_error("Unable to create shader");
stateset->setAttributeAndModes(shaderManager.getProgram(vertexShader, fragmentShader));
firstLayer = false;
addPass(stateset);
}
}
Effect::Effect(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager& shaderManager, const std::vector<TextureLayer> &layers, const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps,
int blendmapScale, float layerTileSize)
: mLayers(layers)
: mShaderManager(shaderManager)
, mUseShaders(useShaders)
, mForcePerPixelLighting(forcePerPixelLighting)
, mClampLighting(clampLighting)
, mLayers(layers)
, mBlendmaps(blendmaps)
, mBlendmapScale(blendmapScale)
, mLayerTileSize(layerTileSize)
@ -82,7 +163,18 @@ namespace Terrain
bool Effect::define_techniques()
{
addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize));
try
{
if (mUseShaders)
addTechnique(new ShaderTechnique(mShaderManager, mForcePerPixelLighting, mClampLighting, mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize));
else
addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize));
}
catch (std::exception& e)
{
std::cerr << "Error: " << e.what() << std::endl;
addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize));
}
return true;
}

@ -11,14 +11,36 @@ namespace osg
class Texture2D;
}
namespace Shader
{
class ShaderManager;
}
namespace Terrain
{
struct TextureLayer
{
osg::ref_ptr<osg::Texture2D> mDiffuseMap;
osg::ref_ptr<osg::Texture2D> mNormalMap; // optional
};
class FixedFunctionTechnique : public osgFX::Technique
{
public:
FixedFunctionTechnique(
const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
protected:
virtual void define_passes() {}
};
class ShaderTechnique : public osgFX::Technique
{
public:
ShaderTechnique(Shader::ShaderManager& shaderManager, bool forcePerPixelLighting, bool clampLighting,
const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
protected:
@ -28,8 +50,8 @@ namespace Terrain
class Effect : public osgFX::Effect
{
public:
Effect(
const std::vector<osg::ref_ptr<osg::Texture2D> >& layers,
Effect(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager& shaderManager,
const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
virtual bool define_techniques();
@ -48,7 +70,11 @@ namespace Terrain
}
private:
std::vector<osg::ref_ptr<osg::Texture2D> > mLayers;
Shader::ShaderManager& mShaderManager;
bool mUseShaders;
bool mForcePerPixelLighting;
bool mClampLighting;
std::vector<TextureLayer> mLayers;
std::vector<osg::ref_ptr<osg::Texture2D> > mBlendmaps;
int mBlendmapScale;
float mLayerTileSize;

@ -148,11 +148,15 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
osg::ref_ptr<osg::Node> textureCompileDummy (new osg::Node);
unsigned int dummyTextureCounter = 0;
std::vector<osg::ref_ptr<osg::Texture2D> > layerTextures;
bool useShaders = mResourceSystem->getSceneManager()->getForceShaders();
if (!mResourceSystem->getSceneManager()->getClampLighting())
useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps
std::vector<TextureLayer> layers;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mTextureCacheMutex);
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
{
TextureLayer textureLayer;
osg::ref_ptr<osg::Texture2D> texture = mTextureCache[it->mDiffuseMap];
if (!texture)
{
@ -162,8 +166,26 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
mResourceSystem->getSceneManager()->applyFilterSettings(texture);
mTextureCache[it->mDiffuseMap] = texture;
}
layerTextures.push_back(texture);
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, layerTextures.back());
textureLayer.mDiffuseMap = texture;
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, texture);
if (!it->mNormalMap.empty())
{
texture = mTextureCache[it->mNormalMap];
if (!texture)
{
texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mNormalMap));
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
mResourceSystem->getSceneManager()->applyFilterSettings(texture);
mTextureCache[it->mNormalMap] = texture;
}
textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, texture);
textureLayer.mNormalMap = texture;
useShaders = true;
}
layers.push_back(textureLayer);
}
}
@ -185,7 +207,8 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
geometry->setTexCoordArray(i, mCache.getUVBuffer());
float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize;
osg::ref_ptr<osgFX::Effect> effect (new Terrain::Effect(layerTextures, blendmapTextures, blendmapScale, blendmapScale));
osg::ref_ptr<osgFX::Effect> effect (new Terrain::Effect(useShaders, mResourceSystem->getSceneManager()->getForcePerPixelLighting(), mResourceSystem->getSceneManager()->getClampLighting(),
mResourceSystem->getSceneManager()->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale));
effect->addCullCallback(new SceneUtil::LightListCallback);

@ -8,6 +8,8 @@ set(SHADER_FILES
water_nm.png
objects_vertex.glsl
objects_fragment.glsl
terrain_vertex.glsl
terrain_fragment.glsl
lighting.glsl
)

@ -0,0 +1,64 @@
#version 120
varying vec2 uv;
uniform sampler2D diffuseMap;
#if @normalMap
uniform sampler2D normalMap;
#endif
#if @blendMap
uniform sampler2D blendMap;
#endif
varying float depth;
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
#if !PER_PIXEL_LIGHTING
varying vec4 lighting;
#else
varying vec3 passViewPos;
varying vec3 passViewNormal;
varying vec4 passColor;
#endif
#include "lighting.glsl"
void main()
{
vec2 diffuseMapUV = (gl_TextureMatrix[0] * vec4(uv, 0.0, 1.0)).xy;
gl_FragData[0] = vec4(texture2D(diffuseMap, diffuseMapUV).xyz, 1.0);
#if @blendMap
vec2 blendMapUV = (gl_TextureMatrix[1] * vec4(uv, 0.0, 1.0)).xy;
gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a;
#endif
#if PER_PIXEL_LIGHTING
vec3 viewNormal = passViewNormal;
#endif
#if @normalMap
vec3 normalTex = texture2D(normalMap, diffuseMapUV).xyz;
vec3 viewTangent = (gl_ModelViewMatrix * vec4(1.0, 0.0, 0.0, 0.0)).xyz;
vec3 viewBinormal = normalize(cross(viewTangent, viewNormal));
viewTangent = normalize(cross(viewNormal, viewBinormal)); // note, now we need to re-cross to derive tangent again because it wasn't orthonormal
mat3 tbn = mat3(viewTangent, viewBinormal, viewNormal);
viewNormal = normalize(tbn * (normalTex * 2.0 - 1.0));
#endif
#if !PER_PIXEL_LIGHTING
gl_FragData[0] *= lighting;
#else
gl_FragData[0] *= doLighting(passViewPos, normalize(viewNormal), passColor);
#endif
float fogValue = clamp((depth - gl_Fog.start) * gl_Fog.scale, 0.0, 1.0);
gl_FragData[0].xyz = mix(gl_FragData[0].xyz, gl_Fog.color.xyz, fogValue);
}

@ -0,0 +1,36 @@
#version 120
varying vec2 uv;
varying float depth;
#define PER_PIXEL_LIGHTING (@normalMap || @forcePPL)
#if !PER_PIXEL_LIGHTING
varying vec4 lighting;
#else
varying vec3 passViewPos;
varying vec3 passViewNormal;
varying vec4 passColor;
#endif
#include "lighting.glsl"
void main(void)
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
depth = gl_Position.z;
vec4 viewPos = (gl_ModelViewMatrix * gl_Vertex);
gl_ClipVertex = viewPos;
vec3 viewNormal = normalize((gl_NormalMatrix * gl_Normal).xyz);
#if !PER_PIXEL_LIGHTING
lighting = doLighting(viewPos.xyz, viewNormal, gl_Color);
#else
passViewPos = viewPos.xyz;
passViewNormal = viewNormal;
passColor = gl_Color;
#endif
uv = gl_MultiTexCoord0.xy;
}
Loading…
Cancel
Save