Merge branch '545-irradiance-light-grid'

This commit is contained in:
Robert Beckebans 2021-04-29 16:33:10 +02:00
commit 5b0a3a8ac8
33 changed files with 4826 additions and 329 deletions

View file

@ -15,6 +15,8 @@ return
"builtin/debug/shadowDebug_skinned.vs.hlsl",
"builtin/debug/octahedron.ps.hlsl",
"builtin/debug/octahedron.vs.hlsl",
"builtin/debug/lightgrid.ps.hlsl",
"builtin/debug/lightgrid.vs.hlsl",
"builtin/fog/blendLight.ps.hlsl",
"builtin/fog/blendLight.vs.hlsl",
@ -40,6 +42,8 @@ return
"builtin/lighting/ambient_lighting.vs.hlsl",
"builtin/lighting/ambient_lighting_IBL.ps.hlsl",
"builtin/lighting/ambient_lighting_IBL.vs.hlsl",
"builtin/lighting/ambient_lightgrid_IBL.ps.hlsl",
"builtin/lighting/ambient_lightgrid_IBL.vs.hlsl",
"builtin/lighting/interaction.ps.hlsl",
"builtin/lighting/interaction.vs.hlsl",
"builtin/lighting/interactionAmbient.ps.hlsl",

View file

@ -0,0 +1,129 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "renderprogs/global.inc.hlsl"
// *INDENT-OFF*
uniform sampler2D samp0 : register(s0); // texture 0 is octahedron cube map atlas
struct PS_IN {
float4 position : VPOS;
float3 texcoord0 : TEXCOORD0_centroid;
float3 texcoord1 : TEXCOORD1_centroid;
float4 color : COLOR0;
};
struct PS_OUT {
float4 color : COLOR;
};
// *INDENT-ON*
int3 GetBaseGridCoord( float3 origin )
{
float3 lightGridOrigin = rpGlobalLightOrigin.xyz;
float3 lightGridSize = rpJitterTexScale.xyz;
int3 lightGridBounds = int3( rpJitterTexOffset.x, rpJitterTexOffset.y, rpJitterTexOffset.z );
int3 pos;
float3 lightOrigin = origin - lightGridOrigin;
for( int i = 0; i < 3; i++ )
{
float v;
v = lightOrigin[i] * ( 1.0f / lightGridSize[i] );
pos[i] = int( floor( v ) );
if( pos[i] < 0 )
{
pos[i] = 0;
}
else if( pos[i] >= lightGridBounds[i] - 1 )
{
pos[i] = lightGridBounds[i] - 1;
}
}
return pos;
}
void main( PS_IN fragment, out PS_OUT result )
{
const int LIGHTGRID_IRRADIANCE_SIZE = 32;
float3 globalPosition = fragment.texcoord0.xyz;
float3 globalNormal = normalize( fragment.texcoord1 );
float2 normalizedOctCoord = octEncode( globalNormal );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
float3 lightGridOrigin = rpGlobalLightOrigin.xyz;
float3 lightGridSize = rpJitterTexScale.xyz;
int3 lightGridBounds = int3( rpJitterTexOffset.x, rpJitterTexOffset.y, rpJitterTexOffset.z );
float invXZ = ( 1.0 / ( lightGridBounds[0] * lightGridBounds[2] ) );
float invY = ( 1.0 / lightGridBounds[1] );
normalizedOctCoordZeroOne.x *= invXZ;
normalizedOctCoordZeroOne.y *= invY;
int3 gridStep;
gridStep[0] = 1;
gridStep[1] = lightGridBounds[0];
gridStep[2] = lightGridBounds[0] * lightGridBounds[1];
int3 gridCoord = GetBaseGridCoord( globalPosition );
normalizedOctCoordZeroOne.x += ( gridCoord[0] * gridStep[0] + gridCoord[2] * gridStep[1] ) * invXZ;
normalizedOctCoordZeroOne.y += ( gridCoord[1] * invY );
// offset by one pixel border bleed size for linear filtering
#if 1
// rpScreenCorrectionFactor.x = probeSize - borderSize, e.g. ( 18 - 2 ) = 16
// rpScreenCorrectionFactor.y = probeSize including border, e.g = 18
// rpScreenCorrectionFactor.z = borderSize e.g = 2
// rpScreenCorrectionFactor.w = probeSize factor accounting account offset border, e.g = ( 16 / 18 ) = 0.8888
float2 octCoordNormalizedToTextureDimensions = normalizedOctCoordZeroOne * rpScreenCorrectionFactor.w;
float2 probeTopLeftPosition;
probeTopLeftPosition.x = ( gridCoord[0] * gridStep[0] + gridCoord[2] * gridStep[1] ) * rpScreenCorrectionFactor.z + rpScreenCorrectionFactor.z * 0.5;
probeTopLeftPosition.y = ( gridCoord[1] ) * rpScreenCorrectionFactor.z + rpScreenCorrectionFactor.z * 0.5;
float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw;
normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions;
#endif
float4 envMap = texture( samp0, normalizedOctCoordZeroOne, 0 );
result.color = float4( envMap.xyz, 1.0f ) * 1.0 * fragment.color;
}

View file

@ -0,0 +1,126 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "renderprogs/global.inc.hlsl"
uniform matrices_ubo { float4 matrices[408]; };
// *INDENT-OFF*
struct VS_IN {
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float4 color : COLOR0;
float4 color2 : COLOR1;
};
struct VS_OUT {
float4 position : POSITION;
float3 texcoord0 : TEXCOORD0;
float3 texcoord1 : TEXCOORD1;
float4 color : COLOR0;
};
// *INDENT-ON*
void main( VS_IN vertex, out VS_OUT result )
{
float4 vNormal = vertex.normal * 2.0 - 1.0;
#if defined( USE_GPU_SKINNING )
//--------------------------------------------------------------
// GPU transformation of the normal / binormal / bitangent
//
// multiplying with 255.1 give us the same result and is faster than floor( w * 255 + 0.5 )
//--------------------------------------------------------------
const float w0 = vertex.color2.x;
const float w1 = vertex.color2.y;
const float w2 = vertex.color2.z;
const float w3 = vertex.color2.w;
float4 matX, matY, matZ; // must be float4 for vec4
int joint = int( vertex.color.x * 255.1 * 3.0 );
matX = matrices[int( joint + 0 )] * w0;
matY = matrices[int( joint + 1 )] * w0;
matZ = matrices[int( joint + 2 )] * w0;
joint = int( vertex.color.y * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w1;
matY += matrices[int( joint + 1 )] * w1;
matZ += matrices[int( joint + 2 )] * w1;
joint = int( vertex.color.z * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w2;
matY += matrices[int( joint + 1 )] * w2;
matZ += matrices[int( joint + 2 )] * w2;
joint = int( vertex.color.w * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w3;
matY += matrices[int( joint + 1 )] * w3;
matZ += matrices[int( joint + 2 )] * w3;
float3 normal;
normal.x = dot3( matX, vNormal );
normal.y = dot3( matY, vNormal );
normal.z = dot3( matZ, vNormal );
normal = normalize( normal );
float4 modelPosition;
modelPosition.x = dot4( matX, vertex.position );
modelPosition.y = dot4( matY, vertex.position );
modelPosition.z = dot4( matZ, vertex.position );
modelPosition.w = 1.0;
#else
float4 modelPosition = vertex.position;
float4 normal = vNormal;
#endif
result.position.x = dot4( modelPosition, rpMVPmatrixX );
result.position.y = dot4( modelPosition, rpMVPmatrixY );
result.position.z = dot4( modelPosition, rpMVPmatrixZ );
result.position.w = dot4( modelPosition, rpMVPmatrixW );
result.texcoord1.x = dot3( normal, rpModelMatrixX );
result.texcoord1.y = dot3( normal, rpModelMatrixY );
result.texcoord1.z = dot3( normal, rpModelMatrixZ );
float4 worldPosition;
worldPosition.x = dot4( modelPosition, rpModelMatrixX );
worldPosition.y = dot4( modelPosition, rpModelMatrixY );
worldPosition.z = dot4( modelPosition, rpModelMatrixZ );
worldPosition.w = dot4( modelPosition, rpModelMatrixW );
result.texcoord0 = worldPosition.xyz;
result.color = sRGBAToLinearRGBA( rpColor );
}

View file

@ -58,7 +58,19 @@ void main( PS_IN fragment, out PS_OUT result )
float2 normalizedOctCoord = octEncode( reflectionVector );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
float4 envMap = tex2D( samp0, normalizedOctCoordZeroOne );
// offset by one pixel border bleed size for linear filtering
#if 0
float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne * ( rpCascadeDistances.x - float( 2.0 ) ) ) / rpCascadeDistances.xy;
result.color = float4( sRGBToLinearRGB( envMap.xyz ), 1.0f ) * fragment.color;
float2 probeTopLeftPosition = float2( 1.0, 1.0 );
float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw;
normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions;
#endif
//normalizedOctCoordZeroOne = TextureCoordFromDirection( reflectionVector, 0, int( rpCascadeDistances.x ), int( rpCascadeDistances.y ), int( rpCascadeDistances.x ) - 2 );
float4 envMap = texture( samp0, normalizedOctCoordZeroOne, 0 );
result.color = float4( envMap.xyz, 1.0f ) * fragment.color * 1.0;
}

View file

@ -0,0 +1,435 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2013-2021 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "renderprogs/global.inc.hlsl"
#include "renderprogs/BRDF.inc.hlsl"
// *INDENT-OFF*
uniform sampler2D samp0 : register(s0); // texture 0 is the per-surface normal map
uniform sampler2D samp1 : register(s1); // texture 1 is the per-surface specular or roughness/metallic/AO mixer map
uniform sampler2D samp2 : register(s2); // texture 2 is the per-surface baseColor map
uniform sampler2D samp3 : register(s3); // texture 3 is the BRDF LUT
uniform sampler2D samp4 : register(s4); // texture 4 is SSAO
uniform sampler2D samp7 : register(s7); // texture 7 is the irradiance cube map
uniform sampler2D samp8 : register(s8); // texture 8 is the radiance cube map 1
uniform sampler2D samp9 : register(s9); // texture 9 is the radiance cube map 2
uniform sampler2D samp10 : register(s10); // texture 10 is the radiance cube map 3
struct PS_IN
{
half4 position : VPOS;
half4 texcoord0 : TEXCOORD0_centroid;
half4 texcoord1 : TEXCOORD1_centroid;
half4 texcoord2 : TEXCOORD2_centroid;
half4 texcoord3 : TEXCOORD3_centroid;
half4 texcoord4 : TEXCOORD4_centroid;
half4 texcoord5 : TEXCOORD5_centroid;
half4 texcoord6 : TEXCOORD6_centroid;
half4 texcoord7 : TEXCOORD7_centroid;
half4 color : COLOR0;
};
struct PS_OUT
{
half4 color : COLOR;
};
// *INDENT-ON*
// RB: TODO OPTIMIZE
// this is a straight port of idBounds::RayIntersection
bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale )
{
int i, ax0, ax1, ax2, side, inside;
float f;
float3 hit;
ax0 = -1;
inside = 0;
for( i = 0; i < 3; i++ )
{
if( start[i] < b[0][i] )
{
side = 0;
}
else if( start[i] > b[1][i] )
{
side = 1;
}
else
{
inside++;
continue;
}
if( dir[i] == 0.0f )
{
continue;
}
f = ( start[i] - b[side][i] );
if( ax0 < 0 || abs( f ) > abs( scale * dir[i] ) )
{
scale = - ( f / dir[i] );
ax0 = i;
}
}
if( ax0 < 0 )
{
scale = 0.0f;
// return true if the start point is inside the bounds
return ( inside == 3 );
}
ax1 = ( ax0 + 1 ) % 3;
ax2 = ( ax0 + 2 ) % 3;
hit[ax1] = start[ax1] + scale * dir[ax1];
hit[ax2] = start[ax2] + scale * dir[ax2];
return ( hit[ax1] >= b[0][ax1] && hit[ax1] <= b[1][ax1] &&
hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] );
}
void main( PS_IN fragment, out PS_OUT result )
{
half4 bumpMap = tex2D( samp0, fragment.texcoord0.xy );
half4 YCoCG = tex2D( samp2, fragment.texcoord1.xy );
half4 specMapSRGB = tex2D( samp1, fragment.texcoord2.xy );
half4 specMap = sRGBAToLinearRGBA( specMapSRGB );
half3 diffuseMap = sRGBToLinearRGB( ConvertYCoCgToRGB( YCoCG ) );
half3 localNormal;
#if defined(USE_NORMAL_FMT_RGB8)
localNormal.xy = bumpMap.rg - 0.5;
#else
localNormal.xy = bumpMap.wy - 0.5;
#endif
localNormal.z = sqrt( abs( dot( localNormal.xy, localNormal.xy ) - 0.25 ) );
localNormal = normalize( localNormal );
float3 globalNormal;
globalNormal.x = dot3( localNormal, fragment.texcoord4 );
globalNormal.y = dot3( localNormal, fragment.texcoord5 );
globalNormal.z = dot3( localNormal, fragment.texcoord6 );
globalNormal = normalize( globalNormal );
float3 globalPosition = fragment.texcoord7.xyz;
float3 globalView = normalize( rpGlobalEyePos.xyz - globalPosition );
float3 reflectionVector = globalNormal * dot3( globalView, globalNormal );
reflectionVector = normalize( ( reflectionVector * 2.0f ) - globalView );
#if 0
// parallax box correction using portal area bounds
float hitScale = 0.0;
float3 bounds[2];
bounds[0].x = rpWobbleSkyX.x;
bounds[0].y = rpWobbleSkyX.y;
bounds[0].z = rpWobbleSkyX.z;
bounds[1].x = rpWobbleSkyY.x;
bounds[1].y = rpWobbleSkyY.y;
bounds[1].z = rpWobbleSkyY.z;
// global fragment position
float3 rayStart = fragment.texcoord7.xyz;
// we can't start inside the box so move this outside and use the reverse path
rayStart += reflectionVector * 10000.0;
// only do a box <-> ray intersection test if we use a local cubemap
if( ( rpWobbleSkyX.w > 0.0 ) && AABBRayIntersection( bounds, rayStart, -reflectionVector, hitScale ) )
{
float3 hitPoint = rayStart - reflectionVector * hitScale;
// rpWobbleSkyZ is cubemap center
reflectionVector = hitPoint - rpWobbleSkyZ.xyz;
}
#endif
half vDotN = saturate( dot3( globalView, globalNormal ) );
#if defined( USE_PBR )
const half metallic = specMapSRGB.g;
const half roughness = specMapSRGB.r;
const half glossiness = 1.0 - roughness;
// the vast majority of real-world materials (anything not metal or gems) have F(0°)
// values in a very narrow range (~0.02 - 0.08)
// approximate non-metals with linear RGB 0.04 which is 0.08 * 0.5 (default in UE4)
const half3 dielectricColor = half3( 0.04 );
// derive diffuse and specular from albedo(m) base color
const half3 baseColor = diffuseMap;
half3 diffuseColor = baseColor * ( 1.0 - metallic );
half3 specularColor = lerp( dielectricColor, baseColor, metallic );
#if defined( DEBUG_PBR )
diffuseColor = half3( 0.0, 0.0, 0.0 );
specularColor = half3( 0.0, 1.0, 0.0 );
#endif
float3 kS = Fresnel_SchlickRoughness( specularColor, vDotN, roughness );
float3 kD = ( float3( 1.0, 1.0, 1.0 ) - kS ) * ( 1.0 - metallic );
#else
const float roughness = EstimateLegacyRoughness( specMapSRGB.rgb );
half3 diffuseColor = diffuseMap;
half3 specularColor = specMap.rgb;
#if defined( DEBUG_PBR )
diffuseColor = half3( 0.0, 0.0, 0.0 );
specularColor = half3( 1.0, 0.0, 0.0 );
#endif
float3 kS = Fresnel_SchlickRoughness( specularColor, vDotN, roughness );
// NOTE: metalness is missing
float3 kD = ( float3( 1.0, 1.0, 1.0 ) - kS );
#endif
//diffuseColor = half3( 1.0, 1.0, 1.0 );
//diffuseColor = half3( 0.0, 0.0, 0.0 );
// calculate the screen texcoord in the 0.0 to 1.0 range
//float2 screenTexCoord = vposToScreenPosTexCoord( fragment.position.xy );
float2 screenTexCoord = fragment.position.xy * rpWindowCoord.xy;
float ao = 1.0;
ao = tex2D( samp4, screenTexCoord ).r;
//diffuseColor.rgb *= ao;
// evaluate diffuse IBL
float2 normalizedOctCoord = octEncode( globalNormal );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
// lightgrid atlas
//float3 lightGridOrigin = float3( -192.0, -128.0, 0 );
//float3 lightGridSize = float3( 64.0, 64.0, 128.0 );
//int3 lightGridBounds = int3( 7, 7, 3 );
float3 lightGridOrigin = rpGlobalLightOrigin.xyz;
float3 lightGridSize = rpJitterTexScale.xyz;
int3 lightGridBounds = int3( rpJitterTexOffset.x, rpJitterTexOffset.y, rpJitterTexOffset.z );
float invXZ = ( 1.0 / ( lightGridBounds[0] * lightGridBounds[2] ) );
float invY = ( 1.0 / lightGridBounds[1] );
normalizedOctCoordZeroOne.x *= invXZ;
normalizedOctCoordZeroOne.y *= invY;
int3 gridCoord;
float3 frac;
float3 lightOrigin = globalPosition - lightGridOrigin;
for( int i = 0; i < 3; i++ )
{
float v;
// walls can be sampled behind the grid sometimes so avoid negative weights
v = max( 0, lightOrigin[i] * ( 1.0 / lightGridSize[i] ) );
gridCoord[i] = int( floor( v ) );
frac[ i ] = v - gridCoord[ i ];
/*
if( gridCoord[i] < 0 )
{
gridCoord[i] = 0;
}
else
*/
if( gridCoord[i] >= lightGridBounds[i] - 1 )
{
gridCoord[i] = lightGridBounds[i] - 1;
}
}
// trilerp the light value
int3 gridStep;
gridStep[0] = 1;
gridStep[1] = lightGridBounds[0];
gridStep[2] = lightGridBounds[0] * lightGridBounds[1];
float totalFactor = 0.0;
float3 irradiance = float3( 0.0, 0.0, 0.0 );
/*
for( int i = 0; i < 8; i++ )
{
for( int j = 0; j < 3; j++ )
{
if( i & ( 1 << j ) )
results in these offsets
*/
const float3 cornerOffsets[8] = float3[](
float3( 0.0, 0.0, 0.0 ),
float3( 1.0, 0.0, 0.0 ),
float3( 0.0, 2.0, 0.0 ),
float3( 1.0, 2.0, 0.0 ),
float3( 0.0, 0.0, 4.0 ),
float3( 1.0, 0.0, 4.0 ),
float3( 0.0, 2.0, 4.0 ),
float3( 1.0, 2.0, 4.0 ) );
for( int i = 0; i < 8; i++ )
{
float factor = 1.0;
int3 gridCoord2 = gridCoord;
for( int j = 0; j < 3; j++ )
{
if( cornerOffsets[ i ][ j ] > 0.0 )
{
factor *= frac[ j ];
gridCoord2[ j ] += 1;
}
else
{
factor *= ( 1.0 - frac[ j ] );
}
}
// build P
//float3 P = lightGridOrigin + ( gridCoord2[0] * gridStep[0] + gridCoord2[1] * gridStep[1] + gridCoord2[2] * gridStep[2] );
float2 atlasOffset;
atlasOffset.x = ( gridCoord2[0] * gridStep[0] + gridCoord2[2] * gridStep[1] ) * invXZ;
atlasOffset.y = ( gridCoord2[1] * invY );
// offset by one pixel border bleed size for linear filtering
#if 1
// rpScreenCorrectionFactor.w = probeSize factor accounting account offset border, e.g = ( 16 / 18 ) = 0.8888
float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne + atlasOffset ) * rpScreenCorrectionFactor.w;
// skip by default 2 pixels for each grid cell and offset the start position by (1,1)
// rpScreenCorrectionFactor.z = borderSize e.g = 2
float2 probeTopLeftPosition;
probeTopLeftPosition.x = ( gridCoord2[0] * gridStep[0] + gridCoord2[2] * gridStep[1] ) * rpScreenCorrectionFactor.z + rpScreenCorrectionFactor.z * 0.5;
probeTopLeftPosition.y = ( gridCoord2[1] ) * rpScreenCorrectionFactor.z + rpScreenCorrectionFactor.z * 0.5;
float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw;
float2 atlasCoord = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions;
#else
float2 atlasCoord = normalizedOctCoordZeroOne + atlasOffset;
#endif
float3 color = texture( samp7, atlasCoord, 0 ).rgb;
if( ( color.r + color.g + color.b ) < 0.0001 )
{
// ignore samples in walls
continue;
}
irradiance += color * factor;
totalFactor += factor;
}
if( totalFactor > 0.0 && totalFactor < 0.9999 )
{
totalFactor = 1.0 / totalFactor;
irradiance *= totalFactor;
}
// lightgrid atlas
float3 diffuseLight = ( kD * irradiance * diffuseColor ) * ao * ( rpDiffuseModifier.xyz * 1.0 );
// evaluate specular IBL
// should be 8 = numMips - 1, 256^2 = 9 mips
const float MAX_REFLECTION_LOD = 10.0;
float mip = clamp( ( roughness * MAX_REFLECTION_LOD ), 0.0, MAX_REFLECTION_LOD );
//float mip = 0.0;
normalizedOctCoord = octEncode( reflectionVector );
normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb * rpLocalLightOrigin.x;
radiance += textureLod( samp9, normalizedOctCoordZeroOne, mip ).rgb * rpLocalLightOrigin.y;
radiance += textureLod( samp10, normalizedOctCoordZeroOne, mip ).rgb * rpLocalLightOrigin.z;
//radiance = float3( 0.0 );
// RB: HACK dim down room radiance by better local irradiance brightness
//float luma = PhotoLuma( irradiance );
//float luma = dot( irradiance, LUMINANCE_LINEAR.rgb );
//float luma = length( irradiance.rgb );
//radiance *= ( luma * rpSpecularModifier.x * 3.0 );
float2 envBRDF = texture( samp3, float2( max( vDotN, 0.0 ), roughness ) ).rg;
#if 0
result.color.rgb = float3( envBRDF.x, envBRDF.y, 0.0 );
result.color.w = fragment.color.a;
return;
#endif
float specAO = ComputeSpecularAO( vDotN, ao, roughness );
float3 specularLight = radiance * ( kS * envBRDF.x + float3( envBRDF.y ) ) * specAO * ( rpSpecularModifier.xyz * 0.5 );
#if 1
// Marmoset Horizon Fade trick
const half horizonFade = 1.3;
half horiz = saturate( 1.0 + horizonFade * saturate( dot3( reflectionVector, globalNormal ) ) );
horiz *= horiz;
//horiz = clamp( horiz, 0.0, 1.0 );
#endif
half3 lightColor = sRGBToLinearRGB( rpAmbientColor.rgb );
//half3 lightColor = ( rpAmbientColor.rgb );
//result.color.rgb = diffuseLight;
//result.color.rgb = diffuseLight * lightColor;
//result.color.rgb = specularLight;
result.color.rgb = ( diffuseLight + specularLight * horiz ) * lightColor * fragment.color.rgb;
//result.color.rgb = localNormal.xyz * 0.5 + 0.5;
//result.color.rgb = float3( ao );
result.color.w = fragment.color.a;
}

View file

@ -0,0 +1,196 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2013-2015 Robert Beckebans
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "renderprogs/global.inc.hlsl"
#if defined( USE_GPU_SKINNING )
uniform matrices_ubo { float4 matrices[408]; };
#endif
// *INDENT-OFF*
struct VS_IN {
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float4 color : COLOR0;
float4 color2 : COLOR1;
};
struct VS_OUT {
float4 position : POSITION;
float4 texcoord0 : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
float4 texcoord4 : TEXCOORD4;
float4 texcoord5 : TEXCOORD5;
float4 texcoord6 : TEXCOORD6;
float4 texcoord7 : TEXCOORD7;
float4 color : COLOR0;
};
// *INDENT-ON*
void main( VS_IN vertex, out VS_OUT result )
{
float4 vNormal = vertex.normal * 2.0 - 1.0;
float4 vTangent = vertex.tangent * 2.0 - 1.0;
float3 vBitangent = cross( vNormal.xyz, vTangent.xyz ) * vTangent.w;
#if defined( USE_GPU_SKINNING )
//--------------------------------------------------------------
// GPU transformation of the normal / tangent / bitangent
//
// multiplying with 255.1 give us the same result and is faster than floor( w * 255 + 0.5 )
//--------------------------------------------------------------
const float w0 = vertex.color2.x;
const float w1 = vertex.color2.y;
const float w2 = vertex.color2.z;
const float w3 = vertex.color2.w;
float4 matX, matY, matZ; // must be float4 for vec4
int joint = int( vertex.color.x * 255.1 * 3.0 );
matX = matrices[int( joint + 0 )] * w0;
matY = matrices[int( joint + 1 )] * w0;
matZ = matrices[int( joint + 2 )] * w0;
joint = int( vertex.color.y * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w1;
matY += matrices[int( joint + 1 )] * w1;
matZ += matrices[int( joint + 2 )] * w1;
joint = int( vertex.color.z * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w2;
matY += matrices[int( joint + 1 )] * w2;
matZ += matrices[int( joint + 2 )] * w2;
joint = int( vertex.color.w * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w3;
matY += matrices[int( joint + 1 )] * w3;
matZ += matrices[int( joint + 2 )] * w3;
float3 normal;
normal.x = dot3( matX, vNormal );
normal.y = dot3( matY, vNormal );
normal.z = dot3( matZ, vNormal );
normal = normalize( normal );
float3 tangent;
tangent.x = dot3( matX, vTangent );
tangent.y = dot3( matY, vTangent );
tangent.z = dot3( matZ, vTangent );
tangent = normalize( tangent );
float3 bitangent;
bitangent.x = dot3( matX, vBitangent );
bitangent.y = dot3( matY, vBitangent );
bitangent.z = dot3( matZ, vBitangent );
bitangent = normalize( bitangent );
float4 modelPosition;
modelPosition.x = dot4( matX, vertex.position );
modelPosition.y = dot4( matY, vertex.position );
modelPosition.z = dot4( matZ, vertex.position );
modelPosition.w = 1.0;
#else
float4 modelPosition = vertex.position;
float3 normal = vNormal.xyz;
float3 tangent = vTangent.xyz;
float3 bitangent = vBitangent.xyz;
#endif
result.position.x = dot4( modelPosition, rpMVPmatrixX );
result.position.y = dot4( modelPosition, rpMVPmatrixY );
result.position.z = dot4( modelPosition, rpMVPmatrixZ );
result.position.w = dot4( modelPosition, rpMVPmatrixW );
float4 defaultTexCoord = float4( 0.0f, 0.5f, 0.0f, 1.0f );
//--------------------------------------------------------------
//# textures 0 takes the base coordinates by the texture matrix
result.texcoord0 = defaultTexCoord;
result.texcoord0.x = dot4( vertex.texcoord.xy, rpBumpMatrixS );
result.texcoord0.y = dot4( vertex.texcoord.xy, rpBumpMatrixT );
//# textures 1 takes the base coordinates by the texture matrix
result.texcoord1 = defaultTexCoord;
result.texcoord1.x = dot4( vertex.texcoord.xy, rpDiffuseMatrixS );
result.texcoord1.y = dot4( vertex.texcoord.xy, rpDiffuseMatrixT );
//# textures 2 takes the base coordinates by the texture matrix
result.texcoord2 = defaultTexCoord;
result.texcoord2.x = dot4( vertex.texcoord.xy, rpSpecularMatrixS );
result.texcoord2.y = dot4( vertex.texcoord.xy, rpSpecularMatrixT );
//# calculate normalized vector to viewer in R1
//result.texcoord3 = modelPosition;
float4 toEye = normalize( rpLocalViewOrigin - modelPosition );
result.texcoord3.x = dot3( toEye, rpModelMatrixX );
result.texcoord3.y = dot3( toEye, rpModelMatrixY );
result.texcoord3.z = dot3( toEye, rpModelMatrixZ );
result.texcoord4.x = dot3( tangent, rpModelMatrixX );
result.texcoord5.x = dot3( tangent, rpModelMatrixY );
result.texcoord6.x = dot3( tangent, rpModelMatrixZ );
result.texcoord4.y = dot3( bitangent, rpModelMatrixX );
result.texcoord5.y = dot3( bitangent, rpModelMatrixY );
result.texcoord6.y = dot3( bitangent, rpModelMatrixZ );
result.texcoord4.z = dot3( normal, rpModelMatrixX );
result.texcoord5.z = dot3( normal, rpModelMatrixY );
result.texcoord6.z = dot3( normal, rpModelMatrixZ );
float4 worldPosition;
worldPosition.x = dot4( modelPosition, rpModelMatrixX );
worldPosition.y = dot4( modelPosition, rpModelMatrixY );
worldPosition.z = dot4( modelPosition, rpModelMatrixZ );
worldPosition.w = dot4( modelPosition, rpModelMatrixW );
result.texcoord7 = worldPosition;
#if defined( USE_GPU_SKINNING )
// for joint transformation of the tangent space, we use color and
// color2 for weighting information, so hopefully there aren't any
// effects that need vertex color...
result.color = float4( 1.0f, 1.0f, 1.0f, 1.0f );
#else
//# generate the vertex color, which can be 1.0, color, or 1.0 - color
//# for 1.0 : env[16] = 0, env[17] = 1
//# for color : env[16] = 1, env[17] = 0
//# for 1.0-color : env[16] = -1, env[17] = 1
result.color = ( swizzleColor( vertex.color ) * rpVertexColorModulate ) + rpVertexColorAdd;
#endif
}

View file

@ -40,7 +40,9 @@ uniform sampler2D samp3 : register(s3); // texture 3 is the BRDF LUT
uniform sampler2D samp4 : register(s4); // texture 4 is SSAO
uniform sampler2D samp7 : register(s7); // texture 7 is the irradiance cube map
uniform sampler2D samp8 : register(s8); // texture 8 is the radiance cube map
uniform sampler2D samp8 : register(s8); // texture 8 is the radiance cube map 1
uniform sampler2D samp9 : register(s9); // texture 9 is the radiance cube map 2
uniform sampler2D samp10 : register(s10); // texture 10 is the radiance cube map 3
struct PS_IN
{
@ -62,6 +64,7 @@ struct PS_OUT
};
// *INDENT-ON*
// RB: TODO OPTIMIZE
// this is a straight port of idBounds::RayIntersection
bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale )
@ -118,6 +121,26 @@ bool AABBRayIntersection( float3 b[2], float3 start, float3 dir, out float scale
hit[ax2] >= b[0][ax2] && hit[ax2] <= b[1][ax2] );
}
float2 OctTexCoord( float3 worldDir )
{
float2 normalizedOctCoord = octEncode( worldDir );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
// offset by one pixel border bleed size for linear filtering
#if 0
// texcoord sizes in rpCascadeDistances are not valid
float2 octCoordNormalizedToTextureDimensions = ( normalizedOctCoordZeroOne * ( rpCascadeDistances.x - float( 2.0 ) ) ) / rpCascadeDistances.xy;
float2 probeTopLeftPosition = float2( 1.0, 1.0 );
float2 normalizedProbeTopLeftPosition = probeTopLeftPosition * rpCascadeDistances.zw;
normalizedOctCoordZeroOne.xy = normalizedProbeTopLeftPosition + octCoordNormalizedToTextureDimensions;
#endif
return normalizedOctCoordZeroOne;
}
void main( PS_IN fragment, out PS_OUT result )
{
half4 bumpMap = tex2D( samp0, fragment.texcoord0.xy );
@ -144,15 +167,14 @@ void main( PS_IN fragment, out PS_OUT result )
float3 globalPosition = fragment.texcoord7.xyz;
// RB: rpGlobalLightOrigin is global view origin
float3 globalEye = normalize( rpGlobalLightOrigin.xyz - globalPosition );
float3 globalView = normalize( rpGlobalEyePos.xyz - globalPosition );
float3 reflectionVector = globalNormal * dot3( globalEye, globalNormal );
reflectionVector = normalize( ( reflectionVector * 2.0f ) - globalEye );
float3 reflectionVector = globalNormal * dot3( globalView, globalNormal );
reflectionVector = normalize( ( reflectionVector * 2.0f ) - globalView );
#if 1
#if 0
// parallax box correction using portal area bounds
float hitScale;
float hitScale = 0.0;
float3 bounds[2];
bounds[0].x = rpWobbleSkyX.x;
bounds[0].y = rpWobbleSkyX.y;
@ -178,7 +200,7 @@ void main( PS_IN fragment, out PS_OUT result )
}
#endif
half vDotN = saturate( dot3( globalEye, globalNormal ) );
half vDotN = saturate( dot3( globalView, globalNormal ) );
#if defined( USE_PBR )
const half metallic = specMapSRGB.g;
@ -236,8 +258,7 @@ void main( PS_IN fragment, out PS_OUT result )
// evaluate diffuse IBL
float2 normalizedOctCoord = octEncode( globalNormal );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
float2 normalizedOctCoordZeroOne = OctTexCoord( globalNormal );
float3 irradiance = tex2D( samp7, normalizedOctCoordZeroOne ).rgb;
float3 diffuseLight = ( kD * irradiance * diffuseColor ) * ao * ( rpDiffuseModifier.xyz * 1.0 );
@ -249,10 +270,11 @@ void main( PS_IN fragment, out PS_OUT result )
float mip = clamp( ( roughness * MAX_REFLECTION_LOD ), 0.0, MAX_REFLECTION_LOD );
//float mip = 0.0;
normalizedOctCoord = octEncode( reflectionVector );
normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
normalizedOctCoordZeroOne = OctTexCoord( reflectionVector );
float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb;
float3 radiance = textureLod( samp8, normalizedOctCoordZeroOne, mip ).rgb * rpLocalLightOrigin.x;
radiance += textureLod( samp9, normalizedOctCoordZeroOne, mip ).rgb * rpLocalLightOrigin.y;
radiance += textureLod( samp10, normalizedOctCoordZeroOne, mip ).rgb * rpLocalLightOrigin.z;
//radiance = float3( 0.0 );
float2 envBRDF = texture( samp3, float2( max( vDotN, 0.0 ), roughness ) ).rg;
@ -266,7 +288,7 @@ void main( PS_IN fragment, out PS_OUT result )
float specAO = ComputeSpecularAO( vDotN, ao, roughness );
float3 specularLight = radiance * ( kS * envBRDF.x + float3( envBRDF.y ) ) * specAO * ( rpSpecularModifier.xyz * 0.5 );
#if 0
#if 1
// Marmoset Horizon Fade trick
const half horizonFade = 1.3;
half horiz = saturate( 1.0 + horizonFade * saturate( dot3( reflectionVector, globalNormal ) ) );
@ -275,11 +297,12 @@ void main( PS_IN fragment, out PS_OUT result )
#endif
half3 lightColor = sRGBToLinearRGB( rpAmbientColor.rgb );
//half3 lightColor = ( rpAmbientColor.rgb );
//result.color.rgb = diffuseLight;
//result.color.rgb = diffuseLight * lightColor;
//result.color.rgb = specularLight;
result.color.rgb = ( diffuseLight + specularLight ) * lightColor * fragment.color.rgb;
result.color.rgb = ( diffuseLight + specularLight * horiz ) * lightColor * fragment.color.rgb;
//result.color.rgb = localNormal.xyz * 0.5 + 0.5;
//result.color.rgb = float3( ao );
result.color.w = fragment.color.a;

View file

@ -66,7 +66,7 @@ struct PS_OUT
#define USE_CAS 0
#define USE_DITHERING 1
#define Dithering_QuantizationSteps 8.0 // 8.0 = 2 ^ 3 quantization bits
#define Dithering_QuantizationSteps 16.0 // 8.0 = 2 ^ 3 quantization bits
#define Dithering_NoiseBoost 1.0
#define Dithering_Wide 1.0
#define DITHER_IN_LINEAR_SPACE 0

View file

@ -2040,7 +2040,7 @@ struct evarPrefix_t
const char* prefix;
};
const evarPrefix_t EvarPrefixes[] =
static const evarPrefix_t EvarPrefixes[] =
{
{ EVAR_STRING, "editor_var " },
{ EVAR_INT, "editor_int " },
@ -2053,15 +2053,15 @@ const evarPrefix_t EvarPrefixes[] =
{ EVAR_SOUND, "editor_snd "}
};
const int NumEvarPrefixes = sizeof( EvarPrefixes ) / sizeof( evarPrefix_t );
static const int NumEvarPrefixes = sizeof( EvarPrefixes ) / sizeof( evarPrefix_t );
typedef struct evar_s
struct LocalEvar_t
{
int type;
idStr fullname;
idStr name;
idStr desc;
} evar_t;
};
#include <d3xp/anim/Anim.h> // idDeclModelDef
@ -2091,15 +2091,15 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
bool exportModels = false;
if( !idStr::Icmp( args.Argv( 1 ), "models" ) )
if( !idStr::Icmp( args.Argv( 1 ), "nomodels" ) )
{
exportModels = true;
common->Printf( "exporting entity decls to FGDs with models:\n" );
exportModels = false;
common->Printf( "exporting entity decls to FGDs without models:\n" );
}
else
{
exportModels = false;
common->Printf( "exporting entity decls to FGDs:\n" );
exportModels = true;
common->Printf( "exporting entity decls to FGDs with models:\n" );
}
if( exportModels )
@ -2361,14 +2361,14 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
}
// collect editor specific spawn flags
idList<evar_t> evars;
idList<LocalEvar_t> evars;
for( int i = 0; i < NumEvarPrefixes; i++ )
{
kv = decl->dict.MatchPrefix( EvarPrefixes[i].prefix );
while( kv )
{
evar_t ev;
LocalEvar_t ev;
ev.fullname = kv->GetKey();
kv->GetKey().Right( kv->GetKey().Length() - strlen( EvarPrefixes[i].prefix ), ev.name );
ev.desc = kv->GetValue();
@ -2386,7 +2386,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
// add missing property to control the radius
evar_t ev;
LocalEvar_t ev;
ev.fullname = "editor_int light";
ev.name = "light";
ev.desc = "light radius";
@ -2401,7 +2401,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
{
// entities with dynamic models
evar_t ev;
LocalEvar_t ev;
ev.fullname = "editor_model model";
ev.name = "model";
ev.desc = "Model Selection (ex mapobjects/model.obj)";
@ -2437,7 +2437,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
//}
// is it an editor var or a regular spawn argument?
evar_t* ev = nullptr;
LocalEvar_t* ev = nullptr;
int vc = evars.Num();
for( int j = 0; j < vc; j++ )
{
@ -2464,7 +2464,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
// add editor_vars that aren't already covered by the default vars
for( int i = 0; i < evars.Num(); i++ )
{
const evar_t* ev = &evars[ i ];
const LocalEvar_t* ev = &evars[ i ];
const idKeyValue* kv2 = dictToWrite.FindKey( ev->name );
if( !kv2 )
@ -2538,7 +2538,7 @@ void idDeclManagerLocal::ExportDeclsToTrenchBroom_f( const idCmdArgs& args )
kv = dictToWrite.GetKeyVal( i );
// is it an editor var or a regular spawn argument?
evar_t* ev = nullptr;
LocalEvar_t* ev = nullptr;
int vc = evars.Num();
for( int j = 0; j < vc; j++ )
{

View file

@ -26,6 +26,10 @@ SOFTWARE.
#ifndef __MATH_SPHERICAL_HARMONICS_H__
#define __MATH_SPHERICAL_HARMONICS_H__
// RB: there is a very good talk by Yuriy O'Donnell that explains the the functions used in this library
// Precomputed Global Illumination in Frostbite (GDC 2018)
// https://www.gdcvault.com/play/1025214/Precomputed-Global-Illumination-in
// https://graphics.stanford.edu/papers/envmap/envmap.pdf
template <typename T, size_t L>

View file

@ -35,7 +35,7 @@ static const int MAX_SSAO_BUFFERS = 2;
static const int MAX_HIERARCHICAL_ZBUFFERS = 6; // native resolution + 5 MIP LEVELS
static const int RADIANCE_CUBEMAP_SIZE = 256;
static const int IRRADIANCE_CUBEMAP_SIZE = 128;
static const int IRRADIANCE_CUBEMAP_SIZE = 30 + 2;
#if 1
static int shadowMapResolutions[MAX_SHADOWMAP_RESOLUTIONS] = { 2048, 1024, 512, 512, 256 };

View file

@ -309,6 +309,11 @@ public:
return opts.height;
}
idVec2i GetUploadResolution() const
{
return idVec2i( opts.width, opts.height );
}
void SetReferencedOutsideLevelLoad()
{
referencedOutsideLevelLoad = true;

View file

@ -751,7 +751,7 @@ void idImage::Reload( bool force )
if( !force )
{
ID_TIME_T current;
if( cubeFiles != CF_2D )
if( cubeFiles == CF_NATIVE || cubeFiles == CF_CAMERA )
{
R_LoadCubeImages( imgName, cubeFiles, NULL, NULL, &current );
}

View file

@ -615,70 +615,62 @@ void idImage::AllocImage()
GL_CheckErrors();
PurgeImage();
int sRGB = r_useSRGB.GetInteger();
switch( opts.format )
{
case FMT_RGBA8:
//internalFormat = GL_RGBA8;
//internalFormat = ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) ) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
internalFormat = ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) ) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
internalFormat = GL_RGBA8;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_XRGB8:
internalFormat = ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) ) ? GL_SRGB : GL_RGB;
internalFormat = GL_RGB;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_RGB565:
//internalFormat = ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) ) ? GL_SRGB : GL_RGB;
internalFormat = GL_RGB;
dataFormat = GL_RGB;
dataType = GL_UNSIGNED_SHORT_5_6_5;
break;
case FMT_ALPHA:
#if 1
if( ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) ) )
{
internalFormat = GL_SRGB;
dataFormat = GL_RED;
}
else
#endif
{
internalFormat = GL_R8;
dataFormat = GL_RED;
}
internalFormat = GL_R8;
dataFormat = GL_RED;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_L8A8:
internalFormat = GL_RG8;
dataFormat = GL_RG;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_LUM8:
internalFormat = GL_R8;
dataFormat = GL_RED;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_INT8:
internalFormat = GL_R8;
dataFormat = GL_RED;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_DXT1:
internalFormat = ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) ) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
//internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_DXT5:
internalFormat = ( glConfig.sRGBFramebufferAvailable && ( sRGB == 1 || sRGB == 3 ) && opts.colorFormat != CFM_YCOCG_DXT5 && opts.colorFormat != CFM_NORMAL_DXT5 ) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
//internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
dataFormat = GL_RGBA;
dataType = GL_UNSIGNED_BYTE;
break;
case FMT_DEPTH:
internalFormat = GL_DEPTH_COMPONENT;
dataFormat = GL_DEPTH_COMPONENT;
@ -731,7 +723,6 @@ void idImage::AllocImage()
internalFormat = GL_R11F_G11F_B10F;
dataFormat = GL_RGB;
dataType = GL_UNSIGNED_INT_10F_11F_11F_REV;
//dataType = GL_FLOAT;
break;
default:

View file

@ -276,10 +276,6 @@ static void R_CheckPortableExtensions()
glConfig.seamlessCubeMapAvailable = GLEW_ARB_seamless_cube_map != 0;
r_useSeamlessCubeMap.SetModified(); // the CheckCvars() next frame will enable / disable it
// GL_ARB_framebuffer_sRGB
glConfig.sRGBFramebufferAvailable = GLEW_ARB_framebuffer_sRGB != 0;
r_useSRGB.SetModified(); // the CheckCvars() next frame will enable / disable it
// GL_ARB_vertex_buffer_object
if( glConfig.driverType == GLDRV_OPENGL_MESA_CORE_PROFILE )
{
@ -1423,21 +1419,6 @@ void idRenderBackend::GL_Clear( bool color, bool depth, bool stencil, byte stenc
glClear( clearFlags );
// RB begin
/*
if( r_useHDR.GetBool() && clearHDR && globalFramebuffers.hdrFBO != NULL )
{
bool isDefaultFramebufferActive = Framebuffer::IsDefaultFramebufferActive();
globalFramebuffers.hdrFBO->Bind();
glClear( clearFlags );
if( isDefaultFramebufferActive )
{
Framebuffer::Unbind();
}
}
*/
if( r_useHDR.GetBool() && clearHDR )
{
bool isDefaultFramebufferActive = Framebuffer::IsDefaultFramebufferActive();
@ -1534,21 +1515,6 @@ void idRenderBackend::CheckCVars()
}
}
if( r_useSRGB.IsModified() )
{
r_useSRGB.ClearModified();
if( glConfig.sRGBFramebufferAvailable )
{
if( r_useSRGB.GetBool() && r_useSRGB.GetInteger() != 3 )
{
glEnable( GL_FRAMEBUFFER_SRGB );
}
else
{
glDisable( GL_FRAMEBUFFER_SRGB );
}
}
}
// SRS - Enable SDL-driven vync changes without restart for UNIX-like OSs
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
@ -1570,7 +1536,6 @@ void idRenderBackend::CheckCVars()
}
#endif
// SRS end
if( r_antiAliasing.IsModified() )
{
switch( r_antiAliasing.GetInteger() )

View file

@ -3,7 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2014-2016 Robert Beckebans
Copyright (C) 2014-2021 Robert Beckebans
Copyright (C) 2014-2016 Kot in Action Creative Artel
Copyright (C) 2016-2017 Dustin Land
@ -1702,9 +1702,38 @@ void idRenderBackend::DBG_ShowLights()
==============
RB_ShowViewEnvprobes
Visualize all environemnt probes used in the current scene
Visualize all environment probes used in the current scene
==============
*/
class idSort_DebugCompareViewEnvprobe : public idSort_Quick< RenderEnvprobeLocal*, idSort_DebugCompareViewEnvprobe >
{
idVec3 viewOrigin;
public:
idSort_DebugCompareViewEnvprobe( const idVec3& origin )
{
viewOrigin = origin;
}
int Compare( RenderEnvprobeLocal* const& a, RenderEnvprobeLocal* const& b ) const
{
float adist = ( viewOrigin - a->parms.origin ).LengthSqr();
float bdist = ( viewOrigin - b->parms.origin ).LengthSqr();
if( adist < bdist )
{
return -1;
}
if( adist > bdist )
{
return 1;
}
return 0;
}
};
void idRenderBackend::DBG_ShowViewEnvprobes()
{
if( !r_showViewEnvprobes.GetInteger() )
@ -1719,16 +1748,15 @@ void idRenderBackend::DBG_ShowViewEnvprobes()
{
count++;
renderProgManager.BindShader_Octahedron();
renderProgManager.BindShader_DebugOctahedron();
GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_DEPTHMASK );
GL_Color( 1.0f, 1.0f, 1.0f );
float modelMatrix[16];
idMat3 axis;
axis.Identity();
float modelMatrix[16];
R_AxisToModelMatrix( axis, vProbe->globalOrigin, modelMatrix );
idRenderMatrix modelRenderMatrix;
@ -1757,16 +1785,26 @@ void idRenderBackend::DBG_ShowViewEnvprobes()
renderProgManager.SetUniformValue( RENDERPARM_LOCALVIEWORIGIN, localViewOrigin.ToFloatPtr() ); // rpLocalViewOrigin
idVec4 textureSize;
GL_SelectTexture( 0 );
if( r_showViewEnvprobes.GetInteger() >= 2 )
{
vProbe->irradianceImage->Bind();
idVec2i res = vProbe->irradianceImage->GetUploadResolution();
textureSize.Set( res.x, res.y, 1.0f / res.x, 1.0f / res.y );
}
else
{
vProbe->radianceImage->Bind();
idVec2i res = vProbe->radianceImage->GetUploadResolution();
textureSize.Set( res.x, res.y, 1.0f / res.x, 1.0f / res.y );
}
renderProgManager.SetUniformValue( RENDERPARM_CASCADEDISTANCES, textureSize.ToFloatPtr() );
DrawElementsWithCounters( &zeroOneSphereSurface );
// non-hidden lines
@ -1785,6 +1823,457 @@ void idRenderBackend::DBG_ShowViewEnvprobes()
}
#endif
}
//if( r_showViewEnvprobes.GetInteger() >= 3 )
if( tr.primaryWorld )
{
/*
idList<viewEnvprobe_t*, TAG_RENDER_ENVPROBE> viewEnvprobes;
for( viewEnvprobe_t* vProbe = viewDef->viewEnvprobes; vProbe != NULL; vProbe = vProbe->next )
{
viewEnvprobes.AddUnique( vProbe );
}
*/
idList<RenderEnvprobeLocal*, TAG_RENDER_ENVPROBE> viewEnvprobes;
for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ )
{
RenderEnvprobeLocal* vProbe = tr.primaryWorld->envprobeDefs[i];
if( vProbe )
{
viewEnvprobes.AddUnique( vProbe );
}
}
if( viewEnvprobes.Num() == 0 )
{
return;
}
idVec3 testOrigin = viewDef->renderView.vieworg;
//testOrigin += viewDef->renderView.viewaxis[0] * 150.0f;
//testOrigin -= viewDef->renderView.viewaxis[2] * 16.0f;
// sort by distance
viewEnvprobes.SortWithTemplate( idSort_DebugCompareViewEnvprobe( testOrigin ) );
// draw 3 nearest probes
renderProgManager.BindShader_Color();
const int numColors = 3;
static idVec4 colors[numColors] = { colorRed, colorGreen, colorBlue };
// form a triangle of the 3 closest probes
idVec3 verts[3];
for( int i = 0; i < 3; i++ )
{
verts[i] = viewEnvprobes[0]->parms.origin;
}
for( int i = 0; i < viewEnvprobes.Num() && i < 3; i++ )
{
RenderEnvprobeLocal* vProbe = viewEnvprobes[i];
verts[i] = vProbe->parms.origin;
}
idVec3 closest = R_ClosestPointPointTriangle( testOrigin, verts[0], verts[1], verts[2] );
idVec3 barycentricWeights;
// find the barycentric coordinates
float denom = idWinding::TriangleArea( verts[0], verts[1], verts[2] );
if( denom == 0 )
{
// all points at same location
barycentricWeights.Set( 1, 0, 0 );
}
else
{
float a, b, c;
a = idWinding::TriangleArea( closest, verts[1], verts[2] ) / denom;
b = idWinding::TriangleArea( closest, verts[2], verts[0] ) / denom;
c = idWinding::TriangleArea( closest, verts[0], verts[1] ) / denom;
barycentricWeights.Set( a, b, c );
}
idMat3 axis;
axis.Identity();
for( int i = 0; i < viewEnvprobes.Num() && i < 3; i++ )
{
RenderEnvprobeLocal* vProbe = viewEnvprobes[i];
verts[i] = vProbe->parms.origin;
//GL_Color( colors[i] );
idVec4 color = Lerp( colorBlack, colors[i], barycentricWeights[i] );
GL_Color( color );
idRenderMatrix modelRenderMatrix;
idRenderMatrix::CreateFromOriginAxis( vProbe->parms.origin, axis, modelRenderMatrix );
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
const float size = 16.0f;
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
idRenderMatrix inverseBaseModelProject;
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
idRenderMatrix invProjectMVPMatrix;
idRenderMatrix::Multiply( viewDef->worldSpace.mvp, inverseBaseModelProject, invProjectMVPMatrix );
RB_SetMVP( invProjectMVPMatrix );
DrawElementsWithCounters( &zeroOneSphereSurface );
}
// draw closest hit
{
GL_Color( colorYellow );
idRenderMatrix modelRenderMatrix;
idRenderMatrix::CreateFromOriginAxis( closest, axis, modelRenderMatrix );
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
const float size = 4.0f;
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
idRenderMatrix inverseBaseModelProject;
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
idRenderMatrix invProjectMVPMatrix;
idRenderMatrix::Multiply( viewDef->worldSpace.mvp, inverseBaseModelProject, invProjectMVPMatrix );
RB_SetMVP( invProjectMVPMatrix );
DrawElementsWithCounters( &zeroOneSphereSurface );
}
}
}
void idRenderBackend::DBG_ShowLightGrid()
{
if( r_showLightGrid.GetInteger() <= 0 || !tr.primaryWorld )
{
return;
}
// all volumes are expressed in world coordinates
GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_DEPTHMASK );
GL_Color( 1.0f, 1.0f, 1.0f );
idMat3 axis;
axis.Identity();
// only show current area
int cameraArea = tr.primaryWorld->PointInArea( viewDef->renderView.vieworg );
if( cameraArea == -1 && r_showLightGrid.GetInteger() < 3 )
{
return;
}
const int numColors = 7;
static idVec4 colors[numColors] = { colorBrown, colorBlue, colorCyan, colorGreen, colorYellow, colorRed, colorWhite };
for( int a = 0; a < tr.primaryWorld->NumAreas(); a++ )
{
if( r_showLightGrid.GetInteger() < 3 && ( cameraArea != a ) )
{
continue;
}
portalArea_t* area = &tr.primaryWorld->portalAreas[a];
// use rpGlobalLightOrigin for lightGrid center
idVec4 lightGridOrigin( area->lightGrid.lightGridOrigin.x, area->lightGrid.lightGridOrigin.y, area->lightGrid.lightGridOrigin.z, 1.0f );
idVec4 lightGridSize( area->lightGrid.lightGridSize.x, area->lightGrid.lightGridSize.y, area->lightGrid.lightGridSize.z, 1.0f );
idVec4 lightGridBounds( area->lightGrid.lightGridBounds[0], area->lightGrid.lightGridBounds[1], area->lightGrid.lightGridBounds[2], 1.0f );
renderProgManager.SetUniformValue( RENDERPARM_GLOBALLIGHTORIGIN, lightGridOrigin.ToFloatPtr() );
renderProgManager.SetUniformValue( RENDERPARM_JITTERTEXSCALE, lightGridSize.ToFloatPtr() );
renderProgManager.SetUniformValue( RENDERPARM_JITTERTEXOFFSET, lightGridBounds.ToFloatPtr() );
// individual probe sizes on the atlas image
idVec4 probeSize;
probeSize[0] = area->lightGrid.imageSingleProbeSize - area->lightGrid.imageBorderSize;
probeSize[1] = area->lightGrid.imageSingleProbeSize;
probeSize[2] = area->lightGrid.imageBorderSize;
probeSize[3] = float( area->lightGrid.imageSingleProbeSize - area->lightGrid.imageBorderSize ) / area->lightGrid.imageSingleProbeSize;
renderProgManager.SetUniformValue( RENDERPARM_SCREENCORRECTIONFACTOR, probeSize.ToFloatPtr() ); // rpScreenCorrectionFactor
for( int i = 0; i < area->lightGrid.lightGridPoints.Num(); i++ )
{
lightGridPoint_t* gridPoint = &area->lightGrid.lightGridPoints[i];
if( !gridPoint->valid && r_showLightGrid.GetInteger() < 3 )
{
continue;
}
idVec3 distanceToCam = gridPoint->origin - viewDef->renderView.vieworg;
if( distanceToCam.LengthSqr() > ( 1024 * 1024 ) && r_showLightGrid.GetInteger() < 3 )
{
continue;
}
#if 0
if( i > 53 )
{
break;
}
#endif
// move center into the cube so we can void using negative results with GetBaseGridCoord
idVec3 gridPointOrigin = gridPoint->origin + idVec3( 4, 4, 4 );
idVec4 localViewOrigin( 1.0f );
idVec4 globalViewOrigin;
globalViewOrigin.x = viewDef->renderView.vieworg.x;
globalViewOrigin.y = viewDef->renderView.vieworg.y;
globalViewOrigin.z = viewDef->renderView.vieworg.z;
globalViewOrigin.w = 1.0f;
float modelMatrix[16];
R_AxisToModelMatrix( axis, gridPointOrigin, modelMatrix );
R_GlobalPointToLocal( modelMatrix, viewDef->renderView.vieworg, localViewOrigin.ToVec3() );
renderProgManager.SetUniformValue( RENDERPARM_LOCALVIEWORIGIN, localViewOrigin.ToFloatPtr() ); // rpLocalViewOrigin
// RB: if we want to get the normals in world space so we need the model -> world matrix
idRenderMatrix modelMatrix2;
idRenderMatrix::Transpose( *( idRenderMatrix* )modelMatrix, modelMatrix2 );
renderProgManager.SetUniformValue( RENDERPARM_MODELMATRIX_X, &modelMatrix2[0][0] );
renderProgManager.SetUniformValue( RENDERPARM_MODELMATRIX_Y, &modelMatrix2[1][0] );
renderProgManager.SetUniformValue( RENDERPARM_MODELMATRIX_Z, &modelMatrix2[2][0] );
renderProgManager.SetUniformValue( RENDERPARM_MODELMATRIX_W, &modelMatrix2[3][0] );
#if 0
renderProgManager.BindShader_Color();
int gridCoord[3];
area->lightGrid.GetBaseGridCoord( gridPoint->origin, gridCoord );
idVec3 color = area->lightGrid.GetGridCoordDebugColor( gridCoord );
//idVec3 color = area->lightGrid.GetProbeIndexDebugColor( i );
//idVec4 color = colors[ i % numColors ];
GL_Color( color );
#else
if( r_showLightGrid.GetInteger() == 4 || !area->lightGrid.GetIrradianceImage() )
{
renderProgManager.BindShader_Color();
idVec4 color;
if( !gridPoint->valid )
{
color = colorPurple;
}
else
{
color = colors[ a % numColors ];
}
GL_Color( color );
}
else
{
renderProgManager.BindShader_DebugLightGrid();
GL_SelectTexture( 0 );
area->lightGrid.GetIrradianceImage()->Bind();
idVec2i res = area->lightGrid.GetIrradianceImage()->GetUploadResolution();
idVec4 textureSize( res.x, res.y, 1.0f / res.x, 1.0f / res.y );
renderProgManager.SetUniformValue( RENDERPARM_CASCADEDISTANCES, textureSize.ToFloatPtr() );
}
#endif
idRenderMatrix modelRenderMatrix;
idRenderMatrix::CreateFromOriginAxis( gridPoint->origin, axis, modelRenderMatrix );
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
const float size = 3.0f;
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
idRenderMatrix inverseBaseModelProject;
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
idRenderMatrix invProjectMVPMatrix;
idRenderMatrix::Multiply( viewDef->worldSpace.mvp, inverseBaseModelProject, invProjectMVPMatrix );
RB_SetMVP( invProjectMVPMatrix );
DrawElementsWithCounters( &zeroOneSphereSurface );
//DrawElementsWithCounters( &zeroOneCubeSurface );
}
}
if( r_showLightGrid.GetInteger() == 2 )
{
// show 8 nearest grid points around the camera and illustrate how the trilerping works
idVec3 lightOrigin;
int pos[3];
int gridPointIndex;
int gridPointIndex2;
lightGridPoint_t* gridPoint;
lightGridPoint_t* gridPoint2;
float frac[3];
int gridStep[3];
float totalFactor;
portalArea_t* area = &tr.primaryWorld->portalAreas[cameraArea];
renderProgManager.BindShader_Color();
lightOrigin = viewDef->renderView.vieworg;
lightOrigin += viewDef->renderView.viewaxis[0] * 150.0f;
lightOrigin -= viewDef->renderView.viewaxis[2] * 16.0f;
// draw sample origin we want to test the grid with
{
GL_Color( colorYellow );
idRenderMatrix modelRenderMatrix;
idRenderMatrix::CreateFromOriginAxis( lightOrigin, axis, modelRenderMatrix );
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
const float size = 2.0f;
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
idRenderMatrix inverseBaseModelProject;
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
idRenderMatrix invProjectMVPMatrix;
idRenderMatrix::Multiply( viewDef->worldSpace.mvp, inverseBaseModelProject, invProjectMVPMatrix );
RB_SetMVP( invProjectMVPMatrix );
DrawElementsWithCounters( &zeroOneSphereSurface );
}
// find base grid point
lightOrigin -= area->lightGrid.lightGridOrigin;
for( int i = 0; i < 3; i++ )
{
float v;
v = lightOrigin[i] * ( 1.0f / area->lightGrid.lightGridSize[i] );
pos[i] = floor( v );
frac[i] = v - pos[i];
if( pos[i] < 0 )
{
pos[i] = 0;
}
else if( pos[i] >= area->lightGrid.lightGridBounds[i] - 1 )
{
pos[i] = area->lightGrid.lightGridBounds[i] - 1;
}
}
// trilerp the light value
gridStep[0] = 1;
gridStep[1] = area->lightGrid.lightGridBounds[0];
gridStep[2] = area->lightGrid.lightGridBounds[0] * area->lightGrid.lightGridBounds[1];
gridPointIndex = pos[0] * gridStep[0] + pos[1] * gridStep[1] + pos[2] * gridStep[2];
gridPoint = &area->lightGrid.lightGridPoints[ gridPointIndex ];
totalFactor = 0;
idVec3 cornerOffsets[8];
for( int i = 0; i < 8; i++ )
{
float factor = 1.0;
gridPoint2 = gridPoint;
gridPointIndex2 = gridPointIndex;
for( int j = 0; j < 3; j++ )
{
cornerOffsets[i][j] = i & ( 1 << j );
if( cornerOffsets[i][j] > 0.0f )
{
factor *= frac[j];
#if 1
gridPointIndex2 += gridStep[j];
if( gridPointIndex2 < 0 || gridPointIndex2 >= area->lightGrid.lightGridPoints.Num() )
{
// ignore values outside lightgrid
continue;
}
gridPoint2 = &area->lightGrid.lightGridPoints[ gridPointIndex2 ];
#else
if( pos[j] + 1 > area->lightGrid.lightGridBounds[j] - 1 )
{
// ignore values outside lightgrid
break;
}
gridPoint2 += gridStep[j];
#endif
}
else
{
factor *= ( 1.0f - frac[j] );
}
}
if( !gridPoint2->valid )
{
// ignore samples in walls
continue;
}
totalFactor += factor;
idVec4 color = Lerp( colorBlack, colorGreen, factor );
GL_Color( color );
idRenderMatrix modelRenderMatrix;
idRenderMatrix::CreateFromOriginAxis( gridPoint2->origin, axis, modelRenderMatrix );
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
const float size = 4.0f;
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
idRenderMatrix inverseBaseModelProject;
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
idRenderMatrix invProjectMVPMatrix;
idRenderMatrix::Multiply( viewDef->worldSpace.mvp, inverseBaseModelProject, invProjectMVPMatrix );
RB_SetMVP( invProjectMVPMatrix );
DrawElementsWithCounters( &zeroOneSphereSurface );
}
// draw main grid point where camera position snapped to
GL_Color( colorRed );
idRenderMatrix modelRenderMatrix;
idRenderMatrix::CreateFromOriginAxis( gridPoint->origin, axis, modelRenderMatrix );
// calculate the matrix that transforms the unit cube to exactly cover the model in world space
const float size = 5.0f;
idBounds debugBounds( idVec3( -size ), idVec3( size ) );
idRenderMatrix inverseBaseModelProject;
idRenderMatrix::OffsetScaleForBounds( modelRenderMatrix, debugBounds, inverseBaseModelProject );
idRenderMatrix invProjectMVPMatrix;
idRenderMatrix::Multiply( viewDef->worldSpace.mvp, inverseBaseModelProject, invProjectMVPMatrix );
RB_SetMVP( invProjectMVPMatrix );
DrawElementsWithCounters( &zeroOneSphereSurface );
}
}
void idRenderBackend::DBG_ShowShadowMapLODs()
@ -3125,6 +3614,11 @@ idRenderBackend::DBG_RenderDebugTools
*/
void idRenderBackend::DBG_RenderDebugTools( drawSurf_t** drawSurfs, int numDrawSurfs )
{
if( viewDef->renderView.rdflags & RDF_IRRADIANCE )
{
return;
}
// don't do much if this was a 2D rendering
if( !viewDef->viewEntitys )
{
@ -3158,6 +3652,7 @@ void idRenderBackend::DBG_RenderDebugTools( drawSurf_t** drawSurfs, int numDrawS
DBG_ShowViewEntitys( viewDef->viewEntitys );
DBG_ShowLights();
// RB begin
DBG_ShowLightGrid();
DBG_ShowViewEnvprobes();
DBG_ShowShadowMapLODs();
DBG_ShowShadowMaps();

View file

@ -1200,9 +1200,13 @@ const int INTERACTION_TEXUNIT_JITTER = 6;
#if defined( USE_VULKAN )
const int INTERACTION_TEXUNIT_AMBIENT_CUBE1 = 5;
const int INTERACTION_TEXUNIT_SPECULAR_CUBE1 = 6;
const int INTERACTION_TEXUNIT_SPECULAR_CUBE2 = 7;
const int INTERACTION_TEXUNIT_SPECULAR_CUBE3 = 8;
#else
const int INTERACTION_TEXUNIT_AMBIENT_CUBE1 = 7;
const int INTERACTION_TEXUNIT_SPECULAR_CUBE1 = 8;
const int INTERACTION_TEXUNIT_SPECULAR_CUBE2 = 9;
const int INTERACTION_TEXUNIT_SPECULAR_CUBE3 = 10;
#endif
/*
@ -1323,7 +1327,7 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useFas
const textureUsage_t specUsage = din->specularImage->GetUsage();
// RB begin
if( useIBL )
if( useIBL && currentSpace->useLightGrid && r_useLightGrid.GetBool() )
{
idVec4 probeMins, probeMaxs, probeCenter;
@ -1344,6 +1348,108 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useFas
SetVertexParm( RENDERPARM_WOBBLESKY_Y, probeMaxs.ToFloatPtr() );
SetVertexParm( RENDERPARM_WOBBLESKY_Z, probeCenter.ToFloatPtr() );
// use rpGlobalLightOrigin for lightGrid center
idVec4 lightGridOrigin( currentSpace->lightGridOrigin.x, currentSpace->lightGridOrigin.y, currentSpace->lightGridOrigin.z, 1.0f );
idVec4 lightGridSize( currentSpace->lightGridSize.x, currentSpace->lightGridSize.y, currentSpace->lightGridSize.z, 1.0f );
idVec4 lightGridBounds( currentSpace->lightGridBounds[0], currentSpace->lightGridBounds[1], currentSpace->lightGridBounds[2], 1.0f );
renderProgManager.SetUniformValue( RENDERPARM_GLOBALLIGHTORIGIN, lightGridOrigin.ToFloatPtr() );
renderProgManager.SetUniformValue( RENDERPARM_JITTERTEXSCALE, lightGridSize.ToFloatPtr() );
renderProgManager.SetUniformValue( RENDERPARM_JITTERTEXOFFSET, lightGridBounds.ToFloatPtr() );
// individual probe sizes on the atlas image
idVec4 probeSize;
probeSize[0] = currentSpace->lightGridAtlasSingleProbeSize - currentSpace->lightGridAtlasBorderSize;
probeSize[1] = currentSpace->lightGridAtlasSingleProbeSize;
probeSize[2] = currentSpace->lightGridAtlasBorderSize;
probeSize[3] = float( currentSpace->lightGridAtlasSingleProbeSize - currentSpace->lightGridAtlasBorderSize ) / currentSpace->lightGridAtlasSingleProbeSize;
renderProgManager.SetUniformValue( RENDERPARM_SCREENCORRECTIONFACTOR, probeSize.ToFloatPtr() ); // rpScreenCorrectionFactor
// specular cubemap blend weights
renderProgManager.SetUniformValue( RENDERPARM_LOCALLIGHTORIGIN, viewDef->radianceImageBlends.ToFloatPtr() );
if( specUsage == TD_SPECULAR_PBR_RMAO || specUsage == TD_SPECULAR_PBR_RMAOD )
{
// PBR path with roughness, metal and AO
if( din->surf->jointCache )
{
renderProgManager.BindShader_ImageBasedLightGridSkinned_PBR();
}
else
{
renderProgManager.BindShader_ImageBasedLightGrid_PBR();
}
}
else
{
if( din->surf->jointCache )
{
renderProgManager.BindShader_ImageBasedLightGridSkinned();
}
else
{
renderProgManager.BindShader_ImageBasedLightGrid();
}
}
GL_SelectTexture( INTERACTION_TEXUNIT_FALLOFF );
globalImages->brdfLutImage->Bind();
GL_SelectTexture( INTERACTION_TEXUNIT_PROJECTION );
#if defined( USE_VULKAN )
globalImages->whiteImage->Bind();
#else
if( !r_useSSAO.GetBool() )
{
globalImages->whiteImage->Bind();
}
else
{
globalImages->ambientOcclusionImage[0]->Bind();
}
#endif
GL_SelectTexture( INTERACTION_TEXUNIT_AMBIENT_CUBE1 );
currentSpace->lightGridAtlasImage->Bind();
idVec2i res = currentSpace->lightGridAtlasImage->GetUploadResolution();
idVec4 textureSize( res.x, res.y, 1.0f / res.x, 1.0f / res.y );
renderProgManager.SetUniformValue( RENDERPARM_CASCADEDISTANCES, textureSize.ToFloatPtr() );
GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE1 );
viewDef->radianceImages[0]->Bind();
GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE2 );
viewDef->radianceImages[1]->Bind();
GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE3 );
viewDef->radianceImages[2]->Bind();
}
else if( useIBL )
{
idVec4 probeMins, probeMaxs, probeCenter;
probeMins[0] = viewDef->globalProbeBounds[0][0];
probeMins[1] = viewDef->globalProbeBounds[0][1];
probeMins[2] = viewDef->globalProbeBounds[0][2];
probeMins[3] = viewDef->globalProbeBounds.IsCleared() ? 0.0f : 1.0f;
probeMaxs[0] = viewDef->globalProbeBounds[1][0];
probeMaxs[1] = viewDef->globalProbeBounds[1][1];
probeMaxs[2] = viewDef->globalProbeBounds[1][2];
probeMaxs[3] = 0.0f;
idVec3 center = viewDef->globalProbeBounds.GetCenter();
probeCenter.Set( center.x, center.y, center.z, 1.0f );
SetVertexParm( RENDERPARM_WOBBLESKY_X, probeMins.ToFloatPtr() );
SetVertexParm( RENDERPARM_WOBBLESKY_Y, probeMaxs.ToFloatPtr() );
SetVertexParm( RENDERPARM_WOBBLESKY_Z, probeCenter.ToFloatPtr() );
// specular cubemap blend weights
renderProgManager.SetUniformValue( RENDERPARM_LOCALLIGHTORIGIN, viewDef->radianceImageBlends.ToFloatPtr() );
if( specUsage == TD_SPECULAR_PBR_RMAO || specUsage == TD_SPECULAR_PBR_RMAOD )
{
// PBR path with roughness, metal and AO
@ -1385,26 +1491,17 @@ void idRenderBackend::DrawSingleInteraction( drawInteraction_t* din, bool useFas
}
#endif
// TODO bind the 3 closest probes
GL_SelectTexture( INTERACTION_TEXUNIT_AMBIENT_CUBE1 );
if( viewDef->irradianceImage )
{
viewDef->irradianceImage->Bind();
}
else
{
globalImages->defaultUACIrradianceCube->Bind();
}
viewDef->irradianceImage->Bind();
GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE1 );
if( viewDef->radianceImage )
{
viewDef->radianceImage->Bind();
}
else
{
globalImages->defaultUACRadianceCube->Bind();
}
viewDef->radianceImages[0]->Bind();
GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE2 );
viewDef->radianceImages[1]->Bind();
GL_SelectTexture( INTERACTION_TEXUNIT_SPECULAR_CUBE3 );
viewDef->radianceImages[2]->Bind();
}
else if( setInteractionShader )
{
@ -2229,10 +2326,6 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
renderProgManager.SetRenderParm( RENDERPARM_AMBIENT_COLOR, ambientColor.ToFloatPtr() );
// use rpGlobalLightOrigin for camera center
idVec4 globalViewOrigin( viewDef->renderView.vieworg.x, viewDef->renderView.vieworg.y, viewDef->renderView.vieworg.z, 1.0f );
SetVertexParm( RENDERPARM_GLOBALLIGHTORIGIN, globalViewOrigin.ToFloatPtr() );
// setup renderparms assuming we will be drawing trivial surfaces first
RB_SetupForFastPathInteractions( diffuseColor, specularColor );
@ -2318,16 +2411,6 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
R_GlobalPointToLocal( drawSurf->space->modelMatrix, viewDef->renderView.vieworg, localViewOrigin.ToVec3() );
SetVertexParm( RENDERPARM_LOCALVIEWORIGIN, localViewOrigin.ToFloatPtr() );
//if( !isWorldModel )
//{
// // tranform the light direction into model local space
// idVec3 globalLightDirection( 0.0f, 0.0f, -1.0f ); // HACK
// idVec4 localLightDirection( 0.0f );
// R_GlobalVectorToLocal( drawSurf->space->modelMatrix, globalLightDirection, localLightDirection.ToVec3() );
//
// SetVertexParm( RENDERPARM_LOCALLIGHTORIGIN, localLightDirection.ToFloatPtr() );
//}
// RB: if we want to store the normals in world space so we need the model -> world matrix
idRenderMatrix modelMatrix;
idRenderMatrix::Transpose( *( idRenderMatrix* )drawSurf->space->modelMatrix, modelMatrix );
@ -2340,27 +2423,6 @@ void idRenderBackend::AmbientPass( const drawSurf_t* const* drawSurfs, int numDr
SetVertexParms( RENDERPARM_MODELVIEWMATRIX_X, modelViewMatrixTranspose, 4 );
}
#if 0
if( !isWorldModel )
{
idVec4 directedColor;
directedColor.x = drawSurf->space->gridDirectedLight.x;
directedColor.y = drawSurf->space->gridDirectedLight.y;
directedColor.z = drawSurf->space->gridDirectedLight.z;
directedColor.w = 1;
idVec4 ambientColor;
ambientColor.x = drawSurf->space->gridAmbientLight.x;
ambientColor.y = drawSurf->space->gridAmbientLight.y;
ambientColor.z = drawSurf->space->gridAmbientLight.z;
ambientColor.w = 1;
renderProgManager.SetRenderParm( RENDERPARM_COLOR, directedColor.ToFloatPtr() );
renderProgManager.SetRenderParm( RENDERPARM_AMBIENT_COLOR, ambientColor.ToFloatPtr() );
}
float ambientBoost = r_useHDR.GetBool() ? 1.5 : 1.0;
#endif
/*
uint64 surfGLState = 0;

View file

@ -427,6 +427,7 @@ private:
void DBG_ShowDominantTris( drawSurf_t** drawSurfs, int numDrawSurfs );
void DBG_ShowEdges( drawSurf_t** drawSurfs, int numDrawSurfs );
void DBG_ShowLights();
void DBG_ShowLightGrid(); // RB
void DBG_ShowViewEnvprobes(); // RB
void DBG_ShowShadowMapLODs(); // RB
void DBG_ShowPortals();

View file

@ -450,6 +450,17 @@ struct viewEntity_t
// be linked to the lights or added to the drawsurf list in a serial code section
drawSurf_t* drawSurfs;
// RB: use light grid of the best area this entity is in
bool useLightGrid;
idImage* lightGridAtlasImage;
int lightGridAtlasSingleProbeSize; // including border
int lightGridAtlasBorderSize;
idVec3 lightGridOrigin;
idVec3 lightGridSize;
int lightGridBounds[3];
// RB end
// R_AddSingleModel will build a chain of parameters here to setup shadow volumes
staticShadowVolumeParms_t* staticShadowVolumes;
dynamicShadowVolumeParms_t* dynamicShadowVolumes;
@ -498,7 +509,28 @@ struct calcEnvprobeParms_t
idStr filename;
// output
halfFloat_t* outBuffer; // HDR R11G11B11F packed atlas
halfFloat_t* outBuffer; // HDR R11G11B11F packed octahedron atlas
int time; // execution time in milliseconds
};
static const int LIGHTGRID_IRRADIANCE_BORDER_SIZE = 2; // one pixel border all around the octahedron so 2 on each side
static const int LIGHTGRID_IRRADIANCE_SIZE = 30 + LIGHTGRID_IRRADIANCE_BORDER_SIZE;
struct calcLightGridPointParms_t
{
// input
byte* radiance[6]; // HDR RGB16F standard OpenGL cubemap sides
int gridCoord[3];
int outWidth; // LIGHTGRID_IRRADIANCE_SIZE
int outHeight;
// output
SphericalHarmonicsT<idVec3, 3> shRadiance; // L3 Spherical Harmonics
halfFloat_t* outBuffer; // HDR R11G11B11F octahedron LIGHTGRID_IRRADIANCE_SIZE^2
int time; // execution time in milliseconds
};
// RB end
@ -616,8 +648,8 @@ struct viewDef_t
idBounds globalProbeBounds;
idRenderMatrix inverseBaseEnvProbeProject; // the matrix for deforming the 'zeroOneCubeModel' to exactly cover the environent probe volume in world space
idImage* irradianceImage; // cubemap image used for diffuse IBL by backend
idImage* radianceImage; // cubemap image used for specular IBL by backend
// RB end
idImage* radianceImages[3]; // cubemap image used for specular IBL by backend
idVec4 radianceImageBlends; // blending weights
};
@ -858,6 +890,7 @@ public:
virtual void RenderCommandBuffers( const emptyCommand_t* commandBuffers );
virtual void TakeScreenshot( int width, int height, const char* fileName, int downSample, renderView_t* ref, int exten );
virtual byte* CaptureRenderToBuffer( int width, int height, renderView_t* ref );
virtual void CropRenderSize( int width, int height );
virtual void CaptureRenderToImage( const char* imageName, bool clearColorAfterCopy = false );
virtual void CaptureRenderToFile( const char* fileName, bool fixAlpha );
@ -940,6 +973,8 @@ public:
unsigned short gammaTable[256]; // brightness / gamma modify this
idMat3 cubeAxis[6]; // RB
srfTriangles_t* unitSquareTriangles;
srfTriangles_t* zeroOneCubeTriangles;
srfTriangles_t* zeroOneSphereTriangles;
@ -956,8 +991,9 @@ public:
idParallelJobList* frontEndJobList;
// RB irradiance and GGX background jobs
idParallelJobList* envprobeJobList;
idList<calcEnvprobeParms_t*> irradianceJobs;
idParallelJobList* envprobeJobList;
idList<calcEnvprobeParms_t*> envprobeJobs;
idList<calcLightGridPointParms_t*> lightGridJobs;
idRenderBackend backend;
@ -1029,7 +1065,6 @@ extern idCVar r_useShadowDepthBounds; // use depth bounds test on individual sh
extern idCVar r_useShadowMapping; // use shadow mapping instead of stencil shadows
extern idCVar r_useHalfLambertLighting; // use Half-Lambert lighting instead of classic Lambert
extern idCVar r_useHDR;
extern idCVar r_useSRGB;
extern idCVar r_useSeamlessCubeMap;
// RB end
@ -1175,6 +1210,9 @@ extern idCVar r_useHierarchicalDepthBuffer;
extern idCVar r_usePBR;
extern idCVar r_pbrDebug;
extern idCVar r_showViewEnvprobes;
extern idCVar r_showLightGrid; // show Quake 3 style light grid points
extern idCVar r_useLightGrid;
extern idCVar r_exposure;
// RB end
@ -1353,6 +1391,85 @@ RENDERWORLD_PORTALS
viewEntity_t* R_SetEntityDefViewEntity( idRenderEntityLocal* def );
viewLight_t* R_SetLightDefViewLight( idRenderLightLocal* def );
/*
============================================================
RENDERWORLD_ENVPROBES
============================================================
*/
void R_SampleCubeMapHDR( const idVec3& dir, int size, byte* buffers[6], float result[3], float& u, float& v );
void R_SampleCubeMapHDR16F( const idVec3& dir, int size, halfFloat_t* buffers[6], float result[3], float& u, float& v );
idVec2 NormalizedOctCoord( int x, int y, const int probeSideLength );
class CommandlineProgressBar
{
private:
size_t tics = 0;
size_t nextTicCount = 0;
int count = 0;
int expectedCount = 0;
public:
CommandlineProgressBar( int _expectedCount )
{
expectedCount = _expectedCount;
}
void Start()
{
common->Printf( "0%% 10 20 30 40 50 60 70 80 90 100%%\n" );
common->Printf( "|----|----|----|----|----|----|----|----|----|----|\n" );
common->UpdateScreen( false );
}
void Increment()
{
if( ( count + 1 ) >= nextTicCount )
{
size_t ticsNeeded = ( size_t )( ( ( double )( count + 1 ) / expectedCount ) * 50.0 );
do
{
common->Printf( "*" );
}
while( ++tics < ticsNeeded );
nextTicCount = ( size_t )( ( tics / 50.0 ) * expectedCount );
if( count == ( expectedCount - 1 ) )
{
if( tics < 51 )
{
common->Printf( "*" );
}
common->Printf( "\n" );
}
common->UpdateScreen( false );
}
count++;
}
void Reset()
{
count = 0;
tics = 0;
nextTicCount = 0;
}
void Reset( int expected )
{
expectedCount = expected;
count = 0;
tics = 0;
nextTicCount = 0;
}
};
/*
====================================================================
@ -1501,6 +1618,9 @@ void R_InitDrawSurfFromTri( drawSurf_t& ds, srfTriangles_t& tri );
// time, rather than being re-created each frame in the frame temporary buffers.
void R_CreateStaticBuffersForTri( srfTriangles_t& tri );
// RB
idVec3 R_ClosestPointPointTriangle( const idVec3& point, const idVec3& vertex1, const idVec3& vertex2, const idVec3& vertex3 );
// deformable meshes precalculate as much as possible from a base frame, then generate
// complete srfTriangles_t from just a new set of vertexes
struct deformInfo_t
@ -1559,7 +1679,6 @@ struct localTrace_t
localTrace_t R_LocalTrace( const idVec3& start, const idVec3& end, const float radius, const srfTriangles_t* tri );
/*
============================================================
@ -1590,6 +1709,9 @@ void RB_DrawBounds( const idBounds& bounds );
void RB_ShutdownDebugTools();
void RB_SetVertexColorParms( stageVertexColor_t svc );
//=============================================
#include "ResolutionScale.h"

View file

@ -109,10 +109,17 @@ void idRenderProgManager::Init()
{ BUILTIN_VERTEX_COLOR, "builtin/vertex_color", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING, "builtin/lighting/ambient_lighting", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_SKINNED, "builtin/lighting/ambient_lighting", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_IBL, "builtin/lighting/ambient_lighting_IBL", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED, "builtin/lighting/ambient_lighting_IBL", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_IBL_PBR, "builtin/lighting/ambient_lighting_IBL", "_PBR", BIT( USE_PBR ), false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED, "builtin/lighting/ambient_lighting_IBL", "_PBR_skinned", BIT( USE_GPU_SKINNING | USE_PBR ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTGRID_IBL, "builtin/lighting/ambient_lightgrid_IBL", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTGRID_IBL_SKINNED, "builtin/lighting/ambient_lightgrid_IBL", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR, "builtin/lighting/ambient_lightgrid_IBL", "_PBR", BIT( USE_PBR ), false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR_SKINNED, "builtin/lighting/ambient_lightgrid_IBL", "_PBR_skinned", BIT( USE_GPU_SKINNING | USE_PBR ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_SMALL_GEOMETRY_BUFFER, "builtin/gbuffer", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_SMALL_GEOMETRY_BUFFER_SKINNED, "builtin/gbuffer", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
// RB end
@ -154,8 +161,12 @@ void idRenderProgManager::Init()
{ BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL, "builtin/lighting/interactionSM", "_parallel_PBR", BIT( LIGHT_PARALLEL ) | BIT( USE_PBR ), false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL_SKINNED, "builtin/lighting/interactionSM", "_parallel_skinned_PBR", BIT( USE_GPU_SKINNING ) | BIT( LIGHT_PARALLEL ) | BIT( USE_PBR ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_OCTAHEDRON, "builtin/debug/octahedron", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_OCTAHEDRON_SKINNED, "builtin/debug/octahedron", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
// debug stuff
{ BUILTIN_DEBUG_LIGHTGRID, "builtin/debug/lightgrid", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_DEBUG_LIGHTGRID_SKINNED, "builtin/debug/lightgrid", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_DEBUG_OCTAHEDRON, "builtin/debug/octahedron", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_DEBUG_OCTAHEDRON_SKINNED, "builtin/debug/octahedron", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
// RB end
{ BUILTIN_ENVIRONMENT, "builtin/legacy/environment", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
@ -272,10 +283,16 @@ void idRenderProgManager::Init()
renderProgs[builtinShaders[BUILTIN_SHADOW_DEBUG_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_FOG_SKINNED]].usesJoints = true;
// RB begin
renderProgs[builtinShaders[BUILTIN_OCTAHEDRON_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_DEBUG_LIGHTGRID_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_DEBUG_OCTAHEDRON_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTGRID_IBL_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_SMALL_GEOMETRY_BUFFER_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_INTERACTION_SHADOW_MAPPING_SPOT_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_INTERACTION_SHADOW_MAPPING_POINT_SKINNED]].usesJoints = true;

View file

@ -147,7 +147,6 @@ enum renderParm_t
// RB begin
RENDERPARM_AMBIENT_COLOR,
// RENDERPARM_GLOBALVIEWORIGIN,
RENDERPARM_GLOBALLIGHTORIGIN,
RENDERPARM_JITTERTEXSCALE,
RENDERPARM_JITTERTEXOFFSET,
@ -311,6 +310,28 @@ public:
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED );
}
void BindShader_ImageBasedLightGrid()
{
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTGRID_IBL );
}
void BindShader_ImageBasedLightGridSkinned()
{
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTGRID_IBL_SKINNED );
}
void BindShader_ImageBasedLightGrid_PBR()
{
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR );
}
void BindShader_ImageBasedLightGridSkinned_PBR()
{
BindShader_Builtin( BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR_SKINNED );
}
void BindShader_SmallGeometryBuffer()
{
BindShader_Builtin( BUILTIN_SMALL_GEOMETRY_BUFFER );
@ -451,14 +472,24 @@ public:
BindShader_Builtin( BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL_SKINNED );
}
void BindShader_Octahedron()
void BindShader_DebugLightGrid()
{
BindShader_Builtin( BUILTIN_OCTAHEDRON );
BindShader_Builtin( BUILTIN_DEBUG_LIGHTGRID );
}
void BindShader_OctahedronSkinned()
void BindShader_DebugLightGridSkinned()
{
BindShader_Builtin( BUILTIN_OCTAHEDRON_SKINNED );
BindShader_Builtin( BUILTIN_DEBUG_LIGHTGRID_SKINNED );
}
void BindShader_DebugOctahedron()
{
BindShader_Builtin( BUILTIN_DEBUG_OCTAHEDRON );
}
void BindShader_DebugOctahedronSkinned()
{
BindShader_Builtin( BUILTIN_DEBUG_OCTAHEDRON_SKINNED );
}
// RB end
@ -731,10 +762,17 @@ private:
BUILTIN_VERTEX_COLOR,
BUILTIN_AMBIENT_LIGHTING,
BUILTIN_AMBIENT_LIGHTING_SKINNED,
BUILTIN_AMBIENT_LIGHTING_IBL,
BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED,
BUILTIN_AMBIENT_LIGHTING_IBL_PBR,
BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED,
BUILTIN_AMBIENT_LIGHTGRID_IBL,
BUILTIN_AMBIENT_LIGHTGRID_IBL_SKINNED,
BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR,
BUILTIN_AMBIENT_LIGHTGRID_IBL_PBR_SKINNED,
BUILTIN_SMALL_GEOMETRY_BUFFER,
BUILTIN_SMALL_GEOMETRY_BUFFER_SKINNED,
// RB end
@ -767,8 +805,11 @@ private:
BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL,
BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL_SKINNED,
BUILTIN_OCTAHEDRON,
BUILTIN_OCTAHEDRON_SKINNED,
BUILTIN_DEBUG_LIGHTGRID,
BUILTIN_DEBUG_LIGHTGRID_SKINNED,
BUILTIN_DEBUG_OCTAHEDRON,
BUILTIN_DEBUG_OCTAHEDRON_SKINNED,
// RB end
BUILTIN_ENVIRONMENT,
BUILTIN_ENVIRONMENT_SKINNED,

File diff suppressed because it is too large Load diff

View file

@ -199,7 +199,6 @@ struct glconfig_t
bool anisotropicFilterAvailable;
bool textureLODBiasAvailable;
bool seamlessCubeMapAvailable;
bool sRGBFramebufferAvailable;
bool vertexBufferObjectAvailable;
bool mapBufferRangeAvailable;
bool vertexArrayObjectAvailable;
@ -405,6 +404,9 @@ public:
// markers. Use WriteRender() instead.
virtual void TakeScreenshot( int width, int height, const char* fileName, int samples, struct renderView_s* ref, int exten ) = 0;
// RB
virtual byte* CaptureRenderToBuffer( int width, int height, renderView_t* ref ) = 0;
// the render output can be cropped down to a subset of the real screen, as
// for save-game reviews and split-screen multiplayer. Users of the renderer
// will not know the actual pixel size of the area they are rendering to

View file

@ -94,7 +94,6 @@ idCVar r_useNodeCommonChildren( "r_useNodeCommonChildren", "1", CVAR_RENDERER |
idCVar r_useShadowSurfaceScissor( "r_useShadowSurfaceScissor", "1", CVAR_RENDERER | CVAR_BOOL, "scissor shadows by the scissor rect of the interaction surfaces" );
idCVar r_useCachedDynamicModels( "r_useCachedDynamicModels", "1", CVAR_RENDERER | CVAR_BOOL, "cache snapshots of dynamic models" );
idCVar r_useSeamlessCubeMap( "r_useSeamlessCubeMap", "1", CVAR_RENDERER | CVAR_BOOL, "use ARB_seamless_cube_map if available" );
idCVar r_useSRGB( "r_useSRGB", "0", CVAR_RENDERER | CVAR_INTEGER | CVAR_ARCHIVE, "1 = both texture and framebuffer, 2 = framebuffer only, 3 = texture only" );
idCVar r_maxAnisotropicFiltering( "r_maxAnisotropicFiltering", "8", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "limit aniso filtering" );
idCVar r_useTrilinearFiltering( "r_useTrilinearFiltering", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Extra quality filtering" );
// RB: not used anymore
@ -271,9 +270,9 @@ idCVar r_shadowMapSunDepthBiasScale( "r_shadowMapSunDepthBiasScale", "0.999991",
// RB: HDR parameters
#if defined( USE_VULKAN )
idCVar r_useHDR( "r_useHDR", "0", CVAR_RENDERER | CVAR_ROM | CVAR_STATIC | CVAR_BOOL, "use high dynamic range rendering" );
idCVar r_useHDR( "r_useHDR", "0", CVAR_RENDERER | CVAR_ROM | CVAR_STATIC | CVAR_BOOL, "Can't be changed, is broken on Vulkan backend" );
#else
idCVar r_useHDR( "r_useHDR", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "use high dynamic range rendering" );
idCVar r_useHDR( "r_useHDR", "1", CVAR_RENDERER | CVAR_ROM | CVAR_STATIC | CVAR_BOOL, "Can't be changed: Use high dynamic range rendering" );
#endif
idCVar r_hdrAutoExposure( "r_hdrAutoExposure", "0", CVAR_RENDERER | CVAR_BOOL, "EXPENSIVE: enables adapative HDR tone mapping otherwise the exposure is derived by r_exposure" );
@ -313,6 +312,9 @@ idCVar r_useHierarchicalDepthBuffer( "r_useHierarchicalDepthBuffer", "1", CVAR_R
idCVar r_usePBR( "r_usePBR", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "use PBR and Image Based Lighting instead of old Quake 4 style ambient lighting" );
idCVar r_pbrDebug( "r_pbrDebug", "0", CVAR_RENDERER | CVAR_INTEGER, "show which materials have PBR support (green = PBR, red = oldschool D3)" );
idCVar r_showViewEnvprobes( "r_showViewEnvprobes", "0", CVAR_RENDERER | CVAR_INTEGER, "1 = displays the bounding boxes of all view environment probes, 2 = show irradiance" );
idCVar r_showLightGrid( "r_showLightGrid", "0", CVAR_RENDERER | CVAR_INTEGER, "show Quake 3 style light grid points" );
idCVar r_useLightGrid( "r_useLightGrid", "1", CVAR_RENDERER | CVAR_BOOL, "" );
idCVar r_exposure( "r_exposure", "0.5", CVAR_ARCHIVE | CVAR_RENDERER | CVAR_FLOAT, "HDR exposure or LDR brightness [0.0 .. 1.0]", 0.0f, 1.0f );
// RB end
@ -751,7 +753,7 @@ void R_ReadTiledPixels( int width, int height, byte* buffer, renderView_t* ref =
if( ref && ref->rdflags & RDF_IRRADIANCE )
{
// * 2 = sizeof( half float )
temp = ( byte* )R_StaticAlloc( RADIANCE_CUBEMAP_SIZE * RADIANCE_CUBEMAP_SIZE * 3 * 2 );
//temp = ( byte* )R_StaticAlloc( RADIANCE_CUBEMAP_SIZE * RADIANCE_CUBEMAP_SIZE * 3 * 2 );
}
else
{
@ -778,8 +780,12 @@ void R_ReadTiledPixels( int width, int height, byte* buffer, renderView_t* ref =
int originalNativeWidth = glConfig.nativeScreenWidth;
int originalNativeHeight = glConfig.nativeScreenHeight;
glConfig.nativeScreenWidth = sysWidth;
glConfig.nativeScreenHeight = sysHeight;
//if( !ref || ( ref && !( ref->rdflags & RDF_IRRADIANCE ) ) )
{
glConfig.nativeScreenWidth = sysWidth;
glConfig.nativeScreenHeight = sysHeight;
}
#endif
// disable scissor, so we don't need to adjust all those rects
@ -878,8 +884,11 @@ void R_ReadTiledPixels( int width, int height, byte* buffer, renderView_t* ref =
// discard anything currently on the list
tr.SwapCommandBuffers( NULL, NULL, NULL, NULL, NULL, NULL );
glConfig.nativeScreenWidth = originalNativeWidth;
glConfig.nativeScreenHeight = originalNativeHeight;
if( !ref || ( ref && !( ref->rdflags & RDF_IRRADIANCE ) ) )
{
glConfig.nativeScreenWidth = originalNativeWidth;
glConfig.nativeScreenHeight = originalNativeHeight;
}
#endif
r_useScissor.SetBool( true );
@ -1024,6 +1033,33 @@ void idRenderSystemLocal::TakeScreenshot( int width, int height, const char* fil
takingScreenshot = false;
}
// RB begin
byte* idRenderSystemLocal::CaptureRenderToBuffer( int width, int height, renderView_t* ref )
{
byte* buffer;
takingScreenshot = true;
int pix = width * height;
const int bufferSize = pix * 3 + 18;
// HDR only for now
//if( exten == EXR )
{
buffer = ( byte* )R_StaticAlloc( pix * 3 * 2 );
}
//else if( exten == PNG )
//{
// buffer = ( byte* )R_StaticAlloc( pix * 3 );
//}
R_ReadTiledPixels( width, height, buffer, ref );
takingScreenshot = false;
return buffer;
}
/*
==================
R_ScreenshotFilename
@ -1283,11 +1319,6 @@ void R_EnvShot_f( const idCmdArgs& args )
//============================================================================
static idMat3 cubeAxis[6];
void R_TransformCubemap( const char* orgDirection[6], const char* orgDir, const char* destDirection[6], const char* destDir, const char* baseName )
{
idStr fullname;
@ -1707,6 +1738,7 @@ void idRenderSystemLocal::Clear()
guiRecursionLevel = 0;
guiModel = NULL;
memset( gammaTable, 0, sizeof( gammaTable ) );
memset( &cubeAxis, 0, sizeof( cubeAxis ) ); // RB
takingScreenshot = false;
if( unitSquareTriangles != NULL )
@ -1737,7 +1769,8 @@ void idRenderSystemLocal::Clear()
// RB
envprobeJobList = NULL;
irradianceJobs.Clear();
envprobeJobs.Clear();
lightGridJobs.Clear();
}
/*
@ -1921,7 +1954,7 @@ static srfTriangles_t* R_MakeZeroOneSphereTris()
verts[ numVerts ].SetTexCoord( s * S, r * R );
verts[ numVerts ].xyz = idVec3( x, y, z ) * radius;
verts[ numVerts ].SetNormal( -x, -y, -z );
verts[ numVerts ].SetNormal( x, y, z );
verts[ numVerts ].SetColor( 0xffffffff );
numVerts++;
@ -2053,6 +2086,38 @@ void idRenderSystemLocal::Init()
identitySpace.modelMatrix[1 * 4 + 1] = 1.0f;
identitySpace.modelMatrix[2 * 4 + 2] = 1.0f;
// set cubemap axis for cubemap sampling tools
// +X
cubeAxis[0][0][0] = 1;
cubeAxis[0][1][2] = 1;
cubeAxis[0][2][1] = 1;
// -X
cubeAxis[1][0][0] = -1;
cubeAxis[1][1][2] = -1;
cubeAxis[1][2][1] = 1;
// +Y
cubeAxis[2][0][1] = 1;
cubeAxis[2][1][0] = -1;
cubeAxis[2][2][2] = -1;
// -Y
cubeAxis[3][0][1] = -1;
cubeAxis[3][1][0] = -1;
cubeAxis[3][2][2] = 1;
// +Z
cubeAxis[4][0][2] = 1;
cubeAxis[4][1][0] = -1;
cubeAxis[4][2][1] = 1;
// -Z
cubeAxis[5][0][2] = -1;
cubeAxis[5][1][0] = 1;
cubeAxis[5][2][1] = 1;
// make sure the tr.unitSquareTriangles data is current in the vertex / index cache
if( unitSquareTriangles == NULL )
{

View file

@ -769,7 +769,7 @@ void R_DeriveEnvprobeData( RenderEnvprobeLocal* probe )
// TODO get preconvolved cubemaps
fullname.Format( "env/%s/envprobe%i_amb", basename.c_str(), probeIndex );
probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_LINEAR, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );
fullname.Format( "env/%s/envprobe%i_spec", basename.c_str(), probeIndex );
probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_R11G11B10F, CF_2D_PACKED_MIPCHAIN );

View file

@ -171,7 +171,6 @@ void idRenderWorldLocal::AddAreaViewEnvprobes( int areaNum, const portalStack_t*
R_SampleCubeMapHDR
==================
*/
static idMat3 cubeAxis[6];
static const char* envDirection[6] = { "_px", "_nx", "_py", "_ny", "_pz", "_nz" };
void R_SampleCubeMapHDR( const idVec3& dir, int size, byte* buffers[6], float result[3], float& u, float& v )
@ -208,8 +207,8 @@ void R_SampleCubeMapHDR( const idVec3& dir, int size, byte* buffers[6], float re
axis = 5;
}
float fx = ( dir * cubeAxis[axis][1] ) / ( dir * cubeAxis[axis][0] );
float fy = ( dir * cubeAxis[axis][2] ) / ( dir * cubeAxis[axis][0] );
float fx = ( dir * tr.cubeAxis[axis][1] ) / ( dir * tr.cubeAxis[axis][0] );
float fy = ( dir * tr.cubeAxis[axis][2] ) / ( dir * tr.cubeAxis[axis][0] );
fx = -fx;
fy = -fy;
@ -252,56 +251,74 @@ void R_SampleCubeMapHDR( const idVec3& dir, int size, byte* buffers[6], float re
r11g11b10f_to_float3( tmp.i, result );
}
class CommandlineProgressBar
void R_SampleCubeMapHDR16F( const idVec3& dir, int size, halfFloat_t* buffers[6], float result[3], float& u, float& v )
{
private:
size_t tics = 0;
size_t nextTicCount = 0;
int count = 0;
int expectedCount = 0;
float adir[3];
int axis, x, y;
public:
CommandlineProgressBar( int _expectedCount )
adir[0] = fabs( dir[0] );
adir[1] = fabs( dir[1] );
adir[2] = fabs( dir[2] );
if( dir[0] >= adir[1] && dir[0] >= adir[2] )
{
expectedCount = _expectedCount;
axis = 0;
}
else if( -dir[0] >= adir[1] && -dir[0] >= adir[2] )
{
axis = 1;
}
else if( dir[1] >= adir[0] && dir[1] >= adir[2] )
{
axis = 2;
}
else if( -dir[1] >= adir[0] && -dir[1] >= adir[2] )
{
axis = 3;
}
else if( dir[2] >= adir[1] && dir[2] >= adir[2] )
{
axis = 4;
}
else
{
axis = 5;
}
void Start()
{
common->Printf( "0%% 10 20 30 40 50 60 70 80 90 100%%\n" );
common->Printf( "|----|----|----|----|----|----|----|----|----|----|\n" );
float fx = ( dir * tr.cubeAxis[axis][1] ) / ( dir * tr.cubeAxis[axis][0] );
float fy = ( dir * tr.cubeAxis[axis][2] ) / ( dir * tr.cubeAxis[axis][0] );
common->UpdateScreen( false );
fx = -fx;
fy = -fy;
x = size * 0.5 * ( fx + 1 );
y = size * 0.5 * ( fy + 1 );
if( x < 0 )
{
x = 0;
}
else if( x >= size )
{
x = size - 1;
}
if( y < 0 )
{
y = 0;
}
else if( y >= size )
{
y = size - 1;
}
void Increment()
{
if( ( count + 1 ) >= nextTicCount )
{
size_t ticsNeeded = ( size_t )( ( ( double )( count + 1 ) / expectedCount ) * 50.0 );
u = x;
v = y;
do
{
common->Printf( "*" );
}
while( ++tics < ticsNeeded );
// unpack RGB16F to 3 floats
result[0] = F16toF32( buffers[axis][( y * size + x ) * 3 + 0] );
result[1] = F16toF32( buffers[axis][( y * size + x ) * 3 + 1] );
result[2] = F16toF32( buffers[axis][( y * size + x ) * 3 + 2] );
}
nextTicCount = ( size_t )( ( tics / 50.0 ) * expectedCount );
if( count == ( expectedCount - 1 ) )
{
if( tics < 51 )
{
common->Printf( "*" );
}
common->Printf( "\n" );
}
common->UpdateScreen( false );
}
count++;
}
};
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
@ -432,14 +449,41 @@ idVec2 IntegrateBRDF( float NdotV, float roughness, int sampleCount )
// Compute normalized oct coord, mapping top left of top left pixel to (-1,-1)
idVec2 NormalizedOctCoord( int x, int y, const int probeSideLength )
idVec2 NormalizedOctCoord( int x, int y, const int probeWithBorderSide )
{
const int margin = 0;
#if 0
// 1 pixel border
const int margin = 1;
int probeWithBorderSide = probeSideLength + margin;
int probeSideLength = Max( 2, probeWithBorderSide - ( margin * 2 ) );
idVec2 octFragCoord;
octFragCoord.x = idMath::ClampInt( 0, probeSideLength - 1, x - margin );
octFragCoord.y = idMath::ClampInt( 0, probeSideLength - 1, y - margin );
return ( idVec2( octFragCoord ) ) * ( 2.0f / float( probeSideLength ) ) - idVec2( 1.0f, 1.0f );
#else
const int margin = 2;
// RB: FIXME - margin * 2 is wrong but looks better
// figure out why
int probeSideLength = Max( 2, probeWithBorderSide - ( margin * 2 ) );
idVec2 octFragCoord = idVec2( ( x - margin ) % probeWithBorderSide, ( y - margin ) % probeWithBorderSide );
// Add back the half pixel to get pixel center normalized coordinates
return ( idVec2( octFragCoord ) + idVec2( 0.5f, 0.5f ) ) * ( 2.0f / float( probeSideLength ) ) - idVec2( 1.0f, 1.0f );
#endif
}
static inline idVec2 NormalizedOctCoordNoBorder( int x, int y, const int probeWithBorderSide )
{
int probeSideLength = probeWithBorderSide;
idVec2 octFragCoord = idVec2( x % probeWithBorderSide, y % probeWithBorderSide );
// Add back the half pixel to get pixel center normalized coordinates
return ( idVec2( octFragCoord ) + idVec2( 0.5f, 0.5f ) ) * ( 2.0f / float( probeSideLength ) ) - idVec2( 1.0f, 1.0f );
}
@ -562,10 +606,10 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms )
progressBar.Start();
}
// build L4 Spherical Harmonics from source image
SphericalHarmonicsT<idVec3, 4> shRadiance;
// build L3 Spherical Harmonics from source image
SphericalHarmonicsT<idVec3, 3> shRadiance;
for( int i = 0; i < shSize( 4 ); i++ )
for( int i = 0; i < shSize( 3 ); i++ )
{
shRadiance[i].Zero();
}
@ -582,7 +626,7 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms )
{
for( int y = dstRect.y; y < ( dstRect.y + dstRect.w ); y++ )
{
idVec2 octCoord = NormalizedOctCoord( x, y, dstRect.z );
idVec2 octCoord = NormalizedOctCoordNoBorder( x, y, dstRect.z );
// convert UV coord to 3D direction
idVec3 dir;
@ -647,7 +691,7 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms )
float texelArea = CubemapTexelSolidAngle( uu, vv, invDstSize );
const SphericalHarmonicsT<float, 4>& sh = shEvaluate<4>( dir );
const SphericalHarmonicsT<float, 3>& sh = shEvaluate<3>( dir );
bool shValid = true;
for( int i = 0; i < 25; i++ )
@ -694,11 +738,11 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms )
if( mip > 0 )
{
// move back to [0, 1] coords
octCoord = NormalizedOctCoord( x - dstRect.x, y - dstRect.y, dstRect.z );
octCoord = NormalizedOctCoordNoBorder( x - dstRect.x, y - dstRect.y, dstRect.z );
}
else
{
octCoord = NormalizedOctCoord( x, y, dstRect.z );
octCoord = NormalizedOctCoordNoBorder( x, y, dstRect.z );
}
// convert UV coord to 3D direction
@ -709,10 +753,10 @@ void CalculateIrradianceJob( calcEnvprobeParms_t* parms )
idVec3 outColor( 0, 0, 0 );
#if 1
// generate ambient colors by evaluating the L4 Spherical Harmonics
SphericalHarmonicsT<float, 4> shDirection = shEvaluate<4>( dir );
// generate ambient colors by evaluating the L3 Spherical Harmonics
SphericalHarmonicsT<float, 3> shDirection = shEvaluate<3>( dir );
idVec3 sampleIrradianceSh = shEvaluateDiffuse<idVec3, 4>( shRadiance, dir ) / idMath::PI;
idVec3 sampleIrradianceSh = shEvaluateDiffuse<idVec3, 3>( shRadiance, dir ) / idMath::PI;
outColor[0] = Max( 0.0f, sampleIrradianceSh.x );
outColor[1] = Max( 0.0f, sampleIrradianceSh.y );
@ -805,11 +849,11 @@ void CalculateRadianceJob( calcEnvprobeParms_t* parms )
if( mip > 0 )
{
// move back to [0, 1] coords
octCoord = NormalizedOctCoord( x - dstRect.x, y - dstRect.y, dstRect.z );
octCoord = NormalizedOctCoordNoBorder( x - dstRect.x, y - dstRect.y, dstRect.z );
}
else
{
octCoord = NormalizedOctCoord( x, y, dstRect.z );
octCoord = NormalizedOctCoordNoBorder( x, y, dstRect.z );
}
// convert UV coord to 3D direction
@ -921,7 +965,7 @@ void R_MakeAmbientMap( const char* baseName, const char* suffix, int outSize, bo
jobParms->outHeight = outSize;
jobParms->outBuffer = ( halfFloat_t* )R_StaticAlloc( idMath::Ceil( outSize * outSize * 3 * sizeof( halfFloat_t ) * 1.5f ), TAG_IMAGE );
tr.irradianceJobs.Append( jobParms );
tr.envprobeJobs.Append( jobParms );
if( useThreads )
{
@ -957,7 +1001,7 @@ void R_MakeAmbientMap( const char* baseName, const char* suffix, int outSize, bo
}
}
CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL )
CONSOLE_COMMAND( bakeEnvironmentProbes, "Bake environment probes", NULL )
{
idStr fullname;
idStr baseName;
@ -990,38 +1034,6 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL
const viewDef_t primary = *tr.primaryView;
memset( &cubeAxis, 0, sizeof( cubeAxis ) );
// +X
cubeAxis[0][0][0] = 1;
cubeAxis[0][1][2] = 1;
cubeAxis[0][2][1] = 1;
// -X
cubeAxis[1][0][0] = -1;
cubeAxis[1][1][2] = -1;
cubeAxis[1][2][1] = 1;
// +Y
cubeAxis[2][0][1] = 1;
cubeAxis[2][1][0] = -1;
cubeAxis[2][2][2] = -1;
// -Y
cubeAxis[3][0][1] = -1;
cubeAxis[3][1][0] = -1;
cubeAxis[3][2][2] = 1;
// +Z
cubeAxis[4][0][2] = 1;
cubeAxis[4][1][0] = -1;
cubeAxis[4][2][1] = 1;
// -Z
cubeAxis[5][0][2] = -1;
cubeAxis[5][1][0] = 1;
cubeAxis[5][2][1] = 1;
//--------------------------------------------
// CAPTURE SCENE LIGHTING TO CUBEMAPS
//--------------------------------------------
@ -1042,7 +1054,7 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL
ref.fov_x = ref.fov_y = 90;
ref.vieworg = def->parms.origin;
ref.viewaxis = cubeAxis[j];
ref.viewaxis = tr.cubeAxis[j];
extension = envDirection[ j ];
fullname.Format( "env/%s/envprobe%i%s", baseName.c_str(), i, extension );
@ -1082,9 +1094,9 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL
tr.envprobeJobList->Wait();
}
for( int j = 0; j < tr.irradianceJobs.Num(); j++ )
for( int j = 0; j < tr.envprobeJobs.Num(); j++ )
{
calcEnvprobeParms_t* job = tr.irradianceJobs[ j ];
calcEnvprobeParms_t* job = tr.envprobeJobs[ j ];
R_WriteEXR( job->filename, ( byte* )job->outBuffer, 3, job->outWidth, job->outHeight, "fs_basepath" );
@ -1103,11 +1115,26 @@ CONSOLE_COMMAND( generateEnvironmentProbes, "Generate environment probes", NULL
delete job;
}
tr.irradianceJobs.Clear();
tr.envprobeJobs.Clear();
int end = Sys_Milliseconds();
common->Printf( "convolved probes in %5.1f seconds\n\n", ( end - start ) * 0.001f );
//--------------------------------------------
// LOAD CONVOLVED OCTAHEDRONS INTO THE GPU
//--------------------------------------------
for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ )
{
RenderEnvprobeLocal* def = tr.primaryWorld->envprobeDefs[i];
if( def == NULL )
{
continue;
}
def->irradianceImage->Reload( false );
def->radianceImage->Reload( false );
}
}
/*

File diff suppressed because it is too large Load diff

View file

@ -858,6 +858,7 @@ bool idRenderWorldLocal::InitFromMap( const char* name )
TouchWorldModels();
AddWorldModelEntities();
ClearPortalStates();
SetupLightGrid();
return true;
}
common->Printf( "idRenderWorldLocal::InitFromMap: timestamp has changed, reloading.\n" );
@ -1047,6 +1048,7 @@ bool idRenderWorldLocal::InitFromMap( const char* name )
AddWorldModelEntities();
ClearPortalStates();
SetupLightGrid();
// done!
return true;

View file

@ -3,7 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2014-2016 Robert Beckebans
Copyright (C) 2014-2021 Robert Beckebans
Copyright (C) 2014-2016 Kot in Action Creative Artel
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -59,6 +59,57 @@ typedef struct doublePortal_s
struct doublePortal_s* nextFoggedPortal;
} doublePortal_t;
// RB: added Quake 3 style light grid
// however this 2021 version features Spherical Harmonics instead of ambient + directed color
struct lightGridPoint_t
{
idVec3 origin; // not saved to .proc
byte valid; // is not in the void
SphericalHarmonicsT<idVec3, 4> shRadiance; // L4 Spherical Harmonics
};
class LightGrid
{
public:
idVec3 lightGridOrigin;
idVec3 lightGridSize;
int lightGridBounds[3];
idList<lightGridPoint_t> lightGridPoints;
int area;
idImage* irradianceImage;
int imageSingleProbeSize; // including border
int imageBorderSize;
LightGrid();
// setup light grid for given world bounds
void SetupLightGrid( const idBounds& bounds, const char* baseName, const idRenderWorld* world, int _area, int limit );
void GetBaseGridCoord( const idVec3& origin, int gridCoord[3] );
int GridCoordToProbeIndex( int gridCoord[3] );
void ProbeIndexToGridCoord( const int probeIndex, int gridCoord[3] );
idVec3 GetGridCoordDebugColor( int gridCoord[3] );
idVec3 GetProbeIndexDebugColor( const int probeIndex );
int CountValidGridPoints() const;
idImage* GetIrradianceImage() const
{
return irradianceImage;
}
// fetch grid lighting on a per object basis
void SetupEntityGridLighting( idRenderEntityLocal* def );
private:
void CalculateLightGridPointPositions( const idRenderWorld* world, int area );
};
// RB end
typedef struct portalArea_s
{
@ -68,6 +119,8 @@ typedef struct portalArea_s
idBounds globalBounds; // RB: AABB of the BSP area used for light grid density
LightGrid lightGrid;
int viewCount; // set by R_FindViewLightsAndEntities
portal_t* portals; // never changes after load
areaReference_t entityRefs; // head/tail of doubly linked list, may change
@ -337,6 +390,24 @@ public:
//-------------------------------
// tr_light.c
void CreateLightDefInteractions( idRenderLightLocal* const ldef, const int renderViewID );
// RB begin
//--------------------------
// RenderWorld_lightgrid.cpp
//private:
void SetupLightGrid();
void WriteLightGridsToFile( const char* filename );
void WriteLightGrid( idFile* fp, const LightGrid& lightGrid );
bool LoadLightGridFile( const char* name );
void LoadLightGridImages();
void ParseLightGridPoints( idLexer* src, idFile* fileOut );
void ReadBinaryLightGridPoints( idFile* file );
// RB end
};
// if an entity / light combination has been evaluated and found to not genrate any surfaces or shadows,

View file

@ -373,6 +373,9 @@ void R_AddSingleModel( viewEntity_t* vEntity )
vEntity->staticShadowVolumes = NULL;
vEntity->dynamicShadowVolumes = NULL;
// RB
vEntity->useLightGrid = false;
// globals we really should pass in...
const viewDef_t* viewDef = tr.viewDef;
@ -549,6 +552,32 @@ void R_AddSingleModel( viewEntity_t* vEntity )
}
}
// RB: use first valid lightgrid
for( areaReference_t* ref = entityDef->entityRefs; ref != NULL; ref = ref->ownerNext )
{
idImage* lightGridImage = ref->area->lightGrid.GetIrradianceImage();
if( ref->area->lightGrid.lightGridPoints.Num() && lightGridImage && !lightGridImage->IsDefaulted() )
{
vEntity->useLightGrid = true;
vEntity->lightGridAtlasImage = lightGridImage;
vEntity->lightGridAtlasSingleProbeSize = ref->area->lightGrid.imageSingleProbeSize;
vEntity->lightGridAtlasBorderSize = ref->area->lightGrid.imageBorderSize;
for( int i = 0; i < 3; i++ )
{
vEntity->lightGridOrigin[i] = ref->area->lightGrid.lightGridOrigin[i];
vEntity->lightGridSize[i] = ref->area->lightGrid.lightGridSize[i];
vEntity->lightGridBounds[i] = ref->area->lightGrid.lightGridBounds[i];
}
break;
}
}
// RB end
//---------------------------
// copy matrix related stuff for back-end use
// and setup a render matrix for faster culling

View file

@ -454,6 +454,121 @@ static void R_SetupSplitFrustums( viewDef_t* viewDef )
}
}
}
class idSort_CompareEnvprobe : public idSort_Quick< RenderEnvprobeLocal*, idSort_CompareEnvprobe >
{
idVec3 viewOrigin;
public:
idSort_CompareEnvprobe( const idVec3& origin )
{
viewOrigin = origin;
}
int Compare( RenderEnvprobeLocal* const& a, RenderEnvprobeLocal* const& b ) const
{
float adist = ( viewOrigin - a->parms.origin ).LengthSqr();
float bdist = ( viewOrigin - b->parms.origin ).LengthSqr();
if( adist < bdist )
{
return -1;
}
if( adist > bdist )
{
return 1;
}
return 0;
}
};
static void R_FindClosestEnvironmentProbes()
{
// set safe defaults
tr.viewDef->globalProbeBounds.Clear();
tr.viewDef->irradianceImage = globalImages->defaultUACIrradianceCube;
tr.viewDef->radianceImageBlends.Set( 1, 0, 0, 0 );
for( int i = 0; i < 3; i++ )
{
tr.viewDef->radianceImages[i] = globalImages->defaultUACRadianceCube;
}
// early out
if( tr.viewDef->areaNum == -1 || tr.viewDef->isSubview )
{
return;
}
idList<RenderEnvprobeLocal*, TAG_RENDER_ENVPROBE> viewEnvprobes;
for( int i = 0; i < tr.primaryWorld->envprobeDefs.Num(); i++ )
{
RenderEnvprobeLocal* vProbe = tr.primaryWorld->envprobeDefs[i];
if( vProbe )
{
viewEnvprobes.AddUnique( vProbe );
}
}
if( viewEnvprobes.Num() == 0 )
{
return;
}
idVec3 testOrigin = tr.viewDef->renderView.vieworg;
// sort by distance
// RB: each Doom 3 level has ~50 - 150 probes so this should be ok for each frame
viewEnvprobes.SortWithTemplate( idSort_CompareEnvprobe( testOrigin ) );
RenderEnvprobeLocal* nearest = viewEnvprobes[0];
tr.viewDef->globalProbeBounds = nearest->globalProbeBounds;
tr.viewDef->irradianceImage = nearest->irradianceImage;
// form a triangle of the 3 closest probes
idVec3 verts[3];
for( int i = 0; i < 3; i++ )
{
verts[i] = viewEnvprobes[0]->parms.origin;
}
for( int i = 0; i < viewEnvprobes.Num() && i < 3; i++ )
{
RenderEnvprobeLocal* vProbe = viewEnvprobes[i];
verts[i] = vProbe->parms.origin;
}
idVec3 closest = R_ClosestPointPointTriangle( testOrigin, verts[0], verts[1], verts[2] );
idVec3 bary;
// find the barycentric coordinates
float denom = idWinding::TriangleArea( verts[0], verts[1], verts[2] );
if( denom == 0 )
{
// all points at same location
bary.Set( 1, 0, 0 );
}
else
{
float a, b, c;
a = idWinding::TriangleArea( closest, verts[1], verts[2] ) / denom;
b = idWinding::TriangleArea( closest, verts[2], verts[0] ) / denom;
c = idWinding::TriangleArea( closest, verts[0], verts[1] ) / denom;
bary.Set( a, b, c );
}
tr.viewDef->radianceImageBlends.Set( bary.x, bary.y, bary.z, 0.0f );
for( int i = 0; i < viewEnvprobes.Num() && i < 3; i++ )
{
tr.viewDef->radianceImages[i] = viewEnvprobes[i]->radianceImage;
}
}
// RB end
/*
@ -539,32 +654,8 @@ void R_RenderView( viewDef_t* parms )
}
}
// RB: find closest environment probe
if( tr.viewDef->areaNum != -1 && !tr.viewDef->isSubview )
{
float bestDist = idMath::INFINITY;
tr.viewDef->globalProbeBounds.Clear();
tr.viewDef->irradianceImage = globalImages->defaultUACIrradianceCube;
tr.viewDef->radianceImage = globalImages->defaultUACRadianceCube;
for( viewEnvprobe_t* vProbe = tr.viewDef->viewEnvprobes; vProbe != NULL; vProbe = vProbe->next )
{
float dist = ( tr.viewDef->renderView.vieworg - vProbe->globalOrigin ).Length();
if( ( dist < bestDist ) )
{
if( vProbe->irradianceImage->IsLoaded() && !vProbe->irradianceImage->IsDefaulted() )
{
tr.viewDef->globalProbeBounds = vProbe->globalProbeBounds;
tr.viewDef->irradianceImage = vProbe->irradianceImage;
tr.viewDef->radianceImage = vProbe->radianceImage;
bestDist = dist;
}
}
}
}
// RB: find closest environment probes so we can interpolate between them in the ambient shaders
R_FindClosestEnvironmentProbes();
// write everything needed to the demo file
if( common->WriteDemo() )

View file

@ -2367,4 +2367,82 @@ static void SetUpMikkTSpaceContext( SMikkTSpaceContext* context )
context->m_pInterface = &mikkTSpaceInterface.mkInterface;
}
// SP end
// SP end
// RB: Determines the closest point between a point and a triangle
idVec3 R_ClosestPointPointTriangle( const idVec3& point, const idVec3& vertex1, const idVec3& vertex2, const idVec3& vertex3 )
{
idVec3 result;
// Source: Real-Time Collision Detection by Christer Ericson
// Reference: Page 136
// check if P in vertex region outside A
idVec3 ab = vertex2 - vertex1;
idVec3 ac = vertex3 - vertex1;
idVec3 ap = point - vertex1;
float d1 = ( ab * ap );
float d2 = ( ac * ap );
if( d1 <= 0.0f && d2 <= 0.0f )
{
result = vertex1; //Barycentric coordinates (1,0,0)
return result;
}
// Check if P in vertex region outside B
idVec3 bp = point - vertex2;
float d3 = ( ab * bp );
float d4 = ( ac * bp );
if( d3 >= 0.0f && d4 <= d3 )
{
result = vertex2; // barycentric coordinates (0,1,0)
return result;
}
// Check if P in edge region of AB, if so return projection of P onto AB
float vc = d1 * d4 - d3 * d2;
if( vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f )
{
float v = d1 / ( d1 - d3 );
result = vertex1 + v * ab; //Barycentric coordinates (1-v,v,0)
return result;
}
// Check if P in vertex region outside C
idVec3 cp = point - vertex3;
float d5 = ( ab * cp );
float d6 = ( ac * cp );
if( d6 >= 0.0f && d5 <= d6 )
{
result = vertex3; //Barycentric coordinates (0,0,1)
return result;
}
// Check if P in edge region of AC, if so return projection of P onto AC
float vb = d5 * d2 - d1 * d6;
if( vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f )
{
float w = d2 / ( d2 - d6 );
result = vertex1 + w * ac; //Barycentric coordinates (1-w,0,w)
return result;
}
// Check if P in edge region of BC, if so return projection of P onto BC
float va = d3 * d6 - d5 * d4;
if( va <= 0.0f && ( d4 - d3 ) >= 0.0f && ( d5 - d6 ) >= 0.0f )
{
float w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );
result = vertex2 + w * ( vertex3 - vertex2 ); //Barycentric coordinates (0,1-w,w)
return result;
}
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
float denom = 1.0f / ( va + vb + vc );
float v2 = vb * denom;
float w2 = vc * denom;
result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w
return result;
}

View file

@ -3,7 +3,7 @@
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2013-2015 Robert Beckebans
Copyright (C) 2013-2021 Robert Beckebans
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).