2017-09-20 4 views
0

Je projette des ombres sur une texture d'écran en fonction des valeurs d'une autre texture.Striation dans mes ombres

Ma texture « profondeur », ce n'est pas vraiment la profondeur, à des valeurs de couleur qui sont échantillonnées pour des hauteurs, ressemble à ceci:

enter image description here

On peut dire que le canal rouge est mon heightmap.

Je dessine ce à côté d'une texture d'herbe sur un quad en plein écran avec le shader frag suivant:

#version 400 

layout(location=0) out vec4 frag_colour; 

in vec2 texelCoords; 

uniform sampler2D uTexture; 
uniform sampler2D uTextureHeightmap; 
uniform float uSunDistance = -10000000.0; 
uniform float uSunInclination; 
uniform float uSunAzimuth; 
uniform float uQuality; 

void main() 
{ 
    vec4 c = texture(uTexture,texelCoords); 

    vec2 textureD = textureSize(uTexture,0); 
    float d = max(textureD.x,textureD.y); 
    float aspectCorrection = textureD.x/textureD.y; 

    vec3 sunPosition = vec3(textureD.x/2,textureD.y/2,0) + vec3( uSunDistance*sin(uSunInclination)*cos(uSunAzimuth), 
                    uSunDistance*sin(uSunInclination)*sin(uSunAzimuth), 
                    uSunDistance*cos(uSunInclination) ); 

    vec4 heights = texture(uTextureHeightmap, texelCoords); 
    float height = max(max(heights.r,heights.g),heights.b); 
    vec3 direction = normalize(vec3(texelCoords,height) - sunPosition); 
    direction.y *= aspectCorrection; 

    float sampleDistance = 0; 

    float samples = d*uQuality; 

    float stepSize = 1.0/((samples/d) * d); 

    for(int i = 0; i < samples; i++) 
    { 
     sampleDistance += stepSize; 

     vec3 newPoint = vec3(texelCoords,height) + direction * sampleDistance; 
     if(newPoint.z > 1.0) 
      break; 

     vec4 h = texture(uTextureHeightmap,newPoint.xy); 
     float base = h.r; 
     float middle = h.g; 
     float top = h.b; 

     if(newPoint.z < base) 
     { 
      c *= 0.5; 
      break; 
     } 
     if(newPoint.z >= middle && newPoint.z <= top) 
     { 
      c *= 0.5; 
      break; 
     } 
    } 

    frag_colour = c; 
} 

Un échantillon de la sortie est:

enter image description here

Le strie n'a pas été cherché. Je peux utiliser cette même méthode avec des «bords» plus nets au lieu de contours lisses, et tout semble très bien. Ce sont les gradients comme celui-ci qui causent des problèmes.

+0

Pouvez-vous expliquer ce que vous voulez réaliser et comment fonctionne votre shader? Comprendre cela sans aucun indice est assez difficile. –

+0

@NicoSchertler le shader se dirige vers le soleil; déterminer si le pixel suivant dans cette étape est au-dessus ou au-dessous du pixel original. Si c'est au dessus, on dit que le pixel d'origine doit donc être dans l'ombre et que sa couleur est légèrement plus sombre. L'effet désiré est l'ombre – NeomerArcana

+0

Ne vous éloignez-vous pas du soleil (points de direction du soleil à la position de texel)? La taille du pas ne devrait-elle pas dépendre de 'direction.z' (vous voulez que les étapes' samples' aboutissent à la hauteur maximale)? Votre texture coordonne-t-elle les composantes x/y de la position du monde? Et les hauteurs de la carte représentent-elles directement les hauteurs du monde (sans échelle)? –

Répondre

1

Pour améliorer la qualité de l'algorithme, il est bien sûr possible d'augmenter le nombre d'échantillons. Acheter une amélioration de la qualité peut également être réalisé, par une limitation de la distance de foulée. La distance minimale significative d'une étape est déterminée par la résolution de la carte de hauteur, car cela n'a aucun sens de tester la même hauteur de la carte de hauteur deux fois.

float d = max(textureD.x,textureD.y); 
float minStepSize = 1.0/d; 

La distance significative maximale est atteinte, si la distance du faisceau lumineux à partir de la position sur le terrain de la position du soleil, atteint une hauteur de 1,0.

heightmap maximum distance

La distance d'un pas est donné, en divisant la distance maximum par le nombre d'échantillons, mais il doit être au moins la distance minimale donnée par la résolution de la carte de la hauteur:

float minStepSize = 1.0/d; 
float maxDist  = (1.0 - height) * length(direction.xy)/abs(direction.z); 
float stepSize = max(minStepSize, maxDist/samples); 
float shadow  = 1.0; 
vec3 startPoint = vec3(texelCoords, height); 
for (float sampleDist = stepSize; sampleDist <= maxDist; sampleDist += stepSize) 
{ 
    vec3 samplePoint = startPoint + direction * sampleDist; 
    vec4 sampleHeight = texture(uTextureHeightmap, samplePoint.xy); 
    if (samplePoint.z < sampleHeight.r) 
    { 
     shadow *= 0.5; 
     break; 
    } 
} 
frag_colour = vec4(c.rgb * shadow, c.a); 


Pour un algorithme d'ombre douce, il faut distinguer l'ombre complète d'une transition d'ombre. Pour cela, la distance du faisceau lumineux, à la carte de hauteur doit être étudiée.
S'il y a une grande distance, le fragment est dans l'ombre complète:

heightmap full shadow

Mais dans le cas d'une distance minuscule, il y a une transition d'ombre:

heightmap shadow transition

L'indication de la force de l'ombre est la distance maximale entre le faisceau lumineux et la hauteur de la carte de hauteur. Cette distance peut être calculée par la différence de hauteur maximale d'un point d'échantillon par rapport à la carte de hauteur. Une transition d'ombre lisse peut être calculée par la fonction GLSL smoothstep. Dans le code

vec3 startPoint = vec3(texelCoords, height); 
float maxHeight = 0.0; 
for (float sampleDist = stepSize; sampleDist <= maxDist; sampleDist += stepSize) 
{ 
    vec3 samplePoint = startPoint + direction * sampleDist; 
    vec4 sampleHeight = texture(uTextureHeightmap, samplePoint.xy); 
    maxHeight   = max(maxHeight, sampleHeight.r - samplePoint.z); 
} 
const float minShadow  = 0.5; 
const float transitionHeight = 0.05; 
shadow = smoothstep(1.0, minShadow, clamp(maxHeight/transitionHeight, 0.0, 1.0)); 
fragColor = vec4(height, height * shadow, height, 1.0); 

ci-dessus, la maxHeight variable contient la distance maximale entre le faisceau lumineux et la hauteur du plan de la hauteur. Si c'est plus grand que 0.0 et moins de transitionHeight, il y a une transition d'ombre. Si elle est supérieure à transitionHeight, il y a une ombre complète. La force de l'ombre complète est définie par minShadow.

+0

Merci pour toute l'info @ Rabbid76, mais ça ne résout pas vraiment le problème de la striation – NeomerArcana