So, I decided to write a simple step by step tutorial on how to pass additional textures to the default PBS implementation (probably this is also useful when passing your custom textures to your custom HLMS also).
So, for the sake of the tutorial, let's say you want to pass an additional diffuse map to the PBS implementation, for instance, to render a few decals over your mesh using the second uv channel.
So, what are we going to do in this tutorial is:
- Add a new diffuse map called "decal_map".
- Configure it the same way a diffuse map is configured.
- Set it to use the second uv map.
- Slithgly change the HLMS implementation to actually render this decal map over the diffuse one.
First in the OgreHlmsPbsPrerequisites.h you'll find this enum, let's add here the entry for our decal map, right after the diffuse one, it is important to remember the position where you entered your custom map.
Code: Select all
enum PbsTextureTypes
{
PBSM_DIFFUSE,
[b]PBSM_DECAL,[/b]
PBSM_NORMAL,
PBSM_SPECULAR,
PBSM_METALLIC = PBSM_SPECULAR,
PBSM_ROUGHNESS,
PBSM_DETAIL_WEIGHT,
PBSM_DETAIL0,
PBSM_DETAIL1,
PBSM_DETAIL2,
PBSM_DETAIL3,
PBSM_DETAIL0_NM,
PBSM_DETAIL1_NM,
PBSM_DETAIL2_NM,
PBSM_DETAIL3_NM,
PBSM_REFLECTION,
NUM_PBSM_SOURCES = PBSM_REFLECTION,
NUM_PBSM_TEXTURE_TYPES
};
Now to the OgreHlmsPbs.h, we have to add the IdString used to locate the map in the script and to set it to the shaders. Just add a IdString DecalMap and a IdString UvDecal in the attributes of the struct PbsProperty, I won't paste the code here because it's really pretty straightforward and it doesn't matter where you put it as long as you put it IN the struct PbsProperty.
Now the implementation (OgreHlmsPbs.cpp). Here you define how the hlms script will find the decal map and send it to the shader.
Step 1: Define the actual strings for DecalMap and UvDecal previously declared.
Code: Select all
const IdString PbsProperty::DecalMap = IdString( "decal_map" );
const IdString PbsProperty::UvDecal = IdString( "uv_decal" );
Step 2: Setup the uv source for the decal map, you need to put it in the correct location (right after the diffuse map):
Code: Select all
const IdString *PbsProperty::UvSourcePtrs[NUM_PBSM_SOURCES] =
{
&PbsProperty::UvDiffuse,
[b]&PbsProperty::UvDecal,[/b]
&PbsProperty::UvNormal,
&PbsProperty::UvSpecular,
&PbsProperty::UvRoughness,
&PbsProperty::UvDetailWeight,
&PbsProperty::UvDetail0,
&PbsProperty::UvDetail1,
&PbsProperty::UvDetail2,
&PbsProperty::UvDetail3,
&PbsProperty::UvDetailNm0,
&PbsProperty::UvDetailNm1,
&PbsProperty::UvDetailNm2,
&PbsProperty::UvDetailNm3
};
Code: Select all
setTextureProperty( PbsProperty::DiffuseMap, datablock, PBSM_DIFFUSE );
[b]setTextureProperty( PbsProperty::DecalMap, datablock, PBSM_DECAL );[/b]
setTextureProperty( PbsProperty::NormalMapTex, datablock, PBSM_NORMAL );
setTextureProperty( PbsProperty::SpecularMap, datablock, PBSM_SPECULAR );
setTextureProperty( PbsProperty::RoughnessMap, datablock, PBSM_ROUGHNESS );
setTextureProperty( PbsProperty::EnvProbeMap, datablock, PBSM_REFLECTION );
setTextureProperty( PbsProperty::DetailWeightMap,datablock, PBSM_DETAIL_WEIGHT );
This finishes the HlmsPbs changes.
Now for the datablock. This bit is a little more complicated, because we'll work simultaneously with 3 blocks of code, and they absolutely NEED to be in sync. The 3 portions are:
C++ Material : In OgreHlmsPbsDatablock.h
Code: Select all
float mkDr, mkDg, mkDb; //kD
float _padding0;
float mkSr, mkSg, mkSb; //kS
float mRoughness;
float mFresnelR, mFresnelG, mFresnelB; //F0
float mTransparencyValue;
float mDetailNormalWeight[4];
float mDetailWeight[4];
Vector4 mDetailsOffsetScale[8];
uint16 mTexIndices[NUM_PBSM_TEXTURE_TYPES];
float mNormalMapWeight;
Code: Select all
struct Material
{
vec4 kD; //kD.w is alpha_test_threshold
vec4 kS; //kS.w is roughness
//Fresnel coefficient, may be per colour component (vec3) or scalar (float)
//F0.w is transparency
vec4 F0;
vec4 normalWeights;
vec4 cDetailWeights;
vec4 detailOffsetScaleD[4];
vec4 detailOffsetScaleN[4];
uvec4 indices0_3;
//uintBitsToFloat( indices4_7.w ) contains mNormalMapWeight.
uvec4 indices4_7;
};
Code: Select all
const size_t HlmsPbsDatablock::MaterialSizeInGpu = 52 * 4 + NUM_PBSM_TEXTURE_TYPES * 2 + 4;
Code: Select all
memcpy( dstPtr, &mkDr, MaterialSizeInGpu );
So in one side we have the C++ Material, in the other side the GPU Material, and the glue between them in MaterialSizeInGpu.
What will be the result then? Let's explore what each GPU variable will be storing after the C++ send it's variables over:
kD.xyzw <- mkDr, mkDg, mkDb, _padding0;
kS.xyzw <- mkSr, mkSg, mkSd, mRoughness;
F0.xyzw <- mFresnelR, mFresnelG, mFresnelB, mTransparencyValue;
normalWeights.xyzw <- mDetailNormalWeight[0, 1, 2, 3];
cDetailWeights.xyzw <- mDetailWeight[0, 1, 2, 3];
detailOffsetScaleD[0, 1, 2, 3].xyzw and detailOffsetScaleN[0, 1, 2, 3].xyzw <- mDetailsOffsetScale[0, 1, 2, 3, 4, 5, 6, 7].xyzw;
indices0_3.x <- mTexIndices[0] concat mTexIndices[1];
indices0_3.y <- mTexIndices[2] concat mTexIndices[3];
indices0_3.z <- mTexIndices[4] concat mTexIndices[5];
indices0_3.w <- mTexIndices[6] concat mTexIndices[7];
indices4_7.x <- mTexIndices[8] concat mTexIndices[9];
indices4_7.y <- mTexIndices[10] concat mTexIndices[11];
indices4_7.z <- mTexIndices[12] concat mTexIndices[13];
indices4_7.w <- mNormalMapWeight;
They are perfectly synced. But now we went and added one more texture, meaning one more index to mTexIndices. So if we do nothing here, we'll end up with the following to the indices4_7:
indices4_7.x <- mTexIndices[8] concat mTexIndices[9];
indices4_7.y <- mTexIndices[10] concat mTexIndices[11];
indices4_7.z <- mTexIndices[12] concat mTexIndices[13];
indices4_7.w <- mTexIndices[14] concat (first 16 bits of mNormalMapWeight);
Note that mNormalMapWeight is now broken. There's actually a few different ways to fix this, one would be to move mNormalMapWeight declaration to before mTexIndices and grab it in the GPU with a single float., another one would be to declare an extra float in the end of the GPU material, which would receive the remaining 16 bits from mNormalMapWeight, than we would reconstruct it from indices4_7.w and this float. But let's to a third way here, let's just push the mNormalMapWeight 16 bits to the front so it ends up directly where it has to be.
To do this we'll do the following changes:
Code: Select all
uint16 mTexIndices[NUM_PBSM_TEXTURE_TYPES [b]+ 1[/b]];
Code: Select all
const size_t HlmsPbsDatablock::MaterialSizeInGpu = 52 * 4 + (NUM_PBSM_TEXTURE_TYPES [b] + 1[/b])* 2 + 4;
Code: Select all
uvec4 indices0_3;
uvec4 indices4_7;
float normalMapWeight;
(Continues...)