Esempio n. 1
0
        public void Emission_ShouldBeOneSided()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            },
                shadingNormals: new Vector3[] {
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0)
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var dummyHit = new SurfacePoint {
                Mesh = mesh
            };

            var r1 = emitter.EmittedRadiance(dummyHit, new Vector3(0, 1, 1));
            var r2 = emitter.EmittedRadiance(dummyHit, new Vector3(0, -1, 1));

            Assert.True(r1.R > 0);
            Assert.Equal(0, r2.R);
        }
Esempio n. 2
0
        public void EmittedRays_SampleInverse()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            },
                shadingNormals: new Vector3[] {
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0)
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var sample = emitter.SampleRay(new Vector2(0.3f, 0.8f), new Vector2(0.56f, 0.03f));

            var(posP, dirP) = emitter.SampleRayInverse(sample.Point, sample.Direction);

            Assert.Equal(0.3f, posP.X, 3);
            Assert.Equal(0.8f, posP.Y, 3);
            Assert.Equal(0.56f, dirP.X, 3);
            Assert.Equal(0.03f, dirP.Y, 3);
        }
Esempio n. 3
0
        public void EmittedRays_Sidedness_ShouldBeNegative()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            },
                shadingNormals: new Vector3[] {
                new Vector3(0, -1, 0),
                new Vector3(0, -1, 0),
                new Vector3(0, -1, 0),
                new Vector3(0, -1, 0)
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var sample = emitter.SampleRay(new Vector2(0.3f, 0.8f), new Vector2(0.56f, 0.03f));

            Assert.True(sample.Direction.Y < 0);
        }
Esempio n. 4
0
        public void EmittedRays_Pdf_ShouldBeCosHemisphere()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            },
                shadingNormals: new Vector3[] {
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0)
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var sample = emitter.SampleRay(new Vector2(0.3f, 0.8f), new Vector2(0.56f, 0.03f));

            float c = Vector3.Dot(sample.Direction, new Vector3(0, 1, 0));

            Assert.Equal(0.25f * c / MathF.PI, sample.Pdf);
        }
Esempio n. 5
0
        public void EmittedRays_Pdf_ShouldBeOneSided()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            },
                shadingNormals: new Vector3[] {
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0)
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var dummyHit = new SurfacePoint {
                Mesh = mesh
            };

            var p1 = emitter.PdfRay(dummyHit, new Vector3(0, 1, 1));
            var p2 = emitter.PdfRay(dummyHit, new Vector3(0, -1, 1));

            Assert.True(p1 > 0);
            Assert.Equal(0, p2);
        }
Esempio n. 6
0
        public void EmittedRays_ShouldHaveOffset()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var sample = emitter.SampleRay(new Vector2(0.3f, 0.8f), new Vector2(0.56f, 0.03f));

            Assert.True(sample.Point.ErrorOffset > 0);
        }
Esempio n. 7
0
        public void EmittedRays_Weight_ShouldBeRadianceOverPdf()
        {
            var mesh = new Mesh(
                new Vector3[] {
                new Vector3(-1, 10, -1),
                new Vector3(1, 10, -1),
                new Vector3(1, 10, 1),
                new Vector3(-1, 10, 1)
            }, new int[] {
                0, 1, 2,
                0, 2, 3
            },
                shadingNormals: new Vector3[] {
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0),
                new Vector3(0, 1, 0)
            }
                );
            var emitter = new DiffuseEmitter(mesh, RgbColor.White);

            var sample = emitter.SampleRay(new Vector2(0.3f, 0.8f), new Vector2(0.56f, 0.03f));

            float c           = Vector3.Dot(sample.Direction, new Vector3(0, 1, 0));
            float expectedPdf = 0.25f * c / MathF.PI;

            Assert.Equal(expectedPdf, sample.Pdf);

            var expectedWeight =
                emitter.EmittedRadiance(sample.Point, sample.Direction) * c
                / expectedPdf;

            Assert.Equal(expectedWeight.R, sample.Weight.R);
            Assert.Equal(expectedWeight.G, sample.Weight.G);
            Assert.Equal(expectedWeight.B, sample.Weight.B);
        }
        public override Scene MakeScene()
        {
            var scene = new Scene();

            // Create the camera
            var worldToCamera = Matrix4x4.CreateLookAt(-1 * Vector3.UnitZ,
                                                       Vector3.Zero,
                                                       Vector3.UnitY);

            scene.Camera = new PerspectiveCamera(worldToCamera, 45);

            // Create the two transmissive planes in-between
            // first plane
            var mesh = new Mesh(new Vector3[] {
                new Vector3(-1, -1, 0),
                new Vector3(1, -1, 0),
                new Vector3(1, 1, 0),
                new Vector3(-1, 1, 0),
            }, new int[] {
                0, 1, 2,
                0, 2, 3,
            });

            mesh.Material = new GenericMaterial(new GenericMaterial.Parameters {
                BaseColor             = new TextureRgb(RgbColor.White),
                Roughness             = new TextureMono(0.5f),
                SpecularTransmittance = 1,
                Thin = true,
                DiffuseTransmittance = 1,
            });
            scene.Meshes.Add(mesh);
            // second plane
            mesh = new Mesh(new Vector3[] {
                new Vector3(-1, -1, 1),
                new Vector3(1, -1, 1),
                new Vector3(1, 1, 1),
                new Vector3(-1, 1, 1),
            }, new int[] {
                0, 1, 2,
                0, 2, 3,
            });
            mesh.Material = new GenericMaterial(new GenericMaterial.Parameters {
                BaseColor            = new TextureRgb(RgbColor.White),
                Roughness            = new TextureMono(0.5f),
                Thin                 = true,
                DiffuseTransmittance = 1,
            });
            scene.Meshes.Add(mesh);

            // Create the light source
            var lightArea = 0.1f;
            var pos       = MathF.Sqrt(lightArea) / 2;
            var lightMesh = new Mesh(new Vector3[] {
                new Vector3(-pos, -pos, 1 + 10),
                new Vector3(pos, -pos, 1 + 10),
                new Vector3(pos, pos, 1 + 10),
                new Vector3(-pos, pos, 1 + 10),
            }, new int[] {
                0, 1, 2,
                0, 2, 3,
            }, new Vector3[] {
                -Vector3.UnitZ,
                -Vector3.UnitZ,
                -Vector3.UnitZ,
                -Vector3.UnitZ,
            });

            lightMesh.Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
                BaseColor = new TextureRgb(RgbColor.Black)
            });
            scene.Meshes.Add(lightMesh);
            var lightPower = 500;
            var radiance   = lightPower / (MathF.PI * lightArea);
            var emitter    = new DiffuseEmitter(lightMesh, RgbColor.White * radiance);

            scene.Emitters.Add(emitter);

            scene.FrameBuffer = new FrameBuffer(512, 512, "");

            scene.Prepare();

            return(scene);
        }
