diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index e201bd29..70751ddb 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -634,10 +634,13 @@ fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option) }, }; + let built_options = render_options.prepare(scene.bounds); + let quad = built_options.quad(); + let built_objects = panic::catch_unwind(|| { match jobs { - Some(1) => scene.build_objects_sequentially(render_options, &z_buffer), - _ => scene.build_objects(render_options, &z_buffer), + Some(1) => scene.build_objects_sequentially(built_options, &z_buffer), + _ => scene.build_objects(built_options, &z_buffer), } }); @@ -650,7 +653,7 @@ fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option) } }; - let mut built_scene = BuiltScene::new(scene.view_box); + let mut built_scene = BuiltScene::new(scene.view_box, &quad); built_scene.shaders = scene.build_shaders(); let mut scene_builder = SceneBuilder::new(built_objects, z_buffer, scene.view_box); @@ -832,4 +835,4 @@ fn create_grid_vertex_positions() -> Vec<(u8, u8)> { ]); } positions -} \ No newline at end of file +} diff --git a/geometry/src/basic/point.rs b/geometry/src/basic/point.rs index f485244f..2c5ccfe4 100644 --- a/geometry/src/basic/point.rs +++ b/geometry/src/basic/point.rs @@ -84,6 +84,12 @@ impl Point2DF32 { self.x() * other.y() - self.y() * other.x() } + #[inline] + pub fn dot(&self, other: Point2DF32) -> f32 { + let xy = self.0 * other.0; + xy.x() + xy.y() + } + #[inline] pub fn scale(&self, x: f32) -> Point2DF32 { Point2DF32(self.0 * F32x4::splat(x)) diff --git a/gl/src/renderer.rs b/gl/src/renderer.rs index 0b5c7d41..eb441826 100644 --- a/gl/src/renderer.rs +++ b/gl/src/renderer.rs @@ -132,12 +132,12 @@ impl Renderer { self.upload_shaders(&built_scene.shaders); self.upload_solid_tiles(&built_scene.solid_tiles); - self.draw_solid_tiles(&built_scene.solid_tiles); + self.draw_solid_tiles(&built_scene); for batch in &built_scene.batches { self.upload_batch(batch); self.draw_batch_fills(batch); - self.draw_batch_mask_tiles(batch); + self.draw_batch_mask_tiles(&built_scene, batch); } if self.postprocessing_needed() { @@ -245,7 +245,7 @@ impl Renderer { } } - fn draw_batch_mask_tiles(&mut self, batch: &Batch) { + fn draw_batch_mask_tiles(&mut self, built_scene: &BuiltScene, batch: &Batch) { unsafe { self.bind_draw_framebuffer(); self.set_main_viewport(); @@ -270,6 +270,22 @@ impl Renderer { FILL_COLORS_TEXTURE_HEIGHT as GLfloat); // FIXME(pcwalton): Fill this in properly! gl::Uniform2f(self.mask_tile_program.view_box_origin_uniform.location, 0.0, 0.0); + gl::Uniform3f(self.mask_tile_program.quad_p0_uniform.location, + built_scene.quad[0].x(), + built_scene.quad[0].y(), + built_scene.quad[0].z()); + gl::Uniform3f(self.mask_tile_program.quad_p1_uniform.location, + built_scene.quad[1].x(), + built_scene.quad[1].y(), + built_scene.quad[1].z()); + gl::Uniform3f(self.mask_tile_program.quad_p2_uniform.location, + built_scene.quad[2].x(), + built_scene.quad[2].y(), + built_scene.quad[2].z()); + gl::Uniform3f(self.mask_tile_program.quad_p3_uniform.location, + built_scene.quad[3].x(), + built_scene.quad[3].y(), + built_scene.quad[3].z()); self.enable_blending(); self.enable_depth_test(); gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, batch.mask_tiles.len() as GLint); @@ -277,7 +293,7 @@ impl Renderer { } } - fn draw_solid_tiles(&mut self, solid_tiles: &[SolidTileScenePrimitive]) { + fn draw_solid_tiles(&mut self, built_scene: &BuiltScene) { unsafe { self.bind_draw_framebuffer(); self.set_main_viewport(); @@ -297,11 +313,28 @@ impl Renderer { FILL_COLORS_TEXTURE_HEIGHT as GLfloat); // FIXME(pcwalton): Fill this in properly! gl::Uniform2f(self.solid_tile_program.view_box_origin_uniform.location, 0.0, 0.0); + gl::Uniform3f(self.solid_tile_program.quad_p0_uniform.location, + built_scene.quad[0].x(), + built_scene.quad[0].y(), + built_scene.quad[0].z()); + gl::Uniform3f(self.solid_tile_program.quad_p1_uniform.location, + built_scene.quad[1].x(), + built_scene.quad[1].y(), + built_scene.quad[1].z()); + gl::Uniform3f(self.solid_tile_program.quad_p2_uniform.location, + built_scene.quad[2].x(), + built_scene.quad[2].y(), + built_scene.quad[2].z()); + gl::Uniform3f(self.solid_tile_program.quad_p3_uniform.location, + built_scene.quad[3].x(), + built_scene.quad[3].y(), + built_scene.quad[3].z()); gl::Disable(gl::BLEND); gl::DepthMask(gl::FALSE); gl::Enable(gl::DEPTH_TEST); gl::Disable(gl::STENCIL_TEST); - gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, solid_tiles.len() as GLint); + let count = built_scene.solid_tiles.len(); + gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, count as GLint); } } @@ -539,6 +572,10 @@ struct SolidTileProgram { fill_colors_texture_uniform: Uniform, fill_colors_texture_size_uniform: Uniform, view_box_origin_uniform: Uniform, + quad_p0_uniform: Uniform, + quad_p1_uniform: Uniform, + quad_p2_uniform: Uniform, + quad_p3_uniform: Uniform, } impl SolidTileProgram { @@ -549,6 +586,10 @@ impl SolidTileProgram { let fill_colors_texture_uniform = Uniform::new(&program, "FillColorsTexture"); let fill_colors_texture_size_uniform = Uniform::new(&program, "FillColorsTextureSize"); let view_box_origin_uniform = Uniform::new(&program, "ViewBoxOrigin"); + let quad_p0_uniform = Uniform::new(&program, "QuadP0"); + let quad_p1_uniform = Uniform::new(&program, "QuadP1"); + let quad_p2_uniform = Uniform::new(&program, "QuadP2"); + let quad_p3_uniform = Uniform::new(&program, "QuadP3"); SolidTileProgram { program, framebuffer_size_uniform, @@ -556,6 +597,10 @@ impl SolidTileProgram { fill_colors_texture_uniform, fill_colors_texture_size_uniform, view_box_origin_uniform, + quad_p0_uniform, + quad_p1_uniform, + quad_p2_uniform, + quad_p3_uniform, } } } @@ -569,6 +614,10 @@ struct MaskTileProgram { fill_colors_texture_uniform: Uniform, fill_colors_texture_size_uniform: Uniform, view_box_origin_uniform: Uniform, + quad_p0_uniform: Uniform, + quad_p1_uniform: Uniform, + quad_p2_uniform: Uniform, + quad_p3_uniform: Uniform, } impl MaskTileProgram { @@ -581,6 +630,10 @@ impl MaskTileProgram { let fill_colors_texture_uniform = Uniform::new(&program, "FillColorsTexture"); let fill_colors_texture_size_uniform = Uniform::new(&program, "FillColorsTextureSize"); let view_box_origin_uniform = Uniform::new(&program, "ViewBoxOrigin"); + let quad_p0_uniform = Uniform::new(&program, "QuadP0"); + let quad_p1_uniform = Uniform::new(&program, "QuadP1"); + let quad_p2_uniform = Uniform::new(&program, "QuadP2"); + let quad_p3_uniform = Uniform::new(&program, "QuadP3"); MaskTileProgram { program, framebuffer_size_uniform, @@ -590,6 +643,10 @@ impl MaskTileProgram { fill_colors_texture_uniform, fill_colors_texture_size_uniform, view_box_origin_uniform, + quad_p0_uniform, + quad_p1_uniform, + quad_p2_uniform, + quad_p3_uniform, } } } diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index c3688116..76d388b8 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -15,7 +15,7 @@ use crate::gpu_data::{MaskTileBatchPrimitive, SolidTileScenePrimitive}; use crate::scene; use crate::tiles; use crate::z_buffer::ZBuffer; -use pathfinder_geometry::basic::point::Point2DF32; +use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32}; use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::Perspective; @@ -173,17 +173,47 @@ impl RenderTransform { for point in &mut points { *point = perspective.transform.transform_point(*point); } - //println!("... PERSPECTIVE quad={:?}", points); + println!("... PERSPECTIVE quad={:?}", points); + + // Compute depth. + let quad = [ + points[0].perspective_divide(), + points[1].perspective_divide(), + points[2].perspective_divide(), + points[3].perspective_divide(), + ]; + println!("barycentric(0, 0) = {:?}", compute_barycentric(&[ + quad[0].to_2d(), + quad[1].to_2d(), + quad[2].to_2d(), + quad[3].to_2d(), + ], Point2DF32::new(0.0, 0.0))); + points = PolygonClipper3D::new(points).clip(); //println!("... CLIPPED quad={:?}", points); for point in &mut points { *point = point.perspective_divide() } + let inverse_transform = perspective.transform.inverse(); let clip_polygon = points.into_iter().map(|point| { inverse_transform.transform_point(point).perspective_divide().to_2d() }).collect(); - PreparedRenderTransform::Perspective { perspective, clip_polygon } + return PreparedRenderTransform::Perspective { perspective, clip_polygon, quad }; + + fn compute_barycentric(quad: &[Point2DF32], point: Point2DF32) -> [f32; 4] { + let (s0, s1) = (quad[0] - point, quad[1] - point); + let (s2, s3) = (quad[2] - point, quad[3] - point); + let (a0, a1, a2, a3) = (s0.det(s1), s1.det(s2), s2.det(s3), s3.det(s0)); + let (d0, d1, d2, d3) = (s0.dot(s1), s1.dot(s2), s2.dot(s3), s3.dot(s0)); + let (r0, r1, r2, r3) = (s0.length(), s1.length(), s2.length(), s3.length()); + let (t0, t1) = ((r0 * r1 - d0) / a0, (r1 * r2 - d1) / a1); + let (t2, t3) = ((r2 * r3 - d2) / a2, (r3 * r0 - d3) / a3); + let (u0, u1) = ((t3 + t0) / r0, (t2 + t1) / r1); + let (u2, u3) = ((t0 + t2) / r2, (t0 + t3) / r3); + let sum = u0 + u1 + u2 + u3; + [u0 / sum, u1 / sum, u2 / sum, u3 / sum] + } } } @@ -192,8 +222,19 @@ pub struct PreparedRenderOptions { pub dilation: Point2DF32, } +impl PreparedRenderOptions { + #[inline] + pub fn quad(&self) -> [Point3DF32; 4] { + match self.transform { + PreparedRenderTransform::Perspective { quad, .. } => quad, + _ => [Point3DF32::default(); 4], + } + } +} + pub enum PreparedRenderTransform { None, Transform2D(Transform2DF32), - Perspective { perspective: Perspective, clip_polygon: Vec } + Perspective { perspective: Perspective, clip_polygon: Vec, quad: [Point3DF32; 4] } } + diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index 9eea07f9..cbf37b5e 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -14,7 +14,7 @@ use crate::paint::{ObjectShader, ShaderId}; use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH}; use fixedbitset::FixedBitSet; use pathfinder_geometry::basic::line_segment::{LineSegmentF32, LineSegmentU4, LineSegmentU8}; -use pathfinder_geometry::basic::point::Point2DF32; +use pathfinder_geometry::basic::point::{Point2DF32, Point3DF32}; use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::util; use pathfinder_simd::default::{F32x4, I32x4}; @@ -32,6 +32,7 @@ pub struct BuiltObject { #[derive(Debug)] pub struct BuiltScene { pub view_box: RectF32, + pub quad: [Point3DF32; 4], pub batches: Vec, pub solid_tiles: Vec, pub shaders: Vec, @@ -249,8 +250,8 @@ impl BuiltObject { impl BuiltScene { #[inline] - pub fn new(view_box: RectF32) -> BuiltScene { - BuiltScene { view_box, batches: vec![], solid_tiles: vec![], shaders: vec![] } + pub fn new(view_box: RectF32, quad: &[Point3DF32; 4]) -> BuiltScene { + BuiltScene { view_box, quad: *quad, batches: vec![], solid_tiles: vec![], shaders: vec![] } } } diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 33f6418e..6c932076 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -10,7 +10,7 @@ //! A set of paths to be rendered. -use crate::builder::{PreparedRenderOptions, PreparedRenderTransform, RenderOptions}; +use crate::builder::{PreparedRenderOptions, PreparedRenderTransform}; use crate::gpu_data::BuiltObject; use crate::paint::{ObjectShader, Paint, PaintId, ShaderId}; use crate::tiles::Tiler; @@ -63,9 +63,10 @@ impl Scene { .collect() } - pub fn build_objects_sequentially(&self, options: RenderOptions, z_buffer: &ZBuffer) + pub fn build_objects_sequentially(&self, + built_options: PreparedRenderOptions, + z_buffer: &ZBuffer) -> Vec { - let built_options = options.prepare(self.bounds); self.objects .iter() .enumerate() @@ -84,8 +85,8 @@ impl Scene { .collect() } - pub fn build_objects(&self, options: RenderOptions, z_buffer: &ZBuffer) -> Vec { - let built_options = options.prepare(self.bounds); + pub fn build_objects(&self, built_options: PreparedRenderOptions, z_buffer: &ZBuffer) + -> Vec { self.objects .par_iter() .enumerate() @@ -108,7 +109,7 @@ impl Scene { -> Outline { let mut outline; match options.transform { - PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon } => { + PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon, .. } => { if original_outline.is_outside_polygon(clip_polygon) { outline = Outline::new(); } else { diff --git a/resources/shaders/mask_tile.vs.glsl b/resources/shaders/mask_tile.vs.glsl index 0684e0b7..38c78f59 100644 --- a/resources/shaders/mask_tile.vs.glsl +++ b/resources/shaders/mask_tile.vs.glsl @@ -18,6 +18,10 @@ uniform vec2 uStencilTextureSize; uniform sampler2D uFillColorsTexture; uniform vec2 uFillColorsTextureSize; uniform vec2 uViewBoxOrigin; +uniform vec3 uQuadP0; +uniform vec3 uQuadP1; +uniform vec3 uQuadP2; +uniform vec3 uQuadP3; in vec2 aTessCoord; in vec2 aTileOrigin; @@ -28,6 +32,21 @@ out vec2 vTexCoord; out float vBackdrop; out vec4 vColor; +float wedge(vec2 a, vec2 b) { + return a.x * b.y - a.y * b.x; +} + +// From "A Quadrilateral Rendering Primitive", Hormann and Tarini 2004. +vec4 barycentricQuad(vec2 p) { + vec2 s0 = uQuadP0.xy - p, s1 = uQuadP1.xy - p, s2 = uQuadP2.xy - p, s3 = uQuadP3.xy - p; + vec4 a = vec4(wedge(s0, s1), wedge(s1, s2), wedge(s2, s3), wedge(s3, s0)); + vec4 d = vec4(dot(s0, s1), dot(s1, s2), dot(s2, s3), dot(s3, s0)); + vec4 r = vec4(length(s0), length(s1), length(s2), length(s3)); + vec4 t = (r * r.yzwx - d) / a; + vec4 u = (t.wxyz + t) / r; + return u / dot(u, vec4(1.0)); +} + vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) { uint tilesPerRow = uint(stencilTextureWidth / uTileSize.x); uvec2 tileOffset = uvec2(tileIndex % tilesPerRow, tileIndex / tilesPerRow); @@ -41,11 +60,17 @@ vec2 computeFillColorTexCoord(uint object, vec2 textureSize) { void main() { uint tileIndex = uint(gl_InstanceID); - vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; + vec2 pixelPosition = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; + vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0); + + vec4 depths = vec4(uQuadP0.z, uQuadP1.z, uQuadP2.z, uQuadP3.z); + float depth = dot(barycentricQuad(position), depths); + vec2 texCoord = computeTileOffset(tileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize; vec2 colorTexCoord = computeFillColorTexCoord(aObject, uFillColorsTextureSize); + vTexCoord = texCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = texture(uFillColorsTexture, colorTexCoord); - gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); + gl_Position = vec4(position, depth, 1.0); } diff --git a/resources/shaders/solid_tile.vs.glsl b/resources/shaders/solid_tile.vs.glsl index 17201e91..eee34396 100644 --- a/resources/shaders/solid_tile.vs.glsl +++ b/resources/shaders/solid_tile.vs.glsl @@ -17,6 +17,10 @@ uniform vec2 uTileSize; uniform sampler2D uFillColorsTexture; uniform vec2 uFillColorsTextureSize; uniform vec2 uViewBoxOrigin; +uniform vec3 uQuadP0; +uniform vec3 uQuadP1; +uniform vec3 uQuadP2; +uniform vec3 uQuadP3; in vec2 aTessCoord; in vec2 aTileOrigin; @@ -24,14 +28,35 @@ in uint aObject; out vec4 vColor; +float wedge(vec2 a, vec2 b) { + return a.x * b.y - a.y * b.x; +} + +// From "A Quadrilateral Rendering Primitive", Hormann and Tarini 2004. +vec4 barycentricQuad(vec2 p) { + vec2 s0 = uQuadP0.xy - p, s1 = uQuadP1.xy - p, s2 = uQuadP2.xy - p, s3 = uQuadP3.xy - p; + vec4 a = vec4(wedge(s0, s1), wedge(s1, s2), wedge(s2, s3), wedge(s3, s0)); + vec4 d = vec4(dot(s0, s1), dot(s1, s2), dot(s2, s3), dot(s3, s0)); + vec4 r = vec4(length(s0), length(s1), length(s2), length(s3)); + vec4 t = (r * r.yzwx - d) / a; + vec4 u = (t.wxyz + t) / r; + return u / dot(u, vec4(1.0)); +} + vec2 computeFillColorTexCoord(uint object, vec2 textureSize) { uint width = uint(textureSize.x); return (vec2(float(object % width), float(object / width)) + vec2(0.5)) / textureSize; } void main() { - vec2 position = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; + vec2 pixelPosition = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin; + vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0); + + vec4 depths = vec4(uQuadP0.z, uQuadP1.z, uQuadP2.z, uQuadP3.z); + float depth = dot(barycentricQuad(position), depths); + vec2 colorTexCoord = computeFillColorTexCoord(aObject, uFillColorsTextureSize); + vColor = texture(uFillColorsTexture, colorTexCoord); - gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0); + gl_Position = vec4(position, depth, 1.0); }