Vision Nocturna

From Valve Developer Community
(Redirected from NightVision Tutorial)
Jump to: navigation, search
English (en)Русский (ru)Translate (Translate)
Abstract Coding series Discuss your thoughts - Help us develop the articles or ideas you want

Levels & XP | Optimization | Procedural Textures | Sights & Sniperrifles | Special effects | Vehicles | Threads | Save Game Files | Night Vision | Non-offensive Weapons | Dynamic Weapon Spawns | Dynamic Weapon Spawns (Advanced)


Night Vision is something that many people have been stumped on, but it has already been implemented in the NightFall mod.

Summary

This example is a very early attempt (it works), and just one of many ways to get the Night Vision Mode to work with your mod.


Setting up the ConVar

Open the file src/cl_dll/view_scene.cpp, and append the code below to the end of this ifdef

//-----------------------------------------------------------------------------
// Precache of necessary materials
//-----------------------------------------------------------------------------

#ifdef HL2_CLIENT_DLL

Found around line 170 (to go to a line number quickly, press ctrl+g)

//Precache Night Vision
CLIENTEFFECT_MATERIAL( "HUDoverlays/nightvision" )


Now jump to the end of the page (ctrl+end) and add this class

//nightfall - amckern - [email protected]
//NightVision
static void ScreenOver_f( void )
{
	IMaterial *pMaterial = materials->FindMaterial( "HUDoverlays/nightvision", TEXTURE_GROUP_OTHER, true );
//This is the texture we are going to use for the 'effect' - never use an ext on material files

	{
		static bool bDisplayed = false;
		
		if( bDisplayed )
		{
			// turn it off
			view->SetScreenOverlayMaterial( NULL );
			// Deactivate the 'light'
			cvar->FindVar("mat_fullbright")->SetValue(0);
			CLocalPlayerFilter filter;
			C_BaseEntity::EmitSound( filter, 0, "Nightfall.NightVisOff" );
			//play the off sound
		}
		else
		{
			// turn it on
			view->SetScreenOverlayMaterial( pMaterial );
			//this is the HUDoverlays/nightvision texture we made a pointer to above
			// Activate the 'light'
			cvar->FindVar("mat_fullbright")->SetValue(1);
			CLocalPlayerFilter filter;
			C_BaseEntity::EmitSound( filter, 0, "Nightfall.NightVisOn" );
			//On we go - play a sound to let the player know that the NV is on
		}
		
		bDisplayed = !bDisplayed;

		//check if fullbright has been disabled, or enabled
		if (cvar->FindVar("mat_fullbright")->GetInt() == 1)//is it on?
			cvar->FindVar("mat_fullbright")->SetValue(0);//well turn it off.
	}
}

static ConCommand r_screenover( "r_screenover", ScreenOver_f );

So what we have done here is set a bool cvar (on/off) that either enables or disables the night vision effect. If you read the comments, you will understand what each part does.

This section has been edited to keep the Fullbright cheat a cheat, to stop people exploiting it - the code has been updated from that supplied in the comments.

It might sound strange to have the off function first, but you don't want the player to be running around the game with night vision turned on from the time they spawn.

The sound

Nightfall.NightVisOn / Nightfall.NightVisOff

This code is calling on the script file nightfall_engine_sounds.txt that in turn is precached in game_sounds_manifest.txt like so

	"precache_file"		"scripts/nightfall_engine_sounds.txt"

Sounds are precached with PrecacheScript("Nightfall.NightVisOn"); in the player.cpp precache function, just in case you are wondering why the game lags when you go to use the sound effect.

You can always use a direct sound event like engine->ClientCmd( "play your_effect_sound.wav\n"); if you don't know how to make a soundscript sound. The downside of this is the game will always lag while it loads the sound file for the first time, and from what is understood, there is no way to precache a sound file directly within the code.

Nightvision with shaders

Creating nightvision with a shader requires a 5 things, the HLSL pixel and vertex shader files, the C++ shader file, the material using the shader and a way to trigger the material from the game. This assumes you have set up and know how to compile shaders as shown here. Credit goes to wraiyth for his amazingly useful tutorials on HLSL.

The pixel shader:

#include "common_ps_fxc.h"

const HALF Brightness : register (c0); //this is a variable to set how bright the NV will be.

sampler BaseTextureSampler : register( s0 ); //a sampler to get the texture this shader will be used on (in this case it will be a frame buffer)

HALF4 main(float2 iTexCoord : TEXCOORD0) : COLOR //out main function
{
	float4 vAdd = float4(0.1,0.1,0.1,0); // just a float4 for use later
	
	float4 cColor = tex2D(BaseTextureSampler,iTexCoord); //this takes our sampler and turns the rgba into floats between 0 and 1
	cColor += tex2D(BaseTextureSampler, iTexCoord.xy + 0.001); // these 3 lines blur the image slightly
	cColor += tex2D(BaseTextureSampler, iTexCoord.xy + 0.002);
	cColor += tex2D(BaseTextureSampler, iTexCoord.xy + 0.003); 
	if (((cColor.r + cColor.g + cColor.b)/3) < 0.9) // if the pixel is bright leave it bright
	{
		cColor = cColor / 4; //otherwise set it to an average color of the 4 images we just added together
	}
	
	float4 cTempColor = cColor; //a new float4 cTempColor for use later
	
	float4 cFinal = cTempColor + vAdd; //adds the floats together

	cFinal.g = (cTempColor.r + cTempColor.g + cTempColor.b)/3; // sets green the the average of rgb
	cFinal.r = 0; //removes red and blue colors
	cFinal.b = 0;
	return cFinal * Brightness; // brighten the final image and return it
}

Save this as post_nightvision_ps20.fxc.

Next up the vertex shader:

#include "common_vs_fxc.h"

void main( in float4 iPosition : POSITION, in float2 iTexCoord : TEXCOORD0, out float4 oPosition : POSITION, out float2 oTexCoord : TEXCOORD0)
{
	oPosition=iPosition;
	oTexCoord=iTexCoord;
}

Save this as PassThrough_vs20.fxc and compile the 2 shaders.

Next the C++ file:

#include "BaseVSShader.h"

#include "post_nightvision_ps20.inc"
#include "PassThrough_vs20.inc" //files generated by compiling the fxc files

BEGIN_VS_SHADER( Post_NightVision, "Help for Post_NightVision" ) //Begin the shader
	
	BEGIN_SHADER_PARAMS
	SHADER_PARAM(NVLEVEL, SHADER_PARAM_TYPE_FLOAT, "1.0", "")
	//This is our shader parameter taken from the material file called NVLEVEL, write "$nvlevel" 10 for example in the .vmt
	END_SHADER_PARAMS

	SHADER_INIT_PARAMS() //called after parameters have been initialized
	{
	}

	SHADER_FALLBACK //doesn't fallback to anything (I know this works on dx9, hasn't been tested on others)
	{
		return 0;
	}

	bool NeedsFullFrameBufferTexture( IMaterialVar **params ) const //does this need the full screen? in our case yes
	{
		return true;
	}

	SHADER_INIT //initializes the shader
	{
	}

	SHADER_DRAW
	{
		SHADOW_STATE
			{
				pShaderShadow->EnableTexture( SHADER_TEXTURE_STAGE0, true ); //enables SHADER_TEXTURE_STAGE0

				pShaderShadow->EnableDepthWrites( false ); //depth writes aren't needed 
				int fmt = VERTEX_POSITION;
				pShaderShadow->VertexShaderVertexFormat( fmt, 1, 0, 0, 0 ); //sets the vertex format for the .fxc
				pShaderShadow->SetVertexShader( "PassThrough_vs20", 0 ); //set the vertex shader
				pShaderShadow->SetPixelShader( "post_nightvision_ps20" ); //set the pixel shader
				DefaultFog();
			}
		DYNAMIC_STATE
			{
				float Scale = params[NVLEVEL]->GetFloatValue(); // get the value of NVLEVEL and turn it to a float
				float vScale[4] = {Scale, Scale, Scale, 1}; //new float using NVLEVEL values
				pShaderAPI->SetPixelShaderConstant(0, vScale); //set the first shader variable to our float
				pShaderAPI->BindFBTexture( SHADER_TEXTURE_STAGE0 ); //set the shader texture to our frame buffer
			}
		Draw(); //draw the shader
	}

END_SHADER

Add that to the game_shader_generic_sample project (or whatever other one your using) and compile it.

Now create a material in your mods materials folder all it needs is a .vmt file:

"Post_NightVision"
{
	"$basetexture" "_rt_FullFrameFB"
	"$nvlevel" 10 //can be whatever value you want
}

Now open view_scene.cpp in the client project of you mod and add the following code at the bottom:

//-----------------------------------------------------------------------------
// NightVision
//-----------------------------------------------------------------------------
static void NightVision_f ( void )
{
	C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); //get the local player 
	
	const Vector *vOrigin = &pPlayer->GetAbsOrigin(); //get the local players origin

	static bool bDisplayed; //static bool

		if ( bDisplayed )
		{
			view->SetScreenOverlayMaterial( null ); //set screenoverlay to nothing
			CLocalPlayerFilter filter;
			pPlayer->EmitSound(filter, 0, "TestMod.NightVisionOff", vOrigin); //and play sound
		}
		else
		{
			IMaterial *pMaterial = materials->FindMaterial( "NightVision", TEXTURE_GROUP_OTHER, true); //set pMaterial to our texture
			view->SetScreenOverlayMaterial( pMaterial ); //and overlay it on the screen
			CLocalPlayerFilter filter;
			pPlayer->EmitSound(filter, 0, "TestMod.NightVisionOn", vOrigin); //and play a sound
		}

		bDisplayed = !bDisplayed; // flip flop the bool
	
}

//night vision console command
static ConCommand r_nightvision( "r_nightvision", NightVision_f ); // console command to trigger the function, bind it by typing bind n r_nightvision.

Compile it and you should have fully working shader driven night vision, customize it to your desire. You can also precache the sounds and material as explained earlier.