perform a perspective-correct interpolation of each vertex's w component

in the ta shader

explicitly write out a logarithmic value to the depth buffer inside of
the fragment shader based on the interpolated w componet. using the
log2(1 + w) / log2(max) provides much better precision in the mantissa
compared to w / wmax when normalizing the w values to be written to the
depth buffer
This commit is contained in:
Anthony Pesch 2017-05-21 16:02:46 -04:00
parent f10ec6c693
commit d3118a9ae8
7 changed files with 101 additions and 117 deletions

View File

@ -395,15 +395,11 @@ static void tr_discard_incomplete_surf(struct tr *tr, struct tr_context *rc) {
/*
* polygon parsing helpers
*/
#define PARSE_XYZ(x, y, z, xyz) \
{ \
xyz[0] = (x); \
xyz[1] = (y); \
xyz[2] = (z); \
if (isfinite(z)) { \
rc->minz = MIN(rc->minz, xyz[2]); \
rc->maxz = MAX(rc->maxz, xyz[2]); \
} \
#define PARSE_XYZ(x, y, z, xyz) \
{ \
xyz[0] = (x); \
xyz[1] = (y); \
xyz[2] = (z); \
}
#define PARSE_COLOR(base_color, color) \
@ -872,37 +868,6 @@ static void tr_parse_eol(struct tr *tr, const struct tile_context *ctx,
tr->vertex_type = TA_NUM_VERTS;
}
static void tr_proj_mat(struct tr *tr, const struct tile_context *ctx,
struct tr_context *rc) {
/* this isn't a traditional projection matrix. xy components coming into the
TA are in window space, while the z component is 1/w with +z headed into
the screen. these coordinates need to be scaled back into ndc space, and
z needs to be flipped so that -z is headed into the screen */
/* fudge so z isn't mapped to exactly 0.0 and 1.0 */
float zdepth = (rc->maxz - rc->minz) * 1.2f;
rc->projection[0] = 2.0f / (float)ctx->video_width;
rc->projection[4] = 0.0f;
rc->projection[8] = 0.0f;
rc->projection[12] = -1.0f;
rc->projection[1] = 0.0f;
rc->projection[5] = -2.0f / (float)ctx->video_height;
rc->projection[9] = 0.0f;
rc->projection[13] = 1.0f;
rc->projection[2] = 0.0f;
rc->projection[6] = 0.0f;
rc->projection[10] = 2.0f / -zdepth;
rc->projection[14] = -2.0f * (rc->maxz / -zdepth) - 1.0f;
rc->projection[3] = 0.0f;
rc->projection[7] = 0.0f;
rc->projection[11] = 0.0f;
rc->projection[15] = 1.0f;
}
static void tr_reset(struct tr *tr, struct tr_context *rc) {
/* reset global state */
tr->last_poly = NULL;
@ -911,8 +876,6 @@ static void tr_reset(struct tr *tr, struct tr_context *rc) {
tr->vertex_type = TA_NUM_VERTS;
/* reset render context state */
rc->minz = FLT_MAX;
rc->maxz = -FLT_MAX;
rc->num_params = 0;
rc->num_surfs = 0;
rc->num_verts = 0;
@ -923,31 +886,47 @@ static void tr_reset(struct tr *tr, struct tr_context *rc) {
}
static void tr_render_list(struct render_backend *r,
const struct tr_context *rc, int list_type) {
const struct tr_context *rc, int list_type, int end_surf, int *stopped) {
if (*stopped) {
return;
}
const struct tr_list *list = &rc->lists[list_type];
const int *sorted_surf = list->surfs;
const int *sorted_surf_end = list->surfs + list->num_surfs;
while (sorted_surf < sorted_surf_end) {
r_draw_ta_surface(r, &rc->surfs[*sorted_surf]);
sorted_surf++;
int surf = *(sorted_surf++);
r_draw_ta_surface(r, &rc->surfs[surf]);
if (surf == end_surf) {
*stopped = 1;
break;
}
}
}
void tr_render_context(struct render_backend *r, const struct tr_context *rc) {
PROF_ENTER("gpu", "tr_render_context");
void tr_render_context_until(struct render_backend *r, const struct tr_context *rc, int end_surf) {
PROF_ENTER("gpu", "tr_render_context_until");
r_begin_ta_surfaces(r, rc->projection, rc->verts, rc->num_verts);
int stopped = 0;
tr_render_list(r, rc, TA_LIST_OPAQUE);
tr_render_list(r, rc, TA_LIST_PUNCH_THROUGH);
tr_render_list(r, rc, TA_LIST_TRANSLUCENT);
r_begin_ta_surfaces(r, rc->width, rc->height, rc->verts, rc->num_verts);
tr_render_list(r, rc, TA_LIST_OPAQUE, end_surf, &stopped);
tr_render_list(r, rc, TA_LIST_PUNCH_THROUGH, end_surf, &stopped);
tr_render_list(r, rc, TA_LIST_TRANSLUCENT, end_surf, &stopped);
r_end_ta_surfaces(r);
PROF_LEAVE();
}
void tr_render_context(struct render_backend *r, const struct tr_context *rc) {
tr_render_context_until(r, rc, -1);
}
void tr_convert_context(struct render_backend *r, void *userdata,
tr_find_texture_cb find_texture,
const struct tile_context *ctx, struct tr_context *rc) {
@ -962,7 +941,12 @@ void tr_convert_context(struct render_backend *r, void *userdata,
const uint8_t *end = ctx->params + ctx->size;
ta_init_tables();
tr_reset(&tr, rc);
rc->width = ctx->video_width;
rc->height = ctx->video_height;
tr_parse_bg(&tr, ctx, rc);
while (data < end) {
@ -1014,7 +998,5 @@ void tr_convert_context(struct render_backend *r, void *userdata,
tr_sort_render_list(&tr, rc, TA_LIST_PUNCH_THROUGH);
}
tr_proj_mat(&tr, ctx, rc);
PROF_LEAVE();
}

View File

@ -52,9 +52,9 @@ struct tr_list {
};
struct tr_context {
/* transforms incoming windows space coordinates to ndc space */
float minz, maxz;
float projection[16];
/* original video dimensions, needed to project surfaces correctly */
int width;
int height;
/* parsed surfaces and vertices, ready to be passed to the render backend */
struct ta_surface surfs[TA_MAX_SURFS];
@ -81,5 +81,7 @@ void tr_convert_context(struct render_backend *r, void *userdata,
tr_find_texture_cb find_texture,
const struct tile_context *ctx, struct tr_context *rc);
void tr_render_context(struct render_backend *r, const struct tr_context *rc);
void tr_render_context_until(struct render_backend *r,
const struct tr_context *rc, int end_surf);
#endif

View File

@ -14,14 +14,15 @@ enum texture_map {
};
enum uniform_attr {
UNIFORM_MVP,
UNIFORM_PROJ,
UNIFORM_DIFFUSE,
UNIFORM_VIDEO_SCALE,
UNIFORM_PT_ALPHA_REF,
UNIFORM_NUM_UNIFORMS,
};
static const char *uniform_names[] = {
"u_mvp", "u_diffuse", "u_pt_alpha_ref",
"u_proj", "u_diffuse", "u_video_scale", "u_pt_alpha_ref",
};
enum shader_attr {
@ -101,7 +102,7 @@ struct render_backend {
/* global uniforms that are constant for every surface rendered between a call
to begin_surfaces and end_surfaces */
uint64_t uniform_token;
const float *uniform_mvp;
float uniform_video_scale[4];
};
#include "render/ta.glsl"
@ -498,7 +499,7 @@ void r_draw_ta_surface(struct render_backend *r,
/* bind global uniforms if they've changed */
if (program->uniform_token != r->uniform_token) {
glUniformMatrix4fv(program->loc[UNIFORM_MVP], 1, GL_FALSE, r->uniform_mvp);
glUniform4fv(program->loc[UNIFORM_VIDEO_SCALE], 1, r->uniform_video_scale);
program->uniform_token = r->uniform_token;
}
@ -512,11 +513,15 @@ void r_draw_ta_surface(struct render_backend *r,
glDrawArrays(GL_TRIANGLE_STRIP, surf->first_vert, surf->num_verts);
}
void r_begin_ta_surfaces(struct render_backend *r, const float *projection,
const struct ta_vertex *verts, int num_verts) {
void r_begin_ta_surfaces(struct render_backend *r, int video_width,
int video_height, const struct ta_vertex *verts,
int num_verts) {
/* uniforms will be lazily bound for each program inside of r_draw_surface */
r->uniform_token++;
r->uniform_mvp = projection;
r->uniform_video_scale[0] = 2.0f / (float)video_width;
r->uniform_video_scale[1] = -1.0f;
r->uniform_video_scale[2] = -2.0f / (float)video_height;
r->uniform_video_scale[3] = 1.0f;
glBindBuffer(GL_ARRAY_BUFFER, r->ta_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(struct ta_vertex) * num_verts, verts,
@ -593,7 +598,7 @@ void r_begin_ui_surfaces(struct render_backend *r,
struct shader_program *program = &r->ui_program;
glBindVertexArray(r->ui_vao);
glUseProgram(program->prog);
glUniformMatrix4fv(program->loc[UNIFORM_MVP], 1, GL_FALSE, ortho);
glUniformMatrix4fv(program->loc[UNIFORM_PROJ], 1, GL_FALSE, ortho);
/* bind buffers */
glBindBuffer(GL_ARRAY_BUFFER, r->ui_vbo);

View File

@ -166,8 +166,9 @@ void r_viewport(struct render_backend *r, int width, int height);
int r_viewport_width(struct render_backend *r);
int r_viewport_height(struct render_backend *r);
void r_begin_ta_surfaces(struct render_backend *r, const float *projection,
const struct ta_vertex *verts, int num_verts);
void r_begin_ta_surfaces(struct render_backend *r, int video_width,
int video_height, const struct ta_vertex *verts,
int num_verts);
void r_draw_ta_surface(struct render_backend *r, const struct ta_surface *surf);
void r_end_ta_surfaces(struct render_backend *r);

View File

@ -1,5 +1,5 @@
static const char *ta_vp =
"uniform mat4 u_mvp;\n"
"uniform vec4 u_video_scale;\n"
"layout(location = 0) in vec3 attr_xyz;\n"
"layout(location = 1) in vec2 attr_texcoord;\n"
@ -15,14 +15,17 @@ static const char *ta_vp =
" var_offset_color = attr_offset_color;\n"
" var_texcoord = attr_texcoord;\n"
" // convert from window space back into ndc space\n"
" gl_Position = u_mvp * vec4(attr_xyz, 1.0);\n"
" // scale x from [0,640] -> [-1,1] and y from [0,480] to [-1,1]\n"
" gl_Position.xy = attr_xyz.xy * u_video_scale.xz + u_video_scale.yw;\n"
" // specify w so OpenGL applies perspective corrected texture mapping, but\n"
" // cancel the perspective divide on the xyz, they're already in ndc space\n"
" float w = 1.0 / attr_xyz.z;\n"
" gl_Position.xyz *= w;\n"
" gl_Position.w = w;\n"
" // the z coordinate is actually 1/w, convert to w. note, there is no\n"
" // actual z coordinate provided to the ta, just 1/w. due to this, we set\n"
" // z = w in the vertex shader such that the clip test always passes, and\n"
" // then gl_FragDepth is manually set to w in the fragment shader\n"
" gl_Position.zw = 1.0f / attr_xyz.zz;\n"
" // cancel the perspective divide on the xy, they're already in ndc space\n"
" gl_Position.xy *= gl_Position.w;\n"
"}";
static const char *ta_fp =
@ -70,7 +73,29 @@ static const char *ta_fp =
" #ifdef OFFSET_COLOR\n"
" fragcolor.rgb += var_offset_color.rgb;\n"
" #endif\n"
" // gl_FragCoord.w is 1/clip.w aka the original 1/w passed to the TA,\n"
" // interpolated in screen space. this value is normally between [0,1],\n"
" // however, values very close to the near plane often raise to 10-100000\n"
" // unfortunately, there doesn't seem to exist a full 32-bit floating-point\n"
" // depth buffer. because of this, the depth value written out here must be\n"
" // normalized to [0,1] to satisfy OpenGL, which will then subsequently\n"
" // quantize it to a 24-bit integer\n"
" // if this value is normalized by (w - wmin) / (wmax - wmin), too much\n"
" // precision is ultimately lost in small w values by the time the value is\n"
" // written to the depth buffer. seeing that most values are between [0,1],\n"
" // with only a few outliers being > 1, writing out log2(w) / log2(wmax)\n"
" // works out well to preserve the precision in these smaller w values\n"
" // note, 2^17 was chosen as ~100000 was largest value i'd seen passed as\n"
" // the w component at the time this was written\n"
" float w = 1.0 / gl_FragCoord.w;\n"
" gl_FragDepth = log2(1.0 + w) / 17.0;\n"
" #ifdef DEBUG_DEPTH_BUFFER\n"
" fragcolor.rgb = vec3(gl_FragCoord.z);\n"
" fragcolor.rgb = vec3(gl_FragDepth);\n"
" #endif\n"
"}";

View File

@ -1,5 +1,5 @@
static const char *ui_vp =
"uniform mat4 u_mvp;\n"
"uniform mat4 u_proj;\n"
"layout(location = 0) in vec2 attr_xy;\n"
"layout(location = 1) in vec2 attr_texcoord;\n"
@ -11,7 +11,7 @@ static const char *ui_vp =
"void main() {\n"
" var_color = attr_color;\n"
" var_texcoord = attr_texcoord;\n"
" gl_Position = u_mvp * vec4(attr_xy, 0.0, 1.0);\n"
" gl_Position = u_proj * vec4(attr_xy, 0.0, 1.0);\n"
"}";
static const char *ui_fp =

View File

@ -748,29 +748,6 @@ static void tracer_render_side_menu(struct tracer *tracer) {
}
}
static void tracer_render_list(struct tracer *tracer,
const struct tr_context *rc, int list_type,
int end, int *stopped) {
if (*stopped) {
return;
}
const struct tr_list *list = &tracer->rc.lists[list_type];
const int *sorted_surf = list->surfs;
const int *sorted_surf_end = list->surfs + list->num_surfs;
while (sorted_surf < sorted_surf_end) {
int idx = *(sorted_surf++);
r_draw_ta_surface(tracer->r, &tracer->rc.surfs[idx]);
if (idx == end) {
*stopped = 1;
break;
}
}
}
static void tracer_input_mousemove(void *data, int port, int x, int y) {
struct tracer *tracer = data;
@ -802,31 +779,23 @@ void tracer_run_frame(struct tracer *tracer) {
nk_update_input(tracer->nk);
/* render ui */
/* build ui */
tracer_render_side_menu(tracer);
tracer_render_scrubber_menu(tracer);
tracer_render_debug_menu(tracer);
/* render context up to the surface of the currently selected param */
{
struct tr_context *rc = &tracer->rc;
int stopped = 0;
int end = -1;
struct tr_context *rc = &tracer->rc;
int end_surf = -1;
if (tracer->current_param >= 0) {
struct tr_param *rp = &rc->params[tracer->current_param];
end = rp->last_surf;
}
r_begin_ta_surfaces(tracer->r, rc->projection, rc->verts, rc->num_verts);
tracer_render_list(tracer, rc, TA_LIST_OPAQUE, end, &stopped);
tracer_render_list(tracer, rc, TA_LIST_PUNCH_THROUGH, end, &stopped);
tracer_render_list(tracer, rc, TA_LIST_TRANSLUCENT, end, &stopped);
r_end_ta_surfaces(tracer->r);
if (tracer->current_param >= 0) {
struct tr_param *rp = &rc->params[tracer->current_param];
end_surf = rp->last_surf;
}
tr_render_context_until(tracer->r, rc, end_surf);
/* render ui */
nk_render(tracer->nk);
}