private void GenerateSamples(int amount = 10) { meshCollider.sharedMesh = currentPointCloud.mesh.mesh; var camRay = camera.ScreenPointToRay(Input.mousePosition); var dir = camRay.direction; var(dist, _) = PolySurface.RayMarch(coefficients, camRay.origin, dir, 1000.0f, surface.Center); var hit = camRay.origin + (dist + 0.001f) * camRay.direction; hit = currentCenter - currentNormal * 0.001f; dir = -currentNormal; int count = 0; while (count < amount) { int stepCount = 0; var exit = TestDataGenerator.SimulatePath( samplesRandom, surface.Coefficients, hit, dir, surface.Center, surface.g, surface.SigmaSReduced, surface.sigmaT, distScale, null, ref stepCount); if (exit != null) { count += 1; var ray = new Ray(exit.Value, -PolySurface.GetNormalAt(surface.Coefficients, exit.Value, surface.Center)); if (autoProjectSamples && meshCollider.Raycast(ray, out var h, float.MaxValue)) { var go = GameObject.Instantiate(samplePointPrefab, transform); go.transform.position = h.point; samples.Add(go); //samples.Add(new MeshDBSample //{ // position = h.point, // gradient = -ray.direction, //}); } else { var go = GameObject.Instantiate(samplePointPrefab, transform); go.transform.position = exit.Value; samples.Add(go); //samples.Add(new MeshDBSample //{ // position = exit.Value, // gradient = -ray.direction //}); } } }
public static Vector3?SimulatePath(System.Random random, float[] coefficients, Vector3 origin, Vector3 direction, Vector3 center, float g, float sigmaSRed, float sigmaA, float distScale, Action <PathPoint> onInteraction, ref int stepCount) { direction = direction.normalized; float maxDist = float.MaxValue; maxDist = GetScatterDistance(random, sigmaSRed) * distScale; float throughput = 1.0f; for (int i = 0; i < 500; i++) { stepCount += 1; var(dist, inside) = PolySurface.RayMarch(coefficients, origin, direction, maxDist, center); var newPoint = origin + direction * dist; var point = new PathPoint { position = newPoint, direction = direction }; var normal = PolySurface.GetNormalAt(coefficients, newPoint, center); origin = newPoint - normal * PolySurface.SURF_DIST * 2; if (!inside) { // exited surface return(newPoint); } direction = GetRandomNewDirection(random, g, direction); // still inside, set random direction and maxDist //if (g == 0) { // direction = OnUnitSphere(random); //} else { // float rand = RandomRange(random, 0.0f, 1.0f); // float cosAngle = InvIntPhaseFunction(rand, g); // float theta = Mathf.Acos(cosAngle); // float d = 1 / Mathf.Tan(theta); // // Debug.Log($"rand: {rand}, cosAngle: {cosAngle}, theta: {theta}, d: {d}"); // if (d == float.PositiveInfinity) { // // do nothing // //direction = direction; // } else if (d == float.NegativeInfinity) { // direction = -direction; // } else { // var right = GetPerpendicular(direction).normalized; // var up = Vector3.Cross(right, direction).normalized; // var uv = InsideUnitCircle(random); // point.uv = uv; // point.right = right; // point.up = up; // // Debug.Log($"right: {right}, up: {up}, uv: {uv}"); // direction = uv.x * right + uv.y * up + d * direction; // direction = direction.normalized; // } //} // Debug.Log($"direction: {direction}"); maxDist = GetScatterDistance(random, sigmaSRed) * distScale; point.direction = point.direction * Vector3.Dot(point.direction, direction * maxDist); onInteraction?.Invoke(point); float transmission = Mathf.Exp(-sigmaA * maxDist); throughput = throughput * transmission; // russian roulette if (i > 5) { if ((float)random.NextDouble() > throughput) { return(null); } throughput = 1; } } // reached max steps, ray counts as absorbed return(null); }
private void Update() { if (Input.GetKeyDown(KeyCode.F1)) { SaveVector("currentCenter", currentCenter); SaveVector("currentNormal", currentNormal); PlayerPrefs.Save(); } if (Input.GetKeyDown(KeyCode.F2)) { currentCenter = LoadVector("currentCenter"); currentNormal = LoadVector("currentNormal"); hitLocation.transform.position = currentCenter; } if (Input.GetKeyDown(KeyCode.F4)) { if (modelStddev == 1) { modelStddev = 20; } else { modelStddev = 1; } } if (Input.GetKeyDown(KeyCode.F5)) { foreach (var go in samples) { GameObject.Destroy(go); } samples.Clear(); GenerateSamples(samplesToGenerate); } if (Input.GetKeyDown(KeyCode.F6)) { modelName = modelNames[currentModelIndex]; currentModelIndex = (currentModelIndex + 1) % modelNames.Length; } if (Input.GetKeyDown(KeyCode.F7)) { modelVersion = modelVersions[currentModelVersionIndex].ToString(); currentModelVersionIndex = (currentModelVersionIndex + 1) % modelVersions.Length; } if (Input.GetKeyDown(KeyCode.F8)) { foreach (var go in samples) { GameObject.Destroy(go); } samples.Clear(); GetSamplesFromModelCurrentPos(); } surface.renderer.enabled = renderPolySurface; var ctrl = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); var shift = Input.GetKey(KeyCode.LeftShift); pointCloudMaterial.SetFloat("_WeightScale", weightScale); pointCloudMaterial.SetFloat("_WeightFactor", weightFactor); if (currentPointCloud != null) { currentVertices = currentPointCloud.vertices; if (ctrl && Input.GetMouseButtonDown(1)) { var ray = camera.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out var hit)) { camera.transform.parent.position = hit.point; } else { camera.transform.parent.position = Vector3.zero; } } if (!ctrl && Input.GetMouseButton(1)) { var ray = camera.ScreenPointToRay(Input.mousePosition); meshCollider.sharedMesh = currentPointCloud.mesh.mesh; if (meshCollider.Raycast(ray, out var hit, float.MaxValue)) { //Debug.Log($"({ray.origin.x}, {ray.origin.y}, {ray.origin.z}), ({ray.direction.x}, {ray.direction.y}, {ray.direction.z})"); hitLocation.position = hit.point; currentCenter = hit.point; currentNormal = hit.normal; currentVertices = currentPointCloud.vertices; pointCloudMaterial.SetVector("center", hit.point); // if (currentPointCloud.mesh.coefficients != null) { var uvw = hit.barycentricCoordinate; var coefficientsArray = currentPointCloud.mesh.coefficients; var faces = currentPointCloud.mesh.faces; float[] c0 = coefficientsArray[faces[hit.triangleIndex * 3 + 0]]; float[] c1 = coefficientsArray[faces[hit.triangleIndex * 3 + 1]]; float[] c2 = coefficientsArray[faces[hit.triangleIndex * 3 + 2]]; float[] coefficients = new float[20]; for (int i = 0; i < 20; i++) { coefficients[i] = uvw.x * c0[i] + uvw.y * c1[i] + uvw.z * c2[i]; } surface.SetCenter(currentCenter); surface.SetCoefficients(coefficients); } } } } if (ctrl && Input.GetMouseButtonDown(0)) { var point = camera.ScreenPointToRay(Input.mousePosition); var random = new System.Random(); currentPath.Clear(); currentPath.Add(new PathPoint { position = point.origin, direction = point.direction }); var(dist, _) = PolySurface.RayMarch(coefficients, point.origin, point.direction, 1000.0f, surface.Center); var hit = point.origin + (dist + 0.001f) * point.direction; int stepCount = 0; var exit = TestDataGenerator.SimulatePath( random, surface.Coefficients, hit, point.direction, surface.Center, surface.g, surface.SigmaSReduced, surface.sigmaT, distScale, (p) => currentPath.Add(p), ref stepCount); if (exit != null) { currentPath.Add(new PathPoint { position = exit.Value, direction = (exit.Value - currentPath.Last().position).normalized, }); currentPath.Add(new PathPoint { position = currentPath.Last().position + currentPath.Last().direction * 10, }); } } if (process != null && process.HasExited) { Debug.Log("generator done"); foreach (var go in samples) { GameObject.Destroy(go); } samples.Clear(); foreach (var sample in samplesTemp) { var go = GameObject.Instantiate(samplePointPrefab, transform); go.transform.position = sample.position; samples.Add(go); } samplesTemp.Clear(); process = null; } // generate samples if (shift && Input.GetMouseButtonDown(0)) { foreach (var go in samples) { GameObject.Destroy(go); } samples.Clear(); if (sampleSource != SampleSource.Generate && process == null) { var ray = camera.ScreenPointToRay(Input.mousePosition); if (meshCollider.Raycast(ray, out var hit, float.MaxValue)) { var dir = ray.direction; if (incidentIsNormal) { dir = -hit.normal; } GetSamplesFromModel(hit.point, hit.normal, dir); } } } if (shift && Input.GetMouseButton(0)) { if (sampleSource == SampleSource.Generate) { GenerateSamples(); } } for (int i = 0; i < currentPath.Count; i++) { var p = currentPath[i]; if (RenderPathAxis) { if (p.uv != null) { var vec = p.right.Value * p.uv.Value.x + p.up.Value * p.uv.Value.y; Debug.DrawRay(p.position, vec * PathAxisLen, Color.yellow, 0, false); } if (p.right != null) { Debug.DrawRay(p.position, p.right.Value * PathAxisLen, Color.red, 0, false); } if (p.up != null) { Debug.DrawRay(p.position, p.up.Value * PathAxisLen, Color.green, 0, false); } Debug.DrawRay(p.position, p.direction, Color.blue, 0, false); } if (RenderPath && i < currentPath.Count - 1) { var col = Color.white; if (i == 0) { col = Color.cyan; } else if (i == currentPath.Count - 2) { col = Color.magenta; } Debug.DrawLine(p.position, currentPath[i + 1].position, col, 0, false); } } }
public static void GenerateTestData( Mesh mesh, int minimumPointCount, float pointsPerAreaMultiplier, float weightFactor, float weightScale, int maxVertexCount, int randomVertexCount, float randomVertexWeight, float largeCoefficientPenalty, int randomVertexSeed, int sampleCount, string outputPath, float distScale, int samplesPerPoint, Action <float> callback) { if (trainThread != null) { return; } if (meshCollider == null) { var tempObject = new GameObject(); meshCollider = tempObject.AddComponent <MeshCollider>(); } meshCollider.sharedMesh = mesh; // get random point on surface + normal Vector3[] meshVertices = mesh.vertices; Vector3[] meshNormals = mesh.normals; int[] meshTriangleIndices = mesh.triangles; // generate point cloud var random = new System.Random(); var pointCloud = ComputePointCloud(random, meshVertices, meshNormals, meshTriangleIndices, minimumPointCount, pointsPerAreaMultiplier, 0.1f); trainThread = new Thread(() => { stopwatch.Restart(); var samples = new List <Sample>(sampleCount * samplesPerPoint); var samplesTemp = new List <Vector3>(samplesPerPoint); AvgStats stats = new AvgStats(); for (int i = 0; i < sampleCount;) { callback((float)i / sampleCount); // generate random point and normal on surface int randomTriangleIndex = (int)RandomRange(random, 0, meshTriangleIndices.Length / 3) * 3; var(b0, b1, b2) = GetRandomBarycentric(random); Vector3 randomSurfacePoint = b0 * meshVertices[meshTriangleIndices[randomTriangleIndex + 0]] + b1 * meshVertices[meshTriangleIndices[randomTriangleIndex + 1]] + b2 * meshVertices[meshTriangleIndices[randomTriangleIndex + 2]]; Vector3 randomSurfacePointNormal = b0 * meshNormals[meshTriangleIndices[randomTriangleIndex + 0]] + b1 * meshNormals[meshTriangleIndices[randomTriangleIndex + 1]] + b2 * meshNormals[meshTriangleIndices[randomTriangleIndex + 2]]; randomSurfacePointNormal = randomSurfacePointNormal.normalized; Vector3 normalCSRight = GetPerpendicular(randomSurfacePointNormal); Vector3 normalCSUp = Vector3.Cross(randomSurfacePointNormal, normalCSRight); Vector3 randomNormalTemp = OnUnitSphere(random); randomNormalTemp.z = Mathf.Abs(randomNormalTemp.z); randomNormalTemp = randomNormalTemp.x * normalCSRight + randomNormalTemp.y * normalCSUp + randomNormalTemp.z * randomSurfacePointNormal; // always send rays in direction of normal to simplify everything //randomNormalTemp = randomSurfacePointNormal; Vector3 direction = -randomNormalTemp.normalized; Vector3 origin = randomSurfacePoint - direction * 0.1f; //Debug.Log($"({origin.x}f, {origin.y}f, {origin.z}f), ({direction.x}f, {direction.y}f, {direction.z}f)"); // generate random surface properties float g = RandomRange(random, 0.1f, 0.95f); float ior = RandomRange(random, 1.0f, 1.5f); float sigmaS = RandomRange(random, 0.5f, 50.0f); float sigmaT = RandomRange(random, sigmaS + 0.1f, 51.0f); // test if fixing these values is better sigmaS = 30; sigmaT = 31; g = 0.9f; ior = 1.0f; float sigmaA = sigmaT - sigmaS; float sigmaSRed = (1 - g) * sigmaS; float effectiveAlbedo = 0.0f; float standardDeviation = 0.0f; { float sigmaTred = sigmaA + sigmaSRed; float alphaRed = sigmaSRed / sigmaTred; float e8 = Mathf.Pow((float)Math.E, 8); effectiveAlbedo = 1 - (0.125f * Mathf.Log(e8 + alphaRed * (1 - e8))); float mad = MAD(g, alphaRed); standardDeviation = 2 * mad / sigmaTred; } stats.effectiveAlbedo += effectiveAlbedo; stats.g += g; stats.ior += ior; // calculate coefficients for random point on surfice float[] coefficients = TestDataGenerator.CalculateCoefficients( random, pointCloud, randomSurfacePoint, standardDeviation, weightFactor, weightScale, maxVertexCount, randomVertexCount, randomVertexWeight, largeCoefficientPenalty); var normalToZ = new Frame(randomSurfacePointNormal); var zToDirection = new Frame(-direction).Invert(); int absorbtionSum = 0; int absorbtionTestCount = samplesPerPoint; var(dist, inside) = PolySurface.RayMarch(coefficients, origin, direction, 1000.0f, randomSurfacePoint); var hit = origin + (dist + 0.001f) * direction; samplesTemp.Clear(); for (int k = 0; k < absorbtionTestCount; k++) { int stepCount = 0; var p = SimulatePath(random, coefficients, hit, direction, randomSurfacePoint, g, sigmaSRed, sigmaT, distScale, null, ref stepCount); stats.steps += (double)stepCount; if (p == null) { absorbtionSum += 1; } if (p != null) { samplesTemp.Add(p.Value - randomSurfacePoint); } callback(((float)i + (float)k / (float)absorbtionTestCount) / sampleCount); } float absorbtion = (float)absorbtionSum / (float)absorbtionTestCount; // rotate from normal space to world space, so the normal aligns with the z-axis RotatePolynomial(coefficients, normalToZ.x, normalToZ.y, normalToZ.z); // rotate from world space to direction space, so the direction aligns with the z-axis RotatePolynomial(coefficients, zToDirection.x, zToDirection.y, zToDirection.z); var features = coefficients.Concat(new float[] { effectiveAlbedo, g, ior }).ToArray(); foreach (var _exitPoint in samplesTemp) { var exitPoint = _exitPoint; stats.pos.x += exitPoint.x / (float)absorbtionTestCount; stats.pos.y += exitPoint.y / (float)absorbtionTestCount; stats.pos.z += exitPoint.z / (float)absorbtionTestCount; //var p1 = PolySurface.EvaluateAt(coefficients, exitPoint, Vector3.zero); //var n1 = PolySurface.GetNormalAt(coefficients, exitPoint, Vector3.zero); // rotate from normal space to world space, so the normal aligns with the z-axis //var worldToNormal1 = new Frame(randomSurfacePointNormal); //var normalToZ = new Frame(PolySurface.GetNormalAt(coefficients, Vector3.zero, Vector3.zero)); exitPoint = normalToZ.ToMatrix().transpose *exitPoint; //var p2 = PolySurface.EvaluateAt(coefficients, exitPoint, Vector3.zero); //var n2 = PolySurface.GetNormalAt(coefficients, exitPoint, Vector3.zero); //var n1_ = normalToZ.ToMatrix().transpose * n1; // rotate from world space to direction space, so the direction aligns with the z-axis exitPoint = zToDirection.ToMatrix().transpose *exitPoint; //var p3 = PolySurface.EvaluateAt(coefficients, exitPoint, Vector3.zero); //var n3 = PolySurface.GetNormalAt(coefficients, exitPoint, Vector3.zero); //var n2_ = zToDirection.ToMatrix().transpose * n2; samples.Add(new Sample { features = features, out_pos = new float[] { exitPoint.x, exitPoint.y, exitPoint.z }, absorbtion = absorbtion, point = new vec3(randomSurfacePoint), normal = new vec3(randomSurfacePointNormal) }); } i++; } callback(1); var now = DateTime.Now; using (var file = File.OpenWrite($"../train_data/samples_{now.Year}-{now.Month}-{now.Day}--{now.Hour}-{now.Minute}-{now.Second}.json")) { JsonSerializer serializer = new JsonSerializer(); serializer.Formatting = Formatting.Indented; var writer = new JsonTextWriter(new StreamWriter(new BufferedStream(file))); serializer.Serialize(writer, samples); writer.Flush(); file.Flush(); } stats.ComputeAvg(sampleCount); var avgJsonString = JsonConvert.SerializeObject(stats, Formatting.Indented); File.WriteAllText($"../train_data/stats_{now.Year}-{now.Month}-{now.Day}--{now.Hour}-{now.Minute}-{now.Second}.json", avgJsonString); elapsedTime = stopwatch.Elapsed; //Debug.Log($"Generating {sampleCount} samples took {time}"); trainThread = null; } ); trainThread.Start(); }