implement perspective correct interpolation to the 2D window space clipped brush stroke sample.

This commit is contained in:
2018-11-15 18:09:19 +01:00
parent 18067726b5
commit 8f9422b6d6
5 changed files with 162 additions and 22 deletions

View File

@@ -423,15 +423,29 @@ void ui::Canvas::stroke_draw()
+ dx + dy, // C - top-right
+ dx - dy, // D - bottom-right
};
static glm::vec4 P[4];
int intersected = 0;
int inside = 0;
// face is the 2d shape of the cube plane i projected onto the window space
auto face = face_to_shape2D(i);
// P is the initial square centered at the cursor location
std::vector<vertex_t> P{
vertex_t{ {0, 0, 1, 1}, {0, 0}, {0, 0} },
vertex_t{ {0, 0, 1, 1}, {0, 1}, {0, 1} },
vertex_t{ {0, 0, 1, 1}, {1, 1}, {1, 1} },
vertex_t{ {0, 0, 1, 1}, {1, 0}, {1, 0} },
};
for (int j = 0; j < 4; j++)
P[j].pos = glm::vec4(xy(s.pos) + off[j] * glm::orientate2(-s.angle), 1, 1);
// intersect P with the current face to clip diverging points from the plane
P = poly_intersect(P, face);
for (int j = 0; j < P.size(); j++)
{
glm::vec3 ray_origin, ray_dir;
if (s.pos.z == 0)
{
point_unproject(xy(s.pos) + off[j] * glm::orientate2(-s.angle), { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir);
point_unproject(P[j].pos, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir);
}
else
{
@@ -451,19 +465,29 @@ void ui::Canvas::stroke_draw()
{
inside++;
}
P[j].x = -(plane_local.x * 0.5f - 0.5f) * m_width;
P[j].y = (plane_local.y * 0.5f + 0.5f) * m_height;
P[j].pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width;
P[j].pos.y = (plane_local.y * 0.5f + 0.5f) * m_height;
P[j].uvs2 = UV2[j];
// Black magic - BEWARE!
// interpolation perspective correction, use the current camera projection to correct the interpolation
// because the new shape will have z fixed with an ortho projection when drawn to the face
// we need to imitate the same perspective as the once in the camera
// see: https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes
auto hit_cam = m_mv * glm::vec4(hit, 1);
P[j].pos.z = hit_cam.z;
P[j].uvs *= hit_cam.z;
P[j].uvs2 *= hit_cam.z;
intersected++;
}
else
{
// if (i==0)
// LOG("no intersection with plane %d", i);
break;
}
}
if (intersected < 4 || inside == 0)
if (intersected < 3 || inside == 0)
continue;
m_dirty_face[i] = true;
@@ -473,13 +497,12 @@ void ui::Canvas::stroke_draw()
glActiveTexture(GL_TEXTURE1);
m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing)
glm::vec2 bb_min(m_width, m_height);
glm::vec2 bb_max(0, 0);
for (int j = 0; j < 4; j++)
for (int j = 0; j < P.size(); j++)
{
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j])));
bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, xy(P[j])));
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j].pos)));
bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, xy(P[j].pos)));
}
auto bb_sz = bb_max - bb_min;
@@ -498,11 +521,41 @@ void ui::Canvas::stroke_draw()
glm::max(zw(m_dirty_box[i]), (glm::vec2)(tex_pos + tex_sz))
);
ShaderManager::use(ui::kShader::Stroke);
ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj);
ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(s.col, m_brush.m_tip_color.a));
ShaderManager::u_float(kShaderUniform::Alpha, s.flow);
m_plane_brush.update_vertices(P, nullptr, UV2);
m_plane_brush.draw_fill();
P = triangulate_simple(P);
m_brush_shape.update_vertices(P.data(), P.size());
m_brush_shape.draw_fill();
/*
// draw sample wireframe
std::vector<vertex_t> lines;
for (int vi = 0; vi < P.size(); vi += 3)
{
auto a = P[vi];
auto b = P[(vi + 1) % P.size()];
auto c = P[(vi + 2) % P.size()];
a.pos.z = b.pos.z = c.pos.z = 0;
lines.push_back(a);
lines.push_back(b);
lines.push_back(b);
lines.push_back(c);
lines.push_back(c);
lines.push_back(a);
}
ShaderManager::use(kShader::Color);
ShaderManager::u_vec4(kShaderUniform::Col, { s.col, 1 });
ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj);
m_brush_shape.update_vertices(lines.data(), lines.size());
m_brush_shape.draw_stroke();
*/
glActiveTexture(GL_TEXTURE1);
m_tex[i].unbind();
@@ -950,6 +1003,7 @@ bool ui::Canvas::create(int width, int height)
m_sampler_mix.create(GL_NEAREST, GL_REPEAT);
m_plane.create<1>(1, 1);
m_plane_brush.create<1>(1, 1);
m_brush_shape.create();
m_mesh.create();
m_brush_mix.create(8, 8);
for (auto& l : m_layers)
@@ -2039,8 +2093,8 @@ std::vector<glm::vec2> ui::Canvas::face_to_shape2D(int plane_index)
{
auto pt_clip = m_proj * glm::vec4(p, 1);
pt_clip = pt_clip / pt_clip.w;
glm::vec2 pt_screen = (glm::vec2(pt_clip) * glm::vec2(1,-1) * 0.5f + 0.5f) * zw(m_vp);
//pt_screen.y = m_vp.w - pt_screen.y - 1;
glm::vec2 pt_screen = (glm::vec2(pt_clip) * 0.5f + 0.5f) * zw(m_vp);
pt_screen.y = m_vp.w - pt_screen.y - 1;
points.push_back(pt_screen);
}
return points;
@@ -2065,11 +2119,8 @@ std::vector<vertex_t> ui::Canvas::triangulate_simple(const std::vector<vertex_t>
vertex_t vertex;
for (int i = 0; i < 3; i++)
{
auto p = t->GetPoint(i);
auto index = std::distance(points.data(), p);
vertex.pos = glm::vec4(p->x, p->y, 0, 1);
vertex.uvs = vertices[index].uvs;
ret.push_back(vertex);
auto index = std::distance(points.data(), t->GetPoint(i));
ret.push_back(vertices[index]);
}
}

