public void DrawBeziers(int count, Position <float>[] controls, Color <float>[] colors, float[] width, Matrix4x4[] transforms) { Utility.Assert(controls.Length >= 3 * count && colors.Length >= count && width.Length >= count); Utility.Assert(Mode == RenderMode.Draw); if (count == 0) { return; } TransformMatrix[] transformMatrices = new TransformMatrix[count]; Equation[] equations = new Equation[count]; Vector4[] color = new Vector4[count]; Vertex[] vertices = new Vertex[4] { new Vertex() { Position = new Vector3(0, 0, 0) }, new Vertex() { Position = new Vector3(0, 1, 0) }, new Vertex() { Position = new Vector3(1, 1, 0) }, new Vertex() { Position = new Vector3(1, 0, 0) } }; var size = new Vector2(mCanvasSize.Width, mCanvasSize.Height) * 0.5f; for (int index = 0; index < count; index++) { Vector2[] points = new Vector2[] { new Vector2(controls[index * 3 + 0].X, controls[index * 3 + 0].Y), new Vector2(controls[index * 3 + 1].X, controls[index * 3 + 1].Y), new Vector2(controls[index * 3 + 2].X, controls[index * 3 + 2].Y) }; //get bounding box(before transform) of bezier curve var box = QuadraticBezierCurve.BoundingBox(points, width[index]); //create transform matrix transformMatrices[index] = new TransformMatrix() { World = Matrix4x4.CreateScale(box.Max.X - box.Min.X, box.Max.Y - box.Min.Y, 1) * Matrix4x4.CreateTranslation(box.Min.X, box.Min.Y, 0) * Transform, Projection = mProjection }; //we need transform the control points to screen space var transformMatrix = (transforms == null ? Matrix4x4.Identity : transforms[index]) * Transform * mProjection; //transform points for (int i = 0; i < 3; i++) { points[i] = (Vector2.Transform(points[i], transformMatrix) + Vector2.One) * size; points[i].Y = mCanvasSize.Height - points[i].Y; } //get parameter format of bezier curve //Q(t) = At^2 + B^t + C var parameters = QuadraticBezierCurve.ParameterFormat(points); var A = parameters[0]; var B = parameters[1]; var C = parameters[2]; //For point P, when any root of equation(Q'(s) * (P - Q(s)) = 0) is legal, the P is inside the curve //The equation full-format is (-2A^2)t^3 + (-3AB)t^2 + (2AP - 2AC - B^2)t + B(P - C) = 0 //we can use Cardano formula to solve it in pixel shader. //but we can cache some calculation for opt. var c0 = -2 * Vector2.Dot(A, A); var c1 = -3 * Vector2.Dot(A, B); var c2 = -2 * Vector2.Dot(A, C) - Vector2.Dot(B, B); var c3 = -Vector2.Dot(B, C); equations[index] = new Equation() { Coefficient0 = new Vector4(c0, c1, c2, c3), Coefficient1 = new Vector4(A.X, A.Y, B.X, B.Y), Coefficient2 = new Vector4(C.X, C.Y, width[index] * width[index] * 0.25f, width[index] * width[index] * 0.25f) }; color[index] = new Vector4(colors[index].Red, colors[index].Green, colors[index].Blue, colors[index].Alpha); } if (mColorBufferArray == null || mColorBufferArray.ElementCount != count) { Utility.Dispose(ref mColorBufferArray); Utility.Dispose(ref mEquationBufferArray); Utility.Dispose(ref mTransformBufferArray); Utility.Dispose(ref mColorBufferArrayUsage); Utility.Dispose(ref mEquationBufferArrayUsage); Utility.Dispose(ref mTransformBufferArrayUsage); mColorBufferArray = new GpuBufferArray( Utility.SizeOf <Vector4>(), count, mDevice, GpuResourceInfo.BufferArray()); mEquationBufferArray = new GpuBufferArray( Utility.SizeOf <Equation>(), count, mDevice, GpuResourceInfo.BufferArray()); mTransformBufferArray = new GpuBufferArray( Utility.SizeOf <TransformMatrix>(), count, mDevice, GpuResourceInfo.BufferArray()); mColorBufferArrayUsage = new GpuResourceUsage(mDevice, mColorBufferArray); mEquationBufferArrayUsage = new GpuResourceUsage(mDevice, mEquationBufferArray); mTransformBufferArrayUsage = new GpuResourceUsage(mDevice, mTransformBufferArray); } mColorBufferArray.Update(color); mEquationBufferArray.Update(equations); mTransformBufferArray.Update(transformMatrices); mDrawVertexBuffer.Update(vertices); //change shader we use mDevice.SetVertexShader(mDrawBeziersVertexShader); mDevice.SetPixelShader(mDrawBeziersPixelShader); //set vertex buffer mDevice.SetVertexBuffer(mDrawVertexBuffer); mDevice.SetIndexBuffer(mDrawIndexBuffer); //set constant buffer and resource mDevice.SetResourceUsage(mColorBufferArrayUsage, 0); mDevice.SetResourceUsage(mEquationBufferArrayUsage, 1); mDevice.SetResourceUsage(mTransformBufferArrayUsage, 2); //drwa indexed instanced mDevice.DrawIndexedInstanced(6, count); //reset the shader mDevice.SetVertexShader(mDrawBezierVertexShader); mDevice.SetPixelShader(mDrawBezierPixelShader); }
public void DrawBezier(Position <float>[] controls, Color <float> color, float width = 2.0f) { Utility.Assert(controls.Length >= 3); Utility.Assert(Mode == RenderMode.Draw); //convert Position<T> to Vector2 Vector2[] points = new Vector2[] { new Vector2(controls[0].X, controls[0].Y), new Vector2(controls[1].X, controls[1].Y), new Vector2(controls[2].X, controls[2].Y) }; //get bounding box(before transform) of bezier curve var box = QuadraticBezierCurve.BoundingBox(points, width); var vertices = new Vertex[] { new Vertex() { Position = new Vector3(box.Min.X, box.Min.Y, 0) }, new Vertex() { Position = new Vector3(box.Min.X, box.Max.Y, 0) }, new Vertex() { Position = new Vector3(box.Max.X, box.Max.Y, 0) }, new Vertex() { Position = new Vector3(box.Max.X, box.Min.Y, 0) } }; for (int i = 0; i < 4; i++) { vertices[i].Color = new Vector4(color.Red, color.Green, color.Blue, color.Alpha); vertices[i].TexCoord = new Vector2(width); } //we need transform the control points to screen space var transformMatrix = Transform * mProjection; var size = new Vector2(mCanvasSize.Width, mCanvasSize.Height) * 0.5f; //transform points for (int i = 0; i < 3; i++) { points[i] = (Vector2.Transform(points[i], transformMatrix) + Vector2.One) * size; points[i].Y = mCanvasSize.Height - points[i].Y; } //get parameter format of bezier curve //Q(t) = At^2 + B^t + C var parameters = QuadraticBezierCurve.ParameterFormat(points); var A = parameters[0]; var B = parameters[1]; var C = parameters[2]; //For point P, when any root of equation(Q'(s) * (P - Q(s)) = 0) is legal, the P is inside the curve //The equation full-format is (-2A^2)t^3 + (-3AB)t^2 + (2AP - 2AC - B^2)t + B(P - C) = 0 //we can use Cardano formula to solve it in pixel shader. //but we can cache some calculation for opt. var c0 = -2 * Vector2.Dot(A, A); var c1 = -3 * Vector2.Dot(A, B); var c2 = -2 * Vector2.Dot(A, C) - Vector2.Dot(B, B); var c3 = -Vector2.Dot(B, C); var equation = new Equation() { Coefficient0 = new Vector4(c0, c1, c2, c3), Coefficient1 = new Vector4(A.X, A.Y, B.X, B.Y), Coefficient2 = new Vector4(C.X, C.Y, width * width * 0.25f, width * width * 0.25f) }; mTransformBuffer.Update(new TransformMatrix() { World = Transform, Projection = mProjection }); mDrawVertexBuffer.Update(vertices); mEquationBuffer.Update(equation); mDevice.SetVertexBuffer(mDrawVertexBuffer); mDevice.SetIndexBuffer(mDrawIndexBuffer); mDevice.SetBuffer(mTransformBuffer, 0); mDevice.SetBuffer(mEquationBuffer, 1); mDevice.DrawIndexed(6); }