


SSAO (Screen Space Ambient Occlusion)
December 23, 2016
Using static noise texture to cast vector onto the Normal buffer and using the result to build occlusion buffer.
Blurring occlusion buffer before multiplying the color buffer.

PBR (Physical Based Rendering)
December 23, 2016
Hlsl Original Shader:
- Albedo texture
- Normal Texture
- RGB (R: Metalness, G: Roughness,B: Occlusion) texture
- HDR cubemap using different Mip levels according to metalness

LInear shadows and tuntime Light Bulbs
December 23, 2016
- HDR static lights and runtime light bulbs mixed
- Linear runtime Shadows

CPP CODE
///<summary>
///Render the Models with PBR Shader
///</summary>
///<param name='pd3dDevice'>A pointer to the actual Hardware device</param>
///<param name='g_aModel'>the Mesh sdkmesh format</param>
///<param name='fTime'>Time</param>
///<param name='fElapsedTime'>Elapsed Time</param>
///<param name='pUserContext'>the user Context</param>
///<param name='modelIndex'>the Model index used to link 3 textures ( Albedo, Normal, Material ) to each model</param>
void ModelRender( ID3D11Device* pd3dDevice, CDXUTSDKMesh * g_aModel, ID3D11DeviceContext* pd3dImmediateContext,
double fTime, float fElapsedTime, void* pUserContext, int modelIndex )
{
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE MappedResource;
V( pd3dImmediateContext->Map( g_pbrCBChangesEveryFrame, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource ) );
auto pCB = reinterpret_cast<CBChangesEveryFrame*>( MappedResource.pData );
XMStoreFloat4x4( &pCB->View, XMMatrixTranspose( mView ) );
XMStoreFloat4x4( &pCB->Projection, XMMatrixTranspose( mProj ) );
XMStoreFloat4x4( &pCB->LightViewProj, XMMatrixTranspose( mLightViewProj ) );
XMStoreFloat4x4( &pCB->World, XMMatrixTranspose( g_World ) ); //XMMatrixMultiply( XMLoadFloat4x4(&myBindPose), g_World) ));
XMStoreFloat4( &pCB->vLightPosition, s_LightPointPosition );
XMStoreFloat4( &pCB->vEyePosition, g_Camera.GetEyePt( ) );
pCB->vMisc.x = 1.0f + s_LightIntensity;
pd3dImmediateContext->Unmap( g_pbrCBChangesEveryFrame, 0 );
//
// Set the Vertex Layout
//
pd3dImmediateContext->IASetInputLayout( g_pbrVertexLayout );
//
// Render the mesh
//
UINT Strides[ 1 ];
UINT Offsets[ 1 ];
ID3D11Buffer* pVB[ 1 ];
pVB[ 0 ] = g_aModel->GetVB11( 0, 0 );
Strides[ 0 ] = ( UINT )g_aModel->GetVertexStride( 0, 0 );
Offsets[ 0 ] = 0;
pd3dImmediateContext->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );
pd3dImmediateContext->IASetIndexBuffer( g_aModel->GetIB11( 0 ), g_aModel->GetIBFormat11( 0 ), 0 );
pd3dImmediateContext->VSSetShader( g_pbrVertexShader, nullptr, 0 );
pd3dImmediateContext->VSSetConstantBuffers( 0, 1, &g_pbrCBNeverChanges );
pd3dImmediateContext->VSSetConstantBuffers( 1, 1, &g_pbrCBChangesEveryFrame );
pd3dImmediateContext->PSSetShader( g_pbrPixelShader, nullptr, 0 );
pd3dImmediateContext->PSSetConstantBuffers( 1, 1, &g_pbrCBChangesEveryFrame );
pd3dImmediateContext->PSSetSamplers( 0, 1, &g_pSamplerLinear );
pd3dImmediateContext->PSSetSamplers( 1, 1, &g_pSamplerPoint );
int actualIndex = ( modelIndex * 3 );
ID3D11ShaderResourceView * arrayOfTextures[ ] = {
g_MMTextureDiffuse[ actualIndex + 0 ],
g_MMTextureDiffuse[ actualIndex + 1 ],
g_MMTextureDiffuse[ actualIndex + 2 ],
refShadowRes,
g_TexCubemapSpecular,
g_TexCubemapIrradiance, };
pd3dImmediateContext->PSSetShaderResources( 0, 6, arrayOfTextures );
for ( UINT subset = 0; subset < g_aModel->GetNumSubsets( 0 ); ++subset )
{
auto pSubset = g_aModel->GetSubset( 0, subset );
auto PrimType = g_aModel->GetPrimitiveType11( ( SDKMESH_PRIMITIVE_TYPE )pSubset->PrimitiveType );
pd3dImmediateContext->IASetPrimitiveTopology( PrimType );
pd3dImmediateContext->DrawIndexed( ( UINT )pSubset->IndexCount, 0, ( UINT )pSubset->VertexStart );
}
};
​
​
///<summary>
///Create the resources for a Full Screen Quad Sprite
///<para>The resource are used to show an eventual fullscreen Overlay</para>
///</summary>
///<param name='dwShaderFlags'>the Device flags</param>
///<param name='g_pd3dDevice'>A pointer to the actual Hardware Device</param>
///<param name='pBackBufferSurfaceDesc'>the Backbuffer description properties</param>
///<param name='pBackBufferSurfaceDesc'>the Device Context</param>
///<returns>Returns OK or the failure value for Debug</returns>
HRESULT CreateFullScreenQuadSprite( DWORD dwShaderFlags,
ID3D11Device* pd3dDevice,
const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
ID3D11DeviceContext* pd3dImmediateContext )
{
HRESULT hr = S_OK;
// load and compile the two shaders
V_RETURN( CompileShaderAndAssignLayout( pd3dImmediateContext, pd3dDevice, dwShaderFlags, screenQuadLayout, ARRAYSIZE( screenQuadLayout ), L"Effect_DrawScreenSprite.fx", &g_SpriteVertexShader, &g_SpritePixelShader, &g_SpriteVertexLayout ) );
//
// Init Vertex Buffer
//
// 1__3
// |\ |
// | \|
// 0--2
VERTEX QuadVertices[] =
{
{ XMFLOAT3(0.0f, 1.0f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },
{ XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },
{ XMFLOAT3(1.0f, 1.0f, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) },
{ XMFLOAT3(1.0f, 0.0f, 0.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f) },
};
​
// Create the vertex buffer
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DYNAMIC; // write access access by CPU and GPU
bd.ByteWidth = sizeof(VERTEX) * 4; // size is the VERTEX struct * 4
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // use as a vertex buffer
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // allow CPU to write in buffer
pd3dDevice->CreateBuffer(&bd, NULL, &m_pScreenSpriteVertexBuffer); // create the buffer
// Copy Vertices into the buffer
D3D11_MAPPED_SUBRESOURCE ms;
pd3dImmediateContext->Map( m_pScreenSpriteVertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms ); // map the buffer
memcpy( ms.pData, QuadVertices, sizeof( QuadVertices ) ); // copy the data
pd3dImmediateContext->Unmap( m_pScreenSpriteVertexBuffer, NULL ); // unmap the buffer
//
// Full Screen Quad Sprite Indexes
//
WORD indices[] =
{
0,1,2,
2,1,3,
};
// Index buffer description
D3D11_BUFFER_DESC indexDesc;
ZeroMemory( &indexDesc, sizeof( indexDesc ) );
indexDesc.Usage = D3D11_USAGE_DEFAULT;
indexDesc.ByteWidth = sizeof(WORD) * 6;
indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexDesc.CPUAccessFlags = 0;
// Index Data
D3D11_SUBRESOURCE_DATA indexData;
ZeroMemory( &indexData, sizeof( indexData ) );
indexData.pSysMem = indices;
pd3dDevice->CreateBuffer( &indexDesc, &indexData, &m_pScreenSpriteIndexBuffer );
//
// Full Screen Quad Sprite Constant Buffer
//
D3D11_BUFFER_DESC constDesc;
ZeroMemory( &constDesc, sizeof( constDesc ) );
constDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constDesc.ByteWidth = sizeof( XMMATRIX );
constDesc.Usage = D3D11_USAGE_DEFAULT;
V_RETURN( pd3dDevice->CreateBuffer( &constDesc, 0, &m_pScreenSpriteCostantBuffer ) );
return hr;
}

HLSL CODE
cbuffer cbNeverChanges : register( b0 )
{
float3 LightDirection;
};
cbuffer cbChangesEveryFrame : register( b1 )
{
//matrix
float LightIntensity;
float4 LightPosition = float4( 300.0f, 300.0f, -300.0f, 0.0f );
float4 EyePosition = float4( 0.0f, 100.0f, -300.0f, 1.0f );
matrix World;
matrix View;
matrix Projection;
matrix LightViewProj;
};
Texture2D ColorMap : register( t0 );
Texture2D NormalMap : register( t1 );
Texture2D MaterialMap : register( t2 );
Texture2D ShadowMap : register( t3 );
TextureCube TexCubemapSpecular : register( t4 );
TextureCube TexCubemapIrradiance : register( t5 );
SamplerState samLinear : register( s0 );
SamplerState samPoint : register( s1 );
SamplerState CubeSamplerSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
struct VS_INPUT
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float3 Normal : NORMAL0;
float3 Binormal : BINORMAL0;
float3 Tangent : TANGENT0;
};
struct PS_INPUT
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
float3 LightVector : TEXCOORD1;
float3x3 WorldToTangentSpace : TEXCOORD2;
float3 ViewDir : COLOR1;
float4 wPosition : COLOR0;
};
float random(float3 seed, int i)
{
float4 seed4 = float4(seed, i);
float dot_prod = dot(seed4, float4(12.9898, 78.233, 45.164, 94.673));
return frac(sin(dot_prod) * 43758.5453);
}
// Poission blur based on multiple samples
float shadowPoisson(float light_space_depth, float2 shadow_coord)
{
float2 poisson_disk[ 8 ] = {
float2( -0.94201624, -0.39906216 ),
float2( 0.94558609, -0.76890725 ),
float2( -0.094184101, -0.92938870 ),
float2( 0.34495938, 0.29387760 ),
float2( -0.91588581, 0.45771432 ),
float2( -0.81544232, -0.87912464 ),
float2( -0.38277543, 0.27676845 ),
float2( 0.97484398, 0.75648379 ),
};
float samples = 8;
float visibility = 1.0f;
//samples
for ( int i = 0; i < samples; i++ )
{
visibility -= ( 1 / samples ) * ( light_space_depth < ShadowMap.SampleLevel( samLinear, shadow_coord + poisson_disk[ i ] / 1000, 0 ).r );
}
return 1.0 - visibility;
}
// Multisample PCF shadow blur
float calcShadowPCF(float light_space_depth, float2 shadow_coord)
{
float shadow_term = 0;
float2 shadow_map_size = float2(1366, 768);
float bias = -0.0015f;
float sizex = 1.0 / shadow_map_size.x;
float sizey = 1.0 / shadow_map_size.y;
float samples[ 4 ];
samples[ 0 ] = ( light_space_depth - bias < ShadowMap.Sample( samPoint, shadow_coord ).r );
samples[ 1 ] = ( light_space_depth - bias < ShadowMap.Sample( samPoint, shadow_coord + float2( sizex, 0 ) ).r );
samples[ 2 ] = ( light_space_depth - bias < ShadowMap.Sample( samPoint, shadow_coord + float2( 0, sizey ) ).r );
samples[ 3 ] = ( light_space_depth - bias < ShadowMap.Sample( samPoint, shadow_coord + float2( sizex, sizey ) ).r );
shadow_term = ( samples[0] + samples[1] + samples[2] + samples[3] ) / 4.0;
return shadow_term;
}
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output;
output.wPosition = mul( input.Position, World );
float4 viewPosition = mul( output.wPosition, View );
output.Position = mul( viewPosition, Projection );
output.TexCoord = input.TexCoord;
output.WorldToTangentSpace[ 0 ] = normalize( mul( input.Tangent, World ) );
output.WorldToTangentSpace[ 1 ] = normalize( mul( input.Binormal, World ) );
output.WorldToTangentSpace[ 2 ] = normalize( mul( input.Normal, World ) );
// set view vector and full lenght light vector ( we don't normalize the light vector to use it for light fall off )
output.ViewDir = normalize( EyePosition.xyz - output.wPosition.xyz );
output.LightVector = ( LightPosition.xyz - output.wPosition.xyz );
return output;
}
// remap values from low1/high1 range to low2/high2
float map( float value, float low1, float high1, float low2, float high2 )
{
return low2 + ( value - low1 ) * ( high2 - low2 ) / ( high1 - low1 );
}
// Lys function
float GetSpecPowToMip( float fSpecPow, int nMips )
{
// Lys constants
static const float k0 = 0.00098, k1 = 0.9921, fUserMaxSPow = 0.2425;
static const float g_fMaxT = ( exp2( -10.0 / sqrt( fUserMaxSPow ) ) - k0 ) / k1;
fSpecPow = 1 - pow( 1 - fSpecPow, 8 );
// Default curve - Inverse of Toolbag 2 curve with adjusted constants.
float fSmulMaxT = ( exp2( -10.0 / sqrt( fSpecPow ) ) - k0 ) / k1;
return float( nMips - 1 ) * ( 1.0 - clamp( fSmulMaxT / g_fMaxT, 0.0, 1.0 ) );
}
float4 PS( PS_INPUT input ) : SV_Target0
{
float3 LightDiffuseColor = float3( 1.0f, 1.0f, 1.0f );
float bias = -1.2655f;
float lightMaxDist = 1000.0f;
// Sample the textures
// NORMAL the Tangent/Normal/Binormal are mized with the texture normals to create final normals
float3 TextureNormal = normalize( ( 2.0f * NormalMap.Sample( samLinear, input.TexCoord ).xyz ) - 1.0f );
float3 Normal = normalize( mul( TextureNormal, input.WorldToTangentSpace ) );
// DIFFUSE texture comes with Albedo Color + some AO backed for some cases of pre occlusion
float3 Diffuse = ColorMap.Sample( samLinear, input.TexCoord ).rgb;
// ROUGHNESS = Material.r ( how much blurry is the reflection )
// METALNESS = Material.g ( how much the material reflects )
// OCCUSION = Material.b ( how much occlusion )
float3 Material = MaterialMap.Sample( samLinear, input.TexCoord ).rgb;
// Cubemap reflections UVW Coords are created with reflection of world and eye position
float3 vecWorldProjection = input.wPosition.xyz - EyePosition.xyz;
vecWorldProjection = normalize( vecWorldProjection );
float3 cubeSampCoords = reflect( vecWorldProjection , Normal );// Normal are reblended with model normals
// Cubamep inversion for reflection
cubeSampCoords.z = -cubeSampCoords.z;
float4 cubemapSampleAmbient = TexCubemapSpecular.Sample( samLinear, cubeSampCoords );///= abs(NormalDotLight);
float4 cubemapSampleSpec = TexCubemapIrradiance.Sample( samLinear, cubeSampCoords );// *fLighting;
float luminance = saturate( ( cubemapSampleSpec.r + cubemapSampleSpec.g + cubemapSampleSpec.b ) / 3.0 );
// Creating specular color and intensity, this needs to be done before gamma correction
float3 specularColor = float3( lerp( 0.04f.rrr, Diffuse.rgb, Material.g ) ) * Material.b;
Diffuse.rgb = lerp( Diffuse.rgb, 0.0f.rrr, luminance * Material.g );
// Gamma Correction
cubemapSampleAmbient = pow( abs( cubemapSampleAmbient ), 2.2 );
cubemapSampleSpec = pow( abs( cubemapSampleSpec ), 2.2 );
Material = pow( abs( Material ), 2.2 );
// Normalize input
float3 vecViewDir = normalize( input.ViewDir );
float3 vecLightDir = normalize( input.LightVector * 0.75 + dot( -TextureNormal, input.LightVector ) * 0.25 );
float3 vecHalf = normalize( vecLightDir + vecViewDir );
// Compute Aliases
float NormalDotHalf = dot( Normal, vecHalf );
float vecViewDotHalf = dot( vecHalf, vecViewDir );
float NormalDotView = dot( Normal, vecViewDir );
float NormalDotLight = dot( Normal, vecLightDir );
// Compute the geometric term
float G1 = ( 2.0f * NormalDotHalf * NormalDotView ) / vecViewDotHalf;
float G2 = ( 2.0f * NormalDotHalf * NormalDotLight ) / vecViewDotHalf;
float G = min( 1.0f, max( 0.0f, min( G1, G2 ) ) );
// Compute the fresnel term
float F = Material.g + ( 1.0f - Material.g ) * pow( 1.0f - NormalDotView, 5.0f );
// Compute the roughness term
float R_2 = Material.r * Material.r;
float NDotH_2 = NormalDotHalf * NormalDotHalf;
float A = 1.0f / ( 4.0f * R_2 * NDotH_2 * NDotH_2 );
float B = exp( -( 1.0f - NDotH_2 ) / ( R_2 * NDotH_2 ) );
float R = A * B;
// Compute the final term
float3 S = specularColor * ( G * F * R ) / ( NormalDotLight * NormalDotView );
// Introduce concept of Point Light Falloff
float distance = length( input.LightVector );
// Distance cap
if ( distance > lightMaxDist )distance = lightMaxDist;
distance = LightIntensity - map( distance, 0.0, lightMaxDist, 0.0, LightIntensity );
float diffuseLighting = 0.5 + saturate(dot(input.WorldToTangentSpace[2], vecLightDir)) * 0.5; // per pixel diffuse lighting
diffuseLighting *= distance / dot( vecLightDir, vecLightDir ); // Distance from light
diffuseLighting = clamp( diffuseLighting, 0.0, 1.0 );
diffuseLighting = 1 - cos( diffuseLighting * 1.57 );
// Composite
float3 Final = ( LightDiffuseColor.rgb * diffuseLighting * ( Diffuse + clamp( S, 0, 1 ) ) );
// Use roughness to mediate between low and high res reflection map ( could be done with mipmaps in Shader Model 4.0 )
float mediator = ( ( GetSpecPowToMip( Material.r, 256 ) ) / 256.0 );
float3 addCube = lerp( cubemapSampleAmbient.rgb, cubemapSampleSpec.rgb, mediator );
addCube *= saturate( Material.g * 2 );// * 2;
// add the diffuse ( consider that the Diffuse has already been mediated by the Metalness value before gamma correction )
addCube += saturate( Diffuse * ( lerp( luminance, diffuseLighting, 0.66 ) ) );
// Add in occlusion
float occlusion = 1 - ( 1 - Material.b ) * 0.75f;
Final += addCube * ( Material.b * 0.75f );
Final = saturate( Final * occlusion );
// Find the position of this pixel in light space
float4 lightingPosition = mul( input.wPosition, LightViewProj );
// Find the position in the shadow map for this pixel
float2 ShadowTexCoord = 0.5 * lightingPosition.xy / lightingPosition.w + float2( 0.5, 0.5 );
ShadowTexCoord.y = 1.0f - ShadowTexCoord.y;
// Get the current depth stored in the shadow map
float shadowdepth = ShadowMap.Sample( samPoint, ShadowTexCoord ).r;
// Calculate the current pixel depth
float ourdepth = ( lightingPosition.z / lightingPosition.w ) - bias;
// Check to see if this pixel is in front or behind the value in the shadow map
if ( shadowdepth < ourdepth )
{
// Blur the shadows using multiple neighor pixel depths
float2 screen_pos = 0.5 + float2( lightingPosition.x, -lightingPosition.y ) * 0.5;
float light_space_depth = lightingPosition.z;
float shadow_term = shadowPoisson( light_space_depth, screen_pos );
// Shadow the pixel by lowering the intensity
Final -= Final * ( 0.15 + ( 1 - saturate( diffuseLighting + shadow_term ) ) * 0.85 );
};
float diffuseIntensity = saturate( dot( -( LightDirection ), Normal ) );
Final = ( Final + ( Final * diffuseIntensity ) );
return float4( Final.rgb, 1.0f );
}