View File

@@ -105,6 +105,7 @@ class Canvas
public:
Plane m_plane;
Plane m_plane_brush;
DynamicShape m_brush_shape;
BrushMesh m_mesh;
bool m_unsaved = false;
bool m_newdoc = true;
@@ -129,6 +130,8 @@ public:
bool m_show_tmp = false;
std::vector<Layer> m_layers;
std::vector<int> m_order;
glm::mat4 m_plane_unproject[6] = SIXPLETTE(glm::mat4(1));
glm::vec3 m_plane_dir[6] = SIXPLETTE(glm::vec3(0));
glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0));
bool m_dirty_face[6] = SIXPLETTE(false);
Layer m_smask; // selection mask

View File

@@ -874,9 +874,19 @@ void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj,
m_circle.draw_fill();
}
ui::ShaderManager::use(ui::kShader::UVs);
ui::ShaderManager::use(ui::kShader::StrokePreview);
ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0);
ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, canvas->m_current_brush.m_tip_flow);
auto tip_color = glm::vec4(glm::vec3(canvas->m_current_brush.m_tip_color), 1);
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, tip_color);
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho);
glEnable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
auto& tex = TextureManager::get(canvas->m_current_brush.m_tex_id);
tex.bind();
canvas->m_sampler.bind(0);
m_shape.draw_fill();
tex.unbind();
if (depth) glEnable(GL_DEPTH_TEST);
}
@@ -887,6 +897,80 @@ void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{
case kEventType::MouseDownR:
//m_origin = canvas->project2Dpoint(loc);
{
auto p = loc;
// flip the y coordinate to match opengl Y up
p.y = canvas->m_box.w - loc.y - 1;
//LOG("loc %f %f", p.x, p.y);
auto ln = (p / zw(canvas->m_box)) * 2.f - 1.f;
glm::vec3 ro, rd, hit_o, hit_d;
glm::vec2 hit_fb;
canvas->point_trace_plane(loc, ro , rd, hit_o, hit_d, hit_fb, 0);
//LOG("hit plane %f %f %f", hit_o.x, hit_o.y, hit_o.z);
auto plane2cam = canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0] * glm::vec4(hit_o, 1);
plane2cam = plane2cam / plane2cam.w;
auto p3d_back = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]) * plane2cam;
p3d_back = p3d_back / p3d_back.w;
//LOG("\nloc nor %f %f", ln.x, ln.y);
//LOG("plane2cam %f %f %f %f", plane2cam.x, plane2cam.y, plane2cam.z, plane2cam.w);
//LOG("\ncam2plane inv %f %f %f %f", p3d_back.x, p3d_back.y, p3d_back.z, p3d_back.w);
auto loc_back = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]) * glm::vec4(ln, 1, 1);
loc_back = loc_back / loc_back.z;
//LOG("loc inv %f %f %f %f", loc_back.x, loc_back.y, loc_back.z, loc_back.w);
auto p2d = (xy(plane2cam) * 0.5f + 0.5f) * zw(canvas->m_box);
p2d.y = canvas->m_box.w - p2d.y - 1;
//LOG("vp %f %f", p2d.x, p2d.y);
auto loc_vp = (xy(loc_back) * glm::vec2(-1, -1) * 0.5f + 0.5f) * glm::vec2(canvas->m_width, canvas->m_height);
LOG("trc fb %f %f", hit_fb.x, hit_fb.y);
LOG("prj vp %f %f", loc_vp.x, loc_vp.y);
LOG("trc dir %f %f %f", hit_d.x, hit_d.y, hit_d.z);
LOG("prj dir %f %f %f", canvas->m_plane_dir[0].x, canvas->m_plane_dir[0].y, canvas->m_plane_dir[0].z);
LOG("------------------------------------------");
// convert to normalized cube space
//auto p3d = glm::vec4((p / zw(canvas->m_box)) * 2.f - 1.f, 1, 1);
//auto m = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]);
//p3d = p3d * m;
//p3d = p3d / p3d.w;
//auto p3d = glm::unProject(glm::vec3(loc, 1), canvas->m_mv * canvas->m_plane_transform[0], canvas->m_proj, canvas->m_vp);
//p3d = p3d / p3d.z;
/*
{
double sum = 0;
auto start = std::chrono::steady_clock::now();
for (int i = 0; i < 10000000; i++)
{
glm::vec3 ro, rd, hit_o, hit_d;
glm::vec2 hit_fb;
canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0);
sum += hit_fb.x;
}
auto t1 = std::chrono::steady_clock::now();
auto m = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]);
for (int i = 0; i < 10000000; i++)
{
auto loc_back = m * glm::vec4(ln, 1, 1);
loc_back = loc_back / loc_back.z;
//auto loc_vp = (xy(loc_back) * glm::vec2(-1, -1) * 0.5f + 0.5f) * glm::vec2(canvas->m_width, canvas->m_height);
sum += loc_back.x;
}
auto t2 = std::chrono::steady_clock::now();
LOG("t1 %d", (int)std::chrono::duration_cast<std::chrono::milliseconds>(t1 - start).count());
LOG("t2 %d", (int)std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count());
LOG("sum %f", sum);
}
*/
}
break;
case kEventType::MouseMove:
case kEventType::MouseUpL:

