🔒 Locked
Engine material systems
Advertisement
Original Post
I am well aware that my graphics engine needs an overhaul for some things. At the moment, I'm concentrating on the material system. It's not really a system it's more like a bunch of objects that inherit from a single base class that just provides an interface. I want my system to be able to represent things from the lighting material for a particular object, to reflective surfaces. I know how to do some of the things individually, but I'm in the dark as to how to have a proper system that will take it all and where new materials can be added as simply as writing a new class. How have others done it?
I have a material class, which contains an ID, a name, some other data, and two member structs, one for Render properties and one for Physical properties.
Render properties include the diffuse & normal map. Physical properties include things like friction, etc.
All materials in my game are the same class ( no inheritance ), rather, I have about 25 properties between the Phyiscal and Render structures to vary how the materials respond.
For instance, I can make a material that has the same texture as a solid metal sheet material, but this one can be uncollidable. I have some materials that are marked as not walkable, others that don't cast or receive shadows.
I would recommend to not tie one material->one texture, or one material-> one shader.
In my case the shader used is a function of the part of the lighting pipeline being used, all materials use the same shaders.
Render properties include the diffuse & normal map. Physical properties include things like friction, etc.
All materials in my game are the same class ( no inheritance ), rather, I have about 25 properties between the Phyiscal and Render structures to vary how the materials respond.
For instance, I can make a material that has the same texture as a solid metal sheet material, but this one can be uncollidable. I have some materials that are marked as not walkable, others that don't cast or receive shadows.
I would recommend to not tie one material->one texture, or one material-> one shader.
In my case the shader used is a function of the part of the lighting pipeline being used, all materials use the same shaders.
Quote:
Original post by SimmerD
I would recommend to not tie one material->one texture, or one material-> one shader.
So, do you have a single shader and single texture associated with your material object? Or can you have a series of shaders and textures associated with a single material object?
I have a rather complex material system
Ttextures just represent the texture data.
Materials combine different textures and effects.
E.g.: You want a multi layered terrain with 4 textures, that has some special bullethit effects and maybe is covered by volumetric fog
so you specify the layers and the effects as you see underneath
Effects take care of the data that is required to render a certain effect( that is the maximum amount of different data that is required to render a certain effect, lower quality settings won t require all the data that is provided)
The effect contains information about the sounds to play, the particles to spawn or what vertex/pixelshader to use
Efx-objects describe certain effect possibilities
e.g.: which shaders it supports, which particle effects it can handle ...(use:...)
where the efx-object plugin is( dll:...)
and if there are efx-objects that can handle the same effect, the priority setting will determine which of these objects to use.
This system allows you to extend your engine in any way you want to the whole rendering is done in the dlls.
However you should think about how complex you want to make it. There s no need for such a complex system if you don t plan to use your engine for further projects.
Mine currently is kept independent of the render of the engine so I can use it in the 3D editor and the 3d engine without code refactoring and it works pretty well.
The text declarations of materials,effects,efx-objects are put into xml files to load them in with tiny xml easily
texture:
-texture data
material:
- layer0: "sometexture"
- layer1: "sometexture2"
- layern: "sometexturen"
- efx1 //graphic effect e.g.: n-layered terrain
- efx2 //bullethit
- efx3 //fog ....
effect
- id: efx1
- use: nlayer_blend
efx-object
- type: graphics
- dll: xyz.dll
- can: nlayer_blend,4layer_blend .....
- priority: 100
Ttextures just represent the texture data.
Materials combine different textures and effects.
E.g.: You want a multi layered terrain with 4 textures, that has some special bullethit effects and maybe is covered by volumetric fog
so you specify the layers and the effects as you see underneath
Effects take care of the data that is required to render a certain effect( that is the maximum amount of different data that is required to render a certain effect, lower quality settings won t require all the data that is provided)
The effect contains information about the sounds to play, the particles to spawn or what vertex/pixelshader to use
Efx-objects describe certain effect possibilities
e.g.: which shaders it supports, which particle effects it can handle ...(use:...)
where the efx-object plugin is( dll:...)
and if there are efx-objects that can handle the same effect, the priority setting will determine which of these objects to use.
This system allows you to extend your engine in any way you want to the whole rendering is done in the dlls.
However you should think about how complex you want to make it. There s no need for such a complex system if you don t plan to use your engine for further projects.
Mine currently is kept independent of the render of the engine so I can use it in the 3D editor and the 3d engine without code refactoring and it works pretty well.
The text declarations of materials,effects,efx-objects are put into xml files to load them in with tiny xml easily
texture:
-texture data
material:
- layer0: "sometexture"
- layer1: "sometexture2"
- layern: "sometexturen"
- efx1 //graphic effect e.g.: n-layered terrain
- efx2 //bullethit
- efx3 //fog ....
effect
- id: efx1
- use: nlayer_blend
efx-object
- type: graphics
- dll: xyz.dll
- can: nlayer_blend,4layer_blend .....
- priority: 100
Well, tomorrow, or rather, today (it is now the wee hours of the morning) I'll be making a couple of more attempts with the material system.
Please, don't hesitate to keep the ideas (or info about your systems) coming. I've gotten really good info from SimmerD and Basiror both.
Please, don't hesitate to keep the ideas (or info about your systems) coming. I've gotten really good info from SimmerD and Basiror both.
Here is an example from my materials.txt configuration file.
Here an excerpt from my material.h header file.
I'm not really using the layers anymore. Instead of multiple pass materials, I have a single material on each object or world triangle, but support an arbitrary number of small or large decals on the world triangles for effects like blood, mud, vines, cracks, etc.
My engine does proper shadowing & lighting, so you can't really have custom shaders for different materials for the most part. I do use a 'glass' shader if a material's opacity < 1.0f. Of course, glass doesn't really do the whole shadow thing properly, so it's not an issue. I suppose you could have a custom shader to determine the diffuse & specular color, given a diffuse color, a normal, and the H and L vectors, but there's only so many ways to do that...
group cartskin1 { int id = 1493; float flammability = 0.0; float bouyancy = 0.0; float stickiness = 0.0; float friction = 1.0; float metallic = 0.7; float reflectivity = 1.0; float opacity = 1.0; float dragCoefficent = 1.0; float solidity = 5.0; vector specular = 1.8 1.6 1.5 1.0; vector emissive = 0.1 0.0 0.0 0.0; vector bump_scale = 1.0 1.0 0.6 0.0; vector spec_scale = 1.0 1.0 0.3 0.0; vector diffuse = 1.0 1.0 1.0 1.0; vector weights = 0.5 0.5 0.5 1.0; vector tex_scale = 0.25 0.25 0.25 0.25; bool shadow_cast = true; bool shadow_receive = true; bool light_receive = true; bool blocks_light = true; bool walkable = false; bool deadly = false; string sound = "metal"; string debris = "sparks"; string decal = "hole"; group layer { int count = 1; group 0 { string diffuseMap = "materials\cavecity\cartskin1.dds"; string normalMap = "materials\cavecity\cartskin1_bump.dds"; } } }Here an excerpt from my material.h header file.
struct Material{ public : std::wstring mName; typedef uint16 ID; ID mID; uint32 mFlags; enum { INVALID_ID = 0, MaxNumLayers = 4, BlocksLight = 0x1, BlocksMovement = 0x2, Liquid = 0x4, Deadly = 0x8, Walkable = 0x10, ShadowRecv = 0x20, ShadowCast = 0x40, LightRecv = 0x80, Any = 0xffff }; struct Physical { float32 mFlammability; float32 mBouyancy; float32 mStickiness; // for arrows sticking in it float32 mFriction; float32 mMetallic; float32 mReflectivity; float32 mOpacity; float32 mDragCoefficent; float32 mWalkability; float32 mSolidity; float32 mLayerMin; float32 mLayerMax; float32 mLayerMinDensity; float32 mLayerMaxDensity; Material::ID mBurnTransitionMaterial; // what material to become }; struct Render { void Preload() const; D3DXVECTOR4 mEmissive; D3DXVECTOR4 mSpecular; D3DXVECTOR4 mBumpScale; D3DXVECTOR4 mSpecScale; D3DXVECTOR4 mDiffuse; D3DXVECTOR4 mWeights; // used for TBN smoothing D3DXVECTOR4 mTexScale; uint32 mLayerCount; mutable uint32 mUses; struct Layer { mutable LPDIRECT3DTEXTURE9 mpDiffuse_Alpha; mutable LPDIRECT3DTEXTURE9 mpNormal_Gloss; std::wstring mDiffuseName; std::wstring mNormalName; }; Layer mLayers[ MAX_LAYER_COUNT ];};I'm not really using the layers anymore. Instead of multiple pass materials, I have a single material on each object or world triangle, but support an arbitrary number of small or large decals on the world triangles for effects like blood, mud, vines, cracks, etc.
My engine does proper shadowing & lighting, so you can't really have custom shaders for different materials for the most part. I do use a 'glass' shader if a material's opacity < 1.0f. Of course, glass doesn't really do the whole shadow thing properly, so it's not an issue. I suppose you could have a custom shader to determine the diffuse & specular color, given a diffuse color, a normal, and the H and L vectors, but there's only so many ways to do that...
What do you do when you render? How do you let the renderer know about the Material?
I suppose the easiest would be:
And then the renderer just sets the light data and any diffuse map data, or bump mapping, or whatever. Is that right?
Something simple along those lines?
I suppose the easiest would be:
void Object::render(){ video_driver->setMaterial( object_material ); video_driver->renderGeometry( vertices );}And then the renderer just sets the light data and any diffuse map data, or bump mapping, or whatever. Is that right?
Something simple along those lines?
This depends on the responsibilities.
My material implementation is used in the 3D editor as well.
Therefore the material is responsible to provide all the information that is required to render a specified effect
1.
Material uses resource requirements from effect to know what data to load/generate
2. At loadtime I associate each effect with and efx-object(the implementation in the dll) and requests which data to send at runtime
3. At runtime the effect requests the resource references ( vertex pointer ,....) from the material and the assigned geometry and passes those to the efx-object.
material: provides data information(texture ids ..)
geometry: stores per vertex information(vertex data, uv coordinates, vertex attributes...)
effect: requests required information
passes the information the to right efx-object
efx-object: uses the data and does some fancy work
This interface is pretty generalized and thus allows pretty much every effect no matter if it is a pre or post effect or is run at runtime
My material implementation is used in the 3D editor as well.
Therefore the material is responsible to provide all the information that is required to render a specified effect
1.
Material uses resource requirements from effect to know what data to load/generate
2. At loadtime I associate each effect with and efx-object(the implementation in the dll) and requests which data to send at runtime
3. At runtime the effect requests the resource references ( vertex pointer ,....) from the material and the assigned geometry and passes those to the efx-object.
material: provides data information(texture ids ..)
geometry: stores per vertex information(vertex data, uv coordinates, vertex attributes...)
effect: requests required information
passes the information the to right efx-object
efx-object: uses the data and does some fancy work
This interface is pretty generalized and thus allows pretty much every effect no matter if it is a pre or post effect or is run at runtime
The Frame object pulls data from the material as needed.
For instance the DeferredFrame object does most of the rendering, and composites the diffuse & normal maps together, and performs the lighting, so it grabs info from the material object as well as the lighting info, and the geometry chunks or entities being rendererd.
The SelfFrame object ( for self-shadowing, although I don't really do this ) doesn't bother with the diffuse or normal maps, it's just using the geometry to render the shadow term to dest alpha.
In the sense of Model View Controller, the material is not a View, but a Model. The Frame object is a View.
For instance the DeferredFrame object does most of the rendering, and composites the diffuse & normal maps together, and performs the lighting, so it grabs info from the material object as well as the lighting info, and the geometry chunks or entities being rendererd.
The SelfFrame object ( for self-shadowing, although I don't really do this ) doesn't bother with the diffuse or normal maps, it's just using the geometry to render the shadow term to dest alpha.
In the sense of Model View Controller, the material is not a View, but a Model. The Frame object is a View.
I used something similar with combining graphics and physics material properties, but kept them seperate (more modular). My engine is divided into several components, including graphics, physics, and simulation. Each of these contains its own namespace to keep things sane. Graphics=gfx, Physics=psx, Simulation=sim. I then have a gfx::cMaterial class, a psx::cMaterial class, and a sim::cMaterial class. The simulation engine basically provides a way for the game using the engine to handle game objects on a higher level, without worrying about graphics and physics and AI, etc. So the gfx::cMaterial class contains data that the graphics engine will use (textures, dif/spec/amb/emi/shine, etc.) and the psx::cMaterial class contains data that the physics engine will use (drag, friction, mass, density, etc.). Finally, the sim::cMaterial class provides higher level interaction, for example setting what happens if the material is shot or collided with (a shimmer effect for dragon scales would be an example). This keeps things modular. If I change out my graphics engine, it doesn't affect the other components. Likewise the physics engine.
- Enosch
- Enosch
For completeness and reference:
Basiror's original thread on this topic.
You should also read the thread linked to by that thread, as it is also very useful.
Whether you want something so complete and extensive is another matter however, and various other implementations (for instance using scripting systems) are mentioned in those two threads.
Basiror's original thread on this topic.
You should also read the thread linked to by that thread, as it is also very useful.
Whether you want something so complete and extensive is another matter however, and various other implementations (for instance using scripting systems) are mentioned in those two threads.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement