📜 ⬆️ ⬇️

Urho3D: Materials

The graphics subsystem is probably the most complex and confusing part of the engine. And at the same time, this is the part in which you need to be very well oriented. You can easily handle the input, play sounds and not even think about how it is arranged inside there. But a rare game will do without its own beautiful effects, and there is no need to do without a certain set of knowledge. In one article it is impossible to cover the whole amount of information on this topic, but I hope that I can provide you with a base, relying on which you will master all the nuances and subtleties of the Urho3D render much easier.

image

For impatient


The result of the experiments is here: github.com/1vanK/Urho3DHabrahabr05 . In the Demo folder of the demo itself (by moving the mouse you can rotate the model, the wheel turns the light source). All added resources are separated from the engine files and are located in the Demo / MyData folder.

Considered version of the engine


Currently, integration into the PBR engine (Physically Based Rendering, physically correct rendering) is underway. It will not add any significant changes to the engine itself, basically it’s just an additional set of shaders. In addition, he has not yet settled down and in the final some things can change. Therefore, in order to narrow the range of the information in question, I will rely on an older version of the engine (at the time of this writing, it was already out of date for ten days :).
')
Version of April 9, 2016.
::    git.exe set "PATH=c:\Program Files (x86)\Git\bin\" ::   git clone https://github.com/Urho3D/Urho3D.git ::       cd Urho3D ::       (9  2016) git reset --hard 4c8bd3efddf442cd31b49ce2c9a2e249a1f1d082 ::   ENTER    pause 

Importing a model


For the experiments, I decided to take the Abaddon model from the Dota 2 game. Here there are both texture maps that Urho3D supports “out of the box” and those specific to the Source 2 engine. We will not disregard them either. Which card is responsible for what you can see in this document . But before we deal with the materials, we need to transform the model into the format understood by Urho3D. This can be done in different ways.

Included with the engine comes a utility AssetImporter, which allows you to import models from a variety of formats. It can be found in the bin / tool folder. The "AssetImporter.exe model abaddon_econ.fbx abaddon.mdl -t" command combines all the model fragments into a single whole (the model is divided into fbx-file). Be sure to include the "-t" option if you are going to use normal maps (this parameter adds information about tangents to model vertices). The “AssetImporter.exe node abaddon_econ.fbx abaddon.xml -t” command will save the model as a prefab, in which parts of the model are organized as a hierarchy of nodes. Since there are no textures assigned to the model in this fbx file, the materials will have to be created manually.

You can import a model directly from the editor (File> Import model ...). In this case, the same AssetImporter utility is used. The "-t" option is enabled by default. The remaining parameters can be specified in the View> Editor settings window.

For fans of Blender there is a separate exporter. And he is great. Download the addon , install it and open the already prepared Abaddon.blend file (I left only the horse from the model and created the material for it). Specify the path where you want to save the result, check the settings (do not forget about tangents) and click the Export button.



The first thing that catches your eye when opening a model in the Urho3D editor is the white legs of a horse. The fact is that Blender interprets the glow map as intensity (black and white image), and the Urho3D engine as color. You can fix the glow map itself (for example, in the IMP multiply it with a diffuse map), but we are not looking for easy ways and we will change the Urho3D shader to adapt it to the Source 2 engine texture set. But it will be later, but for now, to see the model in a more pleasant way, you can change the value of the glow factor MatEmissiveColor to “0 0 0” in the material parameters (abaddon_mount.xml file), or delete this line altogether (just do not forget to return everything back, we still need this parameter). By the way, when you modify and save a material file, the editor automatically picks up the new version and updates its viewport. Moreover, it works even for shaders. You can write a shader and watch the result of their work in real time.

Rendering process


The rendering process in Urho3D is described using a set of text files: renderers (CoreData / RenderPaths / *. Xml), a technician (CoreData / Techniques / *. Xml) and shaders (CoreData / Shaders / GLSL / *. Glsl or CoreData / Shaders / HLSL * .hlsl). Once you understand the relationship between them, the rest will not be difficult to figure out.

So, each object of the scene in most cases is rendered several times using different shaders (the so-called render passes). Which set of passes (shaders) is required to draw each object is described in the technique. In what order these passes (shaders) will be executed, described in the renderpas. Once again I draw your attention: the order of the lines in the technique is not important.

There are also materials (Data / Materials / *. Xml). The material determines which technique will be used for the object, and also fills this technique with specific content, that is, determines the data that will be transferred to the shaders: textures, colors, etc.

That is, the set of renderpas + technology + shaders can be represented as an algorithm, and the material - as a data set for this algorithm.

To understand the procedure for calling shaders, take a look at this comparison label:

 //  //  for (i = 0; i < ; i++) for (int i = 0; i < ; i++) { { for (j = 0; j < ; j++) for (j = 0; j < ; j++) ([i], [j]); { } if ([j]  [i]) ([j], [i]); } } 

If there is a passage in the technique, but there is no such passage in the renderpas, it will not be executed. And vice versa, if there is some kind of pass in the renderpas, but it is absent in the technology, then it will not be executed either. However, there are passages registered in the engine itself, which you will not find in the renderpas, but they will still be executed if they are in the technique.

Shaders


Urho3D supports both OpenGL and DirectX, so it contains two sets of shaders similar in functionality, which are located in the CoreData / Shaders / GLSL and CoreData / Shaders / HLSL folders. Which API and, accordingly, which set of shaders will be used, is determined when compiling the engine. I prefer OpenGL as more universal, so in this article I will focus on it.

There are a lot of files in the CoreData / Shaders / GLSL folder, but you should pay particular attention to one of them - LitSolid.glsl. This shader is used for the vast majority of materials. Urho3D uses the Ubershader method, that is, from the huge universal shader LitSolid.glsl, defines are thrown out unnecessary pieces and a small fast specialized shader is obtained. Define sets are specified in the techniques, and some defines are added by the engine itself.

Back to our horse


Currently, Urho3D supports 3 rendering methods: Forward (traditional), Light Pre-Pass and Deferred. The differences between them are a topic for a separate conversation, so we just confine ourselves to the Forward method, which is used by default and described in CoreData / RenderPaths / Forward.xml.

After exporting from Blender, we received Materials Materials / abaddon_mount.xml.
 <material> <technique name="Techniques/DiffNormalSpecEmissive.xml"/> <texture name="Textures/abaddon_mount_color.tga" unit="diffuse"/> <texture name="Textures/abaddon_mount_normal.tga" unit="normal"/> <texture name="Textures/abaddon_mount_specmask.tga" unit="specular"/> <texture name="Textures/abaddon_mount_selfillummask.tga" unit="emissive"/> <parameter name="MatDiffColor" value="1 1 1 1"/> <parameter name="MatSpecColor" value="1 1 1 50"/> <parameter name="MatEmissiveColor" value="1 1 1"/> </material> 

This material uses Techniques / DiffNormalSpecEmissive.xml technique.
 <technique vs="LitSolid" ps="LitSolid" psdefines="DIFFMAP"> <pass name="base" psdefines="EMISSIVEMAP" /> <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthtest="equal" depthwrite="false" blend="add" /> <pass name="prepass" vsdefines="NORMALMAP" psdefines="PREPASS NORMALMAP SPECMAP" /> <pass name="material" psdefines="MATERIAL SPECMAP EMISSIVEMAP" depthtest="equal" depthwrite="false" /> <pass name="deferred" vsdefines="NORMALMAP" psdefines="DEFERRED NORMALMAP SPECMAP EMISSIVEMAP" /> <pass name="depth" vs="Depth" ps="Depth" /> <pass name="shadow" vs="Shadow" ps="Shadow" /> </technique> 

Since we will modify the technique, copy it into a separate Techniques / MyTechnique.xml file, so as not to affect other materials that may also use this technique. In the material also change the name of the used equipment.

The technique uses the standard for materials shader LitSolid.glsl. Since we are going to change this shader, copy it in Shaders / GLSL / MyLitSolid.glsl. Also change the name of the used shader in the technique. At the same time, this technique can be simplified by throwing out too much. Since the passages “prepass”, “material”, “deferred” and “depth” are absent in the Forward renderpass (they are defined in other renderpasses), they will never be used. However, leave the passage “shadow”. Although it is not in the renderpas, it is a built-in passage and is registered in the engine itself. Without it, the horse will not cast shadows.

