mirror of https://github.com/inolen/redream.git
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:
parent
f10ec6c693
commit
d3118a9ae8
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
"}";
|
||||
|
|
|
@ -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 =
|
||||
|
|
49
src/tracer.c
49
src/tracer.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue