DRW: Armature: New bone outline shader.

This fix the issue with the zfighting we were getting at bones edges.

Moreover, this enables us to render arbitrarly large outline with
varying thickness.
This commit is contained in:
Clément Foucault 2018-04-22 22:49:36 +02:00
parent 77b481fd5a
commit e493a1a1ae
9 changed files with 243 additions and 13 deletions

View File

@ -221,6 +221,8 @@ data_to_c_simple(modes/shaders/common_globals_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_view_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_fxaa_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_fullscreen_vert.glsl SRC)
data_to_c_simple(modes/shaders/armature_shape_outline_vert.glsl SRC)
data_to_c_simple(modes/shaders/armature_shape_outline_geom.glsl SRC)
data_to_c_simple(modes/shaders/edit_mesh_overlay_frag.glsl SRC)
data_to_c_simple(modes/shaders/edit_mesh_overlay_vert.glsl SRC)
data_to_c_simple(modes/shaders/edit_mesh_overlay_geom_tri.glsl SRC)

View File

@ -75,8 +75,10 @@ static struct {
/* Reset when changing current_armature */
DRWShadingGroup *bone_octahedral_solid;
DRWShadingGroup *bone_octahedral_wire;
DRWShadingGroup *bone_octahedral_outline;
DRWShadingGroup *bone_box_solid;
DRWShadingGroup *bone_box_wire;
DRWShadingGroup *bone_box_outline;
DRWShadingGroup *bone_wire_wire;
DRWShadingGroup *bone_envelope_solid;
DRWShadingGroup *bone_envelope_distance;
@ -88,6 +90,7 @@ static struct {
DRWShadingGroup *relationship_lines;
DRWPass *pass_bone_solid;
DRWPass *pass_bone_outline;
DRWPass *pass_bone_wire;
DRWPass *pass_bone_envelope;
} g_data = {NULL};
@ -114,10 +117,13 @@ static void drw_shgroup_bone_octahedral_wire(const float (*bone_mat)[4], const f
if (g_data.bone_octahedral_wire == NULL) {
struct Gwn_Batch *geom = DRW_cache_bone_octahedral_wire_outline_get();
g_data.bone_octahedral_wire = shgroup_instance_wire(g_data.pass_bone_wire, geom);
geom = DRW_cache_bone_octahedral_get();
g_data.bone_octahedral_outline = shgroup_instance_armature_shape_outline(g_data.pass_bone_outline, geom);
}
float final_bonemat[4][4];
mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
DRW_shgroup_call_dynamic_add(g_data.bone_octahedral_wire, final_bonemat, color);
DRW_shgroup_call_dynamic_add(g_data.bone_octahedral_outline, final_bonemat, color);
}
/* Box / B-Bone */
@ -137,10 +143,13 @@ static void drw_shgroup_bone_box_wire(const float (*bone_mat)[4], const float co
if (g_data.bone_box_wire == NULL) {
struct Gwn_Batch *geom = DRW_cache_bone_box_wire_outline_get();
g_data.bone_box_wire = shgroup_instance_wire(g_data.pass_bone_wire, geom);
geom = DRW_cache_bone_box_get();
g_data.bone_box_outline = shgroup_instance_armature_shape_outline(g_data.pass_bone_outline, geom);
}
float final_bonemat[4][4];
mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat);
DRW_shgroup_call_dynamic_add(g_data.bone_box_wire, final_bonemat, color);
DRW_shgroup_call_dynamic_add(g_data.bone_box_outline, final_bonemat, color);
}
/* Wire */
@ -1416,13 +1425,16 @@ static void draw_armature_pose(Object *ob, const float const_color[4])
* This function set the object space to use for all subsequent `DRW_shgroup_bone_*` calls.
*/
static void drw_shgroup_armature(
Object *ob, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope,
Object *ob,
DRWPass *pass_bone_solid, DRWPass *pass_bone_outline,
DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope,
DRWShadingGroup *shgrp_relationship_lines)
{
memset(&g_data, 0x0, sizeof(g_data));
g_data.ob = ob;
g_data.pass_bone_solid = pass_bone_solid;
g_data.pass_bone_outline = pass_bone_outline;
g_data.pass_bone_wire = pass_bone_wire;
g_data.pass_bone_envelope = pass_bone_envelope;
g_data.relationship_lines = shgrp_relationship_lines;
@ -1431,29 +1443,35 @@ static void drw_shgroup_armature(
}
void DRW_shgroup_armature_object(
Object *ob, ViewLayer *view_layer, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *UNUSED(pass_bone_envelope),
Object *ob, ViewLayer *view_layer,
DRWPass *pass_bone_solid, DRWPass *pass_bone_outline,
DRWPass *pass_bone_wire, DRWPass *UNUSED(pass_bone_envelope),
DRWShadingGroup *shgrp_relationship_lines)
{
float *color;
DRW_object_wire_theme_get(ob, view_layer, &color);
drw_shgroup_armature(ob, pass_bone_solid, pass_bone_wire, NULL, shgrp_relationship_lines);
drw_shgroup_armature(ob, pass_bone_solid, pass_bone_outline, pass_bone_wire, NULL, shgrp_relationship_lines);
draw_armature_pose(ob, color);
}
void DRW_shgroup_armature_pose(
Object *ob, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope,
Object *ob,
DRWPass *pass_bone_solid, DRWPass *pass_bone_outline,
DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope,
DRWShadingGroup *shgrp_relationship_lines)
{
drw_shgroup_armature(ob, pass_bone_solid, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines);
drw_shgroup_armature(ob, pass_bone_solid, pass_bone_outline, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines);
draw_armature_pose(ob, NULL);
}
void DRW_shgroup_armature_edit(
Object *ob, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope,
Object *ob,
DRWPass *pass_bone_solid, DRWPass *pass_bone_outline,
DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope,
DRWShadingGroup *shgrp_relationship_lines)
{
drw_shgroup_armature(ob, pass_bone_solid, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines);
drw_shgroup_armature(ob, pass_bone_solid, pass_bone_outline, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines);
draw_armature_edit(ob);
}

View File

@ -156,6 +156,14 @@ void DRW_globals_update(void)
/* ********************************* SHGROUP ************************************* */
extern char datatoc_armature_shape_outline_vert_glsl[];
extern char datatoc_armature_shape_outline_geom_glsl[];
extern char datatoc_gpu_shader_flat_color_frag_glsl[];
static struct {
struct GPUShader *shape_outline;
} g_armature_shaders = {NULL};
static struct {
struct Gwn_VertFormat *instance_screenspace;
struct Gwn_VertFormat *instance_color;
@ -178,6 +186,11 @@ void DRW_globals_free(void)
for (int i = 0; i < sizeof(g_formats) / sizeof(void *); ++i, ++format) {
MEM_SAFE_FREE(*format);
}
struct GPUShader **shader = &g_armature_shaders.shape_outline;
for (int i = 0; i < sizeof(g_armature_shaders) / sizeof(void *); ++i, ++shader) {
DRW_SHADER_FREE_SAFE(*shader);
}
}
DRWShadingGroup *shgroup_dynlines_uniform_color(DRWPass *pass, float color[4])
@ -454,6 +467,32 @@ DRWShadingGroup *shgroup_instance_mball_handles(DRWPass *pass, struct Gwn_Batch
return grp;
}
/* Only works with batches with adjacency infos. */
DRWShadingGroup *shgroup_instance_armature_shape_outline(DRWPass *pass, struct Gwn_Batch *geom)
{
if (g_armature_shaders.shape_outline == NULL) {
g_armature_shaders.shape_outline = DRW_shader_create(
datatoc_armature_shape_outline_vert_glsl,
datatoc_armature_shape_outline_geom_glsl,
datatoc_gpu_shader_flat_color_frag_glsl,
NULL);
}
/* TODO own format? */
DRW_shgroup_instance_format(g_formats.instance_color, {
{"InstanceModelMatrix", DRW_ATTRIB_FLOAT, 16},
{"color" , DRW_ATTRIB_FLOAT, 4}
});
DRWShadingGroup *grp = DRW_shgroup_instance_create(g_armature_shaders.shape_outline,
pass, geom, g_formats.instance_color);
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
return grp;
}
/* ******************************************** COLOR UTILS *********************************************** */

View File

@ -121,6 +121,7 @@ struct DRWShadingGroup *shgroup_spot_instance(struct DRWPass *pass, struct Gwn_B
struct DRWShadingGroup *shgroup_instance_bone_envelope_wire(struct DRWPass *pass, struct Gwn_Batch *geom);
struct DRWShadingGroup *shgroup_instance_bone_envelope_solid(struct DRWPass *pass, struct Gwn_Batch *geom);
struct DRWShadingGroup *shgroup_instance_mball_handles(struct DRWPass *pass, struct Gwn_Batch *geom);
struct DRWShadingGroup *shgroup_instance_armature_shape_outline(struct DRWPass *pass, struct Gwn_Batch *geom);
int DRW_object_wire_theme_get(
struct Object *ob, struct ViewLayer *view_layer, float **r_color);
@ -129,17 +130,20 @@ float *DRW_color_background_blend_get(int theme_id);
/* draw_armature.c */
void DRW_shgroup_armature_object(
struct Object *ob, struct ViewLayer *view_layer,
struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope,
struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_outline,
struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope,
struct DRWShadingGroup *shgrp_relationship_lines);
void DRW_shgroup_armature_pose(
struct Object *ob,
struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope,
struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_outline,
struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope,
struct DRWShadingGroup *shgrp_relationship_lines);
void DRW_shgroup_armature_edit(
struct Object *ob,
struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope,
struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_outline,
struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope,
struct DRWShadingGroup *shgrp_relationship_lines);
/* pose_mode.c */

View File

@ -38,6 +38,7 @@ extern GlobalsUboStorage ts;
typedef struct EDIT_ARMATURE_PassList {
struct DRWPass *bone_solid;
struct DRWPass *bone_wire;
struct DRWPass *bone_outline;
struct DRWPass *bone_envelope;
struct DRWPass *relationship;
} EDIT_ARMATURE_PassList;
@ -78,6 +79,12 @@ static void EDIT_ARMATURE_cache_init(void *vedata)
psl->bone_solid = DRW_pass_create("Bone Solid Pass", state);
}
{
/* Bones Outline */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
psl->bone_outline = DRW_pass_create("Bone Outline Pass", state);
}
{
/* Wire bones */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
@ -112,7 +119,8 @@ static void EDIT_ARMATURE_cache_populate(void *vedata, Object *ob)
if (ob->type == OB_ARMATURE) {
if (arm->edbo) {
DRW_shgroup_armature_edit(
ob, psl->bone_solid, psl->bone_wire, psl->bone_envelope, stl->g_data->relationship_lines);
ob, psl->bone_solid, psl->bone_outline, psl->bone_wire,
psl->bone_envelope, stl->g_data->relationship_lines);
}
}
}
@ -125,6 +133,7 @@ static void EDIT_ARMATURE_draw_scene(void *vedata)
MULTISAMPLE_SYNC_ENABLE(dfbl)
DRW_draw_pass(psl->bone_envelope);
DRW_draw_pass(psl->bone_outline);
DRW_draw_pass(psl->bone_solid);
DRW_draw_pass(psl->bone_wire);
DRW_draw_pass(psl->relationship);

