View 3D Grid: Improve precision and reduce code complexity

Instead of doing manual ray-plane intersection we use normalized positions
of the grid mesh and apply scaling after interpolation so that we keep
good precision even at really far distances.

Precision is now two order of magnitude better and does not produce the
same kind of artifact at lower clip start values.

This commit also cleanup the implementation.

Fixes T58918 Grid not appearing correctly at low clip start in 2.8
This commit is contained in:
Clément Foucault 2019-01-18 16:29:52 +01:00
parent bb4c760fdd
commit b5bc2158a0
Notes: blender-bot 2023-02-14 08:06:38 +01:00
Referenced by issue #58918, Grid not appearing correctly at low clip start in 2.8
3 changed files with 31 additions and 104 deletions

View File

@ -326,14 +326,12 @@ static struct {
GPUShader *lightprobe_grid_sh;
GPUShader *loose_points_sh;
float camera_pos[3];
float screenvecs[3][4];
float grid_settings[5];
float grid_mesh_size;
int grid_flag;
float grid_normal[3];
float grid_axes[3];
int zpos_flag;
int zneg_flag;
float zplane_normal[3];
float zplane_axes[3];
float inv_viewport_size[2];
bool draw_grid;
@ -565,38 +563,10 @@ static void OBJECT_engine_init(void *vedata)
}
}
e_data.grid_normal[0] = (float)((e_data.grid_flag & PLANE_YZ) != 0);
e_data.grid_normal[1] = (float)((e_data.grid_flag & PLANE_XZ) != 0);
e_data.grid_normal[2] = (float)((e_data.grid_flag & PLANE_XY) != 0);
e_data.grid_axes[0] = (float)((e_data.grid_flag & (PLANE_XZ | PLANE_XY)) != 0);
e_data.grid_axes[1] = (float)((e_data.grid_flag & (PLANE_YZ | PLANE_XY)) != 0);
e_data.grid_axes[2] = (float)((e_data.grid_flag & (PLANE_YZ | PLANE_XZ)) != 0);
/* Vectors to recover pixel world position. Fix grid precision issue. */
/* Using pixel at z = 0.0f in ndc space : gives average precision between
* near and far plane. Note that it might not be the best choice. */
copy_v4_fl4(e_data.screenvecs[0], 1.0f, -1.0f, 0.0f, 1.0f);
copy_v4_fl4(e_data.screenvecs[1], -1.0f, 1.0f, 0.0f, 1.0f);
copy_v4_fl4(e_data.screenvecs[2], -1.0f, -1.0f, 0.0f, 1.0f);
for (int i = 0; i < 3; i++) {
/* Doing 2 steps to recover world position of the corners of the frustum.
* Using the inverse perspective matrix is giving very low precision output. */
mul_m4_v4(invwinmat, e_data.screenvecs[i]);
e_data.screenvecs[i][0] /= e_data.screenvecs[i][3]; /* perspective divide */
e_data.screenvecs[i][1] /= e_data.screenvecs[i][3]; /* perspective divide */
e_data.screenvecs[i][2] /= e_data.screenvecs[i][3]; /* perspective divide */
e_data.screenvecs[i][3] = 1.0f;
/* main instability come from this one */
/* TODO : to make things even more stable, don't use
* invviewmat and derive vectors from camera properties */
mul_m4_v4(invviewmat, e_data.screenvecs[i]);
}
sub_v3_v3(e_data.screenvecs[0], e_data.screenvecs[2]);
sub_v3_v3(e_data.screenvecs[1], e_data.screenvecs[2]);
/* Z axis if needed */
if (((rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO)) && show_axis_z) {
e_data.zpos_flag = SHOW_AXIS_Z;
@ -627,10 +597,6 @@ static void OBJECT_engine_init(void *vedata)
e_data.zneg_flag |= CLIP_ZPOS;
}
e_data.zplane_normal[0] = (float)((e_data.zpos_flag & PLANE_YZ) != 0);
e_data.zplane_normal[1] = (float)((e_data.zpos_flag & PLANE_XZ) != 0);
e_data.zplane_normal[2] = (float)((e_data.zpos_flag & PLANE_XY) != 0);
e_data.zplane_axes[0] = (float)((e_data.zpos_flag & (PLANE_XZ | PLANE_XY)) != 0);
e_data.zplane_axes[1] = (float)((e_data.zpos_flag & (PLANE_YZ | PLANE_XY)) != 0);
e_data.zplane_axes[2] = (float)((e_data.zpos_flag & (PLANE_YZ | PLANE_XZ)) != 0);
@ -654,6 +620,14 @@ static void OBJECT_engine_init(void *vedata)
e_data.grid_settings[2] = grid_scale; /* gridScale */
e_data.grid_settings[3] = v3d->gridsubdiv; /* gridSubdiv */
e_data.grid_settings[4] = (v3d->gridsubdiv > 1) ? 1.0f / logf(v3d->gridsubdiv) : 0.0f; /* 1/log(gridSubdiv) */
if (winmat[3][3] == 0.0f) {
e_data.grid_mesh_size = dist;
}
else {
float viewdist = 1.0f / min_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1]));
e_data.grid_mesh_size = viewdist * dist;
}
}
copy_v2_v2(e_data.inv_viewport_size, DRW_viewport_size_get());
@ -1109,21 +1083,18 @@ static void OBJECT_cache_init(void *vedata)
/* Create 3 quads to render ordered transparency Z axis */
DRWShadingGroup *grp = DRW_shgroup_create(e_data.grid_sh, psl->grid);
DRW_shgroup_uniform_int(grp, "gridFlag", &e_data.zneg_flag, 1);
DRW_shgroup_uniform_vec3(grp, "planeNormal", e_data.zplane_normal, 1);
DRW_shgroup_uniform_vec3(grp, "planeAxes", e_data.zplane_axes, 1);
DRW_shgroup_uniform_vec3(grp, "cameraPos", e_data.camera_pos, 1);
DRW_shgroup_uniform_vec4(grp, "screenvecs[0]", e_data.screenvecs[0], 3);
DRW_shgroup_uniform_vec4(grp, "gridSettings", e_data.grid_settings, 1);
DRW_shgroup_uniform_float_copy(grp, "lineKernel", grid_line_size);
DRW_shgroup_uniform_float_copy(grp, "meshSize", e_data.grid_mesh_size);
DRW_shgroup_uniform_float(grp, "gridOneOverLogSubdiv", &e_data.grid_settings[4], 1);
DRW_shgroup_uniform_block(grp, "globalsBlock", globals_ubo);
DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_call_add(grp, geom, mat);
grp = DRW_shgroup_create(e_data.grid_sh, psl->grid);
DRW_shgroup_uniform_int(grp, "gridFlag", &e_data.grid_flag, 1);
DRW_shgroup_uniform_vec3(grp, "planeNormal", e_data.grid_normal, 1);
DRW_shgroup_uniform_vec3(grp, "planeAxes", e_data.grid_axes, 1);
DRW_shgroup_uniform_block(grp, "globalsBlock", globals_ubo);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
@ -1131,7 +1102,6 @@ static void OBJECT_cache_init(void *vedata)
grp = DRW_shgroup_create(e_data.grid_sh, psl->grid);
DRW_shgroup_uniform_int(grp, "gridFlag", &e_data.zpos_flag, 1);
DRW_shgroup_uniform_vec3(grp, "planeNormal", e_data.zplane_normal, 1);
DRW_shgroup_uniform_vec3(grp, "planeAxes", e_data.zplane_axes, 1);
DRW_shgroup_uniform_block(grp, "globalsBlock", globals_ubo);
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);

