Fr/Parallax Corrected Cubemaps

== Qu'est-ce Que C'est ? ==
Par défaut, Les cubemaps du sont naïvement reflétés sur le terrain et ne suivent pas la perspective du joueur. Cela provoque une réflexion irréaliste pour la plupart des surfaces. Une solution possible est de corriger la parallaxe des cubemaps en fonction de la position de la caméra du joueur, en utilisant un shader personnalisé et un trigger boîtes de délimitation pour un cubemap. Ce tutoriel, basé directement sur le travail de Brian Charles , vous montrera comment faire exactement cela.
La vidéo de Brian Charles qui présente l’avant et l’après de ce tutoriel se trouve ici : Parallax Corrected Cubemaps in the Source Engine 
== Le Code ==
Avant de démarrer, nous aurons besoin de ce fichier :
* 
Que vous mettrez dans 
Maintenant allons-y, d’accord ?
Aller dans votre everything.sln solution et ouvert cubemap.cpp
=== ===
Maintenant, ajoutez ceci sous (pas à l’intérieur) la fonctionSideHasCubemapAndWasntManuallyReferenced( int iSide ) :
char *g_pParallaxObbStrs[MAX_MAP_CUBEMAPSAMPLES];
Ensuite, juste en dessous, changez la fonctionCubeMap_InsertSample( ... )pour ressembler à ceci :
void Cubemap_InsertSample( const Vector &origin, int size, char *pParallaxObbStr = "" )
Et à l’intérieur de cette fonction, en haut, ajoutez ceci :
g_pParallaxObbStrs[g_nCubemapSamples] = pParallaxObbStr;
Maintenant descendre à la fonctionPatchEnvmapForMaterialAndDependents( ... )et le changer pour ressembler à ceci :
static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture, const char *pParallaxObbMatrix = "" )
Dans la même fonction, faites défiler légèrement vers le bas jusqu’à la conditionif ( pDependentMaterial )et changer la ligne en ceci :
	bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture, pParallaxObbMatrix );
Faites défiler un peu vers le bas jusqu’à trouver cette ligne :
	MaterialPatchInfo_t pPatchInfo[2];
Puis changez 2 en 6, comme ça :
	MaterialPatchInfo_t pPatchInfo[6];
Faites un autre petit défilement vers le bas, et au-dessus de cette ligne :
	char pDependentPatchedMaterialName[1024];
Ajouter :
	// Parallax cubemap matrix
	CUtlVector<char *> matRowList;
	if ( pParallaxObbMatrix[0] != '\0' )
	{
		V_SplitString( pParallaxObbMatrix, ";", matRowList );
	
		pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB1";
		pPatchInfo[nPatchCount].m_pValue = matRowList[0];
		++nPatchCount;
		pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB2";
		pPatchInfo[nPatchCount].m_pValue = matRowList[1];
		++nPatchCount;
		pPatchInfo[nPatchCount].m_pKey = "$envMapParallaxOBB3";
		pPatchInfo[nPatchCount].m_pValue = matRowList[2];
		++nPatchCount;
		pPatchInfo[nPatchCount].m_pKey = "$envMapOrigin";
		pPatchInfo[nPatchCount].m_pValue = matRowList[3];
		++nPatchCount;
	}
Au bas de la fonction, vous trouverez cette ligne :
	CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
Changez-le en ceci :
	CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_INSERT );
Puis ci-dessous qui ajoutent ceci :
	// Clean up parallax stuff
	matRowList.PurgeAndDeleteElements();
Faire défiler jusqu’à la fonctionCubemap_CreateTexInfo( ... )et le changer en ceci :
static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3], int cubemapIndex )
Faites défiler un peu vers le bas dans cette fonction et trouvez cette ligne :
	GeneratePatchedName( "c", info, false, pTextureName, 1024 );
Maintenant en dessous de cette ligne ajouter ceci :
	// Append origin info if this cubemap has a parallax OBB
	char originAppendedString[1024] = "";
	if ( g_pParallaxObbStrs[cubemapIndex][0] != '\0' )
	{
		Q_snprintf( originAppendedString, 1024, "%s;[%d %d %d]", g_pParallaxObbStrs[cubemapIndex], origin[0], origin[1], origin[2] );
	}
