Hello everyone! This is the second part of 'Implementing Basic SSAO Shader' tutorial. If you haven't the first part, here it is.
In this part I will show you how to make SSAO work in game and say some words about blur.
SSAO Blur
The one of right ways (that gives better results) to blur SSAO is to use the depth blur. In Crossroads I used simple 3x3 gaussian blur found in the Internet, here is code for it, but you really should look for better shader to blur SSAO.
Pixel Shader
Lets name it 'gauss_blur_3x3.fxc'. Vertex shader code is the same as 'ssao_vs30' or 'ssao_combine_vs30'.
// Original shader by brooknovak
#include "common_ps_fxc.h"
sampler texturesampler : register( s0 );
float2 g_TexelSize : register( c0 );
struct PS_INPUT
{
HALF2 vTexCoord : TEXCOORD0;
};
// Blurs using a 3x3 filter kernel
float4 main( const PS_INPUT i ) : COLOR
{
// TOP ROW
float4 s11 = tex2D( texturesampler, i.vTexCoord + float2( -g_TexelSize ) ); // LEFT
float4 s12 = tex2D( texturesampler, i.vTexCoord + float2( 0, -g_TexelSize.y ) ); // MIDDLE
float4 s13 = tex2D( texturesampler, i.vTexCoord + float2( g_TexelSize.x , -g_TexelSize.y ) ); // RIGHT
// MIDDLE ROW
float4 s21 = tex2D( texturesampler, i.vTexCoord + float2( -g_TexelSize.x, 0 ) ); // LEFT
float4 col = tex2D( texturesampler, i.vTexCoord ); // DEAD CENTER
float4 s23 = tex2D( texturesampler, i.vTexCoord + float2( -g_TexelSize.x, 0 ) ); // RIGHT
// LAST ROW
float4 s31 = tex2D( texturesampler, i.vTexCoord + float2( -g_TexelSize.x, g_TexelSize.y ) ); // LEFT
float4 s32 = tex2D( texturesampler, i.vTexCoord + float2( 0, g_TexelSize.y ) ); // MIDDLE
float4 s33 = tex2D( texturesampler, i.vTexCoord + float2( g_TexelSize ) ); // RIGHT
// Average the color with surrounding samples
col = ( col + s11 + s12 + s13 + s21 + s23 + s31 + s32 + s33 ) / 9;
return col;
}
Okay, that's all with blur.
Implementing shaders into 'game_shader_dx9.dll'
If you don't know what is shader dll - read this.
Create three files: 'ssao.cpp', 'ssao_combine.cpp', 'gaussian_blur_3x3.cpp'. A cpp for each shader. Then add them to the project (in MS Visual Studio right click on 'game_shader' project, then click 'Add object', then choose 'Existing object', then choose the files you created).
Then copy the following code in 'ssao.cpp':
//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose: Crossroads devtest
//
// $NoKeywords: $
//=============================================================================
#include "BaseVSShader.h"
#include "ssao_vs30.inc"
#include "ssao_ps30.inc"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// SHADER PARAMS DEFINED IN SHADER FXC CODE
ConVar cr_ssao_samples( "cr_ssao_samples", "8" );
ConVar cr_ssao_contrast( "cr_ssao_contrast", "2.0" );
ConVar cr_ssao_radius( "cr_ssao_radius", "16" );
ConVar cr_ssao_bias( "cr_ssao_bias", "0.02" );
ConVar cr_ssao_bias_offset( "cr_ssao_bias_offset", "0.05" );
ConVar cr_ssao_illuminfluence( "cr_ssao_illuminfluence", "5.0" );
ConVar cr_ssao_zfar( "cr_ssao_zfar", "8.0" );
ConVar cr_ssao_znear( "cr_ssao_znear", "1.0" );
BEGIN_VS_SHADER_FLAGS( SSAO, "Help for SSAO", SHADER_NOT_EDITABLE )
BEGIN_SHADER_PARAMS
SHADER_PARAM( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_FullFrameFB", "Framebuffer" )
END_SHADER_PARAMS
SHADER_INIT_PARAMS()
{
SET_FLAGS2( MATERIAL_VAR2_NEEDS_FULL_FRAME_BUFFER_TEXTURE );
}
SHADER_FALLBACK
{
return 0;
}
SHADER_INIT
{
if( params[BASETEXTURE]->IsDefined() )
{
LoadTexture( BASETEXTURE );
}
}
SHADER_DRAW
{
SHADOW_STATE
{
pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 1, 0, 0 );
pShaderShadow->EnableTexture( SHADER_SAMPLER0, true );
// Render targets are pegged as sRGB on POSIX, so just force these reads and writes
bool bForceSRGBReadAndWrite = IsOSX() && g_pHardwareConfig->CanDoSRGBReadFromRTs();
pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, bForceSRGBReadAndWrite );
pShaderShadow->EnableSRGBWrite( bForceSRGBReadAndWrite );
DECLARE_STATIC_VERTEX_SHADER( ssao_vs30 );
SET_STATIC_VERTEX_SHADER( ssao_vs30 );
DECLARE_STATIC_PIXEL_SHADER( ssao_ps30 );
SET_STATIC_PIXEL_SHADER( ssao_ps30 );
}
DYNAMIC_STATE
{
BindTexture( SHADER_SAMPLER0, BASETEXTURE, -1 );
ITexture *src_texture = params[BASETEXTURE]->GetTextureValue();
int width = src_texture->GetActualWidth();
int height = src_texture->GetActualHeight();
float g_TexelSize[2] = { 1.0f / float( width ), 1.0f / float( height ) };
pShaderAPI->SetPixelShaderConstant( 0, g_TexelSize );
DECLARE_DYNAMIC_VERTEX_SHADER( ssao_vs30 );
SET_DYNAMIC_VERTEX_SHADER( ssao_vs30 );
DECLARE_DYNAMIC_PIXEL_SHADER( ssao_ps30 );
SET_DYNAMIC_PIXEL_SHADER( ssao_ps30 );
float samples = cr_ssao_samples.GetInt();
float contrast = cr_ssao_contrast.GetFloat();
float radius = cr_ssao_radius.GetFloat();
float bias = cr_ssao_bias.GetFloat();
float biasoffset = cr_ssao_bias_offset.GetFloat();
float illuminf = cr_ssao_illuminfluence.GetFloat();
float zfar = cr_ssao_zfar.GetFloat();
float znear = cr_ssao_znear.GetFloat();
pShaderAPI->SetPixelShaderConstant( 1, &samples );
pShaderAPI->SetPixelShaderConstant( 2, &radius );
pShaderAPI->SetPixelShaderConstant( 3, &bias );
pShaderAPI->SetPixelShaderConstant( 4, &illuminf );
pShaderAPI->SetPixelShaderConstant( 5, &contrast );
pShaderAPI->SetPixelShaderConstant( 6, &znear );
pShaderAPI->SetPixelShaderConstant( 7, &zfar );
pShaderAPI->SetPixelShaderConstant( 8, &biasoffset );
}
Draw();
}
END_SHADER
Then goes 'ssao_combine.cpp':
//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "BaseVSShader.h"
#include "ssao_combine_vs30.inc"
#include "ssao_combine_ps30.inc"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_VS_SHADER_FLAGS( SSAOCOMBINE, "Help for SSAO Combine", SHADER_NOT_EDITABLE )
BEGIN_SHADER_PARAMS
SHADER_PARAM( SSAOTEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_SSAO", "SSAO" )
SHADER_PARAM( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_FullFrameFB", "Framebuffer" )
END_SHADER_PARAMS
SHADER_INIT_PARAMS()
{
SET_FLAGS2( MATERIAL_VAR2_NEEDS_FULL_FRAME_BUFFER_TEXTURE );
}
SHADER_FALLBACK
{
return 0;
}
SHADER_INIT
{
if( params[SSAOTEXTURE]->IsDefined() )
{
LoadTexture( SSAOTEXTURE );
}
if( params[BASETEXTURE]->IsDefined() )
{
LoadTexture( BASETEXTURE );
}
}
SHADER_DRAW
{
SHADOW_STATE
{
pShaderShadow->EnableDepthWrites( false );
pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 1, 0, 0 );
DECLARE_STATIC_VERTEX_SHADER( ssao_combine_vs30 );
SET_STATIC_VERTEX_SHADER( ssao_combine_vs30 );
pShaderShadow->EnableTexture( SHADER_SAMPLER0, true );
pShaderShadow->EnableTexture( SHADER_SAMPLER1, true );
DECLARE_STATIC_PIXEL_SHADER( ssao_combine_ps30 );
SET_STATIC_PIXEL_SHADER( ssao_combine_ps30 );
}
DYNAMIC_STATE
{
BindTexture( SHADER_SAMPLER0, SSAOTEXTURE, -1 );
BindTexture( SHADER_SAMPLER1, BASETEXTURE, -1 );
DECLARE_DYNAMIC_VERTEX_SHADER( ssao_combine_vs30 );
SET_DYNAMIC_VERTEX_SHADER( ssao_combine_vs30 );
DECLARE_DYNAMIC_PIXEL_SHADER( ssao_combine_ps30 );
SET_DYNAMIC_PIXEL_SHADER( ssao_combine_ps30 );
}
Draw();
}
END_SHADER
Then goes 'gaussian_blur_3x3.cpp':
//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "BaseVSShader.h"
#include "gauss_blur_3x3_vs30.inc"
#include "gauss_blur_3x3_ps30.inc"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
BEGIN_VS_SHADER_FLAGS( GAUSSBLUR3X3, "Help for GAUSSBLUR3X3", SHADER_NOT_EDITABLE )
BEGIN_SHADER_PARAMS
SHADER_PARAM( BASETEXTURE, SHADER_PARAM_TYPE_TEXTURE, "_rt_FullFrameFB", "Render Target" )
END_SHADER_PARAMS
SHADER_FALLBACK
{
return 0;
}
SHADER_INIT
{
if( params[BASETEXTURE]->IsDefined() )
{
LoadTexture( BASETEXTURE );
}
}
SHADER_DRAW
{
SHADOW_STATE
{
pShaderShadow->EnableDepthWrites( false );
pShaderShadow->EnableAlphaWrites( true );
pShaderShadow->EnableTexture( SHADER_SAMPLER0, true );
pShaderShadow->VertexShaderVertexFormat( VERTEX_POSITION, 1, 0, 0 );
// Render targets are pegged as sRGB on POSIX, so just force these reads and writes
bool bForceSRGBReadAndWrite = IsOSX() && g_pHardwareConfig->CanDoSRGBReadFromRTs();
pShaderShadow->EnableSRGBRead( SHADER_SAMPLER0, bForceSRGBReadAndWrite );
pShaderShadow->EnableSRGBWrite( bForceSRGBReadAndWrite );
DECLARE_STATIC_VERTEX_SHADER( gauss_blur_3x3_vs30 );
SET_STATIC_VERTEX_SHADER( gauss_blur_3x3_vs30 );
DECLARE_STATIC_PIXEL_SHADER( gauss_blur_3x3_ps30 );
SET_STATIC_PIXEL_SHADER( gauss_blur_3x3_ps30 );
}
DYNAMIC_STATE
{
BindTexture( SHADER_SAMPLER0, BASETEXTURE, -1 );
ITexture *src_texture = params[BASETEXTURE]->GetTextureValue();
int width = src_texture->GetActualWidth();
int height = src_texture->GetActualHeight();
float dX = 1.0f / width;
float dY = 1.0f / height;
float fTexelSize[2] = { dX, dY };
pShaderAPI->SetPixelShaderConstant( 0, fTexelSize );
DECLARE_DYNAMIC_VERTEX_SHADER( gauss_blur_3x3_vs30 );
SET_DYNAMIC_VERTEX_SHADER( gauss_blur_3x3_vs30 );
DECLARE_DYNAMIC_PIXEL_SHADER( gauss_blur_3x3_ps30 );
SET_DYNAMIC_PIXEL_SHADER( gauss_blur_3x3_ps30 );
}
Draw();
}
END_SHADER
Okay, now you can compile the shader dll. If you are lucky it should compile with no errors.
Thats all with shader dll, the only thing left is to create new render target for ssao pass and apply the shader material to screen.
Creating render target for SSAO:
Open your usual mod source code and in 'view.cpp' navigate to:
void CViewRender::Init( void )
and add this code to the end of that function:
int iW, iH;
materials->GetBackBufferDimensions( iW, iH );
materials->BeginRenderTargetAllocation();
materials->CreateNamedRenderTargetTextureEx( "_rt_SSAO", iW, iH, RT_SIZE_NO_CHANGE, materials->GetBackBufferFormat(),
MATERIAL_RT_DEPTH_NONE, TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_RENDERTARGET, 0 );
materials->EndRenderTargetAllocation();
Then go to function:
CViewRender::RenderView
and put the following code right before line 'if ( mat_viewportupscale.GetBool() && mat_viewportscale.GetFloat() < 1.0f )'
// Crossroads devtest SSAO
if( cr_ssao_enable.GetBool() )
{
DoSSAO( view );
}
Then navigate to line:
ConVar r_worldlistcache( "r_worldlistcache", "1" );
And right after it add this:
//Crossroads devtest
ConVar cr_ssao_enable( "cr_ssao_enable", "1", FCVAR_ARCHIVE );
You can name it whatever you want.
Then go to 'viewpostprocess.cpp' and add the following code to very end of the file:
// Crossroads devtest
void DoSSAO( const CViewSetup &view )
{
UpdateScreenEffectTexture();
CMatRenderContextPtr pRenderContext( materials );
ITexture *pSrc = materials->FindTexture( "_rt_FullFrameFB", TEXTURE_GROUP_RENDER_TARGET );
int nSrcWidth = pSrc->GetActualWidth();
int nSrcHeight = pSrc->GetActualHeight();
ITexture *pSSAOTex = materials->FindTexture( "_rt_SSAO", TEXTURE_GROUP_RENDER_TARGET );
int nViewportWidth = 0;
int nViewportHeight = 0;
int nDummy = 0;
pRenderContext->GetViewport( nDummy, nDummy, nViewportWidth, nViewportHeight );
Rect_t DestRect;
DestRect.x = 0;
DestRect.y = 0;
DestRect.width = nSrcWidth;
DestRect.height = nSrcHeight;
IMaterial *pSSAOCalcMat = materials->FindMaterial( "dev/ssao", TEXTURE_GROUP_OTHER, true );
if ( pSSAOCalcMat == NULL )
return;
// ssao in Crossroads consist of 3 separate passes:
// 1. ssao calculation (outputs white texture with black shadows)
pRenderContext->PushRenderTargetAndViewport( pSSAOTex );
pRenderContext->DrawScreenSpaceRectangle(
pSSAOCalcMat,
0, 0, nViewportWidth, nViewportHeight,
0, 0, nSrcWidth-1, nSrcHeight-1,
nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() );
pRenderContext->PopRenderTargetAndViewport();
// 2. blurring that texture to avoid grain
if( cr_ssao_blur.GetBool() )
{
IMaterial *pSSAOBlurMat = materials->FindMaterial( "dev/ssaoblur", TEXTURE_GROUP_OTHER, true );
pRenderContext->DrawScreenSpaceRectangle(
pSSAOBlurMat,
0, 0, nViewportWidth, nViewportHeight,
0, 0, nSrcWidth-1, nSrcHeight-1,
nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() );
pRenderContext->CopyRenderTargetToTextureEx( pSSAOTex, 0, &DestRect, NULL );
}
// 3. combine what we got with framebuffer texture
if( cr_ssao_combine.GetBool() )
{
IMaterial *pSSAOCombineMat = materials->FindMaterial( "dev/ssao_combine", TEXTURE_GROUP_OTHER, true );
pRenderContext->DrawScreenSpaceRectangle(
pSSAOCombineMat,
0, 0, nViewportWidth, nViewportHeight,
0, 0, nSrcWidth-1, nSrcHeight-1,
nSrcWidth, nSrcHeight, GetClientWorldEntity()->GetClientRenderable() );
}
}
Then find the line:
static ConVar mat_postprocess_y( "mat_postprocess_y", "1" );
and right after it add:
//crossroads devtest
ConVar cr_ssao_blur( "cr_ssao_blur", "1" );
ConVar cr_ssao_combine( "cr_ssao_combine", "1" );
Then in 'viewpostprocess.h' after line starting with 'void DumpTGAofRenderTarget( ...' add:
void DoSSAO( const CViewSetup &viewSet ); // crossroads devtest
Also, don't forget to precache our materials (ssao.vmt, ssaoblur.vmt and ssao_combine.vmt). In 'viewrender.cpp' find the line "CLIENTEFFECT_REGISTER_BEGIN( PrecachePostProcessingEffects )" and after it add:
//crossroads devtest
CLIENTEFFECT_MATERIAL( "dev/ssao" )
CLIENTEFFECT_MATERIAL( "dev/ssaoblur" )
CLIENTEFFECT_MATERIAL( "dev/ssao_combine" )
And... I believe that's all. Try to compile the dll, everything should be fine.
Now the last step.
Creating 'ssao', 'ssao_combine' and 'ssaoblur' materials:
Go to your mod folder then materials/dev and create there three files: 'ssao.vmt', 'ssao_combine.vmt' and 'ssaoblur.vmt'.
Open 'ssao.vmt' and paste the following in it:
"ssao"
{
"$basetexture" "_rt_FullFrameFB"
}
Then paste this in 'ssao_combine.vmt':
"ssaocombine"
{
"$ssaotexture" "_rt_SSAO"
"$basetexture" "_rt_FullFrameFB"
}
And finally, 'ssaoblur.vmt':
"GAUSSBLUR3X3"
{
"$basetexture" "_rt_SSAO"
}
Okay, if I didn't forget anything you will have SSAO in your mod now.
That's all, feel free to ask me questions and suggest fixes for tutorial. Hope you will find it useful!
In this tutorial I will show you (not teach) how to implement basic SSAO shader into Source Engine.
Test tutorial Hammer "Propper" Static and Dunamic model
Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.
great, another dead mod
This comment is currently awaiting admin approval, join now to view.
it's dead,isn it? :(
no
is it now
Is this Mod still being worked on?
is it still coming along?
Question: Will this possibly be released this year or next?
Multiplayer will be coming out this year. will be an update on the wishes of the players
The models and textures were very good. I didn't understand what was happening because I don't speak Russian. and there was a vent (2nd door on the right, 3rd door on the left) that was too low to enter and there were no other entrances into the room.
do not worry, the demo version of the style shows, that's all