vk: make all texture samplers return values in linear space
Make sure that all SRGB-gamma textures are marked with SRGB format (this includes all the original textues, 8bit PNG textues). All other textures (roughness, metallic, normal maps) are linear (UNORM). Trust KTX2 texture that they store in the correct colorspace. Then we can just sample textures w/o SRGBtoLINEAR'izing them. Supposedly this is better because of hw interpolation in the correct colorspace. This is a bit experimental because Compressonator can't really into BC7_SRGB, which is sad. And also we need to re-gamma all the textures for the gamma/srgb-unaware traditional renderer.
This commit is contained in:
parent
60f83245ee
commit
9b9e89adec
|
@ -1,4 +1,5 @@
|
|||
#version 450
|
||||
#include "color_spaces.glsl"
|
||||
|
||||
layout(set=0,binding=0) uniform sampler2D tex;
|
||||
|
||||
|
@ -8,5 +9,5 @@ layout(location=1) in vec4 vColor;
|
|||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = texture(tex, vUv) * vColor;
|
||||
outColor = LINEARtoSRGB(texture(tex, vUv)) * vColor;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#version 450
|
||||
#include "color_spaces.glsl"
|
||||
|
||||
layout (constant_id = 0) const float alpha_test_threshold = 0.;
|
||||
layout (constant_id = 1) const uint max_dlights = 1;
|
||||
|
@ -29,7 +30,7 @@ const float dlight_attenuation_const = 5000.;
|
|||
|
||||
void main() {
|
||||
outColor = vec4(0.);
|
||||
const vec4 tex_color = texture(sTexture0, vTexture0);
|
||||
const vec4 tex_color = LINEARtoSRGB(texture(sTexture0, vTexture0));
|
||||
// TODO make sure textures are premultiplied alpha
|
||||
const vec4 baseColor = vColor * tex_color;
|
||||
|
||||
|
@ -37,7 +38,7 @@ void main() {
|
|||
discard;
|
||||
|
||||
outColor.a = baseColor.a;
|
||||
outColor.rgb += baseColor.rgb * texture(sLightmap, vLightmapUV).rgb;
|
||||
outColor.rgb += baseColor.rgb * LINEARtoSRGB(texture(sLightmap, vLightmapUV).rgb);
|
||||
|
||||
for (uint i = 0; i < ubo.num_lights; ++i) {
|
||||
const vec4 light_pos_r = ubo.lights[i].pos_r;
|
||||
|
|
|
@ -107,7 +107,7 @@ void main() {
|
|||
L = rayQueryGetIntersectionTEXT(rq, true);
|
||||
} else {
|
||||
// Draw skybox when nothing is hit
|
||||
payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, ray.direction).rgb);
|
||||
payload.emissive.rgb = texture(skybox, ray.direction).rgb;
|
||||
}
|
||||
|
||||
traceSimpleBlending(ray.origin, ray.direction, L, payload.emissive.rgb, payload.base_color_a.rgb);
|
||||
|
|
|
@ -30,7 +30,7 @@ void main() {
|
|||
const Kusok kusok = getKusok(geom.kusok_index);
|
||||
|
||||
if (kusok.material.tex_base_color == TEX_BASE_SKYBOX) {
|
||||
payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, gl_WorldRayDirectionEXT).rgb);
|
||||
payload.emissive.rgb = texture(skybox, gl_WorldRayDirectionEXT).rgb;
|
||||
return;
|
||||
} else {
|
||||
const vec4 color = getModelHeader(gl_InstanceID).color * kusok.material.base_color;
|
||||
|
@ -55,7 +55,7 @@ void main() {
|
|||
#if 1
|
||||
// Real correct emissive color
|
||||
//payload.emissive.rgb = kusok.emissive;
|
||||
payload.emissive.rgb = clamp(kusok.emissive / (1.0/3.0) / 25, 0, 1.5) * SRGBtoLINEAR(payload.base_color_a.rgb);
|
||||
payload.emissive.rgb = clamp(kusok.emissive / (1.0/3.0) / 25, 0, 1.5) * payload.base_color_a.rgb;
|
||||
#else
|
||||
// Fake texture color
|
||||
if (any(greaterThan(kusok.emissive, vec3(0.))))
|
||||
|
|
|
@ -31,10 +31,10 @@ void primaryRayHit(rayQueryEXT rq, inout RayPayloadPrimary payload) {
|
|||
const Material material = kusok.material;
|
||||
|
||||
if (kusok.material.tex_base_color == TEX_BASE_SKYBOX) {
|
||||
payload.emissive.rgb = SRGBtoLINEAR(texture(skybox, rayDirection).rgb);
|
||||
payload.emissive.rgb = texture(skybox, rayDirection).rgb;
|
||||
return;
|
||||
} else {
|
||||
payload.base_color_a = SRGBtoLINEAR(sampleTexture(material.tex_base_color, geom.uv, geom.uv_lods));
|
||||
payload.base_color_a = sampleTexture(material.tex_base_color, geom.uv, geom.uv_lods);
|
||||
payload.material_rmxx.r = sampleTexture(material.tex_roughness, geom.uv, geom.uv_lods).r * material.roughness;
|
||||
payload.material_rmxx.g = sampleTexture(material.tex_metalness, geom.uv, geom.uv_lods).r * material.metalness;
|
||||
|
||||
|
|
|
@ -53,14 +53,7 @@ void traceSimpleBlending(vec3 pos, vec3 dir, float L, inout vec3 emissive, inout
|
|||
const vec4 texture_color = texture(textures[nonuniformEXT(kusok.material.tex_base_color)], geom.uv);
|
||||
const vec4 mm_color = model.color * kusok.material.base_color;
|
||||
float alpha = mm_color.a * texture_color.a * geom.vertex_color.a;
|
||||
vec3 color = mm_color.rgb * SRGBtoLINEAR(texture_color.rgb) * geom.vertex_color.rgb * alpha;
|
||||
|
||||
/* TODO: I think it should be like this instead:
|
||||
const vec4 texture_color = SRGBtoLINEAR(texture(textures[nonuniformEXT(kusok.material.tex_base_color)], geom.uv));
|
||||
const vec4 mm_color = SRGBtoLINEAR(model.color) * SRGBtoLINEAR(kusok.material.base_color);
|
||||
float alpha = mm_color.a * texture_color.a * geom.vertex_color.a;
|
||||
vec3 color = mm_color.rgb * texture_color.rgb * geom.vertex_color.rgb * alpha;
|
||||
*/
|
||||
|
||||
if (model.mode == MATERIAL_MODE_BLEND_GLOW) {
|
||||
// Glow is additive + small overshoot
|
||||
|
|
|
@ -75,9 +75,9 @@ static struct {
|
|||
uint64_t texture_load_duration_ns;
|
||||
} g_stats;
|
||||
|
||||
static int loadTexture( const char *filename, qboolean force_reload ) {
|
||||
static int loadTexture( const char *filename, qboolean force_reload, colorspace_hint_e colorspace ) {
|
||||
const uint64_t load_begin_ns = aprof_time_now_ns();
|
||||
const int tex_id = force_reload ? XVK_LoadTextureReplace( filename, NULL, 0, 0 ) : VK_LoadTexture( filename, NULL, 0, 0 );
|
||||
const int tex_id = R_VkLoadTexture( filename, colorspace, force_reload);
|
||||
DEBUG("Loaded texture %s => %d", filename, tex_id);
|
||||
g_stats.texture_loads++;
|
||||
g_stats.texture_load_duration_ns += aprof_time_now_ns() - load_begin_ns;
|
||||
|
@ -206,11 +206,11 @@ static void loadMaterialsFromFile( const char *filename, int depth ) {
|
|||
continue;
|
||||
}
|
||||
|
||||
#define LOAD_TEXTURE_FOR(name, field) do { \
|
||||
#define LOAD_TEXTURE_FOR(name, field, colorspace) do { \
|
||||
if (name[0] != '\0') { \
|
||||
char texture_path[256]; \
|
||||
MAKE_PATH(texture_path, name); \
|
||||
const int tex_id = loadTexture(texture_path, force_reload); \
|
||||
const int tex_id = loadTexture(texture_path, force_reload, colorspace); \
|
||||
if (tex_id < 0) { \
|
||||
ERR("Failed to load texture \"%s\" for "#name"", name); \
|
||||
} else { \
|
||||
|
@ -218,10 +218,10 @@ static void loadMaterialsFromFile( const char *filename, int depth ) {
|
|||
} \
|
||||
}} while(0)
|
||||
|
||||
LOAD_TEXTURE_FOR(basecolor_map, tex_base_color);
|
||||
LOAD_TEXTURE_FOR(normal_map, tex_normalmap);
|
||||
LOAD_TEXTURE_FOR(metal_map, tex_metalness);
|
||||
LOAD_TEXTURE_FOR(roughness_map, tex_roughness);
|
||||
LOAD_TEXTURE_FOR(basecolor_map, tex_base_color, kColorspaceGamma);
|
||||
LOAD_TEXTURE_FOR(normal_map, tex_normalmap, kColorspaceLinear);
|
||||
LOAD_TEXTURE_FOR(metal_map, tex_metalness, kColorspaceLinear);
|
||||
LOAD_TEXTURE_FOR(roughness_map, tex_roughness, kColorspaceLinear);
|
||||
|
||||
// If there's no explicit basecolor_map value, use the "for" target texture
|
||||
if (current_material.tex_base_color == -1)
|
||||
|
|
|
@ -543,7 +543,7 @@ static const ref_interface_t gReffuncs =
|
|||
VK_FindTexture,
|
||||
VK_TextureName,
|
||||
VK_TextureData,
|
||||
VK_LoadTexture,
|
||||
VK_LoadTextureExternal,
|
||||
VK_CreateTexture,
|
||||
VK_LoadTextureArray,
|
||||
VK_CreateTextureArray,
|
||||
|
|
|
@ -242,12 +242,12 @@ static const dframetype_t *VK_SpriteLoadFrame( model_t *mod, const void *pin, ms
|
|||
if( FBitSet( mod->flags, MODEL_CLIENT )) // it's a HUD sprite
|
||||
{
|
||||
Q_snprintf( texname, sizeof( texname ), "#HUD/%s(%s:%i%i).spr", ctx->sprite_name, ctx->group_suffix, num / 10, num % 10 );
|
||||
gl_texturenum = VK_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, ctx->r_texFlags );
|
||||
gl_texturenum = VK_LoadTextureExternal( texname, pin, pinframe.width * pinframe.height * bytes, ctx->r_texFlags );
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", ctx->sprite_name, ctx->group_suffix, num / 10, num % 10 );
|
||||
gl_texturenum = VK_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, ctx->r_texFlags );
|
||||
gl_texturenum = VK_LoadTextureExternal( texname, pin, pinframe.width * pinframe.height * bytes, ctx->r_texFlags );
|
||||
}
|
||||
|
||||
// setup frame description
|
||||
|
|
|
@ -3337,7 +3337,7 @@ static void R_StudioLoadTexture( model_t *mod, studiohdr_t *phdr, mstudiotexture
|
|||
|
||||
// build the texname
|
||||
Q_snprintf( texname, sizeof( texname ), "#%s/%s.mdl", mdlname, name );
|
||||
ptexture->index = VK_LoadTexture( texname, (byte *)ptexture, size, flags );
|
||||
ptexture->index = VK_LoadTextureExternal( texname, (byte *)ptexture, size, flags );
|
||||
|
||||
if( !ptexture->index )
|
||||
{
|
||||
|
|
|
@ -274,7 +274,7 @@ static void VK_ProcessImage( vk_texture_t *tex, rgbdata_t *pic )
|
|||
}
|
||||
}
|
||||
|
||||
static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap);
|
||||
static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint);
|
||||
|
||||
static void VK_CreateInternalTextures( void )
|
||||
{
|
||||
|
@ -350,14 +350,17 @@ static void VK_CreateInternalTextures( void )
|
|||
sides[4] = pic;
|
||||
sides[5] = pic;
|
||||
|
||||
uploadTexture( &tglob.cubemap_placeholder, sides, 6, true );
|
||||
uploadTexture( &tglob.cubemap_placeholder, sides, 6, true, kColorspaceGamma );
|
||||
}
|
||||
}
|
||||
|
||||
static VkFormat VK_GetFormat(pixformat_t format) {
|
||||
static VkFormat VK_GetFormat(pixformat_t format, colorspace_hint_e colorspace_hint ) {
|
||||
switch(format)
|
||||
{
|
||||
case PF_RGBA_32: return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case PF_RGBA_32:
|
||||
return (colorspace_hint == kColorspaceLinear)
|
||||
? VK_FORMAT_R8G8B8A8_UNORM
|
||||
: VK_FORMAT_R8G8B8A8_SRGB;
|
||||
default:
|
||||
WARN("FIXME unsupported pixformat_t %d", format);
|
||||
return VK_FORMAT_UNDEFINED;
|
||||
|
@ -550,8 +553,8 @@ static VkSampler pickSamplerForFlags( texFlags_t flags ) {
|
|||
return tglob.default_sampler_fixme;
|
||||
}
|
||||
|
||||
static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap) {
|
||||
const VkFormat format = VK_GetFormat(layers[0]->type);
|
||||
static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint) {
|
||||
const VkFormat format = VK_GetFormat(layers[0]->type, colorspace_hint);
|
||||
int mipCount = 0;
|
||||
|
||||
tex->total_size = 0;
|
||||
|
@ -1044,29 +1047,7 @@ finalize:
|
|||
return (tex - vk_textures);
|
||||
}
|
||||
|
||||
static int loadTextureUsingEngine( const char *name, const byte *buf, size_t size, int flags );
|
||||
|
||||
int VK_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
|
||||
{
|
||||
if( !Common_CheckTexName( name ))
|
||||
return 0;
|
||||
|
||||
// see if already loaded
|
||||
vk_texture_t *tex = Common_TextureForName( name );
|
||||
if( tex )
|
||||
return (tex - vk_textures);
|
||||
|
||||
{
|
||||
const char *ext = Q_strrchr(name, '.');
|
||||
if (Q_strcmp(ext, ".ktx2") == 0) {
|
||||
return loadKtx2(name);
|
||||
}
|
||||
}
|
||||
|
||||
return loadTextureUsingEngine(name, buf, size, flags);
|
||||
}
|
||||
|
||||
static int loadTextureUsingEngine( const char *name, const byte *buf, size_t size, int flags ) {
|
||||
static int loadTextureUsingEngine( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint ) {
|
||||
uint picFlags = 0;
|
||||
|
||||
if( FBitSet( flags, TF_NOFLIP_TGA ))
|
||||
|
@ -1087,7 +1068,7 @@ static int loadTextureUsingEngine( const char *name, const byte *buf, size_t siz
|
|||
// upload texture
|
||||
VK_ProcessImage( tex, pic );
|
||||
|
||||
if( !uploadTexture( tex, &pic, 1, false ))
|
||||
if( !uploadTexture( tex, &pic, 1, false, colorspace_hint ))
|
||||
{
|
||||
memset( tex, 0, sizeof( vk_texture_t ));
|
||||
gEngine.FS_FreeImage( pic ); // release source texture
|
||||
|
@ -1103,17 +1084,43 @@ static int loadTextureUsingEngine( const char *name, const byte *buf, size_t siz
|
|||
return tex - vk_textures;
|
||||
}
|
||||
|
||||
int XVK_LoadTextureReplace( const char *name, const byte *buf, size_t size, int flags ) {
|
||||
vk_texture_t *tex;
|
||||
static int loadTextureInternal( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint ) {
|
||||
if( !Common_CheckTexName( name ))
|
||||
return 0;
|
||||
|
||||
// free if already loaded
|
||||
if(( tex = Common_TextureForName( name ))) {
|
||||
VK_FreeTexture( tex - vk_textures );
|
||||
// see if already loaded
|
||||
vk_texture_t *tex = Common_TextureForName( name );
|
||||
if( tex )
|
||||
return (tex - vk_textures);
|
||||
|
||||
{
|
||||
const char *ext = Q_strrchr(name, '.');
|
||||
if (Q_strcmp(ext, ".ktx2") == 0) {
|
||||
return loadKtx2(name);
|
||||
}
|
||||
}
|
||||
|
||||
return VK_LoadTexture( name, buf, size, flags );
|
||||
return loadTextureUsingEngine(name, buf, size, flags, colorspace_hint);
|
||||
}
|
||||
|
||||
int VK_LoadTextureExternal( const char *name, const byte *buf, size_t size, int flags ) {
|
||||
return loadTextureInternal(name, buf, size, flags, kColorspaceGamma);
|
||||
}
|
||||
|
||||
int R_VkLoadTexture( const char *filename, colorspace_hint_e colorspace, qboolean force_reload) {
|
||||
vk_texture_t *tex;
|
||||
if( !Common_CheckTexName( filename ))
|
||||
return 0;
|
||||
|
||||
if (force_reload) {
|
||||
// free if already loaded
|
||||
// TODO consider leaving intact if loading failed
|
||||
if(( tex = Common_TextureForName( filename ))) {
|
||||
VK_FreeTexture( tex - vk_textures );
|
||||
}
|
||||
}
|
||||
|
||||
return loadTextureInternal( filename, NULL, 0, 0, colorspace );
|
||||
}
|
||||
|
||||
int VK_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags )
|
||||
|
@ -1222,7 +1229,7 @@ static int loadTextureFromBuffers( const char *name, rgbdata_t *const *const pic
|
|||
for (int i = 0; i < pic_count; ++i)
|
||||
VK_ProcessImage( tex, pic[i] );
|
||||
|
||||
if( !uploadTexture( tex, pic, pic_count, false ))
|
||||
if( !uploadTexture( tex, pic, pic_count, false, kColorspaceGamma ))
|
||||
{
|
||||
memset( tex, 0, sizeof( vk_texture_t ));
|
||||
return 0;
|
||||
|
@ -1348,7 +1355,7 @@ static qboolean loadSkybox( const char *prefix, int style ) {
|
|||
goto cleanup;
|
||||
|
||||
Q_strncpy( tglob.skybox_cube.name, prefix, sizeof( tglob.skybox_cube.name ));
|
||||
success = uploadTexture(&tglob.skybox_cube, sides, 6, true);
|
||||
success = uploadTexture(&tglob.skybox_cube, sides, 6, true, kColorspaceGamma);
|
||||
|
||||
cleanup:
|
||||
for (int j = 0; j < i; ++j)
|
||||
|
|
|
@ -67,18 +67,24 @@ void initTextures( void );
|
|||
void destroyTextures( void );
|
||||
vk_texture_t *findTexture(int index);
|
||||
|
||||
typedef enum {
|
||||
//kColorspaceNative,
|
||||
kColorspaceLinear,
|
||||
kColorspaceGamma,
|
||||
} colorspace_hint_e;
|
||||
|
||||
// Public API functions
|
||||
int VK_FindTexture( const char *name );
|
||||
const char* VK_TextureName( unsigned int texnum );
|
||||
const byte* VK_TextureData( unsigned int texnum );
|
||||
int VK_LoadTexture( const char *name, const byte *buf, size_t size, int flags );
|
||||
int VK_LoadTextureExternal( const char *name, const byte *buf, size_t size, int flags );
|
||||
int VK_CreateTexture( const char *name, int width, int height, const void *buffer, texFlags_t flags );
|
||||
int VK_LoadTextureArray( const char **names, int flags );
|
||||
int VK_CreateTextureArray( const char *name, int width, int height, int depth, const void *buffer, texFlags_t flags );
|
||||
void VK_FreeTexture( unsigned int texnum );
|
||||
int VK_LoadTextureFromBuffer( const char *name, rgbdata_t *pic, texFlags_t flags, qboolean update );
|
||||
|
||||
int XVK_LoadTextureReplace( const char *name, const byte *buf, size_t size, int flags );
|
||||
int R_VkLoadTexture( const char *filename, colorspace_hint_e colorspace, qboolean force_reload);
|
||||
|
||||
int XVK_TextureLookupF( const char *fmt, ...);
|
||||
|
||||
|
|
Loading…
Reference in New Issue