Skip to content

Commit a07df32

Browse files
Improve Astc & BasisU normal map support (KhronosGroup#493)
normalMap in ktxAstcParams and ktxBasisParams (`--normal_mode` in `toktx`), causes the encoders to split the R & G components of the input into the RGB & alpha channels of the encoded texture and to apply encoder-specific settings that improve quality for normal maps. Add `--normalize` option to `toktx. Add tests for normalMap and normalize functionality. Deprecate `separateRGToRGB_A` and remove related code. Fixes KhronosGroup#455. Co-authored-by: Wasim Abbas <abbas.wasim@gmail.com>
1 parent 8b61f52 commit a07df32

2 files changed

Lines changed: 66 additions & 38 deletions

File tree

include/ktx.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,22 +1291,19 @@ typedef struct ktxBasisParams {
12911291
This will override the value chosen by @c qualityLevel.
12921292
*/
12931293
char inputSwizzle[4];
1294-
/*!< A swizzle to apply before encoding. It must match the regular
1294+
/*!< A swizzle to apply before encoding. It must match the regular
12951295
expression /^[rgba01]{4}$/. If both this and preSwizzle
12961296
are specified ktxTexture_CompressBasisEx will raise
1297-
KTX_INVALID_OPERATION. */
1298-
1297+
KTX_INVALID_OPERATION.
1298+
*/
12991299
ktx_bool_t normalMap;
13001300
/*!< Tunes codec parameters for better quality on normal maps (no
1301-
selector RDO, no endpoint RDO). Only valid for linear textures.
1301+
selector RDO, no endpoint RDO) and sets the texture's DFD appropriately.
1302+
Only valid for linear textures.
13021303
*/
13031304
ktx_bool_t separateRGToRGB_A;
1304-
/*!< Separates the input R and G channels to RGB and A (for tangent
1305-
space XY normal maps). Equivalent to @c inputSwizzle "rrrg".
1306-
Separation is the default for 2 component textures. If both this
1307-
and inputSwizzle are set, the latter wins therefore set
1308-
@c inputSwizzle to change the default for 2 component
1309-
textures.
1305+
/*!< @deprecated. This was and is a no-op. 2-component inputs have always been
1306+
automatically separated using an "rrrg" inputSwizzle. @sa inputSwizzle and normalMode.
13101307
*/
13111308
ktx_bool_t preSwizzle;
13121309
/*!< If the texture has @c KTXswizzle metadata, apply it before

utils/scapp.h

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,15 @@ astcEncoderMode(const char* mode) {
135135
// so the table below has to be laboriously done in html.
136136
//! [scApp options]
137137
<dl>
138-
<dt>--encode &lt;astc|etc1s|uastc&gt;</dt>
138+
<dt>--encode &lt;astc | etc1s | uastc&gt;</dt>
139139
<dd>Compress the image data to ASTC, transcodable ETC1S / BasisLZ or
140140
high-quality transcodable UASTC format. Implies @b --t2.
141141
With each encoding option the following encoder specific options
142142
become valid, otherwise they are ignored.</dd>
143143
<dl>
144144
<dt>astc:</dt>
145145
<dd>Create a texture in high-quality ASTC format.</dd>
146-
<dt>--astc_blk_d &lt;XxY|XxYxZ&gt;</dt>
146+
<dt>--astc_blk_d &lt;XxY | XxYxZ&gt;</dt>
147147
<dd>Specify which block dimension to use for compressing the textures.
148148
e.g. @b --astc_blk_d 6x5 for 2D or @b --astc_blk_d 6x6x6 for 3D.
149149
6x6 is default for 2D.
@@ -175,7 +175,7 @@ astcEncoderMode(const char* mode) {
175175
<tr><td>6x6x5</td> <td>0.71 bpp</td></tr>
176176
<tr><td>6x6x6</td> <td>0.59 bpp</td></tr>
177177
</table></dd>
178-
<dt>--astc_mode &lt;ldr|hdr&gt;</dt>
178+
<dt>--astc_mode &lt;ldr | hdr&gt;</dt>
179179
<dd>Specify which encoding mode to use. LDR is the default unless the input.
180180
image is 16-bit in which case the default is HDR.</dd>
181181
<dt>--astc_quality &lt;level&gt;</dt>
@@ -302,20 +302,32 @@ astcEncoderMode(const char* mode) {
302302
deterministic).</dd>
303303
</dl>
304304
<dt>--normal_mode</dt>
305-
<dd>For ASTC encoder '@b --encode astc' assumes the input texture is
306-
a three component linear LDR normal map storing unit length
307-
normals as (R=X, G=Y, B=Z). The output will be a two component
308-
X+Y normal map stored as (RGB=X, A=Y), optimized for angular
309-
error instead of simple PSNR. The Z component can be recovered
310-
programmatically in shader code by using the equation:
305+
<dd>Only valid for linear textures with two or more components.
306+
If the input texture has three or four linear components it is assumed to
307+
be a three component linear normal map storing unit length
308+
normals as (R=X, G=Y, B=Z). A fourth component will be ignored. The map will be
309+
converted to a two component X+Y normal map stored as (RGB=X, A=Y) prior to
310+
encoding. If unsure that your normals are unit length, use @b --normalize.
311+
If the input has 2 linear components it is assumed to be an X+Y map of unit normals.
312+
313+
The Z component can be recovered programmatically in shader
314+
code by using the equations:
311315
<pre>
312316
nml.xy = texture(...).ga; // Load in [0,1]
313317
nml.xy = nml.xy * 2.0 - 1.0; // Unpack to [-1,1]
314318
nml.z = sqrt(1 - dot(nml.xy, nml.xy)); // Compute Z
315319
</pre>
316-
For ETC1S encoder '@b --encode etc1s' tunes codec parameters for
317-
better quality on normal maps (no selector RDO, no endpoint RDO).
318-
Only valid for linear textures.</dd>
320+
Encoding is optimized for normal maps. For ASTC encoding,
321+
'--encode astc', the encoder is directed to optimize for angular
322+
error instead of simple PSNR. For ETC1S encoding, '@b --encode etc1s',
323+
RDO is disabled (no selector RDO, no endpoint RDO) to provide
324+
better quality.</dd>
325+
<dt>--normalize</dt>
326+
<dd>Normalize input normals to have a unit length. Only valid for
327+
linear textures with 2 or more components. For 2-component inputs 2D
328+
unit normals are calculated. Do not use this to generate X+Y normals
329+
for --normal_mode. For 4-component inputs a 3D unit normal is calculated.
330+
1.0 is used for the value of the 4th component.</dd>
319331
<dt>--no_sse</dt>
320332
<dd>Forbid use of the SSE instruction set. Ignored if CPU does not
321333
support SSE. Only the Basis Universal compressor uses SSE.</dd>
@@ -433,13 +445,15 @@ class scApp : public ktxApp {
433445
mode.clear();
434446
qualityLevel.clear();
435447
normalMap = false;
448+
for (int i = 0; i < 4; i++) inputSwizzle[i] = 0;
436449
}
437450
};
438451
int ktx2;
439452
int etc1s;
440453
int zcmp;
441454
int astc;
442455
ktx_bool_t normalMode;
456+
ktx_bool_t normalize;
443457
clamped<ktx_uint32_t> zcmpLevel;
444458
clamped<ktx_uint32_t> threadCount;
445459
struct basisOptions bopts;
@@ -454,6 +468,7 @@ class scApp : public ktxApp {
454468
zcmp = false;
455469
astc = false;
456470
normalMode = false;
471+
normalize = false;
457472
}
458473
};
459474

@@ -486,14 +501,14 @@ class scApp : public ktxApp {
486501
void usage()
487502
{
488503
cerr <<
489-
" --encode <astc|etc1s|uastc>\n"
504+
" --encode <astc | etc1s | uastc>\n"
490505
" Compress the image data to ASTC, transcodable ETC1S / BasisLZ or\n"
491506
" high-quality transcodable UASTC format. Implies --t2.\n"
492507
" With each encoding option the following encoder specific options\n"
493508
" become valid, otherwise they are ignored.\n\n"
494509
" astc:\n"
495510
" Create a texture in high-quality ASTC format.\n"
496-
" --astc_blk_d <XxY|XxYxZ>\n"
511+
" --astc_blk_d <XxY | XxYxZ>\n"
497512
" Specify which block dimension to use for compressing the textures.\n"
498513
" e.g. --astc_blk_d 6x5 for 2D or --astc_blk_d 6x6x6 for 3D.\n"
499514
" 6x6 is default for 2D.\n\n"
@@ -511,7 +526,7 @@ class scApp : public ktxApp {
511526
" 4x4x3: 2.67 bpp 6x5x5: 0.85 bpp\n"
512527
" 4x4x4: 2.00 bpp 6x6x5: 0.71 bpp\n"
513528
" 5x4x4: 1.60 bpp 6x6x6: 0.59 bpp\n"
514-
" --astc_mode <ldr|hdr>\n"
529+
" --astc_mode <ldr | hdr>\n"
515530
" Specify which encoding mode to use. LDR is the default unless the input.\n"
516531
" image is 16-bit in which case the default is HDR.\n"
517532
" --astc_quality <level>\n"
@@ -630,18 +645,29 @@ class scApp : public ktxApp {
630645
" Disable RDO multithreading (slightly higher compression,\n"
631646
" deterministic).\n\n"
632647
" --normal_mode\n"
633-
" For ASTC encoder '--encode astc' assumes the input texture is\n"
634-
" a three component linear LDR normal map storing unit length\n"
635-
" normals as (R=X, G=Y, B=Z). The output will be a two component\n"
636-
" X+Y normal map stored as (RGB=X, A=Y), optimized for angular\n"
637-
" error instead of simple PSNR. The Z component can be recovered\n"
638-
" programmatically in shader code by using the equation:\n\n"
648+
" Only valid for linear textures with two or more components.\n"
649+
" If the input texture has three or four linear components it is assumed to\n"
650+
" be a three component linear normal map storing unit length\n"
651+
" normals as (R=X, G=Y, B=Z). A fourth component will be ignored. The map will be\n"
652+
" converted to a two component X+Y normal map stored as (RGB=X, A=Y) prior to\n"
653+
" encoding. If unsure that your normals are unit length, use @b --normalize.\n"
654+
" If the input has 2 linear components it is assumed to be an X+Y map of unit normals.\n\n"
655+
" The Z component can be recovered programmatically in shader\n"
656+
" code by using the equations:\n\n"
639657
" nml.xy = texture(...).ga; // Load in [0,1]\n"
640658
" nml.xy = nml.xy * 2.0 - 1.0; // Unpack to [-1,1]\n"
641659
" nml.z = sqrt(1 - dot(nml.xy, nml.xy)); // Compute Z\n\n"
642-
" For ETC1S encoder '--encode etc1s' tunes codec parameters for \n"
643-
" better quality on normal maps (no selector RDO, no endpoint RDO).\n"
644-
" Only valid for linear textures.\n"
660+
" Encoding is optimized for normal maps. For ASTC encoding,\n"
661+
" '--encode astc', the encoder is directed to optimize for angular\n"
662+
" error instead of simple PSNR. For ETC1S encoding, '--encode etc1s',\n"
663+
" RDO is disabled (no selector RDO, no endpoint RDO) to provide \n"
664+
" better quality.\n\n"
665+
" --normalize\n"
666+
" Normalize input normals to have a unit length. Only valid for\n"
667+
" linear textures with 2 or more components. For 2-component inputs 2D\n"
668+
" unit normals are calculated. Do not use this to generate X+Y normals \n"
669+
" for --normal_mode. For 4-component inputs a 3D unit normal is calculated.\n"
670+
" 1.0 is used for the value of the 4th component."
645671
" --no_sse\n"
646672
" Forbid use of the SSE instruction set. Ignored if CPU does not\n"
647673
" support SSE. Only the Basis Universal compressor uses SSE.\n"
@@ -707,9 +733,10 @@ scApp::scApp(string& version, string& defaultVersion,
707733
{ "astc_mode", argparser::option::required_argument, NULL, 1013 },
708734
{ "astc_quality", argparser::option::required_argument, NULL, 1014 },
709735
{ "encode", argparser::option::required_argument, NULL, 1015 },
736+
{ "normalize", argparser::option::no_argument, NULL, 1016 },
710737
// Deprecated options
711738
{ "bcmp", argparser::option::no_argument, NULL, 'b' },
712-
{ "uastc", argparser::option::optional_argument, NULL, 1016 }
739+
{ "uastc", argparser::option::optional_argument, NULL, 1017 }
713740
};
714741
const int lastOptionIndex = sizeof(my_option_list)
715742
/ sizeof(argparser::option);
@@ -757,7 +784,7 @@ scApp::processOption(argparser& parser, int opt)
757784
switch (opt) {
758785
case 'z':
759786
if (options.etc1s) {
760-
cerr << "Only one of '--encode etc1s|--bcmp' and --zcmp can be specified."
787+
cerr << "Only one of '--encode etc1s | --bcmp' and --zcmp can be specified."
761788
<< endl;
762789
usage();
763790
exit(1);
@@ -881,7 +908,7 @@ scApp::processOption(argparser& parser, int opt)
881908
exit(1);
882909
}
883910
if (options.bopts.uastc) {
884-
cerr << "Only one of --bcmp and '--encode etc1s|--uastc' can be specified.\n"
911+
cerr << "Only one of --bcmp and '--encode etc1s | --uastc' can be specified.\n"
885912
<< "--bcmp is deprecated, use '--encode etc1s' instead."
886913
<< endl;
887914
usage();
@@ -893,10 +920,14 @@ scApp::processOption(argparser& parser, int opt)
893920
case 1015:
894921
setEncoder(parser.optarg);
895922
options.ktx2 = 1;
923+
hasArg = true;
896924
break;
897925
case 1016:
926+
options.normalize = true;
927+
break;
928+
case 1017:
898929
if (options.etc1s) {
899-
cerr << "Only one of `--encode etc1s|--bcmp` and `--uastc [<level>]` can be specified."
930+
cerr << "Only one of `--encode etc1s | --bcmp` and `--uastc [<level>]` can be specified."
900931
<< endl;
901932
usage();
902933
exit(1);

0 commit comments

Comments
 (0)