public PdfRatio(BidirPathPdfs pdfs, float radius, int numPaths, JendersieFootprint parent) { this.numPaths = numPaths; this.parent = parent; int numSurfaceVertices = pdfs.PdfsCameraToLight.Length - 1; cameraToLight = new float[numSurfaceVertices]; lightToCamera = new float[numSurfaceVertices]; float offset = 0.01f; // gather camera "footprint" float sum = 1 / (offset + MathF.Sqrt(pdfs.PdfsCameraToLight[0])); cameraToLight[0] = sum; for (int i = 1; i < numSurfaceVertices; ++i) { float next = pdfs.PdfsCameraToLight[i]; sum += 1 / (offset + MathF.Sqrt(next)); cameraToLight[i] = sum; } // gather light "footprint" sum = 1 / (offset + MathF.Sqrt(pdfs.PdfsLightToCamera[numSurfaceVertices - 1])); lightToCamera[numSurfaceVertices - 1] = sum; for (int i = numSurfaceVertices - 2; i >= 0; --i) { float next = pdfs.PdfsLightToCamera[i]; sum += 1 / (offset + MathF.Sqrt(next)); lightToCamera[i] = sum; } }
public PdfRatio(BidirPathPdfs pdfs, float radius, int numPaths, PdfRatioVcm parent, float distToCam) { this.numPaths = numPaths; this.parent = parent; if (parent.RadiusInitializer == null) { Debug.Assert(parent.UseUpperBound); cameraToLight = null; lightToCamera = null; return; } radius = parent.RadiusInitializer.ComputeRadius(parent.scene.Radius, pdfs.PdfsCameraToLight[0], distToCam); int numSurfaceVertices = pdfs.PdfsCameraToLight.Length - 1; cameraToLight = new float[numSurfaceVertices]; lightToCamera = new float[numSurfaceVertices]; float acceptArea = radius * radius * MathF.PI; // Gather camera probability float product = 1.0f; for (int i = 0; i < numSurfaceVertices; ++i) { float next = pdfs.PdfsCameraToLight[i] * acceptArea; next = MathF.Min(next, 1.0f); product *= next; cameraToLight[i] = product; } // Gather light probability product = 1.0f; for (int i = numSurfaceVertices - 1; i >= 0; --i) { float next = pdfs.PdfsLightToCamera[i] * acceptArea; if (i == numSurfaceVertices - 1) { next *= acceptArea; // TODO unless environment map! (in that case: different radius approach) } next = MathF.Min(next, 1.0f); product *= next; lightToCamera[i] = product; } }
public override float EmitterHitMis(CameraPath cameraPath, float pdfEmit, float pdfNextEvent) { int numPdfs = cameraPath.Vertices.Count; int lastCameraVertexIdx = numPdfs - 1; Span <float> camToLight = stackalloc float[numPdfs]; Span <float> lightToCam = stackalloc float[numPdfs]; if (numPdfs == 1) { return(1.0f); // sole technique for rendering directly visible lights. } var pathPdfs = new BidirPathPdfs(LightPaths.PathCache, lightToCam, camToLight); pathPdfs.GatherCameraPdfs(cameraPath, lastCameraVertexIdx); pathPdfs.PdfsLightToCamera[^ 2] = pdfEmit;
public float ComputeNeeFactor(BidirPathPdfs pdfs, PathVertex?lightVertex, int numPdfs, int lastCameraVertexIdx, float pdfNextEvent, float distToCam) { if (numPdfs <= 2) { return(1); // Direct illumination has no correlation } // Set a radius for the probability approximations. float radius = RadiusInitializer.ComputeRadius(scene.Radius, pdfs.PdfsCameraToLight[0], distToCam); float acceptArea = radius * radius * MathF.PI; // Compute the camera path determinism up until the last vertex int numSurfaceVertices = pdfs.PdfsCameraToLight.Length - 1; float cameraProbability = 1.0f; for (int i = 0; i < numSurfaceVertices; ++i) { float next = pdfs.PdfsCameraToLight[i] * acceptArea; next = MathF.Min(next, 1.0f); cameraProbability *= next; } // Retrieve the next event pdf from the second to last light path vertex (if it exists) while (lightVertex?.Depth > 2) { var ancestor = lightPaths.PathCache[lightVertex.Value.PathId, lightVertex.Value.AncestorId]; lightVertex = ancestor; } if (lightVertex?.Depth == 2) { pdfNextEvent = lightVertex.Value.PdfNextEventAncestor; } float nextEventProbability = acceptArea * pdfNextEvent / NumShadowRays; nextEventProbability = MathF.Min(nextEventProbability, 1.0f); float denom = cameraProbability + nextEventProbability - cameraProbability * nextEventProbability; float factor = cameraProbability / denom; // Make sure that we only ever increase the weight, going below the provable upper bound // will always hurt the outcome! factor = MathF.Max(factor, 1.0f / NumShadowRays); // Compute the new joint pdf of BSDF and next event sampling float pdfBsdf = pdfs.PdfsCameraToLight[^ 1] - pdfNextEvent;
public override float MergeMis(CameraPath cameraPath, PathVertex lightVertex, float pdfCameraReverse, float pdfLightReverse, float pdfNextEvent) { int numPdfs = cameraPath.Vertices.Count + lightVertex.Depth; int lastCameraVertexIdx = cameraPath.Vertices.Count - 1; Span <float> camToLight = stackalloc float[numPdfs]; Span <float> lightToCam = stackalloc float[numPdfs]; var pathPdfs = new BidirPathPdfs(lightPaths.PathCache, lightToCam, camToLight); pathPdfs.GatherCameraPdfs(cameraPath, lastCameraVertexIdx); pathPdfs.GatherLightPdfs(lightVertex, lastCameraVertexIdx - 1, numPdfs); // Set the pdf values that are unique to this combination of paths if (lastCameraVertexIdx > 0) // only if this is not the primary hit point { pathPdfs.PdfsLightToCamera[lastCameraVertexIdx - 1] = pdfCameraReverse; } pathPdfs.PdfsLightToCamera[lastCameraVertexIdx] = lightVertex.PdfFromAncestor; pathPdfs.PdfsCameraToLight[lastCameraVertexIdx] = cameraPath.Vertices[^ 1].PdfFromAncestor;
public float ComputeNeeFactor(BidirPathPdfs pdfs, PathVertex?lightVertex, int numPdfs, int lastCameraVertexIdx, float pdfNextEvent, Vector2 pixel) { if (numPdfs <= 2) { return(1); // Direct illumination has no correlation } float factor = varianceFactors.Get(numPdfs - 2, pixel); // Retrieve the next event pdf from the second to last light path vertex (if it exists) while (lightVertex?.Depth > 2) { var ancestor = lightPaths.PathCache[lightVertex.Value.PathId, lightVertex.Value.AncestorId]; lightVertex = ancestor; } if (lightVertex?.Depth == 2) { pdfNextEvent = lightVertex.Value.PdfNextEventAncestor; } // Compute the new joint pdf of BSDF and next event sampling float pdfBsdf = pdfs.PdfsCameraToLight[^ 1] - pdfNextEvent;