public Mesh(Scene _Owner, idTech5Map.Map.Entity _Entity) : base(_Owner, _Entity) { // BUild primitives and local space BBox m_Primitives = new Primitive[_Entity.m_Model.m_Surfaces.Length]; m_BBoxMin_Local = float.MaxValue * float3.One; m_BBoxMax_Local = -float.MaxValue * float3.One; int PrimitiveIndex = 0; foreach (idTech5Map.Model.Surface S in _Entity.m_Model.m_Surfaces) { m_Primitives[PrimitiveIndex] = new Primitive(this, S); m_BBoxMin_Local.Min(m_Primitives[PrimitiveIndex].m_BBoxMin); m_BBoxMax_Local.Max(m_Primitives[PrimitiveIndex].m_BBoxMax); PrimitiveIndex++; } // Convert BBox to world space m_BBoxMin_World = float.MaxValue * float3.One; m_BBoxMax_World = -float.MaxValue * float3.One; for (int CornerIndex = 0; CornerIndex < 8; CornerIndex++) { float X = (CornerIndex >> 0) & 1; float Y = (CornerIndex >> 0) & 1; float Z = (CornerIndex >> 0) & 1; float3 D = m_BBoxMax_Local - m_BBoxMin_Local; float3 lsCorner = m_BBoxMin_Local + new float3(X * D.x, Y * D.y, Z * D.z); float3 wsCorner = (float3)(new float4(lsCorner, 1.0f) * m_Local2Parent); m_BBoxMin_World.Min(wsCorner); m_BBoxMax_World.Max(wsCorner); } }
// uint packR8G8B8A8( float4 value ) { // value = saturate( value ); // return ( ( ( uint( value.x * 255.0 ) ) << 24 ) | ( ( uint( value.y * 255.0 ) ) << 16 ) | ( ( uint( value.z * 255.0 ) ) << 8 ) | ( uint( value.w * 255.0 ) ) ); // } // // float4 unpackR8G8B8A8( uint value ) { // return float4( ( value >> 24 ) & 0xFF, ( value >> 16 ) & 0xFF, ( value >> 8 ) & 0xFF, value & 0xFF ) / 255.0; // } // // // RGBE (ward 1984) // uint packRGBE( float3 value ) { // const float sharedExp = ceil( ApproxLog2( max( max( value.r, value.g ), value.b ) ) ); // return packR8G8B8A8( saturate( float4( value / ApproxExp2( sharedExp ), ( sharedExp + 128.0f ) / 255.0f ) ) ); // } // // float3 unpackRGBE( uint value ) { // const float4 rgbe = unpackR8G8B8A8( value ); // return rgbe.rgb * ApproxExp2( rgbe.a * 255.0f - 128.0f ); // } uint EncodeRGBE(float3 _RGB) { float3 Sign = new float3(Math.Sign(_RGB.x), Math.Sign(_RGB.y), Math.Sign(_RGB.z)); _RGB *= Sign; float maxRGB = _RGB.Max(); int sharedExp = (int)Math.Ceiling(Math.Log(maxRGB) / Math.Log(2)); sharedExp = Math.Max(0, Math.Min(31, sharedExp + EXPONENT_BIAS)) - EXPONENT_BIAS; // float pow2 = (float) Math.Pow( 2.0f, sharedExp ); // float3 reducedRGB = _RGB / pow2; float invPow2 = (float)Math.Pow(2.0f, -sharedExp); float3 reducedRGB = _RGB * invPow2; reducedRGB.x = Math.Max(0.0f, Math.Min(1.0f, reducedRGB.x)); reducedRGB.y = Math.Max(0.0f, Math.Min(1.0f, reducedRGB.y)); reducedRGB.z = Math.Max(0.0f, Math.Min(1.0f, reducedRGB.z)); byte R = (byte)(255.0f * reducedRGB.x); byte G = (byte)(255.0f * reducedRGB.y); byte B = (byte)(255.0f * reducedRGB.z); byte exp = (byte)(EXPONENT_BIAS + sharedExp); exp |= (byte)((Sign.x < 0.0f ? 128 : 0) | (Sign.y < 0.0f ? 64 : 0) | (Sign.z < 0.0f ? 32 : 0)); uint RGBE = (uint)((exp << 24) | (R << 16) | (G << 8) | B); return(RGBE); }
public void Combine(AABB a, AABB b) { m_Min = a.m_Min; m_Min.Min(b.m_Min); m_Max = a.m_Max; m_Max.Max(b.m_Max); }
// Evaluates the SH coefficients in the requested direction // Analytic method from http://www1.cs.columbia.edu/~ravir/papers/envmap/envmap.pdf eq. 3 // void EvaluateSHRadiance(ref float3 _direction, ref float3 _radiance) { const float f0 = 0.28209479177387814347403972578039f; // 0.5 / sqrt(PI); const float f1 = 0.48860251190291992158638462283835f; // 0.5 * sqrt(3/PI); const float f2 = 1.0925484305920790705433857058027f; // 0.5 * sqrt(15/PI); const float f3 = 0.31539156525252000603089369029571f; // 0.25 * sqrt(5.PI); float SH0 = f0; float SH1 = f1 * _direction.y; float SH2 = f1 * _direction.z; float SH3 = f1 * _direction.x; float SH4 = f2 * _direction.x * _direction.y; float SH5 = f2 * _direction.y * _direction.z; float SH6 = f3 * (3.0f * _direction.z * _direction.z - 1.0f); float SH7 = f2 * _direction.x * _direction.z; float SH8 = f2 * 0.5f * (_direction.x * _direction.x - _direction.y * _direction.y); float3[] _SH = m_rotatedLightSH; // Dot the SH together _radiance = SH0 * _SH[0] + SH1 * _SH[1] + SH2 * _SH[2] + SH3 * _SH[3] + SH4 * _SH[4] + SH5 * _SH[5] + SH6 * _SH[6] + SH7 * _SH[7] + SH8 * _SH[8]; _radiance.Max(float3.Zero); }
void TestSHRGBEEncoding() { float3[] coeffs = null; System.IO.FileInfo coeffsFileName = new System.IO.FileInfo("SHCoeffs.sh3"); using (System.IO.FileStream S = coeffsFileName.OpenRead()) using (System.IO.BinaryReader R = new System.IO.BinaryReader(S)) { uint coeffsCount = R.ReadUInt32(); coeffs = new float3[coeffsCount * 9]; for (int i = 0; i < 9 * coeffsCount; i++) { coeffs[i] = new float3(R.ReadSingle(), R.ReadSingle(), R.ReadSingle()); // The exponent bias allows us to support up to 512 in luminance! //coeffs[i] *= 5.0f; } } uint test1_packed = EncodeRGBE(new float3(1, 0, 1.5f)); float3 test1_unpacked = DecodeRGBE(test1_packed); // float3 coeffMin = new float3( float.MaxValue, float.MaxValue, float.MaxValue ); float3 coeffMax = new float3(-float.MaxValue, -float.MaxValue, -float.MaxValue); float3 coeffMinAbs = new float3(float.MaxValue, float.MaxValue, float.MaxValue); int coeffsWithDifferentSignsInRGBCount = 0; for (int i = 0; i < coeffs.Length; i++) { float3 coeff = coeffs[i]; float3 absCoeff = new float3(Math.Abs(coeff.x), Math.Abs(coeff.y), Math.Abs(coeff.z)); if (coeff.x * coeff.y < 0.0f || coeff.x * coeff.z < 0.0f || coeff.y * coeff.z < 0.0f) { coeffsWithDifferentSignsInRGBCount++; } // coeffMin.Min( coeff ); coeffMax.Max(absCoeff); if (absCoeff.x > 0.0f) { coeffMinAbs.x = Math.Min(coeffMinAbs.x, absCoeff.x); } if (absCoeff.y > 0.0f) { coeffMinAbs.y = Math.Min(coeffMinAbs.y, absCoeff.y); } if (absCoeff.z > 0.0f) { coeffMinAbs.z = Math.Min(coeffMinAbs.z, absCoeff.z); } } double expMin = Math.Min(Math.Min(Math.Log(coeffMinAbs.x) / Math.Log(2), Math.Log(coeffMinAbs.y) / Math.Log(2)), Math.Log(coeffMinAbs.z) / Math.Log(2)); double expMax = Math.Max(Math.Max(Math.Log(coeffMax.x) / Math.Log(2), Math.Log(coeffMax.y) / Math.Log(2)), Math.Log(coeffMax.z) / Math.Log(2)); // Measure discrepancies after RGBE encoding // float3 errorAbsMin = new float3( +float.MaxValue, +float.MaxValue, +float.MaxValue ); // float3 errorAbsMax = new float3( -float.MaxValue, -float.MaxValue, -float.MaxValue ); float3 errorRelMin = new float3(+float.MaxValue, +float.MaxValue, +float.MaxValue); float3 errorRelMax = new float3(-float.MaxValue, -float.MaxValue, -float.MaxValue); int minExponent = +int.MaxValue, maxExponent = -int.MaxValue; int largeRelativeErrorsCount = 0; for (int i = 0; i < coeffs.Length; i++) { float3 originalRGB = coeffs[i]; uint RGBE = EncodeRGBE(originalRGB); float3 decodedRGB = DecodeRGBE(RGBE); // Compute absolute error // float3 delta = decodedRGB - originalRGB; // float3 distanceFromOriginal = new float3( Math.Abs( delta.x ), Math.Abs( delta.y ), Math.Abs( delta.z ) ); // errorAbsMin.Min( distanceFromOriginal ); // errorAbsMax.Max( distanceFromOriginal ); // Compute relative error float3 errorRel = new float3(Math.Abs(originalRGB.x) > 0.0f ? Math.Abs(decodedRGB.x / originalRGB.x - 1.0f) : 0.0f, Math.Abs(originalRGB.y) > 0.0f ? Math.Abs(decodedRGB.y / originalRGB.y - 1.0f) : 0.0f, Math.Abs(originalRGB.z) > 0.0f ? Math.Abs(decodedRGB.z / originalRGB.z - 1.0f) : 0.0f); // Scale the relative error by the magnitude of each component as compared to the maximum component // This way, if we happen to have a "large" relative error on a component that is super small compared to the component with maximum amplitude then we can safely drop that small component (it's insignificant compared to the largest contribution) float maxComponent = Math.Max(Math.Max(Math.Abs(originalRGB.x), Math.Abs(originalRGB.y)), Math.Abs(originalRGB.z)); float3 magnitudeScale = maxComponent > 0.0f ? new float3(Math.Abs(originalRGB.x) / maxComponent, Math.Abs(originalRGB.y) / maxComponent, Math.Abs(originalRGB.z) / maxComponent) : float3.Zero; errorRel *= magnitudeScale; // Don't account for dernomalization // if ( decodedRGB.x == 0.0 && originalRGB.x != 0.0f ) errorRel.x = 0.0f; // if ( decodedRGB.y == 0.0 && originalRGB.y != 0.0f ) errorRel.y = 0.0f; // if ( decodedRGB.z == 0.0 && originalRGB.z != 0.0f ) errorRel.z = 0.0f; const float errorThreshold = 0.2f; if (Math.Abs(errorRel.x) > errorThreshold || Math.Abs(errorRel.y) > errorThreshold || Math.Abs(errorRel.z) > errorThreshold) { largeRelativeErrorsCount++; } errorRelMin.Min(errorRel); errorRelMax.Max(errorRel); int exp = (int)((RGBE >> 24) & 31) - EXPONENT_BIAS; minExponent = Math.Min(minExponent, exp); maxExponent = Math.Max(maxExponent, exp); } }
public Primitive(Mesh _Owner, FBX.Scene.Nodes.Mesh.Primitive _Primitive) { m_Owner = _Owner; m_MaterialID = (ushort)_Primitive.MaterialParms.ID; m_Faces = new Face[_Primitive.FacesCount]; m_Vertices = new Vertex[_Primitive.VerticesCount]; // Retrieve streams FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE[] Usages = { FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.POSITION, FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.NORMAL, FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.TANGENT, FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.BITANGENT, FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.TEXCOORDS, }; FBX.Scene.Nodes.Mesh.Primitive.VertexStream[][] Streams = new FBX.Scene.Nodes.Mesh.Primitive.VertexStream[Usages.Length][]; for (int UsageIndex = 0; UsageIndex < Usages.Length; UsageIndex++) { Streams[UsageIndex] = _Primitive.FindStreamsByUsage(Usages[UsageIndex]); if (Streams[UsageIndex].Length == 0) { throw new Exception("No stream for usage " + Usages[UsageIndex] + "! Can't complete target vertex format!"); } } // Build local space bounding box float3 Temp = new float3(); WMath.Vector[] VertexPositions = Streams[0][0].Content as WMath.Vector[]; foreach (WMath.Vector VertexPosition in VertexPositions) { Temp.FromVector3(VertexPosition); m_BBoxMin.Min(Temp); m_BBoxMax.Max(Temp); } // Build faces int FaceIndex = 0; foreach (FBX.Scene.Nodes.Mesh.Primitive.Face F in _Primitive.Faces) { m_Faces[FaceIndex].V0 = F.V0; m_Faces[FaceIndex].V1 = F.V1; m_Faces[FaceIndex].V2 = F.V2; FaceIndex++; } // Build vertices for (int VertexIndex = 0; VertexIndex < m_Vertices.Length; VertexIndex++) { for (int UsageIndex = 0; UsageIndex < Usages.Length; UsageIndex++) { FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE Usage = Usages[UsageIndex]; switch (Usage) { case FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.POSITION: { float3[] Stream = Streams[UsageIndex][0].Content as float3[]; m_Vertices[VertexIndex].P = Stream[VertexIndex]; break; } case FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.NORMAL: { float3[] Stream = Streams[UsageIndex][0].Content as float3[]; m_Vertices[VertexIndex].N = Stream[VertexIndex]; break; } case FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.TANGENT: { float3[] Stream = Streams[UsageIndex][0].Content as float3[]; m_Vertices[VertexIndex].G = Stream[VertexIndex]; break; } case FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.BITANGENT: { float3[] Stream = Streams[UsageIndex][0].Content as float3[]; m_Vertices[VertexIndex].B = Stream[VertexIndex]; break; } case FBX.Scene.Nodes.Mesh.Primitive.VertexStream.USAGE.TEXCOORDS: { float2[] Stream = Streams[UsageIndex][0].Content as float2[]; m_Vertices[VertexIndex].T = Stream[VertexIndex]; break; } } } } }
public void Combine(AABB _Other) { m_Min.Min(_Other.m_Min); m_Max.Max(_Other.m_Max); }
void TestSHRGBEEncoding() { float3[] coeffs = null; System.IO.FileInfo coeffsFileName = new System.IO.FileInfo( "SHCoeffs.sh3" ); using ( System.IO.FileStream S = coeffsFileName.OpenRead() ) using ( System.IO.BinaryReader R = new System.IO.BinaryReader( S ) ) { uint coeffsCount = R.ReadUInt32(); coeffs = new float3[coeffsCount * 9]; for ( int i=0; i < 9*coeffsCount; i++ ) { coeffs[i] = new float3( R.ReadSingle(), R.ReadSingle(), R.ReadSingle() ); // The exponent bias allows us to support up to 512 in luminance! //coeffs[i] *= 5.0f; } } uint test1_packed = EncodeRGBE( new float3( 1, 0, 1.5f ) ); float3 test1_unpacked = DecodeRGBE( test1_packed ); // float3 coeffMin = new float3( float.MaxValue, float.MaxValue, float.MaxValue ); float3 coeffMax = new float3( -float.MaxValue, -float.MaxValue, -float.MaxValue ); float3 coeffMinAbs = new float3( float.MaxValue, float.MaxValue, float.MaxValue ); int coeffsWithDifferentSignsInRGBCount = 0; for ( int i=0; i < coeffs.Length; i++ ) { float3 coeff = coeffs[i]; float3 absCoeff = new float3( Math.Abs( coeff.x ), Math.Abs( coeff.y ), Math.Abs( coeff.z ) ); if ( coeff.x * coeff.y < 0.0f || coeff.x * coeff.z < 0.0f || coeff.y * coeff.z < 0.0f ) coeffsWithDifferentSignsInRGBCount++; // coeffMin.Min( coeff ); coeffMax.Max( absCoeff ); if ( absCoeff.x > 0.0f ) coeffMinAbs.x = Math.Min( coeffMinAbs.x, absCoeff.x ); if ( absCoeff.y > 0.0f ) coeffMinAbs.y = Math.Min( coeffMinAbs.y, absCoeff.y ); if ( absCoeff.z > 0.0f ) coeffMinAbs.z = Math.Min( coeffMinAbs.z, absCoeff.z ); } double expMin = Math.Min( Math.Min( Math.Log( coeffMinAbs.x ) / Math.Log(2), Math.Log( coeffMinAbs.y ) / Math.Log(2) ), Math.Log( coeffMinAbs.z ) / Math.Log(2) ); double expMax = Math.Max( Math.Max( Math.Log( coeffMax.x ) / Math.Log(2), Math.Log( coeffMax.y ) / Math.Log(2) ), Math.Log( coeffMax.z ) / Math.Log(2) ); // Measure discrepancies after RGBE encoding // float3 errorAbsMin = new float3( +float.MaxValue, +float.MaxValue, +float.MaxValue ); // float3 errorAbsMax = new float3( -float.MaxValue, -float.MaxValue, -float.MaxValue ); float3 errorRelMin = new float3( +float.MaxValue, +float.MaxValue, +float.MaxValue ); float3 errorRelMax = new float3( -float.MaxValue, -float.MaxValue, -float.MaxValue ); int minExponent = +int.MaxValue, maxExponent = -int.MaxValue; int largeRelativeErrorsCount = 0; for ( int i=0; i < coeffs.Length; i++ ) { float3 originalRGB = coeffs[i]; uint RGBE = EncodeRGBE( originalRGB ); float3 decodedRGB = DecodeRGBE( RGBE ); // Compute absolute error // float3 delta = decodedRGB - originalRGB; // float3 distanceFromOriginal = new float3( Math.Abs( delta.x ), Math.Abs( delta.y ), Math.Abs( delta.z ) ); // errorAbsMin.Min( distanceFromOriginal ); // errorAbsMax.Max( distanceFromOriginal ); // Compute relative error float3 errorRel = new float3( Math.Abs( originalRGB.x ) > 0.0f ? Math.Abs( decodedRGB.x / originalRGB.x - 1.0f ) : 0.0f, Math.Abs( originalRGB.y ) > 0.0f ? Math.Abs( decodedRGB.y / originalRGB.y - 1.0f ) : 0.0f, Math.Abs( originalRGB.z ) > 0.0f ? Math.Abs( decodedRGB.z / originalRGB.z - 1.0f ) : 0.0f ); // Scale the relative error by the magnitude of each component as compared to the maximum component // This way, if we happen to have a "large" relative error on a component that is super small compared to the component with maximum amplitude then we can safely drop that small component (it's insignificant compared to the largest contribution) float maxComponent = Math.Max( Math.Max( Math.Abs( originalRGB.x ), Math.Abs( originalRGB.y ) ), Math.Abs( originalRGB.z ) ); float3 magnitudeScale = maxComponent > 0.0f ? new float3( Math.Abs( originalRGB.x ) / maxComponent, Math.Abs( originalRGB.y ) / maxComponent, Math.Abs( originalRGB.z ) / maxComponent ) : float3.Zero; errorRel *= magnitudeScale; // Don't account for dernomalization // if ( decodedRGB.x == 0.0 && originalRGB.x != 0.0f ) errorRel.x = 0.0f; // if ( decodedRGB.y == 0.0 && originalRGB.y != 0.0f ) errorRel.y = 0.0f; // if ( decodedRGB.z == 0.0 && originalRGB.z != 0.0f ) errorRel.z = 0.0f; const float errorThreshold = 0.2f; if ( Math.Abs( errorRel.x ) > errorThreshold || Math.Abs( errorRel.y ) > errorThreshold || Math.Abs( errorRel.z ) > errorThreshold ) largeRelativeErrorsCount++; errorRelMin.Min( errorRel ); errorRelMax.Max( errorRel ); int exp = (int) ((RGBE >> 24) & 31) - EXPONENT_BIAS; minExponent = Math.Min( minExponent, exp ); maxExponent = Math.Max( maxExponent, exp ); } }
// uint packR8G8B8A8( float4 value ) { // value = saturate( value ); // return ( ( ( uint( value.x * 255.0 ) ) << 24 ) | ( ( uint( value.y * 255.0 ) ) << 16 ) | ( ( uint( value.z * 255.0 ) ) << 8 ) | ( uint( value.w * 255.0 ) ) ); // } // // float4 unpackR8G8B8A8( uint value ) { // return float4( ( value >> 24 ) & 0xFF, ( value >> 16 ) & 0xFF, ( value >> 8 ) & 0xFF, value & 0xFF ) / 255.0; // } // // // RGBE (ward 1984) // uint packRGBE( float3 value ) { // const float sharedExp = ceil( ApproxLog2( max( max( value.r, value.g ), value.b ) ) ); // return packR8G8B8A8( saturate( float4( value / ApproxExp2( sharedExp ), ( sharedExp + 128.0f ) / 255.0f ) ) ); // } // // float3 unpackRGBE( uint value ) { // const float4 rgbe = unpackR8G8B8A8( value ); // return rgbe.rgb * ApproxExp2( rgbe.a * 255.0f - 128.0f ); // } uint EncodeRGBE( float3 _RGB ) { float3 Sign = new float3( Math.Sign( _RGB.x ), Math.Sign( _RGB.y ), Math.Sign( _RGB.z ) ); _RGB *= Sign; float maxRGB = _RGB.Max(); int sharedExp = (int) Math.Ceiling( Math.Log( maxRGB ) / Math.Log(2) ); sharedExp = Math.Max( 0, Math.Min( 31, sharedExp + EXPONENT_BIAS ) ) - EXPONENT_BIAS; // float pow2 = (float) Math.Pow( 2.0f, sharedExp ); // float3 reducedRGB = _RGB / pow2; float invPow2 = (float) Math.Pow( 2.0f, -sharedExp ); float3 reducedRGB = _RGB * invPow2; reducedRGB.x = Math.Max( 0.0f, Math.Min( 1.0f, reducedRGB.x ) ); reducedRGB.y = Math.Max( 0.0f, Math.Min( 1.0f, reducedRGB.y ) ); reducedRGB.z = Math.Max( 0.0f, Math.Min( 1.0f, reducedRGB.z ) ); byte R = (byte) (255.0f * reducedRGB.x); byte G = (byte) (255.0f * reducedRGB.y); byte B = (byte) (255.0f * reducedRGB.z); byte exp = (byte) (EXPONENT_BIAS + sharedExp); exp |= (byte) ((Sign.x < 0.0f ? 128 : 0) | (Sign.y < 0.0f ? 64 : 0) | (Sign.z < 0.0f ? 32 : 0)); uint RGBE = (uint) ((exp << 24) | (R << 16) | (G << 8) | B); return RGBE; }
public void Combine( AABB a, AABB b ) { m_Min = a.m_Min; m_Min.Min( b.m_Min ); m_Max = a.m_Max; m_Max.Max( b.m_Max ); }
public Mesh( Scene _Owner, idTech5Map.Map.Entity _Entity ) : base(_Owner, _Entity) { // BUild primitives and local space BBox m_Primitives = new Primitive[_Entity.m_Model.m_Surfaces.Length]; m_BBoxMin_Local = float.MaxValue * float3.One; m_BBoxMax_Local = -float.MaxValue * float3.One; int PrimitiveIndex = 0; foreach ( idTech5Map.Model.Surface S in _Entity.m_Model.m_Surfaces ) { m_Primitives[PrimitiveIndex] = new Primitive( this, S ); m_BBoxMin_Local.Min( m_Primitives[PrimitiveIndex].m_BBoxMin ); m_BBoxMax_Local.Max( m_Primitives[PrimitiveIndex].m_BBoxMax ); PrimitiveIndex++; } // Convert BBox to world space m_BBoxMin_World = float.MaxValue * float3.One; m_BBoxMax_World = -float.MaxValue * float3.One; for ( int CornerIndex=0; CornerIndex < 8; CornerIndex++ ) { float X = (CornerIndex >> 0) & 1; float Y = (CornerIndex >> 0) & 1; float Z = (CornerIndex >> 0) & 1; float3 D = m_BBoxMax_Local - m_BBoxMin_Local; float3 lsCorner = m_BBoxMin_Local + new float3( X * D.x, Y * D.y, Z * D.z ); float3 wsCorner = (float3) (new float4( lsCorner, 1.0f ) * m_Local2Parent); m_BBoxMin_World.Min( wsCorner ); m_BBoxMax_World.Max( wsCorner ); } }
public void Compute(uint _X, uint _Y) { float2 __Position = new float2(0.5f + _X, 0.5f + _Y); float2 UV = __Position / m_resolution; uint pixelPositionX = (uint)Mathf.Floor(__Position.x); uint pixelPositionY = (uint)Mathf.Floor(__Position.y); float noise = 0.0f; //_tex_blueNoise[pixelPosition & 0x3F]; //PerformIntegrationTest(); // Setup camera ray float3 csView = BuildCameraRay(UV); float Z2Distance = csView.Length; csView /= Z2Distance; float3 wsView = (new float4(csView, 0.0f) * m_camera2World).xyz; // Read back depth, normal & central radiance value from last frame float Z = FetchDepth(__Position, 0.0f); Z -= 1e-2f; // Prevent acnea by offseting the central depth closer // float distance = Z * Z2Distance; float3 wsNormal = m_arrayNormal[0][pixelPositionX, pixelPositionY].xyz.Normalized; // Read back last frame's radiance value that we always can use as a default for neighbor areas float3 centralRadiance = m_arrayIrradiance[0][pixelPositionX, pixelPositionY].xyz; // Compute local camera-space float3 wsPos = m_camera2World[3].xyz + Z * Z2Distance * wsView; float3 wsRight = wsView.Cross(m_camera2World[1].xyz).Normalized; float3 wsUp = wsRight.Cross(wsView); float3 wsAt = -wsView; float4x4 localCamera2World = new float4x4(new float4(wsRight, 0), new float4(wsUp, 0), new float4(wsAt, 0), new float4(wsPos, 1)); // Compute local camera-space normal float3 N = new float3(wsNormal.Dot(wsRight), wsNormal.Dot(wsUp), wsNormal.Dot(wsAt)); N.z = Math.Max(1e-4f, N.z); // Make sure it's never 0! // float3 T, B; // BuildOrthonormalBasis( N, T, B ); // Compute screen radius of gather sphere float screenSize_m = 2.0f * Z * TAN_HALF_FOV; // Vertical size of the screen in meters when extended to distance Z float sphereRadius_pixels = m_resolution.y * m_gatherSphereMaxRadius_m / screenSize_m; sphereRadius_pixels = Mathf.Min(GATHER_SPHERE_MAX_RADIUS_P, sphereRadius_pixels); // Prevent it to grow larger than our fixed limit float radiusStepSize_pixels = Mathf.Max(1.0f, sphereRadius_pixels / MAX_SAMPLES); // This gives us our radial step size in pixels uint samplesCount = Mathf.Clamp((uint)Mathf.Ceiling(sphereRadius_pixels / radiusStepSize_pixels), 1, MAX_SAMPLES); // Reduce samples count if possible float radiusStepSize_meters = sphereRadius_pixels * screenSize_m / (samplesCount * m_resolution.y); // This gives us our radial step size in meters // Start gathering radiance and bent normal by subdividing the screen-space disk around our pixel into Z slices float4 GATHER_DEBUG = float4.Zero; float3 sumIrradiance = float3.Zero; float3 csAverageBentNormal = float3.Zero; // float averageConeAngle = 0.0f; // float varianceConeAngle = 0.0f; float sumAO = 0.0f; for (uint angleIndex = 0; angleIndex < MAX_ANGLES; angleIndex++) { float phi = (angleIndex + noise) * Mathf.PI / MAX_ANGLES; //phi = 0.0f; float2 csDirection; csDirection.x = Mathf.Cos(phi); csDirection.y = Mathf.Sin(phi); // Gather irradiance and average cone direction for that slice float3 csBentNormal; float2 coneAngles; float AO; sumIrradiance += GatherIrradiance(csDirection, localCamera2World, N, radiusStepSize_meters, samplesCount, Z, centralRadiance, out csBentNormal, out coneAngles, out AO, ref GATHER_DEBUG); // if ( AO < -0.01f || AO > 1.01f ) // throw new Exception( "MERDE!" ); csAverageBentNormal += csBentNormal; sumAO += AO; // // We're using running variance computation from https://www.johndcook.com/blog/standard_deviation/ // // Avg(N) = Avg(N-1) + [V(N) - Avg(N-1)] / N // // S(N) = S(N-1) + [V(N) - Avg(N-1)] * [V(N) - Avg(N)] // // And variance = S(finalN) / (finalN-1) // // // float previousAverageConeAngle = averageConeAngle; // averageConeAngle += (coneAngles.x - averageConeAngle) / (2*angleIndex+1); // varianceConeAngle += (coneAngles.x - previousAverageConeAngle) * (coneAngles.x - averageConeAngle); // // previousAverageConeAngle = averageConeAngle; // averageConeAngle += (coneAngles.y - averageConeAngle) / (2*angleIndex+2); // varianceConeAngle += (coneAngles.y - previousAverageConeAngle) * (coneAngles.y - averageConeAngle); } // Finalize bent cone & irradiance csAverageBentNormal = csAverageBentNormal.Normalized; //csAverageBentNormal *= Mathf.PI / MAX_ANGLES; sumIrradiance *= Mathf.PI / MAX_ANGLES; // varianceConeAngle /= 2.0f*MAX_ANGLES - 1.0f; // float stdDeviation = Mathf.Sqrt( varianceConeAngle ); sumAO /= 2.0f * MAX_ANGLES; //if ( sumAO < 0.0f || sumAO > 1.0f ) // throw new Exception( "MERDE!" ); float averageConeAngle = Mathf.Acos(1.0f - sumAO); float varianceConeAngle = 0.0f; // Unfortunately, we don't have a proper value for the variance anymore... :'( float stdDeviation = Mathf.Sqrt(varianceConeAngle); sumIrradiance.Max(float3.Zero); ////////////////////////////////////////////////////////////////////////// // Finalize results m_sumIrradiance = new float4(sumIrradiance, 0); m_bentCone = new float4(Mathf.Max(0.01f, Mathf.Cos(averageConeAngle)) * csAverageBentNormal, 1.0f - stdDeviation / (0.5f * Mathf.PI)); float3 DEBUG_VALUE = new float3(1, 0, 1); DEBUG_VALUE = csAverageBentNormal; DEBUG_VALUE = csAverageBentNormal.x * wsRight - csAverageBentNormal.y * wsUp - csAverageBentNormal.z * wsAt; // World-space normal //DEBUG_VALUE = cos( averageConeAngle ); //DEBUG_VALUE = dot( ssAverageBentNormal, N ); //DEBUG_VALUE = 0.01 * Z; //DEBUG_VALUE = sphereRadius_pixels / GATHER_SPHERE_MAX_RADIUS_P; //DEBUG_VALUE = 0.1 * (radiusStepSize_pixels-1); //DEBUG_VALUE = 0.5 * float(samplesCount) / MAX_SAMPLES; //DEBUG_VALUE = varianceConeAngle; //DEBUG_VALUE = stdDeviation; //DEBUG_VALUE = float3( GATHER_DEBUG.xy, 0 ); //DEBUG_VALUE = float3( GATHER_DEBUG.zw, 0 ); DEBUG_VALUE = GATHER_DEBUG.xyz; //DEBUG_VALUE = N; //m_bentCone = float4( DEBUG_VALUE, 1 ); ////////////////////////////////////////////////////////////////////////// // Finalize bent code debug info m_wsConePosition = wsPos; m_wsConeDirection = csAverageBentNormal.x * wsRight + csAverageBentNormal.y * wsUp + csAverageBentNormal.z * wsAt; //m_wsConeDirection = wsNormal; m_averageConeAngle = averageConeAngle; m_stdDeviation = stdDeviation; }