private void Render(ref ShaderVariables shader, IEnumerable <Triangle> tris, IUvMap?uvs, Material material) { var backwards = this.Backward; foreach (var tri in tris) { var worldTri = tri.Transform(shader.ModelToWorld); // Convert to world space shader.WorldNormal = worldTri.Normal; // Backface culling if (!material.TwoSided) { var normal = worldTri.Normal; if (Vec3.Dot(backwards, normal) < -0.1) { continue; } } // Get triangles var v1 = WorldToScreenPoint(worldTri.Item1); var v2 = WorldToScreenPoint(worldTri.Item2); var v3 = WorldToScreenPoint(worldTri.Item3); // Get the UV coordinates Vec2 uv1 = (uvs != null) ? uvs[tri.Item1] : Vec2.Zero; Vec2 uv2 = (uvs != null) ? uvs[tri.Item2] : Vec2.Zero; Vec2 uv3 = (uvs != null) ? uvs[tri.Item3] : Vec2.Zero; Rasterize(ref shader, worldTri, v1, v2, v3, uv1, uv2, uv3, material); } }
private void DrawVertex(Vec3 world, Vec3 screen, Vec2 uv, Material material, ref ShaderVariables vars) { vars.WorldPosition = world; vars.ScreenPixel = screen; vars.UVCoordinates = uv; var colour = material.Vert(vars); SetPixel(screen, colour); }
/// <summary> /// Shader to apply to each face's surface /// </summary> /// <param name="variables">shading variables</param> /// <returns>color of point on face</returns> public override Color Fragment(ShaderVariables variables) { var surfColour = ColourSample(variables.UVCoordinates); Vec3 N = variables.WorldNormal; var tint = variables.LightSources.Select((light) => { var lightColour = light.Tint; Vec3 L = light.Direction(variables.WorldPosition, variables.WorldNormal); double Lintensity = light.Intensity(variables.WorldPosition, variables.WorldNormal); double diffuse_surface_shade = (Albedo / Math.PI) * Lintensity * Math.Max(Vec3.Dot(L, N), 0); return(Darken(lightColour, diffuse_surface_shade)); }).Aggregate((a, b) => Mix(a, b)); return(Mix(surfColour, tint)); }
/// <summary> /// Render the given scene to the pixel buffer /// </summary> /// <param name="scene">scene to render</param> public void Render(Scene scene) { // Clean data ClearPixels(); ClearDepth(); // Set constant shader properties var vars = new ShaderVariables(); vars.WorldCameraPosition = this.Position; vars.LightSources = scene.OfType <LightSource>().ToList().AsReadOnly(); // Loop over all models foreach (var renderable in scene.OfType <IRenderable>()) { vars.ModelToWorld = renderable.LocalToWorldMatrix; if (renderable.Mesh != null && renderable.Material != null) { Render(ref vars, renderable.Mesh, renderable.UVs, (Material)renderable.Material); } } }
/// <summary> /// Shader to apply to each face's edges /// </summary> /// <param name="variables">shading variables</param> /// <returns>edge colour</returns> public override Color Edge(ShaderVariables variables) { return(Colour); }
/// <summary> /// Shader to apply to each face's surface /// </summary> /// <param name="variables">shading variables</param> /// <returns>color of point on face</returns> public virtual Color Fragment(ShaderVariables variables) { return(Color.Transparent); }
/// <summary> /// Shader to apply to each face's edges /// </summary> /// <param name="variables">shading variables</param> /// <returns>edge colour</returns> public virtual Color Edge(ShaderVariables variables) { return(Fragment(variables)); }
/// <summary> /// Shader to apply to each face's surface /// </summary> /// <param name="variables">shading variables</param> /// <returns>color of point on face</returns> public override Color Fragment(ShaderVariables variables) { return(ColourSample(variables.UVCoordinates)); }
/// <summary> /// Shader to apply to each face's edges /// </summary> /// <param name="variables">shading variables</param> /// <returns>edge colour</returns> public override Color Edge(ShaderVariables variables) { return(Fragment(variables)); }
/// <summary> /// Shader to apply to each face's surface /// </summary> /// <param name="variables">shading variables</param> /// <returns>color of point on face</returns> public override Color Fragment(ShaderVariables variables) { return(Colour); }
private void DrawFlatTopTriangle(Vec3 worldV1, Vec3 worldV2, Vec3 worldV3, Vec3 a, Vec3 b, Vec3 c, Vec2 uva, Vec2 uvb, Vec2 uvc, Material img, ref ShaderVariables shader) { double invslope1 = (c.X - a.X) / (c.Y - a.Y); double invslope2 = (c.X - b.X) / (c.Y - b.Y); double leftX = Math.Floor(c.X); double rightX = Math.Floor(c.X); int startH = (int)Math.Floor(c.Y); int endH = (int)Math.Floor(a.Y); for (int scan = startH; scan > endH; scan--) { double t = (scan - startH) / (endH - startH); Vec2 left = Vec2.Lerp(uvc, uva, t); Vec2 right = Vec2.Lerp(uvc, uvb, t); Vec3 worldLeft = Vec3.Lerp(worldV3, worldV1, t); Vec3 worldRight = Vec3.Lerp(worldV3, worldV2, t); double zL = Lerp(c.Z, a.Z, t); double zR = Lerp(c.Z, b.Z, t); DrawLine(worldLeft, worldRight, new Vec3(leftX, scan, zL), new Vec3(rightX, scan, zR), left, right, img, ref shader, surface: true); leftX -= invslope1; rightX -= invslope2; } }
private void DrawLine(Vec3 worldV1, Vec3 worldV2, Vec3 v1, Vec3 v2, Vec2 uv1, Vec2 uv2, Material material, ref ShaderVariables shader, bool surface) { double dist2d = Math.Sqrt((v2.X - v1.X) * (v2.X - v1.X) + (v2.Y - v1.Y) * (v2.Y - v1.Y)); if (dist2d != 0) { double invdist = 1 / dist2d; for (double i = 0; i < 1; i += invdist) { Vec3 world = Vec3.Lerp(worldV1, worldV2, i); Vec3 pixel = Vec3.Lerp(v1, v2, i); Vec2 uv = Vec2.Lerp(uv1, uv2, i); shader.WorldPosition = world; shader.ScreenPixel = pixel; shader.UVCoordinates = uv; var colour = surface ? material.Fragment(shader) : material.Edge(shader); SetPixel(pixel, colour); } } }
private void Rasterize(ref ShaderVariables shader, Triangle worldTri, Vec3 v1, Vec3 v2, Vec3 v3, Vec2 uv1, Vec2 uv2, Vec2 uv3, Material material) { // Sort vertices into ascending order of y var worldV1 = worldTri.Item1; var worldV2 = worldTri.Item2; var worldV3 = worldTri.Item3; if (v1.Y > v2.Y) { (v1, v2) = (v2, v1); (uv1, uv2) = (uv2, uv1); (worldV1, worldV2) = (worldV2, worldV1); } if (v2.Y > v3.Y) { (v2, v3) = (v3, v2); (uv2, uv3) = (uv3, uv2); (worldV2, worldV3) = (worldV3, worldV2); } if (v1.Y > v2.Y) { (v1, v2) = (v2, v1); (uv1, uv2) = (uv2, uv1); (worldV1, worldV2) = (worldV2, worldV1); } // Rasterize if (v2.Y == v3.Y) { // Flat Bottom Triangle DrawFlatBottomTriangle(worldV1, worldV2, worldV3, v1, v2, v3, uv1, uv2, uv3, material, ref shader); DrawLine(worldV1, worldV2, v1, v2, uv1, uv2, material, ref shader, surface: false); DrawLine(worldV2, worldV3, v2, v3, uv2, uv3, material, ref shader, surface: false); DrawLine(worldV1, worldV3, v1, v3, uv1, uv3, material, ref shader, surface: false); DrawVertex(worldV1, v1, uv1, material, ref shader); DrawVertex(worldV2, v2, uv2, material, ref shader); DrawVertex(worldV3, v3, uv3, material, ref shader); } else if (v1.Y == v2.Y) { // Flat Top Triangle DrawFlatTopTriangle(worldV1, worldV2, worldV3, v1, v2, v3, uv1, uv2, uv3, material, ref shader); DrawLine(worldV1, worldV2, v1, v2, uv1, uv2, material, ref shader, surface: false); DrawLine(worldV2, worldV3, v2, v3, uv2, uv3, material, ref shader, surface: false); DrawLine(worldV1, worldV3, v1, v3, uv1, uv3, material, ref shader, surface: false); DrawVertex(worldV1, v1, uv1, material, ref shader); DrawVertex(worldV2, v2, uv2, material, ref shader); DrawVertex(worldV3, v3, uv3, material, ref shader); } else { // General Triangle double t = (v2.Y - v3.Y) / (v1.Y - v3.Y); Vec3 d = new Vec3( (v1.X) + ((v2.Y - v1.Y) / (v3.Y - v1.Y)) * (v3.X - v1.X), v2.Y, Lerp(v3.Z, v1.Z, t) ); Vec3 worldD = Vec3.Lerp(worldV3, worldV1, t); Vec2 uvd = new Vec2( Lerp(uv3.X, uv1.X, t), Lerp(uv3.Y, uv1.Y, t) ); DrawFlatBottomTriangle(worldV1, worldV2, worldD, v1, v2, d, uv1, uv2, uvd, material, ref shader); DrawFlatTopTriangle(worldV2, worldD, worldV3, v2, d, v3, uv2, uvd, uv3, material, ref shader); DrawLine(worldV1, worldV2, v1, v2, uv1, uv2, material, ref shader, surface: false); DrawLine(worldV2, worldV3, v2, v3, uv2, uv3, material, ref shader, surface: false); DrawLine(worldV1, worldV3, v1, v3, uv1, uv3, material, ref shader, surface: false); DrawVertex(worldV1, v1, uv1, material, ref shader); DrawVertex(worldV2, v2, uv2, material, ref shader); DrawVertex(worldV3, v3, uv3, material, ref shader); } }