SSAO scheint nicht ganz richtig zu sein.

  • Hallo,


    Ich glaube mein SSAO ist nicht ganz richtig weil flache Flächen die nicht direkt in die Kamera schaun, etwas dunkler werden.


    Zur Illustration:


    2017-01-08 (3).jpg


    Scheint ok oder? innere Kanten sind Dünkler und der Boden und die Fläche sind weiß.


    2017-01-08 (4).jpg


    Hier sieht mann aber dass beim drehen die selber Fläche dünkler wird.


    Meine Normalen, sowie Positionen sind Viewspace. Reconstruction from depth mach ich nicht.
    Das Viewspace maht micht stuzig. Ich hätte mir gedacht es sollte im Worldspace sein.
    Aber alle Tutorials machen das so.


    Hat jemend schonmal so ein Problem gehabt?


    lg,


    marc


    Edit:


    Vielliecht sollte ich meinen Shader Code posten:


    #version 430 core
    in vec2 fragUV;
    uniform sampler2D noiseSampler;
    uniform sampler2D sceneNormalSampler;
    uniform sampler2D sceneColorSampler;
    uniform sampler2D scenePosViewSpace;
    uniform vec3 sampleKernel[128];
    uniform vec3 noiseKernel[16];
    uniform mat4 projectionMatrix;
    uniform mat4 viewMatrix;
    layout (location = 0) out vec4 SceneColor;
    out vec4 outColor;
    vec2 uNoiseScale = vec2(1024.0/4.0, 768.0/4.0); // todo make this uniform
    uniform int sampleKernelSize;
    void main() {
    vec3 albedo = texture(sceneColorSampler, fragUV).rgb;
    vec3 origin = texture(scenePosViewSpace, fragUV).xyz;
    vec3 normal = texture(sceneNormalSampler, fragUV).xyz;
    normal = normalize(normal);
    vec2 noiseTexCoords = vec2(1024.0/4.0,768.0/4.0);
    vec3 rvec = texture(noiseSampler, fragUV * noiseTexCoords).xyz;
    vec3 tangent = normalize(rvec - normal * dot(rvec,normal));
    vec3 bitangent = cross(normal,tangent);
    mat3 tbn = mat3(tangent,bitangent,normal);
    float occlusion = 0.0;
    for(int i = 0; i < sampleKernelSize; i++){
    vec3 sampleFromKernel = sampleKernel[i];
    vec3 screenSample = tbn * sampleFromKernel;
    screenSample = screenSample + origin;
    vec4 offset = vec4(screenSample,1.0);
    offset = projectionMatrix * offset;
    offset.xy /= offset.w;
    offset.xy = offset.xy * 0.5 + 0.5;
    // get sample depth
    float sampleDepth = 1000.0f - texture(scenePosViewSpace, offset.xy).z; // Get depth value of kernel sample


    if(abs((1000.0f - origin.z) - sampleDepth) < 1.0)
    {
    occlusion += step(sampleDepth,(1000.0f - screenSample.z));
    }


    }
    occlusion = 1.0 - (occlusion / sampleKernelSize);
    //occlusion = pow(occlusion,2.0);
    SceneColor = vec4(albedo,occlusion);
    SceneColor = vec4(vec3(occlusion),1.0f);
    //SceneColor = vec4(normal,1.0f);


    }

  • Annahmen:


    scenePosViewSpace - enthält korrekte view position und der textur typ ist korrekt. D.h. eine float textur.
    sceneNormalSampler - enthält korrekte normalen und textur typ ist korrekt. Werte zwischen [-1,1]?
    noise texture - keine konvertierung von [0,1] nach [-1,1], daher auch hier: Textur typ sollte korrekt sein.


    Wenn ich mir euren Code ansehe, dann ist der eh fast gleich mit dem Chapman Tutorial.
    Das einzige was unterschiedlich ist, und dort sehe ich auch den Fehler, ist nach dem Samplen, die Occlusion Bestimmung.
    Das 1000.0f minus ... macht meines Erachtens überhaupt keinen Unterschied, außer, dass ihr alle Tiefenwerte invertiert (Annahme: Far-Plane <= 1000) und der Code unlesbar wird.
    Ich hab versucht mir das durchzudenken und eure step Funktion macht soweit ich das sehe, genau dann eine Occlusion wenn die gespeicherte Tiefe (texture(scenePosViewSpace, offset.xy).z) größer/gleich der Sampletiefe (screenSample.z) ist. Das ist aber genau falsch herum, weil das heißt, dass das gespeicherte Fragment hinter dem Sample liegt.


    Prüft mal meine Annahmen, ob das denn alles richtig konfiguriert ist und nehmt testweise den Code von Chapman und tested es noch einmal...

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]

  • Hey, Danke ich hab den Fehler gefunden. Es waren die Normalen, aber nicht so offensichtlich.


    Wenn ein Objekt eine NormalMap Textur hat dann hab ich versucht diese Normalen in den ViewSpace raum zu transformieren. Und das hat Probleme verursacht.
    Jetzt nehm ich einfach die Normalen vom Model, transformiere die und arbeite mit denen. Sind nicht so fein, aber fürs erste funktioniert es.


    2017-01-09.jpg


    2017-01-09 (1).jpg


    Schaut besser aus oder?


    Danke Nochmals,


    Marc

  • Hmm. Ok. Ja, schaut besser aus.


    Für's Konvertieren von Normalen einer Normal-Map in den View-Space geht man ungefähr so vor:
    - Normale (N_o), Tangente (T_o), Bitangente (B_o) sind gegeben im Object Space
    - Konvertieren mit Normalmatrix (N) und ViewMatrix (V) für view-space: N_v = V * N * N_o. Analog für T_v, B_v
    - Normalisieren der Vektoren
    - Erstellen von TBN Matrix. TBN = mat3(T_v, B_v, N_v)
    - Laden von Normalen aus Normal-Map-Textur im Tangent-space (N_ts)
    - Konvertieren von Normalen von Range [0,1] -> [-1,1]
    - Konvertierte Normale im view space ist dann: TBN * N_ts
    - Diese Normale noch normalisieren, fertig


    Wichtig wäre statt der Model-Matrix die Normal-Matrix zu benutzen, falls non-uniform scaling eines Objektes geschieht.


    Ich bin mir nicht sicher ob es sinnvoll ist die Normalen anhand einer Normal-Map für SSAO anzupassen. Denn für SSAO richtet man die Hemisphere für's Sampling anhand der gegebenen Normalen aus und prüft wie viele Samples in der Geometrie stecken. Die Geometrie ist aber die selbe, nur die Normale ist verdreht.


    Man kann das schon machen (Normal-Maps benutzen vor SSAO - hab ich auch schon gemacht), ich bin mir nur nicht sicher ob es richtig ist. Ich würde empfehlen die Normalen vom Objekt zu nehmen - also lass es lieber so :)

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]

  • Noch eine andere Frage:


    Kann mann diesen Effekt auch in Worldspace berechnen?


    Allte Tutorials machen ihn in Viewspace, aber es müsste auch in Worldspace gehen oder?


    Mann müsste nur
    1) Die normalen in worldspace haben
    2) Die Position in worldspace
    3) die Zeile : offset = projectionMatrix * offset; in offset = projectionMatrix * viewMatrx* offset; ändern oder?


    Ich habs schnell versucht, aber dass alleine scheint nicht zu reichen?


    Was hab ich versaümt?


    Mfg,


    Marc

  • Naja wenn du statt scenePosViewSpace jetzt eine Textur mit world space Koordinaten hast, dann wird das folgende nicht viel Sinn machen:


    texture(scenePosViewSpace, offset.xy).z;


    Denn das ist kein Tiefenwert mehr aus Sicht der Kamera.
    Um die Tiefe zu berechnen musst du die Koordinate aus der Textur holen und die Kamera-Position abziehen oder eben mit der View-Matrix multiplizieren...
    Wennst einen vollständigen Code postest, tu ich mir leichter zu erkennen was du verändert hast. Aber bitte mit [ code ] tags umhüllen...

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]


  • Campos abziehen geht auch nicht. :confused:


    Danke,


    marc

  • Der Code ist einfach irgendwas. Was soll denn jetzt origin.z sein? Hast du jetzt zwei texturen? Eine mit view space positions und eine mit world space positions? scenePosWorldSpace ist aber nicht mal definiert. Dass du die Kamera-Position abgezogen hast, mag ja sein - wennst mir den Code dazu nicht zeigst, kann ich wenig dazu sagen. Ich hoffe mal, dass du danach nicht einfach wieder die Z-Komponente vom Resultat genommen hast.


    Also so nicht...

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]

  • ok sry. Vielleicht hätte ich das besser formulieren sollen was ich mache.


    Also das einzige was ich geändert habe ist das ich anstatt der viewspace Position die Worldspace Position dem Shader mit gebe.
    Die normalen sind noch immer Viewspace.


    Dan proeziere ich die worldspace Position in die viewspace


    Code
    1. vec3 originWS = texture(scenePosWorldSpace, fragUV).xyz;
    2. vec4 originVS = viewMatrix * vec4(originWS,1.0);


    So hab ich mir gedacht dass alles wie vorher sein sollte, sprich als alles im Viewspace war.
    Aber das funktioniert nicht. Ist das weil ich die homogene Cordiante mit 1.0f reinschreibe?


    Den shader im vorigen Post hab ich geändert das alles passst. Sry, habs im post einfach editiert.


    mfg,


    Marc

  • Ok. Der Ansatz schaut in Ordnung aus für mich.
    Stimmt diese Zeile jetzt so?:

    Code
    1. [COLOR=blue]float[/COLOR] sampleDepth = [COLOR=maroon][B]texture[/B][/COLOR](scenePosViewSpace, offset.xy).z;


    Oder benutzt du hier scenePosWorldSpace?


    Oder anders gefragt. Wenn du "scenePosViewSpace" eh verfügbar hast, funktioniert SSAO dann wenn du

    Code
    1. originVS = vec4([COLOR=maroon][B]texture[/B][/COLOR](scenePosViewSpace, fragUV).xyz,1.0)

    setzt? Wenn es funktioniert, dann ist deine Textur "scenePosWorldSpace" falsch. Wenn es nicht funktioniert hat es was anderes am Algorithmus.


    Und die 1.0 als w-Komponente der homogenen Koordinaten passt. Du willst ja eine Position im World-Space zu einer Position im View-Spae umrechnen (mit Translation).

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]

  • sorry das sollte natürlich


    Code
    1. [COLOR=#0000ff]float[/COLOR] sampleDepth = [COLOR=maroon][B]texture[/B][/COLOR](scenePosWorldSpace, offset.xy).z;


    sein. Was natürlich falsch ist weil sampleDepth worldspace und screenSample viewSpace ist.


    Ok neustart. Ich hab den Shader jetzt bissi umgeschrieben. Es sieht besser aus, aber noch immer falsch. also irgendwas ist noch immer in einem Falschem Space


    Code
    1. vec3 albedo = texture(sceneColorSampler, fragUV).rgb;
    2. vec3 origin = texture(scenePosWorldSpace, fragUV).xyz; // worldspace
    3. vec4 originVS = viewMatrix * vec4(origin,1.0f); // brauch ich nicht mehr
    4. vec3 normal = texture(sceneNormalSampler, fragUV).xyz; // Normalen ist jetzt auch worldspace
    5. normal = normalize(normal);


    soweit so gut


    Code
    1. vec2 noiseTexCoords = vec2(1024.0/4.0,768.0/4.0);
    2. vec3 rvec = texture(noiseSampler, fragUV * noiseTexCoords).xyz;
    3. vec3 tangent = normalize(rvec - normal * dot(rvec,normal));
    4. vec3 bitangent = cross(normal,tangent);
    5. mat3 tbn = mat3(tangent,bitangent,normal);


    das sollte auch passen. Also die TBN matrix ist jetzt WS

    Code
    1. float occlusion = 0.0;
    2. for(int i = 0; i < sampleKernelSize; i++){
    3. vec3 sampleFromKernel = sampleKernel[i];
    4. vec3 screenSample = tbn * sampleFromKernel;
    5. screenSample = screenSample + origin.xyz; // screen sample sollte auch WS sein
    6. vec4 offset = vec4(screenSample,1.0);
    7. offset = projectionMatrix * viewMatrix * offset; // da screenSample WS ist muss ich mit der viewMatrix multiplizieren
    8. offset.xy /= offset.w;
    9. offset.xy = offset.xy * 0.5 + 0.5;


    bei dem part bin ich mit nicht ganz sicher. Ich seh aber nicht was ich da Falsch mache


    Code
    1. // get sample depth
    2. float sampleDepth = texture(scenePosWorldSpace, offset.xy).z; // Get depth value of kernel sample (WS)
    3. if(abs(origin.z - sampleDepth) < 1.0) // origin und sampleDepth beide WS
    4. {
    5. occlusion += step(screenSample.z,sampleDepth); // screenSample und sampleDepth sollten beide WS sein
    6. }
    7. }
    8. occlusion = 1.0 - (occlusion / sampleKernelSize);


    Sieht auch OK aus. Die Annahme ist das alle Depthvalues im WS sind




    Ja, also wenn ich alle worldspace texturen mit viewspace vertausche dann geht.


    Es ist nur so: unsere engine benutzt deferred rendering mit worldspace texturen.
    Wenn ich jetzt eine ganze textur mit viewspace mache nur für diesen Effekt, dann leidet die Performance.
    Deswegen will ich es im Worldspace haben.


    Danke nochmal fuer die Hilfe!


    mfg,


    marc

  • Ok. Also es ist grundsätzlich kein Problem mit den Worldspace Daten zu arbeiten. Aber, dir muss bewusst sein, was SSAO überhaupt macht...
    Es werden nämlich Tiefenwerte aus Sicht der Kamera miteinander verglichen.


    Du kannst nicht einfach deine Textur mit world space positions samplen und davon den z-Wert extrahieren und sagen, dass ist mein Tiefenwert. Weil das ist nur die Z-Koordinate von einer Position im Weltkoordinatensystem! Das hat nichts mit Tiefe zu tun.


    D.h. du musst immer noch die Tiefenwerte aus Sicht der Kamera miteinander vergleichen.
    Das einfachste für dich ist: Jedes mal wenn du auf die scenePosWorldSpaceTextur zugreifst, hol dir die xyz Koordinaten raus und dann konvertier sie mit der View Matrix in den View Space. Wie du es bereits vor der Schleife gemacht hast, musst du es auch in der Schleife machen.. Nachdem du konvertiert hast, kannst du die z-Koordinate nehmen. Bzw. richtiger wäre wahrscheinlich die Länge des View-Vektors als Tiefe heranzuziehen.

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]

  • Quote


    Es werden nämlich Tiefenwerte aus Sicht der Kamera miteinander verglichen.


    Ja das war ein Denkfehler. Ich dachte immer es werden nur relative Werte verglichen, also kann mann alles im Worldspace machen. Das war Falsch!


    Ich hab meinen Fehler gefunden.


    Es war die transformation matrix chain.
    Sprich: Wenn ich im SSAO shader


    Code
    1. vec4 originVS = viewMatrix * vec4(origin.xyz,1.0f);


    Mache dann werf ich ja den Homogenen Wert einfach weg, was sich auf die Transformation auswirkt.


    Ich hab die deferred rendering Pipeline so umgeschrieben dass sich in der gPosition Texture alle xyzw Werte der Position befinden


    Jetzt kann ich einfach

    Code
    1. vec4 originVS = viewMatrix * origin.xyzw;


    machen, und der Code funktioniert


    Code
    1. vec4 sampleVS = viewMatrix * texture(scenePosWorldSpace, offset.xy); // Get depth value of kernel sample


    hab ich auch vorher nicht gemacht.


    Danke nochmals fuer die Hilfe!


    Mfg,


    Marc


  • Ich hab meinen Fehler gefunden.
    Ich hab die deferred rendering Pipeline so umgeschrieben dass sich in der gPosition Texture alle xyzw Werte der Position befinden


    Und warum sollte der homogene Anteil der Weltkoordinaten jemals ungleich 1.0 sein?
    Ich behaupte mal daran lag's nicht.

    Stefan Spelitz
    [Computergraphik UE Tutor 2017SS]