-
Notifications
You must be signed in to change notification settings - Fork 59
MDX Rendering
This document describes MDX rendering for both SD and HD modes. The MDX format is fairly complex to load and render. For a binary specification of MDX you can refer to this excellent description by GhostWolf (Note that it is not up-to-date for MDX version 1100 found in WC3 1.33).
The rendering mode (SD/HD) of a model can differ between
- V900, V1000: geosets
- V1100: layers
Thus, it is possible to have a model that is partially SD and partially HD such as the following ogre (image by Retera):
(v900/v1000) If a material has a (valid?) shader then it is rendered with that shader, otherwise it is rendered in SD mode. (v1100) If a layer has the hd flag set, it will be rendered in HD mode, otherwise it is rendered in SD mode.
Some rendering implementations use an alpha of 0.75 for a geoset as a cutoff where they hide the geoset entirely, but this is not correct. Geoset alphas do not have a cutoff.
Every material can have any number of layers each with their own blendmode and properties such as TwoSided. Only the first layer is taken into account when sorting for transparency. So if the blendmode for layer 0 is either None or Transparent then the material is considered opaque. If the blendmode is any of the others such as Blend or Additive then it is considered transparent.
Afaict the only way to draw SD geosets is doing a draw call for every single layer in the geoset its material.
In HD mode only the first 6 layers are used for actual rendering. They have to be in the right order which is
- Diffuse
- Normal
- ORM
- Emissive
- Teamcolor
- Environment
Except for layer 0, all blendmodes, static alphas and any other properties seem to be ignored. It seems that HD mode layer 0 supports all the flags such as TwoSided and NoDepthTest.
To efficiently render HD materials you should have a shader that takes in the 6 layer textures instead of doing 6 individual draw calls for every layer.
The new MDX version no longer separates the different PBR textures into layers. Every layer now has a list of textures (with id and optionally KMTF) instead of just one texture ID per layer. An SD layer will still only refer to a single texture. This allows HD models to have multiple layers per geoset like SD geosets do. The textures in the layer textures array are still in the following order:
- Diffuse
- Normal
- ORM
- Emissive
- Teamcolor
- Environment
The diffuse textures are simple S3TC DXT1/DXT5 textures and your image loading library should easily handle that. It is also possible to directly upload the texture data to the GPU without decompression as most GPUs can use the compressed data natively. The relevant OpenGL function for this is glCompressedTexImage2D
The normal maps are two channel red green 3Dc/DXN/BC5 textures. Again you can upload these directly to the GPU without decompression. Using the model tangents you can then construct a TBN matrix which you multiply with the light direction in the vertex shader to get the tangent light direction. vInstance being your mat4 with the object its translation/rotation/scale.
mat3 model = mat3(vInstance);
vec3 T = normalize(model * vTangent.xyz);
vec3 N = normalize(model * vNormal);
vec3 B = cross(N, T) * vTangent.w; // to fix handedness
mat3 TBN = transpose(mat3(T, B, N));
tangent_light_direction = normalize(TBN * light_direction);
Then in the fragment shader you sample the normal texture and reconstruct the third (blue) component using the fact that the 3D vector they form should have a total length of 1.
vec2 texel = texture(normal, UV).rg;
vec3 normal = vec3(texel, sqrt(1.f - texel.x * texel.x - texel.y * texel.y));
Then you can dot product this with the tangent light direction to get the final contribution. Note that we invert the tangent_light_direction. We add some ambient light because otherwise we would get totally dark sides.
float lambert = clamp(dot(normal, -tangent_light_direction), 0.f, 1.f);
color.rgb *= clamp(lambert + 0.1, 0.f, 1.f);
The orm map has three channels which are occlusion, roughness and metalness. Occlusion seems to be unused by the game at this time, while the other two channels are probably used like in standard OpenGL ORM maps.
Regular emission map, just gets added to the color value
A replaceable texture supposed to represent teamcolor. Since its a solid color you don't need to use a texture per se and can just use a uniform or pass it in a buffer.
A texture used for the reflective and shiny stuff when the ORM texture has metalness.
- Does lighting apply to transparent layers?
- Does the editor always play the stand animation and does it loop it even when it's set to non_looping