[3.0.0] Custom Shaders

Problems building or running the engine, queries about how to use features etc.
User avatar
haloman30
Kobold
Posts: 30
Joined: Mon Aug 29, 2022 2:53 pm
x 2

[3.0.0] Custom Shaders

Post by haloman30 »

Ogre Version: 3.0.0

Hello - not sure if this is just something I've missed in my searching, but I'm wondering how (or if it's possible with reasonable performance) to implement custom shaders in Ogre-Next.

Through my searching, I've run across a couple approaches - using low-level materials (which supposedly can have negative performance implications for multiple of these), or creating a custom HLMS. As such, I'm pretty sure the HLMS route is where I need to go - however, all the posts and topics I've found seem to not quite be in reference to the kind of system I'm looking for - which is, to allow totally any number of arbitrary pixel/vertex/geometry shaders to be authored by users.

My project is a game engine, with a focus on 3D but also ideally usable for 2D as well, and so users being able to write their own shader code for certain objects to me seems to be a pretty essential feature. It's entirely possible I've overlooked something obvious, but from what I can tell, it seems as though I would need to create an entirely separate HLMS for each user-created shader.

So - is this correct? Or am I mistaken? If not, is there any significant overhead or limits on the number of HLMSes? Or is there some other solution that I've just missed?

As usual, I appreciate any and all info and guidance!

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

Re: [3.0.0] Custom Shaders

Post by dark_sylinc »

Hi!

We have an entry in the 2.1+ FAQ regarding custom Hlms implementations.

In the master (4.0) branch we've added tutorials on writing custom Hlms implementations. See Tutorial_Hlms01 through Tutorial_Hlms05.

These tutorials apply to OgreNext 3.0 too. Maybe a virtual function override changed signature parameter, but it should be easy enough to adapt.

Now, onto your question:

haloman30 wrote: Wed May 21, 2025 2:11 am

however, all the posts and topics I've found seem to not quite be in reference to the kind of system I'm looking for - which is, to allow totally any number of arbitrary pixel/vertex/geometry shaders to be authored by users.

The Hlms is very flexible; so you can define custom pieces per object (i.e. apply custom shader code to a particular object), per material, or per pass.

You can apply different pieces to different objects; effectively creating an arbitrary number of shaders by users (though you'd probably have to write something on top to manage your users' shader code -> map into what goes into which object). See the tutorial.

In the master/4.0 branch we've also added a new per-material setting HlmsDatablock::setCustomPieceFile (with material JSON support keyword "custom_piece_file_vs" and co.) which allows registering a custom piece file per material without C++ additional code.

When customizing existing Hlms implementations you can do things like:

Code: Select all

@undefpiece( DeclareBRDF )
@piece( DeclareBRDF )
 // your lighting code here
@end

To force the PBS component of Hlms to not generate its lighting code and use whatever customization you want.

It seems that you want a shader to start from scratch (rather than customize an existing one). That is also possible.

This is often not recommended because Unlit & Pbs deal with a lot of boilerplate (skeletal animation, shadow casting, etc) so customizing these instead of starting from scratch is usually the easiest path. Specially since you can override this code to do whatever else.

For example Terra from the Terrain Sample completely overrides the vertex & pixel shaders of HlmsPbs while still reusing a lot of individual pieces of code from HlmsPbs.

haloman30 wrote: Wed May 21, 2025 2:11 am

So - is this correct? Or am I mistaken? If not, is there any significant overhead or limits on the number of HLMSes? Or is there some other solution that I've just missed?

If you mean HLMS types, we support 8 (with LowLevel, Pbs and Unlit taking away 3, so you're left with 5). However for what you want to implement, you'd want to either customize HlmsPbs/HlmsUnlit, or create an extra Hlms type (assuming that you want users to code from scratch) that manages all user's shader code.

This is what HlmsLowLevel essentially does, it looks at what was defined in the older Material system from v1 Ogre and creates the shaders from it. So if the user creates a thousand different shaders using the v1 material system, the HlmsLowLevel will create a thousand shaders.

A single Hlms type can create a virtually unlimited number of shaders.

haloman30 wrote: Wed May 21, 2025 2:11 am

is there any significant overhead or limits on the number of HLMSes?

Originally the v1 Material system was very slow and unsupported in regular rendering because it was treated as an exception; but those issues were fixed and nowadays the main reason to discourage users from using the v1 Material system for thousands of objects is that its flexibility and ease of use (user-friendly) design goals clash with high performance.

High performance originates from code that can perform assumptions, attempts to treat lots of objects in the same way, and imposes specific limitations on what can be done and what cannot. I'm not talking about OgreNext here, but computers in general.
But "cost" is a relative term here. Applying v1 materials on 100 Items is probably going to be fine on a modern CPU.

But if you apply it on many thousands of them, you won't have the same performance as you'd have with HlmsPbs or Unlit because they have limitations on their interfaces to achieve greater efficiency.

If what you want are shaders from scratch, a custom Hlms type may be what you're looking for; but you may just end up reinventing the v1 Material System (with similar performance pitfalls).

Cheers

User avatar
haloman30
Kobold
Posts: 30
Joined: Mon Aug 29, 2022 2:53 pm
x 2

Re: [3.0.0] Custom Shaders

Post by haloman30 »

dark_sylinc wrote: Wed May 21, 2025 3:10 am

If what you want are shaders from scratch, a custom Hlms type may be what you're looking for; but you may just end up reinventing the v1 Material System (with similar performance pitfalls).

Hey!

Yes, from-scratch shaders is indeed what I'm primarily looking into. The idea of extending PBS and Unlit sounds interesting too, however I do feel it's important regardless to include the ability for new shaders entirely from scratch to be created.

I did some looking into using low-level materials, however I'm getting some issues trying to manually create them (as I'm wanting to avoid relying on a bunch of material scripts since all engine content is in it's own format). Couldn't find much useful on the forum or elsewhere - and tried creating them both using HLMSLowLevel and the v1 MaterialManager.

Creating a HLMSLowLevelDatablock seemingly doesn't populate the proxy material, and creating it via MaterialManager results in a "not supported" error message and no valid techniques being available:

Code: Select all

WARNING: material shaders/solid.shader has no supportable Techniques and will be blank. Explanation:  Pass 0: Vertex program shaders/solid.shader_vtx cannot be used - not supported.

I did find a post suggesting that the issue might be due to the syntax code not being set properly, and even checking supported values (ps_5_0 and vs_5_0 in my case, also tried just 'hlsl' as it also showed up in the list), no luck. I also noticed the following message when creating the material:

Code: Select all

Material shaders/solid.shader was requested with isManual=true, but this is not applicable for materials; the flag has been reset to false

Which has me wondering - is manual creation of low level materials actually possible? Is there some step I'm missing for the program/material creation, or is this a case where I'd need to still create a custom HLMS to achieve this? For reference, the current approach I'm using for creating materials is as follows:

Code: Select all

// tag_path is a std::string with the shader file path
// BLAM_OGRE_RESOURCE_GROUP is a preprocessor macro storing the resource group used for custom datablocks, meshes, etc

Ogre::MaterialManager* material_manager = Ogre::MaterialManager::getSingletonPtr();
material = material_manager->create(tag_path, BLAM_OGRE_RESOURCE_GROUP, true);

Ogre::HighLevelGpuProgramManager* manager = Ogre::HighLevelGpuProgramManager::getSingletonPtr();

if (vertex_program_code.length() > 0)
{
	Ogre::HighLevelGpuProgramPtr program = manager->createProgram(tag_path + "_vtx", BLAM_OGRE_RESOURCE_GROUP,
		"hlsl", Ogre::GpuProgramType::GPT_VERTEX_PROGRAM);

program->setSource(vertex_program_code);
program->setSyntaxCode("hlsl");
program->createParameters();
program->load();
material->getTechnique(0)->getPass(0)->setVertexProgram(program->getName());
}

material->load();