As a result, we have Techniques / MyTechnique.xml
 <technique vs="MyLitSolid" ps="MyLitSolid" psdefines="DIFFMAP"> <pass name="base" psdefines="EMISSIVEMAP" /> <pass name="light" vsdefines="NORMALMAP" psdefines="NORMALMAP SPECMAP" depthtest="equal" depthwrite="false" blend="add" /> <pass name="shadow" vs="Shadow" ps="Shadow" /> </technique> 

Let's work with our Shaders / GLSL / MyLitSolid.glsl shader now and change the way we use the glow map to the one we need. Define EMISSIVEMAP (just signaling that the material should determine the glow map) in the shader is present in several places, and since the shader is disassembled line by line within the article does not work out, we will go the other way. Defaults DEFERRED, PREPASS and MATERIAL will never be defined in the shader, since the passages in which they are defined are removed from the technology as unused. Therefore, we boldly delete the code framed by them. Shader size decreased by one third. Define EMISSIVEMAP remained only in one place.

Let's multiply the glow map with a diffuse color. Replace line

 finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb; 

on line

 finalColor += cMatEmissiveColor * texture2D(sEmissiveMap, vTexCoord.xy).rgb * diffColor.rgb; 

Now the horse's legs glow in the correct blue.

For more beauty, let's add a bloom effect (a halo around the bright parts of the image). It is implemented in two lines in the script :

 //  HDR-. renderer.hdrRendering = true; //   . viewport.renderPath.Append(cache.GetResource("XMLFile", "PostProcess/BloomHDR.xml")); 

Please note that the post-processing effect is added to the end of the render. You can add it directly to the file.

Rim light


Highlighting the edges of the model - fake technique that simulates the light falling on the model behind. In the Source 2 engine, a separate mask is used to identify the parts of the model on which you want to apply this effect.

In order to transfer the texture to the shader, choose an unused texture unit. Our material does not use the environment map, so we will use its slot. Also in the shader, we will need two parameters RimColor and RimPower, so we immediately add them. The final material looks like this . This can sometimes be confusing, but the name of the texture unit does not have to match its use. You are free to transfer whatever you like via texture units and apply it in your shaders as you like.

The name of the uniform in the shader will differ from the name of the parameter in the material by the prefix “c” (const): the RimColor parameter will become the cRimColor uniform, and the RimPower parameter will become the cRimPower uniform.

 uniform float cRimPower; uniform vec3 cRimColor; 

This is how the implementation itself looks like:

 // RIM LIGHT //  =   -  . vec3 viewDir = normalize(cCameraPosPS - vWorldPos.xyz); //      = 1. //     = 0. //  ,   ,      // (     ),    . //      ,      . float rimFactor = 1.0 - clamp(dot(normal, viewDir), 0.0, 1.0); //  cRimPower > 1,     . //  cRimPower < 1,      . //  cRimPower = 0     ,        = 1. rimFactor = pow(rimFactor, cRimPower); // ,     . #ifdef RIMMAP //     . finalColor += texture2D(sEnvMap, vTexCoord.xy).rgb * cRimColor * rimFactor; #else finalColor += cRimColor * rimFactor; #endif 

Pay attention to the default RIMMAP. You may want to not use the backlight map, but simply impose the effect on the entire model. In this case, you simply do not define the RIMMAP in the technology. By the way, do not forget to define the RIMMAP define in the technique :) The final technique looks like this , and the shader does .

Literature


urho3d.imtqy.com/documentation/1.5/_rendering.html
urho3d.imtqy.com/documentation/1.5/_a_p_i_differences.html
urho3d.imtqy.com/documentation/1.5/_shaders.html
urho3d.imtqy.com/documentation/1.5/_render_paths.html
urho3d.imtqy.com/documentation/1.5/_materials.html
urho3d.imtqy.com/documentation/1.5/_rendering_modes.html

Source: https://habr.com/ru/post/281903/


All Articles