Introduction
Real-time shaders are used more and more by game developers to provide a greater sense of realism. They allow the creation of complex lighting and advanced visual effects. DCC Tools such as 3ds Max, Maya and XSI provide a preview of real-time shaders in their viewport. And softwares such as RT/Shader Ginza, RenderMonkey and FX Composer are tools for creating real-time shaders that could be imported and used within DCC tools.
The goal of this article is to show how to use DirectX Shaders in 3ds Max for video game development. It covers all the production process: from implementation and integration to export. This article could be for anybody working with 3ds Max, from technical directors to tools developers. It's recommended that the reader have a basic knowledge of developing tools for 3ds Max – MAXScript, Max SDK and DirectX Shaders - in order to get the most out of this.
DirectX shaders in 3ds Max
DirectX shaders definition
DirectX shaders are shaders in DirectX Effect format. 3ds Max allows developers to create shaders effects and scene effects. There are three scene effects types: pre effects, post effects and environment effects. This article will focus especially on shaders effects. For example, with a DirectX Shader you can view in realtime, in the viewport, a shader used in your game, for example a normal mapping shader.
Advantages of DirectX shaders
Using a DirectX shader material, artists can view the same result in 3ds Max viewport as in the game engine. You can integrate DirectX shaders in your production workflow even if your game engine is not using standard DirectX effects. Indeed, it's possible to export the effects to your specific shader effect format. Also, if you are developing a game not using shaders, you can use DirectX shaders in order to display in Max viewport a specific lighting effects used in the game for example.
DirectX Shaders can help a lot of artists. They provide immediate a feedback. So, real-time DirectX shaders save a lot of time and the gain of productivity in production can be significant.
Drawbacks of DirectX shaders
DirectX shaders are slow in 3ds Max, especially when they are applied on a lot of objects and used in a large scene. Also, they are bugged. You can sometimes get strange bugs, for example, a texture color not the same in the viewport and in the source file. I hope these bugs will be solved in the future and it's possible to configure 3ds Max in order to speed up the display of DirectX shaders in the viewport. Now, let's take a look at the development of DirectX shaders effects for 3ds Max.
FX Effect format for 3ds Max
Max ships with two Effect parser; default Max parser and DXSAS 0.8. If you are not satisfied with these parsers, you can write your own parser and extend the effect support.
When developing a DirectX Effect Shader, to choose the correct Effect parser to load you can use this code:
string ParamID = "0x0"; // Load default Max parser
or
string ParamID = "0x0001"; // Load DXSAS parser
This line is used by 3ds max to load the correct parser. This line has to be mentioned at the beginning of the effect file, as the first instruction.
Also, macros are defined when the effect is compiled. Here is the list:
_3DSMAX_, 3DSMAX, _MAX_ and MAX
These macros are very handy when sharing effects between the game engine, 3ds Max and other DCC tools.
In order to view the shader parameter in the DirectX 9 Shader material UI, you have to use some semantic annotations. For example, in order to create a spinner in the UI you can try this code:
float OffsetScale <
string UIType = "FloatSpinner";
string UIName = "Offset Scale";
float UIMin = -1.0f;
float UIMax = 1.0f;
> = {0.1f};
In this example, the annotations are “UIType”, “UIName”, “UIMin”, “UIMax”. They tell 3ds Max to create a spinner for a float value with a caption “Offset Scale” and a value limited to the range -1.0 - 1.0.
Another example showing the use of a semantic:
float4 PS(VS_OUT In) : COLOR0
{
// Pixel Shader code here
}
In the previous example, the semantic is “COLOR0”, it describes the value returned by the pixel shader. Indeed, the pixel shader returns a float4 read as a color. Now, let's take a look at the integration of DirectX shaders in 3ds Max.
How to integrate DirectX shaders in 3ds Max?
Default DirectX 9 Shader material
DirectX 9 Shader material enables the artists to shade objects in 3ds Max viewports loading DirectX 9 shaders coded in DirectX effect format. The advantage of this material is that it can be easily exported with 3ds Max SDK and 3DXI. But this material is not very handy for the artists because they have to browse and load an effect file each time they create a new DirectX 9 Shader. Also, the auto UI created by the parser is not perfect and ergonomic.
Scripted materials
DirectX 9 Shader material can be extended via MAXScript. With scripted materials, the UI generated is customized, be sure to validate data and check for errors. They are easy to manage and update. An extended DirectX 9 Shader scripted material encapsulates the effect parameters. So, when developing this kind of script, remember to create a parameter in the paramblock for each effect parameter.
Example of a basic scripted plug-in which extends DirectX 9 Shader material:
FX_DEFAULTPATH = “$maps\\fx\\"
FX_TEXTURE = "Texture.fx"
plugin material MyExtendFX
name:"MyExtendFX"
classID:#( 645812,447325 )
extends:DirectX_9_Shader replaceUI:true version:1
(
parameters MainParams rollout:RollParams
(
DiffuseMap type:#bitmap
)
rollout RollParams "Material Parameters"
(
button btnDiffuseMap "None"
)
on create do
(
-- Setup initial material
delegate.effectfile = DEFAULTPATHFX + FX_TEXTURE
)
)
When extending the DirectX 9 Shader, you can access, get and set the properties of this material using the “delegate” variable.
The delegate variable holds:
- The properties of the DirectX 9 Shader
- The parameters defined in the effect
We can write a parameter in the effect like that:
bool g_DiffuseMapEnable <
string UIName = "Diffuse Map Enable";
> = false;
And in the material parameter block, use this parameter as follows:
-- Set DiffuseMap activation value
on DiffuseMapEnable set val do
delegate.g_DiffuseMapEnable = val
In the material parameter block, we can set the color, boolean, integer etc. values but not the bitmaps. We can only set the bitmaps values in the rollout part.
To set the bitmaps you can try:
Effect code:
texture g_ Diffuse Texture <
string UIName = "Diffuse Map";
int Texcoord = 0;
int MapChannel = 1;
>;
Scripted material code (in the rollout bloc):
-- When pressing btn DiffuseMap button
on btn DiffuseMap pressed do
(
bmp = selectbitmap caption: "Diffuse Map"
if(bmp != undefined) do
(
delegate.g_ Diffuse Texture = bmp -- Set effect bitmap
btnDiffuseMap.text = bmp.filename -- Change caption text
DiffuseMap = bmp -- Set Paramblock value
)
)
Write your own material with the SDK
Using 3ds Max SDK, you can develop your own DirectX hardware shader material with the SDK. This solution isn't very easy but it is feasible. Using some classes from the SDK and DirectX SDK, it's possible to create such a plug-in.
In fact, there's two ways to create a DirectX shaders with the SDK: a Material or a DirectX Manager plugin. In my opinion, I think the Material plug-in is better because it's a true material, contrary to a DirectX Manager plug-in which is part of the Standard material, and the is easier for a graphic artist to use in Max. Now, let see how to export DirectX effect shaders.
Exporting DirectX shaders
With the SDK
3ds Max SDK lets us access DirectX 9 Shader materials and scripted materials. We can get bitmap, light and effect properties. To get all DirectX 9 Shader and scripted materials extending DirectX 9 Shader material that are in the scene, you can use this code:
// Get scene materials
Interface* ip = GetCOREInterface();
MtlBaseLib* lib = ip->GetSceneMtls();
if (!lib)
return 0;
// Loop over the scene materials
const int NumMtls = lib->Count();
for( int i = 0; i < NumMtls; i++ ) {
// Get current material
MtlBase* mtl = static_cast<MtlBase*>( (*lib)[ i ] );
if (!mtl)
continue;
// DxMaterial or extend ?
IDxMaterial* dxMtl =
(IDxMaterial*)mtl->GetInterface(IDXMATERIAL_INTERFACE);
if( dxMtl)
ProcessDxMaterial(dxMtl);
}
When processing the DxMaterial, to access the material properties you can use:
// Get informations from the DirectX Shader material
// Get number of bitmaps used
unsigned int NumberOfBmps = dxMtl -> GetNumberOfEffectBitmaps ();
This code works with a DirectX 9 Shader material or an extend of this material. Then, you can get the effect parameters using it parameter block at index 0:
// GetParamBlock() work only on the material pointer
IParamBlock2* pblock = mtl ->GetParamBlock(0);
This time, the previous code works only with DirectX 9 Shader. Running this code on an extend DirectX 9 Shader, the value returned is the scripted material parameter block and not the effect parameters.
With 3DXI
3DXI (known as IGame) is a high level API for 3ds Max SDK. With this API you can easily and quickly extract data from Max. 3DXI is very interesting in order to parse an effect file used in a DirectX Shader. In 3DXI, in order to loop over the scene materials you have to use the root materials. To recognize a DirectX 9 Shader material, the 3DXI material class provides the function GetIGameFX() returning the 3DXI effect representation of this material.
To get all DirectX 9 Shader materials that are in the scene, you can use this code:
// Init IGame
IGameScene* pIgame = GetIGameInterface();
pIgame->InitialiseIGame(false);
pIgame->SetStaticFrame(0);
unsigned int i = 0;
// Loop over scene materials
for(i = 0; i < pIgame->GetRootMaterialCount(); i++)
{
// Get this root material
IGameMaterial* mtl = pIgame->GetRootMaterial(i);
if(!mtl)
return;
// DxMaterial ?
IGameFX* fx = mtl->GetIGameFX();
if(fx)
ProcessEffect(fx)
}
To loop over the effect technique and passes you can try this code:
unsigned int i = 0, j = 0;
// Loop over the technique of the effect
for(i = 0; i < fx->GetNumberOfTechniques(); i++)
{
// Get current technique
IGameFXTechnique* tech = fx->GetIGameFXTechnique(i);
ProcessFXTechnique(tech);
// Loop over the passes of the current technique
for(j = 0 ; j < tech->GetNumberOfPasses(); j++)
{
// Get current pass
IGameFXPass* pass = tech-> GetIGameFXPass(j);
ProcessFXPass(pass);
}
}
3DXI is an interesting library for import/export in constant development. DirectX shaders support is moving along more and more.
With MAXScript
If you only want to export some basic effect properties and informations, you can use MAXScript. In script, we can access bitmaps properties and some common informations. To access DirectX 9 Shader material properties you can use the following MAXScript code:
-- Loop over scene materials
for mtl in sceneMaterials do
(
-- DxMaterial?
if(Classof mtl == DirectX_9_Shader) do
(
-- Get current technique
CurrentTechnique = mtl.technique
EffectFile = mtl.effectfile
-- Get used bitmap(s) in the effect
for i = 1 to mtl.numberofbitmaps() do
(
bmp = mtl.geteffectbitmap i
BmpFileName = bmp.filename
)
)
)
This small script shows how to access current effect filename, the index of the technique used and the bitmaps' filenames.
Conclusion
In conclusion, DirectX shaders can be integrated easily in 3ds Max. The development of a DirectX shaders effect for Max isn't difficult. We can integrate Direct shaders as an entire material with scripted materials or with the SDK and export them using the SDK and 3DXI DirectX shaders support in 3ds Max is extended more and more. So, if you are not yet using DirectX shaders in Max, I think it's time to think about it.
Resources
Sparks website: http://sparks.discreet.com/
Some interesting DirectX shaders effects from Ben Cloward website: http://www.monitorstudios.com/bcloward/resources.html
Some DirectX shaders effects: http://ypuechweb.free.fr/download.html
Very good forum about tools development: http://dl3d.free.fr/phpBB2/index.php
Forum about shader development: http://ypuechweb.free.fr/phpBB2/index.php