Exemplo n.º 1
0
    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();
    }