Un peu plus bas, tu verras c'est condition si :
	if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
Remplacez-le par :
	if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName, originAppendedString ) )
Faites défiler la page jusqu’à trouver la fonctionCubemap_FixupBrushSidesMaterials( void ), et à l’intérieur de lui trouver cette ligne :
	pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
Changez-le en ceci :
	pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin, cubemapID );
Faire défiler jusqu’à la fonctionCubemap_AttachDefaultCubemapToSpecularSides( void ), et à l’intérieur de lui trouver cette ligne :
	pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
Changez-le en ceci :
	pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin, iCubemap );
Maintenant, nous en avons fini avec cubemap.cpp, nous pouvons maintenant passer à vbsp.h
=== ===
Maintenant trouver cette ligne :
void Cubemap_InsertSample( const Vector& origin, int size );
Et change ça en ceci :
void Cubemap_InsertSample( const Vector &origin, int size, char *pParallaxObbStr );
Et au-dessus de cela ajouter ceci :
extern char *g_pParallaxObbStrs[MAX_MAP_CUBEMAPSAMPLES];
Nous en avons terminé avec vbsp.h, Passons maintenant à map.cpp
=== ===
Tout en haut, ajoutez ce qui suit :
#include "matrixinvert.h"
puis faites défiler vers le bas jusqu'à la functionLoadEntityCallback( ... ), puis à l’intérieur trouver cette ligne :
	const char *pSideListStr = ValueForKey( mapent, "sides" );
Maintenant en dessous de cette ligne, ajouter :
	char *pParallaxObbStr = ValueForKey( mapent, "parallaxobb" );
Puis un tout petit peu plus bas trouver cette ligne :
	Cubemap_InsertSample( mapent->origin, size );
Et change ça en ceci :
	Cubemap_InsertSample( mapent->origin, size, pParallaxObbStr );
Maintenant sous le parent de la condition if où on est, ajoutez ça :
	//
	// parallax_obb brushes are removed after the transformation matrix is found and saved into 
	// the entity's data (ent will be removed after data transferred to patched materials)
	//
	if ( !strcmp( "parallax_obb", pClassName ) )
	{
		matrix3x4_t obbMatrix, invObbMatrix;
		SetIdentityMatrix( obbMatrix );
		SetIdentityMatrix( invObbMatrix );
		// Get corner and its 3 edges (scaled, local x, y, and z axes)
		mapbrush_t *brush = &mapbrushes[mapent->firstbrush];
		Vector corner, x, y, z;
		// Find first valid winding (with these whiles, if not enough valid windings then identity matrix is passed through to VMTs)
		int i = 0;
		while ( i < brush->numsides )
		{
			winding_t *wind = brush->original_sides[i].winding;
			if ( !wind )
			{
				i++;
				continue;
			}
			corner = wind->p[0];
			y = wind->p[1] - corner;
			z = wind->p[3] - corner;
			x = CrossProduct( y, z ).Normalized();
			i++;
			break;
		}
		// Skip second valid winding (opposite face from first, unusable for finding Z's length)
		while ( i < brush->numsides )
		{
			winding_t *wind = brush->original_sides[i].winding;
			if ( !wind )
			{
				i++;
				continue;
			}
			i++;
			break;
		}
		// Find third valid winding
		while ( i < brush->numsides )
		{
			winding_t *wind = brush->original_sides[i].winding;
			if ( !wind )
			{
				i++;
				continue;
			}
			// Find length of X
			// Start with diagonal, then scale X by the projection of diag onto X
			Vector diag = wind->p[0] - wind->p[2];
			x *= abs( DotProduct( diag, x ) );
			// Build transformation matrix (what is needed to turn a [0,0,0] - [1,1,1] cube into this brush)
			MatrixSetColumn( x, 0, obbMatrix );
			MatrixSetColumn( y, 1, obbMatrix );
			MatrixSetColumn( z, 2, obbMatrix );
			MatrixSetColumn( corner, 3, obbMatrix );
			// Find inverse (we need the world to local matrix, "transformationmatrix" is kind of a misnomer)
			MatrixInversion( obbMatrix, invObbMatrix );
			break;
		}
			
		char szMatrix[1024];
		Q_snprintf( szMatrix, 1024, "[%f %f %f %f];[%f %f %f %f];[%f %f %f %f]", invObbMatrix[0][0], invObbMatrix[0][1], invObbMatrix[0][2], invObbMatrix[0][3], invObbMatrix[1][0], invObbMatrix[1][1], invObbMatrix[1][2], invObbMatrix[1][3], invObbMatrix[2][0], invObbMatrix[2][1], invObbMatrix[2][2], invObbMatrix[2][3] );
		SetKeyValue( mapent, "transformationmatrix", szMatrix );
		return ( ChunkFile_Ok );
	}
