Skip to content

Commit

Permalink
Shader: Separate weight calculation, add blend sharpness, more docume…
Browse files Browse the repository at this point in the history
…ntation
  • Loading branch information
TokisanGames committed Nov 1, 2023
1 parent 9c6f42b commit 86186e2
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 73 deletions.
13 changes: 6 additions & 7 deletions src/shaders/debug_views.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -26,38 +26,37 @@ R"(

//INSERT: DEBUG_COLORMAP
// Show colormap
ALBEDO = color_tex.rgb;
ALBEDO = color_map.rgb;
ROUGHNESS = 0.7;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

//INSERT: DEBUG_ROUGHMAP
// Show roughness map
ALBEDO = vec3(color_tex.a);
ALBEDO = vec3(color_map.a);
ROUGHNESS = 0.7;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

//INSERT: DEBUG_CONTROLMAP
// Show control map
index00.rg *= 8.; // Since there are only 32 colors, emphasize change between each one
ALBEDO = index00.rgb;
ALBEDO = vec3(float(mat[0].base)/32., float(mat[0].over)/32., float(mat[0].blend)/8.);
ROUGHNESS = 0.7;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

//INSERT: DEBUG_TEXTURE_HEIGHT
// Show height textures
ALBEDO = vec3(color.a);
ALBEDO = vec3(albedo_height.a);
ROUGHNESS = 0.7;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

//INSERT: DEBUG_TEXTURE_NORMAL
// Show normal map textures
ALBEDO = normal.rgb;
ALBEDO = normal_rough.rgb;
ROUGHNESS = 0.7;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

//INSERT: DEBUG_TEXTURE_ROUGHNESS
// Show roughness textures
ALBEDO = vec3(normal.a);
ALBEDO = vec3(normal_rough.a);
ROUGHNESS = 0.7;
NORMAL_MAP = vec3(0.5, 0.5, 1.0);

Expand Down
168 changes: 102 additions & 66 deletions src/shaders/main.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlic
*
*/

const float RANGE[] = { 0.0f, .125f, .25f, .334f, .5f, .667f, .8f, 1.0f };

// Private uniforms
uniform float _region_size = 1024.0;
uniform float _region_texel_size = 0.0009765625; // = 1./1024.
uniform int _region_map_size = 16;
Expand All @@ -36,6 +35,21 @@ uniform float _texture_uv_scale_array[32];
uniform float _texture_uv_rotation_array[32];
uniform vec4 _texture_color_array[32];

// Public uniforms
uniform float height_blend_sharpness : hint_range(0.001, 1) = 0.13;

// Varyings & Types

const float RANGE[] = { 0.0f, .125f, .25f, .334f, .5f, .667f, .8f, 1.0f };

struct Material {
vec4 alb_ht;
vec4 nrm_rg;
uint base;
uint over;
uint blend;
};

varying vec3 v_vertex; // World coordinate vertex location

////////////////////////
Expand All @@ -44,27 +58,27 @@ varying vec3 v_vertex; // World coordinate vertex location

// Takes in UV world space coordinates, returns ivec3 with:
// XY: (0 to _region_size) coordinates within a region
// Z: region index used for texturearrays, -1 if not in a region
// Z: layer index used for texturearrays, -1 if not in a region
ivec3 get_region_uv(vec2 uv) {
uv *= _region_texel_size;
ivec2 pos = ivec2(floor(uv)) + (_region_map_size / 2);
int index = _region_map[ pos.y * _region_map_size + pos.x ] - 1;
return ivec3(ivec2((uv - _region_offsets[index]) * _region_size), index);
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] - 1;
return ivec3(ivec2((uv - _region_offsets[layer_index]) * _region_size), layer_index);
}

