Skip to content

Commit

Permalink
Use texture arrays in the high water shader
Browse files Browse the repository at this point in the history
  • Loading branch information
past-due committed Sep 11, 2023
1 parent b323f32 commit 0e1c3fb
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 67 deletions.
18 changes: 8 additions & 10 deletions data/base/shaders/terrain_water_high.frag
Expand Up @@ -5,12 +5,9 @@
#define WZ_MIP_LOAD_BIAS 0.f
//

uniform sampler2D tex1;
uniform sampler2D tex2;
uniform sampler2D tex1_nm;
uniform sampler2D tex2_nm;
uniform sampler2D tex1_sm;
uniform sampler2D tex2_sm;
uniform sampler2DArray tex;
uniform sampler2DArray tex_nm;
uniform sampler2DArray tex_sm;
uniform sampler2D lightmap_tex;

// light colors/intensity:
Expand All @@ -27,6 +24,7 @@ uniform float fogStart;
#if (!defined(GL_ES) && (__VERSION__ >= 130)) || (defined(GL_ES) && (__VERSION__ >= 300))
#define NEWGL
#define FRAGMENT_INPUT in
#define texture2DArray(tex,coord,bias) texture(tex,coord,bias)
#else
#define texture(tex,uv,bias) texture2D(tex,uv,bias)
#define FRAGMENT_INPUT varying
Expand Down Expand Up @@ -56,8 +54,8 @@ vec4 main_bumpMapping()
vec2 uv1 = uv1_uv2.xy;
vec2 uv2 = uv1_uv2.zw;

vec3 N1 = texture(tex1_nm, uv2, WZ_MIP_LOAD_BIAS).xzy; // y is up in modelSpace
vec3 N2 = texture(tex2_nm, uv1, WZ_MIP_LOAD_BIAS).xzy;
vec3 N1 = texture2DArray(tex_nm, vec3(uv2, 0.f), WZ_MIP_LOAD_BIAS).xzy; // y is up in modelSpace
vec3 N2 = texture2DArray(tex_nm, vec3(uv1, 1.f), WZ_MIP_LOAD_BIAS).xzy;
vec3 N; //use overlay blending to mix normal maps properly
N.x = N1.x < 0.5 ? (2.0 * N1.x * N2.x) : (1.0 - 2.0 * (1.0 - N1.x) * (1.0 - N2.x));
N.z = N1.z < 0.5 ? (2.0 * N1.z * N2.z) : (1.0 - 2.0 * (1.0 - N1.z) * (1.0 - N2.z));
Expand All @@ -71,12 +69,12 @@ vec4 main_bumpMapping()
float lambertTerm = max(dot(N, lightDir), 0.0); // diffuse lighting

// Gaussian specular term computation
float gloss = texture(tex1_sm, uv1, WZ_MIP_LOAD_BIAS).r * texture(tex2_sm, uv2, WZ_MIP_LOAD_BIAS).r;
float gloss = texture2DArray(tex_sm, vec3(uv1, 0.f), WZ_MIP_LOAD_BIAS).r * texture2DArray(tex_sm, vec3(uv2, 1.f), WZ_MIP_LOAD_BIAS).r;
vec3 H = normalize(halfVec);
float exponent = acos(dot(H, N)) / (gloss + 0.05);
float gaussianTerm = exp(-(exponent * exponent));

vec4 fragColor = (texture(tex1, uv1, WZ_MIP_LOAD_BIAS)+texture(tex2, uv2, WZ_MIP_LOAD_BIAS)) * (gloss+vec4(0.08,0.13,0.15,1.0));
vec4 fragColor = (texture2DArray(tex, vec3(uv1, 0.f), WZ_MIP_LOAD_BIAS)+texture2DArray(tex, vec3(uv2, 1.f), WZ_MIP_LOAD_BIAS)) * (gloss+vec4(0.08,0.13,0.15,1.0));
fragColor = fragColor*(ambientLight+diffuseLight*lambertTerm) + specularLight*(1.0-gloss)*gaussianTerm*vec4(1.0,0.843,0.686,1.0);
vec4 lightmap_vec4 = texture(lightmap_tex, uvLightmap, 0.f);
vec4 color = fragColor * vec4(vec3(lightmap_vec4.a), 1.f); // ... * tile brightness / ambient occlusion (stored in lightmap.a);
Expand Down
19 changes: 8 additions & 11 deletions data/base/shaders/vk/terrain_water_high.frag
Expand Up @@ -2,13 +2,10 @@

layout (constant_id = 0) const float WZ_MIP_LOAD_BIAS = 0.f;

layout(set = 1, binding = 0) uniform sampler2D tex1;
layout(set = 1, binding = 1) uniform sampler2D tex2;
layout(set = 1, binding = 2) uniform sampler2D tex1_nm;
layout(set = 1, binding = 3) uniform sampler2D tex2_nm;
layout(set = 1, binding = 4) uniform sampler2D tex1_sm;
layout(set = 1, binding = 5) uniform sampler2D tex2_sm;
layout(set = 1, binding = 6) uniform sampler2D lightmap_tex;
layout(set = 1, binding = 0) uniform sampler2DArray tex;
layout(set = 1, binding = 1) uniform sampler2DArray tex_nm;
layout(set = 1, binding = 2) uniform sampler2DArray tex_sm;
layout(set = 1, binding = 3) uniform sampler2D lightmap_tex;

layout(std140, set = 0, binding = 0) uniform cbuffer {
mat4 ModelViewProjectionMatrix;
Expand Down Expand Up @@ -47,8 +44,8 @@ vec4 main_bumpMapping()
vec2 uv1 = uv1_uv2.xy;
vec2 uv2 = uv1_uv2.zw;

vec3 N1 = texture(tex1_nm, uv2, WZ_MIP_LOAD_BIAS).xzy; // y is up in modelSpace
vec3 N2 = texture(tex2_nm, uv1, WZ_MIP_LOAD_BIAS).xzy;
vec3 N1 = texture(tex_nm, vec3(uv2, 0.f), WZ_MIP_LOAD_BIAS).xzy; // y is up in modelSpace
vec3 N2 = texture(tex_nm, vec3(uv1, 1.f), WZ_MIP_LOAD_BIAS).xzy;
vec3 N; //use overlay blending to mix normal maps properly
N.x = N1.x < 0.5 ? (2.0 * N1.x * N2.x) : (1.0 - 2.0 * (1.0 - N1.x) * (1.0 - N2.x));
N.z = N1.z < 0.5 ? (2.0 * N1.z * N2.z) : (1.0 - 2.0 * (1.0 - N1.z) * (1.0 - N2.z));
Expand All @@ -61,12 +58,12 @@ vec4 main_bumpMapping()
float lambertTerm = max(dot(N, lightDir), 0.0); // diffuse lighting

// Gaussian specular term computation
float gloss = texture(tex1_sm, uv1, WZ_MIP_LOAD_BIAS).r * texture(tex2_sm, uv2, WZ_MIP_LOAD_BIAS).r;
float gloss = texture(tex_sm, vec3(uv1, 0.f), WZ_MIP_LOAD_BIAS).r * texture(tex_sm, vec3(uv2, 1.f), WZ_MIP_LOAD_BIAS).r;
vec3 H = normalize(halfVec);
float exponent = acos(dot(H, N)) / (gloss + 0.05);
float gaussianTerm = exp(-(exponent * exponent));

vec4 fragColor = (texture(tex1, uv1, WZ_MIP_LOAD_BIAS)+texture(tex2, uv2, WZ_MIP_LOAD_BIAS)) * (gloss+vec4(0.08,0.13,0.15,1.0));
vec4 fragColor = (texture(tex, vec3(uv1, 0.f), WZ_MIP_LOAD_BIAS)+texture(tex, vec3(uv2, 1.f), WZ_MIP_LOAD_BIAS)) * (gloss+vec4(0.08,0.13,0.15,1.0));
fragColor = fragColor*(ambientLight+diffuseLight*lambertTerm) + specularLight*(1.0-gloss)*gaussianTerm*vec4(1.0,0.843,0.686,1.0);
vec4 lightmap_vec4 = texture(lightmap_tex, uvLightmap, 0.f);
vec4 color = fragColor * vec4(vec3(lightmap_vec4.a), 1.f); // ... * tile brightness / ambient occlusion (stored in lightmap.a);
Expand Down
11 changes: 4 additions & 7 deletions lib/ivis_opengl/gfx_api.h
Expand Up @@ -1080,13 +1080,10 @@ namespace gfx_api
std::tuple<
vertex_buffer_description<16, gfx_api::vertex_attribute_input_rate::vertex, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float4, 0>> // WaterVertex, w is depth
>, std::tuple<
texture_description<0, sampler_type::anisotropic_repeat>, // tex1
texture_description<1, sampler_type::anisotropic_repeat>, // tex2
texture_description<2, sampler_type::anisotropic_repeat>, // normal map1
texture_description<3, sampler_type::anisotropic_repeat>, // normal map2
texture_description<4, sampler_type::anisotropic_repeat>, // specular map1
texture_description<5, sampler_type::anisotropic_repeat>, // specular map2
texture_description<6, sampler_type::bilinear> // lightmap
texture_description<0, sampler_type::anisotropic_repeat, pixel_format_target::texture_2d_array>, // textures
texture_description<1, sampler_type::anisotropic_repeat, pixel_format_target::texture_2d_array>, // normal maps
texture_description<2, sampler_type::anisotropic_repeat, pixel_format_target::texture_2d_array>, // specular maps
texture_description<3, sampler_type::bilinear> // lightmap
>, SHADER_WATER_HIGH>;

template<>
Expand Down
7 changes: 2 additions & 5 deletions lib/ivis_opengl/gfx_api_gl.cpp
Expand Up @@ -720,7 +720,7 @@ static const std::map<SHADER_MODE, program_data> shader_to_file_table =
"cameraPos", "sunPos",
"emissiveLight", "ambientLight", "diffuseLight", "specularLight",
"fogColor", "fogEnabled", "fogEnd", "fogStart", "timeSec",
"tex1", "tex2", "tex1_nm", "tex2_nm", "tex1_sm", "tex2_sm", "lightmap_tex" } }),
"tex", "tex_nm", "tex_sm", "lightmap_tex" } }),
std::make_pair(SHADER_WATER_CLASSIC, program_data{ "classic water program", "shaders/terrain_water_classic.vert", "shaders/terrain_water_classic.frag",
{ "ModelViewProjectionMatrix", "ModelUVLightmapMatrix", "ShadowMapMVPMatrix", "ModelUV1Matrix", "ModelUV2Matrix",
"cameraPos", "sunPos",
Expand Down Expand Up @@ -2027,10 +2027,7 @@ void gl_pipeline_state_object::set_constants(const gfx_api::constant_buffer_type
setUniforms(i++, 0);
setUniforms(i++, 1);
setUniforms(i++, 2);
setUniforms(i++, 3);
setUniforms(i++, 4);
setUniforms(i++, 5);
setUniforms(i++, 6); // lightmap_tex
setUniforms(i++, 3); // lightmap_tex
}

void gl_pipeline_state_object::set_constants(const gfx_api::constant_buffer_type<SHADER_WATER_CLASSIC>& cbuf)
Expand Down
123 changes: 89 additions & 34 deletions src/terrain.cpp
Expand Up @@ -812,23 +812,16 @@ struct WaterTextures_Normal

struct WaterTextures_High
{
gfx_api::texture* tex1 = nullptr;
gfx_api::texture* tex2 = nullptr;
// water optional textures. null if none
gfx_api::texture* tex1_nm = nullptr;
gfx_api::texture* tex2_nm = nullptr;
gfx_api::texture* tex1_sm = nullptr;
gfx_api::texture* tex2_sm = nullptr;
gfx_api::texture_array* tex = nullptr;
gfx_api::texture_array* tex_nm = nullptr;
gfx_api::texture_array* tex_sm = nullptr;

public:
void clear()
{
delete tex1; tex1 = nullptr;
delete tex2; tex2 = nullptr;
delete tex1_nm; tex1_nm = nullptr;
delete tex2_nm; tex2_nm = nullptr;
delete tex1_sm; tex1_sm = nullptr;
delete tex2_sm; tex2_sm = nullptr;
delete tex; tex = nullptr;
delete tex_nm; tex_nm = nullptr;
delete tex_sm; tex_sm = nullptr;
}
};

Expand Down Expand Up @@ -856,30 +849,92 @@ void loadWaterTextures(int maxTerrainTextureSize)
waterTexturesNormal.clear();
waterTexturesHigh.clear();

auto checkTex = [maxTerrainTextureSize](const WzString &fileName, gfx_api::texture_type type) -> gfx_api::texture* {
WzString fullName = "texpages/" + fileName;
auto imageLoadFilename = gfx_api::imageLoadFilenameFromInputFilename(fullName);
if (!PHYSFS_exists(imageLoadFilename))
{
return nullptr;
}
return gfx_api::context::get().loadTextureFromFile(imageLoadFilename.toUtf8().c_str(), type, maxTerrainTextureSize, maxTerrainTextureSize);
};

if (terrainShaderQuality == TerrainShaderQuality::MEDIUM)
{
auto checkTex = [maxTerrainTextureSize](const WzString &fileName, gfx_api::texture_type type) -> gfx_api::texture* {
WzString fullName = "texpages/" + fileName;
auto imageLoadFilename = gfx_api::imageLoadFilenameFromInputFilename(fullName);
if (!PHYSFS_exists(imageLoadFilename))
{
return nullptr;
}
return gfx_api::context::get().loadTextureFromFile(imageLoadFilename.toUtf8().c_str(), type, maxTerrainTextureSize, maxTerrainTextureSize);
};

waterTexturesNormal.tex1 = checkTex("page-80-water-1.png", gfx_api::texture_type::game_texture);
waterTexturesNormal.tex2 = checkTex("page-81-water-2.png", gfx_api::texture_type::specular_map);
}
else if (terrainShaderQuality == TerrainShaderQuality::NORMAL_MAPPING)
{
waterTexturesHigh.tex1 = checkTex("page-80-water-1.png", gfx_api::texture_type::game_texture);
waterTexturesHigh.tex2 = checkTex("page-81-water-2.png", gfx_api::texture_type::game_texture);
// check water optional textures
waterTexturesHigh.tex1_nm = checkTex("page-80-water-1_nm.png", gfx_api::texture_type::normal_map);
waterTexturesHigh.tex2_nm = checkTex("page-81-water-2_nm.png", gfx_api::texture_type::normal_map);
waterTexturesHigh.tex1_sm = checkTex("page-80-water-1_sm.png", gfx_api::texture_type::specular_map);
waterTexturesHigh.tex2_sm = checkTex("page-81-water-2_sm.png", gfx_api::texture_type::specular_map);
std::vector<WzString> waterTextureFilenames;
std::vector<WzString> waterTextureFilenames_nm;
std::vector<WzString> waterTextureFilenames_sm;

auto optTexturenameToPath = [](const WzString& textureFilename) -> WzString {
if (textureFilename.isEmpty())
{
return WzString();
}
WzString fullName = "texpages/" + textureFilename;
auto imageLoadFilename = gfx_api::imageLoadFilenameFromInputFilename(fullName);
if (!PHYSFS_exists(imageLoadFilename))
{
return WzString();
}
return fullName;
};

// check water optional textures.push_back(optTexturenameToPath("page-80-water-1.png"));
waterTextureFilenames.push_back(optTexturenameToPath("page-81-water-2.png"));
waterTextureFilenames_nm.push_back(optTexturenameToPath("page-80-water-1_nm.png"));
waterTextureFilenames_nm.push_back(optTexturenameToPath("page-81-water-2_nm.png"));
waterTextureFilenames_sm.push_back(optTexturenameToPath("page-80-water-1_sm.png"));
waterTextureFilenames_sm.push_back(optTexturenameToPath("page-81-water-2_sm.png"));

if (!std::all_of(waterTextureFilenames.begin(), waterTextureFilenames.end(), [](const WzString& texturePath) -> bool { return !texturePath.isEmpty(); }))
{
debug(LOG_FATAL, "Missing one or more base water textures?");
return;
}

waterTexturesHigh.tex = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames, gfx_api::texture_type::game_texture, maxTerrainTextureSize, maxTerrainTextureSize, nullptr, []() { resDoResLoadCallback(); });
waterTexturesHigh.tex_nm = nullptr;
waterTexturesHigh.tex_sm = nullptr;

if (std::any_of(waterTextureFilenames_nm.begin(), waterTextureFilenames_nm.end(), [](const WzString& filename) {
return !filename.isEmpty();
}))
{
waterTexturesHigh.tex_nm = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames_nm, gfx_api::texture_type::normal_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr<iV_Image> {
std::unique_ptr<iV_Image> pDefaultNormalMap = std::unique_ptr<iV_Image>(new iV_Image);
pDefaultNormalMap->allocate(width, height, channels, true);
// default normal map: (0,0,1)
unsigned char* pBmpWrite = pDefaultNormalMap->bmp_w();
memset(pBmpWrite, 0x7f, pDefaultNormalMap->data_size());
if (channels >= 3)
{
size_t pixelIncrement = static_cast<size_t>(channels);
for (size_t b = 0; b < pDefaultNormalMap->data_size(); b += pixelIncrement)
{
pBmpWrite[b+2] = 0xff; // blue=z
}
}
return pDefaultNormalMap;
}, []() { resDoResLoadCallback(); });
ASSERT(waterTexturesHigh.tex_nm != nullptr, "Failed to load water normals");
}
if (std::any_of(waterTextureFilenames_sm.begin(), waterTextureFilenames_sm.end(), [](const WzString& filename) {
return !filename.isEmpty();
}))
{
waterTexturesHigh.tex_sm = gfx_api::context::get().loadTextureArrayFromFiles(waterTextureFilenames_sm, gfx_api::texture_type::specular_map, maxTerrainTextureSize, maxTerrainTextureSize, [](int width, int height, int channels) -> std::unique_ptr<iV_Image> {
std::unique_ptr<iV_Image> pDefaultSpecularMap = std::unique_ptr<iV_Image>(new iV_Image);
// default specular map: 0
pDefaultSpecularMap->allocate(width, height, channels, true);
return pDefaultSpecularMap;
}, []() { resDoResLoadCallback(); });
ASSERT(waterTexturesHigh.tex_sm != nullptr, "Failed to load water specular maps");
}
}
else
{
Expand Down Expand Up @@ -2022,13 +2077,13 @@ void drawWaterHighImpl(const glm::mat4 &ModelViewProjection, const Vector3f &cam
const auto ModelUV2 = glm::transpose(glm::mat4(paramsX2, paramsY2, glm::vec4(0,0,1,0), glm::vec4(0,0,0,1)));
const auto &renderState = getCurrentRenderState();

ASSERT_OR_RETURN(, waterTexturesHigh.tex1 && waterTexturesHigh.tex2, "Failed to load water texture");
ASSERT_OR_RETURN(, waterTexturesHigh.tex, "Failed to load water textures");
PSO::get().bind();

PSO::get().bind_textures(
waterTexturesHigh.tex1, waterTexturesHigh.tex2,
waterTexturesHigh.tex1_nm, waterTexturesHigh.tex2_nm,
waterTexturesHigh.tex1_sm, waterTexturesHigh.tex2_sm,
waterTexturesHigh.tex,
waterTexturesHigh.tex_nm,
waterTexturesHigh.tex_sm,
lightmap_texture);
PSO::get().bind_vertex_buffers(waterVBO);
PSO::get().bind_constants({
Expand Down

0 comments on commit 0e1c3fb

Please sign in to comment.