From ba49d37e6a8396623d8c171c145ab1f1a5e5e0d4 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Tue, 17 Mar 2015 02:10:58 -0700 Subject: [PATCH] premultiplied alpha support --- stb_voxel_render.h | 137 +++++++++++++++++++++++++++++++---- tests/caveview/cave_mesher.c | 11 ++- 2 files changed, 130 insertions(+), 18 deletions(-) diff --git a/stb_voxel_render.h b/stb_voxel_render.h index b002437..7ee3681 100644 --- a/stb_voxel_render.h +++ b/stb_voxel_render.h @@ -1,7 +1,6 @@ // @TODO // // - API for texture rotation on side faces (& top&bottom ?) -// - premultiplied alpha // - edge clamp // - better culling of vheight faces with vheight neighbors // - better culling of non-vheight faces with fheight neighbors @@ -280,6 +279,7 @@ STBVXDEC void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_byt STBVXDEC stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm); STBVXDEC char *stbvox_get_vertex_shader(void); STBVXDEC char *stbvox_get_fragment_shader(void); +STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void); STBVXDEC void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh); STBVXDEC int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh); STBVXDEC void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]); @@ -754,7 +754,7 @@ static char *stbvox_vertex_encoderogram = // fragment output data "flat out uvec4 facedata;\n" - " out vec3 objectspace_pos;\n" + " out vec3 voxelspace_pos;\n" " out vec3 vnormal;\n" " out float texlerp;\n" " out float amb_occ;\n" @@ -778,8 +778,8 @@ static char *stbvox_vertex_encoderogram = " texlerp = float( (attr_vertex >> 29u) ) / 7.0;\n" // a[29..31] " vnormal = normal_table[(facedata.w>>2) & 31u];\n" - " objectspace_pos = offset * transform[0];\n" // object-to-world scale - " vec3 position = objectspace_pos + transform[1];\n" // object-to-world translate + " voxelspace_pos = offset * transform[0];\n" // mesh-to-object scale + " vec3 position = voxelspace_pos + transform[1];\n" // mesh-to-object translate #ifdef STBVOX_DEBUG_TEST_NORMALS " if ((facedata.w & 28u) == 16u || (facedata.w & 28u) == 24u)\n" @@ -812,7 +812,7 @@ static char *stbvox_fragment_program = // vertex-shader output data "flat in uvec4 facedata;\n" - " in vec3 objectspace_pos;\n" + " in vec3 voxelspace_pos;\n" " in vec3 vnormal;\n" " in float texlerp;\n" " in float amb_occ;\n" @@ -847,7 +847,7 @@ static char *stbvox_fragment_program = "vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient);\n" #endif #if defined(STBVOX_CONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) - "vec3 compute_fog(vec3 color, vec3 relative_pos);\n" + "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha);\n" #endif "void main()\n" @@ -878,7 +878,7 @@ static char *stbvox_fragment_program = " vec4 color = texelFetch(color_table, int(color_id & 63u));\n" #endif " vec2 texcoord;\n" - " vec3 texturespace_pos = objectspace_pos + transform[2].xyz;\n" + " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" " texcoord.s = dot(texturespace_pos, texgen_s);\n" " texcoord.t = dot(texturespace_pos, texgen_t);\n" @@ -893,13 +893,24 @@ static char *stbvox_fragment_program = " if ((color_id & 64u) != 0u) tex1.xyz *= color.xyz;\n" " if ((color_id & 128u) != 0u) tex2.xyz *= color.xyz;\n" + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " tex2.rgba *= texlerp;\n" + #else " tex2.a *= texlerp;\n" + #endif // @TODO: could use a separate lookup table keyed on tex2 to determine this " if (texblend_mode)\n" " albedo = tex2.xyz * rlerp(tex2.a, 2.0*tex1.xyz, vec3(1.0,1.0,1.0));\n" - " else\n" - " albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n" // @TODO premultiplied alpha + " else {\n" + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " albedo = (1.0-tex2.a)*tex1.xyz + tex2.xyz;\n" + " fragment_alpha = tex1.a;\n" + #else + " albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n" + " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" + #endif + " }\n" " fragment_alpha = tex1.a;\n" #else // UNTEXTURED @@ -926,7 +937,7 @@ static char *stbvox_fragment_program = " vec3 lit_color;\n" " if (!emissive)\n" #if defined(STBVOX_ICONFIG_LIGHTING) || defined(STBVOX_CONFIG_LIGHTING_SIMPLE) - " lit_color = compute_lighting(objectspace_pos + transform[1], normal, albedo, ambient_color);\n" + " lit_color = compute_lighting(voxelspace_pos + transform[1], normal, albedo, ambient_color);\n" #else " lit_color = albedo * ambient_color ;\n" #endif @@ -934,11 +945,15 @@ static char *stbvox_fragment_program = " lit_color = albedo;\n" #if defined(STBVOX_ICONFIG_FOG) || defined(STBVOX_CONFIG_FOG_SMOOTHSTEP) - " vec3 dist = objectspace_pos + (transform[1] - camera_pos.xyz);\n" - " lit_color = compute_fog(lit_color, dist);\n" + " vec3 dist = voxelspace_pos + (transform[1] - camera_pos.xyz);\n" + " lit_color = compute_fog(lit_color, dist, fragment_alpha);\n" #endif - + + #ifdef STBVOX_CONFIG_UNPREMULTIPLY + " vec4 final_color = vec4(lit_color/fragment_alpha, fragment_alpha);\n" + #else " vec4 final_color = vec4(lit_color, fragment_alpha);\n" + #endif " outcolor = final_color;\n" "}\n" @@ -956,17 +971,106 @@ static char *stbvox_fragment_program = #ifdef STBVOX_CONFIG_FOG_SMOOTHSTEP "\n" - "vec3 compute_fog(vec3 color, vec3 relative_pos)\n" + "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha)\n" "{\n" " float f = sqrt(dot(relative_pos,relative_pos))/1320.0;\n" " f = clamp(f, 0.0, 1.0);\n" " f = 3.0*f*f - 2.0*f*f*f;\n" // smoothstep " f = f*f;\n" // fade in more smoothly + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " return rlerp(f, color.xyz, ambient[3]*fragment_alpha);\n" + #else " return rlerp(f, color.xyz, ambient[3]);\n" + #endif "}\n" #endif }; + +static char *stbvox_fragment_program_alpha_only = +{ + STBVOX_SHADER_VERSION + + // vertex-shader output data + "flat in uvec4 facedata;\n" + " in vec3 voxelspace_pos;\n" + " in float texlerp;\n" + + // per-buffer data + "uniform vec3 transform[3];\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // generally constant data + "uniform sampler2DArray tex_array[2];\n" + + #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER + "uniform samplerBuffer texscale;\n" + "uniform samplerBuffer texgen;\n" + #else + "uniform vec2 texscale[64];\n" // instead of 128, to avoid running out of uniforms + "uniform vec3 texgen[64];\n" + #endif + #endif + + "out vec4 outcolor;\n" + + "void main()\n" + "{\n" + " vec3 albedo;\n" + " float fragment_alpha;\n" + + #ifndef STBVOX_ICONFIG_UNTEXTURED + // unpack the values + " uint tex1_id = facedata.x;\n" + " uint tex2_id = facedata.y;\n" + " uint texprojid = facedata.w & 31u;\n" + " bool texblend_mode = ((facedata.w & 128u) != 0u);\n" + + #ifndef STBVOX_CONFIG_PREFER_TEXBUFFER + // load from uniforms / texture buffers + " vec3 texgen_s = texgen[texprojid];\n" + " vec3 texgen_t = texgen[texprojid+32u];\n" + " float tex1_scale = texscale[tex1_id & 63u].x;\n" + " float tex2_scale = texscale[tex2_id & 63u].y;\n" + #else + " vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n" + " vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n" + " float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n" + " float tex2_scale = texelFetch(texscale, int(tex2_id & 127u)).y;\n" + #endif + " vec2 texcoord;\n" + " vec3 texturespace_pos = voxelspace_pos + transform[2].xyz;\n" + " texcoord.s = dot(texturespace_pos, texgen_s);\n" + " texcoord.t = dot(texturespace_pos, texgen_t);\n" + + // @TODO: use 2 bits of facedata.w to enable animation of facedata.x & y? + + " vec4 tex1 = texture(tex_array[0], vec3(tex1_scale * texcoord, float(tex1_id)));\n" + " vec4 tex2 = texture(tex_array[1], vec3(tex2_scale * texcoord, float(tex2_id)));\n" + + " tex2.a *= texlerp;\n" + + // @TODO: could use a separate lookup table keyed on tex2 to determine this + " if (texblend_mode)\n" + " ;\n" + " else {\n" + #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA + " fragment_alpha = tex1.a;\n" + #else + " fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n" + #endif + " }\n" + + #else // UNTEXTURED + " vec4 color;" + " fragment_alpha = 1.0;\n" + #endif + + " outcolor = vec4(0.0, 0.0, 0.0, fragment_alpha);\n" + "}\n" +}; + + STBVXDEC char *stbvox_get_vertex_shader(void) { return stbvox_vertex_encoderogram; @@ -977,6 +1081,11 @@ STBVXDEC char *stbvox_get_fragment_shader(void) return stbvox_fragment_program; } +STBVXDEC char *stbvox_get_fragment_shader_alpha_only(void) +{ + return stbvox_fragment_program_alpha_only; +} + static float stbvox_dummy_transform[3][3]; #ifdef STBVOX_CONFIG_PREFER_TEXBUFFER diff --git a/tests/caveview/cave_mesher.c b/tests/caveview/cave_mesher.c index df26073..f9b7048 100644 --- a/tests/caveview/cave_mesher.c +++ b/tests/caveview/cave_mesher.c @@ -21,7 +21,8 @@ #define STBVOX_CONFIG_PREFER_TEXBUFFER //#define STBVOX_CONFIG_LIGHTING_SIMPLE #define STBVOX_CONFIG_FOG_SMOOTHSTEP - +#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA // use this even though it doesn't really work for alpha test without next #define +//#define STBVOX_CONFIG_UNPREMULTIPLY // slower, makes windows & fancy leaves look better #define STBVOX_ROTATION_IN_LIGHTING #define STB_VOXEL_RENDER_IMPLEMENTATION @@ -33,7 +34,9 @@ extern void ods(char *fmt, ...); #define FAST_CHUNK #define IN_PLACE -#define SKIP_TERRAIN 0 // must be a multiple of 16; doesn't build some underground stuff +#define SKIP_TERRAIN 0 // use to avoid building underground stuff + // allows you to see what perf would be like if underground was efficiently culled, + // or if you were making a game without underground enum { @@ -189,7 +192,7 @@ unsigned char minecraft_info[256][7] = { C_empty }, { C_solid, 54,54,54,54,54,54 }, { C_solid, 125,125,125,125,125,125 }, - { C_solid, 124,124,124,124,124,124 }, + { C_solid, 126,126,126,126,126,126 }, { C_empty }, // bars { C_trans, 49,49,49,49,49,49 }, // glass pane { C_solid, 136,136,136,136,137,137 }, // melon @@ -227,7 +230,7 @@ unsigned char minecraft_info[256][7] = // 128 { C_solid, 192,192,192,192,176,176 }, // sandstone stairs { C_solid, 32,32,32,32,32,32 }, // emerald ore - { C_empty }, // ender chest + { C_solid, 26,26,26,27,25,25 }, // ender chest { C_empty }, { C_empty }, { C_solid, 23,23,23,23,23,23 }, // emerald block -- GitLab