implement perspective correct interpolation to the 2D window space clipped brush stroke sample.
This commit is contained in:
@@ -423,15 +423,29 @@ void ui::Canvas::stroke_draw()
|
|||||||
+ dx + dy, // C - top-right
|
+ dx + dy, // C - top-right
|
||||||
+ dx - dy, // D - bottom-right
|
+ dx - dy, // D - bottom-right
|
||||||
};
|
};
|
||||||
static glm::vec4 P[4];
|
|
||||||
int intersected = 0;
|
int intersected = 0;
|
||||||
int inside = 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++)
|
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;
|
glm::vec3 ray_origin, ray_dir;
|
||||||
if (s.pos.z == 0)
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -451,19 +465,29 @@ void ui::Canvas::stroke_draw()
|
|||||||
{
|
{
|
||||||
inside++;
|
inside++;
|
||||||
}
|
}
|
||||||
P[j].x = -(plane_local.x * 0.5f - 0.5f) * m_width;
|
P[j].pos.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.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++;
|
intersected++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if (i==0)
|
|
||||||
// LOG("no intersection with plane %d", i);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intersected < 4 || inside == 0)
|
if (intersected < 3 || inside == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_dirty_face[i] = true;
|
m_dirty_face[i] = true;
|
||||||
@@ -473,13 +497,12 @@ void ui::Canvas::stroke_draw()
|
|||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing)
|
m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing)
|
||||||
|
|
||||||
|
|
||||||
glm::vec2 bb_min(m_width, m_height);
|
glm::vec2 bb_min(m_width, m_height);
|
||||||
glm::vec2 bb_max(0, 0);
|
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_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])));
|
bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, xy(P[j].pos)));
|
||||||
}
|
}
|
||||||
auto bb_sz = bb_max - bb_min;
|
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))
|
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_mat4(kShaderUniform::MVP, ortho_proj);
|
||||||
ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(s.col, m_brush.m_tip_color.a));
|
ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(s.col, m_brush.m_tip_color.a));
|
||||||
ShaderManager::u_float(kShaderUniform::Alpha, s.flow);
|
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);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
m_tex[i].unbind();
|
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_sampler_mix.create(GL_NEAREST, GL_REPEAT);
|
||||||
m_plane.create<1>(1, 1);
|
m_plane.create<1>(1, 1);
|
||||||
m_plane_brush.create<1>(1, 1);
|
m_plane_brush.create<1>(1, 1);
|
||||||
|
m_brush_shape.create();
|
||||||
m_mesh.create();
|
m_mesh.create();
|
||||||
m_brush_mix.create(8, 8);
|
m_brush_mix.create(8, 8);
|
||||||
for (auto& l : m_layers)
|
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);
|
auto pt_clip = m_proj * glm::vec4(p, 1);
|
||||||
pt_clip = pt_clip / pt_clip.w;
|
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);
|
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;
|
pt_screen.y = m_vp.w - pt_screen.y - 1;
|
||||||
points.push_back(pt_screen);
|
points.push_back(pt_screen);
|
||||||
}
|
}
|
||||||
return points;
|
return points;
|
||||||
@@ -2065,11 +2119,8 @@ std::vector<vertex_t> ui::Canvas::triangulate_simple(const std::vector<vertex_t>
|
|||||||
vertex_t vertex;
|
vertex_t vertex;
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
auto p = t->GetPoint(i);
|
auto index = std::distance(points.data(), t->GetPoint(i));
|
||||||
auto index = std::distance(points.data(), p);
|
ret.push_back(vertices[index]);
|
||||||
vertex.pos = glm::vec4(p->x, p->y, 0, 1);
|
|
||||||
vertex.uvs = vertices[index].uvs;
|
|
||||||
ret.push_back(vertex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ class Canvas
|
|||||||
public:
|
public:
|
||||||
Plane m_plane;
|
Plane m_plane;
|
||||||
Plane m_plane_brush;
|
Plane m_plane_brush;
|
||||||
|
DynamicShape m_brush_shape;
|
||||||
BrushMesh m_mesh;
|
BrushMesh m_mesh;
|
||||||
bool m_unsaved = false;
|
bool m_unsaved = false;
|
||||||
bool m_newdoc = true;
|
bool m_newdoc = true;
|
||||||
@@ -129,6 +130,8 @@ public:
|
|||||||
bool m_show_tmp = false;
|
bool m_show_tmp = false;
|
||||||
std::vector<Layer> m_layers;
|
std::vector<Layer> m_layers;
|
||||||
std::vector<int> m_order;
|
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));
|
glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0));
|
||||||
bool m_dirty_face[6] = SIXPLETTE(false);
|
bool m_dirty_face[6] = SIXPLETTE(false);
|
||||||
Layer m_smask; // selection mask
|
Layer m_smask; // selection mask
|
||||||
|
|||||||
@@ -874,9 +874,19 @@ void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj,
|
|||||||
m_circle.draw_fill();
|
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);
|
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();
|
m_shape.draw_fill();
|
||||||
|
tex.unbind();
|
||||||
|
|
||||||
if (depth) glEnable(GL_DEPTH_TEST);
|
if (depth) glEnable(GL_DEPTH_TEST);
|
||||||
}
|
}
|
||||||
@@ -887,6 +897,80 @@ void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
|||||||
{
|
{
|
||||||
case kEventType::MouseDownR:
|
case kEventType::MouseDownR:
|
||||||
//m_origin = canvas->project2Dpoint(loc);
|
//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;
|
break;
|
||||||
case kEventType::MouseMove:
|
case kEventType::MouseMove:
|
||||||
case kEventType::MouseUpL:
|
case kEventType::MouseUpL:
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ void NodeCanvas::draw()
|
|||||||
|
|
||||||
for (int plane_index = 0; plane_index < 6; plane_index++)
|
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 *
|
auto plane_mvp = proj * camera *
|
||||||
glm::scale(glm::vec3(m_canvas->m_order.size() + 500)) *
|
glm::scale(glm::vec3(m_canvas->m_order.size() + 500)) *
|
||||||
m_canvas->m_plane_transform[plane_index] *
|
m_canvas->m_plane_transform[plane_index] *
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ std::vector<vertex_t> poly_intersect(const std::vector<vertex_t>& poly, const st
|
|||||||
glm::vec2 hit_uv;
|
glm::vec2 hit_uv;
|
||||||
segments_intersect(edge[0], edge[1], s[0].pos, s[1].pos, pt, hit_uv);
|
segments_intersect(edge[0], edge[1], s[0].pos, s[1].pos, pt, hit_uv);
|
||||||
vertex_t v;
|
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.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);
|
v.uvs2 = glm::lerp(s[0].uvs2, s[1].uvs2, hit_uv.y);
|
||||||
if (side0) // outgoing
|
if (side0) // outgoing
|
||||||
|
|||||||
Reference in New Issue
Block a user