Home > Blockchain >  C /OpenGL/GLSL Blending two textures edges
C /OpenGL/GLSL Blending two textures edges

Time:10-22

Firstly here is the screenshot:

enter image description here

I am trying to blend in multiple textures on the mesh based on the height of the point on mesh.

Now what i want to achieve is a smooth blend at the borders(unlike what is currently a sharp line).

Also i would like the border to be slightly random and have a factor to control the randomness.

Here is my current code :

#version 430 core
out vec4 FragColor;

#define NUM_TEXTURE_LAYERS 8

uniform vec3 _LightPosition;
uniform vec3 _LightColor;

in float height;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;

uniform sampler2D  _DiffuseTextures[NUM_TEXTURE_LAYERS];
uniform vec3  _DiffuseTexturesHeights[NUM_TEXTURE_LAYERS];

vec4 GetTextureColorBasedOnHeight(vec2 coord){
    for(int i=0;i<NUM_TEXTURE_LAYERS;i  ){
        if(height > _DiffuseTexturesHeights[i].x && height < _DiffuseTexturesHeights[i].y){
            return texture(_DiffuseTextures[i], coord*_DiffuseTexturesHeights[i].z);
        }
    }
    return vec4(0.0f);
}

void main()
{   
    vec3 objectColor = vec3(1, 1, 1);
    objectColor = GetTextureColorBasedOnHeight(TexCoord).xyz;
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(_LightPosition - FragPos);
    float diff = max(dot(norm, lightDir), 0.0f);
    vec3 diffuse = diff * _LightColor;
    vec3 result = (vec3(0.2, 0.2, 0.2)   diffuse) * objectColor;
    FragColor = vec4(result, 1.0);
} 

I did try the random boundaries but that was not very good and still blending is the issue!

Here is the code with randomness:

vec2 hash( vec2 p ) // replace this by something better
{
    p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) );
    return -1.0   2.0*fract(sin(p)*43758.5453123);
}

float noise( in vec2 p )
{
    const float K1 = 0.366025404; // (sqrt(3)-1)/2;
    const float K2 = 0.211324865; // (3-sqrt(3))/6;

    vec2  i = floor( p   (p.x p.y)*K1 );
    vec2  a = p - i   (i.x i.y)*K2;
    float m = step(a.y,a.x); 
    vec2  o = vec2(m,1.0-m);
    vec2  b = a - o   K2;
    vec2  c = a - 1.0   2.0*K2;
    vec3  h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 );
    vec3  n = h*h*h*h*vec3( dot(a,hash(i 0.0)), dot(b,hash(i o)), dot(c,hash(i 1.0)));
    return dot( n, vec3(70.0) );
}


vec4 GetTextureColorBasedOnHeight(vec2 coord){
    for(int i=0;i<NUM_TEXTURE_LAYERS;i  ){
        float nv=noise(coord*0.01f);
        if(height nv > _DiffuseTexturesHeights[i].x && height nv < _DiffuseTexturesHeights[i].y){
            return texture(_DiffuseTextures[i], coord*_DiffuseTexturesHeights[i].z);
        }
    }
    return vec4(0.0f);
}

CodePudding user response:

Generally speaking, you will want to do multiple texture lookups and combine the results together, mostly likely via a weighted average.

A straightforward way to go about it it to always sample all available textures (optionally with an early out for textures with 0 influence). This way, the "combining" logic and "influence determination" logic are kept cleanly separated. This is very flexible, as it doesn't limit you to creating smooth edges. You can use any combining logic you want, using an influence-map for example.

float getTextureInfluence(int tex_id, vec2 coord) {
  // have fun!
}

vec4 GetTextureColor(vec2 coord) {
    vec4 accum = vec4(0.0);
    float total_influence = 0.0;
    
    for(int i = 0; i < NUM_TEXTURE_LAYERS ; i  ) {
      float texture_influence = getTextureInfluence(i, coord);

      // This if is optional. 
      // It may help or hurt performance depending on what 
      // percentage of samplers are used on average per sample.
      if(texture_influence > INFLUENCE_MIN) {
        total_influence  = texture_influence;
        accum  = texture(_DiffuseTextures[i], coord) * texture_influence;
      }
    }

    if(total_influence > 0.0) {
      accum /= total_influence ;
    }
    return accum;
}

Followup: As far as using this to create blurred edges:

Your current code is effectively the same thing as the following profile: at every point, one texture gets full influence and all other textures get none. This creates a square step like so:


influence
^
|           Sharp edge
|                |
|                v
| |--------------||--------------|
| |              ||              |
| |     Tex[0]   ||   Tex[1]     |
| |              ||              |
 -----------------------------------> Height

Instead, overlapping the textures' influence like the following pattern will give you a smooth transition from one texture to the next.

influence
^
|           Blurred edge
|                |
|                v
|      ---------    -----------
|     /         \  /           \
|   /   Tex[0]   /\    Tex[1]    \
| /            /    \              \
 -----------------------------------> Height

Using a polynomial or exponential decay instead of linear also gives different results, finding the exact math that will lead to the results you prefer will take experimentation.

CodePudding user response:

Thanks to Frank's answer, I am able to finally have something i like,

Here is the result: https://youtu.be/DSkJqPhdRYI

The Code :

#version 430 core
out vec4 FragColor;

#define NUM_TEXTURE_LAYERS 8

uniform vec3 _LightPosition;
uniform vec3 _LightColor;

in float height;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;

uniform sampler2D  _DiffuseTextures[NUM_TEXTURE_LAYERS];
uniform vec3  _DiffuseTexturesHeights[NUM_TEXTURE_LAYERS];


vec2 hash( vec2 p ) // replace this by something better
{
    p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) );
    return -1.0   2.0*fract(sin(p)*43758.5453123);
}

float noise( in vec2 p )
{
    const float K1 = 0.366025404; // (sqrt(3)-1)/2;
    const float K2 = 0.211324865; // (3-sqrt(3))/6;

    vec2  i = floor( p   (p.x p.y)*K1 );
    vec2  a = p - i   (i.x i.y)*K2;
    float m = step(a.y,a.x); 
    vec2  o = vec2(m,1.0-m);
    vec2  b = a - o   K2;
    vec2  c = a - 1.0   2.0*K2;
    vec3  h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 );
    vec3  n = h*h*h*h*vec3( dot(a,hash(i 0.0)), dot(b,hash(i o)), dot(c,hash(i 1.0)));
    return dot( n, vec3(70.0) );
}


float getTextureInfluence(int i, vec2 coord) {
float h = height   noise(coord*5)*2;
  float midVal = (_DiffuseTexturesHeights[i].x   _DiffuseTexturesHeights[i].y)/2;
  float p = 0;
  if(height < midVal)
    p = _DiffuseTexturesHeights[i].x - height;
  if(height >= midVal)
    p =height -  _DiffuseTexturesHeights[i].y;
  
  return pow(2.713, -1.0*p);
}

vec4 GetTextureColorBasedOnHeight(vec2 coord){
    vec4 accum = vec4(0.0);
    float total_influence = 0.0;
    
    for(int i=0; i < NUM_TEXTURE_LAYERS ; i  ){
        float texture_influence = getTextureInfluence(i, coord*_DiffuseTexturesHeights[i].z);

        total_influence  = texture_influence;
        accum  = texture(_DiffuseTextures[i],  coord*_DiffuseTexturesHeights[i].z) * texture_influence;
    }

    if(total_influence > 0) {
      accum /= total_influence ;
    }
    return accum;
}


void main()
{   
    vec3 objectColor = vec3(1, 1, 1);
    objectColor = GetTextureColorBasedOnHeight(TexCoord).xyz;
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(_LightPosition - FragPos);
    float diff = max(dot(norm, lightDir), 0.0f);
    vec3 diffuse = diff * _LightColor;
    vec3 result = (vec3(0.2, 0.2, 0.2)   diffuse) * objectColor;
    FragColor = vec4(result, 1.0);
} 

If you are interested in the full source : https://github.com/Jaysmito101/TerraGen3D

  • Related