Replace 3D texture in landscape shader by a 2D texture array

A texture array is conceptionally what should be used in this case. One
advantage of this is that we don't have to generate mipmaps ourselves but can
let the graphics driver take care of it. Same for selection of the mipmap
level. This would even allow to choose different mipmap levels for different
textures.

This is a somewhat experimental change since it makes OpenGL 3.0 a hard
requirement for OpenClonk. I expect that this is fine, but if this causes
failures during landscape creation on common hardware/drivers we should
revisit.
shapetextures
Armin Burgmeier 2015-09-16 21:41:19 -04:00
parent 061305c0c7
commit 790219ac7e
4 changed files with 52 additions and 88 deletions

View File

@ -3,7 +3,7 @@
// Input textures
uniform sampler2D landscapeTex[2];
uniform sampler2D scalerTex;
uniform sampler3D materialTex;
uniform sampler2DArray materialTex;
// Resolution of the landscape texture
uniform vec2 resolution;
@ -16,7 +16,7 @@ uniform sampler1D matMapTex;
#else
uniform float matMap[256];
#endif
uniform int materialDepth;
uniform float materialDepth;
uniform vec2 materialSize;
// Expected parameters for the scaler
@ -35,9 +35,10 @@ float queryMatMap(int pix)
{
#ifndef NO_BROKEN_ARRAYS_WORKAROUND
int idx = f2i(texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r);
return float(idx) / 256.0 + 0.5 / float(materialDepth);
return (float(idx) / 256.0 + 0.0 / materialDepth) * materialDepth;
//return texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r;
#else
return matMap[pix];
return matMap[pix] * materialDepth;
#endif
}
@ -70,14 +71,14 @@ slice(material)
// Get material pixels
float materialIx = queryMatMap(f2i(landscapePx.r));
vec4 materialPx = texture3D(materialTex, vec3(materialCoo, materialIx));
vec4 normalPx = texture3D(materialTex, vec3(materialCoo, materialIx+0.5));
vec4 materialPx = texture(materialTex, vec3(materialCoo, materialIx));
vec4 normalPx = texture(materialTex, vec3(materialCoo, materialIx+0.5*materialDepth));
// Same for second pixel, but we'll simply use the first normal
#ifdef OC_HAVE_2PX
float materialIx2 = queryMatMap(f2i(landscapePx2.r));
vec4 materialPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2));
vec4 normalPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2+0.5));
vec4 materialPx2 = texture(materialTex, vec3(materialCoo, materialIx2));
vec4 normalPx2 = texture(materialTex, vec3(materialCoo, materialIx2+0.5*materialDepth));
#endif
}

View File

@ -22,7 +22,7 @@
#include "C4Surface.h"
// Shader version
const int C4Shader_Version = 120; // GLSL 1.20 / OpenGL 2.1
const int C4Shader_Version = 130; // GLSL 1.30 / OpenGL 3.0
// Maximum number of texture coordinates
const int C4Shader_MaxTexCoords = 8;

View File

