C++/OpenGL/GLSL多层高度纹理融合的边缘平滑与随机控制问题
多层高度纹理平滑混合问题
效果截图

问题描述
我需要基于网格顶点的高度值,在网格上混合多层纹理。当前实现的纹理过渡边缘为硬切边,我想要实现边缘的平滑融合效果,同时希望边缘带有一定随机性,且可以调整随机度参数。
原始实现代码
#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); }
解决方案
你之前的代码核心问题是用了硬分支判断纹理选择,就算加了噪声偏移高度,本质还是二选一的切边,没有真正的混合过程。下面是修改后的实现,支持平滑过渡、随机边界扰动,还有参数可调:
首先新增两个可调参数的uniform声明,放在代码头部的uniform定义区域即可:
uniform float _TransitionWidth; // 过渡带宽度,数值越大融合范围越宽 uniform float _RandomStrength; // 边界随机强度,0为完全平滑无随机,数值越大随机扭曲越明显
修改GetTextureColorBasedOnHeight函数,改用权重混合逻辑:
vec4 GetTextureColorBasedOnHeight(vec2 coord){ // 计算噪声扰动,叠加到高度值上实现随机边界 float nv = noise(coord * 0.01f) * _RandomStrength; float adjustedHeight = height + nv; vec4 finalColor = vec4(0.0); float totalWeight = 0.0; for(int i=0;i<NUM_TEXTURE_LAYERS;i++){ float layerMin = _DiffuseTexturesHeights[i].x; float layerMax = _DiffuseTexturesHeights[i].y; float texScale = _DiffuseTexturesHeights[i].z; // 用smoothstep计算当前层的权重,实现上下边界的平滑过渡 float lowerWeight = smoothstep(layerMin - _TransitionWidth, layerMin + _TransitionWidth, adjustedHeight); float upperWeight = smoothstep(layerMax + _TransitionWidth, layerMax - _TransitionWidth, adjustedHeight); float weight = lowerWeight * upperWeight; // 累加当前层的颜色贡献和总权重 finalColor += texture(_DiffuseTextures[i], coord * texScale) * weight; totalWeight += weight; } // 权重归一化,避免过渡区域颜色变暗 if(totalWeight > 0.0){ finalColor /= totalWeight; } return finalColor; }
参数调整说明
- 调整
_TransitionWidth参数控制过渡区域的宽度,可根据你的高度值范围设置,比如高度范围是0100的话,可以先试0.55的数值 - 调整
_RandomStrength控制边界的随机扭曲程度,不需要随机就设为0即可 - 如果觉得噪声的颗粒度不符合需求,可以修改
noise(coord * 0.01f)里的缩放系数,数值越小噪声越平缓,越大噪声越细碎
内容的提问来源于stack exchange,提问作者Jaysmito Mukherjee
相关产品推荐
相关产品推荐

