Create Material Ogre-Next

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Create Material Ogre-Next

Post by Lax »

Hi,

I would like to handle split screens dynamically. So I would like to create in C++ a material file, and create glsl/hlsl shaders with a dynamic count of textures and geomData and write the shaders to disc.

The shaders do already work. But I'm struggling in attaching the shaders programmatically to a generated material.

This is the already working material file:

Code: Select all

fragment_program DynamicSplitTexturesShader_GLSL glsl
{
    source DynamicSplitTexturesShader.glsl
    default_params
    {
        param_named tex1 int 0
        param_named tex2 int 1
    }
}

fragment_program DynamicSplitTexturesShader_HLSL hlsl
{
    source DynamicSplitTexturesShader.hlsl
    entry_point main
    target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
    default_params
    {
        param_named tex1 int 0
        param_named tex2 int 1
    }
}

// Unified fragment program
fragment_program DynamicSplitTexturesShader unified
{
    delegate DynamicSplitTexturesShader_GLSL
    delegate DynamicSplitTexturesShader_HLSL
}

// Material definition
material DynamicSplitTexturesMaterial
{
    technique
    {
        pass
        {
            depth_check off
            depth_write off
            cull_hardware none

        vertex_program_ref Ogre/Compositor/Quad_vs
        {
        }

        fragment_program_ref DynamicSplitTexturesShader
        {
        }

        texture_unit tex1
        {
            filtering none
            tex_address_mode clamp
        }
        texture_unit tex2
        {
            filtering none
            tex_address_mode clamp
        }
    }
}
}

In C++ I already have things ready, but I need to set e.g.

Code: Select all

 depth_check off
 depth_write off
 cull_hardware none
 

In C++ I do:

Code: Select all

Ogre::MaterialPtr material = Ogre::MaterialManager::getSingletonPtr()->create(materialName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	Ogre::Technique* technique = material->createTechnique();
	Ogre::Pass* pass = technique->createPass();

	Ogre::HlmsMacroblock macroblock;
	macroblock.mDepthWrite = false;
	macroblock.mDepthCheck = false;
	macroblock.mCullMode = Ogre::CullingMode::CULL_NONE;

	pass->setMacroblock(macroblock);

Or for this part:

Code: Select all

texture_unit tex1
            {
                filtering none
                tex_address_mode clamp
            }

I do:

Code: Select all

		for (size_t i = 0; i < textureNames.size(); ++i)
		{
			Ogre::String paramName = "tex" + Ogre::StringConverter::toString(i + 1); // Match shader uniform
			Ogre::TextureUnitState* texUnit = pass->createTextureUnitState(paramName);
			Ogre::HlmsSamplerblock samplerblock;
			samplerblock.mU = Ogre::TAM_CLAMP;
			samplerblock.mV = Ogre::TAM_CLAMP;
			samplerblock.mW = Ogre::TAM_CLAMP;

		samplerblock.mMinFilter = Ogre::FO_NONE;
		samplerblock.mMagFilter = Ogre::FO_NONE;
		samplerblock.mMipFilter = Ogre::FO_NONE;

		texUnit->setSamplerblock(samplerblock);
	}

But I get always the error message:

Code: Select all

16:56:57: OGRE EXCEPTION(3:RenderingAPIException): Fixed Function pipeline is no longer allowed nor supported. The material DynamicSplitMaterial must use shaders in HlmsLowLevel::calculateHashFor at E:\Ogre2.2SDK\OgreMain\src\OgreHlmsLowLevel.cpp (line 173)
16:56:57: OGRE EXCEPTION(3:RenderingAPIException): Fixed Function pipeline is no longer allowed nor supported. The material DynamicSplitMaterial must use shaders in HlmsLowLevel::calculateHashFor at E:\Ogre2.2SDK\OgreMain\src\OgreHlmsLowLevel.cpp (line 173)
16:56:57: Couldn't apply datablock '[Value 0x0000012f]' to this renderable. Using default one. Check previous log messages to see if there's more information.
16:56:57: OGRE EXCEPTION(9:UnimplementedException): Trying to use slow-path on a desktop implementation. Change the RenderQueue settings. in HlmsPbs::fillBuffersFor at E:\Ogre2.2SDK\Components\Hlms\Pbs\src\OgreHlmsPbs.cpp (line 2970)

But for what?? I thought using the Ogre::Material is always for v1. I have no Idea for which object shall I change the render queue?

The next question would be:

  • I also generate the shader at runtime. So I need to add textures programatically. In the material file, it does work this way:

Code: Select all

fragment_program DynamicSplitTexturesShader_HLSL hlsl
{
    source DynamicSplitTexturesShader.hlsl
    entry_point main
    target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
    default_params
    {
        param_named tex1 int 0
        param_named tex2 int 1
    }
}

But how can that be written in C++? How can I set e.g. both textures there in C++?

I already tried this code, but I think it will not work:

Code: Select all

	Ogre::GpuProgramPtr SplitScreenComponent::loadDynamicShader(const Ogre::String& shaderFolder, const Ogre::String& shaderName, const std::vector<Ogre::String>& textureNames, const Ogre::String& shaderSyntax)
	{
		Ogre::GpuProgramPtr shaderProgram;

	try
	{
		if (shaderSyntax == "GLSL" || shaderSyntax == "GLSLES")
		{
			shaderProgram = Ogre::HighLevelGpuProgramManager::getSingleton().createProgram(shaderName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "glsl", Ogre::GPT_FRAGMENT_PROGRAM);
		}
		else if (shaderSyntax == "HLSL")
		{
			shaderProgram = Ogre::HighLevelGpuProgramManager::getSingleton().createProgram(shaderName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "hlsl", Ogre::GPT_FRAGMENT_PROGRAM);
			shaderProgram->setParameter("entry_point", "main");
			// "ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3"
			shaderProgram->setParameter("target", "ps_5_0");

		}
		else if (shaderSyntax == "Metal")
		{
			shaderProgram = Ogre::HighLevelGpuProgramManager::getSingleton().createProgram(
				shaderName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
				"metal", Ogre::GPT_FRAGMENT_PROGRAM);
			shaderProgram->setParameter("entry_point", "main");
		}
		else
		{
			OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Unsupported shader syntax: " + shaderSyntax, "loadDynamicShader");
		}


		//default_params
		//{
		//	param_named tex1 int 0 // First texture
		//	param_named tex2 int 1 // Second texture
		//}

		// Bind textures dynamically
#if 1
			for (size_t i = 0; i < textureNames.size(); ++i)
			{
				Ogre::String paramName = "tex" + Ogre::StringConverter::toString(i + 1); // Match shader uniform
				shaderProgram->setParameter(paramName, Ogre::StringConverter::toString(i));
			}
#endif

		Ogre::String shaderFileName = shaderName + "." + (shaderSyntax == "HLSL" ? "hlsl" : "glsl");
		shaderProgram->setSourceFile(shaderFolder + "/" + shaderFileName);
		shaderProgram->load();

		Ogre::LogManager::getSingleton().logMessage("[loadDynamicShader] Shader successfully loaded: " + shaderName);
	}
	catch (const Ogre::Exception& e)
	{
		Ogre::LogManager::getSingleton().logMessage("[loadDynamicShader] ERROR: " + e.getFullDescription(), Ogre::LML_CRITICAL);
		return shaderProgram;
	}

	return shaderProgram;
}

void SplitScreenComponent::createDynamicMaterial(const Ogre::String& materialName, const Ogre::String& shaderFolder, const Ogre::String& shaderName, const std::vector<Ogre::String>& textureNames,
	const std::vector<Ogre::Vector4>& geometryVectors, const Ogre::String& shaderSyntax)
{
	Ogre::MaterialPtr material = Ogre::MaterialManager::getSingletonPtr()->create(materialName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	Ogre::Technique* technique = material->createTechnique();
	Ogre::Pass* pass = technique->createPass();

#if 0
		Ogre::HlmsMacroblock macroblock;
		macroblock.mDepthWrite = false;
		macroblock.mDepthCheck = false;
		macroblock.mCullMode = Ogre::CullingMode::CULL_NONE;

	pass->setMacroblock(macroblock);
#endif

	pass->setVertexProgram("Ogre/Compositor/Quad_vs");

	Ogre::GpuProgramPtr fragmentShader;
#if 0
		// Set shader
		Ogre::GpuProgramPtr fragmentShader = Ogre::HighLevelGpuProgramManager::getSingleton().createProgram(shaderName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
			shaderSyntax == "HLSL" ? "hlsl" : "glsl", Ogre::GPT_FRAGMENT_PROGRAM);

	Ogre::String shaderFileName = shaderName + "." + (shaderSyntax == "HLSL" ? "hlsl" : "glsl");
	fragmentShader->setSourceFile(shaderFolder + "/" + shaderFileName);
	fragmentShader->load();

	pass->setFragmentProgram(fragmentShader->getName());
#else
		fragmentShader = this->loadDynamicShader(shaderFolder, shaderName, textureNames, shaderSyntax);
		pass->setFragmentProgram(fragmentShader->getName());
#endif

	for (size_t i = 0; i < textureNames.size(); ++i)
	{
		Ogre::String paramName = "tex" + Ogre::StringConverter::toString(i + 1); // Match shader uniform
		Ogre::TextureUnitState* texUnit = pass->createTextureUnitState(paramName);

#if 0
			Ogre::HlmsSamplerblock samplerblock;
			samplerblock.mU = Ogre::TAM_CLAMP;
			samplerblock.mV = Ogre::TAM_CLAMP;
			samplerblock.mW = Ogre::TAM_CLAMP;

		samplerblock.mMinFilter = Ogre::FO_NONE;
		samplerblock.mMagFilter = Ogre::FO_NONE;
		samplerblock.mMipFilter = Ogre::FO_NONE;

		texUnit->setSamplerblock(samplerblock);
#endif
		}

	// material->load();
	material->compile();

	// Loads dynamic shader parameters
	Ogre::GpuProgramParametersSharedPtr fragmentParams = pass->getFragmentProgramParameters();

	//// Bind textures dynamically
#if 0
		for (size_t i = 0; i < textureNames.size(); ++i)
		{
			Ogre::String paramName = "tex" + Ogre::StringConverter::toString(i + 1); // Match shader uniform
			if (fragmentParams->_findNamedConstantDefinition(paramName, false))
			{
				fragmentParams->setNamedConstant(paramName, static_cast<int>(i));
			}
			else
			{
				Ogre::LogManager::getSingleton().logMessage("[SplitScreenComponent]: Shader uniform not found: " + paramName, Ogre::LML_CRITICAL);
			}
		}
#endif

	// Bind geometry vectors dynamically
	for (size_t i = 0; i < geometryVectors.size(); ++i)
	{
		Ogre::String paramName = "geomData" + Ogre::StringConverter::toString(i + 1); // Match shader uniform
		if (fragmentParams->_findNamedConstantDefinition(paramName, false))
		{
			fragmentParams->setNamedConstant(paramName, geometryVectors[i]);
		}
		else
		{
			Ogre::LogManager::getSingleton().logMessage("[SplitScreenComponent]: Shader uniform not found: " + paramName, Ogre::LML_CRITICAL);
		}
	}
}

I hope somebody has an idea, how to answer those 2 questions.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Create Material Ogre-Next

Post by Lax »

It must have something todo, that I create the material and shaders at runtime:

14:02:01: [WorkspaceBaseComponent] Creating workspace: NOWAWorkspace2087932612
14:02:02: [WorkspaceBaseComponent] Creating workspace: NOWAWorkspace2883427645
14:02:03: Generated dynamic shader: DynamicSplitTexturesShader_ps.glsl
14:02:03: Generated dynamic shader: DynamicSplitTexturesShader_ps.hlsl
14:02:03: Generated dynamic material: ../../media/NOWA/Scripts/DynamicSplitMaterial.material
14:02:26: OGRE EXCEPTION(3:RenderingAPIException): Fixed Function pipeline is no longer allowed nor supported. The material DynamicSplitMaterial must use shaders in HlmsLowLevel::calculateHashFor at E:\Ogre2.2SDK\OgreMain\src\OgreHlmsLowLevel.cpp (line 173)
14:02:26: OGRE EXCEPTION(3:RenderingAPIException): Fixed Function pipeline is no longer allowed nor supported. The material DynamicSplitMaterial must use shaders in HlmsLowLevel::calculateHashFor at E:\Ogre2.2SDK\OgreMain\src\OgreHlmsLowLevel.cpp (line 173)
14:02:26: Couldn't apply datablock '[Value 0x0000012d]' to this renderable. Using default one. Check previous log messages to see if there's more information.

If I create the the material and shaders at runtime (write to disc). Stop the application and restart it, so that the material and shaders are loaded by ogre at an early stage. Everything does work fine.

So what commands do I miss?

I call:

Code: Select all

Ogre::MaterialManager::getSingletonPtr()->createOrRetrieve(materialName, "General");
Ogre::ResourceGroupManager::getSingletonPtr()->declareResource(materialName, "Material", "General");

Ogre::MaterialPtr splitMaterial = Ogre::MaterialManager::getSingletonPtr()->getByName(materialName);
splitMaterial->compile();
splitMaterial->reload();

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Create Material Ogre-Next

Post by Lax »

Ok, after a lot of struggeling. I found out, that its not really possible to everything totally at runtime. So I came up with another solution.

I create the material and shader for up to 4 players:

Code: Select all

// GLSL fragment program
fragment_program DynamicSplitTexturesShader_ps_GLSL glsl
{
    source DynamicSplitTexturesShader_ps.glsl
    default_params
    {
        param_named tex1 int 0
        param_named tex2 int 1
		param_named tex3 int 2
        param_named tex4 int 3
    }
}

// HLSL fragment program
fragment_program DynamicSplitTexturesShader_ps_HLSL hlsl
{
    source DynamicSplitTexturesShader_ps.hlsl
    entry_point main
    target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
    default_params
    {
        param_named tex1 int 0
        param_named tex2 int 1
		param_named tex3 int 2
        param_named tex4 int 3
    }
}

// Unified fragment program
fragment_program DynamicSplitTexturesShader_ps unified
{
    delegate DynamicSplitTexturesShader_ps_GLSL
    delegate DynamicSplitTexturesShader_ps_HLSL
}

// Material definition
material DynamicSplitMaterial
{
    technique
    {
        pass
        {
            depth_check off
            depth_write off
            cull_hardware none

        vertex_program_ref Ogre/Compositor/Quad_vs
        {
        }

        fragment_program_ref DynamicSplitTexturesShader_ps
        {
            param_named geomData1 float4 0.5 0 0.5 1
            param_named geomData2 float4 0 0 0.5 1
			param_named geomData3 float4 0 0 0 0
            param_named geomData4 float4 0 0 0 0
        }

        texture_unit tex1
        {
            filtering none
            tex_address_mode clamp
        }
        texture_unit tex2
        {
            filtering none
            tex_address_mode clamp
        }
		
		texture_unit tex3
        {
            filtering none
            tex_address_mode clamp
        }
        texture_unit tex4
        {
            filtering none
            tex_address_mode clamp
        }
    }
}
}

Shader:

Code: Select all

struct PS_INPUT
{
    float2 uv0 : TEXCOORD0;
};

Texture2D<float4> tex1 : register(t0);
SamplerState samplerState1 : register(s0);
Texture2D<float4> tex2 : register(t1);
SamplerState samplerState2 : register(s1);
Texture2D<float4> tex3 : register(t2);
SamplerState samplerState3 : register(s2);
Texture2D<float4> tex4 : register(t3);
SamplerState samplerState4 : register(s3);
float4 main(PS_INPUT inPs, uniform float4 geomData1, uniform float4 geomData2, uniform float4 geomData3, uniform float4 geomData4) : SV_Target
{
    float2 uv = inPs.uv0;
	
if (any(geomData1 != float4(0.0, 0.0, 0.0, 0.0))) {
	if (uv.x >= geomData1.x && uv.x < (geomData1.x + geomData1.z) &&
		uv.y >= geomData1.y && uv.y < (geomData1.y + geomData1.w)) {
		float2 adjustedUV = (uv - float2(geomData1.x, geomData1.y)) / float2(geomData1.z, geomData1.w);
		return tex1.Sample(samplerState1, adjustedUV);
	}
}

if (any(geomData2 != float4(0.0, 0.0, 0.0, 0.0))) {
	if (uv.x >= geomData2.x && uv.x < (geomData2.x + geomData2.z) &&
		uv.y >= geomData2.y && uv.y < (geomData2.y + geomData2.w)) {
		float2 adjustedUV = (uv - float2(geomData2.x, geomData2.y)) / float2(geomData2.z, geomData2.w);
		return tex2.Sample(samplerState2, adjustedUV);
	}
}

if (any(geomData3 != float4(0.0, 0.0, 0.0, 0.0))) {
	if (uv.x >= geomData3.x && uv.x < (geomData3.x + geomData3.z) &&
		uv.y >= geomData3.y && uv.y < (geomData3.y + geomData3.w)) {
		float2 adjustedUV = (uv - float2(geomData3.x, geomData3.y)) / float2(geomData3.z, geomData3.w);
		return tex3.Sample(samplerState3, adjustedUV);
	}
}

if (any(geomData4 != float4(0.0, 0.0, 0.0, 0.0))) {
	if (uv.x >= geomData4.x && uv.x < (geomData4.x + geomData4.z) &&
		uv.y >= geomData4.y && uv.y < (geomData4.y + geomData4.w)) {
		float2 adjustedUV = (uv - float2(geomData4.x, geomData4.y)) / float2(geomData4.z, geomData4.w);
		return tex4.Sample(samplerState4, adjustedUV);
	}
}

return float4(0, 0, 0, 1);
}

So I prepare up to 4 split screens and check if a given geomData is valid. So in my code I do:

Code: Select all

std::vector<Ogre::String> textureNames;
std::vector<Ogre::Vector4> geometryVectors;

for (size_t i = 0; i < splitScreenComponents.size(); i++)
{
	textureNames.emplace_back(splitScreenComponents[i]->getSplitScreenTexture()->getNameStr());
	geometryVectors.emplace_back(splitScreenComponents[i]->getGeometry());
}

Ogre::String materialName = "DynamicSplitMaterial";
auto splitMaterial = Ogre::MaterialManager::getSingletonPtr()->getByName(materialName);
Ogre::GpuProgramParametersSharedPtr fragmentParams = splitMaterial->getTechnique(0)->getPass(0)->getFragmentProgramParameters();
for (size_t i = 0; i < geometryVectors.size(); ++i)
{
	Ogre::String paramName = "geomData" + Ogre::StringConverter::toString(i + 1); // Match shader uniform
	if (fragmentParams->_findNamedConstantDefinition(paramName, false))
	{
		fragmentParams->setNamedConstant(paramName, geometryVectors[i]);
	}
	else
	{
		Ogre::LogManager::getSingleton().logMessage("[SplitScreenComponent]: Shader uniform not found: " + paramName, Ogre::LML_CRITICAL);
	}
}

So e.g. if just 2 cameras are involved, their geometry is used, so there are just geomData1 and geomData2 valid. Hence just one split is done. Now its possible to fill up to 4 cameras.

Just wanted to finish this topic, if somebody does try similiar things, he ma orientate on the code or ask me for assistance.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5446
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1348

Re: Create Material Ogre-Next

Post by dark_sylinc »

Hi!

Sorry for the late reply. Merry xmas!

I thought you were missing a call to pass->setVertexProgram and pass->setFragmentProgram. But you do call them.

Make sure you call the function after the files on disk have been parsed (usually that's after the call to initialiseAllResourceGroups), otherwise you'll see in the Ogre.log an entry that "Ogre/Compositor/Quad_vs" was not found.

Code: Select all

14:02:26: OGRE EXCEPTION(3:RenderingAPIException): Fixed Function pipeline is no longer allowed nor supported. The material DynamicSplitMaterial must use shaders in HlmsLowLevel::calculateHashFor at E:\Ogre2.2SDK\OgreMain\src\OgreHlmsLowLevel.cpp (line 173)

This error means you didn't properly attach a VS and PS to all passes in all techniques in the Material, or the shaders you attached had problems (look for previous errors in Ogre.log).

If all fails, do the following:

  • Set Debugger to break on all exceptions (tick everything).
  • If that still doesn't get you any hints, start getting stepping inside. Particularly inside setVertexProgram and setFragmentProgram. Particularly you need to find out why mVertexProgramUsage or mFragmentProgramUsage are nullptrs.
Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Create Material Ogre-Next

Post by Lax »

Hi,

merry christmas!

Thanks for you response.
Yeah it must be some order issue.
I now fixed it, by preparing the maximum number of split screens. That does work fine for me now.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62