@ -54,7 +54,7 @@ const char *const SEPERATOR_TEXTURE = "--SEP--";
C4LandscapeRenderGL::C4LandscapeRenderGL()
{
ZeroMem(Surfaces, sizeof(Surfaces));
ZeroMem(hMaterialTexture, sizeof(hMaterialTexture));
hMaterialTexture = 0;
}
C4LandscapeRenderGL::~C4LandscapeRenderGL()
@ -135,8 +135,8 @@ void C4LandscapeRenderGL::Clear()
delete Surfaces[i];
Surfaces[i] = NULL;
}
glDeleteTextures(C4LR_MipMapCount, hMaterialTexture);
std::fill_n(hMaterialTexture, C4LR_MipMapCount, 0);
if (hMaterialTexture) glDeleteTextures(1, &hMaterialTexture);
hMaterialTexture = 0;
}
bool C4LandscapeRenderGL::InitLandscapeTexture()
@ -170,9 +170,7 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
AddTexturesFromMap(pTexs);
// Determine depth to use
iMaterialTextureDepth = 1;
while(iMaterialTextureDepth < 2*int32_t(MaterialTextureMap.size()))
iMaterialTextureDepth <<= 1;
iMaterialTextureDepth = 2*MaterialTextureMap.size();
int32_t iNormalDepth = iMaterialTextureDepth / 2;
// Find the largest texture
@ -186,15 +184,16 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
// Get size for our textures. We might be limited by hardware
int iTexWdt = pRefSfc->Wdt, iTexHgt = pRefSfc->Hgt;
GLint iMaxTexSize;
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &iMaxTexSize);
GLint iMaxTexSize, iMaxTexLayers;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &iMaxTexSize);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &iMaxTexLayers);
if (iTexWdt > iMaxTexSize || iTexHgt > iMaxTexSize)
{
iTexWdt = Min(iTexWdt, iMaxTexSize);
iTexHgt = Min(iTexHgt, iMaxTexSize);
LogF(" gl: Material textures too large, GPU only supports %dx%d! Cropping might occur!", iMaxTexSize, iMaxTexSize);
}
if(iMaterialTextureDepth >= iMaxTexSize)
if(iMaterialTextureDepth >= iMaxTexLayers)
{
LogF(" gl: Too many material textures! GPU only supports 3D texture depth of %d!", iMaxTexSize);
return false;
@ -265,70 +264,42 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
// Clear error error(s?)
while(glGetError()) {}
// Alloc 3D textures
glEnable(GL_TEXTURE_3D);
glGenTextures(C4LR_MipMapCount, hMaterialTexture);
// Generate textures (mipmaps too!)
// Alloc 2D texture array
//glEnable(GL_TEXTURE_2D_ARRAY);
glGenTextures(1, &hMaterialTexture);
// Generate textures
int iSizeSum = 0;
BYTE *pLastData = new BYTE [iSize / 4];
for(int iMMLevel = 0; iMMLevel < C4LR_MipMapCount; iMMLevel++)
{
// Scale the texture down for mip-mapping
if(iMMLevel) {
BYTE *pOut = pData;
BYTE *pIn[4] = {
pLastData, pLastData + iBytesPP,
pLastData + iBytesPP * iTexWdt, pLastData + iBytesPP * iTexWdt + iBytesPP
};
for (int i = 0; i < iMaterialTextureDepth; ++i)
for (int y = 0; y < iTexHgt / 2; ++y)
{
for (int x = 0; x < iTexWdt / 2; ++x)
{
for (int j = 0; j < iBytesPP; j++)
{
unsigned int s = 0;
s += *pIn[0]++; s += 3 * *pIn[1]++; s += 3 * *pIn[2]++; s += *pIn[3]++;
*pOut++ = BYTE(s / 8);
}
pIn[0] += iBytesPP; pIn[1] += iBytesPP; pIn[2] += iBytesPP; pIn[3] += iBytesPP;
}
pIn[0] += iBytesPP * iTexWdt; pIn[1] += iBytesPP * iTexWdt;
pIn[2] += iBytesPP * iTexWdt; pIn[3] += iBytesPP * iTexWdt;
}
iTexWdt /= 2; iTexHgt /= 2;
}
// Select texture
glBindTexture(GL_TEXTURE_3D, hMaterialTexture[iMMLevel]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Select texture
glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// We fully expect to tile these
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// We fully expect to tile these
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_GENERATE_MIPMAP, GL_TRUE);
// Make it happen!
glTexImage3D(GL_TEXTURE_3D, 0, 4, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA,
iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV,
pData);
// Make it happen!
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, 4, iTexWdt, iTexHgt, iMaterialTextureDepth, 0, GL_BGRA,
iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV,
pData);
// Exchange buffers
BYTE *tmp = pLastData;
pLastData = pData;
pData = tmp;
// Exchange buffers
BYTE *tmp = pLastData;
pLastData = pData;
pData = tmp;
// Statistics
iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * iBytesPP;
}
// Statistics
iSizeSum += iTexWdt * iTexHgt * iMaterialTextureDepth * iBytesPP;
// Dispose of data
delete [] pData;
delete [] pLastData;
glDisable(GL_TEXTURE_3D);
//glDisable(GL_TEXTURE_3D);
// Check whether we were successful
if(int err = glGetError())
@ -338,10 +309,9 @@ bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
}
// Announce the good news
LogF(" gl: Texturing uses %d slots at %dx%d, %d levels (%d MB total)",
LogF(" gl: Texturing uses %d slots at %dx%d (%d MB total)",
static_cast<int>(MaterialTextureMap.size()),
iMaterialWidth, iMaterialHeight,
C4LR_MipMapCount,
iSizeSum / 1000000);
return true;
@ -852,7 +822,7 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap)
}
// Assign texture
if(pFMap) pFMap[pix] = (gTexCoo + 0.5) / iMaterialTextureDepth;
if(pFMap) pFMap[pix] = gTexCoo / iMaterialTextureDepth;
if(pIMap) pIMap[pix] = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5);
}
}
@ -896,7 +866,9 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
BuildMatMap(MatMap, NULL);
ShaderCall.SetUniform1fv(C4LRU_MatMap, 256, MatMap);
}
ShaderCall.SetUniform1i(C4LRU_MaterialDepth, iMaterialTextureDepth);
float fMaterialTextureDepth = iMaterialTextureDepth;
ShaderCall.SetUniform1f(C4LRU_MaterialDepth, fMaterialTextureDepth);
ShaderCall.SetUniform2f(C4LRU_MaterialSize,
float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom,
float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom);
@ -951,13 +923,7 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh
}
if(ShaderCall.AllocTexUnit(C4LRU_MaterialTex))
{
// Decide which mip-map level to use
double z = 0.5; int iMM = 0;
while(pGL->Zoom < z * ::Game.C4S.Landscape.MaterialZoom && iMM + 1 <C4LR_MipMapCount)
{ z /= 2; iMM++; }
glBindTexture(GL_TEXTURE_3D, hMaterialTexture[iMM]);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_ARRAY, hMaterialTexture);
}
if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex))
{

View File

@ -62,9 +62,6 @@ const int C4LR_BytesPerPx = 3;
const int C4LR_BytesPerSurface = 4;
const int C4LR_SurfaceCount = (C4LR_ByteCount + C4LR_BytesPerSurface - 1) / C4LR_BytesPerSurface;
// How many mip-map levels should be used at maximum?
const int C4LR_MipMapCount = 6;
class C4Landscape; class C4TextureMap;
class C4LandscapeRender
@ -112,9 +109,9 @@ private:
static const char *UniformNames[];
GLenum hLandscapeTexCoord, hLightTexCoord;
// 3D texture of material textures
GLuint hMaterialTexture[C4LR_MipMapCount];
// material texture positions in 3D texture
// 2D texture array of material textures
GLuint hMaterialTexture;
// material texture positions in texture array
std::vector<StdCopyStrBuf> MaterialTextureMap;
// depth of material texture in layers
int32_t iMaterialTextureDepth;