// Takes in UV2 region space coordinates, returns vec3 with:
// XY: (0 to 1) coordinates within a region
// Z: region index used for texturearrays, -1 if not in a region
// Z: layer index used for texturearrays, -1 if not in a region
vec3 get_region_uv2(vec2 uv) {
ivec2 pos = ivec2(floor(uv)) + (_region_map_size / 2);
int index = _region_map[ pos.y * _region_map_size + pos.x ] - 1;
return vec3(uv - _region_offsets[index], float(index));
int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] - 1;
return vec3(uv - _region_offsets[layer_index], float(layer_index));
}

//INSERT: WORLD_NOISE1
// 1 lookup
float get_height(vec2 uv) {
float height = 0.0;
highp float height = 0.0;
vec3 region = get_region_uv2(uv);
if (region.z >= 0. && abs(uv.x) < float(_region_uv_limit) && abs(uv.y) < float(_region_uv_limit)) {
height = texture(_height_maps, region).r;
Expand Down Expand Up @@ -95,6 +109,12 @@ void vertex() {
// Fragment
////////////////////////

float blend_weights(float weight, float detail) {
weight = sqrt(weight * 0.5);
float result = max(0.1 * weight, 10.0 * (weight + detail) + 1.0f - (detail + 10.0));
return result;
}

// 4 lookups
vec3 get_normal(vec2 uv, out vec3 tangent, out vec3 binormal) {
float left = get_height(uv + vec2(-_region_texel_size, 0));
Expand Down Expand Up @@ -125,59 +145,58 @@ float random(in vec2 xy) {
return fract(sin(dot(xy, vec2(12.9898, 78.233))) * 43758.5453);
}

float blend_weights(float weight, float detail) {
weight = sqrt(weight * 0.5);
float result = max(0.1 * weight, 10.0 * (weight + detail) + 1.0f - (detail + 10.0));
return result;
}

vec4 height_blend(vec4 a_value, float a_bump, vec4 b_value, float b_bump, float t) {
float ma = max(a_bump + (1.0 - t), b_bump + t) - 0.1;
float ba = max(a_bump + (1.0 - t) - ma, 0.0);
float bb = max(b_bump + t - ma, 0.0);
return (a_value * ba + b_value * bb) / (ba + bb);
}

vec2 rotate(vec2 v, float cosa, float sina) {
return vec2(cosa * v.x - sina * v.y, sina * v.x + cosa * v.y);
}

vec3 height_blend(vec4 a_value, float a_height, vec4 b_value, float b_height, float t) {
float ma = max(a_height + (1.0 - t), b_height + t) - height_blend_sharpness;
float b1 = max(a_height + (1.0 - t) - ma, 0.0);
float b2 = max(b_height + t - ma, 0.0);
return (a_value.rgb * b1 + b_value.rgb * b2) / (b1 + b2);
}

// 2-4 lookups
vec4 get_material(vec2 uv, uint control, vec2 uv_center, float weight, inout float total_weight, inout vec4 out_normal) {
void get_material(vec2 uv, uint control, ivec2 iuv_center, out Material out_mat) {
out_mat = Material(vec4(0.), vec4(0.), 0u, 0u, 0u);
uint base_tex = control>>27u & 0x1Fu;
out_mat.base = base_tex;
vec2 uv_center = vec2(iuv_center);
float r = random(uv_center) * PI;
float rand = r * _texture_uv_rotation_array[base_tex];
vec2 rot = vec2(cos(rand), sin(rand));
uv *= .5; // Allow larger numbers on uv scale array - move to C++
vec2 matUV = rotate(uv, rot.x, rot.y) * _texture_uv_scale_array[base_tex];

vec4 albedo = texture(_texture_array_albedo, vec3(matUV, float(base_tex)));
albedo.rgb *= _texture_color_array[base_tex].rgb;
vec4 normal = texture(_texture_array_normal, vec3(matUV, float(base_tex)));
vec3 n = unpack_normal(normal);
normal.xz = rotate(n.xz, rot.x, -rot.y);
vec4 albedo_ht = texture(_texture_array_albedo, vec3(matUV, float(base_tex)));
albedo_ht.rgb *= _texture_color_array[base_tex].rgb;
vec4 normal_rg = texture(_texture_array_normal, vec3(matUV, float(base_tex)));
vec3 n = unpack_normal(normal_rg);
normal_rg.xz = rotate(n.xz, rot.x, -rot.y);

uint blend_idx = control >> 19u & 0x7u;
out_mat.blend = blend_idx;

if (blend_idx > 0u) {
uint over_tex = control >> 22u & 0x1Fu;
out_mat.over = over_tex;
float rand2 = r * _texture_uv_rotation_array[over_tex];
vec2 rot2 = vec2(cos(rand2), sin(rand2));
vec2 matUV2 = rotate(uv, rot2.x, rot2.y) * _texture_uv_scale_array[over_tex];
vec4 albedo2 = texture(_texture_array_albedo, vec3(matUV2, float(over_tex)));
albedo2.rgb *= _texture_color_array[over_tex].rgb;
vec4 normal2 = texture(_texture_array_normal, vec3(matUV2, float(over_tex)));
n = unpack_normal(normal2);
normal2.xz = rotate(n.xz, rot2.x, -rot2.y);

albedo = height_blend(albedo, albedo.a, albedo2, albedo2.a, RANGE[blend_idx]);
normal = height_blend(normal, albedo.a, normal2, albedo2.a, RANGE[blend_idx]);
vec4 albedo_ht2 = texture(_texture_array_albedo, vec3(matUV2, float(over_tex)));
albedo_ht2.rgb *= _texture_color_array[over_tex].rgb;
vec4 normal_rg2 = texture(_texture_array_normal, vec3(matUV2, float(over_tex)));
n = unpack_normal(normal_rg2);
normal_rg2.xz = rotate(n.xz, rot2.x, -rot2.y);

albedo_ht.rgb = height_blend(albedo_ht, albedo_ht.a, albedo_ht2, albedo_ht2.a, RANGE[blend_idx]);
normal_rg.rgb = height_blend(normal_rg, albedo_ht.a, normal_rg2, albedo_ht2.a, RANGE[blend_idx]);
}

normal = pack_normal(normal.xyz, normal.a);
weight = blend_weights(weight, albedo.a);
out_normal += normal * weight;
total_weight += weight;
return albedo * weight;
normal_rg = pack_normal(normal_rg.xyz, normal_rg.a);
out_mat.alb_ht = albedo_ht;
out_mat.nrm_rg = normal_rg;
return;
}

void fragment() {
Expand All @@ -188,57 +207,74 @@ void fragment() {
TANGENT = mat3(VIEW_MATRIX) * w_tangent;
BINORMAL = mat3(VIEW_MATRIX) * w_binormal;

// Calculated Weighted Material
// https://github.com/cdxntchou/IndexMapTerrain/blob/master/Assets/Terrain/Shaders/IndexedTerrainShader.shader
// Idenfity 4 vertices surrounding this pixel
vec2 texel_pos = UV;
vec2 texel_pos_floor = floor(UV);
highp vec2 texel_pos_floor = floor(UV);

// Create a cross hatch grid of alternating 0/1 horizontal and vertical stripes 1 unit wide in XY
vec4 mirror = vec4(fract(texel_pos_floor * 0.5) * 2.0, 1.0, 1.0);
// And the opposite grid in ZW
mirror.zw = vec2(1.0) - mirror.xy;

// Get the region and control map ID of four vertices surrounding this pixel
// Get the region and control map ID for the vertices
ivec3 index00UV = get_region_uv(texel_pos_floor + mirror.xy);
ivec3 index01UV = get_region_uv(texel_pos_floor + mirror.xw);
ivec3 index10UV = get_region_uv(texel_pos_floor + mirror.zy);
ivec3 index11UV = get_region_uv(texel_pos_floor + mirror.zw);

// Lookup adjacent vertices. 4 lookups
uint control00 = texelFetch(_control_maps, index00UV, 0).r;
uint control01 = texelFetch(_control_maps, index01UV, 0).r;
uint control10 = texelFetch(_control_maps, index10UV, 0).r;
uint control11 = texelFetch(_control_maps, index11UV, 0).r;

// Get the textures for each vertex. 8-16 lookups (2-4 ea)
Material mat[4];
get_material(UV, control00, index00UV.xy, mat[0]);
get_material(UV, control01, index01UV.xy, mat[1]);
get_material(UV, control10, index10UV.xy, mat[2]);
get_material(UV, control11, index11UV.xy, mat[3]);

// Calculate weight for the pixel position between the vertices
// Bilinear interpolate difference of UV and floor UV
// Bilinear interpolation of difference of UV and floor(UV)
vec2 weights1 = clamp(texel_pos - texel_pos_floor, 0, 1);
weights1 = mix(weights1, vec2(1.0) - weights1, mirror.xy);
vec2 weights0 = vec2(1.0) - weights1;

float total_weight = 0.0;
vec4 normal = vec4(0.0);
vec4 color = vec4(0.0);

// Accumulate material. 8-16 lookups
color = get_material(UV, control00, vec2(index00UV.xy), weights0.x * weights0.y, total_weight, normal);
color += get_material(UV, control01, vec2(index01UV.xy), weights0.x * weights1.y, total_weight, normal);
color += get_material(UV, control10, vec2(index10UV.xy), weights1.x * weights0.y, total_weight, normal);
color += get_material(UV, control11, vec2(index11UV.xy), weights1.x * weights1.y, total_weight, normal);
total_weight = 1.0 / total_weight;
normal *= total_weight;
color *= total_weight;

// Apply Colormap. 1 lookup
vec4 weights;
weights.x = blend_weights(weights0.x * weights0.y, mat[0].alb_ht.a);
weights.y = blend_weights(weights0.x * weights1.y, mat[1].alb_ht.a);
weights.z = blend_weights(weights1.x * weights0.y, mat[2].alb_ht.a);
weights.w = blend_weights(weights1.x * weights1.y, mat[3].alb_ht.a);
float weight_sum = weights.x + weights.y + weights.z + weights.w;
float weight_inv = 1.0/weight_sum;

// Calculate weighted average of albedo & height
vec4 albedo_height = weight_inv * (
mat[0].alb_ht * weights.x +
mat[1].alb_ht * weights.y +
mat[2].alb_ht * weights.z +
mat[3].alb_ht * weights.w );

// Calculate weighted average of normal & rough
vec4 normal_rough = weight_inv * (
mat[0].nrm_rg * weights.x +
mat[1].nrm_rg * weights.y +
mat[2].nrm_rg * weights.z +
mat[3].nrm_rg * weights.w );

// Get Colormap. 1 lookup
vec3 ruv = get_region_uv2(UV2);
vec4 color_tex = vec4(1., 1., 1., .5);
vec4 color_map = vec4(1., 1., 1., .5);
if (ruv.z >= 0.) {
color_tex = texture(_color_maps, ruv);
color_map = texture(_color_maps, ruv);
}

// Apply PBR
ALBEDO = color.rgb * color_tex.rgb;
ROUGHNESS = clamp(fma(color_tex.a-0.5, 2.0, normal.a), 0., 1.);
NORMAL_MAP = normal.rgb;

ALBEDO = albedo_height.rgb * color_map.rgb;
// Rougness + (rough_modifier-.5)*2 // Calc converts (0 to 1) to (-1 to 1)
ROUGHNESS = clamp(fma(color_map.a-0.5, 2.0, normal_rough.a), 0., 1.);
NORMAL_MAP = normal_rough.rgb;
NORMAL_MAP_DEPTH = 1.0;

//INSERT: DEBUG_CHECKERED
Expand Down

0 comments on commit 86186e2

Please sign in to comment.