- Ogre3D 2.0
- shader templates with hlsl
- custom HLMS
- grass rendering
There are a three different shader template files:
- 500.Structs_piece_vs_piece_ps.any
- 800.PixelShader_piece_ps.any
- 800.VertexShader_piece_vs.any
500.Structs_piece_vs_piece_ps.any
@piece( custom_passBuffer )
//Fog
float4 fogParams;
float4 fogColor;
//wind
float windStrength;
float globalTime;
@end
800.PixelShader_piece_ps.any
@piece(custom_ps_posExecution)
@property(!hlms_shadowcaster)
//Linear Fog
float fogDistance = length(inPs.pos);
float fogFactor = 1.0 - saturate((passBuf.fogParams.y - fogDistance) / (passBuf.fogParams.y - passBuf.fogParams.x));
float3 fogColor = float3(passBuf.fogColor.xyz);
outPs_colour0.xyz = lerp(finalColour, fogColor, fogFactor);
@end
@end
800.VertexShader_piece_vs.any
@piece( custom_vs_uniformDeclaration)
Texture3D texPerlinNoise : register( t14 );
SamplerState texPerlinNoiseSampler : register( s14);
@end
@piece( custom_vs_preTransform )
@property(wind_enabled)
float windFactor = 1.0 - input.uv0.y;
float random = texPerlinNoise.SampleLevel( texPerlinNoiseSampler,
0.2*worldPos.xyz +
float3( 0.3, 0.3, 0.3 ) * passBuf.globalTime, 0 ).xyz - float3( 0.2, 0.2, 0.2 );
worldPos.xyz += random * windFactor * passBuf.windStrength;
@end
@end
The HLMS uses a HLMS listener to pass the fog and wind parameters to the shaders, it also manages one texture and one sampler. The first texture is the perlin noise texture, used to randomize the geometry.
class HlmsWindListener : public Ogre::HlmsListener {
float mWindStrength{ 0.5 };
float mGlobalTime{ 0 };
public:
HlmsWindListener() = default;
virtual ~HlmsWindListener() = default;
void setTime(float time) {
mGlobalTime = time;
}
void addTime(float time) {
mGlobalTime += time;
}
virtual Ogre::uint32 getPassBufferSize(const Ogre::CompositorShadowNode* shadowNode, bool casterPass,
bool dualParaboloid, Ogre::SceneManager* sceneManager) const {
Ogre::uint32 size = 0;
if (!casterPass) {
unsigned int fogSize = sizeof(float) * 4 * 2; // float4 + float4 in bytes
unsigned int windSize = sizeof(float);
unsigned int timeSize = sizeof(float);
size += fogSize + windSize + timeSize;
}
return size;
}
virtual float* preparePassBuffer(const Ogre::CompositorShadowNode* shadowNode, bool casterPass,
bool dualParaboloid, Ogre::SceneManager* sceneManager,
float* passBufferPtr) {
if (!casterPass )
{
//Linear Fog parameters
*passBufferPtr++ = sceneManager->getFogStart();
*passBufferPtr++ = sceneManager->getFogEnd();
*passBufferPtr++ = 0.0;
*passBufferPtr++ = 0.0f;
*passBufferPtr++ = sceneManager->getFogColour().r;
*passBufferPtr++ = sceneManager->getFogColour().g;
*passBufferPtr++ = sceneManager->getFogColour().b;
*passBufferPtr++ = 1.0f;
*passBufferPtr++ = mWindStrength;
*passBufferPtr++ = mGlobalTime;
}
return passBufferPtr;
}
};
class HlmsWind : public Ogre::HlmsPbs {
HlmsWindListener mWindListener;
Ogre::HlmsSamplerblock const* mNoiseSamplerBlock{ nullptr };
Ogre::TextureGpu* mNoiseTexture{ nullptr };
void calculateHashForPreCreate(Ogre::Renderable* renderable, Ogre::PiecesMap* inOutPieces) override {
HlmsPbs::calculateHashForPreCreate(renderable, inOutPieces);
setProperty("wind_enabled", 1);
}
void loadTexturesAndSamplers(Ogre::SceneManager* manager) {
Ogre::TextureGpuManager* textureManager =
manager->getDestinationRenderSystem()->getTextureGpuManager();
Ogre::HlmsSamplerblock samplerblock;
samplerblock.mU = Ogre::TextureAddressingMode::TAM_WRAP;
samplerblock.mV = Ogre::TextureAddressingMode::TAM_WRAP;
samplerblock.mW = Ogre::TextureAddressingMode::TAM_WRAP;
samplerblock.mMaxAnisotropy = 8;
samplerblock.mMagFilter = Ogre::FO_ANISOTROPIC;
mNoiseSamplerBlock = Ogre::Root::getSingleton().getHlmsManager()->getSamplerblock(samplerblock);
mNoiseTexture = textureManager->createOrRetrieveTexture(
"windNoise.dds",
Ogre::GpuPageOutStrategy::Discard,
Ogre::TextureFlags::PrefersLoadingFromFileAsSRGB,
Ogre::TextureTypes::Type3D,
Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
mNoiseTexture->scheduleTransitionTo(Ogre::GpuResidency::Resident);
}
public:
HlmsWind(Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders)
: Ogre::HlmsPbs(dataFolder, libraryFolders)
{
mType = Ogre::HLMS_USER0;
mTypeName = "Wind";
mTypeNameStr = "Wind";
setListener(&mWindListener);
mReservedTexSlots = 2u;
}
virtual ~HlmsWind() = default;
void addTime(float time) {
mWindListener.addTime(time);
}
void setup(Ogre::SceneManager* manager) {
loadTexturesAndSamplers(manager);
}
void shutdown(Ogre::SceneManager* manager) {
Ogre::TextureGpuManager* textureManager = manager->getDestinationRenderSystem()->getTextureGpuManager();
textureManager->destroyTexture(mNoiseTexture);
mNoiseTexture = nullptr;
Ogre::Root::getSingleton().getHlmsManager()->destroySamplerblock(mNoiseSamplerBlock);
mNoiseSamplerBlock = nullptr;
}
void notifyPropertiesMergedPreGenerationStep(void){
HlmsPbs::notifyPropertiesMergedPreGenerationStep();
setTextureReg(Ogre::VertexShader, "texPerlinNoise", 14);
}
static void getDefaultPaths(Ogre::String& outDataFolderPath, Ogre::StringVector& outLibraryFoldersPaths) {
//We need to know what RenderSystem is currently in use, as the
//name of the compatible shading language is part of the path
Ogre::RenderSystem* renderSystem = Ogre::Root::getSingleton().getRenderSystem();
Ogre::String shaderSyntax = "GLSL";
if (renderSystem->getName() == "Direct3D11 Rendering Subsystem")
shaderSyntax = "HLSL";
else if (renderSystem->getName() == "Metal Rendering Subsystem")
shaderSyntax = "Metal";
//Fill the library folder paths with the relevant folders
outLibraryFoldersPaths.clear();
outLibraryFoldersPaths.push_back("Hlms/Common/" + shaderSyntax);
outLibraryFoldersPaths.push_back("Hlms/Common/Any");
outLibraryFoldersPaths.push_back("Hlms/Pbs/Any");
outLibraryFoldersPaths.push_back("Hlms/Pbs/Any/Main");
outLibraryFoldersPaths.push_back("Hlms/Wind/Any");
//Fill the data folder path
outDataFolderPath = "Hlms/pbs/" + shaderSyntax;
}
Ogre::uint32 fillBuffersForV1(const Ogre::HlmsCache* cache,
const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer)
{
return fillBuffersFor(cache, queuedRenderable, casterPass,
lastCacheHash, commandBuffer, true);
}
//-----------------------------------------------------------------------------------
Ogre::uint32 fillBuffersForV2(const Ogre::HlmsCache* cache,
const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer)
{
return fillBuffersFor(cache, queuedRenderable, casterPass,
lastCacheHash, commandBuffer, false);
}
//-----------------------------------------------------------------------------------
Ogre::uint32 fillBuffersFor(const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable,
bool casterPass, Ogre::uint32 lastCacheHash,
Ogre::CommandBuffer* commandBuffer, bool isV1)
{
*commandBuffer->addCommand<Ogre::CbTexture>() = Ogre::CbTexture(14, const_cast<Ogre::TextureGpu*>(mNoiseTexture), mNoiseSamplerBlock);
return Ogre::HlmsPbs::fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer, isV1);
}
};
Note getDefaultPaths, “Hlms/Wind/Any”, is used as the shader script folder.
So to setup the HLMS, it first needs to be registered like any other HLMS.
Ogre::String shaderSyntax = "GLSL";
if (renderSystem->getName() == "OpenGL ES 2.x Rendering Subsystem")
shaderSyntax = "GLSLES";
if (renderSystem->getName() == "Direct3D11 Rendering Subsystem")
shaderSyntax = "HLSL";
else if (renderSystem->getName() == "Metal Rendering Subsystem")
shaderSyntax = "Metal";
auto createHlms = [&]<typename T>(T * &hlms) {
Ogre::String mainFolderPath;
Ogre::StringVector libraryFolderPaths;
T::getDefaultPaths(mainFolderPath, libraryFolderPaths);
Ogre::ArchiveVec archive;
for (Ogre::String str : libraryFolderPaths) {
Ogre::Archive* archiveLibrary = Ogre::ArchiveManager::getSingleton().load(rootHlmsFolder + str, "FileSystem", true);
archive.push_back(archiveLibrary);
}
Ogre::Archive* archiveUnlit = Ogre::ArchiveManager::getSingleton().load(rootHlmsFolder + mainFolderPath, "FileSystem", true);
hlms = OGRE_NEW T(archiveUnlit, &archive);
Ogre::Root::getSingleton().getHlmsManager()->registerHlms(hlms);
};
Ogre::HlmsUnlit* hlmsUnlit = nullptr;
createHlms(hlmsUnlit);
Ogre::HlmsPbs* hlmsPbs = nullptr;
createHlms(hlmsPbs);
HlmsWind* hlmsWind = nullptr;
createHlms(hlmsWind);
Once HlmsWind has been registered, you need to call its setup like so:
HlmsWind* hlmsWind = dynamic_cast<HlmsWind*>(Ogre::Root::getSingleton().getHlmsManager()->getHlms(Ogre::HLMS_USER0));
hlmsWind->setup(mSceneManager);
Note: HLMS_USER0 was set in the HlmsWind ctor.
Later to update the time inside the sim loop:
HlmsWind* hlmsWind = dynamic_cast<HlmsWind*>(Ogre::Root::getSingleton().getHlmsManager()->getHlms(Ogre::HLMS_USER0));
hlmsWind->addTime(timeSinceLastFrameInMS);
The material file must reference HlmsWind.
hlms Grass1 Wind
{
scene_blend alpha_blend
depth_write off
diffuse_map grass1_diffuse.png
specular_map grass1_diffuse.png
normal_map grass1_normal.png
}
Complete Source and Resources: