void AddObstacleSquare(float2 _P, float2 _D, float2 _radius) { float2 X = new float2(_D.x / _radius.x, _D.y / _radius.x); float2 Y = new float2(-_D.y / _radius.y, _D.x / _radius.y); float2 C = m_boxCenter - _P; C = new float2(C.Dot(X), C.Dot(Y)); for (int i = 0; i < PIXELS_COUNT; i++) { float angle = (float)(2.0 * Math.PI * i / PIXELS_COUNT); float2 wsV = new float2((float)Math.Cos(angle), (float)Math.Sin(angle)); float2 V = new float2(wsV.Dot(X), wsV.Dot(Y)); // Compute the intersection with the unit box centered in 0 float t0 = (C.x + Math.Sign(V.x)) / -V.x; float2 I0 = C + t0 * V; float2 N0 = new float2(-Math.Sign(V.x), 0.0f); if (Math.Abs(I0.y) > 1.0f) { t0 = float.MaxValue; } float t1 = (C.y + Math.Sign(V.y)) / -V.y; float2 I1 = C + t1 * V; float2 N1 = new float2(0.0f, -Math.Sign(V.y)); if (Math.Abs(I1.x) > 1.0f) { t1 = float.MaxValue; } float t; // Math.Min( t0, t1 ) float2 N; if (t0 < t1) { t = t0; N = N0; } else { t = t1; N = N1; } if (t < 0.0f || t > m_Pixels[i].Distance) { continue; } float2 wsN = (N.x * _radius.x * _radius.x * X + N.y * _radius.y * _radius.y * Y).Normalized; m_Pixels[i].Distance = t; m_Pixels[i].Position = m_boxCenter + t * wsV; m_Pixels[i].Normal = wsN; } }
private void panelOutput_MouseMove(object sender, MouseEventArgs e) { // Transform mouse position into node space float2 mousePosition = Client2Pos(e.Location); float nodeRadius = 0.01f * 0.5f * (m_CB_Main.m._cameraSize.x + m_CB_Main.m._cameraSize.y); // Radius adapts to camera size float sqNodeRadius = nodeRadius * nodeRadius; // Identify any node under the mouse m_SB_NodeSims[0].Read(); m_displayText = null; m_CB_Main.m._hoveredNodeIndex = ~0U; for (int nodeIndex = 0; nodeIndex < m_nodesCount; nodeIndex++) { float2 nodePosition = m_SB_NodeSims[0].m[nodeIndex].m_position; float2 delta = nodePosition - mousePosition; float sqDistance = delta.Dot(delta); if (sqDistance > sqNodeRadius) { continue; } // Found it! ProtoParser.Neuron selectedNeuron = m_graph[nodeIndex]; m_displayText = selectedNeuron.m_name != null ? selectedNeuron.m_name : selectedNeuron.Parents[0].m_name + "()"; m_selectedNodePosition = nodePosition; m_CB_Main.m._hoveredNodeIndex = (uint)nodeIndex; break; } m_CB_Main.UpdateData(); }
void CheckCrash(float2 _oldPos) { for (int i = 0; i < m_landscape.Count - 1; i++) { float2 P0 = m_landscape[i]; float2 P1 = m_landscape[i + 1]; float2 D = (P1 - P0).Normalized; float2 N = new float2(-D.y, D.x); float2 Dir = Pos - _oldPos; float dirLength = Dir.Length; Dir *= dirLength != 0.0f ? 1.0f / dirLength : 0.0f; float hitDistance = (P0 - Pos).Dot(N) / Dir.Dot(N); if (hitDistance < 0.0f || hitDistance > dirLength) { continue; // Outside our ship's trajectory } float2 HitPos = Pos + hitDistance * Dir; // float dot = (HitPos - P0).Dot( N ); // Must be 0! if (HitPos.x < P0.x || HitPos.x > P1.x) { continue; // Outside of landscape segment } if (Math.Abs(Vel.y) >= 40.0f) { throw new Exception("Crash!"); } else { MessageBox.Show("SUCCESS!"); } } }
void AddObstacleRound(float2 _P, float2 _D, float2 _radius) { float2 X = new float2(_D.x / _radius.x, _D.y / _radius.x); float2 Y = new float2(-_D.y / _radius.y, _D.x / _radius.y); float2 C = m_boxCenter - _P; C = new float2(C.Dot(X), C.Dot(Y)); for (int i = 0; i < PIXELS_COUNT; i++) { float angle = (float)(2.0 * Math.PI * i / PIXELS_COUNT); float2 wsV = new float2((float)Math.Cos(angle), (float)Math.Sin(angle)); float2 V = new float2(wsV.Dot(X), wsV.Dot(Y)); // Compute the intersection with the unit circle centered in 0 float c = C.Dot(C) - 1.0f; float b = C.Dot(V); float a = V.Dot(V); float Delta = b * b - a * c; if (Delta < 0.0f) { continue; } Delta = (float)Math.Sqrt(Delta); float t0 = (-b - Delta) / a; float t1 = (-b + Delta) / a; float t = Math.Min(t0, t1); if (t < 0.0f || t > m_Pixels[i].Distance) { continue; } float2 I = C + t * V; // Hit in local space, also the normal float2 wsN = (I.x * _radius.x * _radius.x * X + I.y * _radius.y * _radius.y * Y).Normalized; m_Pixels[i].Distance = t; m_Pixels[i].Position = m_boxCenter + t * wsV; m_Pixels[i].Normal = wsN; } }
// Cuts the interval with another plane public void Cut(Plane _P) { float ViewDist = m_Direction.Dot(_P.m_Normal); float2 D = m_Position - _P.m_Position; float Dist = D.Dot(_P.m_Normal); float t = -Dist / ViewDist; if (ViewDist > 0.0f) { m_Min = Math.Max(m_Min, t); } else { m_Max = Math.Min(m_Max, t); } }
private void panelOutput_MouseMove(object sender, MouseEventArgs e) { // Transform mouse position into node space float2 mousePosition = Client2Pos(e.Location); float nodeRadius = 0.01f * 0.5f * (m_CB_Main.m._cameraSize.x + m_CB_Main.m._cameraSize.y); // Radius adapts to camera size float sqNodeRadius = nodeRadius * nodeRadius; // Identify any node under the mouse m_SB_Nodes.Read(); m_displayText = null; m_CB_Main.m._hoveredNodeIndex = ~0U; for (int nodeIndex = 0; nodeIndex < m_nodesCount; nodeIndex++) { float2 nodePosition = m_SB_Nodes.m[nodeIndex].m_position; float2 delta = nodePosition - mousePosition; float sqDistance = delta.Dot(delta); if (sqDistance > sqNodeRadius) { continue; } // Found it! ProtoParser.Neuron selectedNeuron = m_graph[nodeIndex]; if (selectedNeuron.m_name != null) { m_displayText = selectedNeuron.m_name; } else { m_displayText = "*" + selectedNeuron.Parents[0].m_name; } if (selectedNeuron.m_value is ProtoParser.NeuronValue) { // Show value m_displayText += "( "; m_displayText += (selectedNeuron.m_value as ProtoParser.NeuronValue).m_valueMean != null ? (selectedNeuron.m_value as ProtoParser.NeuronValue).m_valueMean : "<null>"; m_displayText += " )"; } // Show value if (radioButtonShowTemperature.Checked) { m_SB_HeatSource.Read(); float heat = m_SB_HeatSource.m[m_nodesCount * integerTrackbarControlShowQuerySourceIndex.Value + m_neuron2ID[selectedNeuron]]; m_displayText += " = " + heat.ToString("G4"); } else if (radioButtonShowBarycentrics.Checked) { GetBarycentricsBuffer(); float heat = m_SB_HeatBarycentrics.m[m_nodesCount * integerTrackbarControlShowQuerySourceIndex.Value + m_neuron2ID[selectedNeuron]]; m_displayText += " = " + heat.ToString("G4"); } else if (radioButtonShowResultsBarycentric.Checked) { GetBarycentricsBuffer(); float heat = ComputeResult(m_neuron2ID[selectedNeuron]); m_displayText += " = " + heat.ToString("G4"); } else if (radioButtonShowResultsSum.Checked) { float heatSum = GetSumBuffer().m[m_neuron2ID[selectedNeuron]]; m_displayText += " = " + heatSum.ToString("G4"); } m_selectedNodePosition = nodePosition; m_CB_Main.m._hoveredNodeIndex = (uint)nodeIndex; break; } m_CB_Main.UpdateData(); }
// Solves the best planes for the room void SolveRoom() { ////////////////////////////////////////////////////////////////////////// // Use EM to obtain principal directions List <float3> directions = new List <float3>(PIXELS_COUNT); for (int i = 0; i < PIXELS_COUNT; i++) { // if ( m_Pixels[i].Distance < 1e3f ) directions.Add(new float3(m_Pixels[i].Normal.x, m_Pixels[i].Normal.y, 0.0f)); } int planesCount = integerTrackbarControlResultPlanesCount.Value; m_Planes = new Plane[planesCount]; m_Lobes = new FitLobe[planesCount]; for (int i = 0; i < planesCount; i++) { m_Lobes[i] = new FitLobe(); } PerformExpectationMaximization(directions.ToArray(), m_Lobes, 1000, 1e-6); for (int i = 0; i < planesCount; i++) { m_Planes[i].m_Weight = (float)m_Lobes[i].Alpha; } ////////////////////////////////////////////////////////////////////////// // Remove similar planes for (int i = 0; i < planesCount - 1; i++) { FitLobe P0 = m_Lobes[i]; if (m_Planes[i].m_Dismissed) { continue; // Already dismissed... } float3 averageDirection = (float)P0.Alpha * P0.Direction; double maxKappa = P0.Concentration; for (int j = i + 1; j < planesCount; j++) { FitLobe P1 = m_Lobes[j]; if (m_Planes[j].m_Dismissed) { continue; // Already dismissed... } float dot = P0.Direction.Dot(P1.Direction); if (dot < floatTrackbarControlSimilarPlanes.Value) { continue; } averageDirection += (float)P1.Alpha * P1.Direction; maxKappa = Math.Max(maxKappa, P1.Concentration); m_Planes[j].m_Dismissed = true; // Dismiss m_Planes[j].m_DismissalReason += " SIMILAR" + i; } P0.Direction = averageDirection.Normalized; P0.Concentration = maxKappa; } ////////////////////////////////////////////////////////////////////////// // Place planes at the best positions float maxOrthoDistance = 0.0f; for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { float2 Normal = new float2(m_Lobes[planeIndex].Direction.x, m_Lobes[planeIndex].Direction.y); m_Planes[planeIndex].m_Normal = Normal; float sumOrthoDistances0 = 0.0f; float sumWeights0 = 0.0f; float sumOrthoDistances1 = 0.0f; float sumWeights1 = 0.0f; for (int i = 0; i < PIXELS_COUNT; i++) { float orthoDistance = -(m_Pixels[i].Position - m_boxCenter).Dot(Normal); // Use computed probabilities float weight = (float)probabilities[i, planeIndex]; sumOrthoDistances0 += weight * orthoDistance; sumWeights0 += weight; // Use weighted sum float dot = Math.Max(0.0f, Normal.Dot(m_Pixels[i].Normal)); dot = (float)Math.Pow(dot, floatTrackbarControlWeightExponent.Value); weight = dot; sumOrthoDistances1 += weight * orthoDistance; sumWeights1 += weight; } float averageOrthoDistance0 = sumOrthoDistances0 / sumWeights0; float averageOrthoDistance1 = sumOrthoDistances1 / sumWeights1; // Choose whichever ortho distance is best float finalOrthoDistance; if (radioButtonBest.Checked) { finalOrthoDistance = Math.Max(averageOrthoDistance0, averageOrthoDistance1); } else { finalOrthoDistance = radioButtonProbabilities.Checked ? averageOrthoDistance0 : averageOrthoDistance1; } maxOrthoDistance = Math.Max(maxOrthoDistance, finalOrthoDistance); m_Planes[planeIndex].m_OrthoDistance = finalOrthoDistance; m_Planes[planeIndex].m_Position = m_boxCenter - finalOrthoDistance * Normal; if (finalOrthoDistance <= 0.0f) { m_Planes[planeIndex].m_Dismissed = true; m_Planes[planeIndex].m_DismissalReason += " BEHIND"; } } ////////////////////////////////////////////////////////////////////////// // Reconstruct weight if (radioButtonNormalAffinity.Checked) { // Reconstruct weights by normal affinity // We do the same operation as the normal weighting to compute ortho distances: dot each pixel with each plane normal and use that as a weight for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { float2 Normal = new float2(m_Lobes[planeIndex].Direction.x, m_Lobes[planeIndex].Direction.y); // Use weighted sum float sumWeights = 0.0f; for (int i = 0; i < PIXELS_COUNT; i++) { float weight = Math.Max(0.0f, Normal.Dot(m_Pixels[i].Normal)); weight = (float)Math.Pow(weight, floatTrackbarControlWeightExponent.Value); sumWeights += weight; } float finalWeight = sumWeights / PIXELS_COUNT; m_Lobes[planeIndex].Alpha = finalWeight; m_Planes[planeIndex].m_Weight = finalWeight; } } else if (radioButtonLargestD.Checked) { // Choose largest ortho distances for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { float weight = Math.Max(0.0f, m_Planes[planeIndex].m_OrthoDistance / maxOrthoDistance); m_Lobes[planeIndex].Alpha = weight; m_Planes[planeIndex].m_Weight = weight; } } else if (radioButtonWeightHybrid.Checked) { // Hybrid method combining ortho distance and normal affinity for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { float2 Normal = new float2(m_Lobes[planeIndex].Direction.x, m_Lobes[planeIndex].Direction.y); // Use weighted sum float sumWeights = 0.0f; for (int i = 0; i < PIXELS_COUNT; i++) { float weight = Math.Max(0.0f, Normal.Dot(m_Pixels[i].Normal)); weight = (float)Math.Pow(weight, floatTrackbarControlWeightExponent.Value); sumWeights += weight; } float finalWeight = sumWeights / PIXELS_COUNT; finalWeight *= Math.Max(0.0f, m_Planes[planeIndex].m_OrthoDistance / maxOrthoDistance); m_Lobes[planeIndex].Alpha = finalWeight; m_Planes[planeIndex].m_Weight = finalWeight; } } ////////////////////////////////////////////////////////////////////////// // Dismiss unimportant planes float averageWeight = 0.0f, harmonicAverageWeight = 0.0f, averageConcentration = 0.0f, harmonicAverageConcentration = 0.0f; float maxWeight = 0.0f, maxConcentration = 0.0f; float minWeight = float.MaxValue, minConcentration = float.MaxValue; int validPlanesCount = 0; for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { if (m_Planes[planeIndex].m_Dismissed) { continue; } float weight = (float)m_Lobes[planeIndex].Alpha; minWeight = Math.Min(minWeight, weight); maxWeight = Math.Max(maxWeight, weight); averageWeight += weight; harmonicAverageWeight += 1.0f / Math.Max(1e-4f, weight); float concentration = (float)m_Lobes[planeIndex].Concentration; minConcentration = Math.Min(minConcentration, concentration); maxConcentration = Math.Max(maxConcentration, concentration); averageConcentration += concentration; harmonicAverageConcentration += 1.0f / Math.Max(1e-4f, concentration); validPlanesCount++; } validPlanesCount = Math.Max(1, validPlanesCount); averageWeight /= validPlanesCount; harmonicAverageWeight = validPlanesCount / harmonicAverageWeight; averageConcentration /= validPlanesCount; harmonicAverageConcentration = validPlanesCount / harmonicAverageConcentration; // float dismissWeight = 0.5f / validPlanesCount; float dismissWeight = floatTrackbarControlDismissFactor.Value * harmonicAverageWeight; float dismissConcentration = floatTrackbarControlDismissFactor.Value * harmonicAverageConcentration; // Select the N best planes/Dismiss others if (!radioButtonUseBest.Checked) { for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { bool dismissed = radioButtonDismissKappa.Checked ? m_Lobes[planeIndex].Concentration < dismissConcentration : m_Lobes[planeIndex].Alpha < dismissWeight; if (dismissed) { m_Planes[planeIndex].m_Dismissed = true; m_Planes[planeIndex].m_DismissalReason += " REJECTED"; } } } // Dismiss planes that are totally clipped by others DismissClippedPlanes(); // Dismiss planes above target count List <SortedPlane> SortedPlanes = new List <SortedPlane>(planesCount); for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { SortedPlanes.Add(new SortedPlane() { planeIndex = planeIndex, weight = m_Planes[planeIndex].m_Dismissed ? 0.0f : m_Planes[planeIndex].m_Weight }); } SortedPlanes.Sort(); for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { SortedPlane SP = SortedPlanes[planeIndex]; if (!m_Planes[SP.planeIndex].m_Dismissed && planeIndex >= integerTrackbarControlKeepBestPlanesCount.Value) { m_Planes[SP.planeIndex].m_Dismissed = true; m_Planes[SP.planeIndex].m_DismissalReason += " LIMIT"; } } ////////////////////////////////////////////////////////////////////////// // Display info string text = ""; text += "Min, Max, Avg, HAvg Weight = " + minWeight.ToString("G4") + ", " + maxWeight.ToString("G4") + ", " + averageWeight.ToString("G4") + ", " + harmonicAverageWeight.ToString("G4") + " > Dismiss weight = " + dismissWeight + "\r\n"; text += "Min, Max, Avg, HAvg Kappa = " + minConcentration.ToString("G4") + ", " + maxConcentration.ToString("G4") + ", " + averageConcentration.ToString("G4") + ", " + harmonicAverageConcentration.ToString("G4") + " > Dismiss kappa = " + dismissConcentration + "\r\n\r\n"; text += planesCount + " Planes:\r\n"; for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { text += "#" + planeIndex + " => { " + m_Lobes[planeIndex].Direction.x.ToString("G4") + ", " + m_Lobes[planeIndex].Direction.y.ToString("G4") + "} D = " + m_Planes[planeIndex].m_OrthoDistance.ToString("G4") + " - k=" + m_Lobes[planeIndex].Concentration.ToString("G4") + " - weight = " + m_Lobes[planeIndex].Alpha.ToString("G4") + m_Planes[planeIndex].m_DismissalReason + " " + (m_Planes[planeIndex].m_Dismissed ? "(DIS)" : "") + "\r\n"; } textBoxPlanes.Text = text; // Refresh panelOutput.UpdateBitmap(); panelHistogram.UpdateBitmap(); }
// Build a random polygonal room void BuildRoom() { const float MAX_DISTANCE = 20.0f; Random RNG = new Random(integerTrackbarControlRandomSeed.Value); // int planesCount = (int) (4 + 2 * RNG.NextDouble()); int planesCount = integerTrackbarControlRoomPlanesCount.Value; float2[] planePositions = new float2[planesCount]; float2[] planeNormals = new float2[planesCount]; m_boxCenter = float2.Zero; //new float2( (float) (-10.0 + 20.0 * RNG.NextDouble()), (float) (-10.0 + 20.0 * RNG.NextDouble()) ); float baseAngle = (float)(RNG.NextDouble() * 2.0 * Math.PI); float averageAngle = (float)(2.0 * Math.PI / planesCount); for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { float2 normal = new float2((float)Math.Cos(baseAngle), (float)Math.Sin(baseAngle)); planeNormals[planeIndex] = normal; float2 toCenter = m_boxCenter; float distance2Center = toCenter.Dot(normal); float planeDistance = distance2Center > 0.0f ? distance2Center + 0.1f + (float)(Math.Max(0.0f, MAX_DISTANCE - distance2Center) * RNG.NextDouble()) : (float)(MAX_DISTANCE * RNG.NextDouble()); float2 position = m_boxCenter - planeDistance * normal; planePositions[planeIndex] = position; baseAngle += (float)(averageAngle * (0.9 + 0.2 * RNG.NextDouble())); } // Build the original room pixels for (int i = 0; i < PIXELS_COUNT; i++) { float2 C = m_boxCenter; float angle = (float)(2.0 * Math.PI * i / PIXELS_COUNT); float2 V = new float2((float)Math.Cos(angle), (float)Math.Sin(angle)); // Compute intersection with the planes float minHitDistance = float.MaxValue; int hitPlaneIndex = -1; for (int planeIndex = 0; planeIndex < planesCount; planeIndex++) { float2 D = C - planePositions[planeIndex]; float hitDistance = -D.Dot(planeNormals[planeIndex]) / V.Dot(planeNormals[planeIndex]); if (hitDistance > 0.0f && hitDistance < minHitDistance) { minHitDistance = hitDistance; hitPlaneIndex = planeIndex; } } if (hitPlaneIndex == -1) { m_Pixels[i].Distance = 40.0f; continue; } minHitDistance = Math.Min(40.0f, minHitDistance); float noiseDistance = 0.2f * (2.0f * (float)RNG.NextDouble() - 1.0f); m_Pixels[i].Distance = minHitDistance + noiseDistance; m_Pixels[i].Position = m_boxCenter + m_Pixels[i].Distance * V; float2 normal = planeNormals[hitPlaneIndex]; float normalAngle = (float)Math.Atan2(normal.y, normal.x); float noiseAngle = 0.1f * (2.0f * (float)RNG.NextDouble() - 1.0f); normal = new float2((float)Math.Cos(normalAngle + noiseAngle), (float)Math.Sin(normalAngle + noiseAngle)); m_Pixels[i].Normal = normal; } // Add some obstacles int OBSTACLES_COUNT = integerTrackbarControlObstacles.Value; const float MAX_OBSTACLE_DISTANCE = 15.0f; m_ObstaclesRound.Clear(); m_ObstaclesSquare.Clear(); for (int i = 0; i < OBSTACLES_COUNT; i++) { // float2 pos = m_boxCenter + MAX_OBSTACLE_DISTANCE * new float2( (float) (2.0 * RNG.NextDouble() - 1.0), (float) (2.0 * RNG.NextDouble() - 1.0) ); float d = MAX_OBSTACLE_DISTANCE * (float)(0.3 + 0.7 * RNG.NextDouble()); float a = (float)(2.0 * Math.PI * RNG.NextDouble()); float2 pos = m_boxCenter + d * new float2((float)Math.Cos(a), (float)Math.Sin(a)); float2 dir = new float2((float)(2.0 * RNG.NextDouble() - 1.0), (float)(2.0 * RNG.NextDouble() - 1.0)).Normalized; float2 radius = 1.0f * new float2((float)(0.1 + 0.9 * RNG.NextDouble()), (float)(0.1 + 0.9 * RNG.NextDouble())); Obstacle O = new Obstacle() { m_Position = pos, m_Orientation = dir, m_Scale = radius }; double k = RNG.NextDouble(); if (k < 0.7) { AddObstacleRound(pos, dir, radius); m_ObstaclesRound.Add(O); } else { AddObstacleSquare(pos, dir, radius); m_ObstaclesSquare.Add(O); } } SolveRoom(); }
/// <summary> /// This function performs a simple integration test by slicing a bent plane into N slice and using the integrals /// to compute the resulting average bent normal for each slice, then finalizing the resulting normal which should /// equal the initial test normal /// </summary> void PerformIntegrationTest() { float3 csNormal = new float3(2, 4, 1).Normalized; // Simple 45° bent normal uint SLICES_COUNT = 128; float3 csAverageBentNormal = float3.Zero; for (uint sliceIndex = 0; sliceIndex < SLICES_COUNT; sliceIndex++) { float phi = sliceIndex * Mathf.PI / SLICES_COUNT; float2 csDirection = new float2(Mathf.Cos(phi), Mathf.Sin(phi)); // Compute initial horizon angles float t = -csDirection.Dot(csNormal.xy) / csNormal.z; float maxCosTheta_Front = t / Mathf.Sqrt(t * t + 1.0f); float maxCosTheta_Back = -maxCosTheta_Front; // Back cosine is simply the mirror value // ======================= // Here, the runtime algorithm is normally updating the horizon angles but we keep them flat: our goal is to obtain the original csNormal! // ======================= // Accumulate bent normal direction by rebuilding and averaging the front & back horizon vectors float2 ssNormal = new float2(csNormal.xy.Dot(csDirection), csNormal.z); // Slice-space normal /* // Numerical integration * // Half brute force where we perform the integration numerically as a sum... * // * const uint STEPS_COUNT = 256; * * float theta_Front = Mathf.Acos( maxCosTheta_Front ); * float theta_Back = -Mathf.Acos( maxCosTheta_Back ); // Technically, this is theta0 and it should be in [-PI,0] but we took its absolute value to ease our computation * * float2 ssBentNormal = float2.Zero;// 0.001f * float2.UnitY; * for ( uint i=0; i < STEPS_COUNT; i++ ) { * float theta = Mathf.Lerp( theta_Back, theta_Front, (i+0.5f) / STEPS_COUNT ); * float sinTheta = Mathf.Sin( theta ), cosTheta = Mathf.Cos( theta ); * float2 ssOmega = new float2( sinTheta, cosTheta ); * * float cosAlpha = Mathf.Saturate( ssOmega.Dot( ssNormal ) ); * * float weight = cosAlpha * Mathf.Abs( sinTheta ); // cos(alpha) * sin(theta).dTheta (be very careful to take abs(sin(theta)) because our theta crosses the pole and becomes negative here!) * * ssBentNormal += weight * ssOmega; * } * * float dTheta = (theta_Front - theta_Back) / STEPS_COUNT; * ssBentNormal *= dTheta; * * float3 csBentNormal = new float3( ssBentNormal.x * csDirection, ssBentNormal.y ); * //*/ //* // Analytical integration float cosTheta0 = maxCosTheta_Front; float cosTheta1 = maxCosTheta_Back; float sinTheta0 = Mathf.Sqrt(1.0f - cosTheta0 * cosTheta0); float sinTheta1 = Mathf.Sqrt(1.0f - cosTheta1 * cosTheta1); float cosTheta0_3 = cosTheta0 * cosTheta0 * cosTheta0; float cosTheta1_3 = cosTheta1 * cosTheta1 * cosTheta1; float sinTheta0_3 = sinTheta0 * sinTheta0 * sinTheta0; float sinTheta1_3 = sinTheta1 * sinTheta1 * sinTheta1; float averageX = ssNormal.x * (cosTheta0_3 + cosTheta1_3 - 3.0f * (cosTheta0 + cosTheta1) + 4.0f) + ssNormal.y * (sinTheta0_3 - sinTheta1_3); float averageY = ssNormal.x * (sinTheta0_3 - sinTheta1_3) + ssNormal.y * (2.0f - cosTheta0_3 - cosTheta1_3); float3 csBentNormal = new float3(averageX * csDirection, averageY); //*/ // DON'T NORMALIZE RESULT!! //csBentNormal.Normalize(); csAverageBentNormal += csBentNormal; } float3 csFinalNormal = csAverageBentNormal.Normalized; }
float3 GatherIrradiance(float2 _csDirection, float4x4 _localCamera2World, float3 _csNormal, float _stepSize_meters, uint _stepsCount, float _Z0, float3 _centralRadiance, out float3 _csBentNormal, out float2 _coneAngles, out float _AO, ref float4 _DEBUG) { // Pre-compute factors for the integrals float2 integralFactors_Front = ComputeIntegralFactors(_csDirection, _csNormal); float2 integralFactors_Back = ComputeIntegralFactors(-_csDirection, _csNormal); // Compute initial cos(angle) for front & back horizons // We do that by projecting the screen-space direction ssDirection onto the tangent plane given by the normal // then the cosine of the angle from the Z axis is simply given by the Pythagorean theorem: // P // N\ |Z -*- // \ | --- ^ // \| --- | // --- *..........>+ ssDirection // --- // float hitDistance_Front = -_csDirection.Dot(_csNormal.xy) / _csNormal.z; float maxCosTheta_Front = hitDistance_Front / Mathf.Sqrt(hitDistance_Front * hitDistance_Front + 1.0f); float maxCosTheta_Back = -maxCosTheta_Front; // Back cosine is simply the mirror value // Gather irradiance from front & back directions while updating the horizon angles at the same time float3 sumRadiance = float3.Zero; float3 previousRadiance_Front = _centralRadiance; float3 previousRadianceBack = _centralRadiance; //* // float2 radius = float2.Zero; float2 csStep = _stepSize_meters * _csDirection; float2 csPosition_Front = float2.Zero; float2 csPosition_Back = float2.Zero; for (uint stepIndex = 0; stepIndex < _stepsCount; stepIndex++) { // radius += _stepSize_meters; csPosition_Front += csStep; csPosition_Back -= csStep; // float2 mipLevel = ComputeMipLevel( radius, _radialStepSizes ); float2 mipLevel = float2.Zero; sumRadiance += SampleIrradiance(csPosition_Front, _localCamera2World, _Z0, mipLevel, integralFactors_Front, ref previousRadiance_Front, ref maxCosTheta_Front); sumRadiance += SampleIrradiance(csPosition_Back, _localCamera2World, _Z0, mipLevel, integralFactors_Back, ref previousRadianceBack, ref maxCosTheta_Back); } //*/ // Accumulate bent normal direction by rebuilding and averaging the front & back horizon vectors float2 ssNormal = new float2(_csNormal.xy.Dot(_csDirection), _csNormal.z); #if USE_NUMERICAL_INTEGRATION // Half brute force where we perform the integration numerically as a sum... // const uint STEPS = 256; float thetaFront = Mathf.Acos(maxCosTheta_Front); float thetaBack = -Mathf.Acos(maxCosTheta_Back); float2 ssBentNormal = float2.Zero; for (uint i = 0; i < STEPS; i++) { float theta = Mathf.Lerp(thetaBack, thetaFront, (i + 0.5f) / STEPS); float sinTheta = Mathf.Sin(theta), cosTheta = Mathf.Cos(theta); float2 ssOmega = new float2(sinTheta, cosTheta); float cosAlpha = Mathf.Saturate(ssOmega.Dot(ssNormal)); cosAlpha = 1.0f; float weight = cosAlpha * Mathf.Abs(sinTheta); // cos(alpha) * sin(theta).dTheta (be very careful to take abs(sin(theta)) because our theta crosses the pole and becomes negative here!) ssBentNormal += weight * ssOmega; } float dTheta = (thetaFront - thetaBack) / STEPS; ssBentNormal *= dTheta; _csBentNormal = new float3(ssBentNormal.x * _csDirection, ssBentNormal.y); #else // Analytical solution // Accounts for dot product with normal // float cosTheta0 = maxCosTheta_Front; // float cosTheta1 = maxCosTheta_Back; // float sinTheta0 = Mathf.Sqrt( 1.0f - cosTheta0*cosTheta0 ); // float sinTheta1 = Mathf.Sqrt( 1.0f - cosTheta1*cosTheta1 ); // // float cosTheta0_3 = cosTheta0*cosTheta0*cosTheta0; // float cosTheta1_3 = cosTheta1*cosTheta1*cosTheta1; // float sinTheta0_3 = sinTheta0*sinTheta0*sinTheta0; // float sinTheta1_3 = sinTheta1*sinTheta1*sinTheta1; // // float averageX = ssNormal.x * (cosTheta0_3 + cosTheta1_3 - 3.0f * (cosTheta0 + cosTheta1) + 4.0f) // + ssNormal.y * (sinTheta0_3 - sinTheta1_3); // // float averageY = ssNormal.x * (sinTheta0_3 - sinTheta1_3) // + ssNormal.y * (2.0f - cosTheta0_3 - cosTheta1_3); // // Raw integration, without dot product with normal float theta0 = -Mathf.Acos(maxCosTheta_Back); float theta1 = Mathf.Acos(maxCosTheta_Front); float averageX = theta1 + theta0 - Mathf.Sin(theta0) * Mathf.Cos(theta0) - Mathf.Sin(theta1) * Mathf.Cos(theta1); float averageY = 2.0f - Mathf.Cos(theta0) * Mathf.Cos(theta0) - Mathf.Cos(theta1) * Mathf.Cos(theta1); _csBentNormal = new float3(averageX * _csDirection, averageY); #endif // DON'T NORMALIZE RESULT!! // _csBentNormal = _csBentNormal.Normalized; // Compute cone angles float3 csNormalizedBentNormal = _csBentNormal.Normalized; float3 csHorizon_Front = new float3(Mathf.Sqrt(1.0f - maxCosTheta_Front * maxCosTheta_Front) * _csDirection, maxCosTheta_Front); float3 csHorizon_Back = new float3(-Mathf.Sqrt(1.0f - maxCosTheta_Back * maxCosTheta_Back) * _csDirection, maxCosTheta_Back); #if USE_FAST_ACOS _coneAngles.x = FastPosAcos(saturate(dot(csNormalizedBentNormal, csHorizon_Front))); _coneAngles.y = FastPosAcos(saturate(dot(csNormalizedBentNormal, csHorizon_Back))); #else _coneAngles.x = Mathf.Acos(Mathf.Saturate(csNormalizedBentNormal.Dot(csHorizon_Front))); _coneAngles.y = Mathf.Acos(Mathf.Saturate(csNormalizedBentNormal.Dot(csHorizon_Back))); #endif // Compute AO using equation 11 of the paper _AO = 2.0f - maxCosTheta_Back - maxCosTheta_Front; // _AO = 0.0f; // // float theta0 = -Mathf.Acos( maxCosTheta_Back ); // // float theta1 = Mathf.Acos( maxCosTheta_Front ); // for ( uint i=0; i < 256; i++ ) { // float theta = Mathf.Lerp( theta0, theta1, (i+0.5f) / 256 ); // _AO += Math.Abs( Mathf.Sin( theta ) ); // } // _AO *= (theta1 - theta0) / 256.0f; return(sumRadiance); }