View File

@ -101,6 +101,7 @@ typedef struct OBJECT_PassList {
struct DRWPass *outlines_resolve;
struct DRWPass *grid;
struct DRWPass *bone_solid;
struct DRWPass *bone_outline;
struct DRWPass *bone_wire;
struct DRWPass *bone_envelope;
struct DRWPass *particle;
@ -1040,6 +1041,7 @@ static void OBJECT_cache_init(void *vedata)
/* Solid bones */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
psl->bone_solid = DRW_pass_create("Bone Solid Pass", state);
psl->bone_outline = DRW_pass_create("Bone Outline Pass", state);
}
{
@ -2141,7 +2143,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob)
if (arm->edbo == NULL) {
if (DRW_state_is_select() || !DRW_pose_mode_armature(ob, draw_ctx->obact)) {
DRW_shgroup_armature_object(
ob, view_layer, psl->bone_solid, psl->bone_wire, psl->bone_envelope,
ob, view_layer, psl->bone_solid, psl->bone_outline, psl->bone_wire, psl->bone_envelope,
stl->g_data->relationship_lines);
}
}

View File

@ -44,6 +44,7 @@ extern GlobalsUboStorage ts;
typedef struct POSE_PassList {
struct DRWPass *bone_solid;
struct DRWPass *bone_outline;
struct DRWPass *bone_wire;
struct DRWPass *bone_envelope;
struct DRWPass *relationship;
@ -87,6 +88,12 @@ static void POSE_cache_init(void *vedata)
psl->bone_solid = DRW_pass_create("Bone Solid Pass", state);
}
{
/* Bones Outline */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
psl->bone_outline = DRW_pass_create("Bone Outline Pass", state);
}
{
/* Wire bones */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND;
@ -125,7 +132,7 @@ static void POSE_cache_populate(void *vedata, Object *ob)
if (ob->type == OB_ARMATURE) {
if (DRW_pose_mode_armature(ob, draw_ctx->obact)) {
DRW_shgroup_armature_pose(
ob, psl->bone_solid, psl->bone_wire, psl->bone_envelope,
ob, psl->bone_solid, psl->bone_outline, psl->bone_wire, psl->bone_envelope,
stl->g_data->relationship_lines);
}
}
@ -161,6 +168,7 @@ static void POSE_draw_scene(void *vedata)
POSE_PassList *psl = ((POSE_Data *)vedata)->psl;
DRW_draw_pass(psl->bone_envelope);
DRW_draw_pass(psl->bone_outline);
DRW_draw_pass(psl->bone_wire);
DRW_draw_pass(psl->bone_solid);
DRW_draw_pass(psl->relationship);

View File

@ -0,0 +1,95 @@
/* TODO: See perf with multiple invocations. */
layout(triangles_adjacency) in;
layout(triangle_strip, max_vertices = 16) out;
in vec4 pPos[];
in float vZ[];
in float vFacing[];
in vec2 ssPos[];
in vec2 ssNor[];
in vec4 vCol[];
flat out vec4 finalColor;
uniform mat4 ProjectionMatrix;
uniform vec2 viewportSize;
uniform float lineThickness = 3.0;
vec2 compute_dir(vec2 v0, vec2 v1)
{
vec2 dir = normalize(v1 - v0);
dir = vec2(-dir.y, dir.x);
return dir;
}
void emit_edge(const ivec3 edges, vec2 thick, bool is_persp)
{
vec2 edge_dir = compute_dir(ssPos[edges.x], ssPos[edges.y]);
vec2 hidden_dir = normalize(ssPos[edges.z] - ssPos[edges.x]);
float fac = dot(-hidden_dir, edge_dir);
vec2 t = thick * (is_persp ? vZ[edges.x] : 1.0);
gl_Position = pPos[edges.x];
EmitVertex();
gl_Position.xy += t * edge_dir * sign(fac);
EmitVertex();
t = thick * (is_persp ? vZ[edges.y] : 1.0);
gl_Position = pPos[edges.y];
EmitVertex();
gl_Position.xy += t * edge_dir * sign(fac);
EmitVertex();
}
void emit_corner(const int e, vec2 thick, bool is_persp)
{
vec2 corner_dir = ssNor[e];
vec2 t = thick * (is_persp ? vZ[e] : 1.0);
gl_Position = pPos[e] + vec4(t * corner_dir, 0.0, 0.0);
EmitVertex();
}
void main(void)
{
finalColor = vCol[0];
vec2 thick = lineThickness / viewportSize;
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
const ivec3 edges = ivec3(0, 2, 4);
vec4 facing = vec4(vFacing[1], vFacing[3], vFacing[5], vFacing[0]);
bvec4 do_edge = greaterThanEqual(facing, vec4(0.0));
/* Only generate outlines from backfaces. */
if (do_edge.w)
return;
if (do_edge.x) {
emit_corner(edges.x, thick, is_persp);
emit_edge(edges.xyz, thick, is_persp);
}
if (any(do_edge.xy)) {
emit_corner(edges.y, thick, is_persp);
}
if (do_edge.y) {
emit_edge(edges.yzx, thick, is_persp);
}
else {
EndPrimitive();
}
if (any(do_edge.yz)) {
emit_corner(edges.z, thick, is_persp);
}
if (do_edge.z) {
emit_edge(edges.zxy, thick, is_persp);
emit_corner(edges.x, thick, is_persp);
}
EndPrimitive();
}

View File

@ -0,0 +1,53 @@
uniform mat3 NormalMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectionMatrix;
uniform vec2 viewportSize;
/* ---- Instanciated Attribs ---- */
in vec3 pos;
in vec3 nor;
in vec3 snor;
/* ---- Per instance Attribs ---- */
in mat4 InstanceModelMatrix;
in vec4 color;
out vec4 pPos;
out float vZ;
out float vFacing;
out vec2 ssPos;
out vec2 ssNor;
out vec4 vCol;
/* project to screen space */
vec2 proj(vec4 pos)
{
return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize;
}
void main()
{
/* This is slow and run per vertex, but it's still faster than
* doing it per instance on CPU and sending it on via instance attrib */
mat3 NormalMatrix = transpose(inverse(mat3(ViewMatrix * InstanceModelMatrix)));
vec4 viewpos = ViewMatrix * (InstanceModelMatrix * vec4(pos, 1.0));
pPos = ProjectionMatrix * viewpos;
vZ = abs(viewpos.z);
/* if perspective */
vec3 V = (ProjectionMatrix[3][3] == 0.0) ? normalize(-viewpos.xyz) : vec3(0.0, 0.0, 1.0);
/* TODO FIX: there is still a problem with this vector
* when the bone is scaled or in persp mode. But it's
* barelly visible at the outline corners. */
ssNor = normalize((NormalMatrix * snor).xy);
vFacing = dot(V, normalize(NormalMatrix * nor));
ssPos = proj(pPos);
vCol = color;
}