Faites maintenant défiler jusqu’à la fonctionLoadMapFile( ... ), à l’intérieur, défilez vers le bas jusqu’à ce que vous trouviez cette ligne :
	if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF))
Maintenant au-dessus de cette ligne, ajoutez ceci :
	// Fill out parallax obb matrix array
	for ( int i = 0; i < g_nCubemapSamples; i++ )
	{
		if ( g_pParallaxObbStrs[i][0] != '\0' )
		{
			entity_t *obbEnt = EntityByName( g_pParallaxObbStrs[i] );
			g_pParallaxObbStrs[i] = ValueForKey( obbEnt, "transformationmatrix" );
		}
	}
	// Remove parallax_obb entities (in a nice slow linear search)
	for ( int i = 0; i < g_MainMap->num_entities; i++ )
	{
		entity_t *mapent = &g_MainMap->entities[i];
		const char *pClassName = ValueForKey( mapent, "classname" );
		if ( !strcmp( "parallax_obb", pClassName ) )
		{
			mapent->numbrushes = 0;
			mapent->epairs = NULL;
		}
	}
Et ça devrait être bon ! Essayez de compiler le projet VBSP, il devrait compiler sans aucune erreur.
Maintenant tout ce qui reste est de déplacer le fraîchement compilé fichier vbsp.exe à votre répertoire bin/ de votre jeu.
== Les Shaders ==
Maintenant, le truc fun : l'édition de shader! Les Cubemaps corrigés en fonction de la parallaxe qui seront désormais intégrés dans vos matériaux ont besoin d'un shader LightmappedGeneric personnalisé pour qu’ils soient correctement affichés.
Commençons simplement. Pour éditer les shaders, vous devrez ouvrir votre fichier . Points bonus si vous l’aviez déjà ouvert !
=== Fichiers Shader C++ ===
Tout d’abord, faisons en sorte que le shader LightmappedGeneric soit familier avec les Boîtes de délimitation pour la parallaxe corrigées que vous avez stocké dans le matériel.
==== ====
Ajouter les lignes suivantes à la fin de la structureLightmappedGeneric_DX9_Vars_t(après le membreint m_nOutlineEnd1;) :
	// Parallax cubemaps
	int m_nEnvmapParallaxObb1;
	int m_nEnvmapParallaxObb2;
	int m_nEnvmapParallaxObb3;
	int m_nEnvmapOrigin;
==== ====
Comme nous l’avons déjà dit, nous devons transformer cela en un shader SDK, puisque compiler un shader par défaut LightmappedGeneric fonctionne, mais vous ne pouvez pas l’utiliser dans le jeu. Modifier la ligne :
BEGIN_VS_SHADER( LightmappedGeneric,
				 "Help for LightmappedGeneric" )
en ça :
BEGIN_VS_SHADER( SDK_LightmappedGeneric, "Help for SDK_LightmappedGeneric" )
Maintenant, nous avons besoin du fichier d’implémentation pour remplir ces paramètres shader. Ajouter les lignes suivantes à l’intérieur du bloc
BEGIN_SHADER_PARAMS
en haut de ce fichier, après cette line:
	SHADER_PARAM( OUTLINEEND1, SHADER_PARAM_TYPE_FLOAT, "0.0", "outer end value for outline")
Ajouter ça :
	// Parallax cubemaps
	SHADER_PARAM( ENVMAPPARALLAXOBB1, SHADER_PARAM_TYPE_VEC4, "[1 0 0 0]", "The first line of the parallax correction OBB matrix" )
	SHADER_PARAM( ENVMAPPARALLAXOBB2, SHADER_PARAM_TYPE_VEC4, "[0 1 0 0]", "The second line of the parallax correction OBB matrix" )
	SHADER_PARAM( ENVMAPPARALLAXOBB3, SHADER_PARAM_TYPE_VEC4, "[0 0 1 0]", "The third line of the parallax correction OBB matrix" )
	SHADER_PARAM( ENVMAPORIGIN, SHADER_PARAM_TYPE_VEC3, "[0 0 0]", "The world space position of the env_cubemap being corrected" )
Si vous avez fait vos devoirs dans la section Shader Authoring , vous saurez qu’il s’agit de définir les paramètres shader à l’intérieur du fichier matériel. Maintenant, attribuons-les aux nouveaux membres de la structure. Dans le même fichier, à l’intérieur de la fonction
void SetupVars( LightmappedGeneric_DX9_Vars_t& info )
, ajouter les lignes suivantes à sa fin (après la ligneinfo.m_nOutlineEnd1 = OUTLINEEND1;):
	// Parallax cubemaps
	info.m_nEnvmapParallaxObb1 = ENVMAPPARALLAXOBB1;
	info.m_nEnvmapParallaxObb2 = ENVMAPPARALLAXOBB2;
	info.m_nEnvmapParallaxObb3 = ENVMAPPARALLAXOBB3;
	info.m_nEnvmapOrigin = ENVMAPORIGIN;
Super, maintenant notre code du shader connait nos paramètres patchés. Mais nous devons encore faire des trucs avec eux !
==== ====
Comme je l’ai déjà mentionné, nous devons modifier certains préfixes. Pour vos inclusions, changez les lignes :
#include "lightmappedgeneric_ps20.inc"
#include "lightmappedgeneric_vs20.inc"
#include "lightmappedgeneric_ps20b.inc"
en ça :
#include "SDK_lightmappedgeneric_ps20.inc"
#include "SDK_lightmappedgeneric_vs20.inc"
#include "SDK_lightmappedgeneric_ps20b.inc"
Vous allez les générer dans un moment, ne vous inquiétez pas. Mais maintenant cela va causer d’autres problèmes dans le code! Vous devrez remplacer toutes les instances du code du shader par défaut par votre SDK nouvellement copié.
Par exemple,
	DECLARE_STATIC_VERTEX_SHADER( lightmappedgeneric_vs20 );
en :
	DECLARE_STATIC_VERTEX_SHADER( sdk_lightmappedgeneric_vs20 );
FAITE LE POUR CHAQUE INSTANCE ! Modifier les importations vous aidera à compiler ce module dans un instant, car les anciens shaders lightmappedgeneric_* seront soulignés en rouge, pour vous aider à trouver où vous avez manqué un endroit spécifique.
Tout d’abord, faisons une vérification de sécurité. Nous devons nous assurer que nous avons tous les composants requis pour les Cubemaps Corrigés en Fonction de la Parallaxe fonctionnent correctement. À l'intérieur du
void InitParamsLightmappedGeneric_DX9( CBaseVSShader *pShader, IMaterialVar** params, const char *pMaterialName, LightmappedGeneric_DX9_Vars_t &info )
 
près du haut de ce fichier, ajoutez les lignes de code suivantes à la fin (vous l’avez deviné), après la ligneInitFloatParam( info.m_nOutlineAlpha, params, 1.0 );:
	// Parallax cubemaps
	// Cubemap parallax correction requires all 4 lines (if the 2nd, 3rd, or 4th are undef, undef the first one (checking done on first var)
	if ( !( params[info.m_nEnvmapParallaxObb2]->IsDefined() && params[info.m_nEnvmapParallaxObb3]->IsDefined() && params[info.m_nEnvmapOrigin]->IsDefined() ) )
	{
		params[info.m_nEnvmapParallaxObb1]->SetUndefined();
	}
Cela permet au code de la correction de parallaxe de ne pas fonctionner s’il y a un problème avec vos matériaux patchés ou quelque chose.
Ensuite, nous devons dire au shader lui-même si le code ci-dessus que nous avons ajouté retourne true, et si oui, pour faire le rendu des Cubemaps Corrigés en Fonction de la Parallaxe. À l'intérieur de la fonction
void DrawLightmappedGeneric_DX9_Internal( ... )
, après la line :
	bool hasNormalMapAlphaEnvmapMask = IS_FLAG_SET( MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK );
Ajouter la déclaration booléenne suivante :
	// Parallax cubemaps
	bool hasParallaxCorrection = params[info.m_nEnvmapParallaxObb1]->IsDefined();
Maintenant, vérifions ce booléen. Plus bas dans cette fonction gargantuesque, nous devons définir le combo sur le pixel shader. À l'intérieure du bloc :
	if ( g_pHardwareConfig->SupportsPixelShaders_2_b() )
, ajouter la ligne suivante AVANT la
	SET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20b );
ligne:
	// Parallax cubemaps enabled for 2_0b and onwards
	SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, hasParallaxCorrection );
Si vous êtes capable de lire, vous pouvez commencer à réaliser ce que nous allons faire ensuite : depuis Shader Model 2.0 était à la mode pour les jours GoldSource , et quelque chose comme la parallaxe corrigée cubemaps est un peu élevé-technologie pour cela, nous devons l’activer uniquement si la carte graphique fonctionnant sur le moteur prend en charge Shader Model 2.0b (et plus). Donc un peu plus loin dans cette méga-fonction, à l’intérieur de l’instructionelsepour le code ci-dessus, JUSTE AVANT la lineSET_STATIC_PIXEL_SHADER( sdk_lightmappedgeneric_ps20 );, ajoutez ce qui suit :
	// Parallax cubemaps
	SET_STATIC_PIXEL_SHADER_COMBO( PARALLAXCORRECT, 0 ); // No parallax cubemaps with ps_2_0 :(
Donc maintenant notre pixel shader (que nous modifions dans la section suivante) sait si oui ou non il devrait gérer les Cubemaps Corrigés en Fonction de la Parallaxe. 
Enfin, mais certainement pas le moins important (On n’a même pas encore édité le shader !), nous devons envoyer au pixel shader la bonne valeur à utiliser pour afficher nos Cubemaps Corrigés en Fonction de la Parallaxe. Donc, encore plus bas dans cette fonction de merde, JUSTE AVANT la lignepContextData->m_SemiStaticCmdsOut.End();, ajouter le bloc de code suivant :
	// Parallax cubemaps
	if ( hasParallaxCorrection )
	{
		pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 21, params[info.m_nEnvmapOrigin]->GetVecValue() );
		float *vecs[3];
		vecs[0] = const_cast<float *>( params[info.m_nEnvmapParallaxObb1]->GetVecValue() );
		vecs[1] = const_cast<float *>( params[info.m_nEnvmapParallaxObb2]->GetVecValue() );
		vecs[2] = const_cast<float *>( params[info.m_nEnvmapParallaxObb3]->GetVecValue() );
		float matrix[4][4];
		for ( int i = 0; i < 3; i++ )
		{
			for ( int j = 0; j < 4; j++ )
			{
				matrix[i][j] = vecs[i][j];
			}
		}
		matrix[3][0] = matrix[3][1] = matrix[3][2] = 0;
		matrix[3][3] = 1;
		pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 22, &matrix[0][0], 4 );
	}
Waouh ! Maintenant notre shader a les bonnes données. Bon, il est temps de mettre vos 8 chapeaux DirectX, on y va !
=== Les fichiers Shader ===
==== ====
Maintenant, Je sais ce que tu penses. C’est loin d’être un fichier shader ! C’est un fichu fichier d’en-tête C++ ! Mais ne vous inquiétez pas, ce fichier est inclus dans le fichier suivant que nous modifions, le fichier source shader réel (FXC). Donc tout ce code sera en fait compilé dans le shader, ce qui en fait un shader déguisée !
Tout d’abord, déclarons-nous quelques registres à accrocher. Si vous vous demandez pourquoi ce shader ne fonctionne pas avec Shader Model 2.0, voici pourquoi. Dans ce fichier, juste en dessous de ça :
const float4 g_ShadowTweaks : register( c19 );
Ajouter ça :
// Parallax cubemaps
#if ( PARALLAXCORRECT )
const float3 g_CubemapPos : register( c21 );
const float4x4 g_ObbMatrix : register( c22 ); // Through c25
#endif
Ne vous inquiétez pas de celaPARALLAXCORRECT, cela aura un peu de sens.
Maintenant, faisons le calcul principal de shader dont nous avons besoin ! Nous pouvons sauter plus loin dans le dossier, et trouver où les cubemaps sont traités, à l'intérieure du bloc if( bCubemap ), ajouter le bloc de code suivant après la ligne fresnel = fresnel * g_OneMinusFresnelReflection + g_FresnelReflection; :
	// Parallax correction (2_0b and beyond)
	// Adapted from http://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
#if !( defined( SHADER_MODEL_PS_1_1 ) || defined( SHADER_MODEL_PS_1_4 ) || defined( SHADER_MODEL_PS_2_0 ) )
#if ( PARALLAXCORRECT )
	float3 worldPos = i.worldPos_projPosZ.xyz;
	float3 positionLS = mul( float4( worldPos, 1 ), g_ObbMatrix );
	float3 rayLS = mul( reflectVect, (float3x3)g_ObbMatrix );
	float3 firstPlaneIntersect = ( float3( 1.0f, 1.0f, 1.0f ) - positionLS ) / rayLS;
	float3 secondPlaneIntersect = ( -positionLS ) / rayLS;
	float3 furthestPlane = max( firstPlaneIntersect, secondPlaneIntersect );
	float distance = min( furthestPlane.x, min( furthestPlane.y, furthestPlane.z ) );
	// Use distance in WS directly to recover intersection
	float3 intersectPositionWS = worldPos + reflectVect * distance;
	reflectVect = intersectPositionWS - g_CubemapPos;
#endif
#endif
Merveilleux. Notre shader va maintenant faire des maths sophistiquées pour mettre en parallèle le cubemap basé sur la position du joueur. Passez au fichier FXC !
==== Un Shader Par Tout Autre Nom ====
Vous devrez ensuite faire une copie de ces fichiers :
lightmappedgeneric_ps2x.fxc
lightmappedgeneric_ps11.fxc
lightmappedgeneric_vs20.fxc
Puis renommez les copies en :
SDK_lightmappedgeneric_ps2x.fxc
SDK_lightmappedgeneric_ps11.fxc
SDK_lightmappedgeneric_vs20.fxc
==== ====
Heck ouais, nous y voilà, édition des fichiers du shader! Je t’avais dit qu’on arriverait ! D’accord, tu es prêt ? Accroche-toi bien, ces modifications sont tellement dures.
Ajouter la ligne suivante, après// STATIC: "FLASHLIGHT" "0..1" [ps20b] [XBOX]:
// STATIC: "PARALLAXCORRECT" "0..1"
C’est déclarer le combo statique que notre code de la section précédente déclare. Mais attendez, nous avons encore plus!
Nous devons optimiser le shader pour ne pas compiler sous certaines conditions. Par exemple, quand shadercompile travaille sur la version Shader Model 2.0, nous pouvons lui dire de ne pas construire la version avec les Cubemaps Corrigés en Fonction de la Parallaxe à l’intérieur. De même, s’il n’y a même pas un fichu cubemap pour commencer, nous ne voulons pas que le code du correcteur de parallaxe s’exécute ! Donc après la ligne// SKIP ($DETAIL_BLEND_MODE == 11 ) && ($BUMPMAP != 0 ), ajouter:
// SKIP: $PARALLAXCORRECT && !$CUBEMAP
// SKIP: $PARALLAXCORRECT [ps20]
C’est tout ce qu’il y a à dire ! Tu peux ouvrir les yeux maintenant. En fait, j’espère qu’ils étaient ouverts pour tout ce tutoriel, parce que sinon, vous obtiendrez des résultats très intéressants.
=== Compiler le(s) shader(s) ===
Maintenant nous devons compiler le shader. Tu as du café à faire ? Un repas à préparer ? T'es amis t'attends sur Fifa ? Bien! Compiler un shader LightmappedGeneric personnalisé va prendre à peu près ce temps. Chaque fois que vous avez besoin de changer quelque chose avec le shader, il faudra 30 minutes pour le recompiler complètement! J’espère que ça marchera au premier essai.
À l’intérieur de votre fichier stdshader_dx9_20b.txt (ou de votre jeu personnalisé, je ne juge pas), assurez-vous que les shaders suivantes sont ajoutées :
SDK_lightmappedgeneric_ps2x.fxc
SDK_lightmappedgeneric_ps11.fxc
SDK_lightmappedgeneric_vs20.fxc
Maintenant, compilez. Allez faire la tâche dont je vous parlais. Ça va prendre du temps parce que c’est un script Perl compilant l’un des shaders les plus complexes, donc il a beaucoup de combinaisons à passer. Il va utiliser toutes vos ressources, aussi, donc ne soyez pas effrayé. Est-ce l’hiver? (Pour moi oui (24/12/2022, Joyeux Noël), mais, il ne neige pas?. 9°C, tu connais. Même au nord (Grand-Est, pas Lille. tkt)) Bien, il réchauffera votre chambre. (Si vous n'avez pas de four pour faire chauffer la volaille du Carrefour, C'est parfait.)
Une fois terminé, il générera les bons fichiers SDK_lightmappedgeneric_*.inc que votre code utilise, et vous devriez avoir vos fichiers SDK_lightmappedgeneric_*.vcs dans le sous-répertoire shaders/fxc/. Vous pouvez les copier dans le dossier shaders/fxc/ de votre jeu.
Avec vos nouveaux fichiers .inc, vous devriez pouvoir compiler le projet game_shader_dx9. Assurez-vous que tous les noms correspondent ! Si vous manquez une SDK_*, essayez de chercher le fichier et assurez-vous qu’il est nommé correctement ! Avec le projet compilé, vous aurez un joli game_shader_dx9.dll à copier dans le dossier bin/ de votre jeu, à côté de votre serveur et des DLL client.
== Et Après ? ==
=== Utilisation du nouveau Shader SDK_LightmappedGeneric ===
Maintenant, puisque Valve ne permet pas à la branche SDK 2013 de remplacer le shader par défaut LightmappedGeneric, vous allez devoir soit tronquer le système matériel et remplacer manuellement tous les shaders, soit simplement créer/copier les VMTs qui utilisent le shader.
=== Les fichiers FGD ===
 :
@include "YOUR FGD HERE. THIS ONE WILL OVERRIDE THE REGULAR ENV_CUBEMAP ENTITY AND ALSO ADD THE PARALLAX_OBB ENTITY. ONLY INCLUDE THIS ONE IN HAMMER."
@PointClass color(0 0 255) sidelist(sides) iconsprite("editor/env_cubemap.vmt") = env_cubemap : 
	"An entity that creates a sample point for the Cubic Environment Map."
[
	cubemapsize(choices) : "Cubemap Size" : 0 =
	[
		0 : "Default"
		1 : "1x1"
		2 : "2x2"
		3 : "4x4"
		4 : "8x8"
		5 : "16x16"
		6 : "32x32"
		7 : "64x64"
		8 : "128x128"
		9 : "256x256"
	]
	sides(sidelist) : "Brush faces": : "(Optional) Brushes faces to directly attach to the env_cubemap. Press Pick then click on faces in the 3D View to select them. Use CTRL while clicking to add or remove from the selection."
	parallaxobb(target_destination) : "Cubemap Bounds" : : "(Optional) assigns this cubemap a bounding box for parallax correction (brush entity tied to parallax_obb)."
]
@SolidClass = parallax_obb
[
	targetname(target_source) : "Name" : : "The name that other entities refer to this entity by."
]
== Conclusion ==
Et c’est tout, essayez de lancer votre mod pour voir si ça marche !
Ceci n’est actuellement testé que sur la branche SP de Source SDK 2013. La branche MP et ne sont toujours pas testés au moment de la rédaction de cet article.