View File

@@ -101,6 +101,8 @@ void NodeCanvas::draw()
for (int plane_index = 0; plane_index < 6; plane_index++)
{
m_canvas->m_plane_unproject[plane_index] = glm::inverse(m_canvas->m_proj * m_canvas->m_mv * m_canvas->m_plane_transform[0]);
m_canvas->m_plane_dir[plane_index] = -(m_canvas->m_plane_transform[plane_index] * glm::vec4(m_canvas->m_plane_origin[plane_index], 1));
auto plane_mvp = proj * camera *
glm::scale(glm::vec3(m_canvas->m_order.size() + 500)) *
m_canvas->m_plane_transform[plane_index] *

View File

@@ -119,7 +119,7 @@ std::vector<vertex_t> poly_intersect(const std::vector<vertex_t>& poly, const st
glm::vec2 hit_uv;
segments_intersect(edge[0], edge[1], s[0].pos, s[1].pos, pt, hit_uv);
vertex_t v;
v.pos = glm::vec4(pt, 0, 1);
v.pos = glm::lerp(s[0].pos, s[1].pos, hit_uv.y);
v.uvs = glm::lerp(s[0].uvs, s[1].uvs, hit_uv.y);
v.uvs2 = glm::lerp(s[0].uvs2, s[1].uvs2, hit_uv.y);
if (side0) // outgoing