Since releasing wow.export, people have been asking if we can increase the texture quality that it exports. In this post, I'm going to go over why the 16k terrain textures exported from wow.export are still "low quality", and how we can achieve 1:1 in-game quality.
For this, I will be using wow.export and Blender. This can be done with other software, but this guide won't cover that, you'll have to translate as needed.
Overview of Map Tiles
When using wow.export, we currently allow you to export terrain textures in 1k, 4k, 8k and 16k resolutions. There's also 512 format which uses minimap tiles and is not applicable to this topic. A common occurrence with those numbers is people are often confused about why 16k isn't pixel-perfect with in-game. How do game textures that are a measly 256px end up looking distorted?
To answer this, let's take a look at a map tile:

For those unfamiliar, the terrain in World of Warcraft is split up into ADT tiles. Each of these tiles contains 16x16 chunks (making 64 per tile). Each of these chunks makes up an 8x8 grid (formed with 256 triangles). So in total that's 65,536 (uint16 max for the nerds among you) triangles for an entire ADT tile.
Since those numbers don't give you much perspective, to give you an idea of how big a single ADT tile is, here's the tile where Dalaran Crater sits.

Pre-baking: Why we use it, and why it sucks.
To render the textures that are painted onto a tile, the game engine takes raw texture files and mixes them together with alpha maps (along with a bunch of other information we won't diverge into) for each individual chunk on the tile.
What is an alpha map? An alpha map is a very small (64x64) array of pixels which controls how the textures show up on a chunk. There are up to three alpha maps for every chunk, each one controlling a different texture. This allows for up to four different textures on every chunk (as the first texture is always fully filled).
Let's visual this:

This works great for the game engine (and is how most games handle painted terrain), however it doesn't work well for our exporter due to the widely different use-cases people have for wanting map tiles exporter.
Instead, we pre-bake the textures (using the process above), and export the final painted result as a single texture (or one texture per chunk if you export 8k+). This is generally fine for 99% of use-cases, as you can just import the object into any 3D program and slap the texture onto it. Job's done!
To answer the first question then: why is 16k still low-quality? When you export at X resolution, the X is indicating the total dimensions we render bake the texture at for the entire ADT tile, not just the individual chunk.
That means if you export at 4k resolution, you're getting a 4096x4096 texture, but if you stretch that over 16x16 chunks, you end up with a mere 256px resolution per chunk. That however, is already a 29.9MB file. For 8k+ resolutions, we split the bake up into a single image per chunk, this totals at 256 images. For a 16k export each texture is 1024x1024 in resolution - that's 137MB of textures!
1024x1024 resolution per chunk doesn't even meet Mists of Pandaria levels of texture quality. People keep asking why we can't just increase the 16k to something higher - we can, but read the previous paragraph again and then imagine how insanely ridiculous the texture files would be to get close to actual 8k per chunk. Not only are we burning incredible amounts of disk-space and RAM at that point, but you're probably dramatically extending render times.
The Solution: Do It Yourself
Since the process of blending the textures in real-time is complicated, we're not going to implement some magical solution into wow.export. Instead, I've updated it to allow you to get your hands on the raw information. Now, let's get our hands dirty!
Open up wow.export and select a map tile you want. In the Terrain Texture Quality box, set it to Alpha Maps and then export. Where you would normally find the texture files, you should instead find a whole heap of crap.

The first thing you may notice is the vibrantly coloured texture files. These are our alpha maps. The observant of you may have two immediate questions: why are they colourful and why is there only 256 of them.
To reduce the amount of files exported, and improve both efficiency and simplicity of the shader we're going to build, the three alpha layers have been merged into one file, each using the individual red, green and blue channels of the image.

Lastly, there's now also a JSON (.json) file for every chunk. This file maps each layer of the alpha map to a texture, as well as defining the scale of said texture (more on this last bit later).
To start with, load up Blender (or your preferred 3D software) and import the tile OBJ as you normally would. The alpha maps will already be set-up for you, so you should see a funky looking piece of terrain in-front of you.

As far as alpha maps go, the one I'm using in the example is actually fairly well distributed, so it looks quite neat - don't panic if yours looks like a psychedelic dance floor, that's normal too.
To start with, we're going to work on building a shader for a single chunk. For Blender we'll be doing this in the Shader Editor.
The first thing we want to do is make sure the Interpolation Mode of the Image Texture node is set to Cubic, this makes sure when the alpha map is scaled up, it doesn't become blurred.
Additionally, make sure the Extension of the same node is set to Clip, this makes it so the texture doesn't tile, leading to those edges around every chunk you can see above.

Now, each of the alpha maps are stored on the individual RGB channels of the image, so create a Separate RGB and link the Color output of our texture to the Image input of the splitter and then put them both to the side for a moment.
Open up the JSON file for this specific chunk, and take a look at the mapping; it will contain up to 4 entries, each defining a different texture on the chunk. For each one, create a new Image Texture node and load the specified file.

Note that you don't need to adjust the interpolation or clipping of these image nodes. You should now have something that looks like this:

The next step we're going to do is apply the scale to each of these textures. By default, the textures will just be scaled to 1, however in a lot of places the game will use different scales for different textures, depending on how they're painted.
The scale is defined by the 'scale' property value on each entry in the JSON, however that scale is relative to the entire ADT - divide eight by the value of that scale!
To send that into an image texture, create a Mapping node, set all three of the Scale values to the same one as in the JSON, then link the Vector output of each mapping node to the relative Image Texture node. Repeat this for all four textures.

Quick-tip: Instead of entering the same Scale value in the X, Y and Z fields, you could create a Value node and link that as an input to the scale.

Also, create a Texture Coordinate node and feed the UV output into the Vector input of all four of your Mapping nodes - the UV is already setup for us. We can re-use the same Texture Coordinate node for all four Mapping nodes.

This is the point where things start to turn into spaghetti. For each of the layers, we need to mix it with the layer before. To do this, we use the relative channel from our alpha map to control the factor of a Mix RGB node.
The base layer, having no alpha map, just mixes in directly with the first layer. This is difficult to explain, so let's see it in action:

If everything went to plan, you should now have pixel-perfect terrain textures that match the in-game quality.. at least for 1 chunk. Just 255 more to go!

Plan B: Automate Everything!
Naturally, you're probably thinking I'm insane for expecting you to do that for 256 different chunks. Don't worry, I automated the entire thing in Python, and I've linked the code below!
The reason I went over the steps manually first is to give you an insight into how it works, and so that those not using Blender can apply the same process elsewhere.
View the script on GitHub here.
You can install this script directly as a Python macro using my MacroMan add-on. Not familiar? Check out my recent guide on that over here.
What now?
Experiment! This guide only covers the basics of what can be done here. It's not perfect, and there are some missing things (such as vertex colouring), but this is aimed at advanced users who can likely overcome those pretty easily. Here's some of the things I do with this:
Swap terrain textures to redesign a zone:

Use emission nodes to create awesome terrain lighting:

Load entire maps without running out of memory:
