C++/OpenGL/GLSL 混合两个纹理边缘
C++/OpenGL/GLSL Blending two textures edges
首先是截图:
我正在尝试根据网格上点的高度在网格上混合多个纹理。
现在我想要实现的是边界处的平滑混合(与当前的锐线不同)。
我也希望边框稍微随机一点,并且有一个因素来控制随机性。
这是我当前的代码:
#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);
}
我确实尝试了随机边界,但这不是很好,问题仍然在于混合!
这里是带有随机性的代码:
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);
}
一般来说,您会希望进行多次纹理查找并将结果组合在一起,很可能是通过加权平均。
一种直接的方法是始终对所有可用的纹理进行采样(对于影响为 0 的纹理,可选择提前输出)。这样,“组合”逻辑和“影响确定”逻辑就被清晰地分开了。这是非常灵活的,因为它不会限制您创建平滑的边缘。您可以使用任何您想要的组合逻辑,例如使用影响图。
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;
}
跟进: 至于使用它来创建模糊边缘:
您当前的代码实际上与以下配置文件相同:在每一点,一个纹理得到完全影响,所有其他纹理得到 none。这将创建一个方形步骤,如下所示:
influence
^
| Sharp edge
| |
| v
| |--------------||--------------|
| | || |
| | Tex[0] || Tex[1] |
| | || |
+-----------------------------------> Height
相反,重叠纹理的影响(如下图所示)将使您从一个纹理平滑过渡到下一个纹理。
influence
^
| Blurred edge
| |
| v
| --------- -----------
| / \ / \
| / Tex[0] /\ Tex[1] \
| / / \ \
+-----------------------------------> Height
使用多项式或指数衰减而不是线性衰减也会给出不同的结果,要找到能够导致您喜欢的结果的确切数学需要进行实验。
感谢Frank的回答,我终于可以拥有自己喜欢的东西了,
结果如下:https://youtu.be/DSkJqPhdRYI
代码:
#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);
}
如果您对完整来源感兴趣:https://github.com/Jaysmito101/TerraGen3D
首先是截图:
我正在尝试根据网格上点的高度在网格上混合多个纹理。
现在我想要实现的是边界处的平滑混合(与当前的锐线不同)。
我也希望边框稍微随机一点,并且有一个因素来控制随机性。
这是我当前的代码:
#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);
}
我确实尝试了随机边界,但这不是很好,问题仍然在于混合!
这里是带有随机性的代码:
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);
}
一般来说,您会希望进行多次纹理查找并将结果组合在一起,很可能是通过加权平均。
一种直接的方法是始终对所有可用的纹理进行采样(对于影响为 0 的纹理,可选择提前输出)。这样,“组合”逻辑和“影响确定”逻辑就被清晰地分开了。这是非常灵活的,因为它不会限制您创建平滑的边缘。您可以使用任何您想要的组合逻辑,例如使用影响图。
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;
}
跟进: 至于使用它来创建模糊边缘:
您当前的代码实际上与以下配置文件相同:在每一点,一个纹理得到完全影响,所有其他纹理得到 none。这将创建一个方形步骤,如下所示:
influence
^
| Sharp edge
| |
| v
| |--------------||--------------|
| | || |
| | Tex[0] || Tex[1] |
| | || |
+-----------------------------------> Height
相反,重叠纹理的影响(如下图所示)将使您从一个纹理平滑过渡到下一个纹理。
influence
^
| Blurred edge
| |
| v
| --------- -----------
| / \ / \
| / Tex[0] /\ Tex[1] \
| / / \ \
+-----------------------------------> Height
使用多项式或指数衰减而不是线性衰减也会给出不同的结果,要找到能够导致您喜欢的结果的确切数学需要进行实验。
感谢Frank的回答,我终于可以拥有自己喜欢的东西了,
结果如下:https://youtu.be/DSkJqPhdRYI
代码:
#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);
}
如果您对完整来源感兴趣:https://github.com/Jaysmito101/TerraGen3D