Esempio n. 9
0
        /// <summary>
        /// Loads a mesh from a .fbx file and adds it to the given scene. Technically, this would also work
        /// for any other file format supported by Assimp, but we are doing some .fbx specific hacks to achieve
        /// the correct scale and other conventions.
        /// </summary>
        /// <param name="filename">Path to an existing .fbx mesh</param>
        /// <param name="scene">The scene to which the mesh should be added</param>
        /// <param name="materialOverride">
        ///     Materials from the .fbx with a name matching one of the keys in this dictionary will be
        ///     replaced by the corresponding dictionary entry
        /// </param>
        /// <param name="emissionOverride">
        ///     If a material name is a key in this dictionary, all meshes with that material will be
        ///     converted to diffuse emitters. The value from the dictionary determines their emitted radiance.
        /// </param>
        public static void AddToScene(string filename, Scene scene, Dictionary <string, Material> materialOverride,
                                      Dictionary <string, RgbColor> emissionOverride = null)
        {
            // Load the file with some basic post-processing
            Assimp.AssimpContext context = new();
            var assimpScene = context.ImportFile(filename,
                                                 Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.PreTransformVertices);

            // Add all meshes to the scene
            foreach (var m in assimpScene.Meshes)
            {
                var    material     = assimpScene.Materials[m.MaterialIndex];
                string materialName = material.Name;

                Vector3[] vertices = new Vector3[m.VertexCount];
                for (int i = 0; i < m.VertexCount; ++i)
                {
                    vertices[i] = new Vector3(-m.Vertices[i].X, m.Vertices[i].Z, m.Vertices[i].Y) * 0.01f;
                }

                Vector3[] normals = null;
                if (m.HasNormals)
                {
                    normals = new Vector3[m.VertexCount];
                    for (int i = 0; i < m.VertexCount; ++i)
                    {
                        normals[i] = new(-m.Normals[i].X, m.Normals[i].Z, m.Normals[i].Y);
                    }
                }

                // We currently only support a single uv channel
                Vector2[] texCoords = null;
                if (m.HasTextureCoords(0))
                {
                    texCoords = new Vector2[m.VertexCount];
                    var texCoordChannel = m.TextureCoordinateChannels[0];
                    for (int i = 0; i < m.VertexCount; ++i)
                    {
                        texCoords[i] = new(texCoordChannel[i].X, 1 - texCoordChannel[i].Y);
                    }
                }

                if (m.TextureCoordinateChannelCount > 1)
                {
                    Logger.Log($"Ignoring additional uv channels in mesh \"{m.Name}\" read from \"{filename}\"",
                               Verbosity.Warning);
                }

                // If the material is not overridden by json, we load the diffuse color, or set an error color
                Material mat;
                if (materialOverride.ContainsKey(materialName))
                {
                    mat = materialOverride[materialName];
                }
                else if (material.HasColorDiffuse)
                {
                    var c = material.ColorDiffuse;
                    mat = new DiffuseMaterial(new() {
                        BaseColor = new Image.TextureRgb(new RgbColor(c.R, c.G, c.B))
                    });
                }
                else
                {
                    mat = new DiffuseMaterial(new() {
                        BaseColor = new Image.TextureRgb(new RgbColor(1, 0, 1))
                    });
                }

                Mesh mesh = new(vertices, m.GetIndices(), normals, texCoords) {
                    Material = mat
                };
                scene.Meshes.Add(mesh);

                // Create an emitter if requested
                if (emissionOverride != null && emissionOverride.TryGetValue(materialName, out var emission))
                {
                    var emitter = new DiffuseEmitter(mesh, emission);
                    scene.Emitters.Add(emitter);
                }
            }
        }
    }