View File

@ -2,16 +2,18 @@
/* Infinite grid
* Author: Clément Foucault */
/* We use the normalized local position to avoid precision
* loss during interpolation. */
in vec3 local_pos;
out vec4 FragColor;
uniform mat4 ProjectionMatrix;
uniform vec3 cameraPos;
uniform vec3 planeNormal;
uniform vec3 planeAxes;
uniform vec3 eye;
uniform vec4 gridSettings;
uniform vec2 viewportSize;
uniform vec4 screenvecs[3];
uniform float meshSize;
uniform float lineKernel = 0.0;
uniform float gridOneOverLogSubdiv;
uniform sampler2D depthBuffer;
@ -70,52 +72,11 @@ vec3 get_axes(vec3 co, vec3 fwidthCos, float line_size)
return 1.0 - smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, axes_domain - (line_size + lineKernel));
}
vec3 get_floor_pos(vec2 uv, out vec3 wPos)
{
vec3 camera_vec, camera_pos, corner_pos;
vec3 floored_pos = planeAxes * floor(screenvecs[2].xyz);
corner_pos = screenvecs[2].xyz - floored_pos;
vec3 pixel_pos = corner_pos + uv.x * screenvecs[0].xyz + uv.y * screenvecs[1].xyz;
/* if perspective */
if (ProjectionMatrix[3][3] == 0.0) {
camera_pos = cameraPos - floored_pos;
camera_vec = normalize(pixel_pos - camera_pos);
}
else {
camera_pos = pixel_pos;
camera_vec = normalize(eye);
}
float plane_normal_dot_camera_vec = dot(planeNormal, camera_vec);
float p = -dot(planeNormal, camera_pos);
if (plane_normal_dot_camera_vec != 0) {
p /= plane_normal_dot_camera_vec;
}
vec3 plane = camera_pos + camera_vec * p;
/* fix residual imprecision */
plane *= planeAxes;
/* Recover non-offseted world position */
wPos = plane + floored_pos;
return plane;
}
void main()
{
vec2 sPos = gl_FragCoord.xy / viewportSize; /* Screen [0,1] position */
/* To reduce artifacts, use a local version of the positions
* to compute derivatives since they are not position dependent.
* This gets rid of the blocky artifacts. Unfortunately we still
* need the world position for the grid to scale properly from the origin. */
vec3 gPos, wPos; /* Grid pos., World pos. */
gPos = get_floor_pos(sPos, wPos);
vec3 fwidthPos = fwidth(gPos);
vec3 wPos = local_pos * meshSize;
vec3 fwidthPos = fwidth(wPos);
wPos += cameraPos * planeAxes;
float dist, fade;
/* if persp */
@ -228,7 +189,7 @@ void main()
/* Add a small bias so the grid will always
* be on top of a mesh with the same depth. */
float grid_depth = gl_FragCoord.z - 6e-8 - fwidth(gl_FragCoord.z);
float scene_depth = texture(depthBuffer, sPos).r;
float scene_depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
if ((gridFlag & GRID_BACK) != 0) {
fade *= (scene_depth == 1.0) ? 1.0 : 0.0;
}

View File

@ -5,7 +5,9 @@
uniform mat4 ViewProjectionMatrix;
uniform mat4 ProjectionMatrix;
uniform vec3 cameraPos;
uniform vec3 planeAxes;
uniform vec4 gridSettings;
uniform float meshSize;
#define gridDistance gridSettings.x
#define gridResolution gridSettings.y
@ -22,42 +24,36 @@ uniform int gridFlag;
in vec3 pos;
out vec3 local_pos;
void main()
{
vec3 vert_pos, proj_camera_pos;
vec3 vert_pos;
/* Project camera pos to the needed plane */
if ((gridFlag & PLANE_XY) != 0) {
vert_pos = vec3(pos.x, pos.y, 0.0);
proj_camera_pos = vec3(cameraPos.x, cameraPos.y, 0.0);
}
else if ((gridFlag & PLANE_XZ) != 0) {
vert_pos = vec3(pos.x, 0.0, pos.y);
proj_camera_pos = vec3(cameraPos.x, 0.0, cameraPos.z);
}
else {
vert_pos = vec3(0.0, pos.x, pos.y);
proj_camera_pos = vec3(0.0, cameraPos.y, cameraPos.z);
}
/* if persp */
if (ProjectionMatrix[3][3] == 0.0) {
vert_pos *= gridDistance * 2.0;
}
else {
float viewdist = 1.0 / min(abs(ProjectionMatrix[0][0]), abs(ProjectionMatrix[1][1]));
vert_pos *= viewdist * gridDistance * 2.0;
}
local_pos = vert_pos;
vec3 realPos = proj_camera_pos + vert_pos;
vec3 real_pos = cameraPos * planeAxes + vert_pos * meshSize;
/* Used for additional Z axis */
if ((gridFlag & CLIP_Z_POS) != 0) {
realPos.z = clamp(realPos.z, 0.0, 1e30);
real_pos.z = clamp(real_pos.z, 0.0, 1e30);
local_pos.z = clamp(local_pos.z, 0.0, 1.0);
}
if ((gridFlag & CLIP_Z_NEG) != 0) {
realPos.z = clamp(realPos.z, -1e30, 0.0);
real_pos.z = clamp(real_pos.z, -1e30, 0.0);
local_pos.z = clamp(local_pos.z, -1.0, 0.0);
}
gl_Position = ViewProjectionMatrix * vec4(realPos, 1.0);
gl_Position = ViewProjectionMatrix * vec4(real_pos, 1.0);
}