Page 1 of 2

Adding Wind to Grass

Posted: Tue Jul 07, 2020 12:06 pm
by Nickak2003
I want to add wind to grass / custom vs code. Should I do this by modifying the pbs? I know there is an hlmsListener, but I am using terra, and am unsure if it's a good idea to modify it there. What I would like to do, I think, in the material file, is say like, wind=enabled. Then, conditionally, I would apply wind in the vertex shader. One question I have is, how do I check for wind=enabled to apply it or not in the vs.

Re: Adding Wind to Grass

Posted: Tue Jul 07, 2020 5:20 pm
by dark_sylinc
Hi!

See this post which explains how an existing Hlms implementation was slightly customized to apply custom shader code only to a specific set of Items, which is how Colibri does its rendering (it derives from HlmsUnlit and applies regular HlmsUnlit for regular Items, and applies a specific shader for objects that belong to Colibri)

Re: Adding Wind to Grass

Posted: Tue Jul 07, 2020 5:54 pm
by xrgo
I use this texture to simulate wind on everything (trees, flags, grass)
https://www.dropbox.com/s/75crodirnat7i ... e.dds?dl=0

you just have to customize the hlms and add something like this to the vertex shader:

Code: Select all

@property( syntax == glsl )
	uniform sampler3D texPerlinNoise;
@else
	Texture3D texPerlinNoise : register( t14 );
	SamplerState texPerlinNoiseSampler : register( s14 );
@end

Code: Select all

@property( syntax == glsl )
  	worldPos.xyz += ( texture( texPerlinNoise, 0.2*worldPos.xyz +
					float3(passBuf.globalTime*0.3) ).xyz - 0.2 ) * colour.x * passBuf.windIntensity;
@else
  	worldPos.xyz += ( texPerlinNoise.SampleLevel( texPerlinNoiseSampler,
					0.2*worldPos.xyz +
					float3( passBuf.globalTime*0.3, passBuf.globalTime*0.3, passBuf.globalTime*0.3 ), 0 ).xyz -
					float3( 0.2, 0.2, 0.2 ) ) * input.colour.x * passBuf.windIntensity;
@end
I use the vertex colour.x to control the intensity, and you also have to pass some timer to the passbuf

Re: Adding Wind to Grass

Posted: Wed Jul 08, 2020 2:14 pm
by Lax
Hi all,

great ideas!
@dark_sylinc, would it be possible to add such samples to Ogre Samples? Like Wind on environment, Ocean Sample etc. ?
This would be gread showcases of Ogre in action!

Best Regards
Lax

Re: Adding Wind to Grass

Posted: Wed Jul 08, 2020 2:57 pm
by xrgo
what I think would be really nice is a high-performance grass/foliage system with auto LODing

Re: Adding Wind to Grass

Posted: Wed Jul 08, 2020 3:47 pm
by paroj

Re: Adding Wind to Grass

Posted: Thu Jul 09, 2020 4:15 pm
by Nickak2003
xrgo wrote: Wed Jul 08, 2020 2:57 pm what I think would be really nice is a high-performance grass/foliage system with auto LODing
Well, that is where I'm heading. I've created a webpage where I'll be sharing my ogre code and experiences, at some point I could add them to the wiki or w/e. For now I'm working on learning and implementing stuff, then I'll create some fleshed out posts.

Re: Adding Wind to Grass

Posted: Fri Jul 10, 2020 4:40 pm
by Zonder
paroj wrote: Wed Jul 08, 2020 3:47 pm you could port https://github.com/OGRECave/ogre-pagedg ... edgeometry
That one is definitely worth porting but I wonder if somethings should use ogres own instancing now.

Re: Adding Wind to Grass

Posted: Fri Jul 10, 2020 5:24 pm
by dark_sylinc
Zonder wrote: Fri Jul 10, 2020 4:40 pm That one is definitely worth porting but I wonder if somethings should use ogres own instancing now.
Instancing is quite primitive/old-fashion when it comes to grass rendering.
By merely using SV_VertexID, it is possible to draw a grass blade in 2D, then randomly bend it a little to make it 3D.
This enables adaptive vertex-buffer-less high performance grass with very few instances.
The concept is very similar to hair rendering.
Codemasters does something similar (see page 17, blades are essentially 2D objects which are then bent in 3D).
Codemasters' implementation is a bit more complex because they use compute shaders to keep track of grass that has been stepped by the car tires (rather than being stateless).

The same can be repeated for medium-distance grass using SV_VertexID to generate the grass planes to draw grass using alpha discard (or alpha to coverage), rather than rendering the individual blades using vertex geometry (because at medium distance you'd otherwise process more than 1 vertex per pixel).
See Codemasters's PDF page 19.

At high distance, it's more efficient to use a terrain-like low tesselated object drawn on top of the terrain with low quality raymarching.

Trees would still have to be rendered traditionally though (although it may be possible to use vertex pulling and merge-instancing to increase the vertex_count / instance_count ratio)

Re: Adding Wind to Grass

Posted: Fri Jul 10, 2020 9:12 pm
by Nickak2003
Ok, I've made some progress and have setup a custom hlms for Wind. I am trying to get the noise texture sampled in the hlsl, but there is something wrong. Here's where I'm at:

Code: Select all

class HlmsWindListener : public Ogre::HlmsListener {

protected:
#if OGRE_DEBUG_MODE
	Ogre::SceneManager* mSceneManager;
#endif

	const float mWindFactor{ 2 };
	float mGlobalTime{ 0 };

public:
	HlmsWindListener() {}
	~HlmsWindListener() {}

	virtual Ogre::uint32 getPassBufferSize(const Ogre::CompositorShadowNode* shadowNode, bool casterPass,
		bool dualParaboloid, Ogre::SceneManager* sceneManager) const {

		unsigned int fogSize = (4 * 2) * 4; // float4 + float4 in bytes
		unsigned int windSize = (1) * 4;
		unsigned int timeSize = (1) * 4;
		return (!casterPass) ? fogSize + windSize + timeSize: 0u;

	}

	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++ = mWindFactor;
			
			mGlobalTime += 1;
			if (mGlobalTime >= 1000) mGlobalTime = 0;

			*passBufferPtr++ = mGlobalTime;
		}
		return passBufferPtr;
	}
};

class HlmsWind : public Ogre::HlmsPbs {

	HlmsWindListener mWindListener;
	Ogre::DescriptorSetTexture const* m_descriptorSet{ nullptr };

	Ogre::TextureGpu* mNoiseTexture{ nullptr };

	void calculateHashForPreCreate(Ogre::Renderable* renderable, Ogre::PiecesMap* inOutPieces) override {

		HlmsPbs::calculateHashForPreCreate(renderable, inOutPieces);

		setProperty("wind_enabled", 1);
	}



	void loadNoiseTexture(Ogre::SceneManager* manager) {
		Ogre::Image2 image;
		image.load("windNoise.png", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

		Ogre::TextureGpuManager* textureManager =
			manager->getDestinationRenderSystem()->getTextureGpuManager();

		mNoiseTexture = textureManager->createTexture(
			"WindNoise",
			Ogre::GpuPageOutStrategy::SaveToSystemRam,
			Ogre::TextureFlags::ManualTexture,
			Ogre::TextureTypes::Type2D);

		mNoiseTexture->setResolution(image.getWidth(), image.getHeight());
		mNoiseTexture->setPixelFormat(image.getPixelFormat());
	
		mNoiseTexture->scheduleTransitionTo(Ogre::GpuResidency::Resident);

		Ogre::StagingTexture* stagingTexture = textureManager->getStagingTexture(image.getWidth(),
			image.getHeight(),
			1u, 1u,
			image.getPixelFormat());
		stagingTexture->startMapRegion();
		Ogre::TextureBox texBox = stagingTexture->mapRegion(image.getWidth(), image.getHeight(), 1u, 1u,
			image.getPixelFormat());

		//for( uint8 mip=0; mip<numMipmaps; ++mip )
		texBox.copyFrom(image.getData(0));
		stagingTexture->stopMapRegion();
		stagingTexture->upload(texBox, mNoiseTexture, 0, 0, 0);
		textureManager->removeStagingTexture(stagingTexture);
		stagingTexture = 0;

		mNoiseTexture->notifyDataIsReady();
	}

public:
	HlmsWind(Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders)
		: Ogre::HlmsPbs(dataFolder, libraryFolders)
	{
		mType = Ogre::HLMS_USER0;
		mTypeName = "Wind";
		mTypeNameStr = "Wind";

		setListener(&mWindListener);

		mReservedTexSlots = 1u;



	}

	virtual ~HlmsWind() {
	//	destroyDescriptorSet();
	}


	void setupNoiseTexture(Ogre::SceneManager* manager) {
		loadNoiseTexture(manager);

		createDescriptorSet();
	}

	void notifyPropertiesMergedPreGenerationStep(void)
	{
		HlmsPbs::notifyPropertiesMergedPreGenerationStep();

		setTextureReg(Ogre::VertexShader, "WindNoise", 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;
	}

    void createDescriptorSet(void){

        destroyDescriptorSet();

        Ogre::DescriptorSetTexture descSet;

        descSet.mTextures.push_back(mNoiseTexture);
		
        descSet.mShaderTypeTexCount[Ogre::VertexShader] = 1u;

        Ogre::HlmsManager* hlmsManager = Ogre::Root::getSingleton().getHlmsManager();
        m_descriptorSet = hlmsManager->getDescriptorSetTexture(descSet);
    }
    //-----------------------------------------------------------------------------------
    void destroyDescriptorSet(void) {

        if (m_descriptorSet)
        {
            Ogre::HlmsManager* hlmsManager = Ogre::Root::getSingleton().getHlmsManager();
            hlmsManager->destroyDescriptorSetTexture(m_descriptorSet);
            m_descriptorSet = 0;
        }
    }

	
    Ogre::uint32 fillBuffersFor(const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable,
        bool casterPass, Ogre::uint32 lastCacheHash,
        Ogre::CommandBuffer* commandBuffer, bool isV1)
    {

        *commandBuffer->addCommand<Ogre::CbTextures>() =
            Ogre::CbTextures(0, std::numeric_limits<Ogre::uint16>::max(), m_descriptorSet);

        return Ogre::HlmsPbs::fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer, isV1);
    }
};

shader pieces:

Code: Select all

@piece( custom_vs_preTransform  )
@property(wind_enabled)
	
  	worldPos.xz +=  texPerlinNoise.SampleLevel( texPerlinNoiseSampler,
				worldPos.xyz* passBuf.globalTime,	0	 ).xyz * 100 ;			
@end
@end

Code: Select all

@piece( custom_vs_uniformDeclaration)
	Texture3D texPerlinNoise : register( t14 );
	SamplerState texPerlinNoiseSampler : register( s14 );
@end

/// Extra per-pass global data we need for applying our fog
@piece( custom_passBuffer )
    //Fog
    float4 fogParams;
    float4 fogColor;

	float windFactor;
	float globalTime;
@end
The grass does not move so I think the sampler is not set correctly

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 2:15 am
by xrgo
You're loading a 2d png image but in the shader is a Texture3D, if you're using the file I posted, I can confirm is a 3D texture (dds, not png)

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 2:47 am
by Nickak2003
Ok I changed it to the dds - still, texPerlinNoise.SampleLevel, always returns 0 i think.

I loaded the dds like so:

Code: Select all

	Ogre::TextureBox texBox = stagingTexture->mapRegion(image.getWidth(), image.getHeight(), image.getDepth(), image.getNumSlices(),
			image.getPixelFormat());
					texBox.copyFrom(image.getData(0));
			

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 3:22 am
by xrgo
I do it like this

Code: Select all

            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;
        mSamplerblockWrap = Ogre::Root::getSingleton().getHlmsManager()->getSamplerblock(samplerblock);

    mWindNoiseTexture = textureMgr->createOrRetrieveTexture(
                                    "windNoise.dds",
                                    Ogre::GpuPageOutStrategy::Discard,
                                    Ogre::TextureFlags::PrefersLoadingFromFileAsSRGB,
                                    Ogre::TextureTypes::Type3D,
                                    Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME );
    mWindNoiseTexture->scheduleTransitionTo( Ogre::GpuResidency::Resident );
on shaderCacheEntryCreated (use an slot that you're very sure its not used by any other textures)

Code: Select all

vsParams->setNamedConstant( "texPerlinNoise", 14 );
on hlmsTypeChanged

Code: Select all


    *commandBuffer->addCommand<Ogre::CbTexture>() = Ogre::CbTexture( 14, const_cast<Ogre::TextureGpu*>(mWindNoiseTexture), mSamplerblockWrap );

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 4:39 am
by Nickak2003
It's working, thanks, getting the sampler block setup properly was the problem and you helped me out. I was trying to set it like so, but does not work:

Code: Select all

		
		
		
	 Ogre::DescriptorSetTexture descSet;

        descSet.mTextures.push_back(mNoiseTexture);
	
        descSet.mShaderTypeTexCount[Ogre::VertexShader] = 1u;
		descSet.mShaderTypeTexCount[Ogre::PixelShader] = 0u;

        Ogre::HlmsManager* hlmsManager = Ogre::Root::getSingleton().getHlmsManager();
        m_descriptorSet = hlmsManager->getDescriptorSetTexture(descSet);
        
		Ogre::DescriptorSetSampler baseSet;
		baseSet.mSamplers.push_back(m_samplerBlock);
		baseSet.mShaderTypeSamplerCount[Ogre::VertexShader] = baseSet.mSamplers.size();
		m_descriptorSetSampler = mHlmsManager->getDescriptorSetSampler(baseSet);
		
		
		
	
		*commandBuffer->addCommand<Ogre::CbSamplers>() =
			Ogre::CbSamplers(14, m_descriptorSetSampler);

        *commandBuffer->addCommand<Ogre::CbTextures>() =
            Ogre::CbTextures(14, std::numeric_limits<Ogre::uint16>::max(),
                m_descriptorSet);
		
		

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 9:12 am
by Lax
Nice!

A wiki page would be great!

I would also like to use this feature, but have no idea how to get the code fragments working in a whole grass wind scenario.

Best Regards
Lax

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 5:16 pm
by Nickak2003
Here's my writeup on it:
https://spotcpp.com/creating-a-custom-h ... d-and-fog/
*updated to remove windFactor texture

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 5:44 pm
by Lax
Cool thanks alot! :D

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 7:03 pm
by Lax
Hi Nickak2003,

just a small question: Whats this for Grass1.mesh file? I cannot load that mesh properly. The used mesh serialized is:

Code: Select all

MeshSerializer_v2.1 R2
Where do I get this one from?? Ogre uses version 1.8.

Best Regards
Lax

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 7:08 pm
by Nickak2003
That is the latest mesh serializer that is built from ogre-next.

OgreMeshTool Cerberus (2.2.3)

You cant load it? in Ogre-next?

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 7:09 pm
by Nickak2003
I added the .xml to the git

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 10:58 pm
by Lax
Sorry my fault. The grass has been loaded correctly, but v2 object have renderqueue id 10 whereas my floor has 110 (v1) and the grass was not visible.
Now I must find out, why the textures are not loaded for the grass. I'm getting no warning, but the grass is white and it is not deforming (no wind).
I found out, that if I move the grass via translation gizmo, it starts deforming. That is strange...
What also does not work is fog...

Do you have fog working?

Best Regards
Lax

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 11:03 pm
by Nickak2003
This is how I create the grass

Code: Select all

Ogre::Item* item = graphics.getSceneManager()->createItem("Grass1.mesh", Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, Ogre::SCENE_STATIC);
Ogre::SceneNode* sn = graphics.getSceneManager()->getRootSceneNode()->createChildSceneNode(Ogre::SCENE_STATIC, position, orientation);
sn->scale(Ogre::Vector3(0.5, 0.5, 0.5)*sY);
sn->attachObject(item);
item->setRenderQueueGroup(254);
my fog works



did you put the shader files in hlms/wind/any? Is ogre finding this path when you load the HlmsWind ?
are you using direct x? the shaders are in hlsl.
You also have to call scenemanager->setFog

Code: Select all

		sceneManager->setFog(Ogre::FOG_LINEAR, Ogre::ColourValue(1, 0, 1),NULL,10 , 300.0f);

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 11:05 pm
by xrgo
Lax wrote: Sat Jul 11, 2020 10:58 pm I found out, that if I move the grass via translation gizmo, it starts deforming. That is strange...
that's a symptom of a bad passBuf alignment, make sure *passBufferPtr++ data fits perfectly with what you have in the shaders and of what you've added to custom_passBuffer. You can easily spot misalignment with renderdoc, set some very recognizable values to *passBufferPtr++ like 1,2,3,69,420... and in renderdoc see if it fits what you expect on your shader.. for example you might set the fogParams.x as 69, but you get the 69 value on the .y

this will also explain why you get no texture... you probably are getting texture but its being overwriten by some random color because maybe the fog intensity is 1 (because of a misalignment)

Re: Adding Wind to Grass

Posted: Sat Jul 11, 2020 11:10 pm
by Nickak2003
xrgo wrote: Sat Jul 11, 2020 11:05 pm
Lax wrote: Sat Jul 11, 2020 10:58 pm I found out, that if I move the grass via translation gizmo, it starts deforming. That is strange...
that's a symptom of a bad passBuf alignment, make sure *passBufferPtr++ data fits perfectly with what you have in the shaders and of what you've added to custom_passBuffer. You can easily spot misalignment with renderdoc, set some very recognizable values to *passBufferPtr++ like 1,2,3,69,420... and in renderdoc see if it fits what you expect on your shader.. for example you might set the fogParams.x as 69, but you get the 69 value on the .y

this will also explain why you get no texture... you probably are getting texture but its being overwriten by some random color because maybe the fog intensity is 1 (because of a misalignment)
Why would it be misaligned though?

Re: Adding Wind to Grass

Posted: Sun Jul 12, 2020 6:18 am
by xrgo
lo siento, might not be the correct word
I mean something like this:

Code: Select all

*passBufferPtr++ = fogStart
*passBufferPtr++ = fogEnd
*passBufferPtr++ = fogColor.r
*passBufferPtr++ = fogColor.g
*passBufferPtr++ = fogColor.b
*passBufferPtr++ = globalTimer

Code: Select all

@piece( custom_passBuffer )
float fogStart
float fogEnd
float globalTimer <<< error!! this should be after fogcolor
float3 fogColor
so (in this example) the passBuf.globalTimer in the shader will receive the fogColor.r value (and you'll see no animation, since the value is probably static).. and the passBuf.fogColor.x will be fogColor.g, and the passBuf.fogColor.y will be fogColor.b, and the passBuf.fogColor.z will be globalTimer

you can check the values on renderdoc as I mentioned

Edit: also (if everything looks aligned) I think that this buffer likes float4s (am I right dark_sylinc?), try adding two paddings so you have float4, float4, and float,float,float,float (its like a float4)

Code: Select all

@piece( custom_passBuffer )
    //Fog
    float4 fogParams;
    float4 fogColor;

	float windFactor;
	float globalTime;
	float padding0;
	float padding1;
@end
also in the c++ counterpart
and assign any